@wcag-checkr/ci 1.0.0-rc.68 → 1.0.0-rc.69

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.
@@ -1,7 +1,7 @@
1
- import{c as W,o as D,e as N,f as An,g as Sn,d as j,b as se,i as xn,h as kn}from"./crash-reporter-Dc5lvxvY.js";import{m as Ce,d as En,T as $n,a as ft,c as st,i as Tn}from"./diff-DIBMr3fQ.js";import{y as Cn,z as In,p as gt,i as we,o as ie,A as ve,F as Rn,q as Dt,O as Ft,B as Gt,R as On,C as Wt,s as _n,D as Ue,E as Ne,G as Le,H as Mn,g as Un,I as Nn,n as Ae,m as Vt,a as rt,J as Ln,K as Pn}from"./forensic-log-DaUcnSG0.js";import{s as Te,T as mt,b as Xe,i as Dn}from"./state-PELIq3oj.js";import{m as Pe,D as Fn,s as Gn,h as Wn,b as Vn,g as jn,d as zn,e as Hn}from"./ai-usage-log-Dj9Ub_DT.js";const Bn=W("chrome-api"),qn="1.3";class Kn{constructor(t){this.target=t}async attach(){await chrome.debugger.attach(this.target,qn)}async detach(){try{await chrome.debugger.detach(this.target)}catch(t){Bn.debug("detach soft-failure",t)}}async send(t,n){return chrome.debugger.sendCommand(this.target,t,n)}}function Yn(e){const t=e instanceof Error?e.message:String(e);return/Another debugger is already attached|Cannot attach to/i.test(t)}function Jn(e){const t=e instanceof Error?e.message:String(e);return Yn(e)?Ce("DEBUGGER_BUSY","DevTools is open on this tab. Close it to run the audit.",!0,{raw:t}):Ce("STATE_DRIVE_FAILED",t,!1)}function Xn(e){const t=(n,o)=>e(n,o);return chrome.debugger.onDetach.addListener(t),()=>chrome.debugger.onDetach.removeListener(t)}const B=W("state-driver"),Qn=60*1e3,ee=new Map,Ie=new Map,pe=new Map,de=new Map,Zn={default:[],hover:["hover"],focus:["focus"],"focus-visible":["focus","focus-visible"],active:["active"],disabled:[]};async function jt(e){let t=ee.get(e);if(!t){t=new Kn({tabId:e});try{await t.attach(),await t.send("Runtime.enable"),await t.send("Page.enable"),await t.send("DOM.enable"),await t.send("DOM.getDocument",{depth:-1,pierce:!0}),await t.send("CSS.enable")}catch(n){try{await t.detach()}catch{}throw n}ee.set(e,t),pe.set(e,new Map),ea(e),B.info("attached",{tabId:e})}return na(e),t}function ea(e){const t=(n,o,s)=>{var c,r;if(n.tabId!==e)return;const i=pe.get(e);if(i)if(o==="Runtime.executionContextCreated"){const d=s,l=d==null?void 0:d.context;l&&((c=l.auxData)!=null&&c.isDefault)&&((r=l.auxData)!=null&&r.frameId)&&i.set(l.auxData.frameId,l.id)}else if(o==="Runtime.executionContextDestroyed"){const d=s,l=d==null?void 0:d.executionContextId;if(l!==void 0)for(const[p,h]of i.entries())h===l&&i.delete(p)}else o==="Runtime.executionContextsCleared"&&i.clear()};chrome.debugger.onEvent.addListener(t)}async function ta(e){const t=ee.get(e);if(ee.delete(e),pe.delete(e),de.delete(e),t)try{await t.detach()}catch{}}function na(e){const t=Ie.get(e);t&&clearTimeout(t),Ie.set(e,setTimeout(()=>{it(e).catch(n=>B.warn("idle detach failed",n))},Qn))}async function it(e){const t=ee.get(e);t&&(await t.detach(),ee.delete(e)),pe.delete(e),de.delete(e);const n=Ie.get(e);n&&(clearTimeout(n),Ie.delete(e))}function zt(e){const t=[{id:e.frame.id,url:e.frame.url}];for(const n of e.childFrames??[])t.push(...zt(n));return t}async function aa(e,t,n){var i;if(!n||n===0)return;const o=pe.get(t);if(!o)return;let s;try{s=(i=(await chrome.scripting.executeScript({target:{tabId:t,frameIds:[n]},func:()=>location.href}))[0])==null?void 0:i.result}catch(c){B.debug("frame URL lookup failed",c)}try{const c=await e.send("Page.getFrameTree"),r=zt(c.frameTree);if(s){const p=r.find(h=>h.url===s);if(p){const h=o.get(p.id);if(h!==void 0)return h}}const d=c.frameTree.frame.id,l=r.find(p=>p.id!==d);return l?o.get(l.id):void 0}catch(c){B.warn("Page.getFrameTree failed",c);return}}async function oa(e,t,n){var c;const o={expression:`document.querySelector(${JSON.stringify(t)})`,objectGroup:"wcag-auditor",returnByValue:!1};n!==void 0&&(o.contextId=n);const i=(c=(await e.send("Runtime.evaluate",o)).result)==null?void 0:c.objectId;if(!i)return null;try{return(await e.send("DOM.requestNode",{objectId:i})).nodeId??null}finally{e.send("Runtime.releaseObject",{objectId:i}).catch(()=>{})}}async function Ht(e){const t=de.get(e);if(!t)return;const n=ee.get(e);if(n){try{await n.send("CSS.forcePseudoState",{nodeId:t.nodeId,forcedPseudoClasses:[]})}catch(o){B.debug("forcePseudoState clear failed (node may be gone)",o)}de.delete(e)}}async function Qe(e,t,n,o){await chrome.scripting.executeScript({target:ct(e,t),func:(s,i)=>{const c=document.querySelector(s);c&&(i?c.setAttribute("disabled",""):c.removeAttribute("disabled"))},args:[n,o]})}async function Bt(e,t){await chrome.scripting.executeScript({target:ct(e,t),func:()=>{const n=document.activeElement;n&&typeof n.blur=="function"&&n.blur()}})}async function qt(e,t){await chrome.scripting.executeScript({target:{tabId:e,frameIds:[0]},func:n=>{document.documentElement.setAttribute("dir",n)},args:[t]})}async function sa(e,t,n,o){await chrome.scripting.executeScript({target:ct(e,t),func:(s,i)=>{const c=document.querySelector(s);if(c)for(const[r,d]of Object.entries(i))c.setAttribute(r,d)},args:[n,o]})}function ct(e,t){return t!==void 0&&t!==0?{tabId:e,frameIds:[t]}:{tabId:e}}async function ra(e,t,n,o){const s=await jt(e);if(await Ht(e),n){t.pseudoState==="disabled"?await Qe(e,o,n,!0):(await Qe(e,o,n,!1),await Bt(e,o));const c=Zn[t.pseudoState];if(c.length>0){const r=await aa(s,e,o),d=await oa(s,n,r);if(d!==null)try{await s.send("CSS.forcePseudoState",{nodeId:d,forcedPseudoClasses:c}),de.set(e,{sessionTabId:e,nodeId:d})}catch(l){B.warn(`CSS.forcePseudoState failed for :${t.pseudoState}`,l)}else B.debug(`could not resolve nodeId for ${n} (frameId=${o})`)}}const i=[{name:"prefers-reduced-motion",value:"reduce"}];t.theme==="dark"?(i.push({name:"prefers-color-scheme",value:"dark"}),i.push({name:"forced-colors",value:"none"})):t.theme==="light"?(i.push({name:"prefers-color-scheme",value:"light"}),i.push({name:"forced-colors",value:"none"})):t.theme==="forced-colors-active"?i.push({name:"forced-colors",value:"active"}):t.theme==="forced-colors-none"&&i.push({name:"forced-colors",value:"none"}),await s.send("Emulation.setEmulatedMedia",{features:i}),await new Promise(c=>setTimeout(c,50)),await qt(e,t.direction),await s.send("Emulation.setDeviceMetricsOverride",{width:t.breakpoint.width,height:t.breakpoint.height,deviceScaleFactor:t.breakpoint.deviceScaleFactor,mobile:t.breakpoint.mobile}),t.ariaVariation&&n&&await sa(e,o,n,t.ariaVariation.attributes)}async function ia(e,t,n){const o=ee.get(e);if(o){if(await Ht(e),t)try{await Qe(e,n,t,!1),await Bt(e,n),await qt(e,"ltr")}catch(s){B.warn("element-level reset partial failure",s)}try{await o.send("Emulation.setEmulatedMedia",{features:[]}),await o.send("Emulation.clearDeviceMetricsOverride")}catch(s){B.warn("reset partial failure",s)}await it(e)}}async function Ze(e,t,n,o){try{return await ra(e,t,n,o),N({type:"STATE_CHANGED_EVENT",tabId:e,currentState:t}),{type:"STATE_DRIVE_RESPONSE",success:!0,appliedState:t}}catch(s){return B.error("state drive failed",s),await ta(e),{type:"STATE_DRIVE_RESPONSE",success:!1,error:Jn(s)}}}async function Kt(e,t,n){await ia(e,t,n)}async function ca(e,t={}){try{const n=await jt(e),o={format:"jpeg",quality:t.quality??75};t.fullPage&&(o.captureBeyondViewport=!0);const s=await n.send("Page.captureScreenshot",o);return s!=null&&s.data?`data:image/jpeg;base64,${s.data}`:null}catch(n){return B.debug("Page.captureScreenshot failed",n),null}}function la(){var n;const e=[];if(e.push(D("STATE_DRIVE_REQUEST",o=>Ze(o.tabId,o.state,o.scope,o.frameId))),e.push(D("STATE_RESET_REQUEST",o=>Kt(o.tabId))),(n=chrome.tabs)!=null&&n.onRemoved){const o=s=>{it(s).catch(()=>{})};chrome.tabs.onRemoved.addListener(o),e.push(()=>chrome.tabs.onRemoved.removeListener(o))}const t=Xn((o,s)=>{o.tabId!==void 0&&(ee.delete(o.tabId),pe.delete(o.tabId),de.delete(o.tabId),B.warn("unexpected detach",{tabId:o.tabId,reason:s}))});return e.push(t),B.info("handlers registered"),()=>e.forEach(o=>o())}const da="wcag-component-auditor",ua=1;let ze=null;function De(){return ze||(ze=Cn(da,ua,{upgrade(e,t){if(t<1){const n=e.createObjectStore("baselines",{keyPath:"componentId"});n.createIndex("byUrl","snapshotMeta.url"),n.createIndex("byDate","lastUpdated");const o=e.createObjectStore("audit-history",{keyPath:"auditId",autoIncrement:!0});o.createIndex("byComponentId","componentId"),o.createIndex("byDate","runDate")}}})),ze}async function lt(e){return(await De()).get("baselines",e)}async function Yt(e){await(await De()).put("baselines",e)}async function pa(e){await(await De()).delete("baselines",e)}async function ha(){return(await De()).getAll("baselines")}const le=W("cloud-sync"),yt="cloudSyncEnabled",He="cloudSyncEndpoint",Be="licenseToken",et="cloudSyncVersionCache";async function dt(){try{const e=await chrome.storage.local.get([yt,He,Be]),t=e[yt]===!0,n=typeof e[He]=="string"?e[He]:"",o=typeof e[Be]=="string"?e[Be]:"";return!t||!n||!o?null:{endpoint:n.replace(/\/$/,""),token:o,enabled:t}}catch{return null}}async function Jt(){try{const t=(await chrome.storage.local.get(et))[et];return t&&typeof t=="object"?t:{}}catch{return{}}}async function Re(e){try{await chrome.storage.local.set({[et]:e})}catch{}}async function fa(e,t,n){const o=await dt();if(!o)return!1;const s=await Jt(),i=s[e];try{const c=`${o.endpoint}/v1/products/wcagcheckr/baselines/${encodeURIComponent(e)}`,r=await fetch(c,{method:"PUT",headers:{"Content-Type":"application/json",Authorization:`Bearer ${o.token}`},body:JSON.stringify({snapshot:t,axeVersion:n,expectedVersion:i})});if(r.status===409)return le.warn(`baseline ${e} version conflict on push`,{expectedVersion:i}),delete s[e],await Re(s),!1;if(!r.ok)return le.warn("baseline push failed",{status:r.status}),!1;const d=await r.json();return typeof d.version=="number"&&(s[e]=d.version,await Re(s)),!0}catch(c){return le.debug("push network failure (offline?)",c),!1}}async function ga(e){const t=await dt();if(!t)return!1;try{const n=`${t.endpoint}/v1/products/wcagcheckr/baselines/${encodeURIComponent(e)}`,o=await fetch(n,{method:"DELETE",headers:{Authorization:`Bearer ${t.token}`}});if(o.ok){const s=await Jt();delete s[e],await Re(s)}return o.ok}catch(n){return le.debug("delete network failure",n),!1}}async function ma(){const e=await dt();if(!e)return[];try{const t=await fetch(`${e.endpoint}/v1/products/wcagcheckr/baselines`,{headers:{Authorization:`Bearer ${e.token}`}});if(!t.ok)return le.warn("pull failed",{status:t.status}),[];const o=(await t.json()).items??[],s={};for(const i of o)s[i.componentId]=i.version;return await Re(s),o}catch(t){return le.debug("pull network failure",t),[]}}const Xt=W("baseline-store"),bt="componentIdAliases";async function ue(e,t){try{return await t()}catch(n){const o=n instanceof Error?n.message:String(n);throw Xt.error(`baseline IDB ${e} failed: ${o}`),new $n({code:"BASELINE_DB_ERROR",message:`Baseline storage is unavailable (${e}: ${o}).`,recoverable:!1,details:o})}}async function Fe(e){return((await chrome.storage.local.get(bt))[bt]??{})[e]??e}async function ya(e){const t=await Fe(e),n=await ue("get",()=>lt(t));return n?{violations:n.violations,snapshotMeta:n.snapshotMeta}:null}async function ba(e,t,n,o){const s=await Fe(e),i=await ue("get-for-set",()=>lt(s)),c=(i==null?void 0:i.seenOnUrls)??[],r=c.includes(n.url)?c:[...c,n.url];await ue("set",()=>Yt({componentId:s,violations:t,snapshotMeta:n,lastUpdated:new Date().toISOString(),axeVersion:n.axeVersion,announcements:o==null?void 0:o.announcements,focusEvents:o==null?void 0:o.focusEvents,seenOnUrls:r})),fa(s,{violations:t,snapshotMeta:n},n.axeVersion).catch(()=>{})}function wa(e,t){if(!e||!t||ft(e)===ft(t))return;const n=i=>{var c,r,d,l,p;return(((c=i.pseudoStates)==null?void 0:c.length)??0)*Math.max(1,((r=i.ariaVariations)==null?void 0:r.length)??0)*(((d=i.themes)==null?void 0:d.length)??0)*(((l=i.directions)==null?void 0:l.length)??0)*(((p=i.breakpoints)==null?void 0:p.length)??0)},o=n(e),s=n(t);return`Baseline was captured under a different state-matrix configuration (${o} states) than this run (${s} states). Per-state-instance deltas inflate when the matrix changes — the unique-violation deltas above are still reliable.`}async function Qt(e,t,n,o){var l;const s=await Fe(e),i=await In(s),c=await ue("compare",()=>lt(s));if(!c){const p=t.filter(f=>i.has(f.matchKey)),h=t.filter(f=>!i.has(f.matchKey));return{new:h,persistent:[],fixed:[],newCount:h.length,persistentCount:0,fixedCount:0,baselineSnapshotMeta:null,comparedAt:new Date().toISOString(),newAnnouncements:n==null?void 0:n.announcements,newFocusEvents:n==null?void 0:n.focusEvents,acknowledged:p,acknowledgedCount:p.length}}const r=En(c.violations,t,c.snapshotMeta,{baselineAnnouncements:c.announcements,currentAnnouncements:n==null?void 0:n.announcements,baselineFocusEvents:c.focusEvents,currentFocusEvents:n==null?void 0:n.focusEvents},i),d=wa((l=c.snapshotMeta)==null?void 0:l.matrixConfig,o);return d&&(r.matrixMismatchWarning=d),r}async function va(e){const t=await Fe(e);await ue("delete",()=>pa(t)),ga(t).catch(()=>{})}async function Aa(){const e=await ma();for(const t of e)await Yt({componentId:t.componentId,violations:t.snapshot.violations,snapshotMeta:t.snapshot.snapshotMeta,lastUpdated:t.updatedAt,axeVersion:t.axeVersion});return e.length>0&&N({type:"SCORECARD_UPDATED_EVENT"}),e.length}async function Sa(e){let t=await ue("list",()=>ha());return e!=null&&e.url&&(t=t.filter(n=>n.snapshotMeta.url===e.url)),t.map(n=>{const o=(n.focusEvents??[]).filter(l=>l.isFocusReset).length,s=(n.announcements??[]).length,i=n.violations.filter(l=>l.ruleId==="target-size").length,c=n.violations.some(l=>l.ruleId==="color-contrast"&&l.currentState.pseudoState==="hover"),r=n.violations.filter(l=>l.impact==="critical").length,d=n.violations.filter(l=>l.impact==="serious").length;return{componentId:n.componentId,violationCount:n.violations.length,lastUpdated:n.lastUpdated,seenOnUrlsCount:(n.seenOnUrls??[]).length,metrics:{criticalCount:r,seriousCount:d,focusResetCount:o,announcementCount:s,targetSizeFailCount:i,hoverContrastFail:c}}})}function xa(){const e=[];return e.push(D("BASELINE_GET",async t=>({type:"BASELINE_GET_RESPONSE",baseline:await ya(t.componentId)}))),e.push(D("BASELINE_SET",async t=>{await ba(t.componentId,t.violations,t.snapshotMeta,{announcements:t.announcements,focusEvents:t.focusEvents}),N({type:"SCORECARD_UPDATED_EVENT"})})),e.push(D("BASELINE_COMPARE",async t=>({type:"BASELINE_COMPARE_RESPONSE",delta:await Qt(t.componentId,t.currentViolations,{announcements:t.announcements,focusEvents:t.focusEvents},t.currentMatrix)}))),e.push(D("BASELINE_DELETE",async t=>{await va(t.componentId),N({type:"SCORECARD_UPDATED_EVENT"})})),e.push(D("BASELINE_LIST",async t=>({type:"BASELINE_LIST_RESPONSE",items:await Sa(t.filter)}))),Xt.info("handlers registered"),()=>e.forEach(t=>t())}const tt="tierConfig:cache",ka=60*60*1e3,Ea=24*60*60*1e3,$a=7*24*60*60*1e3;async function Ta(){return(await chrome.storage.local.get(tt))[tt]??null}async function Ca(e){await chrome.storage.local.set({[tt]:e})}function Ia(e,t=Date.now()){const n=t-e.fetchedAt;return n<ka?"fresh":n<Ea?"stale":n<$a?"grace":"expired"}function Ra(e,t){const n={...e},o=u=>{if(typeof u=="number")return u===-1?1/0:u},s=u=>typeof u=="boolean"?u:void 0,i=u=>{if(u==="all"||u==="configurable"||u==="default-only")return u},c=o(t.maxComponents);c!==void 0&&(n.maxComponents=c);const r=o(t.maxBaselines);r!==void 0&&(n.maxBaselines=r);const d=i(t.stateMatrix);d!==void 0&&(n.stateMatrix=d);const l=s(t.storybookAutoIterate);l!==void 0&&(n.storybookAutoIterate=l);const p=s(t.exportJson);p!==void 0&&(n.exportJson=p);const h=s(t.exportSarif);h!==void 0&&(n.exportSarif=h);const f=s(t.exportJunit);f!==void 0&&(n.exportJunit=f);const a=s(t.cloudSync);a!==void 0&&(n.cloudSync=a);const g=s(t.forensicAnchoring);return g!==void 0&&(n.forensicAnchoring=g),n}function Oa(e,t){var o;if(e==="trial")return((o=t.trial)==null?void 0:o.features)??null;const n=t.plans.find(s=>s.code===e||s.code.startsWith(e+"-"));return(n==null?void 0:n.features)??null}const nt=W("tier-config-client"),_a="wcagcheckr",Ma=`https://api.wcagcheckr.com/v1/products/${_a}/tier-config`,Ua=1e4,wt="tier-config-refresh",Na=60,La=["trial","free","solo","team"];function Zt(e){const t={};for(const n of La){const o=Oa(n,e);t[n]=o?Ra(mt[n],o):{...mt[n]}}return t}async function Pa(){const e=await Ta();if(!e){Te(null);return}if(Ia(e)==="expired"){nt.warn("cached tier-config is expired (>7d); using hardcoded defaults"),Te(null);return}Te(Zt(e.config))}async function en(){try{const e=await fetch(Ma,{signal:AbortSignal.timeout(Ua),headers:{accept:"application/json"}});if(!e.ok)throw new Error(`http ${e.status}`);const t=await e.json();await Ca({fetchedAt:Date.now(),config:t}),Te(Zt(t)),nt.info("tier-config refreshed from server")}catch(e){nt.warn("tier-config refresh failed; keeping prior cache",e)}}function Da(){chrome.alarms.create(wt,{periodInMinutes:Na}),chrome.alarms.onAlarm.addListener(e=>{e.name===wt&&en()})}const Fa=W("settings-store"),vt=1,At="__schemaVersion",at={stateMatrix:Xe,componentIdAliases:{},ignorePatterns:[]};async function Oe(e,t){return(await chrome.storage.local.get(e))[e]??t}async function Ga(){const e=await chrome.storage.local.get();if(e[At]===vt)return;const t={[At]:vt};for(const[n,o]of Object.entries(at))n in e||(t[n]=o);await chrome.storage.local.set(t),Fa.info("defaults ensured")}const Wa=["__","inflight:","license:","support:"];function Va(e){return!Wa.some(t=>e.startsWith(t))}function ja(){const e=[];return e.push(D("SETTINGS_GET",async t=>({type:"SETTINGS_RESPONSE",data:(await chrome.storage.local.get(t.key))[t.key]??at[t.key]}))),e.push(D("SETTINGS_SET",async t=>{await chrome.storage.local.set({[t.key]:t.value})})),e.push(D("SETTINGS_GET_ALL",async()=>{const t=await chrome.storage.local.get(),n=Object.fromEntries(Object.entries(t).filter(([o])=>Va(o)));return{type:"SETTINGS_RESPONSE",data:{...at,...n}}})),()=>e.forEach(t=>t())}const za={input:3/1e6,output:15/1e6},Ha={input:1/1e6,output:5/1e6},Ba="claude-sonnet-4-6",qa="https://api.anthropic.com/v1/messages",Ka="2023-06-01";function Ya(e){const t=e.model||Ba,n=t.includes("haiku")?Ha:za;return{providerId:"anthropic",async judgeAltText({imageUrl:o,alt:s,surroundingContext:i,signal:c}){const r=await xt(o,c);if(!r)return kt("Could not fetch image to verify alt text",0,t);const d=ao(s,i),l=await q({apiKey:e.apiKey,model:t,maxTokens:300,signal:c,messages:[{role:"user",content:[{type:"image",source:{type:"base64",media_type:r.mediaType,data:r.data}},{type:"text",text:d}]}]});return Z(l,n)},async judgeHeading({headingText:o,sectionContent:s,signal:i}){const c=await q({apiKey:e.apiKey,model:t,maxTokens:250,signal:i,messages:[{role:"user",content:[{type:"text",text:oo(o,s)}]}]});return Z(c,n)},async judgeSensoryLanguage({instructionText:o,signal:s}){const i=await q({apiKey:e.apiKey,model:t,maxTokens:250,signal:s,messages:[{role:"user",content:[{type:"text",text:so(o)}]}]});return Z(i,n)},async judgeAriaSemantics({elementHtml:o,computedRole:s,surroundingHtml:i,signal:c}){const r=await q({apiKey:e.apiKey,model:t,maxTokens:350,signal:c,messages:[{role:"user",content:[{type:"text",text:ro(o,s,i)}]}]});return Z(r,n)},async judgeImageOfText({imageUrl:o,accessibleName:s,signal:i}){const c=await xt(o,i);if(!c)return kt("Could not fetch image to inspect for embedded text",0,t);const r=await q({apiKey:e.apiKey,model:t,maxTokens:300,signal:i,messages:[{role:"user",content:[{type:"image",source:{type:"base64",media_type:c.mediaType,data:c.data}},{type:"text",text:io(s)}]}]});return Z(r,n)},async judgeColorOnlyMeaning({elementHtml:o,contextDescription:s,signal:i}){const c=await q({apiKey:e.apiKey,model:t,maxTokens:250,signal:i,messages:[{role:"user",content:[{type:"text",text:co(o,s)}]}]});return Z(c,n)},async judgeGenericLinkText({linkText:o,surroundingText:s,signal:i}){const c=await q({apiKey:e.apiKey,model:t,maxTokens:250,signal:i,messages:[{role:"user",content:[{type:"text",text:lo(o,s)}]}]});return Z(c,n)},async judgeWallOfText({blockText:o,structuralChildrenCount:s,signal:i}){const c=await q({apiKey:e.apiKey,model:t,maxTokens:250,signal:i,messages:[{role:"user",content:[{type:"text",text:uo(o,s)}]}]});return Z(c,n)},async judgeLanguageMismatch({declaredLang:o,bodyTextSample:s,signal:i}){const c=await q({apiKey:e.apiKey,model:t,maxTokens:250,signal:i,messages:[{role:"user",content:[{type:"text",text:po(o,s)}]}]});return Z(c,n)},async suggestColorFix(o){const s=await q({apiKey:e.apiKey,model:t,maxTokens:600,messages:[{role:"user",content:[{type:"text",text:ho(o)}]}]});return go(s,n)},async generateExecutiveSummary(o){const s=await q({apiKey:e.apiKey,model:t,maxTokens:800,messages:[{role:"user",content:[{type:"text",text:fo(o)}]}]});return bo(s,n)},async resolveAxeIncomplete(o){const s=await q({apiKey:e.apiKey,model:t,maxTokens:400,signal:o.signal,messages:[{role:"user",content:[{type:"text",text:mo(o)}]}]});return yo(s,n,t)}}}class Se extends Error{constructor(){super("Anthropic API: canceled by caller"),this.name="ExternalAbortError"}}const tn=3e4,St=3,Ja=1e4;function Xa(e){return e===429||e>=500}function Qa(e){return e instanceof Error?e.name==="TimeoutError"||e.name==="AbortError"||e instanceof TypeError:!1}function Za(e,t){if(!t)return e;if(typeof AbortSignal.any=="function")return AbortSignal.any([e,t]);const n=new AbortController,o=()=>n.abort();return e.aborted?n.abort():e.addEventListener("abort",o,{once:!0}),t.aborted?n.abort():t.addEventListener("abort",o,{once:!0}),n.signal}async function eo(e){var s,i;const t=AbortSignal.timeout(tn),n=Za(t,e.signal);let o;try{o=await fetch(qa,{method:"POST",headers:{"x-api-key":e.apiKey,"anthropic-version":Ka,"content-type":"application/json","anthropic-dangerous-direct-browser-access":"true"},body:JSON.stringify({model:e.model,max_tokens:e.maxTokens,messages:e.messages}),signal:n})}catch(c){throw(s=e.signal)!=null&&s.aborted?new Se:c}if(!o.ok){const r=((i=(await o.json().catch(()=>({}))).error)==null?void 0:i.message)??`${o.status} ${o.statusText}`,d=new Error(`Anthropic API: ${r}`);throw d.status=o.status,d}return await o.json()}async function q(e){var n;let t=null;for(let o=1;o<=St;o++){if((n=e.signal)!=null&&n.aborted)throw new Se;try{return await eo(e)}catch(s){if(s instanceof Se)throw s;const i=s.status;if(!(Qa(s)||typeof i=="number"&&Xa(i))||o===St)throw s instanceof Error&&(s.name==="TimeoutError"||s.name==="AbortError")?new Error(`Anthropic API: timed out after ${tn/1e3}s on each of ${o} attempt${o===1?"":"s"}`):s;t=s instanceof Error?s:new Error(String(s)),await to(o*1e3,e.signal)}}throw t??new Error("Anthropic API: all retries failed")}function to(e,t){return new Promise((n,o)=>{if(t!=null&&t.aborted){o(new Se);return}const s=setTimeout(()=>{t==null||t.removeEventListener("abort",i),n()},e),i=()=>{clearTimeout(s),o(new Se)};t==null||t.addEventListener("abort",i,{once:!0})})}async function xt(e,t){var i,c;const n=new AbortController,o=setTimeout(()=>n.abort(),Ja),s=()=>n.abort();t&&(t.aborted?n.abort():t.addEventListener("abort",s,{once:!0}));try{const r=await fetch(e,{signal:n.signal});if(!r.ok)return null;const d=((c=(i=r.headers.get("content-type"))==null?void 0:i.split(";")[0])==null?void 0:c.trim())??"image/png";if(!d.startsWith("image/"))return null;const l=await r.arrayBuffer();let p="";const h=new Uint8Array(l),f=32768;for(let a=0;a<h.length;a+=f)p+=String.fromCharCode(...h.slice(a,a+f));return{data:btoa(p),mediaType:d}}catch{return null}finally{clearTimeout(o),t&&t.removeEventListener("abort",s)}}function Z(e,t){var i;const n=((i=e.content.find(c=>c.type==="text"))==null?void 0:i.text)??"",o=no(n),s=e.usage.input_tokens*t.input+e.usage.output_tokens*t.output;return{verdict:o.verdict,reasoning:o.reasoning,confidence:o.confidence,costUsd:s,model:e.model}}function no(e){const n=e.replace(/```(?:json)?/g,"").trim().match(/\{[\s\S]*\}/);if(!n)return{verdict:"uncertain",reasoning:"Model returned no parseable JSON",confidence:0};try{const o=JSON.parse(n[0]),s=o.verdict==="pass"||o.verdict==="fail"?o.verdict:"uncertain",i=(o.reasoning??"").trim().slice(0,600),c=typeof o.confidence=="number"?o.confidence:.5,r=Math.max(0,Math.min(1,c));return{verdict:s,reasoning:i,confidence:r}}catch{return{verdict:"uncertain",reasoning:"Model returned invalid JSON",confidence:0}}}function kt(e,t,n){return{verdict:"uncertain",reasoning:e,confidence:0,costUsd:t,model:n}}const te=`
1
+ import{c as W,o as D,e as N,f as Sn,g as xn,d as j,b as se,i as kn,h as En}from"./crash-reporter-Dc5lvxvY.js";import{m as Ie,d as $n,T as Tn,a as gt,c as rt,i as Cn}from"./diff-DIBMr3fQ.js";import{y as In,z as Rn,p as mt,i as we,o as ie,A as ve,F as On,q as Ft,O as Gt,B as Wt,R as _n,C as Vt,s as Mn,D as Ne,E as Le,G as Pe,H as Un,g as Nn,I as Ln,n as Ae,m as jt,a as it,J as Pn,K as Dn}from"./forensic-log-DaUcnSG0.js";import{s as Ce,T as yt,b as Qe,i as Fn}from"./state-PELIq3oj.js";import{m as De,D as Gn,s as Wn,h as Vn,b as jn,g as zn,d as Hn,e as Bn}from"./ai-usage-log-Dj9Ub_DT.js";const qn=W("chrome-api"),Kn="1.3";class Yn{constructor(t){this.target=t}async attach(){await chrome.debugger.attach(this.target,Kn)}async detach(){try{await chrome.debugger.detach(this.target)}catch(t){qn.debug("detach soft-failure",t)}}async send(t,n){return chrome.debugger.sendCommand(this.target,t,n)}}function Jn(e){const t=e instanceof Error?e.message:String(e);return/Another debugger is already attached|Cannot attach to/i.test(t)}function Xn(e){const t=e instanceof Error?e.message:String(e);return Jn(e)?Ie("DEBUGGER_BUSY","DevTools is open on this tab. Close it to run the audit.",!0,{raw:t}):Ie("STATE_DRIVE_FAILED",t,!1)}function Qn(e){const t=(n,o)=>e(n,o);return chrome.debugger.onDetach.addListener(t),()=>chrome.debugger.onDetach.removeListener(t)}const B=W("state-driver"),Zn=60*1e3,ee=new Map,Re=new Map,pe=new Map,de=new Map,Se=new Map,ea={default:[],hover:["hover"],focus:["focus"],"focus-visible":["focus","focus-visible"],active:["active"],disabled:[]};async function zt(e){let t=ee.get(e);if(!t){t=new Yn({tabId:e});try{await t.attach(),await t.send("Runtime.enable"),await t.send("Page.enable"),await t.send("DOM.enable"),await t.send("DOM.getDocument",{depth:-1,pierce:!0}),await t.send("CSS.enable")}catch(n){try{await t.detach()}catch{}throw n}ee.set(e,t),pe.set(e,new Map),ta(e),B.info("attached",{tabId:e})}return aa(e),t}function ta(e){const t=(n,o,s)=>{var c,r;if(n.tabId!==e)return;const i=pe.get(e);if(i)if(o==="Runtime.executionContextCreated"){const d=s,l=d==null?void 0:d.context;l&&((c=l.auxData)!=null&&c.isDefault)&&((r=l.auxData)!=null&&r.frameId)&&i.set(l.auxData.frameId,l.id)}else if(o==="Runtime.executionContextDestroyed"){const d=s,l=d==null?void 0:d.executionContextId;if(l!==void 0)for(const[p,h]of i.entries())h===l&&i.delete(p)}else o==="Runtime.executionContextsCleared"&&i.clear()};chrome.debugger.onEvent.addListener(t)}async function na(e){const t=ee.get(e);if(ee.delete(e),pe.delete(e),de.delete(e),Se.delete(e),t)try{await t.detach()}catch{}}function aa(e){const t=Re.get(e);t&&clearTimeout(t),Re.set(e,setTimeout(()=>{ct(e).catch(n=>B.warn("idle detach failed",n))},Zn))}async function ct(e){const t=ee.get(e);t&&(await t.detach(),ee.delete(e)),pe.delete(e),de.delete(e),Se.delete(e);const n=Re.get(e);n&&(clearTimeout(n),Re.delete(e))}function Ht(e){const t=[{id:e.frame.id,url:e.frame.url}];for(const n of e.childFrames??[])t.push(...Ht(n));return t}async function oa(e,t,n){var i;if(!n||n===0)return;const o=pe.get(t);if(!o)return;let s;try{s=(i=(await chrome.scripting.executeScript({target:{tabId:t,frameIds:[n]},func:()=>location.href}))[0])==null?void 0:i.result}catch(c){B.debug("frame URL lookup failed",c)}try{const c=await e.send("Page.getFrameTree"),r=Ht(c.frameTree);if(s){const p=r.find(h=>h.url===s);if(p){const h=o.get(p.id);if(h!==void 0)return h}}const d=c.frameTree.frame.id,l=r.find(p=>p.id!==d);return l?o.get(l.id):void 0}catch(c){B.warn("Page.getFrameTree failed",c);return}}async function sa(e,t,n){var c;const o={expression:`document.querySelector(${JSON.stringify(t)})`,objectGroup:"wcag-auditor",returnByValue:!1};n!==void 0&&(o.contextId=n);const i=(c=(await e.send("Runtime.evaluate",o)).result)==null?void 0:c.objectId;if(!i)return null;try{return(await e.send("DOM.requestNode",{objectId:i})).nodeId??null}finally{e.send("Runtime.releaseObject",{objectId:i}).catch(()=>{})}}async function Bt(e){const t=de.get(e);if(!t)return;const n=ee.get(e);if(n){try{await n.send("CSS.forcePseudoState",{nodeId:t.nodeId,forcedPseudoClasses:[]})}catch(o){B.debug("forcePseudoState clear failed (node may be gone)",o)}de.delete(e)}}async function Ze(e,t,n,o){await chrome.scripting.executeScript({target:lt(e,t),func:(s,i)=>{const c=document.querySelector(s);c&&(i?c.setAttribute("disabled",""):c.removeAttribute("disabled"))},args:[n,o]})}async function qt(e,t){await chrome.scripting.executeScript({target:lt(e,t),func:()=>{const n=document.activeElement;n&&typeof n.blur=="function"&&n.blur()}})}async function Kt(e,t){await chrome.scripting.executeScript({target:{tabId:e,frameIds:[0]},func:n=>{document.documentElement.setAttribute("dir",n)},args:[t]})}async function ra(e,t,n,o){await chrome.scripting.executeScript({target:lt(e,t),func:(s,i)=>{const c=document.querySelector(s);if(c)for(const[r,d]of Object.entries(i))c.setAttribute(r,d)},args:[n,o]})}function lt(e,t){return t!==void 0&&t!==0?{tabId:e,frameIds:[t]}:{tabId:e}}async function ia(e,t,n,o){const s=await zt(e),i=Se.get(e),c=(i==null?void 0:i.scope)===n&&(i==null?void 0:i.frameId)===o,r=t.ariaVariation?JSON.stringify(t.ariaVariation.attributes):"",d=!c||(i==null?void 0:i.pseudoState)!==t.pseudoState,l=!i||i.theme!==t.theme,p=!i||i.direction!==t.direction,h=!i||i.breakpointId!==t.breakpoint.id,f=!c||(i==null?void 0:i.ariaVariationKey)!==r;if(d&&(await Bt(e),n)){t.pseudoState==="disabled"?await Ze(e,o,n,!0):(await Ze(e,o,n,!1),await qt(e,o));const a=ea[t.pseudoState];if(a.length>0){const g=await oa(s,e,o),u=await sa(s,n,g);if(u!==null)try{await s.send("CSS.forcePseudoState",{nodeId:u,forcedPseudoClasses:a}),de.set(e,{sessionTabId:e,nodeId:u})}catch(w){B.warn(`CSS.forcePseudoState failed for :${t.pseudoState}`,w)}else B.debug(`could not resolve nodeId for ${n} (frameId=${o})`)}}if(l){const a=[{name:"prefers-reduced-motion",value:"reduce"}];t.theme==="dark"?(a.push({name:"prefers-color-scheme",value:"dark"}),a.push({name:"forced-colors",value:"none"})):t.theme==="light"?(a.push({name:"prefers-color-scheme",value:"light"}),a.push({name:"forced-colors",value:"none"})):t.theme==="forced-colors-active"?a.push({name:"forced-colors",value:"active"}):t.theme==="forced-colors-none"&&a.push({name:"forced-colors",value:"none"}),await s.send("Emulation.setEmulatedMedia",{features:a}),await new Promise(g=>setTimeout(g,50))}p&&await Kt(e,t.direction),h&&await s.send("Emulation.setDeviceMetricsOverride",{width:t.breakpoint.width,height:t.breakpoint.height,deviceScaleFactor:t.breakpoint.deviceScaleFactor,mobile:t.breakpoint.mobile}),f&&t.ariaVariation&&n&&await ra(e,o,n,t.ariaVariation.attributes),Se.set(e,{scope:n,frameId:o,pseudoState:t.pseudoState,theme:t.theme,direction:t.direction,breakpointId:t.breakpoint.id,ariaVariationKey:r})}async function ca(e,t,n){const o=ee.get(e);if(o){if(await Bt(e),Se.delete(e),t)try{await Ze(e,n,t,!1),await qt(e,n),await Kt(e,"ltr")}catch(s){B.warn("element-level reset partial failure",s)}try{await o.send("Emulation.setEmulatedMedia",{features:[]}),await o.send("Emulation.clearDeviceMetricsOverride")}catch(s){B.warn("reset partial failure",s)}await ct(e)}}async function et(e,t,n,o){try{return await ia(e,t,n,o),N({type:"STATE_CHANGED_EVENT",tabId:e,currentState:t}),{type:"STATE_DRIVE_RESPONSE",success:!0,appliedState:t}}catch(s){return B.error("state drive failed",s),await na(e),{type:"STATE_DRIVE_RESPONSE",success:!1,error:Xn(s)}}}async function Yt(e,t,n){await ca(e,t,n)}async function la(e,t={}){try{const n=await zt(e),o={format:"jpeg",quality:t.quality??75};t.fullPage&&(o.captureBeyondViewport=!0);const s=await n.send("Page.captureScreenshot",o);return s!=null&&s.data?`data:image/jpeg;base64,${s.data}`:null}catch(n){return B.debug("Page.captureScreenshot failed",n),null}}function da(){var n;const e=[];if(e.push(D("STATE_DRIVE_REQUEST",o=>et(o.tabId,o.state,o.scope,o.frameId))),e.push(D("STATE_RESET_REQUEST",o=>Yt(o.tabId))),(n=chrome.tabs)!=null&&n.onRemoved){const o=s=>{ct(s).catch(()=>{})};chrome.tabs.onRemoved.addListener(o),e.push(()=>chrome.tabs.onRemoved.removeListener(o))}const t=Qn((o,s)=>{o.tabId!==void 0&&(ee.delete(o.tabId),pe.delete(o.tabId),de.delete(o.tabId),B.warn("unexpected detach",{tabId:o.tabId,reason:s}))});return e.push(t),B.info("handlers registered"),()=>e.forEach(o=>o())}const ua="wcag-component-auditor",pa=1;let He=null;function Fe(){return He||(He=In(ua,pa,{upgrade(e,t){if(t<1){const n=e.createObjectStore("baselines",{keyPath:"componentId"});n.createIndex("byUrl","snapshotMeta.url"),n.createIndex("byDate","lastUpdated");const o=e.createObjectStore("audit-history",{keyPath:"auditId",autoIncrement:!0});o.createIndex("byComponentId","componentId"),o.createIndex("byDate","runDate")}}})),He}async function dt(e){return(await Fe()).get("baselines",e)}async function Jt(e){await(await Fe()).put("baselines",e)}async function ha(e){await(await Fe()).delete("baselines",e)}async function fa(){return(await Fe()).getAll("baselines")}const le=W("cloud-sync"),bt="cloudSyncEnabled",Be="cloudSyncEndpoint",qe="licenseToken",tt="cloudSyncVersionCache";async function ut(){try{const e=await chrome.storage.local.get([bt,Be,qe]),t=e[bt]===!0,n=typeof e[Be]=="string"?e[Be]:"",o=typeof e[qe]=="string"?e[qe]:"";return!t||!n||!o?null:{endpoint:n.replace(/\/$/,""),token:o,enabled:t}}catch{return null}}async function Xt(){try{const t=(await chrome.storage.local.get(tt))[tt];return t&&typeof t=="object"?t:{}}catch{return{}}}async function Oe(e){try{await chrome.storage.local.set({[tt]:e})}catch{}}async function ga(e,t,n){const o=await ut();if(!o)return!1;const s=await Xt(),i=s[e];try{const c=`${o.endpoint}/v1/products/wcagcheckr/baselines/${encodeURIComponent(e)}`,r=await fetch(c,{method:"PUT",headers:{"Content-Type":"application/json",Authorization:`Bearer ${o.token}`},body:JSON.stringify({snapshot:t,axeVersion:n,expectedVersion:i})});if(r.status===409)return le.warn(`baseline ${e} version conflict on push`,{expectedVersion:i}),delete s[e],await Oe(s),!1;if(!r.ok)return le.warn("baseline push failed",{status:r.status}),!1;const d=await r.json();return typeof d.version=="number"&&(s[e]=d.version,await Oe(s)),!0}catch(c){return le.debug("push network failure (offline?)",c),!1}}async function ma(e){const t=await ut();if(!t)return!1;try{const n=`${t.endpoint}/v1/products/wcagcheckr/baselines/${encodeURIComponent(e)}`,o=await fetch(n,{method:"DELETE",headers:{Authorization:`Bearer ${t.token}`}});if(o.ok){const s=await Xt();delete s[e],await Oe(s)}return o.ok}catch(n){return le.debug("delete network failure",n),!1}}async function ya(){const e=await ut();if(!e)return[];try{const t=await fetch(`${e.endpoint}/v1/products/wcagcheckr/baselines`,{headers:{Authorization:`Bearer ${e.token}`}});if(!t.ok)return le.warn("pull failed",{status:t.status}),[];const o=(await t.json()).items??[],s={};for(const i of o)s[i.componentId]=i.version;return await Oe(s),o}catch(t){return le.debug("pull network failure",t),[]}}const Qt=W("baseline-store"),wt="componentIdAliases";async function ue(e,t){try{return await t()}catch(n){const o=n instanceof Error?n.message:String(n);throw Qt.error(`baseline IDB ${e} failed: ${o}`),new Tn({code:"BASELINE_DB_ERROR",message:`Baseline storage is unavailable (${e}: ${o}).`,recoverable:!1,details:o})}}async function Ge(e){return((await chrome.storage.local.get(wt))[wt]??{})[e]??e}async function ba(e){const t=await Ge(e),n=await ue("get",()=>dt(t));return n?{violations:n.violations,snapshotMeta:n.snapshotMeta}:null}async function wa(e,t,n,o){const s=await Ge(e),i=await ue("get-for-set",()=>dt(s)),c=(i==null?void 0:i.seenOnUrls)??[],r=c.includes(n.url)?c:[...c,n.url];await ue("set",()=>Jt({componentId:s,violations:t,snapshotMeta:n,lastUpdated:new Date().toISOString(),axeVersion:n.axeVersion,announcements:o==null?void 0:o.announcements,focusEvents:o==null?void 0:o.focusEvents,seenOnUrls:r})),ga(s,{violations:t,snapshotMeta:n},n.axeVersion).catch(()=>{})}function va(e,t){if(!e||!t||gt(e)===gt(t))return;const n=i=>{var c,r,d,l,p;return(((c=i.pseudoStates)==null?void 0:c.length)??0)*Math.max(1,((r=i.ariaVariations)==null?void 0:r.length)??0)*(((d=i.themes)==null?void 0:d.length)??0)*(((l=i.directions)==null?void 0:l.length)??0)*(((p=i.breakpoints)==null?void 0:p.length)??0)},o=n(e),s=n(t);return`Baseline was captured under a different state-matrix configuration (${o} states) than this run (${s} states). Per-state-instance deltas inflate when the matrix changes — the unique-violation deltas above are still reliable.`}async function Zt(e,t,n,o){var l;const s=await Ge(e),i=await Rn(s),c=await ue("compare",()=>dt(s));if(!c){const p=t.filter(f=>i.has(f.matchKey)),h=t.filter(f=>!i.has(f.matchKey));return{new:h,persistent:[],fixed:[],newCount:h.length,persistentCount:0,fixedCount:0,baselineSnapshotMeta:null,comparedAt:new Date().toISOString(),newAnnouncements:n==null?void 0:n.announcements,newFocusEvents:n==null?void 0:n.focusEvents,acknowledged:p,acknowledgedCount:p.length}}const r=$n(c.violations,t,c.snapshotMeta,{baselineAnnouncements:c.announcements,currentAnnouncements:n==null?void 0:n.announcements,baselineFocusEvents:c.focusEvents,currentFocusEvents:n==null?void 0:n.focusEvents},i),d=va((l=c.snapshotMeta)==null?void 0:l.matrixConfig,o);return d&&(r.matrixMismatchWarning=d),r}async function Aa(e){const t=await Ge(e);await ue("delete",()=>ha(t)),ma(t).catch(()=>{})}async function Sa(){const e=await ya();for(const t of e)await Jt({componentId:t.componentId,violations:t.snapshot.violations,snapshotMeta:t.snapshot.snapshotMeta,lastUpdated:t.updatedAt,axeVersion:t.axeVersion});return e.length>0&&N({type:"SCORECARD_UPDATED_EVENT"}),e.length}async function xa(e){let t=await ue("list",()=>fa());return e!=null&&e.url&&(t=t.filter(n=>n.snapshotMeta.url===e.url)),t.map(n=>{const o=(n.focusEvents??[]).filter(l=>l.isFocusReset).length,s=(n.announcements??[]).length,i=n.violations.filter(l=>l.ruleId==="target-size").length,c=n.violations.some(l=>l.ruleId==="color-contrast"&&l.currentState.pseudoState==="hover"),r=n.violations.filter(l=>l.impact==="critical").length,d=n.violations.filter(l=>l.impact==="serious").length;return{componentId:n.componentId,violationCount:n.violations.length,lastUpdated:n.lastUpdated,seenOnUrlsCount:(n.seenOnUrls??[]).length,metrics:{criticalCount:r,seriousCount:d,focusResetCount:o,announcementCount:s,targetSizeFailCount:i,hoverContrastFail:c}}})}function ka(){const e=[];return e.push(D("BASELINE_GET",async t=>({type:"BASELINE_GET_RESPONSE",baseline:await ba(t.componentId)}))),e.push(D("BASELINE_SET",async t=>{await wa(t.componentId,t.violations,t.snapshotMeta,{announcements:t.announcements,focusEvents:t.focusEvents}),N({type:"SCORECARD_UPDATED_EVENT"})})),e.push(D("BASELINE_COMPARE",async t=>({type:"BASELINE_COMPARE_RESPONSE",delta:await Zt(t.componentId,t.currentViolations,{announcements:t.announcements,focusEvents:t.focusEvents},t.currentMatrix)}))),e.push(D("BASELINE_DELETE",async t=>{await Aa(t.componentId),N({type:"SCORECARD_UPDATED_EVENT"})})),e.push(D("BASELINE_LIST",async t=>({type:"BASELINE_LIST_RESPONSE",items:await xa(t.filter)}))),Qt.info("handlers registered"),()=>e.forEach(t=>t())}const nt="tierConfig:cache",Ea=60*60*1e3,$a=24*60*60*1e3,Ta=7*24*60*60*1e3;async function Ca(){return(await chrome.storage.local.get(nt))[nt]??null}async function Ia(e){await chrome.storage.local.set({[nt]:e})}function Ra(e,t=Date.now()){const n=t-e.fetchedAt;return n<Ea?"fresh":n<$a?"stale":n<Ta?"grace":"expired"}function Oa(e,t){const n={...e},o=u=>{if(typeof u=="number")return u===-1?1/0:u},s=u=>typeof u=="boolean"?u:void 0,i=u=>{if(u==="all"||u==="configurable"||u==="default-only")return u},c=o(t.maxComponents);c!==void 0&&(n.maxComponents=c);const r=o(t.maxBaselines);r!==void 0&&(n.maxBaselines=r);const d=i(t.stateMatrix);d!==void 0&&(n.stateMatrix=d);const l=s(t.storybookAutoIterate);l!==void 0&&(n.storybookAutoIterate=l);const p=s(t.exportJson);p!==void 0&&(n.exportJson=p);const h=s(t.exportSarif);h!==void 0&&(n.exportSarif=h);const f=s(t.exportJunit);f!==void 0&&(n.exportJunit=f);const a=s(t.cloudSync);a!==void 0&&(n.cloudSync=a);const g=s(t.forensicAnchoring);return g!==void 0&&(n.forensicAnchoring=g),n}function _a(e,t){var o;if(e==="trial")return((o=t.trial)==null?void 0:o.features)??null;const n=t.plans.find(s=>s.code===e||s.code.startsWith(e+"-"));return(n==null?void 0:n.features)??null}const at=W("tier-config-client"),Ma="wcagcheckr",Ua=`https://api.wcagcheckr.com/v1/products/${Ma}/tier-config`,Na=1e4,vt="tier-config-refresh",La=60,Pa=["trial","free","solo","team"];function en(e){const t={};for(const n of Pa){const o=_a(n,e);t[n]=o?Oa(yt[n],o):{...yt[n]}}return t}async function Da(){const e=await Ca();if(!e){Ce(null);return}if(Ra(e)==="expired"){at.warn("cached tier-config is expired (>7d); using hardcoded defaults"),Ce(null);return}Ce(en(e.config))}async function tn(){try{const e=await fetch(Ua,{signal:AbortSignal.timeout(Na),headers:{accept:"application/json"}});if(!e.ok)throw new Error(`http ${e.status}`);const t=await e.json();await Ia({fetchedAt:Date.now(),config:t}),Ce(en(t)),at.info("tier-config refreshed from server")}catch(e){at.warn("tier-config refresh failed; keeping prior cache",e)}}function Fa(){chrome.alarms.create(vt,{periodInMinutes:La}),chrome.alarms.onAlarm.addListener(e=>{e.name===vt&&tn()})}const Ga=W("settings-store"),At=1,St="__schemaVersion",ot={stateMatrix:Qe,componentIdAliases:{},ignorePatterns:[]};async function _e(e,t){return(await chrome.storage.local.get(e))[e]??t}async function Wa(){const e=await chrome.storage.local.get();if(e[St]===At)return;const t={[St]:At};for(const[n,o]of Object.entries(ot))n in e||(t[n]=o);await chrome.storage.local.set(t),Ga.info("defaults ensured")}const Va=["__","inflight:","license:","support:"];function ja(e){return!Va.some(t=>e.startsWith(t))}function za(){const e=[];return e.push(D("SETTINGS_GET",async t=>({type:"SETTINGS_RESPONSE",data:(await chrome.storage.local.get(t.key))[t.key]??ot[t.key]}))),e.push(D("SETTINGS_SET",async t=>{await chrome.storage.local.set({[t.key]:t.value})})),e.push(D("SETTINGS_GET_ALL",async()=>{const t=await chrome.storage.local.get(),n=Object.fromEntries(Object.entries(t).filter(([o])=>ja(o)));return{type:"SETTINGS_RESPONSE",data:{...ot,...n}}})),()=>e.forEach(t=>t())}const Ha={input:3/1e6,output:15/1e6},Ba={input:1/1e6,output:5/1e6},qa="claude-sonnet-4-6",Ka="https://api.anthropic.com/v1/messages",Ya="2023-06-01";function Ja(e){const t=e.model||qa,n=t.includes("haiku")?Ba:Ha;return{providerId:"anthropic",async judgeAltText({imageUrl:o,alt:s,surroundingContext:i,signal:c}){const r=await kt(o,c);if(!r)return Et("Could not fetch image to verify alt text",0,t);const d=oo(s,i),l=await q({apiKey:e.apiKey,model:t,maxTokens:300,signal:c,messages:[{role:"user",content:[{type:"image",source:{type:"base64",media_type:r.mediaType,data:r.data}},{type:"text",text:d}]}]});return Z(l,n)},async judgeHeading({headingText:o,sectionContent:s,signal:i}){const c=await q({apiKey:e.apiKey,model:t,maxTokens:250,signal:i,messages:[{role:"user",content:[{type:"text",text:so(o,s)}]}]});return Z(c,n)},async judgeSensoryLanguage({instructionText:o,signal:s}){const i=await q({apiKey:e.apiKey,model:t,maxTokens:250,signal:s,messages:[{role:"user",content:[{type:"text",text:ro(o)}]}]});return Z(i,n)},async judgeAriaSemantics({elementHtml:o,computedRole:s,surroundingHtml:i,signal:c}){const r=await q({apiKey:e.apiKey,model:t,maxTokens:350,signal:c,messages:[{role:"user",content:[{type:"text",text:io(o,s,i)}]}]});return Z(r,n)},async judgeImageOfText({imageUrl:o,accessibleName:s,signal:i}){const c=await kt(o,i);if(!c)return Et("Could not fetch image to inspect for embedded text",0,t);const r=await q({apiKey:e.apiKey,model:t,maxTokens:300,signal:i,messages:[{role:"user",content:[{type:"image",source:{type:"base64",media_type:c.mediaType,data:c.data}},{type:"text",text:co(s)}]}]});return Z(r,n)},async judgeColorOnlyMeaning({elementHtml:o,contextDescription:s,signal:i}){const c=await q({apiKey:e.apiKey,model:t,maxTokens:250,signal:i,messages:[{role:"user",content:[{type:"text",text:lo(o,s)}]}]});return Z(c,n)},async judgeGenericLinkText({linkText:o,surroundingText:s,signal:i}){const c=await q({apiKey:e.apiKey,model:t,maxTokens:250,signal:i,messages:[{role:"user",content:[{type:"text",text:uo(o,s)}]}]});return Z(c,n)},async judgeWallOfText({blockText:o,structuralChildrenCount:s,signal:i}){const c=await q({apiKey:e.apiKey,model:t,maxTokens:250,signal:i,messages:[{role:"user",content:[{type:"text",text:po(o,s)}]}]});return Z(c,n)},async judgeLanguageMismatch({declaredLang:o,bodyTextSample:s,signal:i}){const c=await q({apiKey:e.apiKey,model:t,maxTokens:250,signal:i,messages:[{role:"user",content:[{type:"text",text:ho(o,s)}]}]});return Z(c,n)},async suggestColorFix(o){const s=await q({apiKey:e.apiKey,model:t,maxTokens:600,messages:[{role:"user",content:[{type:"text",text:fo(o)}]}]});return mo(s,n)},async generateExecutiveSummary(o){const s=await q({apiKey:e.apiKey,model:t,maxTokens:800,messages:[{role:"user",content:[{type:"text",text:go(o)}]}]});return wo(s,n)},async resolveAxeIncomplete(o){const s=await q({apiKey:e.apiKey,model:t,maxTokens:400,signal:o.signal,messages:[{role:"user",content:[{type:"text",text:yo(o)}]}]});return bo(s,n,t)}}}class xe extends Error{constructor(){super("Anthropic API: canceled by caller"),this.name="ExternalAbortError"}}const nn=3e4,xt=3,Xa=1e4;function Qa(e){return e===429||e>=500}function Za(e){return e instanceof Error?e.name==="TimeoutError"||e.name==="AbortError"||e instanceof TypeError:!1}function eo(e,t){if(!t)return e;if(typeof AbortSignal.any=="function")return AbortSignal.any([e,t]);const n=new AbortController,o=()=>n.abort();return e.aborted?n.abort():e.addEventListener("abort",o,{once:!0}),t.aborted?n.abort():t.addEventListener("abort",o,{once:!0}),n.signal}async function to(e){var s,i;const t=AbortSignal.timeout(nn),n=eo(t,e.signal);let o;try{o=await fetch(Ka,{method:"POST",headers:{"x-api-key":e.apiKey,"anthropic-version":Ya,"content-type":"application/json","anthropic-dangerous-direct-browser-access":"true"},body:JSON.stringify({model:e.model,max_tokens:e.maxTokens,messages:e.messages}),signal:n})}catch(c){throw(s=e.signal)!=null&&s.aborted?new xe:c}if(!o.ok){const r=((i=(await o.json().catch(()=>({}))).error)==null?void 0:i.message)??`${o.status} ${o.statusText}`,d=new Error(`Anthropic API: ${r}`);throw d.status=o.status,d}return await o.json()}async function q(e){var n;let t=null;for(let o=1;o<=xt;o++){if((n=e.signal)!=null&&n.aborted)throw new xe;try{return await to(e)}catch(s){if(s instanceof xe)throw s;const i=s.status;if(!(Za(s)||typeof i=="number"&&Qa(i))||o===xt)throw s instanceof Error&&(s.name==="TimeoutError"||s.name==="AbortError")?new Error(`Anthropic API: timed out after ${nn/1e3}s on each of ${o} attempt${o===1?"":"s"}`):s;t=s instanceof Error?s:new Error(String(s)),await no(o*1e3,e.signal)}}throw t??new Error("Anthropic API: all retries failed")}function no(e,t){return new Promise((n,o)=>{if(t!=null&&t.aborted){o(new xe);return}const s=setTimeout(()=>{t==null||t.removeEventListener("abort",i),n()},e),i=()=>{clearTimeout(s),o(new xe)};t==null||t.addEventListener("abort",i,{once:!0})})}async function kt(e,t){var i,c;const n=new AbortController,o=setTimeout(()=>n.abort(),Xa),s=()=>n.abort();t&&(t.aborted?n.abort():t.addEventListener("abort",s,{once:!0}));try{const r=await fetch(e,{signal:n.signal});if(!r.ok)return null;const d=((c=(i=r.headers.get("content-type"))==null?void 0:i.split(";")[0])==null?void 0:c.trim())??"image/png";if(!d.startsWith("image/"))return null;const l=await r.arrayBuffer();let p="";const h=new Uint8Array(l),f=32768;for(let a=0;a<h.length;a+=f)p+=String.fromCharCode(...h.slice(a,a+f));return{data:btoa(p),mediaType:d}}catch{return null}finally{clearTimeout(o),t&&t.removeEventListener("abort",s)}}function Z(e,t){var i;const n=((i=e.content.find(c=>c.type==="text"))==null?void 0:i.text)??"",o=ao(n),s=e.usage.input_tokens*t.input+e.usage.output_tokens*t.output;return{verdict:o.verdict,reasoning:o.reasoning,confidence:o.confidence,costUsd:s,model:e.model}}function ao(e){const n=e.replace(/```(?:json)?/g,"").trim().match(/\{[\s\S]*\}/);if(!n)return{verdict:"uncertain",reasoning:"Model returned no parseable JSON",confidence:0};try{const o=JSON.parse(n[0]),s=o.verdict==="pass"||o.verdict==="fail"?o.verdict:"uncertain",i=(o.reasoning??"").trim().slice(0,600),c=typeof o.confidence=="number"?o.confidence:.5,r=Math.max(0,Math.min(1,c));return{verdict:s,reasoning:i,confidence:r}}catch{return{verdict:"uncertain",reasoning:"Model returned invalid JSON",confidence:0}}}function Et(e,t,n){return{verdict:"uncertain",reasoning:e,confidence:0,costUsd:t,model:n}}const te=`
2
2
  Respond ONLY with a single JSON object — no preamble, no markdown:
3
3
  { "verdict": "pass" | "fail" | "uncertain", "reasoning": "1-3 sentences", "confidence": 0.0-1.0 }
4
- `.trim();function ao(e,t){return`You are an accessibility auditor verifying alt text correctness for screen-reader users (WCAG 1.1.1).
4
+ `.trim();function oo(e,t){return`You are an accessibility auditor verifying alt text correctness for screen-reader users (WCAG 1.1.1).
5
5
 
6
6
  Image is attached. Current alt text: ${e?`"${e}"`:"(empty / missing)"}
7
7
  ${t?`
@@ -12,7 +12,7 @@ Judge whether the alt text accurately represents the image's content or function
12
12
  - "fail" = alt is wrong, misleading, generic ("image", "photo"), filename-style ("img-1234.jpg"), or omitted on a meaningful image
13
13
  - "uncertain" = you can't tell from the image alone whether the alt is appropriate
14
14
 
15
- ${te}`}function oo(e,t){return`You are an accessibility auditor verifying that headings describe their sections (WCAG 2.4.6).
15
+ ${te}`}function so(e,t){return`You are an accessibility auditor verifying that headings describe their sections (WCAG 2.4.6).
16
16
 
17
17
  Heading text: "${e}"
18
18
  Section content beneath it (first 800 chars): "${t.slice(0,800)}"
@@ -22,7 +22,7 @@ Judge whether the heading actually describes what the section contains:
22
22
  - "fail" = heading is non-descriptive ("Section 1", "Welcome", "More info"), unrelated to the content, or generic placeholder
23
23
  - "uncertain" = section content is too short or ambiguous to judge
24
24
 
25
- ${te}`}function so(e){return`You are an accessibility auditor checking instructions for sensory-characteristic dependencies (WCAG 1.3.3).
25
+ ${te}`}function ro(e){return`You are an accessibility auditor checking instructions for sensory-characteristic dependencies (WCAG 1.3.3).
26
26
 
27
27
  Instruction text: "${e.slice(0,800)}"
28
28
 
@@ -31,7 +31,7 @@ WCAG 1.3.3 says instructions must NOT rely solely on shape, size, color, visual
31
31
  - "pass" = instruction uses programmatic identifiers (button label, link text) — sensory cues are fine when accompanied by a name
32
32
  - "uncertain" = ambiguous whether a sensory descriptor is the only cue
33
33
 
34
- ${te}`}function ro(e,t,n){return`You are an accessibility auditor verifying ARIA role appropriateness (WCAG 4.1.2).
34
+ ${te}`}function io(e,t,n){return`You are an accessibility auditor verifying ARIA role appropriateness (WCAG 4.1.2).
35
35
 
36
36
  Element (truncated): ${e.slice(0,600)}
37
37
  Computed role: ${t}
@@ -42,7 +42,7 @@ Judge whether the role is semantically appropriate for this element's apparent p
42
42
  - "fail" = role is misused — e.g., role="button" on a div with no event handlers, role="dialog" on a non-modal overlay, role="navigation" on a non-nav block
43
43
  - "uncertain" = can't tell the element's true behavior from this static snapshot
44
44
 
45
- ${te}`}function io(e){return`You are an accessibility auditor checking for WCAG 1.4.5 (Images of Text) compliance.
45
+ ${te}`}function co(e){return`You are an accessibility auditor checking for WCAG 1.4.5 (Images of Text) compliance.
46
46
 
47
47
  WCAG 1.4.5 says: if text can be presented using actual text instead of a baked-in image, it should be. Exceptions: logos, decorative use, or text that's essential to a specific visual presentation (e.g., a screenshot of code).
48
48
 
@@ -54,7 +54,7 @@ Judge whether the image bakes substantial text into the raster:
54
54
  - "pass" = the image is purely visual (photo, illustration, icon) with no substantial baked-in text, OR the baked-in text is a logo / brand mark / unavoidable visual artifact (code screenshot, equation, etc.).
55
55
  - "uncertain" = the image is too small / low-resolution to tell, or the text is partial/ambiguous.
56
56
 
57
- ${te}`}function co(e,t){return`You are an accessibility auditor checking for WCAG 1.4.1 (Use of Color) compliance.
57
+ ${te}`}function lo(e,t){return`You are an accessibility auditor checking for WCAG 1.4.1 (Use of Color) compliance.
58
58
 
59
59
  WCAG 1.4.1 says: color must not be the only visual means of conveying information, indicating an action, prompting a response, or distinguishing a visual element. There must always be a parallel non-color signal — a text label, an icon shape, a position cue, etc.
60
60
 
@@ -68,7 +68,7 @@ Judge whether the element conveys meaning ONLY through color:
68
68
  - "pass" = the element has a visible text label, an icon child, an aria-label, OR the color is decorative — any non-color signal that a color-blind / grayscale user could detect.
69
69
  - "uncertain" = markup is ambiguous (e.g. the contextDescription says "has icon child" but the icon may itself be color-only).
70
70
 
71
- ${te}`}function lo(e,t){return`You are an accessibility auditor checking for WCAG 2.4.4 (Link Purpose in Context).
71
+ ${te}`}function uo(e,t){return`You are an accessibility auditor checking for WCAG 2.4.4 (Link Purpose in Context).
72
72
 
73
73
  WCAG 2.4.4 says: the purpose of each link must be determinable from the link text alone, OR from the link text together with its programmatically-determined context (the surrounding sentence / paragraph / list item).
74
74
 
@@ -80,7 +80,7 @@ Judge whether a screen-reader user activating "links list" navigation could unde
80
80
  - "pass" = link text alone is descriptive, OR the surrounding context unambiguously clarifies the destination (e.g., "Pricing details are available — learn more →" makes "learn more" passable).
81
81
  - "uncertain" = link text is borderline-generic and context is ambiguous.
82
82
 
83
- ${te}`}function uo(e,t){return`You are an accessibility auditor checking for WCAG 3.1.5 (Reading Level) and broader cognitive-accessibility issues.
83
+ ${te}`}function po(e,t){return`You are an accessibility auditor checking for WCAG 3.1.5 (Reading Level) and broader cognitive-accessibility issues.
84
84
 
85
85
  A "wall of text" is body content that exceeds reasonable read-without-fatigue size with no structural breaks (subheadings, lists, paragraphs, blockquotes). Cognitively-disabled users + speed-skim readers can't scan it.
86
86
 
@@ -92,7 +92,7 @@ Judge whether this block presents a wall-of-text problem:
92
92
  - "pass" = block is short enough OR has adequate structural breaks (paragraphs, sub-headings, lists) for scannable reading.
93
93
  - "uncertain" = block is borderline (~200-300 words with minimal structure).
94
94
 
95
- ${te}`}function po(e,t){return`You are an accessibility auditor checking for WCAG 3.1.1 (Language of Page) and 3.1.2 (Language of Parts).
95
+ ${te}`}function ho(e,t){return`You are an accessibility auditor checking for WCAG 3.1.1 (Language of Page) and 3.1.2 (Language of Parts).
96
96
 
97
97
  The page declares its default human language via the HTML \`lang\` attribute. Screen readers use that to pick the correct pronunciation engine. If the declared language doesn't match the actual content, screen-reader output is unintelligible.
98
98
 
@@ -104,7 +104,7 @@ Judge whether the declared language matches the content:
104
104
  - "pass" = declared language matches the content's primary language.
105
105
  - "uncertain" = content is too short, too multilingual, or in a language closely related to the declared one.
106
106
 
107
- ${te}`}function ho(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.
107
+ ${te}`}function fo(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.
108
108
 
109
109
  Current pair (FAILING):
110
110
  - Foreground: ${e.foreground}
@@ -131,7 +131,7 @@ Respond ONLY with a JSON object — no preamble, no markdown:
131
131
  { "foreground": "#hexHEX", "background": "#hexHEX", "contrast": 4.6, "rationale": "1 sentence" }
132
132
  ],
133
133
  "reasoning": "1-3 sentences explaining your overall approach or why no suggestions are possible"
134
- }`}function fo(e){return`You are an accessibility auditor writing the executive summary for an automated audit report.
134
+ }`}function go(e){return`You are an accessibility auditor writing the executive summary for an automated audit report.
135
135
 
136
136
  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"}.
137
137
  Framing: ${e.framing} report.
@@ -162,7 +162,7 @@ Respond ONLY with a JSON object — no preamble, no markdown:
162
162
  "lead": "2-3 sentence headline framing",
163
163
  "body": "2-3 paragraphs of risk explanation + audit-date framing + ongoing-diligence context if applicable. ~150-300 words total.",
164
164
  "closer": "1-2 sentence closing line"
165
- }`}function go(e,t){var c;const s=(((c=e.content.find(r=>r.type==="text"))==null?void 0:c.text)??"").replace(/```(?:json)?/g,"").trim().match(/\{[\s\S]*\}/),i=e.usage.input_tokens*t.input+e.usage.output_tokens*t.output;if(!s)return{verdict:"error",candidates:[],reasoning:"Model returned no parseable JSON",costUsd:i,model:e.model};try{const r=JSON.parse(s[0]),d=(r.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:r.verdict==="suggested"&&d.length>0?"suggested":"no-suggestion",candidates:d,reasoning:typeof r.reasoning=="string"?r.reasoning:"",costUsd:i,model:e.model}}catch{return{verdict:"error",candidates:[],reasoning:"Could not parse color suggestions",costUsd:i,model:e.model}}}function mo(e){var o,s,i,c,r,d;const t=(o=e.element.styles)==null?void 0:o.effectiveBackground,n=e.ruleId==="color-contrast"||e.ruleId==="color-contrast-enhanced"?`
165
+ }`}function mo(e,t){var c;const s=(((c=e.content.find(r=>r.type==="text"))==null?void 0:c.text)??"").replace(/```(?:json)?/g,"").trim().match(/\{[\s\S]*\}/),i=e.usage.input_tokens*t.input+e.usage.output_tokens*t.output;if(!s)return{verdict:"error",candidates:[],reasoning:"Model returned no parseable JSON",costUsd:i,model:e.model};try{const r=JSON.parse(s[0]),d=(r.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:r.verdict==="suggested"&&d.length>0?"suggested":"no-suggestion",candidates:d,reasoning:typeof r.reasoning=="string"?r.reasoning:"",costUsd:i,model:e.model}}catch{return{verdict:"error",candidates:[],reasoning:"Could not parse color suggestions",costUsd:i,model:e.model}}}function yo(e){var o,s,i,c,r,d;const t=(o=e.element.styles)==null?void 0:o.effectiveBackground,n=e.ruleId==="color-contrast"||e.ruleId==="color-contrast-enhanced"?`
166
166
  This is a COLOR-CONTRAST evaluation. axe-core couldn't determine the
167
167
  contrast ratio (commonly because the element's own background-color
168
168
  is transparent and axe didn't walk the DOM cascade to find the
@@ -235,15 +235,15 @@ Respond with strict JSON (no markdown fences):
235
235
  {
236
236
  "verdict": "pass" | "fail" | "uncertain",
237
237
  "reasoning": "one or two sentences explaining the determination, citing the contrast ratio for color-contrast"
238
- }`}function yo(e,t,n){var r;const o=((r=e.content.find(d=>d.type==="text"))==null?void 0:r.text)??"",i=o.replace(/```(?:json)?/g,"").trim().match(/\{[\s\S]*\}/),c=e.usage.input_tokens*t.input+e.usage.output_tokens*t.output;if(!i)return{verdict:"uncertain",reasoning:o||"AI returned no parseable response.",costUsd:c,model:e.model||n};try{const d=JSON.parse(i[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:c,model:e.model||n}}catch{return{verdict:"uncertain",reasoning:o,costUsd:c,model:e.model||n}}}function bo(e,t){var c;const n=((c=e.content.find(r=>r.type==="text"))==null?void 0:c.text)??"",s=n.replace(/```(?:json)?/g,"").trim().match(/\{[\s\S]*\}/),i=e.usage.input_tokens*t.input+e.usage.output_tokens*t.output;if(!s)return{lead:"",body:n||"Summary generation failed.",closer:"",costUsd:i,model:e.model};try{const r=JSON.parse(s[0]);return{lead:typeof r.lead=="string"?r.lead:"",body:typeof r.body=="string"?r.body:"",closer:typeof r.closer=="string"?r.closer:"",costUsd:i,model:e.model}}catch{return{lead:"",body:n,closer:"",costUsd:i,model:e.model}}}function K(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:Ya({apiKey:e.apiKey,model:e.model})};case"openai":return{ok:!1,reason:"OpenAI provider not yet implemented"};case"gemini":return{ok:!1,reason:"Gemini provider not yet implemented"};default:return{ok:!1,reason:`Unknown provider: ${e.provider}`}}}function Y(e){const t={capUsd:Math.max(0,e),spentUsd:0,exceeded:!1};return{state:t,canCharge(){return!t.exceeded&&t.spentUsd<t.capUsd},recordCharge(n){t.spentUsd+=Math.max(0,n),t.spentUsd>=t.capUsd&&(t.exceeded=!0)}}}const qe=W("ai-compliance-summarizer");async function nn(e,t){if(!t.enabled)return null;const n=K(t);if(!n.ok)return qe.debug(`AI client unavailable: ${n.reason}`),null;const o=Y(t.costCapUsd);if(!o.canCharge())return null;try{const s=await n.client.generateExecutiveSummary(e);return o.recordCharge(s.costUsd),!s.lead&&!s.body?(qe.debug("AI summary returned empty"),null):s}catch(s){return qe.warn("compliance-summary generation failed",s),null}}async function wo(){const e=await chrome.storage.local.get("aiConfig");return Pe(e.aiConfig)}function vo(){return D("AI_COMPLIANCE_SUMMARY_REQUEST",async e=>{const t=await wo();if(!t.enabled)return{type:"AI_COMPLIANCE_SUMMARY_RESPONSE",summary:null,unavailableReason:"AI augmentation is disabled in Settings → AI augmentation."};if(!t.apiKey)return{type:"AI_COMPLIANCE_SUMMARY_RESPONSE",summary:null,unavailableReason:"No AI API key configured."};const n=await nn(e.input,t);return{type:"AI_COMPLIANCE_SUMMARY_RESPONSE",summary:n,unavailableReason:n?void 0:"AI summary generation returned no result."}})}const X="wcag-component-auditor";function re(){try{return chrome.runtime.getManifest().version}catch{return"0.0.0"}}function ae(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 Ao(e,t){return{schemaVersion:1,generatedAt:new Date().toISOString(),tool:{name:X,version:re()},results:e,delta:t}}function Et(e){return e==="critical"||e==="serious"?"error":e==="moderate"?"warning":"note"}function So(e,t){const n=(t?t.new:e.flatMap(r=>r.violations)).filter(r=>!r.needsReview),o=new Set,s=[];for(const r of n)o.has(r.ruleId)||(o.add(r.ruleId),s.push({id:r.ruleId,shortDescription:{text:r.description||r.ruleId},helpUri:r.helpUrl||void 0,defaultConfiguration:{level:Et(r.impact)},properties:{tags:[r.wcagCriterion,`wcag-${r.wcagLevel}`]}}));const i=ae(e),c=n.map(r=>({ruleId:r.ruleId,level:Et(r.impact),message:{text:`${r.description}`+(r.target.failureSummary?`
238
+ }`}function bo(e,t,n){var r;const o=((r=e.content.find(d=>d.type==="text"))==null?void 0:r.text)??"",i=o.replace(/```(?:json)?/g,"").trim().match(/\{[\s\S]*\}/),c=e.usage.input_tokens*t.input+e.usage.output_tokens*t.output;if(!i)return{verdict:"uncertain",reasoning:o||"AI returned no parseable response.",costUsd:c,model:e.model||n};try{const d=JSON.parse(i[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:c,model:e.model||n}}catch{return{verdict:"uncertain",reasoning:o,costUsd:c,model:e.model||n}}}function wo(e,t){var c;const n=((c=e.content.find(r=>r.type==="text"))==null?void 0:c.text)??"",s=n.replace(/```(?:json)?/g,"").trim().match(/\{[\s\S]*\}/),i=e.usage.input_tokens*t.input+e.usage.output_tokens*t.output;if(!s)return{lead:"",body:n||"Summary generation failed.",closer:"",costUsd:i,model:e.model};try{const r=JSON.parse(s[0]);return{lead:typeof r.lead=="string"?r.lead:"",body:typeof r.body=="string"?r.body:"",closer:typeof r.closer=="string"?r.closer:"",costUsd:i,model:e.model}}catch{return{lead:"",body:n,closer:"",costUsd:i,model:e.model}}}function K(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:Ja({apiKey:e.apiKey,model:e.model})};case"openai":return{ok:!1,reason:"OpenAI provider not yet implemented"};case"gemini":return{ok:!1,reason:"Gemini provider not yet implemented"};default:return{ok:!1,reason:`Unknown provider: ${e.provider}`}}}function Y(e){const t={capUsd:Math.max(0,e),spentUsd:0,exceeded:!1};return{state:t,canCharge(){return!t.exceeded&&t.spentUsd<t.capUsd},recordCharge(n){t.spentUsd+=Math.max(0,n),t.spentUsd>=t.capUsd&&(t.exceeded=!0)}}}const Ke=W("ai-compliance-summarizer");async function an(e,t){if(!t.enabled)return null;const n=K(t);if(!n.ok)return Ke.debug(`AI client unavailable: ${n.reason}`),null;const o=Y(t.costCapUsd);if(!o.canCharge())return null;try{const s=await n.client.generateExecutiveSummary(e);return o.recordCharge(s.costUsd),!s.lead&&!s.body?(Ke.debug("AI summary returned empty"),null):s}catch(s){return Ke.warn("compliance-summary generation failed",s),null}}async function vo(){const e=await chrome.storage.local.get("aiConfig");return De(e.aiConfig)}function Ao(){return D("AI_COMPLIANCE_SUMMARY_REQUEST",async e=>{const t=await vo();if(!t.enabled)return{type:"AI_COMPLIANCE_SUMMARY_RESPONSE",summary:null,unavailableReason:"AI augmentation is disabled in Settings → AI augmentation."};if(!t.apiKey)return{type:"AI_COMPLIANCE_SUMMARY_RESPONSE",summary:null,unavailableReason:"No AI API key configured."};const n=await an(e.input,t);return{type:"AI_COMPLIANCE_SUMMARY_RESPONSE",summary:n,unavailableReason:n?void 0:"AI summary generation returned no result."}})}const X="wcag-component-auditor";function re(){try{return chrome.runtime.getManifest().version}catch{return"0.0.0"}}function ae(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 So(e,t){return{schemaVersion:1,generatedAt:new Date().toISOString(),tool:{name:X,version:re()},results:e,delta:t}}function $t(e){return e==="critical"||e==="serious"?"error":e==="moderate"?"warning":"note"}function xo(e,t){const n=(t?t.new:e.flatMap(r=>r.violations)).filter(r=>!r.needsReview),o=new Set,s=[];for(const r of n)o.has(r.ruleId)||(o.add(r.ruleId),s.push({id:r.ruleId,shortDescription:{text:r.description||r.ruleId},helpUri:r.helpUrl||void 0,defaultConfiguration:{level:$t(r.impact)},properties:{tags:[r.wcagCriterion,`wcag-${r.wcagLevel}`]}}));const i=ae(e),c=n.map(r=>({ruleId:r.ruleId,level:$t(r.impact),message:{text:`${r.description}`+(r.target.failureSummary?`
239
239
 
240
- ${r.target.failureSummary}`:"")},locations:[{physicalLocation:{artifactLocation:{uri:i&&/^https?:\/\//.test(i)?`${i}#${r.target.selector}`:r.target.selector?`selector://${r.target.selector}`:"unknown"},region:{snippet:{text:r.target.outerHTML}}},logicalLocations:[{name:r.componentId,kind:"module"}]}],properties:{componentId:r.componentId,wcagCriterion:r.wcagCriterion,wcagLevel:r.wcagLevel,pseudoState:r.currentState.pseudoState,theme:r.currentState.theme,direction:r.currentState.direction,breakpoint:r.currentState.breakpoint.id,detectedAt:r.detectedAt}}));return{$schema:"https://json.schemastore.org/sarif-2.1.0.json",version:"2.1.0",runs:[{tool:{driver:{name:X,version:re(),informationUri:"https://wcagcheckr.com",rules:s}},results:c}]}}function an(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;")}function oe(e){return an(e).replace(/\n/g,"&#10;").replace(/\r/g,"&#13;")}function xo(e,t){const n=!!t,o=l=>(n?l.violations.filter(p=>t.new.some(h=>h.matchKey===p.matchKey)):l.violations).filter(p=>!p.needsReview),s=e.reduce((l,p)=>l+Math.max(1,p.violations.length),0),i=e.reduce((l,p)=>l+o(p).length,0),c=e.reduce((l,p)=>l+p.durationMs,0)/1e3,r=[];r.push('<?xml version="1.0" encoding="UTF-8"?>');const d=`${X} — ${ae(e)}`;r.push(`<testsuites name="${oe(d)}" tests="${s}" failures="${i}" time="${c.toFixed(3)}">`);for(const l of e){const p=`${l.state.pseudoState}_${l.state.theme}_${l.state.direction}_${l.state.breakpoint.id}`,h=`${l.componentId}::${p}`,f=o(l),a=Math.max(1,l.violations.length);if(r.push(` <testsuite name="${oe(h)}" tests="${a}" failures="${f.length}" time="${(l.durationMs/1e3).toFixed(3)}">`),l.violations.length===0)r.push(` <testcase classname="${oe(h)}" name="no-violations" time="${(l.durationMs/1e3).toFixed(3)}" />`);else for(const g of l.violations){const u=f.some(E=>E.matchKey===g.matchKey),v=`classname="${oe(h)}" name="${oe(g.ruleId)}" time="0"`;if(!u){r.push(` <testcase ${v} />`);continue}const O=`${g.impact} - ${g.description}`,S=`Selector: ${g.target.selector}
240
+ ${r.target.failureSummary}`:"")},locations:[{physicalLocation:{artifactLocation:{uri:i&&/^https?:\/\//.test(i)?`${i}#${r.target.selector}`:r.target.selector?`selector://${r.target.selector}`:"unknown"},region:{snippet:{text:r.target.outerHTML}}},logicalLocations:[{name:r.componentId,kind:"module"}]}],properties:{componentId:r.componentId,wcagCriterion:r.wcagCriterion,wcagLevel:r.wcagLevel,pseudoState:r.currentState.pseudoState,theme:r.currentState.theme,direction:r.currentState.direction,breakpoint:r.currentState.breakpoint.id,detectedAt:r.detectedAt}}));return{$schema:"https://json.schemastore.org/sarif-2.1.0.json",version:"2.1.0",runs:[{tool:{driver:{name:X,version:re(),informationUri:"https://wcagcheckr.com",rules:s}},results:c}]}}function on(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;")}function oe(e){return on(e).replace(/\n/g,"&#10;").replace(/\r/g,"&#13;")}function ko(e,t){const n=!!t,o=l=>(n?l.violations.filter(p=>t.new.some(h=>h.matchKey===p.matchKey)):l.violations).filter(p=>!p.needsReview),s=e.reduce((l,p)=>l+Math.max(1,p.violations.length),0),i=e.reduce((l,p)=>l+o(p).length,0),c=e.reduce((l,p)=>l+p.durationMs,0)/1e3,r=[];r.push('<?xml version="1.0" encoding="UTF-8"?>');const d=`${X} — ${ae(e)}`;r.push(`<testsuites name="${oe(d)}" tests="${s}" failures="${i}" time="${c.toFixed(3)}">`);for(const l of e){const p=`${l.state.pseudoState}_${l.state.theme}_${l.state.direction}_${l.state.breakpoint.id}`,h=`${l.componentId}::${p}`,f=o(l),a=Math.max(1,l.violations.length);if(r.push(` <testsuite name="${oe(h)}" tests="${a}" failures="${f.length}" time="${(l.durationMs/1e3).toFixed(3)}">`),l.violations.length===0)r.push(` <testcase classname="${oe(h)}" name="no-violations" time="${(l.durationMs/1e3).toFixed(3)}" />`);else for(const g of l.violations){const u=f.some(E=>E.matchKey===g.matchKey),w=`classname="${oe(h)}" name="${oe(g.ruleId)}" time="0"`;if(!u){r.push(` <testcase ${w} />`);continue}const O=`${g.impact} - ${g.description}`,S=`Selector: ${g.target.selector}
241
241
  WCAG: ${g.wcagCriterion} (${g.wcagLevel})
242
242
  State: ${p}
243
243
  `+(g.helpUrl?`Help: ${g.helpUrl}
244
244
  `:"")+(g.target.failureSummary?`
245
- ${g.target.failureSummary}`:"");r.push(` <testcase ${v}>`),r.push(` <failure message="${oe(O)}" type="${oe(g.ruleId)}">${an(S)}</failure>`),r.push(" </testcase>")}r.push(" </testsuite>")}return r.push("</testsuites>"),r.join(`
246
- `)}function y(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}function ko(e,t){var O,S,E,k,P;const n=((O=e[0])==null?void 0:O.componentId)??"unknown",o=ae(e),s=e.reduce((b,_)=>b+_.durationMs,0),i=e.flatMap(b=>b.violations),c=new Map;for(const b of i){const _=`${b.ruleId}::${b.target.selector}`,M=`:${b.currentState.pseudoState} · ${b.currentState.theme} · ${b.currentState.direction}`,x=c.get(_);if(x){x._states.includes(M)||x._states.push(M);continue}c.set(_,{...b,_states:[M]})}const r=Array.from(c.values()),d={critical:[],serious:[],moderate:[],minor:[]};for(const b of r)(d[b.impact]??d.moderate).push(b);const l=new Set(((S=t==null?void 0:t.new)==null?void 0:S.map(b=>`${b.ruleId}::${b.target.selector}`))??[]),p=new Set(((E=t==null?void 0:t.persistent)==null?void 0:E.map(b=>`${b.ruleId}::${b.target.selector}`))??[]),h=new Set(((k=t==null?void 0:t.fixed)==null?void 0:k.map(b=>`${b.ruleId}::${b.target.selector}`))??[]);let f=0,a=0;for(const b of r){const _=`${b.ruleId}::${b.target.selector}`;p.has(_)?a++:l.has(_)&&f++}let g=0;for(const b of h)!p.has(b)&&!l.has(b)&&g++;const u=b=>{const _=l.has(`${b.ruleId}::${b.target.selector}`);return`
245
+ ${g.target.failureSummary}`:"");r.push(` <testcase ${w}>`),r.push(` <failure message="${oe(O)}" type="${oe(g.ruleId)}">${on(S)}</failure>`),r.push(" </testcase>")}r.push(" </testsuite>")}return r.push("</testsuites>"),r.join(`
246
+ `)}function y(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}function Eo(e,t){var O,S,E,k,P;const n=((O=e[0])==null?void 0:O.componentId)??"unknown",o=ae(e),s=e.reduce((b,_)=>b+_.durationMs,0),i=e.flatMap(b=>b.violations),c=new Map;for(const b of i){const _=`${b.ruleId}::${b.target.selector}`,M=`:${b.currentState.pseudoState} · ${b.currentState.theme} · ${b.currentState.direction}`,x=c.get(_);if(x){x._states.includes(M)||x._states.push(M);continue}c.set(_,{...b,_states:[M]})}const r=Array.from(c.values()),d={critical:[],serious:[],moderate:[],minor:[]};for(const b of r)(d[b.impact]??d.moderate).push(b);const l=new Set(((S=t==null?void 0:t.new)==null?void 0:S.map(b=>`${b.ruleId}::${b.target.selector}`))??[]),p=new Set(((E=t==null?void 0:t.persistent)==null?void 0:E.map(b=>`${b.ruleId}::${b.target.selector}`))??[]),h=new Set(((k=t==null?void 0:t.fixed)==null?void 0:k.map(b=>`${b.ruleId}::${b.target.selector}`))??[]);let f=0,a=0;for(const b of r){const _=`${b.ruleId}::${b.target.selector}`;p.has(_)?a++:l.has(_)&&f++}let g=0;for(const b of h)!p.has(b)&&!l.has(b)&&g++;const u=b=>{const _=l.has(`${b.ruleId}::${b.target.selector}`);return`
247
247
  <article class="violation impact-${y(b.impact)}${_?" is-new":""}">
248
248
  <header>
249
249
  <span class="rule">${y(b.ruleId)}</span>
@@ -256,7 +256,7 @@ ${g.target.failureSummary}`:"");r.push(` <testcase ${v}>`),r.push(` <fai
256
256
  <p class="meta"><strong>Found in state(s):</strong> ${y(b._states.join(", "))}</p>
257
257
  <pre class="snippet"><code>${y(b.target.outerHTML)}</code></pre>
258
258
  ${b.helpUrl?`<p class="help"><a href="${y(b.helpUrl)}">Reference: ${y(b.helpUrl)}</a></p>`:""}
259
- </article>`},v=(b,_)=>_.length===0?"":`
259
+ </article>`},w=(b,_)=>_.length===0?"":`
260
260
  <section class="impact-section impact-${y(b)}">
261
261
  <h2>${y(b[0].toUpperCase()+b.slice(1))} (${_.length})</h2>
262
262
  ${_.map(u).join("")}
@@ -350,21 +350,21 @@ ${g.target.failureSummary}`:"");r.push(` <testcase ${v}>`),r.push(` <fai
350
350
  Tip: use your browser's print dialog (Ctrl+P / Cmd+P) → "Save as PDF" to produce a PDF file.
351
351
  </p>
352
352
  </header>
353
- ${v("critical",d.critical)}
354
- ${v("serious",d.serious)}
355
- ${v("moderate",d.moderate)}
356
- ${v("minor",d.minor)}
353
+ ${w("critical",d.critical)}
354
+ ${w("serious",d.serious)}
355
+ ${w("moderate",d.moderate)}
356
+ ${w("minor",d.minor)}
357
357
  <footer>
358
358
  Generated by ${X} v${re()}. Full audit — all detected violations included, deduped across states. Items flagged "NEW vs baseline" were not present in the most-recent accepted baseline.
359
359
  </footer>
360
360
  </body>
361
- </html>`}const on=[{ref:"1.1.1",title:"Non-text Content",level:"A",rules:["image-alt","input-image-alt","area-alt","object-alt","svg-img-alt"]},{ref:"1.2.1",title:"Audio-only and Video-only (Prerecorded)",level:"A",rules:[]},{ref:"1.2.2",title:"Captions (Prerecorded)",level:"A",rules:["video-caption"]},{ref:"1.3.1",title:"Info and Relationships",level:"A",rules:["heading-order","list","listitem","definition-list","dlitem","th-has-data-cells","td-headers-attr","aria-required-children","aria-required-parent","label"]},{ref:"1.3.2",title:"Meaningful Sequence",level:"A",rules:[]},{ref:"1.3.3",title:"Sensory Characteristics",level:"A",rules:[]},{ref:"1.3.4",title:"Orientation",level:"AA",rules:["css-orientation-lock"]},{ref:"1.3.5",title:"Identify Input Purpose",level:"AA",rules:["autocomplete-valid"]},{ref:"1.4.1",title:"Use of Color",level:"A",rules:["link-in-text-block"]},{ref:"1.4.2",title:"Audio Control",level:"A",rules:["no-autoplay-audio"]},{ref:"1.4.3",title:"Contrast (Minimum)",level:"AA",rules:["color-contrast"]},{ref:"1.4.4",title:"Resize Text",level:"AA",rules:["meta-viewport"]},{ref:"1.4.10",title:"Reflow",level:"AA",rules:[]},{ref:"1.4.11",title:"Non-text Contrast",level:"AA",rules:[]},{ref:"1.4.12",title:"Text Spacing",level:"AA",rules:[]},{ref:"1.4.13",title:"Content on Hover or Focus",level:"AA",rules:[]},{ref:"2.1.1",title:"Keyboard",level:"A",rules:["nested-interactive","frame-focusable-content"]},{ref:"2.1.2",title:"No Keyboard Trap",level:"A",rules:[]},{ref:"2.1.4",title:"Character Key Shortcuts",level:"A",rules:[]},{ref:"2.2.1",title:"Timing Adjustable",level:"A",rules:["meta-refresh"]},{ref:"2.2.2",title:"Pause, Stop, Hide",level:"A",rules:["blink","marquee"]},{ref:"2.4.1",title:"Bypass Blocks",level:"A",rules:["bypass"]},{ref:"2.4.2",title:"Page Titled",level:"A",rules:["document-title"]},{ref:"2.4.3",title:"Focus Order",level:"A",rules:["tabindex"]},{ref:"2.4.4",title:"Link Purpose (In Context)",level:"A",rules:["link-name"]},{ref:"2.4.5",title:"Multiple Ways",level:"AA",rules:[]},{ref:"2.4.6",title:"Headings and Labels",level:"AA",rules:["empty-heading","empty-table-header"]},{ref:"2.4.7",title:"Focus Visible",level:"AA",rules:[]},{ref:"2.5.1",title:"Pointer Gestures",level:"A",rules:[]},{ref:"2.5.2",title:"Pointer Cancellation",level:"A",rules:[]},{ref:"2.5.3",title:"Label in Name",level:"A",rules:["label-content-name-mismatch"]},{ref:"2.5.4",title:"Motion Actuation",level:"A",rules:[]},{ref:"2.5.8",title:"Target Size (Minimum)",level:"AA",rules:["target-size"]},{ref:"3.1.1",title:"Language of Page",level:"A",rules:["html-has-lang","html-lang-valid"]},{ref:"3.1.2",title:"Language of Parts",level:"AA",rules:["valid-lang"]},{ref:"3.2.1",title:"On Focus",level:"A",rules:[]},{ref:"3.2.2",title:"On Input",level:"A",rules:[]},{ref:"3.2.3",title:"Consistent Navigation",level:"AA",rules:[]},{ref:"3.2.4",title:"Consistent Identification",level:"AA",rules:[]},{ref:"3.3.1",title:"Error Identification",level:"A",rules:[]},{ref:"3.3.2",title:"Labels or Instructions",level:"A",rules:["label","form-field-multiple-labels","select-name"]},{ref:"3.3.3",title:"Error Suggestion",level:"AA",rules:[]},{ref:"3.3.4",title:"Error Prevention (Legal, Financial, Data)",level:"AA",rules:[]},{ref:"4.1.1",title:"Parsing",level:"A",rules:["duplicate-id","duplicate-id-aria","duplicate-id-active"]},{ref:"4.1.2",title:"Name, Role, Value",level:"A",rules:["button-name","aria-required-attr","aria-allowed-attr","aria-roles","aria-valid-attr","aria-valid-attr-value","aria-input-field-name","aria-toggle-field-name","select-name","input-button-name"]},{ref:"4.1.3",title:"Status Messages",level:"AA",rules:[]}];function sn(e,t,n){if(e.rules.length===0)return{conformance:"Not Applicable",hits:[]};const o=e.rules.filter(i=>t.has(i));return o.length>0?{conformance:"Partially Supports",hits:o}:{conformance:e.rules.some(i=>n.has(i))?"Supports":"Not Evaluated",hits:o}}function Eo(e,t){var l,p;const n=((l=e[0])==null?void 0:l.componentId)??"unknown",o=ae(e),s=e.flatMap(h=>h.violations),i=new Set(s.map(h=>h.ruleId)),c=new Set(i);for(const h of e){const f=h.axeRulesEvaluated;if(f){for(const a of f.passed)c.add(a.ruleId);for(const a of f.inapplicable)c.add(a.ruleId);for(const a of f.incomplete)c.add(a.ruleId)}}const r=h=>{const{conformance:f,hits:a}=sn(h,i,c);return`
361
+ </html>`}const sn=[{ref:"1.1.1",title:"Non-text Content",level:"A",rules:["image-alt","input-image-alt","area-alt","object-alt","svg-img-alt"]},{ref:"1.2.1",title:"Audio-only and Video-only (Prerecorded)",level:"A",rules:[]},{ref:"1.2.2",title:"Captions (Prerecorded)",level:"A",rules:["video-caption"]},{ref:"1.3.1",title:"Info and Relationships",level:"A",rules:["heading-order","list","listitem","definition-list","dlitem","th-has-data-cells","td-headers-attr","aria-required-children","aria-required-parent","label"]},{ref:"1.3.2",title:"Meaningful Sequence",level:"A",rules:[]},{ref:"1.3.3",title:"Sensory Characteristics",level:"A",rules:[]},{ref:"1.3.4",title:"Orientation",level:"AA",rules:["css-orientation-lock"]},{ref:"1.3.5",title:"Identify Input Purpose",level:"AA",rules:["autocomplete-valid"]},{ref:"1.4.1",title:"Use of Color",level:"A",rules:["link-in-text-block"]},{ref:"1.4.2",title:"Audio Control",level:"A",rules:["no-autoplay-audio"]},{ref:"1.4.3",title:"Contrast (Minimum)",level:"AA",rules:["color-contrast"]},{ref:"1.4.4",title:"Resize Text",level:"AA",rules:["meta-viewport"]},{ref:"1.4.10",title:"Reflow",level:"AA",rules:[]},{ref:"1.4.11",title:"Non-text Contrast",level:"AA",rules:[]},{ref:"1.4.12",title:"Text Spacing",level:"AA",rules:[]},{ref:"1.4.13",title:"Content on Hover or Focus",level:"AA",rules:[]},{ref:"2.1.1",title:"Keyboard",level:"A",rules:["nested-interactive","frame-focusable-content"]},{ref:"2.1.2",title:"No Keyboard Trap",level:"A",rules:[]},{ref:"2.1.4",title:"Character Key Shortcuts",level:"A",rules:[]},{ref:"2.2.1",title:"Timing Adjustable",level:"A",rules:["meta-refresh"]},{ref:"2.2.2",title:"Pause, Stop, Hide",level:"A",rules:["blink","marquee"]},{ref:"2.4.1",title:"Bypass Blocks",level:"A",rules:["bypass"]},{ref:"2.4.2",title:"Page Titled",level:"A",rules:["document-title"]},{ref:"2.4.3",title:"Focus Order",level:"A",rules:["tabindex"]},{ref:"2.4.4",title:"Link Purpose (In Context)",level:"A",rules:["link-name"]},{ref:"2.4.5",title:"Multiple Ways",level:"AA",rules:[]},{ref:"2.4.6",title:"Headings and Labels",level:"AA",rules:["empty-heading","empty-table-header"]},{ref:"2.4.7",title:"Focus Visible",level:"AA",rules:[]},{ref:"2.5.1",title:"Pointer Gestures",level:"A",rules:[]},{ref:"2.5.2",title:"Pointer Cancellation",level:"A",rules:[]},{ref:"2.5.3",title:"Label in Name",level:"A",rules:["label-content-name-mismatch"]},{ref:"2.5.4",title:"Motion Actuation",level:"A",rules:[]},{ref:"2.5.8",title:"Target Size (Minimum)",level:"AA",rules:["target-size"]},{ref:"3.1.1",title:"Language of Page",level:"A",rules:["html-has-lang","html-lang-valid"]},{ref:"3.1.2",title:"Language of Parts",level:"AA",rules:["valid-lang"]},{ref:"3.2.1",title:"On Focus",level:"A",rules:[]},{ref:"3.2.2",title:"On Input",level:"A",rules:[]},{ref:"3.2.3",title:"Consistent Navigation",level:"AA",rules:[]},{ref:"3.2.4",title:"Consistent Identification",level:"AA",rules:[]},{ref:"3.3.1",title:"Error Identification",level:"A",rules:[]},{ref:"3.3.2",title:"Labels or Instructions",level:"A",rules:["label","form-field-multiple-labels","select-name"]},{ref:"3.3.3",title:"Error Suggestion",level:"AA",rules:[]},{ref:"3.3.4",title:"Error Prevention (Legal, Financial, Data)",level:"AA",rules:[]},{ref:"4.1.1",title:"Parsing",level:"A",rules:["duplicate-id","duplicate-id-aria","duplicate-id-active"]},{ref:"4.1.2",title:"Name, Role, Value",level:"A",rules:["button-name","aria-required-attr","aria-allowed-attr","aria-roles","aria-valid-attr","aria-valid-attr-value","aria-input-field-name","aria-toggle-field-name","select-name","input-button-name"]},{ref:"4.1.3",title:"Status Messages",level:"AA",rules:[]}];function rn(e,t,n){if(e.rules.length===0)return{conformance:"Not Applicable",hits:[]};const o=e.rules.filter(i=>t.has(i));return o.length>0?{conformance:"Partially Supports",hits:o}:{conformance:e.rules.some(i=>n.has(i))?"Supports":"Not Evaluated",hits:o}}function $o(e,t){var l,p;const n=((l=e[0])==null?void 0:l.componentId)??"unknown",o=ae(e),s=e.flatMap(h=>h.violations),i=new Set(s.map(h=>h.ruleId)),c=new Set(i);for(const h of e){const f=h.axeRulesEvaluated;if(f){for(const a of f.passed)c.add(a.ruleId);for(const a of f.inapplicable)c.add(a.ruleId);for(const a of f.incomplete)c.add(a.ruleId)}}const r=h=>{const{conformance:f,hits:a}=rn(h,i,c);return`
362
362
  <tr class="${f==="Supports"?"supports":f==="Partially Supports"?"partial":f==="Not Evaluated"?"not-evaluated":"na"}">
363
363
  <td class="ref">${y(h.ref)}</td>
364
364
  <td class="title">${y(h.title)} <span class="level">(${h.level})</span></td>
365
365
  <td class="conf">${f}</td>
366
366
  <td class="notes">${a.length>0?`Auto-detected violations of: ${a.map(u=>`<code>${y(u)}</code>`).join(", ")}. Manual review recommended.`:f==="Not Applicable"?"No automated rule maps to this criterion. Manual evaluation required.":"No automated violations detected. Manual confirmation recommended for full claim."}</td>
367
- </tr>`},d=h=>{const f=on.filter(a=>a.level===h).map(r).join("");return`
367
+ </tr>`},d=h=>{const f=sn.filter(a=>a.level===h).map(r).join("");return`
368
368
  <h2>WCAG 2.1 Level ${h}</h2>
369
369
  <table>
370
370
  <thead>
@@ -415,13 +415,13 @@ ${g.target.failureSummary}`:"");r.push(` <testcase ${v}>`),r.push(` <fai
415
415
  Generated by ${X} v${re()}. Conformance assessed against all violations detected during the audit (not delta-filtered).
416
416
  </footer>
417
417
  </body>
418
- </html>`}function $o(e){const t=[],n=e.target.outerHTML;if(e.ruleId==="image-alt"||e.ruleId==="input-image-alt"||e.ruleId==="area-alt"||e.ruleId==="svg-img-alt"||e.ruleId==="object-alt"){const o=/\bsrc\s*=\s*"([^"]+)"|\bsrc\s*=\s*'([^']+)'/i.exec(n);o&&t.push({label:"Image source",value:`\`${o[1]??o[2]}\` — if you have vision capability, view this image and propose alt text. Use \`alt=""\` if it's decorative.`});const s=/<title>([^<]+)<\/title>/i.exec(n);s&&t.push({label:"Inline SVG <title>",value:s[1]})}if(e.ruleId==="button-name"||e.ruleId==="empty-button"||e.ruleId==="input-button-name"){const o=/\baria-label\s*=\s*"([^"]+)"/i.exec(n);o&&t.push({label:"Existing aria-label",value:o[1]});const s=/<svg[^>]*>[\s\S]*?<title>([^<]+)<\/title>/i.exec(n);s&&t.push({label:"SVG <title> inside",value:s[1]});const i=/<(?:i|svg|span)[^>]*class\s*=\s*"([^"]*(?:icon|fa-|material-icon)[^"]*)"/i.exec(n);i&&t.push({label:"Icon class hint",value:`\`${i[1]}\` — name suggests intent`})}if(e.ruleId==="link-name"||e.ruleId==="empty-link"){const o=/\bhref\s*=\s*"([^"]+)"|\bhref\s*=\s*'([^']+)'/i.exec(n);o&&t.push({label:"Link href",value:`\`${o[1]??o[2]}\` — destination may suggest the right link text`});const s=/\baria-label\s*=\s*"([^"]+)"/i.exec(n);s&&t.push({label:"Existing aria-label",value:s[1]})}if(e.ruleId==="label"||e.ruleId==="select-name"||e.ruleId==="aria-input-field-name"){const o=/\bname\s*=\s*"([^"]+)"|\bname\s*=\s*'([^']+)'/i.exec(n);o&&t.push({label:"Field name attribute",value:`\`${o[1]??o[2]}\` — often hints at the intended label`});const s=/\bplaceholder\s*=\s*"([^"]+)"|\bplaceholder\s*=\s*'([^']+)'/i.exec(n);s&&t.push({label:"Placeholder text (visible)",value:s[1]??s[2]});const i=/\btype\s*=\s*"([^"]+)"|\btype\s*=\s*'([^']+)'/i.exec(n);i&&t.push({label:"Input type",value:`\`${i[1]??i[2]}\``});const c=/\bautocomplete\s*=\s*"([^"]+)"|\bautocomplete\s*=\s*'([^']+)'/i.exec(n);c&&t.push({label:"Autocomplete",value:c[1]??c[2]})}return t}function To(e,t){var i;const n=new Set((t??[]).map(c=>`${c.pageUrl}::${c.ruleId}::${c.selector}`)),o=new Set,s=[];for(const c of e){const r=c.pageUrl??c.scope,d=((i=c.axeRulesEvaluated)==null?void 0:i.incomplete)??[];for(const l of d)for(const p of l.elements??[]){const h=`${r}::${l.ruleId}::${p.selector}`;o.has(h)||n.has(h)||(o.add(h),s.push({pageUrl:r,ruleId:l.ruleId,wcagCriterion:l.wcagCriterion,selector:p.selector,failureSummary:p.failureSummary,styles:p.styles}))}}return s}function Co(e,t,n,o,s,i){var x,w,C,A,T,$,J,ye,Q,ne;const c=new Set(s??[]),r=((x=e[0])==null?void 0:x.componentId)??"unknown",d=ae(e),l=!!(t&&t.baselineSnapshotMeta),p=e.flatMap(m=>m.violations),h=new Map;for(const m of p){const L=m.ruleId.startsWith("ai-")?`${m.ruleId}::${m.target.selector}::${(m.target.outerHTML??"").slice(0,200)}`:we(m.ruleId,m.target.selector),I=`${m.currentState.pseudoState} · ${m.currentState.theme} · ${m.currentState.direction} · ${m.currentState.breakpoint.id}`,F=h.get(L);if(F){F._states.includes(I)||F._states.push(I),F._instanceSelectors.includes(m.target.selector)||F._instanceSelectors.push(m.target.selector);continue}h.set(L,{...m,_states:[I],_instanceSelectors:[m.target.selector]})}const f=Array.from(h.values()).sort((m,L)=>{const I={critical:0,serious:1,moderate:2,minor:3};return(I[m.impact]??99)-(I[L.impact]??99)}),a=[];a.push("# Accessibility Fix Request"),a.push(""),a.push(`You are a senior accessibility engineer pairing with the user on their codebase. The component below has ${f.length} unique WCAG violation${f.length===1?"":"s"} detected by an automated audit running axe-core ${((w=e[0])==null?void 0:w.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.`),a.push("");const g=n&&n.length>0?{runs:n,workflows:ve}:void 0,u=ie(p,g,e);if(a.push("## Context"),a.push(""),a.push(`- **Component / scope:** \`${r}\``),a.push(`- **Audited URL:** ${d}`),a.push("- **WCAG target:** WCAG 2.1 AA + WCAG 2.2 AA + best-practice rules"),a.push("- **Source filter:** Full audit — every detected violation"),a.push(`- **Compliance posture:** Grade **${u.letter}** · **${u.risk.toUpperCase()} risk** (${u.totals.critical} critical · ${u.totals.serious} serious · ${u.totals.moderate} moderate · ${u.totals.minor} minor). ${u.risk==="critical"||u.risk==="high"?"Treat as triage priority — these violations represent meaningful lawsuit exposure under ADA Title III, EAA, and EN 301 549.":u.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."}`),u.coverage){const m=u.coverage.untestedCriteria.length;m>0?a.push(`- **WCAG ${u.coverage.targetVersion} ${u.coverage.targetLevel} coverage:** ${u.coverage.evaluated} of ${u.coverage.totalApplicable} criteria evaluated. **Cannot claim ${u.coverage.targetLevel} conformance** until ${m} untested criteria are evaluated via Guided Tests (Pass / Fail / N/A). Untested: ${u.coverage.untestedCriteria.join(", ")}.`):a.push(`- **WCAG ${u.coverage.targetVersion} ${u.coverage.targetLevel} coverage:** ${u.coverage.evaluated} of ${u.coverage.totalApplicable} criteria evaluated. Full coverage — defensible conformance claim possible once all violations below are fixed.`)}l&&t&&a.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.`),a.push(""),a.push("## Before you start (safety)"),a.push(""),a.push("Do these in order before making any code changes:"),a.push(""),a.push("1. **Verify a clean git working tree** (`git status`). If there are uncommitted changes, ask the user whether to commit, stash, or proceed."),a.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`."),a.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>`."),a.push("4. **Show diffs before applying** when your tooling supports it (Cursor / Copilot Chat agents). Never silently overwrite files."),a.push("5. **Do not bump dependencies** to resolve accessibility issues — fixes should be code changes, not package upgrades."),a.push(""),a.push("## Ask me these questions first"),a.push(""),a.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."),a.push(""),a.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."),a.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?"),a.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.'),a.push("4. **Files or directories that are off-limits?** Generated code, vendored libraries, third-party widgets, lockfiles — confirm what you must NOT touch."),a.push("5. **For color-contrast fixes: prefer adjusting foreground or background?** Brand color preservation is usually a stakeholder call, not a developer call."),a.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?"),a.push(""),a.push("## Decision rules (when ambiguity surfaces during fixes)"),a.push(""),a.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.'),a.push("- **State-specific failures get state-specific fixes.** A `:hover`-only contrast failure goes in the `:hover` rule, not the default selector."),a.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."),a.push("- **When the fix would alter visible design** (color shifts, layout changes, content additions), pause and confirm with the user before applying."),a.push(""),a.push("## ⛔ Do not dismiss findings without empirical verification"),a.push(""),a.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.'),a.push(""),a.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.'),a.push(""),a.push("**To dismiss any finding as a false positive, you must do all of the following:**"),a.push(""),a.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)."),a.push("2. **Inspect the flagged elements directly** — DevTools, real `getBoundingClientRect()` coordinates, real DOM source order."),a.push("3. **Compare your empirical observation to the auditor's claim.** Does the visual layout actually contradict the DOM order, or not?"),a.push("4. **If you conclude false positive, document why** — include the empirical evidence (coordinates, screenshots, DOM excerpt) so the user can audit your reasoning."),a.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").`),a.push(""),a.push("**What does NOT count as verification:**"),a.push(""),a.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)"),a.push('- Pattern-matching the finding shape ("this looks like a perfect arithmetic flip therefore it must be an RTL artifact")'),a.push(`- Reading the auditor's false-positive list and concluding your case "looks like one of those"`),a.push(`- Deciding it's "probably fine" because the structure looks reasonable on a quick read`),a.push(""),a.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."),a.push(""),a.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."),a.push(""),a.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."),a.push(""),a.push("## Content-decision rules (alt text, button labels, link text)"),a.push(""),a.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:"),a.push(""),a.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=""`.'),a.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.'),a.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."),a.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."),a.push("5. **Always show your proposed copy before applying** — let the user approve, edit, or reject."),a.push(""),a.push("## Constraints"),a.push(""),a.push("- Don't fix violations that aren't in this list (no scope creep)."),a.push(`- Don't reformat or reorder unrelated code. No "while I'm here" cleanup.`),a.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.`),a.push("- Don't add new dependencies, configs, or build steps."),a.push("- Add inline comments only where the fix's rationale is non-obvious; otherwise skip comments."),a.push("- Preserve existing functionality and visual design wherever possible."),a.push(""),a.push("## How to use this prompt"),a.push(""),a.push("Paste this into Claude / Cursor / Copilot Chat with your codebase open. The AI should:"),a.push('1. **First**: walk through the "Before you start" + "Ask me these questions" sections with the user.'),a.push("2. **Then**: identify the source files containing each violating element (use the selectors + outerHTML below)."),a.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."),a.push("4. **Finally**: produce one atomic commit per fix, with a clear message."),a.push(""),a.push("---"),a.push("");const v=(((C=e[0])==null?void 0:C.readingOrderIssues)??[]).filter(m=>!c.has(`reading-order::${m.selector}`)),O=(A=e[0])==null?void 0:A.positionAnalysisCapturedAt,S=O?`${O.breakpoint.width}×${O.breakpoint.height} (${O.breakpoint.label}), ${O.direction.toUpperCase()}, ${O.theme} theme, ${O.pseudoState} pseudo-state`:"(reference state unavailable — findings reflect last matrix state)";if(v.length>0){a.push("## Reading-order concerns (DOM ≠ visual order)"),a.push(""),a.push(`_Positions captured at: **${S}**. 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._`),a.push(""),a.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. ${v.length} element${v.length===1?" has":"s have"} a notable gap between their DOM position and their visual position:`),a.push(""),a.push("| DOM index | Visual index | Selector | Text |"),a.push("|---|---|---|---|");for(const m of v){const L=m.textSnippet.replace(/\|/g,"\\|").replace(/\n/g," ");a.push(`| ${m.domIndex} | ${m.visualIndex} | \`${m.selector}\` | ${L} |`)}a.push(""),a.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."),a.push(""),a.push("**False-positive cases — these are the ONLY legitimate reasons to skip a flagged element:**"),a.push(""),a.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.'),a.push('2. **Skip-links** ("Skip to main content") — visually positioned late but DOM-early on purpose.'),a.push("3. **Caption-first patterns** — captions placed before their image in DOM so SR users get context before the alt text."),a.push("4. **Modal / popover triggers** — the trigger button is DOM-near the modal's portal mount point, which is often `<body>` end."),a.push(""),a.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.'),a.push(""),a.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."),a.push(""),a.push("---"),a.push("")}const E=(((T=e[0])==null?void 0:T.tabOrderIssues)??[]).filter(m=>!c.has(`tab-order::${m.selector}`));if(E.length>0){const m=E.filter(I=>I.flag==="visual"||I.flag==="both").length,L=E.filter(I=>I.flag==="tabindex"||I.flag==="both").length;a.push("## Tab-order concerns (keyboard focus sequence ≠ visual order) — WCAG 2.4.3 Focus Order (A)"),a.push(""),a.push(`_Positions captured at: **${S}**. 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._`),a.push(""),a.push(`Keyboard users tab through the page in one order; sighted users scan in another. axe-core does not detect this. ${E.length} focusable element${E.length===1?" has":"s have"} a notable divergence. Of these: ${m} ${m===1?"has":"have"} a visual-vs-tab mismatch (focus jumps around the page); ${L} ${L===1?"is":"are"} caused by positive \`tabindex\` reordering DOM source.`),a.push(""),a.push("| Tab pos | Visual pos | DOM pos | Flag | Selector | Text |"),a.push("|---|---|---|---|---|---|");for(const I of E){const F=I.textSnippet.replace(/\|/g,"\\|").replace(/\n/g," ");a.push(`| ${I.tabPosition} | ${I.visualPosition} | ${I.domPosition} | ${I.flag} | \`${I.selector}\` | ${F} |`)}a.push(""),a.push("**How to fix:**"),a.push(""),a.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."),a.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.'),a.push("- **`flag: both`** — both conditions present. Fix the DOM order first, then remove the positive `tabindex`."),a.push(""),a.push("**False-positive cases — these are the ONLY legitimate reasons to skip a flagged element:**"),a.push(""),a.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.'),a.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."),a.push("3. **Dropdown menus / popovers** — a button DOM-far from its portal-rendered menu may produce false positives when the menu is open."),a.push(""),a.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.`),a.push(""),a.push("Surface candidates to the user; ask which are intended; only fix the rest."),a.push(""),a.push("---"),a.push("")}const k=e.flatMap(m=>m.announcements??[]),b=e.flatMap(m=>m.focusEvents??[]).filter(m=>m.isFocusReset);if(k.length>0||b.length>0){if(a.push("## Runtime behavioral observations"),a.push(""),k.length>0){const m=k.filter(I=>I.politeness==="assertive").length;a.push(`**Live-region announcements** (${k.length} total, ${m} 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:`),a.push("");const L=k.slice(0,8);for(const I of L){const F=I.text.replace(/\|/g,"\\|").replace(/\n/g," ").slice(0,120);a.push(`- \`[${I.politeness}${I.role?`/${I.role}`:""}]\` ${F}`)}k.length>8&&a.push(`- (and ${k.length-8} more)`),a.push(""),a.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)."),a.push("")}if(b.length>0){a.push(`**Focus resets to body** (${b.length} occurrence${b.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.`),a.push("");for(const m of b.slice(0,5))a.push(`- \`${m.fromSelector??"(none)"}\` → \`${m.toSelector}\``);b.length>5&&a.push(`- (and ${b.length-5} more)`),a.push(""),a.push("Common cause: an element is removed from the DOM (e.g., modal closes) without `.focus()` being called on a sensible destination."),a.push("")}a.push("---"),a.push("")}const _=(($=e[0])==null?void 0:$.undefinedCustomProperties)??[];if(_.length>0){a.push("## Undefined CSS custom properties (cascade root cause)"),a.push(""),a.push(`${_.length} CSS custom propert${_.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.`),a.push(""),a.push("| Custom property | References | First sample sites |"),a.push("|---|---|---|");for(const m of _){const L=m.sampleSites.map(I=>`\`${I.selector} { ${I.property} }\``).join("; ");a.push(`| \`${m.name}\` | ${m.referenceCount} | ${L} |`)}a.push(""),a.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."),a.push(""),a.push("---"),a.push("")}if(n&&n.length>0){a.push("## Manual checks already completed (Guided Tests)"),a.push(""),a.push(`The consultant has run ${n.length} Intelligent Guided Test workflow${n.length===1?"":"s"} against this component covering the WCAG criteria automation cannot verify (keyboard navigation, screen-reader experience, focus management, forms, error prevention, etc.). Treat the verdicts below as ground truth — these came from a human running the actual flow.`),a.push(""),a.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."),a.push("");const m=ve;a.push("| Workflow | Status | Notes |"),a.push("|---|---|---|");for(const G of n){const U=m.find(je=>je.id===G.workflowId);if(!U)continue;let V=0,z=0,We=0,Ve=0,ce=0;for(const je of U.steps){const be=G.steps[je.id];if(!be){ce++;continue}be.status==="pass"?V++:be.status==="fail"?z++:be.status==="skip"?We++:be.status==="not-applicable"?Ve++:ce++}const wn=z>0?`❌ ${z} failed`:ce>0?`⚠ ${ce} unanswered`:`✓ all ${U.steps.length} answered`,ke=[`${V}p`,`${z}f`];Ve>0&&ke.push(`${Ve} N/A`),We>0&&ke.push(`${We} skip`),ce>0&&ke.push(`${ce} unanswered`);const vn=`${ke.join(" · ")} of ${U.steps.length} steps`;a.push(`| ${U.name} | ${wn} | ${vn} |`)}a.push("");const L=[];for(const G of n){const U=m.find(V=>V.id===G.workflowId);if(U)for(const V of U.steps){const z=G.steps[V.id];(z==null?void 0:z.status)==="fail"&&L.push({workflow:U.name,stepTitle:V.question,notes:z.notes})}}if(L.length>0){a.push("**Failed manual checks** (these are real WCAG failures the consultant verified by hand):"),a.push("");for(const G of L)a.push(`- **${G.workflow} → ${G.stepTitle}**${G.notes?`: ${G.notes}`:""}`);a.push(""),a.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."),a.push("")}const I=new Set(n.map(G=>G.workflowId)),F=m.filter(G=>!I.has(G.id));F.length>0&&(a.push(`**Not yet manually verified** (${F.length} workflow${F.length===1?"":"s"}): `+F.map(G=>G.name).join(", ")+". These cover WCAG criteria automation cannot detect; ask the consultant whether to run them before declaring the audit complete."),a.push("")),a.push("---"),a.push("")}if(o&&o.pagesAudited>1){const m=(()=>{try{return new URL(d).origin}catch{return null}})(),L=(()=>{try{return new URL(o.startUrl).origin}catch{return null}})();if(m&&L&&m===L){if(a.push("## Site-wide context (from a recent site crawl)"),a.push(""),a.push(`A recent crawl of ${o.pagesAudited} page${o.pagesAudited===1?"":"s"} on this origin produced grade **${o.siteGrade}** site-wide with **${o.totalUniqueViolations}** unique violations. Use this context for WCAG 3.2.3 (Consistent Navigation) and 3.2.4 (Consistent Identification) which require cross-page comparison.`),a.push(""),o.topViolations.length>0){a.push("**Most-frequent violations across the site** (rules to prioritize because they affect multiple pages):"),a.push("");for(const I of o.topViolations.slice(0,6))a.push(`- \`${I.ruleId}\` — ${I.impact} — found on ${I.urlsAffected} page${I.urlsAffected===1?"":"s"} (${I.totalOccurrences} total instances)`);a.push(""),a.push("Fixing one of these rules in shared components / layouts is leverage — one fix resolves N pages."),a.push("")}a.push("**Manual cross-page checks the AI should walk through:**"),a.push(""),a.push("- **3.2.3 Consistent Navigation** — verify primary nav appears in the same DOM order on every page audited above. Inconsistent ordering between pages is a Level AA failure."),a.push('- **3.2.4 Consistent Identification** — verify components with identical functionality (logo "home" link, search button, menu toggle) have identical accessible names across pages.'),a.push("- **3.2.6 Consistent Help (WCAG 2.2 A)** — verify any help mechanism (chat widget, contact link, FAQ link) appears in the same relative position on every page that has one."),a.push(""),a.push("---"),a.push("")}}a.push("## Violations to fix"),a.push(""),f.forEach((m,L)=>{const I=Rn[m.ruleId],F=m._instanceSelectors.length;if(a.push(`### ${L+1}. \`${m.ruleId}\` — ${m.impact} — WCAG ${m.wcagCriterion} ${m.wcagLevel}${F>1?` (${F} instances)`:""}`),a.push(""),a.push(`**Description:** ${m.description}`),a.push(""),F>1){a.push(`**Affects ${F} spots — fix the rule once, applies to all:**`);for(const V of m._instanceSelectors.slice(0,8))a.push(`- \`${V}\``);m._instanceSelectors.length>8&&a.push(`- (and ${m._instanceSelectors.length-8} more)`)}else a.push(`**Selector:** \`${m.target.selector}\``);a.push(""),a.push(`**Found in state(s):** ${m._states.join(" / ")}`),a.push("");const G=$o(m);if(G.length>0){for(const V of G)a.push(`**${V.label}:** ${V.value}`);a.push("")}m.target.failureSummary&&(a.push(`**Diagnostic:** ${m.target.failureSummary.replace(/\n+/g," ")}`),a.push("")),m.ai&&(a.push(`**🤖 AI-verified finding** (model: ${m.ai.model}, confidence: ${(m.ai.confidence*100).toFixed(0)}%)`),a.push(`> ${m.ai.reasoning}`),a.push(""));const U=m.target.cascadeChain;if(U&&(U.color||U.backgroundColor)){if(a.push("**CSS cascade chain (authored → rendered):**"),U.color){const V=U.color.vars.length>0?` (vars: ${U.color.vars.map(z=>`\`${z}\``).join(", ")})`:"";a.push(`- \`color\`: \`${U.color.authored}\` → \`${U.color.rendered}\`${V}${U.color.ruleSelector?` — set in rule \`${U.color.ruleSelector}\``:""}`)}if(U.backgroundColor){const V=U.backgroundColor.vars.length>0?` (vars: ${U.backgroundColor.vars.map(z=>`\`${z}\``).join(", ")})`:"";a.push(`- \`background-color\`: \`${U.backgroundColor.authored}\` → \`${U.backgroundColor.rendered}\`${V}${U.backgroundColor.ruleSelector?` — set in rule \`${U.backgroundColor.ruleSelector}\``:""}`)}a.push("")}a.push("**Element HTML:**"),a.push("```html"),a.push(m.target.outerHTML),a.push("```"),a.push(""),I?(a.push(`**Recipe:** ${I.summary}`),I.snippet&&(a.push(""),a.push("```"+(I.snippetLang??"")),a.push(I.snippet),a.push("```"))):a.push(`**Recipe:** No curated recipe for this rule — apply the standard WCAG ${m.wcagCriterion} guidance.`),m.helpUrl&&(a.push(""),a.push(`**More info:** ${m.helpUrl}`)),a.push(""),a.push("---"),a.push("")});const M=To(e,i);if(M.length>0){a.push("## Items axe couldn't determine — needs human review"),a.push(""),a.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."),a.push("");for(const m of M)a.push(`### \`${m.ruleId}\` on \`${m.selector}\``),a.push(""),a.push(`- **WCAG criterion:** ${m.wcagCriterion}`),a.push(`- **Page URL:** ${m.pageUrl}`),m.failureSummary&&a.push(`- **axe diagnostic:** ${m.failureSummary}`),(J=m.styles)!=null&&J.foreground&&a.push(`- **Foreground:** \`${m.styles.foreground}\``),(ye=m.styles)!=null&&ye.background&&a.push(`- **Background:** \`${m.styles.background}\``),(Q=m.styles)!=null&&Q.fontSize&&a.push(`- **Font size:** ${m.styles.fontSize}px, weight ${m.styles.fontWeight??"unknown"}`),(ne=m.styles)!=null&&ne.textSample&&a.push(`- **Text sample:** ${JSON.stringify(m.styles.textSample)}`),a.push(""),a.push("---"),a.push("")}return a.push("## Manual verification checklist — non-automatable WCAG 2.1 / 2.2 AA criteria"),a.push(""),a.push("Automated audits catch ~30–50% of WCAG criteria. The remaining ~50% require human judgment, multi-page comparison, or runtime interaction we cannot script. **After applying the fixes above, walk the user through each item below before declaring the audit complete.** Mark each one passed, failed (and add to the fix queue), or N/A (with reasoning)."),a.push(""),a.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.'),a.push(""),a.push("### Perceivable"),a.push(""),a.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."),a.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.'),a.push("- [ ] **1.2.3 Audio Description / Media Alternative Prerecorded (A)** — prerecorded video has either audio description or a full text alternative."),a.push("- [ ] **1.2.4 Captions Live (AA)** — live audio content (livestream, real-time meeting) has captions. Usually N/A for static sites."),a.push("- [ ] **1.2.5 Audio Description Prerecorded (AA)** — prerecorded video has audio description for visual-only information."),a.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."),a.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."),a.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."),a.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)."),a.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."),a.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)."),a.push(""),a.push("### Operable"),a.push(""),a.push("- [ ] **2.1.2 No Keyboard Trap (A)** — Tab through the entire page from start to finish; verify focus never gets stuck in a region you can't leave with Tab/Shift+Tab/Esc. Common offenders: video players, custom widgets, modals without proper Esc handling."),a.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.'),a.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.'),a.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."),a.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."),E.length>0?a.push("- [x] **2.4.3 Focus Order (A)** — ALREADY FLAGGED above (`Tab-order concerns` section). Confirm fixes are in place."):a.push("- [ ] **2.4.3 Focus Order (A)** — auto-detector flagged 0 issues, but spot-check by tabbing through the page: focus visits elements in a logical order (matches visual / semantic flow)."),a.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."),a.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."),a.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."),a.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."),a.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."),a.push(""),a.push("### Understandable"),a.push(""),a.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."),a.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."),o&&o.pagesAudited>1?(a.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."),a.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).")):(a.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."),a.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.')),a.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."),a.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."),a.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."),a.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)."),a.push(""),a.push("### Robust"),a.push(""),k.length>0||b.length>0?a.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."):a.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.'),a.push(""),a.push("### Manual checks STILL TO DO"),a.push(""),a.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).`),a.push(""),a.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.'),a.push(""),a.push("---"),a.push(""),a.push("## When you're done — verification"),a.push(""),a.push("Before reporting completion, do all of the following:"),a.push(""),a.push(`1. **Confirm all ${f.length} violation${f.length===1?"":"s"} above are addressed.** Walk back through the list with the user, ticking each one off.`),a.push("2. **Re-run the auditor** (or whatever method we agreed in question 6 above). Verify zero NEW violations were introduced by the fixes."),a.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."`),a.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."),a.push("5. **Commit log review** — surface the list of commits you produced. Each should be atomic and revertable."),a.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."),a.push(""),a.push("## Success criteria"),a.push(""),a.push("- All "+f.length+" violation"+(f.length===1?"":"s")+" above are resolved or explicitly deferred with reason."),a.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.'),a.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.)'),a.push("- No new violations introduced."),a.push("- State-specific behavior preserved (`:hover`, `:focus`, dark-mode, RTL, breakpoints)."),a.push("- One atomic commit per fix; clean revertable history."),a.push("- No unrelated code reformatted, no dependencies bumped."),a.push("- Diff is reviewable in under 10 minutes per fix."),a.push(""),a.push(`_Generated by ${X} v${re()}._`),a.join(`
419
- `)}const Io=5;function Ro(e){const t=new Map;let n=0;for(const o of e){if(!o.screenshotDataUrl||o.violations.length===0)continue;const s=`${o.state.pseudoState} · ${o.state.theme} · ${o.state.direction} · ${o.state.breakpoint.id}`,i=s;t.has(i)||t.set(i,{stateLabel:s,screenshotDataUrl:o.screenshotDataUrl,violations:[]});const c=t.get(i);for(const r of o.violations)n++,c.violations.push({...r,_idx:n})}return Array.from(t.values()).sort((o,s)=>s.violations.length-o.violations.length).slice(0,Io)}const $t=1280,Tt=800,Oo={low:"Low risk",moderate:"Moderate risk",high:"High risk",critical:"Critical risk"},_o={low:"#d1fae5",moderate:"#fef3c7",high:"#fed7aa",critical:"#fecaca"},Mo={low:"#065f46",moderate:"#92400e",high:"#9a3412",critical:"#991b1b"},Uo={A:"#10b981",B:"#84cc16",C:"#eab308",D:"#f97316",F:"#dc2626"},No={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 Lo(e,t,n,o,s){return cn(e,t,"evidence",n,o,s)}function Po(e){return rn(e,"letter")}function Do(e){return rn(e,"report")}function Fo(e){var l;const t=e.flatMap(p=>p.violations),n=ie(t,void 0,e),o=ae(e),s=new Map;for(const p of t){const h=we(p.ruleId,p.target.selector);s.has(h)||s.set(h,p)}const i=Array.from(s.values()).sort((p,h)=>{const f={critical:0,serious:1,moderate:2,minor:3};return(f[p.impact]??99)-(f[h.impact]??99)}),c=new Map;for(const p of i){const h=c.get(p.ruleId)??[];h.push(p),c.set(p.ruleId,h)}const r=[];if(r.push("# Help me fix accessibility issues on my website"),r.push(""),r.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."),r.push(""),r.push("## What I need from you"),r.push(""),r.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."),r.push("2. **Then, for each issue below, tell me clearly:**"),r.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"),r.push(" - Or whether **my developer needs to do this** (code-level fix) — and what to ask them to do"),r.push("3. **Prioritize ruthlessly.** I want to start with the issues that:"),r.push(" - Are most likely to come up in an ADA-related lawsuit (critical-impact items)"),r.push(" - Are easiest for me to fix without involving a developer"),r.push("4. **Don't bury me in jargon.** If you have to use a technical term, explain it in plain English first."),r.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."),r.push(""),r.push("## Audit summary"),r.push(""),r.push(`- **Page audited:** ${o}`),r.push(`- **Lawsuit-target risk:** ${Ft[n.risk]} — ${Gt[n.risk]}`),r.push(`- **Issues found:** ${n.totals.unique} unique problems (${n.totals.critical} critical · ${n.totals.serious} serious · ${n.totals.moderate} moderate · ${n.totals.minor} minor)`),n.coverage&&n.coverage.untestedCriteria.length>0?r.push(`- **WCAG conformance progress:** ${n.coverage.evaluated} of ${n.coverage.totalApplicable} criteria evaluated. Fixing the issues below will clear me of automated lawsuit-targeting tools. To make a formal claim that the site meets WCAG ${n.coverage.targetVersion} ${n.coverage.targetLevel}, ${n.coverage.untestedCriteria.length} more criteria need human review — see the "Guided Tests" workflows in the auditor side panel for these.`):n.coverage&&n.coverage.untestedCriteria.length===0&&r.push(`- **WCAG conformance:** All ${n.coverage.totalApplicable} applicable WCAG ${n.coverage.targetVersion} ${n.coverage.targetLevel} criteria have been evaluated. Fixing the issues below will produce a defensible conformance claim.`),r.push(""),r.push("## The issues, in plain language"),r.push(""),c.size===0)return r.push("No automated issues were detected. Note: automated checks only catch about 30–50% of accessibility problems. Manual testing covers the rest."),r.join(`
420
- `);let d=1;for(const[p,h]of c.entries()){const f=(l=h.find(u=>u.target.opacityContext))==null?void 0:l.target.opacityContext,a=Dt(p,f),g=h[0].impact;r.push(`### ${d}. [${g.toUpperCase()}] ${a.whatsWrong}`),r.push(""),r.push(`**Why this matters.** ${a.whyItMatters}`),r.push(""),r.push(`**Plain-language fix guidance.** ${a.howToFix}`),r.push(""),r.push(`**Where on the page (${h.length} ${h.length===1?"spot":"spots"}):**`);for(const u of h.slice(0,8))r.push(`- \`${u.target.selector}\``);h.length>8&&r.push(`- (and ${h.length-8} more spots)`),r.push(""),r.push(`*Technical reference for your developer:* axe-core rule \`${p}\` — full docs at https://dequeuniversity.com/rules/axe/4.11/${p}`),r.push(""),d++}return r.push("---"),r.push(""),r.push("## How to use this output"),r.push(""),r.push("Walk through this list with me one issue at a time. For each:"),r.push("1. Help me understand the issue with a real example of who it affects."),r.push("2. Tell me clearly if I can fix it myself, and if so, walk me through it for my platform."),r.push("3. If I need a developer, give me a short paragraph I can paste into an email or message to them."),r.push("4. Help me prioritize — I have limited time and want to fix the riskiest things first."),r.push(""),r.push("Start by asking me which platform I use."),r.join(`
421
- `)}function rn(e,t){const n=e.flatMap(f=>f.violations),o=ie(n,void 0,e),s=ae(e),i=new Date,c=new Map;for(const f of n){const a=we(f.ruleId,f.target.selector);c.has(a)||c.set(a,f)}const r=Array.from(c.values()).sort((f,a)=>{const g={critical:0,serious:1,moderate:2,minor:3};return(g[f.impact]??99)-(g[a.impact]??99)}),d=new Map;for(const f of r){const a=d.get(f.ruleId)??[];a.push(f),d.set(f.ruleId,a)}const l=Array.from(d.entries()).map(([f,a])=>{var E;const g=(E=a.find(k=>k.target.opacityContext))==null?void 0:E.target.opacityContext,u=Dt(f,g),v=a[0].impact,O=a.slice(0,8).map(k=>`<code>${y(k.target.selector)}</code>`).join(", "),S=a.length>8?` <em>(and ${a.length-8} more)</em>`:"";return`
418
+ </html>`}function To(e){const t=[],n=e.target.outerHTML;if(e.ruleId==="image-alt"||e.ruleId==="input-image-alt"||e.ruleId==="area-alt"||e.ruleId==="svg-img-alt"||e.ruleId==="object-alt"){const o=/\bsrc\s*=\s*"([^"]+)"|\bsrc\s*=\s*'([^']+)'/i.exec(n);o&&t.push({label:"Image source",value:`\`${o[1]??o[2]}\` — if you have vision capability, view this image and propose alt text. Use \`alt=""\` if it's decorative.`});const s=/<title>([^<]+)<\/title>/i.exec(n);s&&t.push({label:"Inline SVG <title>",value:s[1]})}if(e.ruleId==="button-name"||e.ruleId==="empty-button"||e.ruleId==="input-button-name"){const o=/\baria-label\s*=\s*"([^"]+)"/i.exec(n);o&&t.push({label:"Existing aria-label",value:o[1]});const s=/<svg[^>]*>[\s\S]*?<title>([^<]+)<\/title>/i.exec(n);s&&t.push({label:"SVG <title> inside",value:s[1]});const i=/<(?:i|svg|span)[^>]*class\s*=\s*"([^"]*(?:icon|fa-|material-icon)[^"]*)"/i.exec(n);i&&t.push({label:"Icon class hint",value:`\`${i[1]}\` — name suggests intent`})}if(e.ruleId==="link-name"||e.ruleId==="empty-link"){const o=/\bhref\s*=\s*"([^"]+)"|\bhref\s*=\s*'([^']+)'/i.exec(n);o&&t.push({label:"Link href",value:`\`${o[1]??o[2]}\` — destination may suggest the right link text`});const s=/\baria-label\s*=\s*"([^"]+)"/i.exec(n);s&&t.push({label:"Existing aria-label",value:s[1]})}if(e.ruleId==="label"||e.ruleId==="select-name"||e.ruleId==="aria-input-field-name"){const o=/\bname\s*=\s*"([^"]+)"|\bname\s*=\s*'([^']+)'/i.exec(n);o&&t.push({label:"Field name attribute",value:`\`${o[1]??o[2]}\` — often hints at the intended label`});const s=/\bplaceholder\s*=\s*"([^"]+)"|\bplaceholder\s*=\s*'([^']+)'/i.exec(n);s&&t.push({label:"Placeholder text (visible)",value:s[1]??s[2]});const i=/\btype\s*=\s*"([^"]+)"|\btype\s*=\s*'([^']+)'/i.exec(n);i&&t.push({label:"Input type",value:`\`${i[1]??i[2]}\``});const c=/\bautocomplete\s*=\s*"([^"]+)"|\bautocomplete\s*=\s*'([^']+)'/i.exec(n);c&&t.push({label:"Autocomplete",value:c[1]??c[2]})}return t}function Co(e,t){var i;const n=new Set((t??[]).map(c=>`${c.pageUrl}::${c.ruleId}::${c.selector}`)),o=new Set,s=[];for(const c of e){const r=c.pageUrl??c.scope,d=((i=c.axeRulesEvaluated)==null?void 0:i.incomplete)??[];for(const l of d)for(const p of l.elements??[]){const h=`${r}::${l.ruleId}::${p.selector}`;o.has(h)||n.has(h)||(o.add(h),s.push({pageUrl:r,ruleId:l.ruleId,wcagCriterion:l.wcagCriterion,selector:p.selector,failureSummary:p.failureSummary,styles:p.styles}))}}return s}function Io(e,t,n,o,s,i){var x,v,C,A,T,$,J,ye,Q,ne;const c=new Set(s??[]),r=((x=e[0])==null?void 0:x.componentId)??"unknown",d=ae(e),l=!!(t&&t.baselineSnapshotMeta),p=e.flatMap(m=>m.violations),h=new Map;for(const m of p){const L=m.ruleId.startsWith("ai-")?`${m.ruleId}::${m.target.selector}::${(m.target.outerHTML??"").slice(0,200)}`:we(m.ruleId,m.target.selector),I=`${m.currentState.pseudoState} · ${m.currentState.theme} · ${m.currentState.direction} · ${m.currentState.breakpoint.id}`,F=h.get(L);if(F){F._states.includes(I)||F._states.push(I),F._instanceSelectors.includes(m.target.selector)||F._instanceSelectors.push(m.target.selector);continue}h.set(L,{...m,_states:[I],_instanceSelectors:[m.target.selector]})}const f=Array.from(h.values()).sort((m,L)=>{const I={critical:0,serious:1,moderate:2,minor:3};return(I[m.impact]??99)-(I[L.impact]??99)}),a=[];a.push("# Accessibility Fix Request"),a.push(""),a.push(`You are a senior accessibility engineer pairing with the user on their codebase. The component below has ${f.length} unique WCAG violation${f.length===1?"":"s"} detected by an automated audit running axe-core ${((v=e[0])==null?void 0:v.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.`),a.push("");const g=n&&n.length>0?{runs:n,workflows:ve}:void 0,u=ie(p,g,e);if(a.push("## Context"),a.push(""),a.push(`- **Component / scope:** \`${r}\``),a.push(`- **Audited URL:** ${d}`),a.push("- **WCAG target:** WCAG 2.1 AA + WCAG 2.2 AA + best-practice rules"),a.push("- **Source filter:** Full audit — every detected violation"),a.push(`- **Compliance posture:** Grade **${u.letter}** · **${u.risk.toUpperCase()} risk** (${u.totals.critical} critical · ${u.totals.serious} serious · ${u.totals.moderate} moderate · ${u.totals.minor} minor). ${u.risk==="critical"||u.risk==="high"?"Treat as triage priority — these violations represent meaningful lawsuit exposure under ADA Title III, EAA, and EN 301 549.":u.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."}`),u.coverage){const m=u.coverage.untestedCriteria.length;m>0?a.push(`- **WCAG ${u.coverage.targetVersion} ${u.coverage.targetLevel} coverage:** ${u.coverage.evaluated} of ${u.coverage.totalApplicable} criteria evaluated. **Cannot claim ${u.coverage.targetLevel} conformance** until ${m} untested criteria are evaluated via Guided Tests (Pass / Fail / N/A). Untested: ${u.coverage.untestedCriteria.join(", ")}.`):a.push(`- **WCAG ${u.coverage.targetVersion} ${u.coverage.targetLevel} coverage:** ${u.coverage.evaluated} of ${u.coverage.totalApplicable} criteria evaluated. Full coverage — defensible conformance claim possible once all violations below are fixed.`)}l&&t&&a.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.`),a.push(""),a.push("## Before you start (safety)"),a.push(""),a.push("Do these in order before making any code changes:"),a.push(""),a.push("1. **Verify a clean git working tree** (`git status`). If there are uncommitted changes, ask the user whether to commit, stash, or proceed."),a.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`."),a.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>`."),a.push("4. **Show diffs before applying** when your tooling supports it (Cursor / Copilot Chat agents). Never silently overwrite files."),a.push("5. **Do not bump dependencies** to resolve accessibility issues — fixes should be code changes, not package upgrades."),a.push(""),a.push("## Ask me these questions first"),a.push(""),a.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."),a.push(""),a.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."),a.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?"),a.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.'),a.push("4. **Files or directories that are off-limits?** Generated code, vendored libraries, third-party widgets, lockfiles — confirm what you must NOT touch."),a.push("5. **For color-contrast fixes: prefer adjusting foreground or background?** Brand color preservation is usually a stakeholder call, not a developer call."),a.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?"),a.push(""),a.push("## Decision rules (when ambiguity surfaces during fixes)"),a.push(""),a.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.'),a.push("- **State-specific failures get state-specific fixes.** A `:hover`-only contrast failure goes in the `:hover` rule, not the default selector."),a.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."),a.push("- **When the fix would alter visible design** (color shifts, layout changes, content additions), pause and confirm with the user before applying."),a.push(""),a.push("## ⛔ Do not dismiss findings without empirical verification"),a.push(""),a.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.'),a.push(""),a.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.'),a.push(""),a.push("**To dismiss any finding as a false positive, you must do all of the following:**"),a.push(""),a.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)."),a.push("2. **Inspect the flagged elements directly** — DevTools, real `getBoundingClientRect()` coordinates, real DOM source order."),a.push("3. **Compare your empirical observation to the auditor's claim.** Does the visual layout actually contradict the DOM order, or not?"),a.push("4. **If you conclude false positive, document why** — include the empirical evidence (coordinates, screenshots, DOM excerpt) so the user can audit your reasoning."),a.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").`),a.push(""),a.push("**What does NOT count as verification:**"),a.push(""),a.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)"),a.push('- Pattern-matching the finding shape ("this looks like a perfect arithmetic flip therefore it must be an RTL artifact")'),a.push(`- Reading the auditor's false-positive list and concluding your case "looks like one of those"`),a.push(`- Deciding it's "probably fine" because the structure looks reasonable on a quick read`),a.push(""),a.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."),a.push(""),a.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."),a.push(""),a.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."),a.push(""),a.push("## Content-decision rules (alt text, button labels, link text)"),a.push(""),a.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:"),a.push(""),a.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=""`.'),a.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.'),a.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."),a.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."),a.push("5. **Always show your proposed copy before applying** — let the user approve, edit, or reject."),a.push(""),a.push("## Constraints"),a.push(""),a.push("- Don't fix violations that aren't in this list (no scope creep)."),a.push(`- Don't reformat or reorder unrelated code. No "while I'm here" cleanup.`),a.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.`),a.push("- Don't add new dependencies, configs, or build steps."),a.push("- Add inline comments only where the fix's rationale is non-obvious; otherwise skip comments."),a.push("- Preserve existing functionality and visual design wherever possible."),a.push(""),a.push("## How to use this prompt"),a.push(""),a.push("Paste this into Claude / Cursor / Copilot Chat with your codebase open. The AI should:"),a.push('1. **First**: walk through the "Before you start" + "Ask me these questions" sections with the user.'),a.push("2. **Then**: identify the source files containing each violating element (use the selectors + outerHTML below)."),a.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."),a.push("4. **Finally**: produce one atomic commit per fix, with a clear message."),a.push(""),a.push("---"),a.push("");const w=(((C=e[0])==null?void 0:C.readingOrderIssues)??[]).filter(m=>!c.has(`reading-order::${m.selector}`)),O=(A=e[0])==null?void 0:A.positionAnalysisCapturedAt,S=O?`${O.breakpoint.width}×${O.breakpoint.height} (${O.breakpoint.label}), ${O.direction.toUpperCase()}, ${O.theme} theme, ${O.pseudoState} pseudo-state`:"(reference state unavailable — findings reflect last matrix state)";if(w.length>0){a.push("## Reading-order concerns (DOM ≠ visual order)"),a.push(""),a.push(`_Positions captured at: **${S}**. 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._`),a.push(""),a.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. ${w.length} element${w.length===1?" has":"s have"} a notable gap between their DOM position and their visual position:`),a.push(""),a.push("| DOM index | Visual index | Selector | Text |"),a.push("|---|---|---|---|");for(const m of w){const L=m.textSnippet.replace(/\|/g,"\\|").replace(/\n/g," ");a.push(`| ${m.domIndex} | ${m.visualIndex} | \`${m.selector}\` | ${L} |`)}a.push(""),a.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."),a.push(""),a.push("**False-positive cases — these are the ONLY legitimate reasons to skip a flagged element:**"),a.push(""),a.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.'),a.push('2. **Skip-links** ("Skip to main content") — visually positioned late but DOM-early on purpose.'),a.push("3. **Caption-first patterns** — captions placed before their image in DOM so SR users get context before the alt text."),a.push("4. **Modal / popover triggers** — the trigger button is DOM-near the modal's portal mount point, which is often `<body>` end."),a.push(""),a.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.'),a.push(""),a.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."),a.push(""),a.push("---"),a.push("")}const E=(((T=e[0])==null?void 0:T.tabOrderIssues)??[]).filter(m=>!c.has(`tab-order::${m.selector}`));if(E.length>0){const m=E.filter(I=>I.flag==="visual"||I.flag==="both").length,L=E.filter(I=>I.flag==="tabindex"||I.flag==="both").length;a.push("## Tab-order concerns (keyboard focus sequence ≠ visual order) — WCAG 2.4.3 Focus Order (A)"),a.push(""),a.push(`_Positions captured at: **${S}**. 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._`),a.push(""),a.push(`Keyboard users tab through the page in one order; sighted users scan in another. axe-core does not detect this. ${E.length} focusable element${E.length===1?" has":"s have"} a notable divergence. Of these: ${m} ${m===1?"has":"have"} a visual-vs-tab mismatch (focus jumps around the page); ${L} ${L===1?"is":"are"} caused by positive \`tabindex\` reordering DOM source.`),a.push(""),a.push("| Tab pos | Visual pos | DOM pos | Flag | Selector | Text |"),a.push("|---|---|---|---|---|---|");for(const I of E){const F=I.textSnippet.replace(/\|/g,"\\|").replace(/\n/g," ");a.push(`| ${I.tabPosition} | ${I.visualPosition} | ${I.domPosition} | ${I.flag} | \`${I.selector}\` | ${F} |`)}a.push(""),a.push("**How to fix:**"),a.push(""),a.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."),a.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.'),a.push("- **`flag: both`** — both conditions present. Fix the DOM order first, then remove the positive `tabindex`."),a.push(""),a.push("**False-positive cases — these are the ONLY legitimate reasons to skip a flagged element:**"),a.push(""),a.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.'),a.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."),a.push("3. **Dropdown menus / popovers** — a button DOM-far from its portal-rendered menu may produce false positives when the menu is open."),a.push(""),a.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.`),a.push(""),a.push("Surface candidates to the user; ask which are intended; only fix the rest."),a.push(""),a.push("---"),a.push("")}const k=e.flatMap(m=>m.announcements??[]),b=e.flatMap(m=>m.focusEvents??[]).filter(m=>m.isFocusReset);if(k.length>0||b.length>0){if(a.push("## Runtime behavioral observations"),a.push(""),k.length>0){const m=k.filter(I=>I.politeness==="assertive").length;a.push(`**Live-region announcements** (${k.length} total, ${m} 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:`),a.push("");const L=k.slice(0,8);for(const I of L){const F=I.text.replace(/\|/g,"\\|").replace(/\n/g," ").slice(0,120);a.push(`- \`[${I.politeness}${I.role?`/${I.role}`:""}]\` ${F}`)}k.length>8&&a.push(`- (and ${k.length-8} more)`),a.push(""),a.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)."),a.push("")}if(b.length>0){a.push(`**Focus resets to body** (${b.length} occurrence${b.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.`),a.push("");for(const m of b.slice(0,5))a.push(`- \`${m.fromSelector??"(none)"}\` → \`${m.toSelector}\``);b.length>5&&a.push(`- (and ${b.length-5} more)`),a.push(""),a.push("Common cause: an element is removed from the DOM (e.g., modal closes) without `.focus()` being called on a sensible destination."),a.push("")}a.push("---"),a.push("")}const _=(($=e[0])==null?void 0:$.undefinedCustomProperties)??[];if(_.length>0){a.push("## Undefined CSS custom properties (cascade root cause)"),a.push(""),a.push(`${_.length} CSS custom propert${_.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.`),a.push(""),a.push("| Custom property | References | First sample sites |"),a.push("|---|---|---|");for(const m of _){const L=m.sampleSites.map(I=>`\`${I.selector} { ${I.property} }\``).join("; ");a.push(`| \`${m.name}\` | ${m.referenceCount} | ${L} |`)}a.push(""),a.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."),a.push(""),a.push("---"),a.push("")}if(n&&n.length>0){a.push("## Manual checks already completed (Guided Tests)"),a.push(""),a.push(`The consultant has run ${n.length} Intelligent Guided Test workflow${n.length===1?"":"s"} against this component covering the WCAG criteria automation cannot verify (keyboard navigation, screen-reader experience, focus management, forms, error prevention, etc.). Treat the verdicts below as ground truth — these came from a human running the actual flow.`),a.push(""),a.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."),a.push("");const m=ve;a.push("| Workflow | Status | Notes |"),a.push("|---|---|---|");for(const G of n){const U=m.find(ze=>ze.id===G.workflowId);if(!U)continue;let V=0,z=0,Ve=0,je=0,ce=0;for(const ze of U.steps){const be=G.steps[ze.id];if(!be){ce++;continue}be.status==="pass"?V++:be.status==="fail"?z++:be.status==="skip"?Ve++:be.status==="not-applicable"?je++:ce++}const vn=z>0?`❌ ${z} failed`:ce>0?`⚠ ${ce} unanswered`:`✓ all ${U.steps.length} answered`,Ee=[`${V}p`,`${z}f`];je>0&&Ee.push(`${je} N/A`),Ve>0&&Ee.push(`${Ve} skip`),ce>0&&Ee.push(`${ce} unanswered`);const An=`${Ee.join(" · ")} of ${U.steps.length} steps`;a.push(`| ${U.name} | ${vn} | ${An} |`)}a.push("");const L=[];for(const G of n){const U=m.find(V=>V.id===G.workflowId);if(U)for(const V of U.steps){const z=G.steps[V.id];(z==null?void 0:z.status)==="fail"&&L.push({workflow:U.name,stepTitle:V.question,notes:z.notes})}}if(L.length>0){a.push("**Failed manual checks** (these are real WCAG failures the consultant verified by hand):"),a.push("");for(const G of L)a.push(`- **${G.workflow} → ${G.stepTitle}**${G.notes?`: ${G.notes}`:""}`);a.push(""),a.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."),a.push("")}const I=new Set(n.map(G=>G.workflowId)),F=m.filter(G=>!I.has(G.id));F.length>0&&(a.push(`**Not yet manually verified** (${F.length} workflow${F.length===1?"":"s"}): `+F.map(G=>G.name).join(", ")+". These cover WCAG criteria automation cannot detect; ask the consultant whether to run them before declaring the audit complete."),a.push("")),a.push("---"),a.push("")}if(o&&o.pagesAudited>1){const m=(()=>{try{return new URL(d).origin}catch{return null}})(),L=(()=>{try{return new URL(o.startUrl).origin}catch{return null}})();if(m&&L&&m===L){if(a.push("## Site-wide context (from a recent site crawl)"),a.push(""),a.push(`A recent crawl of ${o.pagesAudited} page${o.pagesAudited===1?"":"s"} on this origin produced grade **${o.siteGrade}** site-wide with **${o.totalUniqueViolations}** unique violations. Use this context for WCAG 3.2.3 (Consistent Navigation) and 3.2.4 (Consistent Identification) which require cross-page comparison.`),a.push(""),o.topViolations.length>0){a.push("**Most-frequent violations across the site** (rules to prioritize because they affect multiple pages):"),a.push("");for(const I of o.topViolations.slice(0,6))a.push(`- \`${I.ruleId}\` — ${I.impact} — found on ${I.urlsAffected} page${I.urlsAffected===1?"":"s"} (${I.totalOccurrences} total instances)`);a.push(""),a.push("Fixing one of these rules in shared components / layouts is leverage — one fix resolves N pages."),a.push("")}a.push("**Manual cross-page checks the AI should walk through:**"),a.push(""),a.push("- **3.2.3 Consistent Navigation** — verify primary nav appears in the same DOM order on every page audited above. Inconsistent ordering between pages is a Level AA failure."),a.push('- **3.2.4 Consistent Identification** — verify components with identical functionality (logo "home" link, search button, menu toggle) have identical accessible names across pages.'),a.push("- **3.2.6 Consistent Help (WCAG 2.2 A)** — verify any help mechanism (chat widget, contact link, FAQ link) appears in the same relative position on every page that has one."),a.push(""),a.push("---"),a.push("")}}a.push("## Violations to fix"),a.push(""),f.forEach((m,L)=>{const I=On[m.ruleId],F=m._instanceSelectors.length;if(a.push(`### ${L+1}. \`${m.ruleId}\` — ${m.impact} — WCAG ${m.wcagCriterion} ${m.wcagLevel}${F>1?` (${F} instances)`:""}`),a.push(""),a.push(`**Description:** ${m.description}`),a.push(""),F>1){a.push(`**Affects ${F} spots — fix the rule once, applies to all:**`);for(const V of m._instanceSelectors.slice(0,8))a.push(`- \`${V}\``);m._instanceSelectors.length>8&&a.push(`- (and ${m._instanceSelectors.length-8} more)`)}else a.push(`**Selector:** \`${m.target.selector}\``);a.push(""),a.push(`**Found in state(s):** ${m._states.join(" / ")}`),a.push("");const G=To(m);if(G.length>0){for(const V of G)a.push(`**${V.label}:** ${V.value}`);a.push("")}m.target.failureSummary&&(a.push(`**Diagnostic:** ${m.target.failureSummary.replace(/\n+/g," ")}`),a.push("")),m.ai&&(a.push(`**🤖 AI-verified finding** (model: ${m.ai.model}, confidence: ${(m.ai.confidence*100).toFixed(0)}%)`),a.push(`> ${m.ai.reasoning}`),a.push(""));const U=m.target.cascadeChain;if(U&&(U.color||U.backgroundColor)){if(a.push("**CSS cascade chain (authored → rendered):**"),U.color){const V=U.color.vars.length>0?` (vars: ${U.color.vars.map(z=>`\`${z}\``).join(", ")})`:"";a.push(`- \`color\`: \`${U.color.authored}\` → \`${U.color.rendered}\`${V}${U.color.ruleSelector?` — set in rule \`${U.color.ruleSelector}\``:""}`)}if(U.backgroundColor){const V=U.backgroundColor.vars.length>0?` (vars: ${U.backgroundColor.vars.map(z=>`\`${z}\``).join(", ")})`:"";a.push(`- \`background-color\`: \`${U.backgroundColor.authored}\` → \`${U.backgroundColor.rendered}\`${V}${U.backgroundColor.ruleSelector?` — set in rule \`${U.backgroundColor.ruleSelector}\``:""}`)}a.push("")}a.push("**Element HTML:**"),a.push("```html"),a.push(m.target.outerHTML),a.push("```"),a.push(""),I?(a.push(`**Recipe:** ${I.summary}`),I.snippet&&(a.push(""),a.push("```"+(I.snippetLang??"")),a.push(I.snippet),a.push("```"))):a.push(`**Recipe:** No curated recipe for this rule — apply the standard WCAG ${m.wcagCriterion} guidance.`),m.helpUrl&&(a.push(""),a.push(`**More info:** ${m.helpUrl}`)),a.push(""),a.push("---"),a.push("")});const M=Co(e,i);if(M.length>0){a.push("## Items axe couldn't determine — needs human review"),a.push(""),a.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."),a.push("");for(const m of M)a.push(`### \`${m.ruleId}\` on \`${m.selector}\``),a.push(""),a.push(`- **WCAG criterion:** ${m.wcagCriterion}`),a.push(`- **Page URL:** ${m.pageUrl}`),m.failureSummary&&a.push(`- **axe diagnostic:** ${m.failureSummary}`),(J=m.styles)!=null&&J.foreground&&a.push(`- **Foreground:** \`${m.styles.foreground}\``),(ye=m.styles)!=null&&ye.background&&a.push(`- **Background:** \`${m.styles.background}\``),(Q=m.styles)!=null&&Q.fontSize&&a.push(`- **Font size:** ${m.styles.fontSize}px, weight ${m.styles.fontWeight??"unknown"}`),(ne=m.styles)!=null&&ne.textSample&&a.push(`- **Text sample:** ${JSON.stringify(m.styles.textSample)}`),a.push(""),a.push("---"),a.push("")}return a.push("## Manual verification checklist — non-automatable WCAG 2.1 / 2.2 AA criteria"),a.push(""),a.push("Automated audits catch ~30–50% of WCAG criteria. The remaining ~50% require human judgment, multi-page comparison, or runtime interaction we cannot script. **After applying the fixes above, walk the user through each item below before declaring the audit complete.** Mark each one passed, failed (and add to the fix queue), or N/A (with reasoning)."),a.push(""),a.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.'),a.push(""),a.push("### Perceivable"),a.push(""),a.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."),a.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.'),a.push("- [ ] **1.2.3 Audio Description / Media Alternative Prerecorded (A)** — prerecorded video has either audio description or a full text alternative."),a.push("- [ ] **1.2.4 Captions Live (AA)** — live audio content (livestream, real-time meeting) has captions. Usually N/A for static sites."),a.push("- [ ] **1.2.5 Audio Description Prerecorded (AA)** — prerecorded video has audio description for visual-only information."),a.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."),a.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."),a.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."),a.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)."),a.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."),a.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)."),a.push(""),a.push("### Operable"),a.push(""),a.push("- [ ] **2.1.2 No Keyboard Trap (A)** — Tab through the entire page from start to finish; verify focus never gets stuck in a region you can't leave with Tab/Shift+Tab/Esc. Common offenders: video players, custom widgets, modals without proper Esc handling."),a.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.'),a.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.'),a.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."),a.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."),E.length>0?a.push("- [x] **2.4.3 Focus Order (A)** — ALREADY FLAGGED above (`Tab-order concerns` section). Confirm fixes are in place."):a.push("- [ ] **2.4.3 Focus Order (A)** — auto-detector flagged 0 issues, but spot-check by tabbing through the page: focus visits elements in a logical order (matches visual / semantic flow)."),a.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."),a.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."),a.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."),a.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."),a.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."),a.push(""),a.push("### Understandable"),a.push(""),a.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."),a.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."),o&&o.pagesAudited>1?(a.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."),a.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).")):(a.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."),a.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.')),a.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."),a.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."),a.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."),a.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)."),a.push(""),a.push("### Robust"),a.push(""),k.length>0||b.length>0?a.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."):a.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.'),a.push(""),a.push("### Manual checks STILL TO DO"),a.push(""),a.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).`),a.push(""),a.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.'),a.push(""),a.push("---"),a.push(""),a.push("## When you're done — verification"),a.push(""),a.push("Before reporting completion, do all of the following:"),a.push(""),a.push(`1. **Confirm all ${f.length} violation${f.length===1?"":"s"} above are addressed.** Walk back through the list with the user, ticking each one off.`),a.push("2. **Re-run the auditor** (or whatever method we agreed in question 6 above). Verify zero NEW violations were introduced by the fixes."),a.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."`),a.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."),a.push("5. **Commit log review** — surface the list of commits you produced. Each should be atomic and revertable."),a.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."),a.push(""),a.push("## Success criteria"),a.push(""),a.push("- All "+f.length+" violation"+(f.length===1?"":"s")+" above are resolved or explicitly deferred with reason."),a.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.'),a.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.)'),a.push("- No new violations introduced."),a.push("- State-specific behavior preserved (`:hover`, `:focus`, dark-mode, RTL, breakpoints)."),a.push("- One atomic commit per fix; clean revertable history."),a.push("- No unrelated code reformatted, no dependencies bumped."),a.push("- Diff is reviewable in under 10 minutes per fix."),a.push(""),a.push(`_Generated by ${X} v${re()}._`),a.join(`
419
+ `)}const Ro=5;function Oo(e){const t=new Map;let n=0;for(const o of e){if(!o.screenshotDataUrl||o.violations.length===0)continue;const s=`${o.state.pseudoState} · ${o.state.theme} · ${o.state.direction} · ${o.state.breakpoint.id}`,i=s;t.has(i)||t.set(i,{stateLabel:s,screenshotDataUrl:o.screenshotDataUrl,violations:[]});const c=t.get(i);for(const r of o.violations)n++,c.violations.push({...r,_idx:n})}return Array.from(t.values()).sort((o,s)=>s.violations.length-o.violations.length).slice(0,Ro)}const Tt=1280,Ct=800,_o={low:"Low risk",moderate:"Moderate risk",high:"High risk",critical:"Critical risk"},Mo={low:"#d1fae5",moderate:"#fef3c7",high:"#fed7aa",critical:"#fecaca"},Uo={low:"#065f46",moderate:"#92400e",high:"#9a3412",critical:"#991b1b"},No={A:"#10b981",B:"#84cc16",C:"#eab308",D:"#f97316",F:"#dc2626"},Lo={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 Po(e,t,n,o,s){return ln(e,t,"evidence",n,o,s)}function Do(e){return cn(e,"letter")}function Fo(e){return cn(e,"report")}function Go(e){var l;const t=e.flatMap(p=>p.violations),n=ie(t,void 0,e),o=ae(e),s=new Map;for(const p of t){const h=we(p.ruleId,p.target.selector);s.has(h)||s.set(h,p)}const i=Array.from(s.values()).sort((p,h)=>{const f={critical:0,serious:1,moderate:2,minor:3};return(f[p.impact]??99)-(f[h.impact]??99)}),c=new Map;for(const p of i){const h=c.get(p.ruleId)??[];h.push(p),c.set(p.ruleId,h)}const r=[];if(r.push("# Help me fix accessibility issues on my website"),r.push(""),r.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."),r.push(""),r.push("## What I need from you"),r.push(""),r.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."),r.push("2. **Then, for each issue below, tell me clearly:**"),r.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"),r.push(" - Or whether **my developer needs to do this** (code-level fix) — and what to ask them to do"),r.push("3. **Prioritize ruthlessly.** I want to start with the issues that:"),r.push(" - Are most likely to come up in an ADA-related lawsuit (critical-impact items)"),r.push(" - Are easiest for me to fix without involving a developer"),r.push("4. **Don't bury me in jargon.** If you have to use a technical term, explain it in plain English first."),r.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."),r.push(""),r.push("## Audit summary"),r.push(""),r.push(`- **Page audited:** ${o}`),r.push(`- **Lawsuit-target risk:** ${Gt[n.risk]} — ${Wt[n.risk]}`),r.push(`- **Issues found:** ${n.totals.unique} unique problems (${n.totals.critical} critical · ${n.totals.serious} serious · ${n.totals.moderate} moderate · ${n.totals.minor} minor)`),n.coverage&&n.coverage.untestedCriteria.length>0?r.push(`- **WCAG conformance progress:** ${n.coverage.evaluated} of ${n.coverage.totalApplicable} criteria evaluated. Fixing the issues below will clear me of automated lawsuit-targeting tools. To make a formal claim that the site meets WCAG ${n.coverage.targetVersion} ${n.coverage.targetLevel}, ${n.coverage.untestedCriteria.length} more criteria need human review — see the "Guided Tests" workflows in the auditor side panel for these.`):n.coverage&&n.coverage.untestedCriteria.length===0&&r.push(`- **WCAG conformance:** All ${n.coverage.totalApplicable} applicable WCAG ${n.coverage.targetVersion} ${n.coverage.targetLevel} criteria have been evaluated. Fixing the issues below will produce a defensible conformance claim.`),r.push(""),r.push("## The issues, in plain language"),r.push(""),c.size===0)return r.push("No automated issues were detected. Note: automated checks only catch about 30–50% of accessibility problems. Manual testing covers the rest."),r.join(`
420
+ `);let d=1;for(const[p,h]of c.entries()){const f=(l=h.find(u=>u.target.opacityContext))==null?void 0:l.target.opacityContext,a=Ft(p,f),g=h[0].impact;r.push(`### ${d}. [${g.toUpperCase()}] ${a.whatsWrong}`),r.push(""),r.push(`**Why this matters.** ${a.whyItMatters}`),r.push(""),r.push(`**Plain-language fix guidance.** ${a.howToFix}`),r.push(""),r.push(`**Where on the page (${h.length} ${h.length===1?"spot":"spots"}):**`);for(const u of h.slice(0,8))r.push(`- \`${u.target.selector}\``);h.length>8&&r.push(`- (and ${h.length-8} more spots)`),r.push(""),r.push(`*Technical reference for your developer:* axe-core rule \`${p}\` — full docs at https://dequeuniversity.com/rules/axe/4.11/${p}`),r.push(""),d++}return r.push("---"),r.push(""),r.push("## How to use this output"),r.push(""),r.push("Walk through this list with me one issue at a time. For each:"),r.push("1. Help me understand the issue with a real example of who it affects."),r.push("2. Tell me clearly if I can fix it myself, and if so, walk me through it for my platform."),r.push("3. If I need a developer, give me a short paragraph I can paste into an email or message to them."),r.push("4. Help me prioritize — I have limited time and want to fix the riskiest things first."),r.push(""),r.push("Start by asking me which platform I use."),r.join(`
421
+ `)}function cn(e,t){const n=e.flatMap(f=>f.violations),o=ie(n,void 0,e),s=ae(e),i=new Date,c=new Map;for(const f of n){const a=we(f.ruleId,f.target.selector);c.has(a)||c.set(a,f)}const r=Array.from(c.values()).sort((f,a)=>{const g={critical:0,serious:1,moderate:2,minor:3};return(g[f.impact]??99)-(g[a.impact]??99)}),d=new Map;for(const f of r){const a=d.get(f.ruleId)??[];a.push(f),d.set(f.ruleId,a)}const l=Array.from(d.entries()).map(([f,a])=>{var E;const g=(E=a.find(k=>k.target.opacityContext))==null?void 0:E.target.opacityContext,u=Ft(f,g),w=a[0].impact,O=a.slice(0,8).map(k=>`<code>${y(k.target.selector)}</code>`).join(", "),S=a.length>8?` <em>(and ${a.length-8} more)</em>`:"";return`
422
422
  <section class="rule">
