{"id":270,"date":"2026-10-14T04:54:35","date_gmt":"2026-10-14T04:54:35","guid":{"rendered":"https:\/\/ongroundmedia.com\/?page_id=270"},"modified":"2026-10-14T04:57:34","modified_gmt":"2026-10-14T04:57:34","slug":"technical-seo-tools","status":"publish","type":"page","link":"https:\/\/ongroundmedia.com\/?page_id=270","title":{"rendered":"Technical SEO Tools"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"270\" class=\"elementor elementor-270\">\n\t\t\t\t<div class=\"elementor-element elementor-element-cf05a38 e-con e-atomic-element e-flexbox-base e-default-div e-a068944 \" data-id=\"cf05a38\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"cf05a38\">\n    \t\t<div class=\"elementor-element elementor-element-06f638b elementor-widget elementor-widget-html\" data-id=\"06f638b\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!doctype html><html><head><meta charset=utf8><meta name=viewport content=\"width=device-width,initial-scale=1,viewport-fit=cover\"><style>:root{color-scheme:light;box-sizing:border-box;padding-top:env(safe-area-inset-top,0px);padding-bottom:env(safe-area-inset-bottom,0px)}html{scroll-padding-top:env(safe-area-inset-top,0px)}body{margin:0;padding:0;font:14px -apple-system,BlinkMacSystemFont,sans-serif;background:#fff;color:#000}img{max-width:100%}[hidden]:not([hidden=until-found i]){display:none!important}<\/style><\/head><body>\r\n<title>Onground Media SEO Tools<\/title>\r\n<link rel=\"preconnect\" href=\"https:\/\/fonts.googleapis.com\">\r\n<link rel=\"preconnect\" href=\"https:\/\/fonts.gstatic.com\" crossorigin>\r\n<link href=\"https:\/\/fonts.googleapis.com\/css2?family=Bricolage+Grotesque:opsz,wght@12..96,500;12..96,700;12..96,800&family=Hanken+Grotesk:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500&display=swap\" rel=\"stylesheet\">\r\n<style>\r\n\/* Layout concept: navy brand band with a search-first hero, then a categorised tool directory. Opening a tool swaps the directory for a full-width workbench with inputs left and results right. *\/\r\n:root{\r\n  --indigo:#4F46E5; --navy:#0F172A; --orange:#FF6B35; --emerald:#10B981;\r\n  --bg:#F8FAFC; --surface:#FFFFFF; --field:#FFFFFF; --text:#1E293B; --muted:#64748B; --border:#E2E8F0;\r\n  --link:#4F46E5; --primary:#4F46E5; --primary-hover:#4338CA; --primary-soft:#EEF2FF; --on-primary:#FFFFFF;\r\n  --cta:#FF6B35; --cta-hover:#F25A22; --on-cta:#0F172A;\r\n  --good-ink:#047857; --good-soft:#ECFDF5; --warn-ink:#B45309; --warn-soft:#FFFBEB; --bad-ink:#B91C1C; --bad-soft:#FEF2F2;\r\n  --good:#10B981; --warn:#F59E0B; --bad:#DC2626;\r\n  --hero-bg:#0F172A; --hero-ink:#F8FAFC; --hero-sub:#CBD5E1; --hero-line:rgba(248,250,252,.22);\r\n  --logo-bg:transparent; --code-bg:#F1F5F9;\r\n  --shadow:0 1px 2px rgba(15,23,42,.05),0 10px 28px -14px rgba(15,23,42,.16);\r\n  --font-display:'Bricolage Grotesque','Trebuchet MS',system-ui,sans-serif;\r\n  --font-body:'Hanken Grotesk',system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;\r\n  --font-mono:'IBM Plex Mono',ui-monospace,Menlo,Consolas,monospace;\r\n}\r\n@media (prefers-color-scheme: dark){\r\n  :root:not([data-theme=\"light\"]){\r\n    --bg:#0B1120; --surface:#111A2E; --field:#0B1325; --text:#E2E8F0; --muted:#94A3B8; --border:#243350;\r\n    --link:#A5B4FC; --primary-hover:#5B54EE; --primary-soft:#1A2347;\r\n    --good-ink:#34D399; --good-soft:#0C2A22; --warn-ink:#FBBF24; --warn-soft:#2A2110; --bad-ink:#FCA5A5; --bad-soft:#2C1518;\r\n    --logo-bg:#FFFFFF; --code-bg:#0B1325; --shadow:0 1px 0 rgba(255,255,255,.03); color-scheme:dark;\r\n  }\r\n}\r\n:root[data-theme=\"dark\"]{\r\n  --bg:#0B1120; --surface:#111A2E; --field:#0B1325; --text:#E2E8F0; --muted:#94A3B8; --border:#243350;\r\n  --link:#A5B4FC; --primary-hover:#5B54EE; --primary-soft:#1A2347;\r\n  --good-ink:#34D399; --good-soft:#0C2A22; --warn-ink:#FBBF24; --warn-soft:#2A2110; --bad-ink:#FCA5A5; --bad-soft:#2C1518;\r\n  --logo-bg:#FFFFFF; --code-bg:#0B1325; --shadow:0 1px 0 rgba(255,255,255,.03); color-scheme:dark;\r\n}\r\n*{box-sizing:border-box}\r\n@media (prefers-reduced-motion:no-preference){html{scroll-behavior:smooth}}\r\nhtml{scroll-padding-top:5.5rem}\r\nbody{background:var(--bg);color:var(--text);font:400 15px\/1.55 var(--font-body);-webkit-font-smoothing:antialiased}\r\nh1,h2,h3,h4{font-family:var(--font-display);margin:0;text-wrap:balance;letter-spacing:-.015em}\r\np{margin:0}\r\na{color:var(--link)}\r\nbutton,input,select,textarea{font:inherit;color:inherit}\r\n:focus-visible{outline:2px solid var(--link);outline-offset:2px}\r\n.wrap{max-width:1120px;margin-inline:auto;padding-inline:20px}\r\n.mono{font-family:var(--font-mono)}\r\n\r\n.top{position:sticky;top:env(safe-area-inset-top,0px);z-index:20;background:var(--surface);border-bottom:1px solid var(--border)}\r\n.top-in{display:flex;align-items:center;justify-content:space-between;gap:16px;min-height:64px}\r\n.brand{display:flex;align-items:center;gap:12px;text-decoration:none;color:var(--text);min-width:0}\r\n.logo-chip{background:var(--logo-bg);border-radius:10px;padding:3px 6px;display:grid;place-items:center;flex:none}\r\n.logo-chip img{display:block;height:34px;width:auto}\r\n.brand-name{font:700 18px\/1 var(--font-display);white-space:nowrap}\r\n.brand-name span{color:var(--muted);font-weight:500}\r\n.top-nav{display:flex;align-items:center;gap:8px}\r\n.nav-link{color:var(--text);text-decoration:none;font-weight:600;font-size:14px;padding:8px 10px;border-radius:8px}\r\n.nav-link:hover{background:var(--primary-soft)}\r\n.icon-btn{width:38px;height:38px;border-radius:8px;border:1px solid var(--border);background:transparent;display:grid;place-items:center;cursor:pointer}\r\n.icon-btn:hover{background:var(--primary-soft)}\r\n.icon-btn svg{width:18px;height:18px}\r\n\r\n.hero{background:var(--hero-bg);color:var(--hero-ink)}\r\n.hero-in{padding-block:48px 40px;display:grid;gap:22px}\r\n.eyebrow{font:500 12px\/1 var(--font-mono);letter-spacing:.12em;text-transform:uppercase;color:var(--hero-sub)}\r\n.hero h1{font-weight:800;font-size:clamp(2rem,5vw,3.25rem);line-height:1.05;letter-spacing:-.025em;max-width:18ch}\r\n.hero h1 em{font-style:normal;color:var(--orange)}\r\n.hero .lede{max-width:60ch;color:var(--hero-sub);font-size:17px}\r\n.search{display:flex;align-items:center;gap:10px;background:#fff;color:#1E293B;border-radius:12px;padding:6px 8px 6px 16px;max-width:620px;border:2px solid transparent}\r\n.search:focus-within{border-color:var(--orange)}\r\n.search svg{width:20px;height:20px;flex:none;stroke:#64748B;fill:none;stroke-width:2;stroke-linecap:round}\r\n.search input[type=text]{flex:1;min-width:0;border:0;border-radius:0;outline:0;background:transparent;color:#1E293B;font-size:16px;padding:10px 0}.search input[type=text]:focus-visible{outline:0}\r\n.search input::placeholder{color:#64748B}\r\n.search kbd{font:500 12px var(--font-mono);color:#64748B;border:1px solid #E2E8F0;border-radius:6px;padding:3px 7px}\r\n.hero-key{display:flex;flex-wrap:wrap;gap:14px 22px;font-size:13.5px;color:var(--hero-sub)}\r\n.hero-key span{display:inline-flex;align-items:center;gap:8px}\r\n\r\n.tag{display:inline-flex;align-items:center;gap:6px;font:500 11px\/1.2 var(--font-mono);letter-spacing:.06em;text-transform:uppercase;border-radius:999px;padding:4px 9px;white-space:nowrap}\r\n.tag i{width:7px;height:7px;border-radius:50%;background:currentColor}\r\n.tag.here{background:var(--good-soft);color:var(--good-ink)}\r\n.tag.paste{background:var(--primary-soft);color:var(--link)}\r\n.tag.link{background:var(--warn-soft);color:var(--warn-ink)}\r\n.hero .tag{background:rgba(248,250,252,.1);color:var(--hero-ink)}\r\n\r\n.dir{padding-block:28px 64px}\r\n.cats{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:22px}\r\n.cats button{border:1px solid var(--border);background:var(--surface);border-radius:999px;padding:8px 14px;font-weight:600;font-size:13.5px;cursor:pointer}\r\n.cats button:hover{background:var(--primary-soft)}\r\n.cats button[aria-pressed=\"true\"]{background:var(--primary);border-color:var(--primary);color:var(--on-primary)}\r\n.cats small{opacity:.75;font-weight:500;margin-left:4px}\r\n.group{margin-bottom:34px}\r\n.group h2{font-size:22px;font-weight:800;display:flex;align-items:baseline;gap:12px;flex-wrap:wrap}\r\n.group h2 span{font:400 14px var(--font-body);color:var(--muted)}\r\n.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(100%,260px),1fr));gap:14px;margin-top:14px}\r\n.card{display:flex;flex-direction:column;gap:10px;text-align:left;padding:16px;border:1px solid var(--border);border-radius:12px;background:var(--surface);cursor:pointer;min-width:0;box-shadow:var(--shadow)}\r\n.card:hover{border-color:var(--link);transform:translateY(-1px)}\r\n@media (prefers-reduced-motion:no-preference){.card{transition:transform .12s,border-color .12s}}\r\n.card h3{font-size:16.5px;font-weight:700}\r\n.card p{color:var(--muted);font-size:13.5px;flex:1}\r\n.card .tag{align-self:flex-start}\r\n.empty{padding:40px 16px;text-align:center;color:var(--muted);border:1px dashed var(--border);border-radius:12px}\r\n\r\n.bench{padding-block:24px 64px}\r\n.crumbs{display:flex;flex-wrap:wrap;align-items:center;gap:12px;margin-bottom:18px}\r\n.back{border:1px solid var(--border);background:var(--surface);border-radius:8px;padding:8px 14px;font-weight:600;font-size:13.5px;cursor:pointer}\r\n.back:hover{background:var(--primary-soft)}\r\n.panel{background:var(--surface);border:1px solid var(--border);border-radius:14px;padding:26px;box-shadow:var(--shadow)}\r\n.panel-head{margin-bottom:20px;display:grid;gap:8px}\r\n.panel-head h2{font-size:clamp(1.4rem,3vw,1.9rem);font-weight:800}\r\n.panel-head p{color:var(--muted);max-width:68ch}\r\n.mode-note{font-size:13.5px;padding:10px 12px;border-radius:8px;background:var(--code-bg);color:var(--muted);margin-bottom:20px}\r\n.split{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,320px),1fr));gap:26px}\r\n.split>*{min-width:0}\r\n.col{display:grid;grid-template-columns:minmax(0,1fr);gap:14px;align-content:start}\r\n.field{display:grid;gap:6px;min-width:0}\r\n.field label,.lbl{font-size:13px;font-weight:600}\r\n.hint{font-size:12.5px;color:var(--muted)}\r\n.opt{font-weight:400;color:var(--muted)}\r\ninput[type=text],input[type=url],input[type=number],input[type=date],select,textarea{width:100%;padding:10px 12px;border:1px solid var(--border);border-radius:8px;background:var(--field);color:var(--text);min-width:0}\r\ntextarea{resize:vertical;line-height:1.5}\r\ntextarea.code,.code{font-family:var(--font-mono);font-size:12.5px;line-height:1.55}\r\ninput:focus-visible,select:focus-visible,textarea:focus-visible{outline:2px solid var(--link);outline-offset:0;border-color:var(--link)}\r\ninput[type=checkbox],input[type=radio]{accent-color:var(--primary)}\r\n.row2{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}\r\n.check{display:flex;gap:8px;align-items:flex-start;font-size:13.5px}\r\n.check input{margin-top:3px}\r\n.checks{display:grid;gap:8px}\r\n.btn{font-weight:600;font-size:14px;line-height:1;padding:12px 18px;border-radius:8px;border:1px solid transparent;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;gap:8px;text-decoration:none;white-space:nowrap}\r\n.btn.primary{background:var(--primary);color:var(--on-primary)}\r\n.btn.primary:hover{background:var(--primary-hover)}\r\n.btn.cta{background:var(--cta);color:var(--on-cta)}\r\n.btn.cta:hover{background:var(--cta-hover)}\r\n.btn.ghost{background:transparent;border-color:var(--border);color:var(--text)}\r\n.btn.ghost:hover{background:var(--primary-soft)}\r\n.btn.sm{padding:9px 13px;font-size:13px}\r\n.btn-row{display:flex;flex-wrap:wrap;gap:8px}\r\n.out-box{border:1px solid var(--border);border-radius:10px;background:var(--bg);padding:14px;display:grid;gap:12px}\r\n.pill{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:999px;font-size:12.5px;font-weight:600;line-height:1.3;justify-self:start}\r\n.pill.good{background:var(--good-soft);color:var(--good-ink)}\r\n.pill.warn{background:var(--warn-soft);color:var(--warn-ink)}\r\n.pill.bad{background:var(--bad-soft);color:var(--bad-ink)}\r\n.pill.idle{background:var(--primary-soft);color:var(--link)}\r\n.issues{list-style:none;margin:0;padding:0;display:grid;gap:8px}\r\n.issues li{display:grid;grid-template-columns:auto minmax(0,1fr);gap:10px;align-items:start;padding:10px 12px;border:1px solid var(--border);border-radius:8px;background:var(--surface);font-size:13.5px;overflow-wrap:anywhere}\r\n.issues .pill{font-size:11px;padding:3px 8px;text-transform:uppercase;letter-spacing:.05em}\r\n.issues b{font-weight:600}\r\n.issues small{display:block;color:var(--muted);margin-top:2px;font-size:12.5px}\r\n.stats{display:grid;grid-template-columns:repeat(auto-fill,minmax(130px,1fr));gap:10px}\r\n.stat{border:1px solid var(--border);border-radius:10px;padding:12px;background:var(--surface);min-width:0}\r\n.stat.key{background:var(--primary-soft);border-color:transparent}\r\n.stat span{display:block;font:500 11px\/1.2 var(--font-mono);letter-spacing:.08em;text-transform:uppercase;color:var(--muted)}\r\n.stat strong{display:block;margin-top:6px;font:700 20px\/1.15 var(--font-display);font-variant-numeric:tabular-nums;overflow-wrap:anywhere}\r\n.stat.key span{color:var(--link)}\r\n.tbl{overflow-x:auto;border:1px solid var(--border);border-radius:8px}\r\ntable{border-collapse:collapse;width:100%;font-size:13.5px}\r\nth,td{text-align:left;padding:8px 12px;border-bottom:1px solid var(--border);vertical-align:top}\r\nth{font:500 11px var(--font-mono);letter-spacing:.08em;text-transform:uppercase;color:var(--muted);background:var(--bg)}\r\ntr:last-child td{border-bottom:0}\r\ntd.mono{font-size:12.5px;overflow-wrap:anywhere}\r\n.bar{height:8px;border-radius:999px;background:var(--border);overflow:hidden}\r\n.bar i{display:block;height:100%;border-radius:999px;background:var(--good);width:0}\r\n.bar i.warn{background:var(--warn)}.bar i.bad{background:var(--bad)}\r\n.links{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,220px),1fr));gap:10px}\r\n.links a{display:flex;flex-direction:column;gap:2px;padding:12px 14px;border:1px solid var(--border);border-radius:10px;background:var(--surface);text-decoration:none;color:var(--text);font-weight:600;font-size:14px}\r\n.links a:hover{border-color:var(--link);background:var(--primary-soft)}\r\n.links a small{font-weight:400;color:var(--muted);font-size:12.5px}\r\n.links a::after{content:\"Opens in a new tab\";font-size:11px;font-weight:500;color:var(--link);margin-top:4px}\r\n.note{font-size:12.5px;color:var(--muted);border-top:1px dashed var(--border);padding-top:12px;margin-top:20px}\r\n.seg{display:inline-flex;border:1px solid var(--border);border-radius:8px;overflow:hidden;flex-wrap:wrap}\r\n.seg button{border:0;background:transparent;padding:8px 14px;font-size:13px;font-weight:600;cursor:pointer}\r\n.seg button[aria-pressed=\"true\"]{background:var(--primary);color:var(--on-primary)}\r\n.rows{display:grid;gap:8px}\r\n.rowline{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,2fr) auto;gap:8px;align-items:center}\r\n@media (max-width:520px){.panel{padding:18px 16px}.brand-name span{display:none}.rowline{grid-template-columns:minmax(0,1fr) auto}.rowline :nth-child(2){grid-column:1 \/ -1;order:3}}\r\nfooter{border-top:1px solid var(--border);background:var(--surface)}\r\n.foot{display:flex;flex-wrap:wrap;gap:12px;align-items:center;justify-content:space-between;padding-block:24px;color:var(--muted);font-size:13.5px}\r\n<\/style>\r\n<script>\r\n(function(){try{var t=localStorage.getItem('og-theme');if(t==='dark'||t==='light')document.documentElement.setAttribute('data-theme',t)}catch(e){}})();\r\n<\/script>\r\n\r\n<header class=\"top\">\r\n  <div class=\"wrap top-in\">\r\n    <a class=\"brand\" href=\"#\" id=\"homeLink\" aria-label=\"Onground Media SEO tools home\">\r\n      <span class=\"logo-chip\"><img decoding=\"async\" src=\"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPAAAACuCAYAAAAI0YAVAACJrUlEQVR42u19eZxcRbX\/91TVvb3MPtkTsrET9n1TEQVFUUGUKCJuCIjLDxX3DdDney4oIuoTcHkqi4A8F3goKCCoCLJDEggkJCH7JJl9erm3qs7vj6rbfafTsyUTCDjlZ2Qy3X379u37rXPO95zzPYSJNYpFAAgPPWSChsc+k23jp\/Oso5zUA6EKRSYDm7eWc6Y4oLSORVQuCQDQFpxrbIwyzW0RW2gBWyhGthxkKA6C6cWGtqYB7P7JMh1wUARrAdiJSz2xhl3MTH+9+CMNr8aUAl1yiaWJS1IfsGsf+nEei\/8yWZS6Z8T9vfNzWT2HdTw3LvdPEdZMC6SYpMg2suastHGgTCStiUgSg2EJIEAI1ggYKssslJUqG1trI2PNgGG7mYk22DDTDanWgbLPIZ9d3qPD9Z2z99t01Mk\/7AVPAHpiAZAKG77z6Wnx8ocXDPT2nFiQDRut2v1Hh111VTwBYAAQEpsW39xUeOT2KZneFQc2Dqw9lks98w3MdKVohkA0hW25Mas0mGMABGIBwwAMgdhAWe0sKBEA9kabAKFgGYBQYAgAgAXDMoMEQ0oBbSwsi6KUclNsxcYygg2RbF5XDtsfsm3zH1RT91k9+\/UX9xCRmfiy\/k0s7aJF4Za\/XTel54l7DmmM+l4bFO3+Shf2Llo7s9S2yzfnXv33zyW+4b8jYrHxxi83hh1\/3T0udB+el+V9TaGwt8qK3dnE0zO23KBsDCAGhAXYACYGOOVRWwFAAlYCwoKlBjGj8iSPYwiASYIBMANEDGIBQIGZIIgBa937kAWYAASwlEWRs5ER+c0GmZVWiEU2J5dSw\/RHizz78dkf\/Ek3kbDVk5pYL+kQjS2t+Pj7WtD91N5B1H+MLA8cHIryAYqKu2YMN2ZYAihho8qVirMP+cD87\/7l+n8vAAuJRd89fs6sBt416uncP8vFk6nQeaQyheZclgR0BAjAGA0SgQejAcF6JHIqRCWPTOl+J8BIBiEGgQdfVIFBfyGwP5yqxNYMAMQO4WxB7N9DKAAhQBIQQAzmgg1K5aD96UzztDti1fKPbqbndt\/v88\/RMccUJ4DwUsKswPL\/+nQLr7t\/dkOpf67t7z8ijIuvo0Lf\/lnmfBaawBHIGwbSAggYWxrbO56beeBJR3z3zkf\/DQAs0fnnr7eEz95yTF\/nhtdLWzyyQZp5FPdPy4VMNo4BSFgAmi2EZAgCmIW7aGxBwnqnFyCQd4IrphUAYEjAkICAhgCDYJ0BpsQQV\/9\/kBUHYIlgiCruNTFDMPsNBCASYAgQE4jJueVEiKAQi2x3ROG6iBqfiML2P9PkV94x+73fWQM74WnvjOtGZnnIxedPyqxduh\/6N786xwP7Z\/XAXlTqn56huE0Y68IyKPcjBEwQQRoNFQlAKXTlJj3Se+zb3jTvk99b\/7IFMDNT\/y\/O2L+8ZdGpKuo+NGRzdIb0FIEYVhcBtjCxhpQEQYAxDBIAE\/sYljxEuRLPcvIQBgPQxbQEhoKDoYVgBgtCFa9VANe62ZYA698JYAgwBPs3TL0NQwAcuNcKC4IByFvvoAG9urEYy9bHOdN+r2yafE\/7ESfeS\/t9tH8CNi\/ueua2yzOF\/71m7pS4\/5Vhb\/9BQVTeV5DYN0Q8NYMyYMqAroZn7O2ClRJWShihoTQj0BmActgcTrth8uV\/fB\/Nn1962QF4099\/0oRn\/u\/wqGP5+8Pypjc3oKclRxpsACYCJCHSMcJsCC6VIYS3jcwgSWD2YHWorF4dqoPatDn1uyYxALIg8nAk51B76ELA+v3ApkDsLHDiXgvmOmEtASxgWcBYAxBDCEBKdhsLA1ABGBmUkUNRUzmWzY\/w5N1\/JKfsf9eUUy9dNxErv3Cu8YoL3zbdUmEP3bH2iEyx923ZQvf+k4XJCl1WRBYwpnrrGOetQSbkJzsAK0ADEIJAEaCQRRyH6Jq026en3fDIpXXuyJcuIfXUFe+bOQ3LD4n715wZmJ7XZbncHoBhIg0hnJVlG\/sb3jhL6jM9DAIEga2FBUNKb2mTeDexyJYcaeUvGRHAlTSPs8EOuDwI+OxdYOcek4ur01gi8p64e617b\/axNYFZwpCEgIHg2P\/N+edsLYjI+QtCwHiXWxNBZBpQ0NkSgvb7I5m\/viuc\/\/c9PnTjM0SkJ1A2vtzKA+d\/aFJr6cmpreUNczNWH8m68FqOS3sjiqY0gB0\/GceAYmhlQESwmkEsIYVyXpn7CgEyMMZAemfLuWgESIVuDot6\/n6vmPrD+x95WQB4\/S8\/NDXT+eQZorjpFGV6D82jt5nIIi7HkCRBlmDYMbxEXP3xwam17qoJIcBsnQUWaauavkRJzEspijl5CsPBp\/pvMIGFt46UOkZNsQbX\/QY49T4CBgSCgWDjQwSC8OSZdyG8a+3AbYhgmaFEACFD9Oscx9S4WMv2P4oZB\/zP5LP+Z8mERd4+0PL\/Xtm04e7rD+1bv+KErNEHZEw8P2PKc1sUN8GWARODdQxrAFISDAmWAFMMYsBYBkFAgJJoCZIIRNZvzFXuFJYQI8SmprYnu6e1H7Pfj5b0v4QBTFh17QW7xhsePrE1Wndms+k8IrD9GbCGBcGyBIEAy5BsAFgYMcjggTyguEIU0ShANcJZ0XCPbf3gtrzHUOdb97mejANbSCkBJsQ6C52d8nQxO\/3Gcsvev575vp8\/NVEsMrq18caLGrc8tGw\/vX7JgtbAHpiNCoeLYteesjwwqZkshLEASbDnL4gM2MYVnsOQcmUB1gDgCmiTr1IM8b5WA5IUtGjAqub2X3a\/5sQPHnbeVfFLEsD8zOXNXXdc\/+a4Z9PHA91zcJvsl4hLgHJepwbBUAAwQ1kDyW4n4xHBt30AJqqxyDsQwKMBb9UhFwApWGtdsYkUgAUiziAO25ZF2Wk\/6w\/n\/3LO+b9ZO2GRh1\/3fPSMIyZvfPqymfGGY0j0QtsiGsqMDAsQWZfyIwWjLRgWQriMRMKpVLiQ1IaZJkaHBLABpBAoyRZsmDz3i\/N\/8cR\/po\/xkgAwMwerLz\/hoFZ0flT1rXx7zvbnYZx1hXB3qyaCJgEmAckWEsZ5w+TpIeZxB\/Dg1+14AI95GQsSyrvUxhFfsM6LVwEGdBYDwYy\/mOZdL5tx0mfvovnHlyagOuQ9SJs+9tqT9POLvxGargPygUFoLMgwIAARCNiYYI3jJBgGUiDlCiswESzruvfKUAAmdi57Tzi5YBYc\/cFJ\/\/mH619SAF5+5VlzMlse+VSrLL05KG6ZF+oe96lECgUkYXzqh8iCuJqYcUQSbeWGbi+AtwblzgVgShJThkGCwJKg2YKso+IgXPaCcq3oNc0dNmj6v8ycIy5tXvjzJRNwHXqtvPA1C9TatZ\/L9G5+V8ZskdkMoKQvomPnCVpLLp9PHphWApwFkwWLcor8HAWAyVXybWmY+Xxp\/hELZ3\/j5gcGf8876Vrx24tac6vvfFOusP7jMioemhd9ACLAAiY2ICkhZAA2jpYXxHDEu60USoGpLlBqQbwjATyUy\/uCWGARwFoDshok3Ldt\/XsbIhgtEKgAgQxgI4OBoP3RaNp+PyjPOeEPs47\/1OYJuA5hjW+8smXlrdde2Fxce16b2TKV7AAQxYAEDAGxBUhIBBYQhjyAA0BqsIhgxwJgAUCF6GiY\/Ze+g048c\/dP\/7hjpwYwCYlllx+\/f3P\/859vKG84LccDGQgFTUDZaLerCeVZGoICQ1gDgndNPHiNIFiSIGZIa8dECo0ErvqgHD2AHds99ktPNHwoUHs2ZQ4ghEBAGmRdsQATYEnAClePTayh2Lo4TgXYglYd5eff0hvOvnivD\/7mSSKaCI6HcKl7v\/yeN\/SvfPzr+f6NBzXHnRAUw0og8jdhxgBkk4yEADtKeWwW2AK2uRnrgin\/Mfva575ce5+pnemirF37UL503Uc\/ML33qQ\/ly5v3BZd944CBkRmwzEBQEYSyKy8kAlvjXGdf3ehcZr8TEiFg7zKOwTrWstQjWfA0mzhqII7yvWrPcbQgZiIIATAsDDMkE0RS9AH3u4UGhIUVXMmVt1KXMkXz1qC4Za\/VV77mUmb+1UTuuO73wgDd9sRF5zwXrXjgYt5ceHsbGQlYX5PhCUfBYLKIlQFbQJmtWVWXyx\/6\/uqOZbkY5p6uZyR2Ggvcd\/tFU8tP\/t8l+f4V78mZ7jzIVEuOQTDeYklfl2yrhSuVkLjyiTyADQkIRjUeHiWAhwMqholVtsWFpjHE50O5\/vXP01X0VDobE98ZAkyuWsWSdpVjwlkEwQBrAikAGYUe0dLbI5uvzsw5\/tvTT\/npxgnY1l9dV310Pv7ym2taipuOYQIsWxAkJAsAMaxgFAPXcBZGVKn+S3+HVPMdJv8UUmCdaFnTMefgtx38g7v+NVrL\/UL6zFj6g1MO6n7oNz\/O9Cw\/L2c9eJM41tPvktm5er7WQlpy+V5I162TINcnv6UFQmOh+OXpAY4mnSTY1VZX6q8JrtupUspJW+3nRC6HDhOjhTc3t+t1nxDP33tlx7Vn7OECsomVXhsveu\/0zr\/88Vxb7N+PGIBhV0TEwhtMVwAUaoI0VLnkRDTsd0jkmmeYBWzQsLEzM73uBvrifiNCYvN\/n3RaW\/dTN02xm96alRFFlmFJVPJmDEAIi6rTKVxVC0tX+Q0CQ8JSCEvKtfgl6jQ2tZW9BECYfKkjgXN0ueCkyyn9XN93zDEIMQQsyLVPOC8HDCsYkAK2DEATGiIWk3TPKbzhb79fffWxJ+8Me\/5O8uWh\/2NvOVg9cd8vZsYDn2uHbYYxLuY1FkwMFq7hRBiFIA4R6sCX09b\/Tgf\/uDDIihCcaVy+4KzP1CUVX7QYuPOhG1sG7vjGhxu7nvyI0r2zrC1BCAshAQaDyTXMMzEEVeFsXfEZQNU8L5N1oAbBQoKYIMjXHO+kHMy2gJRobBGPsKLCCTjyiiuWmcimLK5wqTYCDLkqIRGGMDoGsYUtdaEtUPv09NIPNv\/qdbtOOvgDV9J+C6N\/YwJLrH7PMe8rrn30U7ny5n1ElhEhghQEwcrtk2QA4cWViEAxJa3jg+KtkcK0omHWLBbPPvDAwk4DYH7gF5NW3\/PjrzWU1p2dweYQUkKQBhsDEgKwDCstrHDSNcwSIANKsr2+JrjaJMRgD24m4fKeLCHIOjebyQeBqdjQHyDZEYnrE8nJczl5bs2xBtMJw1dkDV62+rraXZmqR6\/Eyg5ugzyKkb588ryngYARwrVLgl2RiwVkuvmCFRiAFNZVtVkDwRIkDQRbkAHaaMu87s5FX3\/+vh\/GzHz1v6PEDzNnV53\/qo9leld+rhW97bEqImJXiCVZgMpeVcUmBI2\/uZRx8ksQqejF3QOW4CrprWt7oUprIUGTKk\/eZdd7hsoGvOAkVuEXp8+JNyz6Qqg3nxeYXkhThkE1kE82J+vzuOTjLkItwngr1rWKuKQ9z1ZUM0gwmAyY3K6YEAVJTC1SwhuVCyx8SyARrPNpQHAaWKBqvpnYnzC7NiaCa2RwIb4AmMHMYOJK2eWg78NKpDt\/E9bJVMJ6V\/ReGw4QD\/8FMqzfANKutH9vrhdNuQIYruwpVNmWQAxLBJ3Jo9NMKYrmPS6euusxV9Hxl3T\/m\/jMWPXND88US\/92Qa5v7f9r0X1ZjggkjJNT8vu6tIArvhcADCDZ93wTpFUgI\/21N06KSVhoYmgQAggI7XvXrEUsJXpz01ZMOubCfenCC4svugUe+NXHdulZ8ecrm+zG1yvqB2zs2WSuxZ7fhdgTLiPvPVU81ADdWxlm9vbIWVIm4RRs4Io9WDiyhywqm4YlhiV3kZ3rKT0UnDoGA4B2yXYL60FLEGRd\/66wjtkVDLYOvFZYkPVpg7R59WdHZCvdTNKr+CQ6A2MKBhiVTQQVNqHu3lfxCIQ3+e7zOMBKfxCyGiwYIi6iNezJ9fUsu2TLMjONn7\/vSzTn5S\/ns+aCNxyERXf\/Z9C39qQ2lElo625bayvZEk2AkQAJBrHxRCuq6Ts2AAyMlF7dxXenMcDKtbo6QyJgAcQyRCnIPIrZn4yAC\/EiApiw+sp3HFlYfdfXGrn3hIA0sbe2aaNSG+KlY76R8qVDOqqiyrgSkwMPS2864VUt2CWnhAUJeNE5djFjJW6E626qMIvS53NdLhrCVO0eJyWdqLrbwkOUvfIH3C6ddtHZ20mRbDwMCJuSAxFjAPG2NEuQD1kS3S74UMRbDAmGMRa22IPmvMj2DKw87\/n\/+3xm8\/2\/+sLko87qfTkC95nbLs803\/7794nVT3y4iXsOkDICRRasGSSN31mduCEJhiYDo9x3HzKgrPDOmruXrGLEgkEkEFiCMARJcCqljGrHKSnEQRblbP5heocYMlRRLwR4C1e+4Zjy5n\/9KIi7DgwFQ9gySLvyvh2vTO1cYMGiolUFHxkqm9RLO+uZWOgEVDLxaBMcV0JfB3bnWjtSTdihAVSbfWECtHRnQz43K9IeB9tqcW1yAsJuhUr2VlmME0\/HINStEGVfDmg1ZMal57jcjdYwbhgoRx8xi3+25vn7vnP5nGMufFlZ4rWXnjuZfv\/j84LOjovaZRyYchmILEhZ2MBt9mCAWcEicPcUWyS4BjvWhsiRqpYYhl2oqMi4jZoJghQyxhV86MB5amQFpAi6WqbOehC8GC8SgAlbvn\/SsfGGx3\/SjM69rWBYHYPIOvWB7U6VjIqLBQ3SxrEAzOB4MzGUSdxKImGtKiBhMEjCu8J1yC0IbylRsV4V2Z2Kul1V50okuj0+N+ueXmOhkyCXq8eiOl4Ij9LgjuzFsI\/tudrFJQE2BBJeqqQYQ+UJ1liYYj+aQ4v+3hVfLT9+cwszf+FlUXpJAiu+8uaD9KK7vtte6jg+z0UYbQBSoDCA5iJE6MInJhfyMgyE9UbBizhYIR2PAQMmW0ltEgGahWEQKyIFBJ5r8SXBPpY0Uj3Xbfjx4U51hwK4dP279zRL7\/pGDlv2NqYEKIISDDLV+7renTd+4PW1qOSAbJE09Ht3hWpv30T9UVaIH0NwuVEnFwCS7I6ZjjVZVKVhU9aXqs57JU5K5GmFdQJ4ySaRPKXikbB0aTQIOP0Nx8I7EmpwOSVTLQ+wbdeUvCIJUVKq7RtEhM+7a4BEAI4NhL8mHJfRqDYFqlA8e+P3j1jEzNcT0UtWJYCZ1YqPHfu25uee\/EJjadMBIRcRWwMlCcwWGtbfJ6hIMrHQPsyygK526ldVYKxzoKyARAadQq5VIX+LTJhlIT\/dKHgyYECGIYx0+zkxihw\/k2uc3z\/OkdLo1qqfnr9r88b7v9tYfv4UhS4wLKzw+uWOI\/dsp617k43UMTRqAFcYZ4IhAeuT5GALtgZSkFNRIAnjFSwywoItu5JDOFF2QeQbJjQgBKy1MIJA0onZCV3xmyoceIV9olr2yKNVGyADp48kqvlaZrhY3dfIOu+AwbIqvwJbH62iWmTti07H4Ktw9Remah2MEABZCVgBIuXIGDJgYWGIoZQCYoU+NWN9Ib\/L+dPP\/\/vvX4oCAQ9d+qbJUx579v+1lQsfbuKBSRz3g6QrfmFYn7UgwCgQpLuHhIYl69I\/FeeOKubRJKkhSAA5FEXDw2tyrf+15\/XP3Ay2tOz9h72nrdTz+Ta9ZS\/SAwAzTOzkdbaEjV+Z9L9b\/mM4r2aHAHjdjR+bEiz7x\/eb443vJNMHQQVP1zogAdLFl+QuzOiAuK0A5krYaoSAIeVKM4mcxpQ1YEfyO8AKADaGFC5Ad18UgDADEzPK1vRryAEKswUTZPsiiG4TR90BcdHtsVQxk8wckBANBLRaoiZruZnBWQHON9hycy5nCHHRf+sMGzOMJUglQSRg4xgMhlRJ\/3O1qtvpwPM4A9gTMsIVylifUqoKjijAKpcm8+6ekY69FkYBIkQXta9Re594RvNbfv73l4zHTAKrP3H8wWbz4m9NLkUn5EuR26QccwmmuGJohIWjmtkXN0NXiErreRI3L8NZYBYCLHMYoAZtgtY\/5ibN\/lT2h3c+k37\/9V968760\/LFfNhQ3H5K3ZTAHGIhhTL7lPe3\/u+m6YXvNx\/tiLPnFWZOmblxymexZfUaOBxQJDQgDAQ0CUJYZEBSypgwiPWoSa3sBbAkwTLAUQjJBwoJM7O7MVLsSSwGoANoKlG3OaCs25hoaVpKgx8na5zjbuKo7Uutlwy5botz0gmiaVhrI21Kfnax1uZ\/7mubZWcVeXptrprbVi6SQhVBlTTbLKozjch5xuTkb9U7L9T2za16U94qj+AAlzHyKC1MaFLlBSrHLV7NiMEWwpMEWrqMoaewlgEw1NVbHFRy6kWKozieWztKScAAmDSZb9Q6YUhMlrKuGEww2gDROvkeHeWyUbXeZOUecO\/e0G5bv9CzzM89kxGXveNekgU2fzZU27UVKgCNGaBVIG1hE4MAOyqRTOufJtjK0Q8O1agbaQviMB7KNWKeaewYapl6hd9vnRwu+dO36ejB84IOH7Tmn1PHhfLHn7OZANW4uY22vyr1zt5tW\/\/0Fc6Ef\/fl7W6dufubLuf7nL2i0XVLayKVXvF6VFQpen88zOXYrjaDRpJTGhmAP4MS5ZQWVEEfWOP9QurLNEimYINcVyeyjMYePB2HrsrLMPtUyc89l+bf+cvW4j\/8kic4b3jVnoHfVbrJU3DujC\/tQVDoqtPF+WVHMSS6CuQSS2u3u7KxFmoziJP1QF8BiTAAGhC\/C9yAlDVBcMeuWUC06IcfIiKTgw0rAWCCbwQAyKDbv9dPJr\/j8J2ifU\/t2VqJq7Vfesbdd++THsnHHO1ttfztHEQxrEBECSNgSQ6oQhjUgtE8FefF1BqSpsvTGsyRgICABLlpolUV3ftpjfe2zvrv7f\/\/t10QUDx9\/353d\/Mkvf6DY3fv5SATPlxraztjv+395\/gUBMJHE6q8d9KFs3PndBvTklOkFWe16IwEYkjAUIuASiHzDgiBPzGArENM4kVvk3UCb9MGSSFVBESxl0Mf5chy2Ph42T\/mLzORuLgX9yya9+1MDRO8wL1wsR7j77rvUbsv+q7ld0uFx36ZTA931qsD27RVSjwTHFXYz+doqLWl13GlmDAngoZer3GKrQDb04IwckJMcNItKspI8i84GIKG8m8mgbA69mDJgphzwofZ33XrNzhcPE5Zd+OrXNGxa\/e0ptvsQq7tcqkcokI1hrHEgNYCw0t2TiQsNQPvLGlhX1GNZgq2bbUVsEZcJ3NCMnuyke+3MBZ+Yden\/PTLaphpmpie+dOYr+yLe89hvXfc\/I\/VijxuAV1x++kltHff9pIl6Z5VthEBGrsclNXEz7YPYFPM6FGCHBzDVEENDsdCiQii5e49BgUTEGWiRX6nD9scKuWk3cNsxd844\/T837yxpkBuZ5Wtueu90u2XR25Xe\/HZZ7t6vWcatsNqRJ8zVtBRvbVm3BjCP+JWzcBK8ZFxMKxD4FFeUKgeTNek4grUMUgmB5zqb4qAJxeycxZhx7Htb3\/rfD+8s0P37p9\/SNL132bsa+jZ+odX0zwmtcWGJ9hKw8GWzykJKdrrOxmUarA19RkBDQPvMAnnPxRcMCUJB5UtdDZN\/u65p1peOuPzvz419\/yIwWzUaIYVxAfDy771pTtD15I0z4k1Hki25OJK1m3LAbjoBwVaqmFyhk60ytL7+e2sQ19Y7OxBbdoQTJWNKfLkjkcvTsgWUctMULATYEFTgWO8CMlbLxkU223w9N8+4t\/31336MZh1W2IkpFjz8kzfPnMobj2qJe99so743ZXX35BDliu5wlRx0yCXjmHXpx8UwWwzK2LEfjZpIenr6lAXDEHw7toCA9EX5SborqRMjkB\/x4m5agEmBY4aQjgRjQdCyGX3B3FvaX3H2B+mgD3e82Nfx6c+fvHdu3VOfbo23vL2Z+5vdjSeAWPpQTgMyACSgOXblte5WhtASAgGsdvebFNZNhRXkagOyElaG6FZNy4u59ssKs\/e9cc8v3rRpx3+q7Vzdf\/pOe\/zI1d9qKDx3dmBil\/Mk1BTtU3XgtQemu6loqxNgSlva2ikGruzR9f8q3zbIPlazYDCEFE4ZMLbu8SAElELRBDaWjQ8gP+mmAZr8f7t87O5nxj2m3cFr\/e3fbhDr7zyWe1a\/K6s3ndwk+icLlMEwMI5Qh\/ITKdzd5d1ippqxp4nQmqxeZ+EFxzG4OYownIuU1FgTmDMAC1fcTwYkLawhlNRU3de452emn3fvZS+WK732oT\/k+6\/79sL2rnXntUY9R6m4F4ADKMNlGwRXC3FYGFjB1ZnOFpAQ3uHwdfLGQAgFZg0BoF8qFNvn\/L0wdY+L5339jjth7Qu0LW3HYmba9O0jv4K+NV9poG4R6giSqy1SCQtckQ3xc4AqsVtNZVEFuBWw28GsVk0XjU26gZIQxRdVGFKQBJDQABRMdvLybpryczXjmB+1vvvKrpf6NIKH+KFgxk8\/f3xTYf0nssVNrwpkMc9GwxgN6csvK91X8JOyID1zXE17JM0LqIgjACz1kMx2fY7QzfhhZF3TB8UAaRALGCOgGhqwAe3PdTbt\/q59z\/7zAy\/0tVr3gwvnBk\/c9aXMwLqzmrg\/wyZ2Nd8kwIhhhQaT6yISWsBJVjlvUSbOiXdWrAaEUmAdwBqCVRGstRjgpmKxacov5X5HfWXGp6\/teCE3Ktqel6779qtf1dD79C8agtK8clxGzsYga2C5Wl2UnlpANFjmdbAOENcBMA92pYmqmPZVTYPzlP6GlBkwJMqc6S6h8Z5w2q7\/1XDevQ+81CzuiBvooqvbu\/96zdvC4qbPBfGmXQNRBBCBWbscZDKEnJ3ggbtevqwv1aQhLAEm46yPjMYGYHClZ5slw0rj5xsLkGpEFJVgcs0oNOzy40kf\/NEniV6YzqUbb7wxPP6B\/36F7V77pZbS5uNDUQBMCUYAlhSEkZDQ7nokNc2GfFOBgLSiWu4q2cf5AmwYbAJIlUVJEjZTdi23zbmstMcbr97zgkte8IaObQbwup9+bApW3HrDJLvueOFn1SpPrlTIFD+dL6k9HmSJt8pTch2g17rS5F0Y6TcAXenLtQIg4ZsPVQO6ddODpnHWNwtzj71z\/lsv78bLdgn0\/+qMQ0pr7\/9kGHecmQ+LkAHAsQbZwAM4cuoQACykr\/jyY0rhrA\/pcJssMIgrPessAC2d5WILEGfBkiCYUVTTNvY37HvatPNuu29HX5GHP\/3OmfM2PvbZRvSdqjAwh20frDSwPm0r2BWtqJTkkvVCiLBOAsdVnfm7jzVIEKSUsNpCKImyyKA3O+32ztZZ39rr0rvufbGUO7cNwEJiw9cO+VJD35KvNcoC4jJDZaQrLBjiiAx2pMCgSIw8h5V2oe2geDjNQFtyhRbM0rk8TL5E0xU+yFwjurixi5p2+WkPzfzh\/PP\/sPLfZebPip+f0povbXhXQ7zmwgy27CpMCVRWPrTQqQ3VqXW6PmjXwC8tpdRGhgyXUI9iZOHiRumrj1hWWzDZEiT5\/FPYgp7srr8rTD7x3JkL\/2uHkDuLFi0Kcz953+tbu9d\/qm2g81VEMeKQYQIJHcfISOHqlW2i2EmQVoJiCQgNIw2YFRiBbzizEGzBJoYQGWgmmDBEMQy3BJNmXNU3ae\/LZn7xN5tq77E3HHro\/jrgLxalaDQqa0nmSBkmghbWWuekW2fsrLUWgJVSGmOMZOZbHnjggZ9glO7iNgF4zQ\/edVDLhr\/flI+e353hZhNzkqOsESxPx7dc0\/dWH8BVS1y7GbgRvcrVCBvrCzMYRgjEKo9COGWRad\/9q9PP\/uP\/\/jvKvQCEgd++\/dDChsUXZ6Ku1+f1pkBG2jtAnrASjuwzBK8v5uO\/5H5JKr1GCWAjHJMRJF45sevBFgyRtGBaAmeyKIhc3Jmdfd6cjzz+8\/H92AKrvvKmmWHHs+c2mM0fbypvaeHIuuKdjICGQoAAFMdO4N4334Ncukxqxw8YpZGoSDIYwlgIy2DDAAXoVnn05NueKOdaLtrrR4\/9YaimjROOPvi13f2FWyIWOZlpAMkQ5AUThOAEuIP+S0Suphy4Mp\/Pf\/See+4ZlUUfs8Rg192XtcpNT34hG3fuLkRa6Iaq3q6FHxLDPn3oeyLZ6zT7n0QlIum3IU5NcUO1DzfJcwomCA0IzTDGgEgDgqBFHv1q+k08\/ah3z\/jg7Tf9e4LXQarhrTc9bGae8MHucI8fdnI21hIwIgQ4C4gG2MjdoO47sF68YOiKrUEh0ZAAIh9vW8A6pctkMDVDwgYSMTQa0Bk0lVad8\/yfPtE+noRex8cPf2fL8w\/f2Daw5qKGuKclIsZACJRDl1bMaAsZRa6RhgPABpAmdBI3sGBVhg1cAYskCyk0FGu3qTFAQYieXGO5p6H96p5Jc8\/c+8dP\/m64jquiEKZkEbPIwGiGjSNwXIQ1RWgdwxhT97rGcYw4jsWrX\/3qUZM1aqw7fP\/jd7w2W9jyOkXFSm+shYC1DCXczkWUzjr6Ur+kVzYd93KKoKqGVD5VSYNkYJKJAklxgiQJAYl+m42Kuek\/nXb0ey6iV31xEyYWpp\/y\/Y2bb7v8ot5lG7dAd3wxB5PNBxJcLEAIt1HCOOkcgnXfV8V9pjGqnjiRQXbDb73+AIORdY2yFIEDgVgxZAxkuXhYuPZvZ0DIH8Ju3z67\/rOvn6c\/+PbPorj5HS1B3AaKEZUtRCgRAFDsLChBe4nWRGFEuMYNa33VCgCOIcPAk1S+T8ECaMhhA5qXFVtnfvtp3ufXb\/zOtSMSVVZYS4KgICGs8EPNvJ9pq3YzLSPL7IySEIKWLFlCOwTAi35w\/nTVce\/ZrarYAlu1jsa6UsrEbR9yEDWjZqRI\/VERnHru4ENYMGuwZohMBpqaB7hx6lVTXvXZi+llKumyrWvyGy\/oZeZvrv\/Za\/O9\/as\/BLO5LZcXoIIGtPIekfbiAV7NgDE2AivFQldi6ERKVfvOpcAVSMTWQogMcpl80DmwaeHAHR\/8XcMJV67dls\/2zG23ZSY9eeVri0sf\/Mwk3X1cJiihqC0CSQgyAXRkEQryVXg+v1iZaOl0qXzNaGoQHkMbDckCIlCAUCiI7EB30PTXhr2PunjGhTc+BH5kdBSREFaQgCIv1sSudsGwb3KoN15UCORyORhjcNNNN\/G4A5iZxYYvH\/j+xtLaNwjqR2QNFAkI6zWgAuFm0A72rGqExVNN50mqaVBJZLohnrw1SLUEEmCkhQwIJc4M9KjJF0379JPf+\/d1mUfybCkGxBfW\/OL1j\/f12e9wec2sRkUgneR+Ca6+Pp2uG5rDqLfZSivBHHihQFdUYl2DtJck0oCNEQYKNmhHF5pLOtMwsHnN5lYAYwOwkOCr3rvX5rsv\/Aj3bT57albnTWwRGQFihjYEMgaBJAdSwRWVBGK4HLnvdU5SR8nHjiBhgwyUtYhsgD7Vvrowbbevzj7nzmtoPo1pbjKVcxxAM6kYxlpYSFjOgthWRBmSa5u+rsYYKKVw0UUX4ZJLLhllDmKUa\/l3Fh4cFjrPbTD9EBw7eMkQlkKn7W9iF\/CnfHv2MfCoY6mUpaY6CrLkOz36qX11d2bOZ6cdf\/GPJsA7okOHWe+57SY0z75ggFu6tAKs1ICMQIJAnAE48BVYvrIqqbNOOIxhfywERyBEsMTQAjCCARUDsgQYA2kV7EBG98Xtfyw37XZ+sW2fs+e+738Xj+VT9F33+WnLPnXoZzY9ce8Nub6NH2s1fXlR7oWyMUJYBIIQSIIQ3rImTSyCYAKCUb7JSvhuyYSRZwHN0rWYWovOsL3QNXnXH\/VP3\/eds79+58\/HCl4AMJTnSORgRKaimRYghoJxwhB1hCsSQqutrY0vvvjiUb+XGuVWjvzGJe9pjHvmuUob349iGYYlAqVAJh77zFvmwQ3qydBugp\/GkJCYTkVDCIkSN3aVm2Z\/dcaFT\/4EvHACn6OzxJaZ\/3f9VUdP7ukpfHNSpr8FMbyWdQCnOuLcSvIT0ZiqUkSD9nrys5bIDnKakvRUMqyFWYNkAKZGFExuVaZt7pU05bCfNy780YaxMszrLnnDoT33\/\/prrbZ0QostBzADkKThtKaSzd7FmtaTpsLfX1YwtHD3koLLUScnTFZ4ZRjHVPfLhhXxjD2\/N\/Orv7yaaE4RX9+2LGusXPedhASzgSQDgdjxulA1bbPuPRI2eqyZoVEBeOXnjl3QNvDMiaHpBytUyjwNa1c+p11nRioMdpVAiWyr8a5CyqxyJZGUdMi5gntDrulAwLhjKYKhAEoI9FGu0Jmf9b0VB19wDfj9E8gcG4j57rv5p\/MfOWBek1j38UzUm3UPlJzFCrOwZQthrRtE7TWOYQnMCoIFpJRgG4MQpaZBOuG7SDOIJIgtQnbscywbO\/qo7e\/F5tnf2eWj99wH\/teYoutnvv72KVOiNW+Ou5d+uQGFeRlTgNRFX9PohTIBgJyCCWsDK73sjWYI5badIJnSmIT6mgClEBsgCDPQEAPr5KS\/903f+2sHf+1P\/8DX5mzfxS72QdoYSig3p9q4TJ70c62rMk+8lRtdKBTG1wI\/9BAH2ZsOOk+agb1BAmzJDW2CE2WzzFtVPNa5eQa5DFuTWym2mdjrV0kIcq1ewhJ6qRm6dc53517w4\/+aR4fFE5Ac+zr+eNK86Mavd97z7UbJz39EFjcRCQYMwZYiSBm6xgc2rkmE4a2ahRQSNi6DEFdSxZaEq2yyEsoLt1GYxwA3lk227Xe5tnnfbz\/rqw\/SGL8vZpYbv\/XqN8ediz5t474jm02\/lLYEQTFYmmoEzkhNrDBuTCo76ysCBTauKUFIp6IJcp1DrqEqgM03oiNoWcJh46W7nfmhG+iw88alK23GjMm0evXz0JGPd8lJM9mavu10ZWISA\/f19Y3JAo8YA4e3nHig4IE3qwCkvWaUqBB77Gh6rh1rUh3uY9NdGZxSkrDsQ60kbeFa4AQ7yVmtJaxS0GWLslY2ys74aWbXky+nCfBunyXeb2E\/Tz\/qmz1i8p90tgGWCQzX\/AFhYKUXWvAjWpVlBDYG4iIEx65FFABbAcMBWORhNEEiQIx8VKLJtxUa571v\/eSTPhq+57b7xvZ9EZ79+quO3\/DZuT9T65+4cprdcEyW+6VCAYpKgDTQAaADgiUJsgGEFa5iyquDKD9r0adGnFghJDQDmggaGRjZiC3UtKnYOu+baxrnvnPG5U\/8fLzACwC77bYbstkchBCDpk3aIdJmCaiVUhBC2PGzwEJiLvUcR6XOuRRKWCtBtgRB2k1098xepU807SJ7VyFpWEi0lQdN+KjIoVI15mKCNez6e7UCqSyioPmvNjvvP5rfcsnmCQiOQ4rpbVes6br53C+VVvfvllfFPaENBASsBki6iRQiiXiSMVAEsCRE\/jsMBSOAa70rZTImCltXxLmpl9O0o66fetoPtwD\/HNM5df38va2FZ+45K7dl8SdalJ5PIoKNDLLGuEl\/nieR5Egyo6zzBC07EKdH6xAA5fLbQltAKwjkYGWAYpg3HTa7eKBp5n\/+9pv\/uOmSHSCBm8lkyBhDVeLWi7Wn5JDSwAXg6qythRBjq60aFsDrf\/juefzMne9vRp+IYwEr8k4x3qa1xv0MPEGJt7CVZ0yVUsma8WSVhC9X4xO4URSZjITVhP5w0jP92Rkfn\/OJ36+cgN74rba3Xf3Ihu8f+BGg66as6G+FJQgpXS+vbyapFOAkfDYRtHUysmwJZDPoC9rXYtKMbzVP2f86estVm4GxiW\/wjTfmVjzx7TfrZ\/7+malx16Eq7gWXnE4oJTeM8MQawmpvs4ir9xVbMJQjYbxSpIXT72af6tKqAV2qZQ1N2fWytqPf9JM93nhBL76748aCDK624mE1ytIs9FjXsADm7jWvy5qefdloCAqdwHnyhabyuQKOqEJN9RQq2sKerEpNHwQlesxcGWAAr3\/sOC2BomgolLJTL5296z+XvAiDFF\/mi2EyR9xXLHXfHAp9tpRuwDds7EpbbaJ7xD7edSwlQUIGzegqhwNB45TflRvmXzn1A7\/7O8YsRURY883X7dXx6Jc\/1Rb1vrWZCpNIFwEhQNY4ORtLMDKAtdoTnBkAEmQ1FBs3JZJRkWiygtx8Y69lYNmlOnt11kSq7c9qxt7fnfSVP9xZUwZJH\/7w6Q2rVhWCV731rfozZ5\/Nixcvxrlf+AJ1bNhAu7e3o7NzGX7xi\/\/hvfc+Ft\/\/\/vfplltuoTiOqVwul++\/\/\/6t2iOttSSEqIBYSuHByXUZ6ATAwumNi3EBMD92e8OGmz5zSshlpxivAI6LkMqxw2R9Lkl4F6FijesVbnilgxoTzGwrondQBB273HEQEFgT4sap10377L9+PpHr3TFr1nlXFdb84Lgf2JI+VHDPQSQsjCUwGycrEwFC5kCsXFZACWgVxt3U8n+6bd4N2PWVd8w56ZJOnDu2zXXgJ++cWVj39Bmm65mzGm33gVkeABntRAhATi6JHG2sOHLNLqxAiAG4GuXKSFchEDEcqxxHyMDJ4GqRQVnlMcD5JVE46ycN+xz068nnXbUeFw0+14tOPz146LHln+3o7j\/whh\/9KPrfq3\/GsTWITZkaCbRpy3oINOCDH\/wEE0kUixGVYisMIKSxNxPRL2vrG5RSMMa4HmIiWGthrYVSYcoaD26tTYN6XGLg4iPXH6L6Ow6zbCGJQMyQNk4N8RJ+FJdv4PZODqd3lkpai7cu8vHxM0kB1gwbATITAMZx7v2yYdFA05wr2l+kPst\/l7XLR+99rPjDg76vy9EVKtrSIK37FuOYoHJZcByAZCOKEHEJwdOmZdKPZOu8a2Yu\/E0\/cNfY0kK\/eldzbs2SE+n5e7\/QWO49UMFI2NgLEPhhFSRhlUtHKVhnPLx2mkDk0o0WgBBglUEpZghhYWyEMBRQVsBQDr3h5C3FsP3m7IzdvzXr4zctHyqFtf6551QReKUIwuNiE6NknEBsoESlbpyIUCqWYZlgUrXMsdFL6x0ziiKXQReD+96ZrUt3ede6XrxLY5ReVUPR+Bsu3OecVt0\/1QpXpia0RkApILIEMzma3rsyRDQ4bqpI6Pjdxg7u9yUCoC1IEEAKkSEELNCHbCkKG741++N3PDEBsR3vSq+wc\/8wT\/QsVNR5ElkXA1soaC3AqgH9aFhGjdMuz8474pb8my5bBTw4tnd46KGg8673vFavvOv\/ZaPeV+WiQgM0V0ZpWgvHfvupGVWtNFdb7XllyGQWr3fpDWmoAFACsGULohw6bVMpyky6xc7c4\/tzDr3gITr++GErqR4GoGzOsAyhuOAUOciZJ9iqLlZFzI9SZdVDEGBhGBIRIY5jKCWRVpsZqRpxrKqodQHc\/b2F+6tC56uyMkJJJ4oLttoq6ocvMQQsK29hra+sosoHBVcn7iWsMyFtnVNjTyAAlUORLQZE+5\/7M4f\/CZjgrV6IteBjv9uy8QeHX27L\/YdmuW8KiRjMhJLIrdDZKTfHjfP\/d\/oHf\/dP4G9jAy4zPffFgw7v\/d1bz8pEXW9t4uIsQQKG3Gg\/oQ3IJtqYBE4mkrNTxQQFYE4EG7zd0y7\/7HhzhjQGZAlWNtqB7Ox\/9mRn\/Gr+oe\/9DZ323i3A70Y8x82NjXZyD7OU8MPHPDFmE1034SWiqDJ+VZKBZAGplK2dcQ0ApVLJ60wLH9ealLuMuix0Kn7ePgBfxCzK\/2\/PkxvMwC7gCGQFSDo1DLLpWbSu9I5ZVV0grtLQlV\/TEzeTXTXNgvkOGPchBQbCti3B9H0u2\/Mjv5loDXwBV+HkD\/81vvn7f24POt7FKESxkrebpvZvtu\/92AN0\/NjDmMe+feLUtZcc8N5Jev35edM7X5F1aUihUBYu5RiSm\/boZLtdob\/w7YyVKj5JXgjSzdaFVLCswOyqxkhlUEC+K85Nv1rvcfzlu57z3+vAo3ft5wHoV5oMGz91kCt5M7Z+2kIyWwau\/VJ6M6yNQO3dDQCPPfYYl0ol312kU3Fxaobt1pY3cbe3D8CnffbVM7NR1xsbqSShGSEBxrqeSfY6QVTp2ufKKM70rkKDQOxV\/GmrkMi5QgYQSiIjCAW24OZdrpvysT\/d\/e8ihbOzrPnz319af+Xp1w30mNlCNP3S7r7nDVNO\/UPfWNn\/tVeem89uuf9V1L3kk7Lc\/9q80MKCoA1BwcDaGEoQrLCuP5cEBDNg2JNUg\/T\/wYIgLLuSTgqhlfu3YqDX5guFcPLf1Kx9vjH547feuy1jTVeuXIm29iyEysLqoKIRQ57Z9rbTt186DRNFblKhpvrW0hjD3poOImxrCavxWFsBeIbsmx9CHwA\/XsJNC\/cN9oIG2U+yDJW0oyUtgJQ2vzVWt3Y4gAVEIBHHgJIGKtv4RG76Hj+E\/dsEol6EFbftclepbe7D8xd+ZwPw9Jheu2jRojD8\/XuPC1bfflaD7j4pYwtTjDGIrQKRhFSugZ4sQ2hOzR\/21lZQJdYlttVSTuvE1S1JWFjIIIN+HdoYDfcOiPwvuiYffttBn7imA5\/YtjTjvHlAZy+TjWMIUt7QJsOrORWNVzs3LBwWBKFu7nbvvfemhx9+uEJUaR17Kxx4Kzz0GmsuWG1lFpkOzhA3MgtXHcW2oj6fqFc5TV1vibnsdxaqDpWvrdjwgQKl3OZKqxExhAgAFaIU0\/WtZ\/1s6QSUXpw1Z+FlRQBjln1d+p2Tj8j8euEZraLvjBa7eRp0BMQGJAiKktY1pzxfkUhK4024FGPMLm5kkKtr1s4SxoKgVRaaMog4\/6hpnnpj3L7\/tXM\/8ovVwPbdLitXAk3NoXtf1t5jTKZ3uxqH6v3slD3ZV1RZY6meCz1t2jR2ChuuLDipsrLWVFjorb3VilbWtrPQ62\/\/VoO5439eC\/9GiQC6k8kZHHxXJpRTjU9cMcCVgT3Vx9Kqkz53Z60BCYMuyq4oNky7HVg9gaSXyNp0xQkz9cY1Z4mOhy5oQnlGjotAoQwEqAjuCzgVSKq0qfnQSRCYlKtpBkPaCAFpX1cvYFhBKAHiCIZy6KRJm+LGmf+TmbLLt2d+4MbNGKcZVvPmzUNvbz9rHble4lSzfXKj1uZpU2BDPRKrr68PmUwGhUKhWvdUwUHtELrt+xiDABw\/+I99M8XuwwHj+kMHxbbA1nFpVYaUkk4Pn7Mb5EWn6qEpscgArLEQQQCrpCmr7C100DlLgYcmkLFTL8LTF508eUZu9ZG0cenHcsXNJzbI2OnxlA2Qce2gJnY5ft8vVM1OwI\/IIQVLAawQENZNkBCWwGwBBbAQKHGAkmzt66OGRxpn7v1fs86\/9W4iinD2+FXlve9978PNN9+M559\/fjg2fYg0UH1396mnnkKpVIKUCtZqGGMgpeOK6pHM6dhYCLEdJNbAulc32MIMttHgFj9vZq2oqbNiV0heGVLm419it8OCuYZ\/porZdmKIEtYyIlLrgym7XDPlLecVJgCy865FixaFbTe89w3N0WMfzER9xwk70CTIApF1MWzgemwZBFLK1Sz7e4KRHuuqwCzhhgCayuQIo4SLk6VEUTSgnGn\/Y9y4y\/XP2el\/PP7D12\/Gh8e\/nHbevHnI5\/NbubVJBdVQgE58\/3oWtL+\/n4vFIvL5hgSU3oXmSkPD4EmS1d+11jxaOZ0aABPkwJZXZ80AiKzLsnkBbK7kxvzgMlQF3LkiwQI\/KJsrBFdF5bASBnN16Bm757BSiGXj48Ujjlo01g6WifVCGV2BZ79ywjEzbjjr3WFp\/ekB9U5mlFCKLLISgBCVAeokCGQtBLQr8PHTDwbJn4kI0udELZwBkF5QuGADXQ5nPlxu3OW6hrlHXz9l4Tc27ciMxKZNm8haK+rFo4M0zf3jaaBrbeoyy\/vssw89+OCDMH5elRCElOM5aGPY5sH1tQDmRTe0d\/zw\/+0mOfYnX49NxlYlkYPqOrlGjD3FOqfzwpXXG4sSpO6npjvnHPO94gRSdq510UUsPjHnA\/OKa58+Pdu39II8984g2weQhgYQBALWugZ6TnpyGWBrKo4z6mibuXvFD8eGa5QpUN7GlF9u26b\/NHPo237Z\/oavrAf+utVWctIrDpocl0yTDbTpLlUAQcxFArIAsigTsYgET5\/eikKhQFprWSqVImbeuGTJkqgO80sunqVhXeg0eIfrHpoyZUrleY7AMr4WOtjKha4VuBBC0FhE7SoAjh\/+64KQdBuIwcZR5EhKI2uJ6spA46orzQTn\/qQ7LJCw8f4CWGefhXBxElkGBflnio1zbgaemUDMThTnPvftV8+dtnnB+\/Foz1sDlA6gsIyiKCIkC2WBAATDym\/GxjWlVCqO4FqBpB8Ha6pCcgDBGWYBFgoGEmXVtGQgM\/kmM\/OoG2ae8\/OngEfrntV7jzsus3jL2vONxWmxkIZkhg0bsDZkjSKwgSUNCMWBNNi0aRPY1fEqIlrU2tr6OQDPDxfr1nOJ08RVeg3Vu7thwwbhNZ5TZBf57qT67500\/2+jBRbYsPiRgxpN1AIGhLfABPKjGHnQLkopM8qUmhJPNMhoU4rASsgLVNwTgChALHJ\/2e1zdz4\/AZqdYzEvCldcfN6pTZue+4gqb3pVyEXX6RlLsJB+RlWSTdBgKyojWpKYtyrNJsHEYAlYo6GEV74MMyAK0KnDLpNp+4Vsm\/XLaZ\/5x6OwwwtVbmlqEv0dmXkgcaBSCtpaECk\/GVUC1jjlR7hSTG0McrkcoihCEATWGBPWHnP16tUwxgwL4KHZ4vqA27hxI0dRhHw+jziOKpuAMXbInuAE5DxGWloAwPP3\/TrHXR1H5olCo91QKlcmiao7XetG13sbrhND+Oem+Sx3jgI9WumCyNw1AZudALiLFoW9V7\/32M6Lzv7v5v41\/92sN7+KUIAJGaQY0lioSILiLIxVbo4ueZ1jy967En6MqQSRAhjQ2gJCACoAsnnoTAs2U0vHxnDKb\/XkXc\/88YHv\/uzUT9376GinNBiZs5F1w7YVGwgde4FMARICARkEKMOYGESEcrmcAEgPxfAm1i99347up34qa8899+RsNotSqVwhxNwoIIx4TAB88cUXj03YXf3jd1MzIloQaO0Lym2qA9D3hjDShdBVa+wH9FZc5WQvphqkM0OQH6Hl2wijbNtyveuBT0+4zy\/euvtuVoc9+pa9Nv7iLec3RL3vaLWlySIg9EVlZHMBjDaueooYgAaBoIxvHJU+DLIanKipJBU6zCADKE9URWEjCjLXXQza7lCzDvrOUjX\/iePff0lpLGnD9QAkYkgy0AM9yGdCaDZgSDebCQzLbnibJYYUBK01lFIolUo2juMhSx+11sPGwHUDDaovpKy1hrUWUjolz3LZBetSymHzvtssKyt618xUtjRNl8oIZDW+ZaofxNdTlUynjiiV3a4dj1Kpc5UBEDT+c3PjEWuAmyaQ9CKs7stP2q3wx\/3OKZc2nzJZFPeWXHBxbKzQEISIShGUJAhKdKINDLlN2s0cQqouxzXiW+Zk9jogCUJlsQUt3XF26u9MbvrvZx+78E56xQf7tuV8g82b2cQRBzKEUTmUvfC8AEOQgSaBWGQcG84WcRxX0kG5XA5a663QM3v27G3Wo0pKJGtXf39\/Jd611lSO6xob5IjHvfjiiwmjpN4VAOjejbuQKU0JpISJNZSirUbFOnfYdWEw6gOZvGRn0j\/p9hMvJZICOogQIYwgcftB7\/n0wASUXliCatUV758Zr\/zHW+xzj7y\/VQ8ckbUFx3cks62sBTEjpAxgym4T9vGrJde+J7zgXYrg8F1EBCCAUQFKMtdhslP\/afOTfzbz5I\/eRfst7Afu2OYznzx5MnV1dVE5jkAy42cNxVCIvBqlQEQhtBBQcRGKKhYWcRzXtW6rV6+GtZbGs9HAv5driPDVWm4jSe14QxBlY27oZ2Za\/6n998kQhWSMqx4hBlsDonoaztaTWzXXg9yux96FYQQAOcV\/ybErx1QEW3ITziPKrLYtuywea9H8xNp24N79869kd3v29jcEz\/7lMy2m75AmEYdkYz9i1KTSfFwFI4Vgjt0gNO+oCnJQJwFYSDewS2soZYEwg17bODCQmXoHZu51ec+sox\/Z59TP9uGz9454hrvvvnumpaVFPvzww0MW9EgZuPvLRmA2YDA0OT0vAkNwGYEhCOEMTmJZiUgEQUD1LLCzLbbaEosh9MtTRsuxyzzEOUpK5hwlYnZRpBEEQV2iLK0LPWZVyr9ctbB5Xn\/3fu1RBFgDZgMjg0pxVbWuO\/lAohoAp3O6nrCq1n2Lqu\/MbuSF0QahJCDIYCDC6v7m6RsngPVCYFei\/9LXHtj3xA2fzkSdb8ubvmwAAzJeSZAt0tJHDiBlMGmkG\/SEcMU8TK6ySEgBhgIrCaOyKIp8H+da7i4EU65c98b3\/vmww86LR9NUT0Q46qij5m7atOncrq6uPgDfqBsDr1\/vRpBYA2sdIIyxvtxeOBCym\/BhrQNEEAQVq9jY2IghLPAg4NaLe+sDur41nTFjBpRSKJfLPpUE35Wkh9wMEvC6kssxALj9+YFWGZvZZIzP0TpRMzc20isSblXM7awtapuOOOlKcq9la0DCkQNKMmI4eZ1Iw3ImfGS383\/ViQ9fMwGwHQZcgVUXv\/qVYs1Tp+KZf75+CpX3NdZAkhsHSvCDcAluTgkRjNKuDY4A4dUgK8V2lmCZIJRC4CMwlgG6RX6lyU26vTdouy233+n\/2OW0j23BV+4b1Smee+77DnnwwUff0tfXfQKzPSYMw18MGQMHATMXbBgqlMsGWuuKhXO3qBmUYJFSVlxTpRR6enpQzwLXMtDDkVfJ36y18G2\/W63HHnuMnZyOE7fTWiMMwwojXuMyV5hwD+Qx9ROq1nJvS8bodiUA0o5pluRGhiZ52woxRVX6uRLT1iHNmAHpREqdJIpxjKUbMSFQtmogbJ72AJGYEKzbQcBd8ZUT52ULm04Xm5\/7UGMY7RrG\/U6G0CbqF06HzMLPOBMimTgKgnE6aImRYYCEAIQCIYDxs\/Ygsuu6w5a7uW32j2e+6dJ\/TdtvvwgY2VW+6LiL1J\/jW+cyh6c+9NDj74+iaN\/EOpmhUFH1ArhYLFYsmnM5bToYB7whStjgJI0zHMO7NQFMddzp6iCy4ZRvNmzYYK217JhoiTiOK+AdzjX3sfrYYmDZW56SjXW7Zeurr+Dav6gyrjtFYg3OIQ0O\/IXvE\/bCY5Xg3RFbxgKkBFiEgGzsFK0znp1Q3Rj\/1Xv5+6dsWf7Eexo2LPpQkyjvBt3nNHlViEJvhHzoN+CK\/JRwbrEfmUIsXBVVIn9qk8Z6C4QCA6qRC6LxmcZJs37V0DDtN7fOOn3ZwoULDT73pxHPjZnFiSe\/da9rl13\/IUn2bQDNDIKAEv2oIJCIokiiXt+dt5YdHR0iaZRP2OOadwFQdUm1dq621lrk83kagnQirXWN+zq0S50aSlb3Bi6VSpy26EkzQz3w1h5fSslLliwZNYhVC+vWQHOrkAl\/US2jEuzU+BOtK8GpkNe7LUSDeygrbY\/pvl8GSDj32mhGJFRvT36PtRNwG7\/FvCK77uL3vCpaeu9n24tbXtEgSqHlMqQSMExAiZCV0qf4NYxMhnALCOvHbjqGCmxdn64mAikDAUaZBIqy4Wmbn3xTqX32r6d\/5o4lLnYeOQUopcDb3\/6W3Q465IAzLKt3gs2+2lgopSpusKsbDiCllMcdd5y85557tvLOFi9eLK21SmtdAWh99rg6ATABeRAEtr+\/fyv3tKury02FMmbQHKM0qJJUVAJAIURCUHG9929tbRVr1qwhF6M7cfeEwKqNqdPH9Gkv29HRMXoAi8jks8wq0fh0Exaka\/HiVBdnBbxcqZFmcKV0supd0+AOJeFcZ0Hk6mVJQYbZnt0mX7oF+M4E8rYbuEwrPv+aE9d84tR3qMKWkxpt98wMikBkIQU7FYzktlbC9WATQFoAIoD1QK7IMFoFCiWEARDk0INMn863PlCGvB25KffMueSvD45U8pg+t\/3332t\/rc3pSxctOUHb+EiCJEmAreRGTQVobga0UGvXrpUAtgLwlClTsHLlShFFETKZDHvQcer9KuYjsXreNRXNzc3xBz7wAfvpT3960DFzuRwxM3sXnj2IOR0P+2NximhiIQQpperGq83NzSKKomreJtUjkNLtorT6RhAErJQSQgg9derU0VdiNeTCZlkAtDEgP+GcrfXaOBg874irEXBaZiRp7K\/b9M8JqP0\/BKCZumjhxLSF7QSuKF1x1tx1Fxx8Rlvc+8kw6p0kTQFEkSMPK8\/z424EnCh6MsPZuKolQ4SYGSEAkSVABChZBZtt7iuoxr+ZSbN\/ktn7mLunvfVr3UOxrrXruOOOU0cffeiuRx55+BnFYvyeMFS7lrWBEBLaWiQicbUWSUqFgYFeMWPGDLFs2bKtjnvPPfeU99xzz+uZeUkQBIaZtZTSeClWSjaNJE6VUrK3grKxsXHNm970ps21AC4WixGAKzKZzK1SSu2ngFROzLvoFWBLKa0vy6T58+c\/vHz58nos9PKVK1d+KYqiQEppktQQEVmbHJBZCiGk\/zsTkWFmuWDBgkd+85vfjBob1PvBAy5q6nnuYivKblg3w+tgpSupaGv6m7hSKJkE+GQJWhmwYAQpXsENnmIwWWhk0NU494fTv7\/0oxMw3CaGCo986bV7zOhec26u0H1GnvQsacsu1rIaZMruWgeAgQQLgcB63W5y4GECjGXEQoCFgBJASEBRZHhA5hfrXPtvM5P2vqnttPOeoT1PLo+Wq\/jOd76T+9XPf35EOep\/Xz7f8IZisTRNqgD9xTJkmEcMRigloEvVqZUpK+cb5G9uamp6z3C54OGKLtKpmbEUZ9RT3BgqXt3W49V7fFuOPcgCR1GpAcZACMCYhNImpMbTDXMrVbWgKdX0kJ706ORoqeJ+MxuQsRPx7zYwy8u\/fvqePSueektrx8rTVXHToS1UktBuWj0SyRbhimfYMEgQpE1KppKB3QqGBIwykOTK\/Qpo2Nyjcv8sZnJ\/DecsuGPmJ\/+0iO1S4HO\/H63F3WXDhg2vuubaa19f0vp4i2B2WQOQAaLYQgWh62qzblpBQPXBSESIooja29tpBO9jVI+NBRTDpYy20UParsdHDeBMVjVigMHakwLSTRpk4RsZqJpGqsA51VqUTB30zYfws1RSY5EELAsQu5nCkgAl5ZYhiMaJVf\/Llqu+8uZTxcpHPzXbFI5qkQaxLSG2MVQAkHXCcSwkrJSuVlkbCOOtrud1LQisQmgRQEuChuzTnPlTRNlrSwe89q+7nXdVD7Bi1Od10UUXtT695MnXP7noqfOklEcVCoUcww0cGygbBCqAyiiUCwMIlZvEoFQiFVvPUjKEELazs3PixhgtgLWNm8AGBOtE6YRERRfXAzAprRtk9qvTudPbaeXfviIPrrDSMdASbghhEGDzBHhHXnfffbfa\/97LDl1\/zr4fn2x6T20wvVlwhLg\/RjYDWG1hDECSPCCsEx8nNzCdK9JHbiICBSEKHJbLqmGFbZpyC5qn\/qR7Pa\/a84o\/lYHlo469TzjhhHk9nZ3HXfOLX56tsrnDpFQZHZcQSgGCRmwZGRUiNgZaG+QyGei4gJxy6ShXHrJ1xVFS9rhu3bqJL3\/ULvRAoYnZgBS5Xdw4gbJBGlYVUSvUEW4f1L3vphcKVIdCMSChndqgAKBUmSA7Jy79MN6ylFj1mSP2M78++9y40HfqJFOeHdrIVdqQQeB69Lx6o\/9VEGzsJG3cnGYF2BgUBBhADgXR2IGw4d4iyVuyux74j6n\/79crxjLJgJlzJ5xwwkH773\/Aacbok+I43lOGYaiN+16FENBGA7aqHa5IwoJhjYYggjXG3zdi8L3jt\/wkVTNz5kysX79+4kYYDYAlqUYiAox1u7UcpLdRjU9So7vTuu2VXmEGrC\/icK2F7nFpGYK0q+1gwLIsmTiY6EAaglVY9dV37JvpeOLtvHLRaVNF6YCQDYz2M4JgASHBYDfuxicKlGDYmMFQMGyhZBaxAYqUL8mg4YEtRv6rnJty7x7n\/vIO2m+\/CHgauOCGUZ3RKaecMm\/Tpk3HvuY1rzl+8+bNr7XWzEtIJyJAsAVMEjIpQLKvRa72DycCDkZUdcPBVMm7JvUDUgrEcWyDIOAhXHZRLpfn9fX1TQvDkPv6+iCEoCAIEMcxrLUchiELIbhcLnMcx2hubkZ3dzc1NzcjjuOKbGsQBCgWi1BKIQgCDAwMUCaTIV\/lxQ0NDVwulwlw\/b3GGG5ubq68ZmBggJqbmytC7EnfcfI7AORyOZTLZSqXyxyGIScsefJ3KSUlnUvJ63K5nOMJ\/GdKarkBV6UWBAE6OjpEY2Nj1xFHHLFMua26Lj011Fbs8rw1LFulptPngatiHC6mtgRASRgizTk1UUJZszr\/fGVLz+2\/fhetuO9j2VLPXs26JMgSYISb8C4BQxrEgBLSFdUwgyILSIJUCpwJUZIhekEbo2zDvdw463\/6VfODjx38x86FC4XB9\/cb0zntu+++pz355JNfF0LMllI2JIUTXqKmErdWC3rcvWN5CCW72sF29Ykdu379+rpPuO+++3Jr1669kJlPS8okkxs9SdUkxRVEBCkloiiCUqpi3YUQSDqFkr+FYVjJR6ekbSoFHEmdcjpeTwpE\/PtQUrCRNO0bY5iZyb8PJ40VHtyU1GfXkm5J\/XSCqSAIkpRYAmL4PPj9a9euPVcxkXAujanM9rV+a6cRWLTaqpVBddHVlLobRcoMDUKJBUeSJgJgv\/5w5bn5wzrWHNZ542WfbxzY8upJKsoKLjtZIwMQAn8jGVglINkVXJCxzicigqUMeqmhv4DcqqBl2i3B3F1vmP6KI56lg5Je622TLo3jeE9jzN7VMZm2cpMlVmas7W\/DpWyklDDGmHnz5ulVq1Zt9booioiZW+M4np6AJSmBrE3LWGsrZZQJeKoC6\/AqkQ7EURRVQJocM\/l3AjKv6gGlVKUCLLG4ABCGIeI4rrxHGIaIoqiyWaSBn5bZSX\/+dPFJcm2TSrXknJOKMa31tC1btkhVqdioGF0BcCIMNpivqtwMTCCyVbbZL0HS3Vxk3WwbkO81BUhYJ\/odSFuI8hPMMrNY9KGjXtXyr3+8NxzY9PrJtm+GYgvEgJESJgBYWUhokLXO8hrh0kMZArIKZZZWy+D5Agd\/7M+23z\/poNf8s+X8q591Y6u3X+UkCIKk7gDlcrlidaSUW9X3jjXvOtSSUpbvvfdePQz4rVIqcZnTlneQpaz24lYbEJyWs042igoYElAn4E2DPQFUHMfI5\/ODSj+T4ycdR0l9dtIemFj2tOVPrltyDZNrllzn5LFaXWrXQGHTx7cdHR1QhqCTqejsWwjdKOPqzkpcB8i8tTvExIMUKYm9LK119HMSSaus\/ve1wER49uxX77v6rN3fPSXqP6Nd8tzADAA6ch0fAlBWQqgARSLErJEn6\/SdTAiTbUa3DNdEYfZB0dT0t3zL5Hv\/uvf5jy1cuNAAz47jaRIABAlA0i5o8nu6Xa\/Wom7jpoZcLlcY7jiJCHsYhhUwJcBJu9UJuJNWvrRVTR4TqXLOBHQJuKtCdDSoVjk5fvLeUspB\/64Fa9KNlN4oksdT5ZWo8go0aDNJzrn2uUSEhoYGKLAoMRHSg0Or0+PqxyyDLvAgkXdbrZlOmhgsu84kFlAEGGNDaaPg35GgWvndc2aYJx84rbX76f+XM1175KQhXbKwAhDCuCQ5AzAMQRZBoKAFoc9oaMp1l8K2ZUHLlN+bSTN+s6Z9\/rqjPv7DXtdQcPe4n+1XvvIV8Zvf\/KZRKYWkrje5wWpjyO2dLpDeNMrl8pAEZ7FYFOQKlQdtIgkBVNuhlJxnAqB0XJt8jsSqpf+eXmkAJdY7EcALggDlchlBEAxqhEg2glqrnH4s7TkkgE27yum4Nzmv9KailDJCCFZCqAFrGcLPpYFhiEFMdOI2uz7S2rbJxFKT8LHzYJuMpAJIaINQKlhrsrHBv5UPveiis9tb1zxySviv289viPoOyqMcgCyMNZChBTRgNcGq0NPKEjIIEctMnw6yj3da8de+3LR\/2L2Of\/iQC3bsqJFk3XTTTSqO4+bEUtUM4BpX4CY3chzHnMvlhhS8U0px7fjN9Cyj2ob8NAlV+\/x6n6UWxMljyesSQKWBWuuqJ2BM\/zd9jPT7DRV2pL2e9GdLcwf5fN4uWLCAVa6xoZ8GJKyOQL4SyxqDdBCcZhgTsfZaQU3LPARX4pr5mV1njCSTsf0Drf8uVnfzRaccUV72j082DnS9vdn2SqDsr4eAzITQURlkGVKFYDCKQYCuIP8c51rvVk2T72ycf8gdu33sR1tchdT9L9iZNzc3y46OjuZ6hOVYyaoh2OatnhvHsYmiaPMIcTmKxSKklBUvYLw2kJHql8dyrNFcl1pgjvaaMjOKxSKWL18OVSiV+rIMEEmfErBbj0MalCriukmm2r8NArh1UbZUElE5Uhlg0ss9zn3kU2\/cI7tu+dv0kofOnRyX5ofSgOMyKOPJj2yAOLYQYRMKVpqCpdU2k11cUvRn0z75L3e1HLnivEuuKrxYA98KhYIQQjSPp1rjKG5OUyqVOoYBL2utbTabrcSjO+g8tuszD7cRbOtGmD6Wj8d506ZNrMpR1G8YkEqArQYsw1nwarVGWh\/aHVBAeHe62looQLBOFwt+zisIloSL7wjQpRiBylBBRy9TC0z42+fOaNvt+cdOl88+ekEDyvs0kCVjY5SNhgwVLGuwEijEJo5t0BM2tj+7WeSvK+Xn3VlesMuqw867ugCsBfD4i\/pJpk6dqtavXz89ybG+ECC21upsNjuS0CGnyauxntd4ehI76nWjOVaFxMpncptlWYJN5BvzCTAMCFfDPJZzICeu5Cc4uLlK1pdtETkZUmMNIGjayw2663\/57Yb+u68\/uWH5A+e1lbpekZXF0EYRYrYIMjlYG6IY5FAK1fIS8T9LQj7M2fb74tee+th+Cy+JgCU71efp6uoKAMwaD5dytEspNaCUGhbAUkqO43gIOZ2dxQGjbXavx+Cm2w0bNkAF4G5BEiQE2Lq2M\/JC3W5YmZccpdqT4BqmOrHaibJ70sYAgBQYBhYMEUgMxHruywa5zOJfHz76lerPV3xgRl\/hLQ1R1AqOABiYrESBglJZqxWxaFzKouVu3Tbzbw+qA55eeNn3isBq4MrHd8qPpZRqM8a0Dzd2c3tjwTpr9axZs7oWLx5a8SOd6hlvIm20bvC2fO7xOM\/a1svGxkYoUbIlrRkUGkgtnPUlcoyyl+xMlCkh0rkoUYl9k9I5FlQtsSSGYoawDBah+7d0ZZihELOc1L99SWN35RfOnJH9wFGf5K6V72oXhZmiXAIQoMxhNEBBZzHf8ve4KX9tn2l6eiMa1p941Z09wFMA7tzpP1smk9kHQLa2OmhHgcbnSZ\/8\/Oc\/33vHHXcM7+h5yzvcOY1naithp4cC9lDvPZre5JEb++0wTFMjVBDk+7lIRWNMTvohrk6VZGtWefCFSoTAqyMlGQZMsvJoWkPLWoZkhoUBBbZp+Q3\/2bLbws\/1vBSBu+jGi0Jx080nh0v+8Zmptu8oEhbdZYEobN1swoYndEPrzdnpe\/3l6v\/69bJLxtDxszMtIcR+WuuKvtSQc7HGzZFhPvjgg5e+5jWv0WMhdHbU+YzD5xknL6DuGFD\/HRVYrR\/o39xmsbkpULPZGDBbSJKVlzHzoDi4oged+iNVpje4Z7AXvUsquKzRYDg9JgUgDNBk166aCaDnJXZX48Ez9nl1882\/epcs9p4ekmntBB7TJvPIQKZpSall2uPm8Fc8fMBHvtUFfgz4xg0vRezipJNOyixfvnzvoVUfx88VTN3wUUtLy4rxuelf+uAd\/rjOIxgYsKRsS9MW09XZKZhmRzEjCCU4tr6Xf3DPL1Vi3zr5K2shfG9q2ioTE0hIKMEAG1htoAv9baZz5Tw4f3KnX0SE57\/yrl0Kyxe\/U0X97zHEDTxpym2lTP7ushX3bpy14PnjL\/lFCXgW+PnfXw6RfXO5XJ7T2NhYqQMe76KNOqv88MMPr9wW3akXEri19\/14v+9oNkxmRjabxS67TGa1QanuBhFshBZgk25gqD2x6uwc0BBfhNMCcK2DNf56cmwhgbxCS6G381AI+cfRDnZ+0XbTiy4SD\/SvPbhnY8f5Etl10\/bY7VObujY\/sXivA7re+PEfll0c\/yBeTqu7u7sln8\/PMcagWCwin88POb1+WxjXdIVR4qID6IMbAbxdm8KOAPJYLer2nssQHsqgarMgUJg5cybU2j3e3je\/78erUVBQYQytDRTEELuOG6IMWyW00gAnMCwbgGSqAYL8qBXpZs8SkOOSVIUth9z\/iy81H3XWJb078828et99M6WnS1HzrAVf3ufCC9dXlPtwD16ua9OmTVOVUm1EhKamJkRRtENzmv4eWxlF0WjmBtOLBeSxvseOZMkTYQJ1+ic\/Wdr03p8u1hRCogSWAqzJgU1UaikHzYfhRF7DX04i8hMdvHudTHRnJ2HqxrZwZXyl4BgN0uzVuuSfMwHs1ACes3BhEcCT+DdakyZN2qe3t7ch6amtN1l+NDfmUKmYtCVJFfUvMsYMjAU8I7HDoz3X8YhZhzrGaM8n\/Vnqvb625tutJggiYhLhc7FQJcsMq9mpBqakctIHq7zYp5Z40Ek6C01k07rwvrLLVo8XRxBR\/7zMwJZdMbF2vrCB+SAiClJN9uNGFKVfm7T9WWutEGLRU089FY3i9RyG4SDgbg9AdyR4x+sYte2aDsQ21rrLKVd1CPV8kXidDIOKXpEflAMiWe3rHWzEARAEM4bU10hILzbVmg\/h2g4zUV\/eFHpOufvnF2UnILPzrN122233vr6+owEMarcbT\/cz3TbnLcrzTU1ND48EhE2bNiEZg5K069U1LqMEyFB\/G+lYo33P8doA6s1rAhABMAIAunKtG00ms15DQArpJ6\/XaVfg2jejCpCdW03eMDNgUPmd2bpxLdaXVwIITYS8Lh6zYNOKWROw2TkWESGbzR5QKpXmA6hIxox33jXdTO9lYlZ0d3c\/P5rXJu580lM7FhClwTkaQO\/4dNDoN5Za5lsIUZ4yZYoD8LE\/+POmOBs8UbKJvAe5CixUJw6y18sSqKXQ0z\/Cs9dVlR43sCo5noRmgvWCbPly355Y9\/Ru26rZNLHGnRhBY2Pjvrlcri2KokovbeKmjgd4a5vWvRu9fObMmSM1MaC1tZWstSSEGDRvdyRLWg+Y9X7qWeChnjvcz7ZsJkO9b71rJwTBGN0\/MDCgXd+RELGGuV+DigJOPgQktiIG3HSGYdyUwTK\/VaCnEtCGCcZvBs0oh9TXfdQEdHaONXfu3Klbtmx5tZ+8V5F+Gc+mgYSESSzwwMBATESP1hsnWm9Za2UC\/LG4s6OR\/RkK6KMB4QvsKwGQAyef\/D9xYmZRyjY\/HlvuYmH9kO7q6JSR\/PI0aikRd\/cPC6eoU6nSEnDSsx7OULp4St\/vL5s2AZ8Xf0kpDyOiIxOt4rQF3lFuZFNT08ogCO4azWuam5vJGCMStcdaCdiRQDrc30cL1FpBu22dxTQ2sLr\/kk\/JAoRSqVRcuFCYSsK3b9L8Z7VQmyAJkHC9vcaCLAaNqgSLrUir6geyEH6ieyXLxJXRPGA2UMZCGSeZCmtAemDfnnv\/9Ladtab132Udd9xxCsArmbmhnstcT66m9mckYKQF4hKSrFQqPZnNZleP5hz9yFFOtKHS2lj1ZGfSqZehzm+0Lu0Q3sAggbp6sj5DGb9651QfA643gUh40Q1KwFxODCIA4LBLbi0g07LYsBtOZZMUEbxmJSdvXGWYidPucR0XxL+5+\/EfzLKfZOh2kyxHmahjxZufu+uuCTb6RVzlcrnZWnt0WmxtNFZpW6xuWgRdCPHk448\/XhjN62fNmmX9IOxB7HjiTqcVHYcCSO1PbV55ND\/1woEExMm5jNWdH4lYq7X6zFwEUDOVIWz9R1TsOyOwhQrW00acB6OzGg\/TaNzrmr8TYDQjG2q0qdK+8V+\/swDAIxNQenFWX1\/f3mEYHpLEu9s7mnO456ZE4cqZTOZ+otEJ\/ff399soip611j5qjNHGzcPlKIqQjC5JUks158SjPG8Gho4c610bLyygMpnMbCHE1CiKBuk6j0UXbLTPsdaahoaGfqKasSomP+MfpZ5Nq0IU51Gdfl3iJMoFgGqPaKVsEly3v7Huh\/DVmBxHUNw9s2\/dqoW3fezyxW+84oLyBJxe2HXSSSdlFi9e\/KF8Pt801u6jbbHCyVQDrfWKXXfd9aElS0anRvLQQw8VH3jggUu7urr+u729nTs7OymbzW51AgMDA2hoaKje18bUPUljDEspqd7zRvP3gYEB+PdvOP\/887\/FzG8fKi6u17s8mlg9HQYk\/9Zal5qbm7ullIMBvMsbP\/50x7Uf+wcVuueRL6GitEI8tpaadWLu\/g2YBo9XqexpVMcye1daWzQEWhZK3e\/ct\/l3N8GNFZhYL+BaunTpgUEQvDKRqam96WonIIzG2xrKAqXnKwkhfnvrrbduGQODzXCltztV+S0ztzY1NVVSb+mYeChQjhbMtbG1f365p6en1xgzuGuB3nhyuWzxV7CbPIdBed7UlzKkU0J1f9hytYwyKc6y7PcAAhuLFt05N+xY9uaJnPALzjyjqanpNUKI2Wnwpm+ebWWY692YyXsw85ZddtnlTrwMBkWfeuqps40xu9Z0V41LCWftaBj\/nRTL5XIXM9e0HYERa\/tEEUEnfHG15uq810QTutKRw4BgV3LJTHXZvHq7svu7K\/wgCCCKkbFl5Ln33cs+8qrDJmD1wq3dd999r0Kh8HYhhKydx1PbcDAc2TMc6zyEG71848aNy14O13D58uWHl8vl6WnpoTTw0htj7TUaTaxcK\/DOzJuttRsBYKvRcj2Ns1YMUPCQZrn1jlF\/oso2x0qaBRgKMAYwGvm4sFuuZ\/UFdU5rYu2AddFFF4lisXiqtfbg9ICtsViI4aqW6v0tuQnjOH7wla985bqX+jU8\/fTTZW9v7xEAcsnIlXou8LZyCekRMD4eBxE9n8lk1tQF8LI3\/L\/OftVyey8HZaq4A+xrmdm1DdpUkYevqhpNDm3wTCUCW\/LNEsn4lRi5gS1vXnfekW8HTYB4R6877rhjMhGdLKUUNe5tXWsxHIDTrx3JAwPQmc\/n\/3z11VfHL\/VruHr16ulBEOwdhmEFYPVSU6PdFGuvXzItMT3xkJlXPfHEEwN1Abxw4ULTfNTr\/2CyjSuFSNVC25omhhTzbMdQIJ4mt1zRh4WVyb8F2kSphbtWfmrx+SfNnYDYjl29vb1vz2azxyYATHcdjaXIYaji+3q5U2stMpnM37\/0pS\/d+8KXII7\/Wrdu3V4A9kimEY5kccdaR51MWEx+N8bYXC5Xafyoa+amffrKZZGQfzQsbFV5ciuJyrqu9FjauiQxyBpYAuLkMROhMeo9Itf99DnPXH55ZgJmO2bttttuhxQKhQsBiKT4IG09xmIxxhJGWWujQqHw2zPPPLPrZXAZRS6XOxrA9GKxuFVT\/rbWSqdfl8TQyZDwcrlcFEIsHRbAsAalMHNNjGA9W1GZrFDRyfKpIWZnfV3zEdcFeL2YmTnJJlsgjn1RiARBAjGjmcrUYPo+VHr2pjdMQG3816677trCzJ+RUu5KRCiVSoNa\/MbiRtdWJtVzGWte\/2RDQ8PvXw7Xce7cuXMKhcJpzCySoeC1G1stsz8a0NZa4HTvcxAEA729vauHBzAAe8gei7rUpAdjtMBAApIB6RsdyAIWIOOa\/aV1NdBkXKOCYOH\/SxBwvyP5t38MFgAxrALIEqQRgJGupckatJueSa1dqz739CffNHkCcuO7rLVvMca8KclZJjHWcMAcbftbbQ1yeiq9P84fFi1a9HKwvgjD8MRMJnNA4jon1jIBba0g\/nC12cMx9un8vLV200EHHbR5RADvecGfyj1B+x+KyFjlXSwdu\/lGSA0AJ4hKPEzJHKR0nJwo0Q6S5\/EUu1ewJAakFQ680k2CUDrCpLjzyMbuZf\/Bf7gyPwG78Vlz5szZ1Vp7rpSyoZ61HSoGHsly1C6tdcVqJCCOomizEOLOl0Pse+ihh04mojOFECqxtGNxm8fCF8VxDKUUjDHIZrOrfve733WPCGAAaJ+15x85I+4lliAOoARgY8CCYMCwpN3sJBKDmoEHnQgP7VpXa62F98od+i0RrCQoW4Ys9r3ruT\/\/6r3MLCfgt33r9NNPD4nok0qpV6Qtay37PNINNZqbNN15lOSSwzC8uVgsPvpyuJYDAwOnMfOxIzXij9RjPNrupcSL6erqegrAwKgAPO3SmzboIPx1JHJlxE613RpXIq0FIxYMS25YtRsvOphtTLsTWwviwaWRrFe0JAsrLCz5mEEzpAVadKEp29XxpWfff9g7+MYbJ0C8jYuIsHjx4rcLIc5MSv3qFV6MlyJFKt+bsKfrmpub\/2f9+vWFl\/q1PPzww6fHcfwOa60aTfXaaFjn4TbLhIkWQpSbm5tvpdS4nhGTrRtz7X8tcvhsonclBCDAvrEBMEn3PsuKdR18YkOTIsQCglUlt8zsJxoqQChAaI2MKWNKuXNmc\/eaS5\/97X+8caLUctvWrFmzDi8Wi18Nw7BVKbXNxMpwwK5hm0FESPKjzHxPb2\/vkpfDtdy4ceNZzPyKtPdSjzsYDzY6zWyHYfhcS0vLoI69EQF84C8eXVqi8GZNwkAowM89UlydwsApcpot3B8qP7X\/dj+V56WaFUVyc1jnqhMA0mWIQjemUd+Mdt3x1cc+ecqeE3Ac2zr66KOnhmH4xWw2u5vWekiJnNHGbaORp0kssH+\/fq31LcuWLet9qV\/L\/fbbb2+l1PuZORwtCLcHzAmIfYviA8aY4pgADGMgp+52bbcKFlupKmLPwgpIK7xwnQWzrluWPlRHBgYRXRYusrae7Pai3f5xKQwQFdAcdx00teOR7z\/zsVcsmIDlqMmWGWvWrPm6EOIN5XJ5kCLGUG7zWK3EUCtpdI\/j+M\/z5s3740v9Wu6yyy65\/v7+c5l59+QaDqVcMpYqtuH4h+QxY0wcRdFjDz\/8sB4bgAFMvfLOZ7snTflDQSoIEQAyAIwCaYLVgDUWRKMvp6z+uLSUJQYRu9SSJsAQYAQMCxhiWK\/LE3AZ06OO17d3PfetpWcfuc+EOz38Ouqoo3KbNm36klLqbK11mOYmRmt105zGcIx1veIDb30HwjC87p577ul+qV9Pa+0pAD4shAiGE\/rbFnme4chEKSWiKOrMZDJP1IofjLLgmIHdD72qT2Uf4DADiwDQBMFwzHSqNlqM8sc3GsKhNjUxzb8dWwJDIAYhFk5CCwC4WEJbsfvkXXpX37zk9N1OnABx\/XXiiSc2bNy48ZIgCM4hIko3EqQt8GhvvqGs7nDdSACglLp17733vu2lfj132223QzKZzJeFEJlaEnB7rO5QrHSaDIzjGEEQ3J\/NZh\/lYQqkRlzLPrD3O5u7Oq+aZOImUSiCRQQbMsj46QxieBZ0iK3BWVg4BUsyHtqCEEsBLZxrrZhdAYhQIG0hhEUPZ5cUG+dd8rfrl9y8kMhMwNatBQsWNA4MDHxGCPEpIsoloE3UJhPB9rG4xMPNOUrfeGldKmPMCgDvXLFixb9eytdzxowZeyulfhgEwWuMMZWc7HD39XgtH4bEzPzxVatW\/Wirx8dysIZXn3xLOWz5TVETEBAsSejIsclEhOGUjeruTrZa9GHJ\/TjzzE4VkzWEtSBLEEZAGILRgBYEVowWKi1oLKz\/wTFnH\/5eZg4moAvsv\/\/+bcaYLwghLgSQS+qbk2KKZNbRtrh4wwmop29mX4EVAfjhnDlzXtI6Z7NmzdolCILvKaVek4A2mZc8lBXeFgs83E8URauVUvfXBfiYdqL3fGeA8pOui7XdYKGBMAAjC2YFGAZ8PnjoH9r6v4ZgmWBAMPBCeV40QPgfaRnCEMhKKBhIaxENCFhWyKI8Jdvz\/Hc7P37Ux56\/8Tu5f2fwHnDAAQ29vb3fjKLok0KIfNq9JSIk\/arDudDbCuA6cfCjuVzuptEKtu+M64QTTmiZMWPGJ621r09vUklt8o6uKEuuZRiGi9\/97nc\/V9cz2oaDqq4z9vlP7ln9qeYAxLGAYg2yJVjl5WdtzZHJunQTnIIHEYFJgoyDrBZuShMxINkx0QDg+yhATBDWlVgyWRgIkMwhZg2hGJKAAvJxZ9h+V6Fl9sULfnLv\/S8DpZaxWooDiOjrRHSyENXhzVJKlMtlBEFQqYpKa1xtK9ucdh2NMZV6XU\/29CqlPrJy5cprXqplk5\/97GdbrrvuussAvIeIZLLxJZ91tA0eow1RksdqRfGsteVcLveOZ5999vd12w3H+sGISC+beeAVA5lJfzdGgFAGsQYLASNcIUbCIjsryxWOyhldC2YBAwH2TfuSgcD43DKq6WPiRAmTYcgCwoIIICFgyICkABlARhpNXAimD2x8\/ZT+53\/65PsO++AjX\/\/YlH8T7Io5c+a8SUr5PSnlm8jfPen5uUm7YK0I+nBKGiPdlGnWNHHPfazNUsrrrLW\/eamCd+7cufvceuut31ZKnSGEkLXifqPtMBrqOg6XekpPm\/AdYg\/ceuutfxrye9nWD7nk\/Ye\/tX3Dyl9Psd0h2MAKwEoLZQlkEq7ZTWsALOCtrAUgWIKsdHEuBtPxTOw1uHgQ0ZWcLFmnR29k4BohYKFsDCIBNgytMtgStJeK+Rk3z5h36EW5r1+9\/OVqjWfMmJGXUr6PiP5TCNEyFKE0HBiHk5Ed6rEaC4EwDBFFETKZDKy1KzKZzJufeuqpxS\/Fazp\/\/vwDisXiDzOZzCuSsGOo8arjKfiXWHhfMpl4MwVr7YfWrl37qyF37239oBvf861bdGPb10sm1AIMGQDCAhqMSDI0uZkOlggM1z4oTJW9EtaC2NZJUWArvNFWu46FYO3Bb2AlwNaCJEPZEqbr3uz03tVnlhf\/5S9L33Hodx++8ANzX07pJqUUXvWqV71RKfVHIcSlRNQylvh0pGL74dy92scTdtvrPPfk8\/lPPvXUU0+91K7pueeeG8ydO\/dD1trfBkHwioToG23Z6WhJq6H4hMSTSRhuv2n8MZPJ\/GFYi749H3rRWcfOmbRl2bXTVM8rdFSCAhD5bqJQS8dRSZdiUtZCMIN9EpgMnC5W7Qf2Z0Sizs2DSiGY1+Ny84aTAU7WAkK6tsdQEEAZFEUWA9mWe7qChquWZI+65dSf\/bzvpWqRpZQ477zzpv\/1r389t1gsnmOM2aXWSg5f+TbYjRsuDhvpGLW6T8ysgyC4fPLkyV++\/\/77iy+l67pgwYLp5XL5I8z80SiKWhOSaqTZT9szdaGeBQZQ0cCK4zjO5XIfWr58+c+Gjbe398MvPeugN7YPbPpxy8DG2YE1iBVDExAa15tkhUBMDGUJgfYMl6iilbc2rw6gAluZYutDawKgjO8xtm4UqiH3qwFDBk5ri7Sr1xYig82UiaNc86\/jTPvvw0OOu3fmBVdseindZHvsscesxsbG1xWLxYX9\/f0nJdahtmk8zTqPVOY42htvOAHyVJP6PYcccsgZv\/3tb9e\/FK4nEeHkk0\/OP\/3006\/p7+8\/Wyl1alqRJHFjk9h+vGYjDwf6xAL77\/RP8+fPP+eee+5Zs8MsMOCkSd+z\/JYzpnWtubIh7mqwIoYWjpQiF8wiEgSyAsoCZP0Dyotbshgc77IfDi6Ep7NTAAagEwA7TsubbAUDCSslrGRoRCBhICWAiBFKCaMZJEN0WdXdpxoX66m7\/HSPt7zvj\/Smj23YmS3yueee27J06dI3Pvvssx9WSh3CzPm0SkPtgK7RxmcjTeurZ2mH6kqy1q4jonPWrl172xBAmfP888\/LJ554YsVYR7fsiMXM4nWve93hjz322EczmcwbiGhS7ZjS2uFn2xrzjtZC14xH7clkMu9+7rnnbh1pNvO4nNGNfKM88pQvfn1SadOnG0RBWI5BYDfYwWWKYOE6mQQ7K8zSulEsLGqssN3anSYv28G+WJNc\/Fx9ogIjcC2NACxZkDAAxWCyrkDEKhAkCAKWBAakLJQymaWmseX2\/mz7\/0065LTHp5zzhT6wedEtg7VWvvKVr5z73HPPnaaUOoWZDxJCNA5l\/UbDHA9HXA03y6fea9IMt9baAvjAunXrfok6O+Fxxx03ecWKFVdba\/c2xtx8yCGH\/PrWW299SghhXkggCyFw1VVXNX3ta187rLW19X1+kPmcdFpoqGs5lo1xLJviUGEPM\/\/m6KOPfs9NN900YigybszOknPfOiPX8dAVM6PC28LyABCUwX5+odAAoMBw3UssNIxyFVbS1gK4ymJVAAwHYGIB2MA9LmNYn18mC0iW1YKRpMZaGJ+6EgC83hYzhBtODIQKRSGxiXM9cePkPyHb\/FdqavjH89+956njhdS1w9125AqCAN\/61rdar7322sOCIDhx\/fr1J8VxXNFbSlI1Y90MRvP3sShRBkFQySszs1VKXdfQ0PChRKe45rhq1qxZnw2C4KvFYlEAQBiGyydNmnQjEd09efLkf9111109I1mZ7dkMgyDAnnvuOY2ZXx0EwUlr1649rampqblUKiEIggrrW8+9rVdhNhogjwa0tdc8YbqFEGuiKHrHunXr7hvVZxzPC7biq285OP\/YM7+aWujZF2YzdBAjBpDRgDASsNKRTdIgCg2kITfsexCJ5Wiq2omTTC79BBMAxGCpYYSuaGopi1QpJ1Xkedx+IBN\/G5wQZ8wgUogtgCBALIAChAmzuWfJBvcMIHx8k82vNPsctOzgr123kojGXYT8vvvuy1144YVz165du1sQBPtkMplX9PT0HC2EmJqWu0kXSWwrUIdzBUcjA1x78\/qyzHunTJly9v3337\/ViJTTTz9dPvPMM2esX7\/+u0KIKcnrElY3iqJNjY2N\/2Dme4vF4tPHH3\/8suuuu24FEW135RYzh29961t3e\/zxx3fPZrMH9vb2vlprfbRSKl\/bfJHEuSO59uNFYg33mNbatLe3f23x4sVfHe3I1XEFMAmBFe973Wsb1y\/7ySTqmhehC1oxpGaEEYG0K6LnwKIUGChbD8BVK7yVG8fCgZEAK0ylcCSJiZNn2yTnTECgvQfgU9Mmqe4igiTpJIK0gcxI5+8zAWhBicNyUaq+fsiNsim\/CrnsP1Su8REDPN8ya27vM0+t6Q9OfnVhyU1LDAAsXrCAAeDiiy9hoovooouAJUuWUEdHB+VyOfmBD3wgKJfLTX+946+tDz\/68PzIRsdu3LjxCCKamcvlphljWhKBtIRESb7stKrjSC7YaAA8mhzxcFpOnqVdns\/nz1+2bNmf6x1n9uzZrxgYGPgfpdRuiaVLBm+njxdFUZzL5bqNMR3GmJVKqfvb2toefMc73vH87rvv3vPEE08Ub7vttqhQKOiWlhbb2NjI\/f391NPTI\/L5vHrDG96g9ttvv\/zSpUubb7311tnlcvmonp6eY6SU84MgmKK1bgdAiZBBGIaVc0mXlo42FBnvGDi9oZTL5TsnTZr0gSVLljw\/asztAMcFz7z\/VW9q3fjM96fInvmRLYABSCOgYulCXGmhpXHth1YMCeC6MUiFkRa+PNPVTUsP5AqAQWDh2GppvOUV7rEKSECQ7BVAGJChgLXkvQUAGQVDDAoFYmthLCLN1BmR2lzkYL1tbO0qyGzhtysHem9ep3v7IrC2GlCKSAYKQIMlbtJaNxJRg43NtEwYTrfWTi6VyqEKVMUKJJYpsbRJXJYk9+sVE4xGDG1b3M6hjuuVJUFEa3K53EeWL19eN0c5ffr0uXEc\/3cYhm9IWu+8+1zRyEo+c9oSJmFCEASlIAi6yuXyRmNMJxEN+L+VhRCxMUZprTNa6zCO4wYhRLvfBCczczZp2ki6r5L3SvfbJv9OHq8NT3ZUl9FQm68x5jlr7Qc2bNhwz9jQtmOiD2z64GFnZtcv+16j7pkcSwZDQGnhSCy27odQKadkJDpZ5P+eTiNxhciqgBQEy8rF2QQo2IoLTUyVmNdKAyYDC1HtmvLVYcxuRjEFwp0SJNgyiDRIAbEBVEaBLUP4GVAwrvEiCjKIwgxK2UZcvbgHN6xTKAo\/w4YITMpZUZ8Ws8ZAgKB1DCkFhFCD4q+RRlLWq78dCwk0mtzucDduUrDBzF1Syo+sWbPm+nrPO\/TQQ\/Pr16+\/nJk\/mJ5umI4zh7PuyQSC2sKHJJ5NHjPGwBgz6BqGYTio2yr5POkNMAFzemMZ6rOnSyi3hXsYKhZOP+7PfUBr\/eH169f\/arSuc+V72UFEPbr2P+o3HQ2tn9+Sad5iZQgnTasAK8CkwIKcvpZlkHUBLAvXrABmrx0vYISAFY58Is9osweTYAtysPPTHTglu2X9sSysm0KMpOeJmJ0wXyLJZSzYWgi2kNJrW1tAgsDGgI0BGQuyDKMNBGlkUUYYFRAVBlCKIhRijTI7y29B1RssimGiGGyTeFYBoEEjI9PCZekvOF27nHS\/jNQEPlSN81AqoUOpaaRv3ASAQRB05HK5Lxx99NE3D5HyCjZs2PBZZj4zLTmTAC59Tsmx601zSCxnmgew1qJcLqNcLiOKosrxErc8AWgURYPAUyssn65lrq1pHupajLX9r\/Z16Wuf\/kyJRxPH8S\/b29t\/PVbwOmp4B609L7iizMz\/s+R9B+xe7Fx\/4S4yUhwVASlhowgIBYRxVjMhkKs216lxJM0QIrGunlyustYG0ld+VHZ3MLhiigEQQybpKfIzyzFIm97dpOSO54JiAIag4GtSPdNtmSACQAgGWY2ACKwZZc2ILKANwIK8vO7gKQVg9oMcE4DyVrt7+guuBfJYLe5oijKGIqoSOZxMJpO++a0x5ucrV668st6Nxsy0YMGCd0gpLwCQq2eJhis0Sb937Yyh4byH9DETq1p7veo1cIzWIxmJABzu9fU+axzHlU3HW+C7wzD81pIlS6JtwdkOA7A\/cc3MX1z\/gWMGOjcs\/WJ7DhmUS5AZAZtMdCAgVm43VdZXW\/n66cAIN8GQRCUlNJJwXiVdnI4RaKznjSqDPeiPicVwJ06WYWFgja2EAPW+2NqbZjSVPTsqBhuOTU2sRnoeT+K6W2u\/0dbWdulQVmLOnDlvMcZ8VwjRopRKYuURP+tQeed612wsKZyhXPWRNrOxpoPGGv\/68aDJtX0uk8l8fuXKlSu3Ob\/9AhQmGPmGMy\/rbZt5yRZu2KyVRGwtYDVMQIiVcASXId8PDCi2UNaAoF13EglYFmD2sSpj2OKppA3RU+Mg\/1NX1tZL2zK7ecVsCYKSWDyFYrYg\/z\/XQ5FyYwnuPUBbTTuoZRqHulnHoupQ78YYreD6cK9NXOUk7wwApVKpYIz5ZSaT+caTTz5Zd6bRnnvu+Vpr7cUApsRxXFGsGEnBY7Qew0gzhWo\/x3CAH2k0ar3rNdoB3SNNXUj0rfzfn2Pmj69cufLB7SpQeSGKFKYt\/Gj\/vGuf\/Jaed\/AF3bnWDaQEmBgRLIxgSCsgfIUlWUAYhrQWVmoYqV1tlg1BOgB8MchIN0S6LiThzBIpPYAcoBNpPVuzITBS7Cgqj1OKsXYzoAhSSEipILwc0GiZ4dECa3sZz3oxcTomq2eRktxzHMdlZr48DMMLli5d2lfvPc4888y9mPm7RHRQOs+bzEUa6dxGc+6jLUhJhyKj6aQaL+s6vPLq4MyC5zPW7bvvvp9at27dLQC2q4pF4QVaRGRAdN2j79hXmJj+c1I4MFtFRQiyYEkgIwHNrqVIus5+Z\/882QFZsXCWkBqitrW3O8idTuVPh4p9aKvHvAgBO1KsktVKW2Ty\/yZ3XmAGW0+iDeOqjTXeGs4lHOtNNZrnCSGQyWRQKpVia+039thjj28O1V20yy677H7PPff80BhzQOJql8tlZLPZuuAbqdZ6NOc+2uNtS9HKeLnQ9c4huR5xHJeklP95zz33\/HY8cCXwQi5mHHzDkms2tc394HrZ\/rBWWShOcrkASMEygYWCdgOJIQ15tjkCKPIJpMrhxnRzjpVJT2N1qyMlrrk\/EVuz247GdRvLzTYeN17aIqTn1yaPp1ItWxoaGi7Za6+9vjMUeKdNmzavXC5\/3Vr72sTlZmZks9lBx9+e1NdowoVtIfJG4w0Ml+oa7czktEfjvZLeMAy\/fPjhh\/88PUt4+xK2L8oirPrMO\/alZ++9pN30viUTx4GMHfvLRLBKwliL0ALEBmCDhJSu2Q+2+iS1oBktSTHISgsG+4oPI6xrSYTfYMAgMiAQYgqwXrbgiqUDuHaDQhxkIVNdLPWGXY\/kBm6PxRjrzVzL2Pr65ucbGxu\/sXTp0ivTQ7TSa9asWXv09\/dfGobhW4ZSq9jB3ty4vX40se5Q4K7nFSWbWXqmr0+pbWbmS7PZ7OWrVq0qjde1EC8OgBlzv\/Xrxav2evU5m1rn\/9cm21ZmkQcUw0oN5giBACAkmFzca1NVVJUsUQ3bTNuwI9WdJmdp0K7AgB9mnI6RuXIeVS6Lxi2W3R4yazhiJb2SnGkqHn7owAMPfP8zzzxz1VDgPfjgg3cvlUpXK6XekuR3t2c0y3htRuP5+tGK2g\/l6aSrvHyhRpe19gunnHLKd8cTvC+iBR70geUz737lwqaBdZ9r564DlO7x4BCAUYAlEFtopcGCEWjeKlVUx\/ndht2Yao7gSC4jNIjcvCYmCUuAgAYBiCnAOtWCHzw9gGs7Augg5xjsUaRPxsuiDFXdVNNfOug9kvLMMAxRLpehtY6z2ewtcRz\/x+bNm4ec37vXXnsdViqVLi0Wi8clRNeQXswInk89T2NbSz9fzE2j3udPCkuMMbDWPsHMF3d0dPx+ewmrncgCDya39rruH9eb3Q8+qyNo+30XNZpYBojIgt1gFRhhYSDhZnzTICs81K40uHp66CEvzC6FNIih9sevpi6qPjtZHpTGEjw+Mdj23FQjxby1z0mXE+bz+S25XO5SZv7QUOAlIuy6667HFgqF75VKpePSg7vHk9EdTyu+LWNPxhprD8d2+5+\/ZbPZD3V0dPx2R4B3pwBwAozZl978RMdBB55fnLrXt3q4vTdjcpDWgoUBlIEiA2Vd7XKaZa4FMld14cf0JW01iDydAvaVXvVNOw051HmoG+nFuuHS+V6f8tk8e\/bsz\/\/kJz+5ZMOGDUNKDM2ZM2efOI6vMsYcK4TYSuhtpA6msX6msZJP2zJhYqThe8ORW7W56dp7SCmFUql0e0tLyzmrVq36545lk3aydffdd6vdfvLpg\/Pdm76cQe+b8qqfmGNIA8AQ2PJW\/jKJtLtY8+mSboLk6VQH9L5ci0F+4BpDEoFJwDg1Avf+JLyInoHwLvQG1YYrnhnAtR2OxBIYurpoW92+4ciVWhe6tlxxCEBoIcTPwjC8bOXKlUuHqcEVkyZNOgfAF6WUs5Nuotoe2m2RnRmJrBvpOCNd47G0BtYjtUaTuqqtYPOPbwbwvTiOf9TT09O1o\/EidjYAH3\/88XrOtQ89uHjWIed1tM3\/4sbMjMUF2ezaA31W2JJw\/ggRrBCwSe00Un383iMWAAQTBBiCfBMDD\/4RLEAQAElYoSCEE6xl31RBqSKOxANI0sIsGNbL47IduvBgOHXDert9vefX7vbpv1EN+117gycuLzMvJqLPaa0vXLVq1dP1wEtEePe73z116tSpXzTG\/Aczz7bWVsojE\/CmN4x6UwqHYr7rzSROjjnStMPRWth0Vdlor\/1wWlW1qbGEwU8dk3O53ANRFH30+OOP\/8YLAd6d0gLXrpUXvmUBOp79VGOh760Nha7WwBaBBK7MICHB2viqSIIl1wDhQJdI80jf4OC2gASQAl6oA8JJ3JLwqpjGy\/gQLPnuJQswKc9taRADsQywNmjH95\/qwXUbFXSQgxTbrp80GmsxXLthwginLWSqla9QKpV+m8\/nv7FmzZpFw73vueeem7\/zzju\/3t3d\/fGkY8Z3I20FwnpWa7gNbKjPsK1id2MROdjW+Do9kSG9IaaIqoFMJnP9iSee+B8\/+9nPVr2Q+JA7O4C\/98+lm97++rfc1RY23dUXCQKZBaEiJQUBxoCI4XEHgpuYCCgIFhDWNUYYQZViqkpRFSqDEJG0OjknulooAiZYn00RFa3banBsSKGXsnhgc4xFAwJGSl+gNXzHzfa4hvVuxGRmTwKSmjlF3NDQcOfUqVM\/09XV9cONGzeuHukcGhsb+bnnntsAIKO13kdKqdICA0muMy27OhQga9357Z1mMFYXfVsLSYZ6n0SEIMXA\/zOfz3+8v7\/\/yvvuu6\/jBSeB8RJaa\/\/wh7y948cnYcvKD2WiLa+cwj1ZM1B2HUs2Ua8MKnla11xvUM3ZVvR0PHvsRr9UCkT8KCcnn+UqR4x0zrvwfcwQDIsYgoBYZLFWteL7T\/fh+g4alEbaHquyLZYpNY6jokbBzCuCILiivb39+qeeemrDWM9lwYIFjRs2bHibMeYcIjoml8uRMQZBEKBQKCSFH6MGwkidRdsLru0lx2rDn9pNJ8mb+2u9saWl5ZfHHnvsf19zzTUrXrQsDl6C65mvv39K35LHTptse05tLPcf1VLqbZVx2aGPkmoPf+MnzHVieoWfsuZdbpBr+E+Kq6vMtivlrFRiWaey4Vz1GMSEWGSwPmzD95f24foOgTjIQNbcAEOVVo4fgc+D1C6IyCqlnomi6E4A13R0dNy\/ve8xZcqU6WEYftha+wYAB5VKJVUrKj9W0bfxuhZjaUPcFkufPk4ul0OhUNgSx\/E9mUzm5x0dHbe+2BrXL+GBQQKLvvPtdvXob16V6d34vlbbd0IzCg3g2M1c8m1Ews9Wg015v+Ri3oQIc51KnPQmuTjZXxnjuwodC+0KOWzCQksH4B8s7cf1myRilUUSAteKro+HRRmqPDP5mzFmDRFdud9++\/3vXXfd9QyAcZvNK4TA\/vvvP6+rq2thf3\/\/+cw8L00+1WOla3PFQ7VXjhTD1gPjaEtVtzU0Sf+eSOhaa+8xxlzW1tZ2z7Jly3p3BhS8LCZ+PfFf\/9UWrv\/Hvi2FLSfy5lWnZsq9C1qEVawjB0YBCG3BVkGS8MPIJaoVG77riBjWGOdDJzlgH1+LpK1QElg4Ei0WCutVO654th\/Xb1Ioy4yLq+tI5NQTRk8NsarMxEn\/LSGl0lMYElIqjuNkhk5krf1nEAQ3z5gx485TTjnl6UsuuWRHClqL6dOn710ul48D8E5mPiqTyYS1qhi1QnzpIWhpYii5DvWY89EQUcMBWClVUcBI1yWnhQrS301abC+O4+S76G5ubv6\/TCZz04wZM+6\/6667Nu5M9\/7LZ2RfEid\/49w5nQ\/de1qrLpykEB8R6t62Zi5AQQJGAsXIBbsqABsDBOS1swiCCMZokLTVjkLpCCxhfB+wIDAxBAFaBNgQtOH7S\/txbYdCpDKD8sBDxbFpbabkZvJgTKb8bcXyJuAOggBRFCEMw43W2n+Wy+XbiOiGzs7OF9wiNDQ0TFNKvaG5ufltxWLxCCHE1ERdMpPJVBr704Ox642DSadl6gF3pEKZoUCcXK\/0eyfzkpPfk3NLP88TgesA3N7b2\/u\/vb29tw1VGz4B4B30sdb\/8hcNW\/7y\/dc2FzuOzpd7Dwws9s7AzM9YDWtiaKNB0vmY1gAhJBQJWMvOX\/bVXEZyBcBMgBDOyRYAbBBgg2rF957uxzUbq4UctTdiPQCnpUzTVigFUCRaxkn6BkBnHMeLhRAPT5o06bannnrqbiGEfjHjMCLCOeecE9xxxx3H9\/f3vzmKogOUUgdKKVtqGepEkSIBSVq0b6g8bT1iqV4l1XCgTwO39lzS342UsgfAo0KI+xcsWHDbnXfeeR8RmZ37Tn+ZL2YWSy98c3vQuX5Ga0D79vf3ni5LPYeG5Z6pORHlsgJOXM8AZAgQEpYI1ndLGKEhmB2mrdOWJnLElpGuEuuyJX24pkNUWOha65KWVU0XGqQn20spBz0nKZqQUm5m5uWTJk36jbX2b8Vicc20adM6Hn744Xhnu9YLFiwIN27cOLlcLs+fNGnSKwuFwmnMPJ+IJidhQjabrWxW6VTUSITXUOAeavNKT\/pL3PZ0eJJavVEUrZgyZcqtxWLxdiJaduSRR3bcdNNN5qVhqv6tFuGxb3+rIbfm9jnFTWt2a7aF+VzuOzQ08SE5Gc5AKW4OrQ2ywlLgGW0W2ulhGRcQW7YANAQALRXWyVZ896l+XLNRQAcZBEpulQNNaz4nViftrqUAHFtru4lopTHmYSnlvxYsWLC0tbV12e9+97uOsbKqL+qVJsKcOXOmFwqFXYloP2Y+EsBhSqnZcRw3WWtVuiliLKz0aNQ7Epc5KUTxbjNLKQeMMRuCIHiUiO458sgjn3r66aeXL1my5PltkXWdAPBOcAkuYive97m3H1hcveKwhrh3Tls8MCc08UxmarVSN4O5VVhuFpBZBQOri16zK8A62YLLlvTglxuAOMggEyioQMFarqvr5AE7QESd1tpeZu5qbW3dsHHjxuebm5uXL1iw4G933XXXkztq4NeL7Q2dddZZR91yyy2vtNbOCcNwrpRyKjO3EFEbM7fByzzVY5jT1zI9SSG9MaZe009EXcaYzqlTp27esmXLumKxuGqvvfZ65Nhjj73ziiuu6H153L0Tq4a6DPCHL34hv2vvkmzY15fP6u42w32TQm3aRGRaWFB7MSpOMzqaTmF2UqcVbT97vCN78\/qSLIsAUkiWUlqhMhEIhTCTKUyeNKnTGLOht7d7TbFY7slms91hGG6y1m6ZPn1695lnnlm45557ii8Vt2283O2BgYF8GIYNxph2pdTUzs7OyUqp1ubm5qlENKOrq2uatbYZQKMxJtRaC2YmpRQTEUspLTMXhRC906ZN28zM6\/r7+zf29fV1trS0dGUymU2FQqHj05\/+dM+TTz5ZvPrqqwsvFQ9mAsA78pKxpRsvXtjQVo4zav0zwa+f3pK9Y3lX2FfOMEWRjbIwTU1T4jAM46lTp+pLLvlx+XWvO7AgBL3c7p8ds4cqhcceeyx85StfmYuiKNPa2hr29\/erUqkkAIhMJgMiskIILaWMoygqX3PNNaU3v\/nNRSklvxy9l6HW\/weMXz5ac7FxWwAAAABJRU5ErkJggg==\" alt=\"Onground Media logo\"><\/span>\r\n      <span class=\"brand-name\">Onground<span> Media<\/span><\/span>\r\n    <\/a>\r\n    <nav class=\"top-nav\" aria-label=\"Main\">\r\n      <a class=\"nav-link\" href=\"#\" id=\"allToolsLink\">All tools<\/a>\r\n      <button class=\"icon-btn\" id=\"themeBtn\" type=\"button\" aria-label=\"Switch between light and dark theme\" title=\"Switch theme\">\r\n        <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"8\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"\/><path d=\"M12 4a8 8 0 0 1 0 16z\" fill=\"currentColor\"\/><\/svg>\r\n      <\/button>\r\n    <\/nav>\r\n  <\/div>\r\n<\/header>\r\n\r\n<main>\r\n  <section class=\"hero\" id=\"hero\">\r\n    <div class=\"wrap hero-in\">\r\n      <p class=\"eyebrow\">Onground Media \u00b7 Technical SEO<\/p>\r\n      <h1>26 technical SEO tools, <em>one search away.<\/em><\/h1>\r\n      <p class=\"lede\">Generate robots.txt, sitemaps, canonical, hreflang and redirect rules. Test and validate what you already have. Everything runs in your browser.<\/p>\r\n      <label class=\"search\" for=\"q\">\r\n        <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\"><circle cx=\"11\" cy=\"11\" r=\"6\"\/><path d=\"M20 20l-4.5-4.5\"\/><\/svg>\r\n        <input id=\"q\" type=\"text\" placeholder=\"Search tools, for example sitemap, redirect, SSL\" autocomplete=\"off\" aria-label=\"Search tools\">\r\n        <kbd aria-hidden=\"true\">\/<\/kbd>\r\n      <\/label>\r\n      <div class=\"hero-key\" aria-label=\"What the labels mean\">\r\n        <span><span class=\"tag here\"><i><\/i>Runs here<\/span> Works fully on this page<\/span>\r\n        <span><span class=\"tag paste\"><i><\/i>Paste in<\/span> Analyzes data you paste<\/span>\r\n        <span><span class=\"tag link\"><i><\/i>Opens checker<\/span> Opens a trusted site, URL pre-filled<\/span>\r\n      <\/div>\r\n    <\/div>\r\n  <\/section>\r\n\r\n  <section class=\"wrap dir\" id=\"dir\">\r\n    <div class=\"cats\" id=\"cats\" role=\"group\" aria-label=\"Filter by category\"><\/div>\r\n    <div id=\"groups\"><\/div>\r\n  <\/section>\r\n\r\n  <section class=\"wrap bench\" id=\"bench\" hidden><\/section>\r\n<\/main>\r\n\r\n<footer>\r\n  <div class=\"wrap foot\">\r\n    <span>\u00a9 2026 Onground Media<\/span>\r\n    <span>Tools run in your browser. Pasted data is not uploaded or stored.<\/span>\r\n  <\/div>\r\n<\/footer>\r\n<script>\r\n(function(){\r\n\"use strict\";\r\n\/* ===== core ===== *\/\r\nvar $=function(s,r){return (r||document).querySelector(s)};\r\nvar $$=function(s,r){return Array.prototype.slice.call((r||document).querySelectorAll(s))};\r\nfunction esc(s){return String(s).replace(\/&\/g,'&amp;').replace(\/<\/g,'&lt;').replace(\/>\/g,'&gt;').replace(\/\"\/g,'&quot;')}\r\nfunction pill(el,kind,text){el.className='pill '+kind;el.textContent=text}\r\nvar LVL={bad:'Error',warn:'Warning',good:'OK',idle:'Info'};\r\nfunction issues(list){\r\n  if(!list.length)return '<p class=\"hint\">Nothing to report.<\/p>';\r\n  return '<ul class=\"issues\">'+list.map(function(i){return '<li><span class=\"pill '+i.l+'\">'+LVL[i.l]+'<\/span><span><b>'+esc(i.t)+'<\/b>'+(i.d?'<small>'+esc(i.d)+'<\/small>':'')+'<\/span><\/li>'}).join('')+'<\/ul>';\r\n}\r\nfunction summary(list){\r\n  var e=list.filter(function(i){return i.l==='bad'}).length,w=list.filter(function(i){return i.l==='warn'}).length;\r\n  return e?['bad',e+' error'+(e>1?'s':'')+(w?' and '+w+' warning'+(w>1?'s':''):'')]:w?['warn',w+' warning'+(w>1?'s':'')+', no errors']:['good','No problems found'];\r\n}\r\nfunction copyText(text,btn,fb){\r\n  var label=btn.getAttribute('data-label')||btn.textContent;btn.setAttribute('data-label',label);\r\n  function done(ok){btn.textContent=ok?'Copied':'Select the text and press Ctrl+C';if(!ok&&fb){fb.focus();fb.select()}setTimeout(function(){btn.textContent=label},1800)}\r\n  function legacy(){var ok=false;try{if(fb){fb.focus();fb.select();ok=document.execCommand('copy')}}catch(e){}done(ok)}\r\n  if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(text).then(function(){done(true)},legacy)}else legacy();\r\n}\r\nfunction wireCopy(root,btnSel,srcSel){var b=$(btnSel,root);if(b)b.addEventListener('click',function(){var s=$(srcSel,root);if(s&&s.value)copyText(s.value,b,s)})}\r\nfunction today(){return new Date().toISOString().slice(0,10)}\r\nfunction bytes(n){return n<1024?n+' B':n<1048576?(n\/1024).toFixed(1)+' KB':(n\/1048576).toFixed(2)+' MB'}\r\nfunction enc(s){return encodeURIComponent(s)}\r\nfunction normUrl(s,base){s=(s||'').trim();if(!s)return null;try{return new URL(\/^[a-z][a-z0-9+.-]*:\/i.test(s)?s:'https:\/\/'+s)}catch(e){return null}}\r\nfunction hostOf(s){var u=normUrl(s);return u?u.hostname:''}\r\nfunction linkGrid(arr){return '<div class=\"links\">'+arr.map(function(l){return '<a href=\"'+esc(l.h)+'\" target=\"_blank\" rel=\"noopener noreferrer\">'+esc(l.t)+(l.s?'<small>'+esc(l.s)+'<\/small>':'')+'<\/a>'}).join('')+'<\/div>'}\r\nfunction cmdBox(cmd,id){return '<div class=\"out-box\"><label class=\"lbl\" for=\"'+id+'\">Run this command<\/label><textarea id=\"'+id+'\" class=\"code\" rows=\"2\" readonly>'+esc(cmd)+'<\/textarea><\/div>'}\r\n\r\n\/* ===== registry ===== *\/\r\nvar TOOLS=[],CATS=[\r\n  {id:'crawl',n:'Crawling and indexing',d:'Control what search engines can reach and index.'},\r\n  {id:'tags',n:'Tags and redirects',d:'Canonical, hreflang and redirect rules.'},\r\n  {id:'server',n:'Server and security',d:'Status codes, headers, SSL, DNS and hosting.'},\r\n  {id:'speed',n:'Speed and mobile',d:'Performance, page weight and compression.'},\r\n  {id:'code',n:'Code and detection',d:'Validate markup, shrink files and spot technology.'}\r\n];\r\nfunction tool(o){TOOLS.push(o)}\r\nvar MODES={here:'Runs here',paste:'Paste in',link:'Opens checker'};\r\nvar MODENOTE={\r\n  here:'This tool runs entirely on this page.',\r\n  paste:'A published page cannot fetch other websites, so paste the data below. It is analyzed in your browser and not uploaded.',\r\n  link:'A published page cannot query other websites. This tool opens a trusted checker in a new tab with your details pre-filled.'\r\n};\r\n\r\n\/* ===== directory ===== *\/\r\nvar state={cat:'all',q:''};\r\nfunction matches(t){\r\n  if(state.cat!=='all'&&t.cat!==state.cat)return false;\r\n  if(!state.q)return true;\r\n  var hay=(t.name+' '+t.desc+' '+(t.kw||'')).toLowerCase();\r\n  return state.q.toLowerCase().split(\/\\s+\/).every(function(w){return hay.indexOf(w)>=0});\r\n}\r\nfunction renderDir(){\r\n  var cats=$('#cats');\r\n  cats.innerHTML='<button type=\"button\" data-c=\"all\" aria-pressed=\"'+(state.cat==='all')+'\">All<small>'+TOOLS.length+'<\/small><\/button>'+CATS.map(function(c){\r\n    var n=TOOLS.filter(function(t){return t.cat===c.id}).length;\r\n    return '<button type=\"button\" data-c=\"'+c.id+'\" aria-pressed=\"'+(state.cat===c.id)+'\">'+c.n+'<small>'+n+'<\/small><\/button>';\r\n  }).join('');\r\n  var out='',any=false;\r\n  CATS.forEach(function(c){\r\n    var list=TOOLS.filter(function(t){return t.cat===c.id&&matches(t)});\r\n    if(!list.length)return;any=true;\r\n    out+='<section class=\"group\"><h2>'+c.n+' <span>'+c.d+'<\/span><\/h2><div class=\"grid\">'+list.map(function(t){\r\n      return '<button type=\"button\" class=\"card\" data-t=\"'+t.id+'\"><span class=\"tag '+t.mode+'\"><i><\/i>'+MODES[t.mode]+'<\/span><h3>'+esc(t.name)+'<\/h3><p>'+esc(t.desc)+'<\/p><\/button>';\r\n    }).join('')+'<\/div><\/section>';\r\n  });\r\n  $('#groups').innerHTML=any?out:'<div class=\"empty\">No tool matches \u201c'+esc(state.q)+'\u201d. Try a shorter word such as robots, redirect or speed.<\/div>';\r\n}\r\n$('#cats').addEventListener('click',function(e){var b=e.target.closest('button[data-c]');if(!b)return;state.cat=b.getAttribute('data-c');renderDir()});\r\n$('#groups').addEventListener('click',function(e){var b=e.target.closest('[data-t]');if(b)openTool(b.getAttribute('data-t'),true)});\r\n$('#q').addEventListener('input',function(){state.q=this.value.trim();renderDir()});\r\ndocument.addEventListener('keydown',function(e){\r\n  if(e.key==='\/'&&!\/^(INPUT|TEXTAREA|SELECT)$\/.test(document.activeElement.tagName)&&!$('#hero').hidden){e.preventDefault();$('#q').focus()}\r\n});\r\n\r\n\/* ===== workbench ===== *\/\r\nfunction openTool(id,push){\r\n  var t=TOOLS.filter(function(x){return x.id===id})[0];\r\n  if(!t){showDir(false);return}\r\n  $('#hero').hidden=true;$('#dir').hidden=true;\r\n  var b=$('#bench');b.hidden=false;\r\n  b.innerHTML='<div class=\"crumbs\"><button type=\"button\" class=\"back\" id=\"backBtn\">\u2190 All tools<\/button><span class=\"tag '+t.mode+'\"><i><\/i>'+MODES[t.mode]+'<\/span><\/div>'+\r\n    '<article class=\"panel\"><div class=\"panel-head\"><h2>'+esc(t.name)+'<\/h2><p>'+esc(t.desc)+'<\/p><\/div><p class=\"mode-note\">'+MODENOTE[t.mode]+'<\/p><div id=\"toolbody\">'+t.tpl+'<\/div>'+(t.note?'<p class=\"note\">'+t.note+'<\/p>':'')+'<\/article>';\r\n  $('#backBtn').addEventListener('click',function(){showDir(true)});\r\n  try{t.init($('#toolbody'))}catch(err){$('#toolbody').insertAdjacentHTML('afterbegin','<p class=\"pill bad\">This tool failed to load. Refresh the page and try again.<\/p>');if(window.console)console.error(t.id,err)}\r\n  try{history.replaceState(null,'','#'+id)}catch(e){}\r\n  if(push)window.scrollTo(0,0);\r\n}\r\nfunction showDir(push){\r\n  $('#bench').hidden=true;$('#bench').innerHTML='';$('#hero').hidden=false;$('#dir').hidden=false;\r\n  try{history.replaceState(null,'',location.pathname+location.search)}catch(e){}\r\n  if(push)window.scrollTo(0,0);\r\n}\r\n$('#homeLink').addEventListener('click',function(e){e.preventDefault();showDir(true)});\r\n$('#allToolsLink').addEventListener('click',function(e){e.preventDefault();showDir(true);$('#dir').scrollIntoView()});\r\nwindow.addEventListener('hashchange',function(){var id=location.hash.slice(1);if(id)openTool(id,true);else showDir(true)});\r\n$('#themeBtn').addEventListener('click',function(){\r\n  var root=document.documentElement,cur=root.getAttribute('data-theme');\r\n  var dark=cur==='dark'||(!cur&&matchMedia('(prefers-color-scheme: dark)').matches);\r\n  var next=dark?'light':'dark';root.setAttribute('data-theme',next);\r\n  try{localStorage.setItem('og-theme',next)}catch(e){}\r\n});\r\n\r\n\/* ===== minimal X.509 reader ===== *\/\r\nfunction derRead(b,p){\r\n  var tag=b[p],l=b[p+1],hdr=2;\r\n  if(l&128){var n=l&127;l=0;for(var i=0;i<n;i++)l=l*256+b[p+2+i];hdr=2+n}\r\n  return {tag:tag,s:p+hdr,e:p+hdr+l,hdr:hdr};\r\n}\r\nfunction derKids(b,n){var out=[],p=n.s;while(p<n.e){var k=derRead(b,p);out.push(k);p=k.e}return out}\r\nfunction derOid(b,n){\r\n  var o=[Math.floor(b[n.s]\/40),b[n.s]%40],v=0;\r\n  for(var i=n.s+1;i<n.e;i++){v=v*128+(b[i]&127);if(!(b[i]&128)){o.push(v);v=0}}\r\n  return o.join('.');\r\n}\r\nfunction derStr(b,n){var s='';for(var i=n.s;i<n.e;i++)s+=String.fromCharCode(b[i]);try{return decodeURIComponent(escape(s))}catch(e){return s}}\r\nfunction derTime(b,n){\r\n  var s=derStr(b,n),m;\r\n  if(n.tag===0x17){m=s.match(\/^(\\d\\d)(\\d\\d)(\\d\\d)(\\d\\d)(\\d\\d)(\\d\\d)?Z$\/);if(!m)return null;var y=+m[1];y+=y<50?2000:1900;return new Date(Date.UTC(y,m[2]-1,m[3],m[4],m[5],m[6]||0))}\r\n  m=s.match(\/^(\\d{4})(\\d\\d)(\\d\\d)(\\d\\d)(\\d\\d)(\\d\\d)?Z$\/);return m?new Date(Date.UTC(+m[1],m[2]-1,m[3],m[4],m[5],m[6]||0)):null;\r\n}\r\nfunction derName(b,n){\r\n  var r={};\r\n  derKids(b,n).forEach(function(set){derKids(b,set).forEach(function(seq){\r\n    var k=derKids(b,seq);if(k.length<2)return;var oid=derOid(b,k[0]);\r\n    if(oid==='2.5.4.3')r.cn=derStr(b,k[1]);if(oid==='2.5.4.10')r.o=derStr(b,k[1]);\r\n  })});\r\n  return r;\r\n}\r\nfunction parseCert(pem){\r\n  var m=String(pem).match(\/-----BEGIN CERTIFICATE-----([\\s\\S]*?)-----END CERTIFICATE-----\/);\r\n  if(!m)throw new Error('No certificate block found. It should start with -----BEGIN CERTIFICATE-----.');\r\n  var bin=atob(m[1].replace(\/\\s+\/g,'')),b=new Uint8Array(bin.length);\r\n  for(var i=0;i<bin.length;i++)b[i]=bin.charCodeAt(i);\r\n  var cert=derRead(b,0),tbs=derKids(b,cert)[0],k=derKids(b,tbs),i0=0;\r\n  if(k[0].tag===0xA0)i0=1;\r\n  var issuerN=k[i0+2],validity=derKids(b,k[i0+3]),subjectN=k[i0+4],spki=k[i0+5];\r\n  var issuer=derName(b,issuerN),subject=derName(b,subjectN);\r\n  var same=issuerN.e-issuerN.s===subjectN.e-subjectN.s;\r\n  if(same)for(var j=0;j<issuerN.e-issuerN.s;j++)if(b[issuerN.s+j]!==b[subjectN.s+j]){same=false;break}\r\n  var alg=derOid(b,derKids(b,derKids(b,spki)[0])[0]),sans=[];\r\n  k.forEach(function(x){\r\n    if(x.tag!==0xA3)return;\r\n    derKids(b,derKids(b,x)[0]).forEach(function(ext){\r\n      var ek=derKids(b,ext);if(derOid(b,ek[0])!=='2.5.29.17')return;\r\n      var oct=ek[ek.length-1];\r\n      derKids(b,derRead(b,oct.s)).forEach(function(g){if(g.tag===0x82)sans.push(derStr(b,g))});\r\n    });\r\n  });\r\n  return {subject:subject,issuer:issuer,from:derTime(b,validity[0]),to:derTime(b,validity[1]),selfSigned:same,sans:sans,\r\n    keyAlg:alg==='1.2.840.10045.2.1'?'ECDSA':alg==='1.2.840.113549.1.1.1'?'RSA':alg};\r\n}\r\n\r\n\/* ===== minifier engine (safe mode: keeps line breaks where they could change meaning) ===== *\/\r\nvar IDC=\/[A-Za-z0-9_$\\u00a0-\\uffff]\/;\r\nfunction skipStr(s,i){var q=s[i];i++;while(i<s.length){if(s[i]==='\\\\')i+=2;else if(s[i]===q)return i+1;else i++}return i}\r\nfunction skipTpl(s,i){ \/* i at opening backtick *\/\r\n  i++;\r\n  while(i<s.length){\r\n    var c=s[i];\r\n    if(c==='\\\\')i+=2;\r\n    else if(c==='`')return i+1;\r\n    else if(c==='$'&&s[i+1]==='{'){i=skipExpr(s,i+2)}\r\n    else i++;\r\n  }\r\n  return i;\r\n}\r\nfunction skipExpr(s,i){ \/* after ${ , returns index after matching } *\/\r\n  var d=1;\r\n  while(i<s.length&&d>0){\r\n    var c=s[i];\r\n    if(c==='\"'||c===\"'\")i=skipStr(s,i);\r\n    else if(c==='`')i=skipTpl(s,i);\r\n    else{if(c==='{')d++;else if(c==='}')d--;i++}\r\n  }\r\n  return i;\r\n}\r\nfunction skipRegex(s,i){\r\n  i++;var cls=false;\r\n  while(i<s.length){\r\n    var c=s[i];\r\n    if(c==='\\\\')i+=2;\r\n    else if(c==='\\n')return i;\r\n    else if(c==='['){cls=true;i++}\r\n    else if(c===']'){cls=false;i++}\r\n    else if(c==='\/'&&!cls){i++;while(\/[a-z]\/i.test(s[i]||''))i++;return i}\r\n    else i++;\r\n  }\r\n  return i;\r\n}\r\nvar KW_BEFORE_RE=\/(?:^|[^\\w$.])(return|typeof|case|do|else|in|of|void|delete|throw|new|yield|await)$\/;\r\nfunction regexAllowed(out){\r\n  var t=out.replace(\/\\s+$\/,''),p=t.slice(-1);\r\n  return !p||\/[(,=:[!&|?{};+\\-*%<>~^]\/.test(p)||KW_BEFORE_RE.test(t);\r\n}\r\nfunction stripJS(s){\r\n  var out='',i=0,n=s.length;\r\n  while(i<n){\r\n    var c=s[i],d=s[i+1];\r\n    if(c==='\"'||c===\"'\"){var e=skipStr(s,i);out+=s.slice(i,e);i=e;continue}\r\n    if(c==='`'){var e2=skipTpl(s,i);out+=s.slice(i,e2);i=e2;continue}\r\n    if(c==='\/'&&d==='*'){\r\n      var e3=s.indexOf('*\/',i+2);if(e3<0)e3=n-2;\r\n      var cm=s.slice(i,e3+2);\r\n      if(\/^\\\/\\*[!@]|@license|@preserve\/.test(cm))out+=cm;else out+=cm.indexOf('\\n')>=0?'\\n':' ';\r\n      i=e3+2;continue;\r\n    }\r\n    if(c==='\/'&&d==='\/'){while(i<n&&s[i]!=='\\n')i++;continue}\r\n    if(c==='\/'&&regexAllowed(out)){var e4=skipRegex(s,i);out+=s.slice(i,e4);i=e4;continue}\r\n    out+=c;i++;\r\n  }\r\n  return out;\r\n}\r\nfunction minJS(src){\r\n  var s=stripJS(String(src).replace(\/\\r\\n?\/g,'\\n')),out='',i=0,n=s.length;\r\n  while(i<n){\r\n    var c=s[i];\r\n    if(c==='\"'||c===\"'\"){var e=skipStr(s,i);out+=s.slice(i,e);i=e;continue}\r\n    if(c==='`'){var e2=skipTpl(s,i);out+=s.slice(i,e2);i=e2;continue}\r\n    if(c==='\/'&&regexAllowed(out)){var e4=skipRegex(s,i);out+=s.slice(i,e4);i=e4;continue}\r\n    if(\/\\s\/.test(c)){\r\n      var nl=false;while(i<n&&\/\\s\/.test(s[i])){if(s[i]==='\\n')nl=true;i++}\r\n      var nx=s[i]||'',pv=out.slice(-1);\r\n      if(!out||!nx)continue;\r\n      if(nl){\r\n        if(\/[{(\\[,;:=]\/.test(pv)||\/[})\\],;.:?]\/.test(nx)){}\r\n        else if(pv!=='\\n')out+='\\n';\r\n      }else if((IDC.test(pv)&&IDC.test(nx))||(pv==='+'&&nx==='+')||(pv==='-'&&nx==='-')||(\/\\d\/.test(pv)&&nx==='.')||(pv==='\/'&&nx==='\/')||(pv==='<'&&nx==='!'))out+=' ';\r\n      continue;\r\n    }\r\n    out+=c;i++;\r\n  }\r\n  return out.replace(\/^\\s+|\\s+$\/g,'');\r\n}\r\nfunction minCSS(src){\r\n  var s=String(src),o='',i=0,n=s.length;\r\n  while(i<n){\r\n    var c=s[i];\r\n    if(c==='\"'||c===\"'\"){var e=skipStr(s,i);o+=s.slice(i,e);i=e;continue}\r\n    if(c==='\/'&&s[i+1]==='*'){var e2=s.indexOf('*\/',i+2);if(e2<0)e2=n-2;var cm=s.slice(i,e2+2);if(cm[2]==='!')o+=cm;i=e2+2;continue}\r\n    o+=c;i++;\r\n  }\r\n  var parts=o.split(\/(\"(?:\\\\.|[^\"\\\\])*\"|'(?:\\\\.|[^'\\\\])*')\/);\r\n  for(var k=0;k<parts.length;k+=2){\r\n    parts[k]=parts[k].replace(\/\\s+\/g,' ').replace(\/\\s*([{};,>~])\\s*\/g,'$1').replace(\/:\\s+\/g,':').replace(\/\\s+:(?=[^{};]*[;}])\/g,':').replace(\/;}\/g,'}').replace(\/\\(\\s+\/g,'(').replace(\/\\s+\\)\/g,')');\r\n  }\r\n  return parts.join('').trim();\r\n}\r\nvar BLOCK=\/^(?:html|head|body|title|meta|link|script|style|base|div|p|ul|ol|li|dl|dt|dd|section|article|aside|header|footer|nav|main|h[1-6]|table|thead|tbody|tfoot|tr|td|th|caption|form|fieldset|legend|select|option|optgroup|blockquote|figure|figcaption|hr|br|address|details|summary|template|noscript|iframe)$\/i;\r\nfunction minHTML(src){\r\n  var s=String(src).replace(\/\\r\\n?\/g,'\\n');\r\n  var toks=s.split(\/(<\\!--[\\s\\S]*?-->|<(script|style|pre|textarea)\\b[^>]*>[\\s\\S]*?<\\\/\\2>|<[^>]+>)\/gi),out=[],parts=[];\r\n  \/* split() with two capture groups yields groups, rebuild list of {t,raw} *\/\r\n  var list=[];\r\n  for(var i=0;i<toks.length;i+=3){\r\n    if(toks[i])list.push({k:'text',v:toks[i]});\r\n    if(toks[i+1]!==undefined&&toks[i+1]!==''){list.push({k:'tag',v:toks[i+1],name:toks[i+2]})}\r\n  }\r\n  function nameOf(t){var m=t.match(\/^<\\\/?([a-zA-Z][\\w:-]*)\/);return m?m[1]:''}\r\n  var res=[];\r\n  list.forEach(function(x,idx){\r\n    if(x.k==='text'){res.push({k:'text',v:x.v});return}\r\n    var v=x.v;\r\n    if(\/^<\\!--\/.test(v)){if(\/^<\\!--\\[if|^<\\!--<!\\[endif|^<\\!--!\/i.test(v))res.push({k:'tag',v:v,n:''});return}\r\n    var nm=(x.name||nameOf(v)).toLowerCase();\r\n    if(x.name){\r\n      var m=v.match(\/^(<[^>]*>)([\\s\\S]*?)(<\\\/[^>]+>)$\/);\r\n      if(m){\r\n        var body=m[2];\r\n        if(nm==='style')body=minCSS(body);\r\n        else if(nm==='script'){\r\n          if(\/\\bsrc=\/i.test(m[1]))body=body.trim();\r\n          else if(\/type\\s*=\\s*[\"']?(?:application\\\/(?:ld\\+)?json|importmap)\/i.test(m[1])){try{body=JSON.stringify(JSON.parse(body))}catch(e){body=body.trim()}}\r\n          else if(\/type\\s*=\\s*[\"']?(?!module|text\\\/javascript|application\\\/javascript)\/i.test(m[1])&&\/type\\s*=\/i.test(m[1]))body=body;\r\n          else body=minJS(body);\r\n        }\r\n        v=m[1].replace(\/\\s+>\/,'>').replace(\/\\s{2,}\/g,' ')+body+m[3];\r\n      }\r\n      res.push({k:'tag',v:v,n:nm,raw:true});return;\r\n    }\r\n    res.push({k:'tag',v:v.replace(\/\\s*\\n\\s*\/g,' ').replace(\/\\s{2,}\/g,' ').replace(\/\\s+(\\\/?)>$\/,'$1>'),n:nm});\r\n  });\r\n  var o='';\r\n  res.forEach(function(x,i){\r\n    if(x.k==='tag'){o+=x.v;return}\r\n    var t=x.v.replace(\/\\s+\/g,' '),pv=res[i-1],nx=res[i+1];\r\n    var pb=!pv||(pv.k==='tag'&&(BLOCK.test(pv.n)||pv.n===''||pv.raw)),nb=!nx||(nx.k==='tag'&&(BLOCK.test(nx.n)||nx.n===''||nx.raw));\r\n    if(pb)t=t.replace(\/^ \/,'');if(nb)t=t.replace(\/ $\/,'');\r\n    o+=t;\r\n  });\r\n  return o.trim();\r\n}\r\n\r\n\/* ===== shared robots.txt engine ===== *\/\r\nfunction parseRobots(text){\r\n  var groups=[],cur=null,sitemaps=[],lastWasAgent=false;\r\n  String(text||'').split(\/\\r?\\n\/).forEach(function(raw){\r\n    var line=raw.replace(\/#.*$\/,'').trim();if(!line)return;\r\n    var i=line.indexOf(':');if(i<0)return;\r\n    var k=line.slice(0,i).trim().toLowerCase(),v=line.slice(i+1).trim();\r\n    if(k==='user-agent'){\r\n      if(!cur||!lastWasAgent){cur={agents:[],rules:[]};groups.push(cur)}\r\n      cur.agents.push(v.toLowerCase());lastWasAgent=true;\r\n    }else if(k==='allow'||k==='disallow'){\r\n      if(!cur){cur={agents:['*'],rules:[]};groups.push(cur)}\r\n      cur.rules.push({type:k,path:v});lastWasAgent=false;\r\n    }else if(k==='sitemap'){sitemaps.push(v)}else{lastWasAgent=false}\r\n  });\r\n  return {groups:groups,sitemaps:sitemaps};\r\n}\r\nfunction rulesFor(parsed,ua){\r\n  ua=ua.toLowerCase();var best=-1,rules=[],used='';\r\n  parsed.groups.forEach(function(g){\r\n    g.agents.forEach(function(a){\r\n      var score=a==='*'?0:(ua.indexOf(a)>=0?a.length:-1);\r\n      if(score<0)return;\r\n      if(score>best){best=score;rules=[];used=a}\r\n      if(score===best)rules=rules.concat(g.rules);\r\n    });\r\n  });\r\n  return {rules:rules,agent:used||'(none)'};\r\n}\r\nfunction patRe(p){\r\n  var end=\/\\$$\/.test(p);if(end)p=p.slice(0,-1);\r\n  return new RegExp('^'+p.replace(\/[.+?^${}()|[\\]\\\\]\/g,'\\\\$&').replace(\/\\*\/g,'.*')+(end?'$':''));\r\n}\r\nfunction robotsCheck(parsed,ua,path){\r\n  var r=rulesFor(parsed,ua),hit=null;\r\n  r.rules.forEach(function(x){\r\n    if(x.type==='disallow'&&x.path==='')return;\r\n    if(!x.path)return;\r\n    var re;try{re=patRe(x.path)}catch(e){return}\r\n    if(!re.test(path))return;\r\n    if(!hit||x.path.length>hit.path.length||(x.path.length===hit.path.length&&x.type==='allow'))hit=x;\r\n  });\r\n  return {allowed:!hit||hit.type==='allow',rule:hit,agent:r.agent,hasRules:r.rules.length>0};\r\n}\r\nfunction pathOf(s){\r\n  s=(s||'').trim();if(!s)return '\/';\r\n  try{var u=new URL(\/^https?:\\\/\\\/\/i.test(s)?s:'https:\/\/x.test'+(s.charAt(0)==='\/'?'':'\/')+s);return u.pathname+u.search}catch(e){return '\/'}\r\n}\r\nvar UAS=[['Googlebot','Googlebot'],['Googlebot-Image','Googlebot-Image'],['Bingbot','bingbot'],['All other bots (*)','*'],['GPTBot','GPTBot'],['ClaudeBot','ClaudeBot'],['CCBot','CCBot']];\r\nvar UAOPT=UAS.map(function(u){return '<option value=\"'+u[1]+'\">'+u[0]+'<\/option>'}).join('');\r\n\r\n\/* ===== Robots.txt Generator ===== *\/\r\ntool({id:'robots-generator',name:'Robots.txt Generator',cat:'crawl',mode:'here',kw:'robots txt create build disallow allow crawler bots ai',\r\ndesc:'Build a robots.txt file with common rules, AI crawler blocks and your sitemap address.',\r\ntpl:`<div class=\"split\"><div class=\"col\">\r\n<div class=\"field\"><label for=\"rg-site\">Sitemap address <span class=\"opt\">(optional)<\/span><\/label><input type=\"text\" id=\"rg-site\" value=\"https:\/\/www.example.com\/sitemap.xml\" spellcheck=\"false\"><\/div>\r\n<div class=\"field\"><label for=\"rg-def\">Default for all crawlers<\/label><select id=\"rg-def\"><option value=\"allow\">Allow everything<\/option><option value=\"block\">Block everything (staging sites)<\/option><\/select><\/div>\r\n<div class=\"field\"><span class=\"lbl\">Common blocks<\/span><div class=\"checks\">\r\n<label class=\"check\"><input type=\"checkbox\" id=\"rg-wp\" checked><span>WordPress admin (keeps admin-ajax.php open)<\/span><\/label>\r\n<label class=\"check\"><input type=\"checkbox\" id=\"rg-cart\"><span>Cart, checkout and account pages<\/span><\/label>\r\n<label class=\"check\"><input type=\"checkbox\" id=\"rg-search\"><span>Internal search results<\/span><\/label>\r\n<label class=\"check\"><input type=\"checkbox\" id=\"rg-params\"><span>Sort, filter and tracking parameters<\/span><\/label><\/div><\/div>\r\n<div class=\"field\"><span class=\"lbl\">AI training crawlers<\/span><label class=\"check\"><input type=\"checkbox\" id=\"rg-ai\"><span>Block GPTBot, ClaudeBot, CCBot, Google-Extended, PerplexityBot and Bytespider<\/span><\/label><\/div>\r\n<div class=\"field\"><label for=\"rg-custom\">Extra paths to block <span class=\"opt\">(one per line)<\/span><\/label><textarea id=\"rg-custom\" rows=\"3\" class=\"code\">\/private\/\r\n\/tmp\/<\/textarea><\/div>\r\n<div class=\"field\"><label for=\"rg-delay\">Crawl-delay in seconds <span class=\"opt\">(optional)<\/span><\/label><input type=\"number\" id=\"rg-delay\" min=\"0\" step=\"1\" placeholder=\"Google ignores this\"><\/div>\r\n<\/div><div class=\"col\"><div class=\"out-box\"><span id=\"rg-pill\" class=\"pill good\">Ready<\/span><label class=\"lbl\" for=\"rg-out\">Your robots.txt<\/label><textarea id=\"rg-out\" class=\"code\" rows=\"16\" readonly><\/textarea><div class=\"btn-row\"><button type=\"button\" class=\"btn primary\" id=\"rg-copy\">Copy file<\/button><\/div><\/div>\r\n<p class=\"hint\">Save the text as robots.txt and upload it to the root of your site, for example example.com\/robots.txt.<\/p><\/div><\/div>`,\r\nnote:'Example values are loaded. Robots.txt controls crawling, not indexing. To keep a page out of search results use a noindex tag.',\r\ninit:function(r){\r\n  function run(){\r\n    var L=[],blockAll=$('#rg-def',r).value==='block';\r\n    L.push('User-agent: *');\r\n    if(blockAll){L.push('Disallow: \/')}\r\n    else{\r\n      var d=[];\r\n      if($('#rg-wp',r).checked){d.push('Disallow: \/wp-admin\/');d.push('Allow: \/wp-admin\/admin-ajax.php')}\r\n      if($('#rg-cart',r).checked)['\/cart\/','\/checkout\/','\/my-account\/'].forEach(function(p){d.push('Disallow: '+p)});\r\n      if($('#rg-search',r).checked){d.push('Disallow: \/search\/');d.push('Disallow: \/*?s=')}\r\n      if($('#rg-params',r).checked)['\/*?sort=','\/*?filter=','\/*?utm_','\/*&utm_'].forEach(function(p){d.push('Disallow: '+p)});\r\n      $('#rg-custom',r).value.split(\/\\n\/).map(function(s){return s.trim()}).filter(Boolean).forEach(function(p){d.push('Disallow: '+(p.charAt(0)==='\/'||p.charAt(0)==='*'?p:'\/'+p))});\r\n      if(!d.length)d.push('Disallow:');\r\n      L=L.concat(d);\r\n    }\r\n    var cd=parseInt($('#rg-delay',r).value,10);if(cd>0)L.push('Crawl-delay: '+cd);\r\n    if($('#rg-ai',r).checked&&!blockAll){['GPTBot','ClaudeBot','CCBot','Google-Extended','PerplexityBot','Bytespider'].forEach(function(b){L.push('');L.push('User-agent: '+b);L.push('Disallow: \/')})}\r\n    var s=$('#rg-site',r).value.trim();\r\n    if(s){L.push('');L.push('Sitemap: '+s)}\r\n    $('#rg-out',r).value=L.join('\\n')+'\\n';\r\n    var p=$('#rg-pill',r);\r\n    if(blockAll)pill(p,'warn','Blocks the whole site. Use only on staging');\r\n    else if(s&&!\/^https?:\\\/\\\/\/i.test(s))pill(p,'warn','Sitemap address should start with https:\/\/');\r\n    else pill(p,'good','Ready to upload');\r\n  }\r\n  r.addEventListener('input',run);r.addEventListener('change',run);run();wireCopy(r,'#rg-copy','#rg-out');\r\n}});\r\n\r\n\/* ===== Robots.txt Tester ===== *\/\r\ntool({id:'robots-tester',name:'Robots.txt Tester',cat:'crawl',mode:'paste',kw:'robots txt test blocked allowed url googlebot',\r\ndesc:'Check whether a URL is blocked for a crawler, and see which rule decided it.',\r\ntpl:`<div class=\"split\"><div class=\"col\">\r\n<div class=\"field\"><label for=\"rt-txt\">Your robots.txt <span class=\"opt\">(open example.com\/robots.txt and paste it)<\/span><\/label><textarea id=\"rt-txt\" class=\"code\" rows=\"12\">User-agent: *\r\nDisallow: \/wp-admin\/\r\nAllow: \/wp-admin\/admin-ajax.php\r\nDisallow: \/*?sort=\r\nDisallow: \/private\/\r\n\r\nUser-agent: GPTBot\r\nDisallow: \/\r\n\r\nSitemap: https:\/\/www.example.com\/sitemap.xml<\/textarea><\/div><\/div>\r\n<div class=\"col\"><div class=\"field\"><label for=\"rt-urls\">URLs or paths to test <span class=\"opt\">(one per line)<\/span><\/label><textarea id=\"rt-urls\" class=\"code\" rows=\"5\">\/blog\/seo-guide\/\r\n\/wp-admin\/admin-ajax.php\r\n\/shop\/?sort=price\r\n\/private\/report.pdf<\/textarea><\/div>\r\n<div class=\"field\"><label for=\"rt-ua\">Crawler<\/label><select id=\"rt-ua\">${UAOPT}<\/select><\/div>\r\n<div id=\"rt-out\" aria-live=\"polite\"><\/div><\/div><\/div>`,\r\nnote:'Follows Google\u2019s rules: the most specific crawler group applies, the longest matching path wins, and Allow wins a tie. * matches any text and $ marks the end of a URL.',\r\ninit:function(r){\r\n  function run(){\r\n    var p=parseRobots($('#rt-txt',r).value),ua=$('#rt-ua',r).value;\r\n    var urls=$('#rt-urls',r).value.split(\/\\n\/).map(function(s){return s.trim()}).filter(Boolean);\r\n    if(!urls.length){$('#rt-out',r).innerHTML='<p class=\"hint\">Add at least one URL or path.<\/p>';return}\r\n    var h='<div class=\"tbl\"><table><thead><tr><th>Path<\/th><th>Result<\/th><th>Deciding rule<\/th><\/tr><\/thead><tbody>';\r\n    urls.forEach(function(u){\r\n      var path=pathOf(u),res=robotsCheck(p,ua,path);\r\n      h+='<tr><td class=\"mono\">'+esc(path)+'<\/td><td><span class=\"pill '+(res.allowed?'good':'bad')+'\">'+(res.allowed?'Allowed':'Blocked')+'<\/span><\/td><td class=\"mono\">'+(res.rule?esc(res.rule.type[0].toUpperCase()+res.rule.type.slice(1)+': '+res.rule.path):(res.hasRules?'No rule matches':'No rules for this crawler'))+'<\/td><\/tr>';\r\n    });\r\n    h+='<\/tbody><\/table><\/div><p class=\"hint\">Group used: '+esc(rulesFor(p,ua).agent)+(p.sitemaps.length?' \u00b7 Sitemaps declared: '+p.sitemaps.length:' \u00b7 No Sitemap line found')+'<\/p>';\r\n    $('#rt-out',r).innerHTML=h;\r\n  }\r\n  r.addEventListener('input',run);r.addEventListener('change',run);run();\r\n}});\r\n\r\n\/* ===== XML Sitemap Generator ===== *\/\r\nfunction xmlEsc(s){return s.replace(\/&\/g,'&amp;').replace(\/<\/g,'&lt;').replace(\/>\/g,'&gt;').replace(\/\"\/g,'&quot;').replace(\/'\/g,'&apos;')}\r\ntool({id:'sitemap-generator',name:'XML Sitemap Generator',cat:'crawl',mode:'here',kw:'xml sitemap create urls list changefreq priority lastmod',\r\ndesc:'Paste a list of URLs and get a valid sitemap.xml with optional dates and priority.',\r\ntpl:`<div class=\"split\"><div class=\"col\">\r\n<div class=\"field\"><label for=\"sg-urls\">Page URLs <span class=\"opt\">(one per line)<\/span><\/label><textarea id=\"sg-urls\" class=\"code\" rows=\"10\">https:\/\/www.example.com\/\r\nhttps:\/\/www.example.com\/about\/\r\nhttps:\/\/www.example.com\/services\/seo\/\r\nhttps:\/\/www.example.com\/blog\/technical-seo-checklist\/\r\nhttps:\/\/www.example.com\/contact\/<\/textarea><\/div>\r\n<div class=\"row2\"><div class=\"field\"><label for=\"sg-freq\">Change frequency<\/label><select id=\"sg-freq\"><option value=\"\">Leave out<\/option><option>daily<\/option><option selected>weekly<\/option><option>monthly<\/option><option>yearly<\/option><\/select><\/div>\r\n<div class=\"field\"><label for=\"sg-pri\">Priority<\/label><select id=\"sg-pri\"><option value=\"\">Leave out<\/option><option value=\"auto\" selected>Homepage 1.0, others 0.8<\/option><option>0.5<\/option><\/select><\/div><\/div>\r\n<label class=\"check\"><input type=\"checkbox\" id=\"sg-mod\" checked><span>Add today\u2019s date as last modified<\/span><\/label>\r\n<\/div><div class=\"col\"><div class=\"out-box\"><span id=\"sg-pill\" class=\"pill good\">Ready<\/span><label class=\"lbl\" for=\"sg-out\">Your sitemap.xml<\/label><textarea id=\"sg-out\" class=\"code\" rows=\"14\" readonly><\/textarea><div class=\"btn-row\"><button type=\"button\" class=\"btn primary\" id=\"sg-copy\">Copy sitemap<\/button><\/div><\/div>\r\n<div id=\"sg-issues\"><\/div><\/div><\/div>`,\r\nnote:'Upload the file to your site root, then submit its address in Google Search Console and Bing Webmaster Tools. One file can hold up to 50,000 URLs.',\r\ninit:function(r){\r\n  function run(){\r\n    var raw=$('#sg-urls',r).value.split(\/\\n\/).map(function(s){return s.trim()}).filter(Boolean),seen={},ok=[],iss=[];\r\n    raw.forEach(function(u){\r\n      var x=normUrl(u);\r\n      if(!\/^https?:\\\/\\\/\/i.test(u)||!x||x.hostname.indexOf('.')<0){iss.push({l:'bad',t:'Skipped: '+u,d:'Use the full address starting with http:\/\/ or https:\/\/.'});return}\r\n      if(seen[u]){iss.push({l:'warn',t:'Duplicate removed: '+u});return}\r\n      seen[u]=1;ok.push(u);\r\n    });\r\n    var hosts={};ok.forEach(function(u){hosts[new URL(u).host]=1});\r\n    if(Object.keys(hosts).length>1)iss.push({l:'warn',t:'URLs from more than one host',d:'A sitemap should only list pages from the host where it is stored.'});\r\n    if(ok.length>50000)iss.push({l:'bad',t:'More than 50,000 URLs',d:'Split into several sitemaps and add a sitemap index.'});\r\n    var f=$('#sg-freq',r).value,p=$('#sg-pri',r).value,m=$('#sg-mod',r).checked;\r\n    var x='<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n<urlset xmlns=\"http:\/\/www.sitemaps.org\/schemas\/sitemap\/0.9\">\\n';\r\n    ok.forEach(function(u,i){\r\n      x+='  <url>\\n    <loc>'+xmlEsc(u)+'<\/loc>\\n';\r\n      if(m)x+='    <lastmod>'+today()+'<\/lastmod>\\n';\r\n      if(f)x+='    <changefreq>'+f+'<\/changefreq>\\n';\r\n      if(p)x+='    <priority>'+(p==='auto'?(i===0?'1.0':'0.8'):p)+'<\/priority>\\n';\r\n      x+='  <\/url>\\n';\r\n    });\r\n    x+='<\/urlset>\\n';\r\n    $('#sg-out',r).value=ok.length?x:'';\r\n    var s=summary(iss);pill($('#sg-pill',r),ok.length?(iss.length?s[0]:'good'):'idle',ok.length?(ok.length+' URL'+(ok.length>1?'s':'')+' included'+(iss.length?' \u00b7 '+s[1]:'')):'Add URLs');\r\n    $('#sg-issues',r).innerHTML=iss.length?issues(iss):'';\r\n  }\r\n  r.addEventListener('input',run);r.addEventListener('change',run);run();wireCopy(r,'#sg-copy','#sg-out');\r\n}});\r\n\r\n\/* ===== Sitemap Validator ===== *\/\r\ntool({id:'sitemap-validator',name:'Sitemap Validator',cat:'crawl',mode:'paste',kw:'sitemap xml validate errors check format',\r\ndesc:'Paste a sitemap and find format errors, bad URLs, wrong dates and limit problems.',\r\ntpl:`<div class=\"split\"><div class=\"col\"><div class=\"field\"><label for=\"sv-xml\">Sitemap XML <span class=\"opt\">(open the sitemap in your browser, copy and paste)<\/span><\/label>\r\n<textarea id=\"sv-xml\" class=\"code\" rows=\"16\"><?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<urlset xmlns=\"http:\/\/www.sitemaps.org\/schemas\/sitemap\/0.9\">\r\n  <url><loc>https:\/\/www.example.com\/<\/loc><lastmod>2026-10-01<\/lastmod><priority>1.0<\/priority><\/url>\r\n  <url><loc>https:\/\/www.example.com\/about\/<\/loc><lastmod>01\/10\/2026<\/lastmod><\/url>\r\n  <url><loc>\/relative-page\/<\/loc><\/url>\r\n  <url><loc>https:\/\/www.example.com\/about\/<\/loc><changefreq>sometimes<\/changefreq><\/url>\r\n  <url><loc>https:\/\/blog.example.com\/post\/<\/loc><priority>1.5<\/priority><\/url>\r\n<\/urlset><\/textarea><\/div><\/div>\r\n<div class=\"col\"><span id=\"sv-pill\" class=\"pill idle\">Checking<\/span><div class=\"stats\" id=\"sv-stats\"><\/div><div id=\"sv-out\" aria-live=\"polite\"><\/div><\/div><\/div>`,\r\nnote:'Checks the sitemaps.org format. It cannot confirm that each URL returns a 200 status. Use your crawler or Search Console for that.',\r\ninit:function(r){\r\n  var FREQ=['always','hourly','daily','weekly','monthly','yearly','never'];\r\n  function run(){\r\n    var txt=$('#sv-xml',r).value.trim(),iss=[];\r\n    if(!txt){pill($('#sv-pill',r),'idle','Paste a sitemap');$('#sv-out',r).innerHTML='';$('#sv-stats',r).innerHTML='';return}\r\n    var doc=new DOMParser().parseFromString(txt,'application\/xml');\r\n    var pe=doc.getElementsByTagName('parsererror')[0];\r\n    if(pe){pill($('#sv-pill',r),'bad','Not valid XML');$('#sv-stats',r).innerHTML='';$('#sv-out',r).innerHTML=issues([{l:'bad',t:'The XML cannot be read',d:(pe.textContent||'').split('\\n')[0].slice(0,220)+'. Common cause: an unescaped & in a URL, which must be written as &amp;.'}]);return}\r\n    var root=doc.documentElement,rn=root.localName,ns=root.namespaceURI;\r\n    if(rn!=='urlset'&&rn!=='sitemapindex')iss.push({l:'bad',t:'Root element is <'+rn+'>',d:'It must be <urlset> or <sitemapindex>.'});\r\n    if(ns!=='http:\/\/www.sitemaps.org\/schemas\/sitemap\/0.9')iss.push({l:'bad',t:'Missing or wrong namespace',d:'Add xmlns=\"http:\/\/www.sitemaps.org\/schemas\/sitemap\/0.9\" to the root element.'});\r\n    var items=Array.prototype.slice.call(root.children).filter(function(e){return e.localName==='url'||e.localName==='sitemap'});\r\n    function val(e,n){var c=Array.prototype.slice.call(e.children).filter(function(x){return x.localName===n})[0];return c?c.textContent.trim():null}\r\n    var seen={},hosts={},dateRe=\/^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}(:\\d{2}(\\.\\d+)?)?(Z|[+-]\\d{2}:\\d{2}))?$\/;\r\n    items.forEach(function(e,i){\r\n      var n=i+1,loc=val(e,'loc');\r\n      if(!loc){iss.push({l:'bad',t:'Entry '+n+' has no <loc>'});return}\r\n      var u=null;try{u=new URL(loc)}catch(x){}\r\n      if(!u||!\/^https?:$\/.test(u.protocol))iss.push({l:'bad',t:'Entry '+n+': \u201c'+loc+'\u201d is not an absolute URL',d:'Start with https:\/\/ and include the domain.'});\r\n      else hosts[u.host]=(hosts[u.host]||0)+1;\r\n      if(loc.length>2048)iss.push({l:'bad',t:'Entry '+n+': URL is longer than 2,048 characters'});\r\n      if(\/\\s\/.test(loc))iss.push({l:'bad',t:'Entry '+n+': URL contains spaces',d:'Encode spaces as %20.'});\r\n      if(seen[loc])iss.push({l:'warn',t:'Duplicate URL: '+loc,d:'First seen at entry '+seen[loc]+', again at entry '+n+'.'});else seen[loc]=n;\r\n      var lm=val(e,'lastmod');if(lm!==null&&!dateRe.test(lm))iss.push({l:'warn',t:'Entry '+n+': lastmod \u201c'+lm+'\u201d is not a W3C date',d:'Use YYYY-MM-DD, for example 2026-10-01.'});\r\n      var cf=val(e,'changefreq');if(cf!==null&&FREQ.indexOf(cf)<0)iss.push({l:'warn',t:'Entry '+n+': changefreq \u201c'+cf+'\u201d is not valid',d:'Allowed: '+FREQ.join(', ')+'.'});\r\n      var pr=val(e,'priority');if(pr!==null){var pv=parseFloat(pr);if(isNaN(pv)||pv<0||pv>1)iss.push({l:'warn',t:'Entry '+n+': priority \u201c'+pr+'\u201d is out of range',d:'Use a number from 0.0 to 1.0.'})}\r\n    });\r\n    var hk=Object.keys(hosts);\r\n    if(hk.length>1)iss.push({l:'warn',t:'URLs from '+hk.length+' different hosts: '+hk.join(', '),d:'A sitemap may only list URLs from its own host.'});\r\n    if(items.length>50000)iss.push({l:'bad',t:'More than 50,000 entries ('+items.length+')',d:'Split into several files and list them in a sitemap index.'});\r\n    var size=new Blob([txt]).size;if(size>52428800)iss.push({l:'bad',t:'File is over 50 MB uncompressed'});\r\n    if(!items.length)iss.push({l:'bad',t:'No <url> entries found'});\r\n    var s=summary(iss);pill($('#sv-pill',r),s[0],s[1]);\r\n    $('#sv-stats',r).innerHTML='<div class=\"stat key\"><span>Entries<\/span><strong>'+items.length+'<\/strong><\/div><div class=\"stat\"><span>Type<\/span><strong>'+(rn==='sitemapindex'?'Index':'URL set')+'<\/strong><\/div><div class=\"stat\"><span>Size<\/span><strong>'+bytes(size)+'<\/strong><\/div>';\r\n    $('#sv-out',r).innerHTML=iss.length?issues(iss):issues([{l:'good',t:'The sitemap follows the format rules'}]);\r\n  }\r\n  $('#sv-xml',r).addEventListener('input',run);run();\r\n}});\r\n\r\n\/* ===== Crawlability Checker ===== *\/\r\ntool({id:'crawlability-checker',name:'Crawlability Checker',cat:'crawl',mode:'paste',kw:'crawl index noindex robots meta x-robots-tag bots access',\r\ndesc:'Combine robots.txt, meta robots and headers to learn if a page can be crawled and indexed.',\r\ntpl:`<div class=\"split\"><div class=\"col\">\r\n<div class=\"field\"><label for=\"cc-url\">Page address<\/label><input type=\"text\" id=\"cc-url\" value=\"https:\/\/www.example.com\/private\/report\/\" spellcheck=\"false\"><\/div>\r\n<div class=\"field\"><label for=\"cc-ua\">Crawler<\/label><select id=\"cc-ua\">${UAOPT}<\/select><\/div>\r\n<div class=\"field\"><label for=\"cc-robots\">robots.txt<\/label><textarea id=\"cc-robots\" class=\"code\" rows=\"5\">User-agent: *\r\nDisallow: \/private\/<\/textarea><\/div>\r\n<div class=\"field\"><label for=\"cc-html\">Page HTML <span class=\"opt\">(view source and paste, optional)<\/span><\/label><textarea id=\"cc-html\" class=\"code\" rows=\"5\"><head><meta name=\"robots\" content=\"noindex, follow\"><link rel=\"canonical\" href=\"https:\/\/www.example.com\/report\/\"><\/head><\/textarea><\/div>\r\n<div class=\"row2\"><div class=\"field\"><label for=\"cc-xrt\">X-Robots-Tag header<\/label><input type=\"text\" id=\"cc-xrt\" placeholder=\"noindex\" spellcheck=\"false\"><\/div>\r\n<div class=\"field\"><label for=\"cc-st\">HTTP status<\/label><select id=\"cc-st\"><option value=\"200\">200 OK<\/option><option value=\"301\">301 redirect<\/option><option value=\"404\">404 not found<\/option><option value=\"410\">410 gone<\/option><option value=\"500\">500 server error<\/option><\/select><\/div><\/div>\r\n<\/div><div class=\"col\"><span id=\"cc-pill\" class=\"pill idle\">Checking<\/span><div class=\"stats\" id=\"cc-stats\"><\/div><div id=\"cc-out\" aria-live=\"polite\"><\/div><\/div><\/div>`,\r\nnote:'If robots.txt blocks a page, search engines cannot read its noindex tag. To remove a page from results, allow crawling and use noindex.',\r\ninit:function(r){\r\n  function run(){\r\n    var url=$('#cc-url',r).value,ua=$('#cc-ua',r).value,pr=parseRobots($('#cc-robots',r).value),res=robotsCheck(pr,ua,pathOf(url));\r\n    var html=$('#cc-html',r).value,xrt=$('#cc-xrt',r).value.toLowerCase(),st=$('#cc-st',r).value,iss=[],noindex=false,nofollow=false;\r\n    var canCrawl=res.allowed;\r\n    iss.push(canCrawl?{l:'good',t:'robots.txt allows crawling',d:res.rule?'Matched: '+res.rule.type+' '+res.rule.path:'No rule blocks this path.'}:{l:'bad',t:'robots.txt blocks crawling',d:'Matched: '+res.rule.type+' '+res.rule.path+'. Search engines will not fetch this page.'});\r\n    if(html.trim()){\r\n      var d=new DOMParser().parseFromString(html,'text\/html'),metas=$$('meta[name]',d).filter(function(m){var n=m.getAttribute('name').toLowerCase();return n==='robots'||n==='googlebot'||n===ua.toLowerCase()});\r\n      metas.forEach(function(m){var c=(m.getAttribute('content')||'').toLowerCase();if(\/noindex|none\/.test(c))noindex=true;if(\/nofollow|none\/.test(c))nofollow=true});\r\n      if(metas.length)iss.push({l:noindex?'bad':'good',t:'Meta robots: '+metas.map(function(m){return m.getAttribute('content')}).join(' | '),d:noindex?'The page tells search engines not to index it.':'No noindex directive.'});\r\n      else iss.push({l:'good',t:'No meta robots tag',d:'Indexable by default.'});\r\n      var cn=d.querySelector('link[rel~=canonical]');\r\n      if(cn){var ch=cn.getAttribute('href')||'',a=normUrl(url),b=null;try{b=new URL(ch,a||undefined)}catch(e){}\r\n        if(a&&b&&(a.origin+a.pathname.replace(\/\\\/$\/,''))!==(b.origin+b.pathname.replace(\/\\\/$\/,'')))iss.push({l:'warn',t:'Canonical points to a different URL',d:ch+'. Search engines may index that URL instead of this one.'})}\r\n      var nf=$$('a[rel~=nofollow]',d).length;if(nf)iss.push({l:'idle',t:nf+' link'+(nf>1?'s':'')+' marked nofollow'});\r\n    }\r\n    if(xrt){if(\/noindex|none\/.test(xrt))noindex=true;if(\/nofollow|none\/.test(xrt))nofollow=true;iss.push({l:\/noindex|none\/.test(xrt)?'bad':'idle',t:'X-Robots-Tag: '+xrt,d:\/noindex|none\/.test(xrt)?'The server tells search engines not to index this response.':''})}\r\n    if(st!=='200')iss.push({l:st==='301'?'warn':'bad',t:'HTTP status '+st,d:st==='301'?'The crawler follows the redirect and indexes the target, not this URL.':'Pages that do not return 200 are not indexed.'});\r\n    if(!canCrawl&&noindex)iss.push({l:'warn',t:'noindex is hidden by the robots.txt block',d:'Because the page cannot be fetched, search engines never see the noindex tag. The URL can still appear in results without a description.'});\r\n    var indexable=canCrawl&&!noindex&&st==='200';\r\n    pill($('#cc-pill',r),indexable?'good':canCrawl&&st==='200'?'warn':'bad',indexable?'Can be crawled and indexed':canCrawl?'Can be crawled, will not be indexed':'Cannot be crawled');\r\n    $('#cc-stats',r).innerHTML='<div class=\"stat\"><span>Crawl<\/span><strong>'+(canCrawl?'Yes':'No')+'<\/strong><\/div><div class=\"stat\"><span>Index<\/span><strong>'+(indexable?'Yes':'No')+'<\/strong><\/div><div class=\"stat\"><span>Follow links<\/span><strong>'+(nofollow?'No':'Yes')+'<\/strong><\/div>';\r\n    $('#cc-out',r).innerHTML=issues(iss);\r\n  }\r\n  r.addEventListener('input',run);r.addEventListener('change',run);run();\r\n}});\r\n\r\n\/* ===== Index Status Checker ===== *\/\r\ntool({id:'index-status-checker',name:'Index Status Checker',cat:'crawl',mode:'link',kw:'indexed pages site: operator google bing search console',\r\ndesc:'Build site: searches that show which pages a search engine has indexed.',\r\ntpl:`<div class=\"split\"><div class=\"col\">\r\n<div class=\"field\"><label for=\"ix-d\">Domain<\/label><input type=\"text\" id=\"ix-d\" value=\"example.com\" spellcheck=\"false\"><\/div>\r\n<div class=\"field\"><label for=\"ix-u\">Specific pages to check <span class=\"opt\">(one per line)<\/span><\/label><textarea id=\"ix-u\" class=\"code\" rows=\"4\">https:\/\/www.example.com\/blog\/technical-seo-checklist\/\r\nhttps:\/\/www.example.com\/services\/seo\/<\/textarea><\/div>\r\n<div class=\"field\"><label for=\"ix-k\">Find indexed pages that contain <span class=\"opt\">(optional)<\/span><\/label><input type=\"text\" id=\"ix-k\" placeholder=\"for example pricing\"><\/div>\r\n<\/div><div class=\"col\"><div id=\"ix-out\"><\/div>\r\n<div class=\"out-box\"><span class=\"lbl\">How to read the results<\/span><p class=\"hint\">The number of results on a site: search is only a rough estimate. For exact numbers use the Pages report in Google Search Console. Zero results for a page means it is not indexed, or was indexed very recently.<\/p><\/div><\/div><\/div>`,\r\ninit:function(r){\r\n  function run(){\r\n    var d=hostOf($('#ix-d',r).value).replace(\/^www\\.\/,''),k=$('#ix-k',r).value.trim(),o='';\r\n    if(!d){$('#ix-out',r).innerHTML='<p class=\"hint\">Enter a domain such as example.com.<\/p>';return}\r\n    var G='https:\/\/www.google.com\/search?q=',B='https:\/\/www.bing.com\/search?q=';\r\n    var arr=[{h:G+enc('site:'+d),t:'Google: all indexed pages',s:'site:'+d},{h:B+enc('site:'+d),t:'Bing: all indexed pages',s:'site:'+d},\r\n      {h:G+enc('site:'+d+' -inurl:https'),t:'Google: pages not on https',s:'site:'+d+' -inurl:https'},\r\n      {h:G+enc('site:'+d+' inurl:?'),t:'Google: URLs with parameters',s:'Spot duplicate and tracking URLs'}];\r\n    if(k)arr.push({h:G+enc('site:'+d+' \"'+k+'\"'),t:'Google: pages containing \u201c'+k+'\u201d',s:'site:'+d+' \"'+k+'\"'});\r\n    arr.push({h:'https:\/\/search.google.com\/search-console',t:'Google Search Console',s:'Exact index counts and URL Inspection'});\r\n    arr.push({h:'https:\/\/www.bing.com\/webmasters',t:'Bing Webmaster Tools',s:'Index status for Bing'});\r\n    o+='<span class=\"lbl\">Domain checks<\/span>'+linkGrid(arr);\r\n    var pages=$('#ix-u',r).value.split(\/\\n\/).map(function(s){return s.trim()}).filter(Boolean).filter(function(u){return normUrl(u)});\r\n    if(pages.length)o+='<span class=\"lbl\" style=\"display:block;margin-top:14px\">Page checks<\/span>'+linkGrid(pages.map(function(u){var n=normUrl(u);return {h:G+enc('site:'+n.href),t:n.pathname+n.search||'\/',s:'site:'+n.href}}));\r\n    $('#ix-out',r).innerHTML=o;\r\n  }\r\n  r.addEventListener('input',run);run();\r\n}});\r\n\r\n\/* ===== Google Cache Checker ===== *\/\r\ntool({id:'google-cache-checker',name:'Google Cache Checker',cat:'crawl',mode:'link',kw:'cache cached version page archive wayback snapshot',\r\ndesc:'Find a saved copy of a page. Google retired its cache links, so this uses web archives.',\r\ntpl:`<div class=\"split\"><div class=\"col\">\r\n<div class=\"field\"><label for=\"gc-u\">Page address<\/label><input type=\"text\" id=\"gc-u\" value=\"https:\/\/www.example.com\/blog\/technical-seo-checklist\/\" spellcheck=\"false\"><\/div>\r\n<div class=\"out-box\"><span class=\"lbl\">Why not Google cache?<\/span><p class=\"hint\">Google removed cached page links and the cache: search operator in 2024. To see what Google last fetched, use the URL Inspection tool in Search Console and open \u201cView crawled page\u201d.<\/p><\/div>\r\n<\/div><div class=\"col\"><div id=\"gc-out\"><\/div><\/div><\/div>`,\r\ninit:function(r){\r\n  function run(){\r\n    var u=normUrl($('#gc-u',r).value);\r\n    if(!u){$('#gc-out',r).innerHTML='<p class=\"hint\">Enter a full page address.<\/p>';return}\r\n    $('#gc-out',r).innerHTML='<span class=\"lbl\">Saved copies<\/span>'+linkGrid([\r\n      {h:'https:\/\/web.archive.org\/web\/'+u.href,t:'Wayback Machine: latest copy',s:'Internet Archive'},\r\n      {h:'https:\/\/web.archive.org\/web\/*\/'+u.href,t:'Wayback Machine: all dates',s:'Browse the history of this page'},\r\n      {h:'https:\/\/archive.ph\/newest\/'+u.href,t:'archive.ph: newest copy',s:'Independent snapshot service'},\r\n      {h:'https:\/\/web.archive.org\/save\/'+u.href,t:'Save a copy now',s:'Create a new Wayback snapshot'},\r\n      {h:'https:\/\/search.google.com\/search-console',t:'Search Console URL Inspection',s:'What Google last crawled (needs site access)'}]);\r\n  }\r\n  $('#gc-u',r).addEventListener('input',run);run();\r\n}});\r\n\r\n\/* ===== Canonical Tag Checker ===== *\/\r\ntool({id:'canonical-tag-checker',name:'Canonical Tag Checker',cat:'tags',mode:'paste',kw:'canonical rel link duplicate content check issues',\r\ndesc:'Paste a page\u2019s HTML to find missing, duplicate, relative or conflicting canonical tags.',\r\ntpl:`<div class=\"split\"><div class=\"col\">\r\n<div class=\"field\"><label for=\"cch-url\">Page address<\/label><input type=\"text\" id=\"cch-url\" value=\"https:\/\/www.example.com\/shoes\/?utm_source=newsletter\" spellcheck=\"false\"><\/div>\r\n<div class=\"field\"><label for=\"cch-html\">Page HTML <span class=\"opt\">(view source, copy and paste)<\/span><\/label><textarea id=\"cch-html\" class=\"code\" rows=\"12\"><!doctype html>\r\n<html><head>\r\n<title>Running shoes<\/title>\r\n<meta property=\"og:url\" content=\"https:\/\/www.example.com\/shoes\">\r\n<link rel=\"canonical\" href=\"http:\/\/www.example.com\/shoes\/\">\r\n<link rel=\"canonical\" href=\"\/shoes\/\">\r\n<\/head><body><h1>Running shoes<\/h1><\/body><\/html><\/textarea><\/div><\/div>\r\n<div class=\"col\"><span id=\"cch-pill\" class=\"pill idle\">Checking<\/span><div id=\"cch-out\" aria-live=\"polite\"><\/div><\/div><\/div>`,\r\nnote:'Search engines treat canonical as a strong hint, not a command. Each page should have exactly one canonical tag inside the head.',\r\ninit:function(r){\r\n  function run(){\r\n    var page=normUrl($('#cch-url',r).value),html=$('#cch-html',r).value,iss=[];\r\n    if(!html.trim()){pill($('#cch-pill',r),'idle','Paste the page HTML');$('#cch-out',r).innerHTML='';return}\r\n    var d=new DOMParser().parseFromString(html,'text\/html'),tags=$$('link[rel~=canonical]',d);\r\n    if(!tags.length){iss.push({l:'warn',t:'No canonical tag found',d:'Add <link rel=\"canonical\" href=\"https:\/\/...\"> inside the head so duplicates of this page point to one address.'})}\r\n    else{\r\n      if(tags.length>1){var hs=tags.map(function(t){return t.getAttribute('href')||''});var uniq=hs.filter(function(h,i){return hs.indexOf(h)===i});iss.push({l:'bad',t:tags.length+' canonical tags found',d:uniq.length>1?'They disagree ('+hs.join('  |  ')+'). Search engines will ignore all of them.':'They are identical. Keep one.'})}\r\n      var t=tags[0],href=(t.getAttribute('href')||'').trim();\r\n      if(d.body&&d.body.contains(t))iss.push({l:'bad',t:'Canonical tag is inside the body',d:'It must be in the head. Search engines ignore it here.'});\r\n      if(!href)iss.push({l:'bad',t:'Canonical href is empty'});\r\n      else{\r\n        var abs=\/^https?:\\\/\\\/\/i.test(href),res=null;\r\n        if(!abs)iss.push({l:'warn',t:'Canonical is relative: '+href,d:'Use the full address including https:\/\/ and the domain.'});\r\n        try{res=new URL(href,page||undefined)}catch(e){}\r\n        if(!res)iss.push({l:'bad',t:'Canonical is not a valid address'});\r\n        else{\r\n          if(res.hash)iss.push({l:'warn',t:'Canonical contains a # fragment',d:'Remove everything after #.'});\r\n          if(page){\r\n            if(page.protocol==='https:'&&res.protocol==='http:')iss.push({l:'bad',t:'HTTPS page points to an HTTP canonical',d:'Search engines may index the insecure version. Use https.'});\r\n            var ph=page.hostname,ch=res.hostname;\r\n            if(ph!==ch){var base=function(h){return h.replace(\/^www\\.\/,'')};iss.push(base(ph)===base(ch)?{l:'warn',t:'www and non-www differ',d:'Page host is '+ph+' but canonical host is '+ch+'. Choose one version and use it everywhere.'}:{l:'warn',t:'Canonical points to another domain',d:ch+'. This is valid for syndicated content, but confirm it is intended.'})}\r\n            var pp=page.pathname,cp=res.pathname;\r\n            if(pp!==cp&&pp.replace(\/\\\/$\/,'')===cp.replace(\/\\\/$\/,''))iss.push({l:'warn',t:'Trailing slash differs from the page address',d:'Page: '+pp+' \u00b7 Canonical: '+cp});\r\n            if(pp.toLowerCase()===cp.toLowerCase()&&pp!==cp)iss.push({l:'warn',t:'Letter case differs from the page address'});\r\n            if(page.search&&!res.search)iss.push({l:'good',t:'Parameters removed in canonical',d:'The page has '+page.search+'. The canonical correctly drops it.'});\r\n            var same=(page.origin+page.pathname)===(res.origin+res.pathname)&&page.search===res.search;\r\n            if(same)iss.push({l:'good',t:'Self-referencing canonical'});\r\n          }\r\n          if(res.search&&\/utm_|sessionid|sid=\/.test(res.search))iss.push({l:'warn',t:'Canonical contains tracking parameters'});\r\n        }\r\n      }\r\n    }\r\n    var og=d.querySelector('meta[property=\"og:url\"]'),ct=tags[0]&&(tags[0].getAttribute('href')||'');\r\n    if(og&&ct&&og.getAttribute('content')!==ct)iss.push({l:'idle',t:'og:url differs from the canonical',d:'og:url is '+og.getAttribute('content')+'. Keep both the same.'});\r\n    var rb=d.querySelector('meta[name=robots]');\r\n    if(rb&&\/noindex\/i.test(rb.getAttribute('content')||'')&&tags.length)iss.push({l:'warn',t:'noindex and canonical on the same page',d:'These send mixed signals. Use one or the other.'});\r\n    var s=summary(iss);pill($('#cch-pill',r),s[0],s[1]);\r\n    $('#cch-out',r).innerHTML=issues(iss);\r\n  }\r\n  r.addEventListener('input',run);run();\r\n}});\r\n\r\n\/* ===== Canonical Tag Generator ===== *\/\r\ntool({id:'canonical-tag-generator',name:'Canonical Tag Generator',cat:'tags',mode:'here',kw:'canonical rel link build create clean url normalize',\r\ndesc:'Clean a URL into its preferred form and get the canonical tag and HTTP header.',\r\ntpl:`<div class=\"split\"><div class=\"col\">\r\n<div class=\"field\"><label for=\"cg-u\">Page address<\/label><input type=\"text\" id=\"cg-u\" value=\"http:\/\/WWW.Example.com\/Shoes\/Running\/index.html?utm_source=ig&color=blue#reviews\" spellcheck=\"false\"><\/div>\r\n<div class=\"row2\"><div class=\"field\"><label for=\"cg-host\">Preferred host<\/label><select id=\"cg-host\"><option value=\"keep\">Keep as entered<\/option><option value=\"www\" selected>Use www<\/option><option value=\"non\">Remove www<\/option><\/select><\/div>\r\n<div class=\"field\"><label for=\"cg-slash\">Trailing slash<\/label><select id=\"cg-slash\"><option value=\"keep\">Keep as entered<\/option><option value=\"add\" selected>Always add<\/option><option value=\"remove\">Always remove<\/option><\/select><\/div><\/div>\r\n<div class=\"field\"><label for=\"cg-q\">Query parameters<\/label><select id=\"cg-q\"><option value=\"all\">Remove all<\/option><option value=\"keep\" selected>Keep only the ones listed below<\/option><option value=\"none\">Keep all<\/option><\/select><\/div>\r\n<div class=\"field\"><label for=\"cg-keep\">Parameters to keep <span class=\"opt\">(comma separated)<\/span><\/label><input type=\"text\" id=\"cg-keep\" value=\"color, page\" spellcheck=\"false\"><\/div>\r\n<div class=\"checks\"><label class=\"check\"><input type=\"checkbox\" id=\"cg-https\" checked><span>Force https<\/span><\/label>\r\n<label class=\"check\"><input type=\"checkbox\" id=\"cg-lower\" checked><span>Lowercase the address<\/span><\/label>\r\n<label class=\"check\"><input type=\"checkbox\" id=\"cg-idx\" checked><span>Remove index.html, index.php and default.aspx<\/span><\/label><\/div>\r\n<\/div><div class=\"col\"><div class=\"out-box\"><span id=\"cg-pill\" class=\"pill good\">Ready<\/span>\r\n<label class=\"lbl\" for=\"cg-url\">Clean canonical address<\/label><textarea id=\"cg-url\" class=\"code\" rows=\"2\" readonly><\/textarea>\r\n<label class=\"lbl\" for=\"cg-tag\">HTML tag for the head<\/label><textarea id=\"cg-tag\" class=\"code\" rows=\"2\" readonly><\/textarea>\r\n<label class=\"lbl\" for=\"cg-hdr\">HTTP header version (for PDFs and files)<\/label><textarea id=\"cg-hdr\" class=\"code\" rows=\"2\" readonly><\/textarea>\r\n<div class=\"btn-row\"><button type=\"button\" class=\"btn primary\" id=\"cg-copy\">Copy tag<\/button><\/div><\/div><div id=\"cg-chg\"><\/div><\/div><\/div>`,\r\nnote:'The tag should sit inside the head of the page. Pick one preferred version of your site and redirect the others to it.',\r\ninit:function(r){\r\n  function run(){\r\n    var raw=$('#cg-u',r).value.trim(),u=normUrl(raw);\r\n    if(!u||u.hostname.indexOf('.')<0){pill($('#cg-pill',r),'bad','Enter a full page address');['#cg-url','#cg-tag','#cg-hdr'].forEach(function(s){$(s,r).value=''});$('#cg-chg',r).innerHTML='';return}\r\n    var chg=[],o=u.protocol,h=u.hostname,p=u.pathname,q=u.searchParams;\r\n    if($('#cg-https',r).checked&&o==='http:'){o='https:';chg.push('Changed http to https')}\r\n    var hm=$('#cg-host',r).value;\r\n    if(hm==='www'&&!\/^www\\.\/i.test(h)){h='www.'+h;chg.push('Added www')}\r\n    if(hm==='non'&&\/^www\\.\/i.test(h)){h=h.replace(\/^www\\.\/i,'');chg.push('Removed www')}\r\n    if($('#cg-idx',r).checked){var np=p.replace(\/\\\/(index\\.(html?|php)|default\\.aspx?)$\/i,'\/');if(np!==p){p=np;chg.push('Removed the index file name')}}\r\n    var qm=$('#cg-q',r).value,keep=$('#cg-keep',r).value.split(',').map(function(s){return s.trim().toLowerCase()}).filter(Boolean),out=[];\r\n    q.forEach(function(v,k){if(qm==='none'||(qm==='keep'&&keep.indexOf(k.toLowerCase())>=0))out.push([k,v]);else chg.push('Removed parameter '+k)});\r\n    var sm=$('#cg-slash',r).value,last=p.split('\/').pop();\r\n    if(sm==='add'&&!\/\\\/$\/.test(p)&&last.indexOf('.')<0){p+='\/';chg.push('Added trailing slash')}\r\n    if(sm==='remove'&&p.length>1&&\/\\\/$\/.test(p)){p=p.replace(\/\\\/+$\/,'');chg.push('Removed trailing slash')}\r\n    var s=out.length?'?'+out.map(function(x){return enc(x[0])+'='+enc(x[1])}).join('&'):'';\r\n    if(u.hash)chg.push('Removed the #'+u.hash.slice(1)+' fragment');\r\n    var res=o+'\/\/'+h+(u.port?':'+u.port:'')+p+s;\r\n    if($('#cg-lower',r).checked){if(res!==res.toLowerCase())chg.push('Lowercased the address');res=res.toLowerCase()}\r\n    $('#cg-url',r).value=res;\r\n    $('#cg-tag',r).value='<link rel=\"canonical\" href=\"'+esc(res)+'\">';\r\n    $('#cg-hdr',r).value='Link: <'+res+'>; rel=\"canonical\"';\r\n    pill($('#cg-pill',r),'good',chg.length?chg.length+' change'+(chg.length>1?'s':'')+' made':'Already clean');\r\n    $('#cg-chg',r).innerHTML=chg.length?'<span class=\"lbl\">What changed<\/span>'+issues(chg.map(function(c){return {l:'idle',t:c}})):'';\r\n  }\r\n  r.addEventListener('input',run);r.addEventListener('change',run);run();wireCopy(r,'#cg-copy','#cg-tag');\r\n}});\r\n\r\n\/* ===== Hreflang Tag Generator ===== *\/\r\ntool({id:'hreflang-tag-generator',name:'Hreflang Tag Generator',cat:'tags',mode:'here',kw:'hreflang language country international multilingual x-default alternate',\r\ndesc:'Create hreflang tags for sites in several languages or countries, with a sitemap version.',\r\ntpl:`<div class=\"split\"><div class=\"col\">\r\n<span class=\"lbl\">Language versions of one page<\/span>\r\n<div class=\"rows\" id=\"hl-rows\"><\/div>\r\n<div class=\"btn-row\"><button type=\"button\" class=\"btn ghost sm\" id=\"hl-add\">Add a version<\/button><\/div>\r\n<datalist id=\"hl-codes\"><option value=\"en\"><option value=\"en-IN\"><option value=\"en-US\"><option value=\"en-GB\"><option value=\"en-AU\"><option value=\"hi\"><option value=\"hi-IN\"><option value=\"ta-IN\"><option value=\"bn-IN\"><option value=\"mr-IN\"><option value=\"ar-AE\"><option value=\"fr-FR\"><option value=\"de-DE\"><option value=\"es-ES\"><option value=\"es-MX\"><option value=\"pt-BR\"><option value=\"x-default\"><\/datalist>\r\n<p class=\"hint\">Language codes are two letters (en). Add a region with a capital country code (en-IN). Use x-default for visitors who match no version.<\/p>\r\n<\/div><div class=\"col\"><div class=\"out-box\"><span id=\"hl-pill\" class=\"pill good\">Ready<\/span>\r\n<label class=\"lbl\" for=\"hl-tags\">Tags for the head of every version<\/label><textarea id=\"hl-tags\" class=\"code\" rows=\"8\" readonly><\/textarea>\r\n<div class=\"btn-row\"><button type=\"button\" class=\"btn primary\" id=\"hl-copy\">Copy tags<\/button><button type=\"button\" class=\"btn ghost\" id=\"hl-copy2\">Copy sitemap version<\/button><\/div>\r\n<textarea id=\"hl-map\" class=\"code\" rows=\"6\" readonly aria-label=\"Sitemap entry\"><\/textarea><\/div><div id=\"hl-iss\"><\/div><\/div><\/div>`,\r\nnote:'Every version must list all versions, including itself, and each page must link back. Paste the same block into the head of each page.',\r\ninit:function(r){\r\n  var rows=[{c:'en-IN',u:'https:\/\/www.example.com\/en-in\/'},{c:'hi-IN',u:'https:\/\/www.example.com\/hi-in\/'},{c:'en-US',u:'https:\/\/www.example.com\/en-us\/'},{c:'x-default',u:'https:\/\/www.example.com\/'}];\r\n  function draw(){\r\n    $('#hl-rows',r).innerHTML=rows.map(function(x,i){return '<div class=\"rowline\"><input type=\"text\" list=\"hl-codes\" data-i=\"'+i+'\" data-k=\"c\" value=\"'+esc(x.c)+'\" aria-label=\"Language code '+(i+1)+'\" spellcheck=\"false\"><input type=\"text\" data-i=\"'+i+'\" data-k=\"u\" value=\"'+esc(x.u)+'\" aria-label=\"Address '+(i+1)+'\" spellcheck=\"false\"><button type=\"button\" class=\"btn ghost sm\" data-del=\"'+i+'\" aria-label=\"Remove version '+(i+1)+'\">Remove<\/button><\/div>'}).join('');\r\n  }\r\n  function run(){\r\n    var iss=[],seen={},valid=rows.filter(function(x){return x.c.trim()&&x.u.trim()});\r\n    rows.forEach(function(x,i){\r\n      var c=x.c.trim(),u=x.u.trim();if(!c&&!u)return;\r\n      if(!c||!u){iss.push({l:'bad',t:'Version '+(i+1)+' is incomplete',d:'Add both a code and an address.'});return}\r\n      if(c!=='x-default'&&!\/^[a-z]{2,3}(-[A-Za-z]{4})?(-([A-Za-z]{2}|\\d{3}))?$\/.test(c))iss.push({l:'bad',t:'\u201c'+c+'\u201d is not a valid code',d:'Use a format like en, en-IN or zh-Hant.'});\r\n      if(\/^[a-z]{2}-[a-z]{2}$\/.test(c)&&c!=='en-uk')iss.push({l:'warn',t:c+': write the country in capitals',d:'Use '+c.slice(0,3)+c.slice(3).toUpperCase()+'.'});\r\n      if(c.toLowerCase()==='en-uk')iss.push({l:'bad',t:'en-UK is wrong',d:'The United Kingdom code is en-GB.'});\r\n      if(seen[c.toLowerCase()])iss.push({l:'bad',t:'Duplicate code '+c});seen[c.toLowerCase()]=1;\r\n      if(!\/^https?:\\\/\\\/\/i.test(u))iss.push({l:'bad',t:'Version '+(i+1)+': use a full address',d:'Start with https:\/\/.'});\r\n    });\r\n    if(valid.length&&!valid.some(function(x){return x.c.trim()==='x-default'}))iss.push({l:'warn',t:'No x-default version',d:'Add one for visitors whose language you do not cover.'});\r\n    if(valid.length<2)iss.push({l:'warn',t:'Add at least two versions'});\r\n    var tags=valid.map(function(x){return '<link rel=\"alternate\" hreflang=\"'+esc(x.c.trim())+'\" href=\"'+esc(x.u.trim())+'\">'}).join('\\n');\r\n    $('#hl-tags',r).value=tags;\r\n    $('#hl-map',r).value=valid.map(function(x){return '<xhtml:link rel=\"alternate\" hreflang=\"'+esc(x.c.trim())+'\" href=\"'+esc(x.u.trim())+'\"\/>'}).join('\\n');\r\n    var s=summary(iss);pill($('#hl-pill',r),s[0],s[1]);$('#hl-iss',r).innerHTML=iss.length?issues(iss):'';\r\n  }\r\n  r.addEventListener('input',function(e){var t=e.target,i=t.getAttribute('data-i');if(i!==null){rows[+i][t.getAttribute('data-k')]=t.value;run()}});\r\n  r.addEventListener('click',function(e){\r\n    var b=e.target.closest('[data-del]');if(b){rows.splice(+b.getAttribute('data-del'),1);draw();run()}\r\n  });\r\n  $('#hl-add',r).addEventListener('click',function(){rows.push({c:'',u:'https:\/\/www.example.com\/'});draw();run()});\r\n  draw();run();wireCopy(r,'#hl-copy','#hl-tags');wireCopy(r,'#hl-copy2','#hl-map');\r\n}});\r\n\r\n\/* ===== Redirect Checker ===== *\/\r\ntool({id:'redirect-checker',name:'Redirect Checker',cat:'tags',mode:'paste',kw:'redirect chain 301 302 307 308 loop trace curl hops',\r\ndesc:'Trace a redirect chain from curl output and spot loops, long chains and temporary redirects.',\r\ntpl:`<div class=\"split\"><div class=\"col\">\r\n<div class=\"field\"><label for=\"rc-u\">Starting address<\/label><input type=\"text\" id=\"rc-u\" value=\"http:\/\/example.com\/old-page\" spellcheck=\"false\"><\/div>\r\n${cmdBox('curl -sIL -o \/dev\/null -D - http:\/\/example.com\/old-page','rc-cmd')}\r\n<div class=\"field\"><label for=\"rc-t\">Output of the command <span class=\"opt\">(example loaded)<\/span><\/label><textarea id=\"rc-t\" class=\"code\" rows=\"13\">HTTP\/1.1 301 Moved Permanently\r\nLocation: https:\/\/example.com\/old-page\r\n\r\nHTTP\/2 302 Found\r\nLocation: https:\/\/www.example.com\/old-page\r\n\r\nHTTP\/2 301\r\nLocation: https:\/\/www.example.com\/new-page\/\r\n\r\nHTTP\/2 200\r\ncontent-type: text\/html; charset=UTF-8<\/textarea><\/div>\r\n<p class=\"hint\">Replace the address in the command with your own, run it in a terminal (Windows 10 and later include curl) and paste the result. You can also copy the Location headers from the Network tab in browser developer tools.<\/p>\r\n<\/div><div class=\"col\"><span id=\"rc-pill\" class=\"pill idle\">Checking<\/span><div class=\"stats\" id=\"rc-stats\"><\/div><div id=\"rc-out\" aria-live=\"polite\"><\/div><\/div><\/div>`,\r\nnote:'Aim for one redirect at most. Use 301 or 308 for permanent moves. 302 and 307 tell search engines the move is temporary.',\r\ninit:function(r){\r\n  function run(){\r\n    var start=normUrl($('#rc-u',r).value),txt=$('#rc-t',r).value;\r\n    var blocks=txt.replace(\/\\r\/g,'').split(\/\\n\\s*\\n\/).filter(function(b){return \/^\\s*HTTP\\\/\/im.test(b)});\r\n    if(!blocks.length||!start){pill($('#rc-pill',r),'idle','Add a starting address and paste the command output');$('#rc-out',r).innerHTML='';$('#rc-stats',r).innerHTML='';return}\r\n    var hops=[],cur=start,iss=[],seen={};\r\n    blocks.forEach(function(b){\r\n      var m=b.match(\/^\\s*HTTP\\\/[\\d.]+\\s+(\\d{3})\/m),code=m?+m[1]:0,lm=b.match(\/^location:\\s*(.+)$\/im),loc=lm?lm[1].trim():'',next=null;\r\n      if(loc){try{next=new URL(loc,cur)}catch(e){}}\r\n      hops.push({url:cur.href,code:code,to:next?next.href:''});\r\n      if(next)cur=next;\r\n    });\r\n    var redirects=hops.filter(function(h){return h.code>=300&&h.code<400}).length,last=hops[hops.length-1];\r\n    hops.forEach(function(h){if(seen[h.url])iss.push({l:'bad',t:'Redirect loop',d:h.url+' appears more than once.'});seen[h.url]=1});\r\n    if(last.code>=300&&last.code<400)iss.push({l:'warn',t:'The trace ends on a redirect',d:'The output may be cut short. Run the command again with -L to follow every hop.'});\r\n    else if(last.code!==200)iss.push({l:'bad',t:'The chain ends with status '+last.code,d:'Visitors and search engines do not reach a working page.'});\r\n    if(redirects>=5)iss.push({l:'bad',t:redirects+' redirects in a row',d:'Google follows up to 10 hops, but each one slows the page and dilutes signals. Point the first address straight to the final one.'});\r\n    else if(redirects>=2)iss.push({l:'warn',t:redirects+' redirects in a chain',d:'Combine them so the first address goes straight to the final page.'});\r\n    var temp=hops.filter(function(h){return h.code===302||h.code===303||h.code===307});\r\n    if(temp.length)iss.push({l:'warn',t:temp.length+' temporary redirect'+(temp.length>1?'s':''),d:'If the move is permanent, use 301 or 308 so ranking signals transfer.'});\r\n    hops.forEach(function(h){if(h.to&&h.code>=300){try{var a=new URL(h.url),b=new URL(h.to);if(a.protocol==='http:'&&b.protocol==='https:')iss.push({l:'idle',t:'http to https upgrade',d:h.url+' \u2192 '+h.to});if(a.hostname.replace(\/^www\\.\/,'')===b.hostname.replace(\/^www\\.\/,'')&&a.hostname!==b.hostname)iss.push({l:'idle',t:'www change',d:a.hostname+' \u2192 '+b.hostname})}catch(e){}}});\r\n    if(redirects===0&&last.code===200)iss.push({l:'good',t:'No redirect. The address loads directly'});\r\n    if(redirects===1&&last.code===200&&!temp.length)iss.push({l:'good',t:'A single permanent redirect to a working page'});\r\n    var s=summary(iss.filter(function(i){return i.l!=='idle'}));pill($('#rc-pill',r),s[0],s[1]);\r\n    $('#rc-stats',r).innerHTML='<div class=\"stat key\"><span>Redirects<\/span><strong>'+redirects+'<\/strong><\/div><div class=\"stat\"><span>Final status<\/span><strong>'+last.code+'<\/strong><\/div><div class=\"stat\"><span>Final address<\/span><strong style=\"font-size:13px;font-family:var(--font-mono);font-weight:500\">'+esc(last.to||last.url)+'<\/strong><\/div>';\r\n    $('#rc-out',r).innerHTML='<div class=\"tbl\"><table><thead><tr><th>Hop<\/th><th>Status<\/th><th>Address<\/th><\/tr><\/thead><tbody>'+hops.map(function(h,i){var c=h.code,k=c>=200&&c<300?'good':c>=300&&c<400?(c===301||c===308?'idle':'warn'):'bad';return '<tr><td>'+(i+1)+'<\/td><td><span class=\"pill '+k+'\">'+c+'<\/span><\/td><td class=\"mono\">'+esc(h.url)+(h.to?'<br>\u2192 '+esc(h.to):'')+'<\/td><\/tr>'}).join('')+'<\/tbody><\/table><\/div>'+issues(iss);\r\n  }\r\n  r.addEventListener('input',function(){var s=normUrl($('#rc-u',r).value);if(s)$('#rc-cmd',r).value='curl -sIL -o \/dev\/null -D - '+s.href;run()});run();\r\n}});\r\n\r\n\/* ===== .htaccess Redirect Generator ===== *\/\r\ntool({id:'htaccess-redirect-generator',name:'.htaccess Redirect Generator',cat:'tags',mode:'here',kw:'htaccess apache redirect rewrite https www trailing slash 301 gzip cache 404',\r\ndesc:'Build Apache rules for https, www, trailing slashes, page redirects, compression and caching.',\r\ntpl:`<div class=\"split\"><div class=\"col\">\r\n<div class=\"field\"><label for=\"ht-d\">Your domain <span class=\"opt\">(without www)<\/span><\/label><input type=\"text\" id=\"ht-d\" value=\"example.com\" spellcheck=\"false\"><\/div>\r\n<div class=\"checks\"><label class=\"check\"><input type=\"checkbox\" id=\"ht-https\" checked><span>Redirect http to https<\/span><\/label><\/div>\r\n<div class=\"field\"><label for=\"ht-www\">Preferred host<\/label><select id=\"ht-www\"><option value=\"keep\">Do not change<\/option><option value=\"www\">Use www.example.com<\/option><option value=\"non\" selected>Use example.com<\/option><\/select><\/div>\r\n<div class=\"field\"><label for=\"ht-slash\">Trailing slash<\/label><select id=\"ht-slash\"><option value=\"keep\" selected>Do not change<\/option><option value=\"remove\">Remove from addresses<\/option><option value=\"add\">Add to addresses<\/option><\/select><\/div>\r\n<div class=\"field\"><label for=\"ht-map\">Page redirects <span class=\"opt\">(old path, then new path or full address)<\/span><\/label><textarea id=\"ht-map\" class=\"code\" rows=\"5\">\/old-page \/new-page\/\r\n\/services.html \/services\/\r\n\/blog\/2020\/seo-tips https:\/\/www.example.com\/blog\/seo-tips\/<\/textarea><\/div>\r\n<div class=\"row2\"><div class=\"field\"><label for=\"ht-type\">Redirect type<\/label><select id=\"ht-type\"><option value=\"301\" selected>301 permanent<\/option><option value=\"302\">302 temporary<\/option><\/select><\/div>\r\n<div class=\"field\"><label for=\"ht-404\">Custom 404 page<\/label><input type=\"text\" id=\"ht-404\" value=\"\/404.html\" spellcheck=\"false\"><\/div><\/div>\r\n<div class=\"checks\"><span class=\"lbl\">Extras<\/span>\r\n<label class=\"check\"><input type=\"checkbox\" id=\"ht-gz\" checked><span>Compress text files (gzip)<\/span><\/label>\r\n<label class=\"check\"><input type=\"checkbox\" id=\"ht-cache\" checked><span>Browser caching for images, CSS and JS<\/span><\/label>\r\n<label class=\"check\"><input type=\"checkbox\" id=\"ht-sec\"><span>Basic security headers<\/span><\/label>\r\n<label class=\"check\"><input type=\"checkbox\" id=\"ht-idx\" checked><span>Turn off folder listings<\/span><\/label><\/div>\r\n<\/div><div class=\"col\"><div class=\"out-box\"><span id=\"ht-pill\" class=\"pill good\">Ready<\/span><label class=\"lbl\" for=\"ht-out\">Your .htaccess<\/label><textarea id=\"ht-out\" class=\"code\" rows=\"22\" readonly><\/textarea><div class=\"btn-row\"><button type=\"button\" class=\"btn primary\" id=\"ht-copy\">Copy rules<\/button><\/div><\/div>\r\n<div id=\"ht-iss\"><\/div><\/div><\/div>`,\r\nnote:'Back up your current .htaccess first and test on a staging copy. A mistake can cause a 500 error. This works on Apache servers only, not Nginx or Vercel.',\r\ninit:function(r){\r\n  function run(){\r\n    var d=($('#ht-d',r).value.trim().replace(\/^https?:\\\/\\\/\/i,'').replace(\/^www\\.\/i,'').replace(\/\\\/.*$\/,'')),L=[],iss=[];\r\n    var https=$('#ht-https',r).checked,host=$('#ht-www',r).value,sl=$('#ht-slash',r).value;\r\n    if(!d||d.indexOf('.')<0)iss.push({l:'bad',t:'Enter your domain, such as example.com'});\r\n    var type=$('#ht-type',r).value,maps=[];\r\n    $('#ht-map',r).value.split(\/\\n\/).forEach(function(line){\r\n      var p=line.trim().split(\/\\s+\/);if(p.length<2)return;\r\n      var o=p[0].charAt(0)==='\/'?p[0]:'\/'+p[0],n=p[1];if(!\/^https?:\\\/\\\/\/i.test(n)&&n.charAt(0)!=='\/')n='\/'+n;\r\n      if(\/[?#]\/.test(o)){iss.push({l:'warn',t:'Skipped '+o,d:'Redirect rules cannot match query strings or fragments. Use a RewriteCond with %{QUERY_STRING} instead.'});return}\r\n      maps.push('Redirect '+type+' '+o+' '+n);\r\n    });\r\n    L.push('# Generated by Onground Media SEO Tools','');\r\n    if(maps.length){L.push('# Page redirects');L=L.concat(maps);L.push('')}\r\n    if($('#ht-404',r).value.trim()){L.push('ErrorDocument 404 '+$('#ht-404',r).value.trim(),'')}\r\n    if(https||host!=='keep'||sl!=='keep'){\r\n      L.push('<IfModule mod_rewrite.c>','RewriteEngine On','');\r\n      if(https||host!=='keep'){\r\n        var scheme=https?'https':'%{REQUEST_SCHEME}',conds=[];\r\n        if(https)conds.push('RewriteCond %{HTTPS} off');\r\n        if(host==='non')conds.push('RewriteCond %{HTTP_HOST} ^www\\\\. [NC]');\r\n        if(host==='www')conds.push('RewriteCond %{HTTP_HOST} !^www\\\\. [NC]');\r\n        for(var i=0;i<conds.length-1;i++)conds[i]+=' [OR]';\r\n        L.push(host==='keep'?'# Force https':'# Force '+(https?'https and ':'')+(host==='www'?'www':'non-www')+' in one step');\r\n        L=L.concat(conds);\r\n        L.push('RewriteRule ^ '+scheme+':\/\/'+(host==='keep'?'%{HTTP_HOST}':host==='www'?'www.'+d:d)+'%{REQUEST_URI} [L,R=301]','');\r\n      }\r\n      if(sl==='remove')L.push('# Remove trailing slash','RewriteCond %{REQUEST_FILENAME} !-d','RewriteRule ^(.+)\/$ \/$1 [L,R=301]','');\r\n      if(sl==='add')L.push('# Add trailing slash','RewriteCond %{REQUEST_FILENAME} !-f','RewriteCond %{REQUEST_URI} !\\\\.[a-zA-Z0-9]{1,5}$','RewriteCond %{REQUEST_URI} !\/$','RewriteRule ^(.*)$ \/$1\/ [L,R=301]','');\r\n      L.push('<\/IfModule>','');\r\n    }\r\n    if($('#ht-gz',r).checked)L.push('# Compression','<IfModule mod_deflate.c>','AddOutputFilterByType DEFLATE text\/html text\/plain text\/css text\/xml application\/xml application\/xhtml+xml application\/javascript text\/javascript application\/json application\/rss+xml image\/svg+xml','<\/IfModule>','');\r\n    if($('#ht-cache',r).checked)L.push('# Browser caching','<IfModule mod_expires.c>','ExpiresActive On','ExpiresByType image\/jpeg \"access plus 1 year\"','ExpiresByType image\/png \"access plus 1 year\"','ExpiresByType image\/webp \"access plus 1 year\"','ExpiresByType image\/svg+xml \"access plus 1 year\"','ExpiresByType text\/css \"access plus 1 month\"','ExpiresByType application\/javascript \"access plus 1 month\"','ExpiresByType text\/html \"access plus 0 seconds\"','<\/IfModule>','');\r\n    if($('#ht-sec',r).checked)L.push('# Security headers','<IfModule mod_headers.c>','Header always set X-Content-Type-Options \"nosniff\"','Header always set X-Frame-Options \"SAMEORIGIN\"','Header always set Referrer-Policy \"strict-origin-when-cross-origin\"','<\/IfModule>','');\r\n    if($('#ht-idx',r).checked)L.push('# No folder listings','Options -Indexes','');\r\n    $('#ht-out',r).value=L.join('\\n').replace(\/\\n+$\/,'\\n');\r\n    if(https&&host!=='keep')iss.push({l:'good',t:'https and host rules are combined',d:'Visitors reach the final address in one redirect, not two.'});\r\n    if(sl!=='keep'&&host!=='keep'&&https)iss.push({l:'idle',t:'Trailing slash adds a second hop for some addresses',d:'Link to the final form internally to avoid it.'});\r\n    var s=summary(iss.filter(function(i){return i.l==='bad'||i.l==='warn'}));pill($('#ht-pill',r),s[0],s[0]==='good'?'Ready to upload':s[1]);\r\n    $('#ht-iss',r).innerHTML=iss.length?issues(iss):'';\r\n  }\r\n  r.addEventListener('input',run);r.addEventListener('change',run);run();wireCopy(r,'#ht-copy','#ht-out');\r\n}});\r\n\r\n\/* ===== HTTP Status Code Checker (reference) ===== *\/\r\nvar CODES=[\r\n[200,'OK','Success','The page loaded normally.','Ideal. This is the only status that gets indexed normally.','Nothing to fix.'],\r\n[201,'Created','Success','A new resource was created, usually after a form or API request.','Not used for pages that search engines crawl.','Nothing to fix.'],\r\n[204,'No Content','Success','The request worked but there is nothing to show.','Treated as an empty page, so it is not indexed.','Return 200 with content for real pages.'],\r\n[206,'Partial Content','Success','Only part of the file was sent, as with video or large downloads.','Normal for media. Not a problem.','Nothing to fix.'],\r\n[301,'Moved Permanently','Redirect','The page has a new permanent address.','Passes ranking signals to the new address. Use for permanent moves.','Update internal links to the final address to avoid extra hops.'],\r\n[302,'Found','Redirect','The page is temporarily somewhere else.','Search engines keep the old address indexed. Wrong choice for a permanent move.','Change to 301 if the move is permanent.'],\r\n[303,'See Other','Redirect','Go to another address with a GET request, often after a form post.','Rare for pages.','Normally nothing to fix.'],\r\n[304,'Not Modified','Redirect','The saved copy is still current.','Good. Saves crawl budget.','Nothing to fix.'],\r\n[307,'Temporary Redirect','Redirect','Temporary move that keeps the request method.','Treated like 302. Not for permanent moves.','Use 308 or 301 for permanent moves.'],\r\n[308,'Permanent Redirect','Redirect','Permanent move that keeps the request method.','Treated like 301.','Nothing to fix.'],\r\n[400,'Bad Request','Client error','The server could not understand the request.','Pages are not indexed. Often caused by bad characters in URLs.','Fix malformed links and check for oversized cookies or headers.'],\r\n[401,'Unauthorized','Client error','A login is required.','Search engines cannot crawl the page, so it drops out of results.','Remove the login for public pages, or leave private pages blocked on purpose.'],\r\n[403,'Forbidden','Client error','The server refuses access.','The page is not indexed. A firewall may be blocking Googlebot by mistake.','Check firewall, security plugin and file permission rules.'],\r\n[404,'Not Found','Client error','No page exists at this address.','Fine for removed pages. Many 404s from internal links waste crawl budget.','Redirect with 301 if a replacement exists. Fix broken internal links.'],\r\n[405,'Method Not Allowed','Client error','The request type is not supported for this address.','Crawlers use GET, so a 405 for GET blocks indexing.','Allow GET on public pages.'],\r\n[408,'Request Timeout','Client error','The server gave up waiting for the request.','Repeated timeouts reduce crawling.','Check server load and slow connections.'],\r\n[410,'Gone','Client error','The page was removed on purpose and will not return.','Drops out of the index slightly faster than a 404.','Use for permanently deleted content.'],\r\n[429,'Too Many Requests','Client error','Rate limit reached.','Googlebot slows down when it sees many 429s.','Raise limits for known crawlers or fix the bot traffic.'],\r\n[451,'Unavailable For Legal Reasons','Client error','Blocked because of a legal demand.','Not indexed.','Only for legally required blocks.'],\r\n[500,'Internal Server Error','Server error','The server hit an unexpected problem.','Frequent 500s cause pages to drop out of the index.','Check the server error log, plugins and recent changes.'],\r\n[501,'Not Implemented','Server error','The server does not support the request.','Not indexed.','Check server configuration.'],\r\n[502,'Bad Gateway','Server error','A proxy or CDN got a bad reply from the origin server.','Treated as a temporary server error.','Check the origin server, PHP workers and CDN settings.'],\r\n[503,'Service Unavailable','Server error','The server is down or overloaded, often during maintenance.','Safe for short maintenance. Search engines retry later. Long outages cost rankings.','Add a Retry-After header during planned downtime.'],\r\n[504,'Gateway Timeout','Server error','The upstream server took too long.','Repeated 504s slow crawling.','Speed up slow pages and database queries.']];\r\ntool({id:'http-status-code-checker',name:'HTTP Status Code Checker',cat:'server',mode:'here',kw:'http status code 200 301 302 404 410 500 503 meaning lookup seo',\r\ndesc:'Look up what any HTTP status code means, how it affects SEO and how to fix it.',\r\ntpl:`<div class=\"col\"><div class=\"split\" style=\"grid-template-columns:repeat(auto-fit,minmax(min(100%,240px),1fr))\"><div class=\"field\"><label for=\"sc-q\">Search by code or word<\/label><input type=\"text\" id=\"sc-q\" value=\"\" placeholder=\"for example 404, redirect, timeout\" autocomplete=\"off\"><\/div>\r\n<div class=\"field\"><span class=\"lbl\">Group<\/span><div class=\"seg\" id=\"sc-g\" role=\"group\" aria-label=\"Status group\"><button type=\"button\" data-g=\"\" aria-pressed=\"true\">All<\/button><button type=\"button\" data-g=\"Success\" aria-pressed=\"false\">2xx<\/button><button type=\"button\" data-g=\"Redirect\" aria-pressed=\"false\">3xx<\/button><button type=\"button\" data-g=\"Client error\" aria-pressed=\"false\">4xx<\/button><button type=\"button\" data-g=\"Server error\" aria-pressed=\"false\">5xx<\/button><\/div><\/div><\/div>\r\n<div id=\"sc-out\" aria-live=\"polite\"><\/div><\/div>`,\r\nnote:'To see the status of a live page, open browser developer tools, choose the Network tab, reload and read the Status column. Or run: curl -sI https:\/\/example.com',\r\ninit:function(r){\r\n  var g='';\r\n  function run(){\r\n    var q=$('#sc-q',r).value.trim().toLowerCase(),l=CODES.filter(function(c){return (!g||c[2]===g)&&(!q||(c.join(' ').toLowerCase().indexOf(q)>=0))});\r\n    $('#sc-out',r).innerHTML=l.length?'<div class=\"tbl\"><table><thead><tr><th>Code<\/th><th>Meaning<\/th><th>SEO effect<\/th><th>What to do<\/th><\/tr><\/thead><tbody>'+l.map(function(c){var k=c[0]<300?'good':c[0]<400?'idle':c[0]<500?'warn':'bad';return '<tr><td><span class=\"pill '+k+'\">'+c[0]+'<\/span><\/td><td><b>'+esc(c[1])+'<\/b><br><span class=\"hint\">'+esc(c[3])+'<\/span><\/td><td>'+esc(c[4])+'<\/td><td>'+esc(c[5])+'<\/td><\/tr>'}).join('')+'<\/tbody><\/table><\/div>':'<div class=\"empty\">No status code matches \u201c'+esc(q)+'\u201d.<\/div>';\r\n  }\r\n  $('#sc-q',r).addEventListener('input',run);\r\n  $('#sc-g',r).addEventListener('click',function(e){var b=e.target.closest('button');if(!b)return;g=b.getAttribute('data-g');$$('#sc-g button',r).forEach(function(x){x.setAttribute('aria-pressed',x===b)});run()});\r\n  run();\r\n}});\r\n\r\n\/* ===== HTTP Header Checker ===== *\/\r\nfunction parseHeaders(t){\r\n  var h={},status='',lines=String(t).replace(\/\\r\/g,'').split('\\n'),order=[];\r\n  lines.forEach(function(l){\r\n    l=l.trim();if(!l)return;\r\n    if(\/^HTTP\\\/[\\d.]+\/i.test(l)){status=l;h={};order=[];return}\r\n    var i=l.indexOf(':');if(i<1)return;\r\n    var k=l.slice(0,i).trim().toLowerCase(),v=l.slice(i+1).trim();\r\n    if(h[k]===undefined){h[k]=v;order.push(k)}else h[k]+=', '+v;\r\n  });\r\n  return {h:h,status:status,order:order};\r\n}\r\ntool({id:'http-header-checker',name:'HTTP Header Checker',cat:'server',mode:'paste',kw:'http headers response server security hsts csp cache x-robots-tag cookie',\r\ndesc:'Read response headers and get a plain-language review of security, caching and SEO settings.',\r\ntpl:`<div class=\"split\"><div class=\"col\">\r\n${cmdBox('curl -sI https:\/\/www.example.com\/','hc-cmd')}\r\n<div class=\"field\"><label for=\"hc-t\">Response headers <span class=\"opt\">(example loaded)<\/span><\/label><textarea id=\"hc-t\" class=\"code\" rows=\"16\">HTTP\/2 200\r\ndate: Wed, 14 Oct 2026 02:10:00 GMT\r\ncontent-type: text\/html\r\nserver: Apache\/2.4.41 (Ubuntu)\r\nx-powered-by: PHP\/7.4.3\r\nstrict-transport-security: max-age=300\r\nx-content-type-options: nosniff\r\ncache-control: no-cache\r\nx-robots-tag: noindex, nofollow\r\nset-cookie: session=abc123; Path=\/\r\nlink: <https:\/\/www.example.com\/>; rel=\"canonical\"<\/textarea><\/div><\/div>\r\n<div class=\"col\"><span id=\"hc-pill\" class=\"pill idle\">Checking<\/span><div class=\"stats\" id=\"hc-stats\"><\/div><div id=\"hc-out\" aria-live=\"polite\"><\/div><\/div><\/div>`,\r\nnote:'You can also copy headers from browser developer tools: Network tab, click the page request, then Headers.',\r\ninit:function(r){\r\n  function run(){\r\n    var p=parseHeaders($('#hc-t',r).value),h=p.h,iss=[],g=function(k){return h[k]};\r\n    if(!p.order.length){pill($('#hc-pill',r),'idle','Paste the response headers');$('#hc-out',r).innerHTML='';$('#hc-stats',r).innerHTML='';return}\r\n    var sm=p.status.match(\/\\s(\\d{3})\/),code=sm?+sm[1]:0;\r\n    if(code)iss.push({l:code===200?'good':code<400?'idle':'bad',t:'Status '+code});\r\n    var xr=g('x-robots-tag');\r\n    if(xr)iss.push(\/noindex|none\/i.test(xr)?{l:'bad',t:'X-Robots-Tag: '+xr,d:'This tells search engines not to index the page. Remove it if the page should rank.'}:{l:'idle',t:'X-Robots-Tag: '+xr});\r\n    var sec=0;\r\n    var hsts=g('strict-transport-security');\r\n    if(!hsts)iss.push({l:'warn',t:'No Strict-Transport-Security',d:'Add HSTS so browsers always use https.'});\r\n    else{sec++;var ma=(hsts.match(\/max-age=(\\d+)\/)||[])[1];if(ma&&+ma<15552000)iss.push({l:'warn',t:'HSTS max-age is short ('+ma+' seconds)',d:'Use at least 15552000 (180 days), ideally 31536000 (one year).'});else iss.push({l:'good',t:'HSTS is set'})}\r\n    if(g('content-security-policy')){sec++;iss.push({l:'good',t:'Content-Security-Policy is set'})}else iss.push({l:'warn',t:'No Content-Security-Policy',d:'Limits where scripts can load from. Add it carefully and test.'});\r\n    if(\/nosniff\/i.test(g('x-content-type-options')||'')){sec++}else iss.push({l:'warn',t:'No X-Content-Type-Options: nosniff'});\r\n    if(g('x-frame-options')||\/frame-ancestors\/i.test(g('content-security-policy')||'')){sec++}else iss.push({l:'warn',t:'No clickjacking protection',d:'Add X-Frame-Options: SAMEORIGIN.'});\r\n    if(g('referrer-policy')){sec++}else iss.push({l:'idle',t:'No Referrer-Policy',d:'Suggested: strict-origin-when-cross-origin.'});\r\n    if(g('permissions-policy')){sec++}else iss.push({l:'idle',t:'No Permissions-Policy'});\r\n    var sv=g('server');if(sv&&\/\\d+\\.\\d+\/.test(sv))iss.push({l:'warn',t:'Server reveals its version: '+sv,d:'Hide the version number so attackers cannot target known flaws.'});\r\n    var xp=g('x-powered-by');if(xp)iss.push({l:'warn',t:'X-Powered-By reveals '+xp,d:'Remove this header.'});\r\n    var cc=g('cache-control');\r\n    if(!cc&&!g('expires'))iss.push({l:'warn',t:'No caching headers',d:'Add Cache-Control so browsers can reuse files.'});\r\n    else if(cc&&\/no-store|no-cache\/i.test(cc))iss.push({l:'idle',t:'Cache-Control: '+cc,d:'Fine for pages that change per visitor. Static files should be cached longer.'});\r\n    else if(cc)iss.push({l:'good',t:'Cache-Control: '+cc});\r\n    var ct=g('content-type')||'';\r\n    if(\/text\\\/html\/i.test(ct)&&!\/charset\/i.test(ct))iss.push({l:'warn',t:'No charset in Content-Type',d:'Add charset=UTF-8 so text displays correctly.'});\r\n    var ce=g('content-encoding');\r\n    if(\/text|json|javascript|xml\/i.test(ct)){ce?iss.push({l:'good',t:'Compressed with '+ce}):iss.push({l:'warn',t:'Response is not compressed',d:'Enable gzip or Brotli to cut transfer size.'})}\r\n    if(g('set-cookie')&&!\/secure\/i.test(g('set-cookie')))iss.push({l:'warn',t:'Cookie without the Secure flag'});\r\n    if(g('set-cookie')&&!\/httponly\/i.test(g('set-cookie')))iss.push({l:'warn',t:'Cookie without HttpOnly'});\r\n    var lk=g('link');if(lk&&\/rel=\"?canonical\/i.test(lk))iss.push({l:'idle',t:'Canonical set in the Link header',d:lk});\r\n    var loc=g('location');if(loc)iss.push({l:'idle',t:'Redirects to '+loc});\r\n    var s=summary(iss);pill($('#hc-pill',r),s[0],s[1]);\r\n    $('#hc-stats',r).innerHTML='<div class=\"stat key\"><span>Security headers<\/span><strong>'+sec+' of 6<\/strong><\/div><div class=\"stat\"><span>Headers read<\/span><strong>'+p.order.length+'<\/strong><\/div>'+(code?'<div class=\"stat\"><span>Status<\/span><strong>'+code+'<\/strong><\/div>':'');\r\n    $('#hc-out',r).innerHTML=issues(iss)+'<span class=\"lbl\" style=\"display:block;margin-top:16px\">All headers<\/span><div class=\"tbl\"><table><tbody>'+p.order.map(function(k){return '<tr><td class=\"mono\"><b>'+esc(k)+'<\/b><\/td><td class=\"mono\">'+esc(h[k])+'<\/td><\/tr>'}).join('')+'<\/tbody><\/table><\/div>';\r\n  }\r\n  $('#hc-t',r).addEventListener('input',run);run();\r\n}});\r\n\r\n\/* ===== SSL Certificate Checker ===== *\/\r\nvar SAMPLE_PEM='-----BEGIN CERTIFICATE-----\\nMIIB\/jCCAaSgAwIBAgIUV896sYlgcZrzjH1YJcIq1lZx+eEwCgYIKoZIzj0EAwIw\\nNjEUMBIGA1UEAwwLZXhhbXBsZS5jb20xHjAcBgNVBAoMFUV4YW1wbGUgU3RvcmUg\\nUHZ0IEx0ZDAeFw0yNjEwMTQwMTUxMTJaFw0yNzAxMTIwMTUxMTJaMDYxFDASBgNV\\nBAMMC2V4YW1wbGUuY29tMR4wHAYDVQQKDBVFeGFtcGxlIFN0b3JlIFB2dCBMdGQw\\nWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAT885gTyucpWOHORV50kvHKXdONFmfX\\ni8CLVYVU1cT4cFuJf1vBhc5Kg5+OkrIbdsC2LYWE3LqrhJHFrX8fGFx9o4GPMIGM\\nMB0GA1UdDgQWBBS9eHCtxiwG\/Oo2sZo02G+Y7sc1vTAfBgNVHSMEGDAWgBS9eHCt\\nxiwG\/Oo2sZo02G+Y7sc1vTAPBgNVHRMBAf8EBTADAQH\/MDkGA1UdEQQyMDCCC2V4\\nYW1wbGUuY29tgg93d3cuZXhhbXBsZS5jb22CEHNob3AuZXhhbXBsZS5jb20wCgYI\\nKoZIzj0EAwIDSAAwRQIgKd+73rY5vVkhXlLFDZmY6hLRUyDFN06XP+PLOpGxMBwC\\nIQCj\/guTrP2RkwamCpIf\/+Wt0F2D9ywOW3O\/AqbmS2KpfQ==\\n-----END CERTIFICATE-----';\r\ntool({id:'ssl-certificate-checker',name:'SSL Certificate Checker',cat:'server',mode:'paste',kw:'ssl tls certificate expiry expire https validity san issuer',\r\ndesc:'Read a certificate to see who issued it, which names it covers and how many days remain.',\r\ntpl:`<div class=\"split\"><div class=\"col\">\r\n<div class=\"field\"><label for=\"ssl-d\">Domain<\/label><input type=\"text\" id=\"ssl-d\" value=\"example.com\" spellcheck=\"false\"><\/div>\r\n<div id=\"ssl-links\"><\/div>\r\n${cmdBox('openssl s_client -connect example.com:443 -servername example.com <\/dev\/null 2>\/dev\/null | openssl x509','ssl-cmd')}\r\n<div class=\"field\"><label for=\"ssl-pem\">Certificate <span class=\"opt\">(paste the output of the command. A self-signed example is loaded)<\/span><\/label><textarea id=\"ssl-pem\" class=\"code\" rows=\"9\" spellcheck=\"false\"><\/textarea><\/div>\r\n<p class=\"hint\">In a browser: click the padlock, open the certificate details and export or copy the certificate.<\/p>\r\n<\/div><div class=\"col\"><span id=\"ssl-pill\" class=\"pill idle\">Checking<\/span><div class=\"stats\" id=\"ssl-stats\"><\/div><div id=\"ssl-out\" aria-live=\"polite\"><\/div><\/div><\/div>`,\r\nnote:'This reads the certificate you paste. It cannot test the live server, the chain or protocol support. Use the SSL Labs link for a full server test.',\r\ninit:function(r){\r\n  $('#ssl-pem',r).value=SAMPLE_PEM;\r\n  function run(){\r\n    var d=hostOf($('#ssl-d',r).value);\r\n    $('#ssl-links',r).innerHTML=d?linkGrid([{h:'https:\/\/www.ssllabs.com\/ssltest\/analyze.html?d='+enc(d)+'&hideResults=on',t:'SSL Labs full server test',s:'Grades the live server for '+d}]):'';\r\n    if(d)$('#ssl-cmd',r).value='openssl s_client -connect '+d+':443 -servername '+d+' <\/dev\/null 2>\/dev\/null | openssl x509';\r\n    var t=$('#ssl-pem',r).value.trim(),iss=[];\r\n    if(!t){pill($('#ssl-pill',r),'idle','Paste a certificate');$('#ssl-out',r).innerHTML='';$('#ssl-stats',r).innerHTML='';return}\r\n    var c;try{c=parseCert(t)}catch(e){pill($('#ssl-pill',r),'bad','Could not read the certificate');$('#ssl-stats',r).innerHTML='';$('#ssl-out',r).innerHTML=issues([{l:'bad',t:'Not a readable certificate',d:String(e.message||e)}]);return}\r\n    var now=Date.now(),left=Math.floor((c.to-now)\/86400000),life=Math.round((c.to-c.from)\/86400000);\r\n    if(now<c.from)iss.push({l:'bad',t:'Not valid yet',d:'Starts '+c.from.toUTCString()});\r\n    if(left<0)iss.push({l:'bad',t:'Expired '+(-left)+' days ago',d:'Visitors see a security warning. Renew now.'});\r\n    else if(left<=14)iss.push({l:'bad',t:'Expires in '+left+' days',d:'Renew immediately.'});\r\n    else if(left<=30)iss.push({l:'warn',t:'Expires in '+left+' days',d:'Renew soon and check that auto-renewal works.'});\r\n    else iss.push({l:'good',t:'Valid for another '+left+' days',d:'Expires '+c.to.toUTCString()});\r\n    if(c.selfSigned)iss.push({l:'bad',t:'Self-signed certificate',d:'Browsers do not trust it. Use a certificate from a public authority such as Let\u2019s Encrypt.'});\r\n    if(life>398)iss.push({l:'warn',t:'Lifetime is '+life+' days',d:'Browsers reject public certificates valid for more than 398 days.'});\r\n    if(d){\r\n      var covered=c.sans.concat(c.subject.cn?[c.subject.cn]:[]).some(function(n){n=n.toLowerCase();return n===d||(n.indexOf('*.')===0&&d.split('.').slice(1).join('.')===n.slice(2))});\r\n      iss.push(covered?{l:'good',t:'Covers '+d}:{l:'bad',t:'Does not cover '+d,d:'Names on the certificate: '+(c.sans.join(', ')||c.subject.cn||'none')+'.'});\r\n    }\r\n    var s=summary(iss);pill($('#ssl-pill',r),s[0],left<0?'Expired':s[0]==='good'?'Certificate looks healthy':s[1]);\r\n    $('#ssl-stats',r).innerHTML='<div class=\"stat key\"><span>Days left<\/span><strong>'+left+'<\/strong><\/div><div class=\"stat\"><span>Key type<\/span><strong>'+esc(c.keyAlg)+'<\/strong><\/div><div class=\"stat\"><span>Lifetime<\/span><strong>'+life+' days<\/strong><\/div>';\r\n    $('#ssl-out',r).innerHTML=issues(iss)+'<div class=\"tbl\" style=\"margin-top:14px\"><table><tbody>'+\r\n      [['Common name',c.subject.cn||'\u2014'],['Organisation',c.subject.o||'\u2014'],['Issued by',(c.issuer.o||'')+(c.issuer.o&&c.issuer.cn?' \u00b7 ':'')+(c.issuer.cn||'\u2014')],['Valid from',c.from.toUTCString()],['Valid until',c.to.toUTCString()],['Names covered',c.sans.join(', ')||'\u2014']].map(function(x){return '<tr><td><b>'+x[0]+'<\/b><\/td><td class=\"mono\">'+esc(x[1])+'<\/td><\/tr>'}).join('')+'<\/tbody><\/table><\/div>';\r\n  }\r\n  r.addEventListener('input',run);run();\r\n}});\r\n\r\n\/* ===== DNS Lookup ===== *\/\r\ntool({id:'dns-lookup',name:'DNS Lookup',cat:'server',mode:'link',kw:'dns a mx txt ns cname records spf dmarc dkim email lookup dig',\r\ndesc:'Look up A, MX, TXT and NS records, then check your SPF and DMARC email records.',\r\ntpl:`<div class=\"split\"><div class=\"col\">\r\n<div class=\"row2\"><div class=\"field\"><label for=\"dn-d\">Domain<\/label><input type=\"text\" id=\"dn-d\" value=\"example.com\" spellcheck=\"false\"><\/div>\r\n<div class=\"field\"><label for=\"dn-t\">Record type<\/label><select id=\"dn-t\"><option>A<\/option><option>AAAA<\/option><option>MX<\/option><option selected>TXT<\/option><option>NS<\/option><option>CNAME<\/option><option>SOA<\/option><option>CAA<\/option><\/select><\/div><\/div>\r\n<div id=\"dn-links\"><\/div>${cmdBox('dig example.com TXT +short','dn-cmd')}\r\n<\/div><div class=\"col\"><span class=\"lbl\">Check your email records<\/span>\r\n<div class=\"field\"><label for=\"dn-txt\">Paste the TXT records <span class=\"opt\">(for the domain, and _dmarc.domain)<\/span><\/label><textarea id=\"dn-txt\" class=\"code\" rows=\"6\" spellcheck=\"false\">\"v=spf1 include:_spf.google.com include:sendgrid.net include:mailgun.org ~all\"\r\n\"v=DMARC1; p=none; rua=mailto:dmarc@example.com\"\r\n\"google-site-verification=abc123\"<\/textarea><\/div>\r\n<span id=\"dn-pill\" class=\"pill idle\">Checking<\/span><div id=\"dn-out\" aria-live=\"polite\"><\/div><\/div><\/div>`,\r\nnote:'Changes to DNS can take from a few minutes up to 48 hours to appear everywhere.',\r\ninit:function(r){\r\n  function links(){\r\n    var d=hostOf($('#dn-d',r).value),t=$('#dn-t',r).value;\r\n    if(!d){$('#dn-links',r).innerHTML='<p class=\"hint\">Enter a domain.<\/p>';return}\r\n    $('#dn-links',r).innerHTML=linkGrid([{h:'https:\/\/toolbox.googleapps.com\/apps\/dig\/#'+t+'\/'+enc(d),t:'Google Admin Toolbox Dig',s:t+' records for '+d},{h:'https:\/\/dnschecker.org\/#'+t+'\/'+enc(d),t:'DNS Checker',s:'Compare answers from servers worldwide'},{h:'https:\/\/mxtoolbox.com\/SuperTool.aspx?action='+enc((t==='A'||t==='AAAA'||t==='NS'||t==='MX'||t==='TXT'||t==='CNAME'?t.toLowerCase():'a')+':'+d)+'&run=toolpage',t:'MXToolbox',s:'Also tests mail server health'}]);\r\n    $('#dn-cmd',r).value='dig '+d+' '+t+' +short';\r\n  }\r\n  function mail(){\r\n    var t=$('#dn-txt',r).value,lines=t.split(\/\\n\/).map(function(s){return s.replace(\/^[\"\\s]+|[\"\\s]+$\/g,'').replace(\/\"\\s*\"\/g,'')}).filter(Boolean),iss=[];\r\n    var spf=lines.filter(function(l){return \/^v=spf1\\b\/i.test(l)}),dm=lines.filter(function(l){return \/^v=DMARC1\\b\/i.test(l)});\r\n    if(!lines.length){pill($('#dn-pill',r),'idle','Paste TXT records');$('#dn-out',r).innerHTML='';return}\r\n    if(!spf.length)iss.push({l:'warn',t:'No SPF record',d:'Add a TXT record starting v=spf1 to say which servers may send your email.'});\r\n    else{\r\n      if(spf.length>1)iss.push({l:'bad',t:spf.length+' SPF records',d:'Only one is allowed. Merge them into a single record.'});\r\n      var s=spf[0],tk=s.split(\/\\s+\/).slice(1),look=0;\r\n      tk.forEach(function(x){var m=x.replace(\/^[+\\-~?]\/,'');if(\/^(include:|exists:|redirect=)\/i.test(m)||\/^(a|mx|ptr)(:|\\\/|$)\/i.test(m))look++});\r\n      iss.push(look>10?{l:'bad',t:look+' DNS lookups in SPF',d:'The limit is 10. Over it, SPF fails. Remove includes or use flattening.'}:{l:look>8?'warn':'good',t:look+' of 10 allowed DNS lookups used'});\r\n      var all=tk.filter(function(x){return \/^[+\\-~?]?all$\/i.test(x)})[0];\r\n      if(!all&&!tk.some(function(x){return \/^redirect=\/i.test(x)}))iss.push({l:'warn',t:'SPF has no \u201call\u201d ending',d:'End with -all (strict) or ~all (soft fail).'});\r\n      else if(all==='-all')iss.push({l:'good',t:'SPF ends with -all (strict)'});\r\n      else if(all==='~all')iss.push({l:'idle',t:'SPF ends with ~all (soft fail)',d:'Common and acceptable. Move to -all once you confirm every sender is listed.'});\r\n      else if(all)iss.push({l:'bad',t:'SPF ends with '+all,d:'This lets anyone send as your domain. Use -all or ~all.'});\r\n      if(tk.some(function(x){return \/^[+\\-~?]?ptr\/i.test(x)}))iss.push({l:'warn',t:'SPF uses ptr',d:'It is deprecated and slow. Remove it.'});\r\n    }\r\n    if(!dm.length)iss.push({l:'warn',t:'No DMARC record in this paste',d:'Add a TXT record at _dmarc.yourdomain.com starting v=DMARC1. Include it in the paste if it already exists.'});\r\n    else{\r\n      var p=(dm[0].match(\/\\bp=(\\w+)\/i)||[])[1];\r\n      iss.push(p==='reject'?{l:'good',t:'DMARC policy is reject'}:p==='quarantine'?{l:'good',t:'DMARC policy is quarantine'}:{l:'warn',t:'DMARC policy is '+(p||'missing'),d:'p=none only monitors. Move to quarantine, then reject, after reviewing reports.'});\r\n      if(!\/\\brua=\/i.test(dm[0]))iss.push({l:'idle',t:'No rua reporting address',d:'Add rua=mailto:... to receive reports.'});\r\n    }\r\n    if(lines.some(function(l){return \/^v=DKIM1\/i.test(l)}))iss.push({l:'good',t:'A DKIM key record is present'});\r\n    var s2=summary(iss);pill($('#dn-pill',r),s2[0],s2[1]);$('#dn-out',r).innerHTML=issues(iss);\r\n  }\r\n  r.addEventListener('input',function(e){if(e.target.id==='dn-txt')mail();else links()});r.addEventListener('change',links);links();mail();\r\n}});\r\n\r\n\/* ===== WHOIS Lookup ===== *\/\r\ntool({id:'whois-lookup',name:'WHOIS Lookup',cat:'server',mode:'link',kw:'whois domain registration registrar expiry age nameservers rdap',\r\ndesc:'Open a registry lookup, then paste the result here to read dates, registrar and status.',\r\ntpl:`<div class=\"split\"><div class=\"col\">\r\n<div class=\"field\"><label for=\"wh-d\">Domain<\/label><input type=\"text\" id=\"wh-d\" value=\"example.com\" spellcheck=\"false\"><\/div>\r\n<div id=\"wh-links\"><\/div>\r\n<div class=\"field\"><label for=\"wh-t\">WHOIS result <span class=\"opt\">(example loaded)<\/span><\/label><textarea id=\"wh-t\" class=\"code\" rows=\"12\" spellcheck=\"false\"><\/textarea><\/div>\r\n<\/div><div class=\"col\"><span id=\"wh-pill\" class=\"pill idle\">Checking<\/span><div class=\"stats\" id=\"wh-stats\"><\/div><div id=\"wh-out\" aria-live=\"polite\"><\/div><\/div><\/div>`,\r\nnote:'Many registrars hide owner details for privacy, so expect redacted contact fields.',\r\ninit:function(r){\r\n  var ex0=new Date(Date.now()+210*86400000).toISOString().slice(0,19)+'Z';\r\n  $('#wh-t',r).value='Domain Name: EXAMPLE.COM\\nRegistrar: Example Registrar, Inc.\\nCreation Date: 2019-03-12T10:21:00Z\\nUpdated Date: 2026-02-20T08:00:00Z\\nRegistry Expiry Date: '+ex0+'\\nDomain Status: clientTransferProhibited https:\/\/icann.org\/epp#clientTransferProhibited\\nName Server: NS1.EXAMPLEDNS.COM\\nName Server: NS2.EXAMPLEDNS.COM\\nDNSSEC: unsigned';\r\n  function get(t,keys){for(var i=0;i<keys.length;i++){var m=t.match(new RegExp('^\\\\s*'+keys[i]+'\\\\s*:\\\\s*(.+)$','im'));if(m)return m[1].trim()}return ''}\r\n  function run(){\r\n    var d=hostOf($('#wh-d',r).value).replace(\/^www\\.\/,'');\r\n    $('#wh-links',r).innerHTML=d?linkGrid([{h:'https:\/\/lookup.icann.org\/en\/lookup?name='+enc(d),t:'ICANN Lookup',s:'Official registration data for '+d},{h:'https:\/\/who.is\/whois\/'+enc(d),t:'who.is',s:'Registrar and DNS details'}]):'';\r\n    var t=$('#wh-t',r).value,iss=[];\r\n    if(!t.trim()){pill($('#wh-pill',r),'idle','Paste the WHOIS result');$('#wh-out',r).innerHTML='';$('#wh-stats',r).innerHTML='';return}\r\n    var reg=get(t,['Registrar']),cr=get(t,['Creation Date','Created','Registered on']),ex=get(t,['Registry Expiry Date','Registrar Registration Expiration Date','Expiry Date','Expires On']),up=get(t,['Updated Date','Last Updated']),dn=get(t,['DNSSEC']);\r\n    var ns=(t.match(\/^\\s*Name Server\\s*:\\s*(.+)$\/gim)||[]).map(function(x){return x.split(':')[1].trim().toLowerCase()}),st=(t.match(\/^\\s*Domain Status\\s*:\\s*(\\S+)\/gim)||[]).map(function(x){return x.split(':')[1].trim()});\r\n    var dc=cr?new Date(cr):null,de=ex?new Date(ex):null,now=Date.now(),left=de&&!isNaN(de)?Math.floor((de-now)\/86400000):null,age=dc&&!isNaN(dc)?((now-dc)\/31557600000):null;\r\n    if(left===null)iss.push({l:'warn',t:'No expiry date found in the text'});\r\n    else if(left<0)iss.push({l:'bad',t:'Domain expired '+(-left)+' days ago',d:'Renew it at your registrar before it is released.'});\r\n    else if(left<=30)iss.push({l:'bad',t:'Expires in '+left+' days',d:'Renew now and turn on auto-renew.'});\r\n    else if(left<=90)iss.push({l:'warn',t:'Expires in '+left+' days',d:'Plan the renewal.'});\r\n    else iss.push({l:'good',t:'Registered for another '+left+' days'});\r\n    if(st.some(function(s){return \/TransferProhibited\/i.test(s)}))iss.push({l:'good',t:'Transfer lock is on',d:'Protects against unauthorised transfers.'});else if(st.length)iss.push({l:'idle',t:'No transfer lock in the status',d:st.join(', ')});\r\n    if(\/hold|redemption|pendingDelete\/i.test(st.join(' ')))iss.push({l:'bad',t:'Domain status shows a hold or deletion state',d:st.join(', ')});\r\n    if(ns.length&&ns.length<2)iss.push({l:'warn',t:'Only one name server',d:'Use at least two.'});\r\n    if(\/unsigned\/i.test(dn))iss.push({l:'idle',t:'DNSSEC is not enabled'});\r\n    var s=summary(iss);pill($('#wh-pill',r),s[0],s[0]==='good'?'Registration looks healthy':s[1]);\r\n    $('#wh-stats',r).innerHTML='<div class=\"stat key\"><span>Days to expiry<\/span><strong>'+(left===null?'\u2014':left)+'<\/strong><\/div><div class=\"stat\"><span>Domain age<\/span><strong>'+(age===null?'\u2014':age.toFixed(1)+' yrs')+'<\/strong><\/div>';\r\n    $('#wh-out',r).innerHTML=issues(iss)+'<div class=\"tbl\" style=\"margin-top:14px\"><table><tbody>'+[['Registrar',reg||'\u2014'],['Created',cr||'\u2014'],['Updated',up||'\u2014'],['Expires',ex||'\u2014'],['Name servers',ns.join(', ')||'\u2014'],['Status',st.join(', ')||'\u2014']].map(function(x){return '<tr><td><b>'+x[0]+'<\/b><\/td><td class=\"mono\">'+esc(x[1])+'<\/td><\/tr>'}).join('')+'<\/tbody><\/table><\/div>';\r\n  }\r\n  r.addEventListener('input',run);run();\r\n}});\r\n\r\n\/* ===== Website IP \/ Hosting Checker ===== *\/\r\ntool({id:'website-ip-hosting-checker',name:'Website IP \/ Hosting Checker',cat:'server',mode:'paste',kw:'ip address hosting provider server location cdn cloudflare nameserver host',\r\ndesc:'Find a site\u2019s IP, then identify its host or CDN from response headers and name servers.',\r\ntpl:`<div class=\"split\"><div class=\"col\">\r\n<div class=\"field\"><label for=\"ip-d\">Domain<\/label><input type=\"text\" id=\"ip-d\" value=\"example.com\" spellcheck=\"false\"><\/div>\r\n<div id=\"ip-links\"><\/div>\r\n<div class=\"field\"><label for=\"ip-ip\">IP address <span class=\"opt\">(from the A record)<\/span><\/label><input type=\"text\" id=\"ip-ip\" value=\"93.184.216.34\" spellcheck=\"false\"><\/div>\r\n<div class=\"field\"><label for=\"ip-h\">Response headers <span class=\"opt\">(curl -sI https:\/\/example.com)<\/span><\/label><textarea id=\"ip-h\" class=\"code\" rows=\"6\" spellcheck=\"false\">HTTP\/2 200\r\nserver: cloudflare\r\ncf-ray: 8a1b2c3d4e5f-BOM\r\nx-powered-by: PHP\/8.2\r\nx-litespeed-cache: hit<\/textarea><\/div>\r\n<div class=\"field\"><label for=\"ip-ns\">Name servers <span class=\"opt\">(optional)<\/span><\/label><textarea id=\"ip-ns\" class=\"code\" rows=\"2\" spellcheck=\"false\">anna.ns.cloudflare.com\r\nbob.ns.cloudflare.com<\/textarea><\/div>\r\n<\/div><div class=\"col\"><span id=\"ip-pill\" class=\"pill idle\">Checking<\/span><div id=\"ip-out\" aria-live=\"polite\"><\/div><\/div><\/div>`,\r\nnote:'Detection uses well-known header and name server patterns. A CDN can hide the real host behind it.',\r\ninit:function(r){\r\n  var HS=[[\/^cf-ray$\/,'Cloudflare','CDN and proxy'],[\/^x-vercel-id$\/,'Vercel','Hosting'],[\/^x-nf-request-id$\/,'Netlify','Hosting'],[\/^x-github-request-id$\/,'GitHub Pages','Hosting'],[\/^x-amz-cf-id$\/,'Amazon CloudFront','CDN'],[\/^x-azure-ref$\/,'Azure Front Door','CDN'],[\/^x-fastly-request-id$|^x-served-by$\/,'Fastly','CDN'],[\/^x-wix-request-id$\/,'Wix','Hosted site builder'],[\/^x-shopify-stage$|^x-shopid$\/,'Shopify','Hosted store'],[\/^x-kinsta-cache$\/,'Kinsta','WordPress hosting'],[\/^x-pantheon-styx-hostname$\/,'Pantheon','Hosting'],[\/^x-litespeed-cache$|^x-turbo-charged-by$\/,'LiteSpeed','Server software'],[\/^x-sucuri-id$\/,'Sucuri','Firewall and CDN'],[\/^x-akamai-transformed$|^akamai-grn$\/,'Akamai','CDN']];\r\n  var SV=[[\/cloudflare\/i,'Cloudflare','CDN and proxy'],[\/nginx\/i,'Nginx','Server software'],[\/apache\/i,'Apache','Server software'],[\/litespeed\/i,'LiteSpeed','Server software'],[\/gws|google frontend|esf\/i,'Google','Hosting'],[\/netlify\/i,'Netlify','Hosting'],[\/vercel\/i,'Vercel','Hosting'],[\/microsoft-iis\/i,'Microsoft IIS','Server software'],[\/hcdn\/i,'Hostinger','Hosting'],[\/amazons3|awselb\/i,'Amazon Web Services','Hosting']];\r\n  var NS=[[\/cloudflare\\.com\/,'Cloudflare'],[\/awsdns\/,'Amazon Route 53'],[\/domaincontrol\\.com\/,'GoDaddy'],[\/googledomains|google\\.com$\/,'Google'],[\/dns-parking|hostinger\/,'Hostinger'],[\/bluehost|hostmonster\/,'Bluehost'],[\/siteground\/,'SiteGround'],[\/namecheap|registrar-servers\/,'Namecheap'],[\/digitalocean\/,'DigitalOcean'],[\/azure-dns\/,'Microsoft Azure'],[\/wixdns\/,'Wix'],[\/shopify\/,'Shopify']];\r\n  function run(){\r\n    var d=hostOf($('#ip-d',r).value),ip=$('#ip-ip',r).value.trim(),found=[],iss=[];\r\n    var arr=[];if(d){arr.push({h:'https:\/\/toolbox.googleapps.com\/apps\/dig\/#A\/'+enc(d),t:'Find the IP address',s:'A record for '+d});arr.push({h:'https:\/\/dnschecker.org\/#A\/'+enc(d),t:'DNS Checker',s:'Compare IPs worldwide'})}\r\n    if(\/^(\\d{1,3}\\.){3}\\d{1,3}$\/.test(ip)||\/^[0-9a-f:]+:[0-9a-f:]+$\/i.test(ip)){arr.push({h:'https:\/\/ipinfo.io\/'+enc(ip),t:'IP details: location and network',s:'ipinfo.io for '+ip});arr.push({h:'https:\/\/bgp.he.net\/ip\/'+enc(ip),t:'Network owner',s:'Hurricane Electric BGP toolkit'})}\r\n    $('#ip-links',r).innerHTML=arr.length?linkGrid(arr):'';\r\n    var p=parseHeaders($('#ip-h',r).value);\r\n    p.order.forEach(function(k){HS.forEach(function(x){if(x[0].test(k))found.push([x[1],x[2],'Header '+k])})});\r\n    var sv=p.h.server;if(sv)SV.forEach(function(x){if(x[0].test(sv))found.push([x[1],x[2],'Server: '+sv])});\r\n    if(p.h['x-powered-by'])found.push([p.h['x-powered-by'],'Application','X-Powered-By header']);\r\n    if(p.h.via&&\/cloudfront\/i.test(p.h.via))found.push(['Amazon CloudFront','CDN','Via header']);\r\n    $('#ip-ns',r).value.split(\/\\n\/).map(function(s){return s.trim().toLowerCase()}).filter(Boolean).forEach(function(n){NS.forEach(function(x){if(x[0].test(n))found.push([x[1],'DNS provider','Name server '+n])})});\r\n    var seen={},u=found.filter(function(f){var k=f[0]+f[1];if(seen[k])return false;seen[k]=1;return true});\r\n    if(!u.length)iss.push({l:'idle',t:'No known host or CDN pattern found',d:'Paste headers and name servers, or look up the IP owner with the links above.'});\r\n    u.forEach(function(f){iss.push({l:'good',t:f[0]+' \u00b7 '+f[1],d:'Seen in: '+f[2]})});\r\n    pill($('#ip-pill',r),u.length?'good':'idle',u.length?u.length+' signal'+(u.length>1?'s':'')+' found':'Nothing detected yet');\r\n    $('#ip-out',r).innerHTML=issues(iss);\r\n  }\r\n  r.addEventListener('input',run);run();\r\n}});\r\n\r\n\/* ===== Page Speed Checker ===== *\/\r\ntool({id:'page-speed-checker',name:'Page Speed Checker',cat:'speed',mode:'link',kw:'page speed pagespeed insights lighthouse performance score fast slow gtmetrix',\r\ndesc:'Run Google PageSpeed Insights on any URL, then work through a checklist of the fixes that matter most.',\r\ntpl:`<div class=\"split\"><div class=\"col\">\r\n<div class=\"field\"><label for=\"ps-u\">Page address<\/label><input type=\"text\" id=\"ps-u\" value=\"https:\/\/www.example.com\/\" spellcheck=\"false\"><\/div>\r\n<div id=\"ps-links\"><\/div>\r\n<div class=\"out-box\"><span class=\"lbl\">How to read the score<\/span><p class=\"hint\">90 to 100 is good, 50 to 89 needs work and below 50 is poor. Check the Field data section first. It comes from real Chrome visitors and is what Google uses.<\/p><\/div>\r\n<\/div><div class=\"col\"><div class=\"gauge\" style=\"display:grid;gap:6px\"><div style=\"display:flex;justify-content:space-between;gap:12px;flex-wrap:wrap\"><b>Speed fixes checklist<\/b><span class=\"hint\" id=\"ps-count\"><\/span><\/div><div class=\"bar\"><i id=\"ps-bar\"><\/i><\/div><\/div>\r\n<div class=\"checks\" id=\"ps-list\"><\/div><\/div><\/div>`,\r\nnote:'Run the test two or three times. Lab scores vary between runs. Tick items as you finish them. Progress resets when you leave the page.',\r\ninit:function(r){\r\n  var FIX=['Serve images in WebP or AVIF and size them to the space they fill','Add width and height to every image and video to stop layout shifts','Lazy-load images below the first screen, but never the main hero image','Preload the largest above-the-fold image or font','Turn on gzip or Brotli compression','Set long browser-cache times for CSS, JS, fonts and images','Minify CSS, JavaScript and HTML','Remove or delay third-party scripts you do not need (chat, heatmaps, extra pixels)','Load JavaScript with defer or async','Self-host or preconnect to font providers, and use font-display: swap','Use a CDN to serve files closer to visitors','Keep server response time under 800 ms with page caching'];\r\n  $('#ps-list',r).innerHTML=FIX.map(function(f,i){return '<label class=\"check\"><input type=\"checkbox\" data-f=\"'+i+'\"><span>'+esc(f)+'<\/span><\/label>'}).join('');\r\n  function prog(){var n=$$('[data-f]:checked',r).length;$('#ps-count',r).textContent=n+' of '+FIX.length+' done';$('#ps-bar',r).style.width=(n\/FIX.length*100)+'%'}\r\n  function run(){\r\n    var u=normUrl($('#ps-u',r).value);\r\n    $('#ps-links',r).innerHTML=u?linkGrid([{h:'https:\/\/pagespeed.web.dev\/analysis?url='+enc(u.href)+'&form_factor=mobile',t:'PageSpeed Insights: mobile',s:'Lab score plus real-user data'},{h:'https:\/\/pagespeed.web.dev\/analysis?url='+enc(u.href)+'&form_factor=desktop',t:'PageSpeed Insights: desktop',s:'Same test on a desktop profile'},{h:'https:\/\/www.webpagetest.org\/',t:'WebPageTest',s:'Detailed waterfall. Enter the address there'}]):'<p class=\"hint\">Enter a full page address.<\/p>';\r\n  }\r\n  $('#ps-u',r).addEventListener('input',run);r.addEventListener('change',prog);run();prog();\r\n}});\r\n\r\n\/* ===== Core Web Vitals Checker ===== *\/\r\ntool({id:'core-web-vitals-checker',name:'Core Web Vitals Checker',cat:'speed',mode:'paste',kw:'core web vitals lcp inp cls fcp ttfb performance ranking page experience',\r\ndesc:'Enter your LCP, INP and CLS numbers to see whether the page passes Google\u2019s thresholds.',\r\ntpl:`<div class=\"split\"><div class=\"col\">\r\n<div class=\"row2\"><div class=\"field\"><label for=\"cw-lcp\">LCP <span class=\"opt\">seconds<\/span><\/label><input type=\"number\" id=\"cw-lcp\" min=\"0\" step=\"0.1\" value=\"3.1\"><\/div>\r\n<div class=\"field\"><label for=\"cw-inp\">INP <span class=\"opt\">milliseconds<\/span><\/label><input type=\"number\" id=\"cw-inp\" min=\"0\" step=\"1\" value=\"240\"><\/div><\/div>\r\n<div class=\"row2\"><div class=\"field\"><label for=\"cw-cls\">CLS <span class=\"opt\">score<\/span><\/label><input type=\"number\" id=\"cw-cls\" min=\"0\" step=\"0.01\" value=\"0.08\"><\/div>\r\n<div class=\"field\"><label for=\"cw-fcp\">FCP <span class=\"opt\">seconds, optional<\/span><\/label><input type=\"number\" id=\"cw-fcp\" min=\"0\" step=\"0.1\" value=\"1.9\"><\/div><\/div>\r\n<div class=\"field\" style=\"max-width:240px\"><label for=\"cw-ttfb\">TTFB <span class=\"opt\">milliseconds, optional<\/span><\/label><input type=\"number\" id=\"cw-ttfb\" min=\"0\" step=\"10\" value=\"600\"><\/div>\r\n<div id=\"cw-links\"><\/div>\r\n<p class=\"hint\">Find your numbers in the Field data section of PageSpeed Insights or in the Core Web Vitals report in Search Console. Use the 75th percentile value.<\/p>\r\n<\/div><div class=\"col\"><span id=\"cw-pill\" class=\"pill idle\">Checking<\/span><div id=\"cw-out\" aria-live=\"polite\"><\/div><\/div><\/div>`,\r\nnote:'Thresholds are Google\u2019s published values for the 75th percentile of real visits. Example numbers are loaded.',\r\ninit:function(r){\r\n  var M=[['lcp','Largest Contentful Paint (LCP)',2.5,4,'s',function(v){return v.toFixed(1)+' s'},'Compress and preload the main image, speed up the server and remove render-blocking CSS and scripts.',1],\r\n         ['inp','Interaction to Next Paint (INP)',200,500,'ms',function(v){return Math.round(v)+' ms'},'Break up long JavaScript tasks, cut third-party scripts and avoid heavy work on clicks and taps.',1],\r\n         ['cls','Cumulative Layout Shift (CLS)',0.1,0.25,'',function(v){return v.toFixed(2)},'Set width and height on images and ads, reserve space for embeds and avoid inserting content above existing content.',1],\r\n         ['fcp','First Contentful Paint (FCP)',1.8,3,'s',function(v){return v.toFixed(1)+' s'},'Reduce render-blocking resources and speed up the server response.',0],\r\n         ['ttfb','Time to First Byte (TTFB)',800,1800,'ms',function(v){return Math.round(v)+' ms'},'Use page caching, a faster host or a CDN.',0]];\r\n  function run(){\r\n    var h='',core=[],u=normUrl('');\r\n    M.forEach(function(m){\r\n      var raw=$('#cw-'+m[0],r).value,v=parseFloat(raw);\r\n      if(raw===''||isNaN(v)){if(m[7])core.push(null);return}\r\n      var k=v<=m[2]?'good':v<=m[3]?'warn':'bad',lab=k==='good'?'Good':k==='warn'?'Needs improvement':'Poor';\r\n      if(m[7])core.push(k);\r\n      var pct=Math.min(100,v\/(m[3]*1.4)*100);\r\n      h+='<div class=\"out-box\" style=\"margin-bottom:12px\"><div style=\"display:flex;justify-content:space-between;gap:10px;flex-wrap:wrap\"><b>'+m[1]+(m[7]?'':' <span class=\"opt\">supporting<\/span>')+'<\/b><span class=\"pill '+k+'\">'+lab+' \u00b7 '+m[5](v)+'<\/span><\/div><div class=\"bar\"><i class=\"'+(k==='good'?'':k)+'\" style=\"width:'+pct+'%\"><\/i><\/div><span class=\"hint\">Good up to '+m[5](m[2])+'. Poor above '+m[5](m[3])+'.'+(k!=='good'?' '+m[6]:'')+'<\/span><\/div>';\r\n    });\r\n    var have=core.filter(Boolean);\r\n    if(have.length<3)pill($('#cw-pill',r),'idle','Enter LCP, INP and CLS');\r\n    else if(core.every(function(k){return k==='good'}))pill($('#cw-pill',r),'good','Passes Core Web Vitals');\r\n    else pill($('#cw-pill',r),core.indexOf('bad')>=0?'bad':'warn','Does not pass yet \u00b7 '+core.filter(function(k){return k!=='good'}).length+' of 3 to fix');\r\n    $('#cw-out',r).innerHTML=h;\r\n  }\r\n  $('#cw-links',r).innerHTML=linkGrid([{h:'https:\/\/pagespeed.web.dev\/',t:'PageSpeed Insights',s:'Field data for your URL'},{h:'https:\/\/search.google.com\/search-console',t:'Search Console',s:'Core Web Vitals report for your whole site'}]);\r\n  r.addEventListener('input',run);run();\r\n}});\r\n\r\n\/* ===== Mobile-Friendly Test ===== *\/\r\ntool({id:'mobile-friendly-test',name:'Mobile-Friendly Test',cat:'speed',mode:'paste',kw:'mobile friendly responsive viewport usability font size tap targets',\r\ndesc:'Paste your page HTML to check the settings that make a page usable on a phone.',\r\ntpl:`<div class=\"split\"><div class=\"col\">\r\n<div class=\"field\"><label for=\"mf-h\">Page HTML <span class=\"opt\">(view source, copy and paste)<\/span><\/label><textarea id=\"mf-h\" class=\"code\" rows=\"14\" spellcheck=\"false\"><!doctype html>\r\n<html><head><meta charset=\"utf-8\"><title>Shop<\/title>\r\n<meta name=\"viewport\" content=\"width=1024, user-scalable=no\">\r\n<style>body{font-size:11px}.wrap{width:960px}<\/style><\/head>\r\n<body><img decoding=\"async\" src=\"hero.jpg\"><table style=\"width:900px\"><tr><td>Price<\/td><\/tr><\/table>\r\n<a href=\"\/buy\">Buy<\/a><input name=\"phone\"><\/body><\/html><\/textarea><\/div>\r\n<div id=\"mf-links\"><div class=\"links\"><a href=\"https:\/\/pagespeed.web.dev\/\" target=\"_blank\" rel=\"noopener noreferrer\">PageSpeed Insights<small>Run a real mobile test on a live URL<\/small><\/a><\/div><\/div>\r\n<\/div><div class=\"col\"><span id=\"mf-pill\" class=\"pill idle\">Checking<\/span><div id=\"mf-out\" aria-live=\"polite\"><\/div><\/div><\/div>`,\r\nnote:'Google retired its standalone Mobile-Friendly Test in 2023. This checks the HTML for common causes. To see the page as a phone does, use the mobile test in PageSpeed Insights or Chrome DevTools device mode.',\r\ninit:function(r){\r\n  function run(){\r\n    var html=$('#mf-h',r).value,iss=[];\r\n    if(!html.trim()){pill($('#mf-pill',r),'idle','Paste the page HTML');$('#mf-out',r).innerHTML='';return}\r\n    var d=new DOMParser().parseFromString(html,'text\/html'),vp=d.querySelectorAll('meta[name=viewport]');\r\n    if(!vp.length)iss.push({l:'bad',t:'No viewport meta tag',d:'Add <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"> so phones do not shrink the desktop layout.'});\r\n    else{\r\n      if(vp.length>1)iss.push({l:'warn',t:'More than one viewport tag'});\r\n      var c=(vp[0].getAttribute('content')||'').toLowerCase();\r\n      if(!\/width\\s*=\\s*device-width\/.test(c))iss.push({l:'bad',t:'Viewport width is not device-width',d:'Current value: '+c+'.'});else iss.push({l:'good',t:'Viewport uses device-width'});\r\n      if(\/user-scalable\\s*=\\s*(no|0)\/.test(c)||\/maximum-scale\\s*=\\s*1(\\.0)?\\b\/.test(c))iss.push({l:'warn',t:'Pinch-to-zoom is disabled',d:'This hurts accessibility. Remove user-scalable=no and maximum-scale=1.'});\r\n    }\r\n    var css=$$('style',d).map(function(s){return s.textContent}).join('\\n')+' '+$$('[style]',d).map(function(e){return e.getAttribute('style')}).join(' ');\r\n    var fs=[],re=\/font-size\\s*:\\s*(\\d+(?:\\.\\d+)?)px\/gi,m;while((m=re.exec(css)))fs.push(+m[1]);\r\n    var small=fs.filter(function(x){return x<12});\r\n    if(small.length)iss.push({l:'warn',t:'Small text found ('+Math.min.apply(null,small)+'px)',d:'Use at least 16px for body text on mobile.'});\r\n    var wide=[],rw=\/(?:^|[;{\\s])(?:min-)?width\\s*:\\s*(\\d{3,4})px\/gi;while((m=rw.exec(css)))if(+m[1]>480)wide.push(+m[1]);\r\n    if(wide.length)iss.push({l:'warn',t:'Fixed widths wider than a phone ('+Math.max.apply(null,wide)+'px)',d:'Use max-width, percentages or responsive units so content fits narrow screens.'});\r\n    var imgs=$$('img',d),nodim=imgs.filter(function(i){return !i.getAttribute('width')||!i.getAttribute('height')});\r\n    if(nodim.length)iss.push({l:'warn',t:nodim.length+' of '+imgs.length+' images without width and height',d:'Missing sizes cause layout shifts while loading.'});\r\n    var nosrc=imgs.filter(function(i){return !i.getAttribute('srcset')&&!i.closest('picture')});\r\n    if(imgs.length&&nosrc.length===imgs.length)iss.push({l:'idle',t:'No responsive images (srcset)',d:'Serve smaller files to phones with srcset and sizes.'});\r\n    var tbl=$$('table',d).filter(function(t){return !t.closest('[style*=overflow]')});\r\n    if(tbl.length)iss.push({l:'idle',t:tbl.length+' table'+(tbl.length>1?'s':'')+' not in a scrolling wrapper',d:'Wide tables push the page sideways on phones. Wrap them in a container with overflow-x: auto.'});\r\n    var inp=$$('input:not([type]),input[type=text]',d).filter(function(i){return \/phone|tel|mobile|email|mail|zip|pin\/i.test((i.getAttribute('name')||'')+(i.getAttribute('id')||''))});\r\n    if(inp.length)iss.push({l:'idle',t:inp.length+' field'+(inp.length>1?'s':'')+' that could use a better keyboard',d:'Use type=\"tel\" or type=\"email\" so phones show the right keyboard.'});\r\n    if(d.querySelector('embed,object,applet'))iss.push({l:'bad',t:'Plugin content found',d:'Flash and similar plugins do not run on phones.'});\r\n    if(!(d.documentElement.getAttribute('lang')))iss.push({l:'idle',t:'No lang attribute on <html>'});\r\n    var s=summary(iss);pill($('#mf-pill',r),s[0],s[1]);$('#mf-out',r).innerHTML=issues(iss);\r\n  }\r\n  $('#mf-h',r).addEventListener('input',run);run();\r\n}});\r\n\r\n\/* ===== Gzip \/ Compression Checker ===== *\/\r\ntool({id:'gzip-compression-checker',name:'Gzip \/ Compression Checker',cat:'speed',mode:'paste',kw:'gzip brotli compression deflate size content-encoding',\r\ndesc:'See how much smaller a file becomes with gzip, and read a server\u2019s compression setting from its headers.',\r\ntpl:`<div class=\"split\"><div class=\"col\">\r\n<div class=\"field\"><label for=\"gz-t\">File contents <span class=\"opt\">(HTML, CSS or JavaScript. A sample is loaded)<\/span><\/label><textarea id=\"gz-t\" class=\"code\" rows=\"10\" spellcheck=\"false\"><\/textarea><\/div>\r\n<div class=\"field\"><label for=\"gz-h\">Response headers <span class=\"opt\">(optional, curl -sI -H \"Accept-Encoding: gzip, br\" URL)<\/span><\/label><textarea id=\"gz-h\" class=\"code\" rows=\"4\" spellcheck=\"false\">HTTP\/2 200\r\ncontent-type: text\/html; charset=UTF-8\r\ncontent-encoding: br\r\nvary: Accept-Encoding<\/textarea><\/div>\r\n<\/div><div class=\"col\"><span id=\"gz-pill\" class=\"pill idle\">Checking<\/span><div class=\"stats\" id=\"gz-stats\"><\/div><div id=\"gz-out\" aria-live=\"polite\"><\/div><\/div><\/div>`,\r\nnote:'Sizes are measured with your browser\u2019s built-in compressor. Brotli is usually a little smaller than gzip and is best when your server supports it.',\r\ninit:function(r){\r\n  var sample='';for(var i=0;i<40;i++)sample+='<div class=\"product-card\"><h3>Handloom cotton kurta '+(i+1)+'<\/h3><p class=\"price\">Rs. 1,299<\/p><a href=\"\/shop\/kurta-'+(i+1)+'\/\">View product<\/a><\/div>\\n';\r\n  $('#gz-t',r).value=sample;\r\n  function comp(txt,fmt){\r\n    if(typeof CompressionStream==='undefined')return Promise.reject(new Error('unsupported'));\r\n    var cs=new CompressionStream(fmt),w=cs.writable.getWriter();w.write(new TextEncoder().encode(txt));w.close();\r\n    return new Response(cs.readable).arrayBuffer().then(function(b){return b.byteLength});\r\n  }\r\n  var tk=0;\r\n  function run(){\r\n    var txt=$('#gz-t',r).value,h=parseHeaders($('#gz-h',r).value),iss=[],me=++tk;\r\n    var orig=new TextEncoder().encode(txt).length,ce=h.h['content-encoding'];\r\n    if(h.order.length){\r\n      if(ce)iss.push({l:'good',t:'Server compresses responses with '+ce});\r\n      else if(\/text|json|javascript|xml|svg\/i.test(h.h['content-type']||''))iss.push({l:'bad',t:'Server sent this response uncompressed',d:'Turn on gzip or Brotli. On Apache add mod_deflate, on Nginx set gzip on.'});\r\n      else iss.push({l:'idle',t:'No content-encoding header',d:'Images and video are already compressed, so this is normal for them.'});\r\n      if(ce&&!\/accept-encoding\/i.test(h.h.vary||''))iss.push({l:'warn',t:'Missing Vary: Accept-Encoding',d:'Caches may serve compressed files to clients that cannot read them.'});\r\n    }\r\n    if(!txt.trim()){pill($('#gz-pill',r),'idle','Paste file contents');$('#gz-stats',r).innerHTML='';$('#gz-out',r).innerHTML=issues(iss);return}\r\n    comp(txt,'gzip').then(function(g){\r\n      if(me!==tk)return;\r\n      var save=orig?(1-g\/orig)*100:0;\r\n      $('#gz-stats',r).innerHTML='<div class=\"stat\"><span>Original<\/span><strong>'+bytes(orig)+'<\/strong><\/div><div class=\"stat key\"><span>With gzip<\/span><strong>'+bytes(g)+'<\/strong><\/div><div class=\"stat\"><span>Saved<\/span><strong>'+save.toFixed(0)+'%<\/strong><\/div>';\r\n      var all=iss.slice();\r\n      all.push(orig<1000?{l:'idle',t:'Very small file',d:'Files under about 1 KB gain little from compression.'}:save>=60?{l:'good',t:'Compresses very well ('+save.toFixed(0)+'% smaller)'}:{l:'idle',t:'Compression saves '+save.toFixed(0)+'%',d:'Already compact, or contains data that does not compress well.'});\r\n      var sm=summary(all.filter(function(i){return i.l==='bad'||i.l==='warn'}));pill($('#gz-pill',r),sm[0],sm[0]==='good'?'Compression looks good':sm[1]);\r\n      $('#gz-out',r).innerHTML=issues(all);\r\n    },function(){\r\n      if(me!==tk)return;\r\n      pill($('#gz-pill',r),'warn','This browser cannot measure compression');$('#gz-stats',r).innerHTML='<div class=\"stat\"><span>Original<\/span><strong>'+bytes(orig)+'<\/strong><\/div>';$('#gz-out',r).innerHTML=issues(iss.concat([{l:'warn',t:'Compression test unavailable',d:'Try the latest Chrome, Edge, Firefox or Safari.'}]));\r\n    });\r\n  }\r\n  r.addEventListener('input',run);run();\r\n}});\r\n\r\n\/* ===== Page Size Checker ===== *\/\r\ntool({id:'page-size-checker',name:'Page Size Checker',cat:'speed',mode:'paste',kw:'page size weight total kb mb resources images scripts requests budget',\r\ndesc:'List every file a page loads and add up the total weight against a performance budget.',\r\ntpl:`<div class=\"split\"><div class=\"col\">\r\n<div class=\"field\"><label for=\"pz-h\">Page HTML <span class=\"opt\">(view source, copy and paste)<\/span><\/label><textarea id=\"pz-h\" class=\"code\" rows=\"12\" spellcheck=\"false\"><!doctype html><html><head><title>Shop<\/title>\r\n<link rel=\"stylesheet\" href=\"\/css\/main.css\"><link rel=\"stylesheet\" href=\"https:\/\/fonts.googleapis.com\/css2?family=Inter\">\r\n<script src=\"\/js\/app.js\" defer><\\\/script><script src=\"https:\/\/www.googletagmanager.com\/gtag\/js?id=G-XXXX\" async><\\\/script><\/head>\r\n<body><img decoding=\"async\" src=\"\/img\/hero.jpg\"><img decoding=\"async\" src=\"\/img\/product-1.webp\"><img decoding=\"async\" src=\"\/img\/product-2.webp\">\r\n<video src=\"\/media\/promo.mp4\"><\/video><iframe src=\"https:\/\/www.youtube.com\/embed\/abc\"><\/iframe><\/body><\/html><\/textarea><\/div>\r\n<p class=\"hint\">Look up file sizes in browser developer tools: Network tab, reload, and read the Size column. Type them into the boxes. Leave unknown files blank.<\/p>\r\n<\/div><div class=\"col\"><span id=\"pz-pill\" class=\"pill idle\">Checking<\/span><div class=\"stats\" id=\"pz-stats\"><\/div><div class=\"bar\"><i id=\"pz-bar\"><\/i><\/div><div id=\"pz-out\" aria-live=\"polite\"><\/div><\/div><\/div>`,\r\nnote:'Median page weight on mobile is around 2 MB. Many teams aim for under 1.5 MB. Sizes you enter are used only for this total.',\r\ninit:function(r){\r\n  var sizes={'\/css\/main.css':38,'\/js\/app.js':140,'\/img\/hero.jpg':420,'\/img\/product-1.webp':85,'\/img\/product-2.webp':92,'https:\/\/fonts.googleapis.com\/css2?family=Inter':2,'https:\/\/www.googletagmanager.com\/gtag\/js?id=G-XXXX':90},lastKey='';\r\n  function res(html){\r\n    var d=new DOMParser().parseFromString(html,'text\/html'),o=[];\r\n    $$('link[rel~=stylesheet]',d).forEach(function(e){o.push(['CSS',e.getAttribute('href')])});\r\n    $$('script[src]',d).forEach(function(e){o.push(['Script',e.getAttribute('src')])});\r\n    $$('img[src]',d).forEach(function(e){o.push(['Image',e.getAttribute('src')])});\r\n    $$('source[src],video[src],audio[src]',d).forEach(function(e){o.push(['Media',e.getAttribute('src')])});\r\n    $$('iframe[src]',d).forEach(function(e){o.push(['Frame',e.getAttribute('src')])});\r\n    $$('link[rel~=preload][href],link[rel~=icon][href]',d).forEach(function(e){o.push(['Other',e.getAttribute('href')])});\r\n    var inl=$$('style',d).map(function(e){return e.textContent}).join('').length+$$('script:not([src])',d).map(function(e){return e.textContent}).join('').length;\r\n    return {list:o.filter(function(x){return x[1]}),inline:inl};\r\n  }\r\n  function draw(){\r\n    var html=$('#pz-h',r).value,hb=new Blob([html]).size,x=res(html),key=JSON.stringify(x.list);\r\n    if(key!==lastKey){lastKey=key;\r\n      $('#pz-out',r).innerHTML=(x.list.length?'<div class=\"tbl\"><table><thead><tr><th>Type<\/th><th>File<\/th><th>Size, KB<\/th><\/tr><\/thead><tbody>'+x.list.map(function(f,i){return '<tr><td>'+f[0]+'<\/td><td class=\"mono\">'+esc(f[1])+'<\/td><td style=\"width:110px\"><input type=\"number\" min=\"0\" step=\"any\" data-s=\"'+esc(f[1])+'\" value=\"'+(sizes[f[1]]===undefined?'':sizes[f[1]])+'\" aria-label=\"Size in KB of '+esc(f[1])+'\"><\/td><\/tr>'}).join('')+'<\/tbody><\/table><\/div>':'<p class=\"hint\">No external files found in this HTML.<\/p>')+'<div id=\"pz-adv\"><\/div>';\r\n    }\r\n    sum(hb,x);\r\n  }\r\n  function sum(hb,x){\r\n    var tot=hb,known=0;Object.keys(sizes).forEach(function(k){var v=parseFloat(sizes[k]);if(v>0&&$$('[data-s]',r).some(function(i){return i.getAttribute('data-s')===k})){tot+=v*1024;known++}});\r\n    var n=x.list.length+1,mb=tot\/1048576,budget=1.5,k=mb<=budget?'good':mb<=2.5?'warn':'bad';\r\n    $('#pz-stats',r).innerHTML='<div class=\"stat key\"><span>Total so far<\/span><strong>'+bytes(Math.round(tot))+'<\/strong><\/div><div class=\"stat\"><span>HTML<\/span><strong>'+bytes(hb)+'<\/strong><\/div><div class=\"stat\"><span>Inline CSS and JS<\/span><strong>'+bytes(x.inline)+'<\/strong><\/div><div class=\"stat\"><span>Requests<\/span><strong>'+n+'<\/strong><\/div><div class=\"stat\"><span>Sizes entered<\/span><strong>'+known+' of '+x.list.length+'<\/strong><\/div>';\r\n    var bar=$('#pz-bar',r);bar.style.width=Math.min(100,mb\/3*100)+'%';bar.className=k==='good'?'':k;\r\n    var adv=[];\r\n    if(known<x.list.length)adv.push({l:'idle',t:(x.list.length-known)+' file'+(x.list.length-known>1?'s':'')+' without a size',d:'The total is a minimum until you enter every size.'});\r\n    adv.push(k==='good'?{l:'good',t:'Within the 1.5 MB budget'}:k==='warn'?{l:'warn',t:'Above 1.5 MB',d:'Compress images and remove unused scripts.'}:{l:'bad',t:'Heavy page, over 2.5 MB',d:'Large images and video are the usual cause. Convert to WebP or AVIF and lazy-load.'});\r\n    if(x.list.filter(function(f){return f[0]==='Script'}).length>8)adv.push({l:'warn',t:'Many script files',d:'Combine or remove scripts to reduce requests.'});\r\n    if(x.list.some(function(f){return f[0]==='Media'}))adv.push({l:'idle',t:'Video file loaded directly',d:'Use preload=\"none\" or load it on tap so it does not count against first load.'});\r\n    pill($('#pz-pill',r),known?k:'idle',known?mb.toFixed(2)+' MB counted \u00b7 '+n+' requests':n+' requests \u00b7 enter file sizes for the total');\r\n    var a=$('#pz-adv',r);if(a)a.innerHTML='<div style=\"margin-top:14px\">'+issues(adv)+'<\/div>';\r\n  }\r\n  r.addEventListener('input',function(e){\r\n    if(e.target.id==='pz-h'){draw()}\r\n    else if(e.target.hasAttribute('data-s')){sizes[e.target.getAttribute('data-s')]=e.target.value;var html=$('#pz-h',r).value;sum(new Blob([html]).size,res(html))}\r\n  });\r\n  draw();\r\n}});\r\n\r\n\/* ===== Technology Detector ===== *\/\r\ntool({id:'technology-detector',name:'Technology Detector',cat:'code',mode:'paste',kw:'technology detector cms wordpress shopify plugins framework analytics builtwith wappalyzer stack',\r\ndesc:'Paste a page\u2019s source to spot its CMS, plugins, frameworks, analytics and marketing tags.',\r\ntpl:`<div class=\"split\"><div class=\"col\">\r\n<div class=\"field\"><label for=\"td-h\">Page source <span class=\"opt\">(view source, copy and paste)<\/span><\/label><textarea id=\"td-h\" class=\"code\" rows=\"12\" spellcheck=\"false\"><!doctype html><html lang=\"en\"><head>\r\n<meta name=\"generator\" content=\"WordPress 6.4.2\">\r\n<meta name=\"generator\" content=\"Elementor 3.18.3\">\r\n<\\!-- This site is optimized with the Yoast SEO plugin v21.7 -->\r\n<link rel=\"stylesheet\" href=\"\/wp-content\/plugins\/woocommerce\/assets\/css\/woocommerce.css\">\r\n<link href=\"https:\/\/fonts.googleapis.com\/css2?family=Poppins\" rel=\"stylesheet\">\r\n<script src=\"https:\/\/www.googletagmanager.com\/gtag\/js?id=G-AB12CD34EF\" async><\\\/script>\r\n<script src=\"\/wp-includes\/js\/jquery\/jquery.min.js?ver=3.7.1\"><\\\/script>\r\n<script>!function(f){fbq('init','123');}(window)<\\\/script><script src=\"https:\/\/connect.facebook.net\/en_US\/fbevents.js\"><\\\/script>\r\n<\/head><body><\/body><\/html><\/textarea><\/div>\r\n<div class=\"field\"><label for=\"td-r\">Response headers <span class=\"opt\">(optional)<\/span><\/label><textarea id=\"td-r\" class=\"code\" rows=\"4\" spellcheck=\"false\">server: cloudflare\r\ncf-ray: 8a1b2c3d-BOM\r\nx-powered-by: PHP\/8.2.12<\/textarea><\/div><\/div>\r\n<div class=\"col\"><span id=\"td-pill\" class=\"pill idle\">Checking<\/span><div id=\"td-out\" aria-live=\"polite\"><\/div><\/div><\/div>`,\r\nnote:'Detection is based on visible code patterns. Anything loaded privately or hidden behind a proxy will not appear.',\r\ninit:function(r){\r\n  var S=[\r\n  ['WordPress','CMS',\/\\\/wp-content\\\/|\\\/wp-includes\\\/|content=\"WordPress\/i,\/content=\"WordPress\\s*(\\d+(?:\\.\\d+)+)\/i],\r\n  ['Shopify','CMS',\/cdn\\.shopify\\.com|Shopify\\.theme|myshopify\\.com\/i],\r\n  ['Wix','CMS',\/wixstatic\\.com|static\\.parastorage\\.com|content=\"Wix\\.com\/i],\r\n  ['Squarespace','CMS',\/squarespace\\.com|static1\\.squarespace\/i],\r\n  ['Webflow','CMS',\/webflow\\.(?:com|io)|data-wf-page\/i],\r\n  ['Drupal','CMS',\/drupal-settings-json|content=\"Drupal\\s*([\\d.]+)?\/i,\/content=\"Drupal\\s*([\\d.]+)\/i],\r\n  ['Joomla','CMS',\/\\\/media\\\/jui\\\/|content=\"Joomla\/i],\r\n  ['Magento','CMS',\/\\\/static\\\/frontend\\\/|Mage\\.Cookies|magento\/i],\r\n  ['Ghost','CMS',\/content=\"Ghost\\s*[\\d.]*\/i],\r\n  ['HubSpot CMS','CMS',\/hs-scripts\\.com|hubspot\\.net|hsforms\/i],\r\n  ['WooCommerce','Plugin',\/woocommerce\/i,\/WooCommerce[\\s\\\/]+v?(\\d+(?:\\.\\d+)+)\/i],\r\n  ['Elementor','Plugin',\/elementor\/i,\/Elementor\\s+(\\d+(?:\\.\\d+)+)\/i],\r\n  ['Yoast SEO','Plugin',\/yoast seo\/i,\/Yoast SEO plugin v?(\\d+(?:\\.\\d+)+)\/i],\r\n  ['Rank Math','Plugin',\/rank math\/i],\r\n  ['WP Rocket','Plugin',\/wp-rocket|wp rocket\/i],\r\n  ['Divi','Plugin',\/et-boc|\\\/themes\\\/Divi\\\/\/i],\r\n  ['Contact Form 7','Plugin',\/wpcf7\/i],\r\n  ['React','Framework',\/data-reactroot|react-dom|__REACT_DEVTOOLS|_reactRoot\/i],\r\n  ['Next.js','Framework',\/__NEXT_DATA__|\\\/_next\\\/\/i],\r\n  ['Vue.js','Framework',\/vue(?:\\.min)?\\.js|data-v-[0-9a-f]{6,}|__VUE__\/i],\r\n  ['Nuxt','Framework',\/__NUXT__|\\\/_nuxt\\\/\/i],\r\n  ['Angular','Framework',\/ng-version|ng-app|angular(?:\\.min)?\\.js\/i,\/ng-version=\"([\\d.]+)\"\/i],\r\n  ['Gatsby','Framework',\/___gatsby\/i],\r\n  ['Astro','Framework',\/astro-island|astro-slot\/i],\r\n  ['jQuery','Library',\/jquery\/i,\/jquery[-.\\\/](\\d+\\.\\d+(?:\\.\\d+)?)\/i],\r\n  ['Bootstrap','Library',\/bootstrap(?:\\.min)?\\.(?:css|js)|class=\"[^\"]*\\bcol-(?:md|lg|sm)-\\d\/i],\r\n  ['Tailwind CSS','Library',\/tailwindcss|cdn\\.tailwindcss\\.com\/i],\r\n  ['Font Awesome','Library',\/font-?awesome|fa-solid|fas fa-\/i],\r\n  ['Google Fonts','Library',\/fonts\\.googleapis\\.com\/i],\r\n  ['Google Analytics 4','Analytics',\/gtag\\\/js\\?id=G-|[\"']G-[A-Z0-9]{8,12}[\"']\/i],\r\n  ['Universal Analytics','Analytics',\/UA-\\d{4,10}-\\d+|analytics\\.js\/i],\r\n  ['Google Tag Manager','Analytics',\/googletagmanager\\.com\\\/gtm\\.js|GTM-[A-Z0-9]+\/i],\r\n  ['Microsoft Clarity','Analytics',\/clarity\\.ms\/i],\r\n  ['Hotjar','Analytics',\/hotjar\\.com|static\\.hotjar\/i],\r\n  ['Meta Pixel','Marketing',\/fbevents\\.js|fbq\\(\/i],\r\n  ['Google Ads tag','Marketing',\/AW-\\d{6,}\/i],\r\n  ['LinkedIn Insight Tag','Marketing',\/snap\\.licdn\\.com|_linkedin_partner_id\/i],\r\n  ['TikTok Pixel','Marketing',\/analytics\\.tiktok\\.com|ttq\\.load\/i],\r\n  ['Mailchimp','Marketing',\/mailchimp|chimpstatic|list-manage\\.com\/i],\r\n  ['HubSpot tracking','Marketing',\/js\\.hs-scripts\\.com|hs-analytics\/i],\r\n  ['Razorpay','Payments',\/checkout\\.razorpay\\.com\/i],\r\n  ['Stripe','Payments',\/js\\.stripe\\.com\/i],\r\n  ['PayPal','Payments',\/paypal\\.com\\\/sdk|paypalobjects\/i],\r\n  ['reCAPTCHA','Security',\/google\\.com\\\/recaptcha|grecaptcha\/i],\r\n  ['Tawk.to','Chat',\/tawk\\.to\/i],\r\n  ['Intercom','Chat',\/intercomcdn|widget\\.intercom\\.io\/i],\r\n  ['WhatsApp chat link','Chat',\/wa\\.me\\\/|api\\.whatsapp\\.com\\\/send\/i]];\r\n  function run(){\r\n    var html=$('#td-h',r).value,hd=parseHeaders($('#td-r',r).value),found=[];\r\n    if(!html.trim()&&!hd.order.length){pill($('#td-pill',r),'idle','Paste the page source');$('#td-out',r).innerHTML='';return}\r\n    S.forEach(function(s){if(s[2].test(html)){var v=s[3]&&html.match(s[3]);found.push({n:s[0],c:s[1],v:v&&v[1]||''})}});\r\n    var sv=hd.h.server,xp=hd.h['x-powered-by'];\r\n    if(hd.h['cf-ray']||\/cloudflare\/i.test(sv||''))found.push({n:'Cloudflare',c:'CDN',v:''});\r\n    if(sv&&!\/cloudflare\/i.test(sv)){var m=sv.match(\/^([A-Za-z-]+)\\\/?([\\d.]*)\/);if(m)found.push({n:m[1],c:'Web server',v:m[2]})}\r\n    if(xp){var m2=xp.match(\/^([A-Za-z.]+)\\\/?([\\d.]*)\/);if(m2)found.push({n:m2[1],c:'Language',v:m2[2]})}\r\n    if(hd.h['x-aspnet-version'])found.push({n:'ASP.NET',c:'Language',v:hd.h['x-aspnet-version']});\r\n    if(hd.h['x-litespeed-cache']||\/litespeed\/i.test(sv||''))found.push({n:'LiteSpeed',c:'Web server',v:''});\r\n    var gens=[];html.replace(\/<meta[^>]+name=[\"']generator[\"'][^>]*content=[\"']([^\"']+)[\"']\/gi,function(_,g){gens.push(g)});\r\n    if(\/<[^>]*(?:class|id)=\"[^\"]*(?:wp-|elementor)\/i.test(html)&&!found.some(function(f){return f.n==='WordPress'}))found.push({n:'WordPress',c:'CMS',v:''});\r\n    var seen={},u=found.filter(function(f){if(seen[f.n])return false;seen[f.n]=1;return true});\r\n    if(!u.length){pill($('#td-pill',r),'idle','Nothing recognised');$('#td-out',r).innerHTML=issues([{l:'idle',t:'No known technology found',d:'Paste the full page source from view-source: and, if you can, the response headers.'}]);return}\r\n    pill($('#td-pill',r),'good',u.length+' technolog'+(u.length>1?'ies':'y')+' detected');\r\n    var order=['CMS','Plugin','Framework','Library','Language','Web server','CDN','Analytics','Marketing','Payments','Chat','Security'],h='';\r\n    order.forEach(function(c){var l=u.filter(function(f){return f.c===c});if(!l.length)return;h+='<tr><td><b>'+c+'<\/b><\/td><td>'+l.map(function(f){return '<span class=\"pill idle\" style=\"margin:2px 4px 2px 0\">'+esc(f.n)+(f.v?' '+esc(f.v):'')+'<\/span>'}).join('')+'<\/td><\/tr>'});\r\n    $('#td-out',r).innerHTML='<div class=\"tbl\"><table><tbody>'+h+'<\/tbody><\/table><\/div>'+(gens.length?'<p class=\"hint\" style=\"margin-top:10px\">Generator tags: '+gens.map(esc).join(' \u00b7 ')+'<\/p>':'');\r\n  }\r\n  r.addEventListener('input',run);run();\r\n}});\r\n\r\n\/* ===== HTML Validator ===== *\/\r\ntool({id:'html-validator',name:'HTML Validator',cat:'code',mode:'here',kw:'html validator markup errors w3c unclosed tags alt heading duplicate id accessibility seo',\r\ndesc:'Find unclosed tags, duplicate IDs, missing alt text and other markup and SEO errors, with line numbers.',\r\ntpl:`<div class=\"split\"><div class=\"col\">\r\n<div class=\"field\"><label for=\"hv-h\">HTML <span class=\"opt\">(paste a full page or a fragment)<\/span><\/label><textarea id=\"hv-h\" class=\"code\" rows=\"18\" spellcheck=\"false\"><html>\r\n<head>\r\n<title>Home<\/title>\r\n<\/head>\r\n<body>\r\n<center><h1>Welcome<\/h1><\/center>\r\n<h1>Second heading<\/h1>\r\n<h3>Skipped level<\/h3>\r\n<img decoding=\"async\" src=\"hero.jpg\">\r\n<div id=\"box\"><p>Text\r\n<div id=\"box\"><a>No link<\/a><\/div>\r\n<a href=\"https:\/\/other.com\" target=\"_blank\">Visit<\/a>\r\n<\/body>\r\n<\/html><\/textarea><\/div><\/div>\r\n<div class=\"col\"><span id=\"hv-pill\" class=\"pill idle\">Checking<\/span><div id=\"hv-out\" aria-live=\"polite\"><\/div><\/div><\/div>`,\r\nnote:'This checks the most common errors, not the full HTML specification. For a complete check use the W3C validator at validator.w3.org.',\r\ninit:function(r){\r\n  var VOID={area:1,base:1,br:1,col:1,embed:1,hr:1,img:1,input:1,link:1,meta:1,param:1,source:1,track:1,wbr:1};\r\n  var OPT={li:1,p:1,td:1,th:1,tr:1,tbody:1,thead:1,tfoot:1,option:1,dt:1,dd:1,colgroup:1,caption:1,optgroup:1};\r\n  var OLD={center:1,font:1,marquee:1,blink:1,big:1,strike:1,tt:1,frame:1,frameset:1,acronym:1,basefont:1,dir:1};\r\n  function run(){\r\n    var src=$('#hv-h',r).value,iss=[];\r\n    if(!src.trim()){pill($('#hv-pill',r),'idle','Paste some HTML');$('#hv-out',r).innerHTML='';return}\r\n    function line(i){return src.slice(0,i).split('\\n').length}\r\n    function add(l,i,t,d){if(iss.length<80)iss.push({l:l,t:'Line '+line(i)+': '+t,d:d})}\r\n    var re=\/<\\!--[\\s\\S]*?-->|<!doctype[^>]*>|<(\\\/?)([a-zA-Z][a-zA-Z0-9-]*)((?:\"[^\"]*\"|'[^']*'|[^'\">])*)>\/gi,m,stack=[],ids={},h1=0,lastH=0,hasDoctype=\/^\\s*<!doctype html\/i.test(src),meta={};\r\n    while((m=re.exec(src))){\r\n      if(m[0].charAt(1)==='!')continue;\r\n      var close=m[1]==='\/',name=m[2].toLowerCase(),attrs=m[3]||'',at=m.index;\r\n      if(!close&&(name==='script'||name==='style'||name==='textarea'||name==='title')){\r\n        var e=src.toLowerCase().indexOf('<\/'+name,re.lastIndex);\r\n        if(name==='title'||name==='script'||name==='style'||name==='textarea'){\r\n          if(e<0){add('bad',at,'<'+name+'> is never closed');}\r\n          else{\r\n            if(name==='title')meta.title=src.slice(re.lastIndex,e).trim();\r\n            re.lastIndex=src.indexOf('>',e)+1;\r\n          }\r\n          if(name==='title'){meta.titleSeen=(meta.titleSeen||0)+1}\r\n          if(name==='script'||name==='style'||name==='textarea'||name==='title')continue;\r\n        }\r\n      }\r\n      if(OLD[name]&&!close)add('warn',at,'<'+name+'> is obsolete','Use CSS or a modern element instead.');\r\n      if(!close){\r\n        var dup={},am,are=\/([^\\s=\\\/]+)(?:\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\s>]+)))?\/g;\r\n        while((am=are.exec(attrs))){\r\n          var an=am[1].toLowerCase();if(dup[an])add('bad',at,'Duplicate attribute '+an+' on <'+name+'>');dup[an]=1;\r\n          if(an==='id'){var v=am[2]!==undefined?am[2]:am[3]!==undefined?am[3]:am[4];if(ids[v])add('bad',at,'Duplicate id \u201c'+v+'\u201d','First used on line '+ids[v]+'. IDs must be unique.');else ids[v]=line(at)}\r\n        }\r\n        if(name==='img'){if(!\/\\balt\\s*=\/i.test(attrs))add('warn',at,'Image has no alt attribute','Add alt text for accessibility and image search. Use alt=\"\" for decorative images.')}\r\n        if(name==='a'){if(!\/\\bhref\\s*=\/i.test(attrs)&&!\/\\bname\\s*=\/i.test(attrs))add('warn',at,'<a> has no href','Crawlers cannot follow it.');if(\/target\\s*=\\s*[\"']?_blank\/i.test(attrs)&&!\/rel\\s*=\\s*[\"'][^\"']*noopener\/i.test(attrs))add('idle',at,'target=\"_blank\" without rel=\"noopener\"')}\r\n        if(\/^h[1-6]$\/.test(name)){var lv=+name[1];if(name==='h1')h1++;if(lastH&&lv>lastH+1)add('warn',at,'Heading jumps from h'+lastH+' to h'+lv,'Keep heading levels in order.');lastH=lv}\r\n        if(name==='meta'){var mn=(attrs.match(\/\\b(?:name|property)\\s*=\\s*[\"']([^\"']+)\/i)||[])[1];if(mn)meta[mn.toLowerCase()]=(attrs.match(\/content\\s*=\\s*[\"']([^\"']*)\/i)||[])[1]||'';if(\/charset\/i.test(attrs))meta.charset=1}\r\n        if(name==='html'&&!\/\\blang\\s*=\/i.test(attrs))meta.nolang=at;\r\n      }\r\n      if(VOID[name])continue;\r\n      if(!close){\r\n        var top=stack[stack.length-1];\r\n        if(top&&((name==='li'&&top.n==='li')||(name==='p'&&top.n==='p')||((name==='td'||name==='th')&&(top.n==='td'||top.n==='th'))||(name==='tr'&&(top.n==='tr'||top.n==='td'||top.n==='th'))||((name==='dt'||name==='dd')&&(top.n==='dt'||top.n==='dd'))||(name==='option'&&top.n==='option')))stack.pop();\r\n        if(\/\\\/\\s*$\/.test(attrs))continue;\r\n        stack.push({n:name,at:at});\r\n      }else{\r\n        var idx=-1;for(var i=stack.length-1;i>=0;i--)if(stack[i].n===name){idx=i;break}\r\n        if(idx<0){add('bad',at,'Unexpected closing <\/'+name+'>','There is no matching opening tag.');continue}\r\n        while(stack.length-1>idx){var u=stack.pop();if(!OPT[u.n])add('bad',u.at,'<'+u.n+'> is not closed','It is still open when <\/'+name+'> appears on line '+line(at)+'.')}\r\n        stack.pop();\r\n      }\r\n    }\r\n    stack.forEach(function(u){if(!OPT[u.n])add('bad',u.at,'<'+u.n+'> is never closed')});\r\n    if(!hasDoctype&&\/<html\/i.test(src))iss.unshift({l:'warn',t:'Missing <!doctype html>',d:'Put it on the very first line so browsers use standards mode.'});\r\n    var isPage=\/<html|<head\/i.test(src);\r\n    if(isPage){\r\n      if(meta.nolang!==undefined)iss.push({l:'warn',t:'<html> has no lang attribute',d:'Add lang=\"en\" (or your language) for accessibility and search.'});\r\n      if(!meta.charset)iss.push({l:'warn',t:'No character set declared',d:'Add <meta charset=\"utf-8\"> near the top of the head.'});\r\n      if(!meta.titleSeen)iss.push({l:'bad',t:'No <title> tag'});\r\n      else{if(meta.titleSeen>1)iss.push({l:'bad',t:'More than one <title>'});var tl=(meta.title||'').length;if(!tl)iss.push({l:'bad',t:'The title is empty'});else if(tl>60)iss.push({l:'warn',t:'Title is '+tl+' characters',d:'Keep it near 60 characters so it is not cut off in results.'})}\r\n      if(!meta.description)iss.push({l:'warn',t:'No meta description',d:'Add one of about 120 to 160 characters.'});\r\n      if(!meta.viewport)iss.push({l:'warn',t:'No viewport meta tag',d:'Needed for mobile layouts.'});\r\n    }\r\n    if(h1===0&&\/<body|<h[2-6]\/i.test(src))iss.push({l:'warn',t:'No <h1> heading'});\r\n    if(h1>1)iss.push({l:'warn',t:h1+' <h1> headings',d:'Use one main heading per page.'});\r\n    var s=summary(iss.filter(function(i){return i.l==='bad'||i.l==='warn'}));pill($('#hv-pill',r),s[0],s[1]);\r\n    $('#hv-out',r).innerHTML=iss.length?issues(iss):issues([{l:'good',t:'No common errors found'}]);\r\n  }\r\n  $('#hv-h',r).addEventListener('input',run);run();\r\n}});\r\n\r\n\/* ===== JS \/ CSS \/ HTML Minifier ===== *\/\r\ntool({id:'js-css-html-minifier',name:'JS \/ CSS \/ HTML Minifier',cat:'code',mode:'here',kw:'minify minifier compress js javascript css html shrink size whitespace comments',\r\ndesc:'Remove comments and extra whitespace from HTML, CSS and JavaScript to cut file size.',\r\ntpl:`<div class=\"col\"><div class=\"seg\" id=\"mn-t\" role=\"group\" aria-label=\"Language\" style=\"justify-self:start\"><button type=\"button\" data-k=\"html\" aria-pressed=\"true\">HTML<\/button><button type=\"button\" data-k=\"css\" aria-pressed=\"false\">CSS<\/button><button type=\"button\" data-k=\"js\" aria-pressed=\"false\">JavaScript<\/button><\/div>\r\n<div class=\"split\"><div class=\"field\"><label for=\"mn-in\">Your code<\/label><textarea id=\"mn-in\" class=\"code\" rows=\"16\" spellcheck=\"false\"><\/textarea><\/div>\r\n<div class=\"field\"><label for=\"mn-out\">Minified<\/label><textarea id=\"mn-out\" class=\"code\" rows=\"16\" readonly><\/textarea><\/div><\/div>\r\n<div class=\"stats\" id=\"mn-stats\"><\/div>\r\n<div class=\"btn-row\"><button type=\"button\" class=\"btn primary\" id=\"mn-copy\">Copy minified code<\/button><button type=\"button\" class=\"btn ghost\" id=\"mn-sample\">Load example<\/button><\/div><\/div>`,\r\nnote:'Safe mode: it keeps line breaks that could change how JavaScript runs and never renames variables, so the result behaves the same. Always test minified code before publishing.',\r\ninit:function(r){\r\n  var kind='html',SAMPLE={\r\n  html:'<!doctype html>\\n<html lang=\"en\">\\n  <head>\\n    <meta charset=\"utf-8\">\\n    <title>Example Store<\/title>\\n    <\\!-- main styles -->\\n    <style>\\n      body { margin: 0; font-family: sans-serif; }\\n      .hero { padding: 24px; }\\n    <\/style>\\n  <\/head>\\n  <body>\\n    <div class=\"hero\">\\n      <h1>Welcome<\/h1>\\n      <p>Handloom   cotton <b>kurtas<\/b> <i>for every day<\/i>.<\/p>\\n    <\/div>\\n    <script>\\n      \/\/ greet the visitor\\n      var name = \"friend\";\\n      console.log( \"Hello, \" + name );\\n    <\\\/script>\\n  <\/body>\\n<\/html>\\n',\r\n  css:'\/* Buttons *\/\\n.btn {\\n  color : #fff ;\\n  background : #4F46E5 ;\\n  padding : 12px 18px ;\\n}\\n\\n.btn:hover {\\n  background : #4338CA ;\\n}\\n\\n@media (min-width: 600px) {\\n  .btn { padding : 14px 22px ; }\\n}\\n',\r\n  js:'\/\/ Add two prices and format them\\nfunction total(items) {\\n  var sum = 0;\\n  for (var i = 0; i < items.length; i++) {\\n    sum += items[i].price * items[i].qty; \/* line total *\/\\n  }\\n  return \"Rs. \" + sum.toFixed(2);\\n}\\n\\nconsole.log( total([{ price: 499, qty: 2 }, { price: 1299, qty: 1 }]) );\\n'};\r\n  var texts={html:SAMPLE.html,css:SAMPLE.css,js:SAMPLE.js};\r\n  function run(){\r\n    var src=$('#mn-in',r).value;texts[kind]=src;var out='';\r\n    try{out=kind==='html'?minHTML(src):kind==='css'?minCSS(src):minJS(src)}catch(e){out=''}\r\n    $('#mn-out',r).value=out;\r\n    var a=new Blob([src]).size,b=new Blob([out]).size;\r\n    $('#mn-stats',r).innerHTML='<div class=\"stat\"><span>Original<\/span><strong>'+bytes(a)+'<\/strong><\/div><div class=\"stat key\"><span>Minified<\/span><strong>'+bytes(b)+'<\/strong><\/div><div class=\"stat\"><span>Saved<\/span><strong>'+(a?((1-b\/a)*100).toFixed(0):0)+'%<\/strong><\/div>';\r\n  }\r\n  function setKind(k){kind=k;$$('#mn-t button',r).forEach(function(b){b.setAttribute('aria-pressed',b.getAttribute('data-k')===k)});$('#mn-in',r).value=texts[k];run()}\r\n  $('#mn-t',r).addEventListener('click',function(e){var b=e.target.closest('button');if(b)setKind(b.getAttribute('data-k'))});\r\n  $('#mn-in',r).addEventListener('input',run);\r\n  $('#mn-sample',r).addEventListener('click',function(){texts[kind]=SAMPLE[kind];$('#mn-in',r).value=SAMPLE[kind];run()});\r\n  wireCopy(r,'#mn-copy','#mn-out');setKind('html');\r\n}});\r\n\r\n\r\n\/* ===== boot ===== *\/\r\nrenderDir();\r\n(function(){var id=location.hash.slice(1);if(id)openTool(id,false)})();\r\n})();\r\n<\/script>\r\n\r\n<\/body><\/html>\t\t\t\t<\/div>\n\t\t\n<\/div>\n\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>Onground Media SEO Tools Onground Media All tools Onground Media \u00b7 Technical SEO 26 technical SEO tools, one search away. [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"site-sidebar-layout":"no-sidebar","site-content-layout":"","ast-site-content-layout":"full-width-container","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"disabled","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"default","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"set","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"class_list":["post-270","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/ongroundmedia.com\/index.php?rest_route=\/wp\/v2\/pages\/270","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ongroundmedia.com\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/ongroundmedia.com\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/ongroundmedia.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/ongroundmedia.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=270"}],"version-history":[{"count":4,"href":"https:\/\/ongroundmedia.com\/index.php?rest_route=\/wp\/v2\/pages\/270\/revisions"}],"predecessor-version":[{"id":275,"href":"https:\/\/ongroundmedia.com\/index.php?rest_route=\/wp\/v2\/pages\/270\/revisions\/275"}],"wp:attachment":[{"href":"https:\/\/ongroundmedia.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=270"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}