{"id":14,"date":"2026-08-13T09:30:52","date_gmt":"2026-08-13T09:30:52","guid":{"rendered":"https:\/\/deonware.com\/cv\/?page_id=14"},"modified":"2026-08-13T09:51:49","modified_gmt":"2026-08-13T09:51:49","slug":"deon-claassens-cv","status":"publish","type":"page","link":"https:\/\/deonware.com\/cv\/","title":{"rendered":"Deon Claassens CV"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"14\" class=\"elementor elementor-14\">\n\t\t\t\t<div class=\"elementor-element elementor-element-5cfa4b8 e-flex e-con-boxed e-con e-parent\" data-id=\"5cfa4b8\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-03a50b0 elementor-widget elementor-widget-html\" data-id=\"03a50b0\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!DOCTYPE html>\r\n<html lang=\"en\">\r\n<head>\r\n<meta charset=\"UTF-8\">\r\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, viewport-fit=cover\">\r\n<title>Deon Claassens \u2014 Setup Wizard \/\/ Specialist Developer<\/title>\r\n<meta name=\"description\" content=\"Interactive installer-style CV of Deon Claassens \u2014 27+ years shipping production code, from Delphi to .NET Core microservices, now Specialist Developer at Softsure (Dotsure).\">\r\n<style>\r\n:root{\r\n  --bg:#0a1c3d; --bg-deep:#071531; --panel:#0f2450; --panel-2:#122a5c;\r\n  --border:rgba(255,255,255,.09); --border-strong:rgba(255,255,255,.18);\r\n  --amber:#ffb000; --amber-dim:#c98a00; --cyan:#6fe3d0; --green:#7ee08a;\r\n  --text:#e9e6dd; --text-dim:#93a0be; --text-faint:#5c6a8c; --red:#ff6b5b;\r\n  --mono:Consolas,'Courier New',ui-monospace,SFMono-Regular,Menlo,monospace;\r\n  --sans:Tahoma,Geneva,Verdana,-apple-system,BlinkMacSystemFont,sans-serif;\r\n}\r\n*{box-sizing:border-box;margin:0;padding:0;}\r\nhtml{scroll-behavior:smooth;}\r\nbody{background:var(--bg);color:var(--text);font-family:var(--sans);line-height:1.55;overflow:hidden;height:100vh;}\r\n::selection{background:var(--amber);color:var(--bg-deep);}\r\na{color:var(--cyan);}\r\n#bg-canvas{position:fixed;inset:0;z-index:0;width:100%;height:100%;pointer-events:none;}\r\n.noise{position:fixed;inset:0;z-index:1;pointer-events:none;\r\n  background-image:radial-gradient(circle at 20% 20%,rgba(255,255,255,.02),transparent 40%),\r\n                    radial-gradient(circle at 80% 70%,rgba(111,227,208,.03),transparent 45%);}\r\n\r\n\/* ---------- SPLASH ---------- *\/\r\n#splash{position:fixed;inset:0;z-index:60;display:flex;align-items:center;justify-content:center;\r\n  background:radial-gradient(ellipse at 50% 30%,var(--bg) 0%,var(--bg-deep) 75%);\r\n  transition:opacity .6s ease,visibility .6s ease;padding:16px;}\r\n#splash.hidden{opacity:0;visibility:hidden;pointer-events:none;}\r\n.terminal{width:min(640px,94vw);background:rgba(10,24,55,.7);border:1px solid var(--border-strong);\r\n  border-radius:8px;box-shadow:0 30px 80px rgba(0,0,0,.55),0 0 0 1px rgba(255,255,255,.02) inset;\r\n  backdrop-filter:blur(6px);overflow:hidden;}\r\n.terminal-bar{display:flex;align-items:center;gap:8px;padding:10px 14px;background:rgba(255,255,255,.03);border-bottom:1px solid var(--border);}\r\n.dot{width:10px;height:10px;border-radius:50%;}\r\n.dot.r{background:#ff5f56;} .dot.y{background:#ffbd2e;} .dot.g{background:#27c93f;}\r\n.terminal-title{margin-left:10px;font-family:var(--mono);font-size:11px;color:var(--text-faint);letter-spacing:.03em;}\r\n.terminal-body{padding:22px 20px 26px;font-family:var(--mono);font-size:13.5px;min-height:210px;}\r\n.terminal-body .line{color:var(--text-dim);margin-bottom:6px;white-space:pre-wrap;}\r\n.terminal-body .prompt{color:var(--cyan);} .terminal-body .out{color:var(--text);} .terminal-body .highlight{color:var(--amber);}\r\n.cursor{display:inline-block;width:8px;height:15px;background:var(--amber);vertical-align:middle;animation:blink 1s steps(1) infinite;}\r\n@keyframes blink{50%{opacity:0;}}\r\n.enter-btn{margin-top:16px;font-family:var(--mono);background:var(--amber);color:#1a1200;border:none;\r\n  padding:11px 20px;font-size:13px;font-weight:600;letter-spacing:.03em;border-radius:4px;cursor:pointer;\r\n  opacity:0;transform:translateY(6px);transition:opacity .4s ease,transform .4s ease,background .2s ease;}\r\n.enter-btn:hover{background:#ffc733;} .enter-btn.show{opacity:1;transform:translateY(0);}\r\n.enter-btn:focus-visible{outline:2px solid var(--cyan);outline-offset:2px;}\r\n\r\n\/* ---------- WIZARD SHELL ---------- *\/\r\n#wizard{position:fixed;inset:0;z-index:40;display:flex;align-items:center;justify-content:center;padding:18px;\r\n  opacity:0;visibility:hidden;transition:opacity .5s ease,visibility .5s ease;}\r\n#wizard.show{opacity:1;visibility:visible;}\r\n.win{width:min(940px,96vw);height:min(680px,92vh);display:flex;flex-direction:column;\r\n  background:rgba(9,21,48,.88);border:1px solid var(--border-strong);border-radius:10px;\r\n  box-shadow:0 40px 100px rgba(0,0,0,.6),0 0 0 1px rgba(255,255,255,.02) inset;\r\n  backdrop-filter:blur(10px);overflow:hidden;}\r\n.win-bar{display:flex;align-items:center;gap:10px;padding:10px 14px;background:rgba(255,255,255,.03);\r\n  border-bottom:1px solid var(--border);flex-shrink:0;}\r\n.win-bar .fname{margin-left:8px;font-family:var(--mono);font-size:11.5px;color:var(--text-faint);letter-spacing:.02em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}\r\n.win-bar .stepcount{margin-left:auto;font-family:var(--mono);font-size:11px;color:var(--text-faint);white-space:nowrap;padding-left:10px;}\r\n.win-progress{height:3px;background:rgba(255,255,255,.06);flex-shrink:0;}\r\n.win-progress-fill{height:100%;background:linear-gradient(90deg,var(--amber-dim),var(--amber),var(--cyan));\r\n  transition:width .5s cubic-bezier(.4,0,.2,1);width:0%;}\r\n.win-section{display:flex;align-items:center;gap:8px;padding:12px 26px 0;flex-shrink:0;}\r\n.win-section .eyebrow{font-family:var(--mono);font-size:11px;color:var(--amber);letter-spacing:.09em;text-transform:uppercase;display:flex;align-items:center;gap:8px;}\r\n.win-section .eyebrow::before{content:'';width:14px;height:1px;background:var(--amber);}\r\n.win-body{flex:1;overflow-y:auto;padding:14px 26px 26px;}\r\n.win-body::-webkit-scrollbar{width:9px;}\r\n.win-body::-webkit-scrollbar-thumb{background:rgba(255,255,255,.12);border-radius:6px;}\r\n.win-foot{display:flex;justify-content:space-between;align-items:center;gap:10px;padding:14px 20px;\r\n  border-top:1px solid var(--border);flex-shrink:0;background:rgba(255,255,255,.015);}\r\n.wbtn{font-family:var(--mono);font-size:12.5px;background:transparent;color:var(--text-dim);\r\n  border:1px solid var(--border-strong);padding:9px 16px;border-radius:5px;cursor:pointer;\r\n  transition:all .15s ease;display:inline-flex;align-items:center;gap:6px;}\r\n.wbtn:hover:not(:disabled){border-color:var(--cyan);color:var(--cyan);}\r\n.wbtn:disabled{opacity:.3;cursor:not-allowed;}\r\n.wbtn.primary{background:var(--amber);color:#1a1200;border-color:var(--amber);font-weight:600;}\r\n.wbtn.primary:hover:not(:disabled){background:#ffc733;}\r\n.dotrail{display:flex;gap:5px;align-items:center;}\r\n.dotrail span{width:5px;height:5px;border-radius:50%;background:var(--border-strong);}\r\n.dotrail span.on{background:var(--amber);}\r\n\r\nh2.wt{font-family:var(--mono);font-weight:600;font-size:clamp(20px,3.4vw,27px);margin-bottom:6px;color:var(--text);}\r\n.wsub{color:var(--text-faint);font-size:12.5px;font-family:var(--mono);margin-bottom:20px;}\r\np.body-p{color:var(--text-dim);font-size:14.5px;margin-bottom:12px;line-height:1.7;max-width:74ch;}\r\n.tag-tech{font-family:var(--mono);font-size:11px;color:var(--text-dim);border:1px solid var(--border);\r\n  background:rgba(255,255,255,.02);padding:2px 7px;border-radius:3px;display:inline-block;margin:2px 5px 0 0;}\r\n.chip-row{display:flex;flex-wrap:wrap;gap:6px;margin:14px 0 4px;}\r\n\r\n\/* code typing step *\/\r\n.code-block{font-family:var(--mono);font-size:13px;background:var(--panel);border:1px solid var(--border);\r\n  border-radius:8px;padding:20px 22px;white-space:pre-wrap;color:var(--text-dim);min-height:280px;}\r\n.ck{color:#c792ea;} .cs{color:#8fd19e;} .cn{color:var(--amber);} .cc{color:var(--text-faint);} .cp{color:var(--text-dim);} .cf{color:var(--cyan);}\r\n\r\n\/* loader \/ doc *\/\r\n.loader-wrap{max-width:520px;margin:20px auto;text-align:center;}\r\n.loader-status{font-family:var(--mono);font-size:12.5px;color:var(--text-dim);margin-bottom:14px;min-height:18px;}\r\n.loader-track{height:8px;background:rgba(255,255,255,.06);border-radius:5px;overflow:hidden;border:1px solid var(--border);}\r\n.loader-fill{height:100%;width:0%;background:linear-gradient(90deg,var(--amber-dim),var(--amber));transition:width .12s linear;}\r\n.loader-pct{font-family:var(--mono);font-size:22px;color:var(--amber);margin-top:12px;}\r\n.doc-page{font-family:var(--mono);font-size:13.5px;color:var(--text-dim);white-space:pre-wrap;line-height:1.8;\r\n  background:var(--panel);border:1px solid var(--border);border-radius:8px;padding:24px 26px;}\r\n.doc-page .sig{color:var(--text);}\r\n\r\n\/* info grid (about-row reuse) *\/\r\n.info-panel{background:var(--panel);border:1px solid var(--border);border-radius:8px;padding:6px 22px;font-family:var(--mono);font-size:13px;color:var(--text-dim);}\r\n.info-row{display:flex;justify-content:space-between;gap:14px;padding:11px 0;border-bottom:1px dashed var(--border);flex-wrap:wrap;}\r\n.info-row:last-child{border-bottom:none;}\r\n.info-row .k{color:var(--text-faint);flex-shrink:0;}\r\n.info-row .v{color:var(--text);text-align:right;}\r\n.info-row a{color:var(--cyan);text-decoration:none;} .info-row a:hover{text-decoration:underline;}\r\n.note-box{margin-top:16px;font-size:11.5px;color:var(--text-faint);font-family:var(--mono);\r\n  border-left:2px solid var(--border-strong);padding:8px 12px;background:rgba(255,255,255,.02);border-radius:0 4px 4px 0;}\r\n\r\n\/* skills table *\/\r\n.mtable{width:100%;border-collapse:collapse;font-family:var(--mono);font-size:12px;}\r\n.mtable th{text-align:left;color:var(--amber);font-weight:600;padding:8px 10px;border-bottom:1px solid var(--border-strong);\r\n  position:sticky;top:0;background:rgba(9,21,48,.96);text-transform:uppercase;letter-spacing:.04em;font-size:10.5px;}\r\n.mtable td{padding:7px 10px;border-bottom:1px solid var(--border);color:var(--text-dim);white-space:nowrap;}\r\n.mtable td:first-child{color:var(--text);white-space:normal;}\r\n.lvl{display:inline-flex;gap:2px;}\r\n.lvl i{width:6px;height:6px;border-radius:50%;background:var(--border-strong);display:inline-block;}\r\n.lvl i.on{background:var(--cyan);}\r\n\r\n\/* skills matrix \u2014 interactive category buttons *\/\r\n.matrix-grid{display:grid;grid-template-columns:1fr;gap:12px;}\r\n@media(min-width:600px){.matrix-grid{grid-template-columns:1fr 1fr;}}\r\n.matrix-btn{display:flex;flex-direction:column;align-items:flex-start;gap:10px;text-align:left;width:100%;\r\n  cursor:pointer;background:var(--panel);border:1px solid var(--border);border-radius:8px;\r\n  padding:16px 18px;font-family:var(--sans);color:var(--text);\r\n  transition:border-color .15s ease,transform .15s ease,background .15s ease;}\r\n.matrix-btn:hover,.matrix-btn:focus-visible{border-color:rgba(255,176,0,.45);background:var(--panel-2);transform:translateY(-2px);outline:none;}\r\n.matrix-btn-top{display:flex;justify-content:space-between;align-items:flex-start;width:100%;gap:10px;}\r\n.matrix-btn h4{font-family:var(--mono);font-size:14px;color:var(--text);line-height:1.3;}\r\n.matrix-btn .matrix-count{font-family:var(--mono);font-size:11px;color:var(--text-faint);}\r\n.matrix-btn-icon{font-family:var(--mono);font-size:10.5px;color:var(--amber);border:1px solid rgba(255,176,0,.3);\r\n  background:rgba(255,176,0,.07);padding:3px 7px;border-radius:4px;flex-shrink:0;}\r\n.matrix-mini-bars{display:flex;gap:3px;width:100%;}\r\n.matrix-mini-bar{flex:1;height:4px;border-radius:2px;background:rgba(255,255,255,.07);overflow:hidden;}\r\n.matrix-mini-fill{height:100%;background:linear-gradient(90deg,var(--amber-dim),var(--amber));}\r\n.matrix-btn-arrow{font-family:var(--mono);color:var(--text-faint);font-size:12px;}\r\n\r\n\/* skill modal (floats above the wizard window) *\/\r\n.skill-modal-overlay{position:fixed;inset:0;z-index:70;background:rgba(4,10,26,.72);backdrop-filter:blur(3px);\r\n  display:flex;align-items:center;justify-content:center;padding:20px;\r\n  opacity:0;visibility:hidden;pointer-events:none;transition:opacity .2s ease,visibility .2s ease;}\r\n.skill-modal-overlay.open{opacity:1;visibility:visible;pointer-events:auto;}\r\n.skill-modal{width:min(620px,100%);max-height:min(76vh,620px);background:var(--bg-deep);\r\n  border:1px solid var(--border-strong);border-radius:8px;box-shadow:0 30px 80px rgba(0,0,0,.6);\r\n  display:flex;flex-direction:column;overflow:hidden;transform:translateY(10px) scale(.98);transition:transform .2s ease;}\r\n.skill-modal-overlay.open .skill-modal{transform:translateY(0) scale(1);}\r\n.skill-modal-bar{display:flex;align-items:center;gap:8px;padding:10px 14px;background:rgba(255,255,255,.03);\r\n  border-bottom:1px solid var(--border);flex-shrink:0;}\r\n.skill-modal-bar .dot{width:10px;height:10px;border-radius:50%;}\r\n.skill-modal-title{margin-left:6px;font-family:var(--mono);font-size:11.5px;color:var(--text-faint);letter-spacing:.02em;\r\n  flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}\r\n.skill-modal-close{background:none;border:1px solid var(--border-strong);color:var(--text-dim);width:26px;height:26px;\r\n  border-radius:6px;font-family:var(--mono);font-size:14px;cursor:pointer;display:flex;align-items:center;\r\n  justify-content:center;flex-shrink:0;transition:background .15s ease,color .15s ease,border-color .15s ease;}\r\n.skill-modal-close:hover{background:rgba(255,107,91,.12);color:var(--red);border-color:rgba(255,107,91,.4);}\r\n.skill-modal-head{padding:16px 20px 4px;flex-shrink:0;}\r\n.skill-modal-head .eyebrow{margin-bottom:6px;}\r\n.skill-modal-head h3{font-family:var(--mono);font-size:17px;color:var(--text);}\r\n.skill-modal-sub{color:var(--text-faint);font-size:11.5px;font-family:var(--mono);margin-top:4px;}\r\n.skill-modal-body{overflow-y:auto;padding:12px 20px 20px;}\r\n.skill-modal-body::-webkit-scrollbar{width:9px;}\r\n.skill-modal-body::-webkit-scrollbar-thumb{background:rgba(255,255,255,.12);border-radius:6px;}\r\n.modal-skill-row{padding:11px 0;border-bottom:1px dashed var(--border);}\r\n.modal-skill-row:last-child{border-bottom:none;}\r\n.modal-skill-top{display:flex;justify-content:space-between;align-items:baseline;gap:10px;margin-bottom:6px;}\r\n.modal-skill-name{font-family:var(--sans);font-weight:500;font-size:13.5px;color:var(--text);}\r\n.modal-skill-meta{font-family:var(--mono);font-size:10.5px;color:var(--text-faint);white-space:nowrap;}\r\n.modal-skill-levelrow{display:flex;align-items:center;gap:10px;}\r\n.modal-skill-bar{flex:1;height:5px;border-radius:3px;background:rgba(255,255,255,.06);overflow:hidden;}\r\n.modal-skill-fill{height:100%;background:linear-gradient(90deg,var(--amber-dim),var(--amber));border-radius:3px;}\r\n.modal-skill-level{font-family:var(--mono);font-size:10px;color:var(--cyan);flex-shrink:0;width:56px;text-align:right;}\r\n.job-modal{width:min(760px,100%);max-height:min(84vh,720px);}\r\n.job-modal .skill-modal-body{padding:20px 24px 26px;}\r\n\r\n\/* install log (career + projects) *\/\r\n.log-head{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;margin-bottom:4px;}\r\n.log-role{font-family:var(--mono);font-size:18px;color:var(--text);font-weight:600;}\r\n.log-co{font-family:var(--sans);font-size:14px;color:var(--cyan);margin-bottom:2px;}\r\n.log-period{font-family:var(--mono);font-size:11.5px;color:var(--text-faint);margin-bottom:16px;display:flex;align-items:center;gap:6px;}\r\n.log-period::before{content:'\u25cf';font-size:8px;color:var(--green);}\r\nul.blist{margin:10px 0 4px;padding:0;list-style:none;}\r\nul.blist li{font-family:var(--mono);font-size:12.5px;color:#8fd19e;padding-left:16px;position:relative;margin-bottom:6px;line-height:1.55;}\r\nul.blist li::before{content:'+';position:absolute;left:0;color:#5aa869;}\r\n.reason-box{margin-top:16px;padding:10px 14px;border-radius:6px;background:rgba(255,107,91,.06);border:1px solid rgba(255,107,91,.25);font-size:12.5px;color:var(--text-dim);font-family:var(--mono);}\r\n.reason-box b{color:var(--red);font-weight:600;}\r\n.contact-list{margin-top:10px;font-family:var(--mono);font-size:12px;color:var(--text-faint);}\r\n.contact-list div{padding:2px 0;}\r\n.proj-card{border:1px solid var(--border);border-radius:8px;padding:16px 18px;margin-bottom:14px;background:rgba(255,255,255,.015);}\r\n.proj-card h4{font-family:var(--mono);font-size:14.5px;color:var(--text);margin-bottom:2px;}\r\n.proj-card .pp{font-family:var(--mono);font-size:11px;color:var(--text-faint);margin-bottom:8px;}\r\n.proj-card p{font-size:13px;color:var(--text-dim);line-height:1.6;margin-bottom:8px;}\r\n\r\n\/* finish screen *\/\r\n.finish-wrap{text-align:center;padding:20px 10px;}\r\n.finish-check{width:64px;height:64px;border-radius:50%;border:2px solid var(--green);display:flex;align-items:center;\r\n  justify-content:center;margin:0 auto 18px;font-size:28px;color:var(--green);}\r\n.finish-list{max-width:420px;margin:22px auto 0;text-align:left;font-family:var(--mono);font-size:12.5px;color:var(--text-dim);}\r\n.finish-list div{padding:6px 0;display:flex;gap:8px;align-items:center;}\r\n.finish-list div::before{content:'\u2713';color:var(--green);font-weight:700;}\r\n\r\n@media(max-width:640px){\r\n  .win{height:94vh;}\r\n  .win-bar .fname{display:none;}\r\n  .info-row{flex-direction:column;align-items:flex-start;gap:2px;}\r\n  .info-row .v{text-align:left;}\r\n}\r\n@media (prefers-reduced-motion: reduce){\r\n  html{scroll-behavior:auto;} .cursor{animation:none;opacity:1;} *{transition-duration:.01ms !important;}\r\n}\r\n\r\n\/* ---------- ENHANCED TECH ANIMATION LAYER ---------- *\/\r\n@keyframes gridPan{0%{background-position:0 0;}100%{background-position:64px 64px;}}\r\n.grid-overlay{position:fixed;inset:0;z-index:1;pointer-events:none;opacity:.4;\r\n  background-image:linear-gradient(rgba(111,227,208,.06) 1px,transparent 1px),\r\n                    linear-gradient(90deg,rgba(111,227,208,.06) 1px,transparent 1px);\r\n  background-size:56px 56px;animation:gridPan 10s linear infinite;}\r\n@keyframes scan{0%{top:-14%;}100%{top:110%;}}\r\n.scanline{position:fixed;left:0;right:0;height:140px;z-index:2;pointer-events:none;\r\n  background:linear-gradient(180deg,transparent,rgba(111,227,208,.07),transparent);\r\n  animation:scan 6.5s linear infinite;}\r\n.scanline.amber{background:linear-gradient(180deg,transparent,rgba(255,176,0,.05),transparent);animation-duration:9.5s;animation-delay:-3s;}\r\n@keyframes shimmer{0%{background-position:-200% 0;}100%{background-position:200% 0;}}\r\n.win-progress-fill{background-image:linear-gradient(90deg,var(--amber-dim),var(--amber),var(--cyan),var(--amber),var(--amber-dim));\r\n  background-size:200% 100%;animation:shimmer 2.4s linear infinite;}\r\n@keyframes dotPulse{0%,100%{box-shadow:0 0 0 0 rgba(126,224,138,.55);}50%{box-shadow:0 0 0 6px rgba(126,224,138,0);}}\r\n.log-period::before{border-radius:50%;animation:dotPulse 1.8s ease-out infinite;}\r\n@keyframes glowPulse{0%,100%{box-shadow:0 0 0 0 rgba(255,176,0,.35);}50%{box-shadow:0 0 16px 3px rgba(255,176,0,.4);}}\r\n.wbtn.primary,.enter-btn{animation:glowPulse 2.6s ease-in-out infinite;}\r\n.matrix-btn{position:relative;overflow:hidden;}\r\n.matrix-btn::after{content:'';position:absolute;inset:0;background:linear-gradient(120deg,transparent 30%,rgba(111,227,208,.08) 50%,transparent 70%);\r\n  background-size:220% 220%;background-position:120% 0;transition:background-position .6s ease;pointer-events:none;}\r\n.matrix-btn:hover::after,.matrix-btn:focus-visible::after{background-position:-20% 0;}\r\n.matrix-btn:hover,.matrix-btn:focus-visible{box-shadow:0 10px 28px rgba(255,176,0,.16),0 0 0 1px rgba(111,227,208,.1) inset;}\r\n.matrix-mini-fill{transition:width 1s cubic-bezier(.4,0,.2,1);}\r\n@keyframes titleSweep{0%,100%{background-position:0% 50%;}50%{background-position:100% 50%;}}\r\nh2.wt{background:linear-gradient(90deg,var(--text) 30%,var(--cyan) 50%,var(--text) 70%);background-size:220% auto;\r\n  -webkit-background-clip:text;background-clip:text;color:transparent;-webkit-text-fill-color:transparent;\r\n  animation:titleSweep 7s ease-in-out infinite;}\r\n@keyframes bob{0%,100%{transform:translateY(0);}50%{transform:translateY(-4px);}}\r\n.terminal{animation:bob 5s ease-in-out infinite;}\r\n.tag-tech{transition:border-color .2s ease,color .2s ease,background .2s ease;}\r\n.tag-tech:hover{border-color:rgba(111,227,208,.5);color:var(--cyan);background:rgba(111,227,208,.06);}\r\n@media (prefers-reduced-motion: reduce){\r\n  .grid-overlay,.scanline,.win-progress-fill,.log-period::before,.wbtn.primary,.enter-btn,h2.wt{animation:none !important;}\r\n}\r\n\r\n<\/style>\r\n<\/head>\r\n<body class=\"locked\">\r\n\r\n<canvas id=\"bg-canvas\" aria-hidden=\"true\"><\/canvas>\r\n<div class=\"noise\" aria-hidden=\"true\"><\/div>\r\n<div class=\"grid-overlay\" aria-hidden=\"true\"><\/div>\r\n<div class=\"scanline\" aria-hidden=\"true\"><\/div>\r\n<div class=\"scanline amber\" aria-hidden=\"true\"><\/div>\r\n\r\n<!-- ============ SPLASH ============ -->\r\n<div id=\"splash\">\r\n  <div class=\"terminal\" role=\"dialog\" aria-label=\"CV boot sequence\">\r\n    <div class=\"terminal-bar\">\r\n      <span class=\"dot r\"><\/span><span class=\"dot y\"><\/span><span class=\"dot g\"><\/span>\r\n      <span class=\"terminal-title\">deon@deonware:~\/setup \u2014 v1.53<\/span>\r\n    <\/div>\r\n    <div class=\"terminal-body\" id=\"typed-body\"><\/div>\r\n    <button class=\"enter-btn\" id=\"enterBtn\">.\/run_setup.exe &nbsp;\u2192<\/button>\r\n  <\/div>\r\n<\/div>\r\n\r\n<!-- ============ WIZARD ============ -->\r\n<div id=\"wizard\">\r\n  <div class=\"win\">\r\n    <div class=\"win-bar\">\r\n      <span class=\"dot r\"><\/span><span class=\"dot y\"><\/span><span class=\"dot g\"><\/span>\r\n      <span class=\"fname\" id=\"winFname\">DEON_CLAASSENS_SETUP.exe<\/span>\r\n      <span class=\"stepcount\" id=\"winStepcount\">Step 1 of 1<\/span>\r\n    <\/div>\r\n    <div class=\"win-progress\"><div class=\"win-progress-fill\" id=\"winProgressFill\"><\/div><\/div>\r\n    <div class=\"win-section\"><span class=\"eyebrow\" id=\"winEyebrow\">Setup<\/span><\/div>\r\n    <div class=\"win-body\" id=\"winBody\"><\/div>\r\n    <div class=\"win-foot\">\r\n      <button class=\"wbtn\" id=\"btnBack\">\u2190 Back<\/button>\r\n      <div class=\"dotrail\" id=\"winDots\"><\/div>\r\n      <button class=\"wbtn primary\" id=\"btnNext\">Next \u2192<\/button>\r\n    <\/div>\r\n  <\/div>\r\n<\/div>\r\n\r\n<!-- ============ SKILL MODAL (floats above wizard) ============ -->\r\n<div class=\"skill-modal-overlay\" id=\"skillModalOverlay\">\r\n  <div class=\"skill-modal\" role=\"dialog\" aria-modal=\"true\" aria-labelledby=\"skillModalTitle\">\r\n    <div class=\"skill-modal-bar\">\r\n      <span class=\"dot r\"><\/span><span class=\"dot y\"><\/span><span class=\"dot g\"><\/span>\r\n      <span class=\"skill-modal-title\" id=\"skillModalWindowTitle\">skills_matrix.json \u2014 query<\/span>\r\n      <button class=\"skill-modal-close\" id=\"skillModalClose\" aria-label=\"Close\">\u2715<\/button>\r\n    <\/div>\r\n    <div class=\"skill-modal-head\">\r\n      <div class=\"eyebrow\" style=\"font-family:var(--mono);font-size:11px;color:var(--amber);letter-spacing:.09em;text-transform:uppercase;\">SELECT * WHERE category =<\/div>\r\n      <h3 id=\"skillModalTitle\"><\/h3>\r\n      <div class=\"skill-modal-sub\" id=\"skillModalSub\"><\/div>\r\n    <\/div>\r\n    <div class=\"skill-modal-body\" id=\"skillModalBody\"><\/div>\r\n  <\/div>\r\n<\/div>\r\n\r\n<!-- ============ JOB MODAL (floats above wizard) ============ -->\r\n<div class=\"skill-modal-overlay\" id=\"jobModalOverlay\">\r\n  <div class=\"skill-modal job-modal\" role=\"dialog\" aria-modal=\"true\" aria-labelledby=\"jobModalTitle\">\r\n    <div class=\"skill-modal-bar\">\r\n      <span class=\"dot r\"><\/span><span class=\"dot y\"><\/span><span class=\"dot g\"><\/span>\r\n      <span class=\"skill-modal-title\" id=\"jobModalWindowTitle\">career_log.json \u2014 record<\/span>\r\n      <button class=\"skill-modal-close\" id=\"jobModalClose\" aria-label=\"Close\">\u2715<\/button>\r\n    <\/div>\r\n    <div class=\"skill-modal-body\" id=\"jobModalBody\" style=\"padding:20px 24px 26px;\"><\/div>\r\n  <\/div>\r\n<\/div>\r\n\r\n<script id=\"cv-data\" type=\"application\/json\">{\"career\": [{\"period\": \"Jul 2022 \u2014 Present\", \"role\": \"Specialist Developer\", \"company\": \"Softsure (Dotsure)\", \"tech\": [\"VB.NET\", \"WebForms\", \"C#\", \".NET Core\", \"Microsoft SQL Server\", \"Web API\", \"System Integration\"], \"body\": [\"Dotsure is a South African insurance broker best known for its pet insurance, built up over 30 years \u2014 much of that success resting on Softsure, its customer-centric web platform, developed over roughly 20 years mostly in VB.NET, WebForms, C# and .NET Core against Microsoft SQL Server.\", \"Drawing on prior insurance-industry experience, I was brought in to build the framework for future development of Dotsure's commercial offering. Once that landed, the next major piece of work was integrating the framework with Dotsure's external rating (rater) component.\", \"Since the framework went live in July 2022, I have been first \u2014 and often last \u2014 line of support for a commercial book that has grown to over 1,300 policies.\"], \"reason\": null, \"contacts\": []}, {\"period\": \"Jun 2020 \u2014 Mar 2022\", \"role\": \"Technical Director\", \"company\": \"ioHammer\", \"tech\": [\"C#\", \"SQL Server\", \"Entity Framework Core\", \"Web API\", \"Microservices Architecture\", \".NET Core 3\", \"ASP \/ ASP.NET\", \"WordPress\", \"IIS\", \"Windows Server\", \"Facebook Integration\", \"HubSpot Integration\", \"Snowplow Integration\"], \"body\": [\"As Technical Director I was sole architect and developer of a new ERP system aimed at an underserved gap in the market \u2014 built on SQL Server and Entity Framework Core, its functionality exposed through RESTful Web APIs and designed to scale out via a Microservices Architecture on .NET Core 3.\", \"Having been around the block a few times by this point, I was also the go-to for advice on which technologies and methodologies actually work well together for robust business solutions.\", \"Alongside the ERP work I kept providing freelance services to international clients. For about 14 months I acted as technical consultant to TMC Property Media, a UK-based online media and advertising agency focused on real-estate investment \u2014 taking ownership of their legacy site and preparing a hybridised ASP MVC \/ Classic ASP.NET \/ .NET Core \/ WordPress platform (with IIS, Windows Server, Facebook, HubSpot and Snowplow integrations) for its next release.\"], \"reason\": \"Moved into an in-house Specialist Developer role at Softsure (Dotsure).\", \"contacts\": []}, {\"period\": \"Apr 2019 \u2014 May 2020\", \"role\": \"Senior Software Developer\", \"company\": \"The Code Group, Port Elizabeth\", \"tech\": [\"SQL Server (T-SQL, stored procedures)\", \"Entity Framework\", \"SSRS\", \"C#\", \".NET Core 2.2 \/ 3.1\", \".NET Classic 4.5\u20134.7\", \"DevExpress Reports\", \"IoC & Moq\", \"xUnit\", \"Test Server\", \"React\", \"Redux\", \"Redis\", \"Azure\", \"Google Cloud Platform\", \"Git\"], \"body\": [\"The Code Group (TCG) was a web development company in Port Elizabeth. They imported me from Cape Town as the technical lead. I reported to the project manager. During my tenure I provided guidance to 8 junior and 3 intermediate developers.\", \"I spent 80% of the day providing support to \u201cRemchannel\u201d, a PwC-driven enterprise-scale HR solution. It was successfully rolled out to 5 countries across the globe, including South Africa.\", \"We were 8 offsite full-stack developers contributing to the team, using agile and scrum to drive the workload.\", \"The service layer is backend entirely built with .NET Core, moving to a Microservices Architecture (MSA). To make TDD more appealing to my peers, I did much work on integration testing with .NET Core.\", \"I left during Covid, to pursue starting my own company.\"], \"reason\": \"Left during Covid to pursue starting my own company.\", \"contacts\": []}, {\"period\": \"Aug 2018 \u2014 Apr 2019\", \"role\": \"Senior Software Developer\", \"company\": \"PaySoft\", \"tech\": [\"MS SQL\", \"Entity Framework (.NET Core)\", \".NET Core 2.2\", \"RabbitMQ\", \".NET Classic 4.5\u20134.6\", \"Git\"], \"body\": [\"PaySoft is South Africa\u2019s biggest online credit, verification, collection and payment processing site. Chances are your company uses them for the payment of your salaries. Payment processing is done via the back-office solution that comprises a series of microservices, all linked via a queuing system and controlled by a kernel.\", \"My task at PaySoft was to integrate their existing backend with DebiCheck.\", \"The back-office, written in C#, made use of both the .NET Classic and .NET Core frameworks. The Entity Framework ORM persisted data to MSSQL 2014. Data access was exclusively provided via stored procedures. RabbitMQ (pub\/sub) moved messages between the different functional areas.\"], \"reason\": \"Family responsibilities took me to Port Elizabeth.\", \"contacts\": [\"William Hurst\"]}, {\"period\": \"Oct 2017 \u2014 Nov 2017  (concurrently with Syntell)\", \"role\": \"Part-Time Contractor\", \"company\": \"Data Simplified (datasimplified.co.za)\", \"tech\": [\"RDBMS\", \"Procedural SQL Server\", \"T-SQL\", \"Temporal tables\", \"Partitioning\"], \"body\": [\"The focus on money laundering \u2014 Data Simplified contracted me to design and build the Enterprise Data Warehouse (EDW) infrastructure for Bank of Athens (BOA).\", \"The database, comprising over 1,000 tables, required that I build a tool that reliably creates a database infrastructure on the fly.\", \"This means an entire ecosystem, built on best practices, with stored procedures and table-valued and scalar functions. Having a solid, function-rich data layer saves on development time, reduces testing requirements and ensures continuity through uniformity.\", \"A key requirement was day-end, and swapping out partitions with millions of rows \u2014 within seconds.\"], \"reason\": \"Project was completed.\", \"contacts\": [\"John du Toit \u2014 Founder & Director\", \"Michael Hamilton \u2014 Managing Director & Founder\"]}, {\"period\": \"Feb 2016 \u2014 Jul 2018\", \"role\": \"Software Developer \u2192 Solutions Architect\", \"company\": \"Syntell \u2014 paycitybusiness.co.za\", \"tech\": [\"C#\", \"RDBMS\", \"Procedural SQL Server\", \"T-SQL\", \"Entity Framework\", \"WCF\", \"ASP.NET MVC\", \"JavaScript\", \"AJAX\", \".NET Core 1.x (pre .NET Standard)\"], \"body\": [\"Pay City Business (PCB) is an online payment gateway for businesses to pay their traffic fines through. It is an ASP.NET MVC project that runs on MS SQL Server 2014.\", \"Initially employed as Senior Developer, I was soon promoted to Solutions Architect for PCB. I almost exclusively worked remotely. I reported directly to the development manager, with a team of 5 developers reporting to me.\", \"I also performed a lot of ad hoc development for upper management and other business units within Syntell. This included rescuing an SSRS reporting tool built with .NET Core 1.1, long before the SSRS Report Viewer was available for use by the .NET Core framework. I created a workaround to save the project.\"], \"reason\": \"Change of ownership required 80km of travel per day, which was not viable.\", \"contacts\": [\"Herman Lombard \u2014 Technical reference\", \"Michael Hamilton \u2014 Syntell GM, E-commerce\", \"Christopher Cousens \u2014 Current Syntell Software Development Manager, E-commerce\"]}, {\"period\": \"Sep 2014 \u2014 Oct 2015  (Permanent)\", \"role\": \"Technical Consultant\", \"company\": \"Karabina Solutions\", \"tech\": [\"SQL Server 2008R2 \/ 2012R2 \/ 2014\", \"T-SQL & procedural SQL\", \"SSRS\", \"Report Builder 3.0\", \"PostgreSQL (Greenplum)\", \"VB.NET\", \"Microsoft PDW \/ MPP\", \"Tableau\", \"TimeXtender\"], \"body\": [\"To a consultant, no two days are alike. I was involved in delivering services to clients: TFG, Pick n Pay, Fruitways, Microsoft (MPP SQL), RCS and The AVI Group (I&J), as well as Indigo Brands and Green Cross \u2014 I am sure I missed a few\u2026\", \"My involvement with Karabina spanned across many disciplines and technologies. I acted as ETL developer, SQL consultant, report builder, database optimisation specialist and general troubleshooter to clients. This meant finding and solving defects in other developers\u2019 software, quickly.\", \"I was involved in the completion (and roll-out) of a Win32 application, used country-wide to determine pricing of a large portion of Pick n Pay products. It was built with VB.NET and interfaced with Greenplum (an MPP PostgreSQL implementation).\"], \"reason\": \"I was missing software development immensely.\", \"contacts\": [\"Diane Spence\"]}, {\"period\": \"Feb 2014 \u2014 Sep 2014  (Contracting)\", \"role\": \"Contract Developer\", \"company\": \"Software Colors \u2014 worldpigeonracing (wprol.com)\", \"tech\": [\"SQL Server 2008R2\", \"SSRS\", \"Workflow\", \"C#\", \"ASP.NET\", \"Report Builder 3.0\", \"CSS\", \"AJAX\", \"JavaScript\", \"SQL\", \"WCF\"], \"body\": [\"I was one of three developers at Software Colors; we serviced the World Pigeon Racing website (both online and their back office). The back office was designed to accept large volumes of bet-related information and calculate subsequent results. The distributed design was rather complex for the time \u2014 for instance, a solver was employed to determine race results.\", \"This brilliantly put-together system was made up of the database, several workflow services and an ASP.NET front end. LINQ to SQL was generously used throughout, giving rare insight into bottlenecks in the data flow between the database and the application layer.\"], \"reason\": \"Short-term contract expired.\", \"contacts\": [\"Celestine Donough \u2014 Director\"]}, {\"period\": \"8 Jan 2012 \u2014 Dec 2013  (Full time)\", \"role\": \"MXit Developer, \u201cCore Team\u201d \/ Business Intelligence Developer\", \"company\": \"MXit Lifestyle\", \"tech\": [\"SQL Server 2008R2\", \"SSRS\", \"SSIS\", \"C#\", \"ASP.NET\", \"Report Builder 3.0\", \"CSS\", \"AJAX\", \"JavaScript\", \"SQL\", \"MVC 3\", \"WCF\", \"SSAS Tabular\", \"OLAP\", \"Hadoop\", \"Java (Spring)\"], \"body\": [\"I was required to build a few ad hoc applications using Java, in order to extend Hadoop and allow for seamless integration with the very complex (but very, very well written) MXit proxy module.\"], \"bullets\": [\"WCF and ASP.NET web services\", \"Core API development (C#)\", \"Ad hoc web development, data analysis\", \"SSRS report building\", \"RDBMS database design\", \"Java (Spring)\", \"Data porting via SQL, SSIS, SQL Server 2008R2 and specifically-created applications\", \"Platform-independent data porting \u2014 structuring RDBMS source data and automating population to the (then still very much experimental) massive-parallel-processing NoSQL platform, Hadoop\", \"BI dashboard optimisation\"], \"reason\": \"Unsure of future at MXit at the time.\", \"contacts\": [\"Marnus Freeman \u2014 CTO, Technical Director\"]}, {\"period\": \"20 Aug 2011 \u2014 Dec 2011  (Contract)\", \"role\": \"Contractor\", \"company\": \"Ogilvy Cape Town (via Paracon)\", \"tech\": [\"SQL Server 2008R2\", \"SSRS\", \"SSIS\", \"C#\", \"ASP.NET\", \"Report Builder 3.0\", \"CSS\", \"AJAX\", \"JavaScript\"], \"body\": [], \"bullets\": [\"Ad hoc web development\", \"Data analysis\", \"SSRS report building\", \"RDBMS database design\", \"Data porting via SSIS and SQL Server 2008R2\", \"Optimisation of BI dashboard with then Managing Director Johan Human\"], \"reason\": \"Proof-of-concept project for Cashbuild, not taken up by the client. Three-month contract expired and I was head-hunted by DAV for a role at MXit.\", \"contacts\": [\"Johann Muller\"]}, {\"period\": \"Jun 2011 \u2014 Sep 2011  (Part time)\", \"role\": \"Senior Software Developer (sole developer)\", \"company\": \"Liberte Software\", \"tech\": [\"ASP.NET\", \"C# (.NET, Visual Studio 2010)\", \"Microsoft Terminal Services\", \"SQL Server 2005 \/ 2008\", \"Web Services\", \"Web Applications\", \"SOAP\", \"XML\", \"Delphi 7 & Delphi 2005\", \"JavaScript\", \"AJAX\"], \"body\": [\"We provide the Nigerian Pension Commission (PENCOM) and subsidiaries with software to allow for fund administration and even payout of monies to members.\", \"Part-time function entails development of new (and maintenance of legacy) modules of the above software suite, coded in Delphi 2005 and Delphi 7.\"], \"reason\": null, \"contacts\": []}, {\"period\": \"Mar 2011 \u2014 May 2011  (Contract)\", \"role\": \"SQL Developer\", \"company\": \"Taquanta Investment Holdings\", \"tech\": [\"SQL Server 2000 \/ 2005 \/ 2008 \/ 2008R2\"], \"body\": [\"Maintaining existing OLAP database and ETL structures (maintained as stored procedures and functions), derived from data obtained from their current asset management system as well as real-time market pricing data. Post-analysis, the business uses this data to make buy and sell decisions on daily stock market trades.\"], \"reason\": \".NET skills were not being utilised at all.\", \"contacts\": [\"Erna Smith \u2014 then Director of Technology\"]}, {\"period\": \"Mar 2010 \u2014 Feb 2011  (Permanent)\", \"role\": \"Senior Software Developer (sole developer)\", \"company\": \"Liberte Software\", \"tech\": [\"ASP.NET\", \"C# (.NET, Visual Studio 2010)\", \"Microsoft Terminal Services\", \"SQL Server 2005 \/ 2008\", \"Web Services\", \"Web Applications\", \"SOAP\", \"XML\", \"Delphi 7 & Delphi 2005\", \"JavaScript\", \"AJAX\"], \"body\": [\"We provided the Nigerian Pension Commission (PENCOM) and subsidiaries with fund administration software that formalised the entire workflow, up to the payouts to members.\", \"I worked remotely and was instrumental in providing a framework for future ASP.NET development. I built Version 2 of the existing system, migrating from Delphi to ASPX and C#. In the meantime I also maintained and supported the legacy Delphi project, at the time still in production.\"], \"reason\": \"Even though I enjoyed working from home, I found this arrangement placed an unfair amount of stress on my marriage. Thus was the decision made to look elsewhere for employment.\", \"contacts\": []}, {\"period\": \"Feb 2008 \u2014 Mar 2010  (Initial 6-month contract, later permanent)\", \"role\": \"Senior Software Developer, SQL Team Lead\", \"company\": \"Minuco\", \"tech\": [\"ASP.NET\", \"C# (.NET, Visual Studio 2005)\", \"Microsoft Terminal Services\", \"Oracle 10.2 (PL\/SQL, SQL & Admin)\", \"Web Services\", \"SOAP\", \"XML (XSD validation)\"], \"body\": [\"As part of a multinational development team, my time focused primarily on maintaining an ASP.NET (C#) web service (with Oracle 10.2) for Cable & Wireless (a large telephony and communication provider) in the United Kingdom. The system is still in production, known as Vigilize. Test-driven development was encouraged, and regular release cycles ensured occasional interaction with a dedicated team of software testers. Although a PM was present locally, project management was handled via email and daily Skype sessions. The core purpose of Vigilize was to provision BT\u2019s (British Telecom) copper-based connectivity, hardware as well as a legion of other services to the greater UK public (through Cable & Wireless and Virgin).\", \"Although I was also intimately involved in projects related to the application layer (the web service coded in C#), my firm grasp of (and love for) RDBMS-related assignments soon made me the backend \u201cgo-to guy\u201d. Within six months of being employed, I had taken up a team lead role with a group-wide team. I received the opportunity to receive formal training with Oracle University. The result was my sole responsibility to establish and formalise coding standards in the back end.\"], \"reason\": \"Retrenched with effect 1 April 2010.\", \"contacts\": [\"Wilrich Niehaus \u2014 Technical Reference\"]}, {\"period\": \"Jun 2007 \u2014 Feb 2008  (9-month contract)\", \"role\": \"ICT Software Consultant\", \"company\": \"PIC Solutions\", \"tech\": [\"Delphi 7\", \"C# (.NET, Visual Studio 2005)\", \"Microsoft SQL Server 2005\", \"Microsoft Team Foundation Server\", \"Microsoft Terminal Services\", \"Visual Studio 2005 SSIS\", \"SQL Server 2005 Reporting Services\"], \"body\": [\"Database and Application Development.\", \"Automation of daily import and extract of client and application data via text files and XML.\"], \"reason\": \"I was head-hunted by Minuco and made an offer I could not refuse.\", \"contacts\": [\"Larry Roth \u2014 PCS System Architect\"]}, {\"period\": \"Jun 2005 \u2014 Apr 2007  (20 months)\", \"role\": \"Senior Delphi Developer\", \"company\": \"Genasys Technologies\", \"tech\": [\"Delphi 5\", \"Microsoft SQL Server 2000\", \"Terminal Services\", \"MIDAS\", \"DCOM\"], \"body\": [\"Database and Application Development.\"], \"reason\": \"Contract expired.\", \"contacts\": [\"Steve Symes \u2014 MD, Genasys Technologies\"]}, {\"period\": \"Oct 1998 \u2014 May 2005  (67 months)\", \"role\": \"Systems & Programming Manager  \u2192  Analyst Programmer\", \"company\": \"CTP Limited\", \"tech\": [\"Delphi 2 \/ 4 \/ 5 \/ 6 \/ 7\", \"Interbase 4\u20137\", \"Microsoft Windows 98 \/ NT \/ 2000\", \"Citrix MetaFrame\"], \"body\": [], \"bullets\": [\"Project management\", \"System analysis and design\", \"Development (component and application)\", \"Countrywide troubleshooting\"], \"reason\": \"CTP were moving back towards technologies (COBOL, Unix) that would be counterproductive to my own projected career path.\", \"contacts\": [\"Mr Tony Rorke \u2014 IT Manager, CTP (line manager)\", \"Mr Peter Rorke \u2014 Financial Manager, Plastic Wrap\", \"Mr MDW Short \u2014 Board Chairman, CTP\"]}, {\"period\": \"Jan 2001 \u2014 May 2005  (24 months, after-hours \u2014 with the permission of CTP Limited)\", \"role\": \"After-Hours Contractor\", \"company\": \"PIB \u2014 Performance Information Bureau\", \"tech\": [\"Delphi 5\", \"Microsoft SQL Server 7 & 2000\"], \"body\": [\"Development of a financial benchmarking system, used by VWSA, DCSA, McCarthy SA and Barlow SA.\"], \"reason\": \"Contract was completed with almost no comebacks or support required from my side.\", \"contacts\": [\"Mr Francois Briers \u2014 MD, PIB\"]}, {\"period\": \"Jun 1998 \u2014 Sep 1998  (3 months)\", \"role\": \"Junior Contractor\", \"company\": \"Kanimambo (Brainware Consulting)\", \"tech\": [\"Delphi 1 & 2\"], \"body\": [\"Completion of a Vehicle HP (Hire Purchase) system for Infoplan.\"], \"reason\": \"Moved to Cape Town.\", \"contacts\": [\"Ms L. Bester\"]}], \"projects\": [{\"period\": \"Ongoing since Jul 2022\", \"name\": \"Softsure (Dotsure) Commercial Platform Framework\", \"tech\": [\"VB.NET\", \"WebForms\", \"C#\", \".NET Core\", \"Microsoft SQL Server\", \"Web API\"], \"body\": \"Built the framework underpinning future development of Dotsure's commercial insurance offering, on top of the 20-year-old Softsure platform. Integrated the framework with Dotsure's external rating engine, then took on 1st (and often last) line support for the resulting commercial book, now over 1,300 policies strong.\"}, {\"period\": \"21 months\", \"name\": \"ioHammer ERP System\", \"tech\": [\"C#\", \"SQL Server\", \"Entity Framework Core\", \"Web API\", \"Microservices Architecture\", \".NET Core 3\"], \"body\": \"Sole architect and developer of a new ERP system built on SQL Server and Entity Framework Core, its API surface exposed via RESTful Web API and designed to scale out using a Microservices Architecture on .NET Core 3.\"}, {\"period\": \"14 months\", \"name\": \"TMC Property Media Platform Modernisation\", \"tech\": [\"ASP\", \"ASP.NET\", \".NET Core\", \"SQL Server\", \"WordPress\", \"IIS\", \"Windows Server\", \"Facebook Integration\", \"HubSpot Integration\", \"Snowplow Integration\"], \"body\": \"Acted as technical consultant to this UK-based real-estate investment media and advertising agency, taking ownership of their legacy source code and preparing a hybridised ASP MVC \/ Classic ASP.NET \/ .NET Core \/ WordPress platform for its next market release.\"}, {\"period\": \"12 months\", \"name\": \"MXit External Data Import System\", \"tech\": [\"C#\", \"Microsoft SQL Server 2008 \/ 2008R2 \/ 2012\"], \"body\": \"Built the app from the ground up. It was designed and implemented so that the source could, for instance, be an SSH connection (to Amazon Cloud), a database table, a SQL query, or any plugin that outputs a stream. The data was then stored via a highly configurable output mechanism, writing to Microsoft SQL Server (or potentially any other RDBMS). The destination data was batched, and the source was traceable to origin.\"}, {\"period\": \"12 months\", \"name\": \"MXit API Core Development\", \"tech\": [\"C#\", \"Microsoft SQL Server 2008 \/ 2008R2 \/ 2012\"], \"body\": \"Maintenance of existing code as well as creating new features. Work done here was of a sensitive nature, but was done using C# and a test-driven approach in all cases. We had a continuous integration server that periodically tested authored software.\"}, {\"period\": \"3 months\", \"name\": \"Minuco Cable & Wireless CRM System\", \"tech\": [\"Oracle 10g\", \"Microsoft Visual C# .NET\", \"SQL\"], \"body\": \"Troubleshooting and web service development. Minuco develops the software that Cable & Wireless (United Kingdom) uses to place their orders at BT (British Telecom). Our team was assigned the task of fixing defects caused by our UK counterparts when initially coding the interface (as well as ever-changing specifications). We worked with C# on web service technologies that communicated via SOAP messages, with more new development expected on the same backbone.\"}, {\"period\": \"9 months\", \"name\": \"PCS CMS Implementation\", \"tech\": [\"Delphi 7\", \"Microsoft SQL Server 2005\", \"C#\", \"Visual Basic .NET\", \"SSIS\"], \"body\": \"Database and application development. PIC Solutions focuses only on the financial market, cornering the credit industry. At PIC I worked on a product called PCS \u2014 a very much under-appreciated product in a niche area of the industry, aimed at keeping delinquent debtors in line by dovetailing with any large debtor\u2019s system. It provides a means of siphoning delinquent clients through a decision tree, allowing the credit lender to tailor its approach to different delinquencies. We worked with Delphi 7, using native components only, as well as C# (to which we were migrating from Delphi). We interfaced with SQL Server 2005, which did most of the work through stored procedures.\"}, {\"period\": \"18 months\", \"name\": \"Liberte Software \u2014 Pension Fund Administration Software\", \"tech\": [\"Delphi 2005\", \"Delphi 7\", \"Microsoft SQL Server 2000\"], \"body\": \"Maintenance on existing code as well as creating new features. I was the sole developer providing 2nd-level support to the Nigerian Pension Commission (PENCOM) and subsidiaries, with software to allow for fund administration and even payout of monies to members. Work was done mostly in Delphi 7 and Delphi 2005, with a Microsoft SQL Server backend.\"}, {\"period\": \"8 months\", \"name\": \"Liberte Software \u2014 Custodian Payment System\", \"tech\": [\"C#\", \"C# ASP.NET\", \"Microsoft SQL Server 2005\"], \"body\": \"Built the app from the ground up. I initiated a framework for future ASP.NET development via C# and SQL \u2014 starting at database level and finishing with basic tools such as import\/export\/validation, and the subsequent integration thereof into a new payment system for PENCOM subsidiaries.\"}, {\"period\": \"23 months\", \"name\": \"Cable & Wireless Provisioning System (UK)\", \"tech\": [\"C#\", \"C# ASP.NET\", \"Oracle 10g \/ 11g\"], \"body\": \"Maintenance on existing code as well as creating new features. Minuco develops the software that Cable & Wireless (United Kingdom) uses to provision their communication requests at BT (British Telecom). Our team was assigned the task of fixing defects caused by our UK counterparts when the interface was initially coded. I worked with C# on web service technologies that communicated via SOAP messages. We used Oracle 10g\/11g as the database backend, for which I was team lead at Minuco, and was expected to create ad hoc functionality according to specifications provided by the business analyst team.\"}, {\"period\": \"9 months\", \"name\": \"PCS\", \"tech\": [\"Delphi 5\", \"Microsoft SQL Server 2000\", \"C# (Forms)\"], \"body\": \"Rewriting the original application in C# and maintaining the legacy Delphi application.\"}, {\"period\": \"20 months\", \"name\": \"\u201cSki\u201d \/ \u201cCustomer of One\u201d \u2014 Short-Term Insurance System\", \"tech\": [\"Delphi 5\", \"Microsoft SQL Server 2000\"], \"body\": \"Development.\"}, {\"period\": \"24 months (after hours)\", \"name\": \"Performance Information Bureau \u2014 Back-Office System\", \"tech\": [\"Delphi 5\", \"Microsoft SQL Server 7 & 2000\"], \"body\": \"Partial design, development.\"}, {\"period\": \"67 months\", \"name\": \"CTP Caxton Classified System \u2014 interfacing with debtors system\", \"tech\": [\"Delphi 2 \/ 4 \/ 5\", \"Interbase\"], \"body\": \"Partial design, project management, development and subsequent redevelopment, troubleshooting.\"}, {\"period\": \"67 months\", \"name\": \"Debtors Control System \u2014 interfaced with all of CTP\u2019s PC-based systems\", \"tech\": [\"Delphi 2 \/ 4 \/ 5\", \"Interbase\", \"Project Management\"], \"body\": \"Development, troubleshooting.\"}, {\"period\": \"42 months\", \"name\": \"CTP Order Entry & Stock Control System \u2014 interfacing with debtors system\", \"tech\": [\"Delphi 5\", \"Interbase\", \"Project Management\"], \"body\": \"Development, troubleshooting.\"}, {\"period\": \"18 months\", \"name\": \"CTP Plastic Wrap Manufacture Control System\", \"tech\": [\"Delphi 5\", \"Interbase\"], \"body\": \"Analysis, project management, design, development, troubleshooting.\"}, {\"period\": \"5 months\", \"name\": \"CTP Loan System\", \"tech\": [\"Delphi 5\", \"Interbase\"], \"body\": \"Analysis, design, project management, development, troubleshooting.\"}, {\"period\": \"1 month\", \"name\": \"Arabica Point of Sale System\", \"tech\": [\"Delphi 5\", \"Interbase\"], \"body\": \"Analysis, design, database and application development and subsequent implementation. One successful reference site: Arabica Coffee Cafe.\"}, {\"period\": \"6 months\", \"name\": \"Academic Registration System (I)\", \"tech\": [\"Delphi 5\", \"Interbase\"], \"body\": \"Development.\"}, {\"period\": \"6 months\", \"name\": \"Academic Registration System (II)\", \"tech\": [\"Delphi 5\", \"Interbase\"], \"body\": \"Development.\"}, {\"period\": \"6 months\", \"name\": \"General Ledger System\", \"tech\": [\"Delphi 5\", \"Interbase\"], \"body\": \"Development.\"}, {\"period\": \"6 months\", \"name\": \"Creditors System\", \"tech\": [\"Delphi 5\", \"Interbase\"], \"body\": \"Development.\"}], \"skills\": [[\"Delphi 1-7 and 2005\", \"240 mo\", \"Current\", 5], [\"ASP.NET framework\", \"132 mo\", \"Current\", 4], [\"C#\", \"156 mo\", \"Current\", 5], [\"VB.NET\", \"18 mo\", \"2020\", 3], [\"ASP.NET MVC Pattern (v2\u20135)\", \"56 mo\", \"Current\", 4], [\"NAnt Builder (+ custom implementation)\", \"12 mo\", \"2009\", 3], [\"Visual C# .NET \/ Visual Studio 2005, 2010\u20132019\", \"156 mo\", \"Current\", 5], [\"Visual Basic .NET\", \"6 mo\", \"2014\", 2], [\"Visual Studio SSIS\", \"15 mo\", \"Current\", 3], [\"System Analysis and Design\", \"264 mo\", \"Current\", 5], [\"COM, COM+, ActiveX, MIDAS\", \"45 mo\", \"Current\", 4], [\"Interfacing with Excel, Word, Outlook\", \"70 mo\", \"Current\", 5], [\"Post-compilation scripting via MS scripting engine\", \"24 mo\", \"2007\", 4], [\"Interbase\", \"204 mo\", \"2014\", 5], [\"Microsoft SQL Server (7, 2000)\", \"170 mo\", \"Current\", 5], [\"Microsoft SQL Server 2005\", \"180 mo\", \"Current\", 5], [\"Microsoft SQL Server 2008R2\", \"144 mo\", \"Current\", 5], [\"Microsoft SQL Server 2012\", \"84 mo\", \"Current\", 5], [\"Microsoft SQL Server 2014, 2016, 2017, 2019\", \"36 mo\", \"Current\", 5], [\"Apache Hadoop (HDP)\", \"3 mo\", \"2013\", 1], [\"Apache Flume\", \"2 mo\", \"2013\", 1], [\"Java (Spring)\", \"1 mo\", \"2013\", 1], [\"Writing Stored Procedures & Functions (SQL)\", \"168 mo\", \"Current\", 5], [\"SQL (T-SQL)\", \"264 mo\", \"Current\", 5], [\"MS SSRS\", \"72 mo\", \"Current\", 5], [\"MS SSAS, Tabular\", \"18 mo\", \"2015\", 4], [\"MDX\", \"18 mo\", \"2015\", 3], [\"RDBMS OLAP Database Design\", \"12 mo\", \"2015\", 2], [\"ETL Development & Troubleshooting\", \"24 mo\", \"2015\", 3], [\"RDBMS OLTP Database Design\", \"264 mo\", \"Current\", 5], [\"Data porting (legacy \u2192 RDBMS)\", \"264 mo\", \"Current\", 5], [\"Database & SQL query optimisation\", \"264 mo\", \"Current\", 4], [\"Microsoft Windows (95\u201310)\", \"228 mo\", \"Current\", 5], [\"Microsoft Terminal Services (config & troubleshooting)\", \"114 mo\", \"2019\", 4], [\"Citrix MetaFrame (printing, load balancing, troubleshooting)\", \"54 mo\", \"2005\", 4], [\"MS Visual SourceSafe + TFS + SVN\", \"72 mo\", \"Current\", 4], [\"Git\", \"14 mo\", \"Current\", 5], [\"Oracle 10.2 Administration\", \"22 mo\", \"2010\", 2], [\"LINQ to SQL\", \"41 mo\", \"Current\", 4], [\"Microsoft Workflow\", \"5 mo\", \"Current\", 2], [\"Web Services \u2014 WCF \/ Web API \/ Web API 2 (SOAP, REST, RPC)\", \"48 mo\", \"Current\", 4], [\"SSIS\", \"49 mo\", \"Current\", 4], [\"XML, XSD validation, XPath (C#, T-SQL, Delphi)\", \"49 mo\", \"Current\", 4], [\"Identity Server 2, 3, 4\", \"12 mo\", \"Current\", 4], [\"AVRO\", \"3 mo\", \"2013\", 1], [\"Thrift\", \"2 mo\", \"2013\", 3], [\"Tortoise SVN\", \"52 mo\", \"Current\", 4], [\"Oracle PL\/SQL \u2014 Stored Procedures, Functions, Triggers\", \"18 mo\", \"2010\", 3], [\"Greenplum\", \"3 mo\", \"2015\", 3], [\"PostgreSQL\", \"8 mo\", \"2015\", 4], [\"SQL Server MPP\", \"2 mo\", \"2015\", 2], [\"Building frameworks on RDBMS to lower dev cost\", \"5 mo\", \"Current\", 5], [\"HTML5, CSS\", \"120 mo\", \"Current\", 3], [\"jQuery, JavaScript, TypeScript\", \"36 mo\", \"Current\", 3], [\"Test-Driven Development (TDD)\", \"44 mo\", \"Current\", 4], [\"Web API Integration Testing\", \"15 mo\", \"Current\", 4], [\"Inversion of Control\", \"8 mo\", \"Current\", 5], [\"Entity Framework, EF Core, mORMot, Hibernate, own ORM\", \"120 mo\", \"Current\", 5], [\"ReactJS\", \"6 mo\", \"Current\", 2], [\".NET Core 1.1 \u2013 2.1, 2.2\", \"36 mo\", \"Current\", 4], [\".NET Core 3.1\", \"12 mo\", \"Current\", 4], [\"Azure DevOps\", \"48 mo\", \"Current\", 4], [\"SQL Server Reporting Services REST API\", \"18 mo\", \"Current\", 3]], \"courses\": [[\"1998\", \"2 weeks\", \"Project Management\", \"ISCOR\"], [\"1999\", \"5 days\", \"Borland Delphi 4 Client\/Server Development\", \"Borland\"], [\"1999\", \"1 semester\", \"Pascal Programming I\", \"UNISA\"], [\"1999\", \"1 semester\", \"Pascal Programming II\", \"UNISA\"], [\"2004\", \"5 days\", \"Borland Delphi 7 Web Development\", \"Borland\"], [\"2008\", \"5 days\", \"Oracle Database 11g: Database Administration\", \"Oracle University\"], [\"2008\", \"5 days\", \"Oracle Database 11g: PL\/SQL Development\", \"Oracle University\"]], \"coverLetter\": \"To Whom It May Concern,\\n\\nI am writing to introduce myself as a Specialist Developer and Solutions Architect with 27+ years of production experience, currently building and supporting the commercial-insurance platform at Softsure (Dotsure), where I designed the framework for their commercial offering and integrated it with their external rating engine.\\n\\nMy career spans from Delphi and Interbase systems in 1998 through to modern .NET Core microservices today, across industries including insurance (Softsure\/Dotsure), payments (PaySoft, Pay City Business), telecommunications (Cable & Wireless \/ BT provisioning at Minuco), HR technology (Remchannel at The Code Group), pension fund administration (Liberte Software \/ PENCOM), and enterprise data warehousing (Data Simplified \/ Bank of Athens). Along the way I spent nearly two years as Technical Director at ioHammer, architecting an ERP system on SQL Server, Entity Framework Core and a Microservices Architecture on .NET Core 3, while providing freelance consulting to international clients including TMC Property Media in the UK. I have worked as a solutions architect, technical director, team lead, and sole developer, and have consistently been the person teams turn to for RDBMS design, system architecture, and untangling complex legacy systems.\\n\\nI have found that building genuine relationships with clients and colleagues is as important as the quality and affordability of the solutions I deliver.\\n\\nI would welcome the opportunity to bring this depth of experience to a Solutions Architect, Senior Full-Stack Developer, or Integration Developer role. Please find my complete career history, technical skills matrix, and project archive in the pages that follow.\\n\\nKind regards,\\nDeon Claassens\\n\"}<\/script>\r\n<script>\r\n(function(){\r\n  var DATA = JSON.parse(document.getElementById('cv-data').textContent);\r\n  var reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;\r\n  var renderGen = 0; \/\/ bumped every renderStep() call so stale async callbacks (typing\/loader) can detect they're on an old step and stop touching detached DOM\r\n\r\n  \/* ---------------- BACKGROUND: code-rain canvas ---------------- *\/\r\n  var canvas = document.getElementById('bg-canvas');\r\n  var ctx = canvas.getContext('2d');\r\n  var W,H,cols,drops;\r\n  var glyphs = ['SELECT','01','C#','EF Core','WCF','T-SQL','{ }','=>','.NET','GIT','REST','SSRS','\u03bb','DELPHI','TDD','0x1A','MSA','if(','};','<\/>','SQL','10','JOIN','API','K8s','CI\/CD','JSON','YAML','Azure','101'];\r\n  function resize(){ W=canvas.width=window.innerWidth; H=canvas.height=window.innerHeight;\r\n    cols = Math.floor(W\/86); drops = new Array(cols).fill(0).map(function(){return Math.random()*-40;}); }\r\n  resize();\r\n  window.addEventListener('resize', resize);\r\n  function drawRain(){\r\n    ctx.fillStyle='rgba(10,20,45,0.16)'; ctx.fillRect(0,0,W,H);\r\n    ctx.font='12px Consolas, monospace';\r\n    for(var i=0;i<cols;i++){\r\n      var g = glyphs[Math.floor(Math.random()*glyphs.length)];\r\n      var x = i*86 + 6, y = drops[i]*18;\r\n      var alpha = Math.random()*0.5+0.35;\r\n      var roll = Math.random();\r\n      var color = roll>0.965 ? 'rgba(126,224,138,'+alpha+')' : (roll>0.9 ? 'rgba(255,176,0,'+alpha+')' : 'rgba(111,227,208,'+(alpha*0.55)+')');\r\n      ctx.fillStyle = color;\r\n      ctx.shadowColor = color;\r\n      ctx.shadowBlur = roll>0.9 ? 6 : 0;\r\n      ctx.fillText(g, x, y);\r\n      ctx.shadowBlur = 0;\r\n      if(y>H && Math.random()>0.975) drops[i]=0;\r\n      drops[i]++;\r\n    }\r\n  }\r\n  if(!reduceMotion){ setInterval(drawRain, 90); } else { ctx.fillStyle='var(--bg)'; ctx.fillRect(0,0,W,H); }\r\n\r\n  \/* ---------------- SPLASH TYPING ---------------- *\/\r\n  var body = document.getElementById('typed-body');\r\n  var enterBtn = document.getElementById('enterBtn');\r\n  var lines = [\r\n    {p:'$ ', t:'whoami', cls:'out'},\r\n    {t:'Deon Claassens \u2014 Specialist Developer \/ Solutions Architect', cls:'highlight'},\r\n    {p:'$ ', t:'cat experience.log | tail -1', cls:'out'},\r\n    {t:'27+ years in production. Delphi 1998 \u2192 .NET Core microservices, now at Softsure (Dotsure).', cls:'out'},\r\n    {p:'$ ', t:'.\/run_setup.exe --target=career', cls:'out'},\r\n    {t:'Preparing installer: profile, cover letter, career log, project archive...', cls:'out'},\r\n    {t:'Setup is ready.', cls:'highlight'}\r\n  ];\r\n  var li=0, ci=0;\r\n  function typeSequence(){\r\n    if(li>=lines.length){ enterBtn.classList.add('show'); return; }\r\n    var entry = lines[li];\r\n    var div = document.createElement('div'); div.className='line';\r\n    var prompt = entry.p ? '<span class=\"prompt\">'+entry.p+'<\/span>' : '';\r\n    div.innerHTML = prompt + '<span class=\"'+entry.cls+'\"><\/span><span class=\"cursor\"><\/span>';\r\n    body.appendChild(div);\r\n    var target = div.querySelector('.'+entry.cls);\r\n    var full = entry.t;\r\n    target.textContent = full;\r\n    div.querySelector('.cursor').remove();\r\n    li++; setTimeout(typeSequence, reduceMotion?0:60);\r\n  }\r\n  if(reduceMotion){\r\n    lines.forEach(function(entry){\r\n      var div=document.createElement('div'); div.className='line';\r\n      div.innerHTML=(entry.p?'<span class=\"prompt\">'+entry.p+'<\/span>':'')+'<span class=\"'+entry.cls+'\">'+entry.t+'<\/span>';\r\n      body.appendChild(div);\r\n    });\r\n    enterBtn.classList.add('show');\r\n  } else { typeSequence(); }\r\n\r\n  function dismissSplash(){\r\n    document.getElementById('splash').classList.add('hidden');\r\n    document.getElementById('wizard').classList.add('show');\r\n    document.body.classList.remove('locked');\r\n    renderStep(0);\r\n  }\r\n  enterBtn.addEventListener('click', dismissSplash);\r\n  window.addEventListener('keydown', function(e){ if(e.key==='Enter' && enterBtn.classList.contains('show') && !document.getElementById('wizard').classList.contains('show')) dismissSplash(); });\r\n\r\n  \/* ---------------- STEP DEFINITIONS ---------------- *\/\r\n  function el(html){ var d=document.createElement('div'); d.innerHTML=html; return d; }\r\n  function esc(s){ return (s||'').replace(\/&\/g,'&amp;').replace(\/<\/g,'&lt;'); }\r\n  function lvldots(n){ var s=''; for(var i=1;i<=5;i++){ s+='<i class=\"'+(i<=n?'on':'')+'\"><\/i>'; } return '<span class=\"lvl\">'+s+'<\/span>'; }\r\n\r\n  var steps = [];\r\n\r\n  \/\/ STEP: profile \"typed like code\"\r\n  steps.push({section:'Setup', fname:'profile.cs', title:'Compiling Profile', render:function(c){\r\n    c.innerHTML =\r\n      '<h2 class=\"wt\">profile.cs<\/h2><p class=\"wsub\">\/\/ generated at boot \u2014 identity, contact, objective<\/p>'+\r\n      '<div class=\"code-block\" id=\"codeType\"><\/div>';\r\n    var target = document.getElementById('codeType');\r\n    var myGen = renderGen;\r\n    var codeLines = [\r\n      ['<span class=\"ck\">public class<\/span> <span class=\"cf\">DeonClaassens<\/span> <span class=\"cp\">: <\/span><span class=\"cf\">ISolutionsArchitect<\/span>', '{'],\r\n      ['    <span class=\"cn\">Title<\/span> <span class=\"cp\">=<\/span> <span class=\"cs\">\"Specialist Developer, Softsure (Dotsure)\"<\/span><span class=\"cp\">;<\/span>'],\r\n      ['    <span class=\"cn\">Seeking<\/span> <span class=\"cp\">=<\/span> <span class=\"cs\">\"Solutions Architect \u00b7 Senior Full-Stack (backend-heavy) \u00b7 Integration Developer\"<\/span><span class=\"cp\">;<\/span>'],\r\n      ['    <span class=\"cn\">ContractType<\/span> <span class=\"cp\">=<\/span> <span class=\"cs\">\"Contract \/ Freelance\"<\/span><span class=\"cp\">;<\/span>'],\r\n      ['    <span class=\"cn\">Phone<\/span> <span class=\"cp\">=<\/span> <span class=\"cs\">\"+27 61 209 0321\"<\/span><span class=\"cp\">;<\/span>'],\r\n      ['    <span class=\"cn\">Email<\/span> <span class=\"cp\">=<\/span> <span class=\"cs\">\"d_claassens@hotmail.com\"<\/span><span class=\"cp\">;<\/span>'],\r\n      ['    <span class=\"cn\">Location<\/span> <span class=\"cp\">=<\/span> <span class=\"cs\">\"26 Camelot Close, George, Western Cape, 6529\"<\/span><span class=\"cp\">;<\/span>'],\r\n      ['    <span class=\"cn\">YearsInProduction<\/span> <span class=\"cp\">=<\/span> <span class=\"cn\">27<\/span><span class=\"cp\">;<\/span>'],\r\n      ['    <span class=\"cc\">\/\/ Career objective, verbatim<\/span>'],\r\n      ['    <span class=\"cn\">Objective<\/span> <span class=\"cp\">=<\/span> <span class=\"cs\">\"A business with my name on it.\"<\/span><span class=\"cp\">;<\/span>'],\r\n      ['}']\r\n    ];\r\n    var i=0;\r\n    function next(){\r\n      if(myGen !== renderGen) return; \/\/ user navigated away \u2014 stop the stale typing loop\r\n      if(i>=codeLines.length){ return; }\r\n      var p=document.createElement('div'); p.innerHTML = codeLines[i].join('<br>'); target.appendChild(p);\r\n      i++; setTimeout(next, reduceMotion?0:12);\r\n    }\r\n    next();\r\n  }});\r\n\r\n  \/\/ STEP: cover letter loader + doc\r\n  steps.push({section:'Setup', fname:'cover_letter.docx \u2014 Installing', title:'Installing Cover Letter', render:function(c){\r\n    c.innerHTML =\r\n      '<h2 class=\"wt\">Installing cover_letter.docx<\/h2><p class=\"wsub\">Please wait while Setup prepares your documents.<\/p>'+\r\n      '<div class=\"loader-wrap\"><div class=\"loader-status\" id=\"ldStatus\">Extracting files\u2026<\/div>'+\r\n      '<div class=\"loader-track\"><div class=\"loader-fill\" id=\"ldFill\"><\/div><\/div><div class=\"loader-pct\" id=\"ldPct\">0%<\/div><\/div>'+\r\n      '<div id=\"letterWrap\" style=\"margin-top:22px;\"><\/div>';\r\n    var msgs = ['Extracting files\u2026','Reading career.log\u2026','Linking skills.json\u2026','Formatting document\u2026','Finalising\u2026'];\r\n    var fill=document.getElementById('ldFill'), pct=document.getElementById('ldPct'), st=document.getElementById('ldStatus'), letterWrap=document.getElementById('letterWrap');\r\n    var myGen = renderGen;\r\n    var p=0;\r\n    function tick(){\r\n      if(myGen !== renderGen) return; \/\/ user navigated away \u2014 stop the stale loader loop\r\n      p += reduceMotion?100:(2+Math.random()*4);\r\n      if(p>100) p=100;\r\n      fill.style.width=p+'%'; pct.textContent=Math.floor(p)+'%';\r\n      st.textContent = msgs[Math.min(msgs.length-1, Math.floor(p\/100*msgs.length))];\r\n      if(p<100){ setTimeout(tick, 90); }\r\n      else {\r\n        letterWrap.innerHTML =\r\n          '<div class=\"doc-page\">'+esc(DATA.coverLetter)+'<\/div>';\r\n      }\r\n    }\r\n    tick();\r\n  }});\r\n\r\n  \/\/ STEP: contact & availability\r\n  steps.push({section:'Setup', fname:'contact.ini', title:'Contact & Availability', render:function(c){\r\n    c.innerHTML =\r\n      '<h2 class=\"wt\">contact.ini<\/h2><p class=\"wsub\">reachability configuration<\/p>'+\r\n      '<div class=\"info-panel\">'+\r\n      row('phone (cell)', '<a href=\"tel:+27612090321\">061 209 0321<\/a>')+\r\n      row('email', '<a href=\"mailto:d_claassens@hotmail.com\">d_claassens@hotmail.com<\/a>')+\r\n      row('physical address', '26 Camelot Close, George, Western Cape, 6529')+\r\n      row('availability', 'Flexible \u2014 please speak to me about urgent requests')+\r\n      row('current position', 'Specialist Developer, Softsure (Dotsure)')+\r\n      '<\/div>';\r\n  }});\r\n\r\n  \/\/ STEP: personal & career objective\r\n  steps.push({section:'Setup', fname:'personal.yaml', title:'Personal & Career Details', render:function(c){\r\n    c.innerHTML =\r\n      '<h2 class=\"wt\">personal.yaml<\/h2><p class=\"wsub\">additional personal &amp; career configuration<\/p>'+\r\n      '<div class=\"info-panel\">'+\r\n      row('title', 'Mr')+ row('date of birth', '1975\/10\/02')+ row('gender', 'Male')+\r\n      row('nationality', 'South African')+ row('affirmative action', 'No')+\r\n      row('highest qualification', 'N.Dip Analytical Chemistry [N.Dip Anal. (Chem)]')+\r\n      row('required position', 'Solutions Architect \u00b7 Freelance Senior Full-Stack Developer (backend-heavy) \u00b7 Analyst Software Developer \u00b7 Integration Developer')+\r\n      row('position type', 'Contract')+\r\n      row('career level', 'Solutions Architect \u2014 Mid-Career')+\r\n      '<\/div>'+\r\n      '<div class=\"note-box\">ID number withheld from this public page for privacy \u2014 available directly on request.<\/div>';\r\n  }});\r\n\r\n  \/\/ STEP: career level ratings + main skills paragraph\r\n  steps.push({section:'Setup', fname:'career_level.log', title:'Career Level Ratings', render:function(c){\r\n    c.innerHTML =\r\n      '<h2 class=\"wt\">career_level.log<\/h2><p class=\"wsub\">self-rated career-stage per discipline<\/p>'+\r\n      '<div class=\"info-panel\">'+\r\n      row('Delphi', 'Accomplished')+\r\n      row('C# on MS Windows (WinForms)', 'Accomplished')+\r\n      row('C# Web ASP.NET MVC', 'Upper Mid-Career')+\r\n      row('Web Service (SOA)', 'Accomplished')+\r\n      row('Web Service (MSA)', 'Mid-Career')+\r\n      row('Test-Driven Development', 'Accomplished')+\r\n      row('Web Development (C#, VB.NET, ASP.NET, JavaScript)', 'Upper Mid-Career')+\r\n      row('Business Intelligence', 'Mid-Career')+\r\n      '<\/div>'+\r\n      '<p class=\"body-p\" style=\"margin-top:18px;\"><b style=\"color:var(--text)\">Main skills \u2014<\/b> I am a Solutions Provider. I design and build any concept from idea to realisation. Delphi 1 to 2007, Interbase 4\u20137, Windows XP\/2000\/7\/8.1\/10, Microsoft SQL Server (7, 2000, 2005, 2008, 2008R2, 2012, 2014, 2016, 2017), RDBMS database design, system design, object-oriented design, project management, C#, WCF, SOA, data migration, Oracle 10g\/11g PL\/SQL and Admin, PostgreSQL, Web API, C#, VB.NET, ASP.NET, JavaScript, CSS, Tabular, SSAS, SSRS, SSIS, TDD.<\/p>';\r\n  }});\r\n\r\n  \/\/ STEP: education secondary\r\n  steps.push({section:'Setup', fname:'education_secondary.md', title:'Secondary Schooling', render:function(c){\r\n    c.innerHTML =\r\n      '<h2 class=\"wt\">education_secondary.md<\/h2><p class=\"wsub\">secondary schooling record<\/p>'+\r\n      '<div class=\"info-panel\">'+\r\n      row('year matriculated', '1993')+\r\n      row('last school attended', 'Ho\u00ebrskool Zwartkop')+\r\n      row('subjects', 'Afrikaans HG, English HG*, Mathematics HG, Physical Science HG, Accounting HG, Woodwork HG')+\r\n      '<\/div>';\r\n  }});\r\n\r\n  \/\/ STEP: education tertiary\r\n  steps.push({section:'Setup', fname:'education_tertiary.md', title:'Tertiary Education', render:function(c){\r\n    c.innerHTML =\r\n      '<h2 class=\"wt\">education_tertiary.md<\/h2><p class=\"wsub\">tertiary qualification<\/p>'+\r\n      '<div class=\"info-panel\">'+\r\n      row('year', '1997')+\r\n      row('qualification', 'N.Dip. Analytical Chemistry (1997)')+\r\n      row('institution', 'Pretoria Technicon')+\r\n      row('subjects', 'Mathematics 2, Inorganic Chemistry 4, Chemistry 4, Organic Chemistry 4, Statistics 2')+\r\n      '<\/div>';\r\n  }});\r\n\r\n  \/\/ STEP: courses attended\r\n  steps.push({section:'Setup', fname:'courses.csv', title:'Courses Attended', render:function(c){\r\n    var rows = DATA.courses.map(function(r){\r\n      return '<tr><td>'+r[0]+'<\/td><td>'+r[1]+'<\/td><td>'+esc(r[2])+'<\/td><td>'+esc(r[3])+'<\/td><\/tr>';\r\n    }).join('');\r\n    c.innerHTML =\r\n      '<h2 class=\"wt\">courses.csv<\/h2><p class=\"wsub\">formal courses &amp; training attended<\/p>'+\r\n      '<div style=\"overflow:auto;border:1px solid var(--border);border-radius:8px;\">'+\r\n      '<table class=\"mtable\"><thead><tr><th>Year<\/th><th>Duration<\/th><th>Course<\/th><th>Institution<\/th><\/tr><\/thead>'+\r\n      '<tbody>'+rows+'<\/tbody><\/table><\/div>';\r\n  }});\r\n\r\n  \/\/ skill \u2192 category map (mirrors the CV's own Skills Matrix table)\r\n  var skillCatMap = {\r\n    'Delphi 1-7 and 2005':'Language \/ Framework','ASP.NET framework':'Language \/ Framework','C#':'Language \/ Framework',\r\n    'VB.NET':'Language \/ Framework','ASP.NET MVC Pattern (v2\u20135)':'Language \/ Framework',\r\n    'NAnt Builder (+ custom implementation)':'Other','Visual C# .NET \/ Visual Studio 2005, 2010\u20132019':'Language \/ Framework',\r\n    'Visual Basic .NET':'Language \/ Framework','Visual Studio SSIS':'BI \/ Reporting',\r\n    'System Analysis and Design':'Methodology \/ Architecture','COM, COM+, ActiveX, MIDAS':'Other',\r\n    'Interfacing with Excel, Word, Outlook':'Other','Post-compilation scripting via MS scripting engine':'Other',\r\n    'Interbase':'Database','Microsoft SQL Server (7, 2000)':'Database','Microsoft SQL Server 2005':'Database',\r\n    'Microsoft SQL Server 2008R2':'Database','Microsoft SQL Server 2012':'Database',\r\n    'Microsoft SQL Server 2014, 2016, 2017, 2019':'Database','Apache Hadoop (HDP)':'Other','Apache Flume':'Other',\r\n    'Azure DevOps':'Tooling \/ Infrastructure','SQL Server Reporting Services REST API':'BI \/ Reporting',\r\n    'Java (Spring)':'Language \/ Framework','Writing Stored Procedures & Functions (SQL)':'Other','SQL (T-SQL)':'Other',\r\n    'MS SSRS':'BI \/ Reporting','MS SSAS, Tabular':'Database','MDX':'Database','RDBMS OLAP Database Design':'Database',\r\n    'ETL Development & Troubleshooting':'BI \/ Reporting','RDBMS OLTP Database Design':'Database',\r\n    'Data porting (legacy \u2192 RDBMS)':'Database','Database & SQL query optimisation':'Database',\r\n    'Microsoft Windows (95\u201310)':'Tooling \/ Infrastructure','Microsoft Terminal Services (config & troubleshooting)':'Tooling \/ Infrastructure',\r\n    'Citrix MetaFrame (printing, load balancing, troubleshooting)':'Tooling \/ Infrastructure',\r\n    'MS Visual SourceSafe + TFS + SVN':'Tooling \/ Infrastructure','Git':'Tooling \/ Infrastructure',\r\n    'Oracle 10.2 Administration':'Database','LINQ to SQL':'Other','Microsoft Workflow':'Tooling \/ Infrastructure',\r\n    'Web Services \u2014 WCF \/ Web API \/ Web API 2 (SOAP, REST, RPC)':'Integration \/ Web Services','SSIS':'BI \/ Reporting',\r\n    'XML, XSD validation, XPath (C#, T-SQL, Delphi)':'Language \/ Framework','Identity Server 2, 3, 4':'Integration \/ Web Services',\r\n    'AVRO':'Other','Thrift':'Other','Tortoise SVN':'Tooling \/ Infrastructure',\r\n    'Oracle PL\/SQL \u2014 Stored Procedures, Functions, Triggers':'Database','Greenplum':'Database','PostgreSQL':'Database',\r\n    'SQL Server MPP':'Database','Building frameworks on RDBMS to lower dev cost':'Database','HTML5, CSS':'Language \/ Framework',\r\n    'jQuery, JavaScript, TypeScript':'Language \/ Framework','Test-Driven Development (TDD)':'Methodology \/ Architecture',\r\n    'Web API Integration Testing':'Integration \/ Web Services','Inversion of Control':'Methodology \/ Architecture',\r\n    'Entity Framework, EF Core, mORMot, Hibernate, own ORM':'Methodology \/ Architecture','ReactJS':'Other',\r\n    '.NET Core 1.1 \u2013 2.1, 2.2':'Language \/ Framework','.NET Core 3.1':'Language \/ Framework'\r\n  };\r\n  var catOrder = ['Language \/ Framework','Database','BI \/ Reporting','Methodology \/ Architecture','Integration \/ Web Services','Tooling \/ Infrastructure','Other'];\r\n  var catLabel = {'Language \/ Framework':'Languages & Frameworks','Database':'Data & Databases','BI \/ Reporting':'BI & Reporting',\r\n    'Methodology \/ Architecture':'Architecture & Practice','Integration \/ Web Services':'Integration & Web Services',\r\n    'Tooling \/ Infrastructure':'Tools & Infrastructure','Other':'Other Tooling & Skills'};\r\n  var grouped = {};\r\n  DATA.skills.forEach(function(r){\r\n    var cat = skillCatMap[r[0]] || 'Other';\r\n    (grouped[cat] = grouped[cat] || []).push(r);\r\n  });\r\n\r\n  \/\/ STEP: skills matrix \u2014 interactive category buttons opening a modal\r\n  steps.push({section:'Setup', fname:'skills_matrix.json', title:'Skills Matrix', render:function(c){\r\n    var total = DATA.skills.length, expert = DATA.skills.filter(function(r){return r[3]===5;}).length, current = DATA.skills.filter(function(r){return r[2]==='Current';}).length;\r\n    c.innerHTML =\r\n      '<h2 class=\"wt\">skills_matrix.json<\/h2><p class=\"wsub\">'+total+' tracked skills \u00b7 tap a category to query the full breakdown<\/p>'+\r\n      '<div class=\"chip-row\" style=\"margin-bottom:16px;\"><span class=\"tag-tech\">'+total+' skills tracked<\/span><span class=\"tag-tech\">'+expert+' rated Level 5<\/span><span class=\"tag-tech\">'+current+' still current<\/span><\/div>'+\r\n      '<div class=\"matrix-grid\" id=\"matrixGrid\"><\/div>';\r\n    var grid = c.querySelector('#matrixGrid');\r\n    catOrder.forEach(function(cat){\r\n      var rows = grouped[cat] || [];\r\n      if(!rows.length) return;\r\n      var avg = rows.reduce(function(a,b){return a+b[3];},0) \/ rows.length;\r\n      var bars = rows.slice(0,12).map(function(r){ return '<div class=\"matrix-mini-bar\"><div class=\"matrix-mini-fill\" data-w=\"'+(r[3]*20)+'\" style=\"width:0%\"><\/div><\/div>'; }).join('');\r\n      var btn = document.createElement('button');\r\n      btn.type='button'; btn.className='matrix-btn'; btn.setAttribute('aria-haspopup','dialog');\r\n      btn.innerHTML =\r\n        '<div class=\"matrix-btn-top\"><div><h4>'+catLabel[cat]+'<\/h4><span class=\"matrix-count\">'+rows.length+' skills \u00b7 avg level '+avg.toFixed(1)+'<\/span><\/div>'+\r\n        '<span class=\"matrix-btn-icon\">'+cat.split(' \/ ')[0].slice(0,3).toUpperCase()+'<\/span><\/div>'+\r\n        '<div class=\"matrix-mini-bars\">'+bars+'<\/div><span class=\"matrix-btn-arrow\">open matrix \u2192<\/span>';\r\n      btn.addEventListener('click', function(){ openSkillModal(cat, rows); });\r\n      grid.appendChild(btn);\r\n    });\r\n    requestAnimationFrame(function(){\r\n      setTimeout(function(){\r\n        c.querySelectorAll('.matrix-mini-fill').forEach(function(el){ el.style.width = el.getAttribute('data-w')+'%'; });\r\n      }, 30);\r\n    });\r\n  }});\r\n\r\n  \/\/ STEP: career log \u2014 interactive job buttons opening a modal (one window per employer)\r\n  steps.push({section:'Career Log', fname:'career_log.json', title:'Career History', render:function(c){\r\n    c.innerHTML =\r\n      '<h2 class=\"wt\">career_log.json<\/h2><p class=\"wsub\">'+DATA.career.length+' positions, 1998 \u2192 present \u00b7 tap any role to query the full record<\/p>'+\r\n      '<div class=\"chip-row\" style=\"margin-bottom:16px;\"><span class=\"tag-tech\">'+DATA.career.length+' positions logged<\/span><span class=\"tag-tech\">1998 \u2014 present<\/span><span class=\"tag-tech\">27+ years in production<\/span><\/div>'+\r\n      '<div class=\"matrix-grid\" id=\"careerGrid\"><\/div>';\r\n    var grid = c.querySelector('#careerGrid');\r\n    DATA.career.forEach(function(j, idx){\r\n      var previewChips = j.tech.slice(0,3).map(function(t){ return '<span class=\"tag-tech\">'+esc(t)+'<\/span>'; }).join('');\r\n      var moreCount = j.tech.length - 3;\r\n      if(moreCount > 0) previewChips += '<span class=\"tag-tech\">+'+moreCount+' more<\/span>';\r\n      var btn = document.createElement('button');\r\n      btn.type='button'; btn.className='matrix-btn'; btn.setAttribute('aria-haspopup','dialog');\r\n      btn.innerHTML =\r\n        '<div class=\"matrix-btn-top\"><div><h4>'+esc(j.role)+'<\/h4><span class=\"matrix-count\">'+esc(j.company)+'<\/span><\/div>'+\r\n        '<span class=\"matrix-btn-icon\">'+String(DATA.career.length-idx).padStart(2,'0')+'<\/span><\/div>'+\r\n        '<div class=\"matrix-count\" style=\"font-family:var(--mono);\">'+esc(j.period)+'<\/div>'+\r\n        '<div class=\"chip-row\" style=\"margin:0;\">'+previewChips+'<\/div><span class=\"matrix-btn-arrow\">open record \u2192<\/span>';\r\n      btn.addEventListener('click', function(){ openJobModal(j, idx); });\r\n      grid.appendChild(btn);\r\n    });\r\n  }});\r\n\r\n  \/\/ STEPS: project archive (chunks of 4)\r\n  var chunkSize = 4;\r\n  for(var pi=0; pi<DATA.projects.length; pi+=chunkSize){\r\n    (function(chunk, chunkIdx, total){\r\n      steps.push({section:'Project Archive', fname:'projects_'+(chunkIdx+1)+'.log', title:'Project Highlights', render:function(c){\r\n        var cards = chunk.map(function(pr){\r\n          return '<div class=\"proj-card\"><h4>'+esc(pr.name)+'<\/h4><div class=\"pp\">'+esc(pr.period)+'<\/div>'+\r\n            '<p>'+esc(pr.body)+'<\/p>'+\r\n            '<div class=\"chip-row\">'+pr.tech.map(function(t){return '<span class=\"tag-tech\">'+esc(t)+'<\/span>';}).join('')+'<\/div><\/div>';\r\n        }).join('');\r\n        c.innerHTML =\r\n          '<h2 class=\"wt\">Project Highlight Archive<\/h2><p class=\"wsub\">selected projects, in full \u2014 part '+(chunkIdx+1)+' of '+total+'<\/p>'+\r\n          cards;\r\n      }});\r\n    })(DATA.projects.slice(pi, pi+chunkSize), Math.floor(pi\/chunkSize), Math.ceil(DATA.projects.length\/chunkSize));\r\n  }\r\n\r\n  \/\/ STEP: installation exit\r\n  steps.push({section:'Exit', fname:'setup_complete.exe', title:'Installation Complete', render:function(c){\r\n    c.innerHTML =\r\n      '<h2 class=\"wt\">Uninstall Wizard \u2014 not required.<\/h2><p class=\"wsub\">Setup has finished installing 27+ years of production experience.<\/p>'+\r\n      '<p class=\"body-p\">Every module has been verified: career history (19 positions, 1998\u2013present), a project archive of 23 shipped systems, a 63-line skills matrix, education, and courses attended. Nothing has been shortened.<\/p>'+\r\n      '<p class=\"body-p\">One step remains before Setup can exit \u2014 sending Deon a message.<\/p>'+\r\n      '<div class=\"info-panel\" style=\"margin-top:10px;\">'+\r\n      row('components installed', '9 profile modules \u00b7 19 career entries \u00b7 23 projects')+\r\n      row('integrity check', 'Passed \u2014 sourced entirely from CV v1.53')+\r\n      row('status', 'Ready to deploy')+\r\n      '<\/div>';\r\n  }});\r\n\r\n  \/\/ STEP: contact (final)\r\n  steps.push({section:'Contact', fname:'get_in_touch.sh', title:'Get In Touch', render:function(c){\r\n    c.innerHTML =\r\n      '<h2 class=\"wt\">.\/get_in_touch.sh<\/h2><p class=\"wsub\">Let&#39;s talk about your next contract<\/p>'+\r\n      '<div class=\"info-panel\">'+\r\n      row('email', '<a href=\"mailto:d_claassens@hotmail.com\">d_claassens@hotmail.com<\/a>')+\r\n      row('cell', '<a href=\"tel:+27612090321\">061 209 0321<\/a>')+\r\n      row('location', 'George, Western Cape, South Africa')+\r\n      row('availability', 'Flexible \u2014 ask about urgent requests')+\r\n      '<\/div>'+\r\n      '<div class=\"chip-row\">'+\r\n      '<a class=\"wbtn primary\" style=\"text-decoration:none;\" href=\"mailto:d_claassens@hotmail.com\">\u2709 Email Deon<\/a>'+\r\n      '<a class=\"wbtn\" style=\"text-decoration:none;\" href=\"tel:+27612090321\">\u260e Call<\/a>'+\r\n      '<\/div>';\r\n  }});\r\n\r\n  \/\/ STEP: finish\r\n  steps.push({section:'Finish', fname:'complete.msg', title:'All Done', render:function(c){\r\n    c.innerHTML =\r\n      '<div class=\"finish-wrap\"><div class=\"finish-check\">\u2713<\/div>'+\r\n      '<h2 class=\"wt\" style=\"text-align:center;\">Setup has finished installing Deon Claassens.<\/h2>'+\r\n      '<p class=\"body-p\" style=\"margin:0 auto;text-align:center;\">Thank you for taking the time to review 27+ years of production code, from Delphi 1 to .NET Core 3.1.<\/p>'+\r\n      '<div class=\"finish-list\"><div>Profile compiled<\/div><div>Cover letter installed<\/div><div>19 career entries verified<\/div>'+\r\n      '<div>23 projects archived<\/div><div>Contact channel open<\/div><\/div>'+\r\n      '<div class=\"chip-row\" style=\"justify-content:center;margin-top:24px;\">'+\r\n      '<button class=\"wbtn primary\" id=\"restartBtn\">\u21ba Run setup again<\/button>'+\r\n      '<a class=\"wbtn\" style=\"text-decoration:none;\" href=\"mailto:d_claassens@hotmail.com\">\u2709 Email Deon<\/a>'+\r\n      '<\/div><\/div>';\r\n    var rb = c.querySelector('#restartBtn');\r\n    if(rb) rb.addEventListener('click', function(){ current=0; renderStep(0); });\r\n  }});\r\n\r\n  function row(k,v){ return '<div class=\"info-row\"><span class=\"k\">'+k+'<\/span><span class=\"v\">'+v+'<\/span><\/div>'; }\r\n\r\n  \/* ---------------- SKILL MODAL ENGINE ---------------- *\/\r\n  var skillModalOverlay = document.getElementById('skillModalOverlay');\r\n  var skillModalTitle = document.getElementById('skillModalTitle');\r\n  var skillModalSub = document.getElementById('skillModalSub');\r\n  var skillModalWindowTitle = document.getElementById('skillModalWindowTitle');\r\n  var skillModalBody = document.getElementById('skillModalBody');\r\n  var skillModalClose = document.getElementById('skillModalClose');\r\n  var lastFocused = null;\r\n\r\n  function openSkillModal(cat, rows){\r\n    lastFocused = document.activeElement;\r\n    skillModalTitle.textContent = \"'\"+cat+\"'\";\r\n    skillModalWindowTitle.textContent = 'skills_matrix.json \u2014 '+catLabel[cat].toLowerCase();\r\n    var avg = rows.reduce(function(a,b){return a+b[3];},0) \/ rows.length;\r\n    var cur = rows.filter(function(r){return r[2]==='Current';}).length;\r\n    skillModalSub.textContent = rows.length+' rows \u00b7 avg level '+avg.toFixed(1)+' \u00b7 '+cur+' still current';\r\n    var sorted = rows.slice().sort(function(a,b){ return b[3]-a[3]; });\r\n    skillModalBody.innerHTML = sorted.map(function(r){\r\n      return '<div class=\"modal-skill-row\"><div class=\"modal-skill-top\"><span class=\"modal-skill-name\">'+esc(r[0])+'<\/span>'+\r\n        '<span class=\"modal-skill-meta\">'+esc(r[1])+' \u00b7 '+esc(r[2])+'<\/span><\/div>'+\r\n        '<div class=\"modal-skill-levelrow\"><div class=\"modal-skill-bar\"><div class=\"modal-skill-fill\" style=\"width:'+(r[3]*20)+'%\"><\/div><\/div>'+\r\n        '<span class=\"modal-skill-level\">Lv '+r[3]+'\/5<\/span><\/div><\/div>';\r\n    }).join('');\r\n    skillModalOverlay.classList.add('open');\r\n    skillModalClose.focus();\r\n  }\r\n  function closeSkillModal(){\r\n    skillModalOverlay.classList.remove('open');\r\n    if(lastFocused) lastFocused.focus();\r\n  }\r\n  skillModalClose.addEventListener('click', closeSkillModal);\r\n  skillModalOverlay.addEventListener('click', function(e){ if(e.target===skillModalOverlay) closeSkillModal(); });\r\n  window.addEventListener('keydown', function(e){ if(e.key==='Escape' && skillModalOverlay.classList.contains('open')) closeSkillModal(); });\r\n\r\n  \/* ---------------- JOB MODAL ENGINE ---------------- *\/\r\n  var jobModalOverlay = document.getElementById('jobModalOverlay');\r\n  var jobModalWindowTitle = document.getElementById('jobModalWindowTitle');\r\n  var jobModalBody = document.getElementById('jobModalBody');\r\n  var jobModalClose = document.getElementById('jobModalClose');\r\n  var jobLastFocused = null;\r\n\r\n  function openJobModal(j, idx){\r\n    jobLastFocused = document.activeElement;\r\n    jobModalWindowTitle.textContent = 'career_'+(idx+1)+'.log \u2014 '+j.company;\r\n    var bodyHtml = (j.body||[]).map(function(p){ return '<p class=\"body-p\">'+esc(p)+'<\/p>'; }).join('');\r\n    var bulletHtml = j.bullets ? '<ul class=\"blist\">'+j.bullets.map(function(b){return '<li>'+esc(b)+'<\/li>';}).join('')+'<\/ul>' : '';\r\n    var reasonHtml = j.reason ? '<div class=\"reason-box\"><b>Reason for leaving \u2014<\/b> '+esc(j.reason)+'<\/div>' : '';\r\n    var contactHtml = (j.contacts && j.contacts.length) ? '<div class=\"contact-list\"><b style=\"color:var(--text-faint)\">Reference(s):<\/b>'+j.contacts.map(function(n){return '<div>\u00b7 '+esc(n)+' <span style=\"color:var(--text-faint)\">(contact details available on request)<\/span><\/div>';}).join('')+'<\/div>' : '';\r\n    jobModalBody.innerHTML =\r\n      '<div class=\"log-head\"><span class=\"log-role\">'+esc(j.role)+'<\/span><\/div>'+\r\n      '<div class=\"log-co\">'+esc(j.company)+'<\/div>'+\r\n      '<div class=\"log-period\">'+esc(j.period)+'<\/div>'+\r\n      bodyHtml + bulletHtml +\r\n      '<div class=\"chip-row\">'+j.tech.map(function(t){return '<span class=\"tag-tech\">'+esc(t)+'<\/span>';}).join('')+'<\/div>'+\r\n      reasonHtml + contactHtml;\r\n    jobModalOverlay.classList.add('open');\r\n    jobModalClose.focus();\r\n  }\r\n  function closeJobModal(){\r\n    jobModalOverlay.classList.remove('open');\r\n    if(jobLastFocused) jobLastFocused.focus();\r\n  }\r\n  jobModalClose.addEventListener('click', closeJobModal);\r\n  jobModalOverlay.addEventListener('click', function(e){ if(e.target===jobModalOverlay) closeJobModal(); });\r\n  window.addEventListener('keydown', function(e){ if(e.key==='Escape' && jobModalOverlay.classList.contains('open')) closeJobModal(); });\r\n\r\n  \/* ---------------- ENGINE ---------------- *\/\r\n  var current = 0;\r\n  var body_el = document.getElementById('winBody');\r\n  var fnameEl = document.getElementById('winFname');\r\n  var stepcountEl = document.getElementById('winStepcount');\r\n  var eyebrowEl = document.getElementById('winEyebrow');\r\n  var progressFill = document.getElementById('winProgressFill');\r\n  var dotsEl = document.getElementById('winDots');\r\n  var btnBack = document.getElementById('btnBack');\r\n  var btnNext = document.getElementById('btnNext');\r\n\r\n  \/\/ section boundaries for dot rail (compressed: 1 dot per section, not per step)\r\n  var sectionOrder = [];\r\n  steps.forEach(function(s){ if(sectionOrder.indexOf(s.section)===-1) sectionOrder.push(s.section); });\r\n\r\n  function renderStep(idx){\r\n    current = Math.max(0, Math.min(steps.length-1, idx));\r\n    renderGen++; \/\/ invalidate any in-flight async loop (typing\/loader) from the previous step\r\n    var s = steps[current];\r\n    body_el.innerHTML=''; body_el.scrollTop=0;\r\n    s.render(body_el, s);\r\n    fnameEl.textContent = s.fname;\r\n    stepcountEl.textContent = 'Step '+(current+1)+' of '+steps.length;\r\n    eyebrowEl.textContent = s.section;\r\n    progressFill.style.width = (((current+1)\/steps.length)*100)+'%';\r\n    dotsEl.innerHTML = sectionOrder.map(function(sec){\r\n      return '<span class=\"'+(sec===s.section?'on':'')+'\" title=\"'+sec+'\"><\/span>';\r\n    }).join('');\r\n    btnBack.disabled = current===0;\r\n    btnNext.disabled = current===steps.length-1;\r\n    btnNext.textContent = current===steps.length-2 ? 'Finish \u2192' : 'Next \u2192';\r\n  }\r\n  btnBack.addEventListener('click', function(){ renderStep(current-1); });\r\n  btnNext.addEventListener('click', function(){ renderStep(current+1); });\r\n  window.addEventListener('keydown', function(e){\r\n    if(!document.getElementById('wizard').classList.contains('show')) return;\r\n    if(skillModalOverlay.classList.contains('open') || jobModalOverlay.classList.contains('open')) return;\r\n    if(e.key==='ArrowRight') renderStep(current+1);\r\n    if(e.key==='ArrowLeft') renderStep(current-1);\r\n  });\r\n\r\n})();\r\n<\/script>\r\n<\/body>\r\n<\/html>\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>Deon Claassens \u2014 Setup Wizard \/\/ Specialist Developer deon@deonware:~\/setup \u2014 v1.53 .\/run_setup.exe &nbsp;\u2192 DEON_CLAASSENS_SETUP.exe Step 1 of 1 Setup \u2190 Back Next \u2192 skills_matrix.json \u2014 query \u2715 SELECT * WHERE category = career_log.json \u2014 record \u2715<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_canvas","meta":{"footnotes":""},"class_list":["post-14","page","type-page","status-publish","hentry"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.0.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"Deon Claassens \u2014 Setup Wizard \/\/ Specialist Developer deon@deonware:~\/setup \u2014 v1.53 .\/run_setup.exe \u2192 DEON_CLAASSENS_SETUP.exe Step 1 of 1 Setup \u2190 Back Next \u2192 skills_matrix.json \u2014 query \u2715 SELECT * WHERE category = career_log.json \u2014 record \u2715\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<link rel=\"canonical\" href=\"https:\/\/deonware.com\/cv\/\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.0.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"en_US\" \/>\n\t\t<meta property=\"og:site_name\" content=\"DeonWare -\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"Deon Claassens CV - DeonWare\" \/>\n\t\t<meta property=\"og:description\" content=\"Deon Claassens \u2014 Setup Wizard \/\/ Specialist Developer deon@deonware:~\/setup \u2014 v1.53 .\/run_setup.exe \u2192 DEON_CLAASSENS_SETUP.exe Step 1 of 1 Setup \u2190 Back Next \u2192 skills_matrix.json \u2014 query \u2715 SELECT * WHERE category = career_log.json \u2014 record \u2715\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/deonware.com\/cv\/\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2026-08-13T09:30:52+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2026-08-13T09:51:49+00:00\" \/>\n\t\t<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n\t\t<meta name=\"twitter:title\" content=\"Deon Claassens CV - DeonWare\" \/>\n\t\t<meta name=\"twitter:description\" content=\"Deon Claassens \u2014 Setup Wizard \/\/ Specialist Developer deon@deonware:~\/setup \u2014 v1.53 .\/run_setup.exe \u2192 DEON_CLAASSENS_SETUP.exe Step 1 of 1 Setup \u2190 Back Next \u2192 skills_matrix.json \u2014 query \u2715 SELECT * WHERE category = career_log.json \u2014 record \u2715\" \/>\n\t\t<script type=\"application\/ld+json\" class=\"aioseo-schema\">\n\t\t\t{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/deonware.com\\\/cv\\\/#breadcrumblist\",\"itemListElement\":[{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/deonware.com\\\/cv#listItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/deonware.com\\\/cv\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/deonware.com\\\/cv\\\/#listItem\",\"name\":\"Deon Claassens CV\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/deonware.com\\\/cv\\\/#listItem\",\"position\":2,\"name\":\"Deon Claassens CV\",\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/deonware.com\\\/cv#listItem\",\"name\":\"Home\"}}]},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/deonware.com\\\/cv\\\/#organization\",\"name\":\"DeonWare\",\"url\":\"https:\\\/\\\/deonware.com\\\/cv\\\/\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/deonware.com\\\/cv\\\/#webpage\",\"url\":\"https:\\\/\\\/deonware.com\\\/cv\\\/\",\"name\":\"Deon Claassens CV - DeonWare\",\"description\":\"Deon Claassens \\u2014 Setup Wizard \\\/\\\/ Specialist Developer deon@deonware:~\\\/setup \\u2014 v1.53 .\\\/run_setup.exe \\u2192 DEON_CLAASSENS_SETUP.exe Step 1 of 1 Setup \\u2190 Back Next \\u2192 skills_matrix.json \\u2014 query \\u2715 SELECT * WHERE category = career_log.json \\u2014 record \\u2715\",\"inLanguage\":\"en-US\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/deonware.com\\\/cv\\\/#website\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/deonware.com\\\/cv\\\/#breadcrumblist\"},\"datePublished\":\"2026-08-13T09:30:52+00:00\",\"dateModified\":\"2026-08-13T09:51:49+00:00\"},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/deonware.com\\\/cv\\\/#website\",\"url\":\"https:\\\/\\\/deonware.com\\\/cv\\\/\",\"name\":\"DeonWare\",\"inLanguage\":\"en-US\",\"publisher\":{\"@id\":\"https:\\\/\\\/deonware.com\\\/cv\\\/#organization\"}}]}\n\t\t<\/script>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"Deon Claassens CV - DeonWare","description":"Deon Claassens \u2014 Setup Wizard \/\/ Specialist Developer deon@deonware:~\/setup \u2014 v1.53 .\/run_setup.exe \u2192 DEON_CLAASSENS_SETUP.exe Step 1 of 1 Setup \u2190 Back Next \u2192 skills_matrix.json \u2014 query \u2715 SELECT * WHERE category = career_log.json \u2014 record \u2715","canonical_url":"https:\/\/deonware.com\/cv\/","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"BreadcrumbList","@id":"https:\/\/deonware.com\/cv\/#breadcrumblist","itemListElement":[{"@type":"ListItem","@id":"https:\/\/deonware.com\/cv#listItem","position":1,"name":"Home","item":"https:\/\/deonware.com\/cv","nextItem":{"@type":"ListItem","@id":"https:\/\/deonware.com\/cv\/#listItem","name":"Deon Claassens CV"}},{"@type":"ListItem","@id":"https:\/\/deonware.com\/cv\/#listItem","position":2,"name":"Deon Claassens CV","previousItem":{"@type":"ListItem","@id":"https:\/\/deonware.com\/cv#listItem","name":"Home"}}]},{"@type":"Organization","@id":"https:\/\/deonware.com\/cv\/#organization","name":"DeonWare","url":"https:\/\/deonware.com\/cv\/"},{"@type":"WebPage","@id":"https:\/\/deonware.com\/cv\/#webpage","url":"https:\/\/deonware.com\/cv\/","name":"Deon Claassens CV - DeonWare","description":"Deon Claassens \u2014 Setup Wizard \/\/ Specialist Developer deon@deonware:~\/setup \u2014 v1.53 .\/run_setup.exe \u2192 DEON_CLAASSENS_SETUP.exe Step 1 of 1 Setup \u2190 Back Next \u2192 skills_matrix.json \u2014 query \u2715 SELECT * WHERE category = career_log.json \u2014 record \u2715","inLanguage":"en-US","isPartOf":{"@id":"https:\/\/deonware.com\/cv\/#website"},"breadcrumb":{"@id":"https:\/\/deonware.com\/cv\/#breadcrumblist"},"datePublished":"2026-08-13T09:30:52+00:00","dateModified":"2026-08-13T09:51:49+00:00"},{"@type":"WebSite","@id":"https:\/\/deonware.com\/cv\/#website","url":"https:\/\/deonware.com\/cv\/","name":"DeonWare","inLanguage":"en-US","publisher":{"@id":"https:\/\/deonware.com\/cv\/#organization"}}]},"og:locale":"en_US","og:site_name":"DeonWare -","og:type":"article","og:title":"Deon Claassens CV - DeonWare","og:description":"Deon Claassens \u2014 Setup Wizard \/\/ Specialist Developer deon@deonware:~\/setup \u2014 v1.53 .\/run_setup.exe \u2192 DEON_CLAASSENS_SETUP.exe Step 1 of 1 Setup \u2190 Back Next \u2192 skills_matrix.json \u2014 query \u2715 SELECT * WHERE category = career_log.json \u2014 record \u2715","og:url":"https:\/\/deonware.com\/cv\/","article:published_time":"2026-08-13T09:30:52+00:00","article:modified_time":"2026-08-13T09:51:49+00:00","twitter:card":"summary_large_image","twitter:title":"Deon Claassens CV - DeonWare","twitter:description":"Deon Claassens \u2014 Setup Wizard \/\/ Specialist Developer deon@deonware:~\/setup \u2014 v1.53 .\/run_setup.exe \u2192 DEON_CLAASSENS_SETUP.exe Step 1 of 1 Setup \u2190 Back Next \u2192 skills_matrix.json \u2014 query \u2715 SELECT * WHERE category = career_log.json \u2014 record \u2715"},"aioseo_meta_data":{"post_id":"14","title":null,"description":null,"keywords":null,"keyphrases":{"focus":{"keyphrase":"","score":0,"analysis":{"keyphraseInTitle":{"score":0,"maxScore":9,"error":1}}},"additional":[]},"focus_keyword":null,"additional_keywords":null,"truseo_locale":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_custom_url":null,"og_image_custom_fields":null,"og_image_url":null,"og_image_width":null,"og_image_height":null,"og_video":"","og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_image_url":null,"twitter_title":null,"twitter_description":null,"schema_type":"default","schema_type_options":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"WebPage","isEnabled":true},"graphs":[]},"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":"-1","robots_max_videopreview":"-1","robots_max_imagepreview":"large","priority":null,"frequency":"default","local_seo":null,"limit_modified_date":false,"ai":{"faqs":[],"keyPoints":[],"schemas":[],"titles":[],"descriptions":[],"socialPosts":{"email":{"subject":"","preview":"","content":""},"linkedin":[],"twitter":[],"facebook":[],"instagram":[]}},"breadcrumb_settings":null,"seo_analyzer_scan_date":null,"created":"2026-08-13 09:30:53","updated":"2026-08-13 10:19:49"},"aioseo_breadcrumb":"<div class=\"aioseo-breadcrumbs\"><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/deonware.com\/cv\" title=\"Home\">Home<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">&raquo;<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\tDeon Claassens CV\n\t\t<\/span><\/div>","aioseo_breadcrumb_json":[{"label":"Home","link":"https:\/\/deonware.com\/cv"},{"label":"Deon Claassens CV","link":"https:\/\/deonware.com\/cv\/"}],"_hostinger_reach_plugin_has_subscription_block":false,"_hostinger_reach_plugin_is_elementor":false,"_links":{"self":[{"href":"https:\/\/deonware.com\/cv\/wp-json\/wp\/v2\/pages\/14","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/deonware.com\/cv\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/deonware.com\/cv\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/deonware.com\/cv\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/deonware.com\/cv\/wp-json\/wp\/v2\/comments?post=14"}],"version-history":[{"count":10,"href":"https:\/\/deonware.com\/cv\/wp-json\/wp\/v2\/pages\/14\/revisions"}],"predecessor-version":[{"id":28,"href":"https:\/\/deonware.com\/cv\/wp-json\/wp\/v2\/pages\/14\/revisions\/28"}],"wp:attachment":[{"href":"https:\/\/deonware.com\/cv\/wp-json\/wp\/v2\/media?parent=14"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}