{"id":5097,"date":"2026-06-09T13:25:05","date_gmt":"2026-06-09T13:25:05","guid":{"rendered":"https:\/\/primetoolhub.com\/?page_id=5097"},"modified":"2026-08-08T06:30:28","modified_gmt":"2026-08-08T06:30:28","slug":"free-offline-pagespeed-code-analyzer","status":"publish","type":"page","link":"https:\/\/schoolict.net\/tools\/free-offline-pagespeed-code-analyzer\/","title":{"rendered":"Offline PageSpeed Code Analyzer &amp; Accessibility Validator"},"content":{"rendered":"<div class=\"pth-hero-section\">\n<div class=\"pth-hero-content\">\n<h2>Offline PageSpeed Code Analyzer &#038; Accessibility Validator<\/h2>\n<p>Instantly analyze your HTML code for PageSpeed, Core Web Vitals (CLS), and Accessibility errors 100% offline. Get live fixes and proposals.<\/p>\n<div id=\"pth-toc-placeholder\"><\/div>\n<\/p>\n<\/div>\n<div class=\"pth-hero-image\">\n        <img decoding=\"async\" data-no-lazy=\"1\" src=\"https:\/\/schoolict.net\/tools\/wp-content\/uploads\/2026\/06\/pagespeed-code-analyzer-1-800x447.jpeg\" width=\"450\" height=\"253\" alt=\"pagespeed code analyzer\">\n    <\/div>\n<\/div>\n\n\n<div class=\"wp-block-rank-math-toc-block\" id=\"rank-math-toc\"><h2>Table of Contents<\/h2><nav><ul><li><a href=\"#\ud83d\udfe5-the-mechanics-behind-the-page-speed-code-analyzer\">\ud83d\udfe5 The Mechanics of Local DOM Evaluation<\/a><ul><li><a href=\"#\ud83d\udfe7-memory-allocation-and-syntax-parsing\">\ud83d\udfe7 Memory Allocation and Syntax Parsing<\/a><\/li><li><a href=\"#\ud83d\udfe8-algorithmic-accessibility-checks\">\ud83d\udfe8 Algorithmic Accessibility Checks<\/a><\/li><li><a href=\"#\ud83d\udfe9-regular-expression-auto-correction\">\ud83d\udfe9 Regular Expression Auto-Correction<\/a><\/li><li><a href=\"#\ud83d\udfea-performance-and-cpu-execution-context\">\ud83d\udfea Performance and CPU Execution Context<\/a><\/li><\/ul><\/li><\/ul><\/nav><\/div>\n\n\n<!-- ================================================================\r\n  CODE QUALITY & ACCESSIBILITY ANALYZER v6.0  (PrimeToolHub #65)\r\n  Slug: free-offline-pagespeed-code-analyzer\r\n  100% offline, self-contained (no external libraries or image hosts).\r\n  Scoped to #pth-analyzer-wrap. Woody HTML snippet - HTML + Vanilla JS (ES5) + scoped CSS.\r\n  This tool ANALYSES, SCORES and AUTO-FIXES markup. General HTML\/CSS\/JS editing cross-links\r\n  to the Offline HTML Editor; Tailwind components to the Tailwind Studio (rule E5).\r\n================================================================= -->\r\n\r\n<style>\r\n#pth-analyzer-wrap *, #pth-analyzer-wrap *::before, #pth-analyzer-wrap *::after { box-sizing: border-box; min-width: 0; }\r\n#pth-analyzer-wrap { font-family: 'Inter', system-ui, -apple-system, \"Segoe UI\", sans-serif; color: #1e293b; font-weight: 500; line-height: 1.6; max-width: 1480px; margin: 0 auto; padding: 0 20px; }\r\n#pth-analyzer-wrap .pth-an-topbar { display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap; background: #fff; border: 1px solid #e2e8f0; border-radius: 14px; padding: 16px 22px; margin-bottom: 16px; }\r\n#pth-analyzer-wrap .pth-an-brand { display: flex; align-items: center; gap: 14px; }\r\n#pth-analyzer-wrap .pth-an-icon { width: 46px; height: 46px; border-radius: 11px; background: #4338ca; color: #fff; display: flex; align-items: center; justify-content: center; font-size: 22px; flex: 0 0 auto; }\r\n#pth-analyzer-wrap .pth-an-title { font-size: 1.4rem; font-weight: 800; color: #0f172a; margin: 0; }\r\n#pth-analyzer-wrap .pth-an-sub { font-size: 0.8rem; color: #334155; font-weight: 600; margin: 2px 0 0; }\r\n#pth-analyzer-wrap .pth-an-badges { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }\r\n#pth-analyzer-wrap .pth-an-badge { font-size: 0.72rem; font-weight: 800; padding: 5px 11px; border-radius: 20px; border: 1px solid #e2e8f0; color: #334155; background: #f8fafc; }\r\n#pth-analyzer-wrap .pth-an-badge.off { color: #047857; border-color: #a7f3d0; background: #ecfdf5; }\r\n#pth-analyzer-wrap .pth-an-badge.ver { color: #4338ca; border-color: #c7d2fe; background: #eef2ff; }\r\n\r\n#pth-analyzer-wrap .pth-an-tabs { display: flex; gap: 6px; background: #f1f5f9; border: 1px solid #e2e8f0; border-radius: 12px; padding: 5px; margin-bottom: 16px; flex-wrap: wrap; }\r\n#pth-analyzer-wrap .pth-an-tab { flex: 1 1 auto; min-width: 120px; padding: 11px 13px; border: none; cursor: pointer; border-radius: 8px; background: transparent; color: #334155; font-family: inherit; font-size: 0.88rem; font-weight: 700; }\r\n#pth-analyzer-wrap .pth-an-tab:hover { color: #0f172a; }\r\n#pth-analyzer-wrap .pth-an-tab.active { background: #4338ca; color: #fff; }\r\n#pth-analyzer-wrap .pth-an-panel { display: none; }\r\n#pth-analyzer-wrap .pth-an-panel.active { display: block; }\r\n\r\n#pth-analyzer-wrap .pth-an-card { background: #fff; border: 1px solid #e2e8f0; border-radius: 14px; padding: 18px; margin-bottom: 16px; }\r\n#pth-analyzer-wrap .pth-an-ctitle { font-size: 0.72rem; font-weight: 800; text-transform: uppercase; letter-spacing: 1px; color: #4338ca; margin: 0 0 12px; }\r\n#pth-analyzer-wrap textarea.pth-an-code { width: 100%; min-height: 240px; resize: vertical; border: 1.5px solid #e2e8f0; border-radius: 10px; padding: 12px 14px; font-family: 'JetBrains Mono', ui-monospace, Menlo, monospace; font-size: 0.82rem; line-height: 1.6; color: #e2e8f0; background: #0f172a; outline: none; white-space: pre; overflow: auto; }\r\n#pth-analyzer-wrap textarea.pth-an-code:focus { border-color: #4338ca; }\r\n\r\n#pth-analyzer-wrap .pth-an-btn { display: inline-flex; align-items: center; gap: 7px; padding: 10px 16px; border-radius: 8px; border: 1px solid transparent; font-family: inherit; font-size: 0.86rem; font-weight: 700; cursor: pointer; }\r\n#pth-analyzer-wrap .pth-an-btn.sm { padding: 7px 12px; font-size: 0.8rem; }\r\n#pth-analyzer-wrap .pth-an-primary { background: #4338ca; color: #fff; } #pth-analyzer-wrap .pth-an-primary:hover { background: #3730a3; }\r\n#pth-analyzer-wrap .pth-an-ghost { background: #f8fafc; color: #334155; border-color: #e2e8f0; } #pth-analyzer-wrap .pth-an-ghost:hover { background: #f1f5f9; color: #0f172a; }\r\n#pth-analyzer-wrap .pth-an-danger { background: #fef2f2; color: #991b1b; border-color: #fecaca; } #pth-analyzer-wrap .pth-an-danger:hover { background: #fee2e2; }\r\n#pth-analyzer-wrap .pth-an-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }\r\n\r\n#pth-analyzer-wrap .pth-an-scores { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 14px; margin-bottom: 18px; }\r\n#pth-analyzer-wrap .pth-an-gauge { border: 1px solid #e2e8f0; border-radius: 14px; padding: 16px; text-align: center; background: #fff; }\r\n#pth-analyzer-wrap .pth-an-ring { position: relative; width: 84px; height: 84px; margin: 0 auto 10px; border-radius: 50%; display: flex; align-items: center; justify-content: center; }\r\n#pth-analyzer-wrap .pth-an-ring b { position: relative; font-size: 1.4rem; font-weight: 800; color: #0f172a; }\r\n#pth-analyzer-wrap .pth-an-gauge .lab { font-size: 0.8rem; font-weight: 800; color: #334155; }\r\n#pth-analyzer-wrap .pth-an-overall { grid-column: 1 \/ -1; display: flex; align-items: center; justify-content: center; gap: 16px; background: #eef2ff; border-color: #c7d2fe; }\r\n#pth-analyzer-wrap .pth-an-overall .big { font-size: 2.6rem; font-weight: 800; }\r\n\r\n#pth-analyzer-wrap .pth-an-issue { border: 1px solid #e2e8f0; border-left-width: 4px; border-radius: 8px; padding: 12px 14px; margin-bottom: 8px; }\r\n#pth-analyzer-wrap .pth-an-issue.error { border-left-color: #dc2626; }\r\n#pth-analyzer-wrap .pth-an-issue.warning { border-left-color: #d97706; }\r\n#pth-analyzer-wrap .pth-an-issue.info { border-left-color: #2563eb; }\r\n#pth-analyzer-wrap .pth-an-issue .it { display: flex; justify-content: space-between; gap: 10px; align-items: baseline; }\r\n#pth-analyzer-wrap .pth-an-issue .tt { font-weight: 800; color: #0f172a; font-size: 0.9rem; }\r\n#pth-analyzer-wrap .pth-an-issue .sev { font-size: 0.66rem; font-weight: 800; text-transform: uppercase; padding: 2px 7px; border-radius: 20px; flex: 0 0 auto; }\r\n#pth-analyzer-wrap .sev.error { background: #fef2f2; color: #991b1b; } #pth-analyzer-wrap .sev.warning { background: #fffbeb; color: #92400e; } #pth-analyzer-wrap .sev.info { background: #eff6ff; color: #1e40af; }\r\n#pth-analyzer-wrap .pth-an-issue .de { font-size: 0.84rem; color: #334155; margin: 4px 0 0; }\r\n#pth-analyzer-wrap .pth-an-issue .fx { font-size: 0.82rem; color: #047857; margin: 6px 0 0; }\r\n#pth-analyzer-wrap .pth-an-issue .fx b { color: #065f46; }\r\n#pth-analyzer-wrap .pth-an-catlabel { font-size: 0.76rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.5px; color: #64748b; margin: 16px 0 8px; }\r\n\r\n#pth-analyzer-wrap .pth-an-metrics { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 8px; }\r\n#pth-analyzer-wrap .pth-an-metric { border: 1px solid #e2e8f0; border-radius: 8px; padding: 8px 12px; font-size: 0.8rem; }\r\n#pth-analyzer-wrap .pth-an-metric b { color: #4338ca; font-size: 1rem; display: block; }\r\n\r\n#pth-analyzer-wrap .pth-an-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }\r\n#pth-analyzer-wrap .pth-an-fg { display: flex; flex-direction: column; gap: 6px; }\r\n#pth-analyzer-wrap .pth-an-label { font-size: 0.72rem; font-weight: 800; text-transform: uppercase; color: #334155; }\r\n#pth-analyzer-wrap .pth-an-cin { display: flex; gap: 8px; align-items: center; }\r\n#pth-analyzer-wrap .pth-an-cin input[type=color] { width: 46px; height: 38px; border: 1px solid #e2e8f0; border-radius: 8px; padding: 2px; background: #fff; }\r\n#pth-analyzer-wrap .pth-an-cin input[type=text] { flex: 1; border: 1px solid #e2e8f0; border-radius: 8px; padding: 9px 11px; font-family: monospace; font-size: 0.86rem; background: #f8fafc; outline: none; }\r\n#pth-analyzer-wrap .pth-an-contrast-out { margin-top: 16px; border: 1px solid #e2e8f0; border-radius: 12px; overflow: hidden; }\r\n#pth-analyzer-wrap .pth-an-swatch { padding: 28px; text-align: center; font-size: 1.1rem; font-weight: 700; }\r\n#pth-analyzer-wrap .pth-an-ratio { display: flex; justify-content: space-around; padding: 14px; background: #f8fafc; flex-wrap: wrap; gap: 10px; }\r\n#pth-analyzer-wrap .pth-an-ratio .r { text-align: center; }\r\n#pth-analyzer-wrap .pth-an-ratio .r b { font-size: 1.3rem; color: #0f172a; display: block; }\r\n#pth-analyzer-wrap .pth-an-pass { font-size: 0.74rem; font-weight: 800; padding: 3px 9px; border-radius: 20px; }\r\n#pth-analyzer-wrap .pass-y { background: #ecfdf5; color: #047857; } #pth-analyzer-wrap .pass-n { background: #fef2f2; color: #991b1b; }\r\n\r\n#pth-analyzer-wrap iframe.pth-an-frame { width: 100%; height: 480px; border: 1px solid #e2e8f0; border-radius: 10px; background: #fff; }\r\n#pth-analyzer-wrap pre.pth-an-pre { background: #0f172a; color: #e2e8f0; border-radius: 10px; padding: 14px; overflow: auto; font-family: 'JetBrains Mono', ui-monospace, monospace; font-size: 0.8rem; line-height: 1.6; white-space: pre-wrap; word-break: break-word; max-height: 420px; }\r\n#pth-analyzer-wrap .pth-an-fixline { font-size: 0.84rem; color: #334155; padding: 5px 0; border-bottom: 1px dashed #e2e8f0; }\r\n#pth-analyzer-wrap .pth-an-fixline b { color: #047857; }\r\n#pth-analyzer-wrap .pth-an-note { font-size: 0.82rem; color: #334155; background: #eef2ff; border: 1px solid #c7d2fe; border-radius: 8px; padding: 10px 12px; margin-top: 14px; }\r\n#pth-analyzer-wrap .pth-an-note a { color: #4338ca; font-weight: 700; }\r\n#pth-analyzer-wrap .pth-an-empty { color: #64748b; text-align: center; padding: 30px; font-size: 0.9rem; }\r\n\r\n#pth-analyzer-wrap .pth-an-toast { position: fixed; bottom: 22px; right: 22px; z-index: 999999; display: none; padding: 13px 18px; border-radius: 10px; color: #fff; font-family: 'Inter', system-ui, sans-serif; font-size: 0.9rem; font-weight: 700; box-shadow: 0 8px 24px rgba(15,23,42,0.2); max-width: 320px; }\r\n#pth-analyzer-wrap .pth-an-toast.show { display: block; }\r\n\r\n@media (max-width: 768px) { #pth-analyzer-wrap { padding: 0 10px; } #pth-analyzer-wrap .pth-an-row { grid-template-columns: 1fr; } }\r\n<\/style>\r\n\r\n<div id=\"pth-analyzer-wrap\">\r\n\r\n  <div class=\"pth-an-topbar\">\r\n    <div class=\"pth-an-brand\">\r\n      <div class=\"pth-an-icon\" aria-hidden=\"true\">&#128269;<\/div>\r\n      <div>\r\n        <h2 class=\"pth-an-title\">Code Quality &amp; Accessibility Analyzer<\/h2>\r\n        <p class=\"pth-an-sub\">Score your HTML for performance, accessibility, SEO &amp; best practices &mdash; offline<\/p>\r\n      <\/div>\r\n    <\/div>\r\n    <div class=\"pth-an-badges\">\r\n      <span class=\"pth-an-badge ver\">v6.0<\/span>\r\n      <span class=\"pth-an-badge off\">&#128274; 100% Offline<\/span>\r\n      <button type=\"button\" class=\"pth-an-btn pth-an-ghost sm\" data-action=\"sample\">&#128161; Sample<\/button>\r\n      <button type=\"button\" class=\"pth-an-btn pth-an-ghost sm\" data-action=\"wide\">&#9906; Wide<\/button>\r\n      <button type=\"button\" class=\"pth-an-btn pth-an-danger sm\" data-action=\"clear\">&#128465;&#65039; Clear<\/button>\r\n    <\/div>\r\n  <\/div>\r\n\r\n  <div class=\"pth-an-tabs\" role=\"tablist\">\r\n    <button type=\"button\" class=\"pth-an-tab active\" data-tab=\"analyze\" role=\"tab\">&#128202; Analyze<\/button>\r\n    <button type=\"button\" class=\"pth-an-tab\" data-tab=\"fix\" role=\"tab\">&#10024; Auto-Fix<\/button>\r\n    <button type=\"button\" class=\"pth-an-tab\" data-tab=\"contrast\" role=\"tab\">&#127912; Contrast<\/button>\r\n    <button type=\"button\" class=\"pth-an-tab\" data-tab=\"preview\" role=\"tab\">&#128065;&#65039; Preview<\/button>\r\n  <\/div>\r\n\r\n  <!-- ===== ANALYZE ===== -->\r\n  <div class=\"pth-an-panel active\" data-panel=\"analyze\">\r\n    <div class=\"pth-an-card\">\r\n      <p class=\"pth-an-ctitle\">Paste your HTML<\/p>\r\n      <textarea id=\"an-code\" class=\"pth-an-code\" spellcheck=\"false\" placeholder=\"Paste a full HTML document or a fragment...\"><\/textarea>\r\n      <div class=\"pth-an-actions\">\r\n        <button type=\"button\" class=\"pth-an-btn pth-an-primary\" data-action=\"analyze\">&#128640; Analyze<\/button>\r\n        <button type=\"button\" class=\"pth-an-btn pth-an-ghost sm\" data-action=\"report-md\">&#128229; Report (MD)<\/button>\r\n        <button type=\"button\" class=\"pth-an-btn pth-an-ghost sm\" data-action=\"report-json\">&#128229; Report (JSON)<\/button>\r\n      <\/div>\r\n    <\/div>\r\n    <div id=\"an-results\"><\/div>\r\n    <div class=\"pth-an-note\">Want to write and edit the code rather than audit it? Use the <a href=\"https:\/\/schoolict.net\/tools\/offline-html-editor-with-css-js\/\" target=\"_blank\" rel=\"noopener\">Offline HTML Editor<\/a>. Building Tailwind components? Try the <a href=\"https:\/\/schoolict.net\/tools\/tailwind-component-builder\/\" target=\"_blank\" rel=\"noopener\">Tailwind &amp; ShadCN Studio<\/a>.<\/div>\r\n  <\/div>\r\n\r\n  <!-- ===== AUTO-FIX ===== -->\r\n  <div class=\"pth-an-panel\" data-panel=\"fix\">\r\n    <div class=\"pth-an-card\">\r\n      <p class=\"pth-an-ctitle\">Applied fixes<\/p>\r\n      <div id=\"an-fix-list\"><p class=\"pth-an-empty\">Run Analyze first, then come here for safe automatic fixes.<\/p><\/div>\r\n    <\/div>\r\n    <div class=\"pth-an-card\">\r\n      <p class=\"pth-an-ctitle\">Fixed code<\/p>\r\n      <pre id=\"an-fixed\" class=\"pth-an-pre\"><\/pre>\r\n      <div class=\"pth-an-actions\"><button type=\"button\" class=\"pth-an-btn pth-an-ghost sm\" data-action=\"copy-fixed\">&#128203; Copy<\/button><button type=\"button\" class=\"pth-an-btn pth-an-ghost sm\" data-action=\"use-fixed\">&#8618;&#65039; Send back to Analyze<\/button><\/div>\r\n    <\/div>\r\n  <\/div>\r\n\r\n  <!-- ===== CONTRAST ===== -->\r\n  <div class=\"pth-an-panel\" data-panel=\"contrast\">\r\n    <div class=\"pth-an-card\">\r\n      <p class=\"pth-an-ctitle\">WCAG contrast checker<\/p>\r\n      <div class=\"pth-an-row\">\r\n        <div class=\"pth-an-fg\"><label class=\"pth-an-label\" for=\"an-fg\">Text colour<\/label><div class=\"pth-an-cin\"><input type=\"color\" id=\"an-fg-c\" value=\"#475569\"><input type=\"text\" id=\"an-fg\" value=\"#475569\"><\/div><\/div>\r\n        <div class=\"pth-an-fg\"><label class=\"pth-an-label\" for=\"an-bg\">Background<\/label><div class=\"pth-an-cin\"><input type=\"color\" id=\"an-bg-c\" value=\"#ffffff\"><input type=\"text\" id=\"an-bg\" value=\"#ffffff\"><\/div><\/div>\r\n      <\/div>\r\n      <div class=\"pth-an-actions\"><button type=\"button\" class=\"pth-an-btn pth-an-primary sm\" data-action=\"check-contrast\">&#127912; Check<\/button><\/div>\r\n      <div id=\"an-contrast-out\"><\/div>\r\n    <\/div>\r\n  <\/div>\r\n\r\n  <!-- ===== PREVIEW ===== -->\r\n  <div class=\"pth-an-panel\" data-panel=\"preview\">\r\n    <div class=\"pth-an-card\">\r\n      <p class=\"pth-an-ctitle\">Sandboxed live preview<\/p>\r\n      <iframe id=\"an-frame\" class=\"pth-an-frame\" title=\"Live preview\" sandbox=\"allow-scripts\"><\/iframe>\r\n      <div class=\"pth-an-actions\"><button type=\"button\" class=\"pth-an-btn pth-an-primary sm\" data-action=\"refresh-preview\">&#8635; Refresh from Analyze code<\/button><\/div>\r\n    <\/div>\r\n  <\/div>\r\n\r\n  <div id=\"pth-an-toast\" class=\"pth-an-toast\" role=\"status\" aria-live=\"polite\"><\/div>\r\n<\/div>\r\n\r\n<script data-no-optimize=\"1\" data-no-minify=\"1\" data-cfasync=\"false\">\r\n(function () {\r\n  'use strict';\r\n  if (window.pthAnalyzerApp) { return; }\r\n  var LT = '<', GT = '>';\r\n  var CATS = ['Performance', 'Accessibility', 'SEO', 'Best Practices'];\r\n  var WEIGHT = { error: 15, warning: 7, info: 3 };\r\n\r\n  var App = {\r\n    initialized: false, root: null, lastResult: null, fixedCode: '',\r\n\r\n    init: function () { this.root = document.getElementById('pth-analyzer-wrap'); if (!this.root) { return; } this.attach(); },\r\n\r\n    attach: function () {\r\n      var self = this;\r\n      this.root.addEventListener('click', function (e) {\r\n        var el = e.target;\r\n        while (el && el !== self.root && !el.getAttribute('data-action') && !el.getAttribute('data-tab')) { el = el.parentNode; }\r\n        if (!el || el === self.root) { return; }\r\n        if (el.getAttribute('data-tab')) { self.switchTab(el.getAttribute('data-tab')); return; }\r\n        var a = el.getAttribute('data-action');\r\n        if (a) { try { self.handle(a); } catch (err) { self.toast('Something went wrong.', 'error'); if (window.console) { console.error('[Analyzer]', err); } } }\r\n      });\r\n      this.root.addEventListener('input', function (e) {\r\n        if (e.target.id === 'an-fg' || e.target.id === 'an-bg') { var c = document.getElementById(e.target.id + '-c'); if (c && \/^#[0-9a-fA-F]{6}$\/.test(e.target.value)) { c.value = e.target.value; } }\r\n        if (e.target.id === 'an-fg-c') { document.getElementById('an-fg').value = e.target.value; }\r\n        if (e.target.id === 'an-bg-c') { document.getElementById('an-bg').value = e.target.value; }\r\n      });\r\n    },\r\n\r\n    handle: function (a) {\r\n      switch (a) {\r\n        case 'sample': this.loadSample(); break;\r\n        case 'wide': this.root.className = this.root.className.indexOf('full') > -1 ? '' : 'full'; break;\r\n        case 'clear': this.clearAll(); break;\r\n        case 'analyze': this.analyze(); break;\r\n        case 'report-md': this.report('md'); break;\r\n        case 'report-json': this.report('json'); break;\r\n        case 'copy-fixed': this.copyFixed(); break;\r\n        case 'use-fixed': this.useFixed(); break;\r\n        case 'check-contrast': this.checkContrast(); break;\r\n        case 'refresh-preview': this.refreshPreview(); break;\r\n      }\r\n    },\r\n\r\n    switchTab: function (name) {\r\n      var tabs = this.root.querySelectorAll('.pth-an-tab'), i;\r\n      for (i = 0; i < tabs.length; i++) { tabs[i].className = 'pth-an-tab' + (tabs[i].getAttribute('data-tab') === name ? ' active' : ''); }\r\n      var panels = this.root.querySelectorAll('.pth-an-panel');\r\n      for (i = 0; i < panels.length; i++) { panels[i].className = 'pth-an-panel' + (panels[i].getAttribute('data-panel') === name ? ' active' : ''); }\r\n      if (name === 'preview') { this.refreshPreview(); }\r\n    },\r\n\r\n    code: function () { var e = document.getElementById('an-code'); return e ? e.value : ''; },\r\n\r\n    \/* ================= ANALYSIS ENGINE ================= *\/\r\n    analyze: function () {\r\n      var html = this.code();\r\n      if (!html.replace(\/\\s\/g, '')) { this.toast('Paste some HTML first.', 'error'); return; }\r\n      var issues = [];\r\n      this.checkPerformance(html, issues);\r\n      this.checkAccessibility(html, issues);\r\n      this.checkSeo(html, issues);\r\n      this.checkBestPractices(html, issues);\r\n      var scores = this.score(issues);\r\n      var metrics = this.metrics(html);\r\n      this.lastResult = { issues: issues, scores: scores, metrics: metrics };\r\n      this.renderResults();\r\n      this.buildFixes(html);\r\n      this.toast('Analysis complete - overall score ' + scores.overall + '.', 'success');\r\n    },\r\n\r\n    count: function (html, re) { var m = html.match(re); return m ? m.length : 0; },\r\n\r\n    checkPerformance: function (html, out) {\r\n      var imgs = html.match(\/<img\\b[^>]*>\/gi) || [];\r\n      var noDim = 0, i;\r\n      for (i = 0; i < imgs.length; i++) { if (!\/\\bwidth=\/i.test(imgs[i]) || !\/\\bheight=\/i.test(imgs[i])) { noDim++; } }\r\n      if (noDim) { out.push(this.iss('Performance', 'warning', noDim + ' image(s) without width\/height', 'Images without explicit dimensions cause layout shift (CLS) as they load.', 'Add width and height attributes, or reserve space with CSS aspect-ratio.')); }\r\n      var noLazy = 0;\r\n      for (i = 0; i < imgs.length; i++) { if (!\/loading=\/i.test(imgs[i])) { noLazy++; } }\r\n      if (noLazy > 2) { out.push(this.iss('Performance', 'info', noLazy + ' image(s) not lazy-loaded', 'Off-screen images can defer loading to speed up the first paint.', 'Add loading=\"lazy\" to below-the-fold images.')); }\r\n      var inlineStyles = this.count(html, \/\\sstyle=\/gi);\r\n      if (inlineStyles > 5) { out.push(this.iss('Performance', 'info', inlineStyles + ' inline style attributes', 'Heavy inline styling bloats the HTML and cannot be cached separately.', 'Move repeated styles into a stylesheet or class.')); }\r\n      var headScripts = (html.match(\/<head[\\s\\S]*?<\\\/head>\/i) || [''])[0];\r\n      var blocking = 0; var sm = headScripts.match(\/<script\\b[^>]*src=[^>]*>\/gi) || [];\r\n      for (i = 0; i < sm.length; i++) { if (!\/(defer|async)\/i.test(sm[i])) { blocking++; } }\r\n      if (blocking) { out.push(this.iss('Performance', 'warning', blocking + ' render-blocking script(s) in head', 'A synchronous script in the head blocks the page from rendering until it loads.', 'Add defer (or async), or move the script before the closing body tag.')); }\r\n      var deprecated = html.match(\/<(center|font|marquee|blink|big|strike)\\b\/gi) || [];\r\n      if (deprecated.length) { out.push(this.iss('Performance', 'warning', 'Deprecated tags used', 'Tags like center, font and marquee are obsolete and force fallback rendering.', 'Replace them with CSS.')); }\r\n    },\r\n\r\n    checkAccessibility: function (html, out) {\r\n      var imgs = html.match(\/<img\\b[^>]*>\/gi) || [], noAlt = 0, i;\r\n      for (i = 0; i < imgs.length; i++) { if (!\/\\balt=\/i.test(imgs[i])) { noAlt++; } }\r\n      if (noAlt) { out.push(this.iss('Accessibility', 'error', noAlt + ' image(s) missing alt text', 'Screen readers cannot describe images without an alt attribute.', 'Add descriptive alt text, or alt=\"\" for purely decorative images.')); }\r\n      if (\/<html\\b\/i.test(html) && !\/<html\\b[^>]*\\blang=\/i.test(html)) { out.push(this.iss('Accessibility', 'error', 'Missing lang attribute on html', 'Assistive tech needs the page language to pronounce content correctly.', 'Add lang=\"en\" (or the correct language) to the html tag.')); }\r\n      var inputs = html.match(\/<input\\b[^>]*>\/gi) || [], unlabeled = 0;\r\n      for (i = 0; i < inputs.length; i++) { if (\/type=[\"']?(hidden|submit|button)\/i.test(inputs[i])) { continue; } if (!\/aria-label|aria-labelledby|\\bid=\/i.test(inputs[i])) { unlabeled++; } }\r\n      if (unlabeled) { out.push(this.iss('Accessibility', 'warning', unlabeled + ' form field(s) may lack a label', 'Inputs need an associated label or aria-label to be usable with a screen reader.', 'Add a <label for> pointing at the input id, or an aria-label.')); }\r\n      var headings = html.match(\/<h([1-6])\\b\/gi) || [], levels = [], prev = 0, skip = false;\r\n      for (i = 0; i < headings.length; i++) { var lv = parseInt(headings[i].replace(\/\\D\/g, ''), 10); if (prev && lv > prev + 1) { skip = true; } prev = lv; }\r\n      if (skip) { out.push(this.iss('Accessibility', 'warning', 'Heading levels skip a level', 'Jumping from an h2 straight to an h4 breaks the document outline.', 'Use headings in order without skipping levels.')); }\r\n      if (\/tabindex=[\"']?[1-9]\/i.test(html)) { out.push(this.iss('Accessibility', 'warning', 'Positive tabindex found', 'A positive tabindex forces an unnatural focus order that confuses keyboard users.', 'Use tabindex=\"0\" or rely on natural DOM order.')); }\r\n      var emptyLinks = html.match(\/<a\\b(?![^>]*href)[^>]*>\/gi) || [];\r\n      if (emptyLinks.length) { out.push(this.iss('Accessibility', 'info', 'Link(s) without href', 'An anchor without href is not focusable or announced as a link.', 'Add an href, or use a <button> for actions.')); }\r\n    },\r\n\r\n    checkSeo: function (html, out) {\r\n      if (!\/<title\\b[^>]*>[^<]\/i.test(html)) { out.push(this.iss('SEO', 'error', 'Missing or empty title', 'The title is the most important on-page SEO signal and the browser tab label.', 'Add a concise, descriptive <title>.')); }\r\n      if (!\/<meta\\b[^>]*name=[\"']description[\"']\/i.test(html)) { out.push(this.iss('SEO', 'warning', 'Missing meta description', 'The description often becomes the search-result snippet.', 'Add a meta description under about 160 characters.')); }\r\n      if (!\/<meta\\b[^>]*name=[\"']viewport[\"']\/i.test(html)) { out.push(this.iss('SEO', 'warning', 'Missing viewport meta', 'Without a viewport tag the page will not scale correctly on phones.', 'Add <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">.')); }\r\n      var h1 = html.match(\/<h1\\b\/gi) || [];\r\n      if (h1.length === 0) { out.push(this.iss('SEO', 'warning', 'No h1 heading', 'Each page should have one clear h1 describing its main topic.', 'Add a single h1.')); }\r\n      else if (h1.length > 1) { out.push(this.iss('SEO', 'info', h1.length + ' h1 headings', 'Multiple h1 tags dilute the page topic signal.', 'Keep one h1 and use h2\/h3 for sub-sections.')); }\r\n    },\r\n\r\n    checkBestPractices: function (html, out) {\r\n      if (\/<html\\b\/i.test(html) && !(new RegExp('<!' + 'doctype', 'i')).test(html)) { out.push(this.iss('Best Practices', 'warning', 'Missing doctype', 'Without a doctype the browser may fall back to quirks mode.', 'Add ' + LT + '!DOCTYPE html' + GT + ' as the very first line.')); }\r\n      if (\/<head\\b\/i.test(html) && !\/charset=\/i.test(html)) { out.push(this.iss('Best Practices', 'warning', 'No charset declared', 'A missing charset can garble special characters.', 'Add <meta charset=\"utf-8\"> as the first thing in head.')); }\r\n      var blanks = html.match(\/<a\\b[^>]*target=[\"']_blank[\"'][^>]*>\/gi) || [], unsafe = 0, i;\r\n      for (i = 0; i < blanks.length; i++) { if (!\/rel=[\"'][^\"']*noopener\/i.test(blanks[i])) { unsafe++; } }\r\n      if (unsafe) { out.push(this.iss('Best Practices', 'error', unsafe + ' target=\"_blank\" link(s) without rel=\"noopener\"', 'Opening a link in a new tab without noopener lets the new page access window.opener (a security risk).', 'Add rel=\"noopener\" (or noopener noreferrer).')); }\r\n      var onh = html.match(\/\\son[a-z]+=[\"']\/gi) || [];\r\n      if (onh.length) { out.push(this.iss('Best Practices', 'info', onh.length + ' inline event handler(s)', 'Inline handlers like onclick mix behaviour into markup and are blocked by strict CSP.', 'Attach listeners in JavaScript with addEventListener.')); }\r\n      if (\/[\"'(]http:\\\/\\\/\/i.test(html)) { out.push(this.iss('Best Practices', 'warning', 'Insecure http:\/\/ resource', 'Loading resources over http on an https page triggers mixed-content blocking.', 'Use https:\/\/ URLs.')); }\r\n    },\r\n\r\n    iss: function (cat, sev, title, detail, fix) { return { cat: cat, sev: sev, title: title, detail: detail, fix: fix }; },\r\n\r\n    score: function (issues) {\r\n      var pen = { Performance: 0, Accessibility: 0, SEO: 0, 'Best Practices': 0 }, i;\r\n      for (i = 0; i < issues.length; i++) { pen[issues[i].cat] += WEIGHT[issues[i].sev]; }\r\n      var s = {}, sum = 0;\r\n      for (i = 0; i < CATS.length; i++) { var v = Math.max(0, 100 - pen[CATS[i]]); s[CATS[i]] = v; sum += v; }\r\n      s.overall = Math.round(sum \/ CATS.length);\r\n      return s;\r\n    },\r\n\r\n    metrics: function (html) {\r\n      var els = (html.match(\/<[a-z][a-z0-9]*\\b\/gi) || []).length;\r\n      var cssRules = (html.match(\/\\{[^}]*\\}\/g) || []).length;\r\n      var maxDepth = 0;\r\n      try { if (typeof DOMParser !== 'undefined') { var doc = new DOMParser().parseFromString(html, 'text\/html'); maxDepth = this.depth(doc.body || doc.documentElement, 0); } } catch (e) {}\r\n      return { elements: els, cssRules: cssRules, depth: maxDepth };\r\n    },\r\n    depth: function (node, d) { if (!node || !node.children || !node.children.length) { return d; } var max = d; for (var i = 0; i < node.children.length; i++) { var c = this.depth(node.children[i], d + 1); if (c > max) { max = c; } } return max; },\r\n\r\n    \/* ================= RENDER ================= *\/\r\n    renderResults: function () {\r\n      var host = document.getElementById('an-results'); if (!host) { return; }\r\n      host.innerHTML = '';\r\n      var r = this.lastResult;\r\n      var card = document.createElement('div'); card.className = 'pth-an-card';\r\n      var grid = document.createElement('div'); grid.className = 'pth-an-scores';\r\n      var overall = document.createElement('div'); overall.className = 'pth-an-gauge pth-an-overall';\r\n      var ob = document.createElement('div'); var big = document.createElement('div'); big.className = 'big'; big.style.color = this.scoreColor(r.scores.overall); big.textContent = r.scores.overall; var ol = document.createElement('div'); ol.className = 'lab'; ol.textContent = 'Overall score'; ob.appendChild(big); ob.appendChild(ol); overall.appendChild(ob);\r\n      grid.appendChild(overall);\r\n      for (var i = 0; i < CATS.length; i++) { grid.appendChild(this.gauge(CATS[i], r.scores[CATS[i]])); }\r\n      card.appendChild(grid);\r\n      var mrow = document.createElement('div'); mrow.className = 'pth-an-metrics';\r\n      mrow.appendChild(this.metric('Elements', r.metrics.elements));\r\n      mrow.appendChild(this.metric('CSS rules', r.metrics.cssRules));\r\n      mrow.appendChild(this.metric('Max nesting', r.metrics.depth || 'n\/a'));\r\n      mrow.appendChild(this.metric('Issues', r.issues.length));\r\n      card.appendChild(mrow);\r\n      host.appendChild(card);\r\n\r\n      var icard = document.createElement('div'); icard.className = 'pth-an-card';\r\n      var it = document.createElement('p'); it.className = 'pth-an-ctitle'; it.textContent = 'Issues & recommendations'; icard.appendChild(it);\r\n      if (!r.issues.length) { var ok = document.createElement('p'); ok.className = 'pth-an-empty'; ok.textContent = 'No issues detected. Nice and clean.'; icard.appendChild(ok); }\r\n      else {\r\n        for (var c = 0; c < CATS.length; c++) {\r\n          var catIssues = []; for (var j = 0; j < r.issues.length; j++) { if (r.issues[j].cat === CATS[c]) { catIssues.push(r.issues[j]); } }\r\n          if (!catIssues.length) { continue; }\r\n          var cl = document.createElement('p'); cl.className = 'pth-an-catlabel'; cl.textContent = CATS[c] + ' (' + catIssues.length + ')'; icard.appendChild(cl);\r\n          for (var k = 0; k < catIssues.length; k++) { icard.appendChild(this.issueEl(catIssues[k])); }\r\n        }\r\n      }\r\n      host.appendChild(icard);\r\n    },\r\n\r\n    gauge: function (label, val) {\r\n      var g = document.createElement('div'); g.className = 'pth-an-gauge';\r\n      var ring = document.createElement('div'); ring.className = 'pth-an-ring';\r\n      var col = this.scoreColor(val);\r\n      ring.style.background = 'conic-gradient(' + col + ' ' + (val * 3.6) + 'deg, #e2e8f0 0deg)';\r\n      var inner = document.createElement('div'); inner.style.position = 'absolute'; inner.style.width = '64px'; inner.style.height = '64px'; inner.style.background = '#fff'; inner.style.borderRadius = '50%';\r\n      var b = document.createElement('b'); b.textContent = val; b.style.color = col;\r\n      ring.appendChild(inner); ring.appendChild(b);\r\n      var lab = document.createElement('div'); lab.className = 'lab'; lab.textContent = label;\r\n      g.appendChild(ring); g.appendChild(lab); return g;\r\n    },\r\n    scoreColor: function (v) { if (v >= 90) { return '#059669'; } if (v >= 50) { return '#d97706'; } return '#dc2626'; },\r\n    metric: function (label, val) { var m = document.createElement('div'); m.className = 'pth-an-metric'; var b = document.createElement('b'); b.textContent = val; var s = document.createTextNode(label); m.appendChild(b); m.appendChild(s); return m; },\r\n\r\n    issueEl: function (iss) {\r\n      var d = document.createElement('div'); d.className = 'pth-an-issue ' + iss.sev;\r\n      var top = document.createElement('div'); top.className = 'it';\r\n      var tt = document.createElement('span'); tt.className = 'tt'; tt.textContent = iss.title;\r\n      var sv = document.createElement('span'); sv.className = 'sev ' + iss.sev; sv.textContent = iss.sev;\r\n      top.appendChild(tt); top.appendChild(sv); d.appendChild(top);\r\n      var de = document.createElement('p'); de.className = 'de'; de.textContent = iss.detail; d.appendChild(de);\r\n      var fx = document.createElement('p'); fx.className = 'fx'; var fb = document.createElement('b'); fb.textContent = 'Fix: '; fx.appendChild(fb); fx.appendChild(document.createTextNode(iss.fix)); d.appendChild(fx);\r\n      return d;\r\n    },\r\n\r\n    \/* ================= AUTO-FIX ================= *\/\r\n    buildFixes: function (html) {\r\n      var fixes = [], out = html;\r\n      if (\/<html\\b\/i.test(out) && !\/<html\\b[^>]*\\blang=\/i.test(out)) { out = out.replace(\/<html\\b\/i, '<html lang=\"en\"'); fixes.push('Added lang=\"en\" to the html tag.'); }\r\n      if (\/<head\\b\/i.test(out) && !\/charset=\/i.test(out)) { out = out.replace(\/<head\\b[^>]*>\/i, function (m) { return m + '\\n  ' + LT + 'meta charset=\"utf-8\"' + GT; }); fixes.push('Inserted <meta charset=\"utf-8\">.'); }\r\n      if (\/<head\\b\/i.test(out) && !\/name=[\"']viewport[\"']\/i.test(out)) { out = out.replace(\/<head\\b[^>]*>\/i, function (m) { return m + '\\n  ' + LT + 'meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"' + GT; }); fixes.push('Inserted a responsive viewport meta tag.'); }\r\n      var addedAlt = 0;\r\n      out = out.replace(\/<img\\b((?:(?!\\balt=)[^>])*)>\/gi, function (m, attrs) { addedAlt++; return '<img' + attrs + ' alt=\"\">'; });\r\n      if (addedAlt) { fixes.push('Added an empty alt=\"\" to ' + addedAlt + ' image(s) - fill these in with real descriptions.'); }\r\n      var fixedBlank = 0;\r\n      out = out.replace(\/<a\\b([^>]*target=[\"']_blank[\"'][^>]*)>\/gi, function (m, attrs) { if (\/rel=\/i.test(attrs)) { return m; } fixedBlank++; return '<a' + attrs + ' rel=\"noopener\">'; });\r\n      if (fixedBlank) { fixes.push('Added rel=\"noopener\" to ' + fixedBlank + ' new-tab link(s).'); }\r\n      this.fixedCode = out;\r\n      this.renderFixes(fixes, out);\r\n    },\r\n\r\n    renderFixes: function (fixes, code) {\r\n      var list = document.getElementById('an-fix-list'), pre = document.getElementById('an-fixed');\r\n      list.innerHTML = '';\r\n      if (!fixes.length) { var p = document.createElement('p'); p.className = 'pth-an-empty'; p.textContent = 'Nothing to auto-fix - the safe fixes were already in place.'; list.appendChild(p); }\r\n      else { for (var i = 0; i < fixes.length; i++) { var l = document.createElement('div'); l.className = 'pth-an-fixline'; var b = document.createElement('b'); b.textContent = '\\u2713 '; l.appendChild(b); l.appendChild(document.createTextNode(fixes[i])); list.appendChild(l); } }\r\n      if (pre) { pre.textContent = code; }\r\n    },\r\n\r\n    copyFixed: function () { var self = this; if (!this.fixedCode) { this.toast('Run Analyze first.', 'error'); return; } if (navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(this.fixedCode).then(function () { self.toast('Copied.', 'success'); })['catch'](function () {}); } },\r\n    useFixed: function () { if (!this.fixedCode) { return; } var e = document.getElementById('an-code'); if (e) { e.value = this.fixedCode; } this.switchTab('analyze'); this.analyze(); },\r\n\r\n    \/* ================= CONTRAST ================= *\/\r\n    checkContrast: function () {\r\n      var fg = this.parseColor(document.getElementById('an-fg').value), bg = this.parseColor(document.getElementById('an-bg').value);\r\n      var out = document.getElementById('an-contrast-out'); out.innerHTML = '';\r\n      if (!fg || !bg) { var p = document.createElement('p'); p.className = 'pth-an-empty'; p.textContent = 'Enter valid colours (hex like #1a2b3c or rgb).'; out.appendChild(p); return; }\r\n      var ratio = this.contrast(fg, bg);\r\n      var box = document.createElement('div'); box.className = 'pth-an-contrast-out';\r\n      var sw = document.createElement('div'); sw.className = 'pth-an-swatch'; sw.style.background = 'rgb(' + bg.join(',') + ')'; sw.style.color = 'rgb(' + fg.join(',') + ')'; sw.textContent = 'The quick brown fox jumps';\r\n      box.appendChild(sw);\r\n      var rr = document.createElement('div'); rr.className = 'pth-an-ratio';\r\n      rr.appendChild(this.ratioBox('Ratio', ratio.toFixed(2) + ':1', null));\r\n      rr.appendChild(this.ratioBox('Normal AA', ratio >= 4.5 ? 'Pass' : 'Fail', ratio >= 4.5));\r\n      rr.appendChild(this.ratioBox('Normal AAA', ratio >= 7 ? 'Pass' : 'Fail', ratio >= 7));\r\n      rr.appendChild(this.ratioBox('Large AA', ratio >= 3 ? 'Pass' : 'Fail', ratio >= 3));\r\n      box.appendChild(rr); out.appendChild(box);\r\n    },\r\n    ratioBox: function (label, val, pass) {\r\n      var r = document.createElement('div'); r.className = 'r'; var b = document.createElement('b'); b.textContent = val; var l = document.createElement('div'); l.style.fontSize = '0.74rem'; l.style.color = '#64748b'; l.textContent = label;\r\n      r.appendChild(b);\r\n      if (pass !== null) { var pill = document.createElement('span'); pill.className = 'pth-an-pass ' + (pass ? 'pass-y' : 'pass-n'); pill.textContent = pass ? 'PASS' : 'FAIL'; r.appendChild(pill); }\r\n      else { r.appendChild(l); }\r\n      if (pass !== null) { r.appendChild(l); }\r\n      return r;\r\n    },\r\n    parseColor: function (s) {\r\n      s = (s || '').replace(\/^\\s+|\\s+$\/g, '');\r\n      var m = s.match(\/^#?([0-9a-f]{6})$\/i); if (m) { var h = m[1]; return [parseInt(h.slice(0, 2), 16), parseInt(h.slice(2, 4), 16), parseInt(h.slice(4, 6), 16)]; }\r\n      m = s.match(\/^#?([0-9a-f]{3})$\/i); if (m) { var g = m[1]; return [parseInt(g[0] + g[0], 16), parseInt(g[1] + g[1], 16), parseInt(g[2] + g[2], 16)]; }\r\n      m = s.match(\/rgba?\\(\\s*(\\d+)[\\s,]+(\\d+)[\\s,]+(\\d+)\/i); if (m) { return [parseInt(m[1], 10), parseInt(m[2], 10), parseInt(m[3], 10)]; }\r\n      return null;\r\n    },\r\n    lum: function (rgb) { var a = []; for (var i = 0; i < 3; i++) { var v = rgb[i] \/ 255; a[i] = v <= 0.03928 ? v \/ 12.92 : Math.pow((v + 0.055) \/ 1.055, 2.4); } return 0.2126 * a[0] + 0.7152 * a[1] + 0.0722 * a[2]; },\r\n    contrast: function (fg, bg) { var l1 = this.lum(fg), l2 = this.lum(bg); var hi = Math.max(l1, l2), lo = Math.min(l1, l2); return (hi + 0.05) \/ (lo + 0.05); },\r\n\r\n    \/* ================= PREVIEW ================= *\/\r\n    refreshPreview: function () {\r\n      var frame = document.getElementById('an-frame'); if (!frame) { return; }\r\n      var html = this.code() || (LT + 'p' + GT + 'Nothing to preview yet.' + LT + '\/p' + GT);\r\n      frame.srcdoc = html;\r\n    },\r\n\r\n    \/* ================= REPORT ================= *\/\r\n    report: function (kind) {\r\n      if (!this.lastResult) { this.toast('Run Analyze first.', 'error'); return; }\r\n      var r = this.lastResult, data;\r\n      if (kind === 'json') { data = JSON.stringify(r, null, 2); this.download(data, 'code-analysis.json', 'application\/json'); }\r\n      else {\r\n        var L = ['# Code Quality Report', '', 'Overall score: ' + r.scores.overall + '\/100', ''];\r\n        for (var i = 0; i < CATS.length; i++) { L.push('- ' + CATS[i] + ': ' + r.scores[CATS[i]] + '\/100'); }\r\n        L.push(''); L.push('Metrics: ' + r.metrics.elements + ' elements, ' + r.metrics.cssRules + ' CSS rules, max nesting ' + (r.metrics.depth || 'n\/a') + '.'); L.push('');\r\n        L.push('## Issues'); L.push('');\r\n        for (i = 0; i < r.issues.length; i++) { var it = r.issues[i]; L.push('### [' + it.cat + '] ' + it.title + ' (' + it.sev + ')'); L.push(it.detail); L.push('Fix: ' + it.fix); L.push(''); }\r\n        this.download(L.join('\\n'), 'code-analysis.md', 'text\/markdown');\r\n      }\r\n      this.toast('Report downloaded.', 'success');\r\n    },\r\n    download: function (data, name, mime) { var blob = new Blob([data], { type: mime }), url = URL.createObjectURL(blob); var a = document.createElement('a'); a.href = url; a.download = name; document.body.appendChild(a); a.click(); document.body.removeChild(a); setTimeout(function () { URL.revokeObjectURL(url); }, 1500); },\r\n\r\n    \/* ================= MISC ================= *\/\r\n    loadSample: function () {\r\n      var ph = 'data:image\/svg+xml,%3Csvg xmlns=%22http:\/\/www.w3.org\/2000\/svg%22 width=%22400%22 height=%22200%22%3E%3Crect width=%22400%22 height=%22200%22 fill=%22%23ddd%22\/%3E%3C\/svg%3E';\r\n      var s = LT + 'html' + GT + '\\n' + LT + 'head' + GT + '\\n  ' + LT + 'title' + GT + LT + '\/title' + GT + '\\n' + LT + '\/head' + GT + '\\n' + LT + 'body' + GT + '\\n  ' + LT + 'h1' + GT + 'Welcome' + LT + '\/h1' + GT + '\\n  ' + LT + 'h4' + GT + 'Subheading that skips levels' + LT + '\/h4' + GT + '\\n  ' + LT + 'img src=\"' + ph + '\"' + GT + '\\n  ' + LT + 'a href=\"https:\/\/example.com\" target=\"_blank\"' + GT + 'External link' + LT + '\/a' + GT + '\\n  ' + LT + 'input type=\"text\"' + GT + '\\n  ' + LT + 'button onclick=\"go()\"' + GT + 'Go' + LT + '\/button' + GT + '\\n' + LT + '\/body' + GT + '\\n' + LT + '\/html' + GT;\r\n      var e = document.getElementById('an-code'); if (e) { e.value = s; }\r\n      this.analyze();\r\n    },\r\n\r\n    clearAll: function () {\r\n      var e = document.getElementById('an-code'); if (e) { e.value = ''; }\r\n      var res = document.getElementById('an-results'); if (res) { res.innerHTML = ''; }\r\n      var fl = document.getElementById('an-fix-list'); if (fl) { fl.innerHTML = ''; var p = document.createElement('p'); p.className = 'pth-an-empty'; p.textContent = 'Run Analyze first, then come here for safe automatic fixes.'; fl.appendChild(p); }\r\n      var fx = document.getElementById('an-fixed'); if (fx) { fx.textContent = ''; }\r\n      this.lastResult = null; this.fixedCode = '';\r\n      this.toast('Cleared.', 'info');\r\n    },\r\n\r\n    toast: function (msg, type) { var t = document.getElementById('pth-an-toast'); if (!t) { return; } var colors = { success: '#059669', error: '#dc2626', info: '#4338ca' }; t.style.background = colors[type] || colors.info; t.textContent = msg; t.className = 'pth-an-toast show'; if (this._tt) { clearTimeout(this._tt); } this._tt = setTimeout(function () { t.className = 'pth-an-toast'; }, 3000); }\r\n  };\r\n\r\n  window.pthAnalyzerApp = App;\r\n\r\n  function runInit() { if (!window.pthAnalyzerApp.initialized && document.getElementById('pth-analyzer-wrap')) { window.pthAnalyzerApp.initialized = true; window.pthAnalyzerApp.init(); } }\r\n  runInit();\r\n  if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', runInit); }\r\n  window.addEventListener('load', function () { setTimeout(runInit, 50); });\r\n  if (!window.pthAnalyzerApp.initialized && typeof MutationObserver !== 'undefined') {\r\n    var obs = new MutationObserver(function () { if (document.getElementById('pth-analyzer-wrap')) { obs.disconnect(); runInit(); } });\r\n    obs.observe(document.body || document.documentElement, { childList: true, subtree: true });\r\n    setTimeout(function () { obs.disconnect(); }, 10000);\r\n  }\r\n})();\r\n<\/script>\n\n\n\n<style>\n.pth-feature-wrap { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 20px; margin-bottom: 30px; }\n.pth-feat-box { border: 1px solid #e2e8f0; background: #ffffff; padding: 24px; border-radius: 12px; box-shadow: 0 4px 6px -1px rgba(0,0,0,0.02); transition: all 0.3s ease; }\n.pth-feat-box:hover { border-color: #bfdbfe; transform: translateY(-3px); box-shadow: 0 10px 15px -3px rgba(0,0,0,0.05); }\n.pth-feat-title { font-size: 1.1rem; font-weight: 800; color: #0f172a; margin-bottom: 10px; display: flex; align-items: center; gap: 10px; }\n.pth-feat-text { font-size: 0.9rem; color: #475569; line-height: 1.6; margin: 0; }\n.pth-steps-wrap { border: 1px solid #e2e8f0; background: #ffffff; padding: 40px 30px; border-radius: 16px; text-align: center; box-shadow: 0 4px 6px -1px rgba(0,0,0,0.02); margin-bottom: 40px; }\n.pth-steps-title { font-size: 1.2rem; font-weight: 800; color: #1e293b; text-transform: uppercase; margin-bottom: 35px; letter-spacing: 1px; }\n.pth-steps-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 30px; }\n.pth-step-item { display: flex; flex-direction: column; align-items: center; }\n.pth-step-num { width: 45px; height: 45px; background: #3b82f6; color: #ffffff; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: 800; font-size: 1.2rem; margin-bottom: 15px; box-shadow: 0 4px 10px rgba(59,130,246,0.3); }\n.pth-step-name { font-weight: 800; color: #0f172a; margin-bottom: 8px; font-size: 1.05rem; }\n.pth-step-desc { font-size: 0.9rem; color: #64748b; line-height: 1.5; margin: 0; }\n<\/style>\n\n<div class=\"pth-feature-wrap\">\n    <div class=\"pth-feat-box\">\n        <div class=\"pth-feat-title\">\u26a1 Local DOM Parsing<\/div>\n        <p class=\"pth-feat-text\">Evaluates raw markup inside your browser memory. We bypass server round-trips entirely. Instant execution guarantees zero network latency during code inspections.<\/p>\n    <\/div>\n    <div class=\"pth-feat-box\">\n        <div class=\"pth-feat-title\">\ud83d\udee1\ufe0f Absolute Privacy<\/div>\n        <p class=\"pth-feat-text\">Your proprietary code snippets remain strictly on your local machine. No background API transmissions. No database logging. Maximum security for enterprise developers.<\/p>\n    <\/div>\n    <div class=\"pth-feat-box\">\n        <div class=\"pth-feat-title\">\ud83d\udd27 Auto-Fix Generation<\/div>\n        <p class=\"pth-feat-text\">Detects missing attributes and non-sequential tags. Applies immediate string mutations via regular expressions. Generates clean, compliant code blocks instantly.<\/p>\n    <\/div>\n<\/div>\n<br>\n<div class=\"pth-steps-wrap\">\n    <div class=\"pth-steps-title\">How to Use the PageSpeed Code Analyzer<\/div>\n    <div class=\"pth-steps-grid\">\n        <div class=\"pth-step-item\">\n            <div class=\"pth-step-num\">1<\/div>\n            <div class=\"pth-step-name\">Paste Markup<\/div>\n            <p class=\"pth-step-desc\">Insert your raw HTML snippet into the primary text editor area.<\/p>\n        <\/div>\n        <div class=\"pth-step-item\">\n            <div class=\"pth-step-num\">2<\/div>\n            <div class=\"pth-step-name\">Execute Scan<\/div>\n            <p class=\"pth-step-desc\">Click the analysis button to trigger local DOM parsing and metric evaluation.<\/p>\n        <\/div>\n        <div class=\"pth-step-item\">\n            <div class=\"pth-step-num\">3<\/div>\n            <div class=\"pth-step-name\">Copy Output<\/div>\n            <p class=\"pth-step-desc\">Review the identified violations and copy the automatically patched code.<\/p>\n        <\/div>\n    <\/div>\n<\/div>\n\n\n\n<h2 id=\"\ud83d\udfe5-the-mechanics-behind-the-page-speed-code-analyzer\" class=\"wp-block-heading\">\ud83d\udfe5 The Mechanics of Local DOM Evaluation<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Client-side execution fundamentally alters how we inspect markup. Traditional validators send raw HTML to remote servers. That introduces severe network latency. It also risks exposing proprietary layouts to third parties. We engineered the PageSpeed Code Analyzer to run entirely within your browser&#8217;s isolated memory sandbox. It compiles your input into an Abstract Syntax Tree (AST) locally. The CPU processes this tree structure in milliseconds. Your data never touches an external API.<\/p>\n\n\n\n<h3 id=\"\ud83d\udfe7-memory-allocation-and-syntax-parsing\" class=\"wp-block-heading\">\ud83d\udfe7 Memory Allocation and Syntax Parsing<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">When you paste a fragment, the utility instantly allocates a temporary DOM fragment in RAM. It does not render the visual elements to the screen. Instead, it reads the node properties sequentially. Missing dimension attributes on media tags immediately flag layout shift warnings. Browsers need explicit widths and heights to paint the initial frame accurately. Without them, the page stutters. The parsing engine traps these missing attributes during the initial memory read phase.<\/p>\n\n\n\n<h3 id=\"\ud83d\udfe8-algorithmic-accessibility-checks\" class=\"wp-block-heading\">\ud83d\udfe8 Algorithmic Accessibility Checks<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Screen readers rely on rigid semantic structures. Missing ARIA labels break navigation for visually impaired users. The offline inspector recursively traverses every input, select, and anchor node. It verifies the presence of discernible text or valid aria-attributes. Heading tags undergo similar algorithmic scrutiny. The system maps out H1 through H6 tags to confirm a strict sequential descent. Skipping levels ruins the document outline. For deeper reading on how browsers handle these node representations natively, study the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Document_Object_Model\" target=\"_blank\" rel=\"noreferrer noopener\">MDN Web Docs on the Document Object Model<\/a>.<\/p>\n\n\n\n<h3 id=\"\ud83d\udfe9-regular-expression-auto-correction\" class=\"wp-block-heading\">\ud83d\udfe9 Regular Expression Auto-Correction<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Detection is only half the battle. The repair phase requires precision string manipulation. The application applies highly optimized regular expressions to inject missing parameters directly into your raw text. It mutates the string without breaking custom styling or existing JavaScript bindings. It injects empty alt attributes for decorative images and missing aria-labels for orphaned form controls. You receive production-ready markup in real-time. Finding performant client-side scripts is rare. To discover more utilities that operate exclusively without remote servers, explore our <a href=\"https:\/\/schoolict.net\/tools\/free-web-tools-directory-prime-tool-hub\/\">Free Web Tools Directory<\/a>.<\/p>\n\n\n\n<h3 id=\"\ud83d\udfea-performance-and-cpu-execution-context\" class=\"wp-block-heading\">\ud83d\udfea Performance and CPU Execution Context<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">JavaScript runs on a single thread. Heavy DOM manipulation can freeze the user interface. We wrote the traversal loops to yield execution back to the main thread when processing massive code blocks. This prevents browser lockups. The entire validation pipeline completes within the standard 16-millisecond frame budget. You experience zero lag, even on older mobile processors.<\/p>\n\n\n\n<style>\n.pth-faq-card { background: transparent; display: flex; flex-direction: column; width: 100%; margin-top: 40px; margin-bottom: 40px; font-family: inherit; }\n.pth-faq-title { font-size: 1.6rem; font-weight: 800; color: #0f172a; margin: 0 0 25px 0; border-bottom: 2px solid #e2e8f0; padding-bottom: 10px; text-align: center; }\n\/* Changed 1fr to 100% to prevent Woody Snippet linter errors *\/\n.pth-faq-grid { display: grid; grid-template-columns: 100%; gap: 20px; }\n@media (min-width: 768px) { .pth-faq-grid { grid-template-columns: repeat(2, 1fr); } }\n.pth-faq-item { background: #f8fafc; padding: 20px; border-radius: 12px; border: 1px solid #e2e8f0; break-inside: avoid; }\n.pth-faq-q { margin: 0 0 8px 0; font-size: 1.05rem; color: #1e293b; font-weight: 700; }\n.pth-faq-a { margin: 0; font-size: 0.95rem; color: #475569; line-height: 1.6; }\n<\/style>\n\n<div class=\"pth-faq-card\">\n    <h2 class=\"pth-faq-title\">\ud83e\udd14 Frequently Asked Questions<\/h2>\n    <div class=\"pth-faq-grid\">\n        <div class=\"pth-faq-item\">\n            <h3 class=\"pth-faq-q\">Does the PageSpeed Code Analyzer store my code on a server?<\/h3>\n            <p class=\"pth-faq-a\">No. The processing engine operates entirely via local JavaScript. Your snippets never leave your browser window, ensuring absolute data privacy.<\/p>\n        <\/div>\n        <div class=\"pth-faq-item\">\n            <h3 class=\"pth-faq-q\">How does it detect Layout Shifts (CLS)?<\/h3>\n            <p class=\"pth-faq-a\">The parser scans all media elements like images. It checks for explicit width and height attributes. Browsers need these to allocate space before rendering.<\/p>\n        <\/div>\n        <div class=\"pth-faq-item\">\n            <h3 class=\"pth-faq-q\">Can it fix accessibility issues automatically?<\/h3>\n            <p class=\"pth-faq-a\">Yes. The engine identifies missing labels on form inputs and empty anchor tags. It injects default aria-labels to maintain WCAG compliance.<\/p>\n        <\/div>\n        <div class=\"pth-faq-item\">\n            <h3 class=\"pth-faq-q\">Why does it flag heading hierarchies?<\/h3>\n            <p class=\"pth-faq-a\">Search engines index pages based on sequential document structures. Skipping from an H2 directly to an H4 confuses crawlers and screen readers.<\/p>\n        <\/div>\n        <div class=\"pth-faq-item\">\n            <h3 class=\"pth-faq-q\">Is the tool compatible with mobile browsers?<\/h3>\n            <p class=\"pth-faq-a\">Absolutely. The interface uses responsive CSS grids. The local JavaScript engine runs perfectly on modern mobile CPU architectures.<\/p>\n        <\/div>\n        <div class=\"pth-faq-item\">\n            <h3 class=\"pth-faq-q\">Does it format inline CSS styles?<\/h3>\n            <p class=\"pth-faq-a\">It detects excessive inline styling. Heavy inline styles increase HTML byte size. The tool suggests extracting them to scoped stylesheet blocks.<\/p>\n        <\/div>\n        <div class=\"pth-faq-item\">\n            <h3 class=\"pth-faq-q\">What technology powers the auto-fix engine?<\/h3>\n            <p class=\"pth-faq-a\">We use advanced pattern matching with Regular Expressions combined with native DOM traversal methods entirely within the client-side environment.<\/p>\n        <\/div>\n    <\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Offline PageSpeed Code Analyzer &#038; Accessibility Validator Instantly analyze your HTML code for PageSpeed, Core Web Vitals (CLS), and Accessibility errors 100% offline. Get live fixes and proposals. \u26a1 Local DOM Parsing Evaluates raw markup inside your browser memory. We bypass server round-trips entirely. Instant execution guarantees zero network latency during code inspections. \ud83d\udee1\ufe0f Absolute &#8230; <a title=\"Offline PageSpeed Code Analyzer &amp; Accessibility Validator\" class=\"read-more\" href=\"https:\/\/schoolict.net\/tools\/free-offline-pagespeed-code-analyzer\/\" aria-label=\"Read more about Offline PageSpeed Code Analyzer &amp; Accessibility Validator\">Read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":5105,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-5097","page","type-page","status-publish","has-post-thumbnail"],"_links":{"self":[{"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/pages\/5097","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/comments?post=5097"}],"version-history":[{"count":1,"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/pages\/5097\/revisions"}],"predecessor-version":[{"id":6925,"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/pages\/5097\/revisions\/6925"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/media\/5105"}],"wp:attachment":[{"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/media?parent=5097"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}