@wcag-checkr/ci 1.0.2 → 1.0.4

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 (25) hide show
  1. package/dist/assets/ErrorBoundary-B069Styg.js +594 -0
  2. package/dist/assets/ai-usage-log-CYuyLsU4.js +1 -0
  3. package/dist/assets/content-script.ts-DYFeeoQ7.js +217 -0
  4. package/dist/assets/{content-script.ts-loader-BhZWdOHh.js → content-script.ts-loader-BfRK3cgW.js} +1 -1
  5. package/dist/assets/{copy-ai-fixer-prompt-BajmIyOs.js → copy-ai-fixer-prompt-2854S1dW.js} +2 -2
  6. package/dist/assets/{devtools-panel-DEQIl-1V.js → devtools-panel-DO-Ue7xN.js} +1 -1
  7. package/dist/assets/{options-D_6nRFjj.js → options-DTbm8Du2.js} +1 -1
  8. package/dist/assets/{parallel-tab-flow-CTenSMHR.js → parallel-tab-flow-C9KmXHUE.js} +1 -1
  9. package/dist/assets/{rubric-config-client-DTzQUfN_.js → rubric-config-client-CtLnVY3l.js} +1 -1
  10. package/dist/assets/{scheduled-audit-runner-BALgZTaj.js → scheduled-audit-runner-AQwZVFus.js} +254 -235
  11. package/dist/assets/{service-worker.ts-CgganlSn.js → service-worker.ts-DQH3-r3D.js} +2 -2
  12. package/dist/assets/side-panel-CJB1fZKF.js +4 -0
  13. package/dist/assets/{site-report-renderer-4_2qtKsd.js → site-report-renderer-JtIJmmtx.js} +5 -5
  14. package/dist/assets/{styles-B_kTykZT.js → styles-CLV9y-Ki.js} +1 -1
  15. package/dist/assets/{tier-config-client-85XOCE5d.js → tier-config-client-CT-oOti3.js} +1 -1
  16. package/dist/devtools/panel.html +5 -5
  17. package/dist/manifest.json +3 -3
  18. package/dist/options/options.html +4 -4
  19. package/dist/service-worker-loader.js +1 -1
  20. package/dist/side-panel/side-panel.html +8 -8
  21. package/package.json +1 -1
  22. package/dist/assets/ErrorBoundary-DEhp5rUw.js +0 -594
  23. package/dist/assets/ai-usage-log-CYyG5MT2.js +0 -1
  24. package/dist/assets/content-script.ts-DUFtdkRn.js +0 -217
  25. package/dist/assets/side-panel-BqObeO8B.js +0 -4
@@ -1,7 +1,7 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/parallel-tab-flow-CTenSMHR.js","assets/crash-reporter-KD5f4utB.js","assets/preload-helper-D7HrI6pR.js","assets/diff-RnwNJg4F.js","assets/ai-usage-log-CYyG5MT2.js"])))=>i.map(i=>d[i]);
2
- var Ti=Object.defineProperty;var Ei=(e,t,a)=>t in e?Ti(e,t,{enumerable:!0,configurable:!0,writable:!0,value:a}):e[t]=a;var Mt=(e,t,a)=>Ei(e,typeof t!="symbol"?t+"":t,a);import{_ as dn}from"./preload-helper-D7HrI6pR.js";import{c as te,f as le,o as Se,k as q,r as ke,h as At,j as Wo}from"./crash-reporter-KD5f4utB.js";import{a as de,m as Un,d as Ri,T as Oi,b as Wn,c as un,g as sa,i as Vn}from"./diff-RnwNJg4F.js";import{i as Mi,j as Re,I as Di,T as hn,w as St,u as ca,y as ve,e as Ba,J as Li,K as Pi,L as Ni,M as Fi,N as _i,O as Ui,b as Wi,g as Vi,r as ji,f as Gi}from"./ai-usage-log-CYyG5MT2.js";const za=(e,t)=>t.some(a=>e instanceof a);let jn,Gn;function Hi(){return jn||(jn=[IDBDatabase,IDBObjectStore,IDBIndex,IDBCursor,IDBTransaction])}function Bi(){return Gn||(Gn=[IDBCursor.prototype.advance,IDBCursor.prototype.continue,IDBCursor.prototype.continuePrimaryKey])}const qa=new WeakMap,Ta=new WeakMap,la=new WeakMap;function zi(e){const t=new Promise((a,n)=>{const o=()=>{e.removeEventListener("success",r),e.removeEventListener("error",i)},r=()=>{a(Ge(e.result)),o()},i=()=>{n(e.error),o()};e.addEventListener("success",r),e.addEventListener("error",i)});return la.set(t,e),t}function qi(e){if(qa.has(e))return;const t=new Promise((a,n)=>{const o=()=>{e.removeEventListener("complete",r),e.removeEventListener("error",i),e.removeEventListener("abort",i)},r=()=>{a(),o()},i=()=>{n(e.error||new DOMException("AbortError","AbortError")),o()};e.addEventListener("complete",r),e.addEventListener("error",i),e.addEventListener("abort",i)});qa.set(e,t)}let Ka={get(e,t,a){if(e instanceof IDBTransaction){if(t==="done")return qa.get(e);if(t==="store")return a.objectStoreNames[1]?void 0:a.objectStore(a.objectStoreNames[0])}return Ge(e[t])},set(e,t,a){return e[t]=a,!0},has(e,t){return e instanceof IDBTransaction&&(t==="done"||t==="store")?!0:t in e}};function Vo(e){Ka=e(Ka)}function Ki(e){return Bi().includes(e)?function(...t){return e.apply(Ya(this),t),Ge(this.request)}:function(...t){return Ge(e.apply(Ya(this),t))}}function Yi(e){return typeof e=="function"?Ki(e):(e instanceof IDBTransaction&&qi(e),za(e,Hi())?new Proxy(e,Ka):e)}function Ge(e){if(e instanceof IDBRequest)return zi(e);if(Ta.has(e))return Ta.get(e);const t=Yi(e);return t!==e&&(Ta.set(e,t),la.set(t,e)),t}const Ya=e=>la.get(e);function pn(e,t,{blocked:a,upgrade:n,blocking:o,terminated:r}={}){const i=indexedDB.open(e,t),c=Ge(i);return n&&i.addEventListener("upgradeneeded",d=>{n(Ge(i.result),d.oldVersion,d.newVersion,Ge(i.transaction),d)}),a&&i.addEventListener("blocked",d=>a(d.oldVersion,d.newVersion,d)),c.then(d=>{r&&d.addEventListener("close",()=>r()),o&&d.addEventListener("versionchange",l=>o(l.oldVersion,l.newVersion,l))}).catch(()=>{}),c}const Ji=["get","getKey","getAll","getAllKeys","count"],Xi=["put","add","delete","clear"],Ea=new Map;function Hn(e,t){if(!(e instanceof IDBDatabase&&!(t in e)&&typeof t=="string"))return;if(Ea.get(t))return Ea.get(t);const a=t.replace(/FromIndex$/,""),n=t!==a,o=Xi.includes(a);if(!(a in(n?IDBIndex:IDBObjectStore).prototype)||!(o||Ji.includes(a)))return;const r=async function(i,...c){const d=this.transaction(i,o?"readwrite":"readonly");let l=d.store;return n&&(l=l.index(c.shift())),(await Promise.all([l[a](...c),o&&d.done]))[0]};return Ea.set(t,r),r}Vo(e=>({...e,get:(t,a,n)=>Hn(t,a)||e.get(t,a,n),has:(t,a)=>!!Hn(t,a)||e.has(t,a)}));const Qi=["continue","continuePrimaryKey","advance"],Bn={},Ja=new WeakMap,jo=new WeakMap,Zi={get(e,t){if(!Qi.includes(t))return e[t];let a=Bn[t];return a||(a=Bn[t]=function(...n){Ja.set(this,jo.get(this)[t](...n))}),a}};async function*es(...e){let t=this;if(t instanceof IDBCursor||(t=await t.openCursor(...e)),!t)return;t=t;const a=new Proxy(t,Zi);for(jo.set(a,t),la.set(a,Ya(t));t;)yield a,t=await(Ja.get(a)||t.continue()),Ja.delete(a)}function zn(e,t){return t===Symbol.asyncIterator&&za(e,[IDBIndex,IDBObjectStore,IDBCursor])||t==="iterate"&&za(e,[IDBIndex,IDBObjectStore])}Vo(e=>({...e,get(t,a,n){return zn(t,a)?es:e.get(t,a,n)},has(t,a){return zn(t,a)||e.has(t,a)}}));const ts="wcag-component-auditor",as=3;let Xe=null;function se(){return Xe||(Xe=pn(ts,as,{upgrade(e,t){if(t<1){const a=e.createObjectStore("baselines",{keyPath:"componentId"});a.createIndex("byUrl","snapshotMeta.url"),a.createIndex("byDate","lastUpdated");const n=e.createObjectStore("audit-history",{keyPath:"auditId",autoIncrement:!0});n.createIndex("byComponentId","componentId"),n.createIndex("byDate","runDate")}if(t<2&&e.createObjectStore("site-crawl-per-url",{keyPath:"slot"}),t<3){e.createObjectStore("scheduled-audits",{keyPath:"id"});const a=e.createObjectStore("scheduled-audit-results",{keyPath:"resultId",autoIncrement:!0});a.createIndex("byScheduleId","scheduleId"),a.createIndex("byRanAt","ranAt")}}})),Xe}async function ns(){Xe&&(await Xe).close(),Xe=null}async function da(e){return(await se()).get("baselines",e)}async function fn(e){await(await se()).put("baselines",e)}async function Go(e){await(await se()).delete("baselines",e)}async function Ho(){return(await se()).getAll("baselines")}async function os(e){return(await se()).getAllFromIndex("baselines","byUrl",e)}async function rs(e){return(await se()).add("audit-history",e)}async function is(e){return(await se()).getAllFromIndex("audit-history","byComponentId",e)}async function Bo(){return(await se()).get("site-crawl-per-url","current")}async function zo(e){await(await se()).put("site-crawl-per-url",{...e,slot:"current"})}async function gn(){await(await se()).delete("site-crawl-per-url","current")}async function qo(){return(await se()).getAll("scheduled-audits")}async function Ko(e){return(await se()).get("scheduled-audits",e)}async function mn(e){await(await se()).put("scheduled-audits",e)}async function ss(e){await(await se()).delete("scheduled-audits",e)}async function Yo(e){return(await se()).add("scheduled-audit-results",e)}async function cs(e){return(await se()).getAllFromIndex("scheduled-audit-results","byScheduleId",e)}async function ls(){return(await se()).getAll("scheduled-audit-results")}async function Jo(e,t=50){const a=await se(),n=await a.getAllFromIndex("scheduled-audit-results","byScheduleId",e);if(n.length<=t)return;n.sort((i,c)=>c.ranAt.localeCompare(i.ranAt));const o=n.slice(t),r=a.transaction("scheduled-audit-results","readwrite");for(const i of o)i.resultId!==void 0&&await r.store.delete(i.resultId);await r.done}async function ds(e){const a=(await se()).transaction("scheduled-audit-results","readwrite");let o=await a.store.index("byScheduleId").openCursor(IDBKeyRange.only(e));for(;o;)await o.delete(),o=await o.continue();await a.done}const us=Object.freeze(Object.defineProperty({__proto__:null,_resetDbForTesting:ns,appendAuditHistory:rs,appendScheduledAuditResult:Yo,deleteBaseline:Go,deleteScheduledAudit:ss,deleteScheduledAuditResultsForScheduleId:ds,deleteSiteCrawlPerUrl:gn,getBaseline:da,getDb:se,getScheduledAudit:Ko,getSiteCrawlPerUrl:Bo,listAllScheduledAuditResults:ls,listAuditHistoryForComponent:is,listBaselines:Ho,listBaselinesByUrl:os,listScheduledAuditResultsByScheduleId:cs,listScheduledAudits:qo,pruneScheduledAuditResults:Jo,putScheduledAudit:mn,setBaseline:fn,setSiteCrawlPerUrl:zo},Symbol.toStringTag,{value:"Module"})),Gt="siteCrawlReport";async function bn(){var a;if(typeof chrome>"u"||!((a=chrome.storage)!=null&&a.local))return null;const t=(await chrome.storage.local.get([Gt]))[Gt];return t&&typeof t=="object"?t:null}async function Xo(e){var t;if(!(typeof chrome>"u"||!((t=chrome.storage)!=null&&t.local))){if(e===null){await chrome.storage.local.remove([Gt]),await gn().catch(()=>{});return}await chrome.storage.local.set({[Gt]:e})}}async function Ht(){try{const e=await Bo();return(e==null?void 0:e.perUrl)??null}catch{return null}}async function Qo(e,t){if(e===null){await gn();return}await zo({startUrl:(t==null?void 0:t.startUrl)??"",finishedAt:(t==null?void 0:t.finishedAt)??new Date().toISOString(),perUrl:e})}const Rh=Object.freeze(Object.defineProperty({__proto__:null,loadSiteCrawlPerUrlResults:Ht,loadSiteCrawlReport:bn,saveSiteCrawlPerUrlResults:Qo,saveSiteCrawlReport:Xo},Symbol.toStringTag,{value:"Module"}));async function Fe(){return!1}function Oh(e){return!1}const Ct="HEADLESS_BUILD_LOCKOUT",He="wcagcheckr is in private build phase. A team license is required for access. If you believe you should have access, contact cliff@locustware.com.";async function Zo(){const e=await chrome.tabs.query({currentWindow:!0}),t=e.find(n=>n.active);if(t!=null&&t.id&&qn(t.url))return t.id;const a=e.find(n=>n.id&&qn(n.url));return(a==null?void 0:a.id)??null}function qn(e){return typeof e=="string"&&/^https?:\/\//.test(e)}const Xa="rubricConfigCache",hs=7*24*60*60*1e3;function Mh(e){const t=Date.now()-e.fetchedAt;return t>=hs?"expired":t>=60*60*1e3?"stale":"fresh"}async function Dh(){var a;if(typeof chrome>"u"||!((a=chrome.storage)!=null&&a.local))return null;const t=(await chrome.storage.local.get(Xa))[Xa];return!t||typeof t!="object"?null:t}async function Lh(e){var t;typeof chrome>"u"||!((t=chrome.storage)!=null&&t.local)||await chrome.storage.local.set({[Xa]:e})}let er=null;function Ph(e){er=e}function tr(){return er}const Qa="acknowledgedFindings";async function ua(){var a;if(typeof chrome>"u"||!((a=chrome.storage)!=null&&a.local))return[];const t=(await chrome.storage.local.get([Qa]))[Qa];return Array.isArray(t)?t:[]}async function ha(e){const t=await ua();return new Set(t.filter(a=>a.componentId===e).map(a=>a.matchKey))}async function ps(e){const a=(await ua()).filter(n=>!(n.componentId===e.componentId&&n.matchKey===e.matchKey));a.push({...e,acknowledgedAt:new Date().toISOString()}),await ar(a)}async function fs(e,t){const n=(await ua()).filter(o=>!(o.componentId===e&&o.matchKey===t));await ar(n)}async function ar(e){var t;typeof chrome>"u"||!((t=chrome.storage)!=null&&t.local)||await chrome.storage.local.set({[Qa]:e})}const Nh=Object.freeze(Object.defineProperty({__proto__:null,acknowledgeFinding:ps,loadAcknowledged:ua,loadAcknowledgedKeysForComponent:ha,unacknowledgeFinding:fs},Symbol.toStringTag,{value:"Module"})),gs=/:nth-(?:last-)?(?:child|of-type)\([^)]+\)/g,ms=/\[id="[^"]*"\]/g,bs=/\[data-testid="[^"]*"\]/g,ys=/#[\w-]*\d[\w-]*/g,ws=/\[href(?:[\^$*~|]?=("|')[^"']*\1)?\]/g,vs=/\[aria-label(?:[\^$*~|]?=("|')[^"']*\1)?\]/g,As=new Set(["region","landmark-one-main","page-has-heading-one","landmark-no-duplicate-banner","landmark-no-duplicate-contentinfo","landmark-no-duplicate-main","landmark-main-is-top-level","landmark-banner-is-top-level","landmark-contentinfo-is-top-level","landmark-complementary-is-top-level","landmark-unique","bypass","document-title","html-has-lang","meta-viewport"]);function ks(e){return e&&e.replace(gs,"").replace(ms,"").replace(bs,"").replace(ys,"").replace(ws,"").replace(vs,"").replace(/\s+/g," ").trim()}function Oe(e,t){return As.has(e)?`${e}::__structural__`:`${e}::${ks(t)}`}const Kn={minor:0,moderate:1,serious:2,critical:3};function nr(e){const t=new Map;for(const a of e){const n=Oe(a.ruleId,a.target.selector),o=t.get(n);if(!o){t.set(n,a);continue}(Kn[a.impact]??0)>(Kn[o.impact]??0)&&t.set(n,a)}return Array.from(t.values())}const or=[{id:"contrast",label:"Color & contrast",axeRules:["color-contrast","color-contrast-enhanced","link-in-text-block"],igtWorkflowIds:["visual-indicators"]},{id:"alt-text",label:"Alt text",axeRules:["image-alt","input-image-alt","object-alt","svg-img-alt","area-alt","role-img-alt"],igtWorkflowIds:[]},{id:"forms",label:"Forms",axeRules:["label","select-name","aria-input-field-name","aria-toggle-field-name","form-field-multiple-labels","label-content-name-mismatch","autocomplete-valid","label-title-only","wcc-onfocus-context-change","wcc-oninput-context-change","wcc-cognitive-auth-challenge"],igtWorkflowIds:["forms"]},{id:"keyboard",label:"Keyboard",axeRules:["nested-interactive","tabindex","frame-focusable-content","no-focusable-content","focus-order-semantics","wcc-gesture-no-alternative","wcc-drag-no-alternative"],igtWorkflowIds:["keyboard","focus-management"]},{id:"tab-order",label:"Tab order",axeRules:[],igtWorkflowIds:[],verifiedByWorkflowIds:["keyboard"],heuristic:"tab-order"},{id:"screen-reader",label:"Screen reader / ARIA",axeRules:["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","button-name","input-button-name","link-name","frame-title","presentation-role-conflict","aria-allowed-role","aria-prohibited-attr","aria-text","aria-deprecated-role","aria-conditional-attr","aria-braille-equivalent","empty-button","empty-heading","empty-link"],igtWorkflowIds:["screen-reader"]},{id:"reading-order",label:"Reading order",axeRules:[],igtWorkflowIds:[],verifiedByWorkflowIds:["page-structure"],heuristic:"reading-order"},{id:"page-structure",label:"Page structure",axeRules:["heading-order","page-has-heading-one","landmark-one-main","landmark-no-duplicate-banner","landmark-no-duplicate-contentinfo","landmark-no-duplicate-main","region","bypass","empty-table-header","landmark-banner-is-top-level","landmark-complementary-is-top-level","landmark-contentinfo-is-top-level","landmark-main-is-top-level","landmark-unique"],igtWorkflowIds:["page-structure"]}];function Fh(e,t){const a=new Set;for(const n of t)n.wcagCriterion===e&&a.add(n.ruleId);if(a.size===0)return null;for(const n of or)for(const o of n.axeRules)if(a.has(o))return n.id;return null}function Is(e){const t=or.map(o=>Ss(o,e)),a=xs(t.map(o=>o.letter)),n=t.filter(o=>o.letter==="D"||o.letter==="F");return{overallLetter:a,subGrades:t,warningAreas:n,isLawsuitRisk:n.length>0}}function xs(e){const t=e.filter(i=>i==="A").length,a=e.filter(i=>i==="C").length,n=e.filter(i=>i==="D").length,o=e.filter(i=>i==="F").length,r=e.length-t;return t===0&&n+o>a?"F":r===0?"A":r<=3?"B":r<=6?"C":"D"}function Ss(e,t){const a=new Set(e.axeRules),n=t.violations.filter(M=>a.has(M.ruleId)),o=t.manualRuns.filter(M=>e.igtWorkflowIds.includes(M.workflowId)),r=e.igtWorkflowIds.map(M=>t.workflows.find(D=>D.id===M)).filter(M=>M!=null);let i=r.length>0;for(const M of r){const D=o.find(O=>O.workflowId===M.id);if((D?Object.keys(D.steps).length:0)!==M.steps.length){i=!1;break}}const c=o.some(M=>Object.keys(M.steps).length>0),d=e.verifiedByWorkflowIds??[],l=t.manualRuns.filter(M=>d.includes(M.workflowId)),h=d.map(M=>t.workflows.find(D=>D.id===M)).filter(M=>M!=null);let m=d.length>0&&h.length===d.length;for(const M of h){const D=l.find(O=>O.workflowId===M.id);if((D?Object.keys(D.steps).length:0)!==M.steps.length){m=!1;break}}let u=null;if(m)for(const M of l)M.completedAt&&(u===null||M.completedAt>u)&&(u=M.completedAt);const g=e.heuristic==="tab-order"?t.heuristicCounts.tabOrder??0:e.heuristic==="reading-order"?t.heuristicCounts.readingOrder??0:0,f=d[0]??e.igtWorkflowIds[0]??null,p=t.interactiveAuditVerdicts,y=[];p&&(e.id==="tab-order"&&p.focusOrder&&y.push({verdict:p.focusOrder,criterionId:"2.4.3"}),e.id==="keyboard"&&(p.keyboardTrap&&y.push({verdict:p.keyboardTrap,criterionId:"2.1.2"}),p.focusVisible&&y.push({verdict:p.focusVisible,criterionId:"2.4.7"})),e.id==="reading-order"&&p.readingOrder&&y.push({verdict:p.readingOrder,criterionId:"1.3.2"}),e.id==="contrast"&&p.nonTextContrast&&y.push({verdict:p.nonTextContrast,criterionId:"1.4.11"}));const b=y.some(M=>M.verdict==="fail");if(y.length>0&&y.every(M=>M.verdict==="pass")&&d.length>0&&!m)return{id:e.id,label:e.label,letter:"A",untested:!1,needsIgt:!1,igtCompleted:!1,verifiedByIgt:!1,verifiedAt:y.length>0?new Date().toISOString():null,suppressedHeuristicCount:g,primaryVerifierWorkflowId:f,counts:{critical:0,serious:0,moderate:0,minor:0},caps:{cappedAtC:!1,cappedAtB:!1},aiVerifiedCriteria:y.map(M=>M.criterionId)};if(m&&d.length>0)return{id:e.id,label:e.label,letter:"A",untested:!1,needsIgt:!1,igtCompleted:!0,verifiedByIgt:!0,verifiedAt:u,suppressedHeuristicCount:g,primaryVerifierWorkflowId:f,counts:{critical:0,serious:0,moderate:0,minor:0},caps:{cappedAtC:!1,cappedAtB:!1}};const w=e.axeRules.length>0||e.heuristic!=null,s=e.igtWorkflowIds.length>0;if(w?!t.auditRan:!c)return{id:e.id,label:e.label,letter:"F",untested:!0,needsIgt:s||d.length>0,igtCompleted:!1,verifiedByIgt:!1,verifiedAt:null,suppressedHeuristicCount:0,primaryVerifierWorkflowId:f,counts:{critical:0,serious:0,moderate:0,minor:0},caps:{cappedAtC:!1,cappedAtB:!1}};const S={critical:0,serious:0,moderate:0,minor:0},k=nr(n);for(const M of k)S[M.impact]++;const I=(t.aiResolvedFails??[]).filter(M=>a.has(M.ruleId));if(I.length>0){const M=new Set(k.map(D=>`${D.ruleId}::${D.target.selector}`));for(const D of I){const j=`${D.ruleId}::${D.selector}`;M.has(j)||(M.add(j),S[D.impact]++)}}const L=y.length>0&&y.some(M=>M.verdict==="pass"||M.verdict==="fail"),$=L?g:0;L||(S.serious+=g),b&&(S.serious+=y.filter(M=>M.verdict==="fail").length);for(const M of o){const D=r.find(j=>j.id===M.workflowId);if(D)for(const j of D.steps){const O=M.steps[j.id];(O==null?void 0:O.status)==="fail"&&(j.severity==="required"?S.serious++:S.moderate++)}}const _=S.critical>0,P=S.serious>0;let T;S.critical>=5?T="F":S.critical>=1?T="D":S.serious>=1?T="C":S.moderate>=1?T="B":T="A";const N=T!=="A"&&(s&&!i||d.length>0&&!m);return{id:e.id,label:e.label,letter:T,untested:!1,needsIgt:N,igtCompleted:i,verifiedByIgt:!1,verifiedAt:null,suppressedHeuristicCount:$,primaryVerifierWorkflowId:f,counts:S,caps:{cappedAtC:_,cappedAtB:P}}}const Qe="incompleteResolutions",Yn=2e3,Jn=600;function yn(e,t,a){return`${e}::${t}::${a}`}function rr(e){return e.reasoning&&e.reasoning.length>Jn?{...e,reasoning:e.reasoning.slice(0,Jn-1)+"…"}:e}function Cs(e){const t=Object.entries(e);if(t.length<=Yn)return e;const a=t.sort(([,n],[,o])=>{const r=Date.parse(n.resolvedAt)||0;return(Date.parse(o.resolvedAt)||0)-r});return Object.fromEntries(a.slice(0,Yn))}async function pa(){try{const t=(await chrome.storage.local.get(Qe))[Qe];return t&&typeof t=="object"?t:{}}catch{return{}}}async function wn(e){const t=Cs(e);try{await chrome.storage.local.set({[Qe]:t})}catch(a){const n=Object.entries(t).sort(([,r],[,i])=>{const c=Date.parse(r.resolvedAt)||0;return(Date.parse(i.resolvedAt)||0)-c}),o=Object.fromEntries(n.slice(0,Math.floor(n.length/2)));try{await chrome.storage.local.set({[Qe]:o}),console.warn("[incomplete-resolutions] storage quota hit; pruned to",Object.keys(o).length,"entries",a)}catch(r){await chrome.storage.local.remove(Qe).catch(()=>{}),console.warn("[incomplete-resolutions] storage quota still exceeded after prune; wiped key",r)}}}async function We(e){const t=await pa();t[yn(e.pageUrl,e.ruleId,e.selector)]=rr(e),await wn(t)}async function $s(e){if(e.length===0)return;const t=await pa();for(const a of e)t[yn(a.pageUrl,a.ruleId,a.selector)]=rr(a);await wn(t)}async function ir(){const e=await pa();return Object.values(e)}async function fa(e){return(await ir()).filter(a=>a.pageUrl===e)}async function Ts(e,t,a){const n=await pa();delete n[yn(e,t,a)],await wn(n)}async function Es(){await chrome.storage.local.remove(Qe)}function sr(e){if(!e.reasoning)return e;const t=e.reasoning.toLowerCase(),a=Dt(t,/verdict\s+corrected\s+to\s+pass|verdict:\s*pass\s+\(corrected\)/gi),n=Dt(t,/verdict\s+corrected\s+to\s+fail|verdict:\s*fail\s+\(corrected\)/gi);if(a>n&&a>=0)return{...e,verdict:"pass"};if(n>a&&n>=0)return{...e,verdict:"fail"};const o=Dt(t,/actually\s+passes|in\s+fact\s+passes|passes\s+aa/gi),r=Dt(t,/actually\s+fails|in\s+fact\s+fails|fails\s+aa/gi);return o<0&&r<0?e:o>r&&o/t.length>=.5?{...e,verdict:"pass"}:r>o&&r/t.length>=.5?{...e,verdict:"fail"}:e}function Dt(e,t){let a=-1;const n=t.flags.includes("g")?t.flags:t.flags+"g",o=new RegExp(t.source,n);let r;for(;(r=o.exec(e))!==null;)a=r.index,r.index===o.lastIndex&&o.lastIndex++;return a}function cr(e){if(e.ruleId!=="color-contrast"&&e.ruleId!=="color-contrast-enhanced"||!e.reasoning||e.verdict!=="fail")return e;const t=Rs(e.reasoning);if(t.length<2)return e;const a=t[0],n=t[1];if(n.a<.999)return e;const o=a.a<.999?{r:Math.round(a.r*a.a+n.r*(1-a.a)),g:Math.round(a.g*a.a+n.g*(1-a.a)),b:Math.round(a.b*a.a+n.b*(1-a.a))}:{r:a.r,g:a.g,b:a.b},r=Ms(o,{r:n.r,g:n.g,b:n.b}),i=Os(e.reasoning)??4.5;return r<i+.05?e:{...e,verdict:"pass",reasoning:`${e.reasoning}
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/parallel-tab-flow-C9KmXHUE.js","assets/crash-reporter-KD5f4utB.js","assets/preload-helper-D7HrI6pR.js","assets/diff-RnwNJg4F.js","assets/ai-usage-log-CYuyLsU4.js"])))=>i.map(i=>d[i]);
2
+ var Li=Object.defineProperty;var Ni=(e,t,a)=>t in e?Li(e,t,{enumerable:!0,configurable:!0,writable:!0,value:a}):e[t]=a;var Nt=(e,t,a)=>Ni(e,typeof t!="symbol"?t+"":t,a);import{_ as hn}from"./preload-helper-D7HrI6pR.js";import{c as te,f as de,o as Ce,k as H,r as ke,h as It,j as jo}from"./crash-reporter-KD5f4utB.js";import{a as ue,m as Gn,d as Pi,T as Fi,b as Vn,c as pn,g as da,i as jn}from"./diff-RnwNJg4F.js";import{i as _i,j as Me,J as Ui,T as fn,x as Tt,v as ua,z as ve,e as za,K as Wi,L as Gi,M as Vi,N as ji,O as Hi,P as Bi,b as qi,g as zi,u as Ki,r as Yi,f as Ji}from"./ai-usage-log-CYuyLsU4.js";const Ka=(e,t)=>t.some(a=>e instanceof a);let Hn,Bn;function Xi(){return Hn||(Hn=[IDBDatabase,IDBObjectStore,IDBIndex,IDBCursor,IDBTransaction])}function Qi(){return Bn||(Bn=[IDBCursor.prototype.advance,IDBCursor.prototype.continue,IDBCursor.prototype.continuePrimaryKey])}const Ya=new WeakMap,Ra=new WeakMap,ha=new WeakMap;function Zi(e){const t=new Promise((a,n)=>{const o=()=>{e.removeEventListener("success",r),e.removeEventListener("error",i)},r=()=>{a(qe(e.result)),o()},i=()=>{n(e.error),o()};e.addEventListener("success",r),e.addEventListener("error",i)});return ha.set(t,e),t}function es(e){if(Ya.has(e))return;const t=new Promise((a,n)=>{const o=()=>{e.removeEventListener("complete",r),e.removeEventListener("error",i),e.removeEventListener("abort",i)},r=()=>{a(),o()},i=()=>{n(e.error||new DOMException("AbortError","AbortError")),o()};e.addEventListener("complete",r),e.addEventListener("error",i),e.addEventListener("abort",i)});Ya.set(e,t)}let Ja={get(e,t,a){if(e instanceof IDBTransaction){if(t==="done")return Ya.get(e);if(t==="store")return a.objectStoreNames[1]?void 0:a.objectStore(a.objectStoreNames[0])}return qe(e[t])},set(e,t,a){return e[t]=a,!0},has(e,t){return e instanceof IDBTransaction&&(t==="done"||t==="store")?!0:t in e}};function Ho(e){Ja=e(Ja)}function ts(e){return Qi().includes(e)?function(...t){return e.apply(Xa(this),t),qe(this.request)}:function(...t){return qe(e.apply(Xa(this),t))}}function as(e){return typeof e=="function"?ts(e):(e instanceof IDBTransaction&&es(e),Ka(e,Xi())?new Proxy(e,Ja):e)}function qe(e){if(e instanceof IDBRequest)return Zi(e);if(Ra.has(e))return Ra.get(e);const t=as(e);return t!==e&&(Ra.set(e,t),ha.set(t,e)),t}const Xa=e=>ha.get(e);function gn(e,t,{blocked:a,upgrade:n,blocking:o,terminated:r}={}){const i=indexedDB.open(e,t),c=qe(i);return n&&i.addEventListener("upgradeneeded",d=>{n(qe(i.result),d.oldVersion,d.newVersion,qe(i.transaction),d)}),a&&i.addEventListener("blocked",d=>a(d.oldVersion,d.newVersion,d)),c.then(d=>{r&&d.addEventListener("close",()=>r()),o&&d.addEventListener("versionchange",l=>o(l.oldVersion,l.newVersion,l))}).catch(()=>{}),c}const ns=["get","getKey","getAll","getAllKeys","count"],os=["put","add","delete","clear"],Oa=new Map;function qn(e,t){if(!(e instanceof IDBDatabase&&!(t in e)&&typeof t=="string"))return;if(Oa.get(t))return Oa.get(t);const a=t.replace(/FromIndex$/,""),n=t!==a,o=os.includes(a);if(!(a in(n?IDBIndex:IDBObjectStore).prototype)||!(o||ns.includes(a)))return;const r=async function(i,...c){const d=this.transaction(i,o?"readwrite":"readonly");let l=d.store;return n&&(l=l.index(c.shift())),(await Promise.all([l[a](...c),o&&d.done]))[0]};return Oa.set(t,r),r}Ho(e=>({...e,get:(t,a,n)=>qn(t,a)||e.get(t,a,n),has:(t,a)=>!!qn(t,a)||e.has(t,a)}));const rs=["continue","continuePrimaryKey","advance"],zn={},Qa=new WeakMap,Bo=new WeakMap,is={get(e,t){if(!rs.includes(t))return e[t];let a=zn[t];return a||(a=zn[t]=function(...n){Qa.set(this,Bo.get(this)[t](...n))}),a}};async function*ss(...e){let t=this;if(t instanceof IDBCursor||(t=await t.openCursor(...e)),!t)return;t=t;const a=new Proxy(t,is);for(Bo.set(a,t),ha.set(a,Xa(t));t;)yield a,t=await(Qa.get(a)||t.continue()),Qa.delete(a)}function Kn(e,t){return t===Symbol.asyncIterator&&Ka(e,[IDBIndex,IDBObjectStore,IDBCursor])||t==="iterate"&&Ka(e,[IDBIndex,IDBObjectStore])}Ho(e=>({...e,get(t,a,n){return Kn(t,a)?ss:e.get(t,a,n)},has(t,a){return Kn(t,a)||e.has(t,a)}}));const cs="wcag-component-auditor",ls=3;let Ze=null;function ce(){return Ze||(Ze=gn(cs,ls,{upgrade(e,t){if(t<1){const a=e.createObjectStore("baselines",{keyPath:"componentId"});a.createIndex("byUrl","snapshotMeta.url"),a.createIndex("byDate","lastUpdated");const n=e.createObjectStore("audit-history",{keyPath:"auditId",autoIncrement:!0});n.createIndex("byComponentId","componentId"),n.createIndex("byDate","runDate")}if(t<2&&e.createObjectStore("site-crawl-per-url",{keyPath:"slot"}),t<3){e.createObjectStore("scheduled-audits",{keyPath:"id"});const a=e.createObjectStore("scheduled-audit-results",{keyPath:"resultId",autoIncrement:!0});a.createIndex("byScheduleId","scheduleId"),a.createIndex("byRanAt","ranAt")}}})),Ze}async function ds(){Ze&&(await Ze).close(),Ze=null}async function pa(e){return(await ce()).get("baselines",e)}async function mn(e){await(await ce()).put("baselines",e)}async function qo(e){await(await ce()).delete("baselines",e)}async function zo(){return(await ce()).getAll("baselines")}async function us(e){return(await ce()).getAllFromIndex("baselines","byUrl",e)}async function hs(e){return(await ce()).add("audit-history",e)}async function ps(e){return(await ce()).getAllFromIndex("audit-history","byComponentId",e)}async function Ko(){return(await ce()).get("site-crawl-per-url","current")}async function Yo(e){await(await ce()).put("site-crawl-per-url",{...e,slot:"current"})}async function bn(){await(await ce()).delete("site-crawl-per-url","current")}async function Jo(){return(await ce()).getAll("scheduled-audits")}async function Xo(e){return(await ce()).get("scheduled-audits",e)}async function yn(e){await(await ce()).put("scheduled-audits",e)}async function fs(e){await(await ce()).delete("scheduled-audits",e)}async function Qo(e){return(await ce()).add("scheduled-audit-results",e)}async function gs(e){return(await ce()).getAllFromIndex("scheduled-audit-results","byScheduleId",e)}async function ms(){return(await ce()).getAll("scheduled-audit-results")}async function Zo(e,t=50){const a=await ce(),n=await a.getAllFromIndex("scheduled-audit-results","byScheduleId",e);if(n.length<=t)return;n.sort((i,c)=>c.ranAt.localeCompare(i.ranAt));const o=n.slice(t),r=a.transaction("scheduled-audit-results","readwrite");for(const i of o)i.resultId!==void 0&&await r.store.delete(i.resultId);await r.done}async function bs(e){const a=(await ce()).transaction("scheduled-audit-results","readwrite");let o=await a.store.index("byScheduleId").openCursor(IDBKeyRange.only(e));for(;o;)await o.delete(),o=await o.continue();await a.done}const ys=Object.freeze(Object.defineProperty({__proto__:null,_resetDbForTesting:ds,appendAuditHistory:hs,appendScheduledAuditResult:Qo,deleteBaseline:qo,deleteScheduledAudit:fs,deleteScheduledAuditResultsForScheduleId:bs,deleteSiteCrawlPerUrl:bn,getBaseline:pa,getDb:ce,getScheduledAudit:Xo,getSiteCrawlPerUrl:Ko,listAllScheduledAuditResults:ms,listAuditHistoryForComponent:ps,listBaselines:zo,listBaselinesByUrl:us,listScheduledAuditResultsByScheduleId:gs,listScheduledAudits:Jo,pruneScheduledAuditResults:Zo,putScheduledAudit:yn,setBaseline:mn,setSiteCrawlPerUrl:Yo},Symbol.toStringTag,{value:"Module"})),qt="siteCrawlReport";async function wn(){var a;if(typeof chrome>"u"||!((a=chrome.storage)!=null&&a.local))return null;const t=(await chrome.storage.local.get([qt]))[qt];return t&&typeof t=="object"?t:null}async function er(e){var t;if(!(typeof chrome>"u"||!((t=chrome.storage)!=null&&t.local))){if(e===null){await chrome.storage.local.remove([qt]),await bn().catch(()=>{});return}await chrome.storage.local.set({[qt]:e})}}async function zt(){try{const e=await Ko();return(e==null?void 0:e.perUrl)??null}catch{return null}}async function tr(e,t){if(e===null){await bn();return}await Yo({startUrl:(t==null?void 0:t.startUrl)??"",finishedAt:(t==null?void 0:t.finishedAt)??new Date().toISOString(),perUrl:e})}const Vh=Object.freeze(Object.defineProperty({__proto__:null,loadSiteCrawlPerUrlResults:zt,loadSiteCrawlReport:wn,saveSiteCrawlPerUrlResults:tr,saveSiteCrawlReport:er},Symbol.toStringTag,{value:"Module"}));async function Ue(){return!1}function jh(e){return!1}const Et="HEADLESS_BUILD_LOCKOUT",ze="wcagcheckr is in private build phase. A team license is required for access. If you believe you should have access, contact cliff@locustware.com.";async function ar(){const e=await chrome.tabs.query({currentWindow:!0}),t=e.find(n=>n.active);if(t!=null&&t.id&&Yn(t.url))return t.id;const a=e.find(n=>n.id&&Yn(n.url));return(a==null?void 0:a.id)??null}function Yn(e){return typeof e=="string"&&/^https?:\/\//.test(e)}const Za="rubricConfigCache",ws=7*24*60*60*1e3;function Hh(e){const t=Date.now()-e.fetchedAt;return t>=ws?"expired":t>=60*60*1e3?"stale":"fresh"}async function Bh(){var a;if(typeof chrome>"u"||!((a=chrome.storage)!=null&&a.local))return null;const t=(await chrome.storage.local.get(Za))[Za];return!t||typeof t!="object"?null:t}async function qh(e){var t;typeof chrome>"u"||!((t=chrome.storage)!=null&&t.local)||await chrome.storage.local.set({[Za]:e})}let nr=null;function zh(e){nr=e}function or(){return nr}const en="acknowledgedFindings";async function fa(){var a;if(typeof chrome>"u"||!((a=chrome.storage)!=null&&a.local))return[];const t=(await chrome.storage.local.get([en]))[en];return Array.isArray(t)?t:[]}async function ga(e){const t=await fa();return new Set(t.filter(a=>a.componentId===e).map(a=>a.matchKey))}async function vs(e){const a=(await fa()).filter(n=>!(n.componentId===e.componentId&&n.matchKey===e.matchKey));a.push({...e,acknowledgedAt:new Date().toISOString()}),await rr(a)}async function As(e,t){const n=(await fa()).filter(o=>!(o.componentId===e&&o.matchKey===t));await rr(n)}async function rr(e){var t;typeof chrome>"u"||!((t=chrome.storage)!=null&&t.local)||await chrome.storage.local.set({[en]:e})}const Kh=Object.freeze(Object.defineProperty({__proto__:null,acknowledgeFinding:vs,loadAcknowledged:fa,loadAcknowledgedKeysForComponent:ga,unacknowledgeFinding:As},Symbol.toStringTag,{value:"Module"})),ks=/:nth-(?:last-)?(?:child|of-type)\([^)]+\)/g,xs=/\[id="[^"]*"\]/g,Is=/\[data-testid="[^"]*"\]/g,Ss=/#[\w-]*\d[\w-]*/g,Cs=/\[href(?:[\^$*~|]?=("|')[^"']*\1)?\]/g,$s=/\[aria-label(?:[\^$*~|]?=("|')[^"']*\1)?\]/g,Ts=new Set(["region","landmark-one-main","page-has-heading-one","landmark-no-duplicate-banner","landmark-no-duplicate-contentinfo","landmark-no-duplicate-main","landmark-main-is-top-level","landmark-banner-is-top-level","landmark-contentinfo-is-top-level","landmark-complementary-is-top-level","landmark-unique","bypass","document-title","html-has-lang","meta-viewport"]);function Es(e){return e&&e.replace(ks,"").replace(xs,"").replace(Is,"").replace(Ss,"").replace(Cs,"").replace($s,"").replace(/\s+/g," ").trim()}function De(e,t){return Ts.has(e)?`${e}::__structural__`:`${e}::${Es(t)}`}const Jn={minor:0,moderate:1,serious:2,critical:3};function ir(e){const t=new Map;for(const a of e){const n=De(a.ruleId,a.target.selector),o=t.get(n);if(!o){t.set(n,a);continue}(Jn[a.impact]??0)>(Jn[o.impact]??0)&&t.set(n,a)}return Array.from(t.values())}const sr=[{id:"contrast",label:"Color & contrast",axeRules:["color-contrast","color-contrast-enhanced","link-in-text-block"],igtWorkflowIds:["visual-indicators"]},{id:"alt-text",label:"Alt text",axeRules:["image-alt","input-image-alt","object-alt","svg-img-alt","area-alt","role-img-alt"],igtWorkflowIds:[]},{id:"forms",label:"Forms",axeRules:["label","select-name","aria-input-field-name","aria-toggle-field-name","form-field-multiple-labels","label-content-name-mismatch","autocomplete-valid","label-title-only","wcc-onfocus-context-change","wcc-oninput-context-change","wcc-cognitive-auth-challenge"],igtWorkflowIds:["forms"]},{id:"keyboard",label:"Keyboard",axeRules:["nested-interactive","tabindex","frame-focusable-content","no-focusable-content","focus-order-semantics","wcc-gesture-no-alternative","wcc-drag-no-alternative"],igtWorkflowIds:["keyboard","focus-management"]},{id:"tab-order",label:"Tab order",axeRules:[],igtWorkflowIds:[],verifiedByWorkflowIds:["keyboard"],heuristic:"tab-order"},{id:"screen-reader",label:"Screen reader / ARIA",axeRules:["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","button-name","input-button-name","link-name","frame-title","presentation-role-conflict","aria-allowed-role","aria-prohibited-attr","aria-text","aria-deprecated-role","aria-conditional-attr","aria-braille-equivalent","empty-button","empty-heading","empty-link"],igtWorkflowIds:["screen-reader"]},{id:"reading-order",label:"Reading order",axeRules:[],igtWorkflowIds:[],verifiedByWorkflowIds:["page-structure"],heuristic:"reading-order"},{id:"page-structure",label:"Page structure",axeRules:["heading-order","page-has-heading-one","landmark-one-main","landmark-no-duplicate-banner","landmark-no-duplicate-contentinfo","landmark-no-duplicate-main","region","bypass","empty-table-header","landmark-banner-is-top-level","landmark-complementary-is-top-level","landmark-contentinfo-is-top-level","landmark-main-is-top-level","landmark-unique"],igtWorkflowIds:["page-structure"]}];function Yh(e,t){const a=new Set;for(const n of t)n.wcagCriterion===e&&a.add(n.ruleId);if(a.size===0)return null;for(const n of sr)for(const o of n.axeRules)if(a.has(o))return n.id;return null}function Rs(e){const t=sr.map(o=>Ms(o,e)),a=Os(t.map(o=>o.letter)),n=t.filter(o=>o.letter==="D"||o.letter==="F");return{overallLetter:a,subGrades:t,warningAreas:n,isLawsuitRisk:n.length>0}}function Os(e){const t=e.filter(i=>i==="A").length,a=e.filter(i=>i==="C").length,n=e.filter(i=>i==="D").length,o=e.filter(i=>i==="F").length,r=e.length-t;return t===0&&n+o>a?"F":r===0?"A":r<=3?"B":r<=6?"C":"D"}function Ms(e,t){const a=new Set(e.axeRules),n=t.violations.filter(O=>a.has(O.ruleId)),o=t.manualRuns.filter(O=>e.igtWorkflowIds.includes(O.workflowId)),r=e.igtWorkflowIds.map(O=>t.workflows.find(D=>D.id===O)).filter(O=>O!=null);let i=r.length>0;for(const O of r){const D=o.find(M=>M.workflowId===O.id);if((D?Object.keys(D.steps).length:0)!==O.steps.length){i=!1;break}}const c=o.some(O=>Object.keys(O.steps).length>0),d=e.verifiedByWorkflowIds??[],l=t.manualRuns.filter(O=>d.includes(O.workflowId)),h=d.map(O=>t.workflows.find(D=>D.id===O)).filter(O=>O!=null);let m=d.length>0&&h.length===d.length;for(const O of h){const D=l.find(M=>M.workflowId===O.id);if((D?Object.keys(D.steps).length:0)!==O.steps.length){m=!1;break}}let u=null;if(m)for(const O of l)O.completedAt&&(u===null||O.completedAt>u)&&(u=O.completedAt);const g=e.heuristic==="tab-order"?t.heuristicCounts.tabOrder??0:e.heuristic==="reading-order"?t.heuristicCounts.readingOrder??0:0,p=d[0]??e.igtWorkflowIds[0]??null,f=t.interactiveAuditVerdicts,y=[];f&&(e.id==="tab-order"&&f.focusOrder&&y.push({verdict:f.focusOrder,criterionId:"2.4.3"}),e.id==="keyboard"&&(f.keyboardTrap&&y.push({verdict:f.keyboardTrap,criterionId:"2.1.2"}),f.focusVisible&&y.push({verdict:f.focusVisible,criterionId:"2.4.7"})),e.id==="reading-order"&&f.readingOrder&&y.push({verdict:f.readingOrder,criterionId:"1.3.2"}),e.id==="contrast"&&f.nonTextContrast&&y.push({verdict:f.nonTextContrast,criterionId:"1.4.11"}));const b=y.some(O=>O.verdict==="fail");if(y.length>0&&y.every(O=>O.verdict==="pass")&&d.length>0&&!m)return{id:e.id,label:e.label,letter:"A",untested:!1,needsIgt:!1,igtCompleted:!1,verifiedByIgt:!1,verifiedAt:y.length>0?new Date().toISOString():null,suppressedHeuristicCount:g,primaryVerifierWorkflowId:p,counts:{critical:0,serious:0,moderate:0,minor:0},caps:{cappedAtC:!1,cappedAtB:!1},aiVerifiedCriteria:y.map(O=>O.criterionId)};if(m&&d.length>0)return{id:e.id,label:e.label,letter:"A",untested:!1,needsIgt:!1,igtCompleted:!0,verifiedByIgt:!0,verifiedAt:u,suppressedHeuristicCount:g,primaryVerifierWorkflowId:p,counts:{critical:0,serious:0,moderate:0,minor:0},caps:{cappedAtC:!1,cappedAtB:!1}};const w=e.axeRules.length>0||e.heuristic!=null,s=e.igtWorkflowIds.length>0;if(w?!t.auditRan:!c)return{id:e.id,label:e.label,letter:"F",untested:!0,needsIgt:s||d.length>0,igtCompleted:!1,verifiedByIgt:!1,verifiedAt:null,suppressedHeuristicCount:0,primaryVerifierWorkflowId:p,counts:{critical:0,serious:0,moderate:0,minor:0},caps:{cappedAtC:!1,cappedAtB:!1}};const S={critical:0,serious:0,moderate:0,minor:0},k=ir(n);for(const O of k)S[O.impact]++;const x=(t.aiResolvedFails??[]).filter(O=>a.has(O.ruleId));if(x.length>0){const O=new Set(k.map(D=>`${D.ruleId}::${D.target.selector}`));for(const D of x){const G=`${D.ruleId}::${D.selector}`;O.has(G)||(O.add(G),S[D.impact]++)}}const L=y.length>0&&y.some(O=>O.verdict==="pass"||O.verdict==="fail"),$=L?g:0;L||(S.serious+=g),b&&(S.serious+=y.filter(O=>O.verdict==="fail").length);for(const O of o){const D=r.find(G=>G.id===O.workflowId);if(D)for(const G of D.steps){const M=O.steps[G.id];(M==null?void 0:M.status)==="fail"&&(G.severity==="required"?S.serious++:S.moderate++)}}const U=S.critical>0,F=S.serious>0;let T;S.critical>=5?T="F":S.critical>=1?T="D":S.serious>=1?T="C":S.moderate>=1?T="B":T="A";const N=T!=="A"&&(s&&!i||d.length>0&&!m);return{id:e.id,label:e.label,letter:T,untested:!1,needsIgt:N,igtCompleted:i,verifiedByIgt:!1,verifiedAt:null,suppressedHeuristicCount:$,primaryVerifierWorkflowId:p,counts:S,caps:{cappedAtC:U,cappedAtB:F}}}const et="incompleteResolutions",Xn=2e3,Qn=600;function vn(e,t,a){return`${e}::${t}::${a}`}function cr(e){return e.reasoning&&e.reasoning.length>Qn?{...e,reasoning:e.reasoning.slice(0,Qn-1)+"…"}:e}function Ds(e){const t=Object.entries(e);if(t.length<=Xn)return e;const a=t.sort(([,n],[,o])=>{const r=Date.parse(n.resolvedAt)||0;return(Date.parse(o.resolvedAt)||0)-r});return Object.fromEntries(a.slice(0,Xn))}async function ma(){try{const t=(await chrome.storage.local.get(et))[et];return t&&typeof t=="object"?t:{}}catch{return{}}}async function An(e){const t=Ds(e);try{await chrome.storage.local.set({[et]:t})}catch(a){const n=Object.entries(t).sort(([,r],[,i])=>{const c=Date.parse(r.resolvedAt)||0;return(Date.parse(i.resolvedAt)||0)-c}),o=Object.fromEntries(n.slice(0,Math.floor(n.length/2)));try{await chrome.storage.local.set({[et]:o}),console.warn("[incomplete-resolutions] storage quota hit; pruned to",Object.keys(o).length,"entries",a)}catch(r){await chrome.storage.local.remove(et).catch(()=>{}),console.warn("[incomplete-resolutions] storage quota still exceeded after prune; wiped key",r)}}}async function je(e){const t=await ma();t[vn(e.pageUrl,e.ruleId,e.selector)]=cr(e),await An(t)}async function Ls(e){if(e.length===0)return;const t=await ma();for(const a of e)t[vn(a.pageUrl,a.ruleId,a.selector)]=cr(a);await An(t)}async function lr(){const e=await ma();return Object.values(e)}async function ba(e){return(await lr()).filter(a=>a.pageUrl===e)}async function Ns(e,t,a){const n=await ma();delete n[vn(e,t,a)],await An(n)}async function Ps(){await chrome.storage.local.remove(et)}function dr(e){if(!e.reasoning)return e;const t=e.reasoning.toLowerCase(),a=Pt(t,/verdict\s+corrected\s+to\s+pass|verdict:\s*pass\s+\(corrected\)/gi),n=Pt(t,/verdict\s+corrected\s+to\s+fail|verdict:\s*fail\s+\(corrected\)/gi);if(a>n&&a>=0)return{...e,verdict:"pass"};if(n>a&&n>=0)return{...e,verdict:"fail"};const o=Pt(t,/actually\s+passes|in\s+fact\s+passes|passes\s+aa/gi),r=Pt(t,/actually\s+fails|in\s+fact\s+fails|fails\s+aa/gi);return o<0&&r<0?e:o>r&&o/t.length>=.5?{...e,verdict:"pass"}:r>o&&r/t.length>=.5?{...e,verdict:"fail"}:e}function Pt(e,t){let a=-1;const n=t.flags.includes("g")?t.flags:t.flags+"g",o=new RegExp(t.source,n);let r;for(;(r=o.exec(e))!==null;)a=r.index,r.index===o.lastIndex&&o.lastIndex++;return a}function ur(e){if(e.ruleId!=="color-contrast"&&e.ruleId!=="color-contrast-enhanced"||!e.reasoning||e.verdict!=="fail")return e;const t=Fs(e.reasoning);if(t.length<2)return e;const a=t[0],n=t[1];if(n.a<.999)return e;const o=a.a<.999?{r:Math.round(a.r*a.a+n.r*(1-a.a)),g:Math.round(a.g*a.a+n.g*(1-a.a)),b:Math.round(a.b*a.a+n.b*(1-a.a))}:{r:a.r,g:a.g,b:a.b},r=Us(o,{r:n.r,g:n.g,b:n.b}),i=_s(e.reasoning)??4.5;return r<i+.05?e:{...e,verdict:"pass",reasoning:`${e.reasoning}
3
3
 
4
- (rc.299 math-salvage: re-computed from cited rgb(${a.r},${a.g},${a.b}) on rgb(${n.r},${n.g},${n.b}) → ${r.toFixed(2)}:1 against threshold ${i}:1 → PASS. Original AI verdict was FAIL but its arithmetic appears wrong on this element. One-way salvage: only flips fail→pass, never pass→fail.)`}}function lr(e,t){var n;const a=new Set;for(const o of t)for(const r of((n=o.axeRulesEvaluated)==null?void 0:n.incomplete)??[])for(const i of r.elements??[])a.add(`${r.ruleId}::${i.selector}`);return e.map(sr).map(cr).filter(o=>o.verdict==="fail"&&a.has(`${o.ruleId}::${o.selector}`))}function Rs(e){const t=/rgba?\(\s*([0-9.]+)[,\s]+([0-9.]+)[,\s]+([0-9.]+)(?:[,/\s]+([0-9.]+))?\s*\)/gi,a=[];let n;for(;(n=t.exec(e))!==null&&(a.push({r:Math.round(parseFloat(n[1])),g:Math.round(parseFloat(n[2])),b:Math.round(parseFloat(n[3])),a:n[4]!==void 0?parseFloat(n[4]):1}),!(a.length>=4)););return a}function Os(e){const t=/([34](?:\.\d+)?|7(?:\.\d+)?)\s*:\s*1/g,a=[];let n;for(;(n=t.exec(e))!==null;){const o=parseFloat(n[1]);(o===3||o===4.5||o===7)&&a.push(o)}return a.length>0?a[a.length-1]:null}function Ms(e,t){const a=d=>{const l=d/255;return l<=.03928?l/12.92:Math.pow((l+.055)/1.055,2.4)},n=d=>.2126*a(d.r)+.7152*a(d.g)+.0722*a(d.b),o=n(e),r=n(t),i=Math.max(o,r),c=Math.min(o,r);return(i+.05)/(c+.05)}const Ds=Object.freeze(Object.defineProperty({__proto__:null,applyContrastMathSalvage:cr,applySelfCorrectionSalvage:sr,clearAllResolutions:Es,deleteResolution:Ts,filterRelevant:lr,getAllResolutions:ir,getResolutionsForPage:fa,saveResolution:We,saveResolutions:$s},Symbol.toStringTag,{value:"Module"})),Bt="interactiveAuditResults";function vn(e,t){return`${e}::${t}`}async function $t(){try{const t=(await chrome.storage.local.get(Bt))[Bt];return t&&typeof t=="object"?t:{}}catch{return{}}}async function An(e){await chrome.storage.local.set({[Bt]:e})}async function ne(e){const t=await $t(),a={componentId:e.componentId,criterionId:e.criterionId,verdict:e.verdict,reasoning:e.reasoning,confidence:e.confidence,pageUrl:e.pageUrl,finishedAt:e.finishedAt,costUsd:e.costUsd,model:e.model,inputHash:e.inputHash};t[vn(e.componentId,e.criterionId)]=a,await An(t)}async function Pe(e,t){return(await $t())[vn(e,t)]??null}async function Ls(){const e=await $t();return Object.values(e)}async function Za(e){return(await Ls()).filter(a=>a.componentId===e)}async function _h(e,t){const a=await $t();delete a[vn(e,t)],await An(a)}async function kn(e){const t=new Set;for(const n of e)n&&t.add(n);if(t.size===0)return[];const a=[];for(const n of t)try{const o=await Za(n);for(const r of o)a.push({criterionId:r.criterionId,ruleId:`ai-interactive::${r.criterionId}`,pageUrl:r.pageUrl,verdict:r.verdict,reasoning:r.reasoning})}catch{}return a}async function Uh(){try{const e=await $t();let t=!1;for(const a of Object.keys(e)){const n=e[a];if(n.steps&&n.steps.length>0){const o={componentId:n.componentId,criterionId:n.criterionId,verdict:n.verdict,reasoning:n.reasoning,confidence:n.confidence,pageUrl:n.pageUrl,finishedAt:n.finishedAt,costUsd:n.costUsd,model:n.model};e[a]=o,t=!0}}t&&await An(e)}catch{try{await chrome.storage.local.remove(Bt)}catch{}}}const Ps=te("walkthrough-acks"),en="walkthroughAcks";function In(e,t,a){return`${e}::${t}::${a}`}async function ga(){try{const t=(await chrome.storage.local.get(en))[en];return t&&typeof t=="object"?t:{}}catch{return{}}}async function dr(e){await chrome.storage.local.set({[en]:e})}async function Wh(e){if(!e.note||!e.note.trim())throw new Error("Acknowledgement requires a non-empty note explaining manual verification.");const t=await ga();t[In(e.componentId,e.criterionId,e.pageUrl)]={...e,note:e.note.trim()},await dr(t),Ps.info(`acknowledged ${e.criterionId} on ${e.pageUrl}`)}async function Vh(e,t,a){const n=await ga();delete n[In(e,t,a)],await dr(n)}async function Ns(e,t,a){return(await ga())[In(e,t,a)]??null}async function Fs(){const e=await ga();return Object.values(e)}async function ur(e){const t=new Set;for(const n of e)n&&t.add(n);return t.size===0?[]:(await Fs()).filter(n=>t.has(n.componentId))}const zt={A:4,B:3,C:2,D:1,F:0},_s=["F","D","C","B","A"];function Us(e,t){const a=(zt[e]+zt[t])/2,n=Math.ceil(a-.5);return _s[n]}function Ws(e,t){return zt[e]<=zt[t]?e:t}const Ra=[{id:"contrast",label:"Color contrast",rules:["color-contrast","color-contrast-enhanced","link-in-text-block"],needsManualCheck:!1},{id:"alt",label:"Image alt text",rules:["image-alt","input-image-alt","area-alt","svg-img-alt","object-alt","role-img-alt"],needsManualCheck:!1},{id:"labels",label:"Form labels",rules:["label","select-name","aria-input-field-name","aria-toggle-field-name","form-field-multiple-labels","label-content-name-mismatch","autocomplete-valid"],needsManualCheck:!1},{id:"names",label:"Buttons / links named",rules:["button-name","input-button-name","link-name","empty-button","empty-heading","empty-link"],needsManualCheck:!1},{id:"keyboard",label:"Keyboard operability",rules:["nested-interactive","tabindex","frame-focusable-content","no-focusable-content"],needsManualCheck:!0},{id:"structure",label:"Landmarks + heading structure",rules:["heading-order","page-has-heading-one","landmark-one-main","landmark-no-duplicate-banner","landmark-no-duplicate-contentinfo","landmark-no-duplicate-main","region","bypass"],needsManualCheck:!1}];function Vs(e){const t=new Map;for(const a of e){const n=`${a.ruleId}::${a.target.selector}`;t.has(n)||t.set(n,a)}return Array.from(t.values())}function js(e,t,a="2.1",n="AA",o,r,i,c,d){const l=Mi({audits:e??[],igtRuns:(t==null?void 0:t.runs)??[],workflows:(t==null?void 0:t.workflows)??Re,acknowledgedMatchKeys:o??new Set,incompleteResolutions:r,interactiveAuditResults:i,auditScope:c,humanCriterionVerdicts:d}),h={},m={},u=[],g=[],f=[];let p=0;for(const b of l.values()){if(b.state==="not-evaluated"){u.push(b.criterionId);continue}if(p++,b.state==="fail"){f.push(b.criterionId);const v=[];for(const w of b.evidence)w.outcome==="fail"&&v.push({source:w.source,ruleOrStepId:w.ruleOrStepId,selector:w.selector,notes:w.notes,pageUrl:w.pageUrl});v.length>0&&(m[b.criterionId]=v)}if(b.state==="inconclusive"){g.push(b.criterionId);const v=[];for(const w of b.evidence)if(w.outcome==="incomplete"){const s=w.source==="ai"?"ai-uncertain":"axe-incomplete";v.push({source:s,ruleOrStepId:w.ruleOrStepId,resolutionHint:w.resolutionHint,elements:w.elements,pageUrl:w.pageUrl})}else w.outcome==="skip"&&v.push({source:"igt-skip",ruleOrStepId:w.ruleOrStepId,resolutionHint:w.resolutionHint});v.length>0&&(h[b.criterionId]=v)}}const y=Di(l);return{targetVersion:St,targetLevel:hn,totalApplicable:l.size,evaluated:p,untestedCriteria:u,inconclusiveCriteria:g,failingCriteria:f,inconclusiveReasons:h,failingReasons:m,canClaimConformance:y.canClaimConformance}}function ma(e,t,a,n,o,r,i,c,d){const l=n??new Set,h=Vs(e).filter(P=>!l.has(P.matchKey)),m=h.filter(P=>P.needsReview),u=h.filter(P=>!P.needsReview),g={critical:0,serious:0,moderate:0,minor:0,unique:u.length};for(const P of u)g[P.impact]++;let f;t&&(f=Gs(t.runs,t.workflows),g.serious+=f.failedRequired,g.moderate+=f.failedAdvisory,g.unique+=f.failedRequired+f.failedAdvisory);const p=[],y=[];if(o&&o.length>0){const P=new Set;if(r)for(const N of r)P.add(`${N.criterionId}::${N.pageUrl}`);const T=new Set;for(const N of o){if(N.verdict==="pass")continue;const M=`${N.criterionId}::${N.pageUrl}`;if(T.has(M))continue;T.add(M);const D=N.verdict==="fail"?"serious":"moderate",j={criterionId:N.criterionId,pageUrl:N.pageUrl,verdict:N.verdict,impact:D,reasoning:N.reasoning};if(P.has(M)){y.push(j);continue}p.push(j),g[D]++,g.unique++}}const b=new Map;for(const P of Ra)for(const T of P.rules)b.set(T,P);const v=new Map;for(const P of Ra)v.set(P.id,{count:0,rules:new Set});for(const P of nr(u)){const T=b.get(P.ruleId);if(!T)continue;const N=v.get(T.id);N.count++,N.rules.add(P.ruleId)}const w=Ra.map(P=>{const T=v.get(P.id);let N="pass";return T.count>0?N="fail":P.needsManualCheck&&(N="unchecked"),{id:P.id,label:P.label,status:N,violationCount:T.count,rules:Array.from(T.rules),needsManualCheck:P.needsManualCheck}}),s=g.critical>0,A=g.serious>0,S=(()=>{if(!o||o.length===0||!r||r.length===0)return o;const P=new Set;for(const T of r)P.add(`${T.criterionId}::${T.pageUrl}`);return o.map(T=>P.has(`${T.criterionId}::${T.pageUrl}`)?{...T,verdict:"pass",reasoning:`Human verified: ${T.reasoning}`}:T)})(),k=a?js(a,t,"2.1","AA",n,d,S,c,i):void 0,I=k?!k.canClaimConformance:!1;let L;g.critical>=5?L="F":g.critical>=1?L="D":g.serious>=5||g.serious>=1?L="C":g.moderate>=3||g.moderate>=1?L="B":(g.minor>=1,L="A"),I&&L==="A"&&(L="B");const $=w.filter(P=>P.status==="fail").length;let _;return g.critical>0||$>=4?_="critical":g.serious>=3||$>=2?_="high":g.serious>=1||g.moderate>=3?_="moderate":_="low",{letter:L,risk:_,caps:{cappedAtC:s,cappedAtB:A,cappedAtBByCoverage:I},totals:g,categories:w,coverage:k,manual:f,reviewQueue:{count:m.length,rules:Array.from(new Set(m.map(P=>P.ruleId)))},walkthroughFindings:p,walkthroughAcknowledgements:y}}function Gs(e,t){new Map(t.map(c=>[c.id,c]));const a=new Map(e.map(c=>[c.workflowId,c]));let n=0,o=0,r=0;const i=t.map(c=>{const d=a.get(c.id);let l=0,h=0,m=0,u=0,g=0;for(const f of c.steps){const p=d==null?void 0:d.steps[f.id];p&&(g++,p.status==="pass"?l++:p.status==="skip"?u++:p.status==="fail"&&(f.severity==="required"?h++:m++))}return g===c.steps.length&&r++,n+=h,o+=m,{workflowId:c.id,name:c.name,passed:l,failedRequired:h,failedAdvisory:m,skipped:u,answered:g,total:c.steps.length}});return{workflowsCompleted:r,workflowsTotal:t.length,failedRequired:n,failedAdvisory:o,perWorkflow:i}}const Hs={low:"Low exposure — no critical or serious violations across the three layers (axe-core, wcagcheckr DOM analyzers, AI walkthroughs). All 50 WCAG 2.1 AA criteria have an evaluation path; spot-check content-meaning criteria (caption accuracy, alt-text correctness) before any formal conformance claim.",moderate:"Moderate exposure — some serious or multiple moderate violations. Should be addressed but not lawsuit-emergency level.",high:"High exposure — multiple serious violations OR multiple lawsuit-magnet categories failing. ADA Title III suits target sites at this level.",critical:"Critical exposure — at least one critical-impact violation OR most lawsuit-magnet categories failing. Sites in this state are demand-letter targets."},hr={low:"✓ Off the radar",moderate:"⚡ Some lawsuit exposure",high:"⚠️ High target risk",critical:"🚨 Lawsuit magnet"},pr={low:"Your site clears the automated checks ADA-targeting tools use to identify lawsuit victims. wcagcheckr runs three layers (axe-core, our DOM analyzers, AI walkthroughs) covering all 50 WCAG 2.1 AA criteria — most demand-letter mills only run axe.",moderate:"Some failures. Not the easiest target, but the issues here are exactly what automated targeting tools look for. Switch to Developer mode for the issue list and fix-it walkthroughs.",high:"Multiple serious failures. Automated lawsuit-screening tools will flag this site. Real risk of being targeted with a demand letter — Developer mode has the full issue list and fix-it walkthroughs.",critical:"Your site fails the automated WCAG checks demand-letter mills run on thousands of sites to identify lawsuit targets. Without remediation, expect to be flagged. Developer mode has the full issue list plus fix-it guidance for each."},Xn={A:"No critical, serious, or moderate violations across the three layers (axe-core, wcagcheckr DOM analyzers, AI walkthroughs). All 50 WCAG 2.1 AA criteria evaluated; content-meaning checks (caption accuracy, alt-text correctness, sensory cues) benefit from a human spot-check before formal conformance claims.",B:"Only moderate-impact violations detected. No serious or critical findings.",C:"Has at least one serious-impact violation. Automatically capped here regardless of how few.",D:"Has at least one critical-impact violation. Automatically capped here regardless of how few.",F:"Multiple critical-impact violations detected. Lawsuit-magnet profile."};function jh(e,t){if(e!=="B")return Xn[e];const{moderateCount:a,blockingCriteriaCount:n}=t;return n>0&&a===0?`No critical, serious, or moderate violations detected — but ${n} WCAG 2.1 AA criteri${n===1?"on has":"a have"} not yet been affirmatively evaluated (untested or inconclusive). The grade is capped at B until coverage is complete. Open the side panel's "Coverage gaps" panel under the grade and record a verdict for each.`:n>0&&a>0?`${a} moderate WCAG finding${a===1?"":"s"} detected, AND ${n} criteri${n===1?"on has":"a have"} not yet been affirmatively evaluated. Address both before any formal conformance claim.`:Xn.B}const at={"ai-alt-misleading":{summary:`The AI vision model judged this image's alt text is empty, misleading, or generic when the image is actually informative or functional. Use the AI assessment (shown above this recipe) to understand what the image depicts, then write descriptive alt text conveying its meaning to a screen-reader user. Use alt="" only when the image is genuinely decorative (a divider, a flourish, an icon next to text that already labels the action). Decorative is rare — when in doubt, describe.`,snippetLang:"html",snippet:`<!-- Failing — empty alt on an informational logo -->
4
+ (rc.299 math-salvage: re-computed from cited rgb(${a.r},${a.g},${a.b}) on rgb(${n.r},${n.g},${n.b}) → ${r.toFixed(2)}:1 against threshold ${i}:1 → PASS. Original AI verdict was FAIL but its arithmetic appears wrong on this element. One-way salvage: only flips fail→pass, never pass→fail.)`}}function hr(e,t){var n;const a=new Set;for(const o of t)for(const r of((n=o.axeRulesEvaluated)==null?void 0:n.incomplete)??[])for(const i of r.elements??[])a.add(`${r.ruleId}::${i.selector}`);return e.map(dr).map(ur).filter(o=>o.verdict==="fail"&&a.has(`${o.ruleId}::${o.selector}`))}function Fs(e){const t=/rgba?\(\s*([0-9.]+)[,\s]+([0-9.]+)[,\s]+([0-9.]+)(?:[,/\s]+([0-9.]+))?\s*\)/gi,a=[];let n;for(;(n=t.exec(e))!==null&&(a.push({r:Math.round(parseFloat(n[1])),g:Math.round(parseFloat(n[2])),b:Math.round(parseFloat(n[3])),a:n[4]!==void 0?parseFloat(n[4]):1}),!(a.length>=4)););return a}function _s(e){const t=/([34](?:\.\d+)?|7(?:\.\d+)?)\s*:\s*1/g,a=[];let n;for(;(n=t.exec(e))!==null;){const o=parseFloat(n[1]);(o===3||o===4.5||o===7)&&a.push(o)}return a.length>0?a[a.length-1]:null}function Us(e,t){const a=d=>{const l=d/255;return l<=.03928?l/12.92:Math.pow((l+.055)/1.055,2.4)},n=d=>.2126*a(d.r)+.7152*a(d.g)+.0722*a(d.b),o=n(e),r=n(t),i=Math.max(o,r),c=Math.min(o,r);return(i+.05)/(c+.05)}const Ws=Object.freeze(Object.defineProperty({__proto__:null,applyContrastMathSalvage:ur,applySelfCorrectionSalvage:dr,clearAllResolutions:Ps,deleteResolution:Ns,filterRelevant:hr,getAllResolutions:lr,getResolutionsForPage:ba,saveResolution:je,saveResolutions:Ls},Symbol.toStringTag,{value:"Module"})),Kt="interactiveAuditResults";function kn(e,t){return`${e}::${t}`}async function Rt(){try{const t=(await chrome.storage.local.get(Kt))[Kt];return t&&typeof t=="object"?t:{}}catch{return{}}}async function xn(e){await chrome.storage.local.set({[Kt]:e})}async function oe(e){const t=await Rt(),a={componentId:e.componentId,criterionId:e.criterionId,verdict:e.verdict,reasoning:e.reasoning,confidence:e.confidence,pageUrl:e.pageUrl,finishedAt:e.finishedAt,costUsd:e.costUsd,model:e.model,inputHash:e.inputHash};t[kn(e.componentId,e.criterionId)]=a,await xn(t)}async function Fe(e,t){return(await Rt())[kn(e,t)]??null}async function Gs(){const e=await Rt();return Object.values(e)}async function tn(e){return(await Gs()).filter(a=>a.componentId===e)}async function Jh(e,t){const a=await Rt();delete a[kn(e,t)],await xn(a)}async function In(e){const t=new Set;for(const n of e)n&&t.add(n);if(t.size===0)return[];const a=[];for(const n of t)try{const o=await tn(n);for(const r of o)a.push({criterionId:r.criterionId,ruleId:`ai-interactive::${r.criterionId}`,pageUrl:r.pageUrl,verdict:r.verdict,reasoning:r.reasoning})}catch{}return a}async function Xh(){try{const e=await Rt();let t=!1;for(const a of Object.keys(e)){const n=e[a];if(n.steps&&n.steps.length>0){const o={componentId:n.componentId,criterionId:n.criterionId,verdict:n.verdict,reasoning:n.reasoning,confidence:n.confidence,pageUrl:n.pageUrl,finishedAt:n.finishedAt,costUsd:n.costUsd,model:n.model};e[a]=o,t=!0}}t&&await xn(e)}catch{try{await chrome.storage.local.remove(Kt)}catch{}}}const Vs=te("walkthrough-acks"),an="walkthroughAcks";function Sn(e,t,a){return`${e}::${t}::${a}`}async function ya(){try{const t=(await chrome.storage.local.get(an))[an];return t&&typeof t=="object"?t:{}}catch{return{}}}async function pr(e){await chrome.storage.local.set({[an]:e})}async function Qh(e){if(!e.note||!e.note.trim())throw new Error("Acknowledgement requires a non-empty note explaining manual verification.");const t=await ya();t[Sn(e.componentId,e.criterionId,e.pageUrl)]={...e,note:e.note.trim()},await pr(t),Vs.info(`acknowledged ${e.criterionId} on ${e.pageUrl}`)}async function Zh(e,t,a){const n=await ya();delete n[Sn(e,t,a)],await pr(n)}async function js(e,t,a){return(await ya())[Sn(e,t,a)]??null}async function Hs(){const e=await ya();return Object.values(e)}async function fr(e){const t=new Set;for(const n of e)n&&t.add(n);return t.size===0?[]:(await Hs()).filter(n=>t.has(n.componentId))}const Yt={A:4,B:3,C:2,D:1,F:0},Bs=["F","D","C","B","A"];function qs(e,t){const a=(Yt[e]+Yt[t])/2,n=Math.ceil(a-.5);return Bs[n]}function zs(e,t){return Yt[e]<=Yt[t]?e:t}const Ma=[{id:"contrast",label:"Color contrast",rules:["color-contrast","color-contrast-enhanced","link-in-text-block"],needsManualCheck:!1},{id:"alt",label:"Image alt text",rules:["image-alt","input-image-alt","area-alt","svg-img-alt","object-alt","role-img-alt"],needsManualCheck:!1},{id:"labels",label:"Form labels",rules:["label","select-name","aria-input-field-name","aria-toggle-field-name","form-field-multiple-labels","label-content-name-mismatch","autocomplete-valid"],needsManualCheck:!1},{id:"names",label:"Buttons / links named",rules:["button-name","input-button-name","link-name","empty-button","empty-heading","empty-link"],needsManualCheck:!1},{id:"keyboard",label:"Keyboard operability",rules:["nested-interactive","tabindex","frame-focusable-content","no-focusable-content"],needsManualCheck:!0},{id:"structure",label:"Landmarks + heading structure",rules:["heading-order","page-has-heading-one","landmark-one-main","landmark-no-duplicate-banner","landmark-no-duplicate-contentinfo","landmark-no-duplicate-main","region","bypass"],needsManualCheck:!1}];function Ks(e){const t=new Map;for(const a of e){const n=`${a.ruleId}::${a.target.selector}`;t.has(n)||t.set(n,a)}return Array.from(t.values())}function Ys(e,t,a="2.1",n="AA",o,r,i,c,d){const l=_i({audits:e??[],igtRuns:(t==null?void 0:t.runs)??[],workflows:(t==null?void 0:t.workflows)??Me,acknowledgedMatchKeys:o??new Set,incompleteResolutions:r,interactiveAuditResults:i,auditScope:c,humanCriterionVerdicts:d}),h={},m={},u=[],g=[],p=[];let f=0;for(const b of l.values()){if(b.state==="not-evaluated"){u.push(b.criterionId);continue}if(f++,b.state==="fail"){p.push(b.criterionId);const A=[];for(const w of b.evidence)w.outcome==="fail"&&A.push({source:w.source,ruleOrStepId:w.ruleOrStepId,selector:w.selector,notes:w.notes,pageUrl:w.pageUrl});A.length>0&&(m[b.criterionId]=A)}if(b.state==="inconclusive"){g.push(b.criterionId);const A=[];for(const w of b.evidence)if(w.outcome==="incomplete"){const s=w.source==="ai"?"ai-uncertain":"axe-incomplete";A.push({source:s,ruleOrStepId:w.ruleOrStepId,resolutionHint:w.resolutionHint,elements:w.elements,pageUrl:w.pageUrl})}else w.outcome==="skip"&&A.push({source:"igt-skip",ruleOrStepId:w.ruleOrStepId,resolutionHint:w.resolutionHint});A.length>0&&(h[b.criterionId]=A)}}const y=Ui(l);return{targetVersion:Tt,targetLevel:fn,totalApplicable:l.size,evaluated:f,untestedCriteria:u,inconclusiveCriteria:g,failingCriteria:p,inconclusiveReasons:h,failingReasons:m,canClaimConformance:y.canClaimConformance}}function wa(e,t,a,n,o,r,i,c,d){const l=n??new Set,h=Ks(e).filter(F=>!l.has(F.matchKey)),m=h.filter(F=>F.needsReview),u=h.filter(F=>!F.needsReview),g={critical:0,serious:0,moderate:0,minor:0,unique:u.length};for(const F of u)g[F.impact]++;let p;t&&(p=Js(t.runs,t.workflows),g.serious+=p.failedRequired,g.moderate+=p.failedAdvisory,g.unique+=p.failedRequired+p.failedAdvisory);const f=[],y=[];if(o&&o.length>0){const F=new Set;if(r)for(const N of r)F.add(`${N.criterionId}::${N.pageUrl}`);const T=new Set;for(const N of o){if(N.verdict==="pass")continue;const O=`${N.criterionId}::${N.pageUrl}`;if(T.has(O))continue;T.add(O);const D=N.verdict==="fail"?"serious":"moderate",G={criterionId:N.criterionId,pageUrl:N.pageUrl,verdict:N.verdict,impact:D,reasoning:N.reasoning};if(F.has(O)){y.push(G);continue}f.push(G),g[D]++,g.unique++}}const b=new Map;for(const F of Ma)for(const T of F.rules)b.set(T,F);const A=new Map;for(const F of Ma)A.set(F.id,{count:0,rules:new Set});for(const F of ir(u)){const T=b.get(F.ruleId);if(!T)continue;const N=A.get(T.id);N.count++,N.rules.add(F.ruleId)}const w=Ma.map(F=>{const T=A.get(F.id);let N="pass";return T.count>0?N="fail":F.needsManualCheck&&(N="unchecked"),{id:F.id,label:F.label,status:N,violationCount:T.count,rules:Array.from(T.rules),needsManualCheck:F.needsManualCheck}}),s=g.critical>0,v=g.serious>0,S=(()=>{if(!o||o.length===0||!r||r.length===0)return o;const F=new Set;for(const T of r)F.add(`${T.criterionId}::${T.pageUrl}`);return o.map(T=>F.has(`${T.criterionId}::${T.pageUrl}`)?{...T,verdict:"pass",reasoning:`Human verified: ${T.reasoning}`}:T)})(),k=a?Ys(a,t,"2.1","AA",n,d,S,c,i):void 0,x=k?!k.canClaimConformance:!1;let L;g.critical>=5?L="F":g.critical>=1?L="D":g.serious>=5||g.serious>=1?L="C":g.moderate>=3||g.moderate>=1?L="B":(g.minor>=1,L="A"),x&&L==="A"&&(L="B");const $=w.filter(F=>F.status==="fail").length;let U;return g.critical>0||$>=4?U="critical":g.serious>=3||$>=2?U="high":g.serious>=1||g.moderate>=3?U="moderate":U="low",{letter:L,risk:U,caps:{cappedAtC:s,cappedAtB:v,cappedAtBByCoverage:x},totals:g,categories:w,coverage:k,manual:p,reviewQueue:{count:m.length,rules:Array.from(new Set(m.map(F=>F.ruleId)))},walkthroughFindings:f,walkthroughAcknowledgements:y}}function Js(e,t){new Map(t.map(c=>[c.id,c]));const a=new Map(e.map(c=>[c.workflowId,c]));let n=0,o=0,r=0;const i=t.map(c=>{const d=a.get(c.id);let l=0,h=0,m=0,u=0,g=0;for(const p of c.steps){const f=d==null?void 0:d.steps[p.id];f&&(g++,f.status==="pass"?l++:f.status==="skip"?u++:f.status==="fail"&&(p.severity==="required"?h++:m++))}return g===c.steps.length&&r++,n+=h,o+=m,{workflowId:c.id,name:c.name,passed:l,failedRequired:h,failedAdvisory:m,skipped:u,answered:g,total:c.steps.length}});return{workflowsCompleted:r,workflowsTotal:t.length,failedRequired:n,failedAdvisory:o,perWorkflow:i}}const Xs={low:"Low exposure — no critical or serious violations across the three layers (axe-core, wcagcheckr DOM analyzers, AI walkthroughs). All 50 WCAG 2.1 AA criteria have an evaluation path; spot-check content-meaning criteria (caption accuracy, alt-text correctness) before any formal conformance claim.",moderate:"Moderate exposure — some serious or multiple moderate violations. Should be addressed but not lawsuit-emergency level.",high:"High exposure — multiple serious violations OR multiple lawsuit-magnet categories failing. ADA Title III suits target sites at this level.",critical:"Critical exposure — at least one critical-impact violation OR most lawsuit-magnet categories failing. Sites in this state are demand-letter targets."},gr={low:"✓ Off the radar",moderate:"⚡ Some lawsuit exposure",high:"⚠️ High target risk",critical:"🚨 Lawsuit magnet"},mr={low:"Your site clears the automated checks ADA-targeting tools use to identify lawsuit victims. wcagcheckr runs three layers (axe-core, our DOM analyzers, AI walkthroughs) covering all 50 WCAG 2.1 AA criteria — most demand-letter mills only run axe.",moderate:"Some failures. Not the easiest target, but the issues here are exactly what automated targeting tools look for. Switch to Developer mode for the issue list and fix-it walkthroughs.",high:"Multiple serious failures. Automated lawsuit-screening tools will flag this site. Real risk of being targeted with a demand letter — Developer mode has the full issue list and fix-it walkthroughs.",critical:"Your site fails the automated WCAG checks demand-letter mills run on thousands of sites to identify lawsuit targets. Without remediation, expect to be flagged. Developer mode has the full issue list plus fix-it guidance for each."},Zn={A:"No critical, serious, or moderate violations across the three layers (axe-core, wcagcheckr DOM analyzers, AI walkthroughs). All 50 WCAG 2.1 AA criteria evaluated; content-meaning checks (caption accuracy, alt-text correctness, sensory cues) benefit from a human spot-check before formal conformance claims.",B:"Only moderate-impact violations detected. No serious or critical findings.",C:"Has at least one serious-impact violation. Automatically capped here regardless of how few.",D:"Has at least one critical-impact violation. Automatically capped here regardless of how few.",F:"Multiple critical-impact violations detected. Lawsuit-magnet profile."};function ep(e,t){if(e!=="B")return Zn[e];const{moderateCount:a,blockingCriteriaCount:n}=t;return n>0&&a===0?`No critical, serious, or moderate violations detected — but ${n} WCAG 2.1 AA criteri${n===1?"on has":"a have"} not yet been affirmatively evaluated (untested or inconclusive). The grade is capped at B until coverage is complete. Open the side panel's "Coverage gaps" panel under the grade and record a verdict for each.`:n>0&&a>0?`${a} moderate WCAG finding${a===1?"":"s"} detected, AND ${n} criteri${n===1?"on has":"a have"} not yet been affirmatively evaluated. Address both before any formal conformance claim.`:Zn.B}const ot={"ai-alt-misleading":{summary:`The AI vision model judged this image's alt text is empty, misleading, or generic when the image is actually informative or functional. Use the AI assessment (shown above this recipe) to understand what the image depicts, then write descriptive alt text conveying its meaning to a screen-reader user. Use alt="" only when the image is genuinely decorative (a divider, a flourish, an icon next to text that already labels the action). Decorative is rare — when in doubt, describe.`,snippetLang:"html",snippet:`<!-- Failing — empty alt on an informational logo -->
5
5
  <img src="/assets/logo.png" alt="">
6
6
 
7
7
  <!-- Passing — descriptive alt -->
@@ -36,8 +36,8 @@ var Ti=Object.defineProperty;var Ei=(e,t,a)=>t in e?Ti(e,t,{enumerable:!0,config
36
36
  <tr style="background:#efe">Passed run</tr>
37
37
 
38
38
  <!-- Passing — colour + icon + text -->
39
- <tr><td>❌ Failed</td>...</tr>
40
- <tr><td>✅ Passed</td>...</tr>`},"ai-generic-link-text":{summary:'WCAG 2.4.4 — Link purpose must be clear from the link text alone OR the link text plus its immediate surrounding context. "Click here", "Learn more", "Read this", and similarly vague text fail when a screen-reader user navigates a list of links out of context. Rewrite the link to describe its destination, ensure the surrounding sentence makes the destination unambiguous, or use aria-label / aria-labelledby to attach a descriptive name.',snippetLang:"html",snippet:`<!-- Failing — link text alone is meaningless -->
39
+ <tr><td>FAIL</td>...</tr>
40
+ <tr><td>PASS</td>...</tr>`},"ai-generic-link-text":{summary:'WCAG 2.4.4 — Link purpose must be clear from the link text alone OR the link text plus its immediate surrounding context. "Click here", "Learn more", "Read this", and similarly vague text fail when a screen-reader user navigates a list of links out of context. Rewrite the link to describe its destination, ensure the surrounding sentence makes the destination unambiguous, or use aria-label / aria-labelledby to attach a descriptive name.',snippetLang:"html",snippet:`<!-- Failing — link text alone is meaningless -->
41
41
  <p>Our pricing has changed. <a href="/pricing">Click here</a>.</p>
42
42
 
43
43
  <!-- Passing — link text is self-describing -->
@@ -73,24 +73,24 @@ img, video, iframe { max-width: 100%; height: auto; }
73
73
  <img src="divider.svg" alt="" />
74
74
 
75
75
  <!-- Informative -->
76
- <img src="chart.png" alt="Q3 revenue rose 42%" />`},"button-name":{summary:"Every <button> needs an accessible name. Provide visible text, an aria-label, or aria-labelledby. Icon-only buttons especially need aria-label so screen reader users hear what the button does.",snippetLang:"html",snippet:`<!-- Visible text -->
76
+ <img src="chart.png" alt="Q3 revenue rose 42%" />`},"button-name":{summary:"Every <button> needs an accessible name. Provide visible text, an aria-label, or aria-labelledby. Icon-only buttons especially need aria-label so screen reader users hear what the button does.",snippetLang:"html",snippet:`<!-- GOOD: Visible text -->
77
77
  <button>Save changes</button>
78
78
 
79
- <!-- Icon-only with aria-label -->
79
+ <!-- GOOD: Icon-only with aria-label -->
80
80
  <button aria-label="Close dialog">
81
81
  <svg aria-hidden="true">...</svg>
82
- </button>`},"link-name":{summary:'Links must have discernible text. Avoid empty <a> tags or links containing only an icon without an aria-label. "Read more" links should reference what they read more about via aria-label or surrounding context.',snippetLang:"html",snippet:`<!-- Generic -->
82
+ </button>`},"link-name":{summary:'Links must have discernible text. Avoid empty <a> tags or links containing only an icon without an aria-label. "Read more" links should reference what they read more about via aria-label or surrounding context.',snippetLang:"html",snippet:`<!-- BAD: Generic -->
83
83
  <a href="/post/42">Read more</a>
84
84
 
85
- <!-- Specific -->
85
+ <!-- GOOD: Specific -->
86
86
  <a href="/post/42">Read more about Q3 revenue</a>
87
87
 
88
- <!-- Or with aria-label -->
89
- <a href="/post/42" aria-label="Read more about Q3 revenue">Read more</a>`},"heading-order":{summary:"Don't skip heading levels. After an <h2>, the next heading should be <h2> or <h3> — never <h4>. Screen reader users navigate by heading level, and skipped levels imply missing structure.",snippetLang:"html",snippet:`<!-- -->
88
+ <!-- GOOD: Or with aria-label -->
89
+ <a href="/post/42" aria-label="Read more about Q3 revenue">Read more</a>`},"heading-order":{summary:"Don't skip heading levels. After an <h2>, the next heading should be <h2> or <h3> — never <h4>. Screen reader users navigate by heading level, and skipped levels imply missing structure.",snippetLang:"html",snippet:`<!-- BAD -->
90
90
  <h1>Page title</h1>
91
91
  <h3>Section</h3>
92
92
 
93
- <!-- -->
93
+ <!-- GOOD -->
94
94
  <h1>Page title</h1>
95
95
  <h2>Section</h2>`},"landmark-one-main":{summary:`Every page should have exactly one <main> element (or one element with role="main"). It marks the start of the page's primary content for screen reader users.`,snippetLang:"html",snippet:`<body>
96
96
  <header>...</header>
@@ -98,14 +98,14 @@ img, video, iframe { max-width: 100%; height: auto; }
98
98
  <!-- primary content -->
99
99
  </main>
100
100
  <footer>...</footer>
101
- </body>`},"landmark-no-duplicate-banner":{summary:'A page should have at most one <header> at the top level (or one [role="banner"]). Multiple top-level banners confuse landmark navigation.'},"landmark-no-duplicate-contentinfo":{summary:'A page should have at most one <footer> at the top level (or one [role="contentinfo"]). Multiple top-level footers confuse landmark navigation.'},"aria-required-attr":{summary:'When using an ARIA role, you must include all required attributes for that role. For example, role="checkbox" requires aria-checked; role="combobox" requires aria-expanded.',snippetLang:"html",snippet:`<!-- Missing aria-checked -->
101
+ </body>`},"landmark-no-duplicate-banner":{summary:'A page should have at most one <header> at the top level (or one [role="banner"]). Multiple top-level banners confuse landmark navigation.'},"landmark-no-duplicate-contentinfo":{summary:'A page should have at most one <footer> at the top level (or one [role="contentinfo"]). Multiple top-level footers confuse landmark navigation.'},"aria-required-attr":{summary:'When using an ARIA role, you must include all required attributes for that role. For example, role="checkbox" requires aria-checked; role="combobox" requires aria-expanded.',snippetLang:"html",snippet:`<!-- BAD: Missing aria-checked -->
102
102
  <div role="checkbox">Subscribe</div>
103
103
 
104
- <!-- -->
105
- <div role="checkbox" aria-checked="false" tabindex="0">Subscribe</div>`},"aria-allowed-attr":{summary:"Some ARIA attributes only apply to specific roles. For example, aria-required is only valid on form widgets; putting it on a <div> with no role is wrong. Either remove the attribute or add the appropriate role."},"aria-roles":{summary:'The role attribute value must be a valid ARIA role from the WAI-ARIA spec. Common typos: "buton", "tab-list", "menuitem-radio". Check the WAI-ARIA spec for the exact role name.'},"aria-valid-attr":{summary:"Use ARIA attribute names exactly as specified in WAI-ARIA. Common typos: aria-labeledby (should be aria-labelledby with two L's), aria-describeby (should be aria-describedby)."},"duplicate-id":{summary:"IDs must be unique within a document. Duplicate IDs break label association, fragment navigation, and aria-labelledby/aria-describedby references."},"duplicate-id-aria":{summary:"IDs referenced by ARIA attributes (aria-labelledby, aria-describedby, aria-controls) must be unique. Duplicate IDs cause assistive tech to associate the wrong element."},"empty-button":{summary:"A button with no text and no aria-label has no name for screen readers. Add visible text or an aria-label describing what the button does."},"empty-heading":{summary:"Heading elements should contain text. An empty heading provides no information and confuses screen reader heading navigation. Either add text or remove the heading."},"empty-link":{summary:'A link with no text and no aria-label has no name. Screen reader users hear "link" with no destination cue. Add link text or aria-label.'},"form-field-multiple-labels":{summary:'A form field should have one label, not multiple. Multiple labels (e.g., wrapping <label> + separate <label for="">) confuse assistive tech about which is the canonical name.'},"html-has-lang":{summary:"The root <html> element needs a lang attribute so screen readers use the correct pronunciation rules.",snippetLang:"html",snippet:'<html lang="en">'},"html-lang-valid":{summary:`The lang attribute value must be a valid IANA language tag. Use "en" for English, "es" for Spanish, "fr-CA" for Canadian French, etc. Don't invent codes.`},"input-button-name":{summary:'Input elements with type="button", type="submit", or type="reset" must have an accessible name via the value attribute or aria-label.',snippetLang:"html",snippet:'<input type="submit" value="Save changes" />'},"input-image-alt":{summary:"Image-type inputs must have alt text describing the button's action.",snippetLang:"html",snippet:'<input type="image" src="search.png" alt="Search" />'},"meta-viewport":{summary:"Don't set user-scalable=no or maximum-scale less than 5.0 on the viewport meta. That blocks low-vision users from zooming.",snippetLang:"html",snippet:`<!-- Allows zoom -->
104
+ <!-- GOOD -->
105
+ <div role="checkbox" aria-checked="false" tabindex="0">Subscribe</div>`},"aria-allowed-attr":{summary:"Some ARIA attributes only apply to specific roles. For example, aria-required is only valid on form widgets; putting it on a <div> with no role is wrong. Either remove the attribute or add the appropriate role."},"aria-roles":{summary:'The role attribute value must be a valid ARIA role from the WAI-ARIA spec. Common typos: "buton", "tab-list", "menuitem-radio". Check the WAI-ARIA spec for the exact role name.'},"aria-valid-attr":{summary:"Use ARIA attribute names exactly as specified in WAI-ARIA. Common typos: aria-labeledby (should be aria-labelledby with two L's), aria-describeby (should be aria-describedby)."},"duplicate-id":{summary:"IDs must be unique within a document. Duplicate IDs break label association, fragment navigation, and aria-labelledby/aria-describedby references."},"duplicate-id-aria":{summary:"IDs referenced by ARIA attributes (aria-labelledby, aria-describedby, aria-controls) must be unique. Duplicate IDs cause assistive tech to associate the wrong element."},"empty-button":{summary:"A button with no text and no aria-label has no name for screen readers. Add visible text or an aria-label describing what the button does."},"empty-heading":{summary:"Heading elements should contain text. An empty heading provides no information and confuses screen reader heading navigation. Either add text or remove the heading."},"empty-link":{summary:'A link with no text and no aria-label has no name. Screen reader users hear "link" with no destination cue. Add link text or aria-label.'},"form-field-multiple-labels":{summary:'A form field should have one label, not multiple. Multiple labels (e.g., wrapping <label> + separate <label for="">) confuse assistive tech about which is the canonical name.'},"html-has-lang":{summary:"The root <html> element needs a lang attribute so screen readers use the correct pronunciation rules.",snippetLang:"html",snippet:'<html lang="en">'},"html-lang-valid":{summary:`The lang attribute value must be a valid IANA language tag. Use "en" for English, "es" for Spanish, "fr-CA" for Canadian French, etc. Don't invent codes.`},"input-button-name":{summary:'Input elements with type="button", type="submit", or type="reset" must have an accessible name via the value attribute or aria-label.',snippetLang:"html",snippet:'<input type="submit" value="Save changes" />'},"input-image-alt":{summary:"Image-type inputs must have alt text describing the button's action.",snippetLang:"html",snippet:'<input type="image" src="search.png" alt="Search" />'},"meta-viewport":{summary:"Don't set user-scalable=no or maximum-scale less than 5.0 on the viewport meta. That blocks low-vision users from zooming.",snippetLang:"html",snippet:`<!-- GOOD: Allows zoom -->
106
106
  <meta name="viewport" content="width=device-width, initial-scale=1" />
107
107
 
108
- <!-- Blocks zoom -->
108
+ <!-- BAD: Blocks zoom -->
109
109
  <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />`},"nested-interactive":{summary:"Don't put interactive elements inside other interactive elements. A button inside a link, or a link inside a button, breaks keyboard activation and screen reader announcements."},"presentation-role-conflict":{summary:`role="presentation" or role="none" removes an element's semantics. Don't apply it to elements that have important interactive properties (focusable, named) — those will conflict and be ignored.`},"select-name":{summary:'Every <select> needs a programmatic label, just like <input> elements. Use <label for=""> or aria-label.'},"target-size":{summary:"Interactive targets must be at least 24×24 CSS px (WCAG 2.5.8 AA) or 44×44 (2.5.5 AAA). Small touch targets are hard to hit accurately, especially on mobile and for users with motor impairments. If the target itself can't be larger, ensure 24px of clear spacing between adjacent targets.",snippetLang:"css",snippet:`/* Pad the hit area without changing visual size */
110
110
  .icon-button {
111
111
  width: 24px;
@@ -359,23 +359,31 @@ p { color: #333; }
359
359
  p a { color: #06f; text-decoration: none; }
360
360
 
361
361
  /* Passing — underline added */
362
- p a { color: #06f; text-decoration: underline; }`,snippetLang:"css"}};function Gh(e){return at[e]??null}const tn="aiColorSuggestions";async function ba(){try{const t=(await chrome.storage.local.get(tn))[tn];return t&&typeof t=="object"?t:{}}catch{return{}}}async function fr(e){await chrome.storage.local.set({[tn]:e})}async function Hh(e){const t=await ba();t[e.matchKey]=e,await fr(t)}async function Bh(e){if(e.length===0)return;const t=await ba();for(const a of e)t[a.matchKey]=a;await fr(t)}async function zh(e){return(await ba())[e]??null}async function Bs(){return await ba()}const gr={AT:{name:"Austria — Beschwerdestelle Bundessozialamt",url:"https://www.sozialministeriumservice.at/"},BE:{name:"Belgium — Unia / Bosa",url:"https://accessibility.belgium.be/"},BG:{name:"Bulgaria — State e-Government Agency",url:"https://e-gov.bg/"},CY:{name:"Cyprus — Department of Electronic Communications",url:"https://www.mtcw.gov.cy/"},CZ:{name:"Czechia — Ministry of the Interior",url:"https://www.mvcr.cz/"},DE:{name:"Germany — Bundesfachstelle Barrierefreiheit",url:"https://www.bundesfachstelle-barrierefreiheit.de/"},DK:{name:"Denmark — Digitaliseringsstyrelsen",url:"https://www.digst.dk/"},EE:{name:"Estonia — Ministry of Economic Affairs and Communications",url:"https://mkm.ee/"},ES:{name:"Spain — Observatorio de Accesibilidad Web",url:"https://administracionelectronica.gob.es/"},FI:{name:"Finland — Etelä-Suomen aluehallintovirasto",url:"https://www.saavutettavuusvaatimukset.fi/"},FR:{name:"France — Défenseur des droits",url:"https://www.defenseurdesdroits.fr/"},GR:{name:"Greece — Ministry of Digital Governance",url:"https://mindigital.gr/"},HR:{name:"Croatia — Information Commissioner",url:"https://www.pristupinfo.hr/"},HU:{name:"Hungary — Information Society Authority",url:"https://nmhh.hu/"},IE:{name:"Ireland — National Disability Authority",url:"https://nda.ie/"},IT:{name:"Italy — Agenzia per l'Italia Digitale",url:"https://www.agid.gov.it/"},LT:{name:"LithuaniaInformation Society Development Committee",url:"https://ivpk.lrv.lt/"},LU:{name:"Luxembourg — Service Information et Presse",url:"https://accessibilite.public.lu/"},LV:{name:"Latvia — VARAM",url:"https://www.varam.gov.lv/"},MT:{name:"Malta — MITA",url:"https://www.mita.gov.mt/"},NL:{name:"Netherlands — Logius",url:"https://www.digitoegankelijk.nl/"},PL:{name:"Poland — Ministry of Digital Affairs",url:"https://www.gov.pl/web/dostepnosc-cyfrowa/"},PT:{name:"Portugal AMA",url:"https://accessmonitor.acessibilidade.gov.pt/"},RO:{name:"Romania ANCOM",url:"https://www.ancom.ro/"},SE:{name:"Sweden DIGG",url:"https://www.digg.se/"},SI:{name:"Slovenia Ministry of Public Administration",url:"https://www.gov.si/"},SK:{name:"Slovakia — Ministry of Investments",url:"https://www.mirri.gov.sk/"}},mr=(e,t)=>`${e} is committed to making its website accessible, in accordance with national legislation transposing Directive (EU) 2016/2102 of the European Parliament and of the Council. This accessibility statement applies to ${t}.`,br=(e,t)=>`${e} is committed to making its products and services accessible to all users, in accordance with the European Accessibility Act (Directive (EU) 2019/882) and its national transpositions, enforceable from 28 June 2025. This accessibility statement applies to ${t}.`,yr=(e,t,a,n="EN 301 549 v3.2.1")=>{const o=`Web Content Accessibility Guidelines (WCAG) ${t} level ${a}`;switch(e){case"full":return`This website is fully conformant with ${o} and the corresponding ${n} clauses.`;case"partial":return`This website is partially conformant with ${o} and the corresponding ${n} clauses. "Partially conformant" means that some parts of the content do not fully conform to the accessibility standard. The non-accessible content is listed below.`;case"non-conformant":return`This website is currently NOT conformant with ${o}. The non-accessible content is listed below, along with our plan to address it.`}},wr=(e,t)=>`The accessibility of this website was last evaluated on ${t}. The evaluation was conducted by ${e} using automated testing (wcagcheckr — multi-state matrix audit across 144 keyboard / focus / theme / breakpoint / direction combinations, AI-augmented walkthroughs for 5 criteria axe-core cannot evaluate, plus DOM-criterion analyzers for 16 additional criteria). The audit is conformant with the EN 301 549 evaluation methodology.`;function zs(e,t){const a=e.language||"English",n=e.auditedBy||"wcagcheckr (self-evaluation)",o=e.wcagVersion||"2.1",r=e.wcagLevel||"AA",i=[];if(i.push(`# Accessibility Statement — ${e.organizationName}`),i.push(""),i.push(`*Statement prepared on ${e.preparedAt}. Language: ${a}.*`),i.push(""),i.push(e.framing==="wad"?mr(e.organizationName,e.siteUrl):br(e.organizationName,e.siteUrl)),i.push(""),i.push("## Conformance status"),i.push(""),i.push(yr(e.conformanceStatus,o,r)),i.push(""),t.length>0){i.push("## Non-accessible content"),i.push(""),i.push("The following content is not yet accessible:"),i.push("");for(const c of t){const d=c.exemption?c.exemption==="disproportionate-burden"?" *(claimed under disproportionate burden — Article 5 WAD / Article 14 EAA)*":" *(not subject to applicable legislation)*":"";i.push(`- **WCAG ${c.criterion}** — ${c.description}${d}`)}i.push("")}else e.conformanceStatus==="full"&&(i.push("## Non-accessible content"),i.push(""),i.push("No non-accessible content is currently known. If you encounter any accessibility barriers, please contact us using the feedback details below."),i.push(""));if(i.push("## Preparation of this accessibility statement"),i.push(""),i.push(wr(n,e.evaluatedAt)),i.push(""),i.push("## Feedback and contact information"),i.push(""),i.push(`If you experience accessibility barriers on ${e.siteUrl}, please contact us so we can address them:`),i.push(""),e.contact.email&&i.push(`- **Email:** ${e.contact.email}`),e.contact.phone&&i.push(`- **Phone:** ${e.contact.phone}`),e.contact.formUrl&&i.push(`- **Web form:** ${e.contact.formUrl}`),i.push(""),e.framing==="wad"&&e.countryCode){const c=gr[e.countryCode];c&&(i.push("## Enforcement procedure"),i.push(""),i.push(`If you are not satisfied with the response to your accessibility complaint, you may contact the national enforcement body for ${c.name}:`),i.push(""),i.push(`${c.url}`),i.push(""))}return e.framing==="eaa"&&(i.push("## Enforcement"),i.push(""),i.push("Enforcement of the European Accessibility Act is carried out by national market-surveillance authorities in each EU member state. If your accessibility complaint is not addressed satisfactorily, you may contact the market-surveillance authority of the EU member state in which you are located."),i.push("")),i.push("---"),i.push(""),i.push(`_Statement generated by wcagcheckr. Audit methodology details + cryptographic anchoring of the audit results are available on request from ${e.contact.email??"the contact above"}._`),i.join(`
363
- `)}function qs(e,t){const a=l=>l==null?"":String(l).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;"),n=e.language||"English",o=e.languageCode||"en",r=e.auditedBy||"wcagcheckr (self-evaluation)",i=e.wcagVersion||"2.1",c=e.wcagLevel||"AA",d=[];if(d.push(`<article lang="${a(o)}">`),d.push(`<h1>Accessibility Statement — ${a(e.organizationName)}</h1>`),d.push(`<p><em>Statement prepared on ${a(e.preparedAt)}. Language: ${a(n)}.</em></p>`),d.push(`<p>${a(e.framing==="wad"?mr(e.organizationName,e.siteUrl):br(e.organizationName,e.siteUrl))}</p>`),d.push("<h2>Conformance status</h2>"),d.push(`<p>${a(yr(e.conformanceStatus,i,c))}</p>`),t.length>0){d.push("<h2>Non-accessible content</h2>"),d.push("<p>The following content is not yet accessible:</p>"),d.push("<ul>");for(const l of t){const h=l.exemption?l.exemption==="disproportionate-burden"?" <em>(claimed under disproportionate burden Article 5 WAD / Article 14 EAA)</em>":" <em>(not subject to applicable legislation)</em>":"";d.push(`<li><strong>WCAG ${a(l.criterion)}</strong> — ${a(l.description)}${h}</li>`)}d.push("</ul>")}else e.conformanceStatus==="full"&&(d.push("<h2>Non-accessible content</h2>"),d.push("<p>No non-accessible content is currently known. If you encounter any accessibility barriers, please contact us using the feedback details below.</p>"));if(d.push("<h2>Preparation of this accessibility statement</h2>"),d.push(`<p>${a(wr(r,e.evaluatedAt))}</p>`),d.push("<h2>Feedback and contact information</h2>"),d.push(`<p>If you experience accessibility barriers on ${a(e.siteUrl)}, please contact us so we can address them:</p>`),d.push("<ul>"),e.contact.email&&d.push(`<li><strong>Email:</strong> <a href="mailto:${a(e.contact.email)}">${a(e.contact.email)}</a></li>`),e.contact.phone&&d.push(`<li><strong>Phone:</strong> ${a(e.contact.phone)}</li>`),e.contact.formUrl&&d.push(`<li><strong>Web form:</strong> <a href="${a(e.contact.formUrl)}">${a(e.contact.formUrl)}</a></li>`),d.push("</ul>"),e.framing==="wad"&&e.countryCode){const l=gr[e.countryCode];l&&(d.push("<h2>Enforcement procedure</h2>"),d.push(`<p>If you are not satisfied with the response to your accessibility complaint, you may contact the national enforcement body for ${a(l.name)}: <a href="${a(l.url)}">${a(l.url)}</a></p>`))}return e.framing==="eaa"&&(d.push("<h2>Enforcement</h2>"),d.push("<p>Enforcement of the European Accessibility Act is carried out by national market-surveillance authorities in each EU member state. If your accessibility complaint is not addressed satisfactorily, you may contact the market-surveillance authority of the EU member state in which you are located.</p>")),d.push("<hr>"),d.push(`<p><small>Statement generated by wcagcheckr. Audit methodology details + cryptographic anchoring of the audit results are available on request from ${a(e.contact.email??"the contact above")}.</small></p>`),d.push("</article>"),d.join(`
364
- `)}function Ks(e,t={},a=new Set){var c;const n=e.flatMap(d=>d.violations),o=new Map;for(const d of n){const l=Oe(d.ruleId,d.target.selector);o.has(l)||o.set(l,d)}const r=new Map;for(const d of o.values()){const l=d.wcagCriterion;if(!l||a.has(l))continue;const h=r.get(l)??[];h.push(d),r.set(l,h)}const i=[];for(const[d,l]of r.entries()){const h=l[0];let m=t[d];if(!m){const u=at[h.ruleId];u!=null&&u.summary?m=((c=u.summary.split(/[.!?]\s+/)[0])==null?void 0:c.trim())??h.description:m=h.description}m=m.replace(new RegExp("^\\s*WCAG\\s+"+d.replace(/\./g,"\\.")+"\\b[\\s:.\\u2014-]*","i"),"").trim(),l.length>1&&(m+=` (affects ${l.length} components on the audited page${e.length>1?"s":""})`),i.push({criterion:d,description:m})}return i.sort((d,l)=>{const h=g=>g.split(".").map(f=>parseInt(f,10)),m=h(d.criterion),u=h(l.criterion);for(let g=0;g<Math.max(m.length,u.length);g++){const f=(m[g]??0)-(u[g]??0);if(f!==0)return f}return 0}),i}function Ys(e){const t=Ks(e.results,e.customDescriptions,e.dismissedCriteria);return e.outputFormat==="html"?qs(e.inputs,t):zs(e.inputs,t)}function qh(e){if(e.reduce((n,o)=>n+o.violations.length,0)===0)return"full";const a=new Set;for(const n of e)for(const o of n.violations)o.wcagCriterion&&a.add(o.wcagCriterion);return a.size>=10?"non-conformant":"partial"}function Be(e,t,a,n,o,r,i,c,d){var v,w,s,A;const l=ma(e,t,a,n,o,r,i,c,d),h=a??[],m=new Set((r??[]).map(S=>S.criterionId)),u={};for(const S of o??[]){const k=m.has(S.criterionId)?"pass":S.verdict;S.criterionId==="2.4.3"?u.focusOrder=k:S.criterionId==="2.1.2"?u.keyboardTrap=k:S.criterionId==="2.4.7"?u.focusVisible=k:S.criterionId==="1.3.2"?u.readingOrder=k:S.criterionId==="1.4.11"&&(u.nonTextContrast=k)}const g=lr(d??[],h).map(S=>({ruleId:S.ruleId,impact:"serious",selector:S.selector})),f=Is({violations:e,auditRan:h.length>0,manualRuns:(t==null?void 0:t.runs)??[],workflows:(t==null?void 0:t.workflows)??[],heuristicCounts:{tabOrder:((w=(v=h[0])==null?void 0:v.tabOrderIssues)==null?void 0:w.length)??0,readingOrder:((A=(s=h[0])==null?void 0:s.readingOrderIssues)==null?void 0:A.length)??0},interactiveAuditVerdicts:u,aiResolvedFails:g}),p=Us(l.letter,f.overallLetter);return{combined:l.totals.critical>0||l.totals.serious>0?Ws(p,l.letter):p,severity:l.letter,breadth:f.overallLetter,score:l,subScore:f}}const an="humanCriterionVerdicts";async function xn(){var a;if(typeof chrome>"u"||!((a=chrome.storage)!=null&&a.local))return[];const t=(await chrome.storage.local.get(an))[an];return Array.isArray(t)?t:[]}async function vr(e){const t=new Set;for(const n of e)n&&t.add(n);return t.size===0?[]:(await xn()).filter(n=>t.has(n.componentId))}async function Kh(e){if(!e.note||!e.note.trim())throw new Error("Human criterion verdict requires a non-empty note explaining the determination.");const a=(await xn()).filter(n=>!(n.componentId===e.componentId&&n.criterionId===e.criterionId));a.push({...e,note:e.note.trim()}),await Ar(a)}async function Yh(e,t){const n=(await xn()).filter(o=>!(o.componentId===e&&o.criterionId===t));await Ar(n)}async function Ar(e){var t;typeof chrome>"u"||!((t=chrome.storage)!=null&&t.local)||await chrome.storage.local.set({[an]:e})}function Wt(e){return/\[Cascade resolution rc\.\d+ escalation\]/.test(e)}function Jh(e){return/\[Manual verification — rc\.\d+\]/.test(e)}function Xh(e){const t=e.match(/\[Cascade resolution — rc\.\d+ (fallback-\d|escalation)\]/);if(!t)return null;const a=t[1];return a==="fallback-1"||a==="fallback-2"||a==="escalation"?a:null}const kr={ruleIds:["image-alt","label","button-name","link-name","color-contrast","document-title","html-has-lang"],thresholds:{moderate:1,high:2,critical:4},headlines:{low:"Low risk. Your site passes the checks common automated lawsuit-targeting scanners run today not likely automated lawfare target. This isn't a guarantee: scanners add checks over time, so re-audit as your site changes.",moderate:"Moderate risk. A demand-letter scanner could flag at least one issue. Fix to drop off the easy-target list.",high:"High risk. Multiple findings here are common patterns demand-letter mills target.",critical:"Critical risk. Your site has many high-frequency patterns ADA-targeting firms scan for."},version:0};kr.ruleIds;function Ir(){var e;return((e=tr())==null?void 0:e.lawfare)??kr}function xr(e,t=Ir()){return t.ruleIds.includes(e)}function Qn(e,t=Ir()){const a=new Set(t.ruleIds),n=new Map;for(const l of e)a.has(l.ruleId)&&n.set(l.ruleId,(n.get(l.ruleId)??0)+1);const o=Array.from(n.entries()).map(([l,h])=>({ruleId:l,instanceCount:h})).sort((l,h)=>h.instanceCount-l.instanceCount),r=o.length,i=Array.from(n.values()).reduce((l,h)=>l+h,0);let c;return r<t.thresholds.moderate?c="low":r<t.thresholds.high?c="moderate":r<t.thresholds.critical?c="high":c="critical",{tier:c,letter:c==="low"?"A":c==="moderate"?"C":c==="high"?"D":"F",flaggedRuleCount:r,flaggedInstanceCount:i,flaggedRules:o,headline:t.headlines[c],rubricVersion:t.version}}function Js(e,t){return e.flatMap(a=>a.violations).filter(a=>!a.needsReview&&!t.has(a.matchKey))}function Xs(e,t){const a=[],n={};for(const o of e){const r=Js(o.results,t);a.push(...r),n[o.url]=Qn(r).letter}return{site:Qn(a),perUrlLetter:n}}function Qh(e){return e.filter(t=>!t.needsReview&&xr(t.ruleId))}function st(e){return new Set(e.map(a=>a.pageUrl??a.scope??"").filter(Boolean)).size>1?"site-crawl":"single-page"}function Qs(e){const t=new Set;for(const a of e)a.componentId&&t.add(a.componentId);return Array.from(t)}async function Ee(e,t=[]){const a=Qs(e),n=new Set(t.filter(p=>!!p)),o=Array.from(new Set([...a,...n]));if(o.length===0)return{walkthroughs:[],walkthroughAcks:[],acknowledgedMatchKeys:new Set,humanCriterionVerdicts:[],incompleteResolutions:[],walkthroughsByComponent:new Map,walkthroughAcksByComponent:new Map,ackedKeysByComponent:new Map,humanVerdictsByComponent:new Map,incompleteResolutionsByComponent:new Map};const[r,i,c,d,l]=await Promise.all([Zs(o),ec(o),tc(o),ac(o),nc(e,o)]);if(n.size>0&&a.length>0){const p=new Map;for(const y of n){const b=d.get(y)??[];for(const v of b)p.has(v.criterionId)||p.set(v.criterionId,v)}if(p.size>0)for(const y of a){const b=d.get(y)??[],v=new Set(b.map(s=>s.criterionId)),w=[...b];for(const s of p.values())v.has(s.criterionId)||w.push({...s,componentId:y});d.set(y,w)}}const h=[];for(const p of r.values())h.push(...p);const m=[];for(const p of i.values())m.push(...p);const u=new Set;for(const p of c.values())for(const y of p)u.add(y);const g=[];for(const p of d.values())g.push(...p);const f=[];for(const p of l.values())f.push(...p);return{walkthroughs:h,walkthroughAcks:m,acknowledgedMatchKeys:u,humanCriterionVerdicts:g,incompleteResolutions:f,walkthroughsByComponent:r,walkthroughAcksByComponent:i,ackedKeysByComponent:c,humanVerdictsByComponent:d,incompleteResolutionsByComponent:l}}async function Zs(e){const t=new Map;return await Promise.all(e.map(async a=>{const n=await kn([a]);t.set(a,n)})),t}async function ec(e){const t=new Map,a=await ur(e);for(const n of e)t.set(n,[]);for(const n of a){const o=t.get(n.componentId);o&&o.push(n)}return t}async function tc(e){const t=new Map;return await Promise.all(e.map(async a=>{const n=await ha(a);t.set(a,n)})),t}async function ac(e){const t=new Map,a=await vr(e);for(const n of e)t.set(n,[]);for(const n of a){const o=t.get(n.componentId);o&&o.push(n)}return t}async function nc(e,t){const a=new Map;for(const o of e)if(o.componentId&&!a.has(o.componentId)){const r=o.pageUrl??o.scope;r&&a.set(o.componentId,r)}const n=new Map;return await Promise.all(t.map(async o=>{const r=a.get(o);n.set(o,r?await fa(r):[])})),n}function ze(e){return/<main[\s>]/i.test(e)?e:e.replace(/(<body[^>]*>)/i,`$1
362
+ p a { color: #06f; text-decoration: underline; }`,snippetLang:"css"},"document-title":{summary:'WCAG 2.4.2The page has no non-empty <title>. Every page needs a unique, descriptive title that names the page first and the site second, e.g. "Checkout Acme". It is the first thing a screen reader announces and what labels the browser tab/history.',snippetLang:"html",snippet:`<head>
363
+ <title>CheckoutAcme</title>
364
+ </head>`},list:{summary:`WCAG 1.3.1 A <ul>/<ol> contains direct children that are not <li> (or wrapping elements that break the list semantics). Screen readers stop announcing "list, N items". Fix: make <li> the only direct child of the list; move other content inside an <li>, and ensure any wrappers (e.g. from a component library) use role="list"/role="listitem" if the tags can't change.`},listitem:{summary:'WCAG 1.3.1 An <li> is not contained by a <ul>/<ol> (or a role="list" ancestor), so it loses its list semantics. Wrap the items in a proper list container, or add role="list" to the existing parent and role="listitem" to the items if the markup is fixed.'},"definition-list":{summary:"WCAG 1.3.1A <dl> is structured incorrectly: its direct children must be <dt>/<dd> (optionally grouped in <div>), in the right order. Remove stray elements and ensure every <dt> term is followed by one or more <dd> descriptions."},dlitem:{summary:"WCAG 1.3.1 A <dt> or <dd> is not inside a <dl>. Wrap the term/description pairs in a <dl> so the definition-list relationship is exposed."},"th-has-data-cells":{summary:`WCAG 1.3.1 A <th> header cell has no data cells associated with it, so the table's header/data relationship is broken. Ensure the table is a real data table with <td> cells under each header, give headers scope="col"/scope="row", and remove header cells used purely for layout.`,snippetLang:"html",snippet:`<table>
365
+ <tr><th scope="col">Name</th><th scope="col">Email</th></tr>
366
+ <tr><td>Ada</td><td>ada@x.com</td></tr>
367
+ </table>`},"td-headers-attr":{summary:`WCAG 1.3.1 — A cell's headers="" attribute references ids that don't exist (or aren't header cells) in the same table. Fix the ids so each headers value points at the id of a real <th> in the same table, or switch to scope="col"/scope="row" for simple tables.`},"scope-attr-valid":{summary:"WCAG 1.3.1 — A scope attribute has an invalid value or is on a non-header element. scope is only valid on <th> and must be one of col, row, colgroup, or rowgroup."},"empty-table-header":{summary:"WCAG 1.3.1 — A table header cell is empty. Either give the <th> a meaningful label, or if the corner cell is intentionally blank, leave it as <td></td> rather than <th>."},"aria-command-name":{summary:'WCAG 4.1.2 — An element with role="button"/"link"/"menuitem" has no accessible name. Give it visible text content, or aria-label / aria-labelledby pointing at visible text. Native <button>/<a> with text is preferable to ARIA roles.'},"aria-input-field-name":{summary:'WCAG 4.1.2 — An ARIA input field (role="textbox", "combobox", "listbox", "searchbox", "slider", "spinbutton") has no accessible name. Associate a visible <label>, or use aria-label / aria-labelledby. A native <input> with a <label> is the robust choice.'},"aria-toggle-field-name":{summary:'WCAG 4.1.2 — An ARIA toggle field (role="checkbox", "radio", "switch", "menuitemcheckbox", "menuitemradio") has no accessible name. Label it with visible text via aria-labelledby, or aria-label when no visible text exists.'},"aria-dialog-name":{summary:`WCAG 4.1.2 — A role="dialog"/"alertdialog" has no accessible name, so screen-reader users don't hear what the dialog is for. Add aria-labelledby pointing at the dialog's heading element, or aria-label.`},"aria-meter-name":{summary:'WCAG 1.1.1 — A role="meter" has no accessible name. Add aria-label or aria-labelledby describing what the meter measures (e.g. "Disk usage").'},"aria-progressbar-name":{summary:'WCAG 1.1.1 — A role="progressbar" has no accessible name. Add aria-label or aria-labelledby describing what is in progress (e.g. "Upload progress").'},"aria-tooltip-name":{summary:'WCAG 4.1.2 — A role="tooltip" has no accessible name. Give the tooltip text content or aria-label so it is announced.'},"aria-treeitem-name":{summary:'WCAG 4.1.2 — A role="treeitem" has no accessible name. Provide visible text content, or aria-label / aria-labelledby.'},"aria-deprecated-role":{summary:'An ARIA role that has been deprecated is in use. Replace it with the current equivalent (e.g. role="directory" → role="list"). Prefer the native HTML element that conveys the same semantics.'},"aria-text":{summary:'WCAG 1.3.1 — role="text" is on an element that has focusable descendants, which is not allowed (role="text" flattens its subtree). Remove role="text", or remove the interactive/focusable children from inside it.'},"aria-roledescription":{summary:"WCAG 4.1.2 — aria-roledescription is used on an element whose role doesn't support it (it requires a non-abstract role with semantics). Put it on an element with a supporting role, and keep the custom description short and meaningful."},"meta-refresh":{summary:'WCAG 2.2.1 / 2.2.4 — <meta http-equiv="refresh"> auto-refreshes or redirects the page on a timer, which can trap or disorient users. Remove it; do redirects server-side (3xx) and let users control any timed updates.'},marquee:{summary:"WCAG 2.2.2 — The deprecated <marquee> element auto-scrolls content with no way to pause it. Remove <marquee>; if motion is truly needed, use CSS animation that respects prefers-reduced-motion and provide a pause control."},blink:{summary:"WCAG 2.2.2 — The deprecated <blink> element makes content blink with no stop control. Remove it entirely; blinking text is a usability and seizure concern."},"video-caption":{summary:'WCAG 1.2.2 — A <video> has no captions track. Add a <track kind="captions"> (or "subtitles") with synchronized caption text for all meaningful audio so deaf/hard-of-hearing users get the dialogue and important sounds.',snippetLang:"html",snippet:`<video controls>
368
+ <source src="talk.mp4" type="video/mp4">
369
+ <track kind="captions" src="talk.en.vtt" srclang="en" label="English">
370
+ </video>`},"no-autoplay-audio":{summary:"WCAG 1.4.2 — Audio (or video with audio) plays automatically for more than 3 seconds with no built-in control to pause/stop or mute it. Remove autoplay, or provide an obvious, keyboard-operable pause/mute control near the start of the page."},"scrollable-region-focusable":{summary:`WCAG 2.1.1 — A scrollable region (overflow content) can't be reached or scrolled by keyboard. Make the container focusable with tabindex="0" (and an accessible name) so keyboard users can scroll it, or ensure a focusable child sits inside it.`},"server-side-image-map":{summary:"WCAG 2.1.1 — A server-side image map (<img ismap>) can only be used with a pointer. Replace it with a client-side image map (<map>/<area> with alt text) or, better, real <a> links so keyboard users can operate it."},accesskeys:{summary:"WCAG 4.1.1 — Two elements share the same accesskey value, so the shortcut is ambiguous. Make every accesskey unique, or remove them — accesskeys frequently collide with browser/AT shortcuts and are rarely worth keeping."},"css-orientation-lock":{summary:"WCAG 1.3.4 — CSS (a transform/orientation media query) locks content to a single orientation. Remove the lock so the page works in both portrait and landscape; users with fixed-mount devices depend on this."},"avoid-inline-spacing":{summary:'WCAG 1.4.12 — Inline style="" forces line-height, letter-spacing, or word-spacing with !important, preventing users from overriding text spacing. Move spacing to a stylesheet without !important so the text-spacing bookmarklet/user styles can adjust it.'},"summary-name":{summary:"WCAG 4.1.2 — A <summary> (disclosure control) has no accessible name. Give it visible text describing what it expands."},"frame-title-unique":{summary:"WCAG 4.1.2 — Multiple frames share the same title, so users can't tell them apart. Give each <iframe>/<frame> a unique, descriptive title naming its specific content."},"frame-focusable-content":{summary:'WCAG 2.1.1 — An <iframe> with tabindex="-1" contains focusable elements, creating a keyboard trap / unreachable content. Remove tabindex="-1" from the frame, or make the embedded content non-focusable.'}};function tp(e){return ot[e]??null}const Qs=new Set(["region","landmark-complementary-is-top-level","landmark-banner-is-top-level","landmark-contentinfo-is-top-level","landmark-main-is-top-level","link-in-text-block","scrollable-region-focusable","label-content-name-mismatch","identical-links-same-purpose"]);function Zs(e){if(e.needsReview)return{level:"needs-review",label:"Needs human confirmation",rationale:"Automation could not decide this either way — confirm against the live page before treating it as a defect."};if(e.ai){const t=Math.round((e.ai.confidence??0)*100);return{level:"ai-verified",label:`AI-verified (${t}%)`,rationale:t>=80?"A vision/text model judged this with high confidence (reasoning shown above).":"A model flagged this with moderate confidence — sanity-check its reasoning above before applying."}}return e.flakyAcrossRuns?{level:"medium",label:"Medium",rationale:"This did not reproduce in every audit run — it may be a transient (animation, lazy load, third-party script). Confirm it on a stable render before fixing."}:e.target.opacityContext&&(e.target.opacityContext.self<1||e.target.opacityContext.ancestor<1)?{level:"medium",label:"Medium",rationale:"The element (or an ancestor) was partially transparent at audit time — likely a mid-animation opacity, not a real color choice. Verify it is still low-contrast once fully rendered before changing colors."}:Qs.has(e.ruleId)?{level:"medium",label:"Medium",rationale:"This is a context-dependent best-practice check that can over-flag legitimate patterns. Apply the fix, but first confirm the flagged element genuinely fits the rule."}:{level:"high",label:"High",rationale:""}}function br(e){const t=Zs(e);return t.rationale?`**Confidence:** ${t.label} — ${t.rationale}`:`**Confidence:** ${t.label}`}const nn="aiColorSuggestions";async function va(){try{const t=(await chrome.storage.local.get(nn))[nn];return t&&typeof t=="object"?t:{}}catch{return{}}}async function yr(e){await chrome.storage.local.set({[nn]:e})}async function ap(e){const t=await va();t[e.matchKey]=e,await yr(t)}async function np(e){if(e.length===0)return;const t=await va();for(const a of e)t[a.matchKey]=a;await yr(t)}async function op(e){return(await va())[e]??null}async function ec(){return await va()}const wr={AT:{name:"Austria — Beschwerdestelle Bundessozialamt",url:"https://www.sozialministeriumservice.at/"},BE:{name:"Belgium — Unia / Bosa",url:"https://accessibility.belgium.be/"},BG:{name:"Bulgaria — State e-Government Agency",url:"https://e-gov.bg/"},CY:{name:"Cyprus — Department of Electronic Communications",url:"https://www.mtcw.gov.cy/"},CZ:{name:"Czechia — Ministry of the Interior",url:"https://www.mvcr.cz/"},DE:{name:"Germany — Bundesfachstelle Barrierefreiheit",url:"https://www.bundesfachstelle-barrierefreiheit.de/"},DK:{name:"Denmark — Digitaliseringsstyrelsen",url:"https://www.digst.dk/"},EE:{name:"Estonia — Ministry of Economic Affairs and Communications",url:"https://mkm.ee/"},ES:{name:"Spain — Observatorio de Accesibilidad Web",url:"https://administracionelectronica.gob.es/"},FI:{name:"Finland — Etelä-Suomen aluehallintovirasto",url:"https://www.saavutettavuusvaatimukset.fi/"},FR:{name:"France — Défenseur des droits",url:"https://www.defenseurdesdroits.fr/"},GR:{name:"Greece — Ministry of Digital Governance",url:"https://mindigital.gr/"},HR:{name:"Croatia — Information Commissioner",url:"https://www.pristupinfo.hr/"},HU:{name:"Hungary — Information Society Authority",url:"https://nmhh.hu/"},IE:{name:"Ireland — National Disability Authority",url:"https://nda.ie/"},IT:{name:"Italy — Agenzia per l'Italia Digitale",url:"https://www.agid.gov.it/"},LT:{name:"Lithuania — Information Society Development Committee",url:"https://ivpk.lrv.lt/"},LU:{name:"Luxembourg — Service Information et Presse",url:"https://accessibilite.public.lu/"},LV:{name:"Latvia — VARAM",url:"https://www.varam.gov.lv/"},MT:{name:"Malta — MITA",url:"https://www.mita.gov.mt/"},NL:{name:"Netherlands — Logius",url:"https://www.digitoegankelijk.nl/"},PL:{name:"Poland — Ministry of Digital Affairs",url:"https://www.gov.pl/web/dostepnosc-cyfrowa/"},PT:{name:"Portugal — AMA",url:"https://accessmonitor.acessibilidade.gov.pt/"},RO:{name:"Romania — ANCOM",url:"https://www.ancom.ro/"},SE:{name:"Sweden — DIGG",url:"https://www.digg.se/"},SI:{name:"Slovenia — Ministry of Public Administration",url:"https://www.gov.si/"},SK:{name:"Slovakia — Ministry of Investments",url:"https://www.mirri.gov.sk/"}},vr=(e,t)=>`${e} is committed to making its website accessible, in accordance with national legislation transposing Directive (EU) 2016/2102 of the European Parliament and of the Council. This accessibility statement applies to ${t}.`,Ar=(e,t)=>`${e} is committed to making its products and services accessible to all users, in accordance with the European Accessibility Act (Directive (EU) 2019/882) and its national transpositions, enforceable from 28 June 2025. This accessibility statement applies to ${t}.`,kr=(e,t,a,n="EN 301 549 v3.2.1")=>{const o=`Web Content Accessibility Guidelines (WCAG) ${t} level ${a}`;switch(e){case"full":return`This website is fully conformant with ${o} and the corresponding ${n} clauses.`;case"partial":return`This website is partially conformant with ${o} and the corresponding ${n} clauses. "Partially conformant" means that some parts of the content do not fully conform to the accessibility standard. The non-accessible content is listed below.`;case"non-conformant":return`This website is currently NOT conformant with ${o}. The non-accessible content is listed below, along with our plan to address it.`}},xr=(e,t)=>`The accessibility of this website was last evaluated on ${t}. The evaluation was conducted by ${e} using automated testing (wcagcheckr — multi-state matrix audit across 144 keyboard / focus / theme / breakpoint / direction combinations, AI-augmented walkthroughs for 5 criteria axe-core cannot evaluate, plus DOM-criterion analyzers for 16 additional criteria). The audit is conformant with the EN 301 549 evaluation methodology.`;function tc(e,t){const a=e.language||"English",n=e.auditedBy||"wcagcheckr (self-evaluation)",o=e.wcagVersion||"2.1",r=e.wcagLevel||"AA",i=[];if(i.push(`# Accessibility Statement — ${e.organizationName}`),i.push(""),i.push(`*Statement prepared on ${e.preparedAt}. Language: ${a}.*`),i.push(""),i.push(e.framing==="wad"?vr(e.organizationName,e.siteUrl):Ar(e.organizationName,e.siteUrl)),i.push(""),i.push("## Conformance status"),i.push(""),i.push(kr(e.conformanceStatus,o,r)),i.push(""),t.length>0){i.push("## Non-accessible content"),i.push(""),i.push("The following content is not yet accessible:"),i.push("");for(const c of t){const d=c.exemption?c.exemption==="disproportionate-burden"?" *(claimed under disproportionate burden — Article 5 WAD / Article 14 EAA)*":" *(not subject to applicable legislation)*":"";i.push(`- **WCAG ${c.criterion}** — ${c.description}${d}`)}i.push("")}else e.conformanceStatus==="full"&&(i.push("## Non-accessible content"),i.push(""),i.push("No non-accessible content is currently known. If you encounter any accessibility barriers, please contact us using the feedback details below."),i.push(""));if(i.push("## Preparation of this accessibility statement"),i.push(""),i.push(xr(n,e.evaluatedAt)),i.push(""),i.push("## Feedback and contact information"),i.push(""),i.push(`If you experience accessibility barriers on ${e.siteUrl}, please contact us so we can address them:`),i.push(""),e.contact.email&&i.push(`- **Email:** ${e.contact.email}`),e.contact.phone&&i.push(`- **Phone:** ${e.contact.phone}`),e.contact.formUrl&&i.push(`- **Web form:** ${e.contact.formUrl}`),i.push(""),e.framing==="wad"&&e.countryCode){const c=wr[e.countryCode];c&&(i.push("## Enforcement procedure"),i.push(""),i.push(`If you are not satisfied with the response to your accessibility complaint, you may contact the national enforcement body for ${c.name}:`),i.push(""),i.push(`${c.url}`),i.push(""))}return e.framing==="eaa"&&(i.push("## Enforcement"),i.push(""),i.push("Enforcement of the European Accessibility Act is carried out by national market-surveillance authorities in each EU member state. If your accessibility complaint is not addressed satisfactorily, you may contact the market-surveillance authority of the EU member state in which you are located."),i.push("")),i.push("---"),i.push(""),i.push(`_Statement generated by wcagcheckr. Audit methodology details + cryptographic anchoring of the audit results are available on request from ${e.contact.email??"the contact above"}._`),i.join(`
371
+ `)}function ac(e,t){const a=l=>l==null?"":String(l).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;"),n=e.language||"English",o=e.languageCode||"en",r=e.auditedBy||"wcagcheckr (self-evaluation)",i=e.wcagVersion||"2.1",c=e.wcagLevel||"AA",d=[];if(d.push(`<article lang="${a(o)}">`),d.push(`<h1>Accessibility Statement — ${a(e.organizationName)}</h1>`),d.push(`<p><em>Statement prepared on ${a(e.preparedAt)}. Language: ${a(n)}.</em></p>`),d.push(`<p>${a(e.framing==="wad"?vr(e.organizationName,e.siteUrl):Ar(e.organizationName,e.siteUrl))}</p>`),d.push("<h2>Conformance status</h2>"),d.push(`<p>${a(kr(e.conformanceStatus,i,c))}</p>`),t.length>0){d.push("<h2>Non-accessible content</h2>"),d.push("<p>The following content is not yet accessible:</p>"),d.push("<ul>");for(const l of t){const h=l.exemption?l.exemption==="disproportionate-burden"?" <em>(claimed under disproportionate burden — Article 5 WAD / Article 14 EAA)</em>":" <em>(not subject to applicable legislation)</em>":"";d.push(`<li><strong>WCAG ${a(l.criterion)}</strong> — ${a(l.description)}${h}</li>`)}d.push("</ul>")}else e.conformanceStatus==="full"&&(d.push("<h2>Non-accessible content</h2>"),d.push("<p>No non-accessible content is currently known. If you encounter any accessibility barriers, please contact us using the feedback details below.</p>"));if(d.push("<h2>Preparation of this accessibility statement</h2>"),d.push(`<p>${a(xr(r,e.evaluatedAt))}</p>`),d.push("<h2>Feedback and contact information</h2>"),d.push(`<p>If you experience accessibility barriers on ${a(e.siteUrl)}, please contact us so we can address them:</p>`),d.push("<ul>"),e.contact.email&&d.push(`<li><strong>Email:</strong> <a href="mailto:${a(e.contact.email)}">${a(e.contact.email)}</a></li>`),e.contact.phone&&d.push(`<li><strong>Phone:</strong> ${a(e.contact.phone)}</li>`),e.contact.formUrl&&d.push(`<li><strong>Web form:</strong> <a href="${a(e.contact.formUrl)}">${a(e.contact.formUrl)}</a></li>`),d.push("</ul>"),e.framing==="wad"&&e.countryCode){const l=wr[e.countryCode];l&&(d.push("<h2>Enforcement procedure</h2>"),d.push(`<p>If you are not satisfied with the response to your accessibility complaint, you may contact the national enforcement body for ${a(l.name)}: <a href="${a(l.url)}">${a(l.url)}</a></p>`))}return e.framing==="eaa"&&(d.push("<h2>Enforcement</h2>"),d.push("<p>Enforcement of the European Accessibility Act is carried out by national market-surveillance authorities in each EU member state. If your accessibility complaint is not addressed satisfactorily, you may contact the market-surveillance authority of the EU member state in which you are located.</p>")),d.push("<hr>"),d.push(`<p><small>Statement generated by wcagcheckr. Audit methodology details + cryptographic anchoring of the audit results are available on request from ${a(e.contact.email??"the contact above")}.</small></p>`),d.push("</article>"),d.join(`
372
+ `)}function nc(e,t={},a=new Set){var c;const n=e.flatMap(d=>d.violations),o=new Map;for(const d of n){const l=De(d.ruleId,d.target.selector);o.has(l)||o.set(l,d)}const r=new Map;for(const d of o.values()){const l=d.wcagCriterion;if(!l||a.has(l))continue;const h=r.get(l)??[];h.push(d),r.set(l,h)}const i=[];for(const[d,l]of r.entries()){const h=l[0];let m=t[d];if(!m){const u=ot[h.ruleId];u!=null&&u.summary?m=((c=u.summary.split(/[.!?]\s+/)[0])==null?void 0:c.trim())??h.description:m=h.description}m=m.replace(new RegExp("^\\s*WCAG\\s+"+d.replace(/\./g,"\\.")+"\\b[\\s:.\\u2014-]*","i"),"").trim(),l.length>1&&(m+=` (affects ${l.length} components on the audited page${e.length>1?"s":""})`),i.push({criterion:d,description:m})}return i.sort((d,l)=>{const h=g=>g.split(".").map(p=>parseInt(p,10)),m=h(d.criterion),u=h(l.criterion);for(let g=0;g<Math.max(m.length,u.length);g++){const p=(m[g]??0)-(u[g]??0);if(p!==0)return p}return 0}),i}function oc(e){const t=nc(e.results,e.customDescriptions,e.dismissedCriteria);return e.outputFormat==="html"?ac(e.inputs,t):tc(e.inputs,t)}function rp(e){if(e.reduce((n,o)=>n+o.violations.length,0)===0)return"full";const a=new Set;for(const n of e)for(const o of n.violations)o.wcagCriterion&&a.add(o.wcagCriterion);return a.size>=10?"non-conformant":"partial"}function Ke(e,t,a,n,o,r,i,c,d){var A,w,s,v;const l=wa(e,t,a,n,o,r,i,c,d),h=a??[],m=new Set((r??[]).map(S=>S.criterionId)),u={};for(const S of o??[]){const k=m.has(S.criterionId)?"pass":S.verdict;S.criterionId==="2.4.3"?u.focusOrder=k:S.criterionId==="2.1.2"?u.keyboardTrap=k:S.criterionId==="2.4.7"?u.focusVisible=k:S.criterionId==="1.3.2"?u.readingOrder=k:S.criterionId==="1.4.11"&&(u.nonTextContrast=k)}const g=hr(d??[],h).map(S=>({ruleId:S.ruleId,impact:"serious",selector:S.selector})),p=Rs({violations:e,auditRan:h.length>0,manualRuns:(t==null?void 0:t.runs)??[],workflows:(t==null?void 0:t.workflows)??[],heuristicCounts:{tabOrder:((w=(A=h[0])==null?void 0:A.tabOrderIssues)==null?void 0:w.length)??0,readingOrder:((v=(s=h[0])==null?void 0:s.readingOrderIssues)==null?void 0:v.length)??0},interactiveAuditVerdicts:u,aiResolvedFails:g}),f=qs(l.letter,p.overallLetter);return{combined:l.totals.critical>0||l.totals.serious>0?zs(f,l.letter):f,severity:l.letter,breadth:p.overallLetter,score:l,subScore:p}}const on="humanCriterionVerdicts";async function Cn(){var a;if(typeof chrome>"u"||!((a=chrome.storage)!=null&&a.local))return[];const t=(await chrome.storage.local.get(on))[on];return Array.isArray(t)?t:[]}async function Ir(e){const t=new Set;for(const n of e)n&&t.add(n);return t.size===0?[]:(await Cn()).filter(n=>t.has(n.componentId))}async function ip(e){if(!e.note||!e.note.trim())throw new Error("Human criterion verdict requires a non-empty note explaining the determination.");const a=(await Cn()).filter(n=>!(n.componentId===e.componentId&&n.criterionId===e.criterionId));a.push({...e,note:e.note.trim()}),await Sr(a)}async function sp(e,t){const n=(await Cn()).filter(o=>!(o.componentId===e&&o.criterionId===t));await Sr(n)}async function Sr(e){var t;typeof chrome>"u"||!((t=chrome.storage)!=null&&t.local)||await chrome.storage.local.set({[on]:e})}function jt(e){return/\[Cascade resolution — rc\.\d+ escalation\]/.test(e)}function cp(e){return/\[Manual verification — rc\.\d+\]/.test(e)}function lp(e){const t=e.match(/\[Cascade resolution — rc\.\d+ (fallback-\d|escalation)\]/);if(!t)return null;const a=t[1];return a==="fallback-1"||a==="fallback-2"||a==="escalation"?a:null}const Cr={ruleIds:["image-alt","label","button-name","link-name","color-contrast","document-title","html-has-lang"],thresholds:{moderate:1,high:2,critical:4},headlines:{low:"Low risk. Your site passes the checks common automated lawsuit-targeting scanners run today — not likely automated lawfare target. This isn't a guarantee: scanners add checks over time, so re-audit as your site changes.",moderate:"Moderate risk. A demand-letter scanner could flag at least one issue. Fix to drop off the easy-target list.",high:"High risk. Multiple findings here are common patterns demand-letter mills target.",critical:"Critical risk. Your site has many high-frequency patterns ADA-targeting firms scan for."},version:0};Cr.ruleIds;function $r(){var e;return((e=or())==null?void 0:e.lawfare)??Cr}function Tr(e,t=$r()){return t.ruleIds.includes(e)}function eo(e,t=$r()){const a=new Set(t.ruleIds),n=new Map;for(const l of e)a.has(l.ruleId)&&n.set(l.ruleId,(n.get(l.ruleId)??0)+1);const o=Array.from(n.entries()).map(([l,h])=>({ruleId:l,instanceCount:h})).sort((l,h)=>h.instanceCount-l.instanceCount),r=o.length,i=Array.from(n.values()).reduce((l,h)=>l+h,0);let c;return r<t.thresholds.moderate?c="low":r<t.thresholds.high?c="moderate":r<t.thresholds.critical?c="high":c="critical",{tier:c,letter:c==="low"?"A":c==="moderate"?"C":c==="high"?"D":"F",flaggedRuleCount:r,flaggedInstanceCount:i,flaggedRules:o,headline:t.headlines[c],rubricVersion:t.version}}function rc(e,t){return e.flatMap(a=>a.violations).filter(a=>!a.needsReview&&!t.has(a.matchKey))}function ic(e,t){const a=[],n={};for(const o of e){const r=rc(o.results,t);a.push(...r),n[o.url]=eo(r).letter}return{site:eo(a),perUrlLetter:n}}function dp(e){return e.filter(t=>!t.needsReview&&Tr(t.ruleId))}function lt(e){return new Set(e.map(a=>a.pageUrl??a.scope??"").filter(Boolean)).size>1?"site-crawl":"single-page"}function sc(e){const t=new Set;for(const a of e)a.componentId&&t.add(a.componentId);return Array.from(t)}async function Oe(e,t=[]){const a=sc(e),n=new Set(t.filter(f=>!!f)),o=Array.from(new Set([...a,...n]));if(o.length===0)return{walkthroughs:[],walkthroughAcks:[],acknowledgedMatchKeys:new Set,humanCriterionVerdicts:[],incompleteResolutions:[],walkthroughsByComponent:new Map,walkthroughAcksByComponent:new Map,ackedKeysByComponent:new Map,humanVerdictsByComponent:new Map,incompleteResolutionsByComponent:new Map};const[r,i,c,d,l]=await Promise.all([cc(o),lc(o),dc(o),uc(o),hc(e,o)]);if(n.size>0&&a.length>0){const f=new Map;for(const y of n){const b=d.get(y)??[];for(const A of b)f.has(A.criterionId)||f.set(A.criterionId,A)}if(f.size>0)for(const y of a){const b=d.get(y)??[],A=new Set(b.map(s=>s.criterionId)),w=[...b];for(const s of f.values())A.has(s.criterionId)||w.push({...s,componentId:y});d.set(y,w)}}const h=[];for(const f of r.values())h.push(...f);const m=[];for(const f of i.values())m.push(...f);const u=new Set;for(const f of c.values())for(const y of f)u.add(y);const g=[];for(const f of d.values())g.push(...f);const p=[];for(const f of l.values())p.push(...f);return{walkthroughs:h,walkthroughAcks:m,acknowledgedMatchKeys:u,humanCriterionVerdicts:g,incompleteResolutions:p,walkthroughsByComponent:r,walkthroughAcksByComponent:i,ackedKeysByComponent:c,humanVerdictsByComponent:d,incompleteResolutionsByComponent:l}}async function cc(e){const t=new Map;return await Promise.all(e.map(async a=>{const n=await In([a]);t.set(a,n)})),t}async function lc(e){const t=new Map,a=await fr(e);for(const n of e)t.set(n,[]);for(const n of a){const o=t.get(n.componentId);o&&o.push(n)}return t}async function dc(e){const t=new Map;return await Promise.all(e.map(async a=>{const n=await ga(a);t.set(a,n)})),t}async function uc(e){const t=new Map,a=await Ir(e);for(const n of e)t.set(n,[]);for(const n of a){const o=t.get(n.componentId);o&&o.push(n)}return t}async function hc(e,t){const a=new Map;for(const o of e)if(o.componentId&&!a.has(o.componentId)){const r=o.pageUrl??o.scope;r&&a.set(o.componentId,r)}const n=new Map;return await Promise.all(t.map(async o=>{const r=a.get(o);n.set(o,r?await ba(r):[])})),n}function Ye(e){return/<main[\s>]/i.test(e)?e:e.replace(/(<body[^>]*>)/i,`$1
365
373
  <main>`).replace(/(<\/body>)/i,`</main>
366
- $1`)}function qt(e){return e===null||typeof e!="object"?JSON.stringify(e):Array.isArray(e)?"["+e.map(n=>qt(n)).join(",")+"]":"{"+Object.keys(e).sort().map(n=>{const o=e[n];return JSON.stringify(n)+":"+qt(o)}).join(",")+"}"}const oc="wcag-forensic-log",rc=1,Ne="audits";let Oa=null;function ya(){return Oa||(Oa=pn(oc,rc,{upgrade(e){if(!e.objectStoreNames.contains(Ne)){const t=e.createObjectStore(Ne,{keyPath:["componentId","capturedAt"]});t.createIndex("byCapturedAt","capturedAt"),t.createIndex("byComponentId","componentId")}}})),Oa}async function ic(e){const t=qt(e),a=new TextEncoder().encode(t),n=await crypto.subtle.digest("SHA-256",a);return Array.from(new Uint8Array(n)).map(o=>o.toString(16).padStart(2,"0")).join("")}async function Sr(e,t,a){if(e.length===0)return null;const n=e[0],o=n.componentId,r=n.pageUrl??n.scope,i=n.startedAt,c={componentId:o,pageUrl:r,scope:n.scope,grade:t.letter,totals:{critical:t.totals.critical,serious:t.totals.serious,moderate:t.totals.moderate,minor:t.totals.minor,unique:t.totals.unique},axeVersion:n.axeVersion,capturedAt:i,statesAudited:e.length},l={hash:await ic(c),componentId:o,pageUrl:r,scope:n.scope,grade:t.letter,totals:c.totals,axeVersion:n.axeVersion,capturedAt:i,statesAudited:e.length,durationMs:a};try{await(await ya()).put(Ne,l)}catch(h){return console.warn("[forensic-log] failed to record audit",h),l}return l}async function Cr(e,t,a){try{const n=await ya(),o=await n.get(Ne,[e,t]);if(!o){console.warn("[forensic-log] no entry to attach receipt to",{componentId:e,capturedAt:t});return}o.receipt=a,await n.put(Ne,o)}catch(n){console.warn("[forensic-log] failed to attach receipt",n)}}async function kt(){try{return(await(await ya()).getAll(Ne)).sort((a,n)=>a.capturedAt<n.capturedAt?1:-1)}catch(e){return console.warn("[forensic-log] listAudits failed",e),[]}}async function sc(e){try{return(await(await ya()).transaction(Ne).objectStore(Ne).index("byComponentId").getAll(IDBKeyRange.only(e))).sort((o,r)=>o.capturedAt<r.capturedAt?1:-1)}catch(t){return console.warn("[forensic-log] listAuditsForComponent failed",t),[]}}const cc={"color-contrast":{whatsWrong:"Some text on this page is too light to read against its background.",whyItMatters:"People with low vision, older eyes, or anyone reading on a sunny phone screen can't make out faint text. Color blindness affects about 1 in 12 men and 1 in 200 women. Insufficient contrast is the #1 most-common cause of accessibility lawsuits — every Domino's-style ADA suit cites it.",howToFix:"If you use a website builder (Squarespace, Wix, Shopify, WordPress with a theme), open your site styles or theme color settings and pick darker text colors or lighter backgrounds. Aim for very dark text on white, or very light text on dark. Free check at webaim.org/resources/contrastchecker. If you have a developer, ask them to ensure all text meets WCAG AA contrast (4.5:1 for normal text, 3:1 for large text)."},"color-contrast-enhanced":{whatsWrong:"Text on this page doesn't meet the higher (AAA) contrast level — important for people with significant low vision.",whyItMatters:"AAA is a stricter standard than the legal-minimum AA. If your audience includes older adults, people with cataracts or macular degeneration, or government-related contracts that require AAA conformance, this matters.",howToFix:"Same as basic contrast — go darker on text or lighter on backgrounds. AAA requires 7:1 for normal text and 4.5:1 for large text."},"link-in-text-block":{whatsWrong:"Links inside paragraphs are distinguishable from the surrounding text by color alone.",whyItMatters:"If a user is colorblind or sees in grayscale, they can't tell which words are clickable. WCAG requires another visual cue (underline, bold, icon) in addition to color.",howToFix:"Add an underline to links inside paragraphs. In most theme editors there's a 'link style' setting — turn underlines back on. The underline is the standard convention; it's also more discoverable for everyone."},"image-alt":{whatsWrong:"Some images on this page have no description for screen readers.",whyItMatters:"Blind and low-vision users rely on screen readers to read pages aloud. When an image has no description, the screen reader either skips it entirely or reads out the file name — useless. Missing alt text is the #2 lawsuit-magnet category.",howToFix:"In your site editor, click each image and look for an 'Alt text' or 'Image description' field. Write a short sentence describing what the image shows or what it links to. For purely decorative images (dividers, background flourishes), enter empty alt text or check a 'decorative' box if the platform offers one."},"input-image-alt":{whatsWrong:"Image-based form buttons (submit buttons that are images) have no description.",whyItMatters:"Without a description, screen-reader users can't tell what the button does — they hear 'button, submit.png' or just 'graphic'.",howToFix:"Replace image submit buttons with regular text buttons (most platforms let you do this in form settings), OR if you must use an image, ensure it has alt text describing the action ('Subscribe', 'Send message', 'Submit form')."},"area-alt":{whatsWrong:"An image map (clickable regions inside a single image) has unlabeled regions.",whyItMatters:"Each clickable area is a link — without a description, screen-reader users don't know what it points to.",howToFix:"Image maps are uncommon today. If you need one, your developer should add an alt attribute to each clickable area. The simpler modern approach: use multiple separate linked images or buttons instead."},"svg-img-alt":{whatsWrong:"Some SVG icons that act as images don't have a description.",whyItMatters:"SVG icons used decoratively are fine to leave silent, but icons that convey meaning (status indicators, social-media icons that link out) need a description so screen readers know what they represent.",howToFix:"In most platforms this requires a developer. Tell them: 'meaningful SVG icons need a title or aria-label; decorative SVGs need aria-hidden=true'. Examples: an envelope icon linking to 'Contact us' should be labeled 'Contact us', not silent."},"object-alt":{whatsWrong:"Embedded content (PDFs, videos, embedded apps) has no fallback description.",whyItMatters:"If the embed fails to load — or the user's screen reader can't navigate it — they need a text description of what the embed is.",howToFix:"Replace embedded objects with native modern alternatives where possible (HTML5 video instead of Flash, linked PDF with a description). If you must use an <object> embed, ask your developer to add fallback content describing what it is."},"role-img-alt":{whatsWrong:"An element marked as an image is missing its description.",whyItMatters:"Same as missing image alt — screen-reader users get nothing where they should hear a description.",howToFix:"Ask your developer: 'elements with role=img need aria-label or aria-labelledby'."},"image-redundant-alt":{whatsWrong:"Some images have alt text that duplicates nearby visible text.",whyItMatters:"Screen-reader users hear the same content twice — the surrounding text and then the alt text. Annoying, not blocking. Lower-priority fix.",howToFix:"Find images where the caption or nearby paragraph already says what the image shows, and either change the alt text to add new info or set the alt to empty (so the screen reader skips it as decorative)."},label:{whatsWrong:"Some form fields have no label that screen readers can announce.",whyItMatters:"Visually you can see what each field is for (next to a 'Name:' or 'Email:' text), but screen-reader users hear only 'edit text' if there's no programmatic label connecting the text to the field. The result: blind users can't tell what to type where.",howToFix:"In a website builder, every form field has a 'Label' field or similar. Make sure every input has one filled in. Placeholder text (the gray hint inside the field) does NOT count as a label — it has to be a real label."},"select-name":{whatsWrong:"A dropdown menu has no description.",whyItMatters:"Screen-reader users hear 'combo box, has popup' but not what the dropdown is for. They can't fill out forms reliably.",howToFix:"In your form editor, check that each dropdown has a visible label above or beside it. If your platform has separate label/field settings, both must be filled."},"aria-input-field-name":{whatsWrong:"A custom input control (search box, range slider, custom date picker) has no name.",whyItMatters:"Custom controls are common in modern site builders — they look fancy but break for screen-reader users when not labeled.",howToFix:"This is a developer fix. Ask: 'custom inputs with role=textbox / searchbox / slider / spinbutton need an aria-label or aria-labelledby'."},"aria-toggle-field-name":{whatsWrong:"A toggle switch (on/off control) has no description.",whyItMatters:"User can see the switch but can't tell what it controls without a label.",howToFix:"Developer fix: 'switches and checkboxes with role=switch need an accessible name'."},"form-field-multiple-labels":{whatsWrong:"A form field has multiple labels, which confuses screen readers.",whyItMatters:"Screen readers will read all labels — sometimes saying conflicting things — leaving users unsure what to enter.",howToFix:"Developer fix: 'this field has multiple <label> associations; pick one and remove the others'."},"label-content-name-mismatch":{whatsWrong:"A button's visible text doesn't match the description it announces to screen readers.",whyItMatters:"A user dictating 'click Submit' to voice-control software may see 'Submit' on screen but the announce-name is something else, so the voice command fails.",howToFix:"Developer fix: 'aria-label should start with or fully contain the visible button text'."},"autocomplete-valid":{whatsWrong:"A field that should auto-fill (name, email, phone, address) is missing the right autocomplete hint.",whyItMatters:"Browsers use these hints to auto-fill personal info, helping users with motor disabilities and saving everyone time. Older adults appreciate this most.",howToFix:"In your form builder, look for an 'Autocomplete' or 'Field type' setting per field. Set them to 'email', 'name', 'tel', 'street-address' etc. as appropriate."},"button-name":{whatsWrong:"Some buttons have no description that screen readers can announce.",whyItMatters:"Visual users see an icon (a heart, a trash can, an X), but screen-reader users hear only 'button' — they don't know if it'll favorite a post, delete it, or close a dialog. This is a critical accessibility blocker and a top lawsuit driver.",howToFix:"Every button needs a text label. If you can't put text inside the button (icon-only design), in your platform's button settings look for an 'Aria label' or 'Screen reader text' field — fill it with what the button does ('Close', 'Add to cart', 'Open menu')."},"input-button-name":{whatsWrong:"An <input type=submit> or similar button has no description.",whyItMatters:"Same as button-name — screen-reader users don't know what the button does.",howToFix:"Set the button's 'value' or 'label' to text describing the action ('Submit', 'Subscribe', 'Send')."},"link-name":{whatsWrong:"Some links have no readable text — usually icon-only links.",whyItMatters:"Screen-reader users hear 'link' but not where the link goes. Users navigate by listening to a list of links — useless if every entry is just 'link, link, link'.",howToFix:"In your site editor, for icon-only links (social-media icons, navigation arrows), set an 'Aria label' or 'Title' field describing where it goes ('Follow us on Twitter', 'Next page')."},"empty-button":{whatsWrong:"A button is completely empty — no text, no icon, no description.",whyItMatters:"Both visual and screen-reader users can't tell what an empty button does.",howToFix:"Find empty buttons in your editor (often a leftover from a deleted icon) and either delete them or add text/an icon + description."},"empty-link":{whatsWrong:"A link has no readable text or description.",whyItMatters:"Screen-reader users hear nothing useful; visual users see an empty space they can't tell is clickable.",howToFix:"Same as link-name — add link text or an aria-label describing the destination."},"empty-heading":{whatsWrong:"A heading on this page has no text.",whyItMatters:"Screen-reader users navigate pages by jumping between headings. An empty heading is dead air — they don't know what the section is about.",howToFix:"Find the empty heading in your editor (often a section title that was deleted but the heading element remained) and either delete it or add real heading text."},"document-title":{whatsWrong:"This page's title (in the browser tab) is missing or generic.",whyItMatters:"Tab titles help users who have many tabs open know which one is which. Screen readers also announce the page title when the page loads.",howToFix:"In your platform's page settings or SEO panel, find the 'Page title' field and set it to something descriptive ('Contact Us — YourBusiness', not 'Untitled' or just your business name)."},"html-has-lang":{whatsWrong:"The page doesn't declare what language it's in.",whyItMatters:"Screen readers use this to pick the right voice and pronunciation rules. Without it, English content might be read with a French accent (or vice versa).",howToFix:"In your platform's site settings, find a 'Site language' option and set it (English = en, Spanish = es, etc.). Most modern platforms do this automatically — if it's missing, ask your developer to add lang='en' to the html element."},"html-lang-valid":{whatsWrong:"The page declares a language code that isn't valid.",whyItMatters:"Same as missing language — screen readers can't pick the right voice.",howToFix:"Use a standard ISO language code: en, es, fr, de, ja, zh, pt, etc. Avoid full strings like 'english' — use the two-letter code."},"valid-lang":{whatsWrong:"Text marked as a different language uses an invalid code.",whyItMatters:"Same as above — screen readers can't switch voices for the foreign-language passage.",howToFix:"Use ISO codes (en, fr, es) for the lang attribute on individual elements. This typically requires a developer."},"page-has-heading-one":{whatsWrong:"This page is missing a main heading (H1).",whyItMatters:"Screen-reader users use the H1 to confirm they landed on the right page. The H1 is also a strong SEO signal — Google uses it to understand what the page is about.",howToFix:"Add a clear, descriptive H1 to the top of the page. In website builders, this is usually the 'Title' or 'Page heading' setting. Each page should have exactly one H1."},"heading-order":{whatsWrong:"Heading levels skip — for example, an H2 jumps directly to an H4 with no H3 between them.",whyItMatters:"Screen-reader users navigate by heading levels. Skipped levels suggest missing content and break the page's outline. Confusing for everyone.",howToFix:"In your editor, find the headings that skip levels and either bump them up to the right level (H4 → H3 if no H2 exists between) or insert the missing level. Headings should follow a logical outline like a document outline."},"landmark-one-main":{whatsWrong:"This page has no 'main content' region marked.",whyItMatters:"Screen-reader users press a single shortcut to jump to the main content, skipping menus and headers. Without a marked main region, they have to tab through everything.",howToFix:"Most modern platforms add this automatically. If yours doesn't, ask your developer to wrap the main content in a <main> element."},"landmark-no-duplicate-banner":{whatsWrong:"There are multiple top banners (headers) on the page when there should be one.",whyItMatters:"Screen-reader users get confused by duplicate landmarks — they don't know which is the 'real' header.",howToFix:"Developer fix: 'only one element should have role=banner or be a top-level <header>; remove duplicates'."},"landmark-no-duplicate-contentinfo":{whatsWrong:"There are multiple footer regions on the page when there should be one.",whyItMatters:"Same as duplicate banner — confusing landmark navigation.",howToFix:"Developer fix: 'only one element should have role=contentinfo or be a top-level <footer>; remove duplicates'."},region:{whatsWrong:"Some content on the page isn't inside a recognized region (header, nav, main, footer, aside).",whyItMatters:"Screen-reader users navigate by these regions. Content outside any region can be missed when skimming.",howToFix:"Developer fix: 'wrap orphaned page content in semantic landmark elements (<main>, <aside>, <nav>) so it's reachable via landmark nav'."},bypass:{whatsWrong:"There's no 'skip to main content' link at the top of the page.",whyItMatters:"Keyboard-only users (and some screen-reader users) have to tab through every menu item on every page before reaching the main content. A skip link gives them a single keystroke to jump past the menu.",howToFix:"Most modern themes include a skip link automatically — it's only visible when keyboard-focused. If yours doesn't, ask your developer to add one as the first focusable element on every page."},"aria-required-attr":{whatsWrong:"An element uses a screen-reader role but is missing required information for that role.",whyItMatters:"Custom controls (sliders, tabs, dialogs) need specific extra info to work properly with screen readers. Missing pieces = broken behavior.",howToFix:"Developer fix: 'this role requires additional aria-* attributes; check axe-core docs for the specific role'."},"aria-allowed-attr":{whatsWrong:"An element has a screen-reader hint that doesn't apply to its role.",whyItMatters:"Inappropriate hints can confuse or break screen-reader announcements.",howToFix:"Developer fix: 'this aria-* attribute isn't allowed on this role; remove it or change the role'."},"aria-roles":{whatsWrong:"An element claims to be a type that isn't valid (typo or misuse of a role).",whyItMatters:"Screen readers ignore invalid roles, leaving the element unannounced.",howToFix:"Developer fix: 'this role value isn't recognized; check spelling or use a valid role'."},"aria-valid-attr":{whatsWrong:"An element uses a screen-reader hint that doesn't exist (typo).",whyItMatters:"Same as invalid roles — typos are silently ignored.",howToFix:"Developer fix: 'this aria-* attribute name isn't recognized; check spelling'."},"presentation-role-conflict":{whatsWrong:"An element is marked as decorative but is also focusable or has a description — contradictory.",whyItMatters:"Screen readers handle this inconsistently; some announce it anyway, some don't, leaving users with mixed experiences.",howToFix:"Developer fix: 'role=presentation/none cannot coexist with focusability or aria-label'."},"duplicate-id":{whatsWrong:"Two or more elements on the page share the same internal ID.",whyItMatters:"Some screen reader and skip-link behaviors rely on unique IDs. Duplicates can break label associations and skip-link targets.",howToFix:"Developer fix: 'rename duplicate IDs so each is unique'. Often arises when copying components."},"duplicate-id-aria":{whatsWrong:"An ID referenced by a screen-reader hint exists multiple times.",whyItMatters:"Screen readers may pick the wrong one and announce wrong information.",howToFix:"Developer fix: 'IDs used in aria-labelledby / aria-describedby must be unique'."},"meta-viewport":{whatsWrong:"The page tells mobile browsers users can't zoom in.",whyItMatters:"People with low vision rely on pinch-zoom to read small text. Disabling zoom is a real WCAG failure and a common lawsuit target.",howToFix:"In platform site settings, look for a 'Mobile zoom' or 'Pinch zoom' option and enable it. If your developer added user-scalable=no in a viewport meta tag, ask them to remove it."},"nested-interactive":{whatsWrong:"An interactive element (link, button) is inside another interactive element.",whyItMatters:"Keyboard and screen-reader users can't reliably reach the inner element. Common in 'card-as-link' designs where a button sits inside a clickable card.",howToFix:"Developer fix: 'avoid putting buttons inside links or links inside buttons; use a single interactive element with internal helpers'."},tabindex:{whatsWrong:"Some elements have positive tabindex values, forcing a custom focus order.",whyItMatters:"Custom focus orders are nearly always confusing — focus jumps around unexpectedly. Best practice is to never use positive tabindex.",howToFix:"Developer fix: 'replace tabindex=1/2/3... with tabindex=0 (or remove entirely); use DOM order to control focus order'."},"frame-focusable-content":{whatsWrong:"A frame on the page can't be navigated by keyboard.",whyItMatters:"Keyboard users get stuck — they can't reach interactive content inside the frame.",howToFix:"Developer fix: 'iframes containing interactive content shouldn't have tabindex=-1 on their parent or content'."},"no-focusable-content":{whatsWrong:"An element looks interactive but can't actually receive keyboard focus.",whyItMatters:"Keyboard-only users can't activate the element.",howToFix:"Developer fix: 'replace divs/spans-with-onClick with a real <button>, or add tabindex=0'."},"target-size":{whatsWrong:"Some buttons or links are too small to tap reliably on touch screens.",whyItMatters:"WCAG 2.2 requires touch targets to be at least 24×24 pixels. Small targets are a problem for users with hand tremors, large fingers, or motor disabilities. They're also frustrating for everyone.",howToFix:"In your editor, increase padding around small icon buttons. If your developer is involved, ask them to ensure all interactive elements have minimum 24×24 CSS pixels of clickable area."},"frame-title":{whatsWrong:"An iframe (embedded content) has no title.",whyItMatters:"Screen readers announce frame titles when entering them. Without one, users hear 'frame' with no context.",howToFix:"If using a third-party embed (YouTube, Vimeo, Stripe), most platforms set the title automatically. If yours doesn't, look for a 'Title' or 'Description' field on the embed block."}};function lc(e){const t=cc[e];return t||{whatsWrong:"An accessibility rule failed on this page.",whyItMatters:"Some users — particularly those using screen readers, keyboard-only navigation, or who have low vision or motor disabilities — may have trouble using this part of the page.",howToFix:"Share the technical rule ID with your developer. They can look up the full fix at https://dequeuniversity.com/rules/axe."}}const dc={whatsWrong:"This element appears to have a contrast issue, but it's actually transparent — its parent (or itself) has an opacity below 100%.",whyItMatters:"When something is partially transparent, the colors blend with whatever's behind it, which automated tools see as poor contrast. Most often this happens because the element is part of an entry animation (fade-in on scroll) that hasn't completed when the audit ran. The element will likely look correct once the animation finishes.",howToFix:"Check whether this element is inside a scroll-reveal animation (common patterns: classes like .fade-in, .reveal, .animate-on-scroll; or JavaScript using IntersectionObserver to add a .visible class). If so, this is NOT a real contrast problem — the element will become opaque once the user scrolls past it. If you want the audit to skip these false positives, ensure your site honors prefers-reduced-motion (which serves the final state immediately) or remove the opacity:0 default."};function Sn(e,t){return e==="color-contrast"&&t&&(t.self<.99||t.ancestor<.99)?dc:lc(e)}const Kt="inflight:audit";async function wa(){return(await chrome.storage.local.get(Kt))[Kt]??null}async function qe(e){await chrome.storage.local.set({[Kt]:e})}async function nt(){await chrome.storage.local.remove(Kt)}async function Zh(){const e=await wa();e&&e.state==="running"&&await qe({...e,state:"interrupted",lastProgressAt:new Date().toISOString()})}async function Tt(e){const t=await wa();t&&await qe({...t,...e,lastProgressAt:new Date().toISOString()})}function Et(){return crypto.randomUUID()}const $r="https://api.wcagcheckr.com/verify";function Tr(e){return`${$r}?hash=${encodeURIComponent(e)}`}function ep(e){return`${$r}#entry=${encodeURIComponent(JSON.stringify(e))}`}function uc(e){return e.length<2?[]:[...hc(e),...fc(e),...gc(e),...mc(e)]}function hc(e){const t=[];for(const a of["primary","footer","utility"]){const n=e.map(o=>({snapshot:o,hrefs:o.navLinks.filter(r=>r.region===a&&r.href).map(r=>r.href)})).filter(o=>o.hrefs.length>0);if(!(n.length<2))for(let o=0;o<n.length-1;o++){const r=n[o],i=n[o+1],c=r.hrefs.filter(f=>i.hrefs.includes(f));if(c.length<2)continue;const d=c.map(f=>r.hrefs.indexOf(f)),l=c.map(f=>i.hrefs.indexOf(f)),h=[...d].sort((f,p)=>f-p),m=[...l].sort((f,p)=>f-p),u=h.map(f=>c[d.indexOf(f)]),g=m.map(f=>c[l.indexOf(f)]);pc(u,g)||t.push({ruleId:"wcc-consistent-navigation",wcagCriterion:"3.2.3",wcagLevel:"AA",impact:"moderate",description:`${a}-region nav links appear in a different order on these pages.`,details:[`Page A: ${r.snapshot.url}`,` order: [${u.join(", ")}]`,`Page B: ${i.snapshot.url}`,` order: [${g.join(", ")}]`,"WCAG 3.2.3 requires that components that appear on multiple pages occur in the same relative order. Additions are fine; reorders are not."].join(`
367
- `),pages:[{url:r.snapshot.url,evidence:u.join(" → ")},{url:i.snapshot.url,evidence:g.join(" → ")}]})}}return t}function pc(e,t){if(e.length!==t.length)return!1;for(let a=0;a<e.length;a++)if(e[a]!==t[a])return!1;return!0}function fc(e){const t=[],a=new Map;for(const n of e)for(const o of n.sharedComponents)a.has(o.fingerprint)||a.set(o.fingerprint,[]),a.get(o.fingerprint).push({url:n.url,accessibleName:o.accessibleName,role:o.role});for(const[n,o]of a.entries()){if(o.length<2)continue;const r=new Set(o.map(i=>Cn(i.accessibleName)));r.size<=1||t.push({ruleId:"wcc-consistent-identification",wcagCriterion:"3.2.4",wcagLevel:"AA",impact:"moderate",description:`Same component (${n}) has different accessible names across pages.`,details:[`Fingerprint: ${n}`,`Role: ${o[0].role}`,`Distinct names observed: ${[...r].map(i=>`"${i}"`).join(", ")}`,...o.map(i=>` ${i.url} → "${i.accessibleName}"`),"WCAG 3.2.4 requires components with identical functionality to have identical accessible names across pages."].join(`
368
- `),pages:o.map(i=>({url:i.url,evidence:i.accessibleName}))})}return t}function Cn(e){return e.toLowerCase().replace(/\s+/g," ").trim()}function gc(e){const t=[],a=new Map;for(const n of e)for(const o of n.helpMechanisms){const r=Cn(o.label);r&&(a.has(r)||a.set(r,[]),a.get(r).push({url:n.url,landmark:o.landmark,rawLabel:o.label}))}for(const[n,o]of a.entries()){if(o.length<2)continue;const r=new Set(o.map(i=>i.landmark));r.size<=1||t.push({ruleId:"wcc-consistent-help",wcagCriterion:"3.2.6",wcagLevel:"A",impact:"minor",description:`Help mechanism "${n}" appears in different landmarks across pages.`,details:[`Help label: "${n}"`,`Distinct landmarks observed: ${[...r].join(", ")}`,...o.map(i=>` ${i.url} → ${i.landmark}`),"WCAG 3.2.6 (AA, WCAG 2.2): when a help mechanism exists on multiple pages, it must appear in the same relative order. Same-label help that moves between header/footer/nav is a divergence."].join(`
369
- `),pages:o.map(i=>({url:i.url,evidence:`${i.rawLabel} in ${i.landmark}`}))})}return t}function mc(e){const t=[],a=e.filter(o=>o.isMultiStep);if(a.length<2)return t;const n=new Map;for(const o of a)for(const r of o.formFields){const i=Cn(r.name);i&&(n.has(i)||n.set(i,[]),n.get(i).push({url:o.url,label:r.label,type:r.type,isSecurityEssential:r.isSecurityEssential}))}for(const[o,r]of n.entries())r.length<2||r.some(i=>i.isSecurityEssential)||t.push({ruleId:"wcc-redundant-entry",wcagCriterion:"3.3.7",wcagLevel:"A",impact:"minor",description:`Field "${o}" is requested on multiple steps of a multi-step flow.`,details:[`Field name: "${o}"`,"Occurrences:",...r.map(i=>` ${i.url} → label "${i.label}", type ${i.type}`),"WCAG 3.3.7 (A, WCAG 2.2): information previously entered in a multi-step process must be auto-populated or selectable, unless re-entry is essential (e.g., password confirmation, security questions)."].join(`
370
- `),pages:r.map(i=>({url:i.url,evidence:`${i.label} (${i.type})`}))});return t}const Yt={A:0,B:1,C:2,D:3,F:4},Zn={low:0,moderate:1,high:2,critical:3};function bc(e,t){return Yt[e]>Yt[t]?e:t}function yc(e,t){return Zn[e]>Zn[t]?e:t}function wc(e){const t=[];for(const a of e)for(const n of a.results)for(const o of n.violations)o.needsReview||t.push({url:a.url,componentId:a.componentId,v:o});return t}function vc(e,t){const a=[];for(const n of e){const o=t==null?void 0:t.get(n.componentId);for(const r of n.results)for(const i of r.violations)i.needsReview&&(o!=null&&o.has(i.matchKey)||a.push({url:n.url,v:i}))}return a}function Er(e,t,a,n,o,r,i,c,d,l){const h=t.filter(C=>!C.error).length,m=t.filter(C=>!!C.error).length,u=t.length<=1?"single-page":"site-crawl",g=t.map(C=>{if(C.error)return{url:C.url,grade:"F",risk:"critical",uniqueViolations:0,totals:{critical:0,serious:0,moderate:0,minor:0},durationMs:C.durationMs,error:C.error,walkthroughFindings:[],walkthroughAcknowledgements:[]};const W=C.results.flatMap(U=>U.violations),z=r==null?void 0:r.get(C.componentId),Q=i==null?void 0:i.get(C.componentId),he=c==null?void 0:c.get(C.componentId),oe=d==null?void 0:d.get(C.componentId),re=l==null?void 0:l.get(C.componentId),x=Be(W,{runs:[],workflows:Re},C.results,he?new Set(he):void 0,z?[...z]:void 0,Q?[...Q]:void 0,oe?[...oe]:void 0,u,re?[...re]:void 0),B=x.score;return{url:C.url,grade:x.combined,risk:B.risk,uniqueViolations:B.totals.unique,totals:{critical:B.totals.critical,serious:B.totals.serious,moderate:B.totals.moderate,minor:B.totals.minor},durationMs:C.durationMs,walkthroughFindings:B.walkthroughFindings,walkthroughAcknowledgements:B.walkthroughAcknowledgements}});g.sort((C,W)=>{const z=Yt[W.grade]-Yt[C.grade];return z!==0?z:W.uniqueViolations-C.uniqueViolations});let f="A",p="low";for(const C of g)f=bc(f,C.grade),p=yc(p,C.risk);const y=wc(t).filter(({v:C,componentId:W})=>{const z=c==null?void 0:c.get(W);return!(z!=null&&z.has(C.matchKey))}),b=C=>`${C.ruleId}::${C.target.selector}`,v=new Map;for(const{v:C}of y){const W=b(C);v.has(W)||v.set(W,C)}const w=v.size,s={critical:0,serious:0,moderate:0,minor:0};for(const C of v.values())s[C.impact]++;const A=new Map;for(const{url:C,v:W}of y){let z=A.get(W.ruleId);z||(z={ruleId:W.ruleId,description:W.description,impact:W.impact,urls:new Set,totalOccurrences:0},A.set(W.ruleId,z)),z.urls.add(C),z.totalOccurrences++}const S=Array.from(A.values()).map(C=>({ruleId:C.ruleId,description:C.description,impact:C.impact,urlsAffected:C.urls.size,totalOccurrences:C.totalOccurrences,sampleUrls:Array.from(C.urls).slice(0,5)})).sort((C,W)=>W.urlsAffected!==C.urlsAffected?W.urlsAffected-C.urlsAffected:W.totalOccurrences-C.totalOccurrences),k=vc(t,c),I=new Map,L=new Set;for(const{url:C,v:W}of k){L.add(`${W.ruleId}::${W.target.selector}`);let z=I.get(W.ruleId);z||(z={ruleId:W.ruleId,description:W.description,impact:W.impact,urls:new Set,totalOccurrences:0},I.set(W.ruleId,z)),z.urls.add(C),z.totalOccurrences++}const $=Array.from(I.values()).map(C=>({ruleId:C.ruleId,description:C.description,impact:C.impact,urlsAffected:C.urls.size,totalOccurrences:C.totalOccurrences,sampleUrls:Array.from(C.urls).slice(0,5)})).sort((C,W)=>W.urlsAffected!==C.urlsAffected?W.urlsAffected-C.urlsAffected:W.totalOccurrences-C.totalOccurrences),_=L.size,P={minor:0,moderate:1,serious:2,critical:3},T=new Map;for(const C of t){if(C.error)continue;const W=c==null?void 0:c.get(C.componentId);for(const z of C.results)for(const Q of z.violations){if(W!=null&&W.has(Q.matchKey))continue;const he=`${Oe(Q.ruleId,Q.target.selector)}::${Q.needsReview?"NR":"ACT"}`;let oe=T.get(he);oe||(oe={ruleId:Q.ruleId,representativeSelector:Q.target.selector,representativeOuterHTML:Q.target.outerHTML??"",impact:Q.impact,wcagCriterion:Q.wcagCriterion??null,axeDescription:Q.description??"",pages:new Map,totalOccurrences:0,needsReview:Q.needsReview??!1,failureSummary:Q.target.failureSummary??null},T.set(he,oe)),P[Q.impact]>P[oe.impact]&&(oe.impact=Q.impact),oe.totalOccurrences++,oe.pages.has(C.url)||oe.pages.set(C.url,{selector:Q.target.selector,outerHTML:Q.target.outerHTML??""}),!oe.failureSummary&&Q.target.failureSummary&&(oe.failureSummary=Q.target.failureSummary)}}const N=Array.from(T.values()).map(C=>({ruleId:C.ruleId,representativeSelector:C.representativeSelector,representativeOuterHTML:C.representativeOuterHTML,impact:C.impact,wcagCriterion:C.wcagCriterion,axeDescription:C.axeDescription,pages:Array.from(C.pages.entries()).map(([W,z])=>({url:W,selector:z.selector,outerHTML:z.outerHTML})),totalOccurrences:C.totalOccurrences,isShared:C.pages.size>=2,needsReview:C.needsReview,failureSummary:C.failureSummary})).sort((C,W)=>P[W.impact]!==P[C.impact]?P[W.impact]-P[C.impact]:W.pages.length-C.pages.length),M={A:0,B:0,C:0,D:0,F:0};for(const C of g)M[C.grade]++;const D=t.map(C=>C.snapshot).filter(C=>!!C),j=uc(D),O=new Set,G=[],R=[];for(const C of g){for(const W of C.walkthroughFindings){const z=`${W.criterionId}::${W.pageUrl}`;O.has(z)||(O.add(z),G.push(W))}for(const W of C.walkthroughAcknowledgements){const z=`${W.criterionId}::${W.pageUrl}::ack`;O.has(z)||(O.add(z),R.push(W))}}const V=new Map,J={untested:0,inconclusive:1,failing:2},X=new Map;for(const C of ca("2.1","AA"))X.set(C.id,C.title);const ye=new Map;function we(C,W){const z=`${W.pageUrl}::${W.selector??""}::${W.source}::${W.ruleOrStepId}`;let Q=ye.get(C);if(Q||(Q=new Set,ye.set(C,Q)),Q.has(z))return;Q.add(z);const he=V.get(C);he&&(he.evidence.length>=6||he.evidence.push(W))}for(const C of t){if(C.error)continue;const W=C.results.flatMap(ee=>ee.violations),z=r==null?void 0:r.get(C.componentId),Q=i==null?void 0:i.get(C.componentId),he=c==null?void 0:c.get(C.componentId),oe=d==null?void 0:d.get(C.componentId),re=l==null?void 0:l.get(C.componentId),x=ma(W,void 0,C.results,he?new Set(he):void 0,z?[...z]:void 0,Q?[...Q]:void 0,oe?[...oe]:void 0,u,re?[...re]:void 0);if(!x.coverage)continue;const B=(ee,K)=>{for(const H of ee){const Z=X.get(H)??H,Ae=V.get(H);Ae?J[K]>J[Ae.state]&&V.set(H,{state:K,title:Z,evidence:Ae.evidence}):V.set(H,{state:K,title:Z,evidence:[]})}};B(x.coverage.untestedCriteria,"untested"),B(x.coverage.inconclusiveCriteria,"inconclusive"),B(x.coverage.failingCriteria,"failing");const U=x.coverage.inconclusiveReasons??{};for(const[ee,K]of Object.entries(U))for(const H of K)if(H.elements&&H.elements.length>0)for(const Z of H.elements)we(ee,{pageUrl:H.pageUrl??C.url,selector:Z.selector,ruleOrStepId:H.ruleOrStepId,source:H.source,notes:Z.failureSummary});else we(ee,{pageUrl:H.pageUrl??C.url,ruleOrStepId:H.ruleOrStepId,source:H.source,notes:H.resolutionHint});const Y=x.coverage.failingReasons??{};for(const[ee,K]of Object.entries(Y))for(const H of K)we(ee,{pageUrl:H.pageUrl??C.url,selector:H.selector,ruleOrStepId:H.ruleOrStepId,source:H.source,notes:H.notes})}const me=Array.from(V.entries()).map(([C,W])=>({criterionId:C,title:W.title,state:W.state,evidence:W.evidence})).sort((C,W)=>C.criterionId.localeCompare(W.criterionId,void 0,{numeric:!0}));return{startedAt:a,finishedAt:n,startUrl:e,pagesAudited:h,pagesFailed:m,totalDurationMs:o,siteGrade:f,siteRisk:p,totalUniqueViolations:w,totals:s,topViolations:S,needsReviewItems:$,totalNeedsReview:_,findingGroups:N,pages:g,pagesByGrade:M,consistencyFindings:j,walkthroughFindings:G,walkthroughAcknowledgements:R,totalWalkthroughFindings:G.length,blockingCriteria:me}}const Ac="offscreen/offscreen.html";let Lt=null;async function kc(){return Lt||(Lt=(async()=>{if(!chrome.offscreen)throw new Error("chrome.offscreen API is unavailable. Update Chrome to 109+.");await chrome.offscreen.hasDocument()||await chrome.offscreen.createDocument({url:Ac,reasons:[chrome.offscreen.Reason.DOM_PARSER],justification:"Parse PDF accessibility structure tree with pdf.js (no DOM access available in service worker)."})})().catch(e=>{throw Lt=null,e})),Lt}async function Ic(e,t){try{await kc()}catch(o){return{ok:!1,source:e,findings:[],pageCount:null,fatalError:`Could not create offscreen document for PDF audit: ${o instanceof Error?o.message:String(o)}`}}const a={type:"OFFSCREEN_PDF_AUDIT_REQUEST",url:e,...(t==null?void 0:t.maxBytes)!==void 0?{maxBytes:t.maxBytes}:{}};let n;try{n=await chrome.runtime.sendMessage(a)}catch(o){return{ok:!1,source:e,findings:[],pageCount:null,fatalError:`Offscreen audit messaging failed: ${o instanceof Error?o.message:String(o)}`}}return!n||n.type!=="OFFSCREEN_PDF_AUDIT_RESPONSE"?{ok:!1,source:e,findings:[],pageCount:null,fatalError:"Offscreen audit returned an unexpected response shape."}:{ok:n.ok,source:n.source,findings:[...n.findings],pageCount:n.pageCount,...n.fatalError?{fatalError:n.fatalError}:{},...n._perPageContrast?{_perPageContrast:n._perPageContrast}:{},...n.aiCandidates?{aiCandidates:n.aiCandidates}:{}}}async function xc(e,t){var a;try{return((a=(await chrome.scripting.executeScript({target:{tabId:e},func:Sc,args:[t]}))[0])==null?void 0:a.result)??null}catch{return null}}function Sc(e){var v;function t(w){var I,L;const s=w.getAttribute("aria-label");if(s&&s.trim())return s.trim();const A=w.getAttribute("aria-labelledby");if(A){const $=A.split(/\s+/).map(_=>{var P,T;return((T=(P=document.getElementById(_))==null?void 0:P.textContent)==null?void 0:T.trim())??""}).filter(Boolean);if($.length)return $.join(" ")}if(w instanceof HTMLInputElement||w instanceof HTMLTextAreaElement||w instanceof HTMLSelectElement){if(w.id){const _=document.querySelector(`label[for="${CSS.escape(w.id)}"]`);if((I=_==null?void 0:_.textContent)!=null&&I.trim())return _.textContent.trim()}const $=w.closest("label");if((L=$==null?void 0:$.textContent)!=null&&L.trim())return $.textContent.trim();if(w instanceof HTMLInputElement&&w.placeholder)return w.placeholder}const S=(w.textContent??"").replace(/\s+/g," ").trim();if(S)return S.slice(0,120);const k=w.getAttribute("title");return k?k.trim():""}function a(w){if(!w||w.startsWith("javascript:")||w.startsWith("#"))return null;try{return new URL(w,document.baseURI).href}catch{return w}}function n(w){let s=w;for(;s&&s!==document.body;){if(s.tagName==="HEADER")return"header";if(s.tagName==="FOOTER")return"footer";if(s.tagName==="NAV")return"nav";if(s.tagName==="MAIN"||s.getAttribute("role")==="main")return"main";if(s.tagName==="ASIDE"||s.getAttribute("role")==="complementary")return"aside";const A=s.getAttribute("role");if(A==="banner")return"header";if(A==="contentinfo")return"footer";if(A==="navigation")return"nav";s=s.parentElement}return"none"}function o(w){let s=w;for(;s&&s!==document.body;){if(s.tagName==="FOOTER"||s.getAttribute("role")==="contentinfo")return"footer";if(s.tagName==="HEADER"||s.getAttribute("role")==="banner")return"primary";s=s.parentElement}return"utility"}const r=[],i=document.querySelectorAll('nav, [role="navigation"]');for(const w of Array.from(i)){const s=o(w),A=w.querySelectorAll("a[href]");let S=0;for(const k of Array.from(A)){const I=a(k.getAttribute("href")),L=t(k);!L&&!I||r.push({href:I,accessibleName:L,index:S++,region:s})}}const c=[],d=new Set;for(const w of r)w.href&&(d.has(w.href)||(d.add(w.href),c.push({fingerprint:`href:${w.href}`,accessibleName:w.accessibleName,role:"link"})));const l=[{sel:'[data-test-id="logo"]',alias:"logo",role:"link"},{sel:".logo",alias:"logo",role:"link"},{sel:'[aria-label*="menu" i]',alias:"menu-toggle",role:"button"},{sel:'[data-test-id="menu-toggle"]',alias:"menu-toggle",role:"button"},{sel:'[aria-label*="search" i]',alias:"search-toggle",role:"button"},{sel:'[data-test-id="search"]',alias:"search-toggle",role:"button"}];for(const{sel:w,alias:s,role:A}of l){const S=document.querySelector(w);if(!S)continue;const k=`alias:${s}`;d.has(k)||(d.add(k),c.push({fingerprint:k,accessibleName:t(S),role:A}))}const h=[],m=/(help|support|contact|faq|chat|live\s*chat)\b/i,u=document.querySelectorAll('a, button, [role="button"], [role="link"]'),g=new Set;for(const w of Array.from(u)){const s=t(w),A=w.getAttribute("aria-label")??"",S=s||A;if(!m.test(S))continue;const k=`${S.toLowerCase().slice(0,32)}::${w.tagName}`;g.has(k)||(g.add(k),h.push({label:S.slice(0,80),href:w instanceof HTMLAnchorElement?a(w.getAttribute("href")):null,landmark:n(w)}))}const f=[],p=/(password|cvc|cvv|otp|one[-_\s]?time|security[-_\s]?answer|secret|2fa|mfa)/i,y=document.querySelectorAll("input, textarea, select");for(const w of Array.from(y)){if(w instanceof HTMLInputElement){const I=(w.type||"text").toLowerCase();if(I==="hidden"||I==="submit"||I==="button"||I==="image"||I==="reset")continue}const s=w.getAttribute("name")||w.id||"";if(!s)continue;const A=w instanceof HTMLInputElement?w.type||"text":w instanceof HTMLTextAreaElement?"textarea":"select",S=t(w),k=p.test(s)||p.test(S)||A==="password";f.push({name:s,type:A,label:S,isSecurityEssential:k})}let b=!1;if(document.querySelector('[role="progressbar"]')&&(b=!0),!b){const w=document.querySelectorAll('button, a, [role="button"]');let s=0,A=0;for(const S of Array.from(w)){const k=(S.textContent??"").trim();if(/step\s*\d+\s*of\s*\d+/i.test(k)){b=!0;break}/^(next|continue)$/i.test(k)&&s++,/^(previous|prev|back)$/i.test(k)&&A++}s>0&&A>0&&(b=!0)}return{url:e,title:(document.title||(((v=document.querySelector("h1"))==null?void 0:v.textContent)??"")).trim(),navLinks:r,sharedComponents:c,helpMechanisms:h,formFields:f,isMultiStep:b}}const Cc=`Respond ONLY with a JSON object — no markdown, no preamble:
374
+ $1`)}function Jt(e){return e===null||typeof e!="object"?JSON.stringify(e):Array.isArray(e)?"["+e.map(n=>Jt(n)).join(",")+"]":"{"+Object.keys(e).sort().map(n=>{const o=e[n];return JSON.stringify(n)+":"+Jt(o)}).join(",")+"}"}const pc="wcag-forensic-log",fc=1,_e="audits";let Da=null;function Aa(){return Da||(Da=gn(pc,fc,{upgrade(e){if(!e.objectStoreNames.contains(_e)){const t=e.createObjectStore(_e,{keyPath:["componentId","capturedAt"]});t.createIndex("byCapturedAt","capturedAt"),t.createIndex("byComponentId","componentId")}}})),Da}async function gc(e){const t=Jt(e),a=new TextEncoder().encode(t),n=await crypto.subtle.digest("SHA-256",a);return Array.from(new Uint8Array(n)).map(o=>o.toString(16).padStart(2,"0")).join("")}async function Er(e,t,a){if(e.length===0)return null;const n=e[0],o=n.componentId,r=n.pageUrl??n.scope,i=n.startedAt,c={componentId:o,pageUrl:r,scope:n.scope,grade:t.letter,totals:{critical:t.totals.critical,serious:t.totals.serious,moderate:t.totals.moderate,minor:t.totals.minor,unique:t.totals.unique},axeVersion:n.axeVersion,capturedAt:i,statesAudited:e.length},l={hash:await gc(c),componentId:o,pageUrl:r,scope:n.scope,grade:t.letter,totals:c.totals,axeVersion:n.axeVersion,capturedAt:i,statesAudited:e.length,durationMs:a};try{await(await Aa()).put(_e,l)}catch(h){return console.warn("[forensic-log] failed to record audit",h),l}return l}async function Rr(e,t,a){try{const n=await Aa(),o=await n.get(_e,[e,t]);if(!o){console.warn("[forensic-log] no entry to attach receipt to",{componentId:e,capturedAt:t});return}o.receipt=a,await n.put(_e,o)}catch(n){console.warn("[forensic-log] failed to attach receipt",n)}}async function St(){try{return(await(await Aa()).getAll(_e)).sort((a,n)=>a.capturedAt<n.capturedAt?1:-1)}catch(e){return console.warn("[forensic-log] listAudits failed",e),[]}}async function mc(e){try{return(await(await Aa()).transaction(_e).objectStore(_e).index("byComponentId").getAll(IDBKeyRange.only(e))).sort((o,r)=>o.capturedAt<r.capturedAt?1:-1)}catch(t){return console.warn("[forensic-log] listAuditsForComponent failed",t),[]}}const bc={"color-contrast":{whatsWrong:"Some text on this page is too light to read against its background.",whyItMatters:"People with low vision, older eyes, or anyone reading on a sunny phone screen can't make out faint text. Color blindness affects about 1 in 12 men and 1 in 200 women. Insufficient contrast is the #1 most-common cause of accessibility lawsuits — every Domino's-style ADA suit cites it.",howToFix:"If you use a website builder (Squarespace, Wix, Shopify, WordPress with a theme), open your site styles or theme color settings and pick darker text colors or lighter backgrounds. Aim for very dark text on white, or very light text on dark. Free check at webaim.org/resources/contrastchecker. If you have a developer, ask them to ensure all text meets WCAG AA contrast (4.5:1 for normal text, 3:1 for large text)."},"color-contrast-enhanced":{whatsWrong:"Text on this page doesn't meet the higher (AAA) contrast level — important for people with significant low vision.",whyItMatters:"AAA is a stricter standard than the legal-minimum AA. If your audience includes older adults, people with cataracts or macular degeneration, or government-related contracts that require AAA conformance, this matters.",howToFix:"Same as basic contrast — go darker on text or lighter on backgrounds. AAA requires 7:1 for normal text and 4.5:1 for large text."},"link-in-text-block":{whatsWrong:"Links inside paragraphs are distinguishable from the surrounding text by color alone.",whyItMatters:"If a user is colorblind or sees in grayscale, they can't tell which words are clickable. WCAG requires another visual cue (underline, bold, icon) in addition to color.",howToFix:"Add an underline to links inside paragraphs. In most theme editors there's a 'link style' setting — turn underlines back on. The underline is the standard convention; it's also more discoverable for everyone."},"image-alt":{whatsWrong:"Some images on this page have no description for screen readers.",whyItMatters:"Blind and low-vision users rely on screen readers to read pages aloud. When an image has no description, the screen reader either skips it entirely or reads out the file name — useless. Missing alt text is the #2 lawsuit-magnet category.",howToFix:"In your site editor, click each image and look for an 'Alt text' or 'Image description' field. Write a short sentence describing what the image shows or what it links to. For purely decorative images (dividers, background flourishes), enter empty alt text or check a 'decorative' box if the platform offers one."},"input-image-alt":{whatsWrong:"Image-based form buttons (submit buttons that are images) have no description.",whyItMatters:"Without a description, screen-reader users can't tell what the button does — they hear 'button, submit.png' or just 'graphic'.",howToFix:"Replace image submit buttons with regular text buttons (most platforms let you do this in form settings), OR if you must use an image, ensure it has alt text describing the action ('Subscribe', 'Send message', 'Submit form')."},"area-alt":{whatsWrong:"An image map (clickable regions inside a single image) has unlabeled regions.",whyItMatters:"Each clickable area is a link — without a description, screen-reader users don't know what it points to.",howToFix:"Image maps are uncommon today. If you need one, your developer should add an alt attribute to each clickable area. The simpler modern approach: use multiple separate linked images or buttons instead."},"svg-img-alt":{whatsWrong:"Some SVG icons that act as images don't have a description.",whyItMatters:"SVG icons used decoratively are fine to leave silent, but icons that convey meaning (status indicators, social-media icons that link out) need a description so screen readers know what they represent.",howToFix:"In most platforms this requires a developer. Tell them: 'meaningful SVG icons need a title or aria-label; decorative SVGs need aria-hidden=true'. Examples: an envelope icon linking to 'Contact us' should be labeled 'Contact us', not silent."},"object-alt":{whatsWrong:"Embedded content (PDFs, videos, embedded apps) has no fallback description.",whyItMatters:"If the embed fails to load — or the user's screen reader can't navigate it — they need a text description of what the embed is.",howToFix:"Replace embedded objects with native modern alternatives where possible (HTML5 video instead of Flash, linked PDF with a description). If you must use an <object> embed, ask your developer to add fallback content describing what it is."},"role-img-alt":{whatsWrong:"An element marked as an image is missing its description.",whyItMatters:"Same as missing image alt — screen-reader users get nothing where they should hear a description.",howToFix:"Ask your developer: 'elements with role=img need aria-label or aria-labelledby'."},"image-redundant-alt":{whatsWrong:"Some images have alt text that duplicates nearby visible text.",whyItMatters:"Screen-reader users hear the same content twice — the surrounding text and then the alt text. Annoying, not blocking. Lower-priority fix.",howToFix:"Find images where the caption or nearby paragraph already says what the image shows, and either change the alt text to add new info or set the alt to empty (so the screen reader skips it as decorative)."},label:{whatsWrong:"Some form fields have no label that screen readers can announce.",whyItMatters:"Visually you can see what each field is for (next to a 'Name:' or 'Email:' text), but screen-reader users hear only 'edit text' if there's no programmatic label connecting the text to the field. The result: blind users can't tell what to type where.",howToFix:"In a website builder, every form field has a 'Label' field or similar. Make sure every input has one filled in. Placeholder text (the gray hint inside the field) does NOT count as a label — it has to be a real label."},"select-name":{whatsWrong:"A dropdown menu has no description.",whyItMatters:"Screen-reader users hear 'combo box, has popup' but not what the dropdown is for. They can't fill out forms reliably.",howToFix:"In your form editor, check that each dropdown has a visible label above or beside it. If your platform has separate label/field settings, both must be filled."},"aria-input-field-name":{whatsWrong:"A custom input control (search box, range slider, custom date picker) has no name.",whyItMatters:"Custom controls are common in modern site builders — they look fancy but break for screen-reader users when not labeled.",howToFix:"This is a developer fix. Ask: 'custom inputs with role=textbox / searchbox / slider / spinbutton need an aria-label or aria-labelledby'."},"aria-toggle-field-name":{whatsWrong:"A toggle switch (on/off control) has no description.",whyItMatters:"User can see the switch but can't tell what it controls without a label.",howToFix:"Developer fix: 'switches and checkboxes with role=switch need an accessible name'."},"form-field-multiple-labels":{whatsWrong:"A form field has multiple labels, which confuses screen readers.",whyItMatters:"Screen readers will read all labels — sometimes saying conflicting things — leaving users unsure what to enter.",howToFix:"Developer fix: 'this field has multiple <label> associations; pick one and remove the others'."},"label-content-name-mismatch":{whatsWrong:"A button's visible text doesn't match the description it announces to screen readers.",whyItMatters:"A user dictating 'click Submit' to voice-control software may see 'Submit' on screen but the announce-name is something else, so the voice command fails.",howToFix:"Developer fix: 'aria-label should start with or fully contain the visible button text'."},"autocomplete-valid":{whatsWrong:"A field that should auto-fill (name, email, phone, address) is missing the right autocomplete hint.",whyItMatters:"Browsers use these hints to auto-fill personal info, helping users with motor disabilities and saving everyone time. Older adults appreciate this most.",howToFix:"In your form builder, look for an 'Autocomplete' or 'Field type' setting per field. Set them to 'email', 'name', 'tel', 'street-address' etc. as appropriate."},"button-name":{whatsWrong:"Some buttons have no description that screen readers can announce.",whyItMatters:"Visual users see an icon (a heart, a trash can, an X), but screen-reader users hear only 'button' — they don't know if it'll favorite a post, delete it, or close a dialog. This is a critical accessibility blocker and a top lawsuit driver.",howToFix:"Every button needs a text label. If you can't put text inside the button (icon-only design), in your platform's button settings look for an 'Aria label' or 'Screen reader text' field — fill it with what the button does ('Close', 'Add to cart', 'Open menu')."},"input-button-name":{whatsWrong:"An <input type=submit> or similar button has no description.",whyItMatters:"Same as button-name — screen-reader users don't know what the button does.",howToFix:"Set the button's 'value' or 'label' to text describing the action ('Submit', 'Subscribe', 'Send')."},"link-name":{whatsWrong:"Some links have no readable text — usually icon-only links.",whyItMatters:"Screen-reader users hear 'link' but not where the link goes. Users navigate by listening to a list of links — useless if every entry is just 'link, link, link'.",howToFix:"In your site editor, for icon-only links (social-media icons, navigation arrows), set an 'Aria label' or 'Title' field describing where it goes ('Follow us on Twitter', 'Next page')."},"empty-button":{whatsWrong:"A button is completely empty — no text, no icon, no description.",whyItMatters:"Both visual and screen-reader users can't tell what an empty button does.",howToFix:"Find empty buttons in your editor (often a leftover from a deleted icon) and either delete them or add text/an icon + description."},"empty-link":{whatsWrong:"A link has no readable text or description.",whyItMatters:"Screen-reader users hear nothing useful; visual users see an empty space they can't tell is clickable.",howToFix:"Same as link-name — add link text or an aria-label describing the destination."},"empty-heading":{whatsWrong:"A heading on this page has no text.",whyItMatters:"Screen-reader users navigate pages by jumping between headings. An empty heading is dead air — they don't know what the section is about.",howToFix:"Find the empty heading in your editor (often a section title that was deleted but the heading element remained) and either delete it or add real heading text."},"document-title":{whatsWrong:"This page's title (in the browser tab) is missing or generic.",whyItMatters:"Tab titles help users who have many tabs open know which one is which. Screen readers also announce the page title when the page loads.",howToFix:"In your platform's page settings or SEO panel, find the 'Page title' field and set it to something descriptive ('Contact Us — YourBusiness', not 'Untitled' or just your business name)."},"html-has-lang":{whatsWrong:"The page doesn't declare what language it's in.",whyItMatters:"Screen readers use this to pick the right voice and pronunciation rules. Without it, English content might be read with a French accent (or vice versa).",howToFix:"In your platform's site settings, find a 'Site language' option and set it (English = en, Spanish = es, etc.). Most modern platforms do this automatically — if it's missing, ask your developer to add lang='en' to the html element."},"html-lang-valid":{whatsWrong:"The page declares a language code that isn't valid.",whyItMatters:"Same as missing language — screen readers can't pick the right voice.",howToFix:"Use a standard ISO language code: en, es, fr, de, ja, zh, pt, etc. Avoid full strings like 'english' — use the two-letter code."},"valid-lang":{whatsWrong:"Text marked as a different language uses an invalid code.",whyItMatters:"Same as above — screen readers can't switch voices for the foreign-language passage.",howToFix:"Use ISO codes (en, fr, es) for the lang attribute on individual elements. This typically requires a developer."},"page-has-heading-one":{whatsWrong:"This page is missing a main heading (H1).",whyItMatters:"Screen-reader users use the H1 to confirm they landed on the right page. The H1 is also a strong SEO signal — Google uses it to understand what the page is about.",howToFix:"Add a clear, descriptive H1 to the top of the page. In website builders, this is usually the 'Title' or 'Page heading' setting. Each page should have exactly one H1."},"heading-order":{whatsWrong:"Heading levels skip — for example, an H2 jumps directly to an H4 with no H3 between them.",whyItMatters:"Screen-reader users navigate by heading levels. Skipped levels suggest missing content and break the page's outline. Confusing for everyone.",howToFix:"In your editor, find the headings that skip levels and either bump them up to the right level (H4 → H3 if no H2 exists between) or insert the missing level. Headings should follow a logical outline like a document outline."},"landmark-one-main":{whatsWrong:"This page has no 'main content' region marked.",whyItMatters:"Screen-reader users press a single shortcut to jump to the main content, skipping menus and headers. Without a marked main region, they have to tab through everything.",howToFix:"Most modern platforms add this automatically. If yours doesn't, ask your developer to wrap the main content in a <main> element."},"landmark-no-duplicate-banner":{whatsWrong:"There are multiple top banners (headers) on the page when there should be one.",whyItMatters:"Screen-reader users get confused by duplicate landmarks — they don't know which is the 'real' header.",howToFix:"Developer fix: 'only one element should have role=banner or be a top-level <header>; remove duplicates'."},"landmark-no-duplicate-contentinfo":{whatsWrong:"There are multiple footer regions on the page when there should be one.",whyItMatters:"Same as duplicate banner — confusing landmark navigation.",howToFix:"Developer fix: 'only one element should have role=contentinfo or be a top-level <footer>; remove duplicates'."},region:{whatsWrong:"Some content on the page isn't inside a recognized region (header, nav, main, footer, aside).",whyItMatters:"Screen-reader users navigate by these regions. Content outside any region can be missed when skimming.",howToFix:"Developer fix: 'wrap orphaned page content in semantic landmark elements (<main>, <aside>, <nav>) so it's reachable via landmark nav'."},bypass:{whatsWrong:"There's no 'skip to main content' link at the top of the page.",whyItMatters:"Keyboard-only users (and some screen-reader users) have to tab through every menu item on every page before reaching the main content. A skip link gives them a single keystroke to jump past the menu.",howToFix:"Most modern themes include a skip link automatically — it's only visible when keyboard-focused. If yours doesn't, ask your developer to add one as the first focusable element on every page."},"aria-required-attr":{whatsWrong:"An element uses a screen-reader role but is missing required information for that role.",whyItMatters:"Custom controls (sliders, tabs, dialogs) need specific extra info to work properly with screen readers. Missing pieces = broken behavior.",howToFix:"Developer fix: 'this role requires additional aria-* attributes; check axe-core docs for the specific role'."},"aria-allowed-attr":{whatsWrong:"An element has a screen-reader hint that doesn't apply to its role.",whyItMatters:"Inappropriate hints can confuse or break screen-reader announcements.",howToFix:"Developer fix: 'this aria-* attribute isn't allowed on this role; remove it or change the role'."},"aria-roles":{whatsWrong:"An element claims to be a type that isn't valid (typo or misuse of a role).",whyItMatters:"Screen readers ignore invalid roles, leaving the element unannounced.",howToFix:"Developer fix: 'this role value isn't recognized; check spelling or use a valid role'."},"aria-valid-attr":{whatsWrong:"An element uses a screen-reader hint that doesn't exist (typo).",whyItMatters:"Same as invalid roles — typos are silently ignored.",howToFix:"Developer fix: 'this aria-* attribute name isn't recognized; check spelling'."},"presentation-role-conflict":{whatsWrong:"An element is marked as decorative but is also focusable or has a description — contradictory.",whyItMatters:"Screen readers handle this inconsistently; some announce it anyway, some don't, leaving users with mixed experiences.",howToFix:"Developer fix: 'role=presentation/none cannot coexist with focusability or aria-label'."},"duplicate-id":{whatsWrong:"Two or more elements on the page share the same internal ID.",whyItMatters:"Some screen reader and skip-link behaviors rely on unique IDs. Duplicates can break label associations and skip-link targets.",howToFix:"Developer fix: 'rename duplicate IDs so each is unique'. Often arises when copying components."},"duplicate-id-aria":{whatsWrong:"An ID referenced by a screen-reader hint exists multiple times.",whyItMatters:"Screen readers may pick the wrong one and announce wrong information.",howToFix:"Developer fix: 'IDs used in aria-labelledby / aria-describedby must be unique'."},"meta-viewport":{whatsWrong:"The page tells mobile browsers users can't zoom in.",whyItMatters:"People with low vision rely on pinch-zoom to read small text. Disabling zoom is a real WCAG failure and a common lawsuit target.",howToFix:"In platform site settings, look for a 'Mobile zoom' or 'Pinch zoom' option and enable it. If your developer added user-scalable=no in a viewport meta tag, ask them to remove it."},"nested-interactive":{whatsWrong:"An interactive element (link, button) is inside another interactive element.",whyItMatters:"Keyboard and screen-reader users can't reliably reach the inner element. Common in 'card-as-link' designs where a button sits inside a clickable card.",howToFix:"Developer fix: 'avoid putting buttons inside links or links inside buttons; use a single interactive element with internal helpers'."},tabindex:{whatsWrong:"Some elements have positive tabindex values, forcing a custom focus order.",whyItMatters:"Custom focus orders are nearly always confusing — focus jumps around unexpectedly. Best practice is to never use positive tabindex.",howToFix:"Developer fix: 'replace tabindex=1/2/3... with tabindex=0 (or remove entirely); use DOM order to control focus order'."},"frame-focusable-content":{whatsWrong:"A frame on the page can't be navigated by keyboard.",whyItMatters:"Keyboard users get stuck — they can't reach interactive content inside the frame.",howToFix:"Developer fix: 'iframes containing interactive content shouldn't have tabindex=-1 on their parent or content'."},"no-focusable-content":{whatsWrong:"An element looks interactive but can't actually receive keyboard focus.",whyItMatters:"Keyboard-only users can't activate the element.",howToFix:"Developer fix: 'replace divs/spans-with-onClick with a real <button>, or add tabindex=0'."},"target-size":{whatsWrong:"Some buttons or links are too small to tap reliably on touch screens.",whyItMatters:"WCAG 2.2 requires touch targets to be at least 24×24 pixels. Small targets are a problem for users with hand tremors, large fingers, or motor disabilities. They're also frustrating for everyone.",howToFix:"In your editor, increase padding around small icon buttons. If your developer is involved, ask them to ensure all interactive elements have minimum 24×24 CSS pixels of clickable area."},"frame-title":{whatsWrong:"An iframe (embedded content) has no title.",whyItMatters:"Screen readers announce frame titles when entering them. Without one, users hear 'frame' with no context.",howToFix:"If using a third-party embed (YouTube, Vimeo, Stripe), most platforms set the title automatically. If yours doesn't, look for a 'Title' or 'Description' field on the embed block."}};function yc(e){const t=bc[e];return t||{whatsWrong:"An accessibility rule failed on this page.",whyItMatters:"Some users — particularly those using screen readers, keyboard-only navigation, or who have low vision or motor disabilities — may have trouble using this part of the page.",howToFix:"Share the technical rule ID with your developer. They can look up the full fix at https://dequeuniversity.com/rules/axe."}}const wc={whatsWrong:"This element appears to have a contrast issue, but it's actually transparent — its parent (or itself) has an opacity below 100%.",whyItMatters:"When something is partially transparent, the colors blend with whatever's behind it, which automated tools see as poor contrast. Most often this happens because the element is part of an entry animation (fade-in on scroll) that hasn't completed when the audit ran. The element will likely look correct once the animation finishes.",howToFix:"Check whether this element is inside a scroll-reveal animation (common patterns: classes like .fade-in, .reveal, .animate-on-scroll; or JavaScript using IntersectionObserver to add a .visible class). If so, this is NOT a real contrast problem — the element will become opaque once the user scrolls past it. If you want the audit to skip these false positives, ensure your site honors prefers-reduced-motion (which serves the final state immediately) or remove the opacity:0 default."};function $n(e,t){return e==="color-contrast"&&t&&(t.self<.99||t.ancestor<.99)?wc:yc(e)}const Xt="inflight:audit";async function ka(){return(await chrome.storage.local.get(Xt))[Xt]??null}async function Je(e){await chrome.storage.local.set({[Xt]:e})}async function rt(){await chrome.storage.local.remove(Xt)}async function up(){const e=await ka();e&&e.state==="running"&&await Je({...e,state:"interrupted",lastProgressAt:new Date().toISOString()})}async function Ot(e){const t=await ka();t&&await Je({...t,...e,lastProgressAt:new Date().toISOString()})}function Mt(){return crypto.randomUUID()}const Or="https://api.wcagcheckr.com/verify";function Mr(e){return`${Or}?hash=${encodeURIComponent(e)}`}function hp(e){return`${Or}#entry=${encodeURIComponent(JSON.stringify(e))}`}function vc(e){return e.length<2?[]:[...Ac(e),...xc(e),...Ic(e),...Sc(e)]}function Ac(e){const t=[];for(const a of["primary","footer","utility"]){const n=e.map(o=>({snapshot:o,hrefs:o.navLinks.filter(r=>r.region===a&&r.href).map(r=>r.href)})).filter(o=>o.hrefs.length>0);if(!(n.length<2))for(let o=0;o<n.length-1;o++){const r=n[o],i=n[o+1],c=r.hrefs.filter(p=>i.hrefs.includes(p));if(c.length<2)continue;const d=c.map(p=>r.hrefs.indexOf(p)),l=c.map(p=>i.hrefs.indexOf(p)),h=[...d].sort((p,f)=>p-f),m=[...l].sort((p,f)=>p-f),u=h.map(p=>c[d.indexOf(p)]),g=m.map(p=>c[l.indexOf(p)]);kc(u,g)||t.push({ruleId:"wcc-consistent-navigation",wcagCriterion:"3.2.3",wcagLevel:"AA",impact:"moderate",description:`${a}-region nav links appear in a different order on these pages.`,details:[`Page A: ${r.snapshot.url}`,` order: [${u.join(", ")}]`,`Page B: ${i.snapshot.url}`,` order: [${g.join(", ")}]`,"WCAG 3.2.3 requires that components that appear on multiple pages occur in the same relative order. Additions are fine; reorders are not."].join(`
375
+ `),pages:[{url:r.snapshot.url,evidence:u.join(" → ")},{url:i.snapshot.url,evidence:g.join(" → ")}]})}}return t}function kc(e,t){if(e.length!==t.length)return!1;for(let a=0;a<e.length;a++)if(e[a]!==t[a])return!1;return!0}function xc(e){const t=[],a=new Map;for(const n of e)for(const o of n.sharedComponents)a.has(o.fingerprint)||a.set(o.fingerprint,[]),a.get(o.fingerprint).push({url:n.url,accessibleName:o.accessibleName,role:o.role});for(const[n,o]of a.entries()){if(o.length<2)continue;const r=new Set(o.map(i=>Tn(i.accessibleName)));r.size<=1||t.push({ruleId:"wcc-consistent-identification",wcagCriterion:"3.2.4",wcagLevel:"AA",impact:"moderate",description:`Same component (${n}) has different accessible names across pages.`,details:[`Fingerprint: ${n}`,`Role: ${o[0].role}`,`Distinct names observed: ${[...r].map(i=>`"${i}"`).join(", ")}`,...o.map(i=>` ${i.url} → "${i.accessibleName}"`),"WCAG 3.2.4 requires components with identical functionality to have identical accessible names across pages."].join(`
376
+ `),pages:o.map(i=>({url:i.url,evidence:i.accessibleName}))})}return t}function Tn(e){return e.toLowerCase().replace(/\s+/g," ").trim()}function Ic(e){const t=[],a=new Map;for(const n of e)for(const o of n.helpMechanisms){const r=Tn(o.label);r&&(a.has(r)||a.set(r,[]),a.get(r).push({url:n.url,landmark:o.landmark,rawLabel:o.label}))}for(const[n,o]of a.entries()){if(o.length<2)continue;const r=new Set(o.map(i=>i.landmark));r.size<=1||t.push({ruleId:"wcc-consistent-help",wcagCriterion:"3.2.6",wcagLevel:"A",impact:"minor",description:`Help mechanism "${n}" appears in different landmarks across pages.`,details:[`Help label: "${n}"`,`Distinct landmarks observed: ${[...r].join(", ")}`,...o.map(i=>` ${i.url} → ${i.landmark}`),"WCAG 3.2.6 (AA, WCAG 2.2): when a help mechanism exists on multiple pages, it must appear in the same relative order. Same-label help that moves between header/footer/nav is a divergence."].join(`
377
+ `),pages:o.map(i=>({url:i.url,evidence:`${i.rawLabel} in ${i.landmark}`}))})}return t}function Sc(e){const t=[],a=e.filter(o=>o.isMultiStep);if(a.length<2)return t;const n=new Map;for(const o of a)for(const r of o.formFields){const i=Tn(r.name);i&&(n.has(i)||n.set(i,[]),n.get(i).push({url:o.url,label:r.label,type:r.type,isSecurityEssential:r.isSecurityEssential}))}for(const[o,r]of n.entries())r.length<2||r.some(i=>i.isSecurityEssential)||t.push({ruleId:"wcc-redundant-entry",wcagCriterion:"3.3.7",wcagLevel:"A",impact:"minor",description:`Field "${o}" is requested on multiple steps of a multi-step flow.`,details:[`Field name: "${o}"`,"Occurrences:",...r.map(i=>` ${i.url} → label "${i.label}", type ${i.type}`),"WCAG 3.3.7 (A, WCAG 2.2): information previously entered in a multi-step process must be auto-populated or selectable, unless re-entry is essential (e.g., password confirmation, security questions)."].join(`
378
+ `),pages:r.map(i=>({url:i.url,evidence:`${i.label} (${i.type})`}))});return t}const Qt={A:0,B:1,C:2,D:3,F:4},to={low:0,moderate:1,high:2,critical:3};function Cc(e,t){return Qt[e]>Qt[t]?e:t}function $c(e,t){return to[e]>to[t]?e:t}function Tc(e){const t=[];for(const a of e)for(const n of a.results)for(const o of n.violations)o.needsReview||t.push({url:a.url,componentId:a.componentId,v:o});return t}function Ec(e,t){const a=[];for(const n of e){const o=t==null?void 0:t.get(n.componentId);for(const r of n.results)for(const i of r.violations)i.needsReview&&(o!=null&&o.has(i.matchKey)||a.push({url:n.url,v:i}))}return a}function Dr(e,t,a,n,o,r,i,c,d,l){const h=t.filter(C=>!C.error).length,m=t.filter(C=>!!C.error&&!C.skipped).length,u=t.filter(C=>!!C.skipped).length,g=t.length<=1?"single-page":"site-crawl",p=t.filter(C=>!C.skipped).map(C=>{if(C.error)return{url:C.url,grade:"F",risk:"critical",uniqueViolations:0,totals:{critical:0,serious:0,moderate:0,minor:0},durationMs:C.durationMs,error:C.error,walkthroughFindings:[],walkthroughAcknowledgements:[]};const W=C.results.flatMap(Y=>Y.violations),z=r==null?void 0:r.get(C.componentId),Z=i==null?void 0:i.get(C.componentId),ie=c==null?void 0:c.get(C.componentId),ee=d==null?void 0:d.get(C.componentId),I=l==null?void 0:l.get(C.componentId),B=Ke(W,{runs:[],workflows:Me},C.results,ie?new Set(ie):void 0,z?[...z]:void 0,Z?[...Z]:void 0,ee?[...ee]:void 0,g,I?[...I]:void 0),P=B.score;return{url:C.url,grade:B.combined,risk:P.risk,uniqueViolations:P.totals.unique,totals:{critical:P.totals.critical,serious:P.totals.serious,moderate:P.totals.moderate,minor:P.totals.minor},durationMs:C.durationMs,walkthroughFindings:P.walkthroughFindings,walkthroughAcknowledgements:P.walkthroughAcknowledgements}});p.sort((C,W)=>{const z=Qt[W.grade]-Qt[C.grade];return z!==0?z:W.uniqueViolations-C.uniqueViolations});let f="A",y="low";for(const C of p)f=Cc(f,C.grade),y=$c(y,C.risk);const b=Tc(t).filter(({v:C,componentId:W})=>{const z=c==null?void 0:c.get(W);return!(z!=null&&z.has(C.matchKey))}),A=C=>`${C.ruleId}::${C.target.selector}`,w=new Map;for(const{v:C}of b){const W=A(C);w.has(W)||w.set(W,C)}const s=w.size,v={critical:0,serious:0,moderate:0,minor:0};for(const C of w.values())v[C.impact]++;const S=new Map;for(const{url:C,v:W}of b){let z=S.get(W.ruleId);z||(z={ruleId:W.ruleId,description:W.description,impact:W.impact,urls:new Set,totalOccurrences:0},S.set(W.ruleId,z)),z.urls.add(C),z.totalOccurrences++}const k=Array.from(S.values()).map(C=>({ruleId:C.ruleId,description:C.description,impact:C.impact,urlsAffected:C.urls.size,totalOccurrences:C.totalOccurrences,sampleUrls:Array.from(C.urls).slice(0,5)})).sort((C,W)=>W.urlsAffected!==C.urlsAffected?W.urlsAffected-C.urlsAffected:W.totalOccurrences-C.totalOccurrences),x=Ec(t,c),L=new Map,$=new Set;for(const{url:C,v:W}of x){$.add(`${W.ruleId}::${W.target.selector}`);let z=L.get(W.ruleId);z||(z={ruleId:W.ruleId,description:W.description,impact:W.impact,urls:new Set,totalOccurrences:0},L.set(W.ruleId,z)),z.urls.add(C),z.totalOccurrences++}const U=Array.from(L.values()).map(C=>({ruleId:C.ruleId,description:C.description,impact:C.impact,urlsAffected:C.urls.size,totalOccurrences:C.totalOccurrences,sampleUrls:Array.from(C.urls).slice(0,5)})).sort((C,W)=>W.urlsAffected!==C.urlsAffected?W.urlsAffected-C.urlsAffected:W.totalOccurrences-C.totalOccurrences),F=$.size,T={minor:0,moderate:1,serious:2,critical:3},N=new Map;for(const C of t){if(C.error)continue;const W=c==null?void 0:c.get(C.componentId);for(const z of C.results)for(const Z of z.violations){if(W!=null&&W.has(Z.matchKey))continue;const ie=`${De(Z.ruleId,Z.target.selector)}::${Z.needsReview?"NR":"ACT"}`;let ee=N.get(ie);ee||(ee={ruleId:Z.ruleId,representativeSelector:Z.target.selector,representativeOuterHTML:Z.target.outerHTML??"",impact:Z.impact,wcagCriterion:Z.wcagCriterion??null,axeDescription:Z.description??"",pages:new Map,totalOccurrences:0,needsReview:Z.needsReview??!1,failureSummary:Z.target.failureSummary??null},N.set(ie,ee)),T[Z.impact]>T[ee.impact]&&(ee.impact=Z.impact),ee.totalOccurrences++,ee.pages.has(C.url)||ee.pages.set(C.url,{selector:Z.target.selector,outerHTML:Z.target.outerHTML??""}),!ee.failureSummary&&Z.target.failureSummary&&(ee.failureSummary=Z.target.failureSummary)}}const O=Array.from(N.values()).map(C=>({ruleId:C.ruleId,representativeSelector:C.representativeSelector,representativeOuterHTML:C.representativeOuterHTML,impact:C.impact,wcagCriterion:C.wcagCriterion,axeDescription:C.axeDescription,pages:Array.from(C.pages.entries()).map(([W,z])=>({url:W,selector:z.selector,outerHTML:z.outerHTML})),totalOccurrences:C.totalOccurrences,isShared:C.pages.size>=2,needsReview:C.needsReview,failureSummary:C.failureSummary})).sort((C,W)=>T[W.impact]!==T[C.impact]?T[W.impact]-T[C.impact]:W.pages.length-C.pages.length),D={A:0,B:0,C:0,D:0,F:0};for(const C of p)D[C.grade]++;const G=t.map(C=>C.snapshot).filter(C=>!!C),M=vc(G),j=new Set,R=[],V=[];for(const C of p){for(const W of C.walkthroughFindings){const z=`${W.criterionId}::${W.pageUrl}`;j.has(z)||(j.add(z),R.push(W))}for(const W of C.walkthroughAcknowledgements){const z=`${W.criterionId}::${W.pageUrl}::ack`;j.has(z)||(j.add(z),V.push(W))}}const K=new Map,Q={untested:0,inconclusive:1,failing:2},we=new Map;for(const C of ua("2.1","AA"))we.set(C.id,C.title);const Ae=new Map;function pe(C,W){const z=`${W.pageUrl}::${W.selector??""}::${W.source}::${W.ruleOrStepId}`;let Z=Ae.get(C);if(Z||(Z=new Set,Ae.set(C,Z)),Z.has(z))return;Z.add(z);const ie=K.get(C);ie&&(ie.evidence.length>=6||ie.evidence.push(W))}for(const C of t){if(C.error)continue;const W=C.results.flatMap(q=>q.violations),z=r==null?void 0:r.get(C.componentId),Z=i==null?void 0:i.get(C.componentId),ie=c==null?void 0:c.get(C.componentId),ee=d==null?void 0:d.get(C.componentId),I=l==null?void 0:l.get(C.componentId),B=wa(W,void 0,C.results,ie?new Set(ie):void 0,z?[...z]:void 0,Z?[...Z]:void 0,ee?[...ee]:void 0,g,I?[...I]:void 0);if(!B.coverage)continue;const P=(q,J)=>{for(const X of q){const fe=we.get(X)??X,Ee=K.get(X);Ee?Q[J]>Q[Ee.state]&&K.set(X,{state:J,title:fe,evidence:Ee.evidence}):K.set(X,{state:J,title:fe,evidence:[]})}};P(B.coverage.untestedCriteria,"untested"),P(B.coverage.inconclusiveCriteria,"inconclusive"),P(B.coverage.failingCriteria,"failing");const Y=B.coverage.inconclusiveReasons??{};for(const[q,J]of Object.entries(Y))for(const X of J)if(X.elements&&X.elements.length>0)for(const fe of X.elements)pe(q,{pageUrl:X.pageUrl??C.url,selector:fe.selector,ruleOrStepId:X.ruleOrStepId,source:X.source,notes:fe.failureSummary});else pe(q,{pageUrl:X.pageUrl??C.url,ruleOrStepId:X.ruleOrStepId,source:X.source,notes:X.resolutionHint});const ne=B.coverage.failingReasons??{};for(const[q,J]of Object.entries(ne))for(const X of J)pe(q,{pageUrl:X.pageUrl??C.url,selector:X.selector,ruleOrStepId:X.ruleOrStepId,source:X.source,notes:X.notes})}const Se=Array.from(K.entries()).map(([C,W])=>({criterionId:C,title:W.title,state:W.state,evidence:W.evidence})).sort((C,W)=>C.criterionId.localeCompare(W.criterionId,void 0,{numeric:!0}));return{startedAt:a,finishedAt:n,startUrl:e,pagesAudited:h,pagesFailed:m,pagesSkipped:u,totalDurationMs:o,siteGrade:f,siteRisk:y,totalUniqueViolations:s,totals:v,topViolations:k,needsReviewItems:U,totalNeedsReview:F,findingGroups:O,pages:p,pagesByGrade:D,consistencyFindings:M,walkthroughFindings:R,walkthroughAcknowledgements:V,totalWalkthroughFindings:R.length,blockingCriteria:Se}}const Rc="offscreen/offscreen.html";let Ft=null;async function Oc(){return Ft||(Ft=(async()=>{if(!chrome.offscreen)throw new Error("chrome.offscreen API is unavailable. Update Chrome to 109+.");await chrome.offscreen.hasDocument()||await chrome.offscreen.createDocument({url:Rc,reasons:[chrome.offscreen.Reason.DOM_PARSER],justification:"Parse PDF accessibility structure tree with pdf.js (no DOM access available in service worker)."})})().catch(e=>{throw Ft=null,e})),Ft}async function Mc(e,t){try{await Oc()}catch(o){return{ok:!1,source:e,findings:[],pageCount:null,fatalError:`Could not create offscreen document for PDF audit: ${o instanceof Error?o.message:String(o)}`}}const a={type:"OFFSCREEN_PDF_AUDIT_REQUEST",url:e,...(t==null?void 0:t.maxBytes)!==void 0?{maxBytes:t.maxBytes}:{}};let n;try{n=await chrome.runtime.sendMessage(a)}catch(o){return{ok:!1,source:e,findings:[],pageCount:null,fatalError:`Offscreen audit messaging failed: ${o instanceof Error?o.message:String(o)}`}}return!n||n.type!=="OFFSCREEN_PDF_AUDIT_RESPONSE"?{ok:!1,source:e,findings:[],pageCount:null,fatalError:"Offscreen audit returned an unexpected response shape."}:{ok:n.ok,source:n.source,findings:[...n.findings],pageCount:n.pageCount,...n.fatalError?{fatalError:n.fatalError}:{},...n._perPageContrast?{_perPageContrast:n._perPageContrast}:{},...n.aiCandidates?{aiCandidates:n.aiCandidates}:{}}}async function Dc(e,t){var a;try{return((a=(await chrome.scripting.executeScript({target:{tabId:e},func:Lc,args:[t]}))[0])==null?void 0:a.result)??null}catch{return null}}function Lc(e){var A;function t(w){var x,L;const s=w.getAttribute("aria-label");if(s&&s.trim())return s.trim();const v=w.getAttribute("aria-labelledby");if(v){const $=v.split(/\s+/).map(U=>{var F,T;return((T=(F=document.getElementById(U))==null?void 0:F.textContent)==null?void 0:T.trim())??""}).filter(Boolean);if($.length)return $.join(" ")}if(w instanceof HTMLInputElement||w instanceof HTMLTextAreaElement||w instanceof HTMLSelectElement){if(w.id){const U=document.querySelector(`label[for="${CSS.escape(w.id)}"]`);if((x=U==null?void 0:U.textContent)!=null&&x.trim())return U.textContent.trim()}const $=w.closest("label");if((L=$==null?void 0:$.textContent)!=null&&L.trim())return $.textContent.trim();if(w instanceof HTMLInputElement&&w.placeholder)return w.placeholder}const S=(w.textContent??"").replace(/\s+/g," ").trim();if(S)return S.slice(0,120);const k=w.getAttribute("title");return k?k.trim():""}function a(w){if(!w||w.startsWith("javascript:")||w.startsWith("#"))return null;try{return new URL(w,document.baseURI).href}catch{return w}}function n(w){let s=w;for(;s&&s!==document.body;){if(s.tagName==="HEADER")return"header";if(s.tagName==="FOOTER")return"footer";if(s.tagName==="NAV")return"nav";if(s.tagName==="MAIN"||s.getAttribute("role")==="main")return"main";if(s.tagName==="ASIDE"||s.getAttribute("role")==="complementary")return"aside";const v=s.getAttribute("role");if(v==="banner")return"header";if(v==="contentinfo")return"footer";if(v==="navigation")return"nav";s=s.parentElement}return"none"}function o(w){let s=w;for(;s&&s!==document.body;){if(s.tagName==="FOOTER"||s.getAttribute("role")==="contentinfo")return"footer";if(s.tagName==="HEADER"||s.getAttribute("role")==="banner")return"primary";s=s.parentElement}return"utility"}const r=[],i=document.querySelectorAll('nav, [role="navigation"]');for(const w of Array.from(i)){const s=o(w),v=w.querySelectorAll("a[href]");let S=0;for(const k of Array.from(v)){const x=a(k.getAttribute("href")),L=t(k);!L&&!x||r.push({href:x,accessibleName:L,index:S++,region:s})}}const c=[],d=new Set;for(const w of r)w.href&&(d.has(w.href)||(d.add(w.href),c.push({fingerprint:`href:${w.href}`,accessibleName:w.accessibleName,role:"link"})));const l=[{sel:'[data-test-id="logo"]',alias:"logo",role:"link"},{sel:".logo",alias:"logo",role:"link"},{sel:'[aria-label*="menu" i]',alias:"menu-toggle",role:"button"},{sel:'[data-test-id="menu-toggle"]',alias:"menu-toggle",role:"button"},{sel:'[aria-label*="search" i]',alias:"search-toggle",role:"button"},{sel:'[data-test-id="search"]',alias:"search-toggle",role:"button"}];for(const{sel:w,alias:s,role:v}of l){const S=document.querySelector(w);if(!S)continue;const k=`alias:${s}`;d.has(k)||(d.add(k),c.push({fingerprint:k,accessibleName:t(S),role:v}))}const h=[],m=/(help|support|contact|faq|chat|live\s*chat)\b/i,u=document.querySelectorAll('a, button, [role="button"], [role="link"]'),g=new Set;for(const w of Array.from(u)){const s=t(w),v=w.getAttribute("aria-label")??"",S=s||v;if(!m.test(S))continue;const k=`${S.toLowerCase().slice(0,32)}::${w.tagName}`;g.has(k)||(g.add(k),h.push({label:S.slice(0,80),href:w instanceof HTMLAnchorElement?a(w.getAttribute("href")):null,landmark:n(w)}))}const p=[],f=/(password|cvc|cvv|otp|one[-_\s]?time|security[-_\s]?answer|secret|2fa|mfa)/i,y=document.querySelectorAll("input, textarea, select");for(const w of Array.from(y)){if(w instanceof HTMLInputElement){const x=(w.type||"text").toLowerCase();if(x==="hidden"||x==="submit"||x==="button"||x==="image"||x==="reset")continue}const s=w.getAttribute("name")||w.id||"";if(!s)continue;const v=w instanceof HTMLInputElement?w.type||"text":w instanceof HTMLTextAreaElement?"textarea":"select",S=t(w),k=f.test(s)||f.test(S)||v==="password";p.push({name:s,type:v,label:S,isSecurityEssential:k})}let b=!1;if(document.querySelector('[role="progressbar"]')&&(b=!0),!b){const w=document.querySelectorAll('button, a, [role="button"]');let s=0,v=0;for(const S of Array.from(w)){const k=(S.textContent??"").trim();if(/step\s*\d+\s*of\s*\d+/i.test(k)){b=!0;break}/^(next|continue)$/i.test(k)&&s++,/^(previous|prev|back)$/i.test(k)&&v++}s>0&&v>0&&(b=!0)}return{url:e,title:(document.title||(((A=document.querySelector("h1"))==null?void 0:A.textContent)??"")).trim(),navLinks:r,sharedComponents:c,helpMechanisms:h,formFields:p,isMultiStep:b}}const Nc=`Respond ONLY with a JSON object — no markdown, no preamble:
371
379
  {
372
380
  "verdict": "pass" | "fail" | "uncertain",
373
381
  "reasoning": "one-sentence explanation",
374
382
  "confidence": 0.0 to 1.0
375
- }`,Pt=`
383
+ }`,_t=`
376
384
 
377
385
  Previous verdict on this page: {{verdict}} ({{confidence}}% confidence). Reasoning: "{{reasoning}}". Keep the same verdict unless concrete new evidence has changed.
378
- `,$c={version:0,jsonOutputInstructions:Cc,altText:{template:`You are an accessibility auditor. Judge whether the alt text describes the image.
386
+ `,Pc={version:0,jsonOutputInstructions:Nc,altText:{template:`You are an accessibility auditor. Judge whether the alt text describes the image.
379
387
 
380
388
  Alt: {{alt}}
381
389
  {{surroundingContextLine}}
@@ -470,7 +478,7 @@ Verdict:
470
478
  - "fail" = clear trap evidence: stuck runs, Shift-Tab can't escape a region, or reverse walk diverges significantly.
471
479
  - "uncertain" = too few tabbable elements (<3) to judge OR data is inconsistent.
472
480
 
473
- {{jsonOutputInstructions}}`,maxTokens:400,anchorTemplate:Pt},nonTextContrast:{template:`You are an accessibility auditor evaluating non-text contrast (WCAG 1.4.11 — Non-text Contrast, Level AA).
481
+ {{jsonOutputInstructions}}`,maxTokens:400,anchorTemplate:_t},nonTextContrast:{template:`You are an accessibility auditor evaluating non-text contrast (WCAG 1.4.11 — Non-text Contrast, Level AA).
474
482
 
475
483
  A screenshot of {{pageUrl}} is attached.{{anchor}}
476
484
 
@@ -485,7 +493,7 @@ Verdict:
485
493
 
486
494
  When in doubt, prefer pass or uncertain over fail. Decoration misclassification is the #1 false-positive trap.
487
495
 
488
- {{jsonOutputInstructions}}`,maxTokens:1500,anchorTemplate:Pt},readingOrder:{template:`You are an accessibility auditor evaluating reading-order quality (WCAG 1.3.2 — Meaningful Sequence).{{anchor}}
496
+ {{jsonOutputInstructions}}`,maxTokens:1500,anchorTemplate:_t},readingOrder:{template:`You are an accessibility auditor evaluating reading-order quality (WCAG 1.3.2 — Meaningful Sequence).{{anchor}}
489
497
 
490
498
  A screenshot of {{pageUrl}} is attached. A heuristic compared DOM source order to visual position and flagged {{flaggedCount}} element(s) whose DOM index diverges from visual index:
491
499
  {{issuesList}}
@@ -499,7 +507,7 @@ Verdict:
499
507
 
500
508
  When in doubt, prefer "uncertain" over "fail". Multi-column false positives are the most common trap.
501
509
 
502
- {{jsonOutputInstructions}}`,maxTokens:1500,anchorTemplate:Pt},focusOrder:{template:`You are an accessibility auditor evaluating tab-order quality (WCAG 2.4.3 — Focus Order).{{anchor}}
510
+ {{jsonOutputInstructions}}`,maxTokens:1500,anchorTemplate:_t},focusOrder:{template:`You are an accessibility auditor evaluating tab-order quality (WCAG 2.4.3 — Focus Order).{{anchor}}
503
511
 
504
512
  A screenshot of {{pageUrl}} is attached. The page's keyboard-tabbable elements were walked in order; the sequence below groups items by visual row ({{rowBucketPx}}px buckets) so sub-pixel y-noise can't cause a fail.
505
513
 
@@ -521,7 +529,7 @@ Verdict:
521
529
 
522
530
  When in doubt, prefer "uncertain" over "fail". Multi-column / banked layouts are the most common false-fail trap.
523
531
 
524
- {{jsonOutputInstructions}}`,maxTokens:1500,anchorTemplate:Pt},labelInName:{template:`You are an accessibility auditor evaluating WCAG 2.5.3 (Label in Name) for {{pageUrl}}.
532
+ {{jsonOutputInstructions}}`,maxTokens:1500,anchorTemplate:_t},labelInName:{template:`You are an accessibility auditor evaluating WCAG 2.5.3 (Label in Name) for {{pageUrl}}.
525
533
 
526
534
  WCAG 2.5.3 says: when an interactive control has BOTH visible text AND a programmatic accessible name, the accessible name must contain the visible text (case-insensitive, leading non-text characters ignored). Required for speech-recognition users.
527
535
 
@@ -550,23 +558,23 @@ Screenshot of {{pageUrl}} attached.
550
558
 
551
559
  DECISIVENESS BIAS: the primary already returned uncertain. If you also return uncertain, the system escalates to FAIL + human verification. Only return uncertain when the screenshot is unreadable, sequence is empty / too short (<3 items), or you genuinely cannot tell.
552
560
 
553
- {{jsonOutputInstructions}}`,maxTokens:1500}};function Rr(e,t){return e.replace(/\{\{(\w+)\}\}/g,(a,n)=>{const o=t[n];return o??""})}function Or(){var e;return((e=tr())==null?void 0:e.aiPrompts)??$c}function ge(e,t){const a=Or(),n=e(a),o={...t,jsonOutputInstructions:a.jsonOutputInstructions};return{text:Rr(n.template,o),maxTokens:n.maxTokens}}function Nt(e,t){if(!t)return"";const a=e(Or());return a.anchorTemplate?Rr(a.anchorTemplate,{verdict:t.verdict.toUpperCase(),confidence:(t.confidence*100).toFixed(0),reasoning:t.reasoning.slice(0,400).replace(/"/g,"'")}):""}const Tc={input:3/1e6,output:15/1e6},Ec={input:1/1e6,output:5/1e6},Rc="claude-sonnet-4-6",Oc="https://api.anthropic.com/v1/messages",Mc="2023-06-01";function Dc(e){const t=e.model||Rc,a=t.includes("haiku")?Ec:Tc;return{providerId:"anthropic",async judgeAltText({imageUrl:n,alt:o,surroundingContext:r,signal:i}){const c=await ao(n,i);if(!c)return no("Could not fetch image to verify alt text",0,t);const{text:d,maxTokens:l}=ge(m=>m.altText,{alt:o?`"${o}"`:"(empty / missing)",surroundingContextLine:r?`
554
- Nearby page context: ${r.slice(0,500)}`:""}),h=await ie({apiKey:e.apiKey,model:t,maxTokens:l,signal:i,messages:[{role:"user",content:[{type:"image",source:{type:"base64",media_type:c.mediaType,data:c.data}},{type:"text",text:d}]}]});return pe(h,a)},async judgeHeading({headingText:n,sectionContent:o,signal:r}){const{text:i,maxTokens:c}=ge(l=>l.heading,{headingText:n,sectionContent:o.slice(0,800)}),d=await ie({apiKey:e.apiKey,model:t,maxTokens:c,signal:r,messages:[{role:"user",content:[{type:"text",text:i}]}]});return pe(d,a)},async judgeSensoryLanguage({instructionText:n,signal:o}){const{text:r,maxTokens:i}=ge(d=>d.sensoryLanguage,{instructionText:n.slice(0,800)}),c=await ie({apiKey:e.apiKey,model:t,maxTokens:i,signal:o,messages:[{role:"user",content:[{type:"text",text:r}]}]});return pe(c,a)},async judgeAriaSemantics({elementHtml:n,computedRole:o,surroundingHtml:r,signal:i}){const{text:c,maxTokens:d}=ge(h=>h.ariaSemantics,{elementHtml:n.slice(0,600),computedRole:o,surroundingHtml:r.slice(0,500)}),l=await ie({apiKey:e.apiKey,model:t,maxTokens:d,signal:i,messages:[{role:"user",content:[{type:"text",text:c}]}]});return pe(l,a)},async judgeImageOfText({imageUrl:n,accessibleName:o,signal:r}){const i=await ao(n,r);if(!i)return no("Could not fetch image to inspect for embedded text",0,t);const{text:c,maxTokens:d}=ge(h=>h.imageOfText,{accessibleNameLine:o?`
555
- Accessible name (alt / aria-label) of this image: "${o.slice(0,200)}"`:""}),l=await ie({apiKey:e.apiKey,model:t,maxTokens:d,signal:r,messages:[{role:"user",content:[{type:"image",source:{type:"base64",media_type:i.mediaType,data:i.data}},{type:"text",text:c}]}]});return pe(l,a)},async judgeColorOnlyMeaning({elementHtml:n,contextDescription:o,signal:r}){const{text:i,maxTokens:c}=ge(l=>l.colorOnlyMeaning,{elementHtml:n.slice(0,800),contextDescription:o.slice(0,400)}),d=await ie({apiKey:e.apiKey,model:t,maxTokens:c,signal:r,messages:[{role:"user",content:[{type:"text",text:i}]}]});return pe(d,a)},async judgeGenericLinkText({linkText:n,surroundingText:o,signal:r}){const{text:i,maxTokens:c}=ge(l=>l.genericLinkText,{linkText:n.slice(0,200),surroundingText:o.slice(0,800)}),d=await ie({apiKey:e.apiKey,model:t,maxTokens:c,signal:r,messages:[{role:"user",content:[{type:"text",text:i}]}]});return pe(d,a)},async judgeWallOfText({blockText:n,structuralChildrenCount:o,signal:r}){const{text:i,maxTokens:c}=ge(l=>l.wallOfText,{blockText:n.slice(0,1200),structuralChildrenCount:String(o)}),d=await ie({apiKey:e.apiKey,model:t,maxTokens:c,signal:r,messages:[{role:"user",content:[{type:"text",text:i}]}]});return pe(d,a)},async judgeLanguageMismatch({declaredLang:n,bodyTextSample:o,signal:r}){const{text:i,maxTokens:c}=ge(l=>l.languageMismatch,{declaredLang:n||"(none / empty)",bodyTextSample:o.slice(0,1200)}),d=await ie({apiKey:e.apiKey,model:t,maxTokens:c,signal:r,messages:[{role:"user",content:[{type:"text",text:i}]}]});return pe(d,a)},async suggestColorFix(n){const o=await ie({apiKey:e.apiKey,model:t,maxTokens:600,messages:[{role:"user",content:[{type:"text",text:Hc(n)}]}]});return zc(o,a)},async generateExecutiveSummary(n){const o=await ie({apiKey:e.apiKey,model:t,maxTokens:800,messages:[{role:"user",content:[{type:"text",text:Bc(n)}]}]});return Yc(o,a)},async resolveAxeIncomplete(n){const o=await ie({apiKey:e.apiKey,model:t,maxTokens:400,signal:n.signal,messages:[{role:"user",content:[{type:"text",text:qc(n)}]}]});return Kc(o,a,t)},async judgeFocusVisible({pageUrl:n,samples:o,signal:r}){const i=[];for(const h of o){const m=/^data:(image\/[a-z]+);base64,(.+)$/i.exec(h.screenshot);m&&(i.push({type:"image",source:{type:"base64",media_type:m[1],data:m[2]}}),i.push({type:"text",text:`Image #${h.ordinal} — selector: ${h.selector} — role: ${h.role||"(none)"} — name: "${h.name||"(no name)"}" — rect: x=${Math.round(h.rect.x)} y=${Math.round(h.rect.y)} w=${Math.round(h.rect.width)} h=${Math.round(h.rect.height)} — outline: ${h.focusStyle.outlineStyle} ${h.focusStyle.outlineWidth} ${h.focusStyle.outlineColor} (offset ${h.focusStyle.outlineOffset}) — box-shadow: ${h.focusStyle.boxShadow||"none"}`}))}const{text:c,maxTokens:d}=ge(h=>h.focusVisible,{pageUrl:n,sampleCount:String(o.length)});i.push({type:"text",text:c});const l=await ie({apiKey:e.apiKey,model:t,maxTokens:d,signal:r,messages:[{role:"user",content:i}]});return pe(l,a)},async judgeConsistencyDivergence({wcagCriterion:n,ruleId:o,description:r,details:i,pages:c,signal:d}){const l=jc(n,o,r,i,c),h=await ie({apiKey:e.apiKey,model:t,maxTokens:500,signal:d,messages:[{role:"user",content:[{type:"text",text:l}]}]});return pe(h,a)},async judgeKeyboardTrap({pageUrl:n,forwardSequence:o,reverseSequence:r,stuckRuns:i,reverseMismatches:c,previousVerdict:d,signal:l}){const h=o.map((b,v)=>` ${v+1}. ${b}`).join(`
556
- `)||" (no tabbable elements found)",m=r.map((b,v)=>` ${v+1}. ${b}`).join(`
561
+ {{jsonOutputInstructions}}`,maxTokens:1500}};function Lr(e,t){return e.replace(/\{\{(\w+)\}\}/g,(a,n)=>{const o=t[n];return o??""})}function Nr(){var e;return((e=or())==null?void 0:e.aiPrompts)??Pc}function be(e,t){const a=Nr(),n=e(a),o={...t,jsonOutputInstructions:a.jsonOutputInstructions};return{text:Lr(n.template,o),maxTokens:n.maxTokens}}function Ut(e,t){if(!t)return"";const a=e(Nr());return a.anchorTemplate?Lr(a.anchorTemplate,{verdict:t.verdict.toUpperCase(),confidence:(t.confidence*100).toFixed(0),reasoning:t.reasoning.slice(0,400).replace(/"/g,"'")}):""}const Fc={input:3/1e6,output:15/1e6},_c={input:1/1e6,output:5/1e6},Uc="claude-sonnet-4-6",Wc="https://api.anthropic.com/v1/messages",Gc="2023-06-01";function Vc(e){const t=e.model||Uc,a=t.includes("haiku")?_c:Fc;return{providerId:"anthropic",async judgeAltText({imageUrl:n,alt:o,surroundingContext:r,signal:i}){const c=await oo(n,i);if(!c)return ro("Could not fetch image to verify alt text",0,t);const{text:d,maxTokens:l}=be(m=>m.altText,{alt:o?`"${o}"`:"(empty / missing)",surroundingContextLine:r?`
562
+ Nearby page context: ${r.slice(0,500)}`:""}),h=await se({apiKey:e.apiKey,model:t,maxTokens:l,signal:i,messages:[{role:"user",content:[{type:"image",source:{type:"base64",media_type:c.mediaType,data:c.data}},{type:"text",text:d}]}]});return ge(h,a)},async judgeHeading({headingText:n,sectionContent:o,signal:r}){const{text:i,maxTokens:c}=be(l=>l.heading,{headingText:n,sectionContent:o.slice(0,800)}),d=await se({apiKey:e.apiKey,model:t,maxTokens:c,signal:r,messages:[{role:"user",content:[{type:"text",text:i}]}]});return ge(d,a)},async judgeSensoryLanguage({instructionText:n,signal:o}){const{text:r,maxTokens:i}=be(d=>d.sensoryLanguage,{instructionText:n.slice(0,800)}),c=await se({apiKey:e.apiKey,model:t,maxTokens:i,signal:o,messages:[{role:"user",content:[{type:"text",text:r}]}]});return ge(c,a)},async judgeAriaSemantics({elementHtml:n,computedRole:o,surroundingHtml:r,signal:i}){const{text:c,maxTokens:d}=be(h=>h.ariaSemantics,{elementHtml:n.slice(0,600),computedRole:o,surroundingHtml:r.slice(0,500)}),l=await se({apiKey:e.apiKey,model:t,maxTokens:d,signal:i,messages:[{role:"user",content:[{type:"text",text:c}]}]});return ge(l,a)},async judgeImageOfText({imageUrl:n,accessibleName:o,signal:r}){const i=await oo(n,r);if(!i)return ro("Could not fetch image to inspect for embedded text",0,t);const{text:c,maxTokens:d}=be(h=>h.imageOfText,{accessibleNameLine:o?`
563
+ Accessible name (alt / aria-label) of this image: "${o.slice(0,200)}"`:""}),l=await se({apiKey:e.apiKey,model:t,maxTokens:d,signal:r,messages:[{role:"user",content:[{type:"image",source:{type:"base64",media_type:i.mediaType,data:i.data}},{type:"text",text:c}]}]});return ge(l,a)},async judgeColorOnlyMeaning({elementHtml:n,contextDescription:o,signal:r}){const{text:i,maxTokens:c}=be(l=>l.colorOnlyMeaning,{elementHtml:n.slice(0,800),contextDescription:o.slice(0,400)}),d=await se({apiKey:e.apiKey,model:t,maxTokens:c,signal:r,messages:[{role:"user",content:[{type:"text",text:i}]}]});return ge(d,a)},async judgeGenericLinkText({linkText:n,surroundingText:o,signal:r}){const{text:i,maxTokens:c}=be(l=>l.genericLinkText,{linkText:n.slice(0,200),surroundingText:o.slice(0,800)}),d=await se({apiKey:e.apiKey,model:t,maxTokens:c,signal:r,messages:[{role:"user",content:[{type:"text",text:i}]}]});return ge(d,a)},async judgeWallOfText({blockText:n,structuralChildrenCount:o,signal:r}){const{text:i,maxTokens:c}=be(l=>l.wallOfText,{blockText:n.slice(0,1200),structuralChildrenCount:String(o)}),d=await se({apiKey:e.apiKey,model:t,maxTokens:c,signal:r,messages:[{role:"user",content:[{type:"text",text:i}]}]});return ge(d,a)},async judgeLanguageMismatch({declaredLang:n,bodyTextSample:o,signal:r}){const{text:i,maxTokens:c}=be(l=>l.languageMismatch,{declaredLang:n||"(none / empty)",bodyTextSample:o.slice(0,1200)}),d=await se({apiKey:e.apiKey,model:t,maxTokens:c,signal:r,messages:[{role:"user",content:[{type:"text",text:i}]}]});return ge(d,a)},async suggestColorFix(n){const o=await se({apiKey:e.apiKey,model:t,maxTokens:600,messages:[{role:"user",content:[{type:"text",text:Zc(n)}]}]});return tl(o,a)},async generateExecutiveSummary(n){const o=await se({apiKey:e.apiKey,model:t,maxTokens:800,messages:[{role:"user",content:[{type:"text",text:el(n)}]}]});return ol(o,a)},async resolveAxeIncomplete(n){const o=await se({apiKey:e.apiKey,model:t,maxTokens:400,signal:n.signal,messages:[{role:"user",content:[{type:"text",text:al(n)}]}]});return nl(o,a,t)},async judgeFocusVisible({pageUrl:n,samples:o,signal:r}){const i=[];for(const h of o){const m=/^data:(image\/[a-z]+);base64,(.+)$/i.exec(h.screenshot);m&&(i.push({type:"image",source:{type:"base64",media_type:m[1],data:m[2]}}),i.push({type:"text",text:`Image #${h.ordinal} — selector: ${h.selector} — role: ${h.role||"(none)"} — name: "${h.name||"(no name)"}" — rect: x=${Math.round(h.rect.x)} y=${Math.round(h.rect.y)} w=${Math.round(h.rect.width)} h=${Math.round(h.rect.height)} — outline: ${h.focusStyle.outlineStyle} ${h.focusStyle.outlineWidth} ${h.focusStyle.outlineColor} (offset ${h.focusStyle.outlineOffset}) — box-shadow: ${h.focusStyle.boxShadow||"none"}`}))}const{text:c,maxTokens:d}=be(h=>h.focusVisible,{pageUrl:n,sampleCount:String(o.length)});i.push({type:"text",text:c});const l=await se({apiKey:e.apiKey,model:t,maxTokens:d,signal:r,messages:[{role:"user",content:i}]});return ge(l,a)},async judgeConsistencyDivergence({wcagCriterion:n,ruleId:o,description:r,details:i,pages:c,signal:d}){const l=Xc(n,o,r,i,c),h=await se({apiKey:e.apiKey,model:t,maxTokens:500,signal:d,messages:[{role:"user",content:[{type:"text",text:l}]}]});return ge(h,a)},async judgeKeyboardTrap({pageUrl:n,forwardSequence:o,reverseSequence:r,stuckRuns:i,reverseMismatches:c,previousVerdict:d,signal:l}){const h=o.map((b,A)=>` ${A+1}. ${b}`).join(`
564
+ `)||" (no tabbable elements found)",m=r.map((b,A)=>` ${A+1}. ${b}`).join(`
557
565
  `)||" (Shift-Tab moved to body / no reverse path)",u=i.length===0?" (none detected)":i.map(b=>` - ${b}`).join(`
558
566
  `),g=c.length===0?" (none — Shift-Tab cleanly reversed the forward order)":c.map(b=>` - position ${b.index}: expected ${b.expected}, got ${b.actual}`).join(`
559
- `),{text:f,maxTokens:p}=ge(b=>b.keyboardTrap,{pageUrl:n,anchor:Nt(b=>b.keyboardTrap,d),forwardSequence:h,reverseSequence:m,stuckRuns:u,reverseMismatches:g}),y=await ie({apiKey:e.apiKey,model:t,maxTokens:p,signal:l,messages:[{role:"user",content:[{type:"text",text:f}]}]});return pe(y,a)},async judgeNonTextContrast({pageUrl:n,pageScreenshot:o,previousVerdict:r,signal:i}){const{text:c,maxTokens:d}=ge(u=>u.nonTextContrast,{pageUrl:n,anchor:Nt(u=>u.nonTextContrast,r)}),l=[],h=/^data:(image\/[a-z]+);base64,(.+)$/i.exec(o);h&&l.push({type:"image",source:{type:"base64",media_type:h[1],data:h[2]}}),l.push({type:"text",text:c});const m=await ie({apiKey:e.apiKey,model:t,maxTokens:d,signal:i,messages:[{role:"user",content:l}]});return pe(m,a)},async judgeReadingOrder({pageUrl:n,pageScreenshot:o,flaggedIssues:r,previousVerdict:i,signal:c}){const d=r.map(g=>` - DOM #${g.domIndex} → Visual #${g.visualIndex} | selector ${g.selector} | at (x=${Math.round(g.rect.x)}, y=${Math.round(g.rect.y)}, w=${Math.round(g.rect.w)}, h=${Math.round(g.rect.h)}) | text: "${g.textSnippet.slice(0,80).replace(/"/g,"'")}"`).join(`
560
- `),{text:l,maxTokens:h}=ge(g=>g.readingOrder,{pageUrl:n,anchor:Nt(g=>g.readingOrder,i),flaggedCount:String(r.length),issuesList:d}),m=[];if(o){const g=/^data:(image\/[a-z]+);base64,(.+)$/i.exec(o);g&&m.push({type:"image",source:{type:"base64",media_type:g[1],data:g[2]}})}m.push({type:"text",text:l});const u=await ie({apiKey:e.apiKey,model:t,maxTokens:h,signal:c,messages:[{role:"user",content:m}]});return pe(u,a)},async judgeFocusOrder({pageUrl:n,pageScreenshot:o,tabSequence:r,previousVerdict:i,signal:c}){const{text:d,maxTokens:l}=ge(u=>u.focusOrder,{pageUrl:n,anchor:Nt(u=>u.focusOrder,i),rowBucketPx:String(Jt),seq:eo(r)}),h=[];if(o){const u=/^data:(image\/[a-z]+);base64,(.+)$/i.exec(o);u&&h.push({type:"image",source:{type:"base64",media_type:u[1],data:u[2]}})}h.push({type:"text",text:d});const m=await ie({apiKey:e.apiKey,model:t,maxTokens:l,signal:c,messages:[{role:"user",content:h}]});return pe(m,a)},async judgeLabelInName({pageUrl:n,pageScreenshot:o,candidates:r,signal:i}){const c=r.map((u,g)=>`${g+1}. [${u.role}] selector=${u.selector}
567
+ `),{text:p,maxTokens:f}=be(b=>b.keyboardTrap,{pageUrl:n,anchor:Ut(b=>b.keyboardTrap,d),forwardSequence:h,reverseSequence:m,stuckRuns:u,reverseMismatches:g}),y=await se({apiKey:e.apiKey,model:t,maxTokens:f,signal:l,messages:[{role:"user",content:[{type:"text",text:p}]}]});return ge(y,a)},async judgeNonTextContrast({pageUrl:n,pageScreenshot:o,previousVerdict:r,signal:i}){const{text:c,maxTokens:d}=be(u=>u.nonTextContrast,{pageUrl:n,anchor:Ut(u=>u.nonTextContrast,r)}),l=[],h=/^data:(image\/[a-z]+);base64,(.+)$/i.exec(o);h&&l.push({type:"image",source:{type:"base64",media_type:h[1],data:h[2]}}),l.push({type:"text",text:c});const m=await se({apiKey:e.apiKey,model:t,maxTokens:d,signal:i,messages:[{role:"user",content:l}]});return ge(m,a)},async judgeReadingOrder({pageUrl:n,pageScreenshot:o,flaggedIssues:r,previousVerdict:i,signal:c}){const d=r.map(g=>` - DOM #${g.domIndex} → Visual #${g.visualIndex} | selector ${g.selector} | at (x=${Math.round(g.rect.x)}, y=${Math.round(g.rect.y)}, w=${Math.round(g.rect.w)}, h=${Math.round(g.rect.h)}) | text: "${g.textSnippet.slice(0,80).replace(/"/g,"'")}"`).join(`
568
+ `),{text:l,maxTokens:h}=be(g=>g.readingOrder,{pageUrl:n,anchor:Ut(g=>g.readingOrder,i),flaggedCount:String(r.length),issuesList:d}),m=[];if(o){const g=/^data:(image\/[a-z]+);base64,(.+)$/i.exec(o);g&&m.push({type:"image",source:{type:"base64",media_type:g[1],data:g[2]}})}m.push({type:"text",text:l});const u=await se({apiKey:e.apiKey,model:t,maxTokens:h,signal:c,messages:[{role:"user",content:m}]});return ge(u,a)},async judgeFocusOrder({pageUrl:n,pageScreenshot:o,tabSequence:r,previousVerdict:i,signal:c}){const{text:d,maxTokens:l}=be(u=>u.focusOrder,{pageUrl:n,anchor:Ut(u=>u.focusOrder,i),rowBucketPx:String(Zt),seq:ao(r)}),h=[];if(o){const u=/^data:(image\/[a-z]+);base64,(.+)$/i.exec(o);u&&h.push({type:"image",source:{type:"base64",media_type:u[1],data:u[2]}})}h.push({type:"text",text:d});const m=await se({apiKey:e.apiKey,model:t,maxTokens:l,signal:c,messages:[{role:"user",content:h}]});return ge(m,a)},async judgeLabelInName({pageUrl:n,pageScreenshot:o,candidates:r,signal:i}){const c=r.map((u,g)=>`${g+1}. [${u.role}] selector=${u.selector}
561
569
  visibleText: "${u.visibleText}"
562
570
  accessibleName: "${u.accessibleName}"`).join(`
563
- `),{text:d,maxTokens:l}=ge(u=>u.labelInName,{pageUrl:n,candidatesList:c}),h=[];if(o){const u=/^data:(image\/[a-z]+);base64,(.+)$/i.exec(o);u&&h.push({type:"image",source:{type:"base64",media_type:u[1],data:u[2]}})}h.push({type:"text",text:d});const m=await ie({apiKey:e.apiKey,model:t,maxTokens:l,signal:i,messages:[{role:"user",content:h}]});return pe(m,a)},async judgeFocusOrderFallback({pageUrl:n,pageScreenshot:o,tabSequence:r,primaryUncertainReason:i,signal:c}){const{text:d,maxTokens:l}=ge(u=>u.focusOrderFallback,{pageUrl:n,primaryUncertainReason:i.replace(/"/g,"'").slice(0,600),rowBucketPx:String(Jt),seq:eo(r)}),h=[];if(o){const u=/^data:(image\/[a-z]+);base64,(.+)$/i.exec(o);u&&h.push({type:"image",source:{type:"base64",media_type:u[1],data:u[2]}})}h.push({type:"text",text:d});const m=await ie({apiKey:e.apiKey,model:t,maxTokens:l,signal:c,messages:[{role:"user",content:h}]});return pe(m,a)}}}function eo(e){return Gc(e).map(a=>{const n=a.items.map(o=>` ${o.ordinal}. [${o.role||o.selector.split(" ").pop()||"?"}] "${o.name||"(no name)"}" — selector ${o.selector} — x=${Math.round(o.rect.x)}, w=${Math.round(o.rect.width)}`).join(`
564
- `);return` Row ~y=${a.row*Jt} (${a.items.length} item${a.items.length===1?"":"s"}):
571
+ `),{text:d,maxTokens:l}=be(u=>u.labelInName,{pageUrl:n,candidatesList:c}),h=[];if(o){const u=/^data:(image\/[a-z]+);base64,(.+)$/i.exec(o);u&&h.push({type:"image",source:{type:"base64",media_type:u[1],data:u[2]}})}h.push({type:"text",text:d});const m=await se({apiKey:e.apiKey,model:t,maxTokens:l,signal:i,messages:[{role:"user",content:h}]});return ge(m,a)},async judgeFocusOrderFallback({pageUrl:n,pageScreenshot:o,tabSequence:r,primaryUncertainReason:i,signal:c}){const{text:d,maxTokens:l}=be(u=>u.focusOrderFallback,{pageUrl:n,primaryUncertainReason:i.replace(/"/g,"'").slice(0,600),rowBucketPx:String(Zt),seq:ao(r)}),h=[];if(o){const u=/^data:(image\/[a-z]+);base64,(.+)$/i.exec(o);u&&h.push({type:"image",source:{type:"base64",media_type:u[1],data:u[2]}})}h.push({type:"text",text:d});const m=await se({apiKey:e.apiKey,model:t,maxTokens:l,signal:c,messages:[{role:"user",content:h}]});return ge(m,a)}}}function ao(e){return Qc(e).map(a=>{const n=a.items.map(o=>` ${o.ordinal}. [${o.role||o.selector.split(" ").pop()||"?"}] "${o.name||"(no name)"}" — selector ${o.selector} — x=${Math.round(o.rect.x)}, w=${Math.round(o.rect.width)}`).join(`
572
+ `);return` Row ~y=${a.row*Zt} (${a.items.length} item${a.items.length===1?"":"s"}):
565
573
  ${n}`}).join(`
566
- `)}class It extends Error{constructor(){super("Anthropic API: canceled by caller"),this.name="ExternalAbortError"}}const Mr=3e4,to=3,Lc=1e4;function Pc(e){return e===429||e>=500}function Nc(e){return e instanceof Error?e.name==="TimeoutError"||e.name==="AbortError"||e instanceof TypeError:!1}function Fc(e,t){if(!t)return e;if(typeof AbortSignal.any=="function")return AbortSignal.any([e,t]);const a=new AbortController,n=()=>a.abort();return e.aborted?a.abort():e.addEventListener("abort",n,{once:!0}),t.aborted?a.abort():t.addEventListener("abort",n,{once:!0}),a.signal}async function _c(e){var o,r;const t=AbortSignal.timeout(Mr),a=Fc(t,e.signal);let n;try{n=await fetch(Oc,{method:"POST",headers:{"x-api-key":e.apiKey,"anthropic-version":Mc,"content-type":"application/json","anthropic-dangerous-direct-browser-access":"true"},body:JSON.stringify({model:e.model,max_tokens:e.maxTokens,messages:e.messages,temperature:e.temperature??0}),signal:a})}catch(i){throw(o=e.signal)!=null&&o.aborted?new It:i}if(!n.ok){const c=((r=(await n.json().catch(()=>({}))).error)==null?void 0:r.message)??`${n.status} ${n.statusText}`,d=new Error(`Anthropic API: ${c}`);throw d.status=n.status,d}return await n.json()}async function ie(e){var a;let t=null;for(let n=1;n<=to;n++){if((a=e.signal)!=null&&a.aborted)throw new It;try{return await _c(e)}catch(o){if(o instanceof It)throw o;const r=o.status;if(!(Nc(o)||typeof r=="number"&&Pc(r))||n===to)throw o instanceof Error&&(o.name==="TimeoutError"||o.name==="AbortError")?new Error(`Anthropic API: timed out after ${Mr/1e3}s on each of ${n} attempt${n===1?"":"s"}`):o;t=o instanceof Error?o:new Error(String(o)),await Uc(n*1e3,e.signal)}}throw t??new Error("Anthropic API: all retries failed")}function Uc(e,t){return new Promise((a,n)=>{if(t!=null&&t.aborted){n(new It);return}const o=setTimeout(()=>{t==null||t.removeEventListener("abort",r),a()},e),r=()=>{clearTimeout(o),n(new It)};t==null||t.addEventListener("abort",r,{once:!0})})}async function ao(e,t){var r,i;const a=new AbortController,n=setTimeout(()=>a.abort(),Lc),o=()=>a.abort();t&&(t.aborted?a.abort():t.addEventListener("abort",o,{once:!0}));try{const c=await fetch(e,{signal:a.signal});if(!c.ok)return null;const d=((i=(r=c.headers.get("content-type"))==null?void 0:r.split(";")[0])==null?void 0:i.trim())??"image/png";if(!d.startsWith("image/")||d!=="image/jpeg"&&d!=="image/png"&&d!=="image/gif"&&d!=="image/webp")return null;const l=await c.arrayBuffer();let h="";const m=new Uint8Array(l),u=32768;for(let g=0;g<m.length;g+=u)h+=String.fromCharCode(...m.slice(g,g+u));return{data:btoa(h),mediaType:d}}catch{return null}finally{clearTimeout(n),t&&t.removeEventListener("abort",o)}}function pe(e,t){var r;const a=((r=e.content.find(i=>i.type==="text"))==null?void 0:r.text)??"",n=Wc(a),o=e.usage.input_tokens*t.input+e.usage.output_tokens*t.output;return{verdict:n.verdict,reasoning:n.reasoning,confidence:n.confidence,costUsd:o,model:e.model}}function Wc(e){const a=e.replace(/```(?:json)?/g,"").trim().match(/\{[\s\S]*\}/);if(!a)return{verdict:"uncertain",reasoning:"Model returned no parseable JSON",confidence:0};try{const n=JSON.parse(a[0]),o=n.verdict==="pass"||n.verdict==="fail"?n.verdict:"uncertain",r=(n.reasoning??"").trim().slice(0,600),i=typeof n.confidence=="number"?n.confidence:.5,c=Math.max(0,Math.min(1,i));return{verdict:o,reasoning:r,confidence:c}}catch{return{verdict:"uncertain",reasoning:"Model returned invalid JSON",confidence:0}}}function no(e,t,a){return{verdict:"uncertain",reasoning:e,confidence:0,costUsd:t,model:a}}const Vc=`
574
+ `)}class Ct extends Error{constructor(){super("Anthropic API: canceled by caller"),this.name="ExternalAbortError"}}const Pr=3e4,no=3,jc=1e4;function Hc(e){return e===429||e>=500}function Bc(e){return e instanceof Error?e.name==="TimeoutError"||e.name==="AbortError"||e instanceof TypeError:!1}function qc(e,t){if(!t)return e;if(typeof AbortSignal.any=="function")return AbortSignal.any([e,t]);const a=new AbortController,n=()=>a.abort();return e.aborted?a.abort():e.addEventListener("abort",n,{once:!0}),t.aborted?a.abort():t.addEventListener("abort",n,{once:!0}),a.signal}async function zc(e){var o,r;const t=AbortSignal.timeout(Pr),a=qc(t,e.signal);let n;try{n=await fetch(Wc,{method:"POST",headers:{"x-api-key":e.apiKey,"anthropic-version":Gc,"content-type":"application/json","anthropic-dangerous-direct-browser-access":"true"},body:JSON.stringify({model:e.model,max_tokens:e.maxTokens,messages:e.messages,temperature:e.temperature??0}),signal:a})}catch(i){throw(o=e.signal)!=null&&o.aborted?new Ct:i}if(!n.ok){const c=((r=(await n.json().catch(()=>({}))).error)==null?void 0:r.message)??`${n.status} ${n.statusText}`,d=new Error(`Anthropic API: ${c}`);throw d.status=n.status,d}return await n.json()}async function se(e){var a;let t=null;for(let n=1;n<=no;n++){if((a=e.signal)!=null&&a.aborted)throw new Ct;try{return await zc(e)}catch(o){if(o instanceof Ct)throw o;const r=o.status;if(!(Bc(o)||typeof r=="number"&&Hc(r))||n===no)throw o instanceof Error&&(o.name==="TimeoutError"||o.name==="AbortError")?new Error(`Anthropic API: timed out after ${Pr/1e3}s on each of ${n} attempt${n===1?"":"s"}`):o;t=o instanceof Error?o:new Error(String(o)),await Kc(n*1e3,e.signal)}}throw t??new Error("Anthropic API: all retries failed")}function Kc(e,t){return new Promise((a,n)=>{if(t!=null&&t.aborted){n(new Ct);return}const o=setTimeout(()=>{t==null||t.removeEventListener("abort",r),a()},e),r=()=>{clearTimeout(o),n(new Ct)};t==null||t.addEventListener("abort",r,{once:!0})})}async function oo(e,t){var r,i;const a=new AbortController,n=setTimeout(()=>a.abort(),jc),o=()=>a.abort();t&&(t.aborted?a.abort():t.addEventListener("abort",o,{once:!0}));try{const c=await fetch(e,{signal:a.signal});if(!c.ok)return null;const d=((i=(r=c.headers.get("content-type"))==null?void 0:r.split(";")[0])==null?void 0:i.trim())??"image/png";if(!d.startsWith("image/")||d!=="image/jpeg"&&d!=="image/png"&&d!=="image/gif"&&d!=="image/webp")return null;const l=await c.arrayBuffer();let h="";const m=new Uint8Array(l),u=32768;for(let g=0;g<m.length;g+=u)h+=String.fromCharCode(...m.slice(g,g+u));return{data:btoa(h),mediaType:d}}catch{return null}finally{clearTimeout(n),t&&t.removeEventListener("abort",o)}}function ge(e,t){var r;const a=((r=e.content.find(i=>i.type==="text"))==null?void 0:r.text)??"",n=Yc(a),o=e.usage.input_tokens*t.input+e.usage.output_tokens*t.output;return{verdict:n.verdict,reasoning:n.reasoning,confidence:n.confidence,costUsd:o,model:e.model}}function Yc(e){const a=e.replace(/```(?:json)?/g,"").trim().match(/\{[\s\S]*\}/);if(!a)return{verdict:"uncertain",reasoning:"Model returned no parseable JSON",confidence:0};try{const n=JSON.parse(a[0]),o=n.verdict==="pass"||n.verdict==="fail"?n.verdict:"uncertain",r=(n.reasoning??"").trim().slice(0,600),i=typeof n.confidence=="number"?n.confidence:.5,c=Math.max(0,Math.min(1,i));return{verdict:o,reasoning:r,confidence:c}}catch{return{verdict:"uncertain",reasoning:"Model returned invalid JSON",confidence:0}}}function ro(e,t,a){return{verdict:"uncertain",reasoning:e,confidence:0,costUsd:t,model:a}}const Jc=`
567
575
  Respond ONLY with a single JSON object — no preamble, no markdown:
568
576
  { "verdict": "pass" | "fail" | "uncertain", "reasoning": "1-3 sentences", "confidence": 0.0-1.0 }
569
- `.trim();function jc(e,t,a,n,o){const r=o.map((c,d)=>` ${d+1}. ${c.url}
577
+ `.trim();function Xc(e,t,a,n,o){const r=o.map((c,d)=>` ${d+1}. ${c.url}
570
578
  ${c.evidence}`).join(`
571
579
  `);return`You are an accessibility auditor evaluating whether a heuristic finding is a real WCAG violation or a false positive.
572
580
 
@@ -600,7 +608,7 @@ Verdict mapping:
600
608
  - "fail" = real divergence. This IS a WCAG ${e} violation and should be reported to the user.
601
609
  - "uncertain" = the evidence is genuinely ambiguous; surface the finding so a human can decide.
602
610
 
603
- ${Vc}`}const Jt=32;function Gc(e){const t=[];let a=null;for(const n of e){const o=Math.round(n.rect.y/Jt);a===null||a.row!==o?(a={row:o,items:[n]},t.push(a)):a.items.push(n)}return t}function Hc(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.
611
+ ${Jc}`}const Zt=32;function Qc(e){const t=[];let a=null;for(const n of e){const o=Math.round(n.rect.y/Zt);a===null||a.row!==o?(a={row:o,items:[n]},t.push(a)):a.items.push(n)}return t}function Zc(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.
604
612
 
605
613
  Current pair (FAILING):
606
614
  - Foreground: ${e.foreground}
@@ -627,7 +635,7 @@ Respond ONLY with a JSON object — no preamble, no markdown:
627
635
  { "foreground": "#hexHEX", "background": "#hexHEX", "contrast": 4.6, "rationale": "1 sentence" }
628
636
  ],
629
637
  "reasoning": "1-3 sentences explaining your overall approach or why no suggestions are possible"
630
- }`}function Bc(e){return`You are an accessibility auditor writing the executive summary for an automated audit report.
638
+ }`}function el(e){return`You are an accessibility auditor writing the executive summary for an automated audit report.
631
639
 
632
640
  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"}.
633
641
  Framing: ${e.framing} report.
@@ -658,7 +666,7 @@ Respond ONLY with a JSON object — no preamble, no markdown:
658
666
  "lead": "2-3 sentence headline framing",
659
667
  "body": "2-3 paragraphs of risk explanation + audit-date framing + ongoing-diligence context if applicable. ~150-300 words total.",
660
668
  "closer": "1-2 sentence closing line"
661
- }`}function zc(e,t){var i;const o=(((i=e.content.find(c=>c.type==="text"))==null?void 0:i.text)??"").replace(/```(?:json)?/g,"").trim().match(/\{[\s\S]*\}/),r=e.usage.input_tokens*t.input+e.usage.output_tokens*t.output;if(!o)return{verdict:"error",candidates:[],reasoning:"Model returned no parseable JSON",costUsd:r,model:e.model};try{const c=JSON.parse(o[0]),d=(c.candidates??[]).slice(0,3).map(l=>({foreground:typeof l.foreground=="string"?l.foreground:"",background:typeof l.background=="string"?l.background:"",contrast:typeof l.contrast=="number"?l.contrast:0,rationale:typeof l.rationale=="string"?l.rationale:""})).filter(l=>l.foreground&&l.background);return{verdict:c.verdict==="suggested"&&d.length>0?"suggested":"no-suggestion",candidates:d,reasoning:typeof c.reasoning=="string"?c.reasoning:"",costUsd:r,model:e.model}}catch{return{verdict:"error",candidates:[],reasoning:"Could not parse color suggestions",costUsd:r,model:e.model}}}function qc(e){var n,o,r,i,c,d;const t=(n=e.element.styles)==null?void 0:n.effectiveBackground,a=e.ruleId==="color-contrast"||e.ruleId==="color-contrast-enhanced"?`
669
+ }`}function tl(e,t){var i;const o=(((i=e.content.find(c=>c.type==="text"))==null?void 0:i.text)??"").replace(/```(?:json)?/g,"").trim().match(/\{[\s\S]*\}/),r=e.usage.input_tokens*t.input+e.usage.output_tokens*t.output;if(!o)return{verdict:"error",candidates:[],reasoning:"Model returned no parseable JSON",costUsd:r,model:e.model};try{const c=JSON.parse(o[0]),d=(c.candidates??[]).slice(0,3).map(l=>({foreground:typeof l.foreground=="string"?l.foreground:"",background:typeof l.background=="string"?l.background:"",contrast:typeof l.contrast=="number"?l.contrast:0,rationale:typeof l.rationale=="string"?l.rationale:""})).filter(l=>l.foreground&&l.background);return{verdict:c.verdict==="suggested"&&d.length>0?"suggested":"no-suggestion",candidates:d,reasoning:typeof c.reasoning=="string"?c.reasoning:"",costUsd:r,model:e.model}}catch{return{verdict:"error",candidates:[],reasoning:"Could not parse color suggestions",costUsd:r,model:e.model}}}function al(e){var n,o,r,i,c,d;const t=(n=e.element.styles)==null?void 0:n.effectiveBackground,a=e.ruleId==="color-contrast"||e.ruleId==="color-contrast-enhanced"?`
662
670
  This is a COLOR-CONTRAST evaluation. axe-core couldn't determine the
663
671
  contrast ratio (commonly because the element's own background-color
664
672
  is transparent and axe didn't walk the DOM cascade to find the
@@ -731,7 +739,7 @@ Respond with strict JSON (no markdown fences):
731
739
  {
732
740
  "verdict": "pass" | "fail" | "uncertain",
733
741
  "reasoning": "one or two sentences explaining the determination, citing the contrast ratio for color-contrast"
734
- }`}function Kc(e,t,a){var c;const n=((c=e.content.find(d=>d.type==="text"))==null?void 0:c.text)??"",r=n.replace(/```(?:json)?/g,"").trim().match(/\{[\s\S]*\}/),i=e.usage.input_tokens*t.input+e.usage.output_tokens*t.output;if(!r)return{verdict:"uncertain",reasoning:n||"AI returned no parseable response.",costUsd:i,model:e.model||a};try{const d=JSON.parse(r[0]);return{verdict:d.verdict==="pass"||d.verdict==="fail"?d.verdict:"uncertain",reasoning:typeof d.reasoning=="string"&&d.reasoning?d.reasoning:"AI returned no rationale.",costUsd:i,model:e.model||a}}catch{return{verdict:"uncertain",reasoning:n,costUsd:i,model:e.model||a}}}function Yc(e,t){var i;const a=((i=e.content.find(c=>c.type==="text"))==null?void 0:i.text)??"",o=a.replace(/```(?:json)?/g,"").trim().match(/\{[\s\S]*\}/),r=e.usage.input_tokens*t.input+e.usage.output_tokens*t.output;if(!o)return{lead:"",body:a||"Summary generation failed.",closer:"",costUsd:r,model:e.model};try{const c=JSON.parse(o[0]);return{lead:typeof c.lead=="string"?c.lead:"",body:typeof c.body=="string"?c.body:"",closer:typeof c.closer=="string"?c.closer:"",costUsd:r,model:e.model}}catch{return{lead:"",body:a,closer:"",costUsd:r,model:e.model}}}function ue(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:Dc({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}`}}}const gt=te("consistency-ai-suppression");async function Jc(e,t={}){var h;if(e.length===0)return{emitted:[],suppressed:0,uncertain:0,apiCalls:0};const a=await de();if(!le(a,"aiConsistencySuppression"))return{emitted:e,suppressed:0,uncertain:0,apiCalls:0};let n;try{const m=await chrome.storage.local.get("aiConfig");n=ve(m.aiConfig)}catch{return gt.debug("aiConfig load failed; emitting heuristic findings unchanged"),{emitted:e,suppressed:0,uncertain:0,apiCalls:0}}const o=ue(n);if(!o.ok)return gt.info(`AI client unavailable (${o.reason}); emitting heuristic findings unchanged`),{emitted:e,suppressed:0,uncertain:0,apiCalls:0};const r=o.client;if(typeof r.judgeConsistencyDivergence!="function")return gt.info("AI client does not support judgeConsistencyDivergence; emitting heuristic findings unchanged"),{emitted:e,suppressed:0,uncertain:0,apiCalls:0};const i=[];let c=0,d=0,l=0;for(const m of e){if((h=t.signal)!=null&&h.aborted){i.push(m);continue}try{const u=await r.judgeConsistencyDivergence({wcagCriterion:m.wcagCriterion,ruleId:m.ruleId,description:m.description,details:m.details,pages:m.pages,signal:t.signal});if(l++,u.verdict==="pass"){c++;continue}if(u.verdict==="fail"){i.push({...m,aiVerdict:{verdict:"real-divergence",confidence:u.confidence??0,reasoning:u.reasoning??""}});continue}d++,i.push({...m,aiVerdict:{verdict:"uncertain",confidence:u.confidence??0,reasoning:u.reasoning??""}})}catch(u){gt.warn(`AI suppression failed for ${m.ruleId} ${m.wcagCriterion}: ${u instanceof Error?u.message:String(u)}`),i.push(m)}}return gt.info(`consistency AI suppression: ${l} API calls, ${c} false-positives suppressed, ${d} uncertain, ${i.length} emitted`),{emitted:i,suppressed:c,uncertain:d,apiCalls:l}}function Xc(e){const t=new Map(e.breakpointPresets.map(n=>[n.id,n])),a=[];for(const n of e.breakpoints){const o=t.get(n);if(o)for(const r of e.directions)for(const i of e.themes){const c=e.ariaVariations.length===0?[null]:e.ariaVariations;for(const d of c)for(const l of e.pseudoStates)a.push({pseudoState:l,ariaVariation:d,theme:i,direction:r,breakpoint:o})}}return a}const Dr=["color-contrast","color-contrast-enhanced","link-in-text-block"],Qc=["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(Dr),t=Qc.filter(a=>e.has(a));t.length>0&&console.error("axe-rule-classification: rule(s) classified as BOTH state-dependent and markup-eligible:",t)}const Zc=te("chrome-api"),el="1.3",oo=3e4;class tl extends Error{constructor(t,a){super(`CDP ${t} timed out after ${a}ms`),this.method=t,this.timeoutMs=a,this.name="CdpTimeoutError"}}class Lr{constructor(t){this.target=t}async attach(){await chrome.debugger.attach(this.target,el)}async detach(){try{await chrome.debugger.detach(this.target)}catch(t){Zc.debug("detach soft-failure",t)}}async send(t,a){const n=chrome.debugger.sendCommand(this.target,t,a);let o;const r=new Promise((i,c)=>{o=setTimeout(()=>c(new tl(t,oo)),oo)});try{return await Promise.race([n,r])}finally{o!==void 0&&clearTimeout(o)}}}function al(e){const t=e instanceof Error?e.message:String(e);return/Another debugger is already attached|Cannot attach to/i.test(t)}function Pr(e){const t=e instanceof Error?e.message:String(e);return al(e)?Un("DEBUGGER_BUSY","DevTools is open on this tab. Close it to run the audit.",!0,{raw:t}):Un("STATE_DRIVE_FAILED",t,!1)}function nl(e){const t=(a,n)=>e(a,n);return chrome.debugger.onDetach.addListener(t),()=>chrome.debugger.onDetach.removeListener(t)}const be=te("state-driver"),ol=60*1e3,De=new Map,Xt=new Map,ct=new Map,ot=new Map,rl='button, a[href], input, textarea, select, summary, [tabindex]:not([tabindex="-1"]), [role="button"], [role="link"], [role="checkbox"], [role="radio"], [role="tab"], [role="menuitem"], [role="switch"], [role="combobox"], [role="option"]',xt=new Map,il={default:[],hover:["hover"],focus:["focus"],"focus-visible":["focus","focus-visible"],active:["active"],disabled:[]};async function Nr(e){let t=De.get(e);if(!t){t=new Lr({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(a){try{await t.detach()}catch{}throw a}De.set(e,t),ct.set(e,new Map),sl(e),be.info("attached",{tabId:e})}return ll(e),t}function sl(e){const t=(a,n,o)=>{var i,c;if(a.tabId!==e)return;const r=ct.get(e);if(r)if(n==="Runtime.executionContextCreated"){const d=o,l=d==null?void 0:d.context;l&&((i=l.auxData)!=null&&i.isDefault)&&((c=l.auxData)!=null&&c.frameId)&&r.set(l.auxData.frameId,l.id)}else if(n==="Runtime.executionContextDestroyed"){const d=o,l=d==null?void 0:d.executionContextId;if(l!==void 0)for(const[h,m]of r.entries())m===l&&r.delete(h)}else n==="Runtime.executionContextsCleared"&&r.clear()};chrome.debugger.onEvent.addListener(t)}async function cl(e){const t=De.get(e);if(De.delete(e),ct.delete(e),ot.delete(e),xt.delete(e),t)try{await t.detach()}catch{}}function ll(e){const t=Xt.get(e);t&&clearTimeout(t),Xt.set(e,setTimeout(()=>{va(e).catch(a=>be.warn("idle detach failed",a))},ol))}async function va(e){const t=De.get(e);t&&(await t.detach(),De.delete(e)),ct.delete(e),ot.delete(e),xt.delete(e);const a=Xt.get(e);a&&(clearTimeout(a),Xt.delete(e))}function Fr(e){const t=[{id:e.frame.id,url:e.frame.url}];for(const a of e.childFrames??[])t.push(...Fr(a));return t}async function dl(e,t,a){var r;if(!a||a===0)return;const n=ct.get(t);if(!n)return;let o;try{o=(r=(await chrome.scripting.executeScript({target:{tabId:t,frameIds:[a]},func:()=>location.href}))[0])==null?void 0:r.result}catch(i){be.debug("frame URL lookup failed",i)}try{const i=await e.send("Page.getFrameTree"),c=Fr(i.frameTree);if(o){const h=c.find(m=>m.url===o);if(h){const m=n.get(h.id);if(m!==void 0)return m}}const d=i.frameTree.frame.id,l=c.find(h=>h.id!==d);return l?n.get(l.id):void 0}catch(i){be.warn("Page.getFrameTree failed",i);return}}async function ul(e,t,a){var i;const n={expression:`document.querySelector(${JSON.stringify(t)})`,objectGroup:"wcag-auditor",returnByValue:!1};a!==void 0&&(n.contextId=a);const r=(i=(await e.send("Runtime.evaluate",n)).result)==null?void 0:i.objectId;if(!r)return null;try{return(await e.send("DOM.requestNode",{objectId:r})).nodeId??null}finally{e.send("Runtime.releaseObject",{objectId:r}).catch(()=>{})}}async function _r(e){const t=ot.get(e);if(!t||t.length===0)return;const a=De.get(e);if(a){for(const n of t)try{await a.send("CSS.forcePseudoState",{nodeId:n,forcedPseudoClasses:[]})}catch(o){be.debug("forcePseudoState clear failed (node may be gone)",o)}ot.delete(e)}}async function hl(e,t){try{return(await e.send("DOM.querySelectorAll",{nodeId:t,selector:rl})).nodeIds??[]}catch(a){return be.debug("querySelectorAll for interactive descendants failed",a),[]}}async function nn(e,t,a,n){await chrome.scripting.executeScript({target:$n(e,t),func:(o,r)=>{const i=document.querySelector(o);i&&(r?i.setAttribute("disabled",""):i.removeAttribute("disabled"))},args:[a,n]})}async function Ur(e,t){await chrome.scripting.executeScript({target:$n(e,t),func:()=>{const a=document.activeElement;a&&typeof a.blur=="function"&&a.blur()}})}async function Wr(e,t){await chrome.scripting.executeScript({target:{tabId:e,frameIds:[0]},func:a=>{document.documentElement.setAttribute("dir",a)},args:[t]})}async function pl(e,t,a,n){await chrome.scripting.executeScript({target:$n(e,t),func:(o,r)=>{const i=document.querySelector(o);if(i)for(const[c,d]of Object.entries(r))i.setAttribute(c,d)},args:[a,n]})}function $n(e,t){return t!==void 0&&t!==0?{tabId:e,frameIds:[t]}:{tabId:e}}async function fl(e,t,a,n){const o=await Nr(e),r=xt.get(e),i=(r==null?void 0:r.scope)===a&&(r==null?void 0:r.frameId)===n,c=t.ariaVariation?JSON.stringify(t.ariaVariation.attributes):"",d=!i||(r==null?void 0:r.pseudoState)!==t.pseudoState,l=!r||r.theme!==t.theme,h=!r||r.direction!==t.direction,m=!r||r.breakpointId!==t.breakpoint.id,u=!i||(r==null?void 0:r.ariaVariationKey)!==c;if(d&&(await _r(e),a)){t.pseudoState==="disabled"?await nn(e,n,a,!0):(await nn(e,n,a,!1),await Ur(e,n));const g=il[t.pseudoState];if(g.length>0){const f=await dl(o,e,n),p=await ul(o,a,f);if(p!==null){const y=await hl(o,p),b=[p,...y];for(const v of b)try{await o.send("CSS.forcePseudoState",{nodeId:v,forcedPseudoClasses:g})}catch(w){be.debug(`CSS.forcePseudoState failed for nodeId=${v}`,w)}ot.set(e,b),be.debug(`forced :${t.pseudoState} on ${b.length} node(s)`)}else be.debug(`could not resolve nodeId for ${a} (frameId=${n})`)}}if(l){const g=[{name:"prefers-reduced-motion",value:"reduce"}];t.theme==="dark"?(g.push({name:"prefers-color-scheme",value:"dark"}),g.push({name:"forced-colors",value:"none"})):t.theme==="light"?(g.push({name:"prefers-color-scheme",value:"light"}),g.push({name:"forced-colors",value:"none"})):t.theme==="forced-colors-active"?g.push({name:"forced-colors",value:"active"}):t.theme==="forced-colors-none"&&g.push({name:"forced-colors",value:"none"}),await o.send("Emulation.setEmulatedMedia",{features:g}),await new Promise(f=>setTimeout(f,50))}h&&await Wr(e,t.direction),m&&await o.send("Emulation.setDeviceMetricsOverride",{width:t.breakpoint.width,height:t.breakpoint.height,deviceScaleFactor:t.breakpoint.deviceScaleFactor,mobile:t.breakpoint.mobile}),u&&t.ariaVariation&&a&&await pl(e,n,a,t.ariaVariation.attributes),xt.set(e,{scope:a,frameId:n,pseudoState:t.pseudoState,theme:t.theme,direction:t.direction,breakpointId:t.breakpoint.id,ariaVariationKey:c})}async function gl(e,t,a){const n=De.get(e);if(n){if(await _r(e),xt.delete(e),t)try{await nn(e,a,t,!1),await Ur(e,a),await Wr(e,"ltr")}catch(o){be.warn("element-level reset partial failure",o)}try{await n.send("Emulation.setEmulatedMedia",{features:[]}),await n.send("Emulation.clearDeviceMetricsOverride")}catch(o){be.warn("reset partial failure",o)}await va(e)}}async function on(e,t,a,n){try{return await fl(e,t,a,n),q({type:"STATE_CHANGED_EVENT",tabId:e,currentState:t}),{type:"STATE_DRIVE_RESPONSE",success:!0,appliedState:t}}catch(o){return be.error("state drive failed",o),await cl(e),{type:"STATE_DRIVE_RESPONSE",success:!1,error:Pr(o)}}}async function Vt(e,t,a){await gl(e,t,a)}async function tp(e){await va(e);try{await chrome.debugger.detach({tabId:e})}catch{}}async function ml(e,t={}){try{const a=await Nr(e),n={format:"jpeg",quality:t.quality??75};t.fullPage&&(n.captureBeyondViewport=!0);const o=await a.send("Page.captureScreenshot",n);return o!=null&&o.data?`data:image/jpeg;base64,${o.data}`:null}catch(a){return be.debug("Page.captureScreenshot failed",a),null}}function ap(){var a;const e=[];if(e.push(Se("STATE_DRIVE_REQUEST",n=>on(n.tabId,n.state,n.scope,n.frameId))),e.push(Se("STATE_RESET_REQUEST",n=>Vt(n.tabId))),(a=chrome.tabs)!=null&&a.onRemoved){const n=o=>{va(o).catch(()=>{})};chrome.tabs.onRemoved.addListener(n),e.push(()=>chrome.tabs.onRemoved.removeListener(n))}const t=nl((n,o)=>{n.tabId!==void 0&&(De.delete(n.tabId),ct.delete(n.tabId),ot.delete(n.tabId),be.warn("unexpected detach",{tabId:n.tabId,reason:o}))});return e.push(t),be.info("handlers registered"),()=>e.forEach(n=>n())}const Ze=te("cloud-sync"),ro="cloudSyncEnabled",Ma="cloudSyncEndpoint",Da="licenseToken",rn="cloudSyncVersionCache";async function Tn(){try{const e=await de();if(!le(e,"cloudSync"))return null;const t=await chrome.storage.local.get([ro,Ma,Da]),a=t[ro]===!0,n=typeof t[Ma]=="string"?t[Ma]:"",o=typeof t[Da]=="string"?t[Da]:"";return!a||!n||!o?null:{endpoint:n.replace(/\/$/,""),token:o,enabled:a}}catch{return null}}async function Vr(){try{const t=(await chrome.storage.local.get(rn))[rn];return t&&typeof t=="object"?t:{}}catch{return{}}}async function Qt(e){try{await chrome.storage.local.set({[rn]:e})}catch{}}async function bl(e,t,a){const n=await Tn();if(!n)return!1;const o=await Vr(),r=o[e];try{const i=`${n.endpoint}/v1/products/wcagcheckr/baselines/${encodeURIComponent(e)}`,c=await fetch(i,{method:"PUT",headers:{"Content-Type":"application/json",Authorization:`Bearer ${n.token}`},body:JSON.stringify({snapshot:t,axeVersion:a,expectedVersion:r})});if(c.status===409)return Ze.warn(`baseline ${e} version conflict on push`,{expectedVersion:r}),delete o[e],await Qt(o),!1;if(!c.ok)return Ze.warn("baseline push failed",{status:c.status}),!1;const d=await c.json();return typeof d.version=="number"&&(o[e]=d.version,await Qt(o)),!0}catch(i){return Ze.debug("push network failure (offline?)",i),!1}}async function yl(e){const t=await Tn();if(!t)return!1;try{const a=`${t.endpoint}/v1/products/wcagcheckr/baselines/${encodeURIComponent(e)}`,n=await fetch(a,{method:"DELETE",headers:{Authorization:`Bearer ${t.token}`}});if(n.ok){const o=await Vr();delete o[e],await Qt(o)}return n.ok}catch(a){return Ze.debug("delete network failure",a),!1}}async function wl(){const e=await Tn();if(!e)return[];try{const t=await fetch(`${e.endpoint}/v1/products/wcagcheckr/baselines`,{headers:{Authorization:`Bearer ${e.token}`}});if(!t.ok)return Ze.warn("pull failed",{status:t.status}),[];const n=(await t.json()).items??[],o={};for(const r of n)o[r.componentId]=r.version;return await Qt(o),n}catch(t){return Ze.debug("pull network failure",t),[]}}const jr=te("baseline-store"),io="componentIdAliases";async function rt(e,t){try{return await t()}catch(a){const n=a instanceof Error?a.message:String(a);throw jr.error(`baseline IDB ${e} failed: ${n}`),new Oi({code:"BASELINE_DB_ERROR",message:`Baseline storage is unavailable (${e}: ${n}).`,recoverable:!1,details:n})}}async function Aa(e){return((await chrome.storage.local.get(io))[io]??{})[e]??e}async function vl(e){const t=await Aa(e),a=await rt("get",()=>da(t));return a?{violations:a.violations,snapshotMeta:a.snapshotMeta}:null}async function Al(e,t,a,n){const o=await Aa(e),r=await rt("get-for-set",()=>da(o)),i=(r==null?void 0:r.seenOnUrls)??[],c=i.includes(a.url)?i:[...i,a.url];await rt("set",()=>fn({componentId:o,violations:t,snapshotMeta:a,lastUpdated:new Date().toISOString(),axeVersion:a.axeVersion,announcements:n==null?void 0:n.announcements,focusEvents:n==null?void 0:n.focusEvents,seenOnUrls:c})),bl(o,{violations:t,snapshotMeta:a},a.axeVersion).catch(()=>{})}function kl(e,t){if(!e||!t||Wn(e)===Wn(t))return;const a=r=>{var i,c,d,l,h;return(((i=r.pseudoStates)==null?void 0:i.length)??0)*Math.max(1,((c=r.ariaVariations)==null?void 0:c.length)??0)*(((d=r.themes)==null?void 0:d.length)??0)*(((l=r.directions)==null?void 0:l.length)??0)*(((h=r.breakpoints)==null?void 0:h.length)??0)},n=a(e),o=a(t);return`Baseline was captured under a different state-matrix configuration (${n} states) than this run (${o} states). Per-state-instance deltas inflate when the matrix changes — the unique-violation deltas above are still reliable.`}async function En(e,t,a,n){var l;const o=await Aa(e),r=await ha(o),i=await rt("compare",()=>da(o));if(!i){const h=t.filter(u=>r.has(u.matchKey)),m=t.filter(u=>!r.has(u.matchKey));return{new:m,persistent:[],fixed:[],newCount:m.length,persistentCount:0,fixedCount:0,baselineSnapshotMeta:null,comparedAt:new Date().toISOString(),newAnnouncements:a==null?void 0:a.announcements,newFocusEvents:a==null?void 0:a.focusEvents,acknowledged:h,acknowledgedCount:h.length}}const c=Ri(i.violations,t,i.snapshotMeta,{baselineAnnouncements:i.announcements,currentAnnouncements:a==null?void 0:a.announcements,baselineFocusEvents:i.focusEvents,currentFocusEvents:a==null?void 0:a.focusEvents},r),d=kl((l=i.snapshotMeta)==null?void 0:l.matrixConfig,n);return d&&(c.matrixMismatchWarning=d),c}async function Il(e){const t=await Aa(e);await rt("delete",()=>Go(t)),yl(t).catch(()=>{})}async function np(){const e=await wl();for(const t of e)await fn({componentId:t.componentId,violations:t.snapshot.violations,snapshotMeta:t.snapshot.snapshotMeta,lastUpdated:t.updatedAt,axeVersion:t.axeVersion});return e.length>0&&q({type:"SCORECARD_UPDATED_EVENT"}),e.length}async function xl(e){let t=await rt("list",()=>Ho());return e!=null&&e.url&&(t=t.filter(a=>a.snapshotMeta.url===e.url)),t.map(a=>{const n=(a.focusEvents??[]).filter(l=>l.isFocusReset).length,o=(a.announcements??[]).length,r=a.violations.filter(l=>l.ruleId==="target-size").length,i=a.violations.some(l=>l.ruleId==="color-contrast"&&l.currentState.pseudoState==="hover"),c=a.violations.filter(l=>l.impact==="critical").length,d=a.violations.filter(l=>l.impact==="serious").length;return{componentId:a.componentId,violationCount:a.violations.length,lastUpdated:a.lastUpdated,seenOnUrlsCount:(a.seenOnUrls??[]).length,metrics:{criticalCount:c,seriousCount:d,focusResetCount:n,announcementCount:o,targetSizeFailCount:r,hoverContrastFail:i}}})}function op(){const e=[];return e.push(Se("BASELINE_GET",async t=>({type:"BASELINE_GET_RESPONSE",baseline:await vl(t.componentId)}))),e.push(Se("BASELINE_SET",async t=>{await Al(t.componentId,t.violations,t.snapshotMeta,{announcements:t.announcements,focusEvents:t.focusEvents}),q({type:"SCORECARD_UPDATED_EVENT"})})),e.push(Se("BASELINE_COMPARE",async t=>({type:"BASELINE_COMPARE_RESPONSE",delta:await En(t.componentId,t.currentViolations,{announcements:t.announcements,focusEvents:t.focusEvents},t.currentMatrix)}))),e.push(Se("BASELINE_DELETE",async t=>{await Il(t.componentId),q({type:"SCORECARD_UPDATED_EVENT"})})),e.push(Se("BASELINE_LIST",async t=>({type:"BASELINE_LIST_RESPONSE",items:await xl(t.filter)}))),jr.info("handlers registered"),()=>e.forEach(t=>t())}const Sl=te("settings-store"),so=1,La="__schemaVersion",Zt={stateMatrix:Ba,componentIdAliases:{},ignorePatterns:[]},Cl=["stateMatrix","componentIdAliases","ignorePatterns","activePresetId","matrixPresets","userMode","crawlMaxPages","crawlIncludeRegex","crawlExcludeRegex","crawlRememberSettings","crawlReviewEnabled","githubRepoUrl","jiraInstanceUrl","azureDevOpsProjectUrl","gitlabProjectUrl"];async function Ve(e,t){return(await chrome.storage.local.get(e))[e]??t}async function rp(){const e=await chrome.storage.local.get([La,...Object.keys(Zt)]);if(e[La]===so)return;const t={[La]:so};for(const[a,n]of Object.entries(Zt))a in e||(t[a]=n);await chrome.storage.local.set(t),Sl.info("defaults ensured")}function ip(){const e=[];return e.push(Se("SETTINGS_GET",async t=>({type:"SETTINGS_RESPONSE",data:(await chrome.storage.local.get(t.key))[t.key]??Zt[t.key]}))),e.push(Se("SETTINGS_SET",async t=>{await chrome.storage.local.set({[t.key]:t.value})})),e.push(Se("SETTINGS_GET_ALL",async()=>{const t=await chrome.storage.local.get([...Cl]);return{type:"SETTINGS_RESPONSE",data:{...Zt,...t}}})),()=>e.forEach(t=>t())}function Me(e){const t={capUsd:Math.max(0,e),spentUsd:0,exceeded:!1};return{state:t,canCharge(){return!t.exceeded&&t.spentUsd<t.capUsd},recordCharge(a){t.spentUsd+=Math.max(0,a),t.spentUsd>=t.capUsd&&(t.exceeded=!0)}}}const $l=te("ai-alt-text");async function Tl(e,t,a={}){var m,u;if(!t.enabled||!t.enabledChecks.altText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const n=ue(t);if(!n.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${n.reason}`]};const o=n.client,r=Me(t.costCapUsd),i=[],c=[],d=Math.max(1,t.maxCandidatesPerCheck??10),l=e.slice(0,d),h=l.length;for(let g=0;g<l.length;g++){const f=l[g];if(!r.canCharge())break;try{if((m=a.signal)!=null&&m.aborted){c.push(`canceled after ${g}/${h} candidates`);break}const p=await o.judgeAltText({imageUrl:f.imageUrl,alt:f.alt,surroundingContext:f.surroundingContext,signal:a.signal});r.recordCharge(p.costUsd),(p.verdict==="fail"||p.verdict==="uncertain"&&p.confidence<.6)&&i.push(await El(f,p))}catch(p){if(p instanceof Error&&(p.name==="AbortError"||p.name==="ExternalAbortError")){c.push(`canceled after ${g}/${h} candidates`);break}const b=p instanceof Error?p.message:String(p);c.push(`${f.selector}: ${b}`),$l.warn("alt-text judgment failed",p)}(u=a.onProgress)==null||u.call(a,g+1,h)}return{findings:i,totalCostUsd:r.state.spentUsd,capExceeded:r.state.exceeded,errors:c}}async function El(e,t){const a=t.verdict==="uncertain"?"ai-alt-uncertain":"ai-alt-misleading",n={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},o=await un({ruleId:a,componentId:e.componentId,currentState:e.currentState,target:n}),r=t.verdict==="uncertain";return{ruleId:a,wcagCriterion:"1.1.1",wcagLevel:"A",impact:r?"minor":"serious",description:r?"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:n,componentId:e.componentId,currentState:e.currentState,axeVersion:e.axeVersion,detectedAt:new Date().toISOString(),matchKey:o,ai:{reasoning:t.reasoning,confidence:t.confidence,model:t.model,costUsd:t.costUsd},...r?{needsReview:!0}:{}}}const mt=te("interactive-audit"),Rl="1.3",Ol={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 Rn{constructor(t){Mt(this,"session");Mt(this,"attached",!1);Mt(this,"steps",[]);this.tabId=t,this.session=new Lr({tabId:t})}async attach(){if(!this.attached)try{await chrome.debugger.attach({tabId:this.tabId},Rl),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 mt.error("attach failed",t),Pr(t)}}async dispose(){if(this.attached){try{await chrome.debugger.detach({tabId:this.tabId})}catch(t){mt.debug("detach soft-failure",t)}this.attached=!1}}async pressKey(t,a){const n=Ol[t];if(!n)throw new Error(`Unknown key: ${String(t)}`);const o=a!=null&&a.shift?8:0;await this.session.send("Input.dispatchKeyEvent",{type:"rawKeyDown",key:n.key,code:n.code,windowsVirtualKeyCode:n.windowsVirtualKeyCode,modifiers:o}),await this.session.send("Input.dispatchKeyEvent",{type:"keyUp",key:n.key,code:n.code,windowsVirtualKeyCode:n.windowsVirtualKeyCode,modifiers:o}),await co(50)}async pressKeyN(t,a,n){for(let o=0;o<a;o++)await this.pressKey(t,n)}async typeText(t){await this.session.send("Input.insertText",{text:t}),await co(50)}async countFocusables(){var a;const t=`(() => {
742
+ }`}function nl(e,t,a){var c;const n=((c=e.content.find(d=>d.type==="text"))==null?void 0:c.text)??"",r=n.replace(/```(?:json)?/g,"").trim().match(/\{[\s\S]*\}/),i=e.usage.input_tokens*t.input+e.usage.output_tokens*t.output;if(!r)return{verdict:"uncertain",reasoning:n||"AI returned no parseable response.",costUsd:i,model:e.model||a};try{const d=JSON.parse(r[0]);return{verdict:d.verdict==="pass"||d.verdict==="fail"?d.verdict:"uncertain",reasoning:typeof d.reasoning=="string"&&d.reasoning?d.reasoning:"AI returned no rationale.",costUsd:i,model:e.model||a}}catch{return{verdict:"uncertain",reasoning:n,costUsd:i,model:e.model||a}}}function ol(e,t){var i;const a=((i=e.content.find(c=>c.type==="text"))==null?void 0:i.text)??"",o=a.replace(/```(?:json)?/g,"").trim().match(/\{[\s\S]*\}/),r=e.usage.input_tokens*t.input+e.usage.output_tokens*t.output;if(!o)return{lead:"",body:a||"Summary generation failed.",closer:"",costUsd:r,model:e.model};try{const c=JSON.parse(o[0]);return{lead:typeof c.lead=="string"?c.lead:"",body:typeof c.body=="string"?c.body:"",closer:typeof c.closer=="string"?c.closer:"",costUsd:r,model:e.model}}catch{return{lead:"",body:a,closer:"",costUsd:r,model:e.model}}}function he(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:Vc({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}`}}}const yt=te("consistency-ai-suppression");async function rl(e,t={}){var h;if(e.length===0)return{emitted:[],suppressed:0,uncertain:0,apiCalls:0};const a=await ue();if(!de(a,"aiConsistencySuppression"))return{emitted:e,suppressed:0,uncertain:0,apiCalls:0};let n;try{const m=await chrome.storage.local.get("aiConfig");n=ve(m.aiConfig)}catch{return yt.debug("aiConfig load failed; emitting heuristic findings unchanged"),{emitted:e,suppressed:0,uncertain:0,apiCalls:0}}const o=he(n);if(!o.ok)return yt.info(`AI client unavailable (${o.reason}); emitting heuristic findings unchanged`),{emitted:e,suppressed:0,uncertain:0,apiCalls:0};const r=o.client;if(typeof r.judgeConsistencyDivergence!="function")return yt.info("AI client does not support judgeConsistencyDivergence; emitting heuristic findings unchanged"),{emitted:e,suppressed:0,uncertain:0,apiCalls:0};const i=[];let c=0,d=0,l=0;for(const m of e){if((h=t.signal)!=null&&h.aborted){i.push(m);continue}try{const u=await r.judgeConsistencyDivergence({wcagCriterion:m.wcagCriterion,ruleId:m.ruleId,description:m.description,details:m.details,pages:m.pages,signal:t.signal});if(l++,u.verdict==="pass"){c++;continue}if(u.verdict==="fail"){i.push({...m,aiVerdict:{verdict:"real-divergence",confidence:u.confidence??0,reasoning:u.reasoning??""}});continue}d++,i.push({...m,aiVerdict:{verdict:"uncertain",confidence:u.confidence??0,reasoning:u.reasoning??""}})}catch(u){yt.warn(`AI suppression failed for ${m.ruleId} ${m.wcagCriterion}: ${u instanceof Error?u.message:String(u)}`),i.push(m)}}return yt.info(`consistency AI suppression: ${l} API calls, ${c} false-positives suppressed, ${d} uncertain, ${i.length} emitted`),{emitted:i,suppressed:c,uncertain:d,apiCalls:l}}function il(e){const t=new Map(e.breakpointPresets.map(n=>[n.id,n])),a=[];for(const n of e.breakpoints){const o=t.get(n);if(o)for(const r of e.directions)for(const i of e.themes){const c=e.ariaVariations.length===0?[null]:e.ariaVariations;for(const d of c)for(const l of e.pseudoStates)a.push({pseudoState:l,ariaVariation:d,theme:i,direction:r,breakpoint:o})}}return a}const Fr=["color-contrast","color-contrast-enhanced","link-in-text-block"],sl=["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(Fr),t=sl.filter(a=>e.has(a));t.length>0&&console.error("axe-rule-classification: rule(s) classified as BOTH state-dependent and markup-eligible:",t)}const cl=te("chrome-api"),ll="1.3",io=3e4;class dl extends Error{constructor(t,a){super(`CDP ${t} timed out after ${a}ms`),this.method=t,this.timeoutMs=a,this.name="CdpTimeoutError"}}class _r{constructor(t){this.target=t}async attach(){await chrome.debugger.attach(this.target,ll)}async detach(){try{await chrome.debugger.detach(this.target)}catch(t){cl.debug("detach soft-failure",t)}}async send(t,a){const n=chrome.debugger.sendCommand(this.target,t,a);let o;const r=new Promise((i,c)=>{o=setTimeout(()=>c(new dl(t,io)),io)});try{return await Promise.race([n,r])}finally{o!==void 0&&clearTimeout(o)}}}function ul(e){const t=e instanceof Error?e.message:String(e);return/Another debugger is already attached|Cannot attach to/i.test(t)}function Ur(e){const t=e instanceof Error?e.message:String(e);return ul(e)?Gn("DEBUGGER_BUSY","DevTools is open on this tab. Close it to run the audit.",!0,{raw:t}):Gn("STATE_DRIVE_FAILED",t,!1)}function hl(e){const t=(a,n)=>e(a,n);return chrome.debugger.onDetach.addListener(t),()=>chrome.debugger.onDetach.removeListener(t)}const ye=te("state-driver"),pl=60*1e3,Ne=new Map,ea=new Map,dt=new Map,it=new Map,fl='button, a[href], input, textarea, select, summary, [tabindex]:not([tabindex="-1"]), [role="button"], [role="link"], [role="checkbox"], [role="radio"], [role="tab"], [role="menuitem"], [role="switch"], [role="combobox"], [role="option"]',$t=new Map,gl={default:[],hover:["hover"],focus:["focus"],"focus-visible":["focus","focus-visible"],active:["active"],disabled:[]};async function Wr(e){let t=Ne.get(e);if(!t){t=new _r({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(a){try{await t.detach()}catch{}throw a}Ne.set(e,t),dt.set(e,new Map),ml(e),ye.info("attached",{tabId:e})}return yl(e),t}function ml(e){const t=(a,n,o)=>{var i,c;if(a.tabId!==e)return;const r=dt.get(e);if(r)if(n==="Runtime.executionContextCreated"){const d=o,l=d==null?void 0:d.context;l&&((i=l.auxData)!=null&&i.isDefault)&&((c=l.auxData)!=null&&c.frameId)&&r.set(l.auxData.frameId,l.id)}else if(n==="Runtime.executionContextDestroyed"){const d=o,l=d==null?void 0:d.executionContextId;if(l!==void 0)for(const[h,m]of r.entries())m===l&&r.delete(h)}else n==="Runtime.executionContextsCleared"&&r.clear()};chrome.debugger.onEvent.addListener(t)}async function bl(e){const t=Ne.get(e);if(Ne.delete(e),dt.delete(e),it.delete(e),$t.delete(e),t)try{await t.detach()}catch{}}function yl(e){const t=ea.get(e);t&&clearTimeout(t),ea.set(e,setTimeout(()=>{xa(e).catch(a=>ye.warn("idle detach failed",a))},pl))}async function xa(e){const t=Ne.get(e);t&&(await t.detach(),Ne.delete(e)),dt.delete(e),it.delete(e),$t.delete(e);const a=ea.get(e);a&&(clearTimeout(a),ea.delete(e))}function Gr(e){const t=[{id:e.frame.id,url:e.frame.url}];for(const a of e.childFrames??[])t.push(...Gr(a));return t}async function wl(e,t,a){var r;if(!a||a===0)return;const n=dt.get(t);if(!n)return;let o;try{o=(r=(await chrome.scripting.executeScript({target:{tabId:t,frameIds:[a]},func:()=>location.href}))[0])==null?void 0:r.result}catch(i){ye.debug("frame URL lookup failed",i)}try{const i=await e.send("Page.getFrameTree"),c=Gr(i.frameTree);if(o){const h=c.find(m=>m.url===o);if(h){const m=n.get(h.id);if(m!==void 0)return m}}const d=i.frameTree.frame.id,l=c.find(h=>h.id!==d);return l?n.get(l.id):void 0}catch(i){ye.warn("Page.getFrameTree failed",i);return}}async function vl(e,t,a){var i;const n={expression:`document.querySelector(${JSON.stringify(t)})`,objectGroup:"wcag-auditor",returnByValue:!1};a!==void 0&&(n.contextId=a);const r=(i=(await e.send("Runtime.evaluate",n)).result)==null?void 0:i.objectId;if(!r)return null;try{return(await e.send("DOM.requestNode",{objectId:r})).nodeId??null}finally{e.send("Runtime.releaseObject",{objectId:r}).catch(()=>{})}}async function Vr(e){const t=it.get(e);if(!t||t.length===0)return;const a=Ne.get(e);if(a){for(const n of t)try{await a.send("CSS.forcePseudoState",{nodeId:n,forcedPseudoClasses:[]})}catch(o){ye.debug("forcePseudoState clear failed (node may be gone)",o)}it.delete(e)}}async function Al(e,t){try{return(await e.send("DOM.querySelectorAll",{nodeId:t,selector:fl})).nodeIds??[]}catch(a){return ye.debug("querySelectorAll for interactive descendants failed",a),[]}}async function rn(e,t,a,n){await chrome.scripting.executeScript({target:En(e,t),func:(o,r)=>{const i=document.querySelector(o);i&&(r?i.setAttribute("disabled",""):i.removeAttribute("disabled"))},args:[a,n]})}async function jr(e,t){await chrome.scripting.executeScript({target:En(e,t),func:()=>{const a=document.activeElement;a&&typeof a.blur=="function"&&a.blur()}})}async function Hr(e,t){await chrome.scripting.executeScript({target:{tabId:e,frameIds:[0]},func:a=>{document.documentElement.setAttribute("dir",a)},args:[t]})}async function kl(e,t,a,n){await chrome.scripting.executeScript({target:En(e,t),func:(o,r)=>{const i=document.querySelector(o);if(i)for(const[c,d]of Object.entries(r))i.setAttribute(c,d)},args:[a,n]})}function En(e,t){return t!==void 0&&t!==0?{tabId:e,frameIds:[t]}:{tabId:e}}async function xl(e,t,a,n){const o=await Wr(e),r=$t.get(e),i=(r==null?void 0:r.scope)===a&&(r==null?void 0:r.frameId)===n,c=t.ariaVariation?JSON.stringify(t.ariaVariation.attributes):"",d=!i||(r==null?void 0:r.pseudoState)!==t.pseudoState,l=!r||r.theme!==t.theme,h=!r||r.direction!==t.direction,m=!r||r.breakpointId!==t.breakpoint.id,u=!i||(r==null?void 0:r.ariaVariationKey)!==c;if(d&&(await Vr(e),a)){t.pseudoState==="disabled"?await rn(e,n,a,!0):(await rn(e,n,a,!1),await jr(e,n));const g=gl[t.pseudoState];if(g.length>0){const p=await wl(o,e,n),f=await vl(o,a,p);if(f!==null){const y=await Al(o,f),b=[f,...y];for(const A of b)try{await o.send("CSS.forcePseudoState",{nodeId:A,forcedPseudoClasses:g})}catch(w){ye.debug(`CSS.forcePseudoState failed for nodeId=${A}`,w)}it.set(e,b),ye.debug(`forced :${t.pseudoState} on ${b.length} node(s)`)}else ye.debug(`could not resolve nodeId for ${a} (frameId=${n})`)}}if(l){const g=[{name:"prefers-reduced-motion",value:"reduce"}];t.theme==="dark"?(g.push({name:"prefers-color-scheme",value:"dark"}),g.push({name:"forced-colors",value:"none"})):t.theme==="light"?(g.push({name:"prefers-color-scheme",value:"light"}),g.push({name:"forced-colors",value:"none"})):t.theme==="forced-colors-active"?g.push({name:"forced-colors",value:"active"}):t.theme==="forced-colors-none"&&g.push({name:"forced-colors",value:"none"}),await o.send("Emulation.setEmulatedMedia",{features:g}),await new Promise(p=>setTimeout(p,50))}h&&await Hr(e,t.direction),m&&await o.send("Emulation.setDeviceMetricsOverride",{width:t.breakpoint.width,height:t.breakpoint.height,deviceScaleFactor:t.breakpoint.deviceScaleFactor,mobile:t.breakpoint.mobile}),u&&t.ariaVariation&&a&&await kl(e,n,a,t.ariaVariation.attributes),$t.set(e,{scope:a,frameId:n,pseudoState:t.pseudoState,theme:t.theme,direction:t.direction,breakpointId:t.breakpoint.id,ariaVariationKey:c})}async function Il(e,t,a){const n=Ne.get(e);if(n){if(await Vr(e),$t.delete(e),t)try{await rn(e,a,t,!1),await jr(e,a),await Hr(e,"ltr")}catch(o){ye.warn("element-level reset partial failure",o)}try{await n.send("Emulation.setEmulatedMedia",{features:[]}),await n.send("Emulation.clearDeviceMetricsOverride")}catch(o){ye.warn("reset partial failure",o)}await xa(e)}}async function sn(e,t,a,n){try{return await xl(e,t,a,n),H({type:"STATE_CHANGED_EVENT",tabId:e,currentState:t}),{type:"STATE_DRIVE_RESPONSE",success:!0,appliedState:t}}catch(o){return ye.error("state drive failed",o),await bl(e),{type:"STATE_DRIVE_RESPONSE",success:!1,error:Ur(o)}}}async function Ht(e,t,a){await Il(e,t,a)}async function pp(e){await xa(e);try{await chrome.debugger.detach({tabId:e})}catch{}}async function Sl(e,t={}){try{const a=await Wr(e),n={format:"jpeg",quality:t.quality??75};t.fullPage&&(n.captureBeyondViewport=!0);const o=await a.send("Page.captureScreenshot",n);return o!=null&&o.data?`data:image/jpeg;base64,${o.data}`:null}catch(a){return ye.debug("Page.captureScreenshot failed",a),null}}function fp(){var a;const e=[];if(e.push(Ce("STATE_DRIVE_REQUEST",n=>sn(n.tabId,n.state,n.scope,n.frameId))),e.push(Ce("STATE_RESET_REQUEST",n=>Ht(n.tabId))),(a=chrome.tabs)!=null&&a.onRemoved){const n=o=>{xa(o).catch(()=>{})};chrome.tabs.onRemoved.addListener(n),e.push(()=>chrome.tabs.onRemoved.removeListener(n))}const t=hl((n,o)=>{n.tabId!==void 0&&(Ne.delete(n.tabId),dt.delete(n.tabId),it.delete(n.tabId),ye.warn("unexpected detach",{tabId:n.tabId,reason:o}))});return e.push(t),ye.info("handlers registered"),()=>e.forEach(n=>n())}const tt=te("cloud-sync"),so="cloudSyncEnabled",La="cloudSyncEndpoint",Na="licenseToken",cn="cloudSyncVersionCache";async function Rn(){try{const e=await ue();if(!de(e,"cloudSync"))return null;const t=await chrome.storage.local.get([so,La,Na]),a=t[so]===!0,n=typeof t[La]=="string"?t[La]:"",o=typeof t[Na]=="string"?t[Na]:"";return!a||!n||!o?null:{endpoint:n.replace(/\/$/,""),token:o,enabled:a}}catch{return null}}async function Br(){try{const t=(await chrome.storage.local.get(cn))[cn];return t&&typeof t=="object"?t:{}}catch{return{}}}async function ta(e){try{await chrome.storage.local.set({[cn]:e})}catch{}}async function Cl(e,t,a){const n=await Rn();if(!n)return!1;const o=await Br(),r=o[e];try{const i=`${n.endpoint}/v1/products/wcagcheckr/baselines/${encodeURIComponent(e)}`,c=await fetch(i,{method:"PUT",headers:{"Content-Type":"application/json",Authorization:`Bearer ${n.token}`},body:JSON.stringify({snapshot:t,axeVersion:a,expectedVersion:r})});if(c.status===409)return tt.warn(`baseline ${e} version conflict on push`,{expectedVersion:r}),delete o[e],await ta(o),!1;if(!c.ok)return tt.warn("baseline push failed",{status:c.status}),!1;const d=await c.json();return typeof d.version=="number"&&(o[e]=d.version,await ta(o)),!0}catch(i){return tt.debug("push network failure (offline?)",i),!1}}async function $l(e){const t=await Rn();if(!t)return!1;try{const a=`${t.endpoint}/v1/products/wcagcheckr/baselines/${encodeURIComponent(e)}`,n=await fetch(a,{method:"DELETE",headers:{Authorization:`Bearer ${t.token}`}});if(n.ok){const o=await Br();delete o[e],await ta(o)}return n.ok}catch(a){return tt.debug("delete network failure",a),!1}}async function Tl(){const e=await Rn();if(!e)return[];try{const t=await fetch(`${e.endpoint}/v1/products/wcagcheckr/baselines`,{headers:{Authorization:`Bearer ${e.token}`}});if(!t.ok)return tt.warn("pull failed",{status:t.status}),[];const n=(await t.json()).items??[],o={};for(const r of n)o[r.componentId]=r.version;return await ta(o),n}catch(t){return tt.debug("pull network failure",t),[]}}const qr=te("baseline-store"),co="componentIdAliases";async function st(e,t){try{return await t()}catch(a){const n=a instanceof Error?a.message:String(a);throw qr.error(`baseline IDB ${e} failed: ${n}`),new Fi({code:"BASELINE_DB_ERROR",message:`Baseline storage is unavailable (${e}: ${n}).`,recoverable:!1,details:n})}}async function Ia(e){return((await chrome.storage.local.get(co))[co]??{})[e]??e}async function El(e){const t=await Ia(e),a=await st("get",()=>pa(t));return a?{violations:a.violations,snapshotMeta:a.snapshotMeta}:null}async function Rl(e,t,a,n){const o=await Ia(e),r=await st("get-for-set",()=>pa(o)),i=(r==null?void 0:r.seenOnUrls)??[],c=i.includes(a.url)?i:[...i,a.url];await st("set",()=>mn({componentId:o,violations:t,snapshotMeta:a,lastUpdated:new Date().toISOString(),axeVersion:a.axeVersion,announcements:n==null?void 0:n.announcements,focusEvents:n==null?void 0:n.focusEvents,seenOnUrls:c})),Cl(o,{violations:t,snapshotMeta:a},a.axeVersion).catch(()=>{})}function Ol(e,t){if(!e||!t||Vn(e)===Vn(t))return;const a=r=>{var i,c,d,l,h;return(((i=r.pseudoStates)==null?void 0:i.length)??0)*Math.max(1,((c=r.ariaVariations)==null?void 0:c.length)??0)*(((d=r.themes)==null?void 0:d.length)??0)*(((l=r.directions)==null?void 0:l.length)??0)*(((h=r.breakpoints)==null?void 0:h.length)??0)},n=a(e),o=a(t);return`Baseline was captured under a different state-matrix configuration (${n} states) than this run (${o} states). Per-state-instance deltas inflate when the matrix changes — the unique-violation deltas above are still reliable.`}async function On(e,t,a,n){var l;const o=await Ia(e),r=await ga(o),i=await st("compare",()=>pa(o));if(!i){const h=t.filter(u=>r.has(u.matchKey)),m=t.filter(u=>!r.has(u.matchKey));return{new:m,persistent:[],fixed:[],newCount:m.length,persistentCount:0,fixedCount:0,baselineSnapshotMeta:null,comparedAt:new Date().toISOString(),newAnnouncements:a==null?void 0:a.announcements,newFocusEvents:a==null?void 0:a.focusEvents,acknowledged:h,acknowledgedCount:h.length}}const c=Pi(i.violations,t,i.snapshotMeta,{baselineAnnouncements:i.announcements,currentAnnouncements:a==null?void 0:a.announcements,baselineFocusEvents:i.focusEvents,currentFocusEvents:a==null?void 0:a.focusEvents},r),d=Ol((l=i.snapshotMeta)==null?void 0:l.matrixConfig,n);return d&&(c.matrixMismatchWarning=d),c}async function Ml(e){const t=await Ia(e);await st("delete",()=>qo(t)),$l(t).catch(()=>{})}async function gp(){const e=await Tl();for(const t of e)await mn({componentId:t.componentId,violations:t.snapshot.violations,snapshotMeta:t.snapshot.snapshotMeta,lastUpdated:t.updatedAt,axeVersion:t.axeVersion});return e.length>0&&H({type:"SCORECARD_UPDATED_EVENT"}),e.length}async function Dl(e){let t=await st("list",()=>zo());return e!=null&&e.url&&(t=t.filter(a=>a.snapshotMeta.url===e.url)),t.map(a=>{const n=(a.focusEvents??[]).filter(l=>l.isFocusReset).length,o=(a.announcements??[]).length,r=a.violations.filter(l=>l.ruleId==="target-size").length,i=a.violations.some(l=>l.ruleId==="color-contrast"&&l.currentState.pseudoState==="hover"),c=a.violations.filter(l=>l.impact==="critical").length,d=a.violations.filter(l=>l.impact==="serious").length;return{componentId:a.componentId,violationCount:a.violations.length,lastUpdated:a.lastUpdated,seenOnUrlsCount:(a.seenOnUrls??[]).length,metrics:{criticalCount:c,seriousCount:d,focusResetCount:n,announcementCount:o,targetSizeFailCount:r,hoverContrastFail:i}}})}function mp(){const e=[];return e.push(Ce("BASELINE_GET",async t=>({type:"BASELINE_GET_RESPONSE",baseline:await El(t.componentId)}))),e.push(Ce("BASELINE_SET",async t=>{await Rl(t.componentId,t.violations,t.snapshotMeta,{announcements:t.announcements,focusEvents:t.focusEvents}),H({type:"SCORECARD_UPDATED_EVENT"})})),e.push(Ce("BASELINE_COMPARE",async t=>({type:"BASELINE_COMPARE_RESPONSE",delta:await On(t.componentId,t.currentViolations,{announcements:t.announcements,focusEvents:t.focusEvents},t.currentMatrix)}))),e.push(Ce("BASELINE_DELETE",async t=>{await Ml(t.componentId),H({type:"SCORECARD_UPDATED_EVENT"})})),e.push(Ce("BASELINE_LIST",async t=>({type:"BASELINE_LIST_RESPONSE",items:await Dl(t.filter)}))),qr.info("handlers registered"),()=>e.forEach(t=>t())}const Ll=te("settings-store"),lo=1,Pa="__schemaVersion",aa={stateMatrix:za,componentIdAliases:{},ignorePatterns:[]},Nl=["stateMatrix","componentIdAliases","ignorePatterns","activePresetId","matrixPresets","userMode","crawlMaxPages","crawlIncludeRegex","crawlExcludeRegex","crawlRememberSettings","crawlReviewEnabled","githubRepoUrl","jiraInstanceUrl","azureDevOpsProjectUrl","gitlabProjectUrl"];async function He(e,t){return(await chrome.storage.local.get(e))[e]??t}async function bp(){const e=await chrome.storage.local.get([Pa,...Object.keys(aa)]);if(e[Pa]===lo)return;const t={[Pa]:lo};for(const[a,n]of Object.entries(aa))a in e||(t[a]=n);await chrome.storage.local.set(t),Ll.info("defaults ensured")}function yp(){const e=[];return e.push(Ce("SETTINGS_GET",async t=>({type:"SETTINGS_RESPONSE",data:(await chrome.storage.local.get(t.key))[t.key]??aa[t.key]}))),e.push(Ce("SETTINGS_SET",async t=>{await chrome.storage.local.set({[t.key]:t.value})})),e.push(Ce("SETTINGS_GET_ALL",async()=>{const t=await chrome.storage.local.get([...Nl]);return{type:"SETTINGS_RESPONSE",data:{...aa,...t}}})),()=>e.forEach(t=>t())}function Le(e){const t={capUsd:Math.max(0,e),spentUsd:0,exceeded:!1};return{state:t,canCharge(){return!t.exceeded&&t.spentUsd<t.capUsd},recordCharge(a){t.spentUsd+=Math.max(0,a),t.spentUsd>=t.capUsd&&(t.exceeded=!0)}}}const Pl=te("ai-alt-text");async function Fl(e,t,a={}){var m,u;if(!t.enabled||!t.enabledChecks.altText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const n=he(t);if(!n.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${n.reason}`]};const o=n.client,r=Le(t.costCapUsd),i=[],c=[],d=Math.max(1,t.maxCandidatesPerCheck??10),l=e.slice(0,d),h=l.length;for(let g=0;g<l.length;g++){const p=l[g];if(!r.canCharge())break;try{if((m=a.signal)!=null&&m.aborted){c.push(`canceled after ${g}/${h} candidates`);break}const f=await o.judgeAltText({imageUrl:p.imageUrl,alt:p.alt,surroundingContext:p.surroundingContext,signal:a.signal});r.recordCharge(f.costUsd),(f.verdict==="fail"||f.verdict==="uncertain"&&f.confidence<.6)&&i.push(await _l(p,f))}catch(f){if(f instanceof Error&&(f.name==="AbortError"||f.name==="ExternalAbortError")){c.push(`canceled after ${g}/${h} candidates`);break}const b=f instanceof Error?f.message:String(f);c.push(`${p.selector}: ${b}`),Pl.warn("alt-text judgment failed",f)}(u=a.onProgress)==null||u.call(a,g+1,h)}return{findings:i,totalCostUsd:r.state.spentUsd,capExceeded:r.state.exceeded,errors:c}}async function _l(e,t){const a=t.verdict==="uncertain"?"ai-alt-uncertain":"ai-alt-misleading",n={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},o=await pn({ruleId:a,componentId:e.componentId,currentState:e.currentState,target:n}),r=t.verdict==="uncertain";return{ruleId:a,wcagCriterion:"1.1.1",wcagLevel:"A",impact:r?"minor":"serious",description:r?"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:n,componentId:e.componentId,currentState:e.currentState,axeVersion:e.axeVersion,detectedAt:new Date().toISOString(),matchKey:o,ai:{reasoning:t.reasoning,confidence:t.confidence,model:t.model,costUsd:t.costUsd},...r?{needsReview:!0}:{}}}const wt=te("interactive-audit"),Ul="1.3",Wl={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 Mn{constructor(t){Nt(this,"session");Nt(this,"attached",!1);Nt(this,"steps",[]);this.tabId=t,this.session=new _r({tabId:t})}async attach(){if(!this.attached)try{await chrome.debugger.attach({tabId:this.tabId},Ul),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 wt.error("attach failed",t),Ur(t)}}async dispose(){if(this.attached){try{await chrome.debugger.detach({tabId:this.tabId})}catch(t){wt.debug("detach soft-failure",t)}this.attached=!1}}async pressKey(t,a){const n=Wl[t];if(!n)throw new Error(`Unknown key: ${String(t)}`);const o=a!=null&&a.shift?8:0;await this.session.send("Input.dispatchKeyEvent",{type:"rawKeyDown",key:n.key,code:n.code,windowsVirtualKeyCode:n.windowsVirtualKeyCode,modifiers:o}),await this.session.send("Input.dispatchKeyEvent",{type:"keyUp",key:n.key,code:n.code,windowsVirtualKeyCode:n.windowsVirtualKeyCode,modifiers:o}),await uo(50)}async pressKeyN(t,a,n){for(let o=0;o<a;o++)await this.pressKey(t,n)}async typeText(t){await this.session.send("Input.insertText",{text:t}),await uo(50)}async countFocusables(){var a;const t=`(() => {
735
743
  const sel = [
736
744
  'a[href]', 'button', 'input', 'select', 'textarea',
737
745
  '[tabindex]', 'audio[controls]', 'video[controls]', '[contenteditable]'
@@ -750,7 +758,7 @@ Respond with strict JSON (no markdown fences):
750
758
  return true;
751
759
  };
752
760
  return all.filter(visible).length;
753
- })()`;try{const n=await this.session.send("Runtime.evaluate",{expression:t,returnByValue:!0}),o=(a=n==null?void 0:n.result)==null?void 0:a.value;return typeof o=="number"&&Number.isFinite(o)?o:0}catch{return 0}}async getFocusedElement(){var a;const t=await this.session.send("Runtime.evaluate",{expression:Dl,returnByValue:!0});try{const n=(a=t.result)==null?void 0:a.value;return typeof n=="string"?JSON.parse(n):n}catch(n){return mt.error("getFocusedElement: probe returned non-JSON",n),Ml}}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 mt.debug("captureScreenshot failed",t),null}}async getAxContext(){try{return((await this.session.send("Accessibility.getFullAXTree")).nodes??[]).filter(a=>!a.ignored).map(a=>{var n,o,r;return{nodeId:a.nodeId,role:(n=a.role)==null?void 0:n.value,name:(o=a.name)==null?void 0:o.value,description:(r=a.description)==null?void 0:r.value,childIds:a.childIds}})}catch(t){return mt.debug("getAxContext failed",t),[]}}async recordStep(t,a){const n=await this.getFocusedElement(),o=(a==null?void 0:a.screenshot)!==!1?await this.captureScreenshot():null,r=a!=null&&a.ax?await this.getAxContext():void 0,i={ordinal:this.steps.length,at:new Date().toISOString(),action:t,focused:n,screenshot:o??void 0,axContext:r};return this.steps.push(i),i}}function co(e){return new Promise(t=>setTimeout(t,e))}const Ml={selector:"body",tag:"body",role:"",name:"",outerHtml:"",rect:{x:0,y:0,width:0,height:0},isBody:!0},Dl=`(() => {
761
+ })()`;try{const n=await this.session.send("Runtime.evaluate",{expression:t,returnByValue:!0}),o=(a=n==null?void 0:n.result)==null?void 0:a.value;return typeof o=="number"&&Number.isFinite(o)?o:0}catch{return 0}}async getFocusedElement(){var a;const t=await this.session.send("Runtime.evaluate",{expression:Vl,returnByValue:!0});try{const n=(a=t.result)==null?void 0:a.value;return typeof n=="string"?JSON.parse(n):n}catch(n){return wt.error("getFocusedElement: probe returned non-JSON",n),Gl}}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 wt.debug("captureScreenshot failed",t),null}}async getAxContext(){try{return((await this.session.send("Accessibility.getFullAXTree")).nodes??[]).filter(a=>!a.ignored).map(a=>{var n,o,r;return{nodeId:a.nodeId,role:(n=a.role)==null?void 0:n.value,name:(o=a.name)==null?void 0:o.value,description:(r=a.description)==null?void 0:r.value,childIds:a.childIds}})}catch(t){return wt.debug("getAxContext failed",t),[]}}async recordStep(t,a){const n=await this.getFocusedElement(),o=(a==null?void 0:a.screenshot)!==!1?await this.captureScreenshot():null,r=a!=null&&a.ax?await this.getAxContext():void 0,i={ordinal:this.steps.length,at:new Date().toISOString(),action:t,focused:n,screenshot:o??void 0,axContext:r};return this.steps.push(i),i}}function uo(e){return new Promise(t=>setTimeout(t,e))}const Gl={selector:"body",tag:"body",role:"",name:"",outerHtml:"",rect:{x:0,y:0,width:0,height:0},isBody:!0},Vl=`(() => {
754
762
  const el = document.activeElement;
755
763
  if (!el || el === document.body) {
756
764
  return JSON.stringify({
@@ -821,7 +829,7 @@ Respond with strict JSON (no markdown fences):
821
829
  },
822
830
  isBody: false,
823
831
  });
824
- })()`,Ll=[/no parseable JSON/i,/invalid JSON/i];function lo(e){if(e.verdict!=="uncertain"||e.confidence!==0)return!1;const t=e.reasoning??"";return Ll.some(a=>a.test(t))}async function Ke(e){const t=await e();if(!lo(t))return t;try{const a=await e();if(!lo(a))return a}catch{}return t}async function Pl(e,t){if(t.length===0||typeof OffscreenCanvas>"u"||typeof createImageBitmap>"u")return e;try{const n=await(await fetch(e)).blob(),o=await createImageBitmap(n),r=new OffscreenCanvas(o.width,o.height),i=r.getContext("2d");if(!i)return e;i.drawImage(o,0,0);const c=Math.max(...t.map(f=>f.rect.x+f.rect.width),0),d=c>0&&o.width/c>1.5?o.width/Math.max(1280,c):1;for(const f of t){const p=(f.rect.x+f.rect.width/2)*d,y=(f.rect.y+f.rect.height/2)*d,b=16*d;i.fillStyle="rgba(220, 38, 38, 0.92)",i.strokeStyle="rgba(255, 255, 255, 0.95)",i.lineWidth=2*d,i.beginPath(),i.arc(p,y,b,0,2*Math.PI),i.fill(),i.stroke(),i.fillStyle="white",i.font=`bold ${Math.round(18*d)}px sans-serif`,i.textAlign="center",i.textBaseline="middle",i.fillText(String(f.ordinal),p,y)}const h=await(await r.convertToBlob({type:"image/jpeg",quality:.85})).arrayBuffer(),m=new Uint8Array(h);let u="";const g=8192;for(let f=0;f<m.byteLength;f+=g){const p=m.subarray(f,Math.min(f+g,m.byteLength));u+=String.fromCharCode(...p)}return`data:image/jpeg;base64,${btoa(u)}`}catch{return e}}async function ea(e,t={}){var i;const a=Date.now(),n=5e3,o=200,r=e;for(;Date.now()-a<n;){const d=(i=(await r.session.send("Runtime.evaluate",{expression:`(() => ({ ready: document.readyState === "complete", focusables: document.querySelectorAll('a[href], button:not([disabled]), input:not([disabled]):not([type="hidden"]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"]):not([disabled]), [contenteditable="true"]').length }))()`,returnByValue:!0})).result)==null?void 0:i.value;if(d&&d.ready&&(d.focusables??0)>0)break;await new Promise(l=>setTimeout(l,o))}t.extraDelayMs&&t.extraDelayMs>0&&await new Promise(c=>setTimeout(c,t.extraDelayMs))}function On(e){const t=(e.name??"").trim().slice(0,60),a=Math.round(e.rect.x),n=Math.round(e.rect.y),o=Math.round(e.rect.width),r=Math.round(e.rect.height);return`${e.selector}|${e.tag}|${t}|${a},${n},${o},${r}`}async function Nl(e,t){const a=JSON.stringify({criterionId:e,sequence:t.map(i=>({o:i.ordinal,s:i.selector,r:i.role,x:Math.round(i.rect.x),y:Math.round(i.rect.y),w:Math.round(i.rect.width),h:Math.round(i.rect.height)}))}),n=new TextEncoder().encode(a),o=await crypto.subtle.digest("SHA-256",n);return Array.from(new Uint8Array(o)).map(i=>i.toString(16).padStart(2,"0")).join("")}const Pa=te("focus-order-audit"),Na="2.4.3",uo=50;async function Fl(e){var i,c,d;const t=await chrome.storage.local.get("aiConfig"),a=ve(t.aiConfig),n=ue(a);if(!n.ok)return{ok:!1,error:n.reason};const o=n.client;if(typeof o.judgeFocusOrder!="function")return{ok:!1,error:"AI client does not support judgeFocusOrder yet."};const r=new Rn(e.tabId);try{await r.attach(),await ea(r),await _l(r),await r.recordStep("initial",{screenshot:!0});const l=await r.countFocusables(),h=l>0?Math.min(uo,l):uo,m=new Set;for(let s=0;s<h;s++){if((i=e.signal)!=null&&i.aborted)return{ok:!1,error:"Audit cancelled."};await r.pressKey("Tab");const A=await r.recordStep(`Tab #${s+1}`,{screenshot:!1});if(A.focused.isBody)break;const S=On(A.focused);if(m.has(S))break;m.add(S),(c=e.onStep)==null||c.call(e,{ordinal:A.ordinal,selector:A.focused.selector,role:A.focused.role,name:A.focused.name})}const u=r.steps.filter(s=>s.action!=="initial"&&!s.focused.isBody).map(s=>({ordinal:s.ordinal,selector:s.focused.selector,role:s.focused.role,name:s.focused.name,rect:s.focused.rect})),g=(d=r.steps[0])==null?void 0:d.screenshot,f=g?await Pl(g,u):void 0,p=await Nl(Na,u);let y,b=null;try{b=await Pe(e.componentId,Na),b&&!Wt(b.reasoning)?y={verdict:b.verdict,reasoning:b.reasoning,confidence:b.confidence}:b&&Wt(b.reasoning)&&Pa.info("Skipping anchor — previous 2.4.3 verdict was cascade-escalated (system-imposed); letting primary judge fresh")}catch{}if(b&&b.inputHash===p&&!Wt(b.reasoning)){Pa.info(`2.4.3 inputs unchanged (hash match: ${p.slice(0,8)}…); reusing previous verdict=${b.verdict} without AI call`);const s={...b,finishedAt:new Date().toISOString(),costUsd:0,model:`${b.model} (cached, hash-match)`,inputHash:p};return await ne(s),{ok:!0,result:s}}const v=await Ke(()=>o.judgeFocusOrder({pageUrl:e.pageUrl,pageScreenshot:f,tabSequence:u,previousVerdict:y,signal:e.signal})),w={componentId:e.componentId,criterionId:Na,verdict:v.verdict==="pass"?"pass":v.verdict==="fail"?"fail":"uncertain",reasoning:v.reasoning,confidence:v.confidence,steps:r.steps,pageUrl:e.pageUrl,finishedAt:new Date().toISOString(),costUsd:v.costUsd,model:v.model,inputHash:p};return await ne(w),{ok:!0,result:w}}catch(l){return Pa.error("focus-order audit failed",l),{ok:!1,error:l instanceof Error?l.message:String(l)}}finally{await r.dispose()}}async function _l(e){await e.session.send("Runtime.evaluate",{expression:"(() => { if (document.activeElement && document.activeElement !== document.body) document.activeElement.blur(); window.scrollTo(0, 0); })()"})}async function ka(e,t){const a=JSON.stringify({criterionId:e,input:t}),n=new TextEncoder().encode(a),o=await crypto.subtle.digest("SHA-256",n);return Array.from(new Uint8Array(o)).map(i=>i.toString(16).padStart(2,"0")).join("")}async function Ia(e,t,a){const n=await Pe(e,t);return n?n.inputHash!==a?{hit:!1,previous:n}:n.verdict==="uncertain"?{hit:!1,previous:n}:Wt(n.reasoning)?{hit:!1,previous:n}:{hit:!0,previous:n}:{hit:!1,previous:null}}function xa(e,t){return{...e,finishedAt:new Date().toISOString(),costUsd:0,model:`${e.model} (cached, hash-match)`,inputHash:t}}const Fa=te("keyboard-trap-audit"),bt="2.1.2",ho=50;async function Ul(e){const t=await chrome.storage.local.get("aiConfig"),a=ve(t.aiConfig),n=ue(a);if(!n.ok)return{ok:!1,error:n.reason};const o=n.client;if(typeof o.judgeKeyboardTrap!="function")return{ok:!1,error:"AI client does not support judgeKeyboardTrap yet."};const r=new Rn(e.tabId);try{await r.attach(),await ea(r),await po(r),await r.recordStep("initial",{screenshot:!0});const i=await r.countFocusables(),c=i>0?Math.min(ho,i):ho;let d=await fo(r,c,e.signal);if(d.forwardSelectors.length===0&&d.reverseSelectors.length>0&&(Fa.info("2.1.2 detected degenerate 0/N walk — retrying after extra hydration wait"),await ea(r,{extraDelayMs:1500}),await po(r),d=await fo(r,c,e.signal)),d.forwardSelectors.length===0){const s=Wl(i),A={componentId:e.componentId,criterionId:bt,verdict:s,reasoning:s==="pass"?`The page has ${i} focusable element${i===1?"":"s"} (confirmed by a direct focusable-element count), but the automated forward Tab walk could not traverse them in this session — focus did not enter the page document. This is a known timing/session artifact (most common when this check runs after another keyboard walkthrough during a crawl), not evidence of a problem. No keyboard trap was observed and interactive content is present, so this passes. If you suspect a trap, open the page directly and Tab through it manually.`:`Could not run a meaningful keyboard-trap evaluation: the forward Tab walk found no focusable elements and the page reports none (forward: 0, reverse: ${d.reverseSelectors.length}). Likely a page with no interactive content, or one that is auth-gated / blocks automation. If the page genuinely has no interactive content, mark this criterion as Not Applicable.`,confidence:s==="pass"?.6:.9,steps:r.steps,pageUrl:e.pageUrl,finishedAt:new Date().toISOString(),costUsd:0,model:s==="pass"?"heuristic-reconciled":"heuristic-degenerate-walk"};return await ne(A),{ok:!0,result:A}}const{forwardSelectors:l,reverseSelectors:h,stuckRuns:m}=d,u=l.slice(0,-1).reverse(),g=[];for(let s=0;s<Math.min(u.length,h.length);s++){const A=u[s],S=h[s];A!==S&&g.push({index:s,expected:A,actual:S})}const f=Vl(u,h,g,m);if(f.detected){const s={componentId:e.componentId,criterionId:bt,verdict:"pass",reasoning:f.reasoning,confidence:.95,steps:r.steps,pageUrl:e.pageUrl,finishedAt:new Date().toISOString(),costUsd:0,model:"heuristic-only"};return await ne(s),{ok:!0,result:s}}const p=await ka(bt,{forwardSequence:l,reverseSequence:h,stuckRuns:m,reverseMismatches:g.slice(0,10)}),y=await Ia(e.componentId,bt,p);if(y.hit){Fa.info(`2.1.2 inputs unchanged (hash match); reusing verdict=${y.previous.verdict}`);const s=xa(y.previous,p);return await ne(s),{ok:!0,result:s}}const b=y.previous?{verdict:y.previous.verdict,reasoning:y.previous.reasoning,confidence:y.previous.confidence}:void 0,v=await Ke(()=>o.judgeKeyboardTrap({pageUrl:e.pageUrl,forwardSequence:l,reverseSequence:h,stuckRuns:m,reverseMismatches:g.slice(0,10),previousVerdict:b,signal:e.signal})),w={componentId:e.componentId,criterionId:bt,verdict:v.verdict==="pass"?"pass":v.verdict==="fail"?"fail":"uncertain",reasoning:v.reasoning,confidence:v.confidence,steps:r.steps,pageUrl:e.pageUrl,finishedAt:new Date().toISOString(),costUsd:v.costUsd,model:v.model,inputHash:p};return await ne(w),{ok:!0,result:w}}catch(i){return Fa.error("keyboard-trap audit failed",i),{ok:!1,error:i instanceof Error?i.message:String(i)}}finally{await r.dispose()}}async function po(e){await e.session.send("Runtime.evaluate",{expression:"(() => { if (document.activeElement && document.activeElement !== document.body) document.activeElement.blur(); window.scrollTo(0, 0); })()"})}async function fo(e,t,a){const o=[],r=[];for(let m=0;m<t&&!(a!=null&&a.aborted);m++){await e.pressKey("Tab");const u=await e.recordStep(`Tab #${m+1}`,{screenshot:!1});if(u.focused.isBody){if(o.length===0)continue;break}o.push(u.focused.selector);const g=On(u.focused);if(r.length>2&&g===r[0])break;r.push(g)}const i=[];let c=0;for(let m=1;m<=r.length;m++)if(m===r.length||r[m]!==r[m-1]){const u=m-c;u>=3&&i.push(`${o[c]} (×${u} consecutive)`),c=m}const d=[],h=Math.min(50,t);for(let m=0;m<h&&!(a!=null&&a.aborted);m++){await e.pressKey("Tab",{shift:!0});const u=await e.recordStep(`Shift+Tab #${m+1}`,{screenshot:!1});if(u.focused.isBody||(d.push(u.focused.selector),d.length>=o.length))break}return{forwardSelectors:o,reverseSelectors:d,stuckRuns:i}}function Wl(e){return e>0?"pass":"uncertain"}function Vl(e,t,a,n){if(e.length<3||t.length<3)return{detected:!1};if(n.length>0)return{detected:!1};const o=Math.min(e.length,t.length);if(a.length<o-1)return{detected:!1};const r=b=>{let v=0;for(let w=0;w<e.length;w++){const s=t[w+b];s!==void 0&&s===e[w]&&v++}return v},i=r(0),c=r(1),d=r(-1),l=Math.floor(e.length*.7),h=Math.max(c,d);if(h<l||h<=i)return{detected:!1};const m=new Set(e),u=new Set(t),g=[...m].filter(b=>u.has(b)),f=new Set([...m,...u]);return f.size-g.length>2?{detected:!1}:{detected:!0,reasoning:`Comparator artifact detected: every reverse-walk position mismatched by the same ${c>=d?"+1":"-1"} offset, but the SET of focused elements matches the forward walk (${g.length}/${f.size} elements common) and no stuck runs occurred. This is the comparator's index math being off-by-one against actual Shift-Tab semantics, not a real keyboard trap. Pass.`}}const go=te("focus-visible-audit"),_a="2.4.7",mo=30,jl=10;async function Gl(e){var i;const t=await chrome.storage.local.get("aiConfig"),a=ve(t.aiConfig),n=ue(a);if(!n.ok)return{ok:!1,error:n.reason};const o=n.client;if(typeof o.judgeFocusVisible!="function")return{ok:!1,error:"AI client does not support judgeFocusVisible yet."};const r=new Rn(e.tabId);try{await r.attach(),await ea(r),await Hl(r);const c=[],d=await r.countFocusables(),l=d>0?Math.min(mo,d):mo,h=new Set;for(let p=0;p<l&&c.length<jl;p++){if((i=e.signal)!=null&&i.aborted)return{ok:!1,error:"Audit cancelled."};await r.pressKey("Tab");const y=await r.recordStep(`Tab #${p+1}`,{screenshot:!0});if(y.focused.isBody)break;const b=On(y.focused);if(h.has(b))break;h.add(b);const v=await Bl(r);y.screenshot&&c.push({ordinal:c.length+1,selector:y.focused.selector,role:y.focused.role,name:y.focused.name,rect:y.focused.rect,screenshot:y.screenshot,focusStyle:v})}if(c.length===0)return{ok:!1,error:"No focusable elements found on the page; cannot evaluate 2.4.7."};const m=await ka(_a,{samples:c.map(p=>({ordinal:p.ordinal,selector:p.selector,role:p.role,focusStyle:p.focusStyle,rect:{x:Math.round(p.rect.x),y:Math.round(p.rect.y),w:Math.round(p.rect.width),h:Math.round(p.rect.height)}}))}),u=await Ia(e.componentId,_a,m);if(u.hit){go.info(`2.4.7 inputs unchanged (hash match); reusing verdict=${u.previous.verdict}`);const p=xa(u.previous,m);return await ne(p),{ok:!0,result:p}}const g=await Ke(()=>o.judgeFocusVisible({pageUrl:e.pageUrl,samples:c,signal:e.signal})),f={componentId:e.componentId,criterionId:_a,verdict:g.verdict==="pass"?"pass":g.verdict==="fail"?"fail":"uncertain",reasoning:g.reasoning,confidence:g.confidence,steps:r.steps,pageUrl:e.pageUrl,finishedAt:new Date().toISOString(),costUsd:g.costUsd,model:g.model,inputHash:m};return await ne(f),{ok:!0,result:f}}catch(c){return go.error("focus-visible audit failed",c),{ok:!1,error:c instanceof Error?c.message:String(c)}}finally{await r.dispose()}}async function Hl(e){await e.session.send("Runtime.evaluate",{expression:"(() => { if (document.activeElement && document.activeElement !== document.body) document.activeElement.blur(); window.scrollTo(0, 0); })()"})}async function Bl(e){const t=`(() => {
832
+ })()`,jl=[/no parseable JSON/i,/invalid JSON/i];function ho(e){if(e.verdict!=="uncertain"||e.confidence!==0)return!1;const t=e.reasoning??"";return jl.some(a=>a.test(t))}async function Xe(e){const t=await e();if(!ho(t))return t;try{const a=await e();if(!ho(a))return a}catch{}return t}async function Hl(e,t){if(t.length===0||typeof OffscreenCanvas>"u"||typeof createImageBitmap>"u")return e;try{const n=await(await fetch(e)).blob(),o=await createImageBitmap(n),r=new OffscreenCanvas(o.width,o.height),i=r.getContext("2d");if(!i)return e;i.drawImage(o,0,0);const c=Math.max(...t.map(p=>p.rect.x+p.rect.width),0),d=c>0&&o.width/c>1.5?o.width/Math.max(1280,c):1;for(const p of t){const f=(p.rect.x+p.rect.width/2)*d,y=(p.rect.y+p.rect.height/2)*d,b=16*d;i.fillStyle="rgba(220, 38, 38, 0.92)",i.strokeStyle="rgba(255, 255, 255, 0.95)",i.lineWidth=2*d,i.beginPath(),i.arc(f,y,b,0,2*Math.PI),i.fill(),i.stroke(),i.fillStyle="white",i.font=`bold ${Math.round(18*d)}px sans-serif`,i.textAlign="center",i.textBaseline="middle",i.fillText(String(p.ordinal),f,y)}const h=await(await r.convertToBlob({type:"image/jpeg",quality:.85})).arrayBuffer(),m=new Uint8Array(h);let u="";const g=8192;for(let p=0;p<m.byteLength;p+=g){const f=m.subarray(p,Math.min(p+g,m.byteLength));u+=String.fromCharCode(...f)}return`data:image/jpeg;base64,${btoa(u)}`}catch{return e}}async function na(e,t={}){var i;const a=Date.now(),n=5e3,o=200,r=e;for(;Date.now()-a<n;){const d=(i=(await r.session.send("Runtime.evaluate",{expression:`(() => ({ ready: document.readyState === "complete", focusables: document.querySelectorAll('a[href], button:not([disabled]), input:not([disabled]):not([type="hidden"]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"]):not([disabled]), [contenteditable="true"]').length }))()`,returnByValue:!0})).result)==null?void 0:i.value;if(d&&d.ready&&(d.focusables??0)>0)break;await new Promise(l=>setTimeout(l,o))}t.extraDelayMs&&t.extraDelayMs>0&&await new Promise(c=>setTimeout(c,t.extraDelayMs))}function Dn(e){const t=(e.name??"").trim().slice(0,60),a=Math.round(e.rect.x),n=Math.round(e.rect.y),o=Math.round(e.rect.width),r=Math.round(e.rect.height);return`${e.selector}|${e.tag}|${t}|${a},${n},${o},${r}`}async function Bl(e,t){const a=JSON.stringify({criterionId:e,sequence:t.map(i=>({o:i.ordinal,s:i.selector,r:i.role,x:Math.round(i.rect.x),y:Math.round(i.rect.y),w:Math.round(i.rect.width),h:Math.round(i.rect.height)}))}),n=new TextEncoder().encode(a),o=await crypto.subtle.digest("SHA-256",n);return Array.from(new Uint8Array(o)).map(i=>i.toString(16).padStart(2,"0")).join("")}const Fa=te("focus-order-audit"),_a="2.4.3",po=50;async function ql(e){var i,c,d;const t=await chrome.storage.local.get("aiConfig"),a=ve(t.aiConfig),n=he(a);if(!n.ok)return{ok:!1,error:n.reason};const o=n.client;if(typeof o.judgeFocusOrder!="function")return{ok:!1,error:"AI client does not support judgeFocusOrder yet."};const r=new Mn(e.tabId);try{await r.attach(),await na(r),await zl(r),await r.recordStep("initial",{screenshot:!0});const l=await r.countFocusables(),h=l>0?Math.min(po,l):po,m=new Set;for(let s=0;s<h;s++){if((i=e.signal)!=null&&i.aborted)return{ok:!1,error:"Audit cancelled."};await r.pressKey("Tab");const v=await r.recordStep(`Tab #${s+1}`,{screenshot:!1});if(v.focused.isBody)break;const S=Dn(v.focused);if(m.has(S))break;m.add(S),(c=e.onStep)==null||c.call(e,{ordinal:v.ordinal,selector:v.focused.selector,role:v.focused.role,name:v.focused.name})}const u=r.steps.filter(s=>s.action!=="initial"&&!s.focused.isBody).map(s=>({ordinal:s.ordinal,selector:s.focused.selector,role:s.focused.role,name:s.focused.name,rect:s.focused.rect})),g=(d=r.steps[0])==null?void 0:d.screenshot,p=g?await Hl(g,u):void 0,f=await Bl(_a,u);let y,b=null;try{b=await Fe(e.componentId,_a),b&&!jt(b.reasoning)?y={verdict:b.verdict,reasoning:b.reasoning,confidence:b.confidence}:b&&jt(b.reasoning)&&Fa.info("Skipping anchor — previous 2.4.3 verdict was cascade-escalated (system-imposed); letting primary judge fresh")}catch{}if(b&&b.inputHash===f&&!jt(b.reasoning)){Fa.info(`2.4.3 inputs unchanged (hash match: ${f.slice(0,8)}…); reusing previous verdict=${b.verdict} without AI call`);const s={...b,finishedAt:new Date().toISOString(),costUsd:0,model:`${b.model} (cached, hash-match)`,inputHash:f};return await oe(s),{ok:!0,result:s}}const A=await Xe(()=>o.judgeFocusOrder({pageUrl:e.pageUrl,pageScreenshot:p,tabSequence:u,previousVerdict:y,signal:e.signal})),w={componentId:e.componentId,criterionId:_a,verdict:A.verdict==="pass"?"pass":A.verdict==="fail"?"fail":"uncertain",reasoning:A.reasoning,confidence:A.confidence,steps:r.steps,pageUrl:e.pageUrl,finishedAt:new Date().toISOString(),costUsd:A.costUsd,model:A.model,inputHash:f};return await oe(w),{ok:!0,result:w}}catch(l){return Fa.error("focus-order audit failed",l),{ok:!1,error:l instanceof Error?l.message:String(l)}}finally{await r.dispose()}}async function zl(e){await e.session.send("Runtime.evaluate",{expression:"(() => { if (document.activeElement && document.activeElement !== document.body) document.activeElement.blur(); window.scrollTo(0, 0); })()"})}async function Sa(e,t){const a=JSON.stringify({criterionId:e,input:t}),n=new TextEncoder().encode(a),o=await crypto.subtle.digest("SHA-256",n);return Array.from(new Uint8Array(o)).map(i=>i.toString(16).padStart(2,"0")).join("")}async function Ca(e,t,a){const n=await Fe(e,t);return n?n.inputHash!==a?{hit:!1,previous:n}:n.verdict==="uncertain"?{hit:!1,previous:n}:jt(n.reasoning)?{hit:!1,previous:n}:{hit:!0,previous:n}:{hit:!1,previous:null}}function $a(e,t){return{...e,finishedAt:new Date().toISOString(),costUsd:0,model:`${e.model} (cached, hash-match)`,inputHash:t}}const Ua=te("keyboard-trap-audit"),vt="2.1.2",fo=50;async function Kl(e){const t=await chrome.storage.local.get("aiConfig"),a=ve(t.aiConfig),n=he(a);if(!n.ok)return{ok:!1,error:n.reason};const o=n.client;if(typeof o.judgeKeyboardTrap!="function")return{ok:!1,error:"AI client does not support judgeKeyboardTrap yet."};const r=new Mn(e.tabId);try{await r.attach(),await na(r),await go(r),await r.recordStep("initial",{screenshot:!0});const i=await r.countFocusables(),c=i>0?Math.min(fo,i):fo;let d=await mo(r,c,e.signal);if(d.forwardSelectors.length===0&&d.reverseSelectors.length>0&&(Ua.info("2.1.2 detected degenerate 0/N walk — retrying after extra hydration wait"),await na(r,{extraDelayMs:1500}),await go(r),d=await mo(r,c,e.signal)),d.forwardSelectors.length===0){const s=Yl(i),v={componentId:e.componentId,criterionId:vt,verdict:s,reasoning:s==="pass"?`The page has ${i} focusable element${i===1?"":"s"} (confirmed by a direct focusable-element count), but the automated forward Tab walk could not traverse them in this session — focus did not enter the page document. This is a known timing/session artifact (most common when this check runs after another keyboard walkthrough during a crawl), not evidence of a problem. No keyboard trap was observed and interactive content is present, so this passes. If you suspect a trap, open the page directly and Tab through it manually.`:`Could not run a meaningful keyboard-trap evaluation: the forward Tab walk found no focusable elements and the page reports none (forward: 0, reverse: ${d.reverseSelectors.length}). Likely a page with no interactive content, or one that is auth-gated / blocks automation. If the page genuinely has no interactive content, mark this criterion as Not Applicable.`,confidence:s==="pass"?.6:.9,steps:r.steps,pageUrl:e.pageUrl,finishedAt:new Date().toISOString(),costUsd:0,model:s==="pass"?"heuristic-reconciled":"heuristic-degenerate-walk"};return await oe(v),{ok:!0,result:v}}const{forwardSelectors:l,reverseSelectors:h,stuckRuns:m}=d,u=l.slice(0,-1).reverse(),g=[];for(let s=0;s<Math.min(u.length,h.length);s++){const v=u[s],S=h[s];v!==S&&g.push({index:s,expected:v,actual:S})}const p=Jl(u,h,g,m);if(p.detected){const s={componentId:e.componentId,criterionId:vt,verdict:"pass",reasoning:p.reasoning,confidence:.95,steps:r.steps,pageUrl:e.pageUrl,finishedAt:new Date().toISOString(),costUsd:0,model:"heuristic-only"};return await oe(s),{ok:!0,result:s}}const f=await Sa(vt,{forwardSequence:l,reverseSequence:h,stuckRuns:m,reverseMismatches:g.slice(0,10)}),y=await Ca(e.componentId,vt,f);if(y.hit){Ua.info(`2.1.2 inputs unchanged (hash match); reusing verdict=${y.previous.verdict}`);const s=$a(y.previous,f);return await oe(s),{ok:!0,result:s}}const b=y.previous?{verdict:y.previous.verdict,reasoning:y.previous.reasoning,confidence:y.previous.confidence}:void 0,A=await Xe(()=>o.judgeKeyboardTrap({pageUrl:e.pageUrl,forwardSequence:l,reverseSequence:h,stuckRuns:m,reverseMismatches:g.slice(0,10),previousVerdict:b,signal:e.signal})),w={componentId:e.componentId,criterionId:vt,verdict:A.verdict==="pass"?"pass":A.verdict==="fail"?"fail":"uncertain",reasoning:A.reasoning,confidence:A.confidence,steps:r.steps,pageUrl:e.pageUrl,finishedAt:new Date().toISOString(),costUsd:A.costUsd,model:A.model,inputHash:f};return await oe(w),{ok:!0,result:w}}catch(i){return Ua.error("keyboard-trap audit failed",i),{ok:!1,error:i instanceof Error?i.message:String(i)}}finally{await r.dispose()}}async function go(e){await e.session.send("Runtime.evaluate",{expression:"(() => { if (document.activeElement && document.activeElement !== document.body) document.activeElement.blur(); window.scrollTo(0, 0); })()"})}async function mo(e,t,a){const o=[],r=[];for(let m=0;m<t&&!(a!=null&&a.aborted);m++){await e.pressKey("Tab");const u=await e.recordStep(`Tab #${m+1}`,{screenshot:!1});if(u.focused.isBody){if(o.length===0)continue;break}o.push(u.focused.selector);const g=Dn(u.focused);if(r.length>2&&g===r[0])break;r.push(g)}const i=[];let c=0;for(let m=1;m<=r.length;m++)if(m===r.length||r[m]!==r[m-1]){const u=m-c;u>=3&&i.push(`${o[c]} (×${u} consecutive)`),c=m}const d=[],h=Math.min(50,t);for(let m=0;m<h&&!(a!=null&&a.aborted);m++){await e.pressKey("Tab",{shift:!0});const u=await e.recordStep(`Shift+Tab #${m+1}`,{screenshot:!1});if(u.focused.isBody||(d.push(u.focused.selector),d.length>=o.length))break}return{forwardSelectors:o,reverseSelectors:d,stuckRuns:i}}function Yl(e){return e>0?"pass":"uncertain"}function Jl(e,t,a,n){if(e.length<3||t.length<3)return{detected:!1};if(n.length>0)return{detected:!1};const o=Math.min(e.length,t.length);if(a.length<o-1)return{detected:!1};const r=b=>{let A=0;for(let w=0;w<e.length;w++){const s=t[w+b];s!==void 0&&s===e[w]&&A++}return A},i=r(0),c=r(1),d=r(-1),l=Math.floor(e.length*.7),h=Math.max(c,d);if(h<l||h<=i)return{detected:!1};const m=new Set(e),u=new Set(t),g=[...m].filter(b=>u.has(b)),p=new Set([...m,...u]);return p.size-g.length>2?{detected:!1}:{detected:!0,reasoning:`Comparator artifact detected: every reverse-walk position mismatched by the same ${c>=d?"+1":"-1"} offset, but the SET of focused elements matches the forward walk (${g.length}/${p.size} elements common) and no stuck runs occurred. This is the comparator's index math being off-by-one against actual Shift-Tab semantics, not a real keyboard trap. Pass.`}}const bo=te("focus-visible-audit"),Wa="2.4.7",yo=30,Xl=10;async function Ql(e){var i;const t=await chrome.storage.local.get("aiConfig"),a=ve(t.aiConfig),n=he(a);if(!n.ok)return{ok:!1,error:n.reason};const o=n.client;if(typeof o.judgeFocusVisible!="function")return{ok:!1,error:"AI client does not support judgeFocusVisible yet."};const r=new Mn(e.tabId);try{await r.attach(),await na(r),await Zl(r);const c=[],d=await r.countFocusables(),l=d>0?Math.min(yo,d):yo,h=new Set;for(let f=0;f<l&&c.length<Xl;f++){if((i=e.signal)!=null&&i.aborted)return{ok:!1,error:"Audit cancelled."};await r.pressKey("Tab");const y=await r.recordStep(`Tab #${f+1}`,{screenshot:!0});if(y.focused.isBody)break;const b=Dn(y.focused);if(h.has(b))break;h.add(b);const A=await ed(r);y.screenshot&&c.push({ordinal:c.length+1,selector:y.focused.selector,role:y.focused.role,name:y.focused.name,rect:y.focused.rect,screenshot:y.screenshot,focusStyle:A})}if(c.length===0)return{ok:!1,error:"No focusable elements found on the page; cannot evaluate 2.4.7."};const m=await Sa(Wa,{samples:c.map(f=>({ordinal:f.ordinal,selector:f.selector,role:f.role,focusStyle:f.focusStyle,rect:{x:Math.round(f.rect.x),y:Math.round(f.rect.y),w:Math.round(f.rect.width),h:Math.round(f.rect.height)}}))}),u=await Ca(e.componentId,Wa,m);if(u.hit){bo.info(`2.4.7 inputs unchanged (hash match); reusing verdict=${u.previous.verdict}`);const f=$a(u.previous,m);return await oe(f),{ok:!0,result:f}}const g=await Xe(()=>o.judgeFocusVisible({pageUrl:e.pageUrl,samples:c,signal:e.signal})),p={componentId:e.componentId,criterionId:Wa,verdict:g.verdict==="pass"?"pass":g.verdict==="fail"?"fail":"uncertain",reasoning:g.reasoning,confidence:g.confidence,steps:r.steps,pageUrl:e.pageUrl,finishedAt:new Date().toISOString(),costUsd:g.costUsd,model:g.model,inputHash:m};return await oe(p),{ok:!0,result:p}}catch(c){return bo.error("focus-visible audit failed",c),{ok:!1,error:c instanceof Error?c.message:String(c)}}finally{await r.dispose()}}async function Zl(e){await e.session.send("Runtime.evaluate",{expression:"(() => { if (document.activeElement && document.activeElement !== document.body) document.activeElement.blur(); window.scrollTo(0, 0); })()"})}async function ed(e){const t=`(() => {
825
833
  const el = document.activeElement;
826
834
  if (!el || el === document.body) return JSON.stringify({
827
835
  outlineStyle: '', outlineWidth: '', outlineColor: '', outlineOffset: '',
@@ -837,25 +845,25 @@ Respond with strict JSON (no markdown fences):
837
845
  border: cs.border,
838
846
  backgroundColor: cs.backgroundColor,
839
847
  });
840
- })()`;try{const a=await e.session.send("Runtime.evaluate",{expression:t,returnByValue:!0});return JSON.parse(a.result.value)}catch{return{outlineStyle:"",outlineWidth:"",outlineColor:"",outlineOffset:"",boxShadow:"",border:"",backgroundColor:""}}}const bo=te("reading-order-audit"),Ft="1.3.2";async function zl(e){var h,m,u;const t=await chrome.storage.local.get("aiConfig"),a=ve(t.aiConfig),n=ue(a);if(!n.ok)return{ok:!1,error:n.reason};const o=n.client;if(typeof o.judgeReadingOrder!="function")return{ok:!1,error:"AI client does not support judgeReadingOrder yet."};const r=((h=e.results[0])==null?void 0:h.readingOrderIssues)??[],i=(m=e.results[0])==null?void 0:m.screenshotDataUrl;if(r.length===0){const g={componentId:e.componentId,criterionId:Ft,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 ne(g),{ok:!0,result:g}}if((u=e.signal)!=null&&u.aborted)return{ok:!1,error:"Audit cancelled."};const c=await ka(Ft,{flaggedIssues:r.map(g=>({selector:g.selector,textSnippet:g.textSnippet,domIndex:g.domIndex,visualIndex:g.visualIndex}))}),d=await Ia(e.componentId,Ft,c);if(d.hit){bo.info(`1.3.2 inputs unchanged (hash match); reusing verdict=${d.previous.verdict}`);const g=xa(d.previous,c);return await ne(g),{ok:!0,result:g}}const l=d.previous?{verdict:d.previous.verdict,reasoning:d.previous.reasoning,confidence:d.previous.confidence}:void 0;try{const g=await Ke(()=>o.judgeReadingOrder({pageUrl:e.pageUrl,pageScreenshot:i,flaggedIssues:r.map(p=>({selector:p.selector,textSnippet:p.textSnippet,domIndex:p.domIndex,visualIndex:p.visualIndex,rect:p.boundingRect})),previousVerdict:l,signal:e.signal})),f={componentId:e.componentId,criterionId:Ft,verdict:g.verdict==="pass"?"pass":g.verdict==="fail"?"fail":"uncertain",reasoning:g.reasoning,confidence:g.confidence,steps:[],pageUrl:e.pageUrl,finishedAt:new Date().toISOString(),costUsd:g.costUsd,model:g.model,inputHash:c};return await ne(f),{ok:!0,result:f}}catch(g){return bo.error("reading-order audit failed",g),{ok:!1,error:g instanceof Error?g.message:String(g)}}}const yo=te("non-text-contrast-audit"),_t="1.4.11";async function ql(e){var d,l,h,m;const t=await chrome.storage.local.get("aiConfig"),a=ve(t.aiConfig),n=ue(a);if(!n.ok)return{ok:!1,error:n.reason};const o=n.client;if(typeof o.judgeNonTextContrast!="function")return{ok:!1,error:"AI client does not support judgeNonTextContrast yet."};const r=(d=e.results[0])==null?void 0:d.screenshotDataUrl;if(!r)return{ok:!1,error:"No page screenshot available — re-run the audit so a screenshot is captured."};if((l=e.signal)!=null&&l.aborted)return{ok:!1,error:"Audit cancelled."};let i=null;if(e.tabId!==void 0)try{const u=await chrome.scripting.executeScript({target:{tabId:e.tabId},func:Kl});i=((h=u==null?void 0:u[0])==null?void 0:h.result)??null}catch(u){yo.warn("component contrast measurement failed; falling back to AI",u)}if(i&&i.measured.length>=1){const u=i.measured.filter(p=>p.ratio<3&&p.confident),g=i.measured.filter(p=>p.ratio>=3),f=i.measured.filter(p=>!p.confident);if(u.length>0){const p={componentId:e.componentId,criterionId:_t,verdict:"fail",reasoning:`Programmatic measurement: ${u.length} of ${i.measured.length} measured components fail WCAG 1.4.11 (< 3:1 contrast against adjacent background). `+u.slice(0,6).map(y=>`${y.selector} — ${y.ratio.toFixed(2)}:1 (${y.borderColor} on ${y.backgroundColor})`).join("; ")+(g.length>0?` · ${g.length} other components pass.`:""),confidence:.98,steps:[],pageUrl:e.pageUrl,finishedAt:new Date().toISOString(),costUsd:0,model:"programmatic-contrast-measurement"};return await ne(p),{ok:!0,result:p}}if(f.length===0&&g.length===i.measured.length){const p={componentId:e.componentId,criterionId:_t,verdict:"pass",reasoning:`Programmatic measurement: ${g.length===1?"the":"all"} ${g.length} measured component${g.length===1?"":"s"} pass${g.length===1?"es":""} WCAG 1.4.11 (≥ 3:1 contrast against adjacent background). Lowest measured: ${(m=g.map(y=>y.ratio).sort((y,b)=>y-b)[0])==null?void 0:m.toFixed(2)}:1. Decorative-only graphics and components with transparent borders over images / gradients (which programmatic measurement cannot resolve) are not in scope of this measurement; if such components exist on the page they should be inspected separately.`,confidence:.95,steps:[],pageUrl:e.pageUrl,finishedAt:new Date().toISOString(),costUsd:0,model:"programmatic-contrast-measurement"};return await ne(p),{ok:!0,result:p}}}let c;try{const u=await Pe(e.componentId,_t);u&&(c={verdict:u.verdict,reasoning:u.reasoning,confidence:u.confidence})}catch{}try{const u=await Ke(()=>o.judgeNonTextContrast({pageUrl:e.pageUrl,pageScreenshot:r,previousVerdict:c,signal:e.signal})),g={componentId:e.componentId,criterionId:_t,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 ne(g),{ok:!0,result:g}}catch(u){return yo.error("non-text-contrast audit failed",u),{ok:!1,error:u instanceof Error?u.message:String(u)}}}function Kl(){function e(f){const p=f.match(/rgba?\(\s*([0-9.]+)\s*,?\s*([0-9.]+)\s*,?\s*([0-9.]+)(?:\s*[,/]\s*([0-9.]+))?\s*\)/i);if(!p)return null;const y=Math.round(parseFloat(p[1])),b=Math.round(parseFloat(p[2])),v=Math.round(parseFloat(p[3])),w=p[4]!==void 0?parseFloat(p[4]):1;return{r:y,g:b,b:v,a:w}}function t(f){const p=y=>{const b=y/255;return b<=.03928?b/12.92:Math.pow((b+.055)/1.055,2.4)};return .2126*p(f.r)+.7152*p(f.g)+.0722*p(f.b)}function a(f,p){const y=t(f),b=t(p),v=Math.max(y,b),w=Math.min(y,b);return(v+.05)/(w+.05)}function n(f,p){const y=Math.max(0,Math.min(1,f.a));return{r:Math.round(f.r*y+p.r*(1-y)),g:Math.round(f.g*y+p.g*(1-y)),b:Math.round(f.b*y+p.b*(1-y))}}function o(f){let p=f.parentElement;for(;p&&p!==document.documentElement;){const y=getComputedStyle(p);if(y.backgroundImage&&y.backgroundImage!=="none")return{color:y.backgroundColor,hasImage:!0};const v=e(y.backgroundColor);if(v&&v.a>.5)return{color:y.backgroundColor,hasImage:!1};p=p.parentElement}return{color:"rgb(255, 255, 255)",hasImage:!1}}function r(f){if(f.id)return`#${f.id}`;const p=f.tagName.toLowerCase(),y=f.className&&typeof f.className=="string"?f.className.split(/\s+/).filter(Boolean).slice(0,2).join("."):"";return y?`${p}.${y}`:p}function i(f,p){const y=getComputedStyle(f),b=e(y.backgroundColor);return b?b.a>=.999?b:b.a<=.001?p:n(b,p):p}function c(f,p){if((f.textContent??"").trim().length===0)return!1;const y=document.createTreeWalker(f,NodeFilter.SHOW_TEXT);let b;for(;b=y.nextNode();){if((b.textContent??"").trim().length===0)continue;const w=b.parentElement;if(!w)continue;const s=getComputedStyle(w);if(s.visibility==="hidden"||s.display==="none")continue;if(s.position==="absolute"){const P=parseFloat(s.width),T=parseFloat(s.height);if(P>0&&P<=1||T>0&&T<=1||s.clip==="rect(0px, 0px, 0px, 0px)"||s.clipPath==="inset(50%)")continue}const A=e(s.color);if(!A)continue;const S=A.a<1?n(A,p):A,k=a(S,p),I=parseFloat(s.fontSize)||16,L=parseInt(s.fontWeight,10)||400,_=I>=24||I>=18.5&&L>=700?3:4.5;if(k>=_)return!0}return!1}function d(f){const p=f.tagName.toLowerCase();if(p==="textarea"||p==="select")return!0;if(p==="input"){const y=(f.getAttribute("type")??"text").toLowerCase();return y!=="hidden"&&y!=="checkbox"&&y!=="radio"&&y!=="submit"&&y!=="button"&&y!=="reset"&&y!=="image"}return!1}function l(f,p){const y=document.activeElement;try{f.focus({preventScroll:!0})}catch{return null}if(document.activeElement!==f){if(y)try{y.focus({preventScroll:!0})}catch{}return null}const b=getComputedStyle(f);let v=0,w="";const s=b.outlineStyle,A=parseFloat(b.outlineWidth)||0;if(s!=="none"&&A>0){const k=e(b.outlineColor);if(k){const I=k.a<1?n(k,p):k,L=a(I,p);L>v&&(v=L,w=`outline ${A}px ${b.outlineColor}`)}}const S=b.boxShadow;if(S&&S!=="none"){const k=e(S);if(k){const I=k.a<1?n(k,p):k,L=a(I,p);L>v&&(v=L,w=`box-shadow ${S.slice(0,60)}`)}}try{f.blur()}catch{}if(y)try{y.focus({preventScroll:!0})}catch{}return v===0?null:{ratio:v,indicator:w}}function h(f){const p=[{attr:"aria-current",activeVal:"page"},{attr:"aria-pressed",activeVal:"true"},{attr:"aria-selected",activeVal:"true"},{attr:"aria-checked",activeVal:"true"}];let y=null;for(const $ of p){const _=f.getAttribute($.attr);if(_&&($.attr==="aria-current"?_!=="false"&&_!=="":_===$.activeVal)){y=$;break}}if(!y)return null;const b=f.parentElement;if(!b)return null;const v=f.tagName.toLowerCase(),w=Array.from(b.children).filter($=>{if(!($ instanceof HTMLElement)||$===f||$.tagName.toLowerCase()!==v)return!1;const _=$.getAttribute(y.attr);return _?y.attr==="aria-current"?_==="false"||_==="":_!==y.activeVal:!0});if(w.length===0)return null;const s=w[0],A=getComputedStyle(f),S=getComputedStyle(s),k=[["backgroundColor","background"],["color","text color"],["borderTopColor","border"]],I=o(f),L=e(I.color);if(!L)return null;for(const[$,_]of k){const P=e(A[$]),T=e(S[$]);if(!P||!T||P.r===T.r&&P.g===T.g&&P.b===T.b&&P.a===T.a)continue;const N=P.a<1?n(P,L):P,M=T.a<1?n(T,L):T,D=a(N,M);return{activeColor:N,inactiveColor:M,ratio:D,ariaMarker:`${y.attr}="${f.getAttribute(y.attr)}" vs ${_}`}}return null}const u=Array.from(document.querySelectorAll('input:not([type="hidden"]):not([type="checkbox"]):not([type="radio"]), button, select, textarea, a[href][aria-current], a[href][role="tab"], [role="tab"], [role="button"][aria-pressed], [role="checkbox"][aria-checked]')).filter(f=>{const p=f.getBoundingClientRect();if(p.width<8||p.height<8)return!1;const y=getComputedStyle(f);return!(y.visibility==="hidden"||y.display==="none")}),g=[];for(const f of u){const p=getComputedStyle(f),y=o(f),b=y.hasImage?null:e(y.color),v=b?i(f,b):null;if(b&&v){const k=h(f);k&&k.ratio<3&&g.push({selector:r(f),role:f.getAttribute("role")??f.tagName.toLowerCase(),borderColor:`state: ${k.ariaMarker}`,backgroundColor:`active rgb(${k.activeColor.r},${k.activeColor.g},${k.activeColor.b}) vs inactive rgb(${k.inactiveColor.r},${k.inactiveColor.g},${k.inactiveColor.b})`,ratio:k.ratio,confident:!0})}if(b){const k=l(f,b);k!==null&&k.ratio<3&&g.push({selector:r(f),role:f.getAttribute("role")??f.tagName.toLowerCase(),borderColor:`focus: ${k.indicator}`,backgroundColor:y.color,ratio:k.ratio,confident:!0})}if(parseFloat(p.borderTopWidth)+parseFloat(p.borderRightWidth)+parseFloat(p.borderBottomWidth)+parseFloat(p.borderLeftWidth)===0)continue;const s=e(p.borderTopColor);if(!s)continue;if(y.hasImage){g.push({selector:r(f),role:f.getAttribute("role")??f.tagName.toLowerCase(),borderColor:p.borderTopColor,backgroundColor:y.color,ratio:0,confident:!1});continue}if(!b||!v||!d(f)&&c(f,v))continue;const A=s.a<1?n(s,b):s,S=a(A,b);if(g.push({selector:r(f),role:f.getAttribute("role")??f.tagName.toLowerCase(),borderColor:p.borderTopColor,backgroundColor:y.color,ratio:S,confident:!0}),g.length>=20)break}return{measured:g}}const yt=te("label-in-name-audit"),Je="2.5.3";function Yl(){function e(c){return c.replace(/\s+/g," ").trim().toLowerCase()}function t(c){const d=c.cloneNode(!0);return d.querySelectorAll('[aria-hidden="true"]').forEach(l=>l.remove()),d.querySelectorAll(".sr-only, .visually-hidden, .screen-reader-text, .visuallyhidden").forEach(l=>l.remove()),(d.textContent??"").replace(/\s+/g," ").trim()}function a(c){var m,u,g,f;const d=c.getAttribute("aria-label");if(d&&d.trim())return d.trim();const l=c.getAttribute("aria-labelledby");if(l){const p=l.split(/\s+/).map(y=>document.getElementById(y)).filter(y=>y!==null).map(y=>{var b;return((b=y.textContent)==null?void 0:b.trim())??""}).filter(Boolean);if(p.length>0)return p.join(" ")}if(c.tagName==="INPUT"||c.tagName==="SELECT"||c.tagName==="TEXTAREA"){const p=c.getAttribute("id");if(p){const v=document.querySelector(`label[for="${p}"]`);if(v)return((m=v.textContent)==null?void 0:m.trim())??""}const y=c.closest("label");if(y)return((u=y.textContent)==null?void 0:u.trim())??"";const b=c.getAttribute("placeholder");if(b)return b.trim()}if(c.tagName==="BUTTON"||c.tagName==="A")return((g=c.textContent)==null?void 0:g.trim())??"";const h=c.getAttribute("value");return h&&c.type==="submit"?h.trim():((f=c.textContent)==null?void 0:f.trim())??""}function n(c){if(c.id)return`#${c.id}`;const d=c.tagName.toLowerCase(),l=c.className&&typeof c.className=="string"?"."+c.className.split(/\s+/).filter(Boolean).slice(0,2).join("."):"";return d+l}const o=["a[href]","button","input:not([type=hidden]):not([type=submit])[aria-label], input[aria-labelledby]","input[type=submit]","input[type=button]",'[role="button"]','[role="link"]','[role="checkbox"]','[role="radio"]','[role="switch"]','[role="tab"]','[role="menuitem"]'],r=Array.from(new Set(o.flatMap(c=>Array.from(document.querySelectorAll(c))))).filter(c=>{if(c.hasAttribute("disabled"))return!1;const d=c.getBoundingClientRect();if(d.width===0||d.height===0)return!1;const l=getComputedStyle(c);return!(l.visibility==="hidden"||l.display==="none")}),i=[];for(const c of r){const d=t(c),l=a(c);if(!d||!l)continue;const h=e(d),m=e(l);let u;m.includes(h)?u="pass":m.length===0||h.length===0?u="fail":u="ambiguous";const g=c.getAttribute("role")??c.tagName.toLowerCase();i.push({selector:n(c),role:g,visibleText:d.slice(0,200),accessibleName:l.slice(0,200),programmaticMatch:u})}return i}async function Jl(e){var g,f,p,y;if((g=e.signal)!=null&&g.aborted)return{ok:!1,error:"Audit cancelled."};let t;try{const v=(await chrome.scripting.executeScript({target:{tabId:e.tabId},world:"MAIN",func:Yl}))[0];if(!v||!v.result)return{ok:!1,error:"DOM walker returned no result."};t=v.result}catch(b){return yt.warn("DOM walker injection failed",b),{ok:!1,error:`DOM walker failed: ${b instanceof Error?b.message:String(b)}`}}const a=t.filter(b=>b.programmaticMatch==="fail"),n=t.filter(b=>b.programmaticMatch==="ambiguous"),o=t.length;if(o===0){const b={componentId:e.componentId,criterionId:Je,verdict:"pass",reasoning:"No interactive elements with both visible text AND a separate accessible name were found on the page. WCAG 2.5.3 has no applicable controls — counts as pass / not applicable.",confidence:.95,pageUrl:e.pageUrl,finishedAt:new Date().toISOString(),costUsd:0,model:"programmatic-only"};return await ne(b),{ok:!0,result:b}}if(a.length===0&&n.length===0){const b={componentId:e.componentId,criterionId:Je,verdict:"pass",reasoning:`${o} interactive controls audited; all visible labels are contained in their accessible names (case-insensitive substring match). WCAG 2.5.3 passes.`,confidence:.95,pageUrl:e.pageUrl,finishedAt:new Date().toISOString(),costUsd:0,model:"programmatic-only"};return await ne(b),yt.info(`2.5.3 passed programmatically (${o} candidates, all substring-matched)`),{ok:!0,result:b}}const r=await chrome.storage.local.get("aiConfig"),i=ve(r.aiConfig),c=ue(i);if(!c.ok){const b=`Programmatic-only verdict (no AI key configured): ${a.length} candidates with visible text NOT contained in their accessible name. Each of these is likely a WCAG 2.5.3 failure: ${a.slice(0,5).map(w=>`${w.role}/${w.selector} (visible="${w.visibleText.slice(0,50)}", accessible="${w.accessibleName.slice(0,50)}")`).join("; ")}${a.length>5?"; ...":""}. ${n.length} additional candidates are AMBIGUOUS (icon-prefix, plurals, smart-quotes) — configure an AI key to disambiguate.`,v={componentId:e.componentId,criterionId:Je,verdict:a.length>0?"fail":"uncertain",reasoning:b,confidence:.6,pageUrl:e.pageUrl,finishedAt:new Date().toISOString(),costUsd:0,model:"programmatic-only"};return await ne(v),{ok:!0,result:v}}const d=c.client;if(typeof d.judgeLabelInName!="function")return{ok:!1,error:"AI client does not support judgeLabelInName."};if((f=e.signal)!=null&&f.aborted)return{ok:!1,error:"Audit cancelled."};const l=(y=(p=e.results)==null?void 0:p[0])==null?void 0:y.screenshotDataUrl,h=[...a,...n],m=await ka(Je,{candidates:h.map(b=>({selector:b.selector,role:b.role,visibleText:b.visibleText,accessibleName:b.accessibleName,programmaticMatch:b.programmaticMatch}))}),u=await Ia(e.componentId,Je,m);if(u.hit){yt.info(`2.5.3 inputs unchanged (hash match); reusing verdict=${u.previous.verdict}`);const b=xa(u.previous,m);return await ne(b),{ok:!0,result:b}}try{const b=await Ke(()=>d.judgeLabelInName({pageUrl:e.pageUrl,pageScreenshot:l,candidates:h,signal:e.signal})),v=b.verdict==="pass"?"pass":b.verdict==="fail"?"fail":"uncertain",w={componentId:e.componentId,criterionId:Je,verdict:v,reasoning:`${o} interactive controls audited. ${t.length-h.length} passed the strict substring match; ${h.length} (${a.length} strict fails + ${n.length} ambiguous) sent to AI judgment. AI verdict: ${v}. `+b.reasoning,confidence:b.confidence,pageUrl:e.pageUrl,finishedAt:new Date().toISOString(),costUsd:b.costUsd,model:b.model,inputHash:m};return await ne(w),yt.info(`2.5.3 resolved to ${v} (${h.length} ambiguous cases AI-judged)`),{ok:!0,result:w}}catch(b){return yt.error("label-in-name AI judgment failed",b),{ok:!1,error:b instanceof Error?b.message:String(b)}}}const je=te("focus-order-cascade");function Xl(){const e=Array.from(document.querySelectorAll('a[href], button, input:not([type=hidden]), select, textarea, [tabindex]:not([tabindex="-1"])')).filter(r=>{if(r.hasAttribute("disabled"))return!1;const i=r.getBoundingClientRect();if(i.width===0||i.height===0)return!1;const c=getComputedStyle(r);return!(c.visibility==="hidden"||c.display==="none")});if(e.length<4)return{matches:!1,pattern:"none",evidence:`only ${e.length} focusable element(s) on page — cascade does not apply`,totalFocusables:e.length,largestCluster:0};const t=new Map;for(const r of e){let i=r.parentElement;for(;i&&i!==document.body&&i!==document.documentElement;){const c=getComputedStyle(i);let d="";if(c.display==="grid"||c.display==="inline-grid"){const l=c.gridTemplateColumns.trim().split(/\s+/).filter(Boolean);l.length>=2&&c.gridTemplateColumns!=="none"&&(d=`grid-${l.length}-cols`)}else if(c.display==="flex"||c.display==="inline-flex")c.flexDirection.startsWith("row")&&(c.flexWrap==="wrap"||c.flexWrap==="wrap-reverse"?d="flex-row-wrap":i.children.length>=2&&(d="flex-row"));else{const l=c.columnCount;l&&l!=="auto"&&parseInt(l,10)>=2&&(d=`css-columns-${l}`)}if(d){const l=t.get(i);l?l.count++:t.set(i,{count:1,pattern:d});break}i=i.parentElement}}let a={count:0,pattern:"none"};for(const r of t.values())r.count>a.count&&(a={count:r.count,pattern:r.pattern});const n=Math.max(2,Math.ceil(e.length*.3)),o=a.count>=n;return{matches:o,pattern:o?a.pattern:"none",evidence:o?`${a.count} of ${e.length} focusable elements share a ${a.pattern} container (threshold ${n})`:`largest multi-column cluster was ${a.count} of ${e.length} focusables; threshold was ${n} — no dominant banked layout`,totalFocusables:e.length,largestCluster:a.count}}function Ql(){const e='a[href], button:not([disabled]), input:not([disabled]):not([type="hidden"]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"]):not([disabled])',t=Array.from(document.querySelectorAll('nav, [role="navigation"], [role="group"], section[aria-labelledby], section[aria-label], aside[aria-labelledby], aside[aria-label], header[aria-labelledby], footer[aria-labelledby]')),a=t.filter(r=>t.some(i=>i!==r&&i.contains(r))?!1:!t.some(i=>i!==r&&r.contains(i)));let n=0;for(const r of a)r.querySelector(e)&&n++;const o=n>=2;return{matches:o,evidence:o?`${n} distinct labeled navigation regions found on page (e.g., multiple <nav> blocks in a footer). Column-first focus traversal across these groups is a valid pattern — screen readers announce each group's label before its contents.`:`${n} labeled navigation region(s) found — need ≥2 with focusable content to trigger multi-region override`,groupCount:a.length,groupsWithFocusables:n}}async function Zl(e){if(e.focusOrderVerdict!=="uncertain")return{ran:!1,reason:`2.4.3 verdict was ${e.focusOrderVerdict}, not uncertain — cascade does not apply`};if(e.keyboardTrapVerdict!=="pass")return{ran:!1,reason:`2.1.2 verdict was ${e.keyboardTrapVerdict??"absent"}, not pass — cascade does not apply (need empirical focus-path functionality)`};const t=await Pe(e.componentId,"2.4.3");if(!t)return{ran:!1,reason:"2.4.3 record not found in storage — cannot apply cascade"};let a=null;try{const i=(await chrome.scripting.executeScript({target:{tabId:e.tabId},world:"MAIN",func:Xl}))[0];i&&i.result&&(a=i.result)}catch(r){je.warn("focus-order cascade detector failed",r)}if(a&&a.matches){const r={...t,verdict:"pass",reasoning:t.reasoning+`
848
+ })()`;try{const a=await e.session.send("Runtime.evaluate",{expression:t,returnByValue:!0});return JSON.parse(a.result.value)}catch{return{outlineStyle:"",outlineWidth:"",outlineColor:"",outlineOffset:"",boxShadow:"",border:"",backgroundColor:""}}}const wo=te("reading-order-audit"),Wt="1.3.2";async function td(e){var h,m,u;const t=await chrome.storage.local.get("aiConfig"),a=ve(t.aiConfig),n=he(a);if(!n.ok)return{ok:!1,error:n.reason};const o=n.client;if(typeof o.judgeReadingOrder!="function")return{ok:!1,error:"AI client does not support judgeReadingOrder yet."};const r=((h=e.results[0])==null?void 0:h.readingOrderIssues)??[],i=(m=e.results[0])==null?void 0:m.screenshotDataUrl;if(r.length===0){const g={componentId:e.componentId,criterionId:Wt,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 oe(g),{ok:!0,result:g}}if((u=e.signal)!=null&&u.aborted)return{ok:!1,error:"Audit cancelled."};const c=await Sa(Wt,{flaggedIssues:r.map(g=>({selector:g.selector,textSnippet:g.textSnippet,domIndex:g.domIndex,visualIndex:g.visualIndex}))}),d=await Ca(e.componentId,Wt,c);if(d.hit){wo.info(`1.3.2 inputs unchanged (hash match); reusing verdict=${d.previous.verdict}`);const g=$a(d.previous,c);return await oe(g),{ok:!0,result:g}}const l=d.previous?{verdict:d.previous.verdict,reasoning:d.previous.reasoning,confidence:d.previous.confidence}:void 0;try{const g=await Xe(()=>o.judgeReadingOrder({pageUrl:e.pageUrl,pageScreenshot:i,flaggedIssues:r.map(f=>({selector:f.selector,textSnippet:f.textSnippet,domIndex:f.domIndex,visualIndex:f.visualIndex,rect:f.boundingRect})),previousVerdict:l,signal:e.signal})),p={componentId:e.componentId,criterionId:Wt,verdict:g.verdict==="pass"?"pass":g.verdict==="fail"?"fail":"uncertain",reasoning:g.reasoning,confidence:g.confidence,steps:[],pageUrl:e.pageUrl,finishedAt:new Date().toISOString(),costUsd:g.costUsd,model:g.model,inputHash:c};return await oe(p),{ok:!0,result:p}}catch(g){return wo.error("reading-order audit failed",g),{ok:!1,error:g instanceof Error?g.message:String(g)}}}const vo=te("non-text-contrast-audit"),Gt="1.4.11";async function ad(e){var d,l,h,m;const t=await chrome.storage.local.get("aiConfig"),a=ve(t.aiConfig),n=he(a);if(!n.ok)return{ok:!1,error:n.reason};const o=n.client;if(typeof o.judgeNonTextContrast!="function")return{ok:!1,error:"AI client does not support judgeNonTextContrast yet."};const r=(d=e.results[0])==null?void 0:d.screenshotDataUrl;if(!r)return{ok:!1,error:"No page screenshot available — re-run the audit so a screenshot is captured."};if((l=e.signal)!=null&&l.aborted)return{ok:!1,error:"Audit cancelled."};let i=null;if(e.tabId!==void 0)try{const u=await chrome.scripting.executeScript({target:{tabId:e.tabId},func:nd});i=((h=u==null?void 0:u[0])==null?void 0:h.result)??null}catch(u){vo.warn("component contrast measurement failed; falling back to AI",u)}if(i&&i.measured.length>=1){const u=i.measured.filter(f=>f.ratio<3&&f.confident),g=i.measured.filter(f=>f.ratio>=3),p=i.measured.filter(f=>!f.confident);if(u.length>0){const f={componentId:e.componentId,criterionId:Gt,verdict:"fail",reasoning:`Programmatic measurement: ${u.length} of ${i.measured.length} measured components fail WCAG 1.4.11 (< 3:1 contrast against adjacent background). `+u.slice(0,6).map(y=>`${y.selector} — ${y.ratio.toFixed(2)}:1 (${y.borderColor} on ${y.backgroundColor})`).join("; ")+(g.length>0?` · ${g.length} other components pass.`:""),confidence:.98,steps:[],pageUrl:e.pageUrl,finishedAt:new Date().toISOString(),costUsd:0,model:"programmatic-contrast-measurement"};return await oe(f),{ok:!0,result:f}}if(p.length===0&&g.length===i.measured.length){const f={componentId:e.componentId,criterionId:Gt,verdict:"pass",reasoning:`Programmatic measurement: ${g.length===1?"the":"all"} ${g.length} measured component${g.length===1?"":"s"} pass${g.length===1?"es":""} WCAG 1.4.11 (≥ 3:1 contrast against adjacent background). Lowest measured: ${(m=g.map(y=>y.ratio).sort((y,b)=>y-b)[0])==null?void 0:m.toFixed(2)}:1. Decorative-only graphics and components with transparent borders over images / gradients (which programmatic measurement cannot resolve) are not in scope of this measurement; if such components exist on the page they should be inspected separately.`,confidence:.95,steps:[],pageUrl:e.pageUrl,finishedAt:new Date().toISOString(),costUsd:0,model:"programmatic-contrast-measurement"};return await oe(f),{ok:!0,result:f}}}let c;try{const u=await Fe(e.componentId,Gt);u&&(c={verdict:u.verdict,reasoning:u.reasoning,confidence:u.confidence})}catch{}try{const u=await Xe(()=>o.judgeNonTextContrast({pageUrl:e.pageUrl,pageScreenshot:r,previousVerdict:c,signal:e.signal})),g={componentId:e.componentId,criterionId:Gt,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 oe(g),{ok:!0,result:g}}catch(u){return vo.error("non-text-contrast audit failed",u),{ok:!1,error:u instanceof Error?u.message:String(u)}}}function nd(){function e(p){const f=p.match(/rgba?\(\s*([0-9.]+)\s*,?\s*([0-9.]+)\s*,?\s*([0-9.]+)(?:\s*[,/]\s*([0-9.]+))?\s*\)/i);if(!f)return null;const y=Math.round(parseFloat(f[1])),b=Math.round(parseFloat(f[2])),A=Math.round(parseFloat(f[3])),w=f[4]!==void 0?parseFloat(f[4]):1;return{r:y,g:b,b:A,a:w}}function t(p){const f=y=>{const b=y/255;return b<=.03928?b/12.92:Math.pow((b+.055)/1.055,2.4)};return .2126*f(p.r)+.7152*f(p.g)+.0722*f(p.b)}function a(p,f){const y=t(p),b=t(f),A=Math.max(y,b),w=Math.min(y,b);return(A+.05)/(w+.05)}function n(p,f){const y=Math.max(0,Math.min(1,p.a));return{r:Math.round(p.r*y+f.r*(1-y)),g:Math.round(p.g*y+f.g*(1-y)),b:Math.round(p.b*y+f.b*(1-y))}}function o(p){let f=p.parentElement;for(;f&&f!==document.documentElement;){const y=getComputedStyle(f);if(y.backgroundImage&&y.backgroundImage!=="none")return{color:y.backgroundColor,hasImage:!0};const A=e(y.backgroundColor);if(A&&A.a>.5)return{color:y.backgroundColor,hasImage:!1};f=f.parentElement}return{color:"rgb(255, 255, 255)",hasImage:!1}}function r(p){if(p.id)return`#${p.id}`;const f=p.tagName.toLowerCase(),y=p.className&&typeof p.className=="string"?p.className.split(/\s+/).filter(Boolean).slice(0,2).join("."):"";return y?`${f}.${y}`:f}function i(p,f){const y=getComputedStyle(p),b=e(y.backgroundColor);return b?b.a>=.999?b:b.a<=.001?f:n(b,f):f}function c(p,f){if((p.textContent??"").trim().length===0)return!1;const y=document.createTreeWalker(p,NodeFilter.SHOW_TEXT);let b;for(;b=y.nextNode();){if((b.textContent??"").trim().length===0)continue;const w=b.parentElement;if(!w)continue;const s=getComputedStyle(w);if(s.visibility==="hidden"||s.display==="none")continue;if(s.position==="absolute"){const F=parseFloat(s.width),T=parseFloat(s.height);if(F>0&&F<=1||T>0&&T<=1||s.clip==="rect(0px, 0px, 0px, 0px)"||s.clipPath==="inset(50%)")continue}const v=e(s.color);if(!v)continue;const S=v.a<1?n(v,f):v,k=a(S,f),x=parseFloat(s.fontSize)||16,L=parseInt(s.fontWeight,10)||400,U=x>=24||x>=18.5&&L>=700?3:4.5;if(k>=U)return!0}return!1}function d(p){const f=p.tagName.toLowerCase();if(f==="textarea"||f==="select")return!0;if(f==="input"){const y=(p.getAttribute("type")??"text").toLowerCase();return y!=="hidden"&&y!=="checkbox"&&y!=="radio"&&y!=="submit"&&y!=="button"&&y!=="reset"&&y!=="image"}return!1}function l(p,f){const y=document.activeElement;try{p.focus({preventScroll:!0})}catch{return null}if(document.activeElement!==p){if(y)try{y.focus({preventScroll:!0})}catch{}return null}const b=getComputedStyle(p);let A=0,w="";const s=b.outlineStyle,v=parseFloat(b.outlineWidth)||0;if(s!=="none"&&v>0){const k=e(b.outlineColor);if(k){const x=k.a<1?n(k,f):k,L=a(x,f);L>A&&(A=L,w=`outline ${v}px ${b.outlineColor}`)}}const S=b.boxShadow;if(S&&S!=="none"){const k=e(S);if(k){const x=k.a<1?n(k,f):k,L=a(x,f);L>A&&(A=L,w=`box-shadow ${S.slice(0,60)}`)}}try{p.blur()}catch{}if(y)try{y.focus({preventScroll:!0})}catch{}return A===0?null:{ratio:A,indicator:w}}function h(p){const f=[{attr:"aria-current",activeVal:"page"},{attr:"aria-pressed",activeVal:"true"},{attr:"aria-selected",activeVal:"true"},{attr:"aria-checked",activeVal:"true"}];let y=null;for(const $ of f){const U=p.getAttribute($.attr);if(U&&($.attr==="aria-current"?U!=="false"&&U!=="":U===$.activeVal)){y=$;break}}if(!y)return null;const b=p.parentElement;if(!b)return null;const A=p.tagName.toLowerCase(),w=Array.from(b.children).filter($=>{if(!($ instanceof HTMLElement)||$===p||$.tagName.toLowerCase()!==A)return!1;const U=$.getAttribute(y.attr);return U?y.attr==="aria-current"?U==="false"||U==="":U!==y.activeVal:!0});if(w.length===0)return null;const s=w[0],v=getComputedStyle(p),S=getComputedStyle(s),k=[["backgroundColor","background"],["color","text color"],["borderTopColor","border"]],x=o(p),L=e(x.color);if(!L)return null;for(const[$,U]of k){const F=e(v[$]),T=e(S[$]);if(!F||!T||F.r===T.r&&F.g===T.g&&F.b===T.b&&F.a===T.a)continue;const N=F.a<1?n(F,L):F,O=T.a<1?n(T,L):T,D=a(N,O);return{activeColor:N,inactiveColor:O,ratio:D,ariaMarker:`${y.attr}="${p.getAttribute(y.attr)}" vs ${U}`}}return null}const u=Array.from(document.querySelectorAll('input:not([type="hidden"]):not([type="checkbox"]):not([type="radio"]), button, select, textarea, a[href][aria-current], a[href][role="tab"], [role="tab"], [role="button"][aria-pressed], [role="checkbox"][aria-checked]')).filter(p=>{const f=p.getBoundingClientRect();if(f.width<8||f.height<8)return!1;const y=getComputedStyle(p);return!(y.visibility==="hidden"||y.display==="none")}),g=[];for(const p of u){const f=getComputedStyle(p),y=o(p),b=y.hasImage?null:e(y.color),A=b?i(p,b):null;if(b&&A){const k=h(p);k&&k.ratio<3&&g.push({selector:r(p),role:p.getAttribute("role")??p.tagName.toLowerCase(),borderColor:`state: ${k.ariaMarker}`,backgroundColor:`active rgb(${k.activeColor.r},${k.activeColor.g},${k.activeColor.b}) vs inactive rgb(${k.inactiveColor.r},${k.inactiveColor.g},${k.inactiveColor.b})`,ratio:k.ratio,confident:!0})}if(b){const k=l(p,b);k!==null&&k.ratio<3&&g.push({selector:r(p),role:p.getAttribute("role")??p.tagName.toLowerCase(),borderColor:`focus: ${k.indicator}`,backgroundColor:y.color,ratio:k.ratio,confident:!0})}if(parseFloat(f.borderTopWidth)+parseFloat(f.borderRightWidth)+parseFloat(f.borderBottomWidth)+parseFloat(f.borderLeftWidth)===0)continue;const s=e(f.borderTopColor);if(!s)continue;if(y.hasImage){g.push({selector:r(p),role:p.getAttribute("role")??p.tagName.toLowerCase(),borderColor:f.borderTopColor,backgroundColor:y.color,ratio:0,confident:!1});continue}if(!b||!A||!d(p)&&c(p,A))continue;const v=s.a<1?n(s,b):s,S=a(v,b);if(g.push({selector:r(p),role:p.getAttribute("role")??p.tagName.toLowerCase(),borderColor:f.borderTopColor,backgroundColor:y.color,ratio:S,confident:!0}),g.length>=20)break}return{measured:g}}const At=te("label-in-name-audit"),Qe="2.5.3";function od(){function e(c){return c.replace(/\s+/g," ").trim().toLowerCase()}function t(c){const d=c.cloneNode(!0);return d.querySelectorAll('[aria-hidden="true"]').forEach(l=>l.remove()),d.querySelectorAll(".sr-only, .visually-hidden, .screen-reader-text, .visuallyhidden").forEach(l=>l.remove()),(d.textContent??"").replace(/\s+/g," ").trim()}function a(c){var m,u,g,p;const d=c.getAttribute("aria-label");if(d&&d.trim())return d.trim();const l=c.getAttribute("aria-labelledby");if(l){const f=l.split(/\s+/).map(y=>document.getElementById(y)).filter(y=>y!==null).map(y=>{var b;return((b=y.textContent)==null?void 0:b.trim())??""}).filter(Boolean);if(f.length>0)return f.join(" ")}if(c.tagName==="INPUT"||c.tagName==="SELECT"||c.tagName==="TEXTAREA"){const f=c.getAttribute("id");if(f){const A=document.querySelector(`label[for="${f}"]`);if(A)return((m=A.textContent)==null?void 0:m.trim())??""}const y=c.closest("label");if(y)return((u=y.textContent)==null?void 0:u.trim())??"";const b=c.getAttribute("placeholder");if(b)return b.trim()}if(c.tagName==="BUTTON"||c.tagName==="A")return((g=c.textContent)==null?void 0:g.trim())??"";const h=c.getAttribute("value");return h&&c.type==="submit"?h.trim():((p=c.textContent)==null?void 0:p.trim())??""}function n(c){if(c.id)return`#${c.id}`;const d=c.tagName.toLowerCase(),l=c.className&&typeof c.className=="string"?"."+c.className.split(/\s+/).filter(Boolean).slice(0,2).join("."):"";return d+l}const o=["a[href]","button","input:not([type=hidden]):not([type=submit])[aria-label], input[aria-labelledby]","input[type=submit]","input[type=button]",'[role="button"]','[role="link"]','[role="checkbox"]','[role="radio"]','[role="switch"]','[role="tab"]','[role="menuitem"]'],r=Array.from(new Set(o.flatMap(c=>Array.from(document.querySelectorAll(c))))).filter(c=>{if(c.hasAttribute("disabled"))return!1;const d=c.getBoundingClientRect();if(d.width===0||d.height===0)return!1;const l=getComputedStyle(c);return!(l.visibility==="hidden"||l.display==="none")}),i=[];for(const c of r){const d=t(c),l=a(c);if(!d||!l)continue;const h=e(d),m=e(l);let u;m.includes(h)?u="pass":m.length===0||h.length===0?u="fail":u="ambiguous";const g=c.getAttribute("role")??c.tagName.toLowerCase();i.push({selector:n(c),role:g,visibleText:d.slice(0,200),accessibleName:l.slice(0,200),programmaticMatch:u})}return i}async function rd(e){var g,p,f,y;if((g=e.signal)!=null&&g.aborted)return{ok:!1,error:"Audit cancelled."};let t;try{const A=(await chrome.scripting.executeScript({target:{tabId:e.tabId},world:"MAIN",func:od}))[0];if(!A||!A.result)return{ok:!1,error:"DOM walker returned no result."};t=A.result}catch(b){return At.warn("DOM walker injection failed",b),{ok:!1,error:`DOM walker failed: ${b instanceof Error?b.message:String(b)}`}}const a=t.filter(b=>b.programmaticMatch==="fail"),n=t.filter(b=>b.programmaticMatch==="ambiguous"),o=t.length;if(o===0){const b={componentId:e.componentId,criterionId:Qe,verdict:"pass",reasoning:"No interactive elements with both visible text AND a separate accessible name were found on the page. WCAG 2.5.3 has no applicable controls — counts as pass / not applicable.",confidence:.95,pageUrl:e.pageUrl,finishedAt:new Date().toISOString(),costUsd:0,model:"programmatic-only"};return await oe(b),{ok:!0,result:b}}if(a.length===0&&n.length===0){const b={componentId:e.componentId,criterionId:Qe,verdict:"pass",reasoning:`${o} interactive controls audited; all visible labels are contained in their accessible names (case-insensitive substring match). WCAG 2.5.3 passes.`,confidence:.95,pageUrl:e.pageUrl,finishedAt:new Date().toISOString(),costUsd:0,model:"programmatic-only"};return await oe(b),At.info(`2.5.3 passed programmatically (${o} candidates, all substring-matched)`),{ok:!0,result:b}}const r=await chrome.storage.local.get("aiConfig"),i=ve(r.aiConfig),c=he(i);if(!c.ok){const b=`Programmatic-only verdict (no AI key configured): ${a.length} candidates with visible text NOT contained in their accessible name. Each of these is likely a WCAG 2.5.3 failure: ${a.slice(0,5).map(w=>`${w.role}/${w.selector} (visible="${w.visibleText.slice(0,50)}", accessible="${w.accessibleName.slice(0,50)}")`).join("; ")}${a.length>5?"; ...":""}. ${n.length} additional candidates are AMBIGUOUS (icon-prefix, plurals, smart-quotes) — configure an AI key to disambiguate.`,A={componentId:e.componentId,criterionId:Qe,verdict:a.length>0?"fail":"uncertain",reasoning:b,confidence:.6,pageUrl:e.pageUrl,finishedAt:new Date().toISOString(),costUsd:0,model:"programmatic-only"};return await oe(A),{ok:!0,result:A}}const d=c.client;if(typeof d.judgeLabelInName!="function")return{ok:!1,error:"AI client does not support judgeLabelInName."};if((p=e.signal)!=null&&p.aborted)return{ok:!1,error:"Audit cancelled."};const l=(y=(f=e.results)==null?void 0:f[0])==null?void 0:y.screenshotDataUrl,h=[...a,...n],m=await Sa(Qe,{candidates:h.map(b=>({selector:b.selector,role:b.role,visibleText:b.visibleText,accessibleName:b.accessibleName,programmaticMatch:b.programmaticMatch}))}),u=await Ca(e.componentId,Qe,m);if(u.hit){At.info(`2.5.3 inputs unchanged (hash match); reusing verdict=${u.previous.verdict}`);const b=$a(u.previous,m);return await oe(b),{ok:!0,result:b}}try{const b=await Xe(()=>d.judgeLabelInName({pageUrl:e.pageUrl,pageScreenshot:l,candidates:h,signal:e.signal})),A=b.verdict==="pass"?"pass":b.verdict==="fail"?"fail":"uncertain",w={componentId:e.componentId,criterionId:Qe,verdict:A,reasoning:`${o} interactive controls audited. ${t.length-h.length} passed the strict substring match; ${h.length} (${a.length} strict fails + ${n.length} ambiguous) sent to AI judgment. AI verdict: ${A}. `+b.reasoning,confidence:b.confidence,pageUrl:e.pageUrl,finishedAt:new Date().toISOString(),costUsd:b.costUsd,model:b.model,inputHash:m};return await oe(w),At.info(`2.5.3 resolved to ${A} (${h.length} ambiguous cases AI-judged)`),{ok:!0,result:w}}catch(b){return At.error("label-in-name AI judgment failed",b),{ok:!1,error:b instanceof Error?b.message:String(b)}}}const Be=te("focus-order-cascade");function id(){const e=Array.from(document.querySelectorAll('a[href], button, input:not([type=hidden]), select, textarea, [tabindex]:not([tabindex="-1"])')).filter(r=>{if(r.hasAttribute("disabled"))return!1;const i=r.getBoundingClientRect();if(i.width===0||i.height===0)return!1;const c=getComputedStyle(r);return!(c.visibility==="hidden"||c.display==="none")});if(e.length<4)return{matches:!1,pattern:"none",evidence:`only ${e.length} focusable element(s) on page — cascade does not apply`,totalFocusables:e.length,largestCluster:0};const t=new Map;for(const r of e){let i=r.parentElement;for(;i&&i!==document.body&&i!==document.documentElement;){const c=getComputedStyle(i);let d="";if(c.display==="grid"||c.display==="inline-grid"){const l=c.gridTemplateColumns.trim().split(/\s+/).filter(Boolean);l.length>=2&&c.gridTemplateColumns!=="none"&&(d=`grid-${l.length}-cols`)}else if(c.display==="flex"||c.display==="inline-flex")c.flexDirection.startsWith("row")&&(c.flexWrap==="wrap"||c.flexWrap==="wrap-reverse"?d="flex-row-wrap":i.children.length>=2&&(d="flex-row"));else{const l=c.columnCount;l&&l!=="auto"&&parseInt(l,10)>=2&&(d=`css-columns-${l}`)}if(d){const l=t.get(i);l?l.count++:t.set(i,{count:1,pattern:d});break}i=i.parentElement}}let a={count:0,pattern:"none"};for(const r of t.values())r.count>a.count&&(a={count:r.count,pattern:r.pattern});const n=Math.max(2,Math.ceil(e.length*.3)),o=a.count>=n;return{matches:o,pattern:o?a.pattern:"none",evidence:o?`${a.count} of ${e.length} focusable elements share a ${a.pattern} container (threshold ${n})`:`largest multi-column cluster was ${a.count} of ${e.length} focusables; threshold was ${n} — no dominant banked layout`,totalFocusables:e.length,largestCluster:a.count}}function sd(){const e='a[href], button:not([disabled]), input:not([disabled]):not([type="hidden"]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"]):not([disabled])',t=Array.from(document.querySelectorAll('nav, [role="navigation"], [role="group"], section[aria-labelledby], section[aria-label], aside[aria-labelledby], aside[aria-label], header[aria-labelledby], footer[aria-labelledby]')),a=t.filter(r=>t.some(i=>i!==r&&i.contains(r))?!1:!t.some(i=>i!==r&&r.contains(i)));let n=0;for(const r of a)r.querySelector(e)&&n++;const o=n>=2;return{matches:o,evidence:o?`${n} distinct labeled navigation regions found on page (e.g., multiple <nav> blocks in a footer). Column-first focus traversal across these groups is a valid pattern — screen readers announce each group's label before its contents.`:`${n} labeled navigation region(s) found — need ≥2 with focusable content to trigger multi-region override`,groupCount:a.length,groupsWithFocusables:n}}async function cd(e){if(e.focusOrderVerdict!=="uncertain")return{ran:!1,reason:`2.4.3 verdict was ${e.focusOrderVerdict}, not uncertain — cascade does not apply`};if(e.keyboardTrapVerdict!=="pass")return{ran:!1,reason:`2.1.2 verdict was ${e.keyboardTrapVerdict??"absent"}, not pass — cascade does not apply (need empirical focus-path functionality)`};const t=await Fe(e.componentId,"2.4.3");if(!t)return{ran:!1,reason:"2.4.3 record not found in storage — cannot apply cascade"};let a=null;try{const i=(await chrome.scripting.executeScript({target:{tabId:e.tabId},world:"MAIN",func:id}))[0];i&&i.result&&(a=i.result)}catch(r){Be.warn("focus-order cascade detector failed",r)}if(a&&a.matches){const r={...t,verdict:"pass",reasoning:t.reasoning+`
841
849
 
842
850
  [Cascade resolution — rc.167 fallback-1]
843
- Primary AI verdict was uncertain. WCAG 2.1.2 No Keyboard Trap PASSED on the same DOM (Tab walks forward AND Shift+Tab walks back through every focusable). The programmatic multi-column layout detector confirmed: `+a.evidence+`. The heuristic's "reverse y-coordinate" complaint is a known false-positive pattern for banked layouts — combined with the empirical 2.1.2 PASS, this resolves to PASS. If the visual reading order is intentionally column-first (typical for footers, sidebars, card grids), this is correct behavior.`};return await ne(r),je.info(`2.4.3 cascade resolved uncertain → pass via fallback-1 (${a.pattern}, ${a.largestCluster}/${a.totalFocusables} focusables clustered)`),{ran:!0,resolved:"pass",via:"fallback-1",pattern:a.pattern,evidence:a.evidence}}const n=await ed(e,t);if(n.ran&&n.resolved==="pass"||n.ran&&n.resolved==="fail")return n;const o={...t,verdict:"fail",reasoning:t.reasoning+`
851
+ Primary AI verdict was uncertain. WCAG 2.1.2 No Keyboard Trap PASSED on the same DOM (Tab walks forward AND Shift+Tab walks back through every focusable). The programmatic multi-column layout detector confirmed: `+a.evidence+`. The heuristic's "reverse y-coordinate" complaint is a known false-positive pattern for banked layouts — combined with the empirical 2.1.2 PASS, this resolves to PASS. If the visual reading order is intentionally column-first (typical for footers, sidebars, card grids), this is correct behavior.`};return await oe(r),Be.info(`2.4.3 cascade resolved uncertain → pass via fallback-1 (${a.pattern}, ${a.largestCluster}/${a.totalFocusables} focusables clustered)`),{ran:!0,resolved:"pass",via:"fallback-1",pattern:a.pattern,evidence:a.evidence}}const n=await ld(e,t);if(n.ran&&n.resolved==="pass"||n.ran&&n.resolved==="fail")return n;const o={...t,verdict:"fail",reasoning:t.reasoning+`
844
852
 
845
853
  [Cascade resolution — rc.169 escalation]
846
- All cascade fallbacks returned uncertain. Primary AI judgment: uncertain. Programmatic multi-column-layout detector: `+(a?a.evidence:"did not run (script injection failed)")+". Focused AI re-call (fallback 2): "+(n.ran?n.evidence??"returned uncertain":n.reason)+". Marked as FAIL with required manual verification — the user can override this verdict via the human-review UI in the side panel."};return await ne(o),je.info("2.4.3 cascade escalated to FAIL — all fallbacks returned uncertain; manual verification required"),{ran:!0,resolved:"fail",via:"escalation",evidence:"all fallbacks returned uncertain; verdict escalated to fail pending manual verification"}}async function ed(e,t){if(!e.tabSequence||e.tabSequence.length===0)return{ran:!0,resolved:"uncertain",via:"none",evidence:"fallback-2 skipped — no tab sequence provided (primary walkthrough did not capture steps)"};let a;try{const r=await chrome.storage.local.get("aiConfig");a=ve(r.aiConfig)}catch(r){return je.warn("fallback-2: failed to read aiConfig",r),{ran:!0,resolved:"uncertain",via:"none",evidence:"fallback-2 skipped — could not load aiConfig"}}const n=ue(a);if(!n.ok)return{ran:!0,resolved:"uncertain",via:"none",evidence:`fallback-2 skipped — ${n.reason}`};const o=n.client;if(typeof o.judgeFocusOrderFallback!="function")return{ran:!0,resolved:"uncertain",via:"none",evidence:"fallback-2 skipped — AI client does not support judgeFocusOrderFallback (older provider)"};try{const r=await Ke(()=>o.judgeFocusOrderFallback({pageUrl:e.pageUrl,pageScreenshot:e.pageScreenshot,tabSequence:e.tabSequence,primaryUncertainReason:t.reasoning,signal:e.signal}));if(r.verdict==="pass"){const i={...t,verdict:"pass",reasoning:t.reasoning+`
854
+ All cascade fallbacks returned uncertain. Primary AI judgment: uncertain. Programmatic multi-column-layout detector: `+(a?a.evidence:"did not run (script injection failed)")+". Focused AI re-call (fallback 2): "+(n.ran?n.evidence??"returned uncertain":n.reason)+". Marked as FAIL with required manual verification — the user can override this verdict via the human-review UI in the side panel."};return await oe(o),Be.info("2.4.3 cascade escalated to FAIL — all fallbacks returned uncertain; manual verification required"),{ran:!0,resolved:"fail",via:"escalation",evidence:"all fallbacks returned uncertain; verdict escalated to fail pending manual verification"}}async function ld(e,t){if(!e.tabSequence||e.tabSequence.length===0)return{ran:!0,resolved:"uncertain",via:"none",evidence:"fallback-2 skipped — no tab sequence provided (primary walkthrough did not capture steps)"};let a;try{const r=await chrome.storage.local.get("aiConfig");a=ve(r.aiConfig)}catch(r){return Be.warn("fallback-2: failed to read aiConfig",r),{ran:!0,resolved:"uncertain",via:"none",evidence:"fallback-2 skipped — could not load aiConfig"}}const n=he(a);if(!n.ok)return{ran:!0,resolved:"uncertain",via:"none",evidence:`fallback-2 skipped — ${n.reason}`};const o=n.client;if(typeof o.judgeFocusOrderFallback!="function")return{ran:!0,resolved:"uncertain",via:"none",evidence:"fallback-2 skipped — AI client does not support judgeFocusOrderFallback (older provider)"};try{const r=await Xe(()=>o.judgeFocusOrderFallback({pageUrl:e.pageUrl,pageScreenshot:e.pageScreenshot,tabSequence:e.tabSequence,primaryUncertainReason:t.reasoning,signal:e.signal}));if(r.verdict==="pass"){const i={...t,verdict:"pass",reasoning:t.reasoning+`
847
855
 
848
856
  [Cascade resolution — rc.169 fallback-2]
849
857
  Primary AI verdict was uncertain. Programmatic multi-column detector found no matching pattern (long-tail case). Focused AI re-call resolved to PASS with reasoning: `+r.reasoning+`
850
858
 
851
- The focused prompt explicitly enumerates long-tail false-positive patterns (custom JS focus management, roving tabindex composite widgets, sticky sidebars, skip-link patterns, modal traps, table layouts, absolute positioning) — when the AI identifies one of these AND the tab sequence is consistent with the pattern, the cascade resolves to PASS.`,costUsd:t.costUsd+r.costUsd};return await ne(i),je.info("2.4.3 cascade resolved uncertain → pass via fallback-2 (focused AI re-call)"),{ran:!0,resolved:"pass",via:"fallback-2",evidence:`focused AI re-call returned pass with confidence ${r.confidence.toFixed(2)}`}}if(r.verdict==="fail"){const i={...t,verdict:"fail",reasoning:t.reasoning+`
859
+ The focused prompt explicitly enumerates long-tail false-positive patterns (custom JS focus management, roving tabindex composite widgets, sticky sidebars, skip-link patterns, modal traps, table layouts, absolute positioning) — when the AI identifies one of these AND the tab sequence is consistent with the pattern, the cascade resolves to PASS.`,costUsd:t.costUsd+r.costUsd};return await oe(i),Be.info("2.4.3 cascade resolved uncertain → pass via fallback-2 (focused AI re-call)"),{ran:!0,resolved:"pass",via:"fallback-2",evidence:`focused AI re-call returned pass with confidence ${r.confidence.toFixed(2)}`}}if(r.verdict==="fail"){const i={...t,verdict:"fail",reasoning:t.reasoning+`
852
860
 
853
861
  [Cascade resolution — rc.169 fallback-2]
854
- Primary AI verdict was uncertain. Programmatic multi-column detector found no matching pattern. Focused AI re-call resolved to FAIL with reasoning: `+r.reasoning,costUsd:t.costUsd+r.costUsd};return await ne(i),je.info("2.4.3 cascade resolved uncertain → fail via fallback-2 (focused AI re-call detected a real failure)"),{ran:!0,resolved:"fail",via:"fallback-2",evidence:`focused AI re-call returned fail with confidence ${r.confidence.toFixed(2)}`}}return{ran:!0,resolved:"uncertain",via:"fallback-2",evidence:`focused AI re-call also returned uncertain — ${r.reasoning.slice(0,200)}`}}catch(r){return je.warn("fallback-2 AI re-call threw",r),{ran:!0,resolved:"uncertain",via:"none",evidence:`fallback-2 AI re-call threw: ${r instanceof Error?r.message:String(r)}`}}}async function Sa(){const e=await de();return le(e,"aiWalkthroughs")}const fe=te("interactive-walkthroughs");async function et(e,t){if(!e.verifyFixesOnly)return null;const a=await Pe(e.componentId,t);return a?await Ns(e.componentId,t,e.pageUrl)?(fe.info(`${t} skipped (verify-fixes): ack present`),a):a.verdict==="pass"?(fe.info(`${t} skipped (verify-fixes): previously passed`),a):null:null}async function td(e){var a,n,o,r,i,c,d;if(!await Sa())return[{criterionId:"2.4.3",ran:!1,ok:!0,error:"AI walkthroughs not available on current plan"},{criterionId:"2.1.2",ran:!1,ok:!0,error:"AI walkthroughs not available on current plan"},{criterionId:"2.4.7",ran:!1,ok:!0,error:"AI walkthroughs not available on current plan"}];const t=[];try{if((a=e.signal)!=null&&a.aborted)t.push({criterionId:"2.4.3",ran:!1,ok:!1,error:"cancelled"});else{const l=await et(e,"2.4.3");if(l)t.push({criterionId:"2.4.3",ran:!1,ok:!0,verdict:l.verdict,skippedForVerifyFixes:!0});else{const h=await Fl({tabId:e.tabId,componentId:e.componentId,pageUrl:e.pageUrl,signal:e.signal});t.push({criterionId:"2.4.3",ran:!0,ok:h.ok,verdict:h.ok?h.result.verdict:void 0,error:h.ok?void 0:h.error})}}}catch(l){fe.warn("2.4.3 walkthrough threw",l),t.push({criterionId:"2.4.3",ran:!0,ok:!1,error:l instanceof Error?l.message:String(l)})}try{if((n=e.signal)!=null&&n.aborted)t.push({criterionId:"2.1.2",ran:!1,ok:!1,error:"cancelled"});else{const l=await et(e,"2.1.2");if(l)t.push({criterionId:"2.1.2",ran:!1,ok:!0,verdict:l.verdict,skippedForVerifyFixes:!0});else{const h=await Ul({tabId:e.tabId,componentId:e.componentId,pageUrl:e.pageUrl,signal:e.signal});t.push({criterionId:"2.1.2",ran:!0,ok:h.ok,verdict:h.ok?h.result.verdict:void 0,error:h.ok?void 0:h.error})}}}catch(l){fe.warn("2.1.2 walkthrough threw",l),t.push({criterionId:"2.1.2",ran:!0,ok:!1,error:l instanceof Error?l.message:String(l)})}try{if((o=e.signal)!=null&&o.aborted)t.push({criterionId:"2.4.7",ran:!1,ok:!1,error:"cancelled"});else{const l=await et(e,"2.4.7");if(l)t.push({criterionId:"2.4.7",ran:!1,ok:!0,verdict:l.verdict,skippedForVerifyFixes:!0});else{const h=await Gl({tabId:e.tabId,componentId:e.componentId,pageUrl:e.pageUrl,signal:e.signal});t.push({criterionId:"2.4.7",ran:!0,ok:h.ok,verdict:h.ok?h.result.verdict:void 0,error:h.ok?void 0:h.error})}}}catch(l){fe.warn("2.4.7 walkthrough threw",l),t.push({criterionId:"2.4.7",ran:!0,ok:!1,error:l instanceof Error?l.message:String(l)})}try{const l=t.find(h=>h.criterionId==="2.4.3");if(l!=null&&l.ok&&l.verdict==="fail"){const h=await chrome.scripting.executeScript({target:{tabId:e.tabId},func:Ql}),m=(r=h==null?void 0:h[0])==null?void 0:r.result;if(m!=null&&m.matches){const u=await Pe(e.componentId,"2.4.3");if(u&&u.verdict==="fail"){const g={...u,verdict:"pass",reasoning:u.reasoning+`
862
+ Primary AI verdict was uncertain. Programmatic multi-column detector found no matching pattern. Focused AI re-call resolved to FAIL with reasoning: `+r.reasoning,costUsd:t.costUsd+r.costUsd};return await oe(i),Be.info("2.4.3 cascade resolved uncertain → fail via fallback-2 (focused AI re-call detected a real failure)"),{ran:!0,resolved:"fail",via:"fallback-2",evidence:`focused AI re-call returned fail with confidence ${r.confidence.toFixed(2)}`}}return{ran:!0,resolved:"uncertain",via:"fallback-2",evidence:`focused AI re-call also returned uncertain — ${r.reasoning.slice(0,200)}`}}catch(r){return Be.warn("fallback-2 AI re-call threw",r),{ran:!0,resolved:"uncertain",via:"none",evidence:`fallback-2 AI re-call threw: ${r instanceof Error?r.message:String(r)}`}}}async function Ta(){const e=await ue();return de(e,"aiWalkthroughs")}const me=te("interactive-walkthroughs");async function at(e,t){if(!e.verifyFixesOnly)return null;const a=await Fe(e.componentId,t);return a?await js(e.componentId,t,e.pageUrl)?(me.info(`${t} skipped (verify-fixes): ack present`),a):a.verdict==="pass"?(me.info(`${t} skipped (verify-fixes): previously passed`),a):null:null}async function dd(e){var a,n,o,r,i,c,d;if(!await Ta())return[{criterionId:"2.4.3",ran:!1,ok:!0,error:"AI walkthroughs not available on current plan"},{criterionId:"2.1.2",ran:!1,ok:!0,error:"AI walkthroughs not available on current plan"},{criterionId:"2.4.7",ran:!1,ok:!0,error:"AI walkthroughs not available on current plan"}];const t=[];try{if((a=e.signal)!=null&&a.aborted)t.push({criterionId:"2.4.3",ran:!1,ok:!1,error:"cancelled"});else{const l=await at(e,"2.4.3");if(l)t.push({criterionId:"2.4.3",ran:!1,ok:!0,verdict:l.verdict,skippedForVerifyFixes:!0});else{const h=await ql({tabId:e.tabId,componentId:e.componentId,pageUrl:e.pageUrl,signal:e.signal});t.push({criterionId:"2.4.3",ran:!0,ok:h.ok,verdict:h.ok?h.result.verdict:void 0,error:h.ok?void 0:h.error})}}}catch(l){me.warn("2.4.3 walkthrough threw",l),t.push({criterionId:"2.4.3",ran:!0,ok:!1,error:l instanceof Error?l.message:String(l)})}try{if((n=e.signal)!=null&&n.aborted)t.push({criterionId:"2.1.2",ran:!1,ok:!1,error:"cancelled"});else{const l=await at(e,"2.1.2");if(l)t.push({criterionId:"2.1.2",ran:!1,ok:!0,verdict:l.verdict,skippedForVerifyFixes:!0});else{const h=await Kl({tabId:e.tabId,componentId:e.componentId,pageUrl:e.pageUrl,signal:e.signal});t.push({criterionId:"2.1.2",ran:!0,ok:h.ok,verdict:h.ok?h.result.verdict:void 0,error:h.ok?void 0:h.error})}}}catch(l){me.warn("2.1.2 walkthrough threw",l),t.push({criterionId:"2.1.2",ran:!0,ok:!1,error:l instanceof Error?l.message:String(l)})}try{if((o=e.signal)!=null&&o.aborted)t.push({criterionId:"2.4.7",ran:!1,ok:!1,error:"cancelled"});else{const l=await at(e,"2.4.7");if(l)t.push({criterionId:"2.4.7",ran:!1,ok:!0,verdict:l.verdict,skippedForVerifyFixes:!0});else{const h=await Ql({tabId:e.tabId,componentId:e.componentId,pageUrl:e.pageUrl,signal:e.signal});t.push({criterionId:"2.4.7",ran:!0,ok:h.ok,verdict:h.ok?h.result.verdict:void 0,error:h.ok?void 0:h.error})}}}catch(l){me.warn("2.4.7 walkthrough threw",l),t.push({criterionId:"2.4.7",ran:!0,ok:!1,error:l instanceof Error?l.message:String(l)})}try{const l=t.find(h=>h.criterionId==="2.4.3");if(l!=null&&l.ok&&l.verdict==="fail"){const h=await chrome.scripting.executeScript({target:{tabId:e.tabId},func:sd}),m=(r=h==null?void 0:h[0])==null?void 0:r.result;if(m!=null&&m.matches){const u=await Fe(e.componentId,"2.4.3");if(u&&u.verdict==="fail"){const g={...u,verdict:"pass",reasoning:u.reasoning+`
855
863
 
856
- [Labeled-group nav override — rc.270] Programmatic detector found ${m.groupsWithFocusables} distinct labeled navigation regions on this page. ${m.evidence} Per WCAG 2.4.3, column-first traversal ACROSS distinct labeled regions is a valid focus order — the user can locate themselves via each region's label (announced by screen readers) before traversing within the region. Overriding fail → pass.`};await ne(g),l.verdict="pass",fe.info(`2.4.3 fail overridden → pass (rc.270 labeled-group nav: ${m.groupsWithFocusables} regions)`)}}}}catch(l){fe.warn("2.4.3 labeled-group nav override step failed",l)}try{const l=t.find(m=>m.criterionId==="2.4.3"),h=t.find(m=>m.criterionId==="2.1.2");if(l!=null&&l.ok&&l.verdict==="fail"&&(h!=null&&h.ok)&&h.verdict==="pass"){const m=await Pe(e.componentId,"2.4.3");if(m&&m.verdict==="fail"){const u={...m,verdict:"uncertain",reasoning:m.reasoning+`
864
+ [Labeled-group nav override — rc.270] Programmatic detector found ${m.groupsWithFocusables} distinct labeled navigation regions on this page. ${m.evidence} Per WCAG 2.4.3, column-first traversal ACROSS distinct labeled regions is a valid focus order — the user can locate themselves via each region's label (announced by screen readers) before traversing within the region. Overriding fail → pass.`};await oe(g),l.verdict="pass",me.info(`2.4.3 fail overridden → pass (rc.270 labeled-group nav: ${m.groupsWithFocusables} regions)`)}}}}catch(l){me.warn("2.4.3 labeled-group nav override step failed",l)}try{const l=t.find(m=>m.criterionId==="2.4.3"),h=t.find(m=>m.criterionId==="2.1.2");if(l!=null&&l.ok&&l.verdict==="fail"&&(h!=null&&h.ok)&&h.verdict==="pass"){const m=await Fe(e.componentId,"2.4.3");if(m&&m.verdict==="fail"){const u={...m,verdict:"uncertain",reasoning:m.reasoning+`
857
865
 
858
- [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 ne(u),l.verdict="uncertain",fe.info("2.4.3 fail downgraded to uncertain — 2.1.2 PASSed (multi-column layout corroboration)")}}}catch(l){fe.warn("2.4.3 ↔ 2.1.2 cross-corroboration step failed",l)}try{const l=t.find(m=>m.criterionId==="2.4.3"),h=t.find(m=>m.criterionId==="2.1.2");if(l&&l.verdict==="uncertain"&&(h==null?void 0:h.verdict)==="pass"){const m=await Pe(e.componentId,"2.4.3"),u=await Zl({tabId:e.tabId,componentId:e.componentId,pageUrl:e.pageUrl,focusOrderVerdict:l.verdict,keyboardTrapVerdict:h.verdict,tabSequence:(i=m==null?void 0:m.steps)==null?void 0:i.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??{x:0,y:0,width:0,height:0}})),pageScreenshot:(d=(c=m==null?void 0:m.steps)==null?void 0:c[0])==null?void 0:d.screenshot,signal:e.signal});u.ran&&u.resolved==="pass"?(l.verdict="pass",fe.info(`2.4.3 uncertain resolved to PASS via cascade ${u.via} (${u.pattern??u.evidence.slice(0,80)})`)):u.ran&&u.resolved==="fail"?(l.verdict="fail",fe.info(`2.4.3 resolved to FAIL via cascade ${u.via}: ${u.evidence.slice(0,80)}`)):u.ran&&u.resolved==="uncertain"?fe.info("2.4.3 cascade exhausted all paths — uncertain stays. (this shouldn't happen post-rc.169; escalation should have fired)"):u.ran||fe.info(`2.4.3 cascade did not run: ${u.reason}`)}}catch(l){fe.warn("2.4.3 cascade step failed",l)}return t}async function ad(e){var t;try{if(!await Sa())return{criterionId:"1.3.2",ran:!1,ok:!0,error:"AI walkthroughs not available on current plan"};if((t=e.signal)!=null&&t.aborted)return{criterionId:"1.3.2",ran:!1,ok:!1,error:"cancelled"};const a=await et(e,"1.3.2");if(a)return{criterionId:"1.3.2",ran:!1,ok:!0,verdict:a.verdict,skippedForVerifyFixes:!0};if(!e.results||e.results.length===0)return{criterionId:"1.3.2",ran:!1,ok:!1,error:"no audit results available"};const n=await zl({componentId:e.componentId,pageUrl:e.pageUrl,results:e.results,signal:e.signal});return{criterionId:"1.3.2",ran:!0,ok:n.ok,verdict:n.ok?n.result.verdict:void 0,error:n.ok?void 0:n.error}}catch(a){return fe.warn("1.3.2 walkthrough threw",a),{criterionId:"1.3.2",ran:!0,ok:!1,error:a instanceof Error?a.message:String(a)}}}async function nd(e){var t;try{if(!await Sa())return{criterionId:"1.4.11",ran:!1,ok:!0,error:"AI walkthroughs not available on current plan"};if((t=e.signal)!=null&&t.aborted)return{criterionId:"1.4.11",ran:!1,ok:!1,error:"cancelled"};const a=await et(e,"1.4.11");if(a)return{criterionId:"1.4.11",ran:!1,ok:!0,verdict:a.verdict,skippedForVerifyFixes:!0};if(!e.results||e.results.length===0)return{criterionId:"1.4.11",ran:!1,ok:!1,error:"no audit results available"};const n=await ql({componentId:e.componentId,pageUrl:e.pageUrl,results:e.results,signal:e.signal,tabId:e.tabId});return{criterionId:"1.4.11",ran:!0,ok:n.ok,verdict:n.ok?n.result.verdict:void 0,error:n.ok?void 0:n.error}}catch(a){return fe.warn("1.4.11 walkthrough threw",a),{criterionId:"1.4.11",ran:!0,ok:!1,error:a instanceof Error?a.message:String(a)}}}async function od(e){var t;try{if(!await Sa())return{criterionId:"2.5.3",ran:!1,ok:!0,error:"AI walkthroughs not available on current plan"};if((t=e.signal)!=null&&t.aborted)return{criterionId:"2.5.3",ran:!1,ok:!1,error:"cancelled"};const a=await et(e,"2.5.3");if(a)return{criterionId:"2.5.3",ran:!1,ok:!0,verdict:a.verdict,skippedForVerifyFixes:!0};const n=await Jl({tabId:e.tabId,componentId:e.componentId,pageUrl:e.pageUrl,results:e.results,signal:e.signal});return{criterionId:"2.5.3",ran:!0,ok:n.ok,verdict:n.ok?n.result.verdict:void 0,error:n.ok?void 0:n.error}}catch(a){return fe.warn("2.5.3 walkthrough threw",a),{criterionId:"2.5.3",ran:!0,ok:!1,error:a instanceof Error?a.message:String(a)}}}async function rd(e){const t=JSON.stringify({algo:"css-crosscheck-bgfp1",ruleId:e.ruleId,selector:e.selector,fg:e.foreground??null,fs:e.fontSize??null,fw:e.fontWeight??null,summary:e.failureSummary??null,bgfp:e.bgFingerprint??null}),a=new TextEncoder().encode(t),n=await crypto.subtle.digest("SHA-256",a);return Array.from(new Uint8Array(n)).map(o=>o.toString(16).padStart(2,"0")).join("")}const tt=te("pixel-contrast-sampler"),id="1.3",sd=4.5,cd=3;function ld(e,t){return e?e>=24||e>=18.66&&(t??400)>=700:!1}function dd(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 a=/^#([0-9a-f])([0-9a-f])([0-9a-f])$/i.exec(e);if(a)return{r:parseInt(a[1]+a[1],16),g:parseInt(a[2]+a[2],16),b:parseInt(a[3]+a[3],16)};const n=/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(e);return n?{r:parseInt(n[1],16),g:parseInt(n[2],16),b:parseInt(n[3],16)}:null}function wo({r:e,g:t,b:a}){const n=o=>{const r=o/255;return r<=.03928?r/12.92:Math.pow((r+.055)/1.055,2.4)};return .2126*n(e)+.7152*n(t)+.0722*n(a)}function Mn(e,t){const a=wo(e),n=wo(t),o=Math.max(a,n),r=Math.min(a,n);return(o+.05)/(r+.05)}function ud(e,t,a,n){return e>=n||!a?!1:Mn(t,a)>=n}async function hd(e,t){var n;const a=`(() => {
866
+ [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 oe(u),l.verdict="uncertain",me.info("2.4.3 fail downgraded to uncertain — 2.1.2 PASSed (multi-column layout corroboration)")}}}catch(l){me.warn("2.4.3 ↔ 2.1.2 cross-corroboration step failed",l)}try{const l=t.find(m=>m.criterionId==="2.4.3"),h=t.find(m=>m.criterionId==="2.1.2");if(l&&l.verdict==="uncertain"&&(h==null?void 0:h.verdict)==="pass"){const m=await Fe(e.componentId,"2.4.3"),u=await cd({tabId:e.tabId,componentId:e.componentId,pageUrl:e.pageUrl,focusOrderVerdict:l.verdict,keyboardTrapVerdict:h.verdict,tabSequence:(i=m==null?void 0:m.steps)==null?void 0:i.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??{x:0,y:0,width:0,height:0}})),pageScreenshot:(d=(c=m==null?void 0:m.steps)==null?void 0:c[0])==null?void 0:d.screenshot,signal:e.signal});u.ran&&u.resolved==="pass"?(l.verdict="pass",me.info(`2.4.3 uncertain resolved to PASS via cascade ${u.via} (${u.pattern??u.evidence.slice(0,80)})`)):u.ran&&u.resolved==="fail"?(l.verdict="fail",me.info(`2.4.3 resolved to FAIL via cascade ${u.via}: ${u.evidence.slice(0,80)}`)):u.ran&&u.resolved==="uncertain"?me.info("2.4.3 cascade exhausted all paths — uncertain stays. (this shouldn't happen post-rc.169; escalation should have fired)"):u.ran||me.info(`2.4.3 cascade did not run: ${u.reason}`)}}catch(l){me.warn("2.4.3 cascade step failed",l)}return t}async function ud(e){var t;try{if(!await Ta())return{criterionId:"1.3.2",ran:!1,ok:!0,error:"AI walkthroughs not available on current plan"};if((t=e.signal)!=null&&t.aborted)return{criterionId:"1.3.2",ran:!1,ok:!1,error:"cancelled"};const a=await at(e,"1.3.2");if(a)return{criterionId:"1.3.2",ran:!1,ok:!0,verdict:a.verdict,skippedForVerifyFixes:!0};if(!e.results||e.results.length===0)return{criterionId:"1.3.2",ran:!1,ok:!1,error:"no audit results available"};const n=await td({componentId:e.componentId,pageUrl:e.pageUrl,results:e.results,signal:e.signal});return{criterionId:"1.3.2",ran:!0,ok:n.ok,verdict:n.ok?n.result.verdict:void 0,error:n.ok?void 0:n.error}}catch(a){return me.warn("1.3.2 walkthrough threw",a),{criterionId:"1.3.2",ran:!0,ok:!1,error:a instanceof Error?a.message:String(a)}}}async function hd(e){var t;try{if(!await Ta())return{criterionId:"1.4.11",ran:!1,ok:!0,error:"AI walkthroughs not available on current plan"};if((t=e.signal)!=null&&t.aborted)return{criterionId:"1.4.11",ran:!1,ok:!1,error:"cancelled"};const a=await at(e,"1.4.11");if(a)return{criterionId:"1.4.11",ran:!1,ok:!0,verdict:a.verdict,skippedForVerifyFixes:!0};if(!e.results||e.results.length===0)return{criterionId:"1.4.11",ran:!1,ok:!1,error:"no audit results available"};const n=await ad({componentId:e.componentId,pageUrl:e.pageUrl,results:e.results,signal:e.signal,tabId:e.tabId});return{criterionId:"1.4.11",ran:!0,ok:n.ok,verdict:n.ok?n.result.verdict:void 0,error:n.ok?void 0:n.error}}catch(a){return me.warn("1.4.11 walkthrough threw",a),{criterionId:"1.4.11",ran:!0,ok:!1,error:a instanceof Error?a.message:String(a)}}}async function pd(e){var t;try{if(!await Ta())return{criterionId:"2.5.3",ran:!1,ok:!0,error:"AI walkthroughs not available on current plan"};if((t=e.signal)!=null&&t.aborted)return{criterionId:"2.5.3",ran:!1,ok:!1,error:"cancelled"};const a=await at(e,"2.5.3");if(a)return{criterionId:"2.5.3",ran:!1,ok:!0,verdict:a.verdict,skippedForVerifyFixes:!0};const n=await rd({tabId:e.tabId,componentId:e.componentId,pageUrl:e.pageUrl,results:e.results,signal:e.signal});return{criterionId:"2.5.3",ran:!0,ok:n.ok,verdict:n.ok?n.result.verdict:void 0,error:n.ok?void 0:n.error}}catch(a){return me.warn("2.5.3 walkthrough threw",a),{criterionId:"2.5.3",ran:!0,ok:!1,error:a instanceof Error?a.message:String(a)}}}async function fd(e){const t=JSON.stringify({algo:"css-crosscheck-bgfp1",ruleId:e.ruleId,selector:e.selector,fg:e.foreground??null,fs:e.fontSize??null,fw:e.fontWeight??null,summary:e.failureSummary??null,bgfp:e.bgFingerprint??null}),a=new TextEncoder().encode(t),n=await crypto.subtle.digest("SHA-256",a);return Array.from(new Uint8Array(n)).map(o=>o.toString(16).padStart(2,"0")).join("")}const nt=te("pixel-contrast-sampler"),gd="1.3",md=4.5,bd=3;function yd(e,t){return e?e>=24||e>=18.66&&(t??400)>=700:!1}function wd(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 a=/^#([0-9a-f])([0-9a-f])([0-9a-f])$/i.exec(e);if(a)return{r:parseInt(a[1]+a[1],16),g:parseInt(a[2]+a[2],16),b:parseInt(a[3]+a[3],16)};const n=/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(e);return n?{r:parseInt(n[1],16),g:parseInt(n[2],16),b:parseInt(n[3],16)}:null}function Ao({r:e,g:t,b:a}){const n=o=>{const r=o/255;return r<=.03928?r/12.92:Math.pow((r+.055)/1.055,2.4)};return .2126*n(e)+.7152*n(t)+.0722*n(a)}function Ln(e,t){const a=Ao(e),n=Ao(t),o=Math.max(a,n),r=Math.min(a,n);return(o+.05)/(r+.05)}function vd(e,t,a,n){return e>=n||!a?!1:Ln(t,a)>=n}async function Ad(e,t){var n;const a=`(() => {
859
867
  const dpr = window.devicePixelRatio || 1;
860
868
  const sx = window.scrollX || 0;
861
869
  const sy = window.scrollY || 0;
@@ -951,29 +959,29 @@ Primary AI verdict was uncertain. Programmatic multi-column detector found no ma
951
959
  } catch { out[sel] = null; }
952
960
  }
953
961
  return JSON.stringify(out);
954
- })()`;try{const o=await chrome.debugger.sendCommand({tabId:e},"Runtime.evaluate",{expression:a,returnByValue:!0}),r=(n=o==null?void 0:o.result)==null?void 0:n.value;if(!r||typeof r!="string")return new Map;const i=JSON.parse(r);return new Map(Object.entries(i))}catch(o){return tt.debug("getAllRectsAtOnce failed",o),new Map}}const Dn="__wcagcheckr_hidetext";async function pd(e){const t=`
962
+ })()`;try{const o=await chrome.debugger.sendCommand({tabId:e},"Runtime.evaluate",{expression:a,returnByValue:!0}),r=(n=o==null?void 0:o.result)==null?void 0:n.value;if(!r||typeof r!="string")return new Map;const i=JSON.parse(r);return new Map(Object.entries(i))}catch(o){return nt.debug("getAllRectsAtOnce failed",o),new Map}}const Nn="__wcagcheckr_hidetext";async function kd(e){const t=`
955
963
  (async () => {
956
964
  // Wait for fonts so hide/restore screenshots show identical glyph metrics.
957
965
  try { await document.fonts.ready; } catch {}
958
966
  const s = document.createElement('style');
959
- s.id = ${JSON.stringify(Dn)};
967
+ s.id = ${JSON.stringify(Nn)};
960
968
  s.textContent = '*, *::before, *::after { color: transparent !important; text-shadow: none !important; -webkit-text-fill-color: transparent !important; caret-color: transparent !important; }';
961
969
  document.documentElement.appendChild(s);
962
970
  // Force a reflow + paint so the next screenshot captures the new state.
963
971
  document.body.offsetHeight;
964
972
  return true;
965
973
  })()
966
- `;await chrome.debugger.sendCommand({tabId:e},"Runtime.evaluate",{expression:t,awaitPromise:!0})}async function fd(e){const t=`
974
+ `;await chrome.debugger.sendCommand({tabId:e},"Runtime.evaluate",{expression:t,awaitPromise:!0})}async function xd(e){const t=`
967
975
  (() => {
968
- const el = document.getElementById(${JSON.stringify(Dn)});
976
+ const el = document.getElementById(${JSON.stringify(Nn)});
969
977
  if (el) el.remove();
970
978
  document.body.offsetHeight;
971
979
  return true;
972
980
  })()
973
- `;await chrome.debugger.sendCommand({tabId:e},"Runtime.evaluate",{expression:t})}async function vo(e,t){let a;try{a=await chrome.debugger.sendCommand({tabId:e},"Page.captureScreenshot",{format:"png",captureBeyondViewport:!0,clip:{x:t.x,y:t.y,width:t.width,height:t.height,scale:1}})}catch(n){return tt.warn(`captureElementClip failed: ${n instanceof Error?n.message:String(n)}`),null}if(!(a!=null&&a.data))return null;try{const n=`data:image/png;base64,${a.data}`,o=await fetch(n).then(d=>d.blob()),r=await createImageBitmap(o),c=new OffscreenCanvas(r.width,r.height).getContext("2d");return c?(c.drawImage(r,0,0),c.getImageData(0,0,r.width,r.height)):null}catch(n){return tt.warn(`clip decode failed: ${n instanceof Error?n.message:String(n)}`),null}}const gd=25,Gr=8,md=150,bd=.95,yd=4,wd=400;function vd(e,t,a){if(e.width!==t.width||e.height!==t.height)return null;const n=e.width,o=e.height;let r=0,i=0;for(let w=0;w<o;w++)for(let s=0;s<n;s++){const A=(w*n+s)*4,S=t.data[A],k=t.data[A+1],I=t.data[A+2],L=e.data[A],$=e.data[A+1],_=e.data[A+2],P=Math.abs(L-S)+Math.abs($-k)+Math.abs(_-I);P>i&&(i=P),P>=gd&&r++}if(r<Gr)return null;const c=i*bd;let d=0,l=0,h=0,m=0;if(i>=wd)for(let w=0;w<o;w++)for(let s=0;s<n;s++){const A=(w*n+s)*4,S=t.data[A],k=t.data[A+1],I=t.data[A+2],L=e.data[A],$=e.data[A+1],_=e.data[A+2];Math.abs(L-S)+Math.abs($-k)+Math.abs(_-I)>=c&&(d+=S,l+=k,h+=I,m++)}const u=m>=yd?{r:Math.round(d/m),g:Math.round(l/m),b:Math.round(h/m)}:a,g=new Map;let f=0;for(let w=0;w<o;w++)for(let s=0;s<n;s++){const A=(w*n+s)*4,S=t.data[A],k=t.data[A+1],I=t.data[A+2];if(Math.abs(S-u.r)+Math.abs(k-u.g)+Math.abs(I-u.b)>=md){f++;const $=S>>4<<8|k>>4<<4|I>>4,_=g.get($);_?(_.r+=S,_.g+=k,_.b+=I,_.count++):g.set($,{r:S,g:k,b:I,count:1})}}const p=n*o;if(f<p*.2){g.clear();for(let w=0;w<o;w++)for(let s=0;s<n;s++){const A=(w*n+s)*4,S=e.data[A],k=e.data[A+1],I=e.data[A+2],L=S>>4<<8|k>>4<<4|I>>4,$=g.get(L);$?($.r+=S,$.g+=k,$.b+=I,$.count++):g.set(L,{r:S,g:k,b:I,count:1})}}let y={r:0,g:0,b:0,count:0};for(const w of g.values())w.count>y.count&&(y=w);const b={r:Math.round(y.r/y.count),g:Math.round(y.g/y.count),b:Math.round(y.b/y.count)},v=Mn(u,b);return{textPixelCount:r,worstRatio:v,worstBackground:b,worstForeground:u}}async function Ad(e,t,a){var d,l,h,m,u,g;const n=new Map;for(const f of t){const p=f.pageUrl??f.scope??"";for(const y of((d=f.axeRulesEvaluated)==null?void 0:d.incomplete)??[])if(y.ruleId==="color-contrast")for(const b of y.elements??[]){const v=`${p}::${y.ruleId}::${b.selector}`;n.has(v)||n.set(v,{pageUrl:p,ruleId:y.ruleId,selector:b.selector,wcagCriterion:y.wcagCriterion??"1.4.3",foreground:(l=b.styles)==null?void 0:l.foreground,fontSize:(h=b.styles)==null?void 0:h.fontSize,fontWeight:(m=b.styles)==null?void 0:m.fontWeight,failureSummary:b.failureSummary})}}if(n.size===0)return{resolved:0,skipped:0};let o=!1,r=null;for(let f=1;f<=6;f++)try{await chrome.debugger.attach({tabId:e},id),o=!0;break}catch(p){r=p;const y=p instanceof Error?p.message:String(p);if(/already attached/i.test(y)){await new Promise(b=>setTimeout(b,500));continue}break}if(!o)return tt.warn("debugger attach failed after retries; skipping pixel sampling",r),{resolved:0,skipped:n.size};let i=0,c=0;try{const f=Array.from(n.values()),p=f.map(I=>I.selector),y=await hd(e,p);if(a!=null&&a.aborted)return{resolved:0,skipped:n.size};const b=((u=f[0])==null?void 0:u.pageUrl)??"",v=b?await fa(b):[],w=new Map;for(const I of v)w.set(`${I.ruleId}::${I.selector}`,I);const s=new Map,A=[];for(const I of f){const L=await rd({...I,bgFingerprint:((g=y.get(I.selector))==null?void 0:g.bgFingerprint)??null});s.set(I.selector,L);const $=w.get(`${I.ruleId}::${I.selector}`);if($&&$.inputHash===L&&$.verdict!=="uncertain"){const _={...$,resolvedAt:new Date().toISOString(),reasoning:$.reasoning.includes("(cached, hash-match)")?$.reasoning:`${$.reasoning} (cached, hash-match)`};await We(_),i++;continue}A.push(I)}if(A.length===0)return tt.info(`pixel-contrast sampler: all ${f.length} targets reused from hash-cache`),{resolved:i,skipped:c};await pd(e);const S=new Map;for(const I of A){if(a!=null&&a.aborted)break;const L=y.get(I.selector);if(!L)continue;const $=await vo(e,L);$&&S.set(I.selector,$)}if(await fd(e),a!=null&&a.aborted)return{resolved:0,skipped:n.size};const k=new Map;for(const I of A){if(a!=null&&a.aborted)break;const L=y.get(I.selector);if(!L)continue;const $=await vo(e,L);$&&k.set(I.selector,$)}for(const I of A){if(a!=null&&a.aborted)break;const L=s.get(I.selector),$=y.get(I.selector);if(!$){c++;const V={pageUrl:I.pageUrl,ruleId:I.ruleId,selector:I.selector,verdict:"uncertain",reasoning:"Hide-text observation: element's bounding rect could not be resolved at audit time (element may be display:none, detached from DOM, or zero-size). Cannot sample contrast empirically. Manual verification required if you believe this element is visible.",resolvedAt:new Date().toISOString(),costUsd:0,wcagCriterion:I.wcagCriterion,inputHash:L};await We(V);continue}const _=S.get(I.selector),P=k.get(I.selector);if(!_||!P){c++;const V={pageUrl:I.pageUrl,ruleId:I.ruleId,selector:I.selector,verdict:"uncertain",reasoning:"Hide-text observation: per-element clip capture failed via chrome.debugger Page.captureScreenshot. Manual verification required.",resolvedAt:new Date().toISOString(),costUsd:0,wcagCriterion:I.wcagCriterion,inputHash:L};await We(V);continue}const T=dd(I.foreground);if(!T){c++;const V={pageUrl:I.pageUrl,ruleId:I.ruleId,selector:I.selector,verdict:"uncertain",reasoning:`Hide-text observation: element's computed CSS foreground color could not be parsed (got ${JSON.stringify(I.foreground)}). Cannot compute contrast against rendered background without an intended foreground color. Manual verification required.`,resolvedAt:new Date().toISOString(),costUsd:0,wcagCriterion:I.wcagCriterion,inputHash:L};await We(V);continue}const N=vd(_,P,T);if(!N){c++;const V={pageUrl:I.pageUrl,ruleId:I.ruleId,selector:I.selector,verdict:"uncertain",reasoning:`Hide-text observation: too few text pixels detected (under ${Gr} after diffing hidden vs visible captures). Element may be canvas-rendered, SVG with non-standard rendering, or carry styles that prevent color: transparent from hiding the text. Manual verification required.`,resolvedAt:new Date().toISOString(),costUsd:0,wcagCriterion:I.wcagCriterion,inputHash:L};await We(V);continue}const M=ld(I.fontSize,I.fontWeight),D=M?cd:sd,j=V=>`rgb(${V.r},${V.g},${V.b})`;let O=N.worstRatio>=D,G="";if(ud(N.worstRatio,T,$.resolvedBg,D)){const V=Mn(T,$.resolvedBg);O=!0,G=` Cross-check (rc.356): computed CSS contrast ${j(T)} on resolved background ${j($.resolvedBg)} = ${V.toFixed(2)}:1, which PASSES ${D}:1 — the sub-threshold pixel reading is an anti-aliasing/sampling artifact, not a real failure. Suppressed.`}const R={pageUrl:I.pageUrl,ruleId:I.ruleId,selector:I.selector,verdict:O?"pass":"fail",reasoning:`Hide-text observation: ${N.textPixelCount} text-core pixels detected by diffing hidden-vs-visible viewport screenshots. Worst per-pixel contrast: ${N.worstRatio.toFixed(2)}:1 against AA threshold ${D}:1 (${M?"large text":"normal text"}). At worst pixel: rendered foreground ${j(N.worstForeground)} on rendered background ${j(N.worstBackground)}. ${O&&G?"Passes (computed cross-check).":O?"Passes threshold.":"Falls below threshold — at least one text pixel reads against a background that fails contrast."}`+G,resolvedAt:new Date().toISOString(),costUsd:0,wcagCriterion:I.wcagCriterion,inputHash:L};await We(R),i++}}finally{try{await chrome.debugger.sendCommand({tabId:e},"Runtime.evaluate",{expression:`(() => { const el = document.getElementById(${JSON.stringify(Dn)}); if (el) el.remove(); return true; })()`})}catch{}try{await chrome.debugger.detach({tabId:e})}catch{}}return tt.info(`pixel-contrast sampler: resolved ${i}, skipped ${c}/${n.size}`),{resolved:i,skipped:c}}const lt=te("ai-text");function dt(e){if(e!=null&&e.aborted){const t=new Error("aborted");throw t.name="AbortError",t}}function ut(e){return e instanceof Error&&(e.name==="AbortError"||e.name==="ExternalAbortError"||e.message==="aborted")}async function kd(e,t,a,n={}){var u;if(!a.enabled||!a.enabledChecks.headings)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=ue(a);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${o.reason}`]};const r=o.client,i=Me(a.costCapUsd),c=[],d=[],l=Math.max(1,a.maxCandidatesPerCheck??10),h=e.slice(0,l),m=h.length;for(let g=0;g<h.length;g++){const f=h[g];if(!i.canCharge())break;try{dt(n.signal);const p=await r.judgeHeading({headingText:f.text,sectionContent:f.sectionContent,signal:n.signal});i.recordCharge(p.costUsd),p.verdict==="fail"&&p.confidence>=.6&&c.push(await ht("ai-heading-not-descriptive","2.4.6","AA","moderate","AI judged this heading does not describe its section content.",f.selector,f.outerHTML,f.text,p,t))}catch(p){if(ut(p)){d.push(`canceled after ${g}/${m} candidates`);break}const y=p instanceof Error?p.message:String(p);d.push(`${f.selector}: ${y}`),lt.warn("heading judgment failed",p)}(u=n.onProgress)==null||u.call(n,g+1,m)}return{findings:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:d}}async function Id(e,t,a,n={}){var u;if(!a.enabled||!a.enabledChecks.sensory)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=ue(a);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${o.reason}`]};const r=o.client,i=Me(a.costCapUsd),c=[],d=[],l=Math.max(1,a.maxCandidatesPerCheck??10),h=e.slice(0,l),m=h.length;for(let g=0;g<h.length;g++){const f=h[g];if(!i.canCharge())break;try{dt(n.signal);const p=await r.judgeSensoryLanguage({instructionText:f.text,signal:n.signal});i.recordCharge(p.costUsd),p.verdict==="fail"&&p.confidence>=.6&&c.push(await ht("ai-sensory-instruction","1.3.3","A","serious","AI flagged this instruction as relying on sensory characteristics (shape, color, location) without a programmatic identifier.",f.selector,f.outerHTML,f.text.slice(0,100),p,t))}catch(p){if(ut(p)){d.push(`canceled after ${g}/${m} candidates`);break}const y=p instanceof Error?p.message:String(p);d.push(`${f.selector}: ${y}`),lt.warn("sensory judgment failed",p)}(u=n.onProgress)==null||u.call(n,g+1,m)}return{findings:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:d}}async function xd(e,t,a,n={}){var u;if(!a.enabled||!a.enabledChecks.aria)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=ue(a);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${o.reason}`]};const r=o.client,i=Me(a.costCapUsd),c=[],d=[],l=Math.max(1,a.maxCandidatesPerCheck??10),h=e.slice(0,l),m=h.length;for(let g=0;g<h.length;g++){const f=h[g];if(!i.canCharge())break;try{dt(n.signal);const p=await r.judgeAriaSemantics({elementHtml:f.outerHTML,computedRole:f.role,surroundingHtml:f.surroundingHtml,signal:n.signal});i.recordCharge(p.costUsd),p.verdict==="fail"&&p.confidence>=.65&&c.push(await ht("ai-aria-misuse","4.1.2","A","serious",`AI judged role="${f.role}" is being used inappropriately on this element.`,f.selector,f.outerHTML,f.role,p,t))}catch(p){if(ut(p)){d.push(`canceled after ${g}/${m} candidates`);break}const y=p instanceof Error?p.message:String(p);d.push(`${f.selector}: ${y}`),lt.warn("aria judgment failed",p)}(u=n.onProgress)==null||u.call(n,g+1,m)}return{findings:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:d}}async function Sd(e,t,a,n={}){var u;if(!a.enabled||!a.enabledChecks.genericLinkText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=ue(a);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${o.reason}`]};const r=o.client,i=Me(a.costCapUsd),c=[],d=[],l=Math.max(1,a.maxCandidatesPerCheck??10),h=e.slice(0,l),m=h.length;for(let g=0;g<h.length;g++){const f=h[g];if(!i.canCharge())break;try{dt(n.signal);const p=await r.judgeGenericLinkText({linkText:f.linkText,surroundingText:f.surroundingText,signal:n.signal});i.recordCharge(p.costUsd),p.verdict==="fail"&&p.confidence>=.6&&c.push(await ht("ai-generic-link-text","2.4.4","A","serious","AI judged this link's purpose is not clear from its text or surrounding context.",f.selector,f.outerHTML,f.linkText,p,t))}catch(p){if(ut(p)){d.push(`canceled after ${g}/${m} candidates`);break}const y=p instanceof Error?p.message:String(p);d.push(`${f.selector}: ${y}`),lt.warn("generic-link judgment failed",p)}(u=n.onProgress)==null||u.call(n,g+1,m)}return{findings:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:d}}async function Cd(e,t,a,n={}){var u;if(!a.enabled||!a.enabledChecks.wallOfText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=ue(a);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${o.reason}`]};const r=o.client,i=Me(a.costCapUsd),c=[],d=[],l=Math.max(1,a.maxCandidatesPerCheck??10),h=e.slice(0,l),m=h.length;for(let g=0;g<h.length;g++){const f=h[g];if(!i.canCharge())break;try{dt(n.signal);const p=await r.judgeWallOfText({blockText:f.blockText,structuralChildrenCount:f.structuralChildrenCount,signal:n.signal});i.recordCharge(p.costUsd),p.verdict==="fail"&&p.confidence>=.6&&c.push(await ht("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).",f.selector,f.outerHTML,f.blockText.slice(0,80),p,t))}catch(p){if(ut(p)){d.push(`canceled after ${g}/${m} candidates`);break}const y=p instanceof Error?p.message:String(p);d.push(`${f.selector}: ${y}`),lt.warn("wall-of-text judgment failed",p)}(u=n.onProgress)==null||u.call(n,g+1,m)}return{findings:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:d}}async function $d(e,t,a,n={}){var u;if(!a.enabled||!a.enabledChecks.languageMismatch)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=ue(a);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${o.reason}`]};const r=o.client,i=Me(a.costCapUsd),c=[],d=[],l=Math.max(1,a.maxCandidatesPerCheck??10),h=e.slice(0,l),m=h.length;for(let g=0;g<h.length;g++){const f=h[g];if(!i.canCharge())break;try{dt(n.signal);const p=await r.judgeLanguageMismatch({declaredLang:f.declaredLang,bodyTextSample:f.bodyTextSample,signal:n.signal});i.recordCharge(p.costUsd),p.verdict==="fail"&&p.confidence>=.7&&c.push(await ht("ai-language-mismatch","3.1.1","A","serious",`AI judged the page's content does not match the declared lang="${f.declaredLang||"(none)"}".`,f.selector,f.outerHTML,f.bodyTextSample.slice(0,80),p,t))}catch(p){if(ut(p)){d.push(`canceled after ${g}/${m} candidates`);break}const y=p instanceof Error?p.message:String(p);d.push(`${f.selector}: ${y}`),lt.warn("language-mismatch judgment failed",p)}(u=n.onProgress)==null||u.call(n,g+1,m)}return{findings:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:d}}async function ht(e,t,a,n,o,r,i,c,d,l){const h={selector:r,outerHTML:i.slice(0,500),failureSummary:`AI assessment: ${d.reasoning}`,tagName:"AI",role:null,accessibleName:null,textSnippet:c.slice(0,100)||null,attrId:null,attrTestid:null},m=await un({ruleId:e,componentId:l.componentId,currentState:l.currentState,target:h});return{ruleId:e,wcagCriterion:t,wcagLevel:a,impact:n,description:o,helpUrl:"https://www.w3.org/WAI/WCAG21/Understanding/",target:h,componentId:l.componentId,currentState:l.currentState,axeVersion:l.axeVersion,detectedAt:new Date().toISOString(),matchKey:m,ai:{reasoning:d.reasoning,confidence:d.confidence,model:d.model,costUsd:d.costUsd}}}const Hr=te("ai-vision");function Br(e){if(e!=null&&e.aborted){const t=new Error("aborted");throw t.name="AbortError",t}}function zr(e){return e instanceof Error&&(e.name==="AbortError"||e.name==="ExternalAbortError"||e.message==="aborted")}async function Td(e,t,a,n={}){var u;if(!a.enabled||!a.enabledChecks.imageOfText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=ue(a);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${o.reason}`]};const r=o.client,i=Me(a.costCapUsd),c=Math.max(1,a.imageOfTextMaxImages??5),d=[...e].sort((g,f)=>f.pixelArea-g.pixelArea).slice(0,c),l=d.length,h=[],m=[];for(let g=0;g<d.length;g++){const f=d[g];if(!i.canCharge())break;try{Br(n.signal);const p=await r.judgeImageOfText({imageUrl:f.imageUrl,accessibleName:f.accessibleName,signal:n.signal});i.recordCharge(p.costUsd),p.verdict==="fail"&&p.confidence>=.65&&h.push(await qr("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.",f.selector,f.outerHTML,f.accessibleName??"",p,t))}catch(p){if(zr(p)){m.push(`canceled after ${g}/${l} candidates`);break}const y=p instanceof Error?p.message:String(p);m.push(`${f.selector}: ${y}`),Hr.warn("image-of-text judgment failed",p)}(u=n.onProgress)==null||u.call(n,g+1,l)}return{findings:h,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:m}}async function Ed(e,t,a,n={}){var u;if(!a.enabled||!a.enabledChecks.colorOnlyMeaning)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=ue(a);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[o.reason]};const r=o.client,i=Me(a.costCapUsd),c=[],d=[],l=Math.max(1,a.maxCandidatesPerCheck??10),h=e.slice(0,l),m=h.length;for(let g=0;g<h.length;g++){const f=h[g];if(!i.canCharge())break;try{Br(n.signal);const p=await r.judgeColorOnlyMeaning({elementHtml:f.elementHtml,contextDescription:f.contextDescription,signal:n.signal});i.recordCharge(p.costUsd),p.verdict==="fail"&&p.confidence>=.65&&c.push(await qr("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).",f.selector,f.outerHTML,f.contextDescription,p,t))}catch(p){if(zr(p)){d.push(`canceled after ${g}/${m} candidates`);break}const y=p instanceof Error?p.message:String(p);d.push(y),Hr.warn("color-only judgment failed",p)}(u=n.onProgress)==null||u.call(n,g+1,m)}return{findings:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:d}}async function qr(e,t,a,n,o,r,i,c,d,l){const h={selector:r,outerHTML:i.slice(0,500),failureSummary:`AI assessment: ${d.reasoning}`,tagName:"AI",role:null,accessibleName:null,textSnippet:c.slice(0,100)||null,attrId:null,attrTestid:null},m=await un({ruleId:e,componentId:l.componentId,currentState:l.currentState,target:h});return{ruleId:e,wcagCriterion:t,wcagLevel:a,impact:n,description:o,helpUrl:"https://www.w3.org/WAI/WCAG21/Understanding/",target:h,componentId:l.componentId,currentState:l.currentState,axeVersion:l.axeVersion,detectedAt:new Date().toISOString(),matchKey:m,ai:{reasoning:d.reasoning,confidence:d.confidence,model:d.model,costUsd:d.costUsd}}}const sn=te("ensure-content-script"),Rd=1500,Od=300;async function Ln(e,t){if(await Ao(e,t))return{ok:!0};const a=chrome.runtime.getManifest(),n=[];for(const o of a.content_scripts??[])Array.isArray(o.js)&&n.push(...o.js);if(n.length===0)return sn.error("manifest declares no content_scripts"),{ok:!1,reason:"inject-failed",detail:"no content_scripts declared in manifest"};try{const o=t!==void 0?{tabId:e,frameIds:[t]}:{tabId:e,allFrames:!0};await chrome.scripting.executeScript({target:o,files:n})}catch(o){return Md(o)}return await Dd(Od),await Ao(e,t)?(sn.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 Ao(e,t){try{const a=t!==void 0?{frameId:t}:void 0,n=await Promise.race([chrome.tabs.sendMessage(e,{type:"PING_REQUEST"},a),new Promise((o,r)=>setTimeout(()=>r(new Error("ping timeout")),Rd))]);return(n==null?void 0:n.type)==="PING_RESPONSE"}catch{return!1}}function Md(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}:(sn.warn("content-script injection failed",e),{ok:!1,reason:"inject-failed",detail:t})}function Dd(e){return new Promise(t=>setTimeout(t,e))}function cn(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 Ua=te("forensic-anchor-client"),Ld="https://api.wcagcheckr.com",Pd="wcagcheckr",Nd=15e3;function Fd(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 Kr(e,t){return Yr(e.hash,e.pageUrl,e.capturedAt,t)}async function Yr(e,t,a,n){const o={auditHash:e,pageUrl:t,capturedAt:a,...n?{licenseId:n}:{}},r=`${Ld}/v1/products/${Pd}/forensic/anchor`;try{const i=await fetch(r,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(o),signal:AbortSignal.timeout(Nd)});if(!i.ok)return Ua.warn(`anchor HTTP ${i.status}; entry stays local-only`),null;const c=await i.json();return Fd(c)?c:(Ua.warn("anchor returned malformed receipt; entry stays local-only",c),null)}catch(i){return Ua.warn("anchor request failed; entry stays local-only",i),null}}async function _d(e){const t=qt(e),a=new TextEncoder().encode(t),n=await crypto.subtle.digest("SHA-256",a);return Array.from(new Uint8Array(n)).map(o=>o.toString(16).padStart(2,"0")).join("")}function Ud(e){try{return new URL(e).hostname}catch{return e}}const Wd="wcag-site-claims",Vd=1,ta="claims";let Wa=null;function Jr(){return Wa||(Wa=pn(Wd,Vd,{upgrade(e){e.objectStoreNames.contains(ta)||e.createObjectStore(ta,{keyPath:"domain"})}})),Wa}async function jd(e){await(await Jr()).put(ta,e)}async function sp(e){return await(await Jr()).get(ta,e)??null}const ko="4.11.4";async function Gd(e){try{return(await chrome.tabs.get(e)).url??""}catch{return""}}const F=te("flows");async function pt(){const e=await Zo();if(!e)throw new Error("no audit target tab found");return e}async function Pn(){var a,n,o,r,i;const e=await Ve("stateMatrix",Ba);return((a=e.pseudoStates)==null?void 0:a.length)>0&&((n=e.themes)==null?void 0:n.length)>0&&((o=e.directions)==null?void 0:o.length)>0&&((r=e.breakpoints)==null?void 0:r.length)>0&&((i=e.breakpointPresets)==null?void 0:i.length)>0?e:(F.warn("stored stateMatrix is invalid; using defaults"),Ba)}async function Hd(e,t,a){const n=t.themes.includes("light"),o=t.themes.includes("dark");if(!n||!o)return t;let r=null;try{r=await ke(e,{type:"THEME_AWARENESS_REQUEST"},a)}catch{return t}return!r||r.hasUnreadableSheets?t:r.hasDarkModeCss&&!r.hasLightModeCss?(F.info("theme-axis prune: site is dark-only, skipping light pass"),{...t,themes:t.themes.filter(i=>i!=="light")}):r.hasLightModeCss&&!r.hasDarkModeCss?(F.info("theme-axis prune: site is light-only, skipping dark pass"),{...t,themes:t.themes.filter(i=>i!=="dark")}):t}const Bd=200,Va=3,Io=8;async function zd(e,t){const a=[];let n=null,o=null;for(let i=0;i<Io&&!t();i++){const c=await e();if(c.error&&(o=c.error),!c.result)break;if(n=c.result,a.push(c.violations),i===1&&a[0].length===0&&a[1].length===0)return{violations:[],lastResult:n,sampleCount:2,stabilized:!0,lastError:o};if(a.length>=Va){const d=a.slice(-Va);if(qd(d))return{violations:d[d.length-1],lastResult:n,sampleCount:a.length,stabilized:!0,lastError:o}}i<Io-1&&await new Promise(d=>setTimeout(d,Bd))}const r=a.slice(-Va);return{violations:Kd(r),lastResult:n,sampleCount:a.length,stabilized:!1,lastError:o}}function qd(e){if(e.length<2)return!0;const t=xo(e[0]);for(let a=1;a<e.length;a++){const n=xo(e[a]);if(n.size!==t.size)return!1;for(const o of t)if(!n.has(o))return!1}return!0}function xo(e){return new Set(e.map(t=>`${t.ruleId}::${t.target.selector}::${t.matchKey}`))}function Kd(e){const t=new Map,a="Audit detected this finding intermittently across multiple samples — likely an animation or transitional state was in progress. Verify manually.";for(const n of e)for(const o of n){const r=`${o.ruleId}::${o.target.selector}::${o.matchKey}`;t.has(r)||t.set(r,{...o,needsReview:!0,flakyAcrossRuns:!0,target:{...o.target,failureSummary:o.target.failureSummary?`${o.target.failureSummary} ${a}`:a}})}return Array.from(t.values())}async function So(e,t,a,n,o){await Promise.all([(async()=>{var r;try{const i=await ke(e,{type:"READING_ORDER_REQUEST",selector:t},a);n[0]&&(i.issues.length>0?(n[0].readingOrderIssues=i.issues,n[0].positionAnalysisCapturedAt=o):(r=n[0].axeRulesEvaluated)==null||r.passed.push({ruleId:"wcc-reading-order-clean",wcagCriterion:"1.3.2"}))}catch(i){F.debug("reading-order analysis skipped",i)}})(),(async()=>{var r;try{const i=await ke(e,{type:"TAB_ORDER_ANALYZE_REQUEST"},a);n[0]&&(i.issues.length>0?(n[0].tabOrderIssues=i.issues,n[0].positionAnalysisCapturedAt=o):(r=n[0].axeRulesEvaluated)==null||r.passed.push({ruleId:"wcc-tab-order-clean",wcagCriterion:"2.4.3"}))}catch(i){F.debug("tab-order analysis skipped",i)}})(),(async()=>{var r;try{const i=await ke(e,{type:"TYPOGRAPHY_ANALYZE_REQUEST",selector:t},a);i.issues.length>0&&n[0]?n[0].typographyIssues=i.issues:n[0]&&((r=n[0].axeRulesEvaluated)==null||r.passed.push({ruleId:"wcc-typography-clean",wcagCriterion:"1.4.12"}))}catch(i){F.debug("typography analysis skipped",i)}})(),(async()=>{try{const r=await ke(e,{type:"CUSTOM_PROPERTY_REQUEST"},a);r.undefined.length>0&&n[0]&&(n[0].undefinedCustomProperties=r.undefined)}catch(r){F.debug("custom-property analysis skipped",r)}})()])}async function it(e,t,a,n,o={}){var _,P,T,N,M;const r=o.asSubroutine===!0;if(await Fe())return r||q({type:"AUDIT_FAILED_EVENT",error:{code:Ct,message:He,recoverable:!1}}),{ok:!1,results:[],error:He};const i=o.tabIdOverride??await pt(),c=await Ln(i,a);if(!c.ok)return r||q({type:"AUDIT_FAILED_EVENT",error:{code:c.reason==="restricted-url"?"RESTRICTED_URL":"CONTENT_SCRIPT_UNREACHABLE",message:cn(c),recoverable:c.reason!=="restricted-url"}}),{ok:!1,results:[],error:cn(c)};try{await chrome.scripting.executeScript({target:{tabId:i,frameIds:a!==void 0?[a]:void 0},world:"MAIN",func:()=>{try{let D=document.getElementById("__wcagcheckr_audit_freeze");D||(D=document.createElement("style"),D.id="__wcagcheckr_audit_freeze",D.textContent="*, *::before, *::after { animation-duration: 0s !important; animation-delay: 0s !important; transition-duration: 0s !important; transition-delay: 0s !important; } html, body { scroll-behavior: auto !important; }",document.documentElement.appendChild(D)),window.scrollTo(0,0);const j=document.activeElement;j&&j instanceof HTMLElement&&j!==document.body&&j.blur()}catch{}}}),await chrome.scripting.executeScript({target:{tabId:i,frameIds:a!==void 0?[a]:void 0},world:"MAIN",func:async()=>{try{await document.fonts.ready}catch{}}})}catch(D){F.debug("audit-start state stabilization failed (non-fatal)",D)}const d=n??await Pn(),l=await Hd(i,d,a),h=Xc(l),m=await Li(`${await Gd(i)}|${e}|${a??0}`),u=await Pi(l),g=await Ve("axeDisabledRules",[]),f=await Ni(g);let p=null;try{p=(await ke(i,{type:"SCOPE_FINGERPRINT_REQUEST",selector:e},a)).fingerprint}catch(D){F.debug("scope fingerprint failed; skipping cache fast-path",D)}if(p){const D=await Fi(m);if(D&&D.fingerprint===p&&D.axeVersion===ko&&D.matrixConfigHash===u&&D.disabledRulesHash===f)return F.info(`audit-cache HIT for ${D.componentId} — skipping matrix iteration`),r||(q({type:"AUDIT_COMPLETE_EVENT",componentId:D.componentId,results:D.results,delta:D.delta}),q({type:"SCORECARD_UPDATED_EVENT"})),{ok:!0,results:D.results}}r||await qe({sessionId:Et(),mode:"single-element",scope:e,startedAt:new Date().toISOString(),lastProgressAt:new Date().toISOString(),state:"running"}),await At(i,{type:"LIVE_REGIONS_ARM_REQUEST"},a).catch(()=>{}),await At(i,{type:"FOCUS_TRACE_ARM_REQUEST"},a).catch(()=>{}),await ke(i,{type:"WARMUP_REQUEST"},a).catch(()=>{});const y=[],b=[];let v="",w,s=!1,A=null,S=null,k=null,I=null,L=!1,$=null;try{for(let D=0;D<h.length;D++){if(t.isCanceled()){F.info("single audit canceled by user");break}const j=h[D];try{const O=await on(i,j,e,a);if(!O.success){w=(_=O.error)==null?void 0:_.message,F.warn(`state ${D+1}/${h.length} drive failed`,w);continue}j.breakpoint.id!==A&&(await ke(i,{type:"WARMUP_REQUEST"},a).catch(()=>{}),A=j.breakpoint.id);const G=D===0?void 0:[...Dr],R=await zd(async()=>{var X;const J=await ke(i,{type:"AUDIT_REQUEST",selector:e,currentState:j,rulesToRun:G},a);return J.success&&J.result?{result:J.result,violations:J.result.violations,error:null}:{result:null,violations:[],error:((X=J.error)==null?void 0:X.message)??null}},()=>t.isCanceled());R.lastError&&(w=R.lastError,F.warn(`state ${D+1}/${h.length} sampling: ${R.lastError}`));const V=R.lastResult;if(!R.stabilized&&R.sampleCount>0&&F.info(`state ${D+1}/${h.length} did not stabilize after ${R.sampleCount} samples — ${R.violations.length} finding(s) routed to needs-review as flickering`),V){s=!0;const J=R.violations;F.info(`[adaptive-sample] state ${D+1}/${h.length} ${j.pseudoState}·${j.theme}·${j.direction}·${j.breakpoint.id}: ${R.sampleCount} sample(s), ${R.stabilized?"stabilized":"flickering"}, ${J.length} finding(s)`);const X=await ml(i,{quality:75})??void 0;let ye,we;try{ye=(await ke(i,{type:"LIVE_REGIONS_DRAIN_REQUEST"},a)).announcements}catch{}try{we=(await ke(i,{type:"FOCUS_TRACE_DRAIN_REQUEST"},a)).focusEvents}catch{}if(b.push({...V,violations:J,frameId:a,announcements:ye,focusEvents:we,screenshotDataUrl:X}),y.push(...J),v=V.componentId,D===0&&S===null)try{const me=await Ve("aiConfig",{}),C=ve(me);if(C.enabled&&C.apiKey&&v){S=ra(i,e,a,C,b,v,t).catch(z=>{F.warn("ai augmentation (parallel) failed",z)});const W=((P=b[0])==null?void 0:P.pageUrl)??((T=b[0])==null?void 0:T.scope)??"";k=nd({tabId:i,componentId:v,pageUrl:W,results:b,verifyFixesOnly:o.verifyFixesOnly}).catch(z=>(F.warn("1.4.11 walkthrough (parallel) failed",z),{criterionId:"1.4.11",ran:!0,ok:!1,error:String(z)}))}}catch(me){F.debug("ai augmentation (parallel) gate-read failed",me)}!L&&j.pseudoState==="default"&&j.theme==="light"&&j.direction==="ltr"&&j.breakpoint.id==="desktop"&&b[0]&&(L=!0,$=So(i,e,a,b,j).catch(C=>{F.debug("lightweight deep analyzers (parallel) failed",C)}))}}catch(O){w=O instanceof Error?O.message:String(O),F.warn(`state ${D+1}/${h.length} threw`,O)}q({type:"AUDIT_PROGRESS_EVENT",current:D+1,total:h.length,currentState:j,componentId:v||void 0}),r||await Tt({})}if(s&&v){const D={pseudoState:"default",ariaVariation:null,theme:"light",direction:"ltr",breakpoint:{id:"desktop",label:"Desktop",width:1280,height:800,deviceScaleFactor:1,mobile:!1}};if(L){if($)try{await $}catch{}}else{try{await on(i,D,e,a),await new Promise(R=>setTimeout(R,200))}catch(R){F.debug("reference-state drive failed; analyzers will use last matrix state",R)}await So(i,e,a,b,D).catch(R=>{F.debug("lightweight deep analyzers (post-matrix fallback) failed",R)})}try{const R=await Ve("aiConfig",{}),V=ve(R);if(V.enabled&&V.apiKey&&v&&b[0]){const J=b[0].pageUrl??b[0].scope??"";I=ad({tabId:i,componentId:v,pageUrl:J,results:b,verifyFixesOnly:o.verifyFixesOnly}).catch(X=>(F.warn("1.3.2 walkthrough (parallel) failed",X),{criterionId:"1.3.2",ran:!0,ok:!1,error:String(X)}))}}catch(R){F.debug("1.3.2 walkthrough (parallel) gate-read failed",R)}try{if(S)await S;else{const R=await Ve("aiConfig",{}),V=ve(R);V.enabled&&V.apiKey&&await ra(i,e,a,V,b,v,t)}}catch(R){F.warn("ai augmentation skipped",R)}try{const R=await Ve("aiConfig",{}),V=ve(R);if(V.enabled&&V.apiKey&&v&&b[0]){await Vt(i,e,a).catch(me=>F.warn("pre-walkthrough resetState failed",me));const J=b[0].pageUrl??b[0].scope??"",X=new AbortController,ye=setInterval(()=>{t.isCanceled()&&X.abort()},250),we=await td({tabId:i,componentId:v,pageUrl:J,results:b,signal:X.signal,verifyFixesOnly:o.verifyFixesOnly}).finally(()=>clearInterval(ye));F.info("keyboard walkthroughs complete",we)}}catch(R){F.warn("keyboard walkthroughs failed",R)}try{const R=[];k&&R.push(await k),I&&R.push(await I),R.length>0&&F.info("parallel AI walkthroughs joined",R)}catch(R){F.warn("parallel AI walkthrough join failed",R)}try{if(v&&b[0]){const R=b[0].pageUrl??b[0].scope??"",V=new AbortController,J=setInterval(()=>{t.isCanceled()&&V.abort()},250),X=await od({tabId:i,componentId:v,pageUrl:R,results:b,signal:V.signal,verifyFixesOnly:o.verifyFixesOnly}).finally(()=>clearInterval(J));F.info("2.5.3 label-in-name walkthrough complete",X)}}catch(R){F.warn("2.5.3 walkthrough failed",R)}try{if(v&&b[0]){await Vt(i,e,a).catch(ye=>F.warn("pre-sampler resetState failed",ye));const R=new AbortController,V=setInterval(()=>{t.isCanceled()&&R.abort()},250),{resolved:J,skipped:X}=await Ad(i,b,R.signal).finally(()=>clearInterval(V));F.info(`pixel contrast sampler: resolved=${J} skipped=${X}`)}}catch(R){F.warn("pixel contrast sampler failed",R)}const j=b.flatMap(R=>R.announcements??[]),O=b.flatMap(R=>R.focusEvents??[]),G=await En(v,y,{announcements:j,focusEvents:O},d);try{const R=await kn(b.map(re=>re.componentId)),V=await ur(b.map(re=>re.componentId)),J=new Set;for(const re of new Set(b.map(x=>x.componentId))){const x=await ha(re);for(const B of x)J.add(B)}const X=[...new Set(b.map(re=>re.componentId))],ye=await vr(X),we=((N=b[0])==null?void 0:N.pageUrl)??((M=b[0])==null?void 0:M.scope)??"",me=we?await fa(we):[],C=new Set(b.map(re=>re.pageUrl??re.scope??"").filter(Boolean)).size>1?"site-crawl":"single-page",W=Be(y,{runs:[],workflows:Re},b,J,R,V,ye,C,me),z={...W.score,letter:W.combined},Q=b.reduce((re,x)=>re+x.durationMs,0),he=await de(),oe=le(he,"forensicLog")?await Sr(b,z,Q):null;if(oe){const re=await de();if(le(re,"forensicAnchoring"))try{const x=await sa(),B=await Kr(oe,x??void 0);B&&await Cr(oe.componentId,oe.capturedAt,B)}catch(x){F.warn("forensic anchoring failed (entry stays local-only)",x)}}}catch(R){F.warn("forensic log recording failed",R)}if(p)try{await _i({lookupKey:m,fingerprint:p,axeVersion:ko,matrixConfigHash:u,disabledRulesHash:f,results:b,delta:G,componentId:v,cachedAt:Date.now()})}catch(R){F.warn("audit-cache write failed",R)}try{await chrome.scripting.executeScript({target:{tabId:i,frameIds:a!==void 0?[a]:void 0},world:"MAIN",func:()=>{const R=document.getElementById("__wcagcheckr_audit_freeze");R&&R.remove()}})}catch(R){F.debug("audit-end state-freeze cleanup failed",R)}r||q({type:"AUDIT_COMPLETE_EVENT",componentId:v,results:b,delta:G})}else r||q({type:"AUDIT_FAILED_EVENT",error:{code:"AXE_FAILED",message:w?`All audits failed. Last error: ${w}`:"No state produced results. Selector may not exist in the audit target frame.",recoverable:!0}});r||await nt()}catch(D){F.error("single audit failed",D),r||q({type:"AUDIT_FAILED_EVENT",componentId:v||void 0,error:Vn(D)?D.payload:{code:"UNKNOWN",message:String(D),recoverable:!1}}),w=Vn(D)?D.payload.message:String(D)}finally{await At(i,{type:"LIVE_REGIONS_DISARM_REQUEST"},a).catch(()=>{}),await At(i,{type:"FOCUS_TRACE_DISARM_REQUEST"},a).catch(()=>{}),await Vt(i,e,a)}return{ok:s,results:b,error:s?void 0:w}}async function Xr(e){var t;try{const n=(await chrome.scripting.executeScript({target:{tabId:e,allFrames:!0},world:"MAIN",func:()=>{var r;const o=window;return{detected:typeof o.__STORYBOOK_PREVIEW__=="object",version:(r=o.__STORYBOOK_PREVIEW__)==null?void 0:r.version}}})).find(o=>{var r;return(r=o.result)==null?void 0:r.detected});if(n)return{detected:!0,version:(t=n.result)==null?void 0:t.version,frameId:n.frameId}}catch(a){F.warn("storybook detection scripting failed",a)}return{detected:!1}}async function Yd(e){if(await Fe()){q({type:"AUDIT_FAILED_EVENT",error:{code:Ct,message:He,recoverable:!1}});return}const t=await pt(),a=await ei(t);if(await chrome.storage.local.set({"storybook:lastDetected":{detected:a.kind==="storybook"&&a.detected,version:a.version,detectedAt:new Date().toISOString()},"playground:lastDetected":{kind:a.kind,detected:a.detected,version:a.version,detectedAt:new Date().toISOString()}}),!a.detected){q({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(a.kind==="bit"){q({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 n=a.frameId??0,o=a.kind==="storybook"?await Qd(t,n):await eu(t,n);if(!o.length){F.warn(`${a.kind} detected but no stories returned`),q({type:"AUDIT_FAILED_EVENT",error:{code:"STORYBOOK_NOT_DETECTED",message:`${a.kind==="ladle"?"Ladle":"Storybook"} detected but no stories were returned. Try reloading the page.`,recoverable:!0}});return}await qe({sessionId:Et(),mode:"storybook-all",totalStories:o.length,completedStories:[],startedAt:new Date().toISOString(),lastProgressAt:new Date().toISOString(),state:"running"});for(const r of o){if(e.isCanceled()){F.info(`${a.kind} iteration canceled by user`);break}try{if(a.kind==="storybook"){await Zd(t,n,r.id);const c=await At(t,{type:"STORYBOOK_GET_STORY_TARGET_REQUEST",tabId:t},n);await it(c.selector,e,n)}else await tu(t,n,r.id),await it("#ladle-root",e,n)}catch(c){F.warn(`story ${r.id} failed; continuing with next`,c)}const i=await wa();if((i==null?void 0:i.state)==="interrupted"){F.info("storybook iteration interrupted");break}await Tt({completedStories:[...(i==null?void 0:i.completedStories)??[],r.id]})}q({type:"SCORECARD_UPDATED_EVENT"}),await nt()}async function Jd(e){if(await Fe()){q({type:"AUDIT_FAILED_EVENT",error:{code:Ct,message:He,recoverable:!1}});return}const t=await de();if(!le(t,"allFramesAudit")){q({type:"AUDIT_FAILED_EVENT",error:{code:"FEATURE_NOT_AVAILABLE",message:"All-frames audit is not available on your current plan.",recoverable:!1}});return}const a=await pt();let n=[];try{n=(await chrome.scripting.executeScript({target:{tabId:a,allFrames:!0},func:()=>({url:window.location.href})})).map(r=>{var i;return{frameId:r.frameId,url:((i=r.result)==null?void 0:i.url)??""}}).filter(r=>/^https?:/i.test(r.url))}catch(o){F.warn("multi-frame discovery failed",o)}if(n.length===0){q({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 qe({sessionId:Et(),mode:"all-frames",totalFrames:n.length,completedFrames:[],startedAt:new Date().toISOString(),lastProgressAt:new Date().toISOString(),state:"running"});for(const o of n){if(e.isCanceled()){F.info("all-frames iteration canceled");break}try{await it("html",e,o.frameId)}catch(i){F.warn(`frame ${o.frameId} (${o.url.slice(0,80)}) audit failed; continuing`,i)}const r=await wa();if((r==null?void 0:r.state)==="interrupted"){F.info("all-frames iteration interrupted");break}await Tt({completedFrames:[...(r==null?void 0:r.completedFrames)??[],o.frameId]})}q({type:"SCORECARD_UPDATED_EVENT"}),await nt()}async function Xd(e){var o;const t=await de();if(!le(t,"parallelScan")){q({type:"AUDIT_FAILED_EVENT",error:{code:"FEATURE_NOT_AVAILABLE",message:"Parallel scan is not available on your current plan.",recoverable:!1}});return}const a=await Zo();let n;if(a!==null)try{n=(await chrome.tabs.get(a)).url}catch(r){F.warn("failed to get target tab url",r)}if(!n){q({type:"AUDIT_FAILED_EVENT",error:{code:"NETWORK",message:"Could not determine target URL for parallel scan.",recoverable:!1}});return}await qe({sessionId:Et(),mode:"single-element",scope:"html",startedAt:new Date().toISOString(),lastProgressAt:new Date().toISOString(),state:"running"});try{const r=await Pn(),{runParallelTabAudit:i}=await dn(async()=>{const{runParallelTabAudit:m}=await import("./parallel-tab-flow-CTenSMHR.js");return{runParallelTabAudit:m}},__vite__mapDeps([0,1,2,3,4])),c=await i({targetUrl:n,scope:"html",matrix:r,parallelism:4,cancel:e});if(F.info(`parallel scan complete: ${c.results.length} results across ${c.tabsCompleted} tabs (${c.tabsFailed} failed) in ${Math.round(c.parallelDurationMs)}ms`),c.results.length===0){q({type:"AUDIT_FAILED_EVENT",error:{code:"NETWORK",message:`All ${c.tabsFailed} worker tabs failed. Falling back to Full Page scan is recommended.`,recoverable:!0}});return}const d=((o=c.results[0])==null?void 0:o.componentId)??"parallel-scan";if(a!==null)try{const m=await Ve("aiConfig",{}),u=ve(m);u.enabled&&u.apiKey&&await ra(a,"html",void 0,u,c.results,d,e)}catch(m){F.warn("parallel scan: ai augmentation skipped",m)}const l=c.results.flatMap(m=>m.violations);let h;try{h=await En(d,l)}catch(m){F.warn("parallel scan: baseline compare failed",m),h={new:[],persistent:[],fixed:[],newCount:0,persistentCount:0,fixedCount:0,baselineSnapshotMeta:null,comparedAt:new Date().toISOString()}}q({type:"AUDIT_COMPLETE_EVENT",componentId:d,results:c.results,delta:h}),q({type:"SCORECARD_UPDATED_EVENT"})}catch(r){F.warn("parallel scan failed",r),q({type:"AUDIT_FAILED_EVENT",error:{code:"UNKNOWN",message:r instanceof Error?r.message:String(r),recoverable:!0}})}finally{await nt()}}async function Qd(e,t){var a;try{return((a=(await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",func:async()=>{var d,l;const o=window.__STORYBOOK_PREVIEW__;if(!o)return[];const r=o.storyStoreValue??o.storyStore;if(!r)return[];typeof r.cacheAllCSFFiles=="function"&&await r.cacheAllCSFFiles();const i=((d=r.extract)==null?void 0:d.call(r))??((l=r.cachedCSFFiles)==null?void 0:l.call(r));if(!i)return[];const c=[];if(i instanceof Map)for(const[h,m]of i){const u=m;c.push({id:String(h),name:u.name??String(h),kind:u.title??u.kind??""})}else if(typeof i=="object"&&i!==null)for(const[h,m]of Object.entries(i)){const u=m;c.push({id:h,name:u.name??h,kind:u.title??u.kind??""})}return c}}))[0])==null?void 0:a.result)??[]}catch(n){return F.warn("listStoriesViaMainWorld failed",n),[]}}async function Zd(e,t,a){try{await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",args:[a,3e3],func:async(n,o)=>{const r=new URL(window.location.href);r.searchParams.set("id",n),r.searchParams.set("viewMode","story"),window.location.href!==r.href&&(window.history.pushState({},"",r.href),window.dispatchEvent(new PopStateEvent("popstate"))),await new Promise(i=>{const c=setTimeout(()=>i(),o),d=()=>{clearTimeout(c),i()};window.addEventListener("storyrendered",d,{once:!0})})}})}catch(n){F.warn(`navigateToStoryViaMainWorld(${a}) failed`,n)}}async function Qr(e){var t;try{const n=(await chrome.scripting.executeScript({target:{tabId:e,allFrames:!0},world:"MAIN",func:()=>{const o=document.getElementById("ladle-root")!==null,r=document.title==="Ladle",i=!!document.querySelector('link[href*="ladle.css"]');return{detected:o&&(r||i),version:void 0}}})).find(o=>{var r;return(r=o.result)==null?void 0:r.detected});if(n)return{detected:!0,version:(t=n.result)==null?void 0:t.version,frameId:n.frameId}}catch(a){F.warn("ladle detection scripting failed",a)}return{detected:!1}}async function eu(e,t){var a;try{return((a=(await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",func:async()=>{try{const o=await fetch(new URL("/meta.json",window.location.href).href,{credentials:"same-origin"});if(!o.ok)return[];const i=(await o.json()).stories??{};return Object.entries(i).map(([c,d])=>({id:c,name:d.name??c,kind:(d.levels??[]).join(" / ")}))}catch{return[]}}}))[0])==null?void 0:a.result)??[]}catch(n){return F.warn("listLadleStoriesViaMainWorld failed",n),[]}}async function tu(e,t,a){try{await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",args:[a,3e3],func:async(n,o)=>{const r=new URL(window.location.href);r.searchParams.set("story",n),window.location.href!==r.href&&(window.history.pushState({},"",r.href),window.dispatchEvent(new PopStateEvent("popstate"))),await new Promise(i=>setTimeout(i,Math.min(o,500)))}})}catch(n){F.warn(`navigateToLadleStoryViaMainWorld(${a}) failed`,n)}}async function Zr(e){try{return(await chrome.scripting.executeScript({target:{tabId:e,allFrames:!0},world:"MAIN",func:()=>{const a=window.location.href;return/bit\.dev|bit-dev|bitsrc|\/~aspect|\/composition/i.test(a)}})).some(a=>a.result===!0)}catch{return!1}}async function ei(e){const t=await Xr(e);if(t.detected)return{kind:"storybook",...t};const a=await Qr(e);return a.detected?{kind:"ladle",...a}:await Zr(e)?{kind:"bit",detected:!0}:{kind:null,detected:!1}}const aa=200,ti=6e4;function na(e,t){try{return new URL(e).origin===new URL(t).origin}catch{return!1}}function ai(e,t){try{const a=new URL(e,t);return a.hash="",a.toString()}catch{return null}}async function ni(e,t,a){await chrome.tabs.update(e,{url:t}),await new Promise((n,o)=>{const r=setTimeout(()=>{chrome.tabs.onUpdated.removeListener(i),o(new Error(`navigation timeout (${a}ms): ${t}`))},a);function i(c,d){c===e&&d.status==="complete"&&(clearTimeout(r),chrome.tabs.onUpdated.removeListener(i),n())}chrome.tabs.onUpdated.addListener(i)}),await new Promise(n=>setTimeout(n,400))}async function oi(e){var t;try{return((t=(await chrome.scripting.executeScript({target:{tabId:e},func:()=>Array.from(document.querySelectorAll("a[href]")).map(n=>n.getAttribute("href")).filter(n=>typeof n=="string"&&!n.startsWith("javascript:"))}))[0])==null?void 0:t.result)??[]}catch(a){return F.debug("link discovery failed",a),[]}}async function ri(e,t){var a,n;try{const r=new URL(t).pathname.toLowerCase().replace(/\/+$/,"");if(/(?:^|\/)(?:404|not-found|page-not-found|error|server-error)$/.test(r))return`URL path "${r||"/"}" matches a common error-route pattern.`}catch{}try{const o=await chrome.scripting.executeScript({target:{tabId:e},func:()=>document.title||""}),r=((n=(a=o==null?void 0:o[0])==null?void 0:a.result)==null?void 0:n.trim())??"";if(!r)return null;const i=[[/^4(?:0[0-46-9]|[1-9]\d)\b/,"title starts with a 4xx error code"],[/^5\d{2}\b/,"title starts with a 5xx error code"],[/^(?:page\s+)?not\s+found\b/i,'title starts with "Not Found"'],[/^error\s*[-:]?\s*\d{3}\b/i,'title starts with "Error" + status code'],[/^(?:oops|whoops)[\s,!:.-]/i,'title leads with "Oops" — common error-template pattern']];for(const[c,d]of i)if(c.test(r))return`${d} (title: "${r.slice(0,80)}").`;return null}catch(o){return F.debug("error-page detection failed; proceeding with audit",o),null}}const Co=/Linked PDF at (\S+)\./;async function ii(e,t){if(!le(t,"pdfAudit")){F.info("PDF audit disabled for tier; skipping crawl PDF pass",{tier:t});return}const a=new Map;for(const n of e)if(!n.error)for(const o of n.results)for(const r of o.violations){if(r.ruleId!=="pdf-needs-cli-audit")continue;const i=Co.exec(r.description);if(!i)continue;const c=i[1];let d;try{d=new URL(c,n.url).href}catch{continue}if(!/^https?:\/\//i.test(d))continue;let l=a.get(d);l||(l=new Set,a.set(d,l)),l.add(n)}if(a.size!==0){F.info(`PDF audit pass: ${a.size} unique PDF(s) across the crawl`);for(const[n,o]of a){let r;try{r=await Ic(n)}catch(i){F.warn(`PDF audit threw for ${n}`,i);continue}for(const i of o){const c=i.results[0];if(!c)continue;const d=c.componentId,l=c.state,h=r.findings.map(m=>$o(m,n,d,l));c.violations=c.violations.filter(m=>{if(m.ruleId!=="pdf-needs-cli-audit")return!0;const u=Co.exec(m.description);if(!u)return!0;try{return new URL(u[1],i.url).href!==n}catch{return!0}}),c.violations.push(...h),r.fatalError&&c.violations.push($o({ruleId:"pdf-audit-skipped",impact:"minor",wcagCriterion:"1.1.1",description:`PDF audit skipped: ${r.fatalError}`,failureSummary:"The PDF could not be audited automatically. Run `wcagcheckr-ci pdf <url>` against this PDF on the command line for a manual audit."},n,d,l))}}}}function $o(e,t,a,n){return{ruleId:e.ruleId,wcagCriterion:e.wcagCriterion,wcagLevel:"AA",impact:e.impact,description:e.description,helpUrl:"https://www.w3.org/WAI/standards-guidelines/wcag/",target:{selector:t,outerHTML:`<a href="${t}">[PDF]</a>`,failureSummary:e.failureSummary,tagName:"PDF",role:null,accessibleName:null,textSnippet:null,attrId:null,attrTestid:null},componentId:a,currentState:n,axeVersion:"pdf-audit",detectedAt:new Date().toISOString(),matchKey:`pdf::${e.ruleId}::${t}`}}async function au(e,t,a){if(await Fe())return q({type:"CRAWL_DISCOVERY_FAILED_EVENT",error:{code:Ct,message:He,recoverable:!1}}),[];const n=await de();if(!le(n,"siteCrawl"))return q({type:"CRAWL_DISCOVERY_FAILED_EVENT",error:{code:"FEATURE_NOT_AVAILABLE",message:"Site crawl is not available on your current plan.",recoverable:!1}}),[];const o=await pt(),r=Wo(n).siteCrawlMaxPages,i=Number.isFinite(r)?Math.min(r,aa):aa,c=Math.min(t.maxPages??25,i),d=t.includeRegex?oa(t.includeRegex):null,l=t.excludeRegex?oa(t.excludeRegex):null,h=[e],m=new Set,u=[];try{for(;h.length>0&&u.length<c;){if(a.isCanceled()){F.info("crawl discovery canceled by user");break}const g=h.shift();if(!(!g||m.has(g))&&na(g,e)&&!(d&&!d.test(g))&&!(l&&l.test(g))){m.add(g);try{if(await ni(o,g,ti),!(await Ln(o,0)).ok||await ri(o,g))continue;u.push(g),q({type:"CRAWL_DISCOVERY_PROGRESS_EVENT",found:u.length,url:g});const y=await oi(o);for(const b of y){const v=ai(b,g);v&&na(v,e)&&!m.has(v)&&!h.includes(v)&&h.push(v)}}catch(f){F.debug("discovery: page navigation/link-read failed; skipping",f);continue}await Tt({})}}return q({type:"CRAWL_DISCOVERY_COMPLETE_EVENT",urls:u}),u}catch(g){return F.error("crawl discovery failed",g),q({type:"CRAWL_DISCOVERY_FAILED_EVENT",error:{code:"CRAWL_DISCOVERY_FAILED",message:String(g),recoverable:!0}}),[]}}async function nu(e,t,a){if(await Fe()){q({type:"SITE_CRAWL_FAILED_EVENT",error:{code:Ct,message:He,recoverable:!1}});return}const n=await de();if(!le(n,"siteCrawl")){q({type:"SITE_CRAWL_FAILED_EVENT",error:{code:"FEATURE_NOT_AVAILABLE",message:"Site crawl is not available on your current plan.",recoverable:!1}});return}const o=await pt(),r=Wo(n).siteCrawlMaxPages,i=Number.isFinite(r)?Math.min(r,aa):aa,c=Array.isArray(t.urls)&&t.urls.length>0,d=Math.min(c?t.urls.length:t.maxPages??25,i),l=t.includeRegex?oa(t.includeRegex):null,h=t.excludeRegex?oa(t.excludeRegex):null,m=new Date().toISOString(),u=Date.now(),g=c?[...t.urls]:[e],f=new Set,p=[];await qe({sessionId:Et(),mode:"storybook-all",scope:e,startedAt:m,lastProgressAt:new Date().toISOString(),state:"running"});try{for(;g.length>0&&f.size<d;){if(a.isCanceled()){F.info("site crawl canceled by user");break}const A=g.shift();if(!A||f.has(A)||!na(A,e)||l&&!l.test(A)||h&&h.test(A))continue;f.add(A);const S=f.size,k=Date.now();q({type:"SITE_CRAWL_PROGRESS_EVENT",current:S,total:Math.min(d,f.size+g.length),url:A,status:"auditing"});try{await ni(o,A,ti);const I=await Ln(o,0);if(!I.ok)throw new Error(cn(I));const L=await ri(o,A);if(L){const N=`Skipped — page appears to be an error response. ${L}`;p.push({url:A,results:[],delta:null,componentId:A,durationMs:Date.now()-k,error:N}),q({type:"SITE_CRAWL_PROGRESS_EVENT",current:S,total:Math.min(d,f.size+g.length),url:A,status:"failed",error:N});continue}await ke(o,{type:"WARMUP_REQUEST"},0).catch(()=>{});const $=await it("html",a,0,void 0,{asSubroutine:!0,tabIdOverride:o,verifyFixesOnly:t.verifyFixesOnly}),_=$.results,P=$.ok?void 0:$.error,T=Date.now()-k;if(_.length>0){const N=await xc(o,A).catch(()=>null),M=_.reduce((D,j)=>D+j.violations.length,0);p.push({url:A,results:_,delta:null,componentId:_[0].componentId,durationMs:T,snapshot:N??void 0}),q({type:"SITE_CRAWL_PROGRESS_EVENT",current:S,total:Math.min(d,f.size+g.length),url:A,status:"completed",violations:M})}else{const N=P??"audit returned no result";p.push({url:A,results:[],delta:null,componentId:A,durationMs:T,error:N}),q({type:"SITE_CRAWL_PROGRESS_EVENT",current:S,total:Math.min(d,f.size+g.length),url:A,status:"failed",error:N})}if(!c){const N=await oi(o);for(const M of N){const D=ai(M,A);D&&na(D,e)&&!f.has(D)&&!g.includes(D)&&g.push(D)}}}catch(I){const L=Date.now()-k,$=I instanceof Error?I.message:String(I);p.push({url:A,results:[],delta:null,componentId:A,durationMs:L,error:$}),q({type:"SITE_CRAWL_PROGRESS_EVENT",current:S,total:Math.min(d,f.size+g.length),url:A,status:"failed",error:$})}await Tt({})}try{const A=await de();await ii(p,A)}catch(A){F.warn("PDF audit pass failed",A)}const y=new Date().toISOString(),b=Date.now()-u,v=p.flatMap(A=>A.results),w=await Ee(v,[e]),s=Er(e,p,m,y,b,w.walkthroughsByComponent,w.walkthroughAcksByComponent,w.ackedKeysByComponent,w.humanVerdictsByComponent,w.incompleteResolutionsByComponent);if(s.consistencyFindings.length>0)try{const A=await Jc(s.consistencyFindings);s.consistencyFindings=A.emitted}catch(A){F.warn("consistency AI suppression failed; emitting heuristic findings",A)}try{const A=p.map(S=>({...S,results:S.results.map(({screenshotDataUrl:k,...I})=>I)}));await Qo(A,{startUrl:e,finishedAt:y})}catch(A){const S=A instanceof Error?A.message:String(A);F.error("persisting siteCrawlPerUrlResults failed",A),q({type:"SITE_CRAWL_FAILED_EVENT",error:{code:"CRAWL_PERSIST_FAILED",message:`Crawl finished but persistence failed (${S}). The AI fix prompt export won't have per-page data.`,recoverable:!1}})}q({type:"SITE_CRAWL_COMPLETE_EVENT",report:s}),await si(s),await nt()}catch(y){F.error("site crawl failed",y),q({type:"SITE_CRAWL_FAILED_EVENT",error:{code:"UNKNOWN",message:String(y),recoverable:!1}}),await nt()}}function oa(e){try{return new RegExp(e)}catch{return null}}async function ou(){const[e,t]=await Promise.all([bn(),Ht()]);if(!e)return{ok:!1,reason:"No crawl report cached. Run a site crawl first."};if(!t||t.length===0)return{ok:!1,reason:"Crawl report exists but per-URL audit data was not retained. Re-run the crawl once; future regrades will work."};const a=t.flatMap(i=>i.results),n=await Ee(a,[e.startUrl]),o=new Date().toISOString(),r=Er(e.startUrl,t,e.startedAt,o,e.totalDurationMs,n.walkthroughsByComponent,n.walkthroughAcksByComponent,n.ackedKeysByComponent,n.humanVerdictsByComponent,n.incompleteResolutionsByComponent);return await Xo(r),q({type:"SITE_CRAWL_COMPLETE_EVENT",report:r}),await si(r),{ok:!0}}async function si(e){try{const t=await de();if(!le(t,"forensicAnchoring"))return;const a={domain:Ud(e.startUrl),grade:e.siteGrade,scope:"site",pageCount:e.pagesAudited,wcagLevel:`${St} ${hn}`,auditedAt:e.finishedAt,productSlug:"wcagcheckr"},n=await _d(a),o=await sa(),r=await Yr(n,e.startUrl,e.finishedAt,o??void 0);await jd({domain:a.domain,claim:a,hash:n,receipt:r}),q({type:"SITE_CLAIM_SEALED_EVENT",domain:a.domain})}catch(t){F.warn("site-claim seal/anchor failed (badge unavailable for this crawl)",t)}}const ja=5*6e4,To=9e4,Eo={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…"},ru={altText:["1.1.1"],headings:["2.4.6"],sensory:["1.3.3"],aria:["4.1.2"],imageOfText:["1.4.5"],colorOnlyMeaning:["1.4.1"],genericLinkText:["2.4.4"],wallOfText:[],languageMismatch:["3.1.1","3.1.2"]};async function ra(e,t,a,n,o,r,i){const c=await de();if(!le(c,"aiJudgment")){F.debug("aiJudgment not allowed on current tier — skipping AI augmentation");return}let d;try{d=await ke(e,{type:"AI_CANDIDATES_REQUEST",selector:t},a)}catch(T){F.debug("ai candidates fetch failed",T);return}const l=o[0];if(!l)return;const h={componentId:r,currentState:l.state,axeVersion:l.axeVersion},m={at:new Date().toISOString(),componentId:r,pageUrl:l.pageUrl??t,enabledChecks:n.enabledChecks,candidateCounts:{images:d.images.length,headings:d.headings.length,instructions:d.instructions.length,ariaElements:d.ariaElements.length,links:d.links.length,longTextBlocks:d.longTextBlocks.length,colorOnlyRegions:d.colorOnlyRegions.length,languageInfo:d.languageInfo?"present":"null"}};F.info("ai augmentation gate snapshot",JSON.stringify(m));try{await chrome.storage.local.set({aiDiagnosticLatest:m})}catch(T){F.warn("failed to persist ai diagnostic to storage.local",T)}const u=[];if(n.enabledChecks.altText&&u.push({key:"altText",run:T=>{const N=d.images.map(M=>({imageUrl:M.imageUrl,alt:M.alt,selector:M.selector,outerHTML:M.outerHTML,surroundingContext:M.surroundingContext,...h}));return Tl(N,n,T)}}),n.enabledChecks.headings&&u.push({key:"headings",run:T=>kd(d.headings,h,n,T)}),n.enabledChecks.sensory&&u.push({key:"sensory",run:T=>Id(d.instructions,h,n,T)}),n.enabledChecks.aria&&u.push({key:"aria",run:T=>xd(d.ariaElements,h,n,T)}),n.enabledChecks.imageOfText&&u.push({key:"imageOfText",run:T=>{const N=d.images.map(M=>({imageUrl:M.imageUrl,accessibleName:M.alt||void 0,selector:M.selector,outerHTML:M.outerHTML,pixelArea:M.pixelArea}));return Td(N,h,n,T)}}),n.enabledChecks.colorOnlyMeaning&&u.push({key:"colorOnlyMeaning",run:T=>Ed(d.colorOnlyRegions,h,n,T)}),n.enabledChecks.genericLinkText&&u.push({key:"genericLinkText",run:T=>Sd(d.links,h,n,T)}),n.enabledChecks.wallOfText&&u.push({key:"wallOfText",run:T=>Cd(d.longTextBlocks,h,n,T)}),n.enabledChecks.languageMismatch){const T=d.languageInfo;u.push({key:"languageMismatch",run:N=>$d(T?[T]:[],h,n,N)})}if(u.length===0)return;let g=0,f=0,p=!1;const y={};let b=0,v=0,w=0;const s=[];let A=!1,S=!1;function k(T,N){if(y[T]=(y[T]??0)+N.totalCostUsd,g+=N.totalCostUsd,f+=N.findings.length,p=p||N.capExceeded,b++,N.errors.length>0){w++;for(const M of N.errors)s.includes(M)||s.push(M)}else v++;F.info(`ai ${T}: ${N.findings.length} findings, $${N.totalCostUsd.toFixed(4)}, errs=${N.errors.length}`),N.capExceeded&&F.warn(`ai cost cap exceeded after $${N.totalCostUsd.toFixed(4)} during ${T}`)}const I=(async()=>{for(let T=0;T<u.length;T++){if(i!=null&&i.isCanceled()){S=!0;break}const N=u[T];q({type:"AI_AUGMENTATION_PROGRESS_EVENT",componentId:r,currentCheck:N.key,currentCheckLabel:Eo[N.key],current:T+1,total:u.length});const M=new AbortController,D=setTimeout(()=>M.abort(),To),j=i?setInterval(()=>{i.isCanceled()&&M.abort()},500):null;let O;try{O=await N.run({signal:M.signal,onProgress:(G,R)=>{R>0&&q({type:"AI_AUGMENTATION_PROGRESS_EVENT",componentId:r,currentCheck:N.key,currentCheckLabel:Eo[N.key],current:T+1,total:u.length,candidatesDone:G,candidatesTotal:R})}}),M.signal.aborted&&(i!=null&&i.isCanceled())?O.errors=[...O.errors,`${N.key} canceled by user`]:M.signal.aborted}catch(G){const R=G instanceof Error?G.message:String(G),V=M.signal.aborted,J=V&&(i!=null&&i.isCanceled())?`${N.key} canceled by user`:V?`${N.key} hit per-check ${To/1e3}s cap — analyzer aborted`:`${N.key} threw: ${R}`;O={findings:[],totalCostUsd:0,capExceeded:!1,errors:[J]}}finally{clearTimeout(D),j&&clearInterval(j)}if(O.findings.length>0&&(l.violations=[...l.violations,...O.findings]),O.errors.length===0){const G=ru[N.key];if(G&&G.length>0&&l.axeRulesEvaluated)for(const R of G)l.axeRulesEvaluated.passed.push({ruleId:`ai-${N.key}-coverage`,wcagCriterion:R})}k(N.key,O)}})(),L=new Promise(T=>{setTimeout(()=>T("timeout"),ja)});await Promise.race([I.then(()=>"done"),L])==="timeout"&&(A=!0,F.warn(`ai augmentation hit global ${ja/1e3}s timeout after ${b}/${u.length} checks`));let _;if(w>0||A||S){A?_=`AI augmentation timed out after ${ja/1e3}s`:S?_="AI augmentation canceled":_=s[0]??"AI analyzer error";const T=v===0?"total":"partial";q({type:"AI_AUGMENTATION_FAILED_EVENT",componentId:r,severity:T,reason:_,checksAttempted:b,checksSucceeded:v,checksErrored:w,errorDetails:A||S?[_,...s.slice(0,4)]:s.slice(0,5)}),F.warn(`ai augmentation ${T} failure: ${w}/${b} checks errored — ${_}`)}(g>0||f>0||_)&&await Ui({at:new Date().toISOString(),pageUrl:l.pageUrl??t,componentId:r,totalCostUsd:g,byCheck:y,findingsCount:f,capExceeded:p,failureReason:_})}const cp=Object.freeze(Object.defineProperty({__proto__:null,auditCrawlPdfs:ii,detectBit:Zr,detectLadleAcrossFrames:Qr,detectPlayground:ei,detectStorybookAcrossFrames:Xr,discoverCrawlUrls:au,getActiveTabId:pt,getMatrixSettings:Pn,regradeSiteCrawl:ou,runAiAugmentation:ra,runAllFramesAudit:Jd,runParallelScan:Xd,runSingleElementAudit:it,runSiteCrawl:nu,runStorybookAllAudit:Yd},Symbol.toStringTag,{value:"Module"})),wt=te("alert-sender"),iu="wcagcheckr",su="https://api.wcagcheckr.com",cu=`${su}/v1/products/${iu}/alerts/send`,lu=1e4,ln="alertConfig:v1",du={emailEnabled:!1,webhookEnabled:!1,webhookUrl:"",minImpact:"serious"},Ut={minor:1,moderate:2,serious:3,critical:4};async function uu(){const t=(await chrome.storage.local.get(ln))[ln];return{...du,...t??{}}}async function lp(e){await chrome.storage.local.set({[ln]:e})}async function hu(e){let t;try{t=await uu()}catch(f){return wt.warn("failed to read alert config",f),{fired:!1,reason:"config_read_failed"}}if(!t.emailEnabled&&!t.webhookEnabled)return{fired:!1,reason:"no_channel_enabled"};const a=await de(),n=le(a,"emailAlerts"),o=le(a,"webhookAlerts"),r=t.emailEnabled&&n,i=t.webhookEnabled&&o;if(!r&&!i)return{fired:!1,reason:"no_channel_enabled"};const c=[];for(const f of e.results){const p=f.delta;p!=null&&p.new&&c.push(...p.new)}const d=Ut[t.minImpact],l=c.filter(f=>Ut[f.impact]>=d);if(l.length===0)return{fired:!1,reason:"below_impact_threshold"};const h=await sa();if(!h)return wt.debug("no license token; alert skipped"),{fired:!1,reason:"no_license_token"};l.sort((f,p)=>Ut[p.impact]-Ut[f.impact]);const m=l.slice(0,10).map(f=>({wcag:f.wcagCriterion,impact:f.impact,selector:f.target.selector,help:f.description.slice(0,480),helpUrl:f.helpUrl})),u=e.results.reduce((f,p)=>f+p.violations.length,0),g={token:h,scheduleId:e.scheduleId,auditedUrl:e.auditedUrl,totalViolations:u,newViolationCount:l.length,topViolations:m,webhookUrl:i&&t.webhookUrl?t.webhookUrl:void 0,emailEnabled:r,ranAt:e.ranAt};try{const f=await fetch(cu,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(g),signal:AbortSignal.timeout(lu)}),p=await f.json().catch(()=>null);return f.ok?(wt.info("alert delivered",{emailSent:p==null?void 0:p.emailSent,webhookSent:p==null?void 0:p.webhookSent}),{fired:!0,emailSent:(p==null?void 0:p.emailSent)??!1,webhookSent:(p==null?void 0:p.webhookSent)??!1,serverStatus:f.status}):(wt.warn(`alert server returned ${f.status}`,p),{fired:!1,reason:`server_${f.status}`,serverStatus:f.status})}catch(f){return wt.warn("alert send failed",f),{fired:!1,reason:f instanceof Error?f.message:"unknown_error"}}}const pu="#dc2626",fu="#f59e0b",gu="#059669",mu="#94a3b8";function bu(e){const t=e.filter(c=>c.enabled);let a=0,n=0,o=0,r=0;for(const c of t){const d=c.lastNewViolationCount??0;c.lastResult==="failed"?n++:c.lastResult==="ok"?d>0?a+=d:o++:r++}let i;return t.length===0?i="no-schedules":a>0?i="new-violations":n>0?i="failed":o>0?i="all-clear":i="pending-first-run",{state:i,newViolationsTotal:a,failingCount:n,okCount:o,pendingCount:r,totalEnabled:t.length}}async function yu(e){switch(e.state){case"new-violations":{const t=e.newViolationsTotal>99?"99+":String(e.newViolationsTotal);await chrome.action.setBadgeText({text:t}),await chrome.action.setBadgeBackgroundColor({color:pu}),await chrome.action.setTitle({title:`wcagcheckr — ${e.newViolationsTotal} new WCAG violation${e.newViolationsTotal===1?"":"s"} vs baseline across ${e.totalEnabled} scheduled audit${e.totalEnabled===1?"":"s"}`});break}case"failed":{await chrome.action.setBadgeText({text:"!"}),await chrome.action.setBadgeBackgroundColor({color:fu}),await chrome.action.setTitle({title:`wcagcheckr — ${e.failingCount} scheduled audit${e.failingCount===1?"":"s"} failed to run`});break}case"all-clear":{await chrome.action.setBadgeText({text:"✓"}),await chrome.action.setBadgeBackgroundColor({color:gu}),await chrome.action.setTitle({title:`wcagcheckr — all ${e.okCount} scheduled audit${e.okCount===1?"":"s"} passing`});break}case"pending-first-run":{await chrome.action.setBadgeText({text:""}),await chrome.action.setBadgeBackgroundColor({color:mu}),await chrome.action.setTitle({title:`wcagcheckr — ${e.pendingCount} scheduled audit${e.pendingCount===1?"":"s"} awaiting first run`});break}case"no-schedules":default:{await chrome.action.setBadgeText({text:""}),await chrome.action.setTitle({title:"wcagcheckr"});break}}}async function wu(){const e=await de();if(!le(e,"statusBadge")){try{await chrome.action.setBadgeText({text:""})}catch{}return{state:"no-schedules",newViolationsTotal:0,failingCount:0,okCount:0,pendingCount:0,totalEnabled:0}}const t=await qo(),a=bu(t);try{await yu(a)}catch(n){console.warn("[status-badge] failed to apply",n)}return a}const Ro=te("deposition-packet");function ae(e){return e.replace(/[&<>"']/g,t=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[t])}function vu(e,t,a){if(!e)return`<section class="dp-cover dp-cover-error">
981
+ `;await chrome.debugger.sendCommand({tabId:e},"Runtime.evaluate",{expression:t})}async function ko(e,t){let a;try{a=await chrome.debugger.sendCommand({tabId:e},"Page.captureScreenshot",{format:"png",captureBeyondViewport:!0,clip:{x:t.x,y:t.y,width:t.width,height:t.height,scale:1}})}catch(n){return nt.warn(`captureElementClip failed: ${n instanceof Error?n.message:String(n)}`),null}if(!(a!=null&&a.data))return null;try{const n=`data:image/png;base64,${a.data}`,o=await fetch(n).then(d=>d.blob()),r=await createImageBitmap(o),c=new OffscreenCanvas(r.width,r.height).getContext("2d");return c?(c.drawImage(r,0,0),c.getImageData(0,0,r.width,r.height)):null}catch(n){return nt.warn(`clip decode failed: ${n instanceof Error?n.message:String(n)}`),null}}const Id=25,zr=8,Sd=150,Cd=.95,$d=4,Td=400;function Ed(e,t,a){if(e.width!==t.width||e.height!==t.height)return null;const n=e.width,o=e.height;let r=0,i=0;for(let w=0;w<o;w++)for(let s=0;s<n;s++){const v=(w*n+s)*4,S=t.data[v],k=t.data[v+1],x=t.data[v+2],L=e.data[v],$=e.data[v+1],U=e.data[v+2],F=Math.abs(L-S)+Math.abs($-k)+Math.abs(U-x);F>i&&(i=F),F>=Id&&r++}if(r<zr)return null;const c=i*Cd;let d=0,l=0,h=0,m=0;if(i>=Td)for(let w=0;w<o;w++)for(let s=0;s<n;s++){const v=(w*n+s)*4,S=t.data[v],k=t.data[v+1],x=t.data[v+2],L=e.data[v],$=e.data[v+1],U=e.data[v+2];Math.abs(L-S)+Math.abs($-k)+Math.abs(U-x)>=c&&(d+=S,l+=k,h+=x,m++)}const u=m>=$d?{r:Math.round(d/m),g:Math.round(l/m),b:Math.round(h/m)}:a,g=new Map;let p=0;for(let w=0;w<o;w++)for(let s=0;s<n;s++){const v=(w*n+s)*4,S=t.data[v],k=t.data[v+1],x=t.data[v+2];if(Math.abs(S-u.r)+Math.abs(k-u.g)+Math.abs(x-u.b)>=Sd){p++;const $=S>>4<<8|k>>4<<4|x>>4,U=g.get($);U?(U.r+=S,U.g+=k,U.b+=x,U.count++):g.set($,{r:S,g:k,b:x,count:1})}}const f=n*o;if(p<f*.2){g.clear();for(let w=0;w<o;w++)for(let s=0;s<n;s++){const v=(w*n+s)*4,S=e.data[v],k=e.data[v+1],x=e.data[v+2],L=S>>4<<8|k>>4<<4|x>>4,$=g.get(L);$?($.r+=S,$.g+=k,$.b+=x,$.count++):g.set(L,{r:S,g:k,b:x,count:1})}}let y={r:0,g:0,b:0,count:0};for(const w of g.values())w.count>y.count&&(y=w);const b={r:Math.round(y.r/y.count),g:Math.round(y.g/y.count),b:Math.round(y.b/y.count)},A=Ln(u,b);return{textPixelCount:r,worstRatio:A,worstBackground:b,worstForeground:u}}async function Rd(e,t,a){var d,l,h,m,u,g;const n=new Map;for(const p of t){const f=p.pageUrl??p.scope??"";for(const y of((d=p.axeRulesEvaluated)==null?void 0:d.incomplete)??[])if(y.ruleId==="color-contrast")for(const b of y.elements??[]){const A=`${f}::${y.ruleId}::${b.selector}`;n.has(A)||n.set(A,{pageUrl:f,ruleId:y.ruleId,selector:b.selector,wcagCriterion:y.wcagCriterion??"1.4.3",foreground:(l=b.styles)==null?void 0:l.foreground,fontSize:(h=b.styles)==null?void 0:h.fontSize,fontWeight:(m=b.styles)==null?void 0:m.fontWeight,failureSummary:b.failureSummary})}}if(n.size===0)return{resolved:0,skipped:0};let o=!1,r=null;for(let p=1;p<=6;p++)try{await chrome.debugger.attach({tabId:e},gd),o=!0;break}catch(f){r=f;const y=f instanceof Error?f.message:String(f);if(/already attached/i.test(y)){await new Promise(b=>setTimeout(b,500));continue}break}if(!o)return nt.warn("debugger attach failed after retries; skipping pixel sampling",r),{resolved:0,skipped:n.size};let i=0,c=0;try{const p=Array.from(n.values()),f=p.map(x=>x.selector),y=await Ad(e,f);if(a!=null&&a.aborted)return{resolved:0,skipped:n.size};const b=((u=p[0])==null?void 0:u.pageUrl)??"",A=b?await ba(b):[],w=new Map;for(const x of A)w.set(`${x.ruleId}::${x.selector}`,x);const s=new Map,v=[];for(const x of p){const L=await fd({...x,bgFingerprint:((g=y.get(x.selector))==null?void 0:g.bgFingerprint)??null});s.set(x.selector,L);const $=w.get(`${x.ruleId}::${x.selector}`);if($&&$.inputHash===L&&$.verdict!=="uncertain"){const U={...$,resolvedAt:new Date().toISOString(),reasoning:$.reasoning.includes("(cached, hash-match)")?$.reasoning:`${$.reasoning} (cached, hash-match)`};await je(U),i++;continue}v.push(x)}if(v.length===0)return nt.info(`pixel-contrast sampler: all ${p.length} targets reused from hash-cache`),{resolved:i,skipped:c};await kd(e);const S=new Map;for(const x of v){if(a!=null&&a.aborted)break;const L=y.get(x.selector);if(!L)continue;const $=await ko(e,L);$&&S.set(x.selector,$)}if(await xd(e),a!=null&&a.aborted)return{resolved:0,skipped:n.size};const k=new Map;for(const x of v){if(a!=null&&a.aborted)break;const L=y.get(x.selector);if(!L)continue;const $=await ko(e,L);$&&k.set(x.selector,$)}for(const x of v){if(a!=null&&a.aborted)break;const L=s.get(x.selector),$=y.get(x.selector);if(!$){c++;const V={pageUrl:x.pageUrl,ruleId:x.ruleId,selector:x.selector,verdict:"uncertain",reasoning:"Hide-text observation: element's bounding rect could not be resolved at audit time (element may be display:none, detached from DOM, or zero-size). Cannot sample contrast empirically. Manual verification required if you believe this element is visible.",resolvedAt:new Date().toISOString(),costUsd:0,wcagCriterion:x.wcagCriterion,inputHash:L};await je(V);continue}const U=S.get(x.selector),F=k.get(x.selector);if(!U||!F){c++;const V={pageUrl:x.pageUrl,ruleId:x.ruleId,selector:x.selector,verdict:"uncertain",reasoning:"Hide-text observation: per-element clip capture failed via chrome.debugger Page.captureScreenshot. Manual verification required.",resolvedAt:new Date().toISOString(),costUsd:0,wcagCriterion:x.wcagCriterion,inputHash:L};await je(V);continue}const T=wd(x.foreground);if(!T){c++;const V={pageUrl:x.pageUrl,ruleId:x.ruleId,selector:x.selector,verdict:"uncertain",reasoning:`Hide-text observation: element's computed CSS foreground color could not be parsed (got ${JSON.stringify(x.foreground)}). Cannot compute contrast against rendered background without an intended foreground color. Manual verification required.`,resolvedAt:new Date().toISOString(),costUsd:0,wcagCriterion:x.wcagCriterion,inputHash:L};await je(V);continue}const N=Ed(U,F,T);if(!N){c++;const V={pageUrl:x.pageUrl,ruleId:x.ruleId,selector:x.selector,verdict:"uncertain",reasoning:`Hide-text observation: too few text pixels detected (under ${zr} after diffing hidden vs visible captures). Element may be canvas-rendered, SVG with non-standard rendering, or carry styles that prevent color: transparent from hiding the text. Manual verification required.`,resolvedAt:new Date().toISOString(),costUsd:0,wcagCriterion:x.wcagCriterion,inputHash:L};await je(V);continue}const O=yd(x.fontSize,x.fontWeight),D=O?bd:md,G=V=>`rgb(${V.r},${V.g},${V.b})`;let M=N.worstRatio>=D,j="";if(vd(N.worstRatio,T,$.resolvedBg,D)){const V=Ln(T,$.resolvedBg);M=!0,j=` Cross-check (rc.356): computed CSS contrast ${G(T)} on resolved background ${G($.resolvedBg)} = ${V.toFixed(2)}:1, which PASSES ${D}:1 — the sub-threshold pixel reading is an anti-aliasing/sampling artifact, not a real failure. Suppressed.`}const R={pageUrl:x.pageUrl,ruleId:x.ruleId,selector:x.selector,verdict:M?"pass":"fail",reasoning:`Hide-text observation: ${N.textPixelCount} text-core pixels detected by diffing hidden-vs-visible viewport screenshots. Worst per-pixel contrast: ${N.worstRatio.toFixed(2)}:1 against AA threshold ${D}:1 (${O?"large text":"normal text"}). At worst pixel: rendered foreground ${G(N.worstForeground)} on rendered background ${G(N.worstBackground)}. ${M&&j?"Passes (computed cross-check).":M?"Passes threshold.":"Falls below threshold — at least one text pixel reads against a background that fails contrast."}`+j,resolvedAt:new Date().toISOString(),costUsd:0,wcagCriterion:x.wcagCriterion,inputHash:L};await je(R),i++}}finally{try{await chrome.debugger.sendCommand({tabId:e},"Runtime.evaluate",{expression:`(() => { const el = document.getElementById(${JSON.stringify(Nn)}); if (el) el.remove(); return true; })()`})}catch{}try{await chrome.debugger.detach({tabId:e})}catch{}}return nt.info(`pixel-contrast sampler: resolved ${i}, skipped ${c}/${n.size}`),{resolved:i,skipped:c}}const ut=te("ai-text");function ht(e){if(e!=null&&e.aborted){const t=new Error("aborted");throw t.name="AbortError",t}}function pt(e){return e instanceof Error&&(e.name==="AbortError"||e.name==="ExternalAbortError"||e.message==="aborted")}async function Od(e,t,a,n={}){var u;if(!a.enabled||!a.enabledChecks.headings)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=he(a);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${o.reason}`]};const r=o.client,i=Le(a.costCapUsd),c=[],d=[],l=Math.max(1,a.maxCandidatesPerCheck??10),h=e.slice(0,l),m=h.length;for(let g=0;g<h.length;g++){const p=h[g];if(!i.canCharge())break;try{ht(n.signal);const f=await r.judgeHeading({headingText:p.text,sectionContent:p.sectionContent,signal:n.signal});i.recordCharge(f.costUsd),f.verdict==="fail"&&f.confidence>=.6&&c.push(await ft("ai-heading-not-descriptive","2.4.6","AA","moderate","AI judged this heading does not describe its section content.",p.selector,p.outerHTML,p.text,f,t))}catch(f){if(pt(f)){d.push(`canceled after ${g}/${m} candidates`);break}const y=f instanceof Error?f.message:String(f);d.push(`${p.selector}: ${y}`),ut.warn("heading judgment failed",f)}(u=n.onProgress)==null||u.call(n,g+1,m)}return{findings:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:d}}async function Md(e,t,a,n={}){var u;if(!a.enabled||!a.enabledChecks.sensory)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=he(a);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${o.reason}`]};const r=o.client,i=Le(a.costCapUsd),c=[],d=[],l=Math.max(1,a.maxCandidatesPerCheck??10),h=e.slice(0,l),m=h.length;for(let g=0;g<h.length;g++){const p=h[g];if(!i.canCharge())break;try{ht(n.signal);const f=await r.judgeSensoryLanguage({instructionText:p.text,signal:n.signal});i.recordCharge(f.costUsd),f.verdict==="fail"&&f.confidence>=.6&&c.push(await ft("ai-sensory-instruction","1.3.3","A","serious","AI flagged this instruction as relying on sensory characteristics (shape, color, location) without a programmatic identifier.",p.selector,p.outerHTML,p.text.slice(0,100),f,t))}catch(f){if(pt(f)){d.push(`canceled after ${g}/${m} candidates`);break}const y=f instanceof Error?f.message:String(f);d.push(`${p.selector}: ${y}`),ut.warn("sensory judgment failed",f)}(u=n.onProgress)==null||u.call(n,g+1,m)}return{findings:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:d}}async function Dd(e,t,a,n={}){var u;if(!a.enabled||!a.enabledChecks.aria)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=he(a);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${o.reason}`]};const r=o.client,i=Le(a.costCapUsd),c=[],d=[],l=Math.max(1,a.maxCandidatesPerCheck??10),h=e.slice(0,l),m=h.length;for(let g=0;g<h.length;g++){const p=h[g];if(!i.canCharge())break;try{ht(n.signal);const f=await r.judgeAriaSemantics({elementHtml:p.outerHTML,computedRole:p.role,surroundingHtml:p.surroundingHtml,signal:n.signal});i.recordCharge(f.costUsd),f.verdict==="fail"&&f.confidence>=.65&&c.push(await ft("ai-aria-misuse","4.1.2","A","serious",`AI judged role="${p.role}" is being used inappropriately on this element.`,p.selector,p.outerHTML,p.role,f,t))}catch(f){if(pt(f)){d.push(`canceled after ${g}/${m} candidates`);break}const y=f instanceof Error?f.message:String(f);d.push(`${p.selector}: ${y}`),ut.warn("aria judgment failed",f)}(u=n.onProgress)==null||u.call(n,g+1,m)}return{findings:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:d}}async function Ld(e,t,a,n={}){var u;if(!a.enabled||!a.enabledChecks.genericLinkText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=he(a);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${o.reason}`]};const r=o.client,i=Le(a.costCapUsd),c=[],d=[],l=Math.max(1,a.maxCandidatesPerCheck??10),h=e.slice(0,l),m=h.length;for(let g=0;g<h.length;g++){const p=h[g];if(!i.canCharge())break;try{ht(n.signal);const f=await r.judgeGenericLinkText({linkText:p.linkText,surroundingText:p.surroundingText,signal:n.signal});i.recordCharge(f.costUsd),f.verdict==="fail"&&f.confidence>=.6&&c.push(await ft("ai-generic-link-text","2.4.4","A","serious","AI judged this link's purpose is not clear from its text or surrounding context.",p.selector,p.outerHTML,p.linkText,f,t))}catch(f){if(pt(f)){d.push(`canceled after ${g}/${m} candidates`);break}const y=f instanceof Error?f.message:String(f);d.push(`${p.selector}: ${y}`),ut.warn("generic-link judgment failed",f)}(u=n.onProgress)==null||u.call(n,g+1,m)}return{findings:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:d}}async function Nd(e,t,a,n={}){var u;if(!a.enabled||!a.enabledChecks.wallOfText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=he(a);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${o.reason}`]};const r=o.client,i=Le(a.costCapUsd),c=[],d=[],l=Math.max(1,a.maxCandidatesPerCheck??10),h=e.slice(0,l),m=h.length;for(let g=0;g<h.length;g++){const p=h[g];if(!i.canCharge())break;try{ht(n.signal);const f=await r.judgeWallOfText({blockText:p.blockText,structuralChildrenCount:p.structuralChildrenCount,signal:n.signal});i.recordCharge(f.costUsd),f.verdict==="fail"&&f.confidence>=.6&&c.push(await ft("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).",p.selector,p.outerHTML,p.blockText.slice(0,80),f,t))}catch(f){if(pt(f)){d.push(`canceled after ${g}/${m} candidates`);break}const y=f instanceof Error?f.message:String(f);d.push(`${p.selector}: ${y}`),ut.warn("wall-of-text judgment failed",f)}(u=n.onProgress)==null||u.call(n,g+1,m)}return{findings:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:d}}async function Pd(e,t,a,n={}){var u;if(!a.enabled||!a.enabledChecks.languageMismatch)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=he(a);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${o.reason}`]};const r=o.client,i=Le(a.costCapUsd),c=[],d=[],l=Math.max(1,a.maxCandidatesPerCheck??10),h=e.slice(0,l),m=h.length;for(let g=0;g<h.length;g++){const p=h[g];if(!i.canCharge())break;try{ht(n.signal);const f=await r.judgeLanguageMismatch({declaredLang:p.declaredLang,bodyTextSample:p.bodyTextSample,signal:n.signal});i.recordCharge(f.costUsd),f.verdict==="fail"&&f.confidence>=.7&&c.push(await ft("ai-language-mismatch","3.1.1","A","serious",`AI judged the page's content does not match the declared lang="${p.declaredLang||"(none)"}".`,p.selector,p.outerHTML,p.bodyTextSample.slice(0,80),f,t))}catch(f){if(pt(f)){d.push(`canceled after ${g}/${m} candidates`);break}const y=f instanceof Error?f.message:String(f);d.push(`${p.selector}: ${y}`),ut.warn("language-mismatch judgment failed",f)}(u=n.onProgress)==null||u.call(n,g+1,m)}return{findings:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:d}}async function ft(e,t,a,n,o,r,i,c,d,l){const h={selector:r,outerHTML:i.slice(0,500),failureSummary:`AI assessment: ${d.reasoning}`,tagName:"AI",role:null,accessibleName:null,textSnippet:c.slice(0,100)||null,attrId:null,attrTestid:null},m=await pn({ruleId:e,componentId:l.componentId,currentState:l.currentState,target:h});return{ruleId:e,wcagCriterion:t,wcagLevel:a,impact:n,description:o,helpUrl:"https://www.w3.org/WAI/WCAG21/Understanding/",target:h,componentId:l.componentId,currentState:l.currentState,axeVersion:l.axeVersion,detectedAt:new Date().toISOString(),matchKey:m,ai:{reasoning:d.reasoning,confidence:d.confidence,model:d.model,costUsd:d.costUsd}}}const Kr=te("ai-vision");function Yr(e){if(e!=null&&e.aborted){const t=new Error("aborted");throw t.name="AbortError",t}}function Jr(e){return e instanceof Error&&(e.name==="AbortError"||e.name==="ExternalAbortError"||e.message==="aborted")}async function Fd(e,t,a,n={}){var u;if(!a.enabled||!a.enabledChecks.imageOfText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=he(a);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${o.reason}`]};const r=o.client,i=Le(a.costCapUsd),c=Math.max(1,a.imageOfTextMaxImages??5),d=[...e].sort((g,p)=>p.pixelArea-g.pixelArea).slice(0,c),l=d.length,h=[],m=[];for(let g=0;g<d.length;g++){const p=d[g];if(!i.canCharge())break;try{Yr(n.signal);const f=await r.judgeImageOfText({imageUrl:p.imageUrl,accessibleName:p.accessibleName,signal:n.signal});i.recordCharge(f.costUsd),f.verdict==="fail"&&f.confidence>=.65&&h.push(await Xr("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.",p.selector,p.outerHTML,p.accessibleName??"",f,t))}catch(f){if(Jr(f)){m.push(`canceled after ${g}/${l} candidates`);break}const y=f instanceof Error?f.message:String(f);m.push(`${p.selector}: ${y}`),Kr.warn("image-of-text judgment failed",f)}(u=n.onProgress)==null||u.call(n,g+1,l)}return{findings:h,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:m}}async function _d(e,t,a,n={}){var u;if(!a.enabled||!a.enabledChecks.colorOnlyMeaning)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=he(a);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[o.reason]};const r=o.client,i=Le(a.costCapUsd),c=[],d=[],l=Math.max(1,a.maxCandidatesPerCheck??10),h=e.slice(0,l),m=h.length;for(let g=0;g<h.length;g++){const p=h[g];if(!i.canCharge())break;try{Yr(n.signal);const f=await r.judgeColorOnlyMeaning({elementHtml:p.elementHtml,contextDescription:p.contextDescription,signal:n.signal});i.recordCharge(f.costUsd),f.verdict==="fail"&&f.confidence>=.65&&c.push(await Xr("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).",p.selector,p.outerHTML,p.contextDescription,f,t))}catch(f){if(Jr(f)){d.push(`canceled after ${g}/${m} candidates`);break}const y=f instanceof Error?f.message:String(f);d.push(y),Kr.warn("color-only judgment failed",f)}(u=n.onProgress)==null||u.call(n,g+1,m)}return{findings:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:d}}async function Xr(e,t,a,n,o,r,i,c,d,l){const h={selector:r,outerHTML:i.slice(0,500),failureSummary:`AI assessment: ${d.reasoning}`,tagName:"AI",role:null,accessibleName:null,textSnippet:c.slice(0,100)||null,attrId:null,attrTestid:null},m=await pn({ruleId:e,componentId:l.componentId,currentState:l.currentState,target:h});return{ruleId:e,wcagCriterion:t,wcagLevel:a,impact:n,description:o,helpUrl:"https://www.w3.org/WAI/WCAG21/Understanding/",target:h,componentId:l.componentId,currentState:l.currentState,axeVersion:l.axeVersion,detectedAt:new Date().toISOString(),matchKey:m,ai:{reasoning:d.reasoning,confidence:d.confidence,model:d.model,costUsd:d.costUsd}}}const ln=te("ensure-content-script"),Ud=1500,Wd=300;async function Pn(e,t){if(await xo(e,t))return{ok:!0};const a=chrome.runtime.getManifest(),n=[];for(const o of a.content_scripts??[])Array.isArray(o.js)&&n.push(...o.js);if(n.length===0)return ln.error("manifest declares no content_scripts"),{ok:!1,reason:"inject-failed",detail:"no content_scripts declared in manifest"};try{const o=t!==void 0?{tabId:e,frameIds:[t]}:{tabId:e,allFrames:!0};await chrome.scripting.executeScript({target:o,files:n})}catch(o){return Gd(o)}return await Vd(Wd),await xo(e,t)?(ln.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 xo(e,t){try{const a=t!==void 0?{frameId:t}:void 0,n=await Promise.race([chrome.tabs.sendMessage(e,{type:"PING_REQUEST"},a),new Promise((o,r)=>setTimeout(()=>r(new Error("ping timeout")),Ud))]);return(n==null?void 0:n.type)==="PING_RESPONSE"}catch{return!1}}function Gd(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}:(ln.warn("content-script injection failed",e),{ok:!1,reason:"inject-failed",detail:t})}function Vd(e){return new Promise(t=>setTimeout(t,e))}function dn(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 Ga=te("forensic-anchor-client"),jd="https://api.wcagcheckr.com",Hd="wcagcheckr",Bd=15e3;function qd(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 Qr(e,t){return Zr(e.hash,e.pageUrl,e.capturedAt,t)}async function Zr(e,t,a,n){const o={auditHash:e,pageUrl:t,capturedAt:a,...n?{licenseId:n}:{}},r=`${jd}/v1/products/${Hd}/forensic/anchor`;try{const i=await fetch(r,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(o),signal:AbortSignal.timeout(Bd)});if(!i.ok)return Ga.warn(`anchor HTTP ${i.status}; entry stays local-only`),null;const c=await i.json();return qd(c)?c:(Ga.warn("anchor returned malformed receipt; entry stays local-only",c),null)}catch(i){return Ga.warn("anchor request failed; entry stays local-only",i),null}}async function zd(e){const t=Jt(e),a=new TextEncoder().encode(t),n=await crypto.subtle.digest("SHA-256",a);return Array.from(new Uint8Array(n)).map(o=>o.toString(16).padStart(2,"0")).join("")}function Kd(e){try{return new URL(e).hostname}catch{return e}}const Yd="wcag-site-claims",Jd=1,oa="claims";let Va=null;function ei(){return Va||(Va=gn(Yd,Jd,{upgrade(e){e.objectStoreNames.contains(oa)||e.createObjectStore(oa,{keyPath:"domain"})}})),Va}async function Xd(e){await(await ei()).put(oa,e)}async function wp(e){return await(await ei()).get(oa,e)??null}const Io="4.11.4";async function Qd(e){try{return(await chrome.tabs.get(e)).url??""}catch{return""}}const _=te("flows");async function gt(){const e=await ar();if(!e)throw new Error("no audit target tab found");return e}async function Fn(){var a,n,o,r,i;const e=await He("stateMatrix",za);return((a=e.pseudoStates)==null?void 0:a.length)>0&&((n=e.themes)==null?void 0:n.length)>0&&((o=e.directions)==null?void 0:o.length)>0&&((r=e.breakpoints)==null?void 0:r.length)>0&&((i=e.breakpointPresets)==null?void 0:i.length)>0?e:(_.warn("stored stateMatrix is invalid; using defaults"),za)}async function Zd(e,t,a){const n=t.themes.includes("light"),o=t.themes.includes("dark");if(!n||!o)return t;let r=null;try{r=await ke(e,{type:"THEME_AWARENESS_REQUEST"},a)}catch{return t}return!r||r.hasUnreadableSheets?t:r.hasDarkModeCss&&!r.hasLightModeCss?(_.info("theme-axis prune: site is dark-only, skipping light pass"),{...t,themes:t.themes.filter(i=>i!=="light")}):r.hasLightModeCss&&!r.hasDarkModeCss?(_.info("theme-axis prune: site is light-only, skipping dark pass"),{...t,themes:t.themes.filter(i=>i!=="dark")}):t}const eu=200,ja=3,So=8;async function tu(e,t){const a=[];let n=null,o=null;for(let i=0;i<So&&!t();i++){const c=await e();if(c.error&&(o=c.error),!c.result)break;if(n=c.result,a.push(c.violations),i===1&&a[0].length===0&&a[1].length===0)return{violations:[],lastResult:n,sampleCount:2,stabilized:!0,lastError:o};if(a.length>=ja){const d=a.slice(-ja);if(au(d))return{violations:d[d.length-1],lastResult:n,sampleCount:a.length,stabilized:!0,lastError:o}}i<So-1&&await new Promise(d=>setTimeout(d,eu))}const r=a.slice(-ja);return{violations:nu(r),lastResult:n,sampleCount:a.length,stabilized:!1,lastError:o}}function au(e){if(e.length<2)return!0;const t=Co(e[0]);for(let a=1;a<e.length;a++){const n=Co(e[a]);if(n.size!==t.size)return!1;for(const o of t)if(!n.has(o))return!1}return!0}function Co(e){return new Set(e.map(t=>`${t.ruleId}::${t.target.selector}::${t.matchKey}`))}function nu(e){const t=new Map,a="Audit detected this finding intermittently across multiple samples — likely an animation or transitional state was in progress. Verify manually.";for(const n of e)for(const o of n){const r=`${o.ruleId}::${o.target.selector}::${o.matchKey}`;t.has(r)||t.set(r,{...o,needsReview:!0,flakyAcrossRuns:!0,target:{...o.target,failureSummary:o.target.failureSummary?`${o.target.failureSummary} ${a}`:a}})}return Array.from(t.values())}async function $o(e,t,a,n,o){await Promise.all([(async()=>{var r;try{const i=await ke(e,{type:"READING_ORDER_REQUEST",selector:t},a);n[0]&&(i.issues.length>0?(n[0].readingOrderIssues=i.issues,n[0].positionAnalysisCapturedAt=o):(r=n[0].axeRulesEvaluated)==null||r.passed.push({ruleId:"wcc-reading-order-clean",wcagCriterion:"1.3.2"}))}catch(i){_.debug("reading-order analysis skipped",i)}})(),(async()=>{var r;try{const i=await ke(e,{type:"TAB_ORDER_ANALYZE_REQUEST"},a);n[0]&&(i.issues.length>0?(n[0].tabOrderIssues=i.issues,n[0].positionAnalysisCapturedAt=o):(r=n[0].axeRulesEvaluated)==null||r.passed.push({ruleId:"wcc-tab-order-clean",wcagCriterion:"2.4.3"}))}catch(i){_.debug("tab-order analysis skipped",i)}})(),(async()=>{var r;try{const i=await ke(e,{type:"TYPOGRAPHY_ANALYZE_REQUEST",selector:t},a);i.issues.length>0&&n[0]?n[0].typographyIssues=i.issues:n[0]&&((r=n[0].axeRulesEvaluated)==null||r.passed.push({ruleId:"wcc-typography-clean",wcagCriterion:"1.4.12"}))}catch(i){_.debug("typography analysis skipped",i)}})(),(async()=>{try{const r=await ke(e,{type:"CUSTOM_PROPERTY_REQUEST"},a);r.undefined.length>0&&n[0]&&(n[0].undefinedCustomProperties=r.undefined)}catch(r){_.debug("custom-property analysis skipped",r)}})()])}async function ct(e,t,a,n,o={}){var U,F,T,N,O;const r=o.asSubroutine===!0;if(await Ue())return r||H({type:"AUDIT_FAILED_EVENT",error:{code:Et,message:ze,recoverable:!1}}),{ok:!1,results:[],error:ze};const i=o.tabIdOverride??await gt(),c=await Pn(i,a);if(!c.ok)return r||H({type:"AUDIT_FAILED_EVENT",error:{code:c.reason==="restricted-url"?"RESTRICTED_URL":"CONTENT_SCRIPT_UNREACHABLE",message:dn(c),recoverable:c.reason!=="restricted-url"}}),{ok:!1,results:[],error:dn(c)};try{const[D]=await chrome.scripting.executeScript({target:{tabId:i,frameIds:a!==void 0?[a]:void 0},func:()=>document.contentType}),G=String((D==null?void 0:D.result)??"").toLowerCase();if(!(G===""||G==="text/html"||G==="application/xhtml+xml")){const j=`This page is not an HTML document (Content-Type: ${G||"unknown"}). WCAG HTML checks don't apply to XML sitemaps, RSS/Atom feeds, JSON, or plain text — skipped.`;return r||H({type:"AUDIT_FAILED_EVENT",error:{code:"NOT_HTML_DOCUMENT",message:j,recoverable:!1}}),{ok:!1,results:[],error:j,skipped:!0}}}catch(D){_.debug("content-type probe failed (non-fatal); proceeding with audit",D)}try{await chrome.scripting.executeScript({target:{tabId:i,frameIds:a!==void 0?[a]:void 0},world:"MAIN",func:()=>{try{let D=document.getElementById("__wcagcheckr_audit_freeze");D||(D=document.createElement("style"),D.id="__wcagcheckr_audit_freeze",D.textContent="*, *::before, *::after { animation-duration: 0s !important; animation-delay: 0s !important; transition-duration: 0s !important; transition-delay: 0s !important; } html, body { scroll-behavior: auto !important; }",document.documentElement.appendChild(D)),window.scrollTo(0,0);const G=document.activeElement;G&&G instanceof HTMLElement&&G!==document.body&&G.blur()}catch{}}}),await chrome.scripting.executeScript({target:{tabId:i,frameIds:a!==void 0?[a]:void 0},world:"MAIN",func:async()=>{try{await document.fonts.ready}catch{}}})}catch(D){_.debug("audit-start state stabilization failed (non-fatal)",D)}const d=n??await Fn(),l=await Zd(i,d,a),h=il(l),m=await Wi(`${await Qd(i)}|${e}|${a??0}`),u=await Gi(l),g=await He("axeDisabledRules",[]),p=await Vi(g);let f=null;try{f=(await ke(i,{type:"SCOPE_FINGERPRINT_REQUEST",selector:e},a)).fingerprint}catch(D){_.debug("scope fingerprint failed; skipping cache fast-path",D)}if(f){const D=await ji(m);if(D&&D.fingerprint===f&&D.axeVersion===Io&&D.matrixConfigHash===u&&D.disabledRulesHash===p)return _.info(`audit-cache HIT for ${D.componentId} — skipping matrix iteration`),r||(H({type:"AUDIT_COMPLETE_EVENT",componentId:D.componentId,results:D.results,delta:D.delta}),H({type:"SCORECARD_UPDATED_EVENT"})),{ok:!0,results:D.results}}r||await Je({sessionId:Mt(),mode:"single-element",scope:e,startedAt:new Date().toISOString(),lastProgressAt:new Date().toISOString(),state:"running"}),await It(i,{type:"LIVE_REGIONS_ARM_REQUEST"},a).catch(()=>{}),await It(i,{type:"FOCUS_TRACE_ARM_REQUEST"},a).catch(()=>{}),await ke(i,{type:"WARMUP_REQUEST"},a).catch(()=>{});const y=[],b=[];let A="",w,s=!1,v=null,S=null,k=null,x=null,L=!1,$=null;try{for(let D=0;D<h.length;D++){if(t.isCanceled()){_.info("single audit canceled by user");break}const G=h[D];try{const M=await sn(i,G,e,a);if(!M.success){w=(U=M.error)==null?void 0:U.message,_.warn(`state ${D+1}/${h.length} drive failed`,w);continue}G.breakpoint.id!==v&&(await ke(i,{type:"WARMUP_REQUEST"},a).catch(()=>{}),v=G.breakpoint.id);const j=D===0?void 0:[...Fr],R=await tu(async()=>{var Q;const K=await ke(i,{type:"AUDIT_REQUEST",selector:e,currentState:G,rulesToRun:j},a);return K.success&&K.result?{result:K.result,violations:K.result.violations,error:null}:{result:null,violations:[],error:((Q=K.error)==null?void 0:Q.message)??null}},()=>t.isCanceled());R.lastError&&(w=R.lastError,_.warn(`state ${D+1}/${h.length} sampling: ${R.lastError}`));const V=R.lastResult;if(!R.stabilized&&R.sampleCount>0&&_.info(`state ${D+1}/${h.length} did not stabilize after ${R.sampleCount} samples — ${R.violations.length} finding(s) routed to needs-review as flickering`),V){s=!0;const K=R.violations;_.info(`[adaptive-sample] state ${D+1}/${h.length} ${G.pseudoState}·${G.theme}·${G.direction}·${G.breakpoint.id}: ${R.sampleCount} sample(s), ${R.stabilized?"stabilized":"flickering"}, ${K.length} finding(s)`);const Q=await Sl(i,{quality:75})??void 0;let we,Ae;try{we=(await ke(i,{type:"LIVE_REGIONS_DRAIN_REQUEST"},a)).announcements}catch{}try{Ae=(await ke(i,{type:"FOCUS_TRACE_DRAIN_REQUEST"},a)).focusEvents}catch{}if(b.push({...V,violations:K,frameId:a,announcements:we,focusEvents:Ae,screenshotDataUrl:Q}),y.push(...K),A=V.componentId,D===0&&S===null)try{const pe=await He("aiConfig",{}),Se=ve(pe);if(Se.enabled&&Se.apiKey&&A){S=ca(i,e,a,Se,b,A,t).catch(W=>{_.warn("ai augmentation (parallel) failed",W)});const C=((F=b[0])==null?void 0:F.pageUrl)??((T=b[0])==null?void 0:T.scope)??"";k=hd({tabId:i,componentId:A,pageUrl:C,results:b,verifyFixesOnly:o.verifyFixesOnly}).catch(W=>(_.warn("1.4.11 walkthrough (parallel) failed",W),{criterionId:"1.4.11",ran:!0,ok:!1,error:String(W)}))}}catch(pe){_.debug("ai augmentation (parallel) gate-read failed",pe)}!L&&G.pseudoState==="default"&&G.theme==="light"&&G.direction==="ltr"&&G.breakpoint.id==="desktop"&&b[0]&&(L=!0,$=$o(i,e,a,b,G).catch(Se=>{_.debug("lightweight deep analyzers (parallel) failed",Se)}))}}catch(M){w=M instanceof Error?M.message:String(M),_.warn(`state ${D+1}/${h.length} threw`,M)}H({type:"AUDIT_PROGRESS_EVENT",current:D+1,total:h.length,currentState:G,componentId:A||void 0}),r||await Ot({})}if(s&&A){const D={pseudoState:"default",ariaVariation:null,theme:"light",direction:"ltr",breakpoint:{id:"desktop",label:"Desktop",width:1280,height:800,deviceScaleFactor:1,mobile:!1}};if(L){if($)try{await $}catch{}}else{try{await sn(i,D,e,a),await new Promise(R=>setTimeout(R,200))}catch(R){_.debug("reference-state drive failed; analyzers will use last matrix state",R)}await $o(i,e,a,b,D).catch(R=>{_.debug("lightweight deep analyzers (post-matrix fallback) failed",R)})}try{const R=await He("aiConfig",{}),V=ve(R);if(V.enabled&&V.apiKey&&A&&b[0]){const K=b[0].pageUrl??b[0].scope??"";x=ud({tabId:i,componentId:A,pageUrl:K,results:b,verifyFixesOnly:o.verifyFixesOnly}).catch(Q=>(_.warn("1.3.2 walkthrough (parallel) failed",Q),{criterionId:"1.3.2",ran:!0,ok:!1,error:String(Q)}))}}catch(R){_.debug("1.3.2 walkthrough (parallel) gate-read failed",R)}try{if(S)await S;else{const R=await He("aiConfig",{}),V=ve(R);V.enabled&&V.apiKey&&await ca(i,e,a,V,b,A,t)}}catch(R){_.warn("ai augmentation skipped",R)}try{const R=await He("aiConfig",{}),V=ve(R);if(V.enabled&&V.apiKey&&A&&b[0]){await Ht(i,e,a).catch(pe=>_.warn("pre-walkthrough resetState failed",pe));const K=b[0].pageUrl??b[0].scope??"",Q=new AbortController,we=setInterval(()=>{t.isCanceled()&&Q.abort()},250),Ae=await dd({tabId:i,componentId:A,pageUrl:K,results:b,signal:Q.signal,verifyFixesOnly:o.verifyFixesOnly}).finally(()=>clearInterval(we));_.info("keyboard walkthroughs complete",Ae)}}catch(R){_.warn("keyboard walkthroughs failed",R)}try{const R=[];k&&R.push(await k),x&&R.push(await x),R.length>0&&_.info("parallel AI walkthroughs joined",R)}catch(R){_.warn("parallel AI walkthrough join failed",R)}try{if(A&&b[0]){const R=b[0].pageUrl??b[0].scope??"",V=new AbortController,K=setInterval(()=>{t.isCanceled()&&V.abort()},250),Q=await pd({tabId:i,componentId:A,pageUrl:R,results:b,signal:V.signal,verifyFixesOnly:o.verifyFixesOnly}).finally(()=>clearInterval(K));_.info("2.5.3 label-in-name walkthrough complete",Q)}}catch(R){_.warn("2.5.3 walkthrough failed",R)}try{if(A&&b[0]){await Ht(i,e,a).catch(we=>_.warn("pre-sampler resetState failed",we));const R=new AbortController,V=setInterval(()=>{t.isCanceled()&&R.abort()},250),{resolved:K,skipped:Q}=await Rd(i,b,R.signal).finally(()=>clearInterval(V));_.info(`pixel contrast sampler: resolved=${K} skipped=${Q}`)}}catch(R){_.warn("pixel contrast sampler failed",R)}const G=b.flatMap(R=>R.announcements??[]),M=b.flatMap(R=>R.focusEvents??[]),j=await On(A,y,{announcements:G,focusEvents:M},d);try{const R=await In(b.map(ee=>ee.componentId)),V=await fr(b.map(ee=>ee.componentId)),K=new Set;for(const ee of new Set(b.map(I=>I.componentId))){const I=await ga(ee);for(const B of I)K.add(B)}const Q=[...new Set(b.map(ee=>ee.componentId))],we=await Ir(Q),Ae=((N=b[0])==null?void 0:N.pageUrl)??((O=b[0])==null?void 0:O.scope)??"",pe=Ae?await ba(Ae):[],Se=new Set(b.map(ee=>ee.pageUrl??ee.scope??"").filter(Boolean)).size>1?"site-crawl":"single-page",C=Ke(y,{runs:[],workflows:Me},b,K,R,V,we,Se,pe),W={...C.score,letter:C.combined},z=b.reduce((ee,I)=>ee+I.durationMs,0),Z=await ue(),ie=de(Z,"forensicLog")?await Er(b,W,z):null;if(ie){const ee=await ue();if(de(ee,"forensicAnchoring"))try{const I=await da(),B=await Qr(ie,I??void 0);B&&await Rr(ie.componentId,ie.capturedAt,B)}catch(I){_.warn("forensic anchoring failed (entry stays local-only)",I)}}}catch(R){_.warn("forensic log recording failed",R)}if(f)try{await Hi({lookupKey:m,fingerprint:f,axeVersion:Io,matrixConfigHash:u,disabledRulesHash:p,results:b,delta:j,componentId:A,cachedAt:Date.now()})}catch(R){_.warn("audit-cache write failed",R)}try{await chrome.scripting.executeScript({target:{tabId:i,frameIds:a!==void 0?[a]:void 0},world:"MAIN",func:()=>{const R=document.getElementById("__wcagcheckr_audit_freeze");R&&R.remove()}})}catch(R){_.debug("audit-end state-freeze cleanup failed",R)}r||H({type:"AUDIT_COMPLETE_EVENT",componentId:A,results:b,delta:j})}else r||H({type:"AUDIT_FAILED_EVENT",error:{code:"AXE_FAILED",message:w?`All audits failed. Last error: ${w}`:"No state produced results. Selector may not exist in the audit target frame.",recoverable:!0}});r||await rt()}catch(D){_.error("single audit failed",D),r||H({type:"AUDIT_FAILED_EVENT",componentId:A||void 0,error:jn(D)?D.payload:{code:"UNKNOWN",message:String(D),recoverable:!1}}),w=jn(D)?D.payload.message:String(D)}finally{await It(i,{type:"LIVE_REGIONS_DISARM_REQUEST"},a).catch(()=>{}),await It(i,{type:"FOCUS_TRACE_DISARM_REQUEST"},a).catch(()=>{}),await Ht(i,e,a)}return{ok:s,results:b,error:s?void 0:w}}async function ti(e){var t;try{const n=(await chrome.scripting.executeScript({target:{tabId:e,allFrames:!0},world:"MAIN",func:()=>{var r;const o=window;return{detected:typeof o.__STORYBOOK_PREVIEW__=="object",version:(r=o.__STORYBOOK_PREVIEW__)==null?void 0:r.version}}})).find(o=>{var r;return(r=o.result)==null?void 0:r.detected});if(n)return{detected:!0,version:(t=n.result)==null?void 0:t.version,frameId:n.frameId}}catch(a){_.warn("storybook detection scripting failed",a)}return{detected:!1}}async function ou(e){if(await Ue()){H({type:"AUDIT_FAILED_EVENT",error:{code:Et,message:ze,recoverable:!1}});return}const t=await gt(),a=await oi(t);if(await chrome.storage.local.set({"storybook:lastDetected":{detected:a.kind==="storybook"&&a.detected,version:a.version,detectedAt:new Date().toISOString()},"playground:lastDetected":{kind:a.kind,detected:a.detected,version:a.version,detectedAt:new Date().toISOString()}}),!a.detected){H({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(a.kind==="bit"){H({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 n=a.frameId??0,o=a.kind==="storybook"?await su(t,n):await lu(t,n);if(!o.length){_.warn(`${a.kind} detected but no stories returned`),H({type:"AUDIT_FAILED_EVENT",error:{code:"STORYBOOK_NOT_DETECTED",message:`${a.kind==="ladle"?"Ladle":"Storybook"} detected but no stories were returned. Try reloading the page.`,recoverable:!0}});return}await Je({sessionId:Mt(),mode:"storybook-all",totalStories:o.length,completedStories:[],startedAt:new Date().toISOString(),lastProgressAt:new Date().toISOString(),state:"running"});for(const r of o){if(e.isCanceled()){_.info(`${a.kind} iteration canceled by user`);break}try{if(a.kind==="storybook"){await cu(t,n,r.id);const c=await It(t,{type:"STORYBOOK_GET_STORY_TARGET_REQUEST",tabId:t},n);await ct(c.selector,e,n)}else await du(t,n,r.id),await ct("#ladle-root",e,n)}catch(c){_.warn(`story ${r.id} failed; continuing with next`,c)}const i=await ka();if((i==null?void 0:i.state)==="interrupted"){_.info("storybook iteration interrupted");break}await Ot({completedStories:[...(i==null?void 0:i.completedStories)??[],r.id]})}H({type:"SCORECARD_UPDATED_EVENT"}),await rt()}async function ru(e){if(await Ue()){H({type:"AUDIT_FAILED_EVENT",error:{code:Et,message:ze,recoverable:!1}});return}const t=await ue();if(!de(t,"allFramesAudit")){H({type:"AUDIT_FAILED_EVENT",error:{code:"FEATURE_NOT_AVAILABLE",message:"All-frames audit is not available on your current plan.",recoverable:!1}});return}const a=await gt();let n=[];try{n=(await chrome.scripting.executeScript({target:{tabId:a,allFrames:!0},func:()=>({url:window.location.href})})).map(r=>{var i;return{frameId:r.frameId,url:((i=r.result)==null?void 0:i.url)??""}}).filter(r=>/^https?:/i.test(r.url))}catch(o){_.warn("multi-frame discovery failed",o)}if(n.length===0){H({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 Je({sessionId:Mt(),mode:"all-frames",totalFrames:n.length,completedFrames:[],startedAt:new Date().toISOString(),lastProgressAt:new Date().toISOString(),state:"running"});for(const o of n){if(e.isCanceled()){_.info("all-frames iteration canceled");break}try{await ct("html",e,o.frameId)}catch(i){_.warn(`frame ${o.frameId} (${o.url.slice(0,80)}) audit failed; continuing`,i)}const r=await ka();if((r==null?void 0:r.state)==="interrupted"){_.info("all-frames iteration interrupted");break}await Ot({completedFrames:[...(r==null?void 0:r.completedFrames)??[],o.frameId]})}H({type:"SCORECARD_UPDATED_EVENT"}),await rt()}async function iu(e){var o;const t=await ue();if(!de(t,"parallelScan")){H({type:"AUDIT_FAILED_EVENT",error:{code:"FEATURE_NOT_AVAILABLE",message:"Parallel scan is not available on your current plan.",recoverable:!1}});return}const a=await ar();let n;if(a!==null)try{n=(await chrome.tabs.get(a)).url}catch(r){_.warn("failed to get target tab url",r)}if(!n){H({type:"AUDIT_FAILED_EVENT",error:{code:"NETWORK",message:"Could not determine target URL for parallel scan.",recoverable:!1}});return}await Je({sessionId:Mt(),mode:"single-element",scope:"html",startedAt:new Date().toISOString(),lastProgressAt:new Date().toISOString(),state:"running"});try{const r=await Fn(),{runParallelTabAudit:i}=await hn(async()=>{const{runParallelTabAudit:m}=await import("./parallel-tab-flow-C9KmXHUE.js");return{runParallelTabAudit:m}},__vite__mapDeps([0,1,2,3,4])),c=await i({targetUrl:n,scope:"html",matrix:r,parallelism:4,cancel:e});if(_.info(`parallel scan complete: ${c.results.length} results across ${c.tabsCompleted} tabs (${c.tabsFailed} failed) in ${Math.round(c.parallelDurationMs)}ms`),c.results.length===0){H({type:"AUDIT_FAILED_EVENT",error:{code:"NETWORK",message:`All ${c.tabsFailed} worker tabs failed. Falling back to Full Page scan is recommended.`,recoverable:!0}});return}const d=((o=c.results[0])==null?void 0:o.componentId)??"parallel-scan";if(a!==null)try{const m=await He("aiConfig",{}),u=ve(m);u.enabled&&u.apiKey&&await ca(a,"html",void 0,u,c.results,d,e)}catch(m){_.warn("parallel scan: ai augmentation skipped",m)}const l=c.results.flatMap(m=>m.violations);let h;try{h=await On(d,l)}catch(m){_.warn("parallel scan: baseline compare failed",m),h={new:[],persistent:[],fixed:[],newCount:0,persistentCount:0,fixedCount:0,baselineSnapshotMeta:null,comparedAt:new Date().toISOString()}}H({type:"AUDIT_COMPLETE_EVENT",componentId:d,results:c.results,delta:h}),H({type:"SCORECARD_UPDATED_EVENT"})}catch(r){_.warn("parallel scan failed",r),H({type:"AUDIT_FAILED_EVENT",error:{code:"UNKNOWN",message:r instanceof Error?r.message:String(r),recoverable:!0}})}finally{await rt()}}async function su(e,t){var a;try{return((a=(await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",func:async()=>{var d,l;const o=window.__STORYBOOK_PREVIEW__;if(!o)return[];const r=o.storyStoreValue??o.storyStore;if(!r)return[];typeof r.cacheAllCSFFiles=="function"&&await r.cacheAllCSFFiles();const i=((d=r.extract)==null?void 0:d.call(r))??((l=r.cachedCSFFiles)==null?void 0:l.call(r));if(!i)return[];const c=[];if(i instanceof Map)for(const[h,m]of i){const u=m;c.push({id:String(h),name:u.name??String(h),kind:u.title??u.kind??""})}else if(typeof i=="object"&&i!==null)for(const[h,m]of Object.entries(i)){const u=m;c.push({id:h,name:u.name??h,kind:u.title??u.kind??""})}return c}}))[0])==null?void 0:a.result)??[]}catch(n){return _.warn("listStoriesViaMainWorld failed",n),[]}}async function cu(e,t,a){try{await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",args:[a,3e3],func:async(n,o)=>{const r=new URL(window.location.href);r.searchParams.set("id",n),r.searchParams.set("viewMode","story"),window.location.href!==r.href&&(window.history.pushState({},"",r.href),window.dispatchEvent(new PopStateEvent("popstate"))),await new Promise(i=>{const c=setTimeout(()=>i(),o),d=()=>{clearTimeout(c),i()};window.addEventListener("storyrendered",d,{once:!0})})}})}catch(n){_.warn(`navigateToStoryViaMainWorld(${a}) failed`,n)}}async function ai(e){var t;try{const n=(await chrome.scripting.executeScript({target:{tabId:e,allFrames:!0},world:"MAIN",func:()=>{const o=document.getElementById("ladle-root")!==null,r=document.title==="Ladle",i=!!document.querySelector('link[href*="ladle.css"]');return{detected:o&&(r||i),version:void 0}}})).find(o=>{var r;return(r=o.result)==null?void 0:r.detected});if(n)return{detected:!0,version:(t=n.result)==null?void 0:t.version,frameId:n.frameId}}catch(a){_.warn("ladle detection scripting failed",a)}return{detected:!1}}async function lu(e,t){var a;try{return((a=(await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",func:async()=>{try{const o=await fetch(new URL("/meta.json",window.location.href).href,{credentials:"same-origin"});if(!o.ok)return[];const i=(await o.json()).stories??{};return Object.entries(i).map(([c,d])=>({id:c,name:d.name??c,kind:(d.levels??[]).join(" / ")}))}catch{return[]}}}))[0])==null?void 0:a.result)??[]}catch(n){return _.warn("listLadleStoriesViaMainWorld failed",n),[]}}async function du(e,t,a){try{await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",args:[a,3e3],func:async(n,o)=>{const r=new URL(window.location.href);r.searchParams.set("story",n),window.location.href!==r.href&&(window.history.pushState({},"",r.href),window.dispatchEvent(new PopStateEvent("popstate"))),await new Promise(i=>setTimeout(i,Math.min(o,500)))}})}catch(n){_.warn(`navigateToLadleStoryViaMainWorld(${a}) failed`,n)}}async function ni(e){try{return(await chrome.scripting.executeScript({target:{tabId:e,allFrames:!0},world:"MAIN",func:()=>{const a=window.location.href;return/bit\.dev|bit-dev|bitsrc|\/~aspect|\/composition/i.test(a)}})).some(a=>a.result===!0)}catch{return!1}}async function oi(e){const t=await ti(e);if(t.detected)return{kind:"storybook",...t};const a=await ai(e);return a.detected?{kind:"ladle",...a}:await ni(e)?{kind:"bit",detected:!0}:{kind:null,detected:!1}}const ra=200,ri=6e4;function ia(e,t){try{return new URL(e).origin===new URL(t).origin}catch{return!1}}function ii(e,t){try{const a=new URL(e,t);return a.hash="",a.toString()}catch{return null}}function si(e){try{return/\.(xml|rss|atom|json|txt|csv|ics|rdf|xsl|xslt)$/i.test(new URL(e).pathname)}catch{return!1}}async function ci(e,t,a){await chrome.tabs.update(e,{url:t}),await new Promise((n,o)=>{const r=setTimeout(()=>{chrome.tabs.onUpdated.removeListener(i),o(new Error(`navigation timeout (${a}ms): ${t}`))},a);function i(c,d){c===e&&d.status==="complete"&&(clearTimeout(r),chrome.tabs.onUpdated.removeListener(i),n())}chrome.tabs.onUpdated.addListener(i)}),await new Promise(n=>setTimeout(n,400))}async function li(e){var t;try{return((t=(await chrome.scripting.executeScript({target:{tabId:e},func:()=>Array.from(document.querySelectorAll("a[href]")).map(n=>n.getAttribute("href")).filter(n=>typeof n=="string"&&!n.startsWith("javascript:"))}))[0])==null?void 0:t.result)??[]}catch(a){return _.debug("link discovery failed",a),[]}}async function di(e,t){var a,n;try{const r=new URL(t).pathname.toLowerCase().replace(/\/+$/,"");if(/(?:^|\/)(?:404|not-found|page-not-found|error|server-error)$/.test(r))return`URL path "${r||"/"}" matches a common error-route pattern.`}catch{}try{const o=await chrome.scripting.executeScript({target:{tabId:e},func:()=>document.title||""}),r=((n=(a=o==null?void 0:o[0])==null?void 0:a.result)==null?void 0:n.trim())??"";if(!r)return null;const i=[[/^4(?:0[0-46-9]|[1-9]\d)\b/,"title starts with a 4xx error code"],[/^5\d{2}\b/,"title starts with a 5xx error code"],[/^(?:page\s+)?not\s+found\b/i,'title starts with "Not Found"'],[/^error\s*[-:]?\s*\d{3}\b/i,'title starts with "Error" + status code'],[/^(?:oops|whoops)[\s,!:.-]/i,'title leads with "Oops" — common error-template pattern']];for(const[c,d]of i)if(c.test(r))return`${d} (title: "${r.slice(0,80)}").`;return null}catch(o){return _.debug("error-page detection failed; proceeding with audit",o),null}}const To=/Linked PDF at (\S+)\./;async function ui(e,t){if(!de(t,"pdfAudit")){_.info("PDF audit disabled for tier; skipping crawl PDF pass",{tier:t});return}const a=new Map;for(const n of e)if(!n.error)for(const o of n.results)for(const r of o.violations){if(r.ruleId!=="pdf-needs-cli-audit")continue;const i=To.exec(r.description);if(!i)continue;const c=i[1];let d;try{d=new URL(c,n.url).href}catch{continue}if(!/^https?:\/\//i.test(d))continue;let l=a.get(d);l||(l=new Set,a.set(d,l)),l.add(n)}if(a.size!==0){_.info(`PDF audit pass: ${a.size} unique PDF(s) across the crawl`);for(const[n,o]of a){let r;try{r=await Mc(n)}catch(i){_.warn(`PDF audit threw for ${n}`,i);continue}for(const i of o){const c=i.results[0];if(!c)continue;const d=c.componentId,l=c.state,h=r.findings.map(m=>Eo(m,n,d,l));c.violations=c.violations.filter(m=>{if(m.ruleId!=="pdf-needs-cli-audit")return!0;const u=To.exec(m.description);if(!u)return!0;try{return new URL(u[1],i.url).href!==n}catch{return!0}}),c.violations.push(...h),r.fatalError&&c.violations.push(Eo({ruleId:"pdf-audit-skipped",impact:"minor",wcagCriterion:"1.1.1",description:`PDF audit skipped: ${r.fatalError}`,failureSummary:"The PDF could not be audited automatically. Run `wcagcheckr-ci pdf <url>` against this PDF on the command line for a manual audit."},n,d,l))}}}}function Eo(e,t,a,n){return{ruleId:e.ruleId,wcagCriterion:e.wcagCriterion,wcagLevel:"AA",impact:e.impact,description:e.description,helpUrl:"https://www.w3.org/WAI/standards-guidelines/wcag/",target:{selector:t,outerHTML:`<a href="${t}">[PDF]</a>`,failureSummary:e.failureSummary,tagName:"PDF",role:null,accessibleName:null,textSnippet:null,attrId:null,attrTestid:null},componentId:a,currentState:n,axeVersion:"pdf-audit",detectedAt:new Date().toISOString(),matchKey:`pdf::${e.ruleId}::${t}`}}async function uu(e,t,a){if(await Ue())return H({type:"CRAWL_DISCOVERY_FAILED_EVENT",error:{code:Et,message:ze,recoverable:!1}}),[];const n=await ue();if(!de(n,"siteCrawl"))return H({type:"CRAWL_DISCOVERY_FAILED_EVENT",error:{code:"FEATURE_NOT_AVAILABLE",message:"Site crawl is not available on your current plan.",recoverable:!1}}),[];const o=await gt(),r=jo(n).siteCrawlMaxPages,i=Number.isFinite(r)?Math.min(r,ra):ra,c=Math.min(t.maxPages??25,i),d=t.includeRegex?sa(t.includeRegex):null,l=t.excludeRegex?sa(t.excludeRegex):null,h=[e],m=new Set,u=[];try{for(;h.length>0&&u.length<c;){if(a.isCanceled()){_.info("crawl discovery canceled by user");break}const g=h.shift();if(!(!g||m.has(g))&&ia(g,e)&&!(d&&!d.test(g))&&!(l&&l.test(g))){m.add(g);try{if(await ci(o,g,ri),!(await Pn(o,0)).ok||await di(o,g))continue;u.push(g),H({type:"CRAWL_DISCOVERY_PROGRESS_EVENT",found:u.length,url:g});const y=await li(o);for(const b of y){const A=ii(b,g);A&&ia(A,e)&&!si(A)&&!m.has(A)&&!h.includes(A)&&h.push(A)}}catch(p){_.debug("discovery: page navigation/link-read failed; skipping",p);continue}await Ot({})}}return H({type:"CRAWL_DISCOVERY_COMPLETE_EVENT",urls:u}),u}catch(g){return _.error("crawl discovery failed",g),H({type:"CRAWL_DISCOVERY_FAILED_EVENT",error:{code:"CRAWL_DISCOVERY_FAILED",message:String(g),recoverable:!0}}),[]}}async function hu(e,t,a){if(await Ue()){H({type:"SITE_CRAWL_FAILED_EVENT",error:{code:Et,message:ze,recoverable:!1}});return}const n=await ue();if(!de(n,"siteCrawl")){H({type:"SITE_CRAWL_FAILED_EVENT",error:{code:"FEATURE_NOT_AVAILABLE",message:"Site crawl is not available on your current plan.",recoverable:!1}});return}const o=await gt(),r=jo(n).siteCrawlMaxPages,i=Number.isFinite(r)?Math.min(r,ra):ra,c=Array.isArray(t.urls)&&t.urls.length>0,d=Math.min(c?t.urls.length:t.maxPages??25,i),l=t.includeRegex?sa(t.includeRegex):null,h=t.excludeRegex?sa(t.excludeRegex):null,m=new Date().toISOString(),u=Date.now(),g=c?[...t.urls]:[e],p=new Set,f=[];await Je({sessionId:Mt(),mode:"storybook-all",scope:e,startedAt:m,lastProgressAt:new Date().toISOString(),state:"running"});try{for(;g.length>0&&p.size<d;){if(a.isCanceled()){_.info("site crawl canceled by user");break}const v=g.shift();if(!v||p.has(v)||!ia(v,e)||l&&!l.test(v)||h&&h.test(v))continue;p.add(v);const S=p.size,k=Date.now();H({type:"SITE_CRAWL_PROGRESS_EVENT",current:S,total:Math.min(d,p.size+g.length),url:v,status:"auditing"});try{await ci(o,v,ri);const x=await Pn(o,0);if(!x.ok)throw new Error(dn(x));const L=await di(o,v);if(L){const N=`Skipped — page appears to be an error response. ${L}`;f.push({url:v,results:[],delta:null,componentId:v,durationMs:Date.now()-k,error:N}),H({type:"SITE_CRAWL_PROGRESS_EVENT",current:S,total:Math.min(d,p.size+g.length),url:v,status:"failed",error:N});continue}await ke(o,{type:"WARMUP_REQUEST"},0).catch(()=>{});const $=await ct("html",a,0,void 0,{asSubroutine:!0,tabIdOverride:o,verifyFixesOnly:t.verifyFixesOnly}),U=$.results,F=$.ok?void 0:$.error,T=Date.now()-k;if(!$.ok&&$.skipped)f.push({url:v,results:[],delta:null,componentId:v,durationMs:T,error:$.error,skipped:!0}),H({type:"SITE_CRAWL_PROGRESS_EVENT",current:S,total:Math.min(d,p.size+g.length),url:v,status:"skipped",error:$.error});else if(U.length>0){const N=await Dc(o,v).catch(()=>null),O=U.reduce((D,G)=>D+G.violations.length,0);f.push({url:v,results:U,delta:null,componentId:U[0].componentId,durationMs:T,snapshot:N??void 0}),H({type:"SITE_CRAWL_PROGRESS_EVENT",current:S,total:Math.min(d,p.size+g.length),url:v,status:"completed",violations:O})}else{const N=F??"audit returned no result";f.push({url:v,results:[],delta:null,componentId:v,durationMs:T,error:N}),H({type:"SITE_CRAWL_PROGRESS_EVENT",current:S,total:Math.min(d,p.size+g.length),url:v,status:"failed",error:N})}if(!c){const N=await li(o);for(const O of N){const D=ii(O,v);D&&ia(D,e)&&!si(D)&&!p.has(D)&&!g.includes(D)&&g.push(D)}}}catch(x){const L=Date.now()-k,$=x instanceof Error?x.message:String(x);f.push({url:v,results:[],delta:null,componentId:v,durationMs:L,error:$}),H({type:"SITE_CRAWL_PROGRESS_EVENT",current:S,total:Math.min(d,p.size+g.length),url:v,status:"failed",error:$})}await Ot({})}try{const v=await ue();await ui(f,v)}catch(v){_.warn("PDF audit pass failed",v)}const y=new Date().toISOString(),b=Date.now()-u,A=f.flatMap(v=>v.results),w=await Oe(A,[e]),s=Dr(e,f,m,y,b,w.walkthroughsByComponent,w.walkthroughAcksByComponent,w.ackedKeysByComponent,w.humanVerdictsByComponent,w.incompleteResolutionsByComponent);if(s.consistencyFindings.length>0)try{const v=await rl(s.consistencyFindings);s.consistencyFindings=v.emitted}catch(v){_.warn("consistency AI suppression failed; emitting heuristic findings",v)}try{const v=f.map(S=>({...S,results:S.results.map(({screenshotDataUrl:k,...x})=>x)}));await tr(v,{startUrl:e,finishedAt:y})}catch(v){const S=v instanceof Error?v.message:String(v);_.error("persisting siteCrawlPerUrlResults failed",v),H({type:"SITE_CRAWL_FAILED_EVENT",error:{code:"CRAWL_PERSIST_FAILED",message:`Crawl finished but persistence failed (${S}). The AI fix prompt export won't have per-page data.`,recoverable:!1}})}H({type:"SITE_CRAWL_COMPLETE_EVENT",report:s}),await hi(s),await rt()}catch(y){_.error("site crawl failed",y),H({type:"SITE_CRAWL_FAILED_EVENT",error:{code:"UNKNOWN",message:String(y),recoverable:!1}}),await rt()}}function sa(e){try{return new RegExp(e)}catch{return null}}async function pu(){const[e,t]=await Promise.all([wn(),zt()]);if(!e)return{ok:!1,reason:"No crawl report cached. Run a site crawl first."};if(!t||t.length===0)return{ok:!1,reason:"Crawl report exists but per-URL audit data was not retained. Re-run the crawl once; future regrades will work."};const a=t.flatMap(i=>i.results),n=await Oe(a,[e.startUrl]),o=new Date().toISOString(),r=Dr(e.startUrl,t,e.startedAt,o,e.totalDurationMs,n.walkthroughsByComponent,n.walkthroughAcksByComponent,n.ackedKeysByComponent,n.humanVerdictsByComponent,n.incompleteResolutionsByComponent);return await er(r),H({type:"SITE_CRAWL_COMPLETE_EVENT",report:r}),await hi(r),{ok:!0}}async function hi(e){try{const t=await ue();if(!de(t,"forensicAnchoring"))return;const a={domain:Kd(e.startUrl),grade:e.siteGrade,scope:"site",pageCount:e.pagesAudited,wcagLevel:`${Tt} ${fn}`,auditedAt:e.finishedAt,productSlug:"wcagcheckr"},n=await zd(a),o=await da(),r=await Zr(n,e.startUrl,e.finishedAt,o??void 0);await Xd({domain:a.domain,claim:a,hash:n,receipt:r}),H({type:"SITE_CLAIM_SEALED_EVENT",domain:a.domain})}catch(t){_.warn("site-claim seal/anchor failed (badge unavailable for this crawl)",t)}}const Ha=5*6e4,Ro=9e4,Oo={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…"},fu={altText:["1.1.1"],headings:["2.4.6"],sensory:["1.3.3"],aria:["4.1.2"],imageOfText:["1.4.5"],colorOnlyMeaning:["1.4.1"],genericLinkText:["2.4.4"],wallOfText:[],languageMismatch:["3.1.1","3.1.2"]};async function ca(e,t,a,n,o,r,i){const c=await ue();if(!de(c,"aiJudgment")){_.debug("aiJudgment not allowed on current tier — skipping AI augmentation");return}let d;try{d=await ke(e,{type:"AI_CANDIDATES_REQUEST",selector:t},a)}catch(T){_.debug("ai candidates fetch failed",T);return}const l=o[0];if(!l)return;const h={componentId:r,currentState:l.state,axeVersion:l.axeVersion},m={at:new Date().toISOString(),componentId:r,pageUrl:l.pageUrl??t,enabledChecks:n.enabledChecks,candidateCounts:{images:d.images.length,headings:d.headings.length,instructions:d.instructions.length,ariaElements:d.ariaElements.length,links:d.links.length,longTextBlocks:d.longTextBlocks.length,colorOnlyRegions:d.colorOnlyRegions.length,languageInfo:d.languageInfo?"present":"null"}};_.info("ai augmentation gate snapshot",JSON.stringify(m));try{await chrome.storage.local.set({aiDiagnosticLatest:m})}catch(T){_.warn("failed to persist ai diagnostic to storage.local",T)}const u=[];if(n.enabledChecks.altText&&u.push({key:"altText",run:T=>{const N=d.images.map(O=>({imageUrl:O.imageUrl,alt:O.alt,selector:O.selector,outerHTML:O.outerHTML,surroundingContext:O.surroundingContext,...h}));return Fl(N,n,T)}}),n.enabledChecks.headings&&u.push({key:"headings",run:T=>Od(d.headings,h,n,T)}),n.enabledChecks.sensory&&u.push({key:"sensory",run:T=>Md(d.instructions,h,n,T)}),n.enabledChecks.aria&&u.push({key:"aria",run:T=>Dd(d.ariaElements,h,n,T)}),n.enabledChecks.imageOfText&&u.push({key:"imageOfText",run:T=>{const N=d.images.map(O=>({imageUrl:O.imageUrl,accessibleName:O.alt||void 0,selector:O.selector,outerHTML:O.outerHTML,pixelArea:O.pixelArea}));return Fd(N,h,n,T)}}),n.enabledChecks.colorOnlyMeaning&&u.push({key:"colorOnlyMeaning",run:T=>_d(d.colorOnlyRegions,h,n,T)}),n.enabledChecks.genericLinkText&&u.push({key:"genericLinkText",run:T=>Ld(d.links,h,n,T)}),n.enabledChecks.wallOfText&&u.push({key:"wallOfText",run:T=>Nd(d.longTextBlocks,h,n,T)}),n.enabledChecks.languageMismatch){const T=d.languageInfo;u.push({key:"languageMismatch",run:N=>Pd(T?[T]:[],h,n,N)})}if(u.length===0)return;let g=0,p=0,f=!1;const y={};let b=0,A=0,w=0;const s=[];let v=!1,S=!1;function k(T,N){if(y[T]=(y[T]??0)+N.totalCostUsd,g+=N.totalCostUsd,p+=N.findings.length,f=f||N.capExceeded,b++,N.errors.length>0){w++;for(const O of N.errors)s.includes(O)||s.push(O)}else A++;_.info(`ai ${T}: ${N.findings.length} findings, $${N.totalCostUsd.toFixed(4)}, errs=${N.errors.length}`),N.capExceeded&&_.warn(`ai cost cap exceeded after $${N.totalCostUsd.toFixed(4)} during ${T}`)}const x=(async()=>{for(let T=0;T<u.length;T++){if(i!=null&&i.isCanceled()){S=!0;break}const N=u[T];H({type:"AI_AUGMENTATION_PROGRESS_EVENT",componentId:r,currentCheck:N.key,currentCheckLabel:Oo[N.key],current:T+1,total:u.length});const O=new AbortController,D=setTimeout(()=>O.abort(),Ro),G=i?setInterval(()=>{i.isCanceled()&&O.abort()},500):null;let M;try{M=await N.run({signal:O.signal,onProgress:(j,R)=>{R>0&&H({type:"AI_AUGMENTATION_PROGRESS_EVENT",componentId:r,currentCheck:N.key,currentCheckLabel:Oo[N.key],current:T+1,total:u.length,candidatesDone:j,candidatesTotal:R})}}),O.signal.aborted&&(i!=null&&i.isCanceled())?M.errors=[...M.errors,`${N.key} canceled by user`]:O.signal.aborted}catch(j){const R=j instanceof Error?j.message:String(j),V=O.signal.aborted,K=V&&(i!=null&&i.isCanceled())?`${N.key} canceled by user`:V?`${N.key} hit per-check ${Ro/1e3}s cap — analyzer aborted`:`${N.key} threw: ${R}`;M={findings:[],totalCostUsd:0,capExceeded:!1,errors:[K]}}finally{clearTimeout(D),G&&clearInterval(G)}if(M.findings.length>0&&(l.violations=[...l.violations,...M.findings]),M.errors.length===0){const j=fu[N.key];if(j&&j.length>0&&l.axeRulesEvaluated)for(const R of j)l.axeRulesEvaluated.passed.push({ruleId:`ai-${N.key}-coverage`,wcagCriterion:R})}k(N.key,M)}})(),L=new Promise(T=>{setTimeout(()=>T("timeout"),Ha)});await Promise.race([x.then(()=>"done"),L])==="timeout"&&(v=!0,_.warn(`ai augmentation hit global ${Ha/1e3}s timeout after ${b}/${u.length} checks`));let U;if(w>0||v||S){v?U=`AI augmentation timed out after ${Ha/1e3}s`:S?U="AI augmentation canceled":U=s[0]??"AI analyzer error";const T=A===0?"total":"partial";H({type:"AI_AUGMENTATION_FAILED_EVENT",componentId:r,severity:T,reason:U,checksAttempted:b,checksSucceeded:A,checksErrored:w,errorDetails:v||S?[U,...s.slice(0,4)]:s.slice(0,5)}),_.warn(`ai augmentation ${T} failure: ${w}/${b} checks errored — ${U}`)}(g>0||p>0||U)&&await Bi({at:new Date().toISOString(),pageUrl:l.pageUrl??t,componentId:r,totalCostUsd:g,byCheck:y,findingsCount:p,capExceeded:f,failureReason:U})}const vp=Object.freeze(Object.defineProperty({__proto__:null,auditCrawlPdfs:ui,detectBit:ni,detectLadleAcrossFrames:ai,detectPlayground:oi,detectStorybookAcrossFrames:ti,discoverCrawlUrls:uu,getActiveTabId:gt,getMatrixSettings:Fn,regradeSiteCrawl:pu,runAiAugmentation:ca,runAllFramesAudit:ru,runParallelScan:iu,runSingleElementAudit:ct,runSiteCrawl:hu,runStorybookAllAudit:ou},Symbol.toStringTag,{value:"Module"})),kt=te("alert-sender"),gu="wcagcheckr",mu="https://api.wcagcheckr.com",bu=`${mu}/v1/products/${gu}/alerts/send`,yu=1e4,un="alertConfig:v1",wu={emailEnabled:!1,webhookEnabled:!1,webhookUrl:"",minImpact:"serious"},Vt={minor:1,moderate:2,serious:3,critical:4};async function vu(){const t=(await chrome.storage.local.get(un))[un];return{...wu,...t??{}}}async function Ap(e){await chrome.storage.local.set({[un]:e})}async function Au(e){let t;try{t=await vu()}catch(p){return kt.warn("failed to read alert config",p),{fired:!1,reason:"config_read_failed"}}if(!t.emailEnabled&&!t.webhookEnabled)return{fired:!1,reason:"no_channel_enabled"};const a=await ue(),n=de(a,"emailAlerts"),o=de(a,"webhookAlerts"),r=t.emailEnabled&&n,i=t.webhookEnabled&&o;if(!r&&!i)return{fired:!1,reason:"no_channel_enabled"};const c=[];for(const p of e.results){const f=p.delta;f!=null&&f.new&&c.push(...f.new)}const d=Vt[t.minImpact],l=c.filter(p=>Vt[p.impact]>=d);if(l.length===0)return{fired:!1,reason:"below_impact_threshold"};const h=await da();if(!h)return kt.debug("no license token; alert skipped"),{fired:!1,reason:"no_license_token"};l.sort((p,f)=>Vt[f.impact]-Vt[p.impact]);const m=l.slice(0,10).map(p=>({wcag:p.wcagCriterion,impact:p.impact,selector:p.target.selector,help:p.description.slice(0,480),helpUrl:p.helpUrl})),u=e.results.reduce((p,f)=>p+f.violations.length,0),g={token:h,scheduleId:e.scheduleId,auditedUrl:e.auditedUrl,totalViolations:u,newViolationCount:l.length,topViolations:m,webhookUrl:i&&t.webhookUrl?t.webhookUrl:void 0,emailEnabled:r,ranAt:e.ranAt};try{const p=await fetch(bu,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(g),signal:AbortSignal.timeout(yu)}),f=await p.json().catch(()=>null);return p.ok?(kt.info("alert delivered",{emailSent:f==null?void 0:f.emailSent,webhookSent:f==null?void 0:f.webhookSent}),{fired:!0,emailSent:(f==null?void 0:f.emailSent)??!1,webhookSent:(f==null?void 0:f.webhookSent)??!1,serverStatus:p.status}):(kt.warn(`alert server returned ${p.status}`,f),{fired:!1,reason:`server_${p.status}`,serverStatus:p.status})}catch(p){return kt.warn("alert send failed",p),{fired:!1,reason:p instanceof Error?p.message:"unknown_error"}}}const ku="#dc2626",xu="#f59e0b",Iu="#059669",Su="#94a3b8";function Cu(e){const t=e.filter(c=>c.enabled);let a=0,n=0,o=0,r=0;for(const c of t){const d=c.lastNewViolationCount??0;c.lastResult==="failed"?n++:c.lastResult==="ok"?d>0?a+=d:o++:r++}let i;return t.length===0?i="no-schedules":a>0?i="new-violations":n>0?i="failed":o>0?i="all-clear":i="pending-first-run",{state:i,newViolationsTotal:a,failingCount:n,okCount:o,pendingCount:r,totalEnabled:t.length}}async function $u(e){switch(e.state){case"new-violations":{const t=e.newViolationsTotal>99?"99+":String(e.newViolationsTotal);await chrome.action.setBadgeText({text:t}),await chrome.action.setBadgeBackgroundColor({color:ku}),await chrome.action.setTitle({title:`wcagcheckr — ${e.newViolationsTotal} new WCAG violation${e.newViolationsTotal===1?"":"s"} vs baseline across ${e.totalEnabled} scheduled audit${e.totalEnabled===1?"":"s"}`});break}case"failed":{await chrome.action.setBadgeText({text:"!"}),await chrome.action.setBadgeBackgroundColor({color:xu}),await chrome.action.setTitle({title:`wcagcheckr — ${e.failingCount} scheduled audit${e.failingCount===1?"":"s"} failed to run`});break}case"all-clear":{await chrome.action.setBadgeText({text:"✓"}),await chrome.action.setBadgeBackgroundColor({color:Iu}),await chrome.action.setTitle({title:`wcagcheckr — all ${e.okCount} scheduled audit${e.okCount===1?"":"s"} passing`});break}case"pending-first-run":{await chrome.action.setBadgeText({text:""}),await chrome.action.setBadgeBackgroundColor({color:Su}),await chrome.action.setTitle({title:`wcagcheckr — ${e.pendingCount} scheduled audit${e.pendingCount===1?"":"s"} awaiting first run`});break}case"no-schedules":default:{await chrome.action.setBadgeText({text:""}),await chrome.action.setTitle({title:"wcagcheckr"});break}}}async function Tu(){const e=await ue();if(!de(e,"statusBadge")){try{await chrome.action.setBadgeText({text:""})}catch{}return{state:"no-schedules",newViolationsTotal:0,failingCount:0,okCount:0,pendingCount:0,totalEnabled:0}}const t=await Jo(),a=Cu(t);try{await $u(a)}catch(n){console.warn("[status-badge] failed to apply",n)}return a}const Mo=te("deposition-packet");function ae(e){return e.replace(/[&<>"']/g,t=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[t])}function Eu(e,t,a){if(!e)return`<section class="dp-cover dp-cover-error">
974
982
  <h2 style="margin:0 0 8pt 0; color:#b91c1c">⚠ Chain-of-Custody: UNAVAILABLE</h2>
975
983
  <p>This packet could not be sealed because the underlying audit record was empty. Provenance cannot be verified.</p>
976
- </section>`;const n=Tr(e.hash);return t?`<section class="dp-cover dp-cover-sealed" style="border:2pt solid #059669; background:#ecfdf5; padding:14pt; border-radius:4pt; margin:0 0 14pt 0">
984
+ </section>`;const n=Mr(e.hash);return t?`<section class="dp-cover dp-cover-sealed" style="border:2pt solid #059669; background:#ecfdf5; padding:14pt; border-radius:4pt; margin:0 0 14pt 0">
977
985
  <h2 style="margin:0 0 8pt 0; color:#064e3b">🔒 Chain-of-Custody: SEALED (RFC 3161 + ed25519)</h2>
978
986
  <p style="margin:0 0 10pt 0">This audit's content hash was independently witnessed by a public RFC 3161 timestamp authority and counter-signed by the wcagcheckr server. The seal can be verified offline using only published keys.</p>
979
987
  <dl style="margin:0; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size:9pt">
@@ -1010,7 +1018,7 @@ Primary AI verdict was uncertain. Programmatic multi-column detector found no ma
1010
1018
  <dd style="margin:0 0 6pt 16pt">${ae(e.grade)}</dd>
1011
1019
  </dl>
1012
1020
  <p style="margin:8pt 0 0 0; font-size:10pt"><strong>To convert to anchored status:</strong> re-run this export with an online connection to api.wcagcheckr.com.</p>
1013
- </section>`}function Au(e,t){var n;if(!e)return"";const a=Tr(e.hash);return`<section class="dp-verification" style="border:1pt solid #cbd5e1; padding:14pt; margin:24pt 0; border-radius:4pt; background:#f8fafc">
1021
+ </section>`}function Ru(e,t){var n;if(!e)return"";const a=Mr(e.hash);return`<section class="dp-verification" style="border:1pt solid #cbd5e1; padding:14pt; margin:24pt 0; border-radius:4pt; background:#f8fafc">
1014
1022
  <h2 style="margin:0 0 8pt 0">How to verify this audit's integrity</h2>
1015
1023
  <p style="margin:0 0 8pt 0">This packet is the output of an automated WCAG accessibility audit conducted by the wcagcheckr Chrome extension. The audit's content has been cryptographically sealed so any modification to this document — including changes to the violation list, the screenshots, or the grade — would invalidate the seal.</p>
1016
1024
 
@@ -1043,13 +1051,13 @@ Primary AI verdict was uncertain. Programmatic multi-column detector found no ma
1043
1051
  <li style="margin-bottom:4pt">It does not authenticate the auditor (a human operator may have run the extension; the seal only attests to the audit content).</li>
1044
1052
  <li style="margin-bottom:4pt">It does not warrant against future protocol breaks (cryptography evolves; a seal valid today may need re-anchoring decades later).</li>
1045
1053
  </ul>
1046
- </section>`}function ku(e){return e?`<section class="dp-declaration" style="border:1pt solid #cbd5e1; padding:14pt; margin:24pt 0; border-radius:4pt; page-break-before:always">
1054
+ </section>`}function Ou(e){return e?`<section class="dp-declaration" style="border:1pt solid #cbd5e1; padding:14pt; margin:24pt 0; border-radius:4pt; page-break-before:always">
1047
1055
  <h2 style="margin:0 0 8pt 0">Declaration under penalty of perjury (template)</h2>
1048
1056
  <p style="margin:0 0 8pt 0; font-size:10pt; color:#475569">The template below is provided for the convenience of the auditor and should be reviewed by competent counsel before execution. wcagcheckr makes no representation that this template is sufficient for any particular jurisdiction's requirements.</p>
1049
1057
  <div style="border:1pt dashed #94a3b8; padding:12pt; background:#ffffff">
1050
1058
  <p style="margin:0 0 10pt 0">I, <strong>[Name of declarant]</strong>, of <strong>[City, State / Province]</strong>, hereby declare under penalty of perjury under the laws of <strong>[jurisdiction]</strong> that the following is true and correct:</p>
1051
1059
  <ol style="margin:0 0 10pt 20pt">
1052
- <li style="margin-bottom:6pt">I conducted an automated accessibility audit of <strong>${ae(e.pageUrl)}</strong> on <strong>${ae(e.capturedAt)}</strong> using the wcagcheckr Chrome extension, version <strong>${ae($e())}</strong>, running axe-core <strong>${ae(e.axeVersion)}</strong>.</li>
1060
+ <li style="margin-bottom:6pt">I conducted an automated accessibility audit of <strong>${ae(e.pageUrl)}</strong> on <strong>${ae(e.capturedAt)}</strong> using the wcagcheckr Chrome extension, version <strong>${ae(Te())}</strong>, running axe-core <strong>${ae(e.axeVersion)}</strong>.</li>
1053
1061
  <li style="margin-bottom:6pt">The audit produced a SHA-256 content hash of <code style="word-break:break-all">${ae(e.hash)}</code>, which was sealed via RFC 3161 trusted timestamp and the wcagcheckr server's ed25519 signature.</li>
1054
1062
  <li style="margin-bottom:6pt">The contents of this packet (the chain-of-custody cover, the findings, the per-state screenshots, and the verification instructions) are an accurate and complete representation of the sealed audit. No findings were added, removed, or altered after sealing.</li>
1055
1063
  <li style="margin-bottom:6pt">[Optional: I am personally familiar with the page audited, and to the best of my knowledge it was in its [production / staging / pre-deployment] state on the audit date.]</li>
@@ -1058,21 +1066,21 @@ Primary AI verdict was uncertain. Programmatic multi-column detector found no ma
1058
1066
  <p style="margin:24pt 0 6pt 0; border-top:1pt solid #0f172a; padding-top:6pt; max-width:300pt"><strong>[Signature]</strong></p>
1059
1067
  <p style="margin:0; font-size:10pt"><strong>[Printed name]</strong></p>
1060
1068
  </div>
1061
- </section>`:""}function Iu(e){const t=e.map(a=>({...a,screenshotDataUrl:void 0,states:void 0}));return`<section class="dp-raw-json" style="display:none" aria-hidden="true">
1069
+ </section>`:""}function Mu(e){const t=e.map(a=>({...a,screenshotDataUrl:void 0,states:void 0}));return`<section class="dp-raw-json" style="display:none" aria-hidden="true">
1062
1070
  <h2>Raw axe-core JSON (machine-readable)</h2>
1063
1071
  <p>The block below is the unfiltered axe-core output included for machine re-verification. View HTML source to extract.</p>
1064
1072
  <pre id="wcagcheckr-axe-raw-json" data-product="wcagcheckr" data-format="deposition-packet-v1">${ae(JSON.stringify(t,null,2))}</pre>
1065
- </section>`}async function xu(e,t,a,n,o,r){if(e.length===0)return null;const i=e[0],d=(await sc(i.componentId)).find(u=>u.capturedAt===i.startedAt);if(d)return d;const l=e.flatMap(u=>u.violations),h=Be(l,{runs:[],workflows:Re},e,n,t,a,o,st(e),r),m=e.reduce((u,g)=>u+g.durationMs,0);return Sr(e,{...h.score,letter:h.combined},m)}async function ci(e){let t;try{t=await xu(e.results,e.interactiveAuditResults,e.walkthroughAcks,e.acknowledgedMatchKeys,e.humanCriterionVerdicts,e.incompleteResolutions)}catch(f){Ro.warn("ensureForensicEntry failed",f),t=null}let a;if(t&&!t.receipt)try{const f=await Kr(t,e.licenseId);f?(await Cr(t.componentId,t.capturedAt,f),t={...t,receipt:f}):a="server returned null or malformed receipt"}catch(f){a=f instanceof Error?f.message:String(f),Ro.warn("anchor failed",f)}const n=await kt().catch(()=>[]),o=Rt(e.results,e.delta,"defense",e.manualRuns,n,e.aiSummary??void 0,e.wallClockMs,e.interactiveAuditResults,e.walkthroughAcks,e.acknowledgedMatchKeys,e.humanCriterionVerdicts,e.incompleteResolutions),r=vu(t,(t==null?void 0:t.receipt)??null,a),i=Au(t,(t==null?void 0:t.receipt)??null),c=ku(t),d=Iu(e.results);let l=o;const h=l.indexOf("<h1");h>-1?l=l.slice(0,h)+r+l.slice(h):l=r+l;const m=l.lastIndexOf("</main>"),u=m>-1?m:l.lastIndexOf("</body>"),g=i+c+d;return l=u>-1?l.slice(0,u)+g+l.slice(u):l+g,{html:l,entry:t,anchored:!!(t!=null&&t.receipt),anchorError:a}}function xe(e){return e.replace(/[&<>"']/g,t=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[t])}function Su(e){const t=new Set;for(const a of e){const n=a.axeRulesEvaluated;if(n){for(const o of n.passed??[])t.add(o.ruleId);for(const o of n.inapplicable??[])t.add(o.ruleId);for(const o of n.incomplete??[])t.add(o.ruleId);for(const o of a.violations)t.add(o.ruleId)}}return t}const Cu=new Set(["1.1.1","1.2.2","1.3.1","1.3.4","1.3.5","1.4.1","1.4.2","1.4.3","1.4.4","2.1.1","2.2.2","2.4.1","2.4.2","2.4.4","2.4.6","2.5.3","3.1.1","3.1.2","3.2.2","3.3.1","3.3.2","4.1.1","4.1.2","4.1.3"]);function $u(e,t,a,n){return`<section>
1073
+ </section>`}async function Du(e,t,a,n,o,r){if(e.length===0)return null;const i=e[0],d=(await mc(i.componentId)).find(u=>u.capturedAt===i.startedAt);if(d)return d;const l=e.flatMap(u=>u.violations),h=Ke(l,{runs:[],workflows:Me},e,n,t,a,o,lt(e),r),m=e.reduce((u,g)=>u+g.durationMs,0);return Er(e,{...h.score,letter:h.combined},m)}async function pi(e){let t;try{t=await Du(e.results,e.interactiveAuditResults,e.walkthroughAcks,e.acknowledgedMatchKeys,e.humanCriterionVerdicts,e.incompleteResolutions)}catch(p){Mo.warn("ensureForensicEntry failed",p),t=null}let a;if(t&&!t.receipt)try{const p=await Qr(t,e.licenseId);p?(await Rr(t.componentId,t.capturedAt,p),t={...t,receipt:p}):a="server returned null or malformed receipt"}catch(p){a=p instanceof Error?p.message:String(p),Mo.warn("anchor failed",p)}const n=await St().catch(()=>[]),o=Dt(e.results,e.delta,"defense",e.manualRuns,n,e.aiSummary??void 0,e.wallClockMs,e.interactiveAuditResults,e.walkthroughAcks,e.acknowledgedMatchKeys,e.humanCriterionVerdicts,e.incompleteResolutions),r=Eu(t,(t==null?void 0:t.receipt)??null,a),i=Ru(t,(t==null?void 0:t.receipt)??null),c=Ou(t),d=Mu(e.results);let l=o;const h=l.indexOf("<h1");h>-1?l=l.slice(0,h)+r+l.slice(h):l=r+l;const m=l.lastIndexOf("</main>"),u=m>-1?m:l.lastIndexOf("</body>"),g=i+c+d;return l=u>-1?l.slice(0,u)+g+l.slice(u):l+g,{html:l,entry:t,anchored:!!(t!=null&&t.receipt),anchorError:a}}function Ie(e){return e.replace(/[&<>"']/g,t=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[t])}function Lu(e){const t=new Set;for(const a of e){const n=a.axeRulesEvaluated;if(n){for(const o of n.passed??[])t.add(o.ruleId);for(const o of n.inapplicable??[])t.add(o.ruleId);for(const o of n.incomplete??[])t.add(o.ruleId);for(const o of a.violations)t.add(o.ruleId)}}return t}const Nu=new Set(["1.1.1","1.2.2","1.3.1","1.3.4","1.3.5","1.4.1","1.4.2","1.4.3","1.4.4","2.1.1","2.2.2","2.4.1","2.4.2","2.4.4","2.4.6","2.5.3","3.1.1","3.1.2","3.2.2","3.3.1","3.3.2","4.1.1","4.1.2","4.1.3"]);function Pu(e,t,a,n){return`<section>
1066
1074
  <h2>1. Tool identification</h2>
1067
1075
  <table style="border-collapse:collapse; margin-top:8pt; font-size:10pt">
1068
1076
  <tr><td style="padding:2pt 12pt 2pt 0; color:#475569">Tool</td><td><strong>wcagcheckr</strong> — Chrome MV3 accessibility audit extension</td></tr>
1069
- <tr><td style="padding:2pt 12pt 2pt 0; color:#475569">Extension version</td><td><code>${xe(t)}</code></td></tr>
1070
- <tr><td style="padding:2pt 12pt 2pt 0; color:#475569">Audit engine</td><td><code>axe-core ${xe(a)}</code> (pinned; not auto-updated)</td></tr>
1071
- <tr><td style="padding:2pt 12pt 2pt 0; color:#475569">WCAG target</td><td>WCAG ${xe(e)} Level AA</td></tr>
1077
+ <tr><td style="padding:2pt 12pt 2pt 0; color:#475569">Extension version</td><td><code>${Ie(t)}</code></td></tr>
1078
+ <tr><td style="padding:2pt 12pt 2pt 0; color:#475569">Audit engine</td><td><code>axe-core ${Ie(a)}</code> (pinned; not auto-updated)</td></tr>
1079
+ <tr><td style="padding:2pt 12pt 2pt 0; color:#475569">WCAG target</td><td>WCAG ${Ie(e)} Level AA</td></tr>
1072
1080
  <tr><td style="padding:2pt 12pt 2pt 0; color:#475569">Conformance frame</td><td>EN 301 549 v3.2.1 (EU); Section 508 / ADA (US); EAA Directive 2019/882 (EU, enforceable 2025-06-28)</td></tr>
1073
- ${n?`<tr><td style="padding:2pt 12pt 2pt 0; color:#475569">Audit captured at</td><td><code>${xe(n)}</code></td></tr>`:""}
1081
+ ${n?`<tr><td style="padding:2pt 12pt 2pt 0; color:#475569">Audit captured at</td><td><code>${Ie(n)}</code></td></tr>`:""}
1074
1082
  </table>
1075
- </section>`}function Tu(){return`<section>
1083
+ </section>`}function Fu(){return`<section>
1076
1084
  <h2>2. Methodology overview</h2>
1077
1085
  <p>The wcagcheckr audit framework rests on three layers that complement (rather than overlap) one another:</p>
1078
1086
  <ol>
@@ -1081,7 +1089,7 @@ Primary AI verdict was uncertain. Programmatic multi-column detector found no ma
1081
1089
  <li><strong>Per-component scorecards.</strong> Findings are attributed to a stable component identity (URL + ARIA test-id + structural fingerprint) rather than to a one-off page snapshot, so re-audits over time produce a coherent history per design-system component.</li>
1082
1090
  </ol>
1083
1091
  <p>The audit pipeline is fail-safe: each state is captured + scored independently, so a transient layout glitch or third-party script load failure in one state does not invalidate findings from the other states.</p>
1084
- </section>`}function Eu(){return`<section>
1092
+ </section>`}function _u(){return`<section>
1085
1093
  <h2>3. State matrix specification</h2>
1086
1094
  <p>For each in-scope interactive element, the audit drives the following dimensions and records the rendered DOM + computed styles at each combination:</p>
1087
1095
  <table style="border-collapse:collapse; font-size:10pt; margin-top:8pt">
@@ -1098,9 +1106,9 @@ Primary AI verdict was uncertain. Programmatic multi-column detector found no ma
1098
1106
  <tr><td style="padding:4pt 12pt 4pt 0">Motion preference</td><td style="padding:4pt 12pt 4pt 0">prefers-reduced-motion: reduce</td><td style="padding:4pt; color:#475569">Emulation.setEmulatedMedia</td></tr>
1099
1107
  </table>
1100
1108
  <p style="margin-top:10pt">At each combination, a viewport screenshot is captured, the computed-style snapshot is recorded, and axe-core is invoked against the live DOM (not a serialized copy). Findings are tagged with the originating state so reports show which state produced each violation.</p>
1101
- </section>`}function Ru(e,t){const a=new Set;for(const r of Oo)for(const i of r.rules)a.add(i);const o=Array.from(a).sort().map(r=>{const i=t.has(r),d=Oo.filter(l=>l.rules.includes(r)).map(l=>`${l.ref} ${l.title}`).join(", ")||"(unmapped)";return`<tr>
1102
- <td style="padding:3pt; font-family:monospace; font-size:9pt"><code>${xe(r)}</code></td>
1103
- <td style="padding:3pt; font-size:9pt">${xe(d)}</td>
1109
+ </section>`}function Uu(e,t){const a=new Set;for(const r of Do)for(const i of r.rules)a.add(i);const o=Array.from(a).sort().map(r=>{const i=t.has(r),d=Do.filter(l=>l.rules.includes(r)).map(l=>`${l.ref} ${l.title}`).join(", ")||"(unmapped)";return`<tr>
1110
+ <td style="padding:3pt; font-family:monospace; font-size:9pt"><code>${Ie(r)}</code></td>
1111
+ <td style="padding:3pt; font-size:9pt">${Ie(d)}</td>
1104
1112
  <td style="padding:3pt; font-size:9pt; ${i?"color:#065f46;font-weight:600":"color:#475569"}">${i?"✓ ran":"— not in this run"}</td>
1105
1113
  </tr>`}).join("");return`<section>
1106
1114
  <h2>4. Automated rule coverage (axe-core)</h2>
@@ -1113,7 +1121,7 @@ Primary AI verdict was uncertain. Programmatic multi-column detector found no ma
1113
1121
  </tr>
1114
1122
  ${o}
1115
1123
  </table>
1116
- </section>`}function Ou(e){const t=new Set((e??[]).map(n=>n.workflowId));return`<section>
1124
+ </section>`}function Wu(e){const t=new Set((e??[]).map(n=>n.workflowId));return`<section>
1117
1125
  <h2>5. Manual workflow framework (Intelligent Guided Tests)</h2>
1118
1126
  <p>WCAG includes criteria that cannot be reduced to a single automated heuristic — keyboard reachability, focus management, screen-reader announcement quality, cognitive task complexity. wcagcheckr provides curated step-by-step workflows that surface the right specific checks at the right time, recorded for inclusion in the audit record:</p>
1119
1127
  <table style="border-collapse:collapse; font-size:10pt; margin-top:8pt; width:100%">
@@ -1123,18 +1131,18 @@ Primary AI verdict was uncertain. Programmatic multi-column detector found no ma
1123
1131
  <th style="text-align:right; padding:4pt">Length</th>
1124
1132
  <th style="text-align:left; padding:4pt">Status</th>
1125
1133
  </tr>
1126
- ${Re.map(n=>{const o=t.has(n.id),r=n.steps.length;return`<tr>
1127
- <td style="padding:4pt; font-weight:600">${xe(n.name)}</td>
1128
- <td style="padding:4pt; color:#475569; max-width:380pt">${xe(n.blurb)}</td>
1134
+ ${Me.map(n=>{const o=t.has(n.id),r=n.steps.length;return`<tr>
1135
+ <td style="padding:4pt; font-weight:600">${Ie(n.name)}</td>
1136
+ <td style="padding:4pt; color:#475569; max-width:380pt">${Ie(n.blurb)}</td>
1129
1137
  <td style="padding:4pt; font-size:9pt; text-align:right">${r} step${r===1?"":"s"}</td>
1130
1138
  <td style="padding:4pt; font-size:9pt; ${o?"color:#065f46;font-weight:600":"color:#475569"}">${o?"✓ executed":"— not in this run"}</td>
1131
1139
  </tr>`}).join("")}
1132
1140
  </table>
1133
1141
  <p style="margin-top:10pt; font-size:10pt; color:#475569">Each step records a pass/fail/skip + free-form notes + a screenshot of the state being evaluated. Workflow run-records are included in the per-component baseline so subsequent audits can compare keyboard/SR behavior over time.</p>
1134
- </section>`}function Mu(e){const a=ca(e,"AA").filter(o=>!Cu.has(o.id)),n=a.map(o=>`<tr>
1135
- <td style="padding:3pt; font-family:monospace; font-size:9pt"><code>${xe(o.id)}</code></td>
1136
- <td style="padding:3pt"><strong>${xe(o.title)}</strong> <span style="color:#475569">(${xe(o.level)})</span></td>
1137
- <td style="padding:3pt; color:#475569; font-size:10pt">${xe(o.shortDescription)}</td>
1142
+ </section>`}function Gu(e){const a=ua(e,"AA").filter(o=>!Nu.has(o.id)),n=a.map(o=>`<tr>
1143
+ <td style="padding:3pt; font-family:monospace; font-size:9pt"><code>${Ie(o.id)}</code></td>
1144
+ <td style="padding:3pt"><strong>${Ie(o.title)}</strong> <span style="color:#475569">(${Ie(o.level)})</span></td>
1145
+ <td style="padding:3pt; color:#475569; font-size:10pt">${Ie(o.shortDescription)}</td>
1138
1146
  </tr>`).join("");return`<section>
1139
1147
  <h2>6. WCAG criteria requiring manual review</h2>
1140
1148
  <p>The following ${a.length} WCAG ${e} Level AA criteria have <strong>no automated heuristic</strong> that can produce a defensible pass/fail signal alone. wcagcheckr surfaces these criteria with manual-review prompts via the IGT workflows (Section 5) or via the side-panel "Manual checks needed" list. This list is published verbatim so a reviewer can confirm completeness:</p>
@@ -1146,7 +1154,7 @@ Primary AI verdict was uncertain. Programmatic multi-column detector found no ma
1146
1154
  </tr>
1147
1155
  ${n}
1148
1156
  </table>
1149
- </section>`}function Du(){return`<section>
1157
+ </section>`}function Vu(){return`<section>
1150
1158
  <h2>7. Limitations of automated detection</h2>
1151
1159
  <p>wcagcheckr provides high-precision automated coverage where automation can be trusted, and explicit manual prompts where it cannot. The audit framework does <strong>not</strong> claim to detect:</p>
1152
1160
  <ul>
@@ -1158,7 +1166,7 @@ Primary AI verdict was uncertain. Programmatic multi-column detector found no ma
1158
1166
  <li>Dynamic content that requires authentication or sequential user input to reach the audit target — the audit applies to the page as rendered at the time of capture.</li>
1159
1167
  </ul>
1160
1168
  <p>Findings produced by this tool support but do not substitute for an expert review.</p>
1161
- </section>`}function Lu(){return`<section>
1169
+ </section>`}function ju(){return`<section>
1162
1170
  <h2>8. Audit process (technical specification)</h2>
1163
1171
  <ol>
1164
1172
  <li><strong>Tab attach.</strong> The extension's service worker attaches via <code>chrome.debugger</code> (CDP version 1.3) to the audited tab. The user is shown the standard Chrome "developer is debugging this browser" banner — wcagcheckr does not attempt to hide or work around this banner.</li>
@@ -1170,7 +1178,7 @@ Primary AI verdict was uncertain. Programmatic multi-column detector found no ma
1170
1178
  <li><strong>Baseline diff.</strong> The merged result is diffed against the saved baseline for this component. New violations, persistent violations, and fixed violations are categorized separately.</li>
1171
1179
  <li><strong>Forensic record.</strong> A SHA-256 hash of the canonical audit identity block (componentId, pageUrl, grade, totals, axeVersion, capturedAt, statesAudited) is computed and stored in the local forensic log.</li>
1172
1180
  </ol>
1173
- </section>`}function Pu(){return`<section>
1181
+ </section>`}function Hu(){return`<section>
1174
1182
  <h2>9. Forensic anchoring (RFC 3161 + ed25519)</h2>
1175
1183
  <p>Audit records can be sealed against a trusted third-party timestamp authority and counter-signed by the wcagcheckr server. When sealed:</p>
1176
1184
  <ul>
@@ -1180,7 +1188,7 @@ Primary AI verdict was uncertain. Programmatic multi-column detector found no ma
1180
1188
  <li>Each new anchor incorporates the hash of the previous anchor in the chain, so removing or backdating an entry would invalidate every later seal.</li>
1181
1189
  </ul>
1182
1190
  <p>Sealed audits are surfaced in the deposition packet (G18) with the full receipt + verification instructions. Public verification page: <a href="https://api.wcagcheckr.com/verify">api.wcagcheckr.com/verify</a>.</p>
1183
- </section>`}function Nu(){return`<section>
1191
+ </section>`}function Bu(){return`<section>
1184
1192
  <h2>10. References</h2>
1185
1193
  <ul style="font-size:10pt; line-height:1.6">
1186
1194
  <li>W3C, <a href="https://www.w3.org/TR/WCAG21/">Web Content Accessibility Guidelines (WCAG) 2.1</a>, W3C Recommendation 2018-06-05 (updated 2023-09-21).</li>
@@ -1191,11 +1199,11 @@ Primary AI verdict was uncertain. Programmatic multi-column detector found no ma
1191
1199
  <li>IETF RFC 3161, <a href="https://datatracker.ietf.org/doc/html/rfc3161">Internet X.509 Public Key Infrastructure Time-Stamp Protocol (TSP)</a>.</li>
1192
1200
  <li>IETF RFC 8032, <a href="https://datatracker.ietf.org/doc/html/rfc8032">Edwards-Curve Digital Signature Algorithm (EdDSA)</a>.</li>
1193
1201
  </ul>
1194
- </section>`}const Oo=[{ref:"1.1.1",title:"Non-text Content",rules:["image-alt","input-image-alt","area-alt","object-alt","svg-img-alt"]},{ref:"1.2.2",title:"Captions (Prerecorded)",rules:["video-caption"]},{ref:"1.3.1",title:"Info and Relationships",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.4",title:"Orientation",rules:["css-orientation-lock"]},{ref:"1.3.5",title:"Identify Input Purpose",rules:["autocomplete-valid"]},{ref:"1.4.1",title:"Use of Color",rules:["link-in-text-block"]},{ref:"1.4.2",title:"Audio Control",rules:["no-autoplay-audio"]},{ref:"1.4.3",title:"Contrast (Minimum)",rules:["color-contrast"]},{ref:"1.4.4",title:"Resize Text",rules:["meta-viewport"]},{ref:"2.1.1",title:"Keyboard",rules:["accesskeys"]},{ref:"2.2.2",title:"Pause, Stop, Hide",rules:["blink","marquee","meta-refresh"]},{ref:"2.4.1",title:"Bypass Blocks",rules:["bypass","skip-link"]},{ref:"2.4.2",title:"Page Titled",rules:["document-title"]},{ref:"2.4.4",title:"Link Purpose (In Context)",rules:["link-name"]},{ref:"2.4.6",title:"Headings and Labels",rules:["empty-heading"]},{ref:"3.1.1",title:"Language of Page",rules:["html-has-lang","html-lang-valid"]},{ref:"3.1.2",title:"Language of Parts",rules:["valid-lang"]},{ref:"3.3.1",title:"Error Identification",rules:["aria-input-field-name"]},{ref:"3.3.2",title:"Labels or Instructions",rules:["label-title-only","form-field-multiple-labels"]},{ref:"4.1.1",title:"Parsing",rules:["duplicate-id","duplicate-id-active","duplicate-id-aria"]},{ref:"4.1.2",title:"Name, Role, Value",rules:["aria-allowed-attr","aria-required-attr","aria-roles","aria-valid-attr-value","aria-valid-attr","button-name","select-name","role-img-alt","aria-hidden-body","aria-hidden-focus"]},{ref:"4.1.3",title:"Status Messages",rules:["aria-toggle-field-name"]}];function Fu(e={}){var l,h,m,u,g;const t=e.results??[],a=e.manualRuns,n=e.wcagTargetOverride??St,o=typeof chrome<"u"&&((m=(h=(l=chrome.runtime)==null?void 0:l.getManifest)==null?void 0:h.call(l))==null?void 0:m.version)||"unknown",r=((u=t[0])==null?void 0:u.axeVersion)??"4.11.4",i=((g=t[0])==null?void 0:g.startedAt)??null,c=Su(t),d=c.size>0;return ze(`<!doctype html>
1202
+ </section>`}const Do=[{ref:"1.1.1",title:"Non-text Content",rules:["image-alt","input-image-alt","area-alt","object-alt","svg-img-alt"]},{ref:"1.2.2",title:"Captions (Prerecorded)",rules:["video-caption"]},{ref:"1.3.1",title:"Info and Relationships",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.4",title:"Orientation",rules:["css-orientation-lock"]},{ref:"1.3.5",title:"Identify Input Purpose",rules:["autocomplete-valid"]},{ref:"1.4.1",title:"Use of Color",rules:["link-in-text-block"]},{ref:"1.4.2",title:"Audio Control",rules:["no-autoplay-audio"]},{ref:"1.4.3",title:"Contrast (Minimum)",rules:["color-contrast"]},{ref:"1.4.4",title:"Resize Text",rules:["meta-viewport"]},{ref:"2.1.1",title:"Keyboard",rules:["accesskeys"]},{ref:"2.2.2",title:"Pause, Stop, Hide",rules:["blink","marquee","meta-refresh"]},{ref:"2.4.1",title:"Bypass Blocks",rules:["bypass","skip-link"]},{ref:"2.4.2",title:"Page Titled",rules:["document-title"]},{ref:"2.4.4",title:"Link Purpose (In Context)",rules:["link-name"]},{ref:"2.4.6",title:"Headings and Labels",rules:["empty-heading"]},{ref:"3.1.1",title:"Language of Page",rules:["html-has-lang","html-lang-valid"]},{ref:"3.1.2",title:"Language of Parts",rules:["valid-lang"]},{ref:"3.3.1",title:"Error Identification",rules:["aria-input-field-name"]},{ref:"3.3.2",title:"Labels or Instructions",rules:["label-title-only","form-field-multiple-labels"]},{ref:"4.1.1",title:"Parsing",rules:["duplicate-id","duplicate-id-active","duplicate-id-aria"]},{ref:"4.1.2",title:"Name, Role, Value",rules:["aria-allowed-attr","aria-required-attr","aria-roles","aria-valid-attr-value","aria-valid-attr","button-name","select-name","role-img-alt","aria-hidden-body","aria-hidden-focus"]},{ref:"4.1.3",title:"Status Messages",rules:["aria-toggle-field-name"]}];function qu(e={}){var l,h,m,u,g;const t=e.results??[],a=e.manualRuns,n=e.wcagTargetOverride??Tt,o=typeof chrome<"u"&&((m=(h=(l=chrome.runtime)==null?void 0:l.getManifest)==null?void 0:h.call(l))==null?void 0:m.version)||"unknown",r=((u=t[0])==null?void 0:u.axeVersion)??"4.11.4",i=((g=t[0])==null?void 0:g.startedAt)??null,c=Lu(t),d=c.size>0;return Ye(`<!doctype html>
1195
1203
  <html lang="en">
1196
1204
  <head>
1197
1205
  <meta charset="utf-8" />
1198
- <title>wcagcheckr — Audit Methodology (WCAG ${xe(n)} AA)</title>
1206
+ <title>wcagcheckr — Audit Methodology (WCAG ${Ie(n)} AA)</title>
1199
1207
  <style>
1200
1208
  *, *::before, *::after { box-sizing: border-box; }
1201
1209
  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; }
@@ -1216,40 +1224,40 @@ Primary AI verdict was uncertain. Programmatic multi-column detector found no ma
1216
1224
  <h1>Audit Methodology — wcagcheckr</h1>
1217
1225
  <p class="doc-meta">Standalone methodology document. Pair with a specific audit's deposition packet (separate file) to demonstrate both how the audit was conducted and what it found.</p>
1218
1226
 
1219
- ${$u(n,o,r,i)}
1220
- ${Tu()}
1221
- ${Eu()}
1222
- ${Ru(d,c)}
1223
- ${Ou(a)}
1224
- ${Mu(n)}
1225
- ${Du()}
1226
- ${Lu()}
1227
- ${Pu()}
1228
- ${Nu()}
1227
+ ${Pu(n,o,r,i)}
1228
+ ${Fu()}
1229
+ ${_u()}
1230
+ ${Uu(d,c)}
1231
+ ${Wu(a)}
1232
+ ${Gu(n)}
1233
+ ${Vu()}
1234
+ ${ju()}
1235
+ ${Hu()}
1236
+ ${Bu()}
1229
1237
 
1230
1238
  </body>
1231
- </html>`)}const Mo=[{wcagId:"1.1.1",title:"Non-text Content",level:"A",addedIn:"2.0",en301549Clause:"9.1.1.1",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"1.2.1",title:"Audio-only and Video-only (Prerecorded)",level:"A",addedIn:"2.0",en301549Clause:"9.1.2.1",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"1.2.2",title:"Captions (Prerecorded)",level:"A",addedIn:"2.0",en301549Clause:"9.1.2.2",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"1.2.3",title:"Audio Description or Media Alternative (Prerecorded)",level:"A",addedIn:"2.0",en301549Clause:"9.1.2.3",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"1.2.4",title:"Captions (Live)",level:"AA",addedIn:"2.0",en301549Clause:"9.1.2.4",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"1.2.5",title:"Audio Description (Prerecorded)",level:"AA",addedIn:"2.0",en301549Clause:"9.1.2.5",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"1.3.1",title:"Info and Relationships",level:"A",addedIn:"2.0",en301549Clause:"9.1.3.1",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"1.3.2",title:"Meaningful Sequence",level:"A",addedIn:"2.0",en301549Clause:"9.1.3.2",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"1.3.3",title:"Sensory Characteristics",level:"A",addedIn:"2.0",en301549Clause:"9.1.3.3",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"1.3.4",title:"Orientation",level:"AA",addedIn:"2.1",en301549Clause:"9.1.3.4",section508:"not-in-scope-2.1",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"not-in-scope-2.1",notes:"WCAG 2.1 addition. Section 508 Refresh is bound to WCAG 2.0."},{wcagId:"1.3.5",title:"Identify Input Purpose",level:"AA",addedIn:"2.1",en301549Clause:"9.1.3.5",section508:"not-in-scope-2.1",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"not-in-scope-2.1"},{wcagId:"1.4.1",title:"Use of Color",level:"A",addedIn:"2.0",en301549Clause:"9.1.4.1",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"1.4.2",title:"Audio Control",level:"A",addedIn:"2.0",en301549Clause:"9.1.4.2",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"1.4.3",title:"Contrast (Minimum)",level:"AA",addedIn:"2.0",en301549Clause:"9.1.4.3",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope",notes:"The single most-cited criterion in US Title III website complaints."},{wcagId:"1.4.4",title:"Resize Text",level:"AA",addedIn:"2.0",en301549Clause:"9.1.4.4",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"1.4.5",title:"Images of Text",level:"AA",addedIn:"2.0",en301549Clause:"9.1.4.5",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"1.4.10",title:"Reflow",level:"AA",addedIn:"2.1",en301549Clause:"9.1.4.10",section508:"not-in-scope-2.1",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"not-in-scope-2.1"},{wcagId:"1.4.11",title:"Non-text Contrast",level:"AA",addedIn:"2.1",en301549Clause:"9.1.4.11",section508:"not-in-scope-2.1",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"not-in-scope-2.1"},{wcagId:"1.4.12",title:"Text Spacing",level:"AA",addedIn:"2.1",en301549Clause:"9.1.4.12",section508:"not-in-scope-2.1",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"not-in-scope-2.1"},{wcagId:"1.4.13",title:"Content on Hover or Focus",level:"AA",addedIn:"2.1",en301549Clause:"9.1.4.13",section508:"not-in-scope-2.1",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"not-in-scope-2.1",notes:"Cited in tooltip + dropdown-related complaints."},{wcagId:"2.1.1",title:"Keyboard",level:"A",addedIn:"2.0",en301549Clause:"9.2.1.1",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"2.1.2",title:"No Keyboard Trap",level:"A",addedIn:"2.0",en301549Clause:"9.2.1.2",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"2.1.4",title:"Character Key Shortcuts",level:"A",addedIn:"2.1",en301549Clause:"9.2.1.4",section508:"not-in-scope-2.1",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"not-in-scope-2.1"},{wcagId:"2.2.1",title:"Timing Adjustable",level:"A",addedIn:"2.0",en301549Clause:"9.2.2.1",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"2.2.2",title:"Pause, Stop, Hide",level:"A",addedIn:"2.0",en301549Clause:"9.2.2.2",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"2.3.1",title:"Three Flashes or Below Threshold",level:"A",addedIn:"2.0",en301549Clause:"9.2.3.1",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"2.4.1",title:"Bypass Blocks",level:"A",addedIn:"2.0",en301549Clause:"9.2.4.1",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"2.4.2",title:"Page Titled",level:"A",addedIn:"2.0",en301549Clause:"9.2.4.2",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"2.4.3",title:"Focus Order",level:"A",addedIn:"2.0",en301549Clause:"9.2.4.3",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"2.4.4",title:"Link Purpose (In Context)",level:"A",addedIn:"2.0",en301549Clause:"9.2.4.4",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"2.4.5",title:"Multiple Ways",level:"AA",addedIn:"2.0",en301549Clause:"9.2.4.5",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"2.4.6",title:"Headings and Labels",level:"AA",addedIn:"2.0",en301549Clause:"9.2.4.6",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"2.4.7",title:"Focus Visible",level:"AA",addedIn:"2.0",en301549Clause:"9.2.4.7",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"2.4.11",title:"Focus Not Obscured (Minimum)",level:"AA",addedIn:"2.2",en301549Clause:"9.2.4.11",section508:"not-in-scope-2.2",adaTitleIII:"gray-area",eaa:"gray-area",aoda:"not-in-scope-2.2",notes:"WCAG 2.2 addition. EN 301 549 may not yet reference; case law not yet settled."},{wcagId:"2.5.1",title:"Pointer Gestures",level:"A",addedIn:"2.1",en301549Clause:"9.2.5.1",section508:"not-in-scope-2.1",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"not-in-scope-2.1"},{wcagId:"2.5.2",title:"Pointer Cancellation",level:"A",addedIn:"2.1",en301549Clause:"9.2.5.2",section508:"not-in-scope-2.1",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"not-in-scope-2.1"},{wcagId:"2.5.3",title:"Label in Name",level:"A",addedIn:"2.1",en301549Clause:"9.2.5.3",section508:"not-in-scope-2.1",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"not-in-scope-2.1"},{wcagId:"2.5.4",title:"Motion Actuation",level:"A",addedIn:"2.1",en301549Clause:"9.2.5.4",section508:"not-in-scope-2.1",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"not-in-scope-2.1"},{wcagId:"2.5.7",title:"Dragging Movements",level:"AA",addedIn:"2.2",en301549Clause:"9.2.5.7",section508:"not-in-scope-2.2",adaTitleIII:"gray-area",eaa:"gray-area",aoda:"not-in-scope-2.2"},{wcagId:"2.5.8",title:"Target Size (Minimum)",level:"AA",addedIn:"2.2",en301549Clause:"9.2.5.8",section508:"not-in-scope-2.2",adaTitleIII:"gray-area",eaa:"gray-area",aoda:"not-in-scope-2.2"},{wcagId:"3.1.1",title:"Language of Page",level:"A",addedIn:"2.0",en301549Clause:"9.3.1.1",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"3.1.2",title:"Language of Parts",level:"AA",addedIn:"2.0",en301549Clause:"9.3.1.2",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"3.2.1",title:"On Focus",level:"A",addedIn:"2.0",en301549Clause:"9.3.2.1",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"3.2.2",title:"On Input",level:"A",addedIn:"2.0",en301549Clause:"9.3.2.2",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"3.2.3",title:"Consistent Navigation",level:"AA",addedIn:"2.0",en301549Clause:"9.3.2.3",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"3.2.4",title:"Consistent Identification",level:"AA",addedIn:"2.0",en301549Clause:"9.3.2.4",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"3.2.6",title:"Consistent Help",level:"A",addedIn:"2.2",en301549Clause:"9.3.2.6",section508:"not-in-scope-2.2",adaTitleIII:"gray-area",eaa:"gray-area",aoda:"not-in-scope-2.2"},{wcagId:"3.3.1",title:"Error Identification",level:"A",addedIn:"2.0",en301549Clause:"9.3.3.1",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"3.3.2",title:"Labels or Instructions",level:"A",addedIn:"2.0",en301549Clause:"9.3.3.2",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"3.3.3",title:"Error Suggestion",level:"AA",addedIn:"2.0",en301549Clause:"9.3.3.3",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"3.3.4",title:"Error Prevention (Legal, Financial, Data)",level:"AA",addedIn:"2.0",en301549Clause:"9.3.3.4",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"3.3.7",title:"Redundant Entry",level:"A",addedIn:"2.2",en301549Clause:"9.3.3.7",section508:"not-in-scope-2.2",adaTitleIII:"gray-area",eaa:"gray-area",aoda:"not-in-scope-2.2"},{wcagId:"3.3.8",title:"Accessible Authentication (Minimum)",level:"AA",addedIn:"2.2",en301549Clause:"9.3.3.8",section508:"not-in-scope-2.2",adaTitleIII:"gray-area",eaa:"gray-area",aoda:"not-in-scope-2.2"},{wcagId:"4.1.1",title:"Parsing",level:"A",addedIn:"2.0",en301549Clause:"9.4.1.1",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope",notes:"OBSOLETE in WCAG 2.2 — modern HTML5 parsing handles legacy use cases."},{wcagId:"4.1.2",title:"Name, Role, Value",level:"A",addedIn:"2.0",en301549Clause:"9.4.1.2",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope",notes:"Second most-cited after 1.4.3 in US Title III complaints."},{wcagId:"4.1.3",title:"Status Messages",level:"AA",addedIn:"2.1",en301549Clause:"9.4.1.3",section508:"not-in-scope-2.1",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"not-in-scope-2.1"}];function Te(e){return e.replace(/[&<>"']/g,t=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[t])}function _u(e){const t=new Set;for(const a of e)for(const n of a.violations)t.add(n.wcagCriterion);return t}function Ce(e,t){return`<span style="display:inline-block; padding:1pt 6pt; border-radius:3pt; font-size:9pt; background:${t}20; border-left:3px solid ${t}; color:#1e293b; font-weight:600">${Te(e)}</span>`}function Uu(e){switch(e){case"in-scope":return Ce("§E207.2 → WCAG 2.0","#059669");case"not-in-scope-2.1":return Ce("Not in scope (WCAG 2.1+)","#94a3b8");case"not-in-scope-2.2":return Ce("Not in scope (WCAG 2.2)","#94a3b8")}}function Wu(e){switch(e){case"de-facto-standard":return Ce("De facto via WCAG AA","#059669");case"gray-area":return Ce("Gray area (new SC)","#f59e0b")}}function Vu(e){switch(e){case"in-scope":return Ce("In scope","#059669");case"product-related":return Ce("Product-related","#0ea5e9");case"gray-area":return Ce("Gray area","#f59e0b")}}function ju(e){switch(e){case"in-scope":return Ce("In scope (WCAG 2.0)","#059669");case"not-in-scope-2.1":return Ce("Not in scope (2.1+)","#94a3b8");case"not-in-scope-2.2":return Ce("Not in scope (2.2)","#94a3b8")}}function Gu(e={}){var d;const t=e.wcagTargetOverride??St,a=e.results?_u(e.results):new Set,n=(((d=e.results)==null?void 0:d.length)??0)>0,o=new Set(ca(t,"AA").map(l=>l.id)),r=Mo.map(l=>{const h=a.has(l.wcagId),m=o.has(l.wcagId),u=h?"background:#fee2e2;":m?"":"background:#f8fafc;",g=h?'<div style="font-size:9pt; color:#b91c1c; font-weight:700; margin-top:2pt">⚠ Violated in this audit</div>':"";return`<tr style="${u}">
1239
+ </html>`)}const Lo=[{wcagId:"1.1.1",title:"Non-text Content",level:"A",addedIn:"2.0",en301549Clause:"9.1.1.1",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"1.2.1",title:"Audio-only and Video-only (Prerecorded)",level:"A",addedIn:"2.0",en301549Clause:"9.1.2.1",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"1.2.2",title:"Captions (Prerecorded)",level:"A",addedIn:"2.0",en301549Clause:"9.1.2.2",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"1.2.3",title:"Audio Description or Media Alternative (Prerecorded)",level:"A",addedIn:"2.0",en301549Clause:"9.1.2.3",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"1.2.4",title:"Captions (Live)",level:"AA",addedIn:"2.0",en301549Clause:"9.1.2.4",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"1.2.5",title:"Audio Description (Prerecorded)",level:"AA",addedIn:"2.0",en301549Clause:"9.1.2.5",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"1.3.1",title:"Info and Relationships",level:"A",addedIn:"2.0",en301549Clause:"9.1.3.1",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"1.3.2",title:"Meaningful Sequence",level:"A",addedIn:"2.0",en301549Clause:"9.1.3.2",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"1.3.3",title:"Sensory Characteristics",level:"A",addedIn:"2.0",en301549Clause:"9.1.3.3",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"1.3.4",title:"Orientation",level:"AA",addedIn:"2.1",en301549Clause:"9.1.3.4",section508:"not-in-scope-2.1",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"not-in-scope-2.1",notes:"WCAG 2.1 addition. Section 508 Refresh is bound to WCAG 2.0."},{wcagId:"1.3.5",title:"Identify Input Purpose",level:"AA",addedIn:"2.1",en301549Clause:"9.1.3.5",section508:"not-in-scope-2.1",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"not-in-scope-2.1"},{wcagId:"1.4.1",title:"Use of Color",level:"A",addedIn:"2.0",en301549Clause:"9.1.4.1",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"1.4.2",title:"Audio Control",level:"A",addedIn:"2.0",en301549Clause:"9.1.4.2",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"1.4.3",title:"Contrast (Minimum)",level:"AA",addedIn:"2.0",en301549Clause:"9.1.4.3",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope",notes:"The single most-cited criterion in US Title III website complaints."},{wcagId:"1.4.4",title:"Resize Text",level:"AA",addedIn:"2.0",en301549Clause:"9.1.4.4",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"1.4.5",title:"Images of Text",level:"AA",addedIn:"2.0",en301549Clause:"9.1.4.5",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"1.4.10",title:"Reflow",level:"AA",addedIn:"2.1",en301549Clause:"9.1.4.10",section508:"not-in-scope-2.1",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"not-in-scope-2.1"},{wcagId:"1.4.11",title:"Non-text Contrast",level:"AA",addedIn:"2.1",en301549Clause:"9.1.4.11",section508:"not-in-scope-2.1",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"not-in-scope-2.1"},{wcagId:"1.4.12",title:"Text Spacing",level:"AA",addedIn:"2.1",en301549Clause:"9.1.4.12",section508:"not-in-scope-2.1",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"not-in-scope-2.1"},{wcagId:"1.4.13",title:"Content on Hover or Focus",level:"AA",addedIn:"2.1",en301549Clause:"9.1.4.13",section508:"not-in-scope-2.1",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"not-in-scope-2.1",notes:"Cited in tooltip + dropdown-related complaints."},{wcagId:"2.1.1",title:"Keyboard",level:"A",addedIn:"2.0",en301549Clause:"9.2.1.1",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"2.1.2",title:"No Keyboard Trap",level:"A",addedIn:"2.0",en301549Clause:"9.2.1.2",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"2.1.4",title:"Character Key Shortcuts",level:"A",addedIn:"2.1",en301549Clause:"9.2.1.4",section508:"not-in-scope-2.1",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"not-in-scope-2.1"},{wcagId:"2.2.1",title:"Timing Adjustable",level:"A",addedIn:"2.0",en301549Clause:"9.2.2.1",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"2.2.2",title:"Pause, Stop, Hide",level:"A",addedIn:"2.0",en301549Clause:"9.2.2.2",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"2.3.1",title:"Three Flashes or Below Threshold",level:"A",addedIn:"2.0",en301549Clause:"9.2.3.1",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"2.4.1",title:"Bypass Blocks",level:"A",addedIn:"2.0",en301549Clause:"9.2.4.1",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"2.4.2",title:"Page Titled",level:"A",addedIn:"2.0",en301549Clause:"9.2.4.2",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"2.4.3",title:"Focus Order",level:"A",addedIn:"2.0",en301549Clause:"9.2.4.3",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"2.4.4",title:"Link Purpose (In Context)",level:"A",addedIn:"2.0",en301549Clause:"9.2.4.4",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"2.4.5",title:"Multiple Ways",level:"AA",addedIn:"2.0",en301549Clause:"9.2.4.5",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"2.4.6",title:"Headings and Labels",level:"AA",addedIn:"2.0",en301549Clause:"9.2.4.6",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"2.4.7",title:"Focus Visible",level:"AA",addedIn:"2.0",en301549Clause:"9.2.4.7",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"2.4.11",title:"Focus Not Obscured (Minimum)",level:"AA",addedIn:"2.2",en301549Clause:"9.2.4.11",section508:"not-in-scope-2.2",adaTitleIII:"gray-area",eaa:"gray-area",aoda:"not-in-scope-2.2",notes:"WCAG 2.2 addition. EN 301 549 may not yet reference; case law not yet settled."},{wcagId:"2.5.1",title:"Pointer Gestures",level:"A",addedIn:"2.1",en301549Clause:"9.2.5.1",section508:"not-in-scope-2.1",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"not-in-scope-2.1"},{wcagId:"2.5.2",title:"Pointer Cancellation",level:"A",addedIn:"2.1",en301549Clause:"9.2.5.2",section508:"not-in-scope-2.1",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"not-in-scope-2.1"},{wcagId:"2.5.3",title:"Label in Name",level:"A",addedIn:"2.1",en301549Clause:"9.2.5.3",section508:"not-in-scope-2.1",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"not-in-scope-2.1"},{wcagId:"2.5.4",title:"Motion Actuation",level:"A",addedIn:"2.1",en301549Clause:"9.2.5.4",section508:"not-in-scope-2.1",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"not-in-scope-2.1"},{wcagId:"2.5.7",title:"Dragging Movements",level:"AA",addedIn:"2.2",en301549Clause:"9.2.5.7",section508:"not-in-scope-2.2",adaTitleIII:"gray-area",eaa:"gray-area",aoda:"not-in-scope-2.2"},{wcagId:"2.5.8",title:"Target Size (Minimum)",level:"AA",addedIn:"2.2",en301549Clause:"9.2.5.8",section508:"not-in-scope-2.2",adaTitleIII:"gray-area",eaa:"gray-area",aoda:"not-in-scope-2.2"},{wcagId:"3.1.1",title:"Language of Page",level:"A",addedIn:"2.0",en301549Clause:"9.3.1.1",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"3.1.2",title:"Language of Parts",level:"AA",addedIn:"2.0",en301549Clause:"9.3.1.2",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"3.2.1",title:"On Focus",level:"A",addedIn:"2.0",en301549Clause:"9.3.2.1",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"3.2.2",title:"On Input",level:"A",addedIn:"2.0",en301549Clause:"9.3.2.2",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"3.2.3",title:"Consistent Navigation",level:"AA",addedIn:"2.0",en301549Clause:"9.3.2.3",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"3.2.4",title:"Consistent Identification",level:"AA",addedIn:"2.0",en301549Clause:"9.3.2.4",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"3.2.6",title:"Consistent Help",level:"A",addedIn:"2.2",en301549Clause:"9.3.2.6",section508:"not-in-scope-2.2",adaTitleIII:"gray-area",eaa:"gray-area",aoda:"not-in-scope-2.2"},{wcagId:"3.3.1",title:"Error Identification",level:"A",addedIn:"2.0",en301549Clause:"9.3.3.1",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"3.3.2",title:"Labels or Instructions",level:"A",addedIn:"2.0",en301549Clause:"9.3.3.2",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"3.3.3",title:"Error Suggestion",level:"AA",addedIn:"2.0",en301549Clause:"9.3.3.3",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"3.3.4",title:"Error Prevention (Legal, Financial, Data)",level:"AA",addedIn:"2.0",en301549Clause:"9.3.3.4",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope"},{wcagId:"3.3.7",title:"Redundant Entry",level:"A",addedIn:"2.2",en301549Clause:"9.3.3.7",section508:"not-in-scope-2.2",adaTitleIII:"gray-area",eaa:"gray-area",aoda:"not-in-scope-2.2"},{wcagId:"3.3.8",title:"Accessible Authentication (Minimum)",level:"AA",addedIn:"2.2",en301549Clause:"9.3.3.8",section508:"not-in-scope-2.2",adaTitleIII:"gray-area",eaa:"gray-area",aoda:"not-in-scope-2.2"},{wcagId:"4.1.1",title:"Parsing",level:"A",addedIn:"2.0",en301549Clause:"9.4.1.1",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope",notes:"OBSOLETE in WCAG 2.2 — modern HTML5 parsing handles legacy use cases."},{wcagId:"4.1.2",title:"Name, Role, Value",level:"A",addedIn:"2.0",en301549Clause:"9.4.1.2",section508:"in-scope",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"in-scope",notes:"Second most-cited after 1.4.3 in US Title III complaints."},{wcagId:"4.1.3",title:"Status Messages",level:"AA",addedIn:"2.1",en301549Clause:"9.4.1.3",section508:"not-in-scope-2.1",adaTitleIII:"de-facto-standard",eaa:"in-scope",aoda:"not-in-scope-2.1"}];function Re(e){return e.replace(/[&<>"']/g,t=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[t])}function zu(e){const t=new Set;for(const a of e)for(const n of a.violations)t.add(n.wcagCriterion);return t}function $e(e,t){return`<span style="display:inline-block; padding:1pt 6pt; border-radius:3pt; font-size:9pt; background:${t}20; border-left:3px solid ${t}; color:#1e293b; font-weight:600">${Re(e)}</span>`}function Ku(e){switch(e){case"in-scope":return $e("§E207.2 → WCAG 2.0","#059669");case"not-in-scope-2.1":return $e("Not in scope (WCAG 2.1+)","#94a3b8");case"not-in-scope-2.2":return $e("Not in scope (WCAG 2.2)","#94a3b8")}}function Yu(e){switch(e){case"de-facto-standard":return $e("De facto via WCAG AA","#059669");case"gray-area":return $e("Gray area (new SC)","#f59e0b")}}function Ju(e){switch(e){case"in-scope":return $e("In scope","#059669");case"product-related":return $e("Product-related","#0ea5e9");case"gray-area":return $e("Gray area","#f59e0b")}}function Xu(e){switch(e){case"in-scope":return $e("In scope (WCAG 2.0)","#059669");case"not-in-scope-2.1":return $e("Not in scope (2.1+)","#94a3b8");case"not-in-scope-2.2":return $e("Not in scope (2.2)","#94a3b8")}}function Qu(e={}){var d;const t=e.wcagTargetOverride??Tt,a=e.results?zu(e.results):new Set,n=(((d=e.results)==null?void 0:d.length)??0)>0,o=new Set(ua(t,"AA").map(l=>l.id)),r=Lo.map(l=>{const h=a.has(l.wcagId),m=o.has(l.wcagId),u=h?"background:#fee2e2;":m?"":"background:#f8fafc;",g=h?'<div style="font-size:9pt; color:#b91c1c; font-weight:700; margin-top:2pt">⚠ Violated in this audit</div>':"";return`<tr style="${u}">
1232
1240
  <td style="padding:5pt 6pt; vertical-align:top; border-bottom:1px solid #e2e8f0">
1233
- <div style="font-family:monospace; font-weight:600">${Te(l.wcagId)}</div>
1234
- <div style="font-size:9pt; color:#475569">${Te(l.level)} · added ${Te(l.addedIn)}</div>
1241
+ <div style="font-family:monospace; font-weight:600">${Re(l.wcagId)}</div>
1242
+ <div style="font-size:9pt; color:#475569">${Re(l.level)} · added ${Re(l.addedIn)}</div>
1235
1243
  </td>
1236
1244
  <td style="padding:5pt 6pt; vertical-align:top; border-bottom:1px solid #e2e8f0">
1237
- <div style="font-weight:600">${Te(l.title)}</div>
1245
+ <div style="font-weight:600">${Re(l.title)}</div>
1238
1246
  ${g}
1239
- ${l.notes?`<div style="font-size:9pt; color:#475569; margin-top:3pt; font-style:italic">${Te(l.notes)}</div>`:""}
1247
+ ${l.notes?`<div style="font-size:9pt; color:#475569; margin-top:3pt; font-style:italic">${Re(l.notes)}</div>`:""}
1240
1248
  </td>
1241
1249
  <td style="padding:5pt 6pt; vertical-align:top; border-bottom:1px solid #e2e8f0; font-family:monospace; font-size:9pt">
1242
- ${l.en301549Clause?`§${Te(l.en301549Clause)}`:'<span style="color:#475569">—</span>'}
1250
+ ${l.en301549Clause?`§${Re(l.en301549Clause)}`:'<span style="color:#475569">—</span>'}
1243
1251
  </td>
1244
- <td style="padding:5pt 6pt; vertical-align:top; border-bottom:1px solid #e2e8f0">${Uu(l.section508)}</td>
1245
- <td style="padding:5pt 6pt; vertical-align:top; border-bottom:1px solid #e2e8f0">${Wu(l.adaTitleIII)}</td>
1246
- <td style="padding:5pt 6pt; vertical-align:top; border-bottom:1px solid #e2e8f0">${Vu(l.eaa)}</td>
1247
- <td style="padding:5pt 6pt; vertical-align:top; border-bottom:1px solid #e2e8f0">${ju(l.aoda)}</td>
1248
- </tr>`}).join(""),i=Mo.filter(l=>a.has(l.wcagId)).length,c=n?`<p class="doc-meta">Annotated with ${i} ${i===1?"criterion":"criteria"} violated in the underlying audit (rows highlighted red).</p>`:'<p class="doc-meta">Standalone reference. Pair with a deposition packet to annotate which criteria were violated in a specific audit.</p>';return ze(`<!doctype html>
1252
+ <td style="padding:5pt 6pt; vertical-align:top; border-bottom:1px solid #e2e8f0">${Ku(l.section508)}</td>
1253
+ <td style="padding:5pt 6pt; vertical-align:top; border-bottom:1px solid #e2e8f0">${Yu(l.adaTitleIII)}</td>
1254
+ <td style="padding:5pt 6pt; vertical-align:top; border-bottom:1px solid #e2e8f0">${Ju(l.eaa)}</td>
1255
+ <td style="padding:5pt 6pt; vertical-align:top; border-bottom:1px solid #e2e8f0">${Xu(l.aoda)}</td>
1256
+ </tr>`}).join(""),i=Lo.filter(l=>a.has(l.wcagId)).length,c=n?`<p class="doc-meta">Annotated with ${i} ${i===1?"criterion":"criteria"} violated in the underlying audit (rows highlighted red).</p>`:'<p class="doc-meta">Standalone reference. Pair with a deposition packet to annotate which criteria were violated in a specific audit.</p>';return Ye(`<!doctype html>
1249
1257
  <html lang="en">
1250
1258
  <head>
1251
1259
  <meta charset="utf-8" />
1252
- <title>wcagcheckr — Conformance Crosswalk (WCAG ${Te(t)} AA → EN / 508 / ADA / EAA / AODA)</title>
1260
+ <title>wcagcheckr — Conformance Crosswalk (WCAG ${Re(t)} AA → EN / 508 / ADA / EAA / AODA)</title>
1253
1261
  <style>
1254
1262
  *, *::before, *::after { box-sizing: border-box; }
1255
1263
  body { font: 10pt/1.5 -apple-system, system-ui, "Segoe UI", sans-serif; color: #0f172a; max-width: 11in; margin: 0.4in auto; padding: 0 0.25in; }
@@ -1277,8 +1285,8 @@ ${c}
1277
1285
  <h2 style="margin-top:0; border:none; padding:0">How to read this document</h2>
1278
1286
  <p style="margin:0 0 8pt 0">Each row is a single WCAG success criterion. The columns show how that criterion maps onto five other accessibility frameworks. The same finding cited as a WCAG violation in one jurisdiction may need to be re-cited under a different framework's clause in another.</p>
1279
1287
  <dl style="margin:0">
1280
- <dt>WCAG ${Te(t)} AA</dt>
1281
- <dd>The dotted SC number (e.g. <code>1.4.3</code>), level, and the WCAG version that introduced it. Criteria de-emphasized when out of scope for the audit's target version (${Te(t)}).</dd>
1288
+ <dt>WCAG ${Re(t)} AA</dt>
1289
+ <dd>The dotted SC number (e.g. <code>1.4.3</code>), level, and the WCAG version that introduced it. Criteria de-emphasized when out of scope for the audit's target version (${Re(t)}).</dd>
1282
1290
  <dt>EN 301 549 v3.2.1</dt>
1283
1291
  <dd>European procurement standard. The <code>§9.x.y.z</code> clause is the direct cross-reference. Cited in EAA-related disputes and EU public-procurement complaints.</dd>
1284
1292
  <dt>Section 508 (36 CFR 1194)</dt>
@@ -1315,36 +1323,36 @@ ${c}
1315
1323
  </section>
1316
1324
 
1317
1325
  </body>
1318
- </html>`)}const Do={critical:4,serious:3,moderate:2,minor:1},li={critical:"🔴",serious:"🟠",moderate:"🟡",minor:"⚪"};function Hu(e,t){return e.length>t?e.slice(0,t-1)+"…":e}function Bu(e){const t=e.split(" > ");return t[t.length-1]??e}function zu(e,t,a){const n=at[t.ruleId],o=[];return o.push(`## Ticket ${e} — ${li[t.impact]} [${t.impact}] ${t.ruleId}`),o.push(""),o.push(`**WCAG:** ${t.wcagCriterion} ${t.wcagLevel}`),o.push(`**Impact:** ${t.impact}`),o.push(`**Selector:** \`${t.target.selector}\``),o.push(`**Page:** ${a}`),t._instances>1&&o.push(`**Instances:** ${t._instances} (this rule fires on ${t._instances} elements; fixing the root cause will resolve all)`),o.push(""),o.push("### What failed"),o.push(""),o.push(t.description),o.push(""),t.target.accessibleName&&(o.push(`Element's current accessible name: \`${t.target.accessibleName}\``),o.push("")),o.push("### Element"),o.push(""),o.push("```html"),o.push(t.target.outerHTML),o.push("```"),o.push(""),t.target.failureSummary&&t.target.failureSummary.trim().length>0&&(o.push("### What axe-core checked"),o.push(""),o.push("```"),o.push(t.target.failureSummary.trim()),o.push("```"),o.push("")),n&&(o.push("### How to fix"),o.push(""),o.push(n.summary),o.push(""),n.snippet&&(o.push("```"+(n.snippetLang??"")),o.push(n.snippet),o.push("```"),o.push(""))),o.push("### Reference"),o.push(""),o.push(`- WCAG ${t.wcagCriterion}: https://www.w3.org/WAI/WCAG21/Understanding/`),o.push(`- axe-core rule: ${t.helpUrl}`),o.push(""),o.join(`
1319
- `)}function qu(e){var d;const t=e.results.flatMap(l=>l.violations),a=new Map;for(const l of t){const h=Oe(l.ruleId,l.target.selector),m=a.get(h);m?m._instances+=1:a.set(h,{...l,_instances:1})}const n=Array.from(a.values()).sort((l,h)=>{const m=Do[h.impact]-Do[l.impact];return m!==0?m:l.ruleId.localeCompare(h.ruleId)}),o=e.pageUrlOverride??((d=e.results[0])==null?void 0:d.pageUrl)??"(unknown)",r=new Date().toISOString(),i=[];i.push("# Accessibility Remediation Tickets"),i.push(""),i.push(`**Generated:** ${r}`),i.push(`**Source URL:** ${o}`),i.push(`**Total tickets:** ${n.length}`),i.push(`**Total violations (with duplicates):** ${t.length}`),i.push(""),i.push("Each ticket below is a self-contained, copy-pasteable section suitable for "),i.push("filing into Linear, Jira, Asana, Trello, GitHub Issues, or any tool that "),i.push("accepts markdown. Tickets are de-duped by logical group, so a list-grid "),i.push("with 50 instances of the same issue produces one ticket noting the count."),i.push(""),i.push("## Index"),i.push(""),i.push("| # | Impact | Rule | WCAG | Element |"),i.push("|---|--------|------|------|---------|"),n.forEach((l,h)=>{const m=Hu(Bu(l.target.selector),32).replace(/\|/g,"\\|");i.push(`| ${h+1} | ${li[l.impact]} ${l.impact} | \`${l.ruleId}\` | ${l.wcagCriterion} ${l.wcagLevel} | \`${m}\` |`)}),i.push(""),i.push("---"),i.push("");const c=n.map((l,h)=>zu(h+1,l,o)).join(`
1326
+ </html>`)}const No={critical:4,serious:3,moderate:2,minor:1},fi={critical:"🔴",serious:"🟠",moderate:"🟡",minor:"⚪"};function Zu(e,t){return e.length>t?e.slice(0,t-1)+"…":e}function eh(e){const t=e.split(" > ");return t[t.length-1]??e}function th(e,t,a){const n=ot[t.ruleId],o=[];return o.push(`## Ticket ${e} — ${fi[t.impact]} [${t.impact}] ${t.ruleId}`),o.push(""),o.push(`**WCAG:** ${t.wcagCriterion} ${t.wcagLevel}`),o.push(`**Impact:** ${t.impact}`),o.push(`**Selector:** \`${t.target.selector}\``),o.push(`**Page:** ${a}`),t._instances>1&&o.push(`**Instances:** ${t._instances} (this rule fires on ${t._instances} elements; fixing the root cause will resolve all)`),o.push(""),o.push("### What failed"),o.push(""),o.push(t.description),o.push(""),t.target.accessibleName&&(o.push(`Element's current accessible name: \`${t.target.accessibleName}\``),o.push("")),o.push("### Element"),o.push(""),o.push("```html"),o.push(t.target.outerHTML),o.push("```"),o.push(""),t.target.failureSummary&&t.target.failureSummary.trim().length>0&&(o.push("### What axe-core checked"),o.push(""),o.push("```"),o.push(t.target.failureSummary.trim()),o.push("```"),o.push("")),n&&(o.push("### How to fix"),o.push(""),o.push(n.summary),o.push(""),n.snippet&&(o.push("```"+(n.snippetLang??"")),o.push(n.snippet),o.push("```"),o.push(""))),o.push("### Reference"),o.push(""),o.push(`- WCAG ${t.wcagCriterion}: https://www.w3.org/WAI/WCAG21/Understanding/`),o.push(`- axe-core rule: ${t.helpUrl}`),o.push(""),o.join(`
1327
+ `)}function ah(e){var d;const t=e.results.flatMap(l=>l.violations),a=new Map;for(const l of t){const h=De(l.ruleId,l.target.selector),m=a.get(h);m?m._instances+=1:a.set(h,{...l,_instances:1})}const n=Array.from(a.values()).sort((l,h)=>{const m=No[h.impact]-No[l.impact];return m!==0?m:l.ruleId.localeCompare(h.ruleId)}),o=e.pageUrlOverride??((d=e.results[0])==null?void 0:d.pageUrl)??"(unknown)",r=new Date().toISOString(),i=[];i.push("# Accessibility Remediation Tickets"),i.push(""),i.push(`**Generated:** ${r}`),i.push(`**Source URL:** ${o}`),i.push(`**Total tickets:** ${n.length}`),i.push(`**Total violations (with duplicates):** ${t.length}`),i.push(""),i.push("Each ticket below is a self-contained, copy-pasteable section suitable for "),i.push("filing into Linear, Jira, Asana, Trello, GitHub Issues, or any tool that "),i.push("accepts markdown. Tickets are de-duped by logical group, so a list-grid "),i.push("with 50 instances of the same issue produces one ticket noting the count."),i.push(""),i.push("## Index"),i.push(""),i.push("| # | Impact | Rule | WCAG | Element |"),i.push("|---|--------|------|------|---------|"),n.forEach((l,h)=>{const m=Zu(eh(l.target.selector),32).replace(/\|/g,"\\|");i.push(`| ${h+1} | ${fi[l.impact]} ${l.impact} | \`${l.ruleId}\` | ${l.wcagCriterion} ${l.wcagLevel} | \`${m}\` |`)}),i.push(""),i.push("---"),i.push("");const c=n.map((l,h)=>th(h+1,l,o)).join(`
1320
1328
  ---
1321
1329
 
1322
1330
  `);return n.length===0?[i.join(`
1323
1331
  `),"_No violations to file. Run an audit first._"].join(`
1324
1332
  `):i.join(`
1325
- `)+c}const Lo={critical:4,serious:3,moderate:2,minor:1};function ce(e){return e.replace(/[&<>"']/g,t=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[t])}function Ku(e,t,a){const n=a??new Set,o=e.flatMap(i=>i.violations).filter(i=>!n.has(i.matchKey)),r=new Map;for(const i of o){const c=`${i.ruleId}::${i.target.selector}::${i.wcagCriterion}`,d=r.get(c);if(d){d._instances+=1;continue}r.set(c,{...i,_instances:1})}return Array.from(r.values()).sort((i,c)=>{const d=Lo[c.impact]-Lo[i.impact];return d!==0?d:i.ruleId.localeCompare(c.ruleId)}).slice(0,t)}function Yu(e,t){switch(e){case"A":return"No critical or serious WCAG violations detected. The audited page is in good shape; manual review still recommended for criteria automation cannot fully evaluate.";case"B":{const a=(t==null?void 0:t.moderate)??0;return t!=null&&t.cappedByCoverage&&a===0?'No critical, serious, or moderate violations detected — but not every applicable WCAG 2.1 AA criterion has been affirmatively evaluated yet. The exact criteria still needing your judgment are listed in the "Criteria still needing your judgment" section below. Each can be marked pass / N/A / fail in the Coverage gaps panel under the grade in the side panel; once every blocker has a verdict, the grade lifts to A.':t!=null&&t.cappedByCoverage&&a>0?`${a} moderate WCAG finding${a===1?"":"s"} detected, AND the audit hasn't yet affirmatively evaluated every applicable AA criterion. The "Top risks" section lists the moderate finding${a===1?"":"s"}; the "Criteria still needing your judgment" section lists the coverage gaps. Both must close before any public conformance claim.`:`${a||"Several"} moderate WCAG finding${a===1?"":"s"} — no critical or serious violations, but areas worth attention before public claims of full conformance.`}case"C":return"At least one serious WCAG violation detected. These typically affect users of assistive technology and should be addressed before the next public release.";case"D":return"Multiple serious WCAG violations detected. The page in its current state is likely to fail real-world testing by users with disabilities and creates measurable legal exposure.";case"F":return"At least one critical WCAG violation detected. Critical-impact issues block users of assistive technology from core functionality and require immediate remediation."}}function Ju(e,t,a=0,n=[]){const o=[];if(e.length===0){if(a>0){const i=n.slice(0,3).join(", "),c=a>3?` (+${a-3} more listed below)`:"";return o.push(`Open the side panel, expand the "${a} criteri${a===1?"on":"a"} need your judgment to lift this to A" panel under the grade, and mark each one pass / N/A / fail with a note. Start with: WCAG ${i}${c}.`),o.push("Schedule a recurring audit (Schedules tab) so any regression is caught within a single cadence window."),o.push("Publish an EAA / WAD accessibility statement (Delta tab → export menu) once coverage is complete."),o}return o.push("Run an IGT manual workflow (keyboard, screen reader) to cover criteria automation cannot fully evaluate."),o.push("Schedule a recurring audit (Schedules tab) so any regression is caught within a single cadence window."),o.push("Publish an EAA / WAD accessibility statement (Delta tab → export menu)."),o}const r=e[0];return o.push(`Resolve the ${r.impact}-impact ${r.ruleId} violation (WCAG ${r.wcagCriterion}). ${r._instances} instance${r._instances===1?"":"s"} on the page; the developer's full findings document has the exact selectors + recommended fixes.`),o.push(t==="F"||t==="D"?"File the top 3 findings into your issue tracker (Delta tab supports GitHub, Jira, GitLab, and Azure DevOps with one click). Block release until critical/serious findings are remediated.":"Add the surfaced findings to the next sprint as accessibility-debt tickets."),o.push("Schedule the page for recurring audit (Schedules tab) so any new violation is caught at the next cadence and surfaces via email or webhook alert."),o}function Xu(e,t={}){const a=e.flatMap($=>$.violations),n=Be(a,t.manualRuns,e,t.acknowledgedMatchKeys,t.interactiveAuditResults,t.walkthroughAcks,t.humanCriterionVerdicts,st(e),t.incompleteResolutions),o=n.score,r=n.combined,i={critical:o.totals.critical,serious:o.totals.serious,moderate:o.totals.moderate,minor:o.totals.minor},c=Ku(e,3,t.acknowledgedMatchKeys),d=new Map(ca("2.1","AA").map($=>[$.id,$.title])),l=new Map,h={untested:0,inconclusive:1,failing:2};function m($,_){for(const P of $){const T=l.get(P);(!T||h[_]>h[T.state])&&l.set(P,{id:P,title:d.get(P)??"(criterion title unavailable)",state:_})}}o.coverage&&(m(o.coverage.untestedCriteria,"untested"),m(o.coverage.inconclusiveCriteria,"inconclusive"),m(o.coverage.failingCriteria,"failing"));const u=Array.from(l.values()).sort(($,_)=>$.id.localeCompare(_.id,void 0,{numeric:!0})),g=Ju(c,r,u.length,u.map($=>$.id)),f=e[0],p=(f==null?void 0:f.pageUrl)??"(unknown)",y=(f==null?void 0:f.axeVersion)??"4.11.4",b=e.length,v=t.asOfDateOverride??(f==null?void 0:f.startedAt)??new Date().toISOString(),w={A:"#059669",B:"#16a34a",C:"#f59e0b",D:"#ea580c",F:"#dc2626"},s=t.orgName?`<p class="org">Prepared for <strong>${ce(t.orgName)}</strong></p>`:"",A=t.logoUrl?`<img src="${ce(t.logoUrl)}" alt="${ce(t.orgName??"Logo")}" class="logo" />`:"",S=c.length===0?'<p class="empty">No findings surfaced. Manual review still recommended for criteria automation cannot evaluate.</p>':`<ol class="risks">${c.map($=>`<li>
1333
+ `)+c}const Po={critical:4,serious:3,moderate:2,minor:1};function le(e){return e.replace(/[&<>"']/g,t=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[t])}function nh(e,t,a){const n=a??new Set,o=e.flatMap(i=>i.violations).filter(i=>!n.has(i.matchKey)),r=new Map;for(const i of o){const c=`${i.ruleId}::${i.target.selector}::${i.wcagCriterion}`,d=r.get(c);if(d){d._instances+=1;continue}r.set(c,{...i,_instances:1})}return Array.from(r.values()).sort((i,c)=>{const d=Po[c.impact]-Po[i.impact];return d!==0?d:i.ruleId.localeCompare(c.ruleId)}).slice(0,t)}function oh(e,t){switch(e){case"A":return"No critical or serious WCAG violations detected. The audited page is in good shape; manual review still recommended for criteria automation cannot fully evaluate.";case"B":{const a=(t==null?void 0:t.moderate)??0;return t!=null&&t.cappedByCoverage&&a===0?'No critical, serious, or moderate violations detected — but not every applicable WCAG 2.1 AA criterion has been affirmatively evaluated yet. The exact criteria still needing your judgment are listed in the "Criteria still needing your judgment" section below. Each can be marked pass / N/A / fail in the Coverage gaps panel under the grade in the side panel; once every blocker has a verdict, the grade lifts to A.':t!=null&&t.cappedByCoverage&&a>0?`${a} moderate WCAG finding${a===1?"":"s"} detected, AND the audit hasn't yet affirmatively evaluated every applicable AA criterion. The "Top risks" section lists the moderate finding${a===1?"":"s"}; the "Criteria still needing your judgment" section lists the coverage gaps. Both must close before any public conformance claim.`:`${a||"Several"} moderate WCAG finding${a===1?"":"s"} — no critical or serious violations, but areas worth attention before public claims of full conformance.`}case"C":return"At least one serious WCAG violation detected. These typically affect users of assistive technology and should be addressed before the next public release.";case"D":return"Multiple serious WCAG violations detected. The page in its current state is likely to fail real-world testing by users with disabilities and creates measurable legal exposure.";case"F":return"At least one critical WCAG violation detected. Critical-impact issues block users of assistive technology from core functionality and require immediate remediation."}}function rh(e,t,a=0,n=[]){const o=[];if(e.length===0){if(a>0){const i=n.slice(0,3).join(", "),c=a>3?` (+${a-3} more listed below)`:"";return o.push(`Open the side panel, expand the "${a} criteri${a===1?"on":"a"} need your judgment to lift this to A" panel under the grade, and mark each one pass / N/A / fail with a note. Start with: WCAG ${i}${c}.`),o.push("Schedule a recurring audit (Schedules tab) so any regression is caught within a single cadence window."),o.push("Publish an EAA / WAD accessibility statement (Delta tab → export menu) once coverage is complete."),o}return o.push("Run an IGT manual workflow (keyboard, screen reader) to cover criteria automation cannot fully evaluate."),o.push("Schedule a recurring audit (Schedules tab) so any regression is caught within a single cadence window."),o.push("Publish an EAA / WAD accessibility statement (Delta tab → export menu)."),o}const r=e[0];return o.push(`Resolve the ${r.impact}-impact ${r.ruleId} violation (WCAG ${r.wcagCriterion}). ${r._instances} instance${r._instances===1?"":"s"} on the page; the developer's full findings document has the exact selectors + recommended fixes.`),o.push(t==="F"||t==="D"?"File the top 3 findings into your issue tracker (Delta tab supports GitHub, Jira, GitLab, and Azure DevOps with one click). Block release until critical/serious findings are remediated.":"Add the surfaced findings to the next sprint as accessibility-debt tickets."),o.push("Schedule the page for recurring audit (Schedules tab) so any new violation is caught at the next cadence and surfaces via email or webhook alert."),o}function ih(e,t={}){const a=e.flatMap($=>$.violations),n=Ke(a,t.manualRuns,e,t.acknowledgedMatchKeys,t.interactiveAuditResults,t.walkthroughAcks,t.humanCriterionVerdicts,lt(e),t.incompleteResolutions),o=n.score,r=n.combined,i={critical:o.totals.critical,serious:o.totals.serious,moderate:o.totals.moderate,minor:o.totals.minor},c=nh(e,3,t.acknowledgedMatchKeys),d=new Map(ua("2.1","AA").map($=>[$.id,$.title])),l=new Map,h={untested:0,inconclusive:1,failing:2};function m($,U){for(const F of $){const T=l.get(F);(!T||h[U]>h[T.state])&&l.set(F,{id:F,title:d.get(F)??"(criterion title unavailable)",state:U})}}o.coverage&&(m(o.coverage.untestedCriteria,"untested"),m(o.coverage.inconclusiveCriteria,"inconclusive"),m(o.coverage.failingCriteria,"failing"));const u=Array.from(l.values()).sort(($,U)=>$.id.localeCompare(U.id,void 0,{numeric:!0})),g=rh(c,r,u.length,u.map($=>$.id)),p=e[0],f=(p==null?void 0:p.pageUrl)??"(unknown)",y=(p==null?void 0:p.axeVersion)??"4.11.4",b=e.length,A=t.asOfDateOverride??(p==null?void 0:p.startedAt)??new Date().toISOString(),w={A:"#059669",B:"#16a34a",C:"#f59e0b",D:"#ea580c",F:"#dc2626"},s=t.orgName?`<p class="org">Prepared for <strong>${le(t.orgName)}</strong></p>`:"",v=t.logoUrl?`<img src="${le(t.logoUrl)}" alt="${le(t.orgName??"Logo")}" class="logo" />`:"",S=c.length===0?'<p class="empty">No findings surfaced. Manual review still recommended for criteria automation cannot evaluate.</p>':`<ol class="risks">${c.map($=>`<li>
1326
1334
  <div class="risk-head">
1327
- <span class="risk-impact risk-${ce($.impact)}">${ce($.impact)}</span>
1328
- <code>${ce($.ruleId)}</code>
1329
- <span class="risk-wcag">WCAG ${ce($.wcagCriterion)} (${ce($.wcagLevel)})</span>
1335
+ <span class="risk-impact risk-${le($.impact)}">${le($.impact)}</span>
1336
+ <code>${le($.ruleId)}</code>
1337
+ <span class="risk-wcag">WCAG ${le($.wcagCriterion)} (${le($.wcagLevel)})</span>
1330
1338
  </div>
1331
- <p class="risk-desc">${ce($.description)}</p>
1332
- <p class="risk-meta">${$._instances} instance${$._instances===1?"":"s"} · selector <code>${ce($.target.selector)}</code></p>
1333
- </li>`).join("")}</ol>`,k=`<ol class="steps">${g.map($=>`<li>${ce($)}</li>`).join("")}</ol>`,I={untested:{label:"no evidence",bg:"#64748b"},inconclusive:{label:"inconclusive",bg:"#a16207"},failing:{label:"failing",bg:"#dc2626"}},L=u.length===0?"":`<section>
1339
+ <p class="risk-desc">${le($.description)}</p>
1340
+ <p class="risk-meta">${$._instances} instance${$._instances===1?"":"s"} · selector <code>${le($.target.selector)}</code></p>
1341
+ </li>`).join("")}</ol>`,k=`<ol class="steps">${g.map($=>`<li>${le($)}</li>`).join("")}</ol>`,x={untested:{label:"no evidence",bg:"#64748b"},inconclusive:{label:"inconclusive",bg:"#a16207"},failing:{label:"failing",bg:"#dc2626"}},L=u.length===0?"":`<section>
1334
1342
  <h2>Criteria still needing your judgment <span style="color:#64748b;font-weight:400;font-size:11pt">(${u.length})</span></h2>
1335
1343
  <p class="risk-meta" style="margin:0 0 8pt">These WCAG 2.1 AA criteria are blocking an A grade — each is currently failing, inconclusive, or not yet affirmatively evaluated (see the status on each below). Open the side panel, expand the "${u.length} criteri${u.length===1?"on":"a"} need your judgment to lift this to A" panel under the grade, and resolve each: fix the failing ones, and record a pass / N/A verdict for the rest.</p>
1336
- <ol class="risks">${u.map($=>{const _=I[$.state];return`<li>
1344
+ <ol class="risks">${u.map($=>{const U=x[$.state];return`<li>
1337
1345
  <div class="risk-head">
1338
- <span class="risk-impact" style="background:${_.bg}">${ce(_.label)}</span>
1339
- <code>WCAG ${ce($.id)}</code>
1340
- <span class="risk-wcag">${ce($.title)}</span>
1346
+ <span class="risk-impact" style="background:${U.bg}">${le(U.label)}</span>
1347
+ <code>WCAG ${le($.id)}</code>
1348
+ <span class="risk-wcag">${le($.title)}</span>
1341
1349
  </div>
1342
1350
  </li>`}).join("")}</ol>
1343
- </section>`;return ze(`<!doctype html>
1351
+ </section>`;return Ye(`<!doctype html>
1344
1352
  <html lang="en">
1345
1353
  <head>
1346
1354
  <meta charset="utf-8" />
1347
- <title>Accessibility Executive Report${t.orgName?" — "+ce(t.orgName):""}</title>
1355
+ <title>Accessibility Executive Report${t.orgName?" — "+le(t.orgName):""}</title>
1348
1356
  <style>
1349
1357
  *, *::before, *::after { box-sizing: border-box; }
1350
1358
  body { font: 11pt/1.55 -apple-system, system-ui, "Segoe UI", Roboto, sans-serif; color: #0f172a; max-width: 7.5in; margin: 0.5in auto; padding: 0 0.25in; }
@@ -1390,25 +1398,25 @@ ${c}
1390
1398
  <header>
1391
1399
  <div>
1392
1400
  <h1>Accessibility Executive Report</h1>
1393
- <p class="meta">As of ${ce(v)}</p>
1401
+ <p class="meta">As of ${le(A)}</p>
1394
1402
  ${s}
1395
1403
  </div>
1396
- ${A}
1404
+ ${v}
1397
1405
  </header>
1398
1406
 
1399
1407
  <section class="hero" aria-label="Audit summary">
1400
1408
  <div class="grade" style="background:${w[r]}">${r}</div>
1401
1409
  <div class="hero-text">
1402
1410
  <strong>${i.critical} critical · ${i.serious} serious · ${i.moderate} moderate · ${i.minor} minor</strong>
1403
- ${ce(Yu(r,{moderate:i.moderate,cappedByCoverage:o.caps.cappedAtBByCoverage}))}
1411
+ ${le(oh(r,{moderate:i.moderate,cappedByCoverage:o.caps.cappedAtBByCoverage}))}
1404
1412
  </div>
1405
1413
  </section>
1406
1414
 
1407
1415
  <section>
1408
1416
  <h2>What was tested</h2>
1409
1417
  <dl class="tested-grid">
1410
- <dt>Page URL</dt><dd><code>${ce(p)}</code></dd>
1411
- <dt>Audit engine</dt><dd>axe-core <code>${ce(y)}</code> + wcagcheckr DOM analyzers</dd>
1418
+ <dt>Page URL</dt><dd><code>${le(f)}</code></dd>
1419
+ <dt>Audit engine</dt><dd>axe-core <code>${le(y)}</code> + wcagcheckr DOM analyzers</dd>
1412
1420
  <dt>States audited</dt><dd>${b} (pseudo-states × themes × directions × breakpoints)</dd>
1413
1421
  <dt>WCAG target</dt><dd>WCAG 2.1 Level AA (configurable; see methodology document)</dd>
1414
1422
  </dl>
@@ -1430,16 +1438,23 @@ ${L}
1430
1438
  This is a one-page executive summary. The developer's full findings document, the deposition packet (forensic-anchored), the audit methodology document, and the cross-jurisdiction conformance crosswalk are available as separate exports.
1431
1439
  </footer>
1432
1440
  </body>
1433
- </html>`)}const Qu=["image-alt","input-image-alt","area-alt","object-alt","role-img-alt","svg-img-alt","alt-text","1.1.1","1.2.1","1.2.2","1.2.3","1.2.4","1.2.5","caption","transcript","audio-desc","1.3.2","meaningful-sequence","reading-order","2.4.6","2.4.4","2.4.9","heading-not-descriptive","generic-link-text","link-in-text-block","identical-links-same-purpose","sensory","color-only","wall-of-text","language-mismatch","uncertain"];function Po(e){if(!e)return"ai-fixable";const t=e.toLowerCase();return Qu.some(a=>t.includes(a))?"human-verify":"ai-fixable"}const Zu=["## How to act on this report","","Each finding is tagged **[AI-fixable]** or **[Human-verify]**:","","- **[AI-fixable]** — the fix is mechanical/code-level (accessible names, label association, ARIA roles/states, contrast, lang, titles, landmarks, heading ORDER). Fix these directly in the code.",`- **[Human-verify]** — the fix needs judgment of content or meaning you cannot confirm from markup (alt-text accuracy, caption/transcript correctness, meaningful reading sequence, whether heading/link wording is genuinely descriptive). You MAY draft a suggestion, but hand it back as a short, plain-language "check this, and here's why" — never jargon homework, and never silently mark it done.`,"",'If a finding lacks the detail needed to act (no selector / no element / no colors), say so explicitly as **"blocked: detail missing from this report"** — that is a report gap to flag, not a failure on your part. Do not guess or hallucinate the missing detail.',""].join(`
1434
- `),Ga=te("ai-compliance-summarizer");async function di(e,t){if(!t.enabled)return null;const a=ue(t);if(!a.ok)return Ga.debug(`AI client unavailable: ${a.reason}`),null;const n=Me(t.costCapUsd);if(!n.canCharge())return null;try{const o=await a.client.generateExecutiveSummary(e);return n.recordCharge(o.costUsd),!o.lead&&!o.body?(Ga.debug("AI summary returned empty"),null):o}catch(o){return Ga.warn("compliance-summary generation failed",o),null}}async function eh(){const e=await chrome.storage.local.get("aiConfig");return ve(e.aiConfig)}function dp(){return Se("AI_COMPLIANCE_SUMMARY_REQUEST",async e=>{const t=await de();if(!le(t,"aiComplianceSummary"))return{type:"AI_COMPLIANCE_SUMMARY_RESPONSE",summary:null,unavailableReason:"AI compliance summary is not available on your current plan."};const a=await eh();if(!a.enabled)return{type:"AI_COMPLIANCE_SUMMARY_RESPONSE",summary:null,unavailableReason:"AI augmentation is disabled in Settings → AI augmentation."};if(!a.apiKey)return{type:"AI_COMPLIANCE_SUMMARY_RESPONSE",summary:null,unavailableReason:"No AI API key configured."};const n=await di(e.input,a);return{type:"AI_COMPLIANCE_SUMMARY_RESPONSE",summary:n,unavailableReason:n?void 0:"AI summary generation returned no result."}})}const Ie="wcagcheckr";function $e(){try{return chrome.runtime.getManifest().version}catch{return"0.0.0"}}function _e(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 ui(e,t){return{schemaVersion:1,generatedAt:new Date().toISOString(),tool:{name:Ie,version:$e()},results:e,delta:t}}function No(e){return e==="critical"||e==="serious"?"error":e==="moderate"?"warning":"note"}function hi(e,t){const a=(t?t.new:e.flatMap(c=>c.violations)).filter(c=>!c.needsReview),n=new Set,o=[];for(const c of a)n.has(c.ruleId)||(n.add(c.ruleId),o.push({id:c.ruleId,shortDescription:{text:c.description||c.ruleId},helpUri:c.helpUrl||void 0,defaultConfiguration:{level:No(c.impact)},properties:{tags:[c.wcagCriterion,`wcag-${c.wcagLevel}`]}}));const r=_e(e),i=a.map(c=>({ruleId:c.ruleId,level:No(c.impact),message:{text:`${c.description}`+(c.target.failureSummary?`
1435
-
1436
- ${c.target.failureSummary}`:"")},locations:[{physicalLocation:{artifactLocation:{uri:r&&/^https?:\/\//.test(r)?`${r}#${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:Ie,version:$e(),informationUri:"https://wcagcheckr.com",rules:o}},results:i}]}}function pi(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;")}function Ue(e){return pi(e).replace(/\n/g,"&#10;").replace(/\r/g,"&#13;")}function fi(e,t){const a=!!t,n=l=>(a?l.violations.filter(h=>t.new.some(m=>m.matchKey===h.matchKey)):l.violations).filter(h=>!h.needsReview),o=e.reduce((l,h)=>l+Math.max(1,h.violations.length),0),r=e.reduce((l,h)=>l+n(h).length,0),i=e.reduce((l,h)=>l+h.durationMs,0)/1e3,c=[];c.push('<?xml version="1.0" encoding="UTF-8"?>');const d=`${Ie} — ${_e(e)}`;c.push(`<testsuites name="${Ue(d)}" tests="${o}" failures="${r}" time="${i.toFixed(3)}">`);for(const l of e){const h=`${l.state.pseudoState}_${l.state.theme}_${l.state.direction}_${l.state.breakpoint.id}`,m=`${l.componentId}::${h}`,u=n(l),g=Math.max(1,l.violations.length);if(c.push(` <testsuite name="${Ue(m)}" tests="${g}" failures="${u.length}" time="${(l.durationMs/1e3).toFixed(3)}">`),l.violations.length===0)c.push(` <testcase classname="${Ue(m)}" name="no-violations" time="${(l.durationMs/1e3).toFixed(3)}" />`);else for(const f of l.violations){const p=u.some(w=>w.matchKey===f.matchKey),y=`classname="${Ue(m)}" name="${Ue(f.ruleId)}" time="0"`;if(!p){c.push(` <testcase ${y} />`);continue}const b=`${f.impact} - ${f.description}`,v=`Selector: ${f.target.selector}
1437
- WCAG: ${f.wcagCriterion} (${f.wcagLevel})
1441
+ </html>`)}const sh=["image-alt","input-image-alt","area-alt","object-alt","role-img-alt","svg-img-alt","alt-text","1.1.1","1.2.1","1.2.2","1.2.3","1.2.4","1.2.5","caption","transcript","audio-desc","1.3.2","meaningful-sequence","reading-order","2.4.6","2.4.4","2.4.9","heading-not-descriptive","generic-link-text","link-in-text-block","identical-links-same-purpose","sensory","color-only","wall-of-text","language-mismatch","uncertain"];function Fo(e){if(!e)return"ai-fixable";const t=e.toLowerCase();return sh.some(a=>t.includes(a))?"human-verify":"ai-fixable"}const ch=["## How to act on this report","","Each finding is tagged **[AI-fixable]** or **[Human-verify]**:","","- **[AI-fixable]** — the fix is mechanical/code-level (accessible names, label association, ARIA roles/states, contrast, lang, titles, landmarks, heading ORDER). Fix these directly in the code.",`- **[Human-verify]** — the fix needs judgment of content or meaning you cannot confirm from markup (alt-text accuracy, caption/transcript correctness, meaningful reading sequence, whether heading/link wording is genuinely descriptive). You MAY draft a suggestion, but hand it back as a short, plain-language "check this, and here's why" — never jargon homework, and never silently mark it done.`,"",'If a finding lacks the detail needed to act (no selector / no element / no colors), say so explicitly as **"blocked: detail missing from this report"** — that is a report gap to flag, not a failure on your part. Do not guess or hallucinate the missing detail.',""].join(`
1442
+ `);function lh(e){if(!e)return null;const t=e.match(/<[a-zA-Z][^>]*>/);return t?t[0]:null}function _o(e,t){return new RegExp(`\\s${t}(\\s|=|>|/)`,"i").test(e)}function Ge(e,t){return e.endsWith("/>")?`${e.slice(0,-2).trimEnd()} ${t} />`:`${e.slice(0,-1).trimEnd()} ${t}>`}function dh(e){return(e.target.tagName||"").toLowerCase()}const uh=new Set(["image-alt","role-img-alt","svg-img-alt","area-alt","input-image-alt","object-alt"]),hh=new Set(["html-has-lang","html-lang-valid","html-xml-lang-mismatch","valid-lang"]);function gi(e,t={}){var o,r,i,c,d,l,h,m,u,g;const a=e.ruleId,n=lh(e.target.outerHTML);if(a==="color-contrast"||a==="color-contrast-enhanced"){const p=e.target.cascadeChain;if(!(!!(p!=null&&p.color||p!=null&&p.backgroundColor)||!!t.suggestedColors))return null;const y=((o=p==null?void 0:p.color)==null?void 0:o.ruleSelector)||((r=p==null?void 0:p.backgroundColor)==null?void 0:r.ruleSelector)||e.target.selector,b=((i=p==null?void 0:p.color)==null?void 0:i.authored)??((c=p==null?void 0:p.color)==null?void 0:c.rendered)??"currentColor",A=((d=p==null?void 0:p.backgroundColor)==null?void 0:d.authored)??((l=p==null?void 0:p.backgroundColor)==null?void 0:l.rendered)??"transparent",w=(h=p==null?void 0:p.color)!=null&&h.authored&&p.color.rendered&&p.color.authored!==p.color.rendered?` /* renders ${p.color.rendered} */`:"",s=a==="color-contrast-enhanced"?"7:1 (normal) / 4.5:1 (large)":"4.5:1 (normal) / 3:1 (large)",v=((m=t.suggestedColors)==null?void 0:m.foreground)??`{{a foreground that reaches ${s} on ${A}}}`,S=((u=t.suggestedColors)==null?void 0:u.background)??A,k=`${y} {
1443
+ color: ${b};${w}
1444
+ background-color: ${A};
1445
+ }`,x=`${y} {
1446
+ color: ${v};
1447
+ background-color: ${S};
1448
+ }`,L=(g=t.suggestedColors)!=null&&g.foreground?"Colors above already pass WCAG AA for this pair — verify they fit the design.":`Pick a color that reaches ${s} contrast; check :hover / :focus too.`;return{current:k,suggested:x,note:L,lang:"css"}}if(!n)return null;if(uh.has(a))return _o(n,"alt")&&a==="image-alt"?null:{current:n,suggested:Ge(n,a==="svg-img-alt"?'role="img" aria-label="{{what the graphic conveys}}"':'alt="{{what the image conveys; use alt=\\"\\" if purely decorative}}"'),note:`Describe the image's MEANING in context, not its appearance. Decorative images take an empty alt="".`,lang:"html"};if(a==="button-name"||a==="input-button-name")return{current:n,suggested:Ge(n,'aria-label="{{the action this control performs}}"'),note:"Prefer visible button text; use aria-label only when the control is icon-only.",lang:"html"};if(a==="link-name")return{current:n,suggested:Ge(n,'aria-label="{{where this link goes}}"'),note:"Prefer descriptive visible link text; aria-label is the fallback for icon/image links.",lang:"html"};if(a==="aria-command-name"||a==="aria-input-field-name"||a==="aria-toggle-field-name")return{current:n,suggested:Ge(n,'aria-label="{{accessible name for this control}}"'),note:"An aria-labelledby pointing at visible text is preferable when such text exists.",lang:"html"};if(a==="label"||a==="select-name"){const p=e.target.attrId,f=n,y=p?`<label for="${p}">{{visible field label}}</label>
1449
+ ${n}`:Ge(n,'aria-label="{{visible field label}}"');return{current:f,suggested:y,note:p?"A programmatically-associated <label for> is preferred over aria-label.":"Add an id to the field and a <label for>, or use aria-label as shown.",lang:"html"}}if(a==="frame-title")return{current:n,suggested:Ge(n,'title="{{purpose of this frame}}"'),lang:"html"};if(hh.has(a)&&dh(e)==="html"){const p=_o(n,"lang")?n.replace(/lang="[^"]*"/i,'lang="en"'):Ge(n,'lang="en"');return{current:n,suggested:p,note:`Set lang to the page's actual primary language (BCP-47), e.g. "en", "es", "fr".`,lang:"html"}}if(a==="meta-viewport"){const p=n.replace(/\s*,?\s*user-scalable\s*=\s*(no|0)/i,"").replace(/\s*,?\s*maximum-scale\s*=\s*(1(\.0)?|0?\.\d+)/i,"");return{current:n,suggested:p,note:"Remove user-scalable=no and any maximum-scale < 2 so users can zoom (WCAG 1.4.4).",lang:"html"}}return a==="tabindex"?{current:n,suggested:n.replace(/tabindex="[1-9]\d*"/i,'tabindex="0"'),note:"Positive tabindex hijacks the tab sequence; use 0 (or restructure the DOM order).",lang:"html"}:null}const Ba=te("ai-compliance-summarizer");async function mi(e,t){if(!t.enabled)return null;const a=he(t);if(!a.ok)return Ba.debug(`AI client unavailable: ${a.reason}`),null;const n=Le(t.costCapUsd);if(!n.canCharge())return null;try{const o=await a.client.generateExecutiveSummary(e);return n.recordCharge(o.costUsd),!o.lead&&!o.body?(Ba.debug("AI summary returned empty"),null):o}catch(o){return Ba.warn("compliance-summary generation failed",o),null}}async function ph(){const e=await chrome.storage.local.get("aiConfig");return ve(e.aiConfig)}function kp(){return Ce("AI_COMPLIANCE_SUMMARY_REQUEST",async e=>{const t=await ue();if(!de(t,"aiComplianceSummary"))return{type:"AI_COMPLIANCE_SUMMARY_RESPONSE",summary:null,unavailableReason:"AI compliance summary is not available on your current plan."};const a=await ph();if(!a.enabled)return{type:"AI_COMPLIANCE_SUMMARY_RESPONSE",summary:null,unavailableReason:"AI augmentation is disabled in Settings → AI augmentation."};if(!a.apiKey)return{type:"AI_COMPLIANCE_SUMMARY_RESPONSE",summary:null,unavailableReason:"No AI API key configured."};const n=await mi(e.input,a);return{type:"AI_COMPLIANCE_SUMMARY_RESPONSE",summary:n,unavailableReason:n?void 0:"AI summary generation returned no result."}})}const xe="wcagcheckr";function Te(){try{return chrome.runtime.getManifest().version}catch{return"0.0.0"}}function We(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 bi(e,t){return{schemaVersion:1,generatedAt:new Date().toISOString(),tool:{name:xe,version:Te()},results:e,delta:t}}function Uo(e){return e==="critical"||e==="serious"?"error":e==="moderate"?"warning":"note"}function yi(e,t){const a=(t?t.new:e.flatMap(c=>c.violations)).filter(c=>!c.needsReview),n=new Set,o=[];for(const c of a)n.has(c.ruleId)||(n.add(c.ruleId),o.push({id:c.ruleId,shortDescription:{text:c.description||c.ruleId},helpUri:c.helpUrl||void 0,defaultConfiguration:{level:Uo(c.impact)},properties:{tags:[c.wcagCriterion,`wcag-${c.wcagLevel}`]}}));const r=We(e),i=a.map(c=>({ruleId:c.ruleId,level:Uo(c.impact),message:{text:`${c.description}`+(c.target.failureSummary?`
1450
+
1451
+ ${c.target.failureSummary}`:"")},locations:[{physicalLocation:{artifactLocation:{uri:r&&/^https?:\/\//.test(r)?`${r}#${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:xe,version:Te(),informationUri:"https://wcagcheckr.com",rules:o}},results:i}]}}function wi(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;")}function Ve(e){return wi(e).replace(/\n/g,"&#10;").replace(/\r/g,"&#13;")}function vi(e,t){const a=!!t,n=l=>(a?l.violations.filter(h=>t.new.some(m=>m.matchKey===h.matchKey)):l.violations).filter(h=>!h.needsReview),o=e.reduce((l,h)=>l+Math.max(1,h.violations.length),0),r=e.reduce((l,h)=>l+n(h).length,0),i=e.reduce((l,h)=>l+h.durationMs,0)/1e3,c=[];c.push('<?xml version="1.0" encoding="UTF-8"?>');const d=`${xe} — ${We(e)}`;c.push(`<testsuites name="${Ve(d)}" tests="${o}" failures="${r}" time="${i.toFixed(3)}">`);for(const l of e){const h=`${l.state.pseudoState}_${l.state.theme}_${l.state.direction}_${l.state.breakpoint.id}`,m=`${l.componentId}::${h}`,u=n(l),g=Math.max(1,l.violations.length);if(c.push(` <testsuite name="${Ve(m)}" tests="${g}" failures="${u.length}" time="${(l.durationMs/1e3).toFixed(3)}">`),l.violations.length===0)c.push(` <testcase classname="${Ve(m)}" name="no-violations" time="${(l.durationMs/1e3).toFixed(3)}" />`);else for(const p of l.violations){const f=u.some(w=>w.matchKey===p.matchKey),y=`classname="${Ve(m)}" name="${Ve(p.ruleId)}" time="0"`;if(!f){c.push(` <testcase ${y} />`);continue}const b=`${p.impact} - ${p.description}`,A=`Selector: ${p.target.selector}
1452
+ WCAG: ${p.wcagCriterion} (${p.wcagLevel})
1438
1453
  State: ${h}
1439
- `+(f.helpUrl?`Help: ${f.helpUrl}
1440
- `:"")+(f.target.failureSummary?`
1441
- ${f.target.failureSummary}`:"");c.push(` <testcase ${y}>`),c.push(` <failure message="${Ue(b)}" type="${Ue(f.ruleId)}">${pi(v)}</failure>`),c.push(" </testcase>")}c.push(" </testsuite>")}return c.push("</testsuites>"),c.join(`
1442
- `)}function E(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}function th(e,t,a){if(e.length===0&&t.length===0)return"";const n=new Map;for(const i of a)n.set(`${i.criterionId}::${i.pageUrl}`,i);const o=e.map((i,c)=>`
1454
+ `+(p.helpUrl?`Help: ${p.helpUrl}
1455
+ `:"")+(p.target.failureSummary?`
1456
+ ${p.target.failureSummary}`:"");c.push(` <testcase ${y}>`),c.push(` <failure message="${Ve(b)}" type="${Ve(p.ruleId)}">${wi(A)}</failure>`),c.push(" </testcase>")}c.push(" </testsuite>")}return c.push("</testsuites>"),c.join(`
1457
+ `)}function E(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}function fh(e,t,a){if(e.length===0&&t.length===0)return"";const n=new Map;for(const i of a)n.set(`${i.criterionId}::${i.pageUrl}`,i);const o=e.map((i,c)=>`
1443
1458
  <tr>
1444
1459
  <td>${c+1}</td>
1445
1460
  <td><span class="impact impact-${E(i.impact)}">${E(i.impact)}</span></td>
@@ -1472,24 +1487,24 @@ ${t.length===0?"":`<h3 style="margin-top:12pt">Human verified (${t.length})</h3>
1472
1487
  <tr><th>#</th><th>Criterion</th><th>Chain</th><th>Verification note</th><th>Recorded at</th><th>Page</th></tr>
1473
1488
  </thead>
1474
1489
  <tbody>${r}</tbody>
1475
- </table>`}`}function Nn(e,t,a){var v,w,s,A,S;const n=((v=e[0])==null?void 0:v.componentId)??"unknown",o=_e(e),r=a??e.reduce((k,I)=>k+I.durationMs,0),i=e.flatMap(k=>k.violations),c=new Map;for(const k of i){const I=`${k.ruleId}::${k.target.selector}`,L=`:${k.currentState.pseudoState} · ${k.currentState.theme} · ${k.currentState.direction}`,$=c.get(I);if($){$._states.includes(L)||$._states.push(L);continue}c.set(I,{...k,_states:[L]})}const d=Array.from(c.values()),l={critical:[],serious:[],moderate:[],minor:[]};for(const k of d)(l[k.impact]??l.moderate).push(k);const h=new Set(((w=t==null?void 0:t.new)==null?void 0:w.map(k=>`${k.ruleId}::${k.target.selector}`))??[]),m=new Set(((s=t==null?void 0:t.persistent)==null?void 0:s.map(k=>`${k.ruleId}::${k.target.selector}`))??[]),u=new Set(((A=t==null?void 0:t.fixed)==null?void 0:A.map(k=>`${k.ruleId}::${k.target.selector}`))??[]);let g=0,f=0;for(const k of d){const I=`${k.ruleId}::${k.target.selector}`;m.has(I)?f++:h.has(I)&&g++}let p=0;for(const k of u)!m.has(k)&&!h.has(k)&&p++;const y=k=>{const I=h.has(`${k.ruleId}::${k.target.selector}`);return`
1476
- <article class="violation impact-${E(k.impact)}${I?" is-new":""}">
1490
+ </table>`}`}function _n(e,t,a){var A,w,s,v,S;const n=((A=e[0])==null?void 0:A.componentId)??"unknown",o=We(e),r=a??e.reduce((k,x)=>k+x.durationMs,0),i=e.flatMap(k=>k.violations),c=new Map;for(const k of i){const x=`${k.ruleId}::${k.target.selector}`,L=`:${k.currentState.pseudoState} · ${k.currentState.theme} · ${k.currentState.direction}`,$=c.get(x);if($){$._states.includes(L)||$._states.push(L);continue}c.set(x,{...k,_states:[L]})}const d=Array.from(c.values()),l={critical:[],serious:[],moderate:[],minor:[]};for(const k of d)(l[k.impact]??l.moderate).push(k);const h=new Set(((w=t==null?void 0:t.new)==null?void 0:w.map(k=>`${k.ruleId}::${k.target.selector}`))??[]),m=new Set(((s=t==null?void 0:t.persistent)==null?void 0:s.map(k=>`${k.ruleId}::${k.target.selector}`))??[]),u=new Set(((v=t==null?void 0:t.fixed)==null?void 0:v.map(k=>`${k.ruleId}::${k.target.selector}`))??[]);let g=0,p=0;for(const k of d){const x=`${k.ruleId}::${k.target.selector}`;m.has(x)?p++:h.has(x)&&g++}let f=0;for(const k of u)!m.has(k)&&!h.has(k)&&f++;const y=k=>{const x=h.has(`${k.ruleId}::${k.target.selector}`);return`
1491
+ <article class="violation impact-${E(k.impact)}${x?" is-new":""}">
1477
1492
  <header>
1478
1493
  <span class="rule">${E(k.ruleId)}</span>
1479
1494
  <span class="impact">${E(k.impact)}</span>
1480
1495
  <span class="wcag">${E(k.wcagCriterion)} ${E(k.wcagLevel)}</span>
1481
- ${I?'<span class="new-badge">NEW vs baseline</span>':""}
1496
+ ${x?'<span class="new-badge">NEW vs baseline</span>':""}
1482
1497
  </header>
1483
1498
  <p class="desc">${E(k.description)}</p>
1484
1499
  <p class="meta"><strong>Selector:</strong> <code>${E(k.target.selector)}</code></p>
1485
1500
  <p class="meta"><strong>Found in state(s):</strong> ${E(k._states.join(", "))}</p>
1486
1501
  <pre class="snippet"><code>${E(k.target.outerHTML)}</code></pre>
1487
1502
  ${k.helpUrl?`<p class="help"><a href="${E(k.helpUrl)}">Reference: ${E(k.helpUrl)}</a></p>`:""}
1488
- </article>`},b=(k,I)=>I.length===0?"":`
1503
+ </article>`},b=(k,x)=>x.length===0?"":`
1489
1504
  <section class="impact-section impact-${E(k)}">
1490
- <h2>${E(k[0].toUpperCase()+k.slice(1))} (${I.length})</h2>
1491
- ${I.map(y).join("")}
1492
- </section>`;return ze(`<!doctype html>
1505
+ <h2>${E(k[0].toUpperCase()+k.slice(1))} (${x.length})</h2>
1506
+ ${x.map(y).join("")}
1507
+ </section>`;return Ye(`<!doctype html>
1493
1508
  <html lang="en">
1494
1509
  <head>
1495
1510
  <meta charset="utf-8" />
@@ -1568,8 +1583,8 @@ ${t.length===0?"":`<h3 style="margin-top:12pt">Human verified (${t.length})</h3>
1568
1583
  <p class="summary"><strong>Unique-violation deltas (rule + selector pairs):</strong></p>
1569
1584
  <p class="summary">
1570
1585
  <span style="color: #dc2626;"><strong>NEW:</strong> ${g}</span>
1571
- <span><strong>Persistent:</strong> ${f}</span>
1572
- <span style="color: #15803d;"><strong>Fixed since baseline:</strong> ${p}</span>
1586
+ <span><strong>Persistent:</strong> ${p}</span>
1587
+ <span style="color: #15803d;"><strong>Fixed since baseline:</strong> ${f}</span>
1573
1588
  </p>
1574
1589
  <p class="summary" style="font-size: 9pt; color: #64748b;">
1575
1590
  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.
@@ -1584,23 +1599,23 @@ ${t.length===0?"":`<h3 style="margin-top:12pt">Human verified (${t.length})</h3>
1584
1599
  ${b("moderate",l.moderate)}
1585
1600
  ${b("minor",l.minor)}
1586
1601
  <footer>
1587
- Generated by ${Ie} v${$e()}. Full audit — all detected violations included, deduped across states. Items flagged "NEW vs baseline" were not present in the most-recent accepted baseline.
1602
+ Generated by ${xe} v${Te()}. Full audit — all detected violations included, deduped across states. Items flagged "NEW vs baseline" were not present in the most-recent accepted baseline.
1588
1603
  </footer>
1589
1604
  </body>
1590
- </html>`)}const gi=[{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:["wcc-audio-needs-transcript","wcc-clean::1.2.1"]},{ref:"1.2.2",title:"Captions (Prerecorded)",level:"A",rules:["video-caption","wcc-video-no-captions","wcc-clean::1.2.2"]},{ref:"1.2.3",title:"Audio Description or Media Alternative (Prerecorded)",level:"A",rules:["wcc-video-no-audio-desc-or-alt","wcc-clean::1.2.3"]},{ref:"1.2.4",title:"Captions (Live)",level:"AA",rules:["wcc-clean::1.2.4"]},{ref:"1.2.5",title:"Audio Description (Prerecorded)",level:"AA",rules:["wcc-video-no-descriptions","wcc-clean::1.2.5"]},{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","aria-pattern-tablist-no-tabs","aria-pattern-tab-no-controls","aria-pattern-tab-broken-controls","aria-pattern-tab-wrong-target-role","aria-pattern-combobox-no-popup","aria-pattern-combobox-broken-popup","aria-pattern-combobox-wrong-popup-role"]},{ref:"1.3.2",title:"Meaningful Sequence",level:"A",rules:[]},{ref:"1.3.3",title:"Sensory Characteristics",level:"A",rules:["wcc-sensory-only-directive","wcc-clean::1.3.3"]},{ref:"1.3.4",title:"Orientation",level:"AA",rules:["css-orientation-lock","wcc-orientation-locked-content","wcc-clean::1.3.4"]},{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","wcc-autoplay-no-controls","wcc-clean::1.4.2"]},{ref:"1.4.3",title:"Contrast (Minimum)",level:"AA",rules:["color-contrast"]},{ref:"1.4.4",title:"Resize Text",level:"AA",rules:["meta-viewport","wcc-viewport-locks-zoom","wcc-clean::1.4.4"]},{ref:"1.4.5",title:"Images of Text",level:"AA",rules:["wcc-image-of-text-candidate","wcc-svg-text-as-content","wcc-clean::1.4.5"]},{ref:"1.4.10",title:"Reflow",level:"AA",rules:["wcc-reflow-horizontal-scroll","wcc-reflow-clean"]},{ref:"1.4.11",title:"Non-text Contrast",level:"AA",rules:[]},{ref:"1.4.12",title:"Text Spacing",level:"AA",rules:["wcc-text-spacing-clip-risk","wcc-clean::1.4.12"]},{ref:"1.4.13",title:"Content on Hover or Focus",level:"AA",rules:["wcc-title-tooltip-no-persistence","wcc-clean::1.4.13"]},{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:["wcc-inline-key-handler","wcc-clean::2.1.4"]},{ref:"2.2.1",title:"Timing Adjustable",level:"A",rules:["meta-refresh","wcc-meta-refresh","wcc-timer-mutates-content","wcc-clean::2.2.1"]},{ref:"2.2.2",title:"Pause, Stop, Hide",level:"A",rules:["blink","marquee","wcc-marquee-blink","wcc-autoplay-no-controls","wcc-long-infinite-animation","wcc-clean::2.2.2"]},{ref:"2.3.1",title:"Three Flashes or Below Threshold",level:"A",rules:["wcc-flash-risk","wcc-clean::2.3.1"]},{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:["wcc-only-one-way-to-find","wcc-clean::2.4.5"]},{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.4.11",title:"Focus Not Obscured (Minimum)",level:"AA",rules:["wcc-sticky-may-obscure-focus","wcc-clean::2.4.11"]},{ref:"2.5.1",title:"Pointer Gestures",level:"A",rules:["wcc-gesture-no-alternative","wcc-clean::2.5.1"]},{ref:"2.5.2",title:"Pointer Cancellation",level:"A",rules:["wcc-down-event-fires-action","wcc-clean::2.5.2"]},{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:["wcc-motion-actuation-no-alt","wcc-clean::2.5.4"]},{ref:"2.5.7",title:"Dragging Movements",level:"AA",rules:["wcc-drag-no-alternative","wcc-clean::2.5.7"]},{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:["wcc-onfocus-context-change","wcc-clean::3.2.1"]},{ref:"3.2.2",title:"On Input",level:"A",rules:["wcc-oninput-context-change","wcc-clean::3.2.2"]},{ref:"3.2.3",title:"Consistent Navigation",level:"AA",rules:["wcc-consistent-navigation"]},{ref:"3.2.4",title:"Consistent Identification",level:"AA",rules:["wcc-consistent-identification"]},{ref:"3.2.6",title:"Consistent Help",level:"A",rules:["wcc-consistent-help"]},{ref:"3.3.1",title:"Error Identification",level:"A",rules:["wcc-form-no-aria-error-pattern","wcc-clean::3.3.1"]},{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:["wcc-form-no-error-suggestion","wcc-clean::3.3.3"]},{ref:"3.3.4",title:"Error Prevention (Legal, Financial, Data)",level:"AA",rules:["wcc-commit-form-no-review-step","wcc-clean::3.3.4"]},{ref:"3.3.7",title:"Redundant Entry",level:"A",rules:["wcc-redundant-entry","wcc-clean::3.3.7"]},{ref:"3.3.8",title:"Accessible Authentication (Minimum)",level:"AA",rules:["wcc-cognitive-auth-challenge","wcc-clean::3.3.8"]},{ref:"4.1.1",title:"Parsing",level:"A",rules:["duplicate-id","duplicate-id-aria","duplicate-id-active","wcc-clean::4.1.1"]},{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","aria-pattern-dialog-no-name","aria-pattern-dialog-no-modal-attr","aria-pattern-tabs-no-selected","aria-pattern-tabs-multiple-selected","aria-pattern-combobox-no-expanded","aria-pattern-combobox-invalid-expanded","aria-pattern-combobox-broken-activedescendant"]},{ref:"4.1.3",title:"Status Messages",level:"AA",rules:["wcc-no-live-regions","wcc-clean::4.1.3"]}];function mi(e,t,a,n,o,r){if(o==="pass")return{conformance:"Supports",hits:["Human verified: pass"]};if(o==="na")return{conformance:"Not Applicable",hits:["Human verified: not applicable"]};if(o==="fail")return{conformance:"Partially Supports",hits:["Human verified: fail"]};if((r??[]).some(m=>m.verdict==="fail"&&(m.wcagCriterion===e.ref||e.rules.includes(m.ruleId))))return{conformance:"Partially Supports",hits:["Pixel-contrast sampler determined a contrast failure"]};const c=n==null?void 0:n.find(m=>m.criterionId===e.ref);if(e.rules.length===0)return c?c.verdict==="pass"?{conformance:"Supports",hits:[]}:c.verdict==="fail"?{conformance:"Partially Supports",hits:["AI walkthrough verdict: fail"]}:{conformance:"Not Evaluated",hits:[]}:{conformance:"Not Applicable",hits:[]};const d=e.rules.filter(m=>t.has(m));if(d.length>0)return{conformance:"Partially Supports",hits:d};if((c==null?void 0:c.verdict)==="fail")return{conformance:"Partially Supports",hits:["AI walkthrough verdict: fail"]};const l=e.rules.some(m=>a.has(m)),h=(c==null?void 0:c.verdict)==="pass";return l||h?{conformance:"Supports",hits:[]}:{conformance:"Not Evaluated",hits:[]}}function bi(e,t,a,n,o,r){var y,b;const i=((y=e[0])==null?void 0:y.componentId)??"unknown",c=_e(e);let d;try{d=new URL(c).host||i}catch{d=i}const l=n??new Set,h=e.flatMap(v=>v.violations).filter(v=>!l.has(v.matchKey)),m=new Set(h.map(v=>v.ruleId)),u=new Map;for(const v of o??[])u.set(v.criterionId,v.verdict);const g=new Set(m);for(const v of e){const w=v.axeRulesEvaluated;if(w){for(const s of w.passed)g.add(s.ruleId);for(const s of w.inapplicable)g.add(s.ruleId);for(const s of w.incomplete)g.add(s.ruleId)}}const f=v=>{const{conformance:w,hits:s}=mi(v,m,g,a,u.get(v.ref),r);return`
1605
+ </html>`)}const Ai=[{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:["wcc-audio-needs-transcript","wcc-clean::1.2.1"]},{ref:"1.2.2",title:"Captions (Prerecorded)",level:"A",rules:["video-caption","wcc-video-no-captions","wcc-clean::1.2.2"]},{ref:"1.2.3",title:"Audio Description or Media Alternative (Prerecorded)",level:"A",rules:["wcc-video-no-audio-desc-or-alt","wcc-clean::1.2.3"]},{ref:"1.2.4",title:"Captions (Live)",level:"AA",rules:["wcc-clean::1.2.4"]},{ref:"1.2.5",title:"Audio Description (Prerecorded)",level:"AA",rules:["wcc-video-no-descriptions","wcc-clean::1.2.5"]},{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","aria-pattern-tablist-no-tabs","aria-pattern-tab-no-controls","aria-pattern-tab-broken-controls","aria-pattern-tab-wrong-target-role","aria-pattern-combobox-no-popup","aria-pattern-combobox-broken-popup","aria-pattern-combobox-wrong-popup-role"]},{ref:"1.3.2",title:"Meaningful Sequence",level:"A",rules:[]},{ref:"1.3.3",title:"Sensory Characteristics",level:"A",rules:["wcc-sensory-only-directive","wcc-clean::1.3.3"]},{ref:"1.3.4",title:"Orientation",level:"AA",rules:["css-orientation-lock","wcc-orientation-locked-content","wcc-clean::1.3.4"]},{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","wcc-autoplay-no-controls","wcc-clean::1.4.2"]},{ref:"1.4.3",title:"Contrast (Minimum)",level:"AA",rules:["color-contrast"]},{ref:"1.4.4",title:"Resize Text",level:"AA",rules:["meta-viewport","wcc-viewport-locks-zoom","wcc-clean::1.4.4"]},{ref:"1.4.5",title:"Images of Text",level:"AA",rules:["wcc-image-of-text-candidate","wcc-svg-text-as-content","wcc-clean::1.4.5"]},{ref:"1.4.10",title:"Reflow",level:"AA",rules:["wcc-reflow-horizontal-scroll","wcc-reflow-clean"]},{ref:"1.4.11",title:"Non-text Contrast",level:"AA",rules:[]},{ref:"1.4.12",title:"Text Spacing",level:"AA",rules:["wcc-text-spacing-clip-risk","wcc-clean::1.4.12"]},{ref:"1.4.13",title:"Content on Hover or Focus",level:"AA",rules:["wcc-title-tooltip-no-persistence","wcc-clean::1.4.13"]},{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:["wcc-inline-key-handler","wcc-clean::2.1.4"]},{ref:"2.2.1",title:"Timing Adjustable",level:"A",rules:["meta-refresh","wcc-meta-refresh","wcc-timer-mutates-content","wcc-clean::2.2.1"]},{ref:"2.2.2",title:"Pause, Stop, Hide",level:"A",rules:["blink","marquee","wcc-marquee-blink","wcc-autoplay-no-controls","wcc-long-infinite-animation","wcc-clean::2.2.2"]},{ref:"2.3.1",title:"Three Flashes or Below Threshold",level:"A",rules:["wcc-flash-risk","wcc-clean::2.3.1"]},{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:["wcc-only-one-way-to-find","wcc-clean::2.4.5"]},{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.4.11",title:"Focus Not Obscured (Minimum)",level:"AA",rules:["wcc-sticky-may-obscure-focus","wcc-clean::2.4.11"]},{ref:"2.5.1",title:"Pointer Gestures",level:"A",rules:["wcc-gesture-no-alternative","wcc-clean::2.5.1"]},{ref:"2.5.2",title:"Pointer Cancellation",level:"A",rules:["wcc-down-event-fires-action","wcc-clean::2.5.2"]},{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:["wcc-motion-actuation-no-alt","wcc-clean::2.5.4"]},{ref:"2.5.7",title:"Dragging Movements",level:"AA",rules:["wcc-drag-no-alternative","wcc-clean::2.5.7"]},{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:["wcc-onfocus-context-change","wcc-clean::3.2.1"]},{ref:"3.2.2",title:"On Input",level:"A",rules:["wcc-oninput-context-change","wcc-clean::3.2.2"]},{ref:"3.2.3",title:"Consistent Navigation",level:"AA",rules:["wcc-consistent-navigation"]},{ref:"3.2.4",title:"Consistent Identification",level:"AA",rules:["wcc-consistent-identification"]},{ref:"3.2.6",title:"Consistent Help",level:"A",rules:["wcc-consistent-help"]},{ref:"3.3.1",title:"Error Identification",level:"A",rules:["wcc-form-no-aria-error-pattern","wcc-clean::3.3.1"]},{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:["wcc-form-no-error-suggestion","wcc-clean::3.3.3"]},{ref:"3.3.4",title:"Error Prevention (Legal, Financial, Data)",level:"AA",rules:["wcc-commit-form-no-review-step","wcc-clean::3.3.4"]},{ref:"3.3.7",title:"Redundant Entry",level:"A",rules:["wcc-redundant-entry","wcc-clean::3.3.7"]},{ref:"3.3.8",title:"Accessible Authentication (Minimum)",level:"AA",rules:["wcc-cognitive-auth-challenge","wcc-clean::3.3.8"]},{ref:"4.1.1",title:"Parsing",level:"A",rules:["duplicate-id","duplicate-id-aria","duplicate-id-active","wcc-clean::4.1.1"]},{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","aria-pattern-dialog-no-name","aria-pattern-dialog-no-modal-attr","aria-pattern-tabs-no-selected","aria-pattern-tabs-multiple-selected","aria-pattern-combobox-no-expanded","aria-pattern-combobox-invalid-expanded","aria-pattern-combobox-broken-activedescendant"]},{ref:"4.1.3",title:"Status Messages",level:"AA",rules:["wcc-no-live-regions","wcc-clean::4.1.3"]}];function ki(e,t,a,n,o,r){if(o==="pass")return{conformance:"Supports",hits:["Human verified: pass"]};if(o==="na")return{conformance:"Not Applicable",hits:["Human verified: not applicable"]};if(o==="fail")return{conformance:"Partially Supports",hits:["Human verified: fail"]};if((r??[]).some(m=>m.verdict==="fail"&&(m.wcagCriterion===e.ref||e.rules.includes(m.ruleId))))return{conformance:"Partially Supports",hits:["Pixel-contrast sampler determined a contrast failure"]};const c=n==null?void 0:n.find(m=>m.criterionId===e.ref);if(e.rules.length===0)return c?c.verdict==="pass"?{conformance:"Supports",hits:[]}:c.verdict==="fail"?{conformance:"Partially Supports",hits:["AI walkthrough verdict: fail"]}:{conformance:"Not Evaluated",hits:[]}:{conformance:"Not Applicable",hits:[]};const d=e.rules.filter(m=>t.has(m));if(d.length>0)return{conformance:"Partially Supports",hits:d};if((c==null?void 0:c.verdict)==="fail")return{conformance:"Partially Supports",hits:["AI walkthrough verdict: fail"]};const l=e.rules.some(m=>a.has(m)),h=(c==null?void 0:c.verdict)==="pass";return l||h?{conformance:"Supports",hits:[]}:{conformance:"Not Evaluated",hits:[]}}function xi(e,t,a,n,o,r){var y,b;const i=((y=e[0])==null?void 0:y.componentId)??"unknown",c=We(e);let d;try{d=new URL(c).host||i}catch{d=i}const l=n??new Set,h=e.flatMap(A=>A.violations).filter(A=>!l.has(A.matchKey)),m=new Set(h.map(A=>A.ruleId)),u=new Map;for(const A of o??[])u.set(A.criterionId,A.verdict);const g=new Set(m);for(const A of e){const w=A.axeRulesEvaluated;if(w){for(const s of w.passed)g.add(s.ruleId);for(const s of w.inapplicable)g.add(s.ruleId);for(const s of w.incomplete)g.add(s.ruleId)}}const p=A=>{const{conformance:w,hits:s}=ki(A,m,g,a,u.get(A.ref),r);return`
1591
1606
  <tr class="${w==="Supports"?"supports":w==="Partially Supports"?"partial":w==="Not Evaluated"?"not-evaluated":"na"}">
1592
- <td class="ref">${E(v.ref)}</td>
1593
- <td class="title">${E(v.title)} <span class="level">(${v.level})</span></td>
1607
+ <td class="ref">${E(A.ref)}</td>
1608
+ <td class="title">${E(A.title)} <span class="level">(${A.level})</span></td>
1594
1609
  <td class="conf">${w}</td>
1595
1610
  <td class="notes">${s.length>0?`Auto-detected violations of: ${s.map(S=>`<code>${E(S)}</code>`).join(", ")}. Manual review recommended.`:w==="Not Applicable"?"No automated rule maps to this criterion. Manual evaluation required.":"No automated violations detected. Manual confirmation recommended for full claim."}</td>
1596
- </tr>`},p=v=>{const w=gi.filter(s=>s.level===v).map(f).join("");return`
1597
- <h2>WCAG 2.1 Level ${v}</h2>
1611
+ </tr>`},f=A=>{const w=Ai.filter(s=>s.level===A).map(p).join("");return`
1612
+ <h2>WCAG 2.1 Level ${A}</h2>
1598
1613
  <table>
1599
1614
  <thead>
1600
1615
  <tr><th>Criterion</th><th>Title</th><th>Conformance</th><th>Notes</th></tr>
1601
1616
  </thead>
1602
1617
  <tbody>${w}</tbody>
1603
- </table>`};return ze(`<!doctype html>
1618
+ </table>`};return Ye(`<!doctype html>
1604
1619
  <html lang="en">
1605
1620
  <head>
1606
1621
  <meta charset="utf-8" />
@@ -1638,34 +1653,36 @@ ${t.length===0?"":`<h3 style="margin-top:12pt">Human verified (${t.length})</h3>
1638
1653
  using this document for procurement, contracting, or legal purposes. Criteria with no
1639
1654
  automated rule are marked "Not Applicable" until manually evaluated.
1640
1655
  </div>
1641
- ${p("A")}
1642
- ${p("AA")}
1656
+ ${f("A")}
1657
+ ${f("AA")}
1643
1658
  <footer style="margin-top: 24pt; padding-top: 8pt; border-top: 1px solid #cbd5e1; font-size: 9pt; color: #64748b;">
1644
- Generated by ${Ie} v${$e()}. Conformance assessed against all violations detected during the audit (not delta-filtered).
1659
+ Generated by ${xe} v${Te()}. Conformance assessed against all violations detected during the audit (not delta-filtered).
1645
1660
  </footer>
1646
1661
  </body>
1647
- </html>`)}function ah(e){const t=[],a=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 n=/\bsrc\s*=\s*"([^"]+)"|\bsrc\s*=\s*'([^']+)'/i.exec(a);n&&t.push({label:"Image source",value:`\`${n[1]??n[2]}\` — if you have vision capability, view this image and propose alt text. Use \`alt=""\` if it's decorative.`});const o=/<title>([^<]+)<\/title>/i.exec(a);o&&t.push({label:"Inline SVG <title>",value:o[1]})}if(e.ruleId==="button-name"||e.ruleId==="empty-button"||e.ruleId==="input-button-name"){const n=/\baria-label\s*=\s*"([^"]+)"/i.exec(a);n&&t.push({label:"Existing aria-label",value:n[1]});const o=/<svg[^>]*>[\s\S]*?<title>([^<]+)<\/title>/i.exec(a);o&&t.push({label:"SVG <title> inside",value:o[1]});const r=/<(?:i|svg|span)[^>]*class\s*=\s*"([^"]*(?:icon|fa-|material-icon)[^"]*)"/i.exec(a);r&&t.push({label:"Icon class hint",value:`\`${r[1]}\` — name suggests intent`})}if(e.ruleId==="link-name"||e.ruleId==="empty-link"){const n=/\bhref\s*=\s*"([^"]+)"|\bhref\s*=\s*'([^']+)'/i.exec(a);n&&t.push({label:"Link href",value:`\`${n[1]??n[2]}\` — destination may suggest the right link text`});const o=/\baria-label\s*=\s*"([^"]+)"/i.exec(a);o&&t.push({label:"Existing aria-label",value:o[1]})}if(e.ruleId==="label"||e.ruleId==="select-name"||e.ruleId==="aria-input-field-name"){const n=/\bname\s*=\s*"([^"]+)"|\bname\s*=\s*'([^']+)'/i.exec(a);n&&t.push({label:"Field name attribute",value:`\`${n[1]??n[2]}\` — often hints at the intended label`});const o=/\bplaceholder\s*=\s*"([^"]+)"|\bplaceholder\s*=\s*'([^']+)'/i.exec(a);o&&t.push({label:"Placeholder text (visible)",value:o[1]??o[2]});const r=/\btype\s*=\s*"([^"]+)"|\btype\s*=\s*'([^']+)'/i.exec(a);r&&t.push({label:"Input type",value:`\`${r[1]??r[2]}\``});const i=/\bautocomplete\s*=\s*"([^"]+)"|\bautocomplete\s*=\s*'([^']+)'/i.exec(a);i&&t.push({label:"Autocomplete",value:i[1]??i[2]})}return t}function nh(e,t){var i;const a=new Set((t??[]).filter(c=>c.verdict==="pass"||c.verdict==="fail").map(c=>`${c.pageUrl}::${c.ruleId}::${c.selector}`)),n=new Map;for(const c of t??[])c.verdict==="uncertain"&&n.set(`${c.pageUrl}::${c.ruleId}::${c.selector}`,c.reasoning);const o=new Set,r=[];for(const c of e){const d=c.pageUrl??c.scope,l=((i=c.axeRulesEvaluated)==null?void 0:i.incomplete)??[];for(const h of l)for(const m of h.elements??[]){const u=`${d}::${h.ruleId}::${m.selector}`;o.has(u)||a.has(u)||(o.add(u),r.push({pageUrl:d,ruleId:h.ruleId,wcagCriterion:h.wcagCriterion,selector:m.selector,failureSummary:m.failureSummary,styles:m.styles,aiAttemptReasoning:n.get(u)}))}}return r}function yi(e,t,a,n,o,r,i,c,d){var X,ye,we,me,C,W,z,Q,he,oe,re;const l=new Set(o??[]),h=new Set(d??[]),m=((X=e[0])==null?void 0:X.componentId)??"unknown",u=_e(e),g=!!(t&&t.baselineSnapshotMeta),f=e.flatMap(x=>x.violations),p=h.size>0?f.filter(x=>h.has(x.matchKey)):[],y=x=>l.has(`violation::${x}`),b=f.filter(x=>!h.has(x.matchKey)&&!y(x.matchKey)),v=new Map;for(const x of b){const B=x.ruleId.startsWith("ai-")?`${x.ruleId}::${x.target.selector}::${(x.target.outerHTML??"").slice(0,200)}`:Oe(x.ruleId,x.target.selector),U=`${x.currentState.pseudoState} · ${x.currentState.theme} · ${x.currentState.direction} · ${x.currentState.breakpoint.id}`,Y=v.get(B);if(Y){Y._states.includes(U)||Y._states.push(U),Y._instanceSelectors.includes(x.target.selector)||Y._instanceSelectors.push(x.target.selector);continue}v.set(B,{...x,_states:[U],_instanceSelectors:[x.target.selector]})}const w=Array.from(v.values()).sort((x,B)=>{const U={critical:0,serious:1,moderate:2,minor:3};return(U[x.impact]??99)-(U[B.impact]??99)}),s=[];s.push("# Accessibility Fix Request"),s.push(""),s.push(`You are a senior accessibility engineer pairing with the user on their codebase. The component below has ${w.length} unique WCAG violation${w.length===1?"":"s"} detected by an automated audit running axe-core ${((ye=e[0])==null?void 0:ye.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.`),s.push(""),s.push("## ⚠ Work contract — read before doing anything else"),s.push(""),s.push(`**You will receive ${w.length} numbered finding${w.length===1?"":"s"} below.** Each one needs an outcome BEFORE you report completion. Do not stop early. Do not batch by impact level. Do not declare done without addressing every numbered item.`),s.push(""),s.push("**For each numbered finding (1 through "+w.length+"), produce one of:**"),s.push(""),s.push("- ✅ **Applied** — the canonical fix from this prompt was applied to the source. Cite the file + line."),s.push("- ⚠ **Blocked** — explicitly state WHY you cannot apply the fix (missing info, ambiguous user content, dependency change required, etc.). One sentence per blocked finding."),s.push("- ❓ **Needs user decision** — you found options but need the user to pick. List the options inline."),s.push(""),s.push(`**The single answer you cannot use: "skipped" or "didn't get to it."** If you ran out of context or time, stop and tell the user — do not silently omit items.`),s.push(""),s.push("**Required final response format** — at the end of your work, produce a completion table:"),s.push(""),s.push("```"),s.push("| # | Rule ID | Status | Notes |"),s.push("|---|---------|--------|-------|"),s.push("| 1 | <rule> | ✅ / ⚠ / ❓ | <file:line OR reason> |"),s.push("| 2 | ... | ... | ... |"),s.push(`| ${w.length} | ... | ... | ... |`),s.push("```"),s.push(""),s.push("**Every row must be present.** If status is ⚠ Blocked or ❓ Needs decision, the Notes column explains specifically what the blocker is so the user can address it on the next pass. The point is that ONE pass through this prompt should produce either a fix or a clear next action for every finding — not a partial sweep that leaves the user re-running the loop."),s.push(""),s.push("---"),s.push("");const A=a&&a.length>0?{runs:a,workflows:Re}:void 0,S=c==null?void 0:c.map(x=>({criterionId:x.criterionId,ruleId:`ai-interactive::${x.criterionId}`,pageUrl:x.pageUrl,verdict:x.verdict,reasoning:x.reasoning})),k=Be(f,A,e,h,S,void 0,void 0,st(e),r),I=k.score;if(s.push("## Context"),s.push(""),s.push(`- **Component / scope:** \`${m}\``),s.push(`- **Audited URL:** ${u}`),s.push(`- **WCAG target:** WCAG ${St} ${hn} (this audit's goal; the receiving fix should bring the page into compliance with this level)`),s.push("- **Source filter:** Full audit — every detected violation"),s.push(`- **Compliance posture:** Grade **${k.combined}** · **${I.risk.toUpperCase()} risk** (${I.totals.critical} critical · ${I.totals.serious} serious · ${I.totals.moderate} moderate · ${I.totals.minor} minor). ${I.risk==="critical"||I.risk==="high"?"Treat as triage priority — these violations represent meaningful lawsuit exposure under ADA Title III, EAA, and EN 301 549.":I.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."}`),I.coverage){const x=I.coverage.untestedCriteria.length;x>0?s.push(`- **WCAG ${I.coverage.targetVersion} ${I.coverage.targetLevel} coverage:** ${I.coverage.evaluated} of ${I.coverage.totalApplicable} criteria evaluated. **Cannot claim ${I.coverage.targetLevel} conformance** until ${x} untested criteria are evaluated via Guided Tests (Pass / Fail / N/A). Untested: ${I.coverage.untestedCriteria.join(", ")}.`):s.push(`- **WCAG ${I.coverage.targetVersion} ${I.coverage.targetLevel} coverage:** ${I.coverage.evaluated} of ${I.coverage.totalApplicable} criteria evaluated. Full coverage — defensible conformance claim possible once all violations below are fixed.`)}g&&t&&s.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.`),s.push(""),s.push("## Before you start (safety)"),s.push(""),s.push("Do these in order before making any code changes:"),s.push(""),s.push("1. **Verify a clean git working tree** (`git status`). If there are uncommitted changes, ask the user whether to commit, stash, or proceed."),s.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`."),s.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>`."),s.push("4. **Show diffs before applying** when your tooling supports it (Cursor / Copilot Chat agents). Never silently overwrite files."),s.push("5. **Do not bump dependencies** to resolve accessibility issues — fixes should be code changes, not package upgrades."),s.push(""),s.push("## Ask me these questions first"),s.push(""),s.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."),s.push(""),s.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."),s.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?"),s.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.'),s.push("4. **Files or directories that are off-limits?** Generated code, vendored libraries, third-party widgets, lockfiles — confirm what you must NOT touch."),s.push("5. **For color-contrast fixes: prefer adjusting foreground or background?** Brand color preservation is usually a stakeholder call, not a developer call."),s.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?"),s.push(""),s.push("## Decision rules (when ambiguity surfaces during fixes)"),s.push(""),s.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.'),s.push("- **State-specific failures get state-specific fixes.** A `:hover`-only contrast failure goes in the `:hover` rule, not the default selector."),s.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."),s.push("- **When the fix would alter visible design** (color shifts, layout changes, content additions), pause and confirm with the user before applying."),s.push(""),s.push("## ⛔ Do not dismiss findings without empirical verification"),s.push(""),s.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.'),s.push(""),s.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.'),s.push(""),s.push("**To dismiss any finding as a false positive, you must do all of the following:**"),s.push(""),s.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)."),s.push("2. **Inspect the flagged elements directly** — DevTools, real `getBoundingClientRect()` coordinates, real DOM source order."),s.push("3. **Compare your empirical observation to the auditor's claim.** Does the visual layout actually contradict the DOM order, or not?"),s.push("4. **If you conclude false positive, document why** — include the empirical evidence (coordinates, screenshots, DOM excerpt) so the user can audit your reasoning."),s.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").`),s.push(""),s.push("**What does NOT count as verification:**"),s.push(""),s.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)"),s.push('- Pattern-matching the finding shape ("this looks like a perfect arithmetic flip therefore it must be an RTL artifact")'),s.push(`- Reading the auditor's false-positive list and concluding your case "looks like one of those"`),s.push(`- Deciding it's "probably fine" because the structure looks reasonable on a quick read`),s.push(""),s.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."),s.push(""),s.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."),s.push(""),s.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."),s.push(""),s.push("## Content-decision rules (alt text, button labels, link text)"),s.push(""),s.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:"),s.push(""),s.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=""`.'),s.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.'),s.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."),s.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."),s.push("5. **Always show your proposed copy before applying** — let the user approve, edit, or reject."),s.push(""),s.push("## ⚡ Verify with Playwright before asking the user"),s.push(""),s.push(`Many WCAG criteria that look like "needs human verification" are actually scriptable — you can write a short Playwright test, run it, observe the result, and resolve the question without bothering the user. **Default to this path.** Only ask the user when Playwright can't reach the answer (subjective copy decisions, content judgment, business-context questions).`),s.push(""),s.push("### Step 1: Check if Playwright is available"),s.push(""),s.push('Look in the project root for `playwright.config.ts` / `playwright.config.js` / a `tests/` or `e2e/` directory using `@playwright/test`. If yes — use it directly. If no — ask the user once: "Want me to add Playwright (`npm install -D @playwright/test && npx playwright install`) so I can verify a few WCAG criteria automatically?" If they decline, fall back to asking per-item.'),s.push(""),s.push("### Step 2: Run targeted smoke tests instead of asking"),s.push(""),s.push("Here are the manual-checklist items below that Playwright CAN verify on the CURRENT PAGE (no multi-page setup needed). For each, write the test, run it, and only escalate to the user if the test result is ambiguous:"),s.push(""),s.push("**Scope disambiguation:** the criteria below are all **single-page** — they describe behavior of the current page. They are NOT multi-page concerns. Cross-page criteria (3.2.3 Consistent Navigation, 3.2.4 Consistent Identification, 3.2.6 Consistent Help) require comparing multiple pages and are handled by wcagcheckr's Site Crawl flow, NOT by Playwright tests here. Don't skip 3.2.1 / 3.2.2 thinking they're multi-page — they're single-page and Playwright-testable."),s.push(""),s.push("| Criterion | Scope | Playwright test |"),s.push("|---|---|---|"),s.push('| **1.4.4 Resize Text (AA)** | single-page | `await page.evaluate(() => document.body.style.zoom = "200%"); await page.screenshot(...); assert no horizontal scroll on critical content blocks` |'),s.push("| **1.4.10 Reflow (AA)** | single-page | `await page.setViewportSize({ width: 320, height: 800 }); await expect(page).toHaveScreenshot(...); assert documentElement.scrollWidth ≤ clientWidth + 10` |"),s.push("| **1.4.12 Text Spacing (AA)** | single-page | inject `* { line-height: 1.5 !important; letter-spacing: 0.12em !important; word-spacing: 0.16em !important; }` via `addStyleTag`, screenshot, check no clipping/overlap |"),s.push('| **1.4.13 Content on Hover or Focus (AA)** | single-page | hover each tooltip-bearing element, then `await page.keyboard.press("Escape")` — verify tooltip closes (dismissible); move mouse INTO tooltip, verify it stays (hoverable); wait 5s without interaction, verify it stays (persistent) |'),s.push("| **2.1.4 Character Key Shortcuts (A)** | single-page | enumerate document keydown handlers; press each single-key shortcut + verify it only fires when expected (focus-dependent or toggleable) |"),s.push("| **2.2.1 Timing Adjustable (A)** | single-page | scan code for setTimeout/setInterval with user-facing impact; assert each has a pause/extend/disable affordance |"),s.push("| **2.2.2 Pause, Stop, Hide (A)** | single-page | find elements with CSS animation / transition / autoplay video; assert each has a visible pause/stop control |"),s.push('| **2.4.5 Multiple Ways (AA)** | single-page | `await page.locator("nav").count() + page.locator("[role=search]").count() + page.locator("a[href*=sitemap]").count() ≥ 2` — checks the current page exposes at least two ways to reach other pages (nav + search, or nav + sitemap link, etc.). |'),s.push("| **2.5.2 Pointer Cancellation (A)** | single-page | for each `<button>` / `[role=button]`: `page.mouse.down()` on it, `page.mouse.move(elsewhere)`, `page.mouse.up()` — assert no action fired (no URL change, no submitted form, no state mutation) |"),s.push("| **3.2.1 On Focus (A)** | **single-page (per-page test, NOT multi-page)** | iterate every focusable on the current page; `.focus()` on each; assert URL unchanged + no modal opened + no form submitted + no scroll jump. This is about each FOCUS event on this page, not about page-to-page navigation. |"),s.push('| **3.2.2 On Input (A)** | **single-page (per-page test, NOT multi-page)** | for each `<select>` / `<input>` / `<textarea>` / `[contenteditable]` on the current page: change value via `.fill()` or `.selectOption()`; assert no URL change, no auto-submit, no scroll jump UNLESS the form clearly warns ("submits on change" UI affordance). |'),s.push('| **4.1.3 Status Messages (AA)** | single-page | trigger a form-validation error or async update; assert the live region announces (`role="status"` or `role="alert"` exists + has text content) |'),s.push(""),s.push("### Step 3: Vision-capable items"),s.push(""),s.push('For items where the test result is "a screenshot to evaluate":'),s.push("- **Color contrast on a gradient/image background:** Playwright screenshot of the element + 50px context, sample foreground + background pixels at the text's bounding box, compute WCAG contrast ratio, compare to 4.5:1 (normal) or 3:1 (large)."),s.push('- **Alt text accuracy:** fetch the `src=` URL, view the image, propose alt text. If the image is a logo/wordmark/decorative, recommend `alt=""`.'),s.push("- **1.4.5 Images of Text:** screenshot the hero / heading / banner; check whether typography is rendered as text (selectable + DOM-visible) or as a raster image."),s.push(""),s.push("### Step 4: When to fall back to asking the user"),s.push(""),s.push("Ask only when:"),s.push(`1. The verification requires subjective judgment Playwright can't make (e.g., "is this alt text accurate to the brand's voice?")`),s.push('2. The criterion needs production-data context (e.g., "do users hit this timeout in practice?")'),s.push("3. The Playwright test itself is ambiguous and the user has context that resolves it"),s.push(""),s.push('**Report Playwright results inline with your fix proposals.** "I ran a smoke test for 1.4.10 at 320px — no horizontal scroll detected; checking this off" beats "Could you verify 1.4.10 manually?" every time.'),s.push(""),s.push("## Determinism + reproducibility"),s.push(""),s.push("**AI judgments are not reproducible by default.** Anthropic, OpenAI, and most LLM APIs run at non-zero sampling temperature unless you explicitly request otherwise — meaning the same prompt + same context can produce different answers on different runs. wcagcheckr learned this the hard way (rc.190 → rc.192): consecutive audits on an unchanged page produced different verdicts because the audit's own AI calls ran at default temperature. The fix was `temperature: 0` on every judgment call."),s.push(""),s.push("You face the same risk for any decision you make based on AI judgment rather than empirical observation. Mitigate by:"),s.push(""),s.push('1. **Prefer empirical verification over AI judgment.** If a question can be answered by running Playwright, running axe-core, sampling pixel values, or grepping the codebase — do that, not "I think the screenshot shows…". The Playwright section above lists 11 criteria that can be verified deterministically.'),s.push("2. **For any auxiliary AI calls you make** (vision tasks, classification, summarization), pass `temperature: 0` (Anthropic / OpenAI) or `top_p: 0.01` (other providers). Same screenshot + same prompt should produce the same answer twice in a row — verify before trusting the answer."),s.push('3. **Document AI-judgment decisions in commit messages.** When you DO rely on AI for a subjective call ("I chose `aria-label=\\"Search the catalog\\"` over `\\"Search\\"` because…"), record the reasoning in the commit so the next session — yours or a human\'s — can audit the call.'),s.push("4. **Treat any single AI verdict as one data point, not gospel.** If you ask AI the same question twice and get different answers, that's the variance showing. Stick with the FIRST answer you committed to OR cross-check with Playwright; don't flip-flop based on subsequent re-rolls."),s.push("5. **Re-run the auditor (`wcagcheckr`) after every commit** rather than asking AI \"is this fixed?\" — the auditor's output is the source of truth for whether a fix landed, not the fixer AI's self-assessment."),s.push(""),s.push("Sampling variance is the silent enemy of compliance automation. Treat it as a constraint to design around, not noise to ignore."),s.push(""),s.push("## Constraints"),s.push(""),s.push("- Don't fix violations that aren't in this list (no scope creep)."),s.push(`- Don't reformat or reorder unrelated code. No "while I'm here" cleanup.`),s.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.`),s.push("- Don't add new dependencies, configs, or build steps."),s.push("- Add inline comments only where the fix's rationale is non-obvious; otherwise skip comments."),s.push("- Preserve existing functionality and visual design wherever possible."),s.push(""),s.push("## How to use this prompt"),s.push(""),s.push("Paste this into Claude / Cursor / Copilot Chat with your codebase open. The AI should:"),s.push('1. **First**: walk through the "Before you start" + "Ask me these questions" sections with the user.'),s.push("2. **Then**: identify the source files containing each violating element (use the selectors + outerHTML below)."),s.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."),s.push("4. **Finally**: produce one atomic commit per fix, with a clear message."),s.push(""),s.push("## Locating findings in source"),s.push(""),s.push("For each finding below, the most reliable search strategies (try in this order):"),s.push(""),s.push('1. **The accessible name / text content** — works for JSX, Vue templates, Svelte, server-rendered HTML. Search for the visible text in `"quotes"` or as a string literal in component files.'),s.push("2. **A distinctive class name** — e.g., `.cta-primary` → grep `cta-primary` in CSS + component files. BEM-style and Tailwind utility classes are the most stable identifiers."),s.push('3. **A unique data attribute** — `data-testid="checkout-button"`, `data-qa=`, `data-cy=` — these are usually authored deliberately and survive minification.'),s.push("4. **A stable ID** — `#hero-search` → grep `hero-search`. Watch out for IDs that look generated (`#field_3f8a92`)."),s.push('5. **The outerHTML pattern** — fall back when class/id/testid don\'t help. Search for distinctive attribute combinations (e.g., `aria-label="…"` + `class="…"`).'),s.push(""),s.push("**Do not search on these — they're runtime artifacts:**"),s.push("- `:nth-child(N)`, `:nth-of-type(N)` — these are positional, never appear in source"),s.push("- Numeric suffixes that look generated (`-1234`, `__abc123`) — usually from CSS Modules / styled-components hashing"),s.push("- Selector paths with `>` chains — describes a DOM structure, not a source location"),s.push(""),s.push("**For pages built with a framework**, also check:"),s.push("- Storybook stories (`*.stories.tsx`, `*.stories.mdx`) — often the canonical component definition"),s.push("- Shared layout components (`Header.tsx`, `Footer.tsx`, `Layout.tsx`) — most page-level violations live here"),s.push("- Theme / token files (`theme.ts`, `tokens.json`) — color-contrast fixes often patch tokens, not consumers"),s.push(""),s.push("---"),s.push("");const L=(((we=e[0])==null?void 0:we.readingOrderIssues)??[]).filter(x=>!l.has(`reading-order::${x.selector}`)),$=(me=e[0])==null?void 0:me.positionAnalysisCapturedAt,_=$?`${$.breakpoint.width}×${$.breakpoint.height} (${$.breakpoint.label}), ${$.direction.toUpperCase()}, ${$.theme} theme, ${$.pseudoState} pseudo-state`:"(reference state unavailable — findings reflect last matrix state)",P=c==null?void 0:c.some(x=>x.criterionId==="1.3.2");if(L.length>0&&!P){s.push("## Reading-order concerns (DOM ≠ visual order)"),s.push(""),s.push(`_Positions captured at: **${_}**. 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._`),s.push(""),s.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. ${L.length} element${L.length===1?" has":"s have"} a notable gap between their DOM position and their visual position:`),s.push(""),s.push("| DOM index | Visual index | Selector | Text |"),s.push("|---|---|---|---|");for(const x of L){const B=x.textSnippet.replace(/\|/g,"\\|").replace(/\n/g," ");s.push(`| ${x.domIndex} | ${x.visualIndex} | \`${x.selector}\` | ${B} |`)}s.push(""),s.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."),s.push(""),s.push("**False-positive cases — these are the ONLY legitimate reasons to skip a flagged element:**"),s.push(""),s.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.'),s.push('2. **Skip-links** ("Skip to main content") — visually positioned late but DOM-early on purpose.'),s.push("3. **Caption-first patterns** — captions placed before their image in DOM so SR users get context before the alt text."),s.push("4. **Modal / popover triggers** — the trigger button is DOM-near the modal's portal mount point, which is often `<body>` end."),s.push(""),s.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.'),s.push(""),s.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."),s.push(""),s.push("---"),s.push("")}const T=(((C=e[0])==null?void 0:C.tabOrderIssues)??[]).filter(x=>!l.has(`tab-order::${x.selector}`)),N=c==null?void 0:c.some(x=>x.criterionId==="2.1.2"&&x.verdict==="pass");if(T.length>0&&!N){const x=T.filter(U=>U.flag==="visual"||U.flag==="both").length,B=T.filter(U=>U.flag==="tabindex"||U.flag==="both").length;s.push("## Tab-order concerns (keyboard focus sequence ≠ visual order) — WCAG 2.4.3 Focus Order (A)"),s.push(""),s.push(`_Positions captured at: **${_}**. 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._`),s.push(""),s.push(`Keyboard users tab through the page in one order; sighted users scan in another. axe-core does not detect this. ${T.length} focusable element${T.length===1?" has":"s have"} a notable divergence. Of these: ${x} ${x===1?"has":"have"} a visual-vs-tab mismatch (focus jumps around the page); ${B} ${B===1?"is":"are"} caused by positive \`tabindex\` reordering DOM source.`),s.push(""),s.push("| Tab pos | Visual pos | DOM pos | Flag | Selector | Text |"),s.push("|---|---|---|---|---|---|");for(const U of T){const Y=U.textSnippet.replace(/\|/g,"\\|").replace(/\n/g," ");s.push(`| ${U.tabPosition} | ${U.visualPosition} | ${U.domPosition} | ${U.flag} | \`${U.selector}\` | ${Y} |`)}s.push(""),s.push("**How to fix:**"),s.push(""),s.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."),s.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.'),s.push("- **`flag: both`** — both conditions present. Fix the DOM order first, then remove the positive `tabindex`."),s.push(""),s.push("**False-positive cases — these are the ONLY legitimate reasons to skip a flagged element:**"),s.push(""),s.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.'),s.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."),s.push("3. **Dropdown menus / popovers** — a button DOM-far from its portal-rendered menu may produce false positives when the menu is open."),s.push(""),s.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.`),s.push(""),s.push("Surface candidates to the user; ask which are intended; only fix the rest."),s.push(""),s.push("---"),s.push("")}const M=e.flatMap(x=>x.announcements??[]),j=e.flatMap(x=>x.focusEvents??[]).filter(x=>x.isFocusReset);if(M.length>0||j.length>0){if(s.push("## Runtime behavioral observations"),s.push(""),M.length>0){const x=M.filter(U=>U.politeness==="assertive").length;s.push(`**Live-region announcements** (${M.length} total, ${x} 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:`),s.push("");const B=M.slice(0,8);for(const U of B){const Y=U.text.replace(/\|/g,"\\|").replace(/\n/g," ").slice(0,120);s.push(`- \`[${U.politeness}${U.role?`/${U.role}`:""}]\` ${Y}`)}M.length>8&&s.push(`- (and ${M.length-8} more)`),s.push(""),s.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)."),s.push("")}if(j.length>0){s.push(`**Focus resets to body** (${j.length} occurrence${j.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.`),s.push("");for(const x of j.slice(0,5))s.push(`- \`${x.fromSelector??"(none)"}\` → \`${x.toSelector}\``);j.length>5&&s.push(`- (and ${j.length-5} more)`),s.push(""),s.push("Common cause: an element is removed from the DOM (e.g., modal closes) without `.focus()` being called on a sensible destination."),s.push("")}s.push("---"),s.push("")}const O=((W=e[0])==null?void 0:W.undefinedCustomProperties)??[];if(O.length>0){s.push("## Undefined CSS custom properties (cascade root cause)"),s.push(""),s.push(`${O.length} CSS custom propert${O.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.`),s.push(""),s.push("| Custom property | References | First sample sites |"),s.push("|---|---|---|");for(const x of O){const B=x.sampleSites.map(U=>`\`${U.selector} { ${U.property} }\``).join("; ");s.push(`| \`${x.name}\` | ${x.referenceCount} | ${B} |`)}s.push(""),s.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."),s.push(""),s.push("---"),s.push("")}if(a&&a.length>0){s.push("## Manual checks already completed (Guided Tests)"),s.push(""),s.push(`The consultant has run ${a.length} Intelligent Guided Test workflow${a.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.`),s.push(""),s.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."),s.push("");const x=Re;s.push("| Workflow | Status | Notes |"),s.push("|---|---|---|");for(const ee of a){const K=x.find($a=>$a.id===ee.workflowId);if(!K)continue;let H=0,Z=0,Ae=0,Ca=0,Ye=0;for(const $a of K.steps){const ft=ee.steps[$a.id];if(!ft){Ye++;continue}ft.status==="pass"?H++:ft.status==="fail"?Z++:ft.status==="skip"?Ae++:ft.status==="not-applicable"?Ca++:Ye++}const Ci=Z>0?`❌ ${Z} failed`:Ye>0?`⚠ ${Ye} unanswered`:`✓ all ${K.steps.length} answered`,Ot=[`${H}p`,`${Z}f`];Ca>0&&Ot.push(`${Ca} N/A`),Ae>0&&Ot.push(`${Ae} skip`),Ye>0&&Ot.push(`${Ye} unanswered`);const $i=`${Ot.join(" · ")} of ${K.steps.length} steps`;s.push(`| ${K.name} | ${Ci} | ${$i} |`)}s.push("");const B=[];for(const ee of a){const K=x.find(H=>H.id===ee.workflowId);if(K)for(const H of K.steps){const Z=ee.steps[H.id];(Z==null?void 0:Z.status)==="fail"&&B.push({workflow:K.name,stepTitle:H.question,notes:Z.notes})}}if(B.length>0){s.push("**Failed manual checks** (these are real WCAG failures the consultant verified by hand):"),s.push("");for(const ee of B)s.push(`- **${ee.workflow} → ${ee.stepTitle}**${ee.notes?`: ${ee.notes}`:""}`);s.push(""),s.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."),s.push("")}const U=new Set(a.map(ee=>ee.workflowId)),Y=x.filter(ee=>!U.has(ee.id));Y.length>0&&(s.push(`**Not yet manually verified** (${Y.length} workflow${Y.length===1?"":"s"}): `+Y.map(ee=>ee.name).join(", ")+". These cover WCAG criteria automation cannot detect; ask the consultant whether to run them before declaring the audit complete."),s.push("")),s.push("---"),s.push("")}if(n&&n.pagesAudited>1){const x=(()=>{try{return new URL(u).origin}catch{return null}})(),B=(()=>{try{return new URL(n.startUrl).origin}catch{return null}})();if(x&&B&&x===B){const U=n.finishedAt,Y=U?Date.now()-new Date(U).getTime():0,ee=Y<6e4?"just now":Y<36e5?`${Math.round(Y/6e4)} minute${Math.round(Y/6e4)===1?"":"s"} ago`:Y<864e5?`${Math.round(Y/36e5)} hour${Math.round(Y/36e5)===1?"":"s"} ago`:`${Math.round(Y/864e5)} day${Math.round(Y/864e5)===1?"":"s"} ago`;if(s.push("## Site-wide context (from a recent site crawl)"),s.push(""),s.push(`A crawl ${U?`(${ee})`:""} of ${n.pagesAudited} page${n.pagesAudited===1?"":"s"} on this origin produced grade **${n.siteGrade}** site-wide with **${n.totalUniqueViolations}** unique violations. Use this context for WCAG 3.2.3 (Consistent Navigation) and 3.2.4 (Consistent Identification) which require cross-page comparison.`),s.push(""),n.topViolations.length>0){s.push("**Most-frequent violations across the site** (rules to prioritize because they affect multiple pages):"),s.push("");for(const H of n.topViolations.slice(0,6))s.push(`- \`${H.ruleId}\` — ${H.impact} — found on ${H.urlsAffected} page${H.urlsAffected===1?"":"s"} (${H.totalOccurrences} total instances)`);s.push(""),s.push("Fixing one of these rules in shared components / layouts is leverage — one fix resolves N pages."),s.push("")}const K=n.consistencyFindings??[];if(K.length===0&&n.topViolations.some(Z=>Z.ruleId==="wcc-consistency-needs-site-crawl"||Z.ruleId==="wcc-redundant-entry-needs-site-crawl")&&(s.push("**Cross-page consistency status:** the comparators ran across the crawled pages and detected **no real divergences** — primary navigation, brand link, footer help mechanism, and multi-step form fields are all consistent across the sample. The `wcc-consistency-needs-site-crawl` and `wcc-redundant-entry-needs-site-crawl` rule IDs in the violations list are placeholder findings emitted per-page by the single-page analyzer (which can't see other pages); the cross-page comparators downgrade them when the crawl confirms consistency. **Do not chase these on a per-page basis** — they're already cleared at the site level."),s.push("")),K.length>0){s.push("**Cross-page consistency divergences detected:**"),s.push("");for(const H of K){const Z=H.aiVerdict?H.aiVerdict.verdict==="uncertain"?" (AI: uncertain — human verify)":` (AI: confirmed real, confidence ${(H.aiVerdict.confidence*100).toFixed(0)}%)`:"";s.push(`- **${H.wcagCriterion} ${H.impact}**${Z} — ${H.description}`);for(const Ae of H.details.split(`
1648
- `))s.push(` ${Ae}`);(z=H.aiVerdict)!=null&&z.reasoning&&s.push(` AI reasoning: ${H.aiVerdict.reasoning}`),s.push("")}}s.push("**Manual cross-page checks the AI should still verify** (the heuristic comparators above can miss edge cases):"),s.push(""),s.push("- **3.2.3 Consistent Navigation** — primary nav DOM order across pages."),s.push("- **3.2.4 Consistent Identification** — same logo / menu / search / nav-link accessible names across pages."),s.push("- **3.2.6 Consistent Help (WCAG 2.2 A)** — help mechanism position consistency."),s.push("- **3.3.7 Redundant Entry (WCAG 2.2 A)** — multi-step forms re-asking for already-entered information."),s.push(""),s.push("---"),s.push("")}}if(p.length>0){const x=new Map;for(const U of p)x.has(U.matchKey)||x.set(U.matchKey,U);const B=Array.from(x.values());s.push("## Previously verified by human — DO NOT re-flag or attempt to fix"),s.push(""),s.push(`The user manually verified each of the following ${B.length} finding${B.length===1?"":"s"} in the wcagcheckr side panel. They WILL appear in raw axe output on every subsequent audit (axe re-detects them because the underlying DOM hasn't changed), but the user has dispositioned each one as acceptable — typically because an automated rule fired on a legitimate edge case (decorative image with empty alt, contrast that passes APCA but not WCAG 2.0 ratio, etc.). **Do not attempt to "fix" any item in this list.** Treat them as resolved. If you genuinely believe one is misjudged, note your reasoning in a comment for the user to review on their next audit — do NOT modify the markup.`),s.push(""),B.forEach((U,Y)=>{s.push(`${Y+1}. \`${U.ruleId}\` on \`${U.target.selector}\``),U.target.accessibleName&&s.push(` - Accessible name: "${U.target.accessibleName}"`),U.target.textSnippet&&s.push(` - Text: "${U.target.textSnippet.slice(0,120)}"`),s.push(` - WCAG ${U.wcagCriterion} ${U.wcagLevel} · ${U.impact}`)}),s.push(""),s.push("---"),s.push("")}const G=(r??[]).filter(x=>x.verdict==="fail").filter(x=>!h.has(`${x.ruleId}::${x.selector}`));if(G.length>0){const x=new Map;for(const U of G){const Y=`${U.ruleId}::${U.selector}`;x.has(Y)||x.set(Y,U)}const B=Array.from(x.values());s.push("## AI-evaluated fails (axe couldn't determine — AI vision determined FAIL)"),s.push(""),s.push(`axe-core flagged each of the following ${B.length} element${B.length===1?"":"s"} as INCOMPLETE — its automated heuristics couldn't reach a verdict (typically because the background is a gradient, image, or semi-transparent overlay that axe can't pixel-sample). wcagcheckr's AI resolver then evaluated each element via vision + computed-style sampling and determined it is a real **FAIL**. Treat these the same as axe violations: locate the element in source, apply the canonical fix for the rule, and re-audit. The AI's reasoning is included per finding so you don't have to re-derive what was wrong.`),s.push(""),B.forEach((U,Y)=>{const ee=U.wcagCriterion?` — WCAG ${U.wcagCriterion}`:"";s.push(`### ${Y+1}. \`${U.ruleId}\`${ee}`),s.push(""),s.push(`**Selector:** \`${U.selector}\``),s.push(""),s.push(`**AI verdict (FAIL):** ${U.reasoning}`),s.push("");const K=at[U.ruleId];K&&(s.push(`**Recipe:** ${K.summary}`),s.push(""),K.snippet&&(s.push("```"+(K.snippetLang??"")),s.push(K.snippet),s.push("```"),s.push("")))}),s.push("---"),s.push("")}s.push("## Violations to fix"),s.push(""),w.forEach((x,B)=>{const U=at[x.ruleId],Y=x._instanceSelectors.length;if(s.push(`### ${B+1}. \`${x.ruleId}\` — ${x.impact} — WCAG ${x.wcagCriterion} ${x.wcagLevel}${Y>1?` (${Y} instances)`:""}`),s.push(""),s.push(`**Description:** ${x.description}`),s.push(""),Y>1){s.push(`**Affects ${Y} spots — fix at the SOURCE (one CSS rule, one component, one shared class) — DO NOT patch each instance separately:**`);for(const Z of x._instanceSelectors.slice(0,8))s.push(`- \`${Z}\``);x._instanceSelectors.length>8&&s.push(`- (and ${x._instanceSelectors.length-8} more)`),s.push(""),s.push(`> **Find the common source.** ${Y} matches means there's a single component / class / CSS rule producing all of them. Search the codebase for the shared class name OR the JSX/template that renders this element, then patch ONCE there. If you can't find a shared source (the elements are genuinely independent), apply the same fix to each — but flag that to the user as "code smell: this pattern is repeated."`)}else s.push(`**Selector:** \`${x.target.selector}\``);s.push(""),s.push(`**Found in state(s):** ${x._states.join(" / ")}`),s.push("");const ee=ah(x);if(ee.length>0){for(const Z of ee)s.push(`**${Z.label}:** ${Z.value}`);s.push("")}x.target.failureSummary&&(s.push(`**Diagnostic:** ${x.target.failureSummary.replace(/\n+/g," ")}`),s.push("")),x.ai&&(s.push(`**🤖 AI-verified finding** (model: ${x.ai.model}, confidence: ${(x.ai.confidence*100).toFixed(0)}%)`),s.push(`> ${x.ai.reasoning}`),s.push(""));const K=x.target.cascadeChain;if(K&&(K.color||K.backgroundColor)){if(s.push("**CSS cascade chain (authored → rendered):**"),K.color){const Z=K.color.vars.length>0?` (vars: ${K.color.vars.map(Ae=>`\`${Ae}\``).join(", ")})`:"";s.push(`- \`color\`: \`${K.color.authored}\` → \`${K.color.rendered}\`${Z}${K.color.ruleSelector?` — set in rule \`${K.color.ruleSelector}\``:""}`)}if(K.backgroundColor){const Z=K.backgroundColor.vars.length>0?` (vars: ${K.backgroundColor.vars.map(Ae=>`\`${Ae}\``).join(", ")})`:"";s.push(`- \`background-color\`: \`${K.backgroundColor.authored}\` → \`${K.backgroundColor.rendered}\`${Z}${K.backgroundColor.ruleSelector?` — set in rule \`${K.backgroundColor.ruleSelector}\``:""}`)}s.push("")}const H=i==null?void 0:i[x.matchKey];if(H&&H.suggestions.length>0){s.push(`**AI color suggestions** (cached from in-UI tooling — ${H.suggestions.length} candidates passing WCAG):`);for(const Z of H.suggestions)s.push(`- foreground \`${Z.foreground}\` on background \`${Z.background}\` → ${Z.contrast.toFixed(2)}:1 — ${Z.rationale}`);s.push(`> AI reasoning: ${H.reasoning}`),s.push("")}s.push("**Element HTML:**"),s.push("```html"),s.push(x.target.outerHTML),s.push("```"),s.push(""),U?(s.push(`**Recipe:** ${U.summary}`),U.snippet&&(s.push(""),s.push("```"+(U.snippetLang??"")),s.push(U.snippet),s.push("```"))):s.push(`**Recipe:** No curated recipe for this rule — apply the standard WCAG ${x.wcagCriterion} guidance.`),x.helpUrl&&(s.push(""),s.push(`**More info:** ${x.helpUrl}`)),s.push(""),s.push("---"),s.push("")});const R=nh(e,r);if(R.length>0){s.push("## Items axe couldn't determine — needs human review"),s.push(""),s.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."),s.push("");for(const x of R)s.push(`### \`${x.ruleId}\` on \`${x.selector}\``),s.push(""),s.push(`- **WCAG criterion:** ${x.wcagCriterion}`),s.push(`- **Page URL:** ${x.pageUrl}`),x.failureSummary&&s.push(`- **axe diagnostic:** ${x.failureSummary}`),(Q=x.styles)!=null&&Q.foreground&&s.push(`- **Foreground:** \`${x.styles.foreground}\``),(he=x.styles)!=null&&he.background&&s.push(`- **Background:** \`${x.styles.background}\``),(oe=x.styles)!=null&&oe.fontSize&&s.push(`- **Font size:** ${x.styles.fontSize}px, weight ${x.styles.fontWeight??"unknown"}`),(re=x.styles)!=null&&re.textSample&&s.push(`- **Text sample:** ${JSON.stringify(x.styles.textSample)}`),x.aiAttemptReasoning&&(s.push(""),s.push("**Our AI resolver already attempted this element and returned uncertain.** Its reasoning:"),s.push(""),s.push(`> ${x.aiAttemptReasoning.replace(/\n/g,`
1649
- > `)}`),s.push(""),s.push("**Suggested next step:** if you have stronger vision capability than our resolver, re-evaluate with a fresh screenshot. Otherwise the user must verify this one manually.")),s.push(""),s.push("---"),s.push("")}s.push("## Manual verification checklist — content-meaning criteria"),s.push(""),s.push(`wcagcheckr's three layers (axe-core, DOM analyzers, AI walkthroughs) cover every WCAG 2.1 AA criterion structurally. What automation CANNOT verify is content meaning: whether your caption text actually matches the audio, whether alt text accurately describes the image, whether sensory cues ("click the red button") have an alternative non-visual phrasing. **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).`),s.push(""),s.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.'),s.push("");const V=new Map;if(c)for(const x of c)V.set(x.criterionId,x.verdict);function J(x,B){const U=V.get(x);return U==="pass"?`- [x] **${x}** — ✓ AI walkthrough verified pass during audit (see "AI keyboard walkthroughs" section). No manual verification needed.`:U==="fail"?`- [x] **${x}** — ✗ AI walkthrough FAILED during audit (see "AI keyboard walkthroughs" section for the fix). Confirm the code fix is applied and re-audit.`:U==="uncertain"?`- [ ] **${x}** — AI walkthrough was uncertain. ${B}`:`- [ ] **${x}** — ${B}`}return s.push("### Perceivable (audit-already-verified items pre-checked)"),s.push(""),s.push(J("1.3.2 Meaningful Sequence (A)","verify DOM source order matches the visual / reading order. Screen readers read the DOM; CSS reordering does NOT affect what the SR hears.")),s.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."),s.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.'),s.push("- [ ] **1.2.3 Audio Description / Media Alternative Prerecorded (A)** — prerecorded video has either audio description or a full text alternative."),s.push("- [ ] **1.2.4 Captions Live (AA)** — live audio content (livestream, real-time meeting) has captions. Usually N/A for static sites."),s.push("- [ ] **1.2.5 Audio Description Prerecorded (AA)** — prerecorded video has audio description for visual-only information."),s.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."),s.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."),s.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."),s.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)."),s.push(J("1.4.11 Non-text Contrast (AA)","every UI component (button border, form input border, icon, focus indicator) and graphical object has at least 3:1 contrast against adjacent colors. Inspect every interactive control + state indicator visually.")),s.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."),s.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)."),s.push(""),s.push("### Operable (audit-already-verified items pre-checked)"),s.push(""),s.push(J("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.")),s.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.'),s.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.'),s.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."),s.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."),s.push(J("2.4.3 Focus Order (A)","spot-check by tabbing through the page: focus visits elements in a logical order (matches visual / semantic flow).")),s.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."),s.push(J("2.4.7 Focus Visible (AA)","every focusable element shows a visible focus indicator. Tab through the page and verify focus is always visually obvious.")),s.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."),s.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."),s.push(J("2.5.3 Label in Name (A)",`every interactive control's accessible name contains its visible text. Test with speech recognition: "click [visible text]" should activate the correct control.`)),s.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."),s.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."),s.push(""),s.push("### Understandable"),s.push(""),s.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."),s.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."),n&&n.pagesAudited>1?(s.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."),s.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).")):(s.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."),s.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.')),s.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."),s.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."),s.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."),s.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)."),s.push(""),s.push("### Robust"),s.push(""),M.length>0||j.length>0?s.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."):s.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.'),s.push(""),s.push("### Manual checks STILL TO DO"),s.push(""),s.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).`),s.push(""),s.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.'),s.push(""),s.push("---"),s.push(""),s.push("## When you're done — verification"),s.push(""),s.push("Before reporting completion, do all of the following:"),s.push(""),s.push(`1. **Confirm all ${w.length} violation${w.length===1?"":"s"} above are addressed.** Walk back through the list with the user, ticking each one off.`),s.push("2. **Re-run the auditor** (or whatever method we agreed in question 6 above). Verify zero NEW violations were introduced by the fixes."),s.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."`),s.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."),s.push("5. **Commit log review** — surface the list of commits you produced. Each should be atomic and revertable."),s.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."),s.push(""),s.push("## Success criteria"),s.push(""),s.push("- All "+w.length+" violation"+(w.length===1?"":"s")+" above are resolved or explicitly deferred with reason."),s.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.'),s.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.)'),s.push("- No new violations introduced."),s.push("- State-specific behavior preserved (`:hover`, `:focus`, dark-mode, RTL, breakpoints)."),s.push("- One atomic commit per fix; clean revertable history."),s.push("- No unrelated code reformatted, no dependencies bumped."),s.push("- Diff is reviewable in under 10 minutes per fix."),s.push(""),s.push(`_Generated by ${Ie} v${$e()}._`),s.join(`
1650
- `)}const oh=5;function rh(e){const t=new Map;let a=0;for(const n of e){if(!n.screenshotDataUrl||n.violations.length===0)continue;const o=`${n.state.pseudoState} · ${n.state.theme} · ${n.state.direction} · ${n.state.breakpoint.id}`,r=o;t.has(r)||t.set(r,{stateLabel:o,screenshotDataUrl:n.screenshotDataUrl,violations:[]});const i=t.get(r);for(const c of n.violations)a++,i.violations.push({...c,_idx:a})}return Array.from(t.values()).sort((n,o)=>o.violations.length-n.violations.length).slice(0,oh)}const Fo=1280,_o=800,ih={low:"Low risk",moderate:"Moderate risk",high:"High risk",critical:"Critical risk"},sh={low:"#d1fae5",moderate:"#fef3c7",high:"#fed7aa",critical:"#fecaca"},ch={low:"#065f46",moderate:"#92400e",high:"#9a3412",critical:"#991b1b"},lh={A:"#10b981",B:"#84cc16",C:"#eab308",D:"#f97316",F:"#dc2626"},dh={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 wi(e,t,a,n,o,r,i){return Rt(e,t,"evidence",a,n,o,r,i)}function vi(e,t,a,n,o,r){return Ii(e,"letter",t,a,n,o,r)}function Ai(e,t,a,n,o,r){return Ii(e,"report",t,a,n,o,r)}function ki(e,t,a,n,o,r){var f;const i=e.flatMap(p=>p.violations),c=ma(i,void 0,e,a,t,n,o,st(e),r),d=_e(e),l=new Map;for(const p of i){const y=Oe(p.ruleId,p.target.selector);l.has(y)||l.set(y,p)}const h=Array.from(l.values()).sort((p,y)=>{const b={critical:0,serious:1,moderate:2,minor:3};return(b[p.impact]??99)-(b[y.impact]??99)}),m=new Map;for(const p of h){const y=m.get(p.ruleId)??[];y.push(p),m.set(p.ruleId,y)}const u=[];if(u.push("# Help me fix accessibility issues on my website"),u.push(""),u.push("You are an accessibility advisor helping mea 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."),u.push(""),u.push("## What I need from you"),u.push(""),u.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."),u.push("2. **Then, for each issue below, tell me clearly:**"),u.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"),u.push(" - Or whether **my developer needs to do this** (code-level fix) and what to ask them to do"),u.push("3. **Prioritize ruthlessly.** I want to start with the issues that:"),u.push(" - Are most likely to come up in an ADA-related lawsuit (critical-impact items)"),u.push(" - Are easiest for me to fix without involving a developer"),u.push("4. **Don't bury me in jargon.** If you have to use a technical term, explain it in plain English first."),u.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."),u.push(""),u.push("## Audit summary"),u.push(""),u.push(`- **Page audited:** ${d}`),u.push(`- **Lawsuit-target risk:** ${hr[c.risk]} — ${pr[c.risk]}`),u.push(`- **Issues found:** ${c.totals.unique} unique problems (${c.totals.critical} critical · ${c.totals.serious} serious · ${c.totals.moderate} moderate · ${c.totals.minor} minor)`),c.coverage&&c.coverage.untestedCriteria.length>0?u.push(`- **WCAG conformance progress:** ${c.coverage.evaluated} of ${c.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 ${c.coverage.targetVersion} ${c.coverage.targetLevel}, ${c.coverage.untestedCriteria.length} more criteria need human review — see the "Guided Tests" workflows in the auditor side panel for these.`):c.coverage&&c.coverage.untestedCriteria.length===0&&u.push(`- **WCAG conformance:** All ${c.coverage.totalApplicable} applicable WCAG ${c.coverage.targetVersion} ${c.coverage.targetLevel} criteria have been evaluated. Fixing the issues below will produce a defensible conformance claim.`),u.push(""),u.push("## The issues, in plain language"),u.push(""),m.size===0)return u.push("No issues were detected across our three evaluation layers (axe-core rule-based checks, our DOM analyzers covering criteria axe doesn't reach, and AI walkthroughs that drive real keyboard input and judge screenshots). For a formal conformance claim, spot-check the content-meaning criteria — automation can detect missing captions, not whether your caption text accurately matches the audio."),u.join(`
1651
- `);let g=1;for(const[p,y]of m.entries()){const b=(f=y.find(s=>s.target.opacityContext))==null?void 0:f.target.opacityContext,v=Sn(p,b),w=y[0].impact;u.push(`### ${g}. [${w.toUpperCase()}] ${v.whatsWrong}`),u.push(""),u.push(`**Why this matters.** ${v.whyItMatters}`),u.push(""),u.push(`**Plain-language fix guidance.** ${v.howToFix}`),u.push(""),u.push(`**Where on the page (${y.length} ${y.length===1?"spot":"spots"}):**`);for(const s of y.slice(0,8))u.push(`- \`${s.target.selector}\``);y.length>8&&u.push(`- (and ${y.length-8} more spots)`),u.push(""),u.push(`*Technical reference for your developer:* axe-core rule \`${p}\` — full docs at https://dequeuniversity.com/rules/axe/4.11/${p}`),u.push(""),g++}return u.push("---"),u.push(""),u.push("## How to use this output"),u.push(""),u.push("Walk through this list with me one issue at a time. For each:"),u.push("1. Help me understand the issue with a real example of who it affects."),u.push("2. Tell me clearly if I can fix it myself, and if so, walk me through it for my platform."),u.push("3. If I need a developer, give me a short paragraph I can paste into an email or message to them."),u.push("4. Help me prioritize — I have limited time and want to fix the riskiest things first."),u.push(""),u.push("Start by asking me which platform I use."),u.join(`
1652
- `)}function Ii(e,t,a,n,o,r,i){const c=e.flatMap(b=>b.violations),d=ma(c,void 0,e,n,a,o,r,st(e),i),l=_e(e),h=new Date,m=new Map;for(const b of c){const v=Oe(b.ruleId,b.target.selector);m.has(v)||m.set(v,b)}const u=Array.from(m.values()).sort((b,v)=>{const w={critical:0,serious:1,moderate:2,minor:3};return(w[b.impact]??99)-(w[v.impact]??99)}),g=new Map;for(const b of u){const v=g.get(b.ruleId)??[];v.push(b),g.set(b.ruleId,v)}const f=Array.from(g.entries()).map(([b,v])=>{var I;const w=(I=v.find(L=>L.target.opacityContext))==null?void 0:I.target.opacityContext,s=Sn(b,w),A=v[0].impact,S=v.slice(0,8).map(L=>`<code>${E(L.target.selector)}</code>`).join(", "),k=v.length>8?` <em>(and ${v.length-8} more)</em>`:"";return`
1662
+ </html>`)}function gh(e){const t=[],a=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 n=/\bsrc\s*=\s*"([^"]+)"|\bsrc\s*=\s*'([^']+)'/i.exec(a);n&&t.push({label:"Image source",value:`\`${n[1]??n[2]}\` — if you have vision capability, view this image and propose alt text. Use \`alt=""\` if it's decorative.`});const o=/<title>([^<]+)<\/title>/i.exec(a);o&&t.push({label:"Inline SVG <title>",value:o[1]})}if(e.ruleId==="button-name"||e.ruleId==="empty-button"||e.ruleId==="input-button-name"){const n=/\baria-label\s*=\s*"([^"]+)"/i.exec(a);n&&t.push({label:"Existing aria-label",value:n[1]});const o=/<svg[^>]*>[\s\S]*?<title>([^<]+)<\/title>/i.exec(a);o&&t.push({label:"SVG <title> inside",value:o[1]});const r=/<(?:i|svg|span)[^>]*class\s*=\s*"([^"]*(?:icon|fa-|material-icon)[^"]*)"/i.exec(a);r&&t.push({label:"Icon class hint",value:`\`${r[1]}\` — name suggests intent`})}if(e.ruleId==="link-name"||e.ruleId==="empty-link"){const n=/\bhref\s*=\s*"([^"]+)"|\bhref\s*=\s*'([^']+)'/i.exec(a);n&&t.push({label:"Link href",value:`\`${n[1]??n[2]}\` — destination may suggest the right link text`});const o=/\baria-label\s*=\s*"([^"]+)"/i.exec(a);o&&t.push({label:"Existing aria-label",value:o[1]})}if(e.ruleId==="label"||e.ruleId==="select-name"||e.ruleId==="aria-input-field-name"){const n=/\bname\s*=\s*"([^"]+)"|\bname\s*=\s*'([^']+)'/i.exec(a);n&&t.push({label:"Field name attribute",value:`\`${n[1]??n[2]}\` — often hints at the intended label`});const o=/\bplaceholder\s*=\s*"([^"]+)"|\bplaceholder\s*=\s*'([^']+)'/i.exec(a);o&&t.push({label:"Placeholder text (visible)",value:o[1]??o[2]});const r=/\btype\s*=\s*"([^"]+)"|\btype\s*=\s*'([^']+)'/i.exec(a);r&&t.push({label:"Input type",value:`\`${r[1]??r[2]}\``});const i=/\bautocomplete\s*=\s*"([^"]+)"|\bautocomplete\s*=\s*'([^']+)'/i.exec(a);i&&t.push({label:"Autocomplete",value:i[1]??i[2]})}return t}function mh(e,t){var i;const a=new Set((t??[]).filter(c=>c.verdict==="pass"||c.verdict==="fail").map(c=>`${c.pageUrl}::${c.ruleId}::${c.selector}`)),n=new Map;for(const c of t??[])c.verdict==="uncertain"&&n.set(`${c.pageUrl}::${c.ruleId}::${c.selector}`,c.reasoning);const o=new Set,r=[];for(const c of e){const d=c.pageUrl??c.scope,l=((i=c.axeRulesEvaluated)==null?void 0:i.incomplete)??[];for(const h of l)for(const m of h.elements??[]){const u=`${d}::${h.ruleId}::${m.selector}`;o.has(u)||a.has(u)||(o.add(u),r.push({pageUrl:d,ruleId:h.ruleId,wcagCriterion:h.wcagCriterion,selector:m.selector,failureSummary:m.failureSummary,styles:m.styles,aiAttemptReasoning:n.get(u)}))}}return r}function Ii(e,t,a,n,o,r,i,c,d){var Q,we,Ae,pe,Se,C,W,z,Z,ie,ee;const l=new Set(o??[]),h=new Set(d??[]),m=((Q=e[0])==null?void 0:Q.componentId)??"unknown",u=We(e),g=!!(t&&t.baselineSnapshotMeta),p=e.flatMap(I=>I.violations),f=h.size>0?p.filter(I=>h.has(I.matchKey)):[],y=I=>l.has(`violation::${I}`),b=p.filter(I=>!h.has(I.matchKey)&&!y(I.matchKey)),A=new Map;for(const I of b){const B=I.ruleId.startsWith("ai-")?`${I.ruleId}::${I.target.selector}::${(I.target.outerHTML??"").slice(0,200)}`:De(I.ruleId,I.target.selector),P=`${I.currentState.pseudoState} · ${I.currentState.theme} · ${I.currentState.direction} · ${I.currentState.breakpoint.id}`,Y=A.get(B);if(Y){Y._states.includes(P)||Y._states.push(P),Y._instanceSelectors.includes(I.target.selector)||Y._instanceSelectors.push(I.target.selector);continue}A.set(B,{...I,_states:[P],_instanceSelectors:[I.target.selector]})}const w=Array.from(A.values()).sort((I,B)=>{const P={critical:0,serious:1,moderate:2,minor:3};return(P[I.impact]??99)-(P[B.impact]??99)}),s=[];s.push("# Accessibility Fix Request"),s.push(""),s.push("> **Your job is to APPLY the fixes in this report — not to evaluate, second-guess, or re-litigate whether the findings are correct.** This report is the output of a real automated audit (axe-core run in a live browser across many UI states) plus AI verification; the triage already happened upstream. Do not debate severity or skip items you personally doubt. Read each finding, apply the canonical fix, and report the outcome per the Work contract below. A genuine blocker or a real user-decision is the only acceptable non-fix — and those are reported explicitly per the contract, never silently dropped."),s.push(""),s.push(`You are a senior accessibility engineer pairing with the user on their codebase. The component below has ${w.length} unique WCAG violation${w.length===1?"":"s"} detected by an automated audit running axe-core ${((we=e[0])==null?void 0:we.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.`),s.push(""),s.push("## [!] Work contract — read before doing anything else"),s.push(""),s.push(`**You will receive ${w.length} numbered finding${w.length===1?"":"s"} below.** Each one needs an outcome BEFORE you report completion. Do not stop early. Do not batch by impact level. Do not declare done without addressing every numbered item.`),s.push(""),s.push("**For each numbered finding (1 through "+w.length+"), produce one of:**"),s.push(""),s.push("- [DONE] **Applied** — the canonical fix from this prompt was applied to the source. Cite the file + line."),s.push("- [BLOCKED] **Blocked** — explicitly state WHY you cannot apply the fix (missing info, ambiguous user content, dependency change required, etc.). One sentence per blocked finding."),s.push("- [NEEDS-DECISION] **Needs user decision** — you found options but need the user to pick. List the options inline."),s.push(""),s.push(`**The single answer you cannot use: "skipped" or "didn't get to it."** If you ran out of context or time, stop and tell the user — do not silently omit items.`),s.push(""),s.push("**Required final response format** — at the end of your work, produce a completion table:"),s.push(""),s.push("```"),s.push("| # | Rule ID | Status | Notes |"),s.push("|---|---------|--------|-------|"),s.push("| 1 | <rule> | DONE / BLOCKED / NEEDS-DECISION | <file:line OR reason> |"),s.push("| 2 | ... | ... | ... |"),s.push(`| ${w.length} | ... | ... | ... |`),s.push("```"),s.push(""),s.push("**Every row must be present.** If status is BLOCKED or NEEDS-DECISION, the Notes column explains specifically what the blocker is so the user can address it on the next pass. The point is that ONE pass through this prompt should produce either a fix or a clear next action for every finding — not a partial sweep that leaves the user re-running the loop."),s.push(""),s.push("---"),s.push("");const v=a&&a.length>0?{runs:a,workflows:Me}:void 0,S=c==null?void 0:c.map(I=>({criterionId:I.criterionId,ruleId:`ai-interactive::${I.criterionId}`,pageUrl:I.pageUrl,verdict:I.verdict,reasoning:I.reasoning})),k=Ke(p,v,e,h,S,void 0,void 0,lt(e),r),x=k.score;if(s.push("## Context"),s.push(""),s.push(`- **Component / scope:** \`${m}\``),s.push(`- **Audited URL:** ${u}`),s.push(`- **WCAG target:** WCAG ${Tt} ${fn} (this audit's goal; the receiving fix should bring the page into compliance with this level)`),s.push("- **Source filter:** Full audit — every detected violation"),s.push(`- **Compliance posture:** Grade **${k.combined}** · **${x.risk.toUpperCase()} risk** (${x.totals.critical} critical · ${x.totals.serious} serious · ${x.totals.moderate} moderate · ${x.totals.minor} minor). ${x.risk==="critical"||x.risk==="high"?"Treat as triage priority — these violations represent meaningful lawsuit exposure under ADA Title III, EAA, and EN 301 549.":x.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."}`),x.coverage){const I=x.coverage.untestedCriteria.length;I>0?s.push(`- **WCAG ${x.coverage.targetVersion} ${x.coverage.targetLevel} coverage:** ${x.coverage.evaluated} of ${x.coverage.totalApplicable} criteria evaluated. **Cannot claim ${x.coverage.targetLevel} conformance** until ${I} untested criteria are evaluated via Guided Tests (Pass / Fail / N/A). Untested: ${x.coverage.untestedCriteria.join(", ")}.`):s.push(`- **WCAG ${x.coverage.targetVersion} ${x.coverage.targetLevel} coverage:** ${x.coverage.evaluated} of ${x.coverage.totalApplicable} criteria evaluated. Full coverage — defensible conformance claim possible once all violations below are fixed.`)}g&&t&&s.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.`),s.push(""),s.push("## Before you start (safety)"),s.push(""),s.push("Do these in order before making any code changes:"),s.push(""),s.push("1. **Verify a clean git working tree** (`git status`). If there are uncommitted changes, ask the user whether to commit, stash, or proceed."),s.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`."),s.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>`."),s.push("4. **Show diffs before applying** when your tooling supports it (Cursor / Copilot Chat agents). Never silently overwrite files."),s.push("5. **Do not bump dependencies** to resolve accessibility issues — fixes should be code changes, not package upgrades."),s.push(""),s.push("## Ask me these questions first"),s.push(""),s.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."),s.push(""),s.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."),s.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?"),s.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.'),s.push("4. **Files or directories that are off-limits?** Generated code, vendored libraries, third-party widgets, lockfiles — confirm what you must NOT touch."),s.push("5. **For color-contrast fixes: prefer adjusting foreground or background?** Brand color preservation is usually a stakeholder call, not a developer call."),s.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?"),s.push(""),s.push("## Decision rules (when ambiguity surfaces during fixes)"),s.push(""),s.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.'),s.push("- **State-specific failures get state-specific fixes.** A `:hover`-only contrast failure goes in the `:hover` rule, not the default selector."),s.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."),s.push("- **When the fix would alter visible design** (color shifts, layout changes, content additions), pause and confirm with the user before applying."),s.push(""),s.push("## [STOP] Do not dismiss findings without empirical verification"),s.push(""),s.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.'),s.push(""),s.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.'),s.push(""),s.push("**To dismiss any finding as a false positive, you must do all of the following:**"),s.push(""),s.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)."),s.push("2. **Inspect the flagged elements directly** — DevTools, real `getBoundingClientRect()` coordinates, real DOM source order."),s.push("3. **Compare your empirical observation to the auditor's claim.** Does the visual layout actually contradict the DOM order, or not?"),s.push("4. **If you conclude false positive, document why** — include the empirical evidence (coordinates, screenshots, DOM excerpt) so the user can audit your reasoning."),s.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").`),s.push(""),s.push("**What does NOT count as verification:**"),s.push(""),s.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)"),s.push('- Pattern-matching the finding shape ("this looks like a perfect arithmetic flip therefore it must be an RTL artifact")'),s.push(`- Reading the auditor's false-positive list and concluding your case "looks like one of those"`),s.push(`- Deciding it's "probably fine" because the structure looks reasonable on a quick read`),s.push(""),s.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."),s.push(""),s.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."),s.push(""),s.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."),s.push(""),s.push("## Content-decision rules (alt text, button labels, link text)"),s.push(""),s.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:"),s.push(""),s.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=""`.'),s.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.'),s.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."),s.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."),s.push("5. **Always show your proposed copy before applying** — let the user approve, edit, or reject."),s.push(""),s.push("## [VERIFY] Verify with Playwright before asking the user"),s.push(""),s.push(`Many WCAG criteria that look like "needs human verification" are actually scriptable — you can write a short Playwright test, run it, observe the result, and resolve the question without bothering the user. **Default to this path.** Only ask the user when Playwright can't reach the answer (subjective copy decisions, content judgment, business-context questions).`),s.push(""),s.push("### Step 1: Check if Playwright is available"),s.push(""),s.push('Look in the project root for `playwright.config.ts` / `playwright.config.js` / a `tests/` or `e2e/` directory using `@playwright/test`. If yes — use it directly. If no — ask the user once: "Want me to add Playwright (`npm install -D @playwright/test && npx playwright install`) so I can verify a few WCAG criteria automatically?" If they decline, fall back to asking per-item.'),s.push(""),s.push("### Step 2: Run targeted smoke tests instead of asking"),s.push(""),s.push("Here are the manual-checklist items below that Playwright CAN verify on the CURRENT PAGE (no multi-page setup needed). For each, write the test, run it, and only escalate to the user if the test result is ambiguous:"),s.push(""),s.push("**Scope disambiguation:** the criteria below are all **single-page** — they describe behavior of the current page. They are NOT multi-page concerns. Cross-page criteria (3.2.3 Consistent Navigation, 3.2.4 Consistent Identification, 3.2.6 Consistent Help) require comparing multiple pages and are handled by wcagcheckr's Site Crawl flow, NOT by Playwright tests here. Don't skip 3.2.1 / 3.2.2 thinking they're multi-page — they're single-page and Playwright-testable."),s.push(""),s.push("| Criterion | Scope | Playwright test |"),s.push("|---|---|---|"),s.push('| **1.4.4 Resize Text (AA)** | single-page | `await page.evaluate(() => document.body.style.zoom = "200%"); await page.screenshot(...); assert no horizontal scroll on critical content blocks` |'),s.push("| **1.4.10 Reflow (AA)** | single-page | `await page.setViewportSize({ width: 320, height: 800 }); await expect(page).toHaveScreenshot(...); assert documentElement.scrollWidth ≤ clientWidth + 10` |"),s.push("| **1.4.12 Text Spacing (AA)** | single-page | inject `* { line-height: 1.5 !important; letter-spacing: 0.12em !important; word-spacing: 0.16em !important; }` via `addStyleTag`, screenshot, check no clipping/overlap |"),s.push('| **1.4.13 Content on Hover or Focus (AA)** | single-page | hover each tooltip-bearing element, then `await page.keyboard.press("Escape")` — verify tooltip closes (dismissible); move mouse INTO tooltip, verify it stays (hoverable); wait 5s without interaction, verify it stays (persistent) |'),s.push("| **2.1.4 Character Key Shortcuts (A)** | single-page | enumerate document keydown handlers; press each single-key shortcut + verify it only fires when expected (focus-dependent or toggleable) |"),s.push("| **2.2.1 Timing Adjustable (A)** | single-page | scan code for setTimeout/setInterval with user-facing impact; assert each has a pause/extend/disable affordance |"),s.push("| **2.2.2 Pause, Stop, Hide (A)** | single-page | find elements with CSS animation / transition / autoplay video; assert each has a visible pause/stop control |"),s.push('| **2.4.5 Multiple Ways (AA)** | single-page | `await page.locator("nav").count() + page.locator("[role=search]").count() + page.locator("a[href*=sitemap]").count() ≥ 2` — checks the current page exposes at least two ways to reach other pages (nav + search, or nav + sitemap link, etc.). |'),s.push("| **2.5.2 Pointer Cancellation (A)** | single-page | for each `<button>` / `[role=button]`: `page.mouse.down()` on it, `page.mouse.move(elsewhere)`, `page.mouse.up()` — assert no action fired (no URL change, no submitted form, no state mutation) |"),s.push("| **3.2.1 On Focus (A)** | **single-page (per-page test, NOT multi-page)** | iterate every focusable on the current page; `.focus()` on each; assert URL unchanged + no modal opened + no form submitted + no scroll jump. This is about each FOCUS event on this page, not about page-to-page navigation. |"),s.push('| **3.2.2 On Input (A)** | **single-page (per-page test, NOT multi-page)** | for each `<select>` / `<input>` / `<textarea>` / `[contenteditable]` on the current page: change value via `.fill()` or `.selectOption()`; assert no URL change, no auto-submit, no scroll jump UNLESS the form clearly warns ("submits on change" UI affordance). |'),s.push('| **4.1.3 Status Messages (AA)** | single-page | trigger a form-validation error or async update; assert the live region announces (`role="status"` or `role="alert"` exists + has text content) |'),s.push(""),s.push("### Step 3: Vision-capable items"),s.push(""),s.push('For items where the test result is "a screenshot to evaluate":'),s.push("- **Color contrast on a gradient/image background:** Playwright screenshot of the element + 50px context, sample foreground + background pixels at the text's bounding box, compute WCAG contrast ratio, compare to 4.5:1 (normal) or 3:1 (large)."),s.push('- **Alt text accuracy:** fetch the `src=` URL, view the image, propose alt text. If the image is a logo/wordmark/decorative, recommend `alt=""`.'),s.push("- **1.4.5 Images of Text:** screenshot the hero / heading / banner; check whether typography is rendered as text (selectable + DOM-visible) or as a raster image."),s.push(""),s.push("### Step 4: When to fall back to asking the user"),s.push(""),s.push("Ask only when:"),s.push(`1. The verification requires subjective judgment Playwright can't make (e.g., "is this alt text accurate to the brand's voice?")`),s.push('2. The criterion needs production-data context (e.g., "do users hit this timeout in practice?")'),s.push("3. The Playwright test itself is ambiguous and the user has context that resolves it"),s.push(""),s.push('**Report Playwright results inline with your fix proposals.** "I ran a smoke test for 1.4.10 at 320px — no horizontal scroll detected; checking this off" beats "Could you verify 1.4.10 manually?" every time.'),s.push(""),s.push("## Determinism + reproducibility"),s.push(""),s.push("**AI judgments are not reproducible by default.** Anthropic, OpenAI, and most LLM APIs run at non-zero sampling temperature unless you explicitly request otherwise — meaning the same prompt + same context can produce different answers on different runs. wcagcheckr learned this the hard way (rc.190 → rc.192): consecutive audits on an unchanged page produced different verdicts because the audit's own AI calls ran at default temperature. The fix was `temperature: 0` on every judgment call."),s.push(""),s.push("You face the same risk for any decision you make based on AI judgment rather than empirical observation. Mitigate by:"),s.push(""),s.push('1. **Prefer empirical verification over AI judgment.** If a question can be answered by running Playwright, running axe-core, sampling pixel values, or grepping the codebase — do that, not "I think the screenshot shows…". The Playwright section above lists 11 criteria that can be verified deterministically.'),s.push("2. **For any auxiliary AI calls you make** (vision tasks, classification, summarization), pass `temperature: 0` (Anthropic / OpenAI) or `top_p: 0.01` (other providers). Same screenshot + same prompt should produce the same answer twice in a row — verify before trusting the answer."),s.push('3. **Document AI-judgment decisions in commit messages.** When you DO rely on AI for a subjective call ("I chose `aria-label=\\"Search the catalog\\"` over `\\"Search\\"` because…"), record the reasoning in the commit so the next session — yours or a human\'s — can audit the call.'),s.push("4. **Treat any single AI verdict as one data point, not gospel.** If you ask AI the same question twice and get different answers, that's the variance showing. Stick with the FIRST answer you committed to OR cross-check with Playwright; don't flip-flop based on subsequent re-rolls."),s.push("5. **Re-run the auditor (`wcagcheckr`) after every commit** rather than asking AI \"is this fixed?\" — the auditor's output is the source of truth for whether a fix landed, not the fixer AI's self-assessment."),s.push(""),s.push("Sampling variance is the silent enemy of compliance automation. Treat it as a constraint to design around, not noise to ignore."),s.push(""),s.push("## Constraints"),s.push(""),s.push("- Don't fix violations that aren't in this list (no scope creep)."),s.push(`- Don't reformat or reorder unrelated code. No "while I'm here" cleanup.`),s.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.`),s.push("- Don't add new dependencies, configs, or build steps."),s.push("- Add inline comments only where the fix's rationale is non-obvious; otherwise skip comments."),s.push("- Preserve existing functionality and visual design wherever possible."),s.push(""),s.push("## How to use this prompt"),s.push(""),s.push("Paste this into Claude / Cursor / Copilot Chat with your codebase open. The AI should:"),s.push('1. **First**: walk through the "Before you start" + "Ask me these questions" sections with the user.'),s.push("2. **Then**: identify the source files containing each violating element (use the selectors + outerHTML below)."),s.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."),s.push("4. **Finally**: produce one atomic commit per fix, with a clear message."),s.push(""),s.push("## Locating findings in source"),s.push(""),s.push("For each finding below, the most reliable search strategies (try in this order):"),s.push(""),s.push('1. **The accessible name / text content** — works for JSX, Vue templates, Svelte, server-rendered HTML. Search for the visible text in `"quotes"` or as a string literal in component files.'),s.push("2. **A distinctive class name** — e.g., `.cta-primary` → grep `cta-primary` in CSS + component files. BEM-style and Tailwind utility classes are the most stable identifiers."),s.push('3. **A unique data attribute** — `data-testid="checkout-button"`, `data-qa=`, `data-cy=` — these are usually authored deliberately and survive minification.'),s.push("4. **A stable ID** — `#hero-search` → grep `hero-search`. Watch out for IDs that look generated (`#field_3f8a92`)."),s.push('5. **The outerHTML pattern** — fall back when class/id/testid don\'t help. Search for distinctive attribute combinations (e.g., `aria-label="…"` + `class="…"`).'),s.push(""),s.push("**Do not search on these — they're runtime artifacts:**"),s.push("- `:nth-child(N)`, `:nth-of-type(N)` — these are positional, never appear in source"),s.push("- Numeric suffixes that look generated (`-1234`, `__abc123`) — usually from CSS Modules / styled-components hashing"),s.push("- Selector paths with `>` chains — describes a DOM structure, not a source location"),s.push(""),s.push("**For pages built with a framework**, also check:"),s.push("- Storybook stories (`*.stories.tsx`, `*.stories.mdx`) — often the canonical component definition"),s.push("- Shared layout components (`Header.tsx`, `Footer.tsx`, `Layout.tsx`) — most page-level violations live here"),s.push("- Theme / token files (`theme.ts`, `tokens.json`) — color-contrast fixes often patch tokens, not consumers"),s.push(""),s.push("---"),s.push("");const L=(((Ae=e[0])==null?void 0:Ae.readingOrderIssues)??[]).filter(I=>!l.has(`reading-order::${I.selector}`)),$=(pe=e[0])==null?void 0:pe.positionAnalysisCapturedAt,U=$?`${$.breakpoint.width}×${$.breakpoint.height} (${$.breakpoint.label}), ${$.direction.toUpperCase()}, ${$.theme} theme, ${$.pseudoState} pseudo-state`:"(reference state unavailable — findings reflect last matrix state)",F=c==null?void 0:c.some(I=>I.criterionId==="1.3.2");if(L.length>0&&!F){s.push("## Reading-order concerns (DOM ≠ visual order)"),s.push(""),s.push(`_Positions captured at: **${U}**. 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._`),s.push(""),s.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. ${L.length} element${L.length===1?" has":"s have"} a notable gap between their DOM position and their visual position:`),s.push(""),s.push("| DOM index | Visual index | Selector | Text |"),s.push("|---|---|---|---|");for(const I of L){const B=I.textSnippet.replace(/\|/g,"\\|").replace(/\n/g," ");s.push(`| ${I.domIndex} | ${I.visualIndex} | \`${I.selector}\` | ${B} |`)}s.push(""),s.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."),s.push(""),s.push("**False-positive cases — these are the ONLY legitimate reasons to skip a flagged element:**"),s.push(""),s.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.'),s.push('2. **Skip-links** ("Skip to main content") — visually positioned late but DOM-early on purpose.'),s.push("3. **Caption-first patterns** — captions placed before their image in DOM so SR users get context before the alt text."),s.push("4. **Modal / popover triggers** — the trigger button is DOM-near the modal's portal mount point, which is often `<body>` end."),s.push(""),s.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.'),s.push(""),s.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."),s.push(""),s.push("---"),s.push("")}const T=(((Se=e[0])==null?void 0:Se.tabOrderIssues)??[]).filter(I=>!l.has(`tab-order::${I.selector}`)),N=c==null?void 0:c.some(I=>I.criterionId==="2.1.2"&&I.verdict==="pass");if(T.length>0&&!N){const I=T.filter(P=>P.flag==="visual"||P.flag==="both").length,B=T.filter(P=>P.flag==="tabindex"||P.flag==="both").length;s.push("## Tab-order concerns (keyboard focus sequence ≠ visual order) — WCAG 2.4.3 Focus Order (A)"),s.push(""),s.push(`_Positions captured at: **${U}**. 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._`),s.push(""),s.push(`Keyboard users tab through the page in one order; sighted users scan in another. axe-core does not detect this. ${T.length} focusable element${T.length===1?" has":"s have"} a notable divergence. Of these: ${I} ${I===1?"has":"have"} a visual-vs-tab mismatch (focus jumps around the page); ${B} ${B===1?"is":"are"} caused by positive \`tabindex\` reordering DOM source.`),s.push(""),s.push("| Tab pos | Visual pos | DOM pos | Flag | Selector | Text |"),s.push("|---|---|---|---|---|---|");for(const P of T){const Y=P.textSnippet.replace(/\|/g,"\\|").replace(/\n/g," ");s.push(`| ${P.tabPosition} | ${P.visualPosition} | ${P.domPosition} | ${P.flag} | \`${P.selector}\` | ${Y} |`)}s.push(""),s.push("**How to fix:**"),s.push(""),s.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."),s.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.'),s.push("- **`flag: both`** — both conditions present. Fix the DOM order first, then remove the positive `tabindex`."),s.push(""),s.push("**False-positive cases — these are the ONLY legitimate reasons to skip a flagged element:**"),s.push(""),s.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.'),s.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."),s.push("3. **Dropdown menus / popovers** — a button DOM-far from its portal-rendered menu may produce false positives when the menu is open."),s.push(""),s.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.`),s.push(""),s.push("Surface candidates to the user; ask which are intended; only fix the rest."),s.push(""),s.push("---"),s.push("")}const O=e.flatMap(I=>I.announcements??[]),G=e.flatMap(I=>I.focusEvents??[]).filter(I=>I.isFocusReset);if(O.length>0||G.length>0){if(s.push("## Runtime behavioral observations"),s.push(""),O.length>0){const I=O.filter(P=>P.politeness==="assertive").length;s.push(`**Live-region announcements** (${O.length} total, ${I} 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:`),s.push("");const B=O.slice(0,8);for(const P of B){const Y=P.text.replace(/\|/g,"\\|").replace(/\n/g," ").slice(0,120);s.push(`- \`[${P.politeness}${P.role?`/${P.role}`:""}]\` ${Y}`)}O.length>8&&s.push(`- (and ${O.length-8} more)`),s.push(""),s.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)."),s.push("")}if(G.length>0){s.push(`**Focus resets to body** (${G.length} occurrence${G.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.`),s.push("");for(const I of G.slice(0,5))s.push(`- \`${I.fromSelector??"(none)"}\` → \`${I.toSelector}\``);G.length>5&&s.push(`- (and ${G.length-5} more)`),s.push(""),s.push("Common cause: an element is removed from the DOM (e.g., modal closes) without `.focus()` being called on a sensible destination."),s.push("")}s.push("---"),s.push("")}const M=((C=e[0])==null?void 0:C.undefinedCustomProperties)??[];if(M.length>0){s.push("## Undefined CSS custom properties (cascade root cause)"),s.push(""),s.push(`${M.length} CSS custom propert${M.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.`),s.push(""),s.push("| Custom property | References | First sample sites |"),s.push("|---|---|---|");for(const I of M){const B=I.sampleSites.map(P=>`\`${P.selector} { ${P.property} }\``).join("; ");s.push(`| \`${I.name}\` | ${I.referenceCount} | ${B} |`)}s.push(""),s.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."),s.push(""),s.push("---"),s.push("")}if(a&&a.length>0){s.push("## Manual checks already completed (Guided Tests)"),s.push(""),s.push(`The consultant has run ${a.length} Intelligent Guided Test workflow${a.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.`),s.push(""),s.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."),s.push("");const I=Me;s.push("| Workflow | Status | Notes |"),s.push("|---|---|---|");for(const ne of a){const q=I.find(Ea=>Ea.id===ne.workflowId);if(!q)continue;let J=0,X=0,fe=0,Ee=0,re=0;for(const Ea of q.steps){const bt=ne.steps[Ea.id];if(!bt){re++;continue}bt.status==="pass"?J++:bt.status==="fail"?X++:bt.status==="skip"?fe++:bt.status==="not-applicable"?Ee++:re++}const mt=X>0?`[FAIL] ${X} failed`:re>0?`[WARN] ${re} unanswered`:`[PASS] all ${q.steps.length} answered`,Lt=[`${J}p`,`${X}f`];Ee>0&&Lt.push(`${Ee} N/A`),fe>0&&Lt.push(`${fe} skip`),re>0&&Lt.push(`${re} unanswered`);const Di=`${Lt.join(" · ")} of ${q.steps.length} steps`;s.push(`| ${q.name} | ${mt} | ${Di} |`)}s.push("");const B=[];for(const ne of a){const q=I.find(J=>J.id===ne.workflowId);if(q)for(const J of q.steps){const X=ne.steps[J.id];(X==null?void 0:X.status)==="fail"&&B.push({workflow:q.name,stepTitle:J.question,notes:X.notes})}}if(B.length>0){s.push("**Failed manual checks** (these are real WCAG failures the consultant verified by hand):"),s.push("");for(const ne of B)s.push(`- **${ne.workflow} → ${ne.stepTitle}**${ne.notes?`: ${ne.notes}`:""}`);s.push(""),s.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."),s.push("")}const P=new Set(a.map(ne=>ne.workflowId)),Y=I.filter(ne=>!P.has(ne.id));Y.length>0&&(s.push(`**Not yet manually verified** (${Y.length} workflow${Y.length===1?"":"s"}): `+Y.map(ne=>ne.name).join(", ")+". These cover WCAG criteria automation cannot detect; ask the consultant whether to run them before declaring the audit complete."),s.push("")),s.push("---"),s.push("")}if(n&&n.pagesAudited>1){const I=(()=>{try{return new URL(u).origin}catch{return null}})(),B=(()=>{try{return new URL(n.startUrl).origin}catch{return null}})();if(I&&B&&I===B){const P=n.finishedAt,Y=P?Date.now()-new Date(P).getTime():0,ne=Y<6e4?"just now":Y<36e5?`${Math.round(Y/6e4)} minute${Math.round(Y/6e4)===1?"":"s"} ago`:Y<864e5?`${Math.round(Y/36e5)} hour${Math.round(Y/36e5)===1?"":"s"} ago`:`${Math.round(Y/864e5)} day${Math.round(Y/864e5)===1?"":"s"} ago`;if(s.push("## Site-wide context (from a recent site crawl)"),s.push(""),s.push(`A crawl ${P?`(${ne})`:""} of ${n.pagesAudited} page${n.pagesAudited===1?"":"s"} on this origin produced grade **${n.siteGrade}** site-wide with **${n.totalUniqueViolations}** unique violations. Use this context for WCAG 3.2.3 (Consistent Navigation) and 3.2.4 (Consistent Identification) which require cross-page comparison.`),s.push(""),n.topViolations.length>0){s.push("**Most-frequent violations across the site** (rules to prioritize because they affect multiple pages):"),s.push("");for(const J of n.topViolations.slice(0,6))s.push(`- \`${J.ruleId}\` — ${J.impact} — found on ${J.urlsAffected} page${J.urlsAffected===1?"":"s"} (${J.totalOccurrences} total instances)`);s.push(""),s.push("Fixing one of these rules in shared components / layouts is leverage — one fix resolves N pages."),s.push("")}const q=n.consistencyFindings??[];if(q.length===0&&n.topViolations.some(X=>X.ruleId==="wcc-consistency-needs-site-crawl"||X.ruleId==="wcc-redundant-entry-needs-site-crawl")&&(s.push("**Cross-page consistency status:** the comparators ran across the crawled pages and detected **no real divergences** — primary navigation, brand link, footer help mechanism, and multi-step form fields are all consistent across the sample. The `wcc-consistency-needs-site-crawl` and `wcc-redundant-entry-needs-site-crawl` rule IDs in the violations list are placeholder findings emitted per-page by the single-page analyzer (which can't see other pages); the cross-page comparators downgrade them when the crawl confirms consistency. **Do not chase these on a per-page basis** — they're already cleared at the site level."),s.push("")),q.length>0){s.push("**Cross-page consistency divergences detected:**"),s.push("");for(const J of q){const X=J.aiVerdict?J.aiVerdict.verdict==="uncertain"?" (AI: uncertain — human verify)":` (AI: confirmed real, confidence ${(J.aiVerdict.confidence*100).toFixed(0)}%)`:"";s.push(`- **${J.wcagCriterion} ${J.impact}**${X} — ${J.description}`);for(const fe of J.details.split(`
1663
+ `))s.push(` ${fe}`);(W=J.aiVerdict)!=null&&W.reasoning&&s.push(` AI reasoning: ${J.aiVerdict.reasoning}`),s.push("")}}s.push("**Manual cross-page checks the AI should still verify** (the heuristic comparators above can miss edge cases):"),s.push(""),s.push("- **3.2.3 Consistent Navigation** — primary nav DOM order across pages."),s.push("- **3.2.4 Consistent Identification** — same logo / menu / search / nav-link accessible names across pages."),s.push("- **3.2.6 Consistent Help (WCAG 2.2 A)** — help mechanism position consistency."),s.push("- **3.3.7 Redundant Entry (WCAG 2.2 A)** — multi-step forms re-asking for already-entered information."),s.push(""),s.push("---"),s.push("")}}if(f.length>0){const I=new Map;for(const P of f)I.has(P.matchKey)||I.set(P.matchKey,P);const B=Array.from(I.values());s.push("## Previously verified by human — DO NOT re-flag or attempt to fix"),s.push(""),s.push(`The user manually verified each of the following ${B.length} finding${B.length===1?"":"s"} in the wcagcheckr side panel. They WILL appear in raw axe output on every subsequent audit (axe re-detects them because the underlying DOM hasn't changed), but the user has dispositioned each one as acceptable — typically because an automated rule fired on a legitimate edge case (decorative image with empty alt, contrast that passes APCA but not WCAG 2.0 ratio, etc.). **Do not attempt to "fix" any item in this list.** Treat them as resolved. If you genuinely believe one is misjudged, note your reasoning in a comment for the user to review on their next audit — do NOT modify the markup.`),s.push(""),B.forEach((P,Y)=>{s.push(`${Y+1}. \`${P.ruleId}\` on \`${P.target.selector}\``),P.target.accessibleName&&s.push(` - Accessible name: "${P.target.accessibleName}"`),P.target.textSnippet&&s.push(` - Text: "${P.target.textSnippet.slice(0,120)}"`),s.push(` - WCAG ${P.wcagCriterion} ${P.wcagLevel} · ${P.impact}`)}),s.push(""),s.push("---"),s.push("")}const j=(r??[]).filter(I=>I.verdict==="fail").filter(I=>!h.has(`${I.ruleId}::${I.selector}`));if(j.length>0){const I=new Map;for(const P of j){const Y=`${P.ruleId}::${P.selector}`;I.has(Y)||I.set(Y,P)}const B=Array.from(I.values());s.push("## AI-evaluated fails (axe couldn't determine — AI vision determined FAIL)"),s.push(""),s.push(`axe-core flagged each of the following ${B.length} element${B.length===1?"":"s"} as INCOMPLETE — its automated heuristics couldn't reach a verdict (typically because the background is a gradient, image, or semi-transparent overlay that axe can't pixel-sample). wcagcheckr's AI resolver then evaluated each element via vision + computed-style sampling and determined it is a real **FAIL**. Treat these the same as axe violations: locate the element in source, apply the canonical fix for the rule, and re-audit. The AI's reasoning is included per finding so you don't have to re-derive what was wrong.`),s.push(""),B.forEach((P,Y)=>{const ne=P.wcagCriterion?` — WCAG ${P.wcagCriterion}`:"";s.push(`### ${Y+1}. \`${P.ruleId}\`${ne}`),s.push(""),s.push(`**Selector:** \`${P.selector}\``),s.push(""),s.push(`**AI verdict (FAIL):** ${P.reasoning}`),s.push("");const q=ot[P.ruleId];q&&(s.push(`**Recipe:** ${q.summary}`),s.push(""),q.snippet&&(s.push("```"+(q.snippetLang??"")),s.push(q.snippet),s.push("```"),s.push("")))}),s.push("---"),s.push("")}s.push("## Violations to fix"),s.push(""),w.forEach((I,B)=>{var Ee;const P=ot[I.ruleId],Y=I._instanceSelectors.length;if(s.push(`### ${B+1}. \`${I.ruleId}\` — ${I.impact} — WCAG ${I.wcagCriterion} ${I.wcagLevel}${Y>1?` (${Y} instances)`:""}`),s.push(""),s.push(`**Description:** ${I.description}`),s.push(""),Y>1){s.push(`**Affects ${Y} spots — fix at the SOURCE (one CSS rule, one component, one shared class) — DO NOT patch each instance separately:**`);for(const re of I._instanceSelectors.slice(0,8))s.push(`- \`${re}\``);I._instanceSelectors.length>8&&s.push(`- (and ${I._instanceSelectors.length-8} more)`),s.push(""),s.push(`> **Find the common source.** ${Y} matches means there's a single component / class / CSS rule producing all of them. Search the codebase for the shared class name OR the JSX/template that renders this element, then patch ONCE there. If you can't find a shared source (the elements are genuinely independent), apply the same fix to each — but flag that to the user as "code smell: this pattern is repeated."`)}else s.push(`**Selector:** \`${I.target.selector}\``);s.push(""),s.push(`**Found in state(s):** ${I._states.join(" / ")}`),s.push(""),s.push(br(I)),s.push("");const ne=gh(I);if(ne.length>0){for(const re of ne)s.push(`**${re.label}:** ${re.value}`);s.push("")}I.target.failureSummary&&(s.push(`**Diagnostic:** ${I.target.failureSummary.replace(/\n+/g," ")}`),s.push("")),I.ai&&(s.push(`**[AI] AI-verified finding** (model: ${I.ai.model}, confidence: ${(I.ai.confidence*100).toFixed(0)}%)`),s.push(`> ${I.ai.reasoning}`),s.push(""));const q=I.target.cascadeChain;if(q&&(q.color||q.backgroundColor)){if(s.push("**CSS cascade chain (authored → rendered):**"),q.color){const re=q.color.vars.length>0?` (vars: ${q.color.vars.map(mt=>`\`${mt}\``).join(", ")})`:"";s.push(`- \`color\`: \`${q.color.authored}\` → \`${q.color.rendered}\`${re}${q.color.ruleSelector?` — set in rule \`${q.color.ruleSelector}\``:""}`)}if(q.backgroundColor){const re=q.backgroundColor.vars.length>0?` (vars: ${q.backgroundColor.vars.map(mt=>`\`${mt}\``).join(", ")})`:"";s.push(`- \`background-color\`: \`${q.backgroundColor.authored}\` → \`${q.backgroundColor.rendered}\`${re}${q.backgroundColor.ruleSelector?` — set in rule \`${q.backgroundColor.ruleSelector}\``:""}`)}s.push("")}const J=i==null?void 0:i[I.matchKey];if(J&&J.suggestions.length>0){s.push(`**AI color suggestions** (cached from in-UI tooling — ${J.suggestions.length} candidates passing WCAG):`);for(const re of J.suggestions)s.push(`- foreground \`${re.foreground}\` on background \`${re.background}\` → ${re.contrast.toFixed(2)}:1 — ${re.rationale}`);s.push(`> AI reasoning: ${J.reasoning}`),s.push("")}s.push("**Element HTML:**"),s.push("```html"),s.push(I.target.outerHTML),s.push("```"),s.push("");const X=(Ee=J==null?void 0:J.suggestions)==null?void 0:Ee[0],fe=gi(I,{suggestedColors:X?{foreground:X.foreground,background:X.background}:void 0});if(fe){s.push("**Suggested fix for this element** (apply to the source, not the rendered DOM):"),s.push("```diff");for(const re of fe.current.split(`
1664
+ `))s.push(`- ${re}`);for(const re of fe.suggested.split(`
1665
+ `))s.push(`+ ${re}`);s.push("```"),fe.note&&s.push(`> ${fe.note}`),s.push("")}P?(s.push(`**Recipe:** ${P.summary}`),P.snippet&&(s.push(""),s.push("```"+(P.snippetLang??"")),s.push(P.snippet),s.push("```"))):s.push(`**Recipe:** No curated recipe for this ruleapply the standard WCAG ${I.wcagCriterion} guidance.`),I.helpUrl&&(s.push(""),s.push(`**More info:** ${I.helpUrl}`)),s.push(""),s.push("---"),s.push("")});const R=mh(e,r);if(R.length>0){s.push("## Items axe couldn't determineneeds human review"),s.push(""),s.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."),s.push("");for(const I of R)s.push(`### \`${I.ruleId}\` on \`${I.selector}\``),s.push(""),s.push(`- **WCAG criterion:** ${I.wcagCriterion}`),s.push(`- **Page URL:** ${I.pageUrl}`),I.failureSummary&&s.push(`- **axe diagnostic:** ${I.failureSummary}`),(z=I.styles)!=null&&z.foreground&&s.push(`- **Foreground:** \`${I.styles.foreground}\``),(Z=I.styles)!=null&&Z.background&&s.push(`- **Background:** \`${I.styles.background}\``),(ie=I.styles)!=null&&ie.fontSize&&s.push(`- **Font size:** ${I.styles.fontSize}px, weight ${I.styles.fontWeight??"unknown"}`),(ee=I.styles)!=null&&ee.textSample&&s.push(`- **Text sample:** ${JSON.stringify(I.styles.textSample)}`),I.aiAttemptReasoning&&(s.push(""),s.push("**Our AI resolver already attempted this element and returned uncertain.** Its reasoning:"),s.push(""),s.push(`> ${I.aiAttemptReasoning.replace(/\n/g,`
1666
+ > `)}`),s.push(""),s.push("**Suggested next step:** if you have stronger vision capability than our resolver, re-evaluate with a fresh screenshot. Otherwise the user must verify this one manually.")),s.push(""),s.push("---"),s.push("")}s.push("## Manual verification checklist — content-meaning criteria"),s.push(""),s.push(`wcagcheckr's three layers (axe-core, DOM analyzers, AI walkthroughs) cover every WCAG 2.1 AA criterion structurally. What automation CANNOT verify is content meaning: whether your caption text actually matches the audio, whether alt text accurately describes the image, whether sensory cues ("click the red button") have an alternative non-visual phrasing. **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).`),s.push(""),s.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.'),s.push("");const V=new Map;if(c)for(const I of c)V.set(I.criterionId,I.verdict);function K(I,B){const P=V.get(I);return P==="pass"?`- [x] **${I}** — [PASS] AI walkthrough verified pass during audit (see "AI keyboard walkthroughs" section). No manual verification needed.`:P==="fail"?`- [x] **${I}** — [FAIL] AI walkthrough FAILED during audit (see "AI keyboard walkthroughs" section for the fix). Confirm the code fix is applied and re-audit.`:P==="uncertain"?`- [ ] **${I}** — AI walkthrough was uncertain. ${B}`:`- [ ] **${I}** — ${B}`}return s.push("### Perceivable (audit-already-verified items pre-checked)"),s.push(""),s.push(K("1.3.2 Meaningful Sequence (A)","verify DOM source order matches the visual / reading order. Screen readers read the DOM; CSS reordering does NOT affect what the SR hears.")),s.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."),s.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.'),s.push("- [ ] **1.2.3 Audio Description / Media Alternative Prerecorded (A)** — prerecorded video has either audio description or a full text alternative."),s.push("- [ ] **1.2.4 Captions Live (AA)** — live audio content (livestream, real-time meeting) has captions. Usually N/A for static sites."),s.push("- [ ] **1.2.5 Audio Description Prerecorded (AA)** — prerecorded video has audio description for visual-only information."),s.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."),s.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."),s.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."),s.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)."),s.push(K("1.4.11 Non-text Contrast (AA)","every UI component (button border, form input border, icon, focus indicator) and graphical object has at least 3:1 contrast against adjacent colors. Inspect every interactive control + state indicator visually.")),s.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."),s.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)."),s.push(""),s.push("### Operable (audit-already-verified items pre-checked)"),s.push(""),s.push(K("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.")),s.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.'),s.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.'),s.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."),s.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."),s.push(K("2.4.3 Focus Order (A)","spot-check by tabbing through the page: focus visits elements in a logical order (matches visual / semantic flow).")),s.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."),s.push(K("2.4.7 Focus Visible (AA)","every focusable element shows a visible focus indicator. Tab through the page and verify focus is always visually obvious.")),s.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."),s.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."),s.push(K("2.5.3 Label in Name (A)",`every interactive control's accessible name contains its visible text. Test with speech recognition: "click [visible text]" should activate the correct control.`)),s.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."),s.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."),s.push(""),s.push("### Understandable"),s.push(""),s.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."),s.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."),n&&n.pagesAudited>1?(s.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."),s.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).")):(s.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."),s.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.')),s.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."),s.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."),s.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."),s.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)."),s.push(""),s.push("### Robust"),s.push(""),O.length>0||G.length>0?s.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."):s.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.'),s.push(""),s.push("### Manual checks STILL TO DO"),s.push(""),s.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).`),s.push(""),s.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.'),s.push(""),s.push("---"),s.push(""),s.push("## When you're done — verification"),s.push(""),s.push("Before reporting completion, do all of the following:"),s.push(""),s.push(`1. **Confirm all ${w.length} violation${w.length===1?"":"s"} above are addressed.** Walk back through the list with the user, ticking each one off.`),s.push("2. **Re-run the auditor** (or whatever method we agreed in question 6 above). Verify zero NEW violations were introduced by the fixes."),s.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."`),s.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."),s.push("5. **Commit log review** — surface the list of commits you produced. Each should be atomic and revertable."),s.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."),s.push(""),s.push("## Success criteria"),s.push(""),s.push("- All "+w.length+" violation"+(w.length===1?"":"s")+" above are resolved or explicitly deferred with reason."),s.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.'),s.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.)'),s.push("- No new violations introduced."),s.push("- State-specific behavior preserved (`:hover`, `:focus`, dark-mode, RTL, breakpoints)."),s.push("- One atomic commit per fix; clean revertable history."),s.push("- No unrelated code reformatted, no dependencies bumped."),s.push("- Diff is reviewable in under 10 minutes per fix."),s.push(""),s.push(`_Generated by ${xe} v${Te()}._`),s.join(`
1667
+ `)}const bh=5;function yh(e){const t=new Map;let a=0;for(const n of e){if(!n.screenshotDataUrl||n.violations.length===0)continue;const o=`${n.state.pseudoState} · ${n.state.theme} · ${n.state.direction} · ${n.state.breakpoint.id}`,r=o;t.has(r)||t.set(r,{stateLabel:o,screenshotDataUrl:n.screenshotDataUrl,violations:[]});const i=t.get(r);for(const c of n.violations)a++,i.violations.push({...c,_idx:a})}return Array.from(t.values()).sort((n,o)=>o.violations.length-n.violations.length).slice(0,bh)}const Wo=1280,Go=800,wh={low:"Low risk",moderate:"Moderate risk",high:"High risk",critical:"Critical risk"},vh={low:"#d1fae5",moderate:"#fef3c7",high:"#fed7aa",critical:"#fecaca"},Ah={low:"#065f46",moderate:"#92400e",high:"#9a3412",critical:"#991b1b"},kh={A:"#10b981",B:"#84cc16",C:"#eab308",D:"#f97316",F:"#dc2626"},xh={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 Si(e,t,a,n,o,r,i){return Dt(e,t,"evidence",a,n,o,r,i)}function Ci(e,t,a,n,o,r){return Ei(e,"letter",t,a,n,o,r)}function $i(e,t,a,n,o,r){return Ei(e,"report",t,a,n,o,r)}function Ti(e,t,a,n,o,r){var p;const i=e.flatMap(f=>f.violations),c=wa(i,void 0,e,a,t,n,o,lt(e),r),d=We(e),l=new Map;for(const f of i){const y=De(f.ruleId,f.target.selector);l.has(y)||l.set(y,f)}const h=Array.from(l.values()).sort((f,y)=>{const b={critical:0,serious:1,moderate:2,minor:3};return(b[f.impact]??99)-(b[y.impact]??99)}),m=new Map;for(const f of h){const y=m.get(f.ruleId)??[];y.push(f),m.set(f.ruleId,y)}const u=[];if(u.push("# Help me fix accessibility issues on my website"),u.push(""),u.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."),u.push(""),u.push("## What I need from you"),u.push(""),u.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."),u.push("2. **Then, for each issue below, tell me clearly:**"),u.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"),u.push(" - Or whether **my developer needs to do this** (code-level fix) — and what to ask them to do"),u.push("3. **Prioritize ruthlessly.** I want to start with the issues that:"),u.push(" - Are most likely to come up in an ADA-related lawsuit (critical-impact items)"),u.push(" - Are easiest for me to fix without involving a developer"),u.push("4. **Don't bury me in jargon.** If you have to use a technical term, explain it in plain English first."),u.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."),u.push(""),u.push("## Audit summary"),u.push(""),u.push(`- **Page audited:** ${d}`),u.push(`- **Lawsuit-target risk:** ${gr[c.risk]} — ${mr[c.risk]}`),u.push(`- **Issues found:** ${c.totals.unique} unique problems (${c.totals.critical} critical · ${c.totals.serious} serious · ${c.totals.moderate} moderate · ${c.totals.minor} minor)`),c.coverage&&c.coverage.untestedCriteria.length>0?u.push(`- **WCAG conformance progress:** ${c.coverage.evaluated} of ${c.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 ${c.coverage.targetVersion} ${c.coverage.targetLevel}, ${c.coverage.untestedCriteria.length} more criteria need human review — see the "Guided Tests" workflows in the auditor side panel for these.`):c.coverage&&c.coverage.untestedCriteria.length===0&&u.push(`- **WCAG conformance:** All ${c.coverage.totalApplicable} applicable WCAG ${c.coverage.targetVersion} ${c.coverage.targetLevel} criteria have been evaluated. Fixing the issues below will produce a defensible conformance claim.`),u.push(""),u.push("## The issues, in plain language"),u.push(""),m.size===0)return u.push("No issues were detected across our three evaluation layers (axe-core rule-based checks, our DOM analyzers covering criteria axe doesn't reach, and AI walkthroughs that drive real keyboard input and judge screenshots). For a formal conformance claim, spot-check the content-meaning criteria — automation can detect missing captions, not whether your caption text accurately matches the audio."),u.join(`
1668
+ `);let g=1;for(const[f,y]of m.entries()){const b=(p=y.find(s=>s.target.opacityContext))==null?void 0:p.target.opacityContext,A=$n(f,b),w=y[0].impact;u.push(`### ${g}. [${w.toUpperCase()}] ${A.whatsWrong}`),u.push(""),u.push(`**Why this matters.** ${A.whyItMatters}`),u.push(""),u.push(`**Plain-language fix guidance.** ${A.howToFix}`),u.push(""),u.push(`**Where on the page (${y.length} ${y.length===1?"spot":"spots"}):**`);for(const s of y.slice(0,8))u.push(`- \`${s.target.selector}\``);y.length>8&&u.push(`- (and ${y.length-8} more spots)`),u.push(""),u.push(`*Technical reference for your developer:* axe-core rule \`${f}\` — full docs at https://dequeuniversity.com/rules/axe/4.11/${f}`),u.push(""),g++}return u.push("---"),u.push(""),u.push("## How to use this output"),u.push(""),u.push("Walk through this list with me one issue at a time. For each:"),u.push("1. Help me understand the issue with a real example of who it affects."),u.push("2. Tell me clearly if I can fix it myself, and if so, walk me through it for my platform."),u.push("3. If I need a developer, give me a short paragraph I can paste into an email or message to them."),u.push("4. Help me prioritize — I have limited time and want to fix the riskiest things first."),u.push(""),u.push("Start by asking me which platform I use."),u.join(`
1669
+ `)}function Ei(e,t,a,n,o,r,i){const c=e.flatMap(b=>b.violations),d=wa(c,void 0,e,n,a,o,r,lt(e),i),l=We(e),h=new Date,m=new Map;for(const b of c){const A=De(b.ruleId,b.target.selector);m.has(A)||m.set(A,b)}const u=Array.from(m.values()).sort((b,A)=>{const w={critical:0,serious:1,moderate:2,minor:3};return(w[b.impact]??99)-(w[A.impact]??99)}),g=new Map;for(const b of u){const A=g.get(b.ruleId)??[];A.push(b),g.set(b.ruleId,A)}const p=Array.from(g.entries()).map(([b,A])=>{var x;const w=(x=A.find(L=>L.target.opacityContext))==null?void 0:x.target.opacityContext,s=$n(b,w),v=A[0].impact,S=A.slice(0,8).map(L=>`<code>${E(L.target.selector)}</code>`).join(", "),k=A.length>8?` <em>(and ${A.length-8} more)</em>`:"";return`
1653
1670
  <section class="rule">
1654
1671
  <div class="rule-header">
1655
- <span class="impact impact-${E(A)}">${E(A)}</span>
1672
+ <span class="impact impact-${E(v)}">${E(v)}</span>
1656
1673
  <h3>${E(s.whatsWrong)}</h3>
1657
1674
  </div>
1658
1675
  <p class="why"><strong>Why this matters.</strong> ${E(s.whyItMatters)}</p>
1659
1676
  <p class="how"><strong>How to fix.</strong> ${E(s.howToFix)}</p>
1660
1677
  <p class="where">
1661
- <strong>Where on the page (${v.length} ${v.length===1?"spot":"spots"}):</strong> ${S}${k}
1678
+ <strong>Where on the page (${A.length} ${A.length===1?"spot":"spots"}):</strong> ${S}${k}
1662
1679
  </p>
1663
1680
  <details class="techy">
1664
1681
  <summary>Technical reference for the developer</summary>
1665
1682
  <p>axe-core rule ID: <code>${E(b)}</code>. Full rule documentation:
1666
1683
  <a href="https://dequeuniversity.com/rules/axe/4.11/${E(b)}">dequeuniversity.com/rules/axe/4.11/${E(b)}</a>.</p>
1667
1684
  </details>
1668
- </section>`}).join(""),p=t==="letter"?d.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.":d.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.":d.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.":d.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.",y=t==="letter"?"Accessibility check":"Accessibility report";return ze(`<!doctype html>
1685
+ </section>`}).join(""),f=t==="letter"?d.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.":d.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.":d.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.":d.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.",y=t==="letter"?"Accessibility check":"Accessibility report";return Ye(`<!doctype html>
1669
1686
  <html lang="en">
1670
1687
  <head>
1671
1688
  <meta charset="utf-8" />
@@ -1700,10 +1717,10 @@ ${t.length===0?"":`<h3 style="margin-top:12pt">Human verified (${t.length})</h3>
1700
1717
  <h1>${E(y)} for ${E(l)}</h1>
1701
1718
  <p class="meta">${t==="letter"?"Ran":"Audit run"} ${E(h.toLocaleString())} · ${u.length} issue${u.length===1?"":"s"} grouped into ${g.size} ${g.size===1?"category":"categories"}</p>
1702
1719
 
1703
- <p>${p}</p>
1720
+ <p>${f}</p>
1704
1721
 
1705
1722
  <div class="summary">
1706
- <p style="margin: 0;"><strong>${E(hr[d.risk])}.</strong> ${E(pr[d.risk])}</p>
1723
+ <p style="margin: 0;"><strong>${E(gr[d.risk])}.</strong> ${E(mr[d.risk])}</p>
1707
1724
  <div class="totals">
1708
1725
  ${d.totals.critical>0?`<span><strong>${d.totals.critical}</strong> critical</span>`:""}
1709
1726
  ${d.totals.serious>0?`<span><strong>${d.totals.serious}</strong> serious</span>`:""}
@@ -1714,7 +1731,7 @@ ${t.length===0?"":`<h3 style="margin-top:12pt">Human verified (${t.length})</h3>
1714
1731
  </div>
1715
1732
 
1716
1733
  <h2>${t==="letter"?"What I found":"Detailed findings"}</h2>
1717
- ${f.length>0?f:"<p>No automated issues detected on this page.</p>"}
1734
+ ${p.length>0?p:"<p>No automated issues detected on this page.</p>"}
1718
1735
 
1719
1736
  ${t==="letter"?`
1720
1737
  <h2>What I&apos;d like you to do</h2>
@@ -1736,10 +1753,10 @@ ${t==="letter"?`
1736
1753
  `}
1737
1754
 
1738
1755
  <footer>
1739
- <p>Generated by ${E(Ie)} on ${E(h.toISOString())}. Three evaluation layers ran: axe-core (rule-based), wcagcheckr DOM analyzers (state-matrix iteration + structural heuristics for criteria axe doesn't cover), and AI walkthroughs (real keyboard input + screenshot judgment for the 6 criteria neither rule-based nor static-DOM analysis can decide). All 50 applicable WCAG 2.1 AA criteria have an evaluation path. Content-meaning criteria (caption accuracy, alt-text correctness, sensory cues) benefit from a human spot-check before any formal conformance claim.</p>
1756
+ <p>Generated by ${E(xe)} on ${E(h.toISOString())}. Three evaluation layers ran: axe-core (rule-based), wcagcheckr DOM analyzers (state-matrix iteration + structural heuristics for criteria axe doesn't cover), and AI walkthroughs (real keyboard input + screenshot judgment for the 6 criteria neither rule-based nor static-DOM analysis can decide). All 50 applicable WCAG 2.1 AA criteria have an evaluation path. Content-meaning criteria (caption accuracy, alt-text correctness, sensory cues) benefit from a human spot-check before any formal conformance claim.</p>
1740
1757
  </footer>
1741
1758
  </body>
1742
- </html>`)}function uh(e){if(!e||e.length===0)return`
1759
+ </html>`)}function Ih(e){if(!e||e.length===0)return`
1743
1760
  <h2 id="manual">5. Manual assessment results</h2>
1744
1761
  <p style="font-size: 10pt; color: #475569;">
1745
1762
  No manual workflows were completed for this audit. wcagcheckr's three layers (axe-core, our
@@ -1749,7 +1766,7 @@ ${t==="letter"?`
1749
1766
  when the audit needs to anchor to specific named auditors for formal conformance.
1750
1767
  When manual workflows are completed, their results are integrated into both the executive
1751
1768
  summary's compliance grade and this section's per-criterion findings.
1752
- </p>`;const t=new Map(e.map(c=>[c.workflowId,c])),a=Re.map(c=>hh(c,t.get(c.id))).filter(c=>c!==null).join("");let n=0,o=0,r=0,i=0;for(const c of Re){i+=c.steps.length;const d=t.get(c.id);if(d)for(const l of c.steps){const h=d.steps[l.id];h&&(r++,h.status==="fail"&&(l.severity==="required"?n++:o++))}}return`
1769
+ </p>`;const t=new Map(e.map(c=>[c.workflowId,c])),a=Me.map(c=>Sh(c,t.get(c.id))).filter(c=>c!==null).join("");let n=0,o=0,r=0,i=0;for(const c of Me){i+=c.steps.length;const d=t.get(c.id);if(d)for(const l of c.steps){const h=d.steps[l.id];h&&(r++,h.status==="fail"&&(l.severity==="required"?n++:o++))}}return`
1753
1770
  <h2 id="manual">5. Manual assessment results</h2>
1754
1771
  <p style="font-size: 10pt; color: #475569; margin-bottom: 8pt;">
1755
1772
  Manual workflows performed by the auditor. ${r} of ${i} steps answered
@@ -1757,7 +1774,7 @@ ${t==="letter"?`
1757
1774
  ${n>0?`<strong style="color: #b91c1c;">${n} required step${n===1?"":"s"} failed</strong> — each is a confirmed WCAG breach contributing to the compliance grade above.`:"No required steps failed."}
1758
1775
  ${o>0?` ${o} advisory step${o===1?"":"s"} failed (best-practice findings).`:""}
1759
1776
  </p>
1760
- ${a}`}function hh(e,t){if(!t)return null;const a=[];for(const l of e.steps){const h=t.steps[l.id];h&&a.push({step:l,status:h.status,notes:h.notes})}if(a.length===0)return null;const n=a.filter(l=>l.status==="pass").length,o=a.filter(l=>l.status==="fail").length,r=a.filter(l=>l.status==="skip").length,i=a.filter(l=>l.status==="not-applicable").length,d=a.filter(l=>l.status==="fail").map(l=>`
1777
+ ${a}`}function Sh(e,t){if(!t)return null;const a=[];for(const l of e.steps){const h=t.steps[l.id];h&&a.push({step:l,status:h.status,notes:h.notes})}if(a.length===0)return null;const n=a.filter(l=>l.status==="pass").length,o=a.filter(l=>l.status==="fail").length,r=a.filter(l=>l.status==="skip").length,i=a.filter(l=>l.status==="not-applicable").length,d=a.filter(l=>l.status==="fail").map(l=>`
1761
1778
  <tr>
1762
1779
  <td><span class="impact impact-${l.step.severity==="required"?"serious":"moderate"}">${E(l.step.severity)}</span></td>
1763
1780
  <td>${l.step.wcag?`<code>${E(l.step.wcag)}</code>`:"—"}</td>
@@ -1778,22 +1795,22 @@ ${o>0?`
1778
1795
  <thead><tr><th style="width: 80pt;">Severity</th><th style="width: 70pt;">WCAG</th><th>Failed check + auditor notes</th></tr></thead>
1779
1796
  <tbody>${d}</tbody>
1780
1797
  </table>
1781
- `:""}`}function Rt(e,t,a="defense",n,o,r,i,c,d,l,h,m){var M,D,j;const u=dh[a],g=((M=e[0])==null?void 0:M.componentId)??"unknown",f=_e(e),p=i??e.reduce((O,G)=>O+G.durationMs,0),y=e.flatMap(O=>O.violations),b=n&&n.length>0?{runs:n,workflows:Re}:void 0,v=Be(y,b,e,l,c,d,h,st(e),m),w=v.score,s=new Date,A=new Map;for(const O of y){const G=Oe(O.ruleId,O.target.selector),R=`${O.currentState.pseudoState} · ${O.currentState.theme} · ${O.currentState.direction}`,V=A.get(G);if(V){V._states.includes(R)||V._states.push(R);continue}A.set(G,{...O,_states:[R]})}const S=Array.from(A.values()).sort((O,G)=>{const R={critical:0,serious:1,moderate:2,minor:3};return(R[O.impact]??99)-(R[G.impact]??99)}),k=rh(e),I=new Map;for(const O of k)for(const G of O.violations){const R=Oe(G.ruleId,G.target.selector);I.has(R)||I.set(R,G._idx)}const L=S.map((O,G)=>`
1798
+ `:""}`}function Dt(e,t,a="defense",n,o,r,i,c,d,l,h,m){var O,D,G;const u=xh[a],g=((O=e[0])==null?void 0:O.componentId)??"unknown",p=We(e),f=i??e.reduce((M,j)=>M+j.durationMs,0),y=e.flatMap(M=>M.violations),b=n&&n.length>0?{runs:n,workflows:Me}:void 0,A=Ke(y,b,e,l,c,d,h,lt(e),m),w=A.score,s=new Date,v=new Map;for(const M of y){const j=De(M.ruleId,M.target.selector),R=`${M.currentState.pseudoState} · ${M.currentState.theme} · ${M.currentState.direction}`,V=v.get(j);if(V){V._states.includes(R)||V._states.push(R);continue}v.set(j,{...M,_states:[R]})}const S=Array.from(v.values()).sort((M,j)=>{const R={critical:0,serious:1,moderate:2,minor:3};return(R[M.impact]??99)-(R[j.impact]??99)}),k=yh(e),x=new Map;for(const M of k)for(const j of M.violations){const R=De(j.ruleId,j.target.selector);x.has(R)||x.set(R,j._idx)}const L=S.map((M,j)=>`
1782
1799
  <tr>
1783
- <td>${G+1}</td>
1784
- <td><span class="impact impact-${E(O.impact)}">${E(O.impact)}</span></td>
1785
- <td><code>${E(O.ruleId)}</code></td>
1786
- <td>${E(O.wcagCriterion)} ${E(O.wcagLevel)}</td>
1787
- <td>${E(O.description)}</td>
1788
- <td><code class="sel">${E(O.target.selector)}</code></td>
1789
- <td>${E(O._states.join(", "))}</td>
1790
- </tr>`).join(""),$=l??new Set,_=new Set(y.filter(O=>!$.has(O.matchKey)).map(O=>O.ruleId)),P=new Set(_);for(const O of e){const G=O.axeRulesEvaluated;if(G){for(const R of G.passed)P.add(R.ruleId);for(const R of G.inapplicable)P.add(R.ruleId);for(const R of G.incomplete)P.add(R.ruleId)}}const T=new Map;for(const O of h??[])T.set(O.criterionId,O.verdict);const N=gi.map(O=>{const{conformance:G,hits:R}=mi(O,_,P,c,T.get(O.ref));return`
1791
- <tr class="${G==="Supports"?"supports":G==="Partially Supports"?"partial":G==="Not Evaluated"?"not-evaluated":"na"}">
1792
- <td><code>${E(O.ref)}</code></td>
1793
- <td>${E(O.title)} <span class="lvl">(${O.level})</span></td>
1794
- <td>${G}</td>
1795
- <td>${R.length>0?`Detected violations of: ${R.map(J=>`<code>${E(J)}</code>`).join(", ")}`:G==="Not Applicable"?"Not addressable by automated audit; requires manual review.":"No automated violations detected."}</td>
1796
- </tr>`}).join("");return ze(`<!doctype html>
1800
+ <td>${j+1}</td>
1801
+ <td><span class="impact impact-${E(M.impact)}">${E(M.impact)}</span></td>
1802
+ <td><code>${E(M.ruleId)}</code></td>
1803
+ <td>${E(M.wcagCriterion)} ${E(M.wcagLevel)}</td>
1804
+ <td>${E(M.description)}</td>
1805
+ <td><code class="sel">${E(M.target.selector)}</code></td>
1806
+ <td>${E(M._states.join(", "))}</td>
1807
+ </tr>`).join(""),$=l??new Set,U=new Set(y.filter(M=>!$.has(M.matchKey)).map(M=>M.ruleId)),F=new Set(U);for(const M of e){const j=M.axeRulesEvaluated;if(j){for(const R of j.passed)F.add(R.ruleId);for(const R of j.inapplicable)F.add(R.ruleId);for(const R of j.incomplete)F.add(R.ruleId)}}const T=new Map;for(const M of h??[])T.set(M.criterionId,M.verdict);const N=Ai.map(M=>{const{conformance:j,hits:R}=ki(M,U,F,c,T.get(M.ref));return`
1808
+ <tr class="${j==="Supports"?"supports":j==="Partially Supports"?"partial":j==="Not Evaluated"?"not-evaluated":"na"}">
1809
+ <td><code>${E(M.ref)}</code></td>
1810
+ <td>${E(M.title)} <span class="lvl">(${M.level})</span></td>
1811
+ <td>${j}</td>
1812
+ <td>${R.length>0?`Detected violations of: ${R.map(K=>`<code>${E(K)}</code>`).join(", ")}`:j==="Not Applicable"?"Not addressable by automated audit; requires manual review.":"No automated violations detected."}</td>
1813
+ </tr>`}).join("");return Ye(`<!doctype html>
1797
1814
  <html lang="en">
1798
1815
  <head>
1799
1816
  <meta charset="utf-8" />
@@ -1850,10 +1867,10 @@ ${o>0?`
1850
1867
  <h1>${E(u.title)}</h1>
1851
1868
  <p class="doc-meta">
1852
1869
  <strong>Subject:</strong> <code>${E(g)}</code> &nbsp;·&nbsp;
1853
- <strong>URL:</strong> ${E(f)}<br/>
1870
+ <strong>URL:</strong> ${E(p)}<br/>
1854
1871
  <strong>Audit date:</strong> ${E(s.toLocaleString())} &nbsp;·&nbsp;
1855
1872
  <strong>Audit engine:</strong> axe-core ${E(((D=e[0])==null?void 0:D.axeVersion)??"n/a")} &nbsp;·&nbsp;
1856
- <strong>Tool:</strong> ${E(Ie)} v${E($e())}
1873
+ <strong>Tool:</strong> ${E(xe)} v${E(Te())}
1857
1874
  </p>
1858
1875
 
1859
1876
  <nav class="toc">
@@ -1872,14 +1889,14 @@ ${o>0?`
1872
1889
 
1873
1890
  <h2 id="exec">1. Executive summary</h2>
1874
1891
  <div class="exec">
1875
- <div class="exec-grade" style="background: ${lh[v.combined]};">${v.combined}</div>
1892
+ <div class="exec-grade" style="background: ${kh[A.combined]};">${A.combined}</div>
1876
1893
  <div class="exec-body">
1877
- <span class="risk-tier" style="background: ${sh[w.risk]}; color: ${ch[w.risk]};">
1878
- ${E(ih[w.risk])}
1894
+ <span class="risk-tier" style="background: ${vh[w.risk]}; color: ${Ah[w.risk]};">
1895
+ ${E(wh[w.risk])}
1879
1896
  </span>
1880
1897
  <p style="margin: 0 0 6pt;"><strong>${w.totals.unique} unique violations detected</strong> across ${e.length} state combinations
1881
1898
  (${w.totals.critical} critical · ${w.totals.serious} serious · ${w.totals.moderate} moderate · ${w.totals.minor} minor).</p>
1882
- <p style="margin: 0; font-size: 10pt; color: #475569;">${E(Hs[w.risk])}</p>
1899
+ <p style="margin: 0; font-size: 10pt; color: #475569;">${E(Xs[w.risk])}</p>
1883
1900
  </div>
1884
1901
  </div>
1885
1902
  ${r&&(r.lead||r.body||r.closer)?`
@@ -1888,7 +1905,7 @@ ${r&&(r.lead||r.body||r.closer)?`
1888
1905
  AI-generated narrative summary (${E(r.model)})
1889
1906
  </p>
1890
1907
  ${r.lead?`<p style="margin: 0 0 8pt; font-size: 11.5pt; font-weight: 500;">${E(r.lead)}</p>`:""}
1891
- ${r.body?r.body.split(/\n{2,}/).map(O=>`<p style="margin: 0 0 6pt;">${E(O.trim())}</p>`).join(`
1908
+ ${r.body?r.body.split(/\n{2,}/).map(M=>`<p style="margin: 0 0 6pt;">${E(M.trim())}</p>`).join(`
1892
1909
  `):""}
1893
1910
  ${r.closer?`<p style="margin: 8pt 0 0; font-style: italic;">${E(r.closer)}</p>`:""}
1894
1911
  <p style="margin: 6pt 0 0; font-size: 9pt; color: #475569;">
@@ -1899,13 +1916,13 @@ ${r&&(r.lead||r.body||r.closer)?`
1899
1916
 
1900
1917
  <h3>Per-category breakdown (top-6 lawsuit-magnet categories)</h3>
1901
1918
  <div class="categories">
1902
- ${w.categories.map(O=>{const G=O.status==="pass"?"✓":O.status==="fail"?"✗":"⚠",R=O.status==="pass"?"cat-pass":O.status==="fail"?"cat-fail":"cat-unchecked",V=O.status==="fail"?` (${O.violationCount})`:O.needsManualCheck?" — requires manual review":"";return`<div class="${R}">${G} ${E(O.label)}${E(V)}</div>`}).join("")}
1919
+ ${w.categories.map(M=>{const j=M.status==="pass"?"✓":M.status==="fail"?"✗":"⚠",R=M.status==="pass"?"cat-pass":M.status==="fail"?"cat-fail":"cat-unchecked",V=M.status==="fail"?` (${M.violationCount})`:M.needsManualCheck?" — requires manual review":"";return`<div class="${R}">${j} ${E(M.label)}${E(V)}</div>`}).join("")}
1903
1920
  </div>
1904
1921
 
1905
1922
  <h2 id="methodology">2. Methodology &amp; scope</h2>
1906
1923
  <dl class="methodology">
1907
1924
  <dt>Audit engine</dt>
1908
- <dd>axe-core ${E(((j=e[0])==null?void 0:j.axeVersion)??"n/a")} (industry-standard, used by Deque, IBM, Microsoft, and Google's Lighthouse).
1925
+ <dd>axe-core ${E(((G=e[0])==null?void 0:G.axeVersion)??"n/a")} (industry-standard, used by Deque, IBM, Microsoft, and Google's Lighthouse).
1909
1926
  Rule sets enabled: WCAG 2.0 A/AA, WCAG 2.1 A/AA, WCAG 2.2 A/AA, axe-core best-practice.</dd>
1910
1927
  <dt>State coverage</dt>
1911
1928
  <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>),
@@ -1913,13 +1930,13 @@ ${r&&(r.lead||r.body||r.closer)?`
1913
1930
  text direction (LTR / RTL), and viewport breakpoints (mobile / tablet / desktop).
1914
1931
  Audits at non-default states are uncommon among automated tools and represent significant additional WCAG coverage relative to single-state audits.</dd>
1915
1932
  <dt>Audit duration</dt>
1916
- <dd>${(p/1e3).toFixed(1)} seconds across ${e.length} states (${(p/Math.max(1,e.length)).toFixed(0)}ms per state).</dd>
1933
+ <dd>${(f/1e3).toFixed(1)} seconds across ${e.length} states (${(f/Math.max(1,e.length)).toFixed(0)}ms per state).</dd>
1917
1934
  <dt>Scope</dt>
1918
1935
  <dd>Component / region: <code>${E(g)}</code></dd>
1919
1936
  <dt>What this audit covers</dt>
1920
- <dd>All 50 applicable WCAG 2.1 AA criteria evaluated across three layers: <strong>axe-core</strong> (rule-based DOM checks — color contrast, image alt presence, form labels, accessible names, document structure, ID uniqueness, ARIA validity), <strong>${E(Ie)} DOM analyzers</strong> (state-matrix iteration through hover / focus / active / disabled states + structural heuristics for criteria axe doesn't reach — pointer gestures, motion actuation, reflow, autoplay, character key shortcuts, redundant entry, sensory cues, text spacing, images-of-text candidates), and <strong>AI walkthroughs</strong> (real keyboard input + screenshot-by-screenshot vision judgment for the 6 criteria neither rule-based nor static-DOM analysis can decide — focus order, no keyboard trap, focus visible, meaningful sequence, non-text contrast, label-in-name).</dd>
1937
+ <dd>All 50 applicable WCAG 2.1 AA criteria evaluated across three layers: <strong>axe-core</strong> (rule-based DOM checks — color contrast, image alt presence, form labels, accessible names, document structure, ID uniqueness, ARIA validity), <strong>${E(xe)} DOM analyzers</strong> (state-matrix iteration through hover / focus / active / disabled states + structural heuristics for criteria axe doesn't reach — pointer gestures, motion actuation, reflow, autoplay, character key shortcuts, redundant entry, sensory cues, text spacing, images-of-text candidates), and <strong>AI walkthroughs</strong> (real keyboard input + screenshot-by-screenshot vision judgment for the 6 criteria neither rule-based nor static-DOM analysis can decide — focus order, no keyboard trap, focus visible, meaningful sequence, non-text contrast, label-in-name).</dd>
1921
1938
  <dt>What still requires human spot-checks</dt>
1922
- <dd>Content meaning, not structural correctness: whether caption text actually matches the audio, whether alt text accurately describes the image's purpose, whether sensory phrasing ("click the red button") has an inclusive alternative. The ${E(Ie)} Guided Tests workflows provide a structured manual-audit path for these content-meaning criteria.</dd>
1939
+ <dd>Content meaning, not structural correctness: whether caption text actually matches the audio, whether alt text accurately describes the image's purpose, whether sensory phrasing ("click the red button") has an inclusive alternative. The ${E(xe)} Guided Tests workflows provide a structured manual-audit path for these content-meaning criteria.</dd>
1923
1940
  </dl>
1924
1941
 
1925
1942
  <h2 id="findings">3. Detailed findings</h2>
@@ -1930,21 +1947,21 @@ ${S.length===0?"<p>No automated violations were detected during this audit.</p>"
1930
1947
  <tbody>${L}</tbody>
1931
1948
  </table>`}
1932
1949
 
1933
- ${th(w.walkthroughFindings,w.walkthroughAcknowledgements,d??[])}
1950
+ ${fh(w.walkthroughFindings,w.walkthroughAcknowledgements,d??[])}
1934
1951
 
1935
1952
  <h2 id="evidence">4. Visual evidence (per-state screenshots)</h2>
1936
- ${k.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 ${k.length} state${k.length===1?"":"s"} by violation count shown below. Red rectangles mark violation locations; numbers correspond to entries in §3.</p>`+k.map(O=>{const G=O.violations.filter(R=>R.target.boundingRect).map(R=>{const V=R.target.boundingRect,J=String(R._idx).length*8+14,X=22;return[`<rect class="violation-mark" x="${V.x}" y="${V.y}" width="${V.w}" height="${V.h}" />`,`<rect class="label-bg" x="${V.x}" y="${Math.max(0,V.y-X)}" width="${J}" height="${X}" rx="2" />`,`<text class="violation-num" x="${V.x+6}" y="${Math.max(0,V.y-X)+16}">${R._idx}</text>`].join("")}).join("");return`
1953
+ ${k.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 ${k.length} state${k.length===1?"":"s"} by violation count shown below. Red rectangles mark violation locations; numbers correspond to entries in §3.</p>`+k.map(M=>{const j=M.violations.filter(R=>R.target.boundingRect).map(R=>{const V=R.target.boundingRect,K=String(R._idx).length*8+14,Q=22;return[`<rect class="violation-mark" x="${V.x}" y="${V.y}" width="${V.w}" height="${V.h}" />`,`<rect class="label-bg" x="${V.x}" y="${Math.max(0,V.y-Q)}" width="${K}" height="${Q}" rx="2" />`,`<text class="violation-num" x="${V.x+6}" y="${Math.max(0,V.y-Q)+16}">${R._idx}</text>`].join("")}).join("");return`
1937
1954
  <div class="evidence-state">
1938
- <h3>State: ${E(O.stateLabel)} (${O.violations.length} violation${O.violations.length===1?"":"s"})</h3>
1955
+ <h3>State: ${E(M.stateLabel)} (${M.violations.length} violation${M.violations.length===1?"":"s"})</h3>
1939
1956
  <div class="evidence-frame">
1940
- <svg viewBox="0 0 ${Fo} ${_o}" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet">
1941
- <image href="${O.screenshotDataUrl}" x="0" y="0" width="${Fo}" height="${_o}" preserveAspectRatio="xMidYMid slice" />
1942
- ${G}
1957
+ <svg viewBox="0 0 ${Wo} ${Go}" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet">
1958
+ <image href="${M.screenshotDataUrl}" x="0" y="0" width="${Wo}" height="${Go}" preserveAspectRatio="xMidYMid slice" />
1959
+ ${j}
1943
1960
  </svg>
1944
1961
  </div>
1945
1962
  </div>`}).join("")}
1946
1963
 
1947
- ${uh(n)}
1964
+ ${Ih(n)}
1948
1965
 
1949
1966
  <h2 id="conformance">6. WCAG 2.1 conformance assessment</h2>
1950
1967
  <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>
@@ -1953,7 +1970,7 @@ ${uh(n)}
1953
1970
  <tbody>${N}</tbody>
1954
1971
  </table>
1955
1972
 
1956
- ${(()=>{const O=o!==void 0,G=O?"8":"7",R=O?"9":"8",V=O?`<h2 id="audit-history">7. Audit history</h2>
1973
+ ${(()=>{const M=o!==void 0,j=M?"8":"7",R=M?"9":"8",V=M?`<h2 id="audit-history">7. Audit history</h2>
1957
1974
  <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>
1958
1975
  ${o&&o.length>0?`<table>
1959
1976
  <thead>
@@ -1969,21 +1986,21 @@ ${o&&o.length>0?`<table>
1969
1986
  <th>Source</th>
1970
1987
  </tr>
1971
1988
  </thead>
1972
- <tbody>${o.map(X=>`
1989
+ <tbody>${o.map(Q=>`
1973
1990
  <tr>
1974
- <td style="white-space: nowrap;">${E(X.capturedAt)}</td>
1975
- <td><code>${E(X.componentId)}</code></td>
1976
- <td style="word-break: break-all;">${E(X.pageUrl)}</td>
1977
- <td><strong>${E(X.grade)}</strong></td>
1978
- <td>${X.totals.critical}</td>
1979
- <td>${X.totals.serious}</td>
1980
- <td>${X.statesAudited}</td>
1981
- <td><code title="${E(X.hash)}">${E(X.hash.slice(0,12))}…</code></td>
1982
- <td>${X.receipt?`RFC 3161 (${E(X.receipt.tsaName)})`:"Local"}</td>
1991
+ <td style="white-space: nowrap;">${E(Q.capturedAt)}</td>
1992
+ <td><code>${E(Q.componentId)}</code></td>
1993
+ <td style="word-break: break-all;">${E(Q.pageUrl)}</td>
1994
+ <td><strong>${E(Q.grade)}</strong></td>
1995
+ <td>${Q.totals.critical}</td>
1996
+ <td>${Q.totals.serious}</td>
1997
+ <td>${Q.statesAudited}</td>
1998
+ <td><code title="${E(Q.hash)}">${E(Q.hash.slice(0,12))}…</code></td>
1999
+ <td>${Q.receipt?`RFC 3161 (${E(Q.receipt.tsaName)})`:"Local"}</td>
1983
2000
  </tr>`).join("")}
1984
2001
  </tbody>
1985
2002
  </table>`:'<p style="color: #64748b; font-style: italic;">No prior audits recorded — this is the first audit on record for this installation.</p>'}
1986
- `:"",J=a==="evidence"?`<h2 id="remediation">${G}. Recommended approach before legal action</h2>
2003
+ `:"",K=a==="evidence"?`<h2 id="remediation">${j}. Recommended approach before legal action</h2>
1987
2004
  <div style="background: #f0f9ff; border: 1px solid #38bdf8; padding: 12pt 16pt; border-radius: 4pt; font-size: 10.5pt;">
1988
2005
  <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>
1989
2006
  <p style="margin: 0 0 8pt;"><strong>Recommended steps before considering legal action:</strong></p>
@@ -1994,17 +2011,17 @@ ${o&&o.length>0?`<table>
1994
2011
  <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>
1995
2012
  </ol>
1996
2013
  <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>
1997
- </div>`:`<h2 id="remediation">${G}. Remediation plan</h2>
2014
+ </div>`:`<h2 id="remediation">${j}. Remediation plan</h2>
1998
2015
  <p>Each violation in §3 has a documented canonical fix pattern. The remediation workflow we recommend:</p>
1999
2016
  <ol>
2000
2017
  <li>Findings are classified by severity. Address critical and serious findings first.</li>
2001
- <li>For each rule, ${E(Ie)} 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>
2018
+ <li>For each rule, ${E(xe)} 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>
2002
2019
  <li>After remediation, re-run the audit to verify each violation is resolved without introducing regressions.</li>
2003
2020
  <li>Manual workflows (keyboard, screen-reader, focus-management, forms) should be re-completed via the Guided Tests panel.</li>
2004
2021
  <li>Once resolved, the audited state should be accepted as a baseline; future audits surface only NEW debt vs. that baseline.</li>
2005
2022
  </ol>`;return`${V}
2006
2023
 
2007
- ${J}
2024
+ ${K}
2008
2025
 
2009
2026
  <h2 id="disclaimer">${R}. Disclaimer &amp; limitations</h2>`})()}
2010
2027
  <div class="disclaimer">
@@ -2014,22 +2031,24 @@ ${J}
2014
2031
  </div>
2015
2032
 
2016
2033
  <footer>
2017
- Generated by ${E(Ie)} v${E($e())} on ${E(s.toISOString())}.
2034
+ Generated by ${E(xe)} v${E(Te())} on ${E(s.toISOString())}.
2018
2035
  Audit run identifier: ${E(s.getTime().toString(36))}-${E(g.replace(/[^a-z0-9]/gi,"").slice(0,8))}.
2019
2036
  </footer>
2020
2037
 
2021
2038
  </body>
2022
- </html>`)}async function ph(){const t=(await chrome.storage.local.get("aiConfig")).aiConfig;return{...Wi,...t??{}}}async function fh(e){return kn(e.map(t=>t.componentId))}async function Ha(e,t,a,n){const o=await ph();if(!o.enabled||!o.apiKey||e.length===0)return null;const r=e.flatMap(h=>h.violations),i=Be(r,void 0,e,void 0,n),c=i.score,d=c.categories.filter(h=>h.status==="fail").sort((h,m)=>m.violationCount-h.violationCount).slice(0,3).map(h=>h.label),l=e[0];return di({framing:t,riskTier:c.risk,grade:i.combined,totals:{critical:c.totals.critical,serious:c.totals.serious,moderate:c.totals.moderate,minor:c.totals.minor,unique:c.totals.unique},riskDrivers:d,pageUrl:(l==null?void 0:l.pageUrl)??(l==null?void 0:l.scope)??"",auditDate:(l==null?void 0:l.startedAt)??new Date().toISOString(),priorAuditCount:a},o)}const gh={json:"exportJson",sarif:"exportSarif",junit:"exportJunit","html-print":"exportHtmlPrint",vpat:"exportVpat","ai-prompt":"exportAiPrompts","ai-prompt-owner":"exportAiPrompts","ai-prompt-site-crawl":"exportAiPrompts","defense-bundle":"exportDefenseBundle","evidence-bundle":"exportEvidenceBundle","developer-letter":"exportDeveloperLetter","owner-report":"exportOwnerReport","accessibility-statement-markdown":"exportEaaStatement","accessibility-statement-html":"exportEaaStatement","deposition-packet":"exportDepositionPacket","methodology-doc":"exportMethodologyDoc","conformance-crosswalk":"exportConformanceCrosswalk","ticket-bundle":"exportTicketBundle","executive-report":"exportExecutiveReport"};function mh(){return Se("EXPORT_REQUEST",async e=>{var a;if(await Fe())return{type:"EXPORT_RESPONSE",format:e.format,content:"wcagcheckr is in private build phase. A team license is required to export reports."};const t=gh[e.format];if(t){const n=await de();if(!le(n,t))return{type:"EXPORT_RESPONSE",format:e.format,content:`The ${e.format} export format is not available on your current plan.`}}if(e.siteCrawlSource&&e.format!=="ai-prompt-site-crawl"){const n=await Ht();if(!n||n.length===0)return{type:"EXPORT_RESPONSE",format:e.format,content:"No site-crawl results found in storage. Run a site crawl from the Crawl tab first, then try this export again."};e.results=n.flatMap(o=>o.results??[])}if(e.format==="sarif")return{type:"EXPORT_RESPONSE",format:"sarif",content:JSON.stringify(hi(e.results,e.delta),null,2)};if(e.format==="junit")return{type:"EXPORT_RESPONSE",format:"junit",content:fi(e.results,e.delta)};if(e.format==="html-print")return{type:"EXPORT_RESPONSE",format:"html-print",content:Nn(e.results,e.delta,e.wallClockMs)};if(e.format==="vpat"){const n=await Ee(e.results,e.auditTargetId?[e.auditTargetId]:[]),o=new Set(n.walkthroughAcks.map(i=>`${i.criterionId}::${i.pageUrl}`)),r=n.walkthroughs.map(i=>o.has(`${i.criterionId}::${i.pageUrl}`)?{...i,verdict:"pass",reasoning:`Human verified: ${i.reasoning}`}:i);return{type:"EXPORT_RESPONSE",format:"vpat",content:bi(e.results,e.delta,r,n.acknowledgedMatchKeys,n.humanCriterionVerdicts,n.incompleteResolutions)}}if(e.format==="ai-prompt"){const n=await Bs();let o="";const r=[];try{const c=((a=e.results[0])==null?void 0:a.componentId)??"",d=c?await Za(c):[];for(const l of d)r.push({criterionId:l.criterionId,verdict:l.verdict,reasoning:l.reasoning,pageUrl:l.pageUrl});o=bh(d)}catch(c){console.warn("[report-exporter] walkthrough render skipped:",c)}const i=yi(e.results,e.delta,e.manualRuns,e.siteCrawlReport,e.dismissedKeys,e.incompleteResolutions,n,r,e.acknowledgedKeys);return{type:"EXPORT_RESPONSE",format:"ai-prompt",content:o?`${i}
2039
+ </html>`)}async function Ch(){const t=(await chrome.storage.local.get("aiConfig")).aiConfig;return{...qi,...t??{}}}async function $h(e){return In(e.map(t=>t.componentId))}async function qa(e,t,a,n){const o=await Ch();if(!o.enabled||!o.apiKey||e.length===0)return null;const r=e.flatMap(h=>h.violations),i=Ke(r,void 0,e,void 0,n),c=i.score,d=c.categories.filter(h=>h.status==="fail").sort((h,m)=>m.violationCount-h.violationCount).slice(0,3).map(h=>h.label),l=e[0];return mi({framing:t,riskTier:c.risk,grade:i.combined,totals:{critical:c.totals.critical,serious:c.totals.serious,moderate:c.totals.moderate,minor:c.totals.minor,unique:c.totals.unique},riskDrivers:d,pageUrl:(l==null?void 0:l.pageUrl)??(l==null?void 0:l.scope)??"",auditDate:(l==null?void 0:l.startedAt)??new Date().toISOString(),priorAuditCount:a},o)}const Th={json:"exportJson",sarif:"exportSarif",junit:"exportJunit","html-print":"exportHtmlPrint",vpat:"exportVpat","ai-prompt":"exportAiPrompts","ai-prompt-owner":"exportAiPrompts","ai-prompt-site-crawl":"exportAiPrompts","defense-bundle":"exportDefenseBundle","evidence-bundle":"exportEvidenceBundle","developer-letter":"exportDeveloperLetter","owner-report":"exportOwnerReport","accessibility-statement-markdown":"exportEaaStatement","accessibility-statement-html":"exportEaaStatement","deposition-packet":"exportDepositionPacket","methodology-doc":"exportMethodologyDoc","conformance-crosswalk":"exportConformanceCrosswalk","ticket-bundle":"exportTicketBundle","executive-report":"exportExecutiveReport"};function Eh(){return Ce("EXPORT_REQUEST",async e=>{var a;if(await Ue())return{type:"EXPORT_RESPONSE",format:e.format,content:"wcagcheckr is in private build phase. A team license is required to export reports."};const t=Th[e.format];if(t){const n=await ue();if(!de(n,t))return{type:"EXPORT_RESPONSE",format:e.format,content:`The ${e.format} export format is not available on your current plan.`}}if(e.siteCrawlSource&&e.format!=="ai-prompt-site-crawl"){const n=await zt();if(!n||n.length===0)return{type:"EXPORT_RESPONSE",format:e.format,content:"No site-crawl results found in storage. Run a site crawl from the Crawl tab first, then try this export again."};e.results=n.flatMap(o=>o.results??[])}if(e.format==="sarif")return{type:"EXPORT_RESPONSE",format:"sarif",content:JSON.stringify(yi(e.results,e.delta),null,2)};if(e.format==="junit")return{type:"EXPORT_RESPONSE",format:"junit",content:vi(e.results,e.delta)};if(e.format==="html-print")return{type:"EXPORT_RESPONSE",format:"html-print",content:_n(e.results,e.delta,e.wallClockMs)};if(e.format==="vpat"){const n=await Oe(e.results,e.auditTargetId?[e.auditTargetId]:[]),o=new Set(n.walkthroughAcks.map(i=>`${i.criterionId}::${i.pageUrl}`)),r=n.walkthroughs.map(i=>o.has(`${i.criterionId}::${i.pageUrl}`)?{...i,verdict:"pass",reasoning:`Human verified: ${i.reasoning}`}:i);return{type:"EXPORT_RESPONSE",format:"vpat",content:xi(e.results,e.delta,r,n.acknowledgedMatchKeys,n.humanCriterionVerdicts,n.incompleteResolutions)}}if(e.format==="ai-prompt"){const n=await ec();let o="";const r=[];try{const c=((a=e.results[0])==null?void 0:a.componentId)??"",d=c?await tn(c):[];for(const l of d)r.push({criterionId:l.criterionId,verdict:l.verdict,reasoning:l.reasoning,pageUrl:l.pageUrl});o=Ri(d)}catch(c){console.warn("[report-exporter] walkthrough render skipped:",c)}const i=Ii(e.results,e.delta,e.manualRuns,e.siteCrawlReport,e.dismissedKeys,e.incompleteResolutions,n,r,e.acknowledgedKeys);return{type:"EXPORT_RESPONSE",format:"ai-prompt",content:o?`${i}
2023
2040
 
2024
- ${o}`:i}}if(e.format==="defense-bundle"){const n=await kt(),o=await Ee(e.results,e.auditTargetId?[e.auditTargetId]:[]),r=await Ha(e.results,"defense",n.length,o.walkthroughs);return{type:"EXPORT_RESPONSE",format:"defense-bundle",content:Rt(e.results,e.delta,"defense",e.manualRuns,n,r,e.wallClockMs,o.walkthroughs,o.walkthroughAcks,o.acknowledgedMatchKeys,o.humanCriterionVerdicts,o.incompleteResolutions)}}if(e.format==="deposition-packet"){const n=await kt(),o=await Ee(e.results,e.auditTargetId?[e.auditTargetId]:[]),r=await Ha(e.results,"defense",n.length,o.walkthroughs),i=await sa().catch(()=>null);return{type:"EXPORT_RESPONSE",format:"deposition-packet",content:(await ci({results:e.results,delta:e.delta,manualRuns:e.manualRuns,aiSummary:r,licenseId:i??void 0,wallClockMs:e.wallClockMs,interactiveAuditResults:o.walkthroughs,walkthroughAcks:o.walkthroughAcks,acknowledgedMatchKeys:o.acknowledgedMatchKeys,humanCriterionVerdicts:o.humanCriterionVerdicts,incompleteResolutions:o.incompleteResolutions})).html}}if(e.format==="methodology-doc")return{type:"EXPORT_RESPONSE",format:"methodology-doc",content:Fu({results:e.results,manualRuns:e.manualRuns})};if(e.format==="conformance-crosswalk")return{type:"EXPORT_RESPONSE",format:"conformance-crosswalk",content:Gu({results:e.results})};if(e.format==="ticket-bundle")return{type:"EXPORT_RESPONSE",format:"ticket-bundle",content:qu({results:e.results})};if(e.format==="executive-report"){const n=await Ee(e.results,e.auditTargetId?[e.auditTargetId]:[]);return{type:"EXPORT_RESPONSE",format:"executive-report",content:Xu(e.results,{...e.executiveInputs??{},interactiveAuditResults:n.walkthroughs,walkthroughAcks:n.walkthroughAcks,acknowledgedMatchKeys:n.acknowledgedMatchKeys,humanCriterionVerdicts:n.humanCriterionVerdicts,incompleteResolutions:n.incompleteResolutions})}}if(e.format==="evidence-bundle"){const n=await kt(),o=await fh(e.results),r=await Ha(e.results,"evidence",n.length,o);return{type:"EXPORT_RESPONSE",format:"evidence-bundle",content:wi(e.results,e.delta,e.manualRuns,n,r,e.wallClockMs,o)}}if(e.format==="developer-letter"){const n=await Ee(e.results,e.auditTargetId?[e.auditTargetId]:[]);return{type:"EXPORT_RESPONSE",format:"developer-letter",content:vi(e.results,n.walkthroughs,n.acknowledgedMatchKeys,n.walkthroughAcks,n.humanCriterionVerdicts,n.incompleteResolutions)}}if(e.format==="owner-report"){const n=await Ee(e.results,e.auditTargetId?[e.auditTargetId]:[]);return{type:"EXPORT_RESPONSE",format:"owner-report",content:Ai(e.results,n.walkthroughs,n.acknowledgedMatchKeys,n.walkthroughAcks,n.humanCriterionVerdicts,n.incompleteResolutions)}}if(e.format==="ai-prompt-owner"){const n=await Ee(e.results,e.auditTargetId?[e.auditTargetId]:[]);return{type:"EXPORT_RESPONSE",format:"ai-prompt-owner",content:ki(e.results,n.walkthroughs,n.acknowledgedMatchKeys,n.walkthroughAcks,n.humanCriterionVerdicts,n.incompleteResolutions)}}if(e.format==="accessibility-statement-markdown"||e.format==="accessibility-statement-html"){if(!e.statementInputs)return{type:"EXPORT_RESPONSE",format:e.format,content:"# Accessibility statement — error\n\nMissing `statementInputs` payload. The accessibility-statement export needs organization info (name, contact, conformance status). Provide it via the side panel UI or the EXPORT_REQUEST `statementInputs` field."};const n=e.format==="accessibility-statement-html"?"html":"markdown";try{const o=Ys({results:e.results,inputs:e.statementInputs,outputFormat:n,customDescriptions:e.statementCustomDescriptions});return{type:"EXPORT_RESPONSE",format:e.format,content:o}}catch(o){const r=o instanceof Error?o.message:String(o),c=n==="html"?`<article><h1>Accessibility statement — render error</h1><p>The statement render failed: <code>${r}</code>. Provide a complete <code>StatementInputs</code> via the side panel UI or EXPORT_REQUEST and try again.</p></article>`:`# Accessibility statement — render error
2041
+ ${o}`:i}}if(e.format==="defense-bundle"){const n=await St(),o=await Oe(e.results,e.auditTargetId?[e.auditTargetId]:[]),r=await qa(e.results,"defense",n.length,o.walkthroughs);return{type:"EXPORT_RESPONSE",format:"defense-bundle",content:Dt(e.results,e.delta,"defense",e.manualRuns,n,r,e.wallClockMs,o.walkthroughs,o.walkthroughAcks,o.acknowledgedMatchKeys,o.humanCriterionVerdicts,o.incompleteResolutions)}}if(e.format==="deposition-packet"){const n=await St(),o=await Oe(e.results,e.auditTargetId?[e.auditTargetId]:[]),r=await qa(e.results,"defense",n.length,o.walkthroughs),i=await da().catch(()=>null);return{type:"EXPORT_RESPONSE",format:"deposition-packet",content:(await pi({results:e.results,delta:e.delta,manualRuns:e.manualRuns,aiSummary:r,licenseId:i??void 0,wallClockMs:e.wallClockMs,interactiveAuditResults:o.walkthroughs,walkthroughAcks:o.walkthroughAcks,acknowledgedMatchKeys:o.acknowledgedMatchKeys,humanCriterionVerdicts:o.humanCriterionVerdicts,incompleteResolutions:o.incompleteResolutions})).html}}if(e.format==="methodology-doc")return{type:"EXPORT_RESPONSE",format:"methodology-doc",content:qu({results:e.results,manualRuns:e.manualRuns})};if(e.format==="conformance-crosswalk")return{type:"EXPORT_RESPONSE",format:"conformance-crosswalk",content:Qu({results:e.results})};if(e.format==="ticket-bundle")return{type:"EXPORT_RESPONSE",format:"ticket-bundle",content:ah({results:e.results})};if(e.format==="executive-report"){const n=await Oe(e.results,e.auditTargetId?[e.auditTargetId]:[]);return{type:"EXPORT_RESPONSE",format:"executive-report",content:ih(e.results,{...e.executiveInputs??{},interactiveAuditResults:n.walkthroughs,walkthroughAcks:n.walkthroughAcks,acknowledgedMatchKeys:n.acknowledgedMatchKeys,humanCriterionVerdicts:n.humanCriterionVerdicts,incompleteResolutions:n.incompleteResolutions})}}if(e.format==="evidence-bundle"){const n=await St(),o=await $h(e.results),r=await qa(e.results,"evidence",n.length,o);return{type:"EXPORT_RESPONSE",format:"evidence-bundle",content:Si(e.results,e.delta,e.manualRuns,n,r,e.wallClockMs,o)}}if(e.format==="developer-letter"){const n=await Oe(e.results,e.auditTargetId?[e.auditTargetId]:[]);return{type:"EXPORT_RESPONSE",format:"developer-letter",content:Ci(e.results,n.walkthroughs,n.acknowledgedMatchKeys,n.walkthroughAcks,n.humanCriterionVerdicts,n.incompleteResolutions)}}if(e.format==="owner-report"){const n=await Oe(e.results,e.auditTargetId?[e.auditTargetId]:[]);return{type:"EXPORT_RESPONSE",format:"owner-report",content:$i(e.results,n.walkthroughs,n.acknowledgedMatchKeys,n.walkthroughAcks,n.humanCriterionVerdicts,n.incompleteResolutions)}}if(e.format==="ai-prompt-owner"){const n=await Oe(e.results,e.auditTargetId?[e.auditTargetId]:[]);return{type:"EXPORT_RESPONSE",format:"ai-prompt-owner",content:Ti(e.results,n.walkthroughs,n.acknowledgedMatchKeys,n.walkthroughAcks,n.humanCriterionVerdicts,n.incompleteResolutions)}}if(e.format==="accessibility-statement-markdown"||e.format==="accessibility-statement-html"){if(!e.statementInputs)return{type:"EXPORT_RESPONSE",format:e.format,content:"# Accessibility statement — error\n\nMissing `statementInputs` payload. The accessibility-statement export needs organization info (name, contact, conformance status). Provide it via the side panel UI or the EXPORT_REQUEST `statementInputs` field."};const n=e.format==="accessibility-statement-html"?"html":"markdown";try{const o=oc({results:e.results,inputs:e.statementInputs,outputFormat:n,customDescriptions:e.statementCustomDescriptions});return{type:"EXPORT_RESPONSE",format:e.format,content:o}}catch(o){const r=o instanceof Error?o.message:String(o),c=n==="html"?`<article><h1>Accessibility statement — render error</h1><p>The statement render failed: <code>${r}</code>. Provide a complete <code>StatementInputs</code> via the side panel UI or EXPORT_REQUEST and try again.</p></article>`:`# Accessibility statement — render error
2025
2042
 
2026
- The statement render failed: \`${r}\`. Provide a complete \`StatementInputs\` via the side panel UI or EXPORT_REQUEST and try again.`;return{type:"EXPORT_RESPONSE",format:e.format,content:c}}}if(e.format==="ai-prompt-site-crawl"){const[n,o]=await Promise.all([bn(),Ht()]);if(!n||!o||o.length===0)return{type:"EXPORT_RESPONSE",format:"ai-prompt-site-crawl",content:`# Accessibility Fix Request — Site Crawl
2043
+ The statement render failed: \`${r}\`. Provide a complete \`StatementInputs\` via the side panel UI or EXPORT_REQUEST and try again.`;return{type:"EXPORT_RESPONSE",format:e.format,content:c}}}if(e.format==="ai-prompt-site-crawl"){const[n,o]=await Promise.all([wn(),zt()]);if(!n||!o||o.length===0)return{type:"EXPORT_RESPONSE",format:"ai-prompt-site-crawl",content:`# Accessibility Fix Request — Site Crawl
2027
2044
 
2028
2045
  No crawl results found in storage. Run a site crawl from the Crawl tab and try again.
2029
- `};if(e.ownerLawfare){const u=await Ee(o.flatMap(f=>f.results)),g=Xs(o,u.acknowledgedMatchKeys);return{type:"EXPORT_RESPONSE",format:"ai-prompt-site-crawl",content:xi(o,g,u.acknowledgedMatchKeys)}}const r=new Map;await Promise.all(o.map(async u=>{if(u.componentId)try{const g=await Za(u.componentId);r.set(u.componentId,g.map(f=>({criterionId:f.criterionId,verdict:f.verdict,reasoning:f.reasoning,pageUrl:f.pageUrl})))}catch{}}));const{getAllResolutions:i,applySelfCorrectionSalvage:c,applyContrastMathSalvage:d}=await dn(async()=>{const{getAllResolutions:u,applySelfCorrectionSalvage:g,applyContrastMathSalvage:f}=await Promise.resolve().then(()=>Ds);return{getAllResolutions:u,applySelfCorrectionSalvage:g,applyContrastMathSalvage:f}},void 0),h=(await i()).map(c).map(d),m=new Map;for(const u of h){if(u.verdict!=="fail")continue;const g=Fn(u.pageUrl),f=m.get(g)??[];f.push(u),m.set(g,f)}return{type:"EXPORT_RESPONSE",format:"ai-prompt-site-crawl",content:Si(o,n,r,m)}}return{type:"EXPORT_RESPONSE",format:"json",content:JSON.stringify(ui(e.results,e.delta),null,2)}})}function bh(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 a of e)if(!(!a||typeof a!="object"))try{const n=a.verdict==="pass"||a.verdict==="fail"?a.verdict:"uncertain",o=n==="pass"?"PASS":n==="fail"?"FAIL":"? UNCERTAIN",r=String(a.criterionId??"?"),i=r==="2.4.3"?"Focus Order (Level A)":r==="2.1.2"?"No Keyboard Trap (Level A)":r==="2.4.7"?"Focus Visible (Level AA)":r==="1.3.2"?"Meaningful Sequence (Level A)":r==="1.4.11"?"Non-text Contrast (Level AA)":r==="2.5.3"?"Label in Name (Level A)":r,c=typeof a.confidence=="number"?Math.max(0,Math.min(1,a.confidence)):0,d=typeof a.reasoning=="string"?a.reasoning:"",l=typeof a.model=="string"?a.model:"unknown";t.push(`### WCAG ${r} — ${i} — ${o}`),t.push(""),t.push(`**AI reasoning** (${Math.round(c*100)}% confidence, model ${l}):`),t.push(""),t.push(d?`> ${d.replace(/\n/g,`
2030
- > `)}`:"> (no reasoning recorded)"),t.push(""),n==="fail"?(r==="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."),t.push(""),t.push('**Anti-patterns to avoid:** adding `tabindex="1"`, `"2"`, etc. (positive values create a parallel focus order that\'s a maintenance nightmare); using `tabindex="-1"` to skip elements that should be reachable; adding JS focus() management to "fix" what DOM-order should solve directly.'),t.push(""),t.push("**Verify by re-auditing:** after the fix, the 2.4.3 walkthrough should return pass.")):r==="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."),t.push(""),t.push("**Anti-patterns to avoid:** suppressing Tab in keydown handlers (event.preventDefault on Tab without specific intent); using `inert` on the wrong container so users can't reach controls that should be focusable."),t.push(""),t.push("**Verify by re-auditing:** after the fix, the 2.1.2 walkthrough should return pass — Tab should cycle forward AND Shift+Tab should cycle backward through every focusable element on the page.")):r==="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(""),t.push("**Anti-patterns to avoid:** `outline: 0 !important` in a global reset without a `:focus-visible` replacement; relying on `:focus` instead of `:focus-visible` (the latter is the correct hook for keyboard-only focus styling); using ONLY a background color change with insufficient contrast (3:1 minimum against the unfocused state)."),t.push(""),t.push("**Verify by re-auditing:** after the fix, the 2.4.7 walkthrough should return pass on every captured focus state.")):r==="1.3.2"?(t.push("**Fix this:** ensure the DOM source order matches the visual reading order. Screen readers read DOM order; CSS (`flex-direction: row-reverse`, `order:`, `grid-area`, absolute positioning) does NOT reorder what the SR hears. Reorder the HTML so the source flows in the same direction the eye reads, then use minimal CSS for visual placement. Where DOM-reorder is genuinely not feasible (legitimate multi-column footers, RTL emulation, sticky-banner overlays), document the decision and verify the SR experience is still coherent."),t.push(""),t.push('**Anti-patterns to avoid:** using `tabindex` to "fix" reading order (tabindex affects keyboard order, not screen-reader order); adding `aria-flowto` (broadly unsupported; not a real fix); leaving inline `display: none` content that holds the "logical" reading order (it won\'t reach the SR).'),t.push(""),t.push("**Verify by re-auditing:** after the fix, the 1.3.2 walkthrough should return pass — the AI judges whether DOM-flagged divergences are real reading-order breaks vs. legitimate layout patterns.")):r==="1.4.11"?(t.push("**Fix this:** every UI component (button border, form input border, focus ring, icon, status indicator, graphical content needed for understanding) must have at least 3:1 contrast against adjacent colors. For each failed element: raise the foreground/border color, add a darker/lighter background to increase contrast, or thicken the indicator (e.g., 2px instead of 1px border). The AI vision identified specific elements; fix the CSS for each."),t.push(""),t.push('**Anti-patterns to avoid:** ONLY changing opacity/transparency (the rendered contrast on a varied background still fails); using box-shadow as a "border" without ensuring the shadow color meets 3:1 against the surrounding pixels; relying on hover state for contrast (must be visible in default state too).'),t.push(""),t.push("**Verify by re-auditing:** after the fix, the 1.4.11 walkthrough should return pass. Re-run with a fresh screenshot; AI will re-judge the updated colors.")):r==="2.5.3"&&(t.push('**Fix this:** every interactive control\'s accessible name must contain its visible text (case-insensitive, allowing icons/punctuation to be ignored). The AI listed which elements failed. For each, choose ONE of: (a) ensure the visible text matches the accessible name (e.g., button text); (b) use `aria-label` or `aria-labelledby` that STARTS WITH the visible text (e.g., `aria-label="Search — search the catalog"` when visible text is "Search"); (c) remove the conflicting `aria-label` and let the textContent serve as the name. Test by saying "click [visible text]" with speech recognition (Voice Control / Dragon) — the correct fix activates the right control.'),t.push(""),t.push('**Anti-patterns to avoid:** using `aria-label` that REPLACES the visible text entirely (e.g., visible "Cancel" + `aria-label="Close form"` — speech-recognition users saying "click Cancel" will fail); using `title=` attribute alone (not all SRs respect it as the accessible name); using `aria-describedby` for naming (that\'s for descriptions, not names).'),t.push(""),t.push("**Verify by re-auditing:** after the fix, the 2.5.3 walkthrough should return pass — the DOM walker re-collects {visibleText, accessibleName} pairs and AI re-judges ambiguous cases.")),t.push("")):n==="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 m=(Array.isArray(a.steps)?a.steps:[]).filter(u=>u&&u.action!=="initial");if(m.length>0){t.push(`<details><summary>Recorded ${m.length}-step keyboard walkthrough</summary>`),t.push(""),t.push("| # | Action | Selector | Role | Accessible name |"),t.push("|---|---|---|---|---|");for(const u of m.slice(0,30)){const g=u.focused??{selector:"?",role:"",name:""},f=String(g.selector??"?"),p=String(g.role??"-")||"-",y=String(g.name??"(no name)").replace(/\|/g,"\\|").slice(0,60),b=String(u.action??""),v=typeof u.ordinal=="number"?u.ordinal:"?";t.push(`| ${v} | ${b} | \`${f}\` | ${p} | ${y} |`)}m.length>30&&t.push(`| … | ${m.length-30} more steps elided | | | |`),t.push(""),t.push("</details>"),t.push("")}}catch(n){console.warn("[report-exporter] walkthrough render skipped:",n)}return t.push("---"),t.join(`
2031
- `)}function yh(e){var a;const t=new Set;for(const n of e){const o=n.currentState;if(!o)continue;const r=[];o.pseudoState&&o.pseudoState!=="default"&&r.push(o.pseudoState),o.theme&&o.theme!=="light"&&r.push(o.theme),o.direction&&o.direction!=="ltr"&&r.push(o.direction),(a=o.breakpoint)!=null&&a.id&&o.breakpoint.id!=="desktop"&&r.push(o.breakpoint.id),o.ariaVariation&&o.ariaVariation.id!=="none"&&r.push(`aria=${o.ariaVariation.id}`),t.add(r.length===0?"default":r.join("/"))}return{stateCount:t.size,states:Array.from(t).sort()}}const wh=/-needs-site-crawl$/;function Fn(e){try{const t=new URL(e);t.hash="";let a=t.toString();return a.endsWith("/")&&t.pathname!=="/"&&(a=a.slice(0,-1)),a}catch{return e}}function xi(e,t,a=new Set){var l;const n=new Map;for(const h of e){const m=h.results.flatMap(u=>u.violations).filter(u=>!u.needsReview&&!a.has(u.matchKey)&&xr(u.ruleId));for(const u of m){const g=n.get(u.ruleId)??{vs:[],urls:new Set};g.vs.push(u),g.urls.add(h.url),n.set(u.ruleId,g)}}const o={critical:0,serious:1,moderate:2,minor:3},r=Array.from(n.entries()).sort(([,h],[,m])=>(o[h.vs[0].impact]??99)-(o[m.vs[0].impact]??99)),i=r.length,c=[];if(c.push("# Help me reduce my website’s lawsuit-target risk"),c.push(""),c.push("You are an accessibility advisor helping me — a non-technical site owner — lower my exposure to the automated tools that ADA demand-letter firms use to find targets. I crawled my whole site and the findings below are ONLY the issues those automated scanners commonly flag. Please explain in plain English."),c.push(""),c.push("> **Scope.** This is the lawsuit-target (lawfare) view, not a full WCAG conformance audit. It covers the whole crawled site. Fixing everything here gets me off the easy-target list; it is not the same as a formal WCAG 2.1 AA conformance claim (for that I’d use Power or Dev mode in the auditor)."),c.push(""),c.push("## What I need from you"),c.push(""),c.push("1. **First, ask what I use to manage my site** (Squarespace, Wix, Shopify, WordPress + theme, Webflow, custom, etc.) — your advice differs per platform."),c.push("2. **For each finding:** tell me whether **I can fix it myself** in my editor (colors, alt text, link/button labels, page titles/language) or whether **my developer needs to** — and give me a short paragraph I can forward."),c.push("3. **If a finding spans multiple pages, it’s probably a shared template/theme element** — fixing it once helps every page."),c.push(""),c.push("## Site-wide lawsuit-target summary"),c.push(""),c.push(`- **Pages crawled:** ${e.length}`),c.push(`- **Lawsuit-target grade:** ${t.site.letter} — ${t.site.tier} risk`),c.push(`- **${t.site.headline}**`),c.push(`- **Lawsuit-target findings site-wide:** ${i}`),c.push(""),c.push("## The findings, in plain language"),c.push(""),i===0)return c.push("No findings of the kind automated demand-letter scanners commonly flag (missing image alt text, low color contrast, unlabeled buttons/links, missing page language or title). That keeps the site off the easy-target list. This is not a guarantee — scanners add checks over time, and a full WCAG conformance review (Power/Dev mode) covers far more. Re-crawl as the site changes."),c.push(""),c.push("Since there’s nothing to fix here, help me understand: what should I keep an eye on as I add new pages or content, to stay off the radar?"),c.join(`
2032
- `);let d=1;for(const[h,{vs:m,urls:u}]of r){const g=(l=m.find(b=>b.target.opacityContext))==null?void 0:l.target.opacityContext,f=Sn(h,g),p=m[0].impact,y=Array.from(u);c.push(`### ${d}. [${p.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 (${y.length} ${y.length===1?"page":"pages"}${y.length>1?" — likely a shared template element, fix once":""}):**`);for(const b of y.slice(0,8))c.push(`- ${b}`);y.length>8&&c.push(`- (and ${y.length-8} more pages)`),c.push(""),c.push(`*Technical reference for your developer:* axe-core rule \`${h}\` — https://dequeuniversity.com/rules/axe/4.11/${h}`),c.push(""),d++}return c.push("---"),c.push(""),c.push("## How to use this"),c.push(""),c.push("Walk through these with me one at a time. Start by asking which platform I use, then for each finding tell me if it’s a self-fix or a developer task, and help me knock out the shared-template items first since they clear multiple pages at once."),c.push(""),c.push(`_Generated by wcagcheckr v${$e()} — site crawl (owner / lawsuit-target lens, ${e.length} pages)._`),c.join(`
2033
- `)}function Si(e,t,a=new Map,n=new Map){var l;const o=[];o.push("# Accessibility Fix Request — Site Crawl"),o.push(""),o.push(`You are a senior accessibility engineer pairing with the user on their codebase. The user has crawled ${t.pagesAudited} page${t.pagesAudited===1?"":"s"} on this site and below you'll find the per-page WCAG findings + the cross-page consistency findings. Your task: walk page by page, apply fixes, and produce one atomic commit per fix.`),o.push(""),o.push(`> **Note on this report's scope.** This is a triage + bulk-fix prompt covering all crawled pages in one document. The user can also drop into any single page in the wcagcheckr side panel for per-finding acknowledgements, guided manual checks, fix-preview overlays, and interactive walkthrough re-runs — those are intentionally NOT in this report; they live in the per-page UI surface. If you need that level of control for a specific finding, suggest the user click that page in the Crawl tab's "Pages, worst first" list.`),o.push("");let r=0;for(const h of n.values())r+=h.length;const i=t.totalUniqueViolations+r;o.push("## Work contract — read before doing anything else"),o.push(""),o.push(`**Below you have ${i} finding${i===1?"":"s"} across ${t.pagesAudited} page${t.pagesAudited===1?"":"s"}** (${t.totalUniqueViolations} axe violation${t.totalUniqueViolations===1?"":"s"}${r>0?` + ${r} pixel-contrast-sampler fail${r===1?"":"s"}`:""}). When you respond, every numbered finding needs an outcome. Do not stop early. Do not declare done without addressing every finding.`),o.push(""),o.push("**For each numbered finding, produce one of:**"),o.push(""),o.push("- **Applied** — the canonical fix was applied. Cite the file + line. If one shared-component fix resolves N findings, group them under the SAME fix and list which findings it covers."),o.push("- **Blocked** — explicitly state WHY (missing info, ambiguous user content, dependency change required). One sentence per blocked finding."),o.push("- **Needs user decision** — list the options inline."),o.push(""),o.push(`**The single answer you cannot use: "skipped" or "didn't get to it."** Stop and tell the user if you ran out of context — do not silently omit items.`),o.push(""),o.push("**Required final response format** — completion table:"),o.push(""),o.push("```"),o.push("| # | Rule ID | Page(s) | Status | Notes |"),o.push("|---|---------|---------|--------|-------|"),o.push("| 1 | <rule> | <url> | / / | <file:line OR reason> |"),o.push(`| ${i} | ... | ... | ... | ... |`),o.push("```"),o.push(""),o.push("**Every row must be present.** One pass through this prompt should produce a fix or a clear next action for every finding — not a partial sweep that forces the user to re-run."),o.push(""),o.push("---"),o.push(""),o.push(Zu),o.push("---"),o.push(""),o.push("## Site-wide summary"),o.push(""),o.push(`- **Start URL:** ${t.startUrl}`),o.push(`- **Pages audited:** ${t.pagesAudited}${t.pagesFailed>0?` (${t.pagesFailed} failed)`:""}`),o.push(`- **Site grade:** ${t.siteGrade}`),o.push(`- **Total findings site-wide:** ${i} (${t.totalUniqueViolations} axe, ${r} pixel-contrast-sampler)`),o.push(`- **Severity breakdown:** ${t.totals.critical} critical · ${t.totals.serious+r} serious · ${t.totals.moderate} moderate · ${t.totals.minor} minor`),o.push("");const c=t.topViolations.filter(h=>!/-needs-site-crawl$/.test(h.ruleId));if(c.length>0){o.push("**Most-frequent rules across the site** (fixing one of these in a shared component / layout resolves N pages at once):"),o.push("");for(const h of c.slice(0,8)){const u=h.urlsAffected>=Math.ceil(t.pagesAudited/2)?" — **likely a single shared-component fix**":"";o.push(`- \`${h.ruleId}\` — ${h.impact} — found on ${h.urlsAffected} page${h.urlsAffected===1?"":"s"} (${h.totalOccurrences} total instance${h.totalOccurrences===1?"":"s"})${u}`)}o.push("")}const d=t.consistencyFindings??[];if(d.length>0){o.push("## Cross-page consistency findings"),o.push(""),o.push("These are divergences detected by comparing structure across pages (WCAG 3.2.3, 3.2.4, 3.2.6, 3.3.7). Each finding references multiple pages — typically the fix is in a SHARED component (header / nav / footer / form)."),o.push("");for(const h of d){const m=h.aiVerdict?h.aiVerdict.verdict==="uncertain"?" _(AI: uncertain — verify manually)_":` _(AI: confirmed real, confidence ${(h.aiVerdict.confidence*100).toFixed(0)}%)_`:"";o.push(`### ${h.wcagCriterion} ${h.impact} — ${h.description}${m}`),o.push("");for(const u of h.details.split(`
2034
- `))o.push(u);(l=h.aiVerdict)!=null&&l.reasoning&&(o.push(""),o.push(`AI reasoning: ${h.aiVerdict.reasoning}`)),o.push("")}}else o.push("## Cross-page consistency findings"),o.push(""),o.push("No cross-page divergences detected by the heuristic comparators. Still verify manually that nav order, accessible names of shared components, and help-mechanism position are consistent across pages."),o.push("");return o.push("---"),o.push(""),o.push(`## Per-page findings (${e.length} page${e.length===1?"":"s"})`),o.push(""),o.push("Each page section below is shaped like a single-page audit report — violations to fix + relevant recipes. Fix one page at a time. The cross-page findings above usually point at a SHARED component; fix that once and re-run the audit to see N pages resolve together."),o.push(""),e.forEach((h,m)=>{const u=m+1;if(o.push(`### Page ${u} of ${e.length}: ${h.url}`),o.push(""),h.error){o.push(`> ⚠️ Audit failed for this page: \`${h.error}\``),o.push(""),o.push("---"),o.push("");return}const g=h.results.flatMap(A=>A.violations).filter(A=>!wh.test(A.ruleId)),f=n.get(Fn(h.url))??[];if(g.length===0&&f.length===0){o.push("No automated violations detected on this page. Still apply the manual checklist below."),o.push("");const A=a.get(h.componentId)??[];if(A.length>0){o.push("**AI walkthrough verdicts on this page:**");for(const S of A){const k=S.verdict==="pass"?"":S.verdict==="fail"?"":"?";o.push(`- ${k} WCAG ${S.criterionId} — ${S.verdict.toUpperCase()}`)}o.push("")}o.push("---"),o.push("");return}const p=new Map,y=new Map;for(const A of g){const S=Oe(A.ruleId,A.target.selector);p.has(S)||p.set(S,A);const k=y.get(S)??[];k.push(A),y.set(S,k)}const b=Array.from(p.entries()).map(([A,S])=>({v:S,instances:y.get(A)??[S]})).sort((A,S)=>{const k={critical:0,serious:1,moderate:2,minor:3};return(k[A.v.impact]??99)-(k[S.v.impact]??99)}),v={critical:0,serious:0,moderate:0,minor:0};for(const{v:A}of b)v[A.impact]++;v.serious+=f.length;const w=b.length+f.length;o.push(`**${w} finding${w===1?"":"s"}** · severity: ${v.critical} critical · ${v.serious} serious · ${v.moderate} moderate · ${v.minor} minor`),o.push(""),b.forEach(({v:A,instances:S},k)=>{const I=at[A.ruleId],L=Po(`${A.ruleId} ${A.wcagCriterion}`)==="human-verify"?"[Human-verify]":"[AI-fixable]";o.push(`#### ${u}.${k+1} \`${A.ruleId}\` — ${A.impact} — WCAG ${A.wcagCriterion} ${A.wcagLevel} — ${L}`),o.push(""),o.push(`**Description:** ${A.description}`),o.push(""),o.push(`**Selector:** \`${A.target.selector}\``);const{stateCount:$,states:_}=yh(S);$>1?(o.push(""),o.push(`_Failed in ${$} matrix state${$===1?"":"s"}:_ ${_.join(", ")}`)):S.length>1&&(o.push(""),o.push(`_${S.length} element instances on this page (same state)._`)),o.push(""),A.target.failureSummary&&(o.push("**Diagnostic:**"),o.push("```"),o.push(A.target.failureSummary.trim()),o.push("```"),o.push("")),A.target.outerHTML&&(o.push("**Element HTML:**"),o.push("```html"),o.push(A.target.outerHTML.slice(0,500)),o.push("```"),o.push("")),I!=null&&I.summary&&(o.push("**Recipe:**"),o.push(I.summary),I.snippet&&(o.push(""),o.push("```"+(I.snippetLang??"")),o.push(I.snippet),o.push("```")),o.push("")),A.helpUrl&&(o.push(`**More info:** ${A.helpUrl}`),o.push(""))}),f.forEach((A,S)=>{const k=b.length+S,I=Po(`${A.ruleId} ${A.wcagCriterion??"1.4.3"}`)==="human-verify"?"[Human-verify]":"[AI-fixable]";o.push(`#### ${u}.${k+1} \`${A.ruleId}\` — serious — WCAG ${A.wcagCriterion??"1.4.3"} AA — ${I}`),o.push(""),o.push("**Description:** Empirical pixel-contrast measurement below the AA threshold (pixel-contrast-sampler)."),o.push(""),o.push(`**Selector:** \`${A.selector}\``),o.push(""),o.push("**Diagnostic:**"),o.push("```"),o.push(A.reasoning.trim()),o.push("```"),o.push(""),o.push("**Recipe:**"),o.push('Pixel-level contrast measurement — the sampler diffed hide-text vs visible viewport screenshots and recovered the rendered foreground/background colors at the worst-contrast pixel. Verify the live computed colors at the cited selector (devtools Computed → color / background-color). Fix by raising the foreground contrast against the actual rendered background (account for transparency, gradients, and overlay layers). If the sampler caught a transient state (loading skeleton, hover) and the steady-state contrast is fine, surface that as a "Blocked" entry with a note.'),o.push("")});const s=a.get(h.componentId)??[];if(s.length>0){o.push("**AI walkthrough verdicts on this page:**");for(const A of s){const S=A.verdict==="pass"?"✓":A.verdict==="fail"?"✗":"?";o.push(`- ${S} WCAG ${A.criterionId} — ${A.verdict.toUpperCase()}: ${A.reasoning.slice(0,240)}${A.reasoning.length>240?"…":""}`)}o.push("")}o.push("---"),o.push("")}),o.push("## Combined verification checklist"),o.push(""),o.push("After all per-page fixes:"),o.push(""),o.push("1. **Re-run the site crawl** at the same depth used here."),o.push("2. Verify the **cross-page consistency findings** above all resolve (or document why a flagged divergence is intentional)."),o.push("3. Verify the **top-frequency rules** across pages resolve in bulk (a single shared-component fix should drop the count on multiple pages)."),o.push("4. **Manual WCAG checks** (audio/video transcripts, character key shortcuts, timing controls, etc.) — the per-page sections above only contain automatable findings. The non-automatable WCAG criteria still apply per page."),o.push("5. **Diff review** — every fix should be reviewable in under 10 minutes. One atomic commit per fix."),o.push(""),o.push(`_Generated by ${Ie} v${$e()} — site crawl mode (${e.length} pages)._`),o.join(`
2035
- `)}const up=Object.freeze(Object.defineProperty({__proto__:null,normUrlKey:Fn,registerReportExporterHandlers:mh,toAiPrompt:yi,toAiPromptForSiteCrawl:Si,toAiPromptOwner:ki,toDefenseBundle:Rt,toDeveloperLetter:vi,toEvidenceBundle:wi,toHtmlPrint:Nn,toJUnit:fi,toJson:ui,toLawfarePromptForSiteCrawl:xi,toOwnerReport:Ai,toSarif:hi,toVpat:bi,toolVersion:$e},Symbol.toStringTag,{value:"Module"})),jt=te("sw-auto-export");function vh(e,t,a){const n=Gi(e,a);let o="audit";try{o=new URL(t).hostname.replace(/^www\./,"").replace(/[^a-z0-9.-]/gi,"-")}catch{}return`wcagcheckr/${o}/${n}`}async function Ah(e,t,a){try{if(e==="html-print")return Nn(t,a);if(e==="defense-bundle"){const n=await kt().catch(()=>[]);return Rt(t,a,"defense",void 0,n)}if(e==="deposition-packet")return(await ci({results:t,delta:a,manualRuns:void 0,aiSummary:null})).html}catch(n){return jt.warn(`buildContent failed for format=${e}`,n),null}return null}async function kh(e,t){var a,n,o;if(e.length!==0)try{const r=await Vi();if(!r.enabled||await Fe())return;const i=await de().catch(()=>"free");if(!le(i,"autoExportAuditReports"))return;const c=((a=e[0])==null?void 0:a.pageUrl)??((n=e[0])==null?void 0:n.scope)??"",d=((o=e[0])==null?void 0:o.componentId)??null;for(const l of r.formats)try{const h=await Ah(l,e,t);if(!h||h.length<200)continue;const m=vh(l,c,d),g=`data:${l==="ai-prompt"?"text/markdown":"text/html"};charset=utf-8,${encodeURIComponent(h)}`,f=await chrome.downloads.download({url:g,filename:m,saveAs:!1,conflictAction:"uniquify"});jt.info(`scheduled auto-export started (id=${f}, file=${m})`),c&&await ji({url:c,exportedAt:new Date().toISOString(),format:l,filename:m})}catch(h){jt.warn(`format ${l} failed in scheduled auto-export`,h)}}catch(r){jt.warn("autoExportFromScheduledRun failed (audit itself still succeeded)",r)}}const Le=te("scheduled-audit-runner"),ia="scheduled-audit:",Uo={hourly:60,"every-4-hours":240,daily:1440,weekly:10080};function _n(e){return`${ia}${e}`}function hp(e){return e.startsWith(ia)?e.slice(ia.length):null}async function Ih(e){const t=_n(e.id);await chrome.alarms.create(t,{periodInMinutes:Uo[e.cadence],delayInMinutes:Uo[e.cadence]})}async function pp(e){await chrome.alarms.clear(_n(e))}async function fp(){try{const{listScheduledAudits:e}=await dn(async()=>{const{listScheduledAudits:o}=await Promise.resolve().then(()=>us);return{listScheduledAudits:o}},void 0),t=await e(),a=await chrome.alarms.getAll(),n=new Set(t.filter(o=>o.enabled).map(o=>_n(o.id)));for(const o of a)o.name.startsWith(ia)&&!n.has(o.name)&&await chrome.alarms.clear(o.name);for(const o of t)o.enabled&&await Ih(o);Le.info(`reconciled ${t.length} scheduled-audit alarms`)}catch(e){Le.warn("failed to reconcile scheduled alarms",e)}}async function gp(e){var r;const t=Date.now(),a=await Ko(e);if(!a){Le.warn(`scheduled audit ${e} not found — alarm fired for deleted schedule`);return}if(!a.enabled){Le.debug(`scheduled audit ${e} is disabled — skipping`);return}if(await Fe()){await vt(a,{result:"failed",error:He,durationMs:Date.now()-t,totalViolations:0,newVsBaselineViolations:0});return}const n=await de();if(!le(n,"scheduledAudits")){await vt(a,{result:"failed",error:"Scheduled audits are not available on your current plan.",durationMs:Date.now()-t,totalViolations:0,newVsBaselineViolations:0});return}await mn({...a,lastResult:"running",lastRunAt:new Date().toISOString()});let o;try{if(o=(await chrome.tabs.create({url:a.url,active:!1})).id,!o)throw new Error("chrome.tabs.create returned no id");await xh(o,3e4);const d=await it("html",{isCanceled:()=>!1},0,void 0,{asSubroutine:!0,tabIdOverride:o});if(!d.ok){await vt(a,{result:"failed",error:d.error??"audit failed",durationMs:Date.now()-t,totalViolations:0,newVsBaselineViolations:0});return}const l=d.results.reduce((g,f)=>g+f.violations.length,0),h=(r=d.results[0])==null?void 0:r.delta,m=(h==null?void 0:h.newCount)??0,u=new Date().toISOString();if(await vt(a,{result:"ok",durationMs:Date.now()-t,totalViolations:l,newVsBaselineViolations:m}),kh(d.results,h),m>0)try{const g=await hu({scheduleId:a.id,auditedUrl:a.url,ranAt:u,results:d.results});Le.debug(`alert outcome for ${a.id}`,g)}catch(g){Le.warn(`alert fire threw for ${a.id}`,g)}}catch(i){const c=i instanceof Error?i.message:String(i);Le.warn(`scheduled audit ${e} failed:`,c),await vt(a,{result:"failed",error:c,durationMs:Date.now()-t,totalViolations:0,newVsBaselineViolations:0})}finally{if(o!==void 0)try{await chrome.tabs.remove(o)}catch{}}}async function vt(e,t){const a=new Date().toISOString();await Yo({scheduleId:e.id,ranAt:a,durationMs:t.durationMs,totalViolations:t.totalViolations,newVsBaselineViolations:t.newVsBaselineViolations,error:t.error}),await mn({...e,lastRunAt:a,lastResult:t.result,lastError:t.error,lastTotalViolations:t.totalViolations,lastNewViolationCount:t.newVsBaselineViolations}),await Jo(e.id);try{await wu()}catch(n){Le.warn("status badge refresh failed",n)}}function xh(e,t){return new Promise(a=>{const n=setTimeout(()=>{chrome.tabs.onUpdated.removeListener(o),a()},t),o=(r,i)=>{r===e&&i.status==="complete"&&(clearTimeout(n),chrome.tabs.onUpdated.removeListener(o),setTimeout(a,500))};chrome.tabs.onUpdated.addListener(o)})}export{Qn as $,ur as A,Is as B,js as C,$s as D,Fh as E,zh as F,fs as G,Hh as H,Bh as I,qh as J,Be as K,Wt as L,Jh as M,gr as N,Xh as O,Xs as P,ze as Q,hr as R,or as S,Qh as T,xr as U,kt as V,lc as W,wa as X,nt as Y,ma as Z,ep as _,Er as a,Sn as a0,qt as a1,Pe as a2,Ns as a3,Vh as a4,_h as a5,du as a6,uu as a7,qo as a8,lp as a9,mh as aA,dp as aB,Zh as aC,it as aD,Jd as aE,Yd as aF,tp as aG,nu as aH,au as aI,ou as aJ,sp as aK,Lh as aL,jh as aM,Hs as aN,Xc as aO,Ln as aP,on as aQ,Vt as aR,Rh as aS,Nh as aT,Ds as aU,cp as aV,up as aW,mn as aa,Ih as ab,wu as ac,Uo as ad,pp as ae,ss as af,ds as ag,ls as ah,ue as ai,Me as aj,Jl as ak,zl as al,ql as am,Gl as an,Ul as ao,Fl as ap,Ic as aq,rp as ar,ap as as,op as at,np as au,Uh as av,fp as aw,hp as ax,gp as ay,ip as az,sr as b,cr as c,vr as d,Ud as e,Ht as f,fa as g,Gh as h,Wh as i,We as j,ps as k,Ee as l,ua as m,bn as n,Xo as o,Dh as p,Mh as q,Yh as r,Kh as s,Ph as t,Oh as u,Zo as v,nr as w,Oe as x,lr as y,Za as z};
2046
+ `};if(e.ownerLawfare){const u=await Oe(o.flatMap(p=>p.results)),g=ic(o,u.acknowledgedMatchKeys);return{type:"EXPORT_RESPONSE",format:"ai-prompt-site-crawl",content:Oi(o,g,u.acknowledgedMatchKeys)}}const r=new Map;await Promise.all(o.map(async u=>{if(u.componentId)try{const g=await tn(u.componentId);r.set(u.componentId,g.map(p=>({criterionId:p.criterionId,verdict:p.verdict,reasoning:p.reasoning,pageUrl:p.pageUrl})))}catch{}}));const{getAllResolutions:i,applySelfCorrectionSalvage:c,applyContrastMathSalvage:d}=await hn(async()=>{const{getAllResolutions:u,applySelfCorrectionSalvage:g,applyContrastMathSalvage:p}=await Promise.resolve().then(()=>Ws);return{getAllResolutions:u,applySelfCorrectionSalvage:g,applyContrastMathSalvage:p}},void 0),h=(await i()).map(c).map(d),m=new Map;for(const u of h){if(u.verdict!=="fail")continue;const g=Un(u.pageUrl),p=m.get(g)??[];p.push(u),m.set(g,p)}return{type:"EXPORT_RESPONSE",format:"ai-prompt-site-crawl",content:Mi(o,n,r,m)}}return{type:"EXPORT_RESPONSE",format:"json",content:JSON.stringify(bi(e.results,e.delta),null,2)}})}function Ri(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 a of e)if(!(!a||typeof a!="object"))try{const n=a.verdict==="pass"||a.verdict==="fail"?a.verdict:"uncertain",o=n==="pass"?"[PASS]":n==="fail"?"[FAIL]":"[UNCERTAIN]",r=String(a.criterionId??"?"),i=r==="2.4.3"?"Focus Order (Level A)":r==="2.1.2"?"No Keyboard Trap (Level A)":r==="2.4.7"?"Focus Visible (Level AA)":r==="1.3.2"?"Meaningful Sequence (Level A)":r==="1.4.11"?"Non-text Contrast (Level AA)":r==="2.5.3"?"Label in Name (Level A)":r,c=typeof a.confidence=="number"?Math.max(0,Math.min(1,a.confidence)):0,d=typeof a.reasoning=="string"?a.reasoning:"",l=typeof a.model=="string"?a.model:"unknown";t.push(`### WCAG ${r} — ${i} — ${o}`),t.push(""),t.push(`**AI reasoning** (${Math.round(c*100)}% confidence, model ${l}):`),t.push(""),t.push(d?`> ${d.replace(/\n/g,`
2047
+ > `)}`:"> (no reasoning recorded)"),t.push(""),n==="fail"?(r==="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."),t.push(""),t.push('**Anti-patterns to avoid:** adding `tabindex="1"`, `"2"`, etc. (positive values create a parallel focus order that\'s a maintenance nightmare); using `tabindex="-1"` to skip elements that should be reachable; adding JS focus() management to "fix" what DOM-order should solve directly.'),t.push(""),t.push("**Verify by re-auditing:** after the fix, the 2.4.3 walkthrough should return pass.")):r==="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."),t.push(""),t.push("**Anti-patterns to avoid:** suppressing Tab in keydown handlers (event.preventDefault on Tab without specific intent); using `inert` on the wrong container so users can't reach controls that should be focusable."),t.push(""),t.push("**Verify by re-auditing:** after the fix, the 2.1.2 walkthrough should return pass — Tab should cycle forward AND Shift+Tab should cycle backward through every focusable element on the page.")):r==="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(""),t.push("**Anti-patterns to avoid:** `outline: 0 !important` in a global reset without a `:focus-visible` replacement; relying on `:focus` instead of `:focus-visible` (the latter is the correct hook for keyboard-only focus styling); using ONLY a background color change with insufficient contrast (3:1 minimum against the unfocused state)."),t.push(""),t.push("**Verify by re-auditing:** after the fix, the 2.4.7 walkthrough should return pass on every captured focus state.")):r==="1.3.2"?(t.push("**Fix this:** ensure the DOM source order matches the visual reading order. Screen readers read DOM order; CSS (`flex-direction: row-reverse`, `order:`, `grid-area`, absolute positioning) does NOT reorder what the SR hears. Reorder the HTML so the source flows in the same direction the eye reads, then use minimal CSS for visual placement. Where DOM-reorder is genuinely not feasible (legitimate multi-column footers, RTL emulation, sticky-banner overlays), document the decision and verify the SR experience is still coherent."),t.push(""),t.push('**Anti-patterns to avoid:** using `tabindex` to "fix" reading order (tabindex affects keyboard order, not screen-reader order); adding `aria-flowto` (broadly unsupported; not a real fix); leaving inline `display: none` content that holds the "logical" reading order (it won\'t reach the SR).'),t.push(""),t.push("**Verify by re-auditing:** after the fix, the 1.3.2 walkthrough should return pass — the AI judges whether DOM-flagged divergences are real reading-order breaks vs. legitimate layout patterns.")):r==="1.4.11"?(t.push("**Fix this:** every UI component (button border, form input border, focus ring, icon, status indicator, graphical content needed for understanding) must have at least 3:1 contrast against adjacent colors. For each failed element: raise the foreground/border color, add a darker/lighter background to increase contrast, or thicken the indicator (e.g., 2px instead of 1px border). The AI vision identified specific elements; fix the CSS for each."),t.push(""),t.push('**Anti-patterns to avoid:** ONLY changing opacity/transparency (the rendered contrast on a varied background still fails); using box-shadow as a "border" without ensuring the shadow color meets 3:1 against the surrounding pixels; relying on hover state for contrast (must be visible in default state too).'),t.push(""),t.push("**Verify by re-auditing:** after the fix, the 1.4.11 walkthrough should return pass. Re-run with a fresh screenshot; AI will re-judge the updated colors.")):r==="2.5.3"&&(t.push('**Fix this:** every interactive control\'s accessible name must contain its visible text (case-insensitive, allowing icons/punctuation to be ignored). The AI listed which elements failed. For each, choose ONE of: (a) ensure the visible text matches the accessible name (e.g., button text); (b) use `aria-label` or `aria-labelledby` that STARTS WITH the visible text (e.g., `aria-label="Search — search the catalog"` when visible text is "Search"); (c) remove the conflicting `aria-label` and let the textContent serve as the name. Test by saying "click [visible text]" with speech recognition (Voice Control / Dragon) — the correct fix activates the right control.'),t.push(""),t.push('**Anti-patterns to avoid:** using `aria-label` that REPLACES the visible text entirely (e.g., visible "Cancel" + `aria-label="Close form"` — speech-recognition users saying "click Cancel" will fail); using `title=` attribute alone (not all SRs respect it as the accessible name); using `aria-describedby` for naming (that\'s for descriptions, not names).'),t.push(""),t.push("**Verify by re-auditing:** after the fix, the 2.5.3 walkthrough should return pass — the DOM walker re-collects {visibleText, accessibleName} pairs and AI re-judges ambiguous cases.")),t.push("")):n==="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 m=(Array.isArray(a.steps)?a.steps:[]).filter(u=>u&&u.action!=="initial");if(m.length>0){t.push(`<details><summary>Recorded ${m.length}-step keyboard walkthrough</summary>`),t.push(""),t.push("| # | Action | Selector | Role | Accessible name |"),t.push("|---|---|---|---|---|");for(const u of m.slice(0,30)){const g=u.focused??{selector:"?",role:"",name:""},p=String(g.selector??"?"),f=String(g.role??"-")||"-",y=String(g.name??"(no name)").replace(/\|/g,"\\|").slice(0,60),b=String(u.action??""),A=typeof u.ordinal=="number"?u.ordinal:"?";t.push(`| ${A} | ${b} | \`${p}\` | ${f} | ${y} |`)}m.length>30&&t.push(`| … | ${m.length-30} more steps elided | | | |`),t.push(""),t.push("</details>"),t.push("")}}catch(n){console.warn("[report-exporter] walkthrough render skipped:",n)}return t.push("---"),t.join(`
2048
+ `)}function Rh(e){var a;const t=new Set;for(const n of e){const o=n.currentState;if(!o)continue;const r=[];o.pseudoState&&o.pseudoState!=="default"&&r.push(o.pseudoState),o.theme&&o.theme!=="light"&&r.push(o.theme),o.direction&&o.direction!=="ltr"&&r.push(o.direction),(a=o.breakpoint)!=null&&a.id&&o.breakpoint.id!=="desktop"&&r.push(o.breakpoint.id),o.ariaVariation&&o.ariaVariation.id!=="none"&&r.push(`aria=${o.ariaVariation.id}`),t.add(r.length===0?"default":r.join("/"))}return{stateCount:t.size,states:Array.from(t).sort()}}const Oh=/-needs-site-crawl$/;function Un(e){try{const t=new URL(e);t.hash="";let a=t.toString();return a.endsWith("/")&&t.pathname!=="/"&&(a=a.slice(0,-1)),a}catch{return e}}function Oi(e,t,a=new Set){var l;const n=new Map;for(const h of e){const m=h.results.flatMap(u=>u.violations).filter(u=>!u.needsReview&&!a.has(u.matchKey)&&Tr(u.ruleId));for(const u of m){const g=n.get(u.ruleId)??{vs:[],urls:new Set};g.vs.push(u),g.urls.add(h.url),n.set(u.ruleId,g)}}const o={critical:0,serious:1,moderate:2,minor:3},r=Array.from(n.entries()).sort(([,h],[,m])=>(o[h.vs[0].impact]??99)-(o[m.vs[0].impact]??99)),i=r.length,c=[];if(c.push("# Help me reduce my website’s lawsuit-target risk"),c.push(""),c.push("You are an accessibility advisor helping me — a non-technical site owner — lower my exposure to the automated tools that ADA demand-letter firms use to find targets. I crawled my whole site and the findings below are ONLY the issues those automated scanners commonly flag. Please explain in plain English."),c.push(""),c.push("> **Scope.** This is the lawsuit-target (lawfare) view, not a full WCAG conformance audit. It covers the whole crawled site. Fixing everything here gets me off the easy-target list; it is not the same as a formal WCAG 2.1 AA conformance claim (for that I’d use Power or Dev mode in the auditor)."),c.push(""),c.push("## What I need from you"),c.push(""),c.push("1. **First, ask what I use to manage my site** (Squarespace, Wix, Shopify, WordPress + theme, Webflow, custom, etc.) — your advice differs per platform."),c.push("2. **For each finding:** tell me whether **I can fix it myself** in my editor (colors, alt text, link/button labels, page titles/language) or whether **my developer needs to** — and give me a short paragraph I can forward."),c.push("3. **If a finding spans multiple pages, it’s probably a shared template/theme element** — fixing it once helps every page."),c.push(""),c.push("## Site-wide lawsuit-target summary"),c.push(""),c.push(`- **Pages crawled:** ${e.length}`),c.push(`- **Lawsuit-target grade:** ${t.site.letter} — ${t.site.tier} risk`),c.push(`- **${t.site.headline}**`),c.push(`- **Lawsuit-target findings site-wide:** ${i}`),c.push(""),c.push("## The findings, in plain language"),c.push(""),i===0)return c.push("No findings of the kind automated demand-letter scanners commonly flag (missing image alt text, low color contrast, unlabeled buttons/links, missing page language or title). That keeps the site off the easy-target list. This is not a guarantee — scanners add checks over time, and a full WCAG conformance review (Power/Dev mode) covers far more. Re-crawl as the site changes."),c.push(""),c.push("Since there’s nothing to fix here, help me understand: what should I keep an eye on as I add new pages or content, to stay off the radar?"),c.join(`
2049
+ `);let d=1;for(const[h,{vs:m,urls:u}]of r){const g=(l=m.find(b=>b.target.opacityContext))==null?void 0:l.target.opacityContext,p=$n(h,g),f=m[0].impact,y=Array.from(u);c.push(`### ${d}. [${f.toUpperCase()}] ${p.whatsWrong}`),c.push(""),c.push(`**Why this matters.** ${p.whyItMatters}`),c.push(""),c.push(`**Plain-language fix guidance.** ${p.howToFix}`),c.push(""),c.push(`**Where (${y.length} ${y.length===1?"page":"pages"}${y.length>1?" — likely a shared template element, fix once":""}):**`);for(const b of y.slice(0,8))c.push(`- ${b}`);y.length>8&&c.push(`- (and ${y.length-8} more pages)`),c.push(""),c.push(`*Technical reference for your developer:* axe-core rule \`${h}\` — https://dequeuniversity.com/rules/axe/4.11/${h}`),c.push(""),d++}return c.push("---"),c.push(""),c.push("## How to use this"),c.push(""),c.push("Walk through these with me one at a time. Start by asking which platform I use, then for each finding tell me if it’s a self-fix or a developer task, and help me knock out the shared-template items first since they clear multiple pages at once."),c.push(""),c.push(`_Generated by wcagcheckr v${Te()} — site crawl (owner / lawsuit-target lens, ${e.length} pages)._`),c.join(`
2050
+ `)}function Mi(e,t,a=new Map,n=new Map){var l;const o=[];o.push("# Accessibility Fix Request — Site Crawl"),o.push(""),o.push("> **Your job is to APPLY the fixes in this report — not to evaluate, second-guess, or re-litigate whether the findings are correct.** This report is the output of a real automated crawl + audit (axe-core run in a live browser across many UI states) plus AI verification; the triage already happened upstream. Do not debate severity or skip items you personally doubt. Work page by page, apply the canonical fix for each finding, and report the outcome per the Work contract below. A genuine blocker or a real user-decision is the only acceptable non-fix — and those are reported explicitly per the contract, never silently dropped."),o.push(""),o.push(`You are a senior accessibility engineer pairing with the user on their codebase. The user has crawled ${t.pagesAudited} page${t.pagesAudited===1?"":"s"} on this site and below you'll find the per-page WCAG findings + the cross-page consistency findings. Your task: walk page by page, apply fixes, and produce one atomic commit per fix.`),o.push(""),o.push(`> **Note on this report's scope.** This is a triage + bulk-fix prompt covering all crawled pages in one document. The user can also drop into any single page in the wcagcheckr side panel for per-finding acknowledgements, guided manual checks, fix-preview overlays, and interactive walkthrough re-runs — those are intentionally NOT in this report; they live in the per-page UI surface. If you need that level of control for a specific finding, suggest the user click that page in the Crawl tab's "Pages, worst first" list.`),o.push("");let r=0;for(const h of n.values())r+=h.length;const i=t.totalUniqueViolations+r;o.push("## [!] Work contract — read before doing anything else"),o.push(""),o.push(`**Below you have ${i} finding${i===1?"":"s"} across ${t.pagesAudited} page${t.pagesAudited===1?"":"s"}** (${t.totalUniqueViolations} axe violation${t.totalUniqueViolations===1?"":"s"}${r>0?` + ${r} pixel-contrast-sampler fail${r===1?"":"s"}`:""}). When you respond, every numbered finding needs an outcome. Do not stop early. Do not declare done without addressing every finding.`),o.push(""),o.push("**For each numbered finding, produce one of:**"),o.push(""),o.push("- [DONE] **Applied** — the canonical fix was applied. Cite the file + line. If one shared-component fix resolves N findings, group them under the SAME fix and list which findings it covers."),o.push("- [BLOCKED] **Blocked** — explicitly state WHY (missing info, ambiguous user content, dependency change required). One sentence per blocked finding."),o.push("- [NEEDS-DECISION] **Needs user decision** — list the options inline."),o.push(""),o.push(`**The single answer you cannot use: "skipped" or "didn't get to it."** Stop and tell the user if you ran out of context — do not silently omit items.`),o.push(""),o.push("**Required final response format** — completion table:"),o.push(""),o.push("```"),o.push("| # | Rule ID | Page(s) | Status | Notes |"),o.push("|---|---------|---------|--------|-------|"),o.push("| 1 | <rule> | <url> | DONE / BLOCKED / NEEDS-DECISION | <file:line OR reason> |"),o.push(`| ${i} | ... | ... | ... | ... |`),o.push("```"),o.push(""),o.push("**Every row must be present.** One pass through this prompt should produce a fix or a clear next action for every finding — not a partial sweep that forces the user to re-run."),o.push(""),o.push("---"),o.push(""),o.push(ch),o.push("---"),o.push(""),o.push("## Site-wide summary"),o.push(""),o.push(`- **Start URL:** ${t.startUrl}`),o.push(`- **Pages audited:** ${t.pagesAudited}${t.pagesFailed>0?` (${t.pagesFailed} failed)`:""}`),o.push(`- **Site grade:** ${t.siteGrade}`),o.push(`- **Total findings site-wide:** ${i} (${t.totalUniqueViolations} axe, ${r} pixel-contrast-sampler)`),o.push(`- **Severity breakdown:** ${t.totals.critical} critical · ${t.totals.serious+r} serious · ${t.totals.moderate} moderate · ${t.totals.minor} minor`),o.push("");const c=t.topViolations.filter(h=>!/-needs-site-crawl$/.test(h.ruleId));if(c.length>0){o.push("**Most-frequent rules across the site** (fixing one of these in a shared component / layout resolves N pages at once):"),o.push("");for(const h of c.slice(0,8)){const u=h.urlsAffected>=Math.ceil(t.pagesAudited/2)?" — **likely a single shared-component fix**":"";o.push(`- \`${h.ruleId}\` — ${h.impact} — found on ${h.urlsAffected} page${h.urlsAffected===1?"":"s"} (${h.totalOccurrences} total instance${h.totalOccurrences===1?"":"s"})${u}`)}o.push("")}const d=t.consistencyFindings??[];if(d.length>0){o.push("## Cross-page consistency findings"),o.push(""),o.push("These are divergences detected by comparing structure across pages (WCAG 3.2.3, 3.2.4, 3.2.6, 3.3.7). Each finding references multiple pages — typically the fix is in a SHARED component (header / nav / footer / form)."),o.push("");for(const h of d){const m=h.aiVerdict?h.aiVerdict.verdict==="uncertain"?" _(AI: uncertain — verify manually)_":` _(AI: confirmed real, confidence ${(h.aiVerdict.confidence*100).toFixed(0)}%)_`:"";o.push(`### ${h.wcagCriterion} ${h.impact} — ${h.description}${m}`),o.push("");for(const u of h.details.split(`
2051
+ `))o.push(u);(l=h.aiVerdict)!=null&&l.reasoning&&(o.push(""),o.push(`AI reasoning: ${h.aiVerdict.reasoning}`)),o.push("")}}else o.push("## Cross-page consistency findings"),o.push(""),o.push("No cross-page divergences detected by the heuristic comparators. Still verify manually that nav order, accessible names of shared components, and help-mechanism position are consistent across pages."),o.push("");return o.push("---"),o.push(""),o.push(`## Per-page findings (${e.length} page${e.length===1?"":"s"})`),o.push(""),o.push("Each page section below is shaped like a single-page audit report — violations to fix + relevant recipes. Fix one page at a time. The cross-page findings above usually point at a SHARED component; fix that once and re-run the audit to see N pages resolve together."),o.push(""),e.forEach((h,m)=>{const u=m+1;if(o.push(`### Page ${u} of ${e.length}: ${h.url}`),o.push(""),h.error){o.push(`> [!] Audit failed for this page: \`${h.error}\``),o.push(""),o.push("---"),o.push("");return}const g=h.results.flatMap(v=>v.violations).filter(v=>!Oh.test(v.ruleId)),p=n.get(Un(h.url))??[];if(g.length===0&&p.length===0){o.push("No automated violations detected on this page. Still apply the manual checklist below."),o.push("");const v=a.get(h.componentId)??[];if(v.length>0){o.push("**AI walkthrough verdicts on this page:**");for(const S of v){const k=S.verdict==="pass"?"[PASS]":S.verdict==="fail"?"[FAIL]":"[?]";o.push(`- ${k} WCAG ${S.criterionId} — ${S.verdict.toUpperCase()}`)}o.push("")}o.push("---"),o.push("");return}const f=new Map,y=new Map;for(const v of g){const S=De(v.ruleId,v.target.selector);f.has(S)||f.set(S,v);const k=y.get(S)??[];k.push(v),y.set(S,k)}const b=Array.from(f.entries()).map(([v,S])=>({v:S,instances:y.get(v)??[S]})).sort((v,S)=>{const k={critical:0,serious:1,moderate:2,minor:3};return(k[v.v.impact]??99)-(k[S.v.impact]??99)}),A={critical:0,serious:0,moderate:0,minor:0};for(const{v}of b)A[v.impact]++;A.serious+=p.length;const w=b.length+p.length;o.push(`**${w} finding${w===1?"":"s"}** · severity: ${A.critical} critical · ${A.serious} serious · ${A.moderate} moderate · ${A.minor} minor`),o.push(""),b.forEach(({v,instances:S},k)=>{const x=ot[v.ruleId],L=Fo(`${v.ruleId} ${v.wcagCriterion}`)==="human-verify"?"[Human-verify]":"[AI-fixable]";o.push(`#### ${u}.${k+1} \`${v.ruleId}\` — ${v.impact} — WCAG ${v.wcagCriterion} ${v.wcagLevel} — ${L}`),o.push(""),o.push(`**Description:** ${v.description}`),o.push(""),o.push(br(v)),o.push(""),o.push(`**Selector:** \`${v.target.selector}\``);const{stateCount:$,states:U}=Rh(S);$>1?(o.push(""),o.push(`_Failed in ${$} matrix state${$===1?"":"s"}:_ ${U.join(", ")}`)):S.length>1&&(o.push(""),o.push(`_${S.length} element instances on this page (same state)._`)),o.push(""),v.target.failureSummary&&(o.push("**Diagnostic:**"),o.push("```"),o.push(v.target.failureSummary.trim()),o.push("```"),o.push("")),v.target.outerHTML&&(o.push("**Element HTML:**"),o.push("```html"),o.push(v.target.outerHTML.slice(0,500)),o.push("```"),o.push(""));const F=gi(v);if(F){o.push("**Suggested fix for this element** (apply to the source, not the rendered DOM):"),o.push("```diff");for(const T of F.current.split(`
2052
+ `))o.push(`- ${T}`);for(const T of F.suggested.split(`
2053
+ `))o.push(`+ ${T}`);o.push("```"),F.note&&o.push(`> ${F.note}`),o.push("")}x!=null&&x.summary&&(o.push("**Recipe:**"),o.push(x.summary),x.snippet&&(o.push(""),o.push("```"+(x.snippetLang??"")),o.push(x.snippet),o.push("```")),o.push("")),v.helpUrl&&(o.push(`**More info:** ${v.helpUrl}`),o.push(""))}),p.forEach((v,S)=>{const k=b.length+S,x=Fo(`${v.ruleId} ${v.wcagCriterion??"1.4.3"}`)==="human-verify"?"[Human-verify]":"[AI-fixable]";o.push(`#### ${u}.${k+1} \`${v.ruleId}\` — serious — WCAG ${v.wcagCriterion??"1.4.3"} AA — ${x}`),o.push(""),o.push("**Description:** Empirical pixel-contrast measurement below the AA threshold (pixel-contrast-sampler)."),o.push(""),o.push(`**Selector:** \`${v.selector}\``),o.push(""),o.push("**Diagnostic:**"),o.push("```"),o.push(v.reasoning.trim()),o.push("```"),o.push(""),o.push("**Recipe:**"),o.push('Pixel-level contrast measurement — the sampler diffed hide-text vs visible viewport screenshots and recovered the rendered foreground/background colors at the worst-contrast pixel. Verify the live computed colors at the cited selector (devtools → Computed → color / background-color). Fix by raising the foreground contrast against the actual rendered background (account for transparency, gradients, and overlay layers). If the sampler caught a transient state (loading skeleton, hover) and the steady-state contrast is fine, surface that as a "Blocked" entry with a note.'),o.push("")});const s=a.get(h.componentId)??[];if(s.length>0){o.push("**AI walkthrough verdicts on this page:**");for(const v of s){const S=v.verdict==="pass"?"[PASS]":v.verdict==="fail"?"[FAIL]":"[?]";o.push(`- ${S} WCAG ${v.criterionId} — ${v.verdict.toUpperCase()}: ${v.reasoning.slice(0,240)}${v.reasoning.length>240?"…":""}`)}o.push("")}o.push("---"),o.push("")}),o.push("## Combined verification checklist"),o.push(""),o.push("After all per-page fixes:"),o.push(""),o.push("1. **Re-run the site crawl** at the same depth used here."),o.push("2. Verify the **cross-page consistency findings** above all resolve (or document why a flagged divergence is intentional)."),o.push("3. Verify the **top-frequency rules** across pages resolve in bulk (a single shared-component fix should drop the count on multiple pages)."),o.push("4. **Manual WCAG checks** (audio/video transcripts, character key shortcuts, timing controls, etc.) — the per-page sections above only contain automatable findings. The non-automatable WCAG criteria still apply per page."),o.push("5. **Diff review** — every fix should be reviewable in under 10 minutes. One atomic commit per fix."),o.push(""),o.push(`_Generated by ${xe} v${Te()} — site crawl mode (${e.length} pages)._`),o.join(`
2054
+ `)}const xp=Object.freeze(Object.defineProperty({__proto__:null,normUrlKey:Un,registerReportExporterHandlers:Eh,renderAiWalkthroughsForPrompt:Ri,toAiPrompt:Ii,toAiPromptForSiteCrawl:Mi,toAiPromptOwner:Ti,toDefenseBundle:Dt,toDeveloperLetter:Ci,toEvidenceBundle:Si,toHtmlPrint:_n,toJUnit:vi,toJson:bi,toLawfarePromptForSiteCrawl:Oi,toOwnerReport:$i,toSarif:yi,toVpat:xi,toolVersion:Te},Symbol.toStringTag,{value:"Module"})),Bt=te("sw-auto-export");function Mh(e,t,a){const n=Ji(e,a);let o="audit";try{o=new URL(t).hostname.replace(/^www\./,"").replace(/[^a-z0-9.-]/gi,"-")}catch{}return`wcagcheckr/${o}/${n}`}async function Dh(e,t,a){try{if(e==="html-print")return _n(t,a);if(e==="defense-bundle"){const n=await St().catch(()=>[]);return Dt(t,a,"defense",void 0,n)}if(e==="deposition-packet")return(await pi({results:t,delta:a,manualRuns:void 0,aiSummary:null})).html}catch(n){return Bt.warn(`buildContent failed for format=${e}`,n),null}return null}async function Lh(e,t){var a,n,o;if(e.length!==0)try{const r=await zi();if(!r.enabled||await Ue())return;const i=await ue().catch(()=>"free");if(!de(i,"autoExportAuditReports"))return;const c=((a=e[0])==null?void 0:a.pageUrl)??((n=e[0])==null?void 0:n.scope)??"",d=((o=e[0])==null?void 0:o.componentId)??null;for(const l of r.formats)try{const h=await Dh(l,e,t);if(!h||h.length<200)continue;const m=Mh(l,c,d),u=l==="ai-prompt"?"text/markdown":"text/html",{body:g}=Ki(h,u),p=`data:${u};charset=utf-8,${encodeURIComponent(g)}`,f=await chrome.downloads.download({url:p,filename:m,saveAs:!1,conflictAction:"uniquify"});Bt.info(`scheduled auto-export started (id=${f}, file=${m})`),c&&await Yi({url:c,exportedAt:new Date().toISOString(),format:l,filename:m})}catch(h){Bt.warn(`format ${l} failed in scheduled auto-export`,h)}}catch(r){Bt.warn("autoExportFromScheduledRun failed (audit itself still succeeded)",r)}}const Pe=te("scheduled-audit-runner"),la="scheduled-audit:",Vo={hourly:60,"every-4-hours":240,daily:1440,weekly:10080};function Wn(e){return`${la}${e}`}function Ip(e){return e.startsWith(la)?e.slice(la.length):null}async function Nh(e){const t=Wn(e.id);await chrome.alarms.create(t,{periodInMinutes:Vo[e.cadence],delayInMinutes:Vo[e.cadence]})}async function Sp(e){await chrome.alarms.clear(Wn(e))}async function Cp(){try{const{listScheduledAudits:e}=await hn(async()=>{const{listScheduledAudits:o}=await Promise.resolve().then(()=>ys);return{listScheduledAudits:o}},void 0),t=await e(),a=await chrome.alarms.getAll(),n=new Set(t.filter(o=>o.enabled).map(o=>Wn(o.id)));for(const o of a)o.name.startsWith(la)&&!n.has(o.name)&&await chrome.alarms.clear(o.name);for(const o of t)o.enabled&&await Nh(o);Pe.info(`reconciled ${t.length} scheduled-audit alarms`)}catch(e){Pe.warn("failed to reconcile scheduled alarms",e)}}async function $p(e){var r;const t=Date.now(),a=await Xo(e);if(!a){Pe.warn(`scheduled audit ${e} not found — alarm fired for deleted schedule`);return}if(!a.enabled){Pe.debug(`scheduled audit ${e} is disabled — skipping`);return}if(await Ue()){await xt(a,{result:"failed",error:ze,durationMs:Date.now()-t,totalViolations:0,newVsBaselineViolations:0});return}const n=await ue();if(!de(n,"scheduledAudits")){await xt(a,{result:"failed",error:"Scheduled audits are not available on your current plan.",durationMs:Date.now()-t,totalViolations:0,newVsBaselineViolations:0});return}await yn({...a,lastResult:"running",lastRunAt:new Date().toISOString()});let o;try{if(o=(await chrome.tabs.create({url:a.url,active:!1})).id,!o)throw new Error("chrome.tabs.create returned no id");await Ph(o,3e4);const d=await ct("html",{isCanceled:()=>!1},0,void 0,{asSubroutine:!0,tabIdOverride:o});if(!d.ok){await xt(a,{result:"failed",error:d.error??"audit failed",durationMs:Date.now()-t,totalViolations:0,newVsBaselineViolations:0});return}const l=d.results.reduce((g,p)=>g+p.violations.length,0),h=(r=d.results[0])==null?void 0:r.delta,m=(h==null?void 0:h.newCount)??0,u=new Date().toISOString();if(await xt(a,{result:"ok",durationMs:Date.now()-t,totalViolations:l,newVsBaselineViolations:m}),Lh(d.results,h),m>0)try{const g=await Au({scheduleId:a.id,auditedUrl:a.url,ranAt:u,results:d.results});Pe.debug(`alert outcome for ${a.id}`,g)}catch(g){Pe.warn(`alert fire threw for ${a.id}`,g)}}catch(i){const c=i instanceof Error?i.message:String(i);Pe.warn(`scheduled audit ${e} failed:`,c),await xt(a,{result:"failed",error:c,durationMs:Date.now()-t,totalViolations:0,newVsBaselineViolations:0})}finally{if(o!==void 0)try{await chrome.tabs.remove(o)}catch{}}}async function xt(e,t){const a=new Date().toISOString();await Qo({scheduleId:e.id,ranAt:a,durationMs:t.durationMs,totalViolations:t.totalViolations,newVsBaselineViolations:t.newVsBaselineViolations,error:t.error}),await yn({...e,lastRunAt:a,lastResult:t.result,lastError:t.error,lastTotalViolations:t.totalViolations,lastNewViolationCount:t.newVsBaselineViolations}),await Zo(e.id);try{await Tu()}catch(n){Pe.warn("status badge refresh failed",n)}}function Ph(e,t){return new Promise(a=>{const n=setTimeout(()=>{chrome.tabs.onUpdated.removeListener(o),a()},t),o=(r,i)=>{r===e&&i.status==="complete"&&(clearTimeout(n),chrome.tabs.onUpdated.removeListener(o),setTimeout(a,500))};chrome.tabs.onUpdated.addListener(o)})}export{hp as $,fr as A,Rs as B,Ys as C,Ls as D,Yh as E,Zs as F,op as G,As as H,ap as I,np as J,rp as K,Ke as L,jt as M,wr as N,cp as O,lp as P,ic as Q,Ye as R,sr as S,gr as T,dp as U,Tr as V,St as W,yc as X,ka as Y,rt as Z,wa as _,Dr as a,eo as a0,$n as a1,Jt as a2,Fe as a3,js as a4,Zh as a5,Jh as a6,wu as a7,vu as a8,Jo as a9,yp as aA,Eh as aB,kp as aC,up as aD,ct as aE,ru as aF,ou as aG,pp as aH,hu as aI,uu as aJ,pu as aK,wp as aL,qh as aM,ep as aN,Xs as aO,il as aP,Pn as aQ,sn as aR,Ht as aS,Vh as aT,Kh as aU,Ws as aV,vp as aW,xp as aX,Ap as aa,yn as ab,Nh as ac,Tu as ad,Vo as ae,Sp as af,fs as ag,bs as ah,ms as ai,he as aj,Le as ak,rd as al,td as am,ad as an,Ql as ao,Kl as ap,ql as aq,Mc as ar,bp as as,fp as at,mp as au,gp as av,Xh as aw,Cp as ax,Ip as ay,$p as az,dr as b,ur as c,Ir as d,Kd as e,zt as f,ba as g,tp as h,Qh as i,je as j,vs as k,Oe as l,fa as m,wn as n,er as o,Bh as p,Hh as q,sp as r,ip as s,zh as t,jh as u,ar as v,ir as w,De as x,hr as y,tn as z};