{"id":8,"date":"2026-03-30T16:02:22","date_gmt":"2026-03-30T16:02:22","guid":{"rendered":"http:\/\/yepto.nl\/?page_id=8"},"modified":"2026-07-26T13:21:54","modified_gmt":"2026-07-26T13:21:54","slug":"huishoudkunde","status":"publish","type":"page","link":"https:\/\/yepto.nl\/index.php\/huishoudkunde\/","title":{"rendered":"Huishoudkunde"},"content":{"rendered":"\n<!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n     HUISBEHEER \u2014 Modulair huishoudmanagementsysteem v3\n     WordPress Custom HTML blok \u2014 plak in een pagina\n     \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n<link href=\"https:\/\/fonts.googleapis.com\/css2?family=DM+Serif+Display:ital@0;1&amp;family=DM+Sans:wght@300;400;500&amp;display=swap\" rel=\"stylesheet\">\n<style>\n#hb-app *{box-sizing:border-box;margin:0;padding:0;-webkit-tap-highlight-color:transparent}\n#hb-app{\n  --bg:#F7F5F0;--sf:#FFFFFF;--sf2:#F0EDE6;\n  --bd:rgba(0,0,0,0.08);--bd2:rgba(0,0,0,0.15);\n  --tx:#1C1A16;--tx2:#6B6760;--tx3:#A8A49E;\n  --red:#C8402A;--redbg:#FBF0ED;\n  --amb:#9A6A10;--ambbg:#FBF3E3;\n  --grn:#2E7A4F;--grnbg:#E8F5EE;\n  --blu:#1A6898;--blubg:#EAF4FB;\n  --r:14px;--rs:8px;\n  font-family:'DM Sans',sans-serif;background:var(--bg);color:var(--tx);\n  border-radius:var(--r);overflow:hidden;max-width:680px;margin:0 auto;\n}\n#hb-app .top{background:var(--bg);border-bottom:1px solid var(--bd);padding:0 1.25rem;display:flex;align-items:center;justify-content:space-between;height:52px}\n#hb-app .top-t{font-family:'DM Serif Display',serif;font-size:19px}\n#hb-app .top-r{font-size:12px;color:var(--tx3);display:flex;align-items:center;gap:5px}\n#hb-app .content{padding:1.25rem;min-height:480px}\n#hb-app .nav{display:flex;background:var(--sf);border-top:1px solid var(--bd)}\n#hb-app .nb{flex:1;display:flex;flex-direction:column;align-items:center;gap:3px;padding:10px 2px;border:none;background:transparent;cursor:pointer;font-family:inherit;color:var(--tx3);font-size:10px;font-weight:500;transition:color .15s}\n#hb-app .nb svg{width:20px;height:20px;stroke:currentColor;fill:none;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}\n#hb-app .nb.active{color:var(--tx)}\n#hb-app .view{display:none}\n#hb-app .view.active{display:block}\n#hb-app .card{background:var(--sf);border-radius:var(--r);border:1px solid var(--bd);overflow:hidden;margin-bottom:1rem}\n#hb-app .card-h{padding:.875rem 1rem .5rem;display:flex;align-items:center;justify-content:space-between;gap:8px}\n#hb-app .card-t{font-size:11px;font-weight:500;letter-spacing:.06em;text-transform:uppercase;color:var(--tx3)}\n#hb-app .card-c{font-size:11px;color:var(--tx3)}\n#hb-app .task{display:flex;align-items:flex-start;gap:11px;padding:10px 1rem;border-bottom:1px solid var(--bd)}\n#hb-app .task:last-child{border-bottom:none}\n#hb-app .cb{margin-top:2px;flex-shrink:0;width:18px;height:18px;border:1.5px solid var(--bd2);border-radius:5px;cursor:pointer;appearance:none;-webkit-appearance:none;background:var(--sf);position:relative;transition:all .15s}\n#hb-app .cb:checked{background:var(--tx);border-color:var(--tx)}\n#hb-app .cb:checked::after{content:'';position:absolute;left:4px;top:1px;width:5px;height:9px;border:2px solid #fff;border-top:none;border-left:none;transform:rotate(42deg)}\n#hb-app .tb{flex:1;min-width:0}\n#hb-app .tn{font-size:14px;line-height:1.4}\n#hb-app .tt{font-size:12px;color:var(--tx3);margin-top:2px;line-height:1.45;display:flex;flex-wrap:wrap;gap:4px 8px;align-items:center}\n#hb-app .task.done .tn{text-decoration:line-through;color:var(--tx3)}\n#hb-app .pill{display:inline-block;font-size:10px;font-weight:500;padding:2px 7px;border-radius:20px;white-space:nowrap}\n#hb-app .pl-red{background:var(--redbg);color:var(--red)}\n#hb-app .pl-amb{background:var(--ambbg);color:var(--amb)}\n#hb-app .pl-grn{background:var(--grnbg);color:var(--grn)}\n#hb-app .pl-blu{background:var(--blubg);color:var(--blu)}\n#hb-app .pl-gr{background:var(--sf2);color:var(--tx2)}\n#hb-app .meta-i{font-size:11px;color:var(--tx3);display:inline-flex;align-items:center;gap:3px}\n#hb-app .stat-row{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px;margin-bottom:1rem}\n#hb-app .stat{background:var(--sf);border:1px solid var(--bd);border-radius:var(--rs);padding:11px 8px;text-align:center}\n#hb-app .sn{font-size:20px;font-weight:500}\n#hb-app .sl{font-size:10px;color:var(--tx3);margin-top:2px}\n#hb-app .greet{font-family:'DM Serif Display',serif;font-size:24px;line-height:1.2;margin-bottom:1.25rem;letter-spacing:-.4px}\n#hb-app .greet em{font-style:italic;color:var(--tx2)}\n#hb-app .prog{margin-bottom:1rem}\n#hb-app .prog-r{display:flex;justify-content:space-between;margin-bottom:5px}\n#hb-app .prog-l{font-size:12px;color:var(--tx2)}\n#hb-app .prog-v{font-size:12px;color:var(--tx3)}\n#hb-app .bar{height:5px;background:var(--sf2);border-radius:3px;overflow:hidden}\n#hb-app .fill{height:100%;border-radius:3px;background:var(--tx);transition:width .4s}\n#hb-app .btn{padding:8px 16px;border-radius:20px;border:none;background:var(--tx);color:#fff;font-family:inherit;font-size:13px;font-weight:500;cursor:pointer;transition:opacity .15s}\n#hb-app .btn:hover{opacity:.85}\n#hb-app .btn-o{padding:7px 15px;border-radius:20px;border:1px solid var(--bd2);background:transparent;color:var(--tx2);font-family:inherit;font-size:13px;cursor:pointer}\n#hb-app .btn-sm{font-size:11px;padding:4px 10px;border-radius:12px;border:1px solid var(--bd2);background:transparent;color:var(--tx3);cursor:pointer;font-family:inherit}\n#hb-app .btn-sm.danger{color:var(--red);opacity:.7}\n#hb-app .btn-sm:hover{color:var(--tx);border-color:var(--tx)}\n#hb-app .btn-sm.danger:hover{color:var(--red);border-color:var(--red);opacity:1}\n#hb-app .filter-row{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:1rem}\n#hb-app .fb{padding:5px 12px;border-radius:16px;border:1px solid var(--bd2);background:transparent;color:var(--tx2);font-size:12px;cursor:pointer;font-family:inherit}\n#hb-app .fb.on{background:var(--tx);color:#fff;border-color:transparent}\n#hb-app .search{width:100%;font-size:13px;padding:9px 12px;border-radius:var(--rs);border:1px solid var(--bd2);background:var(--sf);color:var(--tx);font-family:inherit;outline:none;margin-bottom:1rem}\n#hb-app .search:focus{border-color:var(--tx)}\n#hb-app .inp{width:100%;font-size:13px;padding:8px 10px;border-radius:var(--rs);border:1px solid var(--bd2);background:var(--bg);color:var(--tx);font-family:inherit;outline:none}\n#hb-app .inp:focus{border-color:var(--tx)}\n#hb-app select.inp{appearance:auto;-webkit-appearance:auto}\n#hb-app .flabel{font-size:11px;font-weight:500;color:var(--tx3);margin-bottom:3px;display:block}\n#hb-app .frow{margin-bottom:10px}\n#hb-app .fgrid{display:grid;grid-template-columns:1fr 1fr;gap:8px}\n#hb-app .modal-bg{display:none;position:fixed;inset:0;background:rgba(0,0,0,0.4);z-index:1000;align-items:flex-end;justify-content:center}\n#hb-app .modal-bg.show{display:flex}\n#hb-app .modal{background:var(--sf);border-radius:var(--r) var(--r) 0 0;width:100%;max-width:680px;max-height:85vh;overflow-y:auto;padding:1.25rem}\n#hb-app .modal-h{display:flex;justify-content:space-between;align-items:center;margin-bottom:1rem}\n#hb-app .modal-t{font-family:'DM Serif Display',serif;font-size:19px}\n#hb-app .modal-x{border:none;background:transparent;font-size:22px;color:var(--tx3);cursor:pointer;line-height:1;padding:4px}\n#hb-app .sec-h{font-size:11px;font-weight:500;letter-spacing:.06em;text-transform:uppercase;color:var(--tx3);margin:1.25rem 0 .5rem}\n#hb-app .sec-h:first-child{margin-top:0}\n#hb-app .adm-tabs{display:grid;grid-template-columns:repeat(4,1fr);gap:6px;margin-bottom:1rem}\n#hb-app .adm-tab{padding:8px 4px;border-radius:var(--rs);border:1px solid var(--bd2);background:transparent;font-family:inherit;font-size:11px;font-weight:500;color:var(--tx2);cursor:pointer;text-align:center}\n#hb-app .adm-tab.active{background:var(--tx);color:#fff;border-color:transparent}\n#hb-app .adm-panel{display:none}\n#hb-app .adm-panel.active{display:block}\n#hb-app .adm-row{display:flex;align-items:center;gap:10px;padding:10px 1rem;border-bottom:1px solid var(--bd)}\n#hb-app .adm-row:last-child{border-bottom:none}\n#hb-app .adm-info{flex:1;min-width:0}\n#hb-app .adm-n{font-size:14px}\n#hb-app .adm-s{font-size:11px;color:var(--tx3);margin-top:1px}\n#hb-app .adm-acts{display:flex;gap:5px;flex-shrink:0}\n#hb-app .add-btn{width:100%;padding:10px;border-radius:var(--rs);border:1px dashed var(--bd2);background:transparent;color:var(--tx3);font-family:inherit;font-size:13px;cursor:pointer;margin-bottom:1rem}\n#hb-app .add-btn:hover{color:var(--tx2);border-color:var(--tx)}\n#hb-app .timer-card{background:var(--tx);color:#fff;border-radius:var(--r);padding:1.25rem;display:flex;align-items:center;justify-content:space-between;margin-bottom:1rem}\n#hb-app .timer-disp{font-family:'DM Serif Display',serif;font-size:34px;letter-spacing:1px}\n#hb-app .timer-ctrl{display:flex;flex-direction:column;gap:6px;align-items:flex-end}\n#hb-app .tbtn{padding:6px 14px;border-radius:20px;border:1px solid rgba(255,255,255,0.3);background:transparent;color:#fff;font-family:inherit;font-size:12px;cursor:pointer}\n#hb-app .tbtn.go{background:#fff;color:var(--tx);border-color:transparent}\n#hb-app .phase-row{display:flex;align-items:center;gap:8px;padding:.875rem 1rem .3rem;border-top:1px solid var(--bd)}\n#hb-app .phase-row:first-child{border-top:none}\n#hb-app .phase-t{font-size:13px;font-weight:500}\n#hb-app .phase-m{font-size:11px;color:var(--tx3);margin-left:auto}\n#hb-app .done-screen{text-align:center;padding:3rem 1rem 2rem}\n#hb-app .done-check{width:64px;height:64px;border-radius:50%;background:var(--grnbg);display:flex;align-items:center;justify-content:center;margin:0 auto 1.25rem}\n#hb-app .done-check svg{width:28px;height:28px;stroke:var(--grn);fill:none;stroke-width:2.5;stroke-linecap:round;stroke-linejoin:round}\n#hb-app .done-t{font-family:'DM Serif Display',serif;font-size:28px;margin-bottom:.5rem}\n#hb-app .done-s{font-size:14px;color:var(--tx2);line-height:1.6}\n#hb-app .reset-hero{background:var(--tx);color:#fff;border-radius:var(--r);padding:1.5rem;margin-bottom:1rem}\n#hb-app .reset-hero h2{font-family:'DM Serif Display',serif;font-size:22px;margin-bottom:.4rem}\n#hb-app .reset-hero p{font-size:13px;opacity:.8;line-height:1.6;margin-bottom:1rem}\n#hb-app .reset-hero .btn{background:#fff;color:var(--tx)}\n#hb-app .day-tabs{display:grid;grid-template-columns:repeat(7,1fr);gap:4px;margin-bottom:1rem}\n#hb-app .day-tab{padding:7px 2px;border-radius:var(--rs);border:1px solid var(--bd2);background:transparent;font-family:inherit;font-size:10px;font-weight:500;color:var(--tx2);cursor:pointer;text-align:center;line-height:1.3}\n#hb-app .day-tab.active{background:var(--tx);color:#fff;border-color:transparent}\n#hb-app .day-tab.done-day{border-color:var(--grn);color:var(--grn)}\n#hb-app .day-tab.active.done-day{background:var(--grn);color:#fff}\n#hb-app .day-panel{display:none}\n#hb-app .day-panel.active{display:block}\n#hb-app .empty{text-align:center;padding:2rem 1rem;color:var(--tx3);font-size:13px;line-height:1.6}\n#hb-app .stat-block{background:var(--sf);border:1px solid var(--bd);border-radius:var(--rs);padding:14px;margin-bottom:8px}\n#hb-app .stat-block-t{font-size:12px;font-weight:500;color:var(--tx2);margin-bottom:8px}\n#hb-app .hist-bar-row{display:flex;align-items:flex-end;gap:4px;height:60px}\n#hb-app .hist-bar{flex:1;background:var(--sf2);border-radius:3px 3px 0 0;position:relative;min-height:3px;transition:height .3s}\n#hb-app .hist-bar.hasval{background:var(--tx)}\n#hb-app .hist-lbls{display:flex;gap:4px;margin-top:4px}\n#hb-app .hist-lbl{flex:1;font-size:9px;color:var(--tx3);text-align:center}\n#hb-app .io-row{display:flex;gap:8px;margin-top:.5rem}\n#hb-app .badge-cnt{background:var(--red);color:#fff;font-size:9px;min-width:15px;height:15px;border-radius:8px;display:inline-flex;align-items:center;justify-content:center;padding:0 4px;margin-left:3px;vertical-align:top}\n@media(max-width:440px){\n  #hb-app .stat-row{grid-template-columns:repeat(2,1fr)}\n  #hb-app .fgrid{grid-template-columns:1fr}\n  #hb-app .adm-tabs{grid-template-columns:repeat(2,1fr)}\n}\n<\/style>\n\n<div id=\"hb-app\">\n  <div class=\"top\">\n    <div class=\"top-t\">Huisbeheer<\/div>\n    <div class=\"top-r\"><span id=\"hb-streak\">\u00b7<\/span><\/div>\n  <\/div>\n  <div class=\"content\">\n    <div class=\"view active\" id=\"v-dash\"><\/div>\n    <div class=\"view\" id=\"v-plan\"><\/div>\n    <div class=\"view\" id=\"v-reset\"><\/div>\n    <div class=\"view\" id=\"v-visit\"><\/div>\n    <div class=\"view\" id=\"v-stats\"><\/div>\n    <div class=\"view\" id=\"v-admin\"><\/div>\n  <\/div>\n  <nav class=\"nav\">\n    <button class=\"nb active\" data-v=\"dash\" onclick=\"HB.nav('dash',this)\"><svg viewBox=\"0 0 24 24\"><path d=\"M3 9l9-7 9 7v11a2 2 0 01-2 2H5a2 2 0 01-2-2z\"><\/path><polyline points=\"9 22 9 12 15 12 15 22\"><\/polyline><\/svg>Vandaag<\/button>\n    <button class=\"nb\" data-v=\"plan\" onclick=\"HB.nav('plan',this)\"><svg viewBox=\"0 0 24 24\"><rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\"><\/rect><line x1=\"16\" y1=\"2\" x2=\"16\" y2=\"6\"><\/line><line x1=\"8\" y1=\"2\" x2=\"8\" y2=\"6\"><\/line><line x1=\"3\" y1=\"10\" x2=\"21\" y2=\"10\"><\/line><\/svg>Planning<\/button>\n    <button class=\"nb\" data-v=\"reset\" onclick=\"HB.nav('reset',this)\"><svg viewBox=\"0 0 24 24\"><polyline points=\"23 4 23 10 17 10\"><\/polyline><path d=\"M20.49 15a9 9 0 11-2.12-9.36L23 10\"><\/path><\/svg>Reset<\/button>\n    <button class=\"nb\" data-v=\"visit\" onclick=\"HB.nav('visit',this)\"><svg viewBox=\"0 0 24 24\"><path d=\"M20.84 4.61a5.5 5.5 0 00-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 00-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 000-7.78z\"><\/path><\/svg>Bezoek<\/button>\n    <button class=\"nb\" data-v=\"stats\" onclick=\"HB.nav('stats',this)\"><svg viewBox=\"0 0 24 24\"><line x1=\"18\" y1=\"20\" x2=\"18\" y2=\"10\"><\/line><line x1=\"12\" y1=\"20\" x2=\"12\" y2=\"4\"><\/line><line x1=\"6\" y1=\"20\" x2=\"6\" y2=\"14\"><\/line><\/svg>Statistiek<\/button>\n    <button class=\"nb\" data-v=\"admin\" onclick=\"HB.nav('admin',this)\"><svg viewBox=\"0 0 24 24\"><circle cx=\"12\" cy=\"12\" r=\"3\"><\/circle><path d=\"M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 010 2.83 2 2 0 01-2.83 0l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-2 2 2 2 0 01-2-2v-.09A1.65 1.65 0 008.6 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83 0 2 2 0 010-2.83l.06-.06a1.65 1.65 0 00.33-1.82 1.65 1.65 0 00-1.51-1H3a2 2 0 01-2-2 2 2 0 012-2h.09A1.65 1.65 0 004.6 8.6a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 010-2.83 2 2 0 012.83 0l.06.06a1.65 1.65 0 001.82.33H9a1.65 1.65 0 001-1.51V3a2 2 0 012-2 2 2 0 012 2v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 0 2 2 0 010 2.83l-.06.06a1.65 1.65 0 00-.33 1.82V9a1.65 1.65 0 001.51 1H21a2 2 0 012 2 2 2 0 01-2 2h-.09a1.65 1.65 0 00-1.51 1z\"><\/path><\/svg>Beheer<\/button>\n  <\/nav>\n\n  <!-- MODAL -->\n  <div class=\"modal-bg\" id=\"hb-modal-bg\" onclick=\"if(event.target===this)HB.closeModal()\">\n    <div class=\"modal\">\n      <div class=\"modal-h\">\n        <div class=\"modal-t\" id=\"hb-modal-t\">Bewerken<\/div>\n        <button class=\"modal-x\" onclick=\"HB.closeModal()\">\u00d7<\/button>\n      <\/div>\n      <div id=\"hb-modal-body\"><\/div>\n    <\/div>\n  <\/div>\n<\/div>\n<script>\nwindow.HB=(function(){\n'use strict';\nvar NS='huisbeheer_v3_';\nfunction sv(k,v){try{localStorage.setItem(NS+k,JSON.stringify(v));}catch(e){}}\nfunction ld(k,d){try{var v=localStorage.getItem(NS+k);return v!==null?JSON.parse(v):d;}catch(e){return d;}}\nfunction esc(s){return String(s==null?'':s).replace(new RegExp(String.fromCharCode(38),'g'),'&amp;').replace(\/<\/g,'&lt;').replace(\/>\/g,'&gt;').replace(\/\"\/g,'&quot;');}\nfunction uid(){return 'x'+Date.now().toString(36)+Math.random().toString(36).slice(2,6);}\nfunction todayStr(){return new Date().toISOString().slice(0,10);}\nfunction daysAgo(dateStr){if(!dateStr)return Infinity;return Math.floor((new Date(todayStr())-new Date(dateStr))\/86400000);}\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550 FREQUENTIES (vast maar uitbreidbaar in code) \u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\nvar FREQS={\n  daily:{label:'Dagelijks',days:1,cls:'pl-red'},\n  weekly:{label:'Wekelijks',days:7,cls:'pl-blu'},\n  biweekly:{label:'Tweewekelijks',days:14,cls:'pl-blu'},\n  monthly:{label:'Maandelijks',days:30,cls:'pl-amb'},\n  quarterly:{label:'Per kwartaal',days:91,cls:'pl-gr'},\n  halfyearly:{label:'Halfjaarlijks',days:182,cls:'pl-gr'},\n  yearly:{label:'Jaarlijks',days:365,cls:'pl-gr'},\n  seasonal:{label:'Seizoensgebonden',days:365,cls:'pl-grn'}\n};\nvar SEASONS={spring:'Lente',summer:'Zomer',autumn:'Herfst',winter:'Winter'};\nfunction currentSeason(){var m=new Date().getMonth()+1;if(m>=3){if(m<=5)return 'spring';}if(m>=6){if(m<=8)return 'summer';}if(m>=9){if(m<=11)return 'autumn';}return 'winter';}\nvar DIFFS={1:'Licht',2:'Gemiddeld',3:'Zwaar'};\nvar RESET_CATS=['Hygi\u00ebne','Veiligheid','Achterstallig onderhoud','Opruimen','Organisatie','Was','Basisvoorraad'];\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550 SEED DATA \u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\nfunction seedRooms(){return[\n  {id:'r_bad',name:'Badkamer'},{id:'r_keuk',name:'Keuken'},{id:'r_woon',name:'Woonkamer'},\n  {id:'r_slaap',name:'Slaapkamer'},{id:'r_hal',name:'Hal'},{id:'r_was',name:'Washok'},\n  {id:'r_balkon',name:'Balkon'},{id:'r_alg',name:'Algemeen'}\n];}\nfunction seedCats(){return[\n  {id:'c_hyg',name:'Hygi\u00ebne'},{id:'c_veil',name:'Veiligheid'},{id:'c_opruim',name:'Opruimen'},\n  {id:'c_org',name:'Organisatie'},{id:'c_was',name:'Was'},{id:'c_onderhoud',name:'Onderhoud'},\n  {id:'c_voorraad',name:'Basisvoorraad'}\n];}\nfunction seedProds(){return[\n  {id:'p1',name:'HG Kalkreiniger',use:'Douchewand, kranen, gootsteen',tags:'Badkamer'},\n  {id:'p2',name:'Microvezeldoekjes',use:'Spiegels en glas, streepvrij',tags:'Overal'},\n  {id:'p3',name:'Wc-reiniger',use:'Inwerken onder de rand',tags:'Toilet'},\n  {id:'p4',name:'Allesreiniger spray',use:'Aanrecht, deurknoppen, apparaten',tags:'Keuken,Woonkamer'},\n  {id:'p5',name:'Witte azijn',use:'Ontkalken, microgolf stomen',tags:'Budget'},\n  {id:'p6',name:'Vaatwasserreiniger',use:'Maandelijkse machinereiniging',tags:'Keuken'},\n  {id:'p7',name:'Geurverspreider',use:'Eerste indruk bij binnenkomst',tags:'Woonkamer'}\n];}\nfunction seedTasks(){\n  function t(name,room,cat,freq,dur,diff,mats,tip,season){\n    return {id:uid(),name:name,roomId:room,catId:cat,freq:freq,duration:dur,difficulty:diff,materials:mats||'',tip:tip||'',season:season||'',lastDone:null,active:true};\n  }\n  return[\n    \/\/ dagelijks\n    t('Afwassen en afdrogen','r_keuk','c_hyg','daily',15,1),\n    t('Aanrecht en eettafel afnemen','r_keuk','c_hyg','daily',5,1),\n    t('Robot stofzuiger aanzetten','r_alg','c_hyg','daily',2,1),\n    t('Opruimronde: alles terug op zijn plek','r_alg','c_opruim','daily',10,1),\n    \/\/ wekelijks\n    t('Toilet volledig schoonmaken','r_bad','c_hyg','weekly',15,2,'p3','Binnen, buiten, bril, deksel, achterkant'),\n    t('Wasbak en kraan poetsen','r_bad','c_hyg','weekly',10,1,'p1'),\n    t('Douche schoonmaken','r_bad','c_hyg','weekly',15,2,'p1'),\n    t('Spiegel streepvrij maken','r_bad','c_hyg','weekly',5,1,'p2'),\n    t('Stofzuigen alle ruimtes','r_alg','c_hyg','weekly',25,2),\n    t('Vloeren dweilen','r_alg','c_hyg','weekly',20,2),\n    t('Prullenbakken legen','r_alg','c_hyg','weekly',5,1),\n    t('Was draaien, ophangen en opruimen','r_was','c_was','weekly',30,2),\n    t('Boodschappen: lijst, halen, opruimen','r_alg','c_voorraad','weekly',60,2),\n    t('Bed verschonen','r_slaap','c_hyg','weekly',10,1),\n    \/\/ tweewekelijks\n    t('Slaapkamer stoffen en dweilen','r_slaap','c_hyg','biweekly',20,2),\n    t('Woonkamer stoffen en oppervlakken','r_woon','c_hyg','biweekly',15,1,'p4'),\n    t('Fornuis en afzuigkap buitenkant','r_keuk','c_hyg','biweekly',15,2,'p4'),\n    \/\/ maandelijks\n    t('Gastentoilet volledig','r_bad','c_hyg','monthly',15,2,'p3'),\n    t('Microgolf van binnen','r_keuk','c_hyg','monthly',10,1,'p5','Citroen + water 3 min opwarmen'),\n    t('Ramen binnenkant','r_alg','c_hyg','monthly',30,2,'p2'),\n    t('Deurknoppen en lichtschakelaars','r_alg','c_hyg','monthly',10,1,'p4'),\n    t('Prullenbakken binnenkant reinigen','r_alg','c_hyg','monthly',10,1),\n    t('Achter en onder meubels stofzuigen','r_woon','c_hyg','monthly',20,2),\n    t('Koelkast controleren op oude producten','r_keuk','c_voorraad','monthly',10,1),\n    \/\/ kwartaal\n    t('Koelkast van binnen reinigen','r_keuk','c_hyg','quarterly',30,2),\n    t('Wasmachine reinigen (trommel + manchet)','r_was','c_onderhoud','quarterly',20,2),\n    t('Vaatwasser reinigen (filter + manchet)','r_keuk','c_onderhoud','quarterly',15,2,'p6'),\n    t('Douchewand en douchekop ontkalken','r_bad','c_hyg','quarterly',25,2,'p1,p5'),\n    t('Plafonds en hoeken plumeaus','r_alg','c_hyg','quarterly',15,1),\n    t('Voordeur spinnenwebben weghalen','r_hal','c_hyg','quarterly',5,1),\n    t('Afzuigkap filter reinigen','r_keuk','c_onderhoud','quarterly',15,2),\n    t('Rookmelders testen','r_alg','c_veil','quarterly',5,1,'','Knop indrukken tot signaal'),\n    t('Matrashoes en kussens wassen (60\u00b0C)','r_slaap','c_hyg','quarterly',15,1),\n    \/\/ halfjaarlijks\n    t('Oven grondig schoonmaken','r_keuk','c_hyg','halfyearly',45,3),\n    t('Keukenkastjes binnenkant','r_keuk','c_org','halfyearly',40,2),\n    t('Matras omdraaien','r_slaap','c_onderhoud','halfyearly',5,2),\n    t('Gordijnen wassen','r_alg','c_was','halfyearly',20,2),\n    t('Medicijnkastje controleren op datums','r_bad','c_veil','halfyearly',10,1),\n    \/\/ jaarlijks\n    t('Vriezer ontdooien en reinigen','r_keuk','c_onderhoud','yearly',60,3),\n    t('Deuren en kozijnen poetsen','r_alg','c_hyg','yearly',45,2),\n    t('Voorraadkast opruimen en reinigen','r_keuk','c_org','yearly',30,2),\n    t('Kledingkast uitzoeken','r_slaap','c_org','yearly',60,2),\n    t('Toiletborstel vervangen','r_bad','c_hyg','yearly',2,1),\n    t('Ramen zemen buitenkant','r_alg','c_hyg','yearly',60,3),\n    t('Brandblusser\/blusdeken controleren','r_alg','c_veil','yearly',5,1),\n    t('Verzekeringen en abonnementen doorlopen','r_alg','c_org','yearly',45,1),\n    \/\/ seizoensgebonden\n    t('Balkon zomerklaar maken','r_balkon','c_onderhoud','seasonal',60,2,'','',\n      'spring'),\n    t('Balkon winterklaar maken','r_balkon','c_onderhoud','seasonal',40,2,'','','autumn'),\n    t('Verwarming ontluchten','r_alg','c_onderhoud','seasonal',20,2,'','','autumn'),\n    t('Winterkleding wisselen met zomerkleding','r_slaap','c_org','seasonal',45,2,'','','spring'),\n    t('Zomerkleding wisselen met winterkleding','r_slaap','c_org','seasonal',45,2,'','','autumn')\n  ];\n}\n\nfunction seedVisit(){\n  function v(name,phase,tip,dur){return {id:uid(),name:name,phase:phase,tip:tip||'',dur:dur||5};}\n  return[\n    v('Gootsteen schuren en kraan poetsen','Badkamer','Kalkrand rond de kraanbasis valt het meest op',5),\n    v('Spiegel streepvrij maken','Badkamer','Microvezeldoek droog napoetsen',3),\n    v('Toilet volledig: binnen, buiten, bril, deksel, achter','Badkamer','',7),\n    v('Vloer dweilen (ook achter toilet)','Badkamer','',5),\n    v('Schone handdoek ophangen','Badkamer','Gevouwen of opgerold',1),\n    v('Prullenbak leegmaken','Badkamer','',1),\n    v('Schoenen opruimen of netjes zetten','Hal \\u0026 woonkamer','Eerste indruk bij binnenkomst',3),\n    v('Kussens opschudden en recht neerleggen','Hal \\u0026 woonkamer','',2),\n    v('Tafel en oppervlakken afstoffen','Hal \\u0026 woonkamer','Vingerstrepen op glanzende vlakken',5),\n    v('Glazen, kopjes en rommel opruimen','Hal \\u0026 woonkamer','',5),\n    v('Kabels netjes wegleggen','Hal \\u0026 woonkamer','',3),\n    v('Aanrechtblad leegmaken en afvegen','Keuken','Leeg aanrecht geeft een georganiseerd gevoel',5),\n    v('Gootsteen schuren','Keuken','',3),\n    v('Prullenbak leegmaken','Keuken','Geur valt meteen op',2),\n    v('Ramen open: 10 minuten ventileren','Finishing touch','Frisse lucht is het eerste wat je voelt',1),\n    v('Kaars of geurverspreider aan','Finishing touch','Subtiel, niet overweldigend',1)\n  ];\n}\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550 STATE \u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\nvar S={\n  rooms:ld('rooms',null),cats:ld('cats',null),prods:ld('prods',null),tasks:ld('tasks',null),\n  log:ld('log',[]),reset:ld('reset',null),name:ld('name',''),\n  visit:ld('visit',null),visitDone:ld('visitDone',{})\n};\nif(!S.rooms){S.rooms=seedRooms();sv('rooms',S.rooms);}\nif(!S.cats){S.cats=seedCats();sv('cats',S.cats);}\nif(!S.prods){S.prods=seedProds();sv('prods',S.prods);}\nif(!S.tasks){S.tasks=seedTasks();sv('tasks',S.tasks);}\nif(!S.visit){S.visit=seedVisit();sv('visit',S.visit);}\nfunction persist(){sv('rooms',S.rooms);sv('cats',S.cats);sv('prods',S.prods);sv('tasks',S.tasks);sv('log',S.log);sv('reset',S.reset);sv('visit',S.visit);sv('visitDone',S.visitDone);}\n\nfunction roomName(id){var r=S.rooms.find(function(x){return x.id===id;});return r?r.name:'\u2014';}\nfunction catName(id){var c=S.cats.find(function(x){return x.id===id;});return c?c.name:'\u2014';}\nfunction prodNames(ids){if(!ids)return '';return ids.split(',').map(function(i){var p=S.prods.find(function(x){return x.id===i.trim();});return p?p.name:null;}).filter(Boolean).join(', ');}\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550 SCHEDULING ENGINE \u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\nfunction taskStatus(t){\n  if(!t.active)return 'inactive';\n  if(t.freq==='seasonal'){\n    if(t.season){\n      if(t.season!==currentSeason()){\n        if(t.lastDone){\n          if(daysAgo(t.lastDone)<300)return 'notdue';\n        }\n        return 'offseason';\n      }\n    }\n  }\n  var interval=FREQS[t.freq].days;\n  var ago=daysAgo(t.lastDone);\n  if(ago===Infinity)return 'never';\n  if(ago>=interval*1.5){\n    if(interval>=7)return 'overdue';\n  }\n  if(ago>=interval)return 'due';\n  return 'notdue';\n}\nfunction urgencyScore(t){\n  var st=taskStatus(t);\n  var base={never:100,overdue:80,due:50,offseason:5,notdue:0,inactive:-1}[st];\n  var catBoost={c_hyg:15,c_veil:20,c_onderhoud:8,c_was:10,c_voorraad:8,c_opruim:6,c_org:4}[t.catId]||0;\n  var freqBoost=t.freq==='daily'?10:t.freq==='weekly'?6:0;\n  return base+catBoost+freqBoost;\n}\nfunction dueTasks(){return S.tasks.filter(function(t){var s=taskStatus(t);return s==='due'||s==='overdue'||s==='never';}).sort(function(a,b){return urgencyScore(b)-urgencyScore(a);});}\nfunction neverDone(){return S.tasks.filter(function(t){\n    if(!t.active)return false;\n    if(taskStatus(t)!=='never')return false;\n    if(t.freq==='daily')return false;\n    return true;\n  });}\n\nfunction completeTask(taskId){\n  var t=S.tasks.find(function(x){return x.id===taskId;});if(!t)return;\n  t.lastDone=todayStr();\n  S.log.push({taskId:taskId,date:todayStr(),name:t.name});\n  if(S.log.length>2000)S.log=S.log.slice(-2000);\n  persist();\n}\nfunction uncompleteTask(taskId){\n  var t=S.tasks.find(function(x){return x.id===taskId;});if(!t)return;\n  var idx=-1;\n  for(var i=S.log.length-1;i>=0;i--){if(S.log[i].taskId===taskId){\n      if(S.log[i].date===todayStr()){idx=i;break;}\n    }}\n  if(idx>=0)S.log.splice(idx,1);\n  var prev=null;\n  for(var j=S.log.length-1;j>=0;j--){if(S.log[j].taskId===taskId){prev=S.log[j].date;break;}}\n  t.lastDone=prev;\n  persist();\n}\nfunction doneToday(taskId){return S.log.some(function(l){\n  if(l.taskId!==taskId)return false;\n  return l.date===todayStr();\n});}\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550 STREAK \u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\nfunction streak(){\n  var days=Array.from(new Set(S.log.map(function(l){return l.date;}))).sort().reverse();\n  if(!days.length)return 0;\n  var s=0,cur=new Date(todayStr());\n  if(days[0]!==todayStr()){cur.setDate(cur.getDate()-1);if(days[0]!==cur.toISOString().slice(0,10))return 0;}\n  for(var i=0;i<days.length;i++){\n    var expect=cur.toISOString().slice(0,10);\n    if(days[i]===expect){s++;cur.setDate(cur.getDate()-1);}else break;\n  }\n  return s;\n}\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550 RESET-7-DAGEN PLAN \u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\nfunction buildResetPlan(){\n  var pool=S.tasks.filter(function(t){\n    if(!t.active)return false;\n    if(t.freq==='daily')return false;\n    return true;\n  });\n  pool.sort(function(a,b){return urgencyScore(b)-urgencyScore(a);});\n  var dayThemes=[\n    {title:'Leefruimte \\u0026 hal',match:function(t){return t.roomId==='r_woon'||t.roomId==='r_hal';}},\n    {title:'Keuken \\u0026 voorraad',match:function(t){return t.roomId==='r_keuk'||t.catId==='c_voorraad';}},\n    {title:'Was \\u0026 slaapkamer',match:function(t){return t.catId==='c_was'||t.roomId==='r_slaap'||t.roomId==='r_was';}},\n    {title:'Opruimen \\u0026 organisatie',match:function(t){return t.catId==='c_opruim'||t.catId==='c_org';}},\n    {title:'Badkamer \\u0026 hygi\u00ebne',match:function(t){\n      if(t.roomId==='r_bad')return true;\n      if(t.catId==='c_hyg'){\n        if(t.roomId==='r_bad')return true;\n      }\n      return false;\n    }},\n    {title:'Veiligheid \\u0026 onderhoud',match:function(t){return t.catId==='c_veil'||t.catId==='c_onderhoud';}},\n    {title:'Restpunten \\u0026 afronding',match:function(){return true;}}\n  ];\n  var used={},plan=[];\n  var MAX_MIN=90;\n  dayThemes.forEach(function(theme,i){\n    var dayTasks=[],mins=0;\n    pool.forEach(function(t){\n      if(used[t.id])return;\n      if(!theme.match(t))return;\n      if(mins+(t.duration||15)>MAX_MIN){\n        if(dayTasks.length>=3)return;\n      }\n      dayTasks.push(t.id);used[t.id]=true;mins+=(t.duration||15);\n    });\n    plan.push({day:i+1,title:theme.title,taskIds:dayTasks,done:{}});\n  });\n  return {startDate:todayStr(),days:plan,active:true};\n}\nfunction resetDayIndex(){\n  if(!S.reset||!S.reset.active)return -1;\n  var d=daysAgo(S.reset.startDate);\n  return Math.min(d,6);\n}\nfunction finishReset(){\n  if(!S.reset)return;\n  S.reset.days.forEach(function(d){\n    Object.keys(d.done).forEach(function(tid){\n      if(d.done[tid]){var t=S.tasks.find(function(x){return x.id===tid;});if(t){\n        if(!doneToday(tid)){t.lastDone=todayStr();S.log.push({taskId:tid,date:todayStr(),name:t.name});}\n      }}\n    });\n  });\n  S.reset=null;persist();render();\n}\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550 RENDER: DASHBOARD \u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\nfunction renderDash(){\n  var el=document.getElementById('v-dash');if(!el)return;\n  var due=dueTasks();\n  var daily=due.filter(function(t){return t.freq==='daily';});\n  var urgent=due.filter(function(t){return t.freq!=='daily';}).slice(0,8);\n  var never=neverDone();\n  var doneTodayN=S.log.filter(function(l){return l.date===todayStr();}).length;\n  var h=new Date().getHours();\n  var g=h<12?'Goedemorgen':h<18?'Goedemiddag':'Goedenavond';\n  var name=S.name?', '+esc(S.name):'';\n  var totalToday=daily.length+Math.min(urgent.length,5);\n  var doneCount=daily.filter(function(t){return doneToday(t.id);}).length;\n\n  var html='<div class=\"greet\">'+g+name+',<br><em>'+(due.length?due.length+' taken vragen aandacht':'alles is bij \u2014 goed bezig')+'<\/em><\/div>';\n  html+='<div class=\"stat-row\">'\n    +'<div class=\"stat\"><div class=\"sn\" style=\"color:var(--red)\">'+never.length+'<\/div><div class=\"sl\">Nooit gedaan<\/div><\/div>'\n    +'<div class=\"stat\"><div class=\"sn\" style=\"color:var(--amb)\">'+due.filter(function(t){return taskStatus(t)==='overdue';}).length+'<\/div><div class=\"sl\">Achterstallig<\/div><\/div>'\n    +'<div class=\"stat\"><div class=\"sn\" style=\"color:var(--blu)\">'+due.length+'<\/div><div class=\"sl\">Open nu<\/div><\/div>'\n    +'<div class=\"stat\"><div class=\"sn\" style=\"color:var(--grn)\">'+doneTodayN+'<\/div><div class=\"sl\">Vandaag gedaan<\/div><\/div>'\n  +'<\/div>';\n\n  if(S.reset){\n  if(S.reset.active){\n    var di=resetDayIndex();\n    html+='<div class=\"card\" style=\"border-color:var(--tx)\"><div class=\"card-h\"><span class=\"card-t\">Reset actief \u2014 dag '+(di+1)+' van 7<\/span>'\n      +'<button class=\"btn-sm\" onclick=\"HB.nav(\\'reset\\',document.querySelector(\\'[data-v=reset]\\'))\">Openen<\/button><\/div><\/div>';\n  }\n  }\n\n  if(daily.length){\n    html+='<div class=\"card\"><div class=\"card-h\"><span class=\"card-t\">Dagelijkse basis<\/span><span class=\"card-c\">'+doneCount+' \/ '+daily.length+'<\/span><\/div>';\n    daily.forEach(function(t){html+=taskRow(t,'dash');});\n    html+='<\/div>';\n  }\n  if(urgent.length){\n    html+='<div class=\"card\"><div class=\"card-h\"><span class=\"card-t\">Nu aan de beurt<\/span><span class=\"card-c\">op prioriteit<\/span><\/div>';\n    urgent.forEach(function(t){html+=taskRow(t,'dash');});\n    html+='<\/div>';\n  }\n  if(!due.length){\n    html+='<div class=\"empty\">Geen openstaande taken.<br>Alles loopt volgens schema.<\/div>';\n  }\n  el.innerHTML=html;\n}\nfunction taskRow(t,ctx){\n  var st=taskStatus(t);\n  var checked=doneToday(t.id);\n  var f=FREQS[t.freq];\n  var meta='<span class=\"pill '+f.cls+'\">'+f.label+'<\/span>';\n  meta+='<span class=\"meta-i\">'+esc(roomName(t.roomId))+'<\/span>';\n  if(t.duration)meta+='<span class=\"meta-i\">~'+t.duration+' min<\/span>';\n  if(st==='never')meta+='<span class=\"pill pl-red\">Nooit gedaan<\/span>';\n  else if(st==='overdue')meta+='<span class=\"pill pl-amb\">Achterstallig<\/span>';\n  else if(t.lastDone)meta+='<span class=\"meta-i\">'+daysAgo(t.lastDone)+' dgn geleden<\/span>';\n  var mats=prodNames(t.materials);\n  var tip=(t.tip?esc(t.tip):'')+(mats?(t.tip?' \u00b7 ':'')+'Nodig: '+esc(mats):'');\n  return '<div class=\"task'+(checked?' done':'')+'\">'\n    +'<input class=\"cb\" type=\"checkbox\" '+(checked?'checked':'')+' onchange=\"HB.tick(\\''+t.id+'\\',this)\">'\n    +'<div class=\"tb\"><div class=\"tn\">'+esc(t.name)+'<\/div>'\n    +'<div class=\"tt\">'+meta+'<\/div>'\n    +(tip?'<div class=\"tt\" style=\"margin-top:1px\">'+tip+'<\/div>':'')\n    +'<\/div><\/div>';\n}\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550 RENDER: PLANNING \u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\nvar planFilter={freq:'all',room:'all',q:''};\nfunction renderPlan(){\n  var el=document.getElementById('v-plan');if(!el)return;\n  var html='<input class=\"search\" placeholder=\"Zoek taken...\" value=\"'+esc(planFilter.q)+'\" oninput=\"HB.planSearch(this.value)\">';\n  html+='<div class=\"filter-row\">';\n  html+='<button class=\"fb'+(planFilter.freq==='all'?' on':'')+'\" onclick=\"HB.planFreq(\\'all\\',this)\">Alles<\/button>';\n  Object.keys(FREQS).forEach(function(fk){\n    html+='<button class=\"fb'+(planFilter.freq===fk?' on':'')+'\" onclick=\"HB.planFreq(\\''+fk+'\\',this)\">'+FREQS[fk].label+'<\/button>';\n  });\n  html+='<\/div>';\n  html+='<div class=\"filter-row\"><select class=\"inp\" style=\"max-width:220px\" onchange=\"HB.planRoom(this.value)\">';\n  html+='<option value=\"all\">Alle ruimtes<\/option>';\n  S.rooms.forEach(function(r){html+='<option value=\"'+r.id+'\"'+(planFilter.room===r.id?' selected':'')+'>'+esc(r.name)+'<\/option>';});\n  html+='<\/select><\/div>';\n\n  var tasks=S.tasks.filter(function(t){\n    if(!t.active)return false;\n    if(planFilter.freq!=='all'){\n      if(t.freq!==planFilter.freq)return false;\n    }\n    if(planFilter.room!=='all'){\n      if(t.roomId!==planFilter.room)return false;\n    }\n    if(planFilter.q){\n      if(t.name.toLowerCase().indexOf(planFilter.q.toLowerCase())<0)return false;\n    }\n    return true;\n  });\n  var groups={};\n  tasks.forEach(function(t){(groups[t.freq]=groups[t.freq]||[]).push(t);});\n  var order=['daily','weekly','biweekly','monthly','quarterly','halfyearly','yearly','seasonal'];\n  var any=false;\n  order.forEach(function(fk){\n    if(!groups[fk])return;any=true;\n    html+='<div class=\"card\"><div class=\"card-h\"><span class=\"card-t\">'+FREQS[fk].label+'<\/span><span class=\"card-c\">'+groups[fk].length+'<\/span><\/div>';\n    groups[fk].sort(function(a,b){return urgencyScore(b)-urgencyScore(a);}).forEach(function(t){html+=taskRow(t,'plan');});\n    html+='<\/div>';\n  });\n  if(!any)html+='<div class=\"empty\">Geen taken gevonden met deze filters.<\/div>';\n  el.innerHTML=html;\n}\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550 RENDER: RESET \u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\nfunction renderReset(){\n  var el=document.getElementById('v-reset');if(!el)return;\n  if(!S.reset||!S.reset.active){\n    el.innerHTML='<div class=\"reset-hero\">'\n      +'<h2>Reset binnen 7 dagen<\/h2>'\n      +'<p>Huishouden uit de hand gelopen? Dit programma bouwt in zeven dagen een solide basis op. Het systeem prioriteert automatisch: hygi\u00ebne en veiligheid eerst, dan achterstand, opruimen en organisatie. Elke dag maximaal ~90 minuten. Na dag 7 schakelt alles automatisch terug naar het reguliere schema.<\/p>'\n      +'<button class=\"btn\" onclick=\"HB.startReset()\">Start het 7-dagenplan<\/button>'\n      +'<\/div>'\n      +'<div class=\"empty\" style=\"padding-top:.5rem\">Het plan wordt samengesteld uit jouw eigen takenlijst,<br>gesorteerd op urgentie en gegroepeerd per thema.<\/div>';\n    return;\n  }\n  var di=resetDayIndex();\n  var html='<div class=\"day-tabs\">';\n  S.reset.days.forEach(function(d,i){\n    var allDone=false;\n    if(d.taskIds.length>0){\n      allDone=d.taskIds.every(function(tid){return d.done[tid];});\n    }\n    html+='<button class=\"day-tab'+(i===api._resetView?' active':'')+(allDone?' done-day':'')+'\" onclick=\"HB.resetDay('+i+')\">Dag '+(i+1)+(i===di?'<br>\u00b7':'')+'<\/button>';\n  });\n  html+='<\/div>';\n  S.reset.days.forEach(function(d,i){\n    html+='<div class=\"day-panel'+(i===api._resetView?' active':'')+'\">';\n    var doneN=d.taskIds.filter(function(tid){return d.done[tid];}).length;\n    var totMin=d.taskIds.reduce(function(a,tid){var t=S.tasks.find(function(x){return x.id===tid;});return a+(t?t.duration||15:0);},0);\n    html+='<div class=\"prog\"><div class=\"prog-r\"><span class=\"prog-l\">Dag '+(i+1)+' \u2014 '+esc(d.title)+'<\/span><span class=\"prog-v\">'+doneN+' \/ '+d.taskIds.length+' \u00b7 ~'+totMin+' min<\/span><\/div>'\n      +'<div class=\"bar\"><div class=\"fill\" style=\"width:'+(d.taskIds.length?Math.round(doneN\/d.taskIds.length*100):0)+'%\"><\/div><\/div><\/div>';\n    html+='<div class=\"card\">';\n    if(!d.taskIds.length)html+='<div class=\"empty\">Geen taken voor deze dag \u2014 lekker rustig.<\/div>';\n    d.taskIds.forEach(function(tid){\n      var t=S.tasks.find(function(x){return x.id===tid;});if(!t)return;\n      var ch=!!d.done[tid];\n      html+='<div class=\"task'+(ch?' done':'')+'\"><input class=\"cb\" type=\"checkbox\" '+(ch?'checked':'')+' onchange=\"HB.resetTick('+i+',\\''+tid+'\\',this)\">'\n        +'<div class=\"tb\"><div class=\"tn\">'+esc(t.name)+'<\/div>'\n        +'<div class=\"tt\"><span class=\"meta-i\">'+esc(roomName(t.roomId))+'<\/span><span class=\"meta-i\">~'+(t.duration||15)+' min<\/span><span class=\"meta-i\">'+DIFFS[t.difficulty||1]+'<\/span><\/div>'\n        +(t.tip?'<div class=\"tt\">'+esc(t.tip)+'<\/div>':'')\n        +'<\/div><\/div>';\n    });\n    html+='<\/div><\/div>';\n  });\n  var allAll=S.reset.days.every(function(d){return d.taskIds.every(function(tid){return d.done[tid];});});\n  html+='<div style=\"display:flex;gap:8px;margin-top:.5rem\">'\n    +(allAll||di>=6?'<button class=\"btn\" onclick=\"HB.confirmFinishReset()\">Reset afronden \u2192 regulier schema<\/button>':'')\n    +'<button class=\"btn-o\" onclick=\"HB.cancelReset()\">Reset annuleren<\/button><\/div>';\n  el.innerHTML=html;\n}\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550 RENDER: BEZOEK \u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\nvar visitTimer={secs:0,run:false,iv:null};\nfunction visitPhases(){\n  var order=[];S.visit.forEach(function(v){if(order.indexOf(v.phase)<0)order.push(v.phase);});\n  return order;\n}\nfunction renderVisit(){\n  var el=document.getElementById('v-visit');if(!el)return;\n  var total=S.visit.length;\n  var done=S.visit.filter(function(v){return S.visitDone[v.id];}).length;\n  if(total){\n  if(done===total){\n    el.innerHTML='<div class=\"done-screen\"><div class=\"done-check\"><svg viewBox=\"0 0 24 24\"><polyline points=\"20 6 9 17 4 12\"\/><\/svg><\/div>'\n      +'<div class=\"done-t\">Klaar.<\/div><div class=\"done-s\">Je huis is klaar voor bezoek.<br>Relax \u2014 de rest telt niet.<\/div>'\n      +'<div style=\"margin-top:1.5rem\"><button class=\"btn-o\" onclick=\"HB.visitReset()\">Opnieuw beginnen<\/button><\/div><\/div>';\n    return;\n  }\n  }\n  var m=Math.floor(visitTimer.secs\/60),sec=visitTimer.secs%60;\n  var html='<div class=\"timer-card\"><div class=\"timer-disp\" id=\"hb-vtimer\">'+(m<10?'0':'')+m+':'+(sec<10?'0':'')+sec+'<\/div>'\n    +'<div class=\"timer-ctrl\"><button class=\"tbtn'+(visitTimer.run?'':' go')+'\" onclick=\"HB.visitTimer()\">'+(visitTimer.run?'Pauze':(visitTimer.secs?'Doorgaan':'Start'))+'<\/button>'\n    +'<button class=\"tbtn\" onclick=\"HB.visitTimerReset()\">Reset<\/button><\/div><\/div>';\n  html+='<div class=\"prog\"><div class=\"prog-r\"><span class=\"prog-l\">Checklist<\/span><span class=\"prog-v\">'+done+' \/ '+total+'<\/span><\/div>'\n    +'<div class=\"bar\"><div class=\"fill\" style=\"width:'+(total?Math.round(done\/total*100):0)+'%\"><\/div><\/div><\/div>';\n  html+='<div class=\"card\">';\n  visitPhases().forEach(function(ph){\n    var items=S.visit.filter(function(v){return v.phase===ph;});\n    var mins=items.reduce(function(a,v){return a+(v.dur||5);},0);\n    html+='<div class=\"phase-row\"><div class=\"dot\" style=\"width:7px;height:7px;border-radius:50%;background:var(--red)\"><\/div><span class=\"phase-t\">'+esc(ph)+'<\/span><span class=\"phase-m\">~'+mins+' min<\/span><\/div>';\n    items.forEach(function(v){\n      var ch=!!S.visitDone[v.id];\n      html+='<div class=\"task'+(ch?' done':'')+'\"><input class=\"cb\" type=\"checkbox\" '+(ch?'checked':'')+' onchange=\"HB.visitTick(\\''+v.id+'\\',this)\">'\n        +'<div class=\"tb\"><div class=\"tn\">'+esc(v.name)+'<\/div>'\n        +(v.tip?'<div class=\"tt\">'+esc(v.tip)+'<\/div>':'')\n        +'<\/div><\/div>';\n    });\n  });\n  html+='<\/div><span class=\"ta-reset-link\" style=\"display:block;text-align:center;font-size:12px;color:var(--tx3);padding:1rem 0;cursor:pointer;text-decoration:underline;text-decoration-color:transparent\" onclick=\"HB.visitReset()\">Checklist resetten<\/span>';\n  html+='<div class=\"empty\" style=\"padding-top:0\">Items aanpassen kan onder Beheer \u2192 Bezoek.<\/div>';\n  el.innerHTML=html;\n}\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550 RENDER: STATS \u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\nfunction renderStats(){\n  var el=document.getElementById('v-stats');if(!el)return;\n  var last14=[];\n  for(var i=13;i>=0;i--){\n    var d=new Date();d.setDate(d.getDate()-i);\n    var ds=d.toISOString().slice(0,10);\n    last14.push({date:ds,n:S.log.filter(function(l){return l.date===ds;}).length,lbl:d.getDate()});\n  }\n  var max=Math.max.apply(null,last14.map(function(x){return x.n;}).concat([1]));\n  var total=S.log.length;\n  var week=S.log.filter(function(l){return daysAgo(l.date)<7;}).length;\n  var st=streak();\n  var byRoom={};\n  S.log.forEach(function(l){\n    var t=S.tasks.find(function(x){return x.id===l.taskId;});\n    var rn=t?roomName(t.roomId):'Overig';\n    byRoom[rn]=(byRoom[rn]||0)+1;\n  });\n  var roomsSorted=Object.keys(byRoom).sort(function(a,b){return byRoom[b]-byRoom[a];}).slice(0,6);\n  var maxRoom=roomsSorted.length?byRoom[roomsSorted[0]]:1;\n\n  var html='<div class=\"stat-row\" style=\"grid-template-columns:repeat(3,1fr)\">'\n    +'<div class=\"stat\"><div class=\"sn\">'+total+'<\/div><div class=\"sl\">Totaal afgerond<\/div><\/div>'\n    +'<div class=\"stat\"><div class=\"sn\" style=\"color:var(--blu)\">'+week+'<\/div><div class=\"sl\">Deze week<\/div><\/div>'\n    +'<div class=\"stat\"><div class=\"sn\" style=\"color:var(--grn)\">'+st+'<\/div><div class=\"sl\">Dagen streak<\/div><\/div>'\n  +'<\/div>';\n  html+='<div class=\"stat-block\"><div class=\"stat-block-t\">Afgeronde taken \u2014 laatste 14 dagen<\/div><div class=\"hist-bar-row\">';\n  last14.forEach(function(d){html+='<div class=\"hist-bar'+(d.n?' hasval':'')+'\" style=\"height:'+Math.max(5,Math.round(d.n\/max*100))+'%\" title=\"'+d.date+': '+d.n+'\"><\/div>';});\n  html+='<\/div><div class=\"hist-lbls\">';\n  last14.forEach(function(d){html+='<div class=\"hist-lbl\">'+d.lbl+'<\/div>';});\n  html+='<\/div><\/div>';\n  html+='<div class=\"stat-block\"><div class=\"stat-block-t\">Per ruimte (alle tijd)<\/div>';\n  if(!roomsSorted.length)html+='<div class=\"empty\" style=\"padding:1rem\">Nog geen data \u2014 vink taken af om statistieken op te bouwen.<\/div>';\n  roomsSorted.forEach(function(rn){\n    html+='<div style=\"margin-bottom:8px\"><div style=\"display:flex;justify-content:space-between;font-size:12px;color:var(--tx2);margin-bottom:3px\"><span>'+esc(rn)+'<\/span><span>'+byRoom[rn]+'<\/span><\/div>'\n      +'<div class=\"bar\"><div class=\"fill\" style=\"width:'+Math.round(byRoom[rn]\/maxRoom*100)+'%\"><\/div><\/div><\/div>';\n  });\n  html+='<\/div>';\n  el.innerHTML=html;\n}\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550 RENDER: ADMIN \u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\nvar adminTab='tasks';\nfunction renderAdmin(){\n  var el=document.getElementById('v-admin');if(!el)return;\n  var html='<div class=\"adm-tabs\">'\n    +'<button class=\"adm-tab'+(adminTab==='tasks'?' active':'')+'\" onclick=\"HB.adminTab(\\'tasks\\')\">Taken<\/button>'\n    +'<button class=\"adm-tab'+(adminTab==='rooms'?' active':'')+'\" onclick=\"HB.adminTab(\\'rooms\\')\">Ruimtes<\/button>'\n    +'<button class=\"adm-tab'+(adminTab==='cats'?' active':'')+'\" onclick=\"HB.adminTab(\\'cats\\')\">Categorie\u00ebn<\/button>'\n    +'<button class=\"adm-tab'+(adminTab==='prods'?' active':'')+'\" onclick=\"HB.adminTab(\\'prods\\')\">Middelen<\/button>'\n    +'<button class=\"adm-tab'+(adminTab==='visit'?' active':'')+'\" onclick=\"HB.adminTab(\\'visit\\')\">Bezoek<\/button>'\n  +'<\/div>';\n  if(adminTab==='tasks'){\n    html+='<button class=\"add-btn\" onclick=\"HB.editTask(null)\">+ Nieuwe taak<\/button><div class=\"card\">';\n    var sorted=S.tasks.slice().sort(function(a,b){return a.name.localeCompare(b.name);});\n    sorted.forEach(function(t){\n      html+='<div class=\"adm-row\"><div class=\"adm-info\"><div class=\"adm-n\"'+(t.active?'':' style=\"color:var(--tx3);text-decoration:line-through\"')+'>'+esc(t.name)+'<\/div>'\n        +'<div class=\"adm-s\">'+FREQS[t.freq].label+' \u00b7 '+esc(roomName(t.roomId))+' \u00b7 '+esc(catName(t.catId))+(t.duration?' \u00b7 '+t.duration+' min':'')+'<\/div><\/div>'\n        +'<div class=\"adm-acts\"><button class=\"btn-sm\" onclick=\"HB.editTask(\\''+t.id+'\\')\">Bewerk<\/button>'\n        +'<button class=\"btn-sm danger\" onclick=\"HB.delTask(\\''+t.id+'\\')\">\u00d7<\/button><\/div><\/div>';\n    });\n    html+='<\/div>';\n  }\n  if(adminTab==='rooms'){\n    html+='<button class=\"add-btn\" onclick=\"HB.editRoom(null)\">+ Nieuwe ruimte<\/button><div class=\"card\">';\n    S.rooms.forEach(function(r){\n      var n=S.tasks.filter(function(t){return t.roomId===r.id;}).length;\n      html+='<div class=\"adm-row\"><div class=\"adm-info\"><div class=\"adm-n\">'+esc(r.name)+'<\/div><div class=\"adm-s\">'+n+' taken<\/div><\/div>'\n        +'<div class=\"adm-acts\"><button class=\"btn-sm\" onclick=\"HB.editRoom(\\''+r.id+'\\')\">Bewerk<\/button>'\n        +'<button class=\"btn-sm danger\" onclick=\"HB.delRoom(\\''+r.id+'\\')\">\u00d7<\/button><\/div><\/div>';\n    });\n    html+='<\/div>';\n  }\n  if(adminTab==='cats'){\n    html+='<button class=\"add-btn\" onclick=\"HB.editCat(null)\">+ Nieuwe categorie<\/button><div class=\"card\">';\n    S.cats.forEach(function(c){\n      var n=S.tasks.filter(function(t){return t.catId===c.id;}).length;\n      html+='<div class=\"adm-row\"><div class=\"adm-info\"><div class=\"adm-n\">'+esc(c.name)+'<\/div><div class=\"adm-s\">'+n+' taken<\/div><\/div>'\n        +'<div class=\"adm-acts\"><button class=\"btn-sm\" onclick=\"HB.editCat(\\''+c.id+'\\')\">Bewerk<\/button>'\n        +'<button class=\"btn-sm danger\" onclick=\"HB.delCat(\\''+c.id+'\\')\">\u00d7<\/button><\/div><\/div>';\n    });\n    html+='<\/div>';\n  }\n  if(adminTab==='prods'){\n    html+='<button class=\"add-btn\" onclick=\"HB.editProd(null)\">+ Nieuw middel<\/button><div class=\"card\">';\n    S.prods.forEach(function(p){\n      html+='<div class=\"adm-row\"><div class=\"adm-info\"><div class=\"adm-n\">'+esc(p.name)+'<\/div><div class=\"adm-s\">'+esc(p.use)+(p.tags?' \u00b7 '+esc(p.tags):'')+'<\/div><\/div>'\n        +'<div class=\"adm-acts\"><button class=\"btn-sm\" onclick=\"HB.editProd(\\''+p.id+'\\')\">Bewerk<\/button>'\n        +'<button class=\"btn-sm danger\" onclick=\"HB.delProd(\\''+p.id+'\\')\">\u00d7<\/button><\/div><\/div>';\n    });\n    html+='<\/div>';\n  }\n  if(adminTab==='visit'){\n    html+='<button class=\"add-btn\" onclick=\"HB.editVisit(null)\">+ Nieuw bezoek-item<\/button><div class=\"card\">';\n    visitPhases().forEach(function(ph){\n      html+='<div class=\"phase-row\"><span class=\"phase-t\">'+esc(ph)+'<\/span><\/div>';\n      S.visit.filter(function(v){return v.phase===ph;}).forEach(function(v){\n        html+='<div class=\"adm-row\"><div class=\"adm-info\"><div class=\"adm-n\">'+esc(v.name)+'<\/div>'\n          +'<div class=\"adm-s\">~'+(v.dur||5)+' min'+(v.tip?' \u00b7 '+esc(v.tip):'')+'<\/div><\/div>'\n          +'<div class=\"adm-acts\"><button class=\"btn-sm\" onclick=\"HB.editVisit(\\''+v.id+'\\')\">Bewerk<\/button>'\n          +'<button class=\"btn-sm danger\" onclick=\"HB.delVisit(\\''+v.id+'\\')\">\u00d7<\/button><\/div><\/div>';\n      });\n    });\n    html+='<\/div>';\n  }\n  html+='<div class=\"sec-h\">Instellingen<\/div>';\n  html+='<div class=\"frow\"><label class=\"flabel\">Je naam (voor de begroeting)<\/label><input class=\"inp\" value=\"'+esc(S.name)+'\" onchange=\"HB.setName(this.value)\"><\/div>';\n  html+='<div class=\"sec-h\">Data<\/div>';\n  html+='<div class=\"io-row\"><button class=\"btn-o\" onclick=\"HB.exportData()\">Exporteer backup<\/button>'\n    +'<label class=\"btn-o\" style=\"cursor:pointer\">Importeer<input type=\"file\" accept=\".json\" style=\"display:none\" onchange=\"HB.importData(this)\"><\/label>'\n    +'<button class=\"btn-sm danger\" onclick=\"HB.factoryReset()\" style=\"margin-left:auto\">Alles wissen<\/button><\/div>';\n  el.innerHTML=html;\n}\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550 MODALS \/ CRUD \u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\nfunction openModal(title,body){\n  document.getElementById('hb-modal-t').textContent=title;\n  document.getElementById('hb-modal-body').innerHTML=body;\n  document.getElementById('hb-modal-bg').classList.add('show');\n}\nfunction closeModal(){document.getElementById('hb-modal-bg').classList.remove('show');}\n\nfunction editTask(id){\n  var t=id?S.tasks.find(function(x){return x.id===id;}):{id:null,name:'',roomId:S.rooms[0].id,catId:S.cats[0].id,freq:'weekly',duration:15,difficulty:1,materials:'',tip:'',season:'',active:true};\n  var roomOpts=S.rooms.map(function(r){return '<option value=\"'+r.id+'\"'+(t.roomId===r.id?' selected':'')+'>'+esc(r.name)+'<\/option>';}).join('');\n  var catOpts=S.cats.map(function(c){return '<option value=\"'+c.id+'\"'+(t.catId===c.id?' selected':'')+'>'+esc(c.name)+'<\/option>';}).join('');\n  var freqOpts=Object.keys(FREQS).map(function(f){return '<option value=\"'+f+'\"'+(t.freq===f?' selected':'')+'>'+FREQS[f].label+'<\/option>';}).join('');\n  var seasonOpts='<option value=\"\">\u2014<\/option>'+Object.keys(SEASONS).map(function(s){return '<option value=\"'+s+'\"'+(t.season===s?' selected':'')+'>'+SEASONS[s]+'<\/option>';}).join('');\n  var prodChecks=S.prods.map(function(p){\n    var sel=(t.materials||'').split(',').indexOf(p.id)>=0;\n    return '<label style=\"display:flex;align-items:center;gap:6px;font-size:13px;padding:3px 0\"><input type=\"checkbox\" class=\"hb-matcheck\" value=\"'+p.id+'\"'+(sel?' checked':'')+'>'+esc(p.name)+'<\/label>';\n  }).join('');\n  var body=\n    '<div class=\"frow\"><label class=\"flabel\">Naam<\/label><input class=\"inp\" id=\"hbf-name\" value=\"'+esc(t.name)+'\"><\/div>'\n    +'<div class=\"fgrid\"><div class=\"frow\"><label class=\"flabel\">Ruimte<\/label><select class=\"inp\" id=\"hbf-room\">'+roomOpts+'<\/select><\/div>'\n    +'<div class=\"frow\"><label class=\"flabel\">Categorie<\/label><select class=\"inp\" id=\"hbf-cat\">'+catOpts+'<\/select><\/div><\/div>'\n    +'<div class=\"fgrid\"><div class=\"frow\"><label class=\"flabel\">Frequentie<\/label><select class=\"inp\" id=\"hbf-freq\" onchange=\"document.getElementById(\\'hbf-season-row\\').style.display=this.value===\\'seasonal\\'?\\'\\':\\'none\\'\">'+freqOpts+'<\/select><\/div>'\n    +'<div class=\"frow\" id=\"hbf-season-row\" style=\"display:'+(t.freq==='seasonal'?'':'none')+'\"><label class=\"flabel\">Seizoen<\/label><select class=\"inp\" id=\"hbf-season\">'+seasonOpts+'<\/select><\/div><\/div>'\n    +'<div class=\"fgrid\"><div class=\"frow\"><label class=\"flabel\">Duur (minuten)<\/label><input class=\"inp\" type=\"number\" id=\"hbf-dur\" value=\"'+(t.duration||15)+'\"><\/div>'\n    +'<div class=\"frow\"><label class=\"flabel\">Zwaarte<\/label><select class=\"inp\" id=\"hbf-diff\"><option value=\"1\"'+(t.difficulty===1?' selected':'')+'>Licht<\/option><option value=\"2\"'+(t.difficulty===2?' selected':'')+'>Gemiddeld<\/option><option value=\"3\"'+(t.difficulty===3?' selected':'')+'>Zwaar<\/option><\/select><\/div><\/div>'\n    +'<div class=\"frow\"><label class=\"flabel\">Tip \/ notitie<\/label><input class=\"inp\" id=\"hbf-tip\" value=\"'+esc(t.tip)+'\"><\/div>'\n    +'<div class=\"frow\"><label class=\"flabel\">Benodigde middelen<\/label>'+(prodChecks||'<div class=\"adm-s\">Nog geen middelen \u2014 voeg toe onder Beheer \u2192 Middelen<\/div>')+'<\/div>'\n    +'<div class=\"frow\"><label style=\"display:flex;align-items:center;gap:6px;font-size:13px\"><input type=\"checkbox\" id=\"hbf-active\"'+(t.active?' checked':'')+'>Taak actief<\/label><\/div>'\n    +'<button class=\"btn\" onclick=\"HB.saveTask(\\''+(t.id||'')+'\\')\">Opslaan<\/button>';\n  openModal(id?'Taak bewerken':'Nieuwe taak',body);\n}\nfunction saveTask(id){\n  var mats=Array.from(document.querySelectorAll('.hb-matcheck:checked')).map(function(c){return c.value;}).join(',');\n  var data={\n    name:document.getElementById('hbf-name').value.trim(),\n    roomId:document.getElementById('hbf-room').value,\n    catId:document.getElementById('hbf-cat').value,\n    freq:document.getElementById('hbf-freq').value,\n    season:document.getElementById('hbf-freq').value==='seasonal'?document.getElementById('hbf-season').value:'',\n    duration:parseInt(document.getElementById('hbf-dur').value)||15,\n    difficulty:parseInt(document.getElementById('hbf-diff').value)||1,\n    tip:document.getElementById('hbf-tip').value.trim(),\n    materials:mats,\n    active:document.getElementById('hbf-active').checked\n  };\n  if(!data.name)return;\n  if(id){\n    var t=S.tasks.find(function(x){return x.id===id;});\n    Object.assign(t,data);\n  } else {\n    data.id=uid();data.lastDone=null;S.tasks.push(data);\n  }\n  persist();closeModal();render();\n}\nfunction delTask(id){\n  askConfirm('Taak verwijderen?','De historie blijft bewaard in de statistieken.',function(){\n    S.tasks=S.tasks.filter(function(t){return t.id!==id;});persist();render();\n  });\n}\nfunction editRoom(id){\n  var r=id?S.rooms.find(function(x){return x.id===id;}):{id:null,name:''};\n  openModal(id?'Ruimte bewerken':'Nieuwe ruimte',\n    '<div class=\"frow\"><label class=\"flabel\">Naam<\/label><input class=\"inp\" id=\"hbf-rname\" value=\"'+esc(r.name)+'\"><\/div>'\n    +'<button class=\"btn\" onclick=\"HB.saveRoom(\\''+(r.id||'')+'\\')\">Opslaan<\/button>');\n}\nfunction saveRoom(id){\n  var name=document.getElementById('hbf-rname').value.trim();if(!name)return;\n  if(id){S.rooms.find(function(x){return x.id===id;}).name=name;}\n  else S.rooms.push({id:uid(),name:name});\n  persist();closeModal();render();\n}\nfunction delRoom(id){\n  var n=S.tasks.filter(function(t){return t.roomId===id;}).length;\n  var msg=n?n+' taken gebruiken deze ruimte en worden verplaatst naar een andere ruimte.':'Deze ruimte wordt verwijderd.';\n  askConfirm('Ruimte verwijderen?',msg,function(){\n    var fallback=S.rooms.find(function(r){return r.id!==id;});\n    S.tasks.forEach(function(t){if(t.roomId===id)t.roomId=fallback?fallback.id:'';});\n    S.rooms=S.rooms.filter(function(r){return r.id!==id;});persist();render();\n  });\n}\nfunction editCat(id){\n  var c=id?S.cats.find(function(x){return x.id===id;}):{id:null,name:''};\n  openModal(id?'Categorie bewerken':'Nieuwe categorie',\n    '<div class=\"frow\"><label class=\"flabel\">Naam<\/label><input class=\"inp\" id=\"hbf-cname\" value=\"'+esc(c.name)+'\"><\/div>'\n    +'<button class=\"btn\" onclick=\"HB.saveCat(\\''+(c.id||'')+'\\')\">Opslaan<\/button>');\n}\nfunction saveCat(id){\n  var name=document.getElementById('hbf-cname').value.trim();if(!name)return;\n  if(id){S.cats.find(function(x){return x.id===id;}).name=name;}\n  else S.cats.push({id:uid(),name:name});\n  persist();closeModal();render();\n}\nfunction delCat(id){\n  var n=S.tasks.filter(function(t){return t.catId===id;}).length;\n  var msg=n?n+' taken gebruiken deze categorie en krijgen een andere.':'Deze categorie wordt verwijderd.';\n  askConfirm('Categorie verwijderen?',msg,function(){\n    var fb=S.cats.find(function(c){return c.id!==id;});\n    S.tasks.forEach(function(t){if(t.catId===id)t.catId=fb?fb.id:'';});\n    S.cats=S.cats.filter(function(c){return c.id!==id;});persist();render();\n  });\n}\nfunction editProd(id){\n  var p=id?S.prods.find(function(x){return x.id===id;}):{id:null,name:'',use:'',tags:''};\n  openModal(id?'Middel bewerken':'Nieuw middel',\n    '<div class=\"frow\"><label class=\"flabel\">Naam<\/label><input class=\"inp\" id=\"hbf-pname\" value=\"'+esc(p.name)+'\"><\/div>'\n    +'<div class=\"frow\"><label class=\"flabel\">Waarvoor<\/label><input class=\"inp\" id=\"hbf-puse\" value=\"'+esc(p.use)+'\"><\/div>'\n    +'<div class=\"frow\"><label class=\"flabel\">Tags (kommagescheiden)<\/label><input class=\"inp\" id=\"hbf-ptags\" value=\"'+esc(p.tags)+'\"><\/div>'\n    +'<button class=\"btn\" onclick=\"HB.saveProd(\\''+(p.id||'')+'\\')\">Opslaan<\/button>');\n}\nfunction saveProd(id){\n  var name=document.getElementById('hbf-pname').value.trim();if(!name)return;\n  var use=document.getElementById('hbf-puse').value.trim();\n  var tags=document.getElementById('hbf-ptags').value.trim();\n  if(id){var p=S.prods.find(function(x){return x.id===id;});p.name=name;p.use=use;p.tags=tags;}\n  else S.prods.push({id:uid(),name:name,use:use,tags:tags});\n  persist();closeModal();render();\n}\nfunction delProd(id){\n  askConfirm('Middel verwijderen?','Het middel wordt ook losgekoppeld van taken die het gebruiken.',function(){\n    S.tasks.forEach(function(t){\n      t.materials=(t.materials||'').split(',').filter(function(m){\n        if(!m)return false;\n        return m!==id;\n      }).join(',');\n    });\n    S.prods=S.prods.filter(function(p){return p.id!==id;});persist();render();\n  });\n}\n\nfunction editVisit(id){\n  var v=id?S.visit.find(function(x){return x.id===id;}):{id:null,name:'',phase:visitPhases()[0]||'Algemeen',tip:'',dur:5};\n  var phaseOpts=visitPhases().map(function(p){return '<option value=\"'+esc(p)+'\"'+(v.phase===p?' selected':'')+'>'+esc(p)+'<\/option>';}).join('');\n  openModal(id?'Bezoek-item bewerken':'Nieuw bezoek-item',\n    '<div class=\"frow\"><label class=\"flabel\">Naam<\/label><input class=\"inp\" id=\"hbf-vname\" value=\"'+esc(v.name)+'\"><\/div>'\n    +'<div class=\"fgrid\"><div class=\"frow\"><label class=\"flabel\">Fase (bestaand of nieuw)<\/label>'\n    +'<select class=\"inp\" id=\"hbf-vphase-sel\" onchange=\"document.getElementById(\\'hbf-vphase-new\\').style.display=this.value===\\'__new__\\'?\\'\\':\\'none\\'\">'+phaseOpts+'<option value=\"__new__\">+ Nieuwe fase...<\/option><\/select>'\n    +'<input class=\"inp\" id=\"hbf-vphase-new\" placeholder=\"Naam nieuwe fase\" style=\"display:none;margin-top:6px\"><\/div>'\n    +'<div class=\"frow\"><label class=\"flabel\">Duur (min)<\/label><input class=\"inp\" type=\"number\" id=\"hbf-vdur\" value=\"'+(v.dur||5)+'\"><\/div><\/div>'\n    +'<div class=\"frow\"><label class=\"flabel\">Tip<\/label><input class=\"inp\" id=\"hbf-vtip\" value=\"'+esc(v.tip)+'\"><\/div>'\n    +'<button class=\"btn\" onclick=\"HB.saveVisit(\\''+(v.id||'')+'\\')\">Opslaan<\/button>');\n}\nfunction saveVisit(id){\n  var name=document.getElementById('hbf-vname').value.trim();if(!name)return;\n  var phase=document.getElementById('hbf-vphase-sel').value;\n  if(phase==='__new__')phase=document.getElementById('hbf-vphase-new').value.trim()||'Algemeen';\n  var dur=parseInt(document.getElementById('hbf-vdur').value)||5;\n  var tip=document.getElementById('hbf-vtip').value.trim();\n  if(id){var v=S.visit.find(function(x){return x.id===id;});v.name=name;v.phase=phase;v.dur=dur;v.tip=tip;}\n  else S.visit.push({id:uid(),name:name,phase:phase,dur:dur,tip:tip});\n  persist();closeModal();render();\n}\nfunction delVisit(id){\n  askConfirm('Item verwijderen?','Het item verdwijnt uit de bezoek-checklist.',function(){\n    S.visit=S.visit.filter(function(v){return v.id!==id;});\n    delete S.visitDone[id];persist();render();\n  });\n}\n\nfunction askConfirm(title,msg,cb){\n  window.__hbConfirmCb=cb;\n  openModal(title,\n    '<p style=\"font-size:13px;color:var(--tx2);line-height:1.6;margin-bottom:1rem\">'+msg+'<\/p>'\n    +'<div style=\"display:flex;gap:8px\"><button class=\"btn\" onclick=\"HB.__doConfirm()\">Ja, doorgaan<\/button>'\n    +'<button class=\"btn-o\" onclick=\"HB.closeModal()\">Annuleren<\/button><\/div>');\n}\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550 IMPORT\/EXPORT \u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\nfunction exportData(){\n  var data={rooms:S.rooms,cats:S.cats,prods:S.prods,tasks:S.tasks,log:S.log,reset:S.reset,name:S.name,visit:S.visit,visitDone:S.visitDone,exported:todayStr()};\n  var blob=new Blob([JSON.stringify(data,null,2)],{type:'application\/json'});\n  var a=document.createElement('a');\n  a.href=URL.createObjectURL(blob);\n  a.download='huisbeheer-backup-'+todayStr()+'.json';\n  a.click();\n}\nfunction importData(input){\n  var f=input.files[0];if(!f)return;\n  var reader=new FileReader();\n  reader.onload=function(){\n    try{\n      var d=JSON.parse(reader.result);\n      if(!d.tasks||!d.rooms)throw new Error('ongeldig');\n      askConfirm('Backup importeren?','De huidige data wordt overschreven met de inhoud van dit bestand.',function(){\n        S.rooms=d.rooms;S.cats=d.cats||seedCats();S.prods=d.prods||[];S.tasks=d.tasks;S.log=d.log||[];S.reset=d.reset||null;S.name=d.name||'';S.visit=d.visit||seedVisit();S.visitDone=d.visitDone||{};\n        persist();sv('name',S.name);render();\n      });\n    }catch(e){openModal('Import mislukt','<p style=\"font-size:13px;color:var(--tx2)\">Kon het bestand niet lezen. Is dit een Huisbeheer-backup?<\/p>');}\n  };\n  reader.readAsText(f);\n}\nfunction factoryReset(){\n  askConfirm('Alles wissen?','Alle taken, instellingen en historie worden verwijderd en de standaardset komt terug. Dit kan niet ongedaan worden gemaakt. Exporteer eerst een backup als je twijfelt.',function(){\n    Object.keys(localStorage).forEach(function(k){if(k.indexOf(NS)===0)localStorage.removeItem(k);});\n    location.reload();\n  });\n}\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550 PUBLIC API \u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\nvar api={\n  _resetView:0,\n  nav:function(v,btn){\n    document.querySelectorAll('#hb-app .view').forEach(function(x){x.classList.remove('active');});\n    document.querySelectorAll('#hb-app .nb').forEach(function(x){x.classList.remove('active');});\n    document.getElementById('v-'+v).classList.add('active');btn.classList.add('active');\n    render();\n  },\n  tick:function(id,cb){\n    if(cb.checked)completeTask(id);else uncompleteTask(id);\n    render();\n  },\n  planSearch:function(q){planFilter.q=q;renderPlan();\n    var inp=document.querySelector('#v-plan .search');if(inp){inp.focus();inp.setSelectionRange(inp.value.length,inp.value.length);}},\n  planFreq:function(f){planFilter.freq=f;renderPlan();},\n  planRoom:function(r){planFilter.room=r;renderPlan();},\n  startReset:function(){\n    openModal('Reset starten?',\n      '<p style=\"font-size:13px;color:var(--tx2);line-height:1.6;margin-bottom:1rem\">Het 7-dagenplan wordt opgebouwd uit jouw eigen taken, gesorteerd op prioriteit en gegroepeerd per thema. Elke dag maximaal ~90 minuten.<\/p>'\n      +'<div style=\"display:flex;gap:8px\"><button class=\"btn\" onclick=\"HB.doStartReset()\">Ja, start het plan<\/button>'\n      +'<button class=\"btn-o\" onclick=\"HB.closeModal()\">Annuleren<\/button><\/div>');\n  },\n  doStartReset:function(){\n    S.reset=buildResetPlan();api._resetView=0;persist();closeModal();render();\n  },\n  resetDay:function(i){api._resetView=i;renderReset();},\n  resetTick:function(dayI,tid,cb){\n    S.reset.days[dayI].done[tid]=cb.checked;persist();renderReset();\n  },\n  confirmFinishReset:function(){\n    openModal('Reset afronden?',\n      '<p style=\"font-size:13px;color:var(--tx2);line-height:1.6;margin-bottom:1rem\">Alle afgevinkte taken krijgen vandaag als laatste-gedaan-datum en het reguliere onderhoudsschema neemt het over.<\/p>'\n      +'<div style=\"display:flex;gap:8px\"><button class=\"btn\" onclick=\"HB.doFinishReset()\">Afronden<\/button>'\n      +'<button class=\"btn-o\" onclick=\"HB.closeModal()\">Terug<\/button><\/div>');\n  },\n  doFinishReset:function(){closeModal();finishReset();},\n  cancelReset:function(){\n    openModal('Reset annuleren?',\n      '<p style=\"font-size:13px;color:var(--tx2);line-height:1.6;margin-bottom:1rem\">De voortgang van het plan gaat verloren. Wil je de afgevinkte taken bewaren, kies dan Afronden in plaats van annuleren.<\/p>'\n      +'<div style=\"display:flex;gap:8px\"><button class=\"btn\" onclick=\"HB.doCancelReset()\">Ja, annuleer<\/button>'\n      +'<button class=\"btn-o\" onclick=\"HB.closeModal()\">Terug<\/button><\/div>');\n  },\n  doCancelReset:function(){S.reset=null;persist();closeModal();render();},\n  adminTab:function(t){adminTab=t;renderAdmin();},\n  editTask:editTask,saveTask:saveTask,delTask:delTask,\n  editRoom:editRoom,saveRoom:saveRoom,delRoom:delRoom,\n  editCat:editCat,saveCat:saveCat,delCat:delCat,\n  editProd:editProd,saveProd:saveProd,delProd:delProd,\n  setName:function(n){S.name=n.trim();sv('name',S.name);},\n  exportData:exportData,importData:importData,factoryReset:factoryReset,\n  visitTick:function(id,cb){S.visitDone[id]=cb.checked;persist();renderVisit();},\n  visitReset:function(){S.visitDone={};visitTimer.secs=0;visitTimer.run=false;clearInterval(visitTimer.iv);persist();renderVisit();},\n  visitTimer:function(){\n    visitTimer.run=!visitTimer.run;\n    if(visitTimer.run){visitTimer.iv=setInterval(function(){visitTimer.secs++;var el=document.getElementById('hb-vtimer');if(el){var m=Math.floor(visitTimer.secs\/60),s2=visitTimer.secs%60;el.textContent=(m<10?'0':'')+m+':'+(s2<10?'0':'')+s2;}},1000);}\n    else clearInterval(visitTimer.iv);\n    renderVisit();\n  },\n  visitTimerReset:function(){visitTimer.secs=0;visitTimer.run=false;clearInterval(visitTimer.iv);renderVisit();},\n  editVisit:editVisit,saveVisit:saveVisit,delVisit:delVisit,\n  closeModal:closeModal,\n  __doConfirm:function(){var cb=window.__hbConfirmCb;window.__hbConfirmCb=null;closeModal();if(cb)cb();}\n};\n\nfunction render(){\n  renderDash();renderPlan();renderReset();renderVisit();renderStats();renderAdmin();\n  var st=streak();\n  var stEl=document.getElementById('hb-streak');\n  if(stEl)stEl.textContent=st>0?st+' dag'+(st!==1?'en':'')+' actief':'welkom';\n}\nfunction init(){\n  if(!document.getElementById('hb-app')){setTimeout(init,150);return;}\n  render();\n}\nif(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',init);}\nelse{init();}\nreturn api;\n})();\n<\/script>\n","protected":false},"excerpt":{"rendered":"<p>Huisbeheer \u00b7 Vandaag Planning Reset Bezoek Statistiek Beheer Bewerken \u00d7<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-8","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/yepto.nl\/index.php\/wp-json\/wp\/v2\/pages\/8","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/yepto.nl\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/yepto.nl\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/yepto.nl\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/yepto.nl\/index.php\/wp-json\/wp\/v2\/comments?post=8"}],"version-history":[{"count":7,"href":"https:\/\/yepto.nl\/index.php\/wp-json\/wp\/v2\/pages\/8\/revisions"}],"predecessor-version":[{"id":44,"href":"https:\/\/yepto.nl\/index.php\/wp-json\/wp\/v2\/pages\/8\/revisions\/44"}],"wp:attachment":[{"href":"https:\/\/yepto.nl\/index.php\/wp-json\/wp\/v2\/media?parent=8"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}