@wcag-checkr/ci 1.0.0-rc.100

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/README.md +135 -0
  2. package/dist/assets/ErrorBoundary-CBJAOIP1.js +544 -0
  3. package/dist/assets/_commonjsHelpers-Cpj98o6Y.js +1 -0
  4. package/dist/assets/ai-usage-log-Dj9Ub_DT.js +1 -0
  5. package/dist/assets/content-script.ts-loader-BjJqq5Xq.js +13 -0
  6. package/dist/assets/content-script.ts-vGsvD70K.js +181 -0
  7. package/dist/assets/copy-ai-fixer-prompt-CZvVr028.js +19 -0
  8. package/dist/assets/crash-reporter-Dc5lvxvY.js +4 -0
  9. package/dist/assets/custom-rules-CAe0nbES.js +1 -0
  10. package/dist/assets/devtools-panel-KS9iXH8M.js +1 -0
  11. package/dist/assets/devtools.html-DQBohI9U.js +1 -0
  12. package/dist/assets/diff-DIBMr3fQ.js +1 -0
  13. package/dist/assets/dom-criterion-analyzers-C2gMp3xS.js +1 -0
  14. package/dist/assets/forensic-log-z_FcTaq6.js +129 -0
  15. package/dist/assets/modulepreload-polyfill-B5Qt9EMX.js +1 -0
  16. package/dist/assets/options-Byz5A0JD.js +6 -0
  17. package/dist/assets/parallel-tab-flow-CGFwqQRo.js +1 -0
  18. package/dist/assets/preload-helper-D7HrI6pR.js +1 -0
  19. package/dist/assets/reflow-analyzer-DNgBX8N_.js +1 -0
  20. package/dist/assets/service-worker.ts-DnDnrbM5.js +985 -0
  21. package/dist/assets/side-panel-CpytrH5X.js +1 -0
  22. package/dist/assets/site-report-renderer-fqGucmuR.js +147 -0
  23. package/dist/assets/state-BIsozXtc.js +1 -0
  24. package/dist/assets/styles-Bh3rgVbO.css +1 -0
  25. package/dist/assets/styles-DhnON7zr.js +84 -0
  26. package/dist/axe.min.js +12 -0
  27. package/dist/devtools/devtools.html +11 -0
  28. package/dist/devtools/panel.html +20 -0
  29. package/dist/fonts/mona-sans-variable.woff2 +0 -0
  30. package/dist/icons/icon-128.png +0 -0
  31. package/dist/icons/icon-16.png +0 -0
  32. package/dist/icons/icon-32.png +0 -0
  33. package/dist/icons/icon-48.png +0 -0
  34. package/dist/manifest.json +76 -0
  35. package/dist/options/options.html +20 -0
  36. package/dist/service-worker-loader.js +1 -0
  37. package/dist/side-panel/App.tsx +193 -0
  38. package/dist/side-panel/README.md +57 -0
  39. package/dist/side-panel/audit-launcher.test.ts +56 -0
  40. package/dist/side-panel/audit-launcher.ts +65 -0
  41. package/dist/side-panel/format-component-id.test.ts +89 -0
  42. package/dist/side-panel/format-component-id.ts +40 -0
  43. package/dist/side-panel/github-issue.test.ts +102 -0
  44. package/dist/side-panel/github-issue.ts +66 -0
  45. package/dist/side-panel/jira-issue.ts +64 -0
  46. package/dist/side-panel/main.tsx +19 -0
  47. package/dist/side-panel/side-panel.html +21 -0
  48. package/dist/side-panel/store.ts +289 -0
  49. package/dist/side-panel/styles.css +16 -0
  50. package/dist/side-panel/wire-messaging.test.ts +202 -0
  51. package/dist/side-panel/wire-messaging.ts +288 -0
  52. package/package.json +44 -0
  53. package/wcagcheckr-ci.mjs +559 -0
