{
  "generator": "Code Snippets v3.9.6",
  "date_created": "2026-08-13 12:39",
  "snippets": [
    {
      "id": 782,
      "name": "Elementor Flight Deck 1.2 KEEP",
      "code": "/*\n * Elementor Flight Deck v1.2 \n */\n\n\n\nfunction add_landmark_audit_guide() {\n    /*\n     * Visibility rule only — no user, email, username, role or capability gate.\n     *\n     * Load the auditor when a page is being edited or explicitly previewed.\n     * Do not output it during an ordinary public/live front-end request.\n     */\n    $request_value = static function ( $key ) {\n        if ( ! isset( $_GET[ $key ] ) ) {\n            return '';\n        }\n\n        return sanitize_text_field( wp_unslash( $_GET[ $key ] ) );\n    };\n\n    $is_preview_request = ( function_exists( 'is_preview' ) && is_preview() )\n        || 'true' === strtolower( $request_value( 'preview' ) )\n        || '' !== $request_value( 'preview_id' )\n        || '' !== $request_value( 'preview_nonce' )\n        || '' !== $request_value( 'elementor-preview' )\n        || '' !== $request_value( 'customize_changeset_uuid' );\n\n    // Front-end editing modes used by common WordPress builders/customizer.\n    $is_frontend_editor = '' !== $request_value( 'elementor-preview' )\n        || 'elementor' === strtolower( $request_value( 'action' ) )\n        || 'run' === strtolower( $request_value( 'bricks' ) )\n        || 'true' === strtolower( $request_value( 'ct_builder' ) )\n        || 'true' === strtolower( $request_value( 'fl_builder' ) )\n        || isset( $GLOBALS['wp_customize'] );\n\n    // Restrict wp-admin output to actual editing interfaces, not every admin page.\n    $is_admin_editor = false;\n    if ( is_admin() && function_exists( 'get_current_screen' ) ) {\n        $screen = get_current_screen();\n\n        if ( $screen ) {\n            $editor_bases = [ 'post', 'site-editor', 'widgets', 'customize' ];\n            $is_admin_editor = in_array( (string) $screen->base, $editor_bases, true );\n        }\n    }\n\n    if ( ! $is_preview_request && ! $is_frontend_editor && ! $is_admin_editor ) {\n        return;\n    }\n\n    // Only mark the auditor as loaded after this request has passed the\n    // preview/editor visibility check. This allows a later footer hook to\n    // output it when an earlier Elementor hook ran before the editor existed.\n    static $landmark_audit_loaded = false;\n    if ( $landmark_audit_loaded ) {\n        return;\n    }\n    $landmark_audit_loaded = true;\n\n\techo \"\";\n    ?>\n    <script>(function(){if(typeof window==='undefined'||typeof document==='undefined')return;if(window.top!==window.self)return;if(window.__AUDIT_GUIDE_INIT__)return;window.__AUDIT_GUIDE_INIT__=true;const OPEN=(window.__AUDIT_OPEN__=window.__AUDIT_OPEN__||new Map());let INTERACTING=false;let lastSignature='';let mo=null,heartbeat=null,debTimer=null,resizeHandler=null,topMO=null;function stopObserver(){try{if(mo)mo.disconnect();}catch(e){}\nmo=null;try{if(heartbeat)clearInterval(heartbeat);}catch(e){}\nheartbeat=null;if(debTimer){clearTimeout(debTimer);debTimer=null;}\nif(resizeHandler){window.removeEventListener('resize',resizeHandler);resizeHandler=null;}\ntry{if(topMO)topMO.disconnect();}catch(e){}\ntopMO=null;}\nconst HIDE_KEY='__AUDIT_HIDE_V1__';const HIDDEN=new Set();try{const saved=localStorage.getItem(HIDE_KEY);if(saved)JSON.parse(saved).forEach(k=>HIDDEN.add(k));}catch(_){}\nfunction isHidden(issueKey){return HIDDEN.has(issueKey);}\nfunction setHidden(issueKey,hidden){if(hidden)HIDDEN.add(issueKey);else HIDDEN.delete(issueKey);try{localStorage.setItem(HIDE_KEY,JSON.stringify(Array.from(HIDDEN)));}catch(_){}}\nlet __CLAMP_CACHE__={time:0,info:null};const btn=document.createElement('button');btn.type='button';btn.textContent='Audit';Object.assign(btn.style,{position:'fixed',bottom:'30px',right:'30px',zIndex:2147483646,padding:'7px 12px',lineHeight:'normal',background:'#e53935',color:'#fff',border:'none',borderRadius:'3px',cursor:'grab',fontSize:'14px',boxShadow:'0 2px 8px rgba(0,0,0,.25)',userSelect:'none'});btn.setAttribute('aria-label','Open build audit panel');btn.setAttribute('aria-pressed','false');btn.setAttribute('title','Audit');btn.setAttribute('aria-haspopup','dialog');btn.setAttribute('aria-controls','audit-panel');btn.setAttribute('data-audit-btn','');btn.style.display='none';btn.style.WebkitTapHighlightColor='transparent';btn.style.setProperty('outline','none','important');btn.style.setProperty('box-shadow','0 2px 6px rgba(0,0,0,.3)','important');const auditBtnStyle=document.createElement('style');auditBtnStyle.textContent=`\n  [data-audit-btn] { outline: none !important; }\n  [data-audit-btn]:focus,\n  [data-audit-btn]:focus-visible {\n    outline: none !important;\n    box-shadow: 0 2px 6px rgba(0,0,0,.3) !important;\n  }\n`;document.head.appendChild(auditBtnStyle);const panel=document.createElement('div');Object.assign(panel.style,{position:'fixed',bottom:'70px',right:'20px',zIndex:2147483645,width:'600px',minWidth:'360px',maxWidth:'min(900px,92vw)',maxHeight:'72vh',overflow:'auto',background:'#fff',color:'#222',border:'1px solid #e5e5e5',borderRadius:'10px',padding:'12px',display:'none',font:'13px/1.45 -apple-system, Segoe UI, Roboto, Arial, sans-serif',boxShadow:'0 12px 36px rgba(0,0,0,.28)'});const title=document.createElement('div');title.textContent='Build Audit';Object.assign(title.style,{fontWeight:700,marginBottom:'8px',cursor:'move',padding:'4px 2px',borderBottom:'1px solid #f0f0f0'});panel.id='audit-panel';panel.setAttribute('role','dialog');panel.setAttribute('aria-modal','false');panel.setAttribute('aria-labelledby','audit-title');title.id='audit-title';const infoBtn=document.createElement('button');infoBtn.type='button';infoBtn.textContent='Info';Object.assign(infoBtn.style,{marginLeft:'auto',fontSize:'12px',lineHeight:'1',padding:'4px 8px',border:'1px solid #e2e2e2',borderRadius:'6px',background:'#f7f7f7',color:'#000',cursor:'pointer'});infoBtn.setAttribute('aria-haspopup','dialog');infoBtn.style.background='#f7f7f7';infoBtn.style.color='#000';const closeBtn=document.createElement('button');closeBtn.type='button';closeBtn.textContent='Close';Object.assign(closeBtn.style,{fontSize:'12px',lineHeight:'1',padding:'4px 8px',border:'1px solid #e2e2e2',borderRadius:'6px',background:'#f7f7f7',color:'#000',cursor:'pointer'});closeBtn.setAttribute('aria-label','Close build audit panel');const titleWrap=document.createElement('div');Object.assign(titleWrap.style,{display:'flex',alignItems:'center',gap:'8px'});titleWrap.appendChild(title);titleWrap.appendChild(infoBtn);titleWrap.appendChild(closeBtn);const content=document.createElement('div');panel.appendChild(titleWrap);const infoBackdrop=document.createElement('div');Object.assign(infoBackdrop.style,{position:'fixed',inset:0,background:'rgba(0,0,0,.35)',zIndex:2147483647,display:'none'});infoBackdrop.setAttribute('aria-hidden','true');const infoPane=document.createElement('div');infoPane.id='audit-info-pane';infoPane.setAttribute('role','dialog');infoPane.setAttribute('aria-modal','true');infoPane.setAttribute('aria-labelledby','audit-info-title');Object.assign(infoPane.style,{position:'fixed',zIndex:2147483648,maxWidth:'min(900px, 92vw)',width:'720px',maxHeight:'95vh',overflow:'auto',left:'50%',top:'50%',transform:'translate(-50%, -50%)',background:'#fff',color:'#222',border:'1px solid #e5e5e5',borderRadius:'12px',boxShadow:'0 20px 60px rgba(0,0,0,.35)',display:'none'});infoPane.style.position='fixed';infoPane.style.top='0';infoPane.style.left='0';infoPane.style.right='auto';infoPane.style.transform='none';infoPane.style.setProperty('width','75vw','important');infoPane.style.setProperty('max-width','none','important');infoPane.style.height='100dvh';infoPane.style.maxHeight='100dvh';infoPane.style.overflow='auto';infoPane.style.boxSizing='border-box';const infoHead=document.createElement('div');infoHead.id='audit-info-title';infoHead.textContent='Build Audit — What each issue means & how to test/fix';Object.assign(infoHead.style,{fontWeight:700,padding:'12px 14px',borderBottom:'1px solid #efefef',position:'sticky',top:0,background:'#fff',zIndex:1});const infoX=document.createElement('button');infoX.type='button';infoX.setAttribute('aria-label','Close information panel');infoX.innerHTML='&times;';Object.assign(infoX.style,{position:'absolute',top:'8px',right:'10px',width:'32px',height:'32px',display:'inline-flex',alignItems:'center',justifyContent:'center',background:'transparent',border:'none',fontSize:'22px',lineHeight:'1',color:'#000',cursor:'pointer'});infoX.addEventListener('click',(e)=>{e.stopPropagation();closeInfo();});infoHead.appendChild(infoX);const infoBody=document.createElement('div');Object.assign(infoBody.style,{padding:'12px 14px',fontSize:'13px',lineHeight:'1.55'});const infoAccStyle=document.createElement('style');infoAccStyle.textContent=`\n  #audit-info-pane .info-acc { margin:7px 0; border:1px solid #efefef; border-radius:3px; overflow:hidden; }\n  #audit-info-pane .info-acc-head {\n    all: unset; display:flex; align-items:center; gap:6px; width:100%;\n    padding:5px 12px; cursor:pointer; background:#fafafa; border-bottom:1px solid #f0f0f0;\n    font-size:13px; font-weight:700; line-height:1.45;\n  }\n#audit-info-pane .info-acc-head:focus,\n#audit-info-pane .info-acc-head:focus-visible { outline: none !important; }\n  #audit-info-pane .info-acc-head .chev { display:inline-block; transition:transform .18s ease; }\n  #audit-info-pane .info-acc-head[aria-expanded=\"true\"] .chev { transform: rotate(90deg); }\n  #audit-info-pane .info-acc-body { padding:10px 12px; background:#fff; }\n  #audit-info-pane .info-acc-body ul { margin:0; padding-left:18px; }\n  #audit-info-pane .info-acc-body li { margin:6px 0; }\n`;document.head.appendChild(infoAccStyle);const section=(title,items)=>{const key='info-'+title.toLowerCase().replace(/[^a-z0-9]+/g,'-').replace(/^-|-$/g,'');return`\n    <section class=\"info-acc\" data-key=\"${key}\">\n      <button type=\"button\" class=\"info-acc-head\" aria-expanded=\"false\">\n        <span class=\"chev\" aria-hidden=\"true\">▶</span>\n        <span>${title}</span>\n      </button>\n      <div class=\"info-acc-body\" hidden>\n        <ul>\n          ${items.map(li => `<li>${li}</li>`).join('')}\n        </ul>\n      </div>\n    </section>`;};const INFO_HTML=`\n${section('MAIN: Missing', [\n  '<b>What it means:</b> Your page’s primary content is not wrapped in a single <code>&lt;main&gt;</code> region, so assistive tech can’t jump past the header/nav directly to content.',\n  '<b>How it checks:</b> Looks for exactly one top-level <code>&lt;main&gt;</code> (or an element with <code>role=\"main\"</code>).',\n  '<b>How to test (Elementor):</b> Open the page in Elementor → select the <i>topmost content</i> Container that holds your page content (not the Header template) → if “HTML Tag” isn’t set to <code>main</code>, the issue will appear.',\n  '<b>How to fix (Elementor):</b> Select that top content Container → <b>Advanced ▸ HTML Tag ▸</b> choose <code>main</code>. If you don’t see the control, add a short “CSS ID” of <code>main</code> and keep using the Container; the Audit accepts <code>role=\"main\"</code>/<code>#main</code> targets as valid.'\n])}\n${section('MAIN: Multiple', [\n  '<b>What it means:</b> More than one region is marked as the main content. There must be exactly one <code>&lt;main&gt;</code> per page.',\n  '<b>How it checks:</b> Counts <code>&lt;main&gt;</code> (and <code>role=\"main\"</code>) elements.',\n  '<b>How to test (Elementor):</b> In Navigator, check your top-level Containers. If more than one Container’s “HTML Tag” is set to <code>main</code>, the issue appears.',\n  '<b>How to fix (Elementor):</b> Keep the real content wrapper as <code>main</code>. For any other Container set to <code>main</code>, change <b>Advanced ▸ HTML Tag</b> to <code>div</code>/<code>section</code>.'\n])}\n${section('HEADER: Missing', [\n  '<b>What it means:</b> No site-level <code>&lt;header&gt;</code> landmark was found.',\n  '<b>How it checks:</b> Looks for a single top-level <code>&lt;header&gt;</code> region.',\n  '<b>How to test (Elementor):</b> If you built the header <i>inside the page</i>, select its outer Container and see if “HTML Tag” is <code>header</code>. If you use a Theme Builder Header, it will be outside the page canvas and typically passes by default.',\n  '<b>How to fix (Elementor):</b> For page-embedded headers: select the header’s outer Container → <b>Advanced ▸ HTML Tag ▸</b> <code>header</code>. For Theme Builder: ensure the template’s outer wrapper uses <code>header</code> (Elementor usually sets this automatically).'\n])}\n${section('HEADER: Multiple', [\n  '<b>What it means:</b> More than one top-level <code>&lt;header&gt;</code> landmark exists.',\n  '<b>How it checks:</b> Counts top-level <code>&lt;header&gt;</code> elements.',\n  '<b>How to test (Elementor):</b> In Navigator, if you placed a “header-like” section inside the page as well as using a Theme Builder Header, you may effectively have two.',\n  '<b>How to fix (Elementor):</b> Keep the Theme Builder Header as the only <code>header</code>. Change any page-level “header” Container’s <b>HTML Tag</b> to <code>div</code>/<code>section</code>.'\n])}\n${section('FOOTER: Missing', [\n  '<b>What it means:</b> No site-level <code>&lt;footer&gt;</code> landmark was found.',\n  '<b>How it checks:</b> Looks for a top-level <code>&lt;footer&gt;</code> region.',\n  '<b>How to test (Elementor):</b> If your footer is built on the page, select its outer Container and check “HTML Tag.” If you use Theme Builder, the footer is typically correct by default.',\n  '<b>How to fix (Elementor):</b> For page-embedded footers: set the outer Container’s <b>HTML Tag</b> to <code>footer</code>. For Theme Builder: ensure the footer template’s wrapper is a <code>footer</code>.'\n])}\n${section('FOOTER: Multiple', [\n  '<b>What it means:</b> More than one <code>&lt;footer&gt;</code> landmark exists.',\n  '<b>How it checks:</b> Counts top-level <code>&lt;footer&gt;</code> elements.',\n  '<b>How to test (Elementor):</b> Using a Theme Builder Footer plus a page-level “footer section” can create two.',\n  '<b>How to fix (Elementor):</b> Keep the Theme Builder Footer as the only <code>footer</code>. Change any page-level “footer” Container’s <b>HTML Tag</b> to <code>div</code>/<code>section</code>.'\n])}\n${section('HEADINGS: Missing H1', [\n  '<b>What it means:</b> The page has no <code>&lt;h1&gt;</code> top heading (the main page title).',\n  '<b>How it checks:</b> Looks for at least one <code>H1</code>.',\n  '<b>How to test (Elementor):</b> Select your main title Heading widget → in Content panel check “HTML Tag.” If it’s not <code>H1</code>, the issue appears.',\n  '<b>How to fix (Elementor):</b> On your main title Heading widget set <b>Content ▸ HTML Tag</b> to <code>H1</code>. Use <code>H2/H3</code> for section titles.'\n])}\n${section('HEADINGS: Multiple H1', [\n  '<b>What it means:</b> There should be exactly one <code>H1</code> per page.',\n  '<b>How it checks:</b> Counts all <code>H1</code> elements.',\n  '<b>How to test (Elementor):</b> In Navigator, click other Heading widgets and check their “HTML Tag.” If more than one is set to <code>H1</code>, the issue appears.',\n  '<b>How to fix (Elementor):</b> Leave the true page title as <code>H1</code>. Change the others: <b>Content ▸ HTML Tag ▸</b> <code>H2</code>/<code>H3</code>.'\n])}\n${section('HEADINGS: Order skips levels', [\n  '<b>What it means:</b> The heading outline jumps (e.g., H1 → H3) or moves backward. Headings should step by 1 level (or stay the same).',\n  '<b>How it checks:</b> Compares each heading to the previous one within its section.',\n  '<b>How to test (Elementor):</b> In Navigator, select a Heading and verify sequence: main title <code>H1</code>, section titles <code>H2</code>, sub-sections <code>H3</code>, etc.',\n  '<b>How to fix (Elementor):</b> For each Heading widget, set <b>Content ▸ HTML Tag</b> so the order flows H1 → H2 → H3 without skipping or going backwards.'\n])}\n${section('HEADINGS: Content too long', [\n  '<b>What it means:</b> A heading should be concise and scannable. If it runs too long, it becomes harder for screen readers and users to parse quickly.',\n  '<b>How it checks:</b> Flags headings (h1–h6) where the text length exceeds 12 words or 80 characters.',\n  '<b>How to test:</b> In your Elementor page, check the heading widget text. Add a heading that is 12+ words or 80+ characters.',\n  '<b>How to fix:</b> Shorten the heading to a clear summary of the section. Move extra detail into a paragraph widget below the heading.'\n])}\n${section('ACCESSIBILITY: “Skip to content”', [\n  '<b>What it means:</b> A keyboard user needs a visible-on-focus link that jumps to the main content.',\n  '<b>How it checks:</b> Requires a link early in the tab order pointing to <code>#main</code> (or an element with <code>role=\"main\"</code>) and that it becomes visible on focus.',\n  '<b>How to test (Elementor):</b> In Navigator, add an <b>HTML</b> widget as the very first widget on the page and tab to it. If you don’t see a Skip link appear when it receives focus, the issue remains.',\n  '<b>How to fix (Elementor):</b> 1) Give your main content Container a <b>CSS ID</b> of <code>main</code> (Advanced ▸ CSS ID). 2) Add an HTML widget at the very top with:<br><code>&lt;a class=\"skip-link\" href=\"#main\"&gt;Skip to content&lt;/a&gt;</code><br>3) In the same HTML widget, add a style block (or use Page ▸ Advanced ▸ Custom CSS in Pro):<br><code>&lt;style&gt;.skip-link{position:absolute;left:-9999px;} .skip-link:focus{left:8px;top:8px;z-index:99999;padding:.5rem 1rem;background:#fff;border:2px solid #000}&lt;/style&gt;</code>'\n])}\n${section('ACCESSIBILITY: Color contrast (AA) failures', [\n  '<b>What it means:</b> Text must be ≥4.5:1 contrast (3:1 if large). Buttons/controls also need ≥3:1 for fills/borders vs surroundings.',\n  '<b>How it checks:</b> Computes text vs background and control background/border vs surrounding background.',\n  '<b>How to test (Elementor):</b> Pick a Heading/Text/Button → <b>Style</b> and temporarily set a very light text or border color. Reopen the Audit to see the failure.',\n  '<b>How to fix (Elementor):</b> Select the widget → <b>Style</b> → adjust Text/Background/Border color until the Audit passes. For Buttons, ensure the fill or border contrasts ≥3:1 against the section background.'\n])}\n${section('ACCESSIBILITY: Text below 16px', [\n  '<b>What it means:</b> Body text under 16px is hard to read for many users.',\n  '<b>How it checks:</b> Finds representative elements computed below 16px.',\n  '<b>How to test (Elementor):</b> Select a Text Editor/Heading widget → <b>Style ▸ Typography ▸ Size</b> and set 14px; the Audit should flag it.',\n  '<b>How to fix (Elementor):</b> Increase <b>Typography ▸ Size</b> to ≥16px. Also check Line-Height for comfortable reading.'\n])}\n${section('ACCESSIBILITY: Button minimum size (24×24) failures', [\n  '<b>What it means:</b> Click/tap targets should be at least 24×24px.',\n  '<b>How it checks:</b> Measures visible hit area of links/buttons/icon-only controls.',\n  '<b>How to test (Elementor):</b> Select a Button/Icon widget → reduce padding/size until it’s tiny; the Audit will flag it.',\n  '<b>How to fix (Elementor):</b> Increase <b>Style ▸ Padding</b> (or Icon Size/Line Height) so the rendered button/hit area is ≥24×24px.'\n])}\n${section('ACCESSIBILITY: Mobile control spacing (≥12px) failures', [\n  '<b>What it means:</b> On small screens, controls need ≥12px clear space between edges.',\n  '<b>How it checks:</b> In a narrow viewport, measures minimum spacing between nearby controls (excludes typical menu-item vs menu-item).',\n  '<b>How to test (Elementor):</b> Switch to Mobile preview → place two Buttons close together → the Audit will report spacing under 12px.',\n  '<b>How to fix (Elementor):</b> On the parent Container, increase <b>Items Gap</b> (if using Containers) or add <b>Advanced ▸ Margin</b> to widgets so nearest edges are ≥12px on Mobile.'\n])}\n${section('ACCESSIBILITY: Aria-Labels for Vague or Missing descriptions', [\n  '<b>What it means:</b> Links/buttons must have clear, specific accessible names. Icon-only controls need a programmatic label.',\n  '<b>How it checks:</b> Flags vague CTAs (e.g., “Read more”), icon-only controls without labels, linked images without names, and unlabeled form fields.',\n  '<b>How to test (Elementor):</b> Create a Button with text “Read more” or an Icon widget wrapped in a link with no text; the Audit will flag them.',\n  '<b>How to fix (Elementor):</b> Prefer specific visible text (e.g., “Read more about Pricing”). For icon-only links/buttons, add <b>Advanced ▸ Attributes</b> → <code>aria-label=\"Open cart\"</code>, or aria-label|Open Cart For forms, ensure each field has a Label in the Form widget.'\n])}\n${section('ACCESSIBILITY: Non-native buttons missing role/tabindex', [\n  '<b>What it means:</b> Clickable <code>&lt;div&gt;</code>/<code>&lt;span&gt;</code> need <code>role=\"button\"</code> and keyboard focus (<code>tabindex=\"0\"</code>)—or better, use a real Button.',\n  '<b>How it checks:</b> Finds non-native “button-like” elements that are missing role/tabindex.',\n  '<b>How to test (Elementor):</b> Make a Container clickable (link in Advanced) with no Button widget; the Audit may flag inner non-native elements.',\n  '<b>How to fix (Elementor):</b> Replace with a Button widget when possible. If you must keep a custom element: add <b>Advanced ▸ Attributes</b> → <code>role=\"button\"</code> and <code>tabindex=\"0\"</code>.'\n])}\n${section('CONTENT: No primary CTA found', [\n  '<b>What it means:</b> The page lacks a clear next action outside navigation.',\n  '<b>How it checks:</b> Looks for prominent non-nav buttons/links.',\n  '<b>How to test (Elementor):</b> Remove the main Button from your hero or key section; the Audit will report no obvious CTA.',\n  '<b>How to fix (Elementor):</b> Add a Button widget in the hero or after key content (e.g., “Contact us”, “Get a quote”) and link it to your target page.'\n])}\n${section('IMAGES: Non-modern formats', [\n  '<b>What it means:</b> JPG/PNG are heavier; prefer WebP/AVIF for performance.',\n  '<b>How it checks:</b> Flags <code>&lt;img&gt;</code> whose current src isn’t WebP/AVIF.',\n  '<b>How to test (Elementor):</b> Drop an Image widget with a .jpg/.png. The Audit will list it.',\n  '<b>How to fix (Elementor):</b> Replace the image: open the Image widget → click the thumbnail → upload/select a <b>.webp</b> (or AVIF) version.'\n])}\n${section('IMAGES: Alt-Text Description Missing', [\n  '<b>What it means:</b> Informative images need meaningful <code>alt</code>. Decorative images should use empty alt (<code>alt=\"\"</code>).',\n  '<b>How it checks:</b> Flags missing, empty, or placeholder alt text.',\n  '<b>How to test (Elementor):</b> Insert an Image widget → choose an image with empty “Alt Text” in the media dialog; the Audit will flag it.',\n  '<b>How to fix (Elementor):</b> Click the image thumbnail → in the media dialog, fill the <b>Alt Text</b> field. For decorative images, set Alt Text intentionally empty (<code>\"\"</code>).'\n])}\n${section('NAV: Multiple menus share the same aria-label', [\n  '<b>What it means:</b> Multiple <code>&lt;nav&gt;</code> regions have the same accessible name, which confuses screen-reader navigation.',\n  '<b>How it checks:</b> Groups navs by <code>aria-label</code>/<code>aria-labelledby</code> and finds duplicates.',\n  '<b>How to test (Elementor):</b> If you use more than one Nav Menu widget, select each one → <b>Advanced ▸ Attributes</b> and check for identical <code>aria-label</code> values.',\n  '<b>How to fix (Elementor):</b> On each Nav Menu’s outer Container or the widget: <b>Advanced ▸ Attributes</b> → add <code>aria-label=\"Primary navigation\"</code>, <code>\"Footer navigation\"</code>, etc., making them unique.'\n])}\n${section('TYPOGRAPHY: font-size without clamp()', [\n  '<b>What it means:</b> Fixed font sizes don’t scale fluidly across viewports; <code>clamp()</code> gives a responsive min/preferred/max.',\n  '<b>How it checks:</b> Scans CSS/inline vars for <code>clamp()</code> on <code>font-size</code>.',\n  '<b>How to test (Elementor):</b> Set a Heading/Text to a fixed px size (no responsive settings) and reopen the Audit.',\n  '<b>How to fix (Elementor):</b> Give the widget a class (e.g., <code>fs-fluid</code> in <b>Advanced ▸ CSS Classes</b>) then add an HTML widget anywhere on the page with:<br><code>&lt;style&gt;.fs-fluid{font-size:clamp(1rem, 2.2vw, 1.5rem)}&lt;/style&gt;</code><br>(Or, if you have Pro: Page ▸ Advanced ▸ Custom CSS).'\n])}\n`;infoBody.innerHTML=INFO_HTML;(function initInfoAccordions(root){const heads=root.querySelectorAll('.info-acc-head');heads.forEach(head=>{const body=head.nextElementSibling;const toggle=()=>{const open=head.getAttribute('aria-expanded')==='true';head.setAttribute('aria-expanded',String(!open));body.hidden=open;};head.addEventListener('click',toggle);head.addEventListener('keydown',e=>{if(e.key==='Enter'||e.key===' '){e.preventDefault();toggle();}});head.setAttribute('role','button');head.setAttribute('tabindex','0');});})(infoBody);const infoClose=document.createElement('button');infoClose.type='button';infoClose.textContent='Close';Object.assign(infoClose.style,{position:'sticky',bottom:0,margin:'12px',marginTop:'0',padding:'6px 10px',border:'1px solid #e2e2e2',borderRadius:'6px',background:'#f7f7f7',cursor:'pointer',float:'right'});infoClose.setAttribute('aria-label','Close information panel');infoPane.appendChild(infoHead);infoPane.appendChild(infoBody);infoPane.appendChild(infoClose);document.body.appendChild(infoBackdrop);document.body.appendChild(infoPane);function openInfo(){infoBackdrop.style.display='block';infoBackdrop.setAttribute('aria-hidden','false');infoPane.style.display='block';infoClose.focus();}\nfunction closeInfo(){infoBackdrop.style.display='none';infoBackdrop.setAttribute('aria-hidden','true');infoPane.style.display='none';infoBtn.focus();}\ninfoBtn.setAttribute('aria-controls',infoPane.id);infoBtn.addEventListener('click',(e)=>{e.stopPropagation();openInfo();});infoClose.addEventListener('click',(e)=>{e.stopPropagation();closeInfo();});infoBackdrop.addEventListener('click',closeInfo);window.addEventListener('keydown',(e)=>{if(e.key==='Escape'&&infoBackdrop.style.display==='block')closeInfo();});panel.appendChild(content);const toggles=document.createElement('div');Object.assign(toggles.style,{display:'flex',flexWrap:'wrap',gap:'8px',margin:'8px 0 10px 0',fontSize:'12px',color:'#333'});const CATS=[{key:'contrast_aa',label:'Color Contrast'},{key:'text_min_16',label:'Text <16px'},{key:'images_alt_missing',label:'Alt-Text'},{key:'images_nonmodern',label:'WebP/AVIF Images'},{key:'font_no_clamp',label:'Clamp()'},{key:'aria_label',label:'Aria-Label'},{key:'cta_missing',label:'CTAs'},{key:'heading_too_long',label:'Long Headings'},{key:'control_min_size',label:'Control Size'},{key:'mobile_control_spacing',label:'Mobile Spacing'},{key:'nav_duplicate_labels',label:'Duplicate Nav'},{key:'skip_link',label:'Skip Link'},{key:'button_semantics',label:'Button Role/Tab'},];const GROUPS=[{groupKey:'landmark_main',label:'Main',members:['main_missing','main_multiple']},{groupKey:'landmark_header',label:'Header',members:['header_missing','header_multiple']},{groupKey:'landmark_footer',label:'Footer',members:['footer_missing','footer_multiple']},{groupKey:'headings_group',label:'Headings',members:['h1_missing','h1_multiple','heading_skips']},];function groupKeyFor(issueKey){if(/^main_/.test(issueKey))return'landmark_main';if(/^header_/.test(issueKey))return'landmark_header';if(/^footer_/.test(issueKey))return'landmark_footer';if(issueKey==='h1_missing'||issueKey==='h1_multiple'||issueKey==='heading_skips')return'headings_group';return null;}\nfunction isHiddenEffective(issueKey){const g=groupKeyFor(issueKey);return isHidden(issueKey)||(g&&isHidden(g));}\nfunction renderTogglesFor(issues){const present=new Set(issues.map(i=>i.key));toggles.innerHTML='';const WRAP_BASE={display:'inline-flex',alignItems:'center',gap:'6px',background:'#f7f7f7',border:'1px solid #eaeaea',borderRadius:'3px',padding:'2px 3px',cursor:'pointer'};const tweakCheckbox=(cb)=>{cb.style.margin='0';cb.style.cursor='pointer';cb.style.verticalAlign='middle';cb.style.position='relative';cb.style.top='0px';};const styleSpan=(span)=>{Object.assign(span.style,{display:'inline-block',background:'transparent',padding:'0',borderRadius:'0',cursor:'pointer'});};GROUPS.forEach(g=>{if(!g.members.some(m=>present.has(m)))return;const id='audit-toggle-'+g.groupKey;const wrap=document.createElement('label');wrap.setAttribute('for',id);Object.assign(wrap.style,WRAP_BASE);const cb=document.createElement('input');cb.type='checkbox';cb.id=id;cb.checked=!isHidden(g.groupKey);cb.addEventListener('change',()=>{setHidden(g.groupKey,!cb.checked);try{const res=audit(current.doc);renderPanel(res,current.doc);}catch(_){}});tweakCheckbox(cb);const span=document.createElement('span');span.textContent=g.label;styleSpan(span);wrap.append(cb,span);toggles.appendChild(wrap);});CATS.forEach(cat=>{if(!present.has(cat.key))return;const id='audit-toggle-'+cat.key;const wrap=document.createElement('label');wrap.setAttribute('for',id);Object.assign(wrap.style,WRAP_BASE);const cb=document.createElement('input');cb.type='checkbox';cb.id=id;cb.checked=!isHidden(cat.key);cb.addEventListener('change',()=>{setHidden(cat.key,!cb.checked);try{const res=audit(current.doc);renderPanel(res,current.doc);}catch(_){}});tweakCheckbox(cb);const span=document.createElement('span');span.textContent=cat.label;styleSpan(span);wrap.append(cb,span);toggles.appendChild(wrap);});toggles.style.display=toggles.children.length?'flex':'none';}\npanel.insertBefore(toggles,content);(document.readyState==='loading'?document.addEventListener('DOMContentLoaded',()=>document.body.append(panel),{once:true}):document.body.append(panel));function makeDraggable(handleEl,moveEl){let drag=false,dx=0,dy=0;handleEl.addEventListener('mousedown',e=>{if(moveEl===panel&&e.target.classList.contains('audit-resizer'))return;drag=true;handleEl.style.cursor='grabbing';const r=moveEl.getBoundingClientRect();dx=e.clientX-r.left;dy=e.clientY-r.top;Object.assign(moveEl.style,{left:r.left+'px',top:r.top+'px',right:'auto',bottom:'auto'});document.addEventListener('mousemove',onMove);document.addEventListener('mouseup',onUp);});function onMove(e){if(!drag)return;Object.assign(moveEl.style,{left:(e.clientX-dx)+'px',top:(e.clientY-dy)+'px'});}\nfunction onUp(){drag=false;handleEl.style.cursor=(handleEl===btn)?'grab':'move';document.removeEventListener('mousemove',onMove);document.removeEventListener('mouseup',onUp);}}\nmakeDraggable(title,panel);makeDraggable(btn,btn);(function addSEResizer(box){const h=document.createElement('div');h.className='audit-resizer audit-resizer-se';Object.assign(h.style,{position:'absolute',width:'14px',height:'14px',right:'-2px',bottom:'-2px',cursor:'nwse-resize',zIndex:2147483647});box.appendChild(h);h.addEventListener('mousedown',e=>{e.preventDefault();e.stopPropagation();INTERACTING=true;const sx=e.clientX,sy=e.clientY,r=box.getBoundingClientRect();const start={left:r.left,top:r.top,width:r.width,height:r.height};const minW=360,minH=160,maxW=Math.min(900,window.innerWidth-20);const onMove=(e)=>{const dx=e.clientX-sx,dy=e.clientY-sy;Object.assign(box.style,{left:start.left+'px',top:start.top+'px',width:Math.max(minW,Math.min(maxW,start.width+dx))+'px',height:Math.max(minH,start.height+dy)+'px',right:'auto',bottom:'auto'});};const onUp=()=>{document.removeEventListener('mousemove',onMove);document.removeEventListener('mouseup',onUp);setTimeout(()=>INTERACTING=false,180);};document.addEventListener('mousemove',onMove);document.addEventListener('mouseup',onUp);});})(panel);function removeHeadingBadges(doc){const d=doc||document;d.querySelectorAll('.audit-heading-badge').forEach(n=>{try{n.remove();}catch(e){}});d.querySelectorAll('[data-audit-pos-mod]').forEach(h=>{try{h.style.position=h.getAttribute('data-audit-prev-pos')||'';h.removeAttribute('data-audit-pos-mod');h.removeAttribute('data-audit-prev-pos');}catch(e){}});}\nfunction addHeadingBadges(doc){const d=doc||document;removeHeadingBadges(d);const headings=d.querySelectorAll('h1,h2,h3,h4,h5,h6');headings.forEach(h=>{if(panel.contains(h)||btn.contains(h))return;const lvl=h.tagName.toUpperCase();const cs=(h.ownerDocument.defaultView||window).getComputedStyle(h);if(cs.position==='static'){h.setAttribute('data-audit-pos-mod','1');h.setAttribute('data-audit-prev-pos',h.style.position||'');h.style.position='relative';}\nconst b=d.createElement('span');b.className='audit-heading-badge';b.textContent=lvl;Object.assign(b.style,{position:'absolute',top:'-10px',right:'-10px',width:'24px',height:'24px',borderRadius:'50%',background:'#e53935BF',color:'#fff',display:'inline-flex',alignItems:'center',justifyContent:'center',fontSize:'14px',fontWeight:'400',fontFamily:'-apple-system, Segoe UI, Roboto, Arial, sans-serif',lineHeight:'14px',pointerEvents:'none',boxShadow:'0 0 0 2px rgba(255,255,255,0.9)',zIndex:2147483644,letterSpacing:'0.5px'});h.appendChild(b);});}\nfunction panelOpen(){return panel.style.display==='block';}\nfunction clearHighlights(doc){const d=doc||document;d.querySelectorAll('.audit-highlight').forEach(el=>{el.style.outline='';el.classList.remove('audit-highlight');});d.querySelectorAll('.audit-overlay').forEach(o=>o.remove());}\nfunction createOverlayFor(el,targetDoc,opts={}){const d=targetDoc||document,overlay=d.createElement('div');overlay.className='audit-overlay';Object.assign(overlay.style,{position:'fixed',zIndex:2147483644,background:'rgba(229,57,53,0.16)',boxShadow:'inset 0 0 0 3px rgba(229,57,53,1)',pointerEvents:'none',borderRadius:getComputedStyle(el).borderRadius||'0'});function place(){const r=el.getBoundingClientRect();overlay.style.left=r.left+'px';overlay.style.top=r.top+'px';overlay.style.width=Math.max(0,r.width)+'px';overlay.style.height=Math.max(0,r.height)+'px';overlay.style.display=(r.width>0&&r.height>0)?'block':'none';}\n(d.body||d.documentElement).appendChild(overlay);place();const win=d.defaultView||window;const onScroll=()=>place(),onResize=()=>place();win.addEventListener('scroll',onScroll,{passive:true});win.addEventListener('resize',onResize);const cleanup=()=>{overlay.remove();win.removeEventListener('scroll',onScroll);win.removeEventListener('resize',onResize);};if(opts.autoRemoveMs)setTimeout(cleanup,opts.autoRemoveMs);return cleanup;}\nfunction highlightOnce(el,doc){el.style.outline='5px dashed #e53935';el.classList.add('audit-highlight');createOverlayFor(el,doc,{autoRemoveMs:1600});setTimeout(()=>{try{el.style.outline='';el.classList.remove('audit-highlight');}catch(e){}},1600);}\nfunction findPreviewDoc(){const candidates=['iframe[name=\"editor-canvas\"]','iframe.elementor-iframe','iframe[src*=\"post.php\"]','iframe[src*=\"site-editor.php\"]','iframe[src*=\"block-editor\"]'];const iframe=document.querySelector(candidates.join(','))||document.querySelector('iframe');if(iframe){try{const fdoc=iframe.contentDocument||iframe.contentWindow.document;if(fdoc&&fdoc.body)return{doc:fdoc,iframe};}catch(e){}}\nreturn{doc:document,iframe:null};}\nfunction labelKind(el){let n=el;for(let i=0;i<6&&n;i++,n=n.parentElement){const id=(n.id||'').toLowerCase();const cls=(n.className||'').toString().toLowerCase();if(/elementor/.test(cls))return'builder:elementor';if(/bricks|brxe-/.test(cls))return'builder:bricks';if(/wp-block|block-editor|is-root-container/.test(cls))return'builder:gutenberg';if(id==='content'||/site-main|entry-content|theme|container/.test(cls))return'theme';}\nreturn'unknown';}\nfunction containerKeyFor(el){let n=el;while(n){if(n.nodeType!==1){n=n.parentElement;continue;}\nconst c=(n.className||'').toString();if(/elementor-section|elementor-top-section|elementor-container|e-con(\\s|$)/.test(c)||n.tagName==='SECTION'||n.tagName==='ARTICLE'||n.tagName==='ASIDE'){return n;}\nn=n.parentElement;}\nreturn null;}\nfunction srgbToLin(c){return(c<=0.04045)?c/12.92:Math.pow((c+0.055)/1.055,2.4);}\nfunction relativeLuminance(rgb){const r=srgbToLin(rgb[0]/255),g=srgbToLin(rgb[1]/255),b=srgbToLin(rgb[2]/255);return 0.2126*r+0.7152*g+0.0722*b;}\nfunction contrastRatio(fgRGB,bgRGB){const L1=relativeLuminance(fgRGB),L2=relativeLuminance(bgRGB);const lighter=Math.max(L1,L2),darker=Math.min(L1,L2);return(lighter+0.05)/(darker+0.05);}\nfunction parseColor(str){if(!str)return null;const s=str.trim();if(s.startsWith('rgb')){const m=s.match(/rgba?\\(([^)]+)\\)/i);if(!m)return null;const p=m[1].split(',').map(v=>v.trim());const r=+p[0],g=+p[1],b=+p[2];const a=p[3]!==undefined?parseFloat(p[3]):1;return{r,g,b,a};}else if(s[0]==='#'){let h=s.slice(1);if(h.length===3)h=h.split('').map(c=>c+c).join('');if(h.length===6){return{r:parseInt(h.slice(0,2),16),g:parseInt(h.slice(2,4),16),b:parseInt(h.slice(4,6),16),a:1};}}\nreturn null;}\nfunction rgbToHex(rgb){const to2=n=>n.toString(16).padStart(2,'0');return'#'+to2(rgb[0])+to2(rgb[1])+to2(rgb[2]);}\nfunction mixRGB(a,b,t){return[Math.round(a[0]*(1-t)+b[0]*t),Math.round(a[1]*(1-t)+b[1]*t),Math.round(a[2]*(1-t)+b[2]*t)];}\nfunction hasRenderedBackgroundImage(cs){const img=(cs&&cs.backgroundImage||'').trim();return!!img&&img!=='none';}\nfunction pseudoCreatesComplexBackground(el,win){for(const pseudo of ['::before','::after']){try{const ps=win.getComputedStyle(el,pseudo);if(!ps)continue;const content=(ps.content||'').trim();if(content==='none'||content==='normal')continue;if(hasRenderedBackgroundImage(ps))return true;const bg=parseColor(ps.backgroundColor);const positioned=/absolute|fixed|sticky/.test(ps.position||'');const visible=ps.display!=='none'&&ps.visibility!=='hidden'&&(parseFloat(ps.opacity||'1')>0);if(visible&&positioned&&bg&&bg.a>0&&bg.a<1)return true;}catch(_){}}return false;}\nfunction overlappingMediaBackground(el){try{const r=el.getBoundingClientRect();let p=el.parentElement;for(let depth=0;p&&depth<6;depth++,p=p.parentElement){const media=p.querySelectorAll(':scope > img,:scope > picture,:scope > video,:scope > canvas,:scope > iframe,.elementor-background-video-container,.elementor-background-slideshow');for(const m of media){if(m===el||m.contains(el)||el.contains(m))continue;const mr=m.getBoundingClientRect();const overlap=mr.right>r.left&&mr.left<r.right&&mr.bottom>r.top&&mr.top<r.bottom;if(overlap)return true;}}}catch(_){}return false;}\nfunction backgroundContext(el,win,startAtSelf=true){let n=startAtSelf?el:el.parentElement;let layers=[];while(n){const cs=win.getComputedStyle(n);if(hasRenderedBackgroundImage(cs)||pseudoCreatesComplexBackground(n,win)){return{rgb:null,source:'image-or-gradient',confidence:'manual',element:n};}\nconst bg=parseColor(cs.backgroundColor);if(bg&&bg.a>=0.999){let rgb=[bg.r,bg.g,bg.b];for(let i=layers.length-1;i>=0;i--){const l=layers[i];rgb=[Math.round(l.rgb[0]*l.a+rgb[0]*(1-l.a)),Math.round(l.rgb[1]*l.a+rgb[1]*(1-l.a)),Math.round(l.rgb[2]*l.a+rgb[2]*(1-l.a))];}return{rgb,source:'solid-colour',confidence:'high',element:n};}\nif(bg&&bg.a>0)layers.push({rgb:[bg.r,bg.g,bg.b],a:bg.a});n=n.parentElement;}\nif(overlappingMediaBackground(el))return{rgb:null,source:'overlapping-media',confidence:'manual',element:null};\nlet rgb=[255,255,255];for(let i=layers.length-1;i>=0;i--){const l=layers[i];rgb=[Math.round(l.rgb[0]*l.a+rgb[0]*(1-l.a)),Math.round(l.rgb[1]*l.a+rgb[1]*(1-l.a)),Math.round(l.rgb[2]*l.a+rgb[2]*(1-l.a))];}\nreturn{rgb,source:layers.length?'transparent-composite':'page-default',confidence:layers.length?'medium':'low',element:null};}\nfunction effectiveBGInfo(el,win){return backgroundContext(el,win,true);}\nfunction effectiveBG(el,win){const info=effectiveBGInfo(el,win);return info.rgb||[255,255,255];}\nfunction effectiveFG(el,win){const cs=win.getComputedStyle(el);const c=parseColor(cs.color);if(c&&(c.a===undefined||c.a>0))return[c.r,c.g,c.b];return null;}\nfunction px(n){return parseFloat(String(n||'').replace('px',''))||0;}\nfunction isLargeText(cs){const size=px(cs.fontSize);const weight=parseInt(cs.fontWeight,10)||400;const bold=weight>=700;return(size>=24)||(bold&&size>=18.66);}\nfunction isUIComponent(el,cs){const tag=el.tagName;if(tag==='BUTTON'||tag==='INPUT'||tag==='SELECT'||tag==='TEXTAREA')return true;if(el.getAttribute('role')==='button')return true;if(tag==='A')return true;const cls=(el.className||'').toString().toLowerCase();if(/\\bbutton\\b|\\bbtn\\b|elementor-button|wp-block-button/.test(cls))return true;if((tag==='SVG'||tag==='I')&&el.closest('a,button,[role=\"button\"]'))return true;return false;}\nfunction isButtonLike(el,cs){const tag=el.tagName;if(tag==='INPUT'&&(el.type||'').toLowerCase()==='hidden')return false;if(tag==='BUTTON'||tag==='INPUT'||tag==='SELECT'||tag==='TEXTAREA')return true;if(el.getAttribute('role')==='button')return true;const cls=(el.className||'').toString().toLowerCase();if(tag==='A'&&isInMenuContext(el))return true;if(/\\bbutton\\b|\\bbtn\\b|elementor-button|wp-block-button__link/.test(cls))return true;if(tag==='A'){const disp=cs.display;const looksLikeIconLink=el.classList.contains('elementor-icon')||el.classList.contains('elementor-social-icon')||(!!el.querySelector('svg,img,i'));if(looksLikeIconLink&&(disp==='inline'||disp==='inline-block'||disp==='block'||disp==='flex'||disp==='inline-flex')){const r=el.getBoundingClientRect();if(r.width>=16&&r.height>=16)return true;}\nconst hasPadding=['paddingTop','paddingRight','paddingBottom','paddingLeft'].some(p=>(parseFloat(cs[p])||0)>2);if((disp==='inline-block'||disp==='block'||disp==='flex'||disp==='inline-flex')&&hasPadding)return true;}\nif((tag==='SVG'||tag==='I')&&el.closest('a,button,[role=\"button\"]'))return true;return false;}\nfunction nearestAccessibleColor(fgRGB,bgRGB,required){const black=[0,0,0],white=[255,255,255];let best=null;for(let t=0;t<=1.0001;t+=0.01){const c=mixRGB(fgRGB,black,t);if(contrastRatio(c,bgRGB)>=required){best={rgb:c,t};break;}}\nlet bestW=null;for(let t=0;t<=1.0001;t+=0.01){const c=mixRGB(fgRGB,white,t);if(contrastRatio(c,bgRGB)>=required){bestW={rgb:c,t};break;}}\nif(best&&bestW)return(best.t<=bestW.t)?best:bestW;else return best||bestW||((contrastRatio([0,0,0],bgRGB)>=contrastRatio([255,255,255],bgRGB))?{rgb:[0,0,0],t:1}:{rgb:[255,255,255],t:1});}\nfunction nearestAccessibleBackground(fgRGB,bgRGB,required){const black=[0,0,0],white=[255,255,255];let best=null;for(let t=0;t<=1.0001;t+=0.01){const c=mixRGB(bgRGB,black,t);if(contrastRatio(fgRGB,c)>=required){best={rgb:c,t};break;}}\nlet bestW=null;for(let t=0;t<=1.0001;t+=0.01){const c=mixRGB(bgRGB,white,t);if(contrastRatio(fgRGB,c)>=required){bestW={rgb:c,t};break;}}\nif(best&&bestW)return(best.t<=bestW.t)?best:bestW;if(best)return best;if(bestW)return bestW;return(contrastRatio(fgRGB,[0,0,0])>=contrastRatio(fgRGB,[255,255,255]))?{rgb:[0,0,0],t:1}:{rgb:[255,255,255],t:1};}\nconst IGNORE_UI=['#wpadminbar','#adminmenu','.wp-toolbar','.update-nag','.interface-interface-skeleton','.edit-post-layout','.edit-post-header','.block-editor','.block-editor__typewriter','.components-popover','.elementor-panel','.elementor-editor-dialog','.elementor-navigator','.bricks-frontend-editor','.brxe-iframe-toolbar','.oxygen-ui'].join(',');function isIgnoredChrome(el){return!!el.closest(IGNORE_UI);}\nfunction isBuilderChrome(el){const body=el.ownerDocument&&el.ownerDocument.body;const bodyCls=(body&&body.className||'').toString().toLowerCase();const inElementor=/\\belementor-editor-active\\b/.test(bodyCls);const inBricks=/\\bbricks-builder\\b|\\bbrxe-editor\\b/.test(bodyCls);const inGuten=/\\bblock-editor\\b/.test(bodyCls);if(!(inElementor||inBricks||inGuten))return false;const cls=(el.className||'').toString().toLowerCase();const looksLikeOverlay=/(overlay|handle|resizer|draggable|sortable|ui|tooltip|context|ghost)/.test(cls);const looksLikeAdd=/(add-|^add$|empty|placeholder|dropzone)/.test(cls);const looksLikeEditor=/(editor|control|navigator|panel|inspector)/.test(cls);const matchesSpecific=el.closest('.elementor-element-overlay,.elementor-editor-element-settings,.elementor-add-section,.elementor-add-column,.elementor-widget-empty,.elementor-empty-view')||el.closest('.brxe-controls,.brxe-toolbar,.bricks-element-overlay');return!!(looksLikeOverlay||looksLikeAdd||looksLikeEditor||matchesSpecific);}\nfunction surroundingBGInfo(el,win){return backgroundContext(el,win,false);}\nfunction surroundingBG(el,win){const info=surroundingBGInfo(el,win);return info.rgb||[255,255,255];}\nfunction hasOwnFill(cs){const bg=parseColor(cs.backgroundColor);return!!(bg&&bg.a>0);}\nfunction visibleBorder(cs){const widths=['Top','Right','Bottom','Left'].map(side=>parseFloat(cs['border'+side+'Width'])||0);const styles=['Top','Right','Bottom','Left'].map(side=>cs['border'+side+'Style']||'none');const colors=['Top','Right','Bottom','Left'].map(side=>parseColor(cs['border'+side+'Color']));const anyIndex=widths.findIndex((w,i)=>w>0.5&&styles[i]!=='none'&&colors[i]&&(colors[i].a===undefined||colors[i].a>0));if(anyIndex===-1)return null;const c=colors[anyIndex];return[c.r,c.g,c.b];}\nfunction nearestContrastingBackground(bgRGB,adjRGB,required){const black=[0,0,0],white=[255,255,255];let best=null,bestT=Infinity;for(let t=0;t<=1.0001;t+=0.01){const c=mixRGB(bgRGB,black,t);if(contrastRatio(c,adjRGB)>=required){best=c;bestT=t;break;}}\nfor(let t=0;t<=1.0001;t+=0.01){const c=mixRGB(bgRGB,white,t);if(contrastRatio(c,adjRGB)>=required){if(t<bestT){best=c;bestT=t;}\nbreak;}}\nif(!best){const k=contrastRatio([0,0,0],adjRGB);const w=contrastRatio([255,255,255],adjRGB);best=(k>=w)?[0,0,0]:[255,255,255];}\nreturn{rgb:best,t:bestT};}\nfunction isMobileView(win){try{return win.matchMedia&&win.matchMedia('(max-width: 767.98px)').matches;}catch(e){return win.innerWidth<=768;}}\nfunction effectiveTargetRect(el){const rect=el.getBoundingClientRect();if(rect.width>0&&rect.height>0)return rect;const desc=el.querySelector('a,button,[role=\"button\"],input:not([type=\"hidden\"]),select,textarea,.button,.btn,.elementor-button,.wp-block-button__link');if(desc){const r2=desc.getBoundingClientRect();if(r2.width>0&&r2.height>0)return r2;}\nreturn rect;}\nfunction rectDistance(r1,r2){const dx=Math.max(0,Math.max(r1.left,r2.left)-Math.min(r1.right,r2.right));const dy=Math.max(0,Math.max(r1.top,r2.top)-Math.min(r1.bottom,r2.bottom));return Math.hypot(dx,dy);}\nfunction labelFor(el){const txt=(el.innerText||el.textContent||'').trim().replace(/\\s+/g,' ');if(txt)return txt.slice(0,80)+(txt.length>80?'…':'');const alt=el.getAttribute?.('aria-label')||el.getAttribute?.('alt')||el.getAttribute?.('title');return(alt||'').trim();}\nfunction isInMenuContext(el){const MENU_CTX=['nav','[role=\"navigation\"]','header .menu','footer .menu','.site-header .menu','.site-footer .menu','.main-navigation','.site-navigation','.navbar','.menu','.menu-container','.menu-wrap','.menu-items','.menu-list','.menu-primary','.menu-secondary','.menu-top','.menu-bottom','.footer-menu','.header-menu','.nav-menu','.navigation','.wp-block-navigation','.wp-block-navigation__container','.elementor-widget-nav-menu','.elementor-nav-menu','.elementor-menu','.elementor-nav','.brxe-nav-menu','.bricks-nav-menu','.bricks-mobile-menu','.mega-menu','.megamenu','.tt-mega-menu','.max-mega-menu'].join(',');return!!el.closest(MENU_CTX);}\nfunction isMenuToggle(el){const cls=(el.className||'').toString().toLowerCase();if(/\\bmenu-toggle\\b|\\belementor-menu-toggle\\b|\\bnav-toggle\\b|\\bhamburger\\b/.test(cls))return true;if(el.matches?.('[aria-controls*=\"menu\"],[aria-haspopup=\"menu\"]'))return true;return false;}\nfunction isSkipLink(el){if(!el||(el.tagName||'').toLowerCase()!=='a')return false;const cls=(el.className||'').toString().toLowerCase();const href=(el.getAttribute('href')||'').trim();if(/\\bskip[-_ ]?link\\b|\\bskip[-_ ]?to[-_ ]?content\\b|\\bscreen-reader-shortcut\\b/.test(cls))return true;if(/^#(main|content|primary|page|skip|site-main)$/i.test(href))return true;if(/\\bscreen-reader-text\\b|\\bsr-only\\b|\\bvisually-hidden\\b/.test(cls))return true;return false;}\nfunction getTabbables(doc){const d=doc,win=d.defaultView||window;const nodes=Array.from(d.querySelectorAll('a[href],button,input,select,textarea,[tabindex]')).filter(el=>{const cs=win.getComputedStyle(el);if(cs.display==='none'||cs.visibility==='hidden'||parseFloat(cs.opacity)===0)return false;if(el.hasAttribute('disabled'))return false;const ti=parseInt(el.getAttribute('tabindex')||'0',10);return!Number.isNaN(ti)&&ti>=0;});return nodes;}\nfunction collectSkipLinkIssue(doc){const d=doc;const win=d.defaultView||window;const links=Array.from(d.querySelectorAll('a')).filter(isSkipLink);const skip=links[0]||null;if(!skip){return{key:'skip_link',title:'ACCESSIBILITY: “Skip to content” link is missing',message:'Add a keyboard-accessible “Skip to content” link as the first focusable element.',fixes:['Place <code>&lt;a class=\"skip-link\" href=\"#main\"&gt;Skip to content&lt;/a&gt;</code> as the first focusable item inside <code>&lt;body&gt;</code>.','Ensure the target exists: <code>&lt;main id=\"main\"&gt;</code> (or <code>role=\"main\"</code>).','Make it visible on focus only (CSS below).','CSS: <code>.skip-link{position:absolute;left:-9999px;top:auto;width:1px;height:1px;overflow:hidden;} .skip-link:focus{left:8px;top:8px;width:auto;height:auto;padding:.5rem 1rem;background:#fff;border:2px solid #000;z-index:100000;}</code>'],elements:[]};}\nconst href=(skip.getAttribute('href')||'').trim();const targetId=href.startsWith('#')?href.slice(1):'';const target=targetId?d.getElementById(targetId):null;const els=[];const isMainLike=!!(target&&(target.tagName?.toLowerCase()==='main'||target.getAttribute('role')==='main'||/^(main|content|primary|page|site-main)$/i.test(targetId)));if(!target){els.push({el:skip,kind:labelKind(skip),role:'a',issue:'skiplink',sub:'target-missing',hint:`The href points to “${href||'(none)'}”, but that target isn’t in the DOM.`});}else if(!isMainLike){els.push({el:target,kind:labelKind(target),role:(target.tagName||'').toLowerCase(),issue:'skiplink',sub:'target-not-main',hint:`Target “#${targetId}” exists but isn’t a <main> or role=\"main\".`});}\nconst tabbables=getTabbables(d);const idx=tabbables.indexOf(skip);if(idx===-1||idx>2){els.push({el:skip,kind:labelKind(skip),role:'a',issue:'skiplink',sub:'late',index:(idx>=0?idx+1:'unknown'),hint:'Make the skip link the first focusable element in the page.'});}\nconst cs=win.getComputedStyle(skip);const offscreenLikely=/clip:|clip-path:/.test(skip.getAttribute('style')||'')||((cs.position==='absolute'||cs.position==='fixed')&&((parseFloat(cs.left)||0)<=-500||(parseFloat(cs.right)||0)<=-500));if(offscreenLikely){els.push({el:skip,kind:labelKind(skip),role:'a',issue:'skiplink',sub:'maybe-not-visible-on-focus',hint:'Ensure the skip link becomes visible on :focus (reset left/top/clip; add padding/background/border).'});}\nif(els.length===0){els.push({el:skip,kind:labelKind(skip),role:'a',issue:'skiplink',sub:'ok',hint:'Skip link present and appears correctly placed.'});}\nreturn{key:'skip_link',title:'ACCESSIBILITY: “Skip to content”',message:'Checks presence, target, and early tab-order placement.',fixes:['Place the link first in tab order (ideally first in <code>&lt;body&gt;</code>).','Point it to a real main region: <code>&lt;main id=\"main\"&gt;</code> (or role=\"main\").','Ensure it is hidden visually but visible on <code>:focus</code>.',],elements:els};}\nconst EXCLUDE_SMALLTEXT='.elementor-item.elementor-item-anchor, .elementor-item.elementor-item-anchor *';function isExcludedSmallText(el){const a=el.closest('.elementor-item.elementor-item-anchor');if(!a)return false;return!isInMenuContext(a);}\nconst MIN_TEXT_PX=16;const HEADING_WORD_LIMIT=12;const HEADING_CHAR_LIMIT=80;function collectContrastFailures(doc){const d=doc;const win=d.defaultView||window;const selectors=['h1,h2,h3,h4,h5,h6','p,li,span,small,em,strong,blockquote,figcaption,label','a,button,[role=\"button\"],.button,.btn,.elementor-button,.wp-block-button__link','svg,svg *','i'].join(',');const raw=Array.from(d.querySelectorAll(selectors)).filter(el=>!panel.contains(el)&&!btn.contains(el)).filter(el=>!el.classList?.contains('audit-heading-badge')&&!el.closest?.('.audit-heading-badge')).filter(el=>!isIgnoredChrome(el)).filter(el=>!isBuilderChrome(el)).filter(el=>!isExcludedSmallText(el));const elements=raw.filter(el=>{const head=el.closest('h1,h2,h3,h4,h5,h6');if(head&&el!==head)return false;const ctl=el.closest('a,button,[role=\"button\"],.button,.btn,.elementor-button,.wp-block-button__link');if(ctl&&el!==ctl&&el.tagName?.toLowerCase()!=='svg')return false;if(el.tagName&&el.tagName.toLowerCase()!=='svg'&&el.closest('svg'))return false;return true;});const fails=[];elements.forEach(el=>{try{const cs=win.getComputedStyle(el);if(cs.display==='none'||cs.visibility==='hidden'||parseFloat(cs.opacity)===0)return;const fg=effectiveFG(el,win);if(!fg)return;const bgInfo=effectiveBGInfo(el,win);const threshold=isLargeText(cs)?3.0:4.5;if(bgInfo.rgb){const bg=bgInfo.rgb;const ratio=contrastRatio(fg,bg);if(ratio+1e-6<threshold){const fgFix=nearestAccessibleColor(fg,bg,threshold);const bgFix=nearestAccessibleBackground(fg,bg,threshold);fails.push({el,kind:labelKind(el),role:el.tagName.toLowerCase(),contrastType:'text-vs-bg',ratio:Number(ratio.toFixed(2)),required:threshold,fontSize:px(cs.fontSize).toFixed(1),fontWeight:parseInt(cs.fontWeight,10)||400,fgHex:rgbToHex(fg),bgHex:rgbToHex(bg),suggestFG:rgbToHex(fgFix.rgb),suggestBG:rgbToHex(bgFix.rgb),confidence:bgInfo.confidence,backgroundSource:bgInfo.source});}}else{fails.push({el,kind:labelKind(el),role:el.tagName.toLowerCase(),contrastType:'manual-text-bg',required:threshold,fontSize:px(cs.fontSize).toFixed(1),fontWeight:parseInt(cs.fontWeight,10)||400,fgHex:rgbToHex(fg),backgroundSource:bgInfo.source,manualReason:'Text sits over an image, gradient, video, canvas, iframe, or layered background.'});}\nif(isUIComponent(el,cs)){const adjInfo=surroundingBGInfo(el,win);const hasFill=hasOwnFill(cs);const borderRGB=visibleBorder(cs);const ownBG=parseColor(cs.backgroundColor);const controlBG=ownBG&&ownBG.a>0?[ownBG.r,ownBG.g,ownBG.b]:(bgInfo.rgb||null);if(!adjInfo.rgb){if(hasFill||borderRGB){fails.push({el,kind:labelKind(el),role:el.tagName.toLowerCase()+(hasFill?' (control background)':' (control border)'),contrastType:'manual-control-bg',required:3.0,bgHex:controlBG?rgbToHex(controlBG):'',borderHex:borderRGB?rgbToHex(borderRGB):'',backgroundSource:adjInfo.source,manualReason:'The surrounding area uses an image, gradient, video, canvas, iframe, pseudo-element, or layered background, so a reliable pixel contrast ratio cannot be calculated from CSS alone.'});}}else{const adj=adjInfo.rgb;if(hasFill&&controlBG){const bgContrast=contrastRatio(controlBG,adj);if(bgContrast+1e-6<3.0){const bgFix2=nearestContrastingBackground(controlBG,adj,3.0);fails.push({el,kind:labelKind(el),role:el.tagName.toLowerCase()+' (control background)',contrastType:'bg-vs-adj',ratio:Number(bgContrast.toFixed(2)),required:3.0,fontSize:px(cs.fontSize).toFixed(1),fontWeight:parseInt(cs.fontWeight,10)||400,bgHex:rgbToHex(controlBG),adjHex:rgbToHex(adj),suggestFG:'',suggestBG:rgbToHex(bgFix2.rgb),confidence:adjInfo.confidence,backgroundSource:adjInfo.source});}}else if(borderRGB){const bContrast=contrastRatio(borderRGB,adj);if(bContrast+1e-6<3.0){const bgFix2=nearestContrastingBackground(borderRGB,adj,3.0);fails.push({el,kind:labelKind(el),role:el.tagName.toLowerCase()+' (control border)',contrastType:'border-vs-adj',ratio:Number(bContrast.toFixed(2)),required:3.0,fontSize:px(cs.fontSize).toFixed(1),fontWeight:parseInt(cs.fontWeight,10)||400,borderHex:rgbToHex(borderRGB),adjHex:rgbToHex(adj),suggestFG:'',suggestBG:rgbToHex(bgFix2.rgb),confidence:adjInfo.confidence,backgroundSource:adjInfo.source});}}}}}catch(e){}});const seenManual=new Set();return fails.filter(f=>{if(!/^manual-/.test(f.contrastType))return true;const key=(f.el&&f.el.tagName)+'|'+f.contrastType+'|'+f.backgroundSource;if(seenManual.has(key))return false;seenManual.add(key);return true;});}\nfunction collectSmallTextFailures(doc){const d=doc;const win=d.defaultView||window;const selectors=['p,li,blockquote,figcaption,dd,dt,pre','h1,h2,h3,h4,h5,h6','.wp-block-paragraph,.wp-block-list,.elementor-widget-text-editor','span,a,em,strong,small'].join(',');const _px=v=>parseFloat(String(v||'').replace('px',''))||0;function isTextContainer(el){const t=(el.tagName||'').toLowerCase();if(/^(p|li|blockquote|figcaption|dd|dt|pre)$/.test(t))return true;if(/^h[1-6]$/.test(t))return true;const cls=(el.className||'').toString().toLowerCase();if(/\\bwp-block-paragraph\\b|\\bwp-block-list\\b|\\belementor-widget-text-editor\\b/.test(cls))return true;return false;}\nfunction bubbleToTextContainer(el,size){let n=el;for(let i=0;i<6&&n&&n.parentElement;i++){const p=n.parentElement;const pcs=win.getComputedStyle(p);if(isIgnoredChrome(p)||isBuilderChrome(p))break;if(_px(pcs.fontSize)!==size)break;n=p;}\nreturn isTextContainer(n)?n:el;}\nconst raw=Array.from(d.querySelectorAll(selectors)).filter(el=>!panel.contains(el)&&!btn.contains(el)).filter(el=>!el.classList?.contains('audit-heading-badge')&&!el.closest?.('.audit-heading-badge')).filter(el=>!isIgnoredChrome(el)).filter(el=>!isBuilderChrome(el));const seen=new Set();const fails=[];raw.forEach(el=>{try{const cs=win.getComputedStyle(el);if(cs.display==='none'||cs.visibility==='hidden'||parseFloat(cs.opacity)===0)return;const heading=/^h[1-6]$/i.test(el.tagName||'');const buttonLike=isButtonLike(el,cs);if(isInMenuContext(el)&&!heading&&!buttonLike)return;const txt=(el.innerText||el.textContent||'').trim().replace(/\\s+/g,' ');if(!txt)return;const r=el.getBoundingClientRect();if(!heading&&!buttonLike){if((r.width<32&&txt.length<12)||r.height<10)return;}\nconst size=_px(cs.fontSize);if(!(size>0))return;let rep=el;if(buttonLike){rep=el.closest('a,button,[role=\"button\"],.button,.btn,.elementor-button,.wp-block-button__link')||el;}else{rep=bubbleToTextContainer(el,size);}\nif(isExcludedSmallText(rep))return;if(seen.has(rep))return;const repCS=win.getComputedStyle(rep);const repSize=_px(repCS.fontSize);if(repSize>0&&repSize<MIN_TEXT_PX){seen.add(rep);fails.push({el:rep,kind:labelKind(rep),role:(rep.tagName||'').toLowerCase()||'node',issue:'small-text',fontSize:Number(repSize.toFixed(1)),required:MIN_TEXT_PX});}}catch(e){}});return fails;}\nfunction collectControlMinSizeFailures(doc){const d=doc;const win=d.defaultView||window;const selectors='a,button,[role=\"button\"],input,select,textarea,.button,.btn,.elementor-button,.wp-block-button__link';const raw=Array.from(d.querySelectorAll(selectors)).filter(el=>!panel.contains(el)&&!btn.contains(el)).filter(el=>!isIgnoredChrome(el)).filter(el=>!isBuilderChrome(el));const elements=raw.filter(el=>{const ctl=el.closest('a,button,[role=\"button\"],input,select,textarea,.button,.btn,.elementor-button,.wp-block-button__link');return!ctl||ctl===el;});const fails=[];elements.forEach(el=>{try{const cs=win.getComputedStyle(el);const type=(el.type||'').toLowerCase();if(type==='hidden')return;if(el.getAttribute('aria-hidden')==='true')return;if(cs.pointerEvents==='none')return;if(!isButtonLike(el,cs))return;if(cs.display==='none'||cs.visibility==='hidden'||parseFloat(cs.opacity)===0)return;const hasText=(el.innerText||'').trim().length>0;const hasIcon=el.querySelector('svg,img,canvas,svg *')!=null;const bg=parseColor(cs.backgroundColor);const hasBg=!!(bg&&bg.a>0);const bCol=visibleBorder(cs);const hasBorder=!!bCol;if(!hasText&&!hasIcon&&!hasBg&&!hasBorder){return;}\nconst r=effectiveTargetRect(el);if(r.width<=0||r.height<=0)return;const w=Math.round(r.width);const h=Math.round(r.height);if(w<24||h<24){fails.push({el,kind:labelKind(el),role:el.tagName.toLowerCase(),width:w,height:h,requiredW:24,requiredH:24});}}catch(e){}});return fails;}\nfunction collectMobileControlSpacingFailures(doc){const d=doc;const win=d.defaultView||window;if(!isMobileView(win))return[];const selectors='a,button,[role=\"button\"],input,select,textarea,.button,.btn,.elementor-button,.wp-block-button__link';const nodes=Array.from(d.querySelectorAll(selectors)).filter(el=>!panel.contains(el)&&!btn.contains(el)).filter(el=>!isIgnoredChrome(el)).filter(el=>!isBuilderChrome(el)).filter(el=>!(isInMenuContext(el)&&!isMenuToggle(el)));const controls=[];nodes.forEach(el=>{try{const cs=win.getComputedStyle(el);if(cs.display==='none'||cs.visibility==='hidden'||parseFloat(cs.opacity)===0)return;if(cs.pointerEvents==='none')return;if((el.type||'').toLowerCase()==='hidden')return;if(!isButtonLike(el,cs))return;const r=effectiveTargetRect(el);if(r.width<=0||r.height<=0)return;controls.push({el,rect:r,kind:labelKind(el),role:el.tagName.toLowerCase(),text:labelFor(el)});}catch(e){}});const fails=[];const REQUIRED=12;for(let i=0;i<controls.length;i++){for(let j=i+1;j<controls.length;j++){const a=controls[i],b=controls[j];const dist=rectDistance(a.rect,b.rect);const aInMenu=isInMenuContext(a.el)&&!isMenuToggle(a.el);const bInMenu=isInMenuContext(b.el)&&!isMenuToggle(b.el);if(aInMenu&&bInMenu)continue;if(dist+1e-6<REQUIRED){fails.push({el:a.el,kind:a.kind,role:a.role,spaceType:'mobile-spacing',dist:Math.round(dist),required:REQUIRED,partnerText:b.text||b.role});}}}\nreturn fails;}\nfunction collectCTACandidates(doc){const d=doc;const win=d.defaultView||window;const nodes=Array.from(d.querySelectorAll('a,button,[role=\"button\"],.button,.btn,.elementor-button,.wp-block-button__link')).filter(el=>!panel.contains(el)&&!btn.contains(el)).filter(el=>!isIgnoredChrome(el)).filter(el=>!isBuilderChrome(el)).filter(el=>!isInMenuContext(el)).filter(el=>!isSkipLink(el));const ctas=[];nodes.forEach(el=>{try{const cs=win.getComputedStyle(el);if(cs.display==='none'||cs.visibility==='hidden'||parseFloat(cs.opacity)===0)return;if(cs.pointerEvents==='none')return;if(!isButtonLike(el,cs))return;const r=effectiveTargetRect(el);if(r.width<=0||r.height<=0)return;const hasIcon=!!el.querySelector('svg,img,i,svg *');const hasBG=!!parseColor(cs.backgroundColor)?.a;const hasBorder=!!visibleBorder(cs);const hasPad=['paddingTop','paddingRight','paddingBottom','paddingLeft'].some(p=>(parseFloat(cs[p])||0)>4);const looksActiony=hasIcon||hasBG||hasBorder||hasPad||/button|btn|wp-block-button__link|elementor-button/.test((el.className||'').toString().toLowerCase());if(!looksActiony)return;ctas.push({el,kind:labelKind(el)});}catch(_){}});return ctas;}\nfunction collectLongHeadings(doc){const d=doc;const win=d.defaultView||window;const out=[];const heads=Array.from(d.querySelectorAll('h1,h2,h3,h4,h5,h6')).filter(el=>!panel.contains(el)&&!btn.contains(el)).filter(el=>!isIgnoredChrome(el)).filter(el=>!isBuilderChrome(el));heads.forEach(h=>{try{const cs=win.getComputedStyle(h);if(cs.display==='none'||cs.visibility==='hidden'||parseFloat(cs.opacity)===0)return;let raw=h.innerText||h.textContent||'';const badge=h.querySelector('.audit-heading-badge');if(badge){const badgeText=badge.innerText||badge.textContent||'';raw=raw.replace(badgeText,'').trim();}\nconst norm=raw.replace(/[\\u200B\\u00AD]/g,'').replace(/[\\s\\u00A0\\u1680\\u180E\\u2000-\\u200A\\u2028\\u2029\\u202F\\u205F\\u3000\\n\\r\\t]+/g,' ').trim();if(!norm)return;const tokens=norm.split(' ').filter(t=>t);const wordCount=tokens.length;const charCount=norm.length;if(wordCount>HEADING_WORD_LIMIT||charCount>HEADING_CHAR_LIMIT){out.push({el:h,kind:labelKind(h),role:(h.tagName||'').toLowerCase(),issue:'heading-long',level:parseInt((h.tagName||'').slice(1),10)||null,words:wordCount,chars:charCount,limitWords:HEADING_WORD_LIMIT,limitChars:HEADING_CHAR_LIMIT,});}}catch(e){console.error('Error processing heading:',e);}});return out;}\nfunction isModernImageURL(url){if(!url)return true;const u=String(url).split('#')[0].split('?')[0].toLowerCase().trim();if(!u||u.startsWith('data:'))return true;return u.endsWith('.webp')||u.endsWith('.avif');}\nfunction collectNonModernImages(doc){const out=[];const d=doc;const imgs=Array.from(d.querySelectorAll('img'));for(const img of imgs){try{const raw=(img.currentSrc||img.getAttribute('src')||'').trim();if(!isModernImageURL(raw)){out.push({el:img,kind:labelKind(img),role:'img',issue:'nonmodern',src:raw});}}catch(e){}}\nconst svgImgs=Array.from(d.querySelectorAll('svg image'));for(const si of svgImgs){try{const href=si.getAttribute('href')||si.getAttributeNS('http://www.w3.org/1999/xlink','href')||'';if(!isModernImageURL(href)){const svg=si.closest('svg')||si;out.push({el:svg,kind:labelKind(svg),role:'svg-image',issue:'nonmodern',src:href});}}catch(e){}}\nreturn out;}\nfunction linkHasAccessibleName(a){const txt=(a.innerText||'').trim();if(txt)return true;const aria=(a.getAttribute('aria-label')||a.getAttribute('title')||'').trim();if(aria)return true;const labelledby=(a.getAttribute('aria-labelledby')||'').trim();if(labelledby){const el=a.ownerDocument.getElementById(labelledby);if(el&&(el.innerText||el.textContent||'').trim())return true;}\nreturn false;}\nfunction getByIdText(doc,id){if(!id)return'';const el=doc.getElementById(id);return(el&&(el.innerText||el.textContent)||'').trim();}\nfunction hasAriaName(el,doc){const aria=(el.getAttribute('aria-label')||'').trim();if(aria)return true;const labelledby=(el.getAttribute('aria-labelledby')||'').trim();if(labelledby){const txt=getByIdText(doc,labelledby);if(txt)return true;}\nreturn false;}\nfunction getAccessibleName(el,doc){const labelledby=(el.getAttribute('aria-labelledby')||'').trim();if(labelledby){const txt=getByIdText(doc,labelledby);if(txt)return txt;}\nconst aria=(el.getAttribute('aria-label')||'').trim();if(aria)return aria;const tag=(el.tagName||'').toLowerCase();if(tag==='img'){const alt=(el.getAttribute('alt')||'').trim();if(alt)return alt;}\nconst txt=(el.innerText||'').trim();if(txt)return txt;const title=(el.getAttribute('title')||'').trim();if(title)return title;return'';}\nfunction hasSpecificContext(name){const n=name.toLowerCase().replace(/\\s+/g,' ').trim();if(/\\b(about|for|on|to|in|of|the|this|that|these|those|pricing|product|services|blog|article|video|case|study|guide|docs|contact|support)\\b/.test(n)&&n.split(' ').length>=3){return true;}\nif(/^(read|learn|see|view)\\s+more\\b.*\\S/.test(n))return true;if(/^(view|see)\\s+(details|all)\\b.*\\S/.test(n))return true;return false;}\nconst VAGUE_CTA=new Set(['click here','read more','learn more','see more','view more','details','view details','info','view','go','continue','submit','start','try','try now','get started']);function isVagueName(name){const n=name.toLowerCase().replace(/\\s+/g,' ').trim();if(VAGUE_CTA.has(n))return true;if(/^(read|learn|see|view)\\s+more[.!>\\-–—\\s]*$/.test(n))return true;if(/^(view\\s+details)[.!>\\-–—\\s]*$/.test(n))return true;return false;}\nfunction isIconOnlyControl(el){const txt=(el.innerText||el.textContent||'').replace(/\\s+/g,'').trim();if(txt)return false;return!!el.querySelector('svg,img,i,svg *');}\nfunction hasFormLabel(el,doc){const tag=(el.tagName||'').toLowerCase();if(!/^(input|select|textarea)$/.test(tag))return true;const type=(el.getAttribute('type')||'').toLowerCase();if(type==='hidden')return true;let p=el.parentElement;for(let i=0;i<3&&p;i++,p=p.parentElement){if((p.tagName||'').toLowerCase()==='label')return true;}\nconst id=el.getAttribute('id');if(id){const lbl=(doc.querySelector(`label[for=\"${CSS.escape(id)}\"]`)||null);if(lbl&&(lbl.innerText||lbl.textContent||'').trim())return true;}\nif(hasAriaName(el,doc))return true;return false;}\nfunction svgDescendantName(el,doc){const svg=el.querySelector('svg');if(!svg)return'';const labelledby=(svg.getAttribute('aria-labelledby')||'').trim();if(labelledby){const txt=getByIdText(doc,labelledby);if(txt)return txt;}\nconst aria=(svg.getAttribute('aria-label')||'').trim();if(aria)return aria;const t=svg.querySelector('title');const tt=(t&&(t.textContent||'').trim())||'';if(tt)return tt;return'';}\nfunction hasMeaningfulAlt(img,opts={}){const{minLen=4}=opts;if(img.getAttribute('aria-hidden')==='true')return true;const role=(img.getAttribute('role')||'').toLowerCase();if(role==='presentation'||role==='none')return true;const altAttr=img.getAttribute('alt');if(altAttr==null)return false;const alt=altAttr.trim();if(alt.length<minLen){const owner=img.closest('a,button');return owner?linkHasAccessibleName(owner):false;}\nif(/^(image|photo|picture|graphic|logo|icon)$/i.test(alt))return false;return true;}\nfunction collectMissingAltImages(doc){const out=[];const d=doc;const win=d.defaultView||window;const imgs=Array.from(d.querySelectorAll('img'));for(const img of imgs){try{if(img.closest('svg'))continue;const cs=win.getComputedStyle(img);const isHidden=cs.display==='none'||cs.visibility==='hidden'||parseFloat(cs.opacity)===0;if(isHidden)continue;const r=img.getBoundingClientRect();if(r.width<=2&&r.height<=2)continue;if(!hasMeaningfulAlt(img,{minLen:4})){const src=(img.currentSrc||img.getAttribute('src')||'').trim();out.push({el:img,kind:labelKind(img),role:'img',issue:'alt-missing',src});}}catch(e){}}\nreturn out;};function collectNonButtonSemantics(doc){const d=doc;const win=d.defaultView||window;const candidates=Array.from(d.querySelectorAll('div,span,i,svg,'+\n'.button,.btn,.elementor-button,.wp-block-button__link,'+\n'[role],[tabindex]')).filter(el=>!panel.contains(el)&&!btn.contains(el)).filter(el=>!isIgnoredChrome(el)).filter(el=>!isBuilderChrome(el));const out=[];const NATIVE=/^(BUTTON|A|INPUT|SELECT|TEXTAREA)$/;candidates.forEach(el=>{try{const cs=win.getComputedStyle(el);if(cs.display==='none'||cs.visibility==='hidden'||parseFloat(cs.opacity)===0)return;if(NATIVE.test((el.tagName||'').toUpperCase()))return;const nativeAncestor=el.closest('a,button,input,select,textarea');if(nativeAncestor&&nativeAncestor!==el)return;if(!isButtonLike(el,cs))return;const containsNative=el.querySelector('a,button,input,select,textarea');const hasRoleAttr=(el.getAttribute('role')||'').trim()!=='';const tiAttr=el.getAttribute('tabindex');const tiNum=tiAttr==null?null:parseInt(tiAttr,10);const hasTab0=tiAttr!==null&&!Number.isNaN(tiNum)&&tiNum>=0;if(!hasRoleAttr&&!hasTab0&&containsNative)return;const role=(el.getAttribute('role')||'').toLowerCase();const hasRoleButton=role==='button';const missingRole=!hasRoleButton;const missingTab=!hasTab0;if(missingRole||missingTab){out.push({el,kind:labelKind(el),role:(el.tagName||'').toLowerCase(),issue:'button-semantics',missingRole,missingTab,tabindex:tiAttr==null?null:tiAttr});}}catch(_){}});return out;}\nfunction navAccessibleLabel(el){const aria=(el.getAttribute('aria-label')||'').trim();if(aria)return aria;const labelledby=(el.getAttribute('aria-labelledby')||'').trim();if(labelledby){const ref=(el.ownerDocument||document).getElementById(labelledby);const txt=(ref&&(ref.innerText||ref.textContent)||'').trim();if(txt)return txt;}\nreturn'';}\nfunction collectDuplicateNavLabels(doc){const d=doc;const navs=Array.from(d.querySelectorAll('nav,[role=\"navigation\"]')).filter(el=>!isIgnoredChrome(el)).filter(el=>!isBuilderChrome(el));const map=new Map();for(const el of navs){const label=navAccessibleLabel(el);if(!label)continue;const key=label.toLowerCase();if(!map.has(key))map.set(key,[]);map.get(key).push({el,label,kind:labelKind(el)});}\nconst groups=[];const elements=[];map.forEach((arr,key)=>{if(arr.length>1){groups.push({label:arr[0].label,count:arr.length});arr.forEach(({el,label,kind})=>{elements.push({el,kind,role:'nav',issue:'nav-duplicate-label',ariaLabel:label});});}});return{groups,elements,total:elements.length};}\nfunction collectNonClampFontSize(doc){const d=doc;const win=d.defaultView||window;const selectors=['h1,h2,h3,h4,h5,h6','p,li,blockquote,figcaption,dd,dt,pre','.wp-block-paragraph,.wp-block-list,.elementor-widget-text-editor,.elementor-heading-title','a,button,[role=\"button\"],.button,.btn,.elementor-button,.wp-block-button__link'].join(',');const raw=Array.from(d.querySelectorAll(selectors)).filter(el=>!panel.contains(el)&&!btn.contains(el)).filter(el=>!isIgnoredChrome(el)).filter(el=>!isBuilderChrome(el)).filter(el=>!isSkipLink(el));const now=Date.now();let clampInfo=__CLAMP_CACHE__.info;if(!clampInfo||(now-__CLAMP_CACHE__.time)>1500){clampInfo=collectClampInfo(d);__CLAMP_CACHE__={time:now,info:clampInfo};}\nconst usingClamp=elementsUsingClampForProp(d,'font-size',clampInfo);const out=[];const seen=new Set();raw.forEach(el=>{try{const cs=win.getComputedStyle(el);if(cs.display==='none'||cs.visibility==='hidden'||parseFloat(cs.opacity)===0)return;const txt=(el.innerText||el.textContent||'').trim();const hasIcon=!!el.querySelector('svg,img,svg *');const buttonLike=isButtonLike(el,cs);if(!txt&&!(buttonLike&&hasIcon))return;const heading=/^h[1-6]$/i.test(el.tagName||'');if(isInMenuContext(el)&&!heading&&!buttonLike)return;let rep=el;if(buttonLike){rep=el.closest('a,button,[role=\"button\"],.button,.btn,.elementor-button,.wp-block-button__link')||el;}\nif(seen.has(rep))return;if(usesClampViaCascade(rep,usingClamp))return;const size=parseFloat(win.getComputedStyle(rep).fontSize)||0;seen.add(rep);out.push({el:rep,kind:labelKind(rep),role:(rep.tagName||'').toLowerCase(),issue:'non-clamp-font',sel:labelSelectorForNonClamp(rep),fontSize:Number(size.toFixed(1))});}catch(_){}});return out;}\nfunction collectAccessibleNameIssues(doc){const d=doc;const win=d.defaultView||window;const out=[];const ctrls=Array.from(d.querySelectorAll('a,button,[role=\"button\"]')).filter(el=>!panel.contains(el)&&!btn.contains(el)).filter(el=>!isIgnoredChrome(el)).filter(el=>!isBuilderChrome(el));ctrls.forEach(el=>{try{const cs=win.getComputedStyle(el);if(cs.display==='none'||cs.visibility==='hidden'||parseFloat(cs.opacity)===0)return;if(!isButtonLike(el,cs))return;const name=getAccessibleName(el,d);const iconOnly=isIconOnlyControl(el);const svgOnlyName=!name&&svgDescendantName(el,d);if(svgOnlyName){out.push({el,kind:labelKind(el),role:(el.tagName||'').toLowerCase(),issue:'accessible-name',sub:'svg-title-only',name:svgOnlyName,hint:'Name comes only from an inline SVG (<title>/aria). Prefer visible text or aria-label/aria-labelledby on the link/button.'});}\nif(iconOnly&&!hasAriaName(el,d)&&!svgOnlyName){out.push({el,kind:labelKind(el),role:(el.tagName||'').toLowerCase(),issue:'accessible-name',sub:'icon-only-no-label',name:'',hint:'Add aria-label or visually hidden text for icon-only control.'});return;}\nif(name&&isVagueName(name)&&!hasSpecificContext(name)){out.push({el,kind:labelKind(el),role:(el.tagName||'').toLowerCase(),issue:'accessible-name',sub:'vague',name,hint:'Make the name specific (e.g., “Read more about Pricing”). Prefer visible text or aria-labelledby.'});}\nif((el.tagName||'').toLowerCase()==='a'){const hasImg=!!el.querySelector('img');if(hasImg&&!linkHasAccessibleName(el)){out.push({el,kind:labelKind(el),role:'a',issue:'accessible-name',sub:'image-link-no-name',name:'',hint:'Linked image needs meaningful alt on the <img>, or use aria-label/aria-labelledby on the link.'});}}}catch(_){}});const inputs=Array.from(d.querySelectorAll('input,select,textarea')).filter(el=>!panel.contains(el)&&!btn.contains(el)).filter(el=>!isIgnoredChrome(el)).filter(el=>!isBuilderChrome(el));inputs.forEach(el=>{try{const cs=win.getComputedStyle(el);if(cs.display==='none'||cs.visibility==='hidden'||parseFloat(cs.opacity)===0)return;const type=(el.getAttribute('type')||'').toLowerCase();if(type==='hidden')return;if(!hasFormLabel(el,d)){out.push({el,kind:labelKind(el),role:(el.tagName||'').toLowerCase(),issue:'accessible-name',sub:'form-missing-label',name:'',hint:'Form controls require a visible <label>, aria-label, or aria-labelledby.'});}}catch(_){}});return out;}\nfunction audit(doc){const mains=Array.from(doc.getElementsByTagName('main'));const headers=Array.from(doc.getElementsByTagName('header'));const footers=Array.from(doc.getElementsByTagName('footer'));const issues=[];if(mains.length===0){issues.push({key:'main_missing',title:'MAIN: Missing',message:'There must be just one Main. No <code>&lt;main&gt;</code> element was found wrapping the unique page content.',fixes:['<b>Elementor:</b> Set top container HTML Tag to <code>main</code>.','<b>Gutenberg:</b> Ensure template outputs a single <code>&lt;main&gt;</code>.'],elements:[]});}else if(mains.length>1){issues.push({key:'main_multiple',title:`MAIN: Multiple (${mains.length})`,message:'There must be just one Main.',fixes:['Reduce page to a single <code>&lt;main&gt;</code>.'],elements:mains.map(el=>({el,kind:labelKind(el)}))});}\nif(headers.length===0){issues.push({key:'header_missing',title:'HEADER: Missing',message:'There should be one <code>&lt;header&gt;</code> landmark wrapping the top of the page.',fixes:['Add a <code>&lt;header&gt;</code> element at the top of the document.'],elements:[]});}else if(headers.length>1){issues.push({key:'header_multiple',title:`HEADER: Multiple (${headers.length})`,message:'There should be at most one Header landmark.',fixes:['Change extra <code>header</code> to <code>div</code>.'],elements:headers.map(el=>({el,kind:labelKind(el)}))});}\nif(footers.length===0){issues.push({key:'footer_missing',title:'FOOTER: Missing',message:'There should be one <code>&lt;footer&gt;</code> landmark wrapping the bottom of the page.',fixes:['Add a <code>&lt;footer&gt;</code> element at the bottom of the document.'],elements:[]});}else if(footers.length>1){issues.push({key:'footer_multiple',title:`FOOTER: Multiple (${footers.length})`,message:'There should be at most one Footer landmark.',fixes:['Change extra <code>footer</code> to <code>div</code>.'],elements:footers.map(el=>({el,kind:labelKind(el)}))});}\nconst headings=Array.from(doc.querySelectorAll('h1,h2,h3,h4,h5,h6'));const h1s=headings.filter(h=>h.tagName.toLowerCase()==='h1');const headingSkips=[];let lastVirtual=null,lastContainer=null;function reset(){lastVirtual=null;}\nfor(let i=0;i<headings.length;i++){const node=headings[i];const lvl=+node.tagName.slice(1);const cKey=containerKeyFor(node);if(cKey!==lastContainer){reset();lastContainer=cKey;}\nif(lastVirtual===null){lastVirtual=lvl;continue;}\nconst min=lastVirtual;const max=Math.min(6,lastVirtual+1);if(lvl<min){const suggest=Math.min(6,lastVirtual+1);headingSkips.push({prev:headings[i-1],curr:node,from:lastVirtual,to:lvl,suggest,kind:labelKind(node)});lastVirtual=suggest;}else if(lvl>max){const suggest=Math.min(6,lastVirtual+1);headingSkips.push({prev:headings[i-1],curr:node,from:lastVirtual,to:lvl,suggest,kind:labelKind(node)});lastVirtual=suggest;}else{lastVirtual=lvl;}}\nif(h1s.length===0){issues.push({key:'h1_missing',title:'HEADINGS: Missing H1',message:'Exactly one <code>&lt;h1&gt;</code> is required.',fixes:['Add a page title as <code>H1</code>.'],elements:[]});}else if(h1s.length>1){issues.push({key:'h1_multiple',title:`HEADINGS: Multiple H1 (${h1s.length})`,message:'There must be only one <code>&lt;h1&gt;</code>.',fixes:['Change extra H1s to <code>H2/H3</code>.'],elements:h1s.map(el=>({el,kind:labelKind(el)}))});}\nif(headingSkips.length){issues.push({key:'heading_skips',title:`HEADINGS: Order skips levels (${headingSkips.length})`,message:'Headings should move forwards; H1 → H2, or can be the same; H2 → H2. Do not go backwards or skip levels.',fixes:['Adjust so that each step changes by at most one level and does not go backwards.'],elements:headingSkips.map(s=>({el:s.curr,kind:s.kind||labelKind(s.curr),from:s.from,to:s.to,suggest:s.suggest}))});}\nconst skipIssue=collectSkipLinkIssue(doc);if(skipIssue&&(!skipIssue.elements||!skipIssue.elements.length||skipIssue.elements.some(e=>e.sub!=='ok'))){issues.push(skipIssue);}\nconst contrastFails=collectContrastFailures(doc);if(contrastFails.length){issues.push({key:'contrast_aa',title:`ACCESSIBILITY: Color contrast (AA) failures (${contrastFails.length})`,message:'Definite failures are calculated only when the auditor can resolve a solid CSS background. Image, gradient, video and layered backgrounds are now marked for manual visual checking instead of being treated as white.',fixes:['Large text 24px regular or 18.66px bold must be ≥3:1. Other text should be ≥4.5:1.','Increase contrast by changing text (foreground) or the background.','Button fill or border should be visually distinguishable from its surrounding area. Image or gradient backgrounds require a manual check.','Ensure states (hover/focus/active) also meet contrast.'],elements:contrastFails.map(f=>({el:f.el,kind:f.kind||'unknown',role:f.role,contrastType:f.contrastType,ratio:f.ratio,required:f.required,fontSize:f.fontSize,fontWeight:f.fontWeight,fgHex:f.fgHex,bgHex:f.bgHex,adjHex:f.adjHex,borderHex:f.borderHex,suggestFG:f.suggestFG,suggestBG:f.suggestBG,confidence:f.confidence,backgroundSource:f.backgroundSource,manualReason:f.manualReason}))});}\nconst smallTextFails=collectSmallTextFailures(doc);if(smallTextFails.length){issues.push({key:'text_min_16',title:`ACCESSIBILITY: Text below 16px (${smallTextFails.length})`,message:'Body text should be at least 16px for readability. Smaller sizes can be difficult to read.',fixes:['Increase base/body font size to ≥ 16px.','If space is tight, ensure good contrast and line-height.'],elements:smallTextFails});}\nconst sizeFails=collectControlMinSizeFailures(doc);if(sizeFails.length){issues.push({key:'control_min_size',title:`ACCESSIBILITY: Button minimum size (24×24) failures (${sizeFails.length})`,message:'Interactive controls should have a minimum visible target size of 24×24px.',fixes:['Increase padding or line-height, or switch to a larger button style.','Ensure icon-only buttons have at least 24×24 px of clickable area.'],elements:sizeFails.map(s=>({el:s.el,kind:s.kind||'unknown',role:s.role,width:s.width,height:s.height,requiredW:s.requiredW,requiredH:s.requiredH}))});}\nconst spacingFails=collectMobileControlSpacingFailures(doc);if(spacingFails.length){issues.push({key:'mobile_control_spacing',title:`ACCESSIBILITY: Mobile control spacing (≥12px) failures (${spacingFails.length})`,message:'On mobile, interactive controls should be separated by at least 12px of clear space.',fixes:['Increase gap/margin between adjacent buttons/controls on mobile breakpoints.','Ensure stacked buttons have ≥12px margin-bottom / gap.'],elements:spacingFails.map(s=>({el:s.el,kind:s.kind||'unknown',role:s.role,spaceType:s.spaceType,dist:s.dist,required:s.required,partnerText:s.partnerText}))});}\nconst nonModernImages=collectNonModernImages(doc);if(nonModernImages.length){issues.push({key:'images_nonmodern',title:`IMAGES: Non-modern formats (${nonModernImages.length})`,message:'Images must use modern formats for performance. Convert to WebP or AVIF.',fixes:['Export assets as WebP or AVIF.','Avoid PNG/JPG unless there is a hard requirement.'],elements:nonModernImages});}\nconst missingAltImages=collectMissingAltImages(doc);if(missingAltImages.length){issues.push({key:'images_alt_missing',title:`IMAGES: Alt-Text Description Missing (${missingAltImages.length})`,message:'Meaningful alt text is required for informative images. Use empty <code>alt=\"\"</code> only for purely decorative images (or set <code>role=\"presentation\"</code>/<code>aria-hidden=\"true\"</code>).',fixes:['Add concise, specific descriptions.','Images in the Header and Footer may adopt the Title as the Alt.','If the image is decorative, set <code>alt=\"\"</code> or use <code>role=\"presentation\"</code> / <code>aria-hidden=\"true\"</code>.',],elements:missingAltImages});}\nconst dupNav=collectDuplicateNavLabels(doc);if(dupNav.total){const groupSummary=dupNav.groups.map(g=>`“${g.label}” × ${g.count}`).join('; ');issues.push({key:'nav_duplicate_labels',title:`NAV: Multiple menus share the same aria-label (${dupNav.groups.length} label${dupNav.groups.length>1?'s':''})`,message:`Multiple <code>&lt;nav&gt;</code> landmarks reuse the same accessible name, which can confuse screen-reader users when jumping between navigation regions. Duplicates: ${groupSummary}.`,fixes:['Give each navigation landmark a distinct, descriptive name via <code>aria-label</code> (or <code>aria-labelledby</code>).','Examples: <code>aria-label=\"Primary navigation\"</code>, <code>\"Secondary navigation\"</code>, <code>\"Footer navigation\"</code>, or <code>\"Additional site navigation\"</code>.'],elements:dupNav.elements});}\nconst nonClampFont=collectNonClampFontSize(doc);if(nonClampFont.length){issues.push({key:'font_no_clamp',title:`TYPOGRAPHY: font-size without clamp() (${nonClampFont.length})`,message:'These text elements use a fixed font-size. Responsive fonts are more fluid.',fixes:['Swap fixed font-size for a clamp().','Use the Font Clamp Calculator.',],elements:nonClampFont});}\nconst accessibleNameFails=collectAccessibleNameIssues(doc);if(accessibleNameFails.length){issues.push({key:'aria_label',title:`ACCESSIBILITY: Aria-Labels for Vague or Missing descriptions (${accessibleNameFails.length})`,message:'Controls and links must have a clear accessible name. Avoid vague CTAs (“Read more”, “Click here”), label icon-only controls, ensure linked images are named, and label all form fields.',fixes:['Prefer specific, visible text on the control.','If needed, add aria-label=\"Contact Me\", or use Custom Attributes: aria-label|Contact Me','Linked images: give the <img> a meaningful alt (or label the link).','Form fields: associate a <label> or use aria-label.'],elements:accessibleNameFails});}\nconst nonButtonSem=collectNonButtonSemantics(doc);if(nonButtonSem.length){issues.push({key:'button_semantics',title:`ACCESSIBILITY: Non-native buttons missing role/tabindex (${nonButtonSem.length})`,message:'Non-native elements (e.g., <div>/<span>) used as buttons must expose button semantics and be keyboard focusable.',fixes:['Add role=\"button\" to non-native interactive elements.','Add tabindex=\"0\" so they are focusable via keyboard.','Ensure Enter and Space activate the control (keyup for Enter, keydown for Space).'],elements:nonButtonSem});}\nconst ctaCandidates=collectCTACandidates(doc);if(ctaCandidates.length===0){issues.push({key:'cta_missing',title:'CONTENT: No primary CTA found',message:'No obvious call-to-action (button or prominent action link) was detected outside navigation. Pages should guide users with at least one clear next step.',fixes:['Add a prominent button or action link (e.g., “Contact us”, “Get a quote”, “Book a demo”).','Place it near the top of the page and/or after key sections.','Ensure it is visually distinct from body links.'],elements:[]});}\nconst longHeadings=collectLongHeadings(doc);if(longHeadings.length){issues.push({key:'heading_too_long',title:`CONTENT: Headings too long (${longHeadings.length})`,message:`Headings should be scannable. Aim for ≤ ${HEADING_WORD_LIMIT} words or ≤ ${HEADING_CHAR_LIMIT} characters.`,fixes:[`Trim to ≤ ${HEADING_WORD_LIMIT} words or ≤ ${HEADING_CHAR_LIMIT} characters.`,'Move supporting detail into body text or a subheading.'],elements:longHeadings});}\nreturn{issues,counts:{main:mains.length,header:headers.length,footer:footers.length,h1:h1s.length,headingSkips:headingSkips.length,contrastFails:contrastFails.length,controlMinSize:sizeFails.length,mobileSpacing:spacingFails.length,imagesNonModern:nonModernImages.length,imagesAltMissing:missingAltImages.length,smallText:smallTextFails.length,navDuplicateLabels:dupNav.total,nonClampFont:nonClampFont.length,accessibleNames:accessibleNameFails.length,ctaCandidates:ctaCandidates.length,headingsLong:longHeadings.length,skipLink:(skipIssue&&(!skipIssue.elements||!skipIssue.elements.length||skipIssue.elements.some(e=>e.sub!=='ok')))?1:0,buttonSemantics:nonButtonSem.length}};}\nfunction cssPath(el){if(!el||!(el.nodeType===1)){return'';}\nconst doc=el.ownerDocument||document;if(!window.CSS||typeof CSS.escape!=='function'){window.CSS=window.CSS||{};CSS.escape=CSS.escape||function(v){return String(v).replace(/[\\0-\\x1f\\x7f]/g,'\\uFFFD').replace(/^[0-9]/,'\\\\3$& ').replace(/[^a-zA-Z0-9_\\u00A0-\\uFFFF-]/g,'\\\\$&');};}\nif(el.id){const s='#'+CSS.escape(el.id);try{if(doc.querySelectorAll(s).length===1)return s;}catch(_){}}\nconst parts=[];let node=el;while(node&&node.nodeType===1&&node!==doc.documentElement){let part=node.nodeName.toLowerCase();const raw=(node.className||'').toString().trim();if(raw){const classes=raw.split(/\\s+/).filter(c=>c&&!/^(active|current|selected|hover|focus|open|opened|closing|loading)$/.test(c));if(classes.length)part+='.'+CSS.escape(classes[0]);}\nconst parent=node.parentElement;if(parent){const sameTag=Array.from(parent.children).filter(n=>n.tagName===node.tagName);if(sameTag.length>1){const idx=sameTag.indexOf(node)+1;part+=`:nth-child(${idx})`;}}\nparts.unshift(part);const candidate=parts.join(' > ');try{if(doc.querySelectorAll(candidate).length===1){return candidate;}}catch(_){}\nnode=parent;}\nif(parts.length)return parts.join(' > ');const tag=(el.tagName||'node').toLowerCase();const c0=((el.className||'')+'').split(/\\s+/).filter(Boolean)[0];if(el.id)return`${tag}#${el.id}`;if(c0)return`${tag}.${c0}`;return tag;}\nfunction labelSelectorForSmallText(el){const doc=el.ownerDocument||document;const ROOTS=['nav.elementor-nav-menu--dropdown','nav.elementor-nav-menu--main','.elementor-widget-nav-menu','.wp-block-navigation','nav[role=\"navigation\"]','nav'].join(',');const root=el.closest(ROOTS);if(root){const tag=root.tagName.toLowerCase();const cls=((root.className||'')+'').trim().split(/\\s+/).find(Boolean);return cls?`${tag}.${cls}`:tag;}\nconst a=el.closest('[class]');if(a){const tag=a.tagName.toLowerCase();const cls=((a.className||'')+'').trim().split(/\\s+/).find(Boolean);return cls?`${tag}.${cls}`:tag;}\nreturn(el.tagName||'node').toLowerCase();}\nconst HAS_CLAMP=v=>!!(v&&String(v).includes('clamp('));const READABLE=s=>{try{void s.cssRules;return true;}catch(e){return false;}};function collectClampInfo(doc){const win=doc.defaultView||window;const matchesMedia=media=>{if(!media||!media.mediaText)return true;try{return win.matchMedia(media.mediaText).matches;}catch(_){return true;}};const byProp=new Map();const clampTokens=new Set();const addSel=(prop,sel)=>{if(!byProp.has(prop))byProp.set(prop,new Set());byProp.get(prop).add(sel);};function scan(list){for(const rule of list){if(rule.type===CSSRule.MEDIA_RULE){if(matchesMedia(rule.media))scan(rule.cssRules);continue;}\nif(rule.type===CSSRule.SUPPORTS_RULE){scan(rule.cssRules);continue;}\nif(rule.type!==CSSRule.STYLE_RULE)continue;const st=rule.style;if(!st)continue;for(let i=0;i<st.length;i++){const p=st[i];if(p.startsWith('--')&&HAS_CLAMP(st.getPropertyValue(p)))clampTokens.add(p.trim());}\nfor(let i=0;i<st.length;i++){const p=st[i];if(HAS_CLAMP(st.getPropertyValue(p)))addSel(p,rule.selectorText);}\nfor(let i=0;i<st.length;i++){const p=st[i];const v=st.getPropertyValue(p);const m=v&&v.match(/var\\(\\s*(--[A-Za-z0-9_-]+)/g);if(!m)continue;for(const ref of m){const name=ref.replace(/^var\\(\\s*/,'').trim();if(clampTokens.has(name)){addSel(p,rule.selectorText);break;}}}}}\nfor(const sheet of Array.from(doc.styleSheets)){if(!READABLE(sheet))continue;try{scan(sheet.cssRules);}catch(_){}}\nreturn{byProp,clampTokens};}\nfunction elementsUsingClampForProp(doc,prop,clampInfo){const out=new Set();const{byProp,clampTokens}=clampInfo;const selSet=byProp.get(prop);if(selSet){for(const sel of selSet){try{doc.querySelectorAll(sel).forEach(n=>out.add(n));}catch(_){}}}\ntry{doc.querySelectorAll('[style*=\"clamp(\"]').forEach(n=>{const v=n.style.getPropertyValue(prop);if(v&&v.includes('clamp('))out.add(n);});}catch(_){}\nif(clampTokens&&clampTokens.size){const toks=Array.from(clampTokens);try{doc.querySelectorAll('[style]').forEach(n=>{const v=n.style.getPropertyValue(prop);if(!v)return;for(const t of toks){if(v.includes(`var(${t}`)){out.add(n);break;}}});}catch(_){}}\nreturn out;}\nfunction usesClampViaCascade(el,usingClampSet){let n=el;for(let i=0;i<10&&n&&n.nodeType===1;i++){if(usingClampSet.has(n))return true;n=n.parentElement;}\nreturn false;}\nfunction labelSelectorForNonClamp(el){return labelSelectorForSmallText(el);}\nfunction esc(s){return String(s).replace(/[&<>\"']/g,m=>({'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[m]));}\nfunction renderPanel(result,doc){const{issues,counts}=result;renderTogglesFor(issues);const visibleIssues=issues.filter(iss=>!isHiddenEffective(iss.key));const hiddenSig=Array.from(HIDDEN).sort().join(',');const sig=`${counts.main}|${counts.header}|${counts.footer}|${counts.h1}|${counts.headingSkips}|${counts.contrastFails}|${counts.controlMinSize}|${counts.mobileSpacing}|${counts.imagesNonModern}|${counts.imagesAltMissing}|${counts.smallText}|${counts.navDuplicateLabels}|${counts.nonClampFont}|${counts.accessibleNames}|${counts.ctaCandidates}|${counts.headingsLong}|${counts.skipLink}|${counts.buttonSemantics}|${issues.length}|hidden:${hiddenSig}`;if(!INTERACTING&&sig===lastSignature&&content.__renderedOnce)return;lastSignature=sig;content.__renderedOnce=true;clearHighlights(doc);const rowStyle='margin:6px 0;padding:8px 10px;background:#fafafa;border:1px solid #eee;border-radius:6px;font-size:12px;color:#333;display:flex;align-items:flex-start;gap:8px;cursor:pointer';const getLabelText=(el)=>{if(!el)return'';const txt=(el.innerText||el.textContent||'').trim().replace(/\\s+/g,' ');if(txt)return txt.slice(0,140)+(txt.length>140?'…':'');const alt=el.getAttribute?.('aria-label')||el.getAttribute?.('alt')||el.getAttribute?.('title');return(alt||'').trim();};let html='';visibleIssues.forEach((issue,idx)=>{const els=(issue.elements||[]).slice();const isOpen=OPEN.get(issue.key)===true;html+=`\n            <section class=\"audit-acc\" data-issue=\"${idx}\" data-key=\"${issue.key}\"\n                     style=\"margin:10px 0;border:1px solid #efefef;border-radius:10px;overflow:hidden\">\n              <button type=\"button\" class=\"audit-acc-head\" aria-expanded=\"${isOpen?'true':'false'}\"\n                      style=\"all:unset;display:flex;align-items:center;gap:8px;width:100%;padding:10px 12px;cursor:pointer;background:#fafafa;border-bottom:1px solid #f0f0f0;font-size:13px;line-height:1.45\">\n                <span class=\"audit-acc-chevron\" aria-hidden=\"true\"\n                      style=\"display:inline-block;transform:${isOpen?'rotate(90deg)':'rotate(0deg)'};transition:transform .18s ease\">▶</span>\n\t\t\t\t<span style=\"font-weight:700;font-size:13px\">#${idx+1} — ${esc(issue.title)}</span>\n              </button>\n              <div class=\"audit-acc-body\" ${isOpen?'':'hidden'} style=\"padding:10px;background:#fff\">\n                ${issue.message ? `<div style=\"margin-bottom:8px\">${issue.message}</div>` : '' }\n                ${(els.length)?`<div class=\"audit-els\"data-count=\"${els.length}\">${els.map((o,elIdx)=>{let meta='';if(o.contrastType==='text-vs-bg'){meta=`(builder:${o.kind?.split(':')[1]||o.kind||'unknown'}) <b>Text vs Background</b> Contrast ${o.ratio}:1; needs ≥ ${o.required}:1; font ${o.fontSize}px/${o.fontWeight}; text ${o.fgHex} on bg ${o.bgHex}. Suggested: <b>text ${o.suggestFG}</b> or <b>bg ${o.suggestBG}</b>.`;}else if(o.contrastType==='manual-text-bg'){meta=`(builder:${o.kind?.split(':')[1]||o.kind||'unknown'}) <b>Manual contrast check required</b>. ${o.manualReason||'The background cannot be resolved reliably.'} Text ${o.fgHex||''}; target ≥ ${o.required}:1. No false white-background result has been assumed.`;}else if(o.contrastType==='manual-control-bg'){meta=`(builder:${o.kind?.split(':')[1]||o.kind||'unknown'}) <b>Manual control contrast check required</b>. ${o.manualReason||'The surrounding background cannot be resolved reliably.'} ${o.bgHex?'Control bg '+o.bgHex+'. ':''}${o.borderHex?'Border '+o.borderHex+'. ':''}Target ≥ ${o.required}:1 where the control boundary is needed to identify the component.`;}else if(o.contrastType==='bg-vs-adj'){meta=`(builder:${o.kind?.split(':')[1]||o.kind||'unknown'}) <b>Background vs Page Background</b> Contrast ${o.ratio}:1; needs ≥ ${o.required}:1; button bg ${o.bgHex} on page bg ${o.adjHex}. Suggested: <b>button bg ${o.suggestBG}</b>.`;}else if(o.contrastType==='border-vs-adj'){meta=`(builder:${o.kind?.split(':')[1]||o.kind||'unknown'}) <b>Border vs Page Background</b> Contrast ${o.ratio}:1; needs ≥ ${o.required}:1; border ${o.borderHex} on page bg ${o.adjHex}. Suggested: <b>border ${o.suggestBG}</b>.`;}else if(typeof o.width==='number'&&typeof o.height==='number'){meta=`(builder:${o.kind?.split(':')[1]||o.kind||'unknown'}) <b>Control size</b> ${o.width}×${o.height}px; needs ≥ ${o.requiredW}×${o.requiredH}px.`;}else if(o.spaceType==='mobile-spacing'){meta=`(builder:${o.kind?.split(':')[1]||o.kind||'unknown'}) <b>Mobile control spacing</b> ${o.dist}px; needs ≥ ${o.required}px; too close to “${o.partnerText||'another control'}”.`;}else if(typeof o.ratio==='number'){meta=`(builder:${o.kind?.split(':')[1]||o.kind||'unknown'}) Contrast ${o.ratio}:1; needs ≥ ${o.required}:1.`;}else if(typeof o.from==='number'&&typeof o.to==='number'){meta=`(builder:${o.kind?.split(':')[1]||o.kind||'unknown'}) Detected jump H${o.from} → H${o.to}. Suggested: H${o.suggest}.`;}else if(o.issue==='nonmodern'){meta=`(builder:${o.kind?.split(':')[1]||o.kind||'unknown'}) <b>Image format</b> ${o.src || '(no src)'}; must be <b>WebP</b> or <b>AVIF</b>.`;}else if(o.issue==='alt-missing'){meta=`(builder:${o.kind?.split(':')[1]||o.kind||'unknown'}) <b>Alt text missing</b> ${o.src || '(no src)'} — add meaningful <code>alt</code> or mark decorative with <code>alt=\"\"</code>.`;}else if(o.issue==='small-text'){const sel=labelSelectorForSmallText(o.el);meta=`(builder:${o.kind?.split(':')[1]||o.kind||'unknown'} • ${sel}) `+\n`<b>Small text</b> ${o.fontSize}px; needs ≥ ${o.required}px.`;}else if(o.issue==='nav-duplicate-label'){const sel=(o.el&&(o.el.id?`#${o.el.id}`:(o.el.className||'').toString().split(/\\s+/).filter(Boolean)[0]))||'nav';const shortSel=sel?`nav${sel.startsWith('#')?sel:'.'+sel}`:'nav';meta=`(builder:${o.kind?.split(':')[1]||o.kind||'unknown'} • ${shortSel}) `+\n`<b>Duplicate nav aria-label</b> “${o.ariaLabel}”.`;}else if(o.issue==='non-clamp-font'){const sel=o.sel||labelSelectorForNonClamp(o.el);meta=`(builder:${o.kind?.split(':')[1]||o.kind||'unknown'} • ${sel}) `+\n`<b>font-size not using clamp()</b> (computed ${o.fontSize}px).`;}else if(o.issue==='accessible-name'){if(o.sub==='vague'){meta=`(builder:${o.kind?.split(':')[1]||o.kind||'unknown'}) `+\n`<b>Vague CTA</b> “${o.name}”. ${o.hint}`;}else if(o.sub==='icon-only-no-label'){meta=`(builder:${o.kind?.split(':')[1]||o.kind||'unknown'}) `+\n`<b>Icon-only control without accessible name</b>. ${o.hint}`;}else if(o.sub==='image-link-no-name'){meta=`(builder:${o.kind?.split(':')[1]||o.kind||'unknown'}) `+\n`<b>Linked image without accessible name</b>. ${o.hint}`;}else if(o.sub==='form-missing-label'){meta=`(builder:${o.kind?.split(':')[1]||o.kind||'unknown'}) `+\n`<b>Form field missing label</b>. ${o.hint}`;}else if(o.sub==='svg-title-only'){meta=`(builder:${o.kind?.split(':')[1]||o.kind||'unknown'}) `+\n`<b>SVG-only accessible name</b> “${o.name}”. ${o.hint}`;}else{meta=`(builder:${o.kind?.split(':')[1]||o.kind||'unknown'}) Accessible name issue.`;}}else if(o.issue==='heading-long'){meta=`(builder:${o.kind?.split(':')[1]||o.kind||'unknown'}) `+\n`<b>Heading too long</b> H${o.level || '?'} — ${o.words} words / ${o.chars} chars; `+\n`aim ≤ ${HEADING_WORD_LIMIT} words or ≤ ${HEADING_CHAR_LIMIT} chars.`;}else if(o.issue==='skiplink'){if(o.sub==='ok'){meta=`(builder:${o.kind?.split(':')[1]||o.kind||'unknown'}) ✓ Skip link present and early in tab order.`;}else if(o.sub==='target-missing'){meta=`(builder:${o.kind?.split(':')[1]||o.kind||'unknown'}) <b>Skip link target missing</b> — ${o.hint}`;}else if(o.sub==='target-not-main'){meta=`(builder:${o.kind?.split(':')[1]||o.kind||'unknown'}) <b>Skip link target isn’t the main region</b> — ${o.hint}`;}else if(o.sub==='late'){meta=`(builder:${o.kind?.split(':')[1]||o.kind||'unknown'}) <b>Skip link appears late in tab order</b> (tab index ~ ${o.index}). ${o.hint}`;}else if(o.sub==='maybe-not-visible-on-focus'){meta=`(builder:${o.kind?.split(':')[1]||o.kind||'unknown'}) <b>Skip link may remain invisible on focus</b> — ${o.hint}`;}else{meta=`(builder:${o.kind?.split(':')[1]||o.kind||'unknown'}) Skip link check.`;}}else if(o.issue==='button-semantics'){const bits=[];if(o.missingRole)bits.push('missing role=\"button\"');if(o.missingTab)bits.push(`missing tabindex=\"0\"${o.tabindex!=null ? `(found tabindex=\"${o.tabindex}\")` : ''}`);meta=`(builder:${o.kind?.split(':')[1]||o.kind||'unknown'}) `+\n`<b>Non-native button</b> — ${bits.join('; ')}.`;}else{const text=getLabelText(o.el);meta=`(builder:${o.kind?.split(':')[1]||o.kind||'unknown'}) ${text}`;}\nreturn`<div class=\"audit-el-block\" data-elidx=\"${elIdx}\" style=\"${rowStyle}\">\n                      <div class=\"audit-row-text\" style=\"flex:1;min-width:0\">${meta}</div>\n                    </div>`;}).join('')}</div>`:''\n                }\n                ${issue.fixes && issue.fixes.length ? `<div style=\"font-size:12px;color:#666;margin:8px 0 4px 0\">How to fix:</div><ol style=\"margin:0 0 0 18px;padding:0\">${issue.fixes.map(step=>`<li style=\"margin:4px 0\">${step}</li>`).join('')}</ol>` : ''}\n              </div>\n            </section>`;});content.innerHTML=html;const accHeads=content.querySelectorAll('.audit-acc-head');accHeads.forEach(head=>{head.setAttribute('tabindex','0');head.addEventListener('keydown',e=>{if(e.key==='Enter'||e.key===' '){e.preventDefault();head.click();}});head.addEventListener('click',()=>{INTERACTING=true;const section=head.parentElement;const body=head.nextElementSibling;const key=section.getAttribute('data-key');const nowOpen=head.getAttribute('aria-expanded')!=='true';OPEN.set(key,nowOpen);head.setAttribute('aria-expanded',String(nowOpen));head.querySelector('.audit-acc-chevron').style.transform=nowOpen?'rotate(90deg)':'rotate(0deg)';body.hidden=!nowOpen;if(nowOpen){const issueIdx=+section.getAttribute('data-issue');const blocks=section.querySelectorAll('.audit-el-block');const issue=visibleIssues[issueIdx];let hoverOff=null;blocks.forEach(b=>{if(b.__auditBound)return;b.__auditBound=true;const elIdx=+b.getAttribute('data-elidx');const obj=(issue.elements||[])[elIdx];if(!obj||!obj.el)return;b.addEventListener('mouseenter',()=>{try{obj.el.style.outline='5px dashed #e53935';obj.el.classList.add('audit-highlight');hoverOff=createOverlayFor(obj.el,doc);}catch(e){}});b.addEventListener('mouseleave',()=>{try{obj.el.style.outline='';obj.el.classList.remove('audit-highlight');if(hoverOff){hoverOff();hoverOff=null;}}catch(e){}});b.addEventListener('click',()=>{try{obj.el.scrollIntoView({behavior:'smooth',block:'center'});highlightOnce(obj.el,doc);}catch(e){}});});}else{clearHighlights(doc);}\nsetTimeout(()=>{INTERACTING=false;},180);});});if(panel.style.display==='block')addHeadingBadges(doc);else removeHeadingBadges(doc);}\nlet current=findPreviewDoc();function startObserver(targetDoc){if(!panelOpen())return;stopObserver();const root=targetDoc.documentElement||targetDoc.body||targetDoc;const fire=()=>{if(INTERACTING||!panelOpen())return;const res=audit(targetDoc);renderPanel(res,targetDoc);};const debounced=()=>{if(debTimer)clearTimeout(debTimer);debTimer=setTimeout(fire,120);};mo=new(window.MutationObserver||window.WebKitMutationObserver||window.MozMutationObserver)(debounced);try{mo.observe(root,{childList:true,subtree:true,attributes:true,characterData:true});}catch(e){}\nheartbeat=setInterval(fire,800);resizeHandler=debounced;window.addEventListener('resize',resizeHandler,{passive:true});fire();}\nfunction attachIframeWatchers(){if(!panelOpen())return;document.querySelectorAll('iframe').forEach(ifr=>{ifr.addEventListener('load',()=>{if(!panelOpen())return;try{const fdoc=ifr.contentDocument||ifr.contentWindow.document;if(fdoc&&fdoc.body){current={doc:fdoc,iframe:ifr};startObserver(fdoc);}}catch(e){current={doc:document,iframe:null};startObserver(document);}});});}\nfunction startTopWatcher(){if(!panelOpen())return;try{topMO=new MutationObserver(()=>{if(!panelOpen())return;const again=findPreviewDoc();if(again.doc&&again.doc!==current.doc){removeHeadingBadges(current.doc);current=again;startObserver(again.doc);attachIframeWatchers();}});topMO.observe(document.documentElement,{childList:true,subtree:true});}catch(e){}}\nfunction closeAuditPanel(){panel.style.display='none';btn.setAttribute('aria-pressed','false');btn.setAttribute('aria-label','Open build audit panel');stopObserver();removeHeadingBadges(current.doc);clearHighlights(current.doc);try{btn.focus();}catch(_){} }closeBtn.addEventListener('click',(e)=>{e.preventDefault();e.stopPropagation();closeAuditPanel();});btn.addEventListener('click',()=>{const opening=panel.style.display==='none'||panel.style.display==='';if(opening){panel.style.display='block';btn.setAttribute('aria-pressed','true');btn.setAttribute('aria-label','Close build audit panel');current=findPreviewDoc();const res=audit(current.doc);renderPanel(res,current.doc);addHeadingBadges(current.doc);startObserver(current.doc);attachIframeWatchers();startTopWatcher();}else{closeAuditPanel();}});window.WSBuilderToolsAuditToggle=()=>btn.click();window.WSBuilderToolsAuditClose=closeAuditPanel;current=findPreviewDoc();window.addEventListener('beforeunload',()=>{try{stopObserver();}catch(e){}});})();</script>\n    <?php\n}\n\n/**\n * Web Squadron Elementor Builder Tools\n * Combines Page Auditor, viewport width/height controls and Page Zoomer.\n * Visible only inside the Elementor Edit screen.\n */\nfunction ws_elementor_builder_tools_panel() {\n    $wsbt_ajax_url = admin_url( 'admin-ajax.php' );\n    $wsbt_nonce    = wp_create_nonce( 'wsbt_quick_actions' );\n    ?>\n    <style>\n      #ws-builder-tools{position:fixed;right:18px;bottom:18px;z-index:2147483643;width:400px;min-width:400px;max-width:400px;max-height:calc(100vh - 8px);overflow:auto;background:rgba(21,21,21,.90);color:#fff;border:1px solid rgba(255,255,255,.14);border-radius:12px;box-shadow:0 14px 38px rgba(0,0,0,.38);font:500 12px/1.3 system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;user-select:none}\n      #ws-builder-tools *{box-sizing:border-box}\n      #ws-builder-tools .wsbt-head{display:flex;align-items:center;gap:10px;padding:9px;cursor:move;border-bottom:1px solid rgba(255,255,255,.12)}\n      #ws-builder-tools button,#ws-builder-tools input,#ws-builder-tools select{font:inherit}\n      #ws-builder-tools .wsbt-action{background:rgba(44,47,51,.90);color:#fff;border:1px solid rgba(255,255,255,.18);border-radius:7px;padding:6px 8px;cursor:pointer}\n      #ws-builder-tools .wsbt-head>.wsbt-action:not(.wsbt-icon-action):not(.wsbt-min){flex:1 1 0;min-width:0;text-align:center}\n      #ws-builder-tools .wsbt-action:hover{background:rgba(58,62,67,.90)}\n      #ws-builder-tools .wsbt-action[aria-pressed=\"true\"]{background:rgba(75,126,187,.90);border-color:rgba(155,202,255,.75)}\n      #ws-builder-tools .wsbt-icon-action{flex:0 0 30px;width:30px;height:30px;padding:0;display:inline-flex;align-items:center;justify-content:center}\n      #ws-builder-tools .wsbt-icon-action svg{display:block;width:18px;height:18px;stroke:#fff;fill:none;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;pointer-events:none}\n      #ws-builder-tools .wsbt-min{flex:0 0 30px;margin-left:0;width:30px;height:30px;padding:0;display:inline-flex;align-items:center;justify-content:center;line-height:1}\n      #ws-builder-tools .wsbt-min svg{display:block;width:20px;height:20px;stroke:#fff;fill:none;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;pointer-events:none}\n      #ws-builder-tools .wsbt-body{padding:9px}\n      #ws-builder-tools .wsbt-row{display:flex;align-items:center;gap:10px}\n      #ws-builder-tools .wsbt-row+.wsbt-row{margin-top:8px}\n      #ws-builder-tools .wsbt-inspect-row{padding-top:8px;border-top:1px solid rgba(255,255,255,.12);flex-wrap:nowrap}\n      #ws-builder-tools .wsbt-inspect-row .wsbt-action{flex:1 1 0;min-width:0;text-align:center;padding:6px 4px;font-size:10px;white-space:nowrap}\n      #ws-builder-tools label{display:flex;align-items:center;gap:4px;color:#ddd}\n      #ws-builder-tools .wsbt-body>.wsbt-row:first-child>label{flex:1 1 0;min-width:0}\n      #ws-builder-tools input[type=text],#ws-builder-tools input[type=number],#ws-builder-tools select{background:rgba(41,44,48,.90);color:#f3f3f3;border:1px solid #494d52;border-radius:6px;height:30px;padding:0 7px;outline:none}\n      #ws-builder-tools input[type=text]{width:auto;min-width:0;flex:1 1 auto}\n      #ws-builder-tools select{width:100%}\n      #ws-builder-tools .wsbt-device-row{display:none}\n      #ws-builder-tools .wsbt-system-row{display:flex;gap:10px;padding-top:8px;border-top:1px solid rgba(255,255,255,.12)}\n      #ws-builder-tools .wsbt-system-row .wsbt-action{flex:1 1 0;min-width:0;text-align:center;padding:6px 3px;font-size:9px;white-space:nowrap}\n      #ws-builder-tools .wsbt-quick-row{display:flex;gap:10px;padding-top:8px;border-top:1px solid rgba(255,255,255,.12)}\n      #ws-builder-tools .wsbt-quick-row .wsbt-action{flex:1 1 0;min-width:0;padding:6px 4px;font-size:10px;white-space:nowrap}\n      #ws-builder-tools .wsbt-grid-row{display:none;padding-top:8px;border-top:1px solid rgba(255,255,255,.12)}\n      #ws-builder-tools .wsbt-grid-row label{flex:1 1 0;min-width:0}\n      #ws-builder-tools .wsbt-grid-row input[type=text]{width:0;min-width:0;flex:1 1 auto;text-transform:uppercase}\n      #ws-builder-tools .wsbt-grid-colour-label{flex:0 0 auto!important;gap:5px;white-space:nowrap}\n      #ws-builder-tools .wsbt-grid-colour{width:34px!important;min-width:34px!important;flex:0 0 34px!important;height:30px;padding:2px;border-radius:6px;cursor:pointer;background:rgba(41,44,48,.90)}\n      #ws-builder-tools .wsbt-grid-colour::-webkit-color-swatch-wrapper{padding:0}\n      #ws-builder-tools .wsbt-grid-colour::-webkit-color-swatch{border:0;border-radius:4px}\n      #ws-builder-tools .wsbt-grid-colour::-moz-color-swatch{border:0;border-radius:4px}\n      #ws-builder-tools .wsbt-swap{flex:0 0 30px;width:30px;height:30px;padding:0;font-size:18px}\n      #ws-builder-tools .wsbt-zoom-row{display:none;flex-direction:column;align-items:stretch;padding-top:8px;border-top:1px solid rgba(255,255,255,.12)}\n      #ws-builder-tools .wsbt-zoom-line{display:flex;align-items:center;gap:8px}\n      #ws-builder-tools input[type=range]{flex:1;min-width:80px}\n      #ws-builder-tools .wsbt-presets{display:flex;gap:10px;flex-wrap:wrap}\n      #ws-builder-tools .wsbt-presets button{padding:4px 7px}\n      #ws-builder-tools .wsbt-min-icon{display:none}\n      #ws-builder-tools.wsbt-minimised{width:42px;min-width:42px;max-width:42px;height:42px;min-height:42px;max-height:42px;border-radius:50%;cursor:grab;overflow:hidden;background:rgba(8,8,8,.90);touch-action:none}\n      #ws-builder-tools.wsbt-minimised:active{cursor:grabbing}\n      #ws-builder-tools.wsbt-minimised .wsbt-head,#ws-builder-tools.wsbt-minimised .wsbt-body{display:none}\n      #ws-builder-tools.wsbt-minimised .wsbt-min-icon{display:flex;position:absolute;inset:0;align-items:center;justify-content:center;pointer-events:none}\n      #ws-builder-tools.wsbt-minimised .wsbt-min-icon svg{display:block;width:22px;height:22px;stroke:#fff;fill:none;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}\n\n      #wsbt-spacing-info{display:none;position:fixed;z-index:2147483644;width:300px;max-width:calc(100vw - 8px);max-height:calc(100vh - 8px);overflow:auto;background:rgba(10,10,10,.92);color:#fff;border:1px solid rgba(255,255,255,.18);border-radius:10px;box-shadow:0 10px 28px rgba(0,0,0,.38);font:500 12px/1.35 system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif}\n      #wsbt-spacing-info *{box-sizing:border-box}\n      #wsbt-spacing-info .wsbt-spacing-info-head{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:9px 10px;cursor:move;border-bottom:1px solid rgba(255,255,255,.12);font-weight:700}\n      #wsbt-spacing-info .wsbt-spacing-info-head small{font-size:9px;font-weight:500;opacity:.55;white-space:nowrap}\n      #wsbt-spacing-info .wsbt-spacing-info-body{padding:7px 10px 9px}\n      #wsbt-spacing-info .wsbt-spacing-info-row{display:flex;justify-content:space-between;gap:12px;padding:4px 0;border-top:1px solid rgba(255,255,255,.10)}\n      #wsbt-spacing-info .wsbt-spacing-info-row:first-child{border-top:0}\n      #wsbt-spacing-info .wsbt-spacing-info-row span{opacity:.76}\n      #wsbt-spacing-info .wsbt-spacing-info-row strong{text-align:right}\n      #wsbt-spacing-info .wsbt-spacing-info-foot{margin-top:7px;font-size:10px;opacity:.60}\n    .fd-font-t{font-family:\"Times New Roman\",Times,serif;font-style:normal;font-weight:700;display:inline-block;line-height:1;}\n</style>\n    <script>\n    (function(){\n      if(window.__WS_BUILDER_TOOLS__) return;\n      window.__WS_BUILDER_TOOLS__=true;\n\n      const DEVICES=[\n        ['Custom','',''],['iPhone 17 PMax/16 PMax',440,956],['iPhone 17/17 Pro/16 Pro',402,874],['iPhone 15/15 Pro/16/16e',393,852],['iPhone 15 PMax/15+/16+',430,932],['iPhone 14 Pro',393,852],['iPhone 14 PMax',430,932],['iPhone 12/12 Pro/13/13 Pro/14',390,844],['iPhone 12 PMax/13 PMax/14+',428,926],['iPhone 12 mini/13 mini',360,780],['iPhone XR/XS Max/11/11 PMax',414,896],['iPhone X/XS/11 Pro',375,812],['iPhone SE 2020/2022',375,667],['Galaxy S25 Ultra/S26 Ultra',412,891],['Galaxy S25+/S26+',412,891],['Galaxy S25/S26',360,780],['Galaxy S24 Ultra',412,889],['Galaxy S24+',412,889],['Galaxy S24',360,780],['Galaxy S22 Ultra/S23 Ultra',384,854],['Galaxy S22+/S23+',360,780],['Galaxy S22/S23',360,780],['Galaxy S21 Ultra',384,854],['Galaxy S20/S20+/S21/S21+',360,800],['Galaxy S10/S10+',360,760],['Galaxy S9/S9+',360,740],['Galaxy Z Fold 6',966,1162],['Galaxy Z Flip 6',360,840],['Galaxy A55/A35',412,892],['Galaxy A54/A34',412,892],['Galaxy A53/A33',412,915],['Galaxy A52/A32',412,915],['Galaxy A51/A50',412,892]\n      ];\n      const KEY_POS='ws_builder_tools_pos',KEY_MIN='ws_builder_tools_min',KEY_VH='imran_elementor_preview_zoom_vh',KEY_GRID='ws_builder_tools_grid_v2';\n      let savedSizes={desktop:null,tablet:null,mobile:null},elementorDefaults={desktop:null,tablet:null,mobile:null},currentMode='',zoomOpen=false,gridOn=false;\n      let rulerOn=false,spacingOn=false,rulerCleanup=null,spacingCleanup=null;\n      let zOn=false,zCleanup=null;\n      let typeMode='',typeCleanup=null;\n      let spacingInfoBox=null,spacingInfoDetached=false,spacingInfoDrag=null;\n      const KEY_SPACING_INFO='ws_builder_tools_spacing_info_v2';\n      function clampFloating(el,left,top){\n        const gap=4,r=el.getBoundingClientRect();\n        return{left:clamp(left,gap,Math.max(gap,window.innerWidth-r.width-gap)),top:clamp(top,gap,Math.max(gap,window.innerHeight-r.height-gap))};\n      }\n      function positionSpacingInfo(force=false){\n        if(!spacingInfoBox||spacingInfoBox.style.display==='none'||(spacingInfoDetached&&!force))return;\n        const deck=document.getElementById('ws-builder-tools');if(!deck)return;\n        const dr=deck.getBoundingClientRect(),ir=spacingInfoBox.getBoundingClientRect(),gap=8;\n        let left=dr.left,top=dr.bottom+gap;\n        if(top+ir.height>window.innerHeight-4)top=dr.top-ir.height-gap;\n        if(top<4)top=4;\n        const pos=clampFloating(spacingInfoBox,left,top);\n        spacingInfoBox.style.left=pos.left+'px';spacingInfoBox.style.top=pos.top+'px';spacingInfoBox.style.right='auto';spacingInfoBox.style.bottom='auto';\n      }\n      function setSpacingInfo(title,rows=[],foot='Click another element to begin a new measurement.'){\n        if(!spacingInfoBox)return;\n        spacingInfoBox.style.display='block';\n        spacingInfoBox.querySelector('.wsbt-spacing-info-title').textContent=title;\n        spacingInfoBox.querySelector('.wsbt-spacing-info-body').innerHTML=rows.map(row=>'<div class=\"wsbt-spacing-info-row\"><span>'+row[0]+'</span><strong>'+row[1]+'</strong></div>').join('')+'<div class=\"wsbt-spacing-info-foot\">'+foot+'</div>';\n        positionSpacingInfo();\n      }\n      function hideSpacingInfo(){if(spacingInfoBox)spacingInfoBox.style.display='none';}\n      const DEFAULT_GRID_COLOUR='#000000';\n      let gridSettings={wValue:'10',wUnit:'vw',hValue:'10',hUnit:'vh',colour:DEFAULT_GRID_COLOUR};\n      try{\n        const stored=JSON.parse(localStorage.getItem(KEY_GRID)||'null');\n        if(stored&&stored.wValue&&stored.wUnit&&stored.hValue&&stored.hUnit){\n          gridSettings={...gridSettings,...stored};\n          if(!/^#[0-9a-f]{6}$/i.test(gridSettings.colour||''))gridSettings.colour=DEFAULT_GRID_COLOUR;\n        }\n      }catch(_){}\n      const clamp=(v,min,max)=>Math.max(min,Math.min(max,v));\n      function mode(){if(document.querySelector('[data-testid=\"switch-device-to-mobile\"][aria-selected=\"true\"]'))return'mobile';if(document.querySelector('[data-testid=\"switch-device-to-tablet\"][aria-selected=\"true\"]'))return'tablet';return'desktop';}\n      function preview(){return document.getElementById('elementor-preview-responsive-wrapper')||document.querySelector('.elementor-preview-responsive-wrapper')||document.querySelector('iframe#elementor-preview-iframe');}\n      function iframe(){return document.querySelector('iframe#elementor-preview-iframe,iframe.elementor-preview-iframe,#elementor-preview iframe,iframe[src*=\"elementor-preview=\"]')||document.querySelector('iframe');}\n      function clean(v){v=String(v||'').trim();return /^\\d+$/.test(v)?v+'px':v;}\n      function applySize(w,h){const p=preview();if(!p)return;if(w||h)p.setAttribute('data-wsbt-custom-size','1');else p.removeAttribute('data-wsbt-custom-size');p.style.width=w||'';p.style.maxWidth=w||'';p.style.height=h||'';p.style.maxHeight=h||'';}\n\n      function ensureScaleWrapper(doc){let wrap=doc.getElementById('imran-scale-wrap');if(wrap)return wrap;let bg='#fff';try{const c=doc.defaultView.getComputedStyle(doc.body).backgroundColor;if(c&&c!=='rgba(0, 0, 0, 0)'&&c!=='transparent')bg=c;}catch(e){}wrap=doc.createElement('div');wrap.id='imran-scale-wrap';wrap.style.transformOrigin='0 0';wrap.style.willChange='transform';wrap.style.width='100%';wrap.style.minHeight='100%';wrap.style.background=bg;while(doc.body.firstChild)wrap.appendChild(doc.body.firstChild);doc.body.appendChild(wrap);if(!doc.getElementById('imran-scale-style')){const st=doc.createElement('style');st.id='imran-scale-style';st.textContent=':root{--imran-scale:1;--imran-right-fill-left:100%}#imran-scale-wrap{transform:scale(var(--imran-scale));width:calc(100% / var(--imran-scale));min-height:calc(100vh / var(--imran-scale));position:relative;z-index:1}body::after{content:\"\";position:fixed;top:0;bottom:0;right:0;left:var(--imran-right-fill-left,100%);background:#0C0D0E;pointer-events:none;z-index:0}body{overflow:auto!important;background:transparent!important}';doc.head.appendChild(st);}return wrap;}\n      function updateFill(doc){try{const w=doc.getElementById('imran-scale-wrap');if(!w)return;const r=w.getBoundingClientRect();doc.documentElement.style.setProperty('--imran-right-fill-left',Math.max(0,Math.round(r.right))+'px');}catch(e){}}\n      function applyZoom(vh){vh=clamp(parseInt(vh,10)||100,10,2000);try{sessionStorage.setItem(KEY_VH,String(vh));}catch(e){}const f=iframe();if(!f)return;const run=()=>{try{const d=f.contentDocument||f.contentWindow.document;ensureScaleWrapper(d);d.documentElement.style.setProperty('--imran-scale',String(clamp(100/vh,.05,1)));updateFill(d);}catch(e){}};try{const d=f.contentDocument||f.contentWindow.document;if(d&&d.readyState!=='loading')run();else f.addEventListener('load',run,{once:true});}catch(e){run();}}\n      function parseGridDimension(raw,fallbackValue,fallbackUnit){\n        const match=String(raw||'').trim().match(/^([0-9]*\\.?[0-9]+)\\s*(px|em|vh|vw|%)$/i);\n        if(!match||parseFloat(match[1])<=0)return{value:fallbackValue,unit:fallbackUnit,text:fallbackValue+fallbackUnit.toUpperCase()};\n        const value=String(parseFloat(match[1])),unit=match[2].toLowerCase();\n        return{value,unit,text:value+unit.toUpperCase()};\n      }\n      function gridSize(){const gx=parseGridDimension(gridSettings.wValue+gridSettings.wUnit,'10','vw'),gy=parseGridDimension(gridSettings.hValue+gridSettings.hUnit,'10','vh');return gx.value+gx.unit+' '+gy.value+gy.unit;}\n      function normaliseGridColour(value){return /^#[0-9a-f]{6}$/i.test(String(value||''))?String(value).toLowerCase():DEFAULT_GRID_COLOUR;}\n      function gridLineColour(){\n        const hex=normaliseGridColour(gridSettings.colour);\n        const r=parseInt(hex.slice(1,3),16),g=parseInt(hex.slice(3,5),16),b=parseInt(hex.slice(5,7),16);\n        return 'rgba('+r+','+g+','+b+',.75)';\n      }\n      function saveGridSettings(){try{localStorage.setItem(KEY_GRID,JSON.stringify(gridSettings));}catch(_){} }\n      function applyGrid(){const f=iframe();if(!f)return;const run=()=>{try{const d=f.contentDocument||f.contentWindow.document;let grid=d.getElementById('wsbt-alignment-grid');if(!gridOn){if(grid)grid.remove();return;}if(!grid){grid=d.createElement('div');grid.id='wsbt-alignment-grid';grid.setAttribute('aria-hidden','true');Object.assign(grid.style,{position:'fixed',inset:'0',pointerEvents:'none',zIndex:'2147483646'});(d.body||d.documentElement).appendChild(grid);}const line=gridLineColour();grid.style.backgroundImage='linear-gradient(to right, '+line+' 1px, transparent 1px), linear-gradient(to bottom, '+line+' 1px, transparent 1px)';grid.style.backgroundSize=gridSize();}catch(_){}};try{const d=f.contentDocument||f.contentWindow.document;if(d&&d.readyState!=='loading')run();else f.addEventListener('load',run,{once:true});}catch(_){}}\n\n      function frameDocument(){\n        const f=iframe();\n        if(!f)return null;\n        try{return f.contentDocument||f.contentWindow.document||null;}catch(_){return null;}\n      }\n      function pageElementFromTarget(target,doc){\n        if(!target||target===doc.body||target===doc.documentElement)return null;\n        if(target.closest&&target.closest('#wsbt-ruler-layer,#wsbt-spacing-layer,#wsbt-type-layer,.wsbt-ruler-guide,.wsbt-ruler-bar,.wsbt-type-badge'))return null;\n\n        const visible=el=>{\n          if(!el||el===doc.body||el===doc.documentElement)return false;\n          try{const r=el.getBoundingClientRect(),cs=(doc.defaultView||window).getComputedStyle(el);return r.width>1&&r.height>1&&cs.display!=='none'&&cs.visibility!=='hidden';}catch(_){return false;}\n        };\n        const isContainer=el=>!!(el&&el.matches&&el.matches('.e-con,.e-con-inner,.elementor-container,.elementor-section,.elementor-column,[data-element_type=\"container\"],[data-element_type=\"section\"],[data-element_type=\"column\"]'));\n        const explicitWidgetSelector='.elementor-widget,[data-element_type=\"widget\"],[data-widget_type],[class*=\"elementor-widget-\"]';\n        const explicit=target.closest&&target.closest(explicitWidgetSelector);\n        if(explicit&&visible(explicit))return explicit;\n\n        // Elementor 4/Atomic elements may use data IDs without the older widget classes.\n        const atomic=target.closest&&target.closest('[data-elementor-id],[data-id]');\n        if(atomic&&visible(atomic)&&!isContainer(atomic))return atomic;\n\n        // Prefer a meaningful rendered control/text/media box over a surrounding Flexbox.\n        const semantic=target.closest&&target.closest('h1,h2,h3,h4,h5,h6,p,a,button,img,picture,video,svg,input,select,textarea,form,ul,ol,li,blockquote,figure');\n        if(semantic&&visible(semantic)){\n          const semanticWidget=semantic.closest&&semantic.closest(explicitWidgetSelector);\n          return semanticWidget&&visible(semanticWidget)?semanticWidget:semantic;\n        }\n\n        // A container is selectable only when no more specific widget/content item exists.\n        const container=target.closest&&target.closest('.e-con,.elementor-container,.elementor-section,.elementor-column,[data-element_type=\"container\"],[data-element_type=\"section\"],[data-element_type=\"column\"]');\n        return container&&visible(container)?container:(visible(target)?target:null);\n      }\n      function installRulerMode(){\n        const d=frameDocument();if(!d||!d.body)return()=>{};\n        const win=d.defaultView||window;\n        let old=d.getElementById('wsbt-ruler-layer');if(old)old.remove();\n        const layer=d.createElement('div');layer.id='wsbt-ruler-layer';layer.setAttribute('aria-hidden','true');\n        Object.assign(layer.style,{position:'fixed',inset:'0',zIndex:'2147483645',pointerEvents:'none',font:'11px/1 system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif'});\n        const top=d.createElement('div');top.className='wsbt-ruler-bar';top.title='Drag down to create a horizontal guide';\n        Object.assign(top.style,{position:'fixed',left:'24px',right:'0',top:'0',height:'24px',pointerEvents:'auto',cursor:'ns-resize',background:'rgba(10,10,10,.75)',borderBottom:'1px solid rgba(255,255,255,.7)',backgroundImage:'repeating-linear-gradient(to right,transparent 0,transparent 9px,rgba(255,255,255,.35) 9px,rgba(255,255,255,.35) 10px),repeating-linear-gradient(to right,transparent 0,transparent 49px,rgba(255,255,255,.85) 49px,rgba(255,255,255,.85) 50px)'});\n        const left=d.createElement('div');left.className='wsbt-ruler-bar';left.title='Drag right to create a vertical guide';\n        Object.assign(left.style,{position:'fixed',left:'0',top:'24px',bottom:'0',width:'24px',pointerEvents:'auto',cursor:'ew-resize',background:'rgba(10,10,10,.75)',borderRight:'1px solid rgba(255,255,255,.7)',backgroundImage:'repeating-linear-gradient(to bottom,transparent 0,transparent 9px,rgba(255,255,255,.35) 9px,rgba(255,255,255,.35) 10px),repeating-linear-gradient(to bottom,transparent 0,transparent 49px,rgba(255,255,255,.85) 49px,rgba(255,255,255,.85) 50px)'});\n        const corner=d.createElement('div');Object.assign(corner.style,{position:'fixed',left:'0',top:'0',width:'24px',height:'24px',pointerEvents:'auto',background:'rgba(10,10,10,.75)',borderRight:'1px solid rgba(255,255,255,.7)',borderBottom:'1px solid rgba(255,255,255,.7)'});\n        const help=d.createElement('div');help.textContent='Drag from a ruler to add a guide. Drag a guide to move it. Double-click a guide to remove it.';\n        Object.assign(help.style,{position:'fixed',left:'32px',bottom:'10px',padding:'6px 8px',background:'rgba(10,10,10,.75)',color:'#fff',borderRadius:'5px',pointerEvents:'none',boxShadow:'0 2px 10px rgba(0,0,0,.28)'});\n        layer.append(corner,top,left,help);d.body.appendChild(layer);\n        const guideCleanups=[];\n        function addGuide(axis,start){\n          const g=d.createElement('div');g.className='wsbt-ruler-guide';g.dataset.axis=axis;\n          const label=d.createElement('span');\n          Object.assign(g.style,{position:'fixed',pointerEvents:'auto',zIndex:'2147483646',cursor:axis==='x'?'ew-resize':'ns-resize'});\n          if(axis==='x')Object.assign(g.style,{top:'0',bottom:'0',width:'1px',background:'#ff3b30',left:start+'px'});\n          else Object.assign(g.style,{left:'0',right:'0',height:'1px',background:'#ff3b30',top:start+'px'});\n          Object.assign(label.style,{position:'absolute',background:'#ff3b30',color:'#fff',padding:'3px 5px',borderRadius:'3px',whiteSpace:'nowrap',pointerEvents:'none'});\n          if(axis==='x')Object.assign(label.style,{top:'28px',left:'4px'});else Object.assign(label.style,{left:'28px',top:'4px'});\n          g.appendChild(label);layer.appendChild(g);\n          function setPos(pos){pos=Math.max(0,Math.round(pos));if(axis==='x'){g.style.left=pos+'px';label.textContent=pos+'px';}else{g.style.top=pos+'px';label.textContent=pos+'px';}}\n          setPos(start);\n          let dragging=false;\n          const down=e=>{dragging=true;g.setPointerCapture&&g.setPointerCapture(e.pointerId);e.preventDefault();e.stopPropagation();};\n          const move=e=>{if(!dragging)return;setPos(axis==='x'?e.clientX:e.clientY);e.preventDefault();};\n          const up=e=>{dragging=false;try{g.releasePointerCapture&&g.releasePointerCapture(e.pointerId);}catch(_){} };\n          const dbl=e=>{e.preventDefault();e.stopPropagation();g.remove();};\n          g.addEventListener('pointerdown',down);g.addEventListener('pointermove',move);g.addEventListener('pointerup',up);g.addEventListener('pointercancel',up);g.addEventListener('dblclick',dbl);\n          guideCleanups.push(()=>{g.removeEventListener('pointerdown',down);g.removeEventListener('pointermove',move);g.removeEventListener('pointerup',up);g.removeEventListener('pointercancel',up);g.removeEventListener('dblclick',dbl);g.remove();});\n          return{g,setPos};\n        }\n        function beginFromRuler(axis,e){\n          e.preventDefault();e.stopPropagation();const item=addGuide(axis,axis==='x'?e.clientX:e.clientY);let active=true;\n          const move=ev=>{if(!active)return;item.setPos(axis==='x'?ev.clientX:ev.clientY);};\n          const up=()=>{active=false;win.removeEventListener('pointermove',move,true);win.removeEventListener('pointerup',up,true);};\n          win.addEventListener('pointermove',move,true);win.addEventListener('pointerup',up,true);\n        }\n        const topDown=e=>beginFromRuler('y',e),leftDown=e=>beginFromRuler('x',e);\n        top.addEventListener('pointerdown',topDown);left.addEventListener('pointerdown',leftDown);\n        return()=>{top.removeEventListener('pointerdown',topDown);left.removeEventListener('pointerdown',leftDown);guideCleanups.forEach(fn=>{try{fn();}catch(_){}});layer.remove();};\n      }\n      function installSpacingMode(){\n        const d=frameDocument();if(!d||!d.body)return()=>{};\n        const win=d.defaultView||window;\n        let old=d.getElementById('wsbt-spacing-layer');if(old)old.remove();\n        const layer=d.createElement('div');layer.id='wsbt-spacing-layer';layer.setAttribute('aria-hidden','true');\n        Object.assign(layer.style,{position:'fixed',inset:'0',zIndex:'2147483647',pointerEvents:'none',font:'12px/1.35 system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif'});\n        d.body.appendChild(layer);\n\n        let first=null,second=null,hover=null;\n        const roundSigned=n=>Math.round(n);\n        const absRound=n=>Math.round(Math.abs(n));\n        const centreX=r=>r.left+r.width/2;\n        const centreY=r=>r.top+r.height/2;\n        const hOverlap=(a,b)=>Math.max(0,Math.min(a.right,b.right)-Math.max(a.left,b.left));\n        const vOverlap=(a,b)=>Math.max(0,Math.min(a.bottom,b.bottom)-Math.max(a.top,b.top));\n        const visibleRect=el=>{try{return el.getBoundingClientRect();}catch(_){return null;}};\n        const elementName=(el,index)=>{\n          let text='';\n          try{text=(el.getAttribute('aria-label')||el.getAttribute('data-widget_type')||el.getAttribute('data-element_type')||el.id||'').trim();}catch(_){}\n          if(!text){try{text=(el.innerText||el.textContent||'').trim().replace(/\\s+/g,' ').slice(0,30);}catch(_){}}\n          return text||('Element '+index);\n        };\n        const direction=(value,positive,negative)=>{\n          const n=roundSigned(value);\n          if(n===0)return'0px · aligned';\n          return Math.abs(n)+'px · '+(n>0?positive:negative);\n        };\n        function rectBox(el,colour,fill){\n          const r=visibleRect(el);if(!r)return null;\n          const o=d.createElement('div');\n          Object.assign(o.style,{position:'fixed',left:r.left+'px',top:r.top+'px',width:r.width+'px',height:r.height+'px',border:'2px solid '+colour,background:fill,pointerEvents:'none',boxSizing:'border-box'});\n          layer.appendChild(o);return o;\n        }\n        function label(text,x,y,colour='#111'){\n          const n=d.createElement('div');n.textContent=text;\n          Object.assign(n.style,{position:'fixed',left:Math.max(6,Math.min(win.innerWidth-6,x))+'px',top:Math.max(6,Math.min(win.innerHeight-6,y))+'px',transform:'translate(-50%,-50%)',padding:'4px 6px',background:colour,color:'#fff',borderRadius:'4px',whiteSpace:'nowrap',boxShadow:'0 1px 5px rgba(0,0,0,.4)'});\n          layer.appendChild(n);return n;\n        }\n        function line(x1,y1,x2,y2,colour){\n          const l=d.createElement('div'),len=Math.hypot(x2-x1,y2-y1),angle=Math.atan2(y2-y1,x2-x1)*180/Math.PI;\n          Object.assign(l.style,{position:'fixed',left:x1+'px',top:y1+'px',width:len+'px',height:'2px',background:colour,transformOrigin:'0 50%',transform:'rotate('+angle+'deg)',pointerEvents:'none'});\n          layer.appendChild(l);return l;\n        }\n        function mark(n){if(n)n.setAttribute('data-wsbt-measure','1');return n;}\n        function clearDraw(){layer.querySelectorAll('[data-wsbt-measure]').forEach(n=>n.remove());}\n        function directChildUnder(parent,el){\n          let node=el;\n          while(node&&node.parentElement&&node.parentElement!==parent)node=node.parentElement;\n          return node&&node.parentElement===parent?node:null;\n        }\n        function sharedLayout(aEl,bEl){\n          let p=aEl.parentElement;\n          while(p&&p!==d.body){\n            if(p.contains(bEl)){\n              const aItem=directChildUnder(p,aEl),bItem=directChildUnder(p,bEl);\n              if(aItem&&bItem&&aItem!==bItem){\n                try{\n                  const cs=win.getComputedStyle(p),display=cs.display;\n                  if(display==='flex'||display==='inline-flex'){\n                    const dir=(cs.flexDirection||'row').startsWith('column')?'column':'row';\n                    const rowGap=parseFloat(cs.rowGap)||0,columnGap=parseFloat(cs.columnGap)||0;\n                    return{type:'flex',parent:p,direction:dir,rowGap,columnGap,alignItems:cs.alignItems||'',justifyContent:cs.justifyContent||'',aItem,bItem};\n                  }\n                  if(display==='grid'||display==='inline-grid'){\n                    return{type:'grid',parent:p,direction:'grid',rowGap:parseFloat(cs.rowGap)||0,columnGap:parseFloat(cs.columnGap)||0,alignItems:cs.alignItems||'',justifyContent:cs.justifyContent||'',aItem,bItem};\n                  }\n                }catch(_){}\n              }\n            }\n            p=p.parentElement;\n          }\n          return null;\n        }\n        function drawHorizontal(a,b,gap){\n          let x1,x2;if(a.right<=b.left){x1=a.right;x2=b.left;}else{x1=b.right;x2=a.left;}\n          const y=Math.max(a.top,b.top)+Math.max(10,Math.min(a.height,b.height)/2);\n          if(gap>0)mark(line(x1,y,x2,y,'#ffb300'));\n          mark(label(absRound(gap)+'px horizontal gap',(x1+x2)/2,y-10,'#a56b00'));\n        }\n        function drawVertical(a,b,gap){\n          let y1,y2;if(a.bottom<=b.top){y1=a.bottom;y2=b.top;}else{y1=b.bottom;y2=a.top;}\n          const x=Math.max(a.left,b.left)+Math.max(10,Math.min(a.width,b.width)/2);\n          if(gap>0)mark(line(x,y1,x,y2,'#e53935'));\n          mark(label(absRound(gap)+'px vertical gap',x+12,(y1+y2)/2,'#b71c1c'));\n        }\n        function draw(){\n          clearDraw();\n          if(first)mark(rectBox(first,'#2196f3','rgba(33,150,243,.10)'));\n          if(second)mark(rectBox(second,'#42d36b','rgba(66,211,107,.10)'));\n          if(!first||!second)return;\n\n          const a=visibleRect(first),b=visibleRect(second);if(!a||!b)return;\n          const horizontalGap=a.right<=b.left?b.left-a.right:(b.right<=a.left?a.left-b.right:0);\n          const verticalGap=a.bottom<=b.top?b.top-a.bottom:(b.bottom<=a.top?a.top-b.bottom:0);\n          const horizontalOverlap=hOverlap(a,b),verticalOverlap=vOverlap(a,b);\n          const firstName=elementName(first,1),secondName=elementName(second,2);\n          const layout=sharedLayout(first,second);\n          let relation='overlap';\n\n          if(layout&&layout.type==='flex')relation=layout.direction==='column'?'stacked':'side';\n          else if(layout&&layout.type==='grid'){\n            if(verticalGap>0&&horizontalGap===0)relation='stacked';\n            else if(horizontalGap>0&&verticalGap===0)relation='side';\n            else relation=Math.abs(centreY(b)-centreY(a))>=Math.abs(centreX(b)-centreX(a))?'stacked':'side';\n          }else if(verticalGap>0&&(horizontalOverlap>0||Math.abs(centreY(b)-centreY(a))>=Math.abs(centreX(b)-centreX(a))))relation='stacked';\n          else if(horizontalGap>0&&(verticalOverlap>0||Math.abs(centreX(b)-centreX(a))>Math.abs(centreY(b)-centreY(a))))relation='side';\n          else if(verticalGap>0)relation='stacked';\n          else if(horizontalGap>0)relation='side';\n\n          const rows=[];\n          if(relation==='stacked'){\n            const firstOnTop=centreY(a)<=centreY(b),topRect=firstOnTop?a:b,bottomRect=firstOnTop?b:a;\n            const topName=firstOnTop?firstName:secondName,bottomName=firstOnTop?secondName:firstName;\n            const gap=Math.max(0,bottomRect.top-topRect.bottom);\n            drawVertical(topRect,bottomRect,gap);\n            rows.push(['Rendered vertical gap',absRound(gap)+'px']);\n            if(layout){\n              const cssGap=layout.type==='flex'&&layout.direction==='column'?layout.rowGap:layout.rowGap;\n              rows.push(['Parent row gap',absRound(cssGap)+'px']);\n              rows.push(['Parent alignment',(layout.alignItems||'normal').replace(/-/g,' ')]);\n            }\n            rows.push(['Left-edge offset',direction(bottomRect.left-topRect.left,bottomName+' further right',bottomName+' further left')]);\n            rows.push(['Right-edge offset',direction(bottomRect.right-topRect.right,bottomName+' further right',bottomName+' further left')]);\n            rows.push(['Horizontal centre offset',direction(centreX(bottomRect)-centreX(topRect),bottomName+' further right',bottomName+' further left')]);\n            rows.push(['Width difference',direction(bottomRect.width-topRect.width,bottomName+' wider',topName+' wider')]);\n            setSpacingInfo('Stacked elements',rows);\n          }else if(relation==='side'){\n            const firstOnLeft=centreX(a)<=centreX(b),leftRect=firstOnLeft?a:b,rightRect=firstOnLeft?b:a;\n            const leftName=firstOnLeft?firstName:secondName,rightName=firstOnLeft?secondName:firstName;\n            const gap=Math.max(0,rightRect.left-leftRect.right);\n            drawHorizontal(leftRect,rightRect,gap);\n            rows.push(['Rendered horizontal gap',absRound(gap)+'px']);\n            if(layout){\n              const cssGap=layout.type==='flex'&&layout.direction==='row'?layout.columnGap:layout.columnGap;\n              rows.push(['Parent column gap',absRound(cssGap)+'px']);\n              rows.push(['Parent alignment',(layout.alignItems||'normal').replace(/-/g,' ')]);\n            }\n            rows.push(['Top-edge offset',direction(rightRect.top-leftRect.top,rightName+' lower',rightName+' higher')]);\n            rows.push(['Bottom-edge offset',direction(rightRect.bottom-leftRect.bottom,rightName+' lower',rightName+' higher')]);\n            rows.push(['Vertical centre offset',direction(centreY(rightRect)-centreY(leftRect),rightName+' lower',rightName+' higher')]);\n            rows.push(['Height difference',direction(rightRect.height-leftRect.height,rightName+' taller',leftName+' taller')]);\n            setSpacingInfo('Side-by-side elements',rows);\n          }else{\n            rows.push(['Horizontal overlap',absRound(horizontalOverlap)+'px']);\n            rows.push(['Vertical overlap',absRound(verticalOverlap)+'px']);\n            rows.push(['Top-edge offset',direction(b.top-a.top,secondName+' lower',secondName+' higher')]);\n            rows.push(['Left-edge offset',direction(b.left-a.left,secondName+' further right',secondName+' further left')]);\n            rows.push(['Centre offset',roundSigned(centreX(b)-centreX(a))+'px X · '+roundSigned(centreY(b)-centreY(a))+'px Y']);\n            rows.push(['Size difference',roundSigned(b.width-a.width)+'px W · '+roundSigned(b.height-a.height)+'px H']);\n            setSpacingInfo('Overlapping or nested elements',rows);\n          }\n        }\n        function onMove(e){\n          if(e.target.closest&&e.target.closest('#wsbt-spacing-layer'))return;\n          const el=pageElementFromTarget(e.target,d);if(el===hover)return;hover=el;\n          layer.querySelectorAll('[data-wsbt-hover]').forEach(n=>n.remove());\n          if(el&&!first&&!second){const n=rectBox(el,'#fff','rgba(255,255,255,.06)');if(n)n.setAttribute('data-wsbt-hover','1');}\n        }\n        function onClick(e){\n          const el=pageElementFromTarget(e.target,d);if(!el)return;\n          e.preventDefault();e.stopPropagation();e.stopImmediatePropagation();\n          layer.querySelectorAll('[data-wsbt-hover]').forEach(n=>n.remove());\n          if(!first||second){first=el;second=null;setSpacingInfo('Spacing Inspector',[], 'Click the second element.');}\n          else if(el!==first){second=el;}\n          draw();\n        }\n        function refresh(){if(first||second)draw();}\n        setSpacingInfo('Spacing Inspector',[], 'Click the first element.');\n        d.addEventListener('mousemove',onMove,true);d.addEventListener('click',onClick,true);\n        win.addEventListener('scroll',refresh,{passive:true});win.addEventListener('resize',refresh);\n        return()=>{\n          d.removeEventListener('mousemove',onMove,true);d.removeEventListener('click',onClick,true);\n          win.removeEventListener('scroll',refresh);win.removeEventListener('resize',refresh);layer.remove();\n        };\n      }\n      function typographyItems(d,mode){\n        const win=d.defaultView||window;\n\n        function isElementVisible(el){\n          try{\n            const cs=win.getComputedStyle(el);\n            if(cs.display==='none'||cs.visibility==='hidden'||parseFloat(cs.opacity||'1')===0)return false;\n            const r=el.getBoundingClientRect();\n            return r.width>=1&&r.height>=1&&r.bottom>=0&&r.right>=0&&r.top<=win.innerHeight&&r.left<=win.innerWidth;\n          }catch(_){return false;}\n        }\n\n        if(mode==='tags'){\n          return Array.from(d.querySelectorAll('h1,h2,h3,h4,h5,h6'))\n            .filter(el=>!el.closest('#wsbt-ruler-layer,#wsbt-spacing-layer,#wsbt-type-layer')&&isElementVisible(el))\n            .map(el=>({el,styleEl:el,rect:el.getBoundingClientRect(),text:(el.innerText||el.textContent||'').trim()}));\n        }\n\n        const items=[];\n        const seenParents=new Set();\n        const walker=d.createTreeWalker(\n          d.body||d.documentElement,\n          win.NodeFilter.SHOW_TEXT,\n          {\n            acceptNode(node){\n              const value=(node.nodeValue||'').replace(/\\s+/g,' ').trim();\n              if(!value)return win.NodeFilter.FILTER_REJECT;\n              const parent=node.parentElement;\n              if(!parent)return win.NodeFilter.FILTER_REJECT;\n              if(parent.closest('#wsbt-ruler-layer,#wsbt-spacing-layer,#wsbt-type-layer,script,style,noscript,template,svg,canvas'))return win.NodeFilter.FILTER_REJECT;\n              if(!isElementVisible(parent))return win.NodeFilter.FILTER_REJECT;\n              return win.NodeFilter.FILTER_ACCEPT;\n            }\n          }\n        );\n\n        let node;\n        while((node=walker.nextNode())){\n          const parent=node.parentElement;\n          if(!parent||seenParents.has(parent))continue;\n\n          const directNodes=Array.from(parent.childNodes).filter(n=>n.nodeType===3&&(n.nodeValue||'').trim());\n          const directText=directNodes.map(n=>n.nodeValue||'').join(' ').replace(/\\s+/g,' ').trim();\n          if(!directText)continue;\n\n          const range=d.createRange();\n          try{\n            range.selectNodeContents(parent);\n          }catch(_){\n            continue;\n          }\n\n          const rects=Array.from(range.getClientRects()).filter(r=>r.width>=1&&r.height>=1);\n          if(!rects.length)continue;\n\n          const left=Math.min(...rects.map(r=>r.left));\n          const top=Math.min(...rects.map(r=>r.top));\n          const right=Math.max(...rects.map(r=>r.right));\n          const bottom=Math.max(...rects.map(r=>r.bottom));\n          const rect={left,top,right,bottom,width:right-left,height:bottom-top};\n\n          items.push({el:parent,styleEl:parent,rect,text:directText});\n          seenParents.add(parent);\n        }\n\n        // Form controls can render text without a DOM text node.\n        d.querySelectorAll('input[type=\"button\"],input[type=\"submit\"],button').forEach(el=>{\n          if(el.closest('#wsbt-type-layer')||!isElementVisible(el))return;\n          const textValue=(el.value||el.innerText||el.textContent||'').replace(/\\s+/g,' ').trim();\n          if(!textValue)return;\n          if(items.some(item=>item.el===el||el.contains(item.el)))return;\n          items.push({el,styleEl:el,rect:el.getBoundingClientRect(),text:textValue});\n        });\n\n        // One visible text rectangle gets one badge. This removes Elementor's\n        // nested link/wrapper duplicates without discarding the actual text style.\n        const kept=[];\n        items\n          .sort((a,b)=>(a.rect.width*a.rect.height)-(b.rect.width*b.rect.height))\n          .forEach(item=>{\n            const duplicate=kept.some(existing=>{\n              const sameText=item.text===existing.text;\n              const sameRect=\n                Math.abs(item.rect.left-existing.rect.left)<5&&\n                Math.abs(item.rect.top-existing.rect.top)<5&&\n                Math.abs(item.rect.width-existing.rect.width)<10&&\n                Math.abs(item.rect.height-existing.rect.height)<10;\n              return sameText&&sameRect;\n            });\n            if(!duplicate)kept.push(item);\n          });\n\n        return kept;\n      }\n\n      function installTypographyMode(mode){\n        const d=frameDocument();if(!d||!d.body)return()=>{};\n        const win=d.defaultView||window;\n        let old=d.getElementById('wsbt-type-layer');if(old)old.remove();\n        const layer=d.createElement('div');layer.id='wsbt-type-layer';layer.setAttribute('aria-hidden','true');\n        Object.assign(layer.style,{position:'fixed',inset:'0',pointerEvents:'none',zIndex:'2147483645',overflow:'visible'});\n        (d.body||d.documentElement).appendChild(layer);\n        let raf=0,observer=null,interval=null;\n\n        function firstFontFamily(full){\n          const value=(full||'').trim();\n          if(!value)return'Unknown';\n          const match=value.match(/^\\s*(?:([\"'])(.*?)\\1|([^,]+))/);\n          return ((match&&(match[2]||match[3]))||'Unknown').trim().replace(/^['\"]+|['\"]+$/g,'').trim()||'Unknown';\n        }\n\n        function badgeText(item){\n          const el=item.styleEl||item.el;\n          const cs=win.getComputedStyle(el);\n          if(mode==='tags'){\n            const tag=item.el.tagName.toUpperCase();\n            return{main:tag,title:'HTML heading tag: '+tag};\n          }\n          if(mode==='size'){\n            const px=parseFloat(cs.fontSize)||0;\n            const root=parseFloat(win.getComputedStyle(d.documentElement).fontSize)||16;\n            const rem=px/root;\n            const remText=(Math.round(rem*100)/100).toString().replace(/\\.0+$/,'')+'rem';\n            const pxText=(Math.round(px*10)/10).toString().replace(/\\.0$/,'')+'px';\n            return{main:remText,title:'Current rendered font size: '+pxText+' ('+remText+')'};\n          }\n          const full=(cs.fontFamily||'Unknown').trim();\n          const first=firstFontFamily(full);\n          const display=first.length>24?first.slice(0,23)+'…':first;\n          return{main:display,title:'Rendered font family: '+first+' · Full stack: '+full};\n        }\n\n        function render(){\n          raf=0;\n          layer.innerHTML='';\n          const placed=[];\n\n          typographyItems(d,mode).forEach(item=>{\n            const r=item.rect;\n            if(r.bottom<0||r.right<0||r.top>win.innerHeight||r.left>win.innerWidth)return;\n\n            const data=badgeText(item),isTag=mode==='tags';\n            const badgeLeft=Math.max(2,Math.min(win.innerWidth-(isTag?28:80),r.left+4));\n            const badgeTop=Math.max(2,r.top+4);\n\n            const duplicate=placed.some(p=>\n              Math.abs(p.left-badgeLeft)<8&&\n              Math.abs(p.top-badgeTop)<8\n            );\n            if(duplicate)return;\n            placed.push({left:badgeLeft,top:badgeTop});\n\n            const b=d.createElement('span');\n            b.className='wsbt-type-badge';\n            b.textContent=data.main;\n            b.title=data.title;\n            Object.assign(b.style,{\n              position:'fixed',\n              left:badgeLeft+'px',\n              top:badgeTop+'px',\n              width:isTag?'24px':'auto',\n              height:isTag?'24px':'auto',\n              minWidth:isTag?'24px':'0',\n              maxWidth:isTag?'24px':mode==='family'?'180px':'110px',\n              overflow:'hidden',\n              textOverflow:'ellipsis',\n              whiteSpace:'nowrap',\n              padding:isTag?'0':'3px 7px',\n              borderRadius:isTag?'50%':'5px',\n              background:isTag?'rgba(229,57,53,.82)':mode==='size'?'rgba(35,116,201,.88)':'rgba(105,70,170,.88)',\n              color:'#fff',\n              font:isTag?'400 12px/24px system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif':'600 11px/1.25 system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif',\n              textAlign:'center',\n              display:isTag?'inline-flex':'inline-block',\n              alignItems:isTag?'center':'initial',\n              justifyContent:isTag?'center':'initial',\n              boxShadow:isTag?'0 0 0 2px rgba(255,255,255,.9),0 1px 4px rgba(0,0,0,.35)':'0 1px 4px rgba(0,0,0,.35)',\n              border:isTag?'0':'1px solid rgba(255,255,255,.75)',\n              pointerEvents:'none'\n            });\n            layer.appendChild(b);\n          });\n        }\n\n        function schedule(){if(!raf)raf=win.requestAnimationFrame(render);}\n        render();\n        try{\n          observer=new win.MutationObserver(schedule);\n          observer.observe(d.documentElement,{subtree:true,childList:true,attributes:true,characterData:true});\n        }catch(_){}\n        win.addEventListener('scroll',schedule,{passive:true});\n        win.addEventListener('resize',schedule,{passive:true});\n        interval=win.setInterval(schedule,650);\n\n        return()=>{\n          if(raf)win.cancelAnimationFrame(raf);\n          if(observer)observer.disconnect();\n          if(interval)win.clearInterval(interval);\n          win.removeEventListener('scroll',schedule);\n          win.removeEventListener('resize',schedule);\n          layer.remove();\n        };\n      }\n\n      function installZIndexMode(){\n        const d=frameDocument();if(!d||!d.body)return()=>{};\n        const win=d.defaultView||window;\n        let old=d.getElementById('wsbt-z-layer');if(old)old.remove();\n        const layer=d.createElement('div');layer.id='wsbt-z-layer';layer.setAttribute('aria-hidden','true');\n        Object.assign(layer.style,{position:'fixed',inset:'0',pointerEvents:'none',zIndex:'2147483646',overflow:'visible'});\n        (d.body||d.documentElement).appendChild(layer);\n        let raf=0,observer=null,interval=null;\n\n        function visiblePageElements(){\n          const skip=new Set(['HTML','HEAD','META','LINK','STYLE','SCRIPT','NOSCRIPT','TEMPLATE','BR','SOURCE','TRACK']);\n          const result=[];\n          Array.from(d.body.querySelectorAll('*')).forEach(el=>{\n            if(skip.has(el.tagName)||el===layer||el.closest('#wsbt-z-layer'))return;\n            let cs,r;\n            try{cs=win.getComputedStyle(el);r=el.getBoundingClientRect();}catch(_){return;}\n            if(cs.display==='none'||cs.visibility==='hidden'||parseFloat(cs.opacity||'1')===0)return;\n            if(r.width<6||r.height<6)return;\n            if(r.bottom<0||r.right<0||r.top>win.innerHeight||r.left>win.innerWidth)return;\n            const z=(cs.zIndex||'auto').trim()||'auto';\n            if(z.toLowerCase()==='auto')return;\n            result.push({el,cs,r,z});\n          });\n          return result;\n        }\n\n        function render(){\n          raf=0;layer.innerHTML='';\n          const placed=[];\n          visiblePageElements().forEach(item=>{\n            const r=item.r,z=item.z;\n            const left=Math.max(2,Math.min(win.innerWidth-48,r.left+3));\n            const top=Math.max(2,Math.min(win.innerHeight-20,r.top+3));\n            const duplicate=placed.some(p=>Math.abs(p.left-left)<5&&Math.abs(p.top-top)<5&&p.z===z);\n            if(duplicate)return;\n            placed.push({left,top,z});\n            const b=d.createElement('span');\n            b.className='wsbt-z-badge';\n            b.textContent=z;\n            b.title='Computed z-index: '+z+' · '+item.el.tagName.toLowerCase();\n            Object.assign(b.style,{position:'fixed',left:left+'px',top:top+'px',minWidth:'20px',maxWidth:'70px',height:'18px',padding:'0 5px',borderRadius:'9px',background:'rgba(20,20,20,.86)',color:'#fff',border:'1px solid rgba(255,255,255,.78)',boxShadow:'0 1px 4px rgba(0,0,0,.35)',font:'700 10px/16px system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif',textAlign:'center',whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis',pointerEvents:'none'});\n            layer.appendChild(b);\n          });\n        }\n        function schedule(){if(!raf)raf=win.requestAnimationFrame(render);}\n        render();\n        try{observer=new win.MutationObserver(schedule);observer.observe(d.documentElement,{subtree:true,childList:true,attributes:true,characterData:false});}catch(_){}\n        win.addEventListener('scroll',schedule,{passive:true});\n        win.addEventListener('resize',schedule,{passive:true});\n        interval=win.setInterval(schedule,700);\n        return()=>{if(raf)win.cancelAnimationFrame(raf);if(observer)observer.disconnect();if(interval)win.clearInterval(interval);win.removeEventListener('scroll',schedule);win.removeEventListener('resize',schedule);layer.remove();};\n      }\n\n      function stopTypography(){\n        typeMode='';if(typeCleanup){try{typeCleanup();}catch(_){}typeCleanup=null;}\n        ['wsbt-html-tags','wsbt-font-size','wsbt-font-family'].forEach(id=>{const b=document.getElementById(id);if(b)b.setAttribute('aria-pressed','false');});\n      }\n      function stopAllInspectors(except=''){\n        if(except!=='ruler')stopRuler();\n        if(except!=='spacing')stopSpacing();\n        if(except!=='z')stopZIndex();\n        if(except!=='type')stopTypography();\n      }\n      function stopRuler(){rulerOn=false;if(rulerCleanup){try{rulerCleanup();}catch(_){}rulerCleanup=null;}const b=document.getElementById('wsbt-ruler');if(b)b.setAttribute('aria-pressed','false');}\n      function stopSpacing(){spacingOn=false;if(spacingCleanup){try{spacingCleanup();}catch(_){}spacingCleanup=null;}hideSpacingInfo();const b=document.getElementById('wsbt-spacing');if(b)b.setAttribute('aria-pressed','false');}\n      function stopZIndex(){zOn=false;if(zCleanup){try{zCleanup();}catch(_){}zCleanup=null;}const b=document.getElementById('wsbt-z-index');if(b)b.setAttribute('aria-pressed','false');}\n\n      function init(){\n        if(!document.body)return;\n        const box=document.createElement('div');box.id='ws-builder-tools';\n        box.innerHTML='<div class=\"wsbt-min-icon\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\"><path d=\"M9 6V4.75A1.75 1.75 0 0 1 10.75 3h2.5A1.75 1.75 0 0 1 15 4.75V6\"></path><rect x=\"3\" y=\"6\" width=\"18\" height=\"14\" rx=\"2.25\"></rect><path d=\"M3 11.25h18\"></path><path d=\"M9.5 11.25v1.5h5v-1.5\"></path></svg></div><div class=\"wsbt-head\"><button class=\"wsbt-action\" id=\"wsbt-audit\">Audit</button><button class=\"wsbt-action\" id=\"wsbt-zoom\">Zoom</button><button class=\"wsbt-action\" id=\"wsbt-css\" title=\"Open Elementor Custom CSS\">CSS</button><button class=\"wsbt-action wsbt-icon-action\" id=\"wsbt-grid\" title=\"Show adjustable alignment grid\" aria-label=\"Show adjustable alignment grid\" aria-pressed=\"false\"><svg viewBox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M4 4h16v16H4zM4 9.33h16M4 14.67h16M9.33 4v16M14.67 4v16\"></path></svg></button><button class=\"wsbt-action wsbt-min\" id=\"wsbt-min\" title=\"Close to suitcase\" aria-label=\"Close tools to suitcase\"><svg viewBox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M9 6V4.75A1.75 1.75 0 0 1 10.75 3h2.5A1.75 1.75 0 0 1 15 4.75V6\"></path><rect x=\"3\" y=\"6\" width=\"18\" height=\"14\" rx=\"2.25\"></rect><path d=\"M3 11.25h18\"></path><path d=\"M9.5 11.25v1.5h5v-1.5\"></path></svg></button></div><div class=\"wsbt-body\"><div class=\"wsbt-row\"><label>W <input id=\"wsbt-width\" type=\"text\"></label><label>H <input id=\"wsbt-height\" type=\"text\"></label><button class=\"wsbt-action wsbt-swap\" id=\"wsbt-swap\" title=\"Swap width and height\">⇄</button><button class=\"wsbt-action wsbt-icon-action\" id=\"wsbt-reset\" title=\"Reset viewport dimensions and zoom\" aria-label=\"Reset viewport dimensions and zoom\"><svg viewBox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M4 4v6h6\"></path><path d=\"M5.6 15.5A8 8 0 1 0 6 7.2L4 10\"></path></svg></button></div><div class=\"wsbt-row wsbt-inspect-row\"><button class=\"wsbt-action\" id=\"wsbt-ruler\" title=\"Show rulers and drag guides from the top or left edge\" aria-pressed=\"false\">Ruler</button><button class=\"wsbt-action\" id=\"wsbt-spacing\" title=\"Select two page elements to measure spacing and alignment\" aria-pressed=\"false\">Spacing</button><button class=\"wsbt-action\" id=\"wsbt-z-index\" title=\"Show the computed Z-index value of visible page elements\" aria-pressed=\"false\">Z-Index</button><button class=\"wsbt-action\" id=\"wsbt-html-tags\" title=\"Show live HTML heading tags\" aria-pressed=\"false\"><span class=\"fd-font-t\">T</span>-Tags</button><button class=\"wsbt-action\" id=\"wsbt-font-size\" title=\"Show each text element’s current computed font size\" aria-pressed=\"false\"><span class=\"fd-font-t\">T</span>-Size</button><button class=\"wsbt-action\" id=\"wsbt-font-family\" title=\"Show each text element’s computed font family\" aria-pressed=\"false\"><span class=\"fd-font-t\">T</span>-Family</button></div><div class=\"wsbt-row wsbt-system-row\"><button class=\"wsbt-action\" id=\"wsbt-v4-classes\" title=\"Open Elementor V4 Class Manager\">V4 Classes</button><button class=\"wsbt-action\" id=\"wsbt-v4-variables\" title=\"Open Elementor V4 Variables Manager\">V4 Variables</button><button class=\"wsbt-action\" id=\"wsbt-v3-colours\" title=\"Open Elementor V3 Global Colours\">V3 Colours</button><button class=\"wsbt-action\" id=\"wsbt-v3-fonts\" title=\"Open Elementor V3 Global Fonts\">V3 Fonts</button></div><div class=\"wsbt-row wsbt-quick-row\"><button class=\"wsbt-action\" id=\"wsbt-save\">Save</button><button class=\"wsbt-action\" id=\"wsbt-regen\">Regen</button><button class=\"wsbt-action\" id=\"wsbt-purge\">Purge</button><button class=\"wsbt-action\" id=\"wsbt-revisions\" title=\"Delete revisions for this page only\">Revisions</button><button class=\"wsbt-action\" id=\"wsbt-reload\">Reload</button></div><div class=\"wsbt-row wsbt-grid-row\" id=\"wsbt-grid-row\"><label>Grid W <input id=\"wsbt-grid-w\" type=\"text\" value=\"10VW\" inputmode=\"decimal\" autocomplete=\"off\" spellcheck=\"false\" aria-label=\"Grid width including unit, for example 10VW\"></label><label>Grid H <input id=\"wsbt-grid-h\" type=\"text\" value=\"10VH\" inputmode=\"decimal\" autocomplete=\"off\" spellcheck=\"false\" aria-label=\"Grid height including unit, for example 10VH\"></label><label class=\"wsbt-grid-colour-label\">Colour <input id=\"wsbt-grid-colour\" class=\"wsbt-grid-colour\" type=\"color\" value=\"#000000\" aria-label=\"Choose grid line colour\" title=\"Choose grid line colour\"></label></div><div class=\"wsbt-row wsbt-device-row\" id=\"wsbt-device-row\"><select id=\"wsbt-device\"></select></div><div class=\"wsbt-row wsbt-zoom-row\" id=\"wsbt-zoom-row\"><div class=\"wsbt-zoom-line\"><input id=\"wsbt-range\" type=\"range\" min=\"100\" max=\"1000\" step=\"10\"><span id=\"wsbt-out\"></span><input id=\"wsbt-vh\" type=\"number\" min=\"10\" max=\"2000\" step=\"1\" style=\"width:70px\"></div><div class=\"wsbt-presets\"><button class=\"wsbt-action\" data-vh=\"100\">100vh</button><button class=\"wsbt-action\" data-vh=\"300\">300vh</button><button class=\"wsbt-action\" data-vh=\"500\">500vh</button><button class=\"wsbt-action\" data-vh=\"1000\">1000vh</button></div></div></div>';\n        document.body.appendChild(box);\n        spacingInfoBox=document.createElement('div');spacingInfoBox.id='wsbt-spacing-info';\n        spacingInfoBox.innerHTML='<div class=\"wsbt-spacing-info-head\"><span class=\"wsbt-spacing-info-title\">Spacing Inspector</span><small>Drag · double-click to reattach</small></div><div class=\"wsbt-spacing-info-body\"></div>';\n        document.body.appendChild(spacingInfoBox);\n        try{\n          const saved=JSON.parse(localStorage.getItem(KEY_SPACING_INFO)||'null');\n          if(saved&&saved.detached){spacingInfoDetached=true;spacingInfoBox.style.left=Number(saved.left||4)+'px';spacingInfoBox.style.top=Number(saved.top||4)+'px';}\n        }catch(_){}\n        const spacingHead=spacingInfoBox.querySelector('.wsbt-spacing-info-head');\n        spacingHead.addEventListener('pointerdown',e=>{\n          const r=spacingInfoBox.getBoundingClientRect();spacingInfoDrag={id:e.pointerId,dx:e.clientX-r.left,dy:e.clientY-r.top,moved:false};spacingHead.setPointerCapture(e.pointerId);e.preventDefault();\n        });\n        spacingHead.addEventListener('pointermove',e=>{\n          if(!spacingInfoDrag)return;\n          spacingInfoDrag.moved=true;spacingInfoDetached=true;\n          const pos=clampFloating(spacingInfoBox,e.clientX-spacingInfoDrag.dx,e.clientY-spacingInfoDrag.dy);\n          spacingInfoBox.style.left=pos.left+'px';spacingInfoBox.style.top=pos.top+'px';spacingInfoBox.style.right='auto';spacingInfoBox.style.bottom='auto';\n        });\n        spacingHead.addEventListener('pointerup',e=>{\n          if(!spacingInfoDrag)return;try{spacingHead.releasePointerCapture(e.pointerId);}catch(_){}\n          spacingInfoDrag=null;\n          try{localStorage.setItem(KEY_SPACING_INFO,JSON.stringify({detached:spacingInfoDetached,left:spacingInfoBox.offsetLeft,top:spacingInfoBox.offsetTop}));}catch(_){}\n        });\n        spacingHead.addEventListener('dblclick',()=>{\n          spacingInfoDetached=false;try{localStorage.removeItem(KEY_SPACING_INFO);}catch(_){}positionSpacingInfo(true);\n        });\n        const head=box.querySelector('.wsbt-head'),w=box.querySelector('#wsbt-width'),h=box.querySelector('#wsbt-height'),sel=box.querySelector('#wsbt-device'),deviceRow=box.querySelector('#wsbt-device-row'),range=box.querySelector('#wsbt-range'),out=box.querySelector('#wsbt-out'),vhInput=box.querySelector('#wsbt-vh'),gridRow=box.querySelector('#wsbt-grid-row'),gridW=box.querySelector('#wsbt-grid-w'),gridH=box.querySelector('#wsbt-grid-h'),gridColour=box.querySelector('#wsbt-grid-colour'),rulerButton=box.querySelector('#wsbt-ruler'),spacingButton=box.querySelector('#wsbt-spacing'),zButton=box.querySelector('#wsbt-z-index'),htmlTagsButton=box.querySelector('#wsbt-html-tags'),fontSizeButton=box.querySelector('#wsbt-font-size'),fontFamilyButton=box.querySelector('#wsbt-font-family');\n        DEVICES.forEach((d,i)=>{const o=document.createElement('option');o.value=i;o.textContent=d[0];sel.appendChild(o);});\n        try{const p=JSON.parse(localStorage.getItem(KEY_POS)||'null');if(p){box.style.left=Number(p.left||0)+'px';box.style.top=Number(p.top||0)+'px';box.style.right='auto';box.style.bottom='auto';}if(localStorage.getItem(KEY_MIN)==='1')box.classList.add('wsbt-minimised');}catch(e){}\n        const VIEWPORT_GAP=4;\n        let clampFrame=0;\n        function savePosition(){try{localStorage.setItem(KEY_POS,JSON.stringify({left:box.offsetLeft,top:box.offsetTop}));}catch(_){} }\n        function clampIntoViewport(save=false){\n          if(!box.isConnected)return;\n          const r=box.getBoundingClientRect();\n          const vw=Math.max(0,window.innerWidth||document.documentElement.clientWidth||0);\n          const vh=Math.max(0,window.innerHeight||document.documentElement.clientHeight||0);\n          const maxLeft=Math.max(VIEWPORT_GAP,vw-r.width-VIEWPORT_GAP);\n          const maxTop=Math.max(VIEWPORT_GAP,vh-r.height-VIEWPORT_GAP);\n          const left=clamp(r.left,VIEWPORT_GAP,maxLeft);\n          const top=clamp(r.top,VIEWPORT_GAP,maxTop);\n          if(Math.abs(r.left-left)>.5||Math.abs(r.top-top)>.5||box.style.right!=='auto'||box.style.bottom!=='auto'){\n            box.style.left=left+'px';box.style.top=top+'px';box.style.right='auto';box.style.bottom='auto';\n          }\n          if(save)savePosition();\n          positionSpacingInfo();\n        }\n        function scheduleClamp(save=false){cancelAnimationFrame(clampFrame);clampFrame=requestAnimationFrame(()=>{clampFrame=requestAnimationFrame(()=>clampIntoViewport(save));});}\n        function expandAtSuitcase(anchorX,anchorY){box.classList.remove('wsbt-minimised');const suitcase=box.querySelector('#wsbt-min'),iconRect=suitcase.getBoundingClientRect(),boxRect=box.getBoundingClientRect();const iconX=iconRect.left+(iconRect.width/2),iconY=iconRect.top+(iconRect.height/2);box.style.left=(boxRect.left+anchorX-iconX)+'px';box.style.top=(boxRect.top+anchorY-iconY)+'px';box.style.right='auto';box.style.bottom='auto';try{localStorage.setItem(KEY_MIN,'0');}catch(_){}scheduleClamp(true);}\n        scheduleClamp(false);\n        let miniDrag=null,miniMoved=false;\n        box.addEventListener('pointerdown',e=>{if(!box.classList.contains('wsbt-minimised'))return;const r=box.getBoundingClientRect();miniMoved=false;miniDrag={id:e.pointerId,startX:e.clientX,startY:e.clientY,dx:e.clientX-r.left,dy:e.clientY-r.top};box.setPointerCapture(e.pointerId);e.preventDefault();});\n        box.addEventListener('pointermove',e=>{if(!miniDrag||!box.classList.contains('wsbt-minimised'))return;if(Math.abs(e.clientX-miniDrag.startX)>3||Math.abs(e.clientY-miniDrag.startY)>3)miniMoved=true;box.style.left=clamp(e.clientX-miniDrag.dx,4,window.innerWidth-box.offsetWidth-4)+'px';box.style.top=clamp(e.clientY-miniDrag.dy,4,window.innerHeight-box.offsetHeight-4)+'px';box.style.right='auto';box.style.bottom='auto';});\n        box.addEventListener('pointerup',e=>{if(!miniDrag)return;try{box.releasePointerCapture(e.pointerId);}catch(_){}const wasMoved=miniMoved,anchor=box.getBoundingClientRect();miniDrag=null;miniMoved=false;if(!wasMoved&&box.classList.contains('wsbt-minimised')){expandAtSuitcase(anchor.left+(anchor.width/2),anchor.top+(anchor.height/2));}else{clampIntoViewport(true);}});\n        box.querySelector('#wsbt-min').onclick=e=>{e.stopPropagation();const icon=e.currentTarget.getBoundingClientRect(),anchorX=icon.left+(icon.width/2),anchorY=icon.top+(icon.height/2);box.classList.add('wsbt-minimised');box.style.left=(anchorX-21)+'px';box.style.top=(anchorY-21)+'px';box.style.right='auto';box.style.bottom='auto';clampIntoViewport(true);try{localStorage.setItem(KEY_MIN,'1');}catch(_){}};\n        box.querySelector('#wsbt-audit').onclick=()=>{if(typeof window.WSBuilderToolsAuditToggle==='function')window.WSBuilderToolsAuditToggle();};\n        box.querySelector('#wsbt-zoom').onclick=()=>{zoomOpen=!zoomOpen;box.querySelector('#wsbt-zoom-row').style.display=zoomOpen?'flex':'none';scheduleClamp(true);};\n        rulerButton.onclick=()=>{if(rulerOn){stopRuler();return;}stopAllInspectors('ruler');rulerOn=true;rulerButton.setAttribute('aria-pressed','true');rulerCleanup=installRulerMode();};\n        spacingButton.onclick=()=>{if(spacingOn){stopSpacing();return;}stopAllInspectors('spacing');spacingOn=true;spacingButton.setAttribute('aria-pressed','true');spacingCleanup=installSpacingMode();};\n        zButton.onclick=()=>{if(zOn){stopZIndex();return;}stopAllInspectors('z');zOn=true;zButton.setAttribute('aria-pressed','true');zCleanup=installZIndexMode();};\n        function toggleTypography(modeName,button){if(typeMode===modeName){stopTypography();return;}stopAllInspectors('type');stopTypography();typeMode=modeName;button.setAttribute('aria-pressed','true');typeCleanup=installTypographyMode(modeName);}\n        htmlTagsButton.onclick=()=>toggleTypography('tags',htmlTagsButton);\n        fontSizeButton.onclick=()=>toggleTypography('size',fontSizeButton);\n        fontFamilyButton.onclick=()=>toggleTypography('family',fontFamilyButton);\n        const cssButton=box.querySelector('#wsbt-css');\n        function runCustomCssCommand(){try{if(window.elementor&&elementor.channels&&elementor.channels.editor){elementor.channels.editor.execute('custom-css:open');return true;}}catch(_){}return false;}\n        function clickCustomCssItem(){const item=document.querySelector('.elementor-panel-menu-item-settings-custom-css');if(!item)return false;item.click();setTimeout(runCustomCssCommand,50);return true;}\n        function showCssUnavailable(){const old=cssButton.textContent;cssButton.textContent='N/A';cssButton.title='Elementor Custom CSS is unavailable. Elementor Pro may be required.';setTimeout(()=>{cssButton.textContent=old;cssButton.title='Open Elementor Custom CSS';},2200);}\n        function openCustomCss(){\n          if(clickCustomCssItem())return;\n          const isMac=/Mac|iPhone|iPad|iPod/i.test(navigator.platform||navigator.userAgent||'');\n          document.dispatchEvent(new KeyboardEvent('keydown',{key:'K',code:'KeyK',keyCode:75,which:75,bubbles:true,cancelable:true,ctrlKey:!isMac,metaKey:isMac}));\n          const started=Date.now();\n          const waitForItem=setInterval(()=>{if(clickCustomCssItem()){clearInterval(waitForItem);return;}if(Date.now()-started>=4000){clearInterval(waitForItem);if(!runCustomCssCommand())showCssUnavailable();}},100);\n        }\n        cssButton.onclick=openCustomCss;\n\n        function isVisibleControl(el){\n          if(!el||el.closest('#ws-builder-tools'))return false;\n          const style=window.getComputedStyle(el);\n          const rect=el.getBoundingClientRect();\n          return style.display!=='none'&&style.visibility!=='hidden'&&rect.width>0&&rect.height>0;\n        }\n        function normaliseUiText(value){return String(value||'').replace(/\\s+/g,' ').trim().toLowerCase();}\n        function clickUiControl(labels,selectors=[]){\n          const wanted=(Array.isArray(labels)?labels:[labels]).map(normaliseUiText).filter(Boolean);\n          for(const selector of selectors){\n            try{\n              const direct=Array.from(document.querySelectorAll(selector)).find(isVisibleControl);\n              if(direct){direct.click();return true;}\n            }catch(_){}\n          }\n          const nodes=Array.from(document.querySelectorAll('button,[role=\"button\"],[role=\"tab\"],a,.elementor-panel-menu-item,.MuiButtonBase-root'));\n          const found=nodes.find(el=>{\n            if(!isVisibleControl(el))return false;\n            const haystack=normaliseUiText((el.getAttribute('aria-label')||'')+' '+(el.getAttribute('title')||'')+' '+(el.textContent||''));\n            return wanted.some(label=>haystack===label||haystack.includes(label));\n          });\n          if(found){found.click();return true;}\n          return false;\n        }\n        function flashNavUnavailable(button,message){\n          const old=button.textContent;\n          button.textContent='N/A';\n          button.disabled=true;\n          window.setTimeout(()=>{button.textContent=old;button.disabled=false;},1600);\n          if(message)console.warn('FlightDeck: '+message);\n        }\n        function waitForUiAction(action,timeout=3500){\n          return new Promise(resolve=>{\n            const started=Date.now();\n            const tryNow=()=>{\n              try{if(action()){resolve(true);return;}}catch(_){}\n              if(Date.now()-started>=timeout){resolve(false);return;}\n              window.setTimeout(tryNow,100);\n            };\n            tryNow();\n          });\n        }\n        function getElementorRouteNames(){\n          try{\n            if(!window.$e||!$e.routes||typeof $e.routes.getAll!=='function')return [];\n            const all=$e.routes.getAll();\n            if(Array.isArray(all))return all.map(route=>typeof route==='string'?route:(route&&route.name)||'').filter(Boolean);\n            if(all&&typeof all==='object')return Object.keys(all);\n          }catch(_){}\n          return [];\n        }\n        function tryElementorRoute(candidates,requiredWords=[]){\n          if(!window.$e||typeof $e.route!=='function')return false;\n          const registered=getElementorRouteNames();\n          const normalise=value=>String(value||'').toLowerCase();\n          const exact=[...candidates];\n          if(registered.length){\n            for(const route of registered){\n              const haystack=normalise(route);\n              if(requiredWords.every(word=>haystack.includes(normalise(word)))&&!exact.includes(route))exact.push(route);\n            }\n          }\n          for(const route of exact){\n            try{\n              if(registered.length&&!registered.includes(route))continue;\n              $e.route(route);\n              return true;\n            }catch(_){}\n          }\n          return false;\n        }\n        async function openV4DesignSystem(target){\n          const targetButton=box.querySelector(target==='classes'?'#wsbt-v4-classes':'#wsbt-v4-variables');\n          const tabLabels=target==='classes'?['classes','class manager']:['variables','variables manager'];\n          const selectors=target==='classes'\n            ? ['[role=\"tab\"][aria-label*=\"Classes\" i]','[role=\"tab\"][title*=\"Classes\" i]','[data-testid*=\"class\" i]']\n            : ['[role=\"tab\"][aria-label*=\"Variables\" i]','[role=\"tab\"][title*=\"Variables\" i]','[data-testid*=\"variable\" i]'];\n\n          // Never use a generic [role=\\\"tab\\\"] selector here: Elementor has many unrelated tabs.\n          // Only an explicitly identified Classes/Variables control is safe to click.\n          // IMPORTANT: when Design System is already open, clicking its top-bar icon\n          // toggles the panel closed. Try the requested tab FIRST so switching between\n          // Classes and Variables always happens in a single click.\n          if(clickUiControl(tabLabels,selectors))return;\n\n          const designOpened=clickUiControl(\n            ['design system'],\n            ['[aria-label*=\"Design System\" i]','[title*=\"Design System\" i]','[data-testid*=\"design-system\" i]']\n          );\n          if(!designOpened){\n            flashNavUnavailable(targetButton,'Elementor V4 Design System control was not found. V4 may be disabled on this site.');\n            return;\n          }\n          const opened=await waitForUiAction(()=>clickUiControl(tabLabels,selectors),2600);\n          if(!opened&&target==='classes')flashNavUnavailable(targetButton,'The V4 Class Manager tab could not be found.');\n          // Variables is Elementor's default Design System tab, so an opened panel is\n          // already the correct destination if no separately clickable Variables tab exists.\n        }\n        function v4DesignSystemIsOpen(){\n          const selectors=[\n            '[role=\"tab\"][aria-label*=\"Classes\" i]',\n            '[role=\"tab\"][title*=\"Classes\" i]',\n            '[role=\"tab\"][aria-label*=\"Variables\" i]',\n            '[role=\"tab\"][title*=\"Variables\" i]',\n            '[data-testid*=\"class\" i]',\n            '[data-testid*=\"variable\" i]'\n          ];\n          return selectors.some(selector=>{\n            try{return Array.from(document.querySelectorAll(selector)).some(isVisibleControl);}catch(_){return false;}\n          });\n        }\n        async function exitV4DesignSystem(){\n          if(!v4DesignSystemIsOpen())return true;\n\n          // The Design System toolbar control is a toggle. Only click it when a\n          // Classes/Variables control proves that the V4 Design System is open.\n          const closed=clickUiControl(\n            ['design system'],\n            ['[aria-label*=\"Design System\" i]','[title*=\"Design System\" i]','[data-testid*=\"design-system\" i]']\n          );\n          if(!closed)return false;\n\n          // Elementor animates/unmounts the V4 surface. Do not open Site Settings\n          // until those controls have disappeared, otherwise the left panel can\n          // enter a blank mixed V3/V4 state.\n          const started=Date.now();\n          while(Date.now()-started<3000){\n            if(!v4DesignSystemIsOpen()){\n              await new Promise(resolve=>window.setTimeout(resolve,180));\n              return true;\n            }\n            await new Promise(resolve=>window.setTimeout(resolve,80));\n          }\n          return !v4DesignSystemIsOpen();\n        }\n        async function openV3Global(target){\n          const targetButton=box.querySelector(target==='colours'?'#wsbt-v3-colours':'#wsbt-v3-fonts');\n\n          // V4 Design System is a separate editor surface. Close it completely before\n          // invoking V3 Site Settings; attempting to route while V4 is still mounted\n          // can leave Elementor displaying an empty panel.\n          const exitedV4=await exitV4DesignSystem();\n          if(!exitedV4){\n            flashNavUnavailable(targetButton,'Could not cleanly exit the Elementor V4 Design System before opening V3 Site Settings.');\n            return;\n          }\n\n          // Elementor's Site Settings is itself a routed panel. Open it through the\n          // official command first, then route directly to the registered Global\n          // Colours / Global Fonts subsection whenever this Elementor build exposes it.\n          try{\n            if(window.$e&&typeof $e.run==='function'){\n              const result=$e.run('panel/global/open');\n              if(result&&typeof result.then==='function')await result;\n            }\n          }catch(_){}\n\n          const routed=target==='colours'\n            ? tryElementorRoute(\n                ['panel/global-colors','panel/global/colors','panel/global/settings-global-colors','panel/global/settings-colors'],\n                ['global','color']\n              )\n            : tryElementorRoute(\n                ['panel/global-fonts','panel/global/typography','panel/global/settings-global-typography','panel/global/settings-typography','panel/global/settings-global-fonts'],\n                ['global','typ']\n              ) || tryElementorRoute([],['global','font']);\n          if(routed)return;\n\n          // DOM fallback for builds where the internal route name is not exposed.\n          // Because panel/global/open was already requested above, Site Settings should\n          // now be visible; target its exact Global Colours / Global Fonts menu item.\n          const labels=target==='colours'\n            ? ['global colors','global colours']\n            : ['global fonts'];\n          const selectors=target==='colours'\n            ? ['[data-tab=\"global-colors\"]','[data-tab=\"global_colors\"]','[data-setting=\"global-colors\"]','[aria-label*=\"Global Colors\" i]','[aria-label*=\"Global Colours\" i]']\n            : ['[data-tab=\"global-typography\"]','[data-tab=\"global_typography\"]','[data-setting=\"global-typography\"]','[aria-label*=\"Global Fonts\" i]'];\n          const opened=await waitForUiAction(()=>clickUiControl(labels,selectors),3200);\n          if(!opened)flashNavUnavailable(targetButton,'The requested Elementor V3 Site Settings section could not be found.');\n        }\n        box.querySelector('#wsbt-v4-classes').onclick=()=>openV4DesignSystem('classes');\n        box.querySelector('#wsbt-v4-variables').onclick=()=>openV4DesignSystem('variables');\n        box.querySelector('#wsbt-v3-colours').onclick=()=>openV3Global('colours');\n        box.querySelector('#wsbt-v3-fonts').onclick=()=>openV3Global('fonts');\n        const gridButton=box.querySelector('#wsbt-grid');\n        gridW.value=gridSettings.wValue+gridSettings.wUnit.toUpperCase();gridH.value=gridSettings.hValue+gridSettings.hUnit.toUpperCase();\n        function updateGridSettings(){\n          const gx=parseGridDimension(gridW.value,'10','vw'),gy=parseGridDimension(gridH.value,'10','vh');\n          gridSettings={wValue:gx.value,wUnit:gx.unit,hValue:gy.value,hUnit:gy.unit,colour:normaliseGridColour(gridColour.value||gridSettings.colour)};\n          gridW.value=gx.text;gridH.value=gy.text;saveGridSettings();applyGrid();\n          gridButton.title=gridOn?'Hide '+gx.text+' × '+gy.text+' alignment grid':'Show adjustable alignment grid';\n        }\n        gridColour.value=normaliseGridColour(gridSettings.colour);\n        [gridW,gridH].forEach(control=>{control.addEventListener('change',updateGridSettings);control.addEventListener('keydown',e=>{if(e.key==='Enter'){e.preventDefault();updateGridSettings();control.blur();}});});\n        gridColour.addEventListener('input',()=>{\n          gridSettings.colour=normaliseGridColour(gridColour.value);\n          saveGridSettings();\n          applyGrid();\n        });\n        gridColour.addEventListener('change',()=>{\n          gridSettings.colour=normaliseGridColour(gridColour.value);\n          gridColour.value=gridSettings.colour;\n          saveGridSettings();\n          applyGrid();\n        });\n        gridButton.onclick=()=>{gridOn=!gridOn;gridRow.style.display=gridOn?'flex':'none';gridButton.setAttribute('aria-pressed',String(gridOn));gridButton.setAttribute('aria-label',gridOn?'Hide alignment grid':'Show adjustable alignment grid');updateGridSettings();scheduleClamp(true);};\n        function custom(){const m=mode(),cw=clean(w.value),ch=clean(h.value);savedSizes[m]={width:cw,height:ch};applySize(cw,ch);}\n        w.onchange=()=>{sel.value=0;custom();};h.onchange=()=>{sel.value=0;custom();};\n        sel.onchange=function(){const d=DEVICES[this.value];if(!d||!d[1])return;w.value=d[1]+'px';h.value=d[2]+'px';custom();};\n        box.querySelector('#wsbt-swap').onclick=()=>{const a=w.value;w.value=h.value;h.value=a;sel.value=0;custom();};\n        let initial=100;try{initial=clamp(parseInt(sessionStorage.getItem(KEY_VH),10)||100,10,2000);}catch(e){}range.value=clamp(initial,100,1000);vhInput.value=initial;out.textContent=initial+'vh';\n        function zoom(v){v=clamp(parseInt(v,10)||100,10,2000);range.value=clamp(v,100,1000);vhInput.value=v;out.textContent=v+'vh';applyZoom(v);}\n        function captureElementorDefault(m){const p=preview();if(!p||elementorDefaults[m])return;const r=p.getBoundingClientRect();elementorDefaults[m]={widthStyle:p.style.width||'',maxWidthStyle:p.style.maxWidth||'',heightStyle:p.style.height||'',maxHeightStyle:p.style.maxHeight||'',renderedWidth:Math.round(r.width),renderedHeight:Math.round(r.height)};}\n        function clearViewportOverrides(){\n          const p=preview();if(!p)return;\n          ['width','max-width','min-width','height','max-height','min-height','flex-basis'].forEach(prop=>p.style.removeProperty(prop));\n          p.removeAttribute('data-wsbt-custom-size');\n          p.removeAttribute('data-width');p.removeAttribute('data-height');\n          const frame=iframe();\n          if(frame){['width','max-width','min-width','height','max-height','min-height','flex-basis'].forEach(prop=>frame.style.removeProperty(prop));}\n        }\n        function askElementorForMode(m){try{if(window.elementor&&typeof elementor.changeDeviceMode==='function'){elementor.changeDeviceMode(m);return true;}}catch(_){}try{if(window.elementor&&elementor.channels&&elementor.channels.deviceMode){elementor.channels.deviceMode.trigger('change:deviceMode',m);return true;}}catch(_){}const button=document.querySelector('[data-testid=\"switch-device-to-'+m+'\"]');if(button){button.click();return true;}return false;}\n        function forceBreakpointRebuild(m){\n          const alternate=m==='desktop'?'tablet':'desktop';\n          askElementorForMode(alternate);\n          setTimeout(()=>{clearViewportOverrides();askElementorForMode(m);setTimeout(()=>{clearViewportOverrides();window.dispatchEvent(new Event('resize'));},90);},90);\n        }\n        function resetGridDefaults(){\n          gridSettings={wValue:'10',wUnit:'vw',hValue:'10',hUnit:'vh',colour:DEFAULT_GRID_COLOUR};\n          gridW.value='10VW';gridH.value='10VH';gridColour.value=DEFAULT_GRID_COLOUR;saveGridSettings();applyGrid();\n          gridButton.title=gridOn?'Hide 10VW × 10VH alignment grid':'Show adjustable alignment grid';\n        }\n        function restoreElementorDefault(){\n          stopRuler();stopSpacing();stopTypography();\n          const m=mode();\n          savedSizes[m]=null;elementorDefaults[m]=null;sel.value=0;\n          clearViewportOverrides();resetGridDefaults();zoom(100);forceBreakpointRebuild(m);\n          setTimeout(()=>{clearViewportOverrides();sync(true);},240);\n          setTimeout(()=>{clearViewportOverrides();const p=preview();if(p){const r=p.getBoundingClientRect();w.value=Math.round(r.width)+'px';h.value=Math.round(r.height)+'px';}scheduleClamp(true);},420);\n        }\n        box.querySelector('#wsbt-reset').onclick=restoreElementorDefault;\n        const WSBT_AJAX_URL=<?php echo wp_json_encode( $wsbt_ajax_url ); ?>;\n        const WSBT_NONCE=<?php echo wp_json_encode( $wsbt_nonce ); ?>;\n\n        function quickButtonState(button,label,ok=true){\n          const original=button.dataset.originalLabel||button.textContent;\n          button.dataset.originalLabel=original;\n          button.textContent=label;\n          button.disabled=true;\n          button.style.opacity=ok?'1':'.72';\n          window.setTimeout(()=>{button.textContent=original;button.disabled=false;button.style.opacity='';},1400);\n        }\n\n        async function wsbtAjax(action,postId=0){\n          const body=new URLSearchParams();\n          body.set('action',action);\n          body.set('_ajax_nonce',WSBT_NONCE);\n          if(postId)body.set('post_id',String(postId));\n          const response=await fetch(WSBT_AJAX_URL,{\n            method:'POST',\n            credentials:'same-origin',\n            headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'},\n            body:body.toString()\n          });\n          const data=await response.json();\n          if(!response.ok||!data||data.success!==true){\n            const message=data&&data.data&&data.data.message?data.data.message:'The action could not be completed.';\n            throw new Error(message);\n          }\n          return data.data||{};\n        }\n\n        function visibleActionButton(){\n          const selectors=[\n            '#elementor-panel-saver-button-publish',\n            '#elementor-panel-saver-button-save-options',\n            '[data-testid=\"save-button\"]',\n            '[data-testid=\"publish-button\"]',\n            '[data-testid=\"update-button\"]',\n            'button[aria-label=\"Save\"]',\n            'button[aria-label=\"Publish\"]',\n            'button[aria-label=\"Update\"]',\n            '.editor-header__publish-button button',\n            '.editor-header__save-button button',\n            '.elementor-panel-saver-button'\n          ];\n          for(const selector of selectors){\n            const candidate=document.querySelector(selector);\n            if(candidate&&candidate.offsetParent!==null&&!candidate.disabled)return candidate;\n          }\n          const words=/^(save|publish|update|submit)$/i;\n          return Array.from(document.querySelectorAll('button,[role=\"button\"]')).find(el=>{\n            const label=(el.getAttribute('aria-label')||el.textContent||'').trim();\n            return words.test(label)&&el.offsetParent!==null&&!el.disabled&&!el.closest('#ws-builder-tools');\n          })||null;\n        }\n\n        function currentElementorPostId(){\n          const candidates=[];\n          try{\n            if(window.elementor&&elementor.config){\n              candidates.push(\n                elementor.config.post_id,\n                elementor.config.document&&elementor.config.document.id,\n                elementor.config.initial_document&&elementor.config.initial_document.id\n              );\n            }\n          }catch(_){}\n          try{\n            const params=new URLSearchParams(window.location.search);\n            candidates.push(params.get('post'),params.get('post_id'),params.get('elementor-preview'));\n          }catch(_){}\n          for(const value of candidates){\n            const id=parseInt(value,10);\n            if(Number.isInteger(id)&&id>0)return id;\n          }\n          return 0;\n        }\n\n        async function runElementorSaveCommand(){\n          // Current Elementor command API.\n          try{\n            if(window.$e&&typeof $e.run==='function'){\n              try{\n                const result=$e.run('document/save/publish');\n                if(result&&typeof result.then==='function')await result;\n                return true;\n              }catch(_){}\n              try{\n                const result=$e.run('document/save/update');\n                if(result&&typeof result.then==='function')await result;\n                return true;\n              }catch(_){}\n              try{\n                const result=$e.run('document/save/save',{status:'publish'});\n                if(result&&typeof result.then==='function')await result;\n                return true;\n              }catch(_){}\n            }\n          }catch(_){}\n\n          // Elementor document API.\n          try{\n            const current=window.elementor&&elementor.documents&&typeof elementor.documents.getCurrent==='function'\n              ? elementor.documents.getCurrent()\n              : null;\n            if(current&&typeof current.save==='function'){\n              const result=current.save({status:'publish'});\n              if(result&&typeof result.then==='function')await result;\n              return true;\n            }\n          }catch(_){}\n\n          // Older Elementor saver APIs.\n          try{\n            if(window.elementor&&elementor.saver){\n              if(typeof elementor.saver.publish==='function'){\n                const result=elementor.saver.publish();\n                if(result&&typeof result.then==='function')await result;\n                return true;\n              }\n              if(typeof elementor.saver.update==='function'){\n                const result=elementor.saver.update({status:'publish'});\n                if(result&&typeof result.then==='function')await result;\n                return true;\n              }\n            }\n          }catch(_){}\n\n          const nativeButton=visibleActionButton();\n          if(nativeButton){\n            nativeButton.click();\n            await new Promise(resolve=>window.setTimeout(resolve,900));\n\n            // Newer Elementor interfaces can open a second publish confirmation.\n            const confirmSelectors=[\n              '[data-testid=\"publish-confirm-button\"]',\n              '[data-testid=\"confirm-publish-button\"]',\n              'button[aria-label=\"Publish\"]',\n              '.dialog-widget-content button.elementor-button-success',\n              '.MuiDialog-root button'\n            ];\n            for(const selector of confirmSelectors){\n              const buttons=Array.from(document.querySelectorAll(selector));\n              const confirmation=buttons.find(el=>{\n                const label=(el.getAttribute('aria-label')||el.textContent||'').trim();\n                return /^(publish|confirm|save)$/i.test(label)&&el.offsetParent!==null&&!el.disabled&&!el.closest('#ws-builder-tools');\n              });\n              if(confirmation){\n                confirmation.click();\n                break;\n              }\n            }\n            await new Promise(resolve=>window.setTimeout(resolve,900));\n            return true;\n          }\n\n          return false;\n        }\n\n        async function saveOrPublish(){\n          const button=box.querySelector('#wsbt-save');\n          const original=button.textContent;\n          button.dataset.originalLabel=original;\n          button.textContent='Saving…';\n          button.disabled=true;\n\n          const postId=currentElementorPostId();\n          if(!postId)throw new Error('The current Elementor document ID could not be determined.');\n\n          const attempted=await runElementorSaveCommand();\n          if(!attempted)throw new Error('Elementor did not expose a usable save command.');\n\n          // Give Elementor time to complete its editor-data request before verifying status.\n          await new Promise(resolve=>window.setTimeout(resolve,1200));\n\n          const result=await wsbtAjax('wsbt_verify_publish_document',postId);\n          if(!result||result.status!=='publish'){\n            throw new Error('The page was saved, but WordPress did not confirm it as published.');\n          }\n\n          button.textContent='Published';\n          window.setTimeout(()=>{\n            button.textContent=original;\n            button.disabled=false;\n          },1600);\n        }\n\n        box.querySelector('#wsbt-save').onclick=()=>{\n          saveOrPublish().catch(error=>{\n            const button=box.querySelector('#wsbt-save');\n            button.textContent='Failed';\n            button.disabled=false;\n            window.setTimeout(()=>{button.textContent=button.dataset.originalLabel||'Save';},1600);\n            alert('FlightDeck could not save/publish the page: '+error.message);\n          });\n        };\n\n        box.querySelector('#wsbt-regen').onclick=async()=>{\n          const button=box.querySelector('#wsbt-regen');\n          quickButtonState(button,'Working…');\n          try{\n            await wsbtAjax('wsbt_regenerate_elementor_css');\n            button.textContent='Regenerated';\n            const frame=iframe();\n            try{if(frame&&frame.contentWindow)frame.contentWindow.location.reload();}catch(_){}\n          }catch(error){\n            quickButtonState(button,'Failed',false);\n            alert('CSS regeneration failed: '+error.message);\n          }\n        };\n\n        box.querySelector('#wsbt-purge').onclick=async()=>{\n          const button=box.querySelector('#wsbt-purge');\n          if(!window.confirm('Purge the WordPress object cache now?'))return;\n          quickButtonState(button,'Purging…');\n          try{\n            await wsbtAjax('wsbt_purge_cache');\n            button.textContent='Purged';\n          }catch(error){\n            quickButtonState(button,'Failed',false);\n            alert('Cache purge failed: '+error.message);\n          }\n        };\n\n        box.querySelector('#wsbt-revisions').onclick=async()=>{\n          const button=box.querySelector('#wsbt-revisions');\n          const postId=currentElementorPostId();\n          if(!postId){\n            alert('FlightDeck could not determine the current Elementor document ID.');\n            return;\n          }\n          if(!window.confirm('Delete all saved WordPress revisions for this page only?\\n\\nThis cannot be undone.'))return;\n          quickButtonState(button,'Clearing…');\n          try{\n            const result=await wsbtAjax('wsbt_clear_document_revisions',postId);\n            const count=Number(result.deleted||0);\n            button.textContent=count?count+' Deleted':'None';\n          }catch(error){\n            quickButtonState(button,'Failed',false);\n            alert('Revision cleanup failed: '+error.message);\n          }\n        };\n\n        box.querySelector('#wsbt-reload').onclick=()=>{\n          // A true full editor reload, equivalent to pressing F5.\n          window.location.reload();\n        };\n        range.oninput=()=>zoom(range.value);vhInput.onchange=()=>zoom(vhInput.value);vhInput.onkeydown=e=>{if(e.key==='Enter'){e.preventDefault();zoom(vhInput.value);}};box.querySelectorAll('[data-vh]').forEach(b=>b.onclick=()=>zoom(b.dataset.vh));zoom(initial);\n        let drag=null;head.addEventListener('pointerdown',e=>{if(e.target.closest('button'))return;const r=box.getBoundingClientRect();drag={id:e.pointerId,dx:e.clientX-r.left,dy:e.clientY-r.top};head.setPointerCapture(e.pointerId);});head.addEventListener('pointermove',e=>{if(!drag)return;box.style.left=clamp(e.clientX-drag.dx,4,window.innerWidth-box.offsetWidth-4)+'px';box.style.top=clamp(e.clientY-drag.dy,4,window.innerHeight-box.offsetHeight-4)+'px';box.style.right='auto';box.style.bottom='auto';positionSpacingInfo();});head.addEventListener('pointerup',e=>{if(!drag)return;drag=null;try{localStorage.setItem(KEY_POS,JSON.stringify({left:box.offsetLeft,top:box.offsetTop}));}catch(_){}});\n        function sync(force=false){const p=preview(),m=mode();deviceRow.style.display=m==='mobile'?'flex':'none';if(!p||(!force&&(document.activeElement===w||document.activeElement===h)))return;if(!p.hasAttribute('data-wsbt-custom-size')&&!savedSizes[m])captureElementorDefault(m);if(m!==currentMode){currentMode=m;if(savedSizes[m]){w.value=savedSizes[m].width;h.value=savedSizes[m].height;applySize(savedSizes[m].width,savedSizes[m].height);}else{sel.value=0;clearViewportOverrides();setTimeout(()=>{const fresh=preview();if(!fresh)return;captureElementorDefault(m);const r=fresh.getBoundingClientRect();w.value=Math.round(r.width)+'px';h.value=Math.round(r.height)+'px';},40);}return;}const r=p.getBoundingClientRect();w.value=Math.round(r.width)+'px';h.value=Math.round(r.height)+'px';}\n        setInterval(sync,250);window.addEventListener('resize',()=>{sync();scheduleClamp(true);if(spacingInfoDetached&&spacingInfoBox){const r=spacingInfoBox.getBoundingClientRect(),pos=clampFloating(spacingInfoBox,r.left,r.top);spacingInfoBox.style.left=pos.left+'px';spacingInfoBox.style.top=pos.top+'px';}else positionSpacingInfo();},{passive:true});\n        if(window.visualViewport)window.visualViewport.addEventListener('resize',()=>scheduleClamp(true),{passive:true});\n        if(typeof ResizeObserver!=='undefined'){const paneResizeObserver=new ResizeObserver(()=>scheduleClamp(true));paneResizeObserver.observe(box);}\n        sync();scheduleClamp(false);\n        const f=iframe();if(f)f.addEventListener('load',()=>{zoom(vhInput.value);applyGrid();if(rulerOn){if(rulerCleanup)rulerCleanup();rulerCleanup=installRulerMode();}if(spacingOn){if(spacingCleanup)spacingCleanup();spacingCleanup=installSpacingMode();}if(zOn){if(zCleanup)zCleanup();zCleanup=installZIndexMode();}if(typeMode){if(typeCleanup)typeCleanup();typeCleanup=installTypographyMode(typeMode);}});\n        window.addEventListener('keydown',e=>{if(e.key==='Escape'){stopRuler();stopSpacing();stopZIndex();stopTypography();}});\n      }\n      if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',init);else init();\n    })();\n    </script>\n    <?php\n}\n\nadd_action('elementor/editor/after_enqueue_scripts', 'add_landmark_audit_guide');\n\n\n/**\n * FlightDeck Quick Actions: verify the current document is genuinely published.\n *\n * Elementor saves its editor data first. This endpoint then checks the current\n * WordPress status and promotes a draft/pending/private document to publish\n * when the current user has permission to publish it.\n */\nfunction wsbt_verify_publish_document() {\n    check_ajax_referer( 'wsbt_quick_actions' );\n\n    $post_id = isset( $_POST['post_id'] ) ? absint( $_POST['post_id'] ) : 0;\n    if ( ! $post_id ) {\n        wp_send_json_error( [ 'message' => 'A valid document ID was not supplied.' ], 400 );\n    }\n\n    $post = get_post( $post_id );\n    if ( ! $post ) {\n        wp_send_json_error( [ 'message' => 'The Elementor document could not be found.' ], 404 );\n    }\n\n    if ( ! current_user_can( 'edit_post', $post_id ) ) {\n        wp_send_json_error( [ 'message' => 'You do not have permission to save this document.' ], 403 );\n    }\n\n    if ( 'publish' !== $post->post_status ) {\n        if ( ! current_user_can( 'publish_post', $post_id ) ) {\n            wp_send_json_error( [ 'message' => 'You can save this document, but you do not have permission to publish it.' ], 403 );\n        }\n\n        $updated = wp_update_post(\n            [\n                'ID'          => $post_id,\n                'post_status' => 'publish',\n            ],\n            true\n        );\n\n        if ( is_wp_error( $updated ) ) {\n            wp_send_json_error( [ 'message' => $updated->get_error_message() ], 500 );\n        }\n\n        clean_post_cache( $post_id );\n        $post = get_post( $post_id );\n    }\n\n    wp_send_json_success(\n        [\n            'message' => 'The Elementor document is saved and published.',\n            'post_id' => $post_id,\n            'status'  => $post ? $post->post_status : '',\n        ]\n    );\n}\nadd_action( 'wp_ajax_wsbt_verify_publish_document', 'wsbt_verify_publish_document' );\n\n/**\n * FlightDeck Quick Actions: regenerate Elementor generated CSS and data.\n */\nfunction wsbt_regenerate_elementor_css() {\n    check_ajax_referer( 'wsbt_quick_actions' );\n\n    if ( ! current_user_can( 'edit_posts' ) ) {\n        wp_send_json_error( [ 'message' => 'You do not have permission to regenerate Elementor CSS.' ], 403 );\n    }\n\n    if ( ! did_action( 'elementor/loaded' ) || ! class_exists( '\\Elementor\\Plugin' ) ) {\n        wp_send_json_error( [ 'message' => 'Elementor is not available.' ], 500 );\n    }\n\n    try {\n        $plugin = \\Elementor\\Plugin::$instance;\n\n        if ( isset( $plugin->files_manager ) && is_object( $plugin->files_manager ) && method_exists( $plugin->files_manager, 'clear_cache' ) ) {\n            $plugin->files_manager->clear_cache();\n        } elseif ( class_exists( '\\Elementor\\Core\\Files\\Manager' ) ) {\n            $manager = new \\Elementor\\Core\\Files\\Manager();\n            if ( method_exists( $manager, 'clear_cache' ) ) {\n                $manager->clear_cache();\n            }\n        } else {\n            wp_send_json_error( [ 'message' => 'This Elementor version does not expose its CSS cache manager.' ], 500 );\n        }\n\n        if ( function_exists( 'wp_cache_flush' ) ) {\n            wp_cache_flush();\n        }\n\n        wp_send_json_success( [ 'message' => 'Elementor CSS and generated files were regenerated.' ] );\n    } catch ( \\Throwable $error ) {\n        wp_send_json_error( [ 'message' => $error->getMessage() ], 500 );\n    }\n}\nadd_action( 'wp_ajax_wsbt_regenerate_elementor_css', 'wsbt_regenerate_elementor_css' );\n\n/**\n * FlightDeck Quick Actions: purge the WordPress object cache.\n */\nfunction wsbt_purge_cache() {\n    check_ajax_referer( 'wsbt_quick_actions' );\n\n    if ( ! current_user_can( 'manage_options' ) ) {\n        wp_send_json_error( [ 'message' => 'You do not have permission to purge the cache.' ], 403 );\n    }\n\n    $flushed = function_exists( 'wp_cache_flush' ) ? wp_cache_flush() : false;\n\n    // Some persistent cache implementations return void despite flushing successfully.\n    if ( false === $flushed && ! function_exists( 'wp_cache_flush' ) ) {\n        wp_send_json_error( [ 'message' => 'WordPress cache flushing is unavailable.' ], 500 );\n    }\n\n    wp_send_json_success( [ 'message' => 'WordPress object cache purged successfully.' ] );\n}\nadd_action( 'wp_ajax_wsbt_purge_cache', 'wsbt_purge_cache' );\n\n/**\n * FlightDeck Quick Actions: delete WordPress revisions for the document\n * currently being edited. This deliberately does not sweep revisions\n * belonging to any other page, post or template.\n */\nfunction wsbt_clear_document_revisions() {\n    check_ajax_referer( 'wsbt_quick_actions' );\n\n    $post_id = isset( $_POST['post_id'] ) ? absint( $_POST['post_id'] ) : 0;\n    if ( ! $post_id ) {\n        wp_send_json_error( [ 'message' => 'A valid document ID was not supplied.' ], 400 );\n    }\n\n    $post = get_post( $post_id );\n    if ( ! $post || 'revision' === $post->post_type ) {\n        wp_send_json_error( [ 'message' => 'The Elementor document could not be found.' ], 404 );\n    }\n\n    if ( ! current_user_can( 'edit_post', $post_id ) ) {\n        wp_send_json_error( [ 'message' => 'You do not have permission to clear revisions for this document.' ], 403 );\n    }\n\n    $revisions = wp_get_post_revisions(\n        $post_id,\n        [\n            'order'          => 'ASC',\n            'check_enabled'  => false,\n        ]\n    );\n\n    if ( empty( $revisions ) ) {\n        wp_send_json_success(\n            [\n                'message' => 'This document has no saved revisions to delete.',\n                'post_id' => $post_id,\n                'deleted' => 0,\n            ]\n        );\n    }\n\n    $deleted = 0;\n    $failed  = 0;\n\n    foreach ( $revisions as $revision ) {\n        if ( ! $revision instanceof \\WP_Post ) {\n            continue;\n        }\n\n        $result = wp_delete_post_revision( $revision->ID );\n        if ( $result ) {\n            $deleted++;\n        } else {\n            $failed++;\n        }\n    }\n\n    clean_post_cache( $post_id );\n\n    if ( $failed ) {\n        wp_send_json_error(\n            [\n                'message' => sprintf(\n                    'Deleted %1$d revision(s), but %2$d revision(s) could not be deleted.',\n                    $deleted,\n                    $failed\n                ),\n                'post_id' => $post_id,\n                'deleted' => $deleted,\n                'failed'  => $failed,\n            ],\n            500\n        );\n    }\n\n    wp_send_json_success(\n        [\n            'message' => sprintf( 'Deleted %d revision(s) for this document.', $deleted ),\n            'post_id' => $post_id,\n            'deleted' => $deleted,\n        ]\n    );\n}\nadd_action( 'wp_ajax_wsbt_clear_document_revisions', 'wsbt_clear_document_revisions' );\n\nadd_action('elementor/editor/after_enqueue_scripts', 'ws_elementor_builder_tools_panel', 20);\n",
      "active": true,
      "modified": "2026-08-11 04:46:17",
      "revision": "1"
    }
  ]
}