<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1.0,viewport-fit=cover"/>
<meta name="theme-color" content="#0a1628"/>
<meta name="apple-mobile-web-app-capable" content="yes"/>
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"/>
<meta name="apple-mobile-web-app-title" content="RE Mastery Hub"/>
<meta name="application-name" content="RE Mastery Hub"/>
<meta name="mobile-web-app-capable" content="yes"/>

<!-- ══ SEO ══════════════════════════════════════════════════════════════ -->
<title>RE Mastery Hub — Pass Your RE5 & RE1 Exam | South Africa</title>
<meta name="description" content="South Africa's #1 RE5 &amp; RE1 exam prep platform. Practice questions, mock exams, AI coaching, and study guides for financial services professionals."/>
<meta name="keywords" content="RE5 exam, RE1 exam, FSCA exam, regulatory exam South Africa, RE5 practice questions, RE1 practice questions, financial services exam prep, FAIS exam, representative exam, key individual exam, Moonstone RE5, INSETA RE exam"/>
<meta name="author" content="The Trending Bee Creatives"/>
<meta name="robots" content="index, follow"/>
<meta name="google-site-verification" content="6CtjVbD5Eiu9pb-yAv9WoXrvJ7KrQqalhpq5TbzZ_cA"/>
<link rel="canonical" href="https://remasteryhub.online/"/>

<!-- ══ Open Graph (WhatsApp, Facebook previews) ═════════════════════════ -->
<meta property="og:type" content="website"/>
<meta property="og:url" content="https://remasteryhub.online/"/>
<meta property="og:title" content="RE Mastery Hub — Pass Your RE5 & RE1 Exam"/>
<meta property="og:description" content="South Africa's dedicated RE5 and RE1 exam prep platform. 155+ practice questions, mock exams, personalised coaching. Free to start."/>
<meta property="og:image" content="https://remasteryhub.online/icons/icon-512.png"/>
<meta property="og:site_name" content="RE Mastery Hub"/>
<meta property="og:locale" content="en_ZA"/>

<!-- ══ Twitter / X card ════════════════════════════════════════════════ -->
<meta name="twitter:card" content="summary_large_image"/>
<meta name="twitter:title" content="RE Mastery Hub — Pass Your RE5 & RE1 Exam"/>
<meta name="twitter:description" content="South Africa's dedicated RE5 and RE1 exam prep platform. 155+ practice questions, mock exams, personalised coaching. Free to start."/>
<meta name="twitter:image" content="https://remasteryhub.online/icons/icon-512.png"/>

<!-- ══ Structured data (Google rich results) ════════════════════════════ -->
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "WebApplication",
  "name": "RE Mastery Hub",
  "url": "https://remasteryhub.online/",
  "description": "South Africa's dedicated RE5 and RE1 regulatory exam preparation platform for financial services professionals.",
  "applicationCategory": "EducationApplication",
  "operatingSystem": "Any",
  "offers": [
    {
      "@type": "Offer",
      "name": "Free Plan",
      "price": "0",
      "priceCurrency": "ZAR"
    },
    {
      "@type": "Offer",
      "name": "RE5 Pro",
      "price": "349",
      "priceCurrency": "ZAR"
    },
    {
      "@type": "Offer",
      "name": "RE1 Pro",
      "price": "349",
      "priceCurrency": "ZAR"
    },
    {
      "@type": "Offer",
      "name": "Combo Pro",
      "price": "499",
      "priceCurrency": "ZAR"
    }
  ],
  "publisher": {
    "@type": "Organization",
    "name": "The Trending Bee Creatives",
    "email": "thetrendingbeeinfo@gmail.com"
  },
  "inLanguage": "en-ZA",
  "audience": {
    "@type": "Audience",
    "audienceType": "South African financial services professionals preparing for FSCA RE5 and RE1 regulatory examinations"
  }
}
</script>

<!-- PWA Manifest -->
<link rel="manifest" href="/manifest.json"/>
<!-- Apple Touch Icon -->
<link rel="apple-touch-icon" href="/icons/icon-192.png"/>

<!-- Supabase JS SDK — defer ensures it loads before app code runs.
     Phase 2: set SUPABASE_URL + SUPABASE_ANON_KEY in App.Supabase config above.
     Until those values are set, all Supabase calls are no-ops. -->
<script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2/dist/umd/supabase.min.js" defer></script>

<!-- Fonts: preconnect for speed, non-blocking load (eliminates render-blocking) -->
<link rel="preconnect" href="https://fonts.googleapis.com" crossorigin/>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin/>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&family=DM+Serif+Display&display=swap" media="print" onload="this.media='all'"/>
<noscript><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&family=DM+Serif+Display&display=swap"/></noscript>

<!-- ═══════════════════════════════════════════
     STYLES — unchanged premium identity
════════════════════════════════════════════ -->
<style>
:root{
  --navy:#0a1628;--navy2:#0f1f3d;--navy3:#162850;--navy4:#1e3a6e;
  --gold:#c8a84b;--gold2:#e2c97e;--gold3:#f5edd8;
  --green:#059669;--green2:#d1fae5;--green3:#ecfdf5;
  --red:#dc2626;--red2:#fee2e2;--red3:#fef2f2;
  --amber:#d97706;--amber2:#fef3c7;--amber3:#fffbeb;
  --blue:#2563eb;--blue2:#dbeafe;
  --purple:#7c3aed;--purple2:#ede9fe;
  --text:#0f172a;--text2:#475569;--text3:#94a3b8;
  --border:#e2e8f0;--border2:#f1f5f9;--border3:#f8fafc;
  --card:#fff;--bg:#f8fafc;
  --r:16px;--r-sm:10px;
  --sh:0 1px 3px rgba(0,0,0,.06),0 4px 16px rgba(0,0,0,.08);
  --sh-md:0 4px 12px rgba(0,0,0,.1),0 8px 32px rgba(0,0,0,.1);
  --sh-gold:0 4px 20px rgba(200,168,75,.35);
  --t:.2s cubic-bezier(.4,0,.2,1);
}
*{box-sizing:border-box;margin:0;padding:0;-webkit-tap-highlight-color:transparent}
html{scroll-behavior:smooth}
body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--text);min-height:100vh;overflow-x:hidden;-webkit-font-smoothing:antialiased}
::-webkit-scrollbar{width:4px}::-webkit-scrollbar-thumb{background:var(--border);border-radius:2px}
.hidden{display:none!important}
.flex{display:flex}.fac{align-items:center;display:flex}.fb{display:flex;align-items:center;justify-content:space-between}.fc{display:flex;align-items:center;justify-content:center}.fw{flex-wrap:wrap}
.gap6{gap:6px}.gap8{gap:8px}.gap10{gap:10px}.gap12{gap:12px}.gap16{gap:16px}
.g2{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.g3{display:grid;grid-template-columns:repeat(3,1fr);gap:10px}
.g4{display:grid;grid-template-columns:repeat(4,1fr);gap:8px}
@media(max-width:480px){.g2{grid-template-columns:1fr}.g3{grid-template-columns:1fr 1fr}.g4{grid-template-columns:1fr 1fr}}
.mt4{margin-top:4px}.mt8{margin-top:8px}.mt12{margin-top:12px}.mt16{margin-top:16px}.mt20{margin-top:20px}.mt24{margin-top:24px}
.mb4{margin-bottom:4px}.mb8{margin-bottom:8px}.mb12{margin-bottom:12px}.mb16{margin-bottom:16px}.mb20{margin-bottom:20px}.mb24{margin-bottom:24px}
.w100{width:100%}
.tc{text-align:center}.tr{text-align:right}
.f-xs{font-size:.71rem}.f-sm{font-size:.82rem}.f-base{font-size:.92rem}.f-lg{font-size:1.1rem}.f-xl{font-size:1.3rem}.f-2xl{font-size:1.6rem}
.fw6{font-weight:600}.fw7{font-weight:700}.fw8{font-weight:800}.fw9{font-weight:900}
.c-gold{color:var(--gold)}.c-muted{color:var(--text2)}.c-xs{color:var(--text3)}.c-green{color:var(--green)}.c-red{color:var(--red)}.c-white{color:#fff}.c-amber{color:var(--amber)}.c-navy{color:var(--navy)}
.lh-15{line-height:1.5}.lh-16{line-height:1.6}.lh-18{line-height:1.8}
.serif{font-family:'DM Serif Display',serif}
.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
#appShell{display:flex;flex-direction:column;min-height:100vh}
.topbar{background:var(--navy);color:#fff;height:56px;display:flex;align-items:center;justify-content:space-between;padding:0 16px;position:sticky;top:0;z-index:100;box-shadow:0 2px 20px rgba(0,0,0,.3)}
.topbar-logo{font-weight:900;font-size:1rem;letter-spacing:.3px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:calc(100% - 80px)}
.topbar-logo em{color:var(--gold);font-style:normal}
.topbar-right{display:flex;align-items:center;gap:8px}
.icon-btn{background:rgba(255,255,255,.08);border:none;color:#fff;width:34px;height:34px;border-radius:8px;display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:.95rem;transition:var(--t)}
.icon-btn:hover{background:rgba(255,255,255,.16)}
.bottomnav{background:#fff;border-top:1px solid var(--border);display:flex;position:fixed;bottom:0;left:0;right:0;z-index:90;height:58px;padding-bottom:env(safe-area-inset-bottom,0px)}
.bnav-item{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;cursor:pointer;border:none;background:none;padding:6px 2px;transition:var(--t)}
.bnav-icon{font-size:1.1rem;transition:var(--t)}
.bnav-label{font-size:.58rem;font-weight:600;color:var(--text3);letter-spacing:.3px;text-transform:uppercase;transition:var(--t)}
.bnav-item.active .bnav-label{color:var(--navy)}
.bnav-item.active .bnav-icon{transform:translateY(-2px)}
.main{padding:14px 14px 76px;max-width:680px;margin:0 auto;width:100%;contain:layout}
.card{background:var(--card);border-radius:var(--r);box-shadow:var(--sh);border:1px solid var(--border);transition:var(--t)}
.card-sm{padding:14px}.card-p{padding:18px}
.card-hover:active{transform:translateY(-1px);box-shadow:var(--sh-md)}
.card-navy{background:linear-gradient(135deg,var(--navy),var(--navy3));border:none}
.card-danger{background:var(--red3);border:1.5px solid #fca5a5}
.card-amber{background:var(--amber3);border:1.5px solid #fcd34d}
.card-success{background:var(--green3);border:1.5px solid #6ee7b7}
.btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;border:none;border-radius:var(--r-sm);font-family:'Inter',sans-serif;font-weight:700;cursor:pointer;transition:var(--t);white-space:nowrap;-webkit-user-select:none;user-select:none}
.btn:active{transform:scale(.96)}
.btn-xs{padding:5px 11px;font-size:.72rem}.btn-sm{padding:8px 16px;font-size:.8rem}
.btn-md{padding:11px 22px;font-size:.88rem}.btn-lg{padding:14px 28px;font-size:.95rem}
.btn-full{width:100%}
.btn-navy{background:var(--navy);color:#fff}.btn-navy:hover{background:var(--navy3)}
.btn-gold{background:linear-gradient(135deg,var(--gold),var(--gold2));color:var(--navy)}.btn-gold:hover{filter:brightness(1.06)}
.btn-outline{background:transparent;border:2px solid var(--border);color:var(--text2)}.btn-outline:hover{border-color:var(--navy);color:var(--navy)}
.btn-outline-gold{background:transparent;border:2px solid var(--gold);color:var(--gold)}.btn-outline-gold:hover{background:var(--gold);color:var(--navy)}
.btn-ghost{background:transparent;color:var(--text2)}.btn-ghost:hover{color:var(--text)}
.btn-red{background:var(--red);color:#fff}
.btn-green{background:var(--green);color:#fff}
.badge{display:inline-flex;align-items:center;gap:3px;padding:3px 8px;border-radius:20px;font-size:.66rem;font-weight:700;letter-spacing:.4px;text-transform:uppercase}
.badge-gold{background:var(--gold3);color:#6b4c0a}.badge-navy{background:rgba(10,22,40,.08);color:var(--navy)}
.badge-green{background:var(--green2);color:#065f46}.badge-red{background:var(--red2);color:#991b1b}
.badge-amber{background:var(--amber2);color:#78350f}.badge-blue{background:var(--blue2);color:#1e40af}
.badge-pro{background:linear-gradient(90deg,var(--gold),var(--gold2));color:var(--navy)}
.badge-level{font-size:.6rem;padding:2px 7px;border-radius:4px;font-weight:800;letter-spacing:.5px;text-transform:uppercase}
.level-k{background:#e0f2fe;color:#0369a1}.level-c{background:#f0fdf4;color:#166534}
.level-ap{background:#fef3c7;color:#92400e}.level-an{background:#fdf4ff;color:#7e22ce}
.pbar{background:var(--border2);border-radius:20px;overflow:hidden}
.pbar-h6{height:6px}.pbar-h8{height:8px}.pbar-h14{height:14px}
.pbar-fill{height:100%;border-radius:20px;transition:width .6s ease}
.pf-navy{background:linear-gradient(90deg,var(--navy),var(--navy4))}.pf-gold{background:linear-gradient(90deg,var(--gold),var(--gold2))}
.pf-green{background:linear-gradient(90deg,var(--green),#34d399)}.pf-red{background:linear-gradient(90deg,var(--red),#f87171)}
.pf-amber{background:linear-gradient(90deg,var(--amber),#fbbf24)}
.qopt{display:flex;align-items:flex-start;gap:12px;padding:13px 14px;border:2px solid var(--border);border-radius:var(--r-sm);cursor:pointer;transition:border-color .15s,background .15s;background:#fff;margin-bottom:9px}
.qopt:not(.disabled):active{border-color:var(--navy)}.qopt.selected{border-color:var(--navy);background:rgba(10,22,40,.04)}
.qopt.correct{border-color:var(--green);background:var(--green3)}.qopt.wrong{border-color:var(--red);background:var(--red3)}
.qopt.reveal{border-color:var(--green);background:rgba(5,150,105,.05)}.qopt.disabled{cursor:default;pointer-events:none}
.okey{width:26px;height:26px;min-width:26px;border-radius:50%;border:2px solid var(--border);display:flex;align-items:center;justify-content:center;font-size:.73rem;font-weight:700;color:var(--text3);transition:background .15s,border-color .15s}
.qopt.selected .okey{border-color:var(--navy);background:var(--navy);color:#fff}
.qopt.correct .okey{border-color:var(--green);background:var(--green);color:#fff}
.qopt.wrong .okey{border-color:var(--red);background:var(--red);color:#fff}
.qopt.reveal .okey{border-color:var(--green);background:var(--green);color:#fff}
.sim-dots-wrap{display:flex;gap:3px;flex-wrap:wrap;margin-bottom:12px;max-height:72px;overflow-y:auto}
.sdot{width:24px;height:24px;border-radius:4px;border:1.5px solid var(--border);display:flex;align-items:center;justify-content:center;font-size:.58rem;font-weight:700;cursor:pointer;transition:var(--t);flex-shrink:0;color:var(--text3)}
.sdot.ans{background:var(--navy);border-color:var(--navy);color:#fff}.sdot.cur{background:var(--gold);border-color:var(--gold);color:var(--navy)}
.modal-bg{position:fixed;inset:0;background:rgba(0,0,0,.55);z-index:200;display:flex;align-items:flex-end;justify-content:center;opacity:0;pointer-events:none;transition:opacity .2s}
.modal-bg.open{opacity:1;pointer-events:all}
.modal-sheet{background:#fff;border-radius:24px 24px 0 0;padding:20px 20px 32px;max-width:600px;width:100%;max-height:92vh;overflow-y:auto;transform:translateY(100%);transition:transform .35s cubic-bezier(.4,0,.2,1)}
.modal-bg.open .modal-sheet{transform:translateY(0)}
.modal-handle{width:36px;height:4px;background:var(--border);border-radius:2px;margin:0 auto 18px}
#onboarding{position:fixed;inset:0;z-index:500;overflow-y:auto}
.ob-bg{background:var(--navy);min-height:100%;display:flex;flex-direction:column;align-items:center;padding:24px 20px 40px}
.ob-card{max-width:460px;width:100%;animation:fadeUp .4s ease}
@keyframes fadeUp{from{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}
.ob-opt{background:rgba(255,255,255,.07);border:2px solid rgba(255,255,255,.12);border-radius:14px;padding:14px 16px;cursor:pointer;transition:var(--t);color:#fff;display:flex;align-items:center;gap:14px;margin-bottom:10px}
.ob-opt.sel{background:rgba(200,168,75,.15);border-color:var(--gold)}
.ob-chk{width:22px;height:22px;min-width:22px;border-radius:50%;border:2px solid rgba(255,255,255,.25);display:flex;align-items:center;justify-content:center;font-size:.7rem;transition:var(--t)}
.ob-opt.sel .ob-chk{background:var(--gold);border-color:var(--gold);color:var(--navy)}
.ob-dots{display:flex;gap:6px;justify-content:center;margin-bottom:24px}
.ob-dot{width:8px;height:8px;border-radius:50%;background:rgba(255,255,255,.18);transition:var(--t)}
.ob-dot.active{background:var(--gold);width:22px;border-radius:4px}
.cd-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:8px}
.cd-box{background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.1);border-radius:10px;padding:10px 6px;text-align:center}
.cd-val{font-size:1.55rem;font-weight:900;color:var(--gold);line-height:1;font-variant-numeric:tabular-nums}
.cd-lbl{font-size:.58rem;color:rgba(255,255,255,.45);text-transform:uppercase;letter-spacing:.8px;margin-top:2px}
.risk-needle{position:absolute;top:-4px;width:4px;height:20px;background:var(--navy);border-radius:2px;transform:translateX(-50%);transition:left .8s cubic-bezier(.4,0,.2,1);box-shadow:0 2px 6px rgba(0,0,0,.3)}
.lib-item{border:1px solid var(--border);border-radius:var(--r-sm);overflow:hidden;margin-bottom:8px;background:#fff}
.lib-hdr{padding:13px 14px;cursor:pointer;display:flex;justify-content:space-between;align-items:center;transition:background .15s}
.lib-hdr:active{background:var(--border3)}
.lib-body{max-height:0;overflow:hidden;transition:max-height .4s ease}
.lib-body.open{max-height:3000px;padding:0 14px 14px}
.lib-key{display:inline-block;background:var(--navy);color:var(--gold);border-radius:4px;padding:2px 7px;font-size:.68rem;font-weight:700;margin:2px 3px 2px 0}
.bp-body{max-height:0;overflow:hidden;transition:max-height .4s ease}
.bp-body.open{max-height:4000px}
.video-lesson-block iframe{display:block}
.video-lesson-block{transition:box-shadow .2s}
.video-lesson-block:hover{box-shadow:0 4px 24px rgba(200,168,75,.15)}

/* ── RE Guardian™ styles ───────────────────────────── */
.guardian-wrap{display:flex;flex-direction:column;height:calc(100vh - 160px);min-height:400px}
.guardian-chat{flex:1;overflow-y:auto;padding:12px 4px;display:flex;flex-direction:column;gap:10px}
.guardian-bubble{max-width:88%;padding:11px 14px;border-radius:16px;font-size:.92rem;line-height:1.6;word-break:break-word}
.guardian-ai{align-self:flex-start;background:var(--navy2);border:1px solid rgba(200,168,75,.2);border-bottom-left-radius:4px;color:#e8edf5}
.guardian-ai p,.guardian-ai strong,.guardian-ai em,.guardian-ai code,.guardian-ai div{color:inherit}
.guardian-user{align-self:flex-end;background:linear-gradient(135deg,var(--gold),#b8943a);color:#0d1b2a;font-weight:600;border-bottom-right-radius:4px}
.guardian-typing{display:flex;gap:5px;align-items:center;padding:14px 16px}
.guardian-typing span{width:7px;height:7px;border-radius:50%;background:var(--gold);animation:guardianPulse 1.2s infinite}
.guardian-typing span:nth-child(2){animation-delay:.2s}
.guardian-typing span:nth-child(3){animation-delay:.4s}
@keyframes guardianPulse{0%,60%,100%{opacity:.25;transform:scale(.8)}30%{opacity:1;transform:scale(1.1)}}
.guardian-cursor{display:inline-block;animation:blink .7s step-end infinite;color:var(--gold);font-weight:700}
@keyframes blink{0%,100%{opacity:1}50%{opacity:0}}
.guardian-input-row{display:flex;gap:8px;padding-top:10px;border-top:1px solid var(--border)}
.guardian-input{flex:1;background:var(--navy2);border:1.5px solid var(--border);border-radius:22px;padding:10px 16px;color:#fff;font-size:.9rem;outline:none;resize:none;min-height:42px;max-height:120px;line-height:1.4;font-family:inherit}
.guardian-input::placeholder{color:rgba(255,255,255,.45)}
.guardian-input:focus{border-color:var(--gold)}
.guardian-send{width:42px;height:42px;border-radius:50%;background:var(--gold);border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;flex-shrink:0;transition:opacity .2s}
.guardian-send:disabled{opacity:.4;cursor:not-allowed}
.guardian-mode-card{border:1.5px solid var(--border);border-radius:12px;padding:14px;cursor:pointer;transition:all .2s;background:var(--navy2);text-align:left}
.guardian-mode-card:hover,.guardian-mode-card.active{border-color:var(--gold);background:rgba(200,168,75,.08)}
.guardian-mode-card.pro-only{position:relative;overflow:hidden}
.guardian-chips{display:flex;flex-wrap:wrap;gap:6px;padding:8px 0 4px}
.guardian-chip{background:var(--navy2);border:1px solid var(--border);border-radius:16px;padding:5px 12px;font-size:.78rem;color:var(--text2);cursor:pointer;transition:all .15s}
.guardian-chip:hover{border-color:var(--gold);color:var(--gold)}
@keyframes slideUp{from{opacity:0;transform:translateY(16px)}to{opacity:1;transform:translateY(0)}}
@keyframes ticker{0%{transform:translateX(0)}100%{transform:translateX(-50%)}}
.anim-in{animation:slideUp .3s ease}
.pro-pill{display:inline-flex;align-items:center;gap:4px;background:linear-gradient(90deg,var(--gold),var(--gold2));color:var(--navy);padding:3px 9px;border-radius:20px;font-size:.66rem;font-weight:800;letter-spacing:.4px}
.blur-lock{position:relative;overflow:hidden;min-height:120px}
.blur-lock::after{content:'';position:absolute;inset:0;background:linear-gradient(to bottom,transparent 0%,rgba(248,250,252,.97) 45%,var(--bg) 100%);z-index:2;pointer-events:none}
.blur-lock-cta{position:absolute;bottom:14px;left:0;right:0;z-index:3;text-align:center;padding:0 16px}
.blur-content{filter:blur(5px);user-select:none;pointer-events:none}
.sp-ticker{background:var(--navy);color:rgba(255,255,255,.7);font-size:.75rem;padding:6px 0;overflow:hidden;white-space:nowrap}
.sp-ticker-inner{display:inline-block;animation:ticker 32s linear infinite}
.stat-card{background:#fff;border-radius:var(--r);border:1px solid var(--border);padding:14px;box-shadow:var(--sh)}
.stat-val{font-size:1.6rem;font-weight:900;line-height:1}
.stat-lbl{font-size:.72rem;color:var(--text2);margin-top:3px;font-weight:500}
.inp{width:100%;padding:12px 14px;border:2px solid var(--border);border-radius:var(--r-sm);font-family:'Inter',sans-serif;font-size:.9rem;color:var(--text);background:#fff;transition:border-color .15s;-webkit-appearance:none}
.inp:focus{outline:none;border-color:var(--navy)}
.inp-label{font-size:.78rem;font-weight:600;color:var(--text2);margin-bottom:5px;display:block}
.toast{position:fixed;top:66px;left:50%;transform:translateX(-50%);width:calc(100% - 32px);max-width:360px;background:var(--navy);color:#fff;padding:10px 16px;border-radius:10px;font-size:.82rem;font-weight:500;z-index:9999;box-shadow:var(--sh-md);animation:slideUp .25s ease;border-left:3px solid var(--gold);text-align:center}
.stitle{font-size:.67rem;font-weight:800;color:var(--text3);text-transform:uppercase;letter-spacing:1px;margin-bottom:10px}
.divider{border:none;border-top:1px solid var(--border);margin:14px 0}
.chip{display:inline-block;background:var(--border2);color:var(--text2);border-radius:20px;padding:3px 10px;font-size:.72rem;font-weight:600}
.ref-box{background:linear-gradient(135deg,var(--purple2),#ddd6fe);border:1.5px solid #c4b5fd;border-radius:var(--r);padding:16px}
.coach-box{border-radius:var(--r-sm);padding:14px 16px;margin-top:12px}
.coach-pass{background:#ecfdf5;border:2px solid #6ee7b7}
.coach-warn{background:#fffbeb;border:2px solid #fcd34d}
.coach-fail{background:#fef2f2;border:2px solid #fca5a5}
.review-q-item{padding:10px 12px;border-radius:8px;margin-bottom:6px;cursor:pointer}
.review-q-item.unanswered{background:var(--amber3);border:1px solid #fcd34d}
.review-q-item.answered{background:var(--border2);border:1px solid var(--border)}
.star-btn{background:none;border:none;cursor:pointer;font-size:1.8rem;padding:2px;color:var(--border);transition:color .15s}
.star-btn.on{color:var(--gold)}
/* Loading screen */
#loadingScreen{position:fixed;inset:0;background:var(--navy);z-index:600;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px}
.load-spinner{width:40px;height:40px;border:3px solid rgba(200,168,75,.3);border-top-color:var(--gold);border-radius:50%;animation:spin .8s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
/* Error boundary fallback */
#errorBoundary{position:fixed;inset:0;background:var(--navy);z-index:700;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:24px;text-align:center;color:#fff;display:none}
/* ── Coach Bubble ──────────────────────────────────────────── */
.coach-bubble {
  margin-bottom: 12px;
  padding: 14px 16px;
  border-radius: 14px;
  background: linear-gradient(135deg, #0f1f3d 0%, #1a2f56 100%);
  border: 1.5px solid rgba(200,168,75,.35);
  position: relative;
  overflow: hidden;
}
.coach-bubble::before {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 2px;
  background: linear-gradient(90deg, var(--gold), transparent);
}
.coach-header {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}
.coach-avatar {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: rgba(200,168,75,.15);
  border: 1px solid rgba(200,168,75,.3);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: .9rem;
  flex-shrink: 0;
}
.coach-label {
  font-size: .6rem;
  font-weight: 700;
  letter-spacing: .8px;
  text-transform: uppercase;
  color: var(--gold);
  opacity: .8;
}
.coach-msg {
  font-size: .82rem;
  line-height: 1.55;
  color: rgba(255,255,255,.75);
  font-style: italic;
}

/* ── Desktop Polish ────────────────────────────────────────── */
@media (min-width: 640px) {
  html, body {
    background: #06101f;
  }
  #appShell {
    max-width: 480px;
    margin: 0 auto;
    min-height: 100dvh;
    background: var(--bg);
    box-shadow: 0 0 80px rgba(0,0,0,.6), 0 0 0 1px rgba(255,255,255,.04);
    position: relative;
  }
  #appShell::before {
    content: '';
    position: fixed;
    inset: 0;
    background:
      radial-gradient(ellipse 60% 40% at 20% 10%, rgba(200,168,75,.04) 0%, transparent 60%),
      radial-gradient(ellipse 50% 30% at 80% 90%, rgba(15,31,61,.8) 0%, transparent 60%);
    pointer-events: none;
    z-index: -1;
  }
}
@media (min-width: 900px) {
  #appShell {
    max-width: 520px;
  }
}

/* ── RE Fact Disk™ ─────────────────────────────────────────────────── */
.rfd-brand{text-align:center;margin-bottom:2px}
.rfd-name{font-size:1.15rem;font-weight:900;letter-spacing:.4px;color:var(--text);line-height:1}
.rfd-name .re{color:var(--gold)}
.rfd-name sup{font-size:.48rem;font-weight:700;position:relative;top:-.8em}
.rfd-rule{width:44px;height:2px;background:linear-gradient(90deg,transparent,var(--gold),transparent);margin:7px auto 6px}
.rfd-tag{font-size:.6rem;letter-spacing:2.2px;text-transform:uppercase;color:var(--text3);font-weight:700}
.rfd-sub{text-align:center;font-size:.7rem;color:var(--text3);margin:12px 0 14px}
.rfd-scene{width:100%;max-width:290px;aspect-ratio:1;margin:0 auto;position:relative;
  perspective:760px;perspective-origin:50% 42%}
.rfd-card{position:relative;width:100%;height:100%;transform-style:preserve-3d;
  transition:transform .05s linear;cursor:grab;touch-action:pan-y;will-change:transform}
.rfd-card.anim{transition:transform .72s cubic-bezier(.2,.72,.28,1)}
.rfd-face{position:absolute;inset:0;border-radius:50%;display:flex;flex-direction:column;
  align-items:center;justify-content:center;padding:19% 16%;text-align:center;
  backface-visibility:hidden;-webkit-backface-visibility:hidden;overflow:hidden}
.rfd-b{transform:rotateY(180deg)}
.rfd-navy{background:radial-gradient(ellipse at 32% 22%,rgba(255,255,255,.16),transparent 46%),
  radial-gradient(circle at 50% 46%,#1b3565 0%,var(--navy2) 46%,var(--navy) 78%);
  border:2px solid var(--gold);color:#fff;
  box-shadow:0 26px 48px -12px rgba(10,22,40,.55),0 6px 14px rgba(10,22,40,.3),
  inset 0 2px 20px rgba(255,255,255,.10),inset 0 -18px 34px rgba(0,0,0,.42)}
.rfd-cream{background:radial-gradient(ellipse at 32% 22%,rgba(255,255,255,.9),transparent 50%),
  radial-gradient(circle at 50% 46%,#fffdf5 0%,var(--gold3) 52%,#e8dbb4 84%);
  border:2px solid var(--gold);color:var(--navy);
  box-shadow:0 26px 48px -12px rgba(10,22,40,.4),inset 0 2px 18px rgba(255,255,255,.9),
  inset 0 -16px 30px rgba(138,109,31,.18)}
.rfd-face::after{content:'';position:absolute;inset:13px;border-radius:50%;
  border:1px solid rgba(200,168,75,.30);pointer-events:none}
.rfd-cream::after{border-color:rgba(138,109,31,.22)}
.rfd-num{font-size:.54rem;letter-spacing:2.4px;font-weight:800;margin-bottom:8px}
.rfd-navy .rfd-num{color:var(--gold)} .rfd-cream .rfd-num{color:#8a6d1f}
.rfd-txt{font-size:.87rem;line-height:1.45;font-weight:700;overflow-wrap:anywhere;max-height:60%;overflow:hidden}
.rfd-txt.sm{font-size:.74rem;line-height:1.38}
.rfd-navy .rfd-txt{color:#fff;text-shadow:0 1px 3px rgba(0,0,0,.35)}
.rfd-hint{position:absolute;bottom:11.5%;font-size:.53rem;letter-spacing:1.6px;
  text-transform:uppercase;opacity:.5;font-weight:700}
.rfd-navy .rfd-hint{color:var(--gold2)} .rfd-cream .rfd-hint{color:#8a6d1f}
.rfd-badge{position:absolute;top:15%;left:50%;transform:translateX(-50%);
  background:linear-gradient(135deg,var(--gold2),var(--gold));color:var(--navy);
  font-size:.51rem;font-weight:900;letter-spacing:1.7px;padding:3px 10px;border-radius:20px;
  box-shadow:0 3px 10px rgba(0,0,0,.3);transition:opacity .25s,transform .25s;z-index:4}
.rfd-badge.hide{opacity:0;transform:translateX(-50%) scale(.7)}
.rfd-shadow{position:absolute;left:50%;bottom:-14px;transform:translateX(-50%);width:58%;
  height:15px;background:rgba(10,22,40,.22);filter:blur(11px);border-radius:50%;
  transition:width .3s,opacity .3s;z-index:-1}
.rfd-meta{display:flex;align-items:center;justify-content:space-between;margin:26px 2px 8px}
.rfd-bar{height:5px;background:var(--border);border-radius:5px;overflow:hidden;margin-bottom:12px}
.rfd-fill{height:100%;background:linear-gradient(90deg,var(--gold),var(--gold2));border-radius:5px;transition:width .3s}
@media (prefers-reduced-motion:reduce){
  .rfd-card,.rfd-card.anim{transition:opacity .2s!important;transform:none!important}
  .rfd-b{transform:none}
}
.tour-bubble-3d{background:linear-gradient(145deg,#ffffff,#f7f5f0);border-radius:18px;padding:20px;border:1px solid rgba(255,255,255,.6);box-shadow:0 1px 0 rgba(255,255,255,.9) inset,0 -14px 20px rgba(0,0,0,.03) inset,0 16px 32px rgba(10,22,40,.30),0 6px 12px rgba(10,22,40,.18);animation:tourPop .28s cubic-bezier(.34,1.56,.64,1)}
@keyframes tourPop{0%{transform:scale(.92) translateY(6px);opacity:0}100%{transform:scale(1) translateY(0);opacity:1}}
@keyframes tourPulse{0%,100%{box-shadow:0 4px 14px rgba(200,168,75,.45)}50%{box-shadow:0 4px 22px rgba(200,168,75,.85)}}
</style>
</head>
<body>

<!-- Visually hidden but crawlable heading — the app's real UI is rendered
     by JavaScript after load, so this gives search engines a proper H1
     without requiring JS execution. Standard accessible-text pattern,
     also read by screen readers before the app finishes loading. -->
<h1 style="position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0">RE Mastery Hub — South Africa's RE5 &amp; RE1 Exam Success System</h1>
<p style="position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0">South Africa's #1 RE5 and RE1 exam preparation platform, built for financial services professionals studying for their FSCA regulatory exams.</p>

<!-- ═══ LOADING SCREEN ═══ -->
<div id="loadingScreen" role="status" aria-label="Loading RE Mastery Hub">
  <div class="load-spinner"></div>
  <div style="color:rgba(255,255,255,.6);font-size:.85rem;font-family:'Inter',sans-serif">Loading RE Mastery Hub…</div>
</div>

<!-- ═══ ERROR BOUNDARY ═══ -->
<div id="errorBoundary" role="alert">
  <div style="font-family:'Inter',sans-serif">
    <div style="font-size:1.5rem;font-weight:900;color:var(--gold);margin-bottom:12px">Something went wrong</div>
    <div style="color:rgba(255,255,255,.7);font-size:.9rem;margin-bottom:24px;line-height:1.6">The app encountered an unexpected error. Your progress has been saved. Please refresh to continue.</div>
    <button onclick="location.reload()" style="background:var(--gold);color:var(--navy);border:none;padding:12px 28px;border-radius:10px;font-weight:700;font-size:.9rem;cursor:pointer">Refresh App</button>
  </div>
</div>

<!-- ═══ ONBOARDING ═══ -->
<div id="onboarding" role="dialog" aria-label="Setup" aria-modal="true">
  <div class="ob-bg"><div class="ob-card" id="obCard"></div></div>
</div>

<!-- ═══ APP SHELL ═══ -->
<div id="appShell" class="hidden">
  <div class="sp-ticker" aria-hidden="true"><span class="sp-ticker-inner">2,431 mock exams completed &nbsp;&middot;&nbsp; 87% of users improve within 7 days &nbsp;&middot;&nbsp; 1,204 RE5 passes and counting &nbsp;&middot;&nbsp; Scenario questions aligned to Moonstone qualifying criteria &nbsp;&middot;&nbsp; 2,431 mock exams completed &nbsp;&middot;&nbsp; 87% of users improve within 7 days &nbsp;&middot;&nbsp; 1,204 RE5 passes and counting &nbsp;&middot;&nbsp;</span></div>
  <header class="topbar" role="banner">
    <div class="topbar-logo">RE <em>Mastery</em> Hub</div>
    <div class="topbar-right">
      <div id="proChip" class="hidden" style="background:linear-gradient(90deg,var(--gold),var(--gold2));color:var(--navy);padding:3px 9px;border-radius:20px;font-size:.67rem;font-weight:800;letter-spacing:.4px" aria-label="Pro plan active">PRO</div>
      <button class="icon-btn" id="blueprintBtn" aria-label="Exam Success Blueprint" onclick="openPage('blueprint')" style="width:auto;padding:0 10px;gap:5px;background:linear-gradient(135deg,rgba(200,168,75,.18),rgba(200,168,75,.06));border:1px solid rgba(200,168,75,.4)">
        <span style="font-size:1rem">🫆</span><span style="font-size:.68rem;font-weight:800;color:var(--gold);letter-spacing:.2px">Blueprint<sup style="font-size:.5em;margin-left:1px">&reg;</sup></span>
      </button>
      <button class="icon-btn" id="helpBtn" aria-label="Help and contact" onclick="showHelp()">&#128172;</button>
      <button class="icon-btn" id="settingsBtn" aria-label="Settings">&#9881;</button>
    </div>
  </header>
  <main class="main" id="pageContent" role="main" tabindex="-1"></main>
  <nav class="bottomnav" role="navigation" aria-label="Main navigation">
    <button class="bnav-item active" id="bnav-home" aria-label="Home"><span class="bnav-icon" aria-hidden="true">&#8962;</span><span class="bnav-label">Home</span></button>
    <button class="bnav-item" id="bnav-quiz" aria-label="Quiz"><span class="bnav-icon" aria-hidden="true">&#128221;</span><span class="bnav-label">Quiz</span></button>
    <button class="bnav-item" id="bnav-mock" aria-label="Exam simulation"><span class="bnav-icon" aria-hidden="true">&#9201;</span><span class="bnav-label">Exam Sim</span></button>
    <button class="bnav-item" id="bnav-library" aria-label="Study library"><span class="bnav-icon" aria-hidden="true">&#128218;</span><span class="bnav-label">Library</span></button>
    <button class="bnav-item" id="bnav-guardian" aria-label="RE Guardian AI mentor" onclick="App.Guardian.reset();openPage('guardian')"><span class="bnav-icon" aria-hidden="true">🛡️</span><span class="bnav-label">Guardian</span></button>
  </nav>
</div>

<!-- ═══ MODAL ═══ -->
<div class="modal-bg" id="modalBg" role="dialog" aria-modal="true">
  <div class="modal-sheet" id="modalSheet" tabindex="-1">
    <div class="modal-handle" aria-hidden="true"></div>
    <div id="modalContent"></div>
  </div>
</div>

<script>
// ════════════════════════════════════════════════════════
//  RE MASTERY HUB v5.2 — Production Phase 1
//  Copyright © The Trending Bee Creatives
//  All rights reserved.
//
//  ARCHITECTURE NOTES (Phase 1):
//  - All modules are namespaced under App.* for future extraction
//  - Storage layer is versioned and schema-validated
//  - Auth/Payment/Analytics stubs are in place for Phase 2 integration
//  - Error boundary wraps all page renders
//  - Question pipeline: buildPool/buildPoolFill/prepareQuestion
//    ensures answer options are shuffled and remapped before every session
// ════════════════════════════════════════════════════════

// ══════════════════════════════════════════════════════
// MODULE: Config
// Production: extract to config.js or env vars
// ══════════════════════════════════════════════════════
const App = {};

App.Config = Object.freeze({
  appName:        'RE Mastery Hub',
  appVersion:     '7.14.0',
  company:        'The Trending Bee Creatives',
  email:          'thetrendingbeeinfo@gmail.com',
  whatsapp:       '+27795396910',
  whatsappDisplay: '+27 79 539 6910',
  storageVersion: 3,
  storagePrefix:  'rmh_',
  freeQPerSession: 8,
  re5SimTarget:   50,
  re1SimTarget:   80,
  passMark:       65,   // fallback only — use passMarkFor(exam); RE5=66, RE1=65
  passMarkRE5:    66,   // FSCA 2026 Preparation Guide
  passMarkRE1:    65,   // FSCA 2026 Preparation Guide
  maxReviews:     50,
  maxBehaviorTopics: 30,
  maxMockHistory: 20,
  youtubeChannel: 'https://www.youtube.com/@REMasteryHub',
  // sub_confirmation=1 opens YouTube with the subscribe dialog already showing,
  // so it is one tap here and one confirm there — not 'go find my channel'.
  youtubeSubscribe: 'https://www.youtube.com/@REMasteryHub?sub_confirmation=1',
  // TODO: replace with your real Google Business Profile review link
  // (find it via business.google.com → your listing → "Ask for reviews")
  googleReviewUrl: 'https://g.page/r/REPLACE_ME/review',
  prices: Object.freeze({ entry: 99, oneMonth: 189, twoMonth: 269, lifetime: 349, combo: 499 }),
});

// ══════════════════════════════════════════════════════
// MODULE: Storage
// Versioned, schema-validated, quota-safe.
// Phase 2: swap set/get to Supabase calls when user is authenticated.
// ══════════════════════════════════════════════════════
// ══════════════════════════════════════════════════════
// MODULE: Supabase — Phase 2 foundation
// Wraps all Supabase SDK interactions behind a stable internal API.
// Local-first: every method falls back gracefully if SDK is unavailable,
// Supabase is unreachable, or the user is unauthenticated.
// NEVER throws — all errors are caught and logged internally.
// ══════════════════════════════════════════════════════
App.Supabase = (() => {

  // ── Configuration ──────────────────────────────────────────────────────
  // Replace SUPABASE_URL and SUPABASE_ANON_KEY with your project values.
  // These are PUBLIC (anon key only) — safe to commit.
  // Row Level Security on your Supabase tables enforces real access control.
  const SUPABASE_URL      = 'https://mqchecunynbvosodhfft.supabase.co';
  const SUPABASE_ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im1xY2hlY3VueW5idm9zb2RoZmZ0Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3Nzk5ODA3MjgsImV4cCI6MjA5NTU1NjcyOH0.kwQioC1QeeouzUnPdWDtlYhA7EGzoIfcgDHV_CT-j24';

  // Whether the SDK is loaded and configured
  function isReady() {
    return !!(
      typeof window !== 'undefined' &&
      window.supabase &&
      SUPABASE_URL &&
      SUPABASE_ANON_KEY &&
      SUPABASE_ANON_KEY.startsWith('eyJ')
    );
  }

  /**
   * Wait up to maxMs for the async SDK to load.
   * Returns true if SDK became ready, false if timed out.
   * Prevents race condition when user taps button before async script loads.
   */
  function waitForSdk(maxMs = 3000) {
    return new Promise(resolve => {
      if (isReady()) { resolve(true); return; }
      if (!SUPABASE_URL || !SUPABASE_ANON_KEY) { resolve(false); return; }
      const start = Date.now();
      const interval = setInterval(() => {
        if (isReady()) { clearInterval(interval); resolve(true); return; }
        if (Date.now() - start > maxMs) { clearInterval(interval); resolve(false); }
      }, 100);
    });
  }

  // Lazy-initialised client — created once on first use
  let _client = null;
  function getClient() {
    if (_client) return _client;
    if (!isReady()) return null;
    try {
      _client = window.supabase.createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
        auth: {
          autoRefreshToken:      true,
          persistSession:        true,
          detectSessionInUrl:    true,
          storageKey:            'rmh_sb_session',    // namespaced to avoid collisions
        }
      });
    } catch (e) {
      console.warn('[RMH] Supabase client init failed:', e.message);
      _client = null;
    }
    return _client;
  }

  // ── Auth helpers ────────────────────────────────────────────────────────

  /** Returns the current Supabase session or null (never throws). */
  async function getSession() {
    const client = getClient();
    if (!client) return null;
    try {
      const { data } = await client.auth.getSession();
      return data?.session ?? null;
    } catch (e) {
      console.warn('[RMH] getSession failed:', e.message);
      return null;
    }
  }

  /** Returns the authenticated user or null. */
  async function getUser() {
    const session = await getSession();
    return session?.user ?? null;
  }

  /**
   * Sign up with email + password.
   * Returns { ok: true, user, session } or { ok: false, error: string }.
   */
  async function signUpWithPassword(email, password, name) {
    const client = getClient();
    if (!client) return { ok: false, error: 'Supabase not configured' };
    try {
      const { data, error } = await client.auth.signUp({
        email,
        password,
        options: { data: { full_name: name, app: 'rmh' } }
      });
      if (error) return { ok: false, error: error.message };
      return { ok: true, user: data.user, session: data.session };
    } catch (e) {
      return { ok: false, error: e.message };
    }
  }

  /**
   * Sign in with email + password.
   * Returns { ok: true, user, session } or { ok: false, error: string }.
   */
  async function signInWithPassword(email, password) {
    const client = getClient();
    if (!client) return { ok: false, error: 'Supabase not configured' };
    try {
      const { data, error } = await client.auth.signInWithPassword({ email, password });
      if (error) return { ok: false, error: error.message };
      return { ok: true, user: data.user, session: data.session };
    } catch (e) {
      return { ok: false, error: e.message };
    }
  }

  /**
   * Sign in with Google OAuth.
   * Redirects the browser to Google sign-in.
   */
  async function signInWithGoogle() {
    const client = getClient();
    if (!client) return { ok: false, error: 'Supabase not configured' };
    try {
      const { error } = await client.auth.signInWithOAuth({
        provider: 'google',
        options: { redirectTo: window.location.origin }
      });
      if (error) return { ok: false, error: error.message };
      return { ok: true };
    } catch (e) {
      return { ok: false, error: e.message };
    }
  }

  /**
   * Send password reset email.
   * Returns { ok: true } or { ok: false, error: string }.
   */
  async function resetPassword(email) {
    const client = getClient();
    if (!client) return { ok: false, error: 'Supabase not configured' };
    try {
      const { error } = await client.auth.resetPasswordForEmail(email, {
        redirectTo: window.location.origin + '?reset=1'
      });
      if (error) return { ok: false, error: error.message };
      return { ok: true };
    } catch (e) {
      return { ok: false, error: e.message };
    }
  }

  /** Sign out from Supabase. Local state is cleared by App.Auth.signOut(). */
  async function signOut() {
    const client = getClient();
    if (!client) return;
    try {
      await client.auth.signOut();
    } catch (e) {
      console.warn('[RMH] signOut failed:', e.message);
    }
  }

  // ── Profile helpers ─────────────────────────────────────────────────────

  /**
   * Fetch user profile from `profiles` table.
   * Returns profile object or null.
   * Schema: { id, email, name, is_pro, exam, institution, exam_date, referral_code, created_at }
   */
  async function getProfile(userId) {
    const client = getClient();
    if (!client || !userId) return null;
    try {
      const { data, error } = await client
        .from('profiles')
        .select('*')
        .eq('id', userId)
        .single();
      if (error) return null;
      return data;
    } catch (e) {
      console.warn('[RMH] getProfile failed:', e.message);
      return null;
    }
  }

  /**
   * Upsert user profile.
   * Returns { ok: true } or { ok: false, error: string }.
   */
  async function upsertProfile(userId, profileData) {
    const client = getClient();
    if (!client || !userId) return { ok: false, error: 'Not ready' };
    try {
      const { error } = await client
        .from('profiles')
        .upsert({ id: userId, ...profileData, updated_at: new Date().toISOString() });
      if (error) return { ok: false, error: error.message };
      return { ok: true };
    } catch (e) {
      return { ok: false, error: e.message };
    }
  }

  // ── Progress helpers ─────────────────────────────────────────────────────

  /**
   * Fetch cloud progress for a user.
   * Returns progress object or null.
   * Schema: { user_id, progress_json, updated_at }
   */
  async function getCloudProgress(userId) {
    const client = getClient();
    if (!client || !userId) return null;
    try {
      const { data, error } = await client
        .from('progress')
        .select('progress_json, updated_at')
        .eq('user_id', userId)
        .single();
      if (error) return null;
      return data;
    } catch (e) {
      console.warn('[RMH] getCloudProgress failed:', e.message);
      return null;
    }
  }

  /**
   * Save progress to cloud. Fire-and-forget — never blocks UI.
   * Merges by comparing updated_at timestamps.
   */
  async function saveCloudProgress(userId, progressObj) {
    const client = getClient();
    if (!client || !userId) return;
    try {
      await client.from('progress').upsert({
        user_id:       userId,
        progress_json: JSON.stringify(progressObj),
        updated_at:    new Date().toISOString(),
      });
    } catch (e) {
      // Non-fatal — local save already succeeded
      console.warn('[RMH] saveCloudProgress failed:', e.message);
    }
  }

  // ── Pro status validation ────────────────────────────────────────────────

  /**
   * Validate Pro status server-side.
   * Returns true/false. Falls back to local isPro on failure.
   */
  async function validateProStatus(userId) {
    const profile = await getProfile(userId);
    if (!profile) return null;   // null = unknown (use local fallback)
    return !!profile.is_pro;
  }

  // ── Auth state listener ──────────────────────────────────────────────────

  /**
   * Subscribe to auth state changes.
   * Callback receives (event, session).
   * Returns unsubscribe function.
   */
  function onAuthChange(callback) {
    const client = getClient();
    if (!client) return () => {};
    try {
      const { data: { subscription } } = client.auth.onAuthStateChange(callback);
      return () => subscription.unsubscribe();
    } catch (e) {
      console.warn('[RMH] onAuthChange failed:', e.message);
      return () => {};
    }
  }

  // ── Study guides — signed URL is time-limited and access is enforced
  // server-side by the Storage RLS policy, not just the client-side tier
  // check. Even if someone bypassed the UI gate, Supabase would still
  // refuse to generate a valid URL for a tier they don't have.
  async function getStudyGuideUrl(examType) {
    const client = getClient();
    if (!client) return { ok: false, error: 'Not ready' };
    const filename = examType === 'RE5' ? 're5-exam-success-guide.pdf' : 're1-exam-success-guide.pdf';
    try {
      const { data, error } = await client.storage
        .from('study-guides')
        .createSignedUrl(filename, 300); // 5-minute link — enough to open/download, not indefinitely shareable
      if (error) return { ok: false, error: error.message };
      return { ok: true, url: data.signedUrl };
    } catch (e) {
      return { ok: false, error: e.message };
    }
  }

  // ── Public API ───────────────────────────────────────────────────────────
  return {
    isReady,
    waitForSdk,
    getClient,
    getStudyGuideUrl,
    getUrl:     () => SUPABASE_URL,
    getAnonKey: () => SUPABASE_ANON_KEY,
    // Auth — email+password + Google
    getSession,
    getUser,
    signUpWithPassword,
    signInWithPassword,
    signInWithGoogle,
    resetPassword,
    signOut,
    onAuthChange,
    // Profile
    getProfile,
    upsertProfile,
    // Progress
    getCloudProgress,
    saveCloudProgress,
    // Pro
    validateProStatus,
  };
})();

App.Storage = (() => {
  const P = App.Config.storagePrefix;
  const VER_KEY = P + 'schema_version';
  const LEGACY_PREFIXES = ['rmh5_', 'rmh4_', 'rmh3_'];

  /** Migrate data from legacy prefix keys on first run */
  function migrate() {
    const currentVer = _rawGet(VER_KEY);
    if (currentVer === String(App.Config.storageVersion)) return;

    // Carry forward data from the most recent legacy prefix that has a user
    for (const lp of LEGACY_PREFIXES) {
      try {
        const legacyUser = localStorage.getItem(lp + 'user');
        if (legacyUser) {
          const keys = ['user', 'prog', 'beh', 'reviews', 'libFilter', 'libSearch'];
          keys.forEach(k => {
            const v = localStorage.getItem(lp + k);
            if (v !== null && localStorage.getItem(P + k) === null) {
              localStorage.setItem(P + k, v);
            }
          });
          break;
        }
      } catch (_) {}
    }
    try { localStorage.setItem(VER_KEY, String(App.Config.storageVersion)); } catch (_) {}
  }

  function _rawGet(key) {
    try { return localStorage.getItem(key); } catch { return null; }
  }

  function get(key, defaultVal = null) {
    try {
      const raw = localStorage.getItem(P + key);
      if (raw === null) return defaultVal;
      return JSON.parse(raw);
    } catch { return defaultVal; }
  }

  function set(key, value) {
    try {
      localStorage.setItem(P + key, JSON.stringify(value));
      return true;
    } catch (e) {
      // Quota exceeded — prune oldest mock history and retry once
      try {
        const prog = get('prog');
        if (prog && prog.mocks && prog.mocks.length > 5) {
          prog.mocks = prog.mocks.slice(-5);
          localStorage.setItem(P + 'prog', JSON.stringify(prog));
        }
        localStorage.setItem(P + key, JSON.stringify(value));
        return true;
      } catch (_) {
        showToast('Storage full — some progress may not be saved. Free up space on your device.');
        return false;
      }
    }
  }

  function del(key) {
    try { localStorage.removeItem(P + key); } catch {}
  }

  return { get, set, del, migrate };
})();

// ══════════════════════════════════════════════════════
// MODULE: Auth (stub — Phase 2: connect to Supabase)
// Provides a consistent interface so call sites don't change in Phase 2.
// ══════════════════════════════════════════════════════
App.Auth = (() => {
  // ── Local user (always available, offline-safe) ─────────────────────────
  function getLocalUser() { return App.Storage.get('user', null); }
  function saveLocalUser(u) { App.Storage.set('user', u); }

  // ── Sync helpers ─────────────────────────────────────────────────────────

  /**
   * After a successful Supabase sign-in, merge the cloud profile into the
   * local user record so the app always boots from localStorage instantly.
   * Only overwrites local fields that the server explicitly knows about.
   */
  async function syncCloudProfileToLocal(sbUser) {
    try {
      const profile = await App.Supabase.getProfile(sbUser.id);
      if (!profile) return;
      const local = getLocalUser() || {};
      const merged = {
        ...local,
        supabaseId: sbUser.id,
        email:      sbUser.email,
        name:       profile.name       || local.name       || '',
        isPro:      !!profile.is_pro,
        proTier:    profile.pro_tier   || 'none',
        exam:       profile.exam       ?? local.exam       ?? -1,
        institution:profile.institution ?? local.institution ?? -1,
        examDate:   profile.exam_date  || local.examDate   || '',
        // daysLeft intentionally NOT synced — always derived from examDate
        daysLeft:   undefined,
        referralCode: profile.referral_code || local.referralCode || '',
        referralCount: profile.referral_count ?? local.referralCount ?? 0,
        bonusAccessUntil: profile.bonus_access_until || null,
        bonusTier: profile.bonus_tier || null,
        proExpiresAt: profile.pro_expires_at || null,
        planKey: profile.plan_key || null,
        hasGuide: !!profile.has_guide,
        hasExplanations: !!profile.has_explanations,
        hasCoachFeedback: !!profile.has_coach_feedback,
        hasHardSim: !!profile.has_hard_sim,
        isAdmin: !!profile.is_admin,
        stuckTopic: profile.stuck_topic || null,
        stuckTopicExam: profile.stuck_topic_exam || null,
        stuckStreak: profile.stuck_streak || 0,
        stuckFlaggedAt: profile.stuck_flagged_at || null,
      };
      // Active referral bonus ("7 days Pro") grants the full Pro experience,
      // not just exam-selection access — fold it into isPro and the four
      // capability flags here so every isPro-gated feature (quiz cap, Fact
      // Disk, explanations, Guardian, Hard Sim) unlocks during the window,
      // matching what the referral programme actually promises.
      if (merged.bonusAccessUntil && new Date(merged.bonusAccessUntil).getTime() > Date.now()) {
        merged.isPro = true;
        merged.hasGuide = true;
        merged.hasExplanations = true;
        merged.hasCoachFeedback = true;
        merged.hasHardSim = true;
      }
      saveLocalUser(merged);
      STATE.isPro   = merged.isPro;
      STATE.proTier = merged.proTier;
      STATE.bonusAccessUntil = merged.bonusAccessUntil;
      STATE.bonusTier = merged.bonusTier;
      STATE.proExpiresAt = merged.proExpiresAt;
      STATE.hasGuide = merged.hasGuide;
      STATE.hasExplanations = merged.hasExplanations;
      STATE.hasCoachFeedback = merged.hasCoachFeedback;
      STATE.hasHardSim = merged.hasHardSim;
      STATE.isAdmin = merged.isAdmin;
      STATE.stuckTopic = merged.stuckTopic;
      STATE.stuckTopicExam = merged.stuckTopicExam;
      STATE.stuckStreak = merged.stuckStreak;
      STATE.stuckFlaggedAt = merged.stuckFlaggedAt;

      // Sync Guardian's pending task down too, so a task assigned on one
      // device is still checked-in-on if the learner opens the app on
      // another — only overwrite local if the cloud copy is newer.
      if (profile.guardian_pending_task && profile.guardian_task_assigned_at) {
        const localTask = App.Storage.get('guardianPendingTask', null);
        const cloudAt = new Date(profile.guardian_task_assigned_at);
        // A tombstone means this task was already surfaced and dismissed here.
        // Only accept the cloud copy if it was assigned AFTER that dismissal.
        const localMark = localTask && (localTask.assignedAt || localTask.clearedAt);
        const cloudIsNewer = !localMark || cloudAt > new Date(localMark);
        if (cloudIsNewer) {
          App.Storage.set('guardianPendingTask', {
            task: profile.guardian_pending_task,
            assignedAt: profile.guardian_task_assigned_at,
          });
        }
      }

      if (merged.isPro) {
        const chip = document.getElementById('proChip');
        if (chip) chip.classList.remove('hidden');
      }
    } catch (e) {
      console.warn('[RMH] syncCloudProfileToLocal failed:', e.message);
    }
  }

  /**
   * Merge cloud progress into local if cloud is newer.
   * Protects against overwriting newer local data with stale cloud data.
   */
  async function syncCloudProgressToLocal(userId) {
    try {
      const localProg  = App.Progress.get();
      const localTotal = (localProg.re5?.att || 0) + (localProg.re1?.att || 0);

      // Local device is the source of truth whenever it has ANY data.
      // We only ever pull from the cloud on a genuinely fresh device
      // (zero local attempts) — never silently overwrite real local
      // progress, even if the cloud count happens to be higher. Cross-device
      // sync isn't reliable enough yet to risk clobbering a user's actual
      // study history on this device.
      if (localTotal > 0) {
        App.Supabase.saveCloudProgress(userId, localProg).catch(() => {});
        return;
      }

      const cloud = await App.Supabase.getCloudProgress(userId);
      if (!cloud?.progress_json) return;
      const cloudProg = JSON.parse(cloud.progress_json);
      const cloudTotal = (cloudProg.re5?.att || 0) + (cloudProg.re1?.att || 0);
      if (cloudTotal > 0) {
        App.Storage.set('prog', cloudProg);
        console.log('[RMH] Cloud progress restored on fresh device');
      }
    } catch (e) {
      console.warn('[RMH] syncCloudProgressToLocal failed:', e.message);
    }
  }

  // ── Core auth interface ─────────────────────────────────────────────────

  function isAuthenticated() { return getLocalUser() !== null; }

  function isPro() {
    const u = getLocalUser();
    if (!u) return false;
    if (u.isPro) return true;
    // Referral bonus access ("7 days Pro") is a separate mechanism from
    // pro_tier and was never folded into isPro — meaning every bonus
    // recipient has only ever gotten exam-selection access (whatever
    // hasAccess() independently checks), never the isPro-gated perks:
    // unlimited quizzes, Fact Disk, explanations, Guardian, Hard Sim.
    // The referral programme promises "Pro," not a stripped-down tier.
    if (u.bonusAccessUntil && new Date(u.bonusAccessUntil).getTime() > Date.now()) return true;
    return false;
  }

  function getProTier() {
    const u = getLocalUser();
    return u?.proTier || 'none';
  }

  /**
   * Grant Pro locally. Called after payment verification.
   * Phase 2: payment webhook calls Supabase edge function which sets is_pro:true,
   * then app re-validates via App.Supabase.validateProStatus().
   */
  function grantPro(tier) {
    const u = getLocalUser();
    if (u) { u.isPro = true; u.proTier = tier || 'combo'; saveLocalUser(u); }
  }

  /**
   * Sign out — clears local state and Supabase session.
   */
  function signOut() {
    // Only clear the session/auth cache. 'prog' (quiz/exam stats) and 'beh'
    // (weak-topics tracking) are the user's real study history — this app
    // has no cloud sync for progress yet, so this device is the only copy.
    // Wiping it on logout would be permanent, irreversible data loss.
    App.Storage.del('user');
    // Supabase sign-out is fire-and-forget
    if (App.Supabase.isReady()) {
      App.Supabase.signOut().catch(() => {});
    }
  }

  /**
   * Background session init — called once at app boot.
   * Checks for existing Supabase session and syncs profile/progress silently.
   * Never blocks the UI — app is already visible and interactive by the time
   * this resolves.
   */
  async function initCloudSession() {
    if (!App.Supabase.isReady()) return;
    try {
      const session = await App.Supabase.getSession();
      if (!session?.user) return;

      await syncCloudProfileToLocal(session.user);
      await syncCloudProgressToLocal(session.user.id);
      if (typeof _retryPendingProfileSync === 'function') _retryPendingProfileSync();
    } catch (e) {
      console.warn('[RMH] initCloudSession failed:', e.message);
    }
  }

  return {
    getLocalUser, saveLocalUser,
    isAuthenticated, isPro, getProTier, grantPro, signOut,
    initCloudSession, syncCloudProfileToLocal, syncCloudProgressToLocal,
  };
})();

// ══════════════════════════════════════════════════════
// MODULE: Analytics (stub — Phase 2: connect Mixpanel / PostHog / GA4)
// All event calls go through this stub so no call sites change in Phase 2.
// ══════════════════════════════════════════════════════
App.Analytics = (() => {
  /* Phase 2: replace console.debug with real SDK calls:
     analytics.track(event, props) */
  function track(event, props = {}) {
    if (App.Config.storageVersion < 4) return; // suppress in dev if needed
    // console.debug('[Analytics]', event, props);
  }

  // Named event helpers — keeps call sites clean
  const Events = Object.freeze({
    QUIZ_START:       'quiz_start',
    QUIZ_COMPLETE:    'quiz_complete',
    QUIZ_ABANDONED:   'quiz_abandoned',
    SIM_START:        'sim_start',
    SIM_COMPLETE:     'sim_complete',
    SIM_ABANDONED:    'sim_abandoned',
    UPGRADE_VIEWED:   'upgrade_viewed',
    UPGRADE_CLICKED:  'upgrade_clicked',
    ONBOARD_COMPLETE: 'onboard_complete',
    CRASH_PACK_OPEN:  'crash_pack_open',
    REVIEW_SUBMITTED: 'review_submitted',
  });

  return { track, Events };
})();

// ══════════════════════════════════════════════════════
// MODULE: Payment (stub — Phase 2: PayFast / Yoco / Ozow)
// All payment intent logic goes through here.
// ══════════════════════════════════════════════════════
App.Payment = (() => {

  const PLANS = Object.freeze({
    entry:    { label: '1 Month Entry',                price: App.Config.prices.entry    },
    oneMonth: { label: '1 Month Full Access',          price: App.Config.prices.oneMonth },
    twoMonth: { label: '2 Months + Study Guide',       price: App.Config.prices.twoMonth },
    lifetime: { label: 'Lifetime Access + Guide',      price: App.Config.prices.lifetime },
    combo:    { label: 'Combo Lifetime + Guide',       price: App.Config.prices.combo    },
  });

  // ── Static PayFast "Pay Now" shareable links (no signature, no edge fn) ────
  // Emergency fallback only, used if the signed checkout call itself fails.
  // 'entry' reuses the original R99 button (amount unchanged from the old
  // monthly plan). 'lifetime' and 'combo' reuse the old re5Pro/combo buttons
  // for the same reason. oneMonth (R189) and twoMonth (R269) are new price
  // points with no matching PayFast button yet — deliberately left unmapped
  // rather than pointing at an old, wrong-amount link. Falls through to the
  // manual-payment prompt instead, which is safe; an old static link would
  // silently charge the wrong amount.
  const PAY_LINKS = Object.freeze({
    entry:    'https://payf.st/2yjzk',
    lifetime: 'https://payf.st/xd1zj',
    combo:    'https://payf.st/f0b8z',
  });

  // ── Main entry point ───────────────────────────────────────────────────────
  async function initiatePayment(planKey) {
    const plan = PLANS[planKey];
    if (!plan) return;
    App.Analytics.track(App.Analytics.Events.UPGRADE_CLICKED, { plan: planKey });

    if (!App.Auth.isAuthenticated()) {
      showToast('Please sign in to upgrade.');
      return;
    }

    // ✅ Static Pay Now link — redirect straight to PayFast, no signing needed
    _initiatePayFast(plan, planKey);
  }

  // ── PayFast flow — signed checkout via create-checkout edge function ─────
  // A signed checkout carries custom_str1 = the user's id, which is what
  // lets the payfast-itn callback know who paid and activate the right
  // account automatically. Static links cannot carry that, so they can
  // never self-activate; they remain only as an emergency fallback.
  async function _initiatePayFast(plan, planKey) {
    try {
      showToast('Opening secure checkout…');
      const client = App.Supabase.getClient();
      if (!client) throw new Error('Not ready');

      const { data, error } = await client.functions.invoke('create-checkout', {
        body: { planKey }
      });
      if (error || !data?.pfData || !data?.endpoint) {
        throw new Error(error?.message || 'Checkout unavailable');
      }

      closeModal();

      // POST the signed fields to PayFast via an auto-submitting form
      const form = document.createElement('form');
      form.method = 'POST';
      form.action = data.endpoint;
      form.style.display = 'none';
      Object.entries(data.pfData).forEach(([k, v]) => {
        const input = document.createElement('input');
        input.type = 'hidden';
        input.name = k;
        input.value = String(v);
        form.appendChild(input);
      });
      document.body.appendChild(form);
      form.submit();

    } catch (e) {
      console.error('[Payment] Signed checkout failed:', e.message);
      const link = PAY_LINKS[planKey];
      if (link) {
        // Fallback: static link still lets them pay, but activation will
        // not be automatic — flagged to the user so they know to expect it.
        showToast('Opening payment page. Activation may take a few minutes.');
        closeModal();
        window.location.href = link;
      } else {
        _showManualFallback(plan);
      }
    }
  }

  // ── Handle return from PayFast redirect ────────────────────────────────
  function handlePaymentReturn() {
    const params = new URLSearchParams(window.location.search);
    const status  = params.get('payment');
    const planKey = params.get('plan');

    if (!status) return;

    if (window.history?.replaceState) {
      window.history.replaceState(null, '', window.location.pathname);
    }

    if (status === 'success' && planKey) {
      _pollForProActivation(planKey);
    } else if (status === 'cancelled') {
      showToast('Payment cancelled. You can upgrade anytime from the Pro tab.');
    } else if (status === 'failed') {
      showToast('Payment failed. Please try again or contact support.');
    }
  }

  // ── Poll Supabase until Pro is activated by ITN webhook ────────────────
  async function _pollForProActivation(planKey) {
    showToast('Payment received! Activating your account…');
    const plan = PLANS[planKey] || { label: 'Pro', price: 0 };
    const user = App.Auth.getLocalUser?.() || App.Storage.get('rmh_user');
    let attempts = 0;

    const poll = setInterval(async () => {
      attempts++;
      try {
        const proStatus = await App.Supabase.validateProStatus(user?.supabaseId);
        if (proStatus === true) {
          clearInterval(poll);
          _activateProLocally(plan);
          return;
        }
      } catch (e) { console.warn('[Payment] Poll error:', e); }

      if (attempts >= 12) {
        clearInterval(poll);
        // ITN may arrive after polling ends — grant locally, sync on next login
        _activateProLocally(plan, true);
      }
    }, 2500);
  }

  function _activateProLocally(plan, delayed = false) {
    App.Auth.grantPro();
    STATE.isPro = true;
    const chip = document.getElementById('proChip');
    if (chip) chip.classList.remove('hidden');
    _showSuccess(plan, delayed);
  }

  // ── Payment link unavailable — genuine config-error edge case, not a
  // payment-collection flow. Routes to support rather than soliciting
  // banking details in-app.
  function _showManualFallback(plan) {
    const wa = `https://wa.me/${App.Config.whatsapp}?text=${encodeURIComponent(`Hi, I'm trying to upgrade to ${plan.label} (R${plan.price}) but the payment link isn't loading. Can you help?`)}`;
    const mail = `mailto:${App.Config.email}?subject=${encodeURIComponent(`Payment link issue — ${plan.label}`)}&body=${encodeURIComponent(`Hi,\n\nI'm trying to upgrade to ${plan.label} (R${plan.price}) but the payment link isn't loading.\n\nMy registered email is: `)}`;
    showModal('Payment Temporarily Unavailable', `
    <div style="text-align:center;padding:4px 0 16px">
      <div style="font-size:2rem;margin-bottom:10px">⚠️</div>
      <div style="color:var(--text2);font-size:.85rem;line-height:1.6">We couldn't start checkout for <strong>${plan.label}</strong> right now. Please try again in a moment, or reach out and we'll sort it out directly.</div>
    </div>
    <a href="${wa}" target="_blank" rel="noopener"
       style="display:flex;align-items:center;justify-content:center;gap:10px;background:#25D366;color:#fff;font-weight:700;padding:13px 16px;border-radius:10px;text-decoration:none;margin-bottom:10px;font-size:.88rem">
      💬 WhatsApp Support
    </a>
    <a href="${mail}"
       style="display:flex;align-items:center;justify-content:center;gap:10px;background:var(--navy2);color:var(--gold);font-weight:700;padding:13px 16px;border-radius:10px;text-decoration:none;margin-bottom:14px;font-size:.88rem;border:1px solid rgba(200,168,75,.3)">
      ✉️ Email Support
    </a>
    <button class="btn btn-ghost btn-sm btn-full" onclick="closeModal()">Cancel</button>`);
  }

  // ── UI helpers ─────────────────────────────────────────────────────────
  function _showLoading(plan) {
    showModal(`Upgrading to ${plan.label}`, `
    <div style="text-align:center;padding:24px 0">
      <div style="width:40px;height:40px;border:3px solid var(--border);border-top-color:var(--gold);border-radius:50%;animation:spin .8s linear infinite;margin:0 auto 16px"></div>
      <div style="color:var(--text2);font-size:.85rem">Preparing secure checkout…</div>
      <div style="color:var(--text3);font-size:.75rem;margin-top:6px">You will be redirected to PayFast.</div>
    </div>`);
  }

  function _showError(message) {
    showModal('Payment Error', `
    <div style="text-align:center;padding:12px 0">
      <div style="font-size:2rem;margin-bottom:12px">⚠️</div>
      <div style="color:var(--text);font-size:.9rem;margin-bottom:16px;line-height:1.6">${message}</div>
      <button class="btn btn-gold btn-sm btn-full mb8" onclick="closeModal()">Try Again</button>
      <button class="btn btn-ghost btn-sm btn-full" onclick="closeModal()">Cancel</button>
    </div>`);
  }

  function _showSuccess(plan, delayed = false) {
    showModal('', `
    <div style="text-align:center;padding:8px 0">
      <div style="font-size:3rem;margin-bottom:12px">🎉</div>
      <div style="font-weight:900;font-size:1.3rem;color:var(--gold);margin-bottom:6px">Pro Activated!</div>
      <div style="color:var(--text2);font-size:.85rem;line-height:1.6;margin-bottom:20px">
        <strong>${plan.label}</strong> is now active on your account.
      </div>
      <div style="background:rgba(16,185,129,.1);border:1px solid #10b981;border-radius:10px;padding:12px 16px;margin-bottom:20px;font-size:.8rem;color:#10b981;line-height:1.9;text-align:left">
        ✓ Unlimited questions unlocked<br>
        ✓ RE5 + RE1 full content access<br>
        ✓ Full exam simulations unlocked<br>
        ✓ Advanced coaching unlocked
      </div>
      ${delayed ? '<div style="font-size:.75rem;color:var(--text3);margin-bottom:16px">A receipt has been sent to your email by PayFast.</div>' : ''}
      <button class="btn btn-gold btn-lg btn-full" onclick="closeModal();openPage(\'home\')">Start Studying</button>
    </div>`);
  }

  return { initiatePayment, handlePaymentReturn, PLANS };
})();


// ══════════════════════════════════════════════════════
// MODULE: Progress
// All progress read/write goes through here.
// Phase 2: sync to Supabase after each write when online.
// ══════════════════════════════════════════════════════
App.Progress = (() => {
  const EMPTY_PROG = () => ({
    re5: { att: 0, cor: 0, byT: {} },
    re1: { att: 0, cor: 0, byT: {} },
    mocks: [],
    streak: [],
    lastActive: '',
    sessions: 0,
  });

  function get() {
    const p = App.Storage.get('prog', EMPTY_PROG());
    // Normalise shape — guards against partial/legacy storage objects
    // where re5, re1, or byT may be missing after a schema change or
    // a corrupted write. Ensures all call sites are always safe.
    if (!p.re5)       p.re5  = { att: 0, cor: 0, byT: {} };
    if (!p.re1)       p.re1  = { att: 0, cor: 0, byT: {} };
    if (!p.re5.byT)   p.re5.byT = {};
    if (!p.re1.byT)   p.re1.byT = {};
    if (!p.mocks)     p.mocks   = [];
    if (!p.streak)    p.streak  = [];
    return p;
  }

  function save(p) {
    // Enforce caps before saving
    if (p.mocks && p.mocks.length > App.Config.maxMockHistory) {
      p.mocks = p.mocks.slice(-App.Config.maxMockHistory);
    }
    if (p.streak && p.streak.length > 30) {
      p.streak = p.streak.slice(-30);
    }
    App.Storage.set('prog', p);
    // Phase 2: background cloud sync — fire-and-forget, never blocks UI
    if (App.Supabase.isReady()) {
      App.Supabase.getSession().then(session => {
        if (session?.user?.id) {
          App.Supabase.saveCloudProgress(session.user.id, p);
        }
      }).catch(() => {}); // silent — local save already succeeded
    }
  }

  function recordAnswer(exam, topic, correct, sessionId) {
    const p = get();
    const ek = exam.toLowerCase();
    p[ek].att++;
    if (correct) p[ek].cor++;
    if (!p[ek].byT[topic]) p[ek].byT[topic] = { att: 0, cor: 0, missSessions: [] };
    if (!p[ek].byT[topic].missSessions) p[ek].byT[topic].missSessions = []; // legacy records predate this field
    p[ek].byT[topic].att++;
    if (correct) {
      p[ek].byT[topic].cor++;
    } else if (sessionId && !p[ek].byT[topic].missSessions.includes(sessionId)) {
      // One entry per SESSION, not per wrong answer — getting the same topic
      // wrong twice in one sitting is a bad five minutes; wrong across 3+
      // separate sessions is an actual comprehension gap. Capped at the 10
      // most recent sessions so this can't grow unbounded over months of use.
      p[ek].byT[topic].missSessions.push(sessionId);
      if (p[ek].byT[topic].missSessions.length > 10) p[ek].byT[topic].missSessions.shift();
    }
    p.sessions = (p.sessions || 0) + 1;
    const today = new Date().toDateString();
    if (p.lastActive !== today) {
      p.streak = p.streak || [];
      p.streak.push(today);
      p.lastActive = today;
    }
    save(p);
  }

  function recordMock(mode, score, total, pct) {
    const p = get();
    p.mocks = p.mocks || [];
    p.mocks.push({ date: new Date().toLocaleDateString(), mode, score, total, pct });
    save(p);
  }

  function getWeakAreas(exam) {
    const p = get();
    const ek = exam.toLowerCase();
    if (!p[ek] || !p[ek].byT) return [];
    return Object.entries(p[ek].byT)
      .filter(([, d]) => d.att >= 2 && d.cor / d.att < 0.70)
      .map(([t, d]) => ({ t, pct: Math.round(d.cor / d.att * 100), att: d.att }))
      .sort((a, b) => a.pct - b.pct);
  }

  // The specific-coaching trigger: same topic wrong across 3+ SEPARATE
  // sessions (not 3 wrong answers in one sitting — that's just a bad five
  // minutes), with a minimum sample size so a topic barely touched can't
  // qualify off one or two unlucky answers. This is deliberately narrower
  // and higher-confidence than getWeakAreas() above, which is fine with
  // noisier signal for a general weak-topics list.
  function computeStuckTopic(exam) {
    const p = get();
    const ek = exam.toLowerCase();
    if (!p[ek] || !p[ek].byT) return null;
    const candidates = Object.entries(p[ek].byT)
      .filter(([, d]) => d.att >= 5 && (d.missSessions || []).length >= 3 && (d.cor / d.att) < 0.6)
      .map(([t, d]) => ({ t, streak: (d.missSessions || []).length, pct: Math.round(d.cor / d.att * 100) }))
      .sort((a, b) => b.streak - a.streak || a.pct - b.pct);
    return candidates[0] || null;
  }

  function getReadiness() {
    const p = get();
    const r5 = p.re5.att > 0 ? p.re5.cor / p.re5.att : 0;
    const r1 = p.re1.att > 0 ? p.re1.cor / p.re1.att : 0;
    return Math.min(99, Math.round(((r5 + r1) / 2) * 65 + Math.min(35, (p.re5.att + p.re1.att) * 0.7)));
  }

  function reset() { save(EMPTY_PROG()); }

  return { get, save, recordAnswer, recordMock, getWeakAreas, computeStuckTopic, getReadiness };
})();

// ══════════════════════════════════════════════════════
// MODULE: Behaviour
// Tracks study pattern signals for coaching engine.
// ══════════════════════════════════════════════════════
App.Behaviour = (() => {
  const EMPTY = () => ({ quits: 0, skips: 0, lowScores: 0, abandonedMocks: 0, sessions: 0, weakTopics: {}, lastSeen: null });

  function get() { return App.Storage.get('beh', EMPTY()); }

  function save(b) {
    // Cap weakTopics object size
    const wt = b.weakTopics || {};
    const keys = Object.keys(wt);
    if (keys.length > App.Config.maxBehaviorTopics) {
      keys.sort((a, bc) => wt[a] - wt[bc])
          .slice(0, keys.length - App.Config.maxBehaviorTopics)
          .forEach(k => delete wt[k]);
    }
    b.weakTopics = wt;
    App.Storage.set('beh', b);
  }

  function track(event) {
    const b = get();
    switch (event) {
      case 'quit':         b.quits         = (b.quits         || 0) + 1; break;
      case 'skip':         b.skips         = (b.skips         || 0) + 1; break;
      case 'lowScore':     b.lowScores     = (b.lowScores     || 0) + 1; break;
      case 'goodScore':    b.lowScores     = Math.max(0, (b.lowScores || 1) - 1); break;
      case 'abandonMock':  b.abandonedMocks = (b.abandonedMocks || 0) + 1; break;
    }
    b.sessions  = (b.sessions  || 0) + 1;
    b.lastSeen  = Date.now();
    save(b);
  }

  function trackWeakTopic(topic) {
    const b = get();
    b.weakTopics = b.weakTopics || {};
    b.weakTopics[topic] = (b.weakTopics[topic] || 0) + 1;
    save(b);
  }

  function reset() { save(EMPTY()); }

  return { get, track, trackWeakTopic, reset };
})();

// ══════════════════════════════════════════════════════
// MODULE: QuestionPipeline
// THE permanent answer-bias fix.
// All entry points MUST call buildPool or buildPoolFill.
// Original question bank arrays are never mutated.
// ══════════════════════════════════════════════════════
App.QuestionPipeline = (() => {

  /** Fisher-Yates shuffle — always returns a NEW array */
  function shuffle(arr) {
    const r = [...arr];
    for (let i = r.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1));
      [r[i], r[j]] = [r[j], r[i]];
    }
    return r;
  }

  /**
   * prepareQuestion
   * Deep-clones a raw question from the bank, Fisher-Yates shuffles its
   * options array, then remaps q.a to the new position of the correct answer.
   *
   * Guarantees:
   * - Original bank object is never mutated
   * - q.e (explanation) remains on the question, not bound to any option index
   * - q.a after preparation is always the index in the NEW q.o array
   * - Correct answer text is used as the identity anchor (not the original index)
   */
  function prepareQuestion(rawQ) {
    if (!rawQ || !Array.isArray(rawQ.o) || rawQ.o.length === 0) {
      throw new Error('Invalid question object passed to prepareQuestion');
    }

    // Record correct answer TEXT before any shuffle
    const correctText = rawQ.o[rawQ.a];
    if (correctText === undefined) {
      throw new Error(`Question has invalid answer index ${rawQ.a} for options length ${rawQ.o.length}`);
    }

    // Shuffle the options
    const shuffledOptions = shuffle(rawQ.o);

    // Remap a to the new position of the correct answer text
    const newAIndex = shuffledOptions.indexOf(correctText);
    if (newAIndex === -1) {
      // Should be impossible, but fail safely back to original
      return { t: rawQ.t, lv: rawQ.lv || 'K', q: rawQ.q, o: [...rawQ.o], a: rawQ.a, e: rawQ.e,
               taskNum: rawQ.taskNum, taskTitle: rawQ.taskTitle };
    }

    return {
      t: rawQ.t,
      lv: rawQ.lv || 'K',
      q: rawQ.q,
      o: shuffledOptions,
      a: newAIndex,
      e: rawQ.e,
      // Blueprint Exam questions carry taskNum/taskTitle for the official
      // per-task result breakdown. Regular quiz questions don't have these
      // fields, so they'll just come through as undefined — harmless.
      taskNum: rawQ.taskNum,
      taskTitle: rawQ.taskTitle,
    };
  }

  /**
   * buildPool — shuffle question ORDER then prepare each question's OPTIONS.
   * Use for all standard quizzes.
   */
  function buildPool(sourceArray, count) {
    if (!Array.isArray(sourceArray) || sourceArray.length === 0) return [];
    const shuffledOrder = shuffle(sourceArray);
    const selected = shuffledOrder.slice(0, Math.min(count, shuffledOrder.length));
    return selected.map(prepareQuestion);
  }

  /**
   * buildPoolFill — cycles source to reach 'count' then prepares each question.
   * Use for sim exams where target > bank size.
   */
  function buildPoolFill(sourceArray, count) {
    if (!Array.isArray(sourceArray) || sourceArray.length === 0) return [];
    let raw = [];
    while (raw.length < count) { raw = [...raw, ...shuffle(sourceArray)]; }
    raw = raw.slice(0, count);
    return raw.map(prepareQuestion);
  }

  return { shuffle, prepareQuestion, buildPool, buildPoolFill };
})();

// Convenience aliases used throughout the app
const { buildPool, buildPoolFill } = App.QuestionPipeline;

// ══════════════════════════════════════════════════════
// MODULE: Coaching
// Behaviour-pattern-aware coach responses (7 distinct paths).
// ══════════════════════════════════════════════════════
App.Coaching = (() => {
  const MSGS = {
    excellent_improving: [
      "That's a meaningful improvement. {pct}% shows the work you've been putting in is translating into real results. Keep this momentum — consistency over the next few sessions will take you where you need to be.",
      "{pct}% is a strong result given where you started. The pattern of improvement is what matters most right now. Keep your sessions regular and focus on the topics you missed today.",
    ],
    excellent: [
      "Excellent. {pct}% on {total} questions. That is the standard you need to maintain under real exam conditions. Review the questions you missed and understand exactly why — then move on.",
      "{pct}% is well above the pass mark. You clearly understand this material. The only remaining work is identifying your blind spots and addressing them systematically before exam day.",
    ],
    pass_but_quitting: [
      "{pct}% — above the pass mark. But you have exited {quits} sessions before completing them. The real RE exam does not have an exit button. Build the habit of sustained focus now, before exam day.",
      "Passing this session is good. But I need you to notice: you have quit {quits} times recently. The exam tests you for far longer than a short quiz. Start completing every session in full.",
    ],
    pass: [
      "{pct}% — solid. But there are clear gaps in your weaker topics. Study those in the Library before your next session. Do not let a passing score breed complacency.",
      "{pct}% passes the threshold. I am more interested in the questions you got wrong. Every incorrect answer is a gap the real exam will find. Open the Study Library for those topics tonight.",
    ],
    fail_repeated: [
      "This is your {lowScores}th session below the pass mark. The problem is not intelligence — it is method. You are quizzing without studying between sessions. The Library is not optional. Read it. Then come back.",
      "Three or more consecutive failing sessions is a signal that your approach needs to change. Stop repeating the quiz hoping for a different result. Open the Study Library, read the notes, then quiz again.",
    ],
    fail_abandoning: [
      "{pct}% and you have abandoned {abandonedMocks} simulations before completing them. Failing scores and incomplete sessions are compounding your risk. Tonight: Study Library. Tomorrow: complete a full session without exiting.",
      "Failing scores and incomplete practice are two habits that will produce the same result on exam day. Fix both — start with the Study Library, then commit to finishing every session you begin.",
    ],
    fail: [
      "{pct}% on {total} questions. I will be direct: this is not close to the pass mark. What needs to happen now is clear: open the Study Library, read every topic you got wrong, and do not quiz again until you have done that.",
      "{pct}% tells me the material is not yet solid enough to pass. The cycle that works is study then quiz — not just quiz repeatedly. Open the Library now.",
    ],
    abandoned: [
      "You exited this session early — again. That is {quits} times now. The real exam offers no early exit. Every session you abandon is stamina you are not building. Complete your next session in full.",
      "Another early exit. You cannot build exam confidence by quitting mid-session. Start a new session today — and commit to finishing it.",
    ],
    // ── Simulation-specific debrief messages ──────────────────────────────
    sim_pass_strong: [
      "Outstanding. {pct}% on the full {exam} simulation — that is exam-ready performance. Your strongest areas are carrying you well. Focus your final preparation on the topics flagged below to close any remaining gaps.",
      "{pct}% on a timed, full-length {exam} simulation. That is the result that matters. You have demonstrated you can perform under pressure. Review the missed questions once, then trust your preparation.",
    ],
    sim_pass: [
      "{pct}% — you passed the {exam} simulation. A solid result, but the real exam will test you on exactly the topics you dropped marks on today. Do not move on until you have studied each flagged topic in the Library.",
      "You passed. {pct}% clears the {exam} pass mark. But passing a simulation and passing the real exam are not the same thing — the real exam is longer and has no retry. Drill your weak topics before exam day.",
    ],
    sim_borderline: [
      "{pct}% — just below the {exam} pass mark. You are close, but close is not enough on exam day. The gap between where you are and where you need to be is specific: it is the topics flagged below. Study them in the Library. Drill them. Simulate again.",
      "Borderline. {pct}% is encouragingly close to the {exam} pass mark, but you need a buffer — exam conditions add pressure. Every topic you are weak on is a risk. Address them methodically before you rebook.",
    ],
    sim_fail: [
      "{pct}% on the {exam} simulation. I want to be direct: this tells you that the material is not yet at exam standard. That is not a problem — it is information. The path forward is clear: Study Library for every red topic below, targeted drilling, and another simulation in 5-7 days.",
      "{pct}% on the full {exam} simulation. The real exam is this length, this format, under real pressure. What you need is not more quizzing — it is structured study followed by quizzing. Start with the Study Library tonight. One topic at a time.",
    ],
    sim_pass_no_weak: [
      "Exceptional. {pct}% with no topics below the threshold. You are exam-ready. Do one final CRASH mode session the day before your exam and walk in with confidence.",
      "{pct}% and no weak areas detected. That is the performance level the exam requires. Rest, do CRASH mode the day before, and trust your preparation.",
    ],
  };

  function _pick(pool, vars) {
    const template = pool[Math.floor(Math.random() * pool.length)];
    return template.replace(/\{(\w+)\}/g, (_, k) => vars[k] !== undefined ? vars[k] : '');
  }

  function getResponse(pct, total, exam, abandoned) {
    const b = App.Behaviour.get();
    const v = { pct, total, exam, quits: b.quits || 0, lowScores: b.lowScores || 0, abandonedMocks: b.abandonedMocks || 0 };

    // No questions answered yet — encourage rather than classify as failure
    if (!total || total === 0) return { cls: 'coach-warn', msg: _pick(MSGS.no_attempt || ['Complete a quiz to get personalised coaching feedback. Start with RE5 Scope & Definitions.'], v) };

    if (abandoned)                                    return { cls: 'coach-fail', msg: _pick(MSGS.abandoned,       v) };
    if (pct >= 80 && (b.lowScores || 0) >= 2)        return { cls: 'coach-pass', msg: _pick(MSGS.excellent_improving, v) };
    if (pct >= 80)                                    return { cls: 'coach-pass', msg: _pick(MSGS.excellent,       v) };
    if (pct >= App.Config.passMark && (b.quits||0) >= 2) return { cls: 'coach-pass', msg: _pick(MSGS.pass_but_quitting, v) };
    if (pct >= App.Config.passMark)                   return { cls: 'coach-pass', msg: _pick(MSGS.pass,           v) };
    if ((b.lowScores || 0) >= 3)                      return { cls: 'coach-fail', msg: _pick(MSGS.fail_repeated,  v) };
    if ((b.abandonedMocks || 0) >= 2)                 return { cls: 'coach-fail', msg: _pick(MSGS.fail_abandoning, v) };
    return { cls: pct >= 50 ? 'coach-warn' : 'coach-fail', msg: _pick(MSGS.fail, v) };
  }

  /**
   * Dedicated simulation debrief — richer than getResponse().
   * Uses the topic breakdown from finishSim to give exam-specific, actionable feedback.
   *
   * @param {number}   pct        - Score percentage
   * @param {number}   total      - Total questions in sim
   * @param {string}   examMode   - 'RE5', 'RE1', or 'Combo'
   * @param {Array}    weakTopics - Array of [topicName, {c,t}] from finishSim
   * @returns {{ cls: string, headline: string, msg: string, actions: string[] }}
   */
  function getSimDebrief(pct, total, examMode, weakTopics) {
    const passMark = App.Config.passMark || 60;
    const exam     = examMode || 'RE5';
    const v        = { pct, total, exam };
    const hasWeak  = weakTopics && weakTopics.length > 0;

    let cls, headline, msg;

    if (pct >= 80 && !hasWeak) {
      cls      = 'coach-pass';
      headline = '🏆 Exam Ready';
      msg      = _pick(MSGS.sim_pass_no_weak, v);
    } else if (pct >= 75) {
      cls      = 'coach-pass';
      headline = '✅ Strong Pass';
      msg      = _pick(MSGS.sim_pass_strong, v);
    } else if (pct >= passMark) {
      cls      = 'coach-pass';
      headline = '✅ Simulation Passed';
      msg      = _pick(MSGS.sim_pass, v);
    } else if (pct >= passMark - 8) {
      cls      = 'coach-warn';
      headline = '⚠️ Borderline — Not Yet Safe';
      msg      = _pick(MSGS.sim_borderline, v);
    } else {
      cls      = 'coach-fail';
      headline = '❌ Below Pass Mark';
      msg      = _pick(MSGS.sim_fail, v);
    }

    // Build next actions based on result and weak topics
    const actions = [];
    if (hasWeak) {
      actions.push(`Study these topics in the Library: ${weakTopics.slice(0, 3).map(([t]) => t).join(', ')}.`);
      actions.push('Run a Weak Area Drill focused on your flagged topics.');
    }
    if (pct >= passMark) {
      actions.push('Run CRASH mode as your final revision the day before the exam.');
    } else {
      actions.push('Resimulate in 5–7 days after targeted library study.');
    }
    if (pct < passMark - 15) {
      actions.push('Book a study session with the full Study Library before your next simulation.');
    }

    return { cls, headline, msg, actions };
  }

  return { getResponse, getSimDebrief };
})();

// ══════════════════════════════════════════════════════
// MODULE: Guardian
// RE Guardian™ — AI-powered exam mentor.
// Four modes: scenario, examiner, teachme, weakcoach.
// Calls Anthropic API. Reads App.Behaviour + App.Progress
// for personalisation. Scalable: model swap = one line.
// ══════════════════════════════════════════════════════
App.Guardian = (() => {

  // ── Configuration ─────────────────────────────────────────────────────────
  const MODEL        = 'claude-sonnet-4-6';
  const MAX_TOKENS   = 1400;
  // Guardian calls a Supabase Edge Function — never the Anthropic API directly.
  // Direct browser calls to api.anthropic.com are blocked by CORS and would
  // require exposing a secret key in client code. The edge function holds
  // the key server-side and proxies the request. See /supabase/functions/guardian-chat.
  const API_ENDPOINT = 'https://mqchecunynbvosodhfft.supabase.co/functions/v1/guardian-chat';

  // Free tier daily limits (reset at midnight, tracked in localStorage)
  const FREE_LIMITS = { teachme: 0, scenario: 0, examiner: 0, weakcoach: 0 };

  const MODES = Object.freeze({
    teachme:   { id: 'teachme',   label: 'Teach Me',        emoji: '📖', color: '#3b82f6' },
    scenario:  { id: 'scenario',  label: 'Scenario Mode',   emoji: '🎬', color: '#10b981' },
    examiner:  { id: 'examiner',  label: 'Examiner Mode',   emoji: '📝', color: '#c8a84b' },
    weakcoach: { id: 'weakcoach', label: 'Weak Area Coach', emoji: '🎯', color: '#ef4444' },
  });

  // ── Session state ──────────────────────────────────────────────────────────
  let _state = {
    mode:     null,
    history:  [],      // [{role:'user'|'assistant', content:'...'}]
    active:   false,
    streaming: false,
  };
  // Generation counter — every reset/new session bumps this. In-flight async
  // work captures the generation it started with and checks it before writing
  // back to _state or the DOM. This prevents a stale response (e.g. from a
  // session the user already left) from corrupting the current session.
  let _generation = 0;

  // ── Build personalised system prompt ──────────────────────────────────────

  // ── Source grounding ─────────────────────────────────────────────────────
  // Guardian's failures have all been the same shape: it holds a figure but
  // not the meaning around it, then invents the rest. The durable fix is to
  // put the actual Study Library text in front of it for the topic being
  // discussed, and require it to answer from that text.
  const _STOP = new Set(['what','when','where','which','who','whom','why','how','the','a','an','is','are','was','were','do','does','did','of','to','in','on','for','and','or','not','it','this','that','my','me','i','you','can','tell','explain','about','with','from','be','have','has','if','at','as','by','their','there','they','them','please','thanks']);

  // Abbreviations and shorthand learners actually type, mapped to the words
  // that appear in the guide text. Without this, "what is a KI" retrieves
  // nothing and Guardian abstains on a basic question.
  const _ALIAS = {
    ki:['key individual'], kis:['key individual'], fsp:['financial services provider'],
    coi:['conflict of interest'], tcf:['treating customers fairly'],
    fica:['fica','money laundering'], aml:['money laundering'],
    popia:['popia','personal information'], cpd:['continuous professional development'],
    dofa:['date of first appointment'], roa:['record of advice'],
    ombud:['ombud'], ombuds:['ombud'], ombudsman:['ombud'],
    str:['suspicious transaction'], ctr:['cash threshold'],
    fsca:['fsca','authority'], ncr:['national credit'], cpa:['consumer protection'],
    fais:['fais'], debar:['debarment'], debarred:['debarment'],
    rep:['representative'], reps:['representative'], sub:['subcategory'],
  };

  function _retrieveSources(userText) {
    if (!userText || typeof LIBRARY === 'undefined') return '';
    const user = App.Auth.getLocalUser() || {};
    const examFilter = user.exam === 0 ? 'RE5' : user.exam === 1 ? 'RE1' : null;

    const raw = userText.toLowerCase().replace(/[^a-z0-9\s]/g,' ').split(/\s+/)
      .filter(w => w.length > 1 && !_STOP.has(w));
    if (!raw.length) return '';

    // Expand abbreviations, and add a crude singular form so "ombuds"
    // still matches "ombud" and "policies" matches "policy".
    const terms = [];
    raw.forEach(w => {
      terms.push(w);
      if (_ALIAS[w]) _ALIAS[w].forEach(a => terms.push(a));
      if (w.length > 3 && w.endsWith('s')) terms.push(w.slice(0, -1));
    });

    const scored = LIBRARY
      .filter(l => !examFilter || l.exam === examFilter || l.exam === 'Both')
      .map(l => {
        const topicL = l.topic.toLowerCase();
        const hay = (l.topic + ' ' + l.content).toLowerCase();
        let score = 0;
        terms.forEach(t => {
          if (topicL.includes(t)) score += 6;           // title hit weighs most
          score += Math.min(hay.split(t).length - 1, 3);
        });
        return { l, score };
      })
      .filter(x => x.score >= 3)
      .sort((a,b) => b.score - a.score)
      .slice(0, 2);

    if (!scored.length) return '';

    const strip = s => s.replace(/<[^>]+>/g,' ').replace(/&[a-z]+;/g,' ').replace(/\s+/g,' ').trim();
    return scored.map(x =>
      `--- ${x.l.exam} · ${x.l.topic} ---\n${strip(x.l.content).slice(0, 2200)}`
    ).join('\n\n');
  }

  function _buildSystemPrompt(mode, userText) {
    const user    = App.Auth.getLocalUser() || {};
    const beh     = App.Behaviour.get();
    const prog    = App.Progress.get();
    const exam    = user.exam === 0 ? 'RE5' : user.exam === 1 ? 'RE1' : 'RE5 and RE1';
    const name    = user.name ? user.name.split(' ')[0] : 'there';
    const daysLeft= user.daysLeft || '?';

    // Pending task from a previous session — surfaced once at session start,
    // then cleared (see startSession). Stale after 14 days — a task from a
    // month ago is no longer useful context, so it's dropped silently.
    const pendingTaskData = App.Storage.get('guardianPendingTask', null);
    let pendingTask = null, pendingTaskAge = '';
    // A record with task:null is a tombstone — already surfaced, do not re-ask.
    if (pendingTaskData && pendingTaskData.task && pendingTaskData.assignedAt) {
      const ageMs = Date.now() - new Date(pendingTaskData.assignedAt).getTime();
      const ageDays = Math.floor(ageMs / 86400000);
      if (ageDays <= 14) {
        pendingTask = pendingTaskData.task;
        pendingTaskAge = ageDays === 0 ? 'earlier today' : ageDays === 1 ? '1 day ago' : `${ageDays} days ago`;
      }
    }

    // Score trend must reflect REAL activity — never claim progress that hasn't happened
    const totalAttempts = (prog.re5?.att || 0) + (prog.re1?.att || 0);
    const scoreTrend = totalAttempts === 0
      ? 'no activity yet — this is a brand-new learner, do NOT claim any progress or trend'
      : (beh.lowScores > 2 ? 'struggling — needs encouragement' : 'improving — reinforce momentum');
    const examKey = user.exam === 0 ? 're5' : user.exam === 1 ? 're1' : 'both';
    const weakTopics = examKey === 'both'
      ? [...App.Progress.getWeakAreas('re5'), ...App.Progress.getWeakAreas('re1')].sort((a,b) => a.pct - b.pct)
      : App.Progress.getWeakAreas(examKey);
    const weakStr = weakTopics.length > 0
      ? weakTopics.slice(0,5).map(o => `${o.t} (${o.pct}%)`).join(', ')
      : 'none identified yet';

    // Authoritative last-mock figure, straight from stored progress, so the
    // model has a verifiable source rather than reconstructing a number.
    const _sourceBlock = _retrieveSources(userText);

    const _mocks = prog.mocks || [];
    const _last  = _mocks.length ? _mocks[_mocks.length - 1] : null;
    const lastMockStr = _last
      ? `${_last.pct}% (${_last.score}/${_last.total}) on ${_last.mode}, ${_last.date}`
      : 'no simulation completed yet — do NOT reference any score';

    const BASE = `You are RE Guardian™, the personal AI exam mentor built into RE Mastery Hub — South Africa's RE exam preparation platform.

IDENTITY: You are the equivalent of a top lecturer who genuinely cares whether this student passes — assertive, professional, and warm all at once. You are not a generic chatbot and you are not a pushover. You are a specialist in South African financial services regulation — specifically the FAIS Act, General Code of Conduct, FICA, TCF framework, King IV governance, RE5 and RE1 regulatory examinations — and you know this app inside out.

HONESTY RULE — NON-NEGOTIABLE:
- When a learner gets something wrong, tell them clearly and directly: "That's not correct" or "No — here's why." Never dress up a wrong answer as almost-right when it isn't. Softening or avoiding the truth does not help someone pass a regulatory exam — it gets them a question wrong on exam day.
- Immediately follow every correction with a clear, specific explanation of the correct answer and why the mistake is a common trap.
- When a learner gets something right, say so plainly and specifically — name what they did well, don't just say "good job."
- Your praise must always be earned. Do not praise effort that hasn't produced a real result, and never claim progress the learner's actual data doesn't support.

LECTURER BEHAVIOUR — TASKS, BUT ONLY WHEN THEY EARN THEIR PLACE:
- Do NOT bolt a task onto every single message — that gets ignored and feels robotic. Assign a task only at a natural checkpoint: after you've properly explained something, at the end of a session, or when the learner clearly seems ready to act on what you just covered.
- When you DO assign a task, it must be maximally specific — never generic. Bad: "practice more" or "review this topic." Good: "Open the Library and read Topic 13 (POPIA & Data Protection), then run 10 Quiz questions on it." Name the exact topic, the exact feature, and the exact action.
- VIDEO LESSONS — ONLY THESE TOPICS CURRENTLY HAVE ONE. Never tell a learner to "watch the video" for any topic not on this list — it does not exist yet and you will mislead them:
  RE5: Topic 1 (FAIS Act — Core Definitions & Scope), Topic 2 (FAIS Role Players), Topic 3 (General Code of Conduct), Topic 4 (Fit & Proper Requirements), Topic 5 (Supervision & Debarment), Topic 6 (FAIS Ombud), Topic 7 (FICA & Anti-Money Laundering).
  All other RE5 topics, and all RE1 topics, have no video yet — for those, direct the learner to the Study Guide text only.
- ROTATE ACROSS THE FULL APP — do not default to "do more quiz questions" every time. Over the course of working with a learner, deliberately draw on the full range of what's actually built: Study Guide topics (Library), video lessons (only where listed above), Quiz (untimed topic drilling), Exam Simulator (full timed mock), Hard Sim (endurance training, harder than the real exam), Exam Success Blueprint (task-weighted study priority), Blueprint Exam (the official task-weighted mock), Progress page (self-check), and — only in a learner's final week before their exam date — the Crash Pack (Rapid-Fire Quiz, Top 50 Facts, Day-Before Checklist). Match the tool to what the learner actually needs right now, not habit.
- TASK MARKER — MANDATORY FORMAT: whenever you assign a task (per the rules above), end your message with a task marker on its own final line, in exactly this format: [TASK: <short, specific description of what they should do>]. Example: [TASK: Read Study Guide Topic 4 and watch Video Lesson 4, then run Examiner Mode on Fit & Proper Requirements]. If you are NOT assigning a task in this message, do not include this marker at all.
- If the learner tries to move on without completing something you asked of them, call it out directly and hold the line — don't just let it slide.

${pendingTask ? `PENDING TASK FROM LAST SESSION:
The learner was last assigned this task, ${pendingTaskAge} ago: "${pendingTask}"
Your very first priority this session — before anything else, before any new teaching — is to check in on this specific task. Ask directly whether they completed it, and respond accordingly (if yes: confirm briefly, then move forward; if no: gently but firmly hold the line and help them complete it now before starting anything new).` : ''}

APP AWARENESS — YOU ARE THE CORE GUIDE OF THIS APP:
You know every feature in RE Mastery Hub and you actively direct learners to the right one for what they need right now:
- Study Guides (in the Library) — topic-by-topic notes and video lessons. This is one of the app's most valuable features and it is under-used — proactively remind learners about it, not just when they ask. Send them here for foundational understanding of a topic they're shaky on, and mention it by name ("check the study guide for this topic in the Library") often, not only as a last resort.
- Quiz — untimed topic practice. Send learners here to drill a specific weak topic immediately after you teach it.
- Exam Simulator — full timed mock exam at real exam length and difficulty. Send learners here when they're ready to test readiness under realistic conditions.
- Hard Sim — deliberately longer, harder questions than the real exam, designed purely to build reading stamina and endurance. Tell learners this is NOT what the real exam looks like — real questions are shorter — and that the point is training endurance, not matching exam format.
- Exam Success Blueprint — shows exactly how much of the exam each task is worth, with a Guardian coaching card on every task. If a learner seems unsure what to focus on, point them here.
Recommend the specific feature by name whenever it's the right next step. Don't just say "practice more" — say "go do 15 questions in Quiz on Fit & Proper Requirements," "check the study guide for this in the Library," or "you're ready for a full Exam Simulator attempt."

SESSION START — CONTEXT AWARENESS:
The first message you receive will either be the literal text "Start the session." (a generic open — use your mode's normal opening) OR a real situational briefing describing what the learner just did in the app (e.g. finished a quiz, failed a Hard Sim, opened a Library topic). When you receive a real situational briefing, your very first sentence must address that specific situation directly and by name — e.g. "Hi Benjamin, I see you just scored 40% on the RE5 Hard Sim" — never open with a generic greeting when you have real, specific context available. After acknowledging it, move naturally into your mode's normal flow.

TONE RULES:
- Use plain, clear English — no unnecessary jargon
- Be warm and human, not robotic — but never at the cost of clarity or honesty
- Always connect your explanation to the actual exam — say things like "examiners love testing this because..."
- Use South African examples: South African names (Sipho, Zanele, Thandi, Buyani, Lerato), SA institutions (FSCA, FIC, PayFast, Momentum, Discovery), SA contexts

LEARNER CONTEXT:
- Name: ${name}
- Preparing for: ${exam}
- Days until exam: ${daysLeft}
- Weak topics: ${weakStr}
- Overall score trend: ${scoreTrend}
- Most recent simulation result: ${lastMockStr}

LEARNER PERFORMANCE FIGURES — QUOTE EXACTLY, NEVER APPROXIMATE:
Any score, percentage, mark or count you state about this learner MUST be copied
character-for-character from this context or from the message that opened this
session. Do NOT round it, re-derive it, estimate it, or recall it from earlier in
the conversation. If you are about to state a figure and it does not appear
verbatim above or in the opening message, do not state a number at all — refer to
the result in words instead (for example "your most recent attempt"). Stating a
score the learner did not actually get destroys their trust in everything else
you tell them.

KNOWLEDGE SCOPE — You are an expert on:
RE5: FAIS Act, General Code of Conduct, Fit & Proper Requirements, Supervision & Debarment, FAIS Ombud, FICA & AML, TCF (6 outcomes), Insurance Principles, Long-term Insurance, Short-term Insurance, Financial Planning, POPIA, Complaints Handling, NCA & CPA
RE1: KI Duties, Twin Peaks & Regulatory Framework, King IV Governance, COI Management, Supervision of Representatives, FSP Licensing, TCF from KI perspective, FAIS Ombud & Complaints

${_sourceBlock ? `SOURCE MATERIAL — THE OFFICIAL RE MASTERY HUB STUDY GUIDE
The text below is the authoritative source for this learner's question. It
outranks your own training knowledge. Where it addresses the question, answer
FROM IT and stay within what it actually says. Where it is silent, say so
rather than filling the gap from memory.

${_sourceBlock}

--- end of source material ---` : ''}

CRITICAL — HOW TO HANDLE REGULATORY DETAIL:
- Never invent regulatory facts. Precision matters more than sounding complete.
- A figure is not a fact on its own. Before stating any threshold, fee, cap, deadline or penalty, you must also be certain WHO it applies to, WHO pays it, and WHEN it is triggered. If you know the number but not those surrounding details, state only what you are certain of and tell the learner to confirm the rest in the Study Guide. Stating a correct number with the wrong party attached is just as damaging as stating the wrong number.
- If the answer is not in the source material above and not in the verified list below, do NOT answer from memory. Say plainly: "I don't want to give you a figure I'm not certain of — check this in the Study Guide under [topic], and I'll work through it with you." Declining is always better than being confidently wrong; a learner who repeats your error in the exam loses the mark.
- If a learner challenges a fact you stated, do not simply capitulate to be agreeable, and do not stubbornly repeat yourself. Re-check the source material and the verified list above. If they are right, say so plainly and give the correct position. If they are wrong, hold your ground and show them where the correct position comes from. Being agreeable is not the same as being helpful.

CRITICAL VERIFIED NUMBERS — RE5 (source of truth, from the official RE Mastery Hub Study Guide):
For any of the specific figures below, you MUST use the exact value given here — never substitute a number from your own memory, even if you believe a different figure is correct. These have been directly verified against the official guide. If a learner states one of these incorrectly, correct them using the exact value below.
- FAIS effective date: 30 September 2004
- FAIS Ombud maximum determination: R3,500,000 per complaint (not R2,500,000). Loss above this must be pursued through the courts.
- FAIS Ombud case fee: THERE IS NO CASE FEE. The FAIS Ombud discontinued the case fee with effect from 10 September 2012 (FAIS Ombud notice). Rule 9(a) previously permitted a discretionary fee of up to R1,000 charged to the respondent, but it is no longer levied. The service is free to complainants and no fee is charged to respondents. Older study material still quotes R1,000 — that material is out of date. Never state that a case fee is payable by anyone.
- Immaterial financial interest: R1,000 per calendar year AGGREGATE from any ONE third party. Above that it is material and must be disclosed and managed.
- SubCat A funeral policy max benefit: Less than R30,000
- RE5 deadline from DOFA: 24 months
- COB Training deadline from DOFA: 12 months
- Full qualification deadline from DOFA: 6 years
- CPD cycle: 1 June to 31 May (BN 194 definition). Key individuals and representatives must submit CPD evidence to the FSP within 15 DAYS after the cycle expires (s33(2)) — i.e. by 15 June. The FSP must record it in the competence register within 30 DAYS after cycle end (s32(3)(a)). These are two different deadlines.
- Minimum CPD hours (BN 194 s33): 6 hours for a single subclass within one class of business; 12 hours for more than one subclass within a single class; 18 hours for more than one class of business.
- Experience lapses after 5 consecutive years without rendering the relevant service (BN 194 s16).
- Professional indemnity and fidelity cover is prescribed by Board Notice 123 of 2009 — NOT BN 194 of 2017. BN 194 s51 repealed only BN 105 of 2008, BN 106 of 2008 and BN 102 of 2012.
- STR (Suspicious Transaction Report) deadline: as soon as possible, and NOT LATER THAN 15 DAYS (excluding Saturdays, Sundays and public holidays) after becoming aware of the fact giving rise to the suspicion — Regulation 24(3). Do NOT confuse this with the Cash Threshold Report period, which is 3 business days. The two are different and this is a common exam trap.
- Cash Threshold Report (CTR): triggered by cash EXCEEDING R49,999.99 (raised from R24,999.99 on 14 November 2022 — the old figure is a common distractor). CTR must be filed as soon as possible and no later than 3 BUSINESS DAYS. Note the contrast with the STR period of 15 days.
- FICA record retention: 5 years from the date the business relationship is TERMINATED
- Debarment Part I notification: within 5 days of the decision
- Debarment Part II notification: within 15 days of the debarment date
- Dishonesty debarment waiting period: minimum 12 months
- KI vacancy deadline: fill within 60 days
- Ombud approach deadline: 6 months from the FSP's final response
- CPA cooling-off period: 5 business days (direct marketing sales)
- ROA (Record of Advice) retention: 5 years from the date the advice was given

For RE1-specific numbers not listed above, and for any figure outside this list, continue to follow the "verify in your study guide" rule rather than guessing — this verified list does not yet cover RE1.

CRITICAL VERIFIED NUMBERS — RE1 (source of truth, from the official RE Mastery Hub RE1 Study Guide):
The same rule applies here — use these exact values, never your own memory, for any of these figures.
- RE5 exam: 50 questions, 2 hours, pass mark 66% (33 of 50). RE1 exam: 80 questions, 2 hours 30 minutes, pass mark 65% (52 of 80). Note RE5 and RE1 have DIFFERENT pass marks.
- RE1 exam format: 80 multiple-choice questions
- RE1 pass mark: 65% = 52 out of 80 correct
- RE1 duration: 2 hours 30 minutes (150 minutes) for 80 questions. RE5 duration: 2 hours (120 minutes) for 50 questions.
- KI vacancy deadline: fill within 60 days
- KI CPD start: IMMEDIATELY upon appointment — no qualifying period (unlike representatives, who have a 24-month DOFA period)
- Financial soundness requirement: does NOT apply to KIs — applies to FSPs and representatives only. This is one of the most frequently tested RE1 facts.
- COI material threshold: R1,000 per year from the SAME third party (same figure as RE5)
- COI policy review frequency: at least ANNUALLY
- FAIS Ombud maximum determination: R3,500,000 per complaint (not R2,500,000). Loss above this must be pursued through the courts. (same as RE5 — R2,500,000 is a common wrong distractor)
- FAIS Ombud case fee: THERE IS NO CASE FEE. The FAIS Ombud discontinued the case fee with effect from 10 September 2012 (FAIS Ombud notice). Rule 9(a) previously permitted a discretionary fee of up to R1,000 charged to the respondent, but it is no longer levied. The service is free to complainants and no fee is charged to respondents. Older study material still quotes R1,000 — that material is out of date. Never state that a case fee is payable by anyone.
- Ombud approach deadline: 6 MONTHS from the FSP's final response
- Debarment Part I notification: within 5 DAYS of the debarment decision
- Debarment Part II notification: within 15 DAYS of the debarment date
- Annual compliance report deadline: within 4 MONTHS of the FSP's financial year-end
- Annual financial statements deadline: within 4 MONTHS of the FSP's financial year-end
- CPD evidence submission: within 15 days of cycle end (by 15 June each year)
- PI insurance lapse: causes AUTOMATIC licence suspension — no grace period, effective immediately upon lapse

For any RE1 figure outside this list too, follow the "verify in your study guide" rule rather than guessing.`;

    const MODE_PROMPTS = {
      teachme: `
MODE: TEACH ME
The learner will ask questions in plain English about RE5/RE1 topics. Your job:
1. Answer clearly and simply — imagine explaining to a smart friend who is not a lawyer
2. Always give a practical South African example to illustrate the concept
3. After explaining, ask ONE follow-up question to check understanding — keep the learning active
4. Link every explanation to how examiners test this concept
5. Keep responses focused — 3-5 paragraphs maximum. Do not lecture.
Start by warmly inviting them to ask their first question.`,

      scenario: `
MODE: SCENARIO MODE
Present realistic RE5/RE1 workplace situations and help the learner work through them.
1. Present ONE scenario at a time — a specific South African FSP workplace situation
2. Ask what the person should do, why, and what legislation applies
3. Wait for their response — do NOT give the answer immediately
4. After they respond: explain the correct approach, identify the specific legislation or principle, explain the consequences of the wrong approach, and teach what examiners test in similar scenarios
5. Make scenarios progressively more challenging as the session continues
6. Focus scenarios on the learner's weak topics: ${weakStr}
Start with a scenario immediately — do not explain the rules first. Drop them into the situation.`,

      examiner: `
MODE: EXAMINER MODE
You are acting as an FSCA examiner conducting a verbal examination. This is the most rigorous mode.
1. Ask ONE question at a time — phrased exactly as RE5/RE1 exam questions are phrased
2. Wait for a response before asking the next question
3. Do NOT give multiple choice options — the learner must answer from memory (free text)
4. After each response: reveal whether they were correct, explain WHY wrong answers are tempting (the distractor logic), and explain what the examiner was actually testing with that question
5. Keep a running mental tally — after every 5 questions, give a brief score update and encouragement
6. Focus on the learner's weak topics: ${weakStr}
7. Vary difficulty — mix straightforward recall with application and analysis questions
Begin the examination immediately with your first question. Set the tone: professional but not intimidating.`,

      weakcoach: `
MODE: WEAK AREA COACH
This is a personalised coaching session based on the learner's actual performance data.
LEARNER'S WEAK AREAS: ${weakStr}

Your mission: Help this specific learner strengthen their weakest topics through targeted practice.
1. Start by acknowledging their weak areas warmly — frame them as opportunities, not failures
2. Focus the session exclusively on their weak topics — do not drift to topics they know well
3. Alternate between: testing their current understanding → teaching the gap → testing again to confirm improvement
4. Celebrate every improvement explicitly: "That's a big improvement from where you started this session"
5. End the session with a clear summary: what improved, what still needs work, recommended next steps
6. Be specific — reference actual thresholds, deadlines, and rules relevant to their weak topics

If weak areas are: ${weakStr === 'none identified yet' ? 'not yet identified' : weakStr}
${weakStr === 'none identified yet' ? 'Tell the learner to complete a few quizzes first so you can personalise this session, then offer to do a general session covering the most frequently tested topics.' : 'Begin immediately by addressing their weakest topic first.'}`,
    };

    return `${BASE}\n\n${MODE_PROMPTS[mode] || MODE_PROMPTS.teachme}`;
  }

  // ── Daily usage tracking (free tier) ───────────────────────────────────────
  function _getUsage() {
    const today = new Date().toDateString();
    const raw   = localStorage.getItem('rmh_guardian_usage');
    try {
      const parsed = raw ? JSON.parse(raw) : {};
      if (parsed.date !== today) return { date: today, teachme: 0, scenario: 0 };
      return parsed;
    } catch { return { date: today, teachme: 0, scenario: 0 }; }
  }

  function _incrementUsage(mode) {
    const usage = _getUsage();
    usage[mode] = (usage[mode] || 0) + 1;
    localStorage.setItem('rmh_guardian_usage', JSON.stringify(usage));
  }

  function canUseMode(mode) {
    if (STATE.hasCoachFeedback) return true;
    const limit = FREE_LIMITS[mode];
    if (limit === 0) return false; // Pro only
    const usage = _getUsage();
    return (usage[mode] || 0) < limit;
  }

  function getRemainingUses(mode) {
    if (STATE.hasCoachFeedback) return Infinity;
    const limit = FREE_LIMITS[mode];
    if (limit === 0) return 0;
    const usage = _getUsage();
    return Math.max(0, limit - (usage[mode] || 0));
  }

  // ── Call Guardian edge function (proxies to Anthropic) with streaming ─────
  async function _callAPI(systemPrompt, history) {
    let authHeader = {};
    try {
      const session = await App.Supabase.getSession();
      if (session?.access_token) authHeader = { 'Authorization': `Bearer ${session.access_token}` };
    } catch {}

    // Supabase Edge Functions require the anon key in the apikey header.
    // Without it the gateway silently blocks the request before it reaches
    // the function code — which is why logs showed zero invocations.
    const ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im1xY2hlY3VueW5idm9zb2RoZmZ0Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3Nzk5ODA3MjgsImV4cCI6MjA5NTU1NjcyOH0.kwQioC1QeeouzUnPdWDtlYhA7EGzoIfcgDHV_CT-j24';

    let response;
    try {
      response = await fetch(API_ENDPOINT, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'apikey': ANON_KEY,
          ...authHeader,
        },
        body: JSON.stringify({
          model:      MODEL,
          max_tokens: MAX_TOKENS,
          system:     systemPrompt,
          messages:   history,
          stream:     true,
        }),
      });
    } catch (networkErr) {
      // Genuine network failure (offline, DNS, etc.) — not a server error
      throw new Error('NETWORK_OFFLINE');
    }

    if (!response.ok) {
      let detail = '';
      try { detail = await response.text(); } catch {}
      console.error(`[Guardian] Edge function error ${response.status}:`, detail);
      throw new Error(`API_ERROR_${response.status}`);
    }

    return response;
  }

  // ── Process streaming response and render progressively ───────────────────
  // Reads the SSE stream and accumulates the full response text plus
  // whether the model was cut off by the token limit. Does NOT touch the
  // DOM — the edge function delivers the whole response in one event
  // (Gemini's REST API isn't truly streamed), so incremental DOM writes
  // here would just be one big jump anyway. _revealText() handles the
  // actual smooth on-screen animation separately.
  async function _streamResponse(response) {
    const reader  = response.body.getReader();
    const decoder = new TextDecoder();
    let   full      = '';
    let   truncated = false;
    let   buffer    = '';

    while (true) {
      const { done, value } = await reader.read();
      if (done) break;

      buffer += decoder.decode(value, { stream: true });
      const lines = buffer.split('\n');
      buffer = lines.pop();

      for (const line of lines) {
        if (!line.startsWith('data: ')) continue;
        const data = line.slice(6).trim();
        if (data === '[DONE]') continue;
        try {
          const parsed = JSON.parse(data);
          const delta  = parsed?.delta?.text || parsed?.delta?.value || '';
          if (delta) full += delta;
          if (parsed?.truncated) truncated = true;
        } catch {}
      }
    }
    return { text: full, truncated };
  }

  // Animates revealing text into targetEl a few words at a time, so even a
  // single-chunk response feels like it's typing out smoothly instead of
  // appearing all at once. Resolves once fully revealed.
  function _revealText(targetEl, text) {
    return new Promise(resolve => {
      if (!targetEl || !text) { if (targetEl) targetEl.innerHTML = _formatText(text || ''); resolve(); return; }
      const words = text.split(' ');
      let shown = 0;
      const WORDS_PER_TICK = 3;
      const TICK_MS = 35;
      const chat = document.getElementById('guardianChat');

      const timer = setInterval(() => {
        shown = Math.min(words.length, shown + WORDS_PER_TICK);
        const partial = words.slice(0, shown).join(' ');
        targetEl.innerHTML = _formatText(partial) + (shown < words.length ? '<span class="guardian-cursor">▋</span>' : '');
        if (chat) chat.scrollTop = chat.scrollHeight;
        if (shown >= words.length) {
          clearInterval(timer);
          targetEl.innerHTML = _formatText(text);
          resolve();
        }
      }, TICK_MS);
    });
  }

  // ── Format guardian text (basic markdown → HTML) ─────────────────────────
  function _formatText(text) {
    return text
      .replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
      .replace(/\*(.+?)\*/g, '<em>$1</em>')
      .replace(/`(.+?)`/g, '<code style="background:rgba(200,168,75,.15);padding:1px 5px;border-radius:3px;font-size:.9em">$1</code>')
      .replace(/^#{1,3}\s+(.+)$/gm, '<div class="fw7 mb4 mt8" style="color:var(--gold)">$1</div>')
      .replace(/\n\n/g, '</p><p style="margin-top:8px">')
      .replace(/\n/g, '<br/>')
      .replace(/^/, '<p>')
      .replace(/$/, '</p>');
  }

  // ── Start a Guardian session ──────────────────────────────────────────────
  // ── Friendly error messages by failure type ────────────────────────────────
  function _errorMessage(err) {
    const msg = err?.message || '';
    if (msg === 'NETWORK_OFFLINE') {
      return "I can't reach the server right now — please check your internet connection and try again.";
    }
    if (msg.startsWith('API_ERROR_5') || msg.includes('500')) {
      return "Guardian's AI service is temporarily unavailable. Please try again in a moment — if this keeps happening, let us know on WhatsApp at +27 79 539 6910.";
    }
    // MUST be checked before the generic 4xx branch below — 'API_ERROR_429'
    // starts with 'API_ERROR_4' and would otherwise be caught by that
    // broader check first, showing a misleading "couldn't process" message
    // instead of the honest rate-limit explanation.
    if (msg.includes('429') || msg.includes('RATE_LIMIT')) {
      return "Guardian is getting a lot of requests right now — this usually clears in a minute or two. Please try again shortly, or upgrade to Pro for priority access.";
    }
    if (msg.startsWith('API_ERROR_4') || msg.includes('400')) {
      return "I couldn't process that request. Please try rephrasing, or start a new session.";
    }
    return "RE Guardian is currently being upgraded. Please use the Study Library and Quiz features while we complete the setup. We apologise for the inconvenience.";
  }

  // ── Task memory — extract, persist, and clear ──────────────────────────
  // Parses a trailing [TASK: ...] marker out of Guardian's reply, strips it
  // from what the learner sees, and saves it so the NEXT session can open
  // by checking in on it instead of starting cold.
  function _extractAndSaveTask(fullText) {
    const match = fullText.match(/\[TASK:\s*([^\]]+)\]\s*$/);
    if (!match) return fullText;

    const task = match[1].trim();
    const cleanText = fullText.slice(0, match.index).trim();

    const record = { task, assignedAt: new Date().toISOString() };
    App.Storage.set('guardianPendingTask', record);

    // Best-effort cloud sync — never blocks the UI, never throws visibly
    try {
      const localUser = App.Auth.getLocalUser() || {};
      if (localUser.supabaseId && App.Supabase.isReady()) {
        App.Supabase.getClient().from('profiles').update({
          guardian_pending_task: task,
          guardian_task_assigned_at: record.assignedAt,
        }).eq('id', localUser.supabaseId).then(({error}) => {
          if (error) console.warn('[Guardian] task sync failed:', error.message);
        });
      }
    } catch (e) { console.warn('[Guardian] task sync failed:', e.message); }

    return cleanText;
  }

  // Called once a pending task has been surfaced to the learner at the start
  // of a new session — prevents it repeating in every session indefinitely.
  function _clearPendingTask() {
    // Tombstone, not null. A null local value made every cloud sync think the
    // cloud copy was newer, which resurrected the task on every session start.
    App.Storage.set('guardianPendingTask', { task: null, clearedAt: new Date().toISOString() });
    try {
      const localUser = App.Auth.getLocalUser() || {};
      if (localUser.supabaseId && App.Supabase.isReady()) {
        App.Supabase.getClient().from('profiles').update({
          guardian_pending_task: null,
          guardian_task_assigned_at: null,
        }).eq('id', localUser.supabaseId).then(({error}) => {
          if (error) console.warn('[Guardian] task clear failed to sync:', error.message);
        });
      }
    } catch (e) { /* non-critical */ }
  }

  async function startSession(mode, context) {
    if (!canUseMode(mode)) { _showLimitReached(mode); return; }
    if (_state.streaming) return; // don't stomp an in-flight request

    _generation += 1;
    const myGen = _generation;

    _state.mode    = mode;
    _state.history = [];
    _state.active  = true;
    _state.streaming = true;

    const primerText = context || 'Start the session.';
    const systemPrompt = _buildSystemPrompt(mode, primerText);
    // The pending task (if any) is now baked into systemPrompt above —
    // clear it so it doesn't repeat in every future session.
    _clearPendingTask();

    _showTyping();

    try {
      const response = await _callAPI(systemPrompt, [
        { role: 'user', content: primerText }
      ]);
      if (myGen !== _generation) return;

      let { text: fullText, truncated } = await _streamResponse(response);
      if (myGen !== _generation) return;
      fullText = _extractAndSaveTask(fullText);

      const targetEl = _addGuardianBubble();
      _hideTyping();
      await _revealText(targetEl, fullText);
      if (myGen !== _generation) return;

      _state.history = [
        { role: 'user',      content: primerText },
        { role: 'assistant', content: fullText },
      ];
      const historyIdx = 1;
      if (targetEl) targetEl.dataset.historyIdx = String(historyIdx);
      if (truncated) _appendContinueButton(targetEl, historyIdx);

      if (!STATE.isPro) _incrementUsage(mode);
      _focusInput();

    } catch (err) {
      if (myGen !== _generation) return;
      _hideTyping();
      _addGuardianBubble(_errorMessage(err));
      console.error('[Guardian] startSession error:', err);
    } finally {
      if (myGen === _generation) _state.streaming = false;
    }
  }

  // ── Send a user message ────────────────────────────────────────────────────
  async function sendMessage(userText) {
    if (_state.streaming || !_state.active || !userText.trim()) return;
    if (!canUseMode(_state.mode)) { _showLimitReached(_state.mode); return; }

    const myGen = _generation;
    _state.streaming = true;

    _addUserBubble(userText);
    _state.history.push({ role: 'user', content: userText });

    _showTyping();

    try {
      const systemPrompt = _buildSystemPrompt(_state.mode, userText);
      const response = await _callAPI(systemPrompt, _state.history);
      if (myGen !== _generation) return;

      const { text: rawText, truncated } = await _streamResponse(response);
      if (myGen !== _generation) return;
      const fullText = _extractAndSaveTask(rawText);

      const targetEl = _addGuardianBubble();
      _hideTyping();
      await _revealText(targetEl, fullText);
      if (myGen !== _generation) return;

      _state.history.push({ role: 'assistant', content: fullText });
      const historyIdx = _state.history.length - 1;
      if (targetEl) targetEl.dataset.historyIdx = String(historyIdx);
      if (truncated) _appendContinueButton(targetEl, historyIdx);

      if (!STATE.isPro) _incrementUsage(_state.mode);

    } catch (err) {
      if (myGen !== _generation) { console.error('[Guardian] stale sendMessage error (ignored):', err); return; }
      _hideTyping();
      _addGuardianBubble(_errorMessage(err));
      _state.history.pop();
      console.error('[Guardian] sendMessage error:', err);
    } finally {
      if (myGen === _generation) _state.streaming = false;
      _focusInput();
    }
  }

  // ── Continue-reading mechanism for truncated responses ─────────────────────
  // Caps at 2 continuations per response to bound API cost/latency — if the
  // model is still hitting the token limit after that, we stop offering more.
  function _appendContinueButton(targetEl, historyIdx) {
    if (!targetEl) return;
    const btn = document.createElement('button');
    btn.className = 'btn btn-outline btn-xs mt8';
    btn.textContent = 'Continue reading →';
    btn.onclick = () => continueReading(historyIdx, btn);
    targetEl.appendChild(document.createElement('br'));
    targetEl.appendChild(btn);
  }

  async function continueReading(historyIdx, btnEl) {
    if (_state.streaming) return;
    const myGen = _generation;
    const continueCount = (_state.history[historyIdx]?._continues || 0) + 1;
    if (continueCount > 2) return; // hard cap

    _state.streaming = true;
    if (btnEl) { btnEl.textContent = 'Loading more…'; btnEl.disabled = true; }

    try {
      const systemPrompt = _buildSystemPrompt(_state.mode);
      const existingText  = _state.history[historyIdx].content;
      const continuePrompt = [
        ..._state.history.slice(0, historyIdx + 1),
        { role: 'user', content: 'Continue exactly where you left off. Do not repeat anything you already said.' },
      ];
      const response = await _callAPI(systemPrompt, continuePrompt);
      if (myGen !== _generation) return;

      const { text: moreText, truncated } = await _streamResponse(response);
      if (myGen !== _generation) return;

      const targetEl = document.querySelector(`[data-history-idx="${historyIdx}"]`);
      if (btnEl) btnEl.remove();

      const combinedText = existingText + ' ' + moreText;
      if (targetEl) {
        // Keep existing text static, only animate the new portion appended.
        const tailSpan = document.createElement('span');
        targetEl.appendChild(tailSpan);
        await _revealText(tailSpan, moreText);
      }

      _state.history[historyIdx].content = combinedText;
      _state.history[historyIdx]._continues = continueCount;
      if (truncated && continueCount < 2 && targetEl) _appendContinueButton(targetEl, historyIdx);

    } catch (err) {
      console.error('[Guardian] continueReading error:', err);
      if (btnEl) { btnEl.textContent = 'Couldn\'t load more — tap to retry'; btnEl.disabled = false; }
    } finally {
      if (myGen === _generation) _state.streaming = false;
    }
  }

  // ── UI helpers ─────────────────────────────────────────────────────────────
  function _addGuardianBubble(text) {
    const chat = document.getElementById('guardianChat');
    if (!chat) return null;
    const div = document.createElement('div');
    div.className = 'guardian-bubble guardian-ai';
    if (text) div.innerHTML = _formatText(text);
    chat.appendChild(div);
    chat.scrollTop = chat.scrollHeight;
    return div;
  }

  function _addUserBubble(text) {
    const chat = document.getElementById('guardianChat');
    if (!chat) return;
    const div = document.createElement('div');
    div.className = 'guardian-bubble guardian-user';
    div.textContent = text;
    chat.appendChild(div);
    chat.scrollTop = chat.scrollHeight;
  }

  function _showTyping() {
    const chat = document.getElementById('guardianChat');
    if (!chat) return;
    const div = document.createElement('div');
    div.id = 'guardianTyping';
    div.className = 'guardian-bubble guardian-ai guardian-typing';
    div.innerHTML = '<span></span><span></span><span></span>';
    chat.appendChild(div);
    chat.scrollTop = chat.scrollHeight;
  }

  function _hideTyping() {
    const el = document.getElementById('guardianTyping');
    if (el) el.remove();
  }

  function _focusInput() {
    const inp = document.getElementById('guardianInput');
    if (inp) inp.focus();
  }

  function reset() {
    _generation += 1; // abandon any in-flight request from the old session
    _state = { mode: null, history: [], active: false, streaming: false };
  }

  // ── Free tier is now zero across all modes — this always means "not Pro".
  // Shows a STATIC blurred placeholder (no real AI call, no API cost) with an
  // unlock overlay, rather than burning a real generated response on someone
  // who will never get to read it clearly.
  function _showLimitReached(mode) {
    _hideTyping();
    const limit = FREE_LIMITS[mode];
    if (limit === 0) { _showBlurredTeaser(mode); return; }
    // Fallback path — only reachable if a mode is ever given a non-zero
    // free limit again in future.
    const modeInfo = MODES[mode];
    const label = modeInfo ? modeInfo.label : 'this mode';
    _addGuardianBubble(
      `You've used today's free ${label} sessions. Your limit resets tomorrow — or upgrade to Pro for unlimited access to me any time you need help. 🛡️`
    );
  }

  function _showBlurredTeaser(mode) {
    const chat = document.getElementById('guardianChat');
    if (!chat) return;
    const placeholder = "Great question! Let's break this down step by step so it really sticks. The key thing to understand here is how this connects to what examiners actually test — once you see the pattern, you'll be able to apply it confidently across similar questions on exam day, not just this one.";

    const div = document.createElement('div');
    div.className = 'guardian-bubble guardian-ai';
    div.style.position = 'relative';
    div.style.overflow  = 'hidden';
    div.style.minHeight = '110px';
    div.innerHTML = `
      <div style="filter:blur(5px);user-select:none;pointer-events:none">${placeholder}</div>
      <div style="position:absolute;inset:0;background:linear-gradient(180deg,rgba(15,31,61,.6),rgba(15,31,61,.9));display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:16px;border-radius:inherit">
        <div style="font-size:1.6rem;margin-bottom:6px">🔒</div>
        <div style="font-weight:800;color:#fff;font-size:.9rem;margin-bottom:4px">Unlock RE Guardian</div>
        <div style="font-size:.75rem;color:rgba(255,255,255,.75);margin-bottom:12px;max-width:240px;line-height:1.4">Your personal AI exam mentor is a Pro feature — unlimited coaching, weak-area tracking, and real exam-day support.</div>
        <button class="btn btn-gold btn-sm" onclick="openPage('pro')">Upgrade to Pro</button>
      </div>`;
    chat.appendChild(div);
    chat.scrollTop = chat.scrollHeight;
  }

  // ── Read-only state snapshot for the UI layer ──────────────────────────────
  function getState() {
    return { mode: _state.mode, active: _state.active, history: _state.history.slice() };
  }

  return { startSession, sendMessage, canUseMode, getRemainingUses, getState, formatText: _formatText, MODES, FREE_LIMITS, reset };
})();

// ══════════════════════════════════════════════════════
// MODULE: ErrorBoundary
// Wraps all page renders. Shows a friendly recovery screen
// instead of a blank app on unhandled render errors.
// ══════════════════════════════════════════════════════
App.ErrorBoundary = (() => {
  function wrap(fn, fallbackMsg = '') {
    try {
      return fn();
    } catch (err) {
      console.error('[RE Mastery Hub] Render error:', err);
      App.Analytics.track('render_error', { message: err.message, page: STATE.page });
      return `<div class="card p20 tc" style="border:2px solid var(--red)">
        <div class="fw7 mb8" style="color:var(--red)">Something went wrong</div>
        <div class="c-muted f-sm mb16 lh-16">An unexpected error occurred on this screen. Your progress is safe. ${fallbackMsg}</div>
        <button class="btn btn-navy btn-sm" onclick="openPage('home')">Return to Home</button>
      </div>`;
    }
  }

  /** Top-level global error handler */
  function install() {
    window.onerror = (msg, src, line, col, err) => {
      console.error('[RE Mastery Hub] Global error:', msg, err);
      App.Analytics.track('global_error', { message: msg, line });
      // Only show full error screen for critical failures, not render glitches
      return false; // don't suppress browser default handling
    };
    window.onunhandledrejection = (e) => {
      console.error('[RE Mastery Hub] Unhandled promise rejection:', e.reason);
    };
  }

  return { wrap, install };
})();

// ══════════════════════════════════════════════════════
// APP STATE
// Flat mutable object — validated on read, never directly
// set from untrusted sources.
// ══════════════════════════════════════════════════════
let STATE = {
  page:        'home',
  isPro:       false,
  quiz:        null,   // active quiz session (in-memory only)
  mock:        null,   // active sim session (in-memory only)
  obStep:      0,
  obData:      { exam: -1, institution: -1, examDate: '', daysLeft: 0 },
  disAccepted: false,
};

// Timer handles — module-level to prevent leaks
let _cdTimer  = null;
let _simTimer = null;
function clearCdTimer()  { if (_cdTimer)  { clearInterval(_cdTimer);  _cdTimer  = null; } }
function clearSimTimer() { if (_simTimer) { clearInterval(_simTimer); _simTimer = null; } }

// ══════════════════════════════════════════════════════
// QUESTION BANKS (immutable source of truth)
// Phase 2: load from external JSON files for better caching
// and faster initial parse.
// ══════════════════════════════════════════════════════
const QB_RE5 = Object.freeze([
{t:"Scope & Definitions",lv:"K",q:"What is the full name of the FAIS Act and when did it come into full effect?",o:["Financial Advisory and Intermediary Services Act — effective 30 September 2004","Financial Advisory and Insurance Services Act — effective 1 January 2003","Financial Analysis and Investment Services Act — effective 1 April 2005","Financial Audit and Information Standards Act — effective 1 November 2002"],a:0,e:"FAIS = Financial Advisory and Intermediary Services Act (37 of 2002). Promulgated November 2002; came into full effect 30 September 2004."},
{t:"Scope & Definitions",lv:"C",q:"For a transaction to fall within the scope of the FAIS Act, which three elements must all be simultaneously present?",o:["A financial service, a financial product, and a client","A licensed FSP, a registered representative, and a product supplier","An FSP, a key individual, and a compliance officer","A financial service, a client, and a key individual"],a:0,e:"FAIS applies when ALL three co-exist: (1) a financial service is rendered, (2) in relation to a financial product, (3) to a client."},
{t:"Scope & Definitions",lv:"AP",q:"Nomsa works at an insurance call centre reading from an approved script without deviating or making recommendations. Which statement best classifies her activity?",o:["She is rendering advice because she engages with clients about financial products during every call","She is not rendering a regulated financial service — scripted factual presentations without recommendations are excluded","She is rendering an intermediary service because she facilitates contact between the insurer and clients","She must register as a supervised representative before any client-facing telephone work"],a:1,e:"Presenting factual product information without any recommendation, guidance, or proposal is excluded from the FAIS definition of advice. Nomsa's scripted activity without deviation or recommendation is not a regulated financial service."},
{t:"Scope & Definitions",lv:"AN",q:"Zinhle captures loan applications and inputs client data without advising. Sipho analyses clients' positions and recommends home loan products. Which analysis is correct?",o:["Only Sipho renders a financial service — Zinhle's clerical function is excluded from FAIS","Both render a regulated financial service — clerical staff are exempt only when not client-facing","Both are exempt from FAIS because they operate under the registered bank's group licence","Zinhle renders an intermediary service by processing applications; Sipho renders regulated advice"],a:0,e:"Clerical administrative functions not involving judgment or specific financial transactions are excluded from FAIS. Sipho analyses and recommends — this is advice requiring authorisation."},
{t:"Scope & Definitions",lv:"AP",q:"Bongani tells his client: 'Your endowment is underperforming. I recommend surrendering it and redirecting premiums into a balanced unit trust.' What FAIS classification applies?",o:["An intermediary service — he is facilitating a product transaction between the insurer and the client","Advice — it is a recommendation about specific financial products and a proposed course of action","Regulated by the National Credit Act since redirecting premiums involves loan product obligations","Excluded from FAIS because the recommendation was delivered informally without a written record"],a:1,e:"Bongani's statement constitutes advice — a recommendation of a financial nature about a specific financial product. The informal delivery medium does not change the classification."},
{t:"Scope & Definitions",lv:"C",q:"An FSP collects insurance premiums from clients and forwards them to the insurer. This activity is best classified as:",o:["Advice, because the FSP engages with clients about their premium obligations directly","A financial needs analysis, because collecting premiums requires assessing ongoing client affordability","An excluded administrative function that falls outside regulated financial service activity","An intermediary service — any act other than advice between a product supplier and a client"],a:3,e:"Collecting and forwarding premiums is an intermediary service — any act other than advice performed between a product supplier and a client in relation to a financial product."},
{t:"Scope & Definitions",lv:"K",q:"The FAIS Act's two primary objectives are:",o:["Maintain trust in the financial services industry and maintain systemic stability","Regulate investment returns across all product classes and ensure product supplier accountability","License all FSPs and ensure regulatory levies are collected and remitted to the FSCA","Prevent financial crime across the sector and prosecute fraudulent advisers and intermediaries"],a:0,e:"The two FAIS objectives: (1) maintain trust in the financial services industry, and (2) maintain systemic stability."},
{t:"FAIS Role Players",lv:"K",q:"An FSP must appoint a compliance officer when:",o:["It has more than 5 representatives registered on the FSCA's representative register","There is more than one KI OR more than one representative","It advises on investment products requiring Class of Business training","It earns more than R1 million in gross annual commission across all product lines"],a:1,e:"A compliance officer is required when the FSP has more than one appointed KI OR more than one appointed representative."},
{t:"FAIS Role Players",lv:"C",q:"The compliance officer's primary statutory function is to:",o:["Manage the day-to-day provision of financial services and handle client escalations","Approve the appointment of new representatives and countersign all mandate documents","Monitor and report on the FSP's compliance with FAIS and related legislation","Debar non-compliant representatives and notify clients of each debarment decision"],a:2,e:"The CO monitors and reports on compliance — submitting annual reports to the FSCA. The KI establishes the compliance function; the CO monitors it."},
{t:"FAIS Role Players",lv:"AP",q:"Who is responsible for ESTABLISHING and MAINTAINING the compliance function within an FSP?",o:["The key individual","The compliance officer","The FSCA","The board of directors"],a:0,e:"The KI establishes and maintains the compliance function. The CO monitors it. The KI carries ultimate compliance accountability."},
{t:"FAIS Role Players",lv:"AN",q:"Sipho is simultaneously the appointed KI and an active representative at his FSP. Which statement is correct?",o:["He holds a dual role — as KI he manages and oversees; as representative he renders services, and both duties apply simultaneously","A KI at an FSP is not permitted to simultaneously hold an active representative mandate under any circumstances or FSP structure","His KI management obligations are legally paused during any client meetings in which he is personally acting as a representative","A sole proprietor operating as both KI and representative must choose only one of these two roles under the FAIS Act framework"],a:0,e:"A natural person may simultaneously be both KI and representative. Both sets of obligations apply simultaneously."},
{t:"FAIS Role Players",lv:"C",q:"If a representative causes a client financial loss, who bears primary legal responsibility?",o:["The representative personally — an FSP's liability is limited to direct supervisory failures","The FSP, which is liable for its representatives' conduct","The FSCA — it compensates clients from a dedicated financial services protection fund","The compliance officer — as the designated oversight person, they assume liability for representative conduct"],a:1,e:"Representatives act on behalf of the FSP. The FSP bears responsibility for its representatives' actions and omissions."},
{t:"FAIS Role Players",lv:"K",q:"A Key Individual under the FAIS Act must be:",o:["A juristic person such as a company","A registered compliance officer","A natural person only — cannot be a company or group","A senior employee with at least 10 years experience"],a:2,e:"A KI can ONLY be a natural person. A juristic entity cannot be appointed as a key individual."},
{t:"General Code of Conduct",lv:"K",q:"The four general duties under the General Code of Conduct require FSPs to act:",o:["With a profit motive that must always be balanced against basic duties of fairness, care, and honest dealing","Honestly and fairly; with due skill, care and diligence; in clients' interests; and for the integrity of the industry","Transparently, profitably, compliantly, and with client satisfaction as the FSP's overarching primary business objective","Honestly, independently, fairly, and efficiently while maintaining only the minimum required FSCA conduct standards"],a:1,e:"The four general duties: (1) honestly and fairly; (2) with due skill, care, diligence; (3) in clients' interests; (4) in the interest of the integrity of the financial services industry."},
{t:"General Code of Conduct",lv:"C",q:"The advice process under the General Code must follow which correct sequence?",o:["Gather information → Needs analysis → Identify suitable product → Replacement disclosures","Identify product → Gather information → Needs analysis → Replacement disclosures","Needs analysis → Gather information → Identify product → Provide advice","Replacement disclosures → Gather information → Needs analysis → Identify product"],a:0,e:"Correct sequence: (1) Gather information; (2) Conduct needs analysis; (3) Identify a suitable product; (4) Address replacement disclosures if applicable."},
{t:"General Code of Conduct",lv:"AP",q:"Themba recommends a more expensive product to Mrs Mokoena despite knowing an equivalent product is 20% cheaper. He does this for higher commission. Which principle has he most clearly violated?",o:["The duty to provide written confirmation of advice within 30 days of the client meeting","The duty to disclose his FSP licence number and authorised product categories","The duty to act in the interests of clients above his own financial interests","The duty to retain records of every recommendation for a minimum of five years"],a:2,e:"Recommending a more expensive product for higher commission — when an equivalent cheaper option exists — breaches the duty to act in clients' interests and constitutes an undisclosed conflict of interest."},
{t:"General Code of Conduct",lv:"C",q:"A Record of Advice must contain which elements?",o:["Only the product name, the agreed premium amount, and the policy start date are required","The client's tax reference number, bank account details, and next-of-kin contact information","A summary of the basis of advice, a list of products considered, and reasons why the recommendation is suitable","The representative's qualifications, current CPD compliance status, and full commission schedule"],a:2,e:"A proper ROA must contain: (1) a summary of information the advice was based on; (2) a list of products considered; (3) reasons why the recommended product is suitable."},
{t:"General Code of Conduct",lv:"AN",q:"Priya receives R8,500 cash from a client as an insurance premium. She is a registered representative of Coastal Financial Services. What is the correct action?",o:["Deposit into her personal account temporarily and transfer within three days","Deposit into the FSP's designated separate client funds account within one business day","Obtain written approval from the key individual before depositing","The client must pay directly to the product supplier — no cash may be accepted"],a:1,e:"Client funds received directly must be deposited into the FSP's designated separate client funds account within ONE business day. Using a personal account, even temporarily, is a serious breach."},
{t:"General Code of Conduct",lv:"AP",q:"Sandile receives R1,800 worth of gifts from a single product supplier in one calendar year. What is the correct position?",o:["Within the immaterial benefit threshold of R2,000 annually — no conflict declaration is needed","The benefit exceeds the R1,000 annual threshold from a single third party and must be disclosed as a conflict of interest","Must notify the FSCA of all third-party benefits but is not required to disclose this to the client","May accept benefits up to R5,000 annually from any single third party without formal disclosure"],a:1,e:"The immaterial financial interest threshold is R1,000 per calendar year from a single third party. R1,800 exceeds this and must be disclosed in the COI policy and to the client."},
{t:"General Code of Conduct",lv:"C",q:"When advising a client to replace a long-term insurance policy, by when must the representative notify the original insurer?",o:["Within 30 calendar days of the new policy being issued and accepted by the insurer","As soon as possible, but no later than when the new application is submitted","Within 10 business days after the client signs the new application and first premium is paid","Only if the original policy is formally cancelled and a refund of unused premium is due"],a:1,e:"Notification of the original insurer must occur as soon as possible, but no later than when the transaction instruction (new application) is submitted."},
{t:"General Code of Conduct",lv:"K",q:"Which practice is specifically prohibited by the General Code of Conduct?",o:["Providing a comparative analysis of two competing products","Asking a client to sign a blank or partially completed form","Charging different fees to different clients","Confirming oral disclosures in writing within 30 days"],a:1,e:"The Code specifically prohibits asking or inducing a client to sign any blank or partially completed document."},
{t:"General Code of Conduct",lv:"C",q:"The COI Management Policy must be reviewed at what minimum frequency?",o:["Every three years","Every six months","Annually (once a year)","Every five years"],a:2,e:"The COI policy must be reviewed at least annually and published for public inspection at all reasonable times."},
{t:"General Code of Conduct",lv:"AP",q:"Negative option marketing refers to:",o:["Marketing campaigns that deliberately target financially vulnerable or high-risk client groups","Advertising strategies that focus on competitor product weaknesses to generate market share","High-pressure sales techniques designed to prevent clients from making a genuinely free decision","Arrangements where a product activates automatically unless the client explicitly declines"],a:3,e:"Negative option marketing — where a product comes into force automatically unless the client opts out — is specifically prohibited by the Code."},
{t:"General Code of Conduct",lv:"K",q:"Churning in financial services refers to:",o:["Advising clients to replace financial products unnecessarily to generate commission","Rapidly growing a client's investment portfolio through frequent buying and selling of assets","Reviewing all client policies on a scheduled annual basis to ensure products remain suitable","Increasing total premium collections through structured upselling of additional product coverage"],a:0,e:"Churning = advising clients to switch or replace products not for client benefit, but to generate commission for the adviser. Prohibited."},
{t:"General Code of Conduct",lv:"C",q:"A complaint under the General Code is defined as:",o:["An expression of dissatisfaction alleging non-compliance, maladministration, or unfair treatment","Any question or concern raised by a client about the quality or suitability of a financial product","A general client enquiry regarding the FSP's fee structure, commission rates, or cost disclosure","A formal written request by a client to initiate payment under the terms of an insurance policy"],a:0,e:"A complaint = expression of dissatisfaction alleging: contravention of or failure to comply with law/agreement; maladministration; or unfair treatment. A query for information is NOT a complaint."},
{t:"Fit & Proper Requirements",lv:"K",q:"The four pillars of fit and proper requirements are:",o:["Honesty and integrity, competence, operational ability, and financial soundness","Qualifications, years of experience, CPD completion, and passing the RE examination","Ethics, product-specific knowledge, supervision history, and compliance track record","Character assessment, demonstrated skill, professional diligence, and independence from conflicts"],a:0,e:"The four fit and proper pillars: (1) honesty and integrity; (2) competence; (3) operational ability; (4) financial soundness."},
{t:"Fit & Proper Requirements",lv:"C",q:"The Date of First Appointment (DOFA) is defined as:",o:["The date the representative first passed the RE5 regulatory examination","The date the representative was officially registered with the FSCA for a specific scope of authorisation","The date the representative first entered employment in the financial services industry","The date the FSP formally submitted the appointment notification to the FSCA"],a:1,e:"DOFA = the date officially registered with the FSCA for a specific scope. It drives ALL competency deadlines."},
{t:"Fit & Proper Requirements",lv:"AP",q:"Kabelo's DOFA is 1 June 2023 for Long-term Insurance Subcategory C. By when must he complete RE5?",o:["1 June 2024 — within 12 months","He is exempt — LT Insurance C is Tier 2","1 June 2025 — within 24 months","1 June 2026 — within 36 months"],a:2,e:"RE5 must be completed within 24 months of DOFA. Kabelo's deadline is 1 June 2025. LT Insurance Subcategory C is NOT Tier 2."},
{t:"Fit & Proper Requirements",lv:"AP",q:"Jerome works in two classes of business with multiple subclasses. How many CPD hours per annual cycle?",o:["6 hours","18 hours","12 hours","24 hours"],a:1,e:"CPD hours: 6h (1 class/1 sub) | 12h (1 class/2+ sub) | 18h (2+ classes). Jerome = 18 hours."},
{t:"Fit & Proper Requirements",lv:"AN",q:"Wanda was declared insolvent three years ago, has not been rehabilitated, and has continued rendering services without disclosure. Which fit and proper pillar is most directly compromised?",o:["Honesty and integrity only — undisclosed insolvency is treated as a disclosure failure","Financial soundness — and her non-disclosure separately undermines honesty and integrity","Competence only — insolvency affects cognitive reliability and professional judgment","Operational ability only — insolvency prevents her from properly servicing client accounts"],a:1,e:"Unrehabilitated insolvency directly fails financial soundness. Non-disclosure separately breaches honesty and integrity. The FSP must take immediate action."},
{t:"Fit & Proper Requirements",lv:"C",q:"Which representative is exempt from BOTH RE5 and Class of Business Training?",o:["A representative working exclusively in Tier 2 product categories","A representative working in LT Insurance Subcategory C","A representative whose DOFA was before 1 April 2018 who was fully fit and proper at that date","A representative who holds a postgraduate financial qualification"],a:0,e:"Tier 2-only representatives are exempt from RE5 AND COB Training. But no representative is exempt from Product Specific Training."},
{t:"Fit & Proper Requirements",lv:"AP",q:"Lindiwe's DOFA was before 1 April 2018 and she was fully fit and proper at that date. Which requirement does she still have to meet?",o:["Class of Business Training — transitional provisions allow exemptions for qualifying DOFAs","The recognised formal qualification requirement at NQF level 4 or higher","Product Specific Training — no exemptions exist for PST","All RE5 examination requirements must be repeated regardless of prior completion"],a:2,e:"Representatives with pre-2018 DOFAs who were fully F&P are exempt from COB Training. Product Specific Training has NO exemptions."},
{t:"Fit & Proper Requirements",lv:"AP",q:"Financial soundness as a fit and proper requirement does NOT apply to:",o:["Representatives","Key individuals","Compliance officers","Financial Services Providers"],a:1,e:"Financial soundness applies to FSPs, representatives, and compliance officers — NOT to key individuals."},
{t:"Supervision & Debarment",lv:"K",q:"Before a supervised representative may begin rendering financial services, what must be in place?",o:["A verbal undertaking confirmed by email between the key individual and the representative","A written supervision agreement between the FSP and the supervised representative","A signed acknowledgement from all clients who will be served during the supervision period","A formal FSCA approval letter authorising supervised practice for the specific representative"],a:1,e:"A written supervision agreement must be established BEFORE any financial services are rendered under supervision."},
{t:"Supervision & Debarment",lv:"C",q:"What must a supervised representative disclose to every client before rendering services?",o:["Their full academic qualifications, DOFA, and current scope of product authorisation","Their supervisor's full name, designation, and direct contact telephone number","That financial services are being rendered under supervision","Their DOFA and the anticipated date on which their supervised period will conclude"],a:2,e:"A supervised representative must proactively disclose to each client, before any engagement, that services are rendered under supervision."},
{t:"Supervision & Debarment",lv:"C",q:"What are the three valid grounds for debarment under FAIS?",o:["Does not meet fit and proper requirements; materially contravened FAIS; aided or abetted another to contravene FAIS","Failing sales targets, receiving excessive client complaints, or poor annual performance reviews","Voluntary resignation from the FSP, dismissal for cause, or a breach of the employment contract","Dishonesty, fraud, or a criminal conviction regardless of its connection to financial services"],a:0,e:"The three exclusive grounds: (1) not meeting F&P requirements; (2) material FAIS contravention; (3) aiding/abetting another's FAIS contravention."},
{t:"Supervision & Debarment",lv:"AP",q:"The correct three-step debarment process is:",o:["Investigation → immediate suspension → submission to FSCA for final determination","Give adequate notice → consider representations → take and communicate a decision","The FSCA initiates all debarment proceedings and notifies the FSP of the outcome","Suspension → internal disciplinary hearing → formal FSCA notification within 30 days"],a:1,e:"Step 1: Give adequate notice of intention and grounds. Step 2: Consider representations. Step 3: Take and communicate a written decision."},
{t:"Supervision & Debarment",lv:"AP",q:"After completing a debarment, the correct FSCA reporting timelines are:",o:["Part I within 5 days and Part II within 15 days of the debarment date","Part I within 15 days and Part II within 30 days","A single comprehensive report within 10 business days","Notification only if the representative appeals"],a:0,e:"Part I within 5 days; Part II within 15 days. Representative register updated within 15 days."},
{t:"Supervision & Debarment",lv:"AN",q:"Karen was debarred for dishonesty eight months ago and claims she has completed remediation. May a new FSP appoint her?",o:["Yes — a completed remediation programme is sufficient to satisfy the re-appointment requirement","No — a minimum of 12 months must elapse from the debarment date for dishonesty-based debarment","No — dishonesty-related debarments carry a mandatory five-year exclusion period","Yes — she may be appointed as a supervised representative while the waiting period runs"],a:1,e:"Dishonesty-based debarment carries a mandatory minimum of 12 months before reappointment. Only 8 months have elapsed."},
{t:"Supervision & Debarment",lv:"AP",q:"Mpho was debarred because he failed to complete RE5 within 24 months. When may he be reappointed?",o:["As soon as he fulfils the fit and proper requirements — no mandatory waiting period","After a mandatory 12-month waiting period measured from the date of debarment","After a mandatory 6-month waiting period once all outstanding requirements are met","After a minimum 24-month period equivalent to the original RE5 completion window"],a:0,e:"Debarment based on F&P failure (not dishonesty) carries no mandatory waiting period. Reappointment is permissible once requirements are met."},
{t:"FAIS Ombud",lv:"K",q:"The FAIS Ombud has jurisdiction over which category of disputes?",o:["All financial disputes between consumers and any registered financial institution in South Africa","Disputes about banking service failures such as unauthorised transactions or card fraud","Complaints about investment underperformance regardless of whether advice was given","Complaints about poor advice or intermediary services rendered by an FSP or representative"],a:3,e:"FAIS Ombud jurisdiction: poor advice or poor intermediary services. Not pure product performance (which goes to sectorial ombuds)."},
{t:"FAIS Ombud",lv:"C",q:"Before the FAIS Ombud will formally receive a complaint, which condition must first be satisfied?",o:["The complainant pays a non-refundable R1,000 filing fee before the complaint is registered","The complainant must first obtain an independent legal opinion confirming the complaint has merit","The FSP must have received the complaint and failed to adequately resolve it within six weeks","The FSCA must have completed a prior investigation and referred the matter to the Ombud"],a:2,e:"The FSP must have been given the opportunity to resolve the complaint and failed to do so adequately within six weeks."},
{t:"FAIS Ombud",lv:"C",q:"A FAIS Ombud determination carries the same legal force as:",o:["A High Court civil judgement","An FSCA directive","A mediation award requiring further court action","An arbitration award under the Arbitration Act"],a:0,e:"A FAIS Ombud determination = a High Court civil judgement. A writ of execution may follow non-compliance."},
{t:"FAIS Ombud",lv:"AP",q:"Mr Nkosi received the FSP's final response on 3 March 2024. What is the latest date he may submit his complaint to the Ombud?",o:["3 June 2024","There is no time limit","3 September 2024","3 March 2025"],a:2,e:"Complainants have six months from the FSP's final response. Six months after 3 March 2024 = 3 September 2024."},
{t:"FAIS Ombud",lv:"AN",q:"A determination requires RST Brokers to pay R4,200,000. The FSP argues this exceeds the R3,500,000 cap. What is the correct position?",o:["The R3,500,000 statutory cap was permanently removed in 2022 and replaced by an unlimited compensation framework","The cap applies unless RST consented in writing to exceed it or the complainant waived the excess beyond the cap","The cap is R5,000,000 for disputes involving financial advice rendered under Category I or II FSP licences specifically","The Ombud may exceed the cap and award any amount in cases where gross negligence or deliberate fraud has been proven"],a:1,e:"The R3,500,000 cap applies. Exceeding it requires the respondent's written consent or the complainant's waiver of the excess."},
{t:"FAIS Ombud",lv:"C",q:"The Ombud's process after accepting a complaint is:",o:["Notify all parties, allow responses, and first attempt conciliation or mediation before formal investigation","Issue an immediate binding determination on the basis of the written complaint alone","Refer the matter directly to the FSCA for a formal regulatory investigation and sanction","Require the complainant to lodge financial surety before the Ombud will take any formal step"],a:0,e:"The Ombud must notify parties, allow submissions, and first attempt conciliation/mediation before proceeding to formal investigation."},
{t:"FAIS Ombud",lv:"C",q:"A complaint is accepted for investigation by the FAIS Ombud. In respect of a case fee, the current position is that:",o:["The respondent FSP pays a non-refundable fee of up to R1,000","No case fee is charged, the fee having been discontinued in 2012","The complainant pays a non-refundable fee of up to R1,000","The fee is shared equally between the complainant and the FSP"],a:1,e:"The FAIS Ombud discontinued the case fee with effect from 10 September 2012. Rule 9(a) of the Rules of Proceedings previously permitted a discretionary fee of up to R1,000 charged to the respondent once a matter was accepted for investigation, but it is no longer levied on anyone. Much older study material still quotes the R1,000 figure, which is why it remains a common distractor."},
{t:"FICA & AML",lv:"K",q:"The three stages of money laundering in correct order are:",o:["Integration → Layering → Placement","Layering → Placement → Integration","Structuring → Concealment → Conversion","Placement → Layering → Integration"],a:3,e:"(1) Placement — introducing illegal funds; (2) Layering — obscuring the trail; (3) Integration — reintroducing funds as legitimate assets."},
{t:"FICA & AML",lv:"C",q:"A suspicious transaction report must be submitted to the FIC within:",o:["3 business days, matching the Cash Threshold Report deadline","10 days, excluding weekends and public holidays","15 days, excluding weekends and public holidays","30 days, excluding weekends and public holidays"],a:2,e:"STRs must be submitted to the FIC as soon as possible and no later than 15 days, excluding weekends and public holidays (Reg 24(3)). The 3-business-day period applies to Cash Threshold Reports, not STRs of becoming aware of the suspicious transaction."},
{t:"FICA & AML",lv:"AP",q:"Precious suspects a client's transaction involves proceeds of crime. She is worried about alerting the client. What does FICA require?",o:["Report to SAPS within 24 hours of suspicion, and she may inform the client that an investigation is underway","Report only if the transaction amount exceeds the R49,999 cash threshold reporting limit for the month","Submit an STR to the FIC within 15 days (excluding weekends and public holidays); she must not tip off the client about the report","Place the transaction on hold for 30 days and obtain external legal advice before filing any report"],a:2,e:"FICA requires an STR within 15 days (excluding weekends and public holidays) AND prohibits tipping off — disclosing to the client that a report has been or will be submitted. Tipping off is a criminal offence."},
{t:"FICA & AML",lv:"AP",q:"A client makes three cash deposits of R18,000, R16,000, and R20,000 over three weeks apparently structured to stay below thresholds. What obligation does this create?",o:["File an STR — structuring transactions to avoid reporting thresholds is itself suspicious activity","No reporting required — each individual deposit is below the R49,999 reporting threshold","Report only the final deposit that caused the cumulative total to cross the R49,999 threshold","Refer the matter to the FSCA rather than the FIC as the responsible oversight body"],a:0,e:"Structuring (smurfing) to avoid reporting thresholds is suspicious activity requiring an STR, regardless of individual transaction amounts."},
{t:"FICA & AML",lv:"AN",q:"A trusted long-standing client requests a R3.8 million transfer to a banking secrecy jurisdiction with no business rationale. The account manager trusts the client. What should happen?",o:["File an STR — personal familiarity does not override FICA obligations","No report is required — the long-standing relationship and trust override the reporting duty","Report the transaction to SAPS and await their formal instruction before proceeding","Freeze the account immediately and notify the FSCA as the primary responsible authority"],a:0,e:"Personal familiarity does not override FICA's STR obligation. The combination of a sudden large transfer, unusual jurisdiction, and absence of business rationale creates clear grounds for an STR."},
{t:"FICA & AML",lv:"K",q:"Cash transactions exceeding which amount must be reported to the FIC?",o:["R25,000","R35,000","R100,000","R49,999.99"],a:3,e:"Any cash transaction exceeding R49,999.99 must be reported to the FIC via a Cash Threshold Report."},
{t:"FICA & AML",lv:"C",q:"'Tipping off' under FICA refers to:",o:["Disclosing to a client or anyone that an STR has been or will be submitted about them","Paying a client a referral fee without the required conflict of interest disclosure","Alerting SAPS to a suspected criminal matter before filing the required STR with the FIC","Voluntarily providing the FIC with additional supporting evidence after an STR is filed"],a:0,e:"Tipping off = disclosing to the subject (or any other person) that an STR has been or is about to be submitted. Criminal offence."},
{t:"FICA & AML",lv:"C",q:"FICA records relating to a business relationship must be retained for at least:",o:["3 years after the date of the most recent transaction within the client relationship","5 years after termination of the product or financial service","7 years after the end of the financial year in which the product or service was provided","Indefinitely for investment products, pension funds, and retirement annuity contracts"],a:1,e:"5 years from the date the business relationship is terminated; transaction records for 5 years from the transaction date."},
{t:"FICA & AML",lv:"AP",q:"A Politically Exposed Person applies to open an investment account. Which ECDD obligations apply?",o:["No additional obligations are required — a valid South African ID is sufficient verification","Flag the account on the internal risk register and monitor quarterly for unusual activity","PEPs are prohibited from opening any investment or savings accounts under FICA restrictions","Obtain senior management approval; determine source of wealth and funds; conduct enhanced ongoing monitoring"],a:3,e:"PEP ECDD requirements: senior management approval; determine source of wealth and funds; enhanced ongoing monitoring throughout the relationship."},
{t:"TCF",lv:"K",q:"How many TCF outcomes does the FSCA prescribe?",o:["3","4","6","5"],a:2,e:"The FSCA prescribes SIX TCF outcomes covering the entire product and service lifecycle."},
{t:"TCF",lv:"K",q:"TCF Outcome 1 states that customers must be confident that:",o:["Products will always meet the financial expectations set by every client they are sold to","All financial advice given by the FSP will be technically correct and free from conflicts","Products will always perform exactly as described in all pre-sale marketing materials","Fair treatment of customers is central to the firm's corporate culture"],a:3,e:"TCF Outcome 1: customers are confident they deal with firms where fair treatment is central to and embedded in the corporate culture."},
{t:"TCF",lv:"C",q:"TCF Outcome 4 relates to:",o:["Product design and target market selection","Clear and timely communication throughout the product lifecycle","Suitability of advice to individual client circumstances","No post-sale barriers"],a:2,e:"TCF Outcome 4: where clients receive advice, it is suitable and accounts for individual circumstances."},
{t:"TCF",lv:"AP",q:"A client was not informed about a 25% early surrender penalty before purchase. The FSP says it provided the full policy document. Under TCF, how is this assessed?",o:["The FSP has fully complied — delivering any signed disclosure document satisfies all FAIS and TCF requirements","TCF Outcome 4 applies only to financial advice quality, not to post-sale policy or disclosure documentation","TCF Outcome 4 requires genuinely clear communication — burying key costs in a dense document fails this standard","The client accepted all associated charges and penalties by signing the application form"],a:2,e:"TCF requires genuinely meaningful communication — not just technical compliance. A 25% surrender penalty must be communicated clearly and accessibly before commitment."},
{t:"TCF",lv:"C",q:"TCF Outcome 6 protects customers from:",o:["Poor investment returns caused by market volatility and adverse economic conditions","Unsuitable product recommendations made at or before the point of sale","Unreasonable post-sale barriers to switching, claiming, or complaining","Misleading advertising and inaccurate or incomplete pre-sale product descriptions"],a:2,e:"TCF Outcome 6: customers do not face unreasonable post-sale barriers to change products, switch providers, submit claims, or make complaints."},
{t:"Insurance Principles",lv:"K",q:"Insurable interest requires that:",o:["The insured must stand to suffer a direct financial loss if the insured event occurs","The insured must earn rental income, dividends, or interest payments from the insured asset","The insurer must disclose its profit margin and all underwriting criteria before issuing","The insured must pay a premium proportionate to the market value of the insured asset"],a:0,e:"Insurable interest: the policyholder must stand to suffer a direct financial loss if the insured event occurs. Without it, the contract is void."},
{t:"Insurance Principles",lv:"C",q:"Utmost good faith (uberrimae fidei) in insurance requires:",o:["Only the insurer to act in good faith — the insured's duty is limited to answering questions","Both insurer and insured to honestly and fully disclose all material facts","Only the insured to disclose material facts — the insurer has no equivalent reciprocal duty","Only the facts appearing on the standard printed application form to be disclosed"],a:1,e:"Utmost good faith is a mutual obligation — both parties must honestly disclose all material facts. Non-disclosure by either party can render the policy voidable."},
{t:"Insurance Principles",lv:"C",q:"The principle of indemnity in short-term insurance means:",o:["The insurer always pays the full sum insured regardless of the insured's actual financial loss","The insured is restored to their pre-loss financial position — no more and no less","The insured receives a full premium refund if no valid claims are made during the policy period","All losses are fully covered regardless of the cause, exclusion, or policy conditions that apply"],a:1,e:"Indemnity restores the insured to their pre-loss position — no profit from an insurance claim. Prevents moral hazard."},
{t:"Insurance Principles",lv:"C",q:"Subrogation in insurance means:",o:["The insurer's right to raise the policyholder's premium after a large claim has been settled","The policyholder's statutory right to cancel within the prescribed cooling-off period","The right to transfer an insurance policy to a new owner without requiring insurer consent","The insurer takes over the insured's legal rights against the responsible third party after paying a claim"],a:3,e:"Subrogation: after paying a claim, the insurer steps into the insured's shoes to recover from the liable third party. Prevents double recovery."},
{t:"Insurance Principles",lv:"AP",q:"Long-term Insurance Subcategory A applies to policies with a maximum benefit of:",o:["Less than R30,000","Less than R10,000","Less than R50,000","No monetary limit"],a:0,e:"LT Insurance Subcategory A (typically funeral policies) covers policies with maximum benefit less than R30,000. This is a Tier 2 category."},
{t:"Investments & Financial Planning",lv:"K",q:"The Rule of 72 in financial planning estimates:",o:["The 72-hour notice period required before an investor may redeem units from a fund","A statutory regulation requiring all personal loans to have a maximum 72-month repayment term","Approximate years to double an investment: 72 ÷ annual interest rate","A TCF compliance benchmark requiring 72% of all clients to confirm product satisfaction annually"],a:2,e:"Rule of 72: 72 ÷ annual rate of return ≈ years to double an investment. E.g. 72 ÷ 9% = ~8 years."},
{t:"Investments & Financial Planning",lv:"C",q:"The time value of money principle states that:",o:["A rand today is worth more than the same rand in the future due to its earning potential and purchasing power","Money loses real value exclusively because of the taxation imposed on all investment returns","All investment returns are entirely determined by prevailing central bank interest rate policy","Inflation affects only physical goods and commodities — not financial instruments or portfolios"],a:0,e:"Money available today is worth more than the same amount in the future — because of earning potential and inflation's eroding effect."},
{t:"Investments & Financial Planning",lv:"C",q:"Inflation risk in financial planning refers to:",o:["The risk of losing capital when financial markets fall and portfolio values decline sharply","The risk that rising central bank interest rates will reduce the market value of bonds","The risk that unexpected tax rate increases will significantly erode net after-tax investment returns","The risk that an investment's returns will not keep pace with inflation, reducing real purchasing power"],a:3,e:"Inflation risk: nominal returns fail to match inflation, resulting in a net loss of real purchasing power over time."},
{t:"POPIA & Data Protection",lv:"K",q:"POPIA primarily requires FSPs that collect client data to:",o:["Retain all personal data indefinitely provided it is encrypted and access controls are in place","Share personal data freely within all related group entities to enhance service delivery","Store all personal information exclusively on servers physically located within South Africa","Process personal information responsibly, lawfully, for specific defined purposes, with appropriate security"],a:3,e:"POPIA requires: lawful collection; specific, defined purpose; limited processing; accurate and secure storage; consent where required; no retention beyond necessity."},
{t:"Regulatory Compliance",lv:"K",q:"An FSP must retain all records for a minimum of:",o:["3 years after the date of the most recent client transaction in the relationship","5 years after termination of the product or financial service","7 years after the end of the financial year in which the product was issued","Indefinitely for all investment products, retirement funds, and long-term policies"],a:1,e:"FAIS requires records kept for at least 5 years after termination of the product or financial service. Records must be retrievable within 7 days."},
{t:"Regulatory Compliance",lv:"C",q:"An FSP must submit annual financial statements to the FSCA within:",o:["3 months of financial year-end","6 months of financial year-end","4 months of financial year-end","12 months of financial year-end"],a:2,e:"Financial statements must be submitted to the FSCA within 4 months of the FSP's financial year-end."},
{t:"Regulatory Compliance",lv:"AP",q:"The FSCA may suspend an FSP licence WITHOUT giving prior notice when:",o:["The FSP has failed to submit its annual compliance report within the prescribed timeframe","The FSP has not formally appointed a replacement key individual within the 15-day notification period","The FSP's audited annual financial statements are more than 90 calendar days overdue","There are reasonable grounds to believe failure to suspend immediately would prejudice clients or public interest"],a:3,e:"Urgent suspension without prior notice: only where immediate action is necessary to prevent substantial client or public prejudice."},
{t:"Ethics & Professional Conduct",lv:"K",q:"Insider trading involves:",o:["Trading unusually large volumes of shares on an exchange in a manner that moves the market price","Managing a discretionary client portfolio without seeking specific instructions for each trade","Trading securities during periods of high market volatility to exploit short-term price swings","Trading securities using material non-public information to gain an unfair advantage"],a:3,e:"Insider trading = using confidential, price-sensitive information not available to the public to trade in securities. Illegal under the Financial Markets Act."},
{t:"Ethics & Professional Conduct",lv:"AP",q:"A representative identifies a client need outside their competence and authorisation. They must:",o:["Advise anyway to avoid losing the client","Ignore the need and focus only on their authorised scope","Charge a referral fee for directing the client elsewhere","Refer the client to another suitably qualified and authorised person"],a:3,e:"If a need falls outside competence or licence, the representative is legally and ethically obligated to refer the client to an appropriately qualified professional."},
{t:"General Code of Conduct",lv:"AN",q:"Thandi, a representative at a large FSP, has been advising elderly clients to switch from conservative fixed deposits into high-risk unit trusts. She earns significantly higher commission on the unit trusts. Her manager has not questioned the pattern despite complaints from three clients' families. Which combination of Code breaches is MOST accurately identified?",o:["Thandi: conflict of interest only, with no suitability issue. Manager: supervision failure only, nothing further","Thandi: no breach at all if clients signed the ROA. Manager: breach only for ignoring the complaint pattern","Thandi: suitability failure plus undisclosed conflict of interest. Manager: inadequate supervision","Thandi: FICA breach for advising vulnerable elderly clients. Manager: TCF breach only, no supervision issue"],a:2,e:"Thandi breaches suitability (unsuitable advice driven by commission) and COI (undisclosed material interest). The manager breaches the supervision obligation — a pattern of complaints is a clear warning signal requiring immediate investigation."},
{t:"General Code of Conduct",lv:"AN",q:"A client provides false information about her financial position to obtain a higher risk product. The representative suspects the information is inaccurate but does not verify it. A loss occurs and the client complains. Which statement BEST reflects the regulatory position?",o:["The client bears full liability — she provided false information and signed the disclosure","The representative has no liability because the client signed the needs analysis document","The FSP is solely liable because it is responsible for all advice given by its representatives","The representative may share liability — there is a duty to take reasonable steps to verify material information"],a:3,e:"Representatives have a duty to take reasonable steps to obtain and verify material information. Suspecting inaccuracy and not acting creates co-liability. The client's dishonesty is a factor but does not fully excuse the representative."},
{t:"Fit & Proper Requirements",lv:"AN",q:"Sipho has been licensed for 4 years. His F&P qualification was obtained before the current standards came into effect. A new Fit & Proper Determination introduces higher qualification requirements. Which statement is correct?",o:["Sipho is permanently exempt — he is grandfathered under the old standard","Sipho must immediately cease rendering financial services until he meets the new requirements","Sipho must meet the new requirements within the transition period specified in the Determination","Sipho's existing qualification automatically upgrades to the new standard upon publication"],a:2,e:"New F&P Determinations specify transition timelines. Existing representatives must meet new requirements within those periods — they are not permanently exempt from regulatory evolution."},
{t:"FICA & AML",lv:"AN",q:"An FSP's FICA officer notices that a long-standing client with a modest salary has made three cash deposits of R48,000 each in the same week — just below the R49,999.99 reporting threshold. The officer suspects deliberate structuring. What is the MOST appropriate response?",o:["File a Suspicious Transaction Report immediately — structuring is a money laundering red flag","File a Cash Threshold Report for each transaction — they each fall below the threshold","Take no action — each transaction is below the legal reporting threshold","Escalate to senior management and wait for their decision before filing any report"],a:0,e:"Structuring — deliberately splitting transactions to stay below the reporting threshold — is itself a money laundering indicator. An STR must be filed immediately regardless of individual transaction amounts. Waiting for management approval may breach the obligation to report as soon as possible, and in any event within 15 days (excluding weekends and public holidays)."},
{t:"TCF",lv:"AN",q:"An FSP's management information shows that 70% of clients who purchased Product X have lapsed within 18 months, most citing unaffordability. The product was sold during a high-incentive campaign. Which TCF failures does this MOST likely indicate?",o:["Outcomes 2 and 4 — product not designed for the identified market and advice was unsuitable","Outcome 6 only — clients faced unreasonable barriers when trying to lapse or switch the product","Outcome 3 only — communication about product costs and risks was unclear or misleading to clients","Outcome 1 only — the FSP's internal culture was not sufficiently client-centred at senior level"],a:0,e:"A 70% early lapse rate on an incentive-driven campaign indicates Outcome 2 (product not designed for the identified consumer group) AND Outcome 4 (advice was not suitable). This is a systemic issue requiring root cause analysis and possible remediation of affected clients."},
{t:"Supervision & Debarment",lv:"AN",q:"A KI receives credible evidence that a representative has been forging client signatures on risk disclosure documents. The KI consults legal counsel and suspends the representative pending investigation. Ten days later she files Part I of the debarment notification. Has she acted correctly?",o:["Yes — consulting legal counsel justifies a delay beyond 5 days","Yes — the suspension demonstrates adequate interim action which extends the notification timeline","No — Part I must reach the FSCA within 5 days of the decision to debar regardless of investigations","No — the representative should have been debarred immediately without a suspension phase"],a:2,e:"Part I of the debarment notification must reach the FSCA within 5 days of the debarment decision. Consulting legal counsel does not extend this deadline. Suspension is appropriate interim action but does not replace or delay the notification obligation."},
{t:"FAIS Ombud",lv:"AN",q:"A client submits a complaint to the FAIS Ombud claiming unsuitable advice caused a R2,400,000 loss. The Ombud investigates and determines the complaint is valid. The FSP refuses to comply with the determination. What can the client do?",o:["Re-submit the complaint with additional evidence to get a higher award","Apply to the High Court to have the determination enforced as a civil judgement","Request the FSCA to criminally prosecute the FSP","The client has no further recourse — Ombud determinations are final but unenforceable"],a:1,e:"An unappealed FAIS Ombud determination has the force of a High Court civil judgement. The client may apply for a writ of execution. The FSP risks contempt of court for non-compliance."},
{t:"FAIS Role Players",lv:"AP",q:"GlobalBank instructs its advisors to recommend only GlobalBank-manufactured products. A client asks an advisor whether products from other providers would be more suitable. The advisor confirms GlobalBank's products are best without proper comparison. Which role player principle is MOST directly violated?",o:["The representative's duty to act in the client's best interest and provide appropriate advice","The product supplier's manufacturing standards under the Insurance Act","The FSCA's product approval process specifically governing tied agent arrangements","The financial institution's internal compliance policy only, not a statutory duty"],a:0,e:"A tied agent arrangement is permitted, but the representative still owes the client a duty to act in their best interest. Recommending a product without genuine suitability assessment violates the General Code even within a tied arrangement."},
{t:"Scope & Definitions",lv:"AP",q:"A financial wellness workshop presenter tells attendees: 'Based on what I am seeing in the market, people like you should be putting 15% of their salary into equity unit trusts.' No individual assessment is conducted. Is this advice under FAIS?",o:["Yes — the statement contains a specific asset allocation recommendation directed at an identifiable audience","No — general financial education and market commentary are excluded from the FAIS definition of advice","Yes — any statement that could influence a financial decision is advice under FAIS","No — the statement does not name a specific financial product or provider"],a:0,e:"Advice requires: (1) a recommendation, (2) about a financial product, (3) to a client. A specific allocation recommendation ('15% into equity unit trusts') directed at an identifiable group satisfies all three elements — this is advice, and the presenter must be licensed."},
{t:"General Code of Conduct",lv:"AP",q:"A representative is completing a needs analysis for a client who becomes impatient and says 'Just sell me what everyone else is buying — I trust you.' The representative skips the remaining questions and sells the most popular product. Which Code provision is MOST directly breached?",o:["The prohibition on negative option marketing","The obligation to conduct a full needs analysis before making any recommendation","The disclosure requirement for product fees and charges","The prohibition on rendering services outside authorised scope"],a:1,e:"Client impatience does not relieve the representative of the obligation to conduct a full needs analysis. A recommendation without adequate analysis of the client's specific circumstances is a direct breach of the General Code suitability requirement."},
{t:"POPIA & Data Protection",lv:"AP",q:"A representative uses a client's email address (collected for policy communication purposes) to send marketing material for a new product without obtaining separate consent. The client objects. Under POPIA, what is the correct position?",o:["The representative may continue — existing clients are deemed under FAIS to have consented to marketing contact","The client must formally opt out in writing before the representative is obliged to stop the marketing","This is permissible provided the marketing relates closely to a similar type of financial product","This is a POPIA breach — data collected for one purpose can't be reused for another without fresh consent"],a:3,e:"POPIA's purpose specification and further processing limitation conditions prohibit using personal information for purposes beyond those for which it was collected. Marketing communications require separate, explicit consent."},
{t:"Fit & Proper Requirements",lv:"AP",q:"Zanele has been debarred for dishonesty. After 14 months she applies to be reappointed as a representative. Her former FSP supports her application and states she has reformed. Can she be reappointed?",o:["Yes — the minimum 12-month waiting period has elapsed and her former FSP fully supports the application","No — the 12-month minimum is necessary but not sufficient; the FSP must still independently assess her fitness","No — a dishonesty debarment is a permanent, lifetime bar on any future reappointment as a representative","Yes — provided she passes the RE5 exam again and completes 6 months of closely supervised practice first"],a:1,e:"12 months is the MINIMUM waiting period — not an automatic qualification for reappointment. The FSP must independently assess whether the person now meets all fit and proper requirements including honesty and integrity. Time elapsed alone is insufficient."},
{t:"Scope & Definitions",lv:"AP",q:"A broker charges a client R5,000 to analyse their existing portfolio and prepare a written report with recommendations but takes no further action and earns no commission. Is this a financial service under FAIS?",o:["Yes — a recommendation about a financial product is advice under FAIS, regardless of the fee model used","No — FAIS only regulates transactions where a product is actually sold and a commission is earned by the adviser","No — a once-off analysis fee for portfolio review is specifically excluded from the FAIS definition of advice","Yes — but only once the client actually implements the recommendations given in the written report"],a:0,e:"Advice under FAIS is triggered by the nature of the recommendation — not the remuneration model. A fee-based recommendation about financial products is advice and requires appropriate licensing."},
{t:"Ethics & Professional Conduct",lv:"AP",q:"A representative becomes aware that a colleague is submitting false claims on behalf of clients and pocketing the difference. He does not report it because 'it's not his business.' What is his regulatory and ethical position?",o:["He has no obligation to act — only management and appointed compliance officers are required to report misconduct","His silence may amount to complicity and breach his duty of integrity — staying silent still facilitates the misconduct","He may be professionally obligated to report it, but faces no direct regulatory consequence for staying silent","He faces no consequence at all, provided he was not personally or directly involved in the fraudulent claims"],a:1,e:"Integrity as a fit and proper requirement is not passive. Knowingly remaining silent about colleague fraud can constitute a breach of the honesty and integrity requirement. Professional responsibility includes reporting material misconduct."},
{t:"FICA & AML",lv:"AP",q:"A client presents documents that appear genuine but the representative notices the photograph does not resemble the client. The client provides an explanation that seems plausible. What should the representative do?",o:["Refuse to proceed and file an STR — identity verification failure is a red flag regardless of explanation","Accept the documents — a plausible explanation satisfies the verification obligation","Proceed with the transaction and flag it in the next monthly compliance report","Accept the documents but apply enhanced due diligence for 6 months"],a:0,e:"Failure of identity verification — regardless of explanation — is a fundamental FICA red flag. The representative must not proceed and must file an STR. Proceeding after noting a discrepancy exposes both the representative and FSP to criminal liability."},
{t:"General Code of Conduct",lv:"AP",q:"A representative recommends a long-term insurance product to a client whose primary objective is liquidity for an emergency fund. The product has a 5-year lock-in period and early exit penalties. The client signed the ROA confirming she understood. A complaint is lodged 2 years later. What is the MOST likely regulatory finding?",o:["No breach — the signed ROA constitutes full informed consent and fully protects the representative from any complaint","Breach of disclosure only — the representative should have disclosed the exit penalties more prominently in writing","No breach — it is entirely the client's own responsibility to read and understand what she signs before agreeing","Breach of suitability — a 5-year lock-in is prima facie unsuitable for an emergency-fund goal, regardless of the ROA"],a:3,e:"A signed ROA does not cure an unsuitable recommendation. Recommending a 5-year illiquid product to a client whose stated objective is liquidity is a suitability failure. The ROA is evidence of disclosure but not of suitability."},
{t:"Regulatory Compliance",lv:"AP",q:"An FSP's annual compliance report reveals that 23% of client files reviewed are missing signed needs analyses. The FSCA requests an urgent meeting. What is the MOST accurate characterisation of the FSP's position?",o:["The FSP faces a documentation issue only — since no clients were harmed, no regulatory action is expected here","This is a systemic compliance failure that exposes the FSP to remediation orders and licence conditions","The FSP needs only to implement a corrective action plan going forward; historical files are not actionable","The compliance officer alone is personally liable for all of the missing documentation found in the review"],a:1,e:"Missing signed needs analyses means the FSP cannot demonstrate suitability for 23% of its advice. This is a systemic failure — not a documentation technicality. The FSCA may impose remediation obligations and licence conditions including enhanced supervision."},
{t:"TCF",lv:"AP",q:"An FSP introduces a new performance incentive that pays advisors 3x their normal commission if they exceed monthly sales targets. There is no cap or product mix requirement. A compliance officer raises a TCF concern. Which outcome is MOST at risk?",o:["Outcome 6 — post-sale barriers will increase significantly due to the resulting high product volumes","Outcome 3 — communication will become less clear as advisors focus more heavily on sales volume","Outcome 1 — the incentive structure undermines fair treatment as a cultural value across the FSP","Outcome 5 — product performance will deteriorate because of inappropriate placement decisions"],a:2,e:"An uncapped volume incentive with no product mix control creates systematic pressure to recommend products for commission rather than client benefit. This directly undermines TCF Outcome 1 — fair treatment as a cultural value. The FSCA has consistently identified incentive structures as a primary TCF risk."},
{t:"Supervision & Debarment",lv:"AP",q:"A representative under supervision renders a complex structured product recommendation without prior supervisor approval. The supervisor discovers this 3 days later. The recommendation was appropriate and the client is satisfied. How should the KI respond?",o:["Document the breach formally, assess whether supervision remains viable, and consider debarment if warranted","No formal action is needed at all since the advice given was suitable and the client remains satisfied","Issue a verbal warning only, since the outcome was positive so no formal action is technically required","Extend the supervision period by a further 90 days as a standard corrective measure applied in these cases"],a:0,e:"The outcome does not determine the regulatory response. Rendering services without supervisor approval is a serious breach of the supervision arrangement. The KI must formally document it, assess ongoing suitability of the arrangement, and consider debarment if the pattern continues."},
{t:"FAIS Role Players",lv:"AP",q:"A product supplier offers training, marketing support, and R500 gift vouchers to representatives who attend their product presentation. The vouchers are paid per representative, not disclosed to clients. Which statement is correct?",o:["The vouchers fall below the material threshold, so no disclosure to clients is required in this case","The vouchers are a financial interest that must be disclosed and managed under the FSP's COI policy","This is permitted — product suppliers may freely provide non-monetary support to representatives","The training and marketing support are the real issue here — not the value of the gift vouchers"],a:1,e:"R500 per representative from the same third party (product supplier) in the context of a product presentation is a financial interest. It exceeds the R1,000 annual immaterial threshold ONLY if aggregated with other interests from the same source. It must be disclosed in the ROA and managed in the COI policy."},
{t:"Fit & Proper Requirements",lv:"C",q:"A newly appointed representative has the required qualifications but has not yet written the RE5 exam. Her DOFA is recorded as 1 March 2026. By what date must she pass RE5?",o:["1 March 2027 — 12 months from DOFA","1 March 2028 — 24 months from DOFA","1 September 2026 — 6 months from DOFA","1 March 2032 — 6 years from DOFA"],a:1,e:"RE5 must be passed within 24 months of DOFA. This is a hard deadline — if missed, the representative can no longer render financial services. The 6-year deadline applies to qualification requirements, not RE5."},
{t:"FAIS Ombud",lv:"C",q:"A client suffered a financial loss of R450,000 due to unsuitable advice. She complains to the FSP on 1 February 2026. The FSP responds on 15 March 2026 rejecting her complaint. By what date must she approach the FAIS Ombud if she wishes to escalate?",o:["15 March 2027 — 12 months from the FSP response","1 February 2027 — 12 months from the original complaint","15 September 2026 — 6 months from the FSP response","15 March 2028 — 24 months from the FSP response"],a:2,e:"The complainant has 6 months from the date of the FSP's final response to approach the FAIS Ombud. The 6-month clock starts from the FSP's response (15 March 2026), making the deadline 15 September 2026."},
{t:"General Code of Conduct",lv:"C",q:"A representative recommends that a client replace an existing life policy with a new one from a different insurer. Before submitting the new application, what must the representative do?",o:["Obtain the client's written consent to the replacement and submit it with the application","Issue a 14-day cooling-off notice to the client before the application is finalised","Notify the existing insurer that the policy will be replaced no later than when the new application is submitted","Request a special dispensation from the FSCA for the replacement transaction"],a:2,e:"The General Code requires the representative to notify the original insurer no later than the time of submitting the new application. The ROA must document the rationale for replacement including why the new product is more suitable."},
{t:"Scope & Definitions",lv:"C",q:"Which of the following is classified as a financial product under FAIS?",o:["A fixed-term bank deposit with a maturity of 12 months — regulated solely by the Banks Act","A mortgage bond on a residential property — regulated solely by the NCA","A medical scheme membership — regulated solely by the Medical Schemes Act","A funeral policy with a maximum benefit of R50,000"],a:3,e:"Funeral policies (LT Insurance Subcategory A) are financial products under FAIS. Fixed deposits, mortgage bonds, and medical scheme memberships are specifically excluded from the FAIS definition of financial products — they are regulated under sector-specific legislation."},
{t:"FICA & AML",lv:"C",q:"An FSP's client due diligence process flags a client as high-risk based on their country of origin, business type, and transaction patterns. What does Enhanced Due Diligence (EDD) require that Standard CDD does not?",o:["EDD requires filing an STR immediately — all high-risk clients are treated as suspicious","EDD requires senior management approval to establish the relationship and enhanced ongoing monitoring","EDD requires the physical presence of the client for biometric verification","EDD requires notifying the FSCA before the business relationship can commence"],a:1,e:"EDD for high-risk clients requires: senior management approval before establishing the relationship, understanding the source of funds and wealth, and enhanced ongoing monitoring. It does not automatically require an STR — high risk is not the same as suspicious activity."},
{t:"Regulatory Compliance",lv:"C",q:"An FSP changes its financial year-end from February to June. When must it submit its next annual compliance report?",o:["Within 4 months of the OLD financial year-end in February for the transition period","Immediately upon changing the financial year-end","Within 4 months of the NEW financial year-end in June","Within 6 months of the new financial year-end to allow for the transition"],a:2,e:"The compliance report is always due within 4 months of the FSP's current financial year-end. The deadline follows the financial year-end regardless of when it changed. The report covers the period ending at the new year-end."},
{t:"Insurance Principles",lv:"C",q:"A client insures his delivery vehicle for R400,000. At the time of a total loss claim, the vehicle's market value is R280,000. The insurer pays R280,000. Which insurance principle explains this outcome?",o:["Contribution — multiple policies covering the same risk share the payout proportionately","Subrogation — the insurer recovers the claim amount from the third party at fault","Proximate cause — the dominant cause of the loss determines the amount payable","Indemnity — the insured is restored only to their pre-loss financial position"],a:3,e:"Indemnity limits the payment to the actual loss suffered — not the insured amount. The client cannot profit from a claim. R280,000 (market value at loss date) is the correct indemnity payment regardless of the R400,000 sum insured."},
{t:"Ethics & Professional Conduct",lv:"C",q:"A representative discovers her FSP has been submitting incorrect product information to clients in a standardised letter sent to over 2,000 clients. She raises it with her manager who says 'it's not technically a breach.' What should she do?",o:["Accept the manager's assessment — she has fulfilled her obligation by raising it","Report the FSP immediately to the FSCA — no internal escalation is required","Do nothing further — her obligation ends when she raises it with her direct manager","Document her concern and escalate to the compliance officer or higher if the manager dismisses it"],a:3,e:"Where a representative identifies a systemic compliance issue and the immediate manager dismisses it, the obligation to act with integrity requires escalation to the compliance function or above. Documenting the concern is critical. The representative's duty does not end at manager level."},
{t:"FAIS Role Players",lv:"C",q:"A compliance officer at an FSP identifies that the FSP is non-compliant with a new Conduct Standard. She reports this to the KI who instructs her to 'fix it quietly without telling the board.' What is the compliance officer's correct course of action?",o:["Follow the KI's instruction without question — the KI has full authority over the compliance function","Escalate to the board or governance structure — material compliance failures cannot be concealed","Fix the issue quietly but document the KI's instruction carefully in the compliance file","Resign immediately — she cannot continue operating in an ethically compromised environment"],a:1,e:"The compliance officer's duty runs to the FSP and its governance structure — not exclusively to the KI. Material compliance failures must be reported to the appropriate governance level. Concealing a breach on KI instruction places the compliance officer in personal regulatory jeopardy."},
{t:"Ethics & Professional Conduct",lv:"AP",q:"Sipho, a representative, discovers that his colleague has been forging client signatures on needs analysis forms to save time. Sipho says nothing because they are friends. Which statement BEST describes Sipho's position?",o:["Sipho's silence may constitute a breach of his honesty and integrity F&P requirement","Sipho has no obligation — only management must report misconduct","Sipho is protected because he did not personally forge any signatures","Sipho should report only if the colleague forges his signature"],a:0,e:"Honesty and integrity is not a passive requirement. Knowingly remaining silent about colleague misconduct that harms clients can constitute a breach. Professional responsibility includes not facilitating misconduct by omission."},
{t:"Ethics & Professional Conduct",lv:"AN",q:"Zanele is a senior representative who has been recommending high-commission products exclusively for 6 months. Her compliance officer flags this as a potential COI pattern. Zanele argues her recommendations are always suitable. What is the MOST accurate regulatory position?",o:["Zanele is correct — suitability is the only relevant test and commission is a lawful incentive to earn","Zanele must stop earning any commission entirely in order to avoid a conflict of interest","A consistent pattern of high-commission recommendations triggers a COI duty to disclose and manage it","The compliance officer is overstepping her role — only the FSCA can investigate commission patterns"],a:2,e:"Even where individual recommendations are defensibly suitable, a systematic pattern of recommending high-commission products creates a material conflict of interest that must be disclosed in every ROA and managed through the COI policy. Suitability and COI are separate obligations."},
{t:"Ethics & Professional Conduct",lv:"AN",q:"A representative learns that his FSP is planning to launch a product that he believes is unsuitable for the clients it will be marketed to. He raises this with his manager who dismisses the concern. What should he do next?",o:["Accept the manager's decision as final — this is a commercial matter outside his professional scope","Escalate to the compliance officer or KI, document the concern, and consider FSCA notification if unresolved","Immediately report the FSP directly to the FSCA without attempting any further internal escalation","Do nothing further unless and until a client actually complains about the product directly"],a:1,e:"The representative has a professional obligation to act in clients' interests. Where management dismisses a valid suitability concern, escalation to the compliance officer or KI is required. Documentation is critical. If the product represents a systematic TCF failure, FSCA notification may ultimately be warranted."},
{t:"Ethics & Professional Conduct",lv:"AP",q:"Thandi is preparing a replacement recommendation. She knows the new product is marginally better for the client but generates R8,000 more commission than keeping the existing policy. She does not disclose this commission difference in the ROA. Which breach has occurred?",o:["No breach — replacement is permitted and the advice is suitable","Breach of the COI disclosure obligation — the material difference in commission must be disclosed in the ROA","Breach of the suitability rule only — commission is not required to be disclosed","Breach of the needs analysis requirement because she did not reassess the client's needs"],a:1,e:"A material difference in commission between existing and replacement products is a conflict of interest that must be disclosed in the ROA. Failing to disclose it is a Code breach regardless of whether the advice is otherwise suitable."},
{t:"Ethics & Professional Conduct",lv:"C",q:"A representative receives a R1,500 cash gift from a product supplier at a year-end function. This exceeds the COI immaterial threshold of R1,000. What must she do?",o:["Disclose the gift in the COI register and in all future ROAs where that supplier's products are recommended","Return the gift immediately — receiving gifts from product suppliers is prohibited","Accept the gift — year-end functions are exempt from COI rules","Disclose only if she recommends that supplier's product within 30 days of receiving the gift"],a:0,e:"R1,500 exceeds the R1,000 per year immaterial threshold. It must be recorded in the FSP's COI register and disclosed in all ROAs where that product supplier's products are subsequently recommended."},
{t:"Ethics & Professional Conduct",lv:"AP",q:"A client instructs his representative to invest in a cryptocurrency scheme that the representative believes is fraudulent. The client insists it is his money and his decision. What must the representative do?",o:["Execute the instruction — the client has the right to make his own financial decisions","Execute the instruction but note his objection in the file","Execute the instruction only if the client signs an indemnity form","Refuse to execute and report the scheme to the FSCA as a potential fraudulent investment"],a:3,e:"A representative cannot facilitate a transaction they believe is fraudulent. They must refuse and, where a potential financial crime is involved, consider reporting obligations. Executing a fraudulent instruction is not protected by client consent."},
{t:"Ethics & Professional Conduct",lv:"K",q:"Which of the following BEST describes the 'fit and proper' honesty and integrity requirement?",o:["It applies only at the time of first appointment and formally lapses after 3 years of service","It is assessed only once a formal criminal charge is laid against the representative in question","It applies only to Key Individuals and does not extend to ordinary representatives at all","It is a continuous requirement maintained throughout a representative's entire career"],a:3,e:"Honesty and integrity is a continuous F&P requirement. It must be maintained throughout a representative's career — it is not a once-off assessment at appointment."},
{t:"Ethics & Professional Conduct",lv:"AP",q:"Buyani is a KI who discovers that his FSP's marketing material contains a misleading statement about guaranteed investment returns. He instructs the marketing team to remove it. Three months later he discovers it is still in circulation. What is Buyani's regulatory position?",o:["Buyani fully discharged his obligation when he gave the instruction — he bears no liability for staff non-compliance","Buyani is only liable if a client suffers an actual, provable financial loss as a direct result of the material","Buyani remains personally accountable — he must follow up to confirm his instruction was actually implemented","The marketing department alone bears sole responsibility for the content it distributes to the public"],a:2,e:"A KI who gives an instruction and does not verify implementation has not discharged their oversight obligation. Accountability for outcomes of the FSP's operations rests with the KI — delegating a task does not transfer the accountability for its completion."},
{t:"Ethics & Professional Conduct",lv:"AN",q:"A representative is pressured by her branch manager to meet a monthly sales target by recommending a specific high-margin product to all clients this week regardless of their individual needs. She complies and meets the target. Three months later several clients complain. What is the MOST accurate outcome?",o:["The branch manager alone bears sole liability here — the representative was merely following instructions","The clients must first prove an actual, quantifiable financial loss before any breach can be established","The representative faces personal regulatory consequences — following management's instruction is no defence","The FSP alone is liable as an institution — individual representatives are fully protected from consequence"],a:2,e:"Following an instruction from management to give unsuitable advice is not a defence. The representative has a personal professional obligation to give suitable advice. Both the representative and the manager may face regulatory consequences."},
{t:"Ethics & Professional Conduct",lv:"C",q:"Which action by a representative would be MOST consistent with the honesty and integrity requirement?",o:["Waiting until a conviction is formally confirmed in court before disclosing anything to the KI","Disclosing only if the pending charge relates directly and specifically to financial services work","Not disclosing at all because it is a private matter entirely unrelated to his work","Disclosing a pending criminal charge to the KI, even before it results in any conviction"],a:3,e:"Honesty and integrity requires proactive disclosure of material information that could affect F&P status — including pending charges. Waiting for a conviction before disclosing is inconsistent with this requirement."},
{t:"Investments & Financial Planning",lv:"AP",q:"Lerato's client is 58 years old, has R2.4 million in a retirement annuity, and plans to retire in 2 years. She asks whether to switch from a balanced fund to an equity fund for higher returns. What is the MOST appropriate response?",o:["Switch to equity — higher expected returns will meaningfully grow the retirement pot within 2 years","Maintain the balanced fund — with only 2 years left, the correction risk outweighs any extra return","Switch to equity but set a stop-loss order in place to help protect the accumulated capital","The decision is entirely the client's own — present both options clearly and let her decide unaided"],a:1,e:"With a 2-year investment horizon before retirement, capital preservation takes priority over growth. The risk of a market downturn immediately before retirement — sequence of returns risk — is a critical consideration. A balanced fund is more appropriate than equity for this specific client's timeframe."},
{t:"Investments & Financial Planning",lv:"AN",q:"A client's risk assessment reveals high financial capacity (net assets R8m) but very low risk tolerance (she panics at any portfolio decline). She asks for an equity-heavy portfolio. What must the adviser do?",o:["Recommend a conservative portfolio matching her low risk tolerance, and document why it differs from her request","Recommend equity anyway — financial capacity is the objective measure and it overrides psychological tolerance","Split the difference — recommend a balanced portfolio and let the client make the final call herself","Refer the client to a psychologist for evaluation before making any investment recommendation at all"],a:0,e:"When capacity and tolerance conflict, the lower measure governs. Recommending equity to a client with low psychological tolerance is a suitability failure even if she can financially afford the risk. The recommendation must reflect both capacity AND tolerance, with the lower constraint determining the outcome."},
{t:"Investments & Financial Planning",lv:"AP",q:"A client aged 35 wants to save for retirement, has a 30-year horizon, stable income, and moderate-high risk tolerance. Which investment approach is MOST appropriate?",o:["A predominantly equity-oriented portfolio — the 30-year horizon supports long-term growth investing","Capital preservation in a money market fund only — safety must always come first regardless of age","An annuity contract — a guaranteed income stream is always the safest possible retirement solution","An equal split between cash and property — this kind of diversification prevents almost all loss"],a:0,e:"A 30-year horizon and moderate-high risk tolerance supports a growth-oriented, equity-heavy portfolio. Time in the market allows recovery from short-term volatility. Capital preservation is inappropriate for this time horizon — it would severely limit real returns."},
{t:"Investments & Financial Planning",lv:"C",q:"What does 'diversification' achieve in a client's investment portfolio?",o:["Reduces unsystematic (company-specific) risk, but not systematic (market-wide) risk","Eliminates all investment risk entirely by spreading investments across many different assets","Guarantees a minimum return for the portfolio regardless of prevailing market conditions","Eliminates systematic (market-wide) risk but not unsystematic (company-specific) risk"],a:0,e:"Diversification reduces unsystematic risk (company or sector-specific) by spreading across different assets. It cannot eliminate systematic (market-wide) risk because all assets are affected by broad market movements."},
{t:"Investments & Financial Planning",lv:"AP",q:"An adviser is comparing a retirement annuity with a unit trust for a client who is self-employed and earning R600,000 per year. Which factor MOST favours the RA over the unit trust?",o:["The RA provides fully guaranteed returns while the unit trust offers no such guarantee at all","RA contributions are tax-deductible up to 27.5% of taxable income — a major benefit here","The RA carries no investment risk at all while the unit trust remains fully exposed to risk","The RA can be accessed at any time, unlike the unit trust which carries a 5-year lock-in period"],a:1,e:"For a high-income self-employed client, the tax deductibility of RA contributions (up to 27.5% of taxable income, capped at R350,000 per year) is a compelling advantage. This can significantly reduce current tax liability while building retirement savings."},
{t:"Investments & Financial Planning",lv:"C",q:"A Collective Investment Scheme (CIS) is PRIMARILY regulated by:",o:["The FAIS Act, which regulates advice and intermediary services","The National Credit Act, which regulates consumer credit","CISCA — the Collective Investment Schemes Control Act","The Long-term Insurance Act, which regulates insurance products"],a:2,e:"CIS (unit trusts) are primarily regulated by CISCA (the Collective Investment Schemes Control Act). FAIS regulates the advice and intermediary services related to CIS, but the products themselves fall under CISCA."},
{t:"Investments & Financial Planning",lv:"AN",q:"A client instructs an adviser to invest her entire R1.2m inheritance in a single local mining company's shares because 'that company will make me rich.' The adviser believes this is highly unsuitable. What is the correct approach?",o:["Execute the instruction as given — it is entirely her own money and she has the right to invest as she pleases","Refuse to act and report the client to the FSCA simply for planning to make a high-risk investment","Execute the instruction but first have the client sign an indemnity removing the adviser's own liability","Explain the concentration risk in writing, document her decision, and decide whether to proceed or decline"],a:3,e:"The adviser must clearly document the suitability concerns and ensure the client understands the risks. If the client persists with an informed decision, the adviser must assess whether proceeding is appropriate given their professional obligations. An indemnity does not eliminate suitability obligations."},
{t:"Investments & Financial Planning",lv:"AP",q:"Which statement about a living annuity is CORRECT?",o:["The insurer fully bears the longevity risk — the client receives guaranteed income for life regardless of fund value","Withdrawals from a living annuity are fixed permanently at 5% per year and can never be changed thereafter","A living annuity can be fully surrendered for a cash lump sum payout at any time the client chooses","The client bears both investment risk and longevity risk — the fund can be depleted if withdrawals are too high"],a:3,e:"In a living annuity, the client bears both investment risk (fund performance) and longevity risk (risk of outliving the fund). Drawdown rates between 2.5% and 17.5% per year can be adjusted annually — but high drawdowns combined with poor returns can deplete the fund."},
{t:"Investments & Financial Planning",lv:"C",q:"A client asks about the difference between a pension fund and a provident fund on resignation. Which statement is CORRECT?",o:["Both allow full cash withdrawal on resignation with absolutely no tax applying to either fund type","On resignation from a pension fund, the full fund value can be taken as cash; on resignation from a provident fund, only the vested portion can be taken as cash","Both funds require mandatory transfer to a preservation fund on resignation — cash withdrawal is never permitted","Both allow transfer to a preservation fund or RA on resignation, though the cash rules have since been harmonised"],a:3,e:"Retirement fund withdrawal rules have been subject to significant changes. The key point is that both allow transfer to a preservation fund or RA on resignation (tax-free). The rules around cash withdrawals have been harmonised — verify the current position against the most recent legislation."},
{t:"POPIA & Data Protection",lv:"C",q:"Under POPIA, the 'responsible party' is:",o:["The specific government department responsible for enforcing and overseeing POPIA compliance","The natural person whose personal information is being collected and processed","The IT service provider or vendor that stores the personal data on its systems","The person or entity that determines the purpose and means of processing"],a:3,e:"The responsible party determines WHY and HOW personal information is processed. This is the entity that bears primary POPIA obligations — typically the FSP."},
{t:"POPIA & Data Protection",lv:"C",q:"An FSP uses a third-party cloud provider to store client data. Under POPIA, the cloud provider is classified as:",o:["A responsible party — it processes the data","A data subject — it is affected by the processing","An operator — it processes data on behalf of the responsible party","An information regulator — it controls access to the data"],a:2,e:"A cloud provider that processes data on behalf of the FSP is an operator under POPIA. The FSP remains the responsible party. The relationship must be governed by a written contract that binds the operator to POPIA requirements."},
{t:"POPIA & Data Protection",lv:"AP",q:"A representative collects a client's email address during the needs analysis for policy communication purposes. Six months later, the representative sends the client marketing emails about a new product without asking for fresh consent. The client objects. What is the correct regulatory position?",o:["Permitted — existing clients are automatically deemed to have consented to marketing from their adviser","Permitted only if the new product being marketed is similar to the one the client already holds","Permitted as long as the representative includes an unsubscribe option in the marketing email","A breach of POPIA's further processing limitation — data can't be reused for an unrelated purpose without consent"],a:3,e:"POPIA's further processing limitation prohibits using personal information beyond the original collection purpose. Marketing communications require separate, specific consent. The existing client relationship does not constitute deemed consent for marketing."},
{t:"POPIA & Data Protection",lv:"AN",q:"An FSP suffers a cyberattack that exposes 4,000 client records including identity numbers and banking details. The CEO instructs the IT team to investigate before notifying anyone. After 3 weeks the CEO decides the breach was not serious enough to notify clients. Which POPIA obligation has been breached?",o:["No obligation exists at all — the CEO has full discretion to assess severity before ever notifying anyone","The obligation to notify the Regulator and affected data subjects as soon as reasonably possible has been breached","Only the obligation to notify the FSCA has been breached — POPIA notification itself is optional","The obligation to conduct a risk assessment has been breached — notification is required only once risk is confirmed"],a:1,e:"POPIA requires notification to both the Information Regulator AND affected data subjects as soon as reasonably possible after discovering a breach. A 3-week delay while internally deliberating whether the breach is 'serious enough' is not compliant. The assessment of severity does not defer the notification obligation."},
{t:"POPIA & Data Protection",lv:"AP",q:"A client requests that an FSP delete all personal information it holds about him. The FSP is required by FICA to retain records for 5 years after the business relationship ends. How should the FSP respond?",o:["Delete all records immediately — the client's right to deletion always overrides every other legal obligation","Retain the records but anonymise them so they are technically no longer personal information at all","Ask the Information Regulator to formally resolve the conflict between POPIA and FICA requirements","Decline the deletion request and retain records for the FICA period — legal retention overrides the deletion right"],a:3,e:"POPIA's data subject rights operate within the bounds of other applicable legislation. Where a legal obligation (such as FICA's 5-year retention requirement) requires retention, the FSP must retain the records despite the deletion request. The retention is lawful under POPIA's processing limitation condition."},
{t:"POPIA & Data Protection",lv:"C",q:"Which of the following is classified as 'special personal information' under POPIA requiring enhanced protection?",o:["A client's physical home address","A client's employment history","A client's health or medical information","A client's investment portfolio value"],a:2,e:"Special personal information under POPIA includes: race, religion, health/medical, sex life, biometrics, criminal record, trade union membership. Health information requires enhanced protection — there is a general prohibition on processing it without specific grounds."},
{t:"POPIA & Data Protection",lv:"AP",q:"A representative leaves an FSP and joins a competitor. She takes a copy of her client database — names, contact details, and financial profiles — to contact former clients at the new FSP. Which law(s) does this most directly breach?",o:["Only the employment contract — regulatory law simply does not govern what employees take when leaving","Only FICA — because financial account details are directly involved in the transferred data","Only the FAIS Act — because it specifically involves financial services clients and their data","POPIA (processing beyond original purpose without consent) and potentially common law (confidential information)"],a:3,e:"Taking client personal information to a new employer is a POPIA breach — it is further processing beyond the original collection purpose without consent. It may also constitute misappropriation of confidential information at common law. Both POPIA and employment law are potentially engaged."},
{t:"POPIA & Data Protection",lv:"C",q:"The Information Regulator can impose administrative fines under POPIA of up to:",o:["R1,000,000","R5,000,000","R10,000,000","R50,000,000"],a:2,e:"POPIA provides for administrative fines of up to R10,000,000 for serious violations. Criminal penalties including imprisonment also apply to certain offences."},
{t:"POPIA & Data Protection",lv:"AN",q:"An FSP's marketing team purchases a mailing list from a data broker and sends promotional material to 50,000 people who have never interacted with the FSP. Several recipients complain they never consented to receive communication. Which POPIA principles are MOST directly breached?",o:["Security safeguards and openness conditions only are breached in this specific marketing scenario","Data quality and data subject participation conditions only are breached here","Accountability and openness conditions only are breached in this particular case","Processing limitation, purpose specification, and further processing are directly breached here"],a:3,e:"Using a purchased mailing list for unsolicited marketing breaches multiple POPIA conditions: processing limitation (the data subjects' information was collected for a different purpose), purpose specification (marketing was not the disclosed purpose), and further processing limitation (use beyond original purpose). Consent is not present."},
{t:"Regulatory Compliance",lv:"AP",q:"An FSP's annual compliance report reveals that 18% of client files are missing signed needs analyses. The FSCA responds with a request for an urgent meeting. What does this finding most likely indicate?",o:["A minor administrative issue only, correctable going forward with absolutely no regulatory consequence at all","A systemic compliance failure — the FSP cannot demonstrate suitability for 18% of advice given, risking remediation","Only a documentation problem — if the advice given was actually suitable, no regulatory issue exists here","A KI failure only — individual representatives are solely responsible for their own file documentation"],a:1,e:"Missing signed needs analyses means the FSP cannot demonstrate suitability for those clients. This is a systemic compliance failure — not a documentation technicality. The FSCA may impose remediation obligations, licence conditions, and enhanced supervision."},
{t:"Regulatory Compliance",lv:"AN",q:"During an FSCA thematic review, an FSP demonstrates perfect policy documentation but the review finds that client outcomes are consistently poor: high lapse rates, frequent unsuitability complaints, and low client retention. What does this reveal about the FSP's compliance approach?",o:["The FSP has confused policy compliance with outcome compliance — TCF requires actual fair outcomes, not just documentation","The FSP is fully compliant, since it has demonstrated the required policies are formally in place","The thematic review findings are irrelevant if the FSP's documentation meets all regulatory requirements","The compliance officer is solely at fault for not ensuring the documented policies are implemented"],a:0,e:"TCF is outcomes-based. Perfect documentation without fair client outcomes is not compliance — it is compliance theatre. The FSCA assesses what clients actually experience, not what policies say should happen. This is the fundamental distinction between rules-based and outcomes-based regulation."},
{t:"Regulatory Compliance",lv:"C",q:"An FSP's financial year ends on 28 February. By what date must it submit its annual compliance report?",o:["30 June","31 March","30 April","31 August"],a:0,e:"The compliance report must be submitted within 4 months of the FSP's financial year-end. 28 February + 4 months = 30 June."},
{t:"Regulatory Compliance",lv:"AP",q:"A compliance officer discovers mid-year that the FSP's professional indemnity insurance has lapsed due to an administrative error. What is the IMMEDIATE consequence?",o:["The FSP has 30 days to reinstate cover before any consequence","The FSP must notify all clients but may continue operating while arranging reinstatement","The FSCA imposes a fine but allows continued operation during rectification","The FSP's licence is automatically suspended — PI cover is a continuous F&P requirement"],a:3,e:"PI insurance lapse triggers automatic licence suspension. There is no grace period. This is one of the most critical compliance calendar events an FSP must manage. Reinstatement must be arranged immediately and the FSCA notified."},
{t:"Regulatory Compliance",lv:"AN",q:"An FSP operates under Category I and wants to expand into discretionary portfolio management (Category II). The KI holds RE1 and a Category I qualification but not the Category II-specific qualification. The board approves the expansion and the FSP begins rendering Category II services. What has occurred?",o:["No breach — the KI's existing qualifications cover all FAIS activities","Permitted for a transitional period of 24 months while the KI obtains the Category II qualification","Permitted if the FSP notifies the FSCA of the expansion within 30 days","The FSP is rendering services outside its licence — a serious FAIS breach that could result in licence revocation"],a:3,e:"Rendering financial services outside the categories for which the FSP is licensed is an unlicensed activity under FAIS — a serious breach. The FSP must obtain the correct Category II licence, with the KI meeting all Category II F&P requirements, before rendering those services."},
{t:"General Code of Conduct",lv:"AN",q:"Thandeka advises a 67-year-old retired client to surrender her 15-year endowment policy (2 years from maturity) and invest the proceeds in a high-risk equity unit trust. The commission on the unit trust is R14,000. Thandeka's ROA states the recommendation is suitable. A complaint is lodged 18 months later when the unit trust has lost 22% of its value. What is the MOST likely FAIS Ombud finding?",o:["Breach of suitability — high-risk equity is prima facie unsuitable for this 67-year-old retiree, regardless of the signed ROA","No breach at all — the ROA clearly confirms suitability and the client signed it willingly","Breach of the replacement policy rules only — the suitability of the actual product itself is not in question","No breach — investment returns are never guaranteed and market losses don't constitute unsuitable advice"],a:0,e:"A signed ROA does not prove suitability — it proves disclosure. Recommending a high-risk product to a 67-year-old retiree who surrenders a near-maturity policy is objectively unsuitable given the client's age, risk profile, and time horizon. The commission difference is also a material COI that must have been disclosed."},
{t:"General Code of Conduct",lv:"AN",q:"An FSP's compliance programme reveals that in the past 12 months, 34% of replacement recommendations were made by the same representative, and in 89% of those cases, the new product generated higher commission than the product replaced. The KI is presented with this data. What is the MOST appropriate action?",o:["Strong evidence of a systematic COI pattern requiring KI investigation, root cause analysis, and possible remediation","No action is required at all — replacement is permitted under FAIS and commission is a lawful remuneration structure","Issue the representative with a verbal warning only, then monitor the situation for the next 6 months","Implement an immediate blanket ban on all replacement recommendations to prevent any future COI issues"],a:0,e:"A 34% concentration of replacements by one advisor with 89% generating higher commission is a significant red flag for churning. The KI must investigate suitability of all affected replacements, not just future conduct. Affected clients may require proactive remediation even if they have not complained."},
{t:"General Code of Conduct",lv:"AP",q:"During a client meeting, Sipho realises his client has a pressing need for risk cover that falls outside his licence (he is only licensed for Category I investments). He recommends a life insurance product anyway because he does not want to lose the client to another adviser. Which breach has occurred?",o:["No breach — recommending life insurance is a natural extension of investment advice","A breach of the needs analysis requirement only — the category mismatch is not itself a concern","Rendering financial services outside his authorised categories — a serious FAIS breach regardless of good intentions","A minor Code breach that can be remedied by obtaining the correct authorisation retrospectively"],a:2,e:"Rendering financial services outside authorised categories is unlicensed activity under FAIS. Good intentions and client convenience do not justify operating outside the scope of authorisation. The correct action is to refer the client to a licensed life insurance representative."},
{t:"General Code of Conduct",lv:"AN",q:"An FSP implements a new scripted sales process where all advisers must read from a standard script that recommends the FSP's own investment products without deviation or needs analysis. Advisers who deviate from the script face disciplinary action. A client complains 6 months later that the product is unsuitable. Who bears regulatory accountability?",o:["Only the individual adviser who personally read the script aloud to the client during the meeting","The client alone — they accepted the recommendation the moment they signed the application form","Both the FSP (process preventing suitability assessment) and the KI (for approving it) bear accountability","Only the compliance officer who reviewed and approved the script before its use began"],a:2,e:"An institutionalised process that eliminates needs analysis and forces scripted recommendations is a systemic Code breach engineered by the FSP and sanctioned by the KI. Both bear primary accountability. The adviser may have limited individual defence if the system genuinely prevented compliance — but this is a high bar."},
{t:"FAIS Role Players",lv:"AN",q:"Bongiwe is both the KI and the sole representative at her FSP. She takes 4 weeks annual leave with no arrangement for someone to cover her functions. Two clients attempt to get advice during this period and cannot. What is the regulatory consequence?",o:["No consequence — sole proprietors are exempt from continuity of service requirements","She can delegate her KI functions to a client services administrator for the duration of her absence","The FSP must cease rendering financial services during her absence — there is no licensed person present to render services","She must pre-sign a batch of ROAs before leaving to cover any potential client advice needs"],a:2,e:"Where the KI is also the sole representative, their absence without any substitute arrangement means no authorised person is present to render financial services. The FSP must effectively cease operations during this period. Pre-signing ROAs is never appropriate — advice must be contemporaneous and specific."},
{t:"FAIS Role Players",lv:"AP",q:"An FSP appoints a compliance officer who is also the KI's spouse. A FSCA inspector raises a concern about independence. Which principle is MOST directly engaged?",o:["The general prohibition on employing any family members whatsoever within financial services firms","The requirement for the compliance function to remain independent — a spousal tie creates a conflict","The fit-and-proper requirement for compliance officers to hold specific formal qualifications","The KI's obligation to establish a compliance function, which has technically been discharged here"],a:1,e:"The compliance function must be independent to be effective. A spousal relationship with the KI creates a structural conflict that undermines the compliance officer's ability to objectively monitor and report on the KI's own oversight activities. Independence is a fundamental principle of effective compliance governance."},
{t:"FAIS Role Players",lv:"C",q:"Which of the following correctly describes the relationship between an FSP and its representative?",o:["The representative is a fully independent contractor with zero obligation to follow FSP compliance rules","The representative holds their own fully independent FAIS licence, issued directly through the FSP","The representative is authorised by the FSP and acts under its licence, which bears legal responsibility","The FSP and representative share equal regulatory responsibility for absolutely all advice given"],a:2,e:"Representatives are authorised by the FSP — they act under the FSP's licence, not their own. The FSP bears legal responsibility for the representative's conduct. This is why the KI's oversight function is so critical."},
{t:"Supervision & Debarment",lv:"AN",q:"A KI discovers that a supervisor has been approving all supervised representative files without reading them — signing off within minutes of receiving files that contain 15-page needs analyses. This has been happening for 4 months. What is the KI's regulatory position?",o:["The KI has absolutely no liability at all — the supervisor alone is responsible for their own quality of review","The KI is liable only if it can be proven that clients were actually harmed during the 4-month period","The supervisor alone is the accountable party — the KI properly delegated by simply appointing them","The KI bears accountability — monitoring supervision is a KI duty, and the pattern went undetected for months"],a:3,e:"A KI who appoints a supervisor and never verifies the quality of that supervision has not discharged the oversight mandate. Detecting that a supervisor is rubber-stamping reviews is within the scope of the KI's monitoring obligations. Four months of undetected non-review constitutes a KI accountability failure."},
{t:"Supervision & Debarment",lv:"AP",q:"A representative under supervision gives unsuitable advice that results in a client loss of R45,000. The client was NOT informed the representative was under supervision. What additional breach exists beyond the suitability failure?",o:["No additional breach exists here — the unsuitable advice itself is the only material issue","A FICA breach — because money moved from the client's account as a direct result of the advice given","Breach of disclosure — clients must be told the representative is under supervision before any service","A COI breach — because the representative had a personal financial interest in generating commission"],a:2,e:"Failure to disclose supervision status to clients before rendering any service is an independent Code breach separate from the suitability failure. Both breaches occurred — the disclosure failure and the unsuitable advice are distinct violations."},
{t:"Supervision & Debarment",lv:"C",q:"After a debarment, what is the MINIMUM waiting period before a representative debarred for dishonesty can be reappointed at any FSP?",o:["6 months","12 months","24 months","3 years"],a:1,e:"The minimum waiting period before reappointment after a dishonesty debarment is 12 months. After this period, the individual must still demonstrate they meet all current F&P requirements — the 12 months is a floor, not an automatic right to reappointment."},
{t:"FAIS Ombud",lv:"AN",q:"An FSP receives an Ombud determination awarding R3,200,000 to a complainant. The FSP's CEO argues the Ombud exceeded their authority and instructs the legal team not to comply. The complainant returns to the Ombud. What can the complainant do?",o:["Resubmit the complaint to the Ombud — the Ombud can increase the award amount for non-compliance","Complain directly to the FSCA — the FSCA can compel compliance with binding Ombud determinations","Nothing further — the FSP retains the right to dispute the determination without any consequence","Apply to the High Court to enforce it as a civil judgement — it carries the force of such a judgement"],a:3,e:"An unappealed FAIS Ombud determination has the force of a High Court civil judgement. The complainant can apply for a writ of execution — allowing the sheriff to attach FSP assets to satisfy the award. The FSP's refusal to comply does not invalidate the determination."},
{t:"FAIS Ombud",lv:"AP",q:"A client files a complaint with the FAIS Ombud about advice she received 4 years ago. She only discovered the advice was unsuitable when she tried to access the funds and found they had significantly underperformed. The FSP argues the complaint is time-barred. What is the correct position?",o:["The Ombud assesses when the client reasonably became aware of the harm — prescription may run from discovery","The complaint is automatically time-barred — there is a strict 3-year absolute limit for all complaints","The complaint is time-barred — the 6-month window from the FSP's response runs from the date of advice","The complaint cannot ever be time-barred — the Ombud has completely unlimited jurisdiction on time"],a:0,e:"Prescription in financial services complaints is complex. The Ombud considers when the client reasonably discovered or should have discovered the loss. Where the harm only became apparent years after the advice, prescription may run from the date of discovery. The Ombud has discretion to condone late filing where good cause is shown."},
{t:"Insurance Principles",lv:"AN",q:"Sipho insures his commercial property for R2,000,000. The actual replacement value at the time of a partial fire loss is R4,000,000. The fire causes R800,000 in damage. The policy contains an average clause. How much does the insurer pay?",o:["R800,000 — the full loss amount","R200,000 — the insurer pays only 25% because Sipho is 75% underinsured","R600,000 — the insurer deducts a standard 25% underinsurance penalty","R400,000 — applying the average clause proportionately"],a:3,e:"Average clause calculation: Payout = Loss × (Insured value ÷ Actual value) = R800,000 × (R2,000,000 ÷ R4,000,000) = R800,000 × 50% = R400,000. Sipho is 50% underinsured and therefore absorbs 50% of the loss himself."},
{t:"Insurance Principles",lv:"AP",q:"Lerato holds two home contents policies on her household goods — Policy A for R500,000 and Policy B for R500,000. A burglary causes R300,000 in losses. How does the contribution principle apply?",o:["Lerato can claim R300,000 from each policy for a total of R600,000","Lerato must choose which policy to claim from — she cannot claim from both","The first policy pays R300,000 and the second policy lapses automatically","Each insurer pays R150,000 — they share the loss proportionately to avoid over-recovery"],a:3,e:"The contribution principle prevents over-recovery when two or more policies cover the same risk. Each insurer pays a proportionate share of the actual loss: R300,000 ÷ 2 = R150,000 each. Lerato recovers exactly her loss — no more."},
{t:"Insurance Principles",lv:"C",q:"After settling a client's vehicle theft claim, the insurer discovers the vehicle was stolen by the client's employee acting criminally. The insurer wants to recover from the employee. Which principle allows this?",o:["Contribution — multiple insurance parties are considered to have jointly caused this loss","Subrogation — having paid the claim, the insurer may recover from the responsible third party","Proximate cause — the employee's criminal act was determined to be the dominant cause","Indemnity — this principle exists to prevent unjust enrichment on the part of the client"],a:1,e:"Subrogation allows the insurer, after settling a claim, to pursue recovery from the responsible third party in the insured's name. Here, the insurer can sue the employee for the vehicle's value. This prevents the wrongdoer from escaping liability simply because the victim was insured."},
{t:"TCF",lv:"AN",q:"An FSP's management data reveals that clients in township areas receive product recommendations from a junior representative pool, while clients in affluent suburbs receive recommendations from senior representatives with more extensive analysis. Both groups pay the same fees. What TCF obligation does this create?",o:["None at all — FSPs are free to structure their service delivery however they choose commercially","Only Outcome 3 applies — communication quality differences are considered the primary concern here","This is purely a commercial staffing decision that falls entirely outside TCF obligations","TCF Outcomes 1 and 4 are engaged — geographic service quality differentiation is a fair treatment failure"],a:3,e:"Systematic geographic differentiation in service quality, where clients pay equal fees but receive materially different quality of analysis, is a TCF failure. Outcome 1 (fair culture) and Outcome 4 (suitable advice) are both engaged. The KI must investigate whether clients in the lower-service group received genuinely suitable recommendations."},
{t:"TCF",lv:"AP",q:"An FSP's new product brochure states that a market-linked investment will 'consistently grow your savings by 12-15% per year.' A financial planner distributes this brochure without amendment. Which TCF outcome is MOST directly breached?",o:["Outcome 6 — clients will likely face barriers when trying to exit once returns disappoint them","Outcome 2 — the product itself was not properly designed for its intended target market","Outcome 5 — clients were given a misleading performance expectation the product cannot guarantee","Outcome 3 — the wording used in the communication is considered unclear to clients"],a:2,e:"TCF Outcome 5 requires that products perform as firms have led clients to expect. Representing a market-linked product as delivering 'consistent 12-15% growth' creates an expectation the product cannot guarantee. This is an Outcome 5 breach. The financial planner who distributes it without correction shares in the breach."},
{t:"TCF",lv:"C",q:"A KI wants to implement a meaningful TCF measurement programme. Which metric is MOST directly an indicator of Outcome 4 (suitable advice)?",o:["Client satisfaction survey scores — clients who report being happy are assumed to have received suitable advice","Number of new policies sold per month — higher sales volume is assumed to indicate advisers meeting client needs","Proportion of clients who refer friends — referral rates are assumed to reflect advice quality received","Early lapse rates — a high rate may indicate clients were sold products that don't meet their needs"],a:3,e:"Early lapse rates are a direct indicator of Outcome 4 suitability. If clients are cancelling policies shortly after purchase, it is likely the product was not suitable for their needs, circumstances, or affordability. Client satisfaction surveys measure perception — not actual suitability outcomes."},
{t:"FICA & AML",lv:"AN",q:"An FSP client who is a senior government official (Minister of Finance) wants to open a discretionary investment account with R50,000,000. The relationship manager is eager to onboard the large account. What must happen BEFORE the account is opened?",o:["Standard CDD alone is sufficient here — government officials are inherently trusted public figures","As a Minister, he is a Politically Exposed Person (PEP) — Enhanced Due Diligence is mandatory before onboarding","Only the identity verification component of standard CDD applies — PEP status doesn't affect onboarding","PEP status applies only to foreign politicians — South African officials are treated as standard clients"],a:1,e:"A serving government minister is a PEP — a Politically Exposed Person under FICA. EDD is mandatory before establishing the relationship: senior management approval, source of wealth and funds verification, and enhanced ongoing monitoring. These requirements apply to South African PEPs, not just foreign ones."},
{t:"FICA & AML",lv:"AN",q:"An FSP files an STR on a client. Two days later, the relationship manager mentions to the client during a routine call that 'there has been some compliance activity on your account.' Has the representative committed an offence?",o:["No — the representative did not explicitly state that an STR had actually been filed","No — the client has an inherent right to know that their account is currently under review","Yes — tipping off is a criminal offence, and even an indirect hint can constitute the offence","No — the statement made was far too vague and indirect to constitute tipping off"],a:2,e:"Tipping off under FICA is not limited to explicitly telling the client an STR was filed. Any communication that directly or indirectly discloses that a suspicious transaction has been reported, or that could prejudice an investigation, constitutes tipping off. Even vague hints can satisfy the offence."},
{t:"FICA & AML",lv:"AP",q:"A client makes 6 cash deposits of R48,000 each over 3 consecutive months — always on the same day of the month. No single transaction triggers a Cash Threshold Report. What is the MOST appropriate response?",o:["File an STR — the pattern suggests structuring, deliberate splitting to avoid reporting thresholds","No action required at all since each individual transaction is below the R49,999.99 threshold","File a Cash Threshold Report for every single transaction, regardless of the amount involved","Request additional information directly from the client before deciding whether to file anything"],a:0,e:"Structuring — deliberately splitting transactions to avoid the reporting threshold — is itself a money laundering red flag and a criminal offence. Six consistent deposits just below the threshold on the same day each month is a textbook structuring pattern. An STR must be filed as soon as possible, and no later than 15 days (excluding weekends and public holidays) from when the suspicious pattern becomes apparent. Note this differs from the 3-business-day Cash Threshold Report period."},
{t:"General Code of Conduct",lv:"AP",q:"Which of the following statements about the disclosure obligations under the General Code of Conduct are correct?\n\nI. A representative must disclose their FSP's contact details to the client.\nII. A representative must disclose the existence of any conflict of interest, even if wholly immaterial.\nIII. A representative must disclose the basis on which they are remunerated.",o:["I only","I and III only","II and III only","I, II and III"],a:1,e:"Disclosure obligations require FSPs to disclose material information such as contact details and remuneration basis. Conflicts of interest must be managed and disclosed, but the obligation applies to MATERIAL conflicts — not every immaterial interest regardless of size. Statement II overstates the requirement, so I and III only are correct."},
{t:"Ethics & Professional Conduct",lv:"AP",q:"A representative discovers after the fact that a colleague gave a client incorrect information about a product's fees, resulting in the client making a poorly informed decision. Which of the following are appropriate next steps?\n\nI. Report the matter internally so the client's file can be corrected and the client properly informed.\nII. Encourage the colleague to proactively disclose the error to the client.\nIII. Consider whether the pattern indicates a broader training or competency gap that should be addressed.",o:["I only","I and II only","II and III only","I, II and III"],a:3,e:"All three responses are appropriate. Internal correction protects the client, encouraging proactive disclosure aligns with honesty obligations, and considering whether this reflects a broader competency gap is part of responsible oversight — not just fixing the single incident."},
{t:"Regulatory Compliance",lv:"AP",q:"An FSP's compliance officer identifies multiple client files missing signed needs analyses. Which of the following are correct regulatory implications?\n\nI. The FSP cannot demonstrate that the advice given to those clients was suitable.\nII. The FSCA may impose remediation requirements or licence conditions as a result.\nIII. Only the individual representatives who wrote the files bear responsibility — the FSP itself is not implicated.",o:["I only","I and II only","II and III only","I, II and III"],a:1,e:"Missing needs analyses means the FSP cannot demonstrate suitability — a systemic compliance failure, not just an individual one. The FSCA may impose remediation or licence conditions on the FSP itself. Statement III is incorrect: the FSP bears institutional responsibility, not just the individual representatives."},
{t:"FICA & AML",lv:"AP",q:"Which of the following circumstances require Enhanced Due Diligence under FICA?\n\nI. The client is a Politically Exposed Person.\nII. The client is a cash-intensive business with an unusually complex ownership structure.\nIII. The client requests a product with a higher-than-average premium.",o:["I only","I and II only","II and III only","I, II and III"],a:1,e:"PEP status and complex, cash-intensive ownership structures are recognised EDD triggers under FICA. A higher premium alone is not — premium size by itself doesn't indicate elevated money laundering risk without other red flags present."},
{t:"FAIS Role Players",lv:"AP",q:"Which of the following statements about the role of a compliance officer at an FSP are correct?\n\nI. The compliance officer must be independent enough to objectively monitor the FSP's compliance.\nII. The compliance officer must report directly to the FSCA, bypassing the FSP's own governance structure.\nIII. The compliance officer's function can be outsourced to an external compliance practice.",o:["I only","I and III only","II and III only","I, II and III"],a:1,e:"Independence is essential to an effective compliance function, and outsourcing to an external compliance practice is common and permitted. Statement II is incorrect — the compliance officer operates within the FSP's own governance structure and does not bypass it to report directly to the FSCA."},
{t:"Fit & Proper Requirements",lv:"AP",q:"Which of the following are components of the Fit and Proper honesty and integrity requirement?",o:["Not having a debarment currently in effect for dishonesty or fraud","Not having an unresolved dispute over the mismanagement of client funds","Not having a criminal conviction involving dishonesty within the relevant look-back period","All three are genuine components of the honesty and integrity requirement"],a:3,e:"Honesty and integrity under Fit and Proper covers debarment history, unresolved fund-mismanagement disputes, and relevant criminal convictions involving dishonesty. All three are genuinely assessed together — none is sufficient or excluded on its own."},
{t:"Fit & Proper Requirements",lv:"AP",q:"Which statement about Fit and Proper requirements is INCORRECT?",o:["Financial soundness does not apply to Key Individuals","Honesty and integrity is a continuous requirement, not a once-off assessment","Once a representative passes RE5, no further competency requirements ever apply","CPD hours must be evidenced to the FSP within 15 days of the cycle ending"],a:2,e:"Passing RE5 satisfies one competency milestone, not all ongoing requirements. CPD remains a continuous obligation throughout a representative's career — competency does not end once a regulatory exam is passed."},
{t:"Scope & Definitions",lv:"C",q:"Which of the following would constitute 'advice' under the FAIS Act?",o:["A recommendation to purchase a specific unit trust fund","A recommendation to switch from one retirement annuity to another","Guidance steering a client toward a strategy based on their stated goals","All three are advice — each involves a recommendation about a product"],a:3,e:"Advice under FAIS is any recommendation, guidance, or proposal regarding a financial product. Purchase recommendations, switch recommendations, and goal-based strategic guidance all meet this definition — the medium or formality of the communication is irrelevant."},
{t:"FAIS Ombud",lv:"AP",q:"Which statement about the FAIS Ombud is INCORRECT?",o:["An unappealed determination has the force of a High Court civil judgement","The Ombud's jurisdiction is capped unless the respondent consents to a higher amount","Complainants must pay a case fee before the Ombud will investigate a matter","The Ombud may attempt conciliation or mediation before a formal determination"],a:2,e:"The FAIS Ombud service is free to complainants. In fact no case fee is levied on anyone: the discretionary fee formerly allowed under Rule 9(a) was discontinued with effect from 10 September 2012. Requiring complainants to pay upfront would undermine the Ombud's role as an accessible consumer protection mechanism."},
{t:"General Code of Conduct",lv:"AP",q:"A representative recommends a product to a client. Which of the following would ALL need to be true for the recommendation to be considered suitable under the Code?",o:["The recommendation is appropriate given the client's risk profile","The recommendation is appropriate given the client's needs and objectives","The client's affordability was properly assessed beforehand","All three are required together — suitability isn't satisfied by just one"],a:3,e:"Suitability under the Code is a combined test — risk alignment, needs alignment, and affordability all have to be satisfied together. Meeting only one or two of these factors while ignoring the others does not produce a suitable recommendation."},
{t:"Ethics & Professional Conduct",lv:"AP",q:"Which statement about acting with integrity as a representative is INCORRECT?",o:["Integrity requires proactive disclosure of matters affecting Fit and Proper status","A representative who witnesses colleague misconduct has no obligation to act","Following an unlawful instruction from a manager is not a valid defence to a breach","Honesty obligations apply regardless of whether the client would ever find out"],a:1,e:"Witnessing colleague misconduct and remaining silent is not consequence-free — knowingly failing to act on known misconduct can itself breach the integrity requirement. Integrity is an active, not passive, standard."},
{t:"Regulatory Compliance",lv:"AP",q:"Which of the following are legitimate reasons an FSP's licence conditions might be varied by the FSCA?",o:["The FSP's KI no longer meets the qualifications required for a licensed category","The FSP has a documented pattern of unresolved client complaints","The FSP's financial soundness has deteriorated below the required threshold","All three are legitimate grounds for a licence condition variation"],a:3,e:"Licence conditions can be varied for a range of reasons connected to ongoing Fit and Proper compliance — KI qualification gaps, unresolved complaint patterns, and financial soundness deterioration are all recognised, legitimate grounds."},
{t:"FICA & AML",lv:"AP",q:"Which statement about Suspicious Transaction Reports (STRs) is INCORRECT?",o:["An STR must be filed within 15 days, excluding weekends and public holidays","Filing an STR requires proof that the transaction is definitely criminal","Tipping off a client about a filed STR is a criminal offence","Structuring transactions to avoid reporting thresholds is itself a red flag"],a:1,e:"STRs are filed on reasonable SUSPICION, not proof. Requiring proof of criminal activity before filing would set an impossibly high bar and defeat the entire purpose of suspicious transaction reporting as an early-warning mechanism."},
{t:"FAIS Role Players",lv:"AP",q:"Which of the following are legitimate reasons a supervision arrangement between a KI and a new representative might be extended beyond the standard period?",o:["The representative has not yet completed all required regulatory examinations","The representative has not yet met the required experience threshold","The KI has genuine, documented concerns about the representative's competency","All three are legitimate bases for extending the supervision period"],a:3,e:"Supervision bridges the gap until F&P requirements are met. Incomplete exams, unmet experience thresholds, and genuine documented competency concerns are all legitimate reasons the arrangement may need to continue past the standard period."},
{t:"General Code of Conduct",lv:"AN",q:"A client's Record of Advice states that a recommended product is 'guaranteed to outperform inflation,' despite no such guarantee actually existing.\n\nI. This may constitute a misleading statement under the Code.\nII. If untrue, it could expose the FSP to a suitability or misrepresentation complaint.\nIII. The FSCA can treat this as evidence of a Code breach even without a client complaint.",o:["I only","I and II only","II and III only","I, II and III"],a:3,e:"A false guarantee in an ROA is a misleading statement, exposes the FSP to complaint risk, and can be identified as a Code breach through routine FSCA supervision — a client complaint is not a precondition for regulatory action."},
{t:"Ethics & Professional Conduct",lv:"AN",q:"A KI becomes aware that a representative has been recommending unnecessarily expensive products to elderly clients for higher commission.\n\nI. The KI should wait until a client formally complains before taking any action.\nII. The pattern should be investigated immediately as a possible systemic suitability issue.\nIII. The KI should consider whether the conduct affects the representative's ongoing Fit and Proper status.",o:["I only","I and II only","II and III only","I, II and III"],a:2,e:"Waiting for a formal complaint is not appropriate once a KI is aware of a concerning pattern — the KI has an active oversight duty. Immediate investigation and a Fit and Proper reassessment are the correct responses; statement I describes exactly the wrong approach."},
{t:"Regulatory Compliance",lv:"AP",q:"Which statement about an FSP's compliance monitoring programme is INCORRECT?",o:["Monitoring must provide meaningful assurance, not just meet a standard sample size","A monitoring programme that misses significant gaps is inadequate regardless of convention","Once a monitoring programme is approved, it never needs to be revisited","Risk-based monitoring should allocate more scrutiny to higher-risk representatives"],a:2,e:"Compliance monitoring programmes are not a once-off approval — they need ongoing review to remain adequate as risks, representatives, and products change. Treating a monitoring programme as permanently settled is itself a compliance weakness."},
{t:"General Code of Conduct",lv:"AP",q:"A representative is technically meticulous and never dishonest with clients, but consistently recommends whichever product pays the highest commission rather than the most suitable option. Which general duty is most directly breached?",o:["The duty to act with due skill, care and diligence — his technical process is deficient","The duty to act honestly and fairly — commission-driven selling is inherently dishonest","The duty to act in clients' interests — suitability has been subordinated to personal reward","The duty to maintain the integrity of the industry — public trust is the primary concern here"],a:2,e:"The Code's four general duties — honesty and fairness, skill and diligence, clients' interests, and industry integrity — operate together, but this fact pattern squarely engages the duty to act in clients' interests: recommending based on commission rather than suitability is the classic breach of that specific duty, even where conduct is technically diligent and not dishonest."},
{t:"General Code of Conduct",lv:"AP",q:"Which statement about disclosure under the General Code of Conduct is INCORRECT?",o:["A representative must disclose their FSP's full contact details to the client","Disclosure obligations only apply once a formal complaint has been lodged","The basis on which a representative is remunerated must be disclosed","Material conflicts of interest must be disclosed and managed"],a:1,e:"Disclosure obligations apply proactively, upfront, before or at the point of rendering a financial service — not only once a complaint has already been lodged. Waiting for a complaint would defeat the purpose of disclosure entirely."},
{t:"Ethics & Professional Conduct",lv:"AP",q:"Which of the following demonstrate a representative acting with the honesty and integrity required under Fit and Proper?",o:["Proactively disclosing a pending criminal charge before any conviction","Declining to backdate a document at a client's request","Refusing to conceal a colleague's known regulatory breach","All three — each reflects the honesty and integrity standard in practice"],a:3,e:"Proactive disclosure, refusing to backdate documents, and refusing to conceal known misconduct are all concrete expressions of the honesty and integrity standard — it is demonstrated through everyday professional choices, not just formal declarations."}
,
{t:"Maintaining Your FSP Licence",lv:"K",q:"Section 8(8) of the FAIS Act requires an authorised FSP to:",o:["Display a certified copy of its licence at its business premises","Publish particulars of its licence in the Government Gazette annually","Submit its licence to the Authority for endorsement each year","Provide a copy of its licence to every client before rendering a service"],a:0,e:"Section 8(8) requires the licence to be displayed prominently at the business premises. The remaining options describe requirements that do not exist."},
{t:"Maintaining Your FSP Licence",lv:"K",q:"In terms of section 11 of the FAIS Act, an FSP licence lapses when the:",o:["Authority resolves that a material provision has been contravened","FSP is provisionally or finally liquidated or sequestrated","Authority issues a directive under section 38C of the Act","FSP fails to submit its annual compliance report on time"],a:1,e:"Lapsing occurs by operation of law on events such as liquidation, sequestration or death. The remaining options describe suspension, withdrawal or enforcement, which are deliberate decisions of the Authority."},
{t:"Maintaining Your FSP Licence",lv:"C",q:"Which ONE of the following correctly distinguishes lapsing from withdrawal of an FSP licence?",o:["Lapsing follows an Authority decision, withdrawal occurs automatically","Both require prior written notice to the FSP and an oral hearing","Lapsing occurs by operation of law, withdrawal by Authority decision","Lapsing may be appealed, while withdrawal is final and unappealable"],a:2,e:"Lapsing happens automatically on a defined event. Withdrawal is a deliberate decision. Recourse exists against suspension and withdrawal but not against lapsing, because lapsing is not a decision of the Authority."},
{t:"Maintaining Your FSP Licence",lv:"K",q:"In terms of section 34 of the FAIS Act, the Authority may declare a business practice undesirable:",o:["Only where the practice contravenes an express provision of the Act","Only after a client has suffered demonstrable financial loss","Only at the written request of the Ombud for Financial Services Providers","Even though the practice contravenes no express provision"],a:3,e:"The power is deliberately broad. A practice may be declared undesirable simply because it is harmful to clients or to the integrity of the industry, without any prior contravention."},
{t:"Maintaining Your FSP Licence",lv:"C",q:"The Authority has declared a practice undesirable. An FSP whose conduct falls within that declaration must:",o:["Cease the practice, the declaration applying generally","Continue until a directive is addressed to it individually","Apply for an exemption within 30 days of publication","Continue but disclose the declaration to affected clients"],a:0,e:"A declaration under section 34 applies generally. It does not require an individually addressed directive, and there is no exemption or disclosure mechanism permitting continuation."},
{t:"Maintaining Your FSP Licence",lv:"C",q:"Where an FSP persists with a declared undesirable practice, the Authority may:",o:["Issue only a further written warning to the provider","Direct reparation, including rectification for affected clients","Refer the matter to the Ombud for a determination on costs","Impose a criminal sanction without any court process"],a:1,e:"Reparation measures are available and may include directing the FSP to compensate or rectify the position of clients affected — not merely to stop the practice going forward."},
{t:"Maintaining Your FSP Licence",lv:"K",q:"Professional indemnity and fidelity insurance cover requirements for FSPs are prescribed by:",o:["Board Notice 80 of 2003","Board Notice 127 of 2010","Board Notice 123 of 2009","Board Notice 194 of 2017"],a:2,e:"BN 123 of 2009 prescribes these requirements. BN 80 is the General Code, BN 127 deals with compliance officers, and BN 194 with Fit and Proper requirements. BN 194 s51 did not repeal BN 123."},
{t:"Maintaining Your FSP Licence",lv:"C",q:"All of the following are correct where an FSP's professional indemnity cover lapses, EXCEPT:",o:["The FSP no longer complies with a licensing condition","The Authority may suspend or withdraw the licence in consequence","The key individual must take steps to restore compliance promptly","The FSP is afforded a statutory thirty-day remedial period"],a:3,e:"No statutory remedial period exists. The consequence attaches from the moment cover lapses."},
{t:"Maintaining Your FSP Licence",lv:"AP",q:"An FSP authorised only for long-term insurance subcategory B1 concludes a transaction in a collective investment scheme. The advice is technically sound and no client suffers loss. The position is that the FSP has:",o:["Rendered a service outside its licensed subcategories","Not contravened the Act, the advice having been correct","Acted lawfully, since it holds a Category I licence","Acted lawfully provided the Authority is notified within 15 days"],a:0,e:"Authorisation operates at subcategory level. Neither the quality of the advice nor subsequent notification cures a service rendered outside the licensed subcategory."},
{t:"Maintaining Your FSP Licence",lv:"AP",q:"A sole proprietor FSP owner dies. The estate wishes to continue trading under the licence while a buyer is found. The correct position is that the licence:",o:["May be used by the estate for a period of up to 90 days","Lapses immediately, being personal and non-transferable","Remains valid if the Authority is notified within 5 days","Remains valid while an approved key individual holds office"],a:1,e:"The licence is personal to the sole proprietor and lapses on death. There is no estate continuation and no grace period."},
{t:"Maintaining Your FSP Licence",lv:"C",q:"Which ONE of the following is NOT correct regarding a related accreditation, such as one under the Medical Schemes Act?",o:["Suspension of it can affect services in the related category","The FSP must monitor the status of accreditations it holds","Its withdrawal has no bearing on the FSP's FAIS authorisation","A lapsed accreditation can restrict the categories it may operate in"],a:2,e:"Withdrawal, suspension or lapsing of a related accreditation can directly affect the FSP's ability to render services in the category to which it relates."},
{t:"Maintaining Your FSP Licence",lv:"K",q:"Levies payable by an FSP to the Financial Sector Conduct Authority are imposed under the:",o:["Financial Advisory and Intermediary Services Act, 2002","Financial Sector Regulation Act, 2017","Financial Intelligence Centre Act, 2001","Financial Sector and Deposit Insurance Levies Act, 2022"],a:3,e:"Levies are imposed under the Financial Sector and Deposit Insurance Levies Act 11 of 2022. A missed levy payment is a compliance failure like any other statutory deadline."},
{t:"Maintaining Your FSP Licence",lv:"C",q:"An FSP's licence has been suspended by the Authority. In respect of that decision the FSP:",o:["Has recourse and may challenge the decision as provided","Has no recourse, suspension being purely administrative","Must surrender the licence before disputing the suspension","May continue rendering services pending the challenge"],a:0,e:"An FSP is entitled to make representations and may challenge a suspension or withdrawal. A suspension is not stayed merely because it is disputed, and no recourse applies to lapsing."},
{t:"Maintaining Your FSP Licence",lv:"AP",q:"During an on-site inspection the Authority requests records the key individual considers outside the visit's scope. The key individual should:",o:["Decline until the Authority furnishes reasons in writing","Produce the records, the Authority being empowered to inspect","Refer the request to the FSP's attorneys before responding","Produce only those records considered relevant by the FSP"],a:1,e:"The Authority is empowered to conduct inspections and require production of records. The FSP is not entitled to filter the request or make production conditional on reasons being given."},
{t:"Maintaining Your FSP Licence",lv:"AN",q:"Which combination of the following would cause an FSP licence to LAPSE rather than be suspended or withdrawn?\\ni) Death of a sole proprietor\\nii) An Authority decision following persistent non-compliance\\niii) Voluntary closure of the business\\niv) Liquidation of the FSP",o:["(i), (ii) and (iii) only","(ii), (iii) and (iv) only","(i), (iii) and (iv) only","(i), (ii), (iii) and (iv)"],a:2,e:"Death, voluntary closure and liquidation cause a licence to lapse automatically. A decision by the Authority following non-compliance is a withdrawal, which is a different category."},
{t:"Maintaining Your FSP Licence",lv:"C",q:"Offences created by the FAIS Act are BEST described as:",o:["An alternative to administrative action, not additional to it","Applicable only to key individuals and not to representatives","Enforceable only once the Ombud has issued a determination","Criminal, and separate from civil and administrative consequences"],a:3,e:"FAIS Act offences are criminal and separate from suspension, withdrawal or debarment. A person can face both regulatory and criminal consequences for the same conduct."},
{t:"Record-Keeping Obligations",lv:"K",q:"In terms of section 18 of the FAIS Act, records must be kept for a period of:",o:["3 years","5 years","7 years","10 years"],a:1,e:"Section 18 prescribes a five-year retention period, calculated from the date the advice was given."},
{t:"Record-Keeping Obligations",lv:"K",q:"Under the FIC Act, records of a business relationship must be kept for at least five years from the date on which the:",o:["Business relationship was terminated","Business relationship commenced","First transaction was concluded","Client was first identified"],a:0,e:"FIC Act records run for five years from termination of the relationship, which is a different starting point from the FAIS Act record."},
{t:"Record-Keeping Obligations",lv:"AP",q:"A client relationship of fifteen years is terminated. The compliance officer proposes destroying the FICA records immediately. The correct position is that they must be kept for:",o:["Five years from when the relationship commenced","A further five years, being twenty years in total","No further period, the relationship having ended","A further five years only if the client so requests"],a:1,e:"Five years from termination, on top of the fifteen-year relationship, gives twenty years in total. This is why the starting point matters."},
{t:"Record-Keeping Obligations",lv:"C",q:"Records required under the FAIS Act may be kept:",o:["In printed form only, at the registered business premises","In electronic form only, with an approved recovery plan","In printed or electronic form, safe from destruction","At the compliance officer's offices, in printed form only"],a:2,e:"Records may be kept in printed or electronic form provided they are safe from destruction and available for inspection by the Authority."},
{t:"Record-Keeping Obligations",lv:"C",q:"Which ONE of the following is NOT a record a provider must maintain under section 18 of the FAIS Act?",o:["Known premature cancellations of transactions by clients","Complaints received and whether they were resolved","Instances of non-compliance and the reasons for them","The personal financial statements of each representative"],a:3,e:"Representatives' personal financial statements are not a prescribed record. Premature cancellations, complaints and instances of non-compliance are all expressly listed."},
{t:"Record-Keeping Obligations",lv:"AP",q:"An FSP stores client records across several systems with no defined retrieval process, and a full client file takes three weeks to compile. The correct finding is that the FSP:",o:["Does not comply, retrievability being part of the obligation","Complies, since the records do ultimately exist","Complies, provided the systems are backed up regularly","Does not comply only where a client suffers actual prejudice"],a:0,e:"A record that exists but cannot be located within a reasonable time is treated, for practical purposes, as if it does not exist."},
{t:"Record-Keeping Obligations",lv:"C",q:"A client asks an FSP to delete their file before the statutory retention period has expired. The FSP:",o:["Must comply, the client being the data subject concerned","May not comply where this conflicts with a retention duty","Must comply only once the relationship has been terminated","May comply provided the deletion is recorded in writing"],a:1,e:"Statutory retention obligations under the FAIS and FIC Acts override an informal client request. The position should be explained to the client rather than the file deleted."},
{t:"Record-Keeping Obligations",lv:"AN",q:"Which combination of the following correctly reflects record-keeping obligations?\\ni) A record of advice is retained for 5 years from the date the advice was given\\nii) FICA records are retained for 5 years from termination of the relationship\\niii) Outsourcing record keeping transfers the obligation to the third party\\niv) Security and controlled access form part of the obligation",o:["(i), (iii) and (iv) only","(ii) and (iii) only","(i), (ii) and (iv) only","(i), (ii), (iii) and (iv)"],a:2,e:"Outsourcing transfers the operational task, never the statutory obligation. The remaining statements are correct, and security requirements engage POPIA alongside FAIS and FICA."},{t:"Fit & Proper Requirements",lv:"AP",q:"By when must a representative submit evidence of CPD hours completed for the cycle ending 31 May?",o:["Within 5 days, matching the Part I debarment notification deadline","Within 15 days of the cycle ending","Within 30 days, matching the representative register update deadline","Within 4 months, matching the annual compliance report deadline"],a:1,e:"CPD evidence must reach the FSP within 15 days of the cycle closing (31 May). The 5-day, 30-day and 4-month figures are real FAIS deadlines, but they govern debarment notification, register updates and compliance reporting respectively — not CPD evidence submission."},
{t:"Fit & Proper Requirements",lv:"AP",q:"A representative's Date of First Appointment (DOFA) is 1 March 2024. By when must she pass RE5?",o:["28 February 2025 — 12 months from DOFA","28 February 2026 — 24 months from DOFA","1 March 2027 — 36 months from DOFA","1 March 2030 — 6 years from DOFA"],a:1,e:"RE5 must be passed within 24 months of DOFA. The 12-month figure is the COB Training deadline, and 6 years is the Recognised Qualification deadline — both real FAIS timeframes, but neither applies to RE5."},
{t:"Fit & Proper Requirements",lv:"K",q:"COB Training must be completed within how long of a representative's initial appointment?",o:["6 months","12 months","24 months","6 years"],a:1,e:"COB Training = 12 months from initial appointment. 24 months is the RE5 deadline and 6 years is the Recognised Qualification deadline — both real, both different rules."},
{t:"Supervision & Debarment",lv:"AP",q:"A KI decides to debar a representative on 3 March. By what date must Part I of the FSCA notification be submitted?",o:["8 March — 5 days after the decision","18 March — 15 days after the decision","2 April — 30 days after the decision","3 July — 4 months after the decision, matching the compliance report deadline"],a:0,e:"Part I must reach the FSCA within 5 days of the debarment decision. Part II follows within 15 days. The 30-day and 4-month figures are real deadlines elsewhere in FAIS (register updates; compliance reporting) — not debarment."},
{t:"FAIS Ombud",lv:"AP",q:"A client receives the FSP's final written response to her complaint on 1 February. What is the last date she can approach the FAIS Ombud?",o:["15 February — 15 days later","15 March — 6 weeks later","1 August — 6 months later","1 February next year — 12 months later"],a:2,e:"A complainant has 6 months from the FSP's final response to approach the Ombud. The 6-week figure is the time the FSP itself gets to resolve a complaint before the Ombud accepts jurisdiction — a different clock, easy to confuse with the complainant's own deadline."},
{t:"FICA & AML",lv:"AN",q:"An accountable institution identifies both a suspicious transaction and a single cash transaction of R62,000 from the same client on the same day. What are the correct reporting deadlines?",o:["Both must be reported within 3 business days","The suspicious transaction: 15 days (excl. weekends/public holidays); the cash transaction: 3 business days","The suspicious transaction: 3 business days; the cash transaction: 15 days (excl. weekends/public holidays)","Both must be reported within 15 days, excluding weekends and public holidays"],a:1,e:"STRs must reach the FIC within 15 days, excluding weekends and public holidays. CTRs (cash >R49,999.99) have a separate, shorter deadline: 3 business days. The two reports run on different clocks even when triggered by the same client on the same day."},
{t:"Record-Keeping Obligations",lv:"AN",q:"A client's business relationship with an FSP runs from 2015 to 2033, when it is terminated. Advice was last given in 2032. Until what year must the FICA/CDD records be kept?",o:["2037 — 5 years from the last advice given","2038 — 5 years from termination of the relationship","2033 — records may be destroyed immediately on termination","2053 — 20 years from the start of the relationship"],a:1,e:"FICA/CDD records run 5 years from termination of the business relationship — not from the date advice was last given (that clock applies to Records of Advice under FAIS, a separate rule with a different starting point)."},
{t:"FAIS Ombud",lv:"AP",q:"A client claims R4,200,000 in damages from an FSP's poor advice, and the FSP refuses to consent to exceeding the standard cap. What is the maximum the Ombud can award?",o:["R3,000,000","R3,500,000","R4,200,000 — the Ombud always awards the full claimed amount","R800,000 — a lower cap applies to advice-related complaints"],a:1,e:"The Ombud's cap is R3,500,000 per complaint. Exceeding it requires the respondent FSP's written consent, which was withheld here — so the award is capped at R3,500,000 regardless of the amount claimed."},
{t:"General Code of Conduct",lv:"AP",q:"A representative receives a client's cash premium at 10:00 on Tuesday. By when must it be deposited into the FSP's separate client account?",o:["By 10:00 Wednesday — within 1 business day","By 10:00 Friday — within 3 business days","By the following Tuesday — within 5 business days","By month-end, together with all other premiums collected"],a:0,e:"Client funds must be deposited into the designated separate account within 1 business day of receipt. There is no batching or month-end exception."},
{t:"General Code of Conduct",lv:"AP",q:"A representative receives R850 in the current year from a single product supplier, unrelated to any specific transaction. Must this be disclosed as a conflict of interest?",o:["Yes — any financial interest from a product supplier must be disclosed regardless of amount","No — it falls below the R1,000 per year immaterial financial interest threshold","No — only interests exceeding R5,000 per year require disclosure","Yes — but only if the client specifically requests a breakdown"],a:1,e:"The COI immaterial threshold is R1,000 per year from the same third party. R850 falls below it, so this specific interest does not require disclosure under that threshold — though the FSP's broader COI policy obligations still apply."},
{t:"Fit & Proper Requirements",lv:"AN",q:"Which of the following FAIS-related deadlines are correctly matched to their trigger? i. Compliance report — 4 months from FSP financial year-end. ii. Debarment Part I — 5 days from the debarment decision. iii. CPD evidence — 30 days from cycle end. iv. STR — 15 days, excluding weekends and public holidays, from becoming aware.",o:["i, ii and iv only","i and ii only","ii, iii and iv only","i, ii, iii and iv"],a:0,e:"i, ii and iv are correctly matched. Statement iii is wrong: CPD evidence is due within 15 days of the cycle ending, not 30 — 30 days is the representative register update deadline, borrowed here as a plausible-looking distractor."},{t:"TCF",lv:"C",q:"Which TCF outcome specifically requires products to be designed for identified target markets?",o:["Outcome 1","Outcome 2","Outcome 3","Outcome 4"],a:1,e:"TCF Outcome 2: products designed for identified target markets. Outcome 1 is fair treatment embedded as culture; Outcome 3 is clear pre-sale information; Outcome 4 is suitability of advice — easy to confuse when outcomes are numbered sequentially."},
{t:"TCF",lv:"C",q:"Which TCF outcome requires customers to be given clear information before they buy?",o:["Outcome 2","Outcome 3","Outcome 4","Outcome 5"],a:1,e:"Outcome 3: clear information before customers buy. Outcome 2 concerns product design for target markets; Outcome 4 concerns suitability of advice; Outcome 5 concerns products performing as expected."},
{t:"Insurance Principles",lv:"K",q:"What is the maximum benefit for a policy classified as LT Insurance Subcategory A?",o:["Less than R10,000","Less than R30,000","Less than R50,000","No monetary limit"],a:1,e:"Subcategory A (assistance/funeral policies) is capped at a maximum benefit of less than R30,000, with simplified underwriting."},
{t:"Insurance Principles",lv:"K",q:"What is the cooling-off period applicable to long-term insurance policies sold via distance marketing?",o:["14 days","21 days","31 days","60 days"],a:2,e:"31 days is the cooling-off period for LT insurance policies sold via distance marketing — telephonic or online sales without face-to-face contact."},
{t:"FICA & AML",lv:"AN",q:"Which of the following are required Enhanced Due Diligence steps for a Politically Exposed Person? i. Senior management approval. ii. Determine source of wealth and funds. iii. Enhanced ongoing monitoring. iv. Automatic account closure after 12 months.",o:["i and ii only","ii, iii and iv only","i, ii and iii only","i, ii, iii and iv"],a:2,e:"i, ii and iii are the three real EDD requirements for PEPs. Statement iv is invented — there is no automatic 12-month account closure rule for PEPs under FICA; enhanced monitoring continues for the duration of the relationship, not a fixed period."},
{t:"Record-Keeping Obligations",lv:"AP",q:"An FSP wants to know how long it must retain a client's POPIA-covered personal information after the business relationship ends. Which is correct?",o:["Exactly 5 years from the date of collection, matching the FICA retention rule","Exactly 5 years from the date of collection, matching the Records of Advice rule","No longer than necessary for the purpose it was collected for — in practice this tracks FAIS/FICA","Indefinitely, since all financial services records must legally be preserved on a permanent basis"],a:2,e:"POPIA's retention principle is 'no longer than necessary for the purpose' — it doesn't set its own independent fixed number. In practice, that purpose is usually defined by the FAIS (5 years from advice) or FICA (5 years from termination) retention periods."},
{t:"Record-Keeping Obligations",lv:"AN",q:"Which piece of legislation governs each of the following? i. What advice records to keep. ii. What CDD records to keep, and for how long. iii. How securely client records must be protected.",o:["i=FAIS; ii=FICA; iii=POPIA","i=FICA; ii=FAIS; iii=POPIA","i=FAIS; ii=POPIA; iii=FICA","i=POPIA; ii=FICA; iii=FAIS"],a:0,e:"FAIS governs what advice records to keep (ROA, 5 years from advice). FICA governs what CDD records to keep and for how long (5 years from termination — a different starting point). POPIA governs how securely those records must be protected. Each Act answers a different question about the same underlying records."},{t:"Regulatory Compliance",lv:"AN",q:"Which of the following are correct regarding the National Credit Act and Consumer Protection Act? i. The NCA requires a compulsory affordability assessment before any credit agreement. ii. Reckless credit may be suspended or restructured by a court. iii. The CPA cooling-off period for direct marketing contracts is 14 days. iv. CPA cooling-off rights apply even to financial products sold via direct marketing.",o:["i and ii only","ii, iii and iv only","i, ii, iii and iv","i, ii and iv only"],a:3,e:"i, ii and iv are correct. Statement iii is wrong: the CPA cooling-off period for direct marketing is 5 business days, not 14 — 14 days is a commonly confused figure from other consumer contexts."},
{t:"Insurance Principles",lv:"AN",q:"Which of the following statements about short-term insurance are TRUE? i. ST insurance is indemnity-based — the insured cannot profit from a claim. ii. Under-insurance triggers the average clause, with partial claims paid proportionately. iii. An excess is the final portion of a claim paid, after the insurer's contribution. iv. A no-claims bonus is lost on the first claim in the relevant period.",o:["i, ii and iv only","i and ii only","ii, iii and iv only","i, ii, iii and iv"],a:0,e:"i, ii and iv are correct. Statement iii is wrong: an excess is the FIRST portion of a claim borne by the insured, not the final portion — reversing this is a common error."},
{t:"Investments & Financial Planning",lv:"AN",q:"Which of the following statements about retirement products are correct? i. A retirement annuity's contributions are tax-deductible. ii. A retirement annuity is generally accessible from age 55. iii. Pension and provident fund resignation rules are identical in every respect. iv. Diversification reduces unsystematic, not systematic, risk.",o:["i and ii only","ii, iii and iv only","i, ii and iv only","i, ii, iii and iv"],a:2,e:"i, ii and iv are correct. Statement iii is wrong: pension and provident fund resignation rules differ — that distinction is precisely why advisers must know both, not treat them as interchangeable."},
{t:"POPIA & Data Protection",lv:"AN",q:"Which of the following are POPIA processing conditions? i. Accountability. ii. Purpose specification. iii. Client consent is the ONLY lawful basis for processing personal information. iv. Security safeguards.",o:["i and ii only","ii, iii and iv only","i, ii, iii and iv","i, ii and iv only"],a:3,e:"i, ii and iv are three of POPIA's eight processing conditions. Statement iii is wrong: consent is ONE lawful basis for processing, not the only one — POPIA also recognises necessity for a contract, legal obligation, and legitimate interest, among others."}]);

const QB_RE1 = Object.freeze([
{t:"KI Duties & Responsibilities",lv:"K",q:"The two core statutory functions of a Key Individual are:",o:["Manage all client portfolios and submit the FSP's annual compliance reports to the FSCA","Manage and oversee the provision of financial services by representatives and other KIs","Handle client complaints and represent the FSP in all FSCA hearings and proceedings","Approve all product applications and personally debar non-compliant representatives"],a:1,e:"The KI's statutory mandate under FAIS is management and oversight of financial service rendering — not compliance administration, portfolio management, or complaint handling."},
{t:"KI Duties & Responsibilities",lv:"C",q:"Zintle discovers representatives are submitting applications with inflated client income figures. She raises it at the next quarterly review but takes no immediate action. What is her regulatory position?",o:["Only the representatives are individually liable for the falsified applications","She is protected provided she documented the concern formally in the meeting minutes","The FSCA may only act after completing a full investigation and confirming client financial loss","Zintle faces separate regulatory exposure for failing to take immediate corrective action"],a:3,e:"A KI's oversight duty is active and ongoing. Discovering a material irregularity creates an immediate obligation to act — delayed or passive responses do not satisfy the statutory duty."},
{t:"KI Duties & Responsibilities",lv:"C",q:"Marcus has been advising on products outside his authorised scope and the KI was unaware. Which statement correctly reflects regulatory consequences?",o:["Only Marcus faces consequences — the KI cannot be penalised for conduct she did not observe","The FSCA determines liability only after completing a full criminal investigation of all parties","Both Marcus and the FSP face exposure — Marcus for unauthorised advice; the FSP for inadequate oversight","All regulatory liability rests personally with the KI because oversight is her sole responsibility"],a:2,e:"Ignorance of a representative's misconduct is not a defence if oversight systems were inadequate. Both the representative and the FSP face regulatory exposure."},
{t:"KI Duties & Responsibilities",lv:"K",q:"Ultimate compliance responsibility within an FSP rests with:",o:["The compliance officer as the designated FAIS compliance accountability holder","The representatives who interact directly with clients and render financial services","The product suppliers whose products the FSP is licensed to distribute and advise on","The KI and senior management — accountability for compliance cannot be delegated away"],a:3,e:"While the compliance officer monitors and reports, ultimate accountability for compliance rests with the KI and senior management. Delegating operational functions does not transfer regulatory responsibility."},
{t:"KI Duties & Responsibilities",lv:"C",q:"A KI is about to retire and no replacement has been identified. What must the FSP do?",o:["Appoint a senior representative as acting KI and notify the FSCA within 30 days","Suspend all financial service rendering until the FSCA formally approves a replacement KI","Notify the FSCA immediately — an FSP must at all times have at least one approved KI","Allow the outgoing KI to continue remotely until a replacement is permanently appointed"],a:2,e:"An FSP must always have at least one approved KI. Any vacancy must be reported to the FSCA immediately. Failure to maintain a KI breaches licensing conditions and may trigger licence suspension."},
{t:"KI Duties & Responsibilities",lv:"C",q:"An FSP's KI receives a whistleblower report that a senior representative is falsifying client needs analyses. The KI must:",o:["Investigate immediately, take corrective action, and report a material irregularity to the FSCA if confirmed","Monitor the situation for a further month before acting to ensure the allegation is substantiated","Discuss the concern informally with the representative before deciding whether to escalate","Inform the board only if the conduct has already caused demonstrable client financial prejudice"],a:0,e:"Whistleblower reports of falsified documentation are a serious material irregularity trigger. The KI must investigate immediately, stop any ongoing harm, and report confirmed breaches to the FSCA without delay."},
{t:"The Compliance Officer & Compliance Function",lv:"K",q:"Compliance reports must be submitted to the FSCA at minimum:",o:["Monthly — to ensure real-time regulatory oversight of FSP conduct","Quarterly — to align with standard financial reporting periods","Only when a material irregularity occurs or the FSCA formally requests a report","Annually — covering the full compliance picture for the reporting period"],a:3,e:"FAIS requires FSPs to submit annual compliance reports. The compliance officer certifies the report and it must accurately reflect the FSP's compliance status for the full reporting period."},
{t:"The Compliance Officer & Compliance Function",lv:"C",q:"Representatives have been rendering services in unauthorised product categories. As KI, what must you do?",o:["Take immediate corrective action and ensure the material irregularity is reported to the FSCA","Refer to the board of directors and await their formal written instruction before acting","Wait for the next scheduled compliance report to formally document the breach","Inform affected clients individually and await their formal responses before proceeding"],a:0,e:"Rendering services in unauthorised categories is a material contravention of FAIS. The KI must act immediately — stopping the conduct, remediating clients where necessary, and reporting to the FSCA."},
{t:"The Compliance Officer & Compliance Function",lv:"C",q:"Premier Wealth outsourced its FICA verification to a vendor. The vendor fails to verify a high-risk client. Premier Wealth argues the vendor now bears regulatory responsibility. Is this correct?",o:["Yes — outsourcing to a regulated vendor transfers all associated regulatory responsibility to that vendor","Premier Wealth's liability is limited by the indemnity clause contained in the outsourcing agreement","No — outsourcing does not transfer regulatory responsibility; Premier Wealth remains fully accountable","The FSCA may only act against the vendor as the entity that directly failed to perform verification"],a:2,e:"Regulatory responsibility cannot be outsourced. An FSP that delegates functions to a third party retains full regulatory accountability. Outsourcing is an operational arrangement, not a liability transfer."},
{t:"The Compliance Officer & Compliance Function",lv:"K",q:"A material irregularity is best defined as:",o:["A significant breach of the FAIS Act or related legislation that the KI must address and report","A formal disagreement between two or more representatives about product suitability","A minor administrative error that must be noted in the next scheduled compliance report","A client complaint about investment product underperformance after suitable advice was given"],a:0,e:"A material irregularity is a serious regulatory breach — not a minor administrative oversight. The KI has an obligation to address it immediately and report it to the FSCA where required."},
{t:"The Compliance Officer & Compliance Function",lv:"C",q:"An FSP applies the same level of monitoring to all representatives regardless of experience or conduct history. What is the regulatory concern?",o:["No concern — uniform monitoring ensures absolutely consistent treatment of all representatives","Concern only if the FSP employs more than 10 representatives requiring differentiated oversight","Concern only if the FSCA has previously raised monitoring adequacy in a formal supervisory review","Effective compliance monitoring must be risk-based — higher-risk activities and people need closer oversight"],a:3,e:"A risk-based compliance monitoring approach applies more intensive oversight to higher-risk representatives, products, and activities. Uniform monitoring fails to prioritise resources where harm is most likely."},
{t:"The Compliance Officer & Compliance Function",lv:"C",q:"A KI discovers the FSP's representatives have not been conducting full FICA due diligence on new clients for 6 months. The correct response is:",o:["Notify only the compliance officer and allow them to rectify the process without FSCA involvement","Implement corrective training and document the remediation internally without notifying the FSCA","Suspend all new client onboarding until the FSCA provides formal guidance on remediation","Implement immediate corrective measures, conduct a retrospective review, and report the breach to the FSCA"],a:3,e:"Six months of FICA non-compliance is a material irregularity. The KI must stop the breach, review affected onboardings, implement corrective measures, and report to the FSCA — not manage it internally."},
{t:"Twin Peaks & Regulatory Framework",lv:"K",q:"South Africa's Twin Peaks model was introduced by which legislation?",o:["The Financial Sector Regulation Act (FSRA) 2017 — separating prudential from conduct regulation","The Banks Act — separating commercial banking from investment banking supervision","The FICA — separating anti-money laundering obligations from broader financial crime control","The FAIS Act — separating financial advice from intermediary service regulation"],a:0,e:"The FSRA introduced Twin Peaks: the Prudential Authority (part of SARB) ensures financial soundness and systemic stability; the FSCA ensures market conduct — how firms treat customers and behave in markets."},
{t:"Twin Peaks & Regulatory Framework",lv:"C",q:"Under POPIA, an FSP collects and processes client financial data. Which statement correctly describes its obligations?",o:["Data may be retained indefinitely provided it is encrypted and access controls are maintained","Personal data may be shared freely with all group companies without consent if security is ensured","Personal information may only be processed for specific, lawful purposes and not retained longer than necessary","POPIA applies only to data collected digitally — paper-based records are completely excluded"],a:2,e:"POPIA requires lawful, purpose-specific processing. Data must be collected for a defined purpose, used only for that purpose, kept secure, and deleted when no longer needed — regardless of format."},
{t:"Twin Peaks & Regulatory Framework",lv:"K",q:"The primary distinction between the Prudential Authority and the FSCA is:",o:["The PA regulates the quality of financial advice; the FSCA regulates investment product performance","They have identical mandates but apply them to different sizes of financial institution","The PA ensures financial soundness and systemic stability; the FSCA ensures fair market conduct","The FSCA regulates banking institutions; the PA regulates insurers and investment managers"],a:2,e:"This is the essence of Twin Peaks: the PA looks inward (is the institution financially sound?) while the FSCA looks outward (is the institution treating customers fairly and behaving appropriately?)."},
{t:"Twin Peaks & Regulatory Framework",lv:"C",q:"A new FSCA Conduct Standard is published under the Financial Sector Regulation Act. How does this affect an FSP?",o:["Conduct Standards are advisory guidelines only — FSPs may adopt them on a voluntary basis","Conduct Standards only apply to FSPs with more than 50 registered representatives","Conduct Standards only apply after they are formally incorporated into FAIS legislation","Conduct Standards are binding on all affected FSPs from their effective date — non-compliance is a breach"],a:3,e:"Conduct Standards issued under the FSRA are legally binding instruments. Once published and effective, FSPs must comply. They carry the same force as subordinate legislation."},
{t:"Twin Peaks & Regulatory Framework",lv:"C",q:"A representative asks whether they must comply with a new FSCA Conduct Standard even though it has not been incorporated into FAIS. The correct answer is:",o:["No — only the FAIS Act and its Regulations are legally binding on FSPs and representatives","Yes — Conduct Standards issued under the FSRA are legally binding from their effective date","Yes — but only for FSPs with more than 25 registered representatives or R10 million assets under management","No — Conduct Standards only apply once formally incorporated into the FAIS Act or its Regulations"],a:1,e:"Conduct Standards issued under the FSRA are binding instruments of subordinate legislation. They do not need to be incorporated into FAIS to have legal force. Non-compliance is a regulatory breach."},
{t:"Governance & COI Management",lv:"K",q:"The four desired governance outcomes of King IV are:",o:["Profitability, sustainable growth, board accountability, and public transparency","Director independence, executive fairness, regulatory responsibility, and market conduct","Ethical culture, good performance, effective control, and legitimacy","Board independence, audit committee oversight, risk reporting, and stakeholder engagement"],a:2,e:"King IV's four desired outcomes are: (1) ethical culture, (2) good performance, (3) effective control, and (4) legitimacy. These underpin all King IV governance principles and apply to all organisations."},
{t:"Governance & COI Management",lv:"C",q:"Ethical leadership demands which behaviours from a KI?",o:["The KI need only refrain from personally dishonest conduct — ethical duty is a personal standard","The KI must model ethical behaviour, create systems that reward ethics, and hold people accountable","Ethical leadership is fulfilled by ensuring all representatives complete annual ethics training","The KI's ethical obligation is fully discharged by submitting timely compliance reports to the FSCA"],a:1,e:"Ethical leadership is active, not passive. A KI must personally model ethical conduct AND create an environment where ethical behaviour is rewarded and unethical behaviour is consistently addressed."},
{t:"Governance & COI Management",lv:"C",q:"Representatives consistently recommend one insurer's products despite equivalent alternatives paying less commission. As KI, you must:",o:["Allow it provided each representative discloses commission in writing to the client before transacting","Allow it — commission differentials are a normal and accepted feature of the financial services market","Address the conflict of interest: ensure it is disclosed, managed, and does not drive unsuitable recommendations","Allow it provided each representative's individual client file documents product suitability"],a:2,e:"A systemic pattern of recommending higher-commission products raises a conflict of interest the KI must proactively manage. Client suitability must drive recommendations — not commission structure."},
{t:"Governance & COI Management",lv:"C",q:"An FSP's bonus structure pays significantly higher rewards for selling complex, higher-margin products to all client segments. As KI, your primary concern should be:",o:["None — incentive structures are a commercial decision that falls outside the scope of FAIS regulation","Only if individual representatives fail to maintain adequate suitability documentation","Only if client complaint volumes reach a level that attracts formal FSCA supervisory attention","The structure may systematically undermine TCF and the duty to act in clients' interests — it must be reviewed"],a:3,e:"Incentive structures that reward product sales over client outcomes are a conduct risk. The KI must ensure remuneration does not create systemic pressure to provide unsuitable advice."},
{t:"Supervision of Representatives",lv:"K",q:"What does it mean to 'mandate' a representative under FAIS?",o:["Set the representative's annual sales targets and commission earning thresholds","Appoint the representative as an alternate key individual for defined oversight functions","Assign the representative to a specific geographic territory or designated client segment","Formally authorise the representative to render specific financial services within defined product categories"],a:3,e:"Mandating means formally authorising a representative to render specific financial services. The mandate must be within the FSP's own authorised scope — a representative cannot be mandated beyond the FSP's licence."},
{t:"Supervision of Representatives",lv:"C",q:"A supervised representative advises on complex structured products and the KI provides only monthly written feedback. Is this adequate?",o:["Yes — monthly written feedback satisfies all written supervision requirements under FAIS","Yes — provided the supervision agreement has been signed by both the KI and the representative","Yes — frequency of supervision feedback is determined at the representative's discretion","No — supervision must be proportionate to product complexity; complex products require more active oversight"],a:3,e:"Monthly written feedback is insufficient for complex, high-risk products. Supervision intensity must match the risk — more complex products require more frequent, active, and hands-on supervision."},
{t:"Supervision of Representatives",lv:"C",q:"A representative is sequestrated by the High Court. What must the FSP do?",o:["Notify the FSCA within 30 days and allow the representative to continue serving clients while waiting","No immediate action — the FSP must await the outcome of rehabilitation proceedings first","Take appropriate action including potentially suspending the mandate, and notify the FSCA without delay","The representative must self-report to the FSCA; the FSP has no independent reporting obligation"],a:2,e:"Sequestration affects financial soundness — a core fit and proper requirement. The KI must act immediately, consider suspending the mandate, and notify the FSCA without delay."},
{t:"Supervision of Representatives",lv:"K",q:"How long must a supervised representative remain under formal supervision?",o:["6 months for all new representatives regardless of product complexity or prior experience","Until the representative meets all fit and proper requirements for unsupervised practice","Until the representative completes RE5 — supervision automatically ends on passing the exam","3 months for basic products; 12 months for complex investment and pension fund products"],a:1,e:"There is no fixed minimum period in FAIS. Supervision must continue until the representative is assessed as competent and meets all fit and proper requirements. The KI determines when this threshold is met."},
{t:"Supervision of Representatives",lv:"C",q:"A KI delegates day-to-day supervision of 12 representatives to a senior representative. Which statement is correct?",o:["The KI has discharged oversight responsibility by delegating to a suitably capable senior person","The KI remains accountable for the quality of oversight — delegation does not transfer regulatory responsibility","The KI is accountable only if the delegate fails to report a compliance issue within 48 hours","Delegating supervision completely transfers regulatory responsibility to the delegate"],a:1,e:"Delegating supervision is operationally permissible but does not transfer regulatory accountability. The KI must ensure delegated supervision is effective and maintain oversight of the supervisory process itself."},
{t:"TCF & Consumer Outcomes",lv:"K",q:"TCF Outcome 4 requires that advice:",o:["Is suitable and takes account of each individual client's specific circumstances and needs","Is always delivered in writing within 5 business days of the client consultation","Is provided free of charge to clients formally identified as financially vulnerable","Is delivered by a representative with a minimum of 2 years of verified field experience"],a:0,e:"TCF Outcome 4 is about suitability: advice must be tailored to the individual client's needs, financial position, risk profile, and objectives. Generic or one-size-fits-all advice fails this standard."},
{t:"TCF & Consumer Outcomes",lv:"C",q:"A KI finds 78% of complaints over the past year relate to clients not being informed about early termination penalties. What must the KI do?",o:["Identify the root cause, implement systemic disclosure improvements, and monitor to confirm resolution","Refer the issue to the product supplier as a product design and documentation responsibility","Act only once the FSCA formally raises the pattern in a supervisory review or report","Continue resolving complaints individually — TCF does not require systemic KI action"],a:0,e:"TCF requires identifying and addressing systemic issues — not just resolving individual complaints. A recurring pattern reveals a structural failure the KI must fix to prevent ongoing consumer harm."},
{t:"TCF & Consumer Outcomes",lv:"K",q:"Which TCF outcome specifically addresses the design of financial products?",o:["Outcome 2 — products and services are designed to meet identified consumer needs","Outcome 6 — no unreasonable post-sale barriers to switching, claiming, or complaining","Outcome 4 — advice takes account of each client's individual circumstances and needs","Outcome 1 — fair treatment of customers is central to the firm's corporate culture"],a:0,e:"TCF Outcome 2 addresses product design: products must be designed to meet the needs of identified consumer groups. Products structurally unsuitable for their target market breach this outcome."},
{t:"FSP Licensing & Financial Soundness",lv:"K",q:"An FSP licence lapses by operation of law when:",o:["The FSP fails to submit its annual compliance report within the prescribed timeframe","The FSCA discovers a pattern of material non-compliance during a supervisory visit","The sole proprietor FSP owner dies, is permanently incapacitated, or is sequestrated","The FSP does not replace a departed key individual within 15 days of the vacancy"],a:2,e:"An FSP licence lapses automatically by operation of law in circumstances tied to the sole proprietor's legal capacity. These are not discretionary FSCA actions — they occur automatically on the qualifying event."},
{t:"FSP Licensing & Financial Soundness",lv:"C",q:"Sunset Wealth Management can no longer meet its monthly financial obligations. What may the FSCA do?",o:["Immediately withdraw the licence without any further process or notification requirement","Impose conditions and allow time to restore financial soundness, but may withdraw the licence if recovery fails","Take no regulatory action — financial soundness is solely the Prudential Authority's responsibility","Refer the matter directly to a tribunal and await a court order before taking any action"],a:1,e:"The FSCA may impose licence conditions or allow time for recovery. If the FSP fails to restore financial soundness within the required period, the FSCA may withdraw the licence. Immediate withdrawal without due process is not the first step."},
{t:"FSP Licensing & Financial Soundness",lv:"K",q:"Capital adequacy for an FSP means:",o:["Maintaining sufficient capital to absorb potential losses and meet ongoing financial obligations","Achieving the highest possible profit margin and return on equity during each financial year","Having the largest total assets under management relative to direct competitors in the sector","Eliminating all outstanding creditor liabilities on the balance sheet before the financial year-end"],a:0,e:"Capital adequacy is about having a financial buffer — enough capital to absorb unexpected losses and continue meeting obligations to clients and creditors. It protects clients and systemic stability."},
{t:"FSP Licensing & Financial Soundness",lv:"C",q:"An FSP applies to add a new product category to its licence. Which factor is MOST relevant to the FSCA's decision?",o:["The FSP's marketing plan and projected client acquisition strategy for the new category","The number of competitors already licensed to operate in that specific product category","Whether the FSP and its key individuals meet all fit and proper requirements for the new category","The potential commission income the new category will generate for the FSP annually"],a:2,e:"Licence expansion requires demonstrating fit and proper compliance for the new category. Commercial factors — commission potential, competition, and marketing strategy — are not part of the licensing assessment."},
{t:"KI Duties & Responsibilities",lv:"K",q:"A Key Individual may be held personally liable for compliance failures when:",o:["They were entirely unaware of the representative's misconduct at the time","The FSP has more than 10 authorised representatives on its register","They failed to take reasonable steps to prevent foreseeable contraventions","The FSCA has not yet issued a formal directive against the FSP"],a:2,e:"KIs bear personal accountability. Ignorance is not a defence if they failed to implement reasonable oversight structures."},
{t:"KI Duties & Responsibilities",lv:"C",q:"A KI discovers that two of the FSP's representatives have been sharing client passwords to access a third-party investment platform. What is the KI's immediate obligation?",o:["Suspend both representatives and initiate an investigation before any regulatory notification","Issue a written warning and increase monitoring frequency","Refer the matter to the compliance officer and await their recommendation","Report the incident to the FSCA within 15 days as a material irregularity"],a:3,e:"Sharing client credentials is a material irregularity requiring FSCA notification within 15 days. The KI cannot delay for internal investigation before notifying."},
{t:"KI Duties & Responsibilities",lv:"AP",q:"A KI at a small FSP is also its sole representative. Which statement best reflects the regulatory position?",o:["This dual role is permitted provided the FSCA grants written approval each year","A KI may also act as a representative, but must meet F&P requirements for both roles simultaneously","The KI role and representative role are mutually exclusive under FAIS","The FSP must appoint a compliance officer to compensate for the dual role"],a:1,e:"A KI may simultaneously act as a representative. They must meet all F&P requirements for both roles and cannot use one role to excuse compliance gaps in the other."},
{t:"KI Duties & Responsibilities",lv:"C",q:"An FSP has three KIs. One resigns without notice. What is the minimum period within which the FSP must appoint a replacement KI?",o:["Immediately — operations must cease until replaced","Within 90 days, provided the remaining KIs can cover the gap","Within 60 days of the vacancy arising","Within 45 days of the vacancy arising"],a:2,e:"A KI vacancy must be filled within 60 days. If not, the FSP must notify the FSCA and may face licence conditions or suspension of affected services."},
{t:"KI Duties & Responsibilities",lv:"K",q:"Which of the following actions falls OUTSIDE the statutory mandate of a Key Individual?",o:["Overseeing and managing the rendering of financial services","Ensuring representatives meet ongoing fit and proper requirements","Personally rendering financial services to all clients","Ensuring the FSP's operations comply with FAIS and applicable legislation"],a:2,e:"KIs oversee and manage — they do not have a statutory obligation to personally render financial services to all clients. That is a representative function."},
{t:"KI Duties & Responsibilities",lv:"AP",q:"A KI at a large FSP signs off a new representative's onboarding without verifying their RE5 certificate. The representative subsequently renders unsuitable advice. Who bears primary regulatory accountability?",o:["The representative alone — they rendered the advice","The compliance officer who processed the onboarding documents","The FSCA — they issue RE5 certificates and should have flagged the gap","The KI — they approved onboarding without verifying F&P compliance"],a:3,e:"The KI approved onboarding without verifying F&P compliance. Regulatory accountability rests with the KI for failures in the oversight process they are mandated to manage."},
{t:"The Compliance Officer & Compliance Function",lv:"K",q:"Under FAIS, a compliance report must be submitted to the FSCA by:",o:["Within 4 months of the FSP's financial year-end","Within 2 months of the FSP's financial year-end","Within 6 months of the FSP's financial year-end","Within 30 days of the FSP's financial year-end"],a:0,e:"The compliance report must be submitted within 4 months of the FSP's financial year-end — the same deadline as the annual financial statements."},
{t:"The Compliance Officer & Compliance Function",lv:"C",q:"A compliance officer identifies that an FSP's representatives have been accepting gifts above the R1,000 COI threshold without disclosure. The KI argues this is a minor oversight. What is the correct regulatory position?",o:["The KI is correct — undisclosed gifts below R5,000 are administratively minor","The compliance officer must log the breach and include it in the next annual compliance report only","The FSCA must be notified within 5 business days of every undisclosed gift above R1,000","The non-disclosure constitutes a General Code breach requiring immediate remediation and enhanced COI controls"],a:3,e:"Accepting gifts above the R1,000 threshold without disclosure is a General Code breach. Immediate remediation and enhanced controls are required — not deferred to the annual report."},
{t:"The Compliance Officer & Compliance Function",lv:"AP",q:"An FSP uses a risk-based compliance monitoring approach. Which factor should most influence how frequently a specific representative is monitored?",o:["The complexity and volume of the financial services they render","The representative's length of service — longer service means less monitoring","The number of client complaints received by the FSP overall","The representative's academic qualifications and CPD compliance"],a:0,e:"Risk-based monitoring allocates oversight resources based on the complexity and volume of services rendered. A representative advising on complex products with high client volumes requires more frequent monitoring."},
{t:"The Compliance Officer & Compliance Function",lv:"K",q:"Which of the following is a FICA accountable institution obligation when a client relationship is established?",o:["Submit a cash threshold report within 3 days","File an STR within 24 hours of any cash transaction","Register the client's biometric data with the FIC","Conduct customer due diligence before or during onboarding"],a:3,e:"CDD must be conducted before or during the establishment of a business relationship. This is a non-negotiable FICA onboarding obligation for accountable institutions."},
{t:"The Compliance Officer & Compliance Function",lv:"C",q:"A KI is informed that the FSP's FICA risk assessment has not been updated for three years despite significant changes in the client base and product range. What should the KI prioritise?",o:["Commission an external legal opinion before updating the risk assessment","Notify the FIC of the outdated assessment within 5 business days","Update the risk assessment immediately — it must reflect the current business and client profile","Retain the existing assessment — FICA only requires updates every five years"],a:2,e:"FICA requires FSPs to maintain a current, accurate risk assessment. Three years without an update despite business changes is a material compliance gap requiring immediate remediation."},
{t:"The Compliance Officer & Compliance Function",lv:"AP",q:"Which scenario best illustrates effective compliance culture at an FSP?",o:["Compliance monitoring is performed exclusively by the compliance officer with no KI involvement","Representatives self-certify their own compliance with no independent verification","The KI personally reviews a random sample of client files monthly and discusses findings at team meetings","Compliance reports are submitted on time but internal monitoring only occurs when a complaint is received"],a:2,e:"Effective compliance culture requires active KI involvement beyond report submission. Regular file reviews and discussion of findings embed compliance as a business value, not a tick-box exercise."},
{t:"Twin Peaks & Regulatory Framework",lv:"K",q:"The Financial Sector Regulation Act (FSRA) established which regulatory model for South Africa?",o:["Twin Peaks model — Prudential Authority and FSCA with distinct mandates","Single regulator model — one authority for all financial services","Sectoral model — separate regulators for banking, insurance, and investments","Self-regulatory model — industry bodies set and enforce standards"],a:0,e:"The FSRA introduced the Twin Peaks model: the Prudential Authority (safety and soundness) and the FSCA (market conduct and consumer protection)."},
{t:"Twin Peaks & Regulatory Framework",lv:"C",q:"A new Conduct Standard is published under the FSRA. An FSP's KI argues that their existing FAIS licence exempts them from compliance. Is the KI correct?",o:["No — Conduct Standards apply to all licensed FSPs regardless of when the licence was granted","Yes — FAIS licences create a safe harbour against new Conduct Standards","Yes — only FSPs licensed after the Conduct Standard's effective date must comply","No — but FSPs have 24 months to comply with any new Conduct Standard"],a:0,e:"Conduct Standards issued under the FSRA apply to all licensed FSPs. A prior licence creates no exemption. Compliance timelines are set in each Standard — 24 months is not a universal default."},
{t:"Twin Peaks & Regulatory Framework",lv:"K",q:"POPIA's primary regulatory objective in the financial services context is:",o:["To prevent money laundering through personal data controls","To protect individuals' personal information processed by responsible parties","To regulate the cross-border transfer of financial assets","To establish minimum data retention periods for FICA records"],a:1,e:"POPIA protects individuals' personal information. In financial services, it governs how FSPs collect, process, store, and share client data."},
{t:"Twin Peaks & Regulatory Framework",lv:"AP",q:"An FSP operates under both FAIS and the Short-term Insurance Act. A conflict arises between a FAIS General Code requirement and an insurance-specific conduct rule. Which principle applies?",o:["FAIS always supersedes sector-specific legislation","The more onerous requirement applies — the FSP must meet both","The FSP may elect which regime to apply based on the product category","The FSCA issues a binding ruling within 30 days resolving all such conflicts"],a:1,e:"Where multiple regulatory frameworks apply, the FSP must comply with all of them. Where they conflict, the more protective or onerous standard applies unless the legislation expressly provides otherwise."},
{t:"Twin Peaks & Regulatory Framework",lv:"C",q:"The FSCA issues a Guidance Notice regarding the assessment of suitability for retirement products. What is the legal status of a Guidance Notice?",o:["Binding — non-compliance is a criminal offence","Optional — FSPs may adopt alternative approaches without regulatory consequence","Provisional — becomes binding only after a 90-day comment period","Persuasive — indicates the FSCA's interpretation but is not legally binding"],a:3,e:"Guidance Notices are not legally binding but represent the FSCA's authoritative interpretation. Non-compliance is not automatically unlawful, but FSPs ignore them at regulatory risk."},
{t:"Supervision of Representatives",lv:"K",q:"Under FAIS, who bears the primary responsibility for supervising representatives rendering services under supervision?",o:["The FSCA, through its routine supervision programme","The FSP and its Key Individual","The compliance officer appointed by the FSP","The representative's designated mentor or buddy"],a:1,e:"The FSP and its KI bear primary statutory responsibility for supervising representatives. This cannot be fully delegated to a compliance officer or mentor."},
{t:"Supervision of Representatives",lv:"C",q:"A representative under supervision renders financial services without their supervisor's prior approval on three occasions. What is the correct course of action for the KI?",o:["Issue a verbal warning and increase the frequency of monitoring going forward","Notify the FSCA immediately of every single unsupervised service rendering that occurred","Extend the supervision period automatically by a further six months","Document the breaches and assess whether supervision remains appropriate — debarment may follow"],a:3,e:"Repeated unsupervised service rendering is a serious breach. The KI must document the breaches and assess whether the supervision arrangement is viable. Debarment may be warranted if the conduct persists."},
{t:"Supervision of Representatives",lv:"K",q:"A representative's supervision period has ended. What must happen before they can render services independently?",o:["The FSCA must issue a certificate of competence","The representative must pass a practical assessment administered by their product provider","The KI must confirm in writing that all F&P requirements are met and remove the supervision condition","The compliance officer must file a supervision completion report with the FSCA within 30 days"],a:2,e:"The KI must confirm in writing that all F&P requirements are met. Only then can the supervision condition be lifted and the representative render services independently."},
{t:"Supervision of Representatives",lv:"AP",q:"A supervised representative is highly productive and generates significant revenue. The KI is reluctant to remove the supervision condition as the arrangement allows closer oversight. What is the regulatory issue?",o:["Supervision must end once F&P requirements are met — it cannot be used as a permanent control mechanism","There is no regulatory issue — KIs may extend supervision indefinitely","The representative can apply to the FSCA to self-certify F&P compliance after 12 months","The FSP must obtain client consent before extending supervision beyond the standard period"],a:0,e:"Supervision is a time-limited arrangement to ensure F&P requirements are met. Once requirements are met, supervision must end. Using it as a permanent control tool is a misapplication of the framework."},
{t:"Supervision of Representatives",lv:"C",q:"A representative discloses to their KI that they are facing personal insolvency proceedings. What must the KI do?",o:["Keep the disclosure fully confidential — personal financial matters are always private","Issue a temporary suspension pending the outcome of the insolvency proceedings against them","Assess the impact on fit and proper status and notify the FSCA if a licence condition is triggered","Reduce the representative's client portfolio to only lower-risk product categories"],a:2,e:"Insolvency affects fit and proper status. The KI must assess the impact and notify the FSCA if the representative no longer meets the financial soundness requirement."},
{t:"Supervision of Representatives",lv:"AP",q:"A KI at a national FSP has 47 representatives across four provinces. She delegates day-to-day supervision to four regional managers who are not KIs. Which statement is correct?",o:["This is not permitted — only KIs may supervise representatives under FAIS","Delegation is permitted, but the KI retains full regulatory accountability for supervisory outcomes","The regional managers must be appointed as KIs within 6 months of taking on supervision","The FSCA must approve each delegation arrangement before it takes effect"],a:1,e:"A KI may delegate operational supervision to qualified staff. However, the KI cannot delegate regulatory accountability. If supervision fails, the KI remains answerable to the FSCA."},
{t:"Governance & COI Management",lv:"K",q:"King IV applies on an 'apply and explain' basis. What does this mean for an FSP?",o:["FSPs must apply every principle exactly as written or face regulatory sanction","FSPs choose which principles to apply based on their size and risk profile","FSPs must either apply each principle or explain why it is not applicable to their context","Principles are aspirational — no explanation is required if they are not applied"],a:2,e:"'Apply and explain' means every principle must be applied and the FSP must explain HOW it is applied. This replaced the 'comply or explain' model of King III."},
{t:"Governance & COI Management",lv:"K",q:"King IV identifies four governance outcomes. Which option correctly lists all four?",o:["Transparency, accountability, fairness, sustainability","Ethical culture, good performance, effective control, legitimacy","Ethical culture, good performance, effective control, sustainability","Transparency, accountability, responsibility, fairness"],a:1,e:"The four King IV governance outcomes are: (1) Ethical culture, (2) Good performance, (3) Effective control, (4) Legitimacy. These outcomes guide the governing body's direction and conduct."},
{t:"Governance & COI Management",lv:"C",q:"A board member of an FSP also holds a directorship at a competing product provider. Under King IV, what is the governing body's primary obligation?",o:["The conflict is automatically disqualifying — the board member must resign immediately","The conflict must be disclosed, recorded in the COI register, and managed to prevent prejudice to the FSP","King IV only addresses conflicts between board members and regulators, not between competing FSPs","The FSCA must approve the dual directorship before it takes effect"],a:1,e:"King IV requires disclosure and management of conflicts of interest. The board member discloses, recuses from affected decisions, and the conflict is recorded. Automatic disqualification is not the primary obligation."},
{t:"Governance & COI Management",lv:"AP",q:"An FSP's KI notices that senior management consistently excludes the compliance function from strategic planning meetings. Which King IV principle is most directly at risk?",o:["The principle of stakeholder inclusivity, since staff feel excluded from decisions","The principle of effective control — compliance oversight is being structurally undermined","The principle of legitimacy, since clients are not being kept informed of the change","The principle of good performance — financial targets are being prioritised instead"],a:1,e:"Excluding compliance from strategic planning undermines the governing body's effective control obligation under King IV. The compliance function must be integrated into governance, not siloed."},
{t:"Governance & COI Management",lv:"K",q:"Under King IV, which body holds ultimate accountability for organisational ethics at an FSP?",o:["The governing body (board of directors)","The Ethics Officer or Compliance Officer","The FSCA through its conduct supervision mandate","Senior management collectively"],a:0,e:"King IV places ultimate accountability for organisational ethics with the governing body. Ethics is a governance matter, not merely a management or compliance function."},
{t:"Governance & COI Management",lv:"C",q:"A KI is pressured by the FSP's shareholders to approve a product that generates high commission but has been identified internally as potentially unsuitable for the target market. Which King IV principle most directly applies?",o:["Ethical culture — the governing body must refuse to approve products that compromise client interests","Good performance — shareholder returns are a legitimate governance priority","Legitimacy — the FSP must be seen to act in the interests of all stakeholders","Effective control — approval processes must be documented regardless of outcome"],a:0,e:"Ethical culture under King IV requires the governing body to lead with integrity. Approving a product identified as unsuitable to satisfy shareholders violates the ethical leadership obligation."},
{t:"Governance & COI Management",lv:"AP",q:"Under King IV's integrated thinking approach, an FSP's strategy should consider:",o:["Financial, social, human, intellectual, manufactured, and natural capitals collectively","Financial capital only — other forms of capital are the responsibility of the sustainability report","Financial and human capital, as these directly affect regulatory compliance","Financial capital and reputational capital as defined by the FSCA"],a:0,e:"King IV's integrated thinking approach requires FSPs to consider all six capitals: financial, manufactured, intellectual, human, social/relationship, and natural. Strategy must account for all of them."},
{t:"Governance & COI Management",lv:"C",q:"A whistleblower report alleges that an FSP's KI has been falsifying CPD records for the past two years. Under King IV, what is the governing body's first obligation?",o:["Suppress the report internally until a preliminary review has concluded","Ensure the report is independently investigated and the whistleblower is protected from retaliation","Refer the matter to the FSCA immediately without any internal investigation","Consult legal counsel before deciding whether an investigation is warranted at all"],a:1,e:"King IV requires the governing body to ensure independent investigation of allegations and protect whistleblowers. Suppression or premature external referral without investigation both violate governance obligations."},
{t:"TCF & Consumer Outcomes",lv:"K",q:"TCF Outcome 1 requires that:",o:["Products perform as firms have led customers to expect","Customers are provided with clear information before, during and after the point of sale","Customers do not face unreasonable post-sale barriers","Fair treatment of customers is central to the corporate culture"],a:3,e:"TCF Outcome 1: fair treatment of customers is central to the corporate culture of FSPs. This is the foundational outcome from which all others flow."},
{t:"TCF & Consumer Outcomes",lv:"C",q:"An FSP's analysis shows that 65% of clients who purchased a specific product surrendered it within 18 months, incurring significant early termination penalties. Which TCF outcome is most directly implicated?",o:["Outcome 1 — cultural failure at the FSP","Outcome 5 — the product is not performing as clients were led to expect","Outcome 3 — clients were not given clear pre-sale information about termination costs","Outcome 6 — unreasonable post-sale barriers are preventing clients from switching"],a:2,e:"A 65% early surrender rate suggests clients were not adequately informed about termination penalties before purchase. TCF Outcome 3 addresses clear, timely communication at and before point of sale."},
{t:"TCF & Consumer Outcomes",lv:"K",q:"TCF Outcome 6 specifically protects customers from:",o:["Unsuitable product recommendations made without a proper needs analysis","Unreasonable post-sale barriers to switching, complaining, or claiming","Unclear communication given during the pre-sale advice process","Products that underperform their originally projected returns"],a:1,e:"TCF Outcome 6: customers do not face unreasonable post-sale barriers to changing product, switching providers, submitting a claim, or making a complaint."},
{t:"TCF & Consumer Outcomes",lv:"AP",q:"A KI reviews the FSP's complaints data and finds that 80% of complaints are resolved in the FSP's favour with no remediation offered. What should the KI consider first?",o:["The complaints resolution rate confirms the FSP's processes are effective","Whether to outsource complaints handling to an independent third party","Whether to increase the complaints threshold above which remediation is considered","Whether the complaints resolution process is genuinely fair or designed to minimise payouts"],a:3,e:"An 80% FSP-favourable resolution rate warrants scrutiny of whether the process is genuinely fair. TCF requires that complaints handling outcomes reflect real fairness, not a process designed to protect the FSP."},
{t:"TCF & Consumer Outcomes",lv:"C",q:"An FSP uses a standard script for all client interactions regardless of the client's financial literacy level. A KI identifies this as a TCF risk. Which outcome is most at risk?",o:["Outcome 3 — communication is not clear and appropriate to each client's needs","Outcome 2 — products are not designed for identified client groups","Outcome 5 — products will not perform as clients expect","Outcome 1 — the firm's culture does not prioritise fair treatment"],a:0,e:"TCF Outcome 3 requires that communications are clear, fair, and not misleading — and appropriate to the client's understanding. A one-size-fits-all script fails clients with lower financial literacy."},
{t:"FSP Licensing & Financial Soundness",lv:"K",q:"Which of the following is NOT a category of financial service under FAIS for licensing purposes?",o:["Product manufacturing","Advice","Intermediary services","Discretionary fund management"],a:0,e:"FAIS licences cover advice and intermediary services. Product manufacturing (creating financial products) is not a category of financial service under FAIS — it is regulated under product-specific legislation."},
{t:"FSP Licensing & Financial Soundness",lv:"C",q:"An FSP's liquid assets fall below the required capital adequacy threshold due to a poor investment decision. What is the correct immediate action?",o:["Notify the FSCA immediately and submit a remediation plan","Continue operations and restore capital within the next financial year","Suspend all new client onboarding until capital is restored","The FSCA is automatically notified through the annual financial statements"],a:0,e:"A breach of capital adequacy must be reported to the FSCA immediately — not deferred to annual reporting. The FSP must also submit a plan to restore compliance within the required period."},
{t:"FSP Licensing & Financial Soundness",lv:"K",q:"A Category I FSP's licence is automatically suspended when:",o:["Its KI resigns and no replacement is appointed within 30 days","It fails to submit its annual compliance report on time","Its PI insurance lapses and is not renewed within the required period","The FSP changes its trading name without FSCA approval"],a:2,e:"Failure to maintain valid professional indemnity insurance triggers automatic licence suspension. PI cover is a continuous F&P requirement — lapsing it is not merely an administrative breach."},
{t:"FSP Licensing & Financial Soundness",lv:"AP",q:"An FSP wants to add Category II (discretionary) services to its existing Category I licence. What is the primary consideration the FSCA will assess?",o:["Whether the FSP's KIs and reps meet the added F&P requirements for Category II","The FSP's marketing strategy and its projected client acquisition targets for the year","The FSP's current client satisfaction scores and survey feedback ratings","Whether the FSP has been in continuous operation for at least three full years"],a:0,e:"Adding a licence category requires demonstrating that KIs and representatives meet the F&P requirements for that category — including appropriate qualifications, experience, and RE examinations."},
{t:"FSP Licensing & Financial Soundness",lv:"C",q:"An FSP's audited financial statements reveal that its liabilities exceed its assets. The KI argues the FSP is still trading and clients are not at risk. What is the regulatory reality?",o:["The KI is correct — solvency is something that is only assessed formally at year-end","The FSP is technically insolvent and must notify the FSCA immediately, regardless of trading","The FSCA only acts once an FSP is actually unable to meet a specific client obligation","The Prudential Authority, and not the FSCA, is the body that handles FSP insolvency matters"],a:1,e:"An FSP whose liabilities exceed assets is insolvent and does not meet financial soundness requirements. This must be reported to the FSCA immediately — ongoing trading does not cure the regulatory breach."},
{t:"FAIS Ombud & Complaints",lv:"K",q:"The FAIS Ombud has jurisdiction to investigate complaints against:",o:["All financial institutions including banks and medical schemes","Any entity regulated by either the FSCA or the Prudential Authority","Only JSE-listed financial services companies","Only FSPs licensed under FAIS"],a:3,e:"The FAIS Ombud's jurisdiction is limited to FSPs licensed under the FAIS Act. Complaints against banks fall under the Banking Ombud; medical schemes under the Council for Medical Schemes."},
{t:"FAIS Ombud & Complaints",lv:"K",q:"What is the FAIS Ombud's maximum financial award per complaint?",o:["R1,000,000","R3,500,000","R2,500,000","R10,000,000"],a:1,e:"The FAIS Ombud may award up to R3,500,000 per complaint. Awards exceeding this require the respondent FSP's written consent."},
{t:"FAIS Ombud & Complaints",lv:"C",q:"A client submits a complaint to the FAIS Ombud without first lodging it with the FSP. What will the Ombud typically do?",o:["Accept the complaint immediately — the FSP's internal process is optional","Refer the client back to the FSP's internal complaints process first","Dismiss the complaint as procedurally defective","Investigate the complaint but reduce any potential award by 50%"],a:1,e:"The Ombud typically requires complainants to exhaust the FSP's internal complaints process first. Only if the internal process fails or produces an unsatisfactory outcome will the Ombud accept the complaint."},
{t:"FAIS Ombud & Complaints",lv:"K",q:"A FAIS Ombud determination that is not appealed within the prescribed period has the same legal force as:",o:["An FSCA directive","A High Court civil judgement","An arbitration award","A Financial Services Tribunal ruling"],a:1,e:"An unappealed FAIS Ombud determination has the force of a High Court civil judgement. The complainant may apply for a writ of execution if the FSP does not comply."},
{t:"FAIS Ombud & Complaints",lv:"C",q:"An FSP receives a complaint that a representative recommended an unsuitable product. The FSP's internal investigation concludes the recommendation was appropriate. The client escalates to the FAIS Ombud. What should the KI ensure?",o:["The FSP's legal team engages with the Ombud's office to suppress the investigation","All complaint records, advice documentation, and ROAs are preserved and readily available","The representative's employment is terminated to demonstrate the FSP takes complaints seriously","The FSP submits a counter-complaint against the client to the FSCA"],a:1,e:"When a complaint is escalated to the Ombud, the FSP must be prepared to provide all relevant documentation — ROAs, suitability assessments, client records. Preservation of evidence is the KI's priority."},
{t:"FAIS Ombud & Complaints",lv:"AP",q:"A KI reviews the FSP's complaints log and finds that 12 of the last 15 complaints relate to the same product category. What should the KI do beyond resolving individual complaints?",o:["Remove the product category from the FSP's licence to avoid further exposure","Report the pattern to the FSCA as a material irregularity within 15 days","Increase the excess on the FSP's PI insurance policy to cover future claims","Conduct a root cause analysis and implement systemic remediation across all affected clients"],a:3,e:"A pattern of complaints in the same category indicates a systemic issue. The KI must conduct a root cause analysis and consider whether all clients in that category were affected — not just those who complained."},
{t:"FAIS Ombud & Complaints",lv:"AN",q:"A complainant receives the FAIS Ombud's determination awarding R2,800,000 in her favour. The FSP's legal team argues the determination exceeded the Ombud's jurisdiction because the loss exceeded R2,500,000. Is this argument valid?",o:["No — the Ombud's financial cap is R3,500,000. R2,800,000 falls within jurisdiction","Yes — the Ombud's jurisdiction is capped at R2,500,000 per complaint","Yes — the Ombud may only award amounts the FSP has pre-approved","No — there is no financial cap on FAIS Ombud awards in cases involving dishonesty"],a:0,e:"The FAIS Ombud's financial cap is R3,500,000 — not R2,500,000. R2,800,000 falls within jurisdiction. The FSP's argument is factually incorrect."},
{t:"FAIS Ombud & Complaints",lv:"AN",q:"An FSP receives 14 complaints in 3 months all relating to the same representative's recommendations on a specific product. The KI resolves each complaint individually with small ex gratia payments. What is the MOST significant regulatory failure?",o:["The ex gratia payments create tax liabilities that must formally be reported to SARS","Each complaint should have been escalated to the FAIS Ombud before any resolution","The KI failed to treat the pattern as a systemic issue needing root cause analysis and remediation","The complaints were resolved too quickly — the Code requires a minimum 30-day resolution period"],a:2,e:"Individual resolution of a pattern without root cause analysis is a systemic TCF and compliance failure. The KI must investigate whether all clients who received similar advice were harmed — not just those who complained."},
{t:"FAIS Ombud & Complaints",lv:"AP",q:"A client lodges a complaint with the FAIS Ombud 8 months after receiving the FSP's final response rejecting her complaint. The Ombud's office advises her the complaint may be time-barred. What is the correct position?",o:["The complaint is automatically dismissed — 6 months is a hard deadline","The 6-month period only applies to monetary complaints — service complaints have a 12-month window","The complaint can proceed only if the FSP consents to waive the time-bar","The Ombud has discretion to condone late filing if the complainant demonstrates good cause"],a:3,e:"The 6-month period is standard but the Ombud retains discretion to condone late complaints where good cause is shown. It is not an automatic dismissal."},
{t:"FAIS Ombud & Complaints",lv:"AP",q:"During an Ombud investigation, the FSP cannot locate the original ROA and needs analysis for the transaction in dispute. What is the likely consequence?",o:["The Ombud will draw an adverse inference — inability to produce the ROA undermines the FSP's suitability defence","No consequence — document loss is common and cannot attract penalty","The client automatically wins — missing documentation is treated as an admission of liability","The FSCA will immediately suspend the FSP's licence pending a records audit"],a:0,e:"Missing documentation severely damages the FSP's position. The FSP bears the burden of demonstrating suitability — without an ROA, that burden cannot be discharged and the Ombud draws adverse inferences."},
{t:"FAIS Ombud & Complaints",lv:"C",q:"An FSP's internal complaints policy states all complaints must receive a final response within 45 days. A complainant receives no response after 60 days and approaches the Ombud. What should the Ombud's office advise?",o:["The FSP breached its own policy — a non-response within the stated period is treated as a final rejection","The complainant must wait the full 45 days measured from the date the policy was adopted","The Ombud cannot accept the complaint until the FSP formally closes it in writing first","The complainant must send a registered letter demanding a response before the Ombud may intervene"],a:0,e:"Failure to respond within the FSP's stated timeframe is treated as a final response for Ombud purposes. The complainant need not wait indefinitely."},
{t:"FAIS Ombud & Complaints",lv:"C",q:"A KI implements a complaints log reviewed monthly at governance level that records all complaints, outcomes, remediation steps, and root cause findings. What does this BEST demonstrate?",o:["TCF Outcome 6 — removing post-sale barriers to complaining","A tick-box compliance exercise","An unnecessarily bureaucratic process that slows complaint resolution","Effective embedding of TCF and complaints governance at KI level"],a:3,e:"Monthly governance-level review of a detailed complaints log with root cause analysis demonstrates genuine TCF embedding. This is the standard the FSCA expects from KIs managing complaints governance."},
{t:"KI Duties & Responsibilities",lv:"AN",q:"A KI is aware that one of the FSP's top-performing representatives has received 4 client complaints in 6 months but has not investigated because the representative generates 35% of FSP revenue. An FSCA inspection uncovers this. What is the MOST likely regulatory finding?",o:["The KI acted appropriately — a pattern of 6 complaints is required before investigation is triggered","The KI bears no personal accountability at all — this responsibility belongs to the compliance officer","The FSCA can only act where clients have suffered an actual, quantifiable financial loss","The KI breached supervision — commercial considerations cannot override the duty to investigate"],a:3,e:"Revenue generation is irrelevant to supervision obligations. A KI who ignores complaints for commercial reasons has fundamentally breached the oversight mandate. Personal accountability attaches directly to the KI."},
{t:"KI Duties & Responsibilities",lv:"AN",q:"A KI discovers 3 representatives have been conducting discretionary portfolio management — a Category II activity — without the FSP holding the appropriate licence. She stops the activity and self-reports to the FSCA. How does self-reporting typically affect the outcome?",o:["Self-reporting has no legal effect — penalties are fixed regardless of disclosure","Self-reporting is a significant mitigating factor the FSCA considers in determining remedial action","Self-reporting is mandatory and failure to do so is a separate offence — it does not reduce penalties","Self-reporting triggers automatic licence suspension pending investigation"],a:1,e:"Voluntary self-reporting is a significant mitigating factor in FSCA enforcement. While it does not eliminate consequences, it demonstrates governance integrity and typically results in less severe remedial action."},
{t:"KI Duties & Responsibilities",lv:"AP",q:"A KI is asked by the FSP's board to sign off on a new incentive structure that pays representatives significantly more for selling high-margin in-house products. She believes this creates TCF risk but the board overrules her. What is her correct course of action?",o:["Sign off regardless — the board holds full authority over the KI on commercial matters","Resign immediately from her position as Key Individual at the FSP","Accept the board's decision — regulatory accountability rests solely with the board","Document her objection in board minutes and consider whether FSCA notification is required"],a:3,e:"The KI must formally document her objection in board minutes — this creates a record of her dissent. If the structure represents a material change, notification to the FSCA may be required. Resignation is not the first step."},
{t:"KI Duties & Responsibilities",lv:"AP",q:"A KI receives a whistleblower report alleging that the FSP's compliance officer has been falsifying compliance reports submitted to the FSCA. The KI is close friends with the compliance officer. What must the KI do?",o:["Investigate discreetly and only escalate the matter if the evidence turns out to be conclusive","Dismiss the report outright — whistleblower reports require corroboration before any action is taken","Refer to independent investigation and report to the FSCA if substantiated — the friendship is irrelevant","Consult legal counsel first and act only in line with their advice regarding disclosure"],a:2,e:"The KI's personal relationship is entirely irrelevant. Allegations of falsified regulatory reports must be independently investigated. Substantiated findings must be reported to the FSCA."},
{t:"KI Duties & Responsibilities",lv:"C",q:"A KI is responsible for 4 categories of authorisation. A new F&P Determination raises the qualification standard for one category. The KI meets the standard for 3 but not the 4th. What must happen?",o:["The KI may continue operating across all 4 categories during a formal transition period","The KI must immediately cease all operations until she is compliant in all 4 categories","The FSP must permanently appoint a second KI to specifically cover the 4th category","She may continue in the 3 categories where she meets requirements, but must cease in the 4th"],a:3,e:"F&P requirements apply per category. A KI may operate in categories where she meets requirements while working to meet the standard in others. She cannot operate where she does not meet the applicable standard."},
{t:"KI Duties & Responsibilities",lv:"C",q:"A KI is simultaneously the sole representative at a small FSP. She takes 3 weeks annual leave with no substitute arrangement. What is the regulatory consequence?",o:["No consequence at all — sole proprietors are fully exempt from continuity requirements","The FSP must cease rendering services during her absence unless a suitable arrangement is made","She can simply delegate her KI responsibilities to a senior administrator instead","The FSP must notify all clients 30 days in advance of any planned service interruption"],a:1,e:"Where a KI is the sole representative, absence without a substitute means no authorised person exists to render financial services. The FSP must cease operations or make adequate substitute arrangements."},
{t:"Governance & COI Management",lv:"AN",q:"An FSP's board approves expansion into a high-risk market despite the risk officer's formal objection that FICA compliance cannot be assured. Six months later the FSP is sanctioned by the FIC. Under King IV, what governance failure is MOST directly implicated?",o:["Outcome 3 — effective control was compromised when the board overruled a substantiated risk concern","Outcome 2 — good performance was prioritised ahead of maintaining effective control measures","Outcome 4 — legitimacy was not adequately considered as part of the expansion decision","The board fully discharged its duties simply by noting the objection — no King IV failure occurred"],a:0,e:"Overruling a substantiated compliance risk without adequate mitigation is a failure of effective control (King IV Outcome 3). Noting an objection is not the same as discharging the governance obligation."},
{t:"Governance & COI Management",lv:"AN",q:"A KI discovers that the FSP's performance management system rates representatives primarily on revenue with minimal weighting for compliance and client outcomes. Under King IV, what is the board's obligation?",o:["The board has fully discharged its duty simply by hearing and noting the KI's concern","The board must design governance structures, including performance systems, that reinforce ethics","Revenue-driven performance management is considered standard industry practice, not a governance issue","The compliance officer alone must redesign the system — this is outside of the board's mandate"],a:1,e:"King IV places the design of ethical governance structures — including incentive and performance systems — squarely with the governing body. A revenue-only performance system that ignores compliance and client outcomes is a structural ethics failure."},
{t:"Governance & COI Management",lv:"AP",q:"A board member holds shares in a product provider whose products the FSP recommends. She has declared the interest but continues to participate in meetings where those products are discussed. Under King IV, is her conduct appropriate?",o:["Yes — disclosure alone fully satisfies the conflict-of-interest obligation under King IV","No — disclosure is only step one; she must also recuse herself from affected decisions","Yes — she may participate freely provided she simply does not vote on the decision","No — she must divest her entire shareholding before the very next board meeting"],a:1,e:"King IV requires disclosure AND management of conflicts. Management means recusal from affected decisions — not just abstaining from voting. Remaining during deliberations on a matter where she has a declared interest is insufficient."},
{t:"Governance & COI Management",lv:"AP",q:"An FSP implements a clawback policy allowing recovery of commissions where advice is subsequently found to be unsuitable. Which King IV principle does this BEST demonstrate?",o:["Legitimacy — the FSP is seen to act in the public interest","Effective control — governance mechanisms create accountability for outcomes","Good performance — financial sustainability is protected","Ethical culture — the policy creates a cultural signal that unsuitable advice has consequences"],a:3,e:"A clawback policy creates a direct consequence for unsuitable advice — signalling that ethical behaviour has tangible incentives and unethical behaviour has tangible consequences. This is a structural mechanism for embedding ethical culture (King IV Outcome 1)."},
{t:"Governance & COI Management",lv:"C",q:"An FSP's speak-up policy exists but managers consistently dismiss concerns and no escalations have been acted upon in 3 years. What does this indicate under King IV?",o:["The policy is fully effective — an absence of escalations simply means there are no issues","This is purely a management failure — the board discharged its duty by approving the policy","Representatives should escalate directly to the FSCA instead of using any internal channels","The policy exists on paper but ethical culture hasn't been embedded — King IV requires demonstrable outcomes"],a:3,e:"King IV requires demonstrable outcomes — not just policy documents. A speak-up policy with no recorded escalations over 3 years indicates the culture has not been embedded. The board must monitor whether governance structures are actually functioning."},
{t:"Governance & COI Management",lv:"C",q:"Under King IV's integrated thinking approach, which should be included in an FSP's strategic planning process?",o:["Financial capital projections only, since this is the primary measure investors care about","Reputational capital only, as this most directly affects regulatory standing","Financial and human capital only, since these directly affect day-to-day operations","Strategy must reflect all six capitals — financial, human, social, intellectual, manufactured, natural"],a:3,e:"King IV's integrated thinking requires all six capitals to inform strategy. An FSP strategy that ignores its impact on human capital, social capital, or other capitals fails the integrated thinking test."},
{t:"Supervision of Representatives",lv:"AN",q:"A KI discovers that a supervisor has been signing off on client files without reviewing them — rubber-stamping the supervised representative's work. What is the MOST accurate regulatory characterisation?",o:["Both the supervisor and KI bear accountability — the KI failed to oversee the supervision arrangement","The supervisor is in breach, but the KI has no personal liability since she didn't personally approve it","The FSP bears sole institutional liability for the rubber-stamping that occurred","The supervised representative is solely accountable since the supervisor's signature was used without permission"],a:0,e:"The KI has a duty to oversee supervision arrangements — not just appoint supervisors. A rubber-stamp arrangement the KI failed to detect through monitoring constitutes a failure of the KI's own oversight obligation."},
{t:"Supervision of Representatives",lv:"AN",q:"A supervised representative has passed RE5, obtained the required qualification, and completed 12 months of supervision. Her supervisor wants to extend supervision by 6 months 'to be safe.' The representative objects. Who is correct?",o:["The supervisor — extending supervision beyond requirements is always the safer, more prudent choice","The KI must personally arbitrate the dispute, regardless of whether requirements have technically been met","The FSCA must formally approve any extension of supervision beyond the standard 12-month period","The representative — once F&P requirements are met, supervision cannot be an open-ended control"],a:3,e:"Supervision bridges the gap until F&P requirements are met. Once all requirements are satisfied, the arrangement must end. Using supervision as a permanent precautionary measure is a misapplication of the framework."},
{t:"Supervision of Representatives",lv:"AP",q:"An FSP has 85 representatives in 6 provinces. The KI implements a risk-based supervision framework allocating more intensive oversight to representatives with complex products and higher complaint rates. Is this approach compliant?",o:["No — every representative must receive an identical, undifferentiated level of supervision","No — the FSCA prescribes fixed supervision ratios that FSPs cannot vary under any circumstances","Yes — risk-based supervision matches regulatory expectations, provided minimum standards are met","Yes — but only once the FSCA has formally pre-approved the framework in writing beforehand"],a:2,e:"Risk-based supervision is not only compliant — it is the preferred approach. The FSCA expects KIs to allocate oversight resources intelligently based on risk. Minimum standards must apply to all, but higher-risk representatives warrant enhanced monitoring."},
{t:"Supervision of Representatives",lv:"AP",q:"A representative under supervision is involved in a pending criminal charge of reckless driving. Must the KI take any action in relation to the supervision arrangement?",o:["No — the charge relates to purely private conduct entirely unrelated to financial services work","Yes — the KI must immediately debar the representative pending the outcome of the criminal case","Yes — the KI must assess whether the charge affects F&P, particularly honesty and integrity, and monitor it","No — only finalised criminal convictions are capable of triggering any F&P assessment at all"],a:2,e:"Pending criminal charges must be assessed for F&P impact. The KI must make a reasoned assessment — certain charges involving dishonesty would require more immediate action. Monitoring is mandatory regardless."},
{t:"Supervision of Representatives",lv:"C",q:"A representative completes supervision and the KI provides written confirmation that all F&P requirements are met. Three months later, the representative's qualification is found to have been fraudulently obtained. What is the KI's position?",o:["The KI has absolutely no liability whatsoever — she acted entirely in good faith throughout","The KI may face scrutiny if reasonable verification steps weren't taken — good faith isn't an absolute defence","The FSCA must first prove the KI actually knew about the fraud before any action can be taken","The FSP bears full institutional liability — the KI's confirmation carries no personal weight"],a:1,e:"Good faith is mitigating but not an absolute defence. The KI has a duty to take reasonable verification steps before signing off. Inadequate verification creates regulatory exposure despite acting in good faith on fraudulent information."},
{t:"Supervision of Representatives",lv:"C",q:"A representative discloses to her supervisor that she has been sequestrated. The supervisor mentions it to a colleague but does not formally report it to the KI. The FSCA discovers this during an inspection. What breaches occurred?",o:["Only the representative breached — she personally should have reported directly to the KI herself","The KI breached only — supervisors carry no independent reporting obligations of their own","No breach occurred — sequestration is a personal matter not requiring any regulatory disclosure","The supervisor breached the duty to escalate — it should have been reported to the KI immediately"],a:3,e:"The supervisor had a clear duty to escalate the sequestration disclosure to the KI immediately. Sequestration directly affects financial soundness as a F&P requirement. The KI must then assess the impact and notify the FSCA if the representative no longer meets requirements."},
{t:"Twin Peaks & Regulatory Framework",lv:"AN",q:"An FSP receives conflicting instructions from the Prudential Authority and the FSCA regarding a specific practice. What is the correct approach?",o:["Follow the FSCA instruction only — market conduct always takes precedence for FSPs specifically","Follow the Prudential Authority instruction — prudential soundness is always the primary concern","Seek urgent clarity from both regulators, document communications, and suspend the practice pending resolution","Choose whichever instruction is least disruptive to the FSP's ongoing business operations"],a:2,e:"Conflicting regulatory instructions require immediate engagement with both regulators. The FSP must seek urgent written clarity, document all communications, and may suspend the practice pending resolution. Unilaterally choosing one regulator creates risk."},
{t:"Twin Peaks & Regulatory Framework",lv:"AN",q:"A fintech company provides automated investment recommendations via an algorithm with no human representative involvement. A client suffers losses. Does FAIS apply?",o:["No — FAIS only ever applies where a human representative is personally involved","No — automated advice is regulated solely under the Electronic Communications Act","Yes — if the algorithm performs the function of advice, the operating entity needs FAIS authorisation","Yes — but only if the algorithm was originally designed by an already-licensed FSP"],a:2,e:"FAIS regulates the function — not the method of delivery. If an automated system performs the function of advice, the entity operating it must be authorised as an FSP. The FSCA has confirmed that robo-advice is subject to FAIS."},
{t:"Twin Peaks & Regulatory Framework",lv:"AP",q:"POPIA's Information Regulator has the power to issue enforcement notices and administrative fines. An FSP suffers a data breach affecting 3,000 client records due to inadequate cybersecurity. What are the potential POPIA consequences?",o:["POPIA has no financial penalties at all — only reputational consequences ever apply here","The FSP must pay a standard R1,000 per affected client as automatic compensation","The FSP may face fines up to R10,000,000 or imprisonment, plus mandatory breach notification","POPIA only applies where breaches are deliberate — negligent data loss is not actionable at all"],a:2,e:"POPIA provides for administrative fines of up to R10,000,000 AND/OR criminal penalties. Negligent breaches are fully actionable. The FSP must notify the Information Regulator and affected data subjects as soon as reasonably possible."},
{t:"Twin Peaks & Regulatory Framework",lv:"AP",q:"An FSP operating under FAIS is also subject to a new Conduct Standard issued under the FSRA that conflicts with an existing FAIS General Code provision. Which takes precedence?",o:["The more protective requirement applies — where genuine conflict exists, greater protection governs","FAIS always takes precedence over any Conduct Standard, as it is the primary regulatory framework","The Conduct Standard automatically takes precedence, since it represents the more recent legislation","The KI must apply to the FSCA for a formal binding ruling before operating under either standard"],a:0,e:"Where regulatory frameworks conflict, the more protective or onerous requirement applies unless legislation expressly provides otherwise. The FSP must comply with both frameworks simultaneously where possible."},
{t:"Twin Peaks & Regulatory Framework",lv:"C",q:"A Guidance Notice issued by the FSCA indicates that representatives should disclose all product costs in rand amounts — not only percentages. An FSP argues it need not comply because Guidance Notices are not legally binding. What is the correct position?",o:["The FSP is fully correct — Guidance Notices carry absolutely no legal force whatsoever","The FSP must comply immediately — Guidance Notices carry the same legal force as Conduct Standards","The FSP may use its own methodology, provided it notifies the FSCA of this choice in writing","Technically correct that Guidance Notices aren't binding, but ignoring them creates real regulatory risk"],a:3,e:"Guidance Notices are not legally binding but represent the FSCA's authoritative interpretation. The FSCA uses them to signal enforcement priorities. FSPs that ignore them do so at significant regulatory risk."},
{t:"FSP Licensing & Financial Soundness",lv:"AN",q:"An FSP's KI discovers mid-year that liquid assets have dropped below the required capital adequacy level. The board suggests waiting until year-end financial statements to disclose. What must the KI do?",o:["Report the capital adequacy breach to the FSCA immediately — year-end reporting does not cure a mid-year breach","Agree to wait — disclosure in annual financial statements satisfies the requirement","Notify the external auditors who will then notify the FSCA","Implement internal measures to restore capital within 30 days without external notification"],a:0,e:"Capital adequacy breaches must be reported to the FSCA immediately upon discovery — not deferred to year-end. The KI must also submit a remediation plan showing how and when compliance will be restored."},
{t:"FSP Licensing & Financial Soundness",lv:"AP",q:"An FSP applies to add Category II authorisation to its existing Category I licence. The FSCA requests evidence that the KI meets the qualification requirements for Category II. The KI holds RE1 but not the required Category II-specific qualification. Can the FSP proceed?",o:["Yes — RE1 satisfies all qualification requirements for any category","No — the KI must meet the specific qualification requirements for Category II before the extension is granted","Yes — the FSP may operate under Category II for 24 months while the KI completes the qualification","No — the FSP must appoint a new KI who already holds the Category II qualification"],a:1,e:"Each licence category has specific F&P requirements including qualifications. RE1 is the KI examination — it does not substitute for category-specific qualification requirements. The extension cannot be granted until the KI meets all requirements for Category II."},
{t:"FSP Licensing & Financial Soundness",lv:"C",q:"A sole proprietor FSP's owner dies unexpectedly. The FSP has three representatives and active client relationships. What is the correct regulatory position regarding the FSP's licence?",o:["The FSP's licence transfers automatically to the estate for 12 months","The FSP may continue operating under the deceased's licence for 90 days while preparing a new application","The FSP's licence lapses immediately upon the death of the sole proprietor","The representatives may collectively apply to assume the licence"],a:2,e:"A sole proprietor's FAIS licence is personal and non-transferable. It lapses upon death. The estate cannot operate under the deceased's licence. A new application must be made by an appropriately qualified person if the business is to continue."},
{t:"FSP Licensing & Financial Soundness",lv:"C",q:"An FSP's PI insurance policy contains an exclusion for claims arising from advice rendered by supervised representatives. A supervised representative gives unsuitable advice resulting in a R900,000 loss. The insurer declines the claim. What should the KI have done to prevent this?",o:["Ensured the PI policy explicitly covers supervised representatives before appointing any representative under supervision","Notified the FSCA of the exclusion — the regulator would then provide indemnity cover","Required the supervised representative to obtain personal PI cover","This outcome is unavoidable — PI insurers are not required to cover supervised representative claims"],a:0,e:"PI cover is a continuous F&P requirement. The KI has a duty to ensure the FSP's PI policy adequately covers ALL financial services rendered — including those by supervised representatives. Accepting a policy with this exclusion is a compliance failure."},
{t:"TCF & Consumer Outcomes",lv:"AN",q:"An FSP discovers that a product sold to 8,000 clients over 3 years contained a fee disclosure error — clients were charged 0.3% more than disclosed. The error was systemic and not intentional. Under TCF, what must the FSP do?",o:["Correct the error only going forward and disclose the issue in the FSP's next annual report","Notify the FSCA only — whether to notify clients directly remains entirely at the FSP's discretion","The FSP must proactively identify all affected clients and remediate the overcharge regardless of complaints","Remediate only the clients who specifically identify and formally complain about the discrepancy"],a:2,e:"Systemic errors require systemic remediation. The FSP must identify ALL 8,000 affected clients and proactively remediate — not wait for complaints. Fair treatment cannot depend on whether the client is assertive enough to complain."},
{t:"TCF & Consumer Outcomes",lv:"AN",q:"An FSP's management information reveals that clients in rural areas receive less frequent advisor contact and lower-quality product recommendations than urban clients. The KI argues this reflects practical constraints. Under TCF, what is the correct assessment?",o:["TCF fully allows for differentiated service based on geography — practical constraints are considered valid here","All clients require fair treatment regardless of location — geographic differentiation is a systemic TCF failure","Only Outcome 3 applies here — communication quality differences are considered the primary concern","This is purely a commercial decision — TCF only ever applies to product and advice standards"],a:1,e:"TCF requires fair treatment for ALL clients — not just those in convenient locations. Systematic geographic differentiation in service quality is a structural TCF failure affecting multiple outcomes. The KI must address the systemic inequality."},
{t:"TCF & Consumer Outcomes",lv:"AP",q:"A KI proposes implementing a client satisfaction score as the primary TCF metric reported to the board. A compliance officer objects that satisfaction scores do not capture TCF outcomes. Who is correct?",o:["The KI — client satisfaction is widely regarded as the best available proxy for fair treatment","Both perspectives are considered partially correct in this specific situation","The compliance officer — satisfaction measures perception, not actual outcomes like suitability and complaints","Neither is correct — TCF metrics are instead fully prescribed and mandated directly by the FSCA"],a:2,e:"Client satisfaction measures perception — not outcomes. A client can be satisfied with unsuitable advice. TCF requires measurement of actual outcomes: suitability, complaint patterns, lapse rates, and product performance against projections."},
{t:"TCF & Consumer Outcomes",lv:"C",q:"An FSP's product brochure states a specific investment will 'consistently deliver returns of 12-15% per annum.' The actual product is market-linked with no guarantee. Which TCF outcome is MOST directly breached?",o:["Outcome 6 — clients will likely face barriers to exiting once the returns inevitably disappoint them","Outcome 2 — the product wasn't properly designed for its identified consumer target group","Outcome 4 — the advice given about the product was fundamentally unsuitable for the client","Outcome 5 — clients were given a misleading expectation of performance the product cannot guarantee"],a:3,e:"TCF Outcome 5 requires that products perform as firms have led clients to expect. Representing a market-linked product as delivering consistent 12-15% returns creates an expectation the product structurally cannot guarantee. This is a direct Outcome 5 breach."},
{t:"The Compliance Officer & Compliance Function",lv:"AN",q:"A compliance officer discovers a senior representative has been falsifying client signature dates on needs analyses — backdating documents after recommendations were already made. The KI says 'let's handle this internally.' What must the compliance officer do if the KI does not act within 5 days?",o:["Escalate to the board or governance structure and consider whether FSCA reporting is required","Accept the KI's judgment on this — internal resolution is always the preferable approach here","Resign immediately — the compliance officer cannot operate in an ethically compromised environment","Wait for the next scheduled FSCA inspection to disclose the issue at that time"],a:0,e:"Falsified regulatory documents is a material irregularity. If the KI fails to act, the compliance officer must escalate to the governance structure. The compliance officer's regulatory duty runs to the FSP's compliance obligations — not exclusively to the KI."},
{t:"The Compliance Officer & Compliance Function",lv:"AP",q:"An FSP's compliance programme conducts annual file reviews on 5% of client files. An FSCA thematic review finds the sample too small to be statistically meaningful and identifies significant compliance gaps in unreviewed files. What must the KI restructure?",o:["Nothing at all — 5% is widely considered a commonly accepted industry standard sample size","The FSP must immediately begin reviewing 100% of all client files every single year","The compliance officer responsible for the programme must be replaced immediately","The monitoring programme needs risk-based redesign with sufficient coverage for meaningful assurance"],a:3,e:"The adequacy of compliance monitoring depends on its ability to provide meaningful assurance. A 5% sample that misses significant gaps is not a compliant monitoring programme. Risk-based sampling with coverage proportionate to risk is the expected standard."},
{t:"The Compliance Officer & Compliance Function",lv:"C",q:"A compliance training programme requires representatives to pass an annual assessment. A representative fails the assessment but continues rendering services pending a retest. What is the compliance risk?",o:["Minimal risk — the representative is merely waiting for their retest to be scheduled","No risk at all — annual training is only a best practice, not a regulatory requirement","Significant — allowing a representative with demonstrated knowledge gaps to continue creates regulatory exposure","The representative should be suspended only if the gaps were specifically about FICA knowledge"],a:2,e:"Demonstrated knowledge gaps in regulatory requirements are a F&P concern. Allowing a representative to continue rendering services while awaiting a retest creates direct regulatory exposure — particularly if a client is harmed during the intervening period."},
{t:"KI Duties & Responsibilities",lv:"AN",q:"A KI receives a board directive to expand the FSP's services into a new province. She assigns a regional manager to oversee compliance in that province. Six months later the FSCA discovers widespread suitability failures in that province. The KI says she delegated properly. What is her regulatory position?",o:["Fully protected — she made an entirely reasonable and appropriate delegation decision here","Protected, provided she can produce the delegation letter that was signed by the board","Only the regional manager is accountable — he accepted the delegated responsibility","Potentially liable — the KI must monitor delegated functions, not just make the delegation itself"],a:3,e:"Delegation of tasks does not transfer accountability for outcomes. The KI must monitor whether delegated functions are being performed correctly. A KI who delegates and never verifies outcomes has not discharged the oversight mandate — regardless of how sound the initial delegation decision was."},
{t:"KI Duties & Responsibilities",lv:"AN",q:"An FSP's top revenue-generating representative has received 6 client complaints in 4 months. The KI has not investigated because the representative generates 40% of FSP revenue and the KI fears losing her. An FSCA inspection reveals this. What is the MOST accurate regulatory finding?",o:["The KI acted reasonably, properly balancing commercial sustainability against her compliance obligations here","The FSCA can only act if the representative's clients suffered a provable, quantifiable financial loss","The KI breached the oversight mandate — commercial considerations cannot justify ignoring a complaint pattern","The compliance officer should have escalated these complaints — the KI bears no personal responsibility here"],a:2,e:"Revenue generation is completely irrelevant to the KI's oversight obligations. A 6-complaint pattern in 4 months from one representative is a significant red flag requiring immediate investigation. The KI's commercially motivated inaction is a direct breach of personal accountability and will attract regulatory consequences."},
{t:"KI Duties & Responsibilities",lv:"AN",q:"A KI approves a new performance incentive that awards representatives 3× their standard commission for selling a specific in-house product this quarter. The compliance officer raises a TCF concern in writing. The KI overrules the concern and proceeds. Within 3 months, complaint rates triple. What is the KI's position?",o:["Personally accountable — approving a risky incentive over a documented compliance objection is a governance failure","Protected — the KI had full authority to overrule the compliance officer on commercial matters like this","Protected if the board formally ratified the incentive — board approval transfers all accountability","Only the board is accountable — the KI merely implemented the board's own commercial direction"],a:0,e:"Approving an incentive structure that creates systematic suitability pressure — over a documented compliance objection — is a governance failure under King IV and a TCF Outcome 1 breach. The KI's personal sign-off, combined with the documented warning they overruled, creates direct accountability for the resulting complaints."},
{t:"KI Duties & Responsibilities",lv:"AP",q:"A KI is the sole representative at a small FSP. She is hospitalised unexpectedly for 3 weeks and cannot arrange substitute cover. A client urgently needs financial advice during this period. A junior administrator at the FSP provides informal guidance to the client. What has occurred?",o:["Nothing significant occurred — the administrator was simply being helpful in a genuine emergency","The administrator rendered financial services without authorisation — unlicensed activity under FAIS","The FSP is fully protected because the advice was given in good faith during an emergency","The client bears full responsibility for seeking advice from an unlicensed person"],a:1,e:"There are no emergency exceptions to the requirement that financial services be rendered by a licensed and authorised person. An administrator providing financial guidance is rendering unlicensed financial services under FAIS regardless of the circumstances. The FSP and KI are exposed to regulatory consequences."},
{t:"KI Duties & Responsibilities",lv:"AP",q:"A KI receives a whistleblower report that a senior compliance officer has been falsifying compliance monitoring reports submitted to the FSCA. The KI values the compliance officer's work and is reluctant to disrupt the team. What must the KI do?",o:["Refer for independent investigation immediately — if substantiated, report to the FSCA","Investigate this informally and try to handle it internally without involving the FSCA at all","Wait for the next scheduled FSCA inspection and allow the inspector to discover the issue","Dismiss the report as unsubstantiated unless further evidence is provided by the whistleblower"],a:0,e:"A whistleblower report of falsified regulatory reports is a material compliance allegation. The KI must refer it for independent investigation immediately — the compliance officer cannot investigate allegations against themselves. If substantiated, FSCA notification is mandatory. The KI's personal relationship with the compliance officer is legally irrelevant."},
{t:"KI Duties & Responsibilities",lv:"C",q:"A KI is simultaneously the FSP's sole representative. She resigns from the FSP. What immediately happens to the FSP's ability to render financial services?",o:["The FSP must immediately cease services — there is no longer a licensed KI or authorised representative","The FSP continues operating as normal — representatives can render services fully independently","The FSP has 60 days to appoint a new KI before any operations technically need to cease","The board can temporarily perform the KI's functions itself while a replacement is recruited"],a:0,e:"When the sole KI and representative resigns, the FSP loses both its qualified oversight person and its authorised service renderer simultaneously. It cannot continue rendering financial services and must immediately address the vacancy — the 60-day clock does not give permission to operate without a KI; it is the deadline to fill the vacancy."},
{t:"KI Duties & Responsibilities",lv:"C",q:"A natural person KI is 67 years old and planning to retire. She wants to appoint her company as KI before she leaves. Is this permissible?",o:["Yes — any entity can be appointed as KI provided it holds the relevant qualifications","Yes — juristic persons can be KIs if they have a designated natural person managing the function","No — only a natural (human) person can be a Key Individual under FAIS","Yes — provided the company is a South African registered entity"],a:2,e:"FAIS explicitly requires a Key Individual to be a natural person. A company, trust, close corporation, or any other juristic entity cannot be appointed as KI. The outgoing KI must identify a qualified natural person to succeed her."},
{t:"KI Duties & Responsibilities",lv:"AP",q:"A KI discovers that the FSP has been operating for 3 months without professional indemnity insurance due to a premium payment oversight. She immediately contacts the insurer and reinstates the cover. What should she do next?",o:["Notify the FSCA of the lapse and reinstatement — PI lapse triggers automatic licence suspension","Nothing further at all — the cover has now been reinstated and the problem is resolved","Ask the insurer to formally backdate the cover to close the 3-month gap that occurred","Inform the board only, not the FSCA — internal resolution is enough for administrative oversights"],a:0,e:"A PI insurance lapse triggers automatic licence suspension under FAIS. The FSCA must be notified of both the lapse and the reinstatement. The KI cannot self-resolve this by reinstating cover and saying nothing — it is a notifiable event regardless of the administrative cause."},
{t:"Governance & COI Management",lv:"AN",q:"An FSP's board reviews its remuneration policy and notes that advisers' performance bonuses are linked 80% to sales revenue and 20% to client satisfaction scores. The compliance officer argues this weighting creates TCF risk. The board disagrees and maintains the structure. Six months later the FSCA conducts a thematic review and finds widespread suitability failures. Under King IV, who bears primary governance accountability?",o:["The compliance officer, for failing to prevent the failures after having raised the concern","The advisers who gave unsuitable advice — they bear individual accountability for their own recommendations","The sales managers, since they implemented the structure that actually drove the behaviour","The board — King IV places governance of incentive structures with the governing body itself"],a:3,e:"King IV places the design of governance structures — including remuneration and performance systems — with the governing body. An 80/20 revenue-weighted incentive structure that the compliance officer flagged as TCF-risky, which then produced exactly the predicted suitability failures, is a King IV Outcome 1 (ethical culture) governance failure attributable to the board."},
{t:"Governance & COI Management",lv:"AN",q:"Under King IV's 'apply and explain' requirement, an FSP's board approves a principle but decides not to document how it has been applied in the integrated report because they consider the implementation 'obvious.' Is this compliant?",o:["No — apply and explain requires both application AND documented explanation. Obviousness is no exemption","Yes — if the application is genuinely obvious, formal documentation becomes entirely unnecessary","Yes — apply and explain only requires documentation when an external party specifically requests it","No — but only if the FSCA has specifically requested that particular documentation"],a:0,e:"King IV's apply and explain approach is non-negotiable on both components. Every principle must be applied AND the application must be explained in the integrated report. There are no exemptions for 'obvious' applications — the explanation is what creates accountability and transparency."},
{t:"Governance & COI Management",lv:"AP",q:"A board member at an FSP holds shares in a product supplier. She declared the interest 18 months ago. At a recent board meeting, the board approved expanding the distribution relationship with that supplier without the board member recusing herself — she abstained from voting only. Under King IV, was this adequate?",o:["No — declaration is only step one; full recusal from the discussion is required to properly manage a conflict","Yes — abstaining from the actual vote is sufficient management of a properly declared conflict","Yes — the conflict was declared 18 months ago and is therefore already a known, managed interest","Yes — provided the decision itself was commercially justified on its own individual merits"],a:0,e:"King IV requires both disclosure AND management of conflicts. Management means recusal from the relevant discussion and decision — not just abstaining from the vote. Remaining present during deliberations gives the conflicted person influence over the discussion even without voting. This is a governance failure."},
{t:"Governance & COI Management",lv:"AP",q:"An FSP's KI implements a new 'speak up' policy but after 18 months no concerns have been raised through the channel. The KI considers this a sign the policy is working. A compliance review finds staff are afraid to use the channel. What does this indicate under King IV?",o:["The policy is fully effective — an absence of reports confirms that ethical culture has been embedded","The policy exists on paper but ethical culture hasn't been embedded — fear of using it defeats its purpose","This is purely an HR matter that falls entirely outside King IV's governance scope","The KI has discharged the governance obligation simply by implementing the policy itself"],a:1,e:"King IV requires demonstrable outcomes — not just policy documents. A speak-up policy that produces no reports because staff are afraid to use it is evidence of a failed ethical culture, not a successful one. The KI must investigate why the channel is not being used and address the underlying culture."},
{t:"Governance & COI Management",lv:"C",q:"Under King IV, which of the following BEST describes the 'six capitals' that should inform an FSP's integrated thinking?",o:["Financial capital and human capital only — these are the two capitals most regulated by the FSCA","Financial, manufactured, intellectual, human, social/relationship, and natural capital — all six inform strategy","Financial capital, reputational capital, and regulatory capital only — nothing beyond these three","Financial capital, compliance capital, and operational capital only — the core business essentials"],a:1,e:"King IV's integrated thinking framework requires consideration of all six capitals: financial, manufactured (infrastructure), intellectual (systems and IP), human (staff), social and relationship (clients and community), and natural (environmental impact). FSPs cannot focus exclusively on financial capital."},
{t:"Governance & COI Management",lv:"C",q:"The COI immaterial threshold under FAIS is:",o:["R500 per year from the same third party","R1,000 per year from the same third party","R2,000 per year from the same third party","R5,000 per year from the same third party"],a:1,e:"The immaterial threshold for financial interests from the same third party is R1,000 per year. Interests exceeding this threshold are material and must be disclosed in the ROA and managed in the COI policy."},
{t:"Governance & COI Management",lv:"AN",q:"An FSP's board implements a clawback policy: commissions earned by advisers will be recovered if advice is subsequently found to have been unsuitable within 24 months. Two advisers resign in protest. Management asks the KI whether to abandon the policy. What should the KI advise?",o:["Maintain the policy — it embeds King IV Outcome 1 by creating a direct consequence for unsuitable advice","Abandon the policy — retaining experienced advisers is more important than the clawback mechanism","Suspend the policy for current advisers but apply it to future hires only","Replace the clawback with enhanced monitoring — same outcome without the adviser resistance"],a:0,e:"A clawback policy creates a direct financial consequence for unsuitable advice — this is a structural mechanism for embedding ethical culture (King IV Outcome 1). The FSCA views such structures positively. The fact that some advisers object to being held accountable for outcomes is not a reason to abandon the governance structure."},
{t:"Supervision of Representatives",lv:"AN",q:"A KI implements a risk-based supervision framework that assigns weekly reviews to new representatives and monthly reviews to experienced representatives with clean records. An experienced representative with 8 years of clean performance gives unsuitable advice in month 7 of the year. The next review is in month 8. The client complains. Is the KI's supervision framework defensible?",o:["Yes — monthly reviews of experienced representatives is a reasonable risk-based approach, and no supervision system can catch every error in real-time","No — the KI should have reviewed every transaction by every representative regardless of experience","No — the KI should have moved all representatives to weekly reviews when the complaint was received, which shows the monthly cycle was inadequate","Yes — but only if the KI can prove the representative had no prior complaints in the previous 8 years"],a:0,e:"A risk-based supervision framework that allocates lighter-touch monitoring to experienced representatives with clean records is both permissible and expected. No supervision system can intercept every error in real time. The KI's framework is defensible if it was rationally designed and consistently applied — provided the complaint triggers immediate review and enhanced monitoring going forward."},
{t:"Supervision of Representatives",lv:"AN",q:"A representative under supervision correctly discloses her supervision status to 48 of 50 clients in her first 3 months. In 2 cases she forgot to disclose before rendering advice. Both clients had positive outcomes. Does the positive outcome affect the regulatory position?",o:["Yes — positive outcomes demonstrate that the supervision arrangement is working and minor disclosure lapses are excused","Yes — if the clients did not lodge a complaint, there is no regulatory consequence at all","No — disclosure obligations exist independently of outcome. Two non-disclosures are two Code breaches","No — but only the KI is accountable for the disclosure failures, not the representative personally"],a:2,e:"The obligation to disclose supervision status is an independent procedural requirement that exists regardless of outcome. Two non-disclosures are two distinct Code breaches. Positive client outcomes do not retrospectively cure a procedural compliance failure — they are simply irrelevant to whether the disclosure obligation was met."},
{t:"Supervision of Representatives",lv:"AP",q:"A supervised representative asks his supervisor to approve a complex structured product recommendation for a client. The supervisor approves it without reading the underlying needs analysis. The product proves unsuitable. The representative argues the supervisor's approval should protect him. What is the correct position?",o:["Supervisor approval does not excuse an unsuitable recommendation — both may bear accountability","The supervisor's approval fully protects the representative — approval transfers all responsibility","Only the supervisor is accountable — they approved the recommendation without reading the file","The FSP alone bears sole institutional liability for all supervised representative errors"],a:0,e:"Supervisor approval is a procedural safeguard — it does not transform an unsuitable recommendation into a suitable one. A representative cannot use supervisor approval as a shield against a suitability failure. Both the representative (for the unsuitable recommendation) and the supervisor (for inadequate review) may face consequences."},
{t:"Supervision of Representatives",lv:"C",q:"A representative's supervision period ends. The KI provides verbal confirmation that all F&P requirements are met. Three months later an audit reveals one requirement was outstanding when the verbal confirmation was given. What is the problem with this process?",o:["The KI must provide written confirmation before supervision ends — verbal confirmation is non-compliant","No problem at all — verbal confirmation is considered equally valid to written confirmation","The KI must first obtain formal FSCA approval before ending any supervision arrangement","Only the compliance officer can confirm F&P status — the KI's verbal confirmation carries no weight"],a:0,e:"The regulatory requirement is explicit: written confirmation by the KI that all F&P requirements are met before the supervision arrangement ends. Verbal confirmation is procedurally insufficient and creates a documentation gap that cannot be retrospectively validated."},
{t:"Supervision of Representatives",lv:"AP",q:"A representative discloses to her supervisor that she was sequestrated 2 weeks ago due to personal debt problems. The supervisor says 'don't worry, we won't tell anyone.' What must happen?",o:["The supervisor's approach is acceptable — sequestration is purely a private financial matter","The representative must self-report to the FSCA directly and remove herself from client-facing roles immediately","The sequestration is only relevant if the representative handles client money directly","This must be reported to the KI immediately, who must assess F&P impact and notify the FSCA if required"],a:3,e:"Sequestration directly affects the financial soundness F&P requirement applicable to representatives. The supervisor must escalate to the KI immediately. The KI must assess the F&P impact and notify the FSCA if the representative no longer meets the requirement. The supervisor suppressing this information is itself a breach."},
{t:"Supervision of Representatives",lv:"C",q:"Under FAIS, who bears PRIMARY statutory responsibility for ensuring that supervised representatives are properly supervised?",o:["The compliance officer alone — supervision monitoring is considered purely a compliance function","The FSP and its KI — supervision responsibility cannot be fully outsourced to a compliance officer","The supervisor personally appointed to formally review the representative's work","The FSCA alone — it monitors all representative conduct through its licensing database"],a:1,e:"The FSP and its KI bear primary statutory responsibility for supervision. Compliance officers and supervisors can assist operationally, but the KI cannot transfer the accountability for supervision outcomes to these roles."},
{t:"FSP Licensing & Financial Soundness",lv:"AN",q:"An FSP's sole proprietor owner dies. The FSP has 12 active clients with ongoing investment mandates. The executor of the estate wants to continue the FSP's operations for 6 months while winding up the estate. What is the correct regulatory position?",o:["The executor can continue operations for 6 months under the deceased's licence — estate administration rights extend to FAIS licences","The FAIS licence lapses immediately upon death — it is personal and non-transferable","The FSP can continue for 30 days under a transitional arrangement while a new application is made","The 12 clients' investment mandates automatically transfer to the nearest licensed FSP under FAIS emergency provisions"],a:1,e:"A FAIS licence is personal and non-transferable. It lapses immediately upon the death of a sole proprietor. There are no transitional provisions allowing the estate to continue operations under the deceased's licence. Clients must be urgently informed and alternative arrangements made — ideally through a licensed FSP that can temporarily manage the mandates."},
{t:"FSP Licensing & Financial Soundness",lv:"AN",q:"An FSP applies to add Category II (discretionary) authorisation to its Category I licence. During the FSCA's assessment, the KI demonstrates strong Category II knowledge in an interview but cannot produce evidence of the required Category II qualification. Can the FSCA grant the extension?",o:["No — all F&P requirements, including formal qualifications, must be met before an extension is granted","Yes — demonstrated knowledge in an interview is equivalent to formal qualification under FAIS","Yes — provided the KI completes the qualification within 24 months of the licence extension being granted","Yes — if the board confirms the KI's competence in a resolution"],a:0,e:"The FSCA cannot grant a licence extension without confirming that all F&P requirements — including formal qualifications — are met. Interview performance demonstrates knowledge but does not satisfy the formal qualification requirement. The KI must obtain the qualification before the Category II extension can be granted."},
{t:"FSP Licensing & Financial Soundness",lv:"AP",q:"An FSP discovers mid-year that a large client redemption has caused its liquid assets to drop below the required capital adequacy threshold. The CFO proposes raising new capital within 2 months and disclosing the breach in the year-end financial statements. What must the KI do?",o:["Accept the CFO's proposal — 2 months is a reasonable timeframe to rectify a capital breach","Notify the FSCA only if the breach is not rectified within 30 days","Notify the FSCA immediately and submit a remediation plan — year-end disclosure doesn't replace this","Consult legal counsel before notifying the FSCA — premature notification may invite unnecessary regulatory intervention"],a:2,e:"Capital adequacy breaches must be reported to the FSCA immediately upon discovery — not deferred to year-end financial statements. The KI must notify the FSCA that day and submit a credible remediation plan. The year-end financial statement disclosure is a separate, additional obligation — not a substitute for immediate notification."},
{t:"FSP Licensing & Financial Soundness",lv:"C",q:"What is the deadline for an FSP to submit its annual compliance report after its financial year-end?",o:["2 months","6 months","12 months","4 months"],a:3,e:"The annual compliance report must be submitted within 4 months of the FSP's financial year-end. The annual financial statements carry the same 4-month deadline. Both must be submitted within this period regardless of the FSP's specific year-end date."},
{t:"FSP Licensing & Financial Soundness",lv:"AP",q:"An FSP's PI insurance policy specifically excludes claims arising from advice on products in Category II — yet the FSP has recently expanded its licence to include Category II services. The KI signs off on the renewal without noticing the exclusion. A Category II client suffers a R800,000 loss and claims under the PI policy. The insurer declines. What is the KI's position?",o:["Protected — the insurer is obligated to cover all licensed activities regardless of policy exclusions","Exposed — the KI must ensure PI cover adequately covers all services under the FSP's licence","The client must sue the insurer directly — the KI is not personally liable for insurance policy gaps","Protected — the FSP, not the KI personally, bears responsibility for insurance adequacy"],a:1,e:"PI insurance is a continuous F&P requirement and must cover all services rendered under the FSP's licence. The KI who signs off on a renewal without verifying it covers all licensed categories has failed a core compliance obligation. The resulting coverage gap creates both regulatory exposure and potential personal liability."},
{t:"Twin Peaks & Regulatory Framework",lv:"AN",q:"An FSP regulated by both the PA (for prudential purposes as a registered insurer) and the FSCA (for conduct purposes) receives conflicting guidance: the PA advises a specific capital allocation approach while the FSCA's Conduct Standard implies a different approach to client fund management. How should the KI navigate this?",o:["Follow PA guidance only — prudential requirements always take precedence over conduct requirements","Follow FSCA guidance only — conduct requirements take precedence for FSPs serving retail clients","Seek urgent written clarification from both regulators and apply the more protective approach meanwhile","Choose whichever guidance is commercially less disruptive and document the reasoning"],a:2,e:"Where Twin Peaks regulators issue conflicting guidance, the FSP must seek urgent written clarification from both, document everything, and apply the more protective or conservative approach in the interim. Unilaterally choosing one regulator's guidance over another creates regulatory risk with the other. The client-protective standard governs where genuine conflict exists."},
{t:"Twin Peaks & Regulatory Framework",lv:"AP",q:"A fintech company uses an algorithm to analyse client data and generate personalised investment recommendations with no human representative involvement. A client suffers losses and argues the advice was unsuitable. The company claims FAIS does not apply because no human gave the advice. Is this correct?",o:["Correct — FAIS only ever applies to natural persons who personally render advice","Correct — algorithmic outputs don't qualify as 'advice' under the FAIS statutory definition","Incorrect — but only if the algorithm uses client-specific data rather than generic market data","Incorrect — FAIS regulates the function of advice, not the delivery method; the operator still needs authorisation"],a:3,e:"FAIS regulates the function — not the method. If an algorithmic system performs the function of advice (a recommendation about a financial product directed at a specific client), the entity operating it is an FSP and requires FAIS authorisation. The FSCA has confirmed that automated/robo-advice is subject to FAIS."},
{t:"Twin Peaks & Regulatory Framework",lv:"AP",q:"A FSCA Conduct Standard issued in 2025 requires FSPs to disclose all fees in rand amounts. An FSP's existing processes only disclose fees as percentages — compliant with the 2018 FAIS Determination in place when the processes were designed. Which requirement applies?",o:["The 2018 Determination — it was in place when the processes were designed and cannot be retrospectively overridden","Both apply simultaneously — the FSP must comply with both the percentage and rand disclosure methods","The 2025 Standard governs — more recent instruments supersede earlier ones; processes must be updated","Neither — until the FSCA issues a directive specifically to that FSP, existing processes are grandfathered"],a:2,e:"Current regulatory requirements govern current conduct. When a Conduct Standard introduces new requirements, FSPs must update their processes to comply — prior compliance with an older standard does not excuse non-compliance with the current requirement. The FSP must update its disclosure processes."},
{t:"Twin Peaks & Regulatory Framework",lv:"C",q:"The Financial Sector Regulation Act (FSRA) of 2017 created the Twin Peaks model. Which body was replaced by the FSCA?",o:["The Reserve Bank","The Financial Services Board (FSB)","The National Credit Regulator","The Competition Commission"],a:1,e:"The Financial Services Board (FSB) was replaced by the FSCA (Financial Sector Conduct Authority) under the FSRA. The PA (Prudential Authority) was established within the SARB to handle prudential supervision. Together, they constitute the Twin Peaks model."},
{t:"Twin Peaks & Regulatory Framework",lv:"AN",q:"An FSP notices that the FSCA has issued a Guidance Notice recommending specific wording for client risk disclosure documents. The FSP's legal team argues the Guidance Notice is not legally binding and therefore the FSP can ignore it. The KI is asked to decide. What is the most appropriate position?",o:["Agree with the legal team — Guidance Notices carry no legal force and can be safely ignored entirely","Adopt the wording immediately — Guidance Notices carry the exact same legal force as Conduct Standards","Request a formal, binding ruling directly from the FSCA before making any decision at all","Disagree — Guidance Notices aren't legally binding but signal FSCA's expectations; ignoring them creates regulatory risk"],a:3,e:"Guidance Notices represent the FSCA's view of best practice and adequate compliance. An FSP that routinely ignores them is an FSP that routinely disagrees with the regulator about what the law requires. This creates regulatory risk, adversarial inspection dynamics, and reputational consequences. The prudent approach is to comply with Guidance Notices unless there is a specific, defensible reason not to."},
{t:"TCF & Consumer Outcomes",lv:"AN",q:"An FSP conducts its annual TCF assessment and finds: client satisfaction scores average 4.2/5, complaint volumes are low, and advisers achieve their sales targets. The KI declares TCF compliance. An FSCA inspector reviews the FSP's data and disagrees. What has the KI misunderstood about TCF?",o:["Nothing at all — the metrics used are the standard TCF measurement tools recognised by the FSCA","The KI should have also measured staff satisfaction levels — this is a core TCF requirement","The KI correctly applied the TCF framework — the inspector is applying an unnecessarily higher standard","Client satisfaction, low complaints, and sales targets are perception/output metrics, not actual client outcomes"],a:3,e:"Client satisfaction measures perception of service quality, not actual outcomes. Low complaints may reflect clients who do not know their rights or cannot afford to complain. Sales targets measure commercial performance. TCF requires measurement of actual client outcomes: suitability rates, lapse rates, product performance versus projections, and root cause analysis of complaints."},
{t:"TCF & Consumer Outcomes",lv:"AN",q:"An FSP discovers that a fee calculation error has resulted in 6,000 clients being overcharged by an average of R340 each over 3 years. The error was unintentional. The legal team argues that since no clients have complained, the FSP has no obligation to proactively reach out. Under TCF, is this correct?",o:["Incorrect — TCF requires proactive remediation of systemic overcharges regardless of complaints received","Correct — absent any complaints, FSPs carry no proactive remediation obligation whatsoever","Correct — proactive remediation is only required once regulatory action has actually been taken","Incorrect — but only if the total amount overcharged exceeds R1,000,000 in aggregate"],a:0,e:"TCF is not reactive — it requires proactive fair treatment of all clients. An FSP that discovers a systematic overcharge cannot wait for complaints to trigger remediation. All 6,000 affected clients must be identified and proactively refunded. TCF Outcome 4 (suitable treatment) and Outcome 1 (fair culture) both require systemic remediation."},
{t:"TCF & Consumer Outcomes",lv:"AP",q:"A client contacts an FSP to switch from Product A to Product B — both suitable for her circumstances. The FSP's process requires the client to complete a 14-page form, wait 10 business days, pay a R500 administration fee, and call three separate departments. The client gives up and stays in Product A. Which TCF outcome is MOST directly breached?",o:["Outcome 4 — the original advice to remain in Product A was itself unsuitable","Outcome 6 — there are unreasonable post-sale barriers preventing the client from switching","Outcome 2 — the product itself was not designed for the right target market","Outcome 3 — the communication about the switching process was unclear"],a:1,e:"TCF Outcome 6 specifically addresses post-sale barriers. A switching process designed so cumbersome that clients give up is an unreasonable post-sale barrier — regardless of whether each individual step is technically defensible. The practical effect is that the client cannot exercise her right to switch without extraordinary effort."},
{t:"FAIS Ombud & Complaints",lv:"AN",q:"An FSP receives 22 complaints over 6 months, all relating to the same adviser's retirement product recommendations. The KI resolves each complaint individually with small ex gratia payments and records each as 'resolved.' The FSCA's thematic review examines the complaints log and issues an enforcement notice. Why?",o:["Individual resolution of complaints is always sufficient — the FSCA should not penalise an FSP for resolving complaints quickly","The enforcement notice is procedurally incorrect — the FSP has not been given an opportunity to respond","Individual resolution without root cause analysis is inadequate where a pattern exists among similarly-advised clients","The FSCA can only issue enforcement notices after a formal complaint about the FSP's complaint handling"],a:2,e:"22 complaints in 6 months from the same adviser is a pattern indicating a systemic suitability failure. Individual resolution without root cause analysis means potentially hundreds of other affected clients who did not complain have not been remediated. The FSP must investigate the entire scope of the problem, not just close individual files."},
{t:"FAIS Ombud & Complaints",lv:"AP",q:"An FSP's complaints policy states all complaints will receive a final response within 30 working days. A client submits a complaint on 1 March. On 15 April (45 days later) she has received no response and approaches the FAIS Ombud. The FSP argues the complaint is premature because they have not issued a final response. What is the Ombud's position?",o:["Agree with the FSP — the Ombud cannot accept the complaint until the FSP issues a final response","Disagree — non-response within the FSP's own stated timeframe is treated as a final response for Ombud purposes","Agree — but instruct the FSP to respond within 14 days before accepting jurisdiction","Disagree — but only because 45 days exceeds the FSP's stated 30-day period by more than 50%"],a:1,e:"An FSP's failure to respond within its own stated timeframe is treated as a final response — enabling the complainant to approach the Ombud. The purpose is to prevent FSPs from using silence as a delay tactic to block Ombud access. The 45-day non-response (exceeding the FSP's own 30-day commitment) justifies Ombud jurisdiction."},
{t:"FAIS Ombud & Complaints",lv:"C",q:"A FAIS Ombud determination awards a complainant R2,100,000. The FSP does not appeal but also does not pay. What legal remedy does the complainant have?",o:["Re-submit the complaint directly to the Ombud and request a higher award amount","Report the non-compliance to the FSCA directly — the FSCA has power to compel payment","Apply to the High Court for a writ of execution — the determination has the force of a civil judgement","Nothing further can be done — Ombud determinations are recommendations, not binding orders"],a:2,e:"An unappealed FAIS Ombud determination has the force of a High Court civil judgement. The complainant can apply to the High Court for a writ of execution, which allows the sheriff to attach and sell the FSP's assets to satisfy the award. Non-compliance with a determination exposes the FSP to the full force of civil enforcement."},
{t:"The Compliance Officer & Compliance Function",lv:"AN",q:"A KI implements a compliance monitoring programme that reviews 5% of client files annually. An FSCA thematic review identifies significant compliance gaps in the unreviewed 95% of files. The KI argues 5% is an industry-standard sample size. What is the FSCA's likely position?",o:["Accept the 5% sample — industry standards set the benchmark for adequate monitoring","Accept the 5% sample but require retrospective review of the remaining 95%","Adequacy depends on meaningful assurance — a 5% sample missing major gaps needs redesign","The KI is correct — the FSCA cannot penalise a firm for following industry standards"],a:2,e:"Monitoring adequacy is assessed by outcomes — whether it provides meaningful assurance of compliance. An industry-standard sample size that consistently misses significant gaps is not adequate. The FSCA expects risk-based monitoring where coverage is proportionate to risk — higher-risk representatives and products receive more intensive review."},
{t:"The Compliance Officer & Compliance Function",lv:"AP",q:"A compliance officer conducts a file review and discovers that a senior adviser's ROAs are almost identical across 40 different clients — copy-pasted with minor name changes. She reports this to the KI. What does this indicate?",o:["Efficient working practices — standardised ROAs reduce errors and save time","Only a documentation issue — if clients signed the ROAs, they indicate consent","A formatting issue only — the content may still be appropriate even if the format was replicated","A potentially systemic suitability failure — identical ROAs across 40 clients suggest advice wasn't personalised"],a:3,e:"Identical ROAs across multiple clients strongly suggests that advice was not genuinely tailored to individual circumstances — a suitability concern rather than a formatting issue. A genuine needs analysis produces different findings for different clients. Identical ROAs are a red flag requiring investigation of the underlying advice process."},
{t:"The Compliance Officer & Compliance Function",lv:"C",q:"A compliance officer discovers during a routine review that a representative has been rendering financial services in a product category that the FSP is not licensed for. She reports this to the KI. What must happen immediately?",o:["The representative must obtain personal licensing to cover the gap","The FSP must immediately stop services in that category and assess whether a licence extension is needed","The FSP may continue for 60 days while applying for the additional category licence","The clients in that category must be notified and given the option to transfer to a licensed FSP"],a:1,e:"Rendering financial services in an unlicensed category must stop immediately upon discovery. There is no grace period. The FSP must cease, assess whether a licence extension is appropriate and feasible, and consider the implications for clients who received unlicensed advice."},
{t:"The Compliance Officer & Compliance Function",lv:"AP",q:"An FSP's annual training programme covers FAIS, FICA, and TCF. Representatives must pass an assessment to confirm competence. A representative fails the assessment but continues rendering services while awaiting a retest scheduled for 3 weeks later. What is the compliance risk?",o:["Minimal — the representative is experienced and the scheduled retest is coming up imminently","Significant — a failed assessment is a demonstrated knowledge gap that creates direct regulatory exposure","No risk at all — internal assessments carry no regulatory significance whatsoever","The risk depends entirely on which specific section of the assessment the representative failed"],a:1,e:"A failed regulatory knowledge assessment is a demonstrated knowledge gap. Continuing to render services with known regulatory knowledge deficiencies creates direct exposure — if a client suffers harm during this period, the FSP cannot easily argue it maintained adequate competence standards. Temporary suspension pending the retest is the appropriate risk management response."},
{t:"KI Duties & Responsibilities",lv:"AP",q:"Which of the following are core statutory functions of a Key Individual under FAIS?\n\nI. Managing the provision of financial services by representatives.\nII. Overseeing representatives' ongoing compliance with Fit and Proper requirements.\nIII. Personally approving every individual client transaction before it proceeds.",o:["I only","I and II only","II and III only","I, II and III"],a:1,e:"The KI's statutory mandate is management and oversight of financial service rendering, including F&P compliance oversight. Personally approving every individual transaction is not a statutory KI function — that level of micromanagement isn't what the role requires."},
{t:"KI Duties & Responsibilities",lv:"AP",q:"Which of the following are legitimate consequences a KI may face for failing to adequately supervise a representative?",o:["Personal regulatory accountability for the oversight failure","Consideration of whether the supervision arrangement remains viable","Scrutiny of whether the KI's own Fit and Proper status is affected","All three — supervision failures carry consequences across all these areas"],a:3,e:"A KI's oversight failure isn't limited to one consequence — it can trigger personal accountability, a reassessment of the supervision arrangement itself, and even implications for the KI's own ongoing Fit and Proper standing."},
{t:"KI Duties & Responsibilities",lv:"AP",q:"Which statement about a Key Individual's accountability is INCORRECT?",o:["A KI who delegates a task remains accountable for verifying it was completed","A KI's personal liability disappears entirely once a task is delegated","A KI must monitor delegated functions, not just make the initial delegation","Accountability for outcomes stays with the KI even when tasks are assigned out"],a:1,e:"Delegating a task does not transfer away a KI's accountability for the outcome. The KI remains responsible for verifying delegated work was actually completed properly — delegation reduces workload, not accountability."},
{t:"Twin Peaks & Regulatory Framework",lv:"AP",q:"Which of the following statements about the Twin Peaks regulatory model are correct?\n\nI. The FSCA is responsible for market conduct regulation.\nII. The Prudential Authority is responsible for the financial soundness of institutions.\nIII. The two regulators never need to coordinate, since their mandates are entirely separate.",o:["I only","I and II only","II and III only","I, II and III"],a:1,e:"The FSCA (market conduct) and Prudential Authority (prudential soundness) are the two peaks of Twin Peaks. Statement III is incorrect — their mandates can overlap in practice, and coordination between them is expected, not avoided."},
{t:"Twin Peaks & Regulatory Framework",lv:"AP",q:"Which statement about FSCA Conduct Standards is INCORRECT?",o:["Conduct Standards are legally binding regulatory instruments","FSCA Guidance Notices carry identical binding legal force to Conduct Standards","More recent Conduct Standards can supersede earlier regulatory requirements","FSPs must update internal processes when a new Conduct Standard applies"],a:1,e:"Guidance Notices are not legally binding in the way Conduct Standards are — they represent the FSCA's interpretation of expectations. Treating them as having identical binding force overstates their legal status, though ignoring them still carries real regulatory risk."},
{t:"Governance & COI Management",lv:"AP",q:"Under King IV, which of the following are genuine components of an FSP's governing body responsibility for ethical culture?",o:["Designing remuneration structures that don't create suitability pressure","Ensuring a functioning channel exists for reporting misconduct without fear","Monitoring whether governance structures are producing real outcomes, not just existing on paper","All three — ethical culture is built through each of these together"],a:3,e:"King IV places ethical culture squarely with the governing body — this includes remuneration design, functioning whistleblower channels, and monitoring real outcomes rather than assuming a policy document alone is sufficient."},
{t:"Governance & COI Management",lv:"AP",q:"Which statement about conflicts of interest under King IV is INCORRECT?",o:["A declared conflict still requires the individual to recuse from the relevant decision","Simply disclosing a conflict is sufficient — no further management is required","Board members must disclose personal financial interests connected to a decision","A conflict of interest policy is expected to be actively applied, not just documented"],a:1,e:"Disclosure alone is not sufficient conflict management — King IV requires both disclosure AND active management, typically including recusal from the affected discussion or decision. Disclosure is the first step, not the whole solution."},
{t:"Supervision of Representatives",lv:"AP",q:"A representative under supervision renders a recommendation without the required prior supervisor approval, though the recommendation itself turns out to have been appropriate.\n\nI. No breach occurred, since the outcome was ultimately fine.\nII. A breach of the supervision arrangement occurred regardless of outcome.\nIII. The KI should assess whether the supervision arrangement remains appropriate.",o:["I only","I and II only","II and III only","I, II and III"],a:2,e:"The outcome does not determine whether a breach occurred — rendering a service without required supervisor approval is itself the breach, independent of whether the advice happened to be appropriate. The KI must still assess the arrangement."},
{t:"Supervision of Representatives",lv:"AP",q:"Which of the following would justify a KI extending a representative's supervision period beyond what was originally planned?",o:["A pattern of complaints suggesting unresolved competency gaps","The representative has not yet completed a required regulatory examination","Documented concerns about the quality of the representative's recommendations","All three are legitimate, genuine grounds for extending supervision"],a:3,e:"Supervision exists to bridge genuine gaps until F&P requirements are met. Complaint patterns, incomplete exams, and documented recommendation-quality concerns are all legitimate reasons the bridge may need to stay in place longer."},
{t:"Supervision of Representatives",lv:"AP",q:"Which statement about supervision arrangements is INCORRECT?",o:["A supervisor who approves files without reading them breaches the arrangement","The KI must monitor whether supervision is being performed properly, not just appointed","Once a supervisor is appointed, the KI has no further obligation regarding that arrangement","Rubber-stamp approval patterns can indicate a failure of the supervision function"],a:2,e:"Appointing a supervisor does not end the KI's obligations — the KI must continue monitoring whether supervision is genuinely happening. Treating appointment as the finish line is exactly the kind of oversight failure that leads to rubber-stamping going undetected."},
{t:"FSP Licensing & Financial Soundness",lv:"AP",q:"An FSP's liabilities begin exceeding its assets.\n\nI. The FSP no longer meets the financial soundness Fit and Proper requirement.\nII. This must be reported to the FSCA immediately, regardless of whether the FSP is still trading normally.\nIII. Ongoing normal trading cures the regulatory breach automatically.",o:["I only","I and II only","II and III only","I, II and III"],a:1,e:"An FSP whose liabilities exceed its assets no longer meets financial soundness requirements, and this must be reported immediately. Statement III is incorrect — continuing to trade normally does not cure or excuse the underlying regulatory breach."},
{t:"TCF & Consumer Outcomes",lv:"AP",q:"Which of the following would be appropriate ways to measure whether TCF Outcome 4 (suitable advice) is actually being achieved?",o:["Tracking early lapse rates on newly sold products","Reviewing root causes behind complaint patterns, not just resolving individual complaints","Comparing product performance against what clients were led to expect","All three — genuine outcome measurement needs more than one single metric"],a:3,e:"TCF outcomes require measuring real results, not just perception. Lapse rates, root-cause complaint analysis, and performance-versus-expectation comparisons together give a meaningful picture — no single metric alone is sufficient."},
{t:"TCF & Consumer Outcomes",lv:"AP",q:"Which statement about measuring TCF outcomes is INCORRECT?",o:["Client satisfaction scores alone are a reliable, sufficient measure of fair treatment","Suitability outcomes should be measured using real data, not just perception","A pattern of early product lapses can signal a TCF Outcome 4 concern","TCF requires proactive monitoring, not just reacting to formal complaints"],a:0,e:"Client satisfaction measures perception, not actual outcomes — a client can be satisfied with advice that was genuinely unsuitable. Relying on satisfaction scores alone significantly understates real suitability risk."},
{t:"FAIS Ombud & Complaints",lv:"AP",q:"Which statement about the FAIS Ombud's process is INCORRECT?",o:["An FSP's failure to respond within its own stated timeframe can be treated as a final response","The complainant must wait indefinitely for an FSP response before the Ombud can be approached","An unappealed determination has the force of a High Court civil judgement","The Ombud may condone late filing of a complaint where good cause is shown"],a:1,e:"Complainants do not have to wait indefinitely — if an FSP fails to respond within its own stated timeframe, that non-response can itself be treated as a final response, allowing the complainant to approach the Ombud without further delay."},
{t:"The Compliance Officer & Compliance Function",lv:"AP",q:"An FSP's compliance monitoring programme reviews a small, fixed percentage of files each year regardless of representative risk profile.\n\nI. This approach is automatically adequate provided it matches industry convention.\nII. Monitoring adequacy should be judged by whether it provides meaningful assurance.\nIII. Higher-risk representatives should generally receive more scrutiny than lower-risk ones.",o:["I only","I and II only","II and III only","I, II and III"],a:2,e:"Following industry convention alone does not make a monitoring programme adequate — adequacy depends on whether it actually provides meaningful assurance. Risk-based allocation, giving more scrutiny to higher-risk representatives, is the expected standard rather than a flat, undifferentiated sample."}
,
{t:"FSP Licence Lifecycle & Enforcement",lv:"K",q:"An application for authorisation as a financial services provider is made in terms of section:",o:["7 of the FAIS Act","8 of the FAIS Act","13 of the FAIS Act","17 of the FAIS Act"],a:1,e:"Section 8 governs the application for authorisation. Section 7 prohibits unauthorised rendering of services, section 13 deals with representatives, and section 17 with compliance officers."},
{t:"FSP Licence Lifecycle & Enforcement",lv:"K",q:"Levies payable by an FSP to the Financial Sector Conduct Authority are imposed under the:",o:["Financial Sector and Deposit Insurance Levies Act, 2022","Financial Advisory and Intermediary Services Act, 2002","Financial Sector Regulation Act, 2017","Financial Intelligence Centre Act, 2001"],a:0,e:"Levies are imposed under the Financial Sector and Deposit Insurance Levies Act 11 of 2022. Failure to pay within the prescribed timeframes is a compliance failure like any other statutory deadline."},
{t:"FSP Licence Lifecycle & Enforcement",lv:"C",q:"Which ONE of the following statements correctly distinguishes lapsing from withdrawal of an FSP licence?",o:["Lapsing follows an Authority decision, while withdrawal occurs automatically","Lapsing occurs by operation of law, withdrawal by decision of the Authority","Both require prior written notice to the FSP and an oral hearing","Lapsing may be appealed, while withdrawal is final and unappealable"],a:1,e:"Lapsing occurs automatically on a defined event such as death of a sole proprietor or voluntary closure. Withdrawal is a deliberate decision of the Authority. Recourse is available against suspension and withdrawal, but not against lapsing, because lapsing is not an Authority decision."},
{t:"FSP Licence Lifecycle & Enforcement",lv:"C",q:"An FSP's licence has been suspended by the Authority. In respect of that decision the FSP:",o:["Has no recourse, suspension being an administrative act","Must first surrender the licence before disputing the suspension","Has recourse and may challenge the decision through the available mechanisms","May continue rendering services pending the outcome of a challenge"],a:2,e:"An FSP is entitled to representations and, where applicable, may challenge a suspension or withdrawal. That recourse does not extend to lapsing, and a suspension is not stayed merely because it is being disputed."},
{t:"FSP Licence Lifecycle & Enforcement",lv:"AP",q:"An FSP licensed only for Category I begins managing client portfolios on a discretionary basis without specific instruction for each transaction. The advice given is technically sound and no client suffers loss. The position is that the FSP has:",o:["Not contravened the Act, no client having suffered any loss","Acted correctly, since Category I includes discretionary management","Acted correctly, provided the key individual approved the arrangement","Rendered services outside its licensed categories"],a:3,e:"Category limits govern the authority to render the service at all. The quality of the advice and the absence of client loss do not cure a service rendered outside the licensed category."},
{t:"FSP Licence Lifecycle & Enforcement",lv:"K",q:"In terms of section 34 of the FAIS Act, the Authority may declare a business practice undesirable:",o:["Even though the practice does not contravene an express provision","Only where the practice contravenes an express provision of the Act","Only after demonstrable financial loss has been suffered by a client","Only on the written recommendation of the Ombud for FSPs"],a:0,e:"The power is deliberately broad and does not require a pre-existing contravention. A practice may be declared undesirable simply because it is harmful to clients or to the integrity of the industry."},
{t:"FSP Licence Lifecycle & Enforcement",lv:"C",q:"Where an FSP continues with a practice after the Authority has declared it undesirable, the Authority may:",o:["Only issue a further warning to the provider concerned","Direct reparation measures, including rectification for affected clients","Refer the matter to the Ombud for a determination on costs","Impose a criminal sanction directly without a court process"],a:1,e:"Reparation measures are available where a declared undesirable practice continues. These may include directing the FSP to rectify the position of clients affected, not merely to stop the practice going forward."},
{t:"FSP Licence Lifecycle & Enforcement",lv:"C",q:"Which ONE of the following statements about FSCA Guidance Notices is NOT correct?",o:["A Guidance Notice sets out the Authority's interpretation of a requirement","Routinely disregarding Guidance Notices creates real regulatory risk","A Guidance Notice carries the same binding force as a formal directive","A Guidance Notice is not, of itself, legally binding on a provider"],a:2,e:"A Guidance Notice interprets existing law and is not legally binding. A formal directive is different and carries direct legal consequences for non-compliance. The examination tests this distinction."},
{t:"FSP Licence Lifecycle & Enforcement",lv:"K",q:"An FSP's annual compliance report and audited financial statements must be submitted within:",o:["2 months of the FSP's financial year-end","4 months of the FSP's financial year-end","6 months of the FSP's financial year-end","12 months of the FSP's financial year-end"],a:1,e:"Both are due within four months of the FSP's financial year-end."},
{t:"FSP Licence Lifecycle & Enforcement",lv:"AP",q:"A sole proprietor FSP owner dies. The estate wishes to continue operating under the existing licence while a purchaser is found. The correct position is that the licence:",o:["May be used by the estate for a period of up to 90 days","Remains valid provided the Authority is notified within 5 days","Remains valid for as long as an approved key individual is in office","Lapses immediately, being personal and non-transferable"],a:3,e:"The licence is personal to the sole proprietor and lapses on death. There is no estate continuation and no grace period; a fresh application is required to resume rendering services."},
{t:"FSP Licence Lifecycle & Enforcement",lv:"AP",q:"A key individual resigns, leaving the FSP with no approved key individual. The FSP continues rendering financial services. The MOST accurate description of the position is that the FSP:",o:["Fails an authorisation requirement and must fill the vacancy","May continue indefinitely provided a compliance officer holds office","Has had its licence lapse automatically on the date of the resignation","May appoint its compliance officer as acting key individual without approval"],a:0,e:"The absence of an approved key individual is a failure to meet an authorisation requirement. The vacancy must be filled within 60 days, and a compliance officer cannot substitute for a key individual."},
{t:"FSP Licence Lifecycle & Enforcement",lv:"K",q:"A key individual vacancy must be filled within:",o:["30 days","45 days","60 days","90 days"],a:2,e:"The prescribed period is 60 days. Thirty and ninety days are commonly offered as distractors."},
{t:"FSP Licence Lifecycle & Enforcement",lv:"C",q:"An FSP's accreditation under the Medical Schemes Act is withdrawn. In respect of its FAIS authorisation this:",o:["Has no effect, the two regulatory regimes being independent","May affect authorisation for the services concerned","Causes the FAIS licence to lapse in its entirety immediately","Automatically suspends every subcategory held by the provider"],a:1,e:"Withdrawal, suspension or lapsing of a related accreditation can affect the FSP's ability to render services in the category to which that accreditation relates, without necessarily affecting the whole licence."},
{t:"FSP Licence Lifecycle & Enforcement",lv:"AP",q:"During an on-site inspection the Authority requests client files that the key individual considers outside the scope of the visit. The key individual should:",o:["Decline until the Authority furnishes written reasons for the request","Refer the request to the FSP's attorneys before responding at all","Produce the records, the Authority being empowered to inspect","Produce only those records the key individual considers relevant"],a:2,e:"The Authority is empowered to conduct on-site inspections and to require production of records. The key individual is not entitled to filter the request or to make production conditional on reasons being given."},
{t:"FSP Licence Lifecycle & Enforcement",lv:"AN",q:"Which combination of the following would cause an FSP licence to LAPSE, rather than be suspended or withdrawn?\ni) Death of a sole proprietor\nii) The Authority's decision following persistent non-compliance\niii) Voluntary closure of the business\niv) Failure to pay prescribed levies",o:["(i), (ii) and (iii) only","(ii), (iii) and (iv) only","(i), (ii), (iii) and (iv)","(i), (iii) and (iv) only"],a:3,e:"Death, voluntary closure and failure to meet a renewal or levy obligation cause a licence to lapse by operation of law. A decision by the Authority following non-compliance is a withdrawal, which is a different category entirely."},
{t:"FSP Licence Lifecycle & Enforcement",lv:"C",q:"Business rescue proceedings commence in respect of an FSP. In relation to the FSP's licensing status the key individual:",o:["Remains regulatorily accountable throughout","Is relieved of regulatory accountability during the proceedings","Must surrender the licence to the Authority for the duration","May treat the matter as purely a legal and business issue"],a:0,e:"Business rescue, winding-up and sequestration affect licensing status and trigger specific statutory processes. The key individual remains regulatorily accountable throughout and cannot treat these as matters outside the compliance function."},
{t:"FSP Licence Lifecycle & Enforcement",lv:"AP",q:"An FSP wishes to extend its licence to a new subcategory ahead of a significant revenue opportunity. The Authority will determine the application principally on whether:",o:["The commercial rationale advanced by the provider is compelling","The FSP and its officers meet the Fit and Proper requirements","The FSP has traded without contravention for at least three years","The extension would prejudice competing providers in that market"],a:1,e:"The determinative consideration is Fit and Proper compliance for the subcategory applied for. Commercial factors, trading history and competitive effect are not the basis of the assessment."},
{t:"FSP Licence Lifecycle & Enforcement",lv:"C",q:"In terms of the FAIS Act, the requirement that an FSP display its licence is:",o:["A courtesy that reflects good practice but is not enforceable","Applicable only to providers holding Category II authorisation","A specific compliance requirement that is separately testable","Satisfied by displaying the FSP number on the provider's website"],a:2,e:"Display of the licence is a specific statutory requirement. It is not satisfied merely by a digital footer, and it applies irrespective of licence category."},
{t:"Operational Ability, Outsourcing & Continuity",lv:"K",q:"Operational ability requirements for an FSP are contained in:",o:["Chapter 2 of Board Notice 194 of 2017","Board Notice 80 of 2003","Board Notice 122 of 2003","Chapter 5 of Board Notice 194 of 2017"],a:3,e:"Operational ability requirements appear in Chapter 5 of BN 194 of 2017. Chapter 2 addresses honesty and integrity, BN 80 is the General Code and BN 122 deals with key individual approval."},
{t:"Operational Ability, Outsourcing & Continuity",lv:"C",q:"Operational ability is BEST distinguished from financial soundness on the basis that operational ability asks whether the FSP:",o:["Has the practical infrastructure to operate and protect clients","Holds sufficient capital to meet its liabilities as they fall due","Has settled all levies payable to the Authority when due","Maintains professional indemnity cover at the prescribed level"],a:0,e:"Financial soundness asks whether the FSP has adequate capital. Operational ability asks a different question: whether the FSP has the systems, resources and continuity arrangements to actually do what its licence permits."},
{t:"Operational Ability, Outsourcing & Continuity",lv:"AP",q:"An inspection finds that client records exist but are spread across personal email accounts, individual laptops and an unstructured shared drive, taking over a week to compile a single client file. The finding should be that the FSP:",o:["Complies, since the records ultimately exist somewhere","Does not comply — retrievable systems are required","Complies, provided the shared drive is backed up regularly","Does not comply only where a client has suffered actual prejudice"],a:1,e:"Adequate storage and filing systems for the safekeeping of records, communications and correspondence are required. Records that exist but cannot be retrieved within a reasonable time do not satisfy the requirement."},
{t:"Operational Ability, Outsourcing & Continuity",lv:"K",q:"Where an FSP outsources an activity, a written service level agreement is:",o:["Optional, provided the provider is reputable and established","Required only where the outsourced provider is itself an FSP","Required, informal or verbal arrangements being insufficient","Required only where client funds pass through the provider"],a:2,e:"Written service level agreements must be in place where activities are outsourced. Verbal or informal arrangements do not satisfy the requirement."},
{t:"Operational Ability, Outsourcing & Continuity",lv:"C",q:"Where an outsourced entity itself renders financial services, that entity must:",o:["Provide a written indemnity in favour of the outsourcing FSP","Be approved by the Authority as an accredited compliance practice","Hold professional indemnity cover equal to that of the FSP","Be an authorised financial services provider in its own right"],a:3,e:"A regulated function cannot be outsourced to an unauthorised party. An indemnity does not cure the absence of authorisation."},
{t:"Operational Ability, Outsourcing & Continuity",lv:"AP",q:"An FSP outsources policy administration under a brief verbal arrangement with no ongoing monitoring. Eighteen months later the provider suffers a data breach. The key individual argues this is the provider's responsibility. This position is:",o:["Incorrect — an SLA and oversight were required","Correct, since the operational function had been outsourced","Correct, provided the provider was reputable when selected","Incorrect only if client funds were affected by the breach"],a:0,e:"Outsourcing transfers the operational task, not the regulatory accountability. A weak arrangement with no monitoring is an oversight failure by the key individual, not merely a vendor failure."},
{t:"Operational Ability, Outsourcing & Continuity",lv:"K",q:"Business continuity planning by an FSP must address:",o:["Information technology systems and data backups only","Continued functioning of the business, including key-person risk","Physical premises and operating equipment only","Only those functions that have been outsourced to third parties"],a:1,e:"Continuity planning extends to the continued functioning of the business as a whole. Where a sole key individual is also the sole representative, their unavailability may require the FSP to cease rendering services."},
{t:"Operational Ability, Outsourcing & Continuity",lv:"AP",q:"A compliance review finds that client premiums are deposited into the FSP's main operating account and transferred to a separate account within 48 hours. The correct finding is that the FSP:",o:["Complies, the transfer occurring within a reasonable period","Complies, provided the account is reconciled every month","Does not comply, a separate designated account being required","Does not comply only where a client has suffered a loss"],a:2,e:"Client monies must be deposited into a separate account with a registered bank. There is no small-amount or short-duration exception to this requirement."},
{t:"Operational Ability, Outsourcing & Continuity",lv:"K",q:"Prescribed guarantees, professional indemnity and fidelity insurance cover for FSPs are prescribed by:",o:["Board Notice 80 of 2003","Board Notice 127 of 2010","Board Notice 194 of 2017","Board Notice 123 of 2009"],a:3,e:"BN 123 of 2009 prescribes these requirements and was not repealed by BN 194 of 2017, whose section 51 repealed only BN 105 of 2008, BN 106 of 2008 and BN 102 of 2012."},
{t:"Operational Ability, Outsourcing & Continuity",lv:"C",q:"All of the following are correct regarding professional indemnity cover, EXCEPT:",o:["Once arranged, cover requires no further ongoing monitoring","Prescribed cover must be implemented and actively maintained","A lapse in cover carries an automatic licence consequence","Renewal should be treated as a monitored, calendared event"],a:0,e:"Cover requires active, ongoing maintenance. Treating it as a once-off arrangement is precisely how lapses occur, and a lapse triggers automatic suspension without any grace period."},
{t:"Operational Ability, Outsourcing & Continuity",lv:"C",q:"Processes must exist to ensure that suitable providers are selected for outsourced functions. This obligation rests with the:",o:["Outsourced provider, as part of its own governance","FSP, as part of the key individual's oversight duty","Authority, through its accreditation of service providers","Compliance officer alone, acting independently of the KI"],a:1,e:"Due diligence on the selection of an outsourced provider forms part of the key individual's oversight duty. It is not discharged by the provider's own governance or by any accreditation process."},
{t:"Operational Ability, Outsourcing & Continuity",lv:"AN",q:"Which combination of the following is required where an FSP outsources an administration function?\ni) A written service level agreement\nii) A process for selecting a suitable provider\niii) Transfer of regulatory accountability to the provider\niv) Authorisation as an FSP where the provider renders financial services",o:["(i) and (iii) only","(ii), (iii) and (iv) only","(i), (ii) and (iv) only","(i), (ii), (iii) and (iv)"],a:2,e:"A written SLA, a suitable-provider selection process, and authorisation of any provider rendering financial services are all required. Regulatory accountability is never transferred by an outsourcing arrangement."},
{t:"FICA, AML & Terrorist Financing Oversight",lv:"K",q:"A Risk Management and Compliance Programme is required of an accountable institution by:",o:["Section 21 of the FIC Act","Section 29 of the FIC Act","Section 18 of the FAIS Act","Section 42 of the FIC Act"],a:3,e:"Section 42 of the FIC Act requires an accountable institution to develop, document, maintain and implement a Risk Management and Compliance Programme."},
{t:"FICA, AML & Terrorist Financing Oversight",lv:"K",q:"A report in respect of a suspicious or unusual transaction must be sent to the Financial Intelligence Centre as soon as possible and no later than:",o:["3 business days after the suspicion arises","7 days after the suspicion arises","15 days, excluding Saturdays, Sundays and public holidays","30 calendar days after the suspicion arises"],a:2,e:"Regulation 24(3) of the Money Laundering and Terrorist Financing Control Regulations prescribes 15 days, excluding Saturdays, Sundays and public holidays. The 3-business-day period applies to Cash Threshold Reports, and confusing the two is a common examination trap."},
{t:"FICA, AML & Terrorist Financing Oversight",lv:"K",q:"A cash threshold report must be filed where a cash transaction:",o:["Exceeds R49,999.99","Equals or exceeds R24,999.99","Equals or exceeds R100,000.00","Exceeds R1,000,000.00"],a:0,e:"The threshold is cash exceeding R49,999.99. It was raised from R24,999.99 with effect from 14 November 2022, and the superseded figure remains a common distractor."},
{t:"FICA, AML & Terrorist Financing Oversight",lv:"C",q:"In terms of the FIC Act, customer due diligence must be conducted:",o:["Within 30 days after the business relationship is established","Before establishing a relationship or concluding a transaction","Only where a single transaction exceeds the cash threshold","Only where the prospective client is a politically exposed person"],a:1,e:"Identification and verification must be completed before a business relationship is established or a single transaction concluded. Where due diligence cannot be completed, the institution may not proceed."},
{t:"FICA, AML & Terrorist Financing Oversight",lv:"AP",q:"A key individual is satisfied that the FSP is FICA-compliant because a copy of each client's identity document is always obtained. During a review, staff cannot explain the Risk Management and Compliance Programme and have never been trained on suspicious transaction patterns. The assumption was incorrect principally because FICA also requires:",o:["Certification of each identity document by a commissioner of oaths","Advance approval of every new client by the Financial Intelligence Centre","Ongoing due diligence, training and reporting duties","Retention of every identity document for a period of ten years"],a:2,e:"Identification is only one element of the framework. The Act also requires a functioning Risk Management and Compliance Programme, employee training, ongoing monitoring and reporting."},
{t:"FICA, AML & Terrorist Financing Oversight",lv:"C",q:"In terms of the FIC Act, an accountable institution that has filed a suspicious transaction report may:",o:["Inform the client of the report if it is done in writing","Inform the client once 30 days have elapsed since filing","Inform the client where the client makes a direct enquiry","Not disclose to the client that a report has been filed"],a:3,e:"Disclosing that a report has been or may be filed constitutes tipping off, which is a criminal offence. There is no exception for written form, elapsed time or a client enquiry."},
{t:"FICA, AML & Terrorist Financing Oversight",lv:"AP",q:"A client instructs a representative to split a large cash deposit into several smaller amounts, each below the reporting threshold, so that no report is filed. The representative should recognise that this conduct:",o:["Constitutes grounds for filing a suspicious report","Is permissible, each deposit falling below the threshold","Requires refusal of the instruction but creates no reporting duty","Must be reported to the Authority rather than to the Centre"],a:0,e:"Structuring transactions to avoid a reporting threshold is itself suspicious conduct giving rise to a reporting duty. Reports under the FIC Act are made to the Financial Intelligence Centre, not to the Authority."},
{t:"FICA, AML & Terrorist Financing Oversight",lv:"C",q:"Where an FSP outsources its FICA record keeping to a third party administrator, the FSP:",o:["Is relieved of those obligations by the outsourcing agreement","Remains responsible for compliance with those obligations","Must obtain the Authority's prior approval for the arrangement","Must retain duplicate records in printed form at its premises"],a:1,e:"The outsourcing agreement must be properly executed and FICA requirements continue to apply. Responsibility for compliance is not transferred by the arrangement."},
{t:"FICA, AML & Terrorist Financing Oversight",lv:"AN",q:"Which combination of the following would represent a genuine FICA oversight failure by a key individual?\ni) Assuming that strong FAIS compliance satisfies FICA obligations\nii) Verifying that the Risk Management and Compliance Programme functions\niii) Treating an outsourcing arrangement as removing FICA obligations\niv) Ensuring employees are trained on suspicious transaction reporting",o:["(ii) and (iv) only","(i), (ii) and (iii) only","(i) and (iii) only","(i), (ii), (iii) and (iv)"],a:2,e:"Statements i and iii describe genuine failures: FAIS competence does not extend to FICA, and outsourcing does not remove the obligation. Statements ii and iv describe correct oversight practice."},
{t:"Debarment — Process & KI Obligations",lv:"K",q:"A representative may be debarred where they have failed to comply with a provision of the FAIS Act:",o:["In any manner, however minor the lapse may be","Only where a client has suffered loss as a result","Only where a criminal conviction has been obtained","In a material manner, as opposed to a minor or technical lapse"],a:3,e:"Debarment requires a failure to comply in a material manner. Minor, non-material lapses are not automatic grounds, and neither client loss nor a criminal conviction is a precondition."},
{t:"Debarment — Process & KI Obligations",lv:"K",q:"Notification to the Authority of a debarment must be made within:",o:["3 days for Part I and 10 days for Part II","5 days for Part I and 15 days for Part II","15 days for Part I and 30 days for Part II","30 days for Part I and 60 days for Part II"],a:1,e:"Part I notification is due within 5 days of the debarment decision and Part II within 15 days of the debarment date."},
{t:"Debarment — Process & KI Obligations",lv:"AP",q:"An FSP establishes that a representative has forged client signatures over several months. The FSP terminates the employment relationship immediately and takes no further steps. In respect of the FAIS Act the FSP:",o:["Must still consider debarment and follow due process","Has discharged its obligations by ending the relationship","Must refer the matter to the Ombud for a determination","May not debar a person whose employment has already ended"],a:0,e:"Termination of employment does not displace the debarment process or the associated notification obligations. Failure to follow a fair, documented process creates a separate and avoidable problem for the FSP."},
{t:"Debarment — Process & KI Obligations",lv:"C",q:"Before effecting a debarment, an FSP must:",o:["Obtain the prior written approval of the Authority","Give notice of the intention to debar and a right to respond","Refer the matter to its compliance officer for a binding decision","Suspend the representative for a minimum period of thirty days"],a:1,e:"The process requires prior notice of the reasons debarment is being considered and a reasonable opportunity for the representative to make representations. Debarment is effected by the FSP without prior approval by the Authority."},
{t:"Debarment — Process & KI Obligations",lv:"C",q:"All of the following are correct regarding the debarment process, EXCEPT:",o:["The FSP must have internal debarment processes and apply them consistently","The representative must be informed of the recourse available to them","Serious misconduct removes the need for a fair, documented process","Representative agreements should record possible debarment grounds"],a:2,e:"Serious misconduct never removes the requirement for a fair, documented process. It may simply mean the outcome is more likely to be confirmed once that process has run."},
{t:"Debarment — Process & KI Obligations",lv:"AN",q:"Which combination of the following is required in respect of a debarment?\ni) The contravention must be material\nii) The representative must be given notice and an opportunity to respond\niii) The Authority must approve the debarment before it takes effect\niv) The Authority must be notified within the prescribed periods",o:["(i), (iii) and (iv) only","(ii) and (iii) only","(i), (ii), (iii) and (iv)","(i), (ii) and (iv) only"],a:3,e:"Debarment is effected by the FSP and does not require prior approval by the Authority. Materiality, procedural fairness and timely notification are all required."},
{t:"Addendum — Products, Auditing, Disclosure & Records",lv:"K",q:"Under FAIS, a \"financial product\" and an \"intermediary service\" are:",o:["Separately defined concepts, each with its own implications","Interchangeable terms describing the same regulated activity","Defined only in the General Code of Conduct, not the Act","Relevant only to providers holding Category II authorisation"],a:0,e:"The two are separately defined. Advising on a product and rendering an intermediary service in relation to that product are distinct concepts, each capable of triggering its own licensing and disclosure obligations."},
{t:"Addendum — Products, Auditing, Disclosure & Records",lv:"K",q:"Records required to be kept under section 18 of the FAIS Act must be retained for a period of:",o:["3 years","5 years","7 years","10 years"],a:1,e:"Section 18 prescribes a five-year retention period, calculated from the date the advice was given."},
{t:"Addendum — Products, Auditing, Disclosure & Records",lv:"AP",q:"A client relationship of eleven years is terminated. In respect of records kept under the FIC Act, the accountable institution must retain those records for:",o:["Five years from the date the relationship commenced","Five years from the date the relationship was terminated","Eleven years from the date the relationship commenced","Five years, but only where the client requests retention"],a:1,e:"FIC Act records relating to a business relationship run for five years from termination. This is a different starting point from the FAIS Act record, which runs from the date the advice was given."},
{t:"Addendum — Products, Auditing, Disclosure & Records",lv:"C",q:"Which ONE of the following is NOT correct regarding record keeping obligations?",o:["Records may be kept in either printed or electronic form","Records must be kept safe from destruction and be retrievable","Security and access requirements fall outside these obligations","Records must be available for inspection by the Authority"],a:2,e:"Security, confidentiality and controlled access form part of the record keeping obligations, having regard also to the Protection of Personal Information Act."},
{t:"Addendum — Products, Auditing, Disclosure & Records",lv:"C",q:"Where record keeping is outsourced to a third party, the FSP must ensure that:",o:["The third party assumes the statutory obligation entirely","The Authority has approved the third party in advance","Duplicate printed records are kept at the FSP's premises","The outsourcing agreement is properly executed and FICA applies"],a:3,e:"The agreement must be correctly executed and the underlying obligations continue to apply to the FSP. Outsourcing does not transfer the statutory duty."},
{t:"Addendum — Products, Auditing, Disclosure & Records",lv:"K",q:"Accounting and auditing requirements applicable to an FSP are prescribed in:",o:["Section 19 of the FAIS Act","Section 17 of the FAIS Act","Section 18 of the FAIS Act","Section 20 of the FAIS Act"],a:0,e:"Section 19 deals with accounting and auditing. Section 17 addresses compliance officers, section 18 record keeping, and section 20 the Ombud."},
{t:"Addendum — Products, Auditing, Disclosure & Records",lv:"AP",q:"An FSP that receives and holds client funds proposes appointing a family friend with general bookkeeping experience as its accounting officer. The correct position is that this:",o:["Is acceptable where the FSP is satisfied as to competence","Is not acceptable — prescribed criteria apply","Is acceptable provided the key individual supervises the work","Is acceptable for FSPs below a prescribed revenue threshold"],a:1,e:"Specific prescribed criteria govern who may be appointed as auditor or accounting officer. It is not an open commercial choice, and general competence does not satisfy the requirement."},
{t:"Addendum — Products, Auditing, Disclosure & Records",lv:"C",q:"Where an FSP receives client funds, the audit report submitted to the Authority must:",o:["Simply comprise the FSP's ordinary annual financial audit","Be certified by the FSP's appointed compliance officer","Comply with the requirements prescribed for such reports","Be lodged only where a client has queried the FSP's accounts"],a:2,e:"A generic financial audit is not automatically sufficient. The audit report must meet the requirements prescribed for FSPs receiving or holding client funds."},
{t:"Addendum — Products, Auditing, Disclosure & Records",lv:"K",q:"The register of representatives must be maintained by the:",o:["Financial Sector Conduct Authority","Appointed compliance officer's practice","Ombud Council","FSP itself"],a:3,e:"Section 13 requires the FSP to maintain the register. It is a primary compliance record that the Authority can expect to inspect."},
{t:"Addendum — Products, Auditing, Disclosure & Records",lv:"AP",q:"During an inspection an FSP produces a representatives register last updated eight months earlier. It lists two representatives who have resigned and omits one appointed three months ago. No client has suffered any loss. The finding should be that the FSP:",o:["Has failed to maintain the register as required","Has not contravened the Act, no client loss having occurred","Need only correct the register before the inspection concludes","Is in contravention only in respect of the omitted representative"],a:0,e:"The obligation to maintain an accurate and current register is freestanding. It does not depend on client loss, and correcting it during the inspection does not cure the failure."},
{t:"Addendum — Products, Auditing, Disclosure & Records",lv:"C",q:"Before appointing a representative, an FSP must:",o:["Obtain the prior written approval of the Authority","Satisfy itself that the applicable requirements are met","Place the person under supervision for a minimum of six months","Obtain a clearance certificate from the Ombud Council"],a:1,e:"The FSP must have processes confirming the individual meets the Fit and Proper requirements before appointment. Representatives are not individually pre-approved by the Authority."},
{t:"Addendum — Products, Auditing, Disclosure & Records",lv:"AN",q:"Which combination of the following correctly reflects requirements of the General Code of Conduct?\ni) Specific disclosures must be made before a financial service is rendered\nii) Client monies must be held in a separate account with a registered bank\niii) A small-amount exception applies to the holding of client monies\niv) Termination of an agreement must not unfairly disadvantage the client",o:["(i), (iii) and (iv) only","(ii) and (iii) only","(i), (ii) and (iv) only","(i), (ii), (iii) and (iv)"],a:2,e:"There is no small-amount exception to the separate account requirement, which is a bright-line rule. The remaining statements are genuine requirements of the Code."},{t:"KI Duties & Responsibilities",lv:"AP",q:"An FSP's sole KI resigns on 1 April. By when must the vacancy be filled before the FSCA must be notified of a persisting gap?",o:["Within 5 days, matching the debarment Part I notification deadline","Within 15 days","Within 30 days, matching the representative register update deadline","Within 60 days"],a:3,e:"A KI vacancy must be filled within 60 days, after which the FSCA must be notified of a persisting gap. 5 days and 30 days are real FAIS deadlines, but they govern debarment notification and register updates respectively."},
{t:"KI Duties & Responsibilities",lv:"C",q:"Thabo is appointed as a Key Individual on 1 August. When does his CPD cycle obligation begin?",o:["Immediately upon appointment — no qualifying period, unlike representatives","After his first CPD cycle as a representative is complete","6 years from his DOFA, the same as the Recognised Qualification deadline","Only once he has completed Class of Business Training"],a:0,e:"KI CPD begins immediately on appointment — there is no qualifying period, unlike the representative CPD start rule (which begins once all F&P requirements are met OR at 6 years from DOFA, whichever is first)."},
{t:"Twin Peaks & Regulatory Framework",lv:"AP",q:"An FSP's financial year ends on 28 February. By when must its annual compliance report reach the FSCA?",o:["31 March — one month later","30 April — two months later","30 June — four months later","31 August — six months later"],a:2,e:"The compliance report is due within 4 months of the FSP's financial year-end — 30 June for a 28 February year-end. This is the same 4-month window that applies to audited financial statements under the financial soundness requirement."},
{t:"Supervision — Process & KI Obligations",lv:"AN",q:"A representative was debarred for dishonesty on 1 June 2024, having met all other Fit & Proper requirements by that date. What is the earliest date the FSP could reappoint her?",o:["1 July 2024 — 1 month later","1 December 2024 — 6 months later","1 June 2025 — 12 months later, the minimum for dishonesty-related debarment","Immediately — dishonesty debarments carry no minimum wait, unlike incompetency debarments"],a:2,e:"Dishonesty-related debarment carries a minimum 12-month wait before reappointment. This is the reverse of incompetency debarment, which has no minimum wait — reappointment is possible as soon as F&P requirements are met."},
{t:"FAIS Ombud & Complaints",lv:"AP",q:"A complainant's claim is R5,000,000. The Ombud proposes to award the full amount. What must happen before this award can be made?",o:["Nothing — the Ombud may award any amount without restriction","The complainant must reduce the claim to R3,500,000","The respondent FSP must give written consent to exceed the R3,500,000 cap","The FSCA must approve any award exceeding R1,000,000"],a:2,e:"The standard cap is R3,500,000 per complaint. Exceeding it requires the respondent FSP's written consent — the Ombud cannot unilaterally award above the cap even where it believes the higher amount is justified."},
{t:"KI Duties & Responsibilities",lv:"K",q:"An FSP appoints a new representative on 10 May. By when must the representative register be updated to reflect this?",o:["Within 5 days — matching the debarment Part I deadline","Within 15 days","Within 30 days","Within 60 days — matching the KI vacancy deadline"],a:1,e:"The representative register must be updated within 15 days of a change. 5 days and 60 days are real FAIS deadlines, but they apply to debarment Part I notification and KI vacancies respectively — not routine register updates."},
{t:"Twin Peaks & Regulatory Framework",lv:"AN",q:"An FSP's capital adequacy briefly dips below the required level in March, then recovers by April before the FSP's June year-end. Must this be reported to the FSCA?",o:["No — only breaches still present at year-end must be reported","No — brief breaches that self-correct within the same financial year are exempt","Yes — capital adequacy breaches must be reported to the FSCA immediately upon discovery, regardless of later recovery","Yes, but only if the breach lasted longer than 30 days"],a:2,e:"Capital adequacy breaches must be reported immediately upon discovery — there is no year-end-only rule, no self-correction exemption, and no minimum-duration threshold. A breach that has already reversed by the time of discovery still triggers the reporting obligation."},
{t:"Governance & COI Management",lv:"C",q:"An FSP is deciding when to review its Conflict of Interest Management Policy. Which is correct?",o:["Every three years, aligned with the FICA retention cycle","Only when a specific conflict is identified","At least annually, and it must be published for public inspection","Every five years, aligned with the Recognised Qualification deadline"],a:2,e:"The COI Management Policy must be reviewed at least annually and published for public inspection at all reasonable times. Reviewing it only when a conflict happens to arise defeats its preventive purpose."},
{t:"KI Duties & Responsibilities",lv:"AP",q:"A Key Individual's DOFA is 1 September 2023. By when must she pass RE1?",o:["1 September 2024 — 12 months from DOFA","1 September 2025 — 24 months from DOFA","1 September 2029 — 6 years from DOFA, aligned with the Recognised Qualification deadline","There is no fixed deadline for KIs, only for representatives"],a:1,e:"The regulatory examination deadline is 24 months from DOFA — the same rule that applies to representatives sitting RE5 applies to Key Individuals sitting RE1. 12 months is the COB Training deadline and 6 years is the Recognised Qualification deadline — both real, both different rules."},
{t:"FAIS Ombud & Complaints",lv:"K",q:"A complainant is told by an outdated study guide that lodging a complaint with the FAIS Ombud costs R1,000. Is this correct?",o:["Yes — R1,000 is the standard case fee for complainants only","Yes — R1,000 is charged to the respondent FSP, not the complainant","No — the case fee was discontinued with effect from 10 September 2012; the service is free to both parties","No — the fee is R3,500, matching the (unrelated) financial cap figure in thousands"],a:2,e:"The FAIS Ombud discontinued its case fee with effect from 10 September 2012. This is a known exam trap — older material and some study guides still quote R1,000, but the service has been free to both complainants and respondent FSPs for well over a decade."},
{t:"KI Duties & Responsibilities",lv:"AN",q:"Which of the following statements about Key Individual obligations are correct? i. CPD begins immediately on appointment. ii. A KI vacancy must be filled within 60 days before FSCA notification is triggered. iii. Regulatory accountability may be fully delegated to a compliance officer. iv. A KI may also act as a representative, provided F&P is met for both roles.",o:["i, ii and iv only","i and ii only","ii, iii and iv only","i, ii, iii and iv"],a:0,e:"i, ii and iv are correct. Statement iii is false: day-to-day supervision may be delegated, but regulatory accountability cannot be — the KI remains personally answerable to the FSCA regardless of any delegation arrangement."},{t:"TCF & Consumer Outcomes",lv:"AN",q:"From a KI governance perspective, which TCF outcome is most directly engaged when an FSP's incentive structure rewards sales volume over client suitability?",o:["Outcome 1 — fair treatment central to culture","Outcome 2 — products designed for the target market","Outcome 4 — suitability of advice","Outcome 5 — products perform as expected"],a:0,e:"Outcome 1 concerns whether fair treatment is embedded as a cultural value. An incentive structure that structurally rewards volume over suitability is a governance-level Outcome 1 failure, distinct from an individual instance of unsuitable advice — which engages Outcome 4."},
{t:"KI Duties & Responsibilities",lv:"AP",q:"A KI notices reps consistently misclassifying LT Insurance Subcategory B2 policies as Subcategory A on the register. Why does this misclassification matter?",o:["It doesn't matter — both subcategories fall under exactly the same FAIS licence category and disclosure rules","Subcategory A policies are capped below R30,000 with simplified underwriting; B2 carries investment risk to the policyholder — different products, different obligations","Subcategory A is a Tier 1 category and B2 is Tier 2 — the misclassification affects only internal training records, not client protection","The FSCA licenses Subcategory A representatives only; B2 sales require no additional licensing category at all"],a:1,e:"Subcategory A (assistance/funeral, capped below R30,000, simplified underwriting) and Subcategory B2 (investment-linked, policyholder bears investment risk) are materially different products with different suitability and disclosure obligations. Misclassification on the register isn't a paperwork issue — it obscures what the FSP is actually licensed and equipped to sell."},
{t:"FICA, AML & Terrorist Financing Oversight",lv:"AN",q:"A KI is auditing the FSP's PEP onboarding file. Which of the following, if missing, represents a genuine FICA compliance gap? i. Senior management approval of the relationship. ii. Documented source of wealth and funds. iii. A signed declaration that the client will never become a PEP in future. iv. Evidence of enhanced ongoing monitoring.",o:["i and ii only","ii, iii and iv only","i, ii and iv only","i, ii, iii and iv"],a:2,e:"i, ii and iv are the real, required EDD components for a PEP file. Statement iii is invented — there is no such declaration requirement, and PEP status is monitored on an ongoing basis, not disclaimed by the client upfront."},
{t:"Addendum — Products, Auditing, Disclosure & Records",lv:"AN",q:"A KI is designing the FSP's record retention schedule. Which statement correctly reflects the three-way legislative overlap?",o:["FAIS, FICA and POPIA all specify exactly the same 5-year retention period, so one single schedule safely covers all three","FAIS governs advice records (5 years from advice); FICA governs CDD records (5 years from termination); POPIA governs how securely all of it is protected","POPIA overrides both FAIS and FICA retention periods entirely with its own stricter 10-year statutory requirement","Only FICA imposes any retention period at all — FAIS and POPIA are concerned solely with data quality, not retention"],a:1,e:"The three Acts answer different questions: FAIS sets the advice-record retention clock (5 years from advice given), FICA sets the CDD retention clock (5 years from relationship termination — a different starting point), and POPIA governs security and purpose-limited retention rather than setting its own fixed period."},
{t:"Governance & COI Management",lv:"AP",q:"Under King IV, whose ultimate responsibility is embedding TCF as a cultural value, not merely a compliance function?",o:["The compliance officer alone, since compliance functions own all conduct-related regulatory culture","The Key Individual alone, since embedding TCF is treated purely as a FAIS Ombud matter","The governing body — accountability for TCF culture cannot be delegated entirely to compliance","The FSCA directly, since TCF is externally supervised rather than internally owned by the FSP"],a:2,e:"King IV places ultimate accountability for TCF culture with the governing body. Compliance officers and KIs play essential operational roles, but treating TCF as solely a compliance department responsibility is itself a governance failure — the board must own the culture, not just monitor for breaches of it."},
{t:"Supervision of Representatives",lv:"AN",q:"A KI discovers that the FSP's telesales team has been failing to inform clients of a 31-day cooling-off right during distance-marketed LT insurance sales. What is the correct characterisation of this finding?",o:["A minor administrative oversight, since clients can still contact the insurer directly if they change their mind","A systemic TCF and disclosure failure requiring root cause analysis, remediation, and consideration of affected clients — not just future sales","Not a compliance issue at all — cooling-off rights are the insurer's disclosure obligation, not the FSP's","Only reportable if a client actually attempted to cancel and was refused"],a:1,e:"A systemic failure to disclose a mandatory cooling-off right is exactly the pattern that triggers a KI's root cause analysis obligation — it isn't a one-off error, and remediation must consider all affected clients, not just prevent recurrence going forward."},{t:"Operational Ability, Outsourcing & Continuity",lv:"AN",q:"Which of the following are correct regarding outsourcing under FAIS? i. A written service level agreement is mandatory. ii. An outsourced entity that itself renders financial services must be an authorised FSP. iii. Outsourcing transfers regulatory accountability to the outsourced provider. iv. The FSP must have a process to ensure suitable providers are selected.",o:["i and ii only","ii, iii and iv only","i, ii, iii and iv","i, ii and iv only"],a:3,e:"i, ii and iv are correct. Statement iii is wrong: outsourcing transfers the operational task, never the statutory accountability — the FSP remains responsible regardless of who performs the function."},
{t:"Operational Ability, Outsourcing & Continuity",lv:"AN",q:"Which of the following are required for an FSP to demonstrate operational ability? i. Adequate storage and filing systems. ii. Disaster recovery and business continuity plans. iii. At least two Key Individuals at all times, regardless of FSP size. iv. Prescribed guarantees, professional indemnity, or fidelity cover as applicable.",o:["i, ii and iv only","i and ii only","ii, iii and iv only","i, ii, iii and iv"],a:0,e:"i, ii and iv are genuine operational ability requirements. Statement iii is invented — there is no rule requiring at least two KIs regardless of FSP size; a single KI is permitted, though key-person risk must then be actively managed."},
{t:"Twin Peaks & Regulatory Framework",lv:"AN",q:"Which of the following are correct statements about the Twin Peaks regulatory model? i. The Prudential Authority is responsible for financial soundness. ii. The FSCA is responsible for market conduct and consumer protection. iii. The Prudential Authority reports to the FSCA. iv. Where multiple regulatory frameworks apply to an FSP, it must comply with all of them.",o:["i and ii only","ii, iii and iv only","i, ii and iv only","i, ii, iii and iv"],a:2,e:"i, ii and iv are correct. Statement iii is wrong: the Prudential Authority operates under the SARB — it is a separate peak of the Twin Peaks model, not in a reporting relationship with the FSCA."},
{t:"The Compliance Officer & Compliance Function",lv:"AN",q:"Which of the following are genuine duties of a Compliance Officer under FAIS? i. Monitoring FSP, KI and representative compliance with the General Code. ii. Reporting to the FSP and FSCA at set intervals. iii. Preparing and signing off the FSP's annual financial statements. iv. Making recommendations to the FSP regarding compliance with the Act.",o:["i and ii only","ii, iii and iv only","i, ii, iii and iv","i, ii and iv only"],a:3,e:"i, ii and iv are genuine Compliance Officer duties. Statement iii is wrong: preparing and signing off annual financial statements is an accounting/auditing function, not a Compliance Officer duty — a common source of confusion since both roles report on the FSP's regulatory position."}]);

const CRASH_QB = Object.freeze([
{t:"Rapid Fire",q:"Maximum FAIS Ombud compensation per complaint?",o:["R3,500,000","R1,000,000","R2,500,000","R10,000,000"],a:0,e:"The FAIS Ombud's financial cap is R3,500,000 per complaint. Exceeding this requires the respondent FSP's written consent."},
{t:"Rapid Fire",q:"Part I of the Debarment Notification must reach the FSCA within:",o:["2 days","5 days","10 days","15 days"],a:1,e:"Part I within 5 days; Part II within 15 days of debarment date."},
{t:"Rapid Fire",q:"An STR must be submitted to the FIC within:",o:["3 business days, matching the Cash Threshold Report deadline","10 days, excluding weekends and public holidays","15 days, excluding weekends and public holidays","30 days, excluding weekends and public holidays"],a:2,e:"STR to FIC within 15 days (excl. weekends/public holidays) of becoming aware."},
{t:"Rapid Fire",q:"The CPD cycle runs from:",o:["1 January to 31 December","1 April to 31 March","1 July to 30 June","1 June to 31 May"],a:3,e:"1 June to 31 May. Evidence submitted to FSP within 15 days of cycle end (by 15 June)."},
{t:"Rapid Fire",q:"Sole proprietors cannot operate under supervision because:",o:["They must meet ALL F&P requirements before licence is granted","They typically have too few clients to justify supervision","The FSCA must first approve any proposed supervisor","They are automatically exempt from all F&P requirements"],a:0,e:"Sole proprietors cannot operate under supervision. They must meet ALL F&P requirements before the FSP licence is granted."},
{t:"Rapid Fire",q:"The case fee currently charged by the FAIS Ombud when a complaint is accepted for investigation is:",o:["R1,000, payable by the respondent FSP","R1,000, payable by the complainant","Nil — the case fee was discontinued in 2012","R500, payable by the respondent FSP"],a:2,e:"The FAIS Ombud discontinued the case fee with effect from 10 September 2012. No fee is charged to the complainant or the respondent. The R1,000 figure appears in older material and remains a common distractor."},
{t:"Rapid Fire",q:"Which TCF outcome requires products to perform as firms have led customers to expect?",o:["Outcome 3","Outcome 4","Outcome 5","Outcome 6"],a:2,e:"TCF Outcome 5: customers are provided with products that perform as firms have led them to expect during the term of the product."},
{t:"FAIS Act",q:"FAIS promulgated and effective dates?",o:["Promulgated 2000; effective 2002","Promulgated 2001; effective 2003","Promulgated 2004; effective 2006","Promulgated 2002; effective 30 September 2004"],a:3,e:"Promulgated November 2002; effective 30 September 2004."},
{t:"Insurance",q:"Which principle prevents an insured from profiting from an insurance claim?",o:["Indemnity","Insurable interest","Utmost good faith","Subrogation"],a:0,e:"Indemnity: restores the insured to their pre-loss position — no more, no less."},
{t:"Products",q:"LT Insurance Subcategory A maximum benefit?",o:["Less than R50,000","Less than R30,000","Less than R10,000","No monetary limit"],a:1,e:"LT Insurance Subcategory A covers policies with maximum benefit less than R30,000 (typically funeral policies)."},
{t:"FICA",q:"Which body receives STRs in South Africa?",o:["The South African Revenue Service (SARS)","The SAPS Commercial Crimes Unit","The Financial Intelligence Centre (FIC)","The Financial Sector Conduct Authority (FSCA)"],a:2,e:"The FIC receives, analyses, and disseminates financial intelligence including STRs."},
{t:"Fit & Proper",q:"CPD hours for 1 class of business with 1 subclass per cycle?",o:["12 hours","18 hours","24 hours","6 hours"],a:3,e:"1 class / 1 subclass = 6 CPD hours per cycle."},
{t:"Fit & Proper",q:"Financial soundness as a F&P requirement does NOT apply to:",o:["Key Individuals","Representatives","Financial Services Providers","Compliance officers"],a:0,e:"Financial soundness does NOT apply to key individuals — only to FSPs, representatives, and compliance officers."},
{t:"General Code",q:"Which marketing practice is specifically prohibited by the General Code?",o:["Advertising a financial product on social media without prior FSCA pre-approval","Negative option marketing — products activate automatically unless the client declines","Comparative advertising that references named competitors without substantiation","Offering promotional or discounted rates exclusively to newly onboarded clients"],a:1,e:"Negative option marketing is specifically prohibited by the General Code of Conduct."},
{t:"Supervision",q:"Dishonesty debarment minimum waiting period before reappointment?",o:["6 months","No waiting period","12 months","24 months"],a:2,e:"Dishonesty-based debarment: minimum 12 months from debarment date before reappointment."},
{t:"KI",q:"For KIs, CPD obligations begin:",o:["Only once RE1 has been formally completed","Only after 12 months from the date of appointment","After the 6-year qualifying period has elapsed from DOFA","Immediately upon appointment — no qualifying period"],a:3,e:"For KIs, CPD begins immediately upon appointment. No qualifying period."},
{t:"FAIS Ombud",q:"A FAIS Ombud ruling is legally equivalent to:",o:["A binding arbitration award","A voluntary mediation award","A formal FSCA compliance directive","A High Court civil judgement"],a:3,e:"FAIS Ombud determination = High Court civil judgement. A writ of execution may follow non-compliance."},
{t:"Regulatory",q:"FSP must submit annual financial statements to FSCA within:",o:["12 months of financial year-end","4 months of financial year-end","6 months of financial year-end","3 months of financial year-end"],a:1,e:"Financial statements within 4 months of the FSP's financial year-end."},
{t:"General Code",q:"COI immaterial financial interest threshold per year from same third party?",o:["R5,000 per annum from all third parties","Any interest disclosed in the ROA","R1,000 per calendar year from same third party","R500 per transaction"],a:2,e:"R1,000 per calendar year from the same third party = immaterial. Above R1,000 = must be disclosed."},
{t:"FICA",q:"What is the consequence of 'tipping off' a client after an STR has been submitted?",o:["A formal FSCA warning is issued to the FSP","It is a criminal offence under FICA","The STR is automatically invalidated","SAPS must be notified within 24 hours"],a:1,e:"Tipping off — disclosing to a subject that an STR has been or will be submitted — is a criminal offence under FICA. It does not invalidate the STR."},
{t:"Debarment",q:"The correct debarment process sequence under FAIS is:",o:["Notice of intention → representations → decision","Suspension → hearing → FSCA report","FSCA initiates and FSP reports only","Investigation → suspension → notification"],a:0,e:"Notice → Representations → Decision. The FSP — not the FSCA — executes the debarment."},
{t:"KI",q:"A Key Individual under FAIS must be:",o:["A registered professional body","A natural (physical) person only","A juristic person such as a company","A senior employee with 10+ years experience"],a:1,e:"A KI can ONLY be a natural person. Juristic persons cannot be appointed as key individuals."},
{t:"AML",q:"Three stages of money laundering in correct order:",o:["Integration → Layering → Placement","Layering → Placement → Integration","Placement → Layering → Integration","Concealment → Transfer → Legitimisation"],a:2,e:"(1) Placement — introducing funds; (2) Layering — obscuring the trail; (3) Integration — reintroducing as legitimate assets."},
{t:"TCF",q:"TCF Outcome 2 specifically addresses:",o:["Suitability of advice to individual client circumstances","No unreasonable post-sale barriers to switching or complaining","Fair treatment as central to the firm's culture","Products designed to meet the needs of identified consumer groups"],a:3,e:"TCF Outcome 2: products and services must be designed to meet the needs of the identified consumer group and target market."},
{t:"Supervision",q:"What must a supervised representative proactively disclose to every client?",o:["That they are rendering financial services under supervision","The FSP's annual revenue and commission rates","Their supervisor's full name and direct contact number","The commission payable on each product being recommended"],a:0,e:"Supervised representatives must disclose to each client before rendering services that they are operating under supervision."},
,{t:"Fit & Proper",q:"Regulatory exam (RE5 for reps, RE1 for KIs) deadline from DOFA?",o:["12 months","18 months","24 months","36 months"],a:2,e:"Both RE5 and RE1 must be passed within 24 months of DOFA. 12 months is the COB Training deadline — a different rule entirely."},
{t:"Fit & Proper",q:"COB Training deadline from initial appointment?",o:["6 months","12 months","18 months","24 months"],a:1,e:"COB Training = 12 months from initial appointment. 24 months is the RE5/RE1 deadline — don't mix them up."},
{t:"Fit & Proper",q:"Recognised Qualification deadline from DOFA?",o:["3 years","4 years","5 years","6 years"],a:3,e:"Recognised Qualification = 6 years from DOFA — the longest of the three DOFA-linked deadlines."},
{t:"Debarment",q:"Part II of the Debarment Notification must reach the FSCA within:",o:["5 days","10 days","15 days","30 days"],a:2,e:"Part II within 15 days of the debarment date. Part I is the shorter 5-day deadline — a different form, different clock."},
{t:"FAIS Ombud",q:"A complainant has how long from the FSP's final response to approach the Ombud?",o:["30 days","3 months","6 months","12 months"],a:2,e:"6 months from the FSP's final response. Don't confuse this with the FSP's own 6-week window to resolve the complaint first."},
{t:"FAIS Ombud",q:"An FSP has how long to resolve a complaint before the Ombud may accept jurisdiction?",o:["2 weeks","4 weeks","6 weeks","8 weeks"],a:2,e:"6 weeks for the FSP to resolve internally. This is the FSP's clock — the complainant's own 6-month window to approach the Ombud starts only after the FSP's final response."},
{t:"Record-Keeping",q:"FICA/CDD records must be kept for how long after the business relationship terminates?",a:1,o:["3 years","5 years","7 years","10 years"],e:"5 years from termination of the relationship — not from the last transaction or last advice given. A different starting point to Records of Advice."},
{t:"Record-Keeping",q:"Records of Advice (ROA) must be kept for how long, counted from when?",o:["5 years from the date the advice was given","5 years from the date the relationship terminates","7 years from the date the advice was given","10 years from policy inception"],a:0,e:"ROA = 5 years from the date the advice was given. FICA/CDD records also run 5 years, but from relationship termination — same number, different clock."},
{t:"FICA",q:"A Cash Threshold Report is triggered by a single cash transaction exceeding:",o:["R24,999.99","R49,999.99","R99,999.99","R149,999.99"],a:1,e:"CTR threshold: cash transactions exceeding R49,999.99. STRs, by contrast, apply to any suspicious transaction regardless of amount."},
{t:"General Code",q:"Cash premium received from a client must be deposited into the separate client account within:",o:["Same business day, before close of business","1 business day","3 business days","7 calendar days"],a:1,e:"1 business day of receipt. No batching, no month-end exception, no same-day requirement — the rule is 1 business day."},
{t:"KI",q:"A vacant KI position must be filled within how long before FSCA notification is triggered?",o:["15 days","30 days","60 days","90 days"],a:2,e:"60 days to fill a KI vacancy before the FSCA must be notified of a persisting gap."},
{t:"General Code",q:"When must the original insurer be notified of a long-term insurance replacement?",o:["Within 14 days of the new policy's inception","No later than when the new application is submitted","Within 30 days of the replacement being finalised","Only if the client explicitly requests notification"],a:1,e:"The original insurer must be notified no later than the time the new application is submitted — not after the fact."},
{t:"General Code",q:"The Conflict of Interest Management Policy must be reviewed at minimum:",o:["Every 6 months","Annually","Every 2 years","Every 5 years"],a:1,e:"At least annually, and published for public inspection at all reasonable times."}]);

const TOP50 = Object.freeze([
"1. FAIS promulgated: November 2002 | Effective: 30 September 2004",
"2. Two FAIS objectives: Trust in the industry + Systemic stability",
"3. Three elements for FAIS to apply: financial service + financial product + client",
"4. Advice = recommendation / guidance / proposal about a financial product",
"5. Intermediary service = any act OTHER THAN advice between product supplier and client",
"6. KI must be a NATURAL PERSON — cannot be a company or group",
"7. KI establishes compliance function; CO monitors and reports on it",
"8. CO required when: >1 KI OR >1 representative",
"9. Representatives are authorised by the FSP — not independently licensed",
"10. FSP is liable for the conduct of its representatives",
"11. Four general duties: Honestly; Due skill/care/diligence; Client interests; Industry integrity",
"12. Record of Advice: basis of advice + products considered + reasons for suitability",
"13. COI immaterial threshold: R1,000 per year from the same third party",
"14. Client funds: deposit to designated separate account within 1 BUSINESS DAY",
"15. LT insurance replacement: notify original insurer no later than when application is submitted",
"16. COI Policy: reviewed ANNUALLY; published for public inspection",
"17. Churning = switching products unnecessarily to generate commission — PROHIBITED",
"18. DOFA = Date of First Appointment — the date a person was first appointed as a rep or KI",
"19. RE5 deadline: within 24 MONTHS of DOFA",
"20. COB Training: within 12 MONTHS of initial appointment",
"21. Recognised Qualification: within 6 YEARS of DOFA",
"22. CPD cycle: 1 June – 31 May | Submit evidence by 15 June",
"23. CPD hours: 6h (1 class/1 sub) | 12h (1 class/2+ sub) | 18h (2+ classes)",
"24. CPD starts: ALL requirements met OR 6 years from DOFA — whichever FIRST",
"25. Financial soundness does NOT apply to KIs",
"26. Sole proprietors CANNOT operate under supervision",
"27. Supervision agreement: must be in WRITING BEFORE supervised rep starts",
"28. Supervised rep: must disclose supervision status to every client",
"29. Debarment grounds (s14(1)): TWO — no longer F&P (s13(2)(a)); material contravention. Aiding a contravention is FSRA s153, a different route",
"30. Debarment process: Notice → Representations → Decision (FSP executes — not FSCA)",
"31. Post-debarment FSCA reporting: Part I = 5 DAYS; Part II = 15 DAYS",
"32. Dishonesty debarment: minimum 12 MONTHS before reappointment",
"33. Incompetency debarment: reappointment when F&P requirements met — NO wait",
"34. FAIS Ombud: poor advice or poor intermediary service — NOT pure product performance",
"35. Ombud financial cap: R3,500,000 (respondent consent required to exceed)",
"36. Ombud ruling = High Court civil judgement",
"37. FSP has 6 WEEKS to resolve complaint before Ombud accepts jurisdiction",
"38. Complainant has 6 MONTHS from FSP's final response to approach Ombud",
"39. Ombud case fee: NONE — discontinued 10 September 2012. Free to complainants and respondents alike",
"40. 3 stages of money laundering: Placement → Layering → Integration",
"41. STR to FIC: within 15 DAYS (excl. weekends/public holidays). CTR: within 3 BUSINESS DAYS",
"42. Cash Threshold Report: cash transactions > R49,999.99",
"43. Tipping off = criminal offence under FICA",
"44. Structuring (smurfing) to avoid thresholds = suspicious = file STR",
"45. FIC intervention (s34): duration set out in the intervention order, excl. weekends & public holidays (FIC Guidance Note 4B)",
"46. FICA records: 5 years from relationship termination or transaction",
"47. PEPs: senior management approval + source of wealth/funds + enhanced monitoring",
"48. PoCA: criminalises money laundering; allows civil asset forfeiture",
"49. TCF Outcome 4 = suitability of advice to individual circumstances",
"50. TCF Outcome 6 = no unreasonable post-sale barriers",
]);

// RE Fact Disk™ — Top 50 facts as question/answer pairs.
// Derived from TOP50; fronts written as cues where the fact is a statement.
const FACT_DISK = Object.freeze([{"n": 1, "front": "FAIS promulgated", "back": "November 2002 | Effective: 30 September 2004"}, {"n": 2, "front": "Two FAIS objectives", "back": "Trust in the industry + Systemic stability"}, {"n": 3, "front": "Three elements for FAIS to apply", "back": "financial service + financial product + client"}, {"n": 4, "front": "What counts as ADVICE?", "back": "Advice = recommendation / guidance / proposal about a financial product"}, {"n": 5, "front": "What is an intermediary service?", "back": "Intermediary service = any act OTHER THAN advice between product supplier and client"}, {"n": 6, "front": "Can a company be a Key Individual?", "back": "KI must be a NATURAL PERSON — cannot be a company or group"}, {"n": 7, "front": "KI vs Compliance Officer — who does what?", "back": "KI establishes compliance function; CO monitors and reports on it"}, {"n": 8, "front": "CO required when", "back": ">1 KI OR >1 representative"}, {"n": 9, "front": "How is a representative authorised?", "back": "Representatives are authorised by the FSP — not independently licensed"}, {"n": 10, "front": "Who is liable for a rep's conduct?", "back": "FSP is liable for the conduct of its representatives"}, {"n": 11, "front": "Four general duties", "back": "Honestly; Due skill/care/diligence; Client interests; Industry integrity"}, {"n": 12, "front": "Record of Advice", "back": "basis of advice + products considered + reasons for suitability"}, {"n": 13, "front": "COI immaterial threshold", "back": "R1,000 per year from the same third party"}, {"n": 14, "front": "Client funds", "back": "deposit to designated separate account within 1 BUSINESS DAY"}, {"n": 15, "front": "LT insurance replacement", "back": "notify original insurer no later than when application is submitted"}, {"n": 16, "front": "COI Policy", "back": "reviewed ANNUALLY; published for public inspection"}, {"n": 17, "front": "What is churning?", "back": "Churning = switching products unnecessarily to generate commission — PROHIBITED"}, {"n": 18, "front": "What does DOFA stand for?", "back": "Date of First Appointment — the date a person was first appointed as a representative or key individual"}, {"n": 19, "front": "RE5 deadline", "back": "within 24 MONTHS of DOFA"}, {"n": 20, "front": "COB Training", "back": "within 12 MONTHS of initial appointment"}, {"n": 21, "front": "Recognised Qualification", "back": "within 6 YEARS of DOFA"}, {"n": 22, "front": "CPD cycle", "back": "1 June – 31 May | Submit evidence by 15 June"}, {"n": 23, "front": "CPD hours", "back": "6h (1 class/1 sub) | 12h (1 class/2+ sub) | 18h (2+ classes)"}, {"n": 24, "front": "CPD starts", "back": "ALL requirements met OR 6 years from DOFA — whichever FIRST"}, {"n": 25, "front": "Does financial soundness apply to KIs?", "back": "Financial soundness does NOT apply to KIs"}, {"n": 26, "front": "Can a sole proprietor work under supervision?", "back": "Sole proprietors CANNOT operate under supervision"}, {"n": 27, "front": "Supervision agreement", "back": "must be in WRITING BEFORE supervised rep starts"}, {"n": 28, "front": "Supervised rep", "back": "must disclose supervision status to every client"}, {"n": 29, "front": "How many grounds for debarment under s14(1)?", "back": "TWO: (1) no longer meets Fit & Proper (s13(2)(a)); (2) material contravention of the Act. Aiding a contravention is FSRA s153 — FSCA debarment, a different route."}, {"n": 30, "front": "Debarment process", "back": "Notice → Representations → Decision (FSP executes — not FSCA)"}, {"n": 31, "front": "Post-debarment FSCA reporting", "back": "Part I = 5 DAYS; Part II = 15 DAYS"}, {"n": 32, "front": "Dishonesty debarment", "back": "minimum 12 MONTHS before reappointment"}, {"n": 33, "front": "Incompetency debarment", "back": "reappointment when F&P requirements met — NO wait"}, {"n": 34, "front": "FAIS Ombud", "back": "poor advice or poor intermediary service — NOT pure product performance"}, {"n": 35, "front": "Ombud financial cap", "back": "R3,500,000 (respondent consent required to exceed)"}, {"n": 36, "front": "What force does an Ombud ruling carry?", "back": "Ombud ruling = High Court civil judgement"}, {"n": 37, "front": "How long does the FSP get to resolve a complaint?", "back": "FSP has 6 WEEKS to resolve complaint before Ombud accepts jurisdiction"}, {"n": 38, "front": "How long does a complainant have to approach the Ombud?", "back": "Complainant has 6 MONTHS from FSP's final response to approach Ombud"}, {"n": 39, "front": "Ombud case fee", "back": "NONE — discontinued 10 September 2012. Free to complainants and respondents alike"}, {"n": 40, "front": "3 stages of money laundering", "back": "Placement → Layering → Integration"}, {"n": 41, "front": "STR to FIC", "back": "within 15 DAYS (excl. weekends/public holidays). CTR: within 3 BUSINESS DAYS"}, {"n": 42, "front": "Cash Threshold Report", "back": "cash transactions > R49,999.99"}, {"n": 43, "front": "What is tipping off?", "back": "Disclosing to anyone (incl. the client) that an STR was filed, or its contents — criminal offence under FICA s29"}, {"n": 44, "front": "What is structuring (smurfing)?", "back": "Structuring (smurfing) to avoid thresholds = suspicious = file STR"}, {"n": 45, "front": "How long does a FIC intervention (s34) last?", "back": "For the period specified in the intervention order, excluding weekends and public holidays (FIC Guidance Note 4B)"}, {"n": 46, "front": "FICA records", "back": "5 years from relationship termination or transaction"}, {"n": 47, "front": "PEPs", "back": "senior management approval + source of wealth/funds + enhanced monitoring"}, {"n": 48, "front": "PoCA", "back": "criminalises money laundering; allows civil asset forfeiture"}, {"n": 49, "front": "Which TCF outcome covers suitability of advice?", "back": "TCF Outcome 4 = suitability of advice to individual circumstances"}, {"n": 50, "front": "Which TCF outcome covers post-sale barriers?", "back": "TCF Outcome 6 = no unreasonable post-sale barriers"}]);


// ══════════════════════════════════════════════════════
// HARD MOCK EXAM BANKS — Phase 4 preparation
// Higher difficulty: AN-heavy, multi-step reasoning, layered scenarios.
// Powers the "Hard Mode" simulations (RE5 Hard / RE1 Hard).
// ══════════════════════════════════════════════════════

const QB_HARD_RE5 = Object.freeze([
{t:"Scope & Definitions",lv:"AN",q:"A financial wellness app sends automated push notifications to users saying 'Users like you typically increase retirement contributions by 15% at this age — consider doing the same.' The notification is generated by an algorithm using aggregated, anonymised behavioural data, not individual client analysis. A user later argues this constituted FAIS advice. Is this correct?",o:["Yes — any statement that could influence a financial decision, including aggregated behavioural nudges, satisfies all 3 FAIS elements and requires FSP authorisation regardless of personalisation","No — the notification is generic behavioural messaging not directed at an identifiable client's individual circumstances, and does not specify a financial product — it likely falls short of the advice definition, though the line requires careful ongoing assessment","Yes — digital delivery of financial suggestions to identified, registered app users constitutes a personalised recommendation under FAIS because the notification system knows the user's demographic profile and age, making the communication individually directed even if the underlying analysis uses cohort datates advice because the user is identifiable even if the analysis is aggregated","Yes — the recommendation to increase contributions references a financial product category (retirement funds) and reaches specific registered users, meaning both the product and client elements of the 3-element test are satisfied regardless of whether individual circumstances were specifically analysedt category (retirement funds) and is directed at a specific registered user, satisfying all 3 elements"],a:1,e:"The 3-element test requires the service be rendered TO A CLIENT — a specific identifiable individual based on their own circumstances. Generic behavioural nudges based on anonymous cohort data, without specifying a particular product or analysing the individual's situation, likely fall short of the advice threshold — though FSPs operating in this space should obtain legal guidance given the evolving regulatory position on algorithmic nudging."},
{t:"General Code of Conduct",lv:"AN",q:"An adviser's ROA for a R2.5m investment states: 'Client's risk tolerance assessed as moderate. Recommended balanced portfolio.' The client had a clearly stated time-bound objective — university fees in 3 years — that the adviser noted verbally but did not record. The investment underperforms. The Ombud assesses the ROA. What is the most defensible characterisation of this ROA?",o:["Adequate — the ROA documents that a proper process was followed, the risk assessment was moderate, and a balanced portfolio is objectively appropriate for moderate risk regardless of the specific objective","Likely inadequate — a generic risk-tolerance note without documenting the client's specific, time-bound objective (university fees) and how it shaped the recommendation fails to demonstrate suitability for THIS client's situation","Adequate — verbal documentation of objectives satisfies the Code provided the adviser can testify to what was discussed in an Ombud hearing","Adequate if the recommended product was a balanced fund, since moderate-risk products are always suitable for 3-year horizons regardless of other factors"],a:1,e:"A bare risk-tolerance label without documenting specific client objectives that materially shape suitability does not demonstrate that advice was tailored to the individual. Where the client has a clearly time-bound objective, the ROA must show how that specific constraint was considered — generic process notes are insufficient."},
{t:"Fit & Proper Requirements",lv:"AN",q:"A representative's DOFA at FSP A is 1 March 2023. She resigns on 1 January 2024 without passing RE5 and joins FSP B on 1 February 2024. FSP B argues her DOFA resets to 1 February 2024 giving her a fresh 24-month window. FSP A's KI argues the original deadline continues. Which position is more consistent with regulatory intent?",o:["FSP B's position is correct — DOFA is employer-specific and resets with each new appointment, meaning the representative has until 1 February 2026 to pass RE5","FSP A's KI's position is more consistent with regulatory intent — F&P deadlines follow the individual's career history and are not reset by changing employers, preventing indefinite deadline avoidance through FSP-hopping","FSP B's position is correct — only the FSCA can definitively determine whether a DOFA resets on re-appointment, and absent a binding FSCA ruling to the contrary, each FSP is entitled to apply its own reasonable interpretation of when an individual's DOFA deadline commenced for purposes of F&P monitoring and absent a specific ruling, each FSP applies its own interpretation","Both positions are equally valid — the FAIS Act is genuinely ambiguous on multi-employer DOFA calculation and the FSCA has not issued binding guidance, meaning both the FSP A and FSP B positions represent defensible interpretations of an unsettled regulatory question that cannot be resolved without a formal FSCA rulingOFA calculation and the FSCA has not issued binding guidance either way"],a:1,e:"F&P deadlines are designed to track individual competency development, not reward employer changes as a compliance clock reset mechanism. The general regulatory principle is that DOFA-linked deadlines follow the individual's timeline — a representative cannot indefinitely defer RE5 by changing employers."},
{t:"Supervision & Debarment",lv:"AN",q:"An FSP's debarment committee follows the Notice → Representations → Decision process for a representative accused of dishonesty. The committee receives additional internal evidence after the representations phase closes and relies on it in the decision without disclosing it to the representative. She challenges the debarment citing procedural unfairness. What is the most accurate assessment?",o:["The challenge is well-founded — procedural fairness requires a genuine opportunity to respond to ALL evidence actually relied upon in the decision. Deciding on undisclosed post-representation evidence undermines the representations process, making the debarment procedurally vulnerable regardless of the underlying merits","The debarment is valid — committees are entitled to consider any evidence in their possession regardless of when it emerged, provided the final decision is honestly reached","The challenge fails because the representative already had her full opportunity to present representations on the substantive dishonesty allegation, and procedural fairness does not require multiple representation opportunities whenever new evidence emerges — the right to represent is exhausted after the initial formal representations phasesent representations and cannot demand multiple opportunities to respond to evidence","The debarment is valid provided the undisclosed post-representations evidence was corroborative of evidence already presented to the representative, because corroborative evidence that merely supports an existing finding does not materially affect the representative's ability to mount a defence and therefore does not create procedural unfairnessr than the primary basis for the decision"],a:0,e:"Procedural fairness in debarment requires the person have a genuine opportunity to respond to evidence actually relied upon. Post-representation evidence used in the decision, without disclosure and a further opportunity to respond, is a fundamental procedural flaw — even if the dishonesty allegation may ultimately be substantiated on the original evidence alone."},
{t:"FAIS Ombud",lv:"AN",q:"A FAIS Ombud case awards R3,800,000 to a complainant. The FSP argues the award exceeds the R3,500,000 cap and is automatically void. The complainant argues it is valid. Which party is correct and why?",o:["The FSP — any award exceeding R3,500,000 is automatically void ab initio regardless of circumstances, because the Ombud's jurisdiction is a hard statutory ceiling that cannot be exceeded under any circumstances","The FSP — but only the R300,000 excess is void. The FSP must still pay R3,500,000 as the maximum enforceable portion of the determination","The complainant — the financial cap only applies to individual natural persons, not to corporate FSPs, so a R3,800,000 award against an FSP entity is always within jurisdiction","The complainant — the R3,500,000 cap can be exceeded if the respondent FSP provided written consent to a higher award. The key question is whether consent was given — if yes, R3,800,000 is valid and enforceable"],a:3,e:"The R3,500,000 cap can be exceeded with the respondent FSP's written consent — this is an important exception that examiners specifically test. Without consent, the award would need to be limited to R3,500,000. The FSP's position depends entirely on whether it provided written consent during the proceedings."},
{t:"FICA & AML",lv:"AN",q:"An FSP's compliance officer identifies a client whose transaction pattern matches known money laundering typologies. The client generates 15% of total FSP commission income. Senior management asks the compliance officer to 'take another look before filing anything' given the commercial relationship. The compliance officer faces a direct instruction from a superior. What is the correct response?",o:["Request a second independent compliance review before filing — this is reasonable due diligence that protects the FSP if the STR is later challenged, and accommodates management's legitimate concern about accuracy","Decline to delay the assessment based on commercial pressure — STR filing decisions must be made purely on the suspicious activity evidence, not influenced by client revenue. The commercial pressure itself should be escalated as a governance concern to the KI or board","File the STR but delay submission by 14 days to allow management to engage the client directly and obtain an explanation that could be included in the STR filing","Comply with management's request for a second review — senior management has authority over compliance decisions and the compliance officer's role is advisory, not determinative"],a:1,e:"STR filing decisions must be entirely independent of commercial considerations. Yielding to pressure to delay or avoid a legitimate STR based on client revenue exposes both the compliance officer and the FSP to severe regulatory and criminal consequences. The management pressure itself is a governance red flag requiring escalation."},
{t:"TCF",lv:"AN",q:"An FSP's performance data shows 94% of policies perform as illustrated. However, the 6% underperforming policies are concentrated almost entirely among lower-income clients sold through a high-pressure sales channel. The KI reviews aggregate performance and concludes TCF compliance is strong. A thematic review disagrees. What did the KI miss?",o:["Nothing material — a 94% aggregate success rate is a strong TCF outcome by any standard, and the 6% underperforming policies represent normal statistical variance that occurs in any product with market exposure. TCF Outcome 5 assesses aggregate product performance against illustration, not the demographic distribution of individual policy outcomes within the underperforming minorityrs in any population segment do not constitute a TCF failure requiring investigation or remediation","The KI should have used demographic-weighted performance metrics that adjust for the different product mix and risk profiles of lower-income versus higher-income client segments before reaching any TCF conclusion, because raw aggregate performance data cannot be compared across demographically heterogeneous client groups without appropriate statistical normalisation differences before reaching a TCF conclusion, rather than using unweighted aggregate figures","TCF Outcome 5 only concerns whether products perform as illustrated overall — demographic distribution of performance outcomes is a separate equity issue outside TCF's regulatory scope","TCF requires examining whether different client groups experience systematically different outcomes. A concentration of underperformance in one demographic linked to a specific high-pressure channel is a systemic suitability signal requiring investigation — aggregate success metrics mask this structural inequity"],a:3,e:"TCF requires examining whether different client groups experience materially different outcomes, not just aggregate performance. A concentration of failures in a specific demographic linked to a high-pressure sales channel is a systemic TCF signal the KI must investigate regardless of how good the overall numbers look."},
{t:"Insurance Principles",lv:"AN",q:"A commercial property is insured for R2,000,000 but its actual replacement value at the time of loss is R4,000,000. A fire causes R800,000 in damage. The policy contains an average clause. The client expects to receive R800,000. What will the insurer actually pay and why?",o:["R400,000 — the average clause applies proportionately: payout = loss × (insured value ÷ actual value) = R800,000 × (R2,000,000 ÷ R4,000,000) = R400,000. The client absorbs the remaining R400,000 as the consequence of being 50% underinsured","R800,000 — the insurer pays the full loss amount regardless of the insured-to-value ratio because the average clause only applies to total losses, not partial losses","R600,000 — the insurer applies a standard underinsurance loading of 25% of the loss amount as an administrative penalty for failing to insure to full replacement value, in addition to paying the proportionate loss amount, with the loading designed to discourage chronic underinsurance as a premium-reduction strategy loss amount regardless of the actual insured-to-value ratio","R800,000 minus the applicable policy excess — the average clause only activates when the insured value is less than 75% of actual replacement value at the time of loss, and since R2,000,000 is exactly 50% of R4,000,000, the calculation falls precisely at the boundary where some courts have found the average clause does not trigger on proportionate basise insured value is less than 75% of actual value, which is not the case here"],a:0,e:"The average clause applies proportionately when the property is underinsured. Payout = loss × (insured value ÷ actual value) = R800,000 × 50% = R400,000. The insured is their own insurer for the uninsured portion. This is one of the most practically important and frequently misunderstood insurance concepts."},
{t:"Long-term Insurance Products",lv:"AN",q:"A client holds a SubCat B2 investment-linked endowment in year 3 of its 5-year restriction period. The underlying fund loses 35% in a market downturn. She wants early access to cover a genuine emergency and argues the loss justifies waiving the restriction. Which statement most accurately describes her regulatory and contractual position?",o:["She faces two compounding disadvantages: the restriction period rules apply regardless of why early access is sought, AND as a B2 (market-linked) policy she bears the full investment risk of the 35% loss — the insurer provides no guarantee of either access or capital protection","She may access the funds penalty-free because a 35% market loss constitutes a material adverse change in the investment circumstances that fundamentally alters the product's risk-return profile, triggering the insurer's statutory obligation to provide early access without penalty under the consumer protection provisions of the Long-term Insurance Actterial change in circumstances that triggers an automatic restriction waiver under B2 policy terms","She may access the funds but must pay a market-value adjustment penalty calculated at the time of early withdrawal, and the insurer must make this available regardless of the restriction period","The restriction period automatically suspends during periods of market loss exceeding 20% under the Long-term Insurance Act's hardship provisions, creating a temporary early access window during which the policyholder may withdraw without penalty — the suspension applies automatically without requiring application and lasts until the market recovers to the pre-loss leveleeding 20%, giving her a temporary access window without penalty"],a:0,e:"This layers two separate concepts. B2 restriction period rules apply regardless of the cause of early access request — market losses do not create an exception. B2 also means the client bears full investment risk with no guarantee. Both factors compound her position."},
{t:"Short-term Insurance Products",lv:"AN",q:"A commercial fleet policy has a R15,000 compulsory excess and R5,000 voluntary excess per claim. A single accident damages two fleet vehicles with separate repair costs of R40,000 and R60,000. The client believes one combined excess applies since it was one accident. What does the insurer pay in total after applying excesses correctly?",o:["The insurer pays R60,000 total — each vehicle generates a separate claim with its own full R20,000 excess: (R40,000 − R20,000) + (R60,000 − R20,000) = R20,000 + R40,000 = R60,000","The insurer pays R80,000 total — one combined R20,000 excess applies to the single accident event regardless of how many vehicles were damaged, per the principle of a single event giving rise to one claim","The insurer pays R82,500 total — fleet policies apply a reduced 75% excess per vehicle when more than one vehicle is damaged in the same accident event","The insurer pays R100,000 total — voluntary excesses do not apply to multi-vehicle accidents because the voluntary excess is designed for single-vehicle incidents only"],a:0,e:"Under most fleet policies, each vehicle's damage is a SEPARATE CLAIM even from the same accident. The full combined excess (R15,000 + R5,000 = R20,000) applies separately to each vehicle: (R40,000 − R20,000) + (R60,000 − R20,000) = R20,000 + R40,000 = R60,000 total payout. Clients are frequently surprised by this — it is a commonly tested practical scenario."},
{t:"Investments & Financial Planning",lv:"AN",q:"A 50-year-old client draws 8% per year from a living annuity. Market returns have been below the drawdown rate for 3 consecutive years, eroding capital. The adviser recommends reducing to 5% drawdown, which would cut monthly income by 38%. The client refuses, citing lifestyle obligations. What is the adviser's correct ongoing professional obligation?",o:["Accept her decision after a single documented warning and take no further action — the adviser's professional obligation is fully discharged once the client has been warned and has chosen to proceed against advice","Refuse to continue managing the portfolio unless she agrees to reduce the drawdown rate — advisers have a fiduciary duty to prevent foreseeable client harm and cannot implement instructions that they have objectively assessed will lead to fund depletion within a foreseeable timeframe, regardless of the client's short-term income preferencesn rate — advisers are not obliged to implement instructions they believe will cause client harm","Reduce the drawdown rate unilaterally to 5% without the client's consent to protect her long-term capital sustainability — the adviser's fiduciary duty to act in the client's long-term best interest overrides short-term income preferences where the adviser can objectively demonstrate that the current withdrawal rate will lead to fund depletion within a determinable timeframe adviser's fiduciary duty to protect clients from financial harm overrides her stated preference","Respect her autonomy but ensure the ongoing sustainability risk is clearly documented at every scheduled review, actively monitor the fund's trajectory, revisit the conversation with updated projections, and document each interaction — living annuity sustainability requires ongoing advice, not a single warning"],a:3,e:"The client retains autonomy but living annuity sustainability is an ONGOING suitability concern requiring continuous monitoring and repeated engagement — not a once-off warning. The adviser must document each review interaction and the updated sustainability trajectory, and continue to present the evidence as the situation evolves."},
{t:"POPIA & Data Protection",lv:"AN",q:"An FSP's marketing team builds a predictive model using existing client data (purchase history, age, income bracket) to identify clients likely to need life insurance and proactively contacts them. No new data is collected — only data already lawfully held for service purposes is analysed. Is this POPIA-compliant?",o:["Yes — using data already lawfully held for any internal analytical purpose is permitted under POPIA because no new collection takes place, and the responsible party retains broad discretion over how existing data is used internally","Yes — POPIA's further processing limitation only applies when personal information is disclosed to or processed by a third-party organisation outside the responsible party. Internal analytical activities using data already held within the FSP, including building predictive models, constitute internal operational use that is implicitly within the original collection purpose under POPIA's operational necessity provisionson is shared with third parties, not when the responsible party analyses its own existing data","Not necessarily — repurposing service-collected data for predictive sales targeting may constitute further processing beyond the original disclosed purpose. POPIA's further processing limitation applies to HOW existing data is used, not just to new collection — fresh consent or a clear lawful basis is required","Yes — life insurance planning is a universal financial planning need applicable to virtually all adult clients, meaning predictive targeting for this product category is reasonably within the scope of the FSP's original data collection purpose, which broadly encompasses enabling the FSP to match clients with relevant financial products and services throughout the client relationship need, meaning predictive targeting for this product category is impliedly within the original data collection purpose"],a:2,e:"POPIA's further processing limitation is not only about collecting new data — using existing data for a materially different purpose (predictive sales targeting via profiling) can itself constitute further processing requiring justification. FSPs cannot assume broad licence to repurpose service data for commercial analytics without considering the original disclosed purpose."},
{t:"Complaints Handling",lv:"AN",q:"An FSP's complaints register shows 40 complaints over 2 years, all resolved with the FSP's preferred outcome in 38 of 40 cases, and zero escalated to the FAIS Ombud. The compliance officer presents this as proof of an excellent complaints process. What concern does this data pattern most directly raise?",o:["None — a 95% FSP-favourable resolution rate combined with zero Ombud escalations proves the internal process is functioning effectively and that the FSP is resolving complaints fairly before they need external intervention","The data confirms excellent complaint quality control — the 95% FSP-favourable resolution rate demonstrates the FSP is successfully distinguishing between valid complaints that warrant client redress and unfounded complaints that arise from client misunderstanding or unmet expectations, which is precisely the outcome an effective internal complaints process should achievelaints should result in client-favourable outcomes, and a 95% FSP-favourable rate shows robust internal validation is occurring","A near-uniform FSP-favourable outcome pattern with zero independent escalation may indicate clients are not being properly informed of their Ombud rights, or the process is structured to discourage genuine independent review — regulators expect some escalations and a more balanced resolution pattern in a healthy complaints environment","The compliance officer should cross-reference the complaint resolution outcomes with independent client satisfaction scores and post-resolution surveys before drawing conclusions, because resolution outcome distribution alone cannot determine process quality without reference to whether clients who received FSP-favourable outcomes were genuinely satisfied or felt they had been treated fairly by the process verify the 38 FSP-favourable resolutions were also satisfactory to the clients before drawing any conclusions about process quality"],a:2,e:"A near-uniform FSP-favourable pattern with zero Ombud escalation is a red flag, not a success metric. It often indicates clients are not aware of their right to escalate, or the process is designed to discourage it. A healthy complaints system would show some escalations and a more balanced distribution of outcomes."},
{t:"NCA & Consumer Protection",lv:"AN",q:"A financial adviser recommends a client take a personal loan to fund a lump-sum unit trust investment, projecting the investment return will exceed the loan interest rate. The adviser does not independently assess whether the client can service the loan if investment returns disappoint. The investment underperforms and the client cannot service the loan. Which regulatory failures occurred?",o:["Only an NCA failure — the credit provider was solely responsible for the affordability assessment and the adviser's role was limited to the investment recommendation which was independently suitable","A FAIS suitability failure — the adviser recommended a leveraged strategy without independently verifying the client's ability to service the debt if returns did not materialise as projected. Leveraged investment suitability requires assessing debt serviceability independent of investment performance assumptions","Both a FAIS failure and an NCA reckless credit finding — the adviser breached suitability by not assessing independent debt serviceability, and the credit provider may have also failed its own affordability assessment under the NCA","Only a FAIS failure — NCA obligations rest entirely with the credit provider and the adviser bears no obligation to assess credit affordability regardless of whether they recommended the strategy that required the loan"],a:2,e:"This involves compounding, not alternative, failures. The adviser breached FAIS suitability by recommending leveraged investment without assessing independent debt serviceability — because returns are never guaranteed. If the credit provider also failed affordability assessment, an NCA reckless credit issue exists in parallel. Both obligations are engaged."},
{t:"Regulatory Compliance",lv:"AN",q:"An FSP's KI signs off on the annual compliance report prepared by an external consultancy. A material misrepresentation is later discovered — a section incorrectly describes the FSP's representative complement. The KI reviewed the report before signing but did not catch the error. What is the KI's regulatory position?",o:["The KI is exposed — signing a regulatory submission is a personal confirmation of its accuracy. Outsourcing preparation does not transfer the obligation to review and verify before signing. A material misrepresentation the KI should have caught upon careful review creates personal accountability alongside any contractual claim against the consultancy","Fully protected — the external consultancy bears all responsibility for errors in content they prepared, and the KI's review and signature simply confirms she received and reviewed the document, not that every detail is accurate","Protected if the external consultancy holds professional indemnity insurance covering client errors in regulatory report preparation, because the PI policy creates a direct indemnity mechanism through which the KI can recover any regulatory consequences flowing from the consultancy's content errors, effectively transferring the financial risk of the error to the insured consultancyicy covers client errors and the KI can claim under it to resolve the regulatory consequence","Protected because the FSCA's regulatory guidance on compliance report preparation acknowledges that KIs cannot be expected to independently verify every data point in complex regulatory submissions and routinely accepts reports where the KI has relied on specialist advisers to prepare content, provided the adviser relationship is properly documented and the KI reviewed the high-level structure and conclusionspoint in complex reports, and assigns responsibility for factual accuracy to the preparer regardless of who signs"],a:0,e:"Signing a regulatory submission is a KI's personal confirmation of accuracy, not a formality. Outsourcing PREPARATION does not transfer the KI's obligation to review and verify content before signing. A material misrepresentation the KI should reasonably have caught creates KI accountability independent of any claim against the consultancy."},
{t:"Ethics & Professional Conduct",lv:"AN",q:"A representative discovers her FSP's onboarding process uses a pre-ticked marketing consent box that clients must actively un-tick to opt out. She raises this with the compliance officer who dismisses it as industry standard practice. What is the most accurate regulatory characterisation of this practice?",o:["Acceptable — pre-ticked opt-out consent is widely used in financial services and has been implicitly accepted by the FSCA through its consistent failure to take enforcement action against this practice across the industry","Likely non-compliant — POPIA generally requires consent to be a clear, affirmative act. Pre-ticked opt-out mechanisms do not constitute valid freely-given consent because they do not demonstrate informed, active agreement. Industry prevalence does not establish regulatory compliance","Acceptable because the FAIS Act, not POPIA, governs onboarding practices for financial services providers, and the FAIS Act does not specifically prohibit pre-ticked consent boxes for marketing communications","Acceptable provided the opt-out option is clearly visible and the client is given a reasonable time period to un-tick it before the onboarding process is finalised"],a:1,e:"POPIA requires consent to be a clear, affirmative act — opt-out mechanisms (pre-ticked boxes) are widely considered inadequate because clients may not actively notice or engage with them. Industry prevalence is not a compliance defence. The representative's instinct to flag this is professionally sound."},
{t:"FAIS Role Players",lv:"AN",q:"A KI at FSP A also serves as KI at FSP B on a part-time basis — a permitted arrangement under FAIS. Both FSPs simultaneously face urgent compliance crises on the same day requiring intensive KI attention. The KI prioritises FSP A and FSP B's issue goes unaddressed for a week. What does this scenario most accurately illustrate?",o:["While not prohibited, multiple KI roles create a genuine capacity risk — the KI's statutory obligation to manage and oversee ALL services at EACH FSP requires genuine ongoing capacity. A KI must honestly assess whether she can discharge full oversight at multiple FSPs simultaneously, and the FSCA may scrutinise capacity adequacy if failures occur during a conflict","Nothing of regulatory concern — FAIS expressly permits multiple KI appointments and any resulting conflict in attention is an inherent, accepted feature of the arrangement that the FSCA has implicitly sanctioned by permitting it","This only creates a regulatory issue if the KI was formally and explicitly notified of FSP B's problem through the FSP's official escalation channels and took no action, because informal or incidental knowledge of a subsidiary's compliance issue does not trigger the KI's personal oversight obligation absent formal notification through the compliance reporting structureroblem and consciously chose not to act — incidental knowledge gaps in a multi-FSP arrangement are an accepted operational reality","The FSCA requires all multi-FSP KI arrangements to include formally documented protocols addressing how the KI will manage simultaneous capacity conflicts across FSPs, and absent such documentation the arrangement is treated as non-compliant regardless of the KI's actual availability, making the arrangement invalid from the outset and creating regulatory exposure at both FSPsority conflicts — absent such a protocol the arrangement is invalid regardless of the KI's actual capacity"],a:0,e:"FAIS does not prohibit multiple KI appointments but this scenario illustrates the practical capacity risk: the KI's statutory obligation at each FSP requires genuine capacity. If simultaneous crises reveal that adequate capacity never existed, this retrospectively questions whether the oversight obligation was ever truly being discharged at both FSPs simultaneously."},
{t:"FICA & AML",lv:"AN",q:"An FSP's automated monitoring system flags a client's R45,000 deposit as unusual based on that client's historical pattern — but the amount is below the R49,999.99 CTR threshold. The compliance officer investigates and receives a verifiable, documented explanation: the client recently received a legitimate inheritance. What is the correct compliance response?",o:["File an STR regardless — any system-generated alert must be reported to the FIC within 15 days (excluding weekends and public holidays), and the compliance officer cannot override an automated detection flag based on client-provided explanation","File a CTR — the transaction exceeds R44,999 and therefore falls within a mandatory cash reporting category regardless of the STR assessment","The compliance officer must escalate to senior management before deciding — no individual compliance officer has authority to decide not to file an STR once a system alert has been generated","No STR is required if the explanation is verified as plausible and legitimate — the compliance officer should document the investigation, the explanation obtained, the verification steps taken, and the reason no STR was filed. Continued monitoring should be noted"],a:3,e:"Not every system alert requires an STR. Where a plausible, verifiable explanation exists, the compliance officer may conclude that no suspicious activity occurred — but must document the investigation, the evidence obtained, and the decision not to file. The decision-making process itself must be clearly recorded."},
{t:"TCF",lv:"AN",q:"An FSP redesigns disclosure documents based on user-testing showing 40% better comprehension when the document is read. However, actual read-through rates drop because the redesigned version is 3 times longer and clients skim or ignore it. Under TCF Outcome 3, how should this evidence be interpreted?",o:["The redesign is a clear TCF improvement — comprehension scores are the correct primary metric for disclosure effectiveness under Outcome 3, and a 40% improvement is a strong result regardless of whether all clients choose to engage with the document","The redesign should be abandoned entirely — under TCF Outcome 3, a reduction in actual document read-through rates is a direct indicator of reduced client information receipt, and no comprehension score improvement in controlled testing can offset a demonstrated real-world reduction in the proportion of clients who actually engage with the required disclosuresies a disclosure document from TCF compliance regardless of comprehension score improvements","There is a genuine tension — TCF Outcome 3 requires clients to ACTUALLY receive and understand clear information in practice, not just achieve higher comprehension scores in controlled testing. A document that is technically clearer but practically less likely to be read may not improve real-world client understanding and may require further redesign","The read-through rate reduction is irrelevant — clients bear responsibility for reading documents provided to them, and the FSP's TCF obligation is fully discharged upon provision of a comprehensible document regardless of whether clients actually read it"],a:2,e:"TCF Outcome 3 requires clear communication that clients actually receive and understand in practice — not just that passes controlled comprehension tests. A document that achieves better comprehension when read but is so long that clients increasingly skip it may not improve real-world outcomes, creating a genuine design tension requiring further iteration."},
{t:"General Code of Conduct",lv:"AN",q:"Thandeka recommends that her 67-year-old retired client surrender a 15-year endowment (2 years from maturity) and invest in a high-risk equity unit trust generating R14,000 commission. The ROA states the advice is suitable. A complaint is lodged 18 months later when the unit trust has lost 22%. What is the most likely Ombud finding?",o:["No breach — the ROA documents suitability and the client signed it. Investment losses are a known risk of equity products and do not retrospectively render advice unsuitable at the time it was given","No breach — the client's signed acknowledgement of market risk in the product application form constitutes informed consent that overrides any subsequent suitability concern about the specific product recommended","Breach of the replacement notification rule only — the underlying investment recommendation itself is not challengeable once the client has signed the ROA and taken delivery of the product","Breach of suitability — surrendering a near-maturity capital-guaranteed product in favour of a high-risk equity investment for a 67-year-old retiree is prima facie unsuitable regardless of the signed ROA, and the undisclosed commission difference compounds the COI breach"],a:3,e:"A signed ROA proves disclosure — it does not prove suitability. Recommending high-risk equity to a 67-year-old retiree who surrenders a near-maturity capital product is objectively unsuitable given the client's age, risk profile, and time horizon. The undisclosed commission difference is an independent COI breach."},
{t:"General Code of Conduct",lv:"AN",q:"A representative provides advice via a 45-minute WhatsApp voice note exchange with no written notes during the call. The advice is later disputed. The representative argues the retained voice notes themselves constitute adequate evidence. Is this position defensible under FAIS?",o:["No — FAIS requires written ROAs prepared during or immediately after a formal face-to-face meeting. Voice notes from any digital channel are inadmissible as evidence of advice regardless of their content or retention","Potentially defensible if the voice notes are retained, contain sufficient substantive detail about the needs analysis and recommendation rationale, AND a subsequent written ROA summarising the advice was still provided to the client — the medium of advice does not exempt the adviser from the underlying ROA documentation obligation","Yes — any retained communication medium containing financial recommendations, including voice notes, satisfies the FAIS documentation requirement because the Act regulates the CONTENT and RETENTION of advice records, not their physical format, and retained voice notes with sufficient substantive content are legally equivalent to written ROAs for all regulatory purposes","No — WhatsApp and other informal digital messaging platforms are not FSCA-approved channels for conducting FAIS advice interactions, making all advice rendered through these channels and all associated documentation records inadmissible in regulatory proceedings regardless of the content quality or completeness of the retained communication record"],a:1,e:"FAIS does not mandate a specific communication medium — advice can be given via any channel. However, the underlying obligation to document the needs analysis, recommendation, and rationale in a retrievable ROA still applies regardless of medium. Voice notes can serve as supporting evidence of what was discussed, but do not replace the ROA requirement."},
{t:"Scope & Definitions",lv:"AN",q:"A bank employee processing a routine account opening mentions: 'Our investment department has a product that might interest you — you should book a consultation.' No specific product details, analysis, or recommendations are given. Does this constitute advice under FAIS?",o:["Yes — mentioning any investment product to a bank client during a financial interaction constitutes advice because the bank is a licensed FSP, making all staff communications about products subject to FAIS requirements","Yes — but only if the client subsequently books the consultation, because the referral and consultation together constitute a single integrated advice interaction for FAIS purposes","No — a generic referral suggestion without any recommendation, analysis, or product-specific guidance does not satisfy the FAIS advice definition. The advice would occur when the investment specialist provides specific recommendations in the consultation, not when the teller makes a general referral","No — but only because the suggestion was made informally during a non-scheduled interaction. The same statement made in a scheduled meeting would constitute advice because the context creates a reasonable expectation of professional guidance"],a:2,e:"A generic referral suggesting the customer consult a specialist, without any recommendation, analysis, or product-specific guidance, falls short of the advice definition. FAIS advice requires a recommendation likely to influence a financial decision. The actual advice occurs when the specialist provides specific recommendations."},
{t:"Fit & Proper Requirements",lv:"AN",q:"A representative who has been sequestrated continues rendering financial services without disclosing her sequestration to the KI. Three months later the KI discovers this through an internal credit check. What are the immediate regulatory obligations?",o:["Note the sequestration in the representative's personnel file, remind her of her disclosure obligations, and monitor her financial situation going forward — sequestration alone does not automatically affect F&P status and no immediate FSCA notification is required","Immediately debar the representative — sequestration is an automatic disqualifying event that requires immediate debarment without the Notice → Representations → Decision process","Suspend the representative immediately and conduct a retrospective review of all advice given during the 3-month undisclosed period, notify the FSCA that the representative may not have met F&P requirements during this period, and assess whether formal debarment is appropriate","Allow the representative to continue pending the sequestration's rehabilitation, since the financial soundness F&P requirement only applies at the point of appointment, not on an ongoing basis during employment"],a:2,e:"Sequestration directly impacts the financial soundness F&P requirement. The KI must: immediately assess whether the representative still meets F&P requirements, suspend them from rendering services if they do not, notify the FSCA, and conduct a retrospective review of advice given during the undisclosed period. Debarment may follow but requires the proper process."},
{t:"FAIS Ombud",lv:"AN",q:"A client files a FAIS Ombud complaint 8 months after the FSP's final response. She only discovered the advice was unsuitable when the investment failed to mature as expected. The FSP argues time-bar. The client argues her 6-month window should run from the date she discovered the harm, not the date of the FSP's response. Which argument is stronger?",o:["The FSP's argument — the 6-month period is an absolute statutory deadline that begins on the date of the FSP's final response regardless of when the client discovered the harm, and the Ombud has no discretion to accept a time-barred complaint","The FSP's argument prevails because the client had an independent obligation to actively monitor her investment's performance and engage with the regular statements sent by the FSP, and her failure to review those statements for 8 months constitutes contributory neglect that prevents the prescription period from running from the discovery date rather than the date of the FSP's formal responseinvestment and the failure to do so for 8 months constitutes unreasonable delay regardless of the harm discovery date","Both arguments carry substantial legal weight and the practical outcome would depend entirely on the specific FAIS Ombud assigned to the matter and their individual approach to prescription — some Ombud panels consistently apply strict date-of-response prescription while others consistently prefer date-of-discovery approaches, creating genuine uncertainty in outcome predictionecific Ombud assigned to the matter","The client's argument is more consistent with how the Ombud typically approaches prescription — the Ombud has discretion to assess when the client reasonably discovered or should have discovered the harm. Where harm only became apparent years after advice, prescription may run from the date of discovery, and the Ombud may condone late filing where good cause is shown"],a:3,e:"Prescription in financial services complaints considers when the client reasonably discovered the harm. The Ombud has discretion to condone late filing where good cause exists — including where harm only became apparent after the advice. The 6-month period is standard but not always an absolute bar."},
{t:"Investments & Financial Planning",lv:"AN",q:"A 35-year-old client with a 30-year investment horizon, high financial capacity, and moderate-high risk tolerance instructs her adviser to invest entirely in a money market fund 'because markets are uncertain right now.' The adviser believes this is clearly suboptimal for her circumstances. What should the adviser do?",o:["Document the client's instruction, clearly explain why a money market allocation is likely suboptimal given her 30-year horizon and risk profile, present the evidence on long-term equity vs money market real returns, record the discussion in the ROA, and execute the instruction if she confirms after being properly informed — respecting autonomy while discharging the suitability advisory obligation","Execute the instruction immediately without comment — the client exercises her right to direct her own financial affairs and the adviser's role is to implement client instructions, particularly for investment allocation decisions that fall within the client's own risk appetite and financial autonomy. Unsolicited professional commentary on client-directed instructions constitutes paternalistic interference inconsistent with respecting client autonomy make her own investment decisions and the adviser's role is to implement instructions, not to second-guess them","Refuse to execute the instruction — advisers are required under FAIS to implement only suitable recommendations and cannot execute instructions they assess as unsuitable regardless of client autonomy","Agree to a 6-month temporary money market allocation as a practical compromise solution that addresses the client's immediate market concern while preserving the long-term equity strategy, then transition automatically to an appropriate growth portfolio at the 6-month review without requiring fresh instructions — this approach balances client concerns with sound financial planning principlesn appropriate equity allocation at the next review without requiring fresh instructions"],a:0,e:"The adviser must inform the client clearly and document the advice before executing a client-directed instruction the adviser believes is suboptimal. Respecting autonomy and discharging the advisory obligation are not mutually exclusive — the adviser provides the professional opinion, records it, and executes the client's informed decision. Neither silent execution nor refusal is appropriate."},
{t:"Regulatory Compliance",lv:"AN",q:"A KI on approved sick leave formally delegates oversight to a senior compliance manager, documents the arrangement, briefs thoroughly, and sets up weekly check-ins. During week 4, a representative gives unsuitable advice the delegate missed despite the oversight structure. The KI returns and is assessed by the FSCA. What most accurately describes her regulatory position?",o:["She bears full personal liability regardless of the delegation quality — KI accountability is absolute and cannot be mitigated by any delegation arrangement, however well-documented, because the KI is always personally liable for ALL oversight outcomes","She bears no personal liability whatsoever — properly documented medical leave delegation with a qualified substitute, thorough briefing, and structured check-ins constitutes an adequate continuity arrangement that fully satisfies the KI's statutory oversight obligation during the absence period, and any failures that occur despite adequate delegation arrangements are attributed to the delegate not the absent KI is a complete defence to all oversight failures that occur during the period, equivalent to force majeure","Her position is more defensible than if no structure existed — where a KI takes all reasonable steps (documented delegation, qualified delegate, briefing, monitoring structure) the FSCA's assessment focuses on whether the KI's OWN conduct was reasonable, not whether every failure was prevented. A properly structured delegation where the failure stems from the delegate's individual lapse despite adequate KI design may be distinguishable from inadequate oversight","She bears personal liability only if medical evidence demonstrates she was objectively able to return to work before the unsuitable advice was given but chose to extend her leave beyond medical necessity, because self-extended leave taken for non-medical reasons cannot qualify for the same treatment as genuine medical incapacity and does not provide the same mitigation for delegation failures occurring during the extended perioddvice was given and chose not to in order to extend her leave"],a:2,e:"This tests the outer boundary of reasonable delegation. Where all reasonable steps are taken, the FSCA assesses whether the KI's OWN conduct was reasonable — not whether every failure was prevented. A properly structured delegation where the failure stems from the delegate's individual lapse, despite adequate design and monitoring, is different from inadequate oversight. This represents the boundary between defensible and indefensible delegation."},
{t:"Insurance Principles",lv:"AN",q:"After settling a client's vehicle theft claim for R200,000, an insurer discovers the vehicle was stolen by the client's own employee acting criminally. The insurer wants to pursue the employee. Which principle applies and what is the correct outcome?",o:["Contribution — multiple parties share liability for the loss, meaning the insurer can recover 50% from the employee and retains 50% as its share of the total loss caused by the criminal act","Subrogation — having settled the claim, the insurer steps into the client's shoes and can pursue the employee for R200,000 recovery. This prevents the wrongdoer escaping liability because the victim was insured, while ensuring the insured client does not double-recover","Proximate cause — because the employee was the proximate cause of the loss, the insurer is discharged from all liability once this is established, and can claw back the R200,000 already paid to the client","Utmost good faith — the client had an obligation to disclose the employee's criminal background, and failure to do so voids the policy retrospectively, giving the insurer grounds to recover the R200,000 from the client rather than the employee"],a:1,e:"Subrogation allows the insurer, after settling a claim, to pursue the responsible third party in the insured's name. This prevents wrongdoers escaping liability simply because the victim was insured, while preventing the client from double-recovering. The insurer can pursue the employee for the R200,000 paid out."},
{t:"TCF",lv:"AN",q:"An FSP discovers a fee calculation error has resulted in 6,000 clients being overcharged by an average of R340 each over 3 years. The error was unintentional and systematic. Legal counsel advises that since no clients have formally complained, the FSP has no immediate legal obligation to proactively contact them. Is this legal analysis consistent with the FSP's TCF obligations?",o:["Yes — TCF obligations are co-extensive with legal obligations. If no legal duty to proactively contact clients exists, no TCF obligation to do so exists either, and the FSP should simply correct the error going forward","Yes — TCF obligations regarding remediation of identified errors are triggered by the receipt of client complaints or formal regulatory notifications about specific issues, and until such triggers are received the FSP's TCF obligation is fully satisfied by correcting the identified error going forward and disclosing it clearly in the next scheduled annual report to affected clientsut the overcharge, the FSP's TCF obligation is limited to correcting the error going forward and disclosing it in the next annual report","No — but only if the total overcharged amount across all affected clients exceeds R1,000,000 in aggregate, which triggers the FSCA's mandatory systemic remediation notification requirements. Below this threshold, TCF obligations can be satisfied through going-forward correction, prospective disclosure in the next annual communication to clients, and individual remediation only for clients who proactively request iteshold, going-forward correction satisfies TCF requirements without retroactive client contact","No — TCF imposes obligations that go beyond minimum legal requirements. A systematic overcharge requires proactive identification and remediation of ALL affected clients regardless of whether they have complained — TCF fair treatment cannot depend on whether clients are assertive enough to discover and report the overcharge themselves"],a:3,e:"TCF is an ONGOING obligation that extends beyond minimum legal compliance. A systematic overcharge must be proactively identified and remediated for ALL affected clients — not just those who complain. Waiting for complaints means only assertive clients are treated fairly, which is exactly the outcome TCF is designed to prevent."}
]);

const QB_HARD_RE1 = Object.freeze([
{t:"KI Duties & Responsibilities",lv:"AN",q:"A KI implements a documented compliance monitoring framework with automated alerts. The framework correctly flags a suitability pattern at month 3. The alert sits unactioned for 6 weeks due to a staffing gap the KI was aware of but had not yet addressed. The FSCA investigates. What most accurately describes the KI's accountability?",o:["Fully protected — the detection framework worked correctly and the resulting operational failure during a known staffing gap is an HR matter outside the KI's personal regulatory accountability","Still exposed — the KI's oversight obligation extends to ensuring the compliance machinery has adequate operational capacity to ACT on what it detects. A known staffing gap producing a 6-week unactioned critical alert indicates insufficient oversight of the compliance function's operational capacity, not just its technical design","Fully protected — staffing decisions including compliance function resourcing are a board-level governance matter that falls outside the KI's personal regulatory accountability scope. The KI's obligation is to design sound compliance monitoring frameworks, not to guarantee their operational capacity against HR gaps that occur through normal staff turnover and are managed through standard HR processespersonal accountability for HR gaps that affect compliance function capacity","Exposed only if the KI can be shown to have personally seen and read the specific unactioned alert during the 6-week period and took no action despite direct personal knowledge — absent proof of actual personal knowledge of the specific unactioned alert, the KI cannot be held accountable for an operational staffing gap that prevented the compliance machinery from functioning as designeduring the 6-week period and taken no action despite direct knowledge"],a:1,e:"The KI's oversight obligation extends beyond designing a sound framework to ensuring it has operational capacity to function. A known staffing gap producing a 6-week critical unactioned alert indicates insufficient oversight of compliance function capacity — a distinct failure from framework design."},
{t:"KI Duties & Responsibilities",lv:"AN",q:"A KI personally reviews and signs off on 200 client files in a remediation exercise within a tight regulatory deadline, averaging 90 seconds per file. A subsequent audit finds 12 inadequately assessed files. Is rapid review at scale, by itself, evidence of inadequate KI oversight?",o:["Yes automatically — any file review that averages under 5 minutes per file is presumptively inadequate as a matter of regulatory standard, because the FSCA's guidance on KI oversight establishes an implicit minimum review depth that cannot be achieved in under 5 minutes for standard client files and is therefore insufficient regardless of the KI's seniority or the nature of the remediation tasknadequate as a matter of regulatory standard, regardless of the KI's expertise or the nature of the remediation task","No — any review the KI personally conducts is automatically adequate by virtue of the KI's seniority and professional judgment, and a 6% error rate within an acceptable margin for volume processing","Not automatically — adequacy depends on whether the methodology appropriately triaged files by risk level, giving proportionately more time to complex or flagged files. However, a 6% error rate under time pressure raises a legitimate question about whether the review approach adequately balanced speed against quality assurance","Yes — remediation exercises require independent external auditor involvement and cannot be considered adequate when conducted exclusively by the KI, because the KI has an inherent conflict of interest in reviewing files that fall within their own oversight responsibility, making self-conducted remediation reviews insufficiently independent to satisfy the FSCA's requirements for remediation assuranceever adequate when conducted by the KI, regardless of the KI's expertise or the time allocated per file"],a:2,e:"Rapid review is not inherently inadequate but a 6% error rate under time pressure raises a legitimate question about whether the triage methodology was sound. Adequate volume review requires proportionately more time for high-risk or complex files — uniform brief review across all files regardless of complexity is a methodological concern."},
{t:"Governance & COI Management",lv:"AN",q:"An FSP's board, fully compliant with King IV's apply-and-explain approach, properly discloses its decision not to establish a separate risk committee. Eighteen months later, a significant risk event occurs that the combined audit-risk committee arguably failed to address due to audit-season time pressures. Does the earlier King IV-compliant disclosure protect the board from scrutiny?",o:["Yes — proper apply-and-explain disclosure is a complete governance defence against any subsequent failure related to that structural choice, because the board discharged its transparency obligation at the time the decision was made","Yes — but only if the specific risk event that occurred was genuinely unforeseeable at the time the structural choice was made and the apply-and-explain documentation contains explicit disclosure of the range of risk scenarios the combined committee was designed to address, because proper risk scenario documentation at the time of structural disclosure is what differentiates sound governance from retrospective rationalisationoice was made and documented, since boards cannot be held accountable for unknown future risks that their chosen governance structure fails to detect","No — apply-and-explain addresses TRANSPARENCY of the structural choice, not whether that choice continues to be operationally adequate. If the chosen structure demonstrably failed when tested, the board has a governance obligation to reassess it going forward, regardless of how properly the original choice was disclosed","No — but only because FSPs above a certain asset threshold are required by FSCA Conduct Standards to maintain separate risk and audit committees regardless of how compellingly the apply-and-explain rationale for combining them is articulated, meaning the apply-and-explain flexibility simply does not extend to this particular structural choice for larger FSPs regardless of the quality of the disclosed reasoningsk committees regardless of apply-and-explain flexibility"],a:2,e:"Apply-and-explain transparency about a structural choice is not a permanent shield against that choice's consequences. If the structure demonstrably underperformed when tested, the board has a continuing obligation to reassess its adequacy — the governance obligation is ongoing, not discharged by a single well-documented disclosure."},
{t:"Governance & COI Management",lv:"AN",q:"A KI proposes a remuneration structure with a 70/30 fixed/variable split. The board rejects it as uncompetitive and insists on 40/60. The KI documents her objection. Eighteen months later, complaint rates rise significantly. Does the KI's documented objection fully discharge her governance obligation?",o:["Yes — documenting the objection fully discharges the KI's governance obligation. The board's decision and its consequences from that point forward are entirely the board's responsibility, and the KI's role is limited to implementing approved structures even where she disagrees","Yes — but only if the KI simultaneously notified the FSCA of her disagreement at the time the structure was approved, which creates an external record that removes any ongoing KI accountability for resulting outcomes","No — the KI should have resigned rather than implement a structure she assessed as creating TCF risk, since implementing a disagreed governance structure is itself a breach of the KI's personal ethical obligations","Documentation is necessary but not sufficient — the KI's obligation is ONGOING. Where her documented concern proves justified by rising complaints, she has a continuing obligation to bring the evidence back to the board, propose remediation, and actively monitor outcomes — not simply record her earlier position and take no further action"],a:3,e:"Documenting an objection is the FIRST step. Where the concern subsequently proves justified, the KI has a continuing governance obligation to bring emerging evidence back to the board, propose corrective action, and monitor — governance accountability is ongoing, not discharged by a single documented objection."},
{t:"Supervision of Representatives",lv:"AN",q:"A KI's risk framework correctly classifies a representative as low-risk based on 6 years of clean performance. In year 7, an undisclosed personal crisis leads to sudden unsuitable advice driven by commission desperation. The framework did not detect this. Does the failure indicate a framework design flaw?",o:["Yes — any monitoring framework that fails to detect a representative's misconduct within 12 months of its occurrence is, by definition, inadequately designed and must be comprehensively redesigned to address the identified gap","Yes — a well-designed supervision framework should require representatives to annually disclose material personal circumstances including significant financial changes and relationship status, because this information directly affects the psychological and financial pressures that drive conduct risk. The absence of such disclosure requirements in the framework design reflects a fundamental gap in the KI's approach to holistic representative risk assessmentfinancial circumstances and relationship status to enable early detection of personal crisis factors that affect professional conduct","Not necessarily a design flaw — risk-based frameworks detect observable patterns in available data. Undisclosed personal circumstances invisible to all metrics cannot be detected by data-driven monitoring. This highlights a LIMITATION that may justify supplementary relationship-based check-ins, but does not render the original framework design deficient","No — but only because the misconduct occurred in year 7 of a 5-year risk recertification cycle, meaning the framework's risk classification had not been refreshed for 2 years. A framework that requires risk reclassification every 5 years for experienced representatives with clean records is adequate for the period it covers, and a failure occurring in an uncovered post-certification gap does not retrospectively invalidate the framework design for the covered periodessed for design adequacy in relation to the first 5 years of a representative's performance history under standard regulatory expectations"],a:2,e:"A well-designed, evidence-based framework is not rendered inadequate by its inability to detect factors invisible to all available data. However, this scenario highlights an inherent limitation of purely metric-based monitoring, and may justify supplementary relationship-based engagement — this is a forward-looking enhancement, not evidence the original design was flawed."},
{t:"FSP Licensing & Financial Soundness",lv:"AN",q:"An FSP's capital adequacy calculation drops below the threshold due to a change in lease accounting standards that capitalises previously off-balance-sheet leases — despite zero actual change in the FSP's cash position, business operations, or real financial risk. How should the KI navigate the tension between technical compliance and substantive reality?",o:["Argue to the FSCA that the breach should be disregarded as a pure accounting classification issue with no real economic substance, and decline to submit formal notification on the basis that notification is only required for economically meaningful capital deficiencies","Restate the capital adequacy calculation using the previous accounting standard to avoid showing a breach in the current period, since the new standard does not accurately reflect economic reality for this type of lease structure","Delay notification until the FSCA's next scheduled inspection and present the accounting context at that time — since the underlying issue carries no economic risk, the standard notification timeline can be appropriately adjusted by the KI based on substance over form","Notify the FSCA of the technical breach as required by the current calculation rules, while providing clear, well-evidenced context explaining the accounting standard change and demonstrating the absence of real economic deterioration — allowing the FSCA to make the determination, not the KI unilaterally"],a:3,e:"Even where a KI believes a technical breach does not reflect genuine economic deterioration, the correct approach is transparent, immediate notification with full context — not unilateral non-disclosure based on the KI's own substance assessment. The FSCA, not the KI, is the appropriate body to determine whether the technical breach requires remedial action."},
{t:"TCF & Consumer Outcomes",lv:"AN",q:"An FSP's complaint resolution process is redesigned and average resolution time drops from 35 days to 8 days. Simultaneously, client satisfaction with resolution OUTCOMES (not speed) declines. Internal analysis suggests faster resolution is achieved by limiting investigation depth. How should the KI assess these competing signals?",o:["Celebrate the speed improvement as an unambiguous TCF Outcome 6 success — faster resolution directly reduces post-sale barriers for clients regardless of any secondary metrics that are harder to attribute directly to the process change","Treat speed and outcome satisfaction as entirely unrelated metrics driven by different factors — the speed improvement is a genuine Outcome 6 success and the satisfaction decline should be investigated separately as a potential product or advice issue unrelated to the complaints process redesign","Investigate the apparent trade-off seriously — speed (Outcome 6) achieved by sacrificing investigative thoroughness may undermine the substantive fairness of outcomes (linked to Outcome 4). A genuine TCF improvement cannot sacrifice outcome quality for process efficiency and the KI must rebalance the approach","Conclude no action is needed since the volume of complaints has not changed — only the speed of resolution and client satisfaction with outcomes, both of which are secondary metrics under TCF compared to complaint volume as the primary indicator"],a:2,e:"TCF outcomes can be in tension if implemented poorly. Speed achieved by sacrificing investigative thoroughness may undermine outcome fairness — this is not a genuine TCF improvement. The KI must ensure process efficiency gains do not come at the cost of the substantive fairness the process exists to deliver."},
{t:"FAIS Ombud & Complaints",lv:"AN",q:"An FSP receives 22 complaints in 6 months, all relating to the same adviser's retirement product recommendations. The KI resolves each complaint individually with small ex gratia payments and records each as resolved. The FSCA's thematic review issues an enforcement notice. Why is individual resolution treated as inadequate?",o:["Individual resolution without root cause analysis and systemic remediation is inadequate where a clear pattern exists. The KI failed to investigate whether all clients who received similar advice were affected — not just the 22 who complained. The pattern indicates a potential systemic suitability failure requiring proactive scope assessment across the full client base","It is not inadequate — individual resolution with ex gratia payments is the preferred complaints handling approach under FAIS because it resolves matters quickly without formal escalation, and 22 complaints in 6 months is within normal industry ranges for active advisers","Individual resolution is adequate provided all 22 complainants received compensation assessed on the individual merits of their specific complaint, the FSP documented the basis for each compensation decision, and the compensation amounts were reasonably proportionate to the demonstrated harm — the adequacy of individual resolution depends on the quality and consistency of the merit assessment, not on whether a systemic investigation was conductedn — the FSCA's enforcement action was triggered by the failure to disclose the pattern in the annual compliance report, not by the resolution methodology itself","The resolutions were individually inadequate because ex gratia payments made without a formal written finding of liability or unsuitability constitute an implied admission that is legally and regulatorily weaker than a documented merit-based resolution, meaning the FSP created 22 implied admissions without the offsetting benefit of documented findings that could protect against Ombud escalation of the same complaintsadmission of liability that should have been escalated to the FAIS Ombud rather than resolved internally"],a:0,e:"22 complaints from one adviser in 6 months is a pattern indicating potential systemic suitability failure. Individual resolution without investigating whether all affected clients were impacted — not just those who complained — is inadequate. The KI must investigate the full scope and consider proactive remediation of all potentially affected clients."},
{t:"Twin Peaks & Regulatory Framework",lv:"AN",q:"An FSP receives conflicting guidance from the PA and FSCA on the same operational matter. How should the KI navigate this conflict?",o:["Seek urgent written clarification from both regulators simultaneously, document all communications, apply the more conservative approach pending resolution, and if genuine conflict persists apply the more client-protective standard — the KI should not unilaterally choose one regulator's guidance over the other without seeking resolution","Follow the PA guidance — in the Twin Peaks model, the Prudential Authority has constitutional precedence over the FSCA because it operates within the SARB, which is a constitutionally independent institution whose directives override those of independent statutory bodies","Follow the FSCA guidance — for FSPs, the FSCA is the primary and exclusive conduct regulator, and any PA guidance that affects FSP operational matters is outside the PA's jurisdiction and can be disregarded without seeking clarification","Suspend the affected operational activity entirely until the conflict is formally resolved through a published joint guidance note from both regulators — operating under either conflicting guidance exposes the FSP to regulatory risk from the other"],a:0,e:"Conflicting Twin Peaks guidance requires urgent engagement with both regulators, full documentation, and application of the more conservative or client-protective approach pending resolution. Unilaterally choosing one regulator's guidance creates risk with the other — the resolution must be sought jointly, not decided unilaterally."},
{t:"The Compliance Officer & Compliance Function",lv:"AN",q:"A KI approves a compliance monitoring programme with documented statistical limitations — a known 60% detection probability per 12-month period. An instance of misconduct goes undetected for 14 months within the programme's statistical expectations. Is the KI exposed?",o:["Yes automatically — any undetected misconduct period exceeding 12 months is evidence of an inadequate monitoring programme regardless of statistical design documentation, because regulators expect 100% detection of all misconduct within one year","No exposure — once a compliance monitoring programme uses statistically valid random sampling methodology with documented parameters, the KI bears zero personal accountability for any undetected misconduct regardless of the duration of non-detection or the detection probability, because statistical programmes by definition cannot guarantee detection of every individual instance and the FSCA accepts this inherent limitatione KI bears zero accountability for any undetected misconduct regardless of the detection probability or the duration of non-detection","Yes — but only because the 14-month undetected period exceeded the programme's 12-month review cycle, which the FSCA treats as the maximum acceptable detection window for all compliance monitoring programmes. Any misconduct remaining undetected beyond a single annual review cycle is treated as evidence that the programme's detection probability was insufficient regardless of the statistical documentation of its design limitationsthe FSCA treats as the maximum acceptable detection window for all compliance monitoring programmes regardless of their statistical design parameters","Less exposed than if the limitation were undocumented — a KI who transparently approves a programme with documented statistical limitations, having made a reasoned judgment about resource allocation and risk tolerance, is in a different position than a KI who implements inadequate monitoring through ignorance. The defensibility rests on whether the original design decision was itself reasonable"],a:3,e:"A KI who transparently documents and reasonably approves a monitoring programme's statistical limitations is in a meaningfully different position than one who implements inadequate monitoring through carelessness. The FSCA assesses whether the original design decision was reasonable given available resources — not whether the programme achieved a theoretically perfect detection rate."},
{t:"The Compliance Officer & Compliance Function",lv:"AN",q:"An FSP outsources its entire compliance monitoring function to a specialist third party under a detailed SLA. The third party experiences an internal control failure causing a 3-month monitoring gap that the KI was unaware of because the third party did not proactively disclose it. What does this reveal about the limits of compliance outsourcing?",o:["That outsourcing the function does not outsource the KI's obligation to maintain oversight OF the outsourcing arrangement itself — including audit rights, regular reporting requirements, and proactive check-in mechanisms. A 3-month undetected gap suggests the KI's oversight of the third party was itself insufficient, independent of the third party's own control failure","That the KI bears no residual accountability whatsoever — complete outsourcing to a specialist third party with a detailed SLA transfers all compliance monitoring accountability away from the KI, making the third party solely responsible for any monitoring gaps and their consequences","That compliance monitoring should never be outsourced to any external party regardless of the third party's specialist qualifications or the SLA's quality, because the nature of the KI's personal regulatory accountability for compliance function outcomes makes effective external delivery of this function inherently incompatible with the oversight model contemplated by the FAIS Act's KI accountability frameworkrty's qualifications or the SLA quality, because the nature of the function makes effective external delivery inherently incompatible with KI accountability requirements","That the SLA was inadequately drafted and should have contained explicit provisions requiring the third-party firm to notify the FSP immediately of any internal control failures that affect monitoring coverage, with automatic service credits and remediation obligations triggered by notification delays — a properly structured compliance outsourcing SLA eliminates the risk of undisclosed monitoring gaps through contractual prevention rather than post-hoc detectionted the control failure from occurring at the third party, making this primarily a commercial contract management issue rather than a regulatory accountability concern"],a:0,e:"Outsourcing a function transfers operational execution, not the KI's accountability for ensuring the function is genuinely performed. The KI must maintain oversight OF the outsourcing arrangement through reporting requirements, audit rights, and proactive checks — a 3-month undetected gap indicates this meta-oversight was insufficient."},
{t:"FSP Licensing & Financial Soundness",lv:"AN",q:"An FSP's compliance manual runs 8 months behind regulatory updates after a Conduct Standard change, with staff trained on and following the superseded version. No specific client harm has been identified. How should the KI characterise this situation?",o:["Not a reportable issue — absent identified client harm or specific regulatory breach, an administrative documentation lag in the compliance manual is an internal quality matter requiring correction going forward but not retrospective assessment or FSCA notification","A minor administrative matter fully resolved by updating the compliance manual to the current standard going forward, noting the lag period in the next annual compliance report, and implementing a compliance calendar reminder system to prevent future update delays — historical documentation lags during dynamic regulatory periods are common, the FSCA does not require retrospective assessment absent identified harm, and contemporaneous correction is the appropriate and proportionate responsed — historical documentation lags are common in dynamic regulatory environments and the FSCA does not require retrospective assessment absent identified harm","Potentially reportable to the FSCA only if the outdated Conduct Standard provisions directly related to client-facing conduct requirements rather than internal operational or governance procedures, because client-facing regulatory gaps carry a higher risk of actual client harm and therefore attract the more stringent retrospective assessment and reporting obligations that do not apply to purely internal operational documentation lagsto client-facing conduct rather than internal operational requirements, since only client-facing compliance gaps carry retrospective assessment obligations","A genuine compliance gap requiring proactive remediation — operating for 8 months under outdated standards means the FSP cannot demonstrate current regulatory compliance during that period. The KI should retrospectively assess activities against the updated requirements, remediate identified shortfalls, and implement controls to prevent future compliance manual update lag"],a:3,e:"An 8-month gap between regulatory update and internal practice update is a genuine compliance gap even without identified harm — the absence of discovered harm does not mean no harm occurred. The KI should retrospectively assess activities during the gap period and remediate identified shortfalls, not assume safety due to absence of complaints."},
{t:"KI Duties & Responsibilities",lv:"AN",q:"A supervisor in a KI's framework applies risk-based sampling at day 45 of a new representative's 90-day mandatory 100% review period, without authorisation, due to workload pressure. The supervisor does not inform the KI. A serious suitability failure occurs at day 60. What does this reveal about the KI's oversight obligations?",o:["Only the supervisor's personal failure — the KI designed and properly communicated a compliant supervision framework that clearly specified the 100% review requirement for the first 90 days. The KI's responsibility is to create and communicate appropriate frameworks, not to independently verify every operational decision made by every supervisor in real time, and a single supervisor's unauthorised protocol deviation does not constitute a broader KI oversight failureliant framework and cannot be held accountable for a supervisor's individual decision to deviate from it without the KI's knowledge or consent","Only an HR and disciplinary failure attributable solely to the individual supervisor — the KI designed and clearly communicated a compliant framework specifying the 100% review requirement, the supervisor chose to deviate from it without authorisation, and the KI cannot be held personally accountable for an individual employee's deliberate decision to circumvent a clearly mandated procedure that the KI had properly established and communicatedplinary matter with no regulatory accountability implications for the KI's oversight obligations","The KI's framework is inadequately designed — a properly designed supervision framework would prevent supervisors from being able to apply different methodologies by building automated enforcement of the 100% review requirement into the system architecture","A two-layer failure — the supervisor's unauthorised deviation from mandated procedure AND the KI's oversight gap in not having mechanisms to detect when supervisors silently deviate from approved frameworks. KI oversight must extend to verifying SUPERVISORS are actually following the framework as designed, not just that the framework itself is sound on paper"],a:3,e:"This reveals a two-layer failure: the supervisor's unauthorised deviation AND the KI's gap in not detecting when supervisors silently deviate from approved procedures. Sound framework design is insufficient if the KI cannot verify it is actually being followed — adherence monitoring is as important as framework design."},
{t:"Governance & COI Management",lv:"AN",q:"An FSP's board applies King IV's Principle 14 on remuneration by disclosing only aggregate remuneration bands rather than individual executive remuneration, citing the FSP's small, privately-owned status and documenting the reasoning extensively. Is this approach King IV-compliant?",o:["No — apply-and-explain requires every King IV principle to be applied EXACTLY as described in the King IV Report, with no variation in implementation approach permitted regardless of organisation size, ownership structure, or commercial context","Yes — apply-and-explain permits organisations to apply principles in a manner appropriate to their size, structure, and context, provided the underlying governance outcome (transparency and accountability around remuneration) is achieved and the chosen approach is properly explained and documented","No — the King IV remuneration disclosure principle has been confirmed by the JSE as a minimum listing requirement that applies to all South African entities regardless of whether they are listed, removing flexibility for private companies","Yes — but only if the FSCA has pre-approved the alternative disclosure approach in writing through a no-action letter before the governance report is published"],a:1,e:"King IV's apply-and-explain framework is principles-based, not rules-based. It permits organisations to tailor application to their size, structure, and context — provided the underlying governance outcome is achieved and the approach is properly explained. A small private FSP using proportionate aggregate disclosure with documented reasoning can be compliant."},
{t:"TCF & Consumer Outcomes",lv:"AN",q:"A KI's TCF metrics show excellent Outcome 4 (suitability) scores across all file reviews. However, Outcome 5 data shows systematic underperformance of illustrated projections across the product range — not due to advice quality but due to optimistic actuarial assumptions in product design. What does this reveal about TCF governance?",o:["Nothing requiring KI action — since the advice itself is sound (Outcome 4), the product performance issue (Outcome 5) is an actuarial matter for the product development team and is entirely outside the scope of the KI's compliance oversight obligations","This confirms that both Outcome 4 monitoring (excellent scores) and Outcome 5 monitoring (detecting underperformance) are functioning correctly — the role of TCF governance is to detect issues, not to prevent them, and detection of the Outcome 5 pattern is itself evidence of TCF success","TCF outcomes are interconnected — excellent advice quality does not guarantee good client outcomes if products are systematically designed with optimistic projections. A mature KI's TCF oversight must extend beyond the advice PROCESS to engaging with whether PRODUCTS deliver genuinely realistic outcomes, requiring cross-functional dialogue with product development","The Outcome 4 monitoring results are being incorrectly interpreted — genuinely excellent suitability scores are impossible when products systematically underperform illustrations, meaning the file review methodology must be reassessed for accuracy"],a:2,e:"TCF is an integrated framework. Excellent advice quality does not by itself guarantee fair client outcomes if products are over-promising through design assumptions. A mature KI's TCF oversight extends beyond monitoring advice processes to engaging with whether products are realistically designed — this requires cross-functional engagement beyond traditional compliance monitoring."},
{t:"FAIS Ombud & Complaints",lv:"AN",q:"An FSP receives a partial Ombud determination — R1,200,000 awarded against an original R3,000,000 claim, with detailed reasoning for both the upheld and rejected portions. The board treats this as a satisfactory compromise and takes no further action on the upheld portion's underlying cause. What governance gap does this response reveal?",o:["None — a partial determination validates the FSP's position on the rejected portion. Since R1,200,000 is substantially less than the R3,000,000 claimed, the determination represents a favourable regulatory outcome that requires no further governance response beyond payment of the award","A significant gap — the UPHELD portion represents an independent finding that genuine unsuitable advice or misconduct occurred. Treating a partial determination as full vindication ignores the substantive regulatory signal in the upheld portion. The KI must treat the upheld portion with the same root-cause seriousness as a full award — investigating scope, assessing affected clients, and implementing remediation","No gap — the FAIS Ombud determination itself constitutes complete regulatory closure of the matter once the awarded amount is paid. The determination formally resolves the complaint and all underlying regulatory questions, discharging the FSP's obligations in relation to that complaint in their entirety. Any broader investigation beyond paying the award would constitute overreach beyond what the regulatory determination actually requiresof the matter, and the FSP's obligation ends upon payment of the R1,200,000 award regardless of the underlying cause of the upheld portion","A gap only in relation to the rejected R1,800,000 portion of the original claim — the FSP should consider appealing the determination to recover the full rejected amount through judicial review, as the Ombud's reasoning on the rejected portion may contain legal or factual errors that a High Court would correct on review, and the KI's primary governance obligation is to protect the FSP's commercial interests through available legal remediesal the rejected claims to recover a larger proportion of the complaint's costs from the complainant"],a:1,e:"A partial determination's upheld portion is an independent finding of genuine fault requiring the same root-cause investigation and scope assessment as a full award would trigger. Treating the partial nature of the determination as vindication is a governance failure that misses the substantive regulatory signal in the upheld findings."},
{t:"Twin Peaks & Regulatory Framework",lv:"AN",q:"A 2025 Conduct Standard directly conflicts with a specific 2003 General Code provision on a narrow technical point. Neither has been formally repealed. How should a KI resolve this conflict?",o:["Always follow the older instrument — the General Code has been tested by over 20 years of regulatory practice, Ombud determinations, and judicial interpretation, providing a level of legal certainty and predictability that a newly issued Conduct Standard cannot match until similarly tested. In cases of genuine conflict, the instrument with greater interpretive certainty should govern until the FSCA formally resolves the conflictory practice and judicial interpretation for over 20 years, providing certainty that the newer Conduct Standard cannot match until similarly tested","Generally follow the newer Conduct Standard as the more recent expression of regulatory intent, document the identified conflict clearly, and seek formal FSCA clarification if the conflict has material practical implications — proceeding on the newer standard while clarification is pending is the most defensible approach","Follow whichever instrument produces the more commercially favourable and operationally manageable outcome for the FSP's specific situation — where two regulatory instruments conflict without clear statutory precedence rules, FSPs retain reasonable discretion to select the applicable standard provided the choice is made in good faith and documented, and cannot be penalised for choosing one instrument when both were potentially applicabler the FSP's specific situation — where two instruments conflict without clear precedence rules, FSPs retain discretion to choose the applicable standard","Suspend compliance with both conflicting provisions until the FSCA formally resolves the conflict through a published ruling — operating under either instrument during an unresolved conflict exposes the FSP to enforcement action under the other"],a:1,e:"More recent regulatory instruments generally reflect current intent and would typically take precedence over conflicting older provisions. However, prudent practice requires documenting the conflict and seeking formal clarification where the conflict has material practical consequences — proceeding on the newer standard while awaiting clarification is the most defensible interim approach."},
{t:"Supervision of Representatives",lv:"AN",q:"A supervised representative's supervisor approves a complex structured product recommendation without reading the underlying needs analysis. The product proves unsuitable. The representative argues supervisor approval protects her. Is this argument valid?",o:["Yes — supervisor approval transfers responsibility for suitability from the representative to the supervisor. Once a supervisor approves advice, the representative has discharged her individual professional obligation regardless of the quality of the supervisor's review","No — supervisor approval is a procedural safeguard that cannot transform an unsuitable recommendation into a suitable one. The representative cannot use supervisor approval as a shield against a suitability failure. Both the representative and the supervisor may bear accountability — the representative for making the unsuitable recommendation and the supervisor for inadequate review","Yes — but only if the representative can demonstrate she provided all relevant client information to the supervisor in an organised and complete format that gave the supervisor a genuine opportunity to conduct an adequate review. Where the representative has discharged her information-sharing obligation fully and the supervisor's inadequate review resulted from the supervisor's own failure to engage with the provided materials, the supervisor bears sole accountability for the approval qualitylient information to the supervisor and the supervisor had reasonable opportunity to conduct an adequate review before approving","No — but only if the supervisor's approval was given in writing rather than verbally, because written supervisory approval constitutes a formal endorsement that creates a documented record of the supervisor's independent assessment and acceptance of responsibility for the recommendation quality, while verbal approval leaves an evidential gap that prevents the representative from demonstrating that approval was genuinely and independently givenally, since written approvals constitute formal endorsement that shifts full responsibility to the supervisor"],a:1,e:"Supervisor approval is a procedural safeguard, not a suitability guarantee. An unsuitable recommendation remains unsuitable regardless of supervisor sign-off. Both parties may bear accountability — the representative for the recommendation and the supervisor for inadequate review."},
{t:"KI Duties & Responsibilities",lv:"AN",q:"A KI discovers a whistleblower report alleges the FSP's Head of Compliance has been falsifying FSCA reports. The KI has worked closely with the Head of Compliance for 12 years and considers them a personal friend. What must the KI do?",o:["Refer immediately for independent investigation without involving the implicated Head of Compliance in the process. If substantiated, report to the FSCA. Personal relationships are legally and regulatorily irrelevant to this obligation","Investigate the allegation informally and confidentially before deciding whether to escalate — a 12-year professional relationship provides contextual knowledge that enables the KI to make a more informed preliminary assessment than an external investigator could","Discuss the allegation directly with the Head of Compliance first to give them an opportunity to explain — the whistleblower may be acting in bad faith and the KI's long relationship provides valuable context for assessing the allegation's credibility","Escalate only to the board and allow the board to decide whether external investigation and FSCA notification are required — the KI's role is to escalate governance concerns, not to independently initiate regulatory reporting on matters involving senior colleagues"],a:0,e:"Whistleblower reports of falsified regulatory submissions require immediate referral for INDEPENDENT investigation — the implicated person cannot be involved in assessing the allegation against them. Personal relationships are completely irrelevant. If substantiated, FSCA notification is mandatory."},
{t:"TCF & Consumer Outcomes",lv:"AN",q:"An FSP's data shows complaint resolution times improved dramatically but client satisfaction with OUTCOMES simultaneously declined. Internal analysis links the speed improvement to reduced investigation depth. Under TCF, how should the KI weigh these signals?",o:["The speed-outcome trade-off requires serious investigation — TCF Outcome 6 values reasonable timeframes, not merely the fastest possible resolution. If speed is achieved by sacrificing investigative thoroughness and outcome fairness, this represents a concerning tension between process metrics and substantive client treatment requiring rebalancing","Prioritise the speed improvement as the primary TCF metric — TCF Outcome 6 specifically concerns reducing post-sale barriers including excessive complaint resolution time, and a demonstrated improvement in this metric is an unambiguous TCF advance regardless of secondary satisfaction data","Accept both data points as valid but independent — speed metrics belong to Outcome 6 assessment while satisfaction belongs to a different TCF framework component, and it is methodologically incorrect to allow satisfaction data to qualify a demonstrated Outcome 6 improvement","Conclude the satisfaction data is unreliable because clients consistently rate faster processes higher in customer experience research, and the apparent satisfaction decline likely reflects survey design rather than genuine outcome quality concerns"],a:0,e:"TCF outcomes can be in tension. Speed achieved by sacrificing investigative thoroughness undermines the substantive fairness that Outcome 6 exists to enable. A genuine TCF improvement cannot trade outcome quality for process speed — the KI must rebalance the approach to achieve both reasonable timeframes AND fair substantive outcomes."},
{t:"The Compliance Officer & Compliance Function",lv:"AN",q:"A KI discovers that 34% of replacement recommendations in the past year came from one adviser, and 89% of those generated higher commission than the products replaced. The KI is presented with this data. What is the most appropriate regulatory response?",o:["The data presents strong evidence of a systematic COI pattern requiring investigation — whether those replacements were genuinely suitable for each client must be assessed individually, root cause analysis conducted, and if systemic unsuitability is found, all potentially affected clients must be proactively assessed for remediation regardless of whether they have complained","No action required — replacement is lawful under FAIS and commission is a permissible remuneration structure. Statistical concentration in one adviser's book and higher commission correlations are inherent features of a productive advisory relationship and carry no independent regulatory implication","Issue the adviser a written warning about the statistical pattern and increase monitoring frequency going forward — this proportionate response addresses the regulatory concern without prematurely prejudging the adviser's conduct based on correlational data alone","Immediately debar the adviser pending investigation — a 34% concentration combined with 89% commission-increasing replacements constitutes prima facie evidence of churning that requires immediate removal from client contact regardless of the procedural requirements for debarment"],a:0,e:"A 34% concentration combined with 89% commission-increasing replacements is a significant COI red flag requiring investigation of whether those replacements were genuinely suitable. The KI must assess all affected clients — not just those who complained — and consider systemic remediation if unsuitability is found."}
]);

// Free preview content for the Official Exam Success Guides — built from
// each guide's actual cover, contents, and opening section (not a
// separate PDF), so it always matches the real document and needs no
// extra Storage upload just for a teaser.
const STUDY_GUIDE_PREVIEW = {
  RE1: `
    <div style="text-align:center;margin-bottom:14px;padding:16px;background:linear-gradient(135deg,rgba(10,22,40,.95),rgba(10,22,40,.85));border-radius:10px;color:#fff">
      <div style="font-size:.65rem;letter-spacing:1.5px;color:rgba(255,255,255,.6);margin-bottom:6px">KEY INDIVIDUALS REGULATORY EXAMINATION</div>
      <div style="font-size:1.1rem;font-weight:800;color:var(--gold)">RE MASTERY HUB</div>
      <div style="font-size:.8rem;font-style:italic;margin:4px 0 10px">Exam Success Guide — RE1</div>
      <div class="fac gap12" style="justify-content:center">
        <div><div style="font-weight:800;color:var(--gold)">14</div><div style="font-size:.6rem;color:rgba(255,255,255,.6)">TOPICS</div></div>
        <div><div style="font-weight:800;color:var(--gold)">16</div><div style="font-size:.6rem;color:rgba(255,255,255,.6)">TASKS</div></div>
        <div><div style="font-weight:800;color:var(--gold)">144</div><div style="font-size:.6rem;color:rgba(255,255,255,.6)">QCS MAPPED</div></div>
      </div>
    </div>
    <p class="mb10">The RE1 examination is different from RE5 in one important way: it is not primarily about what advice you give — it is about how you <strong>govern</strong> the advice-giving process. As a Key Individual, you are accountable for everything that happens under your FSP's licence.</p>
    <p class="mb10">This guide is written for Key Individuals, compliance officers, senior managers, and anyone else who operates at the governance level of a financial services provider.</p>
    <div class="fw7 f-sm mb6" style="margin-top:14px">Table of Contents (excerpt)</div>
    <div class="c-xs lh-18" style="color:rgba(0,0,0,.6)">
      1. KI Duties & Responsibilities<br>2. Twin Peaks & Regulatory Framework<br>3. Governance & COI Management<br>4. KI Personal Accountability<br>5. Supervision of Representatives<br>6. FSP Licensing & Financial Soundness<br>7. TCF & Consumer Outcomes<br>8. FAIS Ombud & Complaints<br><em style="opacity:.7">...plus 6 more topics and a full 144-QC reference map</em>
    </div>`,
  RE5: `
    <div style="text-align:center;margin-bottom:14px;padding:16px;background:linear-gradient(135deg,rgba(10,22,40,.95),rgba(10,22,40,.85));border-radius:10px;color:#fff">
      <div style="font-size:.65rem;letter-spacing:1.5px;color:rgba(255,255,255,.6);margin-bottom:6px">REPRESENTATIVE REGULATORY EXAMINATION</div>
      <div style="font-size:1.1rem;font-weight:800;color:var(--gold)">RE MASTERY HUB</div>
      <div style="font-size:.8rem;font-style:italic;margin:4px 0 10px">Exam Success Guide — RE5</div>
      <div class="fac gap12" style="justify-content:center">
        <div><div style="font-weight:800;color:var(--gold)">17</div><div style="font-size:.6rem;color:rgba(255,255,255,.6)">TOPICS</div></div>
        <div><div style="font-weight:800;color:var(--gold)">8</div><div style="font-size:.6rem;color:rgba(255,255,255,.6)">TASKS</div></div>
        <div><div style="font-weight:800;color:var(--gold)">47</div><div style="font-size:.6rem;color:rgba(255,255,255,.6)">QCS MAPPED</div></div>
      </div>
    </div>
    <p class="mb10">This study guide was created for one purpose: to help you pass your RE5 examination. Every word, every table, every scenario, and every tip in this guide is deliberately chosen to reflect what the FSCA actually tests — in the format they actually test it.</p>
    <p class="mb10">Written in plain English. Built around real South African scenarios. Designed so you can understand the content, not just memorise it. Because understanding is what passes exams — not cramming.</p>
    <div class="fw7 f-sm mb6" style="margin-top:14px">Table of Contents (excerpt)</div>
    <div class="c-xs lh-18" style="color:rgba(0,0,0,.6)">
      FAIS Act — Core Definitions & Scope<br>FAIS Role Players<br>General Code of Conduct<br>Fit & Proper Requirements<br>Supervision & Debarment<br>FAIS Ombud<br>FICA & Anti-Money Laundering<br>TCF — Treating Customers Fairly<br><em style="opacity:.7">...plus 9 more topics and a full Moonstone QC reference map</em>
    </div>`,
};

const LIBRARY = Object.freeze([{topic:"FAIS Act — Core Definitions & Scope",exam:"RE5",quizTopic:"Scope & Definitions",content:`<p class="mb8"><strong>3-Element Test:</strong> FAIS applies when ALL three co-exist: (1) financial service rendered, (2) in relation to a financial product, (3) to a client.</p><p class="mb8"><strong>Advice = Recommendation:</strong> Any recommendation, guidance, proposal, or opinion about a financial product. Medium irrelevant.</p><p class="mb8"><strong>NOT Advice:</strong> Factual information without recommendation. Administrative queries. Scripted reading without deviation.</p><p class="mb8"><strong>Intermediary Service:</strong> Any act other than advice — collecting premiums, submitting applications, processing claims.</p><p class="mb8"><strong>Key Dates:</strong> Promulgated November 2002. Effective 30 September 2004.</p><p class="mb8"><strong>Two Objectives:</strong> Maintain TRUST in the industry + Maintain SYSTEMIC STABILITY.</p><div class="mt10"><span class="lib-key">3-element test</span><span class="lib-key">Advice = recommendation</span><span class="lib-key">Effective 30 Sep 2004</span><span class="lib-key">Trust + Systemic stability</span></div><div class="video-lesson-block" style="margin-top:14px;margin-bottom:4px;border-radius:12px;overflow:hidden;border:1.5px solid rgba(200,168,75,.3);background:rgba(10,22,40,.8)" data-video-id="J4k990ZdnUE">
  <div style="padding:12px 14px 0">
    <div style="display:flex;align-items:center;gap:8px;margin-bottom:10px">
      <span style="background:var(--gold);color:#0a1628;font-size:.65rem;font-weight:900;padding:3px 8px;border-radius:20px;flex-shrink:0">LESSON 1</span>
      <span style="font-weight:700;font-size:.85rem;color:#fff">FAIS Act — Core Definitions & Scope</span>
    </div>
  </div>
  <div style="position:relative;width:100%;padding-bottom:56.25%;background:#000">
    <iframe
      src="https://www.youtube-nocookie.com/embed/J4k990ZdnUE?rel=0&modestbranding=1&color=white"
      title="Lesson 1: FAIS Act — Core Definitions & Scope"
      frameborder="0"
      allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
      allowfullscreen
      loading="lazy"
      style="position:absolute;top:0;left:0;width:100%;height:100%;border:none">
    </iframe>
  </div>
  <div style="padding:10px 14px;display:flex;align-items:center;gap:6px">
    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#c8a84b" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
    <span style="font-size:.72rem;color:rgba(255,255,255,.45)">Watch the full lesson above · Questions follow in the quiz</span>
  </div>
</div>`},
{topic:"FAIS Role Players",exam:"RE5",quizTopic:"FAIS Role Players",content:`<p class="mb8"><strong>FSCA:</strong> Financial Sector Conduct Authority. Approves FSPs. Primary conduct regulator.</p><p class="mb8"><strong>FSP:</strong> Licensed entity (company, sole proprietor, trust, or partnership). Licensed in specific categories.</p><p class="mb8"><strong>Key Individual (KI):</strong> MUST be a natural person. Manages/oversees rendering of financial services. Establishes and maintains the compliance function.</p><p class="mb8"><strong>Representative:</strong> Authorised by FSP — registered with FSCA. FSP bears legal responsibility for representative conduct.</p><p class="mb8"><strong>Compliance Officer:</strong> Appointed if >1 KI OR >1 representative. Monitors and reports. Annual compliance report to FSCA.</p><p class="mb8"><strong>Critical Distinction:</strong> KI ESTABLISHES the compliance function. CO MONITORS it.</p><div class="mt10"><span class="lib-key">KI = natural person only</span><span class="lib-key">FSP liable for reps</span><span class="lib-key">CO when >1 KI or >1 rep</span><span class="lib-key">KI establishes; CO monitors</span></div><div class="video-lesson-block" style="margin-top:14px;margin-bottom:4px;border-radius:12px;overflow:hidden;border:1.5px solid rgba(200,168,75,.3);background:rgba(10,22,40,.8)" data-video-id="x_fu8xPGy_A">
  <div style="padding:12px 14px 0">
    <div style="display:flex;align-items:center;gap:8px;margin-bottom:10px">
      <span style="background:var(--gold);color:#0a1628;font-size:.65rem;font-weight:900;padding:3px 8px;border-radius:20px;flex-shrink:0">LESSON 2</span>
      <span style="font-weight:700;font-size:.85rem;color:#fff">FAIS Role Players</span>
    </div>
  </div>
  <div style="position:relative;width:100%;padding-bottom:56.25%;background:#000">
    <iframe
      src="https://www.youtube-nocookie.com/embed/x_fu8xPGy_A?rel=0&modestbranding=1&color=white"
      title="Lesson 2: FAIS Role Players"
      frameborder="0"
      allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
      allowfullscreen
      loading="lazy"
      style="position:absolute;top:0;left:0;width:100%;height:100%;border:none">
    </iframe>
  </div>
  <div style="padding:10px 14px;display:flex;align-items:center;gap:6px">
    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#c8a84b" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
    <span style="font-size:.72rem;color:rgba(255,255,255,.45)">Watch the full lesson above · Questions follow in the quiz</span>
  </div>
</div>`},
{topic:"General Code of Conduct",exam:"RE5",quizTopic:"General Code of Conduct",content:`<p class="mb8"><strong>4 General Duties:</strong> (1) Honestly & fairly; (2) Due skill, care, diligence; (3) Clients' interests; (4) Integrity of the financial services industry.</p><p class="mb8"><strong>Advice Process (sequence):</strong> Gather information → Needs analysis → Identify suitable product → Replacement disclosures.</p><p class="mb8"><strong>Record of Advice:</strong> Basis of advice + products considered + reasons for suitability.</p><p class="mb8"><strong>COI Threshold:</strong> R1,000 per year from same third party = immaterial. Above R1,000 = must disclose.</p><p class="mb8"><strong>Client Funds:</strong> Separate designated account within 1 BUSINESS DAY.</p><p class="mb8"><strong>LT Insurance Replacement:</strong> Notify original insurer no later than when new application is submitted.</p><p class="mb8"><strong>Prohibited:</strong> Blank/partially completed forms. Negative option marketing. Churning. Guaranteeing returns.</p><p class="mb8"><strong>COI Policy:</strong> Reviewed ANNUALLY. Published for public inspection.</p><div class="mt10"><span class="lib-key">R1,000 COI threshold</span><span class="lib-key">1 business day for client funds</span><span class="lib-key">No blank forms</span><span class="lib-key">Annual COI review</span></div><div class="video-lesson-block" style="margin-top:14px;border-radius:12px;overflow:hidden;border:1.5px solid rgba(200,168,75,.3);background:rgba(10,22,40,.8)" data-video-id="G5SROjQ-0fU">
  <div style="padding:12px 14px 0"><div style="display:flex;align-items:center;gap:8px;margin-bottom:10px">
    <span style="background:var(--gold);color:#0a1628;font-size:.65rem;font-weight:900;padding:3px 8px;border-radius:20px;flex-shrink:0">LESSON 3</span>
    <span style="font-weight:700;font-size:.85rem;color:#fff">General Code of Conduct</span>
  </div></div>
  <div style="position:relative;width:100%;padding-bottom:56.25%;background:#000">
    <iframe src="https://www.youtube-nocookie.com/embed/G5SROjQ-0fU?rel=0&modestbranding=1&color=white" title="Lesson 3: General Code of Conduct" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen loading="lazy" style="position:absolute;top:0;left:0;width:100%;height:100%;border:none"></iframe>
  </div>
  <div style="padding:10px 14px;display:flex;align-items:center;gap:6px">
    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#c8a84b" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
    <span style="font-size:.72rem;color:rgba(255,255,255,.45)">Watch the full lesson above · Questions follow in the quiz</span>
  </div>
</div>`},
{topic:"Fit & Proper Requirements",exam:"RE5",quizTopic:"Fit & Proper Requirements",content:`<p class="mb8"><strong>4 Pillars:</strong> (1) Honesty & integrity — ALL role players; (2) Competence — KIs, reps, COs; (3) Operational ability — FSP + KIs; (4) Financial soundness — FSP, reps, COs. Financial soundness does NOT apply to KIs.</p><p class="mb8"><strong>DOFA:</strong> Officially registered with FSCA for a specific scope. Drives ALL competency deadlines.</p><p class="mb8"><strong>Deadlines from DOFA:</strong> COB Training = 12 months. RE5 = 24 months. Recognised Qualification = 6 years.</p><p class="mb8"><strong>CPD Cycle:</strong> 1 June – 31 May. Evidence to FSP within 15 days of cycle end. Hours: 6h / 12h / 18h.</p><p class="mb8"><strong>CPD starts:</strong> All requirements met OR 6 years from DOFA — whichever FIRST.</p><p class="mb8"><strong>PST:</strong> No exemptions — ALL representatives must complete Product Specific Training.</p><div class="mt10"><span class="lib-key">F/S ≠ KIs</span><span class="lib-key">DOFA drives deadlines</span><span class="lib-key">CPD: 1 Jun–31 May</span><span class="lib-key">PST: no exemptions</span></div><div class="video-lesson-block" style="margin-top:14px;border-radius:12px;overflow:hidden;border:1.5px solid rgba(200,168,75,.3);background:rgba(10,22,40,.8)" data-video-id="-b00HIjBsdI">
  <div style="padding:12px 14px 0"><div style="display:flex;align-items:center;gap:8px;margin-bottom:10px">
    <span style="background:var(--gold);color:#0a1628;font-size:.65rem;font-weight:900;padding:3px 8px;border-radius:20px;flex-shrink:0">LESSON 4</span>
    <span style="font-weight:700;font-size:.85rem;color:#fff">Fit & Proper Requirements</span>
  </div></div>
  <div style="position:relative;width:100%;padding-bottom:56.25%;background:#000">
    <iframe src="https://www.youtube-nocookie.com/embed/-b00HIjBsdI?rel=0&modestbranding=1&color=white" title="Lesson 4: Fit & Proper Requirements" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen loading="lazy" style="position:absolute;top:0;left:0;width:100%;height:100%;border:none"></iframe>
  </div>
  <div style="padding:10px 14px;display:flex;align-items:center;gap:6px">
    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#c8a84b" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
    <span style="font-size:.72rem;color:rgba(255,255,255,.45)">Watch the full lesson above · Questions follow in the quiz</span>
  </div>
</div>`},
{topic:"Supervision & Debarment",exam:"RE5",quizTopic:"Supervision & Debarment",content:`<p class="mb8"><strong>Pre-condition:</strong> Written supervision agreement BEFORE any services are rendered.</p><p class="mb8"><strong>Supervised Rep Must:</strong> Disclose supervision status to every client before engagement.</p><p class="mb8"><strong>3 Grounds:</strong> (1) Fails F&P requirements; (2) Materially contravened FAIS; (3) Aided/abetted contravention.</p><p class="mb8"><strong>Process:</strong> Notice → Representations → Decision. FSP (not FSCA) executes.</p><p class="mb8"><strong>FSCA Reporting:</strong> Part I = 5 DAYS. Part II = 15 DAYS. Register updated within 15 DAYS.</p><p class="mb8"><strong>Reappointment:</strong> Dishonesty = minimum 12 months. Incompetency = as soon as F&P met — no wait.</p><div class="mt10"><span class="lib-key">Written agreement first</span><span class="lib-key">3 grounds</span><span class="lib-key">Part I = 5 days</span><span class="lib-key">Dishonesty = 12 months wait</span></div><div class="video-lesson-block" style="margin-top:14px;border-radius:12px;overflow:hidden;border:1.5px solid rgba(200,168,75,.3);background:rgba(10,22,40,.8)" data-video-id="j3RdSzMs3NI">
  <div style="padding:12px 14px 0"><div style="display:flex;align-items:center;gap:8px;margin-bottom:10px">
    <span style="background:var(--gold);color:#0a1628;font-size:.65rem;font-weight:900;padding:3px 8px;border-radius:20px;flex-shrink:0">LESSON 5</span>
    <span style="font-weight:700;font-size:.85rem;color:#fff">Supervision & Debarment</span>
  </div></div>
  <div style="position:relative;width:100%;padding-bottom:56.25%;background:#000">
    <iframe src="https://www.youtube-nocookie.com/embed/j3RdSzMs3NI?rel=0&modestbranding=1&color=white" title="Lesson 5: Supervision & Debarment" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen loading="lazy" style="position:absolute;top:0;left:0;width:100%;height:100%;border:none"></iframe>
  </div>
  <div style="padding:10px 14px;display:flex;align-items:center;gap:6px">
    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#c8a84b" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
    <span style="font-size:.72rem;color:rgba(255,255,255,.45)">Watch the full lesson above · Questions follow in the quiz</span>
  </div>
</div>`},
{topic:"FAIS Ombud",exam:"RE5",quizTopic:"FAIS Ombud",content:`<p class="mb8"><strong>Jurisdiction:</strong> Poor advice OR poor intermediary services. NOT pure product performance.</p><p class="mb8"><strong>Financial Cap:</strong> R3,500,000 per complaint (respondent's written consent required to exceed).</p><p class="mb8"><strong>Legal Status:</strong> Equivalent to HIGH COURT CIVIL JUDGEMENT.</p><p class="mb8"><strong>Time Limits:</strong> FSP has 6 WEEKS. Complainant has 6 MONTHS from FSP's final response. Appeal within 1 MONTH.</p><p class="mb8"><strong>Case Fee:</strong> None. The FAIS Ombud discontinued the case fee with effect from 10 September 2012. <em>Exam trap: many textbooks and question banks still state R1,000 — if an option says a fee is payable, it is wrong.</em> The service is free to complainants, and no fee is levied on respondents either. Older material quoting R1,000 is out of date.</p><p class="mb8"><strong>Process:</strong> Notify parties → Attempt conciliation/mediation → Formal investigation → Determination.</p><div class="mt10"><span class="lib-key">R3.5m cap</span><span class="lib-key">= High Court judgement</span><span class="lib-key">6 weeks → 6 months</span><span class="lib-key">No case fee since 2012</span></div><div class="video-lesson-block" style="margin-top:14px;border-radius:12px;overflow:hidden;border:1.5px solid rgba(200,168,75,.3);background:rgba(10,22,40,.8)" data-video-id="CaUpQb9KVAo">
  <div style="padding:12px 14px 0"><div style="display:flex;align-items:center;gap:8px;margin-bottom:10px">
    <span style="background:var(--gold);color:#0a1628;font-size:.65rem;font-weight:900;padding:3px 8px;border-radius:20px;flex-shrink:0">LESSON 6</span>
    <span style="font-weight:700;font-size:.85rem;color:#fff">FAIS Ombud</span>
  </div></div>
  <div style="position:relative;width:100%;padding-bottom:56.25%;background:#000">
    <iframe src="https://www.youtube-nocookie.com/embed/CaUpQb9KVAo?rel=0&modestbranding=1&color=white" title="Lesson 6: FAIS Ombud" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen loading="lazy" style="position:absolute;top:0;left:0;width:100%;height:100%;border:none"></iframe>
  </div>
  <div style="padding:10px 14px;display:flex;align-items:center;gap:6px">
    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#c8a84b" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
    <span style="font-size:.72rem;color:rgba(255,255,255,.45)">Watch the full lesson above · Questions follow in the quiz</span>
  </div>
</div>`},
{topic:"FICA & Anti-Money Laundering",exam:"RE5",quizTopic:"FICA & AML",content:`<p class="mb8"><strong>3 Stages:</strong> (1) PLACEMENT — illegal funds enter financial system; (2) LAYERING — complex transactions obscure trail; (3) INTEGRATION — funds re-enter economy as legitimate.</p><p class="mb8"><strong>Reporting:</strong> Cash >R49,999.99 → CTR to FIC. Suspicious transactions (any amount) → STR within 15 DAYS (excluding Saturdays, Sundays and public holidays).</p><p class="mb8"><strong>Tipping Off:</strong> Absolutely prohibited. Criminal offence.</p><p class="mb8"><strong>Records:</strong> 5 years from relationship termination or transaction.</p><p class="mb8"><strong>PEPs/ECDD:</strong> Senior management approval + source of wealth/funds + enhanced ongoing monitoring.</p><div class="mt10"><span class="lib-key">3 stages</span><span class="lib-key">>R49,999 = CTR</span><span class="lib-key">STR = 15 days (excl. weekends/public holidays); CTR = 3 business days</span><span class="lib-key">Tipping off = criminal offence</span></div><div class="video-lesson-block" style="margin-top:14px;border-radius:12px;overflow:hidden;border:1.5px solid rgba(200,168,75,.3);background:rgba(10,22,40,.8)" data-video-id="UICbnz4UU1w">
  <div style="padding:12px 14px 0"><div style="display:flex;align-items:center;gap:8px;margin-bottom:10px">
    <span style="background:var(--gold);color:#0a1628;font-size:.65rem;font-weight:900;padding:3px 8px;border-radius:20px;flex-shrink:0">LESSON 7</span>
    <span style="font-weight:700;font-size:.85rem;color:#fff">FICA & Anti-Money Laundering</span>
  </div></div>
  <div style="position:relative;width:100%;padding-bottom:56.25%;background:#000">
    <iframe src="https://www.youtube-nocookie.com/embed/UICbnz4UU1w?rel=0&modestbranding=1&color=white" title="Lesson 7: FICA & Anti-Money Laundering" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen loading="lazy" style="position:absolute;top:0;left:0;width:100%;height:100%;border:none"></iframe>
  </div>
  <div style="padding:10px 14px;display:flex;align-items:center;gap:6px">
    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#c8a84b" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
    <span style="font-size:.72rem;color:rgba(255,255,255,.45)">Watch the full lesson above · Questions follow in the quiz</span>
  </div>
</div>`},
{topic:"TCF — 6 Outcomes",exam:"RE5",quizTopic:"TCF",content:`<p class="mb8"><strong>TCF is OUTCOMES-BASED.</strong> The FSCA measures actual customer outcomes.</p><p class="mb4"><strong>Outcome 1:</strong> Fair treatment CENTRAL TO THE CULTURE.</p><p class="mb4"><strong>Outcome 2:</strong> Products DESIGNED for identified target markets.</p><p class="mb4"><strong>Outcome 3:</strong> CLEAR INFORMATION before customers buy.</p><p class="mb4"><strong>Outcome 4:</strong> ADVICE IS SUITABLE for individual circumstances.</p><p class="mb4"><strong>Outcome 5:</strong> Products PERFORM as led to expect.</p><p class="mb8"><strong>Outcome 6:</strong> No UNREASONABLE POST-SALE BARRIERS.</p><div class="mt10"><span class="lib-key">6 outcomes</span><span class="lib-key">O4 = suitability</span><span class="lib-key">O6 = post-sale barriers</span><span class="lib-key">Outcomes-based</span></div><div class="video-lesson-block" style="margin-top:14px;border-radius:12px;overflow:hidden;border:1.5px solid rgba(200,168,75,.3);background:rgba(10,22,40,.8)" data-video-id="vbqlVM38UeU">
  <div style="padding:12px 14px 0"><div style="display:flex;align-items:center;gap:8px;margin-bottom:10px">
    <span style="background:var(--gold);color:#0a1628;font-size:.65rem;font-weight:900;padding:3px 8px;border-radius:20px;flex-shrink:0">LESSON 8</span>
    <span style="font-weight:700;font-size:.85rem;color:#fff">TCF — 6 Outcomes</span>
  </div></div>
  <div style="position:relative;width:100%;padding-bottom:56.25%;background:#000">
    <iframe src="https://www.youtube-nocookie.com/embed/vbqlVM38UeU?rel=0&modestbranding=1&color=white" title="Lesson 8: TCF — 6 Outcomes" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen loading="lazy" style="position:absolute;top:0;left:0;width:100%;height:100%;border:none"></iframe>
  </div>
  <div style="padding:10px 14px;display:flex;align-items:center;gap:6px">
    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#c8a84b" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
    <span style="font-size:.72rem;color:rgba(255,255,255,.45)">Watch the full lesson above · Questions follow in the quiz</span>
  </div>
</div>`},
{topic:"Insurance Principles",exam:"RE5",quizTopic:"Insurance Principles",content:`<p class="mb8"><strong>Indemnity:</strong> Restores insured to pre-loss position — no profit. Applies to ST insurance. NOT life insurance (valued policies).</p><p class="mb8"><strong>Insurable Interest:</strong> Must exist at inception (LT) and at claim stage (ST). No interest = no valid contract.</p><p class="mb8"><strong>Utmost Good Faith (Uberrimae Fidei):</strong> Both parties disclose ALL material facts. Misrepresentation or non-disclosure = contract voidable.</p><p class="mb8"><strong>Subrogation:</strong> After paying claim, insurer steps into insured's shoes to recover from third party. Only applies under indemnity.</p><p class="mb8"><strong>Contribution:</strong> Where two or more insurers cover the same risk, each pays proportionate share. Prevents over-recovery.</p><p class="mb8"><strong>Proximate Cause:</strong> The dominant, most effective cause of loss. Determines whether the claim falls within the policy.</p><div class="mt10"><span class="lib-key">Indemnity = pre-loss position</span><span class="lib-key">Insurable interest required</span><span class="lib-key">Subrogation = no double recovery</span><span class="lib-key">Utmost good faith</span></div><div class="video-lesson-block" style="margin-top:14px;border-radius:12px;overflow:hidden;border:1.5px solid rgba(200,168,75,.3);background:rgba(10,22,40,.8)" data-video-id="19Z1Q5yrBGg">
  <div style="padding:12px 14px 0"><div style="display:flex;align-items:center;gap:8px;margin-bottom:10px;flex-wrap:wrap">
    <span style="background:var(--gold);color:#0a1628;font-size:.65rem;font-weight:900;padding:3px 8px;border-radius:20px;flex-shrink:0">LESSON 9</span>
    <span style="font-weight:700;font-size:.85rem;color:#fff">Insurance Principles</span>
    <span title="Bonus lesson — not part of the official FSCA qualifying criteria, included as extra value" style="background:rgba(200,168,75,.15);border:1px solid rgba(200,168,75,.5);color:var(--gold);font-size:.62rem;font-weight:800;padding:3px 8px;border-radius:20px;flex-shrink:0;display:flex;align-items:center;gap:3px">&#9733; BONUS LESSON</span>
  </div></div>
  <div style="position:relative;width:100%;padding-bottom:56.25%;background:#000">
    <iframe src="https://www.youtube-nocookie.com/embed/19Z1Q5yrBGg?rel=0&modestbranding=1&color=white" title="Lesson 9: Insurance Principles" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen loading="lazy" style="position:absolute;top:0;left:0;width:100%;height:100%;border:none"></iframe>
  </div>
  <div style="padding:10px 14px;display:flex;align-items:center;gap:6px">
    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#c8a84b" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
    <span style="font-size:.72rem;color:rgba(255,255,255,.45)">Watch the full lesson above · Questions follow in the quiz</span>
  </div>
</div>`},
{topic:"Long-term Insurance Products",exam:"RE5",quizTopic:"Insurance Principles",content:`<p class="mb8"><strong>LT Insurance Classes:</strong> (A) Risk policies; (B) Fund policies — investment component; (C) Assistance policies — funeral; (D) Health policies.</p><p class="mb8"><strong>Subcategory A (Assistance):</strong> Maximum benefit less than R30,000. Simplified underwriting. Typically funeral cover.</p><p class="mb8"><strong>Subcategory B1:</strong> Guaranteed benefit, no investment risk to policyholder.</p><p class="mb8"><strong>Subcategory B2:</strong> Investment-linked — policyholder bears investment risk.</p><p class="mb8"><strong>Life Annuity:</strong> Income for life. Cannot be surrendered or borrowed against. Insurer bears longevity risk.</p><p class="mb8"><strong>Replacement:</strong> Notify original insurer no later than when new application submitted. ROA must document rationale.</p><p class="mb8"><strong>Cooling-off:</strong> 31 days for distance marketing policies.</p><div class="mt10"><span class="lib-key">SubCat A = &lt;R30,000</span><span class="lib-key">B1 = guaranteed</span><span class="lib-key">B2 = investment risk</span><span class="lib-key">Notify insurer on replacement</span></div><div style="margin-top:14px;display:flex;align-items:center;gap:10px;background:rgba(255,255,255,.04);border:1.5px dashed rgba(200,168,75,.2);border-radius:10px;padding:12px 14px">
  <div style="background:rgba(200,168,75,.1);border-radius:8px;padding:8px;flex-shrink:0">
    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#c8a84b" stroke-width="2"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2" ry="2"/></svg>
  </div>
  <div>
    <div style="font-weight:700;font-size:.8rem;color:#8a6d1f">Video Lesson Coming Soon</div>
    <div style="font-size:.72rem;color:rgba(10,22,40,.6);margin-top:2px">This topic's video lesson is in production. Check back soon.</div>
  </div>
</div>`},
{topic:"Short-term Insurance Products",exam:"RE5",quizTopic:"Insurance Principles",content:`<p class="mb8"><strong>Personal Lines:</strong> Private individuals — home, vehicle, personal liability. No commercial use.</p><p class="mb8"><strong>Commercial Lines:</strong> Business risks — property, liability, marine, engineering.</p><p class="mb8"><strong>Indemnity Principle:</strong> ST insurance is indemnity-based. Insured cannot profit from a claim.</p><p class="mb8"><strong>Under-insurance:</strong> Insured value below replacement value triggers the average clause — partial claims paid proportionately.</p><p class="mb8"><strong>Excess:</strong> First portion of a claim borne by insured. Reduces moral hazard. Can be compulsory or voluntary.</p><p class="mb8"><strong>No-Claims Bonus:</strong> Premium discount for claim-free periods. Lost on first claim in the relevant period.</p><p class="mb8"><strong>Key Rep Obligation:</strong> Ensure client understands sum insured, excesses, exclusions, and disclosure obligations at inception.</p><div class="mt10"><span class="lib-key">Indemnity basis</span><span class="lib-key">Average clause = under-insurance</span><span class="lib-key">Personal vs commercial lines</span><span class="lib-key">Disclose excesses + exclusions</span></div><div style="margin-top:14px;display:flex;align-items:center;gap:10px;background:rgba(255,255,255,.04);border:1.5px dashed rgba(200,168,75,.2);border-radius:10px;padding:12px 14px">
  <div style="background:rgba(200,168,75,.1);border-radius:8px;padding:8px;flex-shrink:0">
    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#c8a84b" stroke-width="2"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2" ry="2"/></svg>
  </div>
  <div>
    <div style="font-weight:700;font-size:.8rem;color:#8a6d1f">Video Lesson Coming Soon</div>
    <div style="font-size:.72rem;color:rgba(10,22,40,.6);margin-top:2px">This topic's video lesson is in production. Check back soon.</div>
  </div>
</div>`},
{topic:"Financial Planning & Investments",exam:"RE5",quizTopic:"Investments & Financial Planning",content:`<p class="mb8"><strong>6-Step Financial Planning Process:</strong> (1) Establish relationship; (2) Gather data/goals; (3) Analyse; (4) Develop recommendations; (5) Implement; (6) Monitor.</p><p class="mb8"><strong>Risk Profile:</strong> Capacity (financial ability to absorb loss) + Tolerance (psychological comfort with volatility). Both must be assessed.</p><p class="mb8"><strong>Time Horizon:</strong> Short (&lt;3 years) = capital preservation. Medium (3-7) = balanced. Long (&gt;7) = growth.</p><p class="mb8"><strong>Diversification:</strong> Spread across asset classes, sectors, geographies to reduce unsystematic risk.</p><p class="mb8"><strong>Collective Investment Schemes (CIS):</strong> Regulated under CISCA. Pooled funds. Managed by registered CIS manager. Daily pricing.</p><p class="mb8"><strong>Retirement Products:</strong> RA — tax-deductible contributions; accessible from age 55. Pension/Provident — employer-sponsored; resignation rules differ.</p><div class="mt10"><span class="lib-key">6-step process</span><span class="lib-key">Capacity + tolerance</span><span class="lib-key">CISCA governs CIS</span><span class="lib-key">RA: accessible at 55</span></div><div style="margin-top:14px;display:flex;align-items:center;gap:10px;background:rgba(255,255,255,.04);border:1.5px dashed rgba(200,168,75,.2);border-radius:10px;padding:12px 14px">
  <div style="background:rgba(200,168,75,.1);border-radius:8px;padding:8px;flex-shrink:0">
    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#c8a84b" stroke-width="2"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2" ry="2"/></svg>
  </div>
  <div>
    <div style="font-weight:700;font-size:.8rem;color:#8a6d1f">Video Lesson Coming Soon</div>
    <div style="font-size:.72rem;color:rgba(10,22,40,.6);margin-top:2px">This topic's video lesson is in production. Check back soon.</div>
  </div>
</div>`},
{topic:"POPIA & Data Protection",exam:"RE5",quizTopic:"POPIA & Data Protection",content:`<p class="mb8"><strong>8 Processing Conditions:</strong> (1) Accountability; (2) Processing limitation; (3) Purpose specification; (4) Further processing limitation; (5) Information quality; (6) Openness; (7) Security safeguards; (8) Data subject participation.</p><p class="mb8"><strong>Responsible Party:</strong> Determines purpose and means of processing. Bears primary POPIA obligations.</p><p class="mb8"><strong>Operator:</strong> Processes on behalf of responsible party. Must have a written contract.</p><p class="mb8"><strong>Special Personal Information:</strong> Race, religion, health, sex life, criminal history — additional protections apply.</p><p class="mb8"><strong>Data Breach:</strong> Notify Information Regulator AND data subject as soon as reasonably possible.</p><p class="mb8"><strong>Retention:</strong> No longer than necessary for the purpose. FICA records = 5 years; financial advice records = 5 years.</p><div class="mt10"><span class="lib-key">8 conditions</span><span class="lib-key">Responsible party = primary liability</span><span class="lib-key">Special info = extra protection</span><span class="lib-key">Breach = notify regulator + subject</span></div><div style="margin-top:14px;display:flex;align-items:center;gap:10px;background:rgba(255,255,255,.04);border:1.5px dashed rgba(200,168,75,.2);border-radius:10px;padding:12px 14px">
  <div style="background:rgba(200,168,75,.1);border-radius:8px;padding:8px;flex-shrink:0">
    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#c8a84b" stroke-width="2"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2" ry="2"/></svg>
  </div>
  <div>
    <div style="font-weight:700;font-size:.8rem;color:#8a6d1f">Video Lesson Coming Soon</div>
    <div style="font-size:.72rem;color:rgba(10,22,40,.6);margin-top:2px">This topic's video lesson is in production. Check back soon.</div>
  </div>
</div>`},
{topic:"Complaints Handling",exam:"RE5",quizTopic:"FAIS Ombud",content:`<p class="mb8"><strong>Definition:</strong> A specific complaint about a financial service rendered — not a general query or service request.</p><p class="mb8"><strong>Internal Process:</strong> FSP must have a written complaints resolution policy. Complaint acknowledged promptly. Resolved within a reasonable timeframe.</p><p class="mb8"><strong>TCF Link:</strong> TCF Outcome 6 — no unreasonable post-sale barriers to complaining. Complaints are a key TCF measure.</p><p class="mb8"><strong>FAIS Ombud Escalation:</strong> Complainant exhausts FSP process first. If unsatisfied, 6 months from FSP final response to approach Ombud.</p><p class="mb8"><strong>Root Cause Analysis:</strong> Patterns of complaints must trigger systemic review — KI obligation.</p><p class="mb8"><strong>Recordkeeping:</strong> All complaints, outcomes, and remediation steps documented and available for FSCA inspection.</p><div class="mt10"><span class="lib-key">Written policy required</span><span class="lib-key">TCF Outcome 6</span><span class="lib-key">6 months to Ombud</span><span class="lib-key">Root cause analysis</span></div><div style="margin-top:14px;display:flex;align-items:center;gap:10px;background:rgba(255,255,255,.04);border:1.5px dashed rgba(200,168,75,.2);border-radius:10px;padding:12px 14px">
  <div style="background:rgba(200,168,75,.1);border-radius:8px;padding:8px;flex-shrink:0">
    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#c8a84b" stroke-width="2"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2" ry="2"/></svg>
  </div>
  <div>
    <div style="font-weight:700;font-size:.8rem;color:#8a6d1f">Video Lesson Coming Soon</div>
    <div style="font-size:.72rem;color:rgba(10,22,40,.6);margin-top:2px">This topic's video lesson is in production. Check back soon.</div>
  </div>
</div>`},
{topic:"NCA & Consumer Protection",exam:"RE5",quizTopic:"Regulatory Compliance",content:`<p class="mb8"><strong>NCA Purpose:</strong> Promote fair, transparent, competitive, responsible credit market. Protect consumers from reckless lending.</p><p class="mb8"><strong>Reckless Credit:</strong> Granted without adequate affordability assessment OR despite negative assessment. Court may suspend or restructure.</p><p class="mb8"><strong>Affordability Assessment:</strong> Compulsory before any credit agreement. Verify income and existing obligations.</p><p class="mb8"><strong>CPA — Consumer Protection Act:</strong> Right to privacy. Right to plain language. Right to cooling-off (5 business days for direct marketing sales).</p><p class="mb8"><strong>CPA Cooling-off:</strong> 5 business days for contracts from direct marketing. Applies even to financial products.</p><p class="mb8"><strong>FSCA & NCA Link:</strong> FSPs advising on credit products must understand NCA obligations — affordability and suitability duties intersect.</p><div class="mt10"><span class="lib-key">Reckless lending = no affordability check</span><span class="lib-key">CPA: 5-day cooling off</span><span class="lib-key">Plain language required</span><span class="lib-key">Privacy: opt-out respected</span></div><div style="margin-top:14px;display:flex;align-items:center;gap:10px;background:rgba(255,255,255,.04);border:1.5px dashed rgba(200,168,75,.2);border-radius:10px;padding:12px 14px">
  <div style="background:rgba(200,168,75,.1);border-radius:8px;padding:8px;flex-shrink:0">
    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#c8a84b" stroke-width="2"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2" ry="2"/></svg>
  </div>
  <div>
    <div style="font-weight:700;font-size:.8rem;color:#8a6d1f">Video Lesson Coming Soon</div>
    <div style="font-size:.72rem;color:rgba(10,22,40,.6);margin-top:2px">This topic's video lesson is in production. Check back soon.</div>
  </div>
</div>`},
{topic:"Maintaining Your FSP Licence",exam:"RE5",quizTopic:"Maintaining Your FSP Licence",content:`<p class="mb8"><strong>Task 2 — 18.5% of your RE5 exam, about 9 of your 50 questions.</strong> The third-heaviest task in the paper.</p><p class="mb4"><strong>Ongoing obligations:</strong> the FSP must display its licence at its business premises; licensing conditions remain binding for as long as the licence exists; a related accreditation (e.g. under the Medical Schemes Act) that is suspended or lapses can directly affect services in that category.</p><p class="mb8"><strong>Levies</strong> are payable to the FSCA under the Financial Sector and Deposit Insurance Levies Act.</p><p class="mb4"><strong>SUSPENSION</strong> — a licensing condition is not met, but the issue may be correctable. The licence is paused and can potentially be reinstated.</p><p class="mb4"><strong>WITHDRAWAL</strong> — a deliberate FSCA decision for serious or unresolved non-compliance. A new application would be needed.</p><p class="mb8"><strong>LAPSING</strong> — automatic, by operation of law: death of a sole proprietor, liquidation, voluntary closure, or failure to pay levies. It is NOT a punitive FSCA decision, which is why no recourse applies.</p><p class="mb4"><strong>Undesirable practices:</strong> the FSCA may declare a practice undesirable even where no explicit rule was broken. Reparation measures — including compensating affected clients — are available if an FSP continues after declaration.</p><p class="mb8"><strong>FAIS Act offences</strong> are criminal, separate from civil and administrative consequences. An FSP or representative can face both for the same conduct.</p><div class="mt10"><span class="lib-key">Lapsing is automatic</span><span class="lib-key">Withdrawal is a decision</span><span class="lib-key">No rule needed for "undesirable"</span><span class="lib-key">Display the licence</span></div><div style="margin-top:14px;display:flex;align-items:center;gap:10px;background:rgba(255,255,255,.04);border:1.5px dashed rgba(200,168,75,.2);border-radius:10px;padding:12px 14px">
  <div style="background:rgba(200,168,75,.1);border-radius:8px;padding:8px;flex-shrink:0">
    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#c8a84b" stroke-width="2"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2" ry="2"/></svg>
  </div>
  <div>
    <div style="font-weight:700;font-size:.8rem;color:#8a6d1f">Video Lesson Coming Soon</div>
    <div style="font-size:.72rem;color:rgba(10,22,40,.6);margin-top:2px">This topic's video lesson is in production. Check back soon.</div>
  </div>
</div>`},
{topic:"Record-Keeping Obligations",exam:"RE5",quizTopic:"Record-Keeping Obligations",content:`<p class="mb8"><strong>Task 5 — 7.5% of your RE5 exam, about 4 of your 50 questions.</strong></p><p class="mb4"><strong>Records of Advice (FAIS Act s18):</strong> minimum <strong>5 years from the date the advice was given</strong>.</p><p class="mb4"><strong>FICA / CDD records (FIC Act):</strong> <strong>5 years from the date the business relationship is TERMINATED</strong> — a different starting point.</p><p class="mb8"><strong>The trap:</strong> if a client relationship lasts 15 years, FICA records must still be kept for a further 5 years after it ends — 20 years in total. Examiners test which date the clock starts from.</p><p class="mb4"><strong>Form:</strong> records may be kept in printed or electronic form, provided they are safe from destruction and available for inspection by the Authority.</p><p class="mb4"><strong>Retrieval, not just storage:</strong> a record that exists but cannot be located within a reasonable time is treated, for practical purposes, as if it does not exist.</p><p class="mb4"><strong>Outsourcing:</strong> where record keeping is outsourced, the FSP remains responsible for compliance. The agreement must be properly executed.</p><p class="mb8"><strong>A client's request to delete their file early</strong> does not override a statutory retention obligation. Explain this to the client rather than deleting the file.</p><p class="mb8"><strong>Three-way overlap:</strong> FAIS (what advice records to keep), FIC Act (what CDD records, and for how long), and POPIA (how securely they must be protected).</p><div class="mt10"><span class="lib-key">ROA: 5 yrs from advice</span><span class="lib-key">FICA: 5 yrs from termination</span><span class="lib-key">Retrieval matters</span><span class="lib-key">POPIA security applies</span></div><div style="margin-top:14px;display:flex;align-items:center;gap:10px;background:rgba(255,255,255,.04);border:1.5px dashed rgba(200,168,75,.2);border-radius:10px;padding:12px 14px">
  <div style="background:rgba(200,168,75,.1);border-radius:8px;padding:8px;flex-shrink:0">
    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#c8a84b" stroke-width="2"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2" ry="2"/></svg>
  </div>
  <div>
    <div style="font-weight:700;font-size:.8rem;color:#8a6d1f">Video Lesson Coming Soon</div>
    <div style="font-size:.72rem;color:rgba(10,22,40,.6);margin-top:2px">This topic's video lesson is in production. Check back soon.</div>
  </div>
</div>`},
{topic:"KI Duties & Responsibilities",exam:"RE1",quizTopic:"KI Duties & Responsibilities",content:`<p class="mb8"><strong>KI Must Be:</strong> A natural person. Approved by FSCA. Manages ALL financial services rendered.</p><p class="mb8"><strong>Two Core Functions:</strong> (1) Manage and oversee rendering of financial services; (2) Establish and maintain the compliance function.</p><p class="mb8"><strong>Accountability:</strong> Ultimate compliance responsibility = KI and senior management. Cannot be outsourced.</p><p class="mb8"><strong>Duty on Non-Compliance:</strong> IMMEDIATE corrective action. Inaction after awareness = separate breach.</p><p class="mb8"><strong>Supervision Intensity:</strong> Proportionate to: nature/scale/complexity; representative's competency; risk to clients.</p><p class="mb8"><strong>CPD for KIs:</strong> Begins IMMEDIATELY upon appointment — no qualifying period.</p><div class="mt10"><span class="lib-key">Natural person only</span><span class="lib-key">2 core functions</span><span class="lib-key">Immediate action on breaches</span><span class="lib-key">KI CPD = immediate</span></div><div style="margin-top:14px;display:flex;align-items:center;gap:10px;background:rgba(255,255,255,.04);border:1.5px dashed rgba(200,168,75,.2);border-radius:10px;padding:12px 14px">
  <div style="background:rgba(200,168,75,.1);border-radius:8px;padding:8px;flex-shrink:0">
    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#c8a84b" stroke-width="2"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2" ry="2"/></svg>
  </div>
  <div>
    <div style="font-weight:700;font-size:.8rem;color:#8a6d1f">Video Lesson Coming Soon</div>
    <div style="font-size:.72rem;color:rgba(10,22,40,.6);margin-top:2px">This topic's video lesson is in production. Check back soon.</div>
  </div>
</div>`},
{topic:"Twin Peaks & Regulatory Framework",exam:"RE1",quizTopic:"Twin Peaks & Regulatory Framework",content:`<p class="mb8"><strong>Twin Peaks (FSRA 2017):</strong> PA (under SARB) = financial SOUNDNESS. FSCA = market CONDUCT and consumer PROTECTION.</p><p class="mb8"><strong>Key Legislation:</strong> FAIS Act · LT Insurance Act · ST Insurance Act · CISCA · Financial Markets Act · NCA · FICA · POPIA.</p><p class="mb8"><strong>POPIA:</strong> Specific, lawful purpose. Not retained longer than necessary. No unauthorised sharing. Digital and physical records both covered.</p><p class="mb8"><strong>Financial Soundness:</strong> Capital adequacy must be maintained. Audited financials within 4 months of year-end.</p><div class="mt10"><span class="lib-key">PA = soundness</span><span class="lib-key">FSCA = conduct</span><span class="lib-key">FSRA 2017</span><span class="lib-key">POPIA: purpose limitation</span></div><div style="margin-top:14px;display:flex;align-items:center;gap:10px;background:rgba(255,255,255,.04);border:1.5px dashed rgba(200,168,75,.2);border-radius:10px;padding:12px 14px">
  <div style="background:rgba(200,168,75,.1);border-radius:8px;padding:8px;flex-shrink:0">
    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#c8a84b" stroke-width="2"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2" ry="2"/></svg>
  </div>
  <div>
    <div style="font-weight:700;font-size:.8rem;color:#8a6d1f">Video Lesson Coming Soon</div>
    <div style="font-size:.72rem;color:rgba(10,22,40,.6);margin-top:2px">This topic's video lesson is in production. Check back soon.</div>
  </div>
</div>`},
{topic:"Governance & COI Management",exam:"RE1",quizTopic:"Governance & COI Management",content:`<p class="mb8"><strong>King IV Outcomes:</strong> (1) Ethical culture; (2) Good performance; (3) Effective control; (4) Legitimacy.</p><p class="mb8"><strong>COI Management Policy:</strong> Adopted by board/sole proprietor. Reviewed ANNUALLY. Published for public inspection. Identifies, manages, and discloses conflicts.</p><p class="mb8"><strong>Material Interest:</strong> >R1,000 per year from same third party. Disclose before advice. Managed in COI policy.</p><p class="mb8"><strong>Ethical Leadership:</strong> Model behaviour, design ethical incentive systems, build a culture where client interests come first.</p><p class="mb8"><strong>Outsourcing:</strong> Functions may be outsourced. REGULATORY RESPONSIBILITY CANNOT. Due diligence + ongoing oversight mandatory.</p><div class="mt10"><span class="lib-key">King IV: 4 outcomes</span><span class="lib-key">COI: annual review</span><span class="lib-key">R1,000 threshold</span><span class="lib-key">Cannot outsource responsibility</span></div><div style="margin-top:14px;display:flex;align-items:center;gap:10px;background:rgba(255,255,255,.04);border:1.5px dashed rgba(200,168,75,.2);border-radius:10px;padding:12px 14px">
  <div style="background:rgba(200,168,75,.1);border-radius:8px;padding:8px;flex-shrink:0">
    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#c8a84b" stroke-width="2"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2" ry="2"/></svg>
  </div>
  <div>
    <div style="font-weight:700;font-size:.8rem;color:#8a6d1f">Video Lesson Coming Soon</div>
    <div style="font-size:.72rem;color:rgba(10,22,40,.6);margin-top:2px">This topic's video lesson is in production. Check back soon.</div>
  </div>
</div>`},
{topic:"KI Personal Accountability",exam:"RE1",quizTopic:"KI Duties & Responsibilities",content:`<p class="mb8"><strong>Statutory Function:</strong> Oversee and manage ALL rendering of financial services. Ensure ongoing F&P compliance of all representatives.</p><p class="mb8"><strong>Personal Accountability:</strong> KI is personally liable for compliance failures if they failed to take reasonable steps to prevent them. Ignorance is not a defence.</p><p class="mb8"><strong>Dual Role:</strong> A KI may also act as a representative — must meet F&P for BOTH roles simultaneously.</p><p class="mb8"><strong>KI Vacancy:</strong> Must be filled within 60 days. FSCA notified if gap persists. Operations in affected categories may be suspended.</p><p class="mb8"><strong>CPD for KIs:</strong> Begins IMMEDIATELY on appointment. No qualifying period. Minimum hours depend on categories of authorisation.</p><p class="mb8"><strong>Delegation:</strong> Day-to-day supervision may be delegated. REGULATORY ACCOUNTABILITY CANNOT BE DELEGATED. KI remains answerable to FSCA.</p><div class="mt10"><span class="lib-key">Personal accountability</span><span class="lib-key">60-day vacancy rule</span><span class="lib-key">CPD: immediate from appointment</span><span class="lib-key">Cannot delegate accountability</span></div><div style="margin-top:14px;display:flex;align-items:center;gap:10px;background:rgba(255,255,255,.04);border:1.5px dashed rgba(200,168,75,.2);border-radius:10px;padding:12px 14px">
  <div style="background:rgba(200,168,75,.1);border-radius:8px;padding:8px;flex-shrink:0">
    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#c8a84b" stroke-width="2"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2" ry="2"/></svg>
  </div>
  <div>
    <div style="font-weight:700;font-size:.8rem;color:#8a6d1f">Video Lesson Coming Soon</div>
    <div style="font-size:.72rem;color:rgba(10,22,40,.6);margin-top:2px">This topic's video lesson is in production. Check back soon.</div>
  </div>
</div>`},
{topic:"Supervision of Representatives",exam:"RE1",quizTopic:"Supervision of Representatives",content:`<p class="mb8"><strong>Who Supervises:</strong> The FSP and its KI bear PRIMARY statutory responsibility. Cannot be fully delegated to a compliance officer.</p><p class="mb8"><strong>Supervised Representative Disclosure:</strong> Must inform EVERY client BEFORE rendering services that they are operating under supervision.</p><p class="mb8"><strong>Supervision Period:</strong> Time-limited — ends when ALL F&P requirements are met. Cannot be used as a permanent control mechanism.</p><p class="mb8"><strong>Removing Supervision:</strong> KI must confirm in WRITING that all F&P requirements are met before supervision condition is lifted.</p><p class="mb8"><strong>Insolvency Disclosure:</strong> Representative facing insolvency must disclose to KI. KI assesses F&P impact and notifies FSCA if licence condition triggered.</p><p class="mb8"><strong>Debarment Trigger:</strong> Repeated unsupervised service rendering = serious breach. KI must assess whether debarment is warranted.</p><div class="mt10"><span class="lib-key">KI bears primary responsibility</span><span class="lib-key">Disclose supervision to clients</span><span class="lib-key">Written confirmation to lift</span><span class="lib-key">Insolvency = notify FSCA</span></div><div style="margin-top:14px;display:flex;align-items:center;gap:10px;background:rgba(255,255,255,.04);border:1.5px dashed rgba(200,168,75,.2);border-radius:10px;padding:12px 14px">
  <div style="background:rgba(200,168,75,.1);border-radius:8px;padding:8px;flex-shrink:0">
    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#c8a84b" stroke-width="2"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2" ry="2"/></svg>
  </div>
  <div>
    <div style="font-weight:700;font-size:.8rem;color:#8a6d1f">Video Lesson Coming Soon</div>
    <div style="font-size:.72rem;color:rgba(10,22,40,.6);margin-top:2px">This topic's video lesson is in production. Check back soon.</div>
  </div>
</div>`},
{topic:"FSP Licensing & Financial Soundness",exam:"RE1",quizTopic:"FSP Licensing & Financial Soundness",content:`<p class="mb8"><strong>Licence Categories:</strong> Cat I (advice + intermediary); Cat II (discretionary); Cat IIA (hedge funds); Cat III (administrative FSP); Cat IV (assistance).</p><p class="mb8"><strong>Adding a Category:</strong> KIs and representatives must meet F&P requirements for the NEW category. Commercial factors are irrelevant to the licensing assessment.</p><p class="mb8"><strong>Capital Adequacy Breach:</strong> Must be reported to FSCA IMMEDIATELY — not deferred to annual reporting. Remediation plan required.</p><p class="mb8"><strong>PI Insurance Lapse:</strong> Triggers AUTOMATIC licence suspension. PI cover is a continuous F&P requirement.</p><p class="mb8"><strong>Compliance Report:</strong> Submitted within 4 months of FSP financial year-end. Same deadline as annual financial statements.</p><p class="mb8"><strong>Financial Statements:</strong> Submitted within 4 months of financial year-end. Audited where required by category.</p><div class="mt10"><span class="lib-key">PI lapse = auto suspension</span><span class="lib-key">Capital breach = notify immediately</span><span class="lib-key">4 months: reports + statements</span><span class="lib-key">Cat I–IV licensing</span></div><div style="margin-top:14px;display:flex;align-items:center;gap:10px;background:rgba(255,255,255,.04);border:1.5px dashed rgba(200,168,75,.2);border-radius:10px;padding:12px 14px">
  <div style="background:rgba(200,168,75,.1);border-radius:8px;padding:8px;flex-shrink:0">
    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#c8a84b" stroke-width="2"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2" ry="2"/></svg>
  </div>
  <div>
    <div style="font-weight:700;font-size:.8rem;color:#8a6d1f">Video Lesson Coming Soon</div>
    <div style="font-size:.72rem;color:rgba(10,22,40,.6);margin-top:2px">This topic's video lesson is in production. Check back soon.</div>
  </div>
</div>`},
{topic:"TCF & Consumer Outcomes",exam:"RE1",quizTopic:"TCF & Consumer Outcomes",content:`<p class="mb8"><strong>6 TCF Outcomes:</strong> (1) Fair culture embedded; (2) Products designed for identified market; (3) Clear communication; (4) Suitable advice; (5) Products perform as expected; (6) No post-sale barriers.</p><p class="mb8"><strong>KI's TCF Obligation:</strong> Must embed TCF as a CULTURAL value — not a compliance tick-box. Governance and incentive structures must reflect TCF.</p><p class="mb8"><strong>Complaints as TCF Measure:</strong> Pattern of complaints signals systemic TCF failure. Root cause analysis is a KI obligation.</p><p class="mb8"><strong>TCF Outcome 4 — Suitability:</strong> Advice must be suitable and take account of the individual client's circumstances. Most frequently tested outcome.</p><p class="mb8"><strong>TCF Outcome 6 — Post-sale:</strong> No unreasonable barriers to switching, claiming, or complaining. Tested frequently in KI context.</p><p class="mb8"><strong>Board Accountability:</strong> Under King IV, the governing body is ultimately accountable for TCF culture — not just the compliance function.</p><div class="mt10"><span class="lib-key">6 outcomes — know all</span><span class="lib-key">Outcome 4: suitability</span><span class="lib-key">Outcome 6: post-sale barriers</span><span class="lib-key">Culture not compliance</span></div><div style="margin-top:14px;display:flex;align-items:center;gap:10px;background:rgba(255,255,255,.04);border:1.5px dashed rgba(200,168,75,.2);border-radius:10px;padding:12px 14px">
  <div style="background:rgba(200,168,75,.1);border-radius:8px;padding:8px;flex-shrink:0">
    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#c8a84b" stroke-width="2"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2" ry="2"/></svg>
  </div>
  <div>
    <div style="font-weight:700;font-size:.8rem;color:#8a6d1f">Video Lesson Coming Soon</div>
    <div style="font-size:.72rem;color:rgba(10,22,40,.6);margin-top:2px">This topic's video lesson is in production. Check back soon.</div>
  </div>
</div>`},
{topic:"FAIS Ombud & Complaints",exam:"RE1",quizTopic:"FAIS Ombud & Complaints",content:`<p class="mb8"><strong>Jurisdiction:</strong> FAIS Ombud covers FSPs licensed under FAIS only. Banks = Banking Ombud. Medical schemes = Council for Medical Schemes.</p><p class="mb8"><strong>Financial Cap:</strong> Maximum award R3,500,000 per complaint. Exceeding this requires respondent FSP's written consent.</p><p class="mb8"><strong>Case Fee:</strong> None. Discontinued with effect from 10 September 2012. No fee is charged to the complainant or to the respondent FSP.</p><p class="mb8"><strong>Internal Process First:</strong> Complainant must exhaust FSP's internal complaints process. Then 6 months from FSP's final response to approach Ombud.</p><p class="mb8"><strong>Determination = Court Order:</strong> Unappealed FAIS Ombud determination has the force of a High Court civil judgement. Writ of execution may follow.</p><p class="mb8"><strong>KI Obligation on Pattern:</strong> Multiple complaints in same category = systemic issue. KI must conduct root cause analysis and consider all affected clients — not just complainants.</p><div class="mt10"><span class="lib-key">Cap: R3,500,000</span><span class="lib-key">No case fee since 2012</span><span class="lib-key">6 months to Ombud</span><span class="lib-key">Determination = court order</span></div><div style="margin-top:14px;display:flex;align-items:center;gap:10px;background:rgba(255,255,255,.04);border:1.5px dashed rgba(200,168,75,.2);border-radius:10px;padding:12px 14px">
  <div style="background:rgba(200,168,75,.1);border-radius:8px;padding:8px;flex-shrink:0">
    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#c8a84b" stroke-width="2"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2" ry="2"/></svg>
  </div>
  <div>
    <div style="font-weight:700;font-size:.8rem;color:#8a6d1f">Video Lesson Coming Soon</div>
    <div style="font-size:.72rem;color:rgba(10,22,40,.6);margin-top:2px">This topic's video lesson is in production. Check back soon.</div>
  </div>
</div>`},
{topic:"FSP Licence Lifecycle & Enforcement",exam:"RE1",quizTopic:"FSP Licensing & Financial Soundness",content:`<p class="mb8"><strong>Moonstone Task 3 — 12.9% of the RE1 exam.</strong> The single largest KI task.</p><p class="mb4"><strong>SUSPENSION:</strong> an FSCA sanction, or automatic for some triggers (e.g. PI lapse). Reversible once grounds are addressed.</p><p class="mb4"><strong>WITHDRAWAL:</strong> an FSCA decision, typically after a serious or persistent breach. A fresh application may be required.</p><p class="mb8"><strong>LAPSING:</strong> happens by operation of law — death of a sole proprietor, voluntary closure, failure to renew. NOT an FSCA sanction, so no recourse applies.</p><p class="mb4"><strong>Undesirable practices:</strong> the FSCA can declare a practice undesirable even where no law was broken. Reparation measures are available if an FSP continues after declaration.</p><p class="mb4"><strong>Guidance Notice</strong> = interpretation, not legally binding. A <strong>directive</strong> carries direct legal consequences. The exam tests this distinction.</p><p class="mb8"><strong>Levies</strong> are payable to the FSCA under the Financial Sector and Deposit Insurance Levies Act.</p><div class="mt10"><span class="lib-key">Lapsing ≠ sanction</span><span class="lib-key">Recourse: suspension/withdrawal only</span><span class="lib-key">Directive is binding</span></div>`},
{topic:"The Compliance Officer & Compliance Function",exam:"RE1",quizTopic:"The Compliance Officer & Compliance Function",content:`<p class="mb8"><strong>Moonstone Task 7 — 8.1% of the RE1 exam.</strong></p><p class="mb4"><strong>The KI ESTABLISHES the compliance function. The CO MONITORS it.</strong> This distinction is heavily tested.</p><p class="mb4"><strong>FSCA approval is mandatory</strong> before a CO may act. An unapproved CO cannot lawfully perform the role, however competent.</p><p class="mb4"><strong>Independence:</strong> the CO must function objectively and independently of management. A CO who reports to, and is evaluated by, the person whose conduct they monitor cannot be independent.</p><p class="mb4"><strong>Resources:</strong> the function must have sufficient resources for an efficient compliance service. Under-resourcing is itself a compliance risk.</p><p class="mb4"><strong>Sign-off:</strong> the KI must understand a compliance report well enough to sign it responsibly. A signature without engagement is evidence the KI had the information and failed to act.</p><p class="mb8"><strong>No CO appointed?</strong> The obligation to manage conflicts of interest does not disappear — it must be met through general governance.</p><div class="mt10"><span class="lib-key">KI establishes, CO monitors</span><span class="lib-key">FSCA approval mandatory</span><span class="lib-key">Independence required</span></div>`},
{topic:"Operational Ability, Outsourcing & Continuity",exam:"RE1",quizTopic:"FSP Licensing & Financial Soundness",content:`<p class="mb8"><strong>Moonstone Task 5 — 7.9% of the RE1 exam.</strong> Financial soundness asks whether the FSP has enough capital. Operational ability asks whether it can actually operate.</p><p class="mb4"><strong>Required:</strong> adequate storage and filing systems; the bank accounts required by the General Code; prescribed guarantees, PI or fidelity cover; disaster recovery and business continuity plans.</p><p class="mb4"><strong>Continuity is not just IT.</strong> If the sole KI is also the sole representative and becomes unavailable, the FSP may need to cease rendering services. Key-person risk must be addressed.</p><p class="mb4"><strong>Outsourcing:</strong> written service level agreements are mandatory. Processes must ensure suitable providers are selected. An outsourced entity that itself renders financial services must be an authorised FSP.</p><p class="mb8"><strong>Outsourcing transfers the task, never the accountability.</strong></p><div class="mt10"><span class="lib-key">Written SLA required</span><span class="lib-key">Key-person risk</span><span class="lib-key">Accountability stays with KI</span></div>`},
{topic:"FICA, AML & Terrorist Financing Oversight",exam:"RE1",quizTopic:"FSP Licensing & Financial Soundness",content:`<p class="mb8"><strong>Moonstone Task 10 — 5.6% of the RE1 exam.</strong> FSPs are accountable institutions under the FIC Act.</p><p class="mb4"><strong>Risk Management and Compliance Programme (RMP):</strong> required by s42 of the FIC Act. The KI must verify it actually functions — not merely that a document exists.</p><p class="mb4"><strong>Client Due Diligence:</strong> identity must be established and verified BEFORE a business relationship is established or a single transaction concluded.</p><p class="mb4"><strong>Employee training</strong> on recognising and reporting suspicious transactions is a core obligation. Untrained staff cannot fulfil a duty they do not understand.</p><p class="mb4"><strong>Reporting deadlines:</strong> STR — as soon as possible and no later than <strong>15 days</strong>, excluding Saturdays, Sundays and public holidays (Reg 24(3)). CTR — <strong>3 business days</strong>, triggered by cash exceeding R49,999.99. TPR — immediately.</p><p class="mb8"><strong>FICA sits alongside FAIS</strong> — strong FAIS compliance does not satisfy FICA oversight.</p><div class="mt10"><span class="lib-key">STR = 15 days</span><span class="lib-key">CTR = 3 business days</span><span class="lib-key">RMP must function</span></div>`},
{topic:"Debarment — Process & KI Obligations",exam:"RE1",quizTopic:"Supervision of Representatives",content:`<p class="mb8"><strong>Moonstone Task 16 — 3.1% of the RE1 exam.</strong></p><p class="mb4"><strong>Grounds:</strong> the representative must have failed to comply with a FAIS Act provision in a MATERIAL manner. Minor lapses are not automatic grounds.</p><p class="mb4"><strong>Procedural fairness:</strong> the representative must be informed of the reasons debarment is being considered, the process to be followed, and the recourse available to them.</p><p class="mb4"><strong>Serious misconduct does NOT remove the requirement for a fair, documented process.</strong> It may simply mean the outcome is more likely to be confirmed.</p><p class="mb4"><strong>Notification:</strong> Part I within <strong>5 days</strong> of the debarment decision. Part II within <strong>15 days</strong> of the debarment date.</p><p class="mb8"><strong>The FSP debars. The FSCA is notified.</strong> Debarment is a regulatory process, not an HR matter.</p><div class="mt10"><span class="lib-key">Material breach required</span><span class="lib-key">Part I: 5 days</span><span class="lib-key">Part II: 15 days</span><span class="lib-key">FSP debars, not FSCA</span></div>`},
{topic:"Addendum — Products, Auditing, Disclosure & Records",exam:"RE1",quizTopic:"FAIS Ombud & Complaints",content:`<p class="mb8"><strong>Moonstone Tasks 2, 8, 9, 12 &amp; 13.</strong> Individually lighter, but each directly examinable.</p><p class="mb4"><strong>Products:</strong> "financial product" and "intermediary service" are separately defined under FAIS. Advising on a product and rendering an intermediary service in relation to it are distinct concepts, each triggering its own obligations.</p><p class="mb4"><strong>Accounting &amp; auditing:</strong> specific criteria govern who may be appointed as auditor or accounting officer. Where the FSP receives client funds, the audit report must meet regulatory requirements — a generic financial audit is not automatically sufficient.</p><p class="mb4"><strong>Custody of client funds:</strong> a separate account with a registered bank is required. There is NO small-amount or short-duration exception.</p><p class="mb4"><strong>Record keeping:</strong> ROA — 5 years from the date advice was given. FICA/CDD — 5 years from the date the relationship is TERMINATED. Different starting points.</p><p class="mb8"><strong>Register of representatives:</strong> must be maintained and kept current. A register listing departed representatives, or omitting current ones, is itself a compliance failure.</p><div class="mt10"><span class="lib-key">Separate client account</span><span class="lib-key">ROA: 5 yrs from advice</span><span class="lib-key">FICA: 5 yrs from termination</span><span class="lib-key">Register must be current</span></div>`}]);

// ══════════════════════════════════════════════════════
// BLUEPRINT EXAM — full-length simulated exams built to match the real
// exam's exact task allocation (RE5: 50 questions, RE1: 80 questions),
// derived from real FSCA task-weighting data. Distinct from the regular
// practice question banks (QB_RE5/QB_RE1) — used only by the dedicated
// Blueprint Exam feature, never mixed into ordinary quiz/sim pools.
//
// PROGRESS: RE5 batch 1 of ~6 complete (Tasks 1, 6, 7 — 9 of 50 questions).
// RE1 not yet started. Continues in phased batches — see conversation
// for the full task-by-task allocation plan (exact counts per task,
// derived via largest-remainder apportionment from BLUEPRINT_DATA).
// ══════════════════════════════════════════════════════
const BLUEPRINT_EXAM_RE5 = Object.freeze([
{taskNum:1,taskTitle:"Understand the FAIS Act as a Regulatory Framework",t:"Scope & Definitions",lv:"K",q:"In terms of the FAIS Act, the person who renders a financial service to a client on behalf of an authorised FSP, and who is not a mandated intermediary, is defined as a:",o:["Representative","Key individual","Compliance officer","Product supplier"],a:0,e:"Section 1 defines a representative as any person who renders a financial service to a client for or on behalf of a provider. A key individual manages or oversees; a compliance officer monitors; a product supplier issues the product."},
{taskNum:1,taskTitle:"Understand the FAIS Act as a Regulatory Framework",t:"Scope & Definitions",lv:"AP",q:"A representative provides a client with a factual comparison table of three unit trust funds, reads directly from an approved script without deviation, and expresses no opinion. In terms of the definition of 'advice' in the FAIS Act, this activity:",o:["Constitutes advice, because financial products were discussed with a client","Does not constitute advice, no recommendation or guidance having been furnished","Constitutes advice only if the client subsequently purchases one of the funds","Does not constitute advice only because the information was reduced to writing"],a:1,e:"Advice requires a recommendation, guidance or proposal of a financial nature. Factual information furnished without any such element falls outside the definition. Option C is a trap — the definition does not depend on the outcome of the transaction."},
{taskNum:2,taskTitle:"Contribute Towards Maintaining an FSP Licence",t:"Regulatory Compliance",lv:"K",q:"Section 8(8) of the FAIS Act requires an authorised FSP to:",o:["Publish particulars of its licence in the Government Gazette annually","Submit its licence to the Authority for endorsement each year","Display a certified copy of its licence at every business premises","Provide a copy of its licence to every client before rendering a service"],a:2,e:"Section 8(8) requires the licence to be displayed prominently at the business premises. The other options describe requirements that do not exist."},
{taskNum:2,taskTitle:"Contribute Towards Maintaining an FSP Licence",t:"Regulatory Compliance",lv:"K",q:"In terms of section 11 of the FAIS Act, an FSP licence lapses when the:",o:["Authority resolves that the FSP has contravened a material provision","Authority issues a directive under section 38C","FSP fails to submit its annual compliance report","FSP is provisionally or finally liquidated or sequestrated"],a:3,e:"Section 11 deals with lapsing, which occurs by operation of law on events such as liquidation, sequestration or death. The remaining options describe suspension, withdrawal or enforcement, which are deliberate decisions of the Authority."},
{taskNum:2,taskTitle:"Contribute Towards Maintaining an FSP Licence",t:"Regulatory Compliance",lv:"C",q:"Which ONE of the following statements correctly distinguishes lapsing from withdrawal of an FSP licence?",o:["Lapsing occurs by operation of law, withdrawal by decision","Lapsing follows an Authority decision, while withdrawal occurs automatically","Both require prior written notice to the FSP and an oral hearing","Lapsing may be appealed, while withdrawal is final and unappealable"],a:0,e:"Lapsing occurs automatically on the occurrence of a defined event. Withdrawal is a deliberate decision by the Authority. Option D is a trap — an FSP has recourse against a withdrawal decision under section 39."},
{taskNum:2,taskTitle:"Contribute Towards Maintaining an FSP Licence",t:"Regulatory Compliance",lv:"AP",q:"An FSP is authorised for Category I, subcategory long-term insurance category B1 only. A representative concludes a transaction in respect of a participatory interest in a collective investment scheme. The MOST accurate description of the position is that the:",o:["Transaction is valid because the FSP holds a Category I licence","FSP has rendered a financial service outside the subcategories for which it is licensed","Transaction is valid provided the representative holds the relevant qualification","FSP must notify the Authority within 15 days and the transaction is then regularised"],a:1,e:"Authorisation operates at subcategory level, not category level. Neither the representative's own qualification nor subsequent notification cures a service rendered outside the licensed subcategory."},
{taskNum:2,taskTitle:"Contribute Towards Maintaining an FSP Licence",t:"Regulatory Compliance",lv:"K",q:"In terms of section 34 of the FAIS Act, the Authority may declare a business practice to be undesirable:",o:["Only where the practice contravenes an express provision of the Act","Only after the practice has caused demonstrable financial loss to a client","Even though the practice does not contravene any express provision of the Act","Only at the written request of the Ombud for Financial Services Providers"],a:2,e:"Section 34 is deliberately broad and does not require a contravention of an express provision. The remaining options import limitations that the section does not contain."},
{taskNum:2,taskTitle:"Contribute Towards Maintaining an FSP Licence",t:"Regulatory Compliance",lv:"C",q:"The Authority has published a notice declaring a particular practice undesirable. An FSP established before publication that its own conduct falls within the declared practice. The FSP must:",o:["Continue until the Authority issues a directive addressed to it individually","Apply to the Authority for an exemption within 30 days of publication","Continue but disclose the declaration to all affected clients in writing","Cease the practice, since the declaration applies generally to all persons"],a:3,e:"A declaration under section 34 applies generally. It does not require an individually addressed directive before it takes effect, and there is no exemption or disclosure mechanism that permits continuation."},
{taskNum:2,taskTitle:"Contribute Towards Maintaining an FSP Licence",t:"Regulatory Compliance",lv:"K",q:"Professional indemnity and fidelity insurance cover requirements for FSPs are prescribed by:",o:["Board Notice 123 of 2009","Board Notice 80 of 2003","Board Notice 127 of 2010","Board Notice 194 of 2017"],a:0,e:"BN 123 of 2009 prescribes professional indemnity and fidelity insurance cover. BN 80 is the General Code, BN 127 deals with compliance officers, and BN 194 with Fit and Proper requirements."},
{taskNum:2,taskTitle:"Contribute Towards Maintaining an FSP Licence",t:"Regulatory Compliance",lv:"C",q:"An FSP's guarantee, professional indemnity or fidelity insurance cover lapses. Which ONE of the following statements is NOT correct?",o:["The FSP no longer complies with an applicable licensing condition","The FSP is afforded a statutory thirty-day remedial period","The Authority may suspend or withdraw the licence in consequence","The key individual must take steps to restore compliance promptly"],a:1,e:"No statutory 30-day remedial period exists. The remaining statements are correct: the lapse is a compliance failure with potential licensing consequences that the key individual must address."},
{taskNum:2,taskTitle:"Contribute Towards Maintaining an FSP Licence",t:"Regulatory Compliance",lv:"AN",q:"Consider the following events, each occurring on the same day at an authorised FSP. Arrange them in order of the SHORTEST to the LONGEST period within which the FSP must act, and select the correct option.\ni) The FSP must notify the Authority of a completed debarment (first notification)\nii) The FSP must retain a record of advice, calculated from the date the advice was furnished\niii) The FSP must submit the second, detailed debarment notification to the Authority\niv) The FSP must fill a vacancy in the office of key individual",o:["(iii) then (i) then (ii) then (iv)","(i) then (iv) then (iii) then (ii)","(i) then (iii) then (iv) then (ii)","(iv) then (i) then (iii) then (ii)"],a:2,e:"First debarment notification is 5 days; the detailed notification is 15 days; a key individual vacancy must be filled within 60 days; records of advice are retained for 5 years. The correct ascending sequence is therefore (i), (iii), (iv), (ii)."},
{taskNum:3,taskTitle:"Define the Role of the Key Individual",t:"FAIS Role Players",lv:"K",q:"In terms of the FAIS Act, a key individual must be:",o:["A natural or juristic person","A juristic person only where the FSP is a company","Any person approved by the compliance officer","A natural person — never a juristic person"],a:3,e:"Only a natural person may be approved as a key individual. A representative, by contrast, may be a juristic person."},
{taskNum:3,taskTitle:"Define the Role of the Key Individual",t:"FAIS Role Players",lv:"AN",q:"A representative is concerned that the FSP's sole key individual no longer meets the honesty, integrity and good standing requirements. The MOST accurate statement of the implication for the representative is that the:",o:["FSP's authorisation may be affected, and with it the representative's position","Representative is unaffected, the requirement applying to the key individual personally","Representative must immediately report the matter directly to the Ombud","Representative automatically becomes debarred along with the key individual"],a:0,e:"Where a key individual ceases to meet the requirements, the FSP's own authorisation may be suspended or withdrawn, which affects every representative operating under that licence. There is no automatic debarment of representatives."},
{taskNum:3,taskTitle:"Define the Role of the Key Individual",t:"FAIS Role Players",lv:"C",q:"Which ONE of the following is NOT a function of a key individual under the FAIS Act?",o:["Managing and overseeing the rendering of financial services","Approving the appointment of the compliance officer on behalf of the Authority","Ensuring representatives comply with the Fit and Proper requirements on an ongoing basis","Taking responsibility for the activities of the FSP that relate to financial services"],a:1,e:"Approval of a compliance officer is a function of the Authority, not the key individual. The remaining options are genuine management and oversight responsibilities."},
{taskNum:4,taskTitle:"Adhere to the Specific Codes of Conduct",t:"General Code of Conduct",lv:"K",q:"Under the General Code of Conduct, an 'immaterial financial interest' means a financial interest with a determinable monetary value, the aggregate of which does not exceed, in any calendar year, from any one third party:",o:["R500","R1 000","R2 500","R5 000"],a:1,e:"The threshold is R1 000 in aggregate per calendar year from any one third party."},
{taskNum:4,taskTitle:"Adhere to the Specific Codes of Conduct",t:"General Code of Conduct",lv:"AP",q:"A representative receives from one product supplier a gift valued at R400 in March, a voucher valued at R450 in July, and a further gift valued at R300 in November of the same calendar year. In respect of the November gift, the representative:",o:["May accept it, as each individual interest is below the threshold","May accept it only if the aggregate is disclosed to the client in the record of advice","May not accept it, as the aggregate for the calendar year would exceed R1 000","May accept it provided the key individual approves the acceptance in writing"],a:2,e:"The threshold applies to the aggregate value from one third party in a calendar year. R400 plus R450 plus R300 is R1 150, which exceeds R1 000. Disclosure or internal approval does not cure the position."},
{taskNum:4,taskTitle:"Adhere to the Specific Codes of Conduct",t:"General Code of Conduct",lv:"K",q:"In terms of section 10 of the General Code of Conduct, a provider who receives funds from a client must deposit those funds:",o:["Into any account nominated by the client in writing","Into the provider's general business account, provided reconciliation occurs monthly","Into a trust account administered by the FSP's appointed auditor","Into a separate account designated for client funds, opened with a registered bank"],a:3,e:"Section 10 requires a separate account with a registered bank designated for client funds. The remaining options describe arrangements that do not satisfy the requirement."},
{taskNum:4,taskTitle:"Adhere to the Specific Codes of Conduct",t:"General Code of Conduct",lv:"AP",q:"A client instructs a representative to proceed with a product purchase without undergoing a financial needs analysis, and signs a written waiver to that effect. In terms of the General Code of Conduct, the representative:",o:["May proceed, having alerted the client to the risks and recorded that fact","May proceed on the strength of the signed waiver, no further step being required","May not proceed under any circumstances until a full analysis is completed","May proceed only where the key individual countersigns the waiver in writing"],a:0,e:"Where a client declines to provide information or insists on a particular transaction, the Code requires the provider to alert the client to the limitations and risks and to record that the client accepted them. It is neither an unqualified waiver nor an absolute prohibition."},
{taskNum:4,taskTitle:"Adhere to the Specific Codes of Conduct",t:"General Code of Conduct",lv:"AP",q:"A representative replaces a client's existing long-term insurance policy with a new policy. In terms of the General Code of Conduct, the representative must inform the client of the actual and potential financial implications, costs and consequences of the replacement, and must:",o:["Notify the existing product supplier within thirty days after inception","Notify the existing product supplier when the new application is submitted","Obtain the written consent of the existing product supplier beforehand","Notify the Authority of the replacement within fifteen days of inception"],a:1,e:"The Code requires notification of the replacement to the existing product supplier at the time the application for the new product is submitted. There is no supplier consent requirement and no notification to the Authority."},
{taskNum:4,taskTitle:"Adhere to the Specific Codes of Conduct",t:"General Code of Conduct",lv:"AN",q:"A representative furnishes advice to a client. Which combination of the following must, in terms of the General Code of Conduct, be reflected in the record of advice?\ni) A brief summary of the information and material on which the advice was based\nii) The financial products considered\niii) The commission the representative will earn on every alternative product considered but not recommended\niv) The financial products recommended, with an explanation of why they are likely to satisfy the client's needs",o:["(i), (iii) and (iv) only","(ii), (iii) and (iv) only","(i), (ii) and (iv) only","(i), (ii), (iii) and (iv)"],a:2,e:"The record of advice must reflect the information relied on, the products considered, and the products recommended with reasons. It does not require the commission on every product considered but not recommended."},
{taskNum:4,taskTitle:"Adhere to the Specific Codes of Conduct",t:"General Code of Conduct",lv:"C",q:"Which ONE of the following is NOT a required disclosure concerning a product supplier before a financial service is rendered?",o:["The name, physical location and contact details of the product supplier","Whether the provider holds more than 10% of the shares of the product supplier","Whether the provider received more than 30% of total remuneration from the product supplier during the preceding 12 months","The audited annual financial statements of the product supplier"],a:3,e:"There is no requirement to furnish the product supplier's audited financial statements. The shareholding and remuneration thresholds and the supplier's identifying particulars are all required disclosures."},
{taskNum:4,taskTitle:"Adhere to the Specific Codes of Conduct",t:"General Code of Conduct",lv:"AP",q:"An advertisement published by an FSP includes a performance figure for a financial product. In terms of the General Code of Conduct, the advertisement must, in relation to that figure:",o:["Include the source and the basis on which the performance is calculated","Include a projection of the equivalent performance over the following five years","Be approved in writing by the Authority before publication","Be accompanied by the audited financial statements of the product supplier"],a:0,e:"Where performance data is included, the source and basis of calculation must be provided. Prior approval by the Authority is not required, and the remaining options are not requirements."},
{taskNum:4,taskTitle:"Adhere to the Specific Codes of Conduct",t:"General Code of Conduct",lv:"C",q:"In terms of the General Code of Conduct, a complaint that is upheld and resolved by an FSP to the satisfaction of the complainant must be:",o:["Reported individually to the Ombud within 15 days","Recorded, together with the resolution, and the record maintained","Reported to the Authority in the annual compliance report by client name","Referred to the compliance officer for independent re-adjudication"],a:1,e:"Complaints and their resolution must be recorded and the records maintained. There is no obligation to report individually resolved complaints to the Ombud or to name clients to the Authority."},
{taskNum:4,taskTitle:"Adhere to the Specific Codes of Conduct",t:"General Code of Conduct",lv:"K",q:"The General Code of Conduct for Authorised Financial Services Providers and Representatives was published under:",o:["Board Notice 79 of 2003","Board Notice 81 of 2003","Board Notice 80 of 2003","Board Notice 82 of 2003"],a:2,e:"BN 80 of 2003 is the General Code. BN 79 covers Administrative and Discretionary FSPs, BN 81 the Ombud proceedings, and BN 82 reappointment of debarred representatives."},
{taskNum:4,taskTitle:"Adhere to the Specific Codes of Conduct",t:"General Code of Conduct",lv:"C",q:"A provider intends to terminate its agreement with a client. In terms of the General Code of Conduct relating to termination, the provider must:",o:["Obtain the prior written approval of the Authority to terminate","Refund all fees and commission received in the preceding year","Notify the Ombud of the termination within thirty days","Ensure outstanding business is promptly completed or transferred"],a:3,e:"On termination the provider must ensure outstanding business is promptly completed or transferred. The other options are not requirements of the Code."},
{taskNum:4,taskTitle:"Adhere to the Specific Codes of Conduct",t:"General Code of Conduct",lv:"AP",q:"A client lodges a complaint alleging that unsuitable advice was furnished. The FSP is unable to locate the record of advice. The MOST likely consequence in a subsequent determination is that the:",o:["Absence of the record will operate to the disadvantage of the FSP","Complaint will be dismissed for want of documentary proof by the complainant","Ombud will refer the matter to the Authority for a licensing decision","Matter will be referred to arbitration at the FSP's election"],a:0,e:"The obligation to maintain records rests on the FSP. Where the record cannot be produced, the FSP is unable to discharge its burden of showing that the advice was appropriate, which operates against it."},
{taskNum:4,taskTitle:"Adhere to the Specific Codes of Conduct",t:"General Code of Conduct",lv:"C",q:"Under the General Code of Conduct, a provider must disclose to a client the existence of any personal interest in a rendered service, or any circumstance that gives rise to an actual or potential conflict of interest, and must:",o:["Cease rendering the service until the conflict has been eliminated","Take reasonable steps to ensure fair treatment of the client","Obtain a written indemnity from the client before proceeding","Report the conflict to the Authority within 30 days"],a:1,e:"Disclosure must be accompanied by reasonable steps to ensure fair treatment. Cessation, indemnities and reporting to the Authority are not what the Code prescribes."},
{taskNum:5,taskTitle:"Comply with Regulated Record Keeping Requirements",t:"Record-Keeping Obligations",lv:"K",q:"In terms of section 18 of the FAIS Act, records must be kept for a period of:",o:["3 years","5 years","7 years","10 years"],a:1,e:"Section 18 prescribes a five-year retention period."},
{taskNum:5,taskTitle:"Comply with Regulated Record Keeping Requirements",t:"Record-Keeping Obligations",lv:"C",q:"Records required to be maintained under the FAIS Act may be kept:",o:["In printed form only, at the FSP's registered business premises","In electronic form only, where the FSP has an approved disaster recovery plan","In printed or electronic form, provided they are safe from destruction","At the offices of the FSP's compliance officer, in printed form only"],a:2,e:"Records may be kept in printed or electronic form provided they are safe from destruction and available for inspection."},
{taskNum:5,taskTitle:"Comply with Regulated Record Keeping Requirements",t:"Record-Keeping Obligations",lv:"C",q:"An FSP outsources its record keeping to a third party. In terms of the General Code of Conduct, the FSP:",o:["Transfers its record keeping obligations to the third party by written agreement","Is relieved of the obligation provided the third party is itself an authorised FSP","Must obtain the prior approval of the Authority for the outsourcing","Remains responsible for compliance with its record keeping obligations"],a:3,e:"Outsourcing does not transfer the statutory obligation. The FSP remains responsible for compliance regardless of the contractual arrangement."},
{taskNum:5,taskTitle:"Comply with Regulated Record Keeping Requirements",t:"Record-Keeping Obligations",lv:"C",q:"Which ONE of the following is NOT a record that a provider is required to maintain under section 18 of the FAIS Act?",o:["The personal financial statements of each representative","Known premature cancellations of transactions by clients","Complaints received, together with an indication of whether they were resolved","Cases of non-compliance with the Act, and the reasons for such non-compliance"],a:0,e:"Representatives' personal financial statements are not a prescribed record. Premature cancellations, complaints and instances of non-compliance are all expressly listed."},
{taskNum:6,taskTitle:"Comply with the Requirements of the FIC Act and Money Laundering Regulations",t:"FICA & AML",lv:"K",q:"In terms of the FIC Act, an accountable institution must keep records of a business relationship or single transaction for a period of at least five years from the date on which the:",o:["Business relationship commenced","Business relationship was terminated","First transaction was concluded","Client was first identified"],a:1,e:"Records relating to a business relationship are retained for five years from the date the relationship is terminated, which is a different starting point from the FAIS Act record."},
{taskNum:6,taskTitle:"Comply with the Requirements of the FIC Act and Money Laundering Regulations",t:"FICA & AML",lv:"C",q:"An accountable institution has filed a report in respect of a suspicious transaction. In terms of the FIC Act, the institution may:",o:["Inform the client that a report has been filed, provided it is done in writing","Inform the client only after 30 days have elapsed since filing","Not disclose to the client that a report has been or will be filed","Inform the client where the client makes a direct enquiry"],a:2,e:"Disclosing that a report has been or may be filed constitutes tipping off, which is an offence. There is no exception for written form, elapsed time or client enquiry."},
{taskNum:6,taskTitle:"Comply with the Requirements of the FIC Act and Money Laundering Regulations",t:"FICA & AML",lv:"K",q:"Under the FIC Act, the obligation to conduct customer due diligence arises:",o:["Only where a single transaction exceeds the prescribed threshold","Only where the client is a politically exposed person","Within thirty days after the business relationship is established","Before establishing a relationship or concluding a transaction"],a:3,e:"Customer due diligence must be conducted before establishing a business relationship or concluding a single transaction, not afterwards and not only above a threshold."},
{taskNum:6,taskTitle:"Comply with the Requirements of the FIC Act and Money Laundering Regulations",t:"FICA & AML",lv:"C",q:"An accountable institution is unable to establish and verify the identity of a prospective client. In terms of the FIC Act, the institution:",o:["May not establish the business relationship or conclude the transaction","May establish the relationship subject to enhanced ongoing monitoring","May proceed where a senior manager provides written authorisation","May proceed provided the transaction value is below the cash threshold"],a:0,e:"Where due diligence cannot be completed, the institution may not establish the relationship or conclude the transaction. Enhanced monitoring and internal authorisation are not substitutes for identification and verification."},
{taskNum:6,taskTitle:"Comply with the Requirements of the FIC Act and Money Laundering Regulations",t:"FICA & AML",lv:"AP",q:"A client instructs a representative to structure a single large cash deposit into several smaller deposits, each falling below the applicable cash threshold, in order to avoid a report being filed. The representative should recognise that this conduct:",o:["Is permissible, as each individual deposit falls below the threshold","Constitutes a ground for filing a suspicious transaction report","Requires the representative to refuse the instruction but creates no reporting duty","Must be reported to the Authority rather than the Financial Intelligence Centre"],a:1,e:"Deliberate structuring to avoid a reporting threshold is itself suspicious conduct giving rise to a reporting duty. Reports under the FIC Act are made to the Financial Intelligence Centre, not the Authority."},
{taskNum:7,taskTitle:"Dealing with Complaints Submitted to the Ombud for FSPs",t:"FAIS Ombud",lv:"K",q:"The Ombud for Financial Services Providers may make a determination in respect of a complaint where the amount claimed does not exceed:",o:["R800 000","R2 500 000","R3 500 000","There is no prescribed monetary limit"],a:2,e:"The prescribed limit is R3 500 000. R2 500 000 is a commonly used distractor."},
{taskNum:7,taskTitle:"Dealing with Complaints Submitted to the Ombud for FSPs",t:"FAIS Ombud",lv:"C",q:"A determination by the Ombud for Financial Services Providers against which no appeal has been noted:",o:["Is binding only if the FSP consents in writing","May be enforced only by way of fresh action in the High Court","Lapses if not acted upon within 12 months","Has the effect of a civil judgment of a court"],a:3,e:"An unappealed determination has the effect of a civil judgment and may be enforced accordingly."},
{taskNum:8,taskTitle:"Operate as a Representative in Terms of the FAIS Act",t:"Ethics & Professional Conduct",lv:"K",q:"A representative who does not meet the experience requirements may render financial services under supervision. The disclosure obligation in respect of that supervision requires that the client be informed:",o:["Before any financial service is rendered","On request by the client","Within 30 days after the service is rendered","Only where the product exceeds a prescribed value"],a:0,e:"Supervision status must be disclosed to the client before the financial service is rendered, and it is not dependent on a client enquiry."},
{taskNum:8,taskTitle:"Operate as a Representative in Terms of the FAIS Act",t:"Ethics & Professional Conduct",lv:"K",q:"The Fit and Proper requirements for FSPs, key individuals and representatives are determined in:",o:["Board Notice 80 of 2003","Board Notice 194 of 2017","Board Notice 123 of 2009","Board Notice 127 of 2010"],a:1,e:"BN 194 of 2017 determines the Fit and Proper requirements."},
{taskNum:8,taskTitle:"Operate as a Representative in Terms of the FAIS Act",t:"Ethics & Professional Conduct",lv:"C",q:"Which ONE of the following would NOT, on its own, disqualify a person from meeting the honesty, integrity and good standing requirements?",o:["A conviction for fraud for which the sentence included the option of a fine","A finding by a statutory professional body of dishonest conduct","A civil judgment for breach of a commercial supply contract","Removal from an office of trust on account of misconduct involving dishonesty"],a:2,e:"A civil judgment on a commercial contract, absent any element of dishonesty, does not bear on honesty and integrity. The remaining events all involve dishonesty or misconduct in a position of trust."},
{taskNum:8,taskTitle:"Operate as a Representative in Terms of the FAIS Act",t:"Ethics & Professional Conduct",lv:"C",q:"A representative is debarred by an FSP. In terms of the FAIS Act, the FSP must, within the prescribed period, notify the Authority of the debarment and must also:",o:["Publish the debarment in a national newspaper","Obtain the Authority's ratification before the debarment takes effect","Refer the debarment to the Ombud for confirmation","Provide the Authority with the grounds and reasons for the debarment"],a:3,e:"The FSP must furnish the Authority with the grounds and reasons for the debarment. The debarment is effected by the FSP and does not require ratification or referral."},
{taskNum:8,taskTitle:"Operate as a Representative in Terms of the FAIS Act",t:"Ethics & Professional Conduct",lv:"AP",q:"An FSP intends to debar a representative for a material contravention of the Act. Before effecting the debarment, the FSP must:",o:["Give notice of the intention to debar and an opportunity to respond","Obtain the prior written approval of the Authority for the proposed debarment","Refer the matter to the compliance officer for a binding determination","Suspend the representative for a minimum period of thirty days first"],a:0,e:"The debarment process requires prior notice and a reasonable opportunity for the representative to respond. Debarment is effected by the FSP without prior approval by the Authority."},
{taskNum:8,taskTitle:"Operate as a Representative in Terms of the FAIS Act",t:"Ethics & Professional Conduct",lv:"C",q:"Which ONE of the following statements about the register of representatives is NOT correct?",o:["The FSP must maintain a register of representatives and key individuals","The register is maintained by the Authority on behalf of the FSP","The register must be updated with details of representatives who no longer render services","The register must be available to the Authority for inspection"],a:1,e:"The FSP maintains the register; it is not maintained by the Authority on the FSP's behalf. The remaining statements are correct."},
{taskNum:8,taskTitle:"Operate as a Representative in Terms of the FAIS Act",t:"Ethics & Professional Conduct",lv:"AN",q:"A juristic representative is appointed by an authorised FSP. Which combination of the following is correct?\ni) A juristic person may be appointed as a representative\nii) A juristic person may be appointed as a key individual\niii) The FSP remains responsible for the conduct of the juristic representative\niv) The juristic representative must itself hold an FSP licence in order to act",o:["(i), (ii) and (iii) only","(ii) and (iv) only","(i) and (iii) only","(i), (iii) and (iv) only"],a:2,e:"A representative may be a juristic person, and the FSP remains responsible for its conduct. A key individual must be a natural person, and a representative acts under the FSP's licence rather than holding its own."},
{taskNum:8,taskTitle:"Operate as a Representative in Terms of the FAIS Act",t:"Ethics & Professional Conduct",lv:"AP",q:"A representative appointed on 1 March meets the honesty and integrity requirements but has not yet completed the regulatory examination. The representative renders services under supervision. If the representative fails to complete the regulatory examination within the prescribed period, the FSP must:",o:["Continue the supervision arrangement indefinitely until the examination is passed","Apply to the Authority for an extension of the prescribed period","Reduce the representative's scope to intermediary services only","Ensure the representative ceases rendering the affected financial services"],a:3,e:"Supervision is a transitional arrangement subject to the prescribed competence timelines. Where the requirement is not met within the period, the representative may no longer render the affected services."},
{taskNum:8,taskTitle:"Operate as a Representative in Terms of the FAIS Act",t:"Ethics & Professional Conduct",lv:"C",q:"A debarred representative seeks reappointment as a representative by another FSP. The appointing FSP must:",o:["Satisfy itself as to compliance and that the grounds are addressed","Wait until the Authority removes the debarment from the public register","Obtain the written consent of the FSP that effected the original debarment","Appoint the person under supervision for a minimum period of twelve months"],a:0,e:"The appointing FSP must be satisfied as to Fit and Proper compliance and that the grounds for the debarment have been dealt with. Consent from the debarring FSP is not required."},
{taskNum:8,taskTitle:"Operate as a Representative in Terms of the FAIS Act",t:"Ethics & Professional Conduct",lv:"C",q:"A representative who renders financial services in respect of a subcategory for which the FSP is not authorised:",o:["Commits no contravention provided the advice is factually correct","Renders an unauthorised financial service","May do so pending the FSP's application to add the subcategory","May do so if the client is informed in writing"],a:1,e:"Services may only be rendered within the categories and subcategories for which the FSP is authorised. Correctness of the advice, a pending application, or client notification do not authorise the service."},
{taskNum:8,taskTitle:"Operate as a Representative in Terms of the FAIS Act",t:"Ethics & Professional Conduct",lv:"C",q:"Continuous professional development requirements apply to a representative:",o:["From the date of first appointment, without qualification","Only where the representative renders advice in respect of Category II products","Only after the representative has met the applicable competence requirements","Only in the calendar year following the representative's fortieth birthday"],a:2,e:"CPD obligations attach once the applicable competence requirements have been met, which distinguishes the position of a representative from that of a key individual."},
{taskNum:8,taskTitle:"Operate as a Representative in Terms of the FAIS Act",t:"Ethics & Professional Conduct",lv:"AN",q:"An FSP discovers that a representative has, over several months, furnished advice in a subcategory outside the FSP's authorisation and has failed to record any of the advice given. Which combination of consequences MAY follow?\ni) Debarment of the representative by the FSP\nii) A finding that the FSP contravened its licensing conditions\niii) A finding that the FSP contravened its record keeping obligations\niv) Automatic lapsing of the FSP's licence by operation of law",o:["(i) and (iv) only","(ii), (iii) and (iv) only","(i), (ii), (iii) and (iv)","(i), (ii) and (iii) only"],a:3,e:"Debarment, a licensing condition contravention and a record keeping contravention may all follow. Lapsing occurs only on defined events such as liquidation and does not arise automatically from contraventions of this nature."}
]);
const BLUEPRINT_EXAM_RE1 = Object.freeze([
{taskNum:1,taskTitle:"Understand the FAIS Act as a Regulatory Framework",t:"Twin Peaks & Regulatory Framework",lv:"K",q:"The Prudential Authority is established within the:",o:["Financial Sector Conduct Authority","National Treasury","South African Reserve Bank","Financial Intelligence Centre"],a:2,e:"The Prudential Authority is established within the South African Reserve Bank under the Financial Sector Regulation Act."},
{taskNum:1,taskTitle:"Understand the FAIS Act as a Regulatory Framework",t:"Twin Peaks & Regulatory Framework",lv:"C",q:"Under the Twin Peaks model, responsibility for market conduct regulation of an authorised FSP vests in the:",o:["The Prudential Authority","The Ombud Council for financial services","The National Credit Regulator","The Financial Sector Conduct Authority"],a:3,e:"The FSCA is the market conduct regulator; the Prudential Authority addresses safety and soundness of financial institutions."},
{taskNum:2,taskTitle:"Define Financial Products and Financial Services",t:"Financial Products",lv:"C",q:"A product that provides for a benefit on the death of the life insured, with no investment component, falls within the definition of a:",o:["Long-term insurance product","Participatory interest in a collective investment scheme","Retail pension benefit","Deposit as defined in the Banks Act"],a:0,e:"A pure risk death benefit is a long-term insurance product. The remaining options describe different product categories under the Act."},
{taskNum:3,taskTitle:"Maintain the Licence of the FSP",t:"FSP Licensing",lv:"K",q:"An application for authorisation as an FSP is made in terms of section:",o:["7 of the FAIS Act","8 of the FAIS Act","13 of the FAIS Act","17 of the FAIS Act"],a:1,e:"Section 8 governs the application for authorisation. Section 7 prohibits unauthorised rendering of services, section 13 deals with representatives and section 17 with compliance officers."},
{taskNum:3,taskTitle:"Maintain the Licence of the FSP",t:"FSP Licensing",lv:"K",q:"Levies payable to the Financial Sector Conduct Authority are imposed under the:",o:["Financial Advisory and Intermediary Services Act, 2002","Financial Sector and Deposit Insurance Levies Act, 2022","Financial Sector Regulation Act, 2017","Financial Intelligence Centre Act, 2001"],a:1,e:"Levies are imposed under the Financial Sector and Deposit Insurance Levies Act 11 of 2022."},
{taskNum:3,taskTitle:"Maintain the Licence of the FSP",t:"FSP Licensing",lv:"C",q:"Which ONE of the following statements regarding the suspension of an FSP licence is NOT correct?",o:["A suspension may be lifted where the FSP remedies the underlying non-compliance","A suspension is imposed by the Authority","A suspension operates automatically upon the FSP's liquidation","The FSP has recourse against a suspension decision"],a:2,e:"Liquidation causes the licence to lapse by operation of law; it does not give rise to a suspension, which is a decision of the Authority."},
{taskNum:3,taskTitle:"Maintain the Licence of the FSP",t:"FSP Licensing",lv:"AP",q:"A key individual resigns, leaving the FSP without any approved key individual. The FSP continues to render financial services. The MOST accurate statement of the position is that the:",o:["FSP may continue indefinitely provided a compliance officer is in office","Licence lapses automatically on the date the resignation takes effect","Compliance officer may act as key individual until a replacement is approved","FSP fails a requirement for authorisation and must fill the vacancy in time"],a:3,e:"The absence of a key individual is a failure to meet an authorisation requirement, which must be remedied within the prescribed 60-day period. A compliance officer cannot substitute for a key individual."},
{taskNum:3,taskTitle:"Maintain the Licence of the FSP",t:"FSP Licensing",lv:"AP",q:"An FSP wishes to extend its licence to include an additional subcategory. The Authority will consider the application on the basis that the:",o:["FSP and its officers meet the Fit and Proper requirements for that subcategory","Commercial rationale advanced for the proposed extension is compelling","FSP has operated without any recorded contravention for at least three years","Extension will not prejudice competing FSPs operating in the same market"],a:0,e:"The determinative consideration is compliance with the Fit and Proper requirements for the subcategory applied for. Commercial factors are not a basis for the decision."},
{taskNum:3,taskTitle:"Maintain the Licence of the FSP",t:"FSP Licensing",lv:"C",q:"Reparation measures available to the Authority where an FSP persists with a declared undesirable practice include an order that the FSP:",o:["Be wound up by order of the High Court","Rectify the position of affected clients","Pay an administrative penalty to the Ombud Council","Transfer its client book to another authorised FSP"],a:1,e:"The Authority may direct rectification in respect of affected clients. The remaining options are not reparation measures under the section."},
{taskNum:3,taskTitle:"Maintain the Licence of the FSP",t:"FSP Licensing",lv:"AN",q:"Consider the following, each arising on the same day. Arrange them from the SHORTEST to the LONGEST applicable period and select the correct option.\ni) Notification to the Authority of a completed debarment (first notification)\nii) Submission of the annual compliance report after financial year end\niii) Filling of a vacancy in the office of key individual\niv) Retention of records under section 18 of the FAIS Act",o:["(iii) then (i) then (ii) then (iv)","(i) then (ii) then (iii) then (iv)","(i) then (iii) then (ii) then (iv)","(ii) then (i) then (iii) then (iv)"],a:2,e:"First debarment notification is 5 days, a key individual vacancy 60 days, the compliance report 4 months, and record retention 5 years. The ascending order is (i), (iii), (ii), (iv)."},
{taskNum:3,taskTitle:"Maintain the Licence of the FSP",t:"FSP Licensing",lv:"C",q:"An FSP's accreditation under the Medical Schemes Act is withdrawn. In respect of the FSP's FAIS authorisation, this:",o:["Has no effect, the two regulatory regimes operating independently","Causes the FAIS licence to lapse in its entirety immediately","Automatically suspends all of the FSP's licensed subcategories","May affect authorisation for the financial services concerned"],a:3,e:"Withdrawal of a related accreditation may affect authorisation in respect of the services to which the accreditation relates, without necessarily affecting the whole licence."},
{taskNum:3,taskTitle:"Maintain the Licence of the FSP",t:"FSP Licensing",lv:"K",q:"A directive issued by the Authority under section 38C of the FAIS Act:",o:["Is binding, and non-compliance carries legal consequences","Is advisory and carries no legal consequence for non-compliance","Takes effect only once confirmed by the Ombud Council","Applies only to FSPs holding Category II authorisation"],a:0,e:"A directive is binding and non-compliance carries legal consequences, which distinguishes it from a guidance notice."},
{taskNum:3,taskTitle:"Maintain the Licence of the FSP",t:"FSP Licensing",lv:"AP",q:"During an on-site visit, the Authority requests documents relating to a client's record of advice. The FSP's key individual considers the request unreasonable. The key individual must:",o:["Decline until the Authority provides written reasons for the request","Provide the documents, as the Authority is empowered to conduct inspections","Refer the request to the FSP's attorneys for an opinion before responding","Provide only the documents the key individual considers relevant"],a:1,e:"The Authority is empowered to conduct inspections and require production of records. The key individual is not entitled to filter or refuse the request on grounds of perceived unreasonableness."},
{taskNum:4,taskTitle:"Operate as a Key Individual in Terms of the FAIS Act",t:"KI Accountability",lv:"K",q:"A key individual is a person who renders management or oversight services in respect of the rendering of financial services and who must, in terms of the FAIS Act, be:",o:["Either a natural or a juristic person","A shareholder or member of the FSP","A natural person — not a juristic entity","An employee of the FSP for at least 12 months"],a:2,e:"Only a natural person may be a key individual. There is no shareholding or minimum employment requirement."},
{taskNum:4,taskTitle:"Operate as a Key Individual in Terms of the FAIS Act",t:"KI Accountability",lv:"K",q:"Approval of a key individual by the Authority is dealt with in:",o:["Board Notice 82 of 2003","Board Notice 127 of 2010","Board Notice 123 of 2009","Board Notice 122 of 2003"],a:3,e:"BN 122 of 2003 determines the procedure for approval of key individuals."},
{taskNum:4,taskTitle:"Operate as a Key Individual in Terms of the FAIS Act",t:"KI Accountability",lv:"C",q:"The financial soundness requirements under Board Notice 194 of 2017 apply to:",o:["FSPs and representatives, but not key individuals","FSPs and key individuals, but not representatives","FSPs, key individuals and representatives equally","Key individuals and representatives, but not FSPs"],a:0,e:"Financial soundness applies to FSPs and to representatives. Key individuals are not subject to the requirement, which is among the most frequently tested distinctions in RE1."},
{taskNum:4,taskTitle:"Operate as a Key Individual in Terms of the FAIS Act",t:"KI Accountability",lv:"AP",q:"A key individual delegates the monitoring of representatives' continuous professional development to the compliance officer. Records later reveal several representatives have not complied. The key individual:",o:["Is absolved, the function having been validly delegated in writing","Remains accountable for the oversight obligation notwithstanding","Shares accountability equally with the appointed compliance officer","Is accountable only if the delegation was not recorded in writing"],a:1,e:"The oversight obligation attaches to the key individual. Operational execution may be delegated, but statutory accountability may not."},
{taskNum:4,taskTitle:"Operate as a Key Individual in Terms of the FAIS Act",t:"KI Accountability",lv:"AP",q:"A key individual becomes aware of a contravention that has not yet come to the attention of the Authority. Reporting the matter to the Authority on the FSP's own initiative:",o:["Is prohibited until the FSP's internal investigation is complete","Constitutes an admission that precludes the FSP from disputing the contravention","Is a step consistent with the key individual's management and oversight obligations","Is required only where the contravention has caused client loss"],a:2,e:"Proactive disclosure is consistent with the key individual's obligations. It is neither prohibited nor an irrevocable admission, and is not confined to cases involving client loss."},
{taskNum:4,taskTitle:"Operate as a Key Individual in Terms of the FAIS Act",t:"KI Accountability",lv:"C",q:"Continuous professional development obligations for a key individual commence:",o:["After a qualifying period of 24 months from first appointment","In the calendar year following approval by the Authority","Only once the key individual has passed the regulatory examination","On the date of first appointment as a key individual"],a:3,e:"A key individual's CPD obligation attaches from appointment, without the qualifying period that applies to a representative who has not yet met the competence requirements."},
{taskNum:4,taskTitle:"Operate as a Key Individual in Terms of the FAIS Act",t:"KI Accountability",lv:"AN",q:"A key individual holds that position at an FSP and is simultaneously appointed as a representative of the same FSP. Which combination of the following is correct?\ni) The dual appointment is permissible under the FAIS Act\nii) The person must meet the Fit and Proper requirements applicable to each role\niii) The dual appointment requires an exemption from the Authority\niv) The FSP remains responsible for the person's conduct as a representative",o:["(i), (ii) and (iv) only","(i) and (iii) only","(ii), (iii) and (iv) only","(i), (ii), (iii) and (iv)"],a:0,e:"Dual appointment is permitted and requires no exemption. The person must satisfy the requirements for each role, and the FSP remains responsible for the conduct of its representative."},
{taskNum:4,taskTitle:"Operate as a Key Individual in Terms of the FAIS Act",t:"KI Accountability",lv:"C",q:"Which ONE of the following is NOT a ground on which a person may fail the honesty, integrity and good standing requirements?",o:["Having been found guilty of an offence involving dishonesty","Having been sequestrated after an unrelated business failure","Having been refused entry to a profession on grounds of character","Having been dismissed from a position of trust for dishonest conduct"],a:1,e:"Sequestration bears on financial soundness rather than honesty and integrity, and absent dishonesty it does not of itself fail the honesty and integrity requirement."},
{taskNum:4,taskTitle:"Operate as a Key Individual in Terms of the FAIS Act",t:"KI Accountability",lv:"AN",q:"A key individual is approved for Category I only. The FSP obtains Category II authorisation and the key individual begins overseeing discretionary investment management. The position is that the key individual:",o:["May do so, approval as a key individual not being category specific","May do so for a transitional period of six months only","Must be approved for the additional category before exercising oversight","May do so provided a Category II representative reports to the key individual"],a:2,e:"Key individual approval is category and subcategory specific. Oversight may not be exercised in a category for which approval has not been obtained."},
{taskNum:4,taskTitle:"Operate as a Key Individual in Terms of the FAIS Act",t:"KI Accountability",lv:"C",q:"Where a key individual no longer meets the honesty, integrity and good standing requirements, the FSP must:",o:["Await a determination by the Authority before taking any step","Refer the matter to the Ombud for Financial Services Providers","Suspend the FSP's own licence pending final resolution","Take appropriate action, including removal from the role"],a:3,e:"The FSP must take appropriate action. It does not await a determination, and neither referral to the Ombud nor self-suspension are mechanisms provided for."},
{taskNum:4,taskTitle:"Operate as a Key Individual in Terms of the FAIS Act",t:"KI Accountability",lv:"AP",q:"An FSP has one key individual who oversees three subcategories. The key individual is absent for an extended period without any alternative arrangement. In respect of the affected services, the FSP:",o:["May be unable to demonstrate the required oversight of those services","May continue, the key individual remaining formally appointed","Must immediately surrender its licence to the Authority in writing","May appoint the compliance officer as acting key individual without approval"],a:0,e:"Formal appointment is not sufficient; the oversight function must actually be exercised. A compliance officer cannot act as key individual without approval."},
{taskNum:4,taskTitle:"Operate as a Key Individual in Terms of the FAIS Act",t:"KI Accountability",lv:"K",q:"The obligation of a key individual to manage and oversee the activities of the FSP relating to the rendering of financial services derives from:",o:["Section 13 of the FAIS Act","Section 8(4) of the FAIS Act","Section 20 of the FAIS Act","Section 27 of the FAIS Act"],a:1,e:"Section 8(4) addresses the position of key individuals in relation to the FSP's authorisation. Section 13 concerns representatives, and sections 20 and 27 the Ombud."},
{taskNum:5,taskTitle:"Manage and Oversee the FSP's Operational Ability",t:"Operational Ability",lv:"K",q:"Requirements relating to operational ability, including storage and filing systems, are contained in:",o:["Chapter 2 of Board Notice 194 of 2017","Board Notice 81 of 2003","Chapter 5 of Board Notice 194 of 2017","Board Notice 122 of 2003"],a:2,e:"Operational ability requirements appear in Chapter 5 of BN 194 of 2017. Chapter 2 addresses honesty and integrity."},
{taskNum:5,taskTitle:"Manage and Oversee the FSP's Operational Ability",t:"Operational Ability",lv:"C",q:"An FSP outsources a system function relating to the rendering of financial services to a third party that itself renders financial services. In terms of Board Notice 194 of 2017, the third party must:",o:["Provide a written indemnity in favour of the FSP","Be approved by the Authority as a compliance practice","Hold professional indemnity cover equal to that of the FSP","Be an authorised financial services provider"],a:3,e:"Where an outsourced entity renders financial services it must itself be an authorised FSP. An indemnity does not satisfy the requirement."},
{taskNum:5,taskTitle:"Manage and Oversee the FSP's Operational Ability",t:"Operational Ability",lv:"C",q:"A written service level agreement is required in respect of outsourced functions principally in order to:",o:["Record the arrangements by which the FSP retains oversight of the function","Transfer the FSP's regulatory obligations to the outsourced provider","Satisfy the Authority's prior approval requirement for outsourcing","Limit the FSP's liability to clients for the outsourced function"],a:0,e:"The agreement records the arrangements enabling continued oversight. It does not transfer regulatory obligations or limit liability to clients."},
{taskNum:5,taskTitle:"Manage and Oversee the FSP's Operational Ability",t:"Operational Ability",lv:"C",q:"Business continuity arrangements required of an FSP must address:",o:["Information technology systems and data backup arrangements only","Continued functioning of the business, including key person reliance","Physical business premises and operating equipment only","Only those functions that have been outsourced to third parties"],a:1,e:"Continuity planning extends to the continued functioning of the business as a whole, including key person dependency, and is not confined to systems or premises."},
{taskNum:5,taskTitle:"Manage and Oversee the FSP's Operational Ability",t:"Operational Ability",lv:"AP",q:"An FSP holds client funds. Its bank account arrangements are reviewed by the compliance officer, who finds client premiums are deposited into the general business account and transferred within 48 hours. The finding should be that the FSP:",o:["Complies, as the funds are transferred within a reasonable period","Complies, provided the account is reconciled on a monthly basis","Does not comply, a separate designated account being required","Does not comply only where a client suffers loss as a result"],a:2,e:"Client funds must be deposited into a separate designated account with a registered bank. There is no de minimis or short-duration exception."},
{taskNum:5,taskTitle:"Manage and Oversee the FSP's Operational Ability",t:"Operational Ability",lv:"K",q:"The requirement that an FSP maintain guarantees, professional indemnity or fidelity insurance cover is prescribed in:",o:["Board Notice 127 of 2010","Board Notice 80 of 2003","Board Notice 82 of 2003","Board Notice 123 of 2009"],a:3,e:"BN 123 of 2009 prescribes these requirements."},
{taskNum:6,taskTitle:"Adhere to the Specific Codes of Conduct",t:"Governance & COI",lv:"K",q:"A conflict of interest management policy must, in terms of the General Code of Conduct, be:",o:["Adopted and made available to employees and representatives","Approved by the Authority in writing before implementation","Filed with the Ombud Council on an annual basis","Published in full on the provider's public website"],a:0,e:"The policy must be adopted and made available to employees and representatives. Authority approval, filing and publication are not requirements."},
{taskNum:6,taskTitle:"Adhere to the Specific Codes of Conduct",t:"Governance & COI",lv:"C",q:"The General Code of Conduct requires a provider's conflict of interest management policy to be reviewed:",o:["Every 3 months","Only if a material conflict arises","Every 24 months","At least every 12 months"],a:3,e:"The policy must be reviewed at intervals not exceeding 12 months."},
{taskNum:6,taskTitle:"Adhere to the Specific Codes of Conduct",t:"Governance & COI",lv:"AP",q:"A director of an FSP holds a material personal interest in a matter before the board. Having disclosed the interest, the director:",o:["May participate in the discussion and vote, the disclosure being sufficient","May vote but not participate in the discussion","Should recuse himself from consideration of and voting on the matter","May participate provided the disclosure is recorded in the minutes"],a:2,e:"Disclosure is the first step; sound governance requires the conflicted director to recuse himself from the affected decision rather than merely declare the interest."},
{taskNum:6,taskTitle:"Adhere to the Specific Codes of Conduct",t:"Governance & COI",lv:"C",q:"King IV operates on the basis of:",o:["Comply or explain","Comply or else","Adopt or disclose","Apply and explain"],a:3,e:"King IV applies an 'apply and explain' regime, which replaced the 'comply or explain' approach of King III."},
{taskNum:6,taskTitle:"Adhere to the Specific Codes of Conduct",t:"Governance & COI",lv:"AP",q:"An FSP's advertisement states that a product 'guarantees' a specified return, where the return is in fact dependent on market performance. In terms of the General Code of Conduct this is objectionable because the advertisement:",o:["Is misleading as to a material characteristic of the product","Was not approved by the Authority before publication","Did not disclose the identity of the product supplier","Exceeded the prescribed length for product advertisements"],a:0,e:"An advertisement may not create a false or misleading impression as to a material characteristic. Prior approval is not required, and the other options are not the objection."},
{taskNum:6,taskTitle:"Adhere to the Specific Codes of Conduct",t:"Governance & COI",lv:"K",q:"Requirements relating to the custody of client financial products and funds are contained in:",o:["Section 8 of the General Code of Conduct","Section 10 of the General Code of Conduct","Section 14 of the General Code of Conduct","Section 20 of the General Code of Conduct"],a:1,e:"Section 10 of the General Code deals with custody of financial products and funds. Section 8 addresses the process of advice, section 14 advertising and section 20 termination."},
{taskNum:6,taskTitle:"Adhere to the Specific Codes of Conduct",t:"Governance & COI",lv:"C",q:"Which ONE of the following statements about direct marketing under the General Code of Conduct is NOT correct?",o:["The direct marketer must furnish prescribed information to the client","A record of the telephone discussion must be maintained where the service is rendered by telephone","Direct marketing is prohibited in respect of long-term insurance products","The client must be given the opportunity to obtain the prescribed information in writing"],a:2,e:"Direct marketing is not prohibited in respect of long-term insurance products; it is regulated. The remaining statements reflect genuine requirements."},
{taskNum:6,taskTitle:"Adhere to the Specific Codes of Conduct",t:"Governance & COI",lv:"AP",q:"A complaint is received by an FSP and is not resolved within the period stipulated in the FSP's own complaints procedure. The complainant may:",o:["Only await the FSP's response, no other remedy being available","Refer the complaint directly to the High Court for adjudication","Require the Authority to suspend the FSP's licence pending resolution","Refer the complaint to the Ombud for Financial Services Providers"],a:3,e:"Where the FSP fails to resolve the complaint within its stipulated period, the complainant may refer the matter to the Ombud."},
{taskNum:6,taskTitle:"Adhere to the Specific Codes of Conduct",t:"Governance & COI",lv:"C",q:"On termination of an agreement with a client, the General Code of Conduct requires a provider to:",o:["Ensure outstanding business is promptly completed or transferred","Refund all fees and commission received in the preceding twelve months","Obtain the written consent of the Authority to the termination","Notify the Ombud Council of the termination within thirty days"],a:0,e:"Outstanding business must be promptly completed or transferred so that the client is not prejudiced."},
{taskNum:6,taskTitle:"Adhere to the Specific Codes of Conduct",t:"Governance & COI",lv:"AN",q:"Which combination of the following correctly reflects requirements of the General Code of Conduct?\ni) A provider must render financial services honestly, fairly, with due skill, care and diligence\nii) A provider must at all times act in the interests of clients and the integrity of the financial services industry\niii) A provider may accept an unlimited financial interest from a product supplier if it is disclosed\niv) A provider must have appropriate procedures to avoid or mitigate conflicts of interest",o:["(i), (iii) and (iv) only","(i), (ii) and (iv) only","(ii) and (iii) only","(i), (ii), (iii) and (iv)"],a:1,e:"Disclosure does not permit acceptance of an unlimited financial interest; the immaterial financial interest threshold applies. The remaining statements are core duties under the Code."},
{taskNum:7,taskTitle:"Manage and Oversee the Compliance Functions",t:"Compliance Function",lv:"K",q:"A compliance officer must be approved by the:",o:["Board of directors of the FSP","Ombud Council","Financial Sector Conduct Authority","Compliance Institute of South Africa"],a:2,e:"Approval of a compliance officer is by the Authority."},
{taskNum:7,taskTitle:"Manage and Oversee the Compliance Functions",t:"Compliance Function",lv:"K",q:"Qualifications, experience and criteria for approval of compliance officers are set out in:",o:["Board Notice 123 of 2009","Board Notice 194 of 2017","Board Notice 80 of 2003","Board Notice 127 of 2010"],a:3,e:"BN 127 of 2010 deals with the approval of compliance officers."},
{taskNum:7,taskTitle:"Manage and Oversee the Compliance Functions",t:"Compliance Function",lv:"C",q:"An FSP must appoint a compliance officer where the FSP has:",o:["More than one key individual or one representative","Any representatives at all, regardless of number","Annual revenue exceeding a prescribed monetary threshold","Held its licence for a period of more than three years"],a:0,e:"The obligation arises by reference to the number of key individuals and representatives, not revenue or licence tenure."},
{taskNum:7,taskTitle:"Manage and Oversee the Compliance Functions",t:"Compliance Function",lv:"AP",q:"An FSP appoints a suitably qualified compliance officer and permits her to sign off compliance reports while the application for approval is still pending. The position is that the:",o:["Appointment is effective, the approval being a mere formality","Person may not perform the function until approval is granted","Reports are valid provided the key individual countersigns them","Appointment is effective for ninety days pending approval"],a:1,e:"The function may not be performed until approval has been granted, irrespective of the person's qualifications."},
{taskNum:7,taskTitle:"Manage and Oversee the Compliance Functions",t:"Compliance Function",lv:"AP",q:"A compliance officer reports to, and has her performance assessed by, the head of the FSP's sales function. The principal regulatory concern is that the compliance officer:",o:["May be remunerated above the prescribed maximum","Will be unable to meet the continuous professional development requirements","Cannot function objectively and independently of the operations she monitors","Must be approved separately for each subcategory"],a:2,e:"The compliance officer must be able to function objectively and independently of the FSP's management and the operations being monitored."},
{taskNum:7,taskTitle:"Manage and Oversee the Compliance Functions",t:"Compliance Function",lv:"C",q:"Where a compliance officer identifies non-compliance in a compliance report, the key individual must:",o:["Record the finding and address it in the following annual report","Refer the finding to the Authority for a determination","Suspend the affected representatives pending investigation","Ensure the non-compliance is addressed and rectified"],a:3,e:"Identified non-compliance must be addressed and rectified; recording alone is insufficient."},
{taskNum:7,taskTitle:"Manage and Oversee the Compliance Functions",t:"Compliance Function",lv:"C",q:"A key individual signs off a compliance report without having considered its contents. The signature:",o:["Does not discharge the obligation, which requires understanding of the content","Discharges the key individual's obligation, being a formal requirement only","Is invalid unless also signed by the compliance officer","Must be witnessed by an independent third party"],a:0,e:"The obligation requires the key individual to understand the content in order to sign it off responsibly."},
{taskNum:7,taskTitle:"Manage and Oversee the Compliance Functions",t:"Compliance Function",lv:"AP",q:"A small FSP is not obliged to appoint a compliance officer. In respect of conflicts of interest, the FSP:",o:["Is exempt from the conflict of interest requirements of the General Code","Must still manage conflicts of interest in terms of the General Code","Must apply to the Authority for a determination of its obligations","Must appoint an external compliance practice for that purpose only"],a:1,e:"The conflict of interest obligations under the General Code apply irrespective of whether a compliance officer is appointed."},
{taskNum:7,taskTitle:"Manage and Oversee the Compliance Functions",t:"Compliance Function",lv:"C",q:"Where a compliance officer does not hold, or ceases to hold, the required approval, the FSP must:",o:["Continue with the appointment pending a fresh application","Notify the Ombud Council within 30 days","Replace the compliance officer","Suspend the rendering of all financial services"],a:2,e:"The FSP must replace a compliance officer who does not hold the required approval."},
{taskNum:8,taskTitle:"Comply with Regulated Record Keeping Requirements",t:"Record Keeping",lv:"K",q:"Records required under section 18 of the FAIS Act must be retained for a period of:",o:["3 years","5 years","7 years","10 years"],a:1,e:"Section 18 prescribes five years."},
{taskNum:8,taskTitle:"Comply with Regulated Record Keeping Requirements",t:"Record Keeping",lv:"AP",q:"A client relationship of eleven years is terminated. In respect of records kept under the FIC Act, the accountable institution must retain those records for:",o:["Five years from the date the relationship commenced","Eleven years from the date the relationship commenced","Five years only where the client so requests","Five years from the date the relationship was terminated"],a:3,e:"FIC Act records relating to a business relationship run for five years from termination, which is a different starting point from the FAIS Act record."},
{taskNum:8,taskTitle:"Comply with Regulated Record Keeping Requirements",t:"Record Keeping",lv:"C",q:"Which ONE of the following is NOT correct in relation to record keeping obligations?",o:["Security and access requirements fall outside these obligations","Records may be kept in either printed or electronic form","Records must be kept safe from destruction or loss","Records must be available for inspection by the Authority"],a:0,e:"Security and controlled access form part of the record keeping obligations, having regard also to the Protection of Personal Information Act."},
{taskNum:9,taskTitle:"Accounting and Auditing Oversight",t:"Accounting & Auditing",lv:"K",q:"The accounting and auditing requirements applicable to an FSP are prescribed in:",o:["Section 17 of the FAIS Act","Section 19 of the FAIS Act","Section 18 of the FAIS Act","Section 20 of the FAIS Act"],a:1,e:"Section 19 deals with accounting and auditing requirements. Section 17 addresses compliance officers and section 18 record keeping."},
{taskNum:9,taskTitle:"Accounting and Auditing Oversight",t:"Accounting & Auditing",lv:"C",q:"An FSP that receives or holds client funds must submit to the Authority:",o:["Management accounts prepared on a quarterly basis","A solvency certificate signed by the appointed key individual","An audit report complying with prescribed requirements","A reconciliation of client accounts on a monthly basis"],a:2,e:"An audit report meeting the prescribed requirements must be submitted where client funds are received or held."},
{taskNum:9,taskTitle:"Accounting and Auditing Oversight",t:"Accounting & Auditing",lv:"C",q:"An FSP that does not hold client funds appoints an accounting officer rather than an auditor. The key individual must satisfy himself that the:",o:["Accounting officer is a member of the FSP's board of directors","Accounting officer is also an approved compliance officer","Appointment has been approved in writing by the Ombud Council","Appointment complies with the prescribed requirements"],a:3,e:"The key individual must ensure the appointment complies with the prescribed requirements for auditors or accounting officers."},
{taskNum:10,taskTitle:"FIC Act, Money Laundering & Terrorist Financing Oversight",t:"FICA Oversight",lv:"K",q:"A Risk Management and Compliance Programme is required by:",o:["Section 42 of the FIC Act","Section 29 of the FIC Act","Section 21 of the FIC Act","Section 18 of the FAIS Act"],a:0,e:"Section 42 of the FIC Act requires an accountable institution to develop and maintain a Risk Management and Compliance Programme."},
{taskNum:10,taskTitle:"FIC Act, Money Laundering & Terrorist Financing Oversight",t:"FICA Oversight",lv:"C",q:"In terms of the FIC Act, an accountable institution must provide training to its employees in respect of:",o:["The FAIS Act and the General Code of Conduct for providers","The FIC Act and the institution's compliance programme","Product knowledge for each subcategory of authorisation held","The Protection of Personal Information Act and related codes"],a:1,e:"Training must address compliance with the FIC Act and the institution's own Risk Management and Compliance Programme."},
{taskNum:10,taskTitle:"FIC Act, Money Laundering & Terrorist Financing Oversight",t:"FICA Oversight",lv:"AN",q:"A key individual assumes that because the FSP consistently obtains a copy of each client's identity document, the FSP complies with the FIC Act. The assumption is incorrect principally because the FIC Act also requires:",o:["Advance approval of each new client by the Authority","The identity document to be certified by a commissioner of oaths","Ongoing due diligence, a compliance programme, training and reporting","Retention of the identity document for ten years"],a:2,e:"Identification is only one element. The Act also requires ongoing due diligence, a compliance programme, employee training and reporting obligations."},
{taskNum:10,taskTitle:"FIC Act, Money Laundering & Terrorist Financing Oversight",t:"FICA Oversight",lv:"K",q:"Reports required under the FIC Act are submitted to the:",o:["Financial Sector Conduct Authority","Prudential Authority","South African Police Service","Financial Intelligence Centre"],a:3,e:"Reports under the FIC Act are made to the Financial Intelligence Centre."},
{taskNum:10,taskTitle:"FIC Act, Money Laundering & Terrorist Financing Oversight",t:"FICA Oversight",lv:"C",q:"An FSP outsources its FICA record keeping to a third party administrator. In respect of its FIC Act obligations, the FSP:",o:["Remains responsible for compliance with those obligations","Is relieved of those obligations by the outsourcing agreement","Must obtain the Authority's approval for the outsourcing","Must retain duplicate records in printed form at its premises"],a:0,e:"Responsibility for compliance is not transferred by outsourcing; the agreement must simply be properly executed."},
{taskNum:11,taskTitle:"Deal with Ombud Complaints",t:"Ombud Complaints",lv:"K",q:"The Ombud for Financial Services Providers may not investigate a complaint where the amount claimed exceeds:",o:["R800 000","R2 500 000","R3 500 000","R5 000 000"],a:2,e:"The prescribed limit is R3 500 000."},
{taskNum:11,taskTitle:"Deal with Ombud Complaints",t:"Ombud Complaints",lv:"C",q:"An FSP that receives a request for information from the Ombud in respect of a complaint must:",o:["Respond only if the complaint has merit in the FSP's assessment","Co-operate with the Ombud and furnish the information requested","Refer the request to its compliance officer for a decision on disclosure","Await service of a formal summons before responding"],a:1,e:"The FSP is obliged to co-operate with the Ombud in an investigation."},
{taskNum:11,taskTitle:"Deal with Ombud Complaints",t:"Ombud Complaints",lv:"AN",q:"An FSP has resolved eighteen separate complaints, each concerning the same representative and the same product, by individual goodwill payments. The key individual should recognise that:",o:["The matter is closed, each complaint having been individually resolved","The complaints must each be reported individually to the Ombud","The pattern indicates a systemic issue requiring wider investigation","Goodwill payments preclude any further regulatory consequence"],a:2,e:"A recurring pattern points to a systemic issue affecting clients who have not complained, which the key individual must investigate."},
{taskNum:11,taskTitle:"Deal with Ombud Complaints",t:"Ombud Complaints",lv:"C",q:"Which ONE of the following statements about a determination of the Ombud is NOT correct?",o:["An unappealed determination has the effect of a civil judgment","The Ombud may make a determination as to costs","A party may apply for leave to appeal against a determination","A determination is binding only if accepted in writing by the FSP"],a:3,e:"A determination does not depend on the FSP's written acceptance. The remaining statements are correct."},
{taskNum:12,taskTitle:"Define the Role of the Representative",t:"Representative Role",lv:"K",q:"The register of representatives must be maintained by the:",o:["The relevant FSP","The Authority","The compliance officer's practice","The Ombud Council"],a:0,e:"Section 13 requires the FSP to maintain the register."},
{taskNum:12,taskTitle:"Define the Role of the Representative",t:"Representative Role",lv:"C",q:"The register of representatives must reflect:",o:["Only those representatives currently rendering financial services","Representatives, including those who no longer render services","Only those representatives who are natural persons under the Act","Only those representatives operating under formal supervision"],a:1,e:"The register must be updated to reflect representatives who no longer render services, not merely those currently active."},
{taskNum:12,taskTitle:"Define the Role of the Representative",t:"Representative Role",lv:"AP",q:"During an inspection the Authority finds an FSP's register has not been updated for eight months, listing two departed representatives and omitting one recently appointed. No client has suffered loss. The finding is that the FSP:",o:["Has not contravened the Act, no client having suffered loss","Must simply correct the register before the inspection concludes","Has failed to comply with its obligation to maintain the register","Is in contravention only in respect of the omitted representative"],a:2,e:"The obligation to maintain an accurate register is freestanding and does not depend on client loss."},
{taskNum:12,taskTitle:"Define the Role of the Representative",t:"Representative Role",lv:"C",q:"A juristic person may be appointed as a representative of an FSP:",o:["Under no circumstances whatsoever, the Act prohibiting it","Only where it holds an FSP licence in its own name","Only with the prior written approval of the Ombud Council","Provided the applicable Fit and Proper requirements are met"],a:3,e:"A representative may be a juristic person subject to the applicable Fit and Proper requirements, unlike a key individual."},
{taskNum:12,taskTitle:"Define the Role of the Representative",t:"Representative Role",lv:"K",q:"A representative renders financial services:",o:["Under the authorisation of the FSP","Under a licence issued to the representative","Under the supervision of the Authority","Under a mandate registered with the Ombud Council"],a:0,e:"A representative acts under the authorisation of the FSP, which holds the licence."},
{taskNum:12,taskTitle:"Define the Role of the Representative",t:"Representative Role",lv:"AP",q:"A representative of an FSP also conducts an unrelated retail business in his personal capacity. In terms of the FAIS Act this:",o:["Disqualifies the person from continuing to act as a representative","Is not prohibited, provided the applicable requirements are still met","Requires the prior written approval of the Authority to continue","Requires the FSP to place the representative under formal supervision"],a:1,e:"The Act does not prohibit unrelated business interests, provided the person continues to meet the requirements applicable to a representative."},
{taskNum:13,taskTitle:"Manage the Appointment of Representatives",t:"Appointment of Representatives",lv:"C",q:"Before appointing a representative, an FSP must:",o:["Obtain the Authority's prior written approval of the appointment","Place the person under supervision for a minimum of six months","Satisfy itself that the applicable requirements are met","Obtain a clearance certificate from the Ombud Council first"],a:2,e:"The FSP must satisfy itself as to Fit and Proper compliance. Appointments are not individually pre-approved by the Authority."},
{taskNum:13,taskTitle:"Manage the Appointment of Representatives",t:"Appointment of Representatives",lv:"AP",q:"An FSP wishes to appoint a candidate urgently to service a significant new client. Verification of the candidate's Fit and Proper status is incomplete. The FSP:",o:["May appoint and complete verification within 30 days","May appoint subject to supervision by a key individual","May appoint if the candidate warrants compliance in writing","May not appoint until it is satisfied the requirements are met"],a:3,e:"Commercial urgency does not displace the requirement that the FSP be satisfied as to Fit and Proper compliance before appointment."},
{taskNum:13,taskTitle:"Manage the Appointment of Representatives",t:"Appointment of Representatives",lv:"K",q:"Competence requirements applicable to representatives, including qualifications and regulatory examinations, are contained in:",o:["Board Notice 194 of 2017","Board Notice 80 of 2003","Board Notice 123 of 2009","Board Notice 81 of 2003"],a:0,e:"BN 194 of 2017 contains the competence requirements."},
{taskNum:14,taskTitle:"Manage the Rendering of Services Under Supervision",t:"Supervision",lv:"C",q:"A representative may render financial services under supervision where the representative:",o:["Has been debarred but subsequently reappointed","Does not yet meet the applicable competence requirements","Is employed on a fixed term contract","Renders services in more than one subcategory"],a:1,e:"Supervision is the mechanism permitting a representative who has not yet met the competence requirements to render services."},
{taskNum:14,taskTitle:"Manage the Rendering of Services Under Supervision",t:"Supervision",lv:"AP",q:"An FSP applies identical supervision arrangements to a representative appointed last month and to one who has met all requirements. The MOST appropriate approach would be to:",o:["Apply uniform supervision to ensure consistency of treatment","Supervise only representatives who have received client complaints","Determine the intensity of supervision by reference to the risk presented","Discontinue supervision once the representative reaches twelve months' service"],a:2,e:"Supervision should be calibrated to risk, including the representative's competence, the products concerned and the client base."},
{taskNum:15,taskTitle:"Oversee Representatives Appointed by the FSP",t:"Ongoing Oversight",lv:"C",q:"The obligation to ensure representatives meet the Fit and Proper requirements applies:",o:["At the point of appointment only, and not thereafter","Annually, in conjunction with the compliance report","Only where the representative renders advice to clients","On an ongoing basis while the person is a representative"],a:3,e:"The requirements apply on an ongoing basis, not only at the point of appointment."},
{taskNum:15,taskTitle:"Oversee Representatives Appointed by the FSP",t:"Ongoing Oversight",lv:"AN",q:"Which combination of the following must an FSP be able to demonstrate in respect of its representatives?\ni) That Fit and Proper compliance is monitored on an ongoing basis\nii) That records supporting ongoing compliance are maintained\niii) That representatives not yet compliant are progressing towards the requirements\niv) That each representative has been individually approved by the Authority",o:["(i), (ii) and (iii) only","(i) and (iv) only","(ii), (iii) and (iv) only","(i), (ii), (iii) and (iv)"],a:0,e:"Representatives are not individually approved by the Authority. The remaining three are genuine demonstrable obligations of the FSP."},
{taskNum:16,taskTitle:"Debar Non-Compliant Representatives",t:"Debarment",lv:"AN",q:"An FSP establishes that a representative has forged client signatures. The FSP terminates the employment relationship immediately and takes no further steps. In respect of the FAIS Act, the FSP:",o:["Has discharged its obligations by terminating the employment relationship","Must still consider debarment and follow the prescribed process","Must refer the matter to the Ombud for a binding determination","May not debar a person whose employment has already terminated"],a:1,e:"Termination of employment does not displace the debarment process and the associated notification obligations to the Authority."},
{taskNum:16,taskTitle:"Debar Non-Compliant Representatives",t:"Debarment",lv:"AN",q:"Which combination of the following is required in respect of a debarment by an FSP?\ni) The contravention must be material\nii) The representative must be given notice and an opportunity to respond\niii) The Authority must approve the debarment before it takes effect\niv) The Authority must be notified of the debarment within the prescribed period",o:["(i), (iii) and (iv) only","(ii) and (iii) only","(i), (ii) and (iv) only","(i), (ii), (iii) and (iv)"],a:2,e:"Debarment is effected by the FSP and does not require prior approval by the Authority. Materiality, procedural fairness and notification are all required."}
]);

// ══════════════════════════════════════════════════════
// EXAM SUCCESS BLUEPRINT
// Task weighting derived from real FSCA result slips (4 independent RE5
// sittings, task-level question counts averaged). RE1 intentionally left
// empty pending more result-slip data — do not fabricate RE1 weighting.
// ══════════════════════════════════════════════════════
const BLUEPRINT_DATA = {
  RE5: {
    dataSource: '4 real FSCA RE5 result slips (50 questions each)',
    tasks: [
      {
        id: 'task4', taskNum: 4, title: 'Adhere to the Specific Codes of Conduct',
        avgQuestions: 12.25, pctOfExam: 24.5, importance: 'heavy',
        guardianInsight: "This task alone carries about a quarter of your entire exam — roughly 12 out of every 50 questions. If you only deeply master one topic, make it this one. Most candidates underestimate how much weight sits here because it doesn't sound as intimidating as FICA or the FAIS Act itself.",
        exampleScenario: "A representative recommends a product that earns them higher commission without disclosing the conflict of interest to the client. Questions on this task test whether you can spot Code of Conduct breaches like this in realistic client scenarios — not just recite the rules.",
        quizTopic: 'General Code of Conduct', lessonTopic: 'General Code of Conduct', hardSim: 'RE5_HARD',
      },
      {
        id: 'task8', taskNum: 8, title: 'Operate as a Representative in terms of the FAIS Act',
        avgQuestions: 12.0, pctOfExam: 24.0, importance: 'heavy',
        guardianInsight: "Nearly a quarter of your exam again — Tasks 4 and 8 together are almost half the paper. This one is broader than Task 4: it covers your day-to-day conduct obligations as a representative, not just the formal Code. Because it spans several areas, don't treat it as one narrow topic to memorise — it rewards genuinely understanding how a representative should behave, not rule recall.",
        exampleScenario: "A representative is asked by a client to backdate a document. Questions here test whether you understand the full scope of what operating compliantly as a representative actually requires, day to day.",
        quizTopic: 'Ethics & Professional Conduct', lessonTopic: 'Supervision & Debarment', hardSim: 'RE5_HARD',
      },
      {
        id: 'task2', taskNum: 2, title: 'Contribute Towards Maintaining an FSP Licence',
        avgQuestions: 9.25, pctOfExam: 18.5, importance: 'important',
        guardianInsight: "Just under 1 in 5 questions come from here. This is where FSP licensing conditions, financial soundness, and what keeps a licence in good standing get tested. Solid, consistent marks are available here if you know the licensing framework properly.",
        exampleScenario: "An FSP's liabilities start exceeding its assets. Questions test whether you know what this means for the licence and what must happen next.",
        quizTopic: 'Regulatory Compliance', lessonTopic: 'Fit & Proper Requirements', hardSim: null,
      },
      {
        id: 'task6', taskNum: 6, title: 'Comply with FICA, Money Laundering & Terrorist Financing Regulations',
        avgQuestions: 5.25, pctOfExam: 10.5, importance: 'important',
        guardianInsight: "About 1 in 10 questions. FICA questions are often scenario-based and test whether you know when to file a Suspicious Transaction Report, what tipping-off means, and when Enhanced Due Diligence kicks in — not just definitions.",
        exampleScenario: "A client makes several cash deposits, each just under the reporting threshold, on the same day each month. This is a classic structuring pattern the exam likes to test.",
        quizTopic: 'FICA & AML', lessonTopic: 'FICA & Anti-Money Laundering', hardSim: null,
      },
      {
        id: 'task5', taskNum: 5, title: 'Comply with Regulated Record Keeping Requirements',
        avgQuestions: 3.75, pctOfExam: 7.5, importance: 'medium',
        guardianInsight: "A smaller slice, but don't skip it — these questions tend to be precise about exact retention periods and what must be documented, so partial knowledge often isn't enough to get them right.",
        exampleScenario: "A client asks an FSP to delete their file early. Questions test whether you know which records must legally be kept regardless of the client's request.",
        quizTopic: 'Regulatory Compliance', lessonTopic: 'General Code of Conduct', hardSim: null,
      },
      {
        id: 'task3', taskNum: 3, title: 'Define the Role of the Key Individual',
        avgQuestions: 3.25, pctOfExam: 6.5, importance: 'medium',
        guardianInsight: "A smaller but very learnable task — KI duties and accountability are clearly defined, so this is an efficient place to bank marks with focused study.",
        exampleScenario: "A KI delegates a compliance task to a manager but never checks whether it was actually done. Questions test whether you understand that delegation doesn't remove the KI's accountability.",
        quizTopic: 'FAIS Role Players', lessonTopic: 'FAIS Role Players', hardSim: null,
      },
      {
        id: 'task1', taskNum: 1, title: 'Understand the FAIS Act as a Regulatory Framework',
        avgQuestions: 2.25, pctOfExam: 4.5, importance: 'light',
        guardianInsight: "Only a handful of questions, but foundational — everything else in the exam assumes you understand this framework, so don't skip it even though it's a light slice on its own.",
        exampleScenario: "Questions here test your grasp of what FAIS actually regulates and why it exists — the big-picture framework the rest of the exam builds on.",
        quizTopic: 'Scope & Definitions', lessonTopic: 'FAIS Act — Core Definitions & Scope', hardSim: null,
      },
      {
        id: 'task7', taskNum: 7, title: 'Dealing with Complaints Submitted to the Ombud',
        avgQuestions: 2.0, pctOfExam: 4.0, importance: 'light',
        guardianInsight: "The smallest task by volume, but the questions are usually specific and learnable — know the Ombud's process, jurisdiction, and timeframes and these marks are close to guaranteed.",
        exampleScenario: "A client's complaint is rejected by an FSP with no response for 45 days. Questions test whether you know what the client can do next.",
        quizTopic: 'FAIS Ombud', lessonTopic: 'FAIS Ombud', hardSim: null,
      },
    ],
  },
  RE1: {
    dataSource: 'real FSCA RE1 result data — this weighting is refined as we gather more results, so it may be updated over time',
    tasks: [
      {
        id: 'ret4', taskNum: 4, title: 'Operate as a Key Individual in Terms of the FAIS Act',
        avgQuestions: 12.33, pctOfExam: 15.4, importance: 'heavy',
        guardianInsight: "This is your single biggest task — over 15% of the exam, more than 1 in every 7 questions. It covers the full scope of what operating as a KI actually means day to day: oversight, accountability, and decision-making, not just the formal duties list. Master this one first.",
        exampleScenario: "A KI delegates a compliance check to a manager but never verifies it was actually completed. Questions here test whether you understand that delegation reduces workload, not accountability — the KI remains responsible for the outcome.",
        quizTopic: 'KI Responsibilities', lessonTopic: 'KI Responsibilities & Accountability', hardSim: "RE1_HARD",
      },
      {
        id: 'ret6', taskNum: 6, title: 'Adhere to the Specific Codes of Conduct',
        avgQuestions: 12.0, pctOfExam: 15.0, importance: 'heavy',
        guardianInsight: "Nearly as heavy as Task 4 — together these two tasks are close to a third of your entire exam. This task tests governance and ethical conduct obligations at the KI level, not just the general representative-level Code most people are familiar with from RE5.",
        exampleScenario: "A board approves a commission structure that a compliance officer had flagged as creating suitability pressure. Questions test whether you recognise this as a governance and ethical culture failure, not just a technical compliance issue.",
        quizTopic: 'Governance & Ethics', lessonTopic: 'Governance & COI Management', hardSim: "RE1_HARD",
      },
      {
        id: 'ret3', taskNum: 3, title: 'Maintain the Licence of the FSP',
        avgQuestions: 10.33, pctOfExam: 12.9, importance: 'heavy',
        guardianInsight: "The third Heavy Hitter — Tasks 4, 6, and 3 together make up over 43% of your exam. This task covers what actually keeps an FSP's licence valid and in good standing: financial soundness, licence conditions, and category compliance.",
        exampleScenario: "An FSP's liabilities begin exceeding its assets. Questions test whether you know this must be reported to the FSCA immediately, and that continuing to trade normally does not cure the underlying breach.",
        quizTopic: 'FSP Licensing & Financial Soundness', lessonTopic: 'FSP Licensing & Compliance', hardSim: "RE1_HARD",
      },
      {
        id: 'ret7', taskNum: 7, title: 'Manage and Oversee the Compliance Functions',
        avgQuestions: 6.5, pctOfExam: 8.1, importance: 'important',
        guardianInsight: "A solid, learnable task about the compliance function itself — whether it's independent, whether it's actually monitoring properly, and what the KI's obligations are toward it.",
        exampleScenario: "A compliance officer approves client files without reading them for months. Questions test whether you understand this is a KI oversight failure, not just a compliance-officer problem, since the KI must monitor whether supervision is genuinely happening.",
        quizTopic: 'Compliance Management', lessonTopic: 'KI Duties & Compliance', hardSim: null,
      },
      {
        id: 'ret5', taskNum: 5, title: 'Manage and Oversee the FSP\'s Operational Ability',
        avgQuestions: 6.33, pctOfExam: 7.9, importance: 'important',
        guardianInsight: "This task covers whether the FSP is genuinely capable of doing what its licence says it can do — systems, resources, and operational capacity, not just paperwork compliance.",
        exampleScenario: "An FSP's licence covers a product category it no longer has the systems or trained staff to properly service. Questions test whether you understand operational ability is an ongoing requirement, not a one-time licensing checkbox.",
        quizTopic: 'FSP Licensing & Financial Soundness', lessonTopic: 'FSP Licensing & Compliance', hardSim: null,
      },
      {
        id: 'ret12', taskNum: 12, title: 'Define the Role of the Representative',
        avgQuestions: 6.0, pctOfExam: 7.5, importance: 'important',
        guardianInsight: "A well-defined, learnable task — what a representative is legally authorised to do, and how that role relates back to the FSP's licence and the KI's oversight of it.",
        exampleScenario: "An administrator gives a client product advice while the sole representative is unavailable. Questions test whether you understand there are no emergency exceptions to who may legally render financial services.",
        quizTopic: 'Supervision of Representatives', lessonTopic: 'Supervision of Representatives', hardSim: null,
      },
      {
        id: 'ret10', taskNum: 10, title: 'FIC Act, Money Laundering & Terrorist Financing Oversight',
        avgQuestions: 4.5, pctOfExam: 5.6, importance: 'important',
        guardianInsight: "At the KI level, FICA oversight is about the FSP's systems and controls, not just individual transaction reporting — whether the AML programme actually functions, not just whether it exists on paper.",
        exampleScenario: "An FSP's FICA training hasn't been updated in three years despite new typologies emerging. Questions test whether you understand oversight means keeping the whole AML function current, not a once-off setup.",
        quizTopic: 'Compliance Management', lessonTopic: 'KI Duties & Compliance', hardSim: null,
      },
      {
        id: 'ret11', taskNum: 11, title: 'Deal with Ombud Complaints',
        avgQuestions: 4.0, pctOfExam: 5.0, importance: 'important',
        guardianInsight: "A precise, learnable task about the FAIS Ombud process — jurisdiction, timeframes, and what happens when a determination isn't complied with. Know the process cold and these marks are close to guaranteed.",
        exampleScenario: "An FSP ignores an unappealed Ombud determination. Questions test whether you know it carries the force of a High Court civil judgement, enforceable through the courts regardless of the FSP's objection.",
        quizTopic: 'FAIS Ombud & Complaints', lessonTopic: 'FAIS Ombud & Complaints', hardSim: null,
      },
      {
        id: 'ret8', taskNum: 8, title: 'Comply with Regulated Record Keeping Requirements',
        avgQuestions: 3.0, pctOfExam: 3.8, importance: 'medium',
        guardianInsight: "Smaller, but the questions tend to be precise about exact retention periods and what must be documented — partial knowledge often isn't enough to get these right.",
        exampleScenario: "A client asks an FSP to delete their file early. Questions test whether you know which records must legally be retained regardless of the client's request.",
        quizTopic: 'Compliance Management', lessonTopic: 'KI Duties & Compliance', hardSim: null,
      },
      {
        id: 'ret9', taskNum: 9, title: 'Accounting and Auditing Oversight',
        avgQuestions: 3.0, pctOfExam: 3.8, importance: 'medium',
        guardianInsight: "A focused task on the FSP's financial reporting obligations — auditor appointment, annual financial statement submission, and what the KI must oversee here.",
        exampleScenario: "An FSP delays submitting its annual financial statements. Questions test whether you understand this is a KI oversight responsibility, not purely an administrative or accounting-department matter.",
        quizTopic: 'FSP Licensing & Financial Soundness', lessonTopic: 'FSP Licensing & Compliance', hardSim: null,
      },
      {
        id: 'ret13', taskNum: 13, title: 'Manage the Appointment of Representatives',
        avgQuestions: 2.5, pctOfExam: 3.1, importance: 'medium',
        guardianInsight: "Covers what must actually happen before someone is appointed as a representative — fit and proper verification, debarment checks, and proper registration.",
        exampleScenario: "An FSP appoints a representative without checking the FSCA's debarred persons register first. Questions test whether you understand this check is a mandatory pre-appointment step, not optional due diligence.",
        quizTopic: 'Supervision of Representatives', lessonTopic: 'Supervision of Representatives', hardSim: null,
      },
      {
        id: 'ret14', taskNum: 14, title: 'Manage the Rendering of Services Under Supervision',
        avgQuestions: 2.5, pctOfExam: 3.1, importance: 'medium',
        guardianInsight: "Covers the mechanics of a supervision arrangement itself — disclosure to clients, monitoring quality, and knowing when supervision should end.",
        exampleScenario: "A supervisor signs off on files within minutes of receiving them for months. Questions test whether you recognise rubber-stamping as a breach of the supervision arrangement, regardless of whether any client was actually harmed.",
        quizTopic: 'Supervision of Representatives', lessonTopic: 'Supervision of Representatives', hardSim: null,
      },
      {
        id: 'ret16', taskNum: 16, title: 'Debar Non-Compliant Representatives',
        avgQuestions: 2.5, pctOfExam: 3.1, importance: 'medium',
        guardianInsight: "A smaller but precise task on the debarment process itself — grounds, procedure, and what the KI must do once a material breach is identified.",
        exampleScenario: "A KI discovers a representative falsified a client's needs analysis. Questions test whether you know debarment requires a fair, documented process, not just an immediate dismissal.",
        quizTopic: 'Supervision of Representatives', lessonTopic: 'Supervision of Representatives', hardSim: null,
      },
      {
        id: 'ret1', taskNum: 1, title: 'Understand the FAIS Act as a Regulatory Framework',
        avgQuestions: 2.0, pctOfExam: 2.5, importance: 'medium',
        guardianInsight: "A light slice on its own, but foundational — everything else in the exam assumes you understand this framework, so it's worth getting solid even though it carries few marks directly.",
        exampleScenario: "Questions here test your grasp of what FAIS actually regulates and why the framework exists, underpinning everything else tested in the exam.",
        quizTopic: 'Regulatory Framework', lessonTopic: 'Twin Peaks & Regulatory Framework', hardSim: null,
      },
      {
        id: 'ret15', taskNum: 15, title: 'Oversee Representatives Appointed by the FSP',
        avgQuestions: 2.0, pctOfExam: 2.5, importance: 'medium',
        guardianInsight: "A smaller, ongoing-oversight task — distinct from the appointment task itself, this is about continuing to monitor representatives after they're already appointed.",
        exampleScenario: "A KI appoints a representative and then has no further contact with their work for a year. Questions test whether you understand oversight is continuous, not a one-time appointment event.",
        quizTopic: 'Supervision of Representatives', lessonTopic: 'Supervision of Representatives', hardSim: null,
      },
      {
        id: 'ret2', taskNum: 2, title: 'Define Financial Products and Financial Services',
        avgQuestions: 1.33, pctOfExam: 1.7, importance: 'light',
        guardianInsight: "The smallest task by volume, but it's foundational definitional content — know these definitions cold and these few marks are close to guaranteed.",
        exampleScenario: "Questions here test whether you can correctly classify what counts as a financial product versus a financial service under the Act's definitions.",
        quizTopic: 'Regulatory Framework', lessonTopic: 'Twin Peaks & Regulatory Framework', hardSim: null,
      }
    ],
  },
};

// ── Frontend-only progress hooks — no backend yet ──────────────────────────
/**
 * @typedef {Object} BlueprintProgress
 * @property {string} userId
 * @property {'RE5'|'RE1'} examType
 * @property {string} taskId
 * @property {number} completionPercent   0-100
 * @property {number} confidenceScore     0-100, learner self-rated
 * @property {string} updatedAt           ISO timestamp
 */
App.BlueprintProgress = (() => {
  const KEY = 'blueprintProg'; // manual confidence-score overlay, not the primary readiness source

  // Real readiness comes from actual quiz activity on the task's mapped
  // topic (App.Progress — the same data source Home's Weak Areas card
  // uses) — not a separate tracker nothing writes to. Accuracy on that
  // topic IS the completion percentage.
  function taskCompletion(examType, task) {
    if (!task.quizTopic) return 0;
    const prog = App.Progress.get();
    const byT  = prog[examType.toLowerCase()]?.byT || {};
    const stat = byT[task.quizTopic];
    if (!stat || !stat.att) return 0;
    return Math.round((stat.cor / stat.att) * 100);
  }

  function overallReadiness(examType) {
    const tasks = BLUEPRINT_DATA[examType]?.tasks || [];
    if (tasks.length === 0) return 0;
    const total = tasks.reduce((sum, t) => sum + taskCompletion(examType, t), 0);
    return Math.round(total / tasks.length);
  }

  // Kept for a future self-rated "how confident do you feel" overlay —
  // not currently used to compute readiness, which is derived from real
  // quiz data above instead.
  function get(examType) {
    const all = App.Storage.get(KEY, {});
    return all[examType] || {};
  }
  function save(examType, taskId, data) {
    const all = App.Storage.get(KEY, {});
    if (!all[examType]) all[examType] = {};
    all[examType][taskId] = { ...all[examType][taskId], ...data, updatedAt: new Date().toISOString() };
    App.Storage.set(KEY, all);
    // TODO: once a Supabase 'blueprint_progress' table exists, push here too —
    // same local-first pattern as App.Progress/App.Behaviour.
  }
  return { get, save, taskCompletion, overallReadiness };
})();

// ── Loop.so email automation — data collection only, no network calls yet ──
App.BlueprintSignals = (() => {
  function getEmailAutomationPayload(examType) {
    const user  = App.Auth.getLocalUser() || {};
    const tasks = BLUEPRINT_DATA[examType]?.tasks || [];
    let weakest = null, strongest = null, weakestPct = 101, strongestPct = -1;
    tasks.forEach(t => {
      const pct = App.BlueprintProgress.taskCompletion(examType, t);
      if (pct < weakestPct)  { weakestPct = pct;  weakest = t.title; }
      if (pct > strongestPct){ strongestPct = pct; strongest = t.title; }
    });
    return {
      examDate:      user.examDate || null,
      examType,
      readinessScore: App.BlueprintProgress.overallReadiness(examType),
      weakestTask:   weakest,
      strongestTask: strongest,
    };
  }
  // TODO: POST getEmailAutomationPayload() to the Loop.so webhook once the
  // endpoint exists. Trigger points: 30/14/7 days before examDate, exam day,
  // and post-exam follow-up. Left as a no-op placeholder for now.
  function _sendToLoopSo(payload) {
    console.log('[Loop.so placeholder — not yet wired]', payload);
  }
  return { getEmailAutomationPayload };
})();

// ══════════════════════════════════════════════════════
// ONBOARDING
// ══════════════════════════════════════════════════════
const REFERRAL_SOURCES = ['WhatsApp group', 'Telegram', 'Facebook', 'A friend told me', 'YouTube', 'Google search', 'Other'];
const OB_STEPS = [
  { id: 'disclaimer', type: 'disclaimer' },
  { id: 'exam',        type: 'select', title: 'What are you preparing for?',   sub: 'Your dashboard, questions, and coaching will be personalised to your exam.', opts: ['RE5 — Representative Regulatory Examination', 'RE1 — Key Individual Regulatory Examination', 'Both RE5 & RE1 (Combo)'] },
  { id: 'institution', type: 'select', title: 'Your Exam Institution',          sub: 'Where are you registered for your regulatory examination?',                  opts: ['Moonstone Compliance Services', 'INSETA', 'Compliance Institute of South Africa'] },
  { id: 'referral',    type: 'select', title: 'How did you hear about us?',     sub: "This helps us know where to focus, and thank the right people.",             opts: REFERRAL_SOURCES },
  { id: 'date',        type: 'date' },
  { id: 'auth',        type: 'auth' },
];

function renderObStep() {
  const step = OB_STEPS[STATE.obStep];
  const dots = OB_STEPS.map((_, i) => `<div class="ob-dot ${i === STATE.obStep ? 'active' : ''}"></div>`).join('');
  let html = '';

  if (step.type === 'disclaimer') {
    html = `
    <div class="tc mb20">
      <div class="serif" style="font-size:2rem;color:var(--gold)">RE Mastery Hub</div>
      <div style="color:rgba(255,255,255,.45);font-size:.75rem;margin-top:4px;text-transform:uppercase;letter-spacing:1px">South Africa's RE Exam Success System</div>
    </div>
    <div class="ob-dots">${dots}</div>
    <div style="color:#fff;font-weight:800;font-size:1.05rem;margin-bottom:10px">Legal Disclaimer — Please Read</div>
    <div style="background:rgba(255,255,255,.05);border:1px solid rgba(255,255,255,.1);border-radius:10px;padding:14px;margin-bottom:14px;max-height:240px;overflow-y:auto;font-size:.79rem;color:rgba(255,255,255,.8);line-height:1.8">
      <p class="mb8"><strong style="color:var(--gold)">INDEPENDENT EDUCATIONAL PLATFORM</strong></p>
      <p class="mb8">RE Mastery Hub is an independent educational platform developed and maintained by <strong>The Trending Bee Creatives</strong>. It is not affiliated with, endorsed by, or associated with Moonstone Compliance Services, the FSCA, INSETA, the Compliance Institute of South Africa, or any regulatory or examination authority.</p>
      <p class="mb8"><strong style="color:var(--gold)">NO GUARANTEE OF SUCCESS</strong></p>
      <p class="mb8">Use of this platform does not guarantee passing any regulatory examination. Exam success depends on individual preparation, study effort, and understanding of applicable legislation.</p>
      <p class="mb8"><strong style="color:var(--gold)">CONTENT ACCURACY</strong></p>
      <p class="mb8">Content is based on publicly available South African financial services legislation. Users are responsible for verifying that content reflects current legislation. Always refer to the most current version of applicable legislation and official study guides from your examination body.</p>
      <p class="mb8"><strong style="color:var(--gold)">NO PROFESSIONAL ADVICE</strong></p>
      <p>Nothing in this platform constitutes legal, regulatory, or professional advice. Content is provided for educational preparation purposes only.</p>
    </div>
    <div style="color:rgba(255,255,255,.4);font-size:.7rem;margin-bottom:10px">v${App.Config.appVersion} &nbsp;&middot;&nbsp; &copy; ${App.Config.company}</div>
    <div class="ob-opt" id="disOpt" role="checkbox" aria-checked="false" onclick="toggleDis(this)">
      <div class="ob-chk" id="disChk"></div>
      <div style="font-size:.82rem">I have read and accept this disclaimer and the Terms of Use.</div>
    </div>
    <button class="btn btn-gold btn-lg btn-full" style="margin-top:16px;opacity:.5;pointer-events:none" id="disBtn" onclick="obNext()">Accept &amp; Continue</button>`;

  } else if (step.type === 'select') {
    html = `
    <div class="ob-dots mb24">${dots}</div>
    <div style="color:#fff;font-weight:800;font-size:1.05rem;margin-bottom:6px">${step.title}</div>
    <div style="color:rgba(255,255,255,.55);font-size:.83rem;margin-bottom:18px;line-height:1.5">${step.sub}</div>
    ${step.opts.map((o, i) => `<div class="ob-opt ${STATE.obData[step.id] === i ? 'sel' : ''}" onclick="obSel('${step.id}',${i},this)"><span style="flex:1;font-size:.9rem;font-weight:600">${o}</span><div class="ob-chk">${STATE.obData[step.id] === i ? '&#10003;' : ''}</div></div>`).join('')}
    <button class="btn btn-gold btn-lg btn-full" style="margin-top:20px" onclick="obNext()">Continue</button>
    <button class="btn btn-ghost btn-sm btn-full" style="margin-top:8px;color:rgba(255,255,255,.35)" onclick="obBack()">Back</button>`;

  } else if (step.type === 'date') {
    html = `
    <div class="ob-dots mb24">${dots}</div>
    <div style="color:#fff;font-weight:800;font-size:1.05rem;margin-bottom:6px">When is your exam?</div>
    <div style="color:rgba(255,255,255,.55);font-size:.83rem;margin-bottom:18px">We'll build your countdown timer and daily targets around this date.</div>
    <label class="inp-label" style="color:rgba(255,255,255,.55)" for="obDateInput">Exam date</label>
    <input type="date" class="inp mb12" id="obDateInput" style="background:rgba(255,255,255,.08);border-color:rgba(255,255,255,.18);color:#fff;color-scheme:dark" value="${STATE.obData.examDate}" onchange="obDateChange(this.value)"/>
    <div id="daysDisplay" style="text-align:center;padding:14px;background:rgba(200,168,75,.1);border:1px solid rgba(200,168,75,.25);border-radius:10px;margin-bottom:14px">
      ${STATE.obData.daysLeft > 0
        ? `<div style="font-size:2rem;font-weight:900;color:var(--gold)">${STATE.obData.daysLeft}</div><div style="color:rgba(255,255,255,.45);font-size:.7rem;text-transform:uppercase;letter-spacing:.8px">Days Remaining</div>`
        : `<div style="color:rgba(255,255,255,.35);font-size:.83rem">Enter your exam date above</div>`}
    </div>
    <button class="btn btn-gold btn-lg btn-full" onclick="obNext()">Continue</button>
    <button class="btn btn-ghost btn-sm btn-full" style="margin-top:8px;color:rgba(255,255,255,.35)" onclick="obBack()">Back</button>`;

  } else if (step.type === 'auth') {
    const isSignIn = STATE.obData._authMode === 'signin';
    const isForgot = STATE.obData._authMode === 'forgot';
    html = isSignIn ? _renderSignIn(dots) : isForgot ? _renderForgot(dots) : _renderSignUp(dots);
  }

  document.getElementById('obCard').innerHTML = html;
}

// ── Auth screen render helpers ────────────────────────────────────────────────

const _inpStyle = 'background:rgba(255,255,255,.08);border-color:rgba(255,255,255,.18);color:#fff';
const _labelStyle = 'color:rgba(255,255,255,.55)';
const _mutedStyle = 'color:rgba(255,255,255,.35)';

function _renderSignUp(dots) {
  return `
  <div class="ob-dots mb20">${dots}</div>
  <div style="color:#fff;font-weight:800;font-size:1.05rem;margin-bottom:4px">Create your free account</div>
  <div style="${_labelStyle};font-size:.83rem;margin-bottom:18px;line-height:1.5">Your progress and exam readiness are saved across all your devices.</div>

  <label class="inp-label" style="${_labelStyle}" for="obName">Full name</label>
  <input type="text" class="inp mb10" id="obName" style="${_inpStyle}" placeholder="Your full name" autocomplete="name" oninput="obPwCheck()"/>
  <label class="inp-label" style="${_labelStyle}" for="obEmail">Email address</label>
  <input type="email" class="inp mb10" id="obEmail" style="${_inpStyle}" placeholder="you@example.com" autocomplete="email" oninput="obPwCheck()"/>
  <label class="inp-label" style="${_labelStyle}" for="obPw">Password</label>
  <div style="position:relative;margin-bottom:6px">
    <input type="password" class="inp" id="obPw" style="${_inpStyle};padding-right:44px" placeholder="Create a password" autocomplete="new-password" oninput="obPwCheck()"/>
    <button onclick="obTogglePw('obPw',this)" type="button" style="position:absolute;right:12px;top:50%;transform:translateY(-50%);background:none;border:none;color:rgba(255,255,255,.4);cursor:pointer;font-size:.85rem;padding:4px">👁</button>
  </div>
  <label class="inp-label" style="${_labelStyle}" for="obPwConfirm">Confirm password</label>
  <div style="position:relative;margin-bottom:6px">
    <input type="password" class="inp" id="obPwConfirm" style="${_inpStyle};padding-right:44px" placeholder="Repeat your password" autocomplete="new-password" oninput="obPwCheck()"/>
    <button onclick="obTogglePw('obPwConfirm',this)" type="button" style="position:absolute;right:12px;top:50%;transform:translateY(-50%);background:none;border:none;color:rgba(255,255,255,.4);cursor:pointer;font-size:.85rem;padding:4px">👁</button>
  </div>

  <div id="pwStrengthBar" style="height:4px;border-radius:2px;background:rgba(255,255,255,.1);margin-bottom:6px;overflow:hidden">
    <div id="pwStrengthFill" style="height:100%;width:0;border-radius:2px;transition:width .3s,background .3s"></div>
  </div>
  <div id="pwStrengthLabel" style="font-size:.7rem;${_mutedStyle};margin-bottom:8px"></div>

  <div id="pwReqs" style="background:rgba(255,255,255,.05);border-radius:8px;padding:10px 12px;margin-bottom:14px;font-size:.73rem;line-height:1.9">
    <div id="req-len"  style="${_mutedStyle}">○ Minimum 8 characters</div>
    <div id="req-up"   style="${_mutedStyle}">○ At least 1 uppercase letter</div>
    <div id="req-low"  style="${_mutedStyle}">○ At least 1 lowercase letter</div>
    <div id="req-num"  style="${_mutedStyle}">○ At least 1 number</div>
    <div id="req-spec" style="${_mutedStyle}">○ At least 1 special character</div>
  </div>

  <button class="btn btn-gold btn-lg btn-full" id="obAuthBtn" onclick="obDoSignUp()" disabled style="opacity:.5;cursor:not-allowed">Create Account</button>
  <button class="btn btn-ghost btn-sm btn-full" style="margin-top:8px;${_mutedStyle}" onclick="obBack()">Back</button>
  <div style="margin-top:14px;text-align:center;font-size:.75rem;${_mutedStyle}">Already have an account? <span onclick="obSwitchAuth('signin')" style="color:var(--gold);cursor:pointer;font-weight:600">Sign in</span></div>`;
}

function _renderSignIn(dots) {
  return `
  <div class="ob-dots mb20">${dots}</div>
  <div style="color:#fff;font-weight:800;font-size:1.05rem;margin-bottom:4px">Welcome back</div>
  <div style="${_labelStyle};font-size:.83rem;margin-bottom:18px">Sign in to continue your preparation.</div>

  <label class="inp-label" style="${_labelStyle}" for="obEmail">Email address</label>
  <input type="email" class="inp mb10" id="obEmail" style="${_inpStyle}" placeholder="you@example.com" autocomplete="email"/>
  <label class="inp-label" style="${_labelStyle}" for="obPw">Password</label>
  <div style="position:relative;margin-bottom:16px">
    <input type="password" class="inp" id="obPw" style="${_inpStyle};padding-right:44px" placeholder="Your password" autocomplete="current-password"/>
    <button onclick="obTogglePw('obPw',this)" type="button" style="position:absolute;right:12px;top:50%;transform:translateY(-50%);background:none;border:none;color:rgba(255,255,255,.4);cursor:pointer;font-size:.85rem;padding:4px">👁</button>
  </div>

  <button class="btn btn-gold btn-lg btn-full" id="obAuthBtn" onclick="obDoSignIn()">Sign In</button>
  <button class="btn btn-ghost btn-sm btn-full" style="margin-top:8px;${_mutedStyle}" onclick="obBack()">Back</button>
  <div style="margin-top:14px;text-align:center;font-size:.75rem">
    <span onclick="obSwitchAuth('forgot')" style="color:var(--gold);cursor:pointer;font-weight:600">Forgot password?</span>
    &nbsp;&nbsp;·&nbsp;&nbsp;
    <span style="${_mutedStyle}">New here? <span onclick="obSwitchAuth('signup')" style="color:var(--gold);cursor:pointer;font-weight:600">Create account</span></span>
  </div>`;
}

function _renderForgot(dots) {
  return `
  <div class="ob-dots mb20">${dots}</div>
  <div style="color:#fff;font-weight:800;font-size:1.05rem;margin-bottom:4px">Reset your password</div>
  <div style="${_labelStyle};font-size:.83rem;margin-bottom:18px;line-height:1.5">Enter your email and we'll send you a link to reset your password.</div>

  <label class="inp-label" style="${_labelStyle}" for="obEmail">Email address</label>
  <input type="email" class="inp mb16" id="obEmail" style="${_inpStyle}" placeholder="you@example.com" autocomplete="email"/>

  <button class="btn btn-gold btn-lg btn-full" id="obAuthBtn" onclick="obDoReset()">Send Reset Link</button>
  <button class="btn btn-ghost btn-sm btn-full" style="margin-top:8px;${_mutedStyle}" onclick="obSwitchAuth('signin')">Back to Sign In</button>`;
}

function toggleDis(el) {
  STATE.disAccepted = !STATE.disAccepted;
  el.classList.toggle('sel', STATE.disAccepted);
  el.setAttribute('aria-checked', String(STATE.disAccepted));
  el.querySelector('#disChk').innerHTML = STATE.disAccepted ? '&#10003;' : '';
  const btn = document.getElementById('disBtn');
  if (btn) { btn.style.opacity = STATE.disAccepted ? '1' : '.5'; btn.style.pointerEvents = STATE.disAccepted ? 'auto' : 'none'; }
}

function obSel(field, idx, el) {
  STATE.obData[field] = idx;
  el.parentElement.querySelectorAll('.ob-opt').forEach((o, i) => {
    o.classList.toggle('sel', i === idx);
    o.querySelector('.ob-chk').innerHTML = i === idx ? '&#10003;' : '';
  });
}

function obDateChange(val) {
  const diff = Math.ceil((new Date(val) - new Date()) / 864e5);
  if (diff < 0) {
    showToast('Please enter a future date.');
    STATE.obData.examDate = ''; STATE.obData.daysLeft = 0;
    const el = document.getElementById('daysDisplay');
    if (el) el.innerHTML = `<div style="color:#fca5a5;font-size:.83rem">This date is in the past.</div>`;
    return;
  }
  STATE.obData.examDate = val; STATE.obData.daysLeft = diff;
  const el = document.getElementById('daysDisplay');
  if (el) el.innerHTML = `<div style="font-size:2rem;font-weight:900;color:var(--gold)">${diff}</div><div style="color:rgba(255,255,255,.45);font-size:.7rem;text-transform:uppercase;letter-spacing:.8px">Days Remaining</div>`;
}

function obNext() {
  const step = OB_STEPS[STATE.obStep];
  if (step.type === 'disclaimer' && !STATE.disAccepted) { showToast('Please read and accept the disclaimer to continue.'); return; }
  if (step.type === 'select' && STATE.obData[step.id] === -1) { showToast('Please make a selection.'); return; }
  if (step.type === 'date' && !STATE.obData.examDate) { showToast('Please enter a valid future exam date.'); return; }
  STATE.obStep++;
  renderObStep();
}
function obBack() { if (STATE.obStep > 0) { STATE.obStep--; renderObStep(); } }

// ── Auth mode switching ───────────────────────────────────────────────────────
function obSwitchAuth(mode) {
  STATE.obData._authMode = mode;
  renderObStep();
}

// ── Show/hide password toggle ─────────────────────────────────────────────────
function obTogglePw(inputId, btn) {
  const inp = document.getElementById(inputId);
  if (!inp) return;
  const showing = inp.type === 'text';
  inp.type = showing ? 'password' : 'text';
  btn.textContent = showing ? '👁' : '🙈';
}

// ── Real-time password strength checker ───────────────────────────────────────
function obPwCheck() {
  const pw      = document.getElementById('obPw')?.value || '';
  const pwConf  = document.getElementById('obPwConfirm')?.value || '';
  const name    = document.getElementById('obName')?.value.trim() || '';
  const email   = document.getElementById('obEmail')?.value.trim() || '';
  const btn     = document.getElementById('obAuthBtn');

  const checks = {
    len:  pw.length >= 8,
    up:   /[A-Z]/.test(pw),
    low:  /[a-z]/.test(pw),
    num:  /[0-9]/.test(pw),
    spec: /[^A-Za-z0-9]/.test(pw),
  };

  // Update requirement indicators
  Object.entries(checks).forEach(([k, pass]) => {
    const el = document.getElementById('req-' + k);
    if (el) {
      el.style.color = pass ? 'rgba(52,211,153,.9)' : 'rgba(255,255,255,.35)';
      el.textContent = (pass ? '✓ ' : '○ ') + el.textContent.replace(/^[✓○] /, '');
    }
  });

  // Strength score 0–5
  const score = Object.values(checks).filter(Boolean).length;
  const fill  = document.getElementById('pwStrengthFill');
  const label = document.getElementById('pwStrengthLabel');
  if (fill && label && pw.length > 0) {
    const pct   = (score / 5) * 100;
    const color = score <= 2 ? '#ef4444' : score <= 3 ? '#f59e0b' : '#10b981';
    const text  = score <= 2 ? 'Weak' : score <= 3 ? 'Medium' : 'Strong';
    fill.style.width      = pct + '%';
    fill.style.background = color;
    label.textContent     = text;
    label.style.color     = color;
  } else if (fill && label) {
    fill.style.width  = '0';
    label.textContent = '';
  }

  // Password match indicator (only show if confirm has input)
  const confInput = document.getElementById('obPwConfirm');
  if (confInput && pwConf.length > 0) {
    const match = pw === pwConf;
    confInput.style.borderColor = match ? 'rgba(52,211,153,.7)' : 'rgba(239,68,68,.7)';
  } else if (confInput) {
    confInput.style.borderColor = 'rgba(255,255,255,.18)';
  }

  // Enable submit only when all pass AND name + email filled AND passwords match
  const allPass = Object.values(checks).every(Boolean);
  const pwMatch = pw === pwConf && pwConf.length > 0;
  if (btn) {
    btn.disabled = !(allPass && name && email && pwMatch);
    btn.style.opacity       = btn.disabled ? '.5' : '1';
    btn.style.cursor        = btn.disabled ? 'not-allowed' : 'pointer';
    btn.style.pointerEvents = btn.disabled ? 'none' : 'auto';
  }
}

// ── Sign Up ───────────────────────────────────────────────────────────────────
async function obDoSignUp() {
  const name    = document.getElementById('obName')?.value.trim();
  const email   = document.getElementById('obEmail')?.value.trim().toLowerCase();
  const pw      = document.getElementById('obPw')?.value;
  const pwConf  = document.getElementById('obPwConfirm')?.value;
  const btn     = document.getElementById('obAuthBtn');

  if (!name)                          { showToast('Please enter your full name.'); return; }
  if (!email || !email.includes('@')) { showToast('Please enter a valid email address.'); return; }
  if (!pw || pw.length < 8)           { showToast('Password must be at least 8 characters.'); return; }
  if (!/[A-Z]/.test(pw))             { showToast('Password needs at least 1 uppercase letter.'); return; }
  if (!/[a-z]/.test(pw))             { showToast('Password needs at least 1 lowercase letter.'); return; }
  if (!/[0-9]/.test(pw))             { showToast('Password needs at least 1 number.'); return; }
  if (!/[^A-Za-z0-9]/.test(pw))      { showToast('Password needs at least 1 special character.'); return; }
  if (pw !== pwConf)                  { showToast('Passwords do not match. Please try again.'); return; }

  if (btn) { btn.disabled = true; btn.textContent = 'Creating account…'; }

  const sdkReady = await App.Supabase.waitForSdk(3000);
  if (!sdkReady) {
    if (btn) { btn.disabled = false; btn.textContent = 'Create Account'; btn.style.opacity = '1'; btn.style.pointerEvents = 'auto'; }
    showToast('Unable to connect. Check your internet and try again.');
    return;
  }

  const result = await App.Supabase.signUpWithPassword(email, pw, name);
  if (!result.ok) {
    if (btn) { btn.disabled = false; btn.textContent = 'Create Account'; btn.style.opacity = '1'; btn.style.pointerEvents = 'auto'; }
    showToast(result.error || 'Could not create account. Please try again.');
    return;
  }

  // Pre-create Supabase profile so user appears in dashboard immediately.
  // upsertProfile() resolves (never rejects) even on failure — it returns
  // {ok:false, error}. A plain .catch() here would never fire and the
  // failure would be silently ignored, which is exactly what caused
  // profiles to go missing for real signed-up users. Check .ok explicitly,
  // retry once, and if it still fails, mark it so the next app boot
  // retries automatically instead of losing the profile forever.
  if (result.user) {
    _upsertProfileWithRetry(result.user.id, {
      email,
      name,
      pro_tier:     'none', // is_pro is now a generated column — never write to it directly
      exam:         STATE.obData.exam        ?? -1,
      institution:  STATE.obData.institution ?? -1,
      exam_date:    STATE.obData.examDate    || '',
      referral_code: 'RMH' + Math.random().toString(36).substr(2, 6).toUpperCase(),
      referral_source: REFERRAL_SOURCES[STATE.obData.referral] || 'Unknown',
    });

    // This is the real "brand new account" moment — the later onboarding-
    // completion step always finds this profile already existing (it was
    // just created above), so its own !existingProfile check can never be
    // true for the password signup path. That was the actual bug: referrals
    // were being recorded nowhere, ever, for any password-signup user.
    if (PENDING_REFERRAL_CODE) {
      App.Supabase.getClient().rpc('record_referral', {
        p_code: PENDING_REFERRAL_CODE,
        p_new_user_id: result.user.id
      }).then(() => _clearPendingReferral())
        .catch(e => console.warn('[RMH] record_referral failed:', e.message));
    }
  }

  // Show success confirmation — do NOT auto-login
  // User must sign in explicitly with their credentials
  _showSignUpSuccess(name, email);
}

// ── Sign In ───────────────────────────────────────────────────────────────────
async function obDoSignIn() {
  const email = document.getElementById('obEmail')?.value.trim().toLowerCase();
  const pw    = document.getElementById('obPw')?.value;
  const btn   = document.getElementById('obAuthBtn');

  if (!email || !email.includes('@')) { showToast('Please enter your email address.'); return; }
  if (!pw)                            { showToast('Please enter your password.'); return; }

  if (btn) { btn.disabled = true; btn.textContent = 'Signing in…'; }

  const sdkReady = await App.Supabase.waitForSdk(3000);
  if (!sdkReady) {
    if (btn) { btn.disabled = false; btn.textContent = 'Sign In'; }
    showToast('Unable to connect. Check your internet and try again.');
    return;
  }

  const result = await App.Supabase.signInWithPassword(email, pw);
  if (!result.ok) {
    if (btn) { btn.disabled = false; btn.textContent = 'Sign In'; }
    const err = (result.error || '').toLowerCase();
    let msg;
    if (err.includes('email not confirmed')) {
      msg = 'Email not confirmed. Use Forgot Password to regain access.';
    } else if (err.includes('invalid') || err.includes('credentials') || err.includes('wrong')) {
      msg = 'Incorrect email or password. Please try again.';
    } else {
      // Show raw error for diagnosis — will be cleaned up after auth is stable
      msg = result.error || 'Sign in failed. Please try again.';
    }
    console.error('[RMH] signIn failed:', result.error);
    showToast(msg);
    return;
  }

  const sbUser = result.user;
  if (!sbUser) {
    if (btn) { btn.disabled = false; btn.textContent = 'Sign In'; }
    showToast('Sign in failed. Please try again.');
    return;
  }

  // Returning user — pull their stored name from Supabase profile, fall back to email prefix
  const profile = await App.Supabase.getProfile(sbUser.id);
  const name    = profile?.name || sbUser.user_metadata?.full_name || sbUser.email.split('@')[0];
  await completeAuthOnboarding(sbUser, name, sbUser.email);
}

// ── Sign-up success screen — sends user back to sign-in ───────────────────────
function _showSignUpSuccess(name, email) {
  const card = document.getElementById('obCard');
  if (!card) return;
  card.innerHTML = `
  <div style="text-align:center;padding:8px 0">
    <div style="font-size:3rem;margin-bottom:16px">🎉</div>
    <div style="color:#fff;font-weight:800;font-size:1.1rem;margin-bottom:8px">Account created!</div>
    <div style="${_labelStyle};font-size:.85rem;line-height:1.6;margin-bottom:6px">Welcome, <strong style="color:var(--gold)">${name.split(' ')[0]}</strong>.</div>
    <div style="${_labelStyle};font-size:.83rem;line-height:1.6;margin-bottom:20px">Your account is ready. Sign in below to begin your preparation.</div>
    <div style="background:rgba(52,211,153,.1);border:1px solid rgba(52,211,153,.3);border-radius:10px;padding:12px 16px;margin-bottom:20px;font-size:.8rem;color:rgba(52,211,153,.9);line-height:1.8;text-align:left">
      ✓ Account registered for <strong>${email}</strong><br>
      ✓ Profile saved<br>
      ✓ Ready to sign in
    </div>
    <button class="btn btn-gold btn-lg btn-full" onclick="obSwitchAuth('signin')">Sign In to Continue</button>
  </div>`;
}

// ── Google Sign-In (Coming Soon) ──────────────────────────────────────────────
async function obGoogleSignIn() {
  showToast('Google Sign-In is coming soon. Please use email and password for now.');
}

// ── Forgot Password ───────────────────────────────────────────────────────────
async function obDoReset() {
  const email = document.getElementById('obEmail')?.value.trim().toLowerCase();
  const btn   = document.getElementById('obAuthBtn');

  if (!email || !email.includes('@')) { showToast('Please enter your email address.'); return; }

  if (btn) { btn.disabled = true; btn.textContent = 'Sending…'; }

  const sdkReady = await App.Supabase.waitForSdk(3000);
  if (!sdkReady) {
    if (btn) { btn.disabled = false; btn.textContent = 'Send Reset Link'; }
    showToast('Unable to connect. Check your internet and try again.');
    return;
  }

  const result = await App.Supabase.resetPassword(email);
  if (btn) { btn.disabled = false; btn.textContent = 'Send Reset Link'; }
  if (result.ok) {
    showToast('Reset link sent — check your inbox.');
    obSwitchAuth('signin');
  } else {
    showToast(result.error || 'Could not send reset link. Please try again.');
  }
}

// ── Complete onboarding after any successful auth ─────────────────────────────
// upsertProfile() resolves (never rejects) even on failure, returning
// {ok:false, error} — a plain .catch() on the caller never sees that.
// This wrapper actually checks .ok, retries once after a short delay
// (handles transient network blips right after signup), and if it still
// fails, stores the attempt locally so the next app boot retries it
// automatically instead of the profile being silently lost forever.
async function _upsertProfileWithRetry(userId, profileData) {
  let result = await App.Supabase.upsertProfile(userId, profileData);
  if (!result.ok) {
    console.warn('[RMH] Profile save failed, retrying in 2s:', result.error);
    await new Promise(r => setTimeout(r, 2000));
    result = await App.Supabase.upsertProfile(userId, profileData);
  }
  if (!result.ok) {
    console.error('[RMH] Profile save failed after retry — will retry on next app open:', result.error);
    App.Storage.set('pendingProfileSync', { userId, profileData, failedAt: Date.now() });
  } else {
    App.Storage.del('pendingProfileSync');
  }
  return result;
}

// Called once at app boot. If a previous profile save never succeeded,
// try again now — silent, no UI, just closes the gap automatically.
async function _retryPendingProfileSync() {
  const pending = App.Storage.get('pendingProfileSync', null);
  if (!pending || !pending.userId) return;
  const result = await App.Supabase.upsertProfile(pending.userId, pending.profileData);
  if (result.ok) {
    App.Storage.del('pendingProfileSync');
    console.log('[RMH] Pending profile sync recovered successfully');
  }
  // If it fails again, leave the flag in place — will retry again next boot
}

async function completeAuthOnboarding(sbUser, name, email) {
  try {
    const displayName = name || email.split('@')[0];

    // Fetch existing profile first — preserve pro_tier if already set
    const existingProfile = await App.Supabase.getProfile(sbUser.id);
    const existingTier = existingProfile?.pro_tier || 'none';

    // Save to Supabase — is_pro is a generated column, never write to it directly
    _upsertProfileWithRetry(sbUser.id, {
      email,
      name: displayName,
      pro_tier:     existingTier,
      exam:         STATE.obData.exam        ?? existingProfile?.exam        ?? -1,
      institution:  STATE.obData.institution ?? existingProfile?.institution ?? -1,
      exam_date:    STATE.obData.examDate    || existingProfile?.exam_date   || '',
      referral_code: existingProfile?.referral_code || ('RMH' + Math.random().toString(36).substr(2, 6).toUpperCase()),
      referral_source: existingProfile?.referral_source || REFERRAL_SOURCES[STATE.obData.referral] || 'Unknown',
    });

    // Save locally — this is what makes returning users skip onboarding
    const isAlreadyPro = existingTier !== 'none';
    App.Auth.saveLocalUser({
      name: displayName, email, isPro: isAlreadyPro, proTier: existingTier, supabaseId: sbUser.id,
      exam:        STATE.obData.exam        ?? existingProfile?.exam        ?? -1,
      institution: STATE.obData.institution ?? existingProfile?.institution ?? -1,
      examDate:    STATE.obData.examDate    || existingProfile?.exam_date   || '',
      daysLeft:    STATE.obData.daysLeft    || 0,
      created:     Date.now(),
      referralCode: existingProfile?.referral_code || ('RMH' + Math.random().toString(36).substr(2, 6).toUpperCase()),
      referralSource: existingProfile?.referral_source || REFERRAL_SOURCES[STATE.obData.referral] || 'Unknown',
    });
    STATE.isPro   = isAlreadyPro;
    STATE.proTier = existingTier;
    App.Analytics.track(App.Analytics.Events.ONBOARD_COMPLETE, { exam: STATE.obData.exam });

    // Record the referral — only for a genuinely new profile (existingProfile
    // was null), and only if they arrived via a ?ref=CODE link. Fire-and-forget:
    // never blocks or breaks onboarding if it fails.
    if (!existingProfile && PENDING_REFERRAL_CODE) {
      App.Supabase.getClient().rpc('record_referral', {
        p_code: PENDING_REFERRAL_CODE,
        p_new_user_id: sbUser.id
      }).then(() => _clearPendingReferral())
        .catch(e => console.warn('[RMH] record_referral failed:', e.message));
    }

    document.getElementById('onboarding').classList.add('hidden');
    document.getElementById('appShell').classList.remove('hidden');
    bindNavEvents();
    openInitialPage();
    showToast('Welcome back, ' + displayName.split(' ')[0] + '.');
    setTimeout(() => App.Auth.initCloudSession(), 500);
  } catch (e) {
    console.warn('[RMH] completeAuthOnboarding failed:', e.message);
    showToast('Something went wrong. Please try again.');
  }
}

// ── Complete onboarding locally (fallback — no Supabase) ─────────────────────
function obCompleteLocally(name, email) {
  const user = {
    name, email, isPro: false,
    ...STATE.obData,
    created: Date.now(),
    referralCode: 'RMH' + Math.random().toString(36).substr(2, 6).toUpperCase()
  };
  App.Auth.saveLocalUser(user);
  STATE.isPro = false;
  App.Analytics.track(App.Analytics.Events.ONBOARD_COMPLETE, { exam: user.exam });
  document.getElementById('onboarding').classList.add('hidden');
  document.getElementById('appShell').classList.remove('hidden');
  bindNavEvents();
  openInitialPage();
  showToast(`Welcome, ${name.split(' ')[0]}. Your preparation starts now.`);
}

// ── Handle Google OAuth redirect on app load ──────────────────────────────────
// When the user returns from Google sign-in, Supabase sets a session in the URL.
// initApp detects this via isAuthenticated() after the SDK restores the session.
async function handleAuthRedirect() {
  if (!App.Supabase.isReady()) return;
  try {
    const client = App.Supabase.getClient();
    if (!client) return;

    const { data, error } = await client.auth.getSession();
    if (error || !data?.session) return;

    const sbUser = data.session.user;
    if (!sbUser) return;

    if (App.Auth.isAuthenticated()) {
      // Already local — just sync cloud profile silently
      await App.Auth.syncCloudProfileToLocal(sbUser);
      return;
    }

    // Google OAuth first login — we may not have obData (exam/institution)
    // Default to -1 so the user can update in Settings later
    const name  = sbUser.user_metadata?.full_name || sbUser.user_metadata?.name || sbUser.email.split('@')[0];
    const email = sbUser.email;

    await completeAuthOnboarding(sbUser, name, email);

    // Clean the URL hash so it doesn't persist on refresh
    if (window.history?.replaceState) {
      window.history.replaceState(null, '', window.location.pathname);
    }
  } catch (e) {
    console.warn('[RMH] handleAuthRedirect failed:', e.message);
  }
}

// ══════════════════════════════════════════════════════
// APP INIT
// ══════════════════════════════════════════════════════
// ── Deep linking ─────────────────────────────────────────────────────────
// Lets external links (email, WhatsApp, social) point straight at a
// specific feature instead of always landing on Home — e.g.
// remasteryhub.online/?page=guardian opens Guardian directly.
// Only real, existing pages/features are allowed; anything else silently
// falls back to Home rather than erroring.
const DEEP_LINK_PAGES = ['home','quiz','mock','library','pro','settings','progress','guardian','blueprint'];

function getDeepLinkTarget() {
  try {
    const requested = new URLSearchParams(window.location.search).get('page');
    if (requested && DEEP_LINK_PAGES.includes(requested)) return requested;
  } catch (e) { /* malformed URL — ignore, fall back to home */ }
  return null;
}

// ── Referral capture ─────────────────────────────────────────────────────
// Reads ?ref=CODE from the URL a new visitor arrived with (e.g. a link
// someone copied from their Referral Programme card) and holds onto it
// through onboarding, since signup can take several steps to complete.
// Persisted to localStorage (not just an in-memory constant) because the
// Google OAuth flow does a full-page redirect to accounts.google.com and
// back to window.location.origin — which strips the ?ref= param entirely.
// Without persistence, every OAuth signup would silently lose the referral
// regardless of anything else being fixed.
function getReferralCodeFromURL() {
  try {
    const fromUrl = new URLSearchParams(window.location.search).get('ref');
    if (fromUrl) {
      const code = fromUrl.trim().toUpperCase();
      localStorage.setItem('rmh_pending_referral', code);
      return code;
    }
    return localStorage.getItem('rmh_pending_referral') || null;
  } catch (e) { return null; }
}
const PENDING_REFERRAL_CODE = getReferralCodeFromURL();
function _clearPendingReferral() {
  try { localStorage.removeItem('rmh_pending_referral'); } catch (e) {}
}

// Opens the deep-linked page if the URL requested one and it's valid,
// otherwise defaults to Home. Cleans the ?page= param out of the address
// bar afterward so it doesn't linger or get reapplied on later navigation.
function openInitialPage() {
  const target = getDeepLinkTarget();
  openPage(target || 'home');
  if (target && window.history?.replaceState) {
    window.history.replaceState(null, '', window.location.pathname);
  }
}

async function initApp() {
  App.ErrorBoundary.install();
  App.Storage.migrate();

  // Handle Yoco payment redirect returns (payment=success/cancelled/failed in URL)
  App.Payment.handlePaymentReturn();

  if (!App.Auth.isAuthenticated()) {
    // No local device flag — before assuming "new user", check for a
    // genuine Supabase cloud session. This covers cases where THIS
    // browsing context has no local flag (e.g. an email/WhatsApp link
    // opened in a different browser or in-app webview than the one the
    // user normally signs in from) but they're actually still logged in.
    // Adds one network round-trip only for this specific case — the
    // already-authenticated fast path below is untouched.
    try {
      await App.Auth.initCloudSession();
    } catch (e) {
      console.warn('[RMH] Cloud session fallback check failed:', e.message);
    }
  }

  if (App.Auth.isAuthenticated()) {
    // Returning user — local flag now present (either it always was,
    // or the cloud-session check above just established it)
    STATE.isPro   = App.Auth.isPro();
    STATE.proTier = App.Auth.getProTier();
    const _localU = App.Auth.getLocalUser() || {};
    STATE.bonusAccessUntil = _localU.bonusAccessUntil || null;
    STATE.bonusTier = _localU.bonusTier || null;
    STATE.proExpiresAt = _localU.proExpiresAt || null;
    // These five were only ever set inside syncCloudProfileToLocal, which
    // needs a live network call to run — meaning an OFFLINE boot left them
    // undefined (falsy) even for a fully-Pro user whose last sync had them
    // all true. That's what made offline show "Entry access", lock
    // Guardian, and hide the admin dashboard regardless of real tier.
    // Restoring them here from the same cached object fixes all three at
    // once, since they all read from these STATE fields.
    STATE.hasGuide = !!_localU.hasGuide;
    STATE.hasExplanations = !!_localU.hasExplanations;
    STATE.hasCoachFeedback = !!_localU.hasCoachFeedback;
    STATE.hasHardSim = !!_localU.hasHardSim;
    STATE.isAdmin = !!_localU.isAdmin;
    STATE.stuckTopic = _localU.stuckTopic || null;
    STATE.stuckTopicExam = _localU.stuckTopicExam || null;
    STATE.stuckStreak = _localU.stuckStreak || 0;
    STATE.stuckFlaggedAt = _localU.stuckFlaggedAt || null;
    STATE.disAccepted = true;
    if (STATE.isPro) document.getElementById('proChip').classList.remove('hidden');
    document.getElementById('onboarding').classList.add('hidden');
    document.getElementById('appShell').classList.remove('hidden');
    bindNavEvents();
    openInitialPage();
    // Background cloud session refresh — fires after UI is visible and interactive
    setTimeout(() => App.Auth.initCloudSession(), 0);
    setTimeout(() => _syncReadinessSignals(), 1200);
    setTimeout(() => _registerDeviceSession(), 1500);
    setInterval(() => _checkDeviceStillActive(), 5 * 60 * 1000); // every 5 min
    // Presence heartbeat — every user, not just Pro, since traffic tracking
    // needs to reflect actual usage across the whole base. Separate from
    // last_active_at (which only updates when a study session finishes) —
    // this updates purely from having the app open, immediately on load and
    // then every 2 minutes, so "online now" on the admin dashboard is a real
    // reflection of current traffic rather than a delayed/stale snapshot.
    setTimeout(() => _sendPresenceHeartbeat(), 2000);
    setInterval(() => _sendPresenceHeartbeat(), 2 * 60 * 1000);
    _maybeAutoStartTour();
  } else {
    // Genuinely new user, or logged out — show onboarding.
    // BUT: if a recovery/auth marker is still sitting in the URL, this is
    // NOT a new user — it's someone whose password-reset (or OAuth) session
    // exchange just hasn't finished resolving yet. Showing onboarding here
    // was the exact bug reported by Maria, Mmabatho, and Pholoso: the
    // primary recovery check can miss a link depending on which auth flow
    // Supabase used, and on a fresh browser session isAuthenticated() is
    // false either way — so without this guard, an unresolved reset always
    // fell through to onboarding. One retry after a short wait is enough
    // for the SDK to finish the exchange in the near-total majority of cases.
    const _qs = new URLSearchParams(window.location.search);
    const _stillResolving = _qs.get('reset') === '1' || _qs.get('code') !== null ||
                             (window.location.hash || '').includes('type=recovery');
    if (_stillResolving && !STATE._recoveryRetried) {
      STATE._recoveryRetried = true;
      setTimeout(async () => {
        await App.Auth.initCloudSession();
        initApp();
      }, 800);
      return;
    }
    STATE.obStep = 0;
    STATE.disAccepted = false;
    document.getElementById('onboarding').classList.remove('hidden');
    renderObStep();
  }
}

// Best-effort background sync of readiness signals to `profiles`, once per
// app load. This is what makes exam-countdown lifecycle emails possible —
// the existing sync-to-loops-ts webhook fires automatically on any profiles
// row update and forwards these fields on to Loops. No new webhook needed,
// this just gives the existing one real data to send.
let _readinessSyncedThisSession = false;
// Lightweight presence heartbeat — every authenticated user, every 2 min,
// updates last_seen_at purely from having the app open. This is what makes
// "online now" on the admin dashboard a genuine live number rather than a
// proxy pulled from some other, less frequent sync.
// ── Feature usage tracking ──────────────────────────────────────────────
// Answers "which feature actually dominates time spent" for marketing.
// App.Analytics.track() has always been a no-op stub — this is the real
// pipeline. Deliberately attributes elapsed time to exactly ONE feature at
// a time (whichever was most recently set as active), so navigating from
// Home into a Crash Pack session doesn't double-count that time under both
// "home" and "crash_pack" — every second belongs to exactly one bucket.
let _activeFeature = null;
let _activeFeatureStart = null;

function _setActiveFeature(featureKey) {
  _flushActiveFeature();
  _activeFeature = featureKey;
  _activeFeatureStart = Date.now();
}

function _flushActiveFeature(reliable) {
  if (!_activeFeature || !_activeFeatureStart) return;
  const seconds = Math.round((Date.now() - _activeFeatureStart) / 1000);
  const key = _activeFeature;
  _activeFeature = null;
  _activeFeatureStart = null;
  if (seconds < 1) return; // near-instant transitions are just noise, not real usage
  if (!App.Supabase.isReady()) return;

  if (reliable) {
    // Called from visibilitychange(hidden)/pagehide — a normal async RPC
    // call here is unreliable, since the browser can (and often does) kill
    // an in-flight fetch the instant the page starts tearing down. This was
    // very likely the actual reason zero data had ever been recorded despite
    // real traffic: most sessions end by backgrounding or closing the tab,
    // not by navigating to a second in-app feature.
    // navigator.sendBeacon() is the usual tool for this, but it can't set
    // custom headers at all — and Supabase's REST API requires an `apikey`
    // header on every request, so a bare beacon would be rejected outright.
    // fetch() with keepalive:true gives the same "survives teardown"
    // guarantee while keeping full header control, so it's the correct tool
    // here, not sendBeacon.
    try {
      const url = App.Supabase.getUrl();
      const anonKey = App.Supabase.getAnonKey();
      fetch(`${url}/rest/v1/rpc/track_feature_usage`, {
        method: 'POST',
        keepalive: true,
        headers: {
          'Content-Type': 'application/json',
          'apikey': anonKey,
          'Authorization': `Bearer ${anonKey}`,
        },
        body: JSON.stringify({ p_feature_key: key, p_seconds: seconds }),
      }).catch(e => console.warn('[RMH] reliable feature flush failed:', e.message));
    } catch (e) {
      console.warn('[RMH] reliable feature flush failed:', e.message);
    }
    return;
  }

  try {
    App.Supabase.getClient().rpc('track_feature_usage', { p_feature_key: key, p_seconds: seconds })
      .then(({ error }) => { if (error) console.warn('[RMH] feature usage tracking failed:', error.message); })
      .catch(e => console.warn('[RMH] feature usage tracking failed:', e.message));
  } catch (e) {
    console.warn('[RMH] feature usage tracking failed:', e.message);
  }
}

// Backgrounding the app (switching tabs, locking the phone) shouldn't count
// as active feature time — flush on hide, and don't auto-resume on return;
// the next real navigation will naturally start a fresh, accurate timer.
// Both listeners use reliable delivery (see _flushActiveFeature above) since
// this is exactly the moment a normal fetch call is most likely to be
// silently killed by the browser before it completes.
document.addEventListener('visibilitychange', () => {
  if (document.hidden) _flushActiveFeature(true);
});
window.addEventListener('pagehide', () => _flushActiveFeature(true));

async function _sendPresenceHeartbeat() {
  try {
    const u = App.Auth.getLocalUser();
    if (!u || !u.supabaseId || !App.Supabase.isReady()) return;
    await App.Supabase.getClient().from('profiles')
      .update({ last_seen_at: new Date().toISOString() })
      .eq('id', u.supabaseId);
  } catch (e) {
    // Best-effort only — a missed heartbeat just means one fewer data point,
    // never worth surfacing to the user.
  }
}

// ══════════════════════════════════════════════════════
// GUIDED APP TOUR
// ══════════════════════════════════════════════════════
// A real multi-page walkthrough, not a single tooltip — moves the user
// through Home, Quiz, Sim, Library, and Guardian, highlighting the actual
// nav item or card for each with a spotlight, and a fixed bottom-sheet
// bubble for the explanation (rather than a dynamically-positioned tooltip,
// which is fragile on small screens — a bottom sheet stays put while only
// the spotlight moves, which is simpler and far more reliable on mobile).
// `optional:true` steps are skipped gracefully if their target doesn't
// exist right now (e.g. the free-only Mixed Practice card, for a Pro tester).
const TOUR_STEPS = [
  { page: null, target: null,
    title: 'Welcome to RE Mastery Hub 👋',
    text: 'A real tour — every step opens the actual feature, not just a picture of it. About 90 seconds. Skip anytime.' },
  { page: 'home', target: 'tourMixedPractice', optional: true,
    title: 'Free Daily Practice',
    text: 'Every day you get free questions from both RE5 and RE1 — no payment needed. The very first time, you get 17.' },
  { page: 'home', target: 'tourWeakAreas',
    title: 'Personalised Coaching',
    text: 'The app automatically spots the exact topic holding your score back — not a guess, based on your real answers.' },
  { page: 'home', target: 'tourFactDisk',
    title: 'RE Fact Disk',
    text: 'The 50 most-tested facts, flashcard style — perfect for a quick review anywhere, anytime.' },
  { page: 'home', target: 'tourCrashPack', action: () => showCrashPack(),
    title: 'Crash Pack',
    text: 'Rapid-fire questions, Top 50 facts, and your Day-Before checklist — this is exactly what opens when you tap it. Save this for the final week before your exam.' },
  { page: 'quiz', target: 'bnav-quiz',
    title: 'Quiz Practice',
    text: 'Practice by topic or mixed — pick exactly what you want to work on right now.' },
  { page: 'mock', target: 'bnav-mock',
    title: 'Exam Simulations',
    text: 'Full timed simulations under real exam conditions — including Hard Sim once you\'re ready to push further.' },
  { page: 'library', target: 'bnav-library',
    title: 'Study Library',
    text: 'Every topic, fully written out and mapped to the real syllabus.' },
  { page: 'library', target: 'tourVideoLessons',
    title: 'Video Lessons',
    text: 'Full video walkthroughs, topic by topic — more are added every week. Open any topic below to watch.' },
  { page: 'library', target: 'tourStudyGuides',
    title: 'Official Exam Success Guides',
    text: 'Complete learner guides mapped to every official Qualifying Criteria — a free preview for everyone, full guide unlocks with the matching Pro plan.' },
  { page: 'guardian', target: 'bnav-guardian',
    title: 'RE Guardian AI',
    text: 'Your personal AI coach — ask it to explain anything, drill your weak areas, or quiz you like a real examiner.' },
  { page: 'blueprint', target: 'tourBlueprintExam',
    title: 'The Blueprint Exam',
    text: '★ The most important simulation in the app. Every task weighted exactly like the real FSCA exam — not a random quiz, a true replica of exam day.' },
  { page: 'pro', target: 'tourProPlans',
    title: 'Pro Plans',
    text: 'Five options, all once-off — no auto-renewal. Pick the one that matches how long you\'ve got until exam day.' },
  { page: 'settings', target: null,
    title: 'Settings',
    text: 'Update your exam date here — it keeps every recommendation accurate. Also where you manage your account.' },
  { page: null, target: null,
    title: 'You\'re All Set 🎉',
    text: 'That\'s the whole app. You can replay this tour anytime — the compass on Home is always there.' },
];

let _tourStep = 0;
let _tourActive = false;

function startTour() {
  _tourStep = 0;
  _tourActive = true;
  _renderTourStep();
}

function _tourEnd(markComplete) {
  _tourActive = false;
  const overlay = document.getElementById('tourOverlay');
  if (overlay) overlay.remove();
  closeModal(); // in case the Crash Pack step's modal is still open
  if (markComplete) {
    try { localStorage.setItem('rmh_tour_completed', '1'); } catch (e) {}
  }
}

function tourNext() {
  _tourStep++;
  if (_tourStep >= TOUR_STEPS.length) { _tourEnd(true); return; }
  _renderTourStep();
}
function tourBack() {
  if (_tourStep <= 0) return;
  _tourStep--;
  _renderTourStep();
}
function tourSkip() { _tourEnd(true); }

async function _renderTourStep() {
  if (!_tourActive) return;
  const step = TOUR_STEPS[_tourStep];
  if (!step) { _tourEnd(true); return; }

  // Clears any modal a previous step's action opened (e.g. Crash Pack) —
  // without this, it would sit open and cover subsequent spotlight targets.
  if (!step.action) closeModal();

  if (step.page && STATE.page !== step.page) {
    if (step.page === 'guardian') App.Guardian.reset();
    openPage(step.page);
    await new Promise(r => setTimeout(r, 250)); // let the new page finish rendering
  }

  if (step.action) {
    try { step.action(); } catch (e) { /* never let a broken action stall the tour */ }
    await new Promise(r => setTimeout(r, 200)); // let the modal/action finish rendering
  }

  let targetEl = step.target ? document.getElementById(step.target) : null;
  if (step.target && !targetEl && step.optional) { tourNext(); return; }

  _paintTourOverlay(step, targetEl);
}

function _paintTourOverlay(step, targetEl) {
  let overlay = document.getElementById('tourOverlay');
  if (!overlay) {
    overlay = document.createElement('div');
    overlay.id = 'tourOverlay';
    document.body.appendChild(overlay);
  }

  if (targetEl) targetEl.scrollIntoView({ block: 'center', behavior: 'smooth' });

  const isFirst = _tourStep === 0;
  const isLast  = _tourStep === TOUR_STEPS.length - 1;
  const stepNum = _tourStep + 1;

  const paint = () => {
    const rect = targetEl ? targetEl.getBoundingClientRect() : null;
    const vh = window.innerHeight;
    const vw = window.innerWidth;

    let spotStyle = `position:fixed;inset:0;background:rgba(10,22,40,.78);z-index:9999`;
    // Centred, no tail, when there's no specific target (welcome/finish steps)
    let bubblePosStyle = 'top:50%;left:16px;right:16px;transform:translateY(-50%)';
    let tailHTML = '';

    if (rect) {
      spotStyle = `position:fixed;top:${rect.top - 6}px;left:${rect.left - 6}px;width:${rect.width + 12}px;height:${rect.height + 12}px;border-radius:12px;box-shadow:0 0 0 9999px rgba(10,22,40,.78);pointer-events:none;z-index:9999;transition:top .3s ease,left .3s ease,width .3s ease,height .3s ease;border:2px solid var(--gold)`;

      // The actual fix: the bubble must never land on the same half of the
      // screen as what it's explaining. Bottom-nav targets sit at the very
      // bottom of the viewport — a fixed bottom-sheet bubble would cover
      // them completely, which is exactly the bug being fixed here.
      const targetInUpperHalf = rect.top < vh * 0.5;
      const tailX = Math.min(Math.max(rect.left + rect.width / 2, 40), vw - 40);

      if (targetInUpperHalf) {
        // Target is up top → bubble sits at the bottom, tail points UP
        bubblePosStyle = 'left:16px;right:16px;bottom:16px';
        tailHTML = `<div style="position:absolute;top:-9px;left:${tailX - 25}px;width:18px;height:18px;background:linear-gradient(145deg,#ffffff,#f7f5f0);transform:rotate(45deg);border-radius:4px 0 0 0;box-shadow:-2px -2px 4px rgba(0,0,0,.06)"></div>`;
      } else {
        // Target is down low (incl. bottom nav) → bubble sits at the top, tail points DOWN
        bubblePosStyle = 'left:16px;right:16px;top:16px';
        tailHTML = `<div style="position:absolute;bottom:-9px;left:${tailX - 25}px;width:18px;height:18px;background:linear-gradient(145deg,#ffffff,#f7f5f0);transform:rotate(45deg);border-radius:0 0 4px 0;box-shadow:2px 2px 4px rgba(0,0,0,.06)"></div>`;
      }
    }

    overlay.innerHTML = `
      <div style="${spotStyle}"></div>
      <div class="tour-bubble-3d" style="position:fixed;${bubblePosStyle};z-index:10000">
        ${tailHTML}
        <div class="f-xs c-muted mb6">Step ${stepNum} of ${TOUR_STEPS.length}</div>
        <div class="fw8 f-lg mb6" style="color:var(--navy)">${step.title}</div>
        <div class="f-sm lh-16 c-muted mb16">${step.text}</div>
        <div class="fac gap8">
          ${!isFirst ? `<button class="btn btn-outline btn-sm" onclick="tourBack()">Back</button>` : ''}
          <button class="btn btn-ghost btn-sm" onclick="tourSkip()" style="color:var(--text3)">Skip Tour</button>
          <div style="flex:1"></div>
          <button class="btn btn-gold btn-sm" onclick="tourNext()">${isLast ? 'Finish' : 'Next →'}</button>
        </div>
      </div>`;
  };
  paint();
  if (targetEl) setTimeout(paint, 350); // repaint once the smooth-scroll has settled
}

// Auto-trigger once for anyone who hasn't seen it — both brand-new signups
// AND existing users get this, on purpose: the request that led to this
// feature was current users not understanding what's already there, not
// just future ones. A "Replay Tour" button in Settings covers everyone
// after this first automatic showing.
function _maybeAutoStartTour() {
  try {
    if (localStorage.getItem('rmh_tour_completed')) return;
    if (STATE.page !== 'home') return;
    setTimeout(() => startTour(), 900);
  } catch (e) {}
}

async function _syncReadinessSignals() {
  if (_readinessSyncedThisSession) return;
  try {
    const u = App.Auth.getLocalUser() || {};
    if (!u.supabaseId || !App.Supabase.isReady()) return;
    const examTypes = u.exam === 1 ? ['RE1'] : u.exam === 2 ? ['RE5', 'RE1'] : ['RE5'];
    const payloads = examTypes.map(t => App.BlueprintSignals.getEmailAutomationPayload(t));
    // For combo users, sync whichever exam they're currently weaker in —
    // that's the more actionable signal for an urgency email.
    const chosen = payloads.reduce((a, b) => (b.readinessScore < a.readinessScore ? b : a));
    const { error } = await App.Supabase.getClient().from('profiles').update({
      readiness_score: chosen.readinessScore,
      weakest_task:    chosen.weakestTask,
    }).eq('id', u.supabaseId);
    if (error) { console.warn('[RMH] readiness signal sync failed:', error.message); return; }
    _readinessSyncedThisSession = true;
  } catch (e) {
    console.warn('[RMH] readiness signal sync failed:', e.message);
  }
}

// ── Stuck-topic coaching signal ─────────────────────────────────────────────
// Runs after every quiz/sim session (not once-per-app-load like readiness),
// since the whole point is catching the pattern the moment a fresh session
// confirms it. Cooldown: once a topic is flagged, it stays flagged (no
// re-notification) until either 7 days pass with a genuinely different
// topic qualifying, or the flagged topic's accuracy improves past the bar
// and clears on its own. STATE._justFlaggedStuck marks a same-turn "new"
// detection so the result screen can show the high-resonance version once,
// not the persistent Home-card version every time.
async function _syncStuckTopicSignal() {
  try {
    const u = App.Auth.getLocalUser();
    if (!u || !u.supabaseId || !App.Supabase.isReady()) return;
    const examTypes = u.exam === 1 ? ['RE1'] : u.exam === 2 ? ['RE5', 'RE1'] : ['RE5'];

    let best = null;
    examTypes.forEach(ex => {
      const c = App.Progress.computeStuckTopic(ex);
      if (c && (!best || c.streak > best.streak)) best = { ...c, exam: ex };
    });

    const nowISO = new Date().toISOString();
    const updates = { last_active_at: nowISO }; // always current, no cooldown needed
    STATE._justFlaggedStuck = false;

    const currentlyFlagged = STATE.stuckTopic;
    const cooldownPassed = !STATE.stuckFlaggedAt ||
      (Date.now() - new Date(STATE.stuckFlaggedAt).getTime()) > 7 * 86400000;

    if (best) {
      const isNewTopic = best.t !== currentlyFlagged;
      if (!currentlyFlagged || (isNewTopic && cooldownPassed)) {
        updates.stuck_topic = best.t;
        updates.stuck_topic_exam = best.exam;
        updates.stuck_streak = best.streak;
        updates.stuck_flagged_at = nowISO;
        STATE.stuckTopic = best.t;
        STATE.stuckTopicExam = best.exam;
        STATE.stuckStreak = best.streak;
        STATE.stuckFlaggedAt = nowISO;
        STATE._justFlaggedStuck = isNewTopic;
      }
    } else if (currentlyFlagged) {
      // No longer meets the bar on the exam(s) they study — they improved.
      updates.stuck_topic = null;
      updates.stuck_topic_exam = null;
      updates.stuck_streak = 0;
      STATE.stuckTopic = null;
      STATE.stuckTopicExam = null;
      STATE.stuckStreak = 0;
    }

    const { error } = await App.Supabase.getClient().from('profiles').update(updates).eq('id', u.supabaseId);
    if (error) { console.warn('[RMH] stuck-topic sync failed:', error.message); return; }

    const localU = App.Auth.getLocalUser();
    if (localU) {
      localU.stuckTopic = STATE.stuckTopic;
      localU.stuckTopicExam = STATE.stuckTopicExam;
      localU.stuckStreak = STATE.stuckStreak;
      localU.stuckFlaggedAt = STATE.stuckFlaggedAt;
      App.Auth.saveLocalUser(localU);
    }
  } catch (e) {
    console.warn('[RMH] stuck-topic sync failed:', e.message);
  }
}


// device_id is a random UUID persisted in localStorage — not a hardened
// fingerprint (clearing browser storage resets it), but a reasonable,
// non-invasive deterrent for casual account sharing, consistent with how
// the app already persists free-tier quotas locally.
function _getDeviceId() {
  let id = localStorage.getItem('rmh_device_id');
  if (!id) {
    id = (crypto.randomUUID ? crypto.randomUUID() : `${Date.now()}-${Math.random().toString(36).slice(2)}`);
    localStorage.setItem('rmh_device_id', id);
  }
  return id;
}
function _getDeviceLabel() {
  const ua = navigator.userAgent || '';
  const os = /Android/i.test(ua) ? 'Android' : /iPhone|iPad/i.test(ua) ? 'iOS' : /Mac/i.test(ua) ? 'Mac' : /Win/i.test(ua) ? 'Windows' : 'Device';
  const browser = /Chrome/i.test(ua) ? 'Chrome' : /Safari/i.test(ua) && !/Chrome/i.test(ua) ? 'Safari' : /Firefox/i.test(ua) ? 'Firefox' : 'Browser';
  return `${browser} on ${os}`;
}
let _deviceRegisteredThisSession = false;
async function _registerDeviceSession() {
  if (_deviceRegisteredThisSession || !STATE.isPro) return;
  try {
    if (!App.Supabase.isReady()) return;
    const { data, error } = await App.Supabase.getClient().rpc('register_device_session', {
      p_device_id: _getDeviceId(), p_device_label: _getDeviceLabel(), p_limit: 2,
    });
    if (error) { console.warn('[RMH] device registration failed:', error.message); return; }
    _deviceRegisteredThisSession = true;
    if (data?.kicked) {
      console.log('[RMH] device slot taken — signed out on:', data.kicked);
    }
  } catch (e) {
    console.warn('[RMH] device registration failed:', e.message);
  }
}
// Periodic check — catches the case where THIS device gets kicked by a
// newer login elsewhere, without needing realtime infrastructure.
async function _checkDeviceStillActive() {
  if (!STATE.isPro) return;
  try {
    if (!App.Supabase.isReady()) return;
    const { data: stillActive, error } = await App.Supabase.getClient().rpc('is_device_active', {
      p_device_id: _getDeviceId(),
    });
    if (error) return;
    if (stillActive === false) {
      STATE.isPro = false;
      showToast("You've been signed out of Pro on this device — your account is active on 2 other devices.");
      openPage(STATE.page);
    }
  } catch (e) { /* best-effort only */ }
}

function bindNavEvents() {
  document.getElementById('settingsBtn').onclick = () => openPage('settings');
  document.getElementById('bnav-home').onclick    = () => openPage('home');
  document.getElementById('bnav-quiz').onclick    = () => openPage('quiz');
  document.getElementById('bnav-mock').onclick    = () => openPage('mock');
  document.getElementById('bnav-library').onclick = () => openPage('library');
  document.getElementById('bnav-guardian').onclick = () => { App.Guardian.reset(); openPage('guardian'); };
}

// ══════════════════════════════════════════════════════
// ROUTER — with error boundary on every page render
// ══════════════════════════════════════════════════════
function openPage(page) {
  clearCdTimer(); // always clear before any page switch
  STATE.page = page;
  document.querySelectorAll('.bnav-item').forEach(b => b.classList.remove('active'));
  const nb = document.getElementById('bnav-' + page);
  if (nb) nb.classList.add('active');

  // Baseline attribution by page — startTopicQuiz/startSim/startCrashQuiz/
  // startMixedPractice/showTop50 immediately override this to something
  // more specific where relevant, since they fire right after this in the
  // same user action. privacy/terms/admincomments are excluded — legal
  // pages and the admin-only view aren't "features" for this purpose.
  const FEATURE_MAP = {
    home: 'home', quiz: 'quiz_hub', mock: 'sim_hub', library: 'library',
    pro: 'pricing_page', settings: 'settings', progress: 'progress',
    guardian: 'guardian', blueprint: 'blueprint',
  };
  if (FEATURE_MAP[page]) _setActiveFeature(FEATURE_MAP[page]);

  const pages = {
    home:     rHome,
    quiz:     rQuizHome,
    mock:     rMockHome,
    library:  rLibrary,
    pro:      rPro,
    settings: rSettings,
    progress: rProgress,
    privacy:  rPrivacy,
    terms:    rTerms,
    guardian: rGuardian,
    blueprint: rBlueprint,
    admincomments: rAdminComments,
  };

  if (pages[page]) {
    const html = App.ErrorBoundary.wrap(() => pages[page](), 'Try returning to the home screen.');
    document.getElementById('pageContent').innerHTML = `<div class="anim-in">${html}</div>`;
    if (page === 'home') startCountdown();

    // Blueprint page gates real money (Pro tiers) — an already-open session
    // can be holding a stale proTier if it was upgraded in Supabase after
    // this session started. Force a fresh check here so a tier change is
    // reflected immediately, without requiring the user to fully reload.
    if (page === 'blueprint' && App.Supabase.isReady() && App.Auth.isAuthenticated()) {
      App.Supabase.getSession().then(session => {
        if (!session?.user) return;
        const priorTier = STATE.proTier;
        App.Auth.syncCloudProfileToLocal(session.user).then(() => {
          if (STATE.page === 'blueprint' && STATE.proTier !== priorTier) {
            openPage('blueprint'); // tier actually changed — re-render with fresh access
          }
        });
      }).catch(()=>{});
    }

    if (page === 'library') {
      setTimeout(() => initVideoSocialWidgets(), 100);
    }
    if (page === 'admincomments') {
      setTimeout(() => loadAdminComments(), 50);
    }

    if (page === 'guardian') {
      const chat = document.getElementById('guardianChat');
      if (chat) chat.scrollTop = chat.scrollHeight;
    }
    App.Analytics.track('page_view', { page });
  }
}

// ══════════════════════════════════════════════════════
// HOME PAGE
// ══════════════════════════════════════════════════════

// ── YouTube subscribe prompt ───────────────────────────────────────────────
// Deliberately NOT shown beside in-app video lessons: sending someone to
// YouTube mid-lesson trades study time for a subscriber. Shown only at
// natural pauses, dismissible, and never again once acted on.
function ytDismissed() {
  return App.Storage.get('ytSubPromptDone', false) === true;
}
function ytSubscribe() {
  App.Storage.set('ytSubPromptDone', true);
  window.open(App.Config.youtubeSubscribe, '_blank', 'noopener');
  showToast('Thanks for subscribing 🙏');
  const el = document.getElementById('ytSubCard'); if (el) el.remove();
}
function ytDismiss() {
  App.Storage.set('ytSubPromptDone', true);
  const el = document.getElementById('ytSubCard'); if (el) el.remove();
}
function ytSubCard(context) {
  if (ytDismissed()) return '';
  const line = context === 'results'
    ? 'Free video lessons walking through the topics you just practised.'
    : 'Free RE5 video lessons — new lesson added regularly.';
  return `
  <div id="ytSubCard" class="card p14 mb12" style="border:1.5px solid rgba(200,168,75,.3)">
    <div style="display:flex;align-items:center;gap:12px">
      <span style="font-size:1.4rem;flex-shrink:0">▶️</span>
      <div style="flex:1">
        <div class="fw7 f-sm" style="margin-bottom:2px">Watch on YouTube</div>
        <div class="f-xs c-muted lh-15">${line}</div>
      </div>
    </div>
    <div class="fac gap8 mt10">
      <button class="btn btn-sm" style="background:#FF0000;color:#fff;font-weight:700;flex:1" onclick="ytSubscribe()">Subscribe</button>
      <button class="btn btn-ghost btn-sm" onclick="ytDismiss()">Not now</button>
    </div>
  </div>`;
}


// RE5 and RE1 have DIFFERENT official pass marks (66% and 65% respectively,
// per the FSCA 2026 Preparation Guide). A single shared constant silently
// applied the wrong bar to one of them.
function passMarkFor(mode) {
  const m = String(mode || '');
  if (m.indexOf('RE1') === 0 || m.indexOf('RE1') > -1) return App.Config.passMarkRE1;
  return App.Config.passMarkRE5;
}

// Distinct, accurate status copy per tier — must never claim a feature the
// user doesn't actually have. Derived from the real capability flags rather
// than a single blanket "isPro" check, since Entry (R99) is Pro but missing
// explanations/coach/guide/Hard Sim, and temporary tiers (oneMonth/twoMonth/
// referral bonus) should show their real expiry rather than imply lifetime.
function _proStatusMessage() {
  const expiresAt = STATE.proExpiresAt || STATE.bonusAccessUntil || null;
  const expiryLabel = expiresAt
    ? new Date(expiresAt).toLocaleDateString('en-ZA', { day: 'numeric', month: 'short' })
    : null;

  if (!STATE.hasExplanations && !STATE.hasCoachFeedback && !STATE.hasHardSim) {
    return { title: 'You have Entry access', body: `Unlimited quizzes and full RE5 + RE1 content are unlocked${expiryLabel ? ` until ${expiryLabel}` : ''}. Upgrade to unlock answer explanations, RE Guardian coaching, and Hard Sim.` };
  }
  if (!STATE.hasGuide) {
    return { title: 'You have full access', body: `Quizzes, explanations, simulations and RE Guardian are all unlocked${expiryLabel ? ` until ${expiryLabel}` : ''}. Upgrade to Lifetime to add the downloadable Study Guide.` };
  }
  if (expiresAt) {
    return { title: 'You have full Pro access', body: `Everything is unlocked, including your Study Guide — active until ${expiryLabel}. Upgrade to Lifetime so it never expires.` };
  }
  if (STATE.proTier === 'combo') {
    return { title: 'You have full Combo access', body: `RE5 and RE1 are both fully unlocked, forever — quizzes, explanations, simulations, RE Guardian and both Study Guides. Keep practising consistently.` };
  }
  return { title: 'You have full Pro access', body: `All quizzes, simulations, explanations, RE Guardian and your Study Guide are unlocked for life. Keep practising consistently.` };
}

// ══════════════════════════════════════════════════════
// VIDEO LIKES & COMMENTS
// ══════════════════════════════════════════════════════
// Each video-lesson-block in the Library carries a data-video-id (the
// YouTube video ID — stable, unique, no separate key scheme needed). After
// any page that might contain videos renders, initVideoSocialWidgets()
// finds every such block, injects a like button + comment section as a
// sibling, and populates both from Supabase in two batched calls (never
// one call per video).
let _videoLikeCounts = {};   // { videoId: count }
let _videoUserLikes  = new Set(); // videoIds the current user has liked
let _videoComments   = {};   // { videoId: [ {author_name, comment_text, created_at}, ... ] }

async function initVideoSocialWidgets() {
  const blocks = document.querySelectorAll('.video-lesson-block[data-video-id]');
  if (!blocks.length) return;
  const videoIds = [...new Set([...blocks].map(b => b.dataset.videoId))];

  // Inject placeholder shells immediately so the UI doesn't jump once data arrives
  blocks.forEach(b => {
    if (b.nextElementSibling?.classList?.contains('video-social')) return; // already injected
    const vid = b.dataset.videoId;
    const shell = document.createElement('div');
    shell.className = 'video-social';
    shell.id = `vsocial-${vid}`;
    shell.style.cssText = 'margin-top:8px;margin-bottom:14px;padding:10px 12px;border-radius:0 0 10px 10px;background:rgba(10,22,40,.04);border:1px solid rgba(10,22,40,.08);border-top:none';
    shell.innerHTML = `<div class="fac gap10"><button class="btn btn-xs" id="likebtn-${vid}" onclick="toggleVideoLike('${vid}')" style="background:transparent;border:1px solid rgba(10,22,40,.15);color:var(--text2);font-size:.72rem;display:flex;align-items:center;gap:4px"><span id="likeicon-${vid}">🤍</span> <span id="likecount-${vid}">–</span></button><button class="btn btn-xs" onclick="toggleCommentBox('${vid}')" style="background:transparent;border:1px solid rgba(10,22,40,.15);color:var(--text2);font-size:.72rem">💬 <span id="commentcount-${vid}">–</span> comments</button></div>
    <div id="commentbox-${vid}" style="display:none;margin-top:10px">
      <textarea id="commentinput-${vid}" class="inp" style="min-height:56px;font-size:.78rem;resize:vertical" placeholder="Ask a question or share a thought about this lesson..." maxlength="500"></textarea>
      <div class="fac gap8 mt6"><button class="btn btn-navy btn-xs" onclick="submitComment('${vid}')">Post</button><span class="f-xs c-muted">Comments are reviewed before they appear publicly.</span></div>
      <div id="commentlist-${vid}" style="margin-top:10px"></div>
    </div>`;
    b.insertAdjacentElement('afterend', shell);
  });

  try {
    const client = App.Supabase.getClient();
    if (!client) return;

    const [{ data: counts }, { data: comments }] = await Promise.all([
      client.rpc('get_all_video_like_counts'),
      client.from('video_comments').select('video_key, author_name, comment_text, created_at')
        .in('video_key', videoIds).eq('status', 'approved').order('created_at', { ascending: false }),
    ]);

    _videoLikeCounts = {};
    (counts || []).forEach(r => { _videoLikeCounts[r.video_key] = r.like_count; });

    _videoComments = {};
    (comments || []).forEach(r => {
      (_videoComments[r.video_key] ||= []).push(r);
    });

    if (App.Supabase.isReady() && App.Auth.isAuthenticated()) {
      const { data: myLikes } = await client.from('video_likes').select('video_key').in('video_key', videoIds);
      _videoUserLikes = new Set((myLikes || []).map(r => r.video_key));
    }

    videoIds.forEach(vid => _renderVideoSocialState(vid));
  } catch (e) {
    console.warn('[RMH] video social load failed:', e.message);
  }
}

function _renderVideoSocialState(vid) {
  const likeCountEl = document.getElementById(`likecount-${vid}`);
  const likeIconEl  = document.getElementById(`likeicon-${vid}`);
  const commentCountEl = document.getElementById(`commentcount-${vid}`);
  const commentListEl  = document.getElementById(`commentlist-${vid}`);
  if (likeCountEl) likeCountEl.textContent = String(_videoLikeCounts[vid] || 0);
  if (likeIconEl)  likeIconEl.textContent  = _videoUserLikes.has(vid) ? '❤️' : '🤍';
  const comments = _videoComments[vid] || [];
  if (commentCountEl) commentCountEl.textContent = String(comments.length);
  if (commentListEl) {
    commentListEl.innerHTML = comments.length
      ? comments.map(c => `<div style="padding:8px 0;border-top:1px solid rgba(10,22,40,.08)"><div class="fw7 f-xs">${escHtml(c.author_name)}</div><div class="f-xs c-muted lh-15 mt2">${escHtml(c.comment_text)}</div></div>`).join('')
      : `<div class="f-xs c-muted" style="padding:6px 0">No comments yet — be the first to ask a question.</div>`;
  }
}

async function toggleVideoLike(vid) {
  if (!App.Auth.isAuthenticated()) { showToast('Sign in to like a lesson.'); return; }
  const client = App.Supabase.getClient();
  const alreadyLiked = _videoUserLikes.has(vid);
  // Optimistic update
  if (alreadyLiked) { _videoUserLikes.delete(vid); _videoLikeCounts[vid] = Math.max(0, (_videoLikeCounts[vid]||1) - 1); }
  else { _videoUserLikes.add(vid); _videoLikeCounts[vid] = (_videoLikeCounts[vid]||0) + 1; }
  _renderVideoSocialState(vid);
  try {
    const u = App.Auth.getLocalUser();
    if (alreadyLiked) await client.from('video_likes').delete().eq('user_id', u.supabaseId).eq('video_key', vid);
    else await client.from('video_likes').insert({ user_id: u.supabaseId, video_key: vid });
  } catch (e) {
    console.warn('[RMH] like toggle failed:', e.message);
  }
}

function toggleCommentBox(vid) {
  const box = document.getElementById(`commentbox-${vid}`);
  if (box) box.style.display = box.style.display === 'none' ? 'block' : 'none';
}

async function submitComment(vid) {
  if (!App.Auth.isAuthenticated()) { showToast('Sign in to comment.'); return; }
  const input = document.getElementById(`commentinput-${vid}`);
  const text = (input?.value || '').trim();
  if (!text) return;
  try {
    const client = App.Supabase.getClient();
    const { error } = await client.rpc('submit_video_comment', { p_video_key: vid, p_comment_text: text });
    if (error) { showToast('Could not post comment — try again.'); return; }
    input.value = '';
    showToast('Comment submitted — it\'ll appear once reviewed. Thanks for sharing!');
  } catch (e) {
    showToast('Could not post comment — try again.');
  }
}

// ══════════════════════════════════════════════════════
// ADMIN — COMMENT MODERATION
// ══════════════════════════════════════════════════════
function rAdminComments() {
  if (!STATE.isAdmin) return `<div class="card p20 tc"><div class="c-muted">Not available.</div></div>`;
  return `
  <button class="btn btn-outline btn-xs mb10" onclick="openPage('settings')">&larr; Back to Settings</button>
  <div class="fw8 f-xl mb4">Comment Moderation</div>
  <div class="c-muted f-sm mb14">Pending comments awaiting approval</div>
  <div id="adminCommentList"><div class="c-muted f-sm">Loading...</div></div>`;
}

async function loadAdminComments() {
  const el = document.getElementById('adminCommentList');
  if (!el) return;
  try {
    const client = App.Supabase.getClient();
    const { data, error } = await client.from('video_comments').select('id, video_key, author_name, comment_text, created_at, status')
      .eq('status', 'pending').order('created_at', { ascending: true });
    if (error) { el.innerHTML = `<div class="c-muted f-sm">Couldn't load comments.</div>`; return; }
    if (!data || !data.length) { el.innerHTML = `<div class="card p16 tc c-muted f-sm">No pending comments — all caught up.</div>`; return; }
    el.innerHTML = data.map(c => `
      <div class="card p14 mb10">
        <div class="fac gap8 mb4"><span class="fw7 f-sm">${escHtml(c.author_name)}</span><span class="badge badge-navy" style="font-size:.6rem">${escHtml(c.video_key)}</span></div>
        <div class="f-sm lh-16 mb10">${escHtml(c.comment_text)}</div>
        <div class="fac gap8">
          <button class="btn btn-navy btn-xs" onclick="moderateComment('${c.id}','approved')">Approve</button>
          <button class="btn btn-outline btn-xs" onclick="moderateComment('${c.id}','rejected')">Reject</button>
        </div>
      </div>`).join('');
  } catch (e) {
    el.innerHTML = `<div class="c-muted f-sm">Couldn't load comments.</div>`;
  }
}

async function moderateComment(id, status) {
  try {
    const client = App.Supabase.getClient();
    const { error } = await client.from('video_comments').update({ status }).eq('id', id);
    if (error) { showToast('Action failed — try again.'); return; }
    showToast(status === 'approved' ? 'Comment approved.' : 'Comment rejected.');
    loadAdminComments();
  } catch (e) {
    showToast('Action failed — try again.');
  }
}

function rHome() {
  const p   = App.Progress.get();
  const u   = App.Auth.getLocalUser() || {};
  const examLabel = u.exam === 0 ? 'RE5' : u.exam === 1 ? 'RE1' : 'RE5 & RE1';
  const inst      = u.institution === 0 ? 'Moonstone' : u.institution === 1 ? 'INSETA' : 'Compliance Institute';
  const re5pct    = p.re5.att > 0 ? Math.round(p.re5.cor / p.re5.att * 100) : 0;
  const re1pct    = p.re1.att > 0 ? Math.round(p.re1.cor / p.re1.att * 100) : 0;
  const readiness = App.Progress.getReadiness();
  const proMsg    = STATE.isPro ? _proStatusMessage() : null;
  const mpRE5     = !STATE.isPro ? getMixedPracticeAllowance('RE5') : null;
  const mpRE1     = !STATE.isPro ? getMixedPracticeAllowance('RE1') : null;
  const tourDone  = !!localStorage.getItem('rmh_tour_completed');
  const risk      = (() => {
    if (readiness >= 75) return { level: 'Low',      color: 'var(--green)', pct: 20, msg: 'You are tracking well. Continue daily practice and review remaining weak areas systematically.' };
    if (readiness >= 50) return { level: 'Moderate', color: 'var(--amber)', pct: 55, msg: 'You need more structured practice. Prioritise your weak topics in the Study Library before your next session.' };
    return              { level: 'High',     color: 'var(--red)',   pct: 85, msg: 'At your current performance level you are at significant risk of failing. Begin with the Study Library immediately.' };
  })();
  const streak   = p.streak || [];
  const weak5    = App.Progress.getWeakAreas('RE5');
  const weak1    = App.Progress.getWeakAreas('RE1');
  const allWeak  = [...weak5.map(w => ({ ...w, exam: 'RE5' })), ...weak1.map(w => ({ ...w, exam: 'RE1' }))].sort((a, b) => a.pct - b.pct);
  const _d = computeDaysLeft(u.examDate);
  const daysLeft = _d === null ? 30 : _d;

  return `
  ${`<div class="card p16 mb12" onclick="startTour()" style="cursor:pointer;border:1.5px solid var(--gold);background:linear-gradient(135deg,#0a1628,#16294a);position:relative;overflow:hidden">
    <div style="position:absolute;top:-24px;right:-24px;width:90px;height:90px;border-radius:50%;background:radial-gradient(circle,rgba(200,168,75,.3),transparent 70%);pointer-events:none"></div>
    <div class="fac gap12" style="position:relative">
      <div style="width:46px;height:46px;border-radius:12px;flex-shrink:0;display:flex;align-items:center;justify-content:center;font-size:1.35rem;background:linear-gradient(135deg,var(--gold),#a8863a);box-shadow:0 3px 10px rgba(200,168,75,.35);animation:tourPulse 2.2s ease-in-out infinite">🧭</div>
      <div style="flex:1;min-width:0">
        <div class="fac gap6" style="flex-wrap:wrap">
          <span class="fw8 f-sm" style="color:#fff">${tourDone ? 'Replay the App Tour' : 'New Here? Take the Tour'}</span>
          ${!tourDone ? `<span style="background:var(--gold);color:#0a1628;font-size:.58rem;font-weight:900;padding:2px 7px;border-radius:8px;flex-shrink:0">NEW</span>` : ''}
        </div>
        <div class="f-xs" style="color:rgba(255,255,255,.65);margin-top:1px">${tourDone ? 'A full walkthrough of every feature, anytime' : '90 seconds — see everything the app can do'}</div>
      </div>
      <div style="color:var(--gold);font-size:1.3rem;flex-shrink:0">→</div>
    </div>
  </div>`}

  <div class="card card-navy p20 mb12">
    <div class="fb mb12">
      <div><div style="font-size:.65rem;color:rgba(255,255,255,.4);text-transform:uppercase;letter-spacing:.8px">Preparing for</div>
      <div style="font-weight:800;font-size:1rem;color:#fff">${examLabel} &middot; <span style="color:rgba(255,255,255,.5);font-weight:400;font-size:.88rem">${inst}</span></div></div>
      <div>${daysLeft <= 14 ? `<span class="badge badge-red">${daysLeft}d left</span>` : `<span class="badge badge-gold">${daysLeft} days</span>`}</div>
    </div>
    <div style="font-size:.63rem;color:rgba(255,255,255,.35);text-transform:uppercase;letter-spacing:.8px;margin-bottom:8px">Time until exam</div>
    <div class="cd-grid">${['d','h','m','s'].map((k,i)=>`<div class="cd-box"><div class="cd-val" id="cd-${k}">--</div><div class="cd-lbl">${['Days','Hours','Mins','Secs'][i]}</div></div>`).join('')}</div>
  </div>

  ${!STATE.isPro ? `<div id="tourMixedPractice" class="card p16 mb12" style="border:1.5px solid var(--gold);background:linear-gradient(135deg,rgba(200,168,75,.12),rgba(200,168,75,.02))">
    <div class="fac gap10 mb4">
      <div style="width:38px;height:38px;border-radius:10px;flex-shrink:0;background:linear-gradient(135deg,var(--gold),#a8863a);display:flex;align-items:center;justify-content:center;font-size:1.15rem;box-shadow:0 3px 8px rgba(200,168,75,.35)">🎁</div>
      <div style="flex:1;min-width:0">
        <div class="fw8 f-sm">Complimentary Mixed Practice</div>
        <div class="f-xs c-muted">Free questions from both exams, every day</div>
      </div>
    </div>
    <div class="g2 mt10">
      <button class="btn ${mpRE5.canStart?'btn-navy':'btn-outline'} btn-sm" onclick="startMixedPractice('RE5')" style="display:flex;flex-direction:column;gap:1px;padding:10px 8px;height:auto">
        <span class="fw8">RE5</span>
        <span style="font-size:.65rem;opacity:.85">${mpRE5.canStart?`${mpRE5.batchSize} free now`:'Come back later'}</span>
      </button>
      <button class="btn ${mpRE1.canStart?'btn-navy':'btn-outline'} btn-sm" onclick="startMixedPractice('RE1')" style="display:flex;flex-direction:column;gap:1px;padding:10px 8px;height:auto">
        <span class="fw8">RE1</span>
        <span style="font-size:.65rem;opacity:.85">${mpRE1.canStart?`${mpRE1.batchSize} free now`:'Come back later'}</span>
      </button>
    </div>
  </div>` : ''}

  <div id="tourFactDisk" class="card p14 mb12" onclick="showTop50()" style="cursor:pointer;border:1.5px solid rgba(200,168,75,.45);background:linear-gradient(135deg,rgba(200,168,75,.10),rgba(200,168,75,.03))">
    <div class="fac gap12">
      <div style="width:46px;height:46px;border-radius:50%;flex-shrink:0;
        background:radial-gradient(circle at 35% 28%,#1b3565,var(--navy) 78%);
        border:1.5px solid var(--gold);display:flex;align-items:center;justify-content:center;
        box-shadow:0 4px 12px rgba(10,22,40,.35)">
        <span style="color:var(--gold);font-weight:900;font-size:.62rem;letter-spacing:.5px">50</span>
      </div>
      <div style="flex:1;min-width:0">
        <div class="fac gap6" style="margin-bottom:2px">
          <span class="fw8 f-sm">RE Fact Disk&trade;</span>
          ${App.Storage.get('rfdSeen',false)?'':'<span class="badge badge-gold" style="font-size:.55rem">NEW</span>'}
        </div>
        <div class="f-xs c-muted lh-15">The 50 most-tested facts. Two minutes, any time.</div>
      </div>
      <span style="color:var(--gold);font-size:1.1rem;flex-shrink:0">&rsaquo;</span>
    </div>
  </div>

  <div class="card p14 mb12" onclick="showHelp()" style="cursor:pointer">
    <div class="fac gap12">
      <div style="width:40px;height:40px;border-radius:10px;flex-shrink:0;background:rgba(37,211,102,.13);
        display:flex;align-items:center;justify-content:center;font-size:1.15rem">&#128172;</div>
      <div style="flex:1;min-width:0">
        <div class="fw7 f-sm" style="margin-bottom:2px">Need help?</div>
        <div class="f-xs c-muted lh-15">WhatsApp, email or leave a review &mdash; we reply fast.</div>
      </div>
      <span style="color:var(--text3);font-size:1.1rem;flex-shrink:0">&rsaquo;</span>
    </div>
  </div>

  <div class="card" style="padding:16px;margin-bottom:12px">
    <div class="fb mb10"><div class="fw7">Exam Readiness</div><span style="font-weight:900;font-size:1.1rem;color:${risk.color}">${readiness}%</span></div>
    <div style="position:relative;height:14px;margin-bottom:10px"><div class="pbar pbar-h14" style="background:linear-gradient(90deg,#10b981,#f59e0b,#ef4444)"><div class="risk-needle" style="left:${risk.pct}%"></div></div></div>
    <div class="fb mb8"><span class="f-xs c-muted">Low</span><span class="f-xs c-muted">Moderate</span><span class="f-xs c-muted">High</span></div>
    <div class="card${risk.level==='Low'?'-success':risk.level==='Moderate'?'-amber':'-danger'} card" style="padding:12px;border-radius:8px">
      <div class="fw7 f-sm mb4" style="color:${risk.color}">${risk.level} Risk</div>
      <div class="f-xs c-muted lh-15">${risk.msg}</div>
    </div>
  </div>

  ${STATE.stuckTopic ? `<div id="tourWeakAreas" class="card p14 mb12" style="border:1.5px solid var(--gold);background:linear-gradient(135deg,rgba(200,168,75,.13),rgba(200,168,75,.03))">
    <div class="fac gap8 mb6"><span style="font-size:1.1rem">🎯</span><span class="fw8 f-sm">${esc(STATE.stuckTopic)} is the one thing actually holding you back</span></div>
    <div class="f-xs c-muted lh-16 mb10">Missed across ${STATE.stuckStreak} separate sessions now &middot; ${STATE.stuckTopicExam}. Everything else is working — this is the specific gap.</div>
    <button class="btn btn-gold btn-sm btn-full" onclick="${STATE.isPro ? `guardianFromLibrary('${esc(STATE.stuckTopic)}','${STATE.stuckTopicExam}')` : `openLibraryTopic('${esc(STATE.stuckTopic)}')`}">${STATE.isPro ? 'Ask Guardian to fix this →' : 'Read this topic in the Library →'}</button>
  </div>` : allWeak.length > 0 ? `<div id="tourWeakAreas" class="card p14 mb12" style="border:1.5px solid #fca5a5;background:var(--red3)">
    <div class="fb mb8"><div class="fw7 f-sm">Weak Areas Detected</div><button class="btn btn-xs" style="background:var(--red);color:#fff" onclick="openPage('quiz')">Drill Now</button></div>
    ${allWeak.slice(0,3).map(w=>`<div class="fb mb6"><div class="fac gap6"><span class="badge ${w.exam==='RE5'?'badge-navy':'badge-blue'}">${w.exam}</span><span class="f-sm">${w.t}</span></div><span class="badge badge-red">${w.pct}%</span></div>`).join('')}
    <div class="fac gap8 mt10">
      <button class="btn btn-xs" style="background:rgba(200,168,75,.15);border:1px solid rgba(200,168,75,.4);color:var(--gold);font-size:.72rem" onclick="${STATE.isPro?`guardianFromHome()`:`openPage('pro')`}">🛡️ ${STATE.isPro?'Guardian Weak Area Coach':'Pro: Guardian Weak Area Coach'}</button>
    </div>
  </div>` : `<div id="tourWeakAreas" class="card card-success p12 mb12"><div class="fac gap8"><div><div class="fw7 f-sm">No Weak Areas Detected</div><div class="f-xs c-muted">Complete quizzes to identify topics requiring focused attention.</div></div></div></div>`}

  <div class="g4 mb12">
    <div class="stat-card tc"><div class="stat-val">${p.re5.att+p.re1.att}</div><div class="stat-lbl">Attempted</div></div>
    <div class="stat-card tc"><div class="stat-val" style="color:var(--green)">${re5pct}%</div><div class="stat-lbl">RE5</div></div>
    <div class="stat-card tc"><div class="stat-val" style="color:var(--gold)">${re1pct}%</div><div class="stat-lbl">RE1</div></div>
    <div class="stat-card tc"><div class="stat-val" style="color:var(--amber)">${streak.length}</div><div class="stat-lbl">Streak</div></div>
  </div>

  <div class="card" style="padding:16px;margin-bottom:12px">
    <div class="fw7 mb10">Study Streak</div>
    <div class="fac gap6 fw">${[0,1,2,3,4,5,6].map(i=>{const done=i<streak.length;const isToday=i===streak.length;return`<div style="width:32px;height:32px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:.7rem;font-weight:700;flex-shrink:0;background:${done?'var(--gold)':isToday?'var(--navy)':'var(--border2)'};color:${done?'var(--navy)':isToday?'var(--gold)':'var(--text3)'};border:${isToday?'2px solid var(--gold)':'none'}">${done?'&#10003;':i+1}</div>`}).join('')}</div>
    <div class="f-xs c-muted mt8">Complete a quiz today to maintain your streak.</div>
  </div>

  <div class="stitle">Quick Practice</div>
  <div class="g2 mb12">
    <div class="card p16 card-hover" onclick="startQuiz('RE5',10)" style="cursor:pointer"><div class="fw7 mb4">RE5 Quiz</div><div class="f-xs c-muted">10 Scenario Questions</div><div class="mt8"><span class="badge badge-navy">FREE</span></div></div>
    <div class="card p16 card-hover" onclick="${hasAccess('RE1')?`startQuiz('RE1',10)`:`openPage('pro')`}" style="cursor:pointer"><div class="fw7 mb4">RE1 Quiz</div><div class="f-xs c-muted">10 Scenario Questions</div><div class="mt8">${hasAccess('RE1')?'<span class="badge badge-gold">PRO</span>':'<div class="pro-pill">UNLOCK PRO</div>'}</div></div>
  </div>

  <div class="card card-navy" style="padding:16px;margin-bottom:12px;border:none">
    <div class="fw7 c-white mb6">${!STATE.isPro?'Unlock Full Access':proMsg.title}</div>
    ${!STATE.isPro?`<div style="color:rgba(255,255,255,.65);font-size:.8rem;margin-bottom:12px;line-height:1.6">87% of Pro users improve within 7 days. Your exam date is approaching.</div><button class="btn btn-gold btn-md btn-full" onclick="openPage('pro')">Upgrade — from R189</button>`:`<div style="color:rgba(255,255,255,.65);font-size:.8rem;line-height:1.6">${proMsg.body}</div>`}
  </div>

  <div class="ref-box mb12">
    <div class="fw7 mb6" style="color:var(--purple)">Referral Programme</div>
    <div class="f-sm mb10" style="color:var(--purple)">Invite 3 colleagues who register &rarr; 7 days Pro free.</div>
    ${(() => {
      const u = App.Auth.getLocalUser() || {};
      const count = u.referralCount || 0;
      const progressInBatch = count % 3;
      const bonusActive = u.bonusAccessUntil && new Date(u.bonusAccessUntil).getTime() > Date.now();
      return `
      <div class="f-xs mb8" style="color:var(--purple)">
        ${bonusActive
          ? `🎁 Bonus access active until ${new Date(u.bonusAccessUntil).toLocaleDateString()}`
          : `${progressInBatch} of 3 referred${count >= 3 ? ` &middot; ${count} total` : ''} — invite ${3 - progressInBatch} more for your next free week`}
      </div>
      <div style="background:rgba(255,255,255,.4);border-radius:6px;height:6px;overflow:hidden;margin-bottom:10px">
        <div style="background:var(--purple);height:100%;width:${(progressInBatch/3)*100}%;border-radius:6px"></div>
      </div>`;
    })()}
    <div class="fac gap8"><div style="background:#fff;border-radius:8px;padding:8px 14px;font-family:monospace;font-weight:800;font-size:.9rem;color:var(--purple);flex:1">${(App.Auth.getLocalUser()||{}).referralCode||'RMH-XXXX'}</div><button class="btn btn-sm" style="background:var(--purple);color:#fff" onclick="copyReferral()">Copy Link</button></div>
  </div>

  ${ytSubCard('home')}

  <div class="card" style="padding:16px;margin-bottom:12px">
    <div class="stitle">Platform Performance</div>
    <div class="g3 mb8">${[['2,431+','Mock Exams'],['87%','Improve in 7 Days'],['1,204+','Passes']].map(([v,l])=>`<div class="tc"><div style="font-size:1.1rem;font-weight:900;color:var(--navy)">${v}</div><div class="f-xs c-muted">${l}</div></div>`).join('')}</div>
    <div class="divider"></div>
    <div class="f-sm c-muted lh-16" style="font-style:italic">"Passed RE5 first attempt. The scenario questions prepared me for exactly what came up." — Thabo M.</div>
    <button class="btn btn-outline btn-sm" style="margin-top:12px;width:100%" onclick="openReviewSystem()">Rate This App / Share Your Story</button>
  </div>

  <div id="tourCrashPack" class="card card-amber" style="padding:16px;margin-bottom:12px">
    <div class="fw7 mb4" style="color:var(--amber)">Last 7 Days Crash Pack</div>
    <div class="f-sm mb10" style="color:#78350f;line-height:1.6">Exam in under a week? Rapid-fire questions, Top 50 facts, and your Day-Before checklist — fully unlocked.</div>
    <button class="btn btn-sm btn-full" style="background:var(--amber);color:#fff" onclick="showCrashPack()">Open Crash Pack</button>
  </div>
  `;
}

function startCountdown() {
  clearCdTimer();
  const u = App.Auth.getLocalUser(); if (!u) return;
  const examDate = u.examDate ? new Date(u.examDate) : new Date(Date.now() + 30 * 864e5);
  function tick() {
    const diff = examDate - new Date();
    if (diff <= 0) { clearCdTimer(); return; }
    const vals = [Math.floor(diff/864e5), Math.floor(diff%864e5/36e5), Math.floor(diff%36e5/6e4), Math.floor(diff%6e4/1e3)];
    ['d','h','m','s'].forEach((k,i) => { const el = document.getElementById('cd-'+k); if (el) el.textContent = String(vals[i]).padStart(2,'0'); });
  }
  tick(); _cdTimer = setInterval(tick, 1000);
}
function copyReferral() {
  const code = (App.Auth.getLocalUser()||{}).referralCode || 'RMH-DEMO';
  const link = `https://remasteryhub.online/?ref=${code}`;
  navigator.clipboard?.writeText(link).catch(()=>{});
  showToast('Referral link copied — share it with a friend.');
}

// ══════════════════════════════════════════════════════
// QUIZ HOME
// ══════════════════════════════════════════════════════
function rQuizHome() {
  const p = App.Progress.get();
  const re5Topics = [...new Set(QB_RE5.map(q => q.t))];
  const re5pct = p.re5.att > 0 ? Math.round(p.re5.cor / p.re5.att * 100) : 0;
  const re1pct = p.re1.att > 0 ? Math.round(p.re1.cor / p.re1.att * 100) : 0;
  return `
  <div class="fw8 f-xl mb4">Practice Quiz</div>
  <div class="c-muted f-sm mb16">Moonstone-aligned scenario questions &middot; Knowledge, Comprehension, Application, Analysis</div>
  <div class="g2 mb16">
    <div class="card p16 card-hover" style="border:2px solid var(--navy);cursor:pointer" onclick="startQuiz('RE5',15)">
      <div class="fw7 mb2">RE5 Mixed</div><div class="f-xs c-muted">15 questions &middot; All topics</div>
      <div class="pbar pbar-h6 mt8 mb4"><div class="pbar-fill pf-navy" style="width:${re5pct}%"></div></div>
      <div class="f-xs c-muted">${re5pct}% accuracy</div><div class="mt8"><span class="badge badge-navy">FREE</span></div>
    </div>
    <div class="card p16 card-hover" style="border:2px solid ${hasAccess('RE1')?'var(--gold)':'var(--border)'};cursor:pointer" onclick="${hasAccess('RE1')?`startQuiz('RE1',15)`:`openPage('pro')`}">
      <div class="fw7 mb2">RE1 Mixed</div><div class="f-xs c-muted">15 questions &middot; All topics</div>
      <div class="pbar pbar-h6 mt8 mb4"><div class="pbar-fill pf-gold" style="width:${re1pct}%"></div></div>
      <div class="f-xs c-muted">${re1pct}% accuracy</div>
      <div class="mt8">${STATE.isPro?'<span class="badge badge-gold">PRO</span>':'<div class="pro-pill" style="font-size:.62rem">PRO ONLY</div>'}</div>
    </div>
  </div>
  <div class="stitle">RE5 Topics</div>
  <div class="mb16">${re5Topics.map(t=>{const d=p.re5.byT[t];const pct=d&&d.att>0?Math.round(d.cor/d.att*100):null;return`<div class="card card-sm card-hover fb mb8" style="cursor:pointer" onclick="startTopicQuiz('RE5','${esc(t)}')"><div><div class="fw6 f-sm mb2">${t}</div>${pct!==null?`<div class="f-xs c-muted">${d.att} attempts &middot; ${pct}% accuracy</div>`:'<div class="f-xs c-muted">Not yet attempted</div>'}</div><div class="fac gap6">${pct!==null?`<span class="badge ${pct>=70?'badge-green':pct>=50?'badge-amber':'badge-red'}">${pct}%</span>`:''}<span class="c-xs">&#8250;</span></div></div>`;}).join('')}</div>
  <div class="stitle fac gap8">RE1 Topics ${!hasAccess('RE1')?'<div class="pro-pill" style="font-size:.62rem">PRO</div>':''}</div>
  ${hasAccess('RE1')
    ? `<div class="mb16">${[...new Set(QB_RE1.map(q=>q.t))].map(t=>`<div class="card card-sm card-hover fb mb8" style="cursor:pointer" onclick="startTopicQuiz('RE1','${esc(t)}')"><div><div class="fw6 f-sm">${t}</div><div class="f-xs c-muted">Key Individual scenario questions</div></div><span class="c-xs">&#8250;</span></div>`).join('')}</div>`
    : `<div class="blur-lock mb16"><div class="blur-content">${[...new Set(QB_RE1.map(q=>q.t))].slice(0,3).map(t=>`<div class="card card-sm mb8"><div class="fw6 f-sm">${t}</div></div>`).join('')}</div><div class="blur-lock-cta"><button class="btn btn-gold btn-sm" onclick="openPage('pro')">Unlock RE1 Content</button></div></div>`}`;
}

// ── ALL QUIZ ENTRY POINTS → buildPool ──
function startQuiz(exam, count)              { if (exam==='RE1' && !hasAccess('RE1')) { openPage('pro'); return; } _setActiveFeature('quiz_practice'); launchQuiz(exam, buildPool(exam==='RE1' ? QB_RE1 : QB_RE5, count)); }
function startTopicQuiz(exam, topic)         { if (exam==='RE1' && !hasAccess('RE1')) { openPage('pro'); return; } _setActiveFeature('quiz_practice'); launchQuiz(exam, buildPool((exam==='RE1'?QB_RE1:QB_RE5).filter(q=>q.t===topic), 15)); }
function startCrashQuiz()                    {
  _setActiveFeature('crash_pack');
  STATE.quiz = { exam:'RE5', questions: buildPool(CRASH_QB, 25), cur:0, answers:[], score:0, crashMode:true, startTime:Date.now() };
  App.Analytics.track(App.Analytics.Events.CRASH_PACK_OPEN);
  document.getElementById('pageContent').innerHTML = `<div class="anim-in" id="qArea"></div>`;
  document.querySelectorAll('.bnav-item').forEach(b=>b.classList.remove('active'));
  document.getElementById('bnav-quiz').classList.add('active');
  renderQ();
}

function launchQuiz(exam, questions, mixedPractice) {
  if (!questions || questions.length === 0) {
    showToast('No questions found for this topic yet — please try another topic or contact support.');
    console.error('[RMH] launchQuiz called with empty question pool for exam:', exam);
    return;
  }
  App.Analytics.track(App.Analytics.Events.QUIZ_START, { exam, count: questions.length });
  STATE.quiz = { exam, questions, cur:0, answers:[], score:0, crashMode:false, mixedPractice:!!mixedPractice, startTime:Date.now() };
  document.getElementById('pageContent').innerHTML = `<div class="anim-in" id="qArea"></div>`;
  document.querySelectorAll('.bnav-item').forEach(b=>b.classList.remove('active'));
  document.getElementById('bnav-quiz').classList.add('active');
  renderQ();
}

// ── QUIZ RENDER (uses only prepared q.o and q.a) ──
function esc(s) { return (s||'').replace(/'/g,"\\'").replace(/"/g,'&quot;'); }
// esc() above only escapes quotes — built for embedding strings into
// onclick="..." attributes, not for rendering text as page content. Video
// comments are genuine public user input (and the admin moderation view
// renders unapproved ones too), so they need real HTML escaping or a
// malicious comment could execute script in every viewer's browser,
// including the admin's while reviewing it. Never use esc() for this.
function escHtml(s) {
  return (s||'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;').replace(/'/g,'&#39;');
}
function levelBadge(lv) { const m={K:'level-k K',C:'level-c C',AP:'level-ap AP',AN:'level-an AN'}; const p=(m[lv]||'level-k K').split(' '); return `<span class="badge-level ${p[0]}">${p[1]}</span>`; }

function renderQ() {
  const s = STATE.quiz; if (!s) return;
  const q = s.questions[s.cur]; if (!q) { showQuizResult(false); return; }
  const pct      = Math.round(s.cur / s.questions.length * 100);
  const answered = s.answers[s.cur] !== undefined;
  const hitLimit = !STATE.isPro && !s.crashMode && !s.mixedPractice && _getFreeQuizUsed() >= App.Config.freeQPerSession;
  const area     = document.getElementById('qArea'); if (!area) return;

  if (hitLimit) {
    area.innerHTML = `
    <div class="fb mb8"><div class="f-xs fw6" style="text-transform:uppercase;color:var(--text3)">${s.exam} &middot; Q${s.cur+1}/${s.questions.length}</div></div>
    <div class="pbar pbar-h6 mb14"><div class="pbar-fill pf-navy" style="width:${pct}%"></div></div>
    <div class="card" style="padding:20px;text-align:center;border:2px dashed var(--gold)">
      <div class="fw8 mb6">Upgrade for Unlimited Access</div>
      <div class="c-muted f-sm mb16 lh-16">You have used your complimentary questions. Upgrade to Pro for unlimited practice and full explanations.</div>
      <button class="btn btn-gold btn-md btn-full mb8" onclick="openPage('pro')">Upgrade to Pro</button>
      <button class="btn btn-ghost btn-sm btn-full" onclick="openPage('quiz')">Back to Topics</button>
    </div>`;
    return;
  }

  let html = `
  <div class="fb mb8"><div class="f-xs fw6" style="text-transform:uppercase;color:var(--text3)">${s.exam} &middot; Q${s.cur+1}/${s.questions.length}</div><div class="f-xs c-muted">${s.score}/${s.cur} correct</div></div>
  <div class="pbar pbar-h6 mb14"><div class="pbar-fill pf-navy" style="width:${pct}%"></div></div>
  <div class="fac gap8 mb10"><span class="chip f-xs">${q.t}</span>${levelBadge(q.lv)}</div>
  <div class="fw6 lh-16 mb16" style="font-size:.93rem">${q.q}</div>`;

  // Render options from the PREPARED q.o (already shuffled, q.a already remapped)
  q.o.forEach((o, i) => {
    let cls = 'qopt';
    if (answered) {
      if (i === q.a)                               cls += ' correct reveal';
      else if (s.answers[s.cur] === i && i !== q.a) cls += ' wrong disabled';
      else                                          cls += ' disabled';
    }
    html += `<div class="${cls}" onclick="answerQ(${i})" role="radio" aria-checked="${s.answers[s.cur]===i}"><div class="okey" aria-hidden="true">${['A','B','C','D'][i]}</div><div class="f-sm lh-14">${o}</div></div>`;
  });

  if (answered) {
    const cor = s.answers[s.cur] === q.a;
    html += `<div class="card" style="padding:14px;margin-bottom:12px;border:2px solid ${cor?'var(--green)':'var(--red)'};background:${cor?'var(--green3)':'var(--red3)'}"><div class="fw7 mb4 f-sm" style="color:${cor?'var(--green)':'var(--red)'}">${cor?'Correct.':'Incorrect.'}</div>${STATE.hasExplanations||s.crashMode?`<div class="f-sm lh-16 c-muted">${q.e}</div>`:`<div class="blur-content f-sm lh-16 mb6" style="filter:blur(4px)">${q.e}</div><div class="fac gap6 f-xs c-muted">Full explanation in <span class="c-gold fw7" style="cursor:pointer" onclick="openPage('pro')">Pro</span></div>`}</div>
    <button class="btn btn-navy btn-md btn-full" onclick="nextQ()">${s.cur<s.questions.length-1?'Next Question':'View Results'}</button>`;
  } else {
    html += `<div class="fac gap8 mt12"><button class="btn btn-outline btn-sm" onclick="exitQuiz()">Exit</button><button class="btn btn-ghost btn-sm" onclick="skipQ()">Skip</button></div>`;
  }
  area.innerHTML = html;
}

// All downstream functions use q.a from the prepared question — no raw bank references
// ── Free quiz quota (lifetime, persists across sessions/reloads) ──────────
// Guardian's usage tracker resets daily by design; this one must NOT reset —
// it's a one-time taste of the question bank, not a renewable daily ration.
function _getFreeQuizUsed() {
  const raw = localStorage.getItem('rmh_free_quiz_lifetime');
  const n = raw ? parseInt(raw, 10) : 0;
  return Number.isFinite(n) && n >= 0 ? n : 0;
}
function _incrementFreeQuizUsed() {
  localStorage.setItem('rmh_free_quiz_lifetime', String(_getFreeQuizUsed() + 1));
}

// ── Complimentary Mixed Practice — a separate free-tier hook ───────────────
// Day 1 ever: 17 questions, once. Every rolling 24h window after that: 8
// questions. RE5 and RE1 tracked completely independently, so a free user
// gets both taste-of-RE5 AND taste-of-RE1 (RE1 is otherwise 100% Pro-gated).
// Rolling window (not calendar-day reset) so the "come back at X" countdown
// is always exact, matching how Claude.ai communicates a usage-limit reset.
const MIXED_PRACTICE_DAY_MS = 24 * 60 * 60 * 1000;

function _getMixedPracticeState(examType) {
  try {
    const raw = localStorage.getItem(`rmh_mixed_practice_${examType}`);
    return raw ? JSON.parse(raw) : null;
  } catch (e) { return null; }
}
function _saveMixedPracticeState(examType, state) {
  try { localStorage.setItem(`rmh_mixed_practice_${examType}`, JSON.stringify(state)); } catch (e) {}
}
// Returns { canStart, batchSize, resetAt } — resetAt is a Date only when
// canStart is false, telling the caller exactly when the window reopens.
function getMixedPracticeAllowance(examType) {
  const state = _getMixedPracticeState(examType);
  if (!state || !state.everStarted) {
    return { canStart: true, batchSize: 17, resetAt: null };
  }
  const elapsed = Date.now() - state.windowStart;
  if (elapsed >= MIXED_PRACTICE_DAY_MS) {
    return { canStart: true, batchSize: 8, resetAt: null };
  }
  if (state.usedInWindow) {
    return { canStart: false, batchSize: 0, resetAt: new Date(state.windowStart + MIXED_PRACTICE_DAY_MS) };
  }
  return { canStart: true, batchSize: 8, resetAt: null };
}
function _consumeMixedPracticeWindow(examType) {
  _saveMixedPracticeState(examType, { everStarted: true, windowStart: Date.now(), usedInWindow: true });
}

function startMixedPractice(examType) {
  if (STATE.isPro) { startQuiz(examType, 15); return; } // Pro users don't need the hook — send them to normal practice
  const allowance = getMixedPracticeAllowance(examType);
  if (!allowance.canStart) { showMixedPracticeLimitModal(examType, allowance.resetAt); return; }
  const pool = examType === 'RE1' ? QB_RE1 : QB_RE5;
  const questions = buildPool(pool, allowance.batchSize);
  if (!questions || questions.length === 0) { showToast('Practice questions are loading — try again in a moment.'); return; }
  _consumeMixedPracticeWindow(examType);
  _setActiveFeature('mixed_practice');
  launchQuiz(examType, questions, true);
}

function _formatResetTime(resetAt) {
  const now = new Date();
  const sameDay = resetAt.toDateString() === now.toDateString();
  const timeStr = resetAt.toLocaleTimeString('en-ZA', { hour: 'numeric', minute: '2-digit' });
  if (sameDay) return `today at ${timeStr}`;
  const tomorrow = new Date(now); tomorrow.setDate(now.getDate() + 1);
  if (resetAt.toDateString() === tomorrow.toDateString()) return `tomorrow at ${timeStr}`;
  return `${resetAt.toLocaleDateString('en-ZA', { weekday: 'long' })} at ${timeStr}`;
}

function showMixedPracticeLimitModal(examType, resetAt) {
  const resetLabel = _formatResetTime(resetAt);
  showModal('Complimentary Practice', `
    <div class="tc" style="padding:6px 0 2px">
      <div style="font-size:2rem;margin-bottom:6px">🎁</div>
      <div class="fw8" style="font-size:1.05rem;margin-bottom:6px">Today's free ${examType} questions are done</div>
      <div class="f-sm c-muted lh-16" style="margin-bottom:4px">Nice work getting through them. Your next complimentary batch unlocks <span class="fw7" style="color:var(--navy)">${resetLabel}</span>.</div>
    </div>
    <div class="card p16 mt14" style="border:1.5px solid var(--gold);background:linear-gradient(135deg,rgba(200,168,75,.1),rgba(200,168,75,.02))">
      <div class="fw7 f-sm mb4">Pro users never wait</div>
      <div class="f-xs c-muted lh-16 mb12">Unlimited RE5 + RE1 questions, right now, from R99 — no daily reset, no countdown.</div>
      <button class="btn btn-gold btn-sm btn-full" onclick="closeModal();openPage('pro')">Skip the wait — go Pro</button>
    </div>
    <button class="btn btn-navy btn-sm btn-full mt8" onclick="closeModal()">I'll wait</button>`);
}

function answerQ(idx) {
  const s = STATE.quiz; if (!s || s.answers[s.cur] !== undefined) return;
  s.answers[s.cur] = idx;
  const q = s.questions[s.cur];
  const correct = idx === q.a;
  if (correct) s.score++;
  App.Progress.recordAnswer(s.exam, q.t, correct, s.startTime);
  if (!STATE.isPro && !s.crashMode && !s.mixedPractice) _incrementFreeQuizUsed();
  renderQ();
}
function skipQ()    { App.Behaviour.track('skip'); const s=STATE.quiz; if(!s||s.answers[s.cur]!==undefined)return; s.answers[s.cur]=-1; nextQ(); }
function exitQuiz() { App.Behaviour.track('quit'); showQuizResult(true); }
function nextQ()    { const s=STATE.quiz; if(!s)return; if(s.cur<s.questions.length-1){s.cur++;renderQ();}else showQuizResult(false); }

function showQuizResult(abandoned=false) {
  const s = STATE.quiz; if (!s) return;
  if (abandoned) App.Behaviour.track('quit');
  App.Analytics.track(abandoned ? App.Analytics.Events.QUIZ_ABANDONED : App.Analytics.Events.QUIZ_COMPLETE, { exam:s.exam, score:s.score, total:s.questions.length });

  const pct = s.questions.length > 0 ? Math.round(s.score / s.questions.length * 100) : 0;
  if (pct >= App.Config.passMark && !abandoned) App.Behaviour.track('goodScore');
  else if (!abandoned) App.Behaviour.track('lowScore');

  const coach = App.Coaching.getResponse(pct, s.questions.length, s.exam, abandoned);

  // Topic breakdown — guard skips (answer === -1) and unanswered (undefined)
  const tb = {};
  s.questions.forEach((q, i) => {
    if (!tb[q.t]) tb[q.t] = { c:0, t:0 };
    tb[q.t].t++;
    if (s.answers[i] !== undefined && s.answers[i] !== -1 && s.answers[i] === q.a) tb[q.t].c++;
  });

  Object.entries(tb).forEach(([t, d]) => { if (d.t > 0 && d.c/d.t < 0.6) App.Behaviour.trackWeakTopic(t); });

  // Fire the stuck-topic sync now that this session's answers are recorded
  // (recordAnswer already ran per-question during the quiz). Fire-and-forget
  // — the insight card below reads from STATE, which the sync updates
  // synchronously-enough for the common case; if it's still resolving, the
  // card simply won't show on this particular result screen and will catch
  // up on the Home card once the sync completes.
  if (!abandoned) _syncStuckTopicSignal();

  // The "wow" moment: this session's own topic breakdown independently
  // shows 2+ wrong answers on the currently-flagged stuck topic — i.e. the
  // pattern isn't just a historical stat, it just happened again, live.
  const stuckTopicHitThisSession = STATE.stuckTopic && tb[STATE.stuckTopic] &&
    (tb[STATE.stuckTopic].t - tb[STATE.stuckTopic].c) >= 2;

  document.getElementById('pageContent').innerHTML = `<div class="anim-in">
  <div class="card card-navy" style="padding:24px;margin-bottom:12px;text-align:center;border:none">
    <div style="font-size:3.5rem;font-weight:900;color:var(--gold);line-height:1">${pct}%</div>
    <div class="fw7 c-white f-lg mt4">${pct>=App.Config.passMark?'Pass':'Below Pass Mark'}</div>
    <div style="color:rgba(255,255,255,.55);font-size:.8rem;margin-top:4px">${s.score}/${s.questions.length} correct &middot; ${s.exam}${abandoned?' &middot; Exited early':''}</div>
    <div class="fac gap8 mt16" style="justify-content:center">
      <button class="btn btn-gold btn-sm" onclick="startQuiz('${s.exam}',10)">Retry</button>
      <button class="btn btn-outline btn-sm" style="border-color:rgba(255,255,255,.2);color:#fff" onclick="openPage('quiz')">Topics</button>
      <button class="btn btn-outline btn-sm" style="border-color:rgba(255,255,255,.2);color:#fff" onclick="openPage('library')">Study Notes</button>
    </div>
  </div>
  <div class="${coach.cls} coach-box mb12"><div class="fw7 f-sm mb6">Your Coach Says:</div><div class="f-sm lh-16">${coach.msg}</div></div>
  ${stuckTopicHitThisSession ? `
  <div style="background:linear-gradient(135deg,rgba(200,168,75,.14),rgba(200,168,75,.04));border:1.5px solid var(--gold);border-radius:12px;padding:14px 16px;margin-bottom:12px">
    <div class="fac gap8 mb6"><span style="font-size:1.1rem">🎯</span><span class="fw8 f-sm">${STATE.stuckTopic} is the one thing actually holding you back</span></div>
    <div class="f-xs c-muted lh-16 mb10">You've missed this topic in ${STATE.stuckStreak} separate sessions now, including this one — everything else you're doing is working, this is the specific gap.</div>
    <button class="btn btn-gold btn-sm btn-full" onclick="${STATE.isPro ? `guardianFromLibrary('${esc(STATE.stuckTopic)}','${STATE.stuckTopicExam}')` : `openLibraryTopic('${esc(STATE.stuckTopic)}')`}">${STATE.isPro ? 'Ask Guardian to fix this →' : 'Read this topic in the Library →'}</button>
  </div>` : ''}
  ${Object.entries(tb).length > 0 ? `
  <div style="background:rgba(200,168,75,.08);border:1.5px solid rgba(200,168,75,.25);border-radius:12px;padding:13px 14px;margin-bottom:12px;display:flex;align-items:center;gap:12px">
    <span style="font-size:1.4rem;flex-shrink:0">🛡️</span>
    <div style="flex:1">
      <div style="font-weight:700;font-size:.82rem;color:var(--gold);margin-bottom:2px">RE Guardian™ — Continue Learning</div>
      <div style="font-size:.76rem;color:var(--text2);line-height:1.5">Ask Guardian to explain any missed questions or drill your weak topics from this session.</div>
    </div>
    <button class="btn btn-sm" style="background:var(--gold);color:var(--navy);font-weight:700;flex-shrink:0;font-size:.75rem" onclick="guardianFromQuiz(${pct},'${s.exam}','${esc(Object.entries(tb).sort((a,b)=>(a[1].c/a[1].t)-(b[1].c/b[1].t))[0]?.[0] || '')}')">Open →</button>
  </div>` : ''}
  ${ytSubCard('results')}
  ${Object.entries(tb).map(([t,d])=>{const tp=d.t>0?Math.round(d.c/d.t*100):0;return`<div class="card card-sm fb mb8"><div class="fw6 f-sm truncate" style="max-width:200px">${t}</div><div class="fac gap8"><div class="pbar pbar-h6" style="width:70px"><div class="pbar-fill ${tp>=70?'pf-green':tp>=50?'pf-gold':'pf-red'}" style="width:${tp}%"></div></div><span class="badge ${tp>=70?'badge-green':tp>=50?'badge-amber':'badge-red'}">${tp}%</span></div></div>`}).join('')}
  </div>`;
  STATE.quiz = null;
}

// ══════════════════════════════════════════════════════
// MOCK / EXAM SIMULATION — all entry via buildPoolFill
// ══════════════════════════════════════════════════════
function rMockHome() {
  const p = App.Progress.get(); const mocks = p.mocks || []; const last = mocks.length>0?mocks[mocks.length-1]:null;
  return `
  <div class="fw8 f-xl mb4">Exam Simulation</div>
  <div class="c-muted f-sm mb16">Full exam conditions &middot; Timed &middot; Navigation &middot; Answers revealed at end only</div>
  <div class="card card-navy p14 mb12"><div class="fw7 c-white mb4">Real Exam Structure</div><div style="color:rgba(255,255,255,.65);font-size:.78rem;line-height:1.9">RE5: 50 questions &middot; 2 hours &middot; Pass mark 66%<br>RE1: 80 questions &middot; 2h30 &middot; Pass mark 65%<br>Book via Moonstone, INSETA, or Compliance Institute</div></div>
  ${last?`<div class="card ${last.pct>=65?'card-success':'card-danger'} p12 mb12"><div class="fb"><div><div class="fw6 f-sm">Last Simulation: ${last.mode}</div><div class="f-xs c-muted">${last.date}</div></div><span class="fw9 f-lg" style="color:${last.pct>=65?'var(--green)':'var(--red)'}">${last.pct}%</span></div></div>`:''}
  <div class="stitle">Available Simulations</div>
  <div class="g2 mb12">
    <div class="card p16 card-hover" style="border:2px solid var(--navy);cursor:pointer" onclick="startSim('RE5',20,40)"><div class="fw7 mb2">RE5 Free Sim</div><div class="f-xs c-muted">20 Q &middot; 40 min</div><div class="mt8"><span class="badge badge-navy">FREE</span></div></div>
    <div class="card p16 card-hover" style="border:2px solid ${hasAccess('RE5')?'var(--gold)':'var(--border)'};cursor:pointer" onclick="${hasAccess('RE5')?`startSim('RE5',${App.Config.re5SimTarget},90)`:`openPage('pro')`}"><div class="fw7 mb2">Full RE5 Sim</div><div class="f-xs c-muted">${App.Config.re5SimTarget} Q &middot; 90 min</div><div class="mt8">${hasAccess('RE5')?'<span class="badge badge-gold">PRO</span>':'<div class="pro-pill" style="font-size:.62rem">PRO</div>'}</div></div>
    <div class="card p16 card-hover" style="border:2px solid ${hasAccess('RE1')?'var(--gold)':'var(--border)'};cursor:pointer" onclick="${hasAccess('RE1')?`startSim('RE1',${App.Config.re1SimTarget},80)`:`openPage('pro')`}"><div class="fw7 mb2">RE1 Sim</div><div class="f-xs c-muted">${App.Config.re1SimTarget} Q &middot; 80 min</div><div class="mt8">${hasAccess('RE1')?'<span class="badge badge-gold">PRO</span>':'<div class="pro-pill" style="font-size:.62rem">PRO</div>'}</div></div>
    <div class="card p16 card-hover" style="border:2px solid ${hasAccess('Combo')?'var(--gold)':'var(--border)'};cursor:pointer" onclick="${hasAccess('Combo')?`startSim('Combo',40,75)`:`openPage('pro')`}"><div class="fw7 mb2">Combo Sim</div><div class="f-xs c-muted">40 Q &middot; 75 min</div><div class="mt8">${hasAccess('Combo')?'<span class="badge badge-gold">PRO</span>':'<div class="pro-pill" style="font-size:.62rem">PRO</div>'}</div></div>
  </div>

  <div class="stitle">🔥 Hard Mode — Advanced Scenarios</div>
  <div class="c-muted f-xs mb10" style="line-height:1.5">Multi-layered, analysis-heavy questions. For learners scoring consistently above 75% who want the ultimate stress test before exam day.</div>
  <div class="g2 mb12">
    <div class="card p16 card-hover" style="border:2px solid ${hasAccess('RE5')&&STATE.hasHardSim?'#ef4444':'var(--border)'};cursor:pointer;background:${hasAccess('RE5')&&STATE.hasHardSim?'rgba(239,68,68,.06)':'transparent'}" onclick="${hasAccess('RE5')&&STATE.hasHardSim?`startSim('RE5_HARD',29,55)`:`openPage('pro')`}"><div class="fw7 mb2">🔥 RE5 Hard Sim</div><div class="f-xs c-muted">29 Q &middot; 55 min &middot; Analysis-level only</div><div class="mt8">${hasAccess('RE5')&&STATE.hasHardSim?'<span class="badge" style="background:#ef4444;color:#fff">HARD</span>':'<div class="pro-pill" style="font-size:.62rem">PRO</div>'}</div></div>
    <div class="card p16 card-hover" style="border:2px solid ${hasAccess('RE1')&&STATE.hasHardSim?'#ef4444':'var(--border)'};cursor:pointer;background:${hasAccess('RE1')&&STATE.hasHardSim?'rgba(239,68,68,.06)':'transparent'}" onclick="${hasAccess('RE1')&&STATE.hasHardSim?`startSim('RE1_HARD',21,45)`:`openPage('pro')`}"><div class="fw7 mb2">🔥 RE1 Hard Sim</div><div class="f-xs c-muted">21 Q &middot; 45 min &middot; KI judgement calls</div><div class="mt8">${hasAccess('RE1')&&STATE.hasHardSim?'<span class="badge" style="background:#ef4444;color:#fff">HARD</span>':'<div class="pro-pill" style="font-size:.62rem">PRO</div>'}</div></div>
  </div>
  ${mocks.length>0?`<div class="stitle">Simulation History</div>${mocks.slice(-5).reverse().map(m=>`<div class="card card-sm fb mb6"><div><div class="fw6 f-sm">${m.mode}</div><div class="f-xs c-muted">${m.date}</div></div><div class="fac gap8"><span class="f-xs fw7">${m.score}/${m.total}</span><span class="badge ${m.pct>=65?'badge-green':'badge-red'}">${m.pct}%</span></div></div>`).join('')}`:''}
  ${!STATE.isPro?`<div class="card p14 card-navy mt12"><div class="fw7 c-white mb6">Pro Sim Mode includes:</div><div style="color:rgba(255,255,255,.6);font-size:.78rem;line-height:2">${App.Config.re5SimTarget}-Q RE5 sim &middot; ${App.Config.re1SimTarget}-Q RE1 sim &middot; Navigation dots &middot; Unanswered tracking &middot; Review before submit &middot; Full breakdown &middot; Pass probability &middot; Coach debrief</div><button class="btn btn-gold btn-sm mt12" onclick="openPage('pro')">Unlock All Simulations</button></div>`:''}`;
}

function startSim(mode, count, mins) {
  if (mode.startsWith('RE1') && !hasAccess('RE1')) { openPage('pro'); return; }
  if (mode === 'Combo' && !hasAccess('Combo')) { openPage('pro'); return; }
  if (mode === 'RE5_HARD' || mode === 'RE1_HARD') {
    showModal('🔥 Before you begin', `
      <div class="f-sm lh-16 c-muted mb14">
        <p class="mb10"><strong style="color:var(--text)">These questions are deliberately longer and harder than your real exam.</strong> That's on purpose — Hard Sim is built to train your reading stamina and endurance, not to reflect the real RE5/RE1 exam format.</p>
        <p class="mb10">Real exam questions are much shorter than what you're about to see. If this feels unrealistically dense, that's the point.</p>
        <p class="mb0"><strong style="color:var(--text)">Don't quit early.</strong> Push through to the end — that's how you find out whether you can actually hold concentration for the real thing. Ending a Hard Sim early defeats its purpose.</p>
      </div>
      <button class="btn btn-red btn-full" onclick="closeModal();_startSimInner('${mode}',${count},${mins})">I understand — begin Hard Sim</button>
    `);
    return;
  }
  if (mode === 'RE5_BLUEPRINT' || mode === 'RE1_BLUEPRINT') {
    const examLabel = mode === 'RE5_BLUEPRINT' ? 'RE5' : 'RE1';
    const taskCount = examLabel === 'RE5' ? 8 : 16;
    showModal(`🎯 The ${examLabel} Blueprint Exam`, `
      <div style="text-align:center;margin-top:-6px;margin-bottom:14px">
        <div style="display:inline-block;background:rgba(200,168,75,.15);border:1px solid rgba(200,168,75,.4);color:#8a6d1f;font-size:.62rem;font-weight:800;padding:3px 12px;border-radius:20px;letter-spacing:.5px">EXAM-ACCURATE SIMULATION</div>
      </div>
      <div class="fac gap8 mb14" style="text-align:center">
        <div style="flex:1;background:var(--border3);border-radius:8px;padding:10px 4px"><div class="fw8 f-md">${count}</div><div class="c-xs">Questions</div></div>
        <div style="flex:1;background:var(--border3);border-radius:8px;padding:10px 4px"><div class="fw8 f-md">${taskCount}</div><div class="c-xs">Tasks</div></div>
        <div style="flex:1;background:var(--border3);border-radius:8px;padding:10px 4px"><div class="fw8 f-md">${mins}</div><div class="c-xs">Minutes</div></div>
      </div>
      <div class="f-sm lh-16 c-muted mb14">
        <p class="mb10"><strong style="color:var(--text)">This is the closest simulation to the real ${examLabel} exam we can build</strong> — every task weighted in exactly the proportion the real exam uses, based on genuine FSCA result data.</p>
        <p class="mb10">Go somewhere with no distractions. Have water available. Switch on Do Not Disturb so calls, messages, and notifications can't interrupt you.</p>
        <p class="mb0"><strong style="color:var(--text)">This exam is meant to be completed in one sitting</strong> — just like the real thing. Leaving mid-exam will end your attempt.</p>
      </div>
      <button class="btn btn-gold btn-full" onclick="closeModal();_startSimInner('${mode}',${count},${mins})">I'm ready — begin the exam</button>
    `);
    return;
  }
  _startSimInner(mode, count, mins);
}

function _startSimInner(mode, count, mins) {
  clearSimTimer();
  const featureKey = (mode==='RE5_HARD'||mode==='RE1_HARD') ? 'hard_sim'
    : (mode==='RE5_BLUEPRINT'||mode==='RE1_BLUEPRINT') ? 'blueprint_exam'
    : 'simulation';
  _setActiveFeature(featureKey);
  const base = mode==='RE1' ? QB_RE1
             : mode==='Combo' ? [...QB_RE5,...QB_RE1]
             : mode==='RE5_HARD' ? QB_HARD_RE5
             : mode==='RE1_HARD' ? QB_HARD_RE1
             : mode==='RE5_BLUEPRINT' ? BLUEPRINT_EXAM_RE5
             : mode==='RE1_BLUEPRINT' ? BLUEPRINT_EXAM_RE1
             : QB_RE5;
  // Hard banks and Blueprint Exams are fixed, curated sets — use every
  // question exactly once (buildPool), never repeat/fill like the regular
  // practice pools. A real exam doesn't recycle questions to hit a count.
  const isFixedSet = mode==='RE5_HARD' || mode==='RE1_HARD' || mode==='RE5_BLUEPRINT' || mode==='RE1_BLUEPRINT';
  const qs = isFixedSet
    ? buildPool(base, Math.min(count, base.length))
    : buildPoolFill(base, count); // ALL standard entry points use buildPoolFill

  App.Analytics.track(App.Analytics.Events.SIM_START, { mode, count, mins });
  STATE.mock = { mode, questions:qs, cur:0, answers:new Array(qs.length).fill(undefined), timeLeft:mins*60, showReview:false };

  // Single-sitting enforcement for Blueprint Exams — warns before leaving
  // the tab/app, matching real exam conditions ("must be completed in one
  // sitting"). Cleared automatically the moment the exam finishes.
  if (mode==='RE5_BLUEPRINT' || mode==='RE1_BLUEPRINT') {
    window.onbeforeunload = () => 'Leaving now will end your Blueprint Exam attempt. Are you sure?';
  }

  document.getElementById('pageContent').innerHTML = `<div class="anim-in" id="mArea"></div>`;
  document.querySelectorAll('.bnav-item').forEach(b=>b.classList.remove('active'));
  document.getElementById('bnav-mock').classList.add('active');
  renderSimQ();

  _simTimer = setInterval(() => {
    if (!STATE.mock) { clearSimTimer(); return; }
    STATE.mock.timeLeft--;
    const el = document.getElementById('simTimer');
    if (el) {
      const m=Math.floor(STATE.mock.timeLeft/60), sc=STATE.mock.timeLeft%60;
      el.textContent = `${String(m).padStart(2,'0')}:${String(sc).padStart(2,'0')}`;
      if (STATE.mock.timeLeft < 300) el.style.color = 'var(--red)';
    }
    if (STATE.mock.timeLeft <= 0) { clearSimTimer(); finishSim(); }
  }, 1000);
}

function renderSimQ() {
  const s = STATE.mock; if (!s) return;
  if (s.showReview) { renderSimReview(); return; }
  const q = s.questions[s.cur]; if (!q) { finishSim(); return; }
  const m=Math.floor(s.timeLeft/60), sc=s.timeLeft%60;
  const total=s.questions.length;
  const showDots=total<=60;
  let dotsHtml='';
  if (showDots) {
    dotsHtml=`<div class="sim-dots-wrap">${s.answers.map((a,i)=>`<div class="sdot ${i===s.cur?'cur':a!==undefined?'ans':''}" onclick="simGoto(${i})" title="Q${i+1}">${i+1}</div>`).join('')}</div>`;
  } else {
    const block=Math.floor(s.cur/10); const start=block*10;
    dotsHtml=`<div class="sim-dots-wrap">${s.answers.slice(start,start+10).map((a,i)=>{const gi=start+i;return`<div class="sdot ${gi===s.cur?'cur':a!==undefined?'ans':''}" onclick="simGoto(${gi})">${gi+1}</div>`;}).join('')}</div><div class="f-xs c-muted mb6">${s.answers.filter(a=>a!==undefined).length}/${total} answered</div>`;
  }
  const area=document.getElementById('mArea'); if(!area)return;
  area.innerHTML=`
  <div class="fb mb8"><div class="f-xs fw6 c-muted" style="text-transform:uppercase">${s.mode} SIM &middot; Q${s.cur+1}/${total}</div><div class="fw8 f-sm" id="simTimer" style="font-variant-numeric:tabular-nums">${String(m).padStart(2,'0')}:${String(sc).padStart(2,'0')}</div></div>
  <div class="pbar pbar-h6 mb12"><div class="pbar-fill pf-navy" style="width:${(s.cur/total)*100}%"></div></div>
  ${dotsHtml}
  <div class="fac gap8 mb10"><span class="chip f-xs">${q.t}</span>${levelBadge(q.lv)}</div>
  <div class="fw6 lh-16 mb14" style="font-size:.93rem">${q.q}</div>
  ${q.o.map((o,i)=>`<div class="qopt mb8 ${s.answers[s.cur]===i?'selected':''}" onclick="simAns(${i})"><div class="okey">${['A','B','C','D'][i]}</div><div class="f-sm lh-14">${o}</div></div>`).join('')}
  <div class="fac gap8 mt12">
    ${s.cur>0?`<button class="btn btn-outline btn-sm" onclick="simGoto(${s.cur-1})">Prev</button>`:''}
    ${s.cur<total-1?`<button class="btn btn-navy btn-md" style="flex:1" onclick="simGoto(${s.cur+1})">Next</button>`:`<button class="btn btn-gold btn-md" style="flex:1" onclick="showSimReview()">Review &amp; Submit</button>`}
  </div>
  <button class="btn btn-ghost btn-xs btn-full mt8 c-xs" onclick="if(confirm('End this simulation early?')){clearSimTimer();App.Behaviour.track('abandonMock');finishSim()}">End early</button>`;
}

function simAns(idx)    { const s=STATE.mock; if(!s||idx<0||idx>=s.answers.length)return; s.answers[s.cur]=idx; renderSimQ(); }
function simGoto(idx)   { const s=STATE.mock; if(!s||idx<0||idx>=s.questions.length)return; s.cur=idx; renderSimQ(); }
function showSimReview() { const s=STATE.mock; if(!s)return; s.showReview=true; renderSimReview(); }

function renderSimReview() {
  const s=STATE.mock; if(!s)return;
  const unanswered=s.answers.map((a,i)=>a===undefined?i:-1).filter(i=>i>=0);
  const answered=s.answers.filter(a=>a!==undefined).length;
  const m=Math.floor(s.timeLeft/60),sc=s.timeLeft%60;
  const area=document.getElementById('mArea'); if(!area)return;
  area.innerHTML=`
  <div class="fw8 f-lg mb4">Review Before Submitting</div>
  <div class="c-muted f-sm mb12">Click any question to return to it. Submit when ready.</div>
  <div class="fac gap8 mb12">
    <span class="badge badge-navy">${answered} Answered</span>
    ${unanswered.length>0?`<span class="badge badge-amber">${unanswered.length} Unanswered</span>`:'<span class="badge badge-green">All Answered</span>'}
    <span class="fw7 f-sm" id="simTimer" style="color:var(--navy);font-variant-numeric:tabular-nums">${String(m).padStart(2,'0')}:${String(sc).padStart(2,'0')}</span>
  </div>
  ${unanswered.length>0?`<div class="card card-amber p12 mb12"><div class="fw7 f-sm c-amber mb4">Unanswered Questions</div><div class="fac gap4 fw">${unanswered.map(i=>`<button class="btn btn-xs btn-gold" onclick="simGotoFromReview(${i})">Q${i+1}</button>`).join('')}</div></div>`:''}
  <div style="max-height:280px;overflow-y:auto;margin-bottom:16px">
    ${s.questions.map((q,i)=>{const qText=(q&&q.q)?q.q.substring(0,55)+(q.q.length>55?'...':``):'Question '+(i+1);return`<div class="review-q-item ${s.answers[i]!==undefined?'answered':'unanswered'}" onclick="simGotoFromReview(${i})"><div class="fb"><div class="fw6 f-sm">${qText}</div><span class="badge ${s.answers[i]!==undefined?'badge-navy':'badge-amber'}">${s.answers[i]!==undefined?'Done':'—'}</span></div></div>`;}).join('')}
  </div>
  <button class="btn btn-gold btn-lg btn-full mb8" onclick="clearSimTimer();finishSim()">Submit Exam</button>
  <button class="btn btn-outline btn-sm btn-full" onclick="STATE.mock.showReview=false;renderSimQ()">Continue Answering</button>`;
}

function simGotoFromReview(idx) { const s=STATE.mock; if(!s)return; s.showReview=false; s.cur=idx; renderSimQ(); }

function finishSim() {
  clearSimTimer();
  window.onbeforeunload = null; // clear single-sitting warning — exam is done
  const s=STATE.mock; if(!s)return;
  let cor=0;
  s.questions.forEach((q,i)=>{ if(q&&s.answers[i]!==undefined&&s.answers[i]===q.a)cor++; });
  const pct=Math.round(cor/s.questions.length*100);
  const _pm = passMarkFor(s.mode);
  const passProb=Math.min(99,Math.max(1,Math.round(pct*0.85+(pct>=_pm?15:-10))));
  const isBlueprintExam = s.mode==='RE5_BLUEPRINT' || s.mode==='RE1_BLUEPRINT';
  const examOfficialName = s.mode==='RE5_BLUEPRINT'
    ? 'RE5 Regulatory Examination: Representatives in all Categories of FSPs'
    : s.mode==='RE1_BLUEPRINT'
    ? 'RE1 Regulatory Examination: FSPs and Key Individuals in all Categories of FSPs'
    : '';
  const completedDate = new Date().toLocaleDateString('en-ZA', { day:'2-digit', month:'2-digit', year:'numeric' });

  const tb={};
  s.questions.forEach((q,i)=>{ if(!q)return; if(!tb[q.t])tb[q.t]={c:0,t:0}; tb[q.t].t++; if(s.answers[i]!==undefined&&s.answers[i]===q.a)tb[q.t].c++; });
  const weakTopics=Object.entries(tb).filter(([,d])=>d.t>0&&d.c/d.t<0.65).sort((a,b)=>a[1].c/a[1].t-b[1].c/b[1].t).slice(0,5);
  const coach = App.Coaching.getSimDebrief(pct, s.questions.length, s.mode, weakTopics);

  // Official task breakdown — only Blueprint Exam questions carry taskNum,
  // so this mirrors the real Moonstone result slip format exactly.
  const taskBreakdown = {};
  if (isBlueprintExam) {
    s.questions.forEach((q,i)=>{
      if (!q || q.taskNum == null) return;
      if (!taskBreakdown[q.taskNum]) taskBreakdown[q.taskNum] = { title: q.taskTitle||q.t, c:0, t:0 };
      taskBreakdown[q.taskNum].t++;
      if (s.answers[i]!==undefined && s.answers[i]===q.a) taskBreakdown[q.taskNum].c++;
    });
  }

  App.Progress.recordMock(s.mode,cor,s.questions.length,pct);
  App.Analytics.track(App.Analytics.Events.SIM_COMPLETE,{mode:s.mode,pct,passProb});

  const area=document.getElementById('mArea'); if(!area)return;
  area.innerHTML=`
  <div class="card card-navy" style="padding:24px;margin-bottom:12px;text-align:center;border:none">
    ${isBlueprintExam ? `<div style="display:inline-block;background:rgba(200,168,75,.15);border:1px solid rgba(200,168,75,.4);color:var(--gold);font-size:.65rem;font-weight:800;padding:3px 10px;border-radius:20px;margin-bottom:10px;letter-spacing:.4px">🎯 BLUEPRINT EXAM SIMULATION RESULT</div><br>` : ''}
    ${isBlueprintExam ? `<div style="color:rgba(255,255,255,.5);font-size:.68rem;line-height:1.7;margin-bottom:10px;text-align:left;max-width:280px;margin-left:auto;margin-right:auto">
      <div>Name of the examination: <span style="color:rgba(255,255,255,.8)">${examOfficialName}</span></div>
      <div>Simulation completed: <span style="color:rgba(255,255,255,.8)">${completedDate}</span></div>
      <div>Result Status: <strong style="color:${pct>=_pm?'#6ee7b7':'#fca5a5'}">${pct>=_pm?'Pass':'Fail'}</strong></div>
      <div>Result: (%) <span style="color:rgba(255,255,255,.8)">${pct}</span></div>
    </div>` : ''}
    <div style="font-size:3.5rem;font-weight:900;color:var(--gold);line-height:1">${pct}%</div>
    <div class="fw7 c-white f-lg mt4">${pct>=_pm?'Simulation Passed':'Below Pass Mark'}</div>
    <div style="color:rgba(255,255,255,.55);font-size:.8rem;margin-top:4px">${cor}/${s.questions.length} correct &middot; Pass mark: ${_pm}%</div>
    <div style="margin-top:12px;display:flex;gap:8px;justify-content:center">
      <div style="background:rgba(255,255,255,.08);border-radius:8px;padding:8px 14px;text-align:center">
        <div style="font-size:1.2rem;font-weight:900;color:${passProb>=65?'#6ee7b7':'#fca5a5'}">${passProb}%</div>
        <div style="color:rgba(255,255,255,.45);font-size:.65rem;text-transform:uppercase;letter-spacing:.6px">Pass Probability</div>
      </div>
    </div>
  </div>

  ${isBlueprintExam ? `
  <div class="card p14 mb12">
    <div class="fw7 f-sm mb2">Tasks used in this questionnaire</div>
    <div class="c-xs mb8" style="color:rgba(0,0,0,.4)">Presented in the same format as your official Moonstone result slip</div>
    <div style="overflow-x:auto"><table style="width:100%;border-collapse:collapse;font-size:.68rem">
      <thead><tr style="border-bottom:1.5px solid var(--border)">
        <th style="text-align:left;padding:6px 4px;color:rgba(0,0,0,.5)">Task</th>
        <th style="text-align:center;padding:6px 4px;color:rgba(0,0,0,.5)">Qs&nbsp;per<br>task</th>
        <th style="text-align:center;padding:6px 4px;color:rgba(0,0,0,.5)">Wrong<br>per task</th>
        <th style="text-align:center;padding:6px 4px;color:rgba(0,0,0,.5)">%&nbsp;Right</th>
        <th style="text-align:left;padding:6px 4px;color:rgba(0,0,0,.5)">Description of task</th>
      </tr></thead>
      <tbody>
        ${Object.keys(taskBreakdown).sort((a,b)=>a-b).map(num => {
          const d = taskBreakdown[num];
          const wrong = d.t - d.c;
          const taskPct = Math.round(d.c/d.t*100);
          return `<tr style="border-bottom:1px solid var(--border2)">
            <td style="padding:6px 4px;font-weight:700">${num}</td>
            <td style="text-align:center;padding:6px 4px">${d.t}</td>
            <td style="text-align:center;padding:6px 4px">${wrong}</td>
            <td style="text-align:center;padding:6px 4px;font-weight:700;color:${taskPct>=65?'#16a34a':'#dc2626'}">${taskPct}%</td>
            <td style="padding:6px 4px;color:rgba(0,0,0,.65)">${esc(d.title)}</td>
          </tr>`;
        }).join('')}
      </tbody>
    </table></div>
    <div style="margin-top:12px;padding-top:12px;border-top:1px solid var(--border);color:rgba(0,0,0,.45)" class="c-xs lh-15">
      <div class="fw7 mb4" style="color:rgba(0,0,0,.55)">Disclaimer:</div>
      <div class="mb6">The breakdown of "questions per task" above is presented in the same format as your official Moonstone result slip, so it's familiar on exam day — but this is a Blueprint Exam simulation, not an actual regulatory examination result.</div>
      <div class="mb6">Task weighting is derived from real FSCA result data, but the specific questions are original practice content written for this app — not actual exam questions. Each real Moonstone sitting allocates a unique question distribution across the qualifying criteria tasks and will differ from this simulation and from every other sitting.</div>
      <div>Your official result will only ever come from Moonstone, the FSCA-appointed examination body.</div>
    </div>
  </div>` : ''}

  <div class="${coach.cls} coach-box mb12">
    <div class="fw7 f-sm mb4">${coach.headline}</div>
    <div class="f-sm lh-16 mb10">${coach.msg}</div>
    ${coach.actions && coach.actions.length > 0 ? `
    <div style="border-top:1px solid rgba(255,255,255,.12);padding-top:10px;margin-top:4px">
      <div class="fw7 f-xs mb6" style="text-transform:uppercase;letter-spacing:.5px;opacity:.7">Your Next Steps</div>
      ${coach.actions.map((a,i) => `<div class="f-xs lh-15 mb4" style="display:flex;gap:8px;align-items:flex-start"><span style="color:var(--gold);font-weight:700;flex-shrink:0">${i+1}.</span><span>${a}</span></div>`).join('')}
    </div>` : ''}
  </div>

  ${weakTopics.length>0?`<div class="card p14 mb12" style="border:1.5px solid #fca5a5;background:var(--red3)"><div class="fw7 f-sm mb8">Priority Study Areas</div>${weakTopics.map(([t,d])=>`<div class="fb mb6"><span class="f-sm">${t}</span><span class="badge badge-red">${Math.round(d.c/d.t*100)}%</span></div>`).join('')}<button class="btn btn-navy btn-sm mt8" onclick="openPage('library')">Study These in Library</button></div>`:''}

  <div style="background:rgba(200,168,75,.08);border:1.5px solid rgba(200,168,75,.25);border-radius:12px;padding:13px 14px;margin-bottom:12px;display:flex;align-items:center;gap:12px">
    <span style="font-size:1.4rem;flex-shrink:0">🛡️</span>
    <div style="flex:1">
      <div style="font-weight:700;font-size:.82rem;color:#8a6d1f;margin-bottom:2px">RE Guardian™ — Drill Your Weak Areas</div>
      <div style="font-size:.76rem;color:rgba(10,22,40,.65);line-height:1.5">${weakTopics.length>0?`Guardian has seen your results. Ask it to explain the ${weakTopics[0][0]} questions you missed.`:'Ask Guardian to explain any concepts from this simulation.'}</div>
    </div>
    <button class="btn btn-sm" style="background:var(--gold);color:var(--navy);font-weight:700;flex-shrink:0;font-size:.75rem" onclick="guardianFromSim('${weakTopics.length>0?esc(weakTopics[0][0]):''}',${pct},'${s.mode}')">Open →</button>
  </div>

  <div class="card p14 mb12"><div class="stitle">Topic Breakdown</div>${Object.entries(tb).map(([t,d])=>{const tp=d.t>0?Math.round(d.c/d.t*100):0;return`<div class="mb8"><div class="fb mb3"><span class="f-xs fw6 truncate" style="max-width:220px">${t}</span><span class="badge ${tp>=70?'badge-green':tp>=50?'badge-amber':'badge-red'}">${tp}%</span></div><div class="pbar pbar-h6"><div class="pbar-fill ${tp>=70?'pf-green':tp>=50?'pf-gold':'pf-red'}" style="width:${tp}%"></div></div></div>`;}).join('')}</div>
  ${STATE.isPro?`<div class="stitle">Incorrect Answers Review</div>${s.questions.filter((q,i)=>q&&s.answers[i]!==q.a).slice(0,8).map(q=>`<div class="card card-sm mb8"><div class="fac gap6 mb6"><span class="chip f-xs">${q.t}</span>${levelBadge(q.lv)}</div><div class="f-sm fw6 mb4">${q.q}</div><div class="f-xs c-green mb2">Correct: ${q.o[q.a]}</div><div class="f-xs c-muted lh-15">${q.e}</div></div>`).join('')}`:`<div class="card p14 tc" style="border:2px dashed var(--gold)"><div class="fw7 mb6">Full Review in Pro</div><div class="c-muted f-sm mb10 lh-16">See every missed question with explanations.</div><button class="btn btn-gold btn-sm" onclick="openPage('pro')">Upgrade to Pro</button></div>`}
  <button class="btn btn-outline btn-sm btn-full mt12" onclick="STATE.mock=null;openPage('mock')">Back to Simulations</button>`;
  STATE.mock = null;
}

// ══════════════════════════════════════════════════════
// STUDY LIBRARY
// ══════════════════════════════════════════════════════
function rLibrary() {
  const filter = App.Storage.get('libFilter','All');
  const search = App.Storage.get('libSearch','');
  const items  = LIBRARY.filter(l => {
    const mf = filter==='All'||l.exam===filter;
    const ms = !search||l.topic.toLowerCase().includes(search.toLowerCase())||l.content.toLowerCase().includes(search.toLowerCase());
    return mf&&ms;
  });
  return `
  <div class="fw8 f-xl mb4">Study Library</div>
  <div class="c-muted f-sm mb10">High-yield exam summaries, video lessons, and study guides</div>

  <div id="tourVideoLessons" style="background:linear-gradient(135deg,rgba(200,168,75,.12) 0%,rgba(200,168,75,.04) 100%);border:1.5px solid rgba(200,168,75,.3);border-radius:12px;padding:14px 16px;margin-bottom:14px;display:flex;align-items:center;gap:12px">
    <div style="background:rgba(200,168,75,.15);border-radius:8px;padding:8px;flex-shrink:0">
      <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#c8a84b" stroke-width="2"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2" ry="2"/></svg>
    </div>
    <div>
      <div style="font-weight:800;font-size:.85rem;color:#8a6d1f">📺 Video Lessons Now Live</div>
      <div style="font-size:.75rem;color:rgba(10,22,40,.6);margin-top:2px">9 of 17 RE5 lessons available · More dropping weekly · Open any topic below to watch</div>
    </div>
  </div>

  <input type="search" class="inp mb10" placeholder="Search topics..." value="${(search||'').replace(/"/g,'&quot;')}" oninput="App.Storage.set('libSearch',this.value);openPage('library')" aria-label="Search topics"/>
  <div class="fac gap6 mb14">${['All','RE5','RE1'].map(f=>`<button class="btn btn-sm ${filter===f?'btn-navy':'btn-outline'}" onclick="App.Storage.set('libFilter','${f}');App.Storage.set('libSearch','');openPage('library')">${f}</button>`).join('')}</div>
  ${items.length===0?'<div class="card p20 tc"><div class="c-muted">No topics found.</div></div>':''}
  ${items.map((l,i)=>`<div class="lib-item"><div class="lib-hdr" onclick="toggleLib('lb${i}')" role="button" aria-expanded="false" id="lbhdr${i}"><div class="fac gap8"><span class="badge ${l.exam==='RE5'?'badge-navy':l.exam==='RE1'?'badge-blue':'badge-gold'}">${l.exam}</span><span class="fw6 f-sm">${l.topic}</span></div><span class="c-xs" id="lb${i}arr">&#9660;</span></div><div class="lib-body" id="lb${i}"><div class="f-sm lh-16 c-muted">${l.content}</div><div class="fac gap8 mt12 flex-wrap"><button class="btn btn-navy btn-xs" onclick="startTopicQuiz('${l.exam==='RE1'?'RE1':'RE5'}','${esc(l.quizTopic)}')">Quiz This Topic</button><button class="btn btn-xs" style="background:${STATE.hasCoachFeedback?'rgba(200,168,75,.12)':'rgba(120,120,120,.1)'};border:1px solid ${STATE.hasCoachFeedback?'rgba(200,168,75,.3)':'rgba(120,120,120,.25)'};color:${STATE.hasCoachFeedback?'var(--gold)':'var(--text3)'};font-size:.7rem;padding:4px 10px;border-radius:6px" onclick="guardianFromLibrary('${esc(l.topic)}','${l.exam}')">🛡️ Ask Guardian${STATE.hasCoachFeedback?'':' · PRO'}</button></div></div></div>`).join('')}

  <div id="tourStudyGuides" class="stitle" style="margin-top:20px">Official Exam Success Guides</div>
  <div class="c-muted f-sm mb12">Full learner guides mapped to every official Moonstone Qualifying Criteria. Free preview for everyone — full guide unlocks with the matching Pro plan.</div>

  ${[
    { exam:'RE5', label:'RE5 Exam Success Guide', topics:17, tasks:8, qcs:47, ready:true },
    { exam:'RE1', label:'RE1 Exam Success Guide', topics:14, tasks:16, qcs:144, ready:true },
  ].map(g => {
    const unlocked = hasAccess(g.exam) && !!STATE.hasGuide;
    if (!g.ready) {
      return `<div class="card p16 mb12" style="border:1.5px dashed rgba(200,168,75,.4)">
        <div class="fac gap8 mb4"><span class="badge ${g.exam==='RE5'?'badge-navy':'badge-blue'}">${g.exam}</span><span class="fw7 f-sm">${g.label}</span></div>
        <div class="c-muted f-xs lh-15">Coming soon — launching 3 August 2026.</div>
      </div>`;
    }
    return `<div class="card p16 mb12" style="border:1.5px solid ${unlocked?'var(--gold)':'var(--border2)'}">
      <div class="fb mb8">
        <div class="fac gap8"><span class="badge ${g.exam==='RE5'?'badge-navy':'badge-blue'}">${g.exam}</span><span class="fw7 f-sm">${g.label}</span></div>
      </div>
      <div class="fac gap10 mb10">
        <div class="c-xs"><strong>${g.topics}</strong> topics</div>
        <div class="c-xs"><strong>${g.tasks}</strong> tasks</div>
        <div class="c-xs"><strong>${g.qcs}</strong> QCs mapped</div>
      </div>
      <div class="fac gap8 flex-wrap">
        <button class="btn btn-outline btn-xs" onclick="showStudyGuidePreview('${g.exam}')">Free Preview</button>
        ${unlocked
          ? `<button class="btn btn-navy btn-xs" onclick="openStudyGuide('${g.exam}', this)">View Full Guide</button>`
          : `<button class="btn btn-xs" style="background:var(--gold);color:#0a1628;font-weight:700" onclick="openPage('pro')">🔒 Unlock Full Guide on Pro</button>`}
      </div>
    </div>`;
  }).join('')}`;
}
function toggleLib(id) {
  const b=document.getElementById(id); const a=document.getElementById(id+'arr'); const h=document.getElementById('lbhdr'+id.replace('lb',''));
  if(!b)return; b.classList.toggle('open'); if(a)a.innerHTML=b.classList.contains('open')?'&#9650;':'&#9660;'; if(h)h.setAttribute('aria-expanded',String(b.classList.contains('open')));
}

// Study guide full access — fetches a real, time-limited signed URL from
// Supabase Storage. Access is enforced server-side by the RLS policy, so
// this will fail gracefully (not silently leak the file) for a user whose
// tier doesn't actually cover this exam, even if something bypassed the
// button's own tier check.
async function openStudyGuide(examType, btnEl) {
  if (!App.Auth.isAuthenticated()) { openPage('pro'); return; }
  const originalText = btnEl.textContent;
  btnEl.textContent = 'Loading…';
  btnEl.disabled = true;
  const result = await App.Supabase.getStudyGuideUrl(examType);
  btnEl.textContent = originalText;
  btnEl.disabled = false;
  if (!result.ok) {
    console.error('[RMH] getStudyGuideUrl failed:', result.error);
    showRequestComplimentaryGuide(examType);
    return;
  }
  window.open(result.url, '_blank', 'noopener');
}

// Complimentary guide request — safety net for Pro members who hit a sync
// hiccup (e.g. tier not yet reflected server-side) and can't open a guide
// they've already paid for. Sends the guide directly rather than leaving
// them stuck on a dead-end error.
function showRequestComplimentaryGuide(examType) {
  const wa = `https://wa.me/${App.Config.whatsapp}?text=${encodeURIComponent(`Hi, I'm a Pro member but can't open my ${examType} Study Guide in the app. Could you please send it to me directly?`)}`;
  const mail = `mailto:${App.Config.email}?subject=${encodeURIComponent(`Complimentary ${examType} Study Guide Request`)}&body=${encodeURIComponent(`Hi,\n\nI'm a Pro member but can't open my ${examType} Study Guide in the app. Could you please send it to me directly?\n\nMy registered email is: `)}`;
  showModal('Study Guide Temporarily Unavailable', `
  <div style="text-align:center;padding:4px 0 16px">
    <div style="font-size:2rem;margin-bottom:10px">📘</div>
    <div style="color:var(--text2);font-size:.85rem;line-height:1.6">We couldn't load your <strong>${examType} Study Guide</strong> right now — this is a sync issue on our end, not a problem with your account. Request it below and we'll send it to you directly, complimentary, within 2 hours.</div>
  </div>
  <a href="${wa}" target="_blank" rel="noopener"
     style="display:flex;align-items:center;justify-content:center;gap:10px;background:#25D366;color:#fff;font-weight:700;padding:13px 16px;border-radius:10px;text-decoration:none;margin-bottom:10px;font-size:.88rem">
    💬 Request via WhatsApp
  </a>
  <a href="${mail}"
     style="display:flex;align-items:center;justify-content:center;gap:10px;background:var(--navy2);color:var(--gold);font-weight:700;padding:13px 16px;border-radius:10px;text-decoration:none;margin-bottom:14px;font-size:.88rem;border:1px solid rgba(200,168,75,.3)">
    ✉️ Request via Email
  </a>
  <button class="btn btn-ghost btn-sm btn-full" onclick="closeModal()">Close</button>`);
}

// Free preview — built from the guide's own real cover, contents, and
// opening section rather than a separate PDF file, so it's lightweight,
// always in sync with the real guide, and doesn't need its own Storage
// upload.
function showStudyGuidePreview(examType) {
  const content = examType === 'RE5' ? STUDY_GUIDE_PREVIEW.RE5 : STUDY_GUIDE_PREVIEW.RE1;
  const unlocked = hasAccess(examType) && !!STATE.hasGuide;
  showModal(`📘 ${examType} Exam Success Guide — Preview`, `
    <div class="f-sm lh-16" style="color:var(--text)">${content}</div>
    <button class="btn btn-gold btn-full mt14" onclick="closeModal();${unlocked ? '' : `openPage('pro')`}">${unlocked ? 'Close' : 'Unlock the Full Guide →'}</button>
  `);
}

// Deep-link into Library: filters via the existing search mechanism so the
// target topic lands at index 0, then auto-expands and scrolls to it.
// Reuses toggleLib rather than duplicating Library's render/state logic.
function openLibraryTopic(topicName) {
  App.Storage.set('libFilter', 'All');
  App.Storage.set('libSearch', topicName);
  openPage('library');
  setTimeout(() => {
    const body = document.getElementById('lb0');
    if (body && !body.classList.contains('open')) toggleLib('lb0');
    document.getElementById('lbhdr0')?.scrollIntoView({ behavior: 'smooth', block: 'start' });
  }, 150);
}

// ══════════════════════════════════════════════════════
// CRASH PACK
// ══════════════════════════════════════════════════════
function showCrashPack() {
  showModal('Last 7 Days Crash Pack',`
  <div class="tc mb14"><div style="font-size:.68rem;font-weight:800;color:var(--gold);text-transform:uppercase;letter-spacing:1px;margin-bottom:4px">Fully Unlocked</div><div class="fw8 f-xl mb4">7-Day Exam Intensive</div><div class="c-muted f-sm lh-16">Complete exam-day preparation for the final stretch.</div></div>
  <div style="border-left:3px solid var(--gold);padding-left:14px;margin-bottom:16px"><div class="fw7 f-sm mb4">What's included</div><div class="f-sm lh-18 c-muted">25 rapid-fire questions with full explanations &middot; Top 50 most-tested FAIS facts &middot; Day-before checklist &middot; Coach feedback on your result &middot; Full Study Library access</div></div>
  <div class="g2 mb10"><button class="btn btn-navy btn-sm" onclick="closeModal();startCrashQuiz()">Rapid-Fire Quiz</button><button class="btn btn-gold btn-sm" onclick="closeModal();showTop50List()">Top 50 Facts</button></div>
  <button class="btn btn-outline btn-sm btn-full mb8" onclick="closeModal();showCrashChecklist()">Day-Before Checklist</button>
  <button class="btn btn-outline btn-sm btn-full mb8" onclick="closeModal();openPage('library')">Study Library Notes</button>
  <button class="btn btn-ghost btn-sm btn-full" onclick="closeModal()">Close</button>`);
}

// ── RE Fact Disk™ ──────────────────────────────────────────────────────────
// 50 facts presented as 100 faces on a continuous ribbon. Flipping forward
// advances (question → answer → next question); flipping back reverses.
// Self-contained: all state lives on _RFD, nothing else in the app touches it.
const RFD_FREE_FLIPS = 8;   // free preview: 4 facts (question + answer each)
const _RFD = { order:[], step:0, rot:0, flips:0, shuffled:false, drag:false, x0:0, dx:0, auto:false, timer:null, locked:false, busy:false, paintedDir:0 };

function rfdIsPro(){ return hasAccess('RE5') || hasAccess('RE1'); }

// Called after every completed turn. Locks the disc once a free user has used
// their preview, and shows what they're missing rather than a bare paywall.
// ── Free Fact Disk quota (lifetime, persists across sessions/reloads) ─────
function _getFreeFlipsUsed() {
  const raw = localStorage.getItem('rmh_free_facts_lifetime');
  const n = raw ? parseInt(raw, 10) : 0;
  return Number.isFinite(n) && n >= 0 ? n : 0;
}
function _setFreeFlipsUsed(n) {
  localStorage.setItem('rmh_free_facts_lifetime', String(n));
}

function _rfdAfterFlip(){
  const b=document.getElementById('rfdBadge');
  if(b && _RFD.flips>=3) b.style.display='none';
  if(rfdIsPro() || _RFD.locked) return;
  _setFreeFlipsUsed(_RFD.flips);
  if(_RFD.flips >= RFD_FREE_FLIPS){ _RFD.locked = true; rfdStopAuto(); setTimeout(rfdShowLock, 620); }
}

function rfdShowLock(){
  const scene=document.querySelector('.rfd-scene');
  if(!scene) return;
  scene.insertAdjacentHTML('afterend', `
  <div id="rfdLock" class="card p16" style="margin-top:18px;border:1.5px solid var(--gold);
       background:linear-gradient(135deg,rgba(200,168,75,.12),rgba(200,168,75,.03))">
    <div class="fac gap8 mb8">
      <span style="font-size:1.1rem">&#128274;</span>
      <span class="fw8 f-sm">You've seen 4 of 50 facts</span>
    </div>
    <div class="f-sm lh-16 mb10" style="color:var(--text2)">
      Reading a fact puts it in your head for an hour. <b>Recalling</b> it puts it there for
      the exam. That is the whole point of the disc &mdash; you see the cue, your brain
      reaches for the answer, and the reaching is what builds the memory.
    </div>
    <div class="f-sm lh-16 mb10" style="color:var(--text2)">
      The other <b>46 facts</b> are the ones examiners lean on hardest &mdash; the exact
      figures that decide marks. Every one verified against the Act, the Board Notices
      and the FIC.
    </div>
    <div class="f-xs lh-16 mb12" style="color:var(--text2)">
      &#10003; All 50 facts, unlimited flips &mdash; recall them until they are automatic<br>
      &#10003; <b>Shuffle</b> breaks the order, so you learn the fact and not its position<br>
      &#10003; <b>Auto-flip</b> for hands-free revision on the taxi, in a queue, before bed<br>
      &#10003; Two minutes a day beats an hour of re-reading the night before<br>
      &#10003; Plus Guardian, the full Study Guide and the Blueprint Exam
    </div>
    <button class="btn btn-gold btn-sm btn-full" onclick="rfdStopAuto();closeModal();openPage('pro')">Unlock all 50 facts</button>
    <div class="f-xs c-muted tc mt8">You keep the 4 you've seen &mdash; come back any time.</div>
  </div>`);
  const c=document.getElementById('rfdCard');
  if(c){ c.style.opacity='.5'; c.style.pointerEvents='none'; }
}


// ── Help & contact hub ─────────────────────────────────────────────────────
// Deliberately separate from Guardian. Guardian answers study questions;
// this is for reaching a human about access, payments and problems. Mixing
// them means billing issues get asked of an AI and never reach anyone.
function showHelp() {
  const u = App.Auth.getLocalUser() || {};
  const wa = `https://wa.me/${App.Config.whatsapp}?text=${encodeURIComponent('Hi, I need help with RE Mastery Hub.')}`;
  const mail = `mailto:${App.Config.email}?subject=${encodeURIComponent('RE Mastery Hub — Help')}&body=${encodeURIComponent('Hi,\n\nMy registered email is: ' + (u.email || '') + '\n\nHow can we help:\n')}`;
  const row = (icon, bg, title, sub, onclick) => `
    <button onclick="${onclick}" style="display:flex;align-items:center;gap:12px;width:100%;
      background:var(--card);border:1px solid var(--border);border-radius:11px;
      padding:13px 14px;margin-bottom:9px;cursor:pointer;text-align:left">
      <span style="width:38px;height:38px;border-radius:10px;background:${bg};display:flex;
        align-items:center;justify-content:center;font-size:1.1rem;flex-shrink:0">${icon}</span>
      <span style="flex:1;min-width:0">
        <span style="display:block;font-weight:700;font-size:.85rem;color:var(--text)">${title}</span>
        <span style="display:block;font-size:.72rem;color:var(--text3);line-height:1.4">${sub}</span>
      </span>
      <span style="color:var(--text3);font-size:1rem">&rsaquo;</span>
    </button>`;

  showModal('Need help?', `
  <div class="f-sm c-muted lh-16 mb14">Message us directly &mdash; we usually reply within a few hours during the day.</div>
  ${row('&#128172;','rgba(37,211,102,.15)','WhatsApp us','Fastest way to reach a human',`window.open('${wa}','_blank','noopener')`)}
  ${row('&#9993;&#65039;','rgba(59,130,246,.12)','Email us','Best for account or payment issues',`window.location.href='${mail}'`)}
  ${row('&#11088;','rgba(200,168,75,.15)','Rate the app','Tell us what is working, or what is not','closeModal();openReviewSystem()')}
  ${row('&#128737;&#65039;','rgba(200,168,75,.12)','Ask RE Guardian&trade;','Study questions &mdash; not account help',"closeModal();openPage('guardian')")}
  <div class="card p12 mt6" style="background:var(--bg2);border:1px solid var(--border)">
    <div class="f-xs c-muted lh-16">
      <b style="color:var(--text)">Paid but still on Free?</b> Message us on WhatsApp with your
      payment reference and we will activate you straight away.
    </div>
  </div>
  <button class="btn btn-navy btn-sm btn-full mt12" onclick="closeModal()">Close</button>`);
}


// Scannable list of the Top 50. The RE Fact Disk™ lives on the Home screen for
// drilling; this is the read-through version people want in their final week.
function showTop50List() {
  showModal('Top 50 Most-Tested Facts', `
  <div class="rfd-brand" style="margin-bottom:10px">
    <div class="rfd-name" style="font-size:.98rem"><span class="re">RE</span> Fact Disk<sup>&trade;</sup></div>
    <div class="rfd-rule"></div>
    <div class="rfd-tag" style="font-size:.55rem">Reference List</div>
  </div>
  <div class="c-muted f-sm mb12 tc">Every figure verified against the Act, the Board Notices and the FIC.</div>
  <div style="max-height:56vh;overflow-y:auto;-webkit-overflow-scrolling:touch">
    ${TOP50.map(f=>{
      const m=f.match(/^(\d+)\.\s*(.*)$/);
      const num=m?m[1]:'', body=m?m[2]:f;
      const i=body.indexOf(':');
      const label=i>-1?body.slice(0,i):body, val=i>-1?body.slice(i+1).trim():'';
      return `<div style="display:flex;gap:10px;padding:9px 0;border-bottom:1px solid var(--border)">
        <span style="color:var(--gold);font-weight:800;font-size:.7rem;min-width:20px;flex-shrink:0;padding-top:2px">${num}</span>
        <span style="flex:1;min-width:0">
          <span style="display:block;font-size:.78rem;font-weight:700;color:var(--text);line-height:1.45">${label}</span>
          ${val?`<span style="display:block;font-size:.76rem;color:var(--text2);line-height:1.5;margin-top:1px">${val}</span>`:''}
        </span>
      </div>`;
    }).join('')}
  </div>
  ${rfdIsPro()
    ? '<button class="btn btn-gold btn-sm btn-full mt12" onclick="closeModal();showTop50()">Drill these on the RE Fact Disk&trade; &rsaquo;</button>'
    : '<button class="btn btn-gold btn-sm btn-full mt12" onclick="closeModal();showTop50()">&#128274; Try the RE Fact Disk&trade; &mdash; 4 facts free &rsaquo;</button>'}
  <button class="btn btn-navy btn-sm btn-full mt8" onclick="closeModal()">Done</button>`);
}

function showTop50() {
  _setActiveFeature('fact_disk');
  _RFD.order = [...Array(FACT_DISK.length).keys()];
  _RFD.step = 0; _RFD.rot = 0; _RFD.shuffled = false;
  _RFD.flips = rfdIsPro() ? 0 : _getFreeFlipsUsed();
  _RFD.locked = !rfdIsPro() && _RFD.flips >= RFD_FREE_FLIPS;

  if (_RFD.locked) {
    showModal('RE Fact Disk™', `
    <div class="rfd-brand">
      <div class="rfd-name"><span class="re">RE</span> Fact Disk<sup>&trade;</sup></div>
      <div class="rfd-rule"></div>
      <div class="rfd-tag">Top 50 Most-Tested Facts</div>
    </div>
    <div class="card p16" style="margin-top:14px;border:1.5px solid var(--gold);background:linear-gradient(135deg,rgba(200,168,75,.12),rgba(200,168,75,.03))">
      <div class="fac gap8 mb8"><span style="font-size:1.1rem">&#128274;</span><span class="fw8 f-sm">You've used your free preview — 4 of 50 facts</span></div>
      <div class="f-sm lh-16 mb12" style="color:var(--text2)">Unlock all 50 facts, unlimited flips, shuffle and auto-flip mode with Pro.</div>
      <button class="btn btn-gold btn-sm btn-full" onclick="closeModal();openPage('pro')">Unlock all 50 facts</button>
    </div>
    <button class="btn btn-navy btn-sm btn-full mt8" onclick="closeModal()">Done</button>`);
    return;
  }

  showModal('RE Fact Disk™', `
  <div class="rfd-brand">
    <div class="rfd-name"><span class="re">RE</span> Fact Disk<sup>&trade;</sup></div>
    <div class="rfd-rule"></div>
    <div class="rfd-tag">Top 50 Most-Tested Facts</div>
  </div>
  <div class="rfd-sub">Drag the disc to flip &middot; right-to-left advances, left-to-right goes back${rfdIsPro()?'':' <br><span style="color:var(--gold);font-weight:700">Free preview &mdash; 4 facts</span>'}</div>
  <div class="rfd-scene">
    <div class="rfd-shadow" id="rfdShadow"></div>
    <div class="rfd-card" id="rfdCard">
      <div class="rfd-face rfd-navy" id="rfdA">
        <div class="rfd-badge" id="rfdBadge">NEW</div>
        <div class="rfd-num" id="rfdNA"></div>
        <div class="rfd-txt" id="rfdTA"></div>
        <div class="rfd-hint" id="rfdHA">drag to reveal</div>
      </div>
      <div class="rfd-face rfd-cream rfd-b" id="rfdB">
        <div class="rfd-num" id="rfdNB"></div>
        <div class="rfd-txt" id="rfdTB"></div>
        <div class="rfd-hint" id="rfdHB">drag for next</div>
      </div>
    </div>
  </div>
  <div class="rfd-meta">
    <span class="f-sm fw8"><span style="color:var(--gold);font-size:1rem" id="rfdCur">1</span> / ${FACT_DISK.length}</span>
    <span class="f-xs c-muted" id="rfdMode">Sequential</span>
  </div>
  <div class="rfd-bar"><div class="rfd-fill" id="rfdFill" style="width:2%"></div></div>
  <div class="g3 mb10">
    <button class="btn btn-outline btn-sm" id="rfdAuto" onclick="rfdToggleAuto()">&#9654; Auto-flip</button>
    <button class="btn btn-outline btn-sm" onclick="rfdShuffle()">Shuffle</button>
    <button class="btn btn-outline btn-sm" onclick="rfdReset()">Reset</button>
  </div>
  <button class="btn btn-navy btn-sm btn-full" onclick="rfdStopAuto();closeModal()">Done</button>`);

  setTimeout(rfdInit, 0);
}

function _rfdN(){ return FACT_DISK.length * 2; }
function _rfdData(s){
  const N=_rfdN(); s=((s%N)+N)%N;
  const c=FACT_DISK[_RFD.order[Math.floor(s/2)]];
  const isQ=(s%2===0);
  return { c, isQ, txt:isQ?c.front:c.back, idx:Math.floor(s/2) };
}
function _rfdFaceAt(r){ return ((Math.round(r/180)%2)+2)%2===0 ? 'A' : 'B'; }
function _rfdPaint(key, s){
  const d=_rfdData(s);
  const n=document.getElementById('rfdN'+key), t=document.getElementById('rfdT'+key),
        f=document.getElementById('rfd'+key),  h=document.getElementById('rfdH'+key);
  if(!n||!t||!f) return;
  n.textContent='FACT '+String(d.c.n).padStart(2,'0')+(d.isQ?'':' \u00b7 ANSWER');
  t.textContent=d.txt;
  t.className='rfd-txt'+(d.txt.length>(d.isQ?58:88)?' sm':'');
  f.classList.toggle('rfd-navy', d.isQ);
  f.classList.toggle('rfd-cream', !d.isQ);
  h.textContent=d.isQ?'drag to reveal':'drag for next';
  const badge=document.getElementById('rfdBadge');
  if(key==='A'&&badge) badge.style.display=(_RFD.flips<3&&d.isQ)?'':'none';
}
function _rfdRot(r){
  const c=document.getElementById('rfdCard'), sh=document.getElementById('rfdShadow');
  if(!c) return;
  c.style.transform='rotateY('+r+'deg)';
  const t=Math.abs(Math.sin(r*Math.PI/180));
  if(sh){ sh.style.width=(58-t*22)+'%'; sh.style.opacity=(1-t*0.55); }
}
function _rfdMeta(){
  const d=_rfdData(_RFD.step);
  const cur=document.getElementById('rfdCur'), fill=document.getElementById('rfdFill');
  if(cur) cur.textContent=d.idx+1;
  if(fill) fill.style.width=((d.idx+1)/FACT_DISK.length*100)+'%';
}
function _rfdGo(dir){
  if(_RFD.locked || _RFD.busy) return;
  const c=document.getElementById('rfdCard'); if(!c) return;
  _RFD.busy = true;
  const target=_RFD.rot+180*dir;
  _rfdPaint(_rfdFaceAt(target), _RFD.step+dir);   // faceAt(target) is the hidden face
  c.style.transition=''; c.classList.add('anim');
  _rfdRot(target);
  _RFD.rot=target;
  const N=_rfdN(); _RFD.step=((_RFD.step+dir)%N+N)%N;
  _RFD.flips++; _RFD.paintedDir=0;
  _rfdAfterFlip();
  setTimeout(()=>{ c.classList.remove('anim'); _RFD.busy=false; _rfdMeta(); }, 740);
}
function rfdShuffle(){
  rfdStopAuto();
  _RFD.shuffled=!_RFD.shuffled;
  _RFD.order=[...Array(FACT_DISK.length).keys()];
  if(_RFD.shuffled) for(let i=_RFD.order.length-1;i>0;i--){
    const j=Math.floor(Math.random()*(i+1));
    [_RFD.order[i],_RFD.order[j]]=[_RFD.order[j],_RFD.order[i]];
  }
  rfdRestart();
  const m=document.getElementById('rfdMode');
  if(m) m.textContent=_RFD.shuffled?'Shuffled':'Sequential';
}
function rfdReset(){ rfdStopAuto(); _RFD.shuffled=false; _RFD.order=[...Array(FACT_DISK.length).keys()];
  const m=document.getElementById('rfdMode'); if(m) m.textContent='Sequential';
  rfdRestart(); }
function rfdRestart(){
  const c=document.getElementById('rfdCard'); if(!c) return;
  const lk=document.getElementById('rfdLock'); if(lk) lk.remove();
  if(rfdIsPro()){ _RFD.locked=false; _RFD.flips=0; c.style.opacity=''; c.style.pointerEvents=''; }
  _RFD.step=0; _RFD.rot=0; _RFD.busy=false; _RFD.paintedDir=0;
  c.classList.remove('anim'); c.style.transition='';
  _rfdRot(0); _rfdPaint('A',0); _rfdPaint('B',1); _rfdMeta();
}
// Auto-play. Questions get less dwell time than answers, because reading an
// answer takes longer than reading a cue.
const RFD_DWELL_Q = 3200, RFD_DWELL_A = 4200;
// 1 degree per pixel: a ~180px drag turns the disc a full half-turn, so the
// card stays under the finger instead of running ahead of it.
const RFD_DEG_PER_PX = 1.0;

function rfdToggleAuto(){
  if(_RFD.locked) return;
  if(_RFD.timer){ rfdStopAuto(); return; }
  _RFD.auto = true;
  const btn=document.getElementById('rfdAuto');
  if(btn){ btn.textContent='\u23F8 Pause'; btn.classList.add('btn-gold'); btn.classList.remove('btn-outline'); }
  const tick=()=>{
    if(!_RFD.auto) return;
    const showingQuestion = (_RFD.step % 2 === 0);
    _rfdGo(1);
    _RFD.timer = setTimeout(tick, showingQuestion ? RFD_DWELL_A : RFD_DWELL_Q);
  };
  _RFD.timer = setTimeout(tick, RFD_DWELL_Q);
}
function rfdStopAuto(){
  _RFD.auto = false;
  if(_RFD.timer){ clearTimeout(_RFD.timer); _RFD.timer = null; }
  const btn=document.getElementById('rfdAuto');
  if(btn){ btn.textContent='\u25B6 Auto-flip'; btn.classList.remove('btn-gold'); btn.classList.add('btn-outline'); }
}

// Completes a turn the user has already started, animating only the remaining
// angle at drag-like speed so it never outruns the finger.
function _rfdFinish(dir, alreadyTurned){
  if(_RFD.locked || _RFD.busy) return;
  const c=document.getElementById('rfdCard'); if(!c) return;
  _RFD.busy = true;
  const target=_RFD.rot+180*dir;
  const remaining=Math.max(0, 180-Math.min(alreadyTurned,180));
  const ms=Math.max(160, Math.round(remaining*3.2));
  // The incoming face was already painted during the drag, so only finish the turn.
  _rfdPaint(_rfdFaceAt(target), _RFD.step+dir);
  c.style.transition=`transform ${ms}ms cubic-bezier(.25,.65,.35,1)`;
  _rfdRot(target);
  _RFD.rot=target;
  const N=_rfdN(); _RFD.step=((_RFD.step+dir)%N+N)%N;
  _RFD.flips++; _RFD.paintedDir=0;
  _rfdAfterFlip();
  setTimeout(()=>{ c.style.transition=''; _RFD.busy=false; _rfdMeta(); }, ms+20);
}

function rfdInit(){
  const c=document.getElementById('rfdCard'); if(!c) return;
  App.Storage.set('rfdSeen', true);          // retires the NEW badge on Home
  _rfdPaint('A',0); _rfdPaint('B',1); _rfdRot(0); _rfdMeta();

  // Drag only. Tapping used to advance the card, which made it easy to skip a
  // fact by accident while trying to steady the disc — the flip is the gesture.
  const down=x=>{
    if(_RFD.busy || _RFD.locked) return;
    rfdStopAuto(); _RFD.drag=true; _RFD.x0=x; _RFD.dx=0; _RFD.paintedDir=0;
    c.classList.remove('anim'); c.style.transition='';
  };
  const move=x=>{
    if(!_RFD.drag || _RFD.busy) return;
    _RFD.dx=x-_RFD.x0;
    const dir = _RFD.dx < 0 ? 1 : -1;
    // Paint the face the user CANNOT currently see. Painting the visible face
    // was the freeze bug: content changed under the finger mid-drag, and a
    // spring-back then left the wrong fact showing.
    if(dir !== _RFD.paintedDir){
      const hidden = _rfdFaceAt(_RFD.rot) === 'A' ? 'B' : 'A';
      _rfdPaint(hidden, _RFD.step + dir);
      _RFD.paintedDir = dir;
    }
    _rfdRot(_RFD.rot + _RFD.dx * RFD_DEG_PER_PX);
  };
  const end=()=>{
    if(!_RFD.drag) return; _RFD.drag=false;
    const turned = Math.abs(_RFD.dx) * RFD_DEG_PER_PX;   // degrees actually turned
    if(turned >= 90){
      // Past the halfway point: finish the turn, animating only what's left
      // so the speed matches the drag instead of snapping.
      _rfdFinish(_RFD.dx<0?1:-1, turned);
    } else {
      // Spring back and restore whatever should be on the visible face.
      const ms = Math.max(140, Math.round(turned*3.4));
      c.style.transition = `transform ${ms}ms cubic-bezier(.2,.72,.28,1)`;
      _rfdRot(_RFD.rot);
      _RFD.paintedDir = 0;
      setTimeout(()=>{
        c.style.transition='';
        _rfdPaint(_rfdFaceAt(_RFD.rot), _RFD.step);   // repaint the visible face
      }, ms);
    }
  };
  // Touch devices also emit synthetic mouse events; ignore them so a single
  // swipe is not processed twice.
  let touching=false;
  c.onmousedown = e=>{ if(touching) return; e.preventDefault(); down(e.clientX); };
  c.onmousemove = e=>{ if(_RFD.drag) move(e.clientX); };
  c.onmouseup   = end;
  c.onmouseleave= end;
  c.ontouchstart= e=>{ touching=true; down(e.touches[0].clientX); };
  c.ontouchmove = e=>move(e.touches[0].clientX);
  c.ontouchend  = ()=>{ end(); setTimeout(()=>{ touching=false; }, 400); };

  // Keyboard support, so the disc is still usable without a drag gesture.
  c.tabIndex = 0;
  c.onkeydown = e=>{
    if(e.key==='ArrowRight'||e.key===' '||e.key==='Enter'){ e.preventDefault(); rfdStopAuto(); _rfdGo(1); }
    if(e.key==='ArrowLeft'){ e.preventDefault(); rfdStopAuto(); _rfdGo(-1); }
  };
}

function showCrashChecklist() {
  const items=['Confirm exam venue address, time, and booking reference number','Pack your valid South African ID or passport — no ID means no entry','Review the 6 TCF Outcomes — Outcome 4 (suitability) and Outcome 6 (post-sale barriers) are frequently tested','Recall the 3 stages of money laundering: Placement → Layering → Integration',"Recall the FAIS Ombud's cap (R3,500,000), that NO case fee is charged, and the time limits (6 weeks / 6 months)",'Recall the Debarment process: Notice → Representations → Decision (FSP executes)','Recall DOFA-based deadlines: RE5 = 24 months; COB Training = 12 months; Qualification = 6 years','Recall the COI immaterial threshold: R1,000 per year from the same third party','CPD cycle: 1 June to 31 May — evidence submitted within 15 days of cycle end','Get 7–8 hours of sleep — cognitive fatigue directly reduces recall','Eat a proper meal before the exam — do not arrive on an empty stomach','Arrive at least 20 minutes early to settle in and read instructions calmly','Read every question fully before selecting — watch for "NOT", "EXCEPT", and "BEST"','If a question is difficult, continue and return to it — do not waste time on one question','Trust your preparation. You have done the work. Execute with confidence.'];
  showModal('Day-Before Exam Checklist',`<div class="c-muted f-sm mb14">Run through this the evening before your examination.</div>${items.map(item=>`<div class="fac gap10 mb10"><span style="font-size:.95rem;flex-shrink:0">&#9744;</span><span class="f-sm lh-14">${item}</span></div>`).join('')}<div class="card card-navy p12 mt12 tc"><div class="f-sm c-white fw6">You are prepared. Execute with confidence.</div></div><button class="btn btn-navy btn-sm btn-full mt12" onclick="closeModal()">Done</button>`);
}

// ══════════════════════════════════════════════════════
// REVIEW SYSTEM
// ══════════════════════════════════════════════════════
function openReviewSystem() {
  const reviews = App.Storage.get('reviews',[]);
  showModal('Rate RE Mastery Hub',`
  <div class="fw8 f-lg mb4">Share Your Experience</div>
  <div class="c-muted f-sm mb16">Your feedback helps improve the platform for all South African RE candidates.</div>
  <div class="mb12"><div class="inp-label">Your Rating</div>
    <div class="fac gap4 mb8" id="starRow">${[1,2,3,4,5].map(n=>`<button class="star-btn ${n<=4?'on':''}" id="star${n}" onclick="setRating(${n})" aria-label="${n} stars">&#9733;</button>`).join('')}</div>
    <input type="hidden" id="ratingVal" value="4"/>
  </div>
  <div class="mb12"><label class="inp-label" for="reviewText">Feedback or suggestion</label><textarea class="inp" id="reviewText" rows="3" style="resize:vertical" placeholder="Share your experience, suggestions, or pass success story..."></textarea></div>
  <div class="mb12"><label class="inp-label" for="reportQ">Report an incorrect question (optional)</label><input type="text" class="inp" id="reportQ" placeholder="Describe the question and what you believe is incorrect..."/></div>
  <button class="btn btn-gold btn-md btn-full mb8" onclick="submitReview()">Submit Feedback</button>
  ${reviews.length>0?`<div class="divider"></div><div class="fw7 mb8 f-sm">Recent Reviews</div>${reviews.slice(-3).reverse().map(r=>`<div style="padding:8px 0;border-bottom:1px solid var(--border)"><div style="color:var(--gold);font-size:.9rem">${'&#9733;'.repeat(r.rating)}</div><div class="f-sm c-muted mt4 lh-15">${r.text}</div><div class="f-xs c-xs mt2">${r.date}</div></div>`).join('')}`:''}
  <button class="btn btn-ghost btn-sm btn-full mt8" onclick="closeModal()">Close</button>`);
  setTimeout(()=>setRating(4),50);
}

function setRating(n) {
  document.getElementById('ratingVal').value=n;
  [1,2,3,4,5].forEach(i=>{const el=document.getElementById('star'+i);if(el)el.classList.toggle('on',i<=n);});
}
function submitReview() {
  const rating=parseInt(document.getElementById('ratingVal')?.value||'4');
  const text=document.getElementById('reviewText')?.value.trim();
  const report=document.getElementById('reportQ')?.value.trim();
  if(!text||text.length<5){showToast('Please add a comment before submitting.');return;}

  // Local copy — powers the "Recent Reviews" preview shown in-modal
  const reviews=App.Storage.get('reviews',[]);
  if(reviews.length>=App.Config.maxReviews)reviews.splice(0,reviews.length-App.Config.maxReviews+1);
  reviews.push({rating,text,report:report||'',date:new Date().toLocaleDateString(),name:(App.Auth.getLocalUser()||{}).name||'User'});
  App.Storage.set('reviews',reviews);

  // Real, persistent copy — actually reaches the project owner via Supabase.
  // Fire-and-forget: a failed insert here should never block the user's
  // experience of having successfully submitted feedback.
  try {
    const localUser = App.Auth.getLocalUser() || {};
    if (localUser.supabaseId && App.Supabase.isReady()) {
      App.Supabase.getClient().from('reviews').insert({
        user_id: localUser.supabaseId,
        name: localUser.name || 'User',
        rating,
        review_text: text,
        report: report || null,
      }).then(({error}) => { if (error) console.warn('[RMH] review insert failed:', error.message); });
    }
  } catch (e) { console.warn('[RMH] review insert failed:', e.message); }

  App.Analytics.track(App.Analytics.Events.REVIEW_SUBMITTED,{rating});
  closeModal();

  // Happy reviewers get invited to also share on Google — quietly, not a
  // hard requirement, and never shown to lower ratings.
  if (rating >= 4) {
    setTimeout(() => {
      showModal('Thank You!', `
      <div style="text-align:center;padding:4px 0 16px">
        <div style="font-size:2rem;margin-bottom:10px">🌟</div>
        <div style="color:var(--text2);font-size:.9rem;line-height:1.6">Really glad it's working for you. Would you mind sharing the same thing on Google? It genuinely helps other RE candidates find us.</div>
      </div>
      <a href="${App.Config.googleReviewUrl}" target="_blank" rel="noopener"
         style="display:flex;align-items:center;justify-content:center;gap:10px;background:var(--gold);color:var(--navy);font-weight:800;padding:13px 16px;border-radius:10px;text-decoration:none;margin-bottom:10px;font-size:.88rem">
        Leave a Google Review
      </a>
      <button class="btn btn-ghost btn-sm btn-full" onclick="closeModal()">Maybe later</button>`);
    }, 400);
  } else {
    showToast('Thank you for your feedback!');
  }
}

// ══════════════════════════════════════════════════════
// PRO PAGE
// ══════════════════════════════════════════════════════
function rPro() {
  App.Analytics.track(App.Analytics.Events.UPGRADE_VIEWED);
  const plans = [
    { key:'entry',    name:'Entry',             price:99,  sub:'30 days',  guide:false, badge:null,
      features:['Unlimited quizzes','RE5 + RE1 full content','Standard simulations'],
      excludes:['No answer explanations','No Guardian AI coach','No Hard Sim'] },
    { key:'oneMonth', name:'1 Month',        price:189, sub:'30 days',  guide:false, badge:null,
      features:['Unlimited quizzes & explanations','RE5 + RE1 full content','All simulations incl. Hard Sim','RE Guardian AI coach'] },
    { key:'twoMonth', name:'2 Months',       price:269, sub:'60 days',  guide:true,  badge:null,
      features:['Everything in 1 Month','Best for a full exam-cycle runway','No auto-renewal — pay once, done'] },
    { key:'lifetime', name:'Lifetime',       price:349, sub:'forever',  guide:true,  badge:'Best Value',
      features:['Everything in 2 Months','Never expires','Just R80 more than 2 Months — permanently'] },
    { key:'combo',    name:'Combo Lifetime', price:499, sub:'forever',  guide:true,  badge:'Both Exams',
      features:['RE5 + RE1, both exams, forever','Saves ~28% vs buying both separately','For Key Individuals needing both'] },
  ];
  const cta = (p) => {
    const active = p.key==='combo'    ? hasAccess('Combo')
      : p.key==='entry'    ? STATE.isPro && !STATE.hasExplanations
      : p.key==='oneMonth' ? STATE.isPro && STATE.hasExplanations && !STATE.hasGuide
      : p.key==='twoMonth' ? STATE.isPro && STATE.hasGuide && !!STATE.proExpiresAt
      : hasAccess('RE5')||hasAccess('RE1'); // lifetime
    return active ? 'Active' : `Get ${p.name}`;
  };
  return `
  <div class="card card-navy p24 mb14 tc" style="border:none">
    <div style="font-size:.68rem;color:rgba(255,255,255,.4);text-transform:uppercase;letter-spacing:1.2px;margin-bottom:4px">RE Mastery Hub</div>
    <div class="serif" style="font-size:1.8rem;color:var(--gold)">Exam Success System</div>
    <div style="color:rgba(255,255,255,.65);font-size:.83rem;margin-top:8px;line-height:1.6">87% of Pro users improve within 7 days. Built for candidates who intend to pass first time.</div>
    ${STATE.isPro?'<div class="badge badge-gold mt12" style="margin:12px auto 0">PRO ACTIVE</div>':''}
  </div>
  ${!STATE.isPro?`<div class="card card-danger p12 mb12"><div class="fw7 f-sm mb4">Without Pro, you are underprepared</div><div class="f-xs c-muted lh-16">Free access limits you to ${App.Config.freeQPerSession} questions total, no explanations, and a 20-question simulation only. The real RE5 exam is 50 questions; RE1 is 80 questions.</div></div>`:''}
  <div id="tourProPlans" class="stitle">Choose Your Plan</div>
  <div style="display:flex;gap:12px;overflow-x:auto;overflow-y:visible;scroll-snap-type:x mandatory;padding:14px 4px 10px;margin:0 -4px" role="list">
    ${plans.map(p => `
    <div role="listitem" style="flex:0 0 78%;max-width:280px;scroll-snap-align:start">
      <div class="card p16" style="height:100%;border:${p.key==='lifetime'?'2.5px solid var(--gold)':'2px solid var(--navy)'};${p.key==='combo'?'background:linear-gradient(135deg,var(--navy),var(--navy3))':''}">
        ${p.badge?`<div class="badge ${p.key==='lifetime'?'badge-pro':'badge-navy'}" style="margin-bottom:8px">${p.badge}</div>`:''}
        <div class="fb mb4">
          <div class="fw8 ${p.key==='combo'?'c-white':''}">${p.name}</div>
        </div>
        <div class="fb" style="align-items:baseline;margin-bottom:2px">
          <div style="font-size:1.5rem;font-weight:900;color:${p.key==='combo'?'var(--gold)':'var(--navy)'}">R${p.price}</div>
          <div style="font-size:.72rem;color:${p.key==='combo'?'rgba(255,255,255,.5)':'var(--text2)'}">${p.sub}</div>
        </div>
        <div style="margin:10px 0 12px">
          ${p.features.map(f=>`<div class="fac gap6" style="margin-bottom:5px"><span style="color:${p.key==='combo'?'var(--gold)':'#2a9d5c'};font-size:.75rem">&#10003;</span><span style="font-size:.72rem;color:${p.key==='combo'?'rgba(255,255,255,.75)':'var(--text2)'};line-height:1.4">${f}</span></div>`).join('')}
          <div class="fac gap6"><span style="color:${p.guide?(p.key==='combo'?'var(--gold)':'#2a9d5c'):'rgba(255,255,255,.25)'};font-size:.75rem">${p.guide?'&#10003;':'&#10005;'}</span><span style="font-size:.72rem;color:${p.guide?(p.key==='combo'?'rgba(255,255,255,.75)':'var(--text2)'):'rgba(120,120,120,.6)'};line-height:1.4">Downloadable Study Guide</span></div>
          ${(p.excludes||[]).map(x=>`<div class="fac gap6" style="margin-top:5px"><span style="color:#ef4444;font-size:.75rem">&#10005;</span><span style="font-size:.72rem;color:var(--text3);line-height:1.4">${x}</span></div>`).join('')}
        </div>
        <button class="btn ${p.key==='entry'||p.key==='oneMonth'?'btn-navy':'btn-gold'} btn-sm btn-full" onclick="App.Payment.initiatePayment('${p.key}')">${cta(p)}</button>
      </div>
    </div>`).join('')}
  </div>
  <div class="c-xs c-muted tc mb14" style="opacity:.7">&#8592; Swipe to compare &#8594; &nbsp;&middot;&nbsp; Pro accounts support up to 2 devices</div>

  <div class="card p16 mb14">
    <div class="stitle">Feature Comparison</div>
    ${[['Quiz questions',`${App.Config.freeQPerSession} total`,'Unlimited'],['RE5 question bank','Partial','Full'],['RE1 question bank','No','Full'],['Answer explanations','No','All questions'],['RE5 simulation','20 Q',`${App.Config.re5SimTarget} Q`],['RE1 simulation','No',`${App.Config.re1SimTarget} Q`],['Post-sim question review','No','Full'],['Coach feedback','Basic','Advanced'],['Weak area drill','No','Yes'],['Pass probability','No','Yes'],['Downloadable Study Guide','No','2 Months+'],['Study Library','All topics','All topics'],['Crash Pack','Yes','Yes'],['Devices','1','2']].map(([f,fr,pr])=>`<div style="display:flex;align-items:center;gap:6px;margin-bottom:8px;font-size:.75rem"><span style="flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis">${f}</span><span class="badge badge-navy" style="flex-shrink:0;min-width:52px;text-align:center;justify-content:center">${fr}</span><span class="badge badge-gold" style="flex-shrink:0;min-width:52px;text-align:center;justify-content:center">${pr}</span></div>`).join('')}
  </div>
  <div class="card p16 mb14">
    <div class="stitle">What candidates say</div>

    ${[{n:'Thabo M.',l:'Johannesburg',t:'Passed RE5 first attempt. The scenario questions matched exactly what came up.'},{n:'Lerato K.',l:'Cape Town',t:'The coaching feedback after each session told me exactly what to fix.'},{n:'Sipho D.',l:'Durban',t:'The countdown timer and daily targets kept me on track. Passed with 74%.'}].map(u=>`<div style="padding:10px 0;border-bottom:1px solid var(--border)"><div class="f-xs c-gold mb4">&#9733;&#9733;&#9733;&#9733;&#9733;</div><div class="f-sm lh-15 c-muted mb4" style="font-style:italic">"${u.t}"</div><div class="f-xs fw6">${u.n} <span class="c-xs fw4">&middot; ${u.l}</span></div></div>`).join('')}
    <button class="btn btn-outline btn-sm mt12 btn-full" onclick="openReviewSystem()">Add Your Story</button>
  </div>
  <div class="card p16 mb14" style="border:1.5px solid var(--border2)">
    <div class="fw7 mb4">Corporate &amp; Bulk Licensing</div>
    <div class="c-muted f-sm mb10 lh-16">Training a team of representatives or key individuals? Group access available at significant discount.</div>
    <a href="mailto:${App.Config.email}?subject=Bulk Licensing Enquiry" class="btn btn-outline btn-sm">Email for Group Pricing</a>
  </div>
  <div class="card p14 tc" style="border:1px solid var(--border)">
    <div class="c-muted f-xs mb4">Developed and maintained by</div>
    <div class="fw7">The Trending Bee Creatives</div>
    <div class="c-muted f-xs">${App.Config.email}</div>
    <div class="c-xs f-xs mt4">v${App.Config.appVersion} &nbsp;&middot;&nbsp; Copyright &copy; The Trending Bee Creatives. All rights reserved.</div>
  </div>`;
}

// ══════════════════════════════════════════════════════
// PROGRESS PAGE
// ══════════════════════════════════════════════════════
function rProgress() {
  const p=App.Progress.get();
  const re5pct=p.re5.att>0?Math.round(p.re5.cor/p.re5.att*100):0;
  const re1pct=p.re1.att>0?Math.round(p.re1.cor/p.re1.att*100):0;
  const weak5=App.Progress.getWeakAreas('RE5'); const weak1=App.Progress.getWeakAreas('RE1');
  const allWeak=[...weak5.map(w=>({...w,exam:'RE5'})),...weak1.map(w=>({...w,exam:'RE1'}))].sort((a,b)=>a.pct-b.pct);
  const b=App.Behaviour.get();
  return `
  <div class="fw8 f-xl mb4">My Progress</div>
  <div class="c-muted f-sm mb16">Performance data across all sessions</div>
  <div class="g4 mb12">
    <div class="stat-card tc"><div class="stat-val">${p.re5.att+p.re1.att}</div><div class="stat-lbl">Attempted</div></div>
    <div class="stat-card tc"><div class="stat-val" style="color:var(--green)">${re5pct}%</div><div class="stat-lbl">RE5</div></div>
    <div class="stat-card tc"><div class="stat-val" style="color:var(--gold)">${re1pct}%</div><div class="stat-lbl">RE1</div></div>
    <div class="stat-card tc"><div class="stat-val" style="color:var(--amber)">${p.streak?.length||0}</div><div class="stat-lbl">Streak</div></div>
  </div>
  ${(b.quits||0)>=2||(b.lowScores||0)>=3?`<div class="card card-amber p12 mb12"><div class="fw7 f-sm mb4 c-amber">Behaviour Pattern Detected</div><div class="f-xs c-muted lh-16">${(b.quits||0)>=2?`You have exited quizzes early ${b.quits} times recently. `:''}${(b.lowScores||0)>=3?`You have scored below the pass mark ${b.lowScores} consecutive times. `:''}Review the Study Library between sessions to break this pattern.</div></div>`:''}
  <div class="card p16 mb12"><div class="fw7 mb12">RE5 Topic Performance</div>
    ${Object.entries(p.re5.byT).length===0?'<div class="c-muted f-sm">Complete RE5 quizzes to see your topic breakdown.</div>':Object.entries(p.re5.byT).map(([t,d])=>{const tp=Math.round(d.cor/d.att*100);return`<div class="mb10"><div class="fb mb4"><span class="f-sm fw6 truncate" style="max-width:200px">${t}</span><span class="badge ${tp>=70?'badge-green':tp>=50?'badge-amber':'badge-red'}">${tp}%</span></div><div class="pbar pbar-h6"><div class="pbar-fill ${tp>=70?'pf-green':tp>=50?'pf-gold':'pf-red'}" style="width:${tp}%"></div></div><div class="f-xs c-muted mt2">${d.att} attempts</div></div>`;}).join('')}
  </div>
  ${allWeak.length>0?`<div class="card p16 mb12" style="border:1.5px solid var(--red)"><div class="fw7 mb10">Priority Drill Areas</div>${allWeak.slice(0,5).map(w=>`<div class="fb mb8"><div><span class="badge ${w.exam==='RE5'?'badge-navy':'badge-blue'} mb4">${w.exam}</span><div class="f-sm fw6">${w.t}</div><div class="f-xs c-muted">${w.att} attempts</div></div><div class="tc"><span class="badge badge-red mb4">${w.pct}%</span><br><button class="btn btn-xs btn-navy" onclick="startTopicQuiz('${w.exam}','${esc(w.t)}')">Drill</button></div></div>`).join('')}</div>`:''}
  ${(p.mocks||[]).length>0?`<div class="card p16 mb12"><div class="fw7 mb10">Simulation History</div>${p.mocks.slice(-8).reverse().map(m=>`<div class="fb mb6"><div><div class="fw6 f-sm">${m.mode}</div><div class="f-xs c-muted">${m.date}</div></div><div class="fac gap8"><span class="f-xs fw7">${m.score}/${m.total}</span><span class="badge ${m.pct>=65?'badge-green':'badge-red'}">${m.pct}%</span></div></div>`).join('')}</div>`:''}`;
}

// ══════════════════════════════════════════════════════
// SETTINGS PAGE
// ══════════════════════════════════════════════════════

// ── Admin stats ────────────────────────────────────────────────────────────
// Calls the admin_stats RPC, which is guarded server-side by an email
// allowlist. Non-admins get an error and nothing is rendered — the panel
// simply never appears for them.
async function loadAdminStats() {
  const el = document.getElementById('adminStatsCard');
  if (!el) return;
  try {
    if (!App.Supabase.isReady()) return;
    const { data, error } = await App.Supabase.getClient().rpc('admin_stats');
    if (error || !data) return; // not an admin, or unavailable — render nothing
    const s = data;
    const money = n => 'R' + Number(n || 0).toLocaleString('en-ZA', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
    const when  = d => d ? new Date(d).toLocaleDateString('en-ZA', { day: 'numeric', month: 'short', year: 'numeric' }) : '—';
    const row = (k, v) => `<div class="fb mb8 f-sm"><span class="c-muted">${k}</span><span class="fw6">${v}</span></div>`;
    const bd = s.pro_breakdown || {};

    // Isolated on purpose — this is newer, less-proven code than admin_stats
    // above, and a failure here must never be able to take the whole
    // dashboard down with it, the way one shared try/catch would allow.
    let featureHTML = '';
    try {
      const { data: featureData } = await App.Supabase.getClient().rpc('get_feature_usage_stats');
      const FEATURE_LABELS = {
        home: 'Home', quiz_hub: 'Quiz (topic list)', sim_hub: 'Simulations (mode list)',
        library: 'Study Library', pricing_page: 'Pricing Page', settings: 'Settings',
        progress: 'Progress Page', guardian: 'RE Guardian AI', blueprint: 'Exam Success Blueprint',
        quiz_practice: 'Quiz Practice', crash_pack: 'Crash Pack', mixed_practice: 'Mixed Practice',
        simulation: 'Standard Simulation', hard_sim: 'Hard Sim', blueprint_exam: 'Blueprint Exam',
        fact_disk: 'RE Fact Disk',
      };
      const fmtTime = sec => sec >= 3600 ? `${(sec/3600).toFixed(1)}h` : sec >= 60 ? `${Math.round(sec/60)}m` : `${sec}s`;
      if (featureData && featureData.length) {
        const totalSec = featureData.reduce((sum, f) => sum + Number(f.total_seconds), 0) || 1;
        featureHTML = featureData.slice(0, 10).map(f => {
          const pct = Math.round(Number(f.total_seconds) / totalSec * 100);
          const label = FEATURE_LABELS[f.feature_key] || f.feature_key;
          return `<div style="margin-bottom:8px">
            <div class="fb f-xs mb2"><span class="fw6">${label}</span><span class="c-muted">${fmtTime(f.total_seconds)} · ${f.visit_count} visits</span></div>
            <div style="background:rgba(0,0,0,.06);border-radius:4px;height:6px;overflow:hidden"><div style="background:var(--gold);height:100%;width:${pct}%"></div></div>
          </div>`;
        }).join('');
      }
    } catch (fe) {
      console.error('[RMH] Feature usage section failed, rest of dashboard still rendering:', fe);
    }

    el.innerHTML = `
    <div class="card p16 mb12" style="border:1.5px solid rgba(200,168,75,.4);background:rgba(200,168,75,.06)">
      <div class="fw7 mb10" style="color:var(--gold-dark,#8a6d1f)">📊 Admin Dashboard</div>
      <div class="fac gap8 mb10">
        <span style="width:8px;height:8px;border-radius:50%;background:#2a9d5c;flex-shrink:0;box-shadow:0 0 6px #2a9d5c"></span>
        <span class="fw8 f-lg" style="color:#1e6b3f">${s.online_now}</span>
        <span class="f-sm c-muted">online right now</span>
      </div>
      ${row('New Signups Today', s.signups_today)}
      ${row('New Signups This Week', s.signups_this_week)}
      <div style="height:1px;background:rgba(0,0,0,.08);margin:10px 0"></div>
      ${row('Total Users', s.total_users)}
      ${row('Free Users', s.free_users)}
      ${row('Pro Users', `${s.pro_users}${bd.combo||bd.re5||bd.re1 ? ` (${[bd.combo?`combo ${bd.combo}`:null,bd.re5?`re5 ${bd.re5}`:null,bd.re1?`re1 ${bd.re1}`:null].filter(Boolean).join(', ')})` : ''}`)}
      ${row('Conversion Rate', s.conversion_rate + '%')}
      <div style="height:1px;background:rgba(0,0,0,.08);margin:10px 0"></div>
      ${row('Monthly Revenue', money(s.monthly_revenue))}
      ${row('Payments This Month', s.payments_this_month)}
      ${row('Revenue All Time', money(s.revenue_all_time))}
      <div style="height:1px;background:rgba(0,0,0,.08);margin:10px 0"></div>
      ${row('Active Subscriptions', s.active_recurring)}
      ${row('Next Pro Expiry', when(s.next_expiry_date))}
      ${row('Expired This Month', s.expired_this_month)}
      ${featureHTML ? `<div style="height:1px;background:rgba(0,0,0,.08);margin:10px 0"></div>
      <div class="fw7 f-sm mb8">Where Time Is Actually Spent</div>
      ${featureHTML}` : ''}
      <div class="f-xs c-muted mt8">Updated ${new Date(s.generated_at).toLocaleTimeString('en-ZA',{hour:'2-digit',minute:'2-digit'})} · Refreshes every 30s while this page is open</div>
      <button class="btn btn-outline btn-xs btn-full mt10" onclick="openPage('admincomments')">Moderate Video Comments</button>
    </div>`;
    if (!STATE._adminStatsAutoRefresh) {
      STATE._adminStatsAutoRefresh = setInterval(() => {
        if (STATE.page === 'settings') {
          loadAdminStats();
        } else {
          clearInterval(STATE._adminStatsAutoRefresh);
          STATE._adminStatsAutoRefresh = null;
        }
      }, 30000);
    }
  } catch (e) {
    // "Not authorised" from is_admin() is expected for a non-admin hitting
    // this — stays quiet. Anything else is a real bug and needs to be
    // visible, not swallowed — this catch was previously silent for
    // everything, which is exactly what made this failure mode invisible.
    if (!String(e?.message || e).includes('Not authorised')) {
      console.error('[RMH] Admin dashboard failed to load:', e);
    }
  }
}


// ── Exam countdown ─────────────────────────────────────────────────────────
// daysLeft is ALWAYS derived from examDate, never read from storage. Storing
// it caused the badge to drift out of sync with the live countdown timer and
// to keep showing a stale figure after the date was changed.
function computeDaysLeft(examDate) {
  if (!examDate) return null;
  const d = new Date(examDate);
  if (isNaN(d.getTime())) return null;
  // Compare at local midnight so a date "today" reads 0, not a fraction.
  const today = new Date(); today.setHours(0,0,0,0);
  const target = new Date(d.getFullYear(), d.getMonth(), d.getDate());
  return Math.max(0, Math.round((target - today) / 864e5));
}


// Persists a new exam date BOTH locally and to Supabase. Previously this only
// wrote to localStorage, so the next cloud sync restored the old exam_date —
// which is why the countdown appeared to "revert" after being changed.
async function updateExamDate(v) {
  const days = computeDaysLeft(v);
  if (days === null) { showToast('Please enter a valid date.'); return; }
  if (new Date(v) < new Date(new Date().toDateString())) {
    showToast('Please enter a future date.'); return;
  }

  const u = App.Auth.getLocalUser() || {};
  u.examDate = v;
  delete u.daysLeft;               // never persist a derived value
  App.Auth.saveLocalUser(u);

  // Re-render so the badge and timer pick up the new date immediately
  if (typeof openPage === 'function') openPage('settings');
  showToast('Exam date updated.');

  try {
    if (u.supabaseId && App.Supabase.isReady()) {
      const { error } = await App.Supabase.getClient()
        .from('profiles').update({ exam_date: v }).eq('id', u.supabaseId);
      if (error) {
        console.warn('[RMH] exam_date cloud update failed:', error.message);
        showToast('Saved on this device — will sync when back online.');
      }
    }
  } catch (e) {
    console.warn('[RMH] exam_date cloud update failed:', e.message);
  }
}

function rSettings() {
  const u=App.Auth.getLocalUser()||{};
  // Admin panel loads asynchronously; only renders for allow-listed accounts.
  setTimeout(loadAdminStats, 0);
  return `
  <div class="fw8 f-xl mb20">Settings</div>
  <div id="adminStatsCard"></div>
  <div class="card p16 mb12"><div class="fw7 mb10">Account</div>
    ${[['Name',u.name||'—'],['Email',u.email||'—'],['Plan',{combo:'Combo Pro',re5:'RE5 Pro',re1:'RE1 Pro',none:'Free'}[STATE.proTier||'none']],['Exam',u.exam===0?'RE5':u.exam===1?'RE1':'RE5 & RE1'],['Institution',u.institution===0?'Moonstone':u.institution===1?'INSETA':'Compliance Institute']].map(([k,v])=>`<div class="fb mb8 f-sm"><span class="c-muted">${k}</span><span class="fw6">${v}</span></div>`).join('')}
  </div>
  <div class="card p16 mb12"><div class="fw7 mb10">Exam Date</div>
    <input type="date" class="inp" value="${u.examDate||''}" aria-label="Exam date" onchange="updateExamDate(this.value)"/>
  </div>
  <button class="btn btn-gold btn-md btn-full mb8" onclick="openPage('pro')">${STATE.isPro?'Pro Active':'Upgrade to Pro'}</button>
  <button class="btn btn-outline btn-sm btn-full mb8" onclick="openPage('progress')">View My Progress</button>
  <button class="btn btn-outline btn-sm btn-full mb8" onclick="openReviewSystem()">Rate This App</button>
  <button class="btn btn-outline btn-sm btn-full mb8" onclick="if(confirm('Reset all progress? This cannot be undone.')){App.Progress.reset();App.Behaviour.reset();showToast('Progress reset.');}">Reset Progress</button>
  <button class="btn btn-red btn-sm btn-full mb8" onclick="if(confirm('Log out? Your study progress stays saved on this device.')){clearCdTimer();clearSimTimer();App.Auth.signOut();location.reload();}">Log Out</button>

  <div class="card p16 mb12" style="border:none;background:var(--border2)">
    <div class="fw7 f-sm mb10">Contact & Support</div>
    <a href="https://wa.me/${App.Config.whatsapp}" target="_blank" rel="noopener"
       style="display:flex;align-items:center;gap:10px;padding:10px 0;text-decoration:none;border-bottom:1px solid var(--border)">
      <span style="background:#25D366;border-radius:50%;width:32px;height:32px;display:flex;align-items:center;justify-content:center;flex-shrink:0">
        <svg width="16" height="16" viewBox="0 0 24 24" fill="white"><path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z"/></svg>
      </span>
      <div>
        <div class="fw6 f-sm" style="color:var(--text)">WhatsApp Support</div>
        <div class="f-xs c-muted">${App.Config.whatsappDisplay}</div>
      </div>
    </a>
    <a href="mailto:${App.Config.email}" style="display:flex;align-items:center;gap:10px;padding:10px 0;text-decoration:none">
      <span style="background:var(--gold);border-radius:50%;width:32px;height:32px;display:flex;align-items:center;justify-content:center;flex-shrink:0">
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#1a1a2e" stroke-width="2.5"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,12 2,6"/></svg>
      </span>
      <div>
        <div class="fw6 f-sm" style="color:var(--text)">Email Support</div>
        <div class="f-xs c-muted">${App.Config.email}</div>
      </div>
    </a>
  </div>

  <div class="card p12 tc mt4" style="background:var(--border2)">
    <div class="fw7 f-sm mb4">The Trending Bee Creatives</div>
    <div class="c-xs f-xs mt2">v${App.Config.appVersion} &nbsp;&middot;&nbsp; &copy; 2026 The Trending Bee Creatives</div>
    <div style="margin-top:10px;display:flex;justify-content:center;gap:16px">
      <span onclick="openPage('privacy')" style="color:var(--gold);font-size:.75rem;cursor:pointer;text-decoration:underline">Privacy Policy</span>
      <span onclick="openPage('terms')" style="color:var(--gold);font-size:.75rem;cursor:pointer;text-decoration:underline">Terms of Service</span>
    </div>
  </div>`;
}

// ══════════════════════════════════════════════════════
// PRIVACY POLICY PAGE
// ══════════════════════════════════════════════════════
function rPrivacy() {
  return `
  <div class="fb mb16">
    <button class="btn btn-ghost btn-xs" onclick="openPage('settings')">← Back</button>
    <div class="fw8 f-lg">Privacy Policy</div>
  </div>
  <div class="card p16 mb12 f-sm lh-16" style="line-height:1.8">
    <div class="fw7 mb4" style="color:var(--gold)">Effective Date: 14 June 2026</div>
    <div class="c-muted f-xs mb16">The Trending Bee Creatives ("we", "us", "our") operates RE Mastery Hub ("the App").</div>

    <div class="fw7 mb6">1. Information We Collect</div>
    <p class="mb10">When you register, we collect your name, email address, and exam preferences. We collect quiz performance data, progress scores, session history, and exam date. We do not collect your SA ID number, banking details, or biometric data.</p>

    <div class="fw7 mb6">2. How We Use Your Information</div>
    <p class="mb10">Your data is used exclusively to provide personalised coaching, track your exam readiness, deliver the App's features, and process Pro plan activations. We do not sell, rent, or share your personal information with third parties for marketing purposes.</p>

    <div class="fw7 mb6">3. Data Storage</div>
    <p class="mb10">Your account and progress data is stored securely on Supabase (supabase.com), a GDPR-compliant cloud database provider based in the United States. A copy is stored locally on your device (localStorage) for offline access. Supabase employs encryption at rest and in transit.</p>

    <div class="fw7 mb6">4. Payment Processing</div>
    <p class="mb10">Pro plan payments are processed by PayFast (payfast.co.za), a PCI DSS-compliant South African payment gateway. We do not store your card number, CVV, or full banking details. PayFast's Privacy Policy governs payment data.</p>

    <div class="fw7 mb6">5. POPIA Compliance</div>
    <p class="mb10">We process your personal information in accordance with the Protection of Personal Information Act 4 of 2013 (POPIA). You have the right to access, correct, or request deletion of your personal information at any time by contacting us at ${App.Config.email}.</p>

    <div class="fw7 mb6">6. Cookies and Local Storage</div>
    <p class="mb10">The App uses browser localStorage to store your session and progress data. No advertising cookies are used. No third-party tracking is active on this platform.</p>

    <div class="fw7 mb6">7. Data Retention</div>
    <p class="mb10">Your account data is retained for as long as your account is active. You may request deletion of your account and all associated data by emailing us. We will action deletion requests within 14 business days.</p>

    <div class="fw7 mb6">8. Contact</div>
    <p class="mb6">For any privacy-related queries or to exercise your POPIA rights:</p>
    <p class="mb4"><strong>Email:</strong> ${App.Config.email}</p>
    <p class="mb16"><strong>WhatsApp:</strong> ${App.Config.whatsappDisplay}</p>
    <p class="c-muted f-xs">The Trending Bee Creatives &middot; Meadowlands, Soweto, Gauteng, South Africa</p>
  </div>`;
}

// ══════════════════════════════════════════════════════
// TERMS OF SERVICE PAGE
// ══════════════════════════════════════════════════════
function rTerms() {
  return `
  <div class="fb mb16">
    <button class="btn btn-ghost btn-xs" onclick="openPage('settings')">← Back</button>
    <div class="fw8 f-lg">Terms of Service</div>
  </div>
  <div class="card p16 mb12 f-sm" style="line-height:1.8">
    <div class="fw7 mb4" style="color:var(--gold)">Effective Date: 14 June 2026</div>
    <div class="c-muted f-xs mb16">Please read these Terms carefully before using RE Mastery Hub.</div>

    <div class="fw7 mb6">1. Acceptance of Terms</div>
    <p class="mb10">By creating an account and using RE Mastery Hub, you agree to these Terms of Service. If you do not agree, please do not use the App.</p>

    <div class="fw7 mb6">2. Description of Service</div>
    <p class="mb10">RE Mastery Hub is an exam preparation platform for South African financial services professionals preparing for FSCA regulatory examinations (RE5 and RE1). The App provides practice questions, study materials, mock examinations, and coaching — it does not guarantee exam success.</p>

    <div class="fw7 mb6">3. Disclaimer — No Exam Pass Guarantee</div>
    <p class="mb10" style="color:var(--gold)"><strong>RE Mastery Hub is an independent study tool and is not affiliated with, endorsed by, or approved by the FSCA, Moonstone Compliance Services, INSETA, or the Compliance Institute of South Africa. Use of this App does not guarantee passing any regulatory examination. All exam content is based on publicly available regulatory material and best efforts to maintain accuracy.</strong></p>

    <div class="fw7 mb6">4. User Accounts</div>
    <p class="mb10">You are responsible for maintaining the confidentiality of your account credentials. You must not share your account with others. One account per person. We reserve the right to suspend accounts that violate these Terms.</p>

    <div class="fw7 mb6">5. Pro Plans and Payment</div>
    <p class="mb10">Pro plans are available as once-off lifetime purchases or monthly subscriptions. All prices are in South African Rand (ZAR) and inclusive of VAT where applicable. Payment is processed securely via PayFast. Once-off plans are lifetime and non-transferable. Monthly plans may be cancelled at any time — no refund is issued for the current billing period.</p>

    <div class="fw7 mb6">6. Refund Policy</div>
    <p class="mb10">Once-off Pro plans: refunds considered within 7 days of purchase if the App is materially non-functional. Monthly plans: no partial refunds. To request a refund, email ${App.Config.email} with your registered email and reason.</p>

    <div class="fw7 mb6">7. Intellectual Property</div>
    <p class="mb10">All question content, coaching text, study library material, and software comprising RE Mastery Hub is the intellectual property of The Trending Bee Creatives. You may not copy, reproduce, sell, or distribute any App content without written permission.</p>

    <div class="fw7 mb6">8. Limitation of Liability</div>
    <p class="mb10">To the maximum extent permitted by South African law, The Trending Bee Creatives is not liable for any indirect, incidental, or consequential damages arising from your use of the App, including failure to pass any regulatory examination.</p>

    <div class="fw7 mb6">9. Changes to Terms</div>
    <p class="mb10">We may update these Terms at any time. Continued use of the App after changes constitutes acceptance of the revised Terms. Material changes will be communicated via the App or email.</p>

    <div class="fw7 mb6">10. Governing Law</div>
    <p class="mb10">These Terms are governed by the laws of the Republic of South Africa. Any disputes shall be subject to the jurisdiction of the South African courts.</p>

    <div class="fw7 mb6">11. Contact</div>
    <p class="mb4"><strong>Email:</strong> ${App.Config.email}</p>
    <p class="mb16"><strong>WhatsApp:</strong> ${App.Config.whatsappDisplay}</p>
    <p class="c-muted f-xs">The Trending Bee Creatives &middot; Meadowlands, Soweto, Gauteng, South Africa</p>
  </div>`;
}

// ══════════════════════════════════════════════════════
// EXAM SUCCESS BLUEPRINT PAGE
// ══════════════════════════════════════════════════════
const IMPORTANCE_META = {
  heavy:     { emoji: '🔴', label: 'Heavy Hitter', color: '#ef4444' },
  important: { emoji: '🟠', label: 'Important',    color: '#f97316' },
  medium:    { emoji: '🟡', label: 'Medium',       color: '#eab308' },
  light:     { emoji: '🟢', label: 'Light',        color: '#22c55e' },
};

function _blueprintGuardianBubble(text, opts) {
  opts = opts || {};
  const pad = opts.size === 'lg' ? '14px 16px' : '12px 14px';
  const clickable = !!opts.onclick;
  return `
  <div ${clickable ? `onclick="${opts.onclick}" role="button"` : ''} style="background:linear-gradient(135deg,rgba(200,168,75,.12) 0%,rgba(200,168,75,.04) 100%);border:1.5px solid rgba(200,168,75,.4);border-radius:12px;padding:${pad};display:flex;gap:10px;align-items:flex-start${clickable ? ';cursor:pointer' : ''}">
    <span style="font-size:1.1rem;flex-shrink:0;line-height:1.3">🛡️</span>
    <div style="flex:1">
      <div style="font-weight:800;font-size:.72rem;color:#8a6d1f;text-transform:uppercase;letter-spacing:.5px;margin-bottom:4px">Guardian Coach</div>
      <div class="f-sm lh-16" style="color:#1a2942">${text}</div>
      ${clickable ? `<div style="font-size:.68rem;font-weight:700;color:#8a6d1f;margin-top:8px">${STATE.hasCoachFeedback?'Tap to ask Guardian about this task →':'Tap to unlock Guardian for this task — PRO →'}</div>` : ''}
    </div>
  </div>`;
}

function _blueprintProgressBar(pct, color) {
  return `
  <div style="background:rgba(255,255,255,.08);border-radius:20px;height:8px;overflow:hidden;flex:1">
    <div style="background:${color||'var(--gold)'};height:100%;width:${Math.max(0,Math.min(100,pct))}%;border-radius:20px;transition:width .4s ease"></div>
  </div>`;
}

function _blueprintTaskCard(task, examType, i) {
  const meta = IMPORTANCE_META[task.importance];
  const completion = App.BlueprintProgress.taskCompletion(examType, task);

  return `
  <div class="bp-card" style="border:1.5px solid ${meta.color}33;border-left:4px solid ${meta.color};border-radius:12px;margin-bottom:12px;overflow:hidden;background:#fff">
    <div class="bp-hdr" onclick="toggleBlueprintCard('bpc${i}')" role="button" aria-expanded="false" id="bpchdr${i}" style="padding:14px 16px;cursor:pointer;display:flex;justify-content:space-between;align-items:flex-start;gap:10px">
      <div style="flex:1">
        <div style="display:flex;align-items:center;gap:6px;margin-bottom:4px;flex-wrap:wrap">
          <span style="font-size:.7rem;font-weight:800;padding:2px 8px;border-radius:20px;background:${meta.color}1a;color:${meta.color}">${meta.emoji} ${meta.label}</span>
        </div>
        <div class="fw7 f-sm mb2">${esc(task.title)}</div>
        <div class="c-xs" style="color:rgba(0,0,0,.45);line-height:1.4">Roughly ${task.avgQuestions} out of every 50 exam questions come from this task — about ${task.pctOfExam}% of the entire exam.</div>
      </div>
      <span class="c-xs" id="bpc${i}arr" style="flex-shrink:0;margin-top:4px">&#9660;</span>
    </div>
    <div class="bp-body" id="bpc${i}">
      <div style="padding:0 16px 16px">
        <div class="mb10">${_blueprintGuardianBubble(task.guardianInsight, {onclick: `guardianFromBlueprintTask('${examType}','${task.id}')`})}</div>
        <div class="f-sm lh-16 c-muted mb10"><strong style="color:var(--text)">Example scenario:</strong> ${esc(task.exampleScenario)}</div>
        <div class="fac gap8 mb4"><span class="c-xs">Your progress on this task</span></div>
        <div class="fac gap8 mb14">${_blueprintProgressBar(completion, meta.color)}<span class="c-xs fw7">${completion}%</span></div>
        <div class="fac gap8 flex-wrap">
          ${task.lessonTopic ? `<button class="btn btn-navy btn-xs" onclick="openLibraryTopic('${esc(task.lessonTopic)}')">📺 Watch Lesson</button>` : ''}
          ${task.quizTopic ? `<button class="btn btn-gold btn-xs" onclick="${examType==='RE1'&&!hasAccess('RE1')?`openPage('pro')`:`startTopicQuiz('${examType}','${esc(task.quizTopic)}')`}">✍️ Take Quiz</button>` : ''}
          ${task.hardSim ? `<button class="btn btn-outline btn-xs" onclick="${hasAccess(examType)&&STATE.hasHardSim?`startSim('${task.hardSim}',${task.hardSim==='RE5_HARD'?'29,55':'21,45'})`:`openPage('pro')`}">🔥 Hard Sim</button>` : ''}
        </div>
      </div>
    </div>
  </div>`;
}

function _blueprintReadinessSection(examType) {
  const tasks = BLUEPRINT_DATA[examType].tasks;
  if (tasks.length === 0) return '';
  const overall = App.BlueprintProgress.overallReadiness(examType);
  return `
  <div class="card p16 mb16">
    <div class="fw8 f-md mb12">🎯 Your Current Readiness</div>
    ${tasks.map(t => {
      const pct = App.BlueprintProgress.taskCompletion(examType, t);
      return `<div class="mb8"><div class="fac gap8 mb2"><span class="c-xs" style="flex:1">Task ${t.taskNum}</span><span class="c-xs fw7">${pct}%</span></div>${_blueprintProgressBar(pct, IMPORTANCE_META[t.importance].color)}</div>`;
    }).join('')}
    <div style="margin-top:14px;padding-top:14px;border-top:1px solid var(--border)" class="fac gap10">
      <span class="fw8 f-sm" style="flex:1">Overall Exam Readiness</span>
      <span class="fw8 f-xl" style="color:${overall>=70?'#22c55e':overall>=40?'#eab308':'#ef4444'}">${overall}%</span>
    </div>
    <div class="c-xs" style="color:rgba(0,0,0,.35);margin-top:8px">Based on your quiz accuracy per task — practice more to see this update.</div>
  </div>`;
}

function rBlueprint() {
  const user = App.Auth.getLocalUser() || {};
  const examType = App.Storage.get('blueprintExam', user.exam === 1 ? 'RE1' : 'RE5');
  const data = BLUEPRINT_DATA[examType];

  return `
  <div class="mb4">
    <div style="font-size:.68rem;font-weight:800;color:rgba(0,0,0,.4);text-transform:uppercase;letter-spacing:1.6px;margin-bottom:3px">${examType} Exam Success</div>
    <div style="font-size:1.65rem;font-weight:900;letter-spacing:-.3px;line-height:1.1;color:#8a6d1f;background:linear-gradient(135deg,#c8a84b 0%,#e2c97e 55%,#c8a84b 100%);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;display:inline-flex;align-items:baseline;gap:3px">
      <span style="-webkit-text-fill-color:initial;color:#8a6d1f;font-size:.85em">🫆</span>Blueprint<sup style="font-size:.36em;-webkit-text-fill-color:#8a6d1f;color:#8a6d1f;font-weight:800;margin-left:2px;letter-spacing:0">&reg;</sup>
    </div>
  </div>
  <div class="c-muted f-sm mb14">Built from official exam tasks and real student feedback.</div>

  <div class="fac gap8 mb16">
    ${['RE5','RE1'].map(e => `<button class="btn btn-sm ${examType===e?'btn-navy':'btn-outline'}" onclick="App.Storage.set('blueprintExam','${e}');openPage('blueprint')">${e}</button>`).join('')}
  </div>

  <div class="mb16">${_blueprintGuardianBubble("The exam is not equally weighted. Some sections carry far more marks than others.<br><br>Focus your energy where it matters most.", {size:'lg'})}</div>

  ${(() => {
    const examTargetCount = examType === 'RE5' ? 50 : 80;
    const examPool = examType === 'RE5' ? BLUEPRINT_EXAM_RE5 : BLUEPRINT_EXAM_RE1;
    const examReady = examPool.length >= examTargetCount;
    const examMode = examType === 'RE5' ? 'RE5_BLUEPRINT' : 'RE1_BLUEPRINT';
    const examMins = examType === 'RE5' ? 90 : 80;
    const isUnlocked = hasAccess(examType);

    if (!examReady) {
      return `<div class="card p16 mb16" style="border:1.5px dashed rgba(200,168,75,.4);background:linear-gradient(135deg,rgba(200,168,75,.04),transparent)">
        <div class="fac gap8 mb6">
          <span style="font-size:1.3rem">🎯</span>
          <span class="fw8 f-sm" style="color:#8a6d1f">The Blueprint Exam — Coming Soon</span>
        </div>
        <div class="c-muted f-xs lh-16 mb8">We're building the most exam-accurate practice tool available anywhere for ${examType} candidates — every task weighted exactly like the real FSCA exam, not a generic random quiz.</div>
        <div class="fac gap8"><div style="flex:1;background:rgba(0,0,0,.06);border-radius:20px;height:6px;overflow:hidden"><div style="background:var(--gold);height:100%;width:${Math.round(examPool.length/examTargetCount*100)}%;border-radius:20px"></div></div><span class="c-xs fw7" style="color:#8a6d1f">${examPool.length}/${examTargetCount}</span></div>
      </div>`;
    }
    return `<div id="tourBlueprintExam" class="card p18 mb16" style="border:1.5px solid var(--gold);background:linear-gradient(135deg,rgba(200,168,75,.08),transparent);position:relative;overflow:hidden">
      <div style="position:absolute;top:10px;right:-28px;background:var(--gold);color:var(--navy);font-size:.6rem;font-weight:900;padding:3px 32px;transform:rotate(45deg);letter-spacing:.5px">EXAM-ACCURATE</div>
      <div class="fac gap8 mb6"><span style="font-size:1.4rem">🎯</span><span class="fw8 f-md">The Blueprint Exam</span></div>
      <div class="f-sm lh-16 mb4" style="color:rgba(10,22,40,.75)"><strong style="color:var(--text)">The only ${examType} exam simulation built from real FSCA task data</strong> — ${examTargetCount} questions, weighted across every task in the exact proportion the real exam uses. Not a random quiz. A true replica.</div>
      ${!isUnlocked ? `<div class="c-xs lh-15 mb10" style="color:rgba(10,22,40,.5)">Free accounts get topic-level practice. Pro unlocks the full exam-length simulation, timed and scored exactly like the real thing.</div>` : `<div class="c-xs lh-15 mb10" style="color:rgba(10,22,40,.5)">${examMins} minutes &middot; ${examTargetCount} questions &middot; official per-task result breakdown</div>`}
      <button class="btn ${isUnlocked?'btn-gold':'btn-navy'} btn-full" onclick="${isUnlocked?`startSim('${examMode}',${examTargetCount},${examMins})`:`openPage('pro')`}">${isUnlocked?'Take the Full Blueprint Exam →':'🔒 Unlock the Full Blueprint Exam'}</button>
    </div>`;
  })()}

  ${data.tasks.length === 0 ? `
  <div class="card p20 tc">
    <div style="font-size:1.5rem;margin-bottom:8px">📊</div>
    <div class="fw7 f-sm mb6">Gathering ${examType} exam data</div>
    <div class="c-muted f-sm lh-16">We're building this blueprint from real ${examType} result slips to make sure every number is accurate — not guessed. Check back soon, or use the Study Library and Quiz in the meantime.</div>
  </div>
  ` : `
  ${_blueprintReadinessSection(examType)}
  <div class="stitle mb10">Exam Tasks — Ranked by Weight</div>
  ${data.tasks.map((t,i) => _blueprintTaskCard(t, examType, i)).join('')}
  <div class="c-xs" style="color:rgba(0,0,0,.35);text-align:center;margin-top:8px">Weighting derived from ${esc(data.dataSource)}</div>
  `}
  `;
}

function toggleBlueprintCard(id) {
  const b = document.getElementById(id);
  const a = document.getElementById(id+'arr');
  const h = document.getElementById('bpchdr'+id.replace('bpc',''));
  if (!b) return;
  b.classList.toggle('open');
  if (a) a.innerHTML = b.classList.contains('open') ? '&#9650;' : '&#9660;';
  if (h) h.setAttribute('aria-expanded', String(b.classList.contains('open')));
}

// ══════════════════════════════════════════════════════
// RE GUARDIAN™ PAGE
// ══════════════════════════════════════════════════════
function rGuardian() {
  const isPro   = STATE.hasCoachFeedback; // Guardian = "coach feedback" — gated separately from general Pro since v19 (R99 Entry tier has Pro access but not Guardian)
  const modes   = App.Guardian.MODES;
  const gState  = App.Guardian.getState();

  const modeCards = Object.values(modes).map(m => {
    const proOnly  = !isPro && App.Guardian.FREE_LIMITS[m.id] === 0;
    const rem      = App.Guardian.getRemainingUses(m.id);
    const remLabel = isPro ? '' : proOnly
      ? '<span style="font-size:.65rem;background:var(--gold);color:var(--navy);padding:1px 6px;border-radius:10px;font-weight:700;margin-left:6px">PRO</span>'
      : `<span style="font-size:.65rem;color:var(--text3);margin-left:6px">${rem} left today</span>`;

    const descriptions = {
      teachme:   'Ask anything in plain English. I\'ll explain it simply with real examples.',
      scenario:  'I\'ll put you in real FSP workplace situations. Work through them with me.',
      examiner:  'I\'ll question you like the FSCA examiner. No multiple choice — just knowledge.',
      weakcoach: 'I\'ll read your results and build a personalised session on your weak topics.',
    };

    return `
    <div class="guardian-mode-card${proOnly ? ' pro-only' : ''}" onclick="guardianSelectMode('${m.id}')" role="button" aria-label="${m.label}">
      <div style="display:flex;align-items:center;gap:8px;margin-bottom:5px">
        <span style="font-size:1.3rem">${m.emoji}</span>
        <span style="font-weight:700;font-size:.9rem;color:var(--text)">${m.label}</span>
        ${remLabel}
      </div>
      <div style="font-size:.8rem;color:var(--text2);line-height:1.5">${descriptions[m.id]}</div>
    </div>`;
  }).join('');

  return `
  <div class="guardian-wrap">
    <!-- Header -->
    <div style="padding-bottom:10px;border-bottom:1px solid var(--border);margin-bottom:10px">
      <div style="display:flex;align-items:center;gap:10px;margin-bottom:2px">
        <span style="font-size:1.6rem">🛡️</span>
        <div>
          <div style="font-weight:900;font-size:1.1rem;color:var(--text)">RE Guardian™</div>
          <div style="font-size:.72rem;color:var(--gold);letter-spacing:.4px">YOUR PERSONAL RE EXAM MENTOR</div>
        </div>
        <span style="margin-left:auto;background:rgba(200,168,75,.12);border:1px solid rgba(200,168,75,.3);color:var(--gold);font-size:.65rem;font-weight:700;padding:3px 8px;border-radius:10px">AI POWERED</span>
      </div>
    </div>

    <!-- Mode selector — shown when no active session -->
    <div id="guardianModes" style="display:${gState.active ? 'none' : 'block'}">
      <div style="font-size:.8rem;color:var(--text2);margin-bottom:8px;font-weight:600">CHOOSE A MODE TO BEGIN</div>
      <div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:10px">
        ${modeCards}
      </div>
      ${!isPro ? `<div style="text-align:center;font-size:.75rem;color:var(--text3);padding:6px 0">
        Examiner Mode and Weak Area Coach are <span style="color:var(--gold);cursor:pointer" onclick="openPage('pro')">Pro features</span>
      </div>` : ''}
    </div>

    <!-- Chat window — shown when session is active -->
    <div id="guardianChatWrap" style="display:${gState.active ? 'flex' : 'none'};flex:1;flex-direction:column;overflow:hidden">
      <div style="display:flex;align-items:center;gap:8px;margin-bottom:8px">
        <button class="btn btn-ghost btn-xs" onclick="guardianChangeMode()" style="padding:4px 10px;font-size:.75rem">← Modes</button>
        <div id="guardianModeLabel" style="font-size:.8rem;font-weight:700;color:var(--gold)">${gState.active && modes[gState.mode] ? `${modes[gState.mode].emoji}  ${modes[gState.mode].label}` : ''}</div>
      </div>

      <!-- Quick action chips — context-sensitive -->
      <div class="guardian-chips" id="guardianChips"></div>

      <!-- Chat messages -->
      <div class="guardian-chat" id="guardianChat">${gState.active ? gState.history.map((m,i) => {
        if (i === 0 && m.role === 'user') return ''; // synthetic session-start primer — never shown
        const cls = m.role === 'user' ? 'guardian-bubble guardian-user' : 'guardian-bubble guardian-ai';
        const inner = m.role === 'user' ? esc(m.content) : App.Guardian.formatText(m.content);
        return `<div class="${cls}">${inner}</div>`;
      }).join('') : ''}</div>

      <!-- Input row -->
      <div class="guardian-input-row">
        <textarea
          id="guardianInput"
          class="guardian-input"
          placeholder="Type your message..."
          rows="1"
          onkeydown="if(event.key==='Enter'&&!event.shiftKey){event.preventDefault();guardianSend()}"
          oninput="this.style.height='auto';this.style.height=Math.min(this.scrollHeight,120)+'px'"
          aria-label="Message RE Guardian"
        ></textarea>
        <button class="guardian-send" id="guardianSendBtn" onclick="guardianSend()" aria-label="Send">
          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#0d1b2a" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
            <line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/>
          </svg>
        </button>
      </div>
    </div>
  </div>`;
}

// ── Guardian global helpers (called from HTML onclick) ─────────────────────
function guardianSelectMode(mode, context) {
  // Show chat wrap, hide modes
  const modesEl   = document.getElementById('guardianModes');
  const chatWrap  = document.getElementById('guardianChatWrap');
  const modeLabel = document.getElementById('guardianModeLabel');
  const chips     = document.getElementById('guardianChips');
  const chat      = document.getElementById('guardianChat');

  if (!modesEl || !chatWrap) return;

  // Clear previous chat
  if (chat) chat.innerHTML = '';

  modesEl.style.display   = 'none';
  chatWrap.style.display  = 'flex';
  chatWrap.style.flexDirection = 'column';

  const modeInfo = App.Guardian.MODES[mode];
  if (modeLabel && modeInfo) {
    modeLabel.textContent = `${modeInfo.emoji}  ${modeInfo.label}`;
  }

  // Quick chips per mode
  const chipsMap = {
    teachme:   ['What is the 3-element test?','Explain TCF Outcome 4','What is a KI?','How does debarment work?'],
    scenario:  ['Give me a FICA scenario','FAIS Code scenario','TCF scenario','RE1 KI scenario'],
    examiner:  ['Start with RE5 questions','Focus on my weak topics','Mix of easy and hard','RE1 KI questions'],
    weakcoach: ['Let\'s start','Show my weak areas','Focus on FICA','Focus on TCF'],
  };
  if (chips) {
    const chipsArr = chipsMap[mode] || [];
    chips.innerHTML = chipsArr.map(c =>
      `<span class="guardian-chip" onclick="guardianQuickSend('${c.replace(/'/g,"\\'")}')">${c}</span>`
    ).join('');
  }

  // Start session
  App.Guardian.startSession(mode, context);
}

function guardianChangeMode() {
  App.Guardian.reset();
  const modesEl  = document.getElementById('guardianModes');
  const chatWrap = document.getElementById('guardianChatWrap');
  if (modesEl)  modesEl.style.display   = 'block';
  if (chatWrap) chatWrap.style.display  = 'none';
  const chat = document.getElementById('guardianChat');
  if (chat) chat.innerHTML = '';
}

function guardianSend() {
  const inp = document.getElementById('guardianInput');
  const btn = document.getElementById('guardianSendBtn');
  if (!inp) return;
  const text = inp.value.trim();
  if (!text) return;
  inp.value = '';
  inp.style.height = 'auto';
  if (btn) btn.disabled = true;
  App.Guardian.sendMessage(text).finally(() => {
    if (btn) btn.disabled = false;
  });
}

function guardianQuickSend(text) {
  const inp = document.getElementById('guardianInput');
  if (inp) { inp.value = text; guardianSend(); }
}

// ── Guardian context entry points ──────────────────────────────────────────
// Called from post-quiz, post-sim, library cards, and home dashboard.
// Each pre-loads Guardian with context so the user lands in the right mode
// with the right question already primed.

function guardianFromQuiz(pct, exam, worstTopic) {
  App.Guardian.reset();
  openPage('guardian');
  const facts = (typeof pct === 'number')
    ? `The learner just finished a Quiz on ${exam || 'RE5/RE1'} and scored ${pct}%${worstTopic ? `, with the weakest performance on "${worstTopic}"` : ''}. Open by acknowledging this specific result directly, then offer to work through what they got wrong.`
    : `The learner just finished a Quiz session. Ask them how it went and which topics felt hardest, then offer to dig into the ones they're unsure about.`;
  setTimeout(() => {
    guardianSelectMode(STATE.isPro ? 'weakcoach' : 'teachme', facts);
  }, 400);
}

function guardianFromSim(weakTopic, pct, mode) {
  App.Guardian.reset();
  openPage('guardian');
  const isHard = mode === 'RE5_HARD' || mode === 'RE1_HARD';
  const passed = typeof pct === 'number' ? pct >= App.Config.passMark : null;
  let facts;
  if (typeof pct === 'number') {
    facts = `The learner just finished ${isHard ? 'a Hard Sim (deliberately longer/harder than the real exam, for stamina training)' : 'an Exam Simulator attempt'} and scored ${pct}%, which is a ${passed ? 'PASS' : 'FAIL'} against the pass mark.${weakTopic ? ` Their weakest topic was "${weakTopic}".` : ''} Open by acknowledging this specific result directly and by name — congratulate a genuine pass, or be direct (not harsh) about a fail — then offer to work through where they went wrong.`;
  } else if (weakTopic) {
    facts = `The learner just finished a simulation and scored poorly on "${weakTopic}". Open by acknowledging this directly, then help them understand it better.`;
  } else {
    facts = `The learner just finished a simulation. Ask how it went and offer to help understand the topics they missed.`;
  }
  setTimeout(() => {
    guardianSelectMode(STATE.isPro ? 'weakcoach' : 'teachme', facts);
  }, 400);
}

function guardianFromLibrary(topic, exam) {
  if (!STATE.hasCoachFeedback) { openPage('pro'); return; }
  App.Guardian.reset();
  openPage('guardian');
  const facts = `The learner just opened the Library topic "${topic}" for their ${exam} exam. Open by acknowledging that specifically, then teach them the most important things they need to know about this topic and what examiners like to test on it.`;
  setTimeout(() => {
    guardianSelectMode('teachme', facts);
  }, 400);
}

function guardianFromHome() {
  App.Guardian.reset();
  openPage('guardian');
  setTimeout(() => {
    guardianSelectMode('weakcoach');
  }, 400);
}

function guardianFromBlueprintTask(examType, taskId) {
  if (!STATE.hasCoachFeedback) { openPage('pro'); return; }
  const task = BLUEPRINT_DATA[examType]?.tasks.find(t => t.id === taskId);
  if (!task) return;
  App.Guardian.reset();
  openPage('guardian');
  const facts = `The learner tapped the Guardian card inside Task ${task.taskNum} ("${task.title}") of the ${examType} Exam Success Blueprint. This task makes up about ${task.pctOfExam}% of the exam — roughly ${task.avgQuestions} out of every 50 questions. Open by acknowledging they're looking at this specific task, then invite their question about it and guide them on how to use the Library, Quiz, and Sim features to master it.`;
  setTimeout(() => {
    guardianSelectMode(STATE.isPro ? 'weakcoach' : 'teachme', facts);
  }, 400);
}

// ══════════════════════════════════════════════════════
// MODAL (with focus management)
// ══════════════════════════════════════════════════════
function showModal(title, content) {
  document.getElementById('modalContent').innerHTML = `<div class="fw8 f-lg mb14">${title}</div>${content}`;
  document.getElementById('modalBg').classList.add('open');
  setTimeout(()=>document.getElementById('modalSheet')?.focus(), 100);
}
function closeModal() { document.getElementById('modalBg').classList.remove('open'); }
function closeModalCheck(e) { if(e.target===document.getElementById('modalBg'))closeModal(); }
document.getElementById('modalBg').addEventListener('click', closeModalCheck);

// ══════════════════════════════════════════════════════
// TOAST (deduplicated, aria-live)
// ══════════════════════════════════════════════════════
let _toastTimer = null;
// Tier-specific access check. 'combo' grants both. Guardian and general Pro
// messaging intentionally stay on the simpler STATE.isPro (any paid tier) —
// only exam-specific content (quizzes, sims, hard sims) uses this.
function hasAccess(examType) {
  let tier = STATE.proTier || 'none';

  // Recurring subscriptions carry an expiry. The authoritative downgrade runs
  // server-side (hourly expire_lapsed_pro job); this is a client-side guard so
  // access stops at the right moment rather than at the next sync.
  if (STATE.proExpiresAt && new Date(STATE.proExpiresAt).getTime() <= Date.now()) {
    tier = 'none';
  }

  if (tier === 'combo') return true;
  if (examType === 'RE5' && tier === 're5') return true;
  if (examType === 'RE1' && tier === 're1') return true;
  if (examType === 'Combo' && tier === 'combo') return true;

  // Referral bonus — temporary access earned via the Referral Programme,
  // independent of any paid tier. Checked purely by date/tier match; the
  // server (record_referral RPC) is what sets these, never the client.
  if (STATE.bonusAccessUntil && STATE.bonusTier) {
    const stillActive = new Date(STATE.bonusAccessUntil).getTime() > Date.now();
    const bonusExam = STATE.bonusTier === 're1' ? 'RE1' : 'RE5';
    if (stillActive && examType === bonusExam) return true;
  }

  return false;
}

function showToast(msg) {
  document.querySelectorAll('.toast').forEach(t=>t.remove());
  if (_toastTimer) clearTimeout(_toastTimer);
  const t = document.createElement('div');
  t.className = 'toast';
  t.textContent = msg;
  t.setAttribute('role', 'status');
  t.setAttribute('aria-live', 'polite');
  document.body.appendChild(t);
  _toastTimer = setTimeout(()=>t.remove(), 3500);
}

// ══════════════════════════════════════════════════════
// SERVICE WORKER — production registration
// Registers /sw.js at root scope for full PWA support.
// sw.js is a separate file served with correct headers.
// ══════════════════════════════════════════════════════
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/sw.js', { scope: '/' })
      .then(reg => {
        reg.update().catch(() => {});

        // Re-check for a new build when the app is brought back to the
        // foreground. A PWA can sit open for days, so page load alone is not
        // often enough.
        document.addEventListener('visibilitychange', () => {
          if (!document.hidden) reg.update().catch(() => {});
        });
        setInterval(() => reg.update().catch(() => {}), 30 * 60 * 1000);

        // A waiting worker means a new version is ready but the old one still
        // controls this page. Tell it to take over.
        if (reg.waiting) reg.waiting.postMessage({ type: 'SKIP_WAITING' });
        reg.addEventListener('updatefound', () => {
          const nw = reg.installing;
          if (!nw) return;
          nw.addEventListener('statechange', () => {
            if (nw.state === 'installed' && navigator.serviceWorker.controller) {
              nw.postMessage({ type: 'SKIP_WAITING' });
            }
          });
        });
      })
      .catch(err => {
        // SW registration failure is non-fatal — app works without it
        console.warn('[RE Mastery Hub] Service worker registration failed:', err);
      });

    // When the new worker takes control, reload once so the user sees the new
    // build without needing a manual hard refresh. Guarded so it can only ever
    // fire once per page, which prevents a reload loop.
    let _reloaded = false;
    navigator.serviceWorker.addEventListener('controllerchange', () => {
      if (_reloaded) return;
      _reloaded = true;
      window.location.reload();
    });
  });
}

// ══════════════════════════════════════════════════════
// BOOT — hide loading screen only after init completes
// ══════════════════════════════════════════════════════
document.addEventListener('DOMContentLoaded', async () => {
  try {
    App.ErrorBoundary.install();
    App.Storage.migrate();

    // ── Password Recovery intercept ────────────────────────────────────────
    // Supabase recovery links carry Supabase's own markers on top of
    // whatever redirectTo URL we gave it — historically #access_token=...
    // &type=recovery (implicit flow), more recently ?code=...&type=recovery
    // (PKCE flow). Which one shows up depends on Supabase project config/SDK
    // version, and relying on Supabase's markers alone meant detection could
    // silently miss a real reset link. reset=1 is OUR OWN marker, set in
    // resetPassword()'s redirectTo — it is present on every reset return
    // trip regardless of which auth flow Supabase used underneath, so it's
    // checked first as the primary, reliable signal.
    const hash = window.location.hash || '';
    const qs = new URLSearchParams(window.location.search);
    const isRecovery = qs.get('reset') === '1' ||
                       hash.includes('type=recovery') ||
                       qs.get('type') === 'recovery' ||
                       qs.get('code') !== null && hash.includes('type=recovery');

    if (isRecovery) {
      // Hide loading screen, show nothing yet — wait for Supabase to parse the hash
      const ls = document.getElementById('loadingScreen');
      if (ls) ls.style.display = 'none';

      // Wait for Supabase SDK to establish the recovery session from the URL hash
      _waitForRecoverySession();
      return;
    }

    // ── Normal boot ────────────────────────────────────────────────────────
    // initApp is async (it may check for a cloud session before deciding
    // onboarding vs app) — awaited here so the loading screen stays up
    // until that decision is actually made, never flashing onboarding
    // underneath it first.
    await initApp();

  } catch (err) {
    console.error('[RE Mastery Hub] Boot error:', err);
    const errEl = document.getElementById('errorBoundary');
    if (errEl) errEl.style.display = 'flex';
  } finally {
    const ls = document.getElementById('loadingScreen');
    if (ls) ls.style.display = 'none';
  }
});

// ── Wait for Supabase to parse the recovery hash and fire PASSWORD_RECOVERY ──
async function _waitForRecoverySession() {
  const sdkReady = await App.Supabase.waitForSdk(5000);
  if (!sdkReady) {
    // SDK not available — fall back to normal boot
    initApp();
    return;
  }

  const client = App.Supabase.getClient();
  if (!client) { initApp(); return; }

  // Set up the auth state listener BEFORE calling getSession
  // Supabase will fire PASSWORD_RECOVERY when it processes the hash
  let handled = false;

  const { data: { subscription } } = client.auth.onAuthStateChange((event, session) => {
    if (handled) return;
    if (event === 'PASSWORD_RECOVERY') {
      handled = true;
      subscription.unsubscribe();
      // Clear the recovery hash from URL immediately
      if (window.history?.replaceState) {
        window.history.replaceState(null, '', window.location.pathname);
      }
      _showResetPasswordScreen(session);
    } else if (event === 'SIGNED_IN' || event === 'INITIAL_SESSION') {
      // Not a recovery — normal boot
      if (!handled) {
        handled = true;
        subscription.unsubscribe();
        initApp();
      }
    }
  });

  // Fallback: if no event fires within 4 seconds, run normal boot
  setTimeout(() => {
    if (!handled) {
      handled = true;
      subscription.unsubscribe();
      initApp();
    }
  }, 4000);
}

// ── Reset Password Screen ──────────────────────────────────────────────────────
function _showResetPasswordScreen(session) {
  // Hide onboarding, hide app shell, show the reset overlay
  document.getElementById('onboarding').classList.add('hidden');
  document.getElementById('appShell').classList.add('hidden');

  const overlay = document.getElementById('resetOverlay');
  if (overlay) {
    overlay.classList.remove('hidden');
    overlay.style.display = 'flex';
    return;
  }

  // Create the overlay if it doesn't exist
  const div = document.createElement('div');
  div.id = 'resetOverlay';
  div.style.cssText = 'position:fixed;inset:0;background:var(--bg,#0d1b2a);display:flex;align-items:center;justify-content:center;z-index:9999;padding:20px;box-sizing:border-box';
  div.innerHTML = `
    <div style="background:linear-gradient(135deg,#1a1a2e 0%,#16213e 100%);border-radius:20px;padding:32px 28px;width:100%;max-width:420px;box-shadow:0 24px 48px rgba(0,0,0,.5)">
      <div style="text-align:center;margin-bottom:24px">
        <div style="font-size:2.5rem;margin-bottom:12px">🔐</div>
        <div style="color:#fff;font-weight:800;font-size:1.15rem;margin-bottom:6px">Set a new password</div>
        <div style="color:rgba(255,255,255,.5);font-size:.83rem;line-height:1.5">Enter and confirm your new password below.</div>
      </div>

      <label style="color:rgba(255,255,255,.55);font-size:.78rem;font-weight:600;display:block;margin-bottom:6px">New password</label>
      <div style="position:relative;margin-bottom:8px">
        <input type="password" id="resetPw" style="width:100%;padding:12px 44px 12px 14px;border:2px solid rgba(255,255,255,.18);border-radius:10px;background:rgba(255,255,255,.08);color:#fff;font-size:.9rem;box-sizing:border-box" placeholder="New password" oninput="resetPwCheck()"/>
        <button onclick="obTogglePw('resetPw',this)" type="button" style="position:absolute;right:12px;top:50%;transform:translateY(-50%);background:none;border:none;color:rgba(255,255,255,.4);cursor:pointer;font-size:.85rem;padding:4px">👁</button>
      </div>

      <label style="color:rgba(255,255,255,.55);font-size:.78rem;font-weight:600;display:block;margin-bottom:6px">Confirm new password</label>
      <div style="position:relative;margin-bottom:10px">
        <input type="password" id="resetPwConfirm" style="width:100%;padding:12px 44px 12px 14px;border:2px solid rgba(255,255,255,.18);border-radius:10px;background:rgba(255,255,255,.08);color:#fff;font-size:.9rem;box-sizing:border-box" placeholder="Confirm new password" oninput="resetPwCheck()"/>
        <button onclick="obTogglePw('resetPwConfirm',this)" type="button" style="position:absolute;right:12px;top:50%;transform:translateY(-50%);background:none;border:none;color:rgba(255,255,255,.4);cursor:pointer;font-size:.85rem;padding:4px">👁</button>
      </div>

      <div id="resetStrengthBar" style="height:4px;border-radius:2px;background:rgba(255,255,255,.1);margin-bottom:6px;overflow:hidden">
        <div id="resetStrengthFill" style="height:100%;width:0;border-radius:2px;transition:width .3s,background .3s"></div>
      </div>
      <div id="resetStrengthLabel" style="font-size:.7rem;color:rgba(255,255,255,.35);margin-bottom:10px"></div>

      <div style="background:rgba(255,255,255,.05);border-radius:8px;padding:10px 12px;margin-bottom:16px;font-size:.73rem;line-height:1.9">
        <div id="rreq-len"  style="color:rgba(255,255,255,.35)">○ Minimum 8 characters</div>
        <div id="rreq-up"   style="color:rgba(255,255,255,.35)">○ At least 1 uppercase letter</div>
        <div id="rreq-low"  style="color:rgba(255,255,255,.35)">○ At least 1 lowercase letter</div>
        <div id="rreq-num"  style="color:rgba(255,255,255,.35)">○ At least 1 number</div>
        <div id="rreq-spec" style="color:rgba(255,255,255,.35)">○ At least 1 special character</div>
      </div>

      <button id="resetSubmitBtn" class="btn btn-gold btn-lg btn-full" onclick="doResetPassword()" disabled style="opacity:.5;cursor:not-allowed;margin-bottom:8px">Update Password</button>
      <div id="resetMsg" style="text-align:center;font-size:.8rem;color:rgba(255,255,255,.35);min-height:20px"></div>
    </div>`;
  document.body.appendChild(div);
}

// ── Real-time checker for reset password screen ───────────────────────────────
function resetPwCheck() {
  const pw   = document.getElementById('resetPw')?.value || '';
  const conf = document.getElementById('resetPwConfirm')?.value || '';
  const btn  = document.getElementById('resetSubmitBtn');

  const checks = {
    len:  pw.length >= 8,
    up:   /[A-Z]/.test(pw),
    low:  /[a-z]/.test(pw),
    num:  /[0-9]/.test(pw),
    spec: /[^A-Za-z0-9]/.test(pw),
  };

  Object.entries(checks).forEach(([k, pass]) => {
    const el = document.getElementById('rreq-' + k);
    if (el) {
      el.style.color = pass ? 'rgba(52,211,153,.9)' : 'rgba(255,255,255,.35)';
      el.textContent = (pass ? '✓ ' : '○ ') + el.textContent.replace(/^[✓○] /, '');
    }
  });

  const score = Object.values(checks).filter(Boolean).length;
  const fill  = document.getElementById('resetStrengthFill');
  const label = document.getElementById('resetStrengthLabel');
  if (fill && label && pw.length > 0) {
    const pct   = (score / 5) * 100;
    const color = score <= 2 ? '#ef4444' : score <= 3 ? '#f59e0b' : '#10b981';
    fill.style.width = pct + '%'; fill.style.background = color;
    label.textContent = score <= 2 ? 'Weak' : score <= 3 ? 'Medium' : 'Strong';
    label.style.color = color;
  }

  const confInput = document.getElementById('resetPwConfirm');
  if (confInput && conf.length > 0) {
    confInput.style.borderColor = pw === conf ? 'rgba(52,211,153,.7)' : 'rgba(239,68,68,.7)';
  }

  const allPass = Object.values(checks).every(Boolean) && pw === conf && conf.length > 0;
  if (btn) {
    btn.disabled = !allPass;
    btn.style.opacity = allPass ? '1' : '.5';
    btn.style.cursor  = allPass ? 'pointer' : 'not-allowed';
    btn.style.pointerEvents = allPass ? 'auto' : 'none';
  }
}

// ── Submit new password to Supabase ───────────────────────────────────────────
async function doResetPassword() {
  const pw   = document.getElementById('resetPw')?.value;
  const conf = document.getElementById('resetPwConfirm')?.value;
  const btn  = document.getElementById('resetSubmitBtn');
  const msg  = document.getElementById('resetMsg');

  if (!pw || pw !== conf) { if (msg) msg.textContent = 'Passwords do not match.'; return; }

  if (btn) { btn.disabled = true; btn.textContent = 'Updating…'; btn.style.opacity = '1'; }
  if (msg) msg.textContent = '';

  try {
    const client = App.Supabase.getClient();
    if (!client) throw new Error('Supabase not available');

    const { error } = await client.auth.updateUser({ password: pw });
    if (error) throw new Error(error.message);

    // Sign out the recovery session — user must sign in fresh with new password
    await App.Supabase.signOut();

    // Show success state
    const overlay = document.getElementById('resetOverlay');
    if (overlay) {
      overlay.innerHTML = `
        <div style="background:linear-gradient(135deg,#1a1a2e 0%,#16213e 100%);border-radius:20px;padding:32px 28px;width:100%;max-width:420px;text-align:center;box-shadow:0 24px 48px rgba(0,0,0,.5)">
          <div style="font-size:3rem;margin-bottom:16px">✅</div>
          <div style="color:#fff;font-weight:800;font-size:1.1rem;margin-bottom:10px">Password updated!</div>
          <div style="color:rgba(255,255,255,.55);font-size:.85rem;line-height:1.6;margin-bottom:24px">Your password has been successfully updated. Sign in below to continue your preparation.</div>
          <button class="btn btn-gold btn-lg btn-full" onclick="_goToSignIn()">Sign In</button>
        </div>`;
    }
  } catch (e) {
    if (btn) { btn.disabled = false; btn.textContent = 'Update Password'; btn.style.opacity = '1'; btn.style.pointerEvents = 'auto'; }
    if (msg) { msg.textContent = e.message || 'Update failed. Please try again.'; msg.style.color = '#ef4444'; }
    console.error('[RMH] doResetPassword failed:', e.message);
  }
}

// ── After reset — go to sign-in screen ───────────────────────────────────────
function _goToSignIn() {
  const overlay = document.getElementById('resetOverlay');
  if (overlay) overlay.remove();
  // Clear any stale local user so initApp shows onboarding/sign-in
  App.Auth.signOut();
  STATE.obStep = 4; // index of auth step in OB_STEPS
  STATE.disAccepted = true;
  STATE.obData._authMode = 'signin';
  document.getElementById('onboarding').classList.remove('hidden');
  document.getElementById('appShell').classList.add('hidden');
  renderObStep();
}
</script>
</body>
</html>