423
423
  <div class="rule-header">
424
- <span class="impact impact-${y(v)}">${y(v)}</span>
424
+ <span class="impact impact-${y(w)}">${y(w)}</span>
425
425
  <h3>${y(u.whatsWrong)}</h3>
426
426
  </div>
427
427
  <p class="why"><strong>Why this matters.</strong> ${y(u.whyItMatters)}</p>
@@ -472,7 +472,7 @@ ${g.target.failureSummary}`:"");r.push(` <testcase ${v}>`),r.push(` <fai
472
472
  <p>${p}</p>
473
473
 
474
474
  <div class="summary">
475
- <p style="margin: 0;"><strong>${y(Ft[o.risk])}.</strong> ${y(Gt[o.risk])}</p>
475
+ <p style="margin: 0;"><strong>${y(Gt[o.risk])}.</strong> ${y(Wt[o.risk])}</p>
476
476
  <div class="totals">
477
477
  ${o.totals.critical>0?`<span><strong>${o.totals.critical}</strong> critical</span>`:""}
478
478
  ${o.totals.serious>0?`<span><strong>${o.totals.serious}</strong> serious</span>`:""}
@@ -509,7 +509,7 @@ ${t==="letter"?`
509
509
  Automated audits typically catch around 30–50% of accessibility issues; some categories require manual review.</p>
510
510
  </footer>
511
511
  </body>
512
- </html>`}function Go(e){if(!e||e.length===0)return`
512
+ </html>`}function Wo(e){if(!e||e.length===0)return`
513
513
  <h2 id="manual">5. Manual assessment results</h2>
514
514
  <p style="font-size: 10pt; color: #475569;">
515
515
  No manual workflows were completed for this audit. Automated audits cover ~30–50% of WCAG;
@@ -519,7 +519,7 @@ ${t==="letter"?`
519
519
  behaviors, error prevention, consistency).
