testaro 78.0.8 → 78.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (356) hide show
  1. package/.gitattributes +94 -0
  2. package/.github/workflows/ci.yml +58 -0
  3. package/.github/workflows/publish.yml +49 -0
  4. package/.github/workflows/typescript.yml +35 -0
  5. package/AGENTS.md +2 -2
  6. package/CLAUDE.md +24 -18
  7. package/CONTAINERS.md +9 -6
  8. package/CONTRIBUTING.md +29 -3
  9. package/Dockerfile +1 -1
  10. package/README.md +73 -19
  11. package/UPGRADES.md +4 -0
  12. package/actSpecs-doc.md +13 -1
  13. package/actSpecs.js +29 -5
  14. package/call.js +6 -5
  15. package/docker-compose.yml +8 -2
  16. package/docs/checkpoint-scanning.md +199 -0
  17. package/docs/standard-result-outcome.md +165 -0
  18. package/env.example +44 -10
  19. package/eslint.config.mjs +101 -0
  20. package/netWatch.js +372 -222
  21. package/package.json +46 -31
  22. package/pour/README.md +47 -0
  23. package/pour/pour.min.js +8 -0
  24. package/procs/actDo.js +808 -0
  25. package/procs/catalog.d.ts +17 -0
  26. package/procs/catalog.js +312 -221
  27. package/procs/catalog.ts +398 -0
  28. package/procs/checkpoint.js +110 -0
  29. package/procs/config.d.ts +35 -0
  30. package/procs/config.js +62 -0
  31. package/procs/dateTime.js +2 -1
  32. package/procs/doActs.js +442 -849
  33. package/procs/doTestAct.js +27 -121
  34. package/procs/flow.js +221 -0
  35. package/procs/generateRuleRegistry.js +94 -0
  36. package/procs/getSource.d.ts +26 -0
  37. package/procs/job.js +115 -1
  38. package/procs/launch.d.ts +40 -0
  39. package/procs/launch.js +239 -108
  40. package/procs/nu.d.ts +48 -0
  41. package/procs/scope.js +196 -0
  42. package/procs/shoot.d.ts +33 -0
  43. package/procs/standard.d.ts +18 -0
  44. package/procs/standard.js +83 -0
  45. package/procs/standard.ts +126 -0
  46. package/procs/testAct.js +145 -0
  47. package/procs/testaro.d.ts +20 -0
  48. package/procs/testaro.js +236 -189
  49. package/procs/testaro.ts +321 -0
  50. package/procs/userPath.js +126 -0
  51. package/procs/xPath.d.ts +4 -0
  52. package/procs/xPath.js +81 -69
  53. package/procs/xPath.ts +106 -0
  54. package/procs/xPathScript.d.ts +6 -0
  55. package/procs/xPathScript.js +70 -0
  56. package/run.js +3 -2
  57. package/surea11y/README.md +44 -0
  58. package/surea11y/surea11y.browser.js +14 -0
  59. package/testaro/adbID.d.ts +3 -0
  60. package/testaro/adbID.js +38 -41
  61. package/testaro/adbID.ts +66 -0
  62. package/testaro/allCapStyle.d.ts +3 -0
  63. package/testaro/allCapStyle.js +31 -34
  64. package/testaro/allCapStyle.ts +55 -0
  65. package/testaro/allCaps.d.ts +16 -0
  66. package/testaro/allCaps.js +179 -151
  67. package/testaro/allCaps.ts +228 -0
  68. package/testaro/allHidden.d.ts +13 -0
  69. package/testaro/allHidden.js +29 -30
  70. package/testaro/allHidden.ts +50 -0
  71. package/testaro/allSlanted.d.ts +3 -0
  72. package/testaro/allSlanted.js +30 -33
  73. package/testaro/allSlanted.ts +54 -0
  74. package/testaro/altScheme.d.ts +3 -0
  75. package/testaro/altScheme.js +27 -30
  76. package/testaro/altScheme.ts +50 -0
  77. package/testaro/attVal.d.ts +3 -0
  78. package/testaro/attVal.js +20 -21
  79. package/testaro/attVal.ts +52 -0
  80. package/testaro/autocomplete.d.ts +3 -0
  81. package/testaro/autocomplete.js +59 -75
  82. package/testaro/autocomplete.ts +101 -0
  83. package/testaro/bulk.d.ts +13 -0
  84. package/testaro/bulk.js +32 -33
  85. package/testaro/bulk.ts +55 -0
  86. package/testaro/buttonMenu.d.ts +9 -0
  87. package/testaro/buttonMenu.js +317 -319
  88. package/testaro/buttonMenu.ts +391 -0
  89. package/testaro/captionLoc.d.ts +3 -0
  90. package/testaro/captionLoc.js +17 -20
  91. package/testaro/captionLoc.ts +40 -0
  92. package/testaro/datalistRef.d.ts +3 -0
  93. package/testaro/datalistRef.js +33 -36
  94. package/testaro/datalistRef.ts +55 -0
  95. package/testaro/distortion.d.ts +3 -0
  96. package/testaro/distortion.js +57 -26
  97. package/testaro/distortion.ts +81 -0
  98. package/testaro/docType.d.ts +15 -0
  99. package/testaro/docType.js +25 -25
  100. package/testaro/docType.ts +45 -0
  101. package/testaro/dupAtt.d.ts +16 -0
  102. package/testaro/dupAtt.js +113 -104
  103. package/testaro/dupAtt.ts +144 -0
  104. package/testaro/elements.d.ts +6 -0
  105. package/testaro/elements.js +153 -153
  106. package/testaro/elements.ts +215 -0
  107. package/testaro/embAc.d.ts +3 -0
  108. package/testaro/embAc.js +19 -20
  109. package/testaro/embAc.ts +40 -0
  110. package/testaro/focAll.d.ts +13 -0
  111. package/testaro/focAll.js +185 -191
  112. package/testaro/focAll.ts +217 -0
  113. package/testaro/focAndOp.d.ts +3 -0
  114. package/testaro/focAndOp.js +100 -104
  115. package/testaro/focAndOp.ts +128 -0
  116. package/testaro/focInd.d.ts +3 -0
  117. package/testaro/focInd.js +64 -66
  118. package/testaro/focInd.ts +96 -0
  119. package/testaro/focVis.d.ts +3 -0
  120. package/testaro/focVis.js +28 -30
  121. package/testaro/focVis.ts +52 -0
  122. package/testaro/headEl.d.ts +10 -0
  123. package/testaro/headEl.js +61 -62
  124. package/testaro/headEl.ts +82 -0
  125. package/testaro/headingAmb.d.ts +3 -0
  126. package/testaro/headingAmb.js +54 -63
  127. package/testaro/headingAmb.ts +72 -0
  128. package/testaro/hovInd.d.ts +12 -0
  129. package/testaro/hovInd.js +163 -130
  130. package/testaro/hovInd.ts +199 -0
  131. package/testaro/hover.d.ts +3 -0
  132. package/testaro/hover.js +155 -126
  133. package/testaro/hover.ts +154 -0
  134. package/testaro/hr.d.ts +3 -0
  135. package/testaro/hr.js +14 -17
  136. package/testaro/hr.ts +36 -0
  137. package/testaro/imageLink.d.ts +3 -0
  138. package/testaro/imageLink.js +17 -20
  139. package/testaro/imageLink.ts +42 -0
  140. package/testaro/labClash.d.ts +3 -0
  141. package/testaro/labClash.js +31 -33
  142. package/testaro/labClash.ts +54 -0
  143. package/testaro/legendLoc.d.ts +3 -0
  144. package/testaro/legendLoc.js +17 -20
  145. package/testaro/legendLoc.ts +42 -0
  146. package/testaro/lineHeight.d.ts +3 -0
  147. package/testaro/lineHeight.js +43 -48
  148. package/testaro/lineHeight.ts +70 -0
  149. package/testaro/linkAmb.d.ts +7 -0
  150. package/testaro/linkAmb.js +79 -80
  151. package/testaro/linkAmb.ts +105 -0
  152. package/testaro/linkExt.d.ts +3 -0
  153. package/testaro/linkExt.js +13 -16
  154. package/testaro/linkExt.ts +35 -0
  155. package/testaro/linkOldAtt.d.ts +3 -0
  156. package/testaro/linkOldAtt.js +25 -28
  157. package/testaro/linkOldAtt.ts +48 -0
  158. package/testaro/linkTo.d.ts +3 -0
  159. package/testaro/linkTo.js +22 -22
  160. package/testaro/linkTo.ts +43 -0
  161. package/testaro/linkUl.d.ts +3 -0
  162. package/testaro/linkUl.js +30 -34
  163. package/testaro/linkUl.ts +54 -0
  164. package/testaro/miniText.d.ts +3 -0
  165. package/testaro/miniText.js +41 -44
  166. package/testaro/miniText.ts +68 -0
  167. package/testaro/motion.d.ts +10 -0
  168. package/testaro/motion.js +92 -96
  169. package/testaro/motion.ts +125 -0
  170. package/testaro/nonTable.d.ts +3 -0
  171. package/testaro/nonTable.js +39 -45
  172. package/testaro/nonTable.ts +66 -0
  173. package/testaro/optRoleSel.d.ts +3 -0
  174. package/testaro/optRoleSel.js +16 -19
  175. package/testaro/optRoleSel.ts +41 -0
  176. package/testaro/phOnly.d.ts +3 -0
  177. package/testaro/phOnly.js +18 -21
  178. package/testaro/phOnly.ts +43 -0
  179. package/testaro/pseudoP.d.ts +3 -0
  180. package/testaro/pseudoP.js +36 -38
  181. package/testaro/pseudoP.ts +59 -0
  182. package/testaro/radioSet.d.ts +3 -0
  183. package/testaro/radioSet.js +57 -59
  184. package/testaro/radioSet.ts +79 -0
  185. package/testaro/registry.d.ts +63 -0
  186. package/testaro/registry.js +67 -0
  187. package/testaro/registry.ts +141 -0
  188. package/testaro/role.d.ts +3 -0
  189. package/testaro/role.js +27 -29
  190. package/testaro/role.ts +53 -0
  191. package/testaro/secHeading.d.ts +3 -0
  192. package/testaro/secHeading.js +30 -33
  193. package/testaro/secHeading.ts +53 -0
  194. package/testaro/styleDiff.d.ts +25 -0
  195. package/testaro/styleDiff.js +248 -252
  196. package/testaro/styleDiff.ts +303 -0
  197. package/testaro/tabNav.d.ts +33 -0
  198. package/testaro/tabNav.js +272 -342
  199. package/testaro/tabNav.ts +454 -0
  200. package/testaro/targetsNear.d.ts +9 -0
  201. package/testaro/targetsNear.js +130 -132
  202. package/testaro/targetsNear.ts +160 -0
  203. package/testaro/textNodes.d.ts +6 -0
  204. package/testaro/textNodes.js +139 -135
  205. package/testaro/textNodes.ts +185 -0
  206. package/testaro/textSem.d.ts +3 -0
  207. package/testaro/textSem.js +25 -28
  208. package/testaro/textSem.ts +47 -0
  209. package/testaro/title.d.ts +9 -0
  210. package/testaro/title.js +16 -13
  211. package/testaro/title.ts +31 -0
  212. package/testaro/titledEl.d.ts +3 -0
  213. package/testaro/titledEl.js +15 -18
  214. package/testaro/titledEl.ts +38 -0
  215. package/testaro/zIndex.d.ts +3 -0
  216. package/testaro/zIndex.js +19 -22
  217. package/testaro/zIndex.ts +42 -0
  218. package/tests/alfa.d.ts +45 -0
  219. package/tests/alfa.js +138 -141
  220. package/tests/alfa.ts +214 -0
  221. package/tests/aslint.d.ts +33 -0
  222. package/tests/aslint.js +272 -249
  223. package/tests/aslint.ts +301 -0
  224. package/tests/axe.d.ts +27 -0
  225. package/tests/axe.js +199 -200
  226. package/tests/axe.ts +277 -0
  227. package/tests/ed11y.d.ts +28 -0
  228. package/tests/ed11y.js +141 -99
  229. package/tests/ed11y.ts +178 -0
  230. package/tests/htmlcs.d.ts +21 -0
  231. package/tests/htmlcs.js +174 -140
  232. package/tests/htmlcs.ts +181 -0
  233. package/tests/ibm.d.ts +52 -0
  234. package/tests/ibm.js +164 -166
  235. package/tests/ibm.ts +251 -0
  236. package/tests/nuVal.d.ts +13 -0
  237. package/tests/nuVal.js +107 -112
  238. package/tests/nuVal.ts +145 -0
  239. package/tests/nuVnu.d.ts +15 -0
  240. package/tests/nuVnu.js +141 -111
  241. package/tests/nuVnu.ts +144 -0
  242. package/tests/pour.d.ts +31 -0
  243. package/tests/pour.js +242 -0
  244. package/tests/pour.ts +273 -0
  245. package/tests/qualWeb.d.ts +39 -0
  246. package/tests/qualWeb.js +302 -272
  247. package/tests/qualWeb.ts +415 -0
  248. package/tests/surea11y.d.ts +33 -0
  249. package/tests/surea11y.js +288 -0
  250. package/tests/surea11y.ts +334 -0
  251. package/tests/testaro.d.ts +25 -0
  252. package/tests/testaro.js +745 -652
  253. package/tests/testaro.ts +862 -0
  254. package/tests/wave.d.ts +46 -0
  255. package/tests/wave.js +166 -177
  256. package/tests/wave.ts +252 -0
  257. package/tsconfig.json +17 -0
  258. package/types.d.ts +243 -0
  259. package/types.js +10 -0
  260. package/types.ts +376 -0
  261. package/validation/act/README.md +46 -0
  262. package/validation/act/capture.js +424 -0
  263. package/validation/act/chromium-issue-draft.md +66 -0
  264. package/validation/act/fp-triage-2026-08-22.md +86 -0
  265. package/validation/act/isolation-notes.md +159 -0
  266. package/validation/act/playwright-issue-draft.md +73 -0
  267. package/validation/act/propose-mappings.js +0 -0
  268. package/validation/act/repro-cdp-raw.js +105 -0
  269. package/validation/act/repro-metarefresh.js +65 -0
  270. package/validation/act/score.js +184 -0
  271. package/validation/act/stage3a-stress-report.md +69 -0
  272. package/validation/act/stage3b-mapping-proposals.md +94 -0
  273. package/validation/act/stage3b-triage-draft.md +138 -0
  274. package/validation/act/surea11y-track-a-2026-09-01.md +50 -0
  275. package/validation/executors/netWatch.js +180 -90
  276. package/validation/executors/test.js +17 -2
  277. package/validation/executors/tests.js +118 -10
  278. package/validation/jobs/reports/raw/260901T1000-surea11y-validation.json +964 -0
  279. package/validation/jobs/todo/240101T1200-simple-example.json +14 -6
  280. package/validation/jobs/todo/240101T1300-shoot-example.json +2 -1
  281. package/validation/jobs/todo/260821T1900-pour-validation.json +45 -0
  282. package/validation/jobs/todo/260901T1000-surea11y-validation.json +45 -0
  283. package/validation/knownFailures.json +1 -0
  284. package/validation/tests/jobProperties/adbID.json +27 -2
  285. package/validation/tests/jobProperties/{focOp.json → allCapStyle.json} +56 -53
  286. package/validation/tests/jobProperties/allCaps.json +25 -0
  287. package/validation/tests/jobProperties/allHidden.json +133 -13
  288. package/validation/tests/jobProperties/allSlanted.json +3 -3
  289. package/validation/tests/jobProperties/altScheme.json +23 -8
  290. package/validation/tests/jobProperties/attVal.json +57 -57
  291. package/validation/tests/jobProperties/autocomplete.json +11 -1
  292. package/validation/tests/jobProperties/bulk.json +6 -1
  293. package/validation/tests/jobProperties/buttonMenu.json +63 -42
  294. package/validation/tests/jobProperties/captionLoc.json +1 -6
  295. package/validation/tests/jobProperties/checkpoint-browser.json +401 -0
  296. package/validation/tests/jobProperties/checkpoint-page.json +397 -0
  297. package/validation/tests/jobProperties/checkpoint.json +395 -0
  298. package/validation/tests/jobProperties/datalistRef.json +20 -5
  299. package/validation/tests/jobProperties/distortion.json +28 -3
  300. package/validation/tests/jobProperties/docType.json +2 -2
  301. package/validation/tests/jobProperties/dupAtt.json +48 -33
  302. package/validation/tests/jobProperties/elements.json +28 -28
  303. package/validation/tests/jobProperties/embAc.json +36 -31
  304. package/validation/tests/jobProperties/focAndOp.json +284 -0
  305. package/validation/tests/jobProperties/focInd.json +34 -34
  306. package/validation/tests/jobProperties/focVis.json +1 -1
  307. package/validation/tests/jobProperties/hover.json +39 -37
  308. package/validation/tests/jobProperties/hr.json +13 -3
  309. package/validation/tests/jobProperties/imageLink.json +0 -5
  310. package/validation/tests/jobProperties/labClash.json +68 -33
  311. package/validation/tests/jobProperties/legendLoc.json +1 -6
  312. package/validation/tests/jobProperties/lineHeight.json +2 -2
  313. package/validation/tests/jobProperties/linkAmb.json +13 -18
  314. package/validation/tests/jobProperties/linkExt.json +1 -1
  315. package/validation/tests/jobProperties/linkOldAtt.json +12 -2
  316. package/validation/tests/jobProperties/linkTo.json +1 -1
  317. package/validation/tests/jobProperties/linkUl.json +65 -65
  318. package/validation/tests/jobProperties/miniText.json +7 -2
  319. package/validation/tests/jobProperties/motion.json +4 -50
  320. package/validation/tests/jobProperties/nonTable.json +48 -3
  321. package/validation/tests/jobProperties/optRoleSel.json +12 -2
  322. package/validation/tests/jobProperties/phOnly.json +6 -16
  323. package/validation/tests/jobProperties/pseudoP.json +17 -2
  324. package/validation/tests/jobProperties/radioSet.json +30 -30
  325. package/validation/tests/jobProperties/role.json +25 -5
  326. package/validation/tests/jobProperties/secHeading.json +26 -21
  327. package/validation/tests/jobProperties/styleDiff.json +35 -5
  328. package/validation/tests/jobProperties/tabNav.json +3 -1
  329. package/validation/tests/jobProperties/targetsNear.json +237 -0
  330. package/validation/tests/jobProperties/textNodes.json +29 -29
  331. package/validation/tests/jobProperties/textSem.json +26 -1
  332. package/validation/tests/jobProperties/title.json +12 -2
  333. package/validation/tests/jobProperties/titledEl.json +39 -9
  334. package/validation/tests/jobProperties/userPath.json +317 -0
  335. package/validation/tests/jobProperties/zIndex.json +40 -35
  336. package/validation/tests/targets/allCapStyle/index.html +26 -0
  337. package/validation/tests/targets/checkpoint/index.html +38 -0
  338. package/validation/tests/targets/datalistRef/index.html +1 -1
  339. package/validation/tests/targets/focAndOp/bad.html +29 -0
  340. package/validation/tests/targets/{focOp → focAndOp}/good.html +3 -1
  341. package/validation/tests/targets/focInd/bad.html +2 -1
  342. package/validation/tests/targets/headEl/index.html +10 -1
  343. package/validation/tests/targets/{targetSmall → targetsNear}/index.html +15 -1
  344. package/validation/tests/targets/userPath/index.html +42 -0
  345. package/validation/validateTest.js +61 -9
  346. package/.claude/settings.local.json +0 -11
  347. package/.eslintrc.json +0 -41
  348. package/htmlcs/.eslintrc.json +0 -67
  349. package/validation/tests/jobProperties/linkTitle.json +0 -127
  350. package/validation/tests/jobProperties/opFoc.json +0 -164
  351. package/validation/tests/jobProperties/targetSmall.json +0 -152
  352. package/validation/tests/jobProperties/targetTiny.json +0 -142
  353. package/validation/tests/targets/focOp/bad.html +0 -25
  354. package/validation/tests/targets/linkTitle/index.html +0 -24
  355. package/validation/tests/targets/opFoc/bad.html +0 -26
  356. package/validation/tests/targets/opFoc/good.html +0 -23