@@ -0,0 +1,985 @@
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/parallel-tab-flow-CGFwqQRo.js","assets/crash-reporter-Dc5lvxvY.js","assets/preload-helper-D7HrI6pR.js","assets/diff-DIBMr3fQ.js","assets/forensic-log-z_FcTaq6.js","assets/state-BIsozXtc.js","assets/ai-usage-log-Dj9Ub_DT.js"])))=>i.map(i=>d[i]);
2
+ var ta=Object.defineProperty;var na=(e,t,n)=>t in e?ta(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var Fe=(e,t,n)=>na(e,typeof t!="symbol"?t+"":t,n);import{_ as dt}from"./preload-helper-D7HrI6pR.js";import{c as D,o as W,e as M,f as aa,g as oa,d as J,b as de,i as ra,h as sa}from"./crash-reporter-Dc5lvxvY.js";import{m as je,d as ia,T as ca,a as Mt,c as vt,i as la}from"./diff-DIBMr3fQ.js";import{F as da,G as ua,H as pa,f as ha,v as Ut,l as Oe,t as fe,A as _e,I as fa,w as sn,O as cn,J as ln,R as ga,K as dn,n as ma,L as ve,B as ya,M as ba,N as Ne,P as De,Q as Je,S as wa,g as va,U as Aa,r as Ae,q as un,a as Xe,V as Sa,W as ka}from"./forensic-log-z_FcTaq6.js";import{s as Ve,T as Nt,b as ut,i as xa}from"./state-BIsozXtc.js";import{m as oe,D as Ea,s as $a,h as Ta,b as Ia,g as Ca,d as Ra,e as Oa}from"./ai-usage-log-Dj9Ub_DT.js";const _a=D("chrome-api"),Ma="1.3";class pn{constructor(t){this.target=t}async attach(){await chrome.debugger.attach(this.target,Ma)}async detach(){try{await chrome.debugger.detach(this.target)}catch(t){_a.debug("detach soft-failure",t)}}async send(t,n){return chrome.debugger.sendCommand(this.target,t,n)}}function Ua(e){const t=e instanceof Error?e.message:String(e);return/Another debugger is already attached|Cannot attach to/i.test(t)}function hn(e){const t=e instanceof Error?e.message:String(e);return Ua(e)?je("DEBUGGER_BUSY","DevTools is open on this tab. Close it to run the audit.",!0,{raw:t}):je("STATE_DRIVE_FAILED",t,!1)}function Na(e){const t=(n,a)=>e(n,a);return chrome.debugger.onDetach.addListener(t),()=>chrome.debugger.onDetach.removeListener(t)}const ee=D("state-driver"),Da=60*1e3,se=new Map,Ke=new Map,xe=new Map,Se=new Map,Me=new Map,Pa={default:[],hover:["hover"],focus:["focus"],"focus-visible":["focus","focus-visible"],active:["active"],disabled:[]};async function fn(e){let t=se.get(e);if(!t){t=new pn({tabId:e});try{await t.attach(),await t.send("Runtime.enable"),await t.send("Page.enable"),await t.send("DOM.enable"),await t.send("DOM.getDocument",{depth:-1,pierce:!0}),await t.send("CSS.enable")}catch(n){try{await t.detach()}catch{}throw n}se.set(e,t),xe.set(e,new Map),La(e),ee.info("attached",{tabId:e})}return Ga(e),t}function La(e){const t=(n,a,r)=>{var i,c;if(n.tabId!==e)return;const s=xe.get(e);if(s)if(a==="Runtime.executionContextCreated"){const l=r,d=l==null?void 0:l.context;d&&((i=d.auxData)!=null&&i.isDefault)&&((c=d.auxData)!=null&&c.frameId)&&s.set(d.auxData.frameId,d.id)}else if(a==="Runtime.executionContextDestroyed"){const l=r,d=l==null?void 0:l.executionContextId;if(d!==void 0)for(const[u,h]of s.entries())h===d&&s.delete(u)}else a==="Runtime.executionContextsCleared"&&s.clear()};chrome.debugger.onEvent.addListener(t)}async function Fa(e){const t=se.get(e);if(se.delete(e),xe.delete(e),Se.delete(e),Me.delete(e),t)try{await t.detach()}catch{}}function Ga(e){const t=Ke.get(e);t&&clearTimeout(t),Ke.set(e,setTimeout(()=>{At(e).catch(n=>ee.warn("idle detach failed",n))},Da))}async function At(e){const t=se.get(e);t&&(await t.detach(),se.delete(e)),xe.delete(e),Se.delete(e),Me.delete(e);const n=Ke.get(e);n&&(clearTimeout(n),Ke.delete(e))}function gn(e){const t=[{id:e.frame.id,url:e.frame.url}];for(const n of e.childFrames??[])t.push(...gn(n));return t}async function Va(e,t,n){var s;if(!n||n===0)return;const a=xe.get(t);if(!a)return;let r;try{r=(s=(await chrome.scripting.executeScript({target:{tabId:t,frameIds:[n]},func:()=>location.href}))[0])==null?void 0:s.result}catch(i){ee.debug("frame URL lookup failed",i)}try{const i=await e.send("Page.getFrameTree"),c=gn(i.frameTree);if(r){const u=c.find(h=>h.url===r);if(u){const h=a.get(u.id);if(h!==void 0)return h}}const l=i.frameTree.frame.id,d=c.find(u=>u.id!==l);return d?a.get(d.id):void 0}catch(i){ee.warn("Page.getFrameTree failed",i);return}}async function Wa(e,t,n){var i;const a={expression:`document.querySelector(${JSON.stringify(t)})`,objectGroup:"wcag-auditor",returnByValue:!1};n!==void 0&&(a.contextId=n);const s=(i=(await e.send("Runtime.evaluate",a)).result)==null?void 0:i.objectId;if(!s)return null;try{return(await e.send("DOM.requestNode",{objectId:s})).nodeId??null}finally{e.send("Runtime.releaseObject",{objectId:s}).catch(()=>{})}}async function mn(e){const t=Se.get(e);if(!t)return;const n=se.get(e);if(n){try{await n.send("CSS.forcePseudoState",{nodeId:t.nodeId,forcedPseudoClasses:[]})}catch(a){ee.debug("forcePseudoState clear failed (node may be gone)",a)}Se.delete(e)}}async function pt(e,t,n,a){await chrome.scripting.executeScript({target:St(e,t),func:(r,s)=>{const i=document.querySelector(r);i&&(s?i.setAttribute("disabled",""):i.removeAttribute("disabled"))},args:[n,a]})}async function yn(e,t){await chrome.scripting.executeScript({target:St(e,t),func:()=>{const n=document.activeElement;n&&typeof n.blur=="function"&&n.blur()}})}async function bn(e,t){await chrome.scripting.executeScript({target:{tabId:e,frameIds:[0]},func:n=>{document.documentElement.setAttribute("dir",n)},args:[t]})}async function ja(e,t,n,a){await chrome.scripting.executeScript({target:St(e,t),func:(r,s)=>{const i=document.querySelector(r);if(i)for(const[c,l]of Object.entries(s))i.setAttribute(c,l)},args:[n,a]})}function St(e,t){return t!==void 0&&t!==0?{tabId:e,frameIds:[t]}:{tabId:e}}async function Ka(e,t,n,a){const r=await fn(e),s=Me.get(e),i=(s==null?void 0:s.scope)===n&&(s==null?void 0:s.frameId)===a,c=t.ariaVariation?JSON.stringify(t.ariaVariation.attributes):"",l=!i||(s==null?void 0:s.pseudoState)!==t.pseudoState,d=!s||s.theme!==t.theme,u=!s||s.direction!==t.direction,h=!s||s.breakpointId!==t.breakpoint.id,p=!i||(s==null?void 0:s.ariaVariationKey)!==c;if(l&&(await mn(e),n)){t.pseudoState==="disabled"?await pt(e,a,n,!0):(await pt(e,a,n,!1),await yn(e,a));const f=Pa[t.pseudoState];if(f.length>0){const g=await Va(r,e,a),o=await Wa(r,n,g);if(o!==null)try{await r.send("CSS.forcePseudoState",{nodeId:o,forcedPseudoClasses:f}),Se.set(e,{sessionTabId:e,nodeId:o})}catch(m){ee.warn(`CSS.forcePseudoState failed for :${t.pseudoState}`,m)}else ee.debug(`could not resolve nodeId for ${n} (frameId=${a})`)}}if(d){const f=[{name:"prefers-reduced-motion",value:"reduce"}];t.theme==="dark"?(f.push({name:"prefers-color-scheme",value:"dark"}),f.push({name:"forced-colors",value:"none"})):t.theme==="light"?(f.push({name:"prefers-color-scheme",value:"light"}),f.push({name:"forced-colors",value:"none"})):t.theme==="forced-colors-active"?f.push({name:"forced-colors",value:"active"}):t.theme==="forced-colors-none"&&f.push({name:"forced-colors",value:"none"}),await r.send("Emulation.setEmulatedMedia",{features:f}),await new Promise(g=>setTimeout(g,50))}u&&await bn(e,t.direction),h&&await r.send("Emulation.setDeviceMetricsOverride",{width:t.breakpoint.width,height:t.breakpoint.height,deviceScaleFactor:t.breakpoint.deviceScaleFactor,mobile:t.breakpoint.mobile}),p&&t.ariaVariation&&n&&await ja(e,a,n,t.ariaVariation.attributes),Me.set(e,{scope:n,frameId:a,pseudoState:t.pseudoState,theme:t.theme,direction:t.direction,breakpointId:t.breakpoint.id,ariaVariationKey:c})}async function za(e,t,n){const a=se.get(e);if(a){if(await mn(e),Me.delete(e),t)try{await pt(e,n,t,!1),await yn(e,n),await bn(e,"ltr")}catch(r){ee.warn("element-level reset partial failure",r)}try{await a.send("Emulation.setEmulatedMedia",{features:[]}),await a.send("Emulation.clearDeviceMetricsOverride")}catch(r){ee.warn("reset partial failure",r)}await At(e)}}async function ht(e,t,n,a){try{return await Ka(e,t,n,a),M({type:"STATE_CHANGED_EVENT",tabId:e,currentState:t}),{type:"STATE_DRIVE_RESPONSE",success:!0,appliedState:t}}catch(r){return ee.error("state drive failed",r),await Fa(e),{type:"STATE_DRIVE_RESPONSE",success:!1,error:hn(r)}}}async function ft(e,t,n){await za(e,t,n)}async function Ba(e,t={}){try{const n=await fn(e),a={format:"jpeg",quality:t.quality??75};t.fullPage&&(a.captureBeyondViewport=!0);const r=await n.send("Page.captureScreenshot",a);return r!=null&&r.data?`data:image/jpeg;base64,${r.data}`:null}catch(n){return ee.debug("Page.captureScreenshot failed",n),null}}function Ha(){var n;const e=[];if(e.push(W("STATE_DRIVE_REQUEST",a=>ht(a.tabId,a.state,a.scope,a.frameId))),e.push(W("STATE_RESET_REQUEST",a=>ft(a.tabId))),(n=chrome.tabs)!=null&&n.onRemoved){const a=r=>{At(r).catch(()=>{})};chrome.tabs.onRemoved.addListener(a),e.push(()=>chrome.tabs.onRemoved.removeListener(a))}const t=Na((a,r)=>{a.tabId!==void 0&&(se.delete(a.tabId),xe.delete(a.tabId),Se.delete(a.tabId),ee.warn("unexpected detach",{tabId:a.tabId,reason:r}))});return e.push(t),ee.info("handlers registered"),()=>e.forEach(a=>a())}const qa="wcag-component-auditor",Ya=1;let at=null;function Qe(){return at||(at=da(qa,Ya,{upgrade(e,t){if(t<1){const n=e.createObjectStore("baselines",{keyPath:"componentId"});n.createIndex("byUrl","snapshotMeta.url"),n.createIndex("byDate","lastUpdated");const a=e.createObjectStore("audit-history",{keyPath:"auditId",autoIncrement:!0});a.createIndex("byComponentId","componentId"),a.createIndex("byDate","runDate")}}})),at}async function kt(e){return(await Qe()).get("baselines",e)}async function wn(e){await(await Qe()).put("baselines",e)}async function Ja(e){await(await Qe()).delete("baselines",e)}async function Xa(){return(await Qe()).getAll("baselines")}const we=D("cloud-sync"),Dt="cloudSyncEnabled",ot="cloudSyncEndpoint",rt="licenseToken",gt="cloudSyncVersionCache";async function xt(){try{const e=await chrome.storage.local.get([Dt,ot,rt]),t=e[Dt]===!0,n=typeof e[ot]=="string"?e[ot]:"",a=typeof e[rt]=="string"?e[rt]:"";return!t||!n||!a?null:{endpoint:n.replace(/\/$/,""),token:a,enabled:t}}catch{return null}}async function vn(){try{const t=(await chrome.storage.local.get(gt))[gt];return t&&typeof t=="object"?t:{}}catch{return{}}}async function ze(e){try{await chrome.storage.local.set({[gt]:e})}catch{}}async function Qa(e,t,n){const a=await xt();if(!a)return!1;const r=await vn(),s=r[e];try{const i=`${a.endpoint}/v1/products/wcagcheckr/baselines/${encodeURIComponent(e)}`,c=await fetch(i,{method:"PUT",headers:{"Content-Type":"application/json",Authorization:`Bearer ${a.token}`},body:JSON.stringify({snapshot:t,axeVersion:n,expectedVersion:s})});if(c.status===409)return we.warn(`baseline ${e} version conflict on push`,{expectedVersion:s}),delete r[e],await ze(r),!1;if(!c.ok)return we.warn("baseline push failed",{status:c.status}),!1;const l=await c.json();return typeof l.version=="number"&&(r[e]=l.version,await ze(r)),!0}catch(i){return we.debug("push network failure (offline?)",i),!1}}async function Za(e){const t=await xt();if(!t)return!1;try{const n=`${t.endpoint}/v1/products/wcagcheckr/baselines/${encodeURIComponent(e)}`,a=await fetch(n,{method:"DELETE",headers:{Authorization:`Bearer ${t.token}`}});if(a.ok){const r=await vn();delete r[e],await ze(r)}return a.ok}catch(n){return we.debug("delete network failure",n),!1}}async function eo(){const e=await xt();if(!e)return[];try{const t=await fetch(`${e.endpoint}/v1/products/wcagcheckr/baselines`,{headers:{Authorization:`Bearer ${e.token}`}});if(!t.ok)return we.warn("pull failed",{status:t.status}),[];const a=(await t.json()).items??[],r={};for(const s of a)r[s.componentId]=s.version;return await ze(r),a}catch(t){return we.debug("pull network failure",t),[]}}const An=D("baseline-store"),Pt="componentIdAliases";async function ke(e,t){try{return await t()}catch(n){const a=n instanceof Error?n.message:String(n);throw An.error(`baseline IDB ${e} failed: ${a}`),new ca({code:"BASELINE_DB_ERROR",message:`Baseline storage is unavailable (${e}: ${a}).`,recoverable:!1,details:a})}}async function Ze(e){return((await chrome.storage.local.get(Pt))[Pt]??{})[e]??e}async function to(e){const t=await Ze(e),n=await ke("get",()=>kt(t));return n?{violations:n.violations,snapshotMeta:n.snapshotMeta}:null}async function no(e,t,n,a){const r=await Ze(e),s=await ke("get-for-set",()=>kt(r)),i=(s==null?void 0:s.seenOnUrls)??[],c=i.includes(n.url)?i:[...i,n.url];await ke("set",()=>wn({componentId:r,violations:t,snapshotMeta:n,lastUpdated:new Date().toISOString(),axeVersion:n.axeVersion,announcements:a==null?void 0:a.announcements,focusEvents:a==null?void 0:a.focusEvents,seenOnUrls:c})),Qa(r,{violations:t,snapshotMeta:n},n.axeVersion).catch(()=>{})}function ao(e,t){if(!e||!t||Mt(e)===Mt(t))return;const n=s=>{var i,c,l,d,u;return(((i=s.pseudoStates)==null?void 0:i.length)??0)*Math.max(1,((c=s.ariaVariations)==null?void 0:c.length)??0)*(((l=s.themes)==null?void 0:l.length)??0)*(((d=s.directions)==null?void 0:d.length)??0)*(((u=s.breakpoints)==null?void 0:u.length)??0)},a=n(e),r=n(t);return`Baseline was captured under a different state-matrix configuration (${a} states) than this run (${r} states). Per-state-instance deltas inflate when the matrix changes — the unique-violation deltas above are still reliable.`}async function Et(e,t,n,a){var d;const r=await Ze(e),s=await ua(r),i=await ke("compare",()=>kt(r));if(!i){const u=t.filter(p=>s.has(p.matchKey)),h=t.filter(p=>!s.has(p.matchKey));return{new:h,persistent:[],fixed:[],newCount:h.length,persistentCount:0,fixedCount:0,baselineSnapshotMeta:null,comparedAt:new Date().toISOString(),newAnnouncements:n==null?void 0:n.announcements,newFocusEvents:n==null?void 0:n.focusEvents,acknowledged:u,acknowledgedCount:u.length}}const c=ia(i.violations,t,i.snapshotMeta,{baselineAnnouncements:i.announcements,currentAnnouncements:n==null?void 0:n.announcements,baselineFocusEvents:i.focusEvents,currentFocusEvents:n==null?void 0:n.focusEvents},s),l=ao((d=i.snapshotMeta)==null?void 0:d.matrixConfig,a);return l&&(c.matrixMismatchWarning=l),c}async function oo(e){const t=await Ze(e);await ke("delete",()=>Ja(t)),Za(t).catch(()=>{})}async function ro(){const e=await eo();for(const t of e)await wn({componentId:t.componentId,violations:t.snapshot.violations,snapshotMeta:t.snapshot.snapshotMeta,lastUpdated:t.updatedAt,axeVersion:t.axeVersion});return e.length>0&&M({type:"SCORECARD_UPDATED_EVENT"}),e.length}async function so(e){let t=await ke("list",()=>Xa());return e!=null&&e.url&&(t=t.filter(n=>n.snapshotMeta.url===e.url)),t.map(n=>{const a=(n.focusEvents??[]).filter(d=>d.isFocusReset).length,r=(n.announcements??[]).length,s=n.violations.filter(d=>d.ruleId==="target-size").length,i=n.violations.some(d=>d.ruleId==="color-contrast"&&d.currentState.pseudoState==="hover"),c=n.violations.filter(d=>d.impact==="critical").length,l=n.violations.filter(d=>d.impact==="serious").length;return{componentId:n.componentId,violationCount:n.violations.length,lastUpdated:n.lastUpdated,seenOnUrlsCount:(n.seenOnUrls??[]).length,metrics:{criticalCount:c,seriousCount:l,focusResetCount:a,announcementCount:r,targetSizeFailCount:s,hoverContrastFail:i}}})}function io(){const e=[];return e.push(W("BASELINE_GET",async t=>({type:"BASELINE_GET_RESPONSE",baseline:await to(t.componentId)}))),e.push(W("BASELINE_SET",async t=>{await no(t.componentId,t.violations,t.snapshotMeta,{announcements:t.announcements,focusEvents:t.focusEvents}),M({type:"SCORECARD_UPDATED_EVENT"})})),e.push(W("BASELINE_COMPARE",async t=>({type:"BASELINE_COMPARE_RESPONSE",delta:await Et(t.componentId,t.currentViolations,{announcements:t.announcements,focusEvents:t.focusEvents},t.currentMatrix)}))),e.push(W("BASELINE_DELETE",async t=>{await oo(t.componentId),M({type:"SCORECARD_UPDATED_EVENT"})})),e.push(W("BASELINE_LIST",async t=>({type:"BASELINE_LIST_RESPONSE",items:await so(t.filter)}))),An.info("handlers registered"),()=>e.forEach(t=>t())}const mt="tierConfig:cache",co=60*60*1e3,lo=24*60*60*1e3,uo=7*24*60*60*1e3;async function po(){return(await chrome.storage.local.get(mt))[mt]??null}async function ho(e){await chrome.storage.local.set({[mt]:e})}function fo(e,t=Date.now()){const n=t-e.fetchedAt;return n<co?"fresh":n<lo?"stale":n<uo?"grace":"expired"}function go(e,t){const n={...e},a=o=>{if(typeof o=="number")return o===-1?1/0:o},r=o=>typeof o=="boolean"?o:void 0,s=o=>{if(o==="all"||o==="configurable"||o==="default-only")return o},i=a(t.maxComponents);i!==void 0&&(n.maxComponents=i);const c=a(t.maxBaselines);c!==void 0&&(n.maxBaselines=c);const l=s(t.stateMatrix);l!==void 0&&(n.stateMatrix=l);const d=r(t.storybookAutoIterate);d!==void 0&&(n.storybookAutoIterate=d);const u=r(t.exportJson);u!==void 0&&(n.exportJson=u);const h=r(t.exportSarif);h!==void 0&&(n.exportSarif=h);const p=r(t.exportJunit);p!==void 0&&(n.exportJunit=p);const f=r(t.cloudSync);f!==void 0&&(n.cloudSync=f);const g=r(t.forensicAnchoring);return g!==void 0&&(n.forensicAnchoring=g),n}function mo(e,t){var a;if(e==="trial")return((a=t.trial)==null?void 0:a.features)??null;const n=t.plans.find(r=>r.code===e||r.code.startsWith(e+"-"));return(n==null?void 0:n.features)??null}const yt=D("tier-config-client"),yo="wcagcheckr",bo=`https://api.wcagcheckr.com/v1/products/${yo}/tier-config`,wo=1e4,Lt="tier-config-refresh",vo=60,Ao=["trial","free","solo","team"];function Sn(e){const t={};for(const n of Ao){const a=mo(n,e);t[n]=a?go(Nt[n],a):{...Nt[n]}}return t}async function So(){const e=await po();if(!e){Ve(null);return}if(fo(e)==="expired"){yt.warn("cached tier-config is expired (>7d); using hardcoded defaults"),Ve(null);return}Ve(Sn(e.config))}async function kn(){try{const e=await fetch(bo,{signal:AbortSignal.timeout(wo),headers:{accept:"application/json"}});if(!e.ok)throw new Error(`http ${e.status}`);const t=await e.json();await ho({fetchedAt:Date.now(),config:t}),Ve(Sn(t)),yt.info("tier-config refreshed from server")}catch(e){yt.warn("tier-config refresh failed; keeping prior cache",e)}}function ko(){chrome.alarms.create(Lt,{periodInMinutes:vo}),chrome.alarms.onAlarm.addListener(e=>{e.name===Lt&&kn()})}const xo=D("settings-store"),Ft=1,Gt="__schemaVersion",bt={stateMatrix:ut,componentIdAliases:{},ignorePatterns:[]};async function ue(e,t){return(await chrome.storage.local.get(e))[e]??t}async function Eo(){const e=await chrome.storage.local.get();if(e[Gt]===Ft)return;const t={[Gt]:Ft};for(const[n,a]of Object.entries(bt))n in e||(t[n]=a);await chrome.storage.local.set(t),xo.info("defaults ensured")}const $o=["__","inflight:","license:","support:"];function To(e){return!$o.some(t=>e.startsWith(t))}function Io(){const e=[];return e.push(W("SETTINGS_GET",async t=>({type:"SETTINGS_RESPONSE",data:(await chrome.storage.local.get(t.key))[t.key]??bt[t.key]}))),e.push(W("SETTINGS_SET",async t=>{await chrome.storage.local.set({[t.key]:t.value})})),e.push(W("SETTINGS_GET_ALL",async()=>{const t=await chrome.storage.local.get(),n=Object.fromEntries(Object.entries(t).filter(([a])=>To(a)));return{type:"SETTINGS_RESPONSE",data:{...bt,...n}}})),()=>e.forEach(t=>t())}const Co={input:3/1e6,output:15/1e6},Ro={input:1/1e6,output:5/1e6},Oo="claude-sonnet-4-6",_o="https://api.anthropic.com/v1/messages",Mo="2023-06-01";function Uo(e){const t=e.model||Oo,n=t.includes("haiku")?Ro:Co;return{providerId:"anthropic",async judgeAltText({imageUrl:a,alt:r,surroundingContext:s,signal:i}){const c=await Wt(a,i);if(!c)return jt("Could not fetch image to verify alt text",0,t);const l=Bo(r,s),d=await H({apiKey:e.apiKey,model:t,maxTokens:300,signal:i,messages:[{role:"user",content:[{type:"image",source:{type:"base64",media_type:c.mediaType,data:c.data}},{type:"text",text:l}]}]});return Q(d,n)},async judgeHeading({headingText:a,sectionContent:r,signal:s}){const i=await H({apiKey:e.apiKey,model:t,maxTokens:250,signal:s,messages:[{role:"user",content:[{type:"text",text:Ho(a,r)}]}]});return Q(i,n)},async judgeSensoryLanguage({instructionText:a,signal:r}){const s=await H({apiKey:e.apiKey,model:t,maxTokens:250,signal:r,messages:[{role:"user",content:[{type:"text",text:qo(a)}]}]});return Q(s,n)},async judgeAriaSemantics({elementHtml:a,computedRole:r,surroundingHtml:s,signal:i}){const c=await H({apiKey:e.apiKey,model:t,maxTokens:350,signal:i,messages:[{role:"user",content:[{type:"text",text:Yo(a,r,s)}]}]});return Q(c,n)},async judgeImageOfText({imageUrl:a,accessibleName:r,signal:s}){const i=await Wt(a,s);if(!i)return jt("Could not fetch image to inspect for embedded text",0,t);const c=await H({apiKey:e.apiKey,model:t,maxTokens:300,signal:s,messages:[{role:"user",content:[{type:"image",source:{type:"base64",media_type:i.mediaType,data:i.data}},{type:"text",text:Jo(r)}]}]});return Q(c,n)},async judgeColorOnlyMeaning({elementHtml:a,contextDescription:r,signal:s}){const i=await H({apiKey:e.apiKey,model:t,maxTokens:250,signal:s,messages:[{role:"user",content:[{type:"text",text:Xo(a,r)}]}]});return Q(i,n)},async judgeGenericLinkText({linkText:a,surroundingText:r,signal:s}){const i=await H({apiKey:e.apiKey,model:t,maxTokens:250,signal:s,messages:[{role:"user",content:[{type:"text",text:Qo(a,r)}]}]});return Q(i,n)},async judgeWallOfText({blockText:a,structuralChildrenCount:r,signal:s}){const i=await H({apiKey:e.apiKey,model:t,maxTokens:250,signal:s,messages:[{role:"user",content:[{type:"text",text:Zo(a,r)}]}]});return Q(i,n)},async judgeLanguageMismatch({declaredLang:a,bodyTextSample:r,signal:s}){const i=await H({apiKey:e.apiKey,model:t,maxTokens:250,signal:s,messages:[{role:"user",content:[{type:"text",text:er(a,r)}]}]});return Q(i,n)},async suggestColorFix(a){const r=await H({apiKey:e.apiKey,model:t,maxTokens:600,messages:[{role:"user",content:[{type:"text",text:tr(a)}]}]});return ar(r,n)},async generateExecutiveSummary(a){const r=await H({apiKey:e.apiKey,model:t,maxTokens:800,messages:[{role:"user",content:[{type:"text",text:nr(a)}]}]});return sr(r,n)},async resolveAxeIncomplete(a){const r=await H({apiKey:e.apiKey,model:t,maxTokens:400,signal:a.signal,messages:[{role:"user",content:[{type:"text",text:or(a)}]}]});return rr(r,n,t)},async judgeFocusVisible({pageUrl:a,samples:r,signal:s}){const i=[];for(const l of r){const d=/^data:(image\/[a-z]+);base64,(.+)$/i.exec(l.screenshot);d&&(i.push({type:"image",source:{type:"base64",media_type:d[1],data:d[2]}}),i.push({type:"text",text:`Image #${l.ordinal} — selector: ${l.selector} — role: ${l.role||"(none)"} — name: "${l.name||"(no name)"}" — rect: x=${Math.round(l.rect.x)} y=${Math.round(l.rect.y)} w=${Math.round(l.rect.width)} h=${Math.round(l.rect.height)} — outline: ${l.focusStyle.outlineStyle} ${l.focusStyle.outlineWidth} ${l.focusStyle.outlineColor} (offset ${l.focusStyle.outlineOffset}) — box-shadow: ${l.focusStyle.boxShadow||"none"}`}))}i.push({type:"text",text:Wo(a,r.length)});const c=await H({apiKey:e.apiKey,model:t,maxTokens:600,signal:s,messages:[{role:"user",content:i}]});return Q(c,n)},async judgeKeyboardTrap({pageUrl:a,forwardSequence:r,reverseSequence:s,stuckRuns:i,reverseMismatches:c,signal:l}){const d=jo(a,r,s,i,c),u=await H({apiKey:e.apiKey,model:t,maxTokens:400,signal:l,messages:[{role:"user",content:[{type:"text",text:d}]}]});return Q(u,n)},async judgeReadingOrder({pageUrl:a,pageScreenshot:r,flaggedIssues:s,signal:i}){const c=Ko(a,s),l=[];if(r){const u=/^data:(image\/[a-z]+);base64,(.+)$/i.exec(r);u&&l.push({type:"image",source:{type:"base64",media_type:u[1],data:u[2]}})}l.push({type:"text",text:c});const d=await H({apiKey:e.apiKey,model:t,maxTokens:1500,signal:i,messages:[{role:"user",content:l}]});return Q(d,n)},async judgeFocusOrder({pageUrl:a,pageScreenshot:r,tabSequence:s,signal:i}){const c=zo(a,s),l=[];if(r){const u=/^data:(image\/[a-z]+);base64,(.+)$/i.exec(r);u&&l.push({type:"image",source:{type:"base64",media_type:u[1],data:u[2]}})}l.push({type:"text",text:c});const d=await H({apiKey:e.apiKey,model:t,maxTokens:1500,signal:i,messages:[{role:"user",content:l}]});return Q(d,n)}}}class Ue extends Error{constructor(){super("Anthropic API: canceled by caller"),this.name="ExternalAbortError"}}const xn=3e4,Vt=3,No=1e4;function Do(e){return e===429||e>=500}function Po(e){return e instanceof Error?e.name==="TimeoutError"||e.name==="AbortError"||e instanceof TypeError:!1}function Lo(e,t){if(!t)return e;if(typeof AbortSignal.any=="function")return AbortSignal.any([e,t]);const n=new AbortController,a=()=>n.abort();return e.aborted?n.abort():e.addEventListener("abort",a,{once:!0}),t.aborted?n.abort():t.addEventListener("abort",a,{once:!0}),n.signal}async function Fo(e){var r,s;const t=AbortSignal.timeout(xn),n=Lo(t,e.signal);let a;try{a=await fetch(_o,{method:"POST",headers:{"x-api-key":e.apiKey,"anthropic-version":Mo,"content-type":"application/json","anthropic-dangerous-direct-browser-access":"true"},body:JSON.stringify({model:e.model,max_tokens:e.maxTokens,messages:e.messages}),signal:n})}catch(i){throw(r=e.signal)!=null&&r.aborted?new Ue:i}if(!a.ok){const c=((s=(await a.json().catch(()=>({}))).error)==null?void 0:s.message)??`${a.status} ${a.statusText}`,l=new Error(`Anthropic API: ${c}`);throw l.status=a.status,l}return await a.json()}async function H(e){var n;let t=null;for(let a=1;a<=Vt;a++){if((n=e.signal)!=null&&n.aborted)throw new Ue;try{return await Fo(e)}catch(r){if(r instanceof Ue)throw r;const s=r.status;if(!(Po(r)||typeof s=="number"&&Do(s))||a===Vt)throw r instanceof Error&&(r.name==="TimeoutError"||r.name==="AbortError")?new Error(`Anthropic API: timed out after ${xn/1e3}s on each of ${a} attempt${a===1?"":"s"}`):r;t=r instanceof Error?r:new Error(String(r)),await Go(a*1e3,e.signal)}}throw t??new Error("Anthropic API: all retries failed")}function Go(e,t){return new Promise((n,a)=>{if(t!=null&&t.aborted){a(new Ue);return}const r=setTimeout(()=>{t==null||t.removeEventListener("abort",s),n()},e),s=()=>{clearTimeout(r),a(new Ue)};t==null||t.addEventListener("abort",s,{once:!0})})}async function Wt(e,t){var s,i;const n=new AbortController,a=setTimeout(()=>n.abort(),No),r=()=>n.abort();t&&(t.aborted?n.abort():t.addEventListener("abort",r,{once:!0}));try{const c=await fetch(e,{signal:n.signal});if(!c.ok)return null;const l=((i=(s=c.headers.get("content-type"))==null?void 0:s.split(";")[0])==null?void 0:i.trim())??"image/png";if(!l.startsWith("image/"))return null;const d=await c.arrayBuffer();let u="";const h=new Uint8Array(d),p=32768;for(let f=0;f<h.length;f+=p)u+=String.fromCharCode(...h.slice(f,f+p));return{data:btoa(u),mediaType:l}}catch{return null}finally{clearTimeout(a),t&&t.removeEventListener("abort",r)}}function Q(e,t){var s;const n=((s=e.content.find(i=>i.type==="text"))==null?void 0:s.text)??"",a=Vo(n),r=e.usage.input_tokens*t.input+e.usage.output_tokens*t.output;return{verdict:a.verdict,reasoning:a.reasoning,confidence:a.confidence,costUsd:r,model:e.model}}function Vo(e){const n=e.replace(/```(?:json)?/g,"").trim().match(/\{[\s\S]*\}/);if(!n)return{verdict:"uncertain",reasoning:"Model returned no parseable JSON",confidence:0};try{const a=JSON.parse(n[0]),r=a.verdict==="pass"||a.verdict==="fail"?a.verdict:"uncertain",s=(a.reasoning??"").trim().slice(0,600),i=typeof a.confidence=="number"?a.confidence:.5,c=Math.max(0,Math.min(1,i));return{verdict:r,reasoning:s,confidence:c}}catch{return{verdict:"uncertain",reasoning:"Model returned invalid JSON",confidence:0}}}function jt(e,t,n){return{verdict:"uncertain",reasoning:e,confidence:0,costUsd:t,model:n}}const te=`
3
+ Respond ONLY with a single JSON object — no preamble, no markdown:
4
+ { "verdict": "pass" | "fail" | "uncertain", "reasoning": "1-3 sentences", "confidence": 0.0-1.0 }
5
+ `.trim();function Wo(e,t){return`You are an accessibility auditor evaluating focus-visibility (WCAG 2.4.7 — Focus Visible).
6
+
7
+ Page: ${e}
8
+ ${t} screenshot(s) above; each shows a different focused element. Each image is paired with a metadata line giving the element's selector, role, accessible name, rect (viewport-relative), and computed outline + box-shadow styles.
9
+
10
+ For each image, look at the element identified by the rect and decide whether it shows a visible focus indicator. A visible indicator can be:
11
+ - A CSS outline (any color contrasting with the background).
12
+ - A box-shadow used as a focus ring.
13
+ - A background-color or border change relative to the unfocused baseline.
14
+ - Any other visual treatment that distinguishes the focused element from its unfocused peers.
15
+
16
+ A "fail" element has NO visible focus indicator — typically \`outline: none\` with no compensating box-shadow / border / background treatment, OR an outline whose color matches the background so it's effectively invisible.
17
+
18
+ Aggregate verdict:
19
+ - "pass" = every sampled element shows a clearly visible focus indicator.
20
+ - "fail" = one or more sampled elements have no visible focus indicator. The reasoning should name the failing selector(s) and why.
21
+ - "uncertain" = images are unreadable, the rects don't match visible elements, or compensating treatments are ambiguous.
22
+
23
+ ${te}`}function jo(e,t,n,a,r){const s=t.map((d,u)=>` ${u+1}. ${d}`).join(`
24
+ `)||" (no tabbable elements found)",i=n.map((d,u)=>` ${u+1}. ${d}`).join(`
25
+ `)||" (Shift-Tab moved to body / no reverse path)",c=a.length===0?" (none detected)":a.map(d=>` - ${d}`).join(`
26
+ `),l=r.length===0?" (none — Shift-Tab cleanly reversed the forward order)":r.map(d=>` - position ${d.index}: expected ${d.expected}, got ${d.actual}`).join(`
27
+ `);return`You are an accessibility auditor evaluating keyboard-trap risk (WCAG 2.1.2 — No Keyboard Trap).
28
+
29
+ Page: ${e}
30
+
31
+ A robot walked the page with Tab, then Shift-Tab. Findings below.
32
+
33
+ Forward walk (Tab key, in order):
34
+ ${s}
35
+
36
+ Reverse walk (Shift+Tab, in order — should walk back through the forward sequence):
37
+ ${i}
38
+
39
+ Stuck-run detection (selector appearing 3+ times in a row in the forward walk — indicates focus may not be progressing past that element):
40
+ ${c}
41
+
42
+ Reverse mismatches (expected vs. actual reverse-walk positions — indicates Shift-Tab didn't cleanly walk backward):
43
+ ${l}
44
+
45
+ Evaluate keyboard-trap risk per WCAG 2.1.2:
46
+ - "pass" = focus moves freely forward and backward. No stuck runs. Reverse walk mirrors forward order (allowing minor variation from focus-management widgets).
47
+ - "fail" = clear evidence of a trap. Stuck runs at the same element OR Shift-Tab can't escape a region OR reverse walk diverges significantly from forward order.
48
+ - "uncertain" = too few tabbable elements (<3) to judge meaningfully OR the data is inconsistent for unclear reasons.
49
+
50
+ ${te}`}function Ko(e,t){const n=t.map(a=>` - DOM #${a.domIndex} → Visual #${a.visualIndex} | selector ${a.selector} | at (x=${Math.round(a.rect.x)}, y=${Math.round(a.rect.y)}, w=${Math.round(a.rect.w)}, h=${Math.round(a.rect.h)}) | text: "${a.textSnippet.slice(0,80).replace(/"/g,"'")}"`).join(`
51
+ `);return`You are an accessibility auditor evaluating reading-order quality (WCAG 1.3.2 — Meaningful Sequence).
52
+
53
+ A screenshot of the page (${e}) is attached. A heuristic compared DOM source order to visual position (top-to-bottom row bins, then left-to-right within a row) and flagged ${t.length} element(s) whose DOM index diverges notably from visual index. Screen readers read the DOM in source order, so divergence CAN indicate a real problem — but multi-column / banked / grid layouts trigger the heuristic by design and are legitimate.
54
+
55
+ Flagged elements:
56
+ ${n}
57
+
58
+ Evaluate whether the flagged divergences represent REAL reading-order breaks that would confuse a screen-reader user, or normal multi-column / banked layout patterns. Per WCAG 1.3.2: the order must preserve the MEANING of the content — not strict top-to-bottom-left-to-right.
59
+
60
+ Legitimate divergence patterns (these are PASS):
61
+ - Multi-column / newspaper layouts: DOM reads column 1 entirely, then column 2 — visual scan groups by row.
62
+ - Skip-links ("Skip to main content"): visually positioned near focus styling, DOM-early on purpose.
63
+ - Caption-before-image: caption placed before its image in DOM so SR users get context before alt-text.
64
+ - Modal / popover triggers near the document-end portal: the trigger is visually inline with content but DOM-near where the portal mounts.
65
+ - Grouped card layouts: each card's contents are DOM-contiguous, but cards visually wrap into rows.
66
+ - Header/nav blocks vs. main content: nav DOM-early is correct even when visually rendered alongside or below the hero.
67
+
68
+ Real reading-order breaks (these are FAIL):
69
+ - Out-of-order paragraphs within a single text block (paragraph 3 of an article appearing in DOM after paragraph 5).
70
+ - Captions, footnotes, or annotations DOM-far from the content they reference, where they're visually adjacent.
71
+ - Form labels DOM-after their inputs in a way that breaks SR association.
72
+ - Headings that DOM-jump regions (h2 of section B inside section A).
73
+
74
+ Verdict:
75
+ - "pass" = every flagged divergence fits one of the legitimate patterns above. Multi-column / banked layouts default to PASS unless internal scrambling is obvious in the screenshot.
76
+ - "fail" = ≥1 flagged divergence is a real reading-order break. Cite the specific selector(s) and why.
77
+ - "uncertain" = the screenshot is missing OR the layout is too ambiguous to judge confidently.
78
+
79
+ When in doubt, prefer "uncertain" over "fail". Multi-column false positives are the most common trap on modern web pages.
80
+
81
+ ${te}`}function zo(e,t){const n=t.map(a=>` ${a.ordinal}. [${a.role||a.selector.split(" ").pop()||"?"}] "${a.name||"(no name)"}" — selector ${a.selector} — at (x=${Math.round(a.rect.x)}, y=${Math.round(a.rect.y)}, w=${Math.round(a.rect.width)}, h=${Math.round(a.rect.height)})`).join(`
82
+ `);return`You are an accessibility auditor evaluating tab-order quality (WCAG 2.4.3 — Focus Order).
83
+
84
+ A screenshot of the page (${e}) is attached. The page's keyboard-tabbable elements were walked in order; the sequence the user actually encountered is below. Each item lists position (viewport-relative x/y, width/height in CSS pixels).
85
+
86
+ Tab sequence (in order):
87
+ ${n}
88
+
89
+ Evaluate whether the focus order preserves the meaning + operability of the page for a keyboard or screen-reader user, per WCAG 2.4.3. A "logical" order:
90
+ - Generally follows reading order WITHIN A REGION. Tab moves through a region's items in reading order, then advances to the next region.
91
+ - Multi-column, multi-section, and grouped-card layouts are normal and expected. Focus jumping between groups (e.g., header nav → main-content cards → sidebar → footer) does NOT count as broken when each region is internally coherent.
92
+ - Y-coordinate reversals are EXPECTED when regions are physically nested or stacked unusually. Example: an SPA with sticky-position headers, banked phase-cards, or footer links rendered after a deep scroll area — the y-coordinate sequence can legitimately zigzag while the tab order is correct.
93
+ - The relevant question is NOT "does y always increase?" but "would a screen-reader user understand WHY each region is encountered in the order it is?"
94
+
95
+ Strict-fail signals (require multiple to call fail):
96
+ - A region's INTERNAL items are scrambled (e.g., card title last, action button first).
97
+ - A SINGLE element is skipped entirely (interactive but never receives focus).
98
+ - Focus jumps INTO a region, then BACK OUT, then BACK IN — the same region visited twice non-contiguously.
99
+ - Positive tabindex values are observable in the recorded order (e.g., a deeply-nested element appears first).
100
+
101
+ Verdict:
102
+ - "pass" = order is logical within regions, even if regions are visited non-strictly-top-to-bottom. Multi-column / multi-region pages PASS by default unless internal scrambling is obvious.
103
+ - "fail" = ≥2 strict-fail signals above. Cite the specific items by ordinal.
104
+ - "uncertain" = the screenshot is missing, the sequence is too short to judge (<5 items), OR you see y-zigzag patterns but the screenshot suggests a multi-region layout you can't fully resolve.
105
+
106
+ When in doubt, prefer "uncertain" over "fail". Multi-column / banked layouts are the most common false-fail trap.
107
+
108
+ ${te}`}function Bo(e,t){return`You are an accessibility auditor verifying alt text correctness for screen-reader users (WCAG 1.1.1).
109
+
110
+ Image is attached. Current alt text: ${e?`"${e}"`:"(empty / missing)"}
111
+ ${t?`
112
+ Nearby page context: ${t.slice(0,500)}`:""}
113
+
114
+ Judge whether the alt text accurately represents the image's content or function:
115
+ - "pass" = alt is meaningful and accurately describes what's in the image OR alt="" is correct because the image is decorative/redundant
116
+ - "fail" = alt is wrong, misleading, generic ("image", "photo"), filename-style ("img-1234.jpg"), or omitted on a meaningful image
117
+ - "uncertain" = you can't tell from the image alone whether the alt is appropriate
118
+
119
+ ${te}`}function Ho(e,t){return`You are an accessibility auditor verifying that headings describe their sections (WCAG 2.4.6).
120
+
121
+ Heading text: "${e}"
122
+ Section content beneath it (first 800 chars): "${t.slice(0,800)}"
123
+
124
+ Judge whether the heading actually describes what the section contains:
125
+ - "pass" = heading meaningfully describes the section's topic
126
+ - "fail" = heading is non-descriptive ("Section 1", "Welcome", "More info"), unrelated to the content, or generic placeholder
127
+ - "uncertain" = section content is too short or ambiguous to judge
128
+
129
+ ${te}`}function qo(e){return`You are an accessibility auditor checking instructions for sensory-characteristic dependencies (WCAG 1.3.3).
130
+
131
+ Instruction text: "${e.slice(0,800)}"
132
+
133
+ WCAG 1.3.3 says instructions must NOT rely solely on shape, size, color, visual location, sound, or orientation. Judge:
134
+ - "fail" = instruction relies on a sensory characteristic without a programmatic identifier (e.g., "click the round button", "the green save icon", "the link on the right")
135
+ - "pass" = instruction uses programmatic identifiers (button label, link text) — sensory cues are fine when accompanied by a name
136
+ - "uncertain" = ambiguous whether a sensory descriptor is the only cue
137
+
138
+ ${te}`}function Yo(e,t,n){return`You are an accessibility auditor verifying ARIA role appropriateness (WCAG 4.1.2).
139
+
140
+ Element (truncated): ${e.slice(0,600)}
141
+ Computed role: ${t}
142
+ Surrounding HTML context: ${n.slice(0,500)}
143
+
144
+ Judge whether the role is semantically appropriate for this element's apparent purpose:
145
+ - "pass" = role matches the element's actual behavior + content + interactivity
146
+ - "fail" = role is misused — e.g., role="button" on a div with no event handlers, role="dialog" on a non-modal overlay, role="navigation" on a non-nav block
147
+ - "uncertain" = can't tell the element's true behavior from this static snapshot
148
+
149
+ ${te}`}function Jo(e){return`You are an accessibility auditor checking for WCAG 1.4.5 (Images of Text) compliance.
150
+
151
+ WCAG 1.4.5 says: if text can be presented using actual text instead of a baked-in image, it should be. Exceptions: logos, decorative use, or text that's essential to a specific visual presentation (e.g., a screenshot of code).
152
+
153
+ Image is attached.${e?`
154
+ Accessible name (alt / aria-label) of this image: "${e.slice(0,200)}"`:""}
155
+
156
+ Judge whether the image bakes substantial text into the raster:
157
+ - "fail" = the image contains body text, headings, button labels, product names, headlines, slogans, or paragraphs that could/should be HTML text. Decorative typography (e.g., a logo) does NOT count as fail unless it's clearly extended text.
158
+ - "pass" = the image is purely visual (photo, illustration, icon) with no substantial baked-in text, OR the baked-in text is a logo / brand mark / unavoidable visual artifact (code screenshot, equation, etc.).
159
+ - "uncertain" = the image is too small / low-resolution to tell, or the text is partial/ambiguous.
160
+
161
+ ${te}`}function Xo(e,t){return`You are an accessibility auditor checking for WCAG 1.4.1 (Use of Color) compliance.
162
+
163
+ WCAG 1.4.1 says: color must not be the only visual means of conveying information, indicating an action, prompting a response, or distinguishing a visual element. There must always be a parallel non-color signal — a text label, an icon shape, a position cue, etc.
164
+
165
+ Element markup (truncated):
166
+ ${e.slice(0,800)}
167
+
168
+ Context: ${t.slice(0,400)}
169
+
170
+ Judge whether the element conveys meaning ONLY through color:
171
+ - "fail" = a status / state / action / category is signaled ONLY by color with no text label, no shape difference, no icon glyph, and no aria-label. Common examples: a colored dot or pill conveying status with empty text content + no aria-label; a validation border (aria-invalid="true") with no accompanying icon or text message; "click the red button" instructions in CONTENT.
172
+ - "pass" = the element has a visible text label, an icon child, an aria-label, OR the color is decorative — any non-color signal that a color-blind / grayscale user could detect.
173
+ - "uncertain" = markup is ambiguous (e.g. the contextDescription says "has icon child" but the icon may itself be color-only).
174
+
175
+ ${te}`}function Qo(e,t){return`You are an accessibility auditor checking for WCAG 2.4.4 (Link Purpose in Context).
176
+
177
+ WCAG 2.4.4 says: the purpose of each link must be determinable from the link text alone, OR from the link text together with its programmatically-determined context (the surrounding sentence / paragraph / list item).
178
+
179
+ Link visible text: "${e.slice(0,200)}"
180
+ Surrounding paragraph / list item / sentence (programmatically associated): "${t.slice(0,800)}"
181
+
182
+ Judge whether a screen-reader user activating "links list" navigation could understand the link's destination from either the text alone or the link+context:
183
+ - "fail" = generic link text ("click here", "learn more", "read more", "this", "here", "more") where surrounding context ALSO does not clarify the destination.
184
+ - "pass" = link text alone is descriptive, OR the surrounding context unambiguously clarifies the destination (e.g., "Pricing details are available — learn more →" makes "learn more" passable).
185
+ - "uncertain" = link text is borderline-generic and context is ambiguous.
186
+
187
+ ${te}`}function Zo(e,t){return`You are an accessibility auditor checking for WCAG 3.1.5 (Reading Level) and broader cognitive-accessibility issues.
188
+
189
+ A "wall of text" is body content that exceeds reasonable read-without-fatigue size with no structural breaks (subheadings, lists, paragraphs, blockquotes). Cognitively-disabled users + speed-skim readers can't scan it.
190
+
191
+ Block text (first 1200 chars): "${e.slice(0,1200)}"
192
+ Structural children inside the block (paragraphs / lists / sub-headings): ${t}
193
+
194
+ Judge whether this block presents a wall-of-text problem:
195
+ - "fail" = block is long-form prose (≥ 300 words) with effectively NO structural breaks (no sub-headings inside it, fewer than ~3 paragraphs, no lists), making it hard to scan.
196
+ - "pass" = block is short enough OR has adequate structural breaks (paragraphs, sub-headings, lists) for scannable reading.
197
+ - "uncertain" = block is borderline (~200-300 words with minimal structure).
198
+
199
+ ${te}`}function er(e,t){return`You are an accessibility auditor checking for WCAG 3.1.1 (Language of Page) and 3.1.2 (Language of Parts).
200
+
201
+ The page declares its default human language via the HTML \`lang\` attribute. Screen readers use that to pick the correct pronunciation engine. If the declared language doesn't match the actual content, screen-reader output is unintelligible.
202
+
203
+ Declared language (from <html lang="…">): "${e||"(none / empty)"}"
204
+ Body text sample (first 1200 chars): "${t.slice(0,1200)}"
205
+
206
+ Judge whether the declared language matches the content:
207
+ - "fail" = content is clearly in a DIFFERENT language than declared (e.g., lang="en" but text is Spanish), OR no lang was declared and content is non-trivial.
208
+ - "pass" = declared language matches the content's primary language.
209
+ - "uncertain" = content is too short, too multilingual, or in a language closely related to the declared one.
210
+
211
+ ${te}`}function tr(e){const t=e.targetLevel==="AAA"?e.fontSize>=18.66||e.fontSize>=14&&e.fontWeight>=700?"4.5":"7.0":e.fontSize>=18.66||e.fontSize>=14&&e.fontWeight>=700?"3.0":"4.5";return`You are a color accessibility expert. Suggest replacement colors that fix a contrast failure while preserving design intent.
212
+
213
+ Current pair (FAILING):
214
+ - Foreground: ${e.foreground}
215
+ - Background: ${e.background}
216
+ - Font size: ${e.fontSize}px, weight: ${e.fontWeight}
217
+ - Required contrast for WCAG ${e.targetLevel}: ${t}:1
218
+
219
+ ${e.paletteHints&&e.paletteHints.length>0?`Available palette tokens (prefer these to keep brand cohesion):
220
+ ${e.paletteHints.slice(0,30).map(n=>` - ${n}`).join(`
221
+ `)}`:"No palette tokens discovered. Stay close in hue + saturation to the original colors."}
222
+
223
+ ${e.brandIntent?`Brand intent: ${e.brandIntent}`:""}
224
+
225
+ Suggest up to 3 candidate replacement pairs. Each candidate should:
226
+ 1. Pass the required ${t}:1 contrast (verify your math).
227
+ 2. Stay close in hue to the original (don't swap a warm palette for a cool one).
228
+ 3. Prefer modifying ONE color (foreground OR background) over both, unless the original is severely off.
229
+ 4. If palette tokens are available, prefer in-palette colors.
230
+
231
+ Respond ONLY with a JSON object — no preamble, no markdown:
232
+ {
233
+ "verdict": "suggested" | "no-suggestion",
234
+ "candidates": [
235
+ { "foreground": "#hexHEX", "background": "#hexHEX", "contrast": 4.6, "rationale": "1 sentence" }
236
+ ],
237
+ "reasoning": "1-3 sentences explaining your overall approach or why no suggestions are possible"
238
+ }`}function nr(e){return`You are an accessibility auditor writing the executive summary for an automated audit report.
239
+
240
+ Audience: ${e.framing==="defense"?"an internal stakeholder team (engineering + legal + risk-management)":e.framing==="evidence"?"an attorney or accessibility advocate considering a notice-and-cure step":"a non-technical site owner who needs to understand their accessibility status"}.
241
+ Framing: ${e.framing} report.
242
+
243
+ Audit data:
244
+ - Page audited: ${e.pageUrl}
245
+ - Audit date: ${e.auditDate}
246
+ - Compliance grade: ${e.grade}
247
+ - Risk tier: ${e.riskTier}
248
+ - Severity totals: ${e.totals.critical} critical, ${e.totals.serious} serious, ${e.totals.moderate} moderate, ${e.totals.minor} minor (unique: ${e.totals.unique})
249
+ - Top categories driving risk: ${e.riskDrivers.length>0?e.riskDrivers.join(", "):"none flagged"}
250
+ ${e.priorAuditCount!==void 0&&e.priorAuditCount>0?`- Prior audit count in record: ${e.priorAuditCount} (ongoing-diligence context)`:""}
251
+
252
+ Tone:
253
+ - Plain-spoken. No legalese, no marketing hype.
254
+ - Direct about risk without alarmism.
255
+ - "Defense" framing: emphasize good-faith remediation effort + that automated audits cover ~30-50% of WCAG; manual checks fill the rest.
256
+ - "Evidence" framing: present findings objectively + suggest notice-and-cure procedures over immediate litigation.
257
+ - "Owner-report" framing: explain what the findings mean for the site owner in everyday terms, no code talk.
258
+
259
+ Constraints:
260
+ - Do NOT suggest specific dollar amounts, settlement figures, or legal predictions.
261
+ - Do NOT claim the audit proves compliance OR non-compliance — it documents detected violations.
262
+ - Do NOT use "ADA-compliant" or "WCAG-compliant" as adjectives — say "documented violations of [specific WCAG SCs]" instead.
263
+
264
+ Respond ONLY with a JSON object — no preamble, no markdown:
265
+ {
266
+ "lead": "2-3 sentence headline framing",
267
+ "body": "2-3 paragraphs of risk explanation + audit-date framing + ongoing-diligence context if applicable. ~150-300 words total.",
268
+ "closer": "1-2 sentence closing line"
269
+ }`}function ar(e,t){var i;const r=(((i=e.content.find(c=>c.type==="text"))==null?void 0:i.text)??"").replace(/```(?:json)?/g,"").trim().match(/\{[\s\S]*\}/),s=e.usage.input_tokens*t.input+e.usage.output_tokens*t.output;if(!r)return{verdict:"error",candidates:[],reasoning:"Model returned no parseable JSON",costUsd:s,model:e.model};try{const c=JSON.parse(r[0]),l=(c.candidates??[]).slice(0,3).map(d=>({foreground:typeof d.foreground=="string"?d.foreground:"",background:typeof d.background=="string"?d.background:"",contrast:typeof d.contrast=="number"?d.contrast:0,rationale:typeof d.rationale=="string"?d.rationale:""})).filter(d=>d.foreground&&d.background);return{verdict:c.verdict==="suggested"&&l.length>0?"suggested":"no-suggestion",candidates:l,reasoning:typeof c.reasoning=="string"?c.reasoning:"",costUsd:s,model:e.model}}catch{return{verdict:"error",candidates:[],reasoning:"Could not parse color suggestions",costUsd:s,model:e.model}}}function or(e){var a,r,s,i,c,l;const t=(a=e.element.styles)==null?void 0:a.effectiveBackground,n=e.ruleId==="color-contrast"||e.ruleId==="color-contrast-enhanced"?`
270
+ This is a COLOR-CONTRAST evaluation. axe-core couldn't determine the
271
+ contrast ratio (commonly because the element's own background-color
272
+ is transparent and axe didn't walk the DOM cascade to find the
273
+ effective rendered backdrop). The audit pipeline has already done
274
+ that walk for you — use the effectiveBackground below, NOT the
275
+ element's own background-color, when computing contrast.
276
+
277
+ Computed styles axe captured at audit time:
278
+ - Foreground (color): ${((r=e.element.styles)==null?void 0:r.foreground)??"unknown"}
279
+ - Element's OWN background-color: ${((s=e.element.styles)==null?void 0:s.background)??"unknown"} (often transparent; ignore unless solid)
280
+ - Font size: ${((i=e.element.styles)==null?void 0:i.fontSize)??"unknown"} px
281
+ - Font weight: ${((c=e.element.styles)==null?void 0:c.fontWeight)??"unknown"}
282
+ - Text sample: ${JSON.stringify(((l=e.element.styles)==null?void 0:l.textSample)??"")}
283
+
284
+ EFFECTIVE BACKGROUND (the actual color rendered beneath this text,
285
+ captured by walking up the DOM cascade until we hit an opaque
286
+ ancestor background-color):
287
+ - Effective background color: ${(t==null?void 0:t.color)??"unknown"}
288
+ - Came from ancestor: ${(t==null?void 0:t.fromAncestor)??"(the element itself)"}
289
+ - Background-image present in cascade: ${t!=null&&t.hasBackgroundImageInChain?`YES (on ${t.imageAncestor??"an ancestor"})`:"no"}
290
+
291
+ WCAG ${e.targetLevel} contrast thresholds:
292
+ - Normal text (< 18pt, or < 14pt and not bold): 4.5:1 for AA, 7:1 for AAA
293
+ - Large text (≥ 18pt OR ≥ 14pt bold): 3:1 for AA, 4.5:1 for AAA
294
+
295
+ Reasoning approach:
296
+ 1. Bold = font-weight >= 700. Large text threshold uses size + weight.
297
+ 2. If 'Background-image present in cascade' is **no** → compute the
298
+ contrast ratio between foreground and effectiveBackground.color.
299
+ This is what the browser is actually rendering. Return 'pass' or
300
+ 'fail' against the appropriate threshold. Cite the computed ratio.
301
+ 3. If 'Background-image present in cascade' is **YES** → the
302
+ effectiveBackground.color is what shows through the image's
303
+ transparent regions, but the image itself may dominate visually.
304
+ Reason about it:
305
+ - If the image is likely dark and text is light (or vice versa),
306
+ contrast is typically OK — lean 'pass' if foreground +
307
+ effectiveBackground also pass, and call out the assumption.
308
+ - If you genuinely can't tell whether the image dominates with a
309
+ color that breaks contrast (text-over-photo scenarios), return
310
+ 'uncertain' and explain that vision capability is needed.
311
+ 4. NEVER return 'uncertain' just because the element's own background
312
+ is transparent — that's expected and the effectiveBackground
313
+ handles it.
314
+
315
+ Return one of:
316
+ - 'pass' if contrast comfortably meets the threshold (give the computed ratio).
317
+ - 'fail' if contrast is below the threshold.
318
+ - 'uncertain' ONLY when a background-image likely dominates visually
319
+ and you cannot reasonably infer its tone.
320
+ `:`
321
+ This is a general WCAG conformance evaluation for rule '${e.ruleId}'.
322
+ axe-core ran but couldn't conclude pass/fail for this element.
323
+ Use the failureSummary + outerHTML to determine if the rule is
324
+ satisfied. If the evidence is insufficient, return 'uncertain'.
325
+ `;return`You are a WCAG accessibility auditor resolving an axe-core "incomplete"
326
+ result. axe ran the rule successfully but couldn't fully determine
327
+ the outcome on this specific element.
328
+
329
+ Rule: ${e.ruleId}
330
+ Page: ${e.pageUrl}
331
+ Selector: ${e.element.selector}
332
+ axe-core diagnostic: ${e.element.failureSummary??"(no detail provided)"}
333
+
334
+ outerHTML snippet:
335
+ ${e.element.outerHTMLSnippet??"(not captured)"}
336
+ ${n}
337
+
338
+ Respond with strict JSON (no markdown fences):
339
+ {
340
+ "verdict": "pass" | "fail" | "uncertain",
341
+ "reasoning": "one or two sentences explaining the determination, citing the contrast ratio for color-contrast"
342
+ }`}function rr(e,t,n){var c;const a=((c=e.content.find(l=>l.type==="text"))==null?void 0:c.text)??"",s=a.replace(/```(?:json)?/g,"").trim().match(/\{[\s\S]*\}/),i=e.usage.input_tokens*t.input+e.usage.output_tokens*t.output;if(!s)return{verdict:"uncertain",reasoning:a||"AI returned no parseable response.",costUsd:i,model:e.model||n};try{const l=JSON.parse(s[0]);return{verdict:l.verdict==="pass"||l.verdict==="fail"?l.verdict:"uncertain",reasoning:typeof l.reasoning=="string"&&l.reasoning?l.reasoning:"AI returned no rationale.",costUsd:i,model:e.model||n}}catch{return{verdict:"uncertain",reasoning:a,costUsd:i,model:e.model||n}}}function sr(e,t){var i;const n=((i=e.content.find(c=>c.type==="text"))==null?void 0:i.text)??"",r=n.replace(/```(?:json)?/g,"").trim().match(/\{[\s\S]*\}/),s=e.usage.input_tokens*t.input+e.usage.output_tokens*t.output;if(!r)return{lead:"",body:n||"Summary generation failed.",closer:"",costUsd:s,model:e.model};try{const c=JSON.parse(r[0]);return{lead:typeof c.lead=="string"?c.lead:"",body:typeof c.body=="string"?c.body:"",closer:typeof c.closer=="string"?c.closer:"",costUsd:s,model:e.model}}catch{return{lead:"",body:n,closer:"",costUsd:s,model:e.model}}}function q(e){if(!e.enabled)return{ok:!1,reason:"AI augmentation is disabled in settings"};if(!e.apiKey)return{ok:!1,reason:"No API key configured"};switch(e.provider){case"anthropic":return{ok:!0,client:Uo({apiKey:e.apiKey,model:e.model})};case"openai":return{ok:!1,reason:"OpenAI provider not yet implemented"};case"gemini":return{ok:!1,reason:"Gemini provider not yet implemented"};default:return{ok:!1,reason:`Unknown provider: ${e.provider}`}}}function ne(e){const t={capUsd:Math.max(0,e),spentUsd:0,exceeded:!1};return{state:t,canCharge(){return!t.exceeded&&t.spentUsd<t.capUsd},recordCharge(n){t.spentUsd+=Math.max(0,n),t.spentUsd>=t.capUsd&&(t.exceeded=!0)}}}const st=D("ai-compliance-summarizer");async function En(e,t){if(!t.enabled)return null;const n=q(t);if(!n.ok)return st.debug(`AI client unavailable: ${n.reason}`),null;const a=ne(t.costCapUsd);if(!a.canCharge())return null;try{const r=await n.client.generateExecutiveSummary(e);return a.recordCharge(r.costUsd),!r.lead&&!r.body?(st.debug("AI summary returned empty"),null):r}catch(r){return st.warn("compliance-summary generation failed",r),null}}async function ir(){const e=await chrome.storage.local.get("aiConfig");return oe(e.aiConfig)}function cr(){return W("AI_COMPLIANCE_SUMMARY_REQUEST",async e=>{const t=await ir();if(!t.enabled)return{type:"AI_COMPLIANCE_SUMMARY_RESPONSE",summary:null,unavailableReason:"AI augmentation is disabled in Settings → AI augmentation."};if(!t.apiKey)return{type:"AI_COMPLIANCE_SUMMARY_RESPONSE",summary:null,unavailableReason:"No AI API key configured."};const n=await En(e.input,t);return{type:"AI_COMPLIANCE_SUMMARY_RESPONSE",summary:n,unavailableReason:n?void 0:"AI summary generation returned no result."}})}const ae="wcag-component-auditor";function he(){try{return chrome.runtime.getManifest().version}catch{return"0.0.0"}}function ie(e){const t=e[0];return t?t.pageUrl?t.pageUrl:t.scope&&/^https?:\/\//.test(t.scope)?t.scope:t.scope?`(selector: ${t.scope})`:"unknown":"unknown"}function lr(e,t){return{schemaVersion:1,generatedAt:new Date().toISOString(),tool:{name:ae,version:he()},results:e,delta:t}}function Kt(e){return e==="critical"||e==="serious"?"error":e==="moderate"?"warning":"note"}function dr(e,t){const n=(t?t.new:e.flatMap(c=>c.violations)).filter(c=>!c.needsReview),a=new Set,r=[];for(const c of n)a.has(c.ruleId)||(a.add(c.ruleId),r.push({id:c.ruleId,shortDescription:{text:c.description||c.ruleId},helpUri:c.helpUrl||void 0,defaultConfiguration:{level:Kt(c.impact)},properties:{tags:[c.wcagCriterion,`wcag-${c.wcagLevel}`]}}));const s=ie(e),i=n.map(c=>({ruleId:c.ruleId,level:Kt(c.impact),message:{text:`${c.description}`+(c.target.failureSummary?`
343
+
344
+ ${c.target.failureSummary}`:"")},locations:[{physicalLocation:{artifactLocation:{uri:s&&/^https?:\/\//.test(s)?`${s}#${c.target.selector}`:c.target.selector?`selector://${c.target.selector}`:"unknown"},region:{snippet:{text:c.target.outerHTML}}},logicalLocations:[{name:c.componentId,kind:"module"}]}],properties:{componentId:c.componentId,wcagCriterion:c.wcagCriterion,wcagLevel:c.wcagLevel,pseudoState:c.currentState.pseudoState,theme:c.currentState.theme,direction:c.currentState.direction,breakpoint:c.currentState.breakpoint.id,detectedAt:c.detectedAt}}));return{$schema:"https://json.schemastore.org/sarif-2.1.0.json",version:"2.1.0",runs:[{tool:{driver:{name:ae,version:he(),informationUri:"https://wcagcheckr.com",rules:r}},results:i}]}}function $n(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;")}function le(e){return $n(e).replace(/\n/g,"&#10;").replace(/\r/g,"&#13;")}function ur(e,t){const n=!!t,a=d=>(n?d.violations.filter(u=>t.new.some(h=>h.matchKey===u.matchKey)):d.violations).filter(u=>!u.needsReview),r=e.reduce((d,u)=>d+Math.max(1,u.violations.length),0),s=e.reduce((d,u)=>d+a(u).length,0),i=e.reduce((d,u)=>d+u.durationMs,0)/1e3,c=[];c.push('<?xml version="1.0" encoding="UTF-8"?>');const l=`${ae} — ${ie(e)}`;c.push(`<testsuites name="${le(l)}" tests="${r}" failures="${s}" time="${i.toFixed(3)}">`);for(const d of e){const u=`${d.state.pseudoState}_${d.state.theme}_${d.state.direction}_${d.state.breakpoint.id}`,h=`${d.componentId}::${u}`,p=a(d),f=Math.max(1,d.violations.length);if(c.push(` <testsuite name="${le(h)}" tests="${f}" failures="${p.length}" time="${(d.durationMs/1e3).toFixed(3)}">`),d.violations.length===0)c.push(` <testcase classname="${le(h)}" name="no-violations" time="${(d.durationMs/1e3).toFixed(3)}" />`);else for(const g of d.violations){const o=p.some(I=>I.matchKey===g.matchKey),m=`classname="${le(h)}" name="${le(g.ruleId)}" time="0"`;if(!o){c.push(` <testcase ${m} />`);continue}const k=`${g.impact} - ${g.description}`,v=`Selector: ${g.target.selector}
345
+ WCAG: ${g.wcagCriterion} (${g.wcagLevel})
346
+ State: ${u}
347
+ `+(g.helpUrl?`Help: ${g.helpUrl}
348
+ `:"")+(g.target.failureSummary?`
349
+ ${g.target.failureSummary}`:"");c.push(` <testcase ${m}>`),c.push(` <failure message="${le(k)}" type="${le(g.ruleId)}">${$n(v)}</failure>`),c.push(" </testcase>")}c.push(" </testsuite>")}return c.push("</testsuites>"),c.join(`
350
+ `)}function w(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}function pr(e,t){var k,v,I,$,N;const n=((k=e[0])==null?void 0:k.componentId)??"unknown",a=ie(e),r=e.reduce((x,_)=>x+_.durationMs,0),s=e.flatMap(x=>x.violations),i=new Map;for(const x of s){const _=`${x.ruleId}::${x.target.selector}`,j=`:${x.currentState.pseudoState} · ${x.currentState.theme} · ${x.currentState.direction}`,b=i.get(_);if(b){b._states.includes(j)||b._states.push(j);continue}i.set(_,{...x,_states:[j]})}const c=Array.from(i.values()),l={critical:[],serious:[],moderate:[],minor:[]};for(const x of c)(l[x.impact]??l.moderate).push(x);const d=new Set(((v=t==null?void 0:t.new)==null?void 0:v.map(x=>`${x.ruleId}::${x.target.selector}`))??[]),u=new Set(((I=t==null?void 0:t.persistent)==null?void 0:I.map(x=>`${x.ruleId}::${x.target.selector}`))??[]),h=new Set((($=t==null?void 0:t.fixed)==null?void 0:$.map(x=>`${x.ruleId}::${x.target.selector}`))??[]);let p=0,f=0;for(const x of c){const _=`${x.ruleId}::${x.target.selector}`;u.has(_)?f++:d.has(_)&&p++}let g=0;for(const x of h)!u.has(x)&&!d.has(x)&&g++;const o=x=>{const _=d.has(`${x.ruleId}::${x.target.selector}`);return`
351
+ <article class="violation impact-${w(x.impact)}${_?" is-new":""}">
352
+ <header>
353
+ <span class="rule">${w(x.ruleId)}</span>
354
+ <span class="impact">${w(x.impact)}</span>
355
+ <span class="wcag">${w(x.wcagCriterion)} ${w(x.wcagLevel)}</span>
356
+ ${_?'<span class="new-badge">NEW vs baseline</span>':""}
357
+ </header>
358
+ <p class="desc">${w(x.description)}</p>
359
+ <p class="meta"><strong>Selector:</strong> <code>${w(x.target.selector)}</code></p>
360
+ <p class="meta"><strong>Found in state(s):</strong> ${w(x._states.join(", "))}</p>
361
+ <pre class="snippet"><code>${w(x.target.outerHTML)}</code></pre>
362
+ ${x.helpUrl?`<p class="help"><a href="${w(x.helpUrl)}">Reference: ${w(x.helpUrl)}</a></p>`:""}
363
+ </article>`},m=(x,_)=>_.length===0?"":`
364
+ <section class="impact-section impact-${w(x)}">
365
+ <h2>${w(x[0].toUpperCase()+x.slice(1))} (${_.length})</h2>
366
+ ${_.map(o).join("")}
367
+ </section>`;return`<!doctype html>
368
+ <html lang="en">
369
+ <head>
370
+ <meta charset="utf-8" />
371
+ <title>Accessibility audit — ${w(n)}</title>
372
+ <style>
373
+ *, *::before, *::after { box-sizing: border-box; }
374
+ body {
375
+ font: 12pt/1.5 -apple-system, system-ui, "Segoe UI", sans-serif;
376
+ color: #0f172a;
377
+ max-width: 7.5in;
378
+ margin: 0.5in auto;
379
+ padding: 0 0.25in;
380
+ }
381
+ h1 { font-size: 22pt; margin: 0 0 6pt; }
382
+ h2 { font-size: 14pt; margin: 18pt 0 8pt; padding-bottom: 4pt; border-bottom: 1px solid #cbd5e1; }
383
+ header.report-header { margin-bottom: 18pt; }
384
+ .summary { font-size: 11pt; color: #475569; margin-bottom: 12pt; }
385
+ .summary span { margin-right: 12pt; }
386
+ .violation {
387
+ border-left: 4px solid #cbd5e1;
388
+ background: #f8fafc;
389
+ padding: 10pt 12pt;
390
+ margin: 8pt 0;
391
+ page-break-inside: avoid;
392
+ }
393
+ .violation.impact-critical { border-left-color: #dc2626; }
394
+ .violation.impact-serious { border-left-color: #ea580c; }
395
+ .violation.impact-moderate { border-left-color: #ca8a04; }
396
+ .violation.impact-minor { border-left-color: #2563eb; }
397
+ .violation.is-new { background: #fef2f2; }
398
+ .violation .new-badge { font-size: 9pt; padding: 1pt 6pt; border-radius: 3pt; background: #dc2626; color: white; text-transform: uppercase; letter-spacing: 0.04em; }
399
+ .violation header { display: flex; gap: 8pt; align-items: baseline; margin-bottom: 4pt; }
400
+ .violation .rule { font-family: "SF Mono", Menlo, monospace; font-weight: 600; font-size: 11pt; }
401
+ .violation .impact { font-size: 9pt; padding: 1pt 6pt; border-radius: 3pt; background: #e2e8f0; text-transform: uppercase; letter-spacing: 0.04em; }
402
+ .violation .wcag { font-size: 10pt; color: #64748b; }
403
+ .violation .desc { margin: 0 0 4pt; }
404
+ .violation .meta { font-size: 10pt; color: #475569; margin: 2pt 0; }
405
+ .violation code { font-family: "SF Mono", Menlo, monospace; font-size: 10pt; }
406
+ .violation .snippet {
407
+ background: #fff; border: 1px solid #cbd5e1; border-radius: 3pt;
408
+ padding: 6pt 8pt; overflow-x: auto;
409
+ font-family: "SF Mono", Menlo, monospace; font-size: 10pt; white-space: pre-wrap;
410
+ }
411
+ .violation .help { font-size: 10pt; }
412
+ footer { margin-top: 24pt; padding-top: 8pt; border-top: 1px solid #cbd5e1; font-size: 9pt; color: #64748b; }
413
+ @media print {
414
+ body { margin: 0; max-width: none; }
415
+ h2 { page-break-after: avoid; }
416
+ .violation { break-inside: avoid; }
417
+ }
418
+ </style>
419
+ </head>
420
+ <body>
421
+ <header class="report-header">
422
+ <h1>Accessibility audit report</h1>
423
+ <p class="summary" style="font-size: 11pt; word-break: break-all;">
424
+ <strong>URL audited:</strong> <code>${w(a)}</code>
425
+ </p>
426
+ <p class="summary">
427
+ <span><strong>Component:</strong> <code>${w(n)}</code></span>
428
+ <span><strong>Date:</strong> ${w(new Date().toLocaleString())}</span>
429
+ <span><strong>axe-core:</strong> ${w(((N=e[0])==null?void 0:N.axeVersion)??"n/a")}</span>
430
+ </p>
431
+ <p class="summary">
432
+ <span><strong>States audited:</strong> ${e.length}</span>
433
+ <span><strong>Unique violations:</strong> ${c.length}</span>
434
+ <span><strong>Total violation instances:</strong> ${s.length}</span>
435
+ <span><strong>Audit duration:</strong> ${(r/1e3).toFixed(1)}s</span>
436
+ </p>
437
+ ${t!=null&&t.matrixMismatchWarning?`
438
+ <p class="summary" style="background: #fef3c7; border: 1px solid #f59e0b; padding: 8pt 10pt; border-radius: 3pt;">
439
+ ⚠ ${w(t.matrixMismatchWarning)}
440
+ </p>
441
+ `:""}
442
+ ${t&&t.baselineSnapshotMeta?`
443
+ <p class="summary"><strong>Unique-violation deltas (rule + selector pairs):</strong></p>
444
+ <p class="summary">
445
+ <span style="color: #dc2626;"><strong>NEW:</strong> ${p}</span>
446
+ <span><strong>Persistent:</strong> ${f}</span>
447
+ <span style="color: #15803d;"><strong>Fixed since baseline:</strong> ${g}</span>
448
+ </p>
449
+ <p class="summary" style="font-size: 9pt; color: #64748b;">
450
+ Per-state-instance deltas (each state×rule×selector combination): ${t.newCount} new instances · ${t.persistentCount} persistent · ${t.fixedCount} fixed. The unique-violation counts above are usually what you want to track; instance counts inflate when the matrix size changes.
451
+ </p>
452
+ `:t?'<p class="summary"><span style="color: #64748b;">No baseline accepted yet — every violation is unrated.</span></p>':""}
453
+ <p style="font-size: 10pt; color: #64748b;">
454
+ Tip: use your browser's print dialog (Ctrl+P / Cmd+P) → "Save as PDF" to produce a PDF file.
455
+ </p>
456
+ </header>
457
+ ${m("critical",l.critical)}
458
+ ${m("serious",l.serious)}
459
+ ${m("moderate",l.moderate)}
460
+ ${m("minor",l.minor)}
461
+ <footer>
462
+ Generated by ${ae} v${he()}. Full audit — all detected violations included, deduped across states. Items flagged "NEW vs baseline" were not present in the most-recent accepted baseline.
463
+ </footer>
464
+ </body>
465
+ </html>`}const Tn=[{ref:"1.1.1",title:"Non-text Content",level:"A",rules:["image-alt","input-image-alt","area-alt","object-alt","svg-img-alt"]},{ref:"1.2.1",title:"Audio-only and Video-only (Prerecorded)",level:"A",rules:[]},{ref:"1.2.2",title:"Captions (Prerecorded)",level:"A",rules:["video-caption"]},{ref:"1.3.1",title:"Info and Relationships",level:"A",rules:["heading-order","list","listitem","definition-list","dlitem","th-has-data-cells","td-headers-attr","aria-required-children","aria-required-parent","label"]},{ref:"1.3.2",title:"Meaningful Sequence",level:"A",rules:[]},{ref:"1.3.3",title:"Sensory Characteristics",level:"A",rules:[]},{ref:"1.3.4",title:"Orientation",level:"AA",rules:["css-orientation-lock"]},{ref:"1.3.5",title:"Identify Input Purpose",level:"AA",rules:["autocomplete-valid"]},{ref:"1.4.1",title:"Use of Color",level:"A",rules:["link-in-text-block"]},{ref:"1.4.2",title:"Audio Control",level:"A",rules:["no-autoplay-audio"]},{ref:"1.4.3",title:"Contrast (Minimum)",level:"AA",rules:["color-contrast"]},{ref:"1.4.4",title:"Resize Text",level:"AA",rules:["meta-viewport"]},{ref:"1.4.10",title:"Reflow",level:"AA",rules:[]},{ref:"1.4.11",title:"Non-text Contrast",level:"AA",rules:[]},{ref:"1.4.12",title:"Text Spacing",level:"AA",rules:[]},{ref:"1.4.13",title:"Content on Hover or Focus",level:"AA",rules:[]},{ref:"2.1.1",title:"Keyboard",level:"A",rules:["nested-interactive","frame-focusable-content"]},{ref:"2.1.2",title:"No Keyboard Trap",level:"A",rules:[]},{ref:"2.1.4",title:"Character Key Shortcuts",level:"A",rules:[]},{ref:"2.2.1",title:"Timing Adjustable",level:"A",rules:["meta-refresh"]},{ref:"2.2.2",title:"Pause, Stop, Hide",level:"A",rules:["blink","marquee"]},{ref:"2.4.1",title:"Bypass Blocks",level:"A",rules:["bypass"]},{ref:"2.4.2",title:"Page Titled",level:"A",rules:["document-title"]},{ref:"2.4.3",title:"Focus Order",level:"A",rules:["tabindex"]},{ref:"2.4.4",title:"Link Purpose (In Context)",level:"A",rules:["link-name"]},{ref:"2.4.5",title:"Multiple Ways",level:"AA",rules:[]},{ref:"2.4.6",title:"Headings and Labels",level:"AA",rules:["empty-heading","empty-table-header"]},{ref:"2.4.7",title:"Focus Visible",level:"AA",rules:[]},{ref:"2.5.1",title:"Pointer Gestures",level:"A",rules:[]},{ref:"2.5.2",title:"Pointer Cancellation",level:"A",rules:[]},{ref:"2.5.3",title:"Label in Name",level:"A",rules:["label-content-name-mismatch"]},{ref:"2.5.4",title:"Motion Actuation",level:"A",rules:[]},{ref:"2.5.8",title:"Target Size (Minimum)",level:"AA",rules:["target-size"]},{ref:"3.1.1",title:"Language of Page",level:"A",rules:["html-has-lang","html-lang-valid"]},{ref:"3.1.2",title:"Language of Parts",level:"AA",rules:["valid-lang"]},{ref:"3.2.1",title:"On Focus",level:"A",rules:[]},{ref:"3.2.2",title:"On Input",level:"A",rules:[]},{ref:"3.2.3",title:"Consistent Navigation",level:"AA",rules:[]},{ref:"3.2.4",title:"Consistent Identification",level:"AA",rules:[]},{ref:"3.3.1",title:"Error Identification",level:"A",rules:[]},{ref:"3.3.2",title:"Labels or Instructions",level:"A",rules:["label","form-field-multiple-labels","select-name"]},{ref:"3.3.3",title:"Error Suggestion",level:"AA",rules:[]},{ref:"3.3.4",title:"Error Prevention (Legal, Financial, Data)",level:"AA",rules:[]},{ref:"4.1.1",title:"Parsing",level:"A",rules:["duplicate-id","duplicate-id-aria","duplicate-id-active"]},{ref:"4.1.2",title:"Name, Role, Value",level:"A",rules:["button-name","aria-required-attr","aria-allowed-attr","aria-roles","aria-valid-attr","aria-valid-attr-value","aria-input-field-name","aria-toggle-field-name","select-name","input-button-name"]},{ref:"4.1.3",title:"Status Messages",level:"AA",rules:[]}];function In(e,t,n){if(e.rules.length===0)return{conformance:"Not Applicable",hits:[]};const a=e.rules.filter(s=>t.has(s));return a.length>0?{conformance:"Partially Supports",hits:a}:{conformance:e.rules.some(s=>n.has(s))?"Supports":"Not Evaluated",hits:a}}function hr(e,t){var d,u;const n=((d=e[0])==null?void 0:d.componentId)??"unknown",a=ie(e),r=e.flatMap(h=>h.violations),s=new Set(r.map(h=>h.ruleId)),i=new Set(s);for(const h of e){const p=h.axeRulesEvaluated;if(p){for(const f of p.passed)i.add(f.ruleId);for(const f of p.inapplicable)i.add(f.ruleId);for(const f of p.incomplete)i.add(f.ruleId)}}const c=h=>{const{conformance:p,hits:f}=In(h,s,i);return`
466
+ <tr class="${p==="Supports"?"supports":p==="Partially Supports"?"partial":p==="Not Evaluated"?"not-evaluated":"na"}">
467
+ <td class="ref">${w(h.ref)}</td>
468
+ <td class="title">${w(h.title)} <span class="level">(${h.level})</span></td>
469
+ <td class="conf">${p}</td>
470
+ <td class="notes">${f.length>0?`Auto-detected violations of: ${f.map(o=>`<code>${w(o)}</code>`).join(", ")}. Manual review recommended.`:p==="Not Applicable"?"No automated rule maps to this criterion. Manual evaluation required.":"No automated violations detected. Manual confirmation recommended for full claim."}</td>
471
+ </tr>`},l=h=>{const p=Tn.filter(f=>f.level===h).map(c).join("");return`
472
+ <h2>WCAG 2.1 Level ${h}</h2>
473
+ <table>
474
+ <thead>
475
+ <tr><th>Criterion</th><th>Title</th><th>Conformance</th><th>Notes</th></tr>
476
+ </thead>
477
+ <tbody>${p}</tbody>
478
+ </table>`};return`<!doctype html>
479
+ <html lang="en">
480
+ <head>
481
+ <meta charset="utf-8" />
482
+ <title>VPAT — ${w(n)}</title>
483
+ <style>
484
+ body { font: 11pt/1.5 -apple-system, system-ui, sans-serif; color: #0f172a; max-width: 8in; margin: 0.5in auto; padding: 0 0.25in; }
485
+ h1 { font-size: 18pt; }
486
+ h2 { font-size: 13pt; margin-top: 20pt; padding-bottom: 4pt; border-bottom: 1px solid #cbd5e1; }
487
+ .meta { font-size: 10pt; color: #475569; }
488
+ .preamble { background: #fef3c7; border: 1px solid #f59e0b; padding: 8pt 10pt; border-radius: 3pt; font-size: 10pt; margin: 12pt 0; }
489
+ table { width: 100%; border-collapse: collapse; margin: 6pt 0 12pt; font-size: 10pt; }
490
+ th, td { border: 1px solid #cbd5e1; padding: 5pt 7pt; text-align: left; vertical-align: top; }
491
+ th { background: #f1f5f9; }
492
+ td.ref { font-family: "SF Mono", Menlo, monospace; white-space: nowrap; }
493
+ td.conf { font-weight: 600; }
494
+ tr.supports td.conf { color: #15803d; }
495
+ tr.partial td.conf { color: #b45309; }
496
+ tr.na td.conf { color: #64748b; }
497
+ tr.not-evaluated td.conf { color: #b45309; background: #fef3c7; }
498
+ .level { color: #94a3b8; font-weight: 400; }
499
+ code { font-family: "SF Mono", Menlo, monospace; font-size: 9pt; background: #f1f5f9; padding: 0 3pt; }
500
+ @media print { body { margin: 0; } table, tr { page-break-inside: avoid; } }
501
+ </style>
502
+ </head>
503
+ <body>
504
+ <h1>Voluntary Product Accessibility Template (VPAT 2.5)</h1>
505
+ <p class="meta"><strong>Product:</strong> ${w(n)}</p>
506
+ <p class="meta" style="word-break: break-all;"><strong>URL evaluated:</strong> <code>${w(a)}</code></p>
507
+ <p class="meta"><strong>Date:</strong> ${w(new Date().toLocaleString())}</p>
508
+ <p class="meta"><strong>Evaluation method:</strong> Automated audit via wcagcheckr (axe-core ${w(((u=e[0])==null?void 0:u.axeVersion)??"n/a")}) across ${e.length} configured states.</p>
509
+ <div class="preamble">
510
+ <strong>Auto-generated draft.</strong> This VPAT was produced automatically from automated test
511
+ results. Conformance values reflect what automated testing can detect — they are not a complete
512
+ accessibility claim. Manual review by qualified accessibility evaluators is required before
513
+ using this document for procurement, contracting, or legal purposes. Criteria with no
514
+ automated rule are marked "Not Applicable" until manually evaluated.
515
+ </div>
516
+ ${l("A")}
517
+ ${l("AA")}
518
+ <footer style="margin-top: 24pt; padding-top: 8pt; border-top: 1px solid #cbd5e1; font-size: 9pt; color: #64748b;">
519
+ Generated by ${ae} v${he()}. Conformance assessed against all violations detected during the audit (not delta-filtered).
520
+ </footer>
521
+ </body>
522
+ </html>`}function fr(e){const t=[],n=e.target.outerHTML;if(e.ruleId==="image-alt"||e.ruleId==="input-image-alt"||e.ruleId==="area-alt"||e.ruleId==="svg-img-alt"||e.ruleId==="object-alt"){const a=/\bsrc\s*=\s*"([^"]+)"|\bsrc\s*=\s*'([^']+)'/i.exec(n);a&&t.push({label:"Image source",value:`\`${a[1]??a[2]}\` — if you have vision capability, view this image and propose alt text. Use \`alt=""\` if it's decorative.`});const r=/<title>([^<]+)<\/title>/i.exec(n);r&&t.push({label:"Inline SVG <title>",value:r[1]})}if(e.ruleId==="button-name"||e.ruleId==="empty-button"||e.ruleId==="input-button-name"){const a=/\baria-label\s*=\s*"([^"]+)"/i.exec(n);a&&t.push({label:"Existing aria-label",value:a[1]});const r=/<svg[^>]*>[\s\S]*?<title>([^<]+)<\/title>/i.exec(n);r&&t.push({label:"SVG <title> inside",value:r[1]});const s=/<(?:i|svg|span)[^>]*class\s*=\s*"([^"]*(?:icon|fa-|material-icon)[^"]*)"/i.exec(n);s&&t.push({label:"Icon class hint",value:`\`${s[1]}\` — name suggests intent`})}if(e.ruleId==="link-name"||e.ruleId==="empty-link"){const a=/\bhref\s*=\s*"([^"]+)"|\bhref\s*=\s*'([^']+)'/i.exec(n);a&&t.push({label:"Link href",value:`\`${a[1]??a[2]}\` — destination may suggest the right link text`});const r=/\baria-label\s*=\s*"([^"]+)"/i.exec(n);r&&t.push({label:"Existing aria-label",value:r[1]})}if(e.ruleId==="label"||e.ruleId==="select-name"||e.ruleId==="aria-input-field-name"){const a=/\bname\s*=\s*"([^"]+)"|\bname\s*=\s*'([^']+)'/i.exec(n);a&&t.push({label:"Field name attribute",value:`\`${a[1]??a[2]}\` — often hints at the intended label`});const r=/\bplaceholder\s*=\s*"([^"]+)"|\bplaceholder\s*=\s*'([^']+)'/i.exec(n);r&&t.push({label:"Placeholder text (visible)",value:r[1]??r[2]});const s=/\btype\s*=\s*"([^"]+)"|\btype\s*=\s*'([^']+)'/i.exec(n);s&&t.push({label:"Input type",value:`\`${s[1]??s[2]}\``});const i=/\bautocomplete\s*=\s*"([^"]+)"|\bautocomplete\s*=\s*'([^']+)'/i.exec(n);i&&t.push({label:"Autocomplete",value:i[1]??i[2]})}return t}function gr(e,t){var s;const n=new Set((t??[]).map(i=>`${i.pageUrl}::${i.ruleId}::${i.selector}`)),a=new Set,r=[];for(const i of e){const c=i.pageUrl??i.scope,l=((s=i.axeRulesEvaluated)==null?void 0:s.incomplete)??[];for(const d of l)for(const u of d.elements??[]){const h=`${c}::${d.ruleId}::${u.selector}`;a.has(h)||n.has(h)||(a.add(h),r.push({pageUrl:c,ruleId:d.ruleId,wcagCriterion:d.wcagCriterion,selector:u.selector,failureSummary:u.failureSummary,styles:u.styles}))}}return r}function mr(e,t,n,a,r,s,i,c){var T,F,P,V,B,Y,re,ge,Ot,_t;const l=new Set(r??[]),d=((T=e[0])==null?void 0:T.componentId)??"unknown",u=ie(e),h=!!(t&&t.baselineSnapshotMeta),p=e.flatMap(y=>y.violations),f=new Map;for(const y of p){const G=y.ruleId.startsWith("ai-")?`${y.ruleId}::${y.target.selector}::${(y.target.outerHTML??"").slice(0,200)}`:Oe(y.ruleId,y.target.selector),O=`${y.currentState.pseudoState} · ${y.currentState.theme} · ${y.currentState.direction} · ${y.currentState.breakpoint.id}`,K=f.get(G);if(K){K._states.includes(O)||K._states.push(O),K._instanceSelectors.includes(y.target.selector)||K._instanceSelectors.push(y.target.selector);continue}f.set(G,{...y,_states:[O],_instanceSelectors:[y.target.selector]})}const g=Array.from(f.values()).sort((y,G)=>{const O={critical:0,serious:1,moderate:2,minor:3};return(O[y.impact]??99)-(O[G.impact]??99)}),o=[];o.push("# Accessibility Fix Request"),o.push(""),o.push(`You are a senior accessibility engineer pairing with the user on their codebase. The component below has ${g.length} unique WCAG violation${g.length===1?"":"s"} detected by an automated audit running axe-core ${((F=e[0])==null?void 0:F.axeVersion)??"4.x"} across ${e.length} state combinations (hover, focus, focus-visible, active, disabled × dark / forced-colors / RTL / breakpoints). Your task: locate each violation in the codebase and apply the canonical fix — carefully, atomically, and asking before guessing.`),o.push("");const m=n&&n.length>0?{runs:n,workflows:_e}:void 0,k=c==null?void 0:c.map(y=>({criterionId:y.criterionId,ruleId:`ai-interactive::${y.criterionId}`,pageUrl:y.pageUrl,verdict:y.verdict,reasoning:y.reasoning})),v=fe(p,m,e,void 0,k);if(o.push("## Context"),o.push(""),o.push(`- **Component / scope:** \`${d}\``),o.push(`- **Audited URL:** ${u}`),o.push("- **WCAG target:** WCAG 2.1 AA + WCAG 2.2 AA + best-practice rules"),o.push("- **Source filter:** Full audit — every detected violation"),o.push(`- **Compliance posture:** Grade **${v.letter}** · **${v.risk.toUpperCase()} risk** (${v.totals.critical} critical · ${v.totals.serious} serious · ${v.totals.moderate} moderate · ${v.totals.minor} minor). ${v.risk==="critical"||v.risk==="high"?"Treat as triage priority — these violations represent meaningful lawsuit exposure under ADA Title III, EAA, and EN 301 549.":v.risk==="moderate"?"Should be addressed in the current sprint cycle; not lawsuit-emergency but real exposure.":"Lower exposure; still worth fixing but not blocking other work."}`),v.coverage){const y=v.coverage.untestedCriteria.length;y>0?o.push(`- **WCAG ${v.coverage.targetVersion} ${v.coverage.targetLevel} coverage:** ${v.coverage.evaluated} of ${v.coverage.totalApplicable} criteria evaluated. **Cannot claim ${v.coverage.targetLevel} conformance** until ${y} untested criteria are evaluated via Guided Tests (Pass / Fail / N/A). Untested: ${v.coverage.untestedCriteria.join(", ")}.`):o.push(`- **WCAG ${v.coverage.targetVersion} ${v.coverage.targetLevel} coverage:** ${v.coverage.evaluated} of ${v.coverage.totalApplicable} criteria evaluated. Full coverage — defensible conformance claim possible once all violations below are fixed.`)}h&&t&&o.push(`- **Trend vs your saved baseline:** ${t.newCount} new · ${t.persistentCount} carried over · ${t.fixedCount} resolved. The fix list below is the FULL current violation set, not just the delta.`),o.push(""),o.push("## Before you start (safety)"),o.push(""),o.push("Do these in order before making any code changes:"),o.push(""),o.push("1. **Verify a clean git working tree** (`git status`). If there are uncommitted changes, ask the user whether to commit, stash, or proceed."),o.push("2. **Switch to a feature branch** dedicated to this work — e.g., `a11y/fix-component-name`. Don't commit directly to `main` / `master` / `develop`."),o.push("3. **Plan one commit per fix.** Each violation gets its own atomic commit so any individual change can be reverted without untangling the rest. Commit messages should follow the pattern: `a11y: fix <ruleId> on <selector or component>`."),o.push("4. **Show diffs before applying** when your tooling supports it (Cursor / Copilot Chat agents). Never silently overwrite files."),o.push("5. **Do not bump dependencies** to resolve accessibility issues — fixes should be code changes, not package upgrades."),o.push(""),o.push("## Ask me these questions first"),o.push(""),o.push("Before applying any fix, ask the user the questions below. Don't proceed until you have answers — guessing here produces fixes that don't match the codebase's conventions and will be rejected on review."),o.push(""),o.push("1. **Framework + style approach?** React / Vue / Svelte / Angular / Solid / vanilla — and CSS Modules / Tailwind / styled-components / Emotion / scoped `<style>` / global CSS / design-tokens system. This determines *how* every fix is written."),o.push("2. **Existing design tokens or raw values?** If the codebase uses `var(--color-text-on-surface)` or a `tokens.ts` / `theme.json`, fixes must use tokens — never introduce new hex codes. Where can you read the available tokens?"),o.push('3. **Existing utility classes or component library?** e.g., does `.text-slate-700` or `<Button variant="primary">` already exist? Use what\'s there before inventing new selectors or components.'),o.push("4. **Files or directories that are off-limits?** Generated code, vendored libraries, third-party widgets, lockfiles — confirm what you must NOT touch."),o.push("5. **For color-contrast fixes: prefer adjusting foreground or background?** Brand color preservation is usually a stakeholder call, not a developer call."),o.push("6. **How should I verify fixes are done?** Re-run this auditor (preferred), run the existing test suite, manual screen-reader pass, or all three?"),o.push(""),o.push("## Decision rules (when ambiguity surfaces during fixes)"),o.push(""),o.push('- **Native HTML beats ARIA.** First rule of ARIA: don\'t use ARIA when a native element works. Prefer `<button>` over `<div role="button">`, `<nav>` over `<div role="navigation">`, etc.'),o.push("- **State-specific failures get state-specific fixes.** A `:hover`-only contrast failure goes in the `:hover` rule, not the default selector."),o.push("- **Where multiple valid fixes exist, surface options — don't pick silently.** E.g., adding an accessible name can be done via visible text, `aria-label`, or `aria-labelledby` referencing existing copy. Present the options with tradeoffs and let the user choose."),o.push("- **When the fix would alter visible design** (color shifts, layout changes, content additions), pause and confirm with the user before applying."),o.push(""),o.push("## ⛔ Do not dismiss findings without empirical verification"),o.push(""),o.push('**Default stance: every finding in this report is real and needs a fix.** Your job is to apply fixes, not to triage which ones "probably aren\'t real." That triage already happened — the auditor ran on a real browser, captured real DOM, real `getBoundingClientRect()` coordinates, real WCAG rule failures from axe-core. You are downstream of all that.'),o.push(""),o.push('Some categories (reading-order, tab-order) are explicitly heuristic and DO have legitimate false-positive cases. Those cases are listed inline in each section. **But "this looks like it might be a false positive based on the pattern" is not verification.** Pattern-matching from the report alone is inference, not evidence.'),o.push(""),o.push("**To dismiss any finding as a false positive, you must do all of the following:**"),o.push(""),o.push("1. **Load the deployed page in a real browser** at the viewport listed in the finding's capture-state header (e.g., 1280×800 LTR for the position-based analyses)."),o.push("2. **Inspect the flagged elements directly** — DevTools, real `getBoundingClientRect()` coordinates, real DOM source order."),o.push("3. **Compare your empirical observation to the auditor's claim.** Does the visual layout actually contradict the DOM order, or not?"),o.push("4. **If you conclude false positive, document why** — include the empirical evidence (coordinates, screenshots, DOM excerpt) so the user can audit your reasoning."),o.push(`5. **Even after empirical verification, ASK THE USER before dismissing.** They have context you don't (e.g., "yes that's an intentional skip-link / multi-column layout / RTL-only behavior").`),o.push(""),o.push("**What does NOT count as verification:**"),o.push(""),o.push("- Looking at the CSS file and not seeing `flex-direction: row-reverse` (the layout could be from `order:`, `grid-area`, absolute positioning, or RTL emulation)"),o.push('- Pattern-matching the finding shape ("this looks like a perfect arithmetic flip therefore it must be an RTL artifact")'),o.push(`- Reading the auditor's false-positive list and concluding your case "looks like one of those"`),o.push(`- Deciding it's "probably fine" because the structure looks reasonable on a quick read`),o.push(""),o.push("**If you cannot run a real browser** (e.g., environment doesn't allow headless Chromium), then you cannot verify, which means you cannot dismiss. In that case: surface the finding to the user, propose the fix, and let them decide."),o.push(""),o.push("**Skipping a finding without verification is worse than fixing one that turns out to be a false positive.** Over-fix is reversible (delete the commit). Under-fix is silent — the user thinks they're compliant when they aren't."),o.push(""),o.push("**When you DO verify a finding is a false positive** (per the empirical rules above), the right action is to recommend the user click **dismiss** on that finding in the auditor's Activity tab. The auditor records the dismissal with your reason, suppresses the finding from displayed counts, the Matrix banner, and future AI prompts. **Don't modify code to silence a verified false positive** — that produces brittle workarounds and pollutes the codebase with comments explaining heuristic quirks. Dismissals persist per-URL and survive re-audits cleanly."),o.push(""),o.push("## Content-decision rules (alt text, button labels, link text)"),o.push(""),o.push("Some violations require copy that the audit can't generate. Don't invent copy out of nowhere, but DON'T just punt to the user either — use the capabilities you have:"),o.push(""),o.push('1. **For images missing alt text** (`image-alt`, `input-image-alt`, `area-alt`, `svg-img-alt`): if you have vision capability, **fetch or view the image** at the `src` URL listed in the violation and PROPOSE descriptive alt text based on what the image actually contains. Mark obviously-decorative images (icons inside buttons that already have text labels, dividers, background flourishes) for `alt=""`.'),o.push('2. **For unlabeled buttons / links** (`button-name`, `link-name`, `empty-button`, `empty-link`): look at the surrounding code context — nearby text, the button\'s icon (often a known SVG name like "close" / "search" / "menu"), the route the link points at — and PROPOSE an accessible name. Show your inference reasoning.'),o.push("3. **For unlabeled form fields** (`label`, `select-name`, `aria-input-field-name`): inspect the field's `name`, `placeholder`, `id`, and any nearby visible text in the parent container. PROPOSE a label."),o.push("4. **For ambiguous content** (alt text where you genuinely can't tell what an image shows; labels where there's no surrounding context), ask the user — don't guess."),o.push("5. **Always show your proposed copy before applying** — let the user approve, edit, or reject."),o.push(""),o.push("## Constraints"),o.push(""),o.push("- Don't fix violations that aren't in this list (no scope creep)."),o.push(`- Don't reformat or reorder unrelated code. No "while I'm here" cleanup.`),o.push(`- Match existing code style. Don't invoke Prettier / ESLint to "fix everything" — focus your diff strictly on the violation's element + relevant style rule.`),o.push("- Don't add new dependencies, configs, or build steps."),o.push("- Add inline comments only where the fix's rationale is non-obvious; otherwise skip comments."),o.push("- Preserve existing functionality and visual design wherever possible."),o.push(""),o.push("## How to use this prompt"),o.push(""),o.push("Paste this into Claude / Cursor / Copilot Chat with your codebase open. The AI should:"),o.push('1. **First**: walk through the "Before you start" + "Ask me these questions" sections with the user.'),o.push("2. **Then**: identify the source files containing each violating element (use the selectors + outerHTML below)."),o.push("3. **Then**: for each violation, propose the fix using the recipe + answers gathered, surface ambiguity per the decision rules, and apply only after the user confirms."),o.push("4. **Finally**: produce one atomic commit per fix, with a clear message."),o.push(""),o.push("---"),o.push("");const I=(((P=e[0])==null?void 0:P.readingOrderIssues)??[]).filter(y=>!l.has(`reading-order::${y.selector}`)),$=(V=e[0])==null?void 0:V.positionAnalysisCapturedAt,N=$?`${$.breakpoint.width}×${$.breakpoint.height} (${$.breakpoint.label}), ${$.direction.toUpperCase()}, ${$.theme} theme, ${$.pseudoState} pseudo-state`:"(reference state unavailable — findings reflect last matrix state)",x=c==null?void 0:c.some(y=>y.criterionId==="1.3.2");if(I.length>0&&!x){o.push("## Reading-order concerns (DOM ≠ visual order)"),o.push(""),o.push(`_Positions captured at: **${N}**. Multi-column layouts, RTL pages, and mobile-stacked sections may have legitimately different reading orders at other viewports — verify each finding against the layout the user actually sees._`),o.push(""),o.push(`Screen readers read the DOM in source order. CSS that visually rearranges things (\`flex-direction: row-reverse\`, \`order:\`, \`grid-area\`, absolute positioning) does NOT reorder what the SR sees. ${I.length} element${I.length===1?" has":"s have"} a notable gap between their DOM position and their visual position:`),o.push(""),o.push("| DOM index | Visual index | Selector | Text |"),o.push("|---|---|---|---|");for(const y of I){const G=y.textSnippet.replace(/\|/g,"\\|").replace(/\n/g," ");o.push(`| ${y.domIndex} | ${y.visualIndex} | \`${y.selector}\` | ${G} |`)}o.push(""),o.push("**How to fix:** match DOM order to intended reading order — reorder the JSX/HTML, NOT the CSS. CSS visual rearrangement is a code smell only because it diverges from DOM order; if the DOM order is what the user should hear, no fix is needed."),o.push(""),o.push("**False-positive cases — these are the ONLY legitimate reasons to skip a flagged element:**"),o.push(""),o.push('1. **Multi-column / newspaper layouts** — DOM order is "column 1 top to bottom, then column 2", which is correct for SR but reads as "out of order" to this analyzer.'),o.push('2. **Skip-links** ("Skip to main content") — visually positioned late but DOM-early on purpose.'),o.push("3. **Caption-first patterns** — captions placed before their image in DOM so SR users get context before the alt text."),o.push("4. **Modal / popover triggers** — the trigger button is DOM-near the modal's portal mount point, which is often `<body>` end."),o.push(""),o.push('**Per the verification rules above (§ "Do not dismiss findings without empirical verification"): even when a finding looks like one of these false-positive cases, you must verify in a real browser before skipping. "Looks like a multi-column layout" is inference, not proof.** Read the DOM. Get real coordinates. Then ask the user. Then skip if confirmed.'),o.push(""),o.push("Surface the candidates above to the user, ask which (if any) are intended, and only reorder the rest. **If in doubt, fix it.** Reordering JSX is reversible; failing a Level A criterion is not."),o.push(""),o.push("---"),o.push("")}const _=(((B=e[0])==null?void 0:B.tabOrderIssues)??[]).filter(y=>!l.has(`tab-order::${y.selector}`)),j=c==null?void 0:c.some(y=>y.criterionId==="2.1.2"&&y.verdict==="pass");if(_.length>0&&!j){const y=_.filter(O=>O.flag==="visual"||O.flag==="both").length,G=_.filter(O=>O.flag==="tabindex"||O.flag==="both").length;o.push("## Tab-order concerns (keyboard focus sequence ≠ visual order) — WCAG 2.4.3 Focus Order (A)"),o.push(""),o.push(`_Positions captured at: **${N}**. Tab-position-vs-visual-position divergence is direction- and breakpoint-sensitive — what looks wrong here may be correct on RTL or mobile (and vice versa). Verify each finding against the layout the user actually sees._`),o.push(""),o.push(`Keyboard users tab through the page in one order; sighted users scan in another. axe-core does not detect this. ${_.length} focusable element${_.length===1?" has":"s have"} a notable divergence. Of these: ${y} ${y===1?"has":"have"} a visual-vs-tab mismatch (focus jumps around the page); ${G} ${G===1?"is":"are"} caused by positive \`tabindex\` reordering DOM source.`),o.push(""),o.push("| Tab pos | Visual pos | DOM pos | Flag | Selector | Text |"),o.push("|---|---|---|---|---|---|");for(const O of _){const K=O.textSnippet.replace(/\|/g,"\\|").replace(/\n/g," ");o.push(`| ${O.tabPosition} | ${O.visualPosition} | ${O.domPosition} | ${O.flag} | \`${O.selector}\` | ${K} |`)}o.push(""),o.push("**How to fix:**"),o.push(""),o.push("- **`flag: visual`** — tab order doesn't match visual layout. Reorder the JSX/HTML so the DOM source order matches the layout order users see. Don't use `tabindex` to \"patch\" this — that creates the next class of bug."),o.push('- **`flag: tabindex`** — a positive `tabindex` value (`tabindex="3"`) is reordering DOM. Remove the positive `tabindex` and rely on DOM source order. Use `tabindex="0"` only to make a non-focusable element focusable; use `tabindex="-1"` only to remove from the tab order.'),o.push("- **`flag: both`** — both conditions present. Fix the DOM order first, then remove the positive `tabindex`."),o.push(""),o.push("**False-positive cases — these are the ONLY legitimate reasons to skip a flagged element:**"),o.push(""),o.push('1. **Skip-links** ("Skip to main content") — visually-late, DOM-early on purpose. Their tab position should be near 1; their visual position is wherever their `:focus` styling places them.'),o.push("2. **Modal triggers + portal-rendered modals** — the trigger button is DOM-near the modal's portal mount point (often `<body>` end), so tab/visual positions can legitimately diverge."),o.push("3. **Dropdown menus / popovers** — a button DOM-far from its portal-rendered menu may produce false positives when the menu is open."),o.push(""),o.push(`**Per the verification rules above (§ "Do not dismiss findings without empirical verification"): even when a finding looks like one of these cases, you must verify in a real browser before skipping. Tabbing through the actual deployed page is empirical evidence; reading the auditor's table and pattern-matching is not.** If in doubt, fix it. Reordering JSX is reversible; failing a Level A criterion is not.`),o.push(""),o.push("Surface candidates to the user; ask which are intended; only fix the rest."),o.push(""),o.push("---"),o.push("")}const b=e.flatMap(y=>y.announcements??[]),R=e.flatMap(y=>y.focusEvents??[]).filter(y=>y.isFocusReset);if(b.length>0||R.length>0){if(o.push("## Runtime behavioral observations"),o.push(""),b.length>0){const y=b.filter(O=>O.politeness==="assertive").length;o.push(`**Live-region announcements** (${b.length} total, ${y} assertive): screen-reader announcements captured during state-driving. WCAG 4.1.3 Status Messages (AA) requires that non-essential changes use polite announcements; only blocking errors warrant assertive. Review:`),o.push("");const G=b.slice(0,8);for(const O of G){const K=O.text.replace(/\|/g,"\\|").replace(/\n/g," ").slice(0,120);o.push(`- \`[${O.politeness}${O.role?`/${O.role}`:""}]\` ${K}`)}b.length>8&&o.push(`- (and ${b.length-8} more)`),o.push(""),o.push("Verify each is appropriate. Common bug: error messages firing as `polite` (SR may not interrupt), or status updates firing as `assertive` (interrupts user mid-sentence)."),o.push("")}if(R.length>0){o.push(`**Focus resets to body** (${R.length} occurrence${R.length===1?"":"s"}): during the audit, focus jumped to \`<body>\` or \`<html>\` unexpectedly. This is almost always a bug — focus should move to a logical landing place (modal, alert, next field), never disappear.`),o.push("");for(const y of R.slice(0,5))o.push(`- \`${y.fromSelector??"(none)"}\` → \`${y.toSelector}\``);R.length>5&&o.push(`- (and ${R.length-5} more)`),o.push(""),o.push("Common cause: an element is removed from the DOM (e.g., modal closes) without `.focus()` being called on a sensible destination."),o.push("")}o.push("---"),o.push("")}const S=((Y=e[0])==null?void 0:Y.undefinedCustomProperties)??[];if(S.length>0){o.push("## Undefined CSS custom properties (cascade root cause)"),o.push(""),o.push(`${S.length} CSS custom propert${S.length===1?"y is":"ies are"} referenced via \`var(--name)\` but never declared. When a custom property is missing, the browser falls back to the property's initial value (often \`Canvas\` for color slots → resolves to white), which can produce a wave of false contrast failures. **Fix these missing tokens FIRST** — many of the contrast violations below may disappear once the cascade resolves correctly.`),o.push(""),o.push("| Custom property | References | First sample sites |"),o.push("|---|---|---|");for(const y of S){const G=y.sampleSites.map(O=>`\`${O.selector} { ${O.property} }\``).join("; ");o.push(`| \`${y.name}\` | ${y.referenceCount} | ${G} |`)}o.push(""),o.push("Each entry needs a definition in `:root` (or wherever the design-token layer lives). Surface to the user before applying contrast fixes — the missing token may be the root cause."),o.push(""),o.push("---"),o.push("")}if(n&&n.length>0){o.push("## Manual checks already completed (Guided Tests)"),o.push(""),o.push(`The consultant has run ${n.length} Intelligent Guided Test workflow${n.length===1?"":"s"} against this component covering the WCAG criteria automation cannot verify (keyboard navigation, screen-reader experience, focus management, forms, error prevention, etc.). Treat the verdicts below as ground truth — these came from a human running the actual flow.`),o.push(""),o.push("**Manual passes outrank heuristic findings.** When an IGT workflow has passed (e.g. Keyboard 7/7) and the heuristic analyzers below (Tab-order concerns, Reading-order concerns) flag findings on the same WCAG dimension, the manual pass is ground truth. A human ran real keyboard/SR navigation against the deployed page; the heuristic computes visual position from `getBoundingClientRect()` and can produce false positives on sticky / multi-column / grid layouts. **When the IGT and the heuristic disagree on the same WCAG criterion, treat the heuristic findings as suspect first**, run the empirical verification described above, and (if confirmed false positive) recommend dismissal via the Activity tab."),o.push("");const y=_e;o.push("| Workflow | Status | Notes |"),o.push("|---|---|---|");for(const z of n){const U=y.find(nt=>nt.id===z.workflowId);if(!U)continue;let X=0,L=0,ce=0,tt=0,me=0;for(const nt of U.steps){const Ce=z.steps[nt.id];if(!Ce){me++;continue}Ce.status==="pass"?X++:Ce.status==="fail"?L++:Ce.status==="skip"?ce++:Ce.status==="not-applicable"?tt++:me++}const Zn=L>0?`❌ ${L} failed`:me>0?`⚠ ${me} unanswered`:`✓ all ${U.steps.length} answered`,Le=[`${X}p`,`${L}f`];tt>0&&Le.push(`${tt} N/A`),ce>0&&Le.push(`${ce} skip`),me>0&&Le.push(`${me} unanswered`);const ea=`${Le.join(" · ")} of ${U.steps.length} steps`;o.push(`| ${U.name} | ${Zn} | ${ea} |`)}o.push("");const G=[];for(const z of n){const U=y.find(X=>X.id===z.workflowId);if(U)for(const X of U.steps){const L=z.steps[X.id];(L==null?void 0:L.status)==="fail"&&G.push({workflow:U.name,stepTitle:X.question,notes:L.notes})}}if(G.length>0){o.push("**Failed manual checks** (these are real WCAG failures the consultant verified by hand):"),o.push("");for(const z of G)o.push(`- **${z.workflow} → ${z.stepTitle}**${z.notes?`: ${z.notes}`:""}`);o.push(""),o.push("**Address these alongside the automated violations below.** They will not appear in the violations list because no automated rule catches them — but they are still real WCAG failures and must be fixed for compliance."),o.push("")}const O=new Set(n.map(z=>z.workflowId)),K=y.filter(z=>!O.has(z.id));K.length>0&&(o.push(`**Not yet manually verified** (${K.length} workflow${K.length===1?"":"s"}): `+K.map(z=>z.name).join(", ")+". These cover WCAG criteria automation cannot detect; ask the consultant whether to run them before declaring the audit complete."),o.push("")),o.push("---"),o.push("")}if(a&&a.pagesAudited>1){const y=(()=>{try{return new URL(u).origin}catch{return null}})(),G=(()=>{try{return new URL(a.startUrl).origin}catch{return null}})();if(y&&G&&y===G){if(o.push("## Site-wide context (from a recent site crawl)"),o.push(""),o.push(`A recent crawl of ${a.pagesAudited} page${a.pagesAudited===1?"":"s"} on this origin produced grade **${a.siteGrade}** site-wide with **${a.totalUniqueViolations}** unique violations. Use this context for WCAG 3.2.3 (Consistent Navigation) and 3.2.4 (Consistent Identification) which require cross-page comparison.`),o.push(""),a.topViolations.length>0){o.push("**Most-frequent violations across the site** (rules to prioritize because they affect multiple pages):"),o.push("");for(const O of a.topViolations.slice(0,6))o.push(`- \`${O.ruleId}\` — ${O.impact} — found on ${O.urlsAffected} page${O.urlsAffected===1?"":"s"} (${O.totalOccurrences} total instances)`);o.push(""),o.push("Fixing one of these rules in shared components / layouts is leverage — one fix resolves N pages."),o.push("")}o.push("**Manual cross-page checks the AI should walk through:**"),o.push(""),o.push("- **3.2.3 Consistent Navigation** — verify primary nav appears in the same DOM order on every page audited above. Inconsistent ordering between pages is a Level AA failure."),o.push('- **3.2.4 Consistent Identification** — verify components with identical functionality (logo "home" link, search button, menu toggle) have identical accessible names across pages.'),o.push("- **3.2.6 Consistent Help (WCAG 2.2 A)** — verify any help mechanism (chat widget, contact link, FAQ link) appears in the same relative position on every page that has one."),o.push(""),o.push("---"),o.push("")}}o.push("## Violations to fix"),o.push(""),g.forEach((y,G)=>{const O=fa[y.ruleId],K=y._instanceSelectors.length;if(o.push(`### ${G+1}. \`${y.ruleId}\` — ${y.impact} — WCAG ${y.wcagCriterion} ${y.wcagLevel}${K>1?` (${K} instances)`:""}`),o.push(""),o.push(`**Description:** ${y.description}`),o.push(""),K>1){o.push(`**Affects ${K} spots — fix the rule once, applies to all:**`);for(const L of y._instanceSelectors.slice(0,8))o.push(`- \`${L}\``);y._instanceSelectors.length>8&&o.push(`- (and ${y._instanceSelectors.length-8} more)`)}else o.push(`**Selector:** \`${y.target.selector}\``);o.push(""),o.push(`**Found in state(s):** ${y._states.join(" / ")}`),o.push("");const z=fr(y);if(z.length>0){for(const L of z)o.push(`**${L.label}:** ${L.value}`);o.push("")}y.target.failureSummary&&(o.push(`**Diagnostic:** ${y.target.failureSummary.replace(/\n+/g," ")}`),o.push("")),y.ai&&(o.push(`**🤖 AI-verified finding** (model: ${y.ai.model}, confidence: ${(y.ai.confidence*100).toFixed(0)}%)`),o.push(`> ${y.ai.reasoning}`),o.push(""));const U=y.target.cascadeChain;if(U&&(U.color||U.backgroundColor)){if(o.push("**CSS cascade chain (authored → rendered):**"),U.color){const L=U.color.vars.length>0?` (vars: ${U.color.vars.map(ce=>`\`${ce}\``).join(", ")})`:"";o.push(`- \`color\`: \`${U.color.authored}\` → \`${U.color.rendered}\`${L}${U.color.ruleSelector?` — set in rule \`${U.color.ruleSelector}\``:""}`)}if(U.backgroundColor){const L=U.backgroundColor.vars.length>0?` (vars: ${U.backgroundColor.vars.map(ce=>`\`${ce}\``).join(", ")})`:"";o.push(`- \`background-color\`: \`${U.backgroundColor.authored}\` → \`${U.backgroundColor.rendered}\`${L}${U.backgroundColor.ruleSelector?` — set in rule \`${U.backgroundColor.ruleSelector}\``:""}`)}o.push("")}const X=i==null?void 0:i[y.matchKey];if(X&&X.suggestions.length>0){o.push(`**AI color suggestions** (cached from in-UI tooling — ${X.suggestions.length} candidates passing WCAG):`);for(const L of X.suggestions)o.push(`- foreground \`${L.foreground}\` on background \`${L.background}\` → ${L.contrast.toFixed(2)}:1 — ${L.rationale}`);o.push(`> AI reasoning: ${X.reasoning}`),o.push("")}o.push("**Element HTML:**"),o.push("```html"),o.push(y.target.outerHTML),o.push("```"),o.push(""),O?(o.push(`**Recipe:** ${O.summary}`),O.snippet&&(o.push(""),o.push("```"+(O.snippetLang??"")),o.push(O.snippet),o.push("```"))):o.push(`**Recipe:** No curated recipe for this rule — apply the standard WCAG ${y.wcagCriterion} guidance.`),y.helpUrl&&(o.push(""),o.push(`**More info:** ${y.helpUrl}`)),o.push(""),o.push("---"),o.push("")});const A=gr(e,s);if(A.length>0){o.push("## Items axe couldn't determine — needs human review"),o.push(""),o.push("axe-core ran these rules but couldn't conclude pass/fail (the most common cause is color-contrast on a gradient / image / semi-transparent background). For each element below: if you have vision capability, sample the rendered background and compute the contrast ratio; otherwise ask the user to verify visually."),o.push("");for(const y of A)o.push(`### \`${y.ruleId}\` on \`${y.selector}\``),o.push(""),o.push(`- **WCAG criterion:** ${y.wcagCriterion}`),o.push(`- **Page URL:** ${y.pageUrl}`),y.failureSummary&&o.push(`- **axe diagnostic:** ${y.failureSummary}`),(re=y.styles)!=null&&re.foreground&&o.push(`- **Foreground:** \`${y.styles.foreground}\``),(ge=y.styles)!=null&&ge.background&&o.push(`- **Background:** \`${y.styles.background}\``),(Ot=y.styles)!=null&&Ot.fontSize&&o.push(`- **Font size:** ${y.styles.fontSize}px, weight ${y.styles.fontWeight??"unknown"}`),(_t=y.styles)!=null&&_t.textSample&&o.push(`- **Text sample:** ${JSON.stringify(y.styles.textSample)}`),o.push(""),o.push("---"),o.push("")}return o.push("## Manual verification checklist — non-automatable WCAG 2.1 / 2.2 AA criteria"),o.push(""),o.push("Automated audits catch ~30–50% of WCAG criteria. The remaining ~50% require human judgment, multi-page comparison, or runtime interaction we cannot script. **After applying the fixes above, walk the user through each item below before declaring the audit complete.** Mark each one passed, failed (and add to the fix queue), or N/A (with reasoning)."),o.push(""),o.push('Items the auditor has already flagged in earlier sections (reading-order, tab-order, behavioral observations, IGT manual checks) are listed below as "ALREADY FLAGGED" — confirm those fixes are in place.'),o.push(""),o.push("### Perceivable"),o.push(""),o.push("- [ ] **1.2.1 Audio-only / Video-only Prerecorded (A)** — every audio-only file has a text transcript; every silent-video file has a text alternative or audio description. Ask the user: does the page have media? If yes, list each `<audio>` / `<video>` and verify the alternative."),o.push('- [ ] **1.2.2 Captions Prerecorded (A)** — every prerecorded video with audio has synchronized captions. If `<video>` elements exist without `<track kind="captions">`, ask the user about caption files.'),o.push("- [ ] **1.2.3 Audio Description / Media Alternative Prerecorded (A)** — prerecorded video has either audio description or a full text alternative."),o.push("- [ ] **1.2.4 Captions Live (AA)** — live audio content (livestream, real-time meeting) has captions. Usually N/A for static sites."),o.push("- [ ] **1.2.5 Audio Description Prerecorded (AA)** — prerecorded video has audio description for visual-only information."),o.push("- [ ] **1.4.2 Audio Control (A)** — if any audio plays automatically for >3 seconds, there is a mechanism to pause / stop / control volume. Test by loading the page; if anything autoplays audibly, fail."),o.push("- [ ] **1.4.4 Resize Text (AA)** — zoom the browser to 200%. All text remains readable; nothing is cut off. No horizontal scrolling within content blocks."),o.push("- [ ] **1.4.5 Images of Text (AA)** — confirm text is rendered as actual text, not as images. Exceptions: logos, brand wordmarks. Walk the user through their hero / heading sections."),o.push("- [ ] **1.4.10 Reflow (AA)** — resize browser to 320px wide × 256px tall. Page is usable; no horizontal scrollbar appears (except for data tables / maps / code)."),o.push("- [ ] **1.4.12 Text Spacing (AA)** — apply user-stylesheet overrides: line-height ≥1.5×; paragraph spacing ≥2×; letter-spacing ≥0.12×; word-spacing ≥0.16×. Layout doesn't break, no clipped/overlapping text."),o.push("- [ ] **1.4.13 Content on Hover or Focus (AA)** — for any tooltip / popover / hover-revealed content: verify it is *dismissible* (Esc closes), *hoverable* (mouse can move into it without it disappearing), and *persistent* (doesn't auto-disappear unless user dismisses)."),o.push(""),o.push("### Operable"),o.push(""),o.push("- [ ] **2.1.2 No Keyboard Trap (A)** — Tab through the entire page from start to finish; verify focus never gets stuck in a region you can't leave with Tab/Shift+Tab/Esc. Common offenders: video players, custom widgets, modals without proper Esc handling."),o.push('- [ ] **2.1.4 Character Key Shortcuts (A)** — if the site implements single-key shortcuts (pressing "j" advances feed, etc.), they must be either turn-off-able OR only active when focus is on a specific control. Ask the user about implemented shortcuts.'),o.push('- [ ] **2.2.1 Timing Adjustable (A)** — if any timeout exists (auto-logout, session expiry, "complete this in X seconds"), user can turn it off, adjust it, or extend it (≥10× the default). Exception: real-time events. Ask the user.'),o.push("- [ ] **2.2.2 Pause, Stop, Hide (A)** — for moving / blinking / scrolling / auto-updating content lasting >5s: there is a mechanism to pause/stop/hide it. Carousels, tickers, animations, autoplaying video — all need controls."),o.push("- [ ] **2.3.1 Three Flashes or Below Threshold (A)** — no content flashes more than 3× per second. Test with anything that pulses, blinks, or has rapid color changes (loading animations, alert flashing). Use https://trace.umd.edu/peat/ for borderline cases."),_.length>0?o.push("- [x] **2.4.3 Focus Order (A)** — ALREADY FLAGGED above (`Tab-order concerns` section). Confirm fixes are in place."):o.push("- [ ] **2.4.3 Focus Order (A)** — auto-detector flagged 0 issues, but spot-check by tabbing through the page: focus visits elements in a logical order (matches visual / semantic flow)."),o.push("- [ ] **2.4.5 Multiple Ways (AA)** — at least two ways to find any page within a set: site search + nav menu, OR menu + sitemap, OR menu + table of contents, etc. Single-page apps may exempt; ask user."),o.push("- [ ] **2.5.1 Pointer Gestures (A)** — any multi-point gesture (pinch-zoom, two-finger swipe) or path-based gesture (signature, drag-along-curve) has a single-pointer alternative. Common offender: maps, signatures, drawing canvases."),o.push("- [ ] **2.5.2 Pointer Cancellation (A)** — actions trigger on the up-event (mouseup / pointerup), NOT the down-event. Pressing then dragging-away cancels. Test by mouse-down on a button → drag off → release: action should not fire."),o.push("- [ ] **2.5.4 Motion Actuation (A)** — features triggered by device motion (shake-to-undo, tilt-to-scroll) can be disabled AND have UI-button alternatives. Usually N/A for desktop sites."),o.push("- [ ] **2.5.7 Dragging Movements (AA, WCAG 2.2)** — any drag-only operation (kanban board, slider) has a non-drag alternative (buttons, click-to-place). Test sliders, drag-and-drop, sortable lists."),o.push(""),o.push("### Understandable"),o.push(""),o.push("- [ ] **3.2.1 On Focus (A)** — focusing any element does NOT trigger a context change (URL change, viewport scroll, modal open, form submit). Tab through every focusable element; verify none cause unexpected behavior."),o.push("- [ ] **3.2.2 On Input (A)** — changing the value of any form control does NOT trigger context change unless the user was warned. Common offender: `<select onChange={navigate}>` — must include adjacent submit button or warn the user."),a&&a.pagesAudited>1?(o.push("- [x] **3.2.3 Consistent Navigation (AA)** — site-crawl context provided above. Walk the user through verifying nav appears in the same order across pages."),o.push("- [x] **3.2.4 Consistent Identification (AA)** — verify components with the same function have the same accessible name across pages (logo home link, search button, menu toggle).")):(o.push("- [ ] **3.2.3 Consistent Navigation (AA)** — REQUIRES MULTI-PAGE COMPARISON. Ask the user for 2-3 representative pages; verify primary nav, footer, and search appear in the same DOM order on each."),o.push('- [ ] **3.2.4 Consistent Identification (AA)** — REQUIRES MULTI-PAGE COMPARISON. Components with identical functionality must have identical accessible names. Compare logo "home" link, search button, menu toggle across pages.')),o.push("- [ ] **3.2.6 Consistent Help (A, WCAG 2.2)** — REQUIRES MULTI-PAGE COMPARISON. If a help mechanism exists (chat, contact link, FAQ), it appears in the same relative order on every page where it appears."),o.push("- [ ] **3.3.4 Error Prevention — Legal, Financial, Data (AA)** — for any form that submits legal commitments (contracts, agreement), financial transactions (payments), or modifies/deletes user data: there is at least ONE of (a) reversibility, (b) error-checking with confirmation, (c) review-and-confirm step before final submit."),o.push("- [ ] **3.3.7 Redundant Entry (A, WCAG 2.2)** — forms in a multi-step process do NOT ask for the same information twice unless essential (re-entering password for confirmation is allowed). Auto-fill or pre-fill where possible."),o.push("- [ ] **3.3.8 Accessible Authentication — Minimum (AA, WCAG 2.2)** — login does not require a cognitive function test (puzzle, riddle, character-recognition) unless an alternative is provided (object recognition + autofill support are allowed; CAPTCHAs that require pattern recognition typically fail)."),o.push(""),o.push("### Robust"),o.push(""),b.length>0||R.length>0?o.push("- [x] **4.1.3 Status Messages (AA)** — runtime observations provided above (`Runtime behavioral observations` section). Confirm announcements use appropriate `aria-live` politeness and focus management is correct."):o.push('- [ ] **4.1.3 Status Messages (AA)** — status updates that don\'t cause a focus change (form-validation errors, "saved" confirmations, search-result counts) must use `role="status"` (polite) or `role="alert"` (assertive). Ask the user to demo their form-error and async-update flows; verify SR announces them.'),o.push(""),o.push("### Manual checks STILL TO DO"),o.push(""),o.push(`Walk the user through each unchecked box above (or batch them by criterion type — "let's test all the keyboard ones together"). For each: either mark passed (and move on), failed (add to the fix queue and resolve), or N/A (briefly justify why this site doesn't require this criterion).`),o.push(""),o.push('**Do not declare the audit complete until every box above is checked.** A site that passes only the automated portion is "axe-clean," not WCAG-compliant.'),o.push(""),o.push("---"),o.push(""),o.push("## When you're done — verification"),o.push(""),o.push("Before reporting completion, do all of the following:"),o.push(""),o.push(`1. **Confirm all ${g.length} violation${g.length===1?"":"s"} above are addressed.** Walk back through the list with the user, ticking each one off.`),o.push("2. **Re-run the auditor** (or whatever method we agreed in question 6 above). Verify zero NEW violations were introduced by the fixes."),o.push(`3. **Walk through the Manual verification checklist above** — every item should be marked passed, failed-and-fixed, or N/A. This is the half of WCAG that automation can't reach; skipping it means "axe-clean," not "compliant."`),o.push("4. **Visual diff** — for design-affecting fixes (color, spacing, layout), confirm the change with the user against the original. Storybook stories and visual regression suites are good checkpoints if the codebase has them."),o.push("5. **Commit log review** — surface the list of commits you produced. Each should be atomic and revertable."),o.push("6. **Report what you couldn't do** explicitly. If any violation required information we don't have (e.g., correct alt text), surface a TODO list for the user rather than guessing."),o.push(""),o.push("## Success criteria"),o.push(""),o.push("- All "+g.length+" violation"+(g.length===1?"":"s")+" above are resolved or explicitly deferred with reason."),o.push('- **Every reading-order and tab-order finding** is either fixed OR documented as a verified false positive (per § "Do not dismiss findings without empirical verification" above). Pattern-matching dismissals do not count.'),o.push('- **Every item in the manual verification checklist** above is marked passed, failed-and-fixed, or N/A-with-reason. (This is what separates "axe-clean" from "WCAG-compliant" — do not skip.)'),o.push("- No new violations introduced."),o.push("- State-specific behavior preserved (`:hover`, `:focus`, dark-mode, RTL, breakpoints)."),o.push("- One atomic commit per fix; clean revertable history."),o.push("- No unrelated code reformatted, no dependencies bumped."),o.push("- Diff is reviewable in under 10 minutes per fix."),o.push(""),o.push(`_Generated by ${ae} v${he()}._`),o.join(`
523
+ `)}const yr=5;function br(e){const t=new Map;let n=0;for(const a of e){if(!a.screenshotDataUrl||a.violations.length===0)continue;const r=`${a.state.pseudoState} · ${a.state.theme} · ${a.state.direction} · ${a.state.breakpoint.id}`,s=r;t.has(s)||t.set(s,{stateLabel:r,screenshotDataUrl:a.screenshotDataUrl,violations:[]});const i=t.get(s);for(const c of a.violations)n++,i.violations.push({...c,_idx:n})}return Array.from(t.values()).sort((a,r)=>r.violations.length-a.violations.length).slice(0,yr)}const zt=1280,Bt=800,wr={low:"Low risk",moderate:"Moderate risk",high:"High risk",critical:"Critical risk"},vr={low:"#d1fae5",moderate:"#fef3c7",high:"#fed7aa",critical:"#fecaca"},Ar={low:"#065f46",moderate:"#92400e",high:"#9a3412",critical:"#991b1b"},Sr={A:"#10b981",B:"#84cc16",C:"#eab308",D:"#f97316",F:"#dc2626"},kr={defense:{title:"Accessibility Defense Bundle",documentLabel:"engineering, design, and risk-management",disclaimerHeading:"This document is not a compliance certification.",disclaimerBody:"Automated accessibility audits, including this one, detect a subset (typically 30–50%) of WCAG success criteria. Conformance with the full WCAG specification requires manual evaluation by qualified accessibility professionals, including (but not limited to): keyboard-only operability testing, screen-reader compatibility testing across NVDA / JAWS / VoiceOver, manual review of dynamic content and live regions, and content-quality review for alt text, link text, and labels."},evidence:{title:"Accessibility Compliance Evidence",documentLabel:"documentation and review",disclaimerHeading:"This document is automated audit evidence — not a determination of liability or conformance.",disclaimerBody:"This report documents detected accessibility defects in the audited resource at the date and time of capture. The audit was conducted using axe-core, an industry-standard automated accessibility-rule engine maintained by Deque Systems and used by Microsoft (Accessibility Insights), Google (Lighthouse), IBM, and others. The findings represent objective rule-engine output, not subjective evaluation. Automated audits surface a subset (typically 30–50%) of WCAG success criteria; the absence of a violation in this report is not evidence of conformance, and the presence of a violation is not, by itself, a determination of legal liability."}};function xr(e,t,n,a,r){return Rn(e,t,"evidence",n,a,r)}function Er(e){return Cn(e,"letter")}function $r(e){return Cn(e,"report")}function Tr(e){var d;const t=e.flatMap(u=>u.violations),n=fe(t,void 0,e),a=ie(e),r=new Map;for(const u of t){const h=Oe(u.ruleId,u.target.selector);r.has(h)||r.set(h,u)}const s=Array.from(r.values()).sort((u,h)=>{const p={critical:0,serious:1,moderate:2,minor:3};return(p[u.impact]??99)-(p[h.impact]??99)}),i=new Map;for(const u of s){const h=i.get(u.ruleId)??[];h.push(u),i.set(u.ruleId,h)}const c=[];if(c.push("# Help me fix accessibility issues on my website"),c.push(""),c.push("You are an accessibility advisor helping me — a non-technical site owner — understand and fix accessibility issues on my website. I ran an automated audit and got the issues below. I may not have technical knowledge, so please explain things in plain English."),c.push(""),c.push("## What I need from you"),c.push(""),c.push("1. **First, ask me what I use to manage my website.** Common platforms: Squarespace, Wix, Shopify, WordPress (with theme name), Webflow, Framer, custom-built. Your advice will be different for each."),c.push("2. **Then, for each issue below, tell me clearly:**"),c.push(" - Whether **I can fix it myself** in my site editor (changing colors, adding alt text, editing button labels, etc.) — and if so, walk me through where to look in my platform"),c.push(" - Or whether **my developer needs to do this** (code-level fix) — and what to ask them to do"),c.push("3. **Prioritize ruthlessly.** I want to start with the issues that:"),c.push(" - Are most likely to come up in an ADA-related lawsuit (critical-impact items)"),c.push(" - Are easiest for me to fix without involving a developer"),c.push("4. **Don't bury me in jargon.** If you have to use a technical term, explain it in plain English first."),c.push("5. **If something is genuinely beyond a non-technical fix, say so.** Tell me what to ask my developer or website-builder support team."),c.push(""),c.push("## Audit summary"),c.push(""),c.push(`- **Page audited:** ${a}`),c.push(`- **Lawsuit-target risk:** ${cn[n.risk]} — ${ln[n.risk]}`),c.push(`- **Issues found:** ${n.totals.unique} unique problems (${n.totals.critical} critical · ${n.totals.serious} serious · ${n.totals.moderate} moderate · ${n.totals.minor} minor)`),n.coverage&&n.coverage.untestedCriteria.length>0?c.push(`- **WCAG conformance progress:** ${n.coverage.evaluated} of ${n.coverage.totalApplicable} criteria evaluated. Fixing the issues below will clear me of automated lawsuit-targeting tools. To make a formal claim that the site meets WCAG ${n.coverage.targetVersion} ${n.coverage.targetLevel}, ${n.coverage.untestedCriteria.length} more criteria need human review — see the "Guided Tests" workflows in the auditor side panel for these.`):n.coverage&&n.coverage.untestedCriteria.length===0&&c.push(`- **WCAG conformance:** All ${n.coverage.totalApplicable} applicable WCAG ${n.coverage.targetVersion} ${n.coverage.targetLevel} criteria have been evaluated. Fixing the issues below will produce a defensible conformance claim.`),c.push(""),c.push("## The issues, in plain language"),c.push(""),i.size===0)return c.push("No automated issues were detected. Note: automated checks only catch about 30–50% of accessibility problems. Manual testing covers the rest."),c.join(`
524
+ `);let l=1;for(const[u,h]of i.entries()){const p=(d=h.find(o=>o.target.opacityContext))==null?void 0:d.target.opacityContext,f=sn(u,p),g=h[0].impact;c.push(`### ${l}. [${g.toUpperCase()}] ${f.whatsWrong}`),c.push(""),c.push(`**Why this matters.** ${f.whyItMatters}`),c.push(""),c.push(`**Plain-language fix guidance.** ${f.howToFix}`),c.push(""),c.push(`**Where on the page (${h.length} ${h.length===1?"spot":"spots"}):**`);for(const o of h.slice(0,8))c.push(`- \`${o.target.selector}\``);h.length>8&&c.push(`- (and ${h.length-8} more spots)`),c.push(""),c.push(`*Technical reference for your developer:* axe-core rule \`${u}\` — full docs at https://dequeuniversity.com/rules/axe/4.11/${u}`),c.push(""),l++}return c.push("---"),c.push(""),c.push("## How to use this output"),c.push(""),c.push("Walk through this list with me one issue at a time. For each:"),c.push("1. Help me understand the issue with a real example of who it affects."),c.push("2. Tell me clearly if I can fix it myself, and if so, walk me through it for my platform."),c.push("3. If I need a developer, give me a short paragraph I can paste into an email or message to them."),c.push("4. Help me prioritize — I have limited time and want to fix the riskiest things first."),c.push(""),c.push("Start by asking me which platform I use."),c.join(`
525
+ `)}function Cn(e,t){const n=e.flatMap(p=>p.violations),a=fe(n,void 0,e),r=ie(e),s=new Date,i=new Map;for(const p of n){const f=Oe(p.ruleId,p.target.selector);i.has(f)||i.set(f,p)}const c=Array.from(i.values()).sort((p,f)=>{const g={critical:0,serious:1,moderate:2,minor:3};return(g[p.impact]??99)-(g[f.impact]??99)}),l=new Map;for(const p of c){const f=l.get(p.ruleId)??[];f.push(p),l.set(p.ruleId,f)}const d=Array.from(l.entries()).map(([p,f])=>{var I;const g=(I=f.find($=>$.target.opacityContext))==null?void 0:I.target.opacityContext,o=sn(p,g),m=f[0].impact,k=f.slice(0,8).map($=>`<code>${w($.target.selector)}</code>`).join(", "),v=f.length>8?` <em>(and ${f.length-8} more)</em>`:"";return`
526
+ <section class="rule">
527
+ <div class="rule-header">
528
+ <span class="impact impact-${w(m)}">${w(m)}</span>
529
+ <h3>${w(o.whatsWrong)}</h3>
530
+ </div>
531
+ <p class="why"><strong>Why this matters.</strong> ${w(o.whyItMatters)}</p>
532
+ <p class="how"><strong>How to fix.</strong> ${w(o.howToFix)}</p>
533
+ <p class="where">
534
+ <strong>Where on the page (${f.length} ${f.length===1?"spot":"spots"}):</strong> ${k}${v}
535
+ </p>
536
+ <details class="techy">
537
+ <summary>Technical reference for the developer</summary>
538
+ <p>axe-core rule ID: <code>${w(p)}</code>. Full rule documentation:
539
+ <a href="https://dequeuniversity.com/rules/axe/4.11/${w(p)}">dequeuniversity.com/rules/axe/4.11/${w(p)}</a>.</p>
540
+ </details>
541
+ </section>`}).join(""),u=t==="letter"?a.totals.critical>0?"Hi! I ran an automated accessibility check on our site and found some things I think we should fix. A few are flagged as <strong>critical</strong> — those tend to be the ones that come up in ADA-related lawsuits, so they're worth prioritizing.":a.totals.serious>0?"Hi! I ran an automated accessibility check on our site. We have some serious issues to fix — nothing immediately critical, but real problems that affect real users.":"Hi! I ran an automated accessibility check on our site. The issues found are mostly minor or moderate — worth tidying up but not urgent.":a.totals.critical>0?"This report summarizes the accessibility findings for the page below. <strong>Critical-impact issues</strong> were detected — these are the categories that most commonly drive ADA-related lawsuits and should be prioritized.":a.totals.serious>0?"This report summarizes the accessibility findings for the page below. Serious issues were detected — real problems for real users, though none rise to the critical level.":"This report summarizes the accessibility findings for the page below. Findings are mostly minor or moderate — worth addressing during normal site maintenance.",h=t==="letter"?"Accessibility check":"Accessibility report";return`<!doctype html>
542
+ <html lang="en">
543
+ <head>
544
+ <meta charset="utf-8" />
545
+ <title>${w(h)} — ${w(r)}</title>
546
+ <style>
547
+ body { font: 15px/1.6 -apple-system, system-ui, "Segoe UI", sans-serif; color: #0f172a; max-width: 720px; margin: 24px auto; padding: 0 24px 60px; }
548
+ h1 { font-size: 22px; margin: 0 0 4px; }
549
+ h2 { font-size: 16px; margin-top: 28px; }
550
+ h3 { font-size: 15px; margin: 0; flex: 1; }
551
+ .meta { color: #64748b; font-size: 13px; margin-bottom: 18px; }
552
+ .summary { background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px; padding: 14px 18px; margin: 18px 0; }
553
+ .summary .totals { display: flex; gap: 14px; flex-wrap: wrap; margin-top: 8px; font-size: 13px; }
554
+ .summary .totals span { color: #64748b; }
555
+ .summary .totals strong { color: #0f172a; }
556
+ .rule { border: 1px solid #e2e8f0; border-radius: 6px; padding: 14px 18px; margin: 14px 0; background: white; }
557
+ .rule-header { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
558
+ .impact { font-size: 11px; padding: 2px 8px; border-radius: 4px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; }
559
+ .impact-critical { background: #fee2e2; color: #991b1b; }
560
+ .impact-serious { background: #ffedd5; color: #9a3412; }
561
+ .impact-moderate { background: #fef9c3; color: #854d0e; }
562
+ .impact-minor { background: #dbeafe; color: #1e40af; }
563
+ .rule p { margin: 8px 0; }
564
+ .where { font-size: 13px; color: #475569; }
565
+ .where code { background: #f1f5f9; padding: 1px 4px; border-radius: 3px; font-size: 12px; }
566
+ details.techy { margin-top: 10px; font-size: 12px; color: #64748b; }
567
+ details.techy summary { cursor: pointer; color: #475569; }
568
+ footer { margin-top: 40px; padding-top: 16px; border-top: 1px solid #e2e8f0; font-size: 12px; color: #94a3b8; }
569
+ @media print { body { max-width: none; margin: 0; padding: 0.5in; } }
570
+ </style>
571
+ </head>
572
+ <body>
573
+ <h1>${w(h)} for ${w(r)}</h1>
574
+ <p class="meta">${t==="letter"?"Ran":"Audit run"} ${w(s.toLocaleString())} · ${c.length} issue${c.length===1?"":"s"} grouped into ${l.size} ${l.size===1?"category":"categories"}</p>
575
+
576
+ <p>${u}</p>
577
+
578
+ <div class="summary">
579
+ <p style="margin: 0;"><strong>${w(cn[a.risk])}.</strong> ${w(ln[a.risk])}</p>
580
+ <div class="totals">
581
+ ${a.totals.critical>0?`<span><strong>${a.totals.critical}</strong> critical</span>`:""}
582
+ ${a.totals.serious>0?`<span><strong>${a.totals.serious}</strong> serious</span>`:""}
583
+ ${a.totals.moderate>0?`<span><strong>${a.totals.moderate}</strong> moderate</span>`:""}
584
+ ${a.totals.minor>0?`<span><strong>${a.totals.minor}</strong> minor</span>`:""}
585
+ ${a.totals.unique===0?"<span>No automated issues detected — manual review still recommended.</span>":""}
586
+ </div>
587
+ </div>
588
+
589
+ <h2>${t==="letter"?"What I found":"Detailed findings"}</h2>
590
+ ${d.length>0?d:"<p>No automated issues detected on this page.</p>"}
591
+
592
+ ${t==="letter"?`
593
+ <h2>What I&apos;d like you to do</h2>
594
+ <ol>
595
+ <li>Take a look at the items above and let me know which ones you can address.</li>
596
+ <li>For the ones marked <strong>critical</strong> — those tend to be the issues that show up in ADA-related lawsuit complaints, so I&apos;d prioritize those if you can.</li>
597
+ <li>If anything is unclear or you need more detail, the technical references inside each section link to the full rule documentation. The whole audit was generated by an automated tool that runs the same checks Deque, IBM, and Microsoft all use.</li>
598
+ </ol>
599
+
600
+ <p>Thanks for taking a look!</p>
601
+ `:`
602
+ <h2>Recommended next steps</h2>
603
+ <ol>
604
+ <li>Address <strong>critical</strong> findings first — these are the categories most commonly cited in ADA-related accessibility lawsuits.</li>
605
+ <li>Address <strong>serious</strong> findings during the next site update cycle.</li>
606
+ <li>Save or print this report for your records. It documents an automated audit was performed on ${w(s.toLocaleDateString())}.</li>
607
+ <li>Re-run the audit after fixes to verify resolution and catch any regressions.</li>
608
+ </ol>
609
+ `}
610
+
611
+ <footer>
612
+ <p>Generated by ${w(ae)} on ${w(s.toISOString())}.
613
+ Automated audits typically catch around 30–50% of accessibility issues; some categories require manual review.</p>
614
+ </footer>
615
+ </body>
616
+ </html>`}function Ir(e){if(!e||e.length===0)return`
617
+ <h2 id="manual">5. Manual assessment results</h2>
618
+ <p style="font-size: 10pt; color: #475569;">
619
+ No manual workflows were completed for this audit. Automated audits cover ~30–50% of WCAG;
620
+ the Guided Tests workflows in the side panel cover the remainder (keyboard operability beyond
621
+ static checks, screen-reader semantics, focus management, forms behavior, page structure,
622
+ visual indicators, reflow / text spacing, hover/focus content, pointer interactions, time-based
623
+ behaviors, error prevention, consistency).
624
+ When manual workflows are completed, their results are integrated into both the executive
625
+ summary's compliance grade and this section's per-criterion findings.
626
+ </p>`;const t=new Map(e.map(c=>[c.workflowId,c])),n=_e.map(c=>Cr(c,t.get(c.id))).filter(c=>c!==null).join("");let a=0,r=0,s=0,i=0;for(const c of _e){i+=c.steps.length;const l=t.get(c.id);if(l)for(const d of c.steps){const u=l.steps[d.id];u&&(s++,u.status==="fail"&&(d.severity==="required"?a++:r++))}}return`
627
+ <h2 id="manual">5. Manual assessment results</h2>
628
+ <p style="font-size: 10pt; color: #475569; margin-bottom: 8pt;">
629
+ Manual workflows performed by the auditor. ${s} of ${i} steps answered
630
+ across ${e.length} workflow${e.length===1?"":"s"}.
631
+ ${a>0?`<strong style="color: #b91c1c;">${a} required step${a===1?"":"s"} failed</strong> — each is a confirmed WCAG breach contributing to the compliance grade above.`:"No required steps failed."}
632
+ ${r>0?` ${r} advisory step${r===1?"":"s"} failed (best-practice findings).`:""}
633
+ </p>
634
+ ${n}`}function Cr(e,t){if(!t)return null;const n=[];for(const d of e.steps){const u=t.steps[d.id];u&&n.push({step:d,status:u.status,notes:u.notes})}if(n.length===0)return null;const a=n.filter(d=>d.status==="pass").length,r=n.filter(d=>d.status==="fail").length,s=n.filter(d=>d.status==="skip").length,i=n.filter(d=>d.status==="not-applicable").length,l=n.filter(d=>d.status==="fail").map(d=>`
635
+ <tr>
636
+ <td><span class="impact impact-${d.step.severity==="required"?"serious":"moderate"}">${w(d.step.severity)}</span></td>
637
+ <td>${d.step.wcag?`<code>${w(d.step.wcag)}</code>`:"—"}</td>
638
+ <td><strong>${w(d.step.question)}</strong>${d.notes?`<br/><span style="font-size: 9pt; color: #475569;">Auditor notes: ${w(d.notes)}</span>`:""}</td>
639
+ </tr>
640
+ `).join("");return`
641
+ <h3>${w(e.name)}</h3>
642
+ <p style="font-size: 10pt; color: #475569; margin: 0 0 6pt;">${w(e.blurb)}</p>
643
+ <p style="font-size: 10pt; margin: 0 0 6pt;">
644
+ <span class="cat-pass">${a} passed</span>
645
+ ${r>0?` · <span class="cat-fail">${r} failed</span>`:""}
646
+ ${s>0?` · <span style="color: #64748b;">${s} skipped</span>`:""}
647
+ ${i>0?` · <span style="color: #64748b;">${i} N/A</span>`:""}
648
+ · ${n.length} of ${e.steps.length} answered
649
+ </p>
650
+ ${r>0?`
651
+ <table>
652
+ <thead><tr><th style="width: 80pt;">Severity</th><th style="width: 70pt;">WCAG</th><th>Failed check + auditor notes</th></tr></thead>
653
+ <tbody>${l}</tbody>
654
+ </table>
655
+ `:""}`}function Rn(e,t,n="defense",a,r,s){var x,_,j;const i=kr[n],c=((x=e[0])==null?void 0:x.componentId)??"unknown",l=ie(e),d=e.reduce((b,C)=>b+C.durationMs,0),u=e.flatMap(b=>b.violations),h=a&&a.length>0?{runs:a,workflows:_e}:void 0,p=fe(u,h,e),f=new Date,g=new Map;for(const b of u){const C=Oe(b.ruleId,b.target.selector),R=`${b.currentState.pseudoState} · ${b.currentState.theme} · ${b.currentState.direction}`,S=g.get(C);if(S){S._states.includes(R)||S._states.push(R);continue}g.set(C,{...b,_states:[R]})}const o=Array.from(g.values()).sort((b,C)=>{const R={critical:0,serious:1,moderate:2,minor:3};return(R[b.impact]??99)-(R[C.impact]??99)}),m=br(e),k=new Map;for(const b of m)for(const C of b.violations){const R=Oe(C.ruleId,C.target.selector);k.has(R)||k.set(R,C._idx)}const v=o.map((b,C)=>`
656
+ <tr>
657
+ <td>${C+1}</td>
658
+ <td><span class="impact impact-${w(b.impact)}">${w(b.impact)}</span></td>
659
+ <td><code>${w(b.ruleId)}</code></td>
660
+ <td>${w(b.wcagCriterion)} ${w(b.wcagLevel)}</td>
661
+ <td>${w(b.description)}</td>
662
+ <td><code class="sel">${w(b.target.selector)}</code></td>
663
+ <td>${w(b._states.join(", "))}</td>
664
+ </tr>`).join(""),I=new Set(u.map(b=>b.ruleId)),$=new Set(I);for(const b of e){const C=b.axeRulesEvaluated;if(C){for(const R of C.passed)$.add(R.ruleId);for(const R of C.inapplicable)$.add(R.ruleId);for(const R of C.incomplete)$.add(R.ruleId)}}const N=Tn.map(b=>{const{conformance:C,hits:R}=In(b,I,$);return`
665
+ <tr class="${C==="Supports"?"supports":C==="Partially Supports"?"partial":C==="Not Evaluated"?"not-evaluated":"na"}">
666
+ <td><code>${w(b.ref)}</code></td>
667
+ <td>${w(b.title)} <span class="lvl">(${b.level})</span></td>
668
+ <td>${C}</td>
669
+ <td>${R.length>0?`Detected violations of: ${R.map(A=>`<code>${w(A)}</code>`).join(", ")}`:C==="Not Applicable"?"Not addressable by automated audit; requires manual review.":"No automated violations detected."}</td>
670
+ </tr>`}).join("");return`<!doctype html>
671
+ <html lang="en">
672
+ <head>
673
+ <meta charset="utf-8" />
674
+ <title>${w(i.title)} — ${w(c)}</title>
675
+ <style>
676
+ *, *::before, *::after { box-sizing: border-box; }
677
+ body { font: 11pt/1.55 -apple-system, system-ui, "Segoe UI", sans-serif; color: #0f172a; max-width: 8in; margin: 0.5in auto; padding: 0 0.25in; }
678
+ h1 { font-size: 22pt; margin: 0 0 4pt; }
679
+ h2 { font-size: 14pt; margin: 24pt 0 8pt; padding-bottom: 4pt; border-bottom: 1px solid #cbd5e1; page-break-after: avoid; }
680
+ h3 { font-size: 12pt; margin: 14pt 0 6pt; page-break-after: avoid; }
681
+ .doc-meta { font-size: 10pt; color: #475569; margin-bottom: 12pt; }
682
+ .toc { background: #f8fafc; border: 1px solid #e2e8f0; padding: 10pt 14pt; border-radius: 4pt; margin: 14pt 0; }
683
+ .toc ol { margin: 4pt 0 0; padding-left: 20pt; }
684
+ .toc a { color: #1e40af; text-decoration: none; }
685
+ .exec { border: 1px solid #cbd5e1; border-radius: 4pt; overflow: hidden; margin: 12pt 0; display: flex; }
686
+ .exec-grade { width: 1.4in; display: flex; align-items: center; justify-content: center; color: white; font-size: 56pt; font-weight: 700; line-height: 1; }
687
+ .exec-body { flex: 1; padding: 14pt 18pt; }
688
+ .risk-tier { display: inline-block; padding: 2pt 10pt; border-radius: 12pt; font-size: 10pt; font-weight: 600; margin-bottom: 6pt; }
689
+ .categories { display: grid; grid-template-columns: 1fr 1fr; gap: 4pt 16pt; margin: 8pt 0; font-size: 10pt; }
690
+ .cat-pass { color: #15803d; }
691
+ .cat-fail { color: #b91c1c; font-weight: 600; }
692
+ .cat-unchecked { color: #b45309; }
693
+ table { width: 100%; border-collapse: collapse; margin: 6pt 0 10pt; font-size: 9.5pt; }
694
+ th, td { border: 1px solid #cbd5e1; padding: 4pt 7pt; text-align: left; vertical-align: top; }
695
+ th { background: #f1f5f9; }
696
+ td.sel, code { font-family: "SF Mono", Menlo, monospace; font-size: 9pt; word-break: break-all; }
697
+ .impact { font-size: 8.5pt; padding: 1pt 5pt; border-radius: 2pt; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; }
698
+ .impact-critical { background: #fee2e2; color: #991b1b; }
699
+ .impact-serious { background: #ffedd5; color: #9a3412; }
700
+ .impact-moderate { background: #fef9c3; color: #854d0e; }
701
+ .impact-minor { background: #dbeafe; color: #1e40af; }
702
+ tr.supports td:nth-child(3) { color: #15803d; font-weight: 600; }
703
+ tr.partial td:nth-child(3) { color: #b45309; font-weight: 600; }
704
+ tr.na td:nth-child(3) { color: #64748b; }
705
+ .lvl { color: #94a3b8; font-weight: 400; font-size: 9pt; }
706
+ .disclaimer { background: #fef3c7; border: 1px solid #f59e0b; padding: 10pt 14pt; border-radius: 3pt; margin: 16pt 0; font-size: 10pt; }
707
+ .evidence-state { margin: 12pt 0 18pt; page-break-inside: avoid; }
708
+ .evidence-state h3 { font-size: 11pt; margin-bottom: 4pt; }
709
+ .evidence-state .caption { font-size: 9.5pt; color: #475569; margin-bottom: 4pt; }
710
+ .evidence-frame { display: block; width: 100%; border: 1px solid #cbd5e1; }
711
+ .evidence-frame svg { display: block; width: 100%; height: auto; }
712
+ .evidence-frame svg rect.violation-mark { fill: rgba(220, 38, 38, 0.10); stroke: #dc2626; stroke-width: 3; }
713
+ .evidence-frame svg text.violation-num { font: bold 14px ui-monospace, Menlo, monospace; fill: white; }
714
+ .evidence-frame svg rect.label-bg { fill: #dc2626; }
715
+ .methodology { font-size: 10pt; }
716
+ .methodology dt { font-weight: 600; margin-top: 6pt; }
717
+ .methodology dd { margin: 0 0 4pt 0; }
718
+ footer { margin-top: 28pt; padding-top: 8pt; border-top: 1px solid #cbd5e1; font-size: 8.5pt; color: #64748b; }
719
+ @media print { body { margin: 0; max-width: none; } .toc { page-break-after: always; } table, tr { page-break-inside: avoid; } }
720
+ </style>
721
+ </head>
722
+ <body>
723
+
724
+ <h1>${w(i.title)}</h1>
725
+ <p class="doc-meta">
726
+ <strong>Subject:</strong> <code>${w(c)}</code> &nbsp;·&nbsp;
727
+ <strong>URL:</strong> ${w(l)}<br/>
728
+ <strong>Audit date:</strong> ${w(f.toLocaleString())} &nbsp;·&nbsp;
729
+ <strong>Audit engine:</strong> axe-core ${w(((_=e[0])==null?void 0:_.axeVersion)??"n/a")} &nbsp;·&nbsp;
730
+ <strong>Tool:</strong> ${w(ae)} v${w(he())}
731
+ </p>
732
+
733
+ <nav class="toc">
734
+ <strong>Contents</strong>
735
+ <ol>
736
+ <li><a href="#exec">Executive summary</a></li>
737
+ <li><a href="#methodology">Methodology &amp; scope</a></li>
738
+ <li><a href="#findings">Detailed findings (automated)</a></li>
739
+ <li><a href="#evidence">Visual evidence (per-state screenshots)</a></li>
740
+ <li><a href="#manual">Manual assessment results</a></li>
741
+ <li><a href="#conformance">WCAG 2.1 conformance assessment</a></li>
742
+ <li><a href="#remediation">${n==="evidence"?"Recommended approach before legal action":"Remediation plan"}</a></li>
743
+ <li><a href="#disclaimer">Disclaimer &amp; limitations</a></li>
744
+ </ol>
745
+ </nav>
746
+
747
+ <h2 id="exec">1. Executive summary</h2>
748
+ <div class="exec">
749
+ <div class="exec-grade" style="background: ${Sr[p.letter]};">${p.letter}</div>
750
+ <div class="exec-body">
751
+ <span class="risk-tier" style="background: ${vr[p.risk]}; color: ${Ar[p.risk]};">
752
+ ${w(wr[p.risk])}
753
+ </span>
754
+ <p style="margin: 0 0 6pt;"><strong>${p.totals.unique} unique violations detected</strong> across ${e.length} state combinations
755
+ (${p.totals.critical} critical · ${p.totals.serious} serious · ${p.totals.moderate} moderate · ${p.totals.minor} minor).</p>
756
+ <p style="margin: 0; font-size: 10pt; color: #475569;">${w(ga[p.risk])}</p>
757
+ </div>
758
+ </div>
759
+ ${s&&(s.lead||s.body||s.closer)?`
760
+ <div style="margin-top: 12pt; padding: 12pt 14pt; border-left: 4px solid #2563eb; background: #f0f6ff; border-radius: 0 4pt 4pt 0;">
761
+ <p style="margin: 0 0 6pt; font-size: 10pt; color: #1e40af; text-transform: uppercase; letter-spacing: 0.04em;">
762
+ AI-generated narrative summary (${w(s.model)})
763
+ </p>
764
+ ${s.lead?`<p style="margin: 0 0 8pt; font-size: 11.5pt; font-weight: 500;">${w(s.lead)}</p>`:""}
765
+ ${s.body?s.body.split(/\n{2,}/).map(b=>`<p style="margin: 0 0 6pt;">${w(b.trim())}</p>`).join(`
766
+ `):""}
767
+ ${s.closer?`<p style="margin: 8pt 0 0; font-style: italic;">${w(s.closer)}</p>`:""}
768
+ <p style="margin: 6pt 0 0; font-size: 9pt; color: #475569;">
769
+ This narrative was generated by an LLM from the audit's structured findings. The numerical
770
+ data in §1 above and the per-violation details in §3 are the authoritative record.
771
+ </p>
772
+ </div>`:""}
773
+
774
+ <h3>Per-category breakdown (top-6 lawsuit-magnet categories)</h3>
775
+ <div class="categories">
776
+ ${p.categories.map(b=>{const C=b.status==="pass"?"✓":b.status==="fail"?"✗":"⚠",R=b.status==="pass"?"cat-pass":b.status==="fail"?"cat-fail":"cat-unchecked",S=b.status==="fail"?` (${b.violationCount})`:b.needsManualCheck?" — requires manual review":"";return`<div class="${R}">${C} ${w(b.label)}${w(S)}</div>`}).join("")}
777
+ </div>
778
+
779
+ <h2 id="methodology">2. Methodology &amp; scope</h2>
780
+ <dl class="methodology">
781
+ <dt>Audit engine</dt>
782
+ <dd>axe-core ${w(((j=e[0])==null?void 0:j.axeVersion)??"n/a")} (industry-standard, used by Deque, IBM, Microsoft, and Google's Lighthouse).
783
+ Rule sets enabled: WCAG 2.0 A/AA, WCAG 2.1 A/AA, WCAG 2.2 A/AA, axe-core best-practice.</dd>
784
+ <dt>State coverage</dt>
785
+ <dd>${e.length} state combinations were audited, including pseudo-states (<code>:hover</code>, <code>:focus</code>, <code>:focus-visible</code>, <code>:active</code>, <code>:disabled</code>),
786
+ color-scheme themes (<code>prefers-color-scheme: dark</code>, <code>forced-colors: active</code>),
787
+ text direction (LTR / RTL), and viewport breakpoints (mobile / tablet / desktop).
788
+ Audits at non-default states are uncommon among automated tools and represent significant additional WCAG coverage relative to single-state audits.</dd>
789
+ <dt>Audit duration</dt>
790
+ <dd>${(d/1e3).toFixed(1)} seconds across ${e.length} states (${(d/Math.max(1,e.length)).toFixed(0)}ms per state).</dd>
791
+ <dt>Scope</dt>
792
+ <dd>Component / region: <code>${w(c)}</code></dd>
793
+ <dt>What automated audit covers</dt>
794
+ <dd>Approximately 30–50% of WCAG success criteria are addressable by automated testing. Categories addressable here: color contrast, image alt text, form labels, accessible names for buttons / links, document structure, ID uniqueness, ARIA validity.</dd>
795
+ <dt>What requires manual evaluation</dt>
796
+ <dd>Keyboard operability beyond static checks (focus traps, focus restoration after dismissal), screen-reader semantic correctness, color use beyond contrast, content meaning (alt text accuracy), and cognitive accessibility. The ${w(ae)} Guided Tests workflows provide a structured manual-audit path for these areas.</dd>
797
+ </dl>
798
+
799
+ <h2 id="findings">3. Detailed findings</h2>
800
+ ${o.length===0?"<p>No automated violations were detected during this audit.</p>":`<table>
801
+ <thead>
802
+ <tr><th>#</th><th>Impact</th><th>Rule</th><th>WCAG</th><th>Description</th><th>Selector</th><th>States</th></tr>
803
+ </thead>
804
+ <tbody>${v}</tbody>
805
+ </table>`}
806
+
807
+ <h2 id="evidence">4. Visual evidence (per-state screenshots)</h2>
808
+ ${m.length===0?'<p style="font-size: 10pt; color: #64748b;">No per-state screenshots were captured for this audit (no states had violations, or screenshot capture failed).</p>':`<p class="caption">Top ${m.length} state${m.length===1?"":"s"} by violation count shown below. Red rectangles mark violation locations; numbers correspond to entries in §3.</p>`+m.map(b=>{const C=b.violations.filter(R=>R.target.boundingRect).map(R=>{const S=R.target.boundingRect,A=String(R._idx).length*8+14,T=22;return[`<rect class="violation-mark" x="${S.x}" y="${S.y}" width="${S.w}" height="${S.h}" />`,`<rect class="label-bg" x="${S.x}" y="${Math.max(0,S.y-T)}" width="${A}" height="${T}" rx="2" />`,`<text class="violation-num" x="${S.x+6}" y="${Math.max(0,S.y-T)+16}">${R._idx}</text>`].join("")}).join("");return`
809
+ <div class="evidence-state">
810
+ <h3>State: ${w(b.stateLabel)} (${b.violations.length} violation${b.violations.length===1?"":"s"})</h3>
811
+ <div class="evidence-frame">
812
+ <svg viewBox="0 0 ${zt} ${Bt}" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet">
813
+ <image href="${b.screenshotDataUrl}" x="0" y="0" width="${zt}" height="${Bt}" preserveAspectRatio="xMidYMid slice" />
814
+ ${C}
815
+ </svg>
816
+ </div>
817
+ </div>`}).join("")}
818
+
819
+ ${Ir(a)}
820
+
821
+ <h2 id="conformance">6. WCAG 2.1 conformance assessment</h2>
822
+ <p style="font-size: 10pt;">Per-criterion conformance based on automated detection. Conformance values follow the VPAT 2.5-INT vocabulary.${t&&t.baselineSnapshotMeta?" Comparison against an accepted baseline is available; see Section 6.":""}</p>
823
+ <table>
824
+ <thead><tr><th>Criterion</th><th>Title</th><th>Conformance</th><th>Notes</th></tr></thead>
825
+ <tbody>${N}</tbody>
826
+ </table>
827
+
828
+ ${(()=>{const b=r!==void 0,C=b?"8":"7",R=b?"9":"8",S=b?`<h2 id="audit-history">7. Audit history</h2>
829
+ <p style="font-size: 10pt; margin-bottom: 8pt;">Chronological record of audits this user has run, with cryptographic hashes of each audit's identifying fields (component, page URL, grade, severity totals, axe version, timestamp, state-matrix size). Hashes are SHA-256 of canonical-JSON serialization, so any tampering with a logged audit's identity is detectable client-side. Entries marked <em>Anchored</em> additionally carry a third-party RFC 3161 trusted timestamp issued by the named TSA — those entries cannot be backdated.</p>
830
+ ${r&&r.length>0?`<table>
831
+ <thead>
832
+ <tr>
833
+ <th>Captured</th>
834
+ <th>Component</th>
835
+ <th>Page</th>
836
+ <th>Grade</th>
837
+ <th>Crit</th>
838
+ <th>Sev</th>
839
+ <th>States</th>
840
+ <th>Hash (first 12)</th>
841
+ <th>Source</th>
842
+ </tr>
843
+ </thead>
844
+ <tbody>${r.map(T=>`
845
+ <tr>
846
+ <td style="white-space: nowrap;">${w(T.capturedAt)}</td>
847
+ <td><code>${w(T.componentId)}</code></td>
848
+ <td style="word-break: break-all;">${w(T.pageUrl)}</td>
849
+ <td><strong>${w(T.grade)}</strong></td>
850
+ <td>${T.totals.critical}</td>
851
+ <td>${T.totals.serious}</td>
852
+ <td>${T.statesAudited}</td>
853
+ <td><code title="${w(T.hash)}">${w(T.hash.slice(0,12))}…</code></td>
854
+ <td>${T.receipt?`RFC 3161 (${w(T.receipt.tsaName)})`:"Local"}</td>
855
+ </tr>`).join("")}
856
+ </tbody>
857
+ </table>`:'<p style="color: #64748b; font-style: italic;">No prior audits recorded — this is the first audit on record for this installation.</p>'}
858
+ `:"",A=n==="evidence"?`<h2 id="remediation">${C}. Recommended approach before legal action</h2>
859
+ <div style="background: #f0f9ff; border: 1px solid #38bdf8; padding: 12pt 16pt; border-radius: 4pt; font-size: 10.5pt;">
860
+ <p style="margin: 0 0 8pt;"><strong>Most businesses are unaware their digital properties have accessibility defects.</strong> Many will fix them once notified. A good-faith notice-and-cure approach is more likely to resolve the underlying accessibility barrier (which is the actual purpose of disability-rights law) and, where remediation does not occur, strengthens any subsequent legal claim by establishing both notice and a reasonable opportunity to comply.</p>
861
+ <p style="margin: 0 0 8pt;"><strong>Recommended steps before considering legal action:</strong></p>
862
+ <ol style="margin: 0 0 8pt; padding-left: 20pt;">
863
+ <li><strong>Send written notice to the site owner / business operator.</strong> Include this evidence bundle as an attachment. Identify yourself, the date you encountered the barrier, the specific access need affected (e.g., screen-reader use, keyboard-only navigation, low-vision contrast), and a reasonable timeframe for response (commonly 30–60 days, depending on jurisdiction and severity).</li>
864
+ <li><strong>Offer to discuss remediation.</strong> Many businesses don't have an accessibility lead — pointing them at the WCAG criteria in §5 of this document and the per-rule canonical fixes (axe-core's documentation, linked from each violation's "More info") gives them a starting point.</li>
865
+ <li><strong>Document the response (or lack of one).</strong> Save the original notice, any reply, dates, and any remediation activity (or absence of it).</li>
866
+ <li><strong>If after the notice period the business has not engaged or remediated</strong>, the documented good-faith notice combined with this evidence bundle materially strengthens an ADA Title III, EAA, EN 301 549, AODA, or analogous claim, depending on jurisdiction.</li>
867
+ </ol>
868
+ <p style="margin: 0; font-size: 9.5pt; color: #475569;">This guidance is general and not legal advice. Specific procedural requirements vary by jurisdiction (e.g., some US states require pre-suit notice under "Unruh Act" amendments; others do not). Consult qualified counsel about the procedural posture appropriate for your situation.</p>
869
+ </div>`:`<h2 id="remediation">${C}. Remediation plan</h2>
870
+ <p>Each violation in §3 has a documented canonical fix pattern. The remediation workflow we recommend:</p>
871
+ <ol>
872
+ <li>Findings are classified by severity. Address critical and serious findings first.</li>
873
+ <li>For each rule, ${w(ae)} produces a structured fix prompt that can be processed by a code-fluent AI assistant operating against the codebase. The fix prompt includes per-violation context (selector, state, recipe), pre-flight safety checks (clean git working tree, atomic commits per fix), and explicit constraints (no scope creep, native HTML preferred over ARIA, fix state-specific failures in state-specific styles).</li>
874
+ <li>After remediation, re-run the audit to verify each violation is resolved without introducing regressions.</li>
875
+ <li>Manual workflows (keyboard, screen-reader, focus-management, forms) should be re-completed via the Guided Tests panel.</li>
876
+ <li>Once resolved, the audited state should be accepted as a baseline; future audits surface only NEW debt vs. that baseline.</li>
877
+ </ol>`;return`${S}
878
+
879
+ ${A}
880
+
881
+ <h2 id="disclaimer">${R}. Disclaimer &amp; limitations</h2>`})()}
882
+ <div class="disclaimer">
883
+ <p style="margin: 0 0 6pt;"><strong>${w(i.disclaimerHeading)}</strong></p>
884
+ <p style="margin: 0 0 6pt;">${i.disclaimerBody}</p>
885
+ <p style="margin: 0;">This document represents detected violations at the time and scope of audit. Subsequent changes to the audited resource may produce different results. The presence or absence of detected violations in this report is not, by itself, ${n==="defense"?"a defense against":"a determination of"} any specific accessibility claim, regulation, or legal action. This document is provided for ${w(i.documentLabel)} purposes.</p>
886
+ </div>
887
+
888
+ <footer>
889
+ Generated by ${w(ae)} v${w(he())} on ${w(f.toISOString())}.
890
+ Audit run identifier: ${w(f.getTime().toString(36))}-${w(c.replace(/[^a-z0-9]/gi,"").slice(0,8))}.
891
+ </footer>
892
+
893
+ </body>
894
+ </html>`}async function Rr(){const t=(await chrome.storage.local.get("aiConfig")).aiConfig;return{...Ea,...t??{}}}async function Ht(e,t,n){const a=await Rr();if(!a.enabled||!a.apiKey||e.length===0)return null;const r=e.flatMap(l=>l.violations),s=fe(r,void 0,e),i=s.categories.filter(l=>l.status==="fail").sort((l,d)=>d.violationCount-l.violationCount).slice(0,3).map(l=>l.label),c=e[0];return En({framing:t,riskTier:s.risk,grade:s.letter,totals:{critical:s.totals.critical,serious:s.totals.serious,moderate:s.totals.moderate,minor:s.totals.minor,unique:s.totals.unique},riskDrivers:i,pageUrl:(c==null?void 0:c.pageUrl)??(c==null?void 0:c.scope)??"",auditDate:(c==null?void 0:c.startedAt)??new Date().toISOString(),priorAuditCount:n},a)}function Or(){return W("EXPORT_REQUEST",async e=>{var t;if(e.format==="sarif")return{type:"EXPORT_RESPONSE",format:"sarif",content:JSON.stringify(dr(e.results,e.delta),null,2)};if(e.format==="junit")return{type:"EXPORT_RESPONSE",format:"junit",content:ur(e.results,e.delta)};if(e.format==="html-print")return{type:"EXPORT_RESPONSE",format:"html-print",content:pr(e.results,e.delta)};if(e.format==="vpat")return{type:"EXPORT_RESPONSE",format:"vpat",content:hr(e.results,e.delta)};if(e.format==="ai-prompt"){const n=await pa();let a="";const r=[];try{const i=((t=e.results[0])==null?void 0:t.componentId)??"",c=i?await ha(i):[];for(const l of c)r.push({criterionId:l.criterionId,verdict:l.verdict,reasoning:l.reasoning,pageUrl:l.pageUrl});a=_r(c)}catch(i){console.warn("[report-exporter] walkthrough render skipped:",i)}const s=mr(e.results,e.delta,e.manualRuns,e.siteCrawlReport,e.dismissedKeys,e.incompleteResolutions,n,r);return{type:"EXPORT_RESPONSE",format:"ai-prompt",content:a?`${s}
895
+
896
+ ${a}`:s}}if(e.format==="defense-bundle"){const n=await Ut(),a=await Ht(e.results,"defense",n.length);return{type:"EXPORT_RESPONSE",format:"defense-bundle",content:Rn(e.results,e.delta,"defense",e.manualRuns,n,a)}}if(e.format==="evidence-bundle"){const n=await Ut(),a=await Ht(e.results,"evidence",n.length);return{type:"EXPORT_RESPONSE",format:"evidence-bundle",content:xr(e.results,e.delta,e.manualRuns,n,a)}}return e.format==="developer-letter"?{type:"EXPORT_RESPONSE",format:"developer-letter",content:Er(e.results)}:e.format==="owner-report"?{type:"EXPORT_RESPONSE",format:"owner-report",content:$r(e.results)}:e.format==="ai-prompt-owner"?{type:"EXPORT_RESPONSE",format:"ai-prompt-owner",content:Tr(e.results)}:{type:"EXPORT_RESPONSE",format:"json",content:JSON.stringify(lr(e.results,e.delta),null,2)}})}function _r(e){if(!Array.isArray(e)||e.length===0)return"";const t=[];t.push("## AI keyboard walkthroughs — interactive WCAG audits"),t.push(""),t.push("The audit ran AI-driven keyboard walkthroughs against the page. Each one Tab-walked the live DOM and AI judged the result against a specific WCAG criterion. Fail verdicts here are NOT heuristic guesses — AI watched the actual focus path and identified a concrete problem. Treat each fail as a required fix."),t.push("");for(const n of e)if(!(!n||typeof n!="object"))try{const a=n.verdict==="pass"||n.verdict==="fail"?n.verdict:"uncertain",r=a==="pass"?"✓ PASS":a==="fail"?"✗ FAIL":"? UNCERTAIN",s=String(n.criterionId??"?"),i=s==="2.4.3"?"Focus Order (Level A)":s==="2.1.2"?"No Keyboard Trap (Level A)":s==="2.4.7"?"Focus Visible (Level AA)":s,c=typeof n.confidence=="number"?Math.max(0,Math.min(1,n.confidence)):0,l=typeof n.reasoning=="string"?n.reasoning:"",d=typeof n.model=="string"?n.model:"unknown";t.push(`### WCAG ${s} — ${i} — ${r}`),t.push(""),t.push(`**AI reasoning** (${Math.round(c*100)}% confidence, model ${d}):`),t.push(""),t.push(l?`> ${l.replace(/\n/g,`
897
+ > `)}`:"> (no reasoning recorded)"),t.push(""),a==="fail"?(s==="2.4.3"?t.push("**Fix this:** Reorder DOM source so the natural tab sequence matches the visual reading order, or assign `tabindex` only where you intentionally diverge from source order (and document the reason). Avoid positive `tabindex` values. The tab sequence the AI walked is below — fix every element whose position is out of order vs. its visible position on the page."):s==="2.1.2"?t.push("**Fix this:** Identify any region where focus gets stuck. Common offenders: modal dialogs that intercept Tab without an escape route (add Escape handler + restore focus on close), custom widgets that loop within their own focus trap without an exit, contenteditable elements consuming Tab as literal input. Ensure forward Tab AND backward Shift+Tab can always reach the rest of the page from any focused element."):s==="2.4.7"&&t.push("**Fix this:** Every focusable element must show a visible focus indicator. If you use `outline: none` (e.g., for design reasons), you MUST compensate with a `:focus-visible` box-shadow, border, or background change that has at least 3:1 contrast against the unfocused state. Check every selector in the recorded walk below."),t.push("")):a==="pass"?(t.push("_AI walked the criterion and found no issues. No action needed; this is positive evidence the criterion is met._"),t.push("")):(t.push("_AI could not confidently decide. Manually verify or re-run the walkthrough after rendering changes settle._"),t.push(""));const h=(Array.isArray(n.steps)?n.steps:[]).filter(p=>p&&p.action!=="initial");if(h.length>0){t.push(`<details><summary>Recorded ${h.length}-step keyboard walkthrough</summary>`),t.push(""),t.push("| # | Action | Selector | Role | Accessible name |"),t.push("|---|---|---|---|---|");for(const p of h.slice(0,30)){const f=p.focused??{selector:"?",role:"",name:""},g=String(f.selector??"?"),o=String(f.role??"-")||"-",m=String(f.name??"(no name)").replace(/\|/g,"\\|").slice(0,60),k=String(p.action??""),v=typeof p.ordinal=="number"?p.ordinal:"?";t.push(`| ${v} | ${k} | \`${g}\` | ${o} | ${m} |`)}h.length>30&&t.push(`| … | ${h.length-30} more steps elided | | | |`),t.push(""),t.push("</details>"),t.push("")}}catch(a){console.warn("[report-exporter] walkthrough render skipped:",a)}return t.push("---"),t.join(`
898
+ `)}const Mr=D("support-messenger"),Ur="https://api.wcagcheckr.com/v1/products/wcagcheckr/support",it="support:rate-window",Nr=5,Dr=10*60*1e3,Pr=2,Lr="wcagcheckr",qt="storybook:lastDetected";async function Fr(e){if(!e)return;const t=await chrome.runtime.getPlatformInfo(),a=(await chrome.storage.local.get(qt))[qt];return{extensionVersion:chrome.runtime.getManifest().version,platform:t,storybookDetected:a!=null&&a.detected?a.version??"detected":"none",licenseTier:await dn(),logTail:aa(oa())}}async function Gr(e=Date.now()){const n=((await chrome.storage.local.get(it))[it]??[]).filter(a=>e-a<Dr);return n.length>=Nr?!1:(n.push(e),await chrome.storage.local.set({[it]:n}),!0)}async function On(e,t=0){try{const n=await fetch(Ur,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!n.ok)throw new Error(`http ${n.status}`);const a=await n.json();if(!a.success)throw new Error(a.error??"server returned success=false");return{ticketRef:a.ticketRef??""}}catch(n){if(t<Pr){const a=(t+1)*1e3;return await new Promise(r=>setTimeout(r,a)),On(e,t+1)}throw n}}function Vr(){return W("SUPPORT_MESSAGE_REQUEST",async e=>{if(!await Gr())return{type:"SUPPORT_MESSAGE_RESPONSE",success:!1,error:je("NETWORK","Rate limit reached. Try again in 10 minutes.",!0)};try{const t=await Fr(e.includeContext);return{type:"SUPPORT_MESSAGE_RESPONSE",success:!0,ticketRef:(await On({productSlug:Lr,subject:e.subject,body:e.body,replyEmail:e.replyEmail,context:t,extensionVersion:chrome.runtime.getManifest().version,timestamp:new Date().toISOString()})).ticketRef}}catch(t){Mr.error("send failed",t);const n=t instanceof Error?t.message:String(t);return{type:"SUPPORT_MESSAGE_RESPONSE",success:!1,error:je("NETWORK",n,!0)}}})}const _n=D("ai-color-suggester");async function Wr(e,t,n=[]){if(!t.enabled||e.length===0)return{results:[],totalCostUsd:0,capExceeded:!1};const a=q(t);if(!a.ok)return{results:[],totalCostUsd:0,capExceeded:!1};const r=a.client,s=ne(t.costCapUsd),i=[];for(const c of e){if(!s.canCharge())break;try{const l=await r.suggestColorFix({foreground:c.foreground,background:c.background,fontSize:c.fontSize,fontWeight:c.fontWeight,targetLevel:c.targetLevel,paletteHints:n});s.recordCharge(l.costUsd),l.verdict==="suggested"&&l.candidates.length>0&&i.push({matchKey:c.matchKey,suggestions:l.candidates,reasoning:l.reasoning,costUsd:l.costUsd})}catch(l){_n.warn(`color-fix suggestion failed for ${c.matchKey}`,l)}}return{results:i,totalCostUsd:s.state.spentUsd,capExceeded:s.state.exceeded}}async function jr(){const e=await chrome.storage.local.get("aiConfig");return oe(e.aiConfig)}function Kr(){return W("AI_COLOR_SUGGEST_REQUEST",async e=>{const t=await jr();if(!t.enabled)return{type:"AI_COLOR_SUGGEST_RESPONSE",results:[],totalCostUsd:0,capExceeded:!1,unavailableReason:"AI augmentation is disabled in Settings → AI augmentation."};if(!t.apiKey)return{type:"AI_COLOR_SUGGEST_RESPONSE",results:[],totalCostUsd:0,capExceeded:!1,unavailableReason:"No AI API key configured. Add one in Settings → AI augmentation."};try{const{results:n,totalCostUsd:a,capExceeded:r}=await Wr(e.inputs,t,e.paletteHints);return{type:"AI_COLOR_SUGGEST_RESPONSE",results:n,totalCostUsd:a,capExceeded:r}}catch(n){return _n.warn("color-suggest handler failed",n),{type:"AI_COLOR_SUGGEST_RESPONSE",results:[],totalCostUsd:0,capExceeded:!1,unavailableReason:n instanceof Error?n.message:String(n)}}})}const Mn=D("ai-incomplete-resolver");async function zr(){const e=await chrome.storage.local.get("aiConfig");return oe(e.aiConfig)}async function Br(e){if(!e.aiConfig.enabled||e.elements.length===0)return{resolutions:[],totalCostUsd:0};const t=q(e.aiConfig);if(!t.ok)return{resolutions:[],totalCostUsd:0};const n=t.client,a=ne(e.aiConfig.costCapUsd),r=[];for(const s of e.elements){if(!a.canCharge())break;try{const i=await n.resolveAxeIncomplete({ruleId:e.ruleId,element:s,pageUrl:e.pageUrl,targetLevel:e.targetLevel});a.recordCharge(i.costUsd),r.push({pageUrl:e.pageUrl,ruleId:e.ruleId,selector:s.selector,verdict:i.verdict,reasoning:i.reasoning,resolvedAt:new Date().toISOString(),costUsd:i.costUsd,wcagCriterion:e.wcagCriterion})}catch(i){Mn.warn(`resolveAxeIncomplete failed for ${s.selector}`,i)}}return r.length>0&&await ma(r),{resolutions:r,totalCostUsd:a.state.spentUsd}}function Hr(){return W("AI_RESOLVE_INCOMPLETE_REQUEST",async e=>{const t=await zr();if(!t.enabled)return{type:"AI_RESOLVE_INCOMPLETE_RESPONSE",resolutions:[],totalCostUsd:0,unavailableReason:"AI augmentation is disabled in Settings → AI augmentation."};if(!t.apiKey)return{type:"AI_RESOLVE_INCOMPLETE_RESPONSE",resolutions:[],totalCostUsd:0,unavailableReason:"No AI API key configured. Add one in Settings → AI augmentation."};try{const{resolutions:n,totalCostUsd:a}=await Br({ruleId:e.ruleId,pageUrl:e.pageUrl,wcagCriterion:e.wcagCriterion,elements:e.elements,targetLevel:e.targetLevel,aiConfig:t});return{type:"AI_RESOLVE_INCOMPLETE_RESPONSE",resolutions:n,totalCostUsd:a}}catch(n){return Mn.warn("ai-incomplete-resolver failed",n),{type:"AI_RESOLVE_INCOMPLETE_RESPONSE",resolutions:[],totalCostUsd:0,unavailableReason:n instanceof Error?n.message:"Unknown error during AI resolution."}}})}const Re=D("interactive-audit"),qr="1.3",Yr={Tab:{key:"Tab",code:"Tab",windowsVirtualKeyCode:9},Enter:{key:"Enter",code:"Enter",windowsVirtualKeyCode:13},Escape:{key:"Escape",code:"Escape",windowsVirtualKeyCode:27},Space:{key:" ",code:"Space",windowsVirtualKeyCode:32},ArrowLeft:{key:"ArrowLeft",code:"ArrowLeft",windowsVirtualKeyCode:37},ArrowUp:{key:"ArrowUp",code:"ArrowUp",windowsVirtualKeyCode:38},ArrowRight:{key:"ArrowRight",code:"ArrowRight",windowsVirtualKeyCode:39},ArrowDown:{key:"ArrowDown",code:"ArrowDown",windowsVirtualKeyCode:40},Home:{key:"Home",code:"Home",windowsVirtualKeyCode:36},End:{key:"End",code:"End",windowsVirtualKeyCode:35}};class $t{constructor(t){Fe(this,"session");Fe(this,"attached",!1);Fe(this,"steps",[]);this.tabId=t,this.session=new pn({tabId:t})}async attach(){if(!this.attached)try{await chrome.debugger.attach({tabId:this.tabId},qr),await this.session.send("Runtime.enable"),await this.session.send("Page.enable"),await this.session.send("DOM.enable"),await this.session.send("DOM.getDocument",{depth:-1,pierce:!0}),await this.session.send("Accessibility.enable"),this.attached=!0}catch(t){throw Re.error("attach failed",t),hn(t)}}async dispose(){if(this.attached){try{await chrome.debugger.detach({tabId:this.tabId})}catch(t){Re.debug("detach soft-failure",t)}this.attached=!1}}async pressKey(t,n){const a=Yr[t];if(!a)throw new Error(`Unknown key: ${String(t)}`);const r=n!=null&&n.shift?8:0;await this.session.send("Input.dispatchKeyEvent",{type:"rawKeyDown",key:a.key,code:a.code,windowsVirtualKeyCode:a.windowsVirtualKeyCode,modifiers:r}),await this.session.send("Input.dispatchKeyEvent",{type:"keyUp",key:a.key,code:a.code,windowsVirtualKeyCode:a.windowsVirtualKeyCode,modifiers:r}),await Yt(50)}async pressKeyN(t,n,a){for(let r=0;r<n;r++)await this.pressKey(t,a)}async typeText(t){await this.session.send("Input.insertText",{text:t}),await Yt(50)}async getFocusedElement(){var n;const t=await this.session.send("Runtime.evaluate",{expression:Xr,returnByValue:!0});try{const a=(n=t.result)==null?void 0:n.value;return typeof a=="string"?JSON.parse(a):a}catch(a){return Re.error("getFocusedElement: probe returned non-JSON",a),Jr}}async captureScreenshot(){try{const t=await this.session.send("Page.captureScreenshot",{format:"jpeg",quality:75});return t!=null&&t.data?`data:image/jpeg;base64,${t.data}`:null}catch(t){return Re.debug("captureScreenshot failed",t),null}}async getAxContext(){try{return((await this.session.send("Accessibility.getFullAXTree")).nodes??[]).filter(n=>!n.ignored).map(n=>{var a,r,s;return{nodeId:n.nodeId,role:(a=n.role)==null?void 0:a.value,name:(r=n.name)==null?void 0:r.value,description:(s=n.description)==null?void 0:s.value,childIds:n.childIds}})}catch(t){return Re.debug("getAxContext failed",t),[]}}async recordStep(t,n){const a=await this.getFocusedElement(),r=(n==null?void 0:n.screenshot)!==!1?await this.captureScreenshot():null,s=n!=null&&n.ax?await this.getAxContext():void 0,i={ordinal:this.steps.length,at:new Date().toISOString(),action:t,focused:a,screenshot:r??void 0,axContext:s};return this.steps.push(i),i}}function Yt(e){return new Promise(t=>setTimeout(t,e))}const Jr={selector:"body",tag:"body",role:"",name:"",outerHtml:"",rect:{x:0,y:0,width:0,height:0},isBody:!0},Xr=`(() => {
899
+ const el = document.activeElement;
900
+ if (!el || el === document.body) {
901
+ return JSON.stringify({
902
+ selector: 'body',
903
+ tag: 'body',
904
+ role: '',
905
+ name: '',
906
+ outerHtml: '',
907
+ rect: { x: 0, y: 0, width: 0, height: 0 },
908
+ isBody: true,
909
+ });
910
+ }
911
+ function selectorFor(node) {
912
+ if (node.id) return '#' + CSS.escape(node.id);
913
+ const parts = [];
914
+ let cur = node;
915
+ while (cur && cur.nodeType === 1 && cur !== document.body) {
916
+ let part = cur.tagName.toLowerCase();
917
+ if (cur.classList.length > 0) {
918
+ part += '.' + Array.from(cur.classList).slice(0, 2).map(c => CSS.escape(c)).join('.');
919
+ }
920
+ const parent = cur.parentElement;
921
+ if (parent) {
922
+ const siblings = Array.from(parent.children).filter(s => s.tagName === cur.tagName);
923
+ if (siblings.length > 1) {
924
+ part += ':nth-of-type(' + (siblings.indexOf(cur) + 1) + ')';
925
+ }
926
+ }
927
+ parts.unshift(part);
928
+ cur = parent;
929
+ if (cur && cur.id) { parts.unshift('#' + CSS.escape(cur.id)); break; }
930
+ }
931
+ return parts.join(' > ');
932
+ }
933
+ function accName(node) {
934
+ return (
935
+ node.getAttribute('aria-label') ||
936
+ (node.getAttribute('aria-labelledby') && (() => {
937
+ const ids = node.getAttribute('aria-labelledby').split(/\\s+/);
938
+ return ids.map(id => { const r = document.getElementById(id); return r ? r.textContent : ''; }).join(' ').trim();
939
+ })()) ||
940
+ node.getAttribute('alt') ||
941
+ (node.tagName === 'INPUT' && node.labels && node.labels[0] ? node.labels[0].textContent : '') ||
942
+ (node.textContent || '').trim().slice(0, 200)
943
+ );
944
+ }
945
+ const rect = el.getBoundingClientRect();
946
+ return JSON.stringify({
947
+ selector: selectorFor(el),
948
+ tag: el.tagName.toLowerCase(),
949
+ role: el.getAttribute('role') || '',
950
+ name: (accName(el) || '').toString().slice(0, 200),
951
+ outerHtml: (el.outerHTML || '').slice(0, 2000),
952
+ rect: { x: rect.left, y: rect.top, width: rect.width, height: rect.height },
953
+ isBody: false,
954
+ });
955
+ })()`,Qr=[/no parseable JSON/i,/invalid JSON/i];function Jt(e){if(e.verdict!=="uncertain"||e.confidence!==0)return!1;const t=e.reasoning??"";return Qr.some(n=>n.test(t))}async function et(e){const t=await e();if(!Jt(t))return t;try{const n=await e();if(!Jt(n))return n}catch{}return t}const Zr=D("focus-order-audit"),es="2.4.3",ts=50;async function Un(e){var i,c,l;const t=await chrome.storage.local.get("aiConfig"),n=oe(t.aiConfig),a=q(n);if(!a.ok)return{ok:!1,error:a.reason};const r=a.client;if(typeof r.judgeFocusOrder!="function")return{ok:!1,error:"AI client does not support judgeFocusOrder yet."};const s=new $t(e.tabId);try{await s.attach(),await ns(s),await s.recordStep("initial",{screenshot:!0});const d=new Set;for(let g=0;g<ts;g++){if((i=e.signal)!=null&&i.aborted)return{ok:!1,error:"Audit cancelled."};await s.pressKey("Tab");const o=await s.recordStep(`Tab #${g+1}`,{screenshot:!1});if(o.focused.isBody||d.has(o.focused.selector))break;d.add(o.focused.selector),(c=e.onStep)==null||c.call(e,{ordinal:o.ordinal,selector:o.focused.selector,role:o.focused.role,name:o.focused.name})}const u=s.steps.filter(g=>g.action!=="initial").map(g=>({ordinal:g.ordinal,selector:g.focused.selector,role:g.focused.role,name:g.focused.name,rect:g.focused.rect})),h=(l=s.steps[0])==null?void 0:l.screenshot,p=await et(()=>r.judgeFocusOrder({pageUrl:e.pageUrl,pageScreenshot:h,tabSequence:u,signal:e.signal})),f={componentId:e.componentId,criterionId:es,verdict:p.verdict==="pass"?"pass":p.verdict==="fail"?"fail":"uncertain",reasoning:p.reasoning,confidence:p.confidence,steps:s.steps,pageUrl:e.pageUrl,finishedAt:new Date().toISOString(),costUsd:p.costUsd,model:p.model};return await ve(f),{ok:!0,result:f}}catch(d){return Zr.error("focus-order audit failed",d),{ok:!1,error:d instanceof Error?d.message:String(d)}}finally{await s.dispose()}}async function ns(e){await e.session.send("Runtime.evaluate",{expression:"(() => { if (document.activeElement && document.activeElement !== document.body) document.activeElement.blur(); window.scrollTo(0, 0); })()"})}const as=D("keyboard-trap-audit"),os="2.1.2",rs=50,ss=50,is=3;async function Nn(e){var i,c;const t=await chrome.storage.local.get("aiConfig"),n=oe(t.aiConfig),a=q(n);if(!a.ok)return{ok:!1,error:a.reason};const r=a.client;if(typeof r.judgeKeyboardTrap!="function")return{ok:!1,error:"AI client does not support judgeKeyboardTrap yet."};const s=new $t(e.tabId);try{await s.attach(),await cs(s),await s.recordStep("initial",{screenshot:!0});const l=[];for(let m=0;m<rs;m++){if((i=e.signal)!=null&&i.aborted)return{ok:!1,error:"Audit cancelled."};await s.pressKey("Tab");const k=await s.recordStep(`Tab #${m+1}`,{screenshot:!1});if(k.focused.isBody||(l.push(k.focused.selector),l.length>2&&k.focused.selector===l[0]))break}const d=[];let u=0;for(let m=1;m<=l.length;m++)if(m===l.length||l[m]!==l[m-1]){const k=m-u;k>=is&&d.push(`${l[u]} (×${k} consecutive)`),u=m}const h=[];for(let m=0;m<ss;m++){if((c=e.signal)!=null&&c.aborted)return{ok:!1,error:"Audit cancelled."};await s.pressKey("Tab",{shift:!0});const k=await s.recordStep(`Shift+Tab #${m+1}`,{screenshot:!1});if(k.focused.isBody||(h.push(k.focused.selector),h.length>=l.length))break}const p=l.slice(0,-1).reverse(),f=[];for(let m=0;m<Math.min(p.length,h.length);m++){const k=p[m],v=h[m];k!==v&&f.push({index:m,expected:k,actual:v})}const g=await et(()=>r.judgeKeyboardTrap({pageUrl:e.pageUrl,forwardSequence:l,reverseSequence:h,stuckRuns:d,reverseMismatches:f.slice(0,10),signal:e.signal})),o={componentId:e.componentId,criterionId:os,verdict:g.verdict==="pass"?"pass":g.verdict==="fail"?"fail":"uncertain",reasoning:g.reasoning,confidence:g.confidence,steps:s.steps,pageUrl:e.pageUrl,finishedAt:new Date().toISOString(),costUsd:g.costUsd,model:g.model};return await ve(o),{ok:!0,result:o}}catch(l){return as.error("keyboard-trap audit failed",l),{ok:!1,error:l instanceof Error?l.message:String(l)}}finally{await s.dispose()}}async function cs(e){await e.session.send("Runtime.evaluate",{expression:"(() => { if (document.activeElement && document.activeElement !== document.body) document.activeElement.blur(); window.scrollTo(0, 0); })()"})}const ls=D("focus-visible-audit"),ds="2.4.7",us=30,ps=10;async function Dn(e){var i;const t=await chrome.storage.local.get("aiConfig"),n=oe(t.aiConfig),a=q(n);if(!a.ok)return{ok:!1,error:a.reason};const r=a.client;if(typeof r.judgeFocusVisible!="function")return{ok:!1,error:"AI client does not support judgeFocusVisible yet."};const s=new $t(e.tabId);try{await s.attach(),await hs(s);const c=[],l=new Set;for(let h=0;h<us&&c.length<ps;h++){if((i=e.signal)!=null&&i.aborted)return{ok:!1,error:"Audit cancelled."};await s.pressKey("Tab");const p=await s.recordStep(`Tab #${h+1}`,{screenshot:!0});if(p.focused.isBody||l.has(p.focused.selector))break;l.add(p.focused.selector);const f=await fs(s);p.screenshot&&c.push({ordinal:c.length+1,selector:p.focused.selector,role:p.focused.role,name:p.focused.name,rect:p.focused.rect,screenshot:p.screenshot,focusStyle:f})}if(c.length===0)return{ok:!1,error:"No focusable elements found on the page; cannot evaluate 2.4.7."};const d=await et(()=>r.judgeFocusVisible({pageUrl:e.pageUrl,samples:c,signal:e.signal})),u={componentId:e.componentId,criterionId:ds,verdict:d.verdict==="pass"?"pass":d.verdict==="fail"?"fail":"uncertain",reasoning:d.reasoning,confidence:d.confidence,steps:s.steps,pageUrl:e.pageUrl,finishedAt:new Date().toISOString(),costUsd:d.costUsd,model:d.model};return await ve(u),{ok:!0,result:u}}catch(c){return ls.error("focus-visible audit failed",c),{ok:!1,error:c instanceof Error?c.message:String(c)}}finally{await s.dispose()}}async function hs(e){await e.session.send("Runtime.evaluate",{expression:"(() => { if (document.activeElement && document.activeElement !== document.body) document.activeElement.blur(); window.scrollTo(0, 0); })()"})}async function fs(e){const t=`(() => {
956
+ const el = document.activeElement;
957
+ if (!el || el === document.body) return JSON.stringify({
958
+ outlineStyle: '', outlineWidth: '', outlineColor: '', outlineOffset: '',
959
+ boxShadow: '', border: '', backgroundColor: '',
960
+ });
961
+ const cs = getComputedStyle(el);
962
+ return JSON.stringify({
963
+ outlineStyle: cs.outlineStyle,
964
+ outlineWidth: cs.outlineWidth,
965
+ outlineColor: cs.outlineColor,
966
+ outlineOffset: cs.outlineOffset,
967
+ boxShadow: cs.boxShadow,
968
+ border: cs.border,
969
+ backgroundColor: cs.backgroundColor,
970
+ });
971
+ })()`;try{const n=await e.session.send("Runtime.evaluate",{expression:t,returnByValue:!0});return JSON.parse(n.result.value)}catch{return{outlineStyle:"",outlineWidth:"",outlineColor:"",outlineOffset:"",boxShadow:"",border:"",backgroundColor:""}}}const gs=D("reading-order-audit"),Xt="1.3.2";async function Pn(e){var c,l,d;const t=await chrome.storage.local.get("aiConfig"),n=oe(t.aiConfig),a=q(n);if(!a.ok)return{ok:!1,error:a.reason};const r=a.client;if(typeof r.judgeReadingOrder!="function")return{ok:!1,error:"AI client does not support judgeReadingOrder yet."};const s=((c=e.results[0])==null?void 0:c.readingOrderIssues)??[],i=(l=e.results[0])==null?void 0:l.screenshotDataUrl;if(s.length===0){const u={componentId:e.componentId,criterionId:Xt,verdict:"pass",reasoning:"The reading-order heuristic flagged zero divergences between DOM source order and visual position order. No AI judgment required.",confidence:1,steps:[],pageUrl:e.pageUrl,finishedAt:new Date().toISOString(),costUsd:0,model:"heuristic-only"};return await ve(u),{ok:!0,result:u}}if((d=e.signal)!=null&&d.aborted)return{ok:!1,error:"Audit cancelled."};try{const u=await et(()=>r.judgeReadingOrder({pageUrl:e.pageUrl,pageScreenshot:i,flaggedIssues:s.map(p=>({selector:p.selector,textSnippet:p.textSnippet,domIndex:p.domIndex,visualIndex:p.visualIndex,rect:p.boundingRect})),signal:e.signal})),h={componentId:e.componentId,criterionId:Xt,verdict:u.verdict==="pass"?"pass":u.verdict==="fail"?"fail":"uncertain",reasoning:u.reasoning,confidence:u.confidence,steps:[],pageUrl:e.pageUrl,finishedAt:new Date().toISOString(),costUsd:u.costUsd,model:u.model};return await ve(h),{ok:!0,result:h}}catch(u){return gs.error("reading-order audit failed",u),{ok:!1,error:u instanceof Error?u.message:String(u)}}}function ms(){return W("INTERACTIVE_AUDIT_REQUEST",async e=>{try{switch(e.criterionId){case"2.4.3":{const t=await Un({tabId:e.tabId,componentId:e.componentId,pageUrl:e.pageUrl});return t.ok?{type:"INTERACTIVE_AUDIT_RESPONSE",ok:!0,result:t.result}:{type:"INTERACTIVE_AUDIT_RESPONSE",ok:!1,error:t.error}}case"2.1.2":{const t=await Nn({tabId:e.tabId,componentId:e.componentId,pageUrl:e.pageUrl});return t.ok?{type:"INTERACTIVE_AUDIT_RESPONSE",ok:!0,result:t.result}:{type:"INTERACTIVE_AUDIT_RESPONSE",ok:!1,error:t.error}}case"2.4.7":{const t=await Dn({tabId:e.tabId,componentId:e.componentId,pageUrl:e.pageUrl});return t.ok?{type:"INTERACTIVE_AUDIT_RESPONSE",ok:!0,result:t.result}:{type:"INTERACTIVE_AUDIT_RESPONSE",ok:!1,error:t.error}}case"1.3.2":{const t=await chrome.storage.local.get("sidePanel:lastAudit"),n=t==null?void 0:t["sidePanel:lastAudit"],a=(n==null?void 0:n.results)??[];if(a.length===0)return{type:"INTERACTIVE_AUDIT_RESPONSE",ok:!1,error:"No audit results available. Run an audit first, then the 1.3.2 walkthrough can judge its reading-order findings."};const r=await Pn({componentId:e.componentId,pageUrl:e.pageUrl,results:a});return r.ok?{type:"INTERACTIVE_AUDIT_RESPONSE",ok:!0,result:r.result}:{type:"INTERACTIVE_AUDIT_RESPONSE",ok:!1,error:r.error}}default:return{type:"INTERACTIVE_AUDIT_RESPONSE",ok:!1,error:`Interactive audit for criterion ${e.criterionId} not yet implemented.`}}}catch(t){return{type:"INTERACTIVE_AUDIT_RESPONSE",ok:!1,error:t instanceof Error?t.message:String(t)}}})}const Be={A:0,B:1,C:2,D:3,F:4},Qt={low:0,moderate:1,high:2,critical:3};function ys(e,t){return Be[e]>Be[t]?e:t}function bs(e,t){return Qt[e]>Qt[t]?e:t}function ws(e){const t=[];for(const n of e)for(const a of n.results)for(const r of a.violations)t.push({url:n.url,v:r});return t}function vs(e,t,n,a,r){const s=t.filter(v=>!v.error).length,i=t.filter(v=>!!v.error).length,c=t.map(v=>{if(v.error)return{url:v.url,grade:"F",risk:"critical",uniqueViolations:0,totals:{critical:0,serious:0,moderate:0,minor:0},durationMs:v.durationMs,error:v.error};const I=v.results.flatMap(N=>N.violations),$=fe(I);return{url:v.url,grade:$.letter,risk:$.risk,uniqueViolations:$.totals.unique,totals:{critical:$.totals.critical,serious:$.totals.serious,moderate:$.totals.moderate,minor:$.totals.minor},durationMs:v.durationMs}});c.sort((v,I)=>{const $=Be[I.grade]-Be[v.grade];return $!==0?$:I.uniqueViolations-v.uniqueViolations});let l="A",d="low";for(const v of c)l=ys(l,v.grade),d=bs(d,v.risk);const u=ws(t),h=v=>`${v.ruleId}::${v.target.selector}`,p=new Map;for(const{v}of u){const I=h(v);p.has(I)||p.set(I,v)}const f=p.size,g={critical:0,serious:0,moderate:0,minor:0};for(const v of p.values())g[v.impact]++;const o=new Map;for(const{url:v,v:I}of u){let $=o.get(I.ruleId);$||($={ruleId:I.ruleId,description:I.description,impact:I.impact,urls:new Set,totalOccurrences:0},o.set(I.ruleId,$)),$.urls.add(v),$.totalOccurrences++}const m=Array.from(o.values()).map(v=>({ruleId:v.ruleId,description:v.description,impact:v.impact,urlsAffected:v.urls.size,totalOccurrences:v.totalOccurrences,sampleUrls:Array.from(v.urls).slice(0,5)})).sort((v,I)=>I.urlsAffected!==v.urlsAffected?I.urlsAffected-v.urlsAffected:I.totalOccurrences-v.totalOccurrences),k={A:0,B:0,C:0,D:0,F:0};for(const v of c)k[v.grade]++;return{startedAt:n,finishedAt:a,startUrl:e,pagesAudited:s,pagesFailed:i,totalDurationMs:r,siteGrade:l,siteRisk:d,totalUniqueViolations:f,totals:g,topViolations:m,pages:c,pagesByGrade:k}}function As(e){const t=new Map(e.breakpointPresets.map(a=>[a.id,a])),n=[];for(const a of e.breakpoints){const r=t.get(a);if(r)for(const s of e.directions)for(const i of e.themes){const c=e.ariaVariations.length===0?[null]:e.ariaVariations;for(const l of c)for(const d of e.pseudoStates)n.push({pseudoState:d,ariaVariation:l,theme:i,direction:s,breakpoint:r})}}return n}const Ln=["color-contrast","color-contrast-enhanced","link-in-text-block"],Ss=["image-alt","input-image-alt","area-alt","svg-img-alt","object-alt","role-img-alt","label","select-name","aria-input-field-name","aria-toggle-field-name","form-field-multiple-labels","autocomplete-valid","label-content-name-mismatch","label-title-only","button-name","input-button-name","link-name","empty-button","empty-heading","empty-link","frame-title","heading-order","page-has-heading-one","landmark-one-main","landmark-no-duplicate-banner","landmark-no-duplicate-contentinfo","landmark-no-duplicate-main","landmark-unique","landmark-banner-is-top-level","landmark-complementary-is-top-level","landmark-contentinfo-is-top-level","landmark-main-is-top-level","region","bypass","empty-table-header","aria-allowed-attr","aria-required-attr","aria-required-children","aria-required-parent","aria-roles","aria-valid-attr-value","aria-valid-attr","aria-hidden-body","aria-hidden-focus","aria-allowed-role","aria-prohibited-attr","presentation-role-conflict","aria-text","aria-deprecated-role","aria-conditional-attr","aria-braille-equivalent","html-has-lang","html-lang-valid","html-xml-lang-mismatch","valid-lang","nested-interactive","tabindex","frame-focusable-content","no-focusable-content","focus-order-semantics","meta-viewport","meta-refresh","duplicate-id","duplicate-id-active","duplicate-id-aria","video-caption","audio-caption","table-fake-caption","scope-attr-valid","th-has-data-cells","td-headers-attr"];{const e=new Set(Ln),t=Ss.filter(n=>e.has(n));t.length>0&&console.error("axe-rule-classification: rule(s) classified as BOTH state-dependent and markup-eligible:",t)}const ks=D("ai-alt-text");async function xs(e,t,n={}){var h,p;if(!t.enabled||!t.enabledChecks.altText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const a=q(t);if(!a.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${a.reason}`]};const r=a.client,s=ne(t.costCapUsd),i=[],c=[],l=Math.max(1,t.maxCandidatesPerCheck??10),d=e.slice(0,l),u=d.length;for(let f=0;f<d.length;f++){const g=d[f];if(!s.canCharge())break;try{if((h=n.signal)!=null&&h.aborted){c.push(`canceled after ${f}/${u} candidates`);break}const o=await r.judgeAltText({imageUrl:g.imageUrl,alt:g.alt,surroundingContext:g.surroundingContext,signal:n.signal});s.recordCharge(o.costUsd),(o.verdict==="fail"||o.verdict==="uncertain"&&o.confidence<.6)&&i.push(await Es(g,o))}catch(o){if(o instanceof Error&&(o.name==="AbortError"||o.name==="ExternalAbortError")){c.push(`canceled after ${f}/${u} candidates`);break}const k=o instanceof Error?o.message:String(o);c.push(`${g.selector}: ${k}`),ks.warn("alt-text judgment failed",o)}(p=n.onProgress)==null||p.call(n,f+1,u)}return{findings:i,totalCostUsd:s.state.spentUsd,capExceeded:s.state.exceeded,errors:c}}async function Es(e,t){const n=t.verdict==="uncertain"?"ai-alt-uncertain":"ai-alt-misleading",a={selector:e.selector,outerHTML:e.outerHTML.slice(0,500),failureSummary:`AI assessment: ${t.reasoning}`,tagName:"IMG",role:null,accessibleName:e.alt,textSnippet:null,attrId:null,attrTestid:null},r=await vt({ruleId:n,componentId:e.componentId,currentState:e.currentState,target:a}),s=t.verdict==="uncertain";return{ruleId:n,wcagCriterion:"1.1.1",wcagLevel:"A",impact:s?"minor":"serious",description:s?"AI couldn't verify whether this alt text is appropriate — flag for human review.":"AI judged the alt text to be misleading, generic, or otherwise inappropriate.",helpUrl:"https://www.w3.org/WAI/WCAG21/Understanding/non-text-content.html",target:a,componentId:e.componentId,currentState:e.currentState,axeVersion:e.axeVersion,detectedAt:new Date().toISOString(),matchKey:r,ai:{reasoning:t.reasoning,confidence:t.confidence,model:t.model,costUsd:t.costUsd},...s?{needsReview:!0}:{}}}const ye=D("interactive-walkthroughs");async function $s(e){var n,a,r,s;const t=[];try{if((n=e.signal)!=null&&n.aborted)t.push({criterionId:"2.4.3",ran:!1,ok:!1,error:"cancelled"});else{const i=await Un({tabId:e.tabId,componentId:e.componentId,pageUrl:e.pageUrl,signal:e.signal});t.push({criterionId:"2.4.3",ran:!0,ok:i.ok,verdict:i.ok?i.result.verdict:void 0,error:i.ok?void 0:i.error})}}catch(i){ye.warn("2.4.3 walkthrough threw",i),t.push({criterionId:"2.4.3",ran:!0,ok:!1,error:i instanceof Error?i.message:String(i)})}try{if((a=e.signal)!=null&&a.aborted)t.push({criterionId:"2.1.2",ran:!1,ok:!1,error:"cancelled"});else{const i=await Nn({tabId:e.tabId,componentId:e.componentId,pageUrl:e.pageUrl,signal:e.signal});t.push({criterionId:"2.1.2",ran:!0,ok:i.ok,verdict:i.ok?i.result.verdict:void 0,error:i.ok?void 0:i.error})}}catch(i){ye.warn("2.1.2 walkthrough threw",i),t.push({criterionId:"2.1.2",ran:!0,ok:!1,error:i instanceof Error?i.message:String(i)})}try{if((r=e.signal)!=null&&r.aborted)t.push({criterionId:"2.4.7",ran:!1,ok:!1,error:"cancelled"});else{const i=await Dn({tabId:e.tabId,componentId:e.componentId,pageUrl:e.pageUrl,signal:e.signal});t.push({criterionId:"2.4.7",ran:!0,ok:i.ok,verdict:i.ok?i.result.verdict:void 0,error:i.ok?void 0:i.error})}}catch(i){ye.warn("2.4.7 walkthrough threw",i),t.push({criterionId:"2.4.7",ran:!0,ok:!1,error:i instanceof Error?i.message:String(i)})}try{if((s=e.signal)!=null&&s.aborted)t.push({criterionId:"1.3.2",ran:!1,ok:!1,error:"cancelled"});else if(!e.results||e.results.length===0)t.push({criterionId:"1.3.2",ran:!1,ok:!1,error:"no audit results available"});else{const i=await Pn({componentId:e.componentId,pageUrl:e.pageUrl,results:e.results,signal:e.signal});t.push({criterionId:"1.3.2",ran:!0,ok:i.ok,verdict:i.ok?i.result.verdict:void 0,error:i.ok?void 0:i.error})}}catch(i){ye.warn("1.3.2 walkthrough threw",i),t.push({criterionId:"1.3.2",ran:!0,ok:!1,error:i instanceof Error?i.message:String(i)})}try{const i=t.find(l=>l.criterionId==="2.4.3"),c=t.find(l=>l.criterionId==="2.1.2");if(i!=null&&i.ok&&i.verdict==="fail"&&(c!=null&&c.ok)&&c.verdict==="pass"){const l=await ya(e.componentId,"2.4.3");if(l&&l.verdict==="fail"){const d={...l,verdict:"uncertain",reasoning:l.reasoning+`
972
+
973
+ [Cross-corroboration override] WCAG 2.1.2 No Keyboard Trap PASSED on this same DOM. Tab walks forward AND Shift+Tab walks back. The "reverse y-coordinate" pattern this rule flagged is consistent with a multi-column or banked layout (header → main → sidebar → footer), not an actual focus-order break. Downgraded fail → uncertain. Verify manually if the visual reading order is ambiguous.`};await ve(d),i.verdict="uncertain",ye.info("2.4.3 fail downgraded to uncertain — 2.1.2 PASSed (multi-column layout corroboration)")}}}catch(i){ye.warn("2.4.3 ↔ 2.1.2 cross-corroboration step failed",i)}return t}const We=D("pixel-contrast-sampler"),Ts="1.3",Is=4.5,Cs=3;function Rs(e,t){return e?e>=24||e>=18.66&&(t??400)>=700:!1}function Os(e){if(!e)return null;const t=/^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/.exec(e);if(t)return{r:Number(t[1]),g:Number(t[2]),b:Number(t[3])};const n=/^#([0-9a-f])([0-9a-f])([0-9a-f])$/i.exec(e);if(n)return{r:parseInt(n[1]+n[1],16),g:parseInt(n[2]+n[2],16),b:parseInt(n[3]+n[3],16)};const a=/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(e);return a?{r:parseInt(a[1],16),g:parseInt(a[2],16),b:parseInt(a[3],16)}:null}function He({r:e,g:t,b:n}){const a=r=>{const s=r/255;return s<=.03928?s/12.92:Math.pow((s+.055)/1.055,2.4)};return .2126*a(e)+.7152*a(t)+.0722*a(n)}function _s(e,t){const n=He(e),a=He(t),r=Math.max(n,a),s=Math.min(n,a);return(r+.05)/(s+.05)}async function Ms(e,t){var a;const n=`(() => {
974
+ const el = document.querySelector(${JSON.stringify(t)});
975
+ if (!el) return null;
976
+ const r = el.getBoundingClientRect();
977
+ if (r.width === 0 || r.height === 0) return null;
978
+ return JSON.stringify({
979
+ x: r.left,
980
+ y: r.top,
981
+ width: r.width,
982
+ height: r.height,
983
+ devicePixelRatio: window.devicePixelRatio || 1,
984
+ });
985
+ })()`;try{const r=await chrome.debugger.sendCommand({tabId:e},"Runtime.evaluate",{expression:n,returnByValue:!0}),s=(a=r==null?void 0:r.result)==null?void 0:a.value;return!s||typeof s!="string"?null:JSON.parse(s)}catch(r){return We.debug("getElementRect failed",r),null}}function Us(e){const{data:t,width:n,height:a}=e,r=[];for(let p=0;p<n;p+=Math.max(1,Math.floor(n/32))){const f=(0*n+p)*4,g=((a-1)*n+p)*4;r.push({r:t[f],g:t[f+1],b:t[f+2]}),r.push({r:t[g],g:t[g+1],b:t[g+2]})}for(let p=0;p<a;p+=Math.max(1,Math.floor(a/16))){const f=(p*n+0)*4,g=(p*n+(n-1))*4;r.push({r:t[f],g:t[f+1],b:t[f+2]}),r.push({r:t[g],g:t[g+1],b:t[g+2]})}const s=r.map(p=>p.r).sort((p,f)=>p-f),i=r.map(p=>p.g).sort((p,f)=>p-f),c=r.map(p=>p.b).sort((p,f)=>p-f),l=Math.floor(r.length/2),d={r:s[l],g:i[l],b:c[l]},u=He(d);let h=0;for(const p of r){const f=He(p)-u;h+=f*f}return h/=r.length,{color:d,variance:h}}const Ns=.05;async function Ds(e,t,n,a,r){const s=Os(n);if(!s)return{ok:!1,reason:"no parseable foreground color"};const i=await Ms(e,t);if(!i)return{ok:!1,reason:"element rect not resolvable (off-DOM or zero-size)"};const c=2;let l;try{l=await chrome.debugger.sendCommand({tabId:e},"Page.captureScreenshot",{format:"png",clip:{x:Math.max(0,i.x-c),y:Math.max(0,i.y-c),width:i.width+2*c,height:i.height+2*c,scale:1}})}catch(o){return{ok:!1,reason:`screenshot failed: ${o instanceof Error?o.message:String(o)}`}}if(!(l!=null&&l.data))return{ok:!1,reason:"no screenshot data"};let d;try{const o=`data:image/png;base64,${l.data}`,m=await fetch(o).then($=>$.blob()),k=await createImageBitmap(m),I=new OffscreenCanvas(k.width,k.height).getContext("2d");if(!I)return{ok:!1,reason:"OffscreenCanvas 2d context unavailable"};I.drawImage(k,0,0),d=I.getImageData(0,0,k.width,k.height)}catch(o){return{ok:!1,reason:`decode failed: ${o instanceof Error?o.message:String(o)}`}}const{color:u,variance:h}=Us(d);if(h>Ns)return{ok:!1,reason:`background non-uniform (variance ${h.toFixed(3)}); likely image/gradient — needs vision AI`};const p=_s(s,u),f=Rs(a,r),g=f?Cs:Is;return{ok:!0,ratio:p,threshold:g,passes:p>=g,sampledBackground:u,isLarge:f}}async function Ps(e,t,n){var i,c,l,d;const a=new Map;for(const u of t){const h=u.pageUrl??u.scope??"";for(const p of((i=u.axeRulesEvaluated)==null?void 0:i.incomplete)??[])if(p.ruleId==="color-contrast")for(const f of p.elements??[]){const g=`${h}::${p.ruleId}::${f.selector}`;a.has(g)||a.set(g,{pageUrl:h,ruleId:p.ruleId,selector:f.selector,wcagCriterion:p.wcagCriterion??"1.4.3",foreground:(c=f.styles)==null?void 0:c.foreground,fontSize:(l=f.styles)==null?void 0:l.fontSize,fontWeight:(d=f.styles)==null?void 0:d.fontWeight})}}if(a.size===0)return{resolved:0,skipped:0};try{await chrome.debugger.attach({tabId:e},Ts)}catch(u){return We.warn("debugger attach failed; skipping pixel sampling",u),{resolved:0,skipped:a.size}}let r=0,s=0;try{for(const u of a.values()){if(n!=null&&n.aborted)break;const h=await Ds(e,u.selector,u.foreground,u.fontSize,u.fontWeight);if(!h.ok){s++,We.debug(`skipped ${u.selector}: ${h.reason}`);continue}const p={pageUrl:u.pageUrl,ruleId:u.ruleId,selector:u.selector,verdict:h.passes?"pass":"fail",reasoning:`Pixel-sampled contrast: ${h.ratio.toFixed(2)}:1 against AA threshold ${h.threshold}:1 (${h.isLarge?"large text":"normal text"}). Foreground from computed CSS; background sampled from ${h.passes?"rendered pixels at the element perimeter — uniform background detected":"rendered pixels — uniform background but ratio falls below threshold"}.`,resolvedAt:new Date().toISOString(),costUsd:0,wcagCriterion:u.wcagCriterion};await ba(p),r++}}finally{try{await chrome.debugger.detach({tabId:e})}catch{}}return We.info(`pixel-contrast sampler: resolved ${r}, skipped ${s}/${a.size}`),{resolved:r,skipped:s}}const Ee=D("ai-text");function $e(e){if(e!=null&&e.aborted){const t=new Error("aborted");throw t.name="AbortError",t}}function Te(e){return e instanceof Error&&(e.name==="AbortError"||e.name==="ExternalAbortError"||e.message==="aborted")}async function Ls(e,t,n,a={}){var p;if(!n.enabled||!n.enabledChecks.headings)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const r=q(n);if(!r.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${r.reason}`]};const s=r.client,i=ne(n.costCapUsd),c=[],l=[],d=Math.max(1,n.maxCandidatesPerCheck??10),u=e.slice(0,d),h=u.length;for(let f=0;f<u.length;f++){const g=u[f];if(!i.canCharge())break;try{$e(a.signal);const o=await s.judgeHeading({headingText:g.text,sectionContent:g.sectionContent,signal:a.signal});i.recordCharge(o.costUsd),o.verdict==="fail"&&o.confidence>=.6&&c.push(await Ie("ai-heading-not-descriptive","2.4.6","AA","moderate","AI judged this heading does not describe its section content.",g.selector,g.outerHTML,g.text,o,t))}catch(o){if(Te(o)){l.push(`canceled after ${f}/${h} candidates`);break}const m=o instanceof Error?o.message:String(o);l.push(`${g.selector}: ${m}`),Ee.warn("heading judgment failed",o)}(p=a.onProgress)==null||p.call(a,f+1,h)}return{findings:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:l}}async function Fs(e,t,n,a={}){var p;if(!n.enabled||!n.enabledChecks.sensory)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const r=q(n);if(!r.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${r.reason}`]};const s=r.client,i=ne(n.costCapUsd),c=[],l=[],d=Math.max(1,n.maxCandidatesPerCheck??10),u=e.slice(0,d),h=u.length;for(let f=0;f<u.length;f++){const g=u[f];if(!i.canCharge())break;try{$e(a.signal);const o=await s.judgeSensoryLanguage({instructionText:g.text,signal:a.signal});i.recordCharge(o.costUsd),o.verdict==="fail"&&o.confidence>=.6&&c.push(await Ie("ai-sensory-instruction","1.3.3","A","serious","AI flagged this instruction as relying on sensory characteristics (shape, color, location) without a programmatic identifier.",g.selector,g.outerHTML,g.text.slice(0,100),o,t))}catch(o){if(Te(o)){l.push(`canceled after ${f}/${h} candidates`);break}const m=o instanceof Error?o.message:String(o);l.push(`${g.selector}: ${m}`),Ee.warn("sensory judgment failed",o)}(p=a.onProgress)==null||p.call(a,f+1,h)}return{findings:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:l}}async function Gs(e,t,n,a={}){var p;if(!n.enabled||!n.enabledChecks.aria)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const r=q(n);if(!r.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${r.reason}`]};const s=r.client,i=ne(n.costCapUsd),c=[],l=[],d=Math.max(1,n.maxCandidatesPerCheck??10),u=e.slice(0,d),h=u.length;for(let f=0;f<u.length;f++){const g=u[f];if(!i.canCharge())break;try{$e(a.signal);const o=await s.judgeAriaSemantics({elementHtml:g.outerHTML,computedRole:g.role,surroundingHtml:g.surroundingHtml,signal:a.signal});i.recordCharge(o.costUsd),o.verdict==="fail"&&o.confidence>=.65&&c.push(await Ie("ai-aria-misuse","4.1.2","A","serious",`AI judged role="${g.role}" is being used inappropriately on this element.`,g.selector,g.outerHTML,g.role,o,t))}catch(o){if(Te(o)){l.push(`canceled after ${f}/${h} candidates`);break}const m=o instanceof Error?o.message:String(o);l.push(`${g.selector}: ${m}`),Ee.warn("aria judgment failed",o)}(p=a.onProgress)==null||p.call(a,f+1,h)}return{findings:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:l}}async function Vs(e,t,n,a={}){var p;if(!n.enabled||!n.enabledChecks.genericLinkText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const r=q(n);if(!r.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${r.reason}`]};const s=r.client,i=ne(n.costCapUsd),c=[],l=[],d=Math.max(1,n.maxCandidatesPerCheck??10),u=e.slice(0,d),h=u.length;for(let f=0;f<u.length;f++){const g=u[f];if(!i.canCharge())break;try{$e(a.signal);const o=await s.judgeGenericLinkText({linkText:g.linkText,surroundingText:g.surroundingText,signal:a.signal});i.recordCharge(o.costUsd),o.verdict==="fail"&&o.confidence>=.6&&c.push(await Ie("ai-generic-link-text","2.4.4","A","serious","AI judged this link's purpose is not clear from its text or surrounding context.",g.selector,g.outerHTML,g.linkText,o,t))}catch(o){if(Te(o)){l.push(`canceled after ${f}/${h} candidates`);break}const m=o instanceof Error?o.message:String(o);l.push(`${g.selector}: ${m}`),Ee.warn("generic-link judgment failed",o)}(p=a.onProgress)==null||p.call(a,f+1,h)}return{findings:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:l}}async function Ws(e,t,n,a={}){var p;if(!n.enabled||!n.enabledChecks.wallOfText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const r=q(n);if(!r.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${r.reason}`]};const s=r.client,i=ne(n.costCapUsd),c=[],l=[],d=Math.max(1,n.maxCandidatesPerCheck??10),u=e.slice(0,d),h=u.length;for(let f=0;f<u.length;f++){const g=u[f];if(!i.canCharge())break;try{$e(a.signal);const o=await s.judgeWallOfText({blockText:g.blockText,structuralChildrenCount:g.structuralChildrenCount,signal:a.signal});i.recordCharge(o.costUsd),o.verdict==="fail"&&o.confidence>=.6&&c.push(await Ie("ai-wall-of-text","3.1.5","AAA","moderate","AI flagged this block as a wall of text with insufficient structural breaks (subheadings, lists, paragraphs).",g.selector,g.outerHTML,g.blockText.slice(0,80),o,t))}catch(o){if(Te(o)){l.push(`canceled after ${f}/${h} candidates`);break}const m=o instanceof Error?o.message:String(o);l.push(`${g.selector}: ${m}`),Ee.warn("wall-of-text judgment failed",o)}(p=a.onProgress)==null||p.call(a,f+1,h)}return{findings:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:l}}async function js(e,t,n,a={}){var p;if(!n.enabled||!n.enabledChecks.languageMismatch)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const r=q(n);if(!r.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${r.reason}`]};const s=r.client,i=ne(n.costCapUsd),c=[],l=[],d=Math.max(1,n.maxCandidatesPerCheck??10),u=e.slice(0,d),h=u.length;for(let f=0;f<u.length;f++){const g=u[f];if(!i.canCharge())break;try{$e(a.signal);const o=await s.judgeLanguageMismatch({declaredLang:g.declaredLang,bodyTextSample:g.bodyTextSample,signal:a.signal});i.recordCharge(o.costUsd),o.verdict==="fail"&&o.confidence>=.7&&c.push(await Ie("ai-language-mismatch","3.1.1","A","serious",`AI judged the page's content does not match the declared lang="${g.declaredLang||"(none)"}".`,g.selector,g.outerHTML,g.bodyTextSample.slice(0,80),o,t))}catch(o){if(Te(o)){l.push(`canceled after ${f}/${h} candidates`);break}const m=o instanceof Error?o.message:String(o);l.push(`${g.selector}: ${m}`),Ee.warn("language-mismatch judgment failed",o)}(p=a.onProgress)==null||p.call(a,f+1,h)}return{findings:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:l}}async function Ie(e,t,n,a,r,s,i,c,l,d){const u={selector:s,outerHTML:i.slice(0,500),failureSummary:`AI assessment: ${l.reasoning}`,tagName:"AI",role:null,accessibleName:null,textSnippet:c.slice(0,100)||null,attrId:null,attrTestid:null},h=await vt({ruleId:e,componentId:d.componentId,currentState:d.currentState,target:u});return{ruleId:e,wcagCriterion:t,wcagLevel:n,impact:a,description:r,helpUrl:"https://www.w3.org/WAI/WCAG21/Understanding/",target:u,componentId:d.componentId,currentState:d.currentState,axeVersion:d.axeVersion,detectedAt:new Date().toISOString(),matchKey:h,ai:{reasoning:l.reasoning,confidence:l.confidence,model:l.model,costUsd:l.costUsd}}}const Fn=D("ai-vision");function Gn(e){if(e!=null&&e.aborted){const t=new Error("aborted");throw t.name="AbortError",t}}function Vn(e){return e instanceof Error&&(e.name==="AbortError"||e.name==="ExternalAbortError"||e.message==="aborted")}async function Ks(e,t,n,a={}){var p;if(!n.enabled||!n.enabledChecks.imageOfText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const r=q(n);if(!r.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${r.reason}`]};const s=r.client,i=ne(n.costCapUsd),c=Math.max(1,n.imageOfTextMaxImages??5),l=[...e].sort((f,g)=>g.pixelArea-f.pixelArea).slice(0,c),d=l.length,u=[],h=[];for(let f=0;f<l.length;f++){const g=l[f];if(!i.canCharge())break;try{Gn(a.signal);const o=await s.judgeImageOfText({imageUrl:g.imageUrl,accessibleName:g.accessibleName,signal:a.signal});i.recordCharge(o.costUsd),o.verdict==="fail"&&o.confidence>=.65&&u.push(await Wn("ai-image-of-text","1.4.5","AA","moderate","AI judged this image bakes substantial text into the raster — should be presented as actual HTML text instead.",g.selector,g.outerHTML,g.accessibleName??"",o,t))}catch(o){if(Vn(o)){h.push(`canceled after ${f}/${d} candidates`);break}const m=o instanceof Error?o.message:String(o);h.push(`${g.selector}: ${m}`),Fn.warn("image-of-text judgment failed",o)}(p=a.onProgress)==null||p.call(a,f+1,d)}return{findings:u,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:h}}async function zs(e,t,n,a={}){var p;if(!n.enabled||!n.enabledChecks.colorOnlyMeaning)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const r=q(n);if(!r.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[r.reason]};const s=r.client,i=ne(n.costCapUsd),c=[],l=[],d=Math.max(1,n.maxCandidatesPerCheck??10),u=e.slice(0,d),h=u.length;for(let f=0;f<u.length;f++){const g=u[f];if(!i.canCharge())break;try{Gn(a.signal);const o=await s.judgeColorOnlyMeaning({elementHtml:g.elementHtml,contextDescription:g.contextDescription,signal:a.signal});i.recordCharge(o.costUsd),o.verdict==="fail"&&o.confidence>=.65&&c.push(await Wn("ai-color-only-meaning","1.4.1","A","serious","AI judged this region conveys information only through color (no text label, icon glyph, or shape difference).",g.selector,g.outerHTML,g.contextDescription,o,t))}catch(o){if(Vn(o)){l.push(`canceled after ${f}/${h} candidates`);break}const m=o instanceof Error?o.message:String(o);l.push(m),Fn.warn("color-only judgment failed",o)}(p=a.onProgress)==null||p.call(a,f+1,h)}return{findings:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:l}}async function Wn(e,t,n,a,r,s,i,c,l,d){const u={selector:s,outerHTML:i.slice(0,500),failureSummary:`AI assessment: ${l.reasoning}`,tagName:"AI",role:null,accessibleName:null,textSnippet:c.slice(0,100)||null,attrId:null,attrTestid:null},h=await vt({ruleId:e,componentId:d.componentId,currentState:d.currentState,target:u});return{ruleId:e,wcagCriterion:t,wcagLevel:n,impact:a,description:r,helpUrl:"https://www.w3.org/WAI/WCAG21/Understanding/",target:u,componentId:d.componentId,currentState:d.currentState,axeVersion:d.axeVersion,detectedAt:new Date().toISOString(),matchKey:h,ai:{reasoning:l.reasoning,confidence:l.confidence,model:l.model,costUsd:l.costUsd}}}const wt=D("ensure-content-script"),Bs=1500,Hs=300;async function jn(e,t){if(await Zt(e,t))return{ok:!0};const n=chrome.runtime.getManifest(),a=[];for(const r of n.content_scripts??[])Array.isArray(r.js)&&a.push(...r.js);if(a.length===0)return wt.error("manifest declares no content_scripts"),{ok:!1,reason:"inject-failed",detail:"no content_scripts declared in manifest"};try{const r=t!==void 0?{tabId:e,frameIds:[t]}:{tabId:e,allFrames:!0};await chrome.scripting.executeScript({target:r,files:a})}catch(r){return qs(r)}return await Ys(Hs),await Zt(e,t)?(wt.info("content script injected on demand",{tabId:e,frameId:t}),{ok:!0}):{ok:!1,reason:"unresponsive-after-inject",detail:"content script injected but did not register handlers — likely Content Security Policy on this page is blocking it"}}async function Zt(e,t){try{const n=t!==void 0?{frameId:t}:void 0,a=await Promise.race([chrome.tabs.sendMessage(e,{type:"PING_REQUEST"},n),new Promise((r,s)=>setTimeout(()=>s(new Error("ping timeout")),Bs))]);return(a==null?void 0:a.type)==="PING_RESPONSE"}catch{return!1}}function qs(e){const t=e instanceof Error?e.message:String(e);return/cannot access contents of (?:the page|url)/i.test(t)||/chrome:\/\//i.test(t)||/chrome-extension:\/\//i.test(t)||/devtools:\/\//i.test(t)||/chrome\.google\.com\/webstore/i.test(t)||/the extensions gallery/i.test(t)?{ok:!1,reason:"restricted-url",detail:t}:/no tab with id/i.test(t)||/tab .* was closed/i.test(t)?{ok:!1,reason:"tab-gone",detail:t}:(wt.warn("content-script injection failed",e),{ok:!1,reason:"inject-failed",detail:t})}function Ys(e){return new Promise(t=>setTimeout(t,e))}function Kn(e){switch(e.reason){case"restricted-url":return"Chrome blocks extensions on this page (chrome:// pages, the Chrome Web Store, view-source: pages, and a few others). Open a regular https:// page and try again.";case"tab-gone":return"The tab was closed or navigated away while the audit was starting. Click into the tab you want audited and click Scan again.";case"unresponsive-after-inject":return"We loaded our auditor onto the page, but the page's Content Security Policy appears to be blocking it. Some sites (banks, gov, strict CSPs) prevent extensions from running. Try a different page on the same site, or open DevTools → Console to confirm a CSP error.";case"inject-failed":return`Could not load our auditor onto the page. Try a hard refresh (Ctrl+Shift+R) on the page and click Scan again. If it persists, the page may be using an unusual document type or origin restriction.${e.detail?` (Detail: ${e.detail})`:""}`;case"unknown":default:return"Could not establish connection with the page. Hard-refresh (Ctrl+Shift+R) the tab you want audited and try again."}}const ct=D("forensic-anchor-client"),Js="https://api.wcagcheckr.com",Xs="wcagcheckr",Qs=15e3;function Zs(e){if(typeof e!="object"||e===null)return!1;const t=e;return t.schemaVersion!==1&&t.schemaVersion!==2||!(typeof t.anchoredAt=="string"&&typeof t.tsaName=="string"&&typeof t.rfc3161TokenBase64=="string"&&t.rfc3161TokenBase64.length>0&&typeof t.serverSignatureBase64=="string"&&t.serverSignatureBase64.length>0&&typeof t.serverKeyFingerprint=="string"&&t.serverKeyFingerprint.length>0)?!1:t.schemaVersion===2?typeof t.prevAuditHash=="string"&&/^[0-9a-f]{64}$/.test(t.prevAuditHash):!0}async function ei(e,t){const n={auditHash:e.hash,pageUrl:e.pageUrl,capturedAt:e.capturedAt,...t?{licenseId:t}:{}},a=`${Js}/v1/products/${Xs}/forensic/anchor`;try{const r=await fetch(a,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(n),signal:AbortSignal.timeout(Qs)});if(!r.ok)return ct.warn(`anchor HTTP ${r.status}; entry stays local-only`),null;const s=await r.json();return Zs(s)?s:(ct.warn("anchor returned malformed receipt; entry stays local-only",s),null)}catch(r){return ct.warn("anchor request failed; entry stays local-only",r),null}}const en="4.11.4";async function ti(e){try{return(await chrome.tabs.get(e)).url??""}catch{return""}}const E=D("flows");async function Pe(){const e=await Xe();if(!e)throw new Error("no audit target tab found");return e}async function Tt(){var n,a,r,s,i;const e=await ue("stateMatrix",ut);return((n=e.pseudoStates)==null?void 0:n.length)>0&&((a=e.themes)==null?void 0:a.length)>0&&((r=e.directions)==null?void 0:r.length)>0&&((s=e.breakpoints)==null?void 0:s.length)>0&&((i=e.breakpointPresets)==null?void 0:i.length)>0?e:(E.warn("stored stateMatrix is invalid; using defaults"),ut)}async function ni(){const e=await ue("runsPerState",1);return typeof e!="number"||e<1||e>5?1:Math.floor(e)}async function ai(e,t,n){const a=t.themes.includes("light"),r=t.themes.includes("dark");if(!a||!r)return t;let s=null;try{s=await J(e,{type:"THEME_AWARENESS_REQUEST"},n)}catch{return t}return!s||s.hasUnreadableSheets?t:s.hasDarkModeCss&&!s.hasLightModeCss?(E.info("theme-axis prune: site is dark-only, skipping light pass"),{...t,themes:t.themes.filter(i=>i!=="light")}):s.hasLightModeCss&&!s.hasDarkModeCss?(E.info("theme-axis prune: site is light-only, skipping dark pass"),{...t,themes:t.themes.filter(i=>i!=="dark")}):t}function oi(e){if(e.length<=1)return e[0]??[];const t=new Map;return e.forEach((n,a)=>{for(const r of n){const s=`${r.ruleId}::${r.target.selector}`,i=t.get(s);i?i.runIndices.add(a):t.set(s,{v:r,runIndices:new Set([a])})}}),Array.from(t.values()).map(({v:n,runIndices:a})=>({...n,flakyAcrossRuns:a.size<e.length}))}async function pe(e,t,n,a){var x,_,j;const r=await Pe(),s=await jn(r,n);if(!s.ok){M({type:"AUDIT_FAILED_EVENT",error:{code:s.reason==="restricted-url"?"RESTRICTED_URL":"CONTENT_SCRIPT_UNREACHABLE",message:Kn(s),recoverable:s.reason!=="restricted-url"}});return}const i=a??await Tt(),c=await ai(r,i,n),l=As(c),d=await ni(),u=await $a(`${await ti(r)}|${e}|${n??0}`),h=await Ta(c),p=await ue("axeDisabledRules",[]),f=await Ia(p);let g=null;try{g=(await J(r,{type:"SCOPE_FINGERPRINT_REQUEST",selector:e},n)).fingerprint}catch(b){E.debug("scope fingerprint failed; skipping cache fast-path",b)}if(g){const b=await Ca(u);if(b&&b.fingerprint===g&&b.axeVersion===en&&b.matrixConfigHash===h&&b.disabledRulesHash===f){E.info(`audit-cache HIT for ${b.componentId} — skipping matrix iteration`),M({type:"AUDIT_COMPLETE_EVENT",componentId:b.componentId,results:b.results,delta:b.delta}),M({type:"SCORECARD_UPDATED_EVENT"});return}}await Ne({sessionId:De(),mode:"single-element",scope:e,startedAt:new Date().toISOString(),lastProgressAt:new Date().toISOString(),state:"running"}),await de(r,{type:"LIVE_REGIONS_ARM_REQUEST"},n).catch(()=>{}),await de(r,{type:"FOCUS_TRACE_ARM_REQUEST"},n).catch(()=>{}),await J(r,{type:"WARMUP_REQUEST"},n).catch(()=>{});const o=[],m=[];let k="",v,I=!1,$=null,N=null;try{for(let b=0;b<l.length;b++){if(t.isCanceled()){E.info("single audit canceled by user");break}const C=l[b];try{const R=await ht(r,C,e,n);if(!R.success){v=(x=R.error)==null?void 0:x.message,E.warn(`state ${b+1}/${l.length} drive failed`,v);continue}C.breakpoint.id!==$&&(await J(r,{type:"WARMUP_REQUEST"},n).catch(()=>{}),$=C.breakpoint.id);const S=[];let A=null;const T=b===0?void 0:[...Ln];for(let F=0;F<d;F++){const P=await J(r,{type:"AUDIT_REQUEST",selector:e,currentState:C,rulesToRun:T},n);P.success&&P.result?(S.push(P.result.violations),A=P.result):P.error&&(v=P.error.message,E.warn(`state ${b+1}/${l.length} run ${F+1} failed`,v))}if(A&&S.length>0){I=!0;const F=oi(S),P=await Ba(r,{quality:75})??void 0;let V,B;try{V=(await J(r,{type:"LIVE_REGIONS_DRAIN_REQUEST"},n)).announcements}catch{}try{B=(await J(r,{type:"FOCUS_TRACE_DRAIN_REQUEST"},n)).focusEvents}catch{}if(m.push({...A,violations:F,frameId:n,announcements:V,focusEvents:B,screenshotDataUrl:P}),o.push(...F),k=A.componentId,b===0&&N===null)try{const Y=await ue("aiConfig",{}),re=oe(Y);re.enabled&&re.apiKey&&k&&(N=qe(r,e,n,re,m,k,t).catch(ge=>{E.warn("ai augmentation (parallel) failed",ge)}))}catch(Y){E.debug("ai augmentation (parallel) gate-read failed",Y)}}}catch(R){v=R instanceof Error?R.message:String(R),E.warn(`state ${b+1}/${l.length} threw`,R)}M({type:"AUDIT_PROGRESS_EVENT",current:b+1,total:l.length,currentState:C,componentId:k||void 0}),await Je({})}if(I&&k){const b={pseudoState:"default",ariaVariation:null,theme:"light",direction:"ltr",breakpoint:{id:"desktop",label:"Desktop",width:1280,height:800,deviceScaleFactor:1,mobile:!1}};try{await ht(r,b,e,n),await new Promise(A=>setTimeout(A,200))}catch(A){E.debug("reference-state drive failed; analyzers will use last matrix state",A)}try{const A=await J(r,{type:"READING_ORDER_REQUEST",selector:e},n);m[0]&&(A.issues.length>0?(m[0].readingOrderIssues=A.issues,m[0].positionAnalysisCapturedAt=b):(_=m[0].axeRulesEvaluated)==null||_.passed.push({ruleId:"wcc-reading-order-clean",wcagCriterion:"1.3.2"}))}catch(A){E.debug("reading-order analysis skipped",A)}try{const A=await J(r,{type:"TAB_ORDER_ANALYZE_REQUEST"},n);m[0]&&(A.issues.length>0?(m[0].tabOrderIssues=A.issues,m[0].positionAnalysisCapturedAt=b):(j=m[0].axeRulesEvaluated)==null||j.passed.push({ruleId:"wcc-tab-order-clean",wcagCriterion:"2.4.3"}))}catch(A){E.debug("tab-order analysis skipped",A)}try{const A=await J(r,{type:"TYPOGRAPHY_ANALYZE_REQUEST",selector:e},n);A.issues.length>0&&m[0]&&(m[0].typographyIssues=A.issues)}catch(A){E.debug("typography analysis skipped",A)}try{const A=await J(r,{type:"CUSTOM_PROPERTY_REQUEST"},n);A.undefined.length>0&&m[0]&&(m[0].undefinedCustomProperties=A.undefined)}catch(A){E.debug("custom-property analysis skipped",A)}try{if(N)await N;else{const A=await ue("aiConfig",{}),T=oe(A);T.enabled&&T.apiKey&&await qe(r,e,n,T,m,k,t)}}catch(A){E.warn("ai augmentation skipped",A)}try{const A=await ue("aiConfig",{}),T=oe(A);if(T.enabled&&T.apiKey&&k&&m[0]){await ft(r,e,n).catch(Y=>E.warn("pre-walkthrough resetState failed",Y));const F=m[0].pageUrl??m[0].scope??"",P=new AbortController,V=setInterval(()=>{t.isCanceled()&&P.abort()},250),B=await $s({tabId:r,componentId:k,pageUrl:F,results:m,signal:P.signal}).finally(()=>clearInterval(V));E.info("interactive walkthroughs complete",B)}}catch(A){E.warn("interactive walkthroughs failed",A)}try{if(k&&m[0]){const A=new AbortController,T=setInterval(()=>{t.isCanceled()&&A.abort()},250),{resolved:F,skipped:P}=await Ps(r,m,A.signal).finally(()=>clearInterval(T));E.info(`pixel contrast sampler: resolved=${F} skipped=${P}`)}}catch(A){E.warn("pixel contrast sampler failed",A)}const C=m.flatMap(A=>A.announcements??[]),R=m.flatMap(A=>A.focusEvents??[]),S=await Et(k,o,{announcements:C,focusEvents:R},i);try{const A=fe(o,void 0,m),T=m.reduce((P,V)=>P+V.durationMs,0),F=await wa(m,A,T);if(F){const P=await dn();if(xa(P,"forensicAnchoring"))try{const V=await va(),B=await ei(F,V??void 0);B&&await Aa(F.componentId,F.capturedAt,B)}catch(V){E.warn("forensic anchoring failed (entry stays local-only)",V)}}}catch(A){E.warn("forensic log recording failed",A)}if(g)try{await Ra({lookupKey:u,fingerprint:g,axeVersion:en,matrixConfigHash:h,disabledRulesHash:f,results:m,delta:S,componentId:k,cachedAt:Date.now()})}catch(A){E.warn("audit-cache write failed",A)}M({type:"AUDIT_COMPLETE_EVENT",componentId:k,results:m,delta:S})}else M({type:"AUDIT_FAILED_EVENT",error:{code:"AXE_FAILED",message:v?`All audits failed. Last error: ${v}`:"No state produced results. Selector may not exist in the audit target frame.",recoverable:!0}});await Ae()}catch(b){E.error("single audit failed",b),M({type:"AUDIT_FAILED_EVENT",componentId:k||void 0,error:la(b)?b.payload:{code:"UNKNOWN",message:String(b),recoverable:!1}})}finally{await de(r,{type:"LIVE_REGIONS_DISARM_REQUEST"},n).catch(()=>{}),await de(r,{type:"FOCUS_TRACE_DISARM_REQUEST"},n).catch(()=>{}),await ft(r,e,n)}}async function zn(e){var t;try{const a=(await chrome.scripting.executeScript({target:{tabId:e,allFrames:!0},world:"MAIN",func:()=>{var s;const r=window;return{detected:typeof r.__STORYBOOK_PREVIEW__=="object",version:(s=r.__STORYBOOK_PREVIEW__)==null?void 0:s.version}}})).find(r=>{var s;return(s=r.result)==null?void 0:s.detected});if(a)return{detected:!0,version:(t=a.result)==null?void 0:t.version,frameId:a.frameId}}catch(n){E.warn("storybook detection scripting failed",n)}return{detected:!1}}async function Bn(e){const t=await Pe(),n=await Jn(t);if(await chrome.storage.local.set({"storybook:lastDetected":{detected:n.kind==="storybook"&&n.detected,version:n.version,detectedAt:new Date().toISOString()},"playground:lastDetected":{kind:n.kind,detected:n.detected,version:n.version,detectedAt:new Date().toISOString()}}),!n.detected){M({type:"AUDIT_FAILED_EVENT",error:{code:"STORYBOOK_NOT_DETECTED",message:"No supported component playground detected on this tab. Open a Storybook (iframe.html / storybook-static), Ladle, or Bit page and try again.",recoverable:!0}});return}if(n.kind==="bit"){M({type:"AUDIT_FAILED_EVENT",error:{code:"STORYBOOK_NOT_DETECTED",message:"Bit detected. Auto-iterate is not yet supported for Bit (compositions are iframe-per-component without a unified listing API). Use the element picker to audit individual compositions one at a time.",recoverable:!0}});return}const a=n.frameId??0,r=n.kind==="storybook"?await si(t,a):await ci(t,a);if(!r.length){E.warn(`${n.kind} detected but no stories returned`),M({type:"AUDIT_FAILED_EVENT",error:{code:"STORYBOOK_NOT_DETECTED",message:`${n.kind==="ladle"?"Ladle":"Storybook"} detected but no stories were returned. Try reloading the page.`,recoverable:!0}});return}await Ne({sessionId:De(),mode:"storybook-all",totalStories:r.length,completedStories:[],startedAt:new Date().toISOString(),lastProgressAt:new Date().toISOString(),state:"running"});for(const s of r){if(e.isCanceled()){E.info(`${n.kind} iteration canceled by user`);break}try{if(n.kind==="storybook"){await ii(t,a,s.id);const c=await de(t,{type:"STORYBOOK_GET_STORY_TARGET_REQUEST",tabId:t},a);await pe(c.selector,e,a)}else await li(t,a,s.id),await pe("#ladle-root",e,a)}catch(c){E.warn(`story ${s.id} failed; continuing with next`,c)}const i=await un();if((i==null?void 0:i.state)==="interrupted"){E.info("storybook iteration interrupted");break}await Je({completedStories:[...(i==null?void 0:i.completedStories)??[],s.id]})}M({type:"SCORECARD_UPDATED_EVENT"}),await Ae()}async function Hn(e){const t=await Pe();let n=[];try{n=(await chrome.scripting.executeScript({target:{tabId:t,allFrames:!0},func:()=>({url:window.location.href})})).map(r=>{var s;return{frameId:r.frameId,url:((s=r.result)==null?void 0:s.url)??""}}).filter(r=>/^https?:/i.test(r.url))}catch(a){E.warn("multi-frame discovery failed",a)}if(n.length===0){M({type:"AUDIT_FAILED_EVENT",error:{code:"CONTENT_SCRIPT_UNREACHABLE",message:"No auditable frames found on this tab. The page may use only chrome-extension:// or data: frames.",recoverable:!0}});return}await Ne({sessionId:De(),mode:"all-frames",totalFrames:n.length,completedFrames:[],startedAt:new Date().toISOString(),lastProgressAt:new Date().toISOString(),state:"running"});for(const a of n){if(e.isCanceled()){E.info("all-frames iteration canceled");break}try{await pe("html",e,a.frameId)}catch(s){E.warn(`frame ${a.frameId} (${a.url.slice(0,80)}) audit failed; continuing`,s)}const r=await un();if((r==null?void 0:r.state)==="interrupted"){E.info("all-frames iteration interrupted");break}await Je({completedFrames:[...(r==null?void 0:r.completedFrames)??[],a.frameId]})}M({type:"SCORECARD_UPDATED_EVENT"}),await Ae()}async function ri(e){var a;const t=await Xe();let n;if(t!==null)try{n=(await chrome.tabs.get(t)).url}catch(r){E.warn("failed to get target tab url",r)}if(!n){M({type:"AUDIT_FAILED_EVENT",error:{code:"NETWORK",message:"Could not determine target URL for parallel scan.",recoverable:!1}});return}await Ne({sessionId:De(),mode:"single-element",scope:"html",startedAt:new Date().toISOString(),lastProgressAt:new Date().toISOString(),state:"running"});try{const r=await Tt(),{runParallelTabAudit:s}=await dt(async()=>{const{runParallelTabAudit:u}=await import("./parallel-tab-flow-CGFwqQRo.js");return{runParallelTabAudit:u}},__vite__mapDeps([0,1,2,3,4,5,6])),i=await s({targetUrl:n,scope:"html",matrix:r,parallelism:4,cancel:e});if(E.info(`parallel scan complete: ${i.results.length} results across ${i.tabsCompleted} tabs (${i.tabsFailed} failed) in ${Math.round(i.parallelDurationMs)}ms`),i.results.length===0){M({type:"AUDIT_FAILED_EVENT",error:{code:"NETWORK",message:`All ${i.tabsFailed} worker tabs failed. Falling back to Full Page scan is recommended.`,recoverable:!0}});return}const c=((a=i.results[0])==null?void 0:a.componentId)??"parallel-scan";if(t!==null)try{const u=await ue("aiConfig",{}),h=oe(u);h.enabled&&h.apiKey&&await qe(t,"html",void 0,h,i.results,c,e)}catch(u){E.warn("parallel scan: ai augmentation skipped",u)}const l=i.results.flatMap(u=>u.violations);let d;try{d=await Et(c,l)}catch(u){E.warn("parallel scan: baseline compare failed",u),d={new:[],persistent:[],fixed:[],newCount:0,persistentCount:0,fixedCount:0,baselineSnapshotMeta:null,comparedAt:new Date().toISOString()}}M({type:"AUDIT_COMPLETE_EVENT",componentId:c,results:i.results,delta:d}),M({type:"SCORECARD_UPDATED_EVENT"})}catch(r){E.warn("parallel scan failed",r),M({type:"AUDIT_FAILED_EVENT",error:{code:"UNKNOWN",message:r instanceof Error?r.message:String(r),recoverable:!0}})}finally{await Ae()}}async function si(e,t){var n;try{return((n=(await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",func:async()=>{var l,d;const r=window.__STORYBOOK_PREVIEW__;if(!r)return[];const s=r.storyStoreValue??r.storyStore;if(!s)return[];typeof s.cacheAllCSFFiles=="function"&&await s.cacheAllCSFFiles();const i=((l=s.extract)==null?void 0:l.call(s))??((d=s.cachedCSFFiles)==null?void 0:d.call(s));if(!i)return[];const c=[];if(i instanceof Map)for(const[u,h]of i){const p=h;c.push({id:String(u),name:p.name??String(u),kind:p.title??p.kind??""})}else if(typeof i=="object"&&i!==null)for(const[u,h]of Object.entries(i)){const p=h;c.push({id:u,name:p.name??u,kind:p.title??p.kind??""})}return c}}))[0])==null?void 0:n.result)??[]}catch(a){return E.warn("listStoriesViaMainWorld failed",a),[]}}async function ii(e,t,n){try{await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",args:[n,3e3],func:async(a,r)=>{const s=new URL(window.location.href);s.searchParams.set("id",a),s.searchParams.set("viewMode","story"),window.location.href!==s.href&&(window.history.pushState({},"",s.href),window.dispatchEvent(new PopStateEvent("popstate"))),await new Promise(i=>{const c=setTimeout(()=>i(),r),l=()=>{clearTimeout(c),i()};window.addEventListener("storyrendered",l,{once:!0})})}})}catch(a){E.warn(`navigateToStoryViaMainWorld(${n}) failed`,a)}}async function qn(e){var t;try{const a=(await chrome.scripting.executeScript({target:{tabId:e,allFrames:!0},world:"MAIN",func:()=>{const r=document.getElementById("ladle-root")!==null,s=document.title==="Ladle",i=!!document.querySelector('link[href*="ladle.css"]');return{detected:r&&(s||i),version:void 0}}})).find(r=>{var s;return(s=r.result)==null?void 0:s.detected});if(a)return{detected:!0,version:(t=a.result)==null?void 0:t.version,frameId:a.frameId}}catch(n){E.warn("ladle detection scripting failed",n)}return{detected:!1}}async function ci(e,t){var n;try{return((n=(await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",func:async()=>{try{const r=await fetch(new URL("/meta.json",window.location.href).href,{credentials:"same-origin"});if(!r.ok)return[];const i=(await r.json()).stories??{};return Object.entries(i).map(([c,l])=>({id:c,name:l.name??c,kind:(l.levels??[]).join(" / ")}))}catch{return[]}}}))[0])==null?void 0:n.result)??[]}catch(a){return E.warn("listLadleStoriesViaMainWorld failed",a),[]}}async function li(e,t,n){try{await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",args:[n,3e3],func:async(a,r)=>{const s=new URL(window.location.href);s.searchParams.set("story",a),window.location.href!==s.href&&(window.history.pushState({},"",s.href),window.dispatchEvent(new PopStateEvent("popstate"))),await new Promise(i=>setTimeout(i,Math.min(r,500)))}})}catch(a){E.warn(`navigateToLadleStoryViaMainWorld(${n}) failed`,a)}}async function Yn(e){try{return(await chrome.scripting.executeScript({target:{tabId:e,allFrames:!0},world:"MAIN",func:()=>{const n=window.location.href;return/bit\.dev|bit-dev|bitsrc|\/~aspect|\/composition/i.test(n)}})).some(n=>n.result===!0)}catch{return!1}}async function Jn(e){const t=await zn(e);if(t.detected)return{kind:"storybook",...t};const n=await qn(e);return n.detected?{kind:"ladle",...n}:await Yn(e)?{kind:"bit",detected:!0}:{kind:null,detected:!1}}const di=200,ui=6e4;function tn(e,t){try{return new URL(e).origin===new URL(t).origin}catch{return!1}}function pi(e,t){try{const n=new URL(e,t);return n.hash="",n.toString()}catch{return null}}async function hi(e,t,n){await chrome.tabs.update(e,{url:t}),await new Promise((a,r)=>{const s=setTimeout(()=>{chrome.tabs.onUpdated.removeListener(i),r(new Error(`navigation timeout (${n}ms): ${t}`))},n);function i(c,l){c===e&&l.status==="complete"&&(clearTimeout(s),chrome.tabs.onUpdated.removeListener(i),a())}chrome.tabs.onUpdated.addListener(i)}),await new Promise(a=>setTimeout(a,400))}async function fi(e){var t;try{return((t=(await chrome.scripting.executeScript({target:{tabId:e},func:()=>Array.from(document.querySelectorAll("a[href]")).map(a=>a.getAttribute("href")).filter(a=>typeof a=="string"&&!a.startsWith("javascript:"))}))[0])==null?void 0:t.result)??[]}catch(n){return E.debug("link discovery failed",n),[]}}async function Xn(e,t,n){var p;const a=await Pe(),r=Math.min(t.maxPages??25,di),s=t.includeRegex?nn(t.includeRegex):null,i=t.excludeRegex?nn(t.excludeRegex):null,c=new Date().toISOString(),l=Date.now(),d=[e],u=new Set,h=[];await Ne({sessionId:De(),mode:"storybook-all",scope:e,startedAt:c,lastProgressAt:new Date().toISOString(),state:"running"});try{for(;d.length>0&&u.size<r;){if(n.isCanceled()){E.info("site crawl canceled by user");break}const m=d.shift();if(!m||u.has(m)||!tn(m,e)||s&&!s.test(m)||i&&i.test(m))continue;u.add(m);const k=u.size,v=Date.now();M({type:"SITE_CRAWL_PROGRESS_EVENT",current:k,total:Math.min(r,u.size+d.length),url:m,status:"auditing"});try{await hi(a,m,ui);const I=await jn(a,0);if(!I.ok)throw new Error(Kn(I));await J(a,{type:"WARMUP_REQUEST"},0).catch(()=>{});const $=await J(a,{type:"AUDIT_REQUEST",selector:"html",currentState:{pseudoState:"default",ariaVariation:null,theme:"light",direction:"ltr",breakpoint:{id:"desktop",label:"Desktop",width:1280,height:800,deviceScaleFactor:1,mobile:!1}}},0),N=Date.now()-v;if($.success&&$.result)h.push({url:m,results:[$.result],delta:null,componentId:$.result.componentId,durationMs:N}),M({type:"SITE_CRAWL_PROGRESS_EVENT",current:k,total:Math.min(r,u.size+d.length),url:m,status:"completed",violations:$.result.violations.length});else{const _=((p=$.error)==null?void 0:p.message)??"audit returned no result";h.push({url:m,results:[],delta:null,componentId:m,durationMs:N,error:_}),M({type:"SITE_CRAWL_PROGRESS_EVENT",current:k,total:Math.min(r,u.size+d.length),url:m,status:"failed",error:_})}const x=await fi(a);for(const _ of x){const j=pi(_,m);j&&tn(j,e)&&!u.has(j)&&!d.includes(j)&&d.push(j)}}catch(I){const $=Date.now()-v,N=I instanceof Error?I.message:String(I);h.push({url:m,results:[],delta:null,componentId:m,durationMs:$,error:N}),M({type:"SITE_CRAWL_PROGRESS_EVENT",current:k,total:Math.min(r,u.size+d.length),url:m,status:"failed",error:N})}await Je({})}const f=new Date().toISOString(),g=Date.now()-l,o=vs(e,h,c,f,g);M({type:"SITE_CRAWL_COMPLETE_EVENT",report:o}),await Ae()}catch(f){E.error("site crawl failed",f),M({type:"SITE_CRAWL_FAILED_EVENT",error:{code:"UNKNOWN",message:String(f),recoverable:!1}}),await Ae()}}function nn(e){try{return new RegExp(e)}catch{return null}}const lt=5*6e4,an=9e4,on={altText:"Verifying alt text…",headings:"Judging heading quality…",sensory:"Scanning for sensory-only instructions…",aria:"Reviewing ARIA semantics…",imageOfText:"Detecting baked-in text in images…",colorOnlyMeaning:"Checking for color-only meaning…",genericLinkText:"Reviewing link clarity…",wallOfText:"Detecting walls of text…",languageMismatch:"Verifying page language…"};async function qe(e,t,n,a,r,s,i){let c;try{c=await J(e,{type:"AI_CANDIDATES_REQUEST",selector:t},n)}catch(S){E.debug("ai candidates fetch failed",S);return}const l=r[0];if(!l)return;const d={componentId:s,currentState:l.state,axeVersion:l.axeVersion},u={at:new Date().toISOString(),componentId:s,pageUrl:l.pageUrl??t,enabledChecks:a.enabledChecks,candidateCounts:{images:c.images.length,headings:c.headings.length,instructions:c.instructions.length,ariaElements:c.ariaElements.length,links:c.links.length,longTextBlocks:c.longTextBlocks.length,colorOnlyRegions:c.colorOnlyRegions.length,languageInfo:c.languageInfo?"present":"null"}};E.info("ai augmentation gate snapshot",JSON.stringify(u));try{await chrome.storage.local.set({aiDiagnosticLatest:u})}catch(S){E.warn("failed to persist ai diagnostic to storage.local",S)}try{const S=JSON.stringify(u,null,2),A=`data:application/json;charset=utf-8,${encodeURIComponent(S)}`,T=u.at.replace(/[:.]/g,"-"),F=await chrome.downloads.download({url:A,filename:`wcagcheckr-ai-diag-${T}.json`,saveAs:!1,conflictAction:"uniquify"});E.info(`ai diagnostic download started (id=${F})`)}catch(S){E.warn("failed to write ai diagnostic to Downloads (storage.local copy is still available under aiDiagnosticLatest)",S)}const h=[];if(a.enabledChecks.altText&&h.push({key:"altText",run:S=>{const A=c.images.map(T=>({imageUrl:T.imageUrl,alt:T.alt,selector:T.selector,outerHTML:T.outerHTML,surroundingContext:T.surroundingContext,...d}));return xs(A,a,S)}}),a.enabledChecks.headings&&h.push({key:"headings",run:S=>Ls(c.headings,d,a,S)}),a.enabledChecks.sensory&&h.push({key:"sensory",run:S=>Fs(c.instructions,d,a,S)}),a.enabledChecks.aria&&h.push({key:"aria",run:S=>Gs(c.ariaElements,d,a,S)}),a.enabledChecks.imageOfText&&h.push({key:"imageOfText",run:S=>{const A=c.images.map(T=>({imageUrl:T.imageUrl,accessibleName:T.alt||void 0,selector:T.selector,outerHTML:T.outerHTML,pixelArea:T.pixelArea}));return Ks(A,d,a,S)}}),a.enabledChecks.colorOnlyMeaning&&h.push({key:"colorOnlyMeaning",run:S=>zs(c.colorOnlyRegions,d,a,S)}),a.enabledChecks.genericLinkText&&h.push({key:"genericLinkText",run:S=>Vs(c.links,d,a,S)}),a.enabledChecks.wallOfText&&h.push({key:"wallOfText",run:S=>Ws(c.longTextBlocks,d,a,S)}),a.enabledChecks.languageMismatch){const S=c.languageInfo;h.push({key:"languageMismatch",run:A=>js(S?[S]:[],d,a,A)})}if(h.length===0)return;let p=0,f=0,g=!1;const o={};let m=0,k=0,v=0;const I=[];let $=!1,N=!1;function x(S,A){if(o[S]=(o[S]??0)+A.totalCostUsd,p+=A.totalCostUsd,f+=A.findings.length,g=g||A.capExceeded,m++,A.errors.length>0){v++;for(const T of A.errors)I.includes(T)||I.push(T)}else k++;E.info(`ai ${S}: ${A.findings.length} findings, $${A.totalCostUsd.toFixed(4)}, errs=${A.errors.length}`),A.capExceeded&&E.warn(`ai cost cap exceeded after $${A.totalCostUsd.toFixed(4)} during ${S}`)}const _=(async()=>{for(let S=0;S<h.length;S++){if(i!=null&&i.isCanceled()){N=!0;break}const A=h[S];M({type:"AI_AUGMENTATION_PROGRESS_EVENT",componentId:s,currentCheck:A.key,currentCheckLabel:on[A.key],current:S+1,total:h.length});const T=new AbortController,F=setTimeout(()=>T.abort(),an),P=i?setInterval(()=>{i.isCanceled()&&T.abort()},500):null;let V;try{V=await A.run({signal:T.signal,onProgress:(B,Y)=>{Y>0&&M({type:"AI_AUGMENTATION_PROGRESS_EVENT",componentId:s,currentCheck:A.key,currentCheckLabel:on[A.key],current:S+1,total:h.length,candidatesDone:B,candidatesTotal:Y})}}),T.signal.aborted&&(i!=null&&i.isCanceled())?V.errors=[...V.errors,`${A.key} canceled by user`]:T.signal.aborted}catch(B){const Y=B instanceof Error?B.message:String(B),re=T.signal.aborted,ge=re&&(i!=null&&i.isCanceled())?`${A.key} canceled by user`:re?`${A.key} hit per-check ${an/1e3}s cap — analyzer aborted`:`${A.key} threw: ${Y}`;V={findings:[],totalCostUsd:0,capExceeded:!1,errors:[ge]}}finally{clearTimeout(F),P&&clearInterval(P)}V.findings.length>0&&(l.violations=[...l.violations,...V.findings]),x(A.key,V)}})(),j=new Promise(S=>{setTimeout(()=>S("timeout"),lt)});await Promise.race([_.then(()=>"done"),j])==="timeout"&&($=!0,E.warn(`ai augmentation hit global ${lt/1e3}s timeout after ${m}/${h.length} checks`));let C;if(v>0||$||N){$?C=`AI augmentation timed out after ${lt/1e3}s`:N?C="AI augmentation canceled":C=I[0]??"AI analyzer error";const S=k===0?"total":"partial";M({type:"AI_AUGMENTATION_FAILED_EVENT",componentId:s,severity:S,reason:C,checksAttempted:m,checksSucceeded:k,checksErrored:v,errorDetails:$||N?[C,...I.slice(0,4)]:I.slice(0,5)}),E.warn(`ai augmentation ${S} failure: ${v}/${m} checks errored — ${C}`)}(p>0||f>0||C)&&await Oa({at:new Date().toISOString(),pageUrl:l.pageUrl??t,componentId:s,totalCostUsd:p,byCheck:o,findingsCount:f,capExceeded:g,failureReason:C})}const gi=Object.freeze(Object.defineProperty({__proto__:null,detectBit:Yn,detectLadleAcrossFrames:qn,detectPlayground:Jn,detectStorybookAcrossFrames:zn,getActiveTabId:Pe,getMatrixSettings:Tt,runAiAugmentation:qe,runAllFramesAudit:Hn,runParallelScan:ri,runSingleElementAudit:pe,runSiteCrawl:Xn,runStorybookAllAudit:Bn},Symbol.toStringTag,{value:"Module"})),Z=D("service-worker");ra("service-worker");Eo();var Ge,rn;(rn=(Ge=chrome.sidePanel)==null?void 0:Ge.setPanelBehavior)==null||rn.call(Ge,{openPanelOnActionClick:!0}).catch(e=>Z.warn("setPanelBehavior failed",e));Ha();io();Sa();ro().catch(e=>Z.warn("initial cloud pull failed",e));(async()=>(await So(),kn()))();ko();Io();Or();Vr();Kr();cr();Hr();ms();self.addEventListener("unhandledrejection",e=>{const t=e.reason;sa(t instanceof Error?t:new Error(String(t)))});let Ye=!1;chrome.runtime.onConnect.addListener(e=>{if(e.name==="audit-keepalive"){Z.debug("keepalive connected"),e.onDisconnect.addListener(async()=>{Z.debug("keepalive disconnected"),await ka()});return}if(e.name==="sidepanel-tracker"){Ye=!0,Z.debug("chrome.sidePanel opened"),e.onDisconnect.addListener(async()=>{Ye=!1,Z.debug("chrome.sidePanel closed — restoring in-page overlay"),await Ct()});return}});let It=!1;const be={isCanceled:()=>It};async function Qn(){const e=await Xe();if(e)try{await de(e,{type:"SIDEBAR_HIDE_REQUEST"})}catch(t){Z.warn("sidebar hide failed",t)}}async function Ct(){const e=await Xe();if(e)try{await de(e,{type:"SIDEBAR_SHOW_REQUEST"})}catch(t){Z.warn("sidebar show failed",t)}}W("START_AUDIT",async e=>{It=!1,await Qn();let t;if(e.mode==="single-element"){if(!e.scope)throw new Error("START_AUDIT single-element requires scope");t=pe(e.scope,be,e.frameId,e.matrixOverride)}else if(e.mode==="full-page")t=pe("html",be,0,e.matrixOverride);else if(e.mode==="quick-scan"){const{QUICK_SCAN_MATRIX_SETTINGS:n}=await dt(async()=>{const{QUICK_SCAN_MATRIX_SETTINGS:a}=await import("./state-BIsozXtc.js").then(r=>r.e);return{QUICK_SCAN_MATRIX_SETTINGS:a}},__vite__mapDeps([5,1]));t=pe(e.scope??"html",be,e.frameId??0,n)}else if(e.mode==="all-frames")t=Hn(be);else if(e.mode==="parallel-scan"){const{runParallelScan:n}=await dt(async()=>{const{runParallelScan:a}=await Promise.resolve().then(()=>gi);return{runParallelScan:a}},void 0);t=n(be)}else t=Bn(be);t.catch(n=>{Z.error("audit failed",n);const a=n instanceof Error?n.message:String(n),r=/no audit target tab/i.test(a);M({type:"AUDIT_FAILED_EVENT",error:{code:r?"RESTRICTED_URL":"UNKNOWN",message:r?"No audit target tab found. Open a regular http(s) page (not chrome:// or about:blank) and try again.":`Audit failed: ${a}`,recoverable:!0}})}).finally(()=>{Ye||Ct()})});W("CANCEL_AUDIT",async()=>{It=!0,Z.info("cancel requested")});let Rt=!1;const mi={isCanceled:()=>Rt};W("START_SITE_CRAWL",async e=>{Rt=!1,await Qn(),Xn(e.startUrl,{maxPages:e.maxPages,includeRegex:e.includeRegex,excludeRegex:e.excludeRegex},mi).catch(t=>Z.error("site crawl failed",t)).finally(()=>{Ye||Ct()})});W("CANCEL_SITE_CRAWL",async()=>{Rt=!0,Z.info("site crawl cancel requested")});W("OPEN_SETTINGS",()=>{chrome.runtime.openOptionsPage()});Z.info("service worker ready");export{jn as a,ht as d,As as e,ft as r};