520
520
  When manual workflows are completed, their results are integrated into both the executive
521
521
  summary's compliance grade and this section's per-criterion findings.
522
- </p>`;const t=new Map(e.map(r=>[r.workflowId,r])),n=ve.map(r=>Wo(r,t.get(r.id))).filter(r=>r!==null).join("");let o=0,s=0,i=0,c=0;for(const r of ve){c+=r.steps.length;const d=t.get(r.id);if(d)for(const l of r.steps){const p=d.steps[l.id];p&&(i++,p.status==="fail"&&(l.severity==="required"?o++:s++))}}return`
522
+ </p>`;const t=new Map(e.map(r=>[r.workflowId,r])),n=ve.map(r=>Vo(r,t.get(r.id))).filter(r=>r!==null).join("");let o=0,s=0,i=0,c=0;for(const r of ve){c+=r.steps.length;const d=t.get(r.id);if(d)for(const l of r.steps){const p=d.steps[l.id];p&&(i++,p.status==="fail"&&(l.severity==="required"?o++:s++))}}return`
523
523
  <h2 id="manual">5. Manual assessment results</h2>
524
524
  <p style="font-size: 10pt; color: #475569; margin-bottom: 8pt;">
525
525
  Manual workflows performed by the auditor. ${i} of ${c} steps answered
