/**
 * Wally Monitor - Quick Fix Engine
 * Provides actionable fix suggestions for accessibility violations.
 * Generates code snippets the user can copy or auto-apply (editor context).
 *
 * @module WallyMonitor.QuickFix
 * @since 1.1.0
 */
(function($) {
    'use strict';

    window.WallyMonitor = window.WallyMonitor || {};
    WallyMonitor.QuickFix = WallyMonitor.QuickFix || {};

    // ─── Fix Registry ───────────────────────────────────────────────────
    // Maps violation rule IDs to fix generators.
    // Each entry: { label, icon, getSuggestion(node, violation) → { description, code, autoFix? } }

    var fixes = {};

    // ── Images ──────────────────────────────────────────────────────────

    fixes['image-alt'] = {
        label: 'Adicionar texto alternativo',
        icon: '🖼️',
        getSuggestion: function(node) {
            var src = extractAttr(node.html, 'src') || 'imagem.jpg';
            var filename = src.split('/').pop().split('?')[0].replace(/[-_]/g, ' ').replace(/\.\w+$/, '');
            return {
                description: 'Adicione um atributo alt descrevendo o conteúdo da imagem.',
                code: '<img src="' + escSnippet(src) + '" alt="' + capitalize(filename) + '">',
                autoFixAttr: { 'alt': capitalize(filename) }
            };
        }
    };

    fixes['image-redundant-alt'] = {
        label: 'Remover texto redundante',
        icon: '🖼️',
        getSuggestion: function(node) {
            return {
                description: 'Remova palavras como "imagem de", "foto de" do atributo alt — o leitor de tela já anuncia que é uma imagem.',
                code: '<!-- Antes: alt="imagem do produto" -->\n<!-- Depois: alt="produto X em embalagem azul" -->'
            };
        }
    };

    fixes['wally-empty-alt-images'] = fixes['image-alt'];

    // ── Buttons & Links ─────────────────────────────────────────────────

    fixes['button-name'] = {
        label: 'Adicionar texto ao botão',
        icon: '🔘',
        getSuggestion: function(node) {
            return {
                description: 'Botões precisam de texto visível ou aria-label.',
                code: '<!-- Opção 1: Texto visível -->\n<button>Enviar formulário</button>\n\n<!-- Opção 2: Ícone com aria-label -->\n<button aria-label="Fechar menu">✕</button>',
                autoFixAttr: { 'aria-label': 'Descreva a ação do botão' }
            };
        }
    };

    fixes['wally-empty-buttons'] = fixes['button-name'];

    fixes['link-name'] = {
        label: 'Adicionar texto ao link',
        icon: '🔗',
        getSuggestion: function(node) {
            var href = extractAttr(node.html, 'href') || '#';
            return {
                description: 'Links precisam de texto descritivo do destino.',
                code: '<a href="' + escSnippet(href) + '">Texto descritivo do destino</a>\n\n<!-- Ou para ícones: -->\n<a href="' + escSnippet(href) + '" aria-label="Ir para página X"><svg>...</svg></a>'
            };
        }
    };

    fixes['nbr-link-purpose'] = {
        label: 'Melhorar texto do link',
        icon: '🔗',
        getSuggestion: function(node) {
            return {
                description: 'Evite textos genéricos como "clique aqui" ou "saiba mais". O texto do link deve descrever o destino.',
                code: '<!-- ❌ Errado -->\n<a href="/planos">Clique aqui</a>\n\n<!-- ✅ Correto -->\n<a href="/planos">Ver nossos planos e preços</a>'
            };
        }
    };

    // ── Forms ────────────────────────────────────────────────────────────

    fixes['label'] = {
        label: 'Associar label ao campo',
        icon: '📝',
        getSuggestion: function(node) {
            var id = extractAttr(node.html, 'id') || 'meu-campo';
            var type = extractAttr(node.html, 'type') || 'text';
            return {
                description: 'Todo campo de formulário precisa de um label associado.',
                code: '<label for="' + escSnippet(id) + '">Nome do campo</label>\n<input type="' + escSnippet(type) + '" id="' + escSnippet(id) + '" name="' + escSnippet(id) + '">',
                autoFixAttr: { 'aria-label': 'Descreva o campo' }
            };
        }
    };

    fixes['select-name'] = fixes['label'];

    fixes['nbr-identify-input-purpose'] = {
        label: 'Adicionar autocomplete',
        icon: '📝',
        getSuggestion: function(node) {
            var type = extractAttr(node.html, 'type') || 'text';
            var name = (extractAttr(node.html, 'name') || '').toLowerCase();
            var autocomplete = guessAutocomplete(name, type);
            return {
                description: 'Campos de dados pessoais devem ter autocomplete para preencher automaticamente.',
                code: '<input type="' + escSnippet(type) + '" name="' + escSnippet(name) + '" autocomplete="' + autocomplete + '">',
                autoFixAttr: { 'autocomplete': autocomplete }
            };
        }
    };

    fixes['nbr-error-identification'] = {
        label: 'Adicionar mensagem de erro',
        icon: '⚠️',
        getSuggestion: function(node) {
            var id = extractAttr(node.html, 'id') || 'campo';
            return {
                description: 'Campos obrigatórios precisam de mensagem de erro acessível.',
                code: '<input id="' + escSnippet(id) + '" aria-describedby="' + escSnippet(id) + '-error" required>\n<span id="' + escSnippet(id) + '-error" role="alert" class="error-msg">Este campo é obrigatório</span>'
            };
        }
    };

    fixes['nbr-error-suggestion'] = {
        label: 'Descrever formato esperado',
        icon: '📋',
        getSuggestion: function(node) {
            return {
                description: 'Campos com validação (pattern, type) precisam descrever o formato esperado.',
                code: '<input type="email" aria-describedby="email-hint">\n<span id="email-hint">Formato: usuario@dominio.com</span>'
            };
        }
    };

    // ── Headings ─────────────────────────────────────────────────────────

    fixes['empty-heading'] = {
        label: 'Preencher título vazio',
        icon: '📑',
        getSuggestion: function(node) {
            var tag = (node.html.match(/<(h[1-6])/i) || [, 'h2'])[1];
            return {
                description: 'Títulos vazios confundem leitores de tela. Adicione texto ou remova o elemento.',
                code: '<' + tag + '>Título descritivo da seção</' + tag + '>',
                autoFixAction: 'fill-text'
            };
        }
    };

    fixes['heading-order'] = {
        label: 'Corrigir hierarquia de títulos',
        icon: '📑',
        getSuggestion: function(node) {
            return {
                description: 'Títulos devem seguir ordem hierárquica (H1→H2→H3). Não pule níveis.',
                code: '<!-- ❌ Errado: H1 → H4 -->\n<h1>Página</h1>\n<h4>Seção</h4>\n\n<!-- ✅ Correto: H1 → H2 -->\n<h1>Página</h1>\n<h2>Seção</h2>'
            };
        }
    };

    fixes['wally-heading-hierarchy'] = fixes['heading-order'];

    // ── ARIA & Semantics ────────────────────────────────────────────────

    fixes['nbr-status-messages'] = {
        label: 'Adicionar role de status',
        icon: '📢',
        getSuggestion: function(node) {
            return {
                description: 'Mensagens de feedback (alertas, notificações) precisam de role para leitores de tela.',
                code: '<!-- Alertas urgentes -->\n<div role="alert">Erro: campo obrigatório não preenchido.</div>\n\n<!-- Status não-urgentes -->\n<div role="status" aria-live="polite">3 resultados encontrados.</div>'
            };
        }
    };

    fixes['nbr-section-headings'] = {
        label: 'Adicionar título à seção',
        icon: '📑',
        getSuggestion: function(node) {
            return {
                description: 'Seções (<section>, <article>) devem ter título para identificação.',
                code: '<section aria-labelledby="sec-titulo">\n  <h2 id="sec-titulo">Nome da Seção</h2>\n  <p>Conteúdo...</p>\n</section>'
            };
        }
    };

    fixes['nbr-skip-navigation'] = {
        label: 'Adicionar link "Pular navegação"',
        icon: '⏭️',
        getSuggestion: function() {
            return {
                description: 'Adicione um link de pular navegação como primeiro elemento focável.',
                code: '<!-- Adicionar logo após <body> -->\n<a href="#main-content" class="skip-link">\n  Pular para o conteúdo principal\n</a>\n\n<!-- E no conteúdo principal: -->\n<main id="main-content">\n  ...\n</main>\n\n<!-- CSS: -->\n<style>\n.skip-link {\n  position: absolute;\n  top: -40px;\n  left: 0;\n  padding: 8px 16px;\n  z-index: 10000;\n  background: #0E4A70;\n  color: #fff;\n}\n.skip-link:focus {\n  top: 0;\n}\n</style>'
            };
        }
    };

    fixes['nbr-focus-order'] = {
        label: 'Remover tabindex positivo',
        icon: '⌨️',
        getSuggestion: function(node) {
            return {
                description: 'tabindex positivo quebra a ordem natural de navegação. Use 0 ou -1.',
                code: '<!-- ❌ Errado -->\n<button tabindex="5">Botão</button>\n\n<!-- ✅ Correto (ordem natural) -->\n<button tabindex="0">Botão</button>\n\n<!-- ✅ Para remover do fluxo -->\n<div tabindex="-1">Focável por script</div>',
                autoFixAttr: { 'tabindex': '0' }
            };
        }
    };

    // ── Language ─────────────────────────────────────────────────────────

    fixes['html-has-lang'] = {
        label: 'Adicionar idioma à página',
        icon: '🌐',
        getSuggestion: function() {
            return {
                description: 'O elemento <html> precisa de atributo lang para definir o idioma.',
                code: '<html lang="pt-BR">\n  ...\n</html>'
            };
        }
    };

    fixes['nbr-lang-attribute'] = fixes['html-has-lang'];

    fixes['nbr-lang-parts'] = {
        label: 'Marcar idioma de trechos',
        icon: '🌐',
        getSuggestion: function() {
            return {
                description: 'Trechos em outro idioma devem usar o atributo lang.',
                code: '<p>O conceito de <span lang="en">responsive design</span> é essencial.</p>'
            };
        }
    };

    // ── Multimedia ───────────────────────────────────────────────────────

    fixes['nbr-audio-autoplay'] = {
        label: 'Desabilitar autoplay',
        icon: '🔊',
        getSuggestion: function(node) {
            var isVideo = node.html.indexOf('<video') !== -1;
            return {
                description: 'Mídia com autoplay deve estar muted ou não ter autoplay.',
                code: isVideo ?
                    '<video autoplay muted>\n  <source src="video.mp4" type="video/mp4">\n</video>' :
                    '<!-- Remova autoplay ou adicione controles -->\n<audio controls>\n  <source src="audio.mp3" type="audio/mpeg">\n</audio>',
                autoFixAttr: { 'muted': '' }
            };
        }
    };

    // ── Contrast & Visual ───────────────────────────────────────────────

    fixes['color-contrast'] = {
        label: 'Corrigir contraste de cores',
        icon: '🎨',
        getSuggestion: function(node) {
            return {
                description: 'O contraste texto/fundo deve ser ≥ 4.5:1 (texto normal) ou ≥ 3:1 (texto grande 18px+).',
                code: '/* Exemplos de combinações com bom contraste: */\n\n/* Fundo claro → texto escuro */\ncolor: #1a1a1a; /* sobre #ffffff = 17.4:1 ✅ */\ncolor: #0E4A70; /* sobre #ffffff = 8.6:1 ✅ */\n\n/* Fundo escuro → texto claro */\ncolor: #ffffff; /* sobre #0E2336 = 15.2:1 ✅ */\ncolor: #38D39F; /* sobre #0E2336 = 7.1:1 ✅ */'
            };
        }
    };

    fixes['nbr-text-spacing'] = {
        label: 'Ajustar espaçamento de texto',
        icon: '📏',
        getSuggestion: function() {
            return {
                description: 'O texto deve suportar ajustes de espaçamento sem perda de conteúdo.',
                code: '/* Espaçamentos mínimos recomendados: */\nbody {\n  line-height: 1.5;        /* ≥ 1.5x tamanho fonte */\n  letter-spacing: 0.12em;  /* ≥ 0.12x tamanho fonte */\n  word-spacing: 0.16em;    /* ≥ 0.16x tamanho fonte */\n}\np {\n  margin-bottom: 2em;      /* ≥ 2x tamanho fonte */\n}'
            };
        }
    };

    fixes['nbr-text-resize'] = {
        label: 'Usar unidades relativas',
        icon: '📏',
        getSuggestion: function() {
            return {
                description: 'Use rem/em em vez de px para font-size, permitindo que o texto seja redimensionado.',
                code: '/* ❌ Errado: unidade fixa */\nfont-size: 14px;\n\n/* ✅ Correto: unidade relativa */\nfont-size: 0.875rem; /* = 14px quando root = 16px */\nfont-size: 1em;      /* herda do pai */'
            };
        }
    };

    // ── Target Size & Touch ─────────────────────────────────────────────

    fixes['nbr-target-size'] = {
        label: 'Aumentar área de toque',
        icon: '👆',
        getSuggestion: function(node) {
            return {
                description: 'Elementos interativos devem ter no mínimo 24×24px (WCAG AA) ou 44×44px (AAA).',
                code: '/* Tamanho mínimo para botões e links: */\nbutton, a, input, select {\n  min-width: 44px;\n  min-height: 44px;\n  padding: 8px 16px;\n}\n\n/* Para ícones pequenos, use padding: */\n.icon-button {\n  padding: 10px;\n  min-width: 44px;\n  min-height: 44px;\n}'
            };
        }
    };

    // ── CAPTCHA ──────────────────────────────────────────────────────────

    fixes['nbr-accessible-auth'] = {
        label: 'Tornar CAPTCHA acessível',
        icon: '🤖',
        getSuggestion: function() {
            return {
                description: 'CAPTCHAs visuais são barreiras para leitores de tela. Ofereça alternativa.',
                code: '<!-- Use reCAPTCHA v3 (invisível) em vez de v2: -->\n<script src="https://www.google.com/recaptcha/api.js?render=YOUR_KEY"></script>\n\n<!-- Ou adicione alternativa de áudio: -->\n<div class="g-recaptcha" data-sitekey="KEY" data-size="normal"></div>\n<p>Dificuldade com CAPTCHA? <a href="/contato">Entre em contato</a></p>'
            };
        }
    };

    // ── Tables ───────────────────────────────────────────────────────────

    fixes['td-has-header'] = {
        label: 'Adicionar cabeçalhos à tabela',
        icon: '📊',
        getSuggestion: function() {
            return {
                description: 'Tabelas de dados precisam de cabeçalhos (<th>) para serem compreensíveis.',
                code: '<table>\n  <caption>Descrição da tabela</caption>\n  <thead>\n    <tr>\n      <th scope="col">Coluna 1</th>\n      <th scope="col">Coluna 2</th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <td>Dado 1</td>\n      <td>Dado 2</td>\n    </tr>\n  </tbody>\n</table>'
            };
        }
    };

    fixes['wally-table-accessibility'] = fixes['td-has-header'];

    // ── Carousels & Animation ───────────────────────────────────────────

    fixes['nbr-pause-stop'] = {
        label: 'Adicionar controle de pausa',
        icon: '⏸️',
        getSuggestion: function() {
            return {
                description: 'Carrosséis e animações automáticas devem ter botão de pausa.',
                code: '/* Respeite prefers-reduced-motion: */\n@media (prefers-reduced-motion: reduce) {\n  * {\n    animation-duration: 0.01ms !important;\n    transition-duration: 0.01ms !important;\n  }\n}\n\n/* Para carrosséis, adicione botão: */\n<button aria-label="Pausar carrossel" class="carousel-pause">⏸</button>'
            };
        }
    };

    // ── Timing ──────────────────────────────────────────────────────────

    fixes['nbr-timing-adjustable'] = {
        label: 'Remover redirecionamento automático',
        icon: '⏱️',
        getSuggestion: function() {
            return {
                description: 'Meta refresh redireciona sem consentimento. Use link ou botão.',
                code: '<!-- ❌ Errado -->\n<meta http-equiv="refresh" content="5;url=/nova-pagina">\n\n<!-- ✅ Correto -->\n<p>Esta página foi movida. <a href="/nova-pagina">Ir para a nova página</a></p>'
            };
        }
    };

    // ── Drag & Drop ─────────────────────────────────────────────────────

    fixes['nbr-dragging-alternative'] = {
        label: 'Adicionar alternativa ao drag',
        icon: '🖱️',
        getSuggestion: function() {
            return {
                description: 'Operações de arrastar devem ter alternativa por clique ou teclado.',
                code: '<!-- Adicione botões de ação alternativos: -->\n<div class="sortable-item">\n  <span>Item 1</span>\n  <button aria-label="Mover item 1 para cima">↑</button>\n  <button aria-label="Mover item 1 para baixo">↓</button>\n</div>'
            };
        }
    };

    // ── Focus Visible ───────────────────────────────────────────────────

    fixes['nbr-focus-visible'] = {
        label: 'Restaurar indicador de foco',
        icon: '🎯',
        getSuggestion: function() {
            return {
                description: 'Nunca remova outline:none sem substituto. Focáveis devem ter indicador visível.',
                code: '/* ❌ Errado */\n*:focus { outline: none; }\n\n/* ✅ Correto */\n*:focus-visible {\n  outline: 3px solid #9361FF;\n  outline-offset: 2px;\n  border-radius: 2px;\n}'
            };
        }
    };

    // ── Abbreviations ───────────────────────────────────────────────────

    fixes['nbr-abbreviations'] = {
        label: 'Usar tag <abbr>',
        icon: '📖',
        getSuggestion: function() {
            return {
                description: 'Abreviações devem usar <abbr> com título expandido na primeira ocorrência.',
                code: '<p>O <abbr title="World Wide Web Consortium">W3C</abbr> publicou as\ndiretrizes <abbr title="Web Content Accessibility Guidelines">WCAG</abbr> 2.2.</p>'
            };
        }
    };

    // ── Page Title ──────────────────────────────────────────────────────

    fixes['nbr-page-titled'] = {
        label: 'Melhorar título da página',
        icon: '📄',
        getSuggestion: function() {
            return {
                description: 'O título deve ser descritivo e único para cada página.',
                code: '<!-- ❌ Genérico -->\n<title>Home</title>\n\n<!-- ✅ Descritivo -->\n<title>Planos e Preços - Equallyze Acessibilidade Digital</title>'
            };
        }
    };

    // ── Content on Hover ────────────────────────────────────────────────

    fixes['nbr-content-on-hover'] = {
        label: 'Substituir title por tooltip acessível',
        icon: '💬',
        getSuggestion: function() {
            return {
                description: 'Tooltips via title não são acessíveis. Use aria-describedby ou tooltips customizados.',
                code: '<!-- ❌ Errado: title -->\n<span title="Explicação longa">Texto</span>\n\n<!-- ✅ Correto: tooltip com aria -->\n<span aria-describedby="tip1">Texto</span>\n<div id="tip1" role="tooltip" class="tooltip">Explicação longa</div>'
            };
        }
    };

    // ── Non-Text Contrast ───────────────────────────────────────────────

    fixes['nbr-non-text-contrast'] = {
        label: 'Adicionar borda visível ao campo',
        icon: '🔲',
        getSuggestion: function() {
            return {
                description: 'Campos de formulário devem ter bordas com contraste ≥ 3:1 contra o fundo.',
                code: '/* ❌ Sem borda visível */\ninput { border: 1px solid transparent; }\n\n/* ✅ Borda com contraste adequado */\ninput {\n  border: 1px solid #767676; /* 4.5:1 contra branco */\n  border-radius: 4px;\n}'
            };
        }
    };

    // ── Three Flashes ───────────────────────────────────────────────────

    fixes['nbr-three-flashes'] = {
        label: 'Reduzir velocidade de animação',
        icon: '⚡',
        getSuggestion: function() {
            return {
                description: 'Animações muito rápidas podem causar convulsões. Reduza a frequência de flash.',
                code: '/* ❌ Animação perigosa (pisca rápido) */\n@keyframes flash { 0%,50% {opacity:0} 50%,100% {opacity:1} }\n.alert { animation: flash 0.2s infinite; }\n\n/* ✅ Animação segura */\n.alert { animation: fadeIn 1s ease-in; }\n@media (prefers-reduced-motion: reduce) {\n  .alert { animation: none; }\n}'
            };
        }
    };

    // ── Character Key Shortcuts ─────────────────────────────────────────

    fixes['nbr-character-key-shortcuts'] = {
        label: 'Adicionar tecla modificadora ao atalho',
        icon: '⌨️',
        getSuggestion: function(node) {
            var key = extractAttr(node.html, 'accesskey');
            return {
                description: 'Atalhos de tecla única conflitam com tecnologias assistivas. Use tecla modificadora.',
                code: '<!-- ❌ Tecla única -->\n<button accesskey="' + escSnippet(key) + '">Ação</button>\n\n<!-- ✅ Remover accesskey e usar JS com modificador -->\n<button data-shortcut="Ctrl+' + escSnippet(key.toUpperCase()) + '">Ação</button>\n<script>// Usar Ctrl+' + escSnippet(key.toUpperCase()) + ' via addEventListener</script>'
            };
        }
    };

    // ── Error Prevention ────────────────────────────────────────────────

    fixes['nbr-error-prevention'] = {
        label: 'Adicionar confirmação antes de enviar',
        icon: '✋',
        getSuggestion: function() {
            return {
                description: 'Formulários com dados financeiros/legais devem ter mecanismo de confirmação.',
                code: '<!-- ❌ Sem confirmação -->\n<form action="/payment">\n  <button type="submit">Pagar</button>\n</form>\n\n<!-- ✅ Com etapa de confirmação -->\n<form action="/payment">\n  <label>\n    <input type="checkbox" required>\n    Confirmo que os dados estão corretos\n  </label>\n  <button type="submit">Confirmar Pagamento</button>\n</form>'
            };
        }
    };

    // ── Mobile Menu ARIA ────────────────────────────────────────────────

    fixes['nbr-mobile-menu-aria'] = {
        label: 'Adicionar ARIA ao menu mobile',
        icon: '📱',
        getSuggestion: function() {
            return {
                description: 'Toggle de menu mobile deve ter aria-expanded, aria-controls e aria-label.',
                code: '<!-- ❌ Sem ARIA -->\n<button class="menu-toggle">☰</button>\n\n<!-- ✅ Com ARIA completo -->\n<button class="menu-toggle"\n  aria-expanded="false"\n  aria-controls="main-menu"\n  aria-label="Menu de navegação">\n  ☰\n</button>\n<nav id="main-menu" hidden>...</nav>',
                autoFixAttr: {
                    'aria-expanded': 'false',
                    'aria-label': 'Menu de navegação'
                }
            };
        }
    };

    // ── Missing NBR QuickFixes (21 regras) ─────────────────────────────

    fixes['nbr-focus-not-obscured'] = {
        label: 'Evitar sobreposição de foco',
        icon: '🎯',
        getSuggestion: function() {
            return {
                description: 'Elementos fixos/sticky com z-index alto podem cobrir o elemento focado. Reduza z-index ou adicione scroll-padding.',
                code: '/* ❌ Barra fixa cobre conteúdo focado */\n.sticky-bar { position: fixed; z-index: 9999; }\n\n/* ✅ Adicione scroll-padding para compensar */\nhtml { scroll-padding-top: 80px; }\n\n/* ✅ Ou reduza z-index */\n.sticky-bar { z-index: 100; }'
            };
        }
    };

    fixes['nbr-focus-appearance'] = {
        label: 'Garantir indicador de foco visível',
        icon: '🎯',
        getSuggestion: function() {
            return {
                description: 'Elementos focáveis devem ter indicador de foco com área ≥ 2px e contraste ≥ 3:1.',
                code: '/* ✅ Indicador de foco acessível */\n:focus-visible {\n  outline: 3px solid #9361FF;\n  outline-offset: 2px;\n}\n\n/* Nunca remova outline sem substituto: */\n/* ❌ *:focus { outline: none; } */'
            };
        }
    };

    fixes['nbr-consistent-help'] = {
        label: 'Manter ajuda em posição consistente',
        icon: '❓',
        getSuggestion: function() {
            return {
                description: 'Links de ajuda/contato/suporte devem aparecer na mesma posição em todas as páginas.',
                code: '<!-- ✅ Ajuda no footer de todas as páginas -->\n<footer>\n  <a href="/ajuda">Central de Ajuda</a>\n  <a href="/contato">Fale Conosco</a>\n</footer>'
            };
        }
    };

    fixes['nbr-redundant-entry'] = {
        label: 'Evitar reentrada de dados',
        icon: '♻️',
        getSuggestion: function() {
            return {
                description: 'Dados já fornecidos pelo usuário devem ser preenchidos automaticamente em etapas seguintes.',
                code: '<!-- ✅ Preencha dados de etapas anteriores -->\n<input type="email" name="email"\n  value="<?php echo esc_attr($user_email); ?>"\n  autocomplete="email">'
            };
        }
    };

    fixes['nbr-reading-level'] = {
        label: 'Simplificar linguagem do texto',
        icon: '📖',
        getSuggestion: function() {
            return {
                description: 'O texto deve ser compreensível para leitores com nível de ensino fundamental. Evite jargões e frases longas.',
                code: '<!-- ❌ Complexo -->\n<p>A implementação da funcionalidade supracitada\nrequer parametrização adequada.</p>\n\n<!-- ✅ Simples -->\n<p>Para usar essa função, ajuste as configurações.</p>'
            };
        }
    };

    fixes['nbr-keyboard-trap'] = {
        label: 'Eliminar armadilha de teclado',
        icon: '⌨️',
        getSuggestion: function() {
            return {
                description: 'O foco do teclado deve poder sair de qualquer componente. Modais devem fechar com Esc.',
                code: '/* ✅ Modal que fecha com Esc */\ndialog.addEventListener("keydown", function(e) {\n  if (e.key === "Escape") {\n    dialog.close();\n    triggerButton.focus();\n  }\n});'
            };
        }
    };

    fixes['nbr-pointer-cancellation'] = {
        label: 'Usar click em vez de mousedown',
        icon: '🖱️',
        getSuggestion: function() {
            return {
                description: 'Ações devem ser ativadas em mouseup/click, não em mousedown, permitindo cancelamento.',
                code: '<!-- ❌ Ação em mousedown (sem cancelamento) -->\n<button onmousedown="deleteItem()">Excluir</button>\n\n<!-- ✅ Ação em click (permite cancelar arrastando para fora) -->\n<button onclick="deleteItem()">Excluir</button>'
            };
        }
    };

    fixes['nbr-images-of-text'] = {
        label: 'Substituir imagem de texto por HTML',
        icon: '🖼️',
        getSuggestion: function() {
            return {
                description: 'Use texto HTML real em vez de imagens contendo texto. Texto em imagem não é acessível nem selecionável.',
                code: '<!-- ❌ Texto em imagem -->\n<img src="banner-promo.jpg" alt="50% de desconto em todos os planos">\n\n<!-- ✅ Texto real com CSS -->\n<div class="banner-promo">\n  <h2>50% de desconto em todos os planos</h2>\n</div>'
            };
        }
    };

    fixes['nbr-reflow'] = {
        label: 'Garantir reflow em 320px',
        icon: '📱',
        getSuggestion: function() {
            return {
                description: 'O conteúdo deve funcionar em 320px de largura (zoom 400%) sem scroll horizontal.',
                code: '/* ✅ Layout responsivo sem scroll horizontal */\n.container {\n  max-width: 100%;\n  overflow-x: hidden;\n}\n\n/* Use unidades relativas */\n.content { width: 100%; max-width: 72ch; }\n\n/* Evite larguras fixas */\n/* ❌ width: 800px; */\n/* ✅ max-width: 800px; width: 100%; */'
            };
        }
    };

    fixes['nbr-meaningful-sequence'] = {
        label: 'Manter ordem lógica no DOM',
        icon: '📋',
        getSuggestion: function() {
            return {
                description: 'A ordem visual (CSS order/flex) deve corresponder à ordem do DOM para leitores de tela.',
                code: '/* ❌ Ordem visual diferente do DOM */\n.item-1 { order: 3; }\n.item-2 { order: 1; }\n\n/* ✅ Reordene no HTML em vez do CSS */\n<!-- Reordene os elementos no HTML para\n     que a ordem do DOM reflita a leitura visual -->'
            };
        }
    };

    fixes['nbr-sensory-characteristics'] = {
        label: 'Não depender apenas de cor/forma',
        icon: '🎨',
        getSuggestion: function() {
            return {
                description: 'Instruções não devem depender apenas de cor, forma, posição ou som. Use texto descritivo.',
                code: '<!-- ❌ Depende de cor/posição -->\n<p>Clique no botão verde à direita</p>\n\n<!-- ✅ Identificação por texto -->\n<p>Clique no botão "Salvar" para confirmar</p>'
            };
        }
    };

    fixes['nbr-orientation'] = {
        label: 'Permitir todas as orientações',
        icon: '🔄',
        getSuggestion: function() {
            return {
                description: 'Não restrinja a orientação da tela (retrato/paisagem) via CSS ou manifest.',
                code: '/* ❌ Bloqueia orientação */\n@media (orientation: portrait) {\n  .container { display: none; }\n}\n\n/* ✅ Adapte para ambas orientações */\n@media (orientation: landscape) {\n  .sidebar { flex-direction: row; }\n}'
            };
        }
    };

    fixes['nbr-multiple-ways'] = {
        label: 'Oferecer múltiplas formas de navegação',
        icon: '🧭',
        getSuggestion: function() {
            return {
                description: 'Ofereça pelo menos 2 formas de encontrar conteúdo: menu, busca, mapa do site ou breadcrumb.',
                code: '<!-- ✅ Menu + Busca + Breadcrumb -->\n<nav aria-label="Menu principal">...</nav>\n<form role="search" aria-label="Busca do site">\n  <input type="search" aria-label="Buscar">\n</form>\n<nav aria-label="Breadcrumb">\n  <ol><li><a href="/">Home</a></li><li>Atual</li></ol>\n</nav>'
            };
        }
    };

    fixes['nbr-video-audiodesc'] = {
        label: 'Adicionar audiodescrição ao vídeo',
        icon: '🎬',
        getSuggestion: function() {
            return {
                description: 'Vídeos com informação visual importante devem ter faixa de audiodescrição.',
                code: '<video controls>\n  <source src="video.mp4" type="video/mp4">\n  <track kind="captions" src="legendas.vtt" srclang="pt" label="Legendas">\n  <track kind="descriptions" src="audiodesc.vtt" srclang="pt" label="Audiodescrição">\n</video>'
            };
        }
    };

    fixes['nbr-motion-animation'] = {
        label: 'Respeitar prefers-reduced-motion',
        icon: '✨',
        getSuggestion: function() {
            return {
                description: 'Animações devem respeitar a preferência do usuário por movimento reduzido.',
                code: '/* ✅ Desabilite animações para quem prefere */\n@media (prefers-reduced-motion: reduce) {\n  *, *::before, *::after {\n    animation-duration: 0.01ms !important;\n    animation-iteration-count: 1 !important;\n    transition-duration: 0.01ms !important;\n    scroll-behavior: auto !important;\n  }\n}'
            };
        }
    };

    fixes['nbr-on-focus'] = {
        label: 'Evitar mudança de contexto no foco',
        icon: '⚠️',
        getSuggestion: function() {
            return {
                description: 'Focar um elemento não deve causar mudança de contexto (abrir popup, navegar, etc.).',
                code: '<!-- ❌ Muda contexto ao focar -->\n<select onfocus="window.location=this.value">\n\n<!-- ✅ Ação explícita do usuário -->\n<select id="nav-select">\n  <option>Escolha...</option>\n</select>\n<button onclick="go()">Ir</button>'
            };
        }
    };

    fixes['nbr-on-input'] = {
        label: 'Evitar mudança de contexto no input',
        icon: '⚠️',
        getSuggestion: function() {
            return {
                description: 'Alterar valor de um campo não deve causar mudança de contexto sem aviso prévio.',
                code: '<!-- ❌ Navega ao selecionar -->\n<select onchange="window.location=this.value">\n\n<!-- ✅ Requer ação explícita -->\n<select id="dest">\n  <option value="/a">Página A</option>\n</select>\n<button onclick="location=document.getElementById(\'dest\').value">Ir</button>'
            };
        }
    };

    fixes['nbr-consistent-navigation'] = {
        label: 'Manter navegação consistente',
        icon: '🧭',
        getSuggestion: function() {
            return {
                description: 'Menus de navegação devem aparecer na mesma ordem e posição em todas as páginas.',
                code: '<!-- ✅ Mesmo menu em todas as páginas -->\n<nav aria-label="Menu principal">\n  <ul>\n    <li><a href="/">Home</a></li>\n    <li><a href="/sobre">Sobre</a></li>\n    <li><a href="/contato">Contato</a></li>\n  </ul>\n</nav>'
            };
        }
    };

    fixes['nbr-consistent-identification'] = {
        label: 'Identificar componentes consistentemente',
        icon: '🏷️',
        getSuggestion: function() {
            return {
                description: 'Componentes com mesma funcionalidade devem ter labels e ícones consistentes em todas as páginas.',
                code: '<!-- ❌ Inconsistente -->\n<!-- Página 1: --> <button>Buscar</button>\n<!-- Página 2: --> <button>Pesquisar</button>\n\n<!-- ✅ Consistente -->\n<!-- Todas as páginas: --> <button>Buscar</button>'
            };
        }
    };

    fixes['nbr-breadcrumb-semantic'] = {
        label: 'Usar breadcrumb semântico',
        icon: '🥖',
        getSuggestion: function() {
            return {
                description: 'Breadcrumbs devem usar <nav> com aria-label e lista ordenada.',
                code: '<nav aria-label="Breadcrumb">\n  <ol class="breadcrumb">\n    <li><a href="/">Home</a></li>\n    <li><a href="/produtos">Produtos</a></li>\n    <li aria-current="page">Produto X</li>\n  </ol>\n</nav>'
            };
        }
    };

    fixes['nbr-widget-heading'] = {
        label: 'Adicionar heading ao widget',
        icon: '📦',
        getSuggestion: function() {
            return {
                description: 'Widgets WordPress devem ter heading para identificação por leitores de tela.',
                code: '<aside class="widget">\n  <h3 class="widget-title">Posts Recentes</h3>\n  <ul>...</ul>\n</aside>'
            };
        }
    };

    // ── Custom Rule Aliases → existing QuickFixes ───────────────────────
    // Custom rules return IDs like "custom-*", map them to existing fixes.

    fixes['custom-image-alt-empty'] = fixes['image-alt'];
    fixes['custom-empty-button-link'] = fixes['button-name'];
    fixes['custom-heading-hierarchy'] = fixes['heading-order'];
    fixes['custom-table-accessibility'] = fixes['td-has-header'];

    fixes['custom-wp-gallery-missing-alt'] = {
        label: 'Adicionar alt às imagens da galeria',
        icon: '🖼️',
        getSuggestion: function() {
            return {
                description: 'Todas as imagens de galerias WordPress devem ter texto alternativo descritivo.',
                code: '<!-- No editor Gutenberg:\n1. Clique na imagem da galeria\n2. No painel lateral, preencha o campo "Texto alternativo"\n3. Descreva o conteúdo da imagem -->'
            };
        }
    };

    fixes['custom-wp-media-no-captions'] = {
        label: 'Adicionar legendas/transcrição à mídia',
        icon: '🎬',
        getSuggestion: function() {
            return {
                description: 'Vídeos precisam de legendas e áudios precisam de transcrição para acessibilidade.',
                code: '<video controls>\n  <source src="video.mp4" type="video/mp4">\n  <track kind="captions" src="legendas.vtt"\n    srclang="pt" label="Português" default>\n</video>\n\n<!-- Para áudio, adicione transcrição: -->\n<details>\n  <summary>Ver transcrição</summary>\n  <p>Texto completo do áudio...</p>\n</details>'
            };
        }
    };

    fixes['custom-wp-forms-missing-labels'] = fixes['label'];

    fixes['custom-cover-contrast'] = fixes['color-contrast'];

    fixes['custom-navigation-structure'] = {
        label: 'Envolver menu em <nav>',
        icon: '🧭',
        getSuggestion: function() {
            return {
                description: 'Menus de navegação devem estar dentro de <nav> com aria-label descritivo.',
                code: '<nav aria-label="Menu principal">\n  <ul class="menu">\n    <li><a href="/">Home</a></li>\n  </ul>\n</nav>'
            };
        }
    };

    fixes['custom-site-logo-alt'] = {
        label: 'Melhorar alt do logo',
        icon: '🖼️',
        getSuggestion: function() {
            return {
                description: 'O logo do site deve ter alt com o nome da empresa, não apenas "logo".',
                code: '<!-- ❌ Genérico -->\n<img src="logo.png" alt="logo">\n\n<!-- ✅ Descritivo -->\n<img src="logo.png" alt="Equallyze - Acessibilidade Digital">'
            };
        }
    };

    fixes['custom-search-widget-label'] = {
        label: 'Adicionar label à busca',
        icon: '🔍',
        getSuggestion: function() {
            return {
                description: 'Campos de busca devem ter label visível ou aria-label.',
                code: '<form role="search">\n  <label for="search-input">Buscar no site</label>\n  <input type="search" id="search-input">\n  <button>Buscar</button>\n</form>',
                autoFixAttr: { 'aria-label': 'Buscar no site' }
            };
        }
    };

    fixes['custom-html-block-warning'] = {
        label: 'Verificar acessibilidade do HTML',
        icon: '⚠️',
        getSuggestion: function() {
            return {
                description: 'Blocos de HTML customizado podem conter código inacessível. Verifique labels, alt e semântica.',
                code: '<!-- Verifique manualmente:\n1. Imagens têm alt descritivo?\n2. Formulários têm labels?\n3. Tabelas têm <th> e scope?\n4. Elementos interativos são focáveis por teclado? -->'
            };
        }
    };

    fixes['custom-embed-no-title'] = {
        label: 'Adicionar título ao embed',
        icon: '🎬',
        getSuggestion: function() {
            return {
                description: 'Iframes de embed devem ter atributo title descritivo.',
                code: '<iframe\n  src="https://youtube.com/embed/..."\n  title="Vídeo: Nome do conteúdo"\n  allowfullscreen>\n</iframe>',
                autoFixAttr: { 'title': 'Descreva o conteúdo do embed' }
            };
        }
    };

    // ═══════════════════════════════════════════════════════════════════
    //  IF-THEN Rules Quick Fixes (57 regras exclusivas)
    // ═══════════════════════════════════════════════════════════════════

    // ── Group A – ARIA & Roles ──────────────────────────────────────────

    fixes['ift-role-button-keyboard'] = {
        label: 'Tornar botão acessível por teclado',
        icon: '⌨️',
        getSuggestion: function(node) {
            return {
                description: 'Elementos com role="button" devem ser focáveis e responder a Enter/Space.',
                code: '<!-- ❌ Inacessível -->\n<div role="button" onclick="action()">Clique</div>\n\n<!-- ✅ Acessível -->\n<div role="button" tabindex="0"\n  onclick="action()"\n  onkeydown="if(event.key===\'Enter\'||event.key===\' \'){action()}">\n  Clique\n</div>\n\n<!-- ✅ Melhor: use <button> -->\n<button onclick="action()">Clique</button>',
                autoFixAttr: { 'tabindex': '0' }
            };
        }
    };

    fixes['ift-tablist-has-tabs'] = {
        label: 'Adicionar role="tab" aos itens',
        icon: '📑',
        getSuggestion: function() {
            return {
                description: 'Um tablist deve conter elementos com role="tab".',
                code: '<div role="tablist">\n  <button role="tab" aria-selected="true" aria-controls="panel-1">Aba 1</button>\n  <button role="tab" aria-selected="false" aria-controls="panel-2">Aba 2</button>\n</div>\n<div role="tabpanel" id="panel-1">Conteúdo 1</div>\n<div role="tabpanel" id="panel-2" hidden>Conteúdo 2</div>'
            };
        }
    };

    fixes['ift-tab-controls-panel'] = {
        label: 'Vincular tab ao tabpanel',
        icon: '📑',
        getSuggestion: function() {
            return {
                description: 'Cada tab deve ter aria-controls apontando para um tabpanel.',
                code: '<button role="tab" aria-controls="painel-1" aria-selected="true">Aba 1</button>\n<div role="tabpanel" id="painel-1">Conteúdo</div>',
                autoFixAttr: { 'aria-controls': 'panel-id' }
            };
        }
    };

    fixes['ift-dialog-has-name'] = {
        label: 'Nomear diálogo modal',
        icon: '💬',
        getSuggestion: function() {
            return {
                description: 'Diálogos devem ter nome acessível via aria-labelledby.',
                code: '<div role="dialog" aria-labelledby="dialog-title" aria-modal="true">\n  <h2 id="dialog-title">Confirmar ação</h2>\n  <p>Deseja continuar?</p>\n  <button>Sim</button> <button>Cancelar</button>\n</div>',
                autoFixAttr: { 'aria-labelledby': 'dialog-title' }
            };
        }
    };

    fixes['ift-expanded-has-target'] = {
        label: 'Vincular aria-expanded ao conteúdo',
        icon: '🔽',
        getSuggestion: function() {
            return {
                description: 'Elementos com aria-expanded devem ter aria-controls apontando para o conteúdo expansível.',
                code: '<button aria-expanded="false" aria-controls="content-1">Mostrar mais</button>\n<div id="content-1" hidden>Conteúdo expandido...</div>',
                autoFixAttr: { 'aria-controls': 'content-id' }
            };
        }
    };

    fixes['ift-slider-values'] = {
        label: 'Adicionar valores ARIA ao slider',
        icon: '🎚️',
        getSuggestion: function() {
            return {
                description: 'Sliders devem ter aria-valuemin, aria-valuemax e aria-valuenow.',
                code: '<div role="slider" tabindex="0"\n  aria-valuemin="0"\n  aria-valuemax="100"\n  aria-valuenow="50"\n  aria-label="Volume">\n</div>',
                autoFixAttr: { 'aria-valuemin': '0', 'aria-valuemax': '100', 'aria-valuenow': '50' }
            };
        }
    };

    fixes['ift-progressbar-value'] = {
        label: 'Adicionar valor à barra de progresso',
        icon: '📊',
        getSuggestion: function() {
            return {
                description: 'Barras de progresso devem comunicar o valor atual.',
                code: '<div role="progressbar"\n  aria-valuenow="75"\n  aria-valuemin="0"\n  aria-valuemax="100"\n  aria-label="Progresso do upload: 75%">\n</div>',
                autoFixAttr: { 'aria-valuenow': '0', 'aria-valuemin': '0', 'aria-valuemax': '100' }
            };
        }
    };

    fixes['ift-alertdialog-desc'] = {
        label: 'Descrever alerta com aria-describedby',
        icon: '⚠️',
        getSuggestion: function() {
            return {
                description: 'Alertas modais devem descrever seu conteúdo via aria-describedby.',
                code: '<div role="alertdialog"\n  aria-labelledby="alert-title"\n  aria-describedby="alert-desc">\n  <h2 id="alert-title">Erro</h2>\n  <p id="alert-desc">Não foi possível salvar. Tente novamente.</p>\n  <button>OK</button>\n</div>',
                autoFixAttr: { 'aria-describedby': 'alert-desc' }
            };
        }
    };

    fixes['ift-menu-has-items'] = {
        label: 'Adicionar role="menuitem" aos itens',
        icon: '📋',
        getSuggestion: function() {
            return {
                description: 'Menus ARIA devem conter filhos com role="menuitem".',
                code: '<ul role="menu">\n  <li role="menuitem">Editar</li>\n  <li role="menuitem">Copiar</li>\n  <li role="menuitem">Excluir</li>\n</ul>'
            };
        }
    };

    fixes['ift-tree-has-items'] = {
        label: 'Adicionar role="treeitem" aos nós',
        icon: '🌳',
        getSuggestion: function() {
            return {
                description: 'Widgets de árvore devem conter filhos com role="treeitem".',
                code: '<ul role="tree">\n  <li role="treeitem" aria-expanded="false">Pasta 1\n    <ul role="group">\n      <li role="treeitem">Arquivo 1.1</li>\n    </ul>\n  </li>\n</ul>'
            };
        }
    };

    // ── Group B – Forms & Inputs ────────────────────────────────────────

    fixes['ift-radio-grouped'] = {
        label: 'Agrupar radio buttons',
        icon: '🔘',
        getSuggestion: function() {
            return {
                description: 'Radio buttons devem estar em fieldset com legend.',
                code: '<fieldset>\n  <legend>Forma de pagamento</legend>\n  <label><input type="radio" name="pagamento" value="pix"> Pix</label>\n  <label><input type="radio" name="pagamento" value="cartao"> Cartão</label>\n  <label><input type="radio" name="pagamento" value="boleto"> Boleto</label>\n</fieldset>'
            };
        }
    };

    fixes['ift-checkbox-grouped'] = {
        label: 'Agrupar checkboxes relacionados',
        icon: '☑️',
        getSuggestion: function() {
            return {
                description: 'Checkboxes relacionados devem estar agrupados com label de grupo.',
                code: '<fieldset>\n  <legend>Selecione os tópicos de interesse</legend>\n  <label><input type="checkbox" name="topics[]" value="a11y"> Acessibilidade</label>\n  <label><input type="checkbox" name="topics[]" value="seo"> SEO</label>\n</fieldset>'
            };
        }
    };

    fixes['ift-required-aria'] = {
        label: 'Adicionar aria-required',
        icon: '⭐',
        getSuggestion: function() {
            return {
                description: 'Campos obrigatórios devem ter aria-required="true" além do atributo required.',
                code: '<input type="text" name="email" required aria-required="true">',
                autoFixAttr: { 'aria-required': 'true' }
            };
        }
    };

    fixes['ift-file-input-label'] = {
        label: 'Adicionar label ao upload de arquivo',
        icon: '📎',
        getSuggestion: function() {
            return {
                description: 'Inputs de upload precisam de label descritiva.',
                code: '<label for="upload-doc">Enviar documento (PDF, máx 5MB)</label>\n<input type="file" id="upload-doc" accept=".pdf">',
                autoFixAttr: { 'aria-label': 'Enviar arquivo' }
            };
        }
    };

    fixes['ift-range-accessible'] = {
        label: 'Tornar range acessível',
        icon: '🎚️',
        getSuggestion: function() {
            return {
                description: 'Sliders range precisam de label e exibição do valor.',
                code: '<label for="volume">Volume</label>\n<input type="range" id="volume" min="0" max="100" value="50"\n  oninput="document.getElementById(\'vol-output\').value=this.value">\n<output id="vol-output" for="volume">50</output>'
            };
        }
    };

    fixes['ift-textarea-label'] = {
        label: 'Adicionar label ao textarea',
        icon: '📝',
        getSuggestion: function() {
            return {
                description: 'Textareas devem ter label associada.',
                code: '<label for="comentario">Seu comentário</label>\n<textarea id="comentario" rows="4"></textarea>',
                autoFixAttr: { 'aria-label': 'Campo de texto' }
            };
        }
    };

    fixes['ift-select-multiple-instructions'] = {
        label: 'Instruções para seleção múltipla',
        icon: '📋',
        getSuggestion: function() {
            return {
                description: 'Selects com múltipla escolha devem instruir o usuário.',
                code: '<label for="categorias">Categorias</label>\n<select id="categorias" multiple aria-describedby="cats-help">\n  <option>Tecnologia</option>\n  <option>Design</option>\n</select>\n<small id="cats-help">Segure Ctrl para selecionar múltiplas opções</small>'
            };
        }
    };

    fixes['ift-pattern-explained'] = {
        label: 'Explicar formato do campo',
        icon: '📐',
        getSuggestion: function(node) {
            return {
                description: 'Campos com pattern devem descrever o formato esperado.',
                code: '<input type="text" pattern="[0-9]{3}\\.[0-9]{3}\\.[0-9]{3}-[0-9]{2}"\n  title="Formato: 000.000.000-00"\n  aria-describedby="cpf-help">\n<small id="cpf-help">Digite seu CPF no formato 000.000.000-00</small>',
                autoFixAttr: { 'title': 'Formato esperado: descreva aqui' }
            };
        }
    };

    fixes['ift-disabled-not-focusable'] = {
        label: 'Remover foco de elemento desabilitado',
        icon: '🚫',
        getSuggestion: function() {
            return {
                description: 'Elementos desabilitados não devem estar na ordem de tabulação.',
                code: '<!-- ❌ Focável mesmo desabilitado -->\n<input type="text" disabled tabindex="0">\n\n<!-- ✅ Sem tabindex ou com -1 -->\n<input type="text" disabled tabindex="-1">',
                autoFixAttr: { 'tabindex': '-1' }
            };
        }
    };

    fixes['ift-form-submit-label'] = {
        label: 'Descrever ação do botão submit',
        icon: '📨',
        getSuggestion: function() {
            return {
                description: 'Botões de envio devem descrever a ação realizada.',
                code: '<!-- ❌ Genérico -->\n<button type="submit">Enviar</button>\n\n<!-- ✅ Descritivo -->\n<button type="submit">Criar minha conta</button>'
            };
        }
    };

    // ── Group C – Tables ────────────────────────────────────────────────

    fixes['ift-th-has-scope'] = {
        label: 'Adicionar scope aos cabeçalhos',
        icon: '📊',
        getSuggestion: function() {
            return {
                description: 'Cabeçalhos de tabela devem indicar se se aplicam a linhas ou colunas.',
                code: '<table>\n  <tr>\n    <th scope="col">Nome</th>\n    <th scope="col">Email</th>\n  </tr>\n  <tr>\n    <th scope="row">João</th>\n    <td>joao@email.com</td>\n  </tr>\n</table>',
                autoFixAttr: { 'scope': 'col' }
            };
        }
    };

    fixes['ift-complex-table-headers'] = {
        label: 'Usar headers+id em tabela complexa',
        icon: '📊',
        getSuggestion: function() {
            return {
                description: 'Tabelas com células mescladas devem usar headers+id para associação.',
                code: '<table>\n  <tr>\n    <th id="nome">Nome</th>\n    <th id="q1">Q1</th>\n    <th id="q2">Q2</th>\n  </tr>\n  <tr>\n    <td headers="nome">João</td>\n    <td headers="q1">R$ 100</td>\n    <td headers="q2">R$ 200</td>\n  </tr>\n</table>'
            };
        }
    };

    fixes['ift-table-has-caption'] = {
        label: 'Adicionar caption à tabela',
        icon: '📊',
        getSuggestion: function() {
            return {
                description: 'Tabelas de dados devem ter uma descrição via <caption>.',
                code: '<table>\n  <caption>Vendas do primeiro trimestre 2024</caption>\n  <tr><th scope="col">Mês</th><th scope="col">Total</th></tr>\n  <tr><td>Janeiro</td><td>R$ 1.000</td></tr>\n</table>'
            };
        }
    };

    fixes['ift-presentation-table-clean'] = {
        label: 'Limpar semântica de tabela layout',
        icon: '🧹',
        getSuggestion: function() {
            return {
                description: 'Tabelas de layout não devem conter elementos semânticos de tabela.',
                code: '<!-- ❌ Contradição -->\n<table role="presentation"><tr><th>Título</th></tr></table>\n\n<!-- ✅ Use CSS em vez de tabelas para layout -->\n<div class="layout-grid">\n  <div class="col">Conteúdo</div>\n</div>'
            };
        }
    };

    fixes['ift-colspan-has-headers'] = {
        label: 'Associar célula com colspan a headers',
        icon: '📊',
        getSuggestion: function() {
            return {
                description: 'Células que ocupam múltiplas colunas devem referenciar seus cabeçalhos.',
                code: '<td colspan="2" headers="nome total">Valor consolidado</td>'
            };
        }
    };

    fixes['ift-no-nested-tables'] = {
        label: 'Evitar tabelas aninhadas',
        icon: '🏗️',
        getSuggestion: function() {
            return {
                description: 'Tabelas aninhadas dificultam a leitura por leitores de tela.',
                code: '<!-- ❌ Tabela dentro de tabela -->\n<table><tr><td><table>...</table></td></tr></table>\n\n<!-- ✅ Estrutura plana com CSS grid -->\n<div class="data-grid">\n  <div class="row">...</div>\n</div>'
            };
        }
    };

    fixes['ift-sortable-aria-sort'] = {
        label: 'Anunciar estado de ordenação',
        icon: '🔃',
        getSuggestion: function() {
            return {
                description: 'Cabeçalhos ordináveis devem comunicar o estado via aria-sort.',
                code: '<th aria-sort="ascending">\n  <button>Nome ▲</button>\n</th>\n<!-- Valores: ascending, descending, none -->',
                autoFixAttr: { 'aria-sort': 'none' }
            };
        }
    };

    // ── Group D – Links & Navigation ────────────────────────────────────

    fixes['ift-new-window-warning'] = {
        label: 'Avisar que link abre nova janela',
        icon: '🔗',
        getSuggestion: function() {
            return {
                description: 'Links que abrem em nova aba devem avisar o usuário.',
                code: '<a href="https://example.com" target="_blank">\n  Site externo\n  <span class="sr-only"> (abre em nova aba)</span>\n</a>\n\n<!-- CSS para sr-only -->\n<style>.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0,0,0,0); }</style>'
            };
        }
    };

    fixes['ift-no-hash-links'] = {
        label: 'Substituir href="#" por button',
        icon: '🔗',
        getSuggestion: function() {
            return {
                description: 'Use <button> para ações e <a href> para navegação.',
                code: '<!-- ❌ Link falso -->\n<a href="#">Clique aqui</a>\n\n<!-- ✅ Botão para ação -->\n<button type="button" onclick="toggleMenu()">Abrir menu</button>\n\n<!-- ✅ Link para navegação -->\n<a href="/pagina">Ir para página</a>'
            };
        }
    };

    fixes['ift-no-javascript-href'] = {
        label: 'Remover javascript: do href',
        icon: '🚫',
        getSuggestion: function() {
            return {
                description: 'Substituir links javascript: por elementos semânticos.',
                code: '<!-- ❌ Antipadrão -->\n<a href="javascript:void(0)" onclick="doStuff()">Ação</a>\n\n<!-- ✅ Semântico -->\n<button type="button" onclick="doStuff()">Ação</button>'
            };
        }
    };

    fixes['ift-no-adjacent-duplicate-links'] = {
        label: 'Combinar links duplicados adjacentes',
        icon: '🔗',
        getSuggestion: function() {
            return {
                description: 'Links adjacentes para o mesmo destino devem ser combinados.',
                code: '<!-- ❌ Duplicado -->\n<a href="/artigo"><img src="thumb.jpg" alt=""></a>\n<a href="/artigo">Leia o artigo</a>\n\n<!-- ✅ Combinado -->\n<a href="/artigo">\n  <img src="thumb.jpg" alt="">\n  Leia o artigo\n</a>'
            };
        }
    };

    fixes['ift-aria-current-page'] = {
        label: 'Marcar página ativa com aria-current',
        icon: '📍',
        getSuggestion: function() {
            return {
                description: 'O item de navegação ativo deve usar aria-current="page".',
                code: '<nav>\n  <a href="/">Home</a>\n  <a href="/sobre" aria-current="page">Sobre</a>\n  <a href="/contato">Contato</a>\n</nav>',
                autoFixAttr: { 'aria-current': 'page' }
            };
        }
    };

    fixes['ift-pagination-semantic'] = {
        label: 'Envolver paginação em <nav>',
        icon: '📄',
        getSuggestion: function() {
            return {
                description: 'Paginação deve estar dentro de <nav> com aria-label.',
                code: '<nav aria-label="Paginação">\n  <a href="?page=1">1</a>\n  <a href="?page=2" aria-current="page">2</a>\n  <a href="?page=3">3</a>\n</nav>'
            };
        }
    };

    fixes['ift-download-link-info'] = {
        label: 'Indicar tipo de arquivo no link',
        icon: '📥',
        getSuggestion: function() {
            return {
                description: 'Links de download devem indicar tipo e tamanho do arquivo.',
                code: '<a href="/docs/relatorio.pdf" download>\n  Relatório Anual (PDF, 2.5 MB)\n</a>'
            };
        }
    };

    fixes['ift-anchor-target-exists'] = {
        label: 'Corrigir link âncora quebrado',
        icon: '🔗',
        getSuggestion: function() {
            return {
                description: 'O elemento alvo do link âncora deve existir na página.',
                code: '<!-- Link -->\n<a href="#secao-contato">Ir para Contato</a>\n\n<!-- Alvo (deve existir) -->\n<section id="secao-contato">\n  <h2>Contato</h2>\n</section>'
            };
        }
    };

    // ── Group E – Media & Embeds ────────────────────────────────────────

    fixes['ift-video-has-captions'] = {
        label: 'Adicionar legendas ao vídeo',
        icon: '🎬',
        getSuggestion: function() {
            return {
                description: 'Vídeos devem ter legendas sincronizadas (captions).',
                code: '<video controls>\n  <source src="video.mp4" type="video/mp4">\n  <track kind="captions" src="legendas-pt.vtt"\n    srclang="pt" label="Português" default>\n</video>'
            };
        }
    };

    fixes['ift-audio-has-transcript'] = {
        label: 'Adicionar transcrição ao áudio',
        icon: '🎙️',
        getSuggestion: function() {
            return {
                description: 'Conteúdo em áudio deve ter transcrição em texto disponível.',
                code: '<audio controls>\n  <source src="podcast.mp3" type="audio/mpeg">\n</audio>\n<p><a href="/transcricao-episodio-1">Ver transcrição completa</a></p>'
            };
        }
    };

    fixes['ift-canvas-fallback'] = {
        label: 'Adicionar fallback ao canvas',
        icon: '🎨',
        getSuggestion: function() {
            return {
                description: 'Canvas deve ter conteúdo alternativo para tecnologias assistivas.',
                code: '<canvas role="img" aria-label="Gráfico de vendas mensais: Janeiro R$1000, Fevereiro R$1500">\n  Gráfico mostrando vendas mensais de 2024.\n</canvas>',
                autoFixAttr: { 'role': 'img', 'aria-label': 'Descrição do conteúdo do canvas' }
            };
        }
    };

    fixes['ift-svg-interactive-keyboard'] = {
        label: 'Tornar SVG interativo acessível',
        icon: '🖱️',
        getSuggestion: function() {
            return {
                description: 'Elementos interativos em SVG devem ser acessíveis por teclado.',
                code: '<svg role="img" aria-label="Mapa interativo">\n  <g role="button" tabindex="0" aria-label="São Paulo"\n    onclick="showInfo(\'SP\')"\n    onkeydown="if(event.key===\'Enter\')showInfo(\'SP\')">\n    <rect .../>\n  </g>\n</svg>'
            };
        }
    };

    fixes['ift-decorative-iframe-hidden'] = {
        label: 'Esconder iframe decorativo',
        icon: '🚫',
        getSuggestion: function() {
            return {
                description: 'Iframes de rastreamento/decorativos devem ser ocultos de leitores de tela.',
                code: '<iframe src="tracker.html" width="1" height="1"\n  aria-hidden="true" tabindex="-1"></iframe>',
                autoFixAttr: { 'aria-hidden': 'true', 'tabindex': '-1' }
            };
        }
    };

    fixes['ift-decorative-img-role'] = {
        label: 'Adicionar role="presentation" à imagem',
        icon: '🖼️',
        getSuggestion: function() {
            return {
                description: 'Imagens decorativas com alt="" devem ter role="presentation".',
                code: '<img src="decorative.png" alt="" role="presentation">',
                autoFixAttr: { 'role': 'presentation' }
            };
        }
    };

    fixes['ift-map-iframe-alternative'] = {
        label: 'Adicionar alternativa ao mapa',
        icon: '🗺️',
        getSuggestion: function() {
            return {
                description: 'Mapas embarcados devem ter título e endereço em texto.',
                code: '<iframe src="https://maps.google.com/..."\n  title="Mapa: Escritório em São Paulo - Av. Paulista, 1000">\n</iframe>\n<address>\n  Av. Paulista, 1000 - Bela Vista<br>\n  São Paulo - SP, 01310-100\n</address>'
            };
        }
    };

    // ── Group F – Structure & Landmarks ─────────────────────────────────

    fixes['ift-nested-header-labeled'] = {
        label: 'Rotular header aninhado',
        icon: '🏷️',
        getSuggestion: function() {
            return {
                description: 'Headers dentro de article/section devem ter aria-label.',
                code: '<article>\n  <header aria-label="Cabeçalho do artigo">\n    <h2>Título do artigo</h2>\n  </header>\n</article>',
                autoFixAttr: { 'aria-label': 'Cabeçalho do artigo' }
            };
        }
    };

    fixes['ift-nested-footer-labeled'] = {
        label: 'Rotular footer aninhado',
        icon: '🏷️',
        getSuggestion: function() {
            return {
                description: 'Footers dentro de article/section devem ter aria-label.',
                code: '<article>\n  <footer aria-label="Rodapé do artigo">\n    <p>Publicado em 01/01/2024</p>\n  </footer>\n</article>',
                autoFixAttr: { 'aria-label': 'Rodapé do artigo' }
            };
        }
    };

    fixes['ift-aside-labeled'] = {
        label: 'Rotular aside com aria-label',
        icon: '📌',
        getSuggestion: function() {
            return {
                description: 'Múltiplos asides devem ter labels únicos.',
                code: '<aside aria-label="Barra lateral de navegação">...</aside>\n<aside aria-label="Artigos relacionados">...</aside>',
                autoFixAttr: { 'aria-label': 'Conteúdo complementar' }
            };
        }
    };

    fixes['ift-unique-main'] = {
        label: 'Garantir <main> único',
        icon: '🏠',
        getSuggestion: function() {
            return {
                description: 'A página deve ter exatamente um <main> visível.',
                code: '<body>\n  <header>...</header>\n  <nav>...</nav>\n  <main id="content">\n    <!-- Conteúdo principal aqui -->\n  </main>\n  <footer>...</footer>\n</body>'
            };
        }
    };

    fixes['ift-section-labeled'] = {
        label: 'Rotular section com heading',
        icon: '📑',
        getSuggestion: function() {
            return {
                description: 'Sections devem ter heading ou aria-label.',
                code: '<section aria-label="Depoimentos de clientes">\n  <h2>O que dizem nossos clientes</h2>\n  ...\n</section>'
            };
        }
    };

    fixes['ift-article-has-heading'] = {
        label: 'Adicionar heading ao article',
        icon: '📰',
        getSuggestion: function() {
            return {
                description: 'Articles devem ter um heading para identificação.',
                code: '<article>\n  <h2>Título do artigo ou post</h2>\n  <p>Conteúdo...</p>\n</article>'
            };
        }
    };

    fixes['ift-search-landmark-labeled'] = {
        label: 'Rotular landmarks de busca',
        icon: '🔍',
        getSuggestion: function() {
            return {
                description: 'Múltiplos landmarks de busca devem ter labels únicos.',
                code: '<form role="search" aria-label="Busca do site">\n  <input type="search" aria-label="Buscar no site">\n  <button>Buscar</button>\n</form>',
                autoFixAttr: { 'aria-label': 'Busca' }
            };
        }
    };

    fixes['ift-modal-aria-modal'] = {
        label: 'Adicionar aria-modal ao diálogo',
        icon: '🪟',
        getSuggestion: function() {
            return {
                description: 'Modais abertos devem ter aria-modal="true" para confinar o foco.',
                code: '<div role="dialog" aria-modal="true"\n  aria-labelledby="modal-title">\n  <h2 id="modal-title">Confirmação</h2>\n  <p>Deseja continuar?</p>\n  <button>Confirmar</button>\n  <button>Cancelar</button>\n</div>',
                autoFixAttr: { 'aria-modal': 'true' }
            };
        }
    };

    // ── Group G – Dynamic Content & Interaction ─────────────────────────

    fixes['ift-contenteditable-accessible'] = {
        label: 'Tornar contenteditable acessível',
        icon: '✏️',
        getSuggestion: function() {
            return {
                description: 'Elementos editáveis devem ter role e label acessível.',
                code: '<div contenteditable="true"\n  role="textbox"\n  aria-label="Editar comentário"\n  aria-multiline="true">\n  Texto editável...\n</div>',
                autoFixAttr: { 'role': 'textbox', 'aria-label': 'Editor de texto', 'aria-multiline': 'true' }
            };
        }
    };

    fixes['ift-infinite-scroll-alternative'] = {
        label: 'Oferecer paginação alternativa',
        icon: '📜',
        getSuggestion: function() {
            return {
                description: 'Scroll infinito deve ter alternativa de paginação tradicional.',
                code: '<!-- Botão de carregar mais (melhor que scroll infinito) -->\n<button type="button" class="load-more"\n  aria-label="Carregar mais artigos">\n  Carregar mais\n</button>\n\n<!-- Ou paginação tradicional -->\n<nav aria-label="Paginação">\n  <a href="?page=1">1</a>\n  <a href="?page=2">2</a>\n</nav>'
            };
        }
    };

    fixes['ift-accordion-aria-expanded'] = {
        label: 'Adicionar aria-expanded ao accordion',
        icon: '🔽',
        getSuggestion: function() {
            return {
                description: 'Gatilhos de accordion devem ter aria-expanded.',
                code: '<h3>\n  <button aria-expanded="false" aria-controls="panel-1">\n    Perguntas Frequentes\n  </button>\n</h3>\n<div id="panel-1" role="region" hidden>\n  <p>Resposta aqui...</p>\n</div>',
                autoFixAttr: { 'aria-expanded': 'false' }
            };
        }
    };

    fixes['ift-tooltip-focusable'] = {
        label: 'Tornar elemento com tooltip focável',
        icon: '💬',
        getSuggestion: function() {
            return {
                description: 'Elementos com tooltip devem ser focáveis por teclado.',
                code: '<span tabindex="0" data-tooltip="Informação adicional"\n  role="note" aria-describedby="tip-1">\n  Texto com dica ℹ️\n</span>\n<div id="tip-1" role="tooltip" hidden>Informação adicional</div>',
                autoFixAttr: { 'tabindex': '0' }
            };
        }
    };

    fixes['ift-loading-spinner-aria'] = {
        label: 'Anunciar estado de carregamento',
        icon: '⏳',
        getSuggestion: function() {
            return {
                description: 'Indicadores de carregamento devem ser anunciados por leitores de tela.',
                code: '<div role="status" aria-label="Carregando, aguarde...">\n  <span class="spinner" aria-hidden="true"></span>\n  <span class="sr-only">Carregando...</span>\n</div>',
                autoFixAttr: { 'role': 'status', 'aria-label': 'Carregando...' }
            };
        }
    };

    fixes['ift-cookie-banner-accessible'] = {
        label: 'Tornar banner de cookies acessível',
        icon: '🍪',
        getSuggestion: function() {
            return {
                description: 'Banners de cookie devem ser navegáveis por teclado com opção de recusar.',
                code: '<div role="dialog" aria-label="Consentimento de cookies"\n  aria-describedby="cookie-desc">\n  <p id="cookie-desc">Usamos cookies para melhorar sua experiência.</p>\n  <button>Aceitar todos</button>\n  <button>Recusar</button>\n  <button>Personalizar</button>\n</div>'
            };
        }
    };

    fixes['ift-countdown-live-region'] = {
        label: 'Anunciar contagem regressiva',
        icon: '⏱️',
        getSuggestion: function() {
            return {
                description: 'Contadores devem usar região ao vivo para anunciar mudanças.',
                code: '<div role="timer" aria-live="polite" aria-atomic="true"\n  aria-label="Tempo restante">\n  <span>02:30</span>\n</div>',
                autoFixAttr: { 'role': 'timer', 'aria-live': 'polite', 'aria-atomic': 'true' }
            };
        }
    };

    // ─── Public API ─────────────────────────────────────────────────────

    /**
     * Get quick fix suggestion for a violation.
     *
     * @param {object} violation - The violation object (with id, nodes[]).
     * @param {number} [nodeIndex=0] - Index of the node to generate fix for.
     * @returns {object|null} { label, icon, description, code, autoFixAttr? }
     */
    WallyMonitor.QuickFix.getSuggestion = function(violation, nodeIndex) {
        nodeIndex = nodeIndex || 0;
        var fix = fixes[violation.id];
        if (!fix) { return null; }

        var node = violation.nodes && violation.nodes[nodeIndex] ? violation.nodes[nodeIndex] : { html: '', target: [''] };
        var suggestion = fix.getSuggestion(node, violation);
        if (!suggestion) { return null; }

        return {
            label: fix.label,
            icon: fix.icon,
            description: suggestion.description,
            code: suggestion.code,
            autoFixAttr: suggestion.autoFixAttr || null,
            autoFixAction: suggestion.autoFixAction || null
        };
    };

    /**
     * Check whether a fix is available for a given rule ID.
     *
     * @param {string} ruleId - The violation rule ID.
     * @returns {boolean}
     */
    WallyMonitor.QuickFix.hasQuickFix = function(ruleId) {
        return !!fixes[ruleId];
    };

    /**
     * Return all registered rule IDs that have quick fixes.
     *
     * @returns {string[]}
     */
    WallyMonitor.QuickFix.registeredRules = function() {
        return Object.keys(fixes);
    };

    // ─── Helpers ────────────────────────────────────────────────────────

    /**
     * Extract an HTML attribute value from a raw HTML string.
     */
    function extractAttr(html, attr) {
        if (!html) { return ''; }
        var re = new RegExp(attr + '=["\']([^"\']*)["\']', 'i');
        var m = html.match(re);
        return m ? m[1] : '';
    }

    /**
     * Escape text for safe inclusion in a code snippet.
     */
    function escSnippet(str) {
        return (str || '').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
    }

    /**
     * Capitalize the first letter of a string.
     */
    function capitalize(str) {
        str = (str || '').trim();
        return str.charAt(0).toUpperCase() + str.slice(1);
    }

    /**
     * Guess autocomplete value from field name / type.
     */
    function guessAutocomplete(name, type) {
        var map = {
            'email': 'email', 'e-mail': 'email', 'mail': 'email',
            'name': 'name', 'nome': 'name', 'full_name': 'name', 'fullname': 'name',
            'first_name': 'given-name', 'firstname': 'given-name', 'fname': 'given-name', 'primeiro_nome': 'given-name',
            'last_name': 'family-name', 'lastname': 'family-name', 'lname': 'family-name', 'sobrenome': 'family-name',
            'phone': 'tel', 'telefone': 'tel', 'tel': 'tel', 'celular': 'tel',
            'address': 'street-address', 'endereco': 'street-address',
            'city': 'address-level2', 'cidade': 'address-level2',
            'state': 'address-level1', 'estado': 'address-level1', 'uf': 'address-level1',
            'zip': 'postal-code', 'cep': 'postal-code', 'postal': 'postal-code',
            'country': 'country-name', 'pais': 'country-name',
            'cpf': 'on', 'cnpj': 'on',
            'username': 'username', 'user': 'username', 'usuario': 'username',
            'password': 'current-password', 'senha': 'current-password', 'pass': 'current-password',
            'new_password': 'new-password', 'nova_senha': 'new-password',
            'cc-number': 'cc-number', 'card': 'cc-number', 'cartao': 'cc-number',
            'organization': 'organization', 'empresa': 'organization', 'company': 'organization'
        };
        if (type === 'email') { return 'email'; }
        if (type === 'tel') { return 'tel'; }
        if (type === 'password') { return 'current-password'; }
        for (var key in map) {
            if (name.indexOf(key) !== -1) { return map[key]; }
        }
        return 'on';
    }

})(jQuery);
