{"id":833,"date":"2026-03-30T09:53:59","date_gmt":"2026-03-30T09:53:59","guid":{"rendered":"https:\/\/primetoolhub.com\/?page_id=833"},"modified":"2026-08-08T06:25:40","modified_gmt":"2026-08-08T06:25:40","slug":"html-entity-encoder-decoder","status":"publish","type":"page","link":"https:\/\/schoolict.net\/tools\/html-entity-encoder-decoder\/","title":{"rendered":"HTML Entity Encoder &amp; Decoder"},"content":{"rendered":"\n<div class=\"pth-hero-section\">\n    <div class=\"pth-hero-content\">\n        <h2>Safely Encode and Decode HTML Entities<\/h2>\n        <p>Safely encode and decode HTML entities, CSS escapes, and JS Unicode offline. Perfect for web developers displaying code snippets securely without breaking layouts.<\/p>\n\n        <div id=\"pth-toc-placeholder\"><\/div>\n    <\/div>\n\n    <div class=\"pth-hero-image\">\n        <img decoding=\"async\" data-no-lazy=\"1\" width=\"450\" height=\"253\" src=\"https:\/\/schoolict.net\/tools\/wp-content\/uploads\/2026\/03\/html-entity-encoder-1024x572.jpeg\" alt=\"html entity encoder\">\n    <\/div>\n<\/div>\n\n\n\n<div class=\"wp-block-rank-math-toc-block\" id=\"rank-math-toc\"><h2>Table of Contents<\/h2><nav><ul><li><a href=\"#\ud83d\udd34-what-each-tab-actually-does\">\ud83d\udd34 What Each Tab Actually Does<\/a><ul><li><a href=\"#convert-nine-escape-formats\">Convert \u2014 nine escape formats<\/a><\/li><li><a href=\"#inspector-one-row-per-character\">Inspector \u2014 one row per character<\/a><\/li><li><a href=\"#cleaner-the-invisible-character-scanner\">Cleaner \u2014 the invisible character scanner<\/a><\/li><li><a href=\"#cheat-sheet-130-entities-searchable\">Cheat Sheet \u2014 130+ entities, searchable<\/a><\/li><\/ul><\/li><li><a href=\"#\ud83d\udfe1-three-jobs-this-page-handles-well\">\ud83d\udfe1 Three Jobs This Page Handles Well<\/a><\/li><li><a href=\"#\ud83d\udfe2-mistakes-and-limits-worth-knowing\">\ud83d\udfe2 Mistakes and Limits Worth Knowing<\/a><ul><li><a href=\"#escaping-is-not-the-same-as-sanitising\">Escaping is not the same as sanitising<\/a><\/li><li><a href=\"#each-format-belongs-to-one-context\">Each format belongs to one context<\/a><\/li><li><a href=\"#what-this-page-deliberately-does-not-do\">What this page deliberately does not do<\/a><\/li><li><a href=\"#performance-ceilings-honestly-stated\">Performance ceilings, honestly stated<\/a><\/li><\/ul><\/li><li><a href=\"#\ud83d\udd34-where-to-go-next\">\ud83d\udd34 Where to Go Next<\/a><\/li><\/ul><\/nav><\/div>\n\n\n<style>\r\n\/* ==========================================================================\r\n   PRIME TOOL HUB - ENTITY & ESCAPE STUDIO PRO v3.1\r\n   All-in-One: Convert \/ Inspect \/ Clean \/ Cheat Sheet\r\n   ========================================================================== *\/\r\n#pth-entity-studio-wrapper { font-family: 'Inter', system-ui, -apple-system, sans-serif; max-width: 1400px; margin: 0 auto; background: #f8fafc; padding: 20px; border-radius: 16px; color: #0f172a; line-height: 1.6; opacity: 1; }\r\n#pth-entity-studio-wrapper * { box-sizing: border-box; min-width: 0; }\r\n\r\n#pth-entity-studio-wrapper .pth-head { display: flex; justify-content: space-between; align-items: flex-start; flex-wrap: wrap; gap: 12px; margin-bottom: 18px; }\r\n#pth-entity-studio-wrapper .pth-h2 { font-size: 30px; font-weight: 800; color: #1d4ed8; margin: 0 0 8px 0; letter-spacing: -0.5px; }\r\n#pth-entity-studio-wrapper .pth-sub { font-size: 15px; color: #475569; margin: 0 0 10px 0; font-weight: 500; }\r\n#pth-entity-studio-wrapper .pth-badges { display: flex; gap: 8px; flex-wrap: wrap; }\r\n#pth-entity-studio-wrapper .pth-version-badge { background: #dbeafe; color: #1e40af; border: 1px solid #bfdbfe; padding: 4px 10px; border-radius: 999px; font-size: 12px; font-weight: 700; }\r\n#pth-entity-studio-wrapper .pth-status-offline { background: #dcfce7; color: #166534; border: 1px solid #bbf7d0; padding: 4px 10px; border-radius: 999px; font-size: 12px; font-weight: 700; }\r\n#pth-entity-studio-wrapper .pth-head-btns { display: flex; gap: 8px; flex-wrap: wrap; }\r\n\r\n#pth-entity-studio-wrapper .pth-btn { padding: 9px 16px; border-radius: 8px; font-weight: 700; cursor: pointer; border: 1px solid #e2e8f0; font-family: inherit; font-size: 14px; transition: 0.15s; background: #ffffff; color: #334155; }\r\n#pth-entity-studio-wrapper .pth-btn:hover { background: #f1f5f9; color: #0f172a; }\r\n#pth-entity-studio-wrapper .pth-btn.primary { background: #1d4ed8; color: #ffffff; border-color: #1d4ed8; }\r\n#pth-entity-studio-wrapper .pth-btn.primary:hover { background: #1e40af; color: #ffffff; }\r\n#pth-entity-studio-wrapper .pth-btn.danger { background: #fee2e2; color: #991b1b; border-color: #fecaca; }\r\n#pth-entity-studio-wrapper .pth-btn.danger:hover { background: #fecaca; color: #7f1d1d; }\r\n#pth-entity-studio-wrapper .pth-btn:focus-visible { outline: 3px solid #93c5fd; outline-offset: 2px; }\r\n\r\n\/* Tabs *\/\r\n#pth-entity-studio-wrapper .pth-tabbar { display: flex; gap: 6px; flex-wrap: wrap; border-bottom: 2px solid #e2e8f0; margin-bottom: 20px; padding-bottom: 0; }\r\n#pth-entity-studio-wrapper .pth-tab { padding: 11px 18px; border: none; background: transparent; font-family: inherit; font-size: 14px; font-weight: 700; color: #64748b; cursor: pointer; border-bottom: 3px solid transparent; margin-bottom: -2px; transition: 0.15s; }\r\n#pth-entity-studio-wrapper .pth-tab:hover { color: #1d4ed8; }\r\n#pth-entity-studio-wrapper .pth-tab.active { color: #1d4ed8; border-bottom-color: #1d4ed8; }\r\n#pth-entity-studio-wrapper .pth-tab:focus-visible { outline: 3px solid #93c5fd; outline-offset: -3px; }\r\n#pth-entity-studio-wrapper .pth-panel { display: none; }\r\n#pth-entity-studio-wrapper .pth-panel.active { display: block; }\r\n\r\n\/* Grid *\/\r\n#pth-entity-studio-wrapper .pth-grid { display: grid; grid-template-columns: 1.8fr 1fr; gap: 22px; align-items: start; transition: 0.25s ease; }\r\n#pth-entity-studio-wrapper .pth-grid.pth-full-width { grid-template-columns: 1fr; }\r\n#pth-entity-studio-wrapper .pth-card { background: #ffffff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 22px; box-shadow: 0 4px 6px -1px rgba(0,0,0,0.05); }\r\n\r\n\/* Controls *\/\r\n#pth-entity-studio-wrapper .pth-ctrlbar { display: flex; gap: 16px; flex-wrap: wrap; align-items: flex-end; margin-bottom: 18px; padding-bottom: 16px; border-bottom: 1px solid #f1f5f9; }\r\n#pth-entity-studio-wrapper .pth-modegroup { display: inline-flex; background: #f1f5f9; border-radius: 8px; padding: 4px; gap: 4px; }\r\n#pth-entity-studio-wrapper .pth-mode-btn { padding: 7px 18px; border: none; background: transparent; border-radius: 6px; font-family: inherit; font-size: 14px; font-weight: 700; color: #475569; cursor: pointer; transition: 0.15s; }\r\n#pth-entity-studio-wrapper .pth-mode-btn.active { background: #ffffff; color: #1d4ed8; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }\r\n#pth-entity-studio-wrapper .pth-field { display: flex; flex-direction: column; gap: 6px; }\r\n#pth-entity-studio-wrapper .pth-label { font-size: 13px; font-weight: 700; color: #334155; }\r\n#pth-entity-studio-wrapper .pth-select, #pth-entity-studio-wrapper .pth-textbox { padding: 9px 12px; border: 1px solid #cbd5e1; border-radius: 8px; font-family: inherit; font-size: 14px; color: #0f172a; background: #ffffff; }\r\n#pth-entity-studio-wrapper .pth-select:focus, #pth-entity-studio-wrapper .pth-textbox:focus { outline: 3px solid #93c5fd; outline-offset: 1px; border-color: #1d4ed8; }\r\n\r\n#pth-entity-studio-wrapper .pth-io { margin-bottom: 18px; }\r\n#pth-entity-studio-wrapper .pth-io-head { display: flex; justify-content: space-between; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 7px; }\r\n#pth-entity-studio-wrapper .pth-textarea { width: 100%; min-height: 170px; padding: 13px; border: 1px solid #cbd5e1; border-radius: 8px; font-family: 'Consolas', 'Monaco', monospace; font-size: 13.5px; line-height: 1.65; color: #0f172a; background: #ffffff; resize: vertical; }\r\n#pth-entity-studio-wrapper .pth-textarea:focus { outline: 3px solid #93c5fd; outline-offset: 1px; border-color: #1d4ed8; }\r\n#pth-entity-studio-wrapper .pth-textarea.output { background: #f8fafc; color: #1e293b; }\r\n#pth-entity-studio-wrapper .pth-stat { background: #f1f5f9; color: #475569; padding: 3px 10px; border-radius: 999px; font-size: 12px; font-weight: 700; }\r\n\r\n\/* Tables *\/\r\n#pth-entity-studio-wrapper .pth-tablewrap { max-height: 460px; overflow: auto; border: 1px solid #e2e8f0; border-radius: 8px; min-height: 120px; }\r\n#pth-entity-studio-wrapper table { width: 100%; border-collapse: collapse; font-size: 13px; }\r\n#pth-entity-studio-wrapper th { background: #f1f5f9; color: #334155; text-align: left; padding: 9px 11px; font-weight: 800; position: sticky; top: 0; font-size: 12px; text-transform: uppercase; letter-spacing: 0.4px; }\r\n#pth-entity-studio-wrapper td { padding: 8px 11px; border-top: 1px solid #f1f5f9; color: #334155; font-family: 'Consolas', 'Monaco', monospace; }\r\n#pth-entity-studio-wrapper tr:hover td { background: #f8fafc; }\r\n#pth-entity-studio-wrapper .pth-charcell { font-family: inherit; font-size: 17px; font-weight: 700; color: #0f172a; }\r\n\r\n\/* Cleaner findings *\/\r\n#pth-entity-studio-wrapper .pth-finding { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 10px 13px; border-radius: 8px; margin-bottom: 8px; font-size: 13.5px; }\r\n#pth-entity-studio-wrapper .pth-finding.warn { background: #fef2f2; border: 1px solid #fecaca; color: #991b1b; }\r\n#pth-entity-studio-wrapper .pth-finding.info { background: #fffbeb; border: 1px solid #fde68a; color: #92400e; }\r\n#pth-entity-studio-wrapper .pth-finding.ok { background: #f0fdf4; border: 1px solid #bbf7d0; color: #166534; }\r\n#pth-entity-studio-wrapper .pth-count { font-weight: 800; font-family: 'Consolas', monospace; }\r\n#pth-entity-studio-wrapper .pth-checkrow { display: flex; align-items: center; gap: 9px; padding: 7px 0; font-size: 13.5px; color: #334155; }\r\n#pth-entity-studio-wrapper .pth-checkrow input { width: 16px; height: 16px; accent-color: #1d4ed8; cursor: pointer; }\r\n#pth-entity-studio-wrapper .pth-preview { border: 1px solid #cbd5e1; border-radius: 8px; padding: 13px; background: #ffffff; font-family: 'Consolas', 'Monaco', monospace; font-size: 13.5px; line-height: 1.9; color: #0f172a; min-height: 90px; max-height: 260px; overflow: auto; white-space: pre-wrap; word-break: break-word; }\r\n#pth-entity-studio-wrapper .pth-mark { background: #fecaca; color: #7f1d1d; border-radius: 3px; padding: 1px 4px; font-size: 11px; font-weight: 800; }\r\n#pth-entity-studio-wrapper .pth-mark.q { background: #fde68a; color: #78350f; }\r\n\r\n\/* Cheat sheet *\/\r\n#pth-entity-studio-wrapper .pth-chipbar { display: flex; gap: 7px; flex-wrap: wrap; margin: 13px 0; }\r\n#pth-entity-studio-wrapper .pth-chip { padding: 6px 13px; border-radius: 999px; border: 1px solid #cbd5e1; background: #ffffff; color: #475569; font-family: inherit; font-size: 12.5px; font-weight: 700; cursor: pointer; transition: 0.15s; }\r\n#pth-entity-studio-wrapper .pth-chip:hover { background: #eff6ff; color: #1d4ed8; }\r\n#pth-entity-studio-wrapper .pth-chip.active { background: #1d4ed8; color: #ffffff; border-color: #1d4ed8; }\r\n#pth-entity-studio-wrapper .pth-copycell { cursor: pointer; color: #1d4ed8; font-weight: 700; }\r\n#pth-entity-studio-wrapper .pth-copycell:hover { text-decoration: underline; }\r\n\r\n\/* Sidebar *\/\r\n#pth-entity-studio-wrapper .pth-side h3 { font-size: 15px; font-weight: 800; color: #1e293b; margin: 0 0 9px 0; }\r\n#pth-entity-studio-wrapper .pth-side h3.spaced { margin-top: 22px; }\r\n#pth-entity-studio-wrapper .pth-side p { font-size: 13.5px; color: #475569; margin: 0 0 12px 0; }\r\n#pth-entity-studio-wrapper .pth-side ul { margin: 0 0 12px 0; padding-left: 18px; }\r\n#pth-entity-studio-wrapper .pth-side li { font-size: 13.5px; color: #475569; margin-bottom: 6px; }\r\n#pth-entity-studio-wrapper .pth-side a { color: #1d4ed8; font-weight: 700; text-decoration: none; }\r\n#pth-entity-studio-wrapper .pth-side a:hover { text-decoration: underline; }\r\n#pth-entity-studio-wrapper code { background: #f1f5f9; padding: 2px 6px; border-radius: 4px; font-family: 'Consolas', monospace; font-size: 12.5px; color: #1e293b; }\r\n#pth-entity-studio-wrapper .pth-tip { background: #eff6ff; border: 1px solid #bfdbfe; padding: 13px; border-radius: 8px; font-size: 13px; color: #1e40af; line-height: 1.6; }\r\n\r\n\/* Toast *\/\r\n#pth-entity-studio-wrapper .pth-toast { position: fixed; bottom: 24px; right: 24px; background: #0f172a; color: #ffffff; padding: 13px 22px; border-radius: 10px; font-size: 14px; font-weight: 700; box-shadow: 0 10px 25px rgba(0,0,0,0.2); z-index: 99999; display: none; }\r\n#pth-entity-studio-wrapper .pth-toast.err { background: #991b1b; }\r\n\r\n@media (max-width: 900px) {\r\n    #pth-entity-studio-wrapper { padding: 14px; }\r\n    #pth-entity-studio-wrapper .pth-grid { grid-template-columns: 1fr; }\r\n    #pth-entity-studio-wrapper .pth-h2 { font-size: 24px; }\r\n    #pth-entity-studio-wrapper .pth-tab { padding: 10px 13px; font-size: 13px; }\r\n}\r\n<\/style>\r\n\r\n<div id=\"pth-entity-studio-wrapper\">\r\n\r\n    <div id=\"pth-toast-entity\" class=\"pth-toast\" role=\"status\" aria-live=\"polite\">Copied to clipboard<\/div>\r\n\r\n    <div class=\"pth-head\">\r\n        <div>\r\n            <h2 class=\"pth-h2\">\u2328\ufe0f Entity & Escape Studio Pro<\/h2>\r\n            <p class=\"pth-sub\">Convert, inspect and clean HTML entities, CSS\/JS escapes and invisible Unicode characters \u2014 all in your browser.<\/p>\r\n            <div class=\"pth-badges\">\r\n                <span class=\"pth-version-badge\">v3.1<\/span>\r\n                <span class=\"pth-status-offline\">\ud83d\udd12 100% Offline<\/span>\r\n            <\/div>\r\n        <\/div>\r\n        <div class=\"pth-head-btns\">\r\n            <button type=\"button\" class=\"pth-btn\" id=\"pth-sample-btn\">\ud83d\udca1 Load Sample<\/button>\r\n            <button type=\"button\" class=\"pth-btn\" id=\"pth-fw-btn\">\u26f6 Full Width<\/button>\r\n            <button type=\"button\" class=\"pth-btn danger\" id=\"pth-clear-btn\">\ud83d\uddd1\ufe0f Clear<\/button>\r\n        <\/div>\r\n    <\/div>\r\n\r\n    <div class=\"pth-tabbar\" role=\"tablist\">\r\n        <button type=\"button\" class=\"pth-tab active\" id=\"pth-tab-convert\" data-pth-panel=\"convert\" role=\"tab\">\ud83d\udd04 Convert<\/button>\r\n        <button type=\"button\" class=\"pth-tab\" id=\"pth-tab-inspect\" data-pth-panel=\"inspect\" role=\"tab\">\ud83d\udd0e Inspector<\/button>\r\n        <button type=\"button\" class=\"pth-tab\" id=\"pth-tab-clean\" data-pth-panel=\"clean\" role=\"tab\">\ud83e\uddf9 Cleaner<\/button>\r\n        <button type=\"button\" class=\"pth-tab\" id=\"pth-tab-cheat\" data-pth-panel=\"cheat\" role=\"tab\">\ud83d\udcd6 Cheat Sheet<\/button>\r\n    <\/div>\r\n\r\n    <div class=\"pth-grid\" id=\"pth-grid-entity\">\r\n\r\n        <div>\r\n            <!-- ============ PANEL 1: CONVERT ============ -->\r\n            <div class=\"pth-panel active\" id=\"pth-panel-convert\" role=\"tabpanel\">\r\n                <div class=\"pth-card\">\r\n                    <div class=\"pth-ctrlbar\">\r\n                        <div class=\"pth-modegroup\">\r\n                            <button type=\"button\" class=\"pth-mode-btn active\" id=\"pth-mode-encode\">Encode<\/button>\r\n                            <button type=\"button\" class=\"pth-mode-btn\" id=\"pth-mode-decode\">Decode<\/button>\r\n                        <\/div>\r\n                        <div class=\"pth-field\" style=\"flex:1; min-width:230px;\">\r\n                            <label class=\"pth-label\" for=\"pth-format-sel\">Conversion Format<\/label>\r\n                            <select id=\"pth-format-sel\" class=\"pth-select\" aria-label=\"Conversion format\">\r\n                                <option value=\"html\">HTML Entities \u2014 essential characters<\/option>\r\n                                <option value=\"html-all\">HTML Entities \u2014 all non-ASCII<\/option>\r\n                                <option value=\"html-named\">HTML Named Entities (&copy; &hellip;)<\/option>\r\n                                <option value=\"xml\">XML \/ XSLT (5 predefined only)<\/option>\r\n                                <option value=\"css\">CSS Escape (\\003C)<\/option>\r\n                                <option value=\"js\">JS Unicode (\\u003C)<\/option>\r\n                                <option value=\"es6\">ES6 Codepoint (\\u{1F600})<\/option>\r\n                                <option value=\"json\">JSON String Escape (\\n \\t \\\")<\/option>\r\n                                <option value=\"codepoint\">Unicode Notation (U+003C)<\/option>\r\n                            <\/select>\r\n                        <\/div>\r\n                    <\/div>\r\n\r\n                    <div class=\"pth-io\">\r\n                        <div class=\"pth-io-head\">\r\n                            <label class=\"pth-label\" id=\"pth-in-label\" for=\"pth-input\">Text to Encode<\/label>\r\n                            <span class=\"pth-stat\" id=\"pth-in-stat\">0 chars<\/span>\r\n                        <\/div>\r\n                        <textarea id=\"pth-input\" class=\"pth-textarea\" placeholder=\"Type or paste your code snippet here...\" aria-label=\"Input text\"><\/textarea>\r\n                    <\/div>\r\n\r\n                    <div class=\"pth-io\">\r\n                        <div class=\"pth-io-head\">\r\n                            <label class=\"pth-label\" id=\"pth-out-label\" for=\"pth-output\">Encoded Result<\/label>\r\n                            <div style=\"display:flex; gap:8px; align-items:center;\">\r\n                                <span class=\"pth-stat\" id=\"pth-out-stat\">0 chars<\/span>\r\n                                <button type=\"button\" class=\"pth-btn primary\" id=\"pth-copy-btn\">Copy Result<\/button>\r\n                            <\/div>\r\n                        <\/div>\r\n                        <textarea id=\"pth-output\" class=\"pth-textarea output\" readonly placeholder=\"Result appears here...\" aria-label=\"Converted result\"><\/textarea>\r\n                    <\/div>\r\n                <\/div>\r\n            <\/div>\r\n\r\n            <!-- ============ PANEL 2: INSPECTOR ============ -->\r\n            <div class=\"pth-panel\" id=\"pth-panel-inspect\" role=\"tabpanel\">\r\n                <div class=\"pth-card\">\r\n                    <div class=\"pth-io\">\r\n                        <div class=\"pth-io-head\">\r\n                            <label class=\"pth-label\" for=\"pth-inspect-input\">Text to Inspect<\/label>\r\n                            <span class=\"pth-stat\" id=\"pth-inspect-stat\">0 characters<\/span>\r\n                        <\/div>\r\n                        <textarea id=\"pth-inspect-input\" class=\"pth-textarea\" style=\"min-height:110px;\" placeholder=\"Paste any text or a single character...\" aria-label=\"Text to inspect\"><\/textarea>\r\n                    <\/div>\r\n                    <div class=\"pth-tablewrap\">\r\n                        <table>\r\n                            <thead>\r\n                                <tr><th>Char<\/th><th>Name<\/th><th>Code<\/th><th>Dec<\/th><th>UTF-8<\/th><th>HTML<\/th><th>CSS<\/th><th>JS<\/th><\/tr>\r\n                            <\/thead>\r\n                            <tbody id=\"pth-inspect-body\"><\/tbody>\r\n                        <\/table>\r\n                    <\/div>\r\n                    <p style=\"font-size:12.5px; color:#64748b; margin:11px 0 0 0;\">Emoji and other astral characters are shown as one row, not as two broken halves.<\/p>\r\n                <\/div>\r\n            <\/div>\r\n\r\n            <!-- ============ PANEL 3: CLEANER ============ -->\r\n            <div class=\"pth-panel\" id=\"pth-panel-clean\" role=\"tabpanel\">\r\n                <div class=\"pth-card\">\r\n                    <div class=\"pth-io\">\r\n                        <div class=\"pth-io-head\">\r\n                            <label class=\"pth-label\" for=\"pth-clean-input\">Paste Text to Scan<\/label>\r\n                            <span class=\"pth-stat\" id=\"pth-clean-stat\">0 issues<\/span>\r\n                        <\/div>\r\n                        <textarea id=\"pth-clean-input\" class=\"pth-textarea\" style=\"min-height:130px;\" placeholder=\"Paste text copied from Word, Google Docs or a PDF...\" aria-label=\"Text to clean\"><\/textarea>\r\n                    <\/div>\r\n\r\n                    <h3 style=\"font-size:15px; font-weight:800; color:#1e293b; margin:0 0 10px 0;\">What we found<\/h3>\r\n                    <div id=\"pth-findings\"><\/div>\r\n\r\n                    <h3 style=\"font-size:15px; font-weight:800; color:#1e293b; margin:20px 0 6px 0;\">Highlighted preview<\/h3>\r\n                    <div class=\"pth-preview\" id=\"pth-clean-preview\" aria-live=\"polite\"><\/div>\r\n\r\n                    <h3 style=\"font-size:15px; font-weight:800; color:#1e293b; margin:20px 0 6px 0;\">Fixes to apply<\/h3>\r\n                    <div class=\"pth-checkrow\"><input type=\"checkbox\" id=\"pth-fix-invisible\" checked><label for=\"pth-fix-invisible\">Remove zero-width and invisible characters<\/label><\/div>\r\n                    <div class=\"pth-checkrow\"><input type=\"checkbox\" id=\"pth-fix-nbsp\" checked><label for=\"pth-fix-nbsp\">Replace non-breaking spaces with normal spaces<\/label><\/div>\r\n                    <div class=\"pth-checkrow\"><input type=\"checkbox\" id=\"pth-fix-quotes\" checked><label for=\"pth-fix-quotes\">Straighten curly quotes and apostrophes<\/label><\/div>\r\n                    <div class=\"pth-checkrow\"><input type=\"checkbox\" id=\"pth-fix-dashes\"><label for=\"pth-fix-dashes\">Convert en\/em dashes and ellipsis to plain ASCII<\/label><\/div>\r\n                    <div class=\"pth-checkrow\"><input type=\"checkbox\" id=\"pth-fix-bidi\" checked><label for=\"pth-fix-bidi\">Strip bidirectional override marks (spoofing risk)<\/label><\/div>\r\n                    <div class=\"pth-checkrow\"><input type=\"checkbox\" id=\"pth-fix-trailing\"><label for=\"pth-fix-trailing\">Trim trailing spaces on every line<\/label><\/div>\r\n\r\n                    <div style=\"display:flex; gap:9px; flex-wrap:wrap; margin:16px 0;\">\r\n                        <button type=\"button\" class=\"pth-btn primary\" id=\"pth-clean-btn\">\ud83e\uddf9 Clean Text<\/button>\r\n                        <button type=\"button\" class=\"pth-btn\" id=\"pth-clean-copy-btn\">Copy Cleaned<\/button>\r\n                    <\/div>\r\n\r\n                    <div class=\"pth-io\" style=\"margin-bottom:0;\">\r\n                        <div class=\"pth-io-head\"><label class=\"pth-label\" for=\"pth-clean-output\">Cleaned Result<\/label><\/div>\r\n                        <textarea id=\"pth-clean-output\" class=\"pth-textarea output\" readonly style=\"min-height:130px;\" placeholder=\"Cleaned text appears here...\" aria-label=\"Cleaned result\"><\/textarea>\r\n                    <\/div>\r\n                <\/div>\r\n            <\/div>\r\n\r\n            <!-- ============ PANEL 4: CHEAT SHEET ============ -->\r\n            <div class=\"pth-panel\" id=\"pth-panel-cheat\" role=\"tabpanel\">\r\n                <div class=\"pth-card\">\r\n                    <div class=\"pth-field\">\r\n                        <label class=\"pth-label\" for=\"pth-cheat-search\">Search entities<\/label>\r\n                        <input type=\"text\" id=\"pth-cheat-search\" class=\"pth-textbox\" placeholder=\"Try: arrow, euro, alpha, quote, 8364...\" aria-label=\"Search entities\">\r\n                    <\/div>\r\n                    <div class=\"pth-chipbar\" id=\"pth-cheat-chips\"><\/div>\r\n                    <div class=\"pth-tablewrap\">\r\n                        <table>\r\n                            <thead>\r\n                                <tr><th>Char<\/th><th>Named<\/th><th>Number<\/th><th>Hex<\/th><th>Description<\/th><\/tr>\r\n                            <\/thead>\r\n                            <tbody id=\"pth-cheat-body\"><\/tbody>\r\n                        <\/table>\r\n                    <\/div>\r\n                    <p style=\"font-size:12.5px; color:#64748b; margin:11px 0 0 0;\">Click any entity code to copy it. <span id=\"pth-cheat-count\"><\/span><\/p>\r\n                <\/div>\r\n            <\/div>\r\n        <\/div>\r\n\r\n        <!-- ============ SIDEBAR ============ -->\r\n        <div class=\"pth-card pth-side\" id=\"pth-side-entity\">\r\n            <div id=\"pth-side-convert\">\r\n                <h3>When you need this<\/h3>\r\n                <p>If you publish a code snippet on a web page without escaping it, the browser reads <code><div><\/code> as a real tag and renders it instead of showing it. Encoding turns those characters into safe text.<\/p>\r\n                <h3 class=\"spaced\">Which format to pick<\/h3>\r\n                <ul>\r\n                    <li><strong>HTML essential<\/strong> \u2014 for text inside a page or an attribute.<\/li>\r\n                    <li><strong>XML<\/strong> \u2014 only the five characters XML defines. Named HTML entities like <code>&nbsp;<\/code> break XML parsers.<\/li>\r\n                    <li><strong>CSS<\/strong> \u2014 for a <code>content: \"\"<\/code> rule.<\/li>\r\n                    <li><strong>JS Unicode<\/strong> \u2014 for string literals in scripts.<\/li>\r\n                    <li><strong>ES6 codepoint<\/strong> \u2014 for emoji and other characters above U+FFFF.<\/li>\r\n                <\/ul>\r\n                <div class=\"pth-tip\"><strong>Watch out:<\/strong> HTML entity decoding is not a security filter. Escaping output protects a page; decoding untrusted input does not.<\/div>\r\n            <\/div>\r\n            <div id=\"pth-side-inspect\" style=\"display:none;\">\r\n                <h3>Reading the table<\/h3>\r\n                <p>Every row is one real character. An emoji such as \ud83d\ude00 is stored as two JavaScript units but is a single character \u2014 the inspector groups it correctly.<\/p>\r\n                <h3 class=\"spaced\">Why UTF-8 bytes matter<\/h3>\r\n                <p>A plain Latin letter costs one byte. An accented letter costs two, most CJK characters three, and emoji four. That is why a database column limited by bytes rather than characters overflows sooner than expected.<\/p>\r\n                <div class=\"pth-tip\"><strong>Handy check:<\/strong> if a character renders as a box, compare its codepoint here against the font you are using before assuming the data is corrupted.<\/div>\r\n            <\/div>\r\n            <div id=\"pth-side-clean\" style=\"display:none;\">\r\n                <h3>The paste problem<\/h3>\r\n                <p>Text copied from Word, Google Docs or a PDF carries characters you cannot see. A non-breaking space looks identical to a normal space but breaks code, and a curly apostrophe inside a JavaScript string causes a syntax error that is genuinely hard to spot.<\/p>\r\n                <h3 class=\"spaced\">Zero-width characters<\/h3>\r\n                <p>Zero-width spaces and joiners occupy no visual width at all. They survive copy and paste, sit inside filenames and CSS class names, and produce selectors that look correct but never match.<\/p>\r\n                <div class=\"pth-tip\"><strong>Security note:<\/strong> bidirectional override marks can reorder how source code is displayed without changing what runs. Stripping them is a sensible default for any pasted code.<\/div>\r\n            <\/div>\r\n            <div id=\"pth-side-cheat\" style=\"display:none;\">\r\n                <h3>Named or numeric?<\/h3>\r\n                <p>Named entities such as <code>&copy;<\/code> read better, but only HTML defines them. In XML, SVG or RSS, numeric references like <code>&#169;<\/code> are the safer choice because they work everywhere.<\/p>\r\n                <h3 class=\"spaced\">One common mistake<\/h3>\r\n                <p>Using <code>&nbsp;<\/code> repeatedly to push text sideways. It stops lines wrapping where they should, which hurts readability on narrow screens. Reach for CSS margin or padding instead.<\/p>\r\n                <div class=\"pth-tip\"><strong>Tip:<\/strong> search by number as well as name \u2014 typing <code>8364<\/code> finds the euro sign straight away.<\/div>\r\n            <\/div>\r\n\r\n            <h3 class=\"spaced\">Related tools<\/h3>\r\n            <ul>\r\n                <li><a href=\"https:\/\/schoolict.net\/tools\/universal-url-encoder-decoder\/\">URL Encoder \/ Decoder<\/a> \u2014 for percent-encoding in links and query strings<\/li>\r\n                <li><a href=\"https:\/\/schoolict.net\/tools\/free-base64-encoder-decoder-utf-8-supported\/\">Base64 Encoder \/ Decoder<\/a> \u2014 for binary-safe text transport<\/li>\r\n                <li><a href=\"https:\/\/schoolict.net\/tools\/advanced-text-regex-tester-debugger\/\">Regex Tester & Debugger<\/a> \u2014 to hunt these characters in bulk<\/li>\r\n                <li><a href=\"https:\/\/schoolict.net\/tools\/offline-html-editor-with-css-js\/\">Offline HTML Editor<\/a> \u2014 to preview escaped markup live<\/li>\r\n            <\/ul>\r\n        <\/div>\r\n\r\n    <\/div>\r\n<\/div>\r\n\r\n<script data-no-optimize=\"1\" data-no-minify=\"1\" data-cfasync=\"false\">\r\n(function() {\r\n    'use strict';\r\n\r\n    \/* Structural-tag + literal helpers.\r\n       WordPress\/LiteSpeed optimisation mangles raw closing-tag substrings and any\r\n       non-ASCII glyph inside a script, which kills the whole block. We build every\r\n       \"<\" and every closing tag from concatenated pieces and keep this file 100% ASCII. *\/\r\n    var LT = String.fromCharCode(60); \/* < *\/\r\n    var GT = String.fromCharCode(62); \/* > *\/\r\n    var CT = LT + '\/';                \/* closing-tag opener \"<\/\" *\/\r\n\r\n    window.pthEntityStudioApp = {\r\n        initialized: false,\r\n        currentMode: 'encode',\r\n        currentTab: 'convert',\r\n        cheatFilter: 'all',\r\n\r\n        \/* ---------- Reference data ---------- *\/\r\n        namedMap: {\r\n            '&': 'amp', '\"': 'quot', \"'\": 'apos', '\\u00A0': 'nbsp', '\\u00A9': 'copy',\r\n            '\\u00AE': 'reg', '\\u2122': 'trade', '\\u00B0': 'deg', '\\u00B1': 'plusmn',\r\n            '\\u00BC': 'frac14', '\\u00BD': 'frac12', '\\u00BE': 'frac34', '\\u00D7': 'times',\r\n            '\\u00F7': 'divide', '\\u2013': 'ndash', '\\u2014': 'mdash', '\\u2018': 'lsquo',\r\n            '\\u2019': 'rsquo', '\\u201C': 'ldquo', '\\u201D': 'rdquo', '\\u2020': 'dagger',\r\n            '\\u2022': 'bull', '\\u2026': 'hellip', '\\u2030': 'permil', '\\u20AC': 'euro',\r\n            '\\u00A3': 'pound', '\\u00A5': 'yen', '\\u00A2': 'cent', '\\u00A7': 'sect',\r\n            '\\u00B6': 'para', '\\u2190': 'larr', '\\u2191': 'uarr', '\\u2192': 'rarr',\r\n            '\\u2193': 'darr', '\\u2194': 'harr', '\\u21D2': 'rArr', '\\u2200': 'forall',\r\n            '\\u2202': 'part', '\\u221A': 'radic', '\\u221E': 'infin', '\\u2229': 'cap',\r\n            '\\u222A': 'cup', '\\u2248': 'asymp', '\\u2260': 'ne', '\\u2264': 'le',\r\n            '\\u2265': 'ge', '\\u03B1': 'alpha', '\\u03B2': 'beta', '\\u03B3': 'gamma',\r\n            '\\u03C0': 'pi', '\\u03A9': 'Omega', '\\u2660': 'spades', '\\u2665': 'hearts',\r\n            '\\u2663': 'clubs', '\\u2666': 'diams', '\\u2605': 'starf', '\\u2713': 'check',\r\n            '\\u2717': 'cross', '\\u00AB': 'laquo', '\\u00BB': 'raquo', '\\u2039': 'lsaquo',\r\n            '\\u203A': 'rsaquo', '\\u00A1': 'iexcl', '\\u00BF': 'iquest', '\\u00E9': 'eacute',\r\n            '\\u00E8': 'egrave', '\\u00E7': 'ccedil', '\\u00F1': 'ntilde', '\\u00FC': 'uuml',\r\n            '\\u00F6': 'ouml', '\\u00E4': 'auml', '\\u00DF': 'szlig', '\\u00E5': 'aring',\r\n            '\\u00F8': 'oslash', '\\u00E6': 'aelig'\r\n        },\r\n\r\n        invisibleList: [\r\n            { c: '\\u200B', n: 'Zero-width space', k: 'invisible' },\r\n            { c: '\\u200C', n: 'Zero-width non-joiner', k: 'invisible' },\r\n            { c: '\\u200D', n: 'Zero-width joiner', k: 'invisible' },\r\n            { c: '\\uFEFF', n: 'Byte order mark', k: 'invisible' },\r\n            { c: '\\u2060', n: 'Word joiner', k: 'invisible' },\r\n            { c: '\\u00AD', n: 'Soft hyphen', k: 'invisible' },\r\n            { c: '\\u180E', n: 'Mongolian vowel separator', k: 'invisible' },\r\n            { c: '\\u00A0', n: 'Non-breaking space', k: 'nbsp' },\r\n            { c: '\\u2007', n: 'Figure space', k: 'nbsp' },\r\n            { c: '\\u202F', n: 'Narrow no-break space', k: 'nbsp' },\r\n            { c: '\\u3000', n: 'Ideographic space', k: 'nbsp' },\r\n            { c: '\\u2018', n: 'Left single quote', k: 'quotes' },\r\n            { c: '\\u2019', n: 'Right single quote', k: 'quotes' },\r\n            { c: '\\u201C', n: 'Left double quote', k: 'quotes' },\r\n            { c: '\\u201D', n: 'Right double quote', k: 'quotes' },\r\n            { c: '\\u2032', n: 'Prime', k: 'quotes' },\r\n            { c: '\\u2013', n: 'En dash', k: 'dashes' },\r\n            { c: '\\u2014', n: 'Em dash', k: 'dashes' },\r\n            { c: '\\u2026', n: 'Horizontal ellipsis', k: 'dashes' },\r\n            { c: '\\u202A', n: 'Left-to-right embedding', k: 'bidi' },\r\n            { c: '\\u202B', n: 'Right-to-left embedding', k: 'bidi' },\r\n            { c: '\\u202D', n: 'Left-to-right override', k: 'bidi' },\r\n            { c: '\\u202E', n: 'Right-to-left override', k: 'bidi' },\r\n            { c: '\\u2066', n: 'Left-to-right isolate', k: 'bidi' },\r\n            { c: '\\u2069', n: 'Pop directional isolate', k: 'bidi' }\r\n        ],\r\n\r\n        charNames: {\r\n            9: 'Character tabulation', 10: 'Line feed', 13: 'Carriage return',\r\n            32: 'Space', 33: 'Exclamation mark', 34: 'Quotation mark', 35: 'Number sign',\r\n            36: 'Dollar sign', 37: 'Percent sign', 38: 'Ampersand', 39: 'Apostrophe',\r\n            40: 'Left parenthesis', 41: 'Right parenthesis', 42: 'Asterisk', 43: 'Plus sign',\r\n            44: 'Comma', 45: 'Hyphen-minus', 46: 'Full stop', 47: 'Solidus',\r\n            58: 'Colon', 59: 'Semicolon', 60: 'Less-than sign', 61: 'Equals sign',\r\n            62: 'Greater-than sign', 63: 'Question mark', 64: 'Commercial at',\r\n            91: 'Left square bracket', 92: 'Reverse solidus', 93: 'Right square bracket',\r\n            94: 'Circumflex accent', 95: 'Low line', 96: 'Grave accent',\r\n            123: 'Left curly bracket', 124: 'Vertical line', 125: 'Right curly bracket',\r\n            126: 'Tilde'\r\n        },\r\n\r\n        cheatData: [\r\n            { c: '\\u00A0', e: 'nbsp', d: 'Non-breaking space', g: 'punct' },\r\n            { c: '&', e: 'amp', d: 'Ampersand', g: 'punct' },\r\n            { c: LT, e: 'lt', d: 'Less-than sign', g: 'punct' },\r\n            { c: GT, e: 'gt', d: 'Greater-than sign', g: 'punct' },\r\n            { c: '\"', e: 'quot', d: 'Double quotation mark', g: 'punct' },\r\n            { c: \"'\", e: 'apos', d: 'Apostrophe', g: 'punct' },\r\n            { c: '\\u2018', e: 'lsquo', d: 'Left single quotation mark', g: 'punct' },\r\n            { c: '\\u2019', e: 'rsquo', d: 'Right single quotation mark', g: 'punct' },\r\n            { c: '\\u201C', e: 'ldquo', d: 'Left double quotation mark', g: 'punct' },\r\n            { c: '\\u201D', e: 'rdquo', d: 'Right double quotation mark', g: 'punct' },\r\n            { c: '\\u00AB', e: 'laquo', d: 'Left double angle quotes', g: 'punct' },\r\n            { c: '\\u00BB', e: 'raquo', d: 'Right double angle quotes', g: 'punct' },\r\n            { c: '\\u2013', e: 'ndash', d: 'En dash', g: 'punct' },\r\n            { c: '\\u2014', e: 'mdash', d: 'Em dash', g: 'punct' },\r\n            { c: '\\u2026', e: 'hellip', d: 'Horizontal ellipsis', g: 'punct' },\r\n            { c: '\\u2022', e: 'bull', d: 'Bullet', g: 'punct' },\r\n            { c: '\\u00B7', e: 'middot', d: 'Middle dot', g: 'punct' },\r\n            { c: '\\u2020', e: 'dagger', d: 'Dagger', g: 'punct' },\r\n            { c: '\\u2021', e: 'Dagger', d: 'Double dagger', g: 'punct' },\r\n            { c: '\\u00A7', e: 'sect', d: 'Section sign', g: 'punct' },\r\n            { c: '\\u00B6', e: 'para', d: 'Pilcrow \/ paragraph sign', g: 'punct' },\r\n            { c: '\\u00A1', e: 'iexcl', d: 'Inverted exclamation mark', g: 'punct' },\r\n            { c: '\\u00BF', e: 'iquest', d: 'Inverted question mark', g: 'punct' },\r\n            { c: '\\u2039', e: 'lsaquo', d: 'Single left angle quote', g: 'punct' },\r\n            { c: '\\u203A', e: 'rsaquo', d: 'Single right angle quote', g: 'punct' },\r\n\r\n            { c: '\\u20AC', e: 'euro', d: 'Euro sign', g: 'currency' },\r\n            { c: '\\u00A3', e: 'pound', d: 'Pound sterling', g: 'currency' },\r\n            { c: '\\u00A5', e: 'yen', d: 'Yen sign', g: 'currency' },\r\n            { c: '\\u00A2', e: 'cent', d: 'Cent sign', g: 'currency' },\r\n            { c: '\\u00A4', e: 'curren', d: 'Generic currency sign', g: 'currency' },\r\n            { c: '\\u20B9', e: '', d: 'Indian rupee sign', g: 'currency' },\r\n            { c: '\\u20A8', e: '', d: 'Rupee sign', g: 'currency' },\r\n            { c: '\\u20BF', e: '', d: 'Bitcoin sign', g: 'currency' },\r\n\r\n            { c: '\\u00A9', e: 'copy', d: 'Copyright sign', g: 'symbol' },\r\n            { c: '\\u00AE', e: 'reg', d: 'Registered trademark', g: 'symbol' },\r\n            { c: '\\u2122', e: 'trade', d: 'Trademark sign', g: 'symbol' },\r\n            { c: '\\u00B0', e: 'deg', d: 'Degree sign', g: 'symbol' },\r\n            { c: '\\u2605', e: 'starf', d: 'Filled star', g: 'symbol' },\r\n            { c: '\\u2606', e: 'star', d: 'Outlined star', g: 'symbol' },\r\n            { c: '\\u2665', e: 'hearts', d: 'Heart suit', g: 'symbol' },\r\n            { c: '\\u2660', e: 'spades', d: 'Spade suit', g: 'symbol' },\r\n            { c: '\\u2663', e: 'clubs', d: 'Club suit', g: 'symbol' },\r\n            { c: '\\u2666', e: 'diams', d: 'Diamond suit', g: 'symbol' },\r\n            { c: '\\u2713', e: 'check', d: 'Check mark', g: 'symbol' },\r\n            { c: '\\u2717', e: 'cross', d: 'Ballot cross', g: 'symbol' },\r\n            { c: '\\u2618', e: '', d: 'Shamrock', g: 'symbol' },\r\n            { c: '\\u260E', e: 'phone', d: 'Telephone sign', g: 'symbol' },\r\n            { c: '\\u2709', e: '', d: 'Envelope', g: 'symbol' },\r\n            { c: '\\u2764', e: '', d: 'Heavy black heart', g: 'symbol' },\r\n            { c: '\\u2699', e: '', d: 'Gear', g: 'symbol' },\r\n\r\n            { c: '\\u2190', e: 'larr', d: 'Leftwards arrow', g: 'arrow' },\r\n            { c: '\\u2191', e: 'uarr', d: 'Upwards arrow', g: 'arrow' },\r\n            { c: '\\u2192', e: 'rarr', d: 'Rightwards arrow', g: 'arrow' },\r\n            { c: '\\u2193', e: 'darr', d: 'Downwards arrow', g: 'arrow' },\r\n            { c: '\\u2194', e: 'harr', d: 'Left right arrow', g: 'arrow' },\r\n            { c: '\\u21D0', e: 'lArr', d: 'Leftwards double arrow', g: 'arrow' },\r\n            { c: '\\u21D2', e: 'rArr', d: 'Rightwards double arrow', g: 'arrow' },\r\n            { c: '\\u21D4', e: 'hArr', d: 'Left right double arrow', g: 'arrow' },\r\n            { c: '\\u21B5', e: 'crarr', d: 'Carriage return arrow', g: 'arrow' },\r\n            { c: '\\u2794', e: '', d: 'Heavy wide-headed arrow', g: 'arrow' },\r\n            { c: '\\u25B6', e: '', d: 'Black right-pointing triangle', g: 'arrow' },\r\n            { c: '\\u25C0', e: '', d: 'Black left-pointing triangle', g: 'arrow' },\r\n\r\n            { c: '\\u00B1', e: 'plusmn', d: 'Plus-minus sign', g: 'math' },\r\n            { c: '\\u00D7', e: 'times', d: 'Multiplication sign', g: 'math' },\r\n            { c: '\\u00F7', e: 'divide', d: 'Division sign', g: 'math' },\r\n            { c: '\\u2260', e: 'ne', d: 'Not equal to', g: 'math' },\r\n            { c: '\\u2264', e: 'le', d: 'Less than or equal to', g: 'math' },\r\n            { c: '\\u2265', e: 'ge', d: 'Greater than or equal to', g: 'math' },\r\n            { c: '\\u2248', e: 'asymp', d: 'Almost equal to', g: 'math' },\r\n            { c: '\\u221E', e: 'infin', d: 'Infinity', g: 'math' },\r\n            { c: '\\u221A', e: 'radic', d: 'Square root', g: 'math' },\r\n            { c: '\\u2211', e: 'sum', d: 'N-ary summation', g: 'math' },\r\n            { c: '\\u220F', e: 'prod', d: 'N-ary product', g: 'math' },\r\n            { c: '\\u222B', e: 'int', d: 'Integral', g: 'math' },\r\n            { c: '\\u2202', e: 'part', d: 'Partial differential', g: 'math' },\r\n            { c: '\\u2206', e: '', d: 'Increment \/ delta', g: 'math' },\r\n            { c: '\\u2208', e: 'isin', d: 'Element of', g: 'math' },\r\n            { c: '\\u2209', e: 'notin', d: 'Not an element of', g: 'math' },\r\n            { c: '\\u2229', e: 'cap', d: 'Intersection', g: 'math' },\r\n            { c: '\\u222A', e: 'cup', d: 'Union', g: 'math' },\r\n            { c: '\\u2200', e: 'forall', d: 'For all', g: 'math' },\r\n            { c: '\\u2203', e: 'exist', d: 'There exists', g: 'math' },\r\n            { c: '\\u2234', e: 'there4', d: 'Therefore', g: 'math' },\r\n            { c: '\\u00BC', e: 'frac14', d: 'One quarter', g: 'math' },\r\n            { c: '\\u00BD', e: 'frac12', d: 'One half', g: 'math' },\r\n            { c: '\\u00BE', e: 'frac34', d: 'Three quarters', g: 'math' },\r\n            { c: '\\u2030', e: 'permil', d: 'Per mille sign', g: 'math' },\r\n            { c: '\\u00AC', e: 'not', d: 'Not sign', g: 'math' },\r\n            { c: '\\u2295', e: 'oplus', d: 'Circled plus \/ XOR', g: 'math' },\r\n            { c: '\\u2297', e: 'otimes', d: 'Circled times', g: 'math' },\r\n\r\n            { c: '\\u03B1', e: 'alpha', d: 'Greek small alpha', g: 'greek' },\r\n            { c: '\\u03B2', e: 'beta', d: 'Greek small beta', g: 'greek' },\r\n            { c: '\\u03B3', e: 'gamma', d: 'Greek small gamma', g: 'greek' },\r\n            { c: '\\u03B4', e: 'delta', d: 'Greek small delta', g: 'greek' },\r\n            { c: '\\u03B5', e: 'epsilon', d: 'Greek small epsilon', g: 'greek' },\r\n            { c: '\\u03B8', e: 'theta', d: 'Greek small theta', g: 'greek' },\r\n            { c: '\\u03BB', e: 'lambda', d: 'Greek small lambda', g: 'greek' },\r\n            { c: '\\u03BC', e: 'mu', d: 'Greek small mu', g: 'greek' },\r\n            { c: '\\u03C0', e: 'pi', d: 'Greek small pi', g: 'greek' },\r\n            { c: '\\u03C3', e: 'sigma', d: 'Greek small sigma', g: 'greek' },\r\n            { c: '\\u03C4', e: 'tau', d: 'Greek small tau', g: 'greek' },\r\n            { c: '\\u03C6', e: 'phi', d: 'Greek small phi', g: 'greek' },\r\n            { c: '\\u03C9', e: 'omega', d: 'Greek small omega', g: 'greek' },\r\n            { c: '\\u0393', e: 'Gamma', d: 'Greek capital gamma', g: 'greek' },\r\n            { c: '\\u0394', e: 'Delta', d: 'Greek capital delta', g: 'greek' },\r\n            { c: '\\u03A3', e: 'Sigma', d: 'Greek capital sigma', g: 'greek' },\r\n            { c: '\\u03A6', e: 'Phi', d: 'Greek capital phi', g: 'greek' },\r\n            { c: '\\u03A9', e: 'Omega', d: 'Greek capital omega', g: 'greek' },\r\n\r\n            { c: '\\u00E9', e: 'eacute', d: 'Small e with acute', g: 'accent' },\r\n            { c: '\\u00E8', e: 'egrave', d: 'Small e with grave', g: 'accent' },\r\n            { c: '\\u00EA', e: 'ecirc', d: 'Small e with circumflex', g: 'accent' },\r\n            { c: '\\u00E1', e: 'aacute', d: 'Small a with acute', g: 'accent' },\r\n            { c: '\\u00E4', e: 'auml', d: 'Small a with diaeresis', g: 'accent' },\r\n            { c: '\\u00E5', e: 'aring', d: 'Small a with ring above', g: 'accent' },\r\n            { c: '\\u00E7', e: 'ccedil', d: 'Small c with cedilla', g: 'accent' },\r\n            { c: '\\u00F1', e: 'ntilde', d: 'Small n with tilde', g: 'accent' },\r\n            { c: '\\u00F6', e: 'ouml', d: 'Small o with diaeresis', g: 'accent' },\r\n            { c: '\\u00F8', e: 'oslash', d: 'Small o with stroke', g: 'accent' },\r\n            { c: '\\u00FC', e: 'uuml', d: 'Small u with diaeresis', g: 'accent' },\r\n            { c: '\\u00DF', e: 'szlig', d: 'Small sharp s (eszett)', g: 'accent' },\r\n            { c: '\\u00E6', e: 'aelig', d: 'Small ligature ae', g: 'accent' },\r\n            { c: '\\u0153', e: 'oelig', d: 'Small ligature oe', g: 'accent' },\r\n            { c: '\\u00FD', e: 'yacute', d: 'Small y with acute', g: 'accent' },\r\n            { c: '\\u010D', e: '', d: 'Small c with caron', g: 'accent' },\r\n            { c: '\\u0161', e: '', d: 'Small s with caron', g: 'accent' },\r\n            { c: '\\u017E', e: '', d: 'Small z with caron', g: 'accent' }\r\n        ],\r\n\r\n        \/* ---------- Bootstrapping ---------- *\/\r\n        init: function() {\r\n            this.attachDelegates();\r\n            this.renderCheatChips();\r\n            this.renderCheat();\r\n            this.processConvert();\r\n            this.runInspect();\r\n            this.runClean(false);\r\n        },\r\n\r\n        \/* Single delegated listener set on the wrapper. Survives DOM re-renders by\r\n           WordPress optimisation plugins, which silently discard per-element listeners. *\/\r\n        attachDelegates: function() {\r\n            var wrap = document.getElementById('pth-entity-studio-wrapper');\r\n            if (!wrap || wrap.getAttribute('data-pth-bound') === '1') { return; }\r\n            wrap.setAttribute('data-pth-bound', '1');\r\n            var self = this;\r\n            wrap.addEventListener('click', function(e) { self.onClick(e); });\r\n            wrap.addEventListener('input', function(e) { self.onInput(e); });\r\n            wrap.addEventListener('change', function(e) { self.onChange(e); });\r\n        },\r\n\r\n        hasClass: function(el, name) {\r\n            if (!el || !el.className || !el.className.split) { return false; }\r\n            var parts = el.className.split(\/\\s+\/);\r\n            var i;\r\n            for (i = 0; i < parts.length; i++) {\r\n                if (parts[i] === name) { return true; }\r\n            }\r\n            return false;\r\n        },\r\n\r\n        onClick: function(e) {\r\n            var wrap = document.getElementById('pth-entity-studio-wrapper');\r\n            var el = e.target;\r\n            var out;\r\n            while (el && el !== wrap) {\r\n                var id = el.id;\r\n                if (id === 'pth-sample-btn') { this.loadSample(); return; }\r\n                if (id === 'pth-fw-btn') { this.toggleWidth(); return; }\r\n                if (id === 'pth-clear-btn') { this.clearAll(); return; }\r\n                if (id === 'pth-mode-encode') { this.switchMode('encode'); return; }\r\n                if (id === 'pth-mode-decode') { this.switchMode('decode'); return; }\r\n                if (id === 'pth-copy-btn') {\r\n                    out = document.getElementById('pth-output');\r\n                    this.copyText(out ? out.value : '', 'Result'); return;\r\n                }\r\n                if (id === 'pth-clean-btn') { this.runClean(true); return; }\r\n                if (id === 'pth-clean-copy-btn') {\r\n                    out = document.getElementById('pth-clean-output');\r\n                    this.copyText(out ? out.value : '', 'Cleaned text'); return;\r\n                }\r\n                if (this.hasClass(el, 'pth-tab')) { this.switchTab(el.getAttribute('data-pth-panel')); return; }\r\n                if (this.hasClass(el, 'pth-chip')) { this.selectChip(el); return; }\r\n                if (this.hasClass(el, 'pth-copycell')) { this.copyText(el.textContent || el.innerText, 'Entity'); return; }\r\n                el = el.parentNode;\r\n            }\r\n        },\r\n\r\n        onInput: function(e) {\r\n            var id = e.target ? e.target.id : '';\r\n            if (id === 'pth-input') { this.processConvert(); }\r\n            else if (id === 'pth-inspect-input') { this.runInspect(); }\r\n            else if (id === 'pth-clean-input') { this.runClean(false); }\r\n            else if (id === 'pth-cheat-search') { this.renderCheat(); }\r\n        },\r\n\r\n        onChange: function(e) {\r\n            var id = e.target ? e.target.id : '';\r\n            if (id === 'pth-format-sel') { this.processConvert(); }\r\n        },\r\n\r\n        switchTab: function(name) {\r\n            var tabs = document.querySelectorAll('#pth-entity-studio-wrapper .pth-tab');\r\n            var panels = document.querySelectorAll('#pth-entity-studio-wrapper .pth-panel');\r\n            var sides = ['convert', 'inspect', 'clean', 'cheat'];\r\n            var i, el;\r\n\r\n            for (i = 0; i < tabs.length; i++) {\r\n                tabs[i].classList.remove('active');\r\n                if (tabs[i].getAttribute('data-pth-panel') === name) { tabs[i].classList.add('active'); }\r\n            }\r\n            for (i = 0; i < panels.length; i++) { panels[i].classList.remove('active'); }\r\n            el = document.getElementById('pth-panel-' + name);\r\n            if (el) { el.classList.add('active'); }\r\n\r\n            for (i = 0; i < sides.length; i++) {\r\n                el = document.getElementById('pth-side-' + sides[i]);\r\n                if (el) { el.style.display = (sides[i] === name) ? 'block' : 'none'; }\r\n            }\r\n            this.currentTab = name;\r\n        },\r\n\r\n        toggleWidth: function() {\r\n            var grid = document.getElementById('pth-grid-entity');\r\n            var side = document.getElementById('pth-side-entity');\r\n            var btn = document.getElementById('pth-fw-btn');\r\n            if (!grid || !side || !btn) { return; }\r\n\r\n            if (grid.classList.contains('pth-full-width')) {\r\n                grid.classList.remove('pth-full-width');\r\n                side.style.display = 'block';\r\n                btn.innerHTML = '\\u26F6 Full Width';\r\n            } else {\r\n                grid.classList.add('pth-full-width');\r\n                side.style.display = 'none';\r\n                btn.innerHTML = '\\u25E8 Split View';\r\n            }\r\n        },\r\n\r\n        toast: function(msg, isError) {\r\n            var t = document.getElementById('pth-toast-entity');\r\n            if (!t) { return; }\r\n            t.innerHTML = msg;\r\n            t.className = isError ? 'pth-toast err' : 'pth-toast';\r\n            t.style.display = 'block';\r\n            if (this.toastTimer) { clearTimeout(this.toastTimer); }\r\n            this.toastTimer = setTimeout(function() { t.style.display = 'none'; }, 3000);\r\n        },\r\n\r\n        copyText: function(value, label) {\r\n            var self = this;\r\n            if (!value) { this.toast('Nothing to copy yet', true); return; }\r\n            if (navigator.clipboard && navigator.clipboard.writeText) {\r\n                navigator.clipboard.writeText(value).then(function() {\r\n                    self.toast(label + ' copied to clipboard');\r\n                })['catch'](function() { self.fallbackCopy(value, label); });\r\n            } else {\r\n                this.fallbackCopy(value, label);\r\n            }\r\n        },\r\n\r\n        fallbackCopy: function(value, label) {\r\n            var ta = document.createElement('textarea');\r\n            ta.value = value;\r\n            ta.setAttribute('readonly', 'readonly');\r\n            ta.style.position = 'fixed';\r\n            ta.style.left = '-9999px';\r\n            document.body.appendChild(ta);\r\n            ta.select();\r\n            try {\r\n                document.execCommand('copy');\r\n                this.toast(label + ' copied to clipboard');\r\n            } catch (err) {\r\n                this.toast('Copy failed - select and copy manually', true);\r\n            }\r\n            document.body.removeChild(ta);\r\n        },\r\n\r\n        escapeForDisplay: function(str) {\r\n            return String(str).replace(\/&\/g, '&').replace(new RegExp(LT, 'g'), '<').replace(new RegExp(GT, 'g'), '>');\r\n        },\r\n\r\n        pad4: function(hex) {\r\n            while (hex.length < 4) { hex = '0' + hex; }\r\n            return hex;\r\n        },\r\n\r\n        \/* ---------- Tab 1: Convert ---------- *\/\r\n        codePoints: function(str) {\r\n            var arr = [];\r\n            var i = 0;\r\n            var cp;\r\n            while (i < str.length) {\r\n                cp = str.codePointAt(i);\r\n                arr.push(cp);\r\n                i += (cp > 0xFFFF) ? 2 : 1;\r\n            }\r\n            return arr;\r\n        },\r\n\r\n        encodeHtmlBasic: function(str) {\r\n            return str.replace(\/&\/g, '&')\r\n                .replace(new RegExp(LT, 'g'), '<')\r\n                .replace(new RegExp(GT, 'g'), '>')\r\n                .replace(\/\"\/g, '\"')\r\n                .replace(\/'\/g, ''')\r\n                .replace(\/\\\/\/g, '\/')\r\n                .replace(\/`\/g, '`');\r\n        },\r\n\r\n        encodeHtmlAll: function(str) {\r\n            var self = this;\r\n            var cps = this.codePoints(str);\r\n            var out = '';\r\n            var i, cp, ch;\r\n            for (i = 0; i < cps.length; i++) {\r\n                cp = cps[i];\r\n                ch = String.fromCodePoint(cp);\r\n                if (cp > 127) {\r\n                    out += '&#' + cp + ';';\r\n                } else {\r\n                    out += self.encodeHtmlBasic(ch);\r\n                }\r\n            }\r\n            return out;\r\n        },\r\n\r\n        encodeHtmlNamed: function(str) {\r\n            var self = this;\r\n            var cps = this.codePoints(str);\r\n            var out = '';\r\n            var i, ch, cp;\r\n            for (i = 0; i < cps.length; i++) {\r\n                cp = cps[i];\r\n                ch = String.fromCodePoint(cp);\r\n                if (ch === LT) { out += '<'; }\r\n                else if (ch === GT) { out += '>'; }\r\n                else if (self.namedMap[ch]) { out += '&' + self.namedMap[ch] + ';'; }\r\n                else if (cp > 127) { out += '&#' + cp + ';'; }\r\n                else { out += ch; }\r\n            }\r\n            return out;\r\n        },\r\n\r\n        encodeXml: function(str) {\r\n            return str.replace(\/&\/g, '&')\r\n                .replace(new RegExp(LT, 'g'), '<')\r\n                .replace(new RegExp(GT, 'g'), '>')\r\n                .replace(\/\"\/g, '\"')\r\n                .replace(\/'\/g, '&apos;');\r\n        },\r\n\r\n        decodeHtml: function(str) {\r\n            var out = str.replace(\/&#x([0-9a-fA-F]+);\/g, function(m, hex) {\r\n                return String.fromCodePoint(parseInt(hex, 16));\r\n            }).replace(\/&#(\\d+);\/g, function(m, dec) {\r\n                return String.fromCodePoint(parseInt(dec, 10));\r\n            });\r\n\r\n            var self = this;\r\n            var reverse = { lt: LT, gt: GT, amp: '&', quot: '\"', apos: \"'\" };\r\n            var key;\r\n            for (key in self.namedMap) {\r\n                if (self.namedMap.hasOwnProperty(key)) { reverse[self.namedMap[key]] = key; }\r\n            }\r\n            out = out.replace(\/&([a-zA-Z][a-zA-Z0-9]*);\/g, function(m, name) {\r\n                return reverse.hasOwnProperty(name) ? reverse[name] : m;\r\n            });\r\n            return out;\r\n        },\r\n\r\n        encodeCss: function(str) {\r\n            var self = this;\r\n            var cps = this.codePoints(str);\r\n            var out = '';\r\n            var i, cp;\r\n            for (i = 0; i < cps.length; i++) {\r\n                cp = cps[i];\r\n                if ((cp >= 48 && cp <= 57) || (cp >= 65 && cp <= 90) || (cp >= 97 && cp <= 122) || cp === 32) {\r\n                    out += String.fromCodePoint(cp);\r\n                } else {\r\n                    out += '\\\\' + self.pad4(cp.toString(16).toUpperCase()) + ' ';\r\n                }\r\n            }\r\n            return out.replace(\/\\s+$\/, '');\r\n        },\r\n\r\n        decodeCss: function(str) {\r\n            return str.replace(\/\\\\([0-9a-fA-F]{1,6})\\s?\/g, function(m, hex) {\r\n                return String.fromCodePoint(parseInt(hex, 16));\r\n            });\r\n        },\r\n\r\n        encodeJs: function(str) {\r\n            var self = this;\r\n            var out = '';\r\n            var i, code;\r\n            for (i = 0; i < str.length; i++) {\r\n                code = str.charCodeAt(i);\r\n                if ((code >= 48 && code <= 57) || (code >= 65 && code <= 90) || (code >= 97 && code <= 122) || code === 32) {\r\n                    out += str.charAt(i);\r\n                } else {\r\n                    out += '\\\\u' + self.pad4(code.toString(16).toUpperCase());\r\n                }\r\n            }\r\n            return out;\r\n        },\r\n\r\n        decodeJs: function(str) {\r\n            return str.replace(\/\\\\u\\{([0-9a-fA-F]+)\\}\/g, function(m, hex) {\r\n                return String.fromCodePoint(parseInt(hex, 16));\r\n            }).replace(\/\\\\u([0-9a-fA-F]{4})\/g, function(m, hex) {\r\n                return String.fromCharCode(parseInt(hex, 16));\r\n            });\r\n        },\r\n\r\n        encodeEs6: function(str) {\r\n            var self = this;\r\n            var cps = this.codePoints(str);\r\n            var out = '';\r\n            var i, cp;\r\n            for (i = 0; i < cps.length; i++) {\r\n                cp = cps[i];\r\n                if ((cp >= 48 && cp <= 57) || (cp >= 65 && cp <= 90) || (cp >= 97 && cp <= 122) || cp === 32) {\r\n                    out += String.fromCodePoint(cp);\r\n                } else {\r\n                    out += '\\\\u{' + cp.toString(16).toUpperCase() + '}';\r\n                }\r\n            }\r\n            return out;\r\n        },\r\n\r\n        encodeJson: function(str) {\r\n            var out = '';\r\n            var i, ch, code;\r\n            for (i = 0; i < str.length; i++) {\r\n                ch = str.charAt(i);\r\n                code = str.charCodeAt(i);\r\n                if (ch === '\\\\') { out += '\\\\\\\\'; }\r\n                else if (ch === '\"') { out += '\\\\\"'; }\r\n                else if (code === 8) { out += '\\\\b'; }\r\n                else if (code === 9) { out += '\\\\t'; }\r\n                else if (code === 10) { out += '\\\\n'; }\r\n                else if (code === 12) { out += '\\\\f'; }\r\n                else if (code === 13) { out += '\\\\r'; }\r\n                else if (code < 32 || code === 127) { out += '\\\\u' + this.pad4(code.toString(16).toUpperCase()); }\r\n                else { out += ch; }\r\n            }\r\n            return out;\r\n        },\r\n\r\n        decodeJson: function(str) {\r\n            return str.replace(\/\\\\u([0-9a-fA-F]{4})\/g, function(m, hex) {\r\n                return String.fromCharCode(parseInt(hex, 16));\r\n            }).replace(\/\\\\n\/g, '\\n')\r\n                .replace(\/\\\\r\/g, '\\r')\r\n                .replace(\/\\\\t\/g, '\\t')\r\n                .replace(\/\\\\b\/g, String.fromCharCode(8))\r\n                .replace(\/\\\\f\/g, String.fromCharCode(12))\r\n                .replace(\/\\\\\"\/g, '\"')\r\n                .replace(\/\\\\\\\\\/g, '\\\\');\r\n        },\r\n\r\n        encodeCodepoint: function(str) {\r\n            var self = this;\r\n            var cps = this.codePoints(str);\r\n            var parts = [];\r\n            var i;\r\n            for (i = 0; i < cps.length; i++) {\r\n                parts.push('U+' + self.pad4(cps[i].toString(16).toUpperCase()));\r\n            }\r\n            return parts.join(' ');\r\n        },\r\n\r\n        decodeCodepoint: function(str) {\r\n            return str.replace(\/U\\+([0-9a-fA-F]{2,6})\\s?\/g, function(m, hex) {\r\n                return String.fromCodePoint(parseInt(hex, 16));\r\n            });\r\n        },\r\n\r\n        processConvert: function() {\r\n            var inEl = document.getElementById('pth-input');\r\n            var outEl = document.getElementById('pth-output');\r\n            var fmtEl = document.getElementById('pth-format-sel');\r\n            var inStat = document.getElementById('pth-in-stat');\r\n            var outStat = document.getElementById('pth-out-stat');\r\n            if (!inEl || !outEl || !fmtEl) { return; }\r\n\r\n            var text = inEl.value;\r\n            var fmt = fmtEl.value;\r\n            var result = '';\r\n\r\n            if (inStat) { inStat.innerHTML = this.codePoints(text).length + ' chars'; }\r\n\r\n            if (!text) {\r\n                outEl.value = '';\r\n                if (outStat) { outStat.innerHTML = '0 chars'; }\r\n                return;\r\n            }\r\n\r\n            try {\r\n                if (this.currentMode === 'encode') {\r\n                    if (fmt === 'html') { result = this.encodeHtmlBasic(text); }\r\n                    else if (fmt === 'html-all') { result = this.encodeHtmlAll(text); }\r\n                    else if (fmt === 'html-named') { result = this.encodeHtmlNamed(text); }\r\n                    else if (fmt === 'xml') { result = this.encodeXml(text); }\r\n                    else if (fmt === 'css') { result = this.encodeCss(text); }\r\n                    else if (fmt === 'js') { result = this.encodeJs(text); }\r\n                    else if (fmt === 'es6') { result = this.encodeEs6(text); }\r\n                    else if (fmt === 'json') { result = this.encodeJson(text); }\r\n                    else if (fmt === 'codepoint') { result = this.encodeCodepoint(text); }\r\n                } else {\r\n                    if (fmt === 'html' || fmt === 'html-all' || fmt === 'html-named' || fmt === 'xml') { result = this.decodeHtml(text); }\r\n                    else if (fmt === 'css') { result = this.decodeCss(text); }\r\n                    else if (fmt === 'js' || fmt === 'es6') { result = this.decodeJs(text); }\r\n                    else if (fmt === 'json') { result = this.decodeJson(text); }\r\n                    else if (fmt === 'codepoint') { result = this.decodeCodepoint(text); }\r\n                }\r\n            } catch (err) {\r\n                result = '';\r\n                this.toast('That input could not be converted', true);\r\n            }\r\n\r\n            outEl.value = result;\r\n            if (outStat) { outStat.innerHTML = result.length + ' chars'; }\r\n        },\r\n\r\n        switchMode: function(mode) {\r\n            var encBtn = document.getElementById('pth-mode-encode');\r\n            var decBtn = document.getElementById('pth-mode-decode');\r\n            var inLbl = document.getElementById('pth-in-label');\r\n            var outLbl = document.getElementById('pth-out-label');\r\n            this.currentMode = mode;\r\n\r\n            if (encBtn) { encBtn.classList.toggle('active', mode === 'encode'); }\r\n            if (decBtn) { decBtn.classList.toggle('active', mode === 'decode'); }\r\n            if (inLbl) { inLbl.innerHTML = (mode === 'encode') ? 'Text to Encode' : 'Text to Decode'; }\r\n            if (outLbl) { outLbl.innerHTML = (mode === 'encode') ? 'Encoded Result' : 'Decoded Result'; }\r\n            this.processConvert();\r\n        },\r\n\r\n        \/* ---------- Tab 2: Inspector ---------- *\/\r\n        utf8Bytes: function(cp) {\r\n            var bytes = [];\r\n            if (cp < 0x80) {\r\n                bytes.push(cp);\r\n            } else if (cp < 0x800) {\r\n                bytes.push(0xC0 | (cp >> 6));\r\n                bytes.push(0x80 | (cp & 0x3F));\r\n            } else if (cp < 0x10000) {\r\n                bytes.push(0xE0 | (cp >> 12));\r\n                bytes.push(0x80 | ((cp >> 6) & 0x3F));\r\n                bytes.push(0x80 | (cp & 0x3F));\r\n            } else {\r\n                bytes.push(0xF0 | (cp >> 18));\r\n                bytes.push(0x80 | ((cp >> 12) & 0x3F));\r\n                bytes.push(0x80 | ((cp >> 6) & 0x3F));\r\n                bytes.push(0x80 | (cp & 0x3F));\r\n            }\r\n            var hex = [];\r\n            var i;\r\n            for (i = 0; i < bytes.length; i++) {\r\n                hex.push((bytes[i] < 16 ? '0' : '') + bytes[i].toString(16).toUpperCase());\r\n            }\r\n            return hex.join(' ');\r\n        },\r\n\r\n        describeChar: function(cp) {\r\n            var i;\r\n            var ch = String.fromCodePoint(cp);\r\n            if (this.charNames[cp]) { return this.charNames[cp]; }\r\n            for (i = 0; i < this.invisibleList.length; i++) {\r\n                if (this.invisibleList[i].c === ch) { return this.invisibleList[i].n; }\r\n            }\r\n            if (cp >= 48 && cp <= 57) { return 'Digit ' + ch; }\r\n            if (cp >= 65 && cp <= 90) { return 'Latin capital letter ' + ch; }\r\n            if (cp >= 97 && cp <= 122) { return 'Latin small letter ' + ch; }\r\n            if (cp < 32) { return 'Control character'; }\r\n            if (cp >= 0x0370 && cp <= 0x03FF) { return 'Greek or Coptic'; }\r\n            if (cp >= 0x0400 && cp <= 0x04FF) { return 'Cyrillic'; }\r\n            if (cp >= 0x0D80 && cp <= 0x0DFF) { return 'Sinhala'; }\r\n            if (cp >= 0x2190 && cp <= 0x21FF) { return 'Arrow symbol'; }\r\n            if (cp >= 0x2200 && cp <= 0x22FF) { return 'Mathematical operator'; }\r\n            if (cp >= 0x4E00 && cp <= 0x9FFF) { return 'CJK unified ideograph'; }\r\n            if (cp >= 0x1F300 && cp <= 0x1FAFF) { return 'Emoji or pictograph'; }\r\n            if (cp > 0xFFFF) { return 'Supplementary plane character'; }\r\n            return 'Unicode character';\r\n        },\r\n\r\n        runInspect: function() {\r\n            var inEl = document.getElementById('pth-inspect-input');\r\n            var body = document.getElementById('pth-inspect-body');\r\n            var stat = document.getElementById('pth-inspect-stat');\r\n            if (!inEl || !body) { return; }\r\n\r\n            var cps = this.codePoints(inEl.value);\r\n            var rows = '';\r\n            var i, cp, ch, display, hex, limit;\r\n\r\n            if (stat) { stat.innerHTML = cps.length + ' characters'; }\r\n\r\n            if (!cps.length) {\r\n                body.innerHTML = '<tr><td colspan=\"8\" style=\"text-align:center; color:#94a3b8; padding:26px;\">Type something above to inspect it' + CT + 'td>' + CT + 'tr>';\r\n                return;\r\n            }\r\n\r\n            limit = cps.length > 400 ? 400 : cps.length;\r\n\r\n            for (i = 0; i < limit; i++) {\r\n                cp = cps[i];\r\n                ch = String.fromCodePoint(cp);\r\n                hex = this.pad4(cp.toString(16).toUpperCase());\r\n\r\n                if (cp === 32) { display = '<span style=\"color:#94a3b8;\">(space)' + CT + 'span>'; }\r\n                else if (cp === 10) { display = '<span style=\"color:#94a3b8;\">(LF)' + CT + 'span>'; }\r\n                else if (cp === 9) { display = '<span style=\"color:#94a3b8;\">(tab)' + CT + 'span>'; }\r\n                else if (cp < 32 || cp === 0x200B || cp === 0x200C || cp === 0x200D || cp === 0xFEFF || cp === 0x00AD) { display = '<span style=\"color:#dc2626; font-weight:800;\">(hidden)' + CT + 'span>'; }\r\n                else { display = this.escapeForDisplay(ch); }\r\n\r\n                rows += '<tr>' +\r\n                    '<td class=\"pth-charcell\">' + display + CT + 'td>' +\r\n                    '<td style=\"font-family:inherit;\">' + this.describeChar(cp) + CT + 'td>' +\r\n                    '<td>U+' + hex + CT + 'td>' +\r\n                    '<td>' + cp + CT + 'td>' +\r\n                    '<td>' + this.utf8Bytes(cp) + CT + 'td>' +\r\n                    '<td>&#' + cp + ';' + CT + 'td>' +\r\n                    '<td>\\\\' + hex + CT + 'td>' +\r\n                    '<td>' + (cp > 0xFFFF ? '\\\\u{' + cp.toString(16).toUpperCase() + '}' : '\\\\u' + hex) + CT + 'td>' +\r\n                    CT + 'tr>';\r\n            }\r\n\r\n            if (cps.length > limit) {\r\n                rows += '<tr><td colspan=\"8\" style=\"text-align:center; color:#92400e; background:#fffbeb; font-family:inherit;\">Showing the first 400 characters of ' + cps.length + CT + 'td>' + CT + 'tr>';\r\n            }\r\n\r\n            body.innerHTML = rows;\r\n        },\r\n\r\n        \/* ---------- Tab 3: Cleaner ---------- *\/\r\n        buildPreview: function(text) {\r\n            var self = this;\r\n            var out = '';\r\n            var i, ch, found, j;\r\n            var limit = text.length > 3000 ? 3000 : text.length;\r\n\r\n            for (i = 0; i < limit; i++) {\r\n                ch = text.charAt(i);\r\n                found = null;\r\n                for (j = 0; j < self.invisibleList.length; j++) {\r\n                    if (self.invisibleList[j].c === ch) { found = self.invisibleList[j]; break; }\r\n                }\r\n                if (found) {\r\n                    if (found.k === 'quotes' || found.k === 'dashes') {\r\n                        out += '<span class=\"pth-mark q\" title=\"' + found.n + '\">' + self.escapeForDisplay(ch) + CT + 'span>';\r\n                    } else {\r\n                        out += '<span class=\"pth-mark\" title=\"' + found.n + '\">' + found.n + CT + 'span>';\r\n                    }\r\n                } else {\r\n                    out += self.escapeForDisplay(ch);\r\n                }\r\n            }\r\n            if (text.length > limit) { out += '\\n<span style=\"color:#92400e;\">... preview truncated' + CT + 'span>'; }\r\n            return out;\r\n        },\r\n\r\n        runClean: function(applyFixes) {\r\n            var inEl = document.getElementById('pth-clean-input');\r\n            var findEl = document.getElementById('pth-findings');\r\n            var prevEl = document.getElementById('pth-clean-preview');\r\n            var outEl = document.getElementById('pth-clean-output');\r\n            var stat = document.getElementById('pth-clean-stat');\r\n            if (!inEl || !findEl || !prevEl) { return; }\r\n\r\n            var text = inEl.value;\r\n            var counts = { invisible: 0, nbsp: 0, quotes: 0, dashes: 0, bidi: 0 };\r\n            var detail = {};\r\n            var i, j, ch, item, total, html, key, trailing;\r\n\r\n            for (i = 0; i < text.length; i++) {\r\n                ch = text.charAt(i);\r\n                for (j = 0; j < this.invisibleList.length; j++) {\r\n                    item = this.invisibleList[j];\r\n                    if (item.c === ch) {\r\n                        counts[item.k] = counts[item.k] + 1;\r\n                        detail[item.n] = (detail[item.n] || 0) + 1;\r\n                        break;\r\n                    }\r\n                }\r\n            }\r\n\r\n            trailing = (text.match(\/[ \\t]+$\/gm) || []).length;\r\n            total = counts.invisible + counts.nbsp + counts.quotes + counts.dashes + counts.bidi;\r\n            if (stat) { stat.innerHTML = total + (total === 1 ? ' issue' : ' issues'); }\r\n\r\n            if (!text) {\r\n                findEl.innerHTML = '<div class=\"pth-finding info\"><span>Paste some text above and the scan runs automatically.' + CT + 'span>' + CT + 'div>';\r\n                prevEl.innerHTML = '<span style=\"color:#94a3b8;\">Preview appears here' + CT + 'span>';\r\n                if (outEl && applyFixes) { outEl.value = ''; }\r\n                return;\r\n            }\r\n\r\n            html = '';\r\n            if (total === 0 && trailing === 0) {\r\n                html = '<div class=\"pth-finding ok\"><span>\\u2705 Clean - no hidden or substituted characters found.' + CT + 'span>' + CT + 'div>';\r\n            } else {\r\n                if (counts.invisible) { html += '<div class=\"pth-finding warn\"><span>\\uD83D\\uDC7B Invisible characters (zero-width, BOM, soft hyphen)' + CT + 'span><span class=\"pth-count\">' + counts.invisible + CT + 'span>' + CT + 'div>'; }\r\n                if (counts.bidi) { html += '<div class=\"pth-finding warn\"><span>\\uD83D\\uDD00 Bidirectional override marks - possible code spoofing' + CT + 'span><span class=\"pth-count\">' + counts.bidi + CT + 'span>' + CT + 'div>'; }\r\n                if (counts.nbsp) { html += '<div class=\"pth-finding warn\"><span>\\u2423 Non-breaking or unusual spaces' + CT + 'span><span class=\"pth-count\">' + counts.nbsp + CT + 'span>' + CT + 'div>'; }\r\n                if (counts.quotes) { html += '<div class=\"pth-finding info\"><span>\\u275D Curly quotes and apostrophes' + CT + 'span><span class=\"pth-count\">' + counts.quotes + CT + 'span>' + CT + 'div>'; }\r\n                if (counts.dashes) { html += '<div class=\"pth-finding info\"><span>\\u2014 En\/em dashes and ellipsis' + CT + 'span><span class=\"pth-count\">' + counts.dashes + CT + 'span>' + CT + 'div>'; }\r\n                if (trailing) { html += '<div class=\"pth-finding info\"><span>\\u23CE Lines ending in spaces or tabs' + CT + 'span><span class=\"pth-count\">' + trailing + CT + 'span>' + CT + 'div>'; }\r\n\r\n                html += '<div style=\"font-size:12.5px; color:#64748b; margin-top:9px;\">Breakdown: ';\r\n                for (key in detail) {\r\n                    if (detail.hasOwnProperty(key)) { html += key + ' (' + detail[key] + ') \u00a0 '; }\r\n                }\r\n                html += CT + 'div>';\r\n            }\r\n            findEl.innerHTML = html;\r\n            prevEl.innerHTML = this.buildPreview(text);\r\n\r\n            if (applyFixes) { this.applyFixes(text); }\r\n        },\r\n\r\n        applyFixes: function(text) {\r\n            var outEl = document.getElementById('pth-clean-output');\r\n            if (!outEl) { return; }\r\n\r\n            var opt = function(id) {\r\n                var el = document.getElementById(id);\r\n                return el ? el.checked : false;\r\n            };\r\n            var result = text;\r\n\r\n            if (opt('pth-fix-invisible')) {\r\n                result = result.replace(\/[\\u200B\\u200C\\u200D\\uFEFF\\u2060\\u00AD\\u180E]\/g, '');\r\n            }\r\n            if (opt('pth-fix-bidi')) {\r\n                result = result.replace(\/[\\u202A-\\u202E\\u2066-\\u2069]\/g, '');\r\n            }\r\n            if (opt('pth-fix-nbsp')) {\r\n                result = result.replace(\/[\\u00A0\\u2007\\u202F\\u2000-\\u200A\\u3000]\/g, ' ');\r\n            }\r\n            if (opt('pth-fix-quotes')) {\r\n                result = result.replace(\/[\\u2018\\u2019\\u201A\\u201B\\u2032]\/g, \"'\")\r\n                    .replace(\/[\\u201C\\u201D\\u201E\\u201F\\u2033]\/g, '\"');\r\n            }\r\n            if (opt('pth-fix-dashes')) {\r\n                result = result.replace(\/\\u2013\/g, '-').replace(\/\\u2014\/g, '--').replace(\/\\u2026\/g, '...');\r\n            }\r\n            if (opt('pth-fix-trailing')) {\r\n                result = result.replace(\/[ \\t]+$\/gm, '');\r\n            }\r\n\r\n            outEl.value = result;\r\n            this.toast('Cleaned - ' + (text.length - result.length) + ' characters changed or removed');\r\n        },\r\n\r\n        \/* ---------- Tab 4: Cheat Sheet ---------- *\/\r\n        renderCheatChips: function() {\r\n            var wrap = document.getElementById('pth-cheat-chips');\r\n            if (!wrap) { return; }\r\n            var groups = [\r\n                { k: 'all', l: 'All' }, { k: 'punct', l: 'Punctuation' },\r\n                { k: 'currency', l: 'Currency' }, { k: 'symbol', l: 'Symbols' },\r\n                { k: 'arrow', l: 'Arrows' }, { k: 'math', l: 'Math' },\r\n                { k: 'greek', l: 'Greek' }, { k: 'accent', l: 'Accented' }\r\n            ];\r\n            var html = '';\r\n            var i;\r\n            for (i = 0; i < groups.length; i++) {\r\n                html += '<button type=\"button\" class=\"pth-chip' + (groups[i].k === 'all' ? ' active' : '') + '\" data-pth-group=\"' + groups[i].k + '\">' + groups[i].l + CT + 'button>';\r\n            }\r\n            wrap.innerHTML = html;\r\n        },\r\n\r\n        selectChip: function(chip) {\r\n            var wrap = document.getElementById('pth-cheat-chips');\r\n            var i;\r\n            if (wrap) {\r\n                var all = wrap.querySelectorAll('.pth-chip');\r\n                for (i = 0; i < all.length; i++) { all[i].classList.remove('active'); }\r\n            }\r\n            chip.classList.add('active');\r\n            this.cheatFilter = chip.getAttribute('data-pth-group');\r\n            this.renderCheat();\r\n        },\r\n\r\n        renderCheat: function() {\r\n            var body = document.getElementById('pth-cheat-body');\r\n            var searchEl = document.getElementById('pth-cheat-search');\r\n            var countEl = document.getElementById('pth-cheat-count');\r\n            if (!body) { return; }\r\n\r\n            var term = searchEl ? searchEl.value.toLowerCase().replace(\/^&|;$\/g, '') : '';\r\n            var rows = '';\r\n            var shown = 0;\r\n            var i, item, cp, hay;\r\n\r\n            for (i = 0; i < this.cheatData.length; i++) {\r\n                item = this.cheatData[i];\r\n                cp = item.c.codePointAt(0);\r\n                if (this.cheatFilter !== 'all' && item.g !== this.cheatFilter) { continue; }\r\n                hay = (item.e + ' ' + item.d + ' ' + cp + ' ' + cp.toString(16)).toLowerCase();\r\n                if (term && hay.indexOf(term) === -1) { continue; }\r\n                shown++;\r\n                rows += '<tr>' +\r\n                    '<td class=\"pth-charcell\">' + this.escapeForDisplay(item.c) + CT + 'td>' +\r\n                    '<td>' + (item.e ? '<span class=\"pth-copycell\" data-pth-copy=\"&' + item.e + ';\">&' + item.e + ';' + CT + 'span>' : '<span style=\"color:#cbd5e1;\">none' + CT + 'span>') + CT + 'td>' +\r\n                    '<td><span class=\"pth-copycell\" data-pth-copy=\"&#' + cp + ';\">&#' + cp + ';' + CT + 'span>' + CT + 'td>' +\r\n                    '<td><span class=\"pth-copycell\" data-pth-copy=\"&#x' + cp.toString(16).toUpperCase() + ';\">&#x' + cp.toString(16).toUpperCase() + ';' + CT + 'span>' + CT + 'td>' +\r\n                    '<td style=\"font-family:inherit;\">' + item.d + CT + 'td>' +\r\n                    CT + 'tr>';\r\n            }\r\n\r\n            if (!shown) {\r\n                rows = '<tr><td colspan=\"5\" style=\"text-align:center; color:#94a3b8; padding:26px;\">No entity matches that search' + CT + 'td>' + CT + 'tr>';\r\n            }\r\n            body.innerHTML = rows;\r\n            if (countEl) { countEl.innerHTML = shown + ' of ' + this.cheatData.length + ' shown.'; }\r\n        },\r\n\r\n        \/* ---------- Shared actions ---------- *\/\r\n        loadSample: function() {\r\n            var tab = this.currentTab;\r\n            var el;\r\n\r\n            if (tab === 'convert') {\r\n                el = document.getElementById('pth-input');\r\n                if (el) {\r\n                    el.value = LT + 'div class=\"card\"' + GT + '\\n  ' + LT + 'p' + GT + 'Price: 12 \\u20AC & rising \\u2014 see O\\u2019Brien\\u2019s note' + CT + 'p' + GT + '\\n  ' + LT + 'p' + GT + 'Math: 5 ' + LT + ' 10 && 10 ' + GT + ' 5 \\u2192 \\u03C0 \\u2248 3.14' + CT + 'p' + GT + '\\n' + CT + 'div' + GT;\r\n                    this.processConvert();\r\n                }\r\n            } else if (tab === 'inspect') {\r\n                el = document.getElementById('pth-inspect-input');\r\n                if (el) {\r\n                    el.value = 'A\\u00E9\\u20AC\\u03C0\\u4E2D\\uD83D\\uDE00';\r\n                    this.runInspect();\r\n                }\r\n            } else if (tab === 'clean') {\r\n                el = document.getElementById('pth-clean-input');\r\n                if (el) {\r\n                    el.value = 'const price\\u00A0= \\u201CHello\\u201D;\\u200B\\n\/\/ O\\u2019Brien said \\u2014 don\\u2019t trust pasted text\\u2026   \\nlet\\u00A0total = price \\u2013 tax;\\uFEFF';\r\n                    this.runClean(false);\r\n                }\r\n            } else {\r\n                el = document.getElementById('pth-cheat-search');\r\n                if (el) {\r\n                    el.value = 'arrow';\r\n                    this.renderCheat();\r\n                }\r\n            }\r\n            this.toast('Sample loaded');\r\n        },\r\n\r\n        clearAll: function() {\r\n            var ids = ['pth-input', 'pth-output', 'pth-inspect-input', 'pth-clean-input', 'pth-clean-output', 'pth-cheat-search'];\r\n            var i, el;\r\n            for (i = 0; i < ids.length; i++) {\r\n                el = document.getElementById(ids[i]);\r\n                if (el) { el.value = ''; }\r\n            }\r\n            this.processConvert();\r\n            this.runInspect();\r\n            this.runClean(false);\r\n            this.renderCheat();\r\n            this.toast('All fields cleared');\r\n        }\r\n    };\r\n\r\n    \/* ---------- 4-layer bootstrap ---------- *\/\r\n    function pthEntityRunInit() {\r\n        var app = window.pthEntityStudioApp;\r\n        if (!app || app.initialized) { return; }\r\n        if (!document.getElementById('pth-entity-studio-wrapper')) { return; }\r\n        app.initialized = true;\r\n        try {\r\n            app.init();\r\n        } catch (err) {\r\n            app.initialized = false;\r\n            \/* Never swallow silently: a dead bootstrap must be debuggable. *\/\r\n            if (window.console && console.error) { console.error('pthEntityStudio init failed:', err); }\r\n        }\r\n    }\r\n\r\n    pthEntityRunInit();\r\n\r\n    if (document.readyState === 'loading') {\r\n        document.addEventListener('DOMContentLoaded', pthEntityRunInit);\r\n    }\r\n\r\n    window.addEventListener('load', function() {\r\n        setTimeout(pthEntityRunInit, 50);\r\n    });\r\n\r\n    if (typeof MutationObserver !== 'undefined') {\r\n        var pthEntityObserver = new MutationObserver(function() {\r\n            if (document.getElementById('pth-entity-studio-wrapper')) {\r\n                \/* Re-run: re-binds delegates on a replaced wrapper (guarded by data-pth-bound). *\/\r\n                var app = window.pthEntityStudioApp;\r\n                if (app) { app.attachDelegates(); }\r\n                pthEntityRunInit();\r\n            }\r\n        });\r\n        if (document.body) {\r\n            pthEntityObserver.observe(document.body, { childList: true, subtree: true });\r\n            setTimeout(function() { pthEntityObserver.disconnect(); }, 10000);\r\n        }\r\n    }\r\n})();\r\n<\/script>\n\n\n\n<style>\n.pth-feature-wrap { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 20px; margin-bottom: 30px; }\n.pth-feat-box { border: 1px solid #e2e8f0; background: #ffffff; padding: 24px; border-radius: 12px; box-shadow: 0 4px 6px -1px rgba(0,0,0,0.02); transition: all 0.3s ease; }\n.pth-feat-box:hover { border-color: #bfdbfe; transform: translateY(-3px); box-shadow: 0 10px 15px -3px rgba(0,0,0,0.05); }\n.pth-feat-title { font-size: 1.1rem; font-weight: 800; color: #0f172a; margin-bottom: 10px; display: flex; align-items: center; gap: 10px; }\n.pth-feat-text { font-size: 0.9rem; color: #475569; line-height: 1.6; margin: 0; }\n.pth-steps-wrap { border: 1px solid #e2e8f0; background: #ffffff; padding: 40px 30px; border-radius: 16px; text-align: center; box-shadow: 0 4px 6px -1px rgba(0,0,0,0.02); margin-bottom: 40px; }\n.pth-steps-title { font-size: 1.2rem; font-weight: 800; color: #1e293b; text-transform: uppercase; margin-bottom: 35px; letter-spacing: 1px; }\n.pth-steps-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 30px; }\n.pth-step-item { display: flex; flex-direction: column; align-items: center; }\n.pth-step-num { width: 45px; height: 45px; background: #3b82f6; color: #ffffff; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: 800; font-size: 1.2rem; margin-bottom: 15px; box-shadow: 0 4px 10px rgba(59,130,246,0.3); }\n.pth-step-name { font-weight: 800; color: #0f172a; margin-bottom: 8px; font-size: 1.05rem; }\n.pth-step-desc { font-size: 0.9rem; color: #64748b; line-height: 1.5; margin: 0; }\n<\/style>\n<div class=\"pth-feature-wrap\">\n    <div class=\"pth-feat-box\">\n        <div class=\"pth-feat-title\">\ud83d\udfe2 Real-Time Character Parsing<\/div>\n        <p class=\"pth-feat-text\">Instantly scans your text strings to identify and convert reserved syntax into safe, universally compatible structural code.<\/p>\n    <\/div>\n    <div class=\"pth-feat-box\">\n        <div class=\"pth-feat-title\">\ud83d\udd35 Bi-Directional Translation<\/div>\n        <p class=\"pth-feat-text\">Seamlessly toggles between securing raw inputs and reversing previously sanitized data back into human-readable formats.<\/p>\n    <\/div>\n    <div class=\"pth-feat-box\">\n        <div class=\"pth-feat-title\">\ud83d\udfe3 XSS Vulnerability Mitigation<\/div>\n        <p class=\"pth-feat-text\">Neutralizes malicious script injections by mathematically escaping sensitive symbols before they render in the DOM.<\/p>\n    <\/div>\n<\/div>\n<div class=\"pth-steps-wrap\">\n    <div class=\"pth-steps-title\">How to Use the HTML Entity Encoder<\/div>\n    <div class=\"pth-steps-grid\">\n        <div class=\"pth-step-item\">\n            <div class=\"pth-step-num\">1<\/div>\n            <div class=\"pth-step-name\">Input Raw Data<\/div>\n            <p class=\"pth-step-desc\">Paste your raw text, source code snippets, or unformatted strings directly into the primary HTML Entity Encoder input field.<\/p>\n        <\/div>\n        <div class=\"pth-step-item\">\n            <div class=\"pth-step-num\">2<\/div>\n            <div class=\"pth-step-name\">Select Operation<\/div>\n            <p class=\"pth-step-desc\">Choose your algorithmic action. Select &#8216;Encode&#8217; to secure reserved symbols or &#8216;Decode&#8217; to revert an already sanitized string.<\/p>\n        <\/div>\n        <div class=\"pth-step-item\">\n            <div class=\"pth-step-num\">3<\/div>\n            <div class=\"pth-step-name\">Execute Engine<\/div>\n            <p class=\"pth-step-desc\">The internal processor immediately intercepts your logic, translating the characters via local client-side memory execution.<\/p>\n        <\/div>\n        <div class=\"pth-step-item\">\n            <div class=\"pth-step-num\">4<\/div>\n            <div class=\"pth-step-name\">Extract String<\/div>\n            <p class=\"pth-step-desc\">Review the translated output and click the copy button to safely integrate the validated code directly into your stylesheet or markup.<\/p>\n        <\/div>\n    <\/div>\n<\/div>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Last Updated: August 2026<\/strong><\/p>\n\n\n\n<h2 id=\"\ud83d\udd34-what-each-tab-actually-does\" class=\"wp-block-heading\">\ud83d\udd34 What Each Tab Actually Does<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This page started life as a plain html entity encoder with four output formats. It now carries four tabs, because in practice the encoding job and the &#8220;why is my pasted code broken&#8221; job turn up in the same ten minutes of work. Here is what each tab is for and when to reach for it.<\/p>\n\n\n\n<h3 id=\"convert-nine-escape-formats\" class=\"wp-block-heading\">Convert \u2014 nine escape formats<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Paste text on the left, pick a format, read the result on the right. The conversion runs on every keystroke, so there is no button to press. Switch the Encode \/ Decode toggle to run it backwards.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A real example. Drop this line into the input with&nbsp;<strong>HTML Entities \u2014 essential characters<\/strong>&nbsp;selected:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>&lt;p class=\"note\"&gt;Price: 12 &amp; rising&lt;\/p&gt;<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">and you get back:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>&amp;lt;p class=&amp;quot;note&amp;quot;&amp;gt;Price: 12 &amp;amp; rising&amp;lt;&amp;#x2F;p&amp;gt;<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Drop that output into a page and the reader sees the tag, rather than the browser rendering an empty paragraph. That is the whole point.<\/p>\n\n\n\n<h3 id=\"inspector-one-row-per-character\" class=\"wp-block-heading\">Inspector \u2014 one row per character<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Paste anything and every character gets a row: its Unicode name, its codepoint, its decimal value, its UTF-8 byte sequence, and its HTML, CSS and JavaScript escapes side by side. Type a single emoji and you get one row, not two broken halves \u2014 the table walks codepoints rather than JavaScript string units, so \ud83d\ude00 is reported correctly as U+1F600 with four UTF-8 bytes.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The byte column is the one people find useful unexpectedly. A database column set to 20 bytes holds 20 plain Latin letters, but only 6 emoji. If a field keeps truncating, check the byte count here before blaming the database.<\/p>\n\n\n\n<h3 id=\"cleaner-the-invisible-character-scanner\" class=\"wp-block-heading\">Cleaner \u2014 the invisible character scanner<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">This is the tab worth bookmarking. Paste text copied from Word, Google Docs, a PDF or a chat window, and the scanner counts what is hiding inside it: zero-width spaces, byte order marks, non-breaking spaces, curly quotes, en and em dashes, and bidirectional override marks. The preview marks every one of them so you can see exactly where they sit.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Then tick the fixes you want and press Clean Text. Each fix is separate, because sometimes you want the curly quotes gone but the em dashes kept \u2014 an editor cleaning prose has the opposite preference to a developer cleaning a code snippet.<\/p>\n\n\n\n<h3 id=\"cheat-sheet-130-entities-searchable\" class=\"wp-block-heading\">Cheat Sheet \u2014 130+ entities, searchable<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Filter by category or search by name, description or number. Typing&nbsp;<code>8364<\/code>&nbsp;finds the euro sign,&nbsp;<code>arrow<\/code>&nbsp;returns the arrow block, and&nbsp;<code>alpha<\/code>&nbsp;jumps to Greek. Click any code to copy it.<\/p>\n\n\n\n<h2 id=\"\ud83d\udfe1-three-jobs-this-page-handles-well\" class=\"wp-block-heading\">\ud83d\udfe1 Three Jobs This Page Handles Well<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\ud83d\udd35&nbsp;<strong>Publishing a code snippet in a blog post or CMS.<\/strong>&nbsp;Run it through Convert with HTML essential characters. Without that step, a WordPress editor swallows your markup and the reader sees a rendered box instead of the code you meant to show.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\ud83d\udfe0&nbsp;<strong>Fixing a script that fails on a line that looks perfect.<\/strong>&nbsp;Paste the failing line into Cleaner. Nine times out of ten it is a non-breaking space where a normal space should be, or a curly apostrophe that arrived with the copy. Both are invisible in most editors and both stop the parser dead.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\ud83d\udfe3&nbsp;<strong>Working out why a character renders as a box.<\/strong>&nbsp;Put it in Inspector, read the codepoint, and you know whether the data is wrong or the font simply has no glyph for it. Those two problems have completely different fixes, and the codepoint tells you which one you have.<\/p>\n\n\n\n<h2 id=\"\ud83d\udfe2-mistakes-and-limits-worth-knowing\" class=\"wp-block-heading\">\ud83d\udfe2 Mistakes and Limits Worth Knowing<\/h2>\n\n\n\n<h3 id=\"escaping-is-not-the-same-as-sanitising\" class=\"wp-block-heading\">Escaping is not the same as sanitising<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Encoding output before it reaches a page is a sound habit. Decoding untrusted input is not a security control, and this tool is not a filter for hostile markup. If you are handling user-submitted content on a live site, escape at the point of output in your own code \u2014 do not paste user input through a browser tool and treat the result as safe.<\/p>\n\n\n\n<h3 id=\"each-format-belongs-to-one-context\" class=\"wp-block-heading\">Each format belongs to one context<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Named entities like&nbsp;<code>&amp;nbsp;<\/code>&nbsp;are defined by HTML, not by XML. Paste them into an RSS feed, an SVG file or a raw XML document and the parser throws an undefined-entity error. XML defines exactly five:&nbsp;<code>&amp;lt;<\/code>,&nbsp;<code>&amp;gt;<\/code>,&nbsp;<code>&amp;amp;<\/code>,&nbsp;<code>&amp;quot;<\/code>&nbsp;and&nbsp;<code>&amp;apos;<\/code>. That is why the XML option in the format list exists as its own entry, and it is the one to pick whenever the destination is not an HTML page.<\/p>\n\n\n\n<h3 id=\"what-this-page-deliberately-does-not-do\" class=\"wp-block-heading\">What this page deliberately does not do<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\ud83d\udd35&nbsp;<strong>Percent-encoding.<\/strong>&nbsp;If you need&nbsp;<code>%3C<\/code>&nbsp;and&nbsp;<code>%20<\/code>&nbsp;for a URL or query string, that lives in the&nbsp;<a href=\"https:\/\/schoolict.net\/tools\/universal-url-encoder-decoder\/\">Universal URL Encoder &amp; Decoder<\/a>. Percent-encoding and HTML entities solve different problems and mixing them produces double-encoded strings that display as literal&nbsp;<code>&amp;amp;lt;<\/code>&nbsp;on the page.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\ud83d\udfe0&nbsp;<strong>Base64.<\/strong>&nbsp;That belongs in the&nbsp;<a href=\"https:\/\/schoolict.net\/tools\/free-base64-encoder-decoder-utf-8-supported\/\">Base64 Encoder \/ Decoder<\/a>. Base64 makes binary data safe to transport as text; entities make reserved characters safe to display. Different jobs.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\ud83d\udfe3&nbsp;<strong>Bulk find and replace across many files.<\/strong>&nbsp;The Cleaner works on whatever you paste into it. For a pattern-driven sweep across a large body of text, the&nbsp;<a href=\"https:\/\/schoolict.net\/tools\/advanced-text-regex-tester-debugger\/\">Regex Tester &amp; Debugger<\/a>&nbsp;is the better fit.<\/p>\n\n\n\n<h3 id=\"performance-ceilings-honestly-stated\" class=\"wp-block-heading\">Performance ceilings, honestly stated<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The Inspector renders the first 400 characters and tells you when it has truncated, because a table with fifty thousand rows locks up the tab on a mid-range phone. The Cleaner highlights the first 3,000 characters in its preview, though the count and the actual cleaning run over the whole input. Convert has no practical limit short of the browser&#8217;s own memory, but pasting a multi-megabyte file into any browser textarea will feel slow no matter which site you are on.<\/p>\n\n\n\n<h2 id=\"\ud83d\udd34-where-to-go-next\" class=\"wp-block-heading\">\ud83d\udd34 Where to Go Next<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">If you want the reasoning underneath all of this \u2014 how Unicode codepoints differ from UTF-8 bytes, why the HTML parser needs entities at all, and how invisible characters became a genuine software supply-chain problem \u2014 that is covered in the companion piece:&nbsp;<a href=\"https:\/\/schoolict.net\/tools\/html-entity-encoder-decoder-article\/\">HTML Entity Encoder &amp; Decoder: the complete guide<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The formal definition of the named character references lives in the&nbsp;<a href=\"https:\/\/html.spec.whatwg.org\/multipage\/named-characters.html\" rel=\"noreferrer noopener\" target=\"_blank\">WHATWG HTML specification<\/a>, and&nbsp;<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Glossary\/Character_reference\" rel=\"noreferrer noopener\" target=\"_blank\">MDN&#8217;s character reference glossary<\/a>&nbsp;is the friendlier read of the two.<\/p>\n\n\n\n<style>\n.pth-faq-wrap { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; margin: 24px 0; }\n.pth-faq-card { background: #ffffff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 20px; box-shadow: 0 4px 6px -1px rgba(0,0,0,0.05); }\n.pth-faq-q { font-size: 15px; font-weight: 800; color: #0f172a; margin: 0 0 8px 0; line-height: 1.45; }\n.pth-faq-a { font-size: 14px; color: #475569; margin: 0; line-height: 1.65; font-weight: 500; }\n@media (max-width: 992px) { .pth-faq-wrap { grid-template-columns: repeat(2, 1fr); } }\n@media (max-width: 640px) { .pth-faq-wrap { grid-template-columns: 1fr; } }\n<\/style>\n \n<h2>\u2753 Frequently Asked Questions<\/h2>\n \n<div class=\"pth-faq-wrap\">\n \n  <div class=\"pth-faq-card\">\n    <p class=\"pth-faq-q\">Is my text sent anywhere when I use this?<\/p>\n    <p class=\"pth-faq-a\">No. Every conversion runs in JavaScript inside your own tab. Open DevTools, switch to the Network panel and encode something \u2014 there is no request to watch, because none is made.<\/p>\n  <\/div>\n \n  <div class=\"pth-faq-card\">\n    <p class=\"pth-faq-q\">Which format should I pick for a blog post?<\/p>\n    <p class=\"pth-faq-a\">HTML Entities \u2014 essential characters. It escapes the reserved characters and leaves ordinary text readable, so the source stays easy to scan later.<\/p>\n  <\/div>\n \n  <div class=\"pth-faq-card\">\n    <p class=\"pth-faq-q\">Why did my code break after pasting from Google Docs?<\/p>\n    <p class=\"pth-faq-a\">Almost certainly a non-breaking space or a curly quote. Both look identical to their plain versions in most editors. Run the line through the Cleaner tab and the count appears immediately.<\/p>\n  <\/div>\n \n  <div class=\"pth-faq-card\">\n    <p class=\"pth-faq-q\">What is a zero-width space and why should I care?<\/p>\n    <p class=\"pth-faq-a\">A character with no visible width at all. It survives copy and paste, hides inside CSS class names and filenames, and produces selectors or comparisons that look right but never match.<\/p>\n  <\/div>\n \n  <div class=\"pth-faq-card\">\n    <p class=\"pth-faq-q\">Can I decode entities back to normal text?<\/p>\n    <p class=\"pth-faq-a\">Yes. Switch the toggle to Decode. Named entities, decimal references like <code>&amp;#8364;<\/code> and hex references like <code>&amp;#x1F600;<\/code> are all handled, including emoji.<\/p>\n  <\/div>\n \n  <div class=\"pth-faq-card\">\n    <p class=\"pth-faq-q\">Does it handle emoji and non-Latin scripts properly?<\/p>\n    <p class=\"pth-faq-a\">Yes. The engine walks codepoints rather than string units, so emoji, Chinese, Sinhala and other scripts survive a full encode and decode round trip unchanged.<\/p>\n  <\/div>\n \n  <div class=\"pth-faq-card\">\n    <p class=\"pth-faq-q\">What is the difference between this and URL encoding?<\/p>\n    <p class=\"pth-faq-a\">Entities make reserved characters safe to display in markup. Percent-encoding makes characters safe inside a URL. Applying the wrong one gives you visible <code>%3C<\/code> in body text or broken links.<\/p>\n  <\/div>\n \n  <div class=\"pth-faq-card\">\n    <p class=\"pth-faq-q\">Why does the Inspector stop at 400 characters?<\/p>\n    <p class=\"pth-faq-a\">A per-character table grows fast, and rendering tens of thousands of rows freezes the tab on a phone. The table says when it has truncated so the limit is never silent.<\/p>\n  <\/div>\n \n  <div class=\"pth-faq-card\">\n    <p class=\"pth-faq-q\">Are named entities safe to use in XML or SVG?<\/p>\n    <p class=\"pth-faq-a\">Only five are: lt, gt, amp, quot and apos. Anything else, including <code>&amp;nbsp;<\/code>, throws an undefined-entity error. Pick the XML format for those destinations.<\/p>\n  <\/div>\n \n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Safely Encode and Decode HTML Entities Safely encode and decode HTML entities, CSS escapes, and JS Unicode offline. Perfect for web developers displaying code snippets securely without breaking layouts. \ud83d\udfe2 Real-Time Character Parsing Instantly scans your text strings to identify and convert reserved syntax into safe, universally compatible structural code. \ud83d\udd35 Bi-Directional Translation Seamlessly toggles &#8230; <a title=\"HTML Entity Encoder &amp; Decoder\" class=\"read-more\" href=\"https:\/\/schoolict.net\/tools\/html-entity-encoder-decoder\/\" aria-label=\"Read more about HTML Entity Encoder &amp; Decoder\">Read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":835,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-833","page","type-page","status-publish","has-post-thumbnail"],"_links":{"self":[{"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/pages\/833","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/comments?post=833"}],"version-history":[{"count":4,"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/pages\/833\/revisions"}],"predecessor-version":[{"id":6910,"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/pages\/833\/revisions\/6910"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/media\/835"}],"wp:attachment":[{"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/media?parent=833"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}