{"id":814,"date":"2026-04-04T08:42:30","date_gmt":"2026-04-04T08:42:30","guid":{"rendered":"https:\/\/primetoolhub.com\/?page_id=814"},"modified":"2026-07-13T10:52:05","modified_gmt":"2026-07-13T10:52:05","slug":"offline-html-editor-with-css-js","status":"publish","type":"page","link":"https:\/\/schoolict.net\/tools\/offline-html-editor-with-css-js\/","title":{"rendered":"Offline HTML Editor | An editor that tells you when your code breaks"},"content":{"rendered":"\n<div class=\"pth-hero-section\">\n    <div class=\"pth-hero-content\">\n        <h2>Offline HTML Editor with CSS &amp; JS<\/h2>\n        <p>Write, test, and debug HTML, CSS, and JavaScript instantly with our free Offline HTML Editor. Features live preview, 3-tab workspace, and HTML export.<\/p>\n\n        <div id=\"pth-toc-placeholder\"><\/div>\n    <\/div>\n\n   \n <div class=\"pth-hero-image\">\n\t<img data-no-lazy=\"1\"\n         src=\"https:\/\/schoolict.net\/tools\/wp-content\/uploads\/2026\/05\/Offline-HTML-Editor-800x447.jpeg\"\n         width=\"800\"\n         height=\"447\"\n         alt=\"Offline HTML Editor\"\n         fetchpriority=\"high\"\n         loading=\"eager\"\n         decoding=\"async\"\n         style=\"width:100%; height:auto; display:block;\">\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-write-run-and-actually-see-the-errors\">\ud83d\udd34\u00a0Write, run, and actually see the errors<\/a><\/li><li><a href=\"#\ud83d\udfe1-templates-saved-projects-and-getting-the-code-out\">\ud83d\udfe1\u00a0Templates, saved projects, and getting the code out<\/a><\/li><li><a href=\"#\ud83d\udfe2-honest-limits-and-which-tool-to-use-next\">\ud83d\udfe2\u00a0Honest limits, and which tool to use next<\/a><\/li><\/ul><\/nav><\/div>\n\n\n\n\n\n<link rel=\"stylesheet\" href=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/font-awesome\/6.5.1\/css\/all.min.css\">\r\n<link rel=\"stylesheet\" href=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/codemirror\/5.65.2\/codemirror.min.css\">\r\n<link rel=\"stylesheet\" href=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/codemirror\/5.65.2\/theme\/monokai.min.css\">\r\n\r\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/codemirror\/5.65.2\/codemirror.min.js\" data-no-optimize=\"1\" data-no-minify=\"1\" data-cfasync=\"false\"><\/script>\r\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/codemirror\/5.65.2\/mode\/xml\/xml.min.js\" data-no-optimize=\"1\" data-no-minify=\"1\" data-cfasync=\"false\"><\/script>\r\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/codemirror\/5.65.2\/mode\/css\/css.min.js\" data-no-optimize=\"1\" data-no-minify=\"1\" data-cfasync=\"false\"><\/script>\r\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/codemirror\/5.65.2\/mode\/javascript\/javascript.min.js\" data-no-optimize=\"1\" data-no-minify=\"1\" data-cfasync=\"false\"><\/script>\r\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/codemirror\/5.65.2\/mode\/htmlmixed\/htmlmixed.min.js\" data-no-optimize=\"1\" data-no-minify=\"1\" data-cfasync=\"false\"><\/script>\r\n\r\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/codemirror\/5.65.2\/addon\/search\/searchcursor.min.js\" data-no-optimize=\"1\" data-no-minify=\"1\" data-cfasync=\"false\"><\/script>\r\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/jszip\/3.10.1\/jszip.min.js\" data-no-optimize=\"1\" data-no-minify=\"1\" data-cfasync=\"false\"><\/script>\r\n\r\n<style>\r\n\/* CRITICAL: Master Skeleton Scoped CSS *\/\r\n#pth-webstudio-wrapper { font-family: 'Inter', system-ui, sans-serif; max-width: 1400px; margin: 0 auto; box-sizing: border-box; background: #f8fafc; padding: 20px; border-radius: 16px; color: #0f172a; position: relative; }\r\n#pth-webstudio-wrapper * { box-sizing: border-box; min-width: 0; }\r\n#pth-webstudio-wrapper .pth-card { background: #ffffff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 25px; margin-bottom: 25px; box-shadow: 0 4px 6px -1px rgba(0,0,0,0.05); }\r\n#pth-webstudio-wrapper .pth-tool-title { font-size: 28px; font-weight: 800; color: #3b82f6; margin-top: 0; margin-bottom: 10px; }\r\n#pth-webstudio-wrapper .pth-flex-layout { display: grid; grid-template-columns: 1fr; gap: 20px; align-items: start; transition: 0.3s ease; }\r\n\r\n\/* \u2500\u2500 TOOL SPECIFIC CSS (Premium Dark Theme) \u2500\u2500 *\/\r\n#ws-app { font-family: 'Inter', system-ui, sans-serif; background: #0f172a; height: 85vh; min-height: 750px; display: flex; flex-direction: column; overflow: hidden; color: #f1f5f9; border-radius: 12px; border: 1px solid #334155; position: relative; }\r\n\r\n\/* Header & Menus - FIXED: Added position: relative and z-index: 100 to overlap editors *\/\r\n#ws-hdr { position: relative; z-index: 100; background: #1e293b; padding: 0 16px; display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; min-height: 48px; border-bottom: 1px solid #0f172a; flex-shrink: 0; }\r\n#ws-hdr-l { display: flex; align-items: center; gap: 10px; height: 100%; }\r\n#ws-hdr h1 { margin: 0; font-size: 15px; font-weight: 800; color: #f1f5f9; display: flex; align-items: center; gap: 8px; margin-right: 15px;}\r\n#ws-hdr i.logo { color: #f87171; font-size: 18px; }\r\n\r\n.ws-menu-wrap { position: relative; height: 100%; display: flex; align-items: center; }\r\n.ws-menu-btn { background: transparent; border: none; color: #94a3b8; font-size: 13px; font-weight: 600; padding: 0 12px; height: 100%; cursor: pointer; display: flex; align-items: center; gap: 6px; transition: 0.2s; font-family: inherit; }\r\n.ws-menu-btn:hover, .ws-menu-wrap.active .ws-menu-btn { background: #334155; color: #f1f5f9; }\r\n.ws-menu-drop { position: absolute; top: 100%; left: 0; background: #1e293b; border: 1px solid #334155; border-radius: 0 0 8px 8px; box-shadow: 0 12px 32px rgba(0,0,0,0.6); display: none; flex-direction: column; z-index: 9999; min-width: 240px; padding: 8px; }\r\n.ws-menu-wrap.active .ws-menu-drop { display: flex; animation: pthFadeDown .15s ease; }\r\n.ws-m-item { background: none; border: none; color: #cbd5e1; font-size: 13px; font-weight: 500; padding: 10px 14px; cursor: pointer; display: flex; align-items: center; gap: 10px; border-radius: 5px; width: 100%; text-align: left; transition: 0.1s; font-family: inherit; }\r\n.ws-m-item:hover { background: #334155; color: #f1f5f9; }\r\n.ws-m-item i { width: 16px; text-align: center; color: #64748b; }\r\n.ws-m-item:hover i { color: #94a3b8; }\r\n.ws-m-item .kk { margin-left: auto; font-size: 11px; color: #475569; background: #0f172a; padding: 2px 6px; border-radius: 4px; font-family: monospace; font-weight: bold; }\r\n.ws-menu-drop hr { border: none; border-top: 1px solid #334155; margin: 4px 0; }\r\n\r\n\/* Header Right *\/\r\n#ws-hdr-r { display: flex; align-items: center; gap: 14px; height: 100%; }\r\n.ws-toggle-label { display: flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 700; color: #94a3b8; cursor: pointer; user-select: none; }\r\n.ws-toggle-label input { width: 14px; height: 14px; accent-color: #3b82f6; cursor: pointer; }\r\n.ws-run-btn { background: #10b981; color: #fff; padding: 6px 16px; border-radius: 6px; border: none; font-size: 13px; font-weight: 700; cursor: pointer; display: flex; align-items: center; gap: 6px; transition: 0.2s; font-family: inherit; }\r\n.ws-run-btn:hover { background: #059669; }\r\n\r\n\/* Body Layout *\/\r\n#ws-body { display: flex; flex: 1; min-height: 0; background: #334155; overflow: hidden; position: relative; }\r\n#ws-L { flex-basis: 50%; display: flex; flex-direction: column; background: #1e1e1e; min-width: 280px; overflow: hidden; transition: flex-basis .25s; position: relative;}\r\n#ws-R { flex-basis: 50%; display: flex; flex-direction: column; background: #f8fafc; min-width: 0; overflow: hidden; position: relative; transition: flex-basis .25s, opacity .25s; }\r\n#ws-app.editor-full #ws-L { flex-basis: 100%; }\r\n#ws-app.editor-full #ws-resizer { display: none; }\r\n#ws-app.editor-full #ws-R { flex-basis: 0; opacity: 0; pointer-events: none; }\r\n#ws-app.code-fullscreen { position: fixed !important; top: 0; left: 0; width: 100vw; height: 100vh; z-index: 999990; border-radius: 0; border: none; }\r\n\r\n\/* Resizer *\/\r\n#ws-resizer { width: 6px; background: #0f172a; cursor: col-resize; flex-shrink: 0; transition: background .15s; z-index: 10; display: flex; align-items: center; justify-content: center; color: #475569; font-size: 10px; }\r\n#ws-resizer:hover, #ws-resizer.active { background: #3b82f6; color: #fff; }\r\n\r\n\/* Left Editor Panes (Tabs & Actions) *\/\r\n.ws-tabs-bar { display: flex; background: #1a2332; border-bottom: 1px solid #0f172a; height: 40px; flex-shrink: 0; align-items: center; justify-content: space-between; padding-right: 10px; }\r\n.ws-tabs { display: flex; height: 100%; }\r\n.ws-tab { padding: 0 16px; display: flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 700; color: #64748b; cursor: pointer; border-bottom: 3px solid transparent; transition: 0.2s; position: relative;}\r\n.ws-tab:hover { color: #f1f5f9; background: #1e293b; }\r\n.ws-tab.active { color: #f1f5f9; background: #1e1e1e; }\r\n.ws-tab.html.active { border-bottom-color: #e34c26; }\r\n.ws-tab.css.active { border-bottom-color: #264de4; }\r\n.ws-tab.js.active { border-bottom-color: #f0db4f; }\r\n\r\n\/* \ud83d\ude80 ADDED: Tab Badges *\/\r\n.ws-tab-badge { background: #334155; color: #94a3b8; font-size: 10px; padding: 2px 6px; border-radius: 12px; font-family: monospace; font-weight: 800; display: inline-block;}\r\n.ws-tab.active .ws-tab-badge { background: #0f172a; color: #3b82f6; }\r\n\r\n.ws-tb-btn { background: transparent; border: none; color: #64748b; font-size: 13px; cursor: pointer; padding: 4px 8px; border-radius: 4px; transition: 0.2s; display: inline-flex; align-items: center; gap: 5px; font-weight: 600; font-family: inherit;}\r\n.ws-tb-btn:hover { color: #f1f5f9; background: #334155; }\r\n.ws-tb-btn.danger:hover { color: #f87171; background: #fee2e2; } \r\n\r\n\/* CodeMirror Wrapper *\/\r\n.ws-editor-wrap { flex: 1; position: relative; min-width: 0; display: flex; flex-direction: column; overflow: hidden; }\r\n.ws-code-view { display: none; width: 100%; height: 100%; flex-direction: column; }\r\n.ws-code-view.active { display: flex; }\r\n.CodeMirror { flex: 1; height: 100% !important; font-family: 'Courier New', monospace; font-size: 15px; line-height: 1.5; }\r\n\r\n\/* \ud83d\ude80 ADDED: Find & Replace Bar *\/\r\n#ws-fr-bar { position: absolute; top: 0; right: 0; background: #1e293b; border-bottom: 1px solid #334155; border-left: 1px solid #334155; border-bottom-left-radius: 8px; padding: 8px 12px; display: none; align-items: center; gap: 8px; z-index: 50; box-shadow: -4px 4px 15px rgba(0,0,0,0.3); }\r\n#ws-fr-bar.show { display: flex; animation: pthFadeDown .2s ease; }\r\n.fr-input { background: #0f172a; border: 1px solid #475569; color: #f1f5f9; padding: 4px 8px; border-radius: 4px; font-size: 12px; font-family: inherit; outline: none; width: 120px; }\r\n.fr-input:focus { border-color: #3b82f6; }\r\n.fr-btn { background: #334155; color: #cbd5e1; border: none; padding: 4px 8px; border-radius: 4px; font-size: 11px; font-weight: 700; cursor: pointer; transition: 0.2s; }\r\n.fr-btn:hover { background: #475569; color: #fff; }\r\n.fr-count { font-size: 11px; font-family: monospace; color: #94a3b8; font-weight: 700; min-width: 35px; text-align: center; }\r\n\r\n\/* \ud83d\ude80 ADDED: Zoom Badge *\/\r\n#ws-zoom-badge { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(15,23,42,0.8); color: white; padding: 10px 20px; border-radius: 8px; font-size: 18px; font-weight: 800; z-index: 60; display: none; backdrop-filter: blur(4px); pointer-events: none; }\r\n\r\n\/* Right Preview Area *\/\r\n.ws-prev-tb { background: #ffffff; border-bottom: 1px solid #e2e8f0; display: flex; align-items: center; justify-content: space-between; padding: 0 14px; height: 44px; flex-shrink: 0; }\r\n.ws-prev-title { font-size: 12px; font-weight: 800; color: #475569; text-transform: uppercase; letter-spacing: 1px; display: flex; align-items: center; gap: 6px; margin: 0; }\r\n\r\n\/* \ud83d\udca1 ADDED: Project Name Cage *\/\r\n.ws-proj-cage { display: flex; align-items: center; gap: 6px; font-size: 15px; color: #94a3b8; font-weight: 800; }\r\n.ws-proj-cage input { border: none; background: transparent; outline: none; font-family: 'Inter', sans-serif; font-size: 14px; font-weight: 700; color: #0f172a; text-align: center; width: 140px; transition: 0.2s; border-bottom: 1px dashed transparent; }\r\n.ws-proj-cage input:focus { color: #3b82f6; border-bottom-color: #3b82f6; width: 160px; }\r\n\r\n.ws-device-group { display: flex; background: #f1f5f9; padding: 3px; border-radius: 6px; border: 1px solid #e2e8f0; }\r\n.ws-dev-btn { background: transparent; border: none; color: #64748b; padding: 4px 10px; border-radius: 4px; font-size: 12px; font-weight: 700; cursor: pointer; transition: 0.2s; display: flex; align-items: center; gap: 5px; }\r\n.ws-dev-btn:hover { color: #0f172a; }\r\n.ws-dev-btn.active { background: #fff; color: #3b82f6; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }\r\n.ws-pv-btn { background: #f1f5f9; border: 1px solid #e2e8f0; color: #475569; padding: 4px 10px; border-radius: 5px; font-size: 12px; font-weight: 700; cursor: pointer; display: flex; align-items: center; gap: 6px; transition: 0.15s; font-family: inherit;}\r\n.ws-pv-btn:hover { background: #e2e8f0; color: #1e293b; }\r\n\r\n\/* \ud83d\ude80 WYSIWYG TOOLBAR (Added Colors, Size, Unlink) *\/\r\n.ws-wysiwyg-bar { display: flex; align-items: center; background: #f8fafc; border-bottom: 1px solid #e2e8f0; padding: 6px 14px; gap: 4px; flex-wrap: wrap; flex-shrink: 0; }\r\n.w-btn { background: transparent; border: 1px solid transparent; color: #475569; padding: 4px 8px; border-radius: 4px; font-size: 13px; cursor: pointer; transition: 0.15s; display: inline-flex; align-items: center; justify-content: center; }\r\n.w-btn:hover { background: #e2e8f0; border-color: #cbd5e1; color: #0f172a; }\r\n.w-btn.danger { color: #f87171; }\r\n.w-btn.danger:hover { color: #e11d48; border-color: #fca5a5; background: #fee2e2; }\r\n.w-btn b { font-family: serif; font-size: 14px; }\r\n.w-sep { width: 1px; height: 16px; background: #cbd5e1; margin: 0 4px; }\r\n.w-color { padding: 0; width: 22px; height: 22px; border: 1px solid #cbd5e1; border-radius: 4px; cursor: pointer; overflow: hidden; }\r\n.w-select { background: #f1f5f9; border: 1px solid #cbd5e1; color: #475569; font-size: 12px; padding: 2px 4px; border-radius: 4px; outline: none; font-weight: 600; cursor: pointer;}\r\n\r\n\/* \ud83d\ude80 REAL MOBILE RESPONSIVE DEVICE WRAPPER *\/\r\n.ws-prev-container { flex: 1; display: flex; justify-content: center; align-items: center; background: #e2e8f0; overflow: auto; padding: 20px; position: relative; }\r\n.ws-device-wrapper { transition: all 0.3s ease; box-shadow: 0 10px 25px rgba(0,0,0,0.15); border: none; background: #fff; overflow: hidden; display: flex; flex-direction: column; }\r\n.ws-device-wrapper iframe { width: 100%; height: 100%; border: none; flex: 1; background: #fff; }\r\n\r\n.device-desktop { width: 100%; height: 100%; box-shadow: none !important; border-radius: 0; }\r\n.device-tablet { width: 100%; max-width: 768px; height: 100%; max-height: 1024px; border: 24px solid #0f172a; border-radius: 24px; box-shadow: 0 25px 50px -12px rgba(0,0,0,0.5); }\r\n.device-mobile { width: 100%; max-width: 375px; height: 100%; max-height: 812px; border: 14px solid #0f172a; border-radius: 36px; position: relative; box-shadow: 0 25px 50px -12px rgba(0,0,0,0.5); }\r\n.device-mobile::before { content: ''; position: absolute; top: 0; left: 50%; transform: translateX(-50%); width: 120px; height: 24px; background: #0f172a; border-bottom-left-radius: 16px; border-bottom-right-radius: 16px; z-index: 10; pointer-events: none; }\r\n\r\n\/* \ud83d\ude80 ADDED: Console Log Levels & Colors *\/\r\n.ws-console-panel { position: absolute; bottom: 0; left: 0; right: 0; height: 240px; background: #1e293b; color: #38bdf8; font-family: 'Courier New', monospace; font-size: 13px; overflow-y: auto; padding: 12px; display: none; border-top: 2px solid #3b82f6; z-index: 100; box-shadow: 0 -10px 20px rgba(0,0,0,0.2); }\r\n.ws-console-panel.show { display: block; animation: pthFadeUp .2s ease; }\r\n.ws-console-hdr { display: flex; justify-content: space-between; font-weight: 700; margin-bottom: 8px; color: #f8fafc; border-bottom: 1px dashed #475569; padding-bottom: 8px; font-size: 12px; text-transform: uppercase; align-items: center;}\r\n.c-log { color: #f1f5f9; margin-bottom: 4px; border-bottom: 1px solid #334155; padding-bottom: 2px;}\r\n.c-warn { color: #f59e0b; background: rgba(245, 158, 11, 0.1); padding: 2px 4px; border-left: 2px solid #f59e0b; margin-bottom: 4px;}\r\n.c-err { color: #ef4444; background: rgba(239, 68, 68, 0.1); padding: 2px 4px; border-left: 2px solid #ef4444; margin-bottom: 4px;}\r\n\r\n\/* \ud83d\ude80 Statusbar (Upgraded) *\/\r\n#ws-statusbar { background: #0f172a; border-top: 1px solid #1e293b; height: 30px; display: flex; align-items: center; padding: 0 16px; gap: 16px; flex-shrink: 0; }\r\n.sbs { font-size: 11px; color: #475569; font-weight: 700; display: flex; align-items: center; gap: 6px; text-transform: uppercase; }\r\n.sbs.ok { color: #10b981; } .sbs.warn { color: #f59e0b; }\r\n.sbs-right { margin-left: auto; display: flex; gap: 16px; align-items: center; }\r\n.sbs-info { color: #94a3b8; font-family: monospace; font-size: 11px; font-weight: 600; }\r\n\r\n\/* Toast & Custom Modal *\/\r\n#ws-toast { position: fixed; bottom: 40px; right: 20px; background: #10b981; color: #fff; padding: 12px 20px; border-radius: 8px; box-shadow: 0 8px 24px rgba(0,0,0,.3); display: none; align-items: center; gap: 8px; z-index: 2147483647; font-weight: 700; font-size: 14px; }\r\n#ws-toast.show { display: flex; animation: pthToastPop .2s ease; }\r\n#ws-toast.err { background: #ef4444; }\r\n\r\n#pth-ms-modal-overlay { display: none; position: fixed; inset: 0; background: rgba(15, 23, 42, 0.7); z-index: 2147483647; align-items: center; justify-content: center; backdrop-filter: blur(3px); }\r\n#pth-ms-modal-overlay.show { display: flex; animation: pthFadeDown .2s ease; }\r\n#pth-ms-modal { background: #1e293b; border: 1px solid #334155; border-radius: 12px; padding: 24px; width: 340px; max-width: 90%; box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.5); text-align: left; }\r\n#pth-ms-modal h3 { margin: 0 0 10px; font-size: 18px; color: #f8fafc; font-weight: 800; }\r\n#pth-ms-modal p { margin: 0 0 20px; font-size: 14px; color: #94a3b8; line-height: 1.5; }\r\n.pth-modal-input { background: #0f172a; border: 1px solid #475569; color: #f1f5f9; padding: 8px 12px; border-radius: 6px; font-size: 14px; width: 100%; margin-bottom: 15px; outline: none; display: none; }\r\n.pth-modal-input:focus { border-color: #3b82f6; }\r\n.pth-ms-modal-btns { display: flex; gap: 10px; justify-content: flex-end; }\r\n.pth-ms-modal-btns button { padding: 8px 16px; border-radius: 6px; font-weight: 700; font-size: 13px; cursor: pointer; border: none; font-family: inherit; }\r\n.pth-ms-modal-btns .btn-cancel { background: #334155; color: #f1f5f9; transition:0.2s;}\r\n.pth-ms-modal-btns .btn-cancel:hover { background: #475569; }\r\n.pth-ms-modal-btns .btn-ok { background: #e11d48; color: #fff; transition:0.2s;}\r\n.pth-ms-modal-btns .btn-ok:hover { background: #be123c; }\r\n\r\n@keyframes pthFadeDown { from { opacity: 0; transform: translateY(-10px); } to { opacity: 1; transform: translateY(0); } }\r\n@keyframes pthFadeUp { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }\r\n@keyframes pthToastPop { from { transform: translateY(50px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }\r\n\r\n@media (max-width: 900px) {\r\n  #ws-body { flex-direction: column; height: auto; min-height: 800px; }\r\n  #ws-L, #ws-R { flex-basis: 400px!important; width: 100%; }\r\n  #ws-resizer { width: 100%; height: 8px; cursor: row-resize; }\r\n  #ws-resizer i { transform: rotate(90deg); }\r\n}\r\n<\/style>\r\n\r\n<div id=\"pth-webstudio-wrapper\">\r\n    <div class=\"pth-flex-layout\">\r\n        <div class=\"pth-main-col\">\r\n            \r\n            <div id=\"ws-app\">\r\n                <div id=\"ws-hdr\">\r\n                    <div id=\"ws-hdr-l\">\r\n                        <h2><i class=\"fas fa-laptop-code logo\"><\/i> Web Studio Pro<\/h2>\r\n                        \r\n                        <div class=\"ws-menu-wrap\" id=\"mw-file\">\r\n                            <button class=\"ws-menu-btn\" id=\"mi-file-btn\">File <i class=\"fas fa-caret-down\" style=\"font-size:10px\"><\/i><\/button>\r\n                            <div class=\"ws-menu-drop\">\r\n                                <button class=\"ws-m-item\" id=\"mi-new\"><i class=\"fas fa-file-plus\"><\/i> New Project<\/button>\r\n                                <button class=\"ws-m-item\" id=\"mi-load\"><i class=\"fas fa-folder-open\"><\/i> Load Auto-Save<\/button>\r\n                                <button class=\"ws-m-item\" id=\"mi-import\"><i class=\"fas fa-file-import\"><\/i> Import Files<\/button>\r\n                                <hr>\r\n                                <button class=\"ws-m-item\" id=\"mi-export\"><i class=\"fas fa-file-download\"><\/i> Export HTML <span class=\"kk\">Ctrl+S<\/span><\/button>\r\n                                <button class=\"ws-m-item\" id=\"mi-export-zip\"><i class=\"fas fa-file-archive\"><\/i> Export as ZIP<\/button>\r\n                            <\/div>\r\n                        <\/div>\r\n\r\n                        <div class=\"ws-menu-wrap\" id=\"mw-edit\">\r\n                            <button class=\"ws-menu-btn\" id=\"mi-edit-btn\">Edit <i class=\"fas fa-caret-down\" style=\"font-size:10px\"><\/i><\/button>\r\n                            <div class=\"ws-menu-drop\">\r\n                                <button class=\"ws-m-item\" id=\"mi-find\"><i class=\"fas fa-search\"><\/i> Find & Replace <span class=\"kk\">Ctrl+F<\/span><\/button>\r\n                                <button class=\"ws-m-item\" id=\"mi-goto\"><i class=\"fas fa-level-down-alt\"><\/i> Go to Line <span class=\"kk\">Ctrl+G<\/span><\/button>\r\n                                <button class=\"ws-m-item\" id=\"mi-format\"><i class=\"fas fa-indent\"><\/i> Format Code <span class=\"kk\">Shift+Alt+F<\/span><\/button>\r\n                                <hr>\r\n                                <h4 style=\"margin: 4px 12px; font-size:11px; color:#475569; text-transform:uppercase;\">Insert Snippets<\/h4>\r\n                                <button class=\"ws-m-item\" id=\"mi-snip-html5\"><i class=\"fab fa-html5\"><\/i> HTML5 Boilerplate<\/button>\r\n                                <button class=\"ws-m-item\" id=\"mi-snip-seo\"><i class=\"fas fa-tags\"><\/i> SEO Meta Tags<\/button>\r\n                                <button class=\"ws-m-item\" id=\"mi-snip-grid\"><i class=\"fas fa-th\"><\/i> CSS Grid Layout<\/button>\r\n                            <\/div>\r\n                        <\/div>\r\n\r\n                        <div class=\"ws-menu-wrap\" id=\"mw-samples\">\r\n                            <button class=\"ws-menu-btn\" id=\"mi-samples-btn\">Samples <i class=\"fas fa-caret-down\" style=\"font-size:10px\"><\/i><\/button>\r\n                            <div class=\"ws-menu-drop\">\r\n                                <button class=\"ws-m-item tpl-btn\" data-tpl=\"tailwind\"><i class=\"fab fa-css3\"><\/i> Tailwind Demo<\/button>\r\n                                <button class=\"ws-m-item tpl-btn\" data-tpl=\"standard\"><i class=\"fab fa-html5\"><\/i> Standard Vanilla<\/button>\r\n                                <hr>\r\n                                <button class=\"ws-m-item tpl-btn\" data-tpl=\"landing\"><i class=\"fas fa-rocket\"><\/i> Landing Page<\/button>\r\n                                <button class=\"ws-m-item tpl-btn\" data-tpl=\"dashboard\"><i class=\"fas fa-chart-line\"><\/i> Dashboard UI<\/button>\r\n                                <button class=\"ws-m-item tpl-btn\" data-tpl=\"contact\"><i class=\"fas fa-envelope\"><\/i> Contact Form<\/button>\r\n                            <\/div>\r\n                        <\/div>\r\n\r\n                        <div class=\"ws-menu-wrap\" id=\"mw-view\">\r\n                            <button class=\"ws-menu-btn\" id=\"mi-view-btn\">View <i class=\"fas fa-caret-down\" style=\"font-size:10px\"><\/i><\/button>\r\n                            <div class=\"ws-menu-drop\">\r\n                                <button class=\"ws-m-item\" id=\"mi-console\"><i class=\"fas fa-terminal\"><\/i> Toggle Console<\/button>\r\n                                <hr>\r\n                                <button class=\"ws-m-item\" id=\"mi-editor-fs\"><i class=\"fas fa-expand-alt\"><\/i> Full Width Editor<\/button>\r\n                                <button class=\"ws-m-item\" id=\"mi-app-fs\"><i class=\"fas fa-expand\"><\/i> App Fullscreen<\/button>\r\n                                <hr>\r\n                                <button class=\"ws-m-item\" id=\"mi-zoom-in\"><i class=\"fas fa-search-plus\"><\/i> Zoom In<\/button>\r\n                                <button class=\"ws-m-item\" id=\"mi-zoom-out\"><i class=\"fas fa-search-minus\"><\/i> Zoom Out<\/button>\r\n                            <\/div>\r\n                        <\/div>\r\n\r\n                        <div class=\"ws-menu-wrap\" id=\"mw-help\">\r\n                            <button class=\"ws-menu-btn\" id=\"mi-help-btn\" style=\"color:#f59e0b;\"><i class=\"fas fa-question-circle\"><\/i> Help<\/button>\r\n                            <div class=\"ws-menu-drop\" style=\"width: 250px; padding: 12px;\">\r\n                                <h4 style=\"margin: 0 0 10px; font-size:13px; color:#f1f5f9; border-bottom:1px solid #334155; padding-bottom:6px; font-weight:800;\"><i class=\"fas fa-keyboard\" style=\"color:#3b82f6\"><\/i> Shortcuts<\/h4>\r\n                                <div class=\"pth-help-row\"><span>Export Project<\/span> <code>Ctrl+S<\/code><\/div>\r\n                                <div class=\"pth-help-row\"><span>Run \/ Refresh<\/span> <code>Ctrl+Enter<\/code><\/div>\r\n                                <div class=\"pth-help-row\"><span>Find \/ Replace<\/span> <code>Ctrl+F<\/code><\/div>\r\n                                <div class=\"pth-help-row\"><span>Go to Line<\/span> <code>Ctrl+G<\/code><\/div>\r\n                                <div class=\"pth-help-row\"><span>Format Code<\/span> <code>Shift+Alt+F<\/code><\/div>\r\n                                <div style=\"text-align:center; font-size:10px; color:#64748b; margin-top:10px;\">Press ESC to close<\/div>\r\n                            <\/div>\r\n                        <\/div>\r\n\r\n                    <\/div>\r\n                    <div id=\"ws-hdr-r\">\r\n                        <label class=\"ws-toggle-label\" title=\"Inject Tailwind CDN\">\r\n                            <input type=\"checkbox\" id=\"ws-tw-toggle\" checked> <span style=\"color:#38bdf8;\"><i class=\"fab fa-css3-alt\"><\/i> Tailwind<\/span>\r\n                        <\/label>\r\n\r\n                        <label class=\"ws-toggle-label\" title=\"Auto run code on type\">\r\n                            <input type=\"checkbox\" id=\"ws-auto-run\" checked> Auto-Run\r\n                        <\/label>\r\n                        \r\n                        <button class=\"ws-run-btn\" id=\"btn-run\"><i class=\"fas fa-play\"><\/i> Run<\/button>\r\n                    <\/div>\r\n                <\/div>\r\n\r\n                <div id=\"ws-body\">\r\n                    \r\n                    <div id=\"ws-L\">\r\n                        <div class=\"ws-tabs-bar\">\r\n                            <div class=\"ws-tabs\">\r\n                                <div class=\"ws-tab html active\" data-target=\"ws-html-box\" id=\"tab-html\"><i class=\"fab fa-html5\" style=\"color:#e34c26;\"><\/i> HTML <span class=\"ws-tab-badge\" id=\"bdg-html\">0<\/span><\/div>\r\n                                <div class=\"ws-tab css\" data-target=\"ws-css-box\" id=\"tab-css\"><i class=\"fab fa-css3-alt\" style=\"color:#264de4;\"><\/i> CSS <span class=\"ws-tab-badge\" id=\"bdg-css\">0<\/span><\/div>\r\n                                <div class=\"ws-tab js\" data-target=\"ws-js-box\" id=\"tab-js\"><i class=\"fab fa-js\" style=\"color:#f0db4f;\"><\/i> JS <span class=\"ws-tab-badge\" id=\"bdg-js\">0<\/span><\/div>\r\n                            <\/div>\r\n                            <div style=\"display:flex; gap:6px;\">\r\n                                <button class=\"ws-tb-btn\" id=\"tb-format\" title=\"Format Code\"><i class=\"fas fa-indent\"><\/i><\/button>\r\n                                <button class=\"ws-tb-btn\" id=\"tb-console\" title=\"Toggle Console\"><i class=\"fas fa-terminal\"><\/i><\/button>\r\n                                <div style=\"width:1px; background:#334155; margin: 4px 2px;\"><\/div>\r\n                                <button class=\"ws-tb-btn danger\" id=\"tb-clear\" title=\"Clear Active Code Tab\"><i class=\"fas fa-trash-alt\"><\/i><\/button>\r\n                            <\/div>\r\n                        <\/div>\r\n\r\n                        <div class=\"ws-editor-wrap\">\r\n                            <div id=\"ws-html-box\" class=\"ws-code-view active\"><textarea id=\"ws-html-code\"><\/textarea><\/div>\r\n                            <div id=\"ws-css-box\" class=\"ws-code-view\"><textarea id=\"ws-css-code\"><\/textarea><\/div>\r\n                            <div id=\"ws-js-box\" class=\"ws-code-view\"><textarea id=\"ws-js-code\"><\/textarea><\/div>\r\n                            \r\n                            <div id=\"ws-fr-bar\">\r\n                                <i class=\"fas fa-search\" style=\"color:#94a3b8; font-size:12px;\"><\/i>\r\n                                <input type=\"text\" id=\"fr-find\" class=\"fr-input\" placeholder=\"Find...\">\r\n                                <button class=\"fr-btn\" id=\"fr-prev\" title=\"Previous\"><i class=\"fas fa-chevron-up\"><\/i><\/button>\r\n                                <button class=\"fr-btn\" id=\"fr-next\" title=\"Next\"><i class=\"fas fa-chevron-down\"><\/i><\/button>\r\n                                <span class=\"fr-count\" id=\"fr-count\">0\/0<\/span>\r\n                                <div style=\"width:1px; height:16px; background:#475569;\"><\/div>\r\n                                <input type=\"text\" id=\"fr-replace\" class=\"fr-input\" placeholder=\"Replace...\">\r\n                                <button class=\"fr-btn\" id=\"fr-rep-btn\">Replace<\/button>\r\n                                <button class=\"fr-btn\" id=\"fr-repall-btn\">All<\/button>\r\n                                <div style=\"width:1px; height:16px; background:#475569;\"><\/div>\r\n                                <button class=\"fr-btn\" id=\"fr-close\" style=\"color:#f87171;\"><i class=\"fas fa-times\"><\/i><\/button>\r\n                            <\/div>\r\n\r\n                            <div id=\"ws-zoom-badge\">100%<\/div>\r\n                        <\/div>\r\n                    <\/div>\r\n\r\n                    <div id=\"ws-resizer\"><i class=\"fas fa-grip-lines-vertical\"><\/i><\/div>\r\n\r\n                    <div id=\"ws-R\">\r\n                        <div class=\"ws-prev-tb\">\r\n                            <p class=\"ws-prev-title\"><i class=\"fas fa-eye\" style=\"color:#3b82f6\"><\/i> Visual Editor<\/p>\r\n                            \r\n                            <div class=\"ws-proj-cage\" title=\"Project Name (Press Enter to apply)\">\r\n                                [ <input type=\"text\" id=\"ws-proj-name\" value=\"Tailwind Project\" placeholder=\"Project name...\"> ]\r\n                            <\/div>\r\n\r\n                            <div style=\"display:flex; align-items:center; gap:12px;\">\r\n                                <div class=\"ws-device-group\">\r\n                                    <button class=\"ws-dev-btn active\" data-device=\"desktop\" title=\"Desktop\" id=\"btn-dev-desk\"><i class=\"fas fa-desktop\"><\/i><\/button>\r\n                                    <button class=\"ws-dev-btn\" data-device=\"tablet\" title=\"Tablet\" id=\"btn-dev-tab\"><i class=\"fas fa-tablet-alt\"><\/i><\/button>\r\n                                    <button class=\"ws-dev-btn\" data-device=\"mobile\" title=\"Mobile\" id=\"btn-dev-mob\"><i class=\"fas fa-mobile-alt\"><\/i><\/button>\r\n                                <\/div>\r\n                                <button class=\"ws-pv-btn\" id=\"pv-fs\" title=\"App Fullscreen\"><i class=\"fas fa-expand\"><\/i><\/button>\r\n                            <\/div>\r\n                        <\/div>\r\n\r\n                        <div class=\"ws-wysiwyg-bar\" id=\"ws-wysiwyg-bar\">\r\n                            <button class=\"w-btn\" data-cmd=\"undo\" title=\"Undo\"><i class=\"fas fa-undo\"><\/i><\/button>\r\n                            <button class=\"w-btn\" data-cmd=\"redo\" title=\"Redo\"><i class=\"fas fa-redo\"><\/i><\/button>\r\n                            <div class=\"w-sep\"><\/div>\r\n                            \r\n                            <select class=\"w-select\" id=\"w-font-size\" title=\"Font Size\">\r\n                                <option value=\"1\">1 (Tiny)<\/option>\r\n                                <option value=\"2\">2 (Small)<\/option>\r\n                                <option value=\"3\" selected>3 (Normal)<\/option>\r\n                                <option value=\"4\">4 (Large)<\/option>\r\n                                <option value=\"5\">5 (Huge)<\/option>\r\n                                <option value=\"6\">6 (Jumbo)<\/option>\r\n                            <\/select>\r\n\r\n                            <button class=\"w-btn\" data-cmd=\"bold\" title=\"Bold (Ctrl+B)\"><i class=\"fas fa-bold\"><\/i><\/button>\r\n                            <button class=\"w-btn\" data-cmd=\"italic\" title=\"Italic (Ctrl+I)\"><i class=\"fas fa-italic\"><\/i><\/button>\r\n                            <button class=\"w-btn\" data-cmd=\"underline\" title=\"Underline (Ctrl+U)\"><i class=\"fas fa-underline\"><\/i><\/button>\r\n                            <button class=\"w-btn\" data-cmd=\"strikeThrough\" title=\"Strikethrough\"><i class=\"fas fa-strikethrough\"><\/i><\/button>\r\n                            \r\n                            <input type=\"color\" id=\"w-btn-color\" class=\"w-color\" title=\"Text Color\" value=\"#000000\">\r\n                            <input type=\"color\" id=\"w-btn-bg\" class=\"w-color\" title=\"Highlight Color\" value=\"#ffff00\">\r\n                            \r\n                            <div class=\"w-sep\"><\/div>\r\n                            <button class=\"w-btn\" data-cmd=\"formatBlock\" data-val=\"H1\" title=\"Heading 1\"><b>H1<\/b><\/button>\r\n                            <button class=\"w-btn\" data-cmd=\"formatBlock\" data-val=\"H2\" title=\"Heading 2\"><b>H2<\/b><\/button>\r\n                            <button class=\"w-btn\" data-cmd=\"formatBlock\" data-val=\"P\" title=\"Paragraph\"><i class=\"fas fa-paragraph\"><\/i><\/button>\r\n                            <div class=\"w-sep\"><\/div>\r\n                            <button class=\"w-btn\" data-cmd=\"insertUnorderedList\" title=\"Bullet List\"><i class=\"fas fa-list-ul\"><\/i><\/button>\r\n                            <button class=\"w-btn\" data-cmd=\"insertOrderedList\" title=\"Numbered List\"><i class=\"fas fa-list-ol\"><\/i><\/button>\r\n                            <div class=\"w-sep\"><\/div>\r\n                            <button class=\"w-btn\" data-cmd=\"justifyLeft\" title=\"Align Left\"><i class=\"fas fa-align-left\"><\/i><\/button>\r\n                            <button class=\"w-btn\" data-cmd=\"justifyCenter\" title=\"Align Center\"><i class=\"fas fa-align-center\"><\/i><\/button>\r\n                            <button class=\"w-btn\" data-cmd=\"justifyRight\" title=\"Align Right\"><i class=\"fas fa-align-right\"><\/i><\/button>\r\n                            <div class=\"w-sep\"><\/div>\r\n                            <button class=\"w-btn\" id=\"w-btn-link\" title=\"Insert Link\"><i class=\"fas fa-link\"><\/i><\/button>\r\n                            <button class=\"w-btn\" id=\"w-btn-unlink\" title=\"Remove Link\"><i class=\"fas fa-unlink\"><\/i><\/button>\r\n                            <button class=\"w-btn\" id=\"w-btn-img\" title=\"Insert Image\"><i class=\"fas fa-image\"><\/i><\/button>\r\n                            <button class=\"w-btn\" id=\"w-btn-table\" title=\"Insert Table\"><i class=\"fas fa-table\"><\/i><\/button>\r\n                            <button class=\"w-btn\" data-cmd=\"insertHorizontalRule\" title=\"Insert Line\"><i class=\"fas fa-minus\"><\/i><\/button>\r\n                            \r\n                            <div style=\"flex-grow: 1;\"><\/div>\r\n                            <button class=\"w-btn danger\" id=\"w-btn-clear\" title=\"Clear Visual Editor\"><i class=\"fas fa-trash-alt\"><\/i><\/button>\r\n                        <\/div>\r\n                        \r\n                        <div class=\"ws-prev-container\">\r\n                            <div id=\"ws-device-wrapper\" class=\"ws-device-wrapper device-desktop\">\r\n                                <iframe id=\"ws-preview-frame\" title=\"Live Preview\" sandbox=\"allow-scripts allow-modals allow-same-origin\"><\/iframe>\r\n                            <\/div>\r\n                        <\/div>\r\n\r\n                        <div id=\"ws-console-panel\" class=\"ws-console-panel\">\r\n                            <div class=\"ws-console-hdr\">\r\n                                <div><i class=\"fas fa-terminal\"><\/i> Console <span id=\"c-err-count\" style=\"background:#ef4444; color:#fff; padding:2px 6px; border-radius:4px; font-size:10px; margin-left:6px; display:none;\">0 Errors<\/span><\/div>\r\n                                <button id=\"btn-clear-console\" style=\"background:none;border:none;color:#f87171;cursor:pointer;font-weight:700;\"><i class=\"fas fa-eraser\"><\/i> Clear<\/button>\r\n                            <\/div>\r\n                            <div id=\"ws-console-output\"><\/div>\r\n                        <\/div>\r\n                    <\/div>\r\n                <\/div>\r\n\r\n                <div id=\"ws-statusbar\">\r\n                    <span class=\"sbs ok\" id=\"sb-save\"><i class=\"fas fa-check-circle\"><\/i> Project Ready<\/span>\r\n                    <span class=\"sbs-info\" id=\"sb-save-time\" style=\"margin-left: 10px;\">Last saved: Just now<\/span>\r\n                    <div class=\"sbs-right\">\r\n                        <span class=\"sbs-info\" id=\"sb-cursor\">Ln 1, Col 1<\/span>\r\n                        <span class=\"sbs-info\" id=\"sb-lang\" style=\"color:#38bdf8;\">HTML<\/span>\r\n                        <span class=\"sbs\" style=\"margin-left:8px;\">UTF-8<\/span>\r\n                    <\/div>\r\n                <\/div>\r\n\r\n                <input type=\"file\" id=\"ws-file-import\" accept=\".html,.css,.js,.txt\" style=\"display:none\">\r\n                \r\n                <div id=\"pth-ms-modal-overlay\">\r\n                    <div id=\"pth-ms-modal\">\r\n                        <h3 id=\"pth-ms-modal-title\">Confirm Action<\/h3>\r\n                        <p id=\"pth-ms-modal-msg\">Are you sure you want to proceed?<\/p>\r\n                        <input type=\"text\" id=\"pth-ms-modal-input\" class=\"pth-modal-input\" placeholder=\"...\">\r\n                        <div class=\"pth-ms-modal-btns\">\r\n                            <button id=\"pth-ms-modal-cancel\" class=\"btn-cancel\">Cancel<\/button>\r\n                            <button id=\"pth-ms-modal-ok\" class=\"btn-ok\">Confirm<\/button>\r\n                        <\/div>\r\n                    <\/div>\r\n                <\/div>\r\n\r\n                <div id=\"ws-toast\"><i class=\"fas fa-check-circle\"><\/i> <span id=\"ws-toast-msg\">Done<\/span><\/div>\r\n            <\/div>\r\n\r\n            <div class=\"pth-card\" style=\"margin-top: 25px;\">\r\n                <span class=\"pth-badge\" style=\"background:#eff6ff; color:#1d4ed8; padding:4px 10px; border-radius:20px; font-size:12px; font-weight:700; margin-bottom:10px; display:inline-block;\">\ud83d\ude80 Ultimate Web Studio Pro<\/span>\r\n                <h3 style=\"margin-top: 0; color: #0f172a; font-size: 20px; font-weight: 800;\">All-in-One Frontend IDE<\/h3>\r\n                <p style=\"font-size: 14px; color: #475569; line-height: 1.6;\">A professional, client-side IDE for frontend developers featuring a revolutionary Two-Way Sync system, Code Search, ZIP Export, and Real Device Simulators.<\/p>\r\n                <ul style=\"list-style: none; padding: 0; margin: 15px 0 0;\">\r\n                    <li style=\"padding: 6px 0; font-size: 14px; color: #475569;\"><i class=\"fas fa-check-circle\" style=\"color:#10b981; margin-right:6px;\"><\/i> <strong>Visual & Code Sync:<\/strong> Format visually; the HTML code updates instantly.<\/li>\r\n                    <li style=\"padding: 6px 0; font-size: 14px; color: #475569;\"><i class=\"fas fa-check-circle\" style=\"color:#10b981; margin-right:6px;\"><\/i> <strong>ZIP Export:<\/strong> Download HTML, CSS, and JS individually bundled in a ZIP.<\/li>\r\n                    <li style=\"padding: 6px 0; font-size: 14px; color: #475569;\"><i class=\"fas fa-check-circle\" style=\"color:#10b981; margin-right:6px;\"><\/i> <strong>Find & Replace:<\/strong> Advanced code searching (Ctrl+F) right in the browser.<\/li>\r\n                <\/ul>\r\n            <\/div>\r\n\r\n        <\/div>\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    window.pthWebStudioApp = {\r\n        initialized: false,\r\n        editors: {},\r\n        currentPreviewMode: 'desktop',\r\n        consoleOutput: [],\r\n        errorCount: 0,\r\n        currentFontSize: 15,\r\n        compileTimer: null,\r\n        isResizing: false,\r\n        editorFull: false,\r\n        isSyncing: false, \r\n        isTypingInVisual: false,\r\n        isInjectingToVisual: false,\r\n        searchState: { cursor: null, query: '', matches: 0, current: 0 },\r\n\r\n        samples: {\r\n            tailwind: {\r\n                html: '<div class=\"max-w-md mx-auto mt-12 bg-white p-8 rounded-2xl shadow-xl text-center border border-gray-100 transition-all hover:shadow-2xl\">\\n  <h2 class=\"text-3xl font-extrabold text-gray-800 mb-4 tracking-tight\">Visual Editing Magic!<\/h2>\\n  <p class=\"text-gray-500 mb-8 text-sm leading-relaxed\">You can select this text right here in the preview and click <strong>Bold<\/strong> on the toolbar above. Watch the HTML code update instantly!<\/p>\\n  <button id=\"btn-test\" class=\"bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-8 rounded-lg transition duration-200 shadow-lg shadow-blue-500\/30 w-full\">Click Me<\/button>\\n<\/div>',\r\n                css: '\/* Tailwind is activated! *\/\\nbody { background-color: #f1f5f9; }',\r\n                js: 'document.getElementById(\"btn-test\").addEventListener(\"click\", function() {\\n  alert(\"JS is working perfectly!\");\\n});'\r\n            },\r\n            standard: {\r\n                html: '<div class=\"demo-card\">\\n  <h2>Standard Web Project<\/h2>\\n  <p>Type here to see it update the code!<\/p>\\n  <button id=\"btn-demo\">Click Me<\/button>\\n<\/div>',\r\n                css: 'body {\\n  background-color: #e2e8f0;\\n  font-family: system-ui, sans-serif;\\n  display: flex;\\n  justify-content: center;\\n  padding-top: 4rem;\\n}\\n.demo-card {\\n  background: white;\\n  padding: 2.5rem;\\n  border-radius: 12px;\\n  box-shadow: 0 10px 25px rgba(0,0,0,0.1);\\n  text-align: center;\\n  max-width: 400px;\\n}\\n.demo-card h2 { color: #0f172a; margin-top: 0; font-size: 1.8rem; }\\n.demo-card p { color: #475569; margin-bottom: 2rem; line-height: 1.6; }\\nbutton {\\n  background: #3b82f6; color: white; border: none; padding: 12px 28px; border-radius: 8px; font-weight: bold; font-size: 1rem; cursor: pointer; transition: background 0.2s;\\n}\\nbutton:hover { background: #2563eb; }',\r\n                js: 'document.getElementById(\"btn-demo\").addEventListener(\"click\", function() {\\n  alert(\"Standard JavaScript logic is running!\");\\n});'\r\n            },\r\n            landing: {\r\n                html: '<header class=\"bg-indigo-600 text-white p-6\">\\n  <nav class=\"flex justify-between items-center max-w-6xl mx-auto\">\\n    <h2 class=\"text-2xl font-bold\">Brand.<\/h2>\\n    <ul class=\"flex space-x-6 font-semibold\">\\n      <li><a href=\"#\" class=\"hover:text-indigo-200\">Home<\/a><\/li>\\n      <li><a href=\"#\" class=\"hover:text-indigo-200\">Features<\/a><\/li>\\n      <li><a href=\"#\" class=\"bg-white text-indigo-600 px-4 py-2 rounded-full\">Sign Up<\/a><\/li>\\n    <\/ul>\\n  <\/nav>\\n<\/header>\\n<main class=\"text-center py-20 bg-gray-50\">\\n  <h2 class=\"text-5xl font-extrabold text-gray-900 mb-6\">Build Faster, Better.<\/h2>\\n  <p class=\"text-xl text-gray-600 mb-10 max-w-2xl mx-auto\">The ultimate toolkit for modern developers. Start your journey today and experience unparalleled speed.<\/p>\\n  <button class=\"bg-indigo-600 hover:bg-indigo-700 text-white font-bold py-4 px-10 rounded-lg shadow-lg\">Get Started Free<\/button>\\n<\/main>',\r\n                css: '\/* Tailwind handles the styling *\/\\nbody { margin: 0; font-family: sans-serif; }',\r\n                js: 'console.log(\"Landing page loaded.\");'\r\n            },\r\n            dashboard: {\r\n                html: '<div class=\"flex h-screen bg-gray-100\">\\n  <aside class=\"w-64 bg-gray-900 text-white flex flex-col\">\\n    <div class=\"p-6 text-2xl font-bold border-b border-gray-800\">Dashboard<\/div>\\n    <nav class=\"flex-1 p-4 space-y-2\">\\n      <a href=\"#\" class=\"block px-4 py-2 bg-gray-800 rounded\">Overview<\/a>\\n      <a href=\"#\" class=\"block px-4 py-2 hover:bg-gray-800 rounded\">Analytics<\/a>\\n      <a href=\"#\" class=\"block px-4 py-2 hover:bg-gray-800 rounded\">Settings<\/a>\\n    <\/nav>\\n  <\/aside>\\n  <main class=\"flex-1 p-8 overflow-y-auto\">\\n    <h2 class=\"text-3xl font-bold text-gray-800 mb-6\">Overview<\/h2>\\n    <div class=\"grid grid-cols-3 gap-6 mb-8\">\\n      <div class=\"bg-white p-6 rounded-lg shadow\"><h3 class=\"text-gray-500 text-sm\">Users<\/h3><p class=\"text-3xl font-bold\">12,450<\/p><\/div>\\n      <div class=\"bg-white p-6 rounded-lg shadow\"><h3 class=\"text-gray-500 text-sm\">Revenue<\/h3><p class=\"text-3xl font-bold text-green-500\">$34,200<\/p><\/div>\\n      <div class=\"bg-white p-6 rounded-lg shadow\"><h3 class=\"text-gray-500 text-sm\">Active Sessions<\/h3><p class=\"text-3xl font-bold text-blue-500\">892<\/p><\/div>\\n    <\/div>\\n  <\/main>\\n<\/div>',\r\n                css: '\/* Dashboard uses Tailwind *\/',\r\n                js: 'console.log(\"Dashboard ready.\");'\r\n            },\r\n            contact: {\r\n                html: '<div class=\"min-h-screen flex items-center justify-center bg-gray-50 py-12 px-4 sm:px-6 lg:px-8\">\\n  <div class=\"max-w-md w-full space-y-8 bg-white p-10 rounded-xl shadow-md\">\\n    <div>\\n      <h2 class=\"text-center text-3xl font-extrabold text-gray-900\">Contact Us<\/h2>\\n      <p class=\"mt-2 text-center text-sm text-gray-600\">We\\'d love to hear from you.<\/p>\\n    <\/div>\\n    <form class=\"mt-8 space-y-6\">\\n      <div class=\"rounded-md shadow-sm space-y-4\">\\n        <div>\\n          <input type=\"text\" required class=\"appearance-none rounded relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm\" placeholder=\"Your Name\">\\n        <\/div>\\n        <div>\\n          <input type=\"email\" required class=\"appearance-none rounded relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm\" placeholder=\"Email address\">\\n        <\/div>\\n        <div>\\n          <textarea required rows=\"4\" class=\"appearance-none rounded relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm\" placeholder=\"Your message...\"><\/textarea>\\n        <\/div>\\n      <\/div>\\n      <div>\\n        <button type=\"button\" id=\"btn-submit\" class=\"group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500\">\\n          Send Message\\n        <\/button>\\n      <\/div>\\n    <\/form>\\n  <\/div>\\n<\/div>',\r\n                css: '',\r\n                js: 'document.getElementById(\"btn-submit\").addEventListener(\"click\", function(){\\n  alert(\"Message sent successfully!\");\\n});'\r\n            }\r\n        },\r\n\r\n        snippets: {\r\n            html5: '<!DOCTYPE html>\\n<html lang=\"en\">\\n<head>\\n  <meta charset=\"UTF-8\">\\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\\n  <title>Document<\/title>\\n<\/head>\\n<body>\\n  \\n<\/body>\\n<\/html>',\r\n            seo: '<meta name=\"description\" content=\"A brief description of the page content.\">\\n<meta name=\"keywords\" content=\"HTML, CSS, JavaScript\">\\n<meta name=\"author\" content=\"John Doe\">',\r\n            grid: '.grid-container {\\n  display: grid;\\n  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));\\n  gap: 20px;\\n}\\n.grid-item {\\n  background: #f1f5f9;\\n  padding: 20px;\\n  border-radius: 8px;\\n}'\r\n        },\r\n        \r\n        \/\/ \ud83d\ude80 BULLETPROOF SAFE DOM FETCHER\r\n        el: function(id) { \r\n            return document.getElementById(id); \r\n        },\r\n\r\n        \/\/ \ud83d\ude80 SAFE EVENT BINDER\r\n        bind: function(id, evt, fn) {\r\n            var element = this.el(id);\r\n            if (element) { element.addEventListener(evt, fn); }\r\n        },\r\n\r\n        init: function() {\r\n            var self = this;\r\n            if (typeof CodeMirror === 'undefined') {\r\n                setTimeout(function() { self.init(); }, 200);\r\n                return;\r\n            }\r\n            try {\r\n                \/\/ \ud83d\udd25 BUG FIX: Move Modals & Toasts to document.body to prevent WP Theme CSS conflicts \ud83d\udd25\r\n                var modalOverlay = document.getElementById('pth-ms-modal-overlay');\r\n                if (modalOverlay) document.body.appendChild(modalOverlay);\r\n                \r\n                var toastMsg = document.getElementById('ws-toast');\r\n                if (toastMsg) document.body.appendChild(toastMsg);\r\n                \/\/ ----------------------------------------------------------------------------------\r\n\r\n                this.initEditors();\r\n                this.attachEvents();\r\n                this.loadLastProject();\r\n                \r\n                setTimeout(function() {\r\n                    if(self.editors.html) self.editors.html.refresh(); \r\n                    if(self.editors.css) self.editors.css.refresh(); \r\n                    if(self.editors.js) self.editors.js.refresh();\r\n                    self.updateLineBadges();\r\n                    self.compileWeb(true);\r\n                }, 300);\r\n            } catch (e) {\r\n                console.error(\"Studio Init Error:\", e);\r\n            }\r\n        },\r\n\r\n        initEditors: function() {\r\n            var self = this;\r\n            var conf = { theme: 'monokai', lineNumbers: true, lineWrapping: true, indentUnit: 4 };\r\n            \r\n            var tHtml = this.el('ws-html-code');\r\n            var tCss = this.el('ws-css-code');\r\n            var tJs = this.el('ws-js-code');\r\n            \r\n            if(tHtml) this.editors.html = CodeMirror.fromTextArea(tHtml, Object.assign({mode: 'htmlmixed'}, conf));\r\n            if(tCss) this.editors.css = CodeMirror.fromTextArea(tCss, Object.assign({mode: 'css'}, conf));\r\n            if(tJs) this.editors.js = CodeMirror.fromTextArea(tJs, Object.assign({mode: 'javascript'}, conf));\r\n\r\n            var triggerRun = function() {\r\n                self.updateLineBadges();\r\n                if (self.isSyncing) return; \r\n                var sb = self.el('sb-save');\r\n                if(sb) { sb.innerHTML = '<i class=\"fas fa-pen\"><\/i> Typing...'; sb.className = 'sbs warn'; }\r\n                \r\n                var chk = self.el('ws-auto-run');\r\n                if(chk && chk.checked) {\r\n                    clearTimeout(self.compileTimer);\r\n                    self.compileTimer = setTimeout(function(){ self.compileWeb(false); self.autoSaveSilent(); }, 600);\r\n                } else {\r\n                    clearTimeout(self.compileTimer);\r\n                    self.compileTimer = setTimeout(function(){ self.autoSaveSilent(); }, 600);\r\n                }\r\n            };\r\n\r\n            var triggerCursor = function() {\r\n                var ed = self.getActiveEditor();\r\n                if(!ed) return;\r\n                var cur = ed.getCursor();\r\n                var sc = self.el('sb-cursor');\r\n                if(sc) sc.textContent = 'Ln ' + (cur.line + 1) + ', Col ' + (cur.ch + 1);\r\n            };\r\n\r\n            if(this.editors.html) { this.editors.html.on('change', triggerRun); this.editors.html.on('cursorActivity', triggerCursor); }\r\n            if(this.editors.css) { this.editors.css.on('change', triggerRun); this.editors.css.on('cursorActivity', triggerCursor); }\r\n            if(this.editors.js) { this.editors.js.on('change', triggerRun); this.editors.js.on('cursorActivity', triggerCursor); }\r\n        },\r\n\r\n        updateLineBadges: function() {\r\n            var bh = this.el('bdg-html'); if(bh && this.editors.html) bh.textContent = this.editors.html.lineCount();\r\n            var bc = this.el('bdg-css');  if(bc && this.editors.css) bc.textContent = this.editors.css.lineCount();\r\n            var bj = this.el('bdg-js');   if(bj && this.editors.js) bj.textContent = this.editors.js.lineCount();\r\n        },\r\n\r\n        compileWeb: function(forceReload) {\r\n            var self = this;\r\n            if(!this.editors.html || !this.editors.css || !this.editors.js) return;\r\n            \r\n            var html = this.editors.html.getValue();\r\n            var css = this.editors.css.getValue();\r\n            var js = this.editors.js.getValue();\r\n            var toggle = this.el('ws-tw-toggle');\r\n            var useTailwind = toggle ? toggle.checked : false;\r\n            \r\n            var frame = this.el('ws-preview-frame');\r\n            if(!frame) return;\r\n            var doc = frame.contentDocument || frame.contentWindow.document;\r\n\r\n            if (forceReload || !doc.getElementById('pth-visual-root') || this.lastJs !== js || this.lastTw !== useTailwind) {\r\n                this.lastJs = js;\r\n                this.lastTw = useTailwind;\r\n                \r\n                var tailwindScript = useTailwind ? '<script src=\"https:\/\/cdn.tailwindcss.com\"><\\\/script>\\n' : '';\r\n                var interceptScript = '<script>\\n' +\r\n                    'window.console.log = function(...args){ window.parent.postMessage({type:\"pth-console\", level:\"log\", args:args}, \"*\"); };\\n' +\r\n                    'window.console.warn = function(...args){ window.parent.postMessage({type:\"pth-console\", level:\"warn\", args:args}, \"*\"); };\\n' +\r\n                    'window.console.error = function(...args){ window.parent.postMessage({type:\"pth-console\", level:\"error\", args:args}, \"*\"); };\\n' +\r\n                    'window.alert = function(msg){ window.parent.postMessage({type:\"pth-alert\", msg:msg}, \"*\"); };\\n' +\r\n                    'window.onerror = function(msg, url, line) { window.parent.postMessage({type:\"pth-console\", level:\"error\", args:[\"ERROR:\", msg, \"Line:\", line]}, \"*\"); return false; };\\n' +\r\n                    '<\\\/script>';\r\n\r\n                var combined = '<!DOCTYPE html><html><head><meta charset=\"UTF-8\">' + tailwindScript + '<style id=\"pth-dynamic-style\">' + css + '<\/style><\/head><body>' + \r\n                    '<div id=\"pth-visual-root\" contenteditable=\"true\" style=\"outline:none; min-height:100vh; width:100%; padding:2px;\">' + html + '<\/div>' + \r\n                    interceptScript + '<script>' + js + '<\\\/script><\/body><\/html>';\r\n                    \r\n                doc.open(); doc.write(combined); doc.close();\r\n                \r\n                setTimeout(function() {\r\n                    var root = doc.getElementById('pth-visual-root');\r\n                    if (root) {\r\n                        root.addEventListener('input', function() { if(self.isInjectingToVisual) return; self.syncFromVisual(); });\r\n                        root.addEventListener('keyup', function() { if(self.isInjectingToVisual) return; self.syncFromVisual(); });\r\n                        root.addEventListener('mouseup', function() { if(self.isInjectingToVisual) return; self.syncFromVisual(); });\r\n                    }\r\n                }, 100);\r\n                \r\n            } else {\r\n                if (!this.isTypingInVisual) {\r\n                    var root = doc.getElementById('pth-visual-root');\r\n                    if (root && root.innerHTML !== html) {\r\n                        this.isInjectingToVisual = true; \r\n                        root.innerHTML = html;\r\n                        setTimeout(function(){ self.isInjectingToVisual = false; }, 50); \r\n                    }\r\n                }\r\n                var styleTag = doc.getElementById('pth-dynamic-style');\r\n                if (styleTag && styleTag.innerHTML !== css) styleTag.innerHTML = css;\r\n            }\r\n\r\n            var sb = this.el('sb-save');\r\n            if(sb) { sb.innerHTML = '<i class=\"fas fa-check-circle\"><\/i> Project Ready'; sb.className = 'sbs ok'; }\r\n        },\r\n\r\n        syncFromVisual: function() {\r\n            this.isTypingInVisual = true;\r\n            var frame = this.el('ws-preview-frame');\r\n            if(!frame || !this.editors.html) return;\r\n            var doc = frame.contentDocument || frame.contentWindow.document;\r\n            var root = doc.getElementById('pth-visual-root');\r\n            \r\n            if (root) {\r\n                var currentCode = this.editors.html.getValue();\r\n                var newHtml = root.innerHTML;\r\n                \r\n                if (currentCode !== newHtml) {\r\n                    this.isSyncing = true;\r\n                    var cursorInfo = this.editors.html.getCursor();\r\n                    this.editors.html.setValue(newHtml);\r\n                    this.editors.html.setCursor(cursorInfo);\r\n                    this.autoSaveSilent();\r\n                    \r\n                    var self = this;\r\n                    setTimeout(function(){ self.isSyncing = false; }, 50);\r\n                }\r\n            }\r\n            clearTimeout(this.visualTypingTimer);\r\n            var self = this;\r\n            this.visualTypingTimer = setTimeout(function(){ self.isTypingInVisual = false; }, 1000);\r\n        },\r\n\r\n        promptDialog: function(title, msg, placeholder, confirmText, callback) {\r\n            var overlay = this.el('pth-ms-modal-overlay');\r\n            var okBtn = this.el('pth-ms-modal-ok');\r\n            var cancelBtn = this.el('pth-ms-modal-cancel');\r\n            var input = this.el('pth-ms-modal-input');\r\n            \r\n            if(!overlay) return;\r\n            \r\n            this.el('pth-ms-modal-title').textContent = title;\r\n            this.el('pth-ms-modal-msg').innerHTML = msg; \r\n            okBtn.textContent = confirmText;\r\n            \r\n            if(placeholder !== null) {\r\n                input.style.display = 'block';\r\n                input.placeholder = placeholder;\r\n                input.value = '';\r\n                setTimeout(function(){ input.focus(); }, 100);\r\n            } else {\r\n                input.style.display = 'none';\r\n            }\r\n            \r\n            overlay.classList.add('show');\r\n            \r\n            var cleanup = function() {\r\n                overlay.classList.remove('show');\r\n                okBtn.replaceWith(okBtn.cloneNode(true));\r\n                cancelBtn.replaceWith(cancelBtn.cloneNode(true));\r\n                input.replaceWith(input.cloneNode(true));\r\n            };\r\n            \r\n            this.el('pth-ms-modal-ok').addEventListener('click', function() { \r\n                var val = input.style.display === 'block' ? input.value : null;\r\n                cleanup(); \r\n                if(callback) callback(val); \r\n            });\r\n            this.el('pth-ms-modal-cancel').addEventListener('click', function() { cleanup(); });\r\n        },\r\n\r\n        confirmDialog: function(title, msg, confirmText, callback) {\r\n            this.promptDialog(title, msg, null, confirmText, callback);\r\n        },\r\n\r\n        toast: function(msg, type) {\r\n            var t = this.el('ws-toast');\r\n            if(!t) return;\r\n            t.className = 'show' + (type === 'error' ? ' err' : '');\r\n            this.el('ws-toast-msg').textContent = msg;\r\n            clearTimeout(t._tm);\r\n            t._tm = setTimeout(function() { t.className = ''; }, 3000);\r\n        },\r\n\r\n        closeAllMenus: function() {\r\n            var menus = document.querySelectorAll('.ws-menu-wrap');\r\n            for(var i=0; i<menus.length; i++) menus[i].classList.remove('active');\r\n        },\r\n\r\n        attachEvents: function() {\r\n            var self = this;\r\n\r\n            \/\/ \ud83d\ude80 FIXED: 'this.el' -> 'self.el' in the toast call (Javascript Scope Error)\r\n            this.bind('ws-tw-toggle', 'change', function(){ self.compileWeb(true); self.toast(self.el('ws-tw-toggle').checked ? 'Tailwind Activated' : 'Tailwind Deactivated'); });\r\n            this.bind('btn-run', 'click', function(){ self.compileWeb(true); });\r\n\r\n            this.bind('ws-proj-name', 'keydown', function(e){\r\n                if(e.key === 'Enter') { this.blur(); self.autoSaveSilent(); self.toast('Project Renamed'); }\r\n            });\r\n\r\n            var menuWraps = document.querySelectorAll('.ws-menu-wrap');\r\n            for(var i=0; i<menuWraps.length; i++) {\r\n                menuWraps[i].addEventListener('click', function(e) {\r\n                    e.stopPropagation();\r\n                    var isActive = this.classList.contains('active');\r\n                    self.closeAllMenus();\r\n                    if(!isActive) this.classList.add('active');\r\n                });\r\n            }\r\n            document.addEventListener('click', function(e) { \r\n                if(!e.target.closest('.ws-menu-wrap')) self.closeAllMenus(); \r\n            });\r\n\r\n            \/\/ FILE Menu\r\n            this.bind('mi-new', 'click', function(){\r\n                self.confirmDialog('New Project', 'Start a fresh project? Unsaved changes will be lost.', 'Start Fresh', function(){\r\n                    if(self.editors.html) self.editors.html.setValue(''); \r\n                    if(self.editors.css) self.editors.css.setValue(''); \r\n                    if(self.editors.js) self.editors.js.setValue('');\r\n                    self.compileWeb(true); self.toast('New project started');\r\n                });\r\n            });\r\n            this.bind('mi-load', 'click', function(){ self.promptLoadLast(); });\r\n            this.bind('mi-import', 'click', function(){ var el = self.el('ws-file-import'); if(el) el.click(); });\r\n            this.bind('mi-export', 'click', function(){ self.downloadProject(); });\r\n            this.bind('mi-export-zip', 'click', function(){ self.exportZIP(); });\r\n\r\n            \/\/ EDIT Menu\r\n            this.bind('mi-format', 'click', function(){ self.formatActiveCode(); });\r\n            this.bind('tb-format', 'click', function(){ self.formatActiveCode(); });\r\n            this.bind('mi-find', 'click', function(){ self.toggleFindReplace(); });\r\n            this.bind('mi-goto', 'click', function(){ self.promptGoToLine(); });\r\n            \r\n            \/\/ SNIPPETS\r\n            this.bind('mi-snip-html5', 'click', function(){ self.insertSnippet('html5'); });\r\n            this.bind('mi-snip-seo', 'click', function(){ self.insertSnippet('seo'); });\r\n            this.bind('mi-snip-grid', 'click', function(){ self.insertSnippet('grid'); });\r\n\r\n            \/\/ CLEAR LOGIC\r\n            this.bind('tb-clear', 'click', function(){ self.clearActive(); });\r\n            this.bind('w-btn-clear', 'click', function(){ \r\n                self.confirmDialog('Clear Visual Content?', 'This will clear all HTML content. Proceed?', 'Clear', function(){\r\n                    if(self.editors.html) self.editors.html.setValue(''); self.compileWeb(false); self.toast('HTML cleared');\r\n                });\r\n            });\r\n\r\n            \/\/ SAMPLES\r\n            var tplBtns = document.querySelectorAll('.tpl-btn');\r\n            for(var j=0; j<tplBtns.length; j++) {\r\n                tplBtns[j].addEventListener('click', function(){\r\n                    var key = this.getAttribute('data-tpl');\r\n                    if(self.samples[key]) {\r\n                        self.confirmDialog('Load Sample', 'Overwrite your current code with the ' + key + ' demo?', 'Load', function(){\r\n                            var tw = self.el('ws-tw-toggle');\r\n                            if(tw) {\r\n                                if(key === 'tailwind' || key === 'landing' || key === 'dashboard' || key === 'contact') tw.checked = true;\r\n                                else tw.checked = false;\r\n                            }\r\n                            if(self.editors.html) self.editors.html.setValue(self.samples[key].html);\r\n                            if(self.editors.css) self.editors.css.setValue(self.samples[key].css);\r\n                            if(self.editors.js) self.editors.js.setValue(self.samples[key].js);\r\n                            self.compileWeb(true); self.toast(key + ' demo loaded');\r\n                        });\r\n                    }\r\n                });\r\n            }\r\n\r\n            \/\/ VIEW Menu\r\n            this.bind('mi-console', 'click', function(){ self.toggleConsole(); });\r\n            this.bind('tb-console', 'click', function(){ self.toggleConsole(); });\r\n            this.bind('mi-editor-fs', 'click', function(){ self.toggleEditorFull(); });\r\n            this.bind('mi-app-fs', 'click', function(){ self.toggleAppFS(); });\r\n            this.bind('pv-fs', 'click', function(){ self.toggleAppFS(); });\r\n            this.bind('mi-zoom-in', 'click', function(){ self.changeZoom(1); });\r\n            this.bind('mi-zoom-out', 'click', function(){ self.changeZoom(-1); });\r\n            this.bind('btn-clear-console', 'click', function(){ self.clearConsole(); });\r\n\r\n            this.bind('ws-file-import', 'change', function(e){ self.handleImport(e); });\r\n\r\n            \/\/ WYSIWYG\r\n            var wBtns = document.querySelectorAll('.w-btn');\r\n            for(var b=0; b<wBtns.length; b++) {\r\n                var btn = wBtns[b];\r\n                var cmd = btn.getAttribute('data-cmd');\r\n                if(cmd) {\r\n                    btn.addEventListener('click', function(e) {\r\n                        e.preventDefault();\r\n                        var command = this.getAttribute('data-cmd');\r\n                        var val = this.getAttribute('data-val') || null;\r\n                        \r\n                        var frame = self.el('ws-preview-frame');\r\n                        if(!frame) return;\r\n                        var doc = frame.contentDocument || frame.contentWindow.document;\r\n                        var root = doc.getElementById('pth-visual-root');\r\n                        \r\n                        if (root && doc.activeElement !== root) root.focus();\r\n                        doc.execCommand(command, false, val);\r\n                        self.syncFromVisual();\r\n                    });\r\n                }\r\n            }\r\n\r\n            this.bind('w-btn-link', 'click', function(e) {\r\n                e.preventDefault(); var url = prompt(\"Enter link URL:\", \"https:\/\/\");\r\n                if (url) { var doc = self.el('ws-preview-frame').contentDocument; doc.execCommand('createLink', false, url); self.syncFromVisual(); }\r\n            });\r\n            this.bind('w-btn-unlink', 'click', function(e) {\r\n                e.preventDefault(); var doc = self.el('ws-preview-frame').contentDocument; doc.execCommand('unlink', false, null); self.syncFromVisual();\r\n            });\r\n            this.bind('w-btn-img', 'click', function(e) {\r\n                e.preventDefault(); var url = prompt(\"Enter image URL:\", \"https:\/\/placehold.co\/400x300\");\r\n                if (url) { var doc = self.el('ws-preview-frame').contentDocument; doc.execCommand('insertImage', false, url); self.syncFromVisual(); }\r\n            });\r\n            this.bind('w-btn-table', 'click', function(e) {\r\n                e.preventDefault();\r\n                var tableHTML = '<table border=\"1\" style=\"width:100%; border-collapse:collapse; margin-bottom:1em;\"><tbody><tr><td style=\"padding:8px;\">Cell 1<\/td><td style=\"padding:8px;\">Cell 2<\/td><\/tr><tr><td style=\"padding:8px;\">Cell 3<\/td><td style=\"padding:8px;\">Cell 4<\/td><\/tr><\/tbody><\/table><br>';\r\n                var doc = self.el('ws-preview-frame').contentDocument;\r\n                doc.execCommand('insertHTML', false, tableHTML); self.syncFromVisual();\r\n            });\r\n            this.bind('w-btn-color', 'input', function(e) {\r\n                var doc = self.el('ws-preview-frame').contentDocument; doc.execCommand('foreColor', false, e.target.value); self.syncFromVisual();\r\n            });\r\n            this.bind('w-btn-bg', 'input', function(e) {\r\n                var doc = self.el('ws-preview-frame').contentDocument; doc.execCommand('hiliteColor', false, e.target.value); self.syncFromVisual();\r\n            });\r\n            this.bind('w-font-size', 'change', function(e) {\r\n                var doc = self.el('ws-preview-frame').contentDocument; doc.execCommand('fontSize', false, e.target.value); self.syncFromVisual();\r\n            });\r\n\r\n            \/\/ FIND REPLACE\r\n            this.bind('fr-close', 'click', function(){ var fr = self.el('ws-fr-bar'); if(fr) fr.classList.remove('show'); });\r\n            this.bind('fr-find', 'input', function(){ self.doSearch(); });\r\n            this.bind('fr-prev', 'click', function(){ self.doSearchNav(-1); });\r\n            this.bind('fr-next', 'click', function(){ self.doSearchNav(1); });\r\n            this.bind('fr-rep-btn', 'click', function(){ self.doReplace(); });\r\n            this.bind('fr-repall-btn', 'click', function(){ self.doReplaceAll(); });\r\n\r\n            \/\/ TABS\r\n            var tabs = document.querySelectorAll('.ws-tab');\r\n            for(var t=0; t<tabs.length; t++) {\r\n                tabs[t].addEventListener('click', function() {\r\n                    var allTabs = document.querySelectorAll('.ws-tab');\r\n                    for(var i=0; i<allTabs.length; i++) allTabs[i].classList.remove('active');\r\n                    \r\n                    var views = document.querySelectorAll('.ws-code-view');\r\n                    for(var j=0; j<views.length; j++) views[j].classList.remove('active');\r\n                    \r\n                    this.classList.add('active');\r\n                    \r\n                    var targetBox = self.el(this.getAttribute('data-target'));\r\n                    if(targetBox) targetBox.classList.add('active');\r\n                    \r\n                    var lang = this.classList.contains('html') ? 'HTML' : this.classList.contains('css') ? 'CSS' : 'JS';\r\n                    var sbLang = self.el('sb-lang');\r\n                    if(sbLang) sbLang.textContent = lang;\r\n                    \r\n                    setTimeout(function(){ \r\n                        if(self.editors.html) self.editors.html.refresh(); \r\n                        if(self.editors.css) self.editors.css.refresh(); \r\n                        if(self.editors.js) self.editors.js.refresh(); \r\n                        var frBar = self.el('ws-fr-bar');\r\n                        if(frBar && frBar.classList.contains('show')) self.doSearch(); \r\n                    }, 50);\r\n                });\r\n            }\r\n\r\n            \/\/ DEVICES\r\n            var devBtns = document.querySelectorAll('.ws-dev-btn');\r\n            for(var d=0; d<devBtns.length; d++) {\r\n                devBtns[d].addEventListener('click', function(){ \r\n                    self.setPreviewMode(this.getAttribute('data-device')); \r\n                });\r\n            }\r\n\r\n            \/\/ RESIZER\r\n            var resizer = this.el('ws-resizer');\r\n            if(resizer) {\r\n                resizer.addEventListener('mousedown', function(e){ e.preventDefault(); self.isResizing = true; resizer.classList.add('active'); document.body.style.cursor = window.innerWidth <= 900 ? 'row-resize' : 'col-resize'; });\r\n                document.addEventListener('mousemove', function(e){\r\n                    if (!self.isResizing) return;\r\n                    var rect = self.el('ws-body').getBoundingClientRect();\r\n                    if (window.innerWidth > 900) {\r\n                        var perc = ((e.clientX - rect.left) \/ rect.width) * 100;\r\n                        perc = Math.max(20, Math.min(80, perc));\r\n                        var wsL = self.el('ws-L'); if(wsL) wsL.style.flexBasis = perc + '%';\r\n                        var wsR = self.el('ws-R'); if(wsR) wsR.style.flexBasis = (100 - perc) + '%';\r\n                    } else {\r\n                        var percy = ((e.clientY - rect.top) \/ rect.height) * 100;\r\n                        percy = Math.max(20, Math.min(80, percy));\r\n                        var wsL2 = self.el('ws-L'); if(wsL2) wsL2.style.flexBasis = percy + '%';\r\n                        var wsR2 = self.el('ws-R'); if(wsR2) wsR2.style.flexBasis = (100 - percy) + '%';\r\n                    }\r\n                    e.preventDefault();\r\n                });\r\n                document.addEventListener('mouseup', function(){ if(self.isResizing){ self.isResizing = false; resizer.classList.remove('active'); document.body.style.cursor = ''; if(self.editors.html) self.editors.html.refresh(); } });\r\n            }\r\n\r\n            \/\/ HOTKEYS\r\n            document.addEventListener('keydown', function(e){\r\n                if(e.ctrlKey && e.key === 's' && !e.shiftKey) { e.preventDefault(); self.downloadProject(); }\r\n                if(e.ctrlKey && e.key === 'Enter') { e.preventDefault(); self.compileWeb(true); self.toast('Preview Refreshed'); }\r\n                if(e.ctrlKey && (e.key === 'f' || e.key === 'F')) { e.preventDefault(); self.toggleFindReplace(); }\r\n                if(e.ctrlKey && (e.key === 'g' || e.key === 'G')) { e.preventDefault(); self.promptGoToLine(); }\r\n                if(e.ctrlKey && e.shiftKey && (e.key === 'E' || e.key === 'e')) { e.preventDefault(); self.toggleEditorFull(); }\r\n                if(e.shiftKey && e.altKey && (e.key === 'F' || e.key === 'f')) { e.preventDefault(); self.formatActiveCode(); }\r\n                if(e.key === 'Escape') { \r\n                    self.closeAllMenus(); \r\n                    var fr = self.el('ws-fr-bar'); if(fr) fr.classList.remove('show'); \r\n                }\r\n            });\r\n\r\n            \/\/ CONSOLE & ALERTS\r\n            window.addEventListener('message', function(e) {\r\n                if (e.data && e.data.type === 'pth-console') {\r\n                    var lvl = e.data.level || 'log';\r\n                    var cls = lvl === 'warn' ? 'color:#f59e0b; background:rgba(245,158,11,0.1); padding:2px 4px; border-left:2px solid #f59e0b; margin-bottom:4px;' \r\n                            : lvl === 'error' ? 'color:#ef4444; background:rgba(239,68,68,0.1); padding:2px 4px; border-left:2px solid #ef4444; margin-bottom:4px;' \r\n                            : 'color:#f1f5f9; padding-bottom:2px; border-bottom:1px solid #334155; margin-bottom:4px;';\r\n                    var icon = lvl === 'warn' ? '\u26a0\ufe0f ' : lvl === 'error' ? '\u274c ' : '> ';\r\n                    \r\n                    self.consoleOutput.push('<div style=\"' + cls + '\">' + icon + e.data.args.join(' ') + '<\/div>');\r\n                    if (self.consoleOutput.length > 50) self.consoleOutput.shift();\r\n                    var cout = self.el('ws-console-output');\r\n                    if(cout) cout.innerHTML = self.consoleOutput.join('');\r\n                    \r\n                    if(lvl === 'error') {\r\n                        self.errorCount++;\r\n                        var errBadge = self.el('c-err-count');\r\n                        if(errBadge) {\r\n                            errBadge.style.display = 'inline-block';\r\n                            errBadge.textContent = self.errorCount + ' Errors';\r\n                        }\r\n                    }\r\n                } else if (e.data && e.data.type === 'pth-alert') {\r\n                    self.confirmDialog(\"Live Preview Alert\", \"<div style='background:#f1f5f9; color:#0f172a; padding:10px; border-radius:6px; border-left:4px solid #3b82f6;'>\" + e.data.msg + \"<\/div>\", \"OK\", null);\r\n                }\r\n            });\r\n        },\r\n\r\n        getActiveEditor: function() {\r\n            var activeEl = document.querySelector('.ws-tab.active');\r\n            var activeTarget = activeEl ? activeEl.getAttribute('data-target') : 'ws-html-box';\r\n            if (activeTarget === 'ws-html-box') return this.editors.html;\r\n            if (activeTarget === 'ws-css-box') return this.editors.css;\r\n            return this.editors.js;\r\n        },\r\n\r\n        insertSnippet: function(type) {\r\n            var ed = this.getActiveEditor();\r\n            var code = this.snippets[type];\r\n            if(ed && code) {\r\n                ed.replaceSelection(code);\r\n                this.toast('Snippet inserted');\r\n            }\r\n        },\r\n\r\n        toggleFindReplace: function() {\r\n            var ed = this.getActiveEditor();\r\n            if (!ed || typeof ed.getSearchCursor === 'undefined') return this.toast('Search module loading...', 'error');\r\n            var bar = this.el('ws-fr-bar');\r\n            if(!bar) return;\r\n            bar.classList.toggle('show');\r\n            if(bar.classList.contains('show')) {\r\n                var inp = this.el('fr-find');\r\n                if(inp) { inp.focus(); this.doSearch(); }\r\n            } else {\r\n                if(this.searchState.cursor) this.searchState.cursor = null;\r\n            }\r\n        },\r\n\r\n        doSearch: function() {\r\n            var findInp = this.el('fr-find');\r\n            if(!findInp) return;\r\n            var query = findInp.value;\r\n            var ed = this.getActiveEditor();\r\n            if(!query || !ed || !ed.getSearchCursor) {\r\n                var c1 = this.el('fr-count'); if(c1) c1.textContent = '0\/0';\r\n                return;\r\n            }\r\n            this.searchState.cursor = ed.getSearchCursor(query);\r\n            var count = 0;\r\n            while(this.searchState.cursor.findNext()) { count++; }\r\n            this.searchState.matches = count;\r\n            this.searchState.current = count > 0 ? 1 : 0;\r\n            var c2 = this.el('fr-count'); if(c2) c2.textContent = this.searchState.current + '\/' + count;\r\n            \r\n            this.searchState.cursor = ed.getSearchCursor(query);\r\n            if(this.searchState.cursor.findNext()) {\r\n                ed.setSelection(this.searchState.cursor.from(), this.searchState.cursor.to());\r\n                ed.scrollIntoView(this.searchState.cursor.from());\r\n            }\r\n        },\r\n\r\n        doSearchNav: function(dir) {\r\n            var ed = this.getActiveEditor();\r\n            if(!ed || !this.searchState.cursor) return;\r\n            var found = dir > 0 ? this.searchState.cursor.findNext() : this.searchState.cursor.findPrevious();\r\n            if(!found) {\r\n                var fInp = this.el('fr-find');\r\n                if(!fInp) return;\r\n                var q = fInp.value;\r\n                this.searchState.cursor = ed.getSearchCursor(q, dir > 0 ? {line:0, ch:0} : {line:ed.lineCount(), ch:0});\r\n                found = dir > 0 ? this.searchState.cursor.findNext() : this.searchState.cursor.findPrevious();\r\n            }\r\n            if(found) {\r\n                ed.setSelection(this.searchState.cursor.from(), this.searchState.cursor.to());\r\n                ed.scrollIntoView(this.searchState.cursor.from());\r\n            }\r\n        },\r\n\r\n        doReplace: function() {\r\n            if(!this.searchState.cursor) return;\r\n            var repInp = this.el('fr-replace');\r\n            if(!repInp) return;\r\n            var rep = repInp.value;\r\n            this.searchState.cursor.replace(rep);\r\n            this.doSearchNav(1);\r\n        },\r\n\r\n        doReplaceAll: function() {\r\n            var ed = this.getActiveEditor();\r\n            var fInp = this.el('fr-find');\r\n            var repInp = this.el('fr-replace');\r\n            if(!fInp || !repInp || !ed) return;\r\n            var q = fInp.value;\r\n            var rep = repInp.value;\r\n            if(!q || !ed.getSearchCursor) return;\r\n            var cursor = ed.getSearchCursor(q);\r\n            var count = 0;\r\n            while(cursor.findNext()) {\r\n                cursor.replace(rep);\r\n                count++;\r\n            }\r\n            this.toast(count + ' replacements made');\r\n            this.doSearch();\r\n        },\r\n\r\n        promptGoToLine: function() {\r\n            var self = this;\r\n            this.promptDialog('Go to Line', 'Enter line number:', 'e.g. 42', 'Go', function(val){\r\n                var line = parseInt(val, 10);\r\n                var ed = self.getActiveEditor();\r\n                if (!isNaN(line) && ed) {\r\n                    ed.setCursor({line: line - 1, ch: 0});\r\n                    ed.focus();\r\n                }\r\n            });\r\n        },\r\n\r\n        setPreviewMode: function(mode) {\r\n            this.currentPreviewMode = mode;\r\n            var devBtns = document.querySelectorAll('.ws-dev-btn');\r\n            for(var i=0; i<devBtns.length; i++) devBtns[i].classList.remove('active');\r\n            \r\n            var activeBtn = document.querySelector('.ws-dev-btn[data-device=\"'+mode+'\"]');\r\n            if(activeBtn) activeBtn.classList.add('active');\r\n            \r\n            var wrapper = this.el('ws-device-wrapper');\r\n            if(wrapper) wrapper.className = 'ws-device-wrapper device-' + mode;\r\n        },\r\n\r\n        toggleConsole: function() {\r\n            var panel = this.el('ws-console-panel');\r\n            if(!panel) return;\r\n            panel.classList.toggle('show');\r\n            if (panel.classList.contains('show')) this.compileWeb(true);\r\n        },\r\n\r\n        clearConsole: function() {\r\n            this.consoleOutput = []; \r\n            this.errorCount = 0;\r\n            var errB = this.el('c-err-count');\r\n            if(errB) errB.style.display = 'none';\r\n            var cout = this.el('ws-console-output');\r\n            if(cout) cout.innerHTML = '<div style=\"color:#f1f5f9; padding-bottom:2px; border-bottom:1px solid #334155; margin-bottom:4px;\"><span style=\"color:#64748b;\">Console cleared \u2713<\/span><\/div>';\r\n        },\r\n\r\n        formatActiveCode: function() {\r\n            var editor = this.getActiveEditor();\r\n            if(!editor) return;\r\n            var totalLines = editor.lineCount();\r\n            for (var i = 0; i < totalLines; i++) { editor.indentLine(i); }\r\n            this.toast('Code formatted!');\r\n        },\r\n\r\n        changeZoom: function(step) {\r\n            this.currentFontSize = Math.max(12, Math.min(26, this.currentFontSize + step));\r\n            var cms = document.querySelectorAll('.CodeMirror');\r\n            for(var i=0; i<cms.length; i++) {\r\n                cms[i].style.fontSize = this.currentFontSize + 'px';\r\n            }\r\n            if(this.editors.html) this.editors.html.refresh(); \r\n            if(this.editors.css) this.editors.css.refresh(); \r\n            if(this.editors.js) this.editors.js.refresh();\r\n            \r\n            var badge = this.el('ws-zoom-badge');\r\n            if(badge) {\r\n                var perc = Math.round((this.currentFontSize \/ 15) * 100);\r\n                badge.textContent = perc + '%';\r\n                badge.style.display = 'block';\r\n                clearTimeout(this.zoomTimer);\r\n                var self = this;\r\n                this.zoomTimer = setTimeout(function(){ badge.style.display = 'none'; }, 1500);\r\n            }\r\n        },\r\n\r\n        handleImport: function(e) {\r\n            var file = e.target.files[0]; if (!file) return;\r\n            var reader = new FileReader();\r\n            var self = this;\r\n            reader.onload = function(event) { \r\n                var ed = self.getActiveEditor();\r\n                if(ed) {\r\n                    ed.setValue(event.target.result); \r\n                    self.compileWeb(true); \r\n                    self.toast('File imported!'); \r\n                }\r\n            };\r\n            reader.readAsText(file); e.target.value = ''; \r\n        },\r\n\r\n        clearActive: function() {\r\n            var self = this;\r\n            this.confirmDialog(\"Clear Active Tab?\", \"This will delete all code in the currently open tab. Cannot be undone.\", \"Clear Tab\", function(){\r\n                var editor = self.getActiveEditor(); \r\n                if(editor) {\r\n                    editor.setValue(''); editor.clearHistory(); self.compileWeb(false); self.toast('Active tab cleared');\r\n                }\r\n            });\r\n        },\r\n\r\n        toggleEditorFull: function() {\r\n            this.editorFull = !this.editorFull;\r\n            var app = this.el('ws-app');\r\n            if(app) app.classList.toggle('editor-full', this.editorFull);\r\n            var self = this;\r\n            setTimeout(function(){ \r\n                if(self.editors.html) self.editors.html.refresh(); \r\n                if(self.editors.css) self.editors.css.refresh(); \r\n                if(self.editors.js) self.editors.js.refresh(); \r\n            }, 300);\r\n            this.toast(this.editorFull ? 'Editor expanded to full width' : 'Split view restored');\r\n        },\r\n\r\n        toggleAppFS: function() {\r\n            var app = this.el('ws-app');\r\n            if(!app) return;\r\n            app.classList.toggle('code-fullscreen');\r\n            var btn = this.el('pv-fs');\r\n            if (app.classList.contains('code-fullscreen')) { \r\n                if(btn) btn.innerHTML = '<i class=\"fas fa-compress\"><\/i>'; \r\n                document.body.style.overflow = 'hidden';\r\n            } else { \r\n                if(btn) btn.innerHTML = '<i class=\"fas fa-expand\"><\/i>'; \r\n                document.body.style.overflow = '';\r\n            }\r\n            var self = this;\r\n            setTimeout(function(){ \r\n                if(self.editors.html) self.editors.html.refresh(); \r\n                if(self.editors.css) self.editors.css.refresh(); \r\n                if(self.editors.js) self.editors.js.refresh(); \r\n            }, 300);\r\n        },\r\n\r\n        autoSaveSilent: function() {\r\n            try {\r\n                var projName = this.el('ws-proj-name');\r\n                var toggle = this.el('ws-tw-toggle');\r\n                \r\n                var hVal = this.editors.html ? this.editors.html.getValue() : '';\r\n                var cVal = this.editors.css ? this.editors.css.getValue() : '';\r\n                var jVal = this.editors.js ? this.editors.js.getValue() : '';\r\n                \r\n                var data = { \r\n                    name: projName ? projName.value : 'Project', \r\n                    html: hVal, \r\n                    css: cVal, \r\n                    js: jVal,\r\n                    tailwind: toggle ? toggle.checked : false\r\n                };\r\n                localStorage.setItem('pth_webstudio_save', JSON.stringify(data));\r\n                \r\n                var now = new Date();\r\n                var timeString = now.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'});\r\n                var st = this.el('sb-save-time');\r\n                if(st) st.textContent = 'Last saved: ' + timeString;\r\n            } catch(e) {}\r\n        },\r\n\r\n        promptLoadLast: function() {\r\n            var saved = localStorage.getItem('pth_webstudio_save');\r\n            var self = this;\r\n            if (saved) {\r\n                this.confirmDialog(\"Load Saved Project?\", \"Overwrite current workspace with last auto-save?\", \"Restore Project\", function(){\r\n                    var data = JSON.parse(saved); \r\n                    var pn = self.el('ws-proj-name'); if(pn) pn.value = data.name || 'My Project';\r\n                    var tw = self.el('ws-tw-toggle'); if(tw && data.tailwind !== undefined) tw.checked = data.tailwind;\r\n                    \r\n                    if(self.editors.html) self.editors.html.setValue(data.html || ''); \r\n                    if(self.editors.css) self.editors.css.setValue(data.css || ''); \r\n                    if(self.editors.js) self.editors.js.setValue(data.js || ''); \r\n                    \r\n                    self.compileWeb(true); self.toast('Project Restored');\r\n                });\r\n            } else { this.toast('No saved project found', 'error'); }\r\n        },\r\n\r\n        loadLastProject: function() {\r\n            try {\r\n                var saved = JSON.parse(localStorage.getItem('pth_webstudio_save'));\r\n                var pn = this.el('ws-proj-name');\r\n                var tw = this.el('ws-tw-toggle');\r\n                \r\n                if(saved && (saved.html || saved.css || saved.js)) {\r\n                    if(pn) pn.value = saved.name || 'My Project';\r\n                    if (tw && saved.tailwind !== undefined) tw.checked = saved.tailwind;\r\n                    if(this.editors.html) this.editors.html.setValue(saved.html || '');\r\n                    if(this.editors.css) this.editors.css.setValue(saved.css || '');\r\n                    if(this.editors.js) this.editors.js.setValue(saved.js || '');\r\n                } else {\r\n                    if(tw) tw.checked = true;\r\n                    if(this.editors.html) this.editors.html.setValue(this.samples.tailwind.html);\r\n                    if(this.editors.css) this.editors.css.setValue(this.samples.tailwind.css);\r\n                    if(this.editors.js) this.editors.js.setValue(this.samples.tailwind.js);\r\n                }\r\n            } catch(e) {\r\n                var tw2 = this.el('ws-tw-toggle');\r\n                if(tw2) tw2.checked = true;\r\n                if(this.editors.html) this.editors.html.setValue(this.samples.tailwind.html);\r\n            }\r\n        },\r\n\r\n        downloadProject: function() {\r\n            if(!this.editors.html || !this.editors.css || !this.editors.js) return;\r\n            var html = this.editors.html.getValue(); var css = this.editors.css.getValue(); var js = this.editors.js.getValue();\r\n            var tw = this.el('ws-tw-toggle');\r\n            var tailwindScript = (tw && tw.checked) ? '\\n<script src=\"https:\/\/cdn.tailwindcss.com\"><\\\/script>' : '';\r\n            var pn = this.el('ws-proj-name');\r\n            var projName = pn ? pn.value : 'Project';\r\n            \r\n            var full = '<!DOCTYPE html>\\n<html lang=\"en\">\\n<head>\\n<meta charset=\"UTF-8\">\\n<title>' + projName + '<\/title>' + tailwindScript + '\\n<style>\\n' + css + '\\n<\/style>\\n<\/head>\\n<body>\\n' + html + '\\n<script>\\n' + js + '\\n<\\\/script>\\n<\/body>\\n<\/html>';\r\n            var blob = new Blob([full], {type: 'text\/html'}); var url = URL.createObjectURL(blob);\r\n            var a = document.createElement('a'); a.href = url; a.download = (projName || 'project').replace(\/\\s\/g,'-').toLowerCase() + '.html';\r\n            document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); \r\n            this.toast('Project Exported as HTML');\r\n        },\r\n        \r\n        exportZIP: function() {\r\n            if (typeof JSZip === 'undefined') {\r\n                return this.toast('JSZip library not loaded yet', 'error');\r\n            }\r\n            if(!this.editors.html || !this.editors.css || !this.editors.js) return;\r\n            \r\n            var zip = new JSZip();\r\n            var tw = this.el('ws-tw-toggle');\r\n            var tailwindScript = (tw && tw.checked) ? '\\n  <script src=\"https:\/\/cdn.tailwindcss.com\"><\\\/script>' : '';\r\n            var pn = this.el('ws-proj-name');\r\n            var projNameStr = pn ? pn.value : 'Project';\r\n            \r\n            var htmlContent = '<!DOCTYPE html>\\n<html lang=\"en\">\\n<head>\\n  <meta charset=\"UTF-8\">\\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\\n  <title>' + projNameStr + '<\/title>' + tailwindScript + '\\n  <link rel=\"stylesheet\" href=\"style.css\">\\n<\/head>\\n<body>\\n' + this.editors.html.getValue() + '\\n  <script src=\"script.js\"><\\\/script>\\n<\/body>\\n<\/html>';\r\n            \r\n            zip.file(\"index.html\", htmlContent);\r\n            zip.file(\"style.css\", this.editors.css.getValue());\r\n            zip.file(\"script.js\", this.editors.js.getValue());\r\n            \r\n            var projName = (projNameStr || 'project').replace(\/\\s\/g,'-').toLowerCase();\r\n            var self = this;\r\n            \r\n            zip.generateAsync({type:\"blob\"}).then(function(content) {\r\n                var a = document.createElement(\"a\");\r\n                a.href = URL.createObjectURL(content);\r\n                a.download = projName + \".zip\";\r\n                document.body.appendChild(a);\r\n                a.click();\r\n                document.body.removeChild(a);\r\n                self.toast('Exported as ZIP! \ud83d\udce6');\r\n            });\r\n        }\r\n    };\r\n\r\n    var loaderPoller = setInterval(function() {\r\n        if (window.CodeMirror && document.getElementById('pth-webstudio-wrapper')) {\r\n            clearInterval(loaderPoller);\r\n            if (!window.pthWebStudioApp.initialized) {\r\n                window.pthWebStudioApp.initialized = true;\r\n                window.pthWebStudioApp.init();\r\n            }\r\n        }\r\n    }, 100);\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 Client-Side DOM Compilation<\/div>\n        <p class=\"pth-feat-text\">This Offline HTML Editor parses markup strings and executable logic directly into a sandboxed Document Object Model within your local browser memory.<\/p>\n    <\/div>\n    <div class=\"pth-feat-box\">\n        <div class=\"pth-feat-title\">\ud83d\udd35 Responsive Viewport Emulation<\/div>\n        <p class=\"pth-feat-text\">The engine calculates strict geometric CSS boundaries to simulate desktop, tablet, and mobile hardware rendering matrices dynamically.<\/p>\n    <\/div>\n    <div class=\"pth-feat-box\">\n        <div class=\"pth-feat-title\">\ud83d\udfe3 Synchronous Terminal Capture<\/div>\n        <p class=\"pth-feat-text\">Programmatic execution outputs are intercepted via an internal listener, routing script events directly to the integrated developer console.<\/p>\n    <\/div>\n<\/div>\n<div class=\"pth-steps-wrap\">\n    <div class=\"pth-steps-title\">How to Compile Layouts with the Offline HTML Editor<\/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\">Inject Syntax Buffers<\/div>\n            <p class=\"pth-step-desc\">Enter your structural markup, cascading styles, and executable scripts into their respective localized editor panes.<\/p>\n        <\/div>\n        <div class=\"pth-step-item\">\n            <div class=\"pth-step-num\">2<\/div>\n            <div class=\"pth-step-name\">Define Output Geometry<\/div>\n            <p class=\"pth-step-desc\">Select the target hardware emulation mode to constrain the iframe boundaries to exact mobile or desktop dimensions.<\/p>\n        <\/div>\n        <div class=\"pth-step-item\">\n            <div class=\"pth-step-num\">3<\/div>\n            <div class=\"pth-step-name\">Trigger Compilation<\/div>\n            <p class=\"pth-step-desc\">Execute the local renderer to paint the Document Object Model synchronously onto the visualization canvas.<\/p>\n        <\/div>\n        <div class=\"pth-step-item\">\n            <div class=\"pth-step-num\">4<\/div>\n            <div class=\"pth-step-name\">Extract Project Files<\/div>\n            <p class=\"pth-step-desc\">Download the fully assembled, unified document file directly from your volatile browser memory.<\/p>\n        <\/div>\n    <\/div>\n<\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Last updated: July 2026<\/p>\n\n\n\n<h2 id=\"\ud83d\udd34-write-run-and-actually-see-the-errors\" class=\"wp-block-heading\">\ud83d\udd34&nbsp;<strong>Write, run, and actually see the errors<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The Editor tab gives you three panes \u2014 HTML, CSS and JavaScript \u2014 and a live preview that re-renders about half a second after you stop typing. Auto-run is on by default; switch it off and press Run when you want control, which matters if your script does something expensive.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The Console tab is where this differs from a plain preview box. Anything your code sends to console.log, console.warn or console.error shows up there, colour-coded, and so does any runtime error. Try it with the Console Playground template: it logs an array, prints a warning, then deliberately calls a function that does not exist. Input: a script with a typo. Output: a red line reading that the function is not defined, with the line number in your JavaScript pane. That one detail turns a blank preview into a five-second fix. The tab badge also shows a running error count so you notice failures without switching tabs.<\/p>\n\n\n\n<h2 id=\"\ud83d\udfe1-templates-saved-projects-and-getting-the-code-out\" class=\"wp-block-heading\">\ud83d\udfe1&nbsp;<strong>Templates, saved projects, and getting the code out<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The Templates tab holds six working starting points: an HTML5 boilerplate with the viewport tag already set, Flexbox centering, a responsive CSS grid that reflows without media queries, an accessible contact form with real labels and inline validation, a landing hero that stacks on mobile, and the console playground. Click one and it loads into the editor immediately, so you are modifying working code rather than staring at an empty pane.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The Projects tab saves your work under a name, stored in your browser. Export gives you two options that suit different jobs. The single-file HTML download inlines your CSS and JavaScript into one page you can double-click and open anywhere \u2014 good for sending a demo to someone. The ZIP download gives you index.html, style.css and script.js as three properly linked files, which is what you want if the code is going into a real project or a repository. The Share Link tab takes a third route: it encodes the entire project into the URL itself, so a colleague clicking your link gets your exact code loaded into their editor with nothing uploaded anywhere.<\/p>\n\n\n\n<h2 id=\"\ud83d\udfe2-honest-limits-and-which-tool-to-use-next\" class=\"wp-block-heading\">\ud83d\udfe2&nbsp;<strong>Honest limits, and which tool to use next<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This is a single-page authoring studio, not a full IDE, and it is worth knowing the edges before you hit them.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\ud83d\udd35 <strong>The preview runs in a sandbox.<\/strong> Your code executes in an isolated frame, which is what keeps the page safe. As a side effect, browser storage APIs inside your preview code are restricted, and network requests to other sites may be blocked by that site&#8217;s CORS policy \u2014 exactly as they would be from a real page.<\/li>\n\n\n\n<li>\ud83d\udfe0 <strong>Projects live in this browser only.<\/strong> Saved work is stored locally, so it will not appear on another device, and clearing your site data removes it. Export a ZIP for anything you cannot afford to lose.<\/li>\n\n\n\n<li>\ud83d\udfe3 <strong>Share links carry the whole project.<\/strong> Because the code travels inside the URL, a large project produces a very long link, and some chat apps truncate long URLs. For big projects, send the ZIP instead.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">When the code works, hand it to the right next tool. To check the layout properly across device widths and breakpoints, open the <a href=\"https:\/\/schoolict.net\/tools\/responsive-website-tester-pro\/\" rel=\"noreferrer noopener\" target=\"_blank\">Responsive Studio<\/a>. To shrink it for production, run it through the <a href=\"https:\/\/schoolict.net\/tools\/html-css-js-code-minifier-pro\/\" rel=\"noreferrer noopener\" target=\"_blank\">Code Minifier<\/a>. And to catch layout shift, render-blocking resources or missing alt attributes before you publish, paste it into the <a href=\"https:\/\/schoolict.net\/tools\/free-offline-pagespeed-code-analyzer\/\" rel=\"noreferrer noopener\" target=\"_blank\">PageSpeed Code Analyzer<\/a>. If you want to understand how a browser can run a full editor with no server behind it, that is covered in our guide to <a href=\"https:\/\/schoolict.net\/tools\/online-html-editor-with-css-js-article\/\" rel=\"noreferrer noopener\" target=\"_blank\">how browser-based code editors work<\/a>.<\/p>\n\n\n\n<style>\n.pth-faq-section{margin:50px auto 40px;font-family:inherit;max-width:1480px;padding:0 20px;box-sizing:border-box}\n.pth-faq-header{font-size:1.8rem;font-weight:800;color:#0f172a;margin-bottom:25px;border-bottom:2px solid #e2e8f0;padding-bottom:10px;display:flex;align-items:center;gap:10px}\n.pth-faq-grid{display:grid;grid-template-columns:1fr;gap:20px}\n@media(min-width:768px){.pth-faq-grid{grid-template-columns:repeat(2,1fr)}}\n@media(min-width:1024px){.pth-faq-grid{grid-template-columns:repeat(3,1fr)}}\n.pth-faq-card{background:#f8fafc;padding:24px;border-radius:12px;border:1px solid #e2e8f0;transition:transform .2s ease;break-inside:avoid}\n.pth-faq-card:hover{transform:translateY(-3px);box-shadow:0 4px 12px rgba(0,0,0,.05)}\n.pth-faq-q{color:#0f172a;font-size:1rem;font-weight:700;margin:0 0 12px;line-height:1.4}\n.pth-faq-a{margin:0;font-size:.95rem;color:#1e293b;line-height:1.6;font-weight:500}\n<\/style>\n<div class=\"pth-faq-section\">\n  <div class=\"pth-faq-header\">\u2753 Frequently Asked Questions<\/div>\n  <div class=\"pth-faq-grid\">\n    <div class=\"pth-faq-card\"><p class=\"pth-faq-q\">Does my code get uploaded anywhere?<\/p><p class=\"pth-faq-a\">No. The editor, the preview and the console all run inside your own browser tab. Nothing is sent to a server, so you can safely work on private or client code.<\/p><\/div>\n    <div class=\"pth-faq-card\"><p class=\"pth-faq-q\">Why is my preview blank?<\/p><p class=\"pth-faq-a\">Usually a JavaScript error stopped the script. Open the Console tab \u2014 the error and its line number will be listed in red. The error badge on the tab tells you when this has happened.<\/p><\/div>\n    <div class=\"pth-faq-card\"><p class=\"pth-faq-q\">Where are my saved projects stored?<\/p><p class=\"pth-faq-a\">In this browser&#8217;s local storage on this device. They will not appear on another computer, and clearing site data deletes them. Export a ZIP to keep a permanent copy.<\/p><\/div>\n    <div class=\"pth-faq-card\"><p class=\"pth-faq-q\">What is in the ZIP download?<\/p><p class=\"pth-faq-a\">Three linked files: index.html, style.css and script.js. The HTML already references the other two, so you can unzip it and open index.html straight away.<\/p><\/div>\n    <div class=\"pth-faq-card\"><p class=\"pth-faq-q\">How does the share link work?<\/p><p class=\"pth-faq-a\">Your code is encoded into the URL itself. Anyone opening the link gets the project loaded into their editor. Nothing is uploaded, but very large projects make very long links.<\/p><\/div>\n    <div class=\"pth-faq-card\"><p class=\"pth-faq-q\">Can I fetch data from an API in my code?<\/p><p class=\"pth-faq-a\">Sometimes. The preview runs in a sandboxed frame and behaves like a real page, so requests succeed only if the other server allows them through its CORS policy.<\/p><\/div>\n    <div class=\"pth-faq-card\"><p class=\"pth-faq-q\">Does it work offline?<\/p><p class=\"pth-faq-a\">Yes. Once the page has loaded, the editor keeps working with your connection switched off. There is no CDN dependency and no external framework to fetch.<\/p><\/div>\n    <div class=\"pth-faq-card\"><p class=\"pth-faq-q\">Can I turn off the live preview?<\/p><p class=\"pth-faq-a\">Yes. Untick Auto-run and the preview only refreshes when you press Run. Useful when your script is heavy or you are mid-edit and do not want it firing.<\/p><\/div>\n  <\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Offline HTML Editor with CSS &amp; JS Write, test, and debug HTML, CSS, and JavaScript instantly with our free Offline HTML Editor. Features live preview, 3-tab workspace, and HTML export. \ud83d\udfe2 Client-Side DOM Compilation This Offline HTML Editor parses markup strings and executable logic directly into a sandboxed Document Object Model within your local browser &#8230; <a title=\"Offline HTML Editor | An editor that tells you when your code breaks\" class=\"read-more\" href=\"https:\/\/schoolict.net\/tools\/offline-html-editor-with-css-js\/\" aria-label=\"Read more about Offline HTML Editor | An editor that tells you when your code breaks\">Read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":3464,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-814","page","type-page","status-publish","has-post-thumbnail"],"_links":{"self":[{"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/pages\/814","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=814"}],"version-history":[{"count":0,"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/pages\/814\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/media\/3464"}],"wp:attachment":[{"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/media?parent=814"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}