PageSourceSearch

https://www.digife.it/wp-content/plugins/accessibiledigife/accessible-web-widget.min.js?v=1779442758

js digife.it collected 2026-09-24 13:04:35 UTC 259,261 bytes, 5,094 lines download raw bytes

1/*!
2 * AccessibleWeb Widget v1.1.1
3 * https://github.com/ifrederico/accessible-web-widget
4 *
5 * Copyright (c) 2025 ifrederico
6 * Released under the MIT License
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
9 * See DISCLAIMER.md for important legal information.
10 * This widget does not guarantee accessibility compliance.
11 */
12var AccessibleWebWidget = (function () {
13    'use strict';
14
15    const WIDGET_THEME = {
16          // Core colors
17          primaryColor: '#1976d2',        // Accessible blue (WCAG AA compliant)
18          primaryColorLight: '#42a5f5',   // Lighter blue
19          primaryColorDark: '#0d47a1',    // Darker blue
20          backgroundColor: '#f5f7fa',
21          textColor: '#222222',
22          textColorInverted: '#ffffff',
23          
24          // UI elements
25          buttonSize: '48px',
26          cardBackground: '#ffffff',
27          borderColor: '#d1d5db',
28          focusRingColor: '#1976d2',      // Match primary
29          
30          // Interactive states
31          hoverColor: '#42a5f5',          // Use light blue for hover
32          activeColor: '#0d47a1',         // Use dark blue for active
33          
34          // Feedback colors
35          successColor: '#2e7d32', // Green
36          errorColor: '#c62828', // Red
37          warningColor: '#f57c00', // Orange
38          
39          // Other UI properties
40          headerHeight: '54px',
41          borderRadius: '8px',
42          buttonBorderRadius: '0.4rem',
43          menuPosition: 'right', // or 'left'
44          zIndex: 100000,
45          
46          // WCAG-specific
47          focusBorderWidth: '3px',
48          focusOutlineOffset: '2px'
49        };
50
51    const WIDGET_ICONS = {
52          accessibility: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M423.5-743.5Q400-767 400-800t23.5-56.5Q447-880 480-880t56.5 23.5Q560-833 560-800t-23.5 56.5Q513-720 480-720t-56.5-23.5ZM360-80v-520q-60-5-122-15t-118-25l20-80q78 21 166 30.5t174 9.5q86 0 174-9.5T820-720l20 80q-56 15-118 25t-122 15v520h-80v-240h-80v240h-80Z"/></svg>',
53          accessibilityVariants: {
54                default: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M423.5-743.5Q400-767 400-800t23.5-56.5Q447-880 480-880t56.5 23.5Q560-833 560-800t-23.5 56.5Q513-720 480-720t-56.5-23.5ZM360-80v-520q-60-5-122-15t-118-25l20-80q78 21 166 30.5t174 9.5q86 0 174-9.5T820-720l20 80q-56 15-118 25t-122 15v520h-80v-240h-80v240h-80Z"/></svg>',
55                'icon-2': '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M423.5-743.5Q400-767 400-800t23.5-56.5Q447-880 480-880t56.5 23.5Q560-833 560-800t-23.5 56.5Q513-720 480-720t-56.5-23.5ZM360-80v-520H120v-80h720v80H600v520h-80v-240h-80v240h-80Z"/></svg>',
56                'icon-3': '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M423.5-743.5Q400-767 400-800t23.5-56.5Q447-880 480-880t56.5 23.5Q560-833 560-800t-23.5 56.5Q513-720 480-720t-56.5-23.5ZM680-80v-200H480q-33 0-56.5-23.5T400-360v-240q0-33 23.5-56.5T480-680q24 0 41.5 10.5T559-636q55 66 99.5 90.5T760-520v80q-53 0-107-23t-93-55v138h120q33 0 56.5 23.5T760-300v220h-80Zm-280 0q-83 0-141.5-58.5T200-280q0-72 45.5-127T360-476v82q-35 14-57.5 44.5T280-280q0 50 35 85t85 35q39 0 69.5-22.5T514-240h82q-14 69-69 114.5T400-80Z"/></svg>',
57                'icon-4': '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M320-80q-83 0-141.5-58.5T120-280q0-83 58.5-141.5T320-480v80q-50 0-85 35t-35 85q0 50 35 85t85 35q50 0 85-35t35-85h80q0 83-58.5 141.5T320-80Zm360-40v-200H440q-44 0-68-37.5t-6-78.5l74-164h-91l-24 62-77-22 28-72q9-23 29.5-35.5T350-680h208q45 0 68.5 36.5T632-566l-66 146h114q33 0 56.5 23.5T760-340v220h-80Zm-96.5-603.5Q560-747 560-780t23.5-56.5Q607-860 640-860t56.5 23.5Q720-813 720-780t-23.5 56.5Q673-700 640-700t-56.5-23.5Z"/></svg>'
58          },
59          largePointer: '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px"><path d="m320-410 79-110h170L320-716v306ZM551-80 406-392 240-160v-720l560 440H516l144 309-109 51ZM399-520Z"/></svg>',
60          pauseMotion: '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="m791-55-91-91q-49 32-104.5 49T480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 17-115.5T146-700l-91-91 57-57 736 736-57 57ZM480-160q43 0 83.5-11t78.5-33L204-642q-22 38-33 78.5T160-480q0 133 93.5 226.5T480-160Zm334-100-58-58q22-38 33-78.5t11-83.5q0-133-93.5-226.5T480-800q-43 0-83.5 11T318-756l-58-58q49-32 104.5-49T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 60-17 115.5T814-260ZM537-537ZM423-423Z"/></svg>',
61          readingAid: '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M320-80v-440l-80-120v-240h480v240l-80 120v440H320Zm160-260q-25 0-42.5-17.5T420-400q0-25 17.5-42.5T480-460q25 0 42.5 17.5T540-400q0 25-17.5 42.5T480-340ZM320-760h320v-40H320v40Zm320 80H320v16l80 120v384h160v-384l80-120v-16ZM480-480Z"/></svg>',
62          boldText: '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M272-200v-560h221q65 0 120 40t55 111q0 51-23 78.5T602-491q25 11 55.5 41t30.5 90q0 89-65 124.5T501-200H272Zm121-112h104q48 0 58.5-24.5T566-372q0-11-10.5-35.5T494-432H393v120Zm0-228h93q33 0 48-17t15-38q0-24-1
627-39t-44-15h-95v109Z"/></svg>',
63          lineSpacing: '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M240-160 80-320l56-56 64 62v-332l-64 62-56-56 160-160 160 160-56 56-64-62v332l64-62 56 56-160 160Zm240-40v-80h400v80H480Zm0-240v-80h400v80H480Zm0-240v-80h400v80H480Z"/></svg>',
64          letterSpacing: '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M320-80 160-240l160-160 57 56-64 64h334l-63-64 56-56 160 160L640-80l-57-56 64-64H313l63 64-56 56ZM200-480v-400h80v400h-80Zm240 0v-400h80v400h-80Zm240 0v-400h80v400h-80Z"/></svg>',
65          dyslexiaFont: '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="m131-252 165-440h79l165 440h-76l-39-112H247l-40 112h-76Zm139-176h131l-64-182h-4l-63 182Zm395 186q-51 0-81-27.5T554-342q0-44 34.5-72.5T677-443q23 0 45 4t38 11v-12q0-29-20.5-47T685-505q-23 0-42 9.5T610-468l-47-35q24-29 54.5-43t68.5-14q69 0 103 32.5t34 97.5v178h-63v-37h-4q-14 23-38 35t-53 12Zm12-54q35 0 59.5-24t24.5-56q-14-8-33.5-12.5T689-393q-32 0-50 14t-18 37q0 20 16 33t40 13Z"/></svg>',
66          highlightLinks: '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M440-280H280q-83 0-141.5-58.5T80-480q0-83 58.5-141.5T280-680h160v80H280q-50 0-85 35t-35 85q0 50 35 85t85 35h160v80ZM320-440v-80h320v80H320Zm200 160v-80h160q50 0 85-35t35-85q0-50-35-85t-85-35H520v-80h160q83 0 141.5 58.5T880-480q0 83-58.5 141.5T680-280H520Z"/></svg>',
67          highlightTitle: '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M320-280q17 0 28.5-11.5T360-320q0-17-11.5-28.5T320-360q-17 0-28.5 11.5T280-320q0 17 11.5 28.5T320-280Zm0-160q17 0 28.5-11.5T360-480q0-17-11.5-28.5T320-520q-17 0-28.5 11.5T280-480q0 17 11.5 28.5T320-440Zm0-160q17 0 28.5-11.5T360-640q0-17-11.5-28.5T320-680q-17 0-28.5 11.5T280-640q0 17 11.5 28.5T320-600Zm120 320h240v-80H440v80Zm0-160h240v-80H440v80Zm0-160h240v-80H440v80ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm0-80h560v-560H200v560Zm0-560v560-560Z"/></svg>',
68          contrast: '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="m480-260 60-60h100v-100l60-60-60-60v-100H540l-60-60-60 60H320v100l-60 60 60 60v100h100l60 60Zm0-100v-240q50 0 85 35t35 85q0 50-35 85t-85 35ZM160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h640q33 0 56.5 23.5T880-720v480q0 33-23.5 56.5T800-160H160Zm0-80h64
680v-480H160v480Zm0 0v-480 480Z"/></svg>',
69          darkContrast: '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M484-80q-84 0-157.5-32t-128-86.5Q144-253 112-326.5T80-484q0-146 93-257.5T410-880q-18 99 11 193.5T521-521q71 71 165.5 100T880-410q-26 144-138 237T484-80Zm0-80q88 0 163-44t118-121q-86-8-163-43.5T464-465q-61-61-97-138t-43-163q-77 43-120.5 118.5T160-484q0 135 94.5 229.5T484-160Zm-20-305Z"/></svg>',
70          lightContrast: '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M440-800v-120h80v120h-80Zm0 760v-120h80v120h-80Zm360-400v-80h120v80H800Zm-760 0v-80h120v80H40Zm708-252-56-56 70-72 58 58-72 70ZM198-140l-58-58 72-70 56 56-70 72Zm564 0-70-72 56-56 72 70-58 58ZM212-692l-72-70 58-58 70 72-56 56Zm268 452q-100 0-170-70t-70-170q0-100 70-170t170-70q100 0 170 70t70 170q0 100-70 170t-170 70Zm0-80q67 0 113.5-46.5T640-480q0-67-46.5-113.5T480-640q-67 0-113.5 46.5T320-480q0 67 46.5 113.5T480-320Zm0-160Z"/></svg>',
71          invertColors: '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-64-24.5-122.5T706-706L254-254q45 45 103.5 69.5T480-160Zm0-160v-60h200v60H480ZM320-500h60v-80h80v-60h-80v-80h-60v80h-80v60h80v80Z"/></svg>',
72          saturation: '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M480-120q-133 0-226.5-92.5T160-436q0-66 25-122t69-100l226-222 226 222q44 44 69 100t25 122q0 131-93.5 223.5T480-120Zm0-80v-568L310-600q-35 33-52.5 74.5T240-436q0 97 70 166.5T480-200Z"/></svg>',
73          lowSaturation: '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M480-120q-133 0-226.5-92T160-436q0-65 25-121.5T254-658l226-222 226 222q44 44 69 100.5T800-436q0 132-93.5 224T480-120ZM242-400h474q12-72-13.5-123T650-600L480-768 310-600q-27 26-53 77t-15 123Z"/></svg>',
74          highSaturation: '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M480-80q-82 0-155-31.5t-127.5-86Q143-252 111.5-325T80-480q0-83 32.5-156t88-127Q256-817 330-848.5T488-880q80 0 151 27.5t124.5 76q53.5 48.5 85 115T880-518q0 115-70 176.5T640-280h-74q-9 0-12.5 5t-3.5 11q0 12 15 34.5t15 51.5q0 50-27.5 74T480-80Zm0-400Zm-220 40q26 0 43-17t17-43q0-26-17-43t-43-17q-26 0-43 17t-17 43q0 26 17 43t43 17Zm120-160q26 0 43-17t17-43q0-26-17-43t-43-17q-26 0-43 17t-17 43q0 26 17 43t43 17Zm200 0q26 0 43-17t17-43q0-26-17-43t-43-17q-26 0-43 17t-17 43q0 26 17 43t43 17Zm120 160q26 0 43-17t17-43q0-26-17-43t-43-17q-26 0-43 17t-17 43q0 26 17 43t43 17ZM480-160q9 0 14.5-5t5.5-13q0-14-15-33t-15-57q0-42 29-67t71-25h70q66 0 113-38.5T800-518q0-121-92.5-201.5T488-800q-136 0-232 93t-96 227q0 133 93.5 226.5T480-160Z"/></svg>',
75          reset: '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M440-122q-121-15-200.5-105.5T160-440q0-66 26-126.5T260-672l57 57q-38 34-57.5 79T240-440q0 88 56 155.5T440-202v80Zm80 0v-80q87-16 143.5-83T720-440q0-100-70-170t-170-70h-3l44 44-56 56-140-140 140-140 56 56-44 44h3q134 0 227 93t93 227q0 121-79.5 211.5T520-122Z"/></svg>',
76          close: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41Z"/></svg>',
77          increase: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2Z"/></svg>',
78          decrease: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 13H5v-2h14v2Z"/></svg>',
79          arrowBack: '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="currentColor"><path d="M400-80 0-480l400-400 71 71-329 329 329 329-71 71Z"/></svg>',
80          adjustFontSize: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
80<path d="M2 4v3h5v12h3V7h5V4H2m19 5h-9v3h3v7h3v-7h3V9Z"/></svg>',
81          language: '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="currentColor"><path d="M480-80q-82 0-155-31.5t-127.5-86Q143-252 111.5-325T80-480q0-83 31.5-155.5t86-127Q252-817 325-848.5T480-880q83 0 155.5 31.5t127 86q54.5 54.5 86 127T880-480q0 82-31.5 155t-86 127.5q-54.5 54.5-127 86T480-80Zm0-82q26-36 45-75t31-83H404q12 44 31 83t45 75Zm-104-16q-18-33-31.5-68.5T322-320H204q29 50 72.5 87t99.5 55Zm208 0q56-18 99.5-55t72.5-87H638q-9 38-22.5 73.5T584-178ZM170-400h136q-3-20-4.5-39.5T300-480q0-21 1.5-40.5T306-560H170q-5 20-7.5 39.5T160-480q0 21 2.5 40.5T170-400Zm216 0h188q3-20 4.5-39.5T580-480q0-21-1.5-40.5T574-560H386q-3 20-4.5 39.5T380-480q0 21 1.5 40.5T386-400Zm268 0h136q5-20 7.5-39.5T800-480q0-21-2.5-40.5T790-560H654q3 20 4.5 39.5T660-480q0 21-1.5 40.5T654-400Zm-16-240h118q-29-50-72.5-87T584-782q18 33 31.5 68.5T638-640Zm-234 0h152q-12-44-31-83t-45-75q-26 36-45 75t-31 83Zm-200 0h118q9-38 22.5-73.5T376-782q-56 18-99.5 55T204-640Z"/></svg>',
82          hideImages: '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="m840-234-80-80v-446H314l-80-80h526q33 0 56.5 23.5T840-760v526ZM792-56l-64-64H200q-33 0-56.5-23.5T120-200v-528l-64-64 56-56 736 736-56 56ZM240-280l120-160 90 120 33-44-283-283v447h447l-80-80H240Zm297-257ZM424-424Z"/></svg>',
83          accessibilityReport: '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h168q13-36 43.5-58t68.5-22q38 0 68.5 22t43.5 58h168q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm0-80h560v-560H200v560Zm80-80h280v-80H280v80Zm0-160h400v-80H280v80Zm0-160h400v-80H280v80Zm221.5-198.5Q510-807 510-820t-8.5-21.5Q493-850 480-850t-21.5 8.5Q450-833 450-820t8.5 21.5Q467-790 480-790t21.5-8.5ZM200-200v-560 560Z"/></svg>',
84          annotations: '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v268q-19-9-39-15.5t-41-9.5v-243H200v560h242q3 22 9.5 42t15.5 38H200Zm0-120v40-560 243-3 280Zm80-40h163q3-21 9.5-41t14.5-39H280v80Zm0-160h244q32-30 71.5-50t84.5-27v-3H280v80Zm0-160h400v-80H280v80ZM720-40q-83 0-141.5-58.5T520-240q0-83 58.5-141.5T720-440q83 0 141.5 58.5T920-240q0 83-58.5 141.5T720-40Zm-20-80h40v-100h100v-40H740v-100h-40v100H600v40h100v100Z"/></svg>',
85          textToSpeech: '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M560-131v-82q90-26 145-100t55-168q0-94-55-168T560-749v-82q124 28 202 125.5T840-481q0 127-78 224.5T560-131ZM120-360v-240h160l200-200v640L280-360H120Zm440 40v-322q47 22 73.5 66t26.5 96q0 51-26.5 94.5T560-320ZM400-606l-86 86H200v80h114l86 86v-252ZM300-480Z"/></svg>',
86          highContrast: '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93v640Z"/></svg>',
87          simplifyLayout: '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M760-360v-80H200v80h560Zm0-160v-80H200v80h560Zm0-160v-80H200v80h560ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm560-80v-80H200v80h560Z"/></svg>'
88        };
89
90    const TARGET_SELECTORS = {
91          ALL: ['', '*:not(.material-icons,.acc-menu,.acc-menu *)'],
92          LINKS: ["a[href]"],
93          HEADERS: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', '.wsite-headline', '.wsite-content-title'],
94          TEXT: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', '.wsite-headline', '.wsite-content-title', 'img', 'p', 'i', 'svg', 'a', 'button:not(.acc-btn)', 'label', 'li', 'ol']
95        };
96
97    const PAGE_CONTENT_SELECTOR = 'body > *:not(.acc-container):not(.acc-rg-container):not(#acc-skip-link)';
98
99    const TRANSLATIONS = {
100          en: {
101            "Accessibility Menu": "Accessibility Menu",
102            "Reset settings": "Reset settings",
103            "Reset All Settings": "Reset All Settings",
104            "Close": "Close",
105            "Content Adjustments": "Content Adjustments",
106            "Adjust Font Size": "Adjust Font Size",
107            "Highlight Title": "Highlight Title",
108            "Highlight Links": "Highlight Links",
109            "Readable Font": "Readable Font",
110            "Color Adjustments": "Color Adjustments",
111            "Invert Colors": "Invert Colors",
112            "Light Contrast": "Light Contrast",
113            "Dark Contrast": "Dark Contrast",
114            "High Contrast": "High Contrast",
115            "High Saturation": "High Saturation",
116            "Low Saturation": "Low Saturation",
117            "Monochrome": "Monochrome",
118            "Tools": "Tools",
119            "Reading Guide": "Reading Guide",
120            "Stop Animations": "Stop Animations",
121            "Big Cursor": "Big Cursor",
122            "Increase Font Size": "Increase Font Size",
123            "Decrease Font Size": "Decrease Font Size",
124            "Letter Spacing": "Letter Spacing",
125            "Line Height": "Line Height",
126            "Font Weight": "Font Weight",
127            "Dyslexia Font": "Dyslexia Font",
128            "Language": "Language",
129            "Open Accessibility Menu": "Open Accessibility Menu",
130            "Hide Images": "Hide Images",
131            "Skip to accessibility menu": "Skip to accessibility menu",
132            "Accessibility Report": "Accessibility Report",
133            "Run Accessibility Check": "Run Accessibility Check",
134            "Loading...": "Loading...",
135            "Analyzing page...": "Analyzing page...",
136            "Critical": "Critical",
137            "Serious": "Serious",
138            "Moderate": "Moderate",
139            "Minor": "Minor",
140            "Violations Found": "Violations Found",
141            "No Issues Found": "No Issues Found",
142            "Element": "Element",
143            "Issue": "Issue",
144            "How to Fix": "How to Fix",
145            "Close Report": "Close Report",
146            "Passed Tests": "Passed Tests",
147            "Items Need Review": "Items Need Review",
148            "Annotations": "Annotations",
149            "Text to Speech": "Text to Speech",
150            "Text to Speech On": "Text to Speech On",
151            "Text to Speech Off": "Text to Speech Off",
152            "Simplify Layout": "Simplify Layout",
153            "Play": "Play",
154            "Pause": "Pause",
155            "Stop": "Stop",
156            "Loading voice...": "Loading voice...",
157            "Reading...": "Reading...",
158          },
159          it: {
160            "Accessibility Menu": "Menu Accessibilità",
161            "Reset settings": "Ripristina impostazioni",
162            "Reset All Settings": "Ripristina tutte le impostazioni",
163            "Close": "Chiudi",
164            "Content Adjustments": "Regolazioni Contenuto",
165            "Adjust Font Size": "Regola Dimensione Font",
166            "Highlight Title": "Evidenzia Titoli",
167            "Highlight Links": "Evidenzia Link",
168            "Readable Font": "Font Leggibile",
169            "Color Adjustments": "Regolazioni Colore",
170            "Invert Colors": "Inverti Colori",
171            "Light Contrast": "Contrasto Chiaro",
172            "Dark Contrast": "Contrasto Scuro",
173            "High Contrast": "Alto Contrasto",
174            "High Saturation": "Alta Saturazione",
175            "Low Saturation": "Bassa Saturazione",
176            "Monochrome": "Monocromatico",
177            "Tools": "Strumenti",
178            "Reading Guide": "Guida di Lettura",
179            "Stop Animations": "Ferma Animazioni",
180            "Big Cursor": "Cursore Grande",
181            "Increase Font Size": "Aumenta Dimensione Font",
182            "Decrease Font Size": "Diminuisci Dimensione Font",
183            "Letter Spacing": "Spaziatura Lettere",
184            "Line Height": "Altezza Riga",
185            "Font Weight": "Grassetto",
186            "Dyslexia Font": "Font Dislessia",
187            "Language": "Lingua",
188            "Open Accessibility Menu": "Apri Menu Accessibilità",
189            "Hide Images": "Nascondi Immagini",
190            "Skip to accessibility menu": "Vai al menu accessibilità",
191            "Accessibility Report": "Report Accessibilità",
192            "Run Accessibility Check": "Esegui Controllo Accessibilità",
193            "Loading...": "Caricamento...",
194            "Analyzing page...": "Analisi pagina...",
195            "Critical": "Critico",
196            "Serious": "Grave",
197            "Moderate": "Moderato",
198            "Minor": "Minore",
199            "Violations Found": "Violazioni Trovate",
200            "No Issues Found": "Nessun Problema Trovato",
201            "Element": "Elemento",
202            "Issue": "Problema",
203            "How to Fix": "Come Risolvere",
204            "Close Report": "Chiudi Report",
205            "Passed Tests": "Test Superati",
206            "Items Need Review": "Elementi da Rivedere",
207            "Annotations": "Annotations",
208            "Text to Speech": "Text to Speech",
209            "Text to Speech On": "Sintesi Vocale Attiva",
210            "Text to Speech Off": "Sintesi Vocale Disattiva",
211            "Simplify Layout": "Simplify Layout",
212            "Play": "Play",
213            "Pause": "Pause",
214            "Stop": "Stop",
215            "Reading...": "Reading...",
216          },
217          fr: {
218            "Accessibility Menu": "Menu Accessibilité",
219            "Reset settings": "Réinitialiser les paramètres",
220            "Reset All Settings": "Réinitialiser tous les paramètres",
221            "Close": "Fermer",
222            "Content Adjustments": "Ajustements du Contenu",
223            "Adjust Font Size": "Ajuster la Taille de Police",
224            "Highlight Title": "Surligner les Titres",
225            "Highlight Links": "Surligner les Liens",
226            "Readable Font": "Police Lisible",
227            "Color Adjustments": "Ajustements des Couleurs",
228            "Invert Colors": "Inverser les Couleurs",
229            "Light Contrast": "Contraste Clair",
230            "Dark Contrast": "Contraste Sombre",
231            "High Contrast": "Contraste Élevé",
232            "High Saturation": "Saturation Élevée",
233            "Low Saturation": "Saturation Faible",
234            "Monochrome": "Monochrome",
235            "Tools": "Outils",
236            "Reading Guide": "Guide de Lecture",
237            "Stop Animations": "Arrêter les Animations",
238            "Big Cursor": "Grand Curseur",
239            "Increase Font Size": "Augmenter la Taille de Police",
240            "Decrease Font Size": "Diminuer la Taille de Police",
241            "Letter Spacing": "Espacement des Lettres",
242            "Line Height": "Hauteur de Ligne",
243            "Font Weight": "Graisse de Police",
244            "Dyslexia Font": "Police Dyslexie",
245            "Language": "Langue",
246            "Open Accessibility Menu": "Ouvrir le Menu Accessibilité",
247            "Hide Images": "Masquer les Images",
248            "Skip to accessibility menu": "Aller au menu accessibilité",
249            "Accessibility Report": "Rapport d'Accessibilité",
250            "Run Accessibility Check": "Lancer le Contrôle d'Accessibilité",
251            "Loading...": "Chargement...",
252            "Analyzing page...": "Analyse de la page...",
253            "Critical": "Critique",
254            "Serious": "Grave",
255            "Moderate": "Modéré",
256            "Minor": "Mineur",
257            "Violations Found": "Violations Trouvées",
258            "No Issues Found": "Aucun Problème Trouvé",
259            "Element": "Élément",
260            "Issue": "Problème",
261            "How to Fix": "Comment Corriger",
262            "Close Report": "Fermer le Rapport",
263            "Passed Tests": "Tests Réussis",
264            "Items Need Review": "Éléments à Vérifier",
265            "Annotations": "Annotations",
266            "Text to Speech": "Text to Speech",
267            "Text to Speech On": "Synthèse Vocale Activée",
268            "Text to Speech Off": "Synthèse Vocale Désactivée",
269            "Simplify Layout": "Simplify Layout",
270            "Play": "Play",
271            "Pause": "Pause",
272            "Stop": "Stop",
273            "Reading...": "Reading...",
274          },
275          de: {
276            "Accessibility Menu": "Barrierefreiheit-Menü",
277            "Reset settings": "Einstellungen zurücksetzen",
278            "Reset All Settings": "Alle Einstellungen zurücksetzen",
279            "Close": "Schließen",
280            "Content Adjustments": "Inhaltsanpassungen",
281            "Adjust Font Size": "Schriftgröße anpassen",
282            "Highlight Title": "Titel hervorheben",
283            "Highlight Links": "Links hervorheben",
284            "Readable Font": "Lesbare Schrift",
285            "Color Adjustments": "Farbanpassungen",
286            "Invert Colors": "Farben invertieren",
287            "Light Contrast": "Heller Kontrast",
288            "Dark Contrast": "Dunkler Kontrast",
289            "High Contrast": "Hoher Kontrast",
290            "High Saturation": "Hohe Sättigung",
291            "Low Saturation": "Niedrige Sättigung",
292            "Monochrome": "Monochrom",
293            "Tools": "Werkzeuge",
294            "Reading Guide": "Lesehilfe",
295            "Stop Animations": "Animationen stoppen",
296            "Big Cursor": "Großer Cursor",
297            "Increase Font Size": "Schriftgröße erhöhen",
298            "Decrease Font Size": "Schriftgröße verringern",
299            "Letter Spacing": "Zeichenabstand",
300            "Line Height": "Zeilenhöhe",
301            "Font Weight": "Schriftstärke",
302            "Dyslexia Font": "Legasthenie-Schrift",
303            "Language": "Sprache",
304            "Open Accessibility Menu": "Barrierefreiheit-Menü öffnen",
305            "Hide Images": "Bilder ausblenden",
306            "Skip to accessibility menu": "Zum Barrierefreiheit-Menü springen",
307            "Accessibility Report": "Barrierefreiheit-Bericht",
308            "Run Accessibility Check": "Barrierefreiheitsprüfung starten",
309            "Loading...": "Laden...",
310            "Analyzing page...": "Seite wird analysiert...",
311            "Critical": "Kritisch",
312            "Serious": "Schwerwiegend",
313            "Moderate": "Mäßig",
314            "Minor": "Gering",
315            "Violations Found": "Verstöße gefunden",
316            "No Issues Found": "Keine Probleme gefunden",
317            "Element": "Element",
318            "Issue": "Problem",
319            "How to Fix": "Lösung",
320            "Close Report": "Bericht schließen",
321            "Passed Tests": "Bestandene Tests",
322            "Items Need Review": "Elemente zur Überprüfung",
323            "Annotations": "Annotations",
324            "Text to Speech": "Text to Speech",
325            "Text to Speech On": "Sprachausgabe Ein",
326            "Text to Speech Off": "Sprachausgabe Aus",
327            "Simplify Layout": "Simplify Layout",
328            "Play": "Play",
329            "Pause": "Pause",
330            "Stop": "Stop",
331            "Reading...": "Reading...",
332          },
333          es: {
334            "Accessibility Menu": "Menú de Accesibilidad",
335            "Reset settings": "Restablecer configuración",
336            "Reset All Settings": "Restablecer toda la configuración",
337            "Close": "Cerrar",
338            "Content Adjustments": "Ajustes de Contenido",
339            "Adjust Font Size": "Ajustar Tamaño de Fuente",
340            "Highlight Title": "Resaltar Títulos",
341            "Highlight Links": "Resaltar Enlaces",
342            "Readable Font": "Fuente Legible",
343            "Color Adjustments": "Ajustes de Color",
344            "Invert Colors": "Invertir Colores",
345            "Light Contrast": "Contraste Claro",
346            "Dark Contrast": "Contraste Oscuro",
347            "High Contrast": "Alto Contraste",
348            "High Saturation": "Alta Saturación",
349            "Low Saturation": "Baja Saturación",
350            "Monochrome": "Monocromo",
351            "Tools": "Herramientas",
352            "Reading Guide": "Guía de Lectura",
353            "Stop Animations": "Detener Animaciones",
354            "Big Cursor": "Cursor Grande",
355            "Increase Font Size": "Aumentar Tamaño de Fuente",
356            "Decrease Font Size": "Disminuir Tamaño de Fuente",
357            "Letter Spacing": "Espaciado de Letras",
358            "Line Height": "Altura de Línea",
359            "Font Weight": "Grosor de Fuente",
360            "Dyslexia Font": "Fuente para Dislexia",
361            "Language": "Idioma",
362            "Open Accessibility Menu": "Abrir Menú de Accesibilidad",
363            "Hide Images": "Ocultar Imágenes",
364            "Skip to accessibility menu": "Ir al menú de accesibilidad",
365            "Accessibility Report": "Informe de Accesibilidad",
366            "Run Accessibility Check": "Ejecutar Comprobación de Accesibilidad",
367            "Loading...": "Cargando...",
368            "Analyzing page...": "Analizando página...",
369            "Critical": "Crítico",
370            "Serious": "Grave",
371            "Moderate": "Moderado",
372            "Minor": "Menor",
373            "Violations Found": "Violaciones Encontradas",
374            "No Issues Found": "Sin Problemas Encontrados",
375            "Element": "Elemento",
376            "Issue": "Problema",
377            "How to Fix": "Cómo Solucionarlo",
378            "Close Report": "Cerrar Informe",
379            "Passed Tests": "Pruebas Superadas",
380            "Items Need Review": "Elementos a Revisar",
381            "Annotations": "Annotations",
382            "Text to Speech": "Text to Speech",
383            "Text to Speech On": "Texto a Voz Activado",
384            "Text to Speech Off": "Texto a Voz Desactivado",
385            "Simplify Layout": "Simplify Layout",
386            "Play": "Play",
387            "Pause": "Pause",
388            "Stop": "Stop",
389            "Reading...": "Reading...",
390          },
391          ru: {
392            "Accessibility Menu": "Меню доступности",
393            "Reset settings": "Сбросить настройки",
394            "Reset All Settings": "Сбросить все настройки",
395            "Close": "Закрыть",
396            "Content Adjustments": "Настройки контента",
397            "Adjust Font Size": "Изменить размер шрифта",
398            "Highlight Title": "Выделить заголовки",
399            "Highlight Links": "Выделить ссылки",
400            "Readable Font": "Читаемый шрифт",
401            "Color Adjustments": "Настройки цвета",
402            "Invert Colors": "Инвертировать цвета",
403            "Light Contrast": "Светлый контраст",
404            "Dark Contrast": "Тёмный контраст",
405            "High Contrast": "Высокий контраст",
406            "High Saturation": "Высокая насыщенность",
407            "Low Saturation": "Низкая насыщенность",
408            "Monochrome": "Монохромный",
409            "Tools": "Инструменты",
410            "Reading Guide": "Линейка для чтения",
411            "Stop Animations": "Остановить анимации",
412            "Big Cursor": "Большой курсор",
413            "Increase Font Size": "Увеличить размер шрифта",
414            "Decrease Font Size": "Уменьшить размер шрифта",
415            "Letter Spacing": "Межбуквенный интервал",
416            "Line Height": "Высота строки",
417            "Font Weight": "Жирный шрифт",
418            "Dyslexia Font": "Шрифт для дислексии",
419            "Language": "Язык",
420            "Open Accessibility Menu": "Открыть меню доступности",
421            "Hide Images": "Скрыть изображения",
422            "Skip to accessibility menu": "Перейти к меню доступности",
423            "Accessibility Report": "Отчёт о доступности",
424            "Run Accessibility Check": "Запустить проверку доступности",
425            "Loading...": "Загрузка...",
426            "Analyzing page...": "Анализ страницы...",
427            "Critical": "Критический",
428            "Serious": "Серьёзный",
429            "Moderate": "Умеренный",
430            "Minor": "Незначительный",
431            "Violations Found": "Найдены нарушения",
432            "No Issues Found": "Проблем не найдено",
433            "Element": "Элемент",
434            "Issue": "Проблема",
435            "How to Fix": "Как исправить",
436            "Close Report": "Закрыть отчёт",
437            "Passed Tests": "Пройденные тесты",
438            "Items Need Review": "Требуют проверки",
439            "Annotations": "Annotations",
440            "Text to Speech": "Text to Speech",
441            "Text to Speech On": "Озвучивание Включено",
442            "Text to Speech Off": "Озвучивание Выключено",
443            "Simplify Layout": "Simplify Layout",
444            "Play": "Play",
445            "Pause": "Pause",
446            "Stop": "Stop",
447            "Reading...": "Reading...",
448          },
449          pl: {
450            "Accessibility Menu": "Menu Dostępności",
451            "Reset settings": "Zresetuj ustawienia",
452            "Reset All Settings": "Zresetuj wszystkie ustawienia",
453            "Close": "Zamknij",
454            "Content Adjustments": "Dostosowanie Treści",
455            "Adjust Font Size": "Dostosuj Rozmiar Czcionki",
456            "Highlight Title": "Podświetl Tytuły",
457            "Highlight Links": "Podświetl Linki",
458            "Readable Font": "Czytelna Czcionka",
459            "Color Adjustments": "Dostosowanie Kolorów",
460            "Invert Colors": "Odwróć Kolory",
461            "Light Contrast": "Jasny Kontrast",
462            "Dark Contrast": "Ciemny Kontrast",
463            "High Contrast": "Wysoki Kontrast",
464            "High Saturation": "Wysoka Saturacja",
465            "Low Saturation": "Niska Saturacja",
466            "Monochrome": "Monochromatyczny",
467            "Tools": "Narzędzia",
468            "Reading Guide": "Linia do Czytania",
469            "Stop Animations": "Zatrzymaj Animacje",
470            "Big Cursor": "Duży Kursor",
471            "Increase Font Size": "Zwiększ Rozmiar Czcionki",
472            "Decrease Font Size": "Zmniejsz Rozmiar Czcionki",
473            "Letter Spacing": "Odstępy Między Literami",
474            "Line Height": "Wysokość Linii",
475            "Font Weight": "Grubość Czcionki",
476            "Dyslexia Font": "Czcionka dla Dysleksji",
477            "Language": "Język",
478            "Open Accessibility Menu": "Otwórz Menu Dostępności",
479            "Hide Images": "Ukryj Obrazy",
480            "Skip to accessibility menu": "Przejdź do menu dostępności",
481            "Accessibility Report": "Raport Dostępności",
482            "Run Accessibility Check": "Uruchom Sprawdzanie Dostępności",
483            "Loading...": "Ładowanie...",
484            "Analyzing page...": "Analizowanie strony...",
485            "Critical": "Krytyczny",
486            "Serious": "Poważny",
487            "Moderate": "Umiarkowany",
488            "Minor": "Drobny",
489            "Violations Found": "Znalezione Naruszenia",
490            "No Issues Found": "Brak Problemów",
491            "Element": "Element",
492            "Issue": "Problem",
493            "How to Fix": "Jak Naprawić",
494            "Close Report": "Zamknij Raport",
495            "Passed Tests": "Zaliczone Testy",
496            "Items Need Review": "Elementy do Sprawdzenia",
497            "Annotations": "Annotations",
498            "Text to Speech": "Text to Speech",
499            "Text to Speech On": "Czytanie Tekstu Włączone",
500            "Text to Speech Off": "Czytanie Tekstu Wyłączone",
501            "Simplify Layout": "Simplify Layout",
502            "Play": "Play",
503            "Pause": "Pause",
504            "Stop": "Stop",
505            "Reading...": "Reading...",
506          },
507          ro: {
508            "Accessibility Menu": "Meniu Accesibilitate",
509            "Reset settings": "Resetare setări",
510            "Reset All Settings": "Resetare toate setările",
511            "Close": "Închide",
512            "Content Adjustments": "Ajustări Conținut",
513            "Adjust Font Size": "Ajustare Dimensiune Font",
514            "Highlight Title": "Evidențiere Titluri",
515            "Highlight Links": "Evidențiere Linkuri",
516            "Readable Font": "Font Lizibil",
517            "Color Adjustments": "Ajustări Culoare",
518            "Invert Colors": "Inversare Culori",
519            "Light Contrast": "Contrast Luminos",
520            "Dark Contrast": "Contrast Întunecat",
521            "High Contrast": "Contrast Ridicat",
522            "High Saturation": "Saturație Ridicată",
523            "Low Saturation": "Saturație Scăzută",
524            "Monochrome": "Monocrom",
525            "Tools": "Instrumente",
526            "Reading Guide": "Ghid de Citire",
527            "Stop Animations": "Oprire Animații",
528            "Big Cursor": "Cursor Mare",
529            "Increase Font Size": "Mărire Dimensiune Font",
530            "Decrease Font Size": "Micșorare Dimensiune Font",
531            "Letter Spacing": "Spațiere Litere",
532            "Line Height": "Înălțime Linie",
533            "Font Weight": "Grosime Font",
534            "Dyslexia Font": "Font pentru Dislexie",
535            "Language": "Limbă",
536            "Open Accessibility Menu": "Deschide Meniul Accesibilitate",
537            "Hide Images": "Ascunde Imagini",
538            "Skip to accessibility menu": "Salt la meniul de accesibilitate",
539            "Accessibility Report": "Raport Accesibilitate",
540            "Run Accessibility Check": "Rulează Verificare Accesibilitate",
541            "Loading...": "Se încarcă...",
542            "Analyzing page...": "Se analizează pagina...",
543            "Critical": "Critic",
544            "Serious": "Grav",
545            "Moderate": "Moderat",
546            "Minor": "Minor",
547            "Violations Found": "Încălcări Găsite",
548            "No Issues Found": "Nicio Problemă Găsită",
549            "Element": "Element",
550            "Issue": "Problemă",
551            "How to Fix": "Cum se Remediază",
552            "Close Report": "Închide Raportul",
553            "Passed Tests": "Teste Trecute",
554            "Items Need Review": "Elemente de Verificat",
555            "Annotations": "Annotations",
556            "Text to Speech": "Text to Speech",
557            "Text to Speech On": "Text to Speech Pornit",
558            "Text to Speech Off": "Text to Speech Oprit",
559            "Simplify Layout": "Simplify Layout",
560            "Play": "Play",
561            "Pause": "Pause",
562            "Stop": "Stop",
563            "Reading...": "Reading...",
564          },
565          nl: {
566            "Accessibility Menu": "Toegankelijkheidsmenu",
567            "Reset settings": "Instellingen resetten",
568            "Reset All Settings": "Alle instellingen resetten",
569            "Close": "Sluiten",
570            "Content Adjustments": "Inhoudsaanpassingen",
571            "Adjust Font Size": "Lettergrootte aanpassen",
572            "Highlight Title": "Titels markeren",
573            "Highlight Links": "Links markeren",
574            "Readable Font": "Leesbaar Lettertype",
575            "Color Adjustments": "Kleuraanpassingen",
576            "Invert Colors": "Kleuren omkeren",
577            "Light Contrast": "Licht Contrast",
578            "Dark Contrast": "Donker Contrast",
579            "High Contrast": "Hoog Contrast",
580            "High Saturation": "Hoge Verzadiging",
581            "Low Saturation": "Lage Verzadiging",
582            "Monochrome": "Monochroom",
583            "Tools": "Hulpmiddelen",
584            "Reading Guide": "Leeshulp",
585            "Stop Animations": "Animaties stoppen",
586            "Big Cursor": "Grote Cursor",
587            "Increase Font Size": "Lettergrootte vergroten",
588            "Decrease Font Size": "Lettergrootte verkleinen",
589            "Letter Spacing": "Letterafstand",
590            "Line Height": "Regelhoogte",
591            "Font Weight": "Letterdikte",
592            "Dyslexia Font": "Dyslexie Lettertype",
593            "Language": "Taal",
594            "Open Accessibility Menu": "Toegankelijkheidsmenu openen",
595            "Hide Images": "Afbeeldingen verbergen",
596            "Skip to accessibility menu": "Ga naar toegankelijkheidsmenu",
597            "Accessibility Report": "Toegankelijkheidsrapport",
598            "Run Accessibility Check": "Toegankelijkheidscontrole uitvoeren",
599            "Loading...": "Laden...",
600            "Analyzing page...": "Pagina analyseren...",
601            "Critical": "Kritiek",
602            "Serious": "Ernstig",
603            "Moderate": "Matig",
604            "Minor": "Gering",
605            "Violations Found": "Overtredingen Gevonden",
606            "No Issues Found": "Geen Problemen Gevonden",
607            "Element": "Element",
608            "Issue": "Probleem",
609            "How to Fix": "Hoe op te lossen",
610            "Close Report": "Rapport Sluiten",
611            "Passed Tests": "Geslaagde Tests",
612            "Items Need Review": "Items te Controleren",
613            "Annotations": "Annotations",
614            "Text to Speech": "Text to Speech",
615            "Text to Speech On": "Tekst naar Spraak Aan",
616            "Text to Speech Off": "Tekst naar Spraak Uit",
617            "Simplify Layout": "Simplify Layout",
618            "Play": "Play",
619            "Pause": "Pause",
620            "Stop": "Stop",
621            "Reading...": "Reading...",
622          },
623          uk: {
624            "Accessibility Menu": "Меню доступності",
625            "Reset settings": "Скинути налаштування",
626            "Reset All Settings": "Скинути всі налаштування",
627            "Close": "Закрити",
628            "Content Adjustments": "Налаштування контенту",
629            "Adjust Font Size": "Змінити розмір шрифту",
630            "Highlight Title": "Виділити заголовки",
631            "Highlight Links": "Виділити посилання",
632            "Readable Font": "Читабельний шрифт",
633            "Color Adjustments": "Налаштування кольору",
634            "Invert Colors": "Інвертувати кольори",
635            "Light Contrast": "Світлий контраст",
636            "Dark Contrast": "Темний контраст",
637            "High Contrast": "Високий контраст",
638            "High Saturation": "Висока насиченість",
639            "Low Saturation": "Низька насиченість",
640            "Monochrome": "Монохромний",
641            "Tools": "Інструменти",
642            "Reading Guide": "Лінійка для читання",
643            "Stop Animations": "Зупинити анімації",
644            "Big Cursor": "Великий курсор",
645            "Increase Font Size": "Збільшити розмір шрифту",
646            "Decrease Font Size": "Зменшити розмір шрифту",
647            "Letter Spacing": "Міжлітерний інтервал",
648            "Line Height": "Висота рядка",
649            "Font Weight": "Жирний шрифт",
650            "Dyslexia Font": "Шрифт для дислексії",
651            "Language": "Мова",
652            "Open Accessibility Menu": "Відкрити меню доступності",
653            "Hide Images": "Приховати зображення",
654            "Skip to accessibility menu": "Перейти до меню доступності",
655            "Accessibility Report": "Звіт про доступність",
656            "Run Accessibility Check": "Запустити перевірку доступності",
657            "Loading...": "Завантаження...",
658            "Analyzing page...": "Аналіз сторінки...",
659            "Critical": "Критичний",
660            "Serious": "Серйозний",
661            "Moderate": "Помірний",
662            "Minor": "Незначний",
663            "Violations Found": "Знайдені порушення",
664            "No Issues Found": "Проблем не знайдено",
665            "Element": "Елемент",
666            "Issue": "Проблема",
667            "How to Fix": "Як виправити",
668            "Close Report": "Закрити звіт",
669            "Passed Tests": "Пройдені тести",
670            "Items Need Review": "Потребують перевірки",
671            "Annotations": "Annotations",
672            "Text to Speech": "Text to Speech",
673            "Text to Speech On": "Озвучування Увімкнено",
674            "Text to Speech Off": "Озвучування Вимкнено",
675            "Simplify Layout": "Simplify Layout",
676            "Play": "Play",
677            "Pause": "Pause",
678            "Stop": "Stop",
679            "Reading...": "Reading...",
680          }
681        };
682
683    const SUPPORTED_LANGUAGES = [
684          { code: "en", label: "English (English)" },
685          { code: "it", label: "Italiano (Italian)" },
686          { code: "fr", label: "Français (French)" },
687          { code: "de", label: "Deutsch (German)" },
688          { code: "es", label: "Español (Spanish)" },
689          { code: "ru", label: "Русский (Russian)" },
690          { code: "pl", label: "Polski (Polish)" },
691          { code: "ro", label: "Română (Romanian)" },
692          { code: "nl", label: "Nederlands (Dutch)" },
693          { code: "uk", label: "Українська (Ukrainian)" }
694        ];
695
696    /** @typedef {import('./index.js').default} AccessibleWebWidget */
697
698    /** @type {{ [methodName: string]: (this: AccessibleWebWidget, ...args: any[]) => any }} */
699    const stateMethods = {
700
701      storageAvailable() {
702          try {
703            const test = '__test__';
704            localStorage.setItem(test, test);
705            localStorage.removeItem(test);
706            return true;
707          } catch {
708            return false;
709          }
710        },
711
712      fetchCookie(name) {
713          const cookieName = name + "=";
714          try {
715            const decodedCookie = decodeURIComponent(document.cookie);
716            return decodedCookie.split(';')
717              .map(c => c.trim())
718              .find(c => c.startsWith(cookieName))
719              ?.substring(cookieName.length) || "{}";
720          } catch (e) {
721            console.warn('Error reading cookie:', e);
722            return "{}";
723          }
724        },
725
726      storeCookie(name, value, days) {
727          try {
728            const d = new Date();
729            d.setTime(d.getTime() + (days * 24 * 60 * 60 * 1000));
730            const expires = "expires=" + d.toUTCString();
731            const isSecure = window.location.protocol === 'https:';
732            document.cookie = name + "=" + value + ";" + expires + ";path=/;SameSite=Strict" + (isSecure ? ";Secure" : "");
733          } catch (e) {
734            console.warn('Error setting cookie:', e);
735          }
736        },
737
738      getSavedLanguage() {
739          try {
740            // Try localStorage first
741            if (this.storageAvailable()) {
742              const stored = localStorage.getItem(this.cookieKey);
743              if (stored) {
744                const config = JSON.parse(stored);
745                if (config.lang) return config.lang;
746              }
747            }
748            // Fallback to cookie
749            const cookieVal = this.fetchCookie(this.cookieKey);
750            if (cookieVal && cookieVal !== "") {
751              const config = JSON.parse(cookieVal);
752              if (config.lang) return config.lang;
753            }
754          } catch {
755            // Ignore parsing errors
756          }
757          return null;
758        },
759
760      getBrowserLanguage() {
761          if (typeof navigator === 'undefined') return 'en';
762      
763          const supportedCodes = this.supportedLanguages.map(lang => lang.code);
764      
765          // Get browser languages (navigator.languages is an array, navigator.language is a string)
766          const browserLanguages = navigator.languages
767            ? [...navigator.languages]
768            : [navigator.language || navigator.userLanguage || 'en'];
769      
770          for (const browserLang of browserLanguages) {
771            // Extract the primary language code (e.g., 'en-US' -> 'en')
772            const primaryCode = browserLang.split('-')[0].toLowerCase();
773      
774            if (supportedCodes.includes(primaryCode)) {
775              return primaryCode;
776            }
777          }
778      
779          // Default to English if no supported language found
780          return 'en';
781        },
782
783      getDefaultLanguage() {
784          // Priority: 1. Saved language, 2. Browser language, 3. English
785          const savedLang = this.getSavedLanguage();
786          if (savedLang) {
787            const supportedCodes = this.supportedLanguages.map(lang => lang.code);
788            if (supportedCodes.includes(savedLang)) {
789              return savedLang;
790            }
791          }
792          return this.getBrowserLanguage();
793        },
794
795      isDevMode() {
796          if (typeof window === 'undefined') return false;
797          try {
798            const urlParams = new URLSearchParams(window.location.search);
799            return urlParams.get('acc-dev') === 'true';
800          } catch {
801            return false;
802          }
803        },
804
805      getDataAttributeOptions() {
806          const options = {};
807          if (typeof document === 'undefined') return options;
808      
809          const attributes = ['lang', 'position', 'offset', 'size', 'icon'];
810      
811          const assignValue = (key, value) => {
812            if (value === null || typeof value === 'undefined' || value === '') return;
813            switch (key) {
814              case 'lang': {
815                const lang = String(value).trim();
816                if (lang) options.lang = lang;
817                break;
818              }
819              case 'position': {
820                const position = String(value).trim();
821                if (position) options.position = position;
822                break;
823              }
824              case 'offset': {
825                const normalized = this.normalizeOffset(value);
826                if (normalized) options.offset = normalized;
827                break;
828              }
829              case 'size': {
830                const normalizedSize = this.normalizeButtonSize(value);
831                if (normalizedSize) options.size = normalizedSize;
832                break;
833              }
834              case 'icon': {
835                const icon = String(value).trim();
836                if (icon) options.icon = icon;
837                break;
838              }
839            }
840          };
841      
842          const inspectElement = (el) => {
843            if (!el) return;
844            attributes.forEach(key => {
845              const attrName = `data-acc-${key}`;
846              const attrValue = el.getAttribute ? el.getAttribute(attrName) : null;
847              assignValue(key, attrValue);
848            });
849          };
850      
851          const scriptCandidates = [];
852          if (document.currentScript) {
853            scriptCandidates.push(document.currentScript);
854          }
855          document.querySelectorAll('script[data-acc-lang],script[data-acc-position],script[data-acc-offset],script[data-acc-size],script[data-acc-icon]').forEach(script => {
856            if (!scriptCandidates.includes(script)) {
857              scriptCandidates.push(script);
858            }
859          });
860          scriptCandidates.forEach(inspectElement);
861      
862          attributes.forEach(key => {
863            if (options[key] !== undefined) return;
864            const el = document.querySelector(`[data-acc-${key}]`);
865            if (el) {
866              assignValue(key, el.getAttribute(`data-acc-${key}`));
867            }
868          });
869      
870          return options;
871        },
872
873      normalizeOffset(value) {
874          if (!value && value !== 0) return undefined;
875          let parts = [];
876          if (Array.isArray(value)) {
877            parts = value;
878          } else if (typeof value === 'string') {
879            parts = value.split(/[, ]+/);
880          } else {
881            parts = [value];
882          }
883          const parsed = parts
884            .map(part => {
885              const number = Number(part);
886              return Number.isFinite(number) ? Math.round(number) : null;
887            })
888            .filter(number => number !== null);
889      
890          if (!parsed.length) return undefined;
891          if (parsed.length === 1) parsed.push(parsed[0]);
892          return [parsed[0], parsed[1] !== undefined ? parsed[1] : parsed[0]];
893        },
894
895      normalizeButtonSize(value) {
896          const fallback = this.widgetTheme?.buttonSize || '48px';
897          if (typeof value === 'number' && Number.isFinite(value)) {
898            return `${Math.max(24, Math.round(value))}px`;
899          }
900          if (typeof value === 'string') {
901            const trimmed = value.trim();
902            if (!trimmed) return fallback;
903            if (/^\d+(\.\d+)?(px|em|rem|%)$/i.test(trimmed)) {
904              return trimmed;
905            }
906            const numeric = Number(trimmed);
907            if (Number.isFinite(numeric)) {
908              return `${Math.max(24, Math.round(numeric))}px`;
909            }
910            return trimmed;
911          }
912          return fallback;
913        },
914
915      toggleDisplay(el, state) {
916          if (!el) return;
917          try {
918            el.style.display = (typeof state === "undefined") 
919              ? (el.style.display === "none" ? "block" : "none") 
920              : (state ? "block" : "none");
921          } catch (e) {
922            console.warn('Error toggling element:', e);
923          }
924        },
925
926      isSystemControlledPreference(key) {
927          const systemDefaults = this.widgetConfig?.systemDefaults || {};
928          return Object.prototype.hasOwnProperty.call(systemDefaults, key);
929        },
930
931      hasExplicitStatePreference(key) {
932          const states = this.widgetConfig?.states || {};
933          if (!Object.prototype.hasOwnProperty.call(states, key)) {
934            return false;
935          }
936          return !this.isSystemControlledPreference(key);
937        },
938
939      hasExplicitColorFilterPreference() {
940          const states = this.widgetConfig?.states || {};
941          const systemDefaults = this.widgetConfig?.systemDefaults || {};
942          const keys = Array.isArray(this.colorFilterKeys) ? this.colorFilterKeys : [];
943          return keys.some((key) =>
944            Object.prototype.hasOwnProperty.call(states, key) &&
945            !Object.prototype.hasOwnProperty.call(systemDefaults, key)
946          );
947        },
948
949      updateState(payload, options = {}) {
950          const source = options.source || 'user';
951          const previousStates = this.widgetConfig.states || {};
952          const previousSystemDefaults = this.widgetConfig.systemDefaults || {};
953          const nextStates = { ...previousStates, ...payload };
954          const nextSystemDefaults = { ...previousSystemDefaults };
955          const keys = Object.keys(payload || {});
956
957          if (source === 'system') {
958            keys.forEach((key) => {
959              nextSystemDefaults[key] = payload[key];
960            });
961          } else {
962            keys.forEach((key) => {
963              if (Object.prototype.hasOwnProperty.call(nextSystemDefaults, key)) {
964                delete nextSystemDefaults[key];
965              }
966            });
967          }
968
969          const updatedConfig = { ...this.widgetConfig, states: nextStates, systemDefaults: nextSystemDefaults };
970          this.saveConfig(updatedConfig);
971          return updatedConfig;
972        },
973
974      saveConfig(newConfig) {
975          this.widgetConfig = { ...this.widgetConfig, ...newConfig };
976          if (this.storageAvailable()) {
977            try {
978              localStorage.setItem(this.cookieKey, JSON.stringify(this.widgetConfig));
979            } catch {
980              this.storeCookie(this.cookieKey, JSON.stringify(this.widgetConfig), 365);
981            }
982          } else {
983            this.storeCookie(this.cookieKey, JSON.stringify(this.widgetConfig), 365);
984          }
985        },
986
987      retrieveState(key) {
988          return this.widgetConfig.states ? this.widgetConfig.states[key] : undefined;
989        },
990
991      loadConfig(cache = true) {
992          if (cache) return this.widgetConfig;
993          const savedConfig = this.fetchSavedConfig();
994          if (savedConfig) {
995            try {
996              this.widgetConfig = JSON.parse(savedConfig);
997            } catch (e) {
998              console.warn('Error parsing config:', e);
999              this.widgetConfig = {};
1000            }
1001          }
1002          return this.widgetConfig;
1003        },
1004
1005      fetchSavedConfig() {
1006          if (this.storageAvailable()) {
1007            try {
1008              const stored = localStorage.getItem(this.cookieKey);
1009              if (stored) return stored;
1010            } catch (e) {
1011              console.warn('localStorage failed, falling back to cookies:', e);
1012            }
1013          }
1014          const cookieVal = this.fetchCookie(this.cookieKey);
1015          return cookieVal && cookieVal !== "" ? cookieVal : "{}";
1016        },
1017
1018      fetchDataAttr(attr) {
1019          try {
1020            const dataAttr = `data-acc-${attr}`;
1021            const element = document.querySelector(`[${dataAttr}]`);
1022            return element ? element.getAttribute(dataAttr) : null;
1023          } catch (e) {
1024            console.warn(`Error getting data attribute: ${attr}`, e);
1025            return null;
1026          }
1027        },
1028
1029    };
1030
1031    var menuCSS = "/* Base styles */\n.acc-menu {\n  position: fixed;\n  left: var(--acc-menu-inline-gap, 12px);\n  top: var(--acc-menu-block-gap, 12px);\n  bottom: var(--acc-menu-block-gap, 12px);\n  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.12);\n  opacity: 1;\n  transition: 0.3s;\n  z-index: var(--acc-widget-z-index, 100000);\n  overflow: hidden;\n  background: var(--acc-bg-color);\n  width: min(430px, calc(100vw - (var(--acc-menu-inline-gap, 12px) * 2)));\n  line-height: 1.5;\n  font-size: 16px;\n  height: auto;\n  letter-spacing: 0.015em;\n  color: var(--acc-text-color);\n  --acc-content-inline-padding: 14px;\n  --acc-menu-inline-gap: clamp(8px, 2.3vw, 18px);\n  --acc-menu-block-gap: clamp(10px, 2.2vh, 20px);\n  border-radius: 16px;\n  box-shadow: 0 18px 42px rgba(0, 0, 0, 0.2);\n}\n\n/* Ensure all elements inherit proper colors for accessibility */\n.acc-menu * {\n  color: var(--acc-text-color);\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;\n  padding: 0;\n  margin: 0;\n  line-height: 1.5 !important;\n  letter-spacing: normal !important;\n}\n\n/* Header section */\n.acc-menu-header {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  padding: 0 var(--acc-content-inline-padding);\n  height: var(--acc-header-height);\n  font-weight: 700 !important;\n  background-color: var(--acc-primary-color) !important;\n}\n\n.acc-menu-title {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  font-size: 18px !important;\n  color: var(--acc-text-color-inverted) !important;\n  font-weight: bold;\n}\n\n.acc-menu-title .acc-label {\n  color: var(--acc-text-color-inverted) !important;\n}\n\n.acc-menu-title-icon {\n  width: 30px;\n  height: 30px;\n  border-radius: 8px;\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  flex-shrink: 0;\n}\n\n.acc-menu-title-icon svg {\n  fill: var(--acc-text-color-inverted) !important;\n  width: 18px !important;\n  height: 18px !important;\n  min-width: 18px !important;\n  min-height: 18px !important;\n  max-width: 18px !important;\n  max-height: 18px !important;\n}\n\n.acc-header-back {\n  display: flex;\n  align-items: center;\n}\n\n.acc-back-btn {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  background: transparent;\n  border: none;\n  padding: 8px;\n  cursor: pointer;\n  font-size: 16px;\n  font-weight: 600;\n  color: var(--acc-text-color-inverted) !important;\n  transition: background-color 0.2s ease;\n  border-radius: 4px;\n  visibility: hidden;\n}\n\n.acc-back-btn > span {\n  color: var(--acc-text-color-inverted) !important;\n}\n\n.acc-back-btn.visible {\n  visibility: visible;\n}\n\n.acc-back-btn:hover {\n  background-color: rgba(255, 255, 255, 0.18);\n}\n\n.acc-back-btn:focus {\n  outline: 2px solid var(--acc-text-color-inverted);\n  outline-offset: 1px;\n}\n\n.acc-back-btn svg {\n  fill: var(--acc-text-color-inverted) !important;\n  width: 24px !important;\n  height: 24px !important;\n}\n\n.acc-menu-title-dynamic {\n  display: none !important;\n}\n\n.acc-menu-title-dynamic.visible {\n  display: block !important;\n}\n\n.acc-menu-title-default {\n  display: block !important;\n}\n\n.acc-menu-title-default.hidden {\n  display: none !important;\n}\n\n.acc-menu-header svg {\n  fill: var(--acc-text-color-inverted) !important;\n  width: 28px !important;\n  height: 28px !important;\n  min-width: 28px !important;\n  min-height: 28px !important;\n  max-width: 28px !important;\n  max-height: 28px !important;\n}\n\n.acc-menu-header > div {\n  display: flex;\n  align-items: center;\n}\n\n/* Interactive elements */\n.acc-menu-header div[role=\"button\"] {\n  cursor: pointer;\n  padding: 8px;\n  border-radius: 50%;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  width: 36px;\n  height: 36px;\n  transition: background-color 0.2s ease;\n}\n\n.acc-menu-header div[role=\"button\"]:hover {\n  background-color: rgba(255, 255, 255, 0.18);\n}\n\n.acc-menu-header div[role=\"button\"]:focus {\n  outline: 2px solid var(--acc-text-color-inverted);\n  outline-offset: 1px;\n}\n\n.acc-menu-header .acc-header-actions {\n  display: flex;\n  align-items: center;\n}\n\n.acc-language-container {\n  margin: 0 var(--acc-content-inline-padding) 24px;\n}\n\n.acc-lang-details {\n  border: 1px solid rgba(0, 0, 0, 0.1);\n  border-radius: var(--acc-button-border-radius);\n  background: var(--acc-card-bg);\n}\n\n.acc-lang-summary {\n  list-style: none;\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  gap: 12px;\n  padding: 12px 14px;\n  cursor: pointer;\n  border-radius: var(--acc-button-border-radius);\n}\n\n.acc-lang-summary::-webkit-details-marker {\n  display: none;\n}\n\n.acc-lang-summary::marker {\n  content: '';\n}\n\n.acc-lang-summary-main {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  min-width: 0;\n}\n\n.acc-lang-current-label {\n  font-size: 16px !important;\n  font-weight: 600 !important;\n}\n\n.acc-lang-summary:hover {\n  background-color: rgba(25, 118, 210, 0.06);\n}\n\n.acc-lang-summary:focus {\n  outline: var(--acc-focus-outline-width) solid var(--acc-focus-ring-color);\n  outline-offset: var(--acc-focus-outline-offset);\n}\n\n.acc-lang-summary-arrow {\n  width: 10px;\n  height: 10px;\n  border-right: 2px solid var(--acc-text-color);\n  border-bottom: 2px solid var(--acc-text-color);\n  transform: rotate(-45deg);\n  transition: transform 0.2s ease;\n}\n\n.acc-lang-details[open] .acc-lang-summary-arrow {\n  transform: rotate(45deg);\n}\n\n.acc-lang-details[open] .acc-lang-summary {\n  border-bottom: 1px solid var(--acc-border-color);\n  border-bottom-left-radius: 0;\n  border-bottom-right-radius: 0;\n}\n\n.acc-lang-details-panel {\n  padding: 12px 0 8px;\n}\n\n.acc-lang-details-panel .acc-section-title {\n  font-size: 16px !important;\n  padding: 0 16px 10px;\n}\n\n.acc-lang-flag {\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  min-width: 22px;\n  font-size: 18px !important;\n  line-height: 1;\n}\n\n.acc-lang-search-wrapper {\n  padding: 0 16px 8px;\n}\n\n.acc-lang-search {\n  width: 100%;\n  padding: 10px 16px;\n  border: 1.5px solid var(--acc-border-color);\n  border-radius: var(--acc-button-border-radius);\n  font-size: 16px;\n  background-color: var(--acc-card-bg);\n  transition: border-color 0.2s ease;\n}\n\n.acc-lang-search:focus {\n  outline: var(--acc-focus-outline-width) solid var(--acc-focus-ring-color);\n  outline-offset: var(--acc-focus-outline-offset);\n  border-color: var(--acc-primary-color) !important;\n}\n\n.acc-lang-list {\n  padding: 6px 8px 12px;\n  max-height: 280px;\n  overflow-y: auto;\n}\n\n.acc-lang-item {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  gap: 8px;\n  width: 100%;\n  text-align: left;\n  padding: 11px 10px;\n  margin-bottom: 4px;\n  background-color: transparent;\n  border: none;\n  border-radius: var(--acc-button-border-radius);\n  cursor: pointer;\n  font-size: 16px;\n  color: var(--acc-text-color);\n  transition: background-color 0.12s ease;\n}\n\n.acc-lang-item:hover {\n  background-color: rgba(25, 118, 210, 0.06);\n}\n\n.acc-lang-item:focus {\n  outline: var(--acc-focus-outline-width) solid var(--acc-focus-ring-color);\n  outline-offset: var(--acc-focus-outline-offset);\n}\n\n.acc-lang-item.selected {\n  background-color: rgba(25, 118, 210, 0.08);\n  font-weight: 600;\n}\n\n.acc-lang-item-main {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  min-width: 0;\n}\n\n.acc-lang-item-label {\n  font-size: 16px !important;\n  line-height: 1.4 !important;\n}\n\n.acc-icon-check {\n  display: inline-block;\n  width: 18px;\n  height: 18px;\n  background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 24 24' fill='none' stroke='%23886f60' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E\");\n  background-repeat: no-repeat;\n  background-position: center;\n  opacity: 0;\n  transition: opacity 0.15s ease;\n}\n\n.acc-lang-item.selected .acc-icon-check {\n  opacity: 1;\n}\n\n.acc-menu .acc-lang-select {\n  width: 100% !important;\n  padding: 0 16px !important;\n  font-size: 16px !important;\n  font-family: inherit !important;\n  font-weight: 600 !important;\n  border-radius: var(--acc-button-border-radius) !important;\n  background: var(--acc-card-bg) !important;\n  border: 1.5px solid var(--acc-border-color) !important;\n  min-height: 48px !important;\n  max-height: 48px !important;\n  height: 48px !important;\n  color: var(--acc-text-color) !important;\n  -webkit-appearance: none !important;\n  -moz-appearance: none !important;\n  appearance: none !important;\n  background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24px' viewBox='0%20-960%20960%20960' width='24px' fill='%231f1f1f'%3E%3Cpath d='M480-344%20240-584l56-56%20184 184%20184-184%2056 56-240 240Z'/%3E%3C/svg%3E\") !important;\n  background-repeat: no-repeat !important;\n  background-position: right 12px center !important;\n  background-size: 20px !important;\n  padding-right: 44px !important;\n}\n\n/* Hide default arrows in Firefox and IE */\n.acc-menu .acc-lang-select::-ms-expand {\n  display: none !important;\n}\n\n.acc-menu .acc-lang-select:focus {\n  outline: var(--acc-focus-outline-width) solid var(--acc-focus-ring-color);\n  outline-offset: var(--acc-focus-outline-offset);\n  border-color: var(--acc-primary-color) !important;\n}\n\n/* Option grid layout */\n.acc-options-all {\n  display: flex;\n  flex-direction: column;\n  gap: 16px;\n  padding: 0 var(--acc-content-inline-padding) 12px;\n}\n\n.acc-option-category {\n  display: flex;\n  flex-direction: column;\n  gap: 10px;\n}\n\n.acc-option-category-interaction {\n  padding-bottom: 12px;\n}\n\n.acc-option-category .acc-section-title {\n  font-size: 14px !important;\n  font-weight: 700 !important;\n  letter-spacing: 0.01em !important;\n  text-transform: none;\n  color: #6b7280 !important;\n  padding: 0 2px;\n}\n\n.acc-options {\n  display: grid;\n  grid-template-columns: repeat(3, minmax(0, 1fr));\n  gap: 12px;\n  margin: 0;\n}\n\n.acc-options-text {\n  grid-template-columns: repeat(3, minmax(0, 1fr));\n}\n\n.acc-options-text-inline {\n  grid-template-columns: repeat(2, minmax(0, 1fr));\n}\n\n.acc-btn.acc-text-inline {\n  min-height: 54px;\n  aspect-ratio: auto;\n  flex-direction: row;\n  align-items: center;\n  justify-content: flex-start;\n  text-align: left;\n  gap: 10px;\n  padding: 12px 14px;\n}\n\n.acc-btn.acc-text-inline .acc-label {\n  font-size: 14px !important;\n  flex: 1 1 auto;\n  min-width: 0;\n}\n\n.acc-btn.acc-text-inline .acc-progress-indicator {\n  margin-top: 0;\n  margin-left: auto;\n  justify-content: flex-end;\n  min-width: 24px;\n}\n\n@media only screen and (max-width: 430px) {\n  .acc-options-text-inline {\n    grid-template-columns: repeat(2, minmax(0, 1fr));\n  }\n\n  .acc-btn.acc-text-inline {\n    min-height: auto;\n    aspect-ratio: 11 / 8;\n    flex-direction: column;
1031\n    align-items: center;\n    justify-content: center;\n    text-align: center;\n    gap: 6px;\n    padding: 10px;\n  }\n\n  .acc-btn.acc-text-inline .acc-label {\n    font-size: 13px !important;\n    flex: 0 1 auto;\n  }\n\n  .acc-btn.acc-text-inline .acc-progress-indicator {\n    margin-top: 8px;\n    margin-left: 0;\n    justify-content: center;\n  }\n}\n\n.acc-tts-toggle-container {\n  margin: 0;\n}\n\n.acc-text-scale-control {\n  width: 100%;\n  background: var(--acc-card-bg) !important;\n  border: 1px solid rgba(0, 0, 0, 0.1) !important;\n  border-radius: var(--acc-border-radius);\n  padding: 12px 14px;\n  display: flex;\n  flex-direction: column;\n  gap: 10px;\n}\n\n.acc-text-scale-meta {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n}\n\n.acc-text-scale-icon {\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  flex-shrink: 0;\n}\n\n.acc-text-scale-icon svg {\n  width: 22px !important;\n  height: 22px !important;\n  min-width: 22px !important;\n  min-height: 22px !important;\n  max-width: 22px !important;\n  max-height: 22px !important;\n  fill: var(--acc-text-color);\n}\n\n.acc-text-scale-meta .acc-label {\n  font-size: 15px !important;\n  font-weight: 600 !important;\n}\n\n.acc-text-scale-percent {\n  margin-left: auto;\n  font-size: 15px !important;\n  font-weight: 700 !important;\n  color: var(--acc-primary-color) !important;\n}\n\n.acc-text-scale-range {\n  width: 100%;\n  appearance: none;\n  -webkit-appearance: none;\n  background: transparent;\n  height: 20px;\n  cursor: pointer;\n  margin: 0;\n}\n\n.acc-text-scale-range:focus {\n  outline: none;\n}\n\n.acc-text-scale-range::-webkit-slider-runnable-track {\n  width: 100%;\n  height: 4px;\n  border-radius: 999px;\n  background: linear-gradient(\n    to right,\n    var(--acc-primary-color) 0%,\n    var(--acc-primary-color) var(--acc-text-scale-progress, 0%),\n    #d8d8d8 var(--acc-text-scale-progress, 0%),\n    #d8d8d8 100%\n  );\n}\n\n.acc-text-scale-range::-webkit-slider-thumb {\n  appearance: none;\n  -webkit-appearance: none;\n  width: 18px;\n  height: 18px;\n  border-radius: 50%;\n  background: #fff;\n  border: 2px solid var(--acc-primary-color);\n  margin-top: -7px;\n  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);\n}\n\n.acc-text-scale-range::-moz-range-track {\n  width: 100%;\n  height: 4px;\n  border-radius: 999px;\n  background: #d8d8d8;\n}\n\n.acc-text-scale-range::-moz-range-progress {\n  height: 4px;\n  border-radius: 999px;\n  background: var(--acc-primary-color);\n}\n\n.acc-text-scale-range::-moz-range-thumb {\n  width: 18px;\n  height: 18px;\n  border-radius: 50%;\n  background: #fff;\n  border: 2px solid var(--acc-primary-color);\n  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);\n}\n\n.acc-btn.acc-tts-toggle {\n  width: 100%;\n  min-height: 54px;\n  aspect-ratio: auto;\n  flex-direction: row;\n  align-items: center;\n  justify-content: flex-start;\n  gap: 10px;\n  text-align: left;\n  padding: 12px 76px 12px 14px;\n}\n\n.acc-btn.acc-tts-toggle .acc-label {\n  font-size: 15px !important;\n}\n\n.acc-btn.acc-tts-toggle svg {\n  width: 22px !important;\n  height: 22px !important;\n  min-width: 22px !important;\n  min-height: 22px !important;\n  max-width: 22px !important;\n  max-height: 22px !important;\n}\n\n.acc-btn.acc-tts-toggle::before {\n  content: \"\";\n  position: absolute;\n  right: 14px;\n  top: 50%;\n  width: 44px;\n  height: 24px;\n  border-radius: 999px;\n  transform: translateY(-50%);\n  background: #dbd7d2;\n  border: 1px solid rgba(0, 0, 0, 0.08);\n}\n\n.acc-btn.acc-tts-toggle::after {\n  content: \"\";\n  position: absolute;\n  right: 38px;\n  top: 50%;\n  width: 18px;\n  height: 18px;\n  border-radius: 50%;\n  transform: translateY(-50%);\n  background: #fff;\n  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18);\n}\n\n.acc-btn.acc-tts-toggle.acc-selected::before {\n  background: var(--acc-primary-color);\n  border-color: var(--acc-primary-color);\n}\n\n.acc-btn.acc-tts-toggle.acc-selected::after {\n  right: 16px;\n}\n\n/* Button styling */\n.acc-btn {\n  aspect-ratio: 11 / 8;\n  border-radius: var(--acc-border-radius);\n  padding: 10px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  flex-direction: column;\n  text-align: center;\n  font-size: 15px !important;\n  background: var(--acc-card-bg) !important;\n  border: 1px solid rgba(0, 0, 0, 0.1) !important;\n  transition: background-color 0.2s ease;
1031\n  cursor: pointer;\n  word-break: break-word;\n  gap: 6px;\n  position: relative;\n}\n\n.acc-btn:hover {\n  border-color: var(--acc-hover-color) !important;\n  border-width: 1px !important;\n  box-shadow: inset 0 0 0 1px var(--acc-hover-color);\n}\n\n.acc-btn:focus {\n  outline: var(--acc-focus-outline-width) solid var(--acc-focus-ring-color);\n  outline-offset: var(--acc-focus-outline-offset);\n  border-color: var(--acc-primary-color) !important;\n}\n\n.acc-btn .acc-label, .acc-text-adjust .acc-label div {\n  font-size: 13px !important;\n  font-weight: 600 !important;\n}\n\n/* SVG icons */\n.acc-text-adjust svg {\n  width: 20px !important;\n  height: 20px !important;\n  min-width: 20px !important;\n  min-height: 20px !important;\n  max-width: 20px !important;\n  max-height: 20px !important;\n}\n\n.acc-btn svg {\n  width: 24px !important;\n  height: 24px !important;\n  min-width: 24px !important;\n  min-height: 24px !important;\n  max-width: 24px !important;\n  max-height: 24px !important;\n  fill: var(--acc-text-color);\n}\n\n/* Selected state */\n.acc-btn.acc-selected {\n  background-color: var(--acc-primary-color) !important;\n  border-color: var(--acc-primary-color) !important;\n}\n\n.acc-btn.acc-selected .acc-progress-dot {\n  background-color: rgba(255, 255, 255, 0.5);\n}\n\n.acc-btn.acc-selected .acc-progress-dot.active {\n  background-color: var(--acc-text-color-inverted) !important;\n}\n\n.acc-btn.acc-selected svg,\n.acc-btn.acc-selected span,\n.acc-btn.acc-selected .acc-label {\n  fill: var(--acc-text-color-inverted) !important;\n  color: var(--acc-text-color-inverted) !important;\n}\n\n.acc-btn.acc-tts-toggle.acc-selected {\n  background-color: var(--acc-card-bg) !important;\n  border-color: rgba(0, 0, 0, 0.1) !important;\n}\n\n.acc-btn.acc-tts-toggle.acc-selected svg,\n.acc-btn.acc-tts-toggle.acc-selected span,\n.acc-btn.acc-tts-toggle.acc-selected .acc-label {\n  fill: var(--acc-text-color) !important;\n  color: var(--acc-text-color) !important;\n}\n\n/* Footer section */\n.acc-footer {\n  position: absolute;\n  bottom: 0;\n  left: 0;\n  right: 0;\n  background: var(--acc-card-bg);\n  padding: 12px 16px;\n  display: flex;\n  flex-direction: column;\n  align-items: stretch;\n  gap: 10px;\n  border-top: 1px solid var(--acc-border-color);\n  z-index: 100;\n  overflow: visible;\n}\n\n.acc-footer-meta {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  gap: 12px;\n  width: 100%;\n  flex-wrap: nowrap;\n}\n\n.acc-footer-reset {\n  display: flex;\n  width: 100%;\n  align-items: center;\n  justify-content: center;\n  gap: 8px;\n  padding: 12px 16px;\n  border: none;\n  border-radius: var(--acc-button-border-radius);\n  background-color: var(--acc-primary-color) !important;\n  cursor: pointer;\n  transition: background-color 0.2s ease;\n  white-space: nowrap;\n}\n\n.acc-footer-reset svg {\n  width: 24px !important;\n  height: 24px !important;\n  fill: var(--acc-text-color-inverted) !important;\n}\n\n.acc-footer-reset .acc-label {\n  font-size: 16px !important;\n  font-weight: 600 !important;\n  color: var(--acc-text-color-inverted) !important;\n  line-height: 1.2 !important;\n}\n\n.acc-footer-reset:hover {\n  background-color: var(--acc-primary-color-dark) !important;\n}\n\n.acc-footer-reset:focus {\n  outline: var(--acc-focus-outline-width) solid var(--acc-focus-ring-color);\n  outline-offset: var(--acc-focus-outline-offset);\n}\n\n.acc-footer a {\n  font-size: 12px !important;\n  text-decoration: none !important;\n  color: #6b7280 !important;\n  background: transparent !important;\n  font-weight: 500 !important;\n  padding: 4px 0;\n  border-radius: 4px;\n  transition: color 0.15s ease;\n  letter-spacing: 0.01em !important;\n  align-self: center;\n  display: inline-flex;\n  align-items: center;\n  white-space: nowrap;\n  line-height: 1.2 !important;\n}\n\n.acc-footer a:hover {\n  text-decoration: none !important;\n  color: var(--acc-primary-color) !important;\n}\n\n.acc-footer a:focus {\n  outline: var(--acc-focus-outline-width) solid var(--acc-focus-ring-color);\n  outline-offset: var(--acc-focus-outline-offset);\n}\n\n.acc-footer-lang-toggle {\n  display: inline-flex;\n  align-items: center;\n  justify-content: space-between;\n  gap: 8px;\n  min-width: 84px;\n  padding: 7px 10px 7px 12px;\n  border: 1px solid var(--acc-border-color);\n  border-radius: var(--acc-button-border-radius);\n  background: var(--acc-card-bg);\n  cursor: pointer;\n  transition: border-color 0.2s ease, background-color 0.2s ease;\n}\n\n.acc-footer-lang-toggle:hover {\n  border-color: var(--acc-hover-color);\n}\n\n.acc-footer-lang-toggle:focus {\n  outline: var(--acc-focus-outline-width) solid var(--acc-focus-ring-color);\n  outline-offset: var(--acc-focus-outline-offset);\n}\n\n.acc-footer-lang-current {\n  display: inline-block;\n  min-width: 2ch;\n  text-align: left;\n  font-size: 14px !important;\n  font-weight: 600 !important;\n  line-height: 1 !important;\n}\n\n.acc-footer-lang-arrow {\n  width: 8px;\n  height: 8px;\n  border-right: 1.5px solid var(--acc-text-color);\n  border-bottom: 1.5px solid var(--acc-text-color);\n  transform: rotate(45deg) translateY(-1px);\n  transition: transform 0.2s ease;\n}\n\n.acc-footer-lang-toggle[aria-expanded=\"true\"] .acc-footer-lang-arrow {\n  transform: rotate(-135deg) translateY(-1px);\n}\n\n.acc-lang-modal {\n  position: absolute;\n  right: 16px;\n  bottom: calc(100% + 10px);\n  width: min(320px, calc(100% - 32px));\n  border: 1px solid var(--acc-border-color);\n  border-radius: 14px;\n  background: var(--acc-card-bg);\n  box-shadow: 0 16px 32px rgba(0, 0, 0, 0.2);\n  z-index: 120;\n  padding: 10px 0 8px;\n}\n\n.acc-lang-modal[hidden] {\n  display: none;\n}\n\n.acc-lang-modal-header {\n  padding: 0 14px 8px;\n}\n\n.acc-lang-modal .acc-section-title {\n  font-size: 14px !important;\n  font-weight: 700 !important;\n  color: #6b7280 !important;\n}\n\n.acc-lang-modal .acc-lang-search-wrapper {\n  padding: 0 12px 8px;\n}\n\n.acc-lang-modal .acc-lang-list {\n  padding: 6px 8px 8px;\n  max-height: 240px;\n}\n\n/* Content area */\n.acc-menu-content {\n  overflow: auto;\n  max-height: calc(100% - 122px);\n  padding: 24px 0 36px;\n}\n\n/* Text adjustments */\n.acc-text-adjust {\n  background: var(--acc-card-bg);\n  padding: 18px 20px;\n  margin-bottom: 20px;\n  border-radius: var(--acc-border-radius);\n  border: 1px solid rgba(0, 0, 0, 0.1);\n}\n\n.acc-text-adjust .acc-label {\n  display: flex;\n  justify-content: flex-start;\n}\n\n.acc-text-adjust > div {\n  display: flex;\n  justify-content: space-between;\n  margin-top: 20px;\n  align-items: center;\n  font-size: 16px;\n}\n\n.acc-text-adjust .acc-label div {\n  font-size: 16px !important;\n}\n\n.acc-text-adjust div[role=\"button\"] {\n  background: var(--acc-bg-color) !important;\n  border-radius: 50%;\n  width: 40px;\n  height: 40px;\n  display: flex;\n  align-items: center;\n  justify-content: center;
1031\n  cursor: pointer;\n  border: 1px solid rgba(0, 0, 0, 0.1);\n  transition: border-color 0.2s ease;\n}\n\n.acc-text-adjust div[role=\"button\"]:hover {\n  border-color: var(--acc-primary-color);\n}\n\n.acc-text-adjust div[role=\"button\"]:focus {\n  outline: var(--acc-focus-outline-width) solid var(--acc-focus-ring-color);\n  outline-offset: var(--acc-focus-outline-offset);\n}\n\n/* Overlay */\n.acc-overlay {\n  position: fixed;\n  top: 0;\n  left: 0;\n  width: 100%;\n  height: 100%;\n  z-index: calc(var(--acc-widget-z-index, 100000) - 1);\n}\n\n/* Progress indicator */\n.acc-progress-indicator {\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  gap: 4px;\n  margin-top: 8px;\n  height: 8px;\n}\n\n.acc-progress-dot {\n  width: 6px;\n  height: 6px;\n  border-radius: 50%;\n  background-color: var(--acc-border-color);\n  transition: background-color 0.2s ease;\n}\n\n.acc-progress-dot.active {\n  background-color: var(--acc-primary-color);\n}\n\n/* Selected state updates indicator colors */\n.acc-btn.acc-selected .acc-progress-dot.active {\n  background-color: var(--acc-bg-color);\n}\n\n/* Responsive adjustments */\n@media only screen and (max-width: 560px) {\n  .acc-menu {\n    width: calc(100vw - (var(--acc-menu-inline-gap, 8px) * 2));\n  }\n}\n\n@media only screen and (max-width: 420px) {\n  .acc-options {\n    grid-template-columns: repeat(2, minmax(0, 1fr));\n    gap: 12px;\n  }\n  .acc-btn {\n    padding: 8px;\n  }\n}\n\n/* Ensure proper focus visibility for assistive technology */\n@media (prefers-reduced-motion: reduce) {\n  .acc-menu,\n  .acc-btn,\n  .acc-lang-select,\n  .acc-progress-dot,\n  .acc-menu-header div[role=\"button\"],\n  .acc-lang-toggle,\n  .acc-back-btn,\n  .acc-footer-reset,\n  .acc-footer-lang-toggle,\n  .acc-footer-lang-arrow,\n  .acc-text-adjust div[role=\"button\"] {\n    transition: none;\n  }\n}\n";
1032
1033    var widgetCSS = "  /* Base styles for the widget */\n  .acc-widget, .acc-menu {\n    -webkit-user-select: none;\n    -moz-user-select: none;\n    -ms-user-select: none;\n    user-select: none;\n    font-weight: 400;\n    -webkit-font-smoothing: antialiased;\n  }\n  \n  .acc-widget *, .acc-menu * { \n    box-sizing: border-box !important; \n  }\n  \n  /* Accessibility toggle button */\n  .acc-toggle-btn {\n    position: fixed;\n    z-index: var(--acc-widget-z-index, 100000);\n    left: 30px;\n    bottom: 30px;\n    border-radius: 50%;\n    align-items: center;\n    justify-content: center;\n    width: var(--acc-button-size, 48px);\n    height: var(--acc-button-size, 48px);\n    display: flex;\n    cursor: pointer;\n    outline: none !important;\n    border: none !important;\n    box-shadow: inset 0 0 0 4px var(--acc-primary-color, #1976d2), inset 0 0 0 6px white, 0 2px 5px rgba(0,0,0,0.2) !important;\n    background: var(--acc-primary-color, #1976d2) !important;\n    transition: transform 0.2s ease, background-color 0.2s ease, box-shadow 0.2s ease;\n    overflow: visible;\n  }\n  \n  .acc-toggle-btn .acc-toggle-icon {\n    width: 60%;\n    height: 60%;\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n  }\n\n  .acc-toggle-btn .acc-toggle-icon svg {\n    width: 100%;\n    height: 100%;\n    fill: white;\n    transition: none;\n  }\n  \n  .acc-toggle-btn:hover {\n    transform: scale(1.12);\n  }\n\n  .acc-toggle-btn::before,\n  .acc-toggle-btn::after {\n    content: \"\";\n    position: absolute;\n    top: 50%;\n    left: 50%;\n    width: 44%;\n    height: 2.6px;\n    border-radius: 999px;\n    background: var(--acc-text-color-inverted, #fff);\n    opacity: 0;\n    pointer-events: none;\n    transform: translate(-50%, -50%) rotate(0deg);\n    transition: none;\n  }\n\n  .acc-toggle-btn[aria-expanded=\"true\"] {\n    background: var(--acc-primary-color, #1976d2) !important;\n    box-shadow: inset 0 0 0 4px var(--acc-primary-color, #1976d2), inset 0 0 0 6px white, 0 6px 14px rgba(0, 0, 0, 0.24) !important;\n  }\n\n  .acc-toggle-btn[aria-expanded=\"true\"] .acc-toggle-icon svg {\n    opacity: 0;\n  }\n\n  .acc-toggle-btn[aria-expanded=\"true\"]::before,\n  .acc-toggle-btn[aria-expanded=\"true\"]::after {\n    opacity: 1;\n  }\n\n  .acc-toggle-btn[aria-expanded=\"true\"]::before {\n    transform: translate(-50%, -50%) rotate(45deg);\n  }\n\n  .acc-toggle-btn[aria-expanded=\"true\"]::after {\n    transform: translate(-50%, -50%) rotate(-45deg);\n  }\n\n  .acc-violation-bubble {\n    position: absolute;\n    top: -8px;\n    right: -8px;\n    min-width: 24px;\n    height: 24px;\n    border-radius: 12px;\n    font-size: 11px;\n    font-weight: 700;\n    display: flex;
1033\n    align-items: center;\n    justify-content: center;\n    padding: 0 4px;\n    pointer-events: none;\n    z-index: 1;\n    color: #fff;\n    border: none;\n    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);\n  }\n\n  .acc-violation-bubble[data-severity=\"critical\"] {\n    background: #d32f2f;\n    color: #fff;\n  }\n\n  .acc-violation-bubble[data-severity=\"serious\"] {\n    background: #f57c00;\n    color: #fff;\n  }\n\n  .acc-violation-bubble[data-severity=\"moderate\"] {\n    background: #fbc02d;\n    color: #333;\n  }\n\n  .acc-violation-bubble[hidden] {\n    display: none;\n  }\n\n  .acc-toggle-btn:focus {\n    outline: none !important;\n  }\n\n  .acc-toggle-btn:focus-visible {\n    outline: 3px solid var(--acc-primary-color, #1976d2) !important;\n    outline-offset: 2px;\n  }\n\n  body.acc-tts-click-mode :is(h1, h2, h3, h4, h5, h6, p, li, dt, dd, blockquote, figcaption, caption, th, td, div, section):not(.acc-container *):hover {\n    cursor: pointer;\n  }\n\n  .acc-tts-active-block {\n    outline: 2px solid var(--acc-primary-color, #1976d2) !important;\n    outline-offset: 3px !important;\n    border-radius: 4px;\n  }\n  \n  @media (prefers-reduced-motion: reduce) {\n    .acc-toggle-btn {\n      transition: none;\n    }\n\n    .acc-toggle-btn .acc-toggle-icon svg,\n    .acc-toggle-btn::before,\n    .acc-toggle-btn::after {\n      transition: none;\n    }\n\n  }\n";
1034
1035    var reportCSS = ".acc-report-panel {\n  display: none;\n  position: fixed;\n  top: 0;\n  left: 0;\n  right: 0;\n  bottom: 0;\n  z-index: calc(var(--acc-widget-z-index, 100000) + 10);\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n}\n.acc-report-panel.acc-report-visible {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n.acc-report-overlay {\n  position: absolute;\n  top: 0;\n  left: 0;\n  right: 0;\n  bottom: 0;\n  background: rgba(0, 0, 0, 0.6);\n}\n.acc-report-dialog {\n  position: relative;\n  background: #fff;\n  border-radius: 12px;\n  width: 90%;\n  max-width: 800px;\n  max-height: 85vh;\n  display: flex;\n  flex-direction: column;\n  box-shadow: 0 20px 60px rgba(0,0,0,0.3);\n}\n.acc-report-header {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  padding: 16px 20px;\n  border-bottom: 1px solid #e0e0e0;\n}\n.acc-report-title {\n  margin: 0;\n  font-size: 18px;\n  font-weight: 600;\n  color: #1a1a1a;\n}\n.acc-report-close {\n  background: none;\n  border: none;\n  padding: 8px;\n  cursor: pointer;\n  border-radius: 6px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n.acc-report-close:hover {\n  background: #f0f0f0;\n}\n.acc-report-close svg {\n  width: 20px;\n  height: 20px;\n  fill: #666;\n}\n.acc-report-status {\n  padding: 8px 20px;\n  font-size: 14px;\n  color: #666;\n  background: #f8f9fa;\n}\n.acc-report-content {\n  flex: 1;\n  overflow-y: auto;\n  padding: 16px 20px;\n}\n.acc-report-loading {\n  text-align: center;\n  padding: 40px;\n  color: #666;\n}\n.acc-report-error {\n  color: #d32f2f;\n  padding: 20px;\n  text-align: center;\n}\n.acc-report-summary {\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));\n  gap: 12px;\n  margin-bottom: 20px;\n}\n.acc-report-stat {\n  background: #f8f9fa;\n  border-radius: 8px;\n  padding: 16px;\n  text-align: center;\n}\n.acc-report-stat-value {\n  font-size: 28px;\n  font-weight: 700;\n  display: block;\n}\n.acc-report-stat-label {\n  font-size: 12px;\n  color: #666;\n  text-transform: uppercase;\n  margin-top: 4px;\n}\n.acc-report-stat.critical .acc-report-stat-value { color: #d32f2f; }\n.acc-report-stat.serious .acc-report-stat-value { color: #f57c00; }\n.acc-report-stat.moderate .acc-report-stat-value { color: #fbc02d; }\n.acc-report-stat.minor .acc-report-stat-value { color: #7cb342; }\n.acc-report-stat.passed .acc-report-stat-value { color: #43a047; }\n.acc-report-section {\n  margin-bottom: 20px;\n}\n.acc-report-section-title {\n  font-size: 14px;\n  font-weight: 600;\n  color: #333;\n  margin-bottom: 12px;\n  padding-bottom: 8px;\n  border-bottom: 2px solid #e0e0e0;\n}\n.acc-report-violation {\n  background: #fff;\n  border: 1px solid #e0e0e0;\n  border-radius: 8px;\n  margin-bottom: 12px;\n  overflow: hidden;\n}\n.acc-report-violation-header {\n  padding: 12px 16px;\n  background: #f8f9fa;\n  cursor: pointer;\n  display: flex;\n  align-items: center;\n  gap: 12px;\n}\n.acc-report-violation-header:hover {\n  background: #f0f0f0;\n}\n.acc-report-violation-impact {\n  font-size: 11px;\n  font-weight: 600;\n  text-transform: uppercase;\n  padding: 4px 8px;\n  border-radius: 4px;\n  color: #fff;\n}\n.acc-report-violation-impact.critical { background: #d32f2f; }\n.acc-report-violation-impact.serious { background: #f57c00; }\n.acc-report-violation-impact.moderate { background: #fbc02d; color: #333; }\n.acc-report-violation-impact.minor { background: #7cb342; }\n.acc-report-violation-title {\n  flex: 1;\n  font-weight: 500;\n  color: #333;\n}\n.acc-report-violation-count {\n  font-size: 12px;\n  color: #666;\n  background: #e0e0e0;\n  padding: 2px 8px;\n  border-radius: 12px;\n}
1035\n.acc-report-violation-details {\n  display: none;\n  padding: 16px;\n  border-top: 1px solid #e0e0e0;\n}\n.acc-report-violation.expanded .acc-report-violation-details {\n  display: block;\n}\n.acc-report-violation-desc {\n  color: #666;\n  font-size: 14px;\n  margin-bottom: 12px;\n}\n.acc-report-violation-help {\n  font-size: 13px;\n  margin-bottom: 12px;\n}\n.acc-report-violation-help a {\n  color: #1976d2;\n}\n.acc-report-node {\n  background: #f8f9fa;\n  border-radius: 6px;\n  padding: 12px;\n  margin-top: 8px;\n}\n.acc-report-node-html {\n  font-family: monospace;\n  font-size: 12px;\n  background: #263238;\n  color: #80cbc4;\n  padding: 8px 12px;\n  border-radius: 4px;\n  overflow-x: auto;\n  white-space: pre-wrap;\n  word-break: break-all;\n}\n.acc-report-node-fix {\n  margin-top: 8px;\n  font-size: 13px;\n  color: #333;\n}\n.acc-report-node-fix strong {\n  color: #1976d2;\n}\n.acc-report-success {\n  text-align: center;\n  padding: 40px;\n}\n.acc-report-success-icon {\n  width: 64px;\n  height: 64px;\n  background: #43a047;\n  border-radius: 50%;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  margin: 0 auto 16px;\n}\n.acc-report-success-icon svg {\n  width: 32px;\n  height: 32px;\n  fill: #fff;\n}\n.acc-report-footer {\n  padding: 12px 20px;\n  border-top: 1px solid #e0e0e0;\n  text-align: center;\n}\n.acc-report-powered {\n  font-size: 12px;\n}\n@media (max-width: 600px) {\n  .acc-report-dialog {\n    width: 95%;\n    max-height: 90vh;\n  }\n  .acc-report-summary {\n    grid-template-columns: repeat(2, 1fr);\n  }\n}\n";
1036
1037    var readingGuideCSS = ".acc-rg {\n  position: fixed;\n  left: 0;\n  right: 0;\n  width: 100%;\n  pointer-events: none;\n  background-color: rgba(0, 0, 0, 0.4);\n  z-index: calc(var(--acc-widget-z-index, 100000) + 1);\n}\n.acc-rg-top {\n  top: 0;\n}\n.acc-rg-bottom {\n  bottom: 0;\n}\n/* Softer overlay when high contrast is active */\nbody.acc-high-contrast-mode .acc-rg {\n  background-color: rgba(0, 0, 0, 0.25);\n}\n";
1038
1039    var skipLinkCSS = ".acc-skip-link {\n  font-family: inherit;\n  position: fixed;\n  top: 16px;\n  left: 16px;\n  background: #ffffff !important;\n  background: var(--acc-card-bg, #ffffff) !important;\n  color: #222222 !important;\n  color: var(--acc-text-color, #222222) !important;\n  border: 3px solid #1976d2 !important;\n  border: 3px solid var(--acc-primary-color, #1976d2) !important;\n  border-radius: var(--acc-button-border-radius, 0.4rem) !important;\n  padding: 0 !important;\n  z-index: calc(var(--acc-widget-z-index, 100000) + 2) !important;\n  width: 1px;\n  height: 1px;\n  overflow: hidden;\n  clip: rect(0, 0, 0, 0);\n  clip-path: inset(50%);\n  white-space: nowrap;\n  pointer-events: none;\n  font-size: 16px;\n  line-height: 1.2;\n  font-weight: 600;\n  background-clip: padding-box;\n}\n.acc-skip-link:focus,\n.acc-skip-link:active {\n  width: auto;\n  height: auto;\n  overflow: visible;\n  clip: auto;\n  clip-path: none;\n  white-space: normal;\n  padding: 8px 16px !important;\n  pointer-events: auto;\n  outline: 3px solid #1976d2;\n  outline: var(--acc-focus-outline-width, 3px) solid var(--acc-focus-ring-color, #1976d2);\n  outline-offset: 2px;\n  outline-offset: var(--acc-focus-outline-offset, 2px);\n}\n";
1040
1041    var annotationsCSS = ".acc-annotation-layer {\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 0;\n  height: 0;\n  z-index: calc(var(--acc-widget-z-index, 100000) + 5);\n  pointer-events: none;\n}\n\n.acc-annotation-marker {\n  position: absolute;\n  width: 20px;\n  height: 20px;\n  border: none;\n  border-radius: 999px;\n  color: #fff;\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.35);\n  cursor: pointer;\n  pointer-events: auto;\n  transform: translate(-40%, -50%);\n}\n\n.acc-annotation-marker svg {\n  width: 12px;\n  height: 12px;\n  fill: currentColor;\n}\n\n.acc-annotation-marker[data-impact=\"critical\"] {\n  background: #b71c1c;\n}\n\n.acc-annotation-marker[data-impact=\"serious\"] {\n  background: #d84315;\n}\n\n.acc-annotation-marker[data-impact=\"moderate\"] {\n  background: #ef6c00;\n}\n\n.acc-annotation-marker[data-impact=\"minor\"] {\n  background: #1565c0;\n}\n\n.acc-annotation-popup {\n  position: absolute;\n  width: min(320px, 92vw);\n  background: #fff;
1041\n  color: #1a1a1a;\n  border: 1px solid #d7d7d7;\n  border-radius: 10px;\n  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.24);\n  padding: 12px;\n  pointer-events: auto;\n}\n\n.acc-annotation-popup-header {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  gap: 10px;\n}\n\n.acc-annotation-popup-title {\n  margin: 0;\n  font-size: 14px;\n  line-height: 1.3;\n}\n\n.acc-annotation-popup-close {\n  border: 0;\n  background: transparent;\n  padding: 2px;\n  width: 24px;\n  height: 24px;\n  color: #444;\n  cursor: pointer;\n}\n\n.acc-annotation-popup-close svg {\n  width: 20px;\n  height: 20px;\n  fill: currentColor;\n}\n\n.acc-annotation-popup p {\n  margin: 8px 0;\n  font-size: 13px;\n  line-height: 1.45;\n}\n\n.acc-annotation-popup a {\n  color: #0d47a1;\n  font-weight: 600;\n  text-decoration: underline;\n}\n";
1042
1043    const STATIC_STYLE_ID = 'acc-static-styles';
1044    const STATIC_STYLES = [
1045      menuCSS,
1046      widgetCSS,
1047      reportCSS,
1048      readingGuideCSS,
1049      skipLinkCSS,
1050      annotationsCSS
1051    ].join('\n');
1052
1053    /** @typedef {import('./index.js').default} AccessibleWebWidget */
1054
1055    /** @type {{ [methodName: string]: (this: AccessibleWebWidget, ...args: any[]) => any }} */
1056    const styleMethods = {
1057
1058      findElement(selector, parent = document) {
1059        try {
1060          return parent.querySelector(selector);
1061        } catch (e) {
1062          console.warn(`Failed to query selector: ${selector}`, e);
1063          return null;
1064        }
1065      },
1066
1067      injectStyle(id, css) {
1068        if (!css || typeof document === 'undefined') return;
1069        try {
1070          let style = document.getElementById(id) || document.createElement('style');
1071          style.innerHTML = css;
1072          if (!style.id) {
1073            style.id = id;
1074            document.head.appendChild(style);
1075          }
1076        } catch (e) {
1077          console.warn('Error adding stylesheet:', e);
1078        }
1079      },
1080
1081      createCSS(styles) {
1082        let css = "";
1083        if (!styles) return css;
1084        const browserPrefixes = ['-o-', '-ms-', '-moz-', '-webkit-', ''];
1085        const prefixedProperties = ['filter'];
1086        for (let key in styles) {
1087          if (!Object.prototype.hasOwnProperty.call(styles, key)) continue;
1088          let prefixes = prefixedProperties.includes(key) ? browserPrefixes : [""];
1089          prefixes.forEach(prefix => {
1090            css += `${prefix}${key}:${styles[key]} !important;`;
1091          });
1092        }
1093        return css;
1094      },
1095
1096      wrapCSS(selector, childrenSelector, css) {
1097        let output = "";
1098        childrenSelector.forEach(child => {
1099          output += `${selector} ${child}{${css}}`;
1100        });
1101        return output;
1102      },
1103
1104      buildCSS(config) {
1105        if (!config) return "";
1106        let output = "";
1107        output += this.createCSS(config.styles || {});
1108        if (output.length && config.selector) {
1109          output = this.wrapCSS(config.selector, config.childrenSelector || [""], output);
1110        }
1111        output += config.css || "";
1112        return output;
1113      },
1114
1115      applyToolStyle(config) {
1116        let { id = "", enable = false } = config;
1117        let styleId = `acc-${id}`;
1118        if (enable) {
1119          let css = this.buildCSS(config);
1120          this.injectStyle(styleId, css);
1121        } else {
1122          let style = document.getElementById(styleId);
1123          if (style) style.remove();
1124        }
1125        document.documentElement.classList.toggle(styleId, enable);
1126      },
1127
1128      applyThemeVariables() {
1129        if (typeof document === 'undefined') return;
1130
1131        const vars = {
1132          '--acc-primary-color': this.widgetTheme.primaryColor,
1133          '--acc-primary-color-light': this.widgetTheme.primaryColorLight,
1134          '--acc-primary-color-dark': this.widgetTheme.primaryColorDark,
1135          '--acc-bg-color': this.widgetTheme.backgroundColor,
1136          '--acc-text-color': this.widgetTheme.textColor,
1137          '--acc-text-color-inverted': this.widgetTheme.textColorInverted,
1138          '--acc-card-bg': this.widgetTheme.cardBackground,
1139          '--acc-border-color': this.widgetTheme.borderColor,
1140          '--acc-focus-ring-color': this.widgetTheme.focusRingColor,
1141          '--acc-hover-color': this.widgetTheme.hoverColor,
1142          '--acc-active-color': this.widgetTheme.activeColor,
1143          '--acc-border-radius': this.widgetTheme.borderRadius,
1144          '--acc-button-border-radius': this.widgetTheme.buttonBorderRadius,
1145          '--acc-header-height': this.widgetTheme.headerHeight,
1146          '--acc-focus-outline-width': this.widgetTheme.focusBorderWidth,
1147          '--acc-focus-outline-offset': this.widgetTheme.focusOutlineOffset,
1148          '--acc-widget-z-index': String(this.widgetTheme.zIndex),
1149          '--acc-button-size': this.widgetTheme.buttonSize
1150        };
1151
1152        Object.entries(vars).forEach(([key, value]) => {
1153          document.documentElement.style.setProperty(key, value);
1154        });
1155      },
1156
1157      registerStaticStyles() {
1158        if (this.staticStylesRegistered) return;
1159        this.injectStyle(STATIC_STYLE_ID, STATIC_STYLES);
1160        this.staticStylesRegistered = true;
1161      }
1162
1163    };
1164
1165    /** @typedef {import('./index.js').default} AccessibleWebWidget */
1166
1167    const AXE_CORE_VERSION = '4.11.1';
1168    const AXE_CORE_SRC = `https://cdn.jsdelivr.net/npm/axe-core@${AXE_CORE_VERSION}/axe.min.js`;
1169    const AXE_CORE_INTEGRITY = 'sha384-wb3zgvLcZeMFSec08dk7g8K8yDFFAX2uNKVwOUuowwc/wIfE2t6XVUjTEgPrOJCS';
1170    const AXE_RUN_OPTIONS = {
1171      runOnly: ['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'best-practice']
1172    };
1173    const MAX_ANNOTATIONS = 50;
1174    const SYSTEM_PREFERS_REDUCED_MOTION = '(prefers-reduced-motion: reduce)';
1175
1176    /** @type {{ [methodName: string]: (this: AccessibleWebWidget, ...args: any[]) => any }} */
1177    const featureMethods = {
1178
1179      getContrastToggleDisplay(index) {
1180          if (index === 0) {
1181            return { key: 'light-contrast', label: 'Light', icon: this.widgetIcons.lightContrast };
1182          }
1183          if (index === 1) {
1184            return { key: 'dark-contrast', label: 'Dark', icon: this.widgetIcons.darkContrast };
1185          }
1186          return { key: null, label: 'Contrast', icon: this.widgetIcons.contrast };
1187        },
1188
1189      updateContrastToggleButton(button, index) {
1190          if (!button) return;
1191          const display = this.getContrastToggleDisplay(index);
1192          const iconNode = button.querySelector('svg');
1193          if (iconNode) {
1194            iconNode.outerHTML = display.icon;
1195          } else {
1196            button.insertAdjacentHTML('afterbegin', display.icon);
1197          }
1198
1199          const translatedLabel = this.translate(display.label);
1200          const labelNode = button.querySelector('.acc-label');
1201          if (labelNode) {
1202            labelNode.setAttribute('data-acc-text', display.label);
1203            labelNode.innerText = translatedLabel;
1204          }
1205          button.setAttribute('title', translatedLabel);
1206          button.setAttribute('aria-label', translatedLabel);
1207          button.setAttribute('data-contrast-mode', display.key || 'off');
1208        },
1209
1210      getSaturationToggleDisplay(index) {
1211          if (index === 0) {
1212            return { key: 'low-saturation', label: 'Low', icon: this.widgetIcons.lowSaturation };
1213          }
1214          if (index === 1) {
1215            return { key: 'high-saturation', label: 'High', icon: this.widgetIcons.highSaturation };
1216          }
1217          return { key: null, label: 'Saturation', icon: this.widgetIcons.saturation };
1218        },
1219
1220      updateSaturationToggleButton(button, index) {
1221          if (!button) return;
1222          const display = this.getSaturationToggleDisplay(index);
1223          const iconNode = button.querySelector('svg');
1224          if (iconNode) {
1225            iconNode.outerHTML = display.icon;
1226          } else {
1227            button.insertAdjacentHTML('afterbegin', display.icon);
1228          }
1229
1230          const translatedLabel = this.translate(display.label);
1231          const labelNode = button.querySelector('.acc-label');
1232          if (labelNode) {
1233            labelNode.setAttribute('data-acc-text', display.label);
1234            labelNode.innerText = translatedLabel;
1235          }
1236          button.setAttribute('title', translatedLabel);
1237          button.setAttribute('aria-label', translatedLabel);
1238          button.setAttribute('data-saturation-mode', display.key || 'off');
1239        },
1240
1241      ensureSkipLink() {
1242          if (typeof document === 'undefined') return null;
1243          if (this.skipLinkElement && document.body.contains(this.skipLinkElement)) {
1244            this.updateSkipLinkLabel();
1245            return this.skipLinkElement;
1246          }
1247          const existing = document.getElementById('acc-skip-link');
1248          if (existing) {
1249            this.skipLinkElement = existing;
1250            if (!existing.getAttribute('data-acc-text')) {
1251              existing.setAttribute('data-acc-text', 'Skip to accessibility menu');
1252            }
1253            this.updateSkipLinkLabel();
1254            return existing;
1255          }
1256      
1257          if (this._skipLinkTabListenerRegistered) return null;
1258          this._skipLinkTabListenerRegistered = true;
1259
1260          const createAndInsert = (event) => {
1261            if (event.key !== 'Tab') return;
1262            document.removeEventListener('keydown', createAndInsert, true);
1263
1264            const button = document.createElement('a');
1265            button.href = '#';
1266            button.id = 'acc-skip-link';
1267            button.className = 'acc-skip-link';
1268            button.setAttribute('tabindex', '0');
1269            button.setAttribute('data-acc-text', 'Skip to accessibility menu');
1270            button.setAttribute('aria-label', this.translate('Skip to accessibility menu'));
1271            button.textContent = this.translate('Skip to accessibility menu');
1272
1273            button.addEventListener('click', (e) => {
1274              e.preventDefault();
1275              const toggle = this.widgetToggleButton;
1276              if (!toggle) return;
1277
1278              const currentMenu = this.activeMenuContainer || this.menuContainer;
1279              const menuIsVisible = currentMenu && currentMenu.style.display !== 'none';
1280
1281              const focusMenu = () => {
1282                const targetMenu = this.activeMenuContainer || this.menuContainer;
1283                if (!targetMenu) return;
1284                const focusables = this.getFocusableElements(targetMenu);
1285                if (focusables.length) {
1286                  focusables[0].focus();
1287                }
1288              };
1289
1290              if (!menuIsVisible) {
1291                toggle.click();
1292                requestAnimationFrame(focusMenu);
1293              } else {
1294                focusMenu();
1295              }
1296            });
1297
1298            document.body.insertBefore(button, document.body.firstChild);
1299            this.skipLinkElement = button;
1300            this.updateSkipLinkLabel();
1301          };
1302
1303          document.addEventListener('keydown', createAndInsert, true);
1304          return null;
1305        },
1306
1307      updateSkipLinkLabel() {
1308          if (!this.skipLinkElement) return;
1309          const key = this.skipLinkElement.getAttribute('data-acc-text') || 'Skip to accessibility menu';
1310          const label = this.translate(key);
1311          this.skipLinkElement.textContent = label;
1312          this.skipLinkElement.setAttribute('aria-label', label);
1313        },
1314
1315      shouldSkipScaling(element) {
1316          return element.closest('.acc-menu, .acc-container, .acc-widget');
1317        },
1318
1319      collectDirectTextParents(rootElement = document.body) {
1320          const directTextParents = new Set();
1321          if (typeof document === 'undefined' || typeof NodeFilter === 'undefined') {
1322            return directTextParents;
1323          }
1324
1325          const root = rootElement instanceof Element ? rootElement : document.body;
1326          if (!root) return directTextParents;
1327
1328          const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
1329            acceptNode: (node) => {
1330              if (!node || !node.textContent || !node.textContent.trim()) {
1331                return NodeFilter.FILTER_REJECT;
1332              }
1333              const parent = node.parentElement;
1334              if (!parent) return NodeFilter.FILTER_REJECT;
1335              if (this.shouldSkipScaling(parent)) return NodeFilter.FILTER_REJECT;
1336              if (parent.closest('script,style,noscript,textarea,pre,code,svg')) {
1337                return NodeFilter.FILTER_REJECT;
1338              }
1339              if (parent.matches(this.textScaleSelectors)) {
1340                return NodeFilter.FILTER_REJECT;
1341              }
1342              return NodeFilter.FILTER_ACCEPT;
1343            }
1344          });
1345
1346          let currentNode = walker.nextNode();
1347          while (currentNode) {
1348            const parent = currentNode.parentElement;
1349            if (parent) {
1350              directTextParents.add(parent);
1351            }
1352            currentNode = walker.nextNode();
1353          }
1354
1355          return directTextParents;
1356        },
1357
1358      applyScaleToElement(element, multiplier) {
1359          if (
1360            !element ||
1361            !(element instanceof Element) ||
1362            this.shouldSkipScaling(element) ||
1363            element.classList.contains('material-icons') ||
1364            element.classList.contains('fa')
1365          ) {
1366            return;
1367          }
1368          const baseAttr = 'data-acc-baseSize';
1369          if (!element.hasAttribute(baseAttr)) {
1370            const computedSize = parseFloat(window.getComputedStyle(element).fontSize);
1371            if (Number.isNaN(computedSize) || computedSize <= 0) {
1372              return;
1373            }
1374            element.setAttribute(baseAttr, String(computedSize));
1375          }
1376          const baseSize = parseFloat(element.getAttribute(baseAttr));
1377          if (Number.isNaN(baseSize) || baseSize <= 0) {
1378            return;
1379          }
1380          element.style.fontSize = `${baseSize * multiplier}px`;
1381        },
1382
1383      ensureTextScaleObserver() {
1384          if (this.textScaleObserver || !document.body) return;
1385          this.textScaleObserver = new MutationObserver((mutations) => {
1386            if (Math.abs(this.currentTextScaleMultiplier - 1) < 0.001) {
1387              return;
1388            }
1389            const pending = new Set();
1390            mutations.forEach(mutation => {
1391              mutation.addedNodes.forEach(node => {
1392                if (!(node instanceof Element)) return;
1393                if (node.matches && node.matches(this.textScaleSelectors)) {
1394                  pending.add(node);
1395                }
1396                node.querySelectorAll?.(this.textScaleSelectors).forEach(el => pending.add(el));
1397                this.collectDirectTextParents(node).forEach(el => pending.add(el));
1398              });
1399            });
1400            if (!pending.size) return;
1401            pending.forEach(el => this.applyScaleToElement(el, this.currentTextScaleMultiplier));
1402          });
1403          this.textScaleObserver.observe(document.body, { childList: true, subtree: true });
1404        },
1405
1406      disconnectTextScaleObserver() {
1407          if (!this.textScaleObserver) return;
1408          this.textScaleObserver.disconnect();
1409          this.textScaleObserver = null;
1410        },
1411
1412      scaleText(multiply = 1) {
1413          try {
1414            const numericMultiply = Number(multiply);
1415            const resolvedMultiply = Number.isFinite(numericMultiply) && numericMultiply > 0 ? numericMultiply : 1;
1416            const isDefaultScale = Math.abs(resolvedMultiply - 1) < 0.001;
1417            this.currentTextScaleMultiplier = resolvedMultiply;
1418            if (!isDefaultScale) {
1419              this.ensureTextScaleObserver();
1420              const elements = document.querySelectorAll(this.textScaleSelectors);
1421              elements.forEach(el => this.applyScaleToElement(el, resolvedMultiply));
1422              this.collectDirectTextParents(document.body).forEach(el => this.applyScaleToElement(el, resolvedMultiply));
1423            } else {
1424              this.disconnectTextScaleObserver();
1425              const scaledElements = document.querySelectorAll('[data-acc-baseSize]');
1426              scaledElements.forEach(el => {
1427                if (this.shouldSkipScaling(el)) return;
1428                el.style.fontSize = '';
1429                el.removeAttribute('data-acc-baseSize');
1430              });
1431            }
1432          } catch (e) {
1433            console.warn('Error scaling text:', e);
1434          }
1435        },
1436
1437      clampTextScalePercent(percent) {
1438          const min = Number(this.textScaleMinPercent) || 80;
1439          const max = Number(this.textScaleMaxPercent) || 150;
1440          const step = Number(this.textScaleStepPercent) || 5;
1441          const numeric = Number(percent);
1442          if (!Number.isFinite(numeric)) return 100;
1443          const snapped = Math.round((numeric - min) / step) * step + min;
1444          return Math.min(max, Math.max(min, snapped));
1445        },
1446
1447      getTextScalePercent(scaleValue = 1) {
1448          if (scaleValue === false || scaleValue === null || typeof scaleValue === 'undefined') {
1449            return this.clampTextScalePercent(100);
1450          }
1451          const numeric = Number(scaleValue);
1452          if (!Number.isFinite(numeric)) {
1453            return this.clampTextScalePercent(100);
1454          }
1455          if (numeric <= 0) {
1456            return this.clampTextScalePercent(100);
1457          }
1458          const percent = numeric > 10 ? numeric : numeric * 100;
1459          return this.clampTextScalePercent(percent);
1460        },
1461
1462      syncTextScaleControlUI(menu, scaleValue = 1) {
1463          if (!menu || !menu.querySelector) return;
1464          const range = menu.querySelector('.acc-text-scale-range');
1465          const label = menu.querySelector('.acc-text-scale-percent');
1466          const percent = this.getTextScalePercent(scaleValue);
1467          if (range) {
1468            const min = Number(this.textScaleMinPercent) || 80;
1469            const max = Number(this.textScaleMaxPercent) || 150;
1470            const progress = ((percent - min) / (max - min)) * 100;
1471            range.value = String(percent);
1472            range.style.setProperty('--acc-text-scale-progress', `${Math.max(0, Math.min(100, progress))}%`);
1473          }
1474          if (label) {
1475            label.textContent = `${percent}%`;
1476          }
1477        },
1478
1479      setTextScaleFromPercent(percent, options = {}) {
1480          const shouldPersist = options.persist !== false;
1481          const clampedPercent = this.getTextScalePercent(percent);
1482          const multiplier = Number((clampedPercent / 100).toFixed(2));
1483          const exactIndex = this.textScaleValues.indexOf(multiplier);
1484
1485          if (this.multiLevelFeatures['text-scale']) {
1486            this.multiLevelFeatures['text-scale'].currentIndex = exactIndex;
1487          }
1488
1489          if (exactIndex > -1) {
1490            this.textScaleIndex = exactIndex;
1491          } else {
1492            let nearestIndex = 0;
1493            let minDistance = Infinity;
1494            this.textScaleValues.forEach((value, index) => {
1495              const distance = Math.abs(value - multiplier);
1496              if (distance < minDistance) {
1497                minDistance = distance;
1498                nearestIndex = index;
1499              }
1500            });
1501            this.textScaleIndex = nearestIndex;
1502          }
1503
1504          this.scaleText(multiplier);
1505
1506          if (shouldPersist) {
1507            this.updateState({ 'text-scale': multiplier });
1508          }
1509
1510          return multiplier;
1511        },
1512
1513      enableBoldText(enable = false) {
1514          const contentSelector = [
1515            '*',
1516            ':not(.material-icons)',
1517            ':not(.material-icons-outlined)',
1518            ':not(.material-icons-round)',
1519            ':not(.material-symbols-outlined)',
1520            ':not(.material-symbols-rounded)',
1521            ':not(.fa)',
1522            ':not(.fas)',
1523            ':not(.far)',
1524            ':not(.fab)',
1525            ':not(.fa-solid)',
1526            ':not(.fa-regular)',
1527            ':not(.fa-brands)',
1528            ':not(.glyphicon)',
1529            ':not(.icon)',
1530            ':not(.icons)',
1531            ':not([class*="icon-"])',
1532            ':not([data-icon])'
1533          ].join('');
1534          const config = {
1535            id: "bold-text",
1536            selector: "body",
1537            childrenSelector: [contentSelector],
1538            styles: { 'font-weight': '700' },
1539            css: `
1540          .acc-container, .acc-container *, .acc-menu, .acc-menu * {
1541            font-weight: inherit !important;
1542          }
1543          input::placeholder, textarea::placeholder {
1544            font-weight: 700 !important;
1545          }
1546        `
1547          };
1548          this.applyToolStyle({ ...config, enable });
1549        },
1550
1551      adjustLetterSpacing(enable = false) {
1552          const config = {
1553            id: "letter-spacing",
1554            selector: "html",
1555            childrenSelector: ['', '*:not(.material-icons,.acc-menu,.acc-menu *, .acc-widget, .acc-widget *)'],
1556            styles: { 'letter-spacing': '2px' }
1557          };
1558          this.applyToolStyle({ ...config, enable });
1559        },
1560
1561      adjustLineSpacing(enable = false) {
1562          const config = {
1563            id: "line-spacing",
1564            selector: "html",
1565            childrenSelector: ['', '*:not(.material-icons,.acc-menu,.acc-menu *, .acc-widget, .acc-widget *)'],
1566            styles: { 'line-height': '3' }
1567          };
1568          this.applyToolStyle({ ...config, enable });
1569        },
1570
1571      enableLargePointer(enable = false) {
1572          const config = {
1573            id: "large-pointer",
1574            selector: "body",
1575            childrenSelector: ['*'],
1576            styles: {
1577              'cursor': `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='98px' height='98px' viewBox='0 0 48 48'%3E%3Cpath fill='%23FFFFFF' d='M27.8 39.7c-.1 0-.2 0-.4-.1s-.4-.3-.6-.5l-3.7-8.6-4.5 4.2c-.1.2-.3.3-.6.3-.1 0-.3 0-.4-.1-.3-.1-.6-.5-.6-.9V12c0-.4.2-.8.6-.9.1-.1.3-.1.4-.1.2 0 .5.1.7.3l16 15c.3.3.4.7.3 1.1-.1.4-.5.6-.9.7l-6.3.6 3.9 8.5c.1.2.1.5 0 .8-.1.2-.3.5-.5.6l-2.9 1.3c-.2-.2-.4-.2-.5-.2z'/%3E%3Cpath fill='%23212121' d='m18 12 16 15-7.7.7 4.5 9.8-2.9 1.3-4.3-9.9L18 34V12m0-2c-.3 0-.5.1-.8.2-.7.3-1.2 1-1.2 1.8v22c0 .8.5 1.5 1.2 1.8.3.2.6.2.8.2.5 0 1-.2 1.4-.5l3.4-3.2 3.1 7.3c.2.5.6.9 1.1 1.1.2.1.5.1.7.1.3 0 .5-.1.8-.2l2.9-1.3c.5-.2.9-.6 1.1-1.1.2-.5.2-1.1 0-1.5l-3.3-7.2 4.9-.4c.8-.1 1.5-.6 1.7-1.3.3-.7.1-1.6-.5-2.1l-16-15c-.3-.5-.8-.7-1.3-.7z'/%3E%3C/svg%3E") 40 15, auto`
1578            }
1579          };
1580          this.applyToolStyle({ ...config, enable });
1581        },
1582
1583      highlightLinks(enable = false) {
1584        const config = {
1585          id: "highlight-links",
1586          selector: "html",
1587          childrenSelector: [
1588            "a[href]:not(.acc-menu a, .acc-widget a)",
1589            "summary.header__menu-item:not(.acc-menu summary, .acc-widget summary)",
1590            "summary.link:not(.acc-menu summary, .acc-widget summary)",
1591            "summary[role='button']:not(.acc-menu summary, .acc-widget summary)",
1592            "details > summary.list-menu__item:not(.acc-menu summary, .acc-widget summary)",
1593            ".header__menu-item[role='button']:not(.acc-menu *, .acc-widget *)"
1594          ],
1595          styles: { 'outline': `3px solid ${this.widgetTheme.primaryColor}`, 'outline-offset': '2px' }
1596        };
1597        this.applyToolStyle({ ...config, enable });
1598      },
1599
1600      highlightTitles(enable = false) {
1601          const config = {
1602            id: "highlight-title",
1603            selector: "html",
1604            childrenSelector: this.targetSelectors.HEADERS,
1605            styles: { 'outline': `3px solid ${this.widgetTheme.primaryColor}`, 'outline-offset': '2px' }
1606          };
1607          this.applyToolStyle({ ...config, enable });
1608        },
1609
1610      ensureReadableFontLoaded() {
1611          if (this.readableFontLoaded) return;
1612          const existing = document.getElementById('acc-readable-text-font');
1613          if (existing) {
1614            this.readableFontLoaded = true;
1615            return;
1616          }
1617          const style = document.createElement('style');
1618          style.id = 'acc-readable-text-font';
1619          style.textContent = `
1620        @font-face {
1621          font-family: "OpenDyslexic3";
1622          src: url("https://website-widgets.pages.dev/fonts/OpenDyslexic3-Regular.woff") format("woff"),
1623               url("https://website-widgets.pages.dev/fonts/OpenDyslexic3-Regular.ttf") format("truetype");
1624          font-display: swap;
1625        }
1626      `;
1627          document.head.appendChild(style);
1628          this.readableFontLoaded = true;
1629        },
1630
1631      enableReadableText(enable = false) {
1632          const shouldEnable = !!enable;
1633          if (shouldEnable) {
1634            this.ensureReadableFontLoaded();
1635          }
1636          const contentSelector = [
1637            '*',
1638            ':not(.material-icons)',
1639            ':not(.material-icons-outlined)',
1640            ':not(.material-icons-round)',
1641            ':not(.material-symbols-outlined)',
1642            ':not(.material-symbols-rounded)',
1643            ':not(.fa)',
1644            ':not(.fas)',
1645            ':not(.far)',
1646            ':not(.fab)',
1647            ':not(.fa-solid)',
1648            ':not(.fa-regular)',
1649            ':not(.fa-brands)',
1650            ':not(.glyphicon)',
1651            ':not(.icon)',
1652            ':not(.icons)',
1653            ':not([class*="icon-"])',
1654            ':not([data-icon])'
1655          ].join('');
1656          const config = {
1657            id: "readable-text",
1658            selector: "body",
1659            childrenSelector: [contentSelector],
1660            styles: { 
1661              'font-family': '"OpenDyslexic3", "Comic Sans MS", Arial, Helvetica, sans-serif' 
1662            },
1663            css: `
1664          .acc-container, .acc-container *, .acc-menu, .acc-menu * {
1665            font-family: inherit !important;
1666          }
1667          input::placeholder, textarea::placeholder {
1668            font-family: "OpenDyslexic3", "Comic Sans MS", Arial, Helvetica, sans-serif !important;
1669          }
1670        `
1671          };
1672          this.applyToolStyle({ ...config, enable: shouldEnable });
1673        },
1674
1675      pauseMotion(enable = false) {
1676        const config = {
1677          id: "pause-motion",
1678          selector: "html",
1679          childrenSelector: ['*'],
1680          styles: { 'transition': 'none', 'animation-fill-mode': 'forwards', 'animation-iteration-count': '1', 'animation-duration': '.01s' }
1681        };
1682        this.applyToolStyle({ ...config, enable });
1683      },
1684
1685      enableHighContrastMode(enable = false) {
1686        const X = ':not(.acc-container):not(.acc-container *):not(.acc-rg-container):not(.acc-rg-container *)';
1687        const config = {
1688          id: 'high-contrast-mode',
1689          css: `
1690        /* ── Base: force black on white ── */
1691        body.acc-high-contrast-mode *${X} {
1692          color: #000 !important;
1693          background-color: #fff !important;
1694          background-image: none !important;
1695          text-shadow: none !important;
1696          box-shadow: none !important;
1697        }
1698
1699        body.acc-high-contrast-mode {
1700          background: #fff !important;
1701        }
1702
1703        /* ── Borders: solid and visible ── */
1704        body.acc-high-contrast-mode *${X} {
1705          border-color: #000 !important;
1706        }
1707
1708        /* ── Links: underlined, distinct color ── */
1709        body.acc-high-contrast-mode a${X} {
1710          color: #00e !important;
1711          text-decoration: underline !important;
1712        }
1713
1714        body.acc-high-contrast-mode a:visited${X} {
1715          color: #551a8b !important;
1716        }
1717
1718        /* ── Headings: bold black ── */
1719        body.acc-high-contrast-mode :where(h1,h2,h3,h4,h5,h6)${X} {
1720          color: #000 !important;
1721          font-weight: 700 !important;
1722        }
1723
1724        /* ── Images: keep visible, add border ── */
1725        body.acc-high-contrast-mode img${X} {
1726          border: 1px solid #000 !important;
1727        }
1728
1729        /* ── Inputs & buttons: high-contrast borders ── */
1730        body.acc-high-contrast-mode :where(input, textarea, select, button)${X} {
1731          border: 2px solid #000 !important;
1732          background: #fff !important;
1733          color: #000 !important;
1734        }
1735
1736        /* ── Focus rings: thick, high-visibility ── */
1737        body.acc-high-contrast-mode :focus-visible${X} {
1738          outline: 3px solid #000 !important;
1739          outline-offset: 2px !important;
1740        }
1741
1742        /* ── Tables: visible cell borders ── */
1743        body.acc-high-contrast-mode :where(table, th, td)${X} {
1744          border: 1px solid #000 !important;
1745        }
1746      `
1747        };
1748        this.applyToolStyle({ ...config, enable });
1749        document.body?.classList.toggle('acc-high-contrast-mode', !!enable);
1750      },
1751
1752      enableReadingAid(enable = false) {
1753        try {
1754          let container = this.findElement('.acc-rg-container');
1755          const guideHeight = 100; // Height of the clear reading window
1756      
1757          if (enable) {
1758            // Always clean up existing listener first to prevent duplicates
1759            if (window.__accweb__scrollGuide) {
1760              document.removeEventListener('mousemove', window.__accweb__scrollGuide);
1761              delete window.__accweb__scrollGuide;
1762            }
1763      
1764            if (!container) {
1765              // Create container but don't make it visible yet
1766              container = document.createElement('div');
1767              container.setAttribute('class', 'acc-rg-container');
1768              container.setAttribute('aria-hidden', 'true');
1769              container.innerHTML = this.readingAidTemplate;
1770              document.body.prepend(container);
1771            }
1772      
1773            const rgTop = container.querySelector('.acc-rg-top');
1774            const rgBottom = container.querySelector('.acc-rg-bottom');
1775            
1776            // Initially hide the guide completely
1777            rgTop.style.display = 'none';
1778            rgBottom.style.display = 'none';
1779            this.readingAidVisible = false;
1780      
1781            const updateGuidePosition = this.throttle((event) => {
1782              // If first mouse movement after enabling, make the guide visible
1783              if (!this.readingAidVisible) {
1784                rgTop.style.display = 'block';
1785                rgBottom.style.display = 'block';
1786                this.readingAidVisible = true;
1787              }
1788              
1789              // Position the guide at the mouse Y coordinate
1790              const mouseY = event.clientY;
1791              const topHeight = Math.max(0, mouseY - guideHeight / 2);
1792              const bottomHeight = Math.max(0, window.innerHeight - (mouseY + guideHeight / 2));
1793              rgTop.style.height = `${topHeight}px`;
1794              rgBottom.style.height = `${bottomHeight}px`;
1795            }, 16);
1796      
1797            window.__accweb__scrollGuide = updateGuidePosition;
1798            document.addEventListener('mousemove', updateGuidePosition, { passive: true });
1799          } else {
1800            // Reset visibility flag when disabled
1801            this.readingAidVisible = false;
1802            
1803            // Clean up event listener
1804            if (window.__accweb__scrollGuide) {
1805              document.removeEventListener('mousemove', window.__accweb__scrollGuide);
1806              delete window.__accweb__scrollGuide;
1807            }
1808            
1809            // Remove container
1810            if (container) {
1811              container.remove();
1812            }
1813          }
1814        } catch (e) {
1815          console.warn('Error with reading aid:', e);
1816        }
1817      },
1818
1819      loadAxeCore() {
1820        if (this.axeCoreLoaded && window.axe) {
1821          return Promise.resolve(window.axe);
1822        }
1823      
1824        if (this.axeCorePromise) {
1825          return this.axeCorePromise;
1826        }
1827      
1828        this.axeCoreLoading = true;
1829        this.axeCorePromise = new Promise((resolve, reject) => {
1830          let script = document.querySelector('script[data-acc-axe-core="true"]');
1831          let settled = false;
1832          let timeoutId = null;
1833      
1834          const settleSuccess = () => {
1835            if (settled) return;
1836            settled = true;
1837            if (timeoutId) clearTimeout(timeoutId);
1838            if (!window.axe) {
1839              this.axeCoreLoading = false;
1840              this.axeCoreLoaded = false;
1841              this.axeCorePromise = null;
1842              reject(new Error('axe-core loaded but window.axe is unavailable'));
1843              return;
1844            }
1845            this.axeCoreLoading = false;
1846            this.axeCoreLoaded = true;
1847            if (script) {
1848              script.setAttribute('data-acc-axe-core-loaded', 'true');
1849            }
1850            resolve(window.axe);
1851          };
1852      
1853          const settleError = (error) => {
1854            if (settled) return;
1855            settled = true;
1856            if (timeoutId) clearTimeout(timeoutId);
1857            this.axeCoreLoading = false;
1858            this.axeCoreLoaded = false;
1859            this.axeCorePromise = null;
1860            reject(error);
1861          };
1862      
1863          if (window.axe) {
1864            settleSuccess();
1865            return;
1866          }
1867      
1868          if (script && !script.src.includes(`/axe-core@${AXE_CORE_VERSION}/`)) {
1869            script.remove();
1870            script = null;
1871          }
1872
1873          if (!script) {
1874            script = document.createElement('script');
1875            script.src = AXE_CORE_SRC;
1876            script.async = true;
1877            script.integrity = AXE_CORE_INTEGRITY;
1878            script.crossOrigin = 'anonymous';
1879            script.setAttribute('data-acc-axe-core', 'true');
1880            document.head.appendChild(script);
1881          }
1882      
1883          script.addEventListener('load', settleSuccess, { once: true });
1884          script.addEventListener('error', () => settleError(new Error('Failed to load axe-core')), { once: true });
1885          timeoutId = setTimeout(() => {
1886            settleError(new Error('Timed out loading axe-core'));
1887          }, 15000);
1888        });
1889      
1890        return this.axeCorePromise;
1891      },
1892
1893      getAxeRunOptions() {
1894        return { ...AXE_RUN_OPTIONS };
1895      },
1896
1897      getViolationCounts(results = {}) {
1898        const counts = { critical: 0, serious: 0, moderate: 0, minor: 0 };
1899        const violations = Array.isArray(results.violations) ? results.violations : [];
1900        violations.forEach((violation) => {
1901          const impact = violation?.impact;
1902          if (impact && counts[impact] !== undefined) {
1903            counts[impact] += Array.isArray(violation.nodes) ? violation.nodes.length : 0;
1904          }
1905        });
1906        return counts;
1907      },
1908
1909      updateViolationBubble(results = null) {
1910        const bubble = this.violationBubble || this.findElement('.acc-violation-bubble');
1911        if (!bubble) return;
1912
1913        const counts = this.getViolationCounts(results || this.axeScanResults || {});
1914        const devMode = this.isDevMode();
1915
1916        let displayCount = 0;
1917        let severity = '';
1918
1919        if (counts.critical > 0) {
1920          displayCount = counts.critical;
1921          severity = 'critical';
1922        } else if (devMode && counts.serious > 0) {
1923          displayCount = counts.serious;
1924          severity = 'serious';
1925        } else if (devMode && counts.moderate > 0) {
1926          displayCount = counts.moderate;
1927          severity = 'moderate';
1928        }
1929
1930        if (displayCount <= 0) {
1931          bubble.textContent = '';
1932          bubble.hidden = true;
1933          bubble.removeAttribute('data-severity');
1934          return;
1935        }
1936
1937        bubble.hidden = false;
1938        bubble.dataset.severity = severity;
1939        bubble.textContent = displayCount > 99 ? '99+' : String(displayCount);
1940      },
1941
1942      async runBackgroundAxeScan({ force = false } = {}) {
1943        if (!force && this.axeScanResults) {
1944          this.updateViolationBubble(this.axeScanResults);
1945          return this.axeScanResults;
1946        }
1947
1948        if (!force && this.axeScanPromise) {
1949          return this.axeScanPromise;
1950        }
1951
1952        this.axeScanPromise = (async () => {
1953          try {
1954            const axe = await this.loadAxeCore();
1955            const results = await axe.run(document, this.getAxeRunOptions());
1956            this.axeScanResults = results;
1957            this.updateViolationBubble(results);
1958            return results;
1959          } catch (error) {
1960            this.updateViolationBubble({ violations: [] });
1961            throw error;
1962          } finally {
1963            this.axeScanPromise = null;
1964          }
1965        })();
1966
1967        return this.axeScanPromise;
1968      },
1969
1970      ensureMediaQuery(query) {
1971        if (!query || typeof window === 'undefined' || typeof window.matchMedia !== 'function') {
1972          return null;
1973        }
1974        if (!this.systemPreferenceMediaQueries) {
1975          this.systemPreferenceMediaQueries = {};
1976        }
1977        if (!this.systemPreferenceMediaQueries[query]) {
1978          this.systemPreferenceMediaQueries[query] = window.matchMedia(query);
1979        }
1980        return this.systemPreferenceMediaQueries[query];
1981      },
1982
1983      applySystemMotionPreference(shouldEnable) {
1984        this.loadConfig();
1985        if (this.hasExplicitStatePreference('pause-motion')) {
1986          return false;
1987        }
1988
1989        const currentValue = !!this.retrieveState('pause-motion');
1990        if (currentValue === shouldEnable && this.isSystemControlledPreference('pause-motion')) {
1991          return false;
1992        }
1993
1994        this.updateState({ 'pause-motion': shouldEnable }, { source: 'system' });
1995        return true;
1996      },
1997
1998      detectSystemPreferences() {
1999        if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return;
2000
2001        const reducedMotionQuery = this.ensureMediaQuery(SYSTEM_PREFERS_REDUCED_MOTION);
2002        const shouldPauseMotion = !!reducedMotionQuery?.matches;
2003        const motionChanged = this.applySystemMotionPreference(shouldPauseMotion);
2004
2005        if (motionChanged) {
2006          this.applyEnhancements();
2007        }
2008      },
2009
2010      clearSystemPreferenceListeners() {
2011        const listeners = Array.isArray(this.systemPreferenceListeners) ? this.systemPreferenceListeners : [];
2012        listeners.forEach((remove) => {
2013          if (typeof remove === 'function') {
2014            remove();
2015          }
2016        });
2017        this.systemPreferenceListeners = [];
2018      },
2019
2020      setupMediaQueryListeners() {
2021        this.clearSystemPreferenceListeners();
2022        if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return;
2023
2024        const reducedMotionQuery = this.ensureMediaQuery(SYSTEM_PREFERS_REDUCED_MOTION);
2025
2026        const listen = (mediaQuery, handler) => {
2027          if (!mediaQuery || typeof handler !== 'function') return;
2028          if (typeof mediaQuery.addEventListener === 'function') {
2029            mediaQuery.addEventListener('change', handler);
2030            this.systemPreferenceListeners.push(() => mediaQuery.removeEventListener('change', handler));
2031          } else if (typeof mediaQuery.addListener === 'function') {
2032            mediaQuery.addListener(handler);
2033            this.systemPreferenceListeners.push(() => mediaQuery.removeListener(handler));
2034          }
2035        };
2036
2037        const onReducedMotionChange = (event) => {
2038          const changed = this.applySystemMotionPreference(!!event.matches);
2039          if (changed) {
2040            this.applyEnhancements();
2041          }
2042        };
2043
2044        listen(reducedMotionQuery, onReducedMotionChange);
2045      },
2046
2047      async runAccessibilityReport() {
2048        // Create or get report panel
2049        let panel = this.findElement('.acc-report-panel');
2050      
2051        if (!panel) {
2052          panel = this.createReportPanel();
2053          document.body.appendChild(panel);
2054        }
2055      
2056        this.openReportPanel(panel);
2057      
2058        const contentArea = this.findElement('.acc-report-content', panel);
2059        const statusArea = this.findElement('.acc-report-status', panel);
2060      
2061        // Show loading state
2062        statusArea.textContent = this.translate('Loading...');
2063        contentArea.innerHTML = `<div class="acc-report-loading">${this.translate('Analyzing page...')}</div>`;
2064      
2065        try {
2066          const results = await this.runBackgroundAxeScan({ force: true });
2067
2068          this.displayReportResults(panel, results);
2069      
2070        } catch (error) {
2071          contentArea.innerHTML = `<div class="acc-report-error">Error: ${error.message}</div>`;
2072          statusArea.textContent = '';
2073        }
2074      },
2075
2076      getReportFocusableElements(panel) {
2077        if (!panel) return [];
2078        const dialog = this.findElement('.acc-report-dialog', panel);
2079        return this.getFocusableElements(dialog || panel);
2080      },
2081
2082      openReportPanel(panel) {
2083        if (!panel) return;
2084      
2085        this.reportPreviousFocus = document.activeElement && typeof document.activeElement.focus === 'function'
2086          ? document.activeElement
2087          : null;
2088      
2089        panel.classList.add('acc-report-visible');
2090        panel.setAttribute('aria-hidden', 'false');
2091      
2092        const dialog = this.findElement('.acc-report-dialog', panel);
2093        const focusTarget = dialog || panel;
2094        if (!focusTarget.hasAttribute('tabindex')) {
2095          focusTarget.setAttribute('tabindex', '-1');
2096        }
2097      
2098        if (this.reportKeyListener) {
2099          document.removeEventListener('keydown', this.reportKeyListener, true);
2100        }
2101      
2102        this.reportKeyListener = (event) => {
2103          if (!panel.classList.contains('acc-report-visible')) return;
2104      
2105          if (event.key === 'Escape' || event.key === 'Esc') {
2106            event.preventDefault();
2107            event.stopPropagation();
2108            this.closeReportPanel();
2109            return;
2110          }
2111      
2112          if (event.key !== 'Tab') return;
2113      
2114          const focusables = this.getReportFocusableElements(panel);
2115          if (!focusables.length) {
2116            event.preventDefault();
2117            focusTarget.focus();
2118            return;
2119          }
2120      
2121          const first = focusables[0];
2122          const last = focusables[focusables.length - 1];
2123          const active = document.activeElement;
2124          const outsidePanel = !panel.contains(active);
2125      
2126          if (event.shiftKey) {
2127            if (active === first || outsidePanel) {
2128              event.preventDefault();
2129              last.focus();
2130            }
2131            return;
2132          }
2133      
2134          if (active === last || outsidePanel) {
2135            event.preventDefault();
2136            first.focus();
2137          }
2138        };
2139      
2140        document.addEventListener('keydown', this.reportKeyListener, true);
2141      
2142        requestAnimationFrame(() => {
2143          const focusables = this.getReportFocusableElements(panel);
2144          if (focusables.length) {
2145            focusables[0].focus();
2146            return;
2147          }
2148          focusTarget.focus();
2149        });
2150      },
2151
2152      createReportPanel() {
2153        const panel = document.createElement('div');
2154        panel.className = 'acc-report-panel acc-container';
2155        panel.setAttribute('role', 'dialog');
2156        panel.setAttribute('aria-modal', 'true');
2157        panel.setAttribute('aria-hidden', 'true');
2158        panel.setAttribute('aria-labelledby', 'acc-report-title');
2159      
2160        panel.innerHTML = `
2161      <div class="acc-report-overlay"></div>
2162      <div class="acc-report-dialog">
2163        <div class="acc-report-header">
2164          <h2 id="acc-report-title" class="acc-report-title">${this.translate('Accessibility Report')}</h2>
2165          <button type="button" class="acc-report-close" aria-label="${this.translate('Close')}">${this.widgetIcons.close}</button>
2166        </div>
2167        <div class="acc-report-status"></div>
2168        <div class="acc-report-content"></div>
2169        <div class="acc-report-footer">
2170          <span class="acc-report-powered">Powered by axe-core</span>
2171        </div>
2172      </div>
2173    `;
2174      
2175        // Close handlers
2176        const closeBtn = panel.querySelector('.acc-report-close');
2177        const overlay = panel.querySelector('.acc-report-overlay');
2178      
2179        closeBtn.addEventListener('click', () => this.closeReportPanel());
2180        overlay.addEventListener('click', () => this.closeReportPanel());
2181      
2182        return panel;
2183      },
2184
2185      displayReportResults(panel, results) {
2186        const contentArea = this.findElement('.acc-report-content', panel);
2187        const statusArea = this.findElement('.acc-report-status', panel);
2188      
2189        const violations = results.violations || [];
2190        const passes = results.passes || [];
2191        const incomplete = results.incomplete || [];
2192      
2193        // Count by severity
2194        const counts = { critical: 0, serious: 0, moderate: 0, minor: 0 };
2195        violations.forEach(v => {
2196          if (counts[v.impact] !== undefined) {
2197            counts[v.impact] += v.nodes.length;
2198          }
2199        });
2200      
2201        const totalViolations = Object.values(counts).reduce((a, b) => a + b, 0);
2202      
2203        statusArea.textContent = totalViolations > 0
2204          ? `${totalViolations} ${this.translate('Violations Found')}`
2205          : this.translate('No Issues Found');
2206      
2207        let html = `
2208      <div class="acc-report-summary">
2209        <div class="acc-report-stat critical">
2210          <span class="acc-report-stat-value">${counts.critical}</span>
2211          <span class="acc-report-stat-label">${this.translate('Critical')}</span>
2212        </div>
2213        <div class="acc-report-stat serious">
2214          <span class="acc-report-stat-value">${counts.serious}</span>
2215          <span class="acc-report-stat-label">${this.translate('Serious')}</span>
2216        </div>
2217        <div class="acc-report-stat moderate">
2218          <span class="acc-report-stat-value">${counts.moderate}</span>
2219          <span class="acc-report-stat-label">${this.translate('Moderate')}</span>
2220        </div>
2221        <div class="acc-report-stat minor">
2222          <span class="acc-report-stat-value">${counts.minor}</span>
2223          <span class="acc-report-stat-label">${this.translate('Minor')}</span>
2224        </div>
2225        <div class="acc-report-stat passed">
2226          <span class="acc-report-stat-value">${passes.length}</span>
2227          <span class="acc-report-stat-label">${this.translate('Passed Tests')}</span>
2228        </div>
2229      </div>
2230    `;
2231      
2232        if (violations.length === 0) {
2233          html += `
2234        <div class="acc-report-success">
2235          <div class="acc-report-success-icon">
2236            <svg viewBox="0 0 24 24"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z"/></svg>
2237          </div>
2238          <h3>${this.translate('No Issues Found')}</h3>
2239        </div>
2240      `;
2241        } else {
2242          html += `<div class="acc-report-section">
2243        <div class="acc-report-section-title">${this.translate('Violations Found')} (${violations.length})</div>
2244      `;
2245      
2246          // Sort by severity
2247          const severityOrder = { critical: 0, serious: 1, moderate: 2, minor: 3 };
2248          violations.sort((a, b) => (severityOrder[a.impact] || 4) - (severityOrder[b.impact] || 4));
2249      
2250          violations.forEach((violation, index) => {
2251            html += `
2252          <div class="acc-report-violation" data-index="${index}">
2253            <div class="acc-report-violation-header">
2254              <span class="acc-report-violation-impact ${violation.impact}">${this.translate(this.capitalizeFirst(violation.impact))}</span>
2255              <span class="acc-report-violation-title">${this.escapeHtml(violation.help)}</span>
2256              <span class="acc-report-violation-count">${violation.nodes.length} ${this.translate('Element')}${violation.nodes.length > 1 ? 's' : ''}</span>
2257            </div>
2258            <div class="acc-report-violation-details">
2259              <p class="acc-report-violation-desc">${this.escapeHtml(violation.description)}</p>
2260              <p class="acc-report-violation-help">
2261                <a href="${violation.helpUrl}" target="_blank" rel="noopener">${this.translate('How to Fix')} →</a>
2262              </p>
2263              ${violation.nodes.slice(0, 5).map(node => `
2264                <div class="acc-report-node">
2265                  <div class="acc-report-node-html">${this.escapeHtml(node.html)}</div>
2266                  ${node.failureSummary ? `<div class="acc-report-node-fix"><strong>${this.translate('Issue')}:</strong> ${this.escapeHtml(node.failureSummary)}</div>` : ''}
2267                </div>
2268              `).join('')}
2269              ${violation.nodes.length > 5 ? `<p style="color:#666;font-size:13px;margin-top:12px;">... and ${violation.nodes.length - 5} more elements</p>` : ''}
2270            </div>
2271          </div>
2272        `;
2273          });
2274      
2275          html += `</div>`;
2276        }
2277      
2278        if (incomplete.length > 0) {
2279          html += `
2280        <div class="acc-report-section">
2281          <div class="acc-report-section-title">${this.translate('Items Need Review')} (${incomplete.length})</div>
2282          <p style="color:#666;font-size:14px;">These items require manual verification.</p>
2283        </div>
2284      `;
2285        }
2286      
2287        contentArea.innerHTML = html;
2288      
2289        // Add click handlers for expandable violations
2290        contentArea.querySelectorAll('.acc-report-violation-header').forEach(header => {
2291          header.addEventListener('click', () => {
2292            header.parentElement.classList.toggle('expanded');
2293          });
2294        });
2295      },
2296
2297      capitalizeFirst(str) {
2298        if (!str) return '';
2299        return str.charAt(0).toUpperCase() + str.slice(1);
2300      },
2301
2302      escapeHtml(str) {
2303        if (!str) return '';
2304        const div = document.createElement('div');
2305        div.textContent = str;
2306        return div.innerHTML;
2307      },
2308
2309      closeReportPanel() {
2310        const panel = this.findElement('.acc-report-panel');
2311        if (panel) {
2312          panel.classList.remove('acc-report-visible');
2313          panel.setAttribute('aria-hidden', 'true');
2314        }
2315      
2316        if (this.reportKeyListener) {
2317          document.removeEventListener('keydown', this.reportKeyListener, true);
2318          this.reportKeyListener = null;
2319        }
2320      
2321        const focusTarget = this.reportPreviousFocus;
2322        this.reportPreviousFocus = null;
2323        if (focusTarget && typeof focusTarget.focus === 'function') {
2324          focusTarget.focus();
2325        }
2326      },
2327
2328      concealImages(enable = false) {
2329        const styleId = `acc-hide-images`;
2330        const existingStyle = document.getElementById(styleId);
2331        if (existingStyle) existingStyle.remove();
2332        document.documentElement.classList.toggle(styleId, enable);
2333        if (enable) {
2334          const css = `
2335        body > *:not(.acc-container) img,
2336        body > *:not(.acc-container) picture,
2337        body > *:not(.acc-container) svg:not(.acc-container svg),
2338        body > *:not(.acc-container) video,
2339        body > *:not(.acc-container) iframe,
2340        body > *:not(.acc-container) canvas,
2341        body > *:not(.acc-container) .video,
2342        body > *:not(.acc-container) .image {
2343          display: none !important;
2344        }
2345      `;
2346          this.injectStyle(styleId, css);
2347        }
2348      },
2349
2350      supportsSpeechSynthesis() {
2351        if (typeof window === 'undefined') return false;
2352        return (
2353          'speechSynthesis' in window &&
2354          typeof window.SpeechSynthesisUtterance !== 'undefined'
2355        );
2356      },
2357
2358      supportsTextToSpeech() {
2359        return this.supportsSpeechSynthesis();
2360      },
2361
2362      normalizeSpeechLanguage(languageCode = 'en') {
2363        const code = String(languageCode || 'en').toLowerCase();
2364        const languageMap = {
2365          en: 'en-US',
2366          it: 'it-IT',
2367          fr: 'fr-FR',
2368          de: 'de-DE',
2369          es: 'es-ES',
2370          ru: 'ru-RU',
2371          pl: 'pl-PL',
2372          ro: 'ro-RO',
2373          nl: 'nl-NL',
2374          uk: 'uk-UA'
2375        };
2376        return languageMap[code] || code;
2377      },
2378
2379      getNativeTtsRate() {
2380        const configured = Number(this.nativeTtsConfig?.rate);
2381        if (!Number.isFinite(configured)) return 1;
2382        return Math.min(2, Math.max(0.5, configured));
2383      },
2384
2385      getNativeTtsPitch() {
2386        const configured = Number(this.nativeTtsConfig?.pitch);
2387        if (!Number.isFinite(configured)) return 1;
2388        return Math.min(2, Math.max(0, configured));
2389      },
2390
2391      isElementVisibleForTts(element) {
2392        if (!(element instanceof Element)) return false;
2393        const style = window.getComputedStyle(element);
2394        if (style.display === 'none' || style.visibility === 'hidden') {
2395          return false;
2396        }
2397        const rect = element.getBoundingClientRect();
2398        return rect.width > 0 && rect.height > 0;
2399      },
2400
2401      isTtsExcludedElement(element, { allowLandmarkRegions = false } = {}) {
2402        if (!(element instanceof Element)) return true;
2403        if (element.closest('.acc-container')) return true;
2404        if (element.closest('script,style,noscript,template')) return true;
2405        if (element.closest('[aria-hidden="true"]')) return true;
2406        if (
2407          !allowLandmarkRegions &&
2408          element.closest(
2409            'nav,header,footer,aside,form,dialog,[role="navigation"],[role="complementary"],[role="search"],[role="menu"],[role="dialog"],[role="alert"],[aria-live]'
2410          )
2411        ) {
2412          return true;
2413        }
2414        return false;
2415      },
2416
2417      normalizeReadableText(text = '') {
2418        return String(text)
2419          .replace(/\s+/g, ' ')
2420          .replace(/[ \t]+([,.;!?])/g, '$1')
2421          .trim();
2422      },
2423
2424      getTtsCandidateRoots() {
2425        const selectors = [
2426          'article',
2427          'main',
2428          '[role="main"]',
2429          '.content',
2430          '.post',
2431          '.entry-content',
2432          '#content'
2433        ];
2434        const roots = [];
2435        const seen = new Set();
2436
2437        selectors.forEach((selector) => {
2438          document.querySelectorAll(selector).forEach((element) => {
2439            if (!(element instanceof Element)) return;
2440            if (this.isTtsExcludedElement(element) || !this.isElementVisibleForTts(element)) return;
2441            if (seen.has(element)) return;
2442            seen.add(element);
2443            roots.push(element);
2444          });
2445        });
2446
2447        return roots;
2448      },
2449
2450      getPrimaryContentRoot() {
2451        if (typeof document === 'undefined') return null;
2452        const candidates = this.getTtsCandidateRoots();
2453        if (!candidates.length) {
2454          const explicitCandidates = Array.from(
2455            document.querySelectorAll('main,article,[role="main"],#content,.content,.post,.entry-content')
2456          ).filter((element) =>
2457            element instanceof Element &&
2458            !this.isTtsExcludedElement(element) &&
2459            this.isElementVisibleForTts(element)
2460          );
2461
2462          if (explicitCandidates.length) {
2463            let explicitBest = explicitCandidates[0];
2464            let explicitBestScore = -1;
2465            explicitCandidates.forEach((candidate) => {
2466              const textLength = this.normalizeReadableText(candidate.innerText || candidate.textContent || '').length;
2467              const rect = candidate.getBoundingClientRect();
2468              const score = textLength + (rect.width * rect.height * 0.0025);
2469              if (score > explicitBestScore) {
2470                explicitBestScore = score;
2471                explicitBest = candidate;
2472              }
2473            });
2474            return explicitBest;
2475          }
2476
2477          if (document.body) {
2478            const topLevelCandidates = Array.from(document.body.children).filter((element) =>
2479              element instanceof Element &&
2480              !element.classList.contains('acc-container') &&
2481              this.isElementVisibleForTts(element)
2482            );
2483
2484            if (topLevelCandidates.length) {
2485              let best = topLevelCandidates[0];
2486              let bestScore = -1;
2487              topLevelCandidates.forEach((candidate) => {
2488                const textLength = this.normalizeReadableText(candidate.innerText || candidate.textContent || '').length;
2489                const rect = candidate.getBoundingClientRect();
2490                const score = textLength + (rect.width * rect.height * 0.0025);
2491                if (score > bestScore) {
2492                  bestScore = score;
2493                  best = candidate;
2494                }
2495              });
2496              return best;
2497            }
2498          }
2499
2500          return document.body;
2501        }
2502
2503        let bestRoot = candidates[0];
2504        let bestScore = -1;
2505        candidates.forEach((candidate) => {
2506          const blocks = this.extractReadableBlocks(candidate);
2507          const score = blocks.join(' ').length;
2508          if (score > bestScore) {
2509            bestScore = score;
2510            bestRoot = candidate;
2511          }
2512        });
2513        return bestRoot;
2514      },
2515
2516      extractReadableBlocks(root) {
2517        if (!(root instanceof Element)) return [];
2518        const blockSelector = 'h1,h2,h3,h4,h5,h6,p,li,dt,dd,blockquote,figcaption,caption,th,td';
2519        const blocks = [];
2520        root.querySelectorAll(blockSelector).forEach((element) => {
2521          if (!(element instanceof Element)) return;
2522          if (this.isTtsExcludedElement(element) || !this.isElementVisibleForTts(element)) return;
2523          const text = this.normalizeReadableText(element.innerText || element.textContent || '');
2524          if (text.length < 15) return;
2525          blocks.push(text);
2526        });
2527        return blocks;
2528      },
2529
2530      getReadableContent() {
2531        if (typeof document === 'undefined') return '';
2532        const candidateRoots = this.getTtsCandidateRoots();
2533        let bestBlocks = [];
2534        let bestScore = -1;
2535
2536        candidateRoots.forEach((root) => {
2537          const blocks = this.extractReadableBlocks(root);
2538          if (!blocks.length) return;
2539          const characterCount = blocks.join(' ').length;
2540          const score = (blocks.length * 60) + characterCount;
2541          if (score > bestScore) {
2542            bestScore = score;
2543            bestBlocks = blocks;
2544          }
2545        });
2546
2547        if (!bestBlocks.length && document.body) {
2548          bestBlocks = this.extractReadableBlocks(document.body);
2549        }
2550
2551        if (!bestBlocks.length && document.body) {
2552          const fallback = this.normalizeReadableText(document.body.innerText || document.body.textContent || '');
2553          return fallback.slice(0, 30000);
2554        }
2555
2556        return bestBlocks.join('\n\n').slice(0, 30000);
2557      },
2558
2559      splitLongSpeechSegment(segment, maxLength = 240) {
2560        if (!segment) return [];
2561        if (segment.length <= maxLength) return [segment];
2562
2563        const parts = [];
2564        let remaining = segment;
2565        while (remaining.length > maxLength) {
2566          let splitIndex = remaining.lastIndexOf(',', maxLength);
2567          if (splitIndex < Math.floor(maxLength * 0.5)) {
2568            splitIndex = remaining.lastIndexOf(' ', maxLength);
2569          }
2570          if (splitIndex < Math.floor(maxLength * 0.5)) {
2571            splitIndex = maxLength;
2572          }
2573          parts.push(remaining.slice(0, splitIndex).trim());
2574          remaining = remaining.slice(splitIndex).trim();
2575        }
2576        if (remaining.length) {
2577          parts.push(remaining);
2578        }
2579        return parts.filter(Boolean);
2580      },
2581
2582      buildSpeechQueue(text = '') {
2583        const normalized = this.normalizeReadableText(text);
2584        if (!normalized) return [];
2585
2586        const paragraphCandidates = text
2587          .split(/\n{2,}/)
2588          .map((paragraph) => this.normalizeReadableText(paragraph))
2589          .filter(Boolean);
2590
2591        const paragraphs = paragraphCandidates.length ? paragraphCandidates : [normalized];
2592        const queue = [];
2593
2594        paragraphs.forEach((paragraph) => {
2595          const sentences = paragraph.split(/(?<=[.!?])\s+/).filter(Boolean);
2596          const sentenceList = sentences.length ? sentences : [paragraph];
2597          let chunk = '';
2598
2599          sentenceList.forEach((sentence) => {
2600            const next = chunk ? `${chunk} ${sentence}` : sentence;
2601            if (next.length <= 240) {
2602              chunk = next;
2603              return;
2604            }
2605            if (chunk) {
2606              queue.push(...this.splitLongSpeechSegment(chunk, 240));
2607            }
2608            chunk = sentence;
2609          });
2610
2611          if (chunk) {
2612            queue.push(...this.splitLongSpeechSegment(chunk, 240));
2613          }
2614        });
2615
2616        return queue.slice(0, 300);
2617      },
2618
2619      resolveSpeechVoice(languageCode) {
2620        if (!this.supportsSpeechSynthesis()) return null;
2621        const synth = window.speechSynthesis;
2622        const voices = synth.getVoices?.() || [];
2623        if (!voices.length) return null;
2624
2625        const preferredVoiceName = String(this.nativeTtsConfig?.preferredVoiceName || '').trim().toLowerCase();
2626        if (preferredVoiceName) {
2627          const byExactName = voices.find((voice) => String(voice.name || '').trim().toLowerCase() === preferredVoiceName);
2628          if (byExactName) return byExactName;
2629          const byNameContains = voices.find((voice) => String(voice.name || '').toLowerCase().includes(preferredVoiceName));
2630          if (byNameContains) return byNameContains;
2631        }
2632
2633        const configuredVoiceLang = String(this.nativeTtsConfig?.preferredVoiceLang || '').trim();
2634        const targetLanguage = configuredVoiceLang || languageCode;
2635        const normalized = this.normalizeSpeechLanguage(targetLanguage).toLowerCase();
2636        const primary = normalized.split('-')[0];
2637        const localExact = voices.find((voice) => (
2638          voice.localService &&
2639          String(voice.lang || '').toLowerCase() === normalized
2640        ));
2641        const exact = voices.find((voice) => String(voice.lang || '').toLowerCase() === normalized);
2642        const localByPrimary = voices.find((voice) => (
2643          voice.localService &&
2644          String(voice.lang || '').toLowerCase().startsWith(primary)
2645        ));
2646        const byPrimary = voices.find((voice) => String(voice.lang || '').toLowerCase().startsWith(primary));
2647        const defaultVoice = voices.find((voice) => voice.default);
2648        return localExact || exact || localByPrimary || byPrimary || defaultVoice || voices[0];
2649      },
2650
2651      getSpeechTargetFromEvent(event) {
2652        const target = event?.target;
2653        if (!(target instanceof Element)) return null;
2654        if (target.closest('.acc-container')) return null;
2655
2656        const block = target.closest('h1,h2,h3,h4,h5,h6,p,li,dt,dd,blockquote,figcaption,caption,th,td,div,section');
2657        if (!(block instanceof Element)) return null;
2658        if (this.isTtsExcludedElement(block, { allowLandmarkRegions: true }) || !this.isElementVisibleForTts(block)) return null;
2659
2660        const text = this.normalizeReadableText(block.innerText || block.textContent || '');
2661        if (text.length < 2) return null;
2662
2663        return {
2664          element: block,
2665          text: text.slice(0, 30000)
2666        };
2667      },
2668
2669      setActiveSpeechTarget(element = null) {
2670        if (this.ttsActiveTarget && this.ttsActiveTarget !== element) {
2671          this.ttsActiveTarget.classList.remove('acc-tts-active-block');
2672        }
2673        this.ttsActiveTarget = element;
2674        if (this.ttsActiveTarget) {
2675          this.ttsActiveTarget.classList.add('acc-tts-active-block');
2676        }
2677      },
2678
2679      startTtsClickMode() {
2680        if (typeof document === 'undefined' || this.ttsClickListener) return;
2681        this.ttsClickListener = (event) => {
2682          this.handleTtsClick(event);
2683        };
2684        document.addEventListener('click', this.ttsClickListener, true);
2685        document.body?.classList.add('acc-tts-click-mode');
2686      },
2687
2688      stopTtsClickMode() {
2689        if (typeof document === 'undefined') return;
2690        if (this.ttsClickListener) {
2691          document.removeEventListener('click', this.ttsClickListener, true);
2692          this.ttsClickListener = null;
2693        }
2694        document.body?.classList.remove('acc-tts-click-mode');
2695        this.setActiveSpeechTarget(null);
2696      },
2697
2698      handleTtsClick(event) {
2699        if (!this.retrieveState('text-to-speech')) return;
2700        const target = this.getSpeechTargetFromEvent(event);
2701        if (!target) return;
2702
2703        this.setActiveSpeechTarget(target.element);
2704        this.ttsTextCache = target.text;
2705        this.startSpeechPlayback({ restart: true });
2706      },
2707
2708      ensureTtsQueue() {
2709        const text = this.ttsTextCache || this.getReadableContent();
2710        if (!text) {
2711          this.ttsQueue = [];
2712          this.ttsQueueIndex = 0;
2713          return false;
2714        }
2715        this.ttsTextCache = text;
2716        this.ttsQueue = this.buildSpeechQueue(text);
2717        this.ttsQueueIndex = 0;
2718        return this.ttsQueue.length > 0;
2719      },
2720
2721      speakNextTtsChunk(sessionId) {
2722        if (!this.supportsSpeechSynthesis()) return;
2723        if (sessionId !== this.ttsSessionId) return;
2724
2725        if (this.ttsQueueIndex >= this.ttsQueue.length) {
2726          this.ttsStatus = 'stopped';
2727          this.setActiveSpeechTarget(null);
2728          return;
2729        }
2730
2731        const synth = window.speechSynthesis;
2732        const chunk = this.ttsQueue[this.ttsQueueIndex];
2733        if (!chunk) {
2734          this.ttsQueueIndex += 1;
2735          this.speakNextTtsChunk(sessionId);
2736          return;
2737        }
2738
2739        const utterance = new window.SpeechSynthesisUtterance(chunk);
2740        utterance.lang = this.normalizeSpeechLanguage(this.loadConfig().lang || 'en');
2741        if (!this.ttsVoice || this.ttsVoice.lang?.toLowerCase() !== utterance.lang.toLowerCase()) {
2742          this.ttsVoice = this.resolveSpeechVoice(utterance.lang);
2743        }
2744        if (this.ttsVoice) {
2745          utterance.voice = this.ttsVoice;
2746        }
2747        utterance.rate = this.getNativeTtsRate();
2748        utterance.pitch = this.getNativeTtsPitch();
2749
2750        utterance.onstart = () => {
2751          if (sessionId !== this.ttsSessionId) return;
2752          this.ttsStatus = 'reading';
2753        };
2754        utterance.onpause = () => {
2755          if (sessionId !== this.ttsSessionId) return;
2756          this.ttsStatus = 'paused';
2757        };
2758        utterance.onresume = () => {
2759          if (sessionId !== this.ttsSessionId) return;
2760          this.ttsStatus = 'reading';
2761        };
2762        utterance.onend = () => {
2763          if (sessionId !== this.ttsSessionId) return;
2764          this.ttsQueueIndex += 1;
2765          this.speakNextTtsChunk(sessionId);
2766        };
2767        utterance.onerror = () => {
2768          if (sessionId !== this.ttsSessionId) return;
2769          this.ttsStatus = 'stopped';
2770          this.setActiveSpeechTarget(null);
2771        };
2772
2773        this.ttsUtterance = utterance;
2774        synth.speak(utterance);
2775      },
2776
2777      startNativeSpeechPlayback({ restart = false } = {}) {
2778        if (!this.supportsSpeechSynthesis()) return;
2779        const synth = window.speechSynthesis;
2780
2781        if (!restart && synth.paused) {
2782          synth.resume();
2783          this.ttsStatus = 'reading';
2784          return;
2785        }
2786
2787        const hasQueue = restart || !this.ttsQueue.length || this.ttsQueueIndex >= this.ttsQueue.length
2788          ? this.ensureTtsQueue()
2789          : true;
2790        if (!hasQueue) {
2791          this.stopSpeech();
2792          return;
2793        }
2794
2795        const sessionId = this.ttsSessionId + 1;
2796        this.ttsSessionId = sessionId;
2797        synth.cancel();
2798        this.ttsStatus = 'reading';
2799        this.speakNextTtsChunk(sessionId);
2800      },
2801
2802      startSpeechPlayback({ restart = false } = {}) {
2803        this.startNativeSpeechPlayback({ restart });
2804      },
2805
2806      pauseSpeech() {
2807        if (!this.supportsSpeechSynthesis()) return;
2808        const synth = window.speechSynthesis;
2809        if (synth.speaking && !synth.paused) {
2810          synth.pause();
2811          this.ttsStatus = 'paused';
2812        }
2813      },
2814
2815      resumeSpeech() {
2816        if (!this.supportsSpeechSynthesis()) return;
2817        const synth = window.speechSynthesis;
2818        if (synth.paused) {
2819          synth.resume();
2820          this.ttsStatus = 'reading';
2821          return;
2822        }
2823        this.startSpeechPlayback({ restart: false });
2824      },
2825
2826      stopSpeech() {
2827        const synth = this.supportsSpeechSynthesis() ? window.speechSynthesis : null;
2828        this.ttsSessionId += 1;
2829        if (synth && (synth.speaking || synth.paused)) {
2830          synth.cancel();
2831        }
2832        this.ttsUtterance = null;
2833        this.ttsQueueIndex = 0;
2834        this.ttsStatus = 'stopped';
2835        this.setActiveSpeechTarget(null);
2836      },
2837
2838      enableTextToSpeech(enable = false) {
2839        if (!this.supportsTextToSpeech()) return;
2840        const isUserToggle = this.userInitiatedToggleKey === 'text-to-speech';
2841
2842        if (!enable) {
2843          if (isUserToggle) this.announceTtsState(false);
2844          this.stopSpeech();
2845          this.stopTtsClickMode();
2846          return;
2847        }
2848
2849        const synth = window.speechSynthesis;
2850        if (synth?.getVoices) {
2851          synth.getVoices();
2852        }
2853
2854        this.startTtsClickMode();
2855        this.ttsStatus = 'idle';
2856        if (isUserToggle) this.announceTtsState(true);
2857      },
2858
2859      announceTtsState(active) {
2860        if (!this.supportsTextToSpeech()) return;
2861        const synth = window.speechSynthesis;
2862        if (!synth) return;
2863        const msg = active
2864          ? this.translate('Text to Speech On')
2865          : this.translate('Text to Speech Off');
2866        synth.cancel();
2867        const utterance = new SpeechSynthesisUtterance(msg);
2868        const lang = this.normalizeSpeechLanguage(this.loadConfig().lang || 'en');
2869        utterance.lang = lang;
2870        const voice = this.resolveSpeechVoice(lang);
2871        if (voice) {
2872          utterance.voice = voice;
2873        }
2874        utterance.rate = this.getNativeTtsRate();
2875        utterance.pitch = this.getNativeTtsPitch();
2876        synth.speak(utterance);
2877      },
2878
2879      ensureAnnotationLayer() {
2880        if (this.annotationLayer && document.body.contains(this.annotationLayer)) {
2881          return this.annotationLayer;
2882        }
2883        const layer = document.createElement('div');
2884        layer.className = 'acc-annotation-layer acc-container';
2885        layer.setAttribute('aria-hidden', 'true');
2886        document.body.appendChild(layer);
2887        this.annotationLayer = layer;
2888        return layer;
2889      },
2890
2891      resolveAnnotationTarget(selector) {
2892        if (!selector || typeof selector !== 'string') return null;
2893        try {
2894          const element = document.querySelector(selector);
2895          if (!element || element.closest('.acc-container')) return null;
2896          return element;
2897        } catch {
2898          return null;
2899        }
2900      },
2901
2902      buildAnnotationEntries(results = {}) {
2903        const violations = Array.isArray(results.violations) ? results.violations : [];
2904        const entries = [];
2905        const seen = new Set();
2906
2907        violations.forEach((violation) => {
2908          const nodes = Array.isArray(violation.nodes) ? violation.nodes : [];
2909          nodes.forEach((node) => {
2910            const targets = Array.isArray(node.target) ? node.target : [];
2911            const selector = targets.find((target) => typeof target === 'string' && target.trim().length);
2912            if (!selector) return;
2913            const key = `${selector}::${violation.id || violation.help || ''}`;
2914            if (seen.has(key)) return;
2915
2916            const element = this.resolveAnnotationTarget(selector);
2917            if (!element) return;
2918            seen.add(key);
2919            entries.push({
2920              selector,
2921              element,
2922              impact: violation.impact || 'minor',
2923              title: violation.help || this.translate('Issue'),
2924              description: violation.description || '',
2925              helpUrl: violation.helpUrl || '',
2926              failureSummary: node.failureSummary || ''
2927            });
2928          });
2929        });
2930
2931        return entries.slice(0, MAX_ANNOTATIONS);
2932      },
2933
2934      createAnnotationMarker(annotation) {
2935        const marker = document.createElement('button');
2936        marker.type = 'button';
2937        marker.className = 'acc-annotation-marker';
2938        marker.dataset.impact = annotation.impact || 'minor';
2939        marker.setAttribute('aria-label', annotation.title);
2940        marker.title = annotation.title;
2941        marker.innerHTML = '';
2942
2943        marker.addEventListener('click', (event) => {
2944          event.preventDefault();
2945          event.stopPropagation();
2946          this.showAnnotationPopup(annotation, marker);
2947        });
2948
2949        return marker;
2950      },
2951
2952      positionAnnotationPopup(popup, marker) {
2953        if (!popup || !marker) return;
2954        const markerRect = marker.getBoundingClientRect();
2955        const popupWidth = popup.offsetWidth || 300;
2956        const popupHeight = popup.offsetHeight || 180;
2957        const viewportWidth = window.innerWidth;
2958        const viewportHeight = window.innerHeight;
2959        const gap = 10;
2960
2961        let left = window.scrollX + markerRect.left + gap;
2962        let top = window.scrollY + markerRect.bottom + gap;
2963
2964        if (left + popupWidth > window.scrollX + viewportWidth - gap) {
2965          left = window.scrollX + viewportWidth - popupWidth - gap;
2966        }
2967        if (left < window.scrollX + gap) {
2968          left = window.scrollX + gap;
2969        }
2970
2971        if (top + popupHeight > window.scrollY + viewportHeight - gap) {
2972          top = window.scrollY + markerRect.top - popupHeight - gap;
2973        }
2974        if (top < window.scrollY + gap) {
2975          top = window.scrollY + gap;
2976        }
2977
2978        popup.style.left = `${left}px`;
2979        popup.style.top = `${top}px`;
2980      },
2981
2982      clearAnnotationPopup() {
2983        if (this.annotationPopup) {
2984          this.annotationPopup.remove();
2985          this.annotationPopup = null;
2986        }
2987      },
2988
2989      showAnnotationPopup(annotation, marker) {
2990        if (!annotation || !marker || !this.annotationLayer) return;
2991        this.clearAnnotationPopup();
2992
2993        const popup = document.createElement('div');
2994        popup.className = 'acc-annotation-popup';
2995        popup.innerHTML = `
2996      <div class="acc-annotation-popup-header">
2997        <h3 class="acc-annotation-popup-title">${this.escapeHtml(annotation.title)}</h3>
2998        <button type="button" class="acc-annotation-popup-close" aria-label="${this.translate('Close')}">${this.widgetIcons.close}</button>
2999      </div>
3000      <p><strong>${this.translate(this.capitalizeFirst(annotation.impact))}</strong></p>
3001      <p>${this.escapeHtml(annotation.description)}</p>
3002      ${annotation.failureSummary ? `<p><strong>${this.translate('Issue')}:</strong> ${this.escapeHtml(annotation.failureSummary)}</p>` : ''}
3003      ${annotation.helpUrl ? `<p><a href="${annotation.helpUrl}" target="_blank" rel="noopener">${this.translate('How to Fix')} →</a></p>` : ''}
3004    `;
3005        popup.__accMarker = marker;
3006
3007        const closeButton = popup.querySelector('.acc-annotation-popup-close');
3008        closeButton?.addEventListener('click', () => this.clearAnnotationPopup());
3009
3010        this.annotationLayer.appendChild(popup);
3011        this.annotationPopup = popup;
3012        this.positionAnnotationPopup(popup, marker);
3013      },
3014
3015      positionAnnotations() {
3016        if (!Array.isArray(this.annotationItems) || !this.annotationItems.length) return;
3017
3018        this.annotationItems.forEach((item) => {
3019          if (!item?.target || !item?.marker) return;
3020          if (!document.contains(item.target)) {
3021            item.marker.hidden = true;
3022            return;
3023          }
3024
3025          const rect = item.target.getBoundingClientRect();
3026          const outOfView = (
3027            rect.width <= 0 ||
3028            rect.height <= 0 ||
3029            rect.bottom < 0 ||
3030            rect.top > window.innerHeight ||
3031            rect.right < 0 ||
3032            rect.left > window.innerWidth
3033          );
3034
3035          if (outOfView) {
3036            item.marker.hidden = true;
3037            return;
3038          }
3039
3040          item.marker.hidden = false;
3041          item.marker.style.top = `${window.scrollY + rect.top + Math.min(16, rect.height / 2)}px`;
3042          item.marker.style.left = `${window.scrollX + rect.right}px`;
3043        });
3044
3045        if (this.annotationPopup?.__accMarker && !this.annotationPopup.__accMarker.hidden) {
3046          this.positionAnnotationPopup(this.annotationPopup, this.annotationPopup.__accMarker);
3047        }
3048      },
3049
3050      enableAnnotations(enable = false) {
3051        if (!enable) {
3052          this.disableAnnotations();
3053          return;
3054        }
3055
3056        const requestId = ++this.annotationRequestId;
3057        this.disableAnnotations({ cancelPending: false });
3058        const layer = this.ensureAnnotationLayer();
3059
3060        this.runBackgroundAxeScan()
3061          .then((results) => {
3062            if (requestId !== this.annotationRequestId || !this.retrieveState('annotations')) {
3063              return;
3064            }
3065            const annotations = this.buildAnnotationEntries(results);
3066            this.annotationItems = annotations.map((annotation) => {
3067              const marker = this.createAnnotationMarker(annotation);
3068              layer.appendChild(marker);
3069              return {
3070                marker,
3071                target: annotation.element,
3072                data: annotation
3073              };
3074            });
3075
3076            if (!this.annotationRepositionHandler) {
3077              this.annotationRepositionHandler = this.throttle(() => this.positionAnnotations(), 80);
3078            }
3079            window.addEventListener('scroll', this.annotationRepositionHandler, { passive: true });
3080            window.addEventListener('resize', this.annotationRepositionHandler, { passive: true });
3081
3082            if (!this.annotationOutsideHandler) {
3083              this.annotationOutsideHandler = (event) => {
3084                if (!this.annotationPopup) return;
3085                const clickedPopup = this.annotationPopup.contains(event.target);
3086                const clickedMarker = event.target?.closest?.('.acc-annotation-marker');
3087                if (!clickedPopup && !clickedMarker) {
3088                  this.clearAnnotationPopup();
3089                }
3090              };
3091            }
3092            document.addEventListener('click', this.annotationOutsideHandler, true);
3093
3094            this.positionAnnotations();
3095          })
3096          .catch((error) => {
3097            console.warn('Failed to render annotations:', error);
3098          });
3099      },
3100
3101      disableAnnotations({ cancelPending = true } = {}) {
3102        if (cancelPending) {
3103          this.annotationRequestId += 1;
3104        }
3105        this.clearAnnotationPopup();
3106
3107        if (this.annotationOutsideHandler) {
3108          document.removeEventListener('click', this.annotationOutsideHandler, true);
3109        }
3110        if (this.annotationRepositionHandler) {
3111          window.removeEventListener('scroll', this.annotationRepositionHandler);
3112          window.removeEventListener('resize', this.annotationRepositionHandler);
3113        }
3114
3115        if (this.annotationLayer) {
3116          this.annotationLayer.remove();
3117          this.annotationLayer = null;
3118        }
3119
3120        this.annotationItems = [];
3121      },
3122
3123
3124      clearSimpleLayoutDomMutations() {
3125        if (this.simpleLayoutRoot) {
3126          this.simpleLayoutRoot.classList.remove('acc-simple-layout-root');
3127          this.simpleLayoutRoot = null;
3128        }
3129
3130        if (Array.isArray(this.simpleLayoutHiddenElements)) {
3131          this.simpleLayoutHiddenElements.forEach((element) => {
3132            if (element && element.classList) {
3133              element.classList.remove('acc-simple-layout-hidden');
3134            }
3135          });
3136        }
3137        this.simpleLayoutHiddenElements = [];
3138      },
3139
3140      applySimpleLayoutDomMutations() {
3141        const root = this.getPrimaryContentRoot();
3142        if (!root || !document.body) return;
3143
3144        this.simpleLayoutRoot = root;
3145        root.classList.add('acc-simple-layout-root');
3146
3147        const hiddenElements = [];
3148        Array.from(document.body.children).forEach((child) => {
3149          if (!(child instanceof Element)) return;
3150          if (child.classList.contains('acc-container')) return;
3151          if (child === root || child.contains(root)) return;
3152          child.classList.add('acc-simple-layout-hidden');
3153          hiddenElements.push(child);
3154        });
3155
3156        const clutterSelectors = [
3157          'aside',
3158          'nav',
3159          'form',
3160          'footer',
3161          '[role="complementary"]',
3162          '[role="search"]',
3163          '[role="contentinfo"]',
3164          '[aria-hidden="true"]',
3165          '[class*="cookie"]',
3166          '[id*="cookie"]',
3167          '[class*="banner"]',
3168          '[id*="banner"]',
3169          '[class*="popup"]',
3170          '[id*="popup"]',
3171          '[class*="modal"]',
3172          '[id*="modal"]',
3173          '[class*="advert"]',
3174          '[id*="advert"]',
3175          '[class*="ads"]',
3176          '[id*="ads"]',
3177          '[class*="sidebar"]',
3178          '[id*="sidebar"]',
3179          '[class*="social"]',
3180          '[id*="social"]',
3181          '[class*="share"]',
3182          '[id*="share"]',
3183          '[class*="newsletter"]',
3184          '[id*="newsletter"]',
3185          '[class*="related"]',
3186          '[id*="related"]',
3187          '[class*="comment"]',
3188          '[id*="comment"]',
3189          '[class*="footer"]',
3190          '[id*="footer"]',
3191          '[class*="promo"]',
3192          '[id*="promo"]'
3193        ].join(',');
3194
3195        root.querySelectorAll(clutterSelectors).forEach((element) => {
3196          if (!(element instanceof Element)) return;
3197          if (element.closest('.acc-container')) return;
3198          if (element === root) return;
3199          element.classList.add('acc-simple-layout-hidden');
3200          hiddenElements.push(element);
3201        });
3202
3203        this.simpleLayoutHiddenElements = hiddenElements;
3204      },
3205
3206      enableSimpleLayout(enable = false) {
3207        const S = 'body.acc-simple-layout-enabled';
3208        const R = `${S} .acc-simple-layout-root`;
3209        const X = ':not(.acc-container):not(.acc-container *)';
3210        const config = {
3211          id: 'simple-layout',
3212          css: `
3213        /* ── Body & root container ── */
3214        ${S} {
3215          background: #fff !important;
3216        }
3217
3218        ${S} .acc-simple-layout-hidden {
3219          display: none !important;
3220        }
3221
3222        ${R} {
3223          max-width: 72ch !important;
3224          margin: 0 auto !important;
3225          padding: clamp(20px, 4vw, 40px) 20px !important;
3226          position: relative !important;
3227          border-radius: 0 !important;
3228          box-shadow: none !important;
3229        }
3230
3231        /* ── Universal decoration strip ── */
3232        ${R} :where(*)${X} {
3233          background-color: transparent !important;
3234          background-image: none !important;
3235          box-shadow: none !important;
3236          text-shadow: none !important;
3237          border-color: transparent !important;
3238        }
3239
3240        /* ── Layout linearization ── */
3241        ${R} :where(div, section, article, header, main, footer, figure, figcaption, details, summary, hgroup, search)${X} {
3242          display: block !important;
3243          position: static !important;
3244          float: none !important;
3245          transform: none !important;
3246          columns: auto !important;
3247          column-count: auto !important;
3248          width: auto !important;
3249          min-width: 0 !important;
3250          max-width: 100% !important;
3251          margin-left: 0 !important;
3252          margin-right: 0 !important;
3253          padding-left: 0 !important;
3254          padding-right: 0 !important;
3255        }
3256
3257        /* ── Color reset ── */
3258        ${R} :where(h1, h2, h3, h4, h5, h6)${X} {
3259          color: #111 !important;
3260        }
3261
3262        ${R} :where(p, li, dt, dd, td, th, span, blockquote, figcaption, label, summary, details)${X} {
3263          color: #222 !important;
3264        }
3265
3266        ${R} :where(a)${X} {
3267          color: #1a0dab !important;
3268        }
3269
3270        ${R} :where(a:visited)${X} {
3271          color: #681da8 !important;
3272        }
3273
3274        /* ── Typography ── */
3275        ${R} :where(*)${X} {
3276          font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !important;
3277        }
3278
3279        ${R} :where(p, li, dt, dd, blockquote, figcaption, td, th, label, summary)${X} {
3280          font-size: clamp(1rem, 0.95rem + 0.25vw, 1.125rem) !important;
3281          line-height: 1.8 !important;
3282          letter-spacing: 0.01em !important;
3283          max-width: 70ch !important;
3284        }
3285
3286        ${R} :where(h1)${X} {
3287          font-size: 2em !important;
3288          line-height: 1.2 !important;
3289          margin: 0.67em 0 !important;
3290          font-weight: 700 !important;
3291        }
3292
3293        ${R} :where(h2)${X} {
3294          font-size: 1.5em !important;
3295          line-height: 1.25 !important;
3296          margin: 0.83em 0 !important;
3297          font-weight: 700 !important;
3298        }
3299
3300        ${R} :where(h3)${X} {
3301          font-size: 1.25em !important;
3302          line-height: 1.3 !important;
3303          margin: 1em 0 !important;
3304          font-weight: 600 !important;
3305        }
3306
3307        ${R} :where(h4, h5, h6)${X} {
3308          font-size: 1.1em !important;
3309          line-height: 1.35 !important;
3310          margin: 1em 0 !important;
3311          font-weight: 600 !important;
3312        }
3313
3314        /* ── Decorative images hidden ── */
3315        ${R} :where(img[role="presentation"], img[alt=""], img:not([alt]), svg[aria-hidden="true"])${X} {
3316          display: none !important;
3317        }
3318
3319        /* ── Meaningful borders restored ── */
3320        ${R} :where(hr)${X} {
3321          border: none !important;
3322          border-top: 1px solid #d1d5db !important;
3323          margin: 1.5em 0 !important;
3324        }
3325
3326        ${R} :where(blockquote)${X} {
3327          border-left: 4px solid #d1d5db !important;
3328          padding-left: 1em !important;
3329          margin-left: 0 !important;
3330          font-style: italic !important;
3331        }
3332
3333        ${R}
3333 :where(table)${X} {
3334          border-collapse: collapse !important;
3335          max-width: 100% !important;
3336          overflow-x: auto !important;
3337          display: table !important;
3338        }
3339
3340        ${R} :where(th, td)${X} {
3341          border: 1px solid #d1d5db !important;
3342          padding: 8px 12px !important;
3343          text-align: left !important;
3344        }
3345
3346        ${R} :where(th)${X} {
3347          font-weight: 600 !important;
3348          background: #f8f9fa !important;
3349        }
3350
3351        /* ── Lists ── */
3352        ${R} :where(ul, ol)${X} {
3353          padding-left: 1.5em !important;
3354          margin: 0.75em 0 !important;
3355        }
3356
3357        ${R} :where(li)${X} {
3358          display: list-item !important;
3359          margin: 0.25em 0 !important;
3360        }
3361
3362        /* ── Code blocks ── */
3363        ${R} :where(pre)${X} {
3364          background: #f6f8fa !important;
3365          border-radius: 6px !important;
3366          padding: 1em !important;
3367          overflow-x: auto !important;
3368          max-width: 100% !important;
3369        }
3370
3371        ${R} :where(code, kbd, samp)${X} {
3372          font-family: ui-monospace, "SFMono-Regular", "SF Mono", Menlo, Consolas, "Liberation Mono", monospace !important;
3373          font-size: 0.9em !important;
3374        }
3375
3376        ${R} :where(code):not(pre code)${X} {
3377          background: #f0f2f5 !important;
3378          padding: 0.15em 0.4em !important;
3379          border-radius: 3px !important;
3380        }
3381
3382        /* ── Empty wrapper collapse ── */
3383        ${R} :where(div:empty)${X} {
3384          display: none !important;
3385        }
3386
3387        /* ── Media ── */
3388        ${R} :where(img, video, iframe)${X} {
3389          max-width: 100% !important;
3390          height: auto !important;
3391          border-radius: 4px !important;
3392        }
3393      `
3394        };
3395
3396        this.applyToolStyle({ ...config, enable });
3397        document.body?.classList.toggle('acc-simple-layout-enabled', !!enable);
3398
3399        this.clearSimpleLayoutDomMutations();
3400        if (enable) {
3401          this.applySimpleLayoutDomMutations();
3402        }
3403      },
3404
3405      applyEnhancements() {
3406          const { states } = this.loadConfig();
3407          // Handle font size scaling
3408          const hasTextScaleState = !!(states && Object.prototype.hasOwnProperty.call(states, 'text-scale'));
3409          if (hasTextScaleState) {
3410            const storedScale = states['text-scale'] === false ? 1 : states['text-scale'];
3411            const appliedScale = this.setTextScaleFromPercent(storedScale, { persist: false });
3412            this.syncTextScaleControlUI(document.querySelector('.acc-menu'), appliedScale);
3413          } else {
3414            this.textScaleIndex = 0;
3415            if (this.multiLevelFeatures['text-scale']) {
3416              this.multiLevelFeatures['text-scale'].currentIndex = -1;
3417            }
3418            this.scaleText(1);
3419            this.syncTextScaleControlUI(document.querySelector('.acc-menu'), 1);
3420          }
3421          // Apply other enhancements
3422          this.concealImages(states && states['hide-images']);
3423          this.highlightTitles(states && states['highlight-title']);
3424          this.highlightLinks(states && states['highlight-links']);
3425          this.adjustLetterSpacing(states && states['letter-spacing']);
3426          this.adjustLineSpacing(states && states['line-spacing']);
3427          this.enableBoldText(states && states['bold-text']);
3428          this.enableReadableText(states && states['readable-text']);
3429          this.enableReadingAid(states && states['reading-aid']);
3430          this.pauseMotion(states && states['pause-motion']);
3431          this.enableLargePointer(states && states['large-pointer']);
3432          this.enableHighContrastMode(states && states['high-contrast-mode']);
3433          this.enableAnnotations(states && states['annotations']);
3434          this.enableTextToSpeech(states && states['text-to-speech']);
3435          this.enableSimpleLayout(states && states['simple-layout']);
3436        },
3437
3438      isColorFilterKey(key) {
3439          return Array.isArray(this.colorFilterKeys) && this.colorFilterKeys.includes(key);
3440        },
3441
3442      getActiveColorFilterKey(states = this.widgetConfig.states) {
3443          if (!this.colorFilterKeys || !this.colorFilterKeys.length) return null;
3444          if (!states) return this.activeColorFilterKey || null;
3445          for (const key of this.colorFilterKeys) {
3446            if (states[key]) {
3447              return key;
3448            }
3449          }
3450          return null;
3451        },
3452
3453      setColorFilterUI(menu, activeKey = null) {
3454          if (!menu || !menu.querySelectorAll) return;
3455          const contrastFeature = this.multiLevelFeatures?.['contrast-toggle'];
3456          const contrastButton = menu.querySelector('.acc-btn[data-key="contrast-toggle"]');
3457          if (contrastFeature) {
3458            const contrastIndex = contrastFeature.values.indexOf(activeKey);
3459            contrastFeature.currentIndex = contrastIndex;
3460            this.updateContrastToggleButton(contrastButton, contrastIndex);
3461            if (contrastButton) {
3462              const isContrastActive = contrastIndex >= 0;
3463              contrastButton.classList.toggle('acc-selected', isContrastActive);
3464              contrastButton.setAttribute('aria-pressed', isContrastActive ? 'true' : 'false');
3465              const indicator = contrastButton.querySelector('.acc-progress-indicator[data-feature="contrast-toggle"]');
3466              if (indicator) {
3467                const dots = indicator.querySelectorAll('.acc-progress-dot');
3468                dots.forEach((dot, index) => {
3469                  dot.classList.toggle('active', index === contrastIndex);
3470                });
3471              }
3472            }
3473          }
3474          const saturationFeature = this.multiLevelFeatures?.['saturation-toggle'];
3475          const saturationButton = menu.querySelector('.acc-btn[data-key="saturation-toggle"]');
3476          if (saturationFeature) {
3477            const saturationIndex = saturationFeature.values.indexOf(activeKey);
3478            saturationFeature.currentIndex = saturationIndex;
3479            this.updateSaturationToggleButton(saturationButton, saturationIndex);
3480            if (saturationButton) {
3481              const isSaturationActive = saturationIndex >= 0;
3482              saturationButton.classList.toggle('acc-selected', isSaturationActive);
3483              saturationButton.setAttribute('aria-pressed', isSaturationActive ? 'true' : 'false');
3484              const indicator = saturationButton.querySelector('.acc-progress-indicator[data-feature="saturation-toggle"]');
3485              if (indicator) {
3486                const dots = indicator.querySelectorAll('.acc-progress-dot');
3487                dots.forEach((dot, index) => {
3488                  dot.classList.toggle('active', index === saturationIndex);
3489                });
3490              }
3491            }
3492          }
3493          this.colorFilterKeys.forEach(filterKey => {
3494            const button = menu.querySelector(`.acc-btn[data-key="${filterKey}"]`);
3495            if (!button) return;
3496            const isActive = filterKey === activeKey;
3497            button.classList.toggle('acc-selected', isActive);
3498            button.setAttribute('aria-pressed', isActive ? 'true' : 'false');
3499          });
3500        },
3501
3502      updateColorFilterState(activeKey = null, source = 'user') {
3503          if (!this.colorFilterKeys || !this.colorFilterKeys.length) {
3504            this.activeColorFilterKey = null;
3505            return;
3506          }
3507          const states = this.widgetConfig.states || {};
3508          const payload = {};
3509          let requiresUpdate = false;
3510          this.colorFilterKeys.forEach(filterKey => {
3511            const rawCurrent = states[filterKey];
3512            const shouldBeActive = activeKey === filterKey;
3513            if (shouldBeActive) {
3514              if (rawCurrent !== true) {
3515                payload[filterKey] = true;
3516                requiresUpdate = true;
3517              }
3518            } else if (rawCurrent) {
3519              payload[filterKey] = false;
3520              requiresUpdate = true;
3521            }
3522          });
3523          if (requiresUpdate) {
3524            this.updateState(payload, { source });
3525          }
3526          this.activeColorFilterKey = activeKey;
3527        },
3528
3529      computeFilteredBodyColor(filterKey) {
3530          const bg = window.getComputedStyle(document.body).backgroundColor;
3531          const m = bg.match(/rgba?\(\s*(\d+),\s*(\d+),\s*(\d+)/);
3532          if (!m) return null;
3533          let r = +m[1], g = +m[2], b = +m[3];
3534          const clamp = (v) => Math.min(255, Math.max(0, Math.round(v)));
3535
3536          switch (filterKey) {
3537            case 'invert-colors':
3538              r = 255 - r; g = 255 - g; b = 255 - b;
3539              break;
3540            case 'dark-contrast': {
3541              // filter: contrast(150%) brightness(0.8) — applied left to right
3542              r = 128 + (r - 128) * 1.5; g = 128 + (g - 128) * 1.5; b = 128 + (b - 128) * 1.5;
3543              r *= 0.8; g *= 0.8; b *= 0.8;
3544              break;
3545            }
3546            case 'light-contrast': {
3547              // filter: contrast(125%) brightness(1.2)
3548              r = 128 + (r - 128) * 1.25; g = 128 + (g - 128) * 1.25; b = 128 + (b - 128) * 1.25;
3549              r *= 1.2; g *= 1.2; b *= 1.2;
3550              break;
3551            }
3552            case 'low-saturation': {
3553              // filter: saturate(50%) — use standard saturate matrix with s=0.5
3554              const s = 0.5;
3555              const nr = (0.213 + 0.787 * s) * r + (0.715 - 0.715 * s) * g + (0.072 - 0.072 * s) * b;
3556              const ng = (0.213 - 0.213 * s) * r + (0.715 + 0.285 * s) * g + (0.072 - 0.072 * s) * b;
3557              const nb = (0.213 - 0.213 * s) * r + (0.715 - 0.715 * s) * g + (0.072 + 0.928 * s) * b;
3558              r = nr; g = ng; b = nb;
3559              break;
3560            }
3561            case 'high-saturation': {
3562              // filter: saturate(200%) — use standard saturate matrix with s=2
3563              const s2 = 2;
3564              const hr = (0.213 + 0.787 * s2) * r + (0.715 - 0.715 * s2) * g + (0.072 - 0.072 * s2) * b;
3565              const hg = (0.213 - 0.213 * s2) * r + (0.715 + 0.285 * s2) * g + (0.072 - 0.072 * s2) * b;
3566              const hb = (0.213 - 0.213 * s2) * r + (0.715 - 0.715 * s2) * g + (0.072 + 0.928 * s2) * b;
3567              r = hr; g = hg; b = hb;
3568              break;
3569            }
3570            default:
3571              return null;
3572          }
3573          r = clamp(r); g = clamp(g); b = clamp(b);
3574          // Skip if the result is the same as the original (no visible change)
3575          if (r === +m[1] && g === +m[2] && b === +m[3]) return null;
3576          return `rgb(${r},${g},${b})`;
3577        },
3578
3579      applyVisualFilters() {
3580          const { states } = this.loadConfig();
3581          const activeKey = this.getActiveColorFilterKey(states);
3582          this.activeColorFilterKey = activeKey;
3583      
3584          if (!activeKey) {
3585            const style = document.getElementById('acc-filter-style');
3586            if (style) style.remove();
3587            return;
3588          }
3589          const filter = this.visualFilters[activeKey];
3590          if (!filter) {
3591            const style = document.getElementById('acc-filter-style');
3592            if (style) style.remove();
3593            return;
3594          }
3595          const adjustedFilter = {
3596            ...filter,
3597            selector: filter.selector || 'body > *:not(.acc-container):not(.acc-rg-container):not(#acc-skip-link)'
3598          };
3599          let css = this.buildCSS(adjustedFilter);
3600
3601          // Compute a filtered body background so the body itself matches
3602          // the visual filter applied to its children.
3603          const filteredBg = this.computeFilteredBodyColor(activeKey);
3604          if (filteredBg) {
3605            css += `body{background-color:${filteredBg}!important;}`;
3606          }
3607
3608          this.injectStyle('acc-filter-style', css);
3609        },
3610
3611      cycleTextScale(enable = false) {
3612          if (enable) {
3613            this.textScaleIndex = (this.textScaleIndex + 1) % this.textScaleValues.length;
3614            if (this.multiLevelFeatures['text-scale']) {
3615              this.multiLevelFeatures['text-scale'].currentIndex = this.textScaleIndex;
3616            }
3617          } else {
3618            this.textScaleIndex = 0;
3619            if (this.multiLevelFeatures['text-scale']) {
3620              this.multiLevelFeatures['text-scale'].currentIndex = -1;
3621            }
3622          }
3623          const progressIndicator = document.querySelector(`.acc-progress-indicator[data-feature="text-scale"]`);
3624          if (progressIndicator) {
3625            const dots = progressIndicator.querySelectorAll('.acc-progress-dot');
3626            dots.forEach(dot => dot.classList.remove('active'));
3627            if (enable && this.textScaleIndex < dots.length) {
3628              dots[this.textScaleIndex].classList.add('active');
3629            }
3630          }
3631          const multiply = enable ? this.textScaleValues[this.textScaleIndex] : 1;
3632          this.scaleText(multiply);
3633          this.updateState({ 'text-scale': multiply });
3634          return this.textScaleIndex;
3635        },
3636
3637      cycleMultiLevelFeature(featureKey, button) {
3638          const feature = this.multiLevelFeatures[featureKey];
3639          if (!feature || !button) return;
3640
3641          if (featureKey === 'contrast-toggle' || featureKey === 'saturation-toggle') {
3642            const newIndex = feature.currentIndex + 1;
3643            const newActiveKey = newIndex >= feature.levels ? null : feature.values[newIndex];
3644            this.updateColorFilterState(newActiveKey);
3645            this.setColorFilterUI(button.closest('.acc-menu'), newActiveKey);
3646            this.applyVisualFilters();
3647            return;
3648          }
3649
3650          const newIndex = feature.currentIndex + 1;
3651          if (newIndex >= feature.levels) {
3652            feature.currentIndex = -1;
3653            button.classList.remove('acc-selected');
3654            button.setAttribute('aria-pressed', 'false');
3655            this.updateState({ [featureKey]: featureKey === 'text-scale' ? 1 : false });
3656            if (featureKey === 'text-scale') {
3657              this.textScaleIndex = 0;
3658            }
3659          } else {
3660            feature.currentIndex = newIndex;
3661            button.classList.add('acc-selected');
3662            button.setAttribute('aria-pressed', 'true');
3663            const newValue = feature.values[newIndex];
3664            this.updateState({ [featureKey]: newValue });
3665            if (featureKey === 'text-scale') {
3666              this.textScaleIndex = newIndex;
3667            }
3668          }
3669          const indicator = button.querySelector(`.acc-progress-indicator[data-feature="${featureKey}"]`);
3670          if (indicator) {
3671            const dots = indicator.querySelectorAll('.acc-progress-dot');
3672            dots.forEach((dot, i) => {
3673              dot.classList.toggle('active', i === feature.currentIndex);
3674            });
3675          }
3676          if (featureKey === 'text-scale') {
3677            const multiply = feature.currentIndex >= 0 ? feature.values[feature.currentIndex] : 1;
3678            this.scaleText(multiply);
3679          } else {
3680            this.applyVisualFilters();
3681          }
3682        },
3683
3684      resetEnhancements() {
3685          this.saveConfig({ states: {}, systemDefaults: {} });
3686          this.textScaleIndex = 0;
3687          this.activeColorFilterKey = null;
3688          Object.keys(this.multiLevelFeatures).forEach(key => {
3689            this.multiLevelFeatures[key].currentIndex = -1;
3690          });
3691          const selected = document.querySelectorAll(".acc-selected");
3692          selected.forEach(el => {
3693            el.classList.remove("acc-selected");
3694            el.setAttribute('aria-pressed', 'false');
3695          });
3696          const indicators = document.querySelectorAll('.acc-progress-indicator');
3697          indicators.forEach(indicator => {
3698            const dots = indicator.querySelectorAll('.acc-progress-dot');
3699            dots.forEach(dot => dot.classList.remove('active'));
3700          });
3701          const menu = document.querySelector('.acc-menu');
3702          if (menu) {
3703            this.setColorFilterUI(menu, null);
3704            this.syncTextScaleControlUI(menu, 1);
3705          }
3706          
3707          // Remove focus from active element to fix the persistent focus ring bug
3708          if (document.activeElement) {
3709            document.activeElement.blur();
3710          }
3711          const styleIds = [
3712            'acc-bold-text',
3713            'acc-letter-spacing',
3714            'acc-line-spacing',
3715            'acc-large-pointer',
3716            'acc-highlight-links',
3717            'acc-highlight-title',
3718            'acc-readable-text',
3719            'acc-pause-motion',
3720            'acc-hide-images',
3721            'acc-filter-style',
3722            'acc-simple-layout'
3723          ];
3724          styleIds.forEach(id => {
3725            const style = document.getElementById(id);
3726            if (style) style.remove();
3727          });
3728          this.clearSimpleLayoutDomMutations();
3729          document.documentElement.classList.remove(
3730            'acc-filter',
3731            'acc-saturation',
3732            'acc-bold-text',
3733            'acc-letter-spacing',
3734            'acc-line-spacing',
3735            'acc-large-pointer',
3736            'acc-highlight-links',
3737            'acc-highlight-title',
3738            'acc-readable-text',
3739            'acc-pause-motion',
3740            'acc-hide-images',
3741            'acc-high-contrast-mode',
3742            'acc-simple-layout'
3743          );
3744          document.body?.classList.remove('acc-simple-layout-enabled');
3745          document.body?.classList.remove('acc-high-contrast-mode');
3746          this.disconnectTextScaleObserver();
3747          this.currentTextScaleMultiplier = 1;
3748          const scaledElements = document.querySelectorAll('[data-acc-baseSize]');
3749          scaledElements.forEach(el => {
3750            if (!(el instanceof Element) || this.shouldSkipScaling(el)) return;
3751            el.style.fontSize = '';
3752            el.removeAttribute('data-acc-baseSize');
3753          });
3754          let guide = this.findElement('.acc-rg-container');
3755          if (guide) {
3756            guide.remove();
3757            if (window.__accweb__scrollGuide) {
3758              document.removeEventListener('mousemove', window.__accweb__scrollGuide);
3759              delete window.__accweb__scrollGuide;
3760            }
3761          }
3762          this.disableAnnotations();
3763          this.stopSpeech();
3764          this.stopTtsClickMode();
3765          this.clearSystemPreferenceListeners();
3766          this.detectSystemPreferences();
3767          this.setupMediaQueryListeners();
3768          this.updateViolationBubble(this.axeScanResults);
3769        },
3770
3771    };
3772
3773    /** @typedef {import('./index.js').default} AccessibleWebWidget */
3774
3775    /** @type {{ [methodName: string]: (this: AccessibleWebWidget, ...args: any[]) => any }} */
3776    const uiMethods = {
3777
3778      translate(label) {
3779          const { lang } = this.loadConfig();
3780          const dictionary = this.translations[lang] || this.translations["en"];
3781          return dictionary[label] || label;
3782        },
3783
3784      getLanguageCountryLabel(languageCode) {
3785          const countryByLanguage = {
3786            en: 'USA',
3787            it: 'Italy',
3788            fr: 'France',
3789            de: 'Germany',
3790            es: 'Spain',
3791            ru: 'Russia',
3792            pl: 'Poland',
3793            ro: 'Romania',
3794            nl: 'Netherlands',
3795            uk: 'Ukraine'
3796          };
3797          return countryByLanguage[languageCode] || String(languageCode || '').toUpperCase();
3798        },
3799
3800      getLanguageFlag(languageCode) {
3801          const countryCodeByLanguage = {
3802            en: 'US',
3803            it: 'IT',
3804            fr: 'FR',
3805            de: 'DE',
3806            es: 'ES',
3807            ru: 'RU',
3808            pl: 'PL',
3809            ro: 'RO',
3810            nl: 'NL',
3811            uk: 'UA'
3812          };
3813          const countryCode = (countryCodeByLanguage[languageCode] || String(languageCode || '').slice(0, 2)).toUpperCase();
3814          if (!/^[A-Z]{2}$/.test(countryCode)) {
3815            return '';
3816          }
3817          return String.fromCodePoint(...countryCode.split('').map(char => 127397 + char.charCodeAt(0)));
3818        },
3819
3820      formatLanguageLabel(language) {
3821          if (!language) return 'English (USA)';
3822          const baseName = String(language.label || language.code || '')
3823            .split('(')[0]
3824            .trim() || String(language.code || 'en').toUpperCase();
3825          return `${baseName} (${this.getLanguageCountryLabel(language.code)})`;
3826        },
3827
3828      getWidgetIconMarkup(iconKey) {
3829          const normalized = typeof iconKey === 'string' ? iconKey.trim().toLowerCase() : '';
3830          const variants = this.widgetIcons?.accessibilityVariants || {};
3831          return variants[normalized] || variants.default || this.widgetIcons.accessibility;
3832        },
3833
3834      throttle(func, limit) {
3835          let inThrottle;
3836          return function (...args) {
3837            if (!inThrottle) {
3838              func.apply(this, args);
3839              inThrottle = true;
3840              setTimeout(() => (inThrottle = false), limit);
3841            }
3842          };
3843        },
3844
3845      getFocusableElements(root) {
3846          if (!root) return [];
3847          const selectors = [
3848            'a[href]',
3849            'button:not([disabled])',
3850            'input:not([disabled])',
3851            'textarea:not([disabled])',
3852            'select:not([disabled])',
3853            '[tabindex]:not([tabindex="-1"])'
3854          ].join(',');
3855          const hasDocument = typeof document !== 'undefined';
3856          const activeElement = hasDocument ? document.activeElement : null;
3857          return Array.from(root.querySelectorAll(selectors)).filter(el => {
3858            if (el.hasAttribute('disabled')) return false;
3859            if (el.getAttribute('aria-hidden') === 'true') return false;
3860            if (el.closest('[aria-hidden="true"]')) return false;
3861            const rect = el.getBoundingClientRect();
3862            const style = typeof window !== 'undefined' && window.getComputedStyle ? window.getComputedStyle(el) : { visibility: 'visible', display: 'block' };
3863            const hasSize = rect.width > 0 || rect.height > 0;
3864            const isVisible = (hasSize || (hasDocument && el === activeElement)) && style.visibility !== 'hidden' && style.display !== 'none';
3865            return isVisible;
3866          });
3867        },
3868
3869      openMenu(menuContainer, toggleBtn) {
3870          if (!menuContainer) return;
3871          const menu = this.findElement('.acc-menu', menuContainer);
3872          this.activeMenuContainer = menuContainer;
3873          this.activeMenuToggle = toggleBtn || this.activeMenuToggle;
3874          this.previousFocus = document.activeElement && typeof document.activeElement.focus === 'function'
3875            ? document.activeElement
3876            : null;
3877      
3878          menuContainer.style.display = 'block';
3879          if (menu) {
3880            menu.setAttribute('aria-hidden', 'false');
3881            menu.setAttribute('aria-modal', 'true');
3882            if (!menu.hasAttribute('tabindex')) {
3883              menu.setAttribute('tabindex', '-1');
3884            }
3885          }
3886      
3887          if (this.activeMenuToggle) {
3888            this.activeMenuToggle.setAttribute('aria-expanded', 'true');
3889          }
3890      
3891          const focusable = this.getFocusableElements(menuContainer);
3892          if (focusable.length) {
3893            focusable[0].focus();
3894          } else if (menu) {
3895            menu.focus();
3896          }
3897      
3898          if (this.menuKeyListener) {
3899            document.removeEventListener('keydown', this.menuKeyListener, true);
3900          }
3901      
3902          this.menuKeyListener = (event) => {
3903            if (!this.activeMenuContainer) return;
3904            if (event.key === 'Escape' || event.key === 'Esc') {
3905              event.preventDefault();
3906              this.closeMenu(this.activeMenuContainer);
3907              return;
3908            }
3909            if (event.key === 'Tab') {
3910              const focusables = this.getFocusableElements(this.activeMenuContainer);
3911              if (!focusables.length) {
3912                event.preventDefault();
3913                return;
3914              }
3915              const first = focusables[0];
3916              const last = focusables[focusables.length - 1];
3917              if (event.shiftKey) {
3918                if (document.activeElement === first) {
3919                  event.preventDefault();
3920                  last.focus();
3921                }
3922              } else if (document.activeElement === last) {
3923                event.preventDefault();
3924                first.focus();
3925              }
3926            }
3927          };
3928      
3929          document.addEventListener('keydown', this.menuKeyListener, true);
3930        },
3931
3932      closeMenu(menuContainer, toggleBtn) {
3933          const targetContainer = menuContainer || this.activeMenuContainer;
3934          if (!targetContainer) return;
3935          const menu = this.findElement('.acc-menu', targetContainer);
3936          targetContainer.style.display = 'none';
3937          if (menu) {
3938            menu.setAttribute('aria-hidden', 'true');
3939            menu.setAttribute('aria-modal', 'false');
3940          }
3941
3942          const langModal = this.findElement('#acc-lang-modal', targetContainer);
3943          if (langModal) {
3944            langModal.setAttribute('hidden', '');
3945          }
3946
3947          const langToggle = this.findElement('.acc-footer-lang-toggle', targetContainer);
3948          if (langToggle) {
3949            langToggle.setAttribute('aria-expanded', 'false');
3950          }
3951
3952          const langSearch = this.findElement('#acc-lang-search', targetContainer);
3953          if (langSearch) {
3954            langSearch.value = '';
3955          }
3956
3957          targetContainer.querySelectorAll('.acc-lang-item').forEach(item => {
3958            item.style.display = '';
3959          });
3960      
3961          if (this.menuKeyListener) {
3962            document.removeEventListener('keydown', this.menuKeyListener, true);
3963            this.menuKeyListener = null;
3964          }
3965      
3966          const toggle = toggleBtn || this.activeMenuToggle;
3967          if (toggle) {
3968            toggle.setAttribute('aria-expanded', 'false');
3969          }
3970      
3971          const focusTarget = toggle || this.previousFocus;
3972          if (focusTarget && typeof focusTarget.focus === 'function') {
3973            focusTarget.focus();
3974          }
3975
3976          this.stopSpeech();
3977      
3978          this.activeMenuContainer = null;
3979          this.activeMenuToggle = null;
3980          this.previousFocus = null;
3981        },
3982
3983      renderOptions(options, optionClass) {
3984          let html = '';
3985          for (let i = 0; i < options.length; i++) {
3986            const opt = options[i];
3987            if (opt.requiresSpeechSynthesis && !this.supportsTextToSpeech()) {
3988              continue;
3989            }
3990            const isMultiLevel = opt.multiLevel === true;
3991            const mergedOptionClass = [optionClass, opt.optionClass].filter(Boolean).join(' ');
3992            let progressIndicator = '';
3993            if (isMultiLevel) {
3994              const featureData = this.multiLevelFeatures[opt.key];
3995              if (featureData) {
3996                progressIndicator = `<div class="acc-progress-indicator" data-feature="${opt.key}">`;
3997                for (let j = 0; j < featureData.levels; j++) {
3998                  const isActive = featureData.currentIndex === j;
3999                  progressIndicator += `<div class="acc-progress-dot${isActive ? ' active' : ''}" data-level="${j}"> </div>`;
4000                }
4001                progressIndicator += ` </div>`;
4002              }
4003            }
4004            html += `<button 
4005          class="acc-btn ${mergedOptionClass} ${this.multiLevelFeatures[opt.key]?.currentIndex >= 0 ? 'acc-selected' : ''}" 
4006          type="button" 
4007          data-key="${opt.key}" 
4008          data-multi-level="${isMultiLevel}"
4009          title="${opt.label}"
4010          aria-label="${opt.label}"
4011          aria-pressed="${this.multiLevelFeatures[opt.key]?.currentIndex >= 0 || false}"
4012          aria-controls="acc-menu-content">
4013            ${opt.icon}
4014            <span class="acc-label">${opt.label}</span>
4015            ${progressIndicator}
4016          </button>`;
4017          }
4018          return html;
4019        },
4020
4021      renderTextScaleControl(scaleValue = 1) {
4022          const percent = this.getTextScalePercent(scaleValue);
4023          const min = Number(this.textScaleMinPercent) || 80;
4024          const max = Number(this.textScaleMaxPercent) || 150;
4025          const step = Number(this.textScaleStepPercent) || 5;
4026          const progress = ((percent - min) / (max - min)) * 100;
4027
4028          return `
4029        <div class="acc-text-scale-control" data-key="text-scale">
4030          <div class="acc-text-scale-meta">
4031            <span class="acc-text-scale-icon" aria-hidden="true">${this.widgetIcons.adjustFontSize}</span>
4032            <span class="acc-label" data-acc-text="Font Size">Font Size</span>
4033            <span class="acc-text-scale-percent">${percent}%</span>
4034          </div>
4035          <input
4036            type="range"
4037            class="acc-text-scale-range"
4038            min="${min}"
4039            max="${max}"
4040            step="${step}"
4041            value="${percent}"
4042            data-acc-text="Font Size"
4043            aria-label="Font Size"
4044            style="--acc-text-scale-progress: ${Math.max(0, Math.min(100, progress))}%">
4045        </div>
4046      `;
4047        },
4048
4049      getTranslatedText(el, defaultValue) {
4050          let text = el.getAttribute("data-acc-text");
4051          if (!text && defaultValue) {
4052            text = defaultValue;
4053            el.setAttribute("data-acc-text", text);
4054          }
4055          return this.translate(text);
4056        },
4057
4058      translateMenuUI(menu) {
4059          menu.querySelectorAll(".acc-section-title, .acc-label").forEach(el => {
4060            el.innerText = this.getTranslatedText(el, el.innerText.trim());
4061          });
4062          menu.querySelectorAll("[title]").forEach(el => {
4063            el.setAttribute("title", this.getTranslatedText(el, el.getAttribute("title")));
4064          });
4065          menu.querySelectorAll("[aria-label]").forEach(el => {
4066            el.setAttribute("aria-label", this.getTranslatedText(el, el.getAttribute("aria-label")));
4067          });
4068          this.updateSkipLinkLabel();
4069        },
4070
4071      displayMenu({ container, lang, position = 'bottom-right', offset = [20, 20], size, icon }) {
4072          try {
4073            this.applyThemeVariables();
4074            this.registerStaticStyles();
4075
4076            const activeLanguageCode = String(lang || 'en').split(/[_-]/)[0].toLowerCase();
4077
4078            const menuTemplate = `
4079        <div class="acc-menu" role="dialog" aria-labelledby="accessibility-title">
4080          <div class="acc-menu-header">
4081            <div id="accessibility-title" class="acc-menu-title">
4082              <span class="acc-menu-title-icon" aria-hidden="true">${this.getWidgetIconMarkup(icon)}</span>
4083              <span class="acc-label">Accessibility</span>
4084            </div>
4085            <div class="acc-header-actions">
4086              
4087            </div>
4088          </div>
4089          <div id="acc-menu-content" class="acc-menu-content">
4090            <div class="acc-options-all"> </div>
4091          </div>
4092          <div class="acc-footer">
4093            <button type="button" class="acc-footer-reset" title="Reset settings" aria-label="Reset settings">
4094              ${this.widgetIcons.reset}
4095              <span class="acc-label">Reset settings</span>
4096            </button>
4097
4098            <div class="acc-footer-meta">
4099              <button type="button" class="acc-footer-lang-toggle" title="Lingua" aria-label="Lingua" aria-expanded="false" aria-controls="acc-lang-modal" data-acc-text="Language">
4100                <span id="acc-current-language" class="acc-footer-lang-current">${activeLanguageCode.toUpperCase()}</span>
4101                <span class="acc-footer-lang-arrow" aria-hidden="true"> </span>
4102              </button>
4103            </div>
4104
4105            <div id="acc-lang-modal" class="acc-lang-modal" hidden>
4106              <div class="acc-lang-modal-header">
4107                <div class="acc-section-title acc-label">All Languages</div>
4108              </div>
4109              <div class="acc-lang-search-wrapper">
4110                <input type="text" id="acc-lang-search" class="acc-lang-search" placeholder="Search language" aria-label="Search language">
4111              </div>
4112              <div class="acc-lang-list">
4113                ${this.supportedLanguages.map(language => {
4114                  const languageLabel = this.formatLanguageLabel(language);
4115                  const languageFlag = this.getLanguageFlag(language.code);
4116                  return `<button type="button" class="acc-lang-item${language.code === activeLanguageCode ? ' selected' : ''}" data-lang="${language.code}" aria-label="${languageLabel}">
4117                    <span class="acc-lang-item-main">
4118                      <span class="acc-lang-flag" aria-hidden="true">${languageFlag}</span>
4119                      <span class="acc-lang-item-label">${languageLabel}</span>
4120                    </span>
4121                    <span class="acc-icon-check" aria-hidden="true"> </span>
4122                  </button>`;
4123                }).join('')}
4124              </div>
4125            </div>
4126          </div>
4127        </div>
4128        <div class="acc-overlay"> </div>
4129        `;
4130            const menuContainer = document.createElement("div");
4131            menuContainer.innerHTML = menuTemplate;
4132            menuContainer.style.display = 'none';
4133            const menu = this.findElement(".acc-menu", menuContainer);
4134            if (menu) {
4135              menu.setAttribute('aria-hidden', 'true');
4136              if (!menu.hasAttribute('tabindex')) {
4137                menu.setAttribute('tabindex', '-1');
4138              }
4139            }
4140            const isRightAligned = position === 'bottom-right' || position === 'top-right' || this.widgetTheme.menuPosition === 'right';
4141            if (isRightAligned) {
4142              menu.style.right = 'var(--acc-menu-inline-gap, 12px)';
4143              menu.style.left = 'auto';
4144            } else {
4145              menu.style.left = 'var(--acc-menu-inline-gap, 12px)';
4146              menu.style.right = 'auto';
4147            }
4148
4149            const normalizedOffset = this.normalizeOffset(offset) || [20, 20];
4150            const offsetY = normalizedOffset[1] ?? 25;
4151            const buttonSize = size !== undefined && size !== null && String(size).trim() !== ''
4152              ? this.normalizeButtonSize(size)
4153              : (this.widgetTheme?.buttonSize || '48px');
4154
4155            if (position === 'bottom-right' || position === 'bottom-left') {
4156              menu.style.bottom = `calc(${offsetY}px + ${buttonSize} + 16px)`;
4157            } else if (position === 'top-right' || position === 'top-left') {
4158              menu.style.top = `calc(${offsetY}px + ${buttonSize} + 16px)`;
4159            }
4160      
4161            const config = this.loadConfig();
4162
4163            const textKeys = new Set(['text-scale', 'bold-text', 'line-spacing', 'letter-spacing', 'readable-text']);
4164            const colorKeys = new Set(['contrast-toggle', 'invert-colors', 'saturation-toggle', 'high-contrast-mode']);
4165            const readingAidsKeys = new Set(['reading-aid', 'highlight-links', 'highlight-title', 'simple-layout']);
4166
4167            const sourceOptions = [
4168              ...this.contentOptions.map(option => ({ ...option })),
4169              ...this.colorOptions.map(option => ({ ...option, optionClass: 'acc-filter' })),
4170              ...this.accessTools.map(option => ({ ...option, optionClass: 'acc-tools' }))
4171            ];
4172
4173            const groupedOptions = {
4174              speech: [],
4175              text: [],
4176              color: [],
4177              reading: [],
4178              interaction: []
4179            };
4180
4181            const seenKeys = new Set();
4182            sourceOptions.forEach(option => {
4183              if (!option?.key || seenKeys.has(option.key)) return;
4184              seenKeys.add(option.key);
4185
4186              if (option.key === 'text-to-speech') {
4187                groupedOptions.speech.push(option);
4188                return;
4189              }
4190              if (textKeys.has(option.key)) {
4191                groupedOptions.text.push(option);
4192                return;
4193              }
4194              if (colorKeys.has(option.key)) {
4195                groupedOptions.color.push(option);
4196                return;
4197              }
4198              if (readingAidsKeys.has(option.key)) {
4199                groupedOptions.reading.push(option);
4200                return;
4201              }
4202              groupedOptions.interaction.push(option);
4203            });
4204
4205            const sectionConfig = [
4206              { key: 'speech', label: 'Speech', containerClass: 'acc-tts-toggle-container', optionClass: 'acc-tts-toggle' },
4207              { key: 'text', label: 'Text', containerClass: 'acc-options acc-options-text' },
4208              { key: 'color', label: 'Color & Contrast', containerClass: 'acc-options' },
4209              { key: 'reading', label: 'Reading Aids', containerClass: 'acc-options' },
4210              { key: 'interaction', label: 'Interaction', containerClass: 'acc-options' }
4211            ];
4212
4213            const sectionMarkup = sectionConfig.map(section => {
4214              let sectionOptions = groupedOptions[section.key];
4215              let specialContent = '';
4216
4217              if (section.key === 'text') {
4218                const textScaleOption = sectionOptions.find(option => option.key === 'text-scale');
4219                sectionOptions = sectionOptions.filter(option => option.key !== 'text-scale');
4220                const textOrder = ['line-spacing', 'letter-spacing', 'bold-text', 'readable-text'];
4221                sectionOptions.sort((a, b) => {
4222                  const indexA = textOrder.indexOf(a.key);
4223                  const indexB = textOrder.indexOf(b.key);
4224                  const rankA = indexA === -1 ? Number.MAX_SAFE_INTEGER : indexA;
4225                  const rankB = indexB === -1 ? Number.MAX_SAFE_INTEGER : indexB;
4226                  return rankA - rankB;
4227                });
4228                if (textScaleOption) {
4229                  specialContent = this.renderTextScaleControl(config.states?.['text-scale'] || 1);
4230                }
4231
4232                const firstThinRowKeys = new Set(['line-spacing', 'letter-spacing']);
4233                const secondThinRowKeys = new Set(['bold-text', 'readable-text']);
4234                const firstThinRowOptions = sectionOptions.filter(option => firstThinRowKeys.has(option.key));
4235                const secondThinRowOptions = sectionOptions.filter(option => secondThinRowKeys.has(option.key));
4236                const remainingTextOptions = sectionOptions.filter(option => (
4237                  !firstThinRowKeys.has(option.key) && !secondThinRowKeys.has(option.key)
4238                ));
4239                const firstThinRowHtml = firstThinRowOptions.length
4240                  ? `<div class="acc-options acc-options-text-inline">${this.renderOptions(firstThinRowOptions, 'acc-text-inline')}</div>`
4241                  : '';
4242                const secondThinRowHtml = secondThinRowOptions.length
4243                  ? `<div class="acc-options acc-options-text-inline">${this.renderOptions(secondThinRowOptions, 'acc-text-inline')}</div>`
4244                  : '';
4245                const remainingTextHtml = remainingTextOptions.length
4246                  ? `<div class="${section.containerClass}">${this.renderOptions(remainingTextOptions, section.optionClass || '')}</div>`
4247                  : '';
4248
4249                if (!specialContent && !firstThinRowHtml && !secondThinRowHtml && !remainingTextHtml) return '';
4250
4251                return `
4252              <section class="acc-option-category acc-option-category-${section.key}">
4253                <div class="acc-section-title acc-label">${section.label}</div>
4254                ${specialContent}
4255                ${firstThinRowHtml}
4256                ${secondThinRowHtml}
4257                ${remainingTextHtml}
4258              </section>
4259            `;
4260              }
4261
4262              if (section.key === 'color') {
4263                const colorOrder = ['contrast-toggle', 'saturation-toggle', 'invert-colors', 'high-contrast-mode'];
4264                sectionOptions.sort((a, b) => {
4265                  const indexA = colorOrder.indexOf(a.key);
4266                  const indexB = colorOrder.indexOf(b.key);
4267                  const rankA = indexA === -1 ? Number.MAX_SAFE_INTEGER : indexA;
4268                  const rankB = indexB === -1 ? Number.MAX_SAFE_INTEGER : indexB;
4269                  return rankA - rankB;
4270                });
4271
4272                const firstThinRowKeys = new Set(['contrast-toggle', 'saturation-toggle']);
4273                const secondThinRowKeys = new Set(['invert-colors', 'high-contrast-mode']);
4274                const firstThinRowOptions = sectionOptions.filter(option => firstThinRowKeys.has(option.key));
4275                const secondThinRowOptions = sectionOptions.filter(option => secondThinRowKeys.has(option.key));
4276                const remainingColorOptions = sectionOptions.filter(option => (
4277                  !firstThinRowKeys.has(option.key) && !secondThinRowKeys.has(option.key)
4278                ));
4279
4280                const firstThinRowHtml = firstThinRowOptions.length
4281                  ? `<div class="acc-options acc-options-text-inline">${this.renderOptions(firstThinRowOptions, 'acc-text-inline')}</div>`
4282                  : '';
4283                const secondThinRowHtml = secondThinRowOptions.length
4284                  ? `<div class="acc-options acc-options-text-inline">${this.renderOptions(secondThinRowOptions, 'acc-text-inline')}</div>`
4285                  : '';
4286                const remainingColorHtml = remainingColorOptions.length
4287                  ? `<div class="${section.containerClass}">${this.renderOptions(remainingColorOptions, section.optionClass || '')}</div>`
4288                  : '';
4289
4290                if (!firstThinRowHtml && !secondThinRowHtml && !remainingColorHtml) return '';
4291
4292                return `
4293              <section class="acc-option-category acc-option-category-${section.key}">
4294                <div class="acc-section-title acc-label">${section.label}</div>
4295                ${firstThinRowHtml}
4296                ${secondThinRowHtml}
4297                ${remainingColorHtml}
4298              </section>
4299            `;
4300              }
4301
4302              if (section.key === 'reading') {
4303                const readingOrder = ['highlight-links', 'highlight-title', 'reading-aid', 'simple-layout'];
4304                sectionOptions.sort((a, b) => {
4305                  const indexA = readingOrder.indexOf(a.key);
4306                  const indexB = readingOrder.indexOf(b.key);
4307                  const rankA = indexA === -1 ? Number.MAX_SAFE_INTEGER : indexA;
4308                  const rankB = indexB === -1 ? Number.MAX_SAFE_INTEGER : indexB;
4309                  return rankA - rankB;
4310                });
4311
4312                const firstThinRowKeys = new Set(['highlight-links', 'highlight-title']);
4313                const secondThinRowKeys = new Set(['reading-aid', 'simple-layout']);
4314                const firstThinRowOptions = sectionOptions.filter(option => firstThinRowKeys.has(option.key));
4315                const secondThinRowOptions = sectionOptions.filter(option => secondThinRowKeys.has(option.key));
4316                const remainingReadingOptions = sectionOptions.filter(option => (
4317                  !firstThinRowKeys.has(option.key) && !secondThinRowKeys.has(option.key)
4318                ));
4319
4320                const firstThinRowHtml = firstThinRowOptions.length
4321                  ? `<div class="acc-options acc-options-text-inline">${this.renderOptions(firstThinRowOptions, 'acc-text-inline')}</div>`
4322                  : '';
4323                const secondThinRowHtml = secondThinRowOptions.length
4324                  ? `<div class="acc-options acc-options-text-inline">${this.renderOptions(secondThinRowOptions, 'acc-text-inline')}</div>`
4325                  : '';
4326                const remainingReadingHtml = remainingReadingOptions.length
4327                  ? `<div class="${section.containerClass}">${this.renderOptions(remainingReadingOptions, section.optionClass || '')}</div>`
4328                  : '';
4329
4330                if (!firstThinRowHtml && !secondThinRowHtml && !remainingReadingHtml) return '';
4331
4332                return `
4333              <section class="acc-option-category acc-option-category-${section.key}">
4334                <div class="acc-section-title acc-label">${section.label}</div>
4335                ${firstThinRowHtml}
4336                ${secondThinRowHtml}
4337                ${remainingReadingHtml}
4338              </section>
4339            `;
4340              }
4341
4342              const sectionOptionsHtml = sectionOptions.length
4343                ? this.renderOptions(sectionOptions, section.optionClass || '')
4344                : '';
4345
4346              if (!specialContent && !sectionOptionsHtml.trim()) return '';
4347
4348              const optionsContainer = sectionOptionsHtml.trim()
4349                ? `<div class="${section.containerClass}">${sectionOptionsHtml}</div>`
4350                : '';
4351
4352              return `
4353            <section class="acc-option-category acc-option-category-${section.key}">
4354              <div class="acc-section-title acc-label">${section.label}</div>
4355              ${specialContent}
4356              ${optionsContainer}
4357            </section>
4358          `;
4359            }).join('');
4360
4361            menu.querySelector(".acc-options-all").innerHTML = sectionMarkup;
4362            const langModal = this.findElement("#acc-lang-modal", menu);
4363            const langToggle = this.findElement(".acc-footer-lang-toggle", menu);
4364            const langSearch = this.findElement("#acc-lang-search", menu);
4365            const langItems = menu.querySelectorAll(".acc-lang-item");
4366
4367            const closeLanguageModal = () => {
4368              if (langModal) {
4369                langModal.setAttribute('hidden', '');
4370              }
4371              if (langToggle) {
4372                langToggle.setAttribute('aria-expanded', 'false');
4373              }
4374              if (langSearch) {
4375                langSearch.value = '';
4376              }
4377              langItems.forEach(item => {
4378                item.style.display = '';
4379              });
4380            };
4381
4382            const openLanguageModal = () => {
4383              if (langModal) {
4384                langModal.removeAttribute('hidden');
4385              }
4386              if (langToggle) {
4387                langToggle.setAttribute('aria-expanded', 'true');
4388              }
4389              if (langSearch) {
4390                langSearch.focus();
4391              }
4392            };
4393
4394            if (langToggle && langModal) {
4395              langToggle.addEventListener('click', (event) => {
4396                event.stopPropagation();
4397                const isOpen = !langModal.hasAttribute('hidden');
4398                if (isOpen) {
4399                  closeLanguageModal();
4400                } else {
4401                  openLanguageModal();
4402                }
4403              });
4404            }
4405      
4406            // Handle language search
4407            if (langSearch) {
4408              langSearch.addEventListener("input", () => {
4409                const searchValue = langSearch.value.toLowerCase();
4410                langItems.forEach(item => {
4411                  const labelElement = item.querySelector('.acc-lang-item-label');
4412                  const text = (labelElement?.textContent || item.textContent).toLowerCase();
4413                  item.style.display = text.includes(searchValue) ? '' : 'none';
4414                });
4415              });
4416            }
4417      
4418            // Handle language selection
4419            langItems.forEach(item => {
4420              item.addEventListener("click", () => {
4421                const langCode = item.getAttribute("data-lang");
4422                if (!langCode) return;
4423                
4424                // Update selected language
4425                langItems.forEach(i => i.classList.remove("selected"));
4426                item.classList.add("selected");
4427                
4428                // Update current language display
4429                const currentLang = this.findElement("#acc-current-language", menu);
4430                if (currentLang) {
4431                  currentLang.textContent = String(langCode).toUpperCase();
4432                }
4433                closeLanguageModal();
4434                
4435                // Save language preference and update UI
4436                this.saveConfig({ lang: langCode });
4437                this.translateMenuUI(menuContainer);
4438              });
4439            });
4440
4441            const textScaleRange = this.findElement('.acc-text-scale-range', menu);
4442            if (textScaleRange) {
4443              textScaleRange.addEventListener('input', () => {
4444                const multiplier = this.setTextScaleFromPercent(textScaleRange.value, { persist: false });
4445                this.syncTextScaleControlUI(menu, multiplier);
4446              });
4447              textScaleRange.addEventListener('change', () => {
4448                const multiplier = this.setTextScaleFromPercent(textScaleRange.value, { persist: true });
4449                this.syncTextScaleControlUI(menu, multiplier);
4450              });
4451              this.syncTextScaleControlUI(menu, config.states?.['text-scale'] || 1);
4452            }
4453      
4454            // click event handler:
4455            menu.addEventListener('click', (e) => {
4456              if (langModal && !langModal.hasAttribute('hidden')) {
4457                const clickedInsideLanguageModal = Boolean(e.target.closest('.acc-lang-modal'));
4458                const clickedLanguageToggle = Boolean(e.target.closest('.acc-footer-lang-toggle'));
4459                if (!clickedInsideLanguageModal && !clickedLanguageToggle) {
4460                  closeLanguageModal();
4461                }
4462              }
4463
4464              const target = e.target.closest('[role="button"], button, .acc-overlay');
4465              if (!target) return;
4466              if (target.classList.contains('acc-overlay')) {
4467                this.closeMenu(menuContainer);
4468                return;
4469              }
4470              if (target.classList.contains('acc-footer-reset')) {
4471                this.resetEnhancements();
4472                return;
4473              }
4474              const btn = target.classList.contains('acc-btn') ? target : null;
4475              if (btn) {
4476                const key = btn.dataset.key;
4477                // Handle accessibility report action
4478                if (key === 'accessibility-report') {
4479                  this.runAccessibilityReport();
4480                }
4481                // Handle multi-level features (font size, contrast).
4482                else if (this.multiLevelFeatures[key]) {
4483                  this.cycleMultiLevelFeature(key, btn);
4484                }
4485                // For color adjustments, deselect any other active color filter.
4486                else if (this.isColorFilterKey(key)) {
4487                  const isCurrentlyActive = btn.classList.contains('acc-selected');
4488                  const newActiveKey = isCurrentlyActive ? null : key;
4489                  this.setColorFilterUI(menu, newActiveKey);
4490                  this.updateColorFilterState(newActiveKey);
4491                  this.applyVisualFilters();
4492                }
4493                // For other adjustments, simply toggle.
4494                else {
4495                  const isSelected = !btn.classList.contains("acc-selected");
4496                  btn.classList.toggle('acc-selected', isSelected);
4497                  btn.setAttribute('aria-pressed', isSelected);
4498                  this.userInitiatedToggleKey = key;
4499                  try {
4500                    this.updateState({ [key]: isSelected });
4501                    this.applyEnhancements();
4502                  } finally {
4503                    this.userInitiatedToggleKey = null;
4504                  }
4505                }
4506              }
4507            });
4508            menu.querySelectorAll('[role="button"], button').forEach(el => {
4509              el.addEventListener('keydown', (e) => {
4510                if (e.key === 'Enter' || e.key === ' ') {
4511                  e.preventDefault();
4512                  el.click();
4513                }
4514              });
4515            });
4516            this.translateMenuUI(menuContainer);
4517            const activeColorFilter = this.getActiveColorFilterKey(config.states);
4518            this.setColorFilterUI(menu, activeColorFilter);
4519            this.updateColorFilterState(activeColorFilter);
4520            if (config.states) {
4521              for (let key in config.states) {
4522                if (this.isColorFilterKey(key)) {
4523                  continue;
4524                }
4525                if (config.states[key] && key !== "text-scale") {
4526                  const selector = key; // keys now directly match our updated options.
4527                  const btn = this.findElement(`.acc-btn[data-key="${selector}"]`, menu);
4528                  if (btn) {
4529                    btn.classList.add("acc-selected");
4530                    btn.setAttribute('aria-pressed', 'true');
4531                  }
4532                }
4533              }
4534            }
4535            container.appendChild(menuContainer);
4536            return menuContainer;
4537          } catch (e) {
4538            console.error('Error displaying menu:', e);
4539            return null;
4540          }
4541        },
4542
4543      displayWidget(options) {
4544        try {
4545          this.applyThemeVariables();
4546          this.registerStaticStyles();
4547
4548          const widgetTemplate = `
4549        <div class="acc-widget">
4550          <a href="#" id="accessibilityWidget" class="acc-toggle-btn" title="Open Accessibility Menu" role="button" aria-label="Open accessibility menu" aria-expanded="false">
4551            <span class="acc-toggle-icon" aria-hidden="true">${this.getWidgetIconMarkup(options?.icon)}</span>
4552            <span class="acc-violation-bubble" data-severity="warning" hidden> </span>
4553          </a>
4554        </div>
4555        `;
4556          
4557            const widget = document.createElement("div");
4558            widget.innerHTML = widgetTemplate;
4559            widget.classList.add("acc-container");
4560            const btn = this.findElement(".acc-toggle-btn", widget);
4561            this.violationBubble = this.findElement('.acc-violation-bubble', widget);
4562      
4563            this.widgetToggleButton = btn;
4564          
4565            const { position = "bottom-right", offset = [20, 20], size } = options;
4566            const normalizedOffset = this.normalizeOffset(offset) || [20, 20];
4567            const offsetX = normalizedOffset[0] ?? 20;
4568            const offsetY = normalizedOffset[1] ?? 25;
4569            let buttonStyle = { left: `${offsetX}px`, bottom: `${offsetY}px` };
4570            if (position === "bottom-right") {
4571              buttonStyle = { right: `${offsetX}px`, left: "auto", bottom: `${offsetY}px` };
4572            } else if (position === "top-left") {
4573              buttonStyle = { top: `${offsetY}px`, bottom: "auto", left: `${offsetX}px` };
4574            } else if (position === "top-right") {
4575              buttonStyle = { top: `${offsetY}px`, right: `${offsetX}px`, bottom: "auto", left: "auto" };
4576            }
4577            Object.assign(btn.style, buttonStyle);
4578      
4579            if (size !== undefined && size !== null && String(size).trim() !== '') {
4580              const buttonSize = this.normalizeButtonSize(size);
4581              btn.style.setProperty('--acc-button-size', buttonSize);
4582            }
4583            
4584            let menu;
4585            btn.addEventListener("click", (event) => {
4586              event.preventDefault();
4587              if (!menu) {
4588                menu = this.displayMenu({ ...options, container: widget });
4589                if (!menu) return;
4590                this.menuContainer = menu;
4591      
4592                const overlay = menu.querySelector('.acc-overlay');
4593                if (overlay) {
4594                  overlay.addEventListener('click', (overlayEvent) => {
4595                    overlayEvent.stopPropagation();
4596                    this.closeMenu(menu, btn);
4597                  });
4598                }
4599      
4600                const closeBtn = menu.querySelector('.acc-menu-close');
4601                if (closeBtn) {
4602                  closeBtn.addEventListener('click', (closeEvent) => {
4603                    closeEvent.stopPropagation();
4604                    this.closeMenu(menu, btn);
4605                  });
4606                }
4607              }
4608      
4609              const isHidden = menu.style.display === 'none' || window.getComputedStyle(menu).display === 'none';
4610              if (isHidden) {
4611                this.openMenu(menu, btn);
4612              } else {
4613                this.closeMenu(menu, btn);
4614              }
4615            });
4616            
4617            // Handle keyboard interaction
4618            btn.addEventListener("keydown", (e) => {
4619              if (e.key === 'Enter' || e.key === ' ') {
4620                e.preventDefault();
4621                btn.click();
4622              }
4623            });
4624            
4625            document.body.appendChild(widget);
4626            this.translateMenuUI(widget);
4627            this.ensureSkipLink();
4628            this.runBackgroundAxeScan().catch(() => {
4629              this.updateViolationBubble({ violations: [] });
4630            });
4631            if (this.isDevMode()) {
4632              const rerunDevScan = () => {
4633                this.runBackgroundAxeScan({ force: true }).catch(() => {
4634                  this.updateViolationBubble(this.axeScanResults || { violations: [] });
4635                });
4636              };
4637              if (document.readyState === 'complete') {
4638                setTimeout(rerunDevScan, 250);
4639              } else {
4640                window.addEventListener('load', () => {
4641                  setTimeout(rerunDevScan, 150);
4642                }, { once: true });
4643              }
4644            }
4645            
4646            // Add a click handler to the document to blur the toggle button when clicking outside
4647            document.addEventListener('click', (e) => {
4648              if (!btn) return;
4649              const clickedToggle = e.target === btn || btn.contains(e.target);
4650              const clickedInsideWidget = e.target.closest('.acc-container');
4651              if (menu && this.activeMenuContainer === menu && menu.style.display !== 'none') {
4652                const clickedInsideMenu = menu.contains(e.target);
4653                if (!clickedToggle && !clickedInsideMenu && !clickedInsideWidget) {
4654                  this.closeMenu(menu, btn);
4655                }
4656              } else if (!clickedToggle) {
4657                btn.blur();
4658              }
4659            });
4660            
4661            return widget;
4662          } catch (e) {
4663            console.error('Error displaying widget:', e);
4664            return null;
4665          }
4666        },
4667
4668      startAccessibleWebWidget() {
4669              try {
4670                if (document.querySelector('.acc-widget .acc-toggle-btn')) {
4671                  return;
4672                }
4673      
4674                const dataAttributeOptions = this.getDataAttributeOptions();
4675                if (dataAttributeOptions && Object.keys(dataAttributeOptions).length) {
4676                  this.dataOptions = dataAttributeOptions;
4677                  this.options = {
4678                    ...this.options,
4679                    ...dataAttributeOptions
4680                  };
4681                }
4682      
4683                const baseOptions = { ...this.options };
4684                const lang = baseOptions.lang ||
4685                  document.querySelector('html')?.getAttribute('lang')?.replace(/[_-].*/, '') ||
4686                  navigator.language ||
4687                  "en";
4688                baseOptions.lang = lang;
4689      
4690                baseOptions.position = baseOptions.position || "bottom-right";
4691      
4692                if (baseOptions.offset) {
4693                  baseOptions.offset = this.normalizeOffset(baseOptions.offset);
4694                }
4695      
4696                if (baseOptions.size) {
4697                  baseOptions.size = this.normalizeButtonSize(baseOptions.size);
4698                  this.widgetTheme.buttonSize = baseOptions.size;
4699                }
4700
4701                this.options = { ...baseOptions };
4702                this.applyThemeOverrides(baseOptions);
4703                this.applyThemeVariables();
4704                this.registerStaticStyles();
4705                
4706                // First load the saved configuration
4707                this.loadConfig(false);
4708                this.detectSystemPreferences();
4709                this.setupMediaQueryListeners();
4710                const initialColorFilter = this.getActiveColorFilterKey(this.widgetConfig.states);
4711                this.updateColorFilterState(initialColorFilter);
4712                
4713                // Apply all saved settings immediately on page load
4714                this.applyEnhancements();
4715                this.applyVisualFilters();
4716                
4717                // Then launch the widget UI
4718                this.launchWidget(baseOptions);
4719              } catch (e) {
4720                console.error('Error starting AccessibleWeb Widget:', e);
4721              }
4722            },
4723
4724      launchWidget(args = {}) {
4725              try {
4726                let options = { lang: this.getDefaultLanguage(), position: 'bottom-right', offset: [20, 20] };
4727                
4728                // Load the saved configuration
4729                try {
4730                  const savedConfig = this.fetchSavedConfig();
4731                  if (savedConfig) {
4732                    options = { ...options, ...JSON.parse(savedConfig) };
4733                    // Note: We don't call applyEnhancements() here anymore as it's already called 
4733in startAccessibleWebWidget
4734                  }
4735                } catch (e) {
4736                  console.warn('Error loading saved config:', e);
4737                }
4738                
4739                // Merge with new arguments
4740                options = { ...options, ...args };
4741                this.saveConfig(options);
4742                
4743                // Display the widget UI
4744                this.displayWidget(options);
4745              } catch (e) {
4746                console.error('Error in widget launch:', e);
4747              }
4748            },
4749
4750    };
4751
4752    /*!
4753     * AccessibleWeb Widget v1.1.4
4754     * https://github.com/ifrederico/accessible-web-widget
4755     *
4756     * Copyright (c) 2025 ifrederico
4757     * Released under the MIT License
4758     *
4759     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
4760     * See DISCLAIMER.md for important legal information.
4761     * This widget does not guarantee accessibility compliance.
4762     */
4763
4764
4765    class AccessibleWebWidget {
4766      constructor(options = {}) {
4767        // ======================================================
4768        // CONFIGURATION & INITIAL STATE
4769        // ======================================================
4770        this.widgetTheme = { ...WIDGET_THEME };
4771        this.widgetIcons = WIDGET_ICONS;
4772        this.targetSelectors = TARGET_SELECTORS;
4773
4774        this.visualFilters = {
4775          'dark-contrast': {
4776            styles: { 'filter': 'contrast(150%) brightness(0.8)' },
4777            selector: PAGE_CONTENT_SELECTOR
4778          },
4779          'light-contrast': {
4780            styles: { 'filter': 'contrast(125%) brightness(1.2)' },
4781            selector: PAGE_CONTENT_SELECTOR
4782          },
4783          'invert-colors': {
4784            styles: { 'filter': 'invert(100%)' },
4785            selector: PAGE_CONTENT_SELECTOR
4786          },
4787          'low-saturation': {
4788            styles: { 'filter': 'saturate(50%)' },
4789            selector: PAGE_CONTENT_SELECTOR
4790          },
4791          'high-saturation': {
4792            styles: { 'filter': 'saturate(200%)' },
4793            selector: PAGE_CONTENT_SELECTOR
4794          }
4795        };
4796
4797        this.translations = TRANSLATIONS;
4798        this.supportedLanguages = SUPPORTED_LANGUAGES.map((language) => ({ ...language }));
4799
4800        this.accessTools = [
4801          { label: 'Big Cursor', key: 'large-pointer', icon: this.widgetIcons.largePointer },
4802          { label: 'Stop Animations', key: 'pause-motion', icon: this.widgetIcons.pauseMotion },
4803          { label: 'Reading Guide', key: 'reading-aid', icon: this.widgetIcons.readingAid },
4804          {
4805            label: 'Text to Speech',
4806            key: 'text-to-speech',
4807            icon: this.widgetIcons.textToSpeech,
4808            requiresSpeechSynthesis: true
4809          },
4810          { label: 'High Contrast', key: 'high-contrast-mode', icon: this.widgetIcons.highContrast },
4811          { label: 'Simplify Layout', key: 'simple-layout', icon: this.widgetIcons.simplifyLayout }
4812        ];
4813
4814        // Add dev-only tools (?acc-dev=true)
4815        if (this.isDevMode()) {
4816          this.accessTools.push(
4817            { label: 'Annotations', key: 'annotations', icon: this.widgetIcons.annotations },
4818            {
4819              label: 'Accessibility Report',
4820              key: 'accessibility-report',
4821              icon: this.widgetIcons.accessibilityReport,
4822              isAction: true
4823            }
4824          );
4825        }
4826
4827        // axe-core state
4828        this.axeCoreLoaded = false;
4829        this.axeCoreLoading = false;
4830        this.axeCorePromise = null;
4831        this.axeScanResults = null;
4832        this.axeScanPromise = null;
4833        this.violationBubble = null;
4834
4835        // Accessibility report modal state
4836        this.reportPreviousFocus = null;
4837        this.reportKeyListener = null;
4838
4839        // System preference state
4840        this.systemPreferenceListeners = [];
4841        this.systemPreferenceMediaQueries = {};
4842
4843        // Annotation state
4844        this.annotationLayer = null;
4845        this.annotationItems = [];
4846        this.annotationPopup = null;
4847        this.annotationRepositionHandler = null;
4848        this.annotationOutsideHandler = null;
4849        this.annotationRequestId = 0;
4850
4851        // Text-to-speech state
4852        this.ttsUtterance = null;
4853        this.ttsClickListener = null;
4854        this.ttsActiveTarget = null;
4855        this.ttsTextCache = '';
4856        this.ttsStatus = 'stopped';
4857        this.ttsQueue = [];
4858        this.ttsQueueIndex = 0;
4859        this.ttsSessionId = 0;
4860        this.ttsVoice = null;
4861
4862
4863        // Simplify layout state
4864        this.simpleLayoutRoot = null;
4865        this.simpleLayoutHiddenElements = [];
4866
4867        // Track direct user toggles for features that have side effects.
4868        this.userInitiatedToggleKey = null;
4869
4870        // Font size cycling
4871        this.textScaleIndex = 0;
4872        this.textScaleValues = [1.2, 1.4, 1.6];
4873        this.textScaleMinPercent = 80;
4874        this.textScaleMaxPercent = 150;
4875        this.textScaleStepPercent = 5;
4876        this.contrastFilterValues = ['light-contrast', 'dark-contrast'];
4877        this.saturationFilterValues = ['low-saturation', 'high-saturation'];
4878
4879        this.contentOptions = [
4880          { label: 'Font Weight', key: 'bold-text', icon: this.widgetIcons.boldText },
4881          { label: 'Line Height', key: 'line-spacing', icon: this.widgetIcons.lineSpacing },
4882          { label: 'Letter Spacing', key: 'letter-spacing', icon: this.widgetIcons.letterSpacing },
4883          { label: 'Hide Images', key: 'hide-images', icon: this.widgetIcons.hideImages },
4884          { label: 'Dyslexia Font', key: 'readable-text', icon: this.widgetIcons.dyslexiaFont },
4885          { label: 'Highlight Links', key: 'highlight-links', icon: this.widgetIcons.highlightLinks },
4886          { label: 'Highlight Title', key: 'highlight-title', icon: this.widgetIcons.highlightTitle },
4887          {
4888            label: 'Font Size',
4889            key: 'text-scale',
4890            icon: this.widgetIcons.adjustFontSize,
4891            multiLevel: true,
4892            levels: this.textScaleValues.length
4893          }
4894        ];
4895
4896        this.multiLevelFeatures = {
4897          'text-scale': {
4898            levels: this.textScaleValues.length,
4899            currentIndex: -1,
4900            values: this.textScaleValues
4901          },
4902          'contrast-toggle': {
4903            levels: this.contrastFilterValues.length,
4904            currentIndex: -1,
4905            values: this.contrastFilterValues
4906          },
4907          'saturation-toggle': {
4908            levels: this.saturationFilterValues.length,
4909            currentIndex: -1,
4910            values: this.saturationFilterValues
4911          }
4912        };
4913
4914        this.readingAidTemplate = `
4915      <div class="acc-rg acc-rg-top" role="presentation"> </div>
4916      <div class="acc-rg acc-rg-bottom" role="presentation"> </div>
4917    `;
4918
4919        this.colorOptions = [
4920          {
4921            label: 'Contrast',
4922            key: 'contrast-toggle',
4923            icon: this.widgetIcons.contrast,
4924            multiLevel: true,
4925            levels: this.contrastFilterValues.length
4926          },
4927          { label: 'Invert Colors', key: 'invert-colors', icon: this.widgetIcons.invertColors },
4928          {
4929            label: 'Saturation',
4930            key: 'saturation-toggle',
4931            icon: this.widgetIcons.saturation,
4932            multiLevel: true,
4933            levels: this.saturationFilterValues.length
4934          }
4935        ];
4936        this.colorFilterKeys = Object.keys(this.visualFilters);
4937        this.activeColorFilterKey = null;
4938
4939        this.textScaleSelectors = 'h1,h2,h3,h4,h5,h6,p,a,dl,dt,li,ol,th,td,span,blockquote,.acc-text';
4940        this.textScaleObserver = null;
4941        this.currentTextScaleMultiplier = 1;
4942
4943        // Global settings and state
4944        this.widgetConfig = {};
4945        this.cookieKey = 'accweb';
4946        this.readingAidVisible = false;
4947        this.readableFontLoaded = false;
4948
4949        // Menu state tracking for focus management
4950        this.activeMenuContainer = null;
4951        this.activeMenuToggle = null;
4952        this.menuKeyListener = null;
4953        this.previousFocus = null;
4954        this.widgetToggleButton = null;
4955        this.skipLinkElement = null;
4956        this.menuContainer = null;
4957
4958        // Style registration state
4959        this.staticStylesRegistered = false;
4960
4961        this.dataOptions = this.getDataAttributeOptions();
4962
4963        this.options = {
4964          lang: this.getDefaultLanguage(),
4965          position: 'bottom-right',
4966          ...this.dataOptions,
4967          ...options
4968        };
4969
4970        this.applyThemeOverrides(this.options);
4971
4972        const normalizeTtsRate = (value) => {
4973          const numeric = Number(value);
4974          if (!Number.isFinite(numeric)) return 1;
4975          return Math.min(2, Math.max(0.5, numeric));
4976        };
4977
4978        const normalizeTtsPitch = (value) => {
4979          const numeric = Number(value);
4980          if (!Number.isFinite(numeric)) return 1;
4981          return Math.min(2, Math.max(0, numeric));
4982        };
4983
4984        this.nativeTtsConfig = {
4985          preferredVoiceName: (
4986            typeof options.ttsNativeVoiceName === 'string' &&
4987            options.ttsNativeVoiceName.trim()
4988          ) ? options.ttsNativeVoiceName.trim() : '',
4989          preferredVoiceLang: (
4990            typeof options.ttsNativeVoiceLang === 'string' &&
4991            options.ttsNativeVoiceLang.trim()
4992          ) ? options.ttsNativeVoiceLang.trim() : '',
4993          rate: normalizeTtsRate(options.ttsRate),
4994          pitch: normalizeTtsPitch(options.ttsPitch)
4995        };
4996
4997        if (this.options.offset) {
4998          this.options.offset = this.normalizeOffset(this.options.offset);
4999        }
5000
5001        if (this.options.size) {
5002          this.widgetTheme.buttonSize = this.normalizeButtonSize(this.options.size);
5003          this.options.size = this.widgetTheme.buttonSize;
5004        }
5005
5006        this.applyThemeVariables();
5007        this.registerStaticStyles();
5008      }
5009
5010      applyThemeOverrides(options = {}) {
5011        if (!options || typeof options !== 'object') return;
5012
5013        const mergedOptions = {
5014          ...(options.theme && typeof options.theme === 'object' ? options.theme : {}),
5015          ...options
5016        };
5017
5018        const themeKeys = [
5019          'primaryColor',
5020          'primaryColorLight',
5021          'primaryColorDark',
5022          'backgroundColor',
5023          'textColor',
5024          'textColorInverted',
5025          'cardBackground',
5026          'borderColor',
5027          'focusRingColor',
5028          'hoverColor',
5029          'activeColor',
5030          'borderRadius',
5031          'buttonBorderRadius',
5032          'headerHeight',
5033          'focusBorderWidth',
5034          'focusOutlineOffset',
5035          'zIndex'
5036        ];
5037
5038        themeKeys.forEach((key) => {
5039          const value = mergedOptions[key];
5040          if (value === undefined || value === null) return;
5041
5042          if (typeof value === 'string') {
5043            const trimmed = value.trim();
5044            if (!trimmed) return;
5045            this.widgetTheme[key] = trimmed;
5046            return;
5047          }
5048
5049          this.widgetTheme[key] = value;
5050        });
5051
5052        if (mergedOptions.buttonSize !== undefined && mergedOptions.buttonSize !== null && mergedOptions.buttonSize !== '') {
5053          this.widgetTheme.buttonSize = this.normalizeButtonSize(mergedOptions.buttonSize);
5054        }
5055      }
5056    }
5057
5058    /** @typedef {typeof stateMethods & typeof styleMethods & typeof featureMethods & typeof uiMethods} WidgetMixedMethods */
5059    /** @typedef {AccessibleWebWidget & WidgetMixedMethods} AccessibleWebWidgetInstance */
5060
5061    /** @type {AccessibleWebWidget['prototype'] & WidgetMixedMethods} */
5062    const widgetPrototype = AccessibleWebWidget.prototype;
5063
5064    Object.assign(
5065      widgetPrototype,
5066      stateMethods,
5067      styleMethods,
5068      featureMethods,
5069      uiMethods
5070    );
5071
5072    if (typeof window !== 'undefined') {
5073      window.AccessibleWebWidget = AccessibleWebWidget;
5074    }
5075
5076    if (typeof document !== 'undefined') {
5077      const globalAutoInitOptions = (
5078        typeof window !== 'undefined' &&
5079        window.AccessibleWebWidgetOptions &&
5080        typeof window.AccessibleWebWidgetOptions === 'object'
5081      ) ? window.AccessibleWebWidgetOptions : {};
5082
5083      /** @type {AccessibleWebWidgetInstance} */
5084      const widgetInstance = new AccessibleWebWidget(globalAutoInitOptions);
5085      if (document.readyState === 'complete' || document.readyState === 'interactive') {
5086        widgetInstance.startAccessibleWebWidget();
5087      } else {
5088        document.addEventListener('DOMContentLoaded', () => widgetInstance.startAccessibleWebWidget());
5089      }
5090    }
5091
5092    return AccessibleWebWidget;
5093
5094})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.