@@ -0,0 +1,8 @@
1
+ var pourEngine=(()=>{var ot=Object.defineProperty;var zn=Object.getOwnPropertyDescriptor;var _n=Object.getOwnPropertyNames;var Vn=Object.prototype.hasOwnProperty;var Hn=(e,t)=>{for(var o in t)ot(e,o,{get:t[o],enumerable:!0})},jn=(e,t,o,a)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of _n(t))!Vn.call(e,n)&&n!==o&&ot(e,n,{get:()=>t[n],enumerable:!(a=zn(t,n))||a.enumerable});return e};var Yn=e=>jn(ot({},"__esModule",{value:!0}),e);var Rs={};Hn(Rs,{name:()=>Fn,run:()=>Ts,version:()=>Pn});var at={engine:{name:"pour engine",version:"1.37.0"}};function de(e){let t=[e];for(let o=0;o<t.length;o++)if(t[o].querySelectorAll)for(let a of t[o].querySelectorAll("*"))a.shadowRoot&&t.push(a.shadowRoot);return t}function Kn(e){return e.parentElement??e.getRootNode()?.host??null}function Ge(e){if(typeof e.checkVisibility=="function")return e.checkVisibility({visibilityProperty:!0});let t=getComputedStyle(e);return t.display!=="none"&&t.visibility!=="hidden"}function nt(e){for(let t=e;t;t=Kn(t))if(t.getAttribute?.("aria-hidden")==="true")return!1;return Ge(e)}function Ht(e){let t=e.getRootNode(),o=r=>r.id&&t.querySelectorAll(`#${CSS.escape(r.id)}`).length===1;if(o(e))return`#${CSS.escape(e.id)}`;let a=[],n=e;for(;n&&n.nodeType===Node.ELEMENT_NODE&&n!==document.documentElement;){let r=n.tagName.toLowerCase();if(n.parentElement){let i=1;for(let l=n.previousElementSibling;l;l=l.previousElementSibling)l.tagName===n.tagName&&(i+=1);let s=i>1;for(let l=n.nextElementSibling;!s&&l;l=l.nextElementSibling)l.tagName===n.tagName&&(s=!0);s&&(r+=`:nth-of-type(${i})`)}if(a.unshift(r),n.parentElement&&o(n.parentElement)){a.unshift(`#${CSS.escape(n.parentElement.id)}`);break}n=n.parentElement}return a.join(" > ")||e.tagName.toLowerCase()}function rt(e){let t=Ht(e),o=e.getRootNode();for(;o&&o.host;)t=`${Ht(o.host)} >>> ${t}`,o=o.host.getRootNode();return t}var jt=typeof Element<"u"?Object.getOwnPropertyDescriptor(Element.prototype,"attributes")?.get:null;function ye(e){let t=e.attributes;return t&&typeof t.length=="number"&&typeof t.item=="function"?t:jt?jt.call(e):[]}function it(e,t=300){let o=`<${e.tagName.toLowerCase()}`;for(let{name:n,value:r}of ye(e)){if(o.length>=t)break;o+=` ${n}="${r}"`}o+=">";let a=st(e);return a&&o.length<t&&(o+=a.slice(0,t-o.length)),o.length>t?`${o.slice(0,t)}\u2026`:o}function Te(e){let t=e?.defaultView;return!!(t&&t.top&&t!==t.top)}function pe(e){for(let t=e;t;t=t.parentElement??t.getRootNode?.()?.host??null)if(t.nodeType===1&&t.hasAttribute("inert"))return!0;return!1}function st(e){return[...e.childNodes].filter(t=>t.nodeType===Node.TEXT_NODE).map(t=>t.textContent).join("").trim()}var Xn=new Set(["untitled document","untitled page","untitled-1","untitled 1","no title","insert title here","title here","page title","document title","web page","webpage"]),Zn=new Set(["untitled","document","page","title","default","index","new document","new page","new tab","react app","create react app","vite app","vite + react","vite + vue","vite + svelte","next app","create next app","vue app","nuxt app","svelte app","angular app","my app","my site","my website","site title","your site title","example domain","hello world"]),Yt={id:"document-title",name:"Page title",impact:"serious",tags:["wcag2a","wcag242"],help:"The page must have a title that describes its topic or purpose",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/page-titled.html",selector:"html",visibleOnly:!1,evaluate(e){if(Te(e.ownerDocument))return{status:"pass"};let t=e.ownerDocument.title.trim();if(!t)return{status:"fail",message:"The page has no title, so users of screen readers and browser tabs cannot tell what it is.",fix:"Add <title>Page name \u2014 Site name</title> inside <head>."};let o=t.replace(/\s+/g," ").toLowerCase();return Xn.has(o)||/^(?:untitled|new (?:document|page|tab))[\s-]*\d+$/.test(o)?{status:"fail",message:`\u201C${t}\u201D is a placeholder left by an editor or template. It gives no topic and no purpose, so a screen reader announces nothing useful when the page loads, and a row of open tabs, bookmarks or history entries becomes impossible to tell apart.`,fix:"Put this page's own subject in the title first, then the site name."}:Zn.has(o)?{status:"incomplete",message:`\u201C${t}\u201D is a generic title, and usually a default nobody replaced. Unless the page really is about that, it describes neither topic nor purpose and 2.4.2 is not met. Check whether it was meant to be filled in.`}:{status:"pass"}}};var Jn=/^([a-zA-Z]{2,3}(-[a-zA-Z0-9]{1,8})*|[xXiI](-[a-zA-Z0-9]{1,8})+)$/,Kt={id:"html-lang",name:"Page language",impact:"serious",tags:["wcag2a","wcag311"],help:"The <html> element must declare a valid language",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/language-of-page.html",selector:"html",visibleOnly:!1,evaluate(e){let t=e.getAttribute("lang")?.trim();return e.hasAttribute("lang")?t?Jn.test(t)?{status:"pass"}:{status:"fail",message:`lang="${t}" is not a valid language tag.`,fix:'Use a BCP 47 tag such as lang="en" or lang="en-GB".'}:{status:"fail",message:"The lang attribute is present but empty, so the page declares no language: screen readers will guess it and may mispronounce everything.",fix:'Give lang a BCP 47 value, e.g. <html lang="en">.'}:{status:"fail",message:"No lang attribute: screen readers will guess the language and may mispronounce everything.",fix:'Add lang to the html element, e.g. <html lang="en">.'}}};var Qn=/^([a-zA-Z]{2,3}(-[a-zA-Z0-9]{1,8})*|[xXiI](-[a-zA-Z0-9]{1,8})+)$/,Xt={id:"valid-lang-parts",name:"Part language tags",impact:"serious",tags:["wcag2aa","wcag312"],help:"lang attributes on page parts must be valid",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/language-of-parts.html",selector:"[lang]:not(html)",evaluate(e){let t=e.getAttribute("lang").trim();return t===""||Qn.test(t)?{status:"pass"}:{status:"fail",message:`lang="${t}" is not a valid language tag, so screen readers may switch to the wrong pronunciation.`,fix:'Use a BCP 47 tag such as lang="fr" or lang="de-AT".'}}};var er=/\.(png|jpe?g|gif|webp|svg|avif|bmp|ico)([?#].*)?$/i,tr=/^(image|img|photo|photograph|picture|graphic|icon|untitled|placeholder|spacer|\d+)$/i,Zt={id:"image-alt",name:"Image alt text",impact:"critical",tags:["wcag2a","wcag111"],help:"Every <img> needs a text alternative",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/non-text-content.html",selector:"img",evaluate(e,{accessibleName:t}){let o=e.getAttribute("role");if(o==="presentation"||o==="none")return{status:"pass"};if(e.hasAttribute("alt")){let a=e.getAttribute("alt");if(a==="")return{status:"pass"};if(!a.trim())return{status:"incomplete",message:`alt="${a}" is only whitespace \u2014 not a description, and not the decorative marker either (that is alt="" with nothing between the quotes). Screen readers skip it today, so check the image is really decorative; if it carries meaning, describe it, and either way use alt="".`};let n=a.trim();return er.test(n)||tr.test(n)?{status:"incomplete",message:`alt="${n}" looks like a file name or placeholder, not a description \u2014 screen-reader users learn nothing from it. If the image carries meaning, describe it; if not, use alt="".`}:{status:"pass"}}return t(e)?{status:"pass"}:{status:"fail",message:"This image has no alt attribute, so screen readers announce its file name or nothing at all.",fix:`Describe the image: <img alt="\u2026" src="${e.getAttribute("src")??""}">, or mark it decorative with alt="".`}}};var or=new Set(["text","search","url","tel","email","password","number","date","datetime-local","month","time","week",""]),ar=new Set(["input","select","textarea","button","meter","output","progress"]);function qe(e){return Qt(e,!1)}function J(e){let t=e.getAttribute?.("aria-labelledby");if(!t)return"";let o=e.getRootNode();return t.split(/\s+/).filter(Boolean).map(a=>{let n=o.getElementById?.(a);return n?Qt(n,!0):""}).join(" ").replace(/\s+/g," ").trim()}function Qt(e,t){if(!t){let r=J(e);if(r)return r}let o=e.getAttribute("aria-label")?.trim();if(o)return o;let a=e.tagName.toLowerCase();if(a==="img"||a==="area"){let r=e.getAttribute("alt")?.trim();if(r)return r}if(ar.has(a)&&e.labels?.length){let r=[...e.labels].map(i=>i.textContent).join(" ").trim();if(r)return r}if(a==="input"||a==="select"||a==="textarea"){if(e.type==="submit"||e.type==="reset"||e.type==="button"){let r=(e.value??e.getAttribute("value")??"").trim();if(r)return r}if(e.type==="image"){let r=e.getAttribute("alt")?.trim();if(r)return r}if(t&&(a==="textarea"||or.has(e.type))){let r=(e.value??"").trim();if(r)return r}if(e.type==="submit")return"Submit";if(e.type==="reset")return"Reset"}let n=nr(e,t).replace(/\s+/g," ").trim();return n||(e.getAttribute("title")??e.getAttribute("placeholder")??"").trim()}function nr(e,t){let o=e.shadowRoot?e.shadowRoot.childNodes:e.childNodes;return Jt(e,"::before")+lt(o,t)+Jt(e,"::after")}function Jt(e,t){let o=getComputedStyle(e,t).content;if(!o||o==="none"||o==="normal")return"";let a=o.match(/^"((?:[^"\\]|\\.)*)"$/);return a?a[1].replace(/\\(.)/g,"$1"):""}function lt(e,t){let o="";for(let a of e){if(a.nodeType===3){o+=a.textContent;continue}if(a.nodeType!==1)continue;let n=a.tagName.toLowerCase();if(n==="script"||n==="style"||n==="noscript"||n==="template")continue;if(!t){if(a.getAttribute("aria-hidden")==="true"||a.hasAttribute("hidden"))continue;let l=getComputedStyle(a);if(l.display==="none"||l.visibility==="hidden")continue}if(n==="slot"){let l=a.assignedNodes?.()??[];o+=lt(l.length?l:a.childNodes,t);continue}if(n==="img"||n==="area"){let l=a.getAttribute("aria-label")?.trim();if(l){o+=` ${l} `;continue}let c=a.getAttribute("alt");if(c!==null){o+=` ${c} `;continue}let d=a.getAttribute("title")?.trim();d&&(o+=` ${d} `);continue}let r=a.getAttribute("aria-label")?.trim();if(r){o+=` ${r} `;continue}let i=lt(a.shadowRoot?a.shadowRoot.childNodes:a.childNodes,t);if(i.trim()){o+=i;continue}let s=a.getAttribute("title")?.trim();s&&(o+=` ${s} `)}return o}var eo={id:"svg-img-alt",name:"SVG accessible name",impact:"serious",tags:["wcag2a","wcag111"],help:'Inline SVG and role="img" graphics need an accessible name',helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/non-text-content.html",selector:'svg[role="img"], svg[role="graphics-document"], svg[role="graphics-symbol"], [role="img"]:not(img):not(svg), svg:not([role])',evaluate(e,{accessibleName:t}){if(e.tagName.toLowerCase()!=="svg"&&e.getAttribute("role")==="img")return J(e)||e.getAttribute("aria-label")?.trim()||e.getAttribute("title")?.trim()?{status:"pass"}:{status:"fail",message:'This element is marked as an image but has no accessible name \u2014 for role="img", content inside it (including an inner SVG\u2019s <title>) does not count as a name.',fix:'Add aria-label="\u2026" or aria-labelledby pointing at visible text.'};if(t(e))return{status:"pass"};if(!e.getAttribute("role")&&e.tagName.toLowerCase()==="svg"){if(e.parentNode instanceof SVGElement)return{status:"pass"};let o=e.closest('a[href], button, [role="button"], [role="link"]');return o&&t(o)?{status:"pass"}:{status:"incomplete",message:"This SVG has no accessible name and is not marked decorative \u2014 some screen readers announce it as an unlabeled graphic.",fix:'Add aria-hidden="true" if decorative; if meaningful, add role="img" and aria-label="\u2026" (or a <title> as the first child).'}}return{status:"fail",message:"This element is marked as an image but has no accessible name.",fix:'Add aria-label="\u2026" (for SVG, a <title> as the first child also works).'}}};var ze=new Set(["activedescendant","atomic","autocomplete","braillelabel","brailleroledescription","busy","checked","colcount","colindex","colindextext","colspan","controls","current","describedby","description","details","disabled","dropeffect","errormessage","expanded","flowto","grabbed","haspopup","hidden","invalid","keyshortcuts","label","labelledby","level","live","modal","multiline","multiselectable","orientation","owns","placeholder","posinset","pressed","readonly","relevant","required","roledescription","rowcount","rowindex","rowindextext","rowspan","selected","setsize","sort","valuemax","valuemin","valuenow","valuetext"]),Le=new Set(["atomic","busy","controls","current","describedby","description","details","dropeffect","flowto","grabbed","hidden","keyshortcuts","label","labelledby","live","owns","relevant","roledescription","braillelabel","brailleroledescription"]),he={link:["disabled","errormessage","expanded","haspopup","invalid"],button:["disabled","errormessage","expanded","haspopup","invalid","pressed"],checkbox:["checked","disabled","errormessage","expanded","haspopup","invalid","readonly","required"],switch:["checked","disabled","errormessage","expanded","haspopup","invalid","readonly","required"],radio:["checked","disabled","errormessage","haspopup","invalid","posinset","setsize"],option:["checked","disabled","errormessage","haspopup","invalid","posinset","selected","setsize"],tab:["disabled","errormessage","expanded","haspopup","invalid","posinset","selected","setsize"],menuitem:["disabled","errormessage","expanded","haspopup","invalid","posinset","setsize"],menuitemcheckbox:["checked","disabled","errormessage","expanded","haspopup","invalid","posinset","setsize"],menuitemradio:["checked","disabled","errormessage","expanded","haspopup","invalid","posinset","setsize"],textbox:["activedescendant","autocomplete","disabled","errormessage","haspopup","invalid","multiline","placeholder","readonly","required"],searchbox:["activedescendant","autocomplete","disabled","errormessage","haspopup","invalid","multiline","placeholder","readonly","required"],combobox:["activedescendant","autocomplete","disabled","errormessage","expanded","haspopup","invalid","readonly","required"],listbox:["activedescendant","disabled","errormessage","expanded","haspopup","invalid","multiselectable","orientation","readonly","required"],slider:["disabled","errormessage","haspopup","invalid","orientation","readonly","valuemax","valuemin","valuenow","valuetext"],spinbutton:["activedescendant","disabled","errormessage","haspopup","invalid","readonly","required","valuemax","valuemin","valuenow","valuetext"],progressbar:["disabled","errormessage","haspopup","invalid","valuemax","valuemin","valuenow","valuetext"],meter:["disabled","errormessage","haspopup","invalid","valuemax","valuemin","valuenow","valuetext"],scrollbar:["disabled","errormessage","haspopup","invalid","orientation","valuemax","valuemin","valuenow","valuetext"],heading:["disabled","errormessage","haspopup","invalid","level"],list:["disabled","errormessage","haspopup","invalid"],listitem:["disabled","errormessage","haspopup","invalid","level","posinset","setsize"],row:["activedescendant","colindex","colindextext","disabled","errormessage","expanded","haspopup","invalid","level","posinset","rowindex","rowindextext","selected","setsize"],rowgroup:["disabled","errormessage","haspopup","invalid"],cell:["colindex","colindextext","colspan","disabled","errormessage","haspopup","invalid","rowindex","rowindextext","rowspan"],gridcell:["colindex","colindextext","colspan","disabled","errormessage","expanded","haspopup","invalid","readonly","required","rowindex","rowindextext","rowspan","selected"],columnheader:["colindex","colindextext","colspan","disabled","errormessage","expanded","haspopup","invalid","readonly","required","rowindex","rowindextext","rowspan","selected","sort"],rowheader:["colindex","colindextext","colspan","disabled","errormessage","expanded","haspopup","invalid","readonly","required","rowindex","rowindextext","rowspan","selected","sort"],table:["colcount","disabled","errormessage","haspopup","invalid","rowcount"],grid:["activedescendant","colcount","disabled","errormessage","haspopup","invalid","multiselectable","readonly","rowcount"],treegrid:["activedescendant","colcount","disabled","errormessage","haspopup","invalid","multiselectable","orientation","readonly","required","rowcount"],tablist:["activedescendant","disabled","errormessage","haspopup","invalid","multiselectable","orientation"],menu:["activedescendant","disabled","errormessage","haspopup","invalid","orientation"],menubar:["activedescendant","disabled","errormessage","haspopup","invalid","orientation"],tree:["activedescendant","disabled","errormessage","haspopup","invalid","multiselectable","orientation","required"],treeitem:["checked","disabled","errormessage","expanded","haspopup","invalid","level","posinset","selected","setsize"],radiogroup:["activedescendant","disabled","errormessage","haspopup","invalid","orientation","readonly","required"],group:["activedescendant","disabled","errormessage","haspopup","invalid"],separator:["disabled","errormessage","haspopup","invalid","orientation","valuemax","valuemin","valuenow","valuetext"],toolbar:["activedescendant","disabled","errormessage","haspopup","invalid","orientation"],dialog:["disabled","errormessage","haspopup","invalid","modal"],alertdialog:["disabled","errormessage","haspopup","invalid","modal"],application:["activedescendant","disabled","errormessage","expanded","haspopup","invalid"],article:["disabled","errormessage","haspopup","invalid","posinset","setsize"],img:["disabled","errormessage","haspopup","invalid"],figure:["disabled","errormessage","haspopup","invalid"],document:["disabled","errormessage","haspopup","invalid"],feed:["disabled","errormessage","haspopup","invalid"],math:["disabled","errormessage","haspopup","invalid"],note:["disabled","errormessage","haspopup","invalid"],presentation:["disabled","errormessage","haspopup","invalid"],none:["disabled","errormessage","haspopup","invalid"],banner:["disabled","errormessage","haspopup","invalid"],complementary:["disabled","errormessage","haspopup","invalid"],contentinfo:["disabled","errormessage","haspopup","invalid"],form:["disabled","errormessage","haspopup","invalid"],main:["disabled","errormessage","haspopup","invalid"],navigation:["disabled","errormessage","haspopup","invalid"],region:["disabled","errormessage","haspopup","invalid"],search:["disabled","errormessage","haspopup","invalid"],alert:["disabled","errormessage","haspopup","invalid"],log:["disabled","errormessage","haspopup","invalid"],marquee:["disabled","errormessage","haspopup","invalid"],status:["disabled","errormessage","haspopup","invalid"],timer:["disabled","errormessage","haspopup","invalid"],tabpanel:["disabled","errormessage","haspopup","invalid"],tooltip:["disabled","errormessage","haspopup","invalid"],definition:["disabled","errormessage","haspopup","invalid"],term:["disabled","errormessage","haspopup","invalid"],paragraph:["disabled","errormessage","haspopup","invalid"],generic:["disabled","errormessage","haspopup","invalid"],blockquote:["disabled","errormessage","haspopup","invalid"],caption:["disabled","errormessage","haspopup","invalid"],code:["disabled","errormessage","haspopup","invalid"],emphasis:["disabled","errormessage","haspopup","invalid"],strong:["disabled","errormessage","haspopup","invalid"],time:["disabled","errormessage","haspopup","invalid"],deletion:["disabled","errormessage","haspopup","invalid"],insertion:["disabled","errormessage","haspopup","invalid"],subscript:["disabled","errormessage","haspopup","invalid"],superscript:["disabled","errormessage","haspopup","invalid"]},rr={checkbox:"checkbox",radio:"radio",range:"slider",number:"spinbutton",search:"searchbox",email:"textbox",tel:"textbox",text:"textbox",url:"textbox",button:"button",submit:"button",reset:"button",image:"button"},ir=new Set(["text","search","tel","url","email"]),sr={button:"button",textarea:"textbox",img:"img",article:"article",aside:"complementary",nav:"navigation",main:"main",search:"search",h1:"heading",h2:"heading",h3:"heading",h4:"heading",h5:"heading",h6:"heading",ul:"list",ol:"list",menu:"list",li:"listitem",table:"table",thead:"rowgroup",tbody:"rowgroup",tfoot:"rowgroup",tr:"row",td:"cell",th:"columnheader",form:"form",fieldset:"group",details:"group",dialog:"dialog",hr:"separator",progress:"progressbar",meter:"meter",output:"status",option:"option",datalist:"listbox",dt:"term",dd:"definition",p:"paragraph",div:"generic",span:"generic",blockquote:"blockquote",figure:"figure",time:"time",code:"code",em:"emphasis",strong:"strong"};function Q(e){let t=e.tagName.toLowerCase();if(t==="a"||t==="area")return e.hasAttribute("href")?"link":"generic";if(t==="input")return ir.has(e.type)&&e.hasAttribute("list")?"combobox":rr[e.type]??null;if(t==="td"||t==="th"){if(t==="th"&&e.getAttribute("scope")?.toLowerCase()==="row")return"rowheader";if(t==="th")return"columnheader";let o=e.closest("table"),a=o&&Y(o);return a==="grid"||a==="treegrid"?"gridcell":"cell"}if(t==="select")return e.multiple||e.size>1?"listbox":"combobox";if(t==="img")return e.getAttribute("alt")===""?"presentation":"img";if(t==="header")return e.closest("article, aside, main, nav, section")?"generic":"banner";if(t==="footer")return e.closest("article, aside, main, nav, section")?"generic":"contentinfo";if(t==="aside"){let o=e.parentElement?.closest("article, aside, nav, section"),a=e.hasAttribute("aria-label")||e.hasAttribute("aria-labelledby");return o&&!a?"generic":"complementary"}return t==="section"?e.hasAttribute("aria-label")||e.hasAttribute("aria-labelledby")?"region":"generic":sr[t]??null}function lr(e){return e.tabIndex>=0||e.matches('a[href], button, input, select, textarea, summary, [contenteditable="true"]')?!0:[...Le].some(t=>e.hasAttribute(`aria-${t}`))}function Y(e){let t=e.getAttribute("role")?.trim().split(/\s+/)??[];for(let o of t){let a=o.toLowerCase();if(he[a])return(a==="presentation"||a==="none")&&lr(e)?Q(e):a}return t.length?null:Q(e)}var to={id:"button-name",name:"Button names",impact:"critical",tags:["wcag2a","wcag412"],help:"Buttons must have an accessible name",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/name-role-value.html",selector:'button, [role="button"], input[type="button"], input[type="submit"], input[type="reset"]',evaluate(e,{accessibleName:t}){return(Y(e)??Q(e))!=="button"?{status:"pass"}:t(e)?{status:"pass"}:{status:"fail",message:'This button has no accessible name \u2014 a screen reader announces just "button".',fix:'Add visible text inside the button, or aria-label="What it does" for icon-only buttons.'}}};var oo={id:"link-name",name:"Link names",impact:"serious",tags:["wcag2a","wcag244","wcag412"],help:"Links must have an accessible name",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/link-purpose-in-context.html",selector:'a[href], [role="link"]',evaluate(e,{accessibleName:t}){return(Y(e)??Q(e))!=="link"?{status:"pass"}:t(e)?{status:"pass"}:{status:"fail",message:"This link has no accessible name \u2014 screen readers can only announce its URL.",fix:"Add text content to the link, alt text to the image inside it, or an aria-label."}}};var ao=new Set(["read more","read more about this","more","much more","learn more","click here","click","click this","tap here","press here","here","this","this link","link","this page","go","go here","more info","more information","further information","see more","view more","show more","find out more","discover more","continue","continue reading","keep reading","full story","full article","details","see details","view details","more details","en savoir plus","savoir plus","lire la suite","lire plus","plus","cliquez ici","cliquer ici","clique ici","ici","ce lien","lien","cette page","voir plus","en voir plus","afficher plus","afficher","voir","voir le d\xE9tail","voir les d\xE9tails","plus de d\xE9tails","plus d informations","plus d infos","en savoir davantage","d\xE9couvrir","d\xE9couvrez","continuer","suite"]);function cr(e){return e.toLowerCase().replace(/[^\p{L}\p{N}]+/gu," ").replace(/\s+/g," ").trim()}function ur(e){let t=e.trim();if(/^[<\[({"'«‹「『].*[>\])}"'»›」』]$/su.test(t))return!1;let o=t.toLowerCase().replace(/[’']/gu," ").replace(/^[^\p{L}\p{N}\s]+\s*/u,"").replace(/[^\p{L}\p{N}\s]+\s*$/u,"").replace(/\s+/g," ").trim();return ao.has(o)}function ct({id:e,impact:t,tags:o,help:a,helpUrl:n,verdict:r}){return{id:e,impact:t,tags:o,help:a,helpUrl:n,selector:'a[href], [role="link"]',evaluate(i,{accessibleName:s}){let l=s(i);if(!l)return{status:"pass"};let c=cr(l);if(!ao.has(c))return{status:"pass"};let d=(i.closest("[lang]")?.getAttribute("lang")??i.ownerDocument.documentElement.getAttribute("lang")??"").trim();return r(l,{provable:ur(l),normalized:c,lang:d})}}}var no=ct({id:"link-text-generic",name:"Generic link text",impact:"moderate",tags:["wcag2a","wcag244"],help:"Link text should describe where the link goes",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/link-purpose-in-context.html",verdict:e=>({status:"incomplete",message:`\u201C${e}\u201D does not say where this link goes. 2.4.4 lets the surrounding text supply that, so this passes only if the context a screen reader reaches \u2014 the same sentence, list item, table cell, or the heading of the card it sits in \u2014 makes the destination obvious. Check it does, and remember that anyone navigating by a list of the page's links sees this name on its own.`,fix:'Put the destination in the link text itself, e.g. "Read more about the 2026 budget", or extend the name with aria-label while keeping the visible words at the start of it.'})});var dr=new Set(["plus","suite"]),pr=/^fr(-|$)/i;function hr(e,t){return e==="go"?!0:dr.has(e)&&!pr.test(t)}var ro=ct({id:"link-text-generic-only",name:"Self-sufficient link text",impact:"moderate",tags:["wcag2aaa","wcag249"],help:"Link text alone must identify where the link goes (AAA)",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/link-purpose-link-only.html",verdict:(e,{provable:t,normalized:o,lang:a})=>!t||hr(o,a)?{status:"incomplete",message:`\u201C${e}\u201D matches a generic link phrase, but may identify its destination anyway: a code term keeps its meaning through the punctuation this comparison drops, and a brand or product name (a logo link named \u201CGo\u201D, a plan named \u201CPlus\u201D) is the destination. Check whether this name, read alone in a list of the page's links, says where it goes.`,fix:'If the name really is generic here, put the destination in the link text, e.g. "Read more about the 2026 budget".'}:{status:"fail",message:`\u201C${e}\u201D gives no purpose on its own. 2.4.9 asks that the link text alone identify where a link goes, without relying on the sentence or heading around it, so anyone reading the page's links as a list can tell them apart.`,fix:'Name the destination in the link text, e.g. "Read more about the 2026 budget".'}});var io={id:"form-label",name:"Form field labels",impact:"critical",tags:["wcag2a","wcag131","wcag332","wcag412"],help:"Every form field needs a label",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/info-and-relationships.html",selector:'input:not([type="hidden"]):not([type="button"]):not([type="submit"]):not([type="reset"]):not([type="image"]), select, textarea',evaluate(e){let t=[...e.labels??[]].filter(s=>{if(s.closest('[aria-hidden="true"]'))return!1;let l=getComputedStyle(s);return l.display!=="none"&&l.visibility!=="hidden"}),o=s=>{if(s.nodeType===Node.TEXT_NODE)return s.textContent;if(s.nodeType!==Node.ELEMENT_NODE||s.getAttribute("aria-hidden")==="true")return"";let l=getComputedStyle(s);if(l.display==="none"||l.visibility==="hidden")return"";let c=s.getAttribute("aria-label");if(c?.trim())return c;let d=s.tagName==="IMG"||s.tagName==="AREA"?s.getAttribute("alt"):null;return d?.trim()?d:[...s.childNodes].map(o).join(" ")},a=t.map(o).join(" ").trim(),n=e.getAttribute("aria-label")?.trim(),r=J(e);if(a||n||r)return{status:"pass"};let i=e.id?` (it already has id="${e.id}")`:" (give it an id first)";if(e.getAttribute("title")?.trim())return{status:"incomplete",message:"This field is named only by its title attribute (technique H65) \u2014 conformant, but the label is invisible until hover and some tools skip it. Check a visible label isn\u2019t needed here."};if(e.getAttribute("placeholder"))return{status:"fail",message:"This field relies on its placeholder as the only label \u2014 the name vanishes as soon as the user types.",fix:`Add <label for="\u2026">Field name</label> pointing at this field${i}; keep the placeholder as an example value only.`};if(e.labels?.length&&!t.length)return[...e.labels].some(l=>{let c=getComputedStyle(l);return c.display!=="none"&&c.visibility!=="hidden"})?{status:"fail",message:`This field's only <label> is inside aria-hidden="true": sighted users still see it, but it is removed from the accessibility tree, so the field has no accessible name.`,fix:"Remove aria-hidden from the label (or from the ancestor that hides it). If the label must stay hidden from assistive technology, name the field another way with aria-label or aria-labelledby."}:{status:"fail",message:"This field's only <label> is hidden (display:none or visibility:hidden), so no one can see or rely on it.",fix:"Make the label visible, or use a visually-hidden-but-rendered technique (clip/sr-only) if it must not show."};if(e.labels?.length){let s=t.some(c=>c.textContent.trim()),l=t.some(c=>[...c.querySelectorAll('[aria-hidden="true"]')].some(d=>d.textContent.trim()));return s&&!l?{status:"fail",message:"This field's <label> holds text, but all of it is inside unrendered content (display:none or visibility:hidden), so the accessible name computes to nothing and nobody sees a label.",fix:"Render the text inside the label, move it out of the hidden element, or add aria-label to the field."}:{status:"fail",message:s?"This field's <label> shows text, but all of it is inside aria-hidden content \u2014 the accessible name computes to nothing.":"This field has a <label>, but the label is empty \u2014 it announces nothing.",fix:s?"Move the visible text out of the aria-hidden wrapper, or add aria-label to the field.":"Put visible text inside the label."}}if(e.id){let s=e.getRootNode(),l=s.querySelector?.(`label[for="${CSS.escape(e.id)}"]`),c=s.getElementById?.(e.id);if(l&&c&&c!==e)return{status:"fail",message:`A <label for="${e.id}"> exists, but an earlier element on the page has the same id, so the label belongs to that one and this field has no label at all.`,fix:"Give this field a unique id and point its label at that; ids must not repeat on a page."}}return{status:"fail",message:"This form field has no label \u2014 users cannot tell what to enter.",fix:`Add <label for="\u2026">Field name</label> pointing at this field${i}.`}}};var ut=new Set(["additional-name","address-level1","address-level2","address-level3","address-level4","address-line1","address-line2","address-line3","bday","bday-day","bday-month","bday-year","cc-additional-name","cc-csc","cc-exp","cc-exp-month","cc-exp-year","cc-family-name","cc-given-name","cc-name","cc-number","cc-type","country","country-name","current-password","email","family-name","given-name","honorific-prefix","honorific-suffix","impp","language","name","new-password","nickname","one-time-code","organization","organization-title","photo","postal-code","sex","street-address","tel","tel-area-code","tel-country-code","tel-extension","tel-local","tel-local-prefix","tel-local-suffix","tel-national","transaction-amount","transaction-currency","url","username"]),dt=new Set(["home","work","mobile","fax","pager"]),fr=new Set(["tel","tel-country-code","tel-national","tel-area-code","tel-local","tel-local-prefix","tel-local-suffix","tel-extension","email","impp"]),mr=new Set(["shipping","billing","webauthn","on","off"]),_e=e=>e.startsWith("section-"),gr=e=>ut.has(e)||dt.has(e)||mr.has(e)||_e(e);function br(e){if(e.length===1&&(e[0]==="on"||e[0]==="off"))return null;if(e.includes("on")||e.includes("off"))return'"on" and "off" must stand alone, with no other tokens';let t=0;if(_e(e[t])&&(t+=1),(e[t]==="shipping"||e[t]==="billing")&&(t+=1),dt.has(e[t])){if(!fr.has(e[t+1]))return`"${e[t]}" must come directly before a tel, tel-*, email or impp field name`;t+=2}else if(ut.has(e[t]))t+=1;else{let o=e[t];return o&&_e(o)?`"${o}" must be the first token`:o==="webauthn"?'"webauthn" must come last, after a field name':o==="shipping"||o==="billing"?`"${o}" may appear once, directly before the field name`:"the value names no field (exactly one field name such as email, tel or given-name is required)"}if(e[t]==="webauthn"&&(t+=1),t<e.length){let o=e[t];return _e(o)?`"${o}" must be the first token`:o==="shipping"||o==="billing"?`"${o}" must come before the field name`:o==="webauthn"?'"webauthn" must be the last token':ut.has(o)||dt.has(o)?`only one field name is allowed, but "${e[t-1]}" is followed by "${o}"`:`"${o}" cannot follow the field name`}return null}var so={id:"autocomplete-valid",name:"Autocomplete tokens",impact:"serious",tags:["wcag21aa","wcag135"],help:"autocomplete attributes must use valid tokens",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/identify-input-purpose.html",selector:'input[autocomplete]:not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="submit"]):not([type="image"]):not([type="reset"]):not([type="button"]):not(:disabled), select[autocomplete]:not(:disabled), textarea[autocomplete]:not(:disabled)',evaluate(e){if(e.getAttribute("aria-disabled")==="true")return{status:"pass"};let t=e.getAttribute("autocomplete").trim().toLowerCase().split(/\s+/).filter(Boolean),o=t.filter(n=>!gr(n));if(o.length)return{status:"fail",message:`autocomplete contains unknown token(s): ${o.join(", ")} \u2014 browsers and assistive tools can't identify this field's purpose.`,fix:'Use tokens from the HTML input-purposes list, e.g. autocomplete="email" or autocomplete="given-name".'};let a=t.length?br(t):null;return a?{status:"fail",message:`autocomplete="${e.getAttribute("autocomplete").trim()}" is not a valid autofill value: ${a}. Browsers discard the whole value, so no purpose is exposed.`,fix:'Use one field name from the HTML autofill list, optionally preceded by section-*, then shipping or billing, and (for tel, email and impp only) a contact type, e.g. autocomplete="shipping tel" or autocomplete="home email".'}:{status:"pass"}}};var wr=/^(html|body|:root)((?::[a-z-]+(?:\([^()]*\))?)*)$/i,lo=/rotate(?:3d\([^)]*,\s*)?\(?\s*(?:-?(?:90|270)deg|0\.25turn|-0\.25turn|100grad|-100grad)/i;function yr(e,t){return(e??"").split(",").some(o=>{let a=wr.exec(o.trim());if(!a)return!1;if(!a[2])return!0;let n=/^body$/i.test(a[1])?t.body:t.documentElement;try{return!!n&&n.matches(o.trim())}catch{return!1}})}function pt(e,t,o,a){for(let n of e??[]){let r=n.conditionText??n.media?.mediaText;if(r!==void 0&&n.cssRules){let i=/orientation\s*:\s*(portrait|landscape)/i.exec(r)?.[1];pt(n.cssRules,i?{condition:r,orientation:i}:t,o,a);continue}if(t&&n.style&&yr(n.selectorText,a)){let{display:i,visibility:s,transform:l,rotate:c}=n.style;i==="none"||s==="hidden"?o.push({kind:"hidden",selector:n.selectorText,...t}):(lo.test(l??"")||lo.test(c??""))&&o.push({kind:"rotated",selector:n.selectorText,...t})}else n.cssRules&&pt(n.cssRules,t,o)}}var co={id:"orientation-lock",name:"Orientation lock",impact:"serious",tags:["wcag21aa","wcag134"],help:"Content must work in both portrait and landscape",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/orientation.html",selector:"html",visibleOnly:!1,evaluate(e){let t=[];for(let n of e.ownerDocument.styleSheets){let r;try{r=n.cssRules}catch{continue}pt(r,null,t,e.ownerDocument)}if(!t.length)return{status:"pass"};let o=t[0],a=o.kind==="hidden"?"hides the page":"rotates the page to force the other orientation";return{status:"fail",message:`A stylesheet rule (${o.selector} under @media ${o.condition}) ${a} when the device is in ${o.orientation} \u2014 the content is locked to a single display orientation.`,fix:"Let the layout adapt to both orientations instead of hiding or rotating the page. If a single orientation is genuinely essential (rare), document why."}}};var uo={id:"meta-viewport",name:"Viewport zoom lock",impact:"critical",tags:["wcag2aa","wcag144"],help:"The viewport must let users zoom to at least 200%",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/resize-text.html",selector:'meta[name="viewport"]',visibleOnly:!1,evaluate(e){if(Te(e.ownerDocument))return{status:"pass"};let t=e.getAttribute("content")??"",o=/user-scalable\s*=\s*(no|0)/i.test(t),a=t.match(/maximum-scale\s*=\s*([\d.]+)/i),n=a&&parseFloat(a[1])<2;return!o&&!n?{status:"pass"}:{status:"fail",message:"This viewport meta tag stops low-vision users from zooming the page.",fix:"Remove user-scalable=no and any maximum-scale below 2 from the viewport meta tag."}}};var xr=/^rgba?\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)\s*(?:,\s*([\d.]+%?)\s*)?\)$/,vr=/^rgba?\(\s*([\d.]+%?)\s+([\d.]+%?)\s+([\d.]+%?)\s*(?:\/\s*([\d.]+%?)\s*)?\)$/,Ar=/^color\(\s*srgb\s+(-?[\d.]+%?)\s+(-?[\d.]+%?)\s+(-?[\d.]+%?)\s*(?:\/\s*([\d.]+%?)\s*)?\)$/,kr=e=>Math.min(255,Math.max(0,e)),Re=(e,t)=>kr(e.endsWith("%")?parseFloat(e)/100*255:parseFloat(e)*t),po=e=>{if(e===void 0)return 1;let t=parseFloat(e);return Math.min(1,Math.max(0,e.endsWith("%")?t/100:t))};function Cr(e){let t=xr.exec(e)??vr.exec(e);if(t)return{r:Re(t[1],1),g:Re(t[2],1),b:Re(t[3],1),a:po(t[4])};let o=Ar.exec(e);return o?{r:Re(o[1],255),g:Re(o[2],255),b:Re(o[3],255),a:po(o[4])}:null}var se,ht=!1;function Sr(e){if(ht)return null;try{if(se??=document.createElement("canvas").getContext("2d",{willReadFrequently:!0}),!se)return ht=!0,null;se.fillStyle="#000000",se.fillStyle=e;let t=se.fillStyle;if(se.fillStyle="#ffffff",se.fillStyle=e,t==="#000000"&&se.fillStyle==="#ffffff")return null;se.clearRect(0,0,1,1),se.fillRect(0,0,1,1);let[o,a,n,r]=se.getImageData(0,0,1,1).data;return{r:o,g:a,b:n,a:r/255}}catch{return ht=!0,null}}var Ve=new Map;function W(e){if(e==null)return null;if(Ve.has(e))return Ve.get(e);let t=Cr(e)??Sr(e);return Ve.size<1e4&&Ve.set(e,t),t}function ft(e){let t=e/255;return t<=.04045?t/12.92:((t+.055)/1.055)**2.4}function me({r:e,g:t,b:o}){return .2126*ft(e)+.7152*ft(t)+.0722*ft(o)}function G(e,t){let[o,a]=[me(e),me(t)].sort((n,r)=>r-n);return(o+.05)/(a+.05)}function q(e){return(e>=10?(Math.floor(e*10)/10).toFixed(1):(Math.floor(e*100)/100).toFixed(2)).replace(/\.0+$/,"")}function O(e,t){let o=e.a+t.a*(1-e.a),a=(n,r)=>(n*e.a+r*t.a*(1-e.a))/(o||1);return{r:a(e.r,t.r),g:a(e.g,t.g),b:a(e.b,t.b),a:o}}var Oe=new WeakMap,gt=new WeakMap,Ne=null,Me=null,fe=null,bt=new WeakMap,wt=new WeakMap,yt=new WeakMap,ho=Symbol("background-image in chain");function go(){Oe=new WeakMap,gt=new WeakMap,Ne=null,Me=null,fe=null,bt=new WeakMap,wt=new WeakMap,yt=new WeakMap}function xt(e){for(let t=e;t;t=t.parentElement){let o=wt.get(t);if(o===void 0){let a=getComputedStyle(t);if(o=!1,a.clip!=="auto"&&a.position!=="static"){let n=/rect\(([^)]+)\)/.exec(a.clip);if(n){let r=n[1].split(",").map(i=>i.trim());if(!r.includes("auto")&&r.length===4){let[i,s,l,c]=r.map(parseFloat);(s-c<=0||l-i<=0)&&(o=!0)}}}if(!o&&a.clipPath&&a.clipPath!=="none"){let n=/inset\(([^)]+)\)/.exec(a.clipPath);if(n&&!n[1].includes("px")){let r=n[1].trim().split(/\s+/).map(parseFloat),[i,s=i,l=i,c=s]=r;(i+l>=100||c+s>=100)&&(o=!0)}}wt.set(t,o)}if(o)return!0}return!1}function bo(e){for(let t=e;t&&t.nodeType===1;t=t.assignedSlot??t.parentElement??t.getRootNode()?.host){let o=getComputedStyle(t);if(o.backgroundImage!=="none"&&!Ue(o.backgroundImage))return null;let a=W(o.backgroundColor);if(!(!a||a.a===0))return a.a>=1?o.backgroundColor:null}return null}var wo=new Set;function Ue(e){if(e.includes("gradient("))return!1;let t=[...e.matchAll(/url\(["']?(.*?)["']?\)/g)].map(o=>o[1]);return t.length>0&&t.every(o=>wo.has(o))}function vt(e){let t=getComputedStyle(e.documentElement).colorScheme??"",o=typeof matchMedia=="function"&&matchMedia("(prefers-color-scheme: dark)").matches;return/dark/.test(t)&&(!/light/.test(t)||o)?{r:18,g:18,b:18,a:1}:{r:255,g:255,b:255,a:1}}function yo(e,t){if(!e||e.nodeType!==1)return vt(t);if(Oe.has(e)){let n=Oe.get(e);return n===ho?null:n}let o,a=getComputedStyle(e);if(a.backgroundImage!=="none"&&!Ue(a.backgroundImage))o=null;else{let n=W(a.backgroundColor);if(n&&n.a>=1)o=n;else{let r=yo(e.assignedSlot??e.parentElement??e.getRootNode()?.host,t);o=r===null?null:n&&n.a>0?O(n,r):r}}return Oe.set(e,o===null?ho:o),o}function oe(e){return yo(e,e.ownerDocument)}function At(e){if(!Ne){Ne=new WeakMap;for(let t of typeof e.getAnimations=="function"?e.getAnimations():[]){if(t.playState!=="running")continue;let o=t.effect?.target;if(!o)continue;let a=t.effect.getKeyframes?.()??[];if(!a.some(s=>s.opacity!==void 0))continue;let n=null,r=t.effect.getComputedTiming?.()??{};if(Number.isFinite(r.endTime)&&(r.fill==="forwards"||r.fill==="both")){let s=[...a].reverse().find(c=>c.opacity!==void 0),l=parseFloat(s?.opacity);Number.isFinite(l)&&(n=l)}let i=Ne.get(o);(i===void 0||n!==null&&(i===null||n<i))&&Ne.set(o,n)}}return Ne}function je(e){let t=At(e.ownerDocument);for(let o=e;o&&o.nodeType===1;o=o.parentElement??o.getRootNode()?.host)if(t.has(o))return!0;return!1}function Ye(e){let t=At(e.ownerDocument),o=1;for(let a=e;a&&a.nodeType===1;a=a.parentElement??a.getRootNode()?.host)if(t.has(a)){let n=t.get(a);o*=n===null?1:n}else o*=parseFloat(getComputedStyle(a).opacity)||0;return o}function ve(e){if(!e||e.nodeType!==1)return 1;let t=gt.get(e);if(t===void 0){let o=e.parentElement??e.getRootNode()?.host??null;t=(parseFloat(getComputedStyle(e).opacity)||0)*ve(o),gt.set(e,t)}return t}function fo(e){let t=At(e.ownerDocument);if(t.has(e)){let a=t.get(e);return a===null?1:a}let o=parseFloat(getComputedStyle(e).opacity);return Number.isFinite(o)?o:1}function xo(e,t){let o=d=>d.parentElement??d.getRootNode()?.host??null,a=null,n=1;for(let d=e;d&&d.nodeType===1;d=o(d)){let u=fo(d);u<1&&(a=d,n*=u)}if(!a)return null;let r={...t},i={r:0,g:0,b:0,a:0},s=!1;for(let d=e;d&&d.nodeType===1;d=o(d)){let u=getComputedStyle(d);if(u.backgroundImage!=="none"&&!Ue(u.backgroundImage)||u.mixBlendMode&&u.mixBlendMode!=="normal"||u.filter&&u.filter!=="none"||u.backdropFilter&&u.backdropFilter!=="none")return null;let h=W(u.backgroundColor);h&&h.a>0&&(r=O(r,h),i=O(i,h),s=!0);let f=fo(d);if(f<1&&(r={...r,a:r.a*f},i={...i,a:i.a*f}),d===a)break}if(!s)return null;let l=o(a),c=l?oe(l):vt(e.ownerDocument);return c?{foreground:O(r,c),background:O(i,c),opacity:n,text:r,paint:i}:{unresolved:!0,opacity:n}}function vo(e){let t=e.ownerDocument;if(typeof t.elementsFromPoint!="function")return null;let o=W(getComputedStyle(e).backgroundColor);if(o&&o.a>=1)return null;let a=e.getBoundingClientRect();if(!a.width||!a.height)return null;let n=t.defaultView,r=Math.min(Math.max(a.left+a.width/2,0),n.innerWidth-1),i=Math.min(Math.max(a.top+a.height/2,0),n.innerHeight-1),s=t.elementsFromPoint(r,i),l=s.indexOf(e);if(l===-1)return"unverifiable";for(let c of s.slice(l+1)){if(c.contains(e)){let d=getComputedStyle(c);if(d.backgroundImage!=="none")return c;let u=W(d.backgroundColor);if(u&&u.a>=1)return null;continue}return c}return null}function Ao(e){for(let o of e.childNodes){if(o.nodeType!==3||!o.textContent.trim())continue;let a=e.ownerDocument.createRange();a.selectNodeContents(o);let n=a.getClientRects()[0];if(n&&n.width&&n.height)return{x:n.left+n.width/2,y:n.top+n.height/2}}let t=e.getBoundingClientRect();return t.width&&t.height?{x:t.left+t.width/2,y:t.top+t.height/2}:null}var ee=e=>typeof e=="string"&&e.endsWith("px")?parseFloat(e):NaN;function Tr(e){for(let t=e;t&&t.nodeType===1;t=t.parentElement){let o=getComputedStyle(t);if(o.position!=="static"||o.transform!=="none"||o.filter!=="none"||/paint|layout|strict|content/.test(o.contain??""))return t}return null}function Rr(e,t){let o=Tr(e);if(!o)return null;let a=getComputedStyle(o),n=o.getBoundingClientRect(),r=ee(a.borderLeftWidth)||0,i=ee(a.borderTopWidth)||0,s=n.left+r,l=n.top+i,c=n.width-r-(ee(a.borderRightWidth)||0),d=n.height-i-(ee(a.borderBottomWidth)||0),u=(ee(t.paddingLeft)||0)+(ee(t.paddingRight)||0)+(ee(t.borderLeftWidth)||0)+(ee(t.borderRightWidth)||0),h=(ee(t.paddingTop)||0)+(ee(t.paddingBottom)||0)+(ee(t.borderTopWidth)||0)+(ee(t.borderBottomWidth)||0),f=(L,F,x,$,R,P)=>{let I=ee(L),B=ee(F),V=ee(x);if(Number.isFinite(I)&&Number.isFinite(B))return[$+I,$+R-B];if(Number.isFinite(I)&&Number.isFinite(V))return[$+I,$+I+V+P];if(Number.isFinite(B)&&Number.isFinite(V)){let ue=$+R-B;return[ue-V-P,ue]}return null},m=f(t.left,t.right,t.width,s,c,u),b=f(t.top,t.bottom,t.height,l,d,h);if(!m||!b)return null;let[p,w]=m,[T,U]=b;if(t.transform&&t.transform!=="none"){let L=/^matrix\(([^)]+)\)$/.exec(t.transform);if(!L)return null;let[F,x,$,R,P,I]=L[1].split(",").map(Number);if(F!==1||x!==0||$!==0||R!==1)return null;p+=P,w+=P,T+=I,U+=I}return{left:p,top:T,right:w,bottom:U,empty:!(w>p)||!(U>T)}}function Nr(e){let t=bt.get(e);if(t!==void 0)return t;t=[];for(let o of["::before","::after"]){let a=getComputedStyle(e,o);if(a.content==="none"||a.display==="none"||a.visibility==="hidden")continue;let n=W(a.backgroundColor);if(!(a.backgroundImage!=="none"||(n?.a??0)>0)||a.position!=="absolute"&&a.position!=="fixed")continue;let i=parseFloat(a.opacity),s=Number.isFinite(i)?i:1,l=a.position==="absolute"?Rr(e,a):null;if(!l){let h=Math.min(1,s*(a.backgroundImage!=="none"?1:n.a));h>0&&t.push({film:h,fixed:a.position==="fixed"});continue}if(l.empty)continue;let c=n;c&&s<1&&(c={...c,a:c.a*s});let d=a.backgroundImage!=="none"?a.backgroundImage:null,u=d?{repeat:a.backgroundRepeat,size:a.backgroundSize,position:a.backgroundPosition}:null;t.push({rect:l,color:c,imageCss:d,imageMeta:u})}return bt.set(e,t),t}function Ke(e){let t=Ao(e);if(!t)return null;let o=null,a=null,n=!1,r=0,i=!1,s=!1;for(let l=e;l&&l.nodeType===1;l=l.parentElement??l.getRootNode()?.host){for(let c of Nr(l)){if(c.film){if(!c.fixed&&i)continue;r=1-(1-r)*(1-c.film);continue}if(n)continue;let{rect:d,color:u,imageCss:h}=c;if(!(t.x<d.left||t.x>d.right||t.y<d.top||t.y>d.bottom)){if(h){a={css:h,element:l,box:d,meta:c.imageMeta},n=!0,s=s||i;continue}!u||u.a===0||(o=o?O(o,u):u,s=s||i,o.a>=1&&(n=!0))}}if(!i){let c=getComputedStyle(l);(c.backgroundImage!=="none"&&!Ue(c.backgroundImage)||(W(c.backgroundColor)?.a??0)>=1)&&(i=!0)}}return a?{image:a,...r>0&&{film:r},...s&&{beyondPaint:s}}:o?{color:o,...r>0&&{film:r},...s&&{beyondPaint:s}}:r>0?{film:r}:null}function ko(e,t,o){let a=[G(e,t)],n=me(e),r=[];for(let i of[0,255]){let s={r:i,g:i,b:i,a:o},l=O(s,t);a.push(G(e,l)),a.push(G(O(s,e),l)),r.push(Math.sign(n-me(l)))}return{min:Math.min(...a),max:Math.max(...a),crossed:r[0]!==r[1]}}function $r(e,t,o){let a=t.getBoundingClientRect(),n=[];for(let r of e){if(r.contains(t)||t.contains(r))continue;let i=getComputedStyle(r),s=W(i.backgroundColor),l=(s?s.a:0)*(parseFloat(i.opacity)||1);if(!(i.backdropFilter&&i.backdropFilter!=="none")&&(l<.15||l>=1))continue;let d=r.getBoundingClientRect(),u=(Math.min(d.right,o.innerWidth)-Math.max(d.left,0))*(Math.min(d.bottom,o.innerHeight)-Math.max(d.top,0))>=.6*o.innerWidth*o.innerHeight,h=d.left<=a.left+1&&d.top<=a.top+1&&d.right>=a.right-1&&d.bottom>=a.bottom-1;u&&h&&n.push(r)}return n.length?n:null}function kt(e){let t=[];for(let o of e){let a=getComputedStyle(o);if(a.backdropFilter&&a.backdropFilter!=="none"||a.filter&&a.filter!=="none"||a.mixBlendMode&&a.mixBlendMode!=="normal"||a.backgroundImage!=="none")return null;let n=W(a.backgroundColor);if(!n)return null;let r=n.a*(parseFloat(a.opacity)||1);r<=0||t.push({...n,a:r})}return t.length?t:null}function Ct(e){let t=e.ownerDocument,o=t.defaultView;if(typeof t.elementsFromPoint!="function")return"unresolved";let a=Ao(e);if(!a)return"unresolved";if(a.x<0||a.y<0||a.x>=o.innerWidth||a.y>=o.innerHeight)return"offscreen";let n=t.elementsFromPoint(a.x,a.y),r=n.indexOf(e),i=r>0?$r(n.slice(0,r),e,o):null,s=null,l=[],c=W(getComputedStyle(e).backgroundColor);if(c&&c.a>0){if(c.a>=1)return{color:c,scrim:i};s=c,l.push(c)}if(r===-1)return"unresolved";let d=[],u=new Set(n);for(let m=e.parentElement??e.getRootNode()?.host;m&&m.nodeType===1;m=m.parentElement??m.getRootNode()?.host){if(u.has(m))continue;let b=m.getBoundingClientRect();if(a.x<b.left||a.x>=b.right||a.y<b.top||a.y>=b.bottom)continue;let p=getComputedStyle(m),w=W(p.backgroundColor);(w&&w.a>0||p.backgroundImage!=="none"&&!Ue(p.backgroundImage))&&d.push(m)}let h=[];for(let m of n.slice(r+1)){for(;d.length&&!d[0].contains(m);)h.push(d.shift());h.push(m)}h.push(...d);for(let m of h){if(/^(img|video|canvas|svg|picture|object|embed|iframe)$/i.test(m.tagName))return{image:m,scrim:i,overlays:l};let b=getComputedStyle(m);if(m.shadowRoot||b.mixBlendMode&&b.mixBlendMode!=="normal"||b.filter&&b.filter!=="none"||b.backdropFilter&&b.backdropFilter!=="none")return"unresolved";if(b.backgroundImage!=="none"){let T=Lr(m),U=L=>!L||a.x>=L.left&&a.x<L.right&&a.y>=L.top&&a.y<L.bottom;if(!T.length||T.some(U))return{image:m,scrim:i,overlays:l}}let p=W(b.backgroundColor);if(!p)return"unresolved";let w=parseFloat(b.opacity);if(w<1&&(p={...p,a:p.a*w}),p.a!==0){if(s=s?O(s,p):p,s.a>=1)return{color:s,scrim:i};l.push(p)}}let f=vt(t);return{color:s?O(s,f):f,scrim:i}}function Xe(e){if(!fe){fe={panels:[],veil:null};let t=e.defaultView,o=e.querySelectorAll("body *");if(o.length<=3e4)for(let a of o){let n=getComputedStyle(a);if(n.position==="static"&&n.cssFloat==="none")continue;let r=W(n.backgroundColor),i=a.getBoundingClientRect();if(r&&r.a>=1&&i.width>=24&&i.height>=12&&fe.panels.push({element:a,rect:i,color:r,hitTestBlind:n.pointerEvents==="none"}),!fe.veil&&(n.position==="fixed"||n.position==="sticky")&&n.visibility!=="hidden"){let s=(r?r.a:0)*(parseFloat(n.opacity)||1),l=n.backdropFilter&&n.backdropFilter!=="none";i.width>=.9*t.innerWidth&&i.height>=.9*t.innerHeight&&(l||s>=.15&&s<1)&&(fe.veil=a)}}}return fe.panels}function St(e){return Xe(e),fe.veil}function Co(e){if(!e||e==="none")return[];let t=[];for(let o of e.split(/,(?![^(]*\))/)){let a=/rgba?\([^)]*\)|#[0-9a-fA-F]{3,8}\b/.exec(o),n=[...o.replace(a?.[0]??"","").matchAll(/(-?\d*\.?\d+)px/g)].map(r=>parseFloat(r[1]));n.length<2||t.push({x:n[0],y:n[1],blur:n[2]??0,color:W(a?.[0]??"")})}return t}function So(e,t){let o=Math.max(2,t*.25),a=new Map;for(let n of Co(e)){if(!n.color||n.color.a<.95||n.blur>o||Math.abs(n.x)>o||Math.abs(n.y)>o)continue;let r=`${Math.round(n.color.r)},${Math.round(n.color.g)},${Math.round(n.color.b)}`,i=a.get(r)??{color:n.color,up:!1,down:!1,left:!1,right:!1};n.blur>=Math.max(Math.abs(n.x),Math.abs(n.y),.5)?i.up=i.down=i.left=i.right=!0:(n.x>0&&(i.right=!0),n.x<0&&(i.left=!0),n.y>0&&(i.down=!0),n.y<0&&(i.up=!0)),a.set(r,i)}for(let n of a.values())if(n.up&&n.down&&n.left&&n.right)return n.color;return null}function To(e,t){return Co(e).every(o=>!o.color||o.color.a<.15||o.blur>t*.5)}function Ir(e,t){let{left:o,top:a,right:n,bottom:r}=t,i=getComputedStyle(e).position;for(let s=e.parentElement;s&&i!=="fixed";s=s.parentElement){let l=getComputedStyle(s),c=l.position!=="static"||l.transform!=="none"||l.filter&&l.filter!=="none";if(i==="absolute"&&!c)continue;let d=u=>u==="hidden"||u==="clip";if(d(l.overflowX)||d(l.overflowY)){let u=s.getBoundingClientRect();if(d(l.overflowX)&&(o=Math.max(o,u.left),n=Math.min(n,u.right)),d(l.overflowY)&&(a=Math.max(a,u.top),r=Math.min(r,u.bottom)),n<=o||r<=a)return null}i=l.position}return{left:o,top:a,right:n,bottom:r,width:n-o,height:r-a,x:o,y:a}}function Tt(e){if(!Me){Me=[];for(let t of e.querySelectorAll("img, video, canvas, svg")){let o=Ir(t,t.getBoundingClientRect());o&&o.width>=8&&o.height>=8&&Me.push({element:t,rect:o,hitTestBlind:getComputedStyle(t).pointerEvents==="none"})}}return Me}function $e(e){let t=[];for(let o=e;o;o=o.parentElement){let a=getComputedStyle(o);if(a.backgroundImage!=="none")return{css:a.backgroundImage,element:o,overlays:t};let n=W(a.backgroundColor);if(n&&n.a>=1)return null;n&&n.a>0&&t.push(n)}return null}function ge(e,t){let o=e;for(let a=t.length-1;a>=0;a--)o=O(t[a],o);return o}var Er=e=>e.map(t=>`${t.r},${t.g},${t.b},${t.a}`).join(";"),mt=new Map,Wr=1024;function Ro(e){if(!e||!(e.width>0)||!(e.height>0))return null;let t=n=>Math.min(Wr,Math.max(32,Math.ceil(n/4))),o=t(e.width),a=t(e.height);return{width:o,height:a,reduced:e.width/o>4.5||e.height/a>4.5}}function De(e,t=[],o=null){let a=o?.width??32,n=o?.height??32,r=a===32&&n===32?"":`|${a}x${n}`,i=`${e}${t.length?`|${Er(t)}`:""}${r}`;if(mt.has(i))return mt.get(i);let s=new Promise(l=>{let c=new Image;c.crossOrigin="anonymous";let d=setTimeout(()=>l(null),1500);c.onload=()=>{clearTimeout(d);try{let u=document.createElement("canvas");u.width=a,u.height=n;let h=u.getContext("2d",{willReadFrequently:!0});h.drawImage(c,0,0,a,n);let f=h.getImageData(0,0,a,n).data,m=1,b=0,p=null,w=null,T=0,U=!1;for(let F=0;F<f.length;F+=4){if(f[F+3]<255&&(U=!0),f[F+3]<128)continue;T+=1;let x={r:f[F],g:f[F+1],b:f[F+2],a:1},$=t.length?ge(x,t):x,R=me($);R<m&&(m=R,p=$),R>b&&(b=R,w=$)}if(!T){wo.add(e),Oe=new WeakMap,l({transparent:!0,width:c.naturalWidth,height:c.naturalHeight});return}let L=!!o?.reduced&&(c.naturalWidth>a||c.naturalHeight>n);l(m<=b?{min:m,max:b,minColor:p,maxColor:w,hasAlpha:U,width:c.naturalWidth,height:c.naturalHeight,reduced:L}:null)}catch{l(null)}},c.onerror=()=>{clearTimeout(d),l(null)},c.src=e});return mt.set(i,s),s}function Lr(e){let t=getComputedStyle(e),o=xe(t.backgroundImage);if(!o.length)return[];let a=e.getBoundingClientRect(),n=xe(t.backgroundSize),r=xe(t.backgroundPosition),i=xe(t.backgroundRepeat),s=(l,c)=>l.length?l[c%l.length]:void 0;return o.map((l,c)=>{if(l==="none")return{left:0,top:0,right:0,bottom:0};if((s(i,c)??"repeat")!=="no-repeat")return null;let u=s(n,c)??"auto";if(u==="cover"||u==="contain")return null;let h=mo(u),f=(x,$)=>x?.endsWith("px")?parseFloat(x):x?.endsWith("%")?parseFloat(x)/100*$:null,m=f(h[0],a.width),b=f(h[1]??h[0],a.height);if(m===null||b===null||Number.isNaN(m)||Number.isNaN(b))return null;let p=mo(s(r,c)??"0% 0%"),w=(x,$,R)=>x?.endsWith("%")?parseFloat(x)/100*($-R):x?.endsWith("px")?parseFloat(x):null,T=w(p[0],a.width,m),U=w(p[1]??"50%",a.height,b);if(T===null||U===null||Number.isNaN(T)||Number.isNaN(U))return null;let L=a.left+T,F=a.top+U;return{left:L,top:F,right:L+m,bottom:F+b}})}function mo(e){let t=[],o=0,a="";for(let n of e){if(n==="("&&(o+=1),n===")"&&(o-=1),/\s/.test(n)&&o===0){a&&t.push(a),a="";continue}a+=n}return a&&t.push(a),t}function xe(e){if(!e||e==="none")return e==="none"?["none"]:[];let t=[],o=0,a="";for(let n of e){if(n==="("&&(o+=1),n===")"&&(o-=1),n===","&&o===0){t.push(a.trim()),a="";continue}a+=n}return a.trim()&&t.push(a.trim()),t}function No(e,t){let o=getComputedStyle(e);if(xe(o.backgroundImage).filter(n=>n!=="none").length!==1)return null;let a=e.getBoundingClientRect();return Rt({left:a.left,top:a.top,right:a.right,bottom:a.bottom},{repeat:o.backgroundRepeat,size:o.backgroundSize,position:o.backgroundPosition},t)}function Rt(e,t,o){if(!e||!t||t.repeat!=="no-repeat"||t.size==="cover"||t.size==="contain")return null;let a=e.right-e.left,n=e.bottom-e.top,r=(b,p,w)=>b?.endsWith("px")?parseFloat(b):b?.endsWith("%")?parseFloat(b)/100*p:w,i=t.size.split(" "),s=r(i[0],a,o?.width),l=r(i[1]??i[0],n,o?.height);if(!Number.isFinite(s)||!Number.isFinite(l))return null;let c=(b,p,w)=>b?.endsWith("%")?parseFloat(b)/100*(p-w):b?.endsWith("px")?parseFloat(b):null,d=t.position.split(" "),u=c(d[0],a,s),h=c(d[1]??"50%",n,l);if(u===null||h===null||Number.isNaN(u)||Number.isNaN(h))return null;let f=e.left+u,m=e.top+h;return{left:f,top:m,right:f+s,bottom:m+l}}function $o(e,t,o=[]){if(!e||!e.hasAlpha)return e;if(!t||t.a<1)return null;let a=o.length?ge(t,o):t,n=me(a);return{...e,min:Math.min(e.min,n),max:Math.max(e.max,n),minColor:n<e.min?a:e.minColor,maxColor:n>e.max?a:e.maxColor}}function Ze(e){return xe(e)}function Nt(e){let t=/^url\(([\s\S]*)\)$/.exec((e??"").trim());if(!t)return null;let o=t[1].trim(),a=o[0];return(a==='"'||a==="'")&&o.endsWith(a)&&(o=o.slice(1,-1).replace(new RegExp(`\\\\${a}`,"g"),a)),o}var Mr=/^(?:(?:to|at|from|in|circle|ellipse|closest-side|closest-corner|farthest-side|farthest-corner)\b|calc\(|-?\d|\.\d)/i;function $t(e){let t={colors:[],unparsed:0,translucent:!1,space:null},o=e.indexOf("("),a=e.lastIndexOf(")");if(o===-1||a<=o)return t;for(let n of xe(e.slice(o+1,a))){let r=/(?:^|\s)in\s+([a-z0-9-]+)/i.exec(n);r&&r[1].toLowerCase()!=="srgb"&&(t.space=r[1].toLowerCase());let i=null,s=/([a-z][a-z0-9-]*)\(/i.exec(n);if(s){let c=0;for(let d=s.index;d<n.length;d++)if(n[d]==="("&&(c+=1),n[d]===")"&&(c-=1,c===0)){i=n.slice(s.index,d+1);break}}else i=n.trim().split(/\s+/)[0]??"";if(!i||Mr.test(i))continue;let l=/^currentcolor$/i.test(i)?null:W(i);if(!l){t.unparsed+=1;continue}l.a<1&&(t.translucent=!0),t.colors.push(l)}return t}function It(e,t=[],o=16){let{colors:a,unparsed:n,translucent:r,space:i}=$t(e);if(!a.length||n||r||i)return null;let s=[];a.length===1&&s.push(a[0]);for(let f=0;f+1<a.length;f++){let m=a[f],b=a[f+1];for(let p=0;p<=o;p++){let w=p/o;s.push({r:m.r+(b.r-m.r)*w,g:m.g+(b.g-m.g)*w,b:m.b+(b.b-m.b)*w,a:1})}}let l=s.map(f=>t.length?ge(f,t):f),c=1/0,d=-1/0,u=null,h=null;for(let f of l){let m=me(f);m<c&&(c=m,u=f),m>d&&(d=m,h=f)}return{min:c,max:d,minColor:u,maxColor:h,sampled:!0}}function He(e,t){let[o,a]=e>t?[e,t]:[t,e];return(o+.05)/(a+.05)}function Io(e,t,o){let a=me(e),r=a>=t.min&&a<=t.max?1:Math.min(He(a,t.min),He(a,t.max)),i=Math.max(He(a,t.min),He(a,t.max));return r>=o?{verdict:"pass",worst:r,best:i}:i<o?{verdict:"fail",worst:r,best:i}:{verdict:"mixed",worst:r,best:i}}function Or(e){let t=yt.get(e);if(t!==void 0)return t;t=!1;let o=n=>{for(let r of n){if(r.selectorText&&/::?first-(?:line|letter)\b/.test(r.selectorText))return!0;let i=r.cssRules??r.styleSheet?.cssRules;if(i&&o(i))return!0}return!1},a=[...e.styleSheets??[],...e.adoptedStyleSheets??[]];for(let n of a)try{if(o(n.cssRules)){t=!0;break}}catch{t=!0;break}return yt.set(e,t),t}function Eo(e,t){if(t.display==="inline"||t.display==="contents")return[];if(!Or(e.getRootNode()))return[];let o=r=>r.webkitTextFillColor&&r.webkitTextFillColor!==r.color?r.webkitTextFillColor:r.color,a=o(t),n=[];for(let r of["::first-line","::first-letter"]){let i=getComputedStyle(e,r),s=o(i);if(!s||s===a)continue;let l=W(s);l&&n.push({pseudo:r,color:l,style:i})}return n}function Ie(e){let t=parseFloat(e.fontSize),o=parseInt(e.fontWeight,10)||400;return t>=24||t>=56/3&&o>=700}var Wo=e=>Ze(e??"").map(Nt).find(Boolean)??null,Je=e=>e?{width:e.right-e.left,height:e.bottom-e.top}:null,Ur=/^([|¦·•∙‧/⁄\\‐‑‒–—―⁃«»‹›-])\1*$/,Et=e=>String(Math.round(e*100)/100),Ae=e=>`rgb(${Et(e.r)}, ${Et(e.g)}, ${Et(e.b)})`,Ee=(e,t)=>({status:"incomplete",message:"This text sits behind a translucent full-page overlay (a modal or loading veil), which changes its presented contrast"+(e&&t?`: ${q(e)}:1 at rest, ${q(t)}:1 as presented through the overlay, which straddles the threshold`:"")+" \u2014 judge the page with the overlay dismissed, or the dimmed state by eye if the overlay is permanent."}),Dr='button, a[href], input, select, textarea, fieldset, optgroup, option, [role="button"], [role="link"], [role="checkbox"], [role="radio"], [role="switch"], [role="tab"], [role="menuitem"], [role="menuitemcheckbox"], [role="menuitemradio"], [role="option"], [role="slider"], [role="spinbutton"], [role="textbox"], [role="searchbox"], [role="combobox"], [role="gridcell"], [role="treeitem"], [role="scrollbar"], [role="separator"], [role="application"], [role="group"], [role="listbox"], [role="menu"], [role="menubar"], [role="radiogroup"], [role="tablist"], [role="tree"], [role="treegrid"], [role="grid"], [role="toolbar"]';function Fr(e){for(let o=e;o;o=o.parentElement){let a=o.closest(":disabled");if(!a)break;if(a.tagName!=="FIELDSET")return!0;let n=e.closest("legend");if(!n||n.parentElement!==a||a.querySelector(":scope > legend")!==n)return!0;o=a}let t=e.closest('[aria-disabled="true"]');return!!(t&&t.matches(Dr))}function Pr(e){return e.matches(':disabled, [aria-disabled="true"], [tabindex="-1"]')||e.closest("[inert]")?!0:!e.matches("button, a[href], input, select")&&!e.hasAttribute("tabindex")&&!e.isContentEditable}function Br(e){let t=e.ownerDocument;if(typeof t.elementsFromPoint!="function")return!1;let o=e.textContent.trim();if(!o)return!1;let a=e.getBoundingClientRect(),n=t.defaultView,r=a.left+a.width/2,i=a.top+a.height/2;if(r<0||i<0||r>=n.innerWidth||i>=n.innerHeight)return!1;let s=t.elementsFromPoint(r,i),l=s.indexOf(e);return l<=0?!1:s.slice(0,l).some(c=>!c.contains(e)&&!e.contains(c)&&c.textContent.trim()===o)}function ke(e,t){let o=e.getBoundingClientRect();if(Math.min(o.right,t.right)-Math.max(o.left,t.left)<3||Math.min(o.bottom,t.bottom)-Math.max(o.top,t.top)<3)return!1;let a=e.ownerDocument.createRange();for(let n of e.childNodes)if(!(n.nodeType!==Node.TEXT_NODE||!n.textContent.trim())){a.selectNodeContents(n);for(let r of a.getClientRects()){let i=Math.min(r.right,t.right)-Math.max(r.left,t.left),s=Math.min(r.bottom,t.bottom)-Math.max(r.top,t.top);if(i>=3&&s>=3)return!0}}return!1}async function Gr(e,t,o){let a=Wo(e.css),n=null;if(a)try{n=await De(new URL(a,o.baseURI).href)}catch{}let r=!n||!n.width||n.width===300&&n.height===150,i=!a&&e.css.includes("gradient("),s=!1;if(i){let d=[...(getComputedStyle(e.element).backgroundSize??"").matchAll(/(\d+(?:\.\d+)?)px/g)].map(u=>parseFloat(u[1]));s=d.length>0&&d.every(u=>u<=40)}let l=No(e.element,r?null:n);return l?{relation:ke(t,l)?"under":"clear",intrinsic:n,dimensionless:r,isGradient:i,sizedSmall:s,paint:l}:{relation:"unknown",intrinsic:n,dimensionless:r,isGradient:i,sizedSmall:s,paint:null}}function qr(e,t,o){if(o?.a===0||parseFloat(t.fontSize)===0||parseFloat(t.opacity)===0)return!0;let a=parseFloat(t.textIndent)||0,n=/hidden|clip/.test(`${t.overflow} ${t.overflowX} ${t.overflowY}`)||t.clipPath!=="none"||t.clip!=="auto"&&t.position==="absolute";if(!n&&Math.abs(a)<=1e3)return xt(e);let r=e.getBoundingClientRect();return(r.width<=1||r.height<=1)&&n||Math.abs(a)>r.width&&(n||Math.abs(a)>1e3)?!0:xt(e)}function Lo(e,t,o,a,n=!1){if(!e)return null;let{verdict:r,worst:i,best:s}=Io(t,e,o);return r==="pass"?n?{status:"incomplete",message:`The ${a} behind this text is too large to sample at glyph scale, so detail narrower than the sampling grid could sit under the text unseen. Every sampled pixel passes (worst ${q(i)}:1), so confirm by eye.`}:{status:"pass"}:r==="fail"?{status:"fail",message:`Contrast against the ${a} behind this text is at best ${q(s)}:1 \u2014 below the ${o}:1 minimum against every part of it.`,fix:"Change the text colour, or place a solid overlay between the text and the image."}:{status:"incomplete",message:`The ${a} behind this text has both light and dark areas (contrast ranges ${q(i)}\u2013${q(s)}:1 for this text) \u2014 whether it passes depends on the exact overlap, check by eye.`}}function zr(e,t){if(Ke(e)?.color?.a>0)return!0;let a=e.ownerDocument;if(typeof a.elementsFromPoint!="function")return!1;let n=e.getBoundingClientRect(),r=a.defaultView,i=n.left+n.width/2,s=n.top+n.height/2;if(i<0||s<0||i>=r.innerWidth||s>=r.innerHeight)return!1;let l=a.elementsFromPoint(i,s),c=l.indexOf(e);return c===-1?!1:l.slice(c+1).some(d=>{if(d.contains(e)||d===t)return!1;let u=W(getComputedStyle(d).backgroundColor);return u&&u.a>0})}var Fe=Symbol("fully transparent image layer");function _r(e){let t=W(getComputedStyle(e).backgroundColor);if(t&&t.a>=1)return t;let o=e.parentElement??e.getRootNode()?.host??null,a=o?oe(o):null;return a?t&&t.a>0?O(t,a):a:null}async function Qe(e,t,o,a,n=[],r=null,i=null){if(!e)return null;let s=null,l="image",c=Ro(i);if(e.tagName==="IMG")s=await De(e.currentSrc||e.src,n,c);else{let d=e.css??getComputedStyle(e).backgroundImage;if(!d||d==="none")return null;let u=Ze(d).filter(m=>m!=="none");if(u.length>1)return null;let h=u[0]??"",f=Nt(h);if(f){let m;try{m=new URL(f,a.baseURI).href}catch{return null}s=await De(m,n,c)}else if(h.includes("gradient(")){l="gradient";let m=$t(h);if(!m.colors.length||m.unparsed)return{status:"incomplete",message:"The gradient behind this text has colour stops this engine could not parse, so its range of colours could not be sampled. Check the contrast by eye."};if(m.space)return{status:"incomplete",message:`The gradient behind this text interpolates in ${m.space}, not sRGB, so the colours between its stops cannot be computed here. Check the contrast by eye.`};if(m.translucent)return{status:"incomplete",message:"The gradient behind this text has translucent colour stops, so what shows behind the glyphs is a blend with whatever is painted beneath the gradient. Check the contrast by eye."};s=It(h,n)}else return null}if(s?.transparent)return Fe;if(s?.hasAlpha&&(s=$o(s,r,n),!s))return{status:"incomplete",message:`The ${l} behind this text has transparent regions, so parts of the text sit on whatever is painted beneath it, and that layer could not be resolved \u2014 check the contrast by eye.`};if(t.a<1){let d=[s?.minColor,s?.maxColor].filter(Boolean);if(d.length===2){let u=d.map(h=>G(O(t,h),h));if(u.every(h=>h<o)){let h=Math.max(...u);return{status:"fail",message:`This text is translucent, so it blends with what is behind it: against every part of that backdrop the blend reaches at best ${q(h)}:1, below the ${o}:1 minimum.`,fix:"Raise the text opacity, change its colour, or put a solid layer between the text and the backdrop.",data:{ratio:Number(q(h)),required:o}}}}return{status:"incomplete",message:"This text is translucent over an image or gradient, so its presented colour depends on the pixels beneath \u2014 contrast must be checked by eye."}}return!s&&l==="image"?{status:"incomplete",message:"The image painted behind this text can\u2019t be read: it is cross-origin, or it failed to load, so a script can\u2019t sample its colours. Check the contrast by eye."}:Lo(s,t,o,l,!!s?.reduced)}function Wt({id:e,tags:t,help:o,helpUrl:a,thresholds:n}){let r=(l,c,d)=>{if(c!==n.normal||d<n.large)return"";let u=parseFloat(l.fontSize)||0,h=parseInt(l.fontWeight,10)||400,f=h>=700?56/3:24,m=h>=700?"18.67px":"24px",b=Math.abs(u-f)<.05,p=b?`${u.toFixed(2)}px`:`${Math.round(u*10)/10}px`;return b&&parseFloat(p)>=parseFloat(m)&&(p=`just under ${m}`),h>=700&&u<18.666666666666668?` This text is bold at ${p}; bold text counts as large scale from 18.67px (14pt), where the ${n.large}:1 minimum would apply and these colours would pass.`:h<700&&u>=18.666666666666668&&u<24?` At ${p} regular weight this is not large scale; from 24px (18pt), or bold at this size, the ${n.large}:1 minimum would apply and these colours would pass.`:""},i={id:e,impact:"serious",tags:t,help:o,helpUrl:a,selector:"*:not(script):not(style):not(noscript):not(title):not(option):not(optgroup):not(input):not(textarea):not(select):not(svg):not(svg *):not(math):not(math *)",visibility:"visual",async evaluate(l,{ownText:c}){if(!c(l))return{status:"pass"};if(Fr(l))return{status:"pass"};let d=l;if(l.shadowRoot){let C=[...l.childNodes].find(N=>N.nodeType===3&&N.textContent.trim())?.assignedSlot??null;if(!C)return{status:"pass"};d=C}let u=getComputedStyle(d),h=u.display.startsWith("inline")||/flex|grid/.test(l.parentElement?getComputedStyle(l.parentElement).display:"");if(Ur.test(c(l))&&h&&!l.closest('code, pre, samp, kbd, var, a[href], button, [role="button"], [role="link"], [role="menuitem"], [role="tab"], [role="option"]')){let k=(C,N)=>{for(let E=C;E;E=E[N])if(E.nodeType===3&&E.textContent.trim()||E.nodeType===1&&(E.textContent.trim()||E.matches("img, svg, video, canvas, input, select, button")))return!0;return!1};if(k(l.previousSibling,"previousSibling")&&k(l.nextSibling,"nextSibling"))return{status:"pass"}}let f=l.closest('button, a[href], input, select, [role="button"], [role="link"], [role="option"], [role="tab"]');if(f&&getComputedStyle(f).pointerEvents==="none"&&Pr(f))return(f.textContent||"").trim().length<=80?{status:"pass"}:{status:"incomplete",message:"This text sits inside a non-interactive (pointer-events: none) control with substantial content \u2014 a disabled control's dimmed text is exempt from contrast, but an inactive content slide is not. Judge which this is by eye."};let m=W(u.webkitTextFillColor);if(/\btext\b/.test(u.webkitBackgroundClip??"")||/\btext\b/.test(u.backgroundClip??"")){let k=oe(l.parentElement??l),C=u.backgroundImage!=="none"?It(Ze(u.backgroundImage)[0]??""):null;if(C&&k){let N=Ie(u)?n.large:n.normal;return Lo(C,k,N,"gradient painting this text")??{status:"incomplete",message:"This text is painted with its background layer (background-clip: text) \u2014 contrast must be checked by eye."}}return{status:"incomplete",message:"This text is painted with its background layer (background-clip: text) \u2014 contrast must be checked by eye."}}let p=m&&u.webkitTextFillColor!==u.color?m:W(u.color);if(qr(l,u,p))return{status:"pass"};if(!p)return{status:"incomplete",message:"The text colour could not be parsed \u2014 check contrast by eye."};let w=Ie(u)?n.large:n.normal,T=l.ownerDocument,U=null,L=p,F=w,x=Eo(l,u).map(({pseudo:k,color:C,style:N})=>({origin:k,color:C,required:Ie(N)?n.large:n.normal})),$=(k,C)=>k.reduce((N,E)=>{let ce=E.color.a<1?O(E.color,C):E.color,be=G(ce,C)/E.required;return!N||be<N.margin?{...E,margin:be}:N},null);if(x.length){let k=oe(d)??{r:255,g:255,b:255,a:1},C=$([{origin:null,color:p,required:w},...x],k);p=C.color,w=C.required,U=C.origin}let R=je(l)?Ye(l):ve(l);if(R<.05)return{status:"pass"};if(R<1){let C=(!u.textShadow||u.textShadow==="none")&&!(Ke(l)?.film>0)?xo(l,p):null;if(C?.unresolved)return{status:"incomplete",message:`This text and its own background render together at ${Math.round(C.opacity*100)}% opacity over an image or gradient, so the presented colours are a blend a script cannot resolve. Check the contrast by eye.`};if(C){let N=G(C.foreground,C.background),E=l.getBoundingClientRect(),ce=_=>Math.min(E.right,_.right)-Math.max(E.left,_.left)>=3&&Math.min(E.bottom,_.bottom)-Math.max(E.top,_.top)>=3;if(Tt(T).some(({element:_,rect:re})=>ce(re)&&!_.contains(l)&&!l.contains(_)&&ke(l,re)))return{status:"incomplete",message:"This text overlaps an image or video, so its real background can\u2019t be computed \u2014 contrast must be checked by eye."};if(Xe(T).some(({element:_,rect:re,color:we})=>ce(re)&&!_.contains(l)&&!l.contains(_)&&ke(l,re)&&G(O(C.text,we),O(C.paint,we))>=w!=N>=w))return{status:"incomplete",message:"This text overlaps a coloured block that isn\u2019t its DOM ancestor, so its real background is ambiguous \u2014 check contrast by eye."};let K=Ct(l),j=K?.scrim??null;if(!j&&K==="offscreen"){let _=St(T);_&&!_.contains(l)&&(j=[_])}if(j){let _=kt(j);if(!_)return Ee();let re=G(ge(C.foreground,_),ge(C.background,_));if(re>=w!=N>=w)return Ee(N,re)}if(N>=w)return{status:"pass"};if(!E.width||!E.height)return{status:"pass"};let z=Ae(C.foreground),te=Ae(C.background);return{status:"fail",message:`Contrast is ${q(N)}:1, below the ${w}:1 WCAG minimum for this text size.${r(u,w,N)}`,fix:`Darken the text or lighten the background until the ratio reaches ${w}:1 (currently ${z} on ${te}).`+(u.color!==z?` Your CSS writes the text as ${u.color}, so searching it for the sRGB values above won't find them.`:"")+` The gap is opacity: this element or an ancestor renders at ${Math.round(C.opacity*100)}% opacity, so the text and its own background are both blended toward what lies behind them, into the sRGB values above.`,data:{foreground:z,background:te,ratio:Number(q(N)),required:w}}}p={...p,a:p.a*R}}let P=$e(l);if(P){let{relation:k,intrinsic:C,dimensionless:N,isGradient:E,sizedSmall:ce,paint:be}=await Gr(P,l,T);if(k!=="clear"){let Se=P.element.getBoundingClientRect(),K=be?Je(be):{width:Math.max(Se.width,N?0:C.width),height:Math.max(Se.height,N?0:C.height)},j=await Qe(P,p,w,T,P.overlays,_r(P.element),K);if(j!==Fe){j&&zr(l,P.element)&&(j={status:"incomplete",message:"This text sits over a background image with a translucent overlay painted on top of it (a pseudo-element or positioned scrim), so the colour behind the glyphs is the blend of the two \u2014 check the contrast by eye."});let z=E?ce||Se.height<=40:N||C.width<=32&&C.height<=32||Se.height<=40;return j?.status==="fail"&&z?{status:"incomplete",message:"A small background icon is painted on this element \u2014 if it sits beside the text rather than under it, judge the contrast against the plain background by eye."}:j??{status:"incomplete",message:"The background here is several stacked image or gradient layers, which a script can\u2019t sample. Contrast must be checked by eye."}}}}let I=oe(d),B=Ct(l),V=null,ue=B?.scrim??null;if(!ue&&B==="offscreen"){let k=St(T);k&&!k.contains(l)&&(ue=[k])}if(ue&&(V=kt(ue),!V))return Ee();let g=Ke(l),v=g?.film??0,A=g?.color||g?.image?g:null,S=!1;if(A?.image?.meta&&A.image.box){let k=Wo(A.image.css);if(k&&A.image.meta.repeat==="no-repeat"){let C=null;try{C=await De(new URL(k,T.baseURI).href)}catch{}let N=!C||!C.width||C.width===300&&C.height===150,E=Rt(A.image.box,A.image.meta,N?null:C);E&&!E.empty&&!ke(l,E)?A=null:S=N||C.width<=32&&C.height<=32}}let M={status:"incomplete",message:"A pseudo-element with its own background paints in this element's chain, but its position can't be computed \u2014 so whether it sits behind this text is unknown. Check the contrast by eye."};if(v>0&&(A?.image||B?.image||B==="unresolved"||V||u.textShadow&&u.textShadow!=="none"))return M;if(V&&(A?.image||B?.image))return Ee();if(A?.image&&A.beyondPaint)return{status:"incomplete",message:"An ancestor's pseudo-element paints an image or gradient where this text is, but other backgrounds sit between them \u2014 which one is behind the glyphs depends on paint order, so check the contrast by eye."};if(A?.image){let k=await Qe(A.image,p,w,T,[],oe(A.image.element),Je(A.image.box));if(k!==Fe)return k?.status==="fail"&&S?{status:"incomplete",message:"A small icon is painted by a pseudo-element on this element \u2014 if it sits beside the text rather than under it, judge the contrast against the plain background by eye."}:k??{status:"incomplete",message:"A pseudo-element paints an image or gradient behind this text, so its real background isn\u2019t the computed colour \u2014 contrast must be checked by eye."}}if(B?.image&&!A){let k=await Qe(B.image,p,w,T,B.overlays??[],null,Je(B.image.getBoundingClientRect()));if(k!==Fe)return k??{status:"incomplete",message:"An image or overlapping element is painted behind this text, so its real background isn\u2019t the computed colour \u2014 contrast must be checked by eye."}}let X=!1;if(B?.color)I=B.color,X=!0;else if(B==="unresolved"){let k=vo(l);if(k&&k!=="unverifiable"){let C=await Qe(k,p,w,T,[],null,Je(k.getBoundingClientRect()));if(C!==Fe)return C??{status:"incomplete",message:"An image or overlapping element is painted behind this text, so its real background isn\u2019t the computed colour \u2014 contrast must be checked by eye."}}}if(A?.color&&A.beyondPaint){let k=A.color.a>=1||!I?A.color:O(A.color,I);if(!I)return{status:"incomplete",message:"The background could not be determined \u2014 check contrast by eye."};let C=p.a<1?O(p,I):p,N=p.a<1?O(p,k):p;if(G(C,I)>=w!=G(N,k)>=w)return{status:"incomplete",message:"An ancestor's pseudo-element paints where this text is, but other backgrounds sit between them \u2014 which colour is behind the glyphs depends on paint order, and the two candidates disagree on the verdict. Check the contrast by eye."}}else A?.color&&(I=A.color.a>=1||!I?A.color:O(A.color,I),X=!0);if(!I)return{status:"incomplete",message:"The background could not be determined \u2014 check contrast by eye."};if(x.length){let k=E=>R<1?{...E,a:E.a*R}:E,C={origin:null,color:k(L),required:F},N=$([C,...x.map(E=>({...E,color:k(E.color)}))],I);p=N.color,w=N.required,U=N.origin}p.a<1&&(p=O(p,I));let H=(k,C)=>{let N=W(u.backgroundColor);if(N&&N.a>=1)return null;let E=l.getBoundingClientRect(),ce=K=>Math.min(E.right,K.right)-Math.max(E.left,K.left)>=3&&Math.min(E.bottom,K.bottom)-Math.max(E.top,K.top)>=3;if(Tt(T).some(({element:K,rect:j,hitTestBlind:z})=>(!C||z)&&ce(j)&&!K.contains(l)&&!l.contains(K)&&ke(l,j)))return{status:"incomplete",message:"This text overlaps an image or video, so its real background can\u2019t be computed \u2014 contrast must be checked by eye."};if(Xe(T).some(({element:K,rect:j,color:z,hitTestBlind:te})=>(!C||te)&&ce(j)&&!K.contains(l)&&!l.contains(K)&&ke(l,j)&&G(p,z)>=w!=(k==="pass")))return{status:"incomplete",message:"This text overlaps a coloured block that isn\u2019t its DOM ancestor, so its real background is ambiguous \u2014 check contrast by eye."};if(!C){let K=!1,j=null;for(let z=l;z&&z.nodeType===1&&z!==T.body;z=z.parentElement??z.getRootNode()?.host){let te=getComputedStyle(z).position;if(z!==l&&te!=="static"){j=z;break}(te==="absolute"||te==="fixed")&&(K=!0)}if(K&&j){let z=j.querySelectorAll("*");if(z.length<=2e3)for(let te of z){if(te===l||te.contains(l)||l.contains(te))continue;let _=getComputedStyle(te);if(_.position!=="static"||_.visibility==="hidden")continue;let re=W(_.backgroundColor);if(!re||re.a<1)continue;let we=te.getBoundingClientRect();if(!(we.width<24||we.height<12||!ce(we)||!ke(l,we))&&G(p,re)>=w!=(k==="pass"))return{status:"incomplete",message:"This text is positioned over a coloured box that isn\u2019t its DOM ancestor, so its real background is ambiguous \u2014 check contrast by eye."}}}}return null},D=G(p,I);if(v>0){let k=ko(p,I,v);if(!(k.min>=w==k.max>=w&&!(k.crossed&&k.min>=w)))return M}let ae=(k,C)=>G(ge(k,V),ge(C,V));if(V){let k=ae(p,I);if(k>=w!=D>=w)return Ee(D,k)}if(D>=w)return H("pass",X)??{status:"pass"};let Z=l.getBoundingClientRect();if(!Z.width||!Z.height)return{status:"pass"};{let k=T.defaultView;if(k&&getComputedStyle(T.documentElement).direction!=="rtl"&&(Z.right+k.scrollX<=0||Z.bottom+k.scrollY<=0))return{status:"pass"}}if(u.textShadow&&u.textShadow!=="none"){let k=parseFloat(u.fontSize)||16,C=So(u.textShadow,k);if(C){let N=G(p,C);if(V){let E=ae(p,C);if(E>=w!=N>=w)return Ee(N,E)}return N>=w?{status:"pass"}:{status:"fail",message:`Contrast is ${q(N)}:1 against this text's own outline and ${q(D)}:1 against the background \u2014 below the ${w}:1 WCAG minimum for this text size.`,fix:`Increase the difference between the text colour and its text-shadow outline (or the background) until the ratio reaches ${w}:1.`,data:{foreground:Ae(p),background:Ae(C),ratio:Number(q(N)),required:w}}}if(!To(u.textShadow,k))return{status:"incomplete",message:`Contrast against the background is ${q(D)}:1, but this text has a text-shadow \u2014 if the shadow forms a solid halo, contrast may be measured against it instead. Check by eye.`}}if(Math.round(p.r)===Math.round(I.r)&&Math.round(p.g)===Math.round(I.g)&&Math.round(p.b)===Math.round(I.b))return{status:"incomplete",message:"This text is exactly the same colour as its background \u2014 invisible. If that's a hiding technique for screen-reader-only labels, use the clip pattern instead; if the text is meant to be seen, this is a serious defect."};if(Br(l))return{status:"pass"};{let k=H("fail",X);if(k)return k}let le=Ae(p),ne=Ae(I),Ce=(k,C)=>{let N=W(k);return N!=null&&N.a>=1&&Ae(N)===C},ie=!U&&u.color!==le?u.color:null,Bn=U?` The judged text colour comes from a ${U} rule on this element, which paints that part of the text in place of its color.`:"",et=bo(l),We=et&&et!==ne?et:null,tt=ie&&Ce(ie,le)?ie:null,zt=We&&Ce(We,ne)?We:null,_t=[ie&&`the text as ${ie}`,We&&`the background as ${We}`].filter(Boolean).join(" and "),Vt=ie&&!tt?W(ie):null,Gn=Vt?R<1?` The gap is opacity: this element or an ancestor renders at ${Math.round(R*100)}% opacity, dimming the text to the sRGB value above.`:Vt.a<1?" The text colour is translucent, so it composites with the background into the sRGB value above.":"":"",qn=r(u,w,D);return{status:"fail",message:`Contrast is ${q(D)}:1 \u2014 below the ${w}:1 WCAG minimum for this text size.${qn}`,fix:`Darken the text or lighten the background until the ratio reaches ${w}:1 (currently ${le} on ${ne}).`+(_t?` Your CSS writes ${_t}, so searching it for the sRGB values above won't find them.`:"")+Gn+Bn,data:{foreground:le,background:ne,...tt?{foregroundCss:tt}:{},...zt?{backgroundCss:zt}:{},ratio:Number(q(D)),required:w}}}},s=i.evaluate.bind(i);return i.evaluate=async(l,c)=>{let d=await s(l,c);return d.status==="fail"&&l.closest('[aria-hidden="true"]')&&(d.message+=" aria-hidden hides this from screen readers, not from sighted users; contrast is judged for the people who see it."),d},i}var Mo=Wt({id:"color-contrast",name:"Text contrast",tags:["wcag2aa","wcag143"],help:"Text must have sufficient contrast against its background",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html",thresholds:{normal:4.5,large:3}});var Oo={id:"frame-title",name:"Frame titles",impact:"serious",tags:["wcag2a","wcag412"],help:"Frames must have a title describing their content",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/name-role-value.html",selector:"iframe, frame",evaluate(e,{accessibleName:t}){return e.getAttribute("title")?.trim()||t(e)?{status:"pass"}:{status:"fail",message:"This frame has no title \u2014 screen-reader users cannot tell what it contains before entering it.",fix:`Add title="\u2026" describing the embedded content, e.g. <iframe title="Newsletter signup" src="${e.getAttribute("src")??""}">.`}}};var Uo=["aria-labelledby","aria-describedby","aria-controls","aria-activedescendant","aria-owns","aria-errormessage","aria-details"],Vr=new Set(["button","checkbox","gridcell","link","menuitem","menuitemcheckbox","menuitemradio","option","progressbar","radio","scrollbar","searchbox","separator","slider","spinbutton","switch","tab","tabpanel","textbox","treeitem","combobox","grid","listbox","menu","menubar","radiogroup","tablist","tree","treegrid","dialog","alertdialog"]),Hr='a[href], button, input:not([type="hidden"]), select, textarea, summary, audio[controls], video[controls], [contenteditable="true"]';function jr(e){let t=e.tagName.toLowerCase();if(t==="img"||t==="area")return e.getAttribute("alt")?.trim()??"";let o={svg:"title",table:"caption",fieldset:"legend",figure:"figcaption"}[t];return o?[...e.children].find(a=>a.tagName.toLowerCase()===o)?.textContent.trim()??"":""}function Yr(e){return e.matches(Hr)||Vr.has(Y(e))?!0:e.tabIndex>=0}function Kr(e){let t=e.getRootNode(),o=e.getAttribute("aria-expanded")==="false",a=[],n=[];for(let r of Uo)for(let i of(e.getAttribute(r)??"").split(/\s+/).filter(Boolean)){if(!t.getElementById?.(i)){if(r==="aria-controls"&&o)continue;a.push({attr:r,id:i});continue}let s=t.querySelectorAll(`[id="${CSS.escape(i)}"]`).length;s>1&&n.push(`${r}="${i}" \u2014 ${s} elements share this id; the reference binds to the FIRST one in the DOM`)}return{missing:a,ambiguous:n}}function Xr({missing:e,ambiguous:t},o,a){if(e.length){let n=Yr(o),r=n?(a?.(o)??"").trim():(J(o)||o.getAttribute("aria-label")||jr(o)||o.getAttribute("title")||"").trim(),i=e.filter(({attr:d})=>!(d==="aria-labelledby"&&r));if(!i.length)return{status:"pass"};let s=({attr:d,id:u})=>d==="aria-labelledby"?`aria-labelledby="${u}" points to nothing and leaves this element without an accessible name`:`${d}="${u}" points to nothing \u2014 assistive technology silently ignores it`;if(i.filter(({attr:d})=>d!=="aria-describedby"&&(d!=="aria-labelledby"||n)).length)return{status:"fail",message:`Broken ARIA references: ${i.map(s).join("; ")}.`,fix:"Correct or remove the reference, or give the target element that id."};let c=i.filter(({attr:d})=>d==="aria-labelledby");if(c.length){let d=o.tagName.toLowerCase();return{status:"incomplete",message:`${c.map(({id:u})=>`aria-labelledby="${u}" points to nothing`).join("; ")}, so this <${d}> has no accessible name. It is not a user interface component, so 4.1.2 does not require one, but the reference was meant to label it: correct it, or remove it if the label was never needed.`,fix:"Correct or remove the reference, or give the target element that id."}}return{status:"incomplete",message:`${i.map(s).join("; ")} at rest. The element keeps its name and role, so this is not a proven 4.1.2 failure: if the description is created when the element is hovered or focused (a tooltip library), this works; otherwise correct the reference.`,fix:"Correct or remove the reference, or give the target element that id."}}return t.length?{status:"incomplete",message:`Ambiguous ARIA references: ${t.join("; ")}. If the first copy in the DOM is the intended target, this works today \u2014 but it's fragile.`}:{status:"pass"}}var Do={id:"aria-valid-refs",name:"ARIA id references",impact:"moderate",tags:["wcag2a","wcag412"],help:"ARIA id references must point to elements that exist",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/name-role-value.html",selector:Uo.map(e=>`[${e}]`).join(", "),evaluate(e,{accessibleName:t}={}){return Xr(Kr(e),e,t)}};var Fo=new Set(["alert","alertdialog","application","article","banner","blockquote","button","caption","cell","checkbox","code","columnheader","combobox","complementary","contentinfo","definition","deletion","dialog","directory","document","emphasis","feed","figure","form","generic","grid","gridcell","group","heading","img","insertion","link","list","listbox","listitem","log","main","marquee","math","menu","menubar","menuitem","menuitemcheckbox","menuitemradio","meter","navigation","none","note","option","paragraph","presentation","progressbar","radio","radiogroup","region","row","rowgroup","rowheader","scrollbar","search","searchbox","separator","slider","spinbutton","status","strong","subscript","superscript","switch","tab","table","tablist","tabpanel","term","textbox","time","timer","toolbar","tooltip","tree","treegrid","treeitem","image","comment","mark","suggestion"]);function Zr(e,t){let o=Array.from({length:e.length+1},(a,n)=>[n]);for(let a=1;a<=t.length;a++)o[0][a]=a;for(let a=1;a<=e.length;a++)for(let n=1;n<=t.length;n++)o[a][n]=Math.min(o[a-1][n]+1,o[a][n-1]+1,o[a-1][n-1]+(e[a-1]===t[n-1]?0:1));return o[e.length][t.length]}function Jr(e){let t=null,o=3;for(let a of e){let n=a.toLowerCase();for(let r of Fo){if(Math.abs(r.length-n.length)>2)continue;let i=Zr(n,r);i>0&&i<o&&(t=r,o=i)}}return t}var Po={id:"valid-role",name:"Valid roles",impact:"critical",tags:["wcag2a","wcag412"],help:"role attributes must use valid ARIA roles",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/name-role-value.html",selector:"[role]",evaluate(e){let t=e.getAttribute("role").trim().split(/\s+/).filter(Boolean),o=d=>{let u=d.toLowerCase();return Fo.has(u)||u.startsWith("doc-")||u.startsWith("graphics-")};if(t.some(o))return{status:"pass"};let a=e.tagName.toLowerCase(),n=Jr(t),r=n?` (did you mean role="${n}"?)`:"",i=n?`Correct the spelling to role="${n}", or remove the attribute to keep the element\u2019s native role.`:"Use a valid role from the ARIA specification, or remove the attribute to keep the element\u2019s native role.",s=a==="div"||a==="span"||a==="svg",l=e.tabIndex>=0||e.matches("a[href], button, input, select, textarea, summary"),c=[...ye(e)].some(d=>d.name.startsWith("aria-")&&d.name!=="aria-hidden");return s&&!l&&!c?{status:"pass"}:!l&&!c?{status:"incomplete",message:`role="${e.getAttribute("role")}" is not a valid ARIA role${r}, so assistive technology ignores it and exposes the element's native <${a}> semantics. Nothing is announced wrongly, but the author reached for a role that does not exist. Is the native <${a}> role the right one here? If a different role was intended, that role is missing.`,fix:i}:{status:"fail",message:`role="${e.getAttribute("role")}" is not a valid ARIA role${r}, so assistive technology ignores it${s?"":` and falls back to the element\u2019s native <${a}> semantics`}.`,fix:i}}};var Bo='a[href], area[href], button, input, select, textarea, summary, iframe, audio[controls], video[controls], [contenteditable]:not([contenteditable="false"]), [tabindex]',Go={id:"aria-hidden-focus",name:"Focusable hidden content",impact:"serious",tags:["wcag2a","wcag412"],help:'Nothing focusable may sit inside aria-hidden="true"',helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/name-role-value.html",selector:'[aria-hidden="true"]',visibleOnly:!1,evaluate(e,{isRendered:t}){let o=[e,...e.querySelectorAll(Bo)].filter(r=>r.matches?.(Bo)&&!(r.hasAttribute("tabindex")&&r.tabIndex<0)&&!r.matches(":disabled")&&!r.closest("[inert]")&&t(r)&&ve(r)>0);if(!o.length)return{status:"pass"};let a=e.ownerDocument,n=null;try{n=a.querySelector("dialog:modal")}catch{}return n??=[...a.querySelectorAll('[aria-modal="true"]')].find(t)??null,n&&!e.contains(n)&&!n.contains(e)?{status:"incomplete",message:`This aria-hidden element contains ${o.length} focusable element(s) while a modal dialog is open \u2014 fine if the modal traps keyboard focus, a failure if Tab can reach them. Check the trap by keyboard.`}:{status:"fail",message:`This aria-hidden element contains ${o.length} focusable element(s) \u2014 keyboard users can Tab into content that screen readers cannot see.`,fix:'Add tabindex="-1" (or disabled) to focusable elements inside aria-hidden regions, or remove aria-hidden.'}}};var Qr=new Set(["SCRIPT","TEMPLATE","STYLE","LINK","META"]),qo=["listitem","presentation","none"],zo={id:"list-structure",name:"List structure",impact:"serious",tags:["wcag2a","wcag131"],help:"Lists must only contain list items",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/info-and-relationships.html",selector:"ul, ol",evaluate(e,{isRendered:t}){if(e.hasAttribute("role")&&e.getAttribute("role")!=="list")return{status:"pass"};let o=i=>[...i.children].flatMap(s=>s.tagName==="SLOT"?s.assignedElements?.().length?s.assignedElements():[...s.children]:[s]),a=i=>{if(Qr.has(i.tagName)||t&&!t(i))return!0;let s=i.getAttribute("role")??"";return i.tagName==="LI"?!s||qo.includes(s):qo.includes(s)?!0:!i.hasAttribute("role")&&(i.tagName==="DIV"||i.tagName==="SPAN")?![...i.childNodes].some(d=>d.nodeType===Node.TEXT_NODE&&d.textContent.trim())&&o(i).every(a):!1},n=o(e).filter(i=>!a(i));if(!n.length){let i=o(e).filter(l=>l.tagName==="LI"&&(!t||t(l))),s=i.filter(l=>["presentation","none"].includes(l.getAttribute("role")??""));return i.length&&s.length===i.length?{status:"fail",message:`Every item in this list carries role="${s[0].getAttribute("role")}", so the list announces itself with zero items while ${i.length} are visible \u2014 screen readers lose the count and the positions.`,fix:'Remove the role from the <li> elements, or neutralise the whole structure by putting role="presentation" (or the intended widget role) on the list element itself.'}:{status:"pass"}}let r=[...new Set(n.map(i=>{let s=i.getAttribute("role");return`<${i.tagName.toLowerCase()}${s?` role="${s}"`:""}>`}))].join(", ");return{status:"fail",message:`This list has ${n.length} direct child(ren) without listitem semantics (${r}) \u2014 screen readers misreport the list's size and structure.`,fix:'Use plain <li> children (an explicit role like role="group" replaces the listitem role), or move non-item elements outside the list.'}}};var _o='a[href], button, input, select, textarea, summary, audio[controls], video[controls], [role="button"], [role="link"], [role="checkbox"], [role="radio"], [role="switch"], [role="menuitem"], [role="tab"], [role="option"]',Vo={id:"nested-interactive",name:"Nested controls",impact:"serious",tags:["wcag2a","wcag412"],help:"A control must not contain another control",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/name-role-value.html",selector:_o,evaluate(e,{isRendered:t}){let o="a[href], button, input, select, textarea, summary, audio[controls], video[controls]",a=[...e.querySelectorAll(_o)].find(n=>!n.matches(":disabled")&&!(n.hasAttribute("tabindex")&&n.tabIndex<0)&&!(n.tagName==="INPUT"&&n.type==="hidden")&&t(n)&&!n.closest('[aria-hidden="true"]')&&(n.matches(o)||n.hasAttribute("tabindex")));return a?e.tagName==="SUMMARY"?{status:"incomplete",message:`This summary contains another interactive element (<${a.tagName.toLowerCase()}>). HTML allows it and both controls keep their name and role, but the disclosure and the control inside it are separate keyboard stops in one label. Check that activating each does what a user expects.`}:{status:"fail",message:`This ${e.tagName.toLowerCase()} contains another interactive element (<${a.tagName.toLowerCase()}>) \u2014 focus order and announcements become unpredictable.`,fix:"Restructure so interactive elements are siblings, not ancestors of each other."}:{status:"pass"}}};var ei=new Set(["banner","complementary","contentinfo","form","main","navigation","region","search"]),ti=new Set(["form","region"]),oi="main, header, footer, nav, aside, section, form, [role]",ai=e=>!!(e.getAttribute("aria-label")?.trim()||e.getAttribute("aria-labelledby")?.trim()||e.getAttribute("title")?.trim()),ni=e=>{let t=Y(e);return ei.has(t)&&(!ti.has(t)||ai(e))},Ho={id:"bypass-blocks",name:"Skip to content",impact:"serious",tags:["wcag2a","wcag241"],help:"The page must offer a way to skip repeated blocks",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/bypass-blocks.html",selector:"html",visibleOnly:!1,evaluate(e,{isRendered:t}){if(Te(e.ownerDocument))return{status:"pass"};let o=e.ownerDocument,a=de(o),n=s=>a.some(l=>l.querySelector?.(s));if(a.some(s=>[...s.querySelectorAll?.(oi)??[]].some(ni)))return{status:"pass"};if(n('h1, h2, h3, h4, h5, h6, [role="heading"]'))return{status:"pass"};for(let s of a)for(let l of s.querySelectorAll?.('a[href^="#"]')??[]){let c=l.getAttribute("href").slice(1);if(c&&!(t&&!t(l))&&a.some(d=>d.getElementById?.(c)||d.querySelector?.(`[id="${CSS.escape(c)}"]`)))return{status:"pass"}}let r=[...o.querySelectorAll("iframe, frame")??[]];if(r.length&&r.every(s=>s.getAttribute("title")?.trim()))return{status:"pass"};let i=a.reduce((s,l)=>s+(l.querySelectorAll?.("a[href], button").length??0),0);return i<4?{status:"pass"}:{status:"fail",message:`This page has ${i} links and buttons but no landmark, heading, skip link, or titled frame \u2014 keyboard and screen-reader users must go through the whole header and nav to reach anything.`,fix:'Add a skip link like <a href="#content">Skip to content</a>, wrap primary content in <main>, or structure the page with headings.'}}};var Ko='a[href], button, input, select, [role="button"], [role="link"]';function ri(e,t){if(t.width<=1||t.height<=1||t.right<=0||t.bottom<=0)return!0;let o=e.ownerDocument.defaultView,a=e.ownerDocument.scrollingElement??e.ownerDocument.documentElement;if(o&&a&&(t.left+o.scrollX>=a.scrollWidth-1||t.top+o.scrollY>=a.scrollHeight-1))return!0;let n=getComputedStyle(e);return(parseFloat(n.opacity)||0)===0||n.clipPath!=="none"||n.clip!=="auto"&&n.position==="absolute"}function ii(e,t){let o=e.ownerDocument,a=o.defaultView;if(!a||typeof o.elementsFromPoint!="function")return[t];let n=[...e.getClientRects()].filter(l=>l.width>0&&l.height>0),r=n.length?n:[t],i=[],s=!1;for(let l of r){let c=l.left+l.width/2,d=l.top+l.height/2;if(c<0||d<0||c>=a.innerWidth||d>=a.innerHeight)continue;let u=o.elementsFromPoint(c,d),h=u.indexOf(e);if(h<0){e.getRootNode()===o&&(s=!0);continue}s=!0,u.slice(0,h).every(f=>f.contains(e)||e.contains(f))&&i.push(l)}return s?i:r}function Xo(e){for(let t of e.childNodes)if(t.nodeType===Node.TEXT_NODE&&t.textContent.trim()||t.nodeType===Node.ELEMENT_NODE&&!t.matches(Ko)&&Xo(t))return!0;return!1}function jo(e){let t=e;for(;t&&getComputedStyle(t).display.startsWith("inline");){for(let o of t.parentNode?.childNodes??[])if(o!==t&&(o.nodeType===Node.TEXT_NODE&&o.textContent.trim()||o.nodeType===Node.ELEMENT_NODE&&!o.matches(Ko)&&Xo(o)&&getComputedStyle(o).display.startsWith("inline")))return!0;t=t.parentElement}return!1}var si=/^(button|input|select|textarea)$/i,li=/^(width|height|(min|max)-(width|height|inline-size|block-size)|inline-size|block-size|padding(-.+)?|border(-.+)?|font(-.+)?|line-height|box-sizing|transform|scale|zoom|appearance|all)$/,Yo=new WeakMap;function Zo(e){for(let t=0;t<e.length;t++)if(li.test(e[t]))return!0;return!1}function ci(e){let t=[...e.styleSheets??[],...e.adoptedStyleSheets??[]],o=t.map(l=>{try{return l.cssRules.length}catch{return"x"}}).join(","),a=Yo.get(e);if(a?.key===o)return a;let n=(e.ownerDocument??e).defaultView,r=(e.ownerDocument??e).createElement("div"),i={key:o,selectors:[],open:!1},s=(l,c)=>{for(let d of l){if(d.media&&n?.matchMedia&&!n.matchMedia(d.media.mediaText).matches)continue;let u=d.selectorText??c;if(c&&d.selectorText&&(u=/&/.test(u)?u.replace(/&/g,`:is(${c})`):`:is(${c}) ${u}`),u&&d.style&&Zo(d.style))try{r.matches(u),i.selectors.push(u)}catch{i.open=!0}d.cssRules?.length&&s(d.cssRules,d.selectorText?u:c)}};for(let l of t)try{s(l.cssRules,null)}catch{i.open=!0}return i.combined=i.selectors.join(", "),Yo.set(e,i),i}function ui(e){if(!si.test(e.tagName)||e.type==="image"||Zo(e.style))return"no";let t=ci(e.getRootNode());return t.combined&&e.matches(t.combined)?"no":t.open?"open":"yes"}function di(e,t){let o=Math.max(t.left-e.x,0,e.x-t.right),a=Math.max(t.top-e.y,0,e.y-t.bottom);return Math.hypot(o,a)}function Lt({id:e,tags:t,help:o,helpUrl:a,min:n,spacingException:r}){let i=s=>{if(!(Math.round(s)>=n&&s<n))return String(Math.round(s));for(let l of[1,2]){let c=s.toFixed(l);if(Number(c)<n)return c}return`just under ${n}`};return{id:e,impact:"serious",tags:t,help:o,helpUrl:a,selector:'button, a[href], input:not([type="hidden"]), select, [role="button"], [role="link"]',visibility:"visual",evaluateAll(s){let l=g=>{let v=g.getBoundingClientRect(),A=g.labels?.[0];if(!A)return v;let S=A.getBoundingClientRect();if(!S.width||!S.height)return v;let M=Math.min(v.left,S.left),X=Math.min(v.top,S.top),H=Math.max(v.right,S.right),D=Math.max(v.bottom,S.bottom);return{left:M,top:X,right:H,bottom:D,width:H-M,height:D-X}},c=g=>g.tagName==="INPUT"&&(g.type==="checkbox"||g.type==="radio")&&getComputedStyle(g).appearance!=="none",d=s.map(l),u=d.map(g=>({x:g.left+g.width/2,y:g.top+g.height/2})),h=g=>{try{return typeof g.checkVisibility=="function"&&g.checkVisibility({visibilityProperty:!0})&&!g.checkVisibility({contentVisibilityAuto:!0,visibilityProperty:!0})}catch{return!1}},f=d.map((g,v)=>g.width>0&&g.height>0&&!pe(s[v])&&!ri(s[v],g)&&!h(s[v])),m=d.map((g,v)=>f[v]&&(g.width<n||g.height<n)),b=(g,v)=>g.left<=v.left&&g.right>=v.right&&g.top<=v.top&&g.bottom>=v.bottom,p=(g,v,A)=>{if(!b(v,A))return!1;let S=[...g.getClientRects()].filter(M=>M.width>0&&M.height>0);return S.length<=1||S.some(M=>b(M,A))},w=new Map,T=g=>{if(w.has(g))return w.get(g);let v=null;for(let A=g;A&&A!==g.ownerDocument.documentElement;A=A.parentElement){let S=getComputedStyle(A).position;if(S==="fixed"||S==="sticky"){v=A;break}}return w.set(g,v),v},U=(g,v)=>{let A=Math.max(g.left,v.left),S=Math.max(g.top,v.top),M=Math.min(g.right,v.right),X=Math.min(g.bottom,v.bottom);return M-A>2&&X-S>2?{left:A,top:S,right:M,bottom:X,width:M-A,height:X-S}:null},L=(g,v,A)=>{let S=s[v].ownerDocument,M=S.defaultView,X=A.left+A.width/2,H=A.top+A.height/2;if(!M||typeof S.elementsFromPoint!="function"||X<0||H<0||X>=M.innerWidth||H>=M.innerHeight)return!1;for(let D of S.elementsFromPoint(X,H)){let ae=s[g]===D||s[g].contains(D),Z=s[v]===D||s[v].contains(D);if(ae&&!Z)return!0;if(Z&&!ae)return!1}return!1},F=(g,v)=>{let A=[{left:g.left,top:g.top,right:v.left,bottom:g.bottom},{left:v.right,top:g.top,right:g.right,bottom:g.bottom},{left:g.left,top:g.top,right:g.right,bottom:v.top},{left:g.left,top:v.bottom,right:g.right,bottom:g.bottom}].map(S=>({...S,width:S.right-S.left,height:S.bottom-S.top})).filter(S=>S.width>=1&&S.height>=1);return A.length?A.reduce((S,M)=>M.width*M.height>S.width*S.height?M:S):{left:g.left,top:g.top,right:g.left,bottom:g.top,width:0,height:0}},x=256,$=null,R=g=>{let v=[];for(let A=Math.floor(g.left/x);A<=Math.floor(g.right/x);A++)for(let S=Math.floor(g.top/x);S<=Math.floor(g.bottom/x);S++)v.push(`${A}:${S}`);return v},P=g=>{$||($=new Map,d.forEach((A,S)=>{if(f[S])for(let M of R(A))$.has(M)||$.set(M,[]),$.get(M).push(S)}));let v=new Set;for(let A of R(d[g]))for(let S of $.get(A)??[])S!==g&&v.add(S);return v},I=g=>{let v=null;for(let A of P(g)){if(s[A].contains(s[g])||s[g].contains(s[A])||b(d[A],d[g])||b(d[g],d[A])||typeof s[g].href=="string"&&s[g].href===s[A].href)continue;let S=U(v??d[g],d[A]);S&&T(s[A])===T(s[g])&&L(A,g,S)&&(v=F(v??d[g],S))}return v},B=g=>{if(typeof g.href!="string"||!g.href)return null;let v=(g.getAttribute("href")??"").trim();return!v||v==="#"||/^javascript:/i.test(v)?null:g.href},V=null,ue=g=>{let v=B(s[g]);return v?(V||(V=new Set,s.forEach((A,S)=>{if(!f[S]||m[S]||A.ownerDocument!==s[g].ownerDocument)return;let M=B(A);M&&V.add(M)})),V.has(v)):!1};return s.map((g,v)=>{if(!f[v])return{status:"pass"};if(!m[v]){let M=I(v);if(!M||M.width>=n&&M.height>=n)return{status:"pass"};if(jo(g))return{status:"pass"};if(!(()=>{let D=g.ownerDocument,ae=D.defaultView;if(!ae||typeof D.elementsFromPoint!="function")return!0;let Z=[...g.getClientRects()].filter(ne=>ne.width>0&&ne.height>0),le=!1;for(let ne of Z.length?Z:[d[v]]){let Ce=ne.left+ne.width/2,ie=ne.top+ne.height/2;if(!(Ce<0||ie<0||Ce>=ae.innerWidth||ie>=ae.innerHeight)&&(le=!0,D.elementsFromPoint(Ce,ie).includes(g)))return!0}return!le})())return{status:"pass"};let H=d[v];return{status:"fail",message:`This target's box is ${Math.round(H.width)}\xD7${Math.round(H.height)}px, but another target overlaps it and clicks in the overlap go there \u2014 the part that still accepts the pointer is ${i(M.width)}\xD7${i(M.height)}px, below the ${n}\xD7${n}px minimum.`,fix:"Space the targets so they no longer overlap, or enlarge this one until the uncovered part reaches the minimum.",data:{box:{width:Math.round(H.width),height:Math.round(H.height)},effective:{width:Math.round(M.width),height:Math.round(M.height)}}}}if(c(g))return{status:"pass"};let A=ui(g);if(A==="yes")return{status:"pass"};if(jo(g))return{status:"pass"};if(r){let M=(H,D)=>{if(D===v||!f[D]||H.contains(g)||g.contains(H)||p(H,d[D],d[v])||p(g,d[v],d[D])||T(H)!==T(g))return!1;let ae=Z=>{if(m[D]){let le={x:Z.left+Z.width/2,y:Z.top+Z.height/2};if(Math.hypot(le.x-u[v].x,le.y-u[v].y)<n)return!0}return di(u[v],Z)<n/2};return ae(d[D])?ii(H,d[D]).some(ae):!1};if(!s.some(M))return{status:"pass"}}if(ue(v))return{status:"pass"};let S=d[v];return A==="open"?{status:"incomplete",message:`This native control is ${i(S.width)}\xD7${i(S.height)}px, under the ${n}\xD7${n}px minimum${r?", and another target crowds it":""}. The browser sizes an unstyled control (the User Agent Control exception), but a stylesheet this audit cannot read might set its size. Check whether author CSS sizes this control; if nothing does, it passes.`}:{status:"fail",message:r?`This target is ${i(S.width)}\xD7${i(S.height)}px AND another target crowds it (within ${n}px) \u2014 small targets are only acceptable with clear space around them; crowded ones are hard to hit for users with motor impairments.`:`This target is ${i(S.width)}\xD7${i(S.height)}px \u2014 below the ${n}\xD7${n}px minimum, hard to hit for users with motor impairments.`,fix:`Increase the element\u2019s size or padding to at least ${n}\xD7${n}px${r?", or add spacing between the targets":""}.`}})}}}var Jo=Lt({id:"target-size",name:"Target size",tags:["wcag22aa","wcag258"],help:"Interactive targets must be at least 24\xD724 pixels",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/target-size-minimum.html",min:24,spacingException:!0});var Qo={id:"audio-control",name:"Auto-playing audio control",impact:"critical",tags:["wcag2a","wcag142"],help:"Auto-playing audio must be stoppable or muted",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/audio-control.html",selector:"audio[autoplay], video[autoplay]",visibleOnly:!1,evaluate(e){if(e.muted||e.volume===0)return{status:"pass"};if(e.hasAttribute("muted")||e.hasAttribute("controls"))return{status:"pass"};if(!e.loop){if(Number.isFinite(e.duration)&&e.duration<=3)return{status:"pass"};if(!Number.isFinite(e.duration))return{status:"incomplete",message:"This media auto-plays without controls or muting; its duration isn\u2019t loaded yet, and only audio playing longer than 3 seconds fails 1.4.2 \u2014 check whether it has sound and how long it plays."}}let t=e.loop?"on a loop":"for more than three seconds",o="Add controls (or muted), or do not autoplay. Otherwise put a pause, stop or volume control near the start of the page, before the media.";if(e.tagName==="VIDEO"){let a=e.webkitAudioDecodedByteCount;return!e.paused&&!e.ended&&e.currentTime>=1&&a===0?{status:"pass"}:a>0?{status:"incomplete",message:`This video auto-plays with sound ${t}, with no controls and no muting, so its audio talks over a screen reader unless the page offers a way to stop it. Check for a pause, stop or volume control near the top of the page; if there is none, 1.4.2 is not met.`,fix:o}:{status:"incomplete",message:`This video auto-plays ${t} with no controls and no muting. If this video has a soundtrack, its audio talks over a screen reader unless the page offers a way to stop it. Check whether it has sound and, if so, that a pause, stop or volume control sits near the top of the page.`,fix:o}}return{status:"incomplete",message:`This audio auto-plays ${t} with no controls and no muting, so it talks over a screen reader unless the page offers a way to stop it. Check for a pause, stop or volume control near the top of the page; if there is none, 1.4.2 is not met.`,fix:o}}};var pi=new Set(["transform","translate","rotate","scale","offsetDistance","offsetPath","offsetRotate","left","right","top","bottom","marginLeft","marginRight","marginTop","marginBottom","backgroundPosition","backgroundPositionX","backgroundPositionY","objectPosition"]);function Mt(e){return e.some(t=>Object.keys(t).some(o=>pi.has(o)))}function hi(e){if(e.some(o=>o.visibility==="hidden"))return!0;let t=e.map(o=>parseFloat(o.opacity)).filter(o=>!Number.isNaN(o));return t.some(o=>o<=.25)&&t.some(o=>o>=.75)}function fi(e){if(!e||e==="none"||e==="normal")return!1;let t=e.replace(/url\([^)]*\)/g,"");return(t.match(/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'/g)??[]).some(a=>a.slice(1,-1).trim())?!0:/\b(?:attr|counters?)\(|(?:open|close)-quote/.test(t)}function mi(e){let t=new Map;if(typeof e.getAnimations!="function")return t;let o;try{o=e.getAnimations()}catch{return t}for(let a of o){if(a.playState!=="running"||!a.playbackRate||a.timeline&&a.timeline!==e.timeline)continue;let n=a.effect,r=n?.target;if(!r||r.nodeType!==1)continue;let i=n.getComputedTiming?.()??{};if(i.iterations!==1/0||!(i.duration>0))continue;let s;try{s=n.getKeyframes()}catch{continue}let l=Mt(s);if(!l&&!hi(s))continue;let c=!1;if(n.pseudoElement){let d="";try{d=r.ownerDocument.defaultView.getComputedStyle(r,n.pseudoElement).content}catch{}if(!fi(d))continue;c=!0}t.has(r)||t.set(r,{name:a.animationName,verb:l?"moves":"blinks",pseudoText:c})}return t}var ea={id:"pause-stop-hide",name:"Pausable moving content",impact:"serious",tags:["wcag2a","wcag222"],help:"Moving content must be pausable",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/pause-stop-hide.html",selector:"*",visibility:"visual",evaluateAll(e){if(!e.length)return[];let t=mi(e[0].ownerDocument);return e.map(o=>{if(o.tagName==="MARQUEE"||o.tagName==="BLINK")return o.tagName==="MARQUEE"&&o.getAttribute("scrollamount")==="0"?{status:"pass"}:{status:"fail",message:`<${o.tagName.toLowerCase()}> scrolls or blinks with no way to pause it, which is unusable for people with attention or vestibular conditions.`,fix:"Replace it with static content, or a CSS animation with a pause control that honours prefers-reduced-motion."};let a=t.get(o);if(!a||!(a.pseudoText||o.textContent.trim()))return{status:"pass"};let n=a.name?` (the \u201C${a.name}\u201D animation)`:"";return{status:"incomplete",message:`This content ${a.verb} continuously${n}: the animation repeats for ever, so it runs well past the five seconds at which WCAG 2.2.2 requires a way to pause, stop or hide it. Check the page offers one, and that it reaches this content.`}})}};var ta={id:"media-captions",name:"Video captions",impact:"critical",tags:["wcag2a","wcag122"],help:"Video content must have captions",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/captions-prerecorded.html",selector:"video",visibleOnly:!1,evaluate(e){return e.querySelector('track[kind="captions" i]')?{status:"pass"}:e.querySelector('track[kind="subtitles" i], track:not([kind])')?{status:"incomplete",message:'This video has a subtitles track but no captions track. Subtitles carry dialogue for people who can hear the audio; captions also carry sound effects and other audio information deaf and hard-of-hearing users need. Check the track covers those, or mark it kind="captions" if it does.',fix:'Use <track kind="captions" src="\u2026" srclang="\u2026" label="\u2026"> for a track that includes non-speech audio information.'}:!e.currentSrc&&!e.getAttribute("src")&&!e.querySelector("source")?{status:"pass"}:e.muted&&!e.controls?{status:"pass"}:{status:"incomplete",message:"No caption track found on this video. If captions aren\u2019t burned in or provided by the player, deaf and hard-of-hearing users are excluded \u2014 please verify.",fix:'Add <track kind="captions" src="\u2026" srclang="\u2026" label="\u2026"> with a WebVTT file.'}}};var Pe={"aria-atomic":["true","false"],"aria-busy":["true","false"],"aria-disabled":["true","false"],"aria-expanded":["true","false","undefined"],"aria-hidden":["true","false","undefined"],"aria-modal":["true","false"],"aria-multiline":["true","false"],"aria-multiselectable":["true","false"],"aria-readonly":["true","false"],"aria-required":["true","false"],"aria-selected":["true","false","undefined"],"aria-checked":["true","false","mixed","undefined"],"aria-pressed":["true","false","mixed","undefined"],"aria-live":["off","polite","assertive"],"aria-orientation":["horizontal","vertical","undefined"],"aria-sort":["none","ascending","descending","other"],"aria-autocomplete":["none","inline","list","both"],"aria-invalid":["true","false","grammar","spelling"],"aria-haspopup":["true","false","menu","listbox","tree","grid","dialog"],"aria-current":["true","false","page","step","location","date","time"]},oa={id:"aria-attr-valid",name:"Valid ARIA values",impact:"critical",tags:["wcag2a","wcag412"],help:"aria attributes must exist and have valid values",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/name-role-value.html",selector:"*",visibleOnly:!1,evaluate(e){let t=[],o=[],a=[],n;for(let{name:r,value:i}of ye(e)){if(!r.startsWith("aria-"))continue;let s=r.slice(5);if(!ze.has(s))t.push(r);else if(Pe[r]&&i.trim()==="")r!=="aria-current"&&a.push(r);else if(Pe[r]&&!Pe[r].includes(i.trim().toLowerCase())){if(!Le.has(s)&&(n??=Y(e),n&&he[n]&&!he[n].includes(s)))continue;o.push(`${r}="${i}" \u2014 allowed values: ${Pe[r].join(", ")}`)}}return o.length?{status:"fail",message:`Invalid ARIA: ${o.join("; ")}. Assistive technology ignores attributes it doesn't recognise.`,fix:"Correct the attribute name/value against the ARIA specification, or remove it."}:a.length?{status:"incomplete",message:`${a.join(", ")} is empty, so the browser ignores it and no state is exposed. Was a value intended here? If a real state was meant (${a.map(r=>Pe[r].slice(0,3).join(", ")).join("; ")}, ...), it is missing and this is a 4.1.2 failure; if the attribute is a template leftover, no one is affected.`,fix:"Give the attribute one of its allowed values, or remove it."}:t.length?{status:"incomplete",message:`${t.join(", ")} is not an ARIA attribute, so the browser ignores it and assistive technology never sees it. Nothing is announced wrongly, but nothing is announced at all. Was a real ARIA state or name intended here? If it was, that state or name is missing and this is a 4.1.2 failure. If not, the attribute is stray and no one is affected. Invalid markup is not by itself a WCAG failure: the criterion that policed validity, 4.1.1 Parsing, was removed in WCAG 2.2.`,fix:"Correct the attribute name against the ARIA specification, or remove it."}:{status:"pass"}}};var gi=new Set(["label","labelledby"]),aa={id:"aria-allowed-attr",name:"Allowed ARIA attributes",impact:"moderate",tags:["wcag2a","wcag412"],help:"ARIA attributes must be supported by the element\u2019s role",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/name-role-value.html",selector:"*",visibleOnly:!1,evaluate(e){let t=[];for(let{name:i}of ye(e)){if(!i.startsWith("aria-"))continue;let s=i.slice(5);!gi.has(s)&&ze.has(s)&&t.push(s)}if(!t.length)return{status:"pass"};let o=Y(e),a=o&&he[o];if(!a)return{status:"pass"};let n=t.filter(i=>!Le.has(i)&&!a.includes(i));if(!n.length)return{status:"pass"};let r=n.map(i=>`aria-${i}`);return{status:"incomplete",message:`${r.join(", ")} is not supported on role "${o}", so the browser drops it and assistive technology never sees it. Nothing is announced wrongly, but nothing is announced at all. Does this element really have that state? If it does, it is invisible to a screen reader and this is a 4.1.2 failure. If it does not, the attribute is simply stray and no one is affected. Invalid markup is not by itself a WCAG failure: the criterion that policed validity, 4.1.1 Parsing, was removed in WCAG 2.2 (w3.org/WAI/WCAG22/Understanding/parsing.html), so what decides this is whether the state reaches users, not the syntax.`,fix:`Move ${r.join("/")} to the element whose role supports it, usually the control that toggles this one, or remove it if the element has no such state. Adding a role to this element to make the attribute legal is rarely right: the host language restricts which roles each element may take.`}}};var na=["textbox","searchbox","combobox","listbox","spinbutton","slider","progressbar","meter"],ra=["checkbox","radio","switch"];function bi(e){return J(e)||e.getAttribute("aria-label")?.trim()||e.getAttribute("title")?.trim()||""}var ia={id:"aria-field-name",name:"ARIA field names",impact:"serious",tags:["wcag2a","wcag412"],help:"ARIA fields and value widgets must have an accessible name",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/name-role-value.html",selector:[...na,...ra].map(e=>`[role="${e}"]:not(input):not(select):not(textarea)`).join(", "),evaluate(e,{accessibleName:t}){let o=e.getAttribute("role");if(ra.includes(o)?t(e):bi(e))return{status:"pass"};if(o==="combobox"&&e.tabIndex<0){let n=e.querySelector('input, [role="textbox"], [role="searchbox"]');if(n&&(n.labels?.length||n.getAttribute("aria-label")||n.getAttribute("aria-labelledby")))return{status:"pass"}}return{status:"fail",message:`This role="${o}" control has no accessible name \u2014 screen readers announce the role and value with no idea what it's for.${na.includes(o)?" (Content inside it does not count as a name for this role.)":""}`,fix:'Add aria-label="\u2026" or aria-labelledby pointing at its visible label.'}}};var wi='a[href], button, summary, h1, h2, h3, h4, h5, h6, th, td, [role="button"], [role="link"], [role="menuitem"], [role="menuitemcheckbox"], [role="menuitemradio"], [role="option"], [role="tab"], [role="treeitem"], [role="checkbox"], [role="radio"], [role="switch"], [role="heading"], [role="cell"], [role="gridcell"], [role="columnheader"], [role="rowheader"], [role="tooltip"]',yi=["caption","code","deletion","emphasis","generic","insertion","paragraph","strong","subscript","superscript"],xi=["div","span","p","strong","em","code","del","s","ins","sub","sup"],vi=["aria-label","aria-labelledby"].flatMap(e=>[...xi.map(t=>`${t}[${e}]:not([role]):not([tabindex])`),...yi.map(t=>`[role="${t}"][${e}]:not([tabindex])`)]).join(", "),sa={id:"aria-label-misuse",name:"aria-label placement",impact:"moderate",tags:["wcag2a","wcag412"],help:"aria-label or aria-labelledby on a plain container is prohibited by ARIA, so its announcement is not guaranteed",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/name-role-value.html",selector:vi,evaluate(e){if(e.closest(wi))return{status:"pass"};let t=e.getAttribute("aria-label");return{status:"incomplete",message:`${t!==null?`aria-label="${t}"`:(()=>{let a=(e.getAttribute("aria-labelledby")??"").trim().split(/\s+/).map(n=>e.ownerDocument.getElementById(n)?.textContent?.trim()).filter(Boolean).join(" ");return a?`aria-labelledby (naming it "${a.slice(0,80)}")`:"aria-labelledby (whose references resolve to no text)"})()} on ${e.hasAttribute("role")?`role="${e.getAttribute("role").trim()}"`:`a plain <${e.tagName.toLowerCase()}>`} is prohibited by ARIA: this element's role does not support naming, so although many browser and screen reader pairings announce the label today, that support is not guaranteed anywhere and can differ between assistive technologies. Where it is announced it replaces the element's visible text; where it is not, users get the visible text alone. Check both presentations read correctly, and that nothing here relies on the label being heard. Prohibited markup is not by itself a WCAG failure: the criterion that policed validity, 4.1.1 Parsing, was removed in WCAG 2.2 (w3.org/WAI/WCAG22/Understanding/parsing.html), so what decides this is what users actually hear.`,fix:"If this element must reliably announce something of its own, give it the role it is playing (a landmark, group, or widget role) so the name is legal and exposure is guaranteed \u2014 or move the spoken text into visible or visually-hidden real text. If the label is stray, remove it. Do not add a role solely to legalise the label."}}};var la={checkbox:["aria-checked"],switch:["aria-checked"],radio:["aria-checked"],menuitemcheckbox:["aria-checked"],menuitemradio:["aria-checked"],slider:["aria-valuenow"],meter:["aria-valuenow"],scrollbar:["aria-valuenow","aria-controls"],separator:["aria-valuenow"],heading:["aria-level"],combobox:["aria-expanded","aria-controls"],option:["aria-selected"]},Ai=new Set(["aria-controls","aria-selected"]),Ot={"aria-checked":"screen readers can't announce whether it is on or off","aria-valuenow":"screen readers can't announce its value or position","aria-level":"browsers assume level 2 for a heading with no level, so the outline a screen reader user navigates by is a guess","aria-expanded":"screen readers can't announce whether its list is open","aria-controls":"assistive technology has no link from the control to what it operates","aria-selected":"a screen reader may not announce which option is selected"};function ki(e,t){let o=e.tagName,a=o==="INPUT"?e.type:null;switch(t){case"aria-checked":return a==="checkbox"||a==="radio";case"aria-valuenow":return a==="range"||o==="METER"||o==="PROGRESS";case"aria-level":return/^H[1-6]$/.test(o);case"aria-selected":return o==="OPTION";case"aria-expanded":return o==="SELECT";default:return!1}}var ca={id:"role-required-aria",name:"Required role states",impact:"serious",tags:["wcag2a","wcag412"],help:"Some roles require a state attribute to work",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/name-role-value.html",selector:Object.keys(la).map(e=>`[role="${e}"]`).join(", "),evaluate(e){let t=e.getAttribute("role");if(t==="separator"&&!(e.tabIndex>=0))return{status:"pass"};let o=la[t].filter(r=>!e.hasAttribute(r)&&!ki(e,r));if(!o.length)return{status:"pass"};let a=o.filter(r=>!Ai.has(r));if(a.length){let r=a.join(" and "),i=t==="separator"?`this separator takes focus, so it is a resizing widget, but without aria-valuenow ${Ot["aria-valuenow"]}`:a.map(s=>Ot[s]).join("; ");return{status:"fail",message:`role="${t}" without ${r} \u2014 ${i}.`,fix:`Add ${r} and keep it updated from your script, or use the native HTML element instead.`}}if(t==="combobox"&&e.getAttribute("aria-expanded")!=="true")return{status:"pass"};let n=o.join(" and ");return{status:"incomplete",message:`role="${t}" without ${n}, which ARIA lists as required for the role \u2014 ${o.map(r=>Ot[r]).join("; ")}. Check the control is usable with a screen reader; adding ${n} settles it.`}}};var Ci='input:not([type="hidden"]), select, textarea, button, meter, output, progress',ua={id:"label-for-valid",name:"Label target references",impact:"serious",tags:["wcag2a","wcag412"],help:"label[for] must reference a form control that exists",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/name-role-value.html",selector:"label[for]",evaluate(e){let t=e.getAttribute("for");if(e.control)return{status:"pass"};let o=e.getRootNode().getElementById?.(t);if(o&&o.tagName==="INPUT"&&o.type==="hidden")return{status:"pass"};let a=e.querySelector(Ci);return a?{status:"fail",message:`This label wraps a <${a.tagName.toLowerCase()}> but its for="${t}" ${o?`points at a <${o.tagName.toLowerCase()}>, which is not labelable`:"points at nothing"}. The for attribute overrides the wrapping, so the wrapped control is not labelled by this text.`,fix:"Point the for attribute at the wrapped control's id, or remove the for attribute so the wrapping labels it."}:{status:"incomplete",message:o?`for="${t}" points at a <${o.tagName.toLowerCase()}>, which is not a labelable element, so the browser ignores the association. Was a form control meant to be here? If so it has no label from this text (form-label reports the control itself); if this text captions something that is not a control, nothing is affected.`:`for="${t}" points at nothing, so this label is not associated with any control. Was a form control meant to be here? If so it has lost its label (form-label reports the control itself); if this is leftover markup, no one is affected.`,fix:"Point the for attribute at the id of the control it labels, or remove the attribute."}}};var da={id:"listitem-parent",name:"List item placement",impact:"serious",tags:["wcag2a","wcag131"],help:"<li> must be inside a list",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/info-and-relationships.html",selector:"li:not([role])",evaluate(e){let t=i=>i.assignedSlot?.parentElement??i.parentElement??i.getRootNode()?.host,o=t(e);for(;o?.tagName==="SLOT";)o=o.assignedSlot?.parentElement??o.parentElement;let a=i=>{let l=(i.getAttribute("role")??"").trim().toLowerCase().split(/\s+/).filter(Boolean).find(c=>he[c])??null;return i.matches("ul, ol, menu")?!l||["list","presentation","none"].includes(l):l==="list"},n=i=>["presentation","none"].includes(i.getAttribute("role")??"")||!i.hasAttribute("role")&&(i.tagName==="DIV"||i.tagName==="SPAN");for(;o&&!a(o)&&n(o);)o=t(o);if(o&&a(o))return{status:"pass"};let r=o?`<${o.tagName.toLowerCase()}${o.getAttribute("role")?` role="${o.getAttribute("role")}"`:""}>`:"nothing";if(o?.getAttribute("role")==="group"){let i=t(o);for(;i&&!a(i)&&(n(i)||i.getAttribute("role")==="group");)i=t(i);if(i&&a(i))return{status:"fail",message:`This <li> sits inside ${r} within its list. A list may only own list items directly, and role="group" is not permitted between a list and its items, so screen readers lose the item's list context.`,fix:'Remove role="group" from the wrapper (a plain <div> keeps the items owned by the list), or move the group label onto the list element with aria-label.'}}return{status:"fail",message:`This <li> sits inside ${r} \u2014 ${o?.matches("ul, ol, menu")&&o.getAttribute("role")?"the explicit role replaces the list semantics, so":"outside a list,"} screen readers lose the item's list context entirely.`,fix:o?.matches("ul, ol, menu")&&o.getAttribute("role")?"Remove the role from the list (use a wrapping element for the landmark), or give the items roles the container expects.":"Wrap it in a <ul> or <ol>, or change it to a <div>/<p> if it isn\u2019t really a list item."}}};var Si=new Set(["DT","DD","DIV"]),Ti=new Set(["SCRIPT","TEMPLATE","STYLE","LINK","META"]),pa={id:"definition-list",name:"Definition list structure",impact:"serious",tags:["wcag2a","wcag131"],help:"<dl> must be structured as term/description pairs",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/info-and-relationships.html",selector:"dl:not([role])",evaluate(e,{isRendered:t}){let o=[...e.children].filter(n=>!Si.has(n.tagName)&&!Ti.has(n.tagName)&&(!t||t(n)));if(o.length)return{status:"fail",message:`This <dl> contains ${[...new Set(o.map(r=>`<${r.tagName.toLowerCase()}>`))].join(", ")} directly \u2014 only <dt>, <dd> (optionally grouped in <div>) are allowed, otherwise the term/description pairing breaks.`,fix:"Restructure the list into <dt>/<dd> pairs, or use a different element."};let a=[...e.children].filter(n=>n.tagName==="DIV"&&![...n.children].some(r=>r.tagName==="DT"||r.tagName==="DD"));return a.length?{status:"fail",message:`${a.length} <div> wrapper(s) in this <dl> hold no <dt>/<dd> directly \u2014 the term/description pairing breaks when the pairs sit deeper than the wrapper.`,fix:"Make each <div> child of the <dl> contain its <dt>/<dd> pair directly, or flatten the pairs into the <dl> itself."}:{status:"pass"}}};var ha={id:"dlitem-parent",name:"Definition item placement",impact:"serious",tags:["wcag2a","wcag131"],help:"<dt> and <dd> must be inside a <dl>",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/info-and-relationships.html",selector:"dt:not([role]), dd:not([role])",evaluate(e){let t=o=>o.assignedSlot?.parentElement??o.parentElement??o.getRootNode()?.host??null;for(let o=t(e);o;o=t(o))if(o.tagName==="DL")return{status:"pass"};return{status:"fail",message:`<${e.tagName.toLowerCase()}> outside a <dl> has no term/description semantics.`,fix:"Wrap the terms and descriptions in a <dl>, or use different elements."}}};var fa={id:"area-alt",name:"Image map alt text",impact:"critical",tags:["wcag2a","wcag111","wcag244"],help:"Image map areas must have alternative text",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/non-text-content.html",selector:"map area[href]",visibleOnly:!1,evaluate(e,{accessibleName:t}){return t(e)?{status:"pass"}:{status:"fail",message:"This clickable map area has no alt text \u2014 screen readers announce nothing for it.",fix:'Add alt="Where this area links to".'}}};var ma={id:"object-alt",name:"Object alt text",impact:"serious",tags:["wcag2a","wcag111"],help:"<object> embeds must have a text alternative",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/non-text-content.html",selector:"object",evaluate(e,{accessibleName:t}){if(t(e)||e.textContent.trim())return{status:"pass"};let o=(e.getAttribute("type")??"").trim().toLowerCase(),a=(e.getAttribute("data")??"").split(/[?#]/)[0].toLowerCase();return o.startsWith("text/")||o==="application/pdf"||o==="application/xhtml+xml"||!o&&/\.(html?|xhtml|pdf|txt)$/.test(a)?{status:"incomplete",message:"This embedded object renders a text document but has no accessible name, so assistive technology users get no indication of what the embedded document is. Check it is identified in the surrounding context.",fix:'Add aria-label="\u2026" naming the embedded document, or fallback content inside the <object> element.'}:{status:"fail",message:"This embedded object has no text alternative \u2014 users who can\u2019t see it get nothing.",fix:'Add aria-label="\u2026", or fallback content inside the <object> element.'}}};var ga={id:"input-image-alt",name:"Image button alt text",impact:"critical",tags:["wcag2a","wcag111","wcag412"],help:"Image inputs need alt text describing their action",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/non-text-content.html",selector:'input[type="image"]',evaluate(e,{accessibleName:t}){return t(e)?{status:"pass"}:{status:"fail",message:'This image button has no alt text, so browsers fall back to a generic "Submit" name that does not describe what the button does.',fix:'Add alt="What the button does", e.g. alt="Search".'}}};var ba={id:"multiple-labels",name:"Multiple labels",impact:"minor",tags:["best-practice"],help:"Form fields should not have multiple labels",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/labels-or-instructions.html",selector:"input, select, textarea",evaluate(e,{isVisible:t}){let o=[...e.labels??[]].filter(t);return o.length<=1?{status:"pass"}:{status:"fail",message:`${o.length} visible labels point at this field \u2014 screen readers differ in which they announce.`,fix:"Keep one label; move extra guidance into aria-describedby text."}}};function Ri(e){let t=e.replace(/^\s*[\d.]*\s*(?:[;,]\s*)?/,"");t=t.replace(/^url\s*(?:=\s*)?/i,"");let o=t[0]==='"'||t[0]==="'"?t[0]:"";if(o){t=t.slice(1);let a=t.indexOf(o);a!==-1&&(t=t.slice(0,a))}return t.trim()}var wa={id:"meta-refresh",name:"Timed page refresh",impact:"critical",tags:["wcag2a","wcag221"],help:"The page must not use a timed refresh",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/timing-adjustable.html",selector:'meta[http-equiv="refresh" i]',visibleOnly:!1,evaluate(e){let t=e.getAttribute("content")??"",o=parseInt(t,10),a=Ri(t)!=="";return o===0&&!a?{status:"fail",message:"The page reloads itself immediately and has no destination to move on to, so the reload repeats for as long as the page is open. Nobody can read or complete anything on it.",fix:'Remove the timed refresh. If this is meant to be a redirect, give it a url (content="0; url=\u2026"); if the content needs updating, update it in place instead of reloading.'}:o>0?o>72e3?{status:"pass"}:{status:"fail",message:`The page refreshes/redirects after ${o}s \u2014 slow readers lose their place (or the whole page) with no control.`,fix:"Remove the timed refresh; let users act in their own time."}:{status:"pass"}}};var Be=e=>(e??"").split(/,(?![^(]*\))/).map(t=>t.trim()),Ni=["Top","Right","Bottom","Left"],xa=(e,t)=>parseFloat(e[`border${t}Width`])>0&&e[`border${t}Style`]!=="none"&&(W(e[`border${t}Color`])?.a??0)>0;function $i(e){return!e||e==="none"?!1:Be(e).some(t=>{let o=t.match(/[a-z-]+\([^)]*\)/)?.[0],a=o?W(o):null;if(a&&a.a===0)return!1;let n=t.replace(o??"","").match(/-?[\d.]+(?=px)/g)??[];return!(n.length&&n.every(r=>parseFloat(r)===0))})}function va(e,t){if(!e.backgroundImage||e.backgroundImage==="none")return"no";let o=Be(e.backgroundImage),a=Be(e.backgroundSize),n=Be(e.backgroundRepeat),r=Be(e.backgroundPosition),i="no";return o.forEach((s,l)=>{if(s==="none")return;let c=a[l%a.length]||"auto";if(/(^|\s)0(px|%)?(\s|$)/.test(c))return;let d=n[l%n.length]||"repeat",u=r[l%r.length]||"0% 0%";if(d.includes("no-repeat")&&u.split(/\s+/).some((f,m)=>{let b=parseFloat(f);if(Number.isNaN(b))return!1;if(f.endsWith("%"))return b<0||b>100;let p=m===0?t?.width:t?.height;return b<0||Number.isFinite(p)&&b>p})){i==="no"&&(i="unclear");return}i="yes"}),i}function Ii(e,t){let o;try{o=getComputedStyle(e,t)}catch{return null}let a=o.content;if(!a||a==="none"||a==="normal"||o.display==="none"||o.visibility!=="visible"||parseFloat(o.opacity)===0)return null;let n=(o.transform??"none").match(/^matrix(3d)?\((.*)\)$/);if(n){let d=n[2].split(",").map(parseFloat),[u,h,f,m]=n[1]?[d[0],d[1],d[4],d[5]]:d;if(u===0&&h===0||f===0&&m===0)return null}let r=parseFloat(o.width),i=parseFloat(o.height),s=/^["'].+["']$/s.test(a)&&parseFloat(o.fontSize)>0||/^(url|counter|counters|attr|image-set)\(/.test(a),l=(W(o.backgroundColor)?.a??0)>0||va(o,Number.isNaN(r)?null:{width:r,height:i})==="yes",c=Ni.some(d=>xa(o,d));if(l||c){if(Number.isNaN(r)||Number.isNaN(i))return s?"glyph":"unclear";let d=f=>f.reduce((m,b)=>m+(parseFloat(o[b])||0),0),u=r+d(["paddingLeft","paddingRight","borderLeftWidth","borderRightWidth"]),h=i+d(["paddingTop","paddingBottom","borderTopWidth","borderBottomWidth"]);if(u>0&&h>0)return"cue"}return s?!Number.isNaN(r)&&!Number.isNaN(i)&&r<=1&&i<=1?null:"glyph":null}var ya="svg, img, picture, canvas, video, object, embed",Aa={id:"link-in-text-block",name:"Link distinction in text",impact:"serious",tags:["wcag2a","wcag141"],help:"Links inside text must be distinguishable by more than colour",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/use-of-color.html",selector:"p a[href], dd a[href], blockquote a[href], td a[href], li a[href]",visibility:"visual",evaluate(e,{ownText:t}){let o=e.closest("p, dd, blockquote, td, li");if(!e.textContent.trim()||!o)return{status:"pass"};if(t(o).replace(/\s+/g,"").length<10)return{status:"pass"};let a=getComputedStyle(e),n=getComputedStyle(o),r=x=>parseInt(x.fontWeight,10)||400,i=x=>/italic|oblique/.test(x.fontStyle??""),s=parseFloat(n.fontSize)||16,l=[{s:a,el:e,text:!0}],c=!1;for(let x of e.querySelectorAll("*")){let $=[...x.getClientRects()].filter(R=>R.width>1&&R.height>1);if($.length){if(x.matches(ya)){$.some(R=>R.width>=4&&R.height>=4)&&(c=!0);continue}x.closest(ya)||l.push({s:getComputedStyle(x),el:x,text:!!x.textContent.trim()})}}let d=!1;for(let{s:x,el:$,text:R}of l){if(R&&(x.textDecorationLine??x.textDecoration??"").includes("underline"))return{status:"pass"};if(xa(x,"Bottom"))return{status:"pass"};if($i(x.boxShadow))return{status:"pass"};let P=W(x.backgroundColor);if(P&&P.a>0)return{status:"pass"};let I=va(x,$.getBoundingClientRect());if(I==="yes")return{status:"pass"};if(I==="unclear"&&(d=!0),!!R){if(Math.abs(r(x)-r(n))>=300)return{status:"pass"};if(i(x)!==i(n))return{status:"pass"}}}let u=l.filter(x=>x.text).map(x=>x.s),h=Math.max(...u.map(x=>Math.abs(r(x)-r(n)))),f=Math.max(...u.map(x=>Math.abs((parseFloat(x.fontSize)||s)-s))),m=f>=2&&f/s>=.1,b=W(a.color),p=W(n.color);if(!b||!p)return{status:"pass"};if((b.a??1)<1||(p.a??1)<1){if($e(e))return{status:"incomplete",message:"This link has no underline and its colour is translucent over a background image or gradient, so its colour difference from the surrounding text depends on the pixels behind it. Check by eye that something other than colour identifies it as a link.",fix:"Underline links inside text (text-decoration: underline), or add a non-colour indicator."};let x=oe(e);if(!x)return{status:"incomplete",message:"This link has no underline and its colour is translucent, but the background it composites over could not be determined. Check by eye that something other than colour identifies it as a link.",fix:"Underline links inside text (text-decoration: underline), or add a non-colour indicator."};let $=R=>(R.a??1)<1?O(R,x):R;b=$(b),p=$(p)}let w=(x,$)=>Math.round(x)===Math.round($);if(w(b.r,p.r)&&w(b.g,p.g)&&w(b.b,p.b))return{status:"pass"};let T=G(b,p);if(T>=3)return{status:"pass"};for(let x=e.parentElement;x&&x!==o;x=x.parentElement)if((getComputedStyle(x).textDecorationLine??"").includes("underline")){if(x.textContent.replace(/\s+/g,"")===e.textContent.replace(/\s+/g,""))return{status:"pass"};break}for(let x of["::after","::before"]){let $=Ii(e,x);if($==="cue")return{status:"pass"};$==="glyph"&&(c=!0),$==="unclear"&&(d=!0)}let U=[...e.getClientRects()].filter(x=>x.width>0&&x.height>0);if(U.length){let x=U.map(I=>I.top+I.height/2),$=e.ownerDocument.createTreeWalker(o,NodeFilter.SHOW_TEXT),R=e.ownerDocument.createRange(),P=!1;for(let I=$.nextNode();I&&!P;I=$.nextNode())if(!(!I.textContent.trim()||e.contains(I))){R.selectNodeContents(I);for(let B of R.getClientRects())if(B.width>0&&x.some(V=>B.top<=V&&B.bottom>=V)){P=!0;break}}if(!P)return{status:"pass"}}let L=T.toFixed(2),F="Underline links inside text (text-decoration: underline), or add another clear non-colour indicator.";return c?{status:"incomplete",message:`This link has no underline and only ${L}:1 colour difference from the surrounding text, but it carries an icon or image. Judge by eye whether the graphic reads as part of the link and marks it out from the prose; if it does not, this fails SC 1.4.1.`,fix:F}:d?{status:"incomplete",message:`This link has no underline and only ${L}:1 colour difference from the surrounding text, but a generated box or background image is painted on it whose size at rest could not be measured. Check by eye whether it draws a visible underline or shape; if nothing shows until hover, this fails SC 1.4.1.`,fix:F}:h>=200?{status:"incomplete",message:`This link has no underline and only ${L}:1 colour difference from the surrounding text, but its font weight differs from the prose by ${h}: a visible non-colour cue that falls short of a clear bold step. Judge by eye whether the weight alone identifies it as a link in this face and size; if it does not, this fails SC 1.4.1.`,fix:"Underline links inside text (text-decoration: underline), raise the weight difference to a clear bold step, or add another non-colour indicator."}:m?{status:"incomplete",message:`This link has no underline and only ${L}:1 colour difference from the surrounding text, but its font size differs from the prose by ${f.toFixed(1).replace(/\.0$/,"")}px, one of the cues G182 names. Judge by eye whether the size alone identifies it as a link; if it does not, this fails SC 1.4.1.`,fix:F}:{status:"fail",message:T<1.01?"This link has no underline and its colour differs from the surrounding text in hue alone, with no luminance difference: the distinction disappears entirely without colour vision, the exact failure F73 describes.":`This link has no underline and only ${L}:1 colour difference from the surrounding text, below the 3:1 WCAG technique G183 asks for when colour is the only thing marking a link.`,fix:"Underline links inside text (text-decoration: underline), or add a non-colour indicator."}}};var ka={id:"p-as-heading",name:"Styled text as heading",impact:"serious",tags:["wcag2a","wcag131"],help:"Bold paragraphs should not stand in for headings",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/info-and-relationships.html",selector:"p",evaluate(e){let t=e.textContent.trim(),o=e.children.length===1&&e.children[0];return t&&t.length<60&&o&&o.matches("b, strong")&&o.textContent.trim()===t&&e.nextElementSibling?.matches("p")?{status:"incomplete",message:`"${t}" is a short bold paragraph followed by text \u2014 if it's acting as a heading, it's invisible to heading navigation. Verify.`,fix:"If it is a heading, use a real heading element (<h2>\u2026</h2>) at the appropriate level."}:{status:"pass"}}};var Ca={feed:["article"],row:["cell","columnheader","gridcell","rowheader"],list:["listitem"],listbox:["option","group"],menu:["menuitem","menuitemcheckbox","menuitemradio","group"],menubar:["menuitem","menuitemcheckbox","menuitemradio","group"],radiogroup:["radio"],tablist:["tab"],tree:["treeitem","group"],table:["row","rowgroup"],grid:["row","rowgroup"],treegrid:["row","rowgroup"],rowgroup:["row"]},Ei=e=>e.getAttribute("role")?.trim().split(/\s+/)[0]?.toLowerCase()??Q(e);function Sa(e){let t=[],o=[e],a=new Set,n=r=>{if(!a.has(r)&&(a.add(r),t.push(r),r.shadowRoot&&o.push(r.shadowRoot),r.tagName==="SLOT"))for(let i of r.assignedElements({flatten:!0}))n(i),o.push(i)};for(let r=0;r<o.length;r++){let i=o[r];if(i.shadowRoot&&o.push(i.shadowRoot),i.tagName==="SLOT")for(let s of i.assignedElements({flatten:!0}))n(s),o.push(s);for(let s of i.querySelectorAll("*"))n(s)}return t}function Wi(e){let t=Sa(e).filter(a=>!a.matches("script, style, template")),o=e.getAttribute("aria-owns");if(o){let a=e.getRootNode();for(let n of o.split(/\s+/).filter(Boolean)){let r=a.getElementById?.(n);r&&t.push(r,...Sa(r))}}return t}var Ta={id:"aria-required-children",name:"Required ARIA children",impact:"critical",tags:["wcag2a","wcag131"],help:"Composite ARIA roles must contain their required children",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/info-and-relationships.html",selector:Object.keys(Ca).map(e=>`[role="${e}"]`).join(", "),evaluate(e){if(e.getAttribute("aria-busy")==="true")return{status:"pass"};let t=e.getAttribute("role").trim().split(/\s+/)[0].toLowerCase(),o=Ca[t];return![...e.children,...e.shadowRoot?.children??[]].filter(n=>!n.matches("script, style, template")).length&&!e.hasAttribute("aria-owns")?{status:"pass"}:Wi(e).some(n=>o.includes(Ei(n)))?{status:"pass"}:{status:"fail",message:`role="${t}" contains no ${o.join("/")} anywhere inside it, and claims none by aria-owns \u2014 screen readers announce a broken, empty ${t}.`,fix:`Give the item elements role="${o[0]}", or remove role="${t}" from the container.`}}};var Ra={caption:["figure","grid","table","treegrid"],columnheader:["row"],rowheader:["row"],listitem:["list"],option:["listbox","group"],menuitem:["menu","menubar","group"],menuitemcheckbox:["menu","menubar","group"],menuitemradio:["menu","menubar","group"],tab:["tablist"],treeitem:["tree","group"],row:["table","grid","treegrid","rowgroup"],rowgroup:["table","grid","treegrid"],cell:["row"],gridcell:["row"]},Na={ul:"list",ol:"list",menu:"list",table:"table",tbody:"rowgroup",thead:"rowgroup",tfoot:"rowgroup",tr:"row",figure:"figure"},$a={id:"aria-required-parent",name:"Required ARIA parent",impact:"critical",tags:["wcag2a","wcag131"],help:"ARIA child roles must be inside their required container role",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/info-and-relationships.html",selector:Object.keys(Ra).map(e=>`[role="${e}"]`).join(", "),evaluate(e){let t=e.getAttribute("role").trim().split(/\s+/)[0].toLowerCase(),o=Ra[t],a=o.filter(r=>r!=="group");if(e.id){let r=e.getRootNode().querySelector?.(`[aria-owns~="${CSS.escape(e.id)}"]`),i=r&&(r.getAttribute("role")?.trim().split(/\s+/)[0]?.toLowerCase()??Na[r.tagName.toLowerCase()]);if(i&&o.includes(i))return{status:"pass"}}let n=r=>r.assignedSlot??r.parentElement??(r.getRootNode()instanceof ShadowRoot?r.getRootNode().host:null);for(let r=n(e);r;r=n(r)){if(r.tagName==="SLOT"&&!r.hasAttribute("role"))continue;let i=r.getAttribute("role")?.trim().split(/\s+/)[0]?.toLowerCase()??Na[r.tagName.toLowerCase()];if(o.includes(i))return{status:"pass"};if(i!=="group"&&!(i==="presentation"||i==="none")&&i)break}return{status:"fail",message:`role="${t}" is not inside a ${a.join("/")} \u2014 assistive technology loses the structure entirely.`,fix:`Wrap it in an element with role="${a[0]}", or fix the intervening roles.`}}};var Ut='a[href], button, input:not([type="hidden"]), select, textarea, summary, [tabindex]';function Dt(e){return e.tabIndex<0||e.disabled||pe(e)?!1:typeof e.checkVisibility=="function"?e.checkVisibility({visibilityProperty:!0}):!0}function Ia(e){for(let t of e.querySelectorAll?.(Ut)??[])if(Dt(t))return!0;for(let t of e.querySelectorAll?.("slot")??[])for(let o of t.assignedElements?.()??[]){if(o.matches?.(Ut)&&Dt(o))return!0;for(let a of o.querySelectorAll?.(Ut)??[])if(Dt(a))return!0}for(let t of e.querySelectorAll?.("*")??[])if(t.shadowRoot&&Ia(t.shadowRoot))return!0;return!1}var Ea={id:"scrollable-region-focusable",name:"Keyboard-reachable scroll areas",impact:"serious",tags:["wcag2a","wcag211","wcag213"],help:"Scrollable regions must be reachable by keyboard",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/keyboard.html",selector:"*",visibleOnly:!1,evaluate(e,{isVisible:t}){let o=e.scrollWidth>e.clientWidth+1,a=e.scrollHeight>e.clientHeight+1;if(!o&&!a)return{status:"pass"};if(e===e.ownerDocument.documentElement||e===e.ownerDocument.body)return{status:"pass"};if(!e.clientWidth||!e.clientHeight)return{status:"pass"};let n=getComputedStyle(e),r=o&&/(auto|scroll)/.test(n.overflowX),i=a&&/(auto|scroll)/.test(n.overflowY);return!r&&!i?{status:"pass"}:t(e)?e.tabIndex>=0?{status:"pass"}:Ia(e)?{status:"pass"}:e.hasAttribute("tabindex")?{status:"fail",message:'This region scrolls, holds nothing tabbable, and its tabindex="-1" removes the region itself from the tab order \u2014 keyboard users cannot reach the overflowed content in any browser.',fix:'Change tabindex="-1" to tabindex="0" (plus role="region" and an aria-label describing it), or make something inside it tabbable.'}:{status:"incomplete",message:'This region scrolls and holds nothing tabbable, so reaching its overflowed content depends on the browser. Chromium gives such containers keyboard focus automatically; not every engine does. Tab to it in the browsers you support, and add tabindex="0" if it cannot be reached.',fix:'If you support browsers that do not focus scroll containers, add tabindex="0" (plus role="region" and an aria-label describing it).'}:{status:"pass"}}};var Li="h1, h2, h3, h4, h5, h6, p, ul, ol, dl, menu, form, nav, main, header, footer, section, article, aside, blockquote, figure, fieldset, pre, hr, table",Wa={id:"table-headers",name:"Table header associations",impact:"serious",tags:["wcag2a","wcag131"],help:"Data tables must have properly associated header cells",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/info-and-relationships.html",selector:"table",evaluate(e){let t=e.getAttribute("role");if(t&&!["table","grid"].includes(t))return{status:"pass"};let o=[...e.rows],a=o.flatMap(u=>[...u.cells]),n=o.length>=2&&Math.max(0,...o.map(u=>u.cells.length))>=2,r=a.filter(u=>u.textContent.trim()).length;if(!n||r<4)return{status:"pass"};let i=u=>u.getAttribute("role")?.trim().split(/\s+/)[0]?.toLowerCase(),s=u=>u.tagName==="TH"||["columnheader","rowheader"].includes(i(u)),l=a.some(s);if(!l&&(e.querySelector("table")||e.parentElement?.closest("table")))return{status:"incomplete",message:'This table has no header cells and is part of a nested-table structure \u2014 a layout-table signature. If it really presents data, mark its header cells with <th>; if it is layout scaffolding, add role="presentation".'};let c=a.filter(u=>{let h=u.getAttribute("headers");return h&&h.trim().split(/\s+/).filter(Boolean).some(f=>{let m=e.getRootNode().getElementById?.(f);return!m||!a.includes(m)||!(/^T[HD]$/.test(m.tagName)||s(m))})});return c.length?{status:"fail",message:`${c.length} cell(s) have headers="\u2026" referencing ids that are not header cells of this table \u2014 screen readers cannot relate the data to its headers.`,fix:"Make each headers attribute list the id(s) of <th> cells in the same table."}:l||a.some(u=>u.hasAttribute("scope")||u.hasAttribute("headers"))?{status:"pass"}:a.some(h=>h.querySelector(Li))?{status:"incomplete",message:'This table has no header cells and its cells hold block content (headings, paragraphs, lists or forms), which is a layout-table signature. If it really presents data, mark its header cells with <th>; if it is layout scaffolding, add role="presentation".'}:{status:"fail",message:"This looks like a data table but has no header cells \u2014 screen reader users get the data with no way to tell what each row/column means.",fix:Math.max(0,...o.map(h=>h.cells.length))===2?'Mark the first cell of each row as <th scope="row">: in a two-column table the first column names what the second holds.':'Mark header cells with <th> (add scope="col" or scope="row" when the table has both).'}}};var La=Wt({id:"color-contrast-enhanced",name:"Enhanced text contrast",tags:["wcag2aaa","wcag146"],help:"Text must have enhanced contrast against its background (AAA)",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/contrast-enhanced.html",thresholds:{normal:7,large:4.5}});var Ma=Lt({id:"target-size-enhanced",name:"Enhanced target size",tags:["wcag21aaa","wcag255"],help:"Interactive targets must be at least 44\xD744 pixels (AAA)",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/target-size-enhanced.html",min:44,spacingException:!1});var Oa={id:"dialog-name",name:"Dialog names",impact:"serious",tags:["wcag2a","wcag412"],help:"Dialogs must have an accessible name",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/name-role-value.html",selector:'dialog, [role="dialog"], [role="alertdialog"]',evaluate(e){return J(e)?{status:"pass"}:e.getAttribute("aria-label")?.trim()||e.getAttribute("title")?.trim()?{status:"pass"}:{status:"fail",message:'This dialog has no accessible name \u2014 screen readers announce just "dialog" with no hint of what it is or why it appeared.',fix:`Point aria-labelledby at the dialog's heading, or add aria-label="What this dialog is".`}}};var Ua={id:"control-contrast",name:"Form control contrast",impact:"serious",tags:["wcag2aa","wcag143"],help:"Text inside form controls must have sufficient contrast",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html",selector:'input:not([type="hidden"]):not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="color"]):not([type="file"]):not([type="image"]), select, textarea',visibility:"visual",evaluate(e){if(e.closest(':disabled, [aria-disabled="true"]'))return{status:"pass"};let t=e.getBoundingClientRect();if(t.width<=1||t.height<=1)return{status:"pass"};let o=getComputedStyle(e);if(parseFloat(o.fontSize)===0)return{status:"pass"};if(/^(submit|button|reset)$/i.test(e.getAttribute("type")??"")&&e.hasAttribute("value")&&e.getAttribute("value")==="")return{status:"pass"};{let u=e.ownerDocument,h=u.defaultView;if(h&&getComputedStyle(u.documentElement).direction!=="rtl"&&(t.right+h.scrollX<=0||t.bottom+h.scrollY<=0))return{status:"pass"}}let a=je(e)?Ye(e):ve(e);if(a<.05)return{status:"pass"};let n=W(o.backgroundColor),r;if(n&&n.a>=1)r=n;else{if($e(e))return{status:"incomplete",message:"This field is see-through and sits over a background image or gradient, so its real text contrast depends on the pixels behind it. Check it by eye."};let u=oe(e);r=u&&n&&n.a>0?O(n,u):u}if(!r)return{status:"incomplete",message:"The control\u2019s background could not be determined \u2014 check its text contrast by eye."};let i=Ie(o)?3:4.5,s=(u,h,f=1)=>{let m=W(u);if(!m||m.a===0)return null;let b=a*f,p=b<1?{...m,a:m.a*b}:m,w=p.a<1?O(p,r):p,T=G(w,r);return T>=i?null:{what:h,ratio:T}},l=[],c=s(o.color,"value text");if(c&&l.push(c),e.getAttribute("placeholder")?.trim()){let u=null,h=1;try{let f=getComputedStyle(e,"::placeholder");u=f.color;let m=parseFloat(f.opacity);Number.isFinite(m)&&(h=Math.min(1,Math.max(0,m)))}catch{}if(u&&(u!==o.color||h<1)){let f=s(u,"placeholder text",h);f&&l.push(f)}}if(!l.length)return{status:"pass"};let d=l.sort((u,h)=>u.ratio-h.ratio)[0];return{status:"fail",message:`This field's ${d.what} has ${q(d.ratio)}:1 contrast against the field background \u2014 below the ${i}:1 minimum.`,fix:`Darken the ${d.what.includes("placeholder")?"placeholder colour (::placeholder)":"text colour"} until it reaches ${i}:1 against the field background.`,data:{ratio:Number(q(d.ratio)),required:i}}}};var Da={id:"non-text-contrast",name:"Form field boundary contrast",impact:"serious",tags:["wcag21aa","wcag1411"],help:"Form field fills and borders below 3:1 need checking against the field's other identifiers",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/non-text-contrast.html",selector:'input:not([type="hidden"]):not([type="checkbox"]):not([type="radio"]):not([type="submit"]):not([type="button"]):not([type="reset"]):not([type="image"]):not([type="range"]):not([type="file"]), select, textarea',visibility:"visual",evaluate(e){if(e.closest(':disabled, [aria-disabled="true"]'))return{status:"pass"};let t=e.getBoundingClientRect();if(t.width<=1||t.height<=1)return{status:"pass"};let o=getComputedStyle(e);if(o.appearance!=="none"&&e.tagName==="SELECT")return{status:"pass"};let a=oe(e.parentElement??e);if(!a)return{status:"incomplete",message:"The background around this field could not be determined \u2014 check the field has a visible boundary by eye."};let n=[],r=!1,i=W(o.backgroundColor);i&&i.a>0&&(n.push(i.a>=1?i:O(i,a)),i.a<1&&(r=!0));let s=!1;for(let c of["Top","Right","Bottom","Left"]){if(parseFloat(o[`border${c}Width`])<=0||o[`border${c}Style`]==="none")continue;s=!0;let d=W(o[`border${c}Color`]);!d||d.a===0||(n.push(d.a>=1?d:O(d,a)),d.a<1&&(r=!0))}if(parseFloat(o.outlineWidth)>0&&o.outlineStyle!=="none"){let c=W(o.outlineColor);c&&c.a>0&&(s=!0,n.push(c.a>=1?c:O(c,a)),c.a<1&&(r=!0))}let l=Math.max(1,...n.map(c=>G(c,a)));if(l>=3)return{status:"pass"};if(o.boxShadow!=="none"||o.backgroundImage!=="none")return{status:"incomplete",message:"This field\u2019s boundary may come from a box-shadow or background image \u2014 check it reaches 3:1 against the page by eye."};if(r)return{status:"incomplete",message:`This field's boundary uses translucent colours and reaches about ${q(l)}:1 against the page \u2014 near the 3:1 minimum; check by eye.`};for(let c=e.parentElement,d=0;c&&d<4;c=c.parentElement,d++){let u=c.getBoundingClientRect();if(u.height>t.height+24||u.width>t.width+160)break;let h=getComputedStyle(c);if(["Top","Right","Bottom","Left"].some(b=>parseFloat(h[`border${b}Width`])>0&&h[`border${b}Style`]!=="none"&&(W(h[`border${b}Color`])?.a??0)>0))return{status:"pass"};let m=W(h.backgroundColor);if(m&&m.a>=1){let b=oe(c.parentElement??c);if(b&&G(m,b)>=3)return{status:"pass"};break}}return!s&&(!i||i.a===0)?{status:"incomplete",message:"This field has no border and no distinct fill \u2014 if its label and layout don\u2019t already make it findable, low-vision users can\u2019t locate it. Check by eye (3:1 boundary or clear identification needed)."}:{status:"incomplete",message:`This field's boundary reaches only ${q(l)}:1 against the page \u2014 below the 3:1 minimum. That\u2019s conformant ONLY if the field is identifiable without the boundary (visible label, placeholder position); check by eye.`}}};var Mi=`html { overflow-anchor: none !important; }
2
+ * {
3
+ line-height: 1.5 !important;
4
+ letter-spacing: 0.12em !important;
5
+ word-spacing: 0.16em !important;
6
+ }
7
+ p { margin-bottom: 2em !important; }`,Fa=e=>e.scrollWidth>e.clientWidth+2||e.scrollHeight>e.clientHeight+2,Pa={id:"text-spacing",name:"Text spacing overrides",impact:"serious",tags:["wcag21aa","wcag1412"],help:"Text must survive user spacing overrides without being cut off",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/text-spacing.html",selector:"p, h1, h2, h3, h4, h5, h6, a, button, li, span, div, td, th, label, dt, dd, figcaption",visibility:"visual",evaluateAll(e){let t=e[0]?.ownerDocument??document;if(e.length>2e4)return e.map(()=>({status:"pass"}));let o=e.map(c=>{let d=getComputedStyle(c);return!/(hidden|clip)/.test(`${d.overflowX} ${d.overflowY}`)||!c.textContent.trim()?null:c.clientWidth>4&&c.clientHeight>4?c:null});if(!o.some(Boolean))return e.map(()=>({status:"pass"}));let a=o.map(c=>c&&Fa(c)),n=t.defaultView,r=n?.scrollX??0,i=n?.scrollY??0,s=t.createElement("style");s.dataset.pourAudit="probe",s.textContent=Mi,t.documentElement.append(s);let l;try{t.documentElement.offsetHeight,l=o.map(c=>c&&Fa(c))}finally{s.remove(),t.documentElement.offsetHeight,n&&(n.scrollX!==r||n.scrollY!==i)&&n.scrollTo(r,i)}return e.map((c,d)=>o[d]?a[d]||!l[d]?{status:"pass"}:{status:"incomplete",message:"With the WCAG text-spacing overrides applied (line height 1.5, letter/word spacing bumps), this container overflows instead of growing \u2014 if that hides text rather than empty trailing spacing, users who need wider spacing lose content. Check with the overrides applied; to be safe, let the container grow (min-height instead of height, avoid overflow:hidden on text)."}:{status:"pass"})}};var Ba={id:"reflow",name:"Content reflow",impact:"moderate",tags:["wcag21aa","wcag1410"],help:"Content should reflow to one-dimensional scrolling",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/reflow.html",selector:"html",visibleOnly:!1,evaluate(e){let t=e.ownerDocument,o=t.scrollingElement??t.documentElement,a=o.scrollWidth-o.clientWidth;return a<=1?{status:"pass"}:{status:"incomplete",message:`The page already scrolls horizontally by ${Math.round(a)}px at the current viewport \u2014 at the 320px reflow breakpoint it will be worse. Verify content reflows at 320 CSS px width (data tables, maps and images are exempt).`}}};var Oi=/:focus(?:-visible)?(?![\w-])/g,Ui=/:focus(?:-visible)?(?![\w-])/;function Di(e){let t=[],o=u=>u.getPropertyValue("box-shadow")||u.getPropertyValue("border")||u.getPropertyValue("border-color")||u.getPropertyValue("background")||u.getPropertyValue("background-color")||u.getPropertyValue("text-decoration"),a=u=>`${u.getPropertyValue("outline")} ${u.getPropertyValue("outline-style")} ${u.getPropertyValue("outline-width")}`,n=u=>/\bnone\b/.test(u)||/(^|\s)0(px)?(\s|$)/.test(u),r=u=>u.split(",").map(h=>h.replace(Oi,"").replace(/\s+/g," ").trim()),i=null,s=u=>{if(!u.selectorText||!/:focus/.test(u.selectorText)||/:not\(\s*:focus-visible\s*\)/.test(u.selectorText))return;let h=u.style;if(!h)return;let f=[h,...[...u.cssRules??[]].filter(w=>w.style&&!w.selectorText).map(w=>w.style)],m=f.some(o),b=f.map(a).filter(w=>w.trim()),p=b.some(w=>!n(w));Ui.test(u.selectorText)&&(m||p)&&i.providers.push({order:i.order,subjects:r(u.selectorText)}),!(!b.some(n)||m)&&i.suspects.push({order:i.order,selector:u.selectorText,subjects:r(u.selectorText)})},l=(u,h)=>h.subjects.some(f=>f===u||f==="*"||f===""),c=()=>{for(let u of i.suspects)u.subjects.every(f=>i.providers.some(m=>m.order>u.order&&l(f,m)))||t.push(u.selector)},d=u=>{for(let h of u)i.order++,s(h),h.cssRules?.length&&d(h.cssRules)};for(let u of e.styleSheets){i={order:0,suspects:[],providers:[]};try{d(u.cssRules)}catch{}c()}return t}var Ga={id:"focus-visible",name:"Visible focus",impact:"serious",tags:["wcag2aa","wcag247"],help:"Keyboard focus must remain visible",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/focus-visible.html",selector:"html",visibleOnly:!1,evaluate(e){let t=Di(e.ownerDocument);if(!t.length)return{status:"pass"};let o=t.slice(0,3).join(", ");return{status:"incomplete",message:`${t.length} CSS rule(s) remove the focus outline without setting a replacement in the same rule (e.g. ${o}) \u2014 if no other rule provides a visible indicator, keyboard users lose their place. Tab through the page to check.`}}};var Fi='a[href], button, input:not([type="hidden"]), select, textarea, summary, [tabindex]:not([tabindex="-1"])';function Pi(e,t,o){let a=e.scrollingElement??e.documentElement,n=getComputedStyle(a),r=t==="top"?n.scrollPaddingTop:n.scrollPaddingBottom;return(r&&r.endsWith("px")?parseFloat(r):0)>=o-1}var qa={id:"focus-not-obscured",name:"Unobscured focus",impact:"serious",tags:["wcag22aa","wcag2411"],help:"Focused elements must not be fully hidden behind overlays",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/focus-not-obscured-minimum.html",selector:Fi,visibility:"visual",evaluateAll(e){let t=e[0]?.ownerDocument??document,o=t.defaultView;if([...t.querySelectorAll('dialog[open], [role="dialog"], [role="alertdialog"], [aria-modal="true"]')].some(s=>{let l=s.getBoundingClientRect();return l.width>0&&l.height>0&&getComputedStyle(s).visibility!=="hidden"}))return e.map(()=>({status:"pass"}));let n=new Map,r=s=>{if(n.has(s))return n.get(s);let l=!1,c=getComputedStyle(s);if(c.position==="fixed"||c.position==="sticky"){let d=c.backgroundColor.match(/rgba?\(([^)]+)\)/)?.[1]?.split(","),u=d?.[3]===void 0?1:parseFloat(d[3]),h=s.getBoundingClientRect();l=u>=.9&&h.width>=40&&h.height>=24}return n.set(s,l),l},i=(s,l)=>s.left>=l.left&&s.right<=l.right&&s.top>=l.top&&s.bottom<=l.bottom;return e.map(s=>{if(s.matches(":disabled"))return{status:"pass"};if(pe(s))return{status:"pass"};let l=s.getBoundingClientRect();if(!l.width||!l.height)return{status:"pass"};if(l.bottom<0||l.right<0||l.top>o.innerHeight||l.left>o.innerWidth)return{status:"pass"};let c=Math.min(Math.max(l.left+l.width/2,0),o.innerWidth-1),d=Math.min(Math.max(l.top+l.height/2,0),o.innerHeight-1),u=t.elementsFromPoint(c,d),h=u.indexOf(s);if(h<=0)return{status:"pass"};let f=u.slice(0,h).find(p=>!p.contains(s)&&!s.contains(p)&&r(p)&&i(l,p.getBoundingClientRect()));if(!f)return{status:"pass"};let m=f.getBoundingClientRect(),b=m.top<=1&&m.bottom<o.innerHeight?"top":m.bottom>=o.innerHeight-1?"bottom":null;return b&&Pi(t,b,m.height)?{status:"pass"}:{status:"incomplete",message:"This element is currently underneath an opaque fixed panel. Whether that breaks 2.4.11 depends on where the page sits when focus reaches it \u2014 the browser does not scroll an element that is already in the viewport, merely covered, so focus can land invisibly. Tab through the page and check the focus indicator is never entirely hidden.",fix:`Reserve room for the panel with scroll-padding-${b??"bottom"} on the scrolling container, or move focus clear of it when the panel is up.`}})}};var za={id:"auth-field-obstruction",name:"Accessible login fields",impact:"serious",tags:["wcag22aa","wcag338"],help:"Login fields must not block paste or password managers",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/accessible-authentication-minimum.html",selector:'input[type="password"]:not([autocomplete~="new-password"]), input[autocomplete~="current-password"], input[autocomplete~="one-time-code"]',evaluate(e){let t=/return\s+false|preventDefault/.test(e.getAttribute("onpaste")??""),o=(e.getAttribute("autocomplete")??"").trim().toLowerCase().split(/\s+/).filter(Boolean),a=o.length===1&&o[0]==="off";if(!t&&!a)return{status:"pass"};let n=e.form??e.closest("form"),r=n?n.querySelectorAll('input[type="password"]').length:e.type==="password"?1:0,i=n?.querySelector('input[autocomplete~="username"], input[autocomplete~="email"], input[type="email"]'),s=o.includes("current-password")||o.includes("one-time-code")||r===1&&!!i,l=r>=2?"This form holds more than one password field, the shape of account sign-up or a password change, which 3.3.8 does not cover unless the field takes an existing credential. Confirm whether this field is part of an authentication step at all.":"Nothing here shows the field takes an existing credential (no current-password purpose, and no username or email field beside it), so confirm it is part of signing in before treating this as a failure.",c=s?"this login":"this step";return t&&a?s?{status:"fail",message:'This authentication field blocks pasting AND sets autocomplete="off", so both of the mechanisms 3.3.8 names are obstructed at once: copy and paste, and password-manager entry. What is left is typing the credential from memory.',fix:'Remove the paste blocking, and drop autocomplete="off" so a password manager can fill the field.'}:{status:"incomplete",message:`This password field blocks pasting AND sets autocomplete="off". If it is part of signing in, both of the mechanisms 3.3.8 names are obstructed at once (copy and paste, and password-manager entry) and it fails. ${l}`,fix:'Remove the paste blocking, and drop autocomplete="off" so a password manager can fill the field.'}:t?{status:"incomplete",message:`This field blocks pasting, which removes copy and paste, one of the two mechanisms 3.3.8 names. A password manager fills the field directly and is unaffected, so this is only a failure if nothing else here helps. Check whether a password manager can complete ${c}, or whether the page offers another way in such as a passkey or a federated sign-in.${s?"":` ${l}`}`,fix:"Remove the paste blocking, or make sure another way to sign in is available that does not rely on recalling the credential."}:{status:"incomplete",message:`This field sets autocomplete="off", discouraging password managers. Browsers often fill anyway: verify a password manager can complete ${c} without retyping.${s?"":` ${l}`}`}}};var _a={id:"embed-alt",name:"Embed accessible name",impact:"serious",tags:["wcag2a","wcag111"],help:"<embed> needs an accessible name",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/non-text-content.html",selector:"embed",evaluate(e,{accessibleName:t}){if(t(e))return{status:"pass"};let o=(e.getAttribute("type")??"").trim().toLowerCase(),a=(e.getAttribute("src")??"").split(/[?#]/)[0].toLowerCase();return o.startsWith("text/")||o==="application/pdf"||o==="application/xhtml+xml"||!o&&/\.(html?|xhtml|pdf|txt)$/.test(a)?{status:"incomplete",message:"This <embed> renders a text document but has no accessible name \u2014 assistive technology users get no indication of what the embedded document is. Check it is identified in the surrounding context.",fix:'Add title="\u2026" naming the embedded document.'}:{status:"fail",message:"This <embed> has no accessible name \u2014 screen readers cannot tell users what the embedded content is.",fix:'Add title="\u2026" (or aria-label) describing what the embed shows.'}}};var Va={id:"canvas-alt",name:"Canvas accessible name",impact:"moderate",tags:["wcag2a","wcag111"],help:"Meaningful <canvas> graphics need an accessible name or fallback",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/non-text-content.html",selector:"canvas:not([role])",evaluate(e,{accessibleName:t}){if(t(e))return{status:"pass"};if([...e.childNodes].filter(r=>!/^(SCRIPT|STYLE|TEMPLATE)$/.test(r.nodeName)).map(r=>r.textContent).join("").trim())return{status:"pass"};let a=e.closest('a[href], button, [role="button"], [role="link"]');if(a&&t(a))return{status:"pass"};let n=e.getBoundingClientRect();return n.width<32||n.height<32?{status:"pass"}:{status:"incomplete",message:"This canvas has no accessible name, no fallback content, and is not marked decorative \u2014 if it draws a chart or diagram, screen reader users get nothing at all.",fix:'If decorative, add aria-hidden="true". If meaningful, add role="img" with aria-label="\u2026", or put a text alternative inside the <canvas> as fallback content.'}}};var Ha={id:"video-loop-motion",name:"Looping video control",impact:"serious",tags:["wcag2a","wcag222"],help:"Looping autoplay video must be pausable",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/pause-stop-hide.html",selector:"video[autoplay][loop]:not([controls])",visibility:"visual",evaluate(e){let t=e.getBoundingClientRect();return t.width<64&&t.height<64?{status:"pass"}:e.paused&&e.readyState>=2?{status:"pass"}:{status:"incomplete",message:"This video auto-plays on a loop with no built-in controls: it starts by itself and never ends, so it runs well past the five seconds at which WCAG 2.2.2 requires a way to pause, stop or hide it. Check the page offers a pause control that reaches this video, or that the video is the only content presented.",fix:"Add controls, wire a visible pause button to it, or drop autoplay \u2014 and honour prefers-reduced-motion."}}};var Ft=/^(inline-)?(flex|grid)$/;function Pt(e){return!(e.disabled||e.tabIndex<0||pe(e))}function Bt(e,t){let o=[];for(let a of[...e].sort((n,r)=>n.rect.top-r.rect.top)){let n=o.find(r=>{let i=r[0].rect;return Math.min(i.bottom,a.rect.bottom)-Math.max(i.top,a.rect.top)>Math.min(i.height,a.rect.height)/2});n?n.push(a):o.push([a])}return o.flatMap(a=>a.sort((n,r)=>t?r.rect.right-n.rect.right:n.rect.left-r.rect.left))}var ja={id:"visual-order-divergence",name:"Focus order vs visual order",impact:"moderate",tags:["wcag2a","wcag243"],help:"CSS reordering should not make Tab jump against the visual order",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/focus-order.html",selector:'a[href], button, input:not([type="hidden"]), select, textarea, summary, [tabindex]',evaluateAll(e){let t=new Map,o=new Map;for(let a of e){if(!Pt(a))continue;let n=a.parentElement;n&&(o.has(n)||o.set(n,[]),o.get(n).push(a))}for(let[a,n]of o){if(n.length<2)continue;let r=getComputedStyle(a);if(!Ft.test(r.display))continue;let i=/-reverse$/.test(r.flexDirection),s=n.map(h=>({element:h,style:getComputedStyle(h)})).filter(({style:h})=>h.position!=="absolute"&&h.position!=="fixed").map(h=>({...h,rect:h.element.getBoundingClientRect()})).filter(({rect:h})=>h.width>0&&h.height>0);if(s.length<2)continue;let l=s.some(({style:h})=>h.order!=="0");if(!i&&!l||!Bt(s,r.direction==="rtl").some((h,f)=>h!==s[f]))continue;let u=i?`flex-direction: ${r.flexDirection}`:"CSS order";t.set(s[0].element,{status:"incomplete",message:`Keyboard focus moves through these ${s.length} controls in DOM order, but ${u} arranges them differently on screen \u2014 Tab will jump against the visual layout. Check the focus sequence still preserves meaning and operability.`,fix:"Reorder the source to match the visual order instead of reordering with CSS, or confirm the divergence is harmless here."})}return e.map(a=>t.get(a)??{status:"pass"})}};var Ya={id:"audio-transcript",name:"Audio transcript",impact:"serious",tags:["wcag2a","wcag121"],help:"Prerecorded audio-only content needs a transcript",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/audio-only-and-video-only-prerecorded.html",selector:"audio",visibleOnly:!1,evaluate(e){return!e.currentSrc&&!e.getAttribute("src")&&!e.querySelector("source")?{status:"pass"}:(e.muted||e.volume===0)&&!e.hasAttribute("controls")?{status:"pass"}:{status:"incomplete",message:"This audio content needs a transcript nearby \u2014 deaf and hard-of-hearing users get nothing from the recording itself. Check that an equivalent text version exists and is easy to find from the player (1.2.1 covers prerecorded audio; a live stream is 1.2.9 instead).",fix:"Provide a transcript of the recording as text on the page or behind a clearly labelled link next to the player."}}};var Ka={id:"reading-order-divergence",name:"Reading order vs visual order",impact:"moderate",tags:["wcag2a","wcag132"],help:"CSS reordering should not make reading order diverge from the visual order",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/meaningful-sequence.html",selector:"*",evaluateAll(e){let t=new Map,o=new Map;for(let a of e){let n=a.parentElement;n&&(o.has(n)||o.set(n,[]),o.get(n).push(a))}for(let[a,n]of o){if(n.length<2)continue;let r=getComputedStyle(a);if(!Ft.test(r.display))continue;let i=/-reverse$/.test(r.flexDirection),s=n.map(f=>({element:f,style:getComputedStyle(f)})).filter(({style:f})=>f.position!=="absolute"&&f.position!=="fixed").map(f=>({...f,rect:f.element.getBoundingClientRect()})).filter(({rect:f})=>f.width>0&&f.height>0);if(s.length<2)continue;let l=s.some(({style:f})=>f.order!=="0");if(!i&&!l||s.filter(({element:f})=>(f.textContent||"").trim().length>0).length<2||s.every(({element:f})=>f.matches("a[href], button, input, select, textarea, summary, [tabindex]")&&Pt(f))||!Bt(s,r.direction==="rtl").some((f,m)=>f!==s[m]))continue;let h=i?`flex-direction: ${r.flexDirection}`:"CSS order";t.set(s[0].element,{status:"incomplete",message:`A screen reader reads these ${s.length} blocks in DOM order, but ${h} arranges them differently on screen \u2014 sighted and screen reader users get two different sequences. Check the source order still tells the same story as the layout.`,fix:"Reorder the source to match the visual order instead of reordering with CSS, or confirm the sequence difference does not change meaning here."})}return e.map(a=>t.get(a)??{status:"pass"})}};var Xa={id:"drag-alternative",name:"Dragging alternative",impact:"moderate",tags:["wcag22aa","wcag257"],help:"Dragging must have a single-pointer alternative",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/dragging-movements.html",selector:'[draggable="true" i]:not(img):not(a[href])',evaluate(){return{status:"incomplete",message:"This element declares itself draggable, and 2.5.7 requires everything achievable by dragging to also work with single clicks or taps \u2014 tremors, switch access and head pointers can press but not drag. Check an equivalent no-drag path exists (buttons to reorder, a menu to move, a field to set the value), unless dragging itself is essential here.",fix:'Add a single-pointer way to do the same thing, e.g. up/down buttons beside a sortable item or a "move to" action in a menu.'}}};var Bi=new Set(["new-password","current-password","one-time-code","on","off"]),Za={id:"redundant-entry",name:"Redundant entry",impact:"moderate",tags:["wcag22a","wcag337"],help:"A form should not ask for the same information twice",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/redundant-entry.html",selector:"form",evaluate(e,{isVisible:t}){let o=new Map;for(let a of e.querySelectorAll("input[autocomplete], select[autocomplete], textarea[autocomplete]")){if(a.disabled||a.readOnly||!t(a)||a instanceof HTMLInputElement&&(a.type==="hidden"||a.type==="password"))continue;let n=(a.getAttribute("autocomplete")||"").trim().toLowerCase().replace(/\s+/g," ");if(!(!n||Bi.has(n))){if(o.has(n))return{status:"incomplete",message:`This form asks for "${n}" twice \u2014 3.3.7 says information the user already entered must be auto-populated or selectable, not typed again. A deliberate confirmation field can be essential (the criterion's own escape); that judgement is yours.`,fix:"Auto-populate the second field from the first, offer the earlier value for selection, or drop the duplicate. Keep it only if re-entry is genuinely essential here.",data:{purpose:n}};o.set(n,a)}}return{status:"pass"}}};var Gi=new Set(["toggle-popover","show-popover","hide-popover"]),qi=new Set(["show-modal","close","request-close"]),Ja={id:"invoker-target",name:"Popover and command targets",impact:"serious",tags:["wcag2a","wcag412"],help:"popovertarget and commandfor must point at an element they can act on",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/name-role-value.html",selector:"[popovertarget], [commandfor]",evaluate(e){let t=e.getRootNode(),o=n=>n&&t.getElementById?.(n)||null,a=[];if(e.hasAttribute("popovertarget")){let n=e.getAttribute("popovertarget").trim(),r=o(n);r?r.hasAttribute("popover")||a.push(`popovertarget="${n}" points at a <${r.tagName.toLowerCase()}> with no popover attribute, which the button cannot open`):a.push(`popovertarget="${n}" names no element in this document`)}if(e.hasAttribute("commandfor")){let n=e.getAttribute("commandfor").trim(),r=(e.getAttribute("command")??"").trim().toLowerCase(),i=o(n);i?r?Gi.has(r)&&!i.hasAttribute("popover")?a.push(`command="${r}" needs a popover, and "${n}" has no popover attribute`):qi.has(r)&&i.tagName!=="DIALOG"&&a.push(`command="${r}" needs a <dialog>, and "${n}" is a <${i.tagName.toLowerCase()}>`):a.push("commandfor has no command attribute, so activating the button does nothing"):a.push(`commandfor="${n}" names no element in this document`)}return a.length?{status:"fail",message:`${a.join("; ")}. Activating this button does nothing, and the expanded state and control relationship the browser would expose for it are missing.`,fix:"Point the attribute at the id of the popover or dialog it drives (give that element the popover attribute, or make it a <dialog>), and keep the id unique in this tree."}:{status:"pass"}}};var zi=["transform","translate","rotate","scale","offset-path","offset-distance","offset-rotate","left","right","top","bottom"],_i=/:(?:hover|focus-visible|focus-within|focus|active)\b/,Vi=/:(?:hover|focus-visible|focus-within|focus|active)\b/g,Hi=/\b(?:all|transform|translate|rotate|scale|offset-path|offset-distance|offset-rotate|left|right|top|bottom|inset)\b/;function ji(e){let t=e.style;if(["offset-path","offset-distance","offset-rotate","rotate","left","right","top","bottom"].some(i=>t.getPropertyValue(i)))return!0;let o=["transform","translate","scale"].map(i=>t.getPropertyValue(i)).join(" ");if(/rotate|skew|matrix|%|em|rem|vw|vh/.test(o)||[...o.matchAll(/(-?\d*\.?\d+)px/g)].map(i=>Math.abs(parseFloat(i[1]))).some(i=>i>=8))return!0;let n=[...o.matchAll(/scale[XYZ3d]*\(([^)]*)\)/gi)].flatMap(i=>i[1].split(",").map(s=>parseFloat(s))),r=t.getPropertyValue("scale").split(/\s+/).map(i=>parseFloat(i)).filter(i=>!Number.isNaN(i));return[...n,...r].some(i=>!Number.isNaN(i)&&(i<.85||i>1.15))}function Yi(e,t){let o=t.style.getPropertyValue("transition-duration")||t.style.getPropertyValue("animation-name");if(o&&o!=="none"&&o!=="0s")return!0;let a=t.selectorText.replace(Vi,"").trim();if(!a||/^[,\s]*$/.test(a))return!1;let n;try{n=e.querySelectorAll(a)}catch{return!1}let r=0;for(let i of n){if(r++>=5)break;let s=e.defaultView.getComputedStyle(i);if(s.transitionDuration.split(",").map(c=>parseFloat(c)||0).some(c=>c>0)&&Hi.test(s.transitionProperty))return!0}return!1}function Ki(e){let t={honours:!1,hoverMotion:0,hoverSelectors:[],unreadable:0},o=n=>{for(let r of n){let i=r.media?.mediaText;i&&/prefers-reduced-motion/i.test(i)&&(t.honours=!0),r.selectorText&&_i.test(r.selectorText)&&r.style&&zi.some(l=>r.style.getPropertyValue(l))&&ji(r)&&Yi(e,r)&&(t.hoverMotion+=1,t.hoverSelectors.length<3&&t.hoverSelectors.push(r.selectorText.slice(0,80)));let s=r.cssRules??r.styleSheet?.cssRules;if(s)try{o(s)}catch{t.unreadable+=1}}},a=[...e.styleSheets??[],...e.adoptedStyleSheets??[]];for(let n of a)try{o(n.cssRules)}catch{t.unreadable+=1}return t}function Xi(e){if(typeof e.getAnimations!="function")return 0;let t;try{t=e.getAnimations()}catch{return 0}let o=0;for(let a of t){if(!a.timeline||a.timeline===e.timeline)continue;let n;try{n=a.effect?.getKeyframes?.()??[]}catch{continue}Mt(n)&&(o+=1)}return o}var Qa={id:"reduced-motion",name:"Motion honours reduced-motion",impact:"moderate",tags:["wcag21aaa","wcag233"],help:"Motion set off by scrolling, hovering or focusing should be switchable off",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/animation-from-interactions.html",selector:"html",visibleOnly:!1,evaluate(e){let t=e.ownerDocument,o=t.defaultView,a=Ki(t);if(a.honours)return{status:"pass"};let n=[],r=Xi(t);if(r&&n.push(`${r} scroll-driven animation${r===1?"":"s"} that move${r===1?"s":""} content`),[t.documentElement,t.body].some(l=>l&&o.getComputedStyle(l).scrollBehavior==="smooth")&&n.push("smooth scrolling on in-page navigation (scroll-behavior: smooth)"),a.hoverMotion){let l=a.hoverMotion>a.hoverSelectors.length?", \u2026":"";n.push(`${a.hoverMotion} style rule${a.hoverMotion===1?"":"s"} animating movement on hover or focus (${a.hoverSelectors.join(", ")}${l})`)}if(!n.length)return{status:"pass"};let s=a.unreadable?` (${a.unreadable} stylesheet${a.unreadable===1?"":"s"} could not be read and might carry one)`:"";return{status:"incomplete",message:`This page moves content when people interact with it (${n.join("; ")}), and none of the stylesheets this audit could read honours prefers-reduced-motion${s}. 2.3.3 asks that such motion can be turned off unless it is essential.`,fix:"Put the motion inside @media (prefers-reduced-motion: no-preference), or remove it under @media (prefers-reduced-motion: reduce), keeping only motion that is essential to the function or the information."}}};var en=/\blocation\s*(\+?=(?!=)|\.\s*(href\s*\+?=(?!=)|(assign|replace|reload)\s*\())|\.(submit|requestSubmit)\s*\(|window\.open\s*\(/,tn={id:"on-input-navigation",name:"Unexpected context change",impact:"serious",tags:["wcag2a","wcag322","wcag321"],help:"Changing or focusing a control must not unexpectedly change context",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/on-input.html",selector:"select[onchange], input[onchange], textarea[onchange], select[oninput], input[oninput], textarea[oninput], [onfocus]",evaluate(e){let t=e.getAttribute("onfocus");if(t&&en.test(t))return{status:"incomplete",message:"Focusing this element appears to navigate or submit (its onfocus handler reaches for location/submit). A change of context on focus alone fails WCAG 3.2.1 \u2014 check what the handler actually does when focus lands.",fix:"Trigger navigation from activation (click/Enter), never from focus."};for(let o of["onchange","oninput"]){let a=e.getAttribute(o);if(a&&en.test(a))return{status:"incomplete",message:`Changing this control appears to navigate or submit (its ${o} handler reaches for location/submit). WCAG 3.2.2 allows that only when users are told beforehand: check the page says so before the control.`,fix:"Describe the behaviour before the control, or navigate from an explicit Go button instead of the change event."}}return{status:"pass"}}};var on={id:"error-message-linkage",name:"Error message linkage",impact:"serious",tags:["wcag2a","wcag331"],help:"Fields marked invalid need their error described in text",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/error-identification.html",selector:'[aria-invalid="true"], [aria-invalid="spelling"], [aria-invalid="grammar"], [aria-errormessage]',evaluate(e,{isVisible:t,accessibleName:o}){let a=e.getRootNode(),n=e.getAttribute("aria-invalid"),r=!!n&&n!=="false"&&n!=="undefined",i=l=>(e.getAttribute(l)??"").trim().split(/\s+/).filter(Boolean).map(c=>a.getElementById?.(c)).filter(Boolean),s=l=>l.some(c=>t(c)&&(c.textContent.trim()||o(c)));if(e.hasAttribute("aria-errormessage")){if(!r)return{status:"pass"};let l=i("aria-errormessage");return s(l)?{status:"pass"}:{status:"incomplete",message:(l.length?"This field is marked invalid, but the element its aria-errormessage points at is empty or hidden \u2014 screen readers follow the reference and find no text.":"This field is marked invalid, but its aria-errormessage points at nothing \u2014 screen readers follow the reference and find no text.")+" The error must be described in text: check a visible message exists, and fix the reference so it reaches assistive technology too.",fix:"Point aria-errormessage at the visible element containing the error text, and keep that element populated while the field is invalid."}}return s(i("aria-describedby"))?{status:"pass"}:{status:"incomplete",message:"This field is marked invalid, but no error text is programmatically linked to it. The error must be described to the user in text \u2014 check a message exists, and associate it with aria-describedby or aria-errormessage so screen readers announce it with the field.",fix:"Add the error text near the field and reference it with aria-describedby (or aria-errormessage)."}}};var Zi=new Set(["tab","menuitem","menuitemcheckbox","menuitemradio","option","treeitem"]),an={id:"composite-widget-name",name:"Widget item names",impact:"serious",tags:["wcag2a","wcag412"],help:"Tabs, menu items, options and tree items need an accessible name",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/name-role-value.html",selector:'[role="tab"], [role="menuitem"], [role="menuitemcheckbox"], [role="menuitemradio"], [role="option"], [role="treeitem"]',evaluate(e,{accessibleName:t}){let o=Y(e);return Zi.has(o)?t(e)?{status:"pass"}:{status:"fail",message:`This ${o} has no accessible name \u2014 a screen reader announces an unnamed ${o}, and users cannot tell what choosing it does.`,fix:'Put text inside the element, or add aria-label="\u2026" for icon-only items.'}:{status:"pass"}}};var nn={id:"summary-name",name:"Disclosure names",impact:"serious",tags:["wcag2a","wcag412"],help:"Disclosure controls (<summary>) need an accessible name",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/name-role-value.html",selector:"summary",evaluate(e,{accessibleName:t}){let o=e.parentElement;return o?.tagName==="DETAILS"&&o.querySelector(":scope > summary")===e?t(e)?{status:"pass"}:{status:"fail",message:"This disclosure control has no accessible name \u2014 a screen reader announces an unnamed button, and users cannot tell what it expands.",fix:'Put text inside the <summary>, or add aria-label="\u2026" for icon-only disclosures.'}:{status:"pass"}}};var Gt=e=>e.hasAttribute("aria-level")?parseInt(e.getAttribute("aria-level"),10):parseInt(e.tagName[1],10)||2,Ji=e=>{let t=Y(e);return t==="heading"||t===null},rn={id:"heading-order",name:"Heading order",impact:"moderate",tags:["best-practice"],help:"Headings should step down one level at a time",helpUrl:"https://www.w3.org/WAI/tutorials/page-structure/headings/",selector:'h1, h2, h3, h4, h5, h6, [role="heading"]',evaluateAll(e){let t=0;return e.map(o=>{if(!Ji(o))return{status:"pass"};let a=Gt(o),r=t>0&&a>t+1?{status:"fail",message:`Heading level jumps from h${t} to h${a}, which breaks the page outline for screen-reader navigation.`,fix:`Use <h${t+1}> here, or restructure so levels increase one at a time.`}:{status:"pass"};return t=a,r})}};var sn={id:"empty-heading",name:"Empty headings",impact:"minor",tags:["best-practice"],help:"Headings must contain text",helpUrl:"https://www.w3.org/WAI/tutorials/page-structure/headings/",selector:'h1, h2, h3, h4, h5, h6, [role="heading"]',evaluate(e,{accessibleName:t}){let o=e.getAttribute("role");return o&&o!=="heading"?{status:"pass"}:t(e)?{status:"pass"}:{status:"fail",message:"This heading is empty \u2014 screen-reader users navigating by headings land on nothing.",fix:"Add text to the heading, or remove the element if it is only used for spacing."}}};var ln={id:"positive-tabindex",name:"Positive tabindex",impact:"serious",tags:["best-practice"],help:"Positive tabindex values disrupt the natural focus order",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/focus-order.html",selector:"[tabindex]",evaluate(e){let t=parseInt(e.getAttribute("tabindex"),10);return t>0?{status:"fail",message:`tabindex="${t}" overrides the natural focus order, which almost always creates a confusing keyboard path.`,fix:'Use tabindex="0" and place the element where it belongs in the document order instead.'}:{status:"pass"}}};var qt='main, nav, aside, search, form[aria-label], form[aria-labelledby], header:not(:is(article, aside, main, nav, section) header), footer:not(:is(article, aside, main, nav, section) footer), section[aria-label], section[aria-labelledby], [role="main"], [role="navigation"], [role="banner"], [role="contentinfo"], [role="complementary"], [role="region"], [role="search"], [role="form"], [role="dialog"], [role="alertdialog"], dialog[open]',cn={id:"region",name:"Content outside landmarks",impact:"moderate",tags:["best-practice"],help:"All readable content belongs inside a landmark region",helpUrl:"https://www.w3.org/WAI/ARIA/apg/practices/landmark-regions/",selector:"body *:not(script):not(style):not(template):not(noscript)",evaluateAll(e,t){return(e[0]?.ownerDocument??document).querySelector(qt)?e.map(a=>this.judge(a,t)):e.map(()=>({status:"pass"}))},judge(e,{ownText:t}){let o=e.matches('img:not([alt=""]):not([role="presentation"]):not([role="none"]), svg[role="img"], video, audio, canvas, iframe, input:not([type="hidden"]), select, textarea, button');if(!t(e)&&!o)return{status:"pass"};if(e.closest(qt))return{status:"pass"};let a=e.ownerDocument,n=e.closest('a[href*="#"]');if(n){let r=n.getAttribute("href").split("#")[1]??"";try{r=decodeURIComponent(r)}catch{}let i=r&&(a.getElementById(r)||a.getElementsByName(r)[0]),s=/^skip\b/i.test(n.textContent.trim()),l=a.querySelector(qt),c=!l||n.compareDocumentPosition(l)&Node.DOCUMENT_POSITION_FOLLOWING;if((i||s)&&c)return{status:"pass"}}for(let r=e.parentElement;r&&r!==a.body;r=r.parentElement)if(t(r))return{status:"pass"};return{status:"fail",message:"This content sits outside any landmark, so screen-reader users navigating by regions never reach it.",fix:"Move it into <main>, <nav>, <header>, <footer>, <aside>, or a labelled <section>."}}};var un={id:"landmark-one-main",name:"One main landmark",impact:"moderate",tags:["best-practice"],help:"The page should have exactly one main landmark",helpUrl:"https://www.w3.org/WAI/ARIA/apg/practices/landmark-regions/",selector:"html",visibleOnly:!1,evaluate(e,{isVisible:t}){let o=de(e.ownerDocument).flatMap(a=>[...a.querySelectorAll('main, [role="main"]')]).filter(t).length;return o===1?{status:"pass"}:{status:"fail",message:o===0?"No <main> landmark \u2014 screen-reader users have no shortcut to the primary content.":`${o} main landmarks \u2014 "skip to main" becomes ambiguous.`,fix:o===0?"Wrap the primary content in a single <main> element.":"Keep one <main>; demote the others to <section> or <div>."}}};var Qi='h1, h2, h3, h4, h5, h6, [role="heading"]',dn={id:"page-heading-one",name:"Leading page heading",impact:"moderate",tags:["best-practice"],help:"Every page should start its outline with an <h1>",helpUrl:"https://www.w3.org/WAI/tutorials/page-structure/headings/",selector:"html",visibleOnly:!1,evaluate(e){return de(e.ownerDocument).some(o=>[...o.querySelectorAll(Qi)].some(a=>Gt(a)===1))?{status:"pass"}:{status:"fail",message:"No <h1> \u2014 the page outline has no starting point for screen-reader navigation.",fix:"Add one <h1> naming what the page is about."}}};var pn=new Set(["banner","contentinfo","main"]),es=new Set(["banner","complementary","contentinfo","form","main","navigation","region","search"]),ts=e=>e.getAttribute("role")?.trim().split(/\s+/)[0]?.toLowerCase()??Q(e),os=e=>J(e)||e.getAttribute("aria-label")?.trim()||e.getAttribute("title")?.trim()||"",hn={id:"landmark-unique",name:"Distinguishable landmarks",impact:"moderate",tags:["best-practice"],help:"Landmarks of the same type need distinguishing labels",helpUrl:"https://www.w3.org/WAI/ARIA/apg/practices/landmark-regions/",selector:'nav, search, aside, header, footer, section[aria-label], section[aria-labelledby], form[aria-label], form[aria-labelledby], [role="navigation"], [role="complementary"], [role="banner"], [role="contentinfo"], [role="region"], [role="search"], [role="form"], [role="main"], main',evaluateAll(e){let t=e.map(ts),o=e.map(()=>({status:"pass"})),a=e.map((r,i)=>({element:r,index:i,role:t[i]})).filter(({role:r})=>es.has(r));for(let r of pn){let i=a.filter(({role:s})=>s===r);if(!(i.length<2))for(let{index:s}of i)o[s]={status:"fail",message:`${i.length} ${r} landmarks on one page \u2014 ${r} must be unique; screen-reader region navigation becomes ambiguous.`,fix:`Keep one ${r}; demote the others (remove the role, or use a non-landmark element).`}}let n={};for(let{element:r,index:i,role:s}of a){if(pn.has(s))continue;let l=`${s}::${os(r).toLowerCase()}`;(n[l]??=[]).push(i)}for(let[r,i]of Object.entries(n)){if(i.length<2)continue;let[s,l]=r.split("::");for(let c of i)o[c]={status:"fail",message:l?`${i.length} ${s} landmarks share the label "${l}" \u2014 screen-reader users can't tell them apart.`:`${i.length} unlabelled ${s} landmarks \u2014 screen-reader users hear "${s}" twice with no way to tell them apart.`,fix:'Give each one a distinct aria-label, e.g. aria-label="Primary" / aria-label="Footer".'}}return o}};var as=new Set(["ul","ol","li","button","nav","main","aside","form","table","article","img","hr","progress","textarea","dialog","option","h1","h2","h3","h4","h5","h6","tr","thead","tbody","tfoot","td","fieldset","a","input"]),ns=new Set(["checkbox","radio","button","submit","reset","text","email","tel","url","range","number","search"]);function rs(e){let t=e.tagName.toLowerCase();if(!as.has(t)||t==="input"&&!ns.has(e.type))return null;let o=Q(e);return o&&o!=="generic"&&o!=="presentation"?o:null}var fn={table:["table","inline-table"],thead:["table-header-group"],tbody:["table-row-group"],tfoot:["table-footer-group"],tr:["table-row"],td:["table-cell"]},is=new Set(["table","rowgroup","row","cell"]);function ss(e,t){let o=t==="table"?e:e.closest("table");if(o&&!fn.table.includes(getComputedStyle(o).display))return!0;let a=fn[t];return!!a&&!a.includes(getComputedStyle(e).display)}function mn(e){if(getComputedStyle(e).listStyleType==="none")return!0;let t=getComputedStyle(e,"::marker").content;return t==="none"||t==='""'||t==="''"}function ls(e){if(getComputedStyle(e).listStyleType==="none")return!0;for(let t of e.children)if(t.tagName==="LI"&&mn(t))return!0;return!1}var gn={id:"redundant-role",name:"Redundant roles",impact:"minor",tags:["best-practice"],help:"role attributes should not repeat the element\u2019s built-in role",helpUrl:"https://www.w3.org/TR/using-aria/#firstrule",selector:"[role]",evaluate(e){let t=e.getAttribute("role").trim().split(/\s+/)[0]?.toLowerCase(),o=rs(e);if(!o||t!==o)return{status:"pass"};let a=e.tagName.toLowerCase();if(t==="list"&&ls(e))return{status:"pass"};if(t==="listitem"){if(mn(e))return{status:"pass"};let r=e.closest("ul, ol");if(r&&getComputedStyle(r).listStyleType==="none")return{status:"pass"}}return is.has(t)&&ss(e,a)?{status:"pass"}:t==="img"&&/\.svg([?#]|$)/i.test(e.currentSrc||e.src||"")?{status:"pass"}:{status:"fail",message:`role="${t}" is redundant: <${a}> already has that role built in. Redundant ARIA adds noise and drift risk, not accessibility.${o==="list"?" (Exception: keep it if you set list-style: none \u2014 Safari/VoiceOver drops list semantics without it.)":""}`,fix:`Remove role="${t}" from the <${a}>.`}}};var cs=[{aria:"aria-required",native:"required"},{aria:"aria-disabled",native:"disabled"},{aria:"aria-readonly",native:"readonly"},{aria:"aria-placeholder",native:"placeholder"},{aria:"aria-checked",native:"checked"}],bn={id:"redundant-aria",name:"Redundant ARIA attributes",impact:"minor",tags:["best-practice"],help:"aria attributes should not duplicate native HTML attributes",helpUrl:"https://www.w3.org/TR/using-aria/#rule2",selector:'[aria-required], [aria-disabled], [aria-readonly], [aria-placeholder], [aria-checked], [aria-hidden="false"]',evaluate(e){let t=cs.filter(({aria:o,native:a})=>e.hasAttribute(o)&&e.hasAttribute(a)).map(({aria:o,native:a})=>`${o} duplicates ${a}`);return e.getAttribute("aria-hidden")==="false"&&t.push('aria-hidden="false" does nothing reliable \u2014 elements are visible to AT by default'),t.length?{status:"fail",message:`Redundant ARIA: ${t.join("; ")}. The native attribute already tells assistive technology everything.`,fix:"Remove the aria-* attribute(s) and keep the native HTML attribute."}:{status:"pass"}}};var us=new Set(["button","link","menuitem","menuitemcheckbox","menuitemradio","option","tab","treeitem","checkbox","radio","switch","heading","cell","gridcell","columnheader","rowheader","tooltip"]),wn=new Set(["none","normal",'""',"''"]);function ds(e){let t=[e,...e.querySelectorAll("*")];for(let o of t){let a=o.tagName.toLowerCase();if(a==="img"||a==="area"||a==="svg"||o!==e&&(o.getAttribute("aria-hidden")==="true"||o.hasAttribute("hidden")))return!0;let n=getComputedStyle(o);if(o!==e&&(n.display==="none"||n.visibility==="hidden")||!wn.has(getComputedStyle(o,"::before").content)||!wn.has(getComputedStyle(o,"::after").content))return!0}return!1}var yn={id:"redundant-aria-label",name:"Redundant aria-label",impact:"minor",tags:["best-practice"],help:"aria-label should not repeat the visible text",helpUrl:"https://www.w3.org/TR/using-aria/#rule2",selector:"[aria-label]",evaluate(e){if(!us.has(Y(e)))return{status:"pass"};let t=e.getAttribute("aria-label").replace(/\s+/g," ").trim().toLowerCase(),o=e.textContent.replace(/\s+/g," ").trim().toLowerCase();return!o||t!==o?{status:"pass"}:ds(e)?{status:"pass"}:{status:"fail",message:`aria-label="${e.getAttribute("aria-label")}" is identical to the element's visible text \u2014 it adds nothing and will drift out of sync when the text changes.`,fix:"Remove the aria-label and let the visible text be the accessible name."}}};var xn=/^(?:(?:image|icon)s?(?:\s*[:-]\s*|\s+(?:of|showing|depicting)\b\s*|\s*$)|(?:picture|graphic)s?\s+of\b\s*)/i,vn={id:"redundant-alt-phrase",name:"Redundant alt phrasing",impact:"minor",tags:["best-practice"],help:'alt text should not start with "image of" or similar',helpUrl:"https://www.w3.org/WAI/tutorials/images/tips/",selector:"img[alt]",evaluate(e){let t=e.getAttribute("alt").trim();return!t||!xn.test(t)?{status:"pass"}:{status:"fail",message:`alt="${t}" \u2014 screen readers already announce this element as an image, so users hear "image, ${t}".`,fix:`Describe the content directly, e.g. alt="${t.replace(xn,"").trim()||"\u2026"}".`}}};var An={id:"redundant-tabindex",name:"Redundant tabindex",impact:"minor",tags:["best-practice"],help:'tabindex="0" is unnecessary on natively focusable elements',helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/focus-order.html",selector:'button[tabindex="0"], a[href][tabindex="0"], input[tabindex="0"], select[tabindex="0"], textarea[tabindex="0"], summary[tabindex="0"]',evaluate(e){return{status:"fail",message:`<${e.tagName.toLowerCase()}> is focusable by default \u2014 tabindex="0" adds nothing.`,fix:"Remove the tabindex attribute."}}};var ps=new Set(["script","style","noscript","template"]);function Cn(e){let t="";for(let o of e.childNodes){if(o.nodeType===Node.TEXT_NODE){t+=o.textContent;continue}if(o.nodeType!==Node.ELEMENT_NODE)continue;let a=o.tagName.toLowerCase();if(ps.has(a)||a==="img"||a==="area"||a==="svg"||o.getAttribute("aria-hidden")==="true"||o.hasAttribute("hidden"))continue;let n=getComputedStyle(o);n.display==="none"||n.visibility==="hidden"||(t+=Cn(o))}return t}var kn=e=>(e??"").replace(/\s+/g," ").trim().toLowerCase(),Sn={id:"redundant-image-alt",name:"Redundant image alt",impact:"minor",tags:["best-practice"],help:"Image alt should not repeat adjacent link or button text",helpUrl:"https://www.w3.org/WAI/tutorials/images/functional/",selector:"a[href] img[alt], button img[alt], a[href] img[title]:not([alt]), button img[title]:not([alt])",evaluate(e){let t=e.getAttribute("alt"),o=t===null,a=kn(o?e.getAttribute("title"):t);if(!a)return{status:"pass"};let n=e.closest("a[href], button"),r=kn(Cn(n));if(!r)return{status:"pass"};if(a!==r)return{status:"pass"};let i=n.tagName==="A"?"link":"button";return{status:"fail",message:o?`The image title repeats the ${i} text \u2014 the title names the image, so screen readers announce "${e.getAttribute("title")}" twice.`:`The image alt repeats the ${i} text \u2014 screen readers announce "${e.getAttribute("alt")}" twice.`,fix:o?'Add alt="" to the image (and drop the title if it is not needed as a tooltip); the visible text already names the control.':'Use alt="" on the image; the visible text already names the control.'}}};var hs=new Set(["main","navigation","banner","contentinfo","complementary","region","search","form"]),fs=e=>e.hasAttribute("aria-label")||e.hasAttribute("aria-labelledby")||e.hasAttribute("title");function ms(e){for(let t=e.parentElement;t;t=t.parentElement){let o=t.getAttribute("role")?.trim().split(/\s+/)[0]?.toLowerCase(),a=o??Q(t);if(!(!o&&(a==="form"||a==="region")&&!fs(t))&&a&&hs.has(a))return{element:t,role:a}}return null}var Tn={id:"landmark-top-level",name:"Top-level landmarks",impact:"moderate",tags:["best-practice"],help:"banner, main, contentinfo and complementary landmarks must be top level",helpUrl:"https://www.w3.org/WAI/ARIA/apg/practices/landmark-regions/",selector:'header, footer, main, aside, [role="banner"], [role="contentinfo"], [role="main"], [role="complementary"]',evaluate(e){let t=e.tagName.toLowerCase();if((t==="header"||t==="footer")&&!e.hasAttribute("role")&&e.closest("article, aside, main, nav, section"))return{status:"pass"};if(t==="aside"&&!e.hasAttribute("role")&&Q(e)!=="complementary")return{status:"pass"};let o=ms(e);return o?{status:"fail",message:`This ${e.getAttribute("role")??{header:"banner",footer:"contentinfo",main:"main",aside:"complementary"}[t]} landmark is nested inside a ${o.role} landmark (<${o.element.tagName.toLowerCase()}>) \u2014 page-level regions lose their meaning when nested.`,fix:"Move it to be a direct child of <body>, or remove the landmark role."}:{status:"pass"}}};var Rn={id:"accesskeys",name:"Duplicate access keys",impact:"serious",tags:["best-practice"],help:"The same accesskey must not be assigned twice",helpUrl:"https://www.w3.org/WAI/WCAG22/Understanding/character-key-shortcuts.html",selector:"[accesskey]",visibleOnly:!1,evaluateAll(e){let t={};e.forEach((a,n)=>{let r=a.getAttribute("accesskey").trim().toLowerCase();(t[r]??=[]).push(n)});let o=e.map(()=>({status:"pass"}));for(let[a,n]of Object.entries(t))if(!(n.length<2))for(let r of n)o[r]={status:"fail",message:`accesskey="${a}" is used ${n.length} times \u2014 the browser can only honour one, so the others silently do nothing.`,fix:"Give each accesskey a unique value, or remove them (they conflict with AT shortcuts anyway)."};return o}};function gs(e){return e.querySelector('button, input[type="submit"], input[type="image"]')}var Nn={id:"button-type",name:"Button types in forms",impact:"minor",tags:["best-practice"],help:"Buttons inside forms should declare their type",helpUrl:"https://html.spec.whatwg.org/multipage/form-elements.html#attr-button-type",selector:'form button:not([type]), form button[type=""]',evaluate(e){let t=e.form??e.closest("form"),o=t.querySelectorAll("button");return o.length===1&&o[0]===e&&gs(t)===e?{status:"pass"}:{status:"fail",message:"A <button> without a type inside a form is a submit button by default (the HTML missing value default is the Submit Button state), so activating it submits the form. Declare the type so the intent is explicit.",fix:'Add type="button" (or type="submit" if submission is intended).'}}};var bs='dialog, [role="dialog"], [role="alertdialog"], [popover]',$n={id:"no-autofocus",name:"Autofocus on load",impact:"minor",tags:["best-practice"],help:"autofocus disorients assistive-technology users",helpUrl:"https://html.spec.whatwg.org/multipage/interaction.html#the-autofocus-attribute",selector:"[autofocus]",visibleOnly:!1,evaluate(e){if(e.closest(bs))return{status:"pass"};let t=e.tagName.toLowerCase();return{status:"incomplete",message:`autofocus moves focus to this <${t}> as soon as the page loads, so screen-reader and magnifier users skip everything before it. Review: the HTML spec allows this only when the control is the page's main purpose (a search box on a search page, say); otherwise it disorients.`,fix:`Remove autofocus from the <${t}> unless this control is the page's whole purpose; let users reach it in document order.`}}};var ws=/new (window|tab)|opens? in/i;function ys(e){return e.closest("[lang]")?.getAttribute("lang")?.trim()||e.ownerDocument.documentElement.getAttribute("lang")?.trim()||""}function xs(e){let t=e.getAttribute("aria-describedby");if(!t)return"";let o=e.getRootNode();return t.split(/\s+/).filter(Boolean).map(a=>o.getElementById?.(a)?.textContent??"").join(" ")}var In={id:"new-window-link",name:"New-window warnings",impact:"minor",tags:["best-practice"],help:"Links opening a new window should say so",helpUrl:"https://www.w3.org/WAI/WCAG22/Techniques/general/G201",selector:'a[target="_blank"], a[target="blank"]',evaluate(e,{accessibleName:t}){let o=`${t(e)} ${e.getAttribute("title")??""} ${xs(e)}`;if(ws.test(o))return{status:"pass"};let a=ys(e);return a&&!/^en(?:[-_]|$)/i.test(a)?{status:"incomplete",message:`This link opens a new window or tab. Does its name, title or description say so in the page's language ("${a}")? The automated wording check reads English only.`,fix:'Say so in the link text or a visually hidden span (for example the local-language equivalent of "opens in new window"), or drop target="_blank".'}:{status:"fail",message:"This link opens a new window/tab without saying so \u2014 the back button stops working and screen-reader users get no warning about the context change.",fix:'Append visually-hidden text like <span class="sr-only">(opens in new window)</span>, or drop target="_blank".'}}};var En={id:"fieldset-legend",name:"Grouped field labels",impact:"moderate",tags:["best-practice"],help:"Radio and checkbox groups should be grouped with a label",helpUrl:"https://www.w3.org/WAI/tutorials/forms/grouping/",selector:'input[type="radio"][name], input[type="checkbox"][name]',evaluateAll(e){let t={};e.forEach((a,n)=>{let r=`${a.type}::${a.form?.id??""}::${a.name}`;(t[r]??=[]).push(n)});let o=e.map(()=>({status:"pass"}));for(let a of Object.values(t)){if(a.length<2)continue;let n=e[a[0]],r=n.closest('fieldset, [role="group"], [role="radiogroup"]'),i=r?.querySelector(":scope > legend");r&&(i&&qe(i)||J(r)||r.getAttribute("aria-label")?.trim())||(o[a[0]]={status:"fail",message:`This group of ${a.length} ${n.type} inputs (name="${n.name}") has no group label \u2014 screen-reader users hear each option without knowing what question it answers.`,fix:'Wrap the group in <fieldset> with a <legend>, or role="radiogroup"/"group" with an aria-label.'})}return o}};function Wn(e){let t=e.tags.find(a=>/^wcag\d{3,4}$/.test(a));if(!t)return[9,9,99];let o=t.slice(4);return[+o[0],+o[1],+o.slice(2)]}var Ln=[Yt,Kt,Xt,Zt,eo,to,oo,no,ro,io,so,co,uo,Mo,Oo,Do,Po,Go,zo,Vo,Ho,Jo,Qo,ea,ta,oa,aa,ia,sa,ca,ua,da,pa,ha,fa,ma,ga,ba,wa,Aa,ka,Ta,$a,Ea,Wa,Oa,Ua,Da,Pa,Ba,Ga,qa,za,_a,Va,Ha,ja,Ya,Ka,Xa,Za,Ja,Qa,tn,on,an,nn,La,Ma,rn,sn,ln,cn,un,dn,hn,gn,bn,yn,vn,An,Sn,Tn,Rn,Nn,$n,In,En].sort((e,t)=>{let o=Wn(e),a=Wn(t);return o[0]-a[0]||o[1]-a[1]||o[2]-a[2]||e.id.localeCompare(t.id)});var y=(e,t,o,a,n="2.0")=>({num:e,name:t,level:o,automation:a,since:n,principle:{1:"Perceivable",2:"Operable",3:"Understandable",4:"Robust"}[e[0]],url:`https://www.w3.org/WAI/WCAG22/Understanding/${t.toLowerCase().replace(/[(),]/g,"").replace(/\s+/g,"-")}.html`}),Mn=[y("1.1.1","Non-text Content","A","partial"),y("1.2.1","Audio-only and Video-only (Prerecorded)","A","partial"),y("1.2.2","Captions (Prerecorded)","A","partial"),y("1.2.3","Audio Description or Media Alternative (Prerecorded)","A","manual"),y("1.2.4","Captions (Live)","AA","manual"),y("1.2.5","Audio Description (Prerecorded)","AA","manual"),y("1.2.6","Sign Language (Prerecorded)","AAA","manual"),y("1.2.7","Extended Audio Description (Prerecorded)","AAA","manual"),y("1.2.8","Media Alternative (Prerecorded)","AAA","manual"),y("1.2.9","Audio-only (Live)","AAA","manual"),y("1.3.1","Info and Relationships","A","partial"),y("1.3.2","Meaningful Sequence","A","partial"),y("1.3.3","Sensory Characteristics","A","manual"),y("1.3.4","Orientation","AA","partial","2.1"),y("1.3.5","Identify Input Purpose","AA","partial","2.1"),y("1.3.6","Identify Purpose","AAA","manual","2.1"),y("1.4.1","Use of Color","A","partial"),y("1.4.2","Audio Control","A","partial"),y("1.4.3","Contrast (Minimum)","AA","partial"),y("1.4.4","Resize Text","AA","partial"),y("1.4.5","Images of Text","AA","manual"),y("1.4.6","Contrast (Enhanced)","AAA","partial"),y("1.4.7","Low or No Background Audio","AAA","manual"),y("1.4.8","Visual Presentation","AAA","manual"),y("1.4.9","Images of Text (No Exception)","AAA","manual"),y("1.4.10","Reflow","AA","partial","2.1"),y("1.4.11","Non-text Contrast","AA","partial","2.1"),y("1.4.12","Text Spacing","AA","partial","2.1"),y("1.4.13","Content on Hover or Focus","AA","manual","2.1"),y("2.1.1","Keyboard","A","partial"),y("2.1.2","No Keyboard Trap","A","manual"),y("2.1.3","Keyboard (No Exception)","AAA","partial"),y("2.1.4","Character Key Shortcuts","A","manual","2.1"),y("2.2.1","Timing Adjustable","A","partial"),y("2.2.2","Pause, Stop, Hide","A","partial"),y("2.2.3","No Timing","AAA","manual"),y("2.2.4","Interruptions","AAA","manual"),y("2.2.5","Re-authenticating","AAA","manual"),y("2.2.6","Timeouts","AAA","manual","2.1"),y("2.3.1","Three Flashes or Below Threshold","A","manual"),y("2.3.2","Three Flashes","AAA","manual"),y("2.3.3","Animation from Interactions","AAA","partial","2.1"),y("2.4.1","Bypass Blocks","A","partial"),y("2.4.2","Page Titled","A","partial"),y("2.4.3","Focus Order","A","partial"),y("2.4.4","Link Purpose (In Context)","A","partial"),y("2.4.5","Multiple Ways","AA","manual"),y("2.4.6","Headings and Labels","AA","manual"),y("2.4.7","Focus Visible","AA","partial"),y("2.4.8","Location","AAA","manual"),y("2.4.9","Link Purpose (Link Only)","AAA","partial"),y("2.4.10","Section Headings","AAA","manual"),y("2.4.11","Focus Not Obscured (Minimum)","AA","partial","2.2"),y("2.4.12","Focus Not Obscured (Enhanced)","AAA","manual","2.2"),y("2.4.13","Focus Appearance","AAA","manual","2.2"),y("2.5.1","Pointer Gestures","A","manual","2.1"),y("2.5.2","Pointer Cancellation","A","manual","2.1"),y("2.5.3","Label in Name","A","manual","2.1"),y("2.5.4","Motion Actuation","A","manual","2.1"),y("2.5.5","Target Size (Enhanced)","AAA","partial","2.1"),y("2.5.6","Concurrent Input Mechanisms","AAA","manual","2.1"),y("2.5.7","Dragging Movements","AA","partial","2.2"),y("2.5.8","Target Size (Minimum)","AA","partial","2.2"),y("3.1.1","Language of Page","A","partial"),y("3.1.2","Language of Parts","AA","partial"),y("3.1.3","Unusual Words","AAA","manual"),y("3.1.4","Abbreviations","AAA","manual"),y("3.1.5","Reading Level","AAA","manual"),y("3.1.6","Pronunciation","AAA","manual"),y("3.2.1","On Focus","A","partial"),y("3.2.2","On Input","A","partial"),y("3.2.3","Consistent Navigation","AA","manual"),y("3.2.4","Consistent Identification","AA","manual"),y("3.2.5","Change on Request","AAA","manual"),y("3.2.6","Consistent Help","A","manual","2.2"),y("3.3.1","Error Identification","A","partial"),y("3.3.2","Labels or Instructions","A","partial"),y("3.3.3","Error Suggestion","AA","manual"),y("3.3.4","Error Prevention (Legal, Financial, Data)","AA","manual"),y("3.3.5","Help","AAA","manual"),y("3.3.6","Error Prevention (All)","AAA","manual"),y("3.3.7","Redundant Entry","A","partial","2.2"),y("3.3.8","Accessible Authentication (Minimum)","AA","partial","2.2"),y("3.3.9","Accessible Authentication (Enhanced)","AAA","manual","2.2"),y("4.1.2","Name, Role, Value","A","partial"),y("4.1.3","Status Messages","AA","manual","2.1")];var Fn=at.engine.name,Pn=at.engine.version,On={isVisible:nt,isRendered:Ge,cssPath:rt,htmlSnippet:it,ownText:st,accessibleName:qe};function vs(e,t){return t?.length?e.tags.some(o=>t.includes(o)):!0}function As(e){if(!e?.length)return{version:"2.2",level:"AAA"};let t=e.filter(n=>/^wcag(2|21|22)(a|aa|aaa)$/.test(n));if(!t.length)return null;let o=t.some(n=>n.startsWith("wcag22"))?"2.2":t.some(n=>n.startsWith("wcag21"))?"2.1":"2.0",a=t.some(n=>/aaa$/.test(n))?"AAA":t.some(n=>/aa$/.test(n))?"AA":"A";return{version:o,level:a}}var Un={"2.0":0,"2.1":1,"2.2":2},Dn={A:0,AA:1,AAA:2};function ks(e){let t=As(e);return t?Mn.filter(o=>o.automation!=="auto").filter(o=>Un[o.since]<=Un[t.version]).filter(o=>Dn[o.level]<=Dn[t.level]).map(({num:o,name:a,level:n,principle:r,url:i,automation:s})=>({num:o,name:a,level:n,principle:r,url:i,automation:s})):[]}function Cs(e,t){let o=e;for(;o;){if(o.closest?.(t))return!0;o=o.getRootNode()?.host??null}return!1}function Ss(e,t){let o={target:[rt(e)],html:it(e),failureSummary:[t.message,t.fix&&`Fix: ${t.fix}`].filter(Boolean).join(`
8
+ `)};return t.data&&(o.data=t.data),o}async function Ts(e=document,t={},o){let a=performance.now(),n={testEngine:{name:Fn,version:Pn},timestamp:new Date().toISOString(),url:e.location?.href??e.ownerDocument?.location?.href??"",violations:[],passes:[],incomplete:[],inapplicable:[],manualReview:ks(t.tags),ruleTimings:[]},r=Ln.filter(p=>!p.disabled&&vs(p,t.tags));go();let i=de(e.querySelectorAll?e:document),s=null,l=p=>p==="*"?(s??=i.flatMap(w=>[...w.querySelectorAll("*")]),s):i.flatMap(w=>[...w.querySelectorAll(p)]),c=0,d={critical:0,serious:0,moderate:0,minor:0,review:0},u={critical:0,serious:0,moderate:0,minor:0,review:0},h=performance.now(),f=typeof MessageChannel=="function"?new MessageChannel:null,m=()=>new Promise(p=>{if(!f){setTimeout(p,0);return}f.port1.onmessage=p,f.port2.postMessage(0)}),b=async p=>{performance.now()-h<16||(p?.(),await m(),h=performance.now())};for(let p of r){if(t.signal?.aborted)throw new DOMException("Audit stopped","AbortError");await b(),o?.({done:c,total:r.length,rule:p.id,running:!0});let w=performance.now(),T=l(p.selector);t.exclude&&(T=T.filter(R=>!Cs(R,t.exclude))),p.visibleOnly!==!1&&(T=T.filter(p.visibility==="visual"?Ge:nt));let U;if(p.evaluateAll)U=await p.evaluateAll(T,On);else{U=new Array(T.length);for(let R=0;R<T.length;R++){if((R&31)===0&&R>0){if(t.signal?.aborted)throw new DOMException("Audit stopped","AbortError");await b(()=>o?.({done:c+R/T.length,total:r.length,rule:p.id,running:!0}))}U[R]=await p.evaluate(T[R],On)}}let L={fail:[],incomplete:[]},F=0;T.forEach((R,P)=>{let I=U[P].status;I==="pass"?F+=1:L[I]?.push(Ss(R,U[P]))});let x={id:p.id,name:p.name??p.id,impact:p.impact,tags:p.tags,help:p.help,description:p.help,helpUrl:p.helpUrl};T.length||n.inapplicable.push({...x,nodes:[]}),L.fail.length&&n.violations.push({...x,nodes:L.fail}),L.incomplete.length&&n.incomplete.push({...x,nodes:L.incomplete}),F&&n.passes.push({...x,nodes:[],nodeCount:F}),d[p.impact]=(d[p.impact]??0)+L.fail.length,d.review+=L.incomplete.length,L.fail.length&&(u[p.impact]=(u[p.impact]??0)+1),L.incomplete.length&&(u.review+=1);let $=Math.round((performance.now()-w)*10)/10;n.ruleTimings.push({rule:p.id,ms:$,elements:T.length}),o?.({done:++c,total:r.length,rule:p.id,ms:$,counts:{...d},typeCounts:{...u},...L.fail.length||L.incomplete.length?{findings:{...L.fail.length?{violation:{...x,nodes:L.fail}}:{},...L.incomplete.length?{incomplete:{...x,nodes:L.incomplete}}:{}}}:{}})}return n.durationMs=Math.round(performance.now()-a),n}return Yn(Rs);})();