Content Validator

Validates content and exports results to CSV

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

作者のサイトでサポートを受ける。または、このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name         Content Validator
// @namespace    https://github.com/MajaBukvic/Scripts
// @version      1.4
// @description  Validates content and exports results to CSV
// @author       Maja Bukvic
// @match        https://share.amazon.com/sites/amazonwatson/*
// @grant        GM_download
// @license      MIT
// @supportURL   https://github.com/MajaBukvic/Scripts/issues
// @homepage     https://github.com/MajaBukvic/Scripts/tree/main/Content-validator
// ==/UserScript==

(function() {
    'use strict';

    // Add validation button to Watson header
    function addValidationButton() {
        const menuContainer = document.querySelector('div[style*="position:relative;float:right;right:25px;padding-top:7px;"]');

        if (menuContainer) {
            const span = document.createElement('span');
            const link = document.createElement('a');
            link.className = 'watson-menu-link';
            link.href = '#';
            link.style.cursor = 'pointer';
            link.innerHTML = '🔍 Validate Content';

            link.addEventListener('click', (e) => {
                e.preventDefault();
                validateAndExport();
            });

            span.appendChild(link);

            // Insert after Export SOP
            const exportContainer = document.getElementById('exportLinkContainer');
            if (exportContainer) {
                exportContainer.parentNode.insertBefore(span, exportContainer.nextSibling);
            } else {
                menuContainer.appendChild(span);
            }
        }
    }

    // Initialize button when page loads
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', addValidationButton);
    } else {
        addValidationButton();
    }

function validateAndExport() {
    // First, check if WatsonSOPSource exists (standalone check only)
    const watsonSource = document.querySelector('#WatsonSOPSource');
    if (!watsonSource) {
        alert('Warning: #WatsonSOPSource element not found on this page!');
    }

    // Now find and validate only content inside WatsonSOPBody
    const watsonBody = document.querySelector('.WatsonSOPBody');

    if (!watsonBody) {
        alert('Could not find .WatsonSOPBody on this page!');
        return;
    }

    const issues = [];

    // Clone WatsonSOPBody to avoid modifying the live page
    const bodyClone = watsonBody.cloneNode(true);

    // Remove WatsonByline divs from the clone
    const bylineElements = bodyClone.querySelectorAll('div.WatsonByline');
    bylineElements.forEach(el => el.remove());

    // Get the HTML of just the WatsonSOPBody content
    const contentHTML = bodyClone.outerHTML;
    const parser = new DOMParser();
    const doc = parser.parseFromString(contentHTML, "text/html");
    const contentDoc = doc.body.firstChild;

    validationRules.forEach(rule => {
        try {
            const category = rule.category.toLowerCase();
            switch(category) {
                case 'html':
                    checkHtmlRule(rule, contentDoc, contentHTML, issues);
                    break;
                case 'css':
                    checkCssRule(rule, contentHTML, issues);
                    break;
                case 'accessibility':
                    checkAccessibilityRule(rule, contentDoc, issues);
                    break;
                case 'content_structure':
                    checkContentStructureRule(rule, contentDoc, contentHTML, issues);
                    break;
                case 'interactive':
                    checkInteractiveRule(rule, contentDoc, issues);
                    break;
            }
        } catch (e) {
            console.error(`Error processing rule: ${rule.pattern}`, e);
        }
    });

    // Call custom structure validation (this will check WatsonSOPSource again within the doc)
    validateWatsonSOPStructure(contentDoc, issues);

    if (issues.length > 0) {
        exportToCSV(issues);
    } else {
        alert('No issues found in the content!');
    }
}
    function checkHtmlRule(rule, doc, html, issues) {
        try {
            if (rule.checkType === "required_tag") {
                const elements = doc.querySelectorAll(rule.pattern.replace(/[<>]/g, ''));
                if (elements.length === 0 && rule.required) {
                    addIssue(issues, "HTML", rule);
                }
            } else if (rule.checkType === "forbidden_tag") {
                const elements = doc.querySelectorAll(rule.pattern.replace(/[<>]/g, ''));
                if (elements.length > 0) {
                    addIssue(issues, "HTML", rule, elements.length);
                }
            } else if (rule.checkType === "regex") {
                const regex = new RegExp(rule.pattern, 'gi');
                const matches = html.match(regex);
                if (matches) {
                    addIssue(issues, "HTML", rule, matches.length);
                }
            } else if (rule.checkType === "duplicate_check") {
                const regex = new RegExp(rule.pattern, 'gi');
                const matches = Array.from(html.matchAll(regex));
                const ids = matches.map(match => match[1]);
                const duplicates = ids.filter((id, index) => ids.indexOf(id) !== index);
                if (duplicates.length > 0) {
                    addIssue(issues, "HTML", rule, duplicates.length);
                }
            }
        } catch (e) {
            console.error(`Error in HTML rule ${rule.pattern}: ${e}`);
        }
    }

    function checkCssRule(rule, html, issues) {
        try {
            if (rule.checkType === "forbidden_property" || rule.checkType === "regex") {
                const regex = new RegExp(rule.pattern, 'gi');
                const matches = html.match(regex);
                if (matches) {
                    addIssue(issues, "CSS", rule, matches.length);
                }
            } else if (rule.checkType === "required_property") {
                const regex = new RegExp(rule.pattern, 'gi');
                const matches = html.match(regex);
                if (!matches && rule.required) {
                    addIssue(issues, "CSS", rule);
                }
            }
        } catch (e) {
            console.error(`Error in CSS rule ${rule.pattern}: ${e}`);
        }
    }

    function checkAccessibilityRule(rule, doc, issues) {
        try {
            if (rule.checkType === "missing_alt") {
                const elements = doc.querySelectorAll('img:not([alt]), img[alt=""]');
                if (elements.length > 0) {
                    addIssue(issues, "Accessibility", rule, elements.length);
                }
            } else if (rule.checkType === "regex") {
                const regex = new RegExp(rule.pattern, 'gi');
                const html = doc.outerHTML;
                const matches = html.match(regex);
                if (matches) {
                    addIssue(issues, "Accessibility", rule, matches.length);
                }
            } else if (rule.checkType === "required_tag") {
                const elements = doc.querySelectorAll(rule.pattern.replace(/[<>]/g, ''));
                if (elements.length === 0 && rule.required) {
                    addIssue(issues, "Accessibility", rule);
                }
            }
        } catch (e) {
            console.error(`Error in accessibility rule ${rule.pattern}: ${e}`);
        }
    }

    function checkContentStructureRule(rule, doc, html, issues) {
        try {
            if (rule.checkType === "percentage_check") {
                const regex = new RegExp(rule.pattern, 'gi');
                const matches = html.match(regex);
                if (matches && matches.length > 0) {
                    const contentLength = html.length;
                    const matchesLength = matches.join('').length;
                    if (matchesLength / contentLength > 0.3) {
                        addIssue(issues, "Content Structure", rule);
                    }
                }
            } else if (rule.checkType === "combined_percentage_check") {
                const regex = new RegExp(rule.pattern, 'gi');
                const matches = html.match(regex);
                if (matches && matches.length > 0) {
                    const contentLength = html.length;
                    const matchesLength = matches.join('').length;
                    if (matchesLength / contentLength > 0.5) {
                        addIssue(issues, "Content Structure", rule);
                    }
                }
            } else if (rule.checkType === "ratio_check") {
                // Handle ratio checks if needed
                console.log("Ratio check not implemented yet");
            }
        } catch (e) {
            console.error(`Error in content structure rule ${rule.pattern}: ${e}`);
        }
    }

    function checkInteractiveRule(rule, doc, issues) {
        try {
            if (rule.checkType === "regex") {
                const regex = new RegExp(rule.pattern, 'gi');
                const html = doc.outerHTML;
                const matches = html.match(regex);
                if (matches) {
                    addIssue(issues, "Interactive", rule, matches.length);
                }
            }
        } catch (e) {
            console.error(`Error in interactive rule ${rule.pattern}: ${e}`);
        }
    }
// Custom Watson SOP Structure Validation Function
function validateWatsonSOPStructure(doc, issues) {
    try {
        // Check #WatsonSOPSource exists (standalone element, not a parent)
        const watsonSource = doc.querySelector('#WatsonSOPSource');
        if (!watsonSource) {
            addIssue(issues, "Structure", {
                pattern: "#WatsonSOPSource",
                description: "Missing #WatsonSOPSource element",
                suggestion: "Add <div id=\"WatsonSOPSource\"></div> as a standalone element",
                required: true
            });
        }

        // Check for WatsonSOPBody class (standalone, not inside WatsonSOPSource)
        const watsonBody = doc.querySelector('.WatsonSOPBody');
        if (!watsonBody) {
            addIssue(issues, "Structure", {
                pattern: ".WatsonSOPBody",
                description: "Missing div.WatsonSOPBody",
                suggestion: "Add <div class=\"WatsonSOPBody\"></div>",
                required: true
            });
            return; // Cannot continue without WatsonSOPBody
        }

        // Check for col-TOC and col-body
        const colTOC = doc.querySelector('#col-TOC');
        const colBody = doc.querySelector('#col-body');

        // If either col-TOC or col-body exists, validate row structure
        if (colTOC || colBody) {
            const row = watsonBody.querySelector('.row');

            if (!row) {
                addIssue(issues, "Structure", {
                    pattern: ".row",
                    description: "Missing div.row when col-TOC or col-body present",
                    suggestion: "Add <div class=\"row\"> inside div.WatsonSOPBody to contain #col-TOC and #col-body",
                    required: true
                });
            } else {
                // Verify col-TOC and col-body are children of row
                if (colTOC && !row.contains(colTOC)) {
                    addIssue(issues, "Structure", {
                        pattern: "#col-TOC parent",
                        description: "#col-TOC must be child of div.row",
                        suggestion: "Move #col-TOC inside div.row",
                        required: true
                    });
                }

                if (colBody && !row.contains(colBody)) {
                    addIssue(issues, "Structure", {
                        pattern: "#col-body parent",
                        description: "#col-body must be child of div.row",
                        suggestion: "Move #col-body inside div.row",
                        required: true
                    });
                }

                // Check order: col-TOC should come before col-body if both exist
                if (colTOC && colBody) {
                    const tocIndex = Array.from(row.children).indexOf(colTOC);
                    const bodyIndex = Array.from(row.children).indexOf(colBody);

                    if (tocIndex > bodyIndex && tocIndex !== -1 && bodyIndex !== -1) {
                        addIssue(issues, "Structure", {
                            pattern: "#col-TOC order",
                            description: "#col-TOC must appear before #col-body",
                            suggestion: "Reorder elements: #col-TOC should come before #col-body in div.row",
                            required: true
                        });
                    }
                }
            }

            // Verify row is child of WatsonSOPBody
            if (row && !watsonBody.contains(row)) {
                addIssue(issues, "Structure", {
                    pattern: ".row parent",
                    description: "div.row must be child of div.WatsonSOPBody",
                    suggestion: "Move div.row inside div.WatsonSOPBody",
                    required: true
                });
            }
        }

        // Check for inline styles inside WatsonSOPBody
        if (watsonBody) {
            const elementsWithInlineStyles = watsonBody.querySelectorAll('[style]');

            if (elementsWithInlineStyles.length > 0) {
                elementsWithInlineStyles.forEach((element) => {
                    const tagName = element.tagName.toLowerCase();
                    const styleValue = element.getAttribute('style');

                    addIssue(issues, "CSS", {
                        pattern: `${tagName}[style]`,
                        description: `Inline style detected on ${tagName} element inside WatsonSOPBody`,
                        suggestion: `Remove style="${styleValue}" and use externally linked standardized CSS instead`,
                        required: true
                    });
                });
            }
        }

        // Check for approved CSS files
        const approvedCssFiles = [
            "Watson-SOP-standard.css",
            "Interactive_features.css",
            "Buttons.css",
            "Function_menu.css",
            "Grey_2_tone.css",
            "WhiteTable.css",
            "KeyTermsTable.css",
            "DefinitionTable.css"
        ];

        // Find all <link> tags with rel="stylesheet"
        const linkTags = doc.querySelectorAll('link[rel="stylesheet"]');

        linkTags.forEach(link => {
            const href = link.getAttribute('href');
            if (href) {
                // Extract just the filename from the href
                const filename = href.split('/').pop().split('?');

                // Check if this CSS file is in the approved list
                if (!approvedCssFiles.includes(filename)) {
                    addIssue(issues, "CSS", {
                        pattern: `link[href*="${filename}"]`,
                        description: `Unapproved CSS file linked: ${filename}`,
                        suggestion: `Replace "${filename}" with one of the approved standardized CSS files: ${approvedCssFiles.join(', ')}`,
                        required: true
                    });
                }
            }
        });

    } catch (e) {
        console.error('Error in Watson SOP structure validation:', e);
    }
}

    function addIssue(issues, type, rule, count = null) {
        let description = rule.description;
        if (count !== null) {
            description = `Found ${count} instances of ${description}`;
        }

        // Add location information if possible
        let location = '';
        try {
            const regex = new RegExp(rule.pattern, 'gi');
            const match = regex.exec(document.documentElement.outerHTML);
            if (match) {
                const context = match.input.substr(Math.max(0, match.index - 100), 500);
                location = `...${context}...`;
            }
        } catch (e) {
            console.log('Could not determine location for issue');
        }

        issues.push({
            type: type,
            element: rule.pattern,
            issue: description,
            required: rule.required,
            suggestion: rule.suggestion,
            location: location
        });
    }

    function exportToCSV(issues) {
        try {
            // Add timestamp to report
            const timestamp = new Date().toISOString().replace(/[:.]/g, '-');

            // Include more detailed information in the headers
            const headers = ['Type', 'Element', 'Issue', 'Required', 'Suggestion', 'Location in Code'];
            const csvContent = [
                headers.join(','),
                ...issues.map(issue => [
                    issue.type,
                    issue.element,
                    issue.issue,
                    issue.required,
                    issue.suggestion,
                    issue.location || 'N/A'
                ].map(field => `"${String(field).replace(/"/g, '""')}"`).join(','))
            ].join('\n');

            const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8' });

            // Create more descriptive filename
            const pageTitle = document.title.replace(/[^a-z0-9]/gi, '_').toLowerCase();
            const filename = `content-validation_${pageTitle}_${timestamp}.csv`;

            GM_download({
                url: URL.createObjectURL(blob),
                name: filename
            });

            // Show summary to user
            const summary = summarizeIssues(issues);
            alert(`Validation complete!\n\n${summary}\n\nReport saved as: ${filename}`);
        } catch (e) {
            console.error('Error exporting to CSV:', e);
            alert('Error creating validation report. Check console for details.');
        }
    }

    function summarizeIssues(issues) {
        // Create a summary of issues by type
        const summary = {};
        issues.forEach(issue => {
            if (!summary[issue.type]) {
                summary[issue.type] = 0;
            }
            summary[issue.type]++;
        });

        // Format the summary
        let summaryText = `Found ${issues.length} total issues:\n`;
        for (const [type, count] of Object.entries(summary)) {
            summaryText += `\n${type}: ${count} issues`;
        }

        return summaryText;
    }

    // Utility function to check if element is visible
    function isVisible(element) {
        return !!(element.offsetWidth || element.offsetHeight || element.getClientRects().length);
    }

    // Utility function to get element's XPath
    function getXPath(element) {
        if (!element) return '';
        try {
            if (element.id) {
                return `//*[@id="${element.id}"]`;
            }
            if (element === document.body) {
                return '/html/body';
            }
            let path = '';
            while (element.parentNode) {
                let sibCount = 0;
                let sibIndex = 0;
                for (let sib = element.previousSibling; sib; sib = sib.previousSibling) {
                    if (sib.nodeType === 1 && sib.tagName === element.tagName) {
                        sibCount++;
                    }
                }
                for (let sib = element; sib; sib = sib.previousSibling) {
                    if (sib.nodeType === 1 && sib.tagName === element.tagName) {
                        sibIndex++;
                    }
                }
                const tagName = element.tagName.toLowerCase();
                const pathIndex = (sibCount > 0 ? `[${sibIndex}]` : '');
                path = `/${tagName}${pathIndex}${path}`;
                element = element.parentNode;
            }
            return path;
        } catch (e) {
            console.error('Error generating XPath:', e);
            return '';
        }
    }

// Define validation rules
const validationRules = [
  {
  "category": "html",
  "checkType": "regex",
  "pattern": "<title>\\s*\\S+",
  "required": true,
  "suggestion": "Include descriptive <title> tag with content",
  "description": "Missing or empty <title> tag"
},
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<table[^>]*>(?!([\\s\\S]*?<thead|[\\s\\S]*?<tr[^>]*>\\s*<th))",
    "required": false,
    "suggestion": "Tables should have either a <thead> section or a first row with <th> elements for proper structure and accessibility",
    "description": "Table missing header structure (no thead or th elements)"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<table[^>]*>[\\s\\S]*?<tr[^>]*>[\\s\\S]*?</tr>[\\s\\S]*?(?!<tr)[\\s\\S]*?</table>",
    "required": false,
    "suggestion": "Don't use tables for single rows of content. Consider using divs with appropriate styling or a more semantic structure",
    "description": "Single-row table detected - tables should be used for data relationships, not layout"
  },
  {
    "category": "content_structure",
    "checkType": "regex",
    "pattern": "<table[^>]*>\\s*(?:<thead>)?\\s*<tr[^>]*>[\\s\\S]*?</tr>\\s*(?:</thead>)?\\s*(?!<tr)[\\s\\S]*?</table>",
    "required": false,
    "suggestion": "Single row tables indicate potential misuse of tables for layout. Consider:\n1. For layout: Use CSS Grid or Flexbox\n2. For data: Add more rows or use a different component\n3. For lists: Use <ul> or <ol>",
    "description": "Table with single row detected - might be misused for layout"
  },
  {
    "category": "html",
    "checkType": "forbidden_tag",
    "pattern": "<font>",
    "required": false,
    "suggestion": "Remove deprecated <font> tag",
    "description": "Deprecated tag used"
  },
  {
    "category": "html",
    "checkType": "forbidden_tag",
    "pattern": "<center>",
    "required": false,
    "suggestion": "Replace with CSS text-align",
    "description": "Deprecated <center> tag"
  },
  {
    "category": "html",
    "checkType": "forbidden_tag",
    "pattern": "<marquee>",
    "required": false,
    "suggestion": "Avoid <marquee>; use CSS animation instead",
    "description": "Deprecated <marquee> element"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<div[^>]+id\\s*=\\s*['\\\"]content['\\\"]?",
    "required": false,
    "suggestion": "Use <main> instead of <div id='content'>",
    "description": "Non-semantic main container found"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<section(?![^>]*aria-label)[^>]*>",
    "required": false,
    "suggestion": "Add aria-label to <section> for screen reader clarity",
    "description": "Section missing aria-label"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<header(?![^>]*role=)[^>]*>",
    "required": false,
    "suggestion": "Add role='banner' for header landmarks",
    "description": "Missing ARIA role on header"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<footer(?![^>]*role=)[^>]*>",
    "required": false,
    "suggestion": "Add role='contentinfo' for footer",
    "description": "Footer missing ARIA role"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<nav(?![^>]*aria-label)[^>]*>",
    "required": false,
    "suggestion": "Provide aria-label for navigation menus",
    "description": "Navigation missing label"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<article(?![^>]*aria-labelledby)[^>]*>",
    "required": false,
    "suggestion": "Add aria-labelledby for article region",
    "description": "Article missing label"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<table(?![^>]*summary=)[^>]*>",
    "required": false,
    "suggestion": "Add summary/caption for table context",
    "description": "Table missing summary"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<ul[^>]*>\\s*(?!\\s*<li)",
    "required": false,
    "suggestion": "Ensure lists contain <li> elements",
    "description": "Empty list detected"
  },
  {
  "category": "html",
  "checkType": "regex",
  "pattern": "<ol[^>]*>\\s*(?!\\s*<li)",
  "required": false,
  "suggestion": "Ensure ordered lists contain <li>",
  "description": "Empty ordered list"
},
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<blockquote(?![^>]*cite=)[^>]*>",
    "required": false,
    "suggestion": "Include cite attribute for blockquotes",
    "description": "Missing citation source"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<dl(?![^>]*dt)[^>]*>",
    "required": false,
    "suggestion": "Ensure <dl> contains <dt>/<dd> pairs",
    "description": "Malformed description list"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<meta\\s+name=['\\\"]description['\\\"]\\s+content=['\\\"]{01}['\\\"]{01}",
    "required": true,
    "suggestion": "Add meta description",
    "description": "Missing meta description"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<link[^>]*rel=['\\\"]canonical['\\\"]",
    "required": true,
    "suggestion": "Add canonical link to prevent duplicate content",
    "description": "Missing canonical link"
  },
  {
    "category": "accessibility",
    "checkType": "missing_alt",
    "pattern": "alt=\"\"",
    "required": false,
    "suggestion": "Add descriptive alt text for all <img>",
    "description": "Image missing alt text"
  },
  {
    "category": "accessibility",
    "checkType": "regex",
    "pattern": "<button(?![^>]*aria-label)(?![^>]*title)[^>]*>",
    "required": false,
    "suggestion": "Add aria-label or title",
    "description": "Button missing accessible name"
  },
  {
    "category": "accessibility",
    "checkType": "regex",
    "pattern": "<input(?![^>]*aria-label)(?![^>]*title)[^>]*>",
    "required": false,
    "suggestion": "Add label, aria-label, or title",
    "description": "Input missing label"
  },
  {
    "category": "accessibility",
    "checkType": "regex",
    "pattern": "<textarea(?![^>]*aria-label)(?![^>]*title)[^>]*>",
    "required": false,
    "suggestion": "Add label to text areas",
    "description": "Textarea missing label"
  },
  {
    "category": "accessibility",
    "checkType": "regex",
    "pattern": "<a(?![^>]*href=)[^>]*>",
    "required": false,
    "suggestion": "Add href or use <button>",
    "description": "Anchor missing href"
  },
  {
    "category": "accessibility",
    "checkType": "regex",
    "pattern": "<a[^>]*href=['\\\"]#['\\\"]",
    "required": false,
    "suggestion": "Provide valid link targets",
    "description": "Anchor links to #"
  },
  {
    "category": "accessibility",
    "checkType": "regex",
    "pattern": "<iframe(?![^>]*title)[^>]*>",
    "required": false,
    "suggestion": "Add title for iframe",
    "description": "Iframe missing title"
  },
  {
    "category": "accessibility",
    "checkType": "regex",
    "pattern": "<video(?![^>]*controls)[^>]*>",
    "required": false,
    "suggestion": "Add controls to videos",
    "description": "Video missing controls"
  },
  {
    "category": "accessibility",
    "checkType": "regex",
    "pattern": "<audio(?![^>]*controls)[^>]*>",
    "required": false,
    "suggestion": "Add controls to audio",
    "description": "Audio missing controls"
  },
  {
    "category": "accessibility",
    "checkType": "regex",
    "pattern": "<form(?![^>]*aria-label)(?![^>]*label)[^>]*>",
    "required": false,
    "suggestion": "Add form label or ARIA label",
    "description": "Form missing accessible name"
  },
  {
    "category": "accessibility",
    "checkType": "regex",
    "pattern": "<th(?![^>]*scope=)[^>]*>",
    "required": false,
    "suggestion": "Add scope='col' or 'row' to <th>",
    "description": "Missing table header scope"
  },
  {
    "category": "accessibility",
    "checkType": "regex",
    "pattern": "<td[^>]*headers=",
    "required": false,
    "suggestion": "Ensure <td> references <th> IDs",
    "description": "Table data not linked to headers"
  },
  {
    "category": "accessibility",
    "checkType": "regex",
    "pattern": "tabindex\\s*=\\s*['\\\"]-[1-9]+['\\\"]",
    "required": false,
    "suggestion": "Do not remove elements from tab order",
    "description": "Negative tabindex"
  },
  {
    "category": "accessibility",
    "checkType": "regex",
    "pattern": "<div[^>]*role=['\\\"]presentation['\\\"]",
    "required": false,
    "suggestion": "Confirm no interactive children",
    "description": "Potential ARIA misuse"
  },
  {
    "category": "accessibility",
    "checkType": "regex",
    "pattern": "style\\s*=\\s*['\\\"][^'\\\"]*color\\s*:\\s*(#ff0000|#f00|red)[^'\\\"]*['\\\"]",
    "required": false,
    "suggestion": "Avoid pure red text",
    "description": "Low contrast red text"
  },
  {
    "category": "accessibility",
    "checkType": "regex",
    "pattern": "@media\\s*\\(prefers-reduced-motion:\\s*reduce\\)",
    "required": true,
    "suggestion": "Respect reduced motion preference",
    "description": "Missing reduced-motion query"
  },
  {
    "category": "accessibility",
    "checkType": "regex",
    "pattern": "@media\\s*\\(forced-colors:\\s*active\\)",
    "required": true,
    "suggestion": "Ensure high contrast support",
    "description": "Missing forced-color query"
  },
  {
    "category": "accessibility",
    "checkType": "regex",
    "pattern": ":focus-visible",
    "required": true,
    "suggestion": "Ensure focus outlines visible",
    "description": "Missing focus-visible handling"
  },
  {
    "category": "accessibility",
    "checkType": "regex",
    "pattern": "\\.visually-hidden",
    "required": true,
    "suggestion": "Include visually hidden class for screen readers",
    "description": "Missing visually-hidden support"
  },
  {
    "category": "css",
    "checkType": "forbidden_property",
    "pattern": "font-family\\s*:\\s*(?!.*Amazon Ember)",
    "required": true,
    "suggestion": "Use Amazon Ember font",
    "description": "Non-standard font family"
  },
  {
    "category": "css",
    "checkType": "forbidden_property",
    "pattern": "color\\s*:\\s*(#000|black)",
    "required": false,
    "suggestion": "Use var(--primary-color) for text",
    "description": "Avoid pure black text"
  },
  {
    "category": "css",
    "checkType": "forbidden_property",
    "pattern": "background-color\\s*:\\s*(#fff|white)",
    "required": false,
    "suggestion": "Use var(--light-gray) for background",
    "description": "Avoid pure white"
  },
  {
    "category": "css",
    "checkType": "forbidden_property",
    "pattern": "float\\s*:\\s*(left|right)",
    "required": false,
    "suggestion": "Use flex/grid layout",
    "description": "Floats are deprecated"
  },
  {
    "category": "css",
    "checkType": "forbidden_property",
    "pattern": "!important",
    "required": true,
    "suggestion": "Avoid !important; fix cascade",
    "description": "!important used"
  },
  {
    "category": "css",
    "checkType": "forbidden_property",
    "pattern": "outline:\\s*none",
    "required": true,
    "suggestion": "Keep focus outlines",
    "description": "Focus indicator removed"
  },
  {
    "category": "css",
    "checkType": "forbidden_property",
    "pattern": "display\\s*:\\s*inline-block",
    "required": true,
    "suggestion": "Prefer flexbox/grid",
    "description": "Inline-block layout discouraged"
  },
    /*
  {
    "category": "css",
    "checkType": "forbidden_property",
    "pattern": "z-index\\s*:\\s*\\d{3}",
    "required": false,
    "suggestion": "Keep z-index <100",
    "description": "High z-index found"
  },
  */
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "\\.button--[1-4]",
    "required": true,
    "suggestion": "Use Watson button classes for consistency",
    "description": "Missing standard button style"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "style=\"[^\"]*display:\\s*none",
    "required": false,
    "suggestion": "Carefully review use of display:none - ensure content isn't being hidden inappropriately",
    "description": "Hidden content detected with display:none"
},
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "\\.animated_button_(red|green)",
    "required": true,
    "suggestion": "Use standard Watson animated buttons",
    "description": "Custom animation class found"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "\\.update-button",
    "required": true,
    "suggestion": "Follow Watson update button design",
    "description": "Ensure proper update button styling"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "\\.collapsible",
    "required": true,
    "suggestion": "Use standard collapsible class",
    "description": "Ensure collapsible matches Watson style"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "\\.flip-card",
    "required": true,
    "suggestion": "Use approved flip card layout",
    "description": "Ensure flip-card markup follows standard"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "\\.dropdown",
    "required": true,
    "suggestion": "Use Watson dropdown style",
    "description": "Dropdown class missing"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "\\.popup-container",
    "required": true,
    "suggestion": "Use standardized popup markup",
    "description": "Popup container missing"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "\\.tree-list",
    "required": true,
    "suggestion": "Use standardized tree list styles",
    "description": "Tree list missing class"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "\\.Htab_tag",
    "required": true,
    "suggestion": "Ensure correct heading tab styles",
    "description": "Missing Htab_tag element"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "@media\\s*\\(max-width:\\s*768px\\)",
    "required": true,
    "suggestion": "Responsive layout must exist",
    "description": "Missing responsive breakpoint"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "@media\\s*\\(forced-colors:\\s*active\\)",
    "required": true,
    "suggestion": "Support high contrast",
    "description": "Missing forced color rules"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "\\.dark-mode",
    "required": true,
    "suggestion": "Support dark mode",
    "description": "Dark mode class missing"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "\\.pulsing_button",
    "required": true,
    "suggestion": "Avoid excessive motion, include prefers-reduced-motion",
    "description": "Check pulsing animation compliance"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "\\.update-button:focus",
    "required": true,
    "suggestion": "Focus outline must be visible",
    "description": "Missing focus state for button"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "style\\s*=\\s*['\\\"][^'\\\"]*(background|color|padding|margin|font)[^'\\\"]*['\\\"]",
    "required": false,
    "suggestion": "Move inline styles to CSS",
    "description": "Inline style found"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "style\\s*=\\s*['\\\"][^'\\\"]*!important[^'\\\"]*['\\\"]",
    "required": false,
    "suggestion": "Avoid !important inline styles",
    "description": "Inline override found"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<[^>]*class\\s*=\\s*['\\\"][^'\\\"]*(button--|update-button|animated_button)[^'\\\"]*['\\\"].*style=",
    "required": false,
    "suggestion": "Do not override button styles inline",
    "description": "Inline override on Watson button"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "on(click|mouseover|keydown|submit)=",
    "required": false,
    "suggestion": "Move event handlers to JS",
    "description": "Inline event found"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<script[^>]*src=['\\\"]http:",
    "required": false,
    "suggestion": "Use HTTPS for scripts",
    "description": "Insecure script link"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<script[^>]*>[^<]*(alert|prompt|confirm)\\(.*\\)[^<]*</script>",
    "required": false,
    "suggestion": "Remove debug alerts",
    "description": "Debug JS found"
  },
  {
  "category": "html",
  "checkType": "required_tag",
  "pattern": "<h1>",
  "required": true,
  "suggestion": "Include one <h1> heading",
  "description": "Missing H1 heading"
},
{
  "category": "html",
  "checkType": "regex",
  "pattern": "(<h1[^>]*>.*?</h1>.*?){2,}",
  "required": false,
  "suggestion": "Use only one <h1> per page",
  "description": "Multiple <h1> elements found"
},
/*
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<h2[^>]*>.*?</h2>",
    "required": true,
    "suggestion": "Use <h2> for sections",
    "description": "Missing section heading"
  },
  */
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<br\\s*/?>\\s*<br\\s*/?>",
    "required": false,
    "suggestion": "Check if you should use a standalone tag instead",
    "description": "Multiple <br> tags found"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<img[^>]*width=['\\\"][0-9]+['\\\"][^>]*height=['\\\"][0-9]+['\\\"]?",
    "required": false,
    "suggestion": "Use CSS for sizing",
    "description": "Hardcoded image dimensions"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<video(?![^>]*aria-label)(?![^>]*title)[^>]*>",
    "required": false,
    "suggestion": "Add aria-label to describe video",
    "description": "Video missing label"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<audio(?![^>]*aria-label)(?![^>]*title)[^>]*>",
    "required": false,
    "suggestion": "Add aria-label to describe audio",
    "description": "Audio missing label"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<meta\\s+http-equiv=['\\\"]refresh['\\\"]",
    "required": false,
    "suggestion": "Avoid meta refresh redirects",
    "description": "Meta refresh detected"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<a[^>]*>[\\s]*click here[\\s]*</a>",
    "required": false,
    "suggestion": "Use descriptive link text",
    "description": "Generic link text found"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<a[^>]*target=['\\\"]_blank['\\\"](?![^>]*aria-label)",
    "required": false,
    "suggestion": "Add aria-label '(opens in new window)'",
    "description": "Missing accessibility notice on external link"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<div[^>]*id=['\\\"]TOCRight['\\\"]?",
    "required": true,
    "suggestion": "Ensure floating TOC element exists",
    "description": "Missing floating TOC"
  },
  {
    "category": "css",
    "checkType": "forbidden_property",
    "pattern": "#s4-[a-zA-Z0-9\\-_]+\\s*\\{[^}]*display\\s*:\\s*none",
    "required": true,
    "suggestion": "Do not modify visibility of SharePoint s4 elements",
    "description": "SharePoint native s4 element visibility modified"
  },
  {
    "category": "css",
    "checkType": "forbidden_selector",
    "pattern": "#s4-[a-zA-Z0-9\\-_]+",
    "required": true,
    "suggestion": "Avoid targeting SharePoint native configuration elements",
    "description": "CSS targeting SharePoint s4 elements"
  },
  {
    "category": "css",
    "checkType": "forbidden_property",
    "pattern": "\\.s4-[a-zA-Z0-9\\-_]+",
    "required": true,
    "suggestion": "Do not modify SharePoint s4 class elements",
    "description": "CSS targeting SharePoint s4 class elements"
  },
  {
    "category": "css",
    "checkType": "required_property",
    "pattern": "\\.WatsonSOPBody\\s*\\{[^}]*margin-left\\s*:\\s*30px",
    "required": true,
    "suggestion": "Use standard 30px left margin in WatsonSOPBody",
    "description": "Non-standard left margin in WatsonSOPBody"
  },
  {
    "category": "css",
    "checkType": "required_property",
    "pattern": "\\.WatsonSOPBody\\s*\\{[^}]*max-width\\s*:\\s*85%",
    "required": true,
    "suggestion": "Use max-width: 85% for responsive design",
    "description": "Non-standard max-width in WatsonSOPBody"
  },
  {
    "category": "css",
    "checkType": "required_property",
    "pattern": "font-family\\s*:\\s*[\"\\']?Amazon Ember[\"\\']?",
    "required": true,
    "suggestion": "Use Amazon Ember as primary font",
    "description": "Amazon Ember font not set as primary"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<h1>.*?</h1>.*?<h3>",
    "required": false,
    "suggestion": "Use h2 before h3; maintain heading hierarchy",
    "description": "Heading hierarchy violated (h1 to h3 skip)"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<h2>.*?</h2>.*?<h4>",
    "required": false,
    "suggestion": "Use h3 before h4; maintain heading hierarchy",
    "description": "Heading hierarchy violated (h2 to h4 skip)"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<table(?![^>]*class=)",
    "required": false,
    "suggestion": "Apply table style class",
    "description": "Missing table class"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<img(?![^>]*class=)",
    "required": false,
    "suggestion": "Apply image class ",
    "description": "Missing image class"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<strong(?![^>]*class=[\"\\']ui[\"\\'])[^>]*>(?:Click|Select|Choose|Press)",
    "required": false,
    "suggestion": "Use class=\"ui\" for UI elements users interact with",
    "description": "UI element missing .ui class"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "\\$\\?![^<]*</span>",
    "required": false,
    "suggestion": "Wrap tokens/placeholders in <span class=\"token\">",
    "description": "Token/placeholder missing .token class"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<strong(?![^>]*class=[\"\\']blurb[\"\\'])'",
    "required": false,
    "suggestion": "Use class=\"blurb\" for CTCM blurbs",
    "description": "Blurb missing .blurb class"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<strong(?![^>]*class=[\"\\']queue[\"\\'])'",
    "required": false,
    "suggestion": "Use class=\"queue\" for queue names users interact with",
    "description": "Queue element missing .queue class"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<div class=\"(note|warning|important|tip|example|exception|bestPractice|annotation|script|template)\"(?![^>]*role=)",
    "required": false,
    "suggestion": "Add role and aria attributes to call-outs for accessibility",
    "description": "Call-out missing accessibility attributes"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<div[^>]*class=\"tab\"(?![^>]*role=\"tablist\")",
    "required": false,
    "suggestion": "Add role=\"tablist\" to tab containers",
    "description": "Tab container missing tablist role"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<b>",
    "required": false,
    "suggestion": "Use <strong> instead of <b> for semantic HTML",
    "description": "Non-semantic <b> tag used"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<i>",
    "required": false,
    "suggestion": "Use <em> instead of <i> for semantic HTML",
    "description": "Non-semantic <i> tag used"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<div[^>]*id=\"toc\"(?![^>]*role=\"navigation\")",
    "required": false,
    "suggestion": "Add role=\"navigation\" and aria-label to TOC",
    "description": "TOC missing navigation role"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "[^\\x00-\\x7F]{10}(?![^<]*xml:lang=)(?![^<]*lang=)",
    "required": false,
    "suggestion": "Use lang or xml:lang attribute for non-English content",
    "description": "Non-English content missing language attribute"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<span class=\"propernoun\">",
    "required": false,
    "suggestion": "Use .propernoun class for proper nouns to avoid Acrolinx flags",
    "description": "Proper noun class usage for capitalization"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<span class=\"tool\">",
    "required": false,
    "suggestion": "Use .tool class for investigation tools",
    "description": "Tool class for investigation tools"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<(acronym|abbr)(?![^>]*title=)",
    "required": false,
    "suggestion": "Add title attribute to acronyms and abbreviations",
    "description": "Acronym/abbreviation missing title attribute"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<table[^>]*>(?![^<]*<th)",
    "required": false,
    "suggestion": "Use layout grid (LayoutContainerHorizontal) instead of tables for layout",
    "description": "Table used for layout instead of data"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<(ul|ol)>\\s*<li class=\"(DO|DO_NOT)\">",
    "required": false,
    "suggestion": "Use DO/DO_NOT classes for important reminders list",
    "description": "Important reminders list structure"
  },
{
  "category": "structure",
  "checkType": "regex",
  "pattern": "<div[^>]*id=['\"]WatsonSOPBody['\"]",
  "required": true,
  "suggestion": "Change <div id=\"WatsonSOPBody\"> to <div class=\"WatsonSOPBody\">. WatsonSOPBody must be a class, not an ID.",
  "description": "WatsonSOPBody incorrectly used as ID instead of class"
},
{
  "category": "structure",
  "checkType": "regex",
  "pattern": "<div[^>]*class=['\"][^'\"]*\\btoc\\b[^'\"]*['\"]",
  "required": true,
  "suggestion": "Change <div class=\"toc\"> to <div id=\"toc\">. The toc element must be an ID, not a class.",
  "description": "toc incorrectly used as class instead of ID"
},
{
  "category": "structure",
  "checkType": "regex",
  "pattern": "<div[^>]*class=['\"][^'\"]*\\bcol-TOC\\b[^'\"]*['\"]",
  "required": true,
  "suggestion": "Change <div class=\"col-TOC\"> to <div id=\"col-TOC\">. The toc-body element must be an ID, not a class.",
  "description": "col-TOC incorrectly used as class instead of ID"
},
{
  "category": "structure",
  "checkType": "regex",
  "pattern": "<div[^>]*class=['\"][^'\"]*\\bcol-body\\b[^'\"]*['\"]",
  "required": true,
  "suggestion": "Change <div class=\"col-body\"> to <div id=\"col-body\">. The col-body element must be an ID, not a class.",
  "description": "col-body incorrectly used as class instead of ID"
},
    {
  "category": "structure",
  "checkType": "regex",
  "pattern": "<div[^>]*class=['\"][^'\"]*\\brow\\b[^'\"]*['\"]",
  "required": true,
  "suggestion": "Change <div class=\"row\"> to <div id=\"row\">. The row element must be an ID, not a class.",
  "description": "row incorrectly used as class instead of ID"
},
  {
    "category": "css",
    "checkType": "required_property",
    "pattern": "margin-right\\s*:\\s*15%",
    "required": true,
    "suggestion": "Use 15% right margin for responsive design",
    "description": "Non-standard right margin"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<div[^>]*class=\"dataTable\"",
    "required": false,
    "suggestion": "Use dataTable class for filterable tables",
    "description": "Filterable table structure"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<div[^>]*class=\"(blue|orange|grey|ink)\"(?![^>]*<span[^>]*class=\"visually-hidden\")",
    "required": false,
    "suggestion": "Add visually-hidden span for colored margins to describe content to screen readers",
    "description": "Colored margin missing screen reader description"
  },
  {
    "category": "html",
    "checkType": "required_tag",
    "pattern": "<div class=\"row\">",
    "required": true,
    "suggestion": "Floating TOC requires row container wrapper",
    "description": "Missing row container for floating TOC layout"
  },
  {
    "category": "html",
    "checkType": "required_tag",
    "pattern": "<div id=\"col-TOC\">",
    "required": true,
    "suggestion": "Floating TOC requires col-TOC container",
    "description": "Missing col-TOC container for floating TOC"
  },
  {
    "category": "html",
    "checkType": "required_tag",
    "pattern": "<div class=\"sticky\">",
    "required": true,
    "suggestion": "Floating TOC requires sticky div inside col-TOC",
    "description": "Missing sticky property for floating TOC"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<div id=\"col-TOC\">\\s*<div class=\"sticky\">\\s*<div id=\"toc\"",
    "required": true,
    "suggestion": "Floating TOC structure must be: col-TOC > sticky > toc",
    "description": "Incorrect nesting order for floating TOC elements"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<div id=\"toc\"[^>]*>\\s*</div>\\s*</div>\\s*</div>\\s*<div id=\"col-body\">",
    "required": true,
    "suggestion": "Floating TOC must be followed by col-body div",
    "description": "Missing col-body div after floating TOC structure"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<div id=\"toc\"(?![^>]*role=\"navigation\")",
    "required": false,
    "suggestion": "Add role=\"navigation\" to floating TOC",
    "description": "Floating TOC missing navigation role"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<div id=\"toc\"(?![^>]*aria-label)",
    "required": false,
    "suggestion": "Add aria-label to floating TOC",
    "description": "Floating TOC missing aria-label attribute"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<div id=\"toc\"(?![^>]*tabindex=\"0\")",
    "required": false,
    "suggestion": "Add tabindex=\"0\" to floating TOC for keyboard access",
    "description": "Floating TOC missing tabindex attribute"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<div id=\"toc\"[^>]*>(?!.*<span class=\"sr-only\">)",
    "required": false,
    "suggestion": "Add screen reader description to floating TOC",
    "description": "Floating TOC missing screen reader description"
  },
  {
    "category": "html",
    "checkType": "required_tag",
    "pattern": "<div id=\"TOCRight\">",
    "required": true,
    "suggestion": "Right-side floating TOC requires TOCRight container",
    "description": "Missing TOCRight container"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<div id=\"TOCRight\">\\s*<div id=\"full\">",
    "required": true,
    "suggestion": "TOCRight must contain nested full div",
    "description": "Incorrect structure for right-side floating TOC"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<div id=\"TOCRight\"(?![^>]*role=\"navigation\")",
    "required": false,
    "suggestion": "Add role=\"navigation\" to right-side TOC",
    "description": "Right-side TOC missing navigation role"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<div id=\"TOCRight\"(?![^>]*aria-label)",
    "required": false,
    "suggestion": "Add aria-label to right-side TOC",
    "description": "Right-side TOC missing aria-label attribute"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<div id=\"TOCRight\"(?![^>]*tabindex=\"0\")",
    "required": false,
    "suggestion": "Add tabindex=\"0\" to right-side TOC",
    "description": "Right-side TOC missing tabindex attribute"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<div id=\"TOCRight\"[^>]*>(?!.*<span class=\"sr-only\">)",
    "required": false,
    "suggestion": "Add screen reader instructions to right-side TOC",
    "description": "Right-side TOC missing screen reader instructions"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<div id=\"toc\"[^>]*>(?!.*<(ul|ol|p))",
    "required": false,
    "suggestion": "TOC should contain list or paragraph elements",
    "description": "Empty or improperly structured TOC"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<div id=\"toc\"[^>]*>.*(<li>.*</li>.*){16}",
    "required": false,
    "suggestion": "TOC contains more than 15 items",
    "description": "Consider reorganizing your TOC structure"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<div id=\"col-TOC\">.*<div id=\"col-body\">(?!.*</div>\\s*</div>\\s*</div>)",
    "required": false,
    "suggestion": "Ensure proper closing tags for row and WatsonSOPBody",
    "description": "Incomplete closing structure for floating TOC layout"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<table(?![^>]*>[\\s\\S]*?<th)",
    "required": false,
    "suggestion": "Add header row with <th> elements to tables",
    "description": "Table missing header row"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<table(?![^>]*>[\\s\\S]*?<caption)",
    "required": false,
    "suggestion": "Add <caption> element to describe table purpose",
    "description": "Table missing caption for accessibility"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<th(?![^>]*scope=)",
    "required": false,
    "suggestion": "Add scope=\"col\" or scope=\"row\" to table headers",
    "description": "Table header missing scope attribute"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<table[^>]*class=\"(WhiteTable|Grey_2_tone|KeyTermsTable|DefinitionTable)\"",
    "required": true,
    "suggestion": "Apply one of the standard table style classes",
    "description": "Table must use approved style class"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<ol[^>]*start=",
    "required": false,
    "suggestion": "Remove start attribute unless required for specific numbering",
    "description": "Ordered list has start attribute"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<ol[^>]*type=",
    "required": false,
    "suggestion": "Remove type attribute; use CSS for list styling",
    "description": "Ordered list has type attribute"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<li[^>]*value=",
    "required": false,
    "suggestion": "Remove value attribute from list items",
    "description": "List item has value attribute"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<iframe(?![^>]*sandbox=)",
    "required": true,
    "suggestion": "Add sandbox attribute with appropriate permissions",
    "description": "Iframe missing sandbox attribute"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<iframe[^>]*sandbox=\"\"",
    "required": false,
    "suggestion": "Specify sandbox permissions: allow-same-origin allow-scripts allow-popups allow-forms",
    "description": "Iframe has empty sandbox attribute"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<iframe(?![^>]*title=)",
    "required": true,
    "suggestion": "Add title attribute to describe iframe content",
    "description": "Iframe missing title attribute"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<iframe[^>]*width=\"\\d+px\"",
    "required": false,
    "suggestion": "Use percentage width (e.g., width=\"100%\") for responsive design",
    "description": "Iframe has fixed pixel width"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<img(?![^>]*class=)[^>]*src=\"[^\"]*\\.(png|jpg|jpeg|gif)\"",
    "required": true,
    "suggestion": "Apply image class (flag, icon, tiny, flowchart, no_border, or zoom)",
    "description": "Image missing required style class"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<img(?![^>]*alt=)",
    "required": true,
    "suggestion": "Add alt attribute with descriptive text (max 160 characters)",
    "description": "Image missing alt attribute"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<img[^>]*alt=\"\"[^>]*>",
    "required": false,
    "suggestion": "Provide descriptive alt text or use decorative image handling",
    "description": "Image has empty alt attribute"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<img[^>]*style=\"[^\"]*border[^\"]*\"",
    "required": false,
    "suggestion": "Remove inline border styles; use CSS classes instead",
    "description": "Image has inline border style"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<img[^>]*width=\"\\d+\"[^>]*height=\"\\d+\"",
    "required": false,
    "suggestion": "Remove fixed dimensions; let CSS handle responsive sizing",
    "description": "Image has hardcoded dimensions"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<img[^>]*class=\"flowchart\"(?![^>]*aria-describedby=)",
    "required": true,
    "suggestion": "Add aria-describedby with detailed flowchart description",
    "description": "Flowchart image missing aria-describedby"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<button(?![^>]*type=)",
    "required": true,
    "suggestion": "Add type=\"button\" or type=\"submit\" to button elements",
    "description": "Button missing type attribute"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<button[^>]*class=\"collapsible\"(?![^>]*aria-expanded=)",
    "required": true,
    "suggestion": "Add aria-expanded=\"false\" to collapsible buttons",
    "description": "Collapsible button missing aria-expanded"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<button[^>]*class=\"collapsible\"(?![^>]*aria-controls=)",
    "required": false,
    "suggestion": "Add aria-controls to reference controlled section ID",
    "description": "Collapsible button missing aria-controls"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<button(?![^>]*aria-label=)(?![^>]*>[^<]+<)",
    "required": false,
    "suggestion": "Add aria-label or visible text content to button",
    "description": "Button missing accessible label"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<button[^>]*class=\"tablinks\"(?![^>]*role=\"tab\")",
    "required": false,
    "suggestion": "Add role=\"tab\" to tab buttons",
    "description": "Tab button missing role attribute"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "class=\"[^\"]*\\s{2,}[^\"]*\"",
    "required": false,
    "suggestion": "Remove extra spaces between class names",
    "description": "Multiple spaces in class attribute"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "id=\"[^\"]*\\s[^\"]*\"",
    "required": false,
    "suggestion": "ID attributes cannot contain spaces",
    "description": "ID attribute contains spaces"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<[^>]*class=\"\"[^>]*>",
    "required": false,
    "suggestion": "Remove empty class attributes",
    "description": "Empty class attribute found"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<[^>]*id=\"\"[^>]*>",
    "required": false,
    "suggestion": "Remove empty id attributes",
    "description": "Empty id attribute found"
  },
  {
    "category": "html",
    "checkType": "duplicate_check",
    "pattern": "id=\"([^\"]+)\"",
    "required": true,
    "suggestion": "Ensure all ID attributes are unique within the document",
    "description": "Duplicate ID found in document"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<strong[^>]*>(?:Click|Select|Choose|Press|Navigate)(?![^<]*</strong>[^<]*<[^>]*class=\"ui\")",
    "required": false,
    "suggestion": "Add class=\"ui\" to UI interaction elements",
    "description": "UI interaction element missing .ui class"
  },
  {
    "category": "accessibility",
    "checkType": "regex",
    "pattern": "<div[^>]*class=\"(note|warning|important|tip|example|exception|bestPractice|annotation|script|template)\"(?![^>]*role=)",
    "required": true,
    "suggestion": "Add role=\"note\" or role=\"alert\" to call-out divs",
    "description": "Call-out div missing ARIA role"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<div[^>]*class=\"(blue|orange|grey|ink)\"(?![^>]*<span[^>]*class=\"visually-hidden\")",
    "required": true,
    "suggestion": "Add visually-hidden span describing the colored section purpose",
    "description": "Colored margin missing screen reader description"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<div[^>]*class=\"tab\"(?![^>]*role=\"tablist\")",
    "required": true,
    "suggestion": "Add role=\"tablist\" to tab container",
    "description": "Tab container missing tablist role"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<div[^>]*class=\"tabcontent\"(?![^>]*role=\"tabpanel\")",
    "required": true,
    "suggestion": "Add role=\"tabpanel\" to tab content containers",
    "description": "Tab content missing tabpanel role"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<button[^>]*class=\"tablinks\"(?![^>]*aria-selected=)",
    "required": false,
    "suggestion": "Add aria-selected=\"true/false\" to tab buttons",
    "description": "Tab button missing aria-selected"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<div[^>]*class=\"flip-card\"(?![^>]*tabindex=)",
    "required": false,
    "suggestion": "Add tabindex=\"0\" to make flip cards keyboard accessible",
    "description": "Flip card missing tabindex for keyboard access"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<div[^>]*class=\"flip-card\"(?![^>]*aria-label=)",
    "required": false,
    "suggestion": "Add aria-label describing flip card interaction",
    "description": "Flip card missing aria-label"
  },
  {
    "category": "content_structure",
    "checkType": "percentage_check",
    "pattern": "<div[^>]*class=\"(note|warning|important|tip|example|exception|bestPractice|annotation|script|template)\"[^>]*>[\\s\\S]*?</div>",
    "required": false,
    "suggestion": "Visual callouts exceed 30% of content; consider restructuring to reduce cognitive load",
    "description": "Excessive visual callouts detected (>30% of content)"
  },
  {
    "category": "content_structure",
    "checkType": "percentage_check",
    "pattern": "<table[^>]*>[\\s\\S]*?</table>",
    "required": false,
    "suggestion": "Tables exceed 30% of content; consider using lists or prose for some information",
    "description": "Excessive table usage detected (>30% of content)"
  },
  {
    "category": "content_structure",
    "checkType": "percentage_check",
    "pattern": "<(strong|em|b|i)[^>]*>[^<]+</(strong|em|b|i)>",
    "required": false,
    "suggestion": "Text emphasis exceeds 30% of content; reduce highlighting to maintain effectiveness",
    "description": "Excessive text emphasis detected (>30% of content)"
  },
  {
    "category": "content_structure",
    "checkType": "combined_percentage_check",
    "pattern": "(<div[^>]*class=\"(note|warning|important|tip|example|exception|bestPractice|annotation|script|template)\"[^>]*>[\\s\\S]*?</div>|<table[^>]*>[\\s\\S]*?</table>|<(strong|em|b|i)[^>]*>[^<]+</(strong|em|b|i)>)",
    "required": false,
    "suggestion": "Combined visual elements (callouts, tables, emphasis) exceed 50% of content; restructure for better readability",
    "description": "Excessive combined visual elements detected (>50% of content)"
  },
  {
    "category": "content_structure",
    "checkType": "regex",
    "pattern": "(<div[^>]*class=\"(note|warning|important|tip|example|exception|bestPractice|annotation|script|template)\"[^>]*>[\\s\\S]*?</div>\\s*){3,}",
    "required": false,
    "suggestion": "Three or more consecutive callouts found; consolidate or separate with regular content",
    "description": "Multiple consecutive callouts reduce effectiveness"
  },
  {
    "category": "content_structure",
    "checkType": "regex",
    "pattern": "(<table[^>]*>[\\s\\S]*?</table>\\s*){3,}",
    "required": false,
    "suggestion": "Three or more consecutive tables found; consider consolidating or adding explanatory text",
    "description": "Multiple consecutive tables reduce readability"
  },
  {
    "category": "content_structure",
    "checkType": "ratio_check",
    "pattern": "callout_count / paragraph_count > 0.5",
    "required": false,
    "suggestion": "Callout-to-paragraph ratio exceeds 0.5; too many callouts dilute their impact",
    "description": "High callout-to-content ratio detected"
  },
  {
    "category": "content_structure",
    "checkType": "ratio_check",
    "pattern": "table_count / total_sections > 0.4",
    "required": false,
    "suggestion": "Table-to-section ratio exceeds 0.4; consider alternative content formats",
    "description": "High table-to-section ratio detected"
  },
  {
    "category": "accessibility",
    "checkType": "required_tag",
    "pattern": "<html lang=>",
    "required": true,
    "suggestion": "Add lang=\"en\" to <html> element",
    "description": "Root <html> missing language attribute"
  },
  {
    "category": "accessibility",
    "checkType": "regex",
    "pattern": "<label(?![^>]*for=)[^>]*>",
    "required": false,
    "suggestion": "Add for=\"id\" matching the input ID",
    "description": "Label missing for= attribute"
  },
  {
    "category": "accessibility",
    "checkType": "regex",
    "pattern": "<input[^>]*id=['\\\"][^'\\\"]+['\\\"][^>]*>(?![\\s\\S]*<label[^>]*for=['\\\"]\\1['\\\"]>)",
    "required": false,
    "suggestion": "Add matching <label for=\"\"> for each input",
    "description": "Input missing corresponding label"
  },
  {
    "category": "accessibility",
    "checkType": "regex",
    "pattern": "<input(?![^>]*(aria-label|title|id))[^\\>]*>",
    "required": false,
    "suggestion": "Add aria-label/title or label+id",
    "description": "Form input missing accessible name"
  },
  {
    "category": "accessibility",
    "checkType": "regex",
    "pattern": "<video(?![^>]*<track[^>]*kind=['\\\"]captions['\\\"])[\\s\\S]*?</video>",
    "required": false,
    "suggestion": "Add <track kind=\"captions\"> for videos",
    "description": "Video missing captions"
  },
  {
    "category": "accessibility",
    "checkType": "regex",
    "pattern": "<audio(?![^>]*transcript)[\\s\\S]*?</audio>",
    "required": false,
    "suggestion": "Provide transcript for audio content",
    "description": "Audio missing transcript"
  },
  {
    "category": "accessibility",
    "checkType": "regex",
    "pattern": "<video[^>]*autoplay(?![^>]*muted)",
    "required": false,
    "suggestion": "Autoplay videos must also be muted",
    "description": "Autoplay video without muted attribute"
  },
  {
    "category": "accessibility",
    "checkType": "regex",
    "pattern": "<audio[^>]*autoplay",
    "required": false,
    "suggestion": "Remove autoplay for audio",
    "description": "Autoplay audio violates accessibility"
  },
  {
    "category": "accessibility",
    "checkType": "regex",
    "pattern": "<a href=\"#?(?:(?![a-zA-Z0-9_-]).)*\">",
    "required": false,
    "suggestion": "Ensure href anchors point to valid IDs",
    "description": "Invalid internal link anchor"
  },
  {
    "category": "accessibility",
    "checkType": "regex",
    "pattern": "<div[^>]*tabindex=\"0\"(?![^>]*role=)",
    "required": false,
    "suggestion": "Focusable div missing role attribute non-interactive",
    "description": "Non-interactive element focusable"
  },
  {
    "category": "accessibility",
    "checkType": "regex",
    "pattern": "<nav(?![^>]*role=\"navigation\")",
    "required": false,
    "suggestion": "Add role=\"navigation\" to nav elements",
    "description": "Missing ARIA role navigation"
  },
  {
    "category": "accessibility",
    "checkType": "regex",
    "pattern": "<main(?![^>]*role=\"main\")",
    "required": true,
    "suggestion": "Add role=\"main\" to <main> region",
    "description": "Main landmark missing role attribute"
  },
  {
    "category": "accessibility",
    "checkType": "regex",
    "pattern": "<a class=\"skip-link\"(?![^>]*href=\"#main\")",
    "required": true,
    "suggestion": "Skip to content link must point to #main",
    "description": "Skip-link missing proper target"
  },
  {
    "category": "accessibility",
    "checkType": "regex",
    "pattern": "<a class=\"skip-link\">",
    "required": true,
    "suggestion": "Add skip-link to top of page",
    "description": "Missing skip-link for keyboard users"
  },
  {
    "category": "html",
    "checkType": "required_tag",
    "pattern": "<meta charset=\"UTF-8\">",
    "required": true,
    "suggestion": "Add <meta charset=\"UTF-8\"> to document head",
    "description": "Missing charset declaration"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<html(?![^>]*lang=)",
    "required": true,
    "suggestion": "Add lang=\"en\" to <html> element",
    "description": "Root <html> missing language attribute"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<html[^>]*lang=\"\"[^>]*>",
    "required": false,
    "suggestion": "Specify correct lang attribute value",
    "description": "Empty lang attribute detected"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<h1>[\\s\\S]*?<h1>",
    "required": false,
    "suggestion": "Use only one <h1> per page",
    "description": "Multiple <h1> elements found"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<h[3-6]>[\\s\\S]*?<h1>",
    "required": false,
    "suggestion": "Do not place lower headings before <h1>",
    "description": "Heading out of order (content before H1)"
  },
  {
  "category": "html",
  "checkType": "regex",
  "pattern": "<h2>.*?</h2>.*?<h4>",
  "required": false,
  "suggestion": "Use h3 before h4; maintain heading hierarchy",
  "description": "Heading hierarchy violated (h2 to h4 skip)"
},
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<h[5]>[\\s\\S]*?<h3>",
    "required": false,
    "suggestion": "Do not skip from h3 to h5",
    "description": "Heading hierarchy violation (h3\u2192h5)"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<h[6]>[\\s\\S]*?<h4>",
    "required": false,
    "suggestion": "Do not skip from h4 to h6",
    "description": "Heading hierarchy violation (h4\u2192h6)"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<h[2-6]>[\\s\\S]*?<h1>",
    "required": false,
    "suggestion": "H1 must appear before all other headings",
    "description": "H1 missing or placed too late"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<hr[^>]*>",
    "required": false,
    "suggestion": "Avoid using <hr>; use CSS borders instead",
    "description": "Non-semantic horizontal rule detected"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<header>(?![\\s\\S]*?<h1>)",
    "required": false,
    "suggestion": "Header must contain an <h1> element",
    "description": "Header missing page title"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<section>[\\s\\S]*?</section>(?![\\s\\S]*?<h2>)",
    "required": false,
    "suggestion": "Each section should begin with an <h2> heading",
    "description": "Section missing proper heading structure"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<article>[\\s\\S]*?</article>(?![\\s\\S]*?<h2>)",
    "required": false,
    "suggestion": "Article content requires heading",
    "description": "H2 missing from article"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<footer>(?![\\s\\S]*?copyright)",
    "required": false,
    "suggestion": "Include copyright statement in footer",
    "description": "Footer missing copyright text"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<table(?![^>]*summary=)(?![^>]*caption)",
    "required": false,
    "suggestion": "Add caption or summary describing table content",
    "description": "Table missing caption and summary"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<div[^>]*class=\"container\"(?![^>]*role=)",
    "required": false,
    "suggestion": "Add role=\"region\" or semantic element instead of generic container",
    "description": "Container div missing ARIA role"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<a href=\"#[^\"]+\">(?![\\s\\S]*id=\"[^\"]+\")",
    "required": false,
    "suggestion": "Ensure internal links reference existing IDs",
    "description": "Broken internal anchor reference"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<a[^>]*href=\"\"[^>]*>",
    "required": false,
    "suggestion": "Empty href attributes must be removed",
    "description": "Empty hyperlink reference"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<a[^>]*href=\"[^\"]*\"(?![^>]*rel=\"noopener\")",
    "required": false,
    "suggestion": "Add rel=\"noopener\" for links with target=\"_blank\"",
    "description": "Missing rel=\"noopener\" attribute"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<a[^>]*href=\" \">",
    "required": false,
    "suggestion": "Whitespace-only href attribute",
    "description": "Invalid href attribute"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<table[^>]*>\\s*<tr[^>]*>\\s*</tr>",
    "required": false,
    "suggestion": "Avoid empty table rows",
    "description": "Empty table row detected"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<table[^>]*>\\s*<tr[^>]*>\\s*<td[^>]*>\\s*</td>",
    "required": false,
    "suggestion": "Avoid empty table cells",
    "description": "Empty table cell detected"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<table[^>]*>\\s*(?![\\s\\S]*?<tr)",
    "required": false,
    "suggestion": "Table must contain row elements",
    "description": "Table missing rows"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<thead>(?![\\s\\S]*?</thead>)",
    "required": false,
    "suggestion": "Ensure proper <thead> structure",
    "description": "Malformed thead element"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<ul[^>]*class=\"numbers\">",
    "required": false,
    "suggestion": "Do not use custom numbered list classes; use <ol>",
    "description": "Improper list type (numbers class misuse)"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<li[^>]*style=",
    "required": false,
    "suggestion": "Remove inline styles from list items",
    "description": "Inline style applied to list item"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<td[^>]*colspan=\"1\"",
    "required": false,
    "suggestion": "Avoid unnecessary colspan=\"1\" attributes",
    "description": "Redundant colspan value"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<td[^>]*rowspan=\"1\"",
    "required": false,
    "suggestion": "Avoid unnecessary rowspan=\"1\" attributes",
    "description": "Redundant rowspan value"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<div[^>]*style=\"[^\"]*text-indent",
    "required": false,
    "suggestion": "Use CSS classes instead of inline text indentation",
    "description": "Inline text-indent styling found"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<div[^>]*style=\"[^\"]*(left|right):\\s*\\d+px",
    "required": false,
    "suggestion": "Do not position layout using inline left/right offsets",
    "description": "Inline positional offset detected"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<div[^>]*data-[a-zA-Z-]+=",
    "required": false,
    "suggestion": "Remove unapproved data-* attributes",
    "description": "Unauthorized data attribute found"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<o:p>",
    "required": false,
    "suggestion": "Remove MS Office O:P tags",
    "description": "Detected Microsoft Office markup"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "mso-[a-zA-Z-]+:",
    "required": false,
    "suggestion": "Remove MS Office mso-* inline styles",
    "description": "Microsoft Word styling detected"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<span[^>]*style=\"[^\"]*mso-",
    "required": false,
    "suggestion": "Remove pasted MS Office formatting",
    "description": "MSO inline formatting detected"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<style[^>]*>(?![\\s\\S]*@media)",
    "required": false,
    "suggestion": "Move inline <style> blocks to CSS files",
    "description": "Inline style block detected in content"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<style>[\\s\\S]*</style>",
    "required": false,
    "suggestion": "Do not include <style> in the content body",
    "description": "Style tags must not appear in content region"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<meta[^>]*http-equiv=['\\\"]pragma['\\\"]",
    "required": false,
    "suggestion": "Remove pragma no-cache meta usage",
    "description": "Deprecated pragma meta tag"
  },
  {
    "category": "html",
    "checkType": "regex",
    "pattern": "<script(?![^>]*src=)[^>]*>",
    "required": false,
    "suggestion": "Do not include inline <script> tags in content",
    "description": "Inline script tag found"
  },
{
  "category": "css",
  "checkType": "regex",
  "pattern": "<div[^>]*class=['\"][^'\"]*WatsonSOPBody[^'\"]*['\"][^>]*>[\\s\\S]*?style=['\"][^'\"]+['\"]",
  "required": true,
  "suggestion": "Remove inline styles from elements inside div.WatsonSOPBody. Use externally linked standardized CSS instead.",
  "description": "Inline styles detected inside WatsonSOPBody"
},
  {
    "category": "css",
    "checkType": "forbidden_property",
    "pattern": "#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})",
    "required": false,
    "suggestion": "Use Watson brand color variables instead of hex codes",
    "description": "Hard-coded hex color value detected"
  },
  {
    "category": "css",
    "checkType": "forbidden_property",
    "pattern": "rgb\\([^)]*\\)",
    "required": false,
    "suggestion": "Use Watson brand color variables instead of rgb()",
    "description": "Hard-coded rgb() color detected"
  },
  {
    "category": "css",
    "checkType": "forbidden_property",
    "pattern": "hsl\\([^)]*\\)",
    "required": false,
    "suggestion": "Use Watson brand color variables instead of hsl()",
    "description": "Hard-coded hsl() color detected"
  },
  {
    "category": "css",
    "checkType": "forbidden_property",
    "pattern": "font-family\\s*:\\s*(Arial|Calibri|Times New Roman|Roboto|Segoe UI)",
    "required": true,
    "suggestion": "Use Amazon Ember as the approved font",
    "description": "Non-approved font family detected"
  },
  {
    "category": "css",
    "checkType": "forbidden_property",
    "pattern": "font\\s*:\\s*[^;]*\\bArial\\b",
    "required": true,
    "suggestion": "Remove MS Word default font formatting",
    "description": "MS Word font usage detected"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "[0-9]+px",
    "required": true,
    "suggestion": "Use relative units ",
    "description": "Fixed pixel value used where not permitted"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "margin-(left|right|top|bottom)\\s*:\\s*\\d+px",
    "required": false,
    "suggestion": "Use CSS layout classes instead of fixed margins",
    "description": "Fixed margin detected"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "padding-(left|right|top|bottom)\\s*:\\s*\\d+px",
    "required": false,
    "suggestion": "Use CSS spacing utilities instead of fixed padding",
    "description": "Fixed padding detected"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "width\\s*:\\s*\\d+px",
    "required": false,
    "suggestion": "Use responsive width values (%auto)",
    "description": "Fixed width detected"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "height\\s*:\\s*\\d+px",
    "required": false,
    "suggestion": "Avoid fixed heights; use min-height or auto",
    "description": "Fixed height detected"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "position\\s*:\\s*absolute",
    "required": true,
    "suggestion": "Avoid absolute positioning unless essential",
    "description": "Absolute positioning may break layout"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "position\\s*:\\s*fixed",
    "required": false,
    "suggestion": "Avoid fixed positioning in SOP content",
    "description": "Fixed positioning detected"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "top\\s*:\\s*\\d+px|left\\s*:\\s*\\d+px|right\\s*:\\s*\\d+px|bottom\\s*:\\s*\\d+px",
    "required": false,
    "suggestion": "Do not manually place elements using offsets",
    "description": "Manual offset positioning detected"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "transition\\s*:\\s*[^;]*(?<!none)",
    "required": true,
    "suggestion": "Ensure transitions include prefers-reduced-motion",
    "description": "Transition animation missing reduced-motion fallback"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "animation\\s*:\\s*(?!none)[^;]*",
    "required": true,
    "suggestion": "Ensure animations include prefers-reduced-motion",
    "description": "Animation missing reduced-motion compliance"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "@keyframes",
    "required": true,
    "suggestion": "Verify keyframes also support reduced motion",
    "description": "Motion keyframes detected; check compliance"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "filter\\s*:\\s*blur",
    "required": false,
    "suggestion": "Blur effects reduce readability",
    "description": "Blur CSS filter detected"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "opacity\\s*:\\s*0(\\.\\d+)?",
    "required": false,
    "suggestion": "Do not hide content visually; use aria-hidden if appropriate",
    "description": "Opacity used to hide elements"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "box-shadow\\s*:\\s*[^;]*0\\s+0\\s+0",
    "required": false,
    "suggestion": "Use meaningful shadows or remove entirely",
    "description": "Zero-value shadow detected"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "text-shadow\\s*:\\s*[^;]*",
    "required": false,
    "suggestion": "Avoid text-shadow; reduces accessibility",
    "description": "Text shadow detected"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "display\\s*:\\s*table",
    "required": true,
    "suggestion": "Use layout grid classes instead of CSS display:table",
    "description": "Table-based layout CSS detected"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "display\\s*:\\s*table-cell",
    "required": true,
    "suggestion": "Use flex/grid instead of table cells",
    "description": "Table-cell CSS layout detected"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "overflow\\s*:\\s*hidden",
    "required": true,
    "suggestion": "Ensure focus outlines are not clipped",
    "description": "Overflow hidden may clip focus outlines"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "overflow-x\\s*:\\s*scroll",
    "required": true,
    "suggestion": "Avoid horizontal scrolling in SOP content",
    "description": "Horizontal scroll detected"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "overflow-y\\s*:\\s*scroll",
    "required": false,
    "suggestion": "Verify content container is intended for scrolling",
    "description": "Vertical scroll container detected"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "background-image\\s*:\\s*url",
    "required": true,
    "suggestion": "Background images should not be used in SOP content",
    "description": "Background image detected"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "cursor\\s*:\\s*(wait|progress|none)",
    "required": false,
    "suggestion": "Do not override default cursors",
    "description": "CSS cursor override detected"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "mso-[a-zA-Z-]+:",
    "required": true,
    "suggestion": "Remove Microsoft Office CSS artifacts",
    "description": "MS Word styling found in CSS"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "webkit-margin",
    "required": true,
    "suggestion": "Remove browser-specific pasted formatting",
    "description": "Copy-paste vendor CSS detected"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "ms-grid",
    "required": true,
    "suggestion": "Avoid legacy MS grid syntax",
    "description": "Deprecated Microsoft grid detected"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "\\.dark-mode[^}]*color\\s*:\\s*(white|#fff)",
    "required": true,
    "suggestion": "Ensure dark-mode text has sufficient contrast",
    "description": "Dark-mode text color too light"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "\\.dark-mode[^}]*background[^}]*:\\s*(black|#000)",
    "required": true,
    "suggestion": "Ensure dark-mode backgrounds meet contrast standards",
    "description": "Dark-mode background too dark without contrast balancing"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "\\.dark-mode(?![^}]*--)",
    "required": false,
    "suggestion": "Ensure dark-mode uses CSS variables",
    "description": "Dark-mode class missing CSS variable usage"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "@media\\s*\\(min-width:\\s*1024px\\)",
    "required": true,
    "suggestion": "Ensure layout supports large screens",
    "description": "Missing large-screen breakpoint"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "@media\\s*\\(max-width:\\s*480px\\)",
    "required": true,
    "suggestion": "Ensure layout supports mobile devices",
    "description": "Missing mobile breakpoint"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "grid-template-columns\\s*:\\s*repeat\\(11fr\\)",
    "required": false,
    "suggestion": "Ensure multi-column layouts where appropriate",
    "description": "Grid using single column unnecessarily"
  },
  {
    "category": "css",
    "checkType": "regex",
    "pattern": "flex-direction\\s*:\\s*column(?![^}]*@media)",
    "required": true,
    "suggestion": "Ensure vertical flex layouts have responsive breakpoints",
    "description": "Column flex layout missing responsive variant"
  },
  {
    "category": "interactive",
    "checkType": "regex",
    "pattern": "<button[^>]*aria-controls=['\\\"]([^'\\\"]+)['\\\"](?![\\s\\S]*id=['\\\"]\\1['\\\"])",
    "required": false,
    "suggestion": "Ensure aria-controls target ID exists",
    "description": "aria-controls references a missing ID"
  },
  {
    "category": "interactive",
    "checkType": "regex",
    "pattern": "id=['\\\"]([^'\\\"]+)['\\\"](?![\\s\\S]*aria-controls=['\\\"]\\1['\\\"])",
    "required": false,
    "suggestion": "Ensure all collapsible regions are controlled by a button",
    "description": "Region missing corresponding controls"
  },
  {
    "category": "interactive",
    "checkType": "regex",
    "pattern": "<button(?![^>]*role=['\\\"]button['\\\"])",
    "required": true,
    "suggestion": "Add role=\"button\" or <button> element for clickable items",
    "description": "Clickable element missing button role"
  },
  {
    "category": "interactive",
    "checkType": "regex",
    "pattern": "(<div[^>]*role=['\\\"]button['\\\"])(?![^>]*tabindex=\"0\")",
    "required": true,
    "suggestion": "Add tabindex=\"0\" to role=\"button\" items",
    "description": "Div button missing keyboard focusability"
  },
  {
    "category": "interactive",
    "checkType": "regex",
    "pattern": "role=['\\\"]button['\\\"](?![\\s\\S]*onkeydown)",
    "required": true,
    "suggestion": "Add keyboard handlers for Space/Enter",
    "description": "Custom button missing keyboard activation"
  },
  {
    "category": "interactive",
    "checkType": "regex",
    "pattern": "<button[^>]*class=\"collapsible\"(?![^>]*aria-expanded=)",
    "required": true,
    "suggestion": "Add aria-expanded to collapsible buttons",
    "description": "Collapsible missing expanded state"
  },
  {
    "category": "interactive",
    "checkType": "regex",
    "pattern": "<button[^>]*class=\"collapsible\"(?![^>]*aria-controls=)",
    "required": true,
    "suggestion": "Add aria-controls referencing controlled panel",
    "description": "Collapsible missing aria-controls"
  },
  {
    "category": "interactive",
    "checkType": "regex",
    "pattern": "<div[^>]*class=\"collapsible-content\"(?![^>]*id=)",
    "required": true,
    "suggestion": "Add unique ID for collapsible content",
    "description": "Collapsible content missing ID"
  },
  {
    "category": "interactive",
    "checkType": "regex",
    "pattern": "<button[^>]*class=\"collapsible\"[^>]*aria-controls=['\\\"]([^'\\\"]+)['\\\"]>(?![\\s\\S]*?<div[^>]*id=['\\\"]\\1['\\\"])",
    "required": true,
    "suggestion": "aria-controls must match collapsible panel ID",
    "description": "Mismatched aria-controls/ID binding"
  },
  {
    "category": "interactive",
    "checkType": "regex",
    "pattern": "<div[^>]*role=['\\\"]tablist['\\\"](?![^>]*aria-orientation)",
    "required": true,
    "suggestion": "Add aria-orientation=\"horizontal\" or \"vertical\"",
    "description": "Tablist missing orientation"
  },
  {
    "category": "interactive",
    "checkType": "regex",
    "pattern": "<button[^>]*class=\"tablinks\"(?![^>]*role=['\\\"]tab['\\\"])",
    "required": true,
    "suggestion": "Add role=\"tab\" to tab buttons",
    "description": "Tab button missing tab role"
  },
  {
    "category": "interactive",
    "checkType": "regex",
    "pattern": "<button[^>]*role=['\\\"]tab['\\\"](?![^>]*aria-selected)",
    "required": true,
    "suggestion": "Add aria-selected=\"true/false\" to tabs",
    "description": "Tab missing aria-selected state"
  },
  {
    "category": "interactive",
    "checkType": "regex",
    "pattern": "<div[^>]*class=\"tabcontent\"(?![^>]*role=['\\\"]tabpanel['\\\"])",
    "required": true,
    "suggestion": "Add role=\"tabpanel\" to tab panels",
    "description": "Tab content missing panel role"
  },
  {
    "category": "interactive",
    "checkType": "regex",
    "pattern": "role=['\\\"]tab['\\\"](?![\\s\\S]*id=)",
    "required": true,
    "suggestion": "Add unique ID for each tab",
    "description": "Tab requires unique ID"
  },
  {
    "category": "interactive",
    "checkType": "regex",
    "pattern": "role=['\\\"]tabpanel['\\\"](?![\\s\\S]*aria-labelledby=)",
    "required": true,
    "suggestion": "Each tabpanel needs aria-labelledby",
    "description": "Tabpanel missing label linkage"
  },
  {
    "category": "interactive",
    "checkType": "regex",
    "pattern": "<button[^>]*role=['\\\"]tab['\\\"](?![^>]*aria-controls)",
    "required": true,
    "suggestion": "Add aria-controls to tabs to link to associated panel",
    "description": "Tab missing aria-controls"
  },
  {
    "category": "interactive",
    "checkType": "regex",
    "pattern": "<div[^>]*role=['\\\"]dialog['\\\"](?![^>]*aria-modal=)",
    "required": true,
    "suggestion": "Add aria-modal=\"true\" to dialogs",
    "description": "Dialog missing modal attribute"
  },
  {
    "category": "interactive",
    "checkType": "regex",
    "pattern": "<div[^>]*role=['\\\"]dialog['\\\"](?![^>]*aria-labelledby=)",
    "required": true,
    "suggestion": "Add aria-labelledby referencing dialog title",
    "description": "Dialog missing label"
  },
  {
    "category": "interactive",
    "checkType": "regex",
    "pattern": "<div[^>]*role=['\\\"]dialog['\\\"](?![^>]*tabindex=\"0\")",
    "required": true,
    "suggestion": "Make dialog focusable for trapping",
    "description": "Dialog missing focusability"
  },
  {
    "category": "interactive",
    "checkType": "regex",
    "pattern": "<div[^>]*role=['\\\"]dialog['\\\"](?![\\s\\S]*<button[^>]*class=\"close\")",
    "required": false,
    "suggestion": "Dialogs must include a close button",
    "description": "Dialog close button missing"
  },
  {
    "category": "interactive",
    "checkType": "regex",
    "pattern": "<div[^>]*class=\"tooltip\"(?![^>]*role=\"tooltip\")",
    "required": true,
    "suggestion": "Add role=\"tooltip\" for tooltips",
    "description": "Tooltip missing ARIA role"
  },
  {
    "category": "interactive",
    "checkType": "regex",
    "pattern": "<div[^>]*role=['\\\"]tooltip['\\\"](?![^>]*id=)",
    "required": true,
    "suggestion": "Add unique ID to tooltip",
    "description": "Tooltip missing ID"
  },
  {
    "category": "interactive",
    "checkType": "regex",
    "pattern": "aria-describedby=['\\\"]([^'\\\"]+)['\\\"](?![\\s\\S]*id=['\\\"]\\1['\\\"])",
    "required": false,
    "suggestion": "aria-describedby must reference a real element",
    "description": "Broken aria-describedby reference"
  },
  {
    "category": "interactive",
    "checkType": "regex",
    "pattern": "<div[^>]*class=\"modal\"(?![^>]*role=['\\\"]dialog['\\\"])",
    "required": true,
    "suggestion": "Modal containers must use role=\"dialog\"",
    "description": "Modal missing dialog role"
  },
  {
    "category": "interactive",
    "checkType": "regex",
    "pattern": "<div[^>]*class=\"flip-card\"(?![^>]*tabindex=\"0\")",
    "required": true,
    "suggestion": "Make flip-cards keyboard accessible",
    "description": "Flip card missing tabindex"
  },
  {
    "category": "interactive",
    "checkType": "regex",
    "pattern": "<div[^>]*class=\"flip-card\"(?![^>]*aria-label=)",
    "required": true,
    "suggestion": "Add aria-label describing interactive nature",
    "description": "Flip card missing aria-label"
  },
  {
    "category": "interactive",
    "checkType": "regex",
    "pattern": "<div[^>]*class=\"flip-card\"(?![\\s\\S]*onkeydown)",
    "required": false,
    "suggestion": "Add keyboard arrow/Enter support for flip cards",
    "description": "Flip card missing keyboard interaction"
  },
  {
    "category": "interactive",
    "checkType": "regex",
    "pattern": "<button[^>]*(onclick|onmouseover|onfocus|onkeydown)=",
    "required": false,
    "suggestion": "Move inline event handlers to JS files",
    "description": "Inline event handler detected"
  },
  {
    "category": "interactive",
    "checkType": "regex",
    "pattern": "document\\.write",
    "required": true,
    "suggestion": "Avoid using document.write in SOP content",
    "description": "Prohibited JS API detected"
  },
  {
    "category": "interactive",
    "checkType": "regex",
    "pattern": "alert\\(|prompt\\(|confirm\\(",
    "required": false,
    "suggestion": "Remove debug or blocking JS calls",
    "description": "Debug JS calls detected"
  },
  {
    "category": "interactive",
    "checkType": "regex",
    "pattern": "const\\s+[A-Za-z]+\\s*=\\s*document\\.querySelectorAll\\(['\\\"][^'\\\"]+['\\\"]\\)",
    "required": false,
    "suggestion": "Ensure querySelectorAll has a length check before iteration",
    "description": "querySelectorAll used unsafely"
  },
  {
    "category": "interactive",
    "checkType": "regex",
    "pattern": "getElementById\\([^)]*\\)(?![\\s\\S]*addEventListener)",
    "required": false,
    "suggestion": "Ensure elements retrieved by ID have event listeners or logic",
    "description": "Unused DOM reference detected"
  },
  {
    "category": "interactive",
    "checkType": "regex",
    "pattern": "<script(?![^>]*src=)[^>]*>",
    "required": false,
    "suggestion": "Inline script blocks not allowed in body",
    "description": "Inline script block found"
  }
];
})();