@@ -527,7 +527,7 @@ ${t==="letter"?`
527
527
  ${o>0?`<strong style="color: #b91c1c;">${o} required step${o===1?"":"s"} failed</strong> — each is a confirmed WCAG breach contributing to the compliance grade above.`:"No required steps failed."}
528
528
  ${s>0?` ${s} advisory step${s===1?"":"s"} failed (best-practice findings).`:""}
529
529
  </p>
530
- ${n}`}function Wo(e,t){if(!t)return null;const n=[];for(const l of e.steps){const p=t.steps[l.id];p&&n.push({step:l,status:p.status,notes:p.notes})}if(n.length===0)return null;const o=n.filter(l=>l.status==="pass").length,s=n.filter(l=>l.status==="fail").length,i=n.filter(l=>l.status==="skip").length,c=n.filter(l=>l.status==="not-applicable").length,d=n.filter(l=>l.status==="fail").map(l=>`
530
+ ${n}`}function Vo(e,t){if(!t)return null;const n=[];for(const l of e.steps){const p=t.steps[l.id];p&&n.push({step:l,status:p.status,notes:p.notes})}if(n.length===0)return null;const o=n.filter(l=>l.status==="pass").length,s=n.filter(l=>l.status==="fail").length,i=n.filter(l=>l.status==="skip").length,c=n.filter(l=>l.status==="not-applicable").length,d=n.filter(l=>l.status==="fail").map(l=>`
531
531
  <tr>
532
532
  <td><span class="impact impact-${l.step.severity==="required"?"serious":"moderate"}">${y(l.step.severity)}</span></td>
533
533
  <td>${l.step.wcag?`<code>${y(l.step.wcag)}</code>`:"—"}</td>
@@ -548,21 +548,21 @@ ${s>0?`
548
548
  <thead><tr><th style="width: 80pt;">Severity</th><th style="width: 70pt;">WCAG</th><th>Failed check + auditor notes</th></tr></thead>
549
549
  <tbody>${d}</tbody>
550
550
  </table>
551
- `:""}`}function cn(e,t,n="defense",o,s,i){var b,_,M;const c=No[n],r=((b=e[0])==null?void 0:b.componentId)??"unknown",d=ae(e),l=e.reduce((x,w)=>x+w.durationMs,0),p=e.flatMap(x=>x.violations),h=o&&o.length>0?{runs:o,workflows:ve}:void 0,f=ie(p,h,e),a=new Date,g=new Map;for(const x of p){const w=we(x.ruleId,x.target.selector),C=`${x.currentState.pseudoState} · ${x.currentState.theme} · ${x.currentState.direction}`,A=g.get(w);if(A){A._states.includes(C)||A._states.push(C);continue}g.set(w,{...x,_states:[C]})}const u=Array.from(g.values()).sort((x,w)=>{const C={critical:0,serious:1,moderate:2,minor:3};return(C[x.impact]??99)-(C[w.impact]??99)}),v=Ro(e),O=new Map;for(const x of v)for(const w of x.violations){const C=we(w.ruleId,w.target.selector);O.has(C)||O.set(C,w._idx)}const S=u.map((x,w)=>`
551
+ `:""}`}function ln(e,t,n="defense",o,s,i){var b,_,M;const c=Lo[n],r=((b=e[0])==null?void 0:b.componentId)??"unknown",d=ae(e),l=e.reduce((x,v)=>x+v.durationMs,0),p=e.flatMap(x=>x.violations),h=o&&o.length>0?{runs:o,workflows:ve}:void 0,f=ie(p,h,e),a=new Date,g=new Map;for(const x of p){const v=we(x.ruleId,x.target.selector),C=`${x.currentState.pseudoState} · ${x.currentState.theme} · ${x.currentState.direction}`,A=g.get(v);if(A){A._states.includes(C)||A._states.push(C);continue}g.set(v,{...x,_states:[C]})}const u=Array.from(g.values()).sort((x,v)=>{const C={critical:0,serious:1,moderate:2,minor:3};return(C[x.impact]??99)-(C[v.impact]??99)}),w=Oo(e),O=new Map;for(const x of w)for(const v of x.violations){const C=we(v.ruleId,v.target.selector);O.has(C)||O.set(C,v._idx)}const S=u.map((x,v)=>`
552
552
  <tr>
553
- <td>${w+1}</td>
553
+ <td>${v+1}</td>
554
554
  <td><span class="impact impact-${y(x.impact)}">${y(x.impact)}</span></td>
555
555
  <td><code>${y(x.ruleId)}</code></td>
556
556
  <td>${y(x.wcagCriterion)} ${y(x.wcagLevel)}</td>
557
557
  <td>${y(x.description)}</td>
558
558
  <td><code class="sel">${y(x.target.selector)}</code></td>
559
559
  <td>${y(x._states.join(", "))}</td>
560
- </tr>`).join(""),E=new Set(p.map(x=>x.ruleId)),k=new Set(E);for(const x of e){const w=x.axeRulesEvaluated;if(w){for(const C of w.passed)k.add(C.ruleId);for(const C of w.inapplicable)k.add(C.ruleId);for(const C of w.incomplete)k.add(C.ruleId)}}const P=on.map(x=>{const{conformance:w,hits:C}=sn(x,E,k);return`
561
- <tr class="${w==="Supports"?"supports":w==="Partially Supports"?"partial":w==="Not Evaluated"?"not-evaluated":"na"}">
560
+ </tr>`).join(""),E=new Set(p.map(x=>x.ruleId)),k=new Set(E);for(const x of e){const v=x.axeRulesEvaluated;if(v){for(const C of v.passed)k.add(C.ruleId);for(const C of v.inapplicable)k.add(C.ruleId);for(const C of v.incomplete)k.add(C.ruleId)}}const P=sn.map(x=>{const{conformance:v,hits:C}=rn(x,E,k);return`
561
+ <tr class="${v==="Supports"?"supports":v==="Partially Supports"?"partial":v==="Not Evaluated"?"not-evaluated":"na"}">
562
562
  <td><code>${y(x.ref)}</code></td>
563
563
  <td>${y(x.title)} <span class="lvl">(${x.level})</span></td>
564
- <td>${w}</td>
565
- <td>${C.length>0?`Detected violations of: ${C.map(T=>`<code>${y(T)}</code>`).join(", ")}`:w==="Not Applicable"?"Not addressable by automated audit; requires manual review.":"No automated violations detected."}</td>
564
+ <td>${v}</td>
565
+ <td>${C.length>0?`Detected violations of: ${C.map(T=>`<code>${y(T)}</code>`).join(", ")}`:v==="Not Applicable"?"Not addressable by automated audit; requires manual review.":"No automated violations detected."}</td>
566
566
  </tr>`}).join("");return`<!doctype html>
567
567
  <html lang="en">
568
568
  <head>
@@ -642,14 +642,14 @@ ${s>0?`
642
642
 
643
643
  <h2 id="exec">1. Executive summary</h2>
644
644
  <div class="exec">
645
- <div class="exec-grade" style="background: ${Uo[f.letter]};">${f.letter}</div>
645
+ <div class="exec-grade" style="background: ${No[f.letter]};">${f.letter}</div>
646
646
  <div class="exec-body">
647
- <span class="risk-tier" style="background: ${_o[f.risk]}; color: ${Mo[f.risk]};">
648
- ${y(Oo[f.risk])}
647
+ <span class="risk-tier" style="background: ${Mo[f.risk]}; color: ${Uo[f.risk]};">
648
+ ${y(_o[f.risk])}
649
649
  </span>
650
650
  <p style="margin: 0 0 6pt;"><strong>${f.totals.unique} unique violations detected</strong> across ${e.length} state combinations
651
651
  (${f.totals.critical} critical · ${f.totals.serious} serious · ${f.totals.moderate} moderate · ${f.totals.minor} minor).</p>
652
- <p style="margin: 0; font-size: 10pt; color: #475569;">${y(On[f.risk])}</p>
652
+ <p style="margin: 0; font-size: 10pt; color: #475569;">${y(_n[f.risk])}</p>
653
653
  </div>
654
654
  </div>
655
655
  ${i&&(i.lead||i.body||i.closer)?`
@@ -669,7 +669,7 @@ ${i&&(i.lead||i.body||i.closer)?`
669
669
 
670
670
  <h3>Per-category breakdown (top-6 lawsuit-magnet categories)</h3>
671
671
  <div class="categories">
672
- ${f.categories.map(x=>{const w=x.status==="pass"?"✓":x.status==="fail"?"✗":"⚠",C=x.status==="pass"?"cat-pass":x.status==="fail"?"cat-fail":"cat-unchecked",A=x.status==="fail"?` (${x.violationCount})`:x.needsManualCheck?" — requires manual review":"";return`<div class="${C}">${w} ${y(x.label)}${y(A)}</div>`}).join("")}
672
+ ${f.categories.map(x=>{const v=x.status==="pass"?"✓":x.status==="fail"?"✗":"⚠",C=x.status==="pass"?"cat-pass":x.status==="fail"?"cat-fail":"cat-unchecked",A=x.status==="fail"?` (${x.violationCount})`:x.needsManualCheck?" — requires manual review":"";return`<div class="${C}">${v} ${y(x.label)}${y(A)}</div>`}).join("")}
673
673
  </div>
674
674
 
675
675
  <h2 id="methodology">2. Methodology &amp; scope</h2>
@@ -701,18 +701,18 @@ ${u.length===0?"<p>No automated violations were detected during this audit.</p>"
701
701
  </table>`}
702
702
 
703
703
  <h2 id="evidence">4. Visual evidence (per-state screenshots)</h2>
704
- ${v.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 ${v.length} state${v.length===1?"":"s"} by violation count shown below. Red rectangles mark violation locations; numbers correspond to entries in §3.</p>`+v.map(x=>{const w=x.violations.filter(C=>C.target.boundingRect).map(C=>{const A=C.target.boundingRect,T=String(C._idx).length*8+14,$=22;return[`<rect class="violation-mark" x="${A.x}" y="${A.y}" width="${A.w}" height="${A.h}" />`,`<rect class="label-bg" x="${A.x}" y="${Math.max(0,A.y-$)}" width="${T}" height="${$}" rx="2" />`,`<text class="violation-num" x="${A.x+6}" y="${Math.max(0,A.y-$)+16}">${C._idx}</text>`].join("")}).join("");return`
704
+ ${w.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 ${w.length} state${w.length===1?"":"s"} by violation count shown below. Red rectangles mark violation locations; numbers correspond to entries in §3.</p>`+w.map(x=>{const v=x.violations.filter(C=>C.target.boundingRect).map(C=>{const A=C.target.boundingRect,T=String(C._idx).length*8+14,$=22;return[`<rect class="violation-mark" x="${A.x}" y="${A.y}" width="${A.w}" height="${A.h}" />`,`<rect class="label-bg" x="${A.x}" y="${Math.max(0,A.y-$)}" width="${T}" height="${$}" rx="2" />`,`<text class="violation-num" x="${A.x+6}" y="${Math.max(0,A.y-$)+16}">${C._idx}</text>`].join("")}).join("");return`
705
705
  <div class="evidence-state">
706
706
  <h3>State: ${y(x.stateLabel)} (${x.violations.length} violation${x.violations.length===1?"":"s"})</h3>
707
707
  <div class="evidence-frame">
708
- <svg viewBox="0 0 ${$t} ${Tt}" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet">
709
- <image href="${x.screenshotDataUrl}" x="0" y="0" width="${$t}" height="${Tt}" preserveAspectRatio="xMidYMid slice" />
710
- ${w}
708
+ <svg viewBox="0 0 ${Tt} ${Ct}" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet">
709
+ <image href="${x.screenshotDataUrl}" x="0" y="0" width="${Tt}" height="${Ct}" preserveAspectRatio="xMidYMid slice" />
710
+ ${v}
711
711
  </svg>
712
712
  </div>
713
713
  </div>`}).join("")}
714
714
 
715
- ${Go(o)}
715
+ ${Wo(o)}
716
716
 
717
717
  <h2 id="conformance">6. WCAG 2.1 conformance assessment</h2>
718
718
  <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>
@@ -721,7 +721,7 @@ ${Go(o)}
721
721
  <tbody>${P}</tbody>
722
722
  </table>
723
723
 
724
- ${(()=>{const x=s!==void 0,w=x?"8":"7",C=x?"9":"8",A=x?`<h2 id="audit-history">7. Audit history</h2>
724
+ ${(()=>{const x=s!==void 0,v=x?"8":"7",C=x?"9":"8",A=x?`<h2 id="audit-history">7. Audit history</h2>
725
725
  <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>
726
726
  ${s&&s.length>0?`<table>
727
727
  <thead>
@@ -751,7 +751,7 @@ ${s&&s.length>0?`<table>
751
751
  </tr>`).join("")}
752
752
  </tbody>
753
753
  </table>`:'<p style="color: #64748b; font-style: italic;">No prior audits recorded — this is the first audit on record for this installation.</p>'}
754
- `:"",T=n==="evidence"?`<h2 id="remediation">${w}. Recommended approach before legal action</h2>
754
+ `:"",T=n==="evidence"?`<h2 id="remediation">${v}. Recommended approach before legal action</h2>
755
755
  <div style="background: #f0f9ff; border: 1px solid #38bdf8; padding: 12pt 16pt; border-radius: 4pt; font-size: 10.5pt;">
756
756
  <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>
757
757
  <p style="margin: 0 0 8pt;"><strong>Recommended steps before considering legal action:</strong></p>
@@ -762,7 +762,7 @@ ${s&&s.length>0?`<table>
762
762
  <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>
763
763
  </ol>
764
764
  <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>
765
- </div>`:`<h2 id="remediation">${w}. Remediation plan</h2>
765
+ </div>`:`<h2 id="remediation">${v}. Remediation plan</h2>
766
766
  <p>Each violation in §3 has a documented canonical fix pattern. The remediation workflow we recommend:</p>
767
767
  <ol>
768
768
  <li>Findings are classified by severity. Address critical and serious findings first.</li>
@@ -787,4 +787,4 @@ ${T}
787
787
  </footer>
788
788
 
789
789
  </body>
790
- </html>`}async function Vo(){const t=(await chrome.storage.local.get("aiConfig")).aiConfig;return{...Fn,...t??{}}}async function Ct(e,t,n){const o=await Vo();if(!o.enabled||!o.apiKey||e.length===0)return null;const s=e.flatMap(d=>d.violations),i=ie(s,void 0,e),c=i.categories.filter(d=>d.status==="fail").sort((d,l)=>l.violationCount-d.violationCount).slice(0,3).map(d=>d.label),r=e[0];return nn({framing:t,riskTier:i.risk,grade:i.letter,totals:{critical:i.totals.critical,serious:i.totals.serious,moderate:i.totals.moderate,minor:i.totals.minor,unique:i.totals.unique},riskDrivers:c,pageUrl:(r==null?void 0:r.pageUrl)??(r==null?void 0:r.scope)??"",auditDate:(r==null?void 0:r.startedAt)??new Date().toISOString(),priorAuditCount:n},o)}function jo(){return D("EXPORT_REQUEST",async e=>{if(e.format==="sarif")return{type:"EXPORT_RESPONSE",format:"sarif",content:JSON.stringify(So(e.results,e.delta),null,2)};if(e.format==="junit")return{type:"EXPORT_RESPONSE",format:"junit",content:xo(e.results,e.delta)};if(e.format==="html-print")return{type:"EXPORT_RESPONSE",format:"html-print",content:ko(e.results,e.delta)};if(e.format==="vpat")return{type:"EXPORT_RESPONSE",format:"vpat",content:Eo(e.results,e.delta)};if(e.format==="ai-prompt")return{type:"EXPORT_RESPONSE",format:"ai-prompt",content:Co(e.results,e.delta,e.manualRuns,e.siteCrawlReport,e.dismissedKeys,e.incompleteResolutions)};if(e.format==="defense-bundle"){const t=await gt(),n=await Ct(e.results,"defense",t.length);return{type:"EXPORT_RESPONSE",format:"defense-bundle",content:cn(e.results,e.delta,"defense",e.manualRuns,t,n)}}if(e.format==="evidence-bundle"){const t=await gt(),n=await Ct(e.results,"evidence",t.length);return{type:"EXPORT_RESPONSE",format:"evidence-bundle",content:Lo(e.results,e.delta,e.manualRuns,t,n)}}return e.format==="developer-letter"?{type:"EXPORT_RESPONSE",format:"developer-letter",content:Po(e.results)}:e.format==="owner-report"?{type:"EXPORT_RESPONSE",format:"owner-report",content:Do(e.results)}:e.format==="ai-prompt-owner"?{type:"EXPORT_RESPONSE",format:"ai-prompt-owner",content:Fo(e.results)}:{type:"EXPORT_RESPONSE",format:"json",content:JSON.stringify(Ao(e.results,e.delta),null,2)}})}const zo=W("support-messenger"),Ho="https://api.wcagcheckr.com/v1/products/wcagcheckr/support",Ke="support:rate-window",Bo=5,qo=10*60*1e3,Ko=2,Yo="wcagcheckr",It="storybook:lastDetected";async function Jo(e){if(!e)return;const t=await chrome.runtime.getPlatformInfo(),o=(await chrome.storage.local.get(It))[It];return{extensionVersion:chrome.runtime.getManifest().version,platform:t,storybookDetected:o!=null&&o.detected?o.version??"detected":"none",licenseTier:await Wt(),logTail:An(Sn())}}async function Xo(e=Date.now()){const n=((await chrome.storage.local.get(Ke))[Ke]??[]).filter(o=>e-o<qo);return n.length>=Bo?!1:(n.push(e),await chrome.storage.local.set({[Ke]:n}),!0)}async function ln(e,t=0){try{const n=await fetch(Ho,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!n.ok)throw new Error(`http ${n.status}`);const o=await n.json();if(!o.success)throw new Error(o.error??"server returned success=false");return{ticketRef:o.ticketRef??""}}catch(n){if(t<Ko){const o=(t+1)*1e3;return await new Promise(s=>setTimeout(s,o)),ln(e,t+1)}throw n}}function Qo(){return D("SUPPORT_MESSAGE_REQUEST",async e=>{if(!await Xo())return{type:"SUPPORT_MESSAGE_RESPONSE",success:!1,error:Ce("NETWORK","Rate limit reached. Try again in 10 minutes.",!0)};try{const t=await Jo(e.includeContext);return{type:"SUPPORT_MESSAGE_RESPONSE",success:!0,ticketRef:(await ln({productSlug:Yo,subject:e.subject,body:e.body,replyEmail:e.replyEmail,context:t,extensionVersion:chrome.runtime.getManifest().version,timestamp:new Date().toISOString()})).ticketRef}}catch(t){zo.error("send failed",t);const n=t instanceof Error?t.message:String(t);return{type:"SUPPORT_MESSAGE_RESPONSE",success:!1,error:Ce("NETWORK",n,!0)}}})}const dn=W("ai-color-suggester");async function Zo(e,t,n=[]){if(!t.enabled||e.length===0)return{results:[],totalCostUsd:0,capExceeded:!1};const o=K(t);if(!o.ok)return{results:[],totalCostUsd:0,capExceeded:!1};const s=o.client,i=Y(t.costCapUsd),c=[];for(const r of e){if(!i.canCharge())break;try{const d=await s.suggestColorFix({foreground:r.foreground,background:r.background,fontSize:r.fontSize,fontWeight:r.fontWeight,targetLevel:r.targetLevel,paletteHints:n});i.recordCharge(d.costUsd),d.verdict==="suggested"&&d.candidates.length>0&&c.push({matchKey:r.matchKey,suggestions:d.candidates,reasoning:d.reasoning,costUsd:d.costUsd})}catch(d){dn.warn(`color-fix suggestion failed for ${r.matchKey}`,d)}}return{results:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded}}async function es(){const e=await chrome.storage.local.get("aiConfig");return Pe(e.aiConfig)}function ts(){return D("AI_COLOR_SUGGEST_REQUEST",async e=>{const t=await es();if(!t.enabled)return{type:"AI_COLOR_SUGGEST_RESPONSE",results:[],totalCostUsd:0,capExceeded:!1,unavailableReason:"AI augmentation is disabled in Settings → AI augmentation."};if(!t.apiKey)return{type:"AI_COLOR_SUGGEST_RESPONSE",results:[],totalCostUsd:0,capExceeded:!1,unavailableReason:"No AI API key configured. Add one in Settings → AI augmentation."};try{const{results:n,totalCostUsd:o,capExceeded:s}=await Zo(e.inputs,t,e.paletteHints);return{type:"AI_COLOR_SUGGEST_RESPONSE",results:n,totalCostUsd:o,capExceeded:s}}catch(n){return dn.warn("color-suggest handler failed",n),{type:"AI_COLOR_SUGGEST_RESPONSE",results:[],totalCostUsd:0,capExceeded:!1,unavailableReason:n instanceof Error?n.message:String(n)}}})}const un=W("ai-incomplete-resolver");async function ns(){const e=await chrome.storage.local.get("aiConfig");return Pe(e.aiConfig)}async function as(e){if(!e.aiConfig.enabled||e.elements.length===0)return{resolutions:[],totalCostUsd:0};const t=K(e.aiConfig);if(!t.ok)return{resolutions:[],totalCostUsd:0};const n=t.client,o=Y(e.aiConfig.costCapUsd),s=[];for(const i of e.elements){if(!o.canCharge())break;try{const c=await n.resolveAxeIncomplete({ruleId:e.ruleId,element:i,pageUrl:e.pageUrl,targetLevel:e.targetLevel});o.recordCharge(c.costUsd),s.push({pageUrl:e.pageUrl,ruleId:e.ruleId,selector:i.selector,verdict:c.verdict,reasoning:c.reasoning,resolvedAt:new Date().toISOString(),costUsd:c.costUsd,wcagCriterion:e.wcagCriterion})}catch(c){un.warn(`resolveAxeIncomplete failed for ${i.selector}`,c)}}return s.length>0&&await _n(s),{resolutions:s,totalCostUsd:o.state.spentUsd}}function os(){return D("AI_RESOLVE_INCOMPLETE_REQUEST",async e=>{const t=await ns();if(!t.enabled)return{type:"AI_RESOLVE_INCOMPLETE_RESPONSE",resolutions:[],totalCostUsd:0,unavailableReason:"AI augmentation is disabled in Settings → AI augmentation."};if(!t.apiKey)return{type:"AI_RESOLVE_INCOMPLETE_RESPONSE",resolutions:[],totalCostUsd:0,unavailableReason:"No AI API key configured. Add one in Settings → AI augmentation."};try{const{resolutions:n,totalCostUsd:o}=await as({ruleId:e.ruleId,pageUrl:e.pageUrl,wcagCriterion:e.wcagCriterion,elements:e.elements,targetLevel:e.targetLevel,aiConfig:t});return{type:"AI_RESOLVE_INCOMPLETE_RESPONSE",resolutions:n,totalCostUsd:o}}catch(n){return un.warn("ai-incomplete-resolver failed",n),{type:"AI_RESOLVE_INCOMPLETE_RESPONSE",resolutions:[],totalCostUsd:0,unavailableReason:n instanceof Error?n.message:"Unknown error during AI resolution."}}})}const _e={A:0,B:1,C:2,D:3,F:4},Rt={low:0,moderate:1,high:2,critical:3};function ss(e,t){return _e[e]>_e[t]?e:t}function rs(e,t){return Rt[e]>Rt[t]?e:t}function is(e){const t=[];for(const n of e)for(const o of n.results)for(const s of o.violations)t.push({url:n.url,v:s});return t}function cs(e,t,n,o,s){const i=t.filter(S=>!S.error).length,c=t.filter(S=>!!S.error).length,r=t.map(S=>{if(S.error)return{url:S.url,grade:"F",risk:"critical",uniqueViolations:0,totals:{critical:0,serious:0,moderate:0,minor:0},durationMs:S.durationMs,error:S.error};const E=S.results.flatMap(P=>P.violations),k=ie(E);return{url:S.url,grade:k.letter,risk:k.risk,uniqueViolations:k.totals.unique,totals:{critical:k.totals.critical,serious:k.totals.serious,moderate:k.totals.moderate,minor:k.totals.minor},durationMs:S.durationMs}});r.sort((S,E)=>{const k=_e[E.grade]-_e[S.grade];return k!==0?k:E.uniqueViolations-S.uniqueViolations});let d="A",l="low";for(const S of r)d=ss(d,S.grade),l=rs(l,S.risk);const p=is(t),h=S=>`${S.ruleId}::${S.target.selector}`,f=new Map;for(const{v:S}of p){const E=h(S);f.has(E)||f.set(E,S)}const a=f.size,g={critical:0,serious:0,moderate:0,minor:0};for(const S of f.values())g[S.impact]++;const u=new Map;for(const{url:S,v:E}of p){let k=u.get(E.ruleId);k||(k={ruleId:E.ruleId,description:E.description,impact:E.impact,urls:new Set,totalOccurrences:0},u.set(E.ruleId,k)),k.urls.add(S),k.totalOccurrences++}const v=Array.from(u.values()).map(S=>({ruleId:S.ruleId,description:S.description,impact:S.impact,urlsAffected:S.urls.size,totalOccurrences:S.totalOccurrences,sampleUrls:Array.from(S.urls).slice(0,5)})).sort((S,E)=>E.urlsAffected!==S.urlsAffected?E.urlsAffected-S.urlsAffected:E.totalOccurrences-S.totalOccurrences),O={A:0,B:0,C:0,D:0,F:0};for(const S of r)O[S.grade]++;return{startedAt:n,finishedAt:o,startUrl:e,pagesAudited:i,pagesFailed:c,totalDurationMs:s,siteGrade:d,siteRisk:l,totalUniqueViolations:a,totals:g,topViolations:v,pages:r,pagesByGrade:O}}function ls(e){const t=new Map(e.breakpointPresets.map(o=>[o.id,o])),n=[];for(const o of e.breakpoints){const s=t.get(o);if(s)for(const i of e.directions)for(const c of e.themes){const r=e.ariaVariations.length===0?[null]:e.ariaVariations;for(const d of r)for(const l of e.pseudoStates)n.push({pseudoState:l,ariaVariation:d,theme:c,direction:i,breakpoint:s})}}return n}const ds=W("ai-alt-text");async function us(e,t,n={}){var h,f;if(!t.enabled||!t.enabledChecks.altText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=K(t);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${o.reason}`]};const s=o.client,i=Y(t.costCapUsd),c=[],r=[],d=Math.max(1,t.maxCandidatesPerCheck??10),l=e.slice(0,d),p=l.length;for(let a=0;a<l.length;a++){const g=l[a];if(!i.canCharge())break;try{if((h=n.signal)!=null&&h.aborted){r.push(`canceled after ${a}/${p} candidates`);break}const u=await s.judgeAltText({imageUrl:g.imageUrl,alt:g.alt,surroundingContext:g.surroundingContext,signal:n.signal});i.recordCharge(u.costUsd),(u.verdict==="fail"||u.verdict==="uncertain"&&u.confidence<.6)&&c.push(await ps(g,u))}catch(u){if(u instanceof Error&&(u.name==="AbortError"||u.name==="ExternalAbortError")){r.push(`canceled after ${a}/${p} candidates`);break}const O=u instanceof Error?u.message:String(u);r.push(`${g.selector}: ${O}`),ds.warn("alt-text judgment failed",u)}(f=n.onProgress)==null||f.call(n,a+1,p)}return{findings:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:r}}async function ps(e,t){const n=t.verdict==="uncertain"?"ai-alt-uncertain":"ai-alt-misleading",o={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},s=await st({ruleId:n,componentId:e.componentId,currentState:e.currentState,target:o}),i=t.verdict==="uncertain";return{ruleId:n,wcagCriterion:"1.1.1",wcagLevel:"A",impact:i?"minor":"serious",description:i?"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:o,componentId:e.componentId,currentState:e.currentState,axeVersion:e.axeVersion,detectedAt:new Date().toISOString(),matchKey:s,ai:{reasoning:t.reasoning,confidence:t.confidence,model:t.model,costUsd:t.costUsd},...i?{needsReview:!0}:{}}}const he=W("ai-text");function fe(e){if(e!=null&&e.aborted){const t=new Error("aborted");throw t.name="AbortError",t}}function ge(e){return e instanceof Error&&(e.name==="AbortError"||e.name==="ExternalAbortError"||e.message==="aborted")}async function hs(e,t,n,o={}){var f;if(!n.enabled||!n.enabledChecks.headings)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const s=K(n);if(!s.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${s.reason}`]};const i=s.client,c=Y(n.costCapUsd),r=[],d=[],l=Math.max(1,n.maxCandidatesPerCheck??10),p=e.slice(0,l),h=p.length;for(let a=0;a<p.length;a++){const g=p[a];if(!c.canCharge())break;try{fe(o.signal);const u=await i.judgeHeading({headingText:g.text,sectionContent:g.sectionContent,signal:o.signal});c.recordCharge(u.costUsd),u.verdict==="fail"&&u.confidence>=.6&&r.push(await me("ai-heading-not-descriptive","2.4.6","AA","moderate","AI judged this heading does not describe its section content.",g.selector,g.outerHTML,g.text,u,t))}catch(u){if(ge(u)){d.push(`canceled after ${a}/${h} candidates`);break}const v=u instanceof Error?u.message:String(u);d.push(`${g.selector}: ${v}`),he.warn("heading judgment failed",u)}(f=o.onProgress)==null||f.call(o,a+1,h)}return{findings:r,totalCostUsd:c.state.spentUsd,capExceeded:c.state.exceeded,errors:d}}async function fs(e,t,n,o={}){var f;if(!n.enabled||!n.enabledChecks.sensory)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const s=K(n);if(!s.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${s.reason}`]};const i=s.client,c=Y(n.costCapUsd),r=[],d=[],l=Math.max(1,n.maxCandidatesPerCheck??10),p=e.slice(0,l),h=p.length;for(let a=0;a<p.length;a++){const g=p[a];if(!c.canCharge())break;try{fe(o.signal);const u=await i.judgeSensoryLanguage({instructionText:g.text,signal:o.signal});c.recordCharge(u.costUsd),u.verdict==="fail"&&u.confidence>=.6&&r.push(await me("ai-sensory-instruction","1.3.3","A","serious","AI flagged this instruction as relying on sensory characteristics (shape, color, location) without a programmatic identifier.",g.selector,g.outerHTML,g.text.slice(0,100),u,t))}catch(u){if(ge(u)){d.push(`canceled after ${a}/${h} candidates`);break}const v=u instanceof Error?u.message:String(u);d.push(`${g.selector}: ${v}`),he.warn("sensory judgment failed",u)}(f=o.onProgress)==null||f.call(o,a+1,h)}return{findings:r,totalCostUsd:c.state.spentUsd,capExceeded:c.state.exceeded,errors:d}}async function gs(e,t,n,o={}){var f;if(!n.enabled||!n.enabledChecks.aria)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const s=K(n);if(!s.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${s.reason}`]};const i=s.client,c=Y(n.costCapUsd),r=[],d=[],l=Math.max(1,n.maxCandidatesPerCheck??10),p=e.slice(0,l),h=p.length;for(let a=0;a<p.length;a++){const g=p[a];if(!c.canCharge())break;try{fe(o.signal);const u=await i.judgeAriaSemantics({elementHtml:g.outerHTML,computedRole:g.role,surroundingHtml:g.surroundingHtml,signal:o.signal});c.recordCharge(u.costUsd),u.verdict==="fail"&&u.confidence>=.65&&r.push(await me("ai-aria-misuse","4.1.2","A","serious",`AI judged role="${g.role}" is being used inappropriately on this element.`,g.selector,g.outerHTML,g.role,u,t))}catch(u){if(ge(u)){d.push(`canceled after ${a}/${h} candidates`);break}const v=u instanceof Error?u.message:String(u);d.push(`${g.selector}: ${v}`),he.warn("aria judgment failed",u)}(f=o.onProgress)==null||f.call(o,a+1,h)}return{findings:r,totalCostUsd:c.state.spentUsd,capExceeded:c.state.exceeded,errors:d}}async function ms(e,t,n,o={}){var f;if(!n.enabled||!n.enabledChecks.genericLinkText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const s=K(n);if(!s.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${s.reason}`]};const i=s.client,c=Y(n.costCapUsd),r=[],d=[],l=Math.max(1,n.maxCandidatesPerCheck??10),p=e.slice(0,l),h=p.length;for(let a=0;a<p.length;a++){const g=p[a];if(!c.canCharge())break;try{fe(o.signal);const u=await i.judgeGenericLinkText({linkText:g.linkText,surroundingText:g.surroundingText,signal:o.signal});c.recordCharge(u.costUsd),u.verdict==="fail"&&u.confidence>=.6&&r.push(await me("ai-generic-link-text","2.4.4","A","serious","AI judged this link's purpose is not clear from its text or surrounding context.",g.selector,g.outerHTML,g.linkText,u,t))}catch(u){if(ge(u)){d.push(`canceled after ${a}/${h} candidates`);break}const v=u instanceof Error?u.message:String(u);d.push(`${g.selector}: ${v}`),he.warn("generic-link judgment failed",u)}(f=o.onProgress)==null||f.call(o,a+1,h)}return{findings:r,totalCostUsd:c.state.spentUsd,capExceeded:c.state.exceeded,errors:d}}async function ys(e,t,n,o={}){var f;if(!n.enabled||!n.enabledChecks.wallOfText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const s=K(n);if(!s.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${s.reason}`]};const i=s.client,c=Y(n.costCapUsd),r=[],d=[],l=Math.max(1,n.maxCandidatesPerCheck??10),p=e.slice(0,l),h=p.length;for(let a=0;a<p.length;a++){const g=p[a];if(!c.canCharge())break;try{fe(o.signal);const u=await i.judgeWallOfText({blockText:g.blockText,structuralChildrenCount:g.structuralChildrenCount,signal:o.signal});c.recordCharge(u.costUsd),u.verdict==="fail"&&u.confidence>=.6&&r.push(await me("ai-wall-of-text","3.1.5","AAA","moderate","AI flagged this block as a wall of text with insufficient structural breaks (subheadings, lists, paragraphs).",g.selector,g.outerHTML,g.blockText.slice(0,80),u,t))}catch(u){if(ge(u)){d.push(`canceled after ${a}/${h} candidates`);break}const v=u instanceof Error?u.message:String(u);d.push(`${g.selector}: ${v}`),he.warn("wall-of-text judgment failed",u)}(f=o.onProgress)==null||f.call(o,a+1,h)}return{findings:r,totalCostUsd:c.state.spentUsd,capExceeded:c.state.exceeded,errors:d}}async function bs(e,t,n,o={}){var f;if(!n.enabled||!n.enabledChecks.languageMismatch)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const s=K(n);if(!s.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${s.reason}`]};const i=s.client,c=Y(n.costCapUsd),r=[],d=[],l=Math.max(1,n.maxCandidatesPerCheck??10),p=e.slice(0,l),h=p.length;for(let a=0;a<p.length;a++){const g=p[a];if(!c.canCharge())break;try{fe(o.signal);const u=await i.judgeLanguageMismatch({declaredLang:g.declaredLang,bodyTextSample:g.bodyTextSample,signal:o.signal});c.recordCharge(u.costUsd),u.verdict==="fail"&&u.confidence>=.7&&r.push(await me("ai-language-mismatch","3.1.1","A","serious",`AI judged the page's content does not match the declared lang="${g.declaredLang||"(none)"}".`,g.selector,g.outerHTML,g.bodyTextSample.slice(0,80),u,t))}catch(u){if(ge(u)){d.push(`canceled after ${a}/${h} candidates`);break}const v=u instanceof Error?u.message:String(u);d.push(`${g.selector}: ${v}`),he.warn("language-mismatch judgment failed",u)}(f=o.onProgress)==null||f.call(o,a+1,h)}return{findings:r,totalCostUsd:c.state.spentUsd,capExceeded:c.state.exceeded,errors:d}}async function me(e,t,n,o,s,i,c,r,d,l){const p={selector:i,outerHTML:c.slice(0,500),failureSummary:`AI assessment: ${d.reasoning}`,tagName:"AI",role:null,accessibleName:null,textSnippet:r.slice(0,100)||null,attrId:null,attrTestid:null},h=await st({ruleId:e,componentId:l.componentId,currentState:l.currentState,target:p});return{ruleId:e,wcagCriterion:t,wcagLevel:n,impact:o,description:s,helpUrl:"https://www.w3.org/WAI/WCAG21/Understanding/",target:p,componentId:l.componentId,currentState:l.currentState,axeVersion:l.axeVersion,detectedAt:new Date().toISOString(),matchKey:h,ai:{reasoning:d.reasoning,confidence:d.confidence,model:d.model,costUsd:d.costUsd}}}const pn=W("ai-vision");function hn(e){if(e!=null&&e.aborted){const t=new Error("aborted");throw t.name="AbortError",t}}function fn(e){return e instanceof Error&&(e.name==="AbortError"||e.name==="ExternalAbortError"||e.message==="aborted")}async function ws(e,t,n,o={}){var f;if(!n.enabled||!n.enabledChecks.imageOfText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const s=K(n);if(!s.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${s.reason}`]};const i=s.client,c=Y(n.costCapUsd),r=Math.max(1,n.imageOfTextMaxImages??5),d=[...e].sort((a,g)=>g.pixelArea-a.pixelArea).slice(0,r),l=d.length,p=[],h=[];for(let a=0;a<d.length;a++){const g=d[a];if(!c.canCharge())break;try{hn(o.signal);const u=await i.judgeImageOfText({imageUrl:g.imageUrl,accessibleName:g.accessibleName,signal:o.signal});c.recordCharge(u.costUsd),u.verdict==="fail"&&u.confidence>=.65&&p.push(await gn("ai-image-of-text","1.4.5","AA","moderate","AI judged this image bakes substantial text into the raster — should be presented as actual HTML text instead.",g.selector,g.outerHTML,g.accessibleName??"",u,t))}catch(u){if(fn(u)){h.push(`canceled after ${a}/${l} candidates`);break}const v=u instanceof Error?u.message:String(u);h.push(`${g.selector}: ${v}`),pn.warn("image-of-text judgment failed",u)}(f=o.onProgress)==null||f.call(o,a+1,l)}return{findings:p,totalCostUsd:c.state.spentUsd,capExceeded:c.state.exceeded,errors:h}}async function vs(e,t,n,o={}){var f;if(!n.enabled||!n.enabledChecks.colorOnlyMeaning)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const s=K(n);if(!s.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[s.reason]};const i=s.client,c=Y(n.costCapUsd),r=[],d=[],l=Math.max(1,n.maxCandidatesPerCheck??10),p=e.slice(0,l),h=p.length;for(let a=0;a<p.length;a++){const g=p[a];if(!c.canCharge())break;try{hn(o.signal);const u=await i.judgeColorOnlyMeaning({elementHtml:g.elementHtml,contextDescription:g.contextDescription,signal:o.signal});c.recordCharge(u.costUsd),u.verdict==="fail"&&u.confidence>=.65&&r.push(await gn("ai-color-only-meaning","1.4.1","A","serious","AI judged this region conveys information only through color (no text label, icon glyph, or shape difference).",g.selector,g.outerHTML,g.contextDescription,u,t))}catch(u){if(fn(u)){d.push(`canceled after ${a}/${h} candidates`);break}const v=u instanceof Error?u.message:String(u);d.push(v),pn.warn("color-only judgment failed",u)}(f=o.onProgress)==null||f.call(o,a+1,h)}return{findings:r,totalCostUsd:c.state.spentUsd,capExceeded:c.state.exceeded,errors:d}}async function gn(e,t,n,o,s,i,c,r,d,l){const p={selector:i,outerHTML:c.slice(0,500),failureSummary:`AI assessment: ${d.reasoning}`,tagName:"AI",role:null,accessibleName:null,textSnippet:r.slice(0,100)||null,attrId:null,attrTestid:null},h=await st({ruleId:e,componentId:l.componentId,currentState:l.currentState,target:p});return{ruleId:e,wcagCriterion:t,wcagLevel:n,impact:o,description:s,helpUrl:"https://www.w3.org/WAI/WCAG21/Understanding/",target:p,componentId:l.componentId,currentState:l.currentState,axeVersion:l.axeVersion,detectedAt:new Date().toISOString(),matchKey:h,ai:{reasoning:d.reasoning,confidence:d.confidence,model:d.model,costUsd:d.costUsd}}}const ot=W("ensure-content-script"),As=1500,Ss=300;async function mn(e,t){if(await Ot(e,t))return{ok:!0};const n=chrome.runtime.getManifest(),o=[];for(const s of n.content_scripts??[])Array.isArray(s.js)&&o.push(...s.js);if(o.length===0)return ot.error("manifest declares no content_scripts"),{ok:!1,reason:"inject-failed",detail:"no content_scripts declared in manifest"};try{const s=t!==void 0?{tabId:e,frameIds:[t]}:{tabId:e,allFrames:!0};await chrome.scripting.executeScript({target:s,files:o})}catch(s){return xs(s)}return await ks(Ss),await Ot(e,t)?(ot.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 Ot(e,t){try{const n=t!==void 0?{frameId:t}:void 0,o=await Promise.race([chrome.tabs.sendMessage(e,{type:"PING_REQUEST"},n),new Promise((s,i)=>setTimeout(()=>i(new Error("ping timeout")),As))]);return(o==null?void 0:o.type)==="PING_RESPONSE"}catch{return!1}}function xs(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}:(ot.warn("content-script injection failed",e),{ok:!1,reason:"inject-failed",detail:t})}function ks(e){return new Promise(t=>setTimeout(t,e))}function yn(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 Ye=W("forensic-anchor-client"),Es="https://api.wcagcheckr.com",$s="wcagcheckr",Ts=15e3;function Cs(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 Is(e,t){const n={auditHash:e.hash,pageUrl:e.pageUrl,capturedAt:e.capturedAt,...t?{licenseId:t}:{}},o=`${Es}/v1/products/${$s}/forensic/anchor`;try{const s=await fetch(o,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(n),signal:AbortSignal.timeout(Ts)});if(!s.ok)return Ye.warn(`anchor HTTP ${s.status}; entry stays local-only`),null;const i=await s.json();return Cs(i)?i:(Ye.warn("anchor returned malformed receipt; entry stays local-only",i),null)}catch(s){return Ye.warn("anchor request failed; entry stays local-only",s),null}}const _t="4.11.4";async function Rs(e){try{return(await chrome.tabs.get(e)).url??""}catch{return""}}const R=W("flows");async function Ge(){const e=await rt();if(!e)throw new Error("no audit target tab found");return e}async function Os(){var n,o,s,i,c;const e=await Oe("stateMatrix",Xe);return((n=e.pseudoStates)==null?void 0:n.length)>0&&((o=e.themes)==null?void 0:o.length)>0&&((s=e.directions)==null?void 0:s.length)>0&&((i=e.breakpoints)==null?void 0:i.length)>0&&((c=e.breakpointPresets)==null?void 0:c.length)>0?e:(R.warn("stored stateMatrix is invalid; using defaults"),Xe)}async function _s(){const e=await Oe("runsPerState",1);return typeof e!="number"||e<1||e>5?1:Math.floor(e)}async function Ms(e,t,n){const o=t.themes.includes("light"),s=t.themes.includes("dark");if(!o||!s)return t;let i=null;try{i=await j(e,{type:"THEME_AWARENESS_REQUEST"},n)}catch{return t}return!i||i.hasUnreadableSheets?t:i.hasDarkModeCss&&!i.hasLightModeCss?(R.info("theme-axis prune: site is dark-only, skipping light pass"),{...t,themes:t.themes.filter(c=>c!=="light")}):i.hasLightModeCss&&!i.hasDarkModeCss?(R.info("theme-axis prune: site is light-only, skipping dark pass"),{...t,themes:t.themes.filter(c=>c!=="dark")}):t}function Us(e){if(e.length<=1)return e[0]??[];const t=new Map;return e.forEach((n,o)=>{for(const s of n){const i=`${s.ruleId}::${s.target.selector}`,c=t.get(i);c?c.runIndices.add(o):t.set(i,{v:s,runIndices:new Set([o])})}}),Array.from(t.values()).map(({v:n,runIndices:o})=>({...n,flakyAcrossRuns:o.size<e.length}))}async function xe(e,t,n,o){var P;const s=await Ge(),i=await mn(s,n);if(!i.ok){N({type:"AUDIT_FAILED_EVENT",error:{code:i.reason==="restricted-url"?"RESTRICTED_URL":"CONTENT_SCRIPT_UNREACHABLE",message:yn(i),recoverable:i.reason!=="restricted-url"}});return}const c=o??await Os(),r=await Ms(s,c,n),d=ls(r),l=await _s(),p=await Gn(`${await Rs(s)}|${e}|${n??0}`),h=await Wn(r),f=await Oe("axeDisabledRules",[]),a=await Vn(f);let g=null;try{g=(await j(s,{type:"SCOPE_FINGERPRINT_REQUEST",selector:e},n)).fingerprint}catch(b){R.debug("scope fingerprint failed; skipping cache fast-path",b)}if(g){const b=await jn(p);if(b&&b.fingerprint===g&&b.axeVersion===_t&&b.matrixConfigHash===h&&b.disabledRulesHash===a){R.info(`audit-cache HIT for ${b.componentId} — skipping matrix iteration`),N({type:"AUDIT_COMPLETE_EVENT",componentId:b.componentId,results:b.results,delta:b.delta}),N({type:"SCORECARD_UPDATED_EVENT"});return}}await Ue({sessionId:Ne(),mode:"single-element",scope:e,startedAt:new Date().toISOString(),lastProgressAt:new Date().toISOString(),state:"running"}),await se(s,{type:"LIVE_REGIONS_ARM_REQUEST"},n).catch(()=>{}),await se(s,{type:"FOCUS_TRACE_ARM_REQUEST"},n).catch(()=>{}),await j(s,{type:"WARMUP_REQUEST"},n).catch(()=>{});const u=[],v=[];let O="",S,E=!1,k=null;try{for(let b=0;b<d.length;b++){if(t.isCanceled()){R.info("single audit canceled by user");break}const _=d[b];try{const M=await Ze(s,_,e,n);if(!M.success){S=(P=M.error)==null?void 0:P.message,R.warn(`state ${b+1}/${d.length} drive failed`,S);continue}_.breakpoint.id!==k&&(await j(s,{type:"WARMUP_REQUEST"},n).catch(()=>{}),k=_.breakpoint.id);const x=[];let w=null;for(let C=0;C<l;C++){const A=await j(s,{type:"AUDIT_REQUEST",selector:e,currentState:_},n);A.success&&A.result?(x.push(A.result.violations),w=A.result):A.error&&(S=A.error.message,R.warn(`state ${b+1}/${d.length} run ${C+1} failed`,S))}if(w&&x.length>0){E=!0;const C=Us(x),A=await ca(s,{quality:75})??void 0;let T,$;try{T=(await j(s,{type:"LIVE_REGIONS_DRAIN_REQUEST"},n)).announcements}catch{}try{$=(await j(s,{type:"FOCUS_TRACE_DRAIN_REQUEST"},n)).focusEvents}catch{}v.push({...w,violations:C,frameId:n,announcements:T,focusEvents:$,screenshotDataUrl:A}),u.push(...C),O=w.componentId}}catch(M){S=M instanceof Error?M.message:String(M),R.warn(`state ${b+1}/${d.length} threw`,M)}N({type:"AUDIT_PROGRESS_EVENT",current:b+1,total:d.length,currentState:_,componentId:O||void 0}),await Le({})}if(E&&O){const b={pseudoState:"default",ariaVariation:null,theme:"light",direction:"ltr",breakpoint:{id:"desktop",label:"Desktop",width:1280,height:800,deviceScaleFactor:1,mobile:!1}};try{await Ze(s,b,e,n),await new Promise(w=>setTimeout(w,200))}catch(w){R.debug("reference-state drive failed; analyzers will use last matrix state",w)}try{const w=await j(s,{type:"READING_ORDER_REQUEST",selector:e},n);w.issues.length>0&&v[0]&&(v[0].readingOrderIssues=w.issues,v[0].positionAnalysisCapturedAt=b)}catch(w){R.debug("reading-order analysis skipped",w)}try{const w=await j(s,{type:"TAB_ORDER_ANALYZE_REQUEST"},n);w.issues.length>0&&v[0]&&(v[0].tabOrderIssues=w.issues,v[0].positionAnalysisCapturedAt=b)}catch(w){R.debug("tab-order analysis skipped",w)}try{const w=await j(s,{type:"TYPOGRAPHY_ANALYZE_REQUEST",selector:e},n);w.issues.length>0&&v[0]&&(v[0].typographyIssues=w.issues)}catch(w){R.debug("typography analysis skipped",w)}try{const w=await j(s,{type:"CUSTOM_PROPERTY_REQUEST"},n);w.undefined.length>0&&v[0]&&(v[0].undefinedCustomProperties=w.undefined)}catch(w){R.debug("custom-property analysis skipped",w)}try{const w=await Oe("aiConfig",{}),C=Pe(w);C.enabled&&C.apiKey&&await Xs(s,e,n,C,v,O,t)}catch(w){R.warn("ai augmentation skipped",w)}const _=v.flatMap(w=>w.announcements??[]),M=v.flatMap(w=>w.focusEvents??[]),x=await Qt(O,u,{announcements:_,focusEvents:M},c);try{const w=ie(u,void 0,v),C=v.reduce((T,$)=>T+$.durationMs,0),A=await Mn(v,w,C);if(A){const T=await Wt();if(Dn(T,"forensicAnchoring"))try{const $=await Un(),J=await Is(A,$??void 0);J&&await Nn(A.componentId,A.capturedAt,J)}catch($){R.warn("forensic anchoring failed (entry stays local-only)",$)}}}catch(w){R.warn("forensic log recording failed",w)}if(g)try{await zn({lookupKey:p,fingerprint:g,axeVersion:_t,matrixConfigHash:h,disabledRulesHash:a,results:v,delta:x,componentId:O,cachedAt:Date.now()})}catch(w){R.warn("audit-cache write failed",w)}N({type:"AUDIT_COMPLETE_EVENT",componentId:O,results:v,delta:x})}else N({type:"AUDIT_FAILED_EVENT",error:{code:"AXE_FAILED",message:S?`All audits failed. Last error: ${S}`:"No state produced results. Selector may not exist in the audit target frame.",recoverable:!0}});await Ae()}catch(b){R.error("single audit failed",b),N({type:"AUDIT_FAILED_EVENT",componentId:O||void 0,error:Tn(b)?b.payload:{code:"UNKNOWN",message:String(b),recoverable:!1}})}finally{await se(s,{type:"LIVE_REGIONS_DISARM_REQUEST"},n).catch(()=>{}),await se(s,{type:"FOCUS_TRACE_DISARM_REQUEST"},n).catch(()=>{}),await Kt(s,e,n)}}async function Ns(e){var t;try{const o=(await chrome.scripting.executeScript({target:{tabId:e,allFrames:!0},world:"MAIN",func:()=>{var i;const s=window;return{detected:typeof s.__STORYBOOK_PREVIEW__=="object",version:(i=s.__STORYBOOK_PREVIEW__)==null?void 0:i.version}}})).find(s=>{var i;return(i=s.result)==null?void 0:i.detected});if(o)return{detected:!0,version:(t=o.result)==null?void 0:t.version,frameId:o.frameId}}catch(n){R.warn("storybook detection scripting failed",n)}return{detected:!1}}async function Ls(e){const t=await Ge(),n=await zs(t);if(await chrome.storage.local.set({"storybook:lastDetected":{detected:n.kind==="storybook"&&n.detected,version:n.version,detectedAt:new Date().toISOString()},"playground:lastDetected":{kind:n.kind,detected:n.detected,version:n.version,detectedAt:new Date().toISOString()}}),!n.detected){N({type:"AUDIT_FAILED_EVENT",error:{code:"STORYBOOK_NOT_DETECTED",message:"No supported component playground detected on this tab. Open a Storybook (iframe.html / storybook-static), Ladle, or Bit page and try again.",recoverable:!0}});return}if(n.kind==="bit"){N({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 o=n.frameId??0,s=n.kind==="storybook"?await Ds(t,o):await Ws(t,o);if(!s.length){R.warn(`${n.kind} detected but no stories returned`),N({type:"AUDIT_FAILED_EVENT",error:{code:"STORYBOOK_NOT_DETECTED",message:`${n.kind==="ladle"?"Ladle":"Storybook"} detected but no stories were returned. Try reloading the page.`,recoverable:!0}});return}await Ue({sessionId:Ne(),mode:"storybook-all",totalStories:s.length,completedStories:[],startedAt:new Date().toISOString(),lastProgressAt:new Date().toISOString(),state:"running"});for(const i of s){if(e.isCanceled()){R.info(`${n.kind} iteration canceled by user`);break}try{if(n.kind==="storybook"){await Fs(t,o,i.id);const r=await se(t,{type:"STORYBOOK_GET_STORY_TARGET_REQUEST",tabId:t},o);await xe(r.selector,e,o)}else await Vs(t,o,i.id),await xe("#ladle-root",e,o)}catch(r){R.warn(`story ${i.id} failed; continuing with next`,r)}const c=await Vt();if((c==null?void 0:c.state)==="interrupted"){R.info("storybook iteration interrupted");break}await Le({completedStories:[...(c==null?void 0:c.completedStories)??[],i.id]})}N({type:"SCORECARD_UPDATED_EVENT"}),await Ae()}async function Ps(e){const t=await Ge();let n=[];try{n=(await chrome.scripting.executeScript({target:{tabId:t,allFrames:!0},func:()=>({url:window.location.href})})).map(s=>{var i;return{frameId:s.frameId,url:((i=s.result)==null?void 0:i.url)??""}}).filter(s=>/^https?:/i.test(s.url))}catch(o){R.warn("multi-frame discovery failed",o)}if(n.length===0){N({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 Ue({sessionId:Ne(),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()){R.info("all-frames iteration canceled");break}try{await xe("html",e,o.frameId)}catch(i){R.warn(`frame ${o.frameId} (${o.url.slice(0,80)}) audit failed; continuing`,i)}const s=await Vt();if((s==null?void 0:s.state)==="interrupted"){R.info("all-frames iteration interrupted");break}await Le({completedFrames:[...(s==null?void 0:s.completedFrames)??[],o.frameId]})}N({type:"SCORECARD_UPDATED_EVENT"}),await Ae()}async function Ds(e,t){var n;try{return((n=(await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",func:async()=>{var d,l;const s=window.__STORYBOOK_PREVIEW__;if(!s)return[];const i=s.storyStoreValue??s.storyStore;if(!i)return[];typeof i.cacheAllCSFFiles=="function"&&await i.cacheAllCSFFiles();const c=((d=i.extract)==null?void 0:d.call(i))??((l=i.cachedCSFFiles)==null?void 0:l.call(i));if(!c)return[];const r=[];if(c instanceof Map)for(const[p,h]of c){const f=h;r.push({id:String(p),name:f.name??String(p),kind:f.title??f.kind??""})}else if(typeof c=="object"&&c!==null)for(const[p,h]of Object.entries(c)){const f=h;r.push({id:p,name:f.name??p,kind:f.title??f.kind??""})}return r}}))[0])==null?void 0:n.result)??[]}catch(o){return R.warn("listStoriesViaMainWorld failed",o),[]}}async function Fs(e,t,n){try{await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",args:[n,3e3],func:async(o,s)=>{const i=new URL(window.location.href);i.searchParams.set("id",o),i.searchParams.set("viewMode","story"),window.location.href!==i.href&&(window.history.pushState({},"",i.href),window.dispatchEvent(new PopStateEvent("popstate"))),await new Promise(c=>{const r=setTimeout(()=>c(),s),d=()=>{clearTimeout(r),c()};window.addEventListener("storyrendered",d,{once:!0})})}})}catch(o){R.warn(`navigateToStoryViaMainWorld(${n}) failed`,o)}}async function Gs(e){var t;try{const o=(await chrome.scripting.executeScript({target:{tabId:e,allFrames:!0},world:"MAIN",func:()=>{const s=document.getElementById("ladle-root")!==null,i=document.title==="Ladle",c=!!document.querySelector('link[href*="ladle.css"]');return{detected:s&&(i||c),version:void 0}}})).find(s=>{var i;return(i=s.result)==null?void 0:i.detected});if(o)return{detected:!0,version:(t=o.result)==null?void 0:t.version,frameId:o.frameId}}catch(n){R.warn("ladle detection scripting failed",n)}return{detected:!1}}async function Ws(e,t){var n;try{return((n=(await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",func:async()=>{try{const s=await fetch(new URL("/meta.json",window.location.href).href,{credentials:"same-origin"});if(!s.ok)return[];const c=(await s.json()).stories??{};return Object.entries(c).map(([r,d])=>({id:r,name:d.name??r,kind:(d.levels??[]).join(" / ")}))}catch{return[]}}}))[0])==null?void 0:n.result)??[]}catch(o){return R.warn("listLadleStoriesViaMainWorld failed",o),[]}}async function Vs(e,t,n){try{await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",args:[n,3e3],func:async(o,s)=>{const i=new URL(window.location.href);i.searchParams.set("story",o),window.location.href!==i.href&&(window.history.pushState({},"",i.href),window.dispatchEvent(new PopStateEvent("popstate"))),await new Promise(c=>setTimeout(c,Math.min(s,500)))}})}catch(o){R.warn(`navigateToLadleStoryViaMainWorld(${n}) failed`,o)}}async function js(e){try{return(await chrome.scripting.executeScript({target:{tabId:e,allFrames:!0},world:"MAIN",func:()=>{const n=window.location.href;return/bit\.dev|bit-dev|bitsrc|\/~aspect|\/composition/i.test(n)}})).some(n=>n.result===!0)}catch{return!1}}async function zs(e){const t=await Ns(e);if(t.detected)return{kind:"storybook",...t};const n=await Gs(e);return n.detected?{kind:"ladle",...n}:await js(e)?{kind:"bit",detected:!0}:{kind:null,detected:!1}}const Hs=200,Bs=6e4;function Mt(e,t){try{return new URL(e).origin===new URL(t).origin}catch{return!1}}function qs(e,t){try{const n=new URL(e,t);return n.hash="",n.toString()}catch{return null}}async function Ks(e,t,n){await chrome.tabs.update(e,{url:t}),await new Promise((o,s)=>{const i=setTimeout(()=>{chrome.tabs.onUpdated.removeListener(c),s(new Error(`navigation timeout (${n}ms): ${t}`))},n);function c(r,d){r===e&&d.status==="complete"&&(clearTimeout(i),chrome.tabs.onUpdated.removeListener(c),o())}chrome.tabs.onUpdated.addListener(c)}),await new Promise(o=>setTimeout(o,400))}async function Ys(e){var t;try{return((t=(await chrome.scripting.executeScript({target:{tabId:e},func:()=>Array.from(document.querySelectorAll("a[href]")).map(o=>o.getAttribute("href")).filter(o=>typeof o=="string"&&!o.startsWith("javascript:"))}))[0])==null?void 0:t.result)??[]}catch(n){return R.debug("link discovery failed",n),[]}}async function Js(e,t,n){var f;const o=await Ge(),s=Math.min(t.maxPages??25,Hs),i=t.includeRegex?Ut(t.includeRegex):null,c=t.excludeRegex?Ut(t.excludeRegex):null,r=new Date().toISOString(),d=Date.now(),l=[e],p=new Set,h=[];await Ue({sessionId:Ne(),mode:"storybook-all",scope:e,startedAt:r,lastProgressAt:new Date().toISOString(),state:"running"});try{for(;l.length>0&&p.size<s;){if(n.isCanceled()){R.info("site crawl canceled by user");break}const v=l.shift();if(!v||p.has(v)||!Mt(v,e)||i&&!i.test(v)||c&&c.test(v))continue;p.add(v);const O=p.size,S=Date.now();N({type:"SITE_CRAWL_PROGRESS_EVENT",current:O,total:Math.min(s,p.size+l.length),url:v,status:"auditing"});try{await Ks(o,v,Bs);const E=await mn(o,0);if(!E.ok)throw new Error(yn(E));await j(o,{type:"WARMUP_REQUEST"},0).catch(()=>{});const k=await j(o,{type:"AUDIT_REQUEST",selector:"html",currentState:{pseudoState:"default",ariaVariation:null,theme:"light",direction:"ltr",breakpoint:{id:"desktop",label:"Desktop",width:1280,height:800,deviceScaleFactor:1,mobile:!1}}},0),P=Date.now()-S;if(k.success&&k.result)h.push({url:v,results:[k.result],delta:null,componentId:k.result.componentId,durationMs:P}),N({type:"SITE_CRAWL_PROGRESS_EVENT",current:O,total:Math.min(s,p.size+l.length),url:v,status:"completed",violations:k.result.violations.length});else{const _=((f=k.error)==null?void 0:f.message)??"audit returned no result";h.push({url:v,results:[],delta:null,componentId:v,durationMs:P,error:_}),N({type:"SITE_CRAWL_PROGRESS_EVENT",current:O,total:Math.min(s,p.size+l.length),url:v,status:"failed",error:_})}const b=await Ys(o);for(const _ of b){const M=qs(_,v);M&&Mt(M,e)&&!p.has(M)&&!l.includes(M)&&l.push(M)}}catch(E){const k=Date.now()-S,P=E instanceof Error?E.message:String(E);h.push({url:v,results:[],delta:null,componentId:v,durationMs:k,error:P}),N({type:"SITE_CRAWL_PROGRESS_EVENT",current:O,total:Math.min(s,p.size+l.length),url:v,status:"failed",error:P})}await Le({})}const a=new Date().toISOString(),g=Date.now()-d,u=cs(e,h,r,a,g);N({type:"SITE_CRAWL_COMPLETE_EVENT",report:u}),await Ae()}catch(a){R.error("site crawl failed",a),N({type:"SITE_CRAWL_FAILED_EVENT",error:{code:"UNKNOWN",message:String(a),recoverable:!1}}),await Ae()}}function Ut(e){try{return new RegExp(e)}catch{return null}}const Je=5*6e4,Nt=9e4,Lt={altText:"Verifying alt text…",headings:"Judging heading quality…",sensory:"Scanning for sensory-only instructions…",aria:"Reviewing ARIA semantics…",imageOfText:"Detecting baked-in text in images…",colorOnlyMeaning:"Checking for color-only meaning…",genericLinkText:"Reviewing link clarity…",wallOfText:"Detecting walls of text…",languageMismatch:"Verifying page language…"};async function Xs(e,t,n,o,s,i,c){let r;try{r=await j(e,{type:"AI_CANDIDATES_REQUEST",selector:t},n)}catch(A){R.debug("ai candidates fetch failed",A);return}const d=s[0];if(!d)return;const l={componentId:i,currentState:d.state,axeVersion:d.axeVersion},p={at:new Date().toISOString(),componentId:i,pageUrl:d.pageUrl??t,enabledChecks:o.enabledChecks,candidateCounts:{images:r.images.length,headings:r.headings.length,instructions:r.instructions.length,ariaElements:r.ariaElements.length,links:r.links.length,longTextBlocks:r.longTextBlocks.length,colorOnlyRegions:r.colorOnlyRegions.length,languageInfo:r.languageInfo?"present":"null"}};R.info("ai augmentation gate snapshot",JSON.stringify(p));try{await chrome.storage.local.set({aiDiagnosticLatest:p})}catch(A){R.warn("failed to persist ai diagnostic to storage.local",A)}try{const A=JSON.stringify(p,null,2),T=`data:application/json;charset=utf-8,${encodeURIComponent(A)}`,$=p.at.replace(/[:.]/g,"-"),J=await chrome.downloads.download({url:T,filename:`wcagcheckr-ai-diag-${$}.json`,saveAs:!1,conflictAction:"uniquify"});R.info(`ai diagnostic download started (id=${J})`)}catch(A){R.warn("failed to write ai diagnostic to Downloads (storage.local copy is still available under aiDiagnosticLatest)",A)}const h=[];if(o.enabledChecks.altText&&h.push({key:"altText",run:A=>{const T=r.images.map($=>({imageUrl:$.imageUrl,alt:$.alt,selector:$.selector,outerHTML:$.outerHTML,surroundingContext:$.surroundingContext,...l}));return us(T,o,A)}}),o.enabledChecks.headings&&h.push({key:"headings",run:A=>hs(r.headings,l,o,A)}),o.enabledChecks.sensory&&h.push({key:"sensory",run:A=>fs(r.instructions,l,o,A)}),o.enabledChecks.aria&&h.push({key:"aria",run:A=>gs(r.ariaElements,l,o,A)}),o.enabledChecks.imageOfText&&h.push({key:"imageOfText",run:A=>{const T=r.images.map($=>({imageUrl:$.imageUrl,accessibleName:$.alt||void 0,selector:$.selector,outerHTML:$.outerHTML,pixelArea:$.pixelArea}));return ws(T,l,o,A)}}),o.enabledChecks.colorOnlyMeaning&&h.push({key:"colorOnlyMeaning",run:A=>vs(r.colorOnlyRegions,l,o,A)}),o.enabledChecks.genericLinkText&&h.push({key:"genericLinkText",run:A=>ms(r.links,l,o,A)}),o.enabledChecks.wallOfText&&h.push({key:"wallOfText",run:A=>ys(r.longTextBlocks,l,o,A)}),o.enabledChecks.languageMismatch){const A=r.languageInfo;h.push({key:"languageMismatch",run:T=>bs(A?[A]:[],l,o,T)})}if(h.length===0)return;let f=0,a=0,g=!1;const u={};let v=0,O=0,S=0;const E=[];let k=!1,P=!1;function b(A,T){if(u[A]=(u[A]??0)+T.totalCostUsd,f+=T.totalCostUsd,a+=T.findings.length,g=g||T.capExceeded,v++,T.errors.length>0){S++;for(const $ of T.errors)E.includes($)||E.push($)}else O++;R.info(`ai ${A}: ${T.findings.length} findings, $${T.totalCostUsd.toFixed(4)}, errs=${T.errors.length}`),T.capExceeded&&R.warn(`ai cost cap exceeded after $${T.totalCostUsd.toFixed(4)} during ${A}`)}const _=(async()=>{for(let A=0;A<h.length;A++){if(c!=null&&c.isCanceled()){P=!0;break}const T=h[A];N({type:"AI_AUGMENTATION_PROGRESS_EVENT",componentId:i,currentCheck:T.key,currentCheckLabel:Lt[T.key],current:A+1,total:h.length});const $=new AbortController,J=setTimeout(()=>$.abort(),Nt),ye=c?setInterval(()=>{c.isCanceled()&&$.abort()},500):null;let Q;try{Q=await T.run({signal:$.signal,onProgress:(ne,m)=>{m>0&&N({type:"AI_AUGMENTATION_PROGRESS_EVENT",componentId:i,currentCheck:T.key,currentCheckLabel:Lt[T.key],current:A+1,total:h.length,candidatesDone:ne,candidatesTotal:m})}}),$.signal.aborted&&(c!=null&&c.isCanceled())?Q.errors=[...Q.errors,`${T.key} canceled by user`]:$.signal.aborted}catch(ne){const m=ne instanceof Error?ne.message:String(ne),L=$.signal.aborted,I=L&&(c!=null&&c.isCanceled())?`${T.key} canceled by user`:L?`${T.key} hit per-check ${Nt/1e3}s cap — analyzer aborted`:`${T.key} threw: ${m}`;Q={findings:[],totalCostUsd:0,capExceeded:!1,errors:[I]}}finally{clearTimeout(J),ye&&clearInterval(ye)}Q.findings.length>0&&(d.violations=[...d.violations,...Q.findings]),b(T.key,Q)}})(),M=new Promise(A=>{setTimeout(()=>A("timeout"),Je)});await Promise.race([_.then(()=>"done"),M])==="timeout"&&(k=!0,R.warn(`ai augmentation hit global ${Je/1e3}s timeout after ${v}/${h.length} checks`));let w;if(S>0||k||P){k?w=`AI augmentation timed out after ${Je/1e3}s`:P?w="AI augmentation canceled":w=E[0]??"AI analyzer error";const A=O===0?"total":"partial";N({type:"AI_AUGMENTATION_FAILED_EVENT",componentId:i,severity:A,reason:w,checksAttempted:v,checksSucceeded:O,checksErrored:S,errorDetails:k||P?[w,...E.slice(0,4)]:E.slice(0,5)}),R.warn(`ai augmentation ${A} failure: ${S}/${v} checks errored — ${w}`)}(f>0||a>0||w)&&await Hn({at:new Date().toISOString(),pageUrl:d.pageUrl??t,componentId:i,totalCostUsd:f,byCheck:u,findingsCount:a,capExceeded:g,failureReason:w})}const H=W("service-worker");xn("service-worker");Ga();var $e,Pt;(Pt=($e=chrome.sidePanel)==null?void 0:$e.setPanelBehavior)==null||Pt.call($e,{openPanelOnActionClick:!0}).catch(e=>H.warn("setPanelBehavior failed",e));la();xa();Ln();Aa().catch(e=>H.warn("initial cloud pull failed",e));(async()=>(await Pa(),en()))();Da();ja();jo();Qo();ts();vo();os();self.addEventListener("unhandledrejection",e=>{const t=e.reason;kn(t instanceof Error?t:new Error(String(t)))});let Me=!1;chrome.runtime.onConnect.addListener(e=>{if(e.name==="audit-keepalive"){H.debug("keepalive connected"),e.onDisconnect.addListener(async()=>{H.debug("keepalive disconnected"),await Pn()});return}if(e.name==="sidepanel-tracker"){Me=!0,H.debug("chrome.sidePanel opened"),e.onDisconnect.addListener(async()=>{Me=!1,H.debug("chrome.sidePanel closed — restoring in-page overlay"),await pt()});return}});let ut=!1;const Ee={isCanceled:()=>ut};async function bn(){const e=await rt();if(e)try{await se(e,{type:"SIDEBAR_HIDE_REQUEST"})}catch(t){H.warn("sidebar hide failed",t)}}async function pt(){const e=await rt();if(e)try{await se(e,{type:"SIDEBAR_SHOW_REQUEST"})}catch(t){H.warn("sidebar show failed",t)}}D("START_AUDIT",async e=>{ut=!1,await bn();let t;if(e.mode==="single-element"){if(!e.scope)throw new Error("START_AUDIT single-element requires scope");t=xe(e.scope,Ee,e.frameId,e.matrixOverride)}else e.mode==="full-page"?t=xe("html",Ee,0,e.matrixOverride):e.mode==="all-frames"?t=Ps(Ee):t=Ls(Ee);t.catch(n=>{H.error("audit failed",n);const o=n instanceof Error?n.message:String(n),s=/no audit target tab/i.test(o);N({type:"AUDIT_FAILED_EVENT",error:{code:s?"RESTRICTED_URL":"UNKNOWN",message:s?"No audit target tab found. Open a regular http(s) page (not chrome:// or about:blank) and try again.":`Audit failed: ${o}`,recoverable:!0}})}).finally(()=>{Me||pt()})});D("CANCEL_AUDIT",async()=>{ut=!0,H.info("cancel requested")});let ht=!1;const Qs={isCanceled:()=>ht};D("START_SITE_CRAWL",async e=>{ht=!1,await bn(),Js(e.startUrl,{maxPages:e.maxPages,includeRegex:e.includeRegex,excludeRegex:e.excludeRegex},Qs).catch(t=>H.error("site crawl failed",t)).finally(()=>{Me||pt()})});D("CANCEL_SITE_CRAWL",async()=>{ht=!0,H.info("site crawl cancel requested")});D("OPEN_SETTINGS",()=>{chrome.runtime.openOptionsPage()});H.info("service worker ready");
790
+ </html>`}async function jo(){const t=(await chrome.storage.local.get("aiConfig")).aiConfig;return{...Gn,...t??{}}}async function It(e,t,n){const o=await jo();if(!o.enabled||!o.apiKey||e.length===0)return null;const s=e.flatMap(d=>d.violations),i=ie(s,void 0,e),c=i.categories.filter(d=>d.status==="fail").sort((d,l)=>l.violationCount-d.violationCount).slice(0,3).map(d=>d.label),r=e[0];return an({framing:t,riskTier:i.risk,grade:i.letter,totals:{critical:i.totals.critical,serious:i.totals.serious,moderate:i.totals.moderate,minor:i.totals.minor,unique:i.totals.unique},riskDrivers:c,pageUrl:(r==null?void 0:r.pageUrl)??(r==null?void 0:r.scope)??"",auditDate:(r==null?void 0:r.startedAt)??new Date().toISOString(),priorAuditCount:n},o)}function zo(){return D("EXPORT_REQUEST",async e=>{if(e.format==="sarif")return{type:"EXPORT_RESPONSE",format:"sarif",content:JSON.stringify(xo(e.results,e.delta),null,2)};if(e.format==="junit")return{type:"EXPORT_RESPONSE",format:"junit",content:ko(e.results,e.delta)};if(e.format==="html-print")return{type:"EXPORT_RESPONSE",format:"html-print",content:Eo(e.results,e.delta)};if(e.format==="vpat")return{type:"EXPORT_RESPONSE",format:"vpat",content:$o(e.results,e.delta)};if(e.format==="ai-prompt")return{type:"EXPORT_RESPONSE",format:"ai-prompt",content:Io(e.results,e.delta,e.manualRuns,e.siteCrawlReport,e.dismissedKeys,e.incompleteResolutions)};if(e.format==="defense-bundle"){const t=await mt(),n=await It(e.results,"defense",t.length);return{type:"EXPORT_RESPONSE",format:"defense-bundle",content:ln(e.results,e.delta,"defense",e.manualRuns,t,n)}}if(e.format==="evidence-bundle"){const t=await mt(),n=await It(e.results,"evidence",t.length);return{type:"EXPORT_RESPONSE",format:"evidence-bundle",content:Po(e.results,e.delta,e.manualRuns,t,n)}}return e.format==="developer-letter"?{type:"EXPORT_RESPONSE",format:"developer-letter",content:Do(e.results)}:e.format==="owner-report"?{type:"EXPORT_RESPONSE",format:"owner-report",content:Fo(e.results)}:e.format==="ai-prompt-owner"?{type:"EXPORT_RESPONSE",format:"ai-prompt-owner",content:Go(e.results)}:{type:"EXPORT_RESPONSE",format:"json",content:JSON.stringify(So(e.results,e.delta),null,2)}})}const Ho=W("support-messenger"),Bo="https://api.wcagcheckr.com/v1/products/wcagcheckr/support",Ye="support:rate-window",qo=5,Ko=10*60*1e3,Yo=2,Jo="wcagcheckr",Rt="storybook:lastDetected";async function Xo(e){if(!e)return;const t=await chrome.runtime.getPlatformInfo(),o=(await chrome.storage.local.get(Rt))[Rt];return{extensionVersion:chrome.runtime.getManifest().version,platform:t,storybookDetected:o!=null&&o.detected?o.version??"detected":"none",licenseTier:await Vt(),logTail:Sn(xn())}}async function Qo(e=Date.now()){const n=((await chrome.storage.local.get(Ye))[Ye]??[]).filter(o=>e-o<Ko);return n.length>=qo?!1:(n.push(e),await chrome.storage.local.set({[Ye]:n}),!0)}async function dn(e,t=0){try{const n=await fetch(Bo,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!n.ok)throw new Error(`http ${n.status}`);const o=await n.json();if(!o.success)throw new Error(o.error??"server returned success=false");return{ticketRef:o.ticketRef??""}}catch(n){if(t<Yo){const o=(t+1)*1e3;return await new Promise(s=>setTimeout(s,o)),dn(e,t+1)}throw n}}function Zo(){return D("SUPPORT_MESSAGE_REQUEST",async e=>{if(!await Qo())return{type:"SUPPORT_MESSAGE_RESPONSE",success:!1,error:Ie("NETWORK","Rate limit reached. Try again in 10 minutes.",!0)};try{const t=await Xo(e.includeContext);return{type:"SUPPORT_MESSAGE_RESPONSE",success:!0,ticketRef:(await dn({productSlug:Jo,subject:e.subject,body:e.body,replyEmail:e.replyEmail,context:t,extensionVersion:chrome.runtime.getManifest().version,timestamp:new Date().toISOString()})).ticketRef}}catch(t){Ho.error("send failed",t);const n=t instanceof Error?t.message:String(t);return{type:"SUPPORT_MESSAGE_RESPONSE",success:!1,error:Ie("NETWORK",n,!0)}}})}const un=W("ai-color-suggester");async function es(e,t,n=[]){if(!t.enabled||e.length===0)return{results:[],totalCostUsd:0,capExceeded:!1};const o=K(t);if(!o.ok)return{results:[],totalCostUsd:0,capExceeded:!1};const s=o.client,i=Y(t.costCapUsd),c=[];for(const r of e){if(!i.canCharge())break;try{const d=await s.suggestColorFix({foreground:r.foreground,background:r.background,fontSize:r.fontSize,fontWeight:r.fontWeight,targetLevel:r.targetLevel,paletteHints:n});i.recordCharge(d.costUsd),d.verdict==="suggested"&&d.candidates.length>0&&c.push({matchKey:r.matchKey,suggestions:d.candidates,reasoning:d.reasoning,costUsd:d.costUsd})}catch(d){un.warn(`color-fix suggestion failed for ${r.matchKey}`,d)}}return{results:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded}}async function ts(){const e=await chrome.storage.local.get("aiConfig");return De(e.aiConfig)}function ns(){return D("AI_COLOR_SUGGEST_REQUEST",async e=>{const t=await ts();if(!t.enabled)return{type:"AI_COLOR_SUGGEST_RESPONSE",results:[],totalCostUsd:0,capExceeded:!1,unavailableReason:"AI augmentation is disabled in Settings → AI augmentation."};if(!t.apiKey)return{type:"AI_COLOR_SUGGEST_RESPONSE",results:[],totalCostUsd:0,capExceeded:!1,unavailableReason:"No AI API key configured. Add one in Settings → AI augmentation."};try{const{results:n,totalCostUsd:o,capExceeded:s}=await es(e.inputs,t,e.paletteHints);return{type:"AI_COLOR_SUGGEST_RESPONSE",results:n,totalCostUsd:o,capExceeded:s}}catch(n){return un.warn("color-suggest handler failed",n),{type:"AI_COLOR_SUGGEST_RESPONSE",results:[],totalCostUsd:0,capExceeded:!1,unavailableReason:n instanceof Error?n.message:String(n)}}})}const pn=W("ai-incomplete-resolver");async function as(){const e=await chrome.storage.local.get("aiConfig");return De(e.aiConfig)}async function os(e){if(!e.aiConfig.enabled||e.elements.length===0)return{resolutions:[],totalCostUsd:0};const t=K(e.aiConfig);if(!t.ok)return{resolutions:[],totalCostUsd:0};const n=t.client,o=Y(e.aiConfig.costCapUsd),s=[];for(const i of e.elements){if(!o.canCharge())break;try{const c=await n.resolveAxeIncomplete({ruleId:e.ruleId,element:i,pageUrl:e.pageUrl,targetLevel:e.targetLevel});o.recordCharge(c.costUsd),s.push({pageUrl:e.pageUrl,ruleId:e.ruleId,selector:i.selector,verdict:c.verdict,reasoning:c.reasoning,resolvedAt:new Date().toISOString(),costUsd:c.costUsd,wcagCriterion:e.wcagCriterion})}catch(c){pn.warn(`resolveAxeIncomplete failed for ${i.selector}`,c)}}return s.length>0&&await Mn(s),{resolutions:s,totalCostUsd:o.state.spentUsd}}function ss(){return D("AI_RESOLVE_INCOMPLETE_REQUEST",async e=>{const t=await as();if(!t.enabled)return{type:"AI_RESOLVE_INCOMPLETE_RESPONSE",resolutions:[],totalCostUsd:0,unavailableReason:"AI augmentation is disabled in Settings → AI augmentation."};if(!t.apiKey)return{type:"AI_RESOLVE_INCOMPLETE_RESPONSE",resolutions:[],totalCostUsd:0,unavailableReason:"No AI API key configured. Add one in Settings → AI augmentation."};try{const{resolutions:n,totalCostUsd:o}=await os({ruleId:e.ruleId,pageUrl:e.pageUrl,wcagCriterion:e.wcagCriterion,elements:e.elements,targetLevel:e.targetLevel,aiConfig:t});return{type:"AI_RESOLVE_INCOMPLETE_RESPONSE",resolutions:n,totalCostUsd:o}}catch(n){return pn.warn("ai-incomplete-resolver failed",n),{type:"AI_RESOLVE_INCOMPLETE_RESPONSE",resolutions:[],totalCostUsd:0,unavailableReason:n instanceof Error?n.message:"Unknown error during AI resolution."}}})}const Me={A:0,B:1,C:2,D:3,F:4},Ot={low:0,moderate:1,high:2,critical:3};function rs(e,t){return Me[e]>Me[t]?e:t}function is(e,t){return Ot[e]>Ot[t]?e:t}function cs(e){const t=[];for(const n of e)for(const o of n.results)for(const s of o.violations)t.push({url:n.url,v:s});return t}function ls(e,t,n,o,s){const i=t.filter(S=>!S.error).length,c=t.filter(S=>!!S.error).length,r=t.map(S=>{if(S.error)return{url:S.url,grade:"F",risk:"critical",uniqueViolations:0,totals:{critical:0,serious:0,moderate:0,minor:0},durationMs:S.durationMs,error:S.error};const E=S.results.flatMap(P=>P.violations),k=ie(E);return{url:S.url,grade:k.letter,risk:k.risk,uniqueViolations:k.totals.unique,totals:{critical:k.totals.critical,serious:k.totals.serious,moderate:k.totals.moderate,minor:k.totals.minor},durationMs:S.durationMs}});r.sort((S,E)=>{const k=Me[E.grade]-Me[S.grade];return k!==0?k:E.uniqueViolations-S.uniqueViolations});let d="A",l="low";for(const S of r)d=rs(d,S.grade),l=is(l,S.risk);const p=cs(t),h=S=>`${S.ruleId}::${S.target.selector}`,f=new Map;for(const{v:S}of p){const E=h(S);f.has(E)||f.set(E,S)}const a=f.size,g={critical:0,serious:0,moderate:0,minor:0};for(const S of f.values())g[S.impact]++;const u=new Map;for(const{url:S,v:E}of p){let k=u.get(E.ruleId);k||(k={ruleId:E.ruleId,description:E.description,impact:E.impact,urls:new Set,totalOccurrences:0},u.set(E.ruleId,k)),k.urls.add(S),k.totalOccurrences++}const w=Array.from(u.values()).map(S=>({ruleId:S.ruleId,description:S.description,impact:S.impact,urlsAffected:S.urls.size,totalOccurrences:S.totalOccurrences,sampleUrls:Array.from(S.urls).slice(0,5)})).sort((S,E)=>E.urlsAffected!==S.urlsAffected?E.urlsAffected-S.urlsAffected:E.totalOccurrences-S.totalOccurrences),O={A:0,B:0,C:0,D:0,F:0};for(const S of r)O[S.grade]++;return{startedAt:n,finishedAt:o,startUrl:e,pagesAudited:i,pagesFailed:c,totalDurationMs:s,siteGrade:d,siteRisk:l,totalUniqueViolations:a,totals:g,topViolations:w,pages:r,pagesByGrade:O}}function ds(e){const t=new Map(e.breakpointPresets.map(o=>[o.id,o])),n=[];for(const o of e.breakpoints){const s=t.get(o);if(s)for(const i of e.directions)for(const c of e.themes){const r=e.ariaVariations.length===0?[null]:e.ariaVariations;for(const d of r)for(const l of e.pseudoStates)n.push({pseudoState:l,ariaVariation:d,theme:c,direction:i,breakpoint:s})}}return n}const us=W("ai-alt-text");async function ps(e,t,n={}){var h,f;if(!t.enabled||!t.enabledChecks.altText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=K(t);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${o.reason}`]};const s=o.client,i=Y(t.costCapUsd),c=[],r=[],d=Math.max(1,t.maxCandidatesPerCheck??10),l=e.slice(0,d),p=l.length;for(let a=0;a<l.length;a++){const g=l[a];if(!i.canCharge())break;try{if((h=n.signal)!=null&&h.aborted){r.push(`canceled after ${a}/${p} candidates`);break}const u=await s.judgeAltText({imageUrl:g.imageUrl,alt:g.alt,surroundingContext:g.surroundingContext,signal:n.signal});i.recordCharge(u.costUsd),(u.verdict==="fail"||u.verdict==="uncertain"&&u.confidence<.6)&&c.push(await hs(g,u))}catch(u){if(u instanceof Error&&(u.name==="AbortError"||u.name==="ExternalAbortError")){r.push(`canceled after ${a}/${p} candidates`);break}const O=u instanceof Error?u.message:String(u);r.push(`${g.selector}: ${O}`),us.warn("alt-text judgment failed",u)}(f=n.onProgress)==null||f.call(n,a+1,p)}return{findings:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:r}}async function hs(e,t){const n=t.verdict==="uncertain"?"ai-alt-uncertain":"ai-alt-misleading",o={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},s=await rt({ruleId:n,componentId:e.componentId,currentState:e.currentState,target:o}),i=t.verdict==="uncertain";return{ruleId:n,wcagCriterion:"1.1.1",wcagLevel:"A",impact:i?"minor":"serious",description:i?"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:o,componentId:e.componentId,currentState:e.currentState,axeVersion:e.axeVersion,detectedAt:new Date().toISOString(),matchKey:s,ai:{reasoning:t.reasoning,confidence:t.confidence,model:t.model,costUsd:t.costUsd},...i?{needsReview:!0}:{}}}const he=W("ai-text");function fe(e){if(e!=null&&e.aborted){const t=new Error("aborted");throw t.name="AbortError",t}}function ge(e){return e instanceof Error&&(e.name==="AbortError"||e.name==="ExternalAbortError"||e.message==="aborted")}async function fs(e,t,n,o={}){var f;if(!n.enabled||!n.enabledChecks.headings)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const s=K(n);if(!s.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${s.reason}`]};const i=s.client,c=Y(n.costCapUsd),r=[],d=[],l=Math.max(1,n.maxCandidatesPerCheck??10),p=e.slice(0,l),h=p.length;for(let a=0;a<p.length;a++){const g=p[a];if(!c.canCharge())break;try{fe(o.signal);const u=await i.judgeHeading({headingText:g.text,sectionContent:g.sectionContent,signal:o.signal});c.recordCharge(u.costUsd),u.verdict==="fail"&&u.confidence>=.6&&r.push(await me("ai-heading-not-descriptive","2.4.6","AA","moderate","AI judged this heading does not describe its section content.",g.selector,g.outerHTML,g.text,u,t))}catch(u){if(ge(u)){d.push(`canceled after ${a}/${h} candidates`);break}const w=u instanceof Error?u.message:String(u);d.push(`${g.selector}: ${w}`),he.warn("heading judgment failed",u)}(f=o.onProgress)==null||f.call(o,a+1,h)}return{findings:r,totalCostUsd:c.state.spentUsd,capExceeded:c.state.exceeded,errors:d}}async function gs(e,t,n,o={}){var f;if(!n.enabled||!n.enabledChecks.sensory)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const s=K(n);if(!s.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${s.reason}`]};const i=s.client,c=Y(n.costCapUsd),r=[],d=[],l=Math.max(1,n.maxCandidatesPerCheck??10),p=e.slice(0,l),h=p.length;for(let a=0;a<p.length;a++){const g=p[a];if(!c.canCharge())break;try{fe(o.signal);const u=await i.judgeSensoryLanguage({instructionText:g.text,signal:o.signal});c.recordCharge(u.costUsd),u.verdict==="fail"&&u.confidence>=.6&&r.push(await me("ai-sensory-instruction","1.3.3","A","serious","AI flagged this instruction as relying on sensory characteristics (shape, color, location) without a programmatic identifier.",g.selector,g.outerHTML,g.text.slice(0,100),u,t))}catch(u){if(ge(u)){d.push(`canceled after ${a}/${h} candidates`);break}const w=u instanceof Error?u.message:String(u);d.push(`${g.selector}: ${w}`),he.warn("sensory judgment failed",u)}(f=o.onProgress)==null||f.call(o,a+1,h)}return{findings:r,totalCostUsd:c.state.spentUsd,capExceeded:c.state.exceeded,errors:d}}async function ms(e,t,n,o={}){var f;if(!n.enabled||!n.enabledChecks.aria)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const s=K(n);if(!s.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${s.reason}`]};const i=s.client,c=Y(n.costCapUsd),r=[],d=[],l=Math.max(1,n.maxCandidatesPerCheck??10),p=e.slice(0,l),h=p.length;for(let a=0;a<p.length;a++){const g=p[a];if(!c.canCharge())break;try{fe(o.signal);const u=await i.judgeAriaSemantics({elementHtml:g.outerHTML,computedRole:g.role,surroundingHtml:g.surroundingHtml,signal:o.signal});c.recordCharge(u.costUsd),u.verdict==="fail"&&u.confidence>=.65&&r.push(await me("ai-aria-misuse","4.1.2","A","serious",`AI judged role="${g.role}" is being used inappropriately on this element.`,g.selector,g.outerHTML,g.role,u,t))}catch(u){if(ge(u)){d.push(`canceled after ${a}/${h} candidates`);break}const w=u instanceof Error?u.message:String(u);d.push(`${g.selector}: ${w}`),he.warn("aria judgment failed",u)}(f=o.onProgress)==null||f.call(o,a+1,h)}return{findings:r,totalCostUsd:c.state.spentUsd,capExceeded:c.state.exceeded,errors:d}}async function ys(e,t,n,o={}){var f;if(!n.enabled||!n.enabledChecks.genericLinkText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const s=K(n);if(!s.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${s.reason}`]};const i=s.client,c=Y(n.costCapUsd),r=[],d=[],l=Math.max(1,n.maxCandidatesPerCheck??10),p=e.slice(0,l),h=p.length;for(let a=0;a<p.length;a++){const g=p[a];if(!c.canCharge())break;try{fe(o.signal);const u=await i.judgeGenericLinkText({linkText:g.linkText,surroundingText:g.surroundingText,signal:o.signal});c.recordCharge(u.costUsd),u.verdict==="fail"&&u.confidence>=.6&&r.push(await me("ai-generic-link-text","2.4.4","A","serious","AI judged this link's purpose is not clear from its text or surrounding context.",g.selector,g.outerHTML,g.linkText,u,t))}catch(u){if(ge(u)){d.push(`canceled after ${a}/${h} candidates`);break}const w=u instanceof Error?u.message:String(u);d.push(`${g.selector}: ${w}`),he.warn("generic-link judgment failed",u)}(f=o.onProgress)==null||f.call(o,a+1,h)}return{findings:r,totalCostUsd:c.state.spentUsd,capExceeded:c.state.exceeded,errors:d}}async function bs(e,t,n,o={}){var f;if(!n.enabled||!n.enabledChecks.wallOfText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const s=K(n);if(!s.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${s.reason}`]};const i=s.client,c=Y(n.costCapUsd),r=[],d=[],l=Math.max(1,n.maxCandidatesPerCheck??10),p=e.slice(0,l),h=p.length;for(let a=0;a<p.length;a++){const g=p[a];if(!c.canCharge())break;try{fe(o.signal);const u=await i.judgeWallOfText({blockText:g.blockText,structuralChildrenCount:g.structuralChildrenCount,signal:o.signal});c.recordCharge(u.costUsd),u.verdict==="fail"&&u.confidence>=.6&&r.push(await me("ai-wall-of-text","3.1.5","AAA","moderate","AI flagged this block as a wall of text with insufficient structural breaks (subheadings, lists, paragraphs).",g.selector,g.outerHTML,g.blockText.slice(0,80),u,t))}catch(u){if(ge(u)){d.push(`canceled after ${a}/${h} candidates`);break}const w=u instanceof Error?u.message:String(u);d.push(`${g.selector}: ${w}`),he.warn("wall-of-text judgment failed",u)}(f=o.onProgress)==null||f.call(o,a+1,h)}return{findings:r,totalCostUsd:c.state.spentUsd,capExceeded:c.state.exceeded,errors:d}}async function ws(e,t,n,o={}){var f;if(!n.enabled||!n.enabledChecks.languageMismatch)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const s=K(n);if(!s.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${s.reason}`]};const i=s.client,c=Y(n.costCapUsd),r=[],d=[],l=Math.max(1,n.maxCandidatesPerCheck??10),p=e.slice(0,l),h=p.length;for(let a=0;a<p.length;a++){const g=p[a];if(!c.canCharge())break;try{fe(o.signal);const u=await i.judgeLanguageMismatch({declaredLang:g.declaredLang,bodyTextSample:g.bodyTextSample,signal:o.signal});c.recordCharge(u.costUsd),u.verdict==="fail"&&u.confidence>=.7&&r.push(await me("ai-language-mismatch","3.1.1","A","serious",`AI judged the page's content does not match the declared lang="${g.declaredLang||"(none)"}".`,g.selector,g.outerHTML,g.bodyTextSample.slice(0,80),u,t))}catch(u){if(ge(u)){d.push(`canceled after ${a}/${h} candidates`);break}const w=u instanceof Error?u.message:String(u);d.push(`${g.selector}: ${w}`),he.warn("language-mismatch judgment failed",u)}(f=o.onProgress)==null||f.call(o,a+1,h)}return{findings:r,totalCostUsd:c.state.spentUsd,capExceeded:c.state.exceeded,errors:d}}async function me(e,t,n,o,s,i,c,r,d,l){const p={selector:i,outerHTML:c.slice(0,500),failureSummary:`AI assessment: ${d.reasoning}`,tagName:"AI",role:null,accessibleName:null,textSnippet:r.slice(0,100)||null,attrId:null,attrTestid:null},h=await rt({ruleId:e,componentId:l.componentId,currentState:l.currentState,target:p});return{ruleId:e,wcagCriterion:t,wcagLevel:n,impact:o,description:s,helpUrl:"https://www.w3.org/WAI/WCAG21/Understanding/",target:p,componentId:l.componentId,currentState:l.currentState,axeVersion:l.axeVersion,detectedAt:new Date().toISOString(),matchKey:h,ai:{reasoning:d.reasoning,confidence:d.confidence,model:d.model,costUsd:d.costUsd}}}const hn=W("ai-vision");function fn(e){if(e!=null&&e.aborted){const t=new Error("aborted");throw t.name="AbortError",t}}function gn(e){return e instanceof Error&&(e.name==="AbortError"||e.name==="ExternalAbortError"||e.message==="aborted")}async function vs(e,t,n,o={}){var f;if(!n.enabled||!n.enabledChecks.imageOfText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const s=K(n);if(!s.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${s.reason}`]};const i=s.client,c=Y(n.costCapUsd),r=Math.max(1,n.imageOfTextMaxImages??5),d=[...e].sort((a,g)=>g.pixelArea-a.pixelArea).slice(0,r),l=d.length,p=[],h=[];for(let a=0;a<d.length;a++){const g=d[a];if(!c.canCharge())break;try{fn(o.signal);const u=await i.judgeImageOfText({imageUrl:g.imageUrl,accessibleName:g.accessibleName,signal:o.signal});c.recordCharge(u.costUsd),u.verdict==="fail"&&u.confidence>=.65&&p.push(await mn("ai-image-of-text","1.4.5","AA","moderate","AI judged this image bakes substantial text into the raster — should be presented as actual HTML text instead.",g.selector,g.outerHTML,g.accessibleName??"",u,t))}catch(u){if(gn(u)){h.push(`canceled after ${a}/${l} candidates`);break}const w=u instanceof Error?u.message:String(u);h.push(`${g.selector}: ${w}`),hn.warn("image-of-text judgment failed",u)}(f=o.onProgress)==null||f.call(o,a+1,l)}return{findings:p,totalCostUsd:c.state.spentUsd,capExceeded:c.state.exceeded,errors:h}}async function As(e,t,n,o={}){var f;if(!n.enabled||!n.enabledChecks.colorOnlyMeaning)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const s=K(n);if(!s.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[s.reason]};const i=s.client,c=Y(n.costCapUsd),r=[],d=[],l=Math.max(1,n.maxCandidatesPerCheck??10),p=e.slice(0,l),h=p.length;for(let a=0;a<p.length;a++){const g=p[a];if(!c.canCharge())break;try{fn(o.signal);const u=await i.judgeColorOnlyMeaning({elementHtml:g.elementHtml,contextDescription:g.contextDescription,signal:o.signal});c.recordCharge(u.costUsd),u.verdict==="fail"&&u.confidence>=.65&&r.push(await mn("ai-color-only-meaning","1.4.1","A","serious","AI judged this region conveys information only through color (no text label, icon glyph, or shape difference).",g.selector,g.outerHTML,g.contextDescription,u,t))}catch(u){if(gn(u)){d.push(`canceled after ${a}/${h} candidates`);break}const w=u instanceof Error?u.message:String(u);d.push(w),hn.warn("color-only judgment failed",u)}(f=o.onProgress)==null||f.call(o,a+1,h)}return{findings:r,totalCostUsd:c.state.spentUsd,capExceeded:c.state.exceeded,errors:d}}async function mn(e,t,n,o,s,i,c,r,d,l){const p={selector:i,outerHTML:c.slice(0,500),failureSummary:`AI assessment: ${d.reasoning}`,tagName:"AI",role:null,accessibleName:null,textSnippet:r.slice(0,100)||null,attrId:null,attrTestid:null},h=await rt({ruleId:e,componentId:l.componentId,currentState:l.currentState,target:p});return{ruleId:e,wcagCriterion:t,wcagLevel:n,impact:o,description:s,helpUrl:"https://www.w3.org/WAI/WCAG21/Understanding/",target:p,componentId:l.componentId,currentState:l.currentState,axeVersion:l.axeVersion,detectedAt:new Date().toISOString(),matchKey:h,ai:{reasoning:d.reasoning,confidence:d.confidence,model:d.model,costUsd:d.costUsd}}}const st=W("ensure-content-script"),Ss=1500,xs=300;async function yn(e,t){if(await _t(e,t))return{ok:!0};const n=chrome.runtime.getManifest(),o=[];for(const s of n.content_scripts??[])Array.isArray(s.js)&&o.push(...s.js);if(o.length===0)return st.error("manifest declares no content_scripts"),{ok:!1,reason:"inject-failed",detail:"no content_scripts declared in manifest"};try{const s=t!==void 0?{tabId:e,frameIds:[t]}:{tabId:e,allFrames:!0};await chrome.scripting.executeScript({target:s,files:o})}catch(s){return ks(s)}return await Es(xs),await _t(e,t)?(st.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 _t(e,t){try{const n=t!==void 0?{frameId:t}:void 0,o=await Promise.race([chrome.tabs.sendMessage(e,{type:"PING_REQUEST"},n),new Promise((s,i)=>setTimeout(()=>i(new Error("ping timeout")),Ss))]);return(o==null?void 0:o.type)==="PING_RESPONSE"}catch{return!1}}function ks(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}:(st.warn("content-script injection failed",e),{ok:!1,reason:"inject-failed",detail:t})}function Es(e){return new Promise(t=>setTimeout(t,e))}function bn(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 Je=W("forensic-anchor-client"),$s="https://api.wcagcheckr.com",Ts="wcagcheckr",Cs=15e3;function Is(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 Rs(e,t){const n={auditHash:e.hash,pageUrl:e.pageUrl,capturedAt:e.capturedAt,...t?{licenseId:t}:{}},o=`${$s}/v1/products/${Ts}/forensic/anchor`;try{const s=await fetch(o,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(n),signal:AbortSignal.timeout(Cs)});if(!s.ok)return Je.warn(`anchor HTTP ${s.status}; entry stays local-only`),null;const i=await s.json();return Is(i)?i:(Je.warn("anchor returned malformed receipt; entry stays local-only",i),null)}catch(s){return Je.warn("anchor request failed; entry stays local-only",s),null}}const Mt="4.11.4";async function Os(e){try{return(await chrome.tabs.get(e)).url??""}catch{return""}}const R=W("flows");async function We(){const e=await it();if(!e)throw new Error("no audit target tab found");return e}async function _s(){var n,o,s,i,c;const e=await _e("stateMatrix",Qe);return((n=e.pseudoStates)==null?void 0:n.length)>0&&((o=e.themes)==null?void 0:o.length)>0&&((s=e.directions)==null?void 0:s.length)>0&&((i=e.breakpoints)==null?void 0:i.length)>0&&((c=e.breakpointPresets)==null?void 0:c.length)>0?e:(R.warn("stored stateMatrix is invalid; using defaults"),Qe)}async function Ms(){const e=await _e("runsPerState",1);return typeof e!="number"||e<1||e>5?1:Math.floor(e)}async function Us(e,t,n){const o=t.themes.includes("light"),s=t.themes.includes("dark");if(!o||!s)return t;let i=null;try{i=await j(e,{type:"THEME_AWARENESS_REQUEST"},n)}catch{return t}return!i||i.hasUnreadableSheets?t:i.hasDarkModeCss&&!i.hasLightModeCss?(R.info("theme-axis prune: site is dark-only, skipping light pass"),{...t,themes:t.themes.filter(c=>c!=="light")}):i.hasLightModeCss&&!i.hasDarkModeCss?(R.info("theme-axis prune: site is light-only, skipping dark pass"),{...t,themes:t.themes.filter(c=>c!=="dark")}):t}function Ns(e){if(e.length<=1)return e[0]??[];const t=new Map;return e.forEach((n,o)=>{for(const s of n){const i=`${s.ruleId}::${s.target.selector}`,c=t.get(i);c?c.runIndices.add(o):t.set(i,{v:s,runIndices:new Set([o])})}}),Array.from(t.values()).map(({v:n,runIndices:o})=>({...n,flakyAcrossRuns:o.size<e.length}))}async function ke(e,t,n,o){var P;const s=await We(),i=await yn(s,n);if(!i.ok){N({type:"AUDIT_FAILED_EVENT",error:{code:i.reason==="restricted-url"?"RESTRICTED_URL":"CONTENT_SCRIPT_UNREACHABLE",message:bn(i),recoverable:i.reason!=="restricted-url"}});return}const c=o??await _s(),r=await Us(s,c,n),d=ds(r),l=await Ms(),p=await Wn(`${await Os(s)}|${e}|${n??0}`),h=await Vn(r),f=await _e("axeDisabledRules",[]),a=await jn(f);let g=null;try{g=(await j(s,{type:"SCOPE_FINGERPRINT_REQUEST",selector:e},n)).fingerprint}catch(b){R.debug("scope fingerprint failed; skipping cache fast-path",b)}if(g){const b=await zn(p);if(b&&b.fingerprint===g&&b.axeVersion===Mt&&b.matrixConfigHash===h&&b.disabledRulesHash===a){R.info(`audit-cache HIT for ${b.componentId} — skipping matrix iteration`),N({type:"AUDIT_COMPLETE_EVENT",componentId:b.componentId,results:b.results,delta:b.delta}),N({type:"SCORECARD_UPDATED_EVENT"});return}}await Ne({sessionId:Le(),mode:"single-element",scope:e,startedAt:new Date().toISOString(),lastProgressAt:new Date().toISOString(),state:"running"}),await se(s,{type:"LIVE_REGIONS_ARM_REQUEST"},n).catch(()=>{}),await se(s,{type:"FOCUS_TRACE_ARM_REQUEST"},n).catch(()=>{}),await j(s,{type:"WARMUP_REQUEST"},n).catch(()=>{});const u=[],w=[];let O="",S,E=!1,k=null;try{for(let b=0;b<d.length;b++){if(t.isCanceled()){R.info("single audit canceled by user");break}const _=d[b];try{const M=await et(s,_,e,n);if(!M.success){S=(P=M.error)==null?void 0:P.message,R.warn(`state ${b+1}/${d.length} drive failed`,S);continue}_.breakpoint.id!==k&&(await j(s,{type:"WARMUP_REQUEST"},n).catch(()=>{}),k=_.breakpoint.id);const x=[];let v=null;for(let C=0;C<l;C++){const A=await j(s,{type:"AUDIT_REQUEST",selector:e,currentState:_},n);A.success&&A.result?(x.push(A.result.violations),v=A.result):A.error&&(S=A.error.message,R.warn(`state ${b+1}/${d.length} run ${C+1} failed`,S))}if(v&&x.length>0){E=!0;const C=Ns(x),A=await la(s,{quality:75})??void 0;let T,$;try{T=(await j(s,{type:"LIVE_REGIONS_DRAIN_REQUEST"},n)).announcements}catch{}try{$=(await j(s,{type:"FOCUS_TRACE_DRAIN_REQUEST"},n)).focusEvents}catch{}w.push({...v,violations:C,frameId:n,announcements:T,focusEvents:$,screenshotDataUrl:A}),u.push(...C),O=v.componentId}}catch(M){S=M instanceof Error?M.message:String(M),R.warn(`state ${b+1}/${d.length} threw`,M)}N({type:"AUDIT_PROGRESS_EVENT",current:b+1,total:d.length,currentState:_,componentId:O||void 0}),await Pe({})}if(E&&O){const b={pseudoState:"default",ariaVariation:null,theme:"light",direction:"ltr",breakpoint:{id:"desktop",label:"Desktop",width:1280,height:800,deviceScaleFactor:1,mobile:!1}};try{await et(s,b,e,n),await new Promise(v=>setTimeout(v,200))}catch(v){R.debug("reference-state drive failed; analyzers will use last matrix state",v)}try{const v=await j(s,{type:"READING_ORDER_REQUEST",selector:e},n);v.issues.length>0&&w[0]&&(w[0].readingOrderIssues=v.issues,w[0].positionAnalysisCapturedAt=b)}catch(v){R.debug("reading-order analysis skipped",v)}try{const v=await j(s,{type:"TAB_ORDER_ANALYZE_REQUEST"},n);v.issues.length>0&&w[0]&&(w[0].tabOrderIssues=v.issues,w[0].positionAnalysisCapturedAt=b)}catch(v){R.debug("tab-order analysis skipped",v)}try{const v=await j(s,{type:"TYPOGRAPHY_ANALYZE_REQUEST",selector:e},n);v.issues.length>0&&w[0]&&(w[0].typographyIssues=v.issues)}catch(v){R.debug("typography analysis skipped",v)}try{const v=await j(s,{type:"CUSTOM_PROPERTY_REQUEST"},n);v.undefined.length>0&&w[0]&&(w[0].undefinedCustomProperties=v.undefined)}catch(v){R.debug("custom-property analysis skipped",v)}try{const v=await _e("aiConfig",{}),C=De(v);C.enabled&&C.apiKey&&await Qs(s,e,n,C,w,O,t)}catch(v){R.warn("ai augmentation skipped",v)}const _=w.flatMap(v=>v.announcements??[]),M=w.flatMap(v=>v.focusEvents??[]),x=await Zt(O,u,{announcements:_,focusEvents:M},c);try{const v=ie(u,void 0,w),C=w.reduce((T,$)=>T+$.durationMs,0),A=await Un(w,v,C);if(A){const T=await Vt();if(Fn(T,"forensicAnchoring"))try{const $=await Nn(),J=await Rs(A,$??void 0);J&&await Ln(A.componentId,A.capturedAt,J)}catch($){R.warn("forensic anchoring failed (entry stays local-only)",$)}}}catch(v){R.warn("forensic log recording failed",v)}if(g)try{await Hn({lookupKey:p,fingerprint:g,axeVersion:Mt,matrixConfigHash:h,disabledRulesHash:a,results:w,delta:x,componentId:O,cachedAt:Date.now()})}catch(v){R.warn("audit-cache write failed",v)}N({type:"AUDIT_COMPLETE_EVENT",componentId:O,results:w,delta:x})}else N({type:"AUDIT_FAILED_EVENT",error:{code:"AXE_FAILED",message:S?`All audits failed. Last error: ${S}`:"No state produced results. Selector may not exist in the audit target frame.",recoverable:!0}});await Ae()}catch(b){R.error("single audit failed",b),N({type:"AUDIT_FAILED_EVENT",componentId:O||void 0,error:Cn(b)?b.payload:{code:"UNKNOWN",message:String(b),recoverable:!1}})}finally{await se(s,{type:"LIVE_REGIONS_DISARM_REQUEST"},n).catch(()=>{}),await se(s,{type:"FOCUS_TRACE_DISARM_REQUEST"},n).catch(()=>{}),await Yt(s,e,n)}}async function Ls(e){var t;try{const o=(await chrome.scripting.executeScript({target:{tabId:e,allFrames:!0},world:"MAIN",func:()=>{var i;const s=window;return{detected:typeof s.__STORYBOOK_PREVIEW__=="object",version:(i=s.__STORYBOOK_PREVIEW__)==null?void 0:i.version}}})).find(s=>{var i;return(i=s.result)==null?void 0:i.detected});if(o)return{detected:!0,version:(t=o.result)==null?void 0:t.version,frameId:o.frameId}}catch(n){R.warn("storybook detection scripting failed",n)}return{detected:!1}}async function Ps(e){const t=await We(),n=await Hs(t);if(await chrome.storage.local.set({"storybook:lastDetected":{detected:n.kind==="storybook"&&n.detected,version:n.version,detectedAt:new Date().toISOString()},"playground:lastDetected":{kind:n.kind,detected:n.detected,version:n.version,detectedAt:new Date().toISOString()}}),!n.detected){N({type:"AUDIT_FAILED_EVENT",error:{code:"STORYBOOK_NOT_DETECTED",message:"No supported component playground detected on this tab. Open a Storybook (iframe.html / storybook-static), Ladle, or Bit page and try again.",recoverable:!0}});return}if(n.kind==="bit"){N({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 o=n.frameId??0,s=n.kind==="storybook"?await Fs(t,o):await Vs(t,o);if(!s.length){R.warn(`${n.kind} detected but no stories returned`),N({type:"AUDIT_FAILED_EVENT",error:{code:"STORYBOOK_NOT_DETECTED",message:`${n.kind==="ladle"?"Ladle":"Storybook"} detected but no stories were returned. Try reloading the page.`,recoverable:!0}});return}await Ne({sessionId:Le(),mode:"storybook-all",totalStories:s.length,completedStories:[],startedAt:new Date().toISOString(),lastProgressAt:new Date().toISOString(),state:"running"});for(const i of s){if(e.isCanceled()){R.info(`${n.kind} iteration canceled by user`);break}try{if(n.kind==="storybook"){await Gs(t,o,i.id);const r=await se(t,{type:"STORYBOOK_GET_STORY_TARGET_REQUEST",tabId:t},o);await ke(r.selector,e,o)}else await js(t,o,i.id),await ke("#ladle-root",e,o)}catch(r){R.warn(`story ${i.id} failed; continuing with next`,r)}const c=await jt();if((c==null?void 0:c.state)==="interrupted"){R.info("storybook iteration interrupted");break}await Pe({completedStories:[...(c==null?void 0:c.completedStories)??[],i.id]})}N({type:"SCORECARD_UPDATED_EVENT"}),await Ae()}async function Ds(e){const t=await We();let n=[];try{n=(await chrome.scripting.executeScript({target:{tabId:t,allFrames:!0},func:()=>({url:window.location.href})})).map(s=>{var i;return{frameId:s.frameId,url:((i=s.result)==null?void 0:i.url)??""}}).filter(s=>/^https?:/i.test(s.url))}catch(o){R.warn("multi-frame discovery failed",o)}if(n.length===0){N({type:"AUDIT_FAILED_EVENT",error:{code:"CONTENT_SCRIPT_UNREACHABLE",message:"No auditable frames found on this tab. The page may use only chrome-extension:// or data: frames.",recoverable:!0}});return}await Ne({sessionId:Le(),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()){R.info("all-frames iteration canceled");break}try{await ke("html",e,o.frameId)}catch(i){R.warn(`frame ${o.frameId} (${o.url.slice(0,80)}) audit failed; continuing`,i)}const s=await jt();if((s==null?void 0:s.state)==="interrupted"){R.info("all-frames iteration interrupted");break}await Pe({completedFrames:[...(s==null?void 0:s.completedFrames)??[],o.frameId]})}N({type:"SCORECARD_UPDATED_EVENT"}),await Ae()}async function Fs(e,t){var n;try{return((n=(await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",func:async()=>{var d,l;const s=window.__STORYBOOK_PREVIEW__;if(!s)return[];const i=s.storyStoreValue??s.storyStore;if(!i)return[];typeof i.cacheAllCSFFiles=="function"&&await i.cacheAllCSFFiles();const c=((d=i.extract)==null?void 0:d.call(i))??((l=i.cachedCSFFiles)==null?void 0:l.call(i));if(!c)return[];const r=[];if(c instanceof Map)for(const[p,h]of c){const f=h;r.push({id:String(p),name:f.name??String(p),kind:f.title??f.kind??""})}else if(typeof c=="object"&&c!==null)for(const[p,h]of Object.entries(c)){const f=h;r.push({id:p,name:f.name??p,kind:f.title??f.kind??""})}return r}}))[0])==null?void 0:n.result)??[]}catch(o){return R.warn("listStoriesViaMainWorld failed",o),[]}}async function Gs(e,t,n){try{await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",args:[n,3e3],func:async(o,s)=>{const i=new URL(window.location.href);i.searchParams.set("id",o),i.searchParams.set("viewMode","story"),window.location.href!==i.href&&(window.history.pushState({},"",i.href),window.dispatchEvent(new PopStateEvent("popstate"))),await new Promise(c=>{const r=setTimeout(()=>c(),s),d=()=>{clearTimeout(r),c()};window.addEventListener("storyrendered",d,{once:!0})})}})}catch(o){R.warn(`navigateToStoryViaMainWorld(${n}) failed`,o)}}async function Ws(e){var t;try{const o=(await chrome.scripting.executeScript({target:{tabId:e,allFrames:!0},world:"MAIN",func:()=>{const s=document.getElementById("ladle-root")!==null,i=document.title==="Ladle",c=!!document.querySelector('link[href*="ladle.css"]');return{detected:s&&(i||c),version:void 0}}})).find(s=>{var i;return(i=s.result)==null?void 0:i.detected});if(o)return{detected:!0,version:(t=o.result)==null?void 0:t.version,frameId:o.frameId}}catch(n){R.warn("ladle detection scripting failed",n)}return{detected:!1}}async function Vs(e,t){var n;try{return((n=(await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",func:async()=>{try{const s=await fetch(new URL("/meta.json",window.location.href).href,{credentials:"same-origin"});if(!s.ok)return[];const c=(await s.json()).stories??{};return Object.entries(c).map(([r,d])=>({id:r,name:d.name??r,kind:(d.levels??[]).join(" / ")}))}catch{return[]}}}))[0])==null?void 0:n.result)??[]}catch(o){return R.warn("listLadleStoriesViaMainWorld failed",o),[]}}async function js(e,t,n){try{await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",args:[n,3e3],func:async(o,s)=>{const i=new URL(window.location.href);i.searchParams.set("story",o),window.location.href!==i.href&&(window.history.pushState({},"",i.href),window.dispatchEvent(new PopStateEvent("popstate"))),await new Promise(c=>setTimeout(c,Math.min(s,500)))}})}catch(o){R.warn(`navigateToLadleStoryViaMainWorld(${n}) failed`,o)}}async function zs(e){try{return(await chrome.scripting.executeScript({target:{tabId:e,allFrames:!0},world:"MAIN",func:()=>{const n=window.location.href;return/bit\.dev|bit-dev|bitsrc|\/~aspect|\/composition/i.test(n)}})).some(n=>n.result===!0)}catch{return!1}}async function Hs(e){const t=await Ls(e);if(t.detected)return{kind:"storybook",...t};const n=await Ws(e);return n.detected?{kind:"ladle",...n}:await zs(e)?{kind:"bit",detected:!0}:{kind:null,detected:!1}}const Bs=200,qs=6e4;function Ut(e,t){try{return new URL(e).origin===new URL(t).origin}catch{return!1}}function Ks(e,t){try{const n=new URL(e,t);return n.hash="",n.toString()}catch{return null}}async function Ys(e,t,n){await chrome.tabs.update(e,{url:t}),await new Promise((o,s)=>{const i=setTimeout(()=>{chrome.tabs.onUpdated.removeListener(c),s(new Error(`navigation timeout (${n}ms): ${t}`))},n);function c(r,d){r===e&&d.status==="complete"&&(clearTimeout(i),chrome.tabs.onUpdated.removeListener(c),o())}chrome.tabs.onUpdated.addListener(c)}),await new Promise(o=>setTimeout(o,400))}async function Js(e){var t;try{return((t=(await chrome.scripting.executeScript({target:{tabId:e},func:()=>Array.from(document.querySelectorAll("a[href]")).map(o=>o.getAttribute("href")).filter(o=>typeof o=="string"&&!o.startsWith("javascript:"))}))[0])==null?void 0:t.result)??[]}catch(n){return R.debug("link discovery failed",n),[]}}async function Xs(e,t,n){var f;const o=await We(),s=Math.min(t.maxPages??25,Bs),i=t.includeRegex?Nt(t.includeRegex):null,c=t.excludeRegex?Nt(t.excludeRegex):null,r=new Date().toISOString(),d=Date.now(),l=[e],p=new Set,h=[];await Ne({sessionId:Le(),mode:"storybook-all",scope:e,startedAt:r,lastProgressAt:new Date().toISOString(),state:"running"});try{for(;l.length>0&&p.size<s;){if(n.isCanceled()){R.info("site crawl canceled by user");break}const w=l.shift();if(!w||p.has(w)||!Ut(w,e)||i&&!i.test(w)||c&&c.test(w))continue;p.add(w);const O=p.size,S=Date.now();N({type:"SITE_CRAWL_PROGRESS_EVENT",current:O,total:Math.min(s,p.size+l.length),url:w,status:"auditing"});try{await Ys(o,w,qs);const E=await yn(o,0);if(!E.ok)throw new Error(bn(E));await j(o,{type:"WARMUP_REQUEST"},0).catch(()=>{});const k=await j(o,{type:"AUDIT_REQUEST",selector:"html",currentState:{pseudoState:"default",ariaVariation:null,theme:"light",direction:"ltr",breakpoint:{id:"desktop",label:"Desktop",width:1280,height:800,deviceScaleFactor:1,mobile:!1}}},0),P=Date.now()-S;if(k.success&&k.result)h.push({url:w,results:[k.result],delta:null,componentId:k.result.componentId,durationMs:P}),N({type:"SITE_CRAWL_PROGRESS_EVENT",current:O,total:Math.min(s,p.size+l.length),url:w,status:"completed",violations:k.result.violations.length});else{const _=((f=k.error)==null?void 0:f.message)??"audit returned no result";h.push({url:w,results:[],delta:null,componentId:w,durationMs:P,error:_}),N({type:"SITE_CRAWL_PROGRESS_EVENT",current:O,total:Math.min(s,p.size+l.length),url:w,status:"failed",error:_})}const b=await Js(o);for(const _ of b){const M=Ks(_,w);M&&Ut(M,e)&&!p.has(M)&&!l.includes(M)&&l.push(M)}}catch(E){const k=Date.now()-S,P=E instanceof Error?E.message:String(E);h.push({url:w,results:[],delta:null,componentId:w,durationMs:k,error:P}),N({type:"SITE_CRAWL_PROGRESS_EVENT",current:O,total:Math.min(s,p.size+l.length),url:w,status:"failed",error:P})}await Pe({})}const a=new Date().toISOString(),g=Date.now()-d,u=ls(e,h,r,a,g);N({type:"SITE_CRAWL_COMPLETE_EVENT",report:u}),await Ae()}catch(a){R.error("site crawl failed",a),N({type:"SITE_CRAWL_FAILED_EVENT",error:{code:"UNKNOWN",message:String(a),recoverable:!1}}),await Ae()}}function Nt(e){try{return new RegExp(e)}catch{return null}}const Xe=5*6e4,Lt=9e4,Pt={altText:"Verifying alt text…",headings:"Judging heading quality…",sensory:"Scanning for sensory-only instructions…",aria:"Reviewing ARIA semantics…",imageOfText:"Detecting baked-in text in images…",colorOnlyMeaning:"Checking for color-only meaning…",genericLinkText:"Reviewing link clarity…",wallOfText:"Detecting walls of text…",languageMismatch:"Verifying page language…"};async function Qs(e,t,n,o,s,i,c){let r;try{r=await j(e,{type:"AI_CANDIDATES_REQUEST",selector:t},n)}catch(A){R.debug("ai candidates fetch failed",A);return}const d=s[0];if(!d)return;const l={componentId:i,currentState:d.state,axeVersion:d.axeVersion},p={at:new Date().toISOString(),componentId:i,pageUrl:d.pageUrl??t,enabledChecks:o.enabledChecks,candidateCounts:{images:r.images.length,headings:r.headings.length,instructions:r.instructions.length,ariaElements:r.ariaElements.length,links:r.links.length,longTextBlocks:r.longTextBlocks.length,colorOnlyRegions:r.colorOnlyRegions.length,languageInfo:r.languageInfo?"present":"null"}};R.info("ai augmentation gate snapshot",JSON.stringify(p));try{await chrome.storage.local.set({aiDiagnosticLatest:p})}catch(A){R.warn("failed to persist ai diagnostic to storage.local",A)}try{const A=JSON.stringify(p,null,2),T=`data:application/json;charset=utf-8,${encodeURIComponent(A)}`,$=p.at.replace(/[:.]/g,"-"),J=await chrome.downloads.download({url:T,filename:`wcagcheckr-ai-diag-${$}.json`,saveAs:!1,conflictAction:"uniquify"});R.info(`ai diagnostic download started (id=${J})`)}catch(A){R.warn("failed to write ai diagnostic to Downloads (storage.local copy is still available under aiDiagnosticLatest)",A)}const h=[];if(o.enabledChecks.altText&&h.push({key:"altText",run:A=>{const T=r.images.map($=>({imageUrl:$.imageUrl,alt:$.alt,selector:$.selector,outerHTML:$.outerHTML,surroundingContext:$.surroundingContext,...l}));return ps(T,o,A)}}),o.enabledChecks.headings&&h.push({key:"headings",run:A=>fs(r.headings,l,o,A)}),o.enabledChecks.sensory&&h.push({key:"sensory",run:A=>gs(r.instructions,l,o,A)}),o.enabledChecks.aria&&h.push({key:"aria",run:A=>ms(r.ariaElements,l,o,A)}),o.enabledChecks.imageOfText&&h.push({key:"imageOfText",run:A=>{const T=r.images.map($=>({imageUrl:$.imageUrl,accessibleName:$.alt||void 0,selector:$.selector,outerHTML:$.outerHTML,pixelArea:$.pixelArea}));return vs(T,l,o,A)}}),o.enabledChecks.colorOnlyMeaning&&h.push({key:"colorOnlyMeaning",run:A=>As(r.colorOnlyRegions,l,o,A)}),o.enabledChecks.genericLinkText&&h.push({key:"genericLinkText",run:A=>ys(r.links,l,o,A)}),o.enabledChecks.wallOfText&&h.push({key:"wallOfText",run:A=>bs(r.longTextBlocks,l,o,A)}),o.enabledChecks.languageMismatch){const A=r.languageInfo;h.push({key:"languageMismatch",run:T=>ws(A?[A]:[],l,o,T)})}if(h.length===0)return;let f=0,a=0,g=!1;const u={};let w=0,O=0,S=0;const E=[];let k=!1,P=!1;function b(A,T){if(u[A]=(u[A]??0)+T.totalCostUsd,f+=T.totalCostUsd,a+=T.findings.length,g=g||T.capExceeded,w++,T.errors.length>0){S++;for(const $ of T.errors)E.includes($)||E.push($)}else O++;R.info(`ai ${A}: ${T.findings.length} findings, $${T.totalCostUsd.toFixed(4)}, errs=${T.errors.length}`),T.capExceeded&&R.warn(`ai cost cap exceeded after $${T.totalCostUsd.toFixed(4)} during ${A}`)}const _=(async()=>{for(let A=0;A<h.length;A++){if(c!=null&&c.isCanceled()){P=!0;break}const T=h[A];N({type:"AI_AUGMENTATION_PROGRESS_EVENT",componentId:i,currentCheck:T.key,currentCheckLabel:Pt[T.key],current:A+1,total:h.length});const $=new AbortController,J=setTimeout(()=>$.abort(),Lt),ye=c?setInterval(()=>{c.isCanceled()&&$.abort()},500):null;let Q;try{Q=await T.run({signal:$.signal,onProgress:(ne,m)=>{m>0&&N({type:"AI_AUGMENTATION_PROGRESS_EVENT",componentId:i,currentCheck:T.key,currentCheckLabel:Pt[T.key],current:A+1,total:h.length,candidatesDone:ne,candidatesTotal:m})}}),$.signal.aborted&&(c!=null&&c.isCanceled())?Q.errors=[...Q.errors,`${T.key} canceled by user`]:$.signal.aborted}catch(ne){const m=ne instanceof Error?ne.message:String(ne),L=$.signal.aborted,I=L&&(c!=null&&c.isCanceled())?`${T.key} canceled by user`:L?`${T.key} hit per-check ${Lt/1e3}s cap — analyzer aborted`:`${T.key} threw: ${m}`;Q={findings:[],totalCostUsd:0,capExceeded:!1,errors:[I]}}finally{clearTimeout(J),ye&&clearInterval(ye)}Q.findings.length>0&&(d.violations=[...d.violations,...Q.findings]),b(T.key,Q)}})(),M=new Promise(A=>{setTimeout(()=>A("timeout"),Xe)});await Promise.race([_.then(()=>"done"),M])==="timeout"&&(k=!0,R.warn(`ai augmentation hit global ${Xe/1e3}s timeout after ${w}/${h.length} checks`));let v;if(S>0||k||P){k?v=`AI augmentation timed out after ${Xe/1e3}s`:P?v="AI augmentation canceled":v=E[0]??"AI analyzer error";const A=O===0?"total":"partial";N({type:"AI_AUGMENTATION_FAILED_EVENT",componentId:i,severity:A,reason:v,checksAttempted:w,checksSucceeded:O,checksErrored:S,errorDetails:k||P?[v,...E.slice(0,4)]:E.slice(0,5)}),R.warn(`ai augmentation ${A} failure: ${S}/${w} checks errored — ${v}`)}(f>0||a>0||v)&&await Bn({at:new Date().toISOString(),pageUrl:d.pageUrl??t,componentId:i,totalCostUsd:f,byCheck:u,findingsCount:a,capExceeded:g,failureReason:v})}const H=W("service-worker");kn("service-worker");Wa();var Te,Dt;(Dt=(Te=chrome.sidePanel)==null?void 0:Te.setPanelBehavior)==null||Dt.call(Te,{openPanelOnActionClick:!0}).catch(e=>H.warn("setPanelBehavior failed",e));da();ka();Pn();Sa().catch(e=>H.warn("initial cloud pull failed",e));(async()=>(await Da(),tn()))();Fa();za();zo();Zo();ns();Ao();ss();self.addEventListener("unhandledrejection",e=>{const t=e.reason;En(t instanceof Error?t:new Error(String(t)))});let Ue=!1;chrome.runtime.onConnect.addListener(e=>{if(e.name==="audit-keepalive"){H.debug("keepalive connected"),e.onDisconnect.addListener(async()=>{H.debug("keepalive disconnected"),await Dn()});return}if(e.name==="sidepanel-tracker"){Ue=!0,H.debug("chrome.sidePanel opened"),e.onDisconnect.addListener(async()=>{Ue=!1,H.debug("chrome.sidePanel closed — restoring in-page overlay"),await ht()});return}});let pt=!1;const $e={isCanceled:()=>pt};async function wn(){const e=await it();if(e)try{await se(e,{type:"SIDEBAR_HIDE_REQUEST"})}catch(t){H.warn("sidebar hide failed",t)}}async function ht(){const e=await it();if(e)try{await se(e,{type:"SIDEBAR_SHOW_REQUEST"})}catch(t){H.warn("sidebar show failed",t)}}D("START_AUDIT",async e=>{pt=!1,await wn();let t;if(e.mode==="single-element"){if(!e.scope)throw new Error("START_AUDIT single-element requires scope");t=ke(e.scope,$e,e.frameId,e.matrixOverride)}else e.mode==="full-page"?t=ke("html",$e,0,e.matrixOverride):e.mode==="all-frames"?t=Ds($e):t=Ps($e);t.catch(n=>{H.error("audit failed",n);const o=n instanceof Error?n.message:String(n),s=/no audit target tab/i.test(o);N({type:"AUDIT_FAILED_EVENT",error:{code:s?"RESTRICTED_URL":"UNKNOWN",message:s?"No audit target tab found. Open a regular http(s) page (not chrome:// or about:blank) and try again.":`Audit failed: ${o}`,recoverable:!0}})}).finally(()=>{Ue||ht()})});D("CANCEL_AUDIT",async()=>{pt=!0,H.info("cancel requested")});let ft=!1;const Zs={isCanceled:()=>ft};D("START_SITE_CRAWL",async e=>{ft=!1,await wn(),Xs(e.startUrl,{maxPages:e.maxPages,includeRegex:e.includeRegex,excludeRegex:e.excludeRegex},Zs).catch(t=>H.error("site crawl failed",t)).finally(()=>{Ue||ht()})});D("CANCEL_SITE_CRAWL",async()=>{ft=!0,H.info("site crawl cancel requested")});D("OPEN_SETTINGS",()=>{chrome.runtime.openOptionsPage()});H.info("service worker ready");
@@ -2,8 +2,8 @@
2
2
  "manifest_version": 3,
3
3
  "name": "wcagcheckr",
4
4
  "description": "Audit components across hover, focus, dark mode, forced colors, RTL — every state your users actually encounter. Per-component baselines surface only NEW violations.",
5
- "version": "1.0.0.68",
6
- "version_name": "1.0.0-rc.68",
5
+ "version": "1.0.0.69",
6
+ "version_name": "1.0.0-rc.69",
7
7
  "author": "Locustware",
8
8
  "homepage_url": "https://wcagcheckr.com",
9
9
  "icons": {
@@ -1 +1 @@
1
- import './assets/service-worker.ts-4rGODblX.js';
1
+ import './assets/service-worker.ts-DtDbkZBg.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wcag-checkr/ci",
3
- "version": "1.0.0-rc.68",
3
+ "version": "1.0.0-rc.69",
4
4
  "private": false,
5
5
  "description": "Headless wcagcheckr accessibility audit runner for CI/CD pipelines. Drives the wcagcheckr Chrome extension via Playwright, runs full-page audits across the state matrix (108 combinations: hover, focus, dark mode, RTL, breakpoints), outputs JSON / SARIF / JUnit, exits with severity-aware codes.",
6
6
  "license": "UNLICENSED",