{"id":2528,"date":"2026-04-15T10:06:29","date_gmt":"2026-04-15T10:06:29","guid":{"rendered":"https:\/\/primetoolhub.com\/?page_id=2528"},"modified":"2026-07-14T04:38:01","modified_gmt":"2026-07-14T04:38:01","slug":"free-ai-face-blur-anonymizer","status":"publish","type":"page","link":"https:\/\/schoolict.net\/tools\/free-ai-face-blur-anonymizer\/","title":{"rendered":"Free AI Face Blur &amp; Anonymizer Studio | Auto Censor Photos"},"content":{"rendered":"<div class=\"pth-hero-section\">\n<div class=\"pth-hero-content\">\n<h2>Free AI Face Blur &#038; Anonymizer Studio | Auto Censor Photos<\/h2>\n<p>Protect privacy instantly with our free AI face blur tool. Automatically detect and censor faces in photos 100% offline in your browser. No image uploads required.<\/p>\n<div id=\"pth-toc-placeholder\"><\/div>\n<\/p>\n<\/div>\n<div class=\"pth-hero-image\">\n        <img decoding=\"async\" data-no-lazy=\"1\" width=\"450\" height=\"253\" src=\"https:\/\/schoolict.net\/tools\/wp-content\/uploads\/2026\/04\/Free-AI-Face-Blur-1024x572.jpeg\" alt=\"Free AI Face Blur\">\n    <\/div>\n<\/div>\n\n\n<div class=\"wp-block-rank-math-toc-block\" id=\"rank-math-toc\"><h2>Table of Contents<\/h2><nav><ul><li><a href=\"#\ud83d\udd34-why-we-put-image-and-video-blur-in-one-tool\">\ud83d\udd34 Why We Put Image and Video Blur in One Tool<\/a><\/li><li><a href=\"#\ud83d\udfe1-whats-actually-happening-when-you-click-detect\">\ud83d\udfe1 What&#8217;s Actually Happening When You Click &#8220;Detect&#8221;<\/a><ul><li><a href=\"#\ud83d\udfe2-a-real-example\">\ud83d\udfe2 A Real Example<\/a><ul><li><a href=\"#video-works-the-same-way-frame-by-frame\">Video Works the Same Way, Frame by Frame<\/a><\/li><\/ul><\/li><\/ul><\/li><li><a href=\"#\ud83d\udd34-common-mistakes-and-what-this-tool-doesnt-do\">\ud83d\udd34 Common Mistakes and What This Tool Doesn&#8217;t Do<\/a><\/li><li><a href=\"#\ud83d\udfe1-when-youd-actually-reach-for-this\">\ud83d\udfe1 When You&#8217;d Actually Reach for This<\/a><\/li><\/ul><\/nav><\/div>\n\n\n<!-- LSCACHE_DISABLE -->\r\n<div id=\"pth-fbs-wrapper\">\r\n\r\n<style data-no-optimize=\"1\">\r\n#pth-fbs-wrapper *,#pth-fbs-wrapper *::before,#pth-fbs-wrapper *::after{box-sizing:border-box;min-width:0;}\r\n#pth-fbs-wrapper{font-family:'Inter',system-ui,sans-serif;background:#f8fafc;border-radius:16px;padding:20px;color:#1e293b;font-weight:500;opacity:1;display:block;}\r\n.pth-fbs-header{margin-bottom:16px;}\r\n.pth-fbs-meta-bar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:10px;}\r\n.pth-fbs-version-badge{background:#eff6ff;color:#1d4ed8;font-size:.72rem;font-weight:700;padding:3px 10px;border-radius:20px;border:1px solid #bfdbfe;}\r\n.pth-fbs-status-offline{background:#f0fdf4;color:#047857;font-size:.72rem;font-weight:700;padding:3px 10px;border-radius:20px;border:1px solid #bbf7d0;}\r\n.pth-fbs-header h2{color:#1d4ed8;font-size:1.5rem;font-weight:800;margin:0 0 6px;}\r\n.pth-fbs-header p{color:#334155;font-size:.88rem;margin:0;font-weight:500;}\r\n.pth-fbs-tabs{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:16px;border-bottom:2px solid #e2e8f0;padding-bottom:10px;}\r\n.pth-fbs-tab-btn{background:#fff;border:2px solid #e2e8f0;color:#334155;font-size:.85rem;font-weight:700;padding:9px 16px;border-radius:8px;cursor:pointer;font-family:'Inter',system-ui,sans-serif;white-space:nowrap;transition:all .15s;}\r\n.pth-fbs-tab-btn:hover{border-color:#2563eb;color:#1d4ed8;background:#eff6ff;}\r\n.pth-fbs-tab-btn.pth-fbs-active{background:#2563eb;border-color:#2563eb;color:#fff;}\r\n.pth-fbs-panel{display:none;opacity:1;}\r\n.pth-fbs-panel.pth-fbs-panel-active{display:block;}\r\n.pth-fbs-empty{background:#fff;border:1px dashed #cbd5e1;border-radius:12px;padding:30px 20px;text-align:center;color:#334155;font-weight:600;min-height:120px;}\r\n@media(max-width:768px){#pth-fbs-wrapper{padding:10px;border-radius:0;background:transparent;}.pth-fbs-tab-btn{font-size:.78rem;padding:7px 12px;}.pth-fbs-header h2{font-size:1.2rem;}}\r\n<\/style>\r\n\r\n<div class=\"pth-fbs-header\">\r\n  <div class=\"pth-fbs-meta-bar\">\r\n    <span class=\"pth-fbs-version-badge\">v1.0<\/span>\r\n    <span class=\"pth-fbs-status-offline\">\ud83d\udd12 100% Offline<\/span>\r\n  <\/div>\r\n  <h2>\ud83c\udfad Face Blur Studio<\/h2>\r\n  <p>Blur or pixelate faces in photos and videos with automatic AI detection \u2014 right in your browser. No upload, no login.<\/p>\r\n<\/div>\r\n\r\n<div class=\"pth-fbs-tabs\" role=\"tablist\" aria-label=\"Face Blur Studio tools\">\r\n  <button class=\"pth-fbs-tab-btn pth-fbs-active\" data-tab=\"image\" role=\"tab\" aria-selected=\"true\">\ud83d\uddbc\ufe0f Image Face Blur<\/button>\r\n  <button class=\"pth-fbs-tab-btn\"                data-tab=\"video\" role=\"tab\" aria-selected=\"false\">\ud83c\udfac Video Face Blur<\/button>\r\n<\/div>\r\n\r\n<div class=\"pth-fbs-panel pth-fbs-panel-active\" id=\"pth-fbs-tab-image\" role=\"tabpanel\">\r\n  <div class=\"pth-fbs-empty\" id=\"pth-fbs-empty-image\">Loading Image Face Blur\u2026<\/div>\r\n<\/div>\r\n<div class=\"pth-fbs-panel\" id=\"pth-fbs-tab-video\" role=\"tabpanel\">\r\n  <div class=\"pth-fbs-empty\" id=\"pth-fbs-empty-video\">Loading Video Face Blur\u2026<\/div>\r\n<\/div>\r\n\r\n<\/div>\r\n<script data-no-optimize=\"1\" data-no-minify=\"1\" data-cfasync=\"false\">\r\n(function () {\r\n  'use strict';\r\n  window.pthFBS = window.pthFBS || {};\r\n  window.pthFBS.activeTab = 'image';\r\n\r\n  function initTabs() {\r\n    var root = document.getElementById('pth-fbs-wrapper');\r\n    if (!root) return;\r\n    var btns = root.querySelectorAll('.pth-fbs-tab-btn');\r\n    btns.forEach(function (btn) {\r\n      btn.addEventListener('click', function () {\r\n        var tab = btn.getAttribute('data-tab');\r\n        btns.forEach(function (b) {\r\n          b.classList.remove('pth-fbs-active');\r\n          b.setAttribute('aria-selected', 'false');\r\n        });\r\n        btn.classList.add('pth-fbs-active');\r\n        btn.setAttribute('aria-selected', 'true');\r\n        root.querySelectorAll('.pth-fbs-panel').forEach(function (p) {\r\n          p.classList.remove('pth-fbs-panel-active');\r\n        });\r\n        var panel = document.getElementById('pth-fbs-tab-' + tab);\r\n        if (panel) panel.classList.add('pth-fbs-panel-active');\r\n        window.pthFBS.activeTab = tab;\r\n        document.dispatchEvent(new CustomEvent('pthFBSTabActive', { detail: { tab: tab } }));\r\n      });\r\n    });\r\n\r\n    \/* Deep-link: ?tab=video \/ ?tab=image (SEO preservation for old URLs) *\/\r\n    try {\r\n      var want = new URLSearchParams(window.location.search).get('tab');\r\n      if (want === 'video' || want === 'image') {\r\n        var target = root.querySelector('.pth-fbs-tab-btn[data-tab=\"' + want + '\"]');\r\n        if (target) target.click();\r\n      }\r\n    } catch (e) {}\r\n  }\r\n\r\n  function runInit() {\r\n    if (window.pthFBS.shellReady) return;\r\n    if (!document.getElementById('pth-fbs-wrapper')) return;\r\n    window.pthFBS.shellReady = true;\r\n    initTabs();\r\n  }\r\n\r\n  \/* 4-layer bootstrap *\/\r\n  runInit();\r\n  if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', runInit); }\r\n  window.addEventListener('load', function () { setTimeout(runInit, 50); });\r\n  if (!window.pthFBS.shellReady && typeof MutationObserver !== 'undefined') {\r\n    var obs = new MutationObserver(function () {\r\n      if (document.getElementById('pth-fbs-wrapper')) { obs.disconnect(); runInit(); }\r\n    });\r\n    obs.observe(document.body || document.documentElement, { childList: true, subtree: true });\r\n    setTimeout(function () { obs.disconnect(); }, 10000);\r\n  }\r\n})();\r\n<\/script>\n\n\n<style>\r\n\/* ==========================================================================\r\n   PRIME TOOL HUB - ALL-IN-ONE AI FACE BLUR & ANONYMIZER PRO\r\n   Engine: face-api.js | Update: Detailed Face-by-Face Analysis Report\r\n   ========================================================================== *\/\r\n#pth-ai-anon-wrapper { box-sizing: border-box; font-family: 'Inter', system-ui, -apple-system, sans-serif; color: #0f172a; background-color: #ffffff; border-radius: 12px; overflow: hidden; border: 1px solid #e2e8f0; margin: 20px 0; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05); transition: all 0.3s ease; position: relative; }\r\n#pth-ai-anon-wrapper * { box-sizing: border-box; }\r\n\/* TRUE FULL SCREEN *\/\r\n#pth-ai-anon-wrapper.is-fullscreen { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; z-index: 999999; margin: 0; border-radius: 0; overflow-y: auto; display: flex; flex-direction: column; background: #f8fafc; }\r\n#pth-ai-anon-wrapper.is-fullscreen .pth-main-area { flex: 1; max-width: 1400px; margin: 0 auto; width: 100%; }\r\n\/* Header *\/\r\n#pth-ai-anon-wrapper .pth-header { padding: 24px 24px 0 24px; display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; flex-wrap: wrap; gap: 12px; }\r\n#pth-ai-anon-wrapper .pth-title { margin: 0; font-size: 1.5rem; font-weight: 800; color: #8b5cf6; display: flex; align-items: center; gap: 8px; }\r\n#pth-ai-anon-wrapper .pth-btn { border: none; padding: 10px 18px; font-size: 0.875rem; border-radius: 8px; cursor: pointer; font-weight: 700; display: inline-flex; align-items: center; justify-content: center; gap: 6px; transition: all 0.2s; }\r\n#pth-ai-anon-wrapper .pth-btn.danger { background: #fee2e2; color: #991b1b; }\r\n#pth-ai-anon-wrapper .pth-btn.danger:hover { background: #fecaca; }\r\n#pth-ai-anon-wrapper .pth-btn.primary { background: #8b5cf6; color: #ffffff; box-shadow: 0 4px 10px rgba(139, 92, 246, 0.25); }\r\n#pth-ai-anon-wrapper .pth-btn.primary:hover { background: #7c3aed; transform: translateY(-2px); }\r\n#pth-ai-anon-wrapper .pth-btn.primary:disabled { background: #cbd5e1; color: #64748b; cursor: not-allowed; box-shadow: none; transform: none; }\r\n#pth-ai-anon-wrapper .pth-fullwidth-btn { background: #1e293b; color: #fff; }\r\n#pth-ai-anon-wrapper .pth-fullwidth-btn:hover { background: #0f172a; }\r\n\/* Main Area & Grid *\/\r\n#pth-ai-anon-wrapper .pth-main-area { padding: 0 24px 24px 24px; }\r\n#pth-ai-anon-wrapper .pth-content-grid { display: grid; grid-template-columns: 1.8fr 1fr; gap: 24px; }\r\n@media (max-width: 900px) { #pth-ai-anon-wrapper .pth-content-grid { grid-template-columns: 1fr; } }\r\n\/* Inputs *\/\r\n#pth-ai-anon-wrapper .pth-drop-zone { border: 2px dashed #c4b5fd; border-radius: 12px; padding: 40px 20px; text-align: center; background: #f5f3ff; transition: all 0.3s; cursor: pointer; margin-bottom: 20px; position: relative; }\r\n#pth-ai-anon-wrapper .pth-drop-zone:hover, #pth-ai-anon-wrapper .pth-drop-zone.active { border-color: #8b5cf6; background: #ede9fe; }\r\n#pth-ai-anon-wrapper .pth-dz-icon { font-size: 40px; margin-bottom: 10px; }\r\n#pth-ai-anon-wrapper .pth-dz-text { font-size: 1rem; font-weight: 700; color: #4c1d95; margin: 0 0 5px 0; }\r\n#pth-ai-anon-wrapper .pth-dz-sub { font-size: 0.85rem; color: #6d28d9; margin: 0; }\r\n#pth-ai-anon-wrapper .pth-or-section { text-align: center; margin: 12px 0; color: #64748b; font-size: 0.875rem; font-weight: 600; }\r\n#pth-ai-anon-wrapper .pth-url-group { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 20px; justify-content: center; }\r\n#pth-ai-anon-wrapper .pth-url-group input { flex: 1; min-width: 200px; padding: 10px 16px; border: 1px solid #cbd5e1; border-radius: 8px; font-size: 0.95rem; outline: none; }\r\n#pth-ai-anon-wrapper .pth-url-group input:focus { border-color: #8b5cf6; }\r\n\/* Original Preview *\/\r\n#pth-ai-anon-wrapper .pth-orig-preview-area { display: none; background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 12px; padding: 20px; margin-bottom: 20px; text-align: center; }\r\n#pth-ai-anon-wrapper .pth-orig-img-wrap { max-height: 400px; margin-bottom: 15px; background: #e2e8f0; border-radius: 8px; overflow: hidden; display: flex; align-items: center; justify-content: center; }\r\n#pth-ai-anon-wrapper .pth-orig-img-wrap img { max-width: 100%; max-height: 400px; display: block; object-fit: contain; }\r\n#pth-ai-anon-wrapper .pth-actions-row { display: flex; justify-content: center; gap: 15px; flex-wrap: wrap; }\r\n\/* Progress *\/\r\n#pth-ai-anon-wrapper .pth-progress-area { display: none; background: #ffffff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 25px; margin-bottom: 20px; text-align: center; }\r\n#pth-ai-anon-wrapper .pth-progress-title { font-size: 1rem; font-weight: 800; color: #0f172a; margin: 0 0 15px 0; }\r\n#pth-ai-anon-wrapper .pth-progress-sub { font-size: 0.85rem; color: #64748b; margin: 0 0 5px 0; }\r\n#pth-ai-anon-wrapper .pth-progress-bar-wrap { width: 100%; height: 12px; background: #e2e8f0; border-radius: 10px; overflow: hidden; margin-bottom: 10px; }\r\n#pth-ai-anon-wrapper .pth-progress-bar { height: 100%; background: linear-gradient(90deg, #8b5cf6, #d946ef); width: 0%; transition: width 0.3s ease; border-radius: 10px; }\r\n#pth-ai-anon-wrapper .pth-first-time-note { font-size: 0.8rem; color: #8b5cf6; font-weight: 600; margin-top: 8px; background: #f5f3ff; padding: 8px; border-radius: 6px; border: 1px solid #ddd6fe; display: none; }\r\n\/* Scanning Animation *\/\r\n#pth-ai-anon-wrapper .pth-scanning-wrapper { position: relative; max-width: 250px; margin: 0 auto 15px auto; border-radius: 8px; overflow: hidden; border: 2px solid #8b5cf6; display: none; box-shadow: 0 0 20px rgba(139, 92, 246, 0.3); }\r\n#pth-ai-anon-wrapper .pth-scanning-img { width: 100%; max-height: 250px; object-fit: contain; display: block; opacity: 0.6; filter: grayscale(80%); }\r\n#pth-ai-anon-wrapper .pth-scanner-line { position: absolute; top: 0; left: 0; width: 100%; height: 3px; background: #d946ef; box-shadow: 0 0 15px 5px rgba(217, 70, 239, 0.8); animation: pthScanAnim 1.2s ease-in-out infinite alternate; }\r\n@keyframes pthScanAnim { 0% { top: 0%; } 100% { top: 98%; } }\r\n\/* Preview Box (Canvas) *\/\r\n#pth-ai-anon-wrapper .pth-preview-box { border: 1px solid #e2e8f0; border-radius: 12px; overflow: hidden; margin-bottom: 20px; display: none; background: #fff; }\r\n#pth-ai-anon-wrapper .pth-preview-header { background: #f1f5f9; padding: 12px 15px; border-bottom: 1px solid #e2e8f0; font-weight: 800; color: #0f172a; display: flex; justify-content: space-between; align-items: center; }\r\n#pth-ai-anon-wrapper .pth-canvas-container { background-image: linear-gradient(45deg, #e2e8f0 25%, transparent 25%), linear-gradient(-45deg, #e2e8f0 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #e2e8f0 75%), linear-gradient(-45deg, transparent 75%, #e2e8f0 75%); background-size: 20px 20px; background-position: 0 0, 0 10px, 10px -10px, -10px 0px; padding: 20px; display: flex; justify-content: center; align-items: center; min-height: 350px; position: relative; overflow: hidden; }\r\n#pth-ai-anon-wrapper .pth-compare-wrap { position: relative; display: inline-block; max-width: 100%; z-index: 2; user-select: none; line-height: 0; }\r\n#pth-ai-anon-wrapper .pth-result-canvas { max-width: 100%; max-height: 600px; height: auto; display: block; box-shadow: 0 4px 15px rgba(0,0,0,0.1); border-radius: 4px; }\r\n#pth-ai-anon-wrapper .pth-img-before { position: absolute; top: 0; left: 0; width: 50%; height: 100%; overflow: hidden; border-right: 3px solid #fff; box-shadow: 3px 0 8px rgba(0,0,0,0.25); }\r\n#pth-ai-anon-wrapper .pth-compare-slider { position: absolute; top: 0; left: 0; width: 100%; height: 100%; opacity: 0; z-index: 4; cursor: ew-resize; margin: 0; }\r\n\/* Settings Panel *\/\r\n#pth-ai-anon-wrapper .pth-filters-panel { background: #f5f3ff; padding: 15px; border-top: 1px solid #ede9fe; }\r\n#pth-ai-anon-wrapper .pth-filter-row { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; font-size: 0.85rem; }\r\n#pth-ai-anon-wrapper .pth-filter-row label { min-width: 100px; font-weight: 700; color: #4c1d95; }\r\n#pth-ai-anon-wrapper select, #pth-ai-anon-wrapper input[type=\"range\"] { flex: 1; padding: 8px 12px; border-radius: 8px; border: 1px solid #c4b5fd; outline: none; accent-color: #8b5cf6; font-family: inherit; }\r\n\/* Quick Presets *\/\r\n#pth-ai-anon-wrapper .pth-presets { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 18px; }\r\n#pth-ai-anon-wrapper .pth-preset-btn { font-size: 0.8rem; padding: 8px 16px; border-radius: 9999px; background: #e0e7ff; color: #4c1d95; border: none; cursor: pointer; font-weight: 600; }\r\n#pth-ai-anon-wrapper .pth-preset-btn:hover { background: #c4b5fd; }\r\n\/* Sidebar & Stats *\/\r\n#pth-ai-anon-wrapper .pth-sidebar { background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 12px; padding: 20px; height: fit-content; }\r\n#pth-ai-anon-wrapper .pth-sidebar h4 { margin: 0 0 15px 0; font-size: 1.1rem; color: #0f172a; border-bottom: 2px solid #cbd5e1; padding-bottom: 10px; }\r\n#pth-ai-anon-wrapper .pth-sidebar ul { padding-left: 0; list-style: none; margin: 0 0 20px 0; }\r\n#pth-ai-anon-wrapper .pth-sidebar li { margin-bottom: 12px; display: flex; align-items: flex-start; gap: 10px; font-size: 0.9rem; color: #475569; }\r\n#pth-ai-anon-wrapper .pth-stats-area { display: none; margin-top: 20px; padding: 15px; background: #fff; border-radius: 8px; border: 1px solid #e2e8f0; box-shadow: 0 2px 4px rgba(0,0,0,0.02); }\r\n\/* Custom Scrollbar for Face List *\/\r\n#pth-ai-anon-wrapper .pth-face-list::-webkit-scrollbar { width: 6px; }\r\n#pth-ai-anon-wrapper .pth-face-list::-webkit-scrollbar-track { background: #f1f5f9; border-radius: 4px; }\r\n#pth-ai-anon-wrapper .pth-face-list::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 4px; }\r\n#pth-ai-anon-wrapper .pth-toast { position: fixed; bottom: 20px; right: 20px; background: #10b981; color: #fff; padding: 12px 24px; border-radius: 8px; box-shadow: 0 10px 25px -5px rgba(0,0,0,0.2); display: none; align-items: center; gap: 12px; z-index: 9999999; font-weight: 700; font-size: 14px; }\r\n#pth-ai-anon-wrapper .pth-toast.show { display: flex; animation: pthToastFade 0.3s ease; }\r\n@keyframes pthToastFade { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }\r\n<\/style>\r\n\r\n<div id=\"pth-ai-anon-wrapper\">\r\n    <div id=\"pth-ai-toast\" class=\"pth-toast\">\u2705 Action Successful!<\/div>\r\n  \r\n    <div class=\"pth-header\">\r\n        <h2 class=\"pth-title\">\ud83c\udfad AI Face Blur & Anonymizer Pro <span style=\"font-size:0.75rem; background:#8b5cf6; color:#fff; padding:2px 8px; border-radius:9999px;\">Detailed Analysis<\/span><\/h2>\r\n        <div style=\"display:flex; gap:8px; flex-wrap: wrap;\">\r\n            <button class=\"pth-btn pth-fullwidth-btn\" id=\"pth-ai-full-btn\" title=\"Toggle Full Screen\">\u26f6 Full Screen<\/button>\r\n            <button class=\"pth-btn danger\" id=\"pth-ai-clear-btn\" style=\"display:none;\">\ud83d\udd04 New Image<\/button>\r\n        <\/div>\r\n    <\/div>\r\n  \r\n    <div class=\"pth-main-area\">\r\n        <div class=\"pth-content-grid\">\r\n          \r\n            <div class=\"pth-main-col\">\r\n                <input type=\"file\" id=\"pth-ai-file\" accept=\"image\/*\" style=\"display:none;\">\r\n              \r\n                <div id=\"pth-ai-input-area\">\r\n                    <div class=\"pth-drop-zone\" id=\"pth-ai-drop-zone\">\r\n                        <div class=\"pth-dz-icon\">\ud83d\udcf8<\/div>\r\n                        <h3 class=\"pth-dz-text\">Click or Drag & Drop an Image<\/h3>\r\n                        <p class=\"pth-dz-sub\">100% Secure \u2022 Analyzed offline \u2022 Detects Age, Gender & Side Profiles<\/p>\r\n                    <\/div>\r\n                    <div class=\"pth-or-section\">\u2014 OR \u2014<\/div>\r\n                    <div class=\"pth-url-group\">\r\n                        <input type=\"text\" id=\"pth-url-input\" placeholder=\"https:\/\/example.com\/image.jpg\">\r\n                        <button class=\"pth-btn primary\" id=\"pth-ai-load-url-btn\">\ud83d\udd17 Load URL<\/button>\r\n                        <button class=\"pth-btn primary\" id=\"pth-ai-paste-btn\">\ud83d\udccb Paste<\/button>\r\n                        <button class=\"pth-btn\" id=\"pth-ai-sample-btn\" style=\"background: #f59e0b; color: white;\">\ud83d\uddbc\ufe0f Try Sample<\/button>\r\n                    <\/div>\r\n                <\/div>\r\n              \r\n                <div class=\"pth-orig-preview-area\" id=\"pth-ai-orig-area\">\r\n                    <h3 style=\"margin-top:0; color:#0f172a;\">\ud83d\udee1\ufe0f Ready to Anonymize<\/h3>\r\n                    <div class=\"pth-orig-img-wrap\">\r\n                        <img decoding=\"async\" id=\"pth-ai-orig-img\" src=\"\" alt=\"ai face blur tool original\">\r\n                    <\/div>\r\n                    <div class=\"pth-actions-row\">\r\n                        <button class=\"pth-btn primary\" id=\"pth-ai-start-btn\" style=\"font-size: 1.1rem; padding: 12px 30px;\">\ud83d\ude80 Auto Detect & Analyze<\/button>\r\n                        <button class=\"pth-btn danger\" id=\"pth-ai-cancel-btn\">Cancel<\/button>\r\n                    <\/div>\r\n                <\/div>\r\n              \r\n                <div class=\"pth-progress-area\" id=\"pth-ai-progress-area\">\r\n                    <h3 class=\"pth-progress-title\" id=\"pth-ai-prog-head\">\ud83e\udd16 Booting AI Engine...<\/h3>\r\n                    <div class=\"pth-progress-bar-wrap\" id=\"pth-ai-prog-bar-wrap\">\r\n                        <div class=\"pth-progress-bar\" id=\"pth-ai-progress-bar\"><\/div>\r\n                    <\/div>\r\n                    <div class=\"pth-scanning-wrapper\" id=\"pth-ai-scanning-wrapper\">\r\n                        <img decoding=\"async\" id=\"pth-ai-scanning-img\" class=\"pth-scanning-img\" src=\"\" alt=\"\">\r\n                        <div class=\"pth-scanner-line\"><\/div>\r\n                    <\/div>\r\n                    <p class=\"pth-progress-sub\" id=\"pth-ai-prog-sub\">Preparing Environment...<\/p>\r\n                    <div class=\"pth-first-time-note\" id=\"pth-ai-first-time-note\">\r\n                        \ud83d\udce5 <b>Caching AI Models:<\/b> Downloading advanced detection models (~5MB). This only happens on your first visit!\r\n                    <\/div>\r\n                <\/div>\r\n              \r\n                <div class=\"pth-preview-box\" id=\"pth-ai-preview-box\">\r\n                    <div class=\"pth-preview-header\">\r\n                        <span>\u2705 <span id=\"pth-ai-count\" style=\"color:#8b5cf6;\">0<\/span> Face(s) Censored<\/span>\r\n                        <label style=\"display:flex; align-items:center; gap:6px; font-size:0.85rem; cursor:pointer;\">\r\n                            <input type=\"checkbox\" id=\"pth-ai-compare-toggle\" checked style=\"accent-color:#8b5cf6; width: 16px; height: 16px;\"> Before\/After Slider\r\n                        <\/label>\r\n                    <\/div>\r\n                    <div class=\"pth-canvas-container\">\r\n                        <div class=\"pth-compare-wrap\" id=\"pth-ai-compare-wrap\">\r\n                            <canvas id=\"pth-ai-canvas\" class=\"pth-result-canvas\"><\/canvas>\r\n                            <div class=\"pth-img-before\" id=\"pth-ai-img-before-box\">\r\n                                <img decoding=\"async\" id=\"pth-ai-orig-compare\" src=\"\" alt=\"ai face blur tool\">\r\n                            <\/div>\r\n                            <input type=\"range\" min=\"0\" max=\"100\" value=\"50\" class=\"pth-compare-slider\" id=\"pth-ai-compare-slider\">\r\n                        <\/div>\r\n                    <\/div>\r\n                  \r\n                    <div class=\"pth-filters-panel\">\r\n                        <h4 style=\"margin:0 0 12px 0; font-size:1rem; color:#4c1d95;\">\ud83c\udf9b\ufe0f Live Censor Adjustments<\/h4>\r\n                        \r\n                        <div class=\"pth-presets\" id=\"pth-ai-presets\">\r\n                            <button class=\"pth-preset-btn\" data-preset=\"news\">\ud83d\udcf0 News Style<\/button>\r\n                            <button class=\"pth-preset-btn\" data-preset=\"social\">\ud83d\udcf1 Social Safe<\/button>\r\n                            <button class=\"pth-preset-btn\" data-preset=\"child\">\ud83e\uddd2 Child Protection<\/button>\r\n                            <button class=\"pth-preset-btn\" data-preset=\"full\">\ud83d\udd12 Full Anonymize<\/button>\r\n                        <\/div>\r\n                        \r\n                        <div class=\"pth-filter-row\">\r\n                            <label>Target Faces<\/label>\r\n                            <select id=\"pth-ai-target\">\r\n                                <option value=\"all\" selected>\ud83d\udc65 All Detected Faces<\/option>\r\n                                <option value=\"male\">\ud83d\udc68 Male Faces Only<\/option>\r\n                                <option value=\"female\">\ud83d\udc69 Female Faces Only<\/option>\r\n                                <option value=\"adult\">\ud83e\uddd1 Adults Only (16+)<\/option>\r\n                                <option value=\"child\">\ud83e\uddd2 Children Only (<16)<\/option>\r\n                            <\/select>\r\n                        <\/div>\r\n\r\n                        <div class=\"pth-filter-row\">\r\n                            <label>Censor Style<\/label>\r\n                            <select id=\"pth-ai-style\">\r\n                                <option value=\"blur\" selected>\ud83d\udca7 Soft Blur (Smooth)<\/option>\r\n                                <option value=\"pixel\">\ud83e\uddf1 Pixelate (Mosaic)<\/option>\r\n                                <option value=\"bar\">\u2b1b Black Bar<\/option>\r\n                                <option value=\"emoji\">\ud83d\ude0e Emoji Overlay<\/option>\r\n                            <\/select>\r\n                        <\/div>\r\n\r\n                        <div class=\"pth-filter-row\">\r\n                            <label>Intensity<\/label>\r\n                            <input type=\"range\" id=\"pth-ai-intensity\" min=\"2\" max=\"40\" value=\"15\">\r\n                            <span id=\"pth-ai-int-val\" style=\"width:30px; font-weight:bold;\">15<\/span>\r\n                        <\/div>\r\n                        \r\n                        <div class=\"pth-filter-row\">\r\n                            <label>Area Padding<\/label>\r\n                            <input type=\"range\" id=\"pth-ai-padding\" min=\"0\" max=\"100\" value=\"30\">\r\n                            <span id=\"pth-ai-pad-val\" style=\"width:30px; font-weight:bold;\">30%<\/span>\r\n                        <\/div>\r\n                    <\/div>\r\n                  \r\n                    <div style=\"padding: 15px; background: #fff; border-top: 1px solid #e2e8f0; display:flex; justify-content:center; gap:10px; flex-wrap:wrap;\">\r\n                        <select id=\"pth-ai-out-format\" style=\"max-width:120px; background:#f1f5f9; padding: 10px; border-radius: 8px; border: 1px solid #cbd5e1;\">\r\n                            <option value=\"image\/jpeg\">JPG<\/option>\r\n                            <option value=\"image\/png\">PNG<\/option>\r\n                            <option value=\"image\/webp\">WebP<\/option>\r\n                        <\/select>\r\n                        <button class=\"pth-btn primary\" id=\"pth-ai-dwn-btn\" style=\"background: #10b981;\">\ud83d\udce5 Download<\/button>\r\n                        <button class=\"pth-btn primary\" id=\"pth-ai-copy-btn\">\ud83d\udccb Copy Image<\/button>\r\n                        <button class=\"pth-btn primary\" id=\"pth-ai-print-btn\" style=\"background: #3b82f6;\">\ud83d\udda8\ufe0f Print<\/button>\r\n                    <\/div>\r\n                <\/div>\r\n            <\/div>\r\n          \r\n            <div class=\"pth-sidebar-col\">\r\n                <div class=\"pth-sidebar\">\r\n                    <h4>\ud83d\udca1 All-In-One Pro Features<\/h4>\r\n                    <ul>\r\n                        <li>\ud83c\udfaf <b>AI Targeting:<\/b> Selectively blur only children, men, women or adults using real age\/gender detection.<\/li>\r\n                        <li>\ud83d\udcca <b>AI Analysis Report:<\/b> Get a detailed breakdown of estimated age and gender for every detected face.<\/li>\r\n                        <li>\u26a1 <b>Quick Presets:<\/b> One-click News Style, Social Safe, Child Protection & Full Anonymize.<\/li>\r\n                        <li>\ud83d\udee1\ufe0f <b>100% Offline:<\/b> Your image never leaves your device. AI runs locally via WebGL.<\/li>\r\n                    <\/ul>\r\n                    \r\n                    <div id=\"pth-ai-stats-area\" class=\"pth-stats-area\">\r\n                        <h4 style=\"margin:0 0 10px 0; font-size:1.1rem; color:#0f172a; display:flex; align-items:center; gap:6px;\">\ud83d\udcc8 AI Analysis Report<\/h4>\r\n                        <div id=\"pth-ai-stats-content\" style=\"line-height:1.4;\"><\/div>\r\n                    <\/div>\r\n                <\/div>\r\n            <\/div>\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\ndocument.addEventListener('DOMContentLoaded', () => {\r\n    window.pthAllInOneAnonApp = {\r\n        initialized: false,\r\n        originalImgObj: null,\r\n        detections: [],\r\n        \r\n        init: function() {\r\n            this.cacheDOM();\r\n            this.attachEvents();\r\n        },\r\n        \r\n        cacheDOM: function() {\r\n            this.UI = {\r\n                wrapper: document.getElementById('pth-ai-anon-wrapper'),\r\n                inputArea: document.getElementById('pth-ai-input-area'),\r\n                origArea: document.getElementById('pth-ai-orig-area'),\r\n                origImg: document.getElementById('pth-ai-orig-img'),\r\n                progArea: document.getElementById('pth-ai-progress-area'),\r\n                progHead: document.getElementById('pth-ai-prog-head'),\r\n                progSub: document.getElementById('pth-ai-prog-sub'),\r\n                progBarWrap: document.getElementById('pth-ai-prog-bar-wrap'),\r\n                progBar: document.getElementById('pth-ai-progress-bar'),\r\n                scanWrap: document.getElementById('pth-ai-scanning-wrapper'),\r\n                scanImg: document.getElementById('pth-ai-scanning-img'),\r\n                firstNote: document.getElementById('pth-ai-first-time-note'),\r\n                \r\n                prevBox: document.getElementById('pth-ai-preview-box'),\r\n                canvas: document.getElementById('pth-ai-canvas'),\r\n                ctx: document.getElementById('pth-ai-canvas').getContext('2d'),\r\n                countLabel: document.getElementById('pth-ai-count'),\r\n                imgOrigComp: document.getElementById('pth-ai-orig-compare'),\r\n                beforeBox: document.getElementById('pth-ai-img-before-box'),\r\n                slider: document.getElementById('pth-ai-compare-slider'),\r\n                togComp: document.getElementById('pth-ai-compare-toggle'),\r\n                \r\n                dz: document.getElementById('pth-ai-drop-zone'),\r\n                fileIn: document.getElementById('pth-ai-file'),\r\n                urlInput: document.getElementById('pth-url-input'),\r\n                loadUrlBtn: document.getElementById('pth-ai-load-url-btn'),\r\n                pasteBtn: document.getElementById('pth-ai-paste-btn'),\r\n                sampleBtn: document.getElementById('pth-ai-sample-btn'),\r\n                startBtn: document.getElementById('pth-ai-start-btn'),\r\n                cancelBtn: document.getElementById('pth-ai-cancel-btn'),\r\n                clearBtn: document.getElementById('pth-ai-clear-btn'),\r\n                fullBtn: document.getElementById('pth-ai-full-btn'),\r\n                dwnBtn: document.getElementById('pth-ai-dwn-btn'),\r\n                copyBtn: document.getElementById('pth-ai-copy-btn'),\r\n                printBtn: document.getElementById('pth-ai-print-btn'),\r\n                toast: document.getElementById('pth-ai-toast'),\r\n                \r\n                targetSel: document.getElementById('pth-ai-target'),\r\n                styleSel: document.getElementById('pth-ai-style'),\r\n                intSli: document.getElementById('pth-ai-intensity'),\r\n                intVal: document.getElementById('pth-ai-int-val'),\r\n                padSli: document.getElementById('pth-ai-padding'),\r\n                padVal: document.getElementById('pth-ai-pad-val'),\r\n                outFormat: document.getElementById('pth-ai-out-format'),\r\n                statsArea: document.getElementById('pth-ai-stats-area'),\r\n                statsContent: document.getElementById('pth-ai-stats-content'),\r\n                presets: document.getElementById('pth-ai-presets')\r\n            };\r\n        },\r\n        showToast: function(msg, isError = false) {\r\n            this.UI.toast.innerText = msg;\r\n            this.UI.toast.style.background = isError ? '#ef4444' : '#10b981';\r\n            this.UI.toast.classList.add('show');\r\n            setTimeout(() => this.UI.toast.classList.remove('show'), 3500);\r\n        },\r\n        attachEvents: function() {\r\n            const self = this;\r\n            \r\n            this.UI.fullBtn.addEventListener('click', () => {\r\n                self.UI.wrapper.classList.toggle('is-fullscreen');\r\n                self.UI.fullBtn.innerHTML = self.UI.wrapper.classList.contains('is-fullscreen') ? '\u27f2 Exit Full Screen' : '\u26f6 Full Screen';\r\n                if (self.detections.length) self.syncSliderWidth();\r\n            });\r\n            \r\n            this.UI.dz.addEventListener('click', () => self.UI.fileIn.click());\r\n            this.UI.dz.addEventListener('dragover', e => { e.preventDefault(); self.UI.dz.classList.add('active'); });\r\n            this.UI.dz.addEventListener('dragleave', () => self.UI.dz.classList.remove('active'));\r\n            this.UI.dz.addEventListener('drop', e => { e.preventDefault(); self.UI.dz.classList.remove('active'); if (e.dataTransfer.files.length) self.handleFile(e.dataTransfer.files[0]); });\r\n            this.UI.fileIn.addEventListener('change', e => { if (e.target.files.length) self.handleFile(e.target.files[0]); });\r\n            \r\n            this.UI.loadUrlBtn.addEventListener('click', async () => {\r\n                const url = self.UI.urlInput.value.trim();\r\n                if (!url) return self.showToast('\u26a0\ufe0f Please enter a URL', true);\r\n                self.UI.loadUrlBtn.innerText = 'Loading...';\r\n                try {\r\n                    const res = await fetch(url, { mode: 'cors' });\r\n                    if (!res.ok) throw new Error();\r\n                    const blob = await res.blob();\r\n                    self.handleFile(blob);\r\n                } catch (err) { self.showToast('\u274c Failed to load URL (CORS).', true); }\r\n                self.UI.loadUrlBtn.innerText = '\ud83d\udd17 Load URL';\r\n            });\r\n\r\n            this.UI.sampleBtn.addEventListener('click', async () => {\r\n                self.UI.sampleBtn.innerText = 'Loading...';\r\n                try {\r\n                    const res = await fetch('https:\/\/schoolict.net\/tools\/sampledata\/face.jpeg', { mode: 'cors' });\r\n                    if (!res.ok) throw new Error();\r\n                    const blob = await res.blob();\r\n                    self.handleFile(blob);\r\n                } catch (err) { \r\n                    self.showToast('\u274c Failed to load Sample image.', true); \r\n                }\r\n                self.UI.sampleBtn.innerText = '\ud83d\uddbc\ufe0f Try Sample';\r\n            });\r\n            \r\n            this.UI.pasteBtn.addEventListener('click', async () => {\r\n                try {\r\n                    const items = await navigator.clipboard.read();\r\n                    for (const item of items) {\r\n                        if (item.types.some(t => t.startsWith('image\/'))) {\r\n                            const blob = await item.getType(item.types.find(t => t.startsWith('image\/')));\r\n                            self.handleFile(blob); return;\r\n                        }\r\n                    }\r\n                    self.showToast('\u26a0\ufe0f No image in clipboard.', true);\r\n                } catch (err) { self.showToast('\u274c Clipboard access denied.', true); }\r\n            });\r\n            \r\n            this.UI.cancelBtn.addEventListener('click', () => self.resetTool());\r\n            this.UI.clearBtn.addEventListener('click', () => self.resetTool());\r\n            this.UI.startBtn.addEventListener('click', () => self.startAiProcessing());\r\n            \r\n            \/\/ Slider\r\n            this.UI.slider.addEventListener('input', e => { self.UI.beforeBox.style.width = e.target.value + '%'; });\r\n            this.UI.togComp.addEventListener('change', e => {\r\n                const show = e.target.checked;\r\n                self.UI.slider.style.display = show ? 'block' : 'none';\r\n                self.UI.beforeBox.style.display = show ? 'block' : 'none';\r\n            });\r\n            window.addEventListener('resize', () => { if(self.detections.length) self.syncSliderWidth(); });\r\n            \r\n            const redraw = () => { if(self.detections.length) self.renderCensor(); };\r\n            this.UI.targetSel.addEventListener('change', redraw);\r\n            this.UI.styleSel.addEventListener('change', redraw);\r\n            this.UI.intSli.addEventListener('input', () => { self.UI.intVal.textContent = self.UI.intSli.value; redraw(); });\r\n            this.UI.padSli.addEventListener('input', () => { self.UI.padVal.textContent = self.UI.padSli.value + '%'; redraw(); });\r\n            \r\n            this.UI.presets.querySelectorAll('.pth-preset-btn').forEach(btn => {\r\n                btn.addEventListener('click', () => {\r\n                    const preset = btn.getAttribute('data-preset');\r\n                    if (preset === 'news') {\r\n                        self.UI.styleSel.value = 'pixel'; self.UI.intSli.value = 25; self.UI.padSli.value = 40;\r\n                    } else if (preset === 'social') {\r\n                        self.UI.styleSel.value = 'blur'; self.UI.intSli.value = 18; self.UI.padSli.value = 25; self.UI.targetSel.value = 'all';\r\n                    } else if (preset === 'child') {\r\n                        self.UI.styleSel.value = 'blur'; self.UI.intSli.value = 15; self.UI.padSli.value = 50; self.UI.targetSel.value = 'child';\r\n                    } else if (preset === 'full') {\r\n                        self.UI.styleSel.value = 'bar'; self.UI.intSli.value = 30; self.UI.padSli.value = 60;\r\n                    }\r\n                    self.UI.intVal.textContent = self.UI.intSli.value;\r\n                    self.UI.padVal.textContent = self.UI.padSli.value + '%';\r\n                    redraw();\r\n                    self.showToast('\u2705 Preset applied');\r\n                });\r\n            });\r\n            \r\n            this.UI.dwnBtn.addEventListener('click', () => self.downloadImage());\r\n            this.UI.copyBtn.addEventListener('click', () => self.copyImage());\r\n            this.UI.printBtn.addEventListener('click', () => self.printImage());\r\n        },\r\n        handleFile: function(fileOrBlob) {\r\n            const file = fileOrBlob instanceof File ? fileOrBlob : new File([fileOrBlob], 'image.jpg', { type: fileOrBlob.type });\r\n            if (!file.type.startsWith('image\/')) return this.showToast('\u26a0\ufe0f Invalid image format.', true);\r\n            const url = URL.createObjectURL(file);\r\n            this.originalImgObj = new Image();\r\n            const self = this;\r\n            this.originalImgObj.onload = () => {\r\n                self.UI.inputArea.style.display = 'none';\r\n                self.UI.origArea.style.display = 'block';\r\n                self.UI.clearBtn.style.display = 'inline-flex';\r\n                self.UI.origImg.src = url;\r\n            };\r\n            this.originalImgObj.src = url;\r\n        },\r\n        loadFaceApi: function() {\r\n            return new Promise((resolve, reject) => {\r\n                if(window.faceapi) return resolve();\r\n                const script = document.createElement('script');\r\n                script.src = \"https:\/\/cdn.jsdelivr.net\/npm\/@vladmandic\/face-api\/dist\/face-api.min.js\";\r\n                script.setAttribute('data-cfasync', 'false');\r\n                script.setAttribute('data-no-optimize', '1');\r\n                script.onload = resolve;\r\n                script.onerror = reject;\r\n                document.head.appendChild(script);\r\n            });\r\n        },\r\n        startAiProcessing: async function() {\r\n            if (!this.originalImgObj) return;\r\n            this.UI.origArea.style.display = 'none';\r\n            this.UI.progArea.style.display = 'block';\r\n            this.UI.scanWrap.style.display = 'none';\r\n            this.UI.progBarWrap.style.display = 'block';\r\n            this.UI.progBar.style.width = '30%';\r\n            \r\n            try {\r\n                if (!window.faceapi) {\r\n                    this.UI.progHead.innerText = '\ud83d\udce5 Loading Engine...';\r\n                    this.UI.progSub.innerText = 'Downloading Face-API.js...';\r\n                    await this.loadFaceApi();\r\n                }\r\n                this.UI.firstNote.style.display = 'block';\r\n                this.UI.progHead.innerText = '\ud83e\udde0 Initializing Neural Networks...';\r\n                this.UI.progSub.innerText = 'Loading SSD Mobilenet & Age\/Gender models...';\r\n                this.UI.progBar.style.width = '60%';\r\n                \r\n                const MODEL_URL = 'https:\/\/cdn.jsdelivr.net\/npm\/@vladmandic\/face-api\/model\/';\r\n                await faceapi.nets.ssdMobilenetv1.loadFromUri(MODEL_URL);\r\n                await faceapi.nets.ageGenderNet.loadFromUri(MODEL_URL);\r\n                \r\n                this.UI.firstNote.style.display = 'none';\r\n                this.UI.progBarWrap.style.display = 'none';\r\n                this.UI.scanWrap.style.display = 'block';\r\n                this.UI.scanImg.src = this.originalImgObj.src;\r\n                this.UI.progHead.innerText = '\ud83d\udd75\ufe0f\u200d\u2642\ufe0f Scanning for Faces...';\r\n                this.UI.progSub.innerText = 'Detecting faces, analyzing age and gender...';\r\n                \r\n                this.detections = await faceapi.detectAllFaces(this.originalImgObj).withAgeAndGender();\r\n                if (this.detections.length === 0) {\r\n                    this.showToast('\u26a0\ufe0f No faces found in this image.', true);\r\n                    this.UI.progArea.style.display = 'none';\r\n                    this.UI.origArea.style.display = 'block';\r\n                    return;\r\n                }\r\n                \r\n                this.UI.canvas.width = this.originalImgObj.width;\r\n                this.UI.canvas.height = this.originalImgObj.height;\r\n                this.UI.imgOrigComp.src = this.originalImgObj.src;\r\n                \r\n                this.renderCensor();\r\n                this.updateStats(); \/\/ Generate Detailed Report!\r\n                this.UI.progArea.style.display = 'none';\r\n                this.UI.prevBox.style.display = 'block';\r\n                this.showToast(`\u2728 Found ${this.detections.length} face(s)!`);\r\n                \r\n            } catch (error) {\r\n                console.error(error);\r\n                this.showToast('\u274c AI Processing failed.', true);\r\n                this.UI.progArea.style.display = 'none';\r\n                this.UI.inputArea.style.display = 'block';\r\n            }\r\n        },\r\n        updateStats: function() {\r\n            \/\/ Sort detections left-to-right based on x-coordinate\r\n            const sortedDets = [...this.detections].sort((a, b) => a.detection.box.x - b.detection.box.x);\r\n            \r\n            let males = 0, females = 0, totalAge = 0;\r\n            let detailsHtml = '<div class=\"pth-face-list\" style=\"margin-top:12px; max-height:220px; overflow-y:auto; border-top:1px solid #e2e8f0; padding-top:10px; padding-right:5px;\">';\r\n\r\n            sortedDets.forEach((det, index) => {\r\n                const isMale = det.gender === 'male';\r\n                if (isMale) males++; else females++;\r\n                totalAge += det.age;\r\n\r\n                const genderEmoji = isMale ? '\ud83d\udc68' : '\ud83d\udc69';\r\n                const genderText = isMale ? 'Male' : 'Female';\r\n                const conf = Math.round(det.genderProbability * 100);\r\n                const ageGroup = det.age < 16 ? '(Child)' : '';\r\n\r\n                detailsHtml += `\r\n                    <div style=\"display:flex; justify-content:space-between; align-items:center; padding:8px 0; border-bottom:1px dashed #e2e8f0; font-size:0.85rem;\">\r\n                        <span style=\"font-weight:600; color:#8b5cf6; width:30px;\">#${index + 1}<\/span>\r\n                        <span style=\"flex:1; font-weight:500;\">${genderEmoji} ${genderText} <small style=\"color:#64748b\">${ageGroup}<\/small><\/span>\r\n                        <span style=\"font-weight:600; color:#0f172a; width:60px;\">~${Math.round(det.age)} yrs<\/span>\r\n                        <span style=\"color:#10b981; font-size:0.75rem; text-align:right; font-weight:600;\">${conf}% Conf<\/span>\r\n                    <\/div>\r\n                `;\r\n            });\r\n            detailsHtml += '<\/div>';\r\n\r\n            const avgAge = this.detections.length ? (totalAge \/ this.detections.length).toFixed(1) : 0;\r\n            \r\n            this.UI.statsContent.innerHTML = `\r\n                <div style=\"background:#f8fafc; padding:12px; border-radius:8px; margin-bottom:5px; font-size:0.85rem;\">\r\n                    <div style=\"display:flex; justify-content:space-between; margin-bottom:4px;\">\r\n                        <span style=\"color:#64748b;\">Total Detected:<\/span> <strong>${this.detections.length} Faces<\/strong>\r\n                    <\/div>\r\n                    <div style=\"display:flex; justify-content:space-between; margin-bottom:4px;\">\r\n                        <span style=\"color:#64748b;\">Gender Split:<\/span> <strong>\ud83d\udc68 ${males} \u00a0\u2022\u00a0 \ud83d\udc69 ${females}<\/strong>\r\n                    <\/div>\r\n                    <div style=\"display:flex; justify-content:space-between;\">\r\n                        <span style=\"color:#64748b;\">Average Age:<\/span> <strong>~${avgAge} years<\/strong>\r\n                    <\/div>\r\n                <\/div>\r\n                ${detailsHtml}\r\n                <div style=\"margin-top:12px; font-size:0.75rem; color:#64748b; text-align:center; display:flex; align-items:center; justify-content:center; gap:5px;\">\r\n                    <span>\ud83d\udee1\ufe0f<\/span> All data processed offline. 100% private.\r\n                <\/div>\r\n            `;\r\n            this.UI.statsArea.style.display = 'block';\r\n        },\r\n        renderCensor: function() {\r\n            if(!this.originalImgObj || !this.detections.length) return;\r\n            \r\n            this.UI.ctx.drawImage(this.originalImgObj, 0, 0);\r\n            const target = this.UI.targetSel.value;\r\n            const style = this.UI.styleSel.value;\r\n            const intensity = parseInt(this.UI.intSli.value);\r\n            const padPct = parseInt(this.UI.padSli.value) \/ 100;\r\n            let censoredCount = 0;\r\n            \r\n            this.detections.forEach(det => {\r\n                const isChild = det.age < 16;\r\n                const isMale = det.gender === 'male';\r\n                let shouldCensor = false;\r\n                if (target === 'all') shouldCensor = true;\r\n                else if (target === 'male' && isMale) shouldCensor = true;\r\n                else if (target === 'female' && !isMale) shouldCensor = true;\r\n                else if (target === 'adult' && !isChild) shouldCensor = true;\r\n                else if (target === 'child' && isChild) shouldCensor = true;\r\n                if (!shouldCensor) return;\r\n                \r\n                censoredCount++;\r\n                const box = det.detection.box;\r\n                const padX = box.width * padPct;\r\n                const padY = box.height * padPct;\r\n                const x = Math.max(0, box.x - padX);\r\n                const y = Math.max(0, box.y - padY);\r\n                const w = Math.min(this.UI.canvas.width - x, box.width + (padX * 2));\r\n                const h = Math.min(this.UI.canvas.height - y, box.height + (padY * 2));\r\n                \r\n                const tCan = document.createElement('canvas');\r\n                tCan.width = w; tCan.height = h;\r\n                const tCtx = tCan.getContext('2d');\r\n                tCtx.drawImage(this.UI.canvas, x, y, w, h, 0, 0, w, h);\r\n                \r\n                if (style === 'blur') {\r\n                    this.UI.ctx.save();\r\n                    this.UI.ctx.filter = `blur(${intensity}px)`;\r\n                    this.UI.ctx.drawImage(tCan, 0, 0, w, h, x, y, w, h);\r\n                    this.UI.ctx.restore();\r\n                } else if (style === 'pixel') {\r\n                    const pSize = intensity;\r\n                    const scaledW = Math.ceil(w \/ pSize);\r\n                    const scaledH = Math.ceil(h \/ pSize);\r\n                    tCtx.imageSmoothingEnabled = false;\r\n                    tCtx.drawImage(tCan, 0, 0, w, h, 0, 0, scaledW, scaledH);\r\n                    tCtx.drawImage(tCan, 0, 0, scaledW, scaledH, 0, 0, w, h);\r\n                    this.UI.ctx.drawImage(tCan, 0, 0, w, h, x, y, w, h);\r\n                } else if (style === 'bar') {\r\n                    this.UI.ctx.fillStyle = '#000000';\r\n                    this.UI.ctx.fillRect(x, y + (h*0.1), w, h*0.8);\r\n                } else if (style === 'emoji') {\r\n                    const emojis = ['\ud83d\ude0e','\ud83d\udd76\ufe0f','\ud83e\udd16','\ud83d\udd12','\ud83d\udc64'];\r\n                    const emoji = emojis[Math.floor(Math.random() * emojis.length)];\r\n                    this.UI.ctx.font = `${Math.min(w, h) * 0.8}px Arial`;\r\n                    this.UI.ctx.textAlign = 'center';\r\n                    this.UI.ctx.textBaseline = 'middle';\r\n                    this.UI.ctx.fillText(emoji, x + w \/ 2, y + h \/ 2 + (h*0.05));\r\n                }\r\n            });\r\n            this.UI.countLabel.innerText = censoredCount;\r\n            this.syncSliderWidth();\r\n        },\r\n        syncSliderWidth: function() {\r\n            setTimeout(() => {\r\n                if (this.UI.canvas.offsetWidth) {\r\n                    this.UI.imgOrigComp.style.width = this.UI.canvas.offsetWidth + 'px';\r\n                    this.UI.imgOrigComp.style.height = this.UI.canvas.offsetHeight + 'px';\r\n                }\r\n            }, 50);\r\n        },\r\n        resetTool: function() {\r\n            this.UI.fileIn.value = ''; this.UI.urlInput.value = '';\r\n            this.originalImgObj = null; this.detections = [];\r\n            this.UI.inputArea.style.display = 'block';\r\n            this.UI.origArea.style.display = 'none';\r\n            this.UI.progArea.style.display = 'none';\r\n            this.UI.prevBox.style.display = 'none';\r\n            this.UI.clearBtn.style.display = 'none';\r\n            this.UI.statsArea.style.display = 'none';\r\n            this.UI.slider.value = 50; this.UI.beforeBox.style.width = '50%';\r\n        },\r\n        downloadImage: function() {\r\n            if (!this.detections.length) return;\r\n            const format = this.UI.outFormat.value;\r\n            const ext = format === 'image\/jpeg' ? 'jpg' : (format === 'image\/webp' ? 'webp' : 'png');\r\n            const dataUrl = this.UI.canvas.toDataURL(format, 0.95);\r\n            const a = document.createElement('a');\r\n            a.href = dataUrl;\r\n            a.download = 'PTH-Anonymized-' + Date.now() + '.' + ext;\r\n            a.click();\r\n            this.showToast('\ud83d\udce5 Image downloaded successfully!');\r\n        },\r\n        copyImage: async function() {\r\n            if (!this.detections.length) return;\r\n            try {\r\n                this.UI.canvas.toBlob(async (blob) => {\r\n                    await navigator.clipboard.write([new ClipboardItem({ 'image\/png': blob })]);\r\n                    this.showToast('\ud83d\udccb Image copied to clipboard!');\r\n                }, 'image\/png');\r\n            } catch (err) { this.showToast('\u274c Clipboard copy failed.', true); }\r\n        },\r\n        printImage: function() {\r\n            if (!this.detections.length) return;\r\n            const dataUrl = this.UI.canvas.toDataURL('image\/png');\r\n            const printWin = window.open('', '_blank');\r\n            printWin.document.write(`\r\n                <style>body{margin:0;padding:20px;background:#f8fafc;} img{max-width:100%;height:auto;display:block;margin:0 auto;}<\/style>\r\n                <img decoding=\"async\" src=\"${dataUrl}\" alt=\"ai face blur tool print\">\r\n            `);\r\n            printWin.document.close();\r\n            setTimeout(() => printWin.print(), 300);\r\n            this.showToast('\ud83d\udda8\ufe0f Print dialog opened');\r\n        }\r\n    };\r\n    if (!window.pthAllInOneAnonApp.initialized) {\r\n        window.pthAllInOneAnonApp.initialized = true;\r\n        window.pthAllInOneAnonApp.init();\r\n    }\r\n});\r\n<\/script>\r\n\t<script data-no-optimize=\"1\" data-no-minify=\"1\" data-cfasync=\"false\">\r\n(function () {\r\n  'use strict';\r\n  function mount() {\r\n    var panel = document.getElementById('pth-fbs-tab-image');\r\n    var tool  = document.getElementById('pth-ai-anon-wrapper');\r\n    if (!panel || !tool) return false;\r\n    if (tool.parentNode !== panel) {\r\n      var ph = document.getElementById('pth-fbs-empty-image');\r\n      if (ph && ph.parentNode) ph.parentNode.removeChild(ph);\r\n      tool.style.margin = '0';\r\n      panel.appendChild(tool);\r\n    }\r\n    return true;\r\n  }\r\n  if (mount()) return;\r\n  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', mount);\r\n  window.addEventListener('load', function () { setTimeout(mount, 60); });\r\n  if (typeof MutationObserver !== 'undefined') {\r\n    var o = new MutationObserver(function () { if (mount()) o.disconnect(); });\r\n    o.observe(document.body || document.documentElement, { childList: true, subtree: true });\r\n    setTimeout(function () { o.disconnect(); }, 10000);\r\n  }\r\n})();\r\n<\/script>\n\n\n<style>\r\n\/* ==========================================================================\r\n   PRIME TOOL HUB - AI VIDEO FACE BLUR & CENSOR PRO v2.1\r\n   New: Dynamic Progress Bars (Hide inactive) + Smooth Fade Animations\r\n   ========================================================================== *\/\r\n#pth-vid-blur-wrapper { box-sizing: border-box; font-family: 'Inter', system-ui, -apple-system, sans-serif; color: #0f172a; background-color: #ffffff; border-radius: 12px; overflow: hidden; border: 1px solid #e2e8f0; margin: 20px 0; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05); transition: all 0.3s ease; position: relative; }\r\n#pth-vid-blur-wrapper * { box-sizing: border-box; }\r\n\/* TRUE FULL SCREEN *\/\r\n#pth-vid-blur-wrapper.is-fullscreen { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; z-index: 999999; margin: 0; border-radius: 0; overflow-y: auto; display: flex; flex-direction: column; background: #f8fafc; }\r\n#pth-vid-blur-wrapper.is-fullscreen .pth-main-area { flex: 1; max-width: 1400px; margin: 0 auto; width: 100%; }\r\n\/* Header *\/\r\n#pth-vid-blur-wrapper .pth-header { padding: 24px 24px 0 24px; display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; flex-wrap: wrap; gap: 12px; }\r\n#pth-vid-blur-wrapper .pth-title { margin: 0; font-size: 1.5rem; font-weight: 800; color: #2563eb; display: flex; align-items: center; gap: 8px; }\r\n#pth-vid-blur-wrapper .pth-badge { font-size: 0.75rem; background: #2563eb; color: #fff; padding: 3px 10px; border-radius: 9999px; font-weight: 600; letter-spacing: 0.5px; }\r\n\/* Buttons *\/\r\n#pth-vid-blur-wrapper .pth-btn { border: none; padding: 10px 18px; font-size: 0.875rem; border-radius: 8px; cursor: pointer; font-weight: 700; display: inline-flex; align-items: center; justify-content: center; gap: 6px; transition: all 0.2s; }\r\n#pth-vid-blur-wrapper .pth-btn.primary { background: #2563eb; color: #ffffff; box-shadow: 0 4px 10px rgba(37, 99, 235, 0.25); }\r\n#pth-vid-blur-wrapper .pth-btn.primary:hover { background: #1d4ed8; transform: translateY(-2px); }\r\n#pth-vid-blur-wrapper .pth-btn.primary:disabled { background: #94a3b8; color: #f1f5f9; cursor: not-allowed; box-shadow: none; transform: none; }\r\n#pth-vid-blur-wrapper .pth-btn.danger { background: #fee2e2; color: #b91c1c; }\r\n#pth-vid-blur-wrapper .pth-btn.danger:hover { background: #fecaca; }\r\n#pth-vid-blur-wrapper .pth-fullwidth-btn { background: #1e293b; color: #fff; }\r\n#pth-vid-blur-wrapper .pth-fullwidth-btn:hover { background: #0f172a; }\r\n\/* Main Area & Grid *\/\r\n#pth-vid-blur-wrapper .pth-main-area { padding: 0 24px 24px 24px; }\r\n#pth-vid-blur-wrapper .pth-content-grid { display: grid; grid-template-columns: 1.8fr 1fr; gap: 24px; }\r\n@media (max-width: 900px) { #pth-vid-blur-wrapper .pth-content-grid { grid-template-columns: 1fr; } }\r\n\/* Drop Zone *\/\r\n#pth-vid-blur-wrapper .pth-drop-zone { border: 2px dashed #93c5fd; border-radius: 12px; padding: 40px 20px; text-align: center; background: #eff6ff; transition: all 0.3s; cursor: pointer; }\r\n#pth-vid-blur-wrapper .pth-drop-zone:hover, #pth-vid-blur-wrapper .pth-drop-zone.active { border-color: #2563eb; background: #dbeafe; }\r\n#pth-vid-blur-wrapper .pth-dz-icon { font-size: 48px; margin-bottom: 15px; }\r\n#pth-vid-blur-wrapper .pth-dz-text { font-size: 1.1rem; font-weight: 700; color: #1e3a8a; margin: 0 0 8px 0; }\r\n#pth-vid-blur-wrapper .pth-dz-sub { font-size: 0.85rem; color: #3b82f6; margin: 0; font-weight: 500; }\r\n#pth-vid-blur-wrapper .pth-warning-text { font-size: 0.8rem; color: #b45309; margin-top: 15px; background: #fef3c7; padding: 8px; border-radius: 6px; display: inline-block; }\r\n\/* Player Area *\/\r\n#pth-vid-blur-wrapper .pth-player-area { display: none; background: #fff; border: 1px solid #e2e8f0; border-radius: 12px; overflow: hidden; }\r\n#pth-vid-blur-wrapper .pth-vid-container { width: 100%; max-height: 500px; background: #000; overflow: hidden; display: flex; align-items: center; justify-content: center; position: relative; }\r\n#pth-vid-blur-wrapper video { max-width: 100%; max-height: 500px; display: block; }\r\n#pth-vid-blur-wrapper canvas { max-width: 100%; max-height: 500px; display: none; }\r\n\/* Controls *\/\r\n#pth-vid-blur-wrapper .pth-controls-panel { padding: 20px; background: #f8fafc; border-top: 1px solid #e2e8f0; }\r\n#pth-vid-blur-wrapper .pth-control-row { display: flex; align-items: center; gap: 15px; margin-bottom: 15px; flex-wrap: wrap; }\r\n#pth-vid-blur-wrapper .pth-control-row label { font-weight: 700; color: #1e40af; font-size: 0.9rem; min-width: 110px; }\r\n#pth-vid-blur-wrapper select, #pth-vid-blur-wrapper input[type=\"range\"], #pth-vid-blur-wrapper input[type=\"number\"] { flex: 1; min-width: 150px; padding: 8px 12px; border-radius: 8px; border: 1px solid #93c5fd; outline: none; font-family: inherit; font-size: 0.9rem; accent-color: #2563eb; }\r\n\/* Quick Presets *\/\r\n#pth-vid-blur-wrapper .pth-presets { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 18px; padding: 0 20px; }\r\n#pth-vid-blur-wrapper .pth-preset-btn { font-size: 0.8rem; padding: 8px 16px; border-radius: 9999px; background: #dbeafe; color: #1e40af; border: none; cursor: pointer; font-weight: 600; }\r\n#pth-vid-blur-wrapper .pth-preset-btn:hover { background: #bfdbfe; }\r\n\r\n\/* =========================================================\r\n   3-PHASE PROGRESS UI (DYNAMIC VISIBILITY FIX)\r\n   ========================================================= *\/\r\n#pth-vid-blur-wrapper .pth-processing-ui { display: none; padding: 20px; background: #eff6ff; border-top: 1px solid #bfdbfe; }\r\n#pth-vid-blur-wrapper .pth-step-section { margin-bottom: 0; animation: pthFadeInUp 0.4s ease-out forwards; }\r\n#pth-vid-blur-wrapper .pth-step-section.inactive { display: none; \/* \ud83d\udd25 THIS HIDES THE INACTIVE BARS \ud83d\udd25 *\/ }\r\n\r\n@keyframes pthFadeInUp { from { opacity: 0; transform: translateY(15px); } to { opacity: 1; transform: translateY(0); } }\r\n\r\n#pth-vid-blur-wrapper .pth-status-text { font-size: 0.9rem; font-weight: 700; margin: 0 0 8px 0; display: flex; align-items: center; justify-content: space-between; }\r\n#pth-vid-blur-wrapper .pth-prog-wrap { width: 100%; height: 12px; background: #dbeafe; border-radius: 999px; overflow: hidden; margin-bottom: 5px; box-shadow: inset 0 2px 4px rgba(0,0,0,0.05); }\r\n#pth-vid-blur-wrapper .pth-prog-bar { height: 100%; width: 0%; transition: width 0.3s ease; border-radius: 999px; position: relative; }\r\n\/* Bar Colors *\/\r\n#pth-vid-blur-wrapper .pth-prog-bar.orange { background: linear-gradient(90deg, #f59e0b, #d97706); }\r\n#pth-vid-blur-wrapper .pth-prog-bar.blue { background: linear-gradient(90deg, #3b82f6, #1d4ed8); }\r\n#pth-vid-blur-wrapper .pth-prog-bar.green { background: linear-gradient(90deg, #10b981, #047857); }\r\n#pth-vid-blur-wrapper .pth-prog-bar::after { content: ''; position: absolute; top: 0; left: 0; bottom: 0; right: 0; background: linear-gradient(45deg, rgba(255,255,255,0.2) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.2) 50%, rgba(255,255,255,0.2) 75%, transparent 75%, transparent); background-size: 20px 20px; animation: pthProgStripes 1s linear infinite; }\r\n@keyframes pthProgStripes { 0% { background-position: 20px 0; } 100% { background-position: 0 0; } }\r\n#pth-vid-blur-wrapper .pth-stats-row { display: flex; justify-content: space-between; font-size: 0.8rem; color: #475569; font-weight: 600; margin-top: 5px; }\r\n#pth-vid-blur-wrapper .pth-spinner { display: inline-block; width: 14px; height: 14px; border: 3px solid rgba(37,99,235,0.3); border-radius: 50%; border-top-color: currentColor; animation: pthSpin 1s ease-in-out infinite; margin-right: 8px; }\r\n@keyframes pthSpin { to { transform: rotate(360deg); } }\r\n\r\n\/* Sidebar & Stats *\/\r\n#pth-vid-blur-wrapper .pth-sidebar { background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 12px; padding: 20px; height: fit-content; }\r\n#pth-vid-blur-wrapper .pth-sidebar h4 { margin: 0 0 15px 0; font-size: 1.1rem; color: #0f172a; border-bottom: 2px solid #cbd5e1; padding-bottom: 10px; }\r\n#pth-vid-blur-wrapper .pth-sidebar ul { padding-left: 0; list-style: none; margin: 0 0 20px 0; }\r\n#pth-vid-blur-wrapper .pth-sidebar li { margin-bottom: 12px; display: flex; align-items: flex-start; gap: 10px; font-size: 0.9rem; color: #475569; }\r\n#pth-vid-blur-wrapper .pth-live-stats-box, #pth-vid-blur-wrapper .pth-final-report-box { margin-top: 20px; padding: 15px; background: #fff; border-radius: 8px; border: 1px solid #e2e8f0; box-shadow: 0 2px 4px rgba(0,0,0,0.02); }\r\n#pth-vid-blur-wrapper .pth-stat-item { display: flex; justify-content: space-between; margin-bottom: 8px; font-size: 0.9rem; border-bottom: 1px dashed #f1f5f9; padding-bottom: 5px; }\r\n#pth-vid-blur-wrapper .pth-stat-item:last-child { border-bottom: none; margin-bottom: 0; padding-bottom: 0; }\r\n#pth-vid-blur-wrapper .pth-stat-item strong { color: #1e40af; }\r\n\/* Toast *\/\r\n#pth-vid-blur-wrapper .pth-toast { position: fixed; bottom: 20px; right: 20px; background: #10b981; color: #fff; padding: 12px 24px; border-radius: 8px; box-shadow: 0 10px 25px -5px rgba(0,0,0,0.2); display: none; align-items: center; gap: 12px; z-index: 9999999; font-weight: 700; font-size: 14px; }\r\n#pth-vid-blur-wrapper .pth-toast.show { display: flex; animation: pthToastFade 0.3s ease; }\r\n@keyframes pthToastFade { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }\r\n<\/style>\r\n\r\n<div id=\"pth-vid-blur-wrapper\">\r\n    <div id=\"pth-vid-toast\" class=\"pth-toast\">\u2705 Action Successful!<\/div>\r\n    \r\n    <div class=\"pth-header\">\r\n        <h2 class=\"pth-title\">\ud83c\udfac AI Video Face Blur Pro <span class=\"pth-badge\">v2.1 \u2022 FFmpeg<\/span><\/h2>\r\n        <div style=\"display:flex; gap:8px; flex-wrap: wrap;\">\r\n            <button class=\"pth-btn pth-fullwidth-btn\" id=\"pth-vid-full-btn\" title=\"Toggle Full Screen\">\u26f6 Full Screen<\/button>\r\n            <button class=\"pth-btn danger\" id=\"pth-vid-clear-btn\" style=\"display:none;\">\ud83d\udd04 New Video<\/button>\r\n        <\/div>\r\n    <\/div>\r\n    \r\n    <div class=\"pth-main-area\">\r\n        <div class=\"pth-content-grid\">\r\n            \r\n            <div class=\"pth-main-col\">\r\n                <input type=\"file\" id=\"pth-vid-file\" accept=\"video\/mp4,video\/webm,video\/mov\" style=\"display:none;\">\r\n                <div id=\"pth-vid-input-area\">\r\n                    <div class=\"pth-drop-zone\" id=\"pth-vid-drop-zone\">\r\n                        <div class=\"pth-dz-icon\">\ud83d\udcf9<\/div>\r\n                        <h3 class=\"pth-dz-text\">Click or Drag & Drop a Video<\/h3>\r\n                        <p class=\"pth-dz-sub\">100% Offline \u2022 New trimming & quality controls<\/p>\r\n                        <div class=\"pth-warning-text\">\u26a0\ufe0f Shorter videos process much faster. MP4 recommended.<\/div>\r\n                    <\/div>\r\n                <\/div>\r\n\r\n                <div class=\"pth-player-area\" id=\"pth-vid-player-area\">\r\n                    \r\n                    <div class=\"pth-vid-container\">\r\n                        <video id=\"pth-source-video\" controls playsinline><\/video>\r\n                        <canvas id=\"pth-render-canvas\"><\/canvas>\r\n                    <\/div>\r\n\r\n                    <div style=\"padding-top: 15px;\">\r\n                        <div class=\"pth-presets\" id=\"pth-vid-presets\">\r\n                            <button class=\"pth-preset-btn\" data-preset=\"news\">\ud83d\udcf0 News Style<\/button>\r\n                            <button class=\"pth-preset-btn\" data-preset=\"social\">\ud83d\udcf1 Social Safe<\/button>\r\n                            <button class=\"pth-preset-btn\" data-preset=\"child\">\ud83e\uddd2 Child Protection<\/button>\r\n                            <button class=\"pth-preset-btn\" data-preset=\"full\">\ud83d\udd12 Full Anonymize<\/button>\r\n                        <\/div>\r\n                    <\/div>\r\n\r\n                    <div class=\"pth-controls-panel\" id=\"pth-controls-panel\">\r\n                        <div class=\"pth-control-row\">\r\n                            <label>\ud83c\udfaf Target Faces<\/label>\r\n                            <select id=\"pth-vid-target\">\r\n                                <option value=\"all\" selected>\ud83d\udc65 Blur All Faces<\/option>\r\n                                <option value=\"male\">\ud83d\udc68 Male Faces Only<\/option>\r\n                                <option value=\"female\">\ud83d\udc69 Female Faces Only<\/option>\r\n                                <option value=\"adult\">\ud83e\uddd1 Adults Only (16+)<\/option>\r\n                                <option value=\"child\">\ud83e\uddd2 Children Only (<16)<\/option>\r\n                            <\/select>\r\n                        <\/div>\r\n                        <div class=\"pth-control-row\">\r\n                            <label>\ud83c\udfa8 Censor Style<\/label>\r\n                            <select id=\"pth-vid-style\">\r\n                                <option value=\"blur\" selected>\ud83d\udca7 Smooth Blur<\/option>\r\n                                <option value=\"pixel\">\ud83e\uddf1 Pixelate (Mosaic)<\/option>\r\n                                <option value=\"bar\">\u2b1b Black Bar<\/option>\r\n                                <option value=\"emoji\">\ud83d\ude0e Emoji Overlay<\/option>\r\n                                <option value=\"frosted\">\u2744\ufe0f Frosted Glass (NEW)<\/option>\r\n                            <\/select>\r\n                        <\/div>\r\n                        <div class=\"pth-control-row\">\r\n                            <label>\ud83c\udf9a\ufe0f Intensity<\/label>\r\n                            <input type=\"range\" id=\"pth-vid-intensity\" min=\"5\" max=\"50\" value=\"20\">\r\n                            <span id=\"pth-vid-int-val\" style=\"font-weight:bold; color:#1e40af; width:35px; text-align:right;\">20<\/span>\r\n                        <\/div>\r\n                        \r\n                        <div class=\"pth-control-row\">\r\n                            <label>\u2702\ufe0f Trim Video (sec)<\/label>\r\n                            <div style=\"display:flex; gap:8px; flex:1;\">\r\n                                <input type=\"number\" id=\"pth-vid-trim-start\" value=\"0\" min=\"0\" step=\"0.1\" style=\"flex:1;\">\r\n                                <span style=\"align-self:center; color:#64748b; font-size:0.85rem;\">to<\/span>\r\n                                <input type=\"number\" id=\"pth-vid-trim-end\" value=\"999\" min=\"0\" step=\"0.1\" style=\"flex:1;\">\r\n                            <\/div>\r\n                        <\/div>\r\n                        <div class=\"pth-control-row\">\r\n                            <label>\ud83d\udccf Output Resolution<\/label>\r\n                            <select id=\"pth-vid-resolution\">\r\n                                <option value=\"original\" selected>Original<\/option>\r\n                                <option value=\"1280\">1080p (1280\u00d7720)<\/option>\r\n                                <option value=\"854\">720p (854\u00d7480)<\/option>\r\n                            <\/select>\r\n                        <\/div>\r\n                        <div class=\"pth-control-row\">\r\n                            <label>\u26a1 Output Quality<\/label>\r\n                            <select id=\"pth-vid-quality\">\r\n                                <option value=\"20\" selected>High (CRF 20)<\/option>\r\n                                <option value=\"28\">Balanced (CRF 28)<\/option>\r\n                                <option value=\"35\">Fast (CRF 35)<\/option>\r\n                            <\/select>\r\n                        <\/div>\r\n                        \r\n                        <div style=\"display:flex; justify-content:center; margin-top:20px;\">\r\n                            <button class=\"pth-btn primary\" id=\"pth-vid-start-btn\" style=\"width:100%; font-size:1.1rem; padding: 14px;\">\r\n                                \ud83d\ude80 Start AI Processing & Export\r\n                            <\/button>\r\n                        <\/div>\r\n                    <\/div>\r\n\r\n                    <div class=\"pth-processing-ui\" id=\"pth-processing-ui\">\r\n                        <div class=\"pth-step-section\" id=\"pth-phase-prep\">\r\n                            <div class=\"pth-status-text\" style=\"color: #b45309;\">\r\n                                <span id=\"pth-prep-msg\"><span class=\"pth-spinner\" style=\"border-top-color:#d97706;\"><\/span> Preparing Engines...<\/span>\r\n                                <span id=\"pth-prep-pct\">0%<\/span>\r\n                            <\/div>\r\n                            <div class=\"pth-prog-wrap\"><div class=\"pth-prog-bar orange\" id=\"pth-prep-bar\"><\/div><\/div>\r\n                        <\/div>\r\n\r\n                        <div class=\"pth-step-section inactive\" id=\"pth-phase-render\">\r\n                            <div class=\"pth-status-text\" style=\"color: #1d4ed8;\">\r\n                                <span id=\"pth-status-msg\">Waiting for engines...<\/span>\r\n                                <span id=\"pth-status-pct\">0%<\/span>\r\n                            <\/div>\r\n                            <div class=\"pth-prog-wrap\"><div class=\"pth-prog-bar blue\" id=\"pth-prog-bar\"><\/div><\/div>\r\n                            <div class=\"pth-stats-row\">\r\n                                <span id=\"pth-stat-frames\">Frames: 0 \/ 0<\/span>\r\n                                <span id=\"pth-stat-time\">Est. Time: Calculating...<\/span>\r\n                            <\/div>\r\n                        <\/div>\r\n\r\n                        <div class=\"pth-step-section inactive\" id=\"pth-phase-encode\">\r\n                            <div class=\"pth-status-text\" style=\"color: #047857;\">\r\n                                <span id=\"pth-encode-msg\">Waiting for frames...<\/span>\r\n                                <span id=\"pth-encode-pct\">0%<\/span>\r\n                            <\/div>\r\n                            <div class=\"pth-prog-wrap\"><div class=\"pth-prog-bar green\" id=\"pth-encode-bar\"><\/div><\/div>\r\n                        <\/div>\r\n                    <\/div>\r\n\r\n                <\/div>\r\n            <\/div>\r\n            \r\n            <div class=\"pth-sidebar-col\">\r\n                <div class=\"pth-sidebar\">\r\n                    <h4>\ud83d\udca1 Video Pro Features<\/h4>\r\n                    <ul>\r\n                        <li>\ud83c\udfaf <b>Smart Tracking:<\/b> New timeline trimming and demographic tracking.<\/li>\r\n                        <li>\ud83d\ude80 <b>Native Speed:<\/b> Video plays and exports at its native framerate using FFmpeg WASM.<\/li>\r\n                        <li>\ud83d\udee1\ufe0f <b>100% Offline:<\/b> The video never leaves your browser. Absolute privacy guaranteed.<\/li>\r\n                        <li>\ud83d\udccf <b>Resolution Control:<\/b> Resize and compress with CRF options.<\/li>\r\n                        <li>\u2744\ufe0f <b>Frosted Glass:<\/b> Try the new aesthetic censor style.<\/li>\r\n                    <\/ul>\r\n                    \r\n                    <div class=\"pth-live-stats-box\" id=\"pth-live-stats-box\" style=\"display:none;\">\r\n                        <h4 style=\"margin:0 0 12px 0; font-size:1rem; color:#0f172a; display:flex; align-items:center; gap:6px;\">\ud83d\udce1 Live AI Radar<\/h4>\r\n                        <div class=\"pth-stat-item\"><span>Detected in Frame:<\/span><strong id=\"live-total\">0 Faces<\/strong><\/div>\r\n                        <div class=\"pth-stat-item\"><span>Demographics:<\/span><strong id=\"live-gender\">\ud83d\udc68 0 \u00a0\u2022\u00a0 \ud83d\udc69 0<\/strong><\/div>\r\n                        <div class=\"pth-stat-item\"><span>Est. Frame Age:<\/span><strong id=\"live-age\">-- yrs<\/strong><\/div>\r\n                        <div style=\"margin-top:10px; font-size:0.75rem; color:#64748b; text-align:center;\">Updating per processed frame<\/div>\r\n                    <\/div>\r\n\r\n                    <div class=\"pth-final-report-box\" id=\"pth-final-report-box\" style=\"display:none; border-color:#10b981; background:#ecfdf5;\">\r\n                        <h4 style=\"margin:0 0 12px 0; font-size:1.1rem; color:#047857; display:flex; align-items:center; gap:6px;\">\ud83d\udcca Final AI Report<\/h4>\r\n                        <div class=\"pth-stat-item\"><span>Est. Unique People:<\/span><strong id=\"rep-unique\" style=\"color:#047857;\">--<\/strong><\/div>\r\n                        <div class=\"pth-stat-item\"><span>Frames with Faces:<\/span><strong id=\"rep-frames\" style=\"color:#047857;\">--<\/strong><\/div>\r\n                        <div class=\"pth-stat-item\"><span>Overall Gender:<\/span><strong id=\"rep-gender\" style=\"color:#047857;\">--<\/strong><\/div>\r\n                        <div class=\"pth-stat-item\"><span>Average Video Age:<\/span><strong id=\"rep-age\" style=\"color:#047857;\">--<\/strong><\/div>\r\n                        <div class=\"pth-stat-item\"><span>Child Detections:<\/span><strong id=\"rep-child\" style=\"color:#047857;\">--<\/strong><\/div>\r\n                        <div style=\"margin-top:12px; font-size:0.75rem; color:#059669; text-align:center;\">Summary across selected timeline<\/div>\r\n                    <\/div>\r\n                <\/div>\r\n            <\/div>\r\n\r\n        <\/div>\r\n    <\/div>\r\n<\/div>\r\n\r\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/@vladmandic\/face-api\/dist\/face-api.min.js\" data-no-optimize=\"1\"><\/script>\r\n<script src=\"https:\/\/unpkg.com\/@ffmpeg\/ffmpeg@0.11.6\/dist\/ffmpeg.min.js\" data-no-optimize=\"1\"><\/script>\r\n\t\t\t\t\t\t\t\r\n<script data-no-optimize=\"1\">\r\ndocument.addEventListener('DOMContentLoaded', () => {\r\n    window.pthVideoBlurApp = {\r\n        videoFile: null,\r\n        totalFrames: 0,\r\n        ffmpeg: null,\r\n        isProcessing: false,\r\n        isEncodingPhase: false,\r\n        startTime: 0,\r\n\r\n        init: function() {\r\n            this.cacheDOM();\r\n            this.attachEvents();\r\n        },\r\n\r\n        cacheDOM: function() {\r\n            this.UI = {\r\n                wrapper: document.getElementById('pth-vid-blur-wrapper'),\r\n                dz: document.getElementById('pth-vid-drop-zone'),\r\n                fileIn: document.getElementById('pth-vid-file'),\r\n                inputArea: document.getElementById('pth-vid-input-area'),\r\n                playerArea: document.getElementById('pth-vid-player-area'),\r\n                video: document.getElementById('pth-source-video'),\r\n                canvas: document.getElementById('pth-render-canvas'),\r\n                ctx: document.getElementById('pth-render-canvas').getContext('2d'),\r\n                \r\n                controlsPanel: document.getElementById('pth-controls-panel'),\r\n                targetSel: document.getElementById('pth-vid-target'),\r\n                styleSel: document.getElementById('pth-vid-style'),\r\n                intSli: document.getElementById('pth-vid-intensity'),\r\n                intVal: document.getElementById('pth-vid-int-val'),\r\n                \r\n                trimStart: document.getElementById('pth-vid-trim-start'),\r\n                trimEnd: document.getElementById('pth-vid-trim-end'),\r\n                resSel: document.getElementById('pth-vid-resolution'),\r\n                qualSel: document.getElementById('pth-vid-quality'),\r\n\r\n                startBtn: document.getElementById('pth-vid-start-btn'),\r\n                clearBtn: document.getElementById('pth-vid-clear-btn'),\r\n                fullBtn: document.getElementById('pth-vid-full-btn'),\r\n                presets: document.getElementById('pth-vid-presets'),\r\n                \r\n                progUI: document.getElementById('pth-processing-ui'),\r\n                \r\n                phasePrep: document.getElementById('pth-phase-prep'),\r\n                prepMsg: document.getElementById('pth-prep-msg'),\r\n                prepPct: document.getElementById('pth-prep-pct'),\r\n                prepBar: document.getElementById('pth-prep-bar'),\r\n\r\n                phaseRender: document.getElementById('pth-phase-render'),\r\n                statusMsg: document.getElementById('pth-status-msg'),\r\n                statusPct: document.getElementById('pth-status-pct'),\r\n                progBar: document.getElementById('pth-prog-bar'),\r\n                statFrames: document.getElementById('pth-stat-frames'),\r\n                statTime: document.getElementById('pth-stat-time'),\r\n                \r\n                phaseEncode: document.getElementById('pth-phase-encode'),\r\n                encodeMsg: document.getElementById('pth-encode-msg'),\r\n                encodePct: document.getElementById('pth-encode-pct'),\r\n                encodeBar: document.getElementById('pth-encode-bar'),\r\n\r\n                toast: document.getElementById('pth-vid-toast'),\r\n                liveStatsBox: document.getElementById('pth-live-stats-box'),\r\n                liveTotal: document.getElementById('live-total'),\r\n                liveGender: document.getElementById('live-gender'),\r\n                liveAge: document.getElementById('live-age'),\r\n                finalReportBox: document.getElementById('pth-final-report-box'),\r\n                repUnique: document.getElementById('rep-unique'),\r\n                repFrames: document.getElementById('rep-frames'),\r\n                repGender: document.getElementById('rep-gender'),\r\n                repAge: document.getElementById('rep-age'),\r\n                repChild: document.getElementById('rep-child')\r\n            };\r\n        },\r\n\r\n        showToast: function(msg, isError = false) {\r\n            this.UI.toast.innerHTML = msg;\r\n            this.UI.toast.style.background = isError ? '#ef4444' : '#10b981';\r\n            this.UI.toast.classList.add('show');\r\n            setTimeout(() => this.UI.toast.classList.remove('show'), 4000);\r\n        },\r\n\r\n        attachEvents: function() {\r\n            const self = this;\r\n            \r\n            this.UI.fullBtn.addEventListener('click', () => {\r\n                self.UI.wrapper.classList.toggle('is-fullscreen');\r\n                self.UI.fullBtn.innerHTML = self.UI.wrapper.classList.contains('is-fullscreen') ? '\u27f2 Exit Full Screen' : '\u26f6 Full Screen';\r\n            });\r\n\r\n            this.UI.dz.addEventListener('click', () => self.UI.fileIn.click());\r\n            this.UI.dz.addEventListener('dragover', e => { e.preventDefault(); self.UI.dz.classList.add('active'); });\r\n            this.UI.dz.addEventListener('dragleave', () => self.UI.dz.classList.remove('active'));\r\n            this.UI.dz.addEventListener('drop', e => { e.preventDefault(); self.UI.dz.classList.remove('active'); if (e.dataTransfer.files.length) self.loadVideo(e.dataTransfer.files[0]); });\r\n            this.UI.fileIn.addEventListener('change', e => { if (e.target.files.length) self.loadVideo(e.target.files[0]); });\r\n            this.UI.intSli.addEventListener('input', e => { this.UI.intVal.innerText = e.target.value; });\r\n            \r\n            this.UI.clearBtn.addEventListener('click', () => {\r\n                if(self.isProcessing) return alert(\"Processing in progress. Please wait or refresh the page.\");\r\n                self.UI.playerArea.style.display = 'none'; self.UI.inputArea.style.display = 'block'; self.UI.clearBtn.style.display = 'none';\r\n                self.UI.liveStatsBox.style.display = 'none'; self.UI.finalReportBox.style.display = 'none';\r\n                self.videoFile = null; self.UI.video.src = '';\r\n            });\r\n\r\n            this.UI.presets.querySelectorAll('.pth-preset-btn').forEach(btn => {\r\n                btn.addEventListener('click', () => {\r\n                    const preset = btn.getAttribute('data-preset');\r\n                    if (preset === 'news') { self.UI.styleSel.value = 'pixel'; self.UI.intSli.value = 25; }\r\n                    else if (preset === 'social') { self.UI.styleSel.value = 'blur'; self.UI.intSli.value = 20; self.UI.targetSel.value = 'all'; }\r\n                    else if (preset === 'child') { self.UI.styleSel.value = 'frosted'; self.UI.intSli.value = 15; self.UI.targetSel.value = 'child'; }\r\n                    else if (preset === 'full') { self.UI.styleSel.value = 'bar'; self.UI.intSli.value = 30; }\r\n                    self.UI.intVal.innerText = self.UI.intSli.value;\r\n                    self.showToast('\u2705 Preset applied');\r\n                });\r\n            });\r\n\r\n            this.UI.startBtn.addEventListener('click', () => self.processVideo());\r\n        },\r\n\r\n        loadVideo: function(file) {\r\n            if (!file.type.startsWith('video\/')) return this.showToast('\u26a0\ufe0f Please upload a valid video file (MP4, WebM).', true);\r\n            if (file.size > 50 * 1024 * 1024) return this.showToast('\u26a0\ufe0f Video is too large! Please upload a file smaller than 50MB.', true);\r\n            \r\n            this.videoFile = file;\r\n            this.UI.video.src = URL.createObjectURL(file);\r\n            \r\n            this.UI.video.onloadedmetadata = () => {\r\n                this.UI.trimEnd.value = this.UI.video.duration.toFixed(1);\r\n            };\r\n\r\n            this.UI.inputArea.style.display = 'none'; this.UI.playerArea.style.display = 'block';\r\n            this.UI.controlsPanel.style.display = 'block'; this.UI.progUI.style.display = 'none';\r\n            this.UI.video.style.display = 'block'; this.UI.canvas.style.display = 'none';\r\n            this.UI.clearBtn.style.display = 'inline-flex'; this.UI.finalReportBox.style.display = 'none';\r\n            this.UI.video.play().then(() => { this.UI.video.pause(); }); \r\n        },\r\n\r\n        seekToFrame: function(time) {\r\n            return new Promise((resolve) => {\r\n                const onSeeked = () => { this.UI.video.removeEventListener('seeked', onSeeked); resolve(); };\r\n                this.UI.video.addEventListener('seeked', onSeeked);\r\n                this.UI.video.currentTime = time;\r\n            });\r\n        },\r\n\r\n        updatePrepProgress: function(msg, pct) {\r\n            this.UI.prepMsg.innerHTML = pct >= 100 ? `\u2705 ${msg}` : `<span class=\"pth-spinner\" style=\"border-top-color:#d97706;\"><\/span> ${msg}`;\r\n            this.UI.prepPct.innerText = `${pct}%`; this.UI.prepBar.style.width = `${pct}%`;\r\n        },\r\n\r\n        updateRenderProgress: function(msg, pct, framesText = \"\") {\r\n            this.UI.statusMsg.innerHTML = pct >= 100 ? `\u2705 ${msg}` : `<span class=\"pth-spinner\"><\/span> ${msg}`;\r\n            this.UI.statusPct.innerText = `${pct}%`; this.UI.progBar.style.width = `${pct}%`;\r\n            if (framesText) this.UI.statFrames.innerText = framesText;\r\n            \r\n            if (pct > 0 && pct < 100 && this.startTime > 0) {\r\n                const elapsed = (Date.now() - this.startTime) \/ 1000;\r\n                const totalEst = elapsed \/ (pct \/ 100);\r\n                const remaining = Math.max(0, totalEst - elapsed);\r\n                this.UI.statTime.innerText = `Est. Time Left: ${Math.ceil(remaining)}s`;\r\n            }\r\n        },\r\n\r\n        updateEncodeProgress: function(msg, pct) {\r\n            this.UI.encodeMsg.innerHTML = pct >= 100 ? `\u2705 ${msg}` : `<span class=\"pth-spinner\" style=\"border-top-color:#10b981;\"><\/span> ${msg}`;\r\n            this.UI.encodePct.innerText = `${pct}%`; this.UI.encodeBar.style.width = `${pct}%`;\r\n        },\r\n\r\n        updateLiveStats: function(total, males, females, avgAge) {\r\n            this.UI.liveTotal.innerText = `${total} Faces`;\r\n            this.UI.liveGender.innerHTML = `\ud83d\udc68 ${males} \u00a0\u2022\u00a0 \ud83d\udc69 ${females}`;\r\n            this.UI.liveAge.innerText = total > 0 ? `~${avgAge} yrs` : '-- yrs';\r\n        },\r\n\r\n        processVideo: async function() {\r\n            if (!this.videoFile || this.isProcessing) return;\r\n            const self = this;\r\n            \r\n            this.isProcessing = true; this.isEncodingPhase = false;\r\n            this.UI.controlsPanel.style.display = 'none'; this.UI.video.style.display = 'none';\r\n            this.UI.canvas.style.display = 'block'; this.UI.finalReportBox.style.display = 'none';\r\n            this.UI.liveStatsBox.style.display = 'block';\r\n            \r\n            this.UI.progUI.style.display = 'block';\r\n            this.UI.phasePrep.classList.remove('inactive'); this.UI.phaseRender.classList.add('inactive'); this.UI.phaseEncode.classList.add('inactive');\r\n            \r\n            this.updatePrepProgress(\"Initializing Engines...\", 0);\r\n            this.updateRenderProgress(\"Waiting for engines...\", 0);\r\n            this.updateEncodeProgress(\"Waiting for frames...\", 0);\r\n\r\n            const target = this.UI.targetSel.value;\r\n            const style = this.UI.styleSel.value;\r\n            const intensity = parseInt(this.UI.intSli.value);\r\n\r\n            let reportData = { maxFacesInSingleFrame: 0, framesWithFaces: 0, totalMaleDetections: 0, totalFemaleDetections: 0, totalChildDetections: 0, sumOfAllAges: 0, totalDetectionsOverall: 0 };\r\n\r\n            try {\r\n                \/\/ --- PHASE 1: PREPARATION ---\r\n                this.updatePrepProgress(\"Loading AI Models (~5MB)...\", 30);\r\n                if (!faceapi.nets.ssdMobilenetv1.isLoaded) {\r\n                    await faceapi.nets.ssdMobilenetv1.loadFromUri('https:\/\/cdn.jsdelivr.net\/npm\/@vladmandic\/face-api\/model\/');\r\n                    await faceapi.nets.ageGenderNet.loadFromUri('https:\/\/cdn.jsdelivr.net\/npm\/@vladmandic\/face-api\/model\/');\r\n                }\r\n\r\n               this.updatePrepProgress(\"Starting FFmpeg Core...\", 70);\r\n                if (!this.ffmpeg) {\r\n                    const { createFFmpeg } = FFmpeg;\r\n                    \/* Single-thread core (core-st) + mainName:'main' \u2014 runs WITHOUT SharedArrayBuffer,\r\n                       so no COOP\/COEP headers needed. Keeps AdSense + YouTube embeds working. *\/\r\n                    var PTH_CORE = 'https:\/\/cdn.jsdelivr.net\/npm\/@ffmpeg\/core-st@0.11.1\/dist\/ffmpeg-core.js';\r\n                    var makeFFmpeg = function () {\r\n                        var f = createFFmpeg({ log: false, mainName: 'main', corePath: PTH_CORE });\r\n                        f.setProgress(({ ratio }) => {\r\n                            if (self.isEncodingPhase) {\r\n                                const p = Math.max(0, Math.min(100, Math.round(ratio * 100)));\r\n                                self.updateEncodeProgress(`Compiling Final Video...`, p);\r\n                            }\r\n                        });\r\n                        return f;\r\n                    };\r\n                    var loadWithTimeout = function (ms) {\r\n                        return Promise.race([\r\n                            self.ffmpeg.load(),\r\n                            new Promise(function (_, reject) {\r\n                                setTimeout(function () { reject(new Error('FFMPEG_TIMEOUT')); }, ms);\r\n                            })\r\n                        ]);\r\n                    };\r\n                    this.ffmpeg = makeFFmpeg();\r\n                    try {\r\n                        await loadWithTimeout(45000);\r\n                    } catch (err) {\r\n                        self.updatePrepProgress(\"Retrying FFmpeg Core...\", 70);\r\n                        self.ffmpeg = makeFFmpeg();\r\n                        await self.ffmpeg.load();\r\n                    }\r\n                }\r\n\r\n                this.updatePrepProgress(\"Allocating Memory...\", 90);\r\n                const vw = this.UI.video.videoWidth || 640; const vh = this.UI.video.videoHeight || 360;\r\n                this.UI.canvas.width = vw; this.UI.canvas.height = vh;\r\n                const displaySize = { width: vw, height: vh };\r\n                \r\n                const FPS = 24; const duration = this.UI.video.duration;\r\n                if (!duration || !isFinite(duration)) throw new Error(\"Could not determine video length.\");\r\n                \r\n                const tStart = Math.max(0, parseFloat(this.UI.trimStart.value) || 0);\r\n                const tEnd = Math.min(duration, parseFloat(this.UI.trimEnd.value) || duration);\r\n                const startFrame = Math.floor(tStart * FPS);\r\n                const endFrame = Math.floor(tEnd * FPS);\r\n                const totalFramesToProcess = endFrame - startFrame;\r\n\r\n                const { fetchFile } = FFmpeg;\r\n\r\n                this.updatePrepProgress(\"Engines Ready!\", 100);\r\n                \/\/ DYNAMIC HIDE: Remove Prep bar, show Render bar\r\n                this.UI.phasePrep.classList.add('inactive'); this.UI.phaseRender.classList.remove('inactive');\r\n                this.startTime = Date.now(); \r\n\r\n                \/\/ --- PHASE 2: PROCESSING & RENDERING ---\r\n                for (let i = startFrame; i <= endFrame; i++) {\r\n                    const time = i \/ FPS;\r\n                    if (time > duration) break;\r\n\r\n                    await this.seekToFrame(time);\r\n                    const detections = await faceapi.detectAllFaces(this.UI.video, new faceapi.SsdMobilenetv1Options({ minConfidence: 0.25 })).withAgeAndGender();\r\n                    this.UI.ctx.drawImage(this.UI.video, 0, 0, vw, vh);\r\n\r\n                    let cMales = 0; let cFemales = 0; let cTotalAge = 0;\r\n\r\n                    if (detections.length > 0) {\r\n                        const resizedDetections = faceapi.resizeResults(detections, displaySize);\r\n                        reportData.framesWithFaces++;\r\n                        if (detections.length > reportData.maxFacesInSingleFrame) reportData.maxFacesInSingleFrame = detections.length;\r\n                        \r\n                        resizedDetections.forEach(det => {\r\n                            const isChild = det.age < 16; const isMale = det.gender === 'male';\r\n                            if (isMale) { cMales++; reportData.totalMaleDetections++; } else { cFemales++; reportData.totalFemaleDetections++; }\r\n                            if (isChild) reportData.totalChildDetections++;\r\n                            cTotalAge += det.age; reportData.sumOfAllAges += det.age; reportData.totalDetectionsOverall++;\r\n\r\n                            let shouldCensor = false;\r\n                            if (target === 'all' || (target === 'male' && isMale) || (target === 'female' && !isMale) || (target === 'adult' && !isChild) || (target === 'child' && isChild)) shouldCensor = true;\r\n                            if (!shouldCensor) return;\r\n\r\n                            const box = det.detection.box; \r\n                            const padX = box.width * 0.25; const padY = box.height * 0.25;\r\n                            const x = Math.max(0, box.x - padX); const y = Math.max(0, box.y - padY);\r\n                            const w = Math.min(vw - x, box.width + (padX * 2)); const h = Math.min(vh - y, box.height + (padY * 2));\r\n\r\n                            const tCan = document.createElement('canvas'); tCan.width = w; tCan.height = h;\r\n                            const tCtx = tCan.getContext('2d'); tCtx.drawImage(this.UI.canvas, x, y, w, h, 0, 0, w, h);\r\n\r\n                            if (style === 'blur') {\r\n                                this.UI.ctx.save(); this.UI.ctx.filter = `blur(${intensity}px)`;\r\n                                this.UI.ctx.drawImage(tCan, 0, 0, w, h, x, y, w, h); this.UI.ctx.restore();\r\n                            } else if (style === 'frosted') { \r\n                                this.UI.ctx.save();\r\n                                this.UI.ctx.filter = `blur(${intensity}px) contrast(1.2) brightness(1.1)`;\r\n                                this.UI.ctx.drawImage(tCan, 0, 0, w, h, x, y, w, h);\r\n                                this.UI.ctx.fillStyle = 'rgba(255,255,255,0.15)'; \r\n                                this.UI.ctx.fillRect(x, y, w, h);\r\n                                this.UI.ctx.restore();\r\n                            } else if (style === 'pixel') {\r\n                                const pSize = intensity; const sw = Math.ceil(w \/ pSize); const sh = Math.ceil(h \/ pSize);\r\n                                tCtx.imageSmoothingEnabled = false; tCtx.drawImage(tCan, 0, 0, w, h, 0, 0, sw, sh);\r\n                                tCtx.drawImage(tCan, 0, 0, sw, sh, 0, 0, w, h); this.UI.ctx.drawImage(tCan, 0, 0, w, h, x, y, w, h);\r\n                            } else if (style === 'bar') {\r\n                                this.UI.ctx.fillStyle = '#000000'; this.UI.ctx.fillRect(x, y + (h*0.1), w, h*0.8);\r\n                            } else if (style === 'emoji') {\r\n                                const emojis = ['\ud83d\ude0e','\ud83d\udd76\ufe0f','\ud83e\udd16','\ud83d\udd12','\ud83d\udc64']; const emoji = emojis[Math.floor(Math.random() * emojis.length)];\r\n                                this.UI.ctx.font = `${Math.min(w, h) * 0.8}px Arial`; this.UI.ctx.textAlign = 'center';\r\n                                this.UI.ctx.textBaseline = 'middle'; this.UI.ctx.fillText(emoji, x + w \/ 2, y + h \/ 2);\r\n                            }\r\n                        });\r\n                        const avgAge = (cTotalAge \/ detections.length).toFixed(1);\r\n                        this.updateLiveStats(detections.length, cMales, cFemales, avgAge);\r\n                    } else { this.updateLiveStats(0, 0, 0, 0); }\r\n\r\n                    const dataUrl = this.UI.canvas.toDataURL('image\/jpeg', 0.9);\r\n                    let ffFrame = i - startFrame;\r\n                    this.ffmpeg.FS('writeFile', `frame_${ffFrame}.jpg`, await fetchFile(dataUrl));\r\n\r\n                    const pct = Math.round((ffFrame \/ totalFramesToProcess) * 100);\r\n                    this.updateRenderProgress(\"Analyzing & Blurring Frames...\", pct, `Frames: ${ffFrame} \/ ${totalFramesToProcess}`);\r\n                }\r\n\r\n                \/\/ --- PHASE 3: FINAL ENCODING ---\r\n                this.updateRenderProgress(\"Frame Analysis Complete\", 100);\r\n                this.UI.statTime.innerText = \"Frames ready.\";\r\n                \/\/ DYNAMIC HIDE: Remove Render bar, show Encode bar\r\n                this.UI.phaseRender.classList.add('inactive'); this.UI.phaseEncode.classList.remove('inactive');\r\n                \r\n                this.isEncodingPhase = true;\r\n                this.updateEncodeProgress(\"Starting Video Compiler...\", 0);\r\n                await new Promise(r => setTimeout(r, 100)); \r\n\r\n                this.ffmpeg.FS('writeFile', 'input.mp4', await fetchFile(this.videoFile));\r\n\r\n                let ffmpegArgs = [\r\n                    '-framerate', `${FPS}`, \r\n                    '-i', 'frame_%d.jpg', \r\n                    '-ss', `${tStart}`, \r\n                    '-t', `${tEnd - tStart}`, \r\n                    '-i', 'input.mp4', \r\n                    '-map', '0:v', \r\n                    '-map', '1:a?', \r\n                    '-c:v', 'libx264', \r\n                    '-crf', this.UI.qualSel.value, \r\n                    '-pix_fmt', 'yuv420p'\r\n                ];\r\n\r\n                if (this.UI.resSel.value !== 'original') {\r\n                    ffmpegArgs.push('-vf', `scale=${this.UI.resSel.value}:-2`);\r\n                }\r\n                ffmpegArgs.push('-shortest', 'output.mp4');\r\n\r\n                await this.ffmpeg.run(...ffmpegArgs);\r\n\r\n                this.updateEncodeProgress(\"Encoding Complete. Generating File...\", 99);\r\n\r\n                const data = this.ffmpeg.FS('readFile', 'output.mp4');\r\n                const blob = new Blob([data.buffer], { type: 'video\/mp4' });\r\n                const finalUrl = URL.createObjectURL(blob);\r\n\r\n                const a = document.createElement('a'); a.style.display = 'none'; a.href = finalUrl;\r\n                a.download = 'PTH-AI-Blurred-Video.mp4'; document.body.appendChild(a); a.click();\r\n\r\n                this.updateEncodeProgress(\"Video Downloaded!\", 100);\r\n                this.showToast('\ud83c\udf89 Video successfully anonymized and downloaded!');\r\n\r\n                \/\/ Final Report\r\n                this.UI.liveStatsBox.style.display = 'none'; \r\n                const overAllAvgAge = reportData.totalDetectionsOverall > 0 ? Math.round(reportData.sumOfAllAges \/ reportData.totalDetectionsOverall) : 0;\r\n                const malePct = reportData.totalDetectionsOverall > 0 ? Math.round((reportData.totalMaleDetections \/ reportData.totalDetectionsOverall) * 100) : 0;\r\n                const femalePct = reportData.totalDetectionsOverall > 0 ? Math.round((reportData.totalFemaleDetections \/ reportData.totalDetectionsOverall) * 100) : 0;\r\n\r\n                this.UI.repUnique.innerText = `~${reportData.maxFacesInSingleFrame} Unique Persons`;\r\n                this.UI.repFrames.innerText = `${reportData.framesWithFaces} out of ${totalFramesToProcess}`;\r\n                this.UI.repGender.innerText = `\ud83d\udc68 ${malePct}% | \ud83d\udc69 ${femalePct}%`;\r\n                this.UI.repAge.innerText = `${overAllAvgAge} Years`;\r\n                this.UI.repChild.innerText = `${reportData.totalChildDetections} Times`;\r\n                \r\n                this.UI.finalReportBox.style.display = 'block';\r\n                \r\n                for (let i = 0; i <= totalFramesToProcess; i++) {\r\n                    try { this.ffmpeg.FS('unlink', `frame_${i}.jpg`); } catch(e){}\r\n                }\r\n                try { this.ffmpeg.FS('unlink', 'input.mp4'); } catch(e){}\r\n                try { this.ffmpeg.FS('unlink', 'output.mp4'); } catch(e){}\r\n\r\n            } catch (err) {\r\n                if(this.isEncodingPhase) this.updateEncodeProgress(\"\u274c Encoding Error.\", 0);\r\n                else this.updateRenderProgress(\"\u274c Error during processing.\", 0);\r\n                console.error(err);\r\n                this.showToast(err.message || 'Processing failed.', true);\r\n            } finally {\r\n                this.isProcessing = false; this.isEncodingPhase = false;\r\n                this.UI.startBtn.innerText = \"\ud83d\ude80 Start AI Processing & Export\";\r\n                this.UI.startBtn.disabled = false;\r\n            }\r\n        }\r\n    };\r\n\r\n    if (!window.pthVideoBlurApp.initialized) {\r\n        window.pthVideoBlurApp.initialized = true;\r\n        window.pthVideoBlurApp.init();\r\n    }\r\n});\r\n<\/script>\r\n\t<script data-no-optimize=\"1\" data-no-minify=\"1\" data-cfasync=\"false\">\r\n(function () {\r\n  'use strict';\r\n  function mount() {\r\n    var panel = document.getElementById('pth-fbs-tab-video');\r\n    var tool  = document.getElementById('pth-vid-blur-wrapper');\r\n    if (!panel || !tool) return false;\r\n    if (tool.parentNode !== panel) {\r\n      var ph = document.getElementById('pth-fbs-empty-video');\r\n      if (ph && ph.parentNode) ph.parentNode.removeChild(ph);\r\n      tool.style.margin = '0';\r\n      panel.appendChild(tool);\r\n    }\r\n    return true;\r\n  }\r\n  if (mount()) return;\r\n  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', mount);\r\n  window.addEventListener('load', function () { setTimeout(mount, 60); });\r\n  if (typeof MutationObserver !== 'undefined') {\r\n    var o = new MutationObserver(function () { if (mount()) o.disconnect(); });\r\n    o.observe(document.body || document.documentElement, { childList: true, subtree: true });\r\n    setTimeout(function () { o.disconnect(); }, 10000);\r\n  }\r\n})();\r\n<\/script>\n\n\n\n<style>\n.pth-container{max-width:1480px;margin:0 auto;padding:0 20px;box-sizing:border-box}\n.pth-feature-wrap{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:20px;margin-bottom:40px}\n.pth-feat-box{border:1px solid #e2e8f0;background:#fff;padding:24px;border-radius:12px;box-shadow:0 4px 6px -1px rgba(0,0,0,0.02);transition:all .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.05rem;font-weight:800;color:#0f172a;margin:0 0 10px;display:flex;align-items:center;gap:10px}\n.pth-feat-text{font-size:.9rem;color:#1e293b;line-height:1.6;margin:0;font-weight:500}\n.pth-steps-wrap{border:1px solid #e2e8f0;background:#fff;padding:40px 30px;border-radius:16px;text-align:center;box-shadow:0 4px 6px -1px rgba(0,0,0,0.02);margin-bottom:40px;width:100%;box-sizing:border-box}\n.pth-steps-title{font-size:1.2rem;font-weight:800;color:#0f172a;text-transform:uppercase;margin:0 0 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:#2563eb;color:#fff;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(37,99,235,.3)}\n.pth-step-name{font-weight:800;color:#0f172a;margin:0 0 8px;font-size:1rem}\n.pth-step-desc{font-size:.9rem;color:#1e293b;line-height:1.5;margin:0;font-weight:500}\n<\/style>\n<div class=\"pth-container\">\n  <div class=\"pth-feature-wrap\">\n    <div class=\"pth-feat-box\">\n      <p class=\"pth-feat-title\"><span aria-hidden=\"true\">\ud83d\udfe2<\/span> Two Tools, One Page<\/p>\n      <p class=\"pth-feat-text\">The Image tab and Video tab share the same Face Blur Studio, so you don&#8217;t have to hunt for a separate video tool if a photo job turns into a video job halfway through.<\/p>\n    <\/div>\n    <div class=\"pth-feat-box\">\n      <p class=\"pth-feat-title\"><span aria-hidden=\"true\">\ud83d\udd35<\/span> Automatic Detection, Age &#038; Gender Read<\/p>\n      <p class=\"pth-feat-text\">Faces are found automatically \u2014 no manual boxes to drag. The sidebar also shows a rough age and gender estimate per face, useful if you need to confirm children were caught by the blur.<\/p>\n    <\/div>\n    <div class=\"pth-feat-box\">\n      <p class=\"pth-feat-title\"><span aria-hidden=\"true\">\ud83d\udfe3<\/span> Style Presets for Both Formats<\/p>\n      <p class=\"pth-feat-text\">News Style, Social Safe, Child Protection, and Full Anonymize apply sensible blur strength and coverage instantly, on both images and video frames, instead of you guessing a slider value.<\/p>\n    <\/div>\n  <\/div>\n  <div class=\"pth-steps-wrap\">\n    <p class=\"pth-steps-title\">How to Use Face Blur Studio<\/p>\n    <div class=\"pth-steps-grid\">\n      <div class=\"pth-step-item\">\n        <div class=\"pth-step-num\" aria-hidden=\"true\">1<\/div>\n        <p class=\"pth-step-name\">Pick Image or Video Tab<\/p>\n        <p class=\"pth-step-desc\">Choose the tab that matches your file type at the top of the tool.<\/p>\n      <\/div>\n      <div class=\"pth-step-item\">\n        <div class=\"pth-step-num\" aria-hidden=\"true\">2<\/div>\n        <p class=\"pth-step-name\">Drop Your File In<\/p>\n        <p class=\"pth-step-desc\">Drag and drop, or click the drop zone to browse. Nothing leaves your browser.<\/p>\n      <\/div>\n      <div class=\"pth-step-item\">\n        <div class=\"pth-step-num\" aria-hidden=\"true\">3<\/div>\n        <p class=\"pth-step-name\">Choose a Preset or Adjust<\/p>\n        <p class=\"pth-step-desc\">Tap a preset like Child Protection, or fine-tune blur style and intensity yourself.<\/p>\n      <\/div>\n      <div class=\"pth-step-item\">\n        <div class=\"pth-step-num\" aria-hidden=\"true\">4<\/div>\n        <p class=\"pth-step-name\">Check the Result and Download<\/p>\n        <p class=\"pth-step-desc\">Use the before\/after slider (image) or preview (video), then download the finished file.<\/p>\n      <\/div>\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-why-we-put-image-and-video-blur-in-one-tool\" class=\"wp-block-heading\">\ud83d\udd34 <strong>Why We Put Image and Video Blur in One Tool<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">We used to run these as two separate tools. The problem was simple: if you were blurring faces in a batch of event photos and then needed to do the same thing to the event highlight video, you had to leave the page, search for the video version, and start over. Now both live under one Face Blur Studio \u2014 click a tab, keep working.<\/p>\n\n\n\n<h2 id=\"\ud83d\udfe1-whats-actually-happening-when-you-click-detect\" class=\"wp-block-heading\">\ud83d\udfe1 <strong>What&#8217;s Actually Happening When You Click &#8220;Detect&#8221;<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The tool loads a small face-detection model (built on <a href=\"https:\/\/github.com\/vladmandic\/face-api\" rel=\"noreferrer noopener\" target=\"_blank\">face-api.js<\/a>, a JavaScript port of a face recognition model) directly into your browser tab. It scans the image or each video frame for face-shaped patterns, estimates a bounding box, and \u2014 where the model is confident enough \u2014 a rough age and gender. None of this touches a server. If your Wi-Fi died mid-process, the tool wouldn&#8217;t notice, because it was never talking to anything outside your tab.<\/p>\n\n\n\n<h3 id=\"\ud83d\udfe2-a-real-example\" class=\"wp-block-heading\">\ud83d\udfe2 <strong>A Real Example<\/strong><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Say you upload a school sports day photo with 14 kids in frame. Detection finds 12 of them cleanly. Two are turned sideways near the edge of the photo and get missed \u2014 this is the single most common gap we see, and it&#8217;s worth checking for before you publish anything. Switch to Child Protection preset, review the result in the before\/after slider, and manually note any face the model skipped so you can crop it out or blur it by hand.<\/p>\n\n\n\n<h4 id=\"video-works-the-same-way-frame-by-frame\" class=\"wp-block-heading\">Video Works the Same Way, Frame by Frame<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">For video, the same detection runs on individual frames while <a href=\"https:\/\/ffmpegwasm.netlify.app\/\" rel=\"noreferrer noopener\" target=\"_blank\">FFmpeg compiled to WebAssembly<\/a> handles the trimming and re-encoding. This is why video processing takes noticeably longer than a single photo \u2014 a 30-second clip at normal frame rate means detection runs dozens of times, not once.<\/p>\n\n\n\n<h2 id=\"\ud83d\udd34-common-mistakes-and-what-this-tool-doesnt-do\" class=\"wp-block-heading\">\ud83d\udd34 <strong>Common Mistakes and What This Tool Doesn&#8217;t Do<\/strong><\/h2>\n\n\n<figure class=\"pth-article-figure pth-img-left\" style=\"float:left; width:700px; max-width:100%; margin:4px 28px 16px 0; clear:left;\"><img decoding=\"async\" src=\"https:\/\/schoolict.net\/tools\/wp-content\/uploads\/2026\/07\/Auto-Censor-Photos-800x447.jpeg\" alt=\"Auto Censor Photos\" width=\"700\" height=\"394\" loading=\"lazy\" data-no-lazy=\"1\" class=\"pth-article-img\" style=\"width:100%;height:auto;display:block;border-radius:10px;border:1px solid #e2e8f0;\"><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The biggest mistake is trusting automatic detection completely on a photo with faces at odd angles, in shadow, or far in the background \u2014 the model isn&#8217;t perfect and it can miss faces the presets don&#8217;t catch. Always scan the result yourself before sharing anything sensitive. On the video side, very shaky or low-light footage produces more missed frames than a stable, well-lit clip. Videos over a couple of minutes will also take a while to process since everything runs on your device&#8217;s CPU, not a server farm \u2014 there&#8217;s no way around that trade-off if you want the &#8220;never leaves your browser&#8221; privacy guarantee.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This also isn&#8217;t a tool for identifying people \u2014 it&#8217;s the opposite. It hides faces; it doesn&#8217;t recognize who they belong to, and no data about the faces is stored or sent anywhere.<\/p>\n\n\n\n<h2 id=\"\ud83d\udfe1-when-youd-actually-reach-for-this\" class=\"wp-block-heading\">\ud83d\udfe1 <strong>When You&#8217;d Actually Reach for This<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Parents sharing school event photos in a class WhatsApp group, use Child Protection so other kids&#8217; faces aren&#8217;t identifiable to strangers who might see a forwarded copy. Someone posting a street interview video to social media uses Social Safe to blur bystanders who didn&#8217;t consent to being filmed. A journalist protecting a source&#8217;s identity in raw footage before it goes to an editor reaches for Full Anonymize. Related tools on the site \u2014 try our <a href=\"#\">Image Background Remover<\/a> if you need to clean up the rest of the photo too, or check our <a href=\"#\">guide to <\/a><a href=\"https:\/\/schoolict.net\/tools\/blur-faces-in-photos-and-videos\/\">safely sharing<\/a><a href=\"#\"> photos online<\/a> for more context on what else to check before posting.<\/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\">\n      <p class=\"pth-faq-q\">Does my photo or video get uploaded anywhere?<\/p>\n      <p class=\"pth-faq-a\">No. Detection and blurring both run in your browser tab using JavaScript and WebAssembly. Your file never reaches our server or anyone else&#8217;s.<\/p>\n    <\/div>\n    <div class=\"pth-faq-card\">\n      <p class=\"pth-faq-q\">Why did it miss a face in my photo?<\/p>\n      <p class=\"pth-faq-a\">Detection struggles most with faces turned far to the side, partly covered, or very small in the frame. Always check the before\/after slider before you rely on it fully.<\/p>\n    <\/div>\n    <div class=\"pth-faq-card\">\n      <p class=\"pth-faq-q\">Why is video processing so much slower than the image tab?<\/p>\n      <p class=\"pth-faq-a\">Video means running face detection on dozens or hundreds of individual frames, then re-encoding the result \u2014 all on your device&#8217;s processor, not a server.<\/p>\n    <\/div>\n    <div class=\"pth-faq-card\">\n      <p class=\"pth-faq-q\">What&#8217;s the difference between the blur presets?<\/p>\n      <p class=\"pth-faq-a\">News Style and Social Safe use a moderate blur that still shows a face is present. Child Protection and Full Anonymize apply stronger coverage meant to make identification impossible.<\/p>\n    <\/div>\n    <div class=\"pth-faq-card\">\n      <p class=\"pth-faq-q\">Can I undo a blur after downloading?<\/p>\n      <p class=\"pth-faq-a\">No. Once a face is blurred and the file is exported, the original pixel data in that area is gone. Keep your original file if you might need it again.<\/p>\n    <\/div>\n    <div class=\"pth-faq-card\">\n      <p class=\"pth-faq-q\">Is there a file size or length limit for video?<\/p>\n      <p class=\"pth-faq-a\">There&#8217;s no hard cap, but longer or higher-resolution clips take proportionally longer to process since everything runs on your CPU. Very long videos may be slow on older laptops.<\/p>\n    <\/div>\n    <div class=\"pth-faq-card\">\n      <p class=\"pth-faq-q\">Does the age and gender estimate actually identify anyone?<\/p>\n      <p class=\"pth-faq-a\">No. It&#8217;s a rough statistical guess from the detection model, shown for context only. Nothing about the estimate is stored, logged, or tied to an identity.<\/p>\n    <\/div>\n  <\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Free AI Face Blur &#038; Anonymizer Studio | Auto Censor Photos Protect privacy instantly with our free AI face blur tool. Automatically detect and censor faces in photos 100% offline in your browser. No image uploads required. \ud83d\udfe2 Two Tools, One Page The Image tab and Video tab share the same Face Blur Studio, so &#8230; <a title=\"Free AI Face Blur &amp; Anonymizer Studio | Auto Censor Photos\" class=\"read-more\" href=\"https:\/\/schoolict.net\/tools\/free-ai-face-blur-anonymizer\/\" aria-label=\"Read more about Free AI Face Blur &amp; Anonymizer Studio | Auto Censor Photos\">Read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":2533,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-2528","page","type-page","status-publish","has-post-thumbnail"],"_links":{"self":[{"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/pages\/2528","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=2528"}],"version-history":[{"count":0,"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/pages\/2528\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/media\/2533"}],"wp:attachment":[{"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/media?parent=2528"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}