@cognitvs/a11y-widget 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1325 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+ const widgetCss = ':host{--a11y-primary: #0077d4;--a11y-primary-hover: #0066b8;--a11y-primary-light: #eff6ff;--a11y-bg: #ffffff;--a11y-text: #0f172a;--a11y-text-muted: #64748b;--a11y-border: #e2e8f0;--a11y-card-bg: #f8fafc;--a11y-card-hover: #f1f5f9;--a11y-shadow: 0 20px 40px -15px rgba(0, 0, 0, .25);--a11y-radius: 20px;font-family:Segoe UI,-apple-system,BlinkMacSystemFont,Roboto,Helvetica,Arial,sans-serif;font-size:14px;line-height:1.5;box-sizing:border-box}:host([theme="dark"]){--a11y-bg: #0f172a;--a11y-text: #f8fafc;--a11y-text-muted: #94a3b8;--a11y-border: #1e293b;--a11y-card-bg: #1e293b;--a11y-card-hover: #334155;--a11y-shadow: 0 25px 50px -12px rgba(0, 0, 0, .6)}*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}.a11y-top-banner{position:fixed;top:0;left:0;width:100%;z-index:999995;background-color:#0b192c;color:#fff;border-bottom:2px solid var(--a11y-primary);box-shadow:0 10px 25px #0006;padding:10px 20px;display:flex;align-items:center;justify-content:space-between;gap:16px;font-size:14px;transition:opacity .35s cubic-bezier(.16,1,.3,1),transform .35s cubic-bezier(.16,1,.3,1)}.a11y-top-banner.hidden{opacity:0;pointer-events:none;transform:translateY(-100%)}.a11y-banner-content{display:flex;align-items:center;gap:12px}.a11y-banner-icon{width:26px;height:26px;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;color:var(--a11y-primary)}.a11y-banner-icon svg{width:100%;height:100%}.a11y-banner-text strong{color:var(--a11y-cyan, #44a5dd)}.a11y-banner-close{background:#ffffff1a;border:none;color:#cbd5e1;width:32px;height:32px;border-radius:50%;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:all .2s;font-size:16px;font-weight:700}.a11y-banner-close:hover{background:#ef4444;color:#fff;transform:scale(1.1)}.a11y-widget-root{position:fixed;bottom:20px;right:20px;z-index:999990;display:flex;flex-direction:column;align-items:flex-end;gap:12px;max-width:calc(100vw - 32px);pointer-events:auto}:host([position="bottom-left"]) .a11y-widget-root{right:auto;left:20px;align-items:flex-start}.a11y-panel{background:var(--a11y-bg);color:var(--a11y-text);border:2px solid var(--a11y-primary);border-radius:var(--a11y-radius);box-shadow:var(--a11y-shadow);width:310px;max-height:var(--fab-panel-max-height, calc(100vh - 160px) );overflow-y:auto;overflow-x:hidden;padding:20px;transition:max-height .25s cubic-bezier(.16,1,.3,1),opacity .25s ease,transform .25s ease,padding .25s ease;display:flex;flex-direction:column;gap:18px}.a11y-panel.closed{max-height:0!important;padding-top:0!important;padding-bottom:0!important;border-width:0!important;opacity:0;pointer-events:none;transform:translateY(15px) scale(.95)}.a11y-panel-header{display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--a11y-border);padding-bottom:12px}.a11y-panel-title{font-size:12px;font-weight:900;letter-spacing:.1em;text-transform:uppercase;color:var(--a11y-primary);display:flex;align-items:center;gap:8px}.a11y-panel-title svg{color:var(--a11y-primary);flex-shrink:0}.a11y-profile-select{width:100%;padding:10px 12px;border-radius:12px;border:2px solid var(--a11y-border);background:var(--a11y-card-bg);color:var(--a11y-text);font-weight:700;font-size:13px;cursor:pointer;outline:none;transition:border-color .2s}.a11y-profile-select:focus{border-color:var(--a11y-primary)}.a11y-section-title{font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:.05em;color:var(--a11y-text-muted);margin-top:8px;margin-bottom:6px}.a11y-btn{width:100%;padding:12px 14px;border-radius:14px;font-size:13px;font-weight:800;cursor:pointer;border:2px solid transparent;background:var(--a11y-card-bg);color:var(--a11y-text);display:flex;align-items:center;justify-content:flex-start;gap:10px;transition:all .2s ease;-webkit-user-select:none;user-select:none}.a11y-btn:hover{background:var(--a11y-card-hover);border-color:#0077d44d}.a11y-btn.active{background:var(--a11y-primary);color:#fff;border-color:var(--a11y-primary);box-shadow:0 4px 14px #0077d459}.a11y-btn-group{display:flex;align-items:center;gap:6px}.a11y-btn-mini{flex:1;padding:8px;border-radius:10px;font-size:12px;font-weight:700;text-align:center;border:1px solid var(--a11y-border);background:var(--a11y-card-bg);color:var(--a11y-text);cursor:pointer;transition:all .15s}.a11y-btn-mini:hover{background:var(--a11y-card-hover)}.a11y-btn-mini.active{background:var(--a11y-primary);color:#fff;border-color:var(--a11y-primary)}.a11y-scale-box{display:flex;align-items:center;gap:8px;background:var(--a11y-card-bg);padding:6px;border-radius:14px;border:1px solid var(--a11y-border)}.a11y-scale-btn{flex:1;padding:8px 12px;font-size:15px;font-weight:900;background:var(--a11y-bg);border:1px solid var(--a11y-border);border-radius:10px;color:var(--a11y-primary);cursor:pointer;transition:all .2s}.a11y-scale-btn:hover{background:var(--a11y-primary);color:#fff}.a11y-scale-val{min-width:48px;text-align:center;font-weight:900;color:var(--a11y-text);font-size:14px}.a11y-reset-btn{background:transparent;border:none;color:var(--a11y-text-muted);font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;text-decoration:underline;text-decoration-style:dashed;cursor:pointer;padding:8px 0;text-align:center;width:100%;margin-top:6px;transition:color .2s}.a11y-reset-btn:hover{color:#ef4444}.a11y-fab-wrapper{position:relative;display:flex;align-items:center;justify-content:flex-end}.a11y-fab{width:58px;height:58px;border-radius:50%;border:2px solid var(--a11y-primary);background:var(--a11y-primary);color:#fff;display:flex;align-items:center;justify-content:center;cursor:pointer;box-shadow:0 10px 25px #0077d473;transition:all .25s cubic-bezier(.175,.885,.32,1.275)}.a11y-fab:hover{transform:scale(1.1);box-shadow:0 15px 30px #0077d499}.a11y-fab:active{transform:scale(.95)}.a11y-fab svg{width:34px;height:34px;color:#fff;flex-shrink:0}.a11y-tour-bubble{position:absolute;right:74px;top:50%;transform:translateY(-50%);width:220px;background:#0f172a;border:2px solid var(--a11y-primary);color:#fff;padding:12px 14px;border-radius:16px;box-shadow:0 15px 35px #00000080;display:flex;align-items:center;gap:10px;cursor:pointer;animation:a11yFadeIn .3s ease-out;z-index:100}.a11y-tour-bubble:after{content:"";position:absolute;right:-8px;top:50%;transform:translateY(-50%);border-top:8px solid transparent;border-bottom:8px solid transparent;border-left:8px solid var(--a11y-primary)}.a11y-ping-ring{position:absolute;right:0;width:58px;height:58px;border-radius:50%;background-color:var(--a11y-primary);animation:a11yPing 2s cubic-bezier(0,0,.2,1) infinite;pointer-events:none;opacity:.6}@keyframes a11yPing{75%,to{transform:scale(1.7);opacity:0}}@keyframes a11yFadeIn{0%{opacity:0;transform:translateY(-50%) scale(.9)}to{opacity:1;transform:translateY(-50%) scale(1)}}';
5
+ const globalCss = `html.high-contrast{filter:contrast(160%)!important}html.high-contrast *{border-color:#facc15!important}html.reduce-motion *,html.reduce-motion *:before,html.reduce-motion *:after{animation-duration:.001ms!important;animation-iteration-count:1!important;transition-duration:.001ms!important;scroll-behavior:auto!important}body{transition:padding-top .35s cubic-bezier(.16,1,.3,1)!important}body.a11y-banner-active{padding-top:var(--a11y-top-banner-height, 46px)!important}html.big-cursor,html.big-cursor *{cursor:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 32 32"><path fill="%23ffffff" stroke="%230077d4" stroke-width="3" stroke-linejoin="round" d="M2 2 L2 28 L9 22 L15 31 L19 29 L12 20 L21 20 Z"/></svg>'),auto!important}html.cursor-hidden,html.cursor-hidden *{cursor:none!important}html.keyboard-focus *:focus,html.keyboard-focus *:focus-visible{outline:4px solid #0077d4!important;outline-offset:3px!important}html.highlight-links a,html.highlight-links button,html.highlight-links [role=button],html.highlight-links input[type=submit],html.highlight-links input[type=button]{outline:2px dashed #0077d4!important;outline-offset:2px!important;background-color:#0077d41f!important}html.dyslexia-mode,html.dyslexia-mode *{font-family:Comic Sans MS,OpenDyslexic,Arial,sans-serif!important;letter-spacing:.08em!important;word-spacing:.16em!important;line-height:1.8!important}html.text-spacing-1 *{letter-spacing:.12em!important;word-spacing:.16em!important;line-height:1.8!important}html.text-spacing-2 *{letter-spacing:.2em!important;word-spacing:.28em!important;line-height:2.2!important}html.daltonism-protanopia{filter:url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg"><filter id="f"><feColorMatrix type="matrix" values="0.567, 0.433, 0, 0, 0 0.558, 0.442, 0, 0, 0 0, 0.242, 0.758, 0, 0 0, 0, 0, 1, 0"/></filter></svg>#f')!important}html.daltonism-deuteranopia{filter:url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg"><filter id="f"><feColorMatrix type="matrix" values="0.625, 0.375, 0, 0, 0 0.7, 0.3, 0, 0, 0 0, 0.3, 0.7, 0, 0 0, 0, 0, 1, 0"/></filter></svg>#f')!important}html.daltonism-tritanopia{filter:url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg"><filter id="f"><feColorMatrix type="matrix" values="0.95, 0.05, 0, 0, 0 0, 0.433, 0.567, 0, 0 0, 0.475, 0.525, 0, 0 0, 0, 0, 1, 0"/></filter></svg>#f')!important}html.daltonism-monochromacy{filter:grayscale(100%)!important}:root{--font-scale: 1}body{font-size:calc(1rem * var(--font-scale, 1))!important}`;
6
+ class A11yStore {
7
+ constructor(prefix = "cognitvs") {
8
+ __publicField(this, "prefix");
9
+ __publicField(this, "state");
10
+ __publicField(this, "listeners", /* @__PURE__ */ new Set());
11
+ this.prefix = prefix;
12
+ this.state = this.loadInitialState();
13
+ }
14
+ getState() {
15
+ return { ...this.state };
16
+ }
17
+ subscribe(listener) {
18
+ this.listeners.add(listener);
19
+ listener(this.getState());
20
+ return () => this.listeners.delete(listener);
21
+ }
22
+ notify() {
23
+ const current = this.getState();
24
+ this.listeners.forEach((fn) => fn(current));
25
+ }
26
+ loadInitialState() {
27
+ const getItem = (key) => {
28
+ try {
29
+ return localStorage.getItem(`${this.prefix}_${key}`);
30
+ } catch {
31
+ return null;
32
+ }
33
+ };
34
+ const fontScale = parseFloat(getItem("font_scale") || "1") || 1;
35
+ const isHighContrast = getItem("high_contrast") === "true";
36
+ const isReduceMotion = getItem("reduce_motion") === "true";
37
+ const isBigCursor = getItem("big_cursor") === "true";
38
+ const isKeyboardFocus = getItem("keyboard_focus") === "true";
39
+ const isHighlightLinks = getItem("highlight_links") === "true";
40
+ const isReadingMaskActive = getItem("reading_mask") === "true";
41
+ const isReadingMaskSmart = getItem("reading_mask_smart") !== "false";
42
+ const isDyslexiaMode = getItem("dyslexia") === "true";
43
+ const textSpacingLevel = parseInt(getItem("text_spacing") || "0", 10) || 0;
44
+ const colorBlindMode = getItem("colorblind") || "none";
45
+ const isScrollAssistActive = getItem("scroll_assist") === "true";
46
+ const isCursorMagnetActive = getItem("magnet_cursor") === "true";
47
+ const cursorMagnetForce = getItem("magnet_force") || "suave";
48
+ const isVLibrasActive = getItem("vlibras") === "true";
49
+ const isTTSActive = getItem("tts") === "true";
50
+ const ttsRate = parseFloat(getItem("tts_rate") || "1") || 1;
51
+ const hasSeenTour = getItem("seen_tour") === "true";
52
+ const bannerDismissed = getItem("banner_dismissed") === "true";
53
+ return {
54
+ isExpanded: false,
55
+ activeProfile: "all",
56
+ fontScale,
57
+ isHighContrast,
58
+ isReadingMaskActive,
59
+ isReadingMaskSmart,
60
+ colorBlindMode,
61
+ isDaltonismMenuOpen: false,
62
+ isTTSActive,
63
+ ttsRate,
64
+ isVLibrasActive,
65
+ isBigCursor,
66
+ isKeyboardFocus,
67
+ isScrollAssistActive,
68
+ isCursorMagnetActive,
69
+ cursorMagnetForce,
70
+ isHighlightLinks,
71
+ isReduceMotion,
72
+ isDyslexiaMode,
73
+ textSpacingLevel,
74
+ showAttentionGrabber: false,
75
+ hasInteracted: hasSeenTour,
76
+ bannerDismissed
77
+ };
78
+ }
79
+ setStorage(key, value) {
80
+ try {
81
+ localStorage.setItem(`${this.prefix}_${key}`, value);
82
+ } catch (e) {
83
+ console.warn("A11yWidget: falha ao salvar no localStorage", e);
84
+ }
85
+ }
86
+ removeStorage(key) {
87
+ try {
88
+ localStorage.removeItem(`${this.prefix}_${key}`);
89
+ } catch (e) {
90
+ console.warn("A11yWidget: falha ao remover do localStorage", e);
91
+ }
92
+ }
93
+ update(partial) {
94
+ this.state = { ...this.state, ...partial };
95
+ this.notify();
96
+ }
97
+ toggleExpanded() {
98
+ this.update({
99
+ isExpanded: !this.state.isExpanded,
100
+ showAttentionGrabber: false,
101
+ hasInteracted: true
102
+ });
103
+ this.setStorage("seen_tour", "true");
104
+ }
105
+ setActiveProfile(profile) {
106
+ this.update({ activeProfile: profile });
107
+ }
108
+ updateFontScale(delta) {
109
+ const newScale = Math.min(Math.max(parseFloat((this.state.fontScale + delta).toFixed(2)), 0.8), 2);
110
+ this.update({ fontScale: newScale });
111
+ this.setStorage("font_scale", newScale.toString());
112
+ }
113
+ toggleHighContrast() {
114
+ const val = !this.state.isHighContrast;
115
+ this.update({ isHighContrast: val });
116
+ this.setStorage("high_contrast", val.toString());
117
+ }
118
+ toggleReduceMotion() {
119
+ const val = !this.state.isReduceMotion;
120
+ this.update({ isReduceMotion: val });
121
+ this.setStorage("reduce_motion", val.toString());
122
+ }
123
+ toggleBigCursor() {
124
+ const val = !this.state.isBigCursor;
125
+ this.update({ isBigCursor: val });
126
+ this.setStorage("big_cursor", val.toString());
127
+ }
128
+ toggleKeyboardFocus() {
129
+ const val = !this.state.isKeyboardFocus;
130
+ this.update({ isKeyboardFocus: val });
131
+ this.setStorage("keyboard_focus", val.toString());
132
+ }
133
+ toggleHighlightLinks() {
134
+ const val = !this.state.isHighlightLinks;
135
+ this.update({ isHighlightLinks: val });
136
+ this.setStorage("highlight_links", val.toString());
137
+ }
138
+ toggleScrollAssist() {
139
+ const val = !this.state.isScrollAssistActive;
140
+ this.update({ isScrollAssistActive: val });
141
+ this.setStorage("scroll_assist", val.toString());
142
+ }
143
+ toggleCursorMagnet() {
144
+ const val = !this.state.isCursorMagnetActive;
145
+ this.update({ isCursorMagnetActive: val });
146
+ this.setStorage("magnet_cursor", val.toString());
147
+ }
148
+ setCursorMagnetForce(force) {
149
+ this.update({ cursorMagnetForce: force });
150
+ this.setStorage("magnet_force", force);
151
+ }
152
+ toggleReadingMask() {
153
+ const val = !this.state.isReadingMaskActive;
154
+ this.update({ isReadingMaskActive: val });
155
+ this.setStorage("reading_mask", val.toString());
156
+ }
157
+ setReadingMaskSmart(smart) {
158
+ this.update({ isReadingMaskSmart: smart });
159
+ this.setStorage("reading_mask_smart", smart.toString());
160
+ }
161
+ toggleDyslexia() {
162
+ const val = !this.state.isDyslexiaMode;
163
+ this.update({ isDyslexiaMode: val });
164
+ this.setStorage("dyslexia", val.toString());
165
+ }
166
+ setTextSpacing(level) {
167
+ this.update({ textSpacingLevel: level });
168
+ this.setStorage("text_spacing", level.toString());
169
+ }
170
+ setColorBlindMode(mode) {
171
+ this.update({ colorBlindMode: mode, isDaltonismMenuOpen: false });
172
+ this.setStorage("colorblind", mode);
173
+ }
174
+ toggleDaltonismMenu() {
175
+ this.update({ isDaltonismMenuOpen: !this.state.isDaltonismMenuOpen });
176
+ }
177
+ toggleTTS() {
178
+ const val = !this.state.isTTSActive;
179
+ const rate = val ? 1 : this.state.ttsRate;
180
+ this.update({ isTTSActive: val, ttsRate: rate });
181
+ this.setStorage("tts", val.toString());
182
+ if (val) this.setStorage("tts_rate", "1");
183
+ }
184
+ setTTSRate(rate) {
185
+ this.update({ ttsRate: rate });
186
+ this.setStorage("tts_rate", rate.toString());
187
+ }
188
+ toggleVLibras() {
189
+ const val = !this.state.isVLibrasActive;
190
+ this.update({ isVLibrasActive: val });
191
+ this.setStorage("vlibras", val.toString());
192
+ }
193
+ dismissBanner() {
194
+ this.update({ bannerDismissed: true });
195
+ this.setStorage("banner_dismissed", "true");
196
+ }
197
+ resetAll() {
198
+ const keys = [
199
+ "font_scale",
200
+ "high_contrast",
201
+ "reduce_motion",
202
+ "big_cursor",
203
+ "keyboard_focus",
204
+ "highlight_links",
205
+ "reading_mask",
206
+ "reading_mask_smart",
207
+ "dyslexia",
208
+ "text_spacing",
209
+ "colorblind",
210
+ "vlibras",
211
+ "magnet_cursor",
212
+ "magnet_force",
213
+ "tts",
214
+ "tts_rate"
215
+ ];
216
+ keys.forEach((k) => this.removeStorage(k));
217
+ this.update({
218
+ fontScale: 1,
219
+ isHighContrast: false,
220
+ isReadingMaskActive: false,
221
+ isReadingMaskSmart: true,
222
+ colorBlindMode: "none",
223
+ isDaltonismMenuOpen: false,
224
+ isTTSActive: false,
225
+ ttsRate: 1,
226
+ isVLibrasActive: false,
227
+ isBigCursor: false,
228
+ isKeyboardFocus: false,
229
+ isScrollAssistActive: false,
230
+ isCursorMagnetActive: false,
231
+ cursorMagnetForce: "suave",
232
+ isHighlightLinks: false,
233
+ isReduceMotion: false,
234
+ isDyslexiaMode: false,
235
+ textSpacingLevel: 0
236
+ });
237
+ }
238
+ }
239
+ class DomEffectsManager {
240
+ constructor() {
241
+ __publicField(this, "prevClasses", /* @__PURE__ */ new Set());
242
+ __publicField(this, "readingMaskEl", null);
243
+ __publicField(this, "maskFrameId", null);
244
+ __publicField(this, "boundMouseMove", null);
245
+ }
246
+ applyGlobalState(state) {
247
+ const docEl = document.documentElement;
248
+ docEl.style.setProperty("--font-scale", state.fontScale.toString());
249
+ this.syncClass(docEl, "high-contrast", state.isHighContrast);
250
+ this.syncClass(docEl, "reduce-motion", state.isReduceMotion);
251
+ this.syncClass(docEl, "big-cursor", state.isBigCursor);
252
+ this.syncClass(docEl, "keyboard-focus", state.isKeyboardFocus);
253
+ this.syncClass(docEl, "highlight-links", state.isHighlightLinks);
254
+ this.syncClass(docEl, "scroll-assist-active", state.isScrollAssistActive);
255
+ this.syncClass(docEl, "cursor-magnet-active", state.isCursorMagnetActive);
256
+ this.syncClass(docEl, "dyslexia-mode", state.isDyslexiaMode);
257
+ docEl.setAttribute("data-magnet-force", state.cursorMagnetForce);
258
+ docEl.classList.remove("text-spacing-1", "text-spacing-2");
259
+ if (state.textSpacingLevel > 0) {
260
+ docEl.classList.add(`text-spacing-${state.textSpacingLevel}`);
261
+ }
262
+ ["protanopia", "deuteranopia", "tritanopia", "monochromacy"].forEach((m) => {
263
+ docEl.classList.remove(`daltonism-${m}`);
264
+ });
265
+ if (state.colorBlindMode !== "none") {
266
+ docEl.classList.add(`daltonism-${state.colorBlindMode}`);
267
+ }
268
+ this.syncReadingMask(state.isReadingMaskActive, state.isReadingMaskSmart);
269
+ }
270
+ syncClass(el, className, active) {
271
+ if (active) {
272
+ el.classList.add(className);
273
+ this.prevClasses.add(className);
274
+ } else {
275
+ el.classList.remove(className);
276
+ this.prevClasses.delete(className);
277
+ }
278
+ }
279
+ syncReadingMask(active, isSmart) {
280
+ if (!active) {
281
+ if (this.readingMaskEl) {
282
+ this.readingMaskEl.remove();
283
+ this.readingMaskEl = null;
284
+ }
285
+ if (this.boundMouseMove) {
286
+ window.removeEventListener("mousemove", this.boundMouseMove);
287
+ this.boundMouseMove = null;
288
+ }
289
+ if (this.maskFrameId) {
290
+ cancelAnimationFrame(this.maskFrameId);
291
+ this.maskFrameId = null;
292
+ }
293
+ return;
294
+ }
295
+ if (!this.readingMaskEl) {
296
+ this.readingMaskEl = document.createElement("div");
297
+ this.readingMaskEl.id = "cognitvs-a11y-reading-mask";
298
+ this.readingMaskEl.setAttribute("aria-hidden", "true");
299
+ this.readingMaskEl.style.cssText = `
300
+ position: fixed;
301
+ left: 0;
302
+ width: 100vw;
303
+ pointer-events: none;
304
+ z-index: 999998;
305
+ border-top: 2px solid rgba(0, 119, 212, 0.7);
306
+ border-bottom: 2px solid rgba(0, 119, 212, 0.7);
307
+ box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.7);
308
+ height: var(--mask-height, 140px);
309
+ top: var(--mask-top, calc(50vh - 70px));
310
+ transition: top 0.15s cubic-bezier(0.2, 0, 0, 1), height 0.15s cubic-bezier(0.2, 0, 0, 1);
311
+ `;
312
+ document.body.appendChild(this.readingMaskEl);
313
+ }
314
+ let lastTarget = null;
315
+ if (this.boundMouseMove) {
316
+ window.removeEventListener("mousemove", this.boundMouseMove);
317
+ }
318
+ this.boundMouseMove = (e) => {
319
+ if (this.maskFrameId) cancelAnimationFrame(this.maskFrameId);
320
+ this.maskFrameId = requestAnimationFrame(() => {
321
+ if (isSmart) {
322
+ const target = document.elementFromPoint(e.clientX, e.clientY);
323
+ if (target) {
324
+ const textContainer = target.closest("p, h1, h2, h3, h4, h5, h6, li, blockquote, figcaption, td, th, a, button, label");
325
+ const elToMeasure = textContainer || target;
326
+ const rect = elToMeasure.getBoundingClientRect();
327
+ if (textContainer && rect.height > 10 && rect.height < 500) {
328
+ if (elToMeasure !== lastTarget) {
329
+ lastTarget = elToMeasure;
330
+ const margin = 24;
331
+ const maskHeight = rect.height + margin;
332
+ const topPos = rect.top - margin / 2;
333
+ document.documentElement.style.setProperty("--mask-height", `${maskHeight}px`);
334
+ document.documentElement.style.setProperty("--mask-top", `${topPos}px`);
335
+ }
336
+ return;
337
+ }
338
+ }
339
+ }
340
+ lastTarget = null;
341
+ document.documentElement.style.setProperty("--mask-height", "140px");
342
+ document.documentElement.style.setProperty("--mask-top", `${e.clientY - 70}px`);
343
+ });
344
+ };
345
+ window.addEventListener("mousemove", this.boundMouseMove, { passive: true });
346
+ }
347
+ destroy() {
348
+ const docEl = document.documentElement;
349
+ this.prevClasses.forEach((c) => docEl.classList.remove(c));
350
+ ["protanopia", "deuteranopia", "tritanopia", "monochromacy", "text-spacing-1", "text-spacing-2"].forEach((c) => docEl.classList.remove(c));
351
+ docEl.style.removeProperty("--font-scale");
352
+ docEl.removeAttribute("data-magnet-force");
353
+ this.syncReadingMask(false, false);
354
+ }
355
+ }
356
+ class TTSController {
357
+ constructor(isWidgetElement) {
358
+ __publicField(this, "active", false);
359
+ __publicField(this, "rate", 1);
360
+ __publicField(this, "boundClickHandler", null);
361
+ __publicField(this, "isWidgetElement");
362
+ this.isWidgetElement = isWidgetElement;
363
+ }
364
+ update(active, rate) {
365
+ this.active = active;
366
+ this.rate = rate;
367
+ if (!active) {
368
+ if (typeof window !== "undefined" && "speechSynthesis" in window) {
369
+ window.speechSynthesis.cancel();
370
+ }
371
+ if (this.boundClickHandler) {
372
+ document.removeEventListener("click", this.boundClickHandler, { capture: true });
373
+ this.boundClickHandler = null;
374
+ }
375
+ return;
376
+ }
377
+ if (!this.boundClickHandler) {
378
+ this.boundClickHandler = this.handleClick.bind(this);
379
+ document.addEventListener("click", this.boundClickHandler, { capture: true });
380
+ }
381
+ }
382
+ handleClick(e) {
383
+ if (!this.active) return;
384
+ const target = e.target;
385
+ if (!target) return;
386
+ if (this.isWidgetElement(target)) return;
387
+ const invalidTags = [
388
+ "BODY",
389
+ "HTML",
390
+ "MAIN",
391
+ "SECTION",
392
+ "ARTICLE",
393
+ "HEADER",
394
+ "FOOTER",
395
+ "NAV",
396
+ "ASIDE",
397
+ "FORM",
398
+ "UL",
399
+ "OL",
400
+ "TABLE",
401
+ "TBODY",
402
+ "TR"
403
+ ];
404
+ if (invalidTags.includes(target.tagName) || target.id === "root") return;
405
+ const blockTags = [
406
+ "DIV",
407
+ "P",
408
+ "H1",
409
+ "H2",
410
+ "H3",
411
+ "H4",
412
+ "H5",
413
+ "H6",
414
+ "UL",
415
+ "OL",
416
+ "LI",
417
+ "SECTION",
418
+ "ARTICLE",
419
+ "IMG",
420
+ "PICTURE",
421
+ "VIDEO",
422
+ "HEADER",
423
+ "FOOTER",
424
+ "NAV",
425
+ "ASIDE",
426
+ "FORM",
427
+ "TABLE",
428
+ "TR",
429
+ "TD",
430
+ "TH",
431
+ "MAIN"
432
+ ];
433
+ const hasBlockChildren = Array.from(target.children).some((child) => blockTags.includes(child.tagName));
434
+ if (hasBlockChildren) return;
435
+ const rawText = target.innerText || target.textContent || "";
436
+ const text = rawText.replace(/[\p{Extended_Pictographic}]/gu, "").trim();
437
+ if (text.length > 0 && "speechSynthesis" in window) {
438
+ if (target.closest("a") || target.closest("button")) {
439
+ e.preventDefault();
440
+ e.stopPropagation();
441
+ }
442
+ window.speechSynthesis.cancel();
443
+ const utterance = new SpeechSynthesisUtterance(text);
444
+ utterance.lang = "pt-BR";
445
+ utterance.rate = this.rate;
446
+ const originalHTML = target.innerHTML;
447
+ const originalOutline = target.style.outline;
448
+ const originalOutlineOffset = target.style.outlineOffset;
449
+ const tokens = text.split(/(\s+)/);
450
+ target.innerHTML = "";
451
+ const spanElements = [];
452
+ tokens.forEach((token) => {
453
+ const span = document.createElement("span");
454
+ span.textContent = token;
455
+ span.style.transition = "all 150ms ease-in-out";
456
+ span.style.borderRadius = "2px";
457
+ target.appendChild(span);
458
+ spanElements.push(span);
459
+ });
460
+ target.style.outline = "3px solid #f59e0b";
461
+ target.style.outlineOffset = "2px";
462
+ utterance.onboundary = (ev) => {
463
+ if (ev.name === "word") {
464
+ let charCount = 0;
465
+ for (let i = 0; i < spanElements.length; i++) {
466
+ const spanText = spanElements[i].textContent || "";
467
+ const span = spanElements[i];
468
+ if (charCount <= ev.charIndex && charCount + spanText.length > ev.charIndex && spanText.trim().length > 0) {
469
+ span.style.backgroundColor = "#fef08a";
470
+ span.style.color = "#0f172a";
471
+ span.style.boxShadow = "0 0 0 2px #fef08a";
472
+ } else {
473
+ span.style.backgroundColor = "transparent";
474
+ span.style.color = "inherit";
475
+ span.style.boxShadow = "0 0 0 2px transparent";
476
+ }
477
+ charCount += spanText.length;
478
+ }
479
+ }
480
+ };
481
+ const restoreDOM = () => {
482
+ target.style.outline = originalOutline;
483
+ target.style.outlineOffset = originalOutlineOffset;
484
+ target.innerHTML = originalHTML;
485
+ };
486
+ utterance.onend = restoreDOM;
487
+ utterance.onerror = restoreDOM;
488
+ window.speechSynthesis.speak(utterance);
489
+ }
490
+ }
491
+ destroy() {
492
+ if (this.boundClickHandler) {
493
+ document.removeEventListener("click", this.boundClickHandler, { capture: true });
494
+ this.boundClickHandler = null;
495
+ }
496
+ if (typeof window !== "undefined" && "speechSynthesis" in window) {
497
+ window.speechSynthesis.cancel();
498
+ }
499
+ }
500
+ }
501
+ class CursorMagnetController {
502
+ constructor() {
503
+ __publicField(this, "active", false);
504
+ __publicField(this, "force", "suave");
505
+ __publicField(this, "cursorEl", null);
506
+ __publicField(this, "isMagnetized", false);
507
+ __publicField(this, "targetButton", null);
508
+ __publicField(this, "cachedButtons", []);
509
+ __publicField(this, "lastCacheTime", 0);
510
+ __publicField(this, "isBig", false);
511
+ __publicField(this, "boundMouseMove", null);
512
+ __publicField(this, "boundClick", null);
513
+ __publicField(this, "boundScroll", null);
514
+ }
515
+ update(active, force, isBig) {
516
+ this.active = active;
517
+ this.force = force;
518
+ this.isBig = isBig;
519
+ if (!active) {
520
+ this.teardown();
521
+ return;
522
+ }
523
+ if (!this.boundMouseMove) {
524
+ this.setup();
525
+ }
526
+ }
527
+ setup() {
528
+ this.boundMouseMove = this.handleMouseMove.bind(this);
529
+ this.boundClick = this.handleClick.bind(this);
530
+ this.boundScroll = () => {
531
+ this.lastCacheTime = 0;
532
+ };
533
+ window.addEventListener("mousemove", this.boundMouseMove, { passive: true });
534
+ window.addEventListener("click", this.boundClick, { capture: true });
535
+ window.addEventListener("scroll", this.boundScroll, { passive: true });
536
+ }
537
+ teardown() {
538
+ if (this.boundMouseMove) {
539
+ window.removeEventListener("mousemove", this.boundMouseMove);
540
+ this.boundMouseMove = null;
541
+ }
542
+ if (this.boundClick) {
543
+ window.removeEventListener("click", this.boundClick, { capture: true });
544
+ this.boundClick = null;
545
+ }
546
+ if (this.boundScroll) {
547
+ window.removeEventListener("scroll", this.boundScroll);
548
+ this.boundScroll = null;
549
+ }
550
+ document.documentElement.classList.remove("cursor-hidden");
551
+ this.isMagnetized = false;
552
+ this.targetButton = null;
553
+ if (this.cursorEl) {
554
+ this.cursorEl.remove();
555
+ this.cursorEl = null;
556
+ }
557
+ }
558
+ ensureCursorElement() {
559
+ if (!this.cursorEl) {
560
+ this.cursorEl = document.createElement("div");
561
+ this.cursorEl.id = "cognitvs-a11y-virtual-cursor";
562
+ this.cursorEl.setAttribute("aria-hidden", "true");
563
+ this.cursorEl.style.cssText = `
564
+ position: fixed;
565
+ pointer-events: none;
566
+ z-index: 999999;
567
+ transition: transform 75ms ease;
568
+ transform: translate(-5px, -5px);
569
+ filter: drop-shadow(0 0 10px rgba(0, 119, 212, 0.9));
570
+ `;
571
+ document.body.appendChild(this.cursorEl);
572
+ }
573
+ this.cursorEl.innerHTML = this.isBig ? `<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 32 32">
574
+ <path fill="#ffffff" stroke="#0077d4" stroke-width="3" stroke-linejoin="round" d="M2 2 L2 28 L9 22 L15 31 L19 29 L12 20 L21 20 Z" />
575
+ </svg>` : `<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 32 32">
576
+ <path fill="#ffffff" stroke="#0077d4" stroke-width="2.5" stroke-linejoin="round" d="M2 2 L2 28 L9 22 L15 31 L19 29 L12 20 L21 20 Z" />
577
+ </svg>`;
578
+ return this.cursorEl;
579
+ }
580
+ updateButtonCache() {
581
+ const now = Date.now();
582
+ if (now - this.lastCacheTime > 200) {
583
+ const elements = document.querySelectorAll('a, button, [role="button"], input[type="submit"], input[type="button"]');
584
+ this.cachedButtons = Array.from(elements).map((el) => ({
585
+ el,
586
+ rect: el.getBoundingClientRect()
587
+ }));
588
+ this.lastCacheTime = now;
589
+ }
590
+ }
591
+ handleMouseMove(e) {
592
+ if (!this.active) return;
593
+ const ATTRACTION_RADIUS = this.force === "forte" ? 45 : 25;
594
+ const ESCAPE_RADIUS = this.force === "forte" ? 60 : 40;
595
+ this.updateButtonCache();
596
+ const mouseX = e.clientX;
597
+ const mouseY = e.clientY;
598
+ let targetDistance = Infinity;
599
+ let targetCenter = { x: 0, y: 0 };
600
+ let closestDistance = Infinity;
601
+ let closestButton = null;
602
+ let closestCenter = { x: 0, y: 0 };
603
+ for (const { el } of this.cachedButtons) {
604
+ const currentRect = el.getBoundingClientRect();
605
+ const centerX = currentRect.left + currentRect.width / 2;
606
+ const centerY = currentRect.top + currentRect.height / 2;
607
+ const dxEdge = Math.max(currentRect.left - mouseX, 0, mouseX - currentRect.right);
608
+ const dyEdge = Math.max(currentRect.top - mouseY, 0, mouseY - currentRect.bottom);
609
+ const distanceToEdge = Math.sqrt(dxEdge * dxEdge + dyEdge * dyEdge);
610
+ if (distanceToEdge < closestDistance) {
611
+ closestDistance = distanceToEdge;
612
+ closestButton = el;
613
+ closestCenter = { x: centerX, y: centerY };
614
+ }
615
+ if (el === this.targetButton) {
616
+ targetDistance = distanceToEdge;
617
+ targetCenter = { x: centerX, y: centerY };
618
+ }
619
+ }
620
+ if (this.isMagnetized && this.targetButton) {
621
+ if (targetDistance > ESCAPE_RADIUS) {
622
+ this.isMagnetized = false;
623
+ this.targetButton = null;
624
+ document.documentElement.classList.remove("cursor-hidden");
625
+ if (closestDistance < ATTRACTION_RADIUS && closestButton) {
626
+ this.isMagnetized = true;
627
+ this.targetButton = closestButton;
628
+ this.setCursorPos(closestCenter.x, closestCenter.y);
629
+ document.documentElement.classList.add("cursor-hidden");
630
+ } else {
631
+ this.hideVirtualCursor();
632
+ }
633
+ } else {
634
+ this.setCursorPos(targetCenter.x, targetCenter.y);
635
+ }
636
+ } else {
637
+ if (closestDistance < ATTRACTION_RADIUS && closestButton) {
638
+ this.isMagnetized = true;
639
+ this.targetButton = closestButton;
640
+ this.setCursorPos(closestCenter.x, closestCenter.y);
641
+ document.documentElement.classList.add("cursor-hidden");
642
+ } else {
643
+ this.hideVirtualCursor();
644
+ }
645
+ }
646
+ }
647
+ setCursorPos(x, y) {
648
+ const el = this.ensureCursorElement();
649
+ el.style.display = "block";
650
+ el.style.left = `${x}px`;
651
+ el.style.top = `${y}px`;
652
+ }
653
+ hideVirtualCursor() {
654
+ if (this.cursorEl) {
655
+ this.cursorEl.style.display = "none";
656
+ }
657
+ document.documentElement.classList.remove("cursor-hidden");
658
+ }
659
+ handleClick(e) {
660
+ if (!this.active) return;
661
+ this.lastCacheTime = 0;
662
+ if (this.isMagnetized && this.targetButton) {
663
+ if (!this.targetButton.contains(e.target)) {
664
+ e.preventDefault();
665
+ e.stopPropagation();
666
+ this.targetButton.click();
667
+ }
668
+ }
669
+ }
670
+ destroy() {
671
+ this.teardown();
672
+ }
673
+ }
674
+ class ScrollAssistController {
675
+ constructor() {
676
+ __publicField(this, "containerEl", null);
677
+ __publicField(this, "boundScroll", null);
678
+ }
679
+ update(active) {
680
+ if (!active) {
681
+ this.teardown();
682
+ return;
683
+ }
684
+ if (!this.containerEl) {
685
+ this.setup();
686
+ }
687
+ this.checkScrollLimits();
688
+ }
689
+ setup() {
690
+ this.containerEl = document.createElement("div");
691
+ this.containerEl.id = "cognitvs-a11y-scroll-assist";
692
+ this.containerEl.setAttribute("aria-label", "Assistente de Rolagem");
693
+ this.containerEl.style.cssText = `
694
+ position: fixed;
695
+ bottom: 24px;
696
+ left: 24px;
697
+ z-index: 999990;
698
+ display: flex;
699
+ flex-direction: column;
700
+ gap: 12px;
701
+ pointer-events: auto;
702
+ `;
703
+ const upBtn = document.createElement("button");
704
+ upBtn.id = "cognitvs-scroll-up";
705
+ upBtn.setAttribute("aria-label", "Rolar para cima");
706
+ upBtn.title = "Rolar para cima";
707
+ upBtn.innerHTML = `
708
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
709
+ <polyline points="18 15 12 9 6 15"></polyline>
710
+ </svg>
711
+ `;
712
+ this.styleButton(upBtn, false);
713
+ upBtn.onclick = () => window.scrollBy({ top: -window.innerHeight * 0.7, behavior: "smooth" });
714
+ const downBtn = document.createElement("button");
715
+ downBtn.id = "cognitvs-scroll-down";
716
+ downBtn.setAttribute("aria-label", "Rolar para baixo");
717
+ downBtn.title = "Rolar para baixo";
718
+ downBtn.innerHTML = `
719
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
720
+ <polyline points="6 9 12 15 18 9"></polyline>
721
+ </svg>
722
+ `;
723
+ this.styleButton(downBtn, true);
724
+ downBtn.onclick = () => window.scrollBy({ top: window.innerHeight * 0.7, behavior: "smooth" });
725
+ this.containerEl.appendChild(upBtn);
726
+ this.containerEl.appendChild(downBtn);
727
+ document.body.appendChild(this.containerEl);
728
+ this.boundScroll = this.checkScrollLimits.bind(this);
729
+ window.addEventListener("scroll", this.boundScroll, { passive: true });
730
+ }
731
+ styleButton(btn, isPrimary) {
732
+ btn.style.cssText = `
733
+ width: 52px;
734
+ height: 52px;
735
+ border-radius: 16px;
736
+ border: 2px solid ${isPrimary ? "#0077d4" : "rgba(0, 119, 212, 0.4)"};
737
+ background: ${isPrimary ? "#0077d4" : "#0f172a"};
738
+ color: #ffffff;
739
+ display: flex;
740
+ align-items: center;
741
+ justify-content: center;
742
+ cursor: pointer;
743
+ box-shadow: 0 10px 25px rgba(0, 0, 0, 0.35);
744
+ backdrop-filter: blur(8px);
745
+ transition: all 0.2s ease;
746
+ font-weight: bold;
747
+ `;
748
+ btn.onmouseenter = () => {
749
+ btn.style.transform = "scale(1.08)";
750
+ btn.style.background = isPrimary ? "#0066b8" : "#1e293b";
751
+ };
752
+ btn.onmouseleave = () => {
753
+ btn.style.transform = "scale(1)";
754
+ btn.style.background = isPrimary ? "#0077d4" : "#0f172a";
755
+ };
756
+ }
757
+ checkScrollLimits() {
758
+ if (!this.containerEl) return;
759
+ const isAtTop = window.scrollY < 20;
760
+ const isAtBottom = Math.ceil(window.innerHeight + window.scrollY) >= document.documentElement.scrollHeight - 20;
761
+ const upBtn = this.containerEl.querySelector("#cognitvs-scroll-up");
762
+ const downBtn = this.containerEl.querySelector("#cognitvs-scroll-down");
763
+ if (upBtn) upBtn.style.opacity = isAtTop ? "0.4" : "1";
764
+ if (downBtn) downBtn.style.opacity = isAtBottom ? "0.4" : "1";
765
+ }
766
+ teardown() {
767
+ if (this.boundScroll) {
768
+ window.removeEventListener("scroll", this.boundScroll);
769
+ this.boundScroll = null;
770
+ }
771
+ if (this.containerEl) {
772
+ this.containerEl.remove();
773
+ this.containerEl = null;
774
+ }
775
+ }
776
+ destroy() {
777
+ this.teardown();
778
+ }
779
+ }
780
+ class VLibrasController {
781
+ constructor() {
782
+ __publicField(this, "active", false);
783
+ __publicField(this, "scriptLoaded", false);
784
+ }
785
+ update(active) {
786
+ this.active = active;
787
+ const windowWithLibras = window;
788
+ if (!active) {
789
+ if (typeof windowWithLibras.toggleVLibras === "function") {
790
+ windowWithLibras.toggleVLibras(false);
791
+ }
792
+ return;
793
+ }
794
+ if (this.scriptLoaded && typeof windowWithLibras.toggleVLibras === "function") {
795
+ windowWithLibras.toggleVLibras(true);
796
+ return;
797
+ }
798
+ this.loadVLibras();
799
+ }
800
+ loadVLibras() {
801
+ if (document.getElementById("vlibras-plugin-container")) {
802
+ return;
803
+ }
804
+ const div = document.createElement("div");
805
+ div.id = "vlibras-plugin-container";
806
+ div.innerHTML = `
807
+ <div vw class="enabled">
808
+ <div vw-access-button class="active"></div>
809
+ <div vw-plugin-wrapper>
810
+ <div class="vw-plugin-top-wrapper"></div>
811
+ </div>
812
+ </div>
813
+ `;
814
+ document.body.appendChild(div);
815
+ const script = document.createElement("script");
816
+ script.id = "vlibras-official-script";
817
+ script.src = "https://vlibras.gov.br/app/vlibras-plugin.js";
818
+ script.async = true;
819
+ script.onload = () => {
820
+ this.scriptLoaded = true;
821
+ const win = window;
822
+ if (win.VLibras && win.VLibras.Widget) {
823
+ new win.VLibras.Widget("https://vlibras.gov.br/app");
824
+ }
825
+ };
826
+ document.head.appendChild(script);
827
+ }
828
+ destroy() {
829
+ if (this.active) {
830
+ this.update(false);
831
+ }
832
+ }
833
+ }
834
+ const A11Y_ICON_SVG = (size = 32) => `
835
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
836
+ width="${size}" height="${size}" viewBox="0 0 539.7 501.1" aria-hidden="true">
837
+ <!-- Estrutura principal (braços, pernas, arco superior/inferior, texto nós) -->
838
+ <path fill="currentColor" d="M268,276.6c-18.8,28.2-37,55.4-55.3,82.7c-14.9,22.3-29.7,44.6-44.8,66.8c-2.1,3-2.4,4.8,0.3,7.9
839
+ c11.2,13,10.9,32.4-0.1,45.5c-11.3,13.4-30.3,16.7-45.3,7.9c-15-8.8-21.5-27-15.5-43.3c6-16.1,22.6-25.7,39.5-22.6
840
+ c2.9,0.5,4.4,0.1,6.1-2.4c32.7-49.1,65.4-98.1,98.2-147.1c0.6-0.9,1.1-1.9,1.9-3.2c-9.2-1.1-18.1-1.8-26.9-3.2
841
+ c-43.2-6.6-84.4-20-124.9-36c-13.8-5.4-27.3-11.4-40.9-17.3c-2.6-1.1-4.4-1-6.8,0.4c-15.2,8.8-34.2,5.3-45.2-8
842
+ c-11-13.2-11-32.3-0.1-45.6c11-13.3,29.8-16.8,44.8-8.5c15.2,8.4,22.1,26.4,16.3,42.9c-0.9,2.7-1.3,4.1,2,5.5
843
+ c45.8,19.7,92.3,37.1,141.3,47.4c38.9,8.2,77.4,6.5,116.1-0.9c48.7-9.3,95.8-24.1,142.4-40.8c3.9-1.4,3.7-2.5,1.9-5.8
844
+ c-10.2-18.2-2.4-41.3,16.5-49.8c19-8.5,41.4,1.1,48.3,20.7c7,19.9-4.5,41.3-24.9,46.5c-5.9,1.5-11.9,1.6-17.8-0.2
845
+ c-3.1-1-5.6-0.7-8.6,0.4c-48.2,18.1-97.1,33.8-147.7,43.8c-18.3,3.6-36.8,6.3-55.4,7.6c-0.9,1.9,0.7,2.8,1.5,3.9
846
+ c32.8,49.2,65.7,98.3,98.4,147.5c1.8,2.7,3.3,3.2,6.4,2.4c17.2-4.2,34.2,4.3,41.2,20.2c7,15.9,1.5,34.5-13,44.1
847
+ c-14.3,9.5-33.3,7.4-45.3-5c-12-12.4-13.4-31.6-3.1-45.9c1.3-1.9,2.8-3.1,0.9-5.9c-33.8-50.3-67.4-100.7-101.1-151.1
848
+ C268.8,278.1,268.6,277.7,268,276.6z"/>
849
+ <!-- Arco superior -->
850
+ <path fill="currentColor" d="M268.9,0c94.9,3.7,167.9,44.9,217.2,126.7c2.1,3.6,1.5,5.1-2,6.5c-3.4,1.4-6.8,4.9-9.7,4.4
851
+ c-3.1-0.5-4.1-5.6-6-8.7C376.9-21.2,158.6-20.3,68.3,130.7c-2.6,4.3-4.2,5.4-8.7,2.3c-8.6-5.8-9-5.7-3.3-14.3
852
+ C101.9,48.4,166.3,8.8,250,1.1C256.3,0.5,262.6,0.3,268.9,0z"/>
853
+ <!-- Cabeça (anel externo) -->
854
+ <path fill="currentColor" d="M200.5,135.2c0.2-39,31.7-70.4,70.8-70.3c38.9,0.1,70.5,31.8,70.4,70.7c-0.1,39.1-31.8,70.7-71,70.5
855
+ C231.8,206,200.3,174.2,200.5,135.2z M325.1,135.4c-0.1-29.9-24.3-54.1-54-54.1c-29.9,0-54.2,24.6-54.1,54.6
856
+ c0.1,29.7,24.6,53.9,54.4,53.7C301.2,189.5,325.2,165.4,325.1,135.4z"/>
857
+ <!-- Arco lateral esquerdo -->
858
+ <path fill="currentColor" d="M35.1,255.4c2,61.5,23.9,114.7,66.9,158.9c3,3.1,3.4,4.6,0.1,7.7c-8.3,7.9-8.2,8.2-15.9-0.2
859
+ C39,370.2,16.7,309.3,18.9,239.4c0.1-3.1,0.7-4.6,4.2-4.2c3.9,0.4,9.1-1.1,11.6,0.8c2.8,2.2,0.2,7.4,0.5,11.4
860
+ C35.3,250.1,35.1,252.7,35.1,255.4z"/>
861
+ <!-- Arco lateral direito -->
862
+ <path fill="currentColor" d="M518.9,252.8c-1.4,66.4-24.6,123.9-70.4,172.2c-2.7,2.8-4.4,3.1-6.9,0.1c-2.4-2.9-7.5-4.8-7.5-8.1
863
+ c0-2.7,4.3-5.4,6.8-8.1c43.4-48.2,63.8-104.7,61.2-169.5c-0.1-3.4,0.4-4.6,4.1-4.5c12.6,0.1,12.6,0,12.7,12.4
864
+ C518.9,249.2,518.9,251,518.9,252.8z"/>
865
+ <!-- Arco inferior -->
866
+ <path fill="currentColor" d="M268.6,501.1c-28.7,0-56.6-4.9-83.7-14.3c-3.5-1.2-5-2.5-2.9-6.2c1.9-3.3,1.7-8.7,4.8-10
867
+ c2.6-1.1,6.8,1.7,10.2,2.8c51.1,16,101.9,15.1,152.3-3.1c3.8-1.4,5.3-0.9,6.7,3c3.9,11,4.1,10.9-6.9,14.7
868
+ C323.3,496.7,296.3,501.1,268.6,501.1z"/>
869
+ <!-- Nós de destaque (azul ciano — mantido como identidade visual do ícone) -->
870
+ <circle fill="#44A5DD" cx="398" cy="456.5" r="19.3"/>
871
+ <circle fill="#44A5DD" cx="505.1" cy="181.9" r="19.3"/>
872
+ <circle fill="#44A5DD" cx="140.6" cy="456.6" r="19.1"/>
873
+ <circle fill="#44A5DD" cx="35.8" cy="181.8" r="19.2"/>
874
+ <!-- Cabeça (preenchimento interno azul) -->
875
+ <path fill="#44A5DD" d="M325.1,135.4c0.1,30-23.9,54.1-53.7,54.3c-29.8,0.1-54.3-24-54.4-53.7c-0.1-30,24.2-54.6,54.1-54.6
876
+ C300.8,81.4,325,105.6,325.1,135.4z"/>
877
+ </svg>
878
+ `.trim();
879
+ class CognitvsA11yWidget extends HTMLElement {
880
+ constructor() {
881
+ super();
882
+ __publicField(this, "shadow");
883
+ __publicField(this, "store");
884
+ __publicField(this, "domEffects");
885
+ __publicField(this, "tts");
886
+ __publicField(this, "cursorMagnet");
887
+ __publicField(this, "scrollAssist");
888
+ __publicField(this, "vlibras");
889
+ __publicField(this, "unsubscribe", null);
890
+ __publicField(this, "tourTimeoutId", null);
891
+ __publicField(this, "resizeRafId", null);
892
+ __publicField(this, "onViewportChange", () => {
893
+ if (this.resizeRafId) {
894
+ cancelAnimationFrame(this.resizeRafId);
895
+ }
896
+ this.resizeRafId = requestAnimationFrame(() => {
897
+ this.updatePanelDimensions();
898
+ });
899
+ });
900
+ this.shadow = this.attachShadow({ mode: "open" });
901
+ const prefix = this.getAttribute("storage-prefix") || "cognitvs";
902
+ this.store = new A11yStore(prefix);
903
+ this.domEffects = new DomEffectsManager();
904
+ this.tts = new TTSController((node) => this.contains(node) || this.shadow.contains(node));
905
+ this.cursorMagnet = new CursorMagnetController();
906
+ this.scrollAssist = new ScrollAssistController();
907
+ this.vlibras = new VLibrasController();
908
+ }
909
+ static get observedAttributes() {
910
+ return ["theme", "accent-color", "position", "storage-prefix", "show-banner"];
911
+ }
912
+ attributeChangedCallback(name, oldValue, newValue) {
913
+ if (oldValue === newValue) return;
914
+ if (name === "accent-color" && newValue) {
915
+ this.style.setProperty("--a11y-primary", newValue);
916
+ }
917
+ }
918
+ connectedCallback() {
919
+ this.injectGlobalStyles();
920
+ this.renderInitialShadow();
921
+ window.addEventListener("resize", this.onViewportChange, { passive: true });
922
+ window.addEventListener("scroll", this.onViewportChange, { passive: true });
923
+ const state = this.store.getState();
924
+ if (!state.hasInteracted) {
925
+ this.tourTimeoutId = window.setTimeout(() => {
926
+ if (!this.store.getState().isExpanded && !this.store.getState().hasInteracted) {
927
+ this.store.update({ showAttentionGrabber: true });
928
+ }
929
+ }, 3500);
930
+ }
931
+ this.unsubscribe = this.store.subscribe((currState) => {
932
+ this.domEffects.applyGlobalState(currState);
933
+ this.tts.update(currState.isTTSActive, currState.ttsRate);
934
+ this.cursorMagnet.update(currState.isCursorMagnetActive, currState.cursorMagnetForce, currState.isBigCursor);
935
+ this.scrollAssist.update(currState.isScrollAssistActive);
936
+ this.vlibras.update(currState.isVLibrasActive);
937
+ this.updateShadowUI(currState);
938
+ });
939
+ requestAnimationFrame(() => this.updatePanelDimensions());
940
+ setTimeout(() => this.updatePanelDimensions(), 50);
941
+ setTimeout(() => this.updatePanelDimensions(), 250);
942
+ }
943
+ disconnectedCallback() {
944
+ window.removeEventListener("resize", this.onViewportChange);
945
+ window.removeEventListener("scroll", this.onViewportChange);
946
+ if (this.resizeRafId) {
947
+ cancelAnimationFrame(this.resizeRafId);
948
+ }
949
+ document.body.classList.remove("a11y-banner-active");
950
+ if (this.unsubscribe) {
951
+ this.unsubscribe();
952
+ this.unsubscribe = null;
953
+ }
954
+ if (this.tourTimeoutId) {
955
+ clearTimeout(this.tourTimeoutId);
956
+ }
957
+ this.domEffects.destroy();
958
+ this.tts.destroy();
959
+ this.cursorMagnet.destroy();
960
+ this.scrollAssist.destroy();
961
+ this.vlibras.destroy();
962
+ }
963
+ injectGlobalStyles() {
964
+ const styleId = "cognitvs-a11y-global-styles";
965
+ if (!document.getElementById(styleId)) {
966
+ const style = document.createElement("style");
967
+ style.id = styleId;
968
+ style.textContent = globalCss;
969
+ document.head.appendChild(style);
970
+ }
971
+ }
972
+ renderInitialShadow() {
973
+ const showBanner = this.getAttribute("show-banner") !== "false";
974
+ const accent = this.getAttribute("accent-color");
975
+ if (accent) {
976
+ this.style.setProperty("--a11y-primary", accent);
977
+ }
978
+ this.shadow.innerHTML = `
979
+ <style>${widgetCss}</style>
980
+ ${showBanner ? `
981
+ <div id="a11y-top-banner" class="a11y-top-banner" role="banner">
982
+ <div class="a11y-banner-content">
983
+ <span class="a11y-banner-icon" aria-hidden="true">
984
+ ${A11Y_ICON_SVG(26)}
985
+ </span>
986
+ <p class="a11y-banner-text">
987
+ <strong>Acessibilidade:</strong> Personalize sua navegação clicando no botão no canto da tela.
988
+ </p>
989
+ </div>
990
+ <button id="banner-close-btn" class="a11y-banner-close" aria-label="Dispensar aviso de acessibilidade">✕</button>
991
+ </div>
992
+ ` : ""}
993
+
994
+ <div class="a11y-widget-root">
995
+ <!-- Painel Expansível -->
996
+ <div id="a11y-panel" class="a11y-panel closed" role="region" aria-label="Painel de Ferramentas de Acessibilidade">
997
+ <div class="a11y-panel-header">
998
+ <div class="a11y-panel-title">
999
+ ${A11Y_ICON_SVG(18)}
1000
+ ACESSIBILIDADE
1001
+ </div>
1002
+ <button id="panel-close-btn" class="a11y-btn-mini" style="flex:0; width:28px; padding:4px;" aria-label="Fechar painel">✕</button>
1003
+ </div>
1004
+
1005
+ <div>
1006
+ <label class="a11y-section-title" for="profile-select">Perfil Assistivo</label>
1007
+ <select id="profile-select" class="a11y-profile-select">
1008
+ <option value="all">Ver Todos os Recursos</option>
1009
+ <option value="vision">👁️ Visão Reduzida</option>
1010
+ <option value="hearing">🧏 Audição / Surdez</option>
1011
+ <option value="motor">🎯 Dificuldade Motora</option>
1012
+ <option value="cognitive">🧠 TDAH e Foco</option>
1013
+ </select>
1014
+ </div>
1015
+
1016
+ <div id="a11y-feature-container"></div>
1017
+
1018
+ <button id="reset-btn" class="a11y-reset-btn">Resetar Preferências</button>
1019
+ </div>
1020
+
1021
+ <!-- Botão Flutuante e Dica -->
1022
+ <div class="a11y-fab-wrapper">
1023
+ <div id="tour-bubble" class="a11y-tour-bubble" style="display: none;">
1024
+ <span style="font-size: 20px;">👋</span>
1025
+ <p style="font-size: 11px; font-weight: bold; line-height: 1.3;">Dificuldade para navegar? Clique aqui!</p>
1026
+ </div>
1027
+ <div id="ping-ring" class="a11y-ping-ring" style="display: none;"></div>
1028
+
1029
+ <button id="fab-btn" class="a11y-fab" aria-label="Abrir recursos de acessibilidade" aria-haspopup="true">
1030
+ ${A11Y_ICON_SVG(34)}
1031
+ </button>
1032
+ </div>
1033
+ </div>
1034
+ `;
1035
+ this.bindEvents();
1036
+ }
1037
+ bindEvents() {
1038
+ const fabBtn = this.shadow.querySelector("#fab-btn");
1039
+ const panelCloseBtn = this.shadow.querySelector("#panel-close-btn");
1040
+ const bannerCloseBtn = this.shadow.querySelector("#banner-close-btn");
1041
+ const tourBubble = this.shadow.querySelector("#tour-bubble");
1042
+ const resetBtn = this.shadow.querySelector("#reset-btn");
1043
+ const profileSelect = this.shadow.querySelector("#profile-select");
1044
+ fabBtn == null ? void 0 : fabBtn.addEventListener("click", () => this.store.toggleExpanded());
1045
+ panelCloseBtn == null ? void 0 : panelCloseBtn.addEventListener("click", () => this.store.toggleExpanded());
1046
+ tourBubble == null ? void 0 : tourBubble.addEventListener("click", () => this.store.toggleExpanded());
1047
+ bannerCloseBtn == null ? void 0 : bannerCloseBtn.addEventListener("click", () => this.store.dismissBanner());
1048
+ resetBtn == null ? void 0 : resetBtn.addEventListener("click", () => this.store.resetAll());
1049
+ profileSelect == null ? void 0 : profileSelect.addEventListener("change", (e) => {
1050
+ const val = e.target.value;
1051
+ this.store.setActiveProfile(val);
1052
+ });
1053
+ }
1054
+ updateShadowUI(state) {
1055
+ const banner = this.shadow.querySelector("#a11y-top-banner");
1056
+ if (banner) {
1057
+ if (state.bannerDismissed) {
1058
+ banner.classList.add("hidden");
1059
+ document.body.classList.remove("a11y-banner-active");
1060
+ document.documentElement.style.setProperty("--a11y-top-banner-height", "0px");
1061
+ } else {
1062
+ banner.classList.remove("hidden");
1063
+ document.body.classList.add("a11y-banner-active");
1064
+ }
1065
+ }
1066
+ const panel = this.shadow.querySelector("#a11y-panel");
1067
+ const fab = this.shadow.querySelector("#fab-btn");
1068
+ if (panel) {
1069
+ if (state.isExpanded) {
1070
+ panel.classList.remove("closed");
1071
+ } else {
1072
+ panel.classList.add("closed");
1073
+ }
1074
+ }
1075
+ if (fab) {
1076
+ fab.innerHTML = state.isExpanded ? `<span style="font-size:24px; font-weight:900;">✕</span>` : A11Y_ICON_SVG(34);
1077
+ }
1078
+ const tourBubble = this.shadow.querySelector("#tour-bubble");
1079
+ const pingRing = this.shadow.querySelector("#ping-ring");
1080
+ if (tourBubble) tourBubble.style.display = state.showAttentionGrabber ? "flex" : "none";
1081
+ if (pingRing) pingRing.style.display = state.showAttentionGrabber ? "block" : "none";
1082
+ const profileSelect = this.shadow.querySelector("#profile-select");
1083
+ if (profileSelect && profileSelect.value !== state.activeProfile) {
1084
+ profileSelect.value = state.activeProfile;
1085
+ }
1086
+ this.renderFeatureButtons(state);
1087
+ this.updatePanelDimensions();
1088
+ }
1089
+ updatePanelDimensions() {
1090
+ const panel = this.shadow.querySelector("#a11y-panel");
1091
+ const banner = this.shadow.querySelector("#a11y-top-banner");
1092
+ const root = this.shadow.querySelector(".a11y-widget-root");
1093
+ if (!panel || !root) return;
1094
+ let bannerHeight = 0;
1095
+ const isBannerDismissed = this.store.getState().bannerDismissed;
1096
+ if (banner && !isBannerDismissed) {
1097
+ bannerHeight = banner.getBoundingClientRect().height || banner.offsetHeight || 0;
1098
+ }
1099
+ document.documentElement.style.setProperty("--a11y-top-banner-height", `${bannerHeight}px`);
1100
+ if (bannerHeight > 0) {
1101
+ document.body.classList.add("a11y-banner-active");
1102
+ } else {
1103
+ document.body.classList.remove("a11y-banner-active");
1104
+ }
1105
+ let hostHeaderBottom = 0;
1106
+ const customHeaderH = parseFloat(
1107
+ getComputedStyle(document.documentElement).getPropertyValue("--a11y-header-height") || "0"
1108
+ );
1109
+ if (customHeaderH > 0) {
1110
+ hostHeaderBottom = customHeaderH;
1111
+ } else {
1112
+ const candidates = document.querySelectorAll(
1113
+ 'header, nav, [role="banner"], .header, .site-header, .navbar, .top-bar'
1114
+ );
1115
+ for (const el of candidates) {
1116
+ const style = window.getComputedStyle(el);
1117
+ if (style.position === "fixed" || style.position === "sticky") {
1118
+ const rect = el.getBoundingClientRect();
1119
+ if (rect.top <= 10 && rect.bottom > hostHeaderBottom && rect.bottom < window.innerHeight * 0.45) {
1120
+ hostHeaderBottom = rect.bottom;
1121
+ }
1122
+ }
1123
+ }
1124
+ }
1125
+ const rootRect = root.getBoundingClientRect();
1126
+ const bottomDist = Math.max(0, window.innerHeight - rootRect.bottom);
1127
+ const fabWrapper = this.shadow.querySelector(".a11y-fab-wrapper");
1128
+ const fabHeight = fabWrapper ? fabWrapper.offsetHeight : 56;
1129
+ const gap = 12;
1130
+ const PANEL_TOP_MARGIN = 24;
1131
+ const topObstacle = Math.max(bannerHeight, hostHeaderBottom);
1132
+ const totalTopReserved = topObstacle + PANEL_TOP_MARGIN;
1133
+ const availableHeight = window.innerHeight - totalTopReserved - bottomDist - fabHeight - gap;
1134
+ const finalMaxHeight = Math.max(160, Math.floor(availableHeight));
1135
+ panel.style.setProperty("--fab-panel-max-height", `${finalMaxHeight}px`);
1136
+ if (this.store.getState().isExpanded) {
1137
+ panel.style.maxHeight = `${finalMaxHeight}px`;
1138
+ } else {
1139
+ panel.style.maxHeight = "0px";
1140
+ }
1141
+ }
1142
+ renderFeatureButtons(state) {
1143
+ const container = this.shadow.querySelector("#a11y-feature-container");
1144
+ if (!container) return;
1145
+ const p = state.activeProfile;
1146
+ let html = "";
1147
+ if (["all", "vision", "cognitive"].includes(p)) {
1148
+ html += `<div class="a11y-section-title">Ajustes Visuais</div>`;
1149
+ html += `
1150
+ <div class="a11y-scale-box">
1151
+ <button id="btn-scale-down" class="a11y-scale-btn" aria-label="Diminuir texto">A-</button>
1152
+ <span class="a11y-scale-val">${Math.round(state.fontScale * 100)}%</span>
1153
+ <button id="btn-scale-up" class="a11y-scale-btn" aria-label="Aumentar texto">A+</button>
1154
+ </div>
1155
+ `;
1156
+ html += `
1157
+ <button id="btn-contrast" class="a11y-btn ${state.isHighContrast ? "active" : ""}">
1158
+ <span>${state.isHighContrast ? "☀️" : "👁️"}</span>
1159
+ <span>${state.isHighContrast ? "Contraste Ativo" : "Alto Contraste"}</span>
1160
+ </button>
1161
+ `;
1162
+ html += `
1163
+ <div style="display:flex; flex-direction:column; gap:4px; margin-top:4px;">
1164
+ <button id="btn-mask" class="a11y-btn ${state.isReadingMaskActive ? "active" : ""}">
1165
+ <span>${state.isReadingMaskActive ? "📖" : "📏"}</span>
1166
+ <span>${state.isReadingMaskActive ? "Máscara Ativa" : "Máscara de Leitura"}</span>
1167
+ </button>
1168
+ ${state.isReadingMaskActive ? `
1169
+ <div class="a11y-btn-group" style="padding-left:4px;">
1170
+ <button id="mask-mode-smart" class="a11y-btn-mini ${state.isReadingMaskSmart ? "active" : ""}">🧠 Inteligente</button>
1171
+ <button id="mask-mode-free" class="a11y-btn-mini ${!state.isReadingMaskSmart ? "active" : ""}">🆓 Livre</button>
1172
+ </div>
1173
+ ` : ""}
1174
+ </div>
1175
+ `;
1176
+ html += `
1177
+ <div style="display:flex; flex-direction:column; gap:4px; margin-top:4px;">
1178
+ <label class="a11y-section-title" style="margin:0;">Filtros de Daltonismo</label>
1179
+ <select id="select-daltonism" class="a11y-profile-select">
1180
+ <option value="none" ${state.colorBlindMode === "none" ? "selected" : ""}>Padrão (Desativado)</option>
1181
+ <option value="protanopia" ${state.colorBlindMode === "protanopia" ? "selected" : ""}>Protanopia</option>
1182
+ <option value="deuteranopia" ${state.colorBlindMode === "deuteranopia" ? "selected" : ""}>Deuteranopia</option>
1183
+ <option value="tritanopia" ${state.colorBlindMode === "tritanopia" ? "selected" : ""}>Tritanopia</option>
1184
+ <option value="monochromacy" ${state.colorBlindMode === "monochromacy" ? "selected" : ""}>Monocromia</option>
1185
+ </select>
1186
+ </div>
1187
+ `;
1188
+ }
1189
+ if (["all", "hearing", "cognitive"].includes(p)) {
1190
+ html += `<div class="a11y-section-title">Audição e Leitura</div>`;
1191
+ html += `
1192
+ <div style="display:flex; flex-direction:column; gap:4px;">
1193
+ <button id="btn-tts" class="a11y-btn ${state.isTTSActive ? "active" : ""}">
1194
+ <span>${state.isTTSActive ? "🔊" : "🔈"}</span>
1195
+ <span>${state.isTTSActive ? "Leitor Ativo (Clique no Texto)" : "Leitor de Tela"}</span>
1196
+ </button>
1197
+ ${state.isTTSActive ? `
1198
+ <div class="a11y-btn-group">
1199
+ <button id="tts-rate-05" class="a11y-btn-mini ${state.ttsRate === 0.5 ? "active" : ""}">🐢 0.5x</button>
1200
+ <button id="tts-rate-1" class="a11y-btn-mini ${state.ttsRate === 1 ? "active" : ""}">🚶 1.0x</button>
1201
+ <button id="tts-rate-15" class="a11y-btn-mini ${state.ttsRate === 1.5 ? "active" : ""}">🐇 1.5x</button>
1202
+ </div>
1203
+ ` : ""}
1204
+ </div>
1205
+ `;
1206
+ html += `
1207
+ <button id="btn-vlibras" class="a11y-btn ${state.isVLibrasActive ? "active" : ""}">
1208
+ <span>${state.isVLibrasActive ? "🤟" : "👐"}</span>
1209
+ <span>${state.isVLibrasActive ? "LIBRAS Ativo" : "Tradutor LIBRAS"}</span>
1210
+ </button>
1211
+ `;
1212
+ }
1213
+ if (["all", "motor", "vision"].includes(p)) {
1214
+ html += `<div class="a11y-section-title">Interação e Mobilidade</div>`;
1215
+ html += `
1216
+ <button id="btn-big-cursor" class="a11y-btn ${state.isBigCursor ? "active" : ""}">
1217
+ <span>${state.isBigCursor ? "↖️" : "🖱️"}</span>
1218
+ <span>${state.isBigCursor ? "Ponteiro Gigante Ativo" : "Ponteiro Gigante"}</span>
1219
+ </button>
1220
+ `;
1221
+ html += `
1222
+ <button id="btn-keyboard-focus" class="a11y-btn ${state.isKeyboardFocus ? "active" : ""}">
1223
+ <span>${state.isKeyboardFocus ? "🎯" : "⌨️"}</span>
1224
+ <span>${state.isKeyboardFocus ? "Foco Teclado Ativo" : "Foco por Teclado"}</span>
1225
+ </button>
1226
+ `;
1227
+ html += `
1228
+ <button id="btn-scroll-assist" class="a11y-btn ${state.isScrollAssistActive ? "active" : ""}">
1229
+ <span>${state.isScrollAssistActive ? "↕️" : "🖱️"}</span>
1230
+ <span>${state.isScrollAssistActive ? "Rolagem Assistida Ativa" : "Rolagem Assistida"}</span>
1231
+ </button>
1232
+ `;
1233
+ html += `
1234
+ <div style="display:flex; flex-direction:column; gap:4px;">
1235
+ <button id="btn-cursor-magnet" class="a11y-btn ${state.isCursorMagnetActive ? "active" : ""}">
1236
+ <span>🧲</span>
1237
+ <span>${state.isCursorMagnetActive ? "Ímã de Cursor Ativo" : "Ímã de Cursor"}</span>
1238
+ </button>
1239
+ ${state.isCursorMagnetActive ? `
1240
+ <div class="a11y-btn-group">
1241
+ <button id="magnet-force-suave" class="a11y-btn-mini ${state.cursorMagnetForce === "suave" ? "active" : ""}">🪶 Suave</button>
1242
+ <button id="magnet-force-forte" class="a11y-btn-mini ${state.cursorMagnetForce === "forte" ? "active" : ""}">🧲 Forte</button>
1243
+ </div>
1244
+ ` : ""}
1245
+ </div>
1246
+ `;
1247
+ html += `
1248
+ <button id="btn-highlight-links" class="a11y-btn ${state.isHighlightLinks ? "active" : ""}">
1249
+ <span>🔗</span>
1250
+ <span>${state.isHighlightLinks ? "Links Destacados" : "Destacar Links"}</span>
1251
+ </button>
1252
+ `;
1253
+ }
1254
+ if (["all", "cognitive"].includes(p)) {
1255
+ html += `<div class="a11y-section-title">Ajustes Cognitivos</div>`;
1256
+ html += `
1257
+ <button id="btn-reduce-motion" class="a11y-btn ${state.isReduceMotion ? "active" : ""}">
1258
+ <span>${state.isReduceMotion ? "⏸️" : "✨"}</span>
1259
+ <span>${state.isReduceMotion ? "Animações Pausadas" : "Pausar Animações"}</span>
1260
+ </button>
1261
+ `;
1262
+ html += `
1263
+ <button id="btn-dyslexia" class="a11y-btn ${state.isDyslexiaMode ? "active" : ""}">
1264
+ <span>${state.isDyslexiaMode ? "📖" : "🅰️"}</span>
1265
+ <span>${state.isDyslexiaMode ? "Fonte Dislexia Ativa" : "Ajuste para Dislexia"}</span>
1266
+ </button>
1267
+ `;
1268
+ html += `
1269
+ <div style="display:flex; flex-direction:column; gap:4px;">
1270
+ <label class="a11y-section-title" style="margin:0;">Espaçamento de Texto</label>
1271
+ <div class="a11y-btn-group">
1272
+ <button id="spacing-0" class="a11y-btn-mini ${state.textSpacingLevel === 0 ? "active" : ""}">Padrão</button>
1273
+ <button id="spacing-1" class="a11y-btn-mini ${state.textSpacingLevel === 1 ? "active" : ""}">Espaçado</button>
1274
+ <button id="spacing-2" class="a11y-btn-mini ${state.textSpacingLevel === 2 ? "active" : ""}">Largo</button>
1275
+ </div>
1276
+ </div>
1277
+ `;
1278
+ }
1279
+ container.innerHTML = html;
1280
+ this.bindFeatureButtons();
1281
+ }
1282
+ bindFeatureButtons() {
1283
+ var _a;
1284
+ const bind = (id, fn) => {
1285
+ var _a2;
1286
+ (_a2 = this.shadow.querySelector(`#${id}`)) == null ? void 0 : _a2.addEventListener("click", fn);
1287
+ };
1288
+ bind("btn-scale-down", () => this.store.updateFontScale(-0.1));
1289
+ bind("btn-scale-up", () => this.store.updateFontScale(0.1));
1290
+ bind("btn-contrast", () => this.store.toggleHighContrast());
1291
+ bind("btn-mask", () => this.store.toggleReadingMask());
1292
+ bind("mask-mode-smart", () => this.store.setReadingMaskSmart(true));
1293
+ bind("mask-mode-free", () => this.store.setReadingMaskSmart(false));
1294
+ bind("btn-tts", () => this.store.toggleTTS());
1295
+ bind("tts-rate-05", () => this.store.setTTSRate(0.5));
1296
+ bind("tts-rate-1", () => this.store.setTTSRate(1));
1297
+ bind("tts-rate-15", () => this.store.setTTSRate(1.5));
1298
+ bind("btn-vlibras", () => this.store.toggleVLibras());
1299
+ bind("btn-big-cursor", () => this.store.toggleBigCursor());
1300
+ bind("btn-keyboard-focus", () => this.store.toggleKeyboardFocus());
1301
+ bind("btn-scroll-assist", () => this.store.toggleScrollAssist());
1302
+ bind("btn-cursor-magnet", () => this.store.toggleCursorMagnet());
1303
+ bind("magnet-force-suave", () => this.store.setCursorMagnetForce("suave"));
1304
+ bind("magnet-force-forte", () => this.store.setCursorMagnetForce("forte"));
1305
+ bind("btn-highlight-links", () => this.store.toggleHighlightLinks());
1306
+ bind("btn-reduce-motion", () => this.store.toggleReduceMotion());
1307
+ bind("btn-dyslexia", () => this.store.toggleDyslexia());
1308
+ bind("spacing-0", () => this.store.setTextSpacing(0));
1309
+ bind("spacing-1", () => this.store.setTextSpacing(1));
1310
+ bind("spacing-2", () => this.store.setTextSpacing(2));
1311
+ (_a = this.shadow.querySelector("#select-daltonism")) == null ? void 0 : _a.addEventListener("change", (e) => {
1312
+ const val = e.target.value;
1313
+ this.store.setColorBlindMode(val);
1314
+ });
1315
+ }
1316
+ }
1317
+ if (typeof window !== "undefined" && !customElements.get("cognitvs-a11y")) {
1318
+ customElements.define("cognitvs-a11y", CognitvsA11yWidget);
1319
+ }
1320
+ export {
1321
+ A11yStore,
1322
+ CognitvsA11yWidget,
1323
+ CognitvsA11yWidget as default
1324
+ };
1325
+ //# sourceMappingURL=a11y-widget.js.map