{"id":1301,"date":"2026-07-29T07:03:59","date_gmt":"2026-07-29T05:03:59","guid":{"rendered":"https:\/\/pcwallis.malo.wf\/?page_id=1301"},"modified":"2026-07-30T00:05:23","modified_gmt":"2026-07-29T22:05:23","slug":"jeu-sur-le-journalisme-dinvestigation-the-black-ledger","status":"publish","type":"page","link":"https:\/\/pcwallis.malo.wf\/index.php\/jeu-sur-le-journalisme-dinvestigation-the-black-ledger\/","title":{"rendered":"Jeu sur le journalisme d&rsquo;investigation : The Black Ledger"},"content":{"rendered":"\n<style>\n.ijgame-wrap-bl2026{width:100% !important;max-width:1100px !important;height:1050px !important;min-height:1050px !important;margin:0 auto !important;overflow:hidden !important;border-radius:16px !important;background:#0a1826 !important;box-shadow:0 8px 24px rgba(0,0,0,0.15) !important;}\n.ijgame-wrap-bl2026 iframe{display:block !important;width:100% !important;max-width:none !important;height:1050px !important;min-height:1050px !important;margin:0 !important;padding:0 !important;border:0 !important;border-radius:16px !important;background:#0a1826 !important;}\n@media(max-width:700px){.ijgame-wrap-bl2026,.ijgame-wrap-bl2026 iframe{height:1150px !important;min-height:1150px !important;}}\n<\/style><div class=\"ijgame-wrap-bl2026\" style=\"width:100% !important;max-width:1100px !important;height:1050px !important;min-height:1050px !important;margin:0 auto !important;overflow:hidden !important;border-radius:16px !important;background:#0a1826 !important;box-shadow:0 8px 24px rgba(0,0,0,0.15) !important;\"><iframe title=\"The Black Ledger\" width=\"1100\" height=\"1050\" scrolling=\"yes\" allow=\"fullscreen\" allowfullscreen style=\"display:block !important;width:100% !important;max-width:none !important;height:1050px !important;min-height:1050px !important;margin:0 !important;padding:0 !important;border:0 !important;border-radius:16px !important;background:#0a1826 !important;\" srcdoc=\"<!DOCTYPE html>\n<html lang=&quot;en&quot;>\n<head>\n<meta charset=&quot;UTF-8&quot;>\n<meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;>\n<title>The Black Ledger \u2014 Investigative Journalism Escape Game<\/title>\n<style>\n  :root{\n    --bg:#0a1826; --bg2:#0f2740; --panel:#122a40; --panel-2:#173350;\n    --teal:#2bb3a3; --teal-dark:#1c8579; --orange:#ef8354; --orange-dark:#d1652c;\n    --paper:#f3e9d6; --ink:#241f1a; --text:#eef3f5; --muted:#9fb4c2;\n    --good:#3fbf7f; --amber:#e8b23a; --bad:#e0574f;\n    --gold:#f0c04a; --silver:#c7d0d8; --bronze:#cd8a4a;\n  }\n  *{box-sizing:border-box;}\n  html,body{margin:0;padding:0;width:100%;min-height:100%;}\n  html{background:var(--bg);}\n  body{overflow-y:auto;}\n  body{\n    background:\n      radial-gradient(rgba(255,255,255,0.035) 1px, transparent 1.4px) 0 0\/26px 26px,\n      radial-gradient(circle at 20% -10%, var(--bg2), var(--bg) 60%);\n    color:var(--text); font-family:'Segoe UI', Helvetica, Arial, sans-serif;\n    min-height:100vh; display:flex; flex-direction:column;\n  }\n  header.topbar{\n    display:flex; flex-direction:column; gap:8px; padding:14px 20px;\n    border-bottom:1px solid rgba(255,255,255,0.08); background:rgba(0,0,0,0.25);\n    position:sticky; top:0; z-index:10;\n  }\n  .top-row{display:flex; justify-content:space-between; align-items:center; flex-wrap:wrap; gap:10px;}\n  .brand{display:flex; align-items:center; gap:8px; font-weight:800; letter-spacing:1.4px; font-size:0.92rem; color:var(--orange); text-transform:uppercase;}\n  .brand svg{width:22px;height:22px;}\n  .brand .fl{fill:var(--orange);}\n  .hud{font-size:0.85rem; color:var(--muted); display:flex; gap:16px; align-items:center;}\n  #scoreLabel{background:var(--teal-dark); color:#fff; padding:4px 10px; border-radius:20px; font-weight:700;}\n  .dots{display:flex; gap:5px; flex-wrap:wrap;}\n  .dot{width:8px;height:8px;border-radius:50%; background:rgba(255,255,255,0.15); transition:background 0.3s;}\n  .dot.done{background:var(--teal);}\n  .dot.current{background:var(--orange); box-shadow:0 0 8px var(--orange);}\n  main#stage{flex:1; display:flex; align-items:flex-start; justify-content:center; padding:34px 16px 60px;}\n  .card{\n    background:var(--panel); border:1px solid rgba(255,255,255,0.08); border-radius:16px;\n    padding:28px 30px 32px; max-width:800px; width:100%;\n    box-shadow:0 16px 46px rgba(0,0,0,0.4); animation:fadeIn 0.4s ease; overflow:hidden;\n  }\n  @keyframes fadeIn{from{opacity:0; transform:translateY(10px);} to{opacity:1; transform:translateY(0);}}\n  .scene-banner{position:relative; height:150px; margin:-28px -30px 22px; display:flex; align-items:center; justify-content:center; overflow:hidden;}\n  .scene-banner.tone-teal{background:linear-gradient(135deg,#0a1826 0%, #144d47 60%, #1c8579 100%);}\n  .scene-banner.tone-orange{background:linear-gradient(135deg,#0a1826 0%, #5a3220 60%, #d1652c 100%);}\n  .scene-banner.tone-mix{background:linear-gradient(120deg,#0a1826 0%, #144d47 45%, #d1652c 100%);}\n  .scene-banner.tone-red{background:linear-gradient(135deg,#1a0a0a 0%, #5a1f1f 60%, #b3382b 100%);}\n  .scene-banner::after{content:&quot;&quot;; position:absolute; inset:0; background:radial-gradient(circle at 50% 40%, rgba(255,255,255,0.16), transparent 60%);}\n  .spark{position:absolute; width:4px; height:4px; border-radius:50%; background:rgba(255,255,255,0.55); animation:floaty 6s ease-in-out infinite;}\n  @keyframes floaty{0%,100%{transform:translateY(0); opacity:0.2;} 50%{transform:translateY(-14px); opacity:0.8;}}\n  .mission-icon{position:relative; z-index:1; filter:drop-shadow(0 4px 10px rgba(0,0,0,0.35));}\n  .mission-icon svg{width:76px; height:76px;}\n  .mission-icon .ln{fill:none; stroke:var(--paper); stroke-width:3.4; stroke-linecap:round; stroke-linejoin:round;}\n  .mission-icon .fl{fill:var(--paper);}\n  .scene-caption{position:absolute; left:16px; bottom:10px; z-index:1; background:rgba(10,24,38,0.55); padding:4px 10px; border-radius:6px; font-size:0.7rem; letter-spacing:1px; text-transform:uppercase; font-weight:800; color:#fff;}\n  .tag{display:inline-block; background:var(--orange); color:#241407; font-weight:800; font-size:0.72rem; letter-spacing:1.2px; padding:4px 10px; border-radius:5px; margin-bottom:12px; text-transform:uppercase;}\n  h1{font-size:2rem; margin:0 0 6px; color:#fff;}\n  h2{font-size:1.5rem; margin:0 0 14px; color:#fff;}\n  .narrative{font-size:1.02rem; line-height:1.6; color:var(--text); margin-bottom:20px;}\n  .narrative em{color:var(--orange); font-style:italic;}\n  .disclaimer{font-size:0.8rem; color:var(--muted); border-left:3px solid var(--teal); padding:8px 12px; background:rgba(43,179,163,0.08); border-radius:4px; margin-bottom:22px;}\n  .intro-actions,.result-card{text-align:center;}\n  .case-number{font-family:'Courier New', monospace; color:var(--muted); font-size:0.85rem; letter-spacing:2px; margin-bottom:4px;}\n  .subtitle{color:var(--muted); margin-bottom:14px; font-size:1.05rem;}\n\n  .docs-grid{display:grid; grid-template-columns:repeat(auto-fit, minmax(200px,1fr)); gap:14px; margin-bottom:20px;}\n  .doc-card{\n    background:repeating-linear-gradient(0deg, var(--paper), var(--paper) 27px, #ece0c9 28px);\n    color:var(--ink); border-radius:4px; padding:12px 12px 14px;\n    box-shadow:0 6px 16px rgba(0,0,0,0.3); position:relative; font-size:0.85rem; line-height:1.35;\n    cursor:pointer; transform:rotate(-1deg); transition:transform 0.15s, box-shadow 0.15s;\n  }\n  .doc-card:nth-child(2n){transform:rotate(0.8deg);}\n  .doc-card:nth-child(3n){transform:rotate(-0.5deg);}\n  .doc-card:hover{transform:scale(1.03) rotate(0deg); box-shadow:0 10px 22px rgba(0,0,0,0.4);}\n  .doc-card.selected{outline:3px solid var(--teal);}\n  .doc-card.wrong-pick{outline:3px solid var(--bad);}\n  .doc-card::before{content:&quot;&quot;; position:absolute; top:-7px; left:14px; width:26px; height:13px; background:silver; border-radius:2px; box-shadow:0 1px 2px rgba(0,0,0,0.4);}\n  .doc-thumb{width:100%; height:54px; border-radius:4px; margin-bottom:6px; display:flex; align-items:center; justify-content:center; background:linear-gradient(135deg,#dcd0b4,#c8bb9b);}\n  .doc-thumb svg{width:28px; height:28px; opacity:0.55;}\n  .doc-tag{display:inline-block; background:var(--bg2); color:#fff; font-size:0.65rem; font-weight:800; letter-spacing:1px; padding:3px 7px; border-radius:4px; margin-bottom:6px; text-transform:uppercase;}\n  .doc-meta{margin-top:6px; font-size:0.72rem; color:#5a5347; font-style:italic;}\n\n  .options{display:flex; flex-direction:column; gap:10px; margin-bottom:14px;}\n  button.opt{\n    display:flex; align-items:center; gap:12px; text-align:left;\n    background:var(--panel-2); color:var(--text); border:1px solid rgba(255,255,255,0.12); border-radius:10px;\n    padding:12px 16px; font-size:0.95rem; cursor:pointer; transition:background 0.2s, transform 0.1s;\n  }\n  button.opt .badge{flex:0 0 auto; width:26px; height:26px; border-radius:50%; background:rgba(255,255,255,0.1); display:flex; align-items:center; justify-content:center; font-weight:800; font-size:0.8rem; border:1px solid rgba(255,255,255,0.2);}\n  button.opt:hover:not(:disabled){background:var(--teal-dark); transform:translateX(3px);}\n  button.opt:disabled{cursor:default;}\n  button.opt.correct{background:var(--good); color:#08210f; border-color:var(--good); font-weight:700;}\n  button.opt.correct .badge{background:#08210f; color:var(--good);}\n  button.opt.wrong{background:rgba(224,87,79,0.22); color:var(--bad); border-color:var(--bad); opacity:0.85;}\n  button.opt.shake{animation:shake 0.35s;}\n  @keyframes shake{10%,90%{transform:translateX(-2px);} 20%,80%{transform:translateX(3px);} 30%,50%,70%{transform:translateX(-5px);} 40%,60%{transform:translateX(5px);}}\n  button.opt.flavor-picked{background:var(--teal-dark); border-color:var(--teal);}\n\n  .checklist{display:flex; flex-direction:column; gap:9px; margin-bottom:16px;}\n  .check-item{display:flex; align-items:center; gap:12px; background:var(--panel-2); border-radius:10px; padding:11px 14px; cursor:pointer; border:1px solid transparent;}\n  .check-item input{width:18px; height:18px; accent-color:var(--teal); flex:0 0 auto;}\n  .check-item .check-icon{flex:0 0 auto; width:26px; height:26px; display:flex; align-items:center; justify-content:center;}\n  .check-item .check-icon svg{width:24px; height:24px;}\n  .check-item .check-icon .ln{fill:none; stroke:var(--teal); stroke-width:3.4; stroke-linecap:round; stroke-linejoin:round;}\n  .check-item .check-icon .fl{fill:var(--teal);}\n  .check-item.correct-mark{border-color:var(--good);}\n  .check-item.wrong-mark{border-color:var(--bad);}\n\n  .feedback{min-height:22px; font-size:0.92rem; margin:6px 0 18px; font-weight:600;}\n  .feedback.good{color:var(--good);}\n  .feedback.bad{color:var(--bad);}\n  .continue-btn{background:var(--orange); color:#241407; border:none; padding:11px 22px; border-radius:24px; font-weight:800; font-size:0.9rem; letter-spacing:0.4px; cursor:pointer; text-transform:uppercase;}\n  .continue-btn:hover{background:var(--orange-dark);}\n  .continue-btn:disabled{opacity:0.4; cursor:not-allowed;}\n  .secondary-btn{background:transparent; color:var(--text); border:1px solid rgba(255,255,255,0.25); padding:10px 18px; border-radius:22px; font-weight:700; font-size:0.85rem; cursor:pointer;}\n\n  .match-rows{display:flex; flex-direction:column; gap:12px; margin-bottom:18px;}\n  .match-row{display:flex; align-items:center; gap:14px; background:var(--panel-2); border-radius:10px; padding:11px 14px; flex-wrap:wrap; border:1px solid transparent;}\n  .match-desc{flex:1 1 260px; font-size:0.9rem;}\n  select.match-select{padding:8px 10px; border-radius:6px; border:1px solid rgba(255,255,255,0.2); background:#0e2033; color:var(--text); font-size:0.86rem;}\n  .match-feedback{font-size:1.1rem; min-width:20px; text-align:center;}\n  .match-row.correct{border-color:var(--good);}\n  .match-row.wrong{border-color:var(--bad);}\n\n  .decode-box{background:var(--panel-2); border-radius:10px; padding:18px; margin-bottom:18px; text-align:center;}\n  .cipher-text{font-family:'Courier New', monospace; font-size:1.5rem; letter-spacing:4px; color:var(--orange); margin-bottom:14px;}\n  .decode-input{padding:10px 14px; border-radius:8px; border:1px solid rgba(255,255,255,0.25); background:#0e2033; color:var(--text); font-size:1rem; font-family:'Courier New', monospace; letter-spacing:2px; text-transform:uppercase; width:220px; text-align:center;}\n  .hint-btn{background:none; border:none; color:var(--teal); font-size:0.8rem; text-decoration:underline; cursor:pointer; margin-top:8px; display:block; margin-left:auto; margin-right:auto;}\n\n  .start-btn{background:var(--teal); color:#052824; border:none; padding:13px 30px; border-radius:26px; font-weight:800; font-size:1rem; letter-spacing:0.5px; cursor:pointer; text-transform:uppercase; box-shadow:0 6px 20px rgba(43,179,163,0.35);}\n  .start-btn:hover{background:var(--teal-dark); color:#fff;}\n\n  .result-card{position:relative;}\n  .ring-wrap{position:relative; width:170px; height:170px; margin:6px auto 8px;}\n  .ring-wrap svg{width:170px; height:170px; transform:rotate(-90deg);}\n  .ring-track{fill:none; stroke:rgba(255,255,255,0.1); stroke-width:12;}\n  .ring-fill{fill:none; stroke-width:12; stroke-linecap:round; transition:stroke-dashoffset 1s ease;}\n  .ring-center{position:absolute; inset:0; display:flex; flex-direction:column; align-items:center; justify-content:center;}\n  .ring-center .score-num{font-size:1.9rem; font-weight:800; color:#fff;}\n  .ring-center .score-den{font-size:0.78rem; color:var(--muted);}\n  .medal-icon{width:56px; height:56px; margin:6px auto 0;}\n  .medal-title{font-size:1.3rem; font-weight:800; margin:10px 0 8px; text-align:center;}\n  .medal-msg{font-size:0.95rem; color:var(--text); margin:0 auto 22px; line-height:1.5; max-width:580px; text-align:center;}\n  .review-list{text-align:left; display:flex; flex-direction:column; gap:9px; margin-bottom:22px;}\n  .review-item{display:flex; gap:12px; background:var(--panel-2); border-radius:10px; padding:11px 14px; align-items:flex-start;}\n  .review-icon{flex:0 0 auto; width:26px; height:26px; border-radius:50%; display:flex; align-items:center; justify-content:center; font-weight:800; font-size:0.85rem;}\n  .review-icon.perfect{background:rgba(63,191,127,0.18); color:var(--good);}\n  .review-icon.retry{background:rgba(232,178,58,0.18); color:var(--amber);}\n  .review-icon.low{background:rgba(224,87,79,0.18); color:var(--bad);}\n  .review-body{flex:1;}\n  .review-title{font-weight:700; font-size:0.9rem; margin-bottom:2px; display:flex; justify-content:space-between; gap:10px;}\n  .review-pts{font-size:0.78rem; font-weight:800;}\n  .review-pts.pos{color:var(--good);}\n  .review-pts.zero{color:var(--muted);}\n  .review-pts.neg{color:var(--bad);}\n  .review-note{font-size:0.83rem; color:var(--muted); line-height:1.4;}\n  .reflection{font-size:0.85rem; color:var(--muted); font-style:italic; margin:0 0 20px; text-align:center;}\n  .confetti-piece{position:absolute; top:-10px; width:8px; height:14px; opacity:0.9; animation:fall 3.2s linear infinite;}\n  @keyframes fall{0%{transform:translateY(-20px) rotate(0deg); opacity:1;} 100%{transform:translateY(340px) rotate(360deg); opacity:0;}}\n\n  .ending-tag{display:inline-block; padding:4px 12px; border-radius:16px; font-weight:800; font-size:0.72rem; letter-spacing:1px; text-transform:uppercase; margin-bottom:10px;}\n  .ending-tag.A{background:var(--good); color:#08210f;}\n  .ending-tag.B{background:var(--amber); color:#241407;}\n  .ending-tag.C{background:var(--bad); color:#fff;}\n\n  .fullscreen-btn{background:transparent;color:var(--text);border:1px solid rgba(255,255,255,0.3);padding:6px 11px;border-radius:18px;font-size:0.76rem;font-weight:800;cursor:pointer;white-space:nowrap;}\n  .fullscreen-btn:hover{background:var(--teal-dark);border-color:var(--teal);}\n  :fullscreen,:-webkit-full-screen{width:100% !important;height:100% !important;background:var(--bg) !important;}\n  html:fullscreen body,html:-webkit-full-screen body{width:100%;height:100%;margin:0;overflow:auto;}\n  footer{text-align:center; font-size:0.72rem; color:var(--muted); padding:14px;}\n  @media (max-width:480px){ .card{padding:22px 18px 26px;} .scene-banner{margin:-22px -18px 18px;} }\n<\/style>\n<\/head>\n<body>\n<header class=&quot;topbar&quot;>\n  <div class=&quot;top-row&quot;>\n    <div class=&quot;brand&quot;>\n      <svg viewBox=&quot;0 0 64 64&quot;><path class=&quot;fl&quot; d=&quot;M32 4 L38 20 L55 20 L41 30 L47 47 L32 37 L17 47 L23 30 L9 20 L26 20 Z&quot;\/><\/svg>\n      The Black Ledger\n    <\/div>\n    <div class=&quot;hud&quot;>\n      <span id=&quot;stepLabel&quot;><\/span>\n      <span id=&quot;scoreLabel&quot;>Score: 0<\/span>\n      <button type=&quot;button&quot; class=&quot;fullscreen-btn&quot; id=&quot;fullscreenBtn&quot;>\u26f6 Full screen<\/button>\n    <\/div>\n  <\/div>\n  <div class=&quot;dots&quot; id=&quot;dots&quot;><\/div>\n<\/header>\n<main id=&quot;stage&quot;><\/main>\n<footer>S\u00e9quence journalisme d&rsquo;investigation \u2014 1\u00e8re AMC<\/footer>\n\n<script>\nconst icons = {\n  badge:'<svg viewBox=&quot;0 0 64 64&quot;><path class=&quot;fl&quot; d=&quot;M32 4 L38 20 L55 20 L41 30 L47 47 L32 37 L17 47 L23 30 L9 20 L26 20 Z&quot;\/><\/svg>',\n  envelope:'<svg viewBox=&quot;0 0 64 64&quot;><rect class=&quot;ln&quot; x=&quot;8&quot; y=&quot;16&quot; width=&quot;48&quot; height=&quot;34&quot; rx=&quot;3&quot;\/><path class=&quot;ln&quot; d=&quot;M10 18 L32 38 L54 18&quot;\/><\/svg>',\n  evidence:'<svg viewBox=&quot;0 0 64 64&quot;><rect class=&quot;ln&quot; x=&quot;9&quot; y=&quot;9&quot; width=&quot;27&quot; height=&quot;35&quot; rx=&quot;2&quot;\/><line class=&quot;ln&quot; x1=&quot;15&quot; y1=&quot;19&quot; x2=&quot;30&quot; y2=&quot;19&quot;\/><line class=&quot;ln&quot; x1=&quot;15&quot; y1=&quot;27&quot; x2=&quot;30&quot; y2=&quot;27&quot;\/><line class=&quot;ln&quot; x1=&quot;15&quot; y1=&quot;35&quot; x2=&quot;24&quot; y2=&quot;35&quot;\/><circle class=&quot;ln&quot; cx=&quot;45&quot; cy=&quot;42&quot; r=&quot;10&quot;\/><line class=&quot;ln&quot; x1=&quot;52&quot; y1=&quot;49&quot; x2=&quot;59&quot; y2=&quot;56&quot;\/><\/svg>',\n  shield:'<svg viewBox=&quot;0 0 64 64&quot;><path class=&quot;ln&quot; d=&quot;M32 6 L54 14 V30 C54 46 44 56 32 60 C20 56 10 46 10 30 V14 Z&quot;\/><rect class=&quot;ln&quot; x=&quot;24&quot; y=&quot;30&quot; width=&quot;16&quot; height=&quot;14&quot; rx=&quot;2&quot;\/><path class=&quot;ln&quot; d=&quot;M27 30 V24 a5 5 0 0 1 10 0 v6&quot;\/><\/svg>',\n  press:'<svg viewBox=&quot;0 0 64 64&quot;><rect class=&quot;ln&quot; x=&quot;8&quot; y=&quot;10&quot; width=&quot;48&quot; height=&quot;44&quot; rx=&quot;2&quot;\/><line class=&quot;ln&quot; x1=&quot;14&quot; y1=&quot;20&quot; x2=&quot;50&quot; y2=&quot;20&quot;\/><line class=&quot;ln&quot; x1=&quot;14&quot; y1=&quot;27&quot; x2=&quot;50&quot; y2=&quot;27&quot;\/><line class=&quot;ln&quot; x1=&quot;14&quot; y1=&quot;34&quot; x2=&quot;36&quot; y2=&quot;34&quot;\/><line class=&quot;ln&quot; x1=&quot;14&quot; y1=&quot;41&quot; x2=&quot;36&quot; y2=&quot;41&quot;\/><rect class=&quot;ln&quot; x=&quot;39&quot; y=&quot;34&quot; width=&quot;11&quot; height=&quot;12&quot;\/><\/svg>',\n  photo:'<svg viewBox=&quot;0 0 64 64&quot;><rect class=&quot;ln&quot; x=&quot;8&quot; y=&quot;12&quot; width=&quot;48&quot; height=&quot;38&quot; rx=&quot;2&quot;\/><circle class=&quot;ln&quot; cx=&quot;20&quot; cy=&quot;26&quot; r=&quot;5&quot;\/><path class=&quot;ln&quot; d=&quot;M10 44 L24 30 L34 40 L42 32 L54 44&quot;\/><\/svg>',\n  mic:'<svg viewBox=&quot;0 0 64 64&quot;><rect class=&quot;ln&quot; x=&quot;26&quot; y=&quot;8&quot; width=&quot;12&quot; height=&quot;26&quot; rx=&quot;6&quot;\/><path class=&quot;ln&quot; d=&quot;M18 28 a14 14 0 0 0 28 0&quot;\/><line class=&quot;ln&quot; x1=&quot;32&quot; y1=&quot;42&quot; x2=&quot;32&quot; y2=&quot;52&quot;\/><line class=&quot;ln&quot; x1=&quot;22&quot; y1=&quot;52&quot; x2=&quot;42&quot; y2=&quot;52&quot;\/><\/svg>',\n  badge2:'<svg viewBox=&quot;0 0 64 64&quot;><rect class=&quot;ln&quot; x=&quot;14&quot; y=&quot;18&quot; width=&quot;36&quot; height=&quot;24&quot; rx=&quot;3&quot;\/><circle class=&quot;ln&quot; cx=&quot;32&quot; cy=&quot;30&quot; r=&quot;6&quot;\/><\/svg>',\n  cctv:'<svg viewBox=&quot;0 0 64 64&quot;><rect class=&quot;ln&quot; x=&quot;8&quot; y=&quot;26&quot; width=&quot;26&quot; height=&quot;14&quot; rx=&quot;2&quot;\/><path class=&quot;ln&quot; d=&quot;M34 29 L52 22 V44 L34 37 Z&quot;\/><\/svg>',\n  mask:'<svg viewBox=&quot;0 0 64 64&quot;><path class=&quot;ln&quot; d=&quot;M10 26 C10 16 20 10 32 10 C44 10 54 16 54 26 C54 40 44 50 32 50 C20 50 10 40 10 26 Z&quot;\/><circle class=&quot;ln&quot; cx=&quot;22&quot; cy=&quot;26&quot; r=&quot;4&quot;\/><circle class=&quot;ln&quot; cx=&quot;42&quot; cy=&quot;26&quot; r=&quot;4&quot;\/><path class=&quot;ln&quot; d=&quot;M24 38 Q32 44 40 38&quot;\/><\/svg>',\n  money:'<svg viewBox=&quot;0 0 64 64&quot;><circle class=&quot;ln&quot; cx=&quot;32&quot; cy=&quot;32&quot; r=&quot;22&quot;\/><path class=&quot;ln&quot; d=&quot;M26 22 h8 a6 6 0 0 1 0 12 h-8 a6 6 0 0 0 0 12 h10&quot;\/><line class=&quot;ln&quot; x1=&quot;32&quot; y1=&quot;16&quot; x2=&quot;32&quot; y2=&quot;48&quot;\/><\/svg>',\n  nodes:'<svg viewBox=&quot;0 0 64 64&quot;><line class=&quot;ln&quot; x1=&quot;14&quot; y1=&quot;16&quot; x2=&quot;50&quot; y2=&quot;32&quot;\/><line class=&quot;ln&quot; x1=&quot;14&quot; y1=&quot;48&quot; x2=&quot;50&quot; y2=&quot;32&quot;\/><line class=&quot;ln&quot; x1=&quot;14&quot; y1=&quot;16&quot; x2=&quot;14&quot; y2=&quot;48&quot;\/><circle class=&quot;fl&quot; cx=&quot;14&quot; cy=&quot;16&quot; r=&quot;6&quot;\/><circle class=&quot;fl&quot; cx=&quot;14&quot; cy=&quot;48&quot; r=&quot;6&quot;\/><circle class=&quot;fl&quot; cx=&quot;50&quot; cy=&quot;32&quot; r=&quot;6&quot;\/><\/svg>',\n  newsflash:'<svg viewBox=&quot;0 0 64 64&quot;><path class=&quot;ln&quot; d=&quot;M32 6 L38 24 L56 24 L41 34 L47 52 L32 41 L17 52 L23 34 L8 24 L26 24 Z&quot;\/><\/svg>',\n  island:'<svg viewBox=&quot;0 0 64 64&quot;><path class=&quot;ln&quot; d=&quot;M6 40 C16 30 48 30 58 40&quot;\/><path class=&quot;ln&quot; d=&quot;M6 46 C16 40 48 40 58 46&quot;\/><line class=&quot;ln&quot; x1=&quot;32&quot; y1=&quot;30&quot; x2=&quot;32&quot; y2=&quot;10&quot;\/><path class=&quot;ln&quot; d=&quot;M32 10 L46 16 L32 20 Z&quot;\/><\/svg>',\n  ledger:'<svg viewBox=&quot;0 0 64 64&quot;><rect class=&quot;ln&quot; x=&quot;14&quot; y=&quot;8&quot; width=&quot;36&quot; height=&quot;48&quot; rx=&quot;3&quot;\/><line class=&quot;ln&quot; x1=&quot;21&quot; y1=&quot;20&quot; x2=&quot;43&quot; y2=&quot;20&quot;\/><line class=&quot;ln&quot; x1=&quot;21&quot; y1=&quot;28&quot; x2=&quot;43&quot; y2=&quot;28&quot;\/><line class=&quot;ln&quot; x1=&quot;21&quot; y1=&quot;36&quot; x2=&quot;43&quot; y2=&quot;36&quot;\/><line class=&quot;ln&quot; x1=&quot;21&quot; y1=&quot;44&quot; x2=&quot;34&quot; y2=&quot;44&quot;\/><rect class=&quot;ln&quot; x=&quot;27&quot; y=&quot;4&quot; width=&quot;10&quot; height=&quot;8&quot; rx=&quot;2&quot;\/><\/svg>',\n  lock:'<svg viewBox=&quot;0 0 64 64&quot;><rect class=&quot;ln&quot; x=&quot;16&quot; y=&quot;28&quot; width=&quot;32&quot; height=&quot;26&quot; rx=&quot;3&quot;\/><path class=&quot;ln&quot; d=&quot;M22 28 V20 a10 10 0 0 1 20 0 v8&quot;\/><circle class=&quot;fl&quot; cx=&quot;32&quot; cy=&quot;40&quot; r=&quot;4&quot;\/><\/svg>',\n  scales:'<svg viewBox=&quot;0 0 64 64&quot;><line class=&quot;ln&quot; x1=&quot;32&quot; y1=&quot;8&quot; x2=&quot;32&quot; y2=&quot;52&quot;\/><line class=&quot;ln&quot; x1=&quot;12&quot; y1=&quot;18&quot; x2=&quot;52&quot; y2=&quot;18&quot;\/><path class=&quot;ln&quot; d=&quot;M4 18 L12 34 L20 18 Z&quot;\/><path class=&quot;ln&quot; d=&quot;M44 18 L52 34 L60 18 Z&quot;\/><line class=&quot;ln&quot; x1=&quot;20&quot; y1=&quot;56&quot; x2=&quot;44&quot; y2=&quot;56&quot;\/><\/svg>',\n  megaphone:'<svg viewBox=&quot;0 0 64 64&quot;><path class=&quot;ln&quot; d=&quot;M8 26 L30 14 V44 L8 32 Z&quot;\/><path class=&quot;ln&quot; d=&quot;M30 14 L52 8 V50 L30 44&quot;\/><path class=&quot;ln&quot; d=&quot;M14 32 L18 46&quot;\/><\/svg>',\n  hourglass:'<svg viewBox=&quot;0 0 64 64&quot;><path class=&quot;ln&quot; d=&quot;M16 8 H48 V16 L32 32 L48 48 V56 H16 V48 L32 32 L16 16 Z&quot;\/><\/svg>',\n  trap:'<svg viewBox=&quot;0 0 64 64&quot;><circle class=&quot;ln&quot; cx=&quot;32&quot; cy=&quot;24&quot; r=&quot;12&quot;\/><path class=&quot;ln&quot; d=&quot;M14 54 C14 40 22 34 32 34 C42 34 50 40 50 54&quot;\/><line class=&quot;ln&quot; x1=&quot;10&quot; y1=&quot;54&quot; x2=&quot;54&quot; y2=&quot;54&quot;\/><\/svg>',\n  trophy:'<svg viewBox=&quot;0 0 64 64&quot;><path class=&quot;ln&quot; d=&quot;M20 10 h24 v14 c0 10 -6 16 -12 16 s-12 -6 -12 -16 Z&quot;\/><path class=&quot;ln&quot; d=&quot;M20 14 h-8 c0 8 4 12 8 13&quot;\/><path class=&quot;ln&quot; d=&quot;M44 14 h8 c0 8 -4 12 -8 13&quot;\/><rect class=&quot;ln&quot; x=&quot;28&quot; y=&quot;40&quot; width=&quot;8&quot; height=&quot;8&quot;\/><rect class=&quot;ln&quot; x=&quot;18&quot; y=&quot;50&quot; width=&quot;28&quot; height=&quot;6&quot; rx=&quot;2&quot;\/><\/svg>',\n  check:'<svg viewBox=&quot;0 0 24 24&quot;><path fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;3&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; d=&quot;M4 12 L10 18 L20 6&quot;\/><\/svg>',\n  retry:'<svg viewBox=&quot;0 0 24 24&quot;><path fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;3&quot; stroke-linecap=&quot;round&quot; d=&quot;M4 4 v6 h6&quot;\/><path fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;3&quot; stroke-linecap=&quot;round&quot; d=&quot;M5 14 A9 9 0 1 0 7 6&quot;\/><\/svg>',\n  podium:'<svg viewBox=&quot;0 0 64 64&quot;><rect class=&quot;ln&quot; x=&quot;22&quot; y=&quot;34&quot; width=&quot;20&quot; height=&quot;18&quot; rx=&quot;2&quot;\/><circle class=&quot;ln&quot; cx=&quot;32&quot; cy=&quot;16&quot; r=&quot;7&quot;\/><path class=&quot;ln&quot; d=&quot;M24 34 C24 26 40 26 40 34&quot;\/><line class=&quot;ln&quot; x1=&quot;46&quot; y1=&quot;20&quot; x2=&quot;55&quot; y2=&quot;16&quot;\/><line class=&quot;ln&quot; x1=&quot;46&quot; y1=&quot;27&quot; x2=&quot;55&quot; y2=&quot;27&quot;\/><\/svg>',\n  socialmedia:'<svg viewBox=&quot;0 0 64 64&quot;><rect class=&quot;ln&quot; x=&quot;20&quot; y=&quot;6&quot; width=&quot;24&quot; height=&quot;42&quot; rx=&quot;4&quot;\/><line class=&quot;ln&quot; x1=&quot;27&quot; y1=&quot;44&quot; x2=&quot;37&quot; y2=&quot;44&quot;\/><path class=&quot;ln&quot; d=&quot;M32 30 C28 22 18 26 20 33 C22 40 32 44 32 44 C32 44 42 40 44 33 C46 26 36 22 32 30 Z&quot;\/><\/svg>',\n  tvscreen:'<svg viewBox=&quot;0 0 64 64&quot;><rect class=&quot;ln&quot; x=&quot;10&quot; y=&quot;14&quot; width=&quot;44&quot; height=&quot;30&quot; rx=&quot;3&quot;\/><circle class=&quot;ln&quot; cx=&quot;46&quot; cy=&quot;22&quot; r=&quot;3&quot;\/><line class=&quot;ln&quot; x1=&quot;24&quot; y1=&quot;50&quot; x2=&quot;40&quot; y2=&quot;50&quot;\/><line class=&quot;ln&quot; x1=&quot;32&quot; y1=&quot;44&quot; x2=&quot;32&quot; y2=&quot;50&quot;\/><\/svg>',\n  clipboard:'<svg viewBox=&quot;0 0 64 64&quot;><rect class=&quot;ln&quot; x=&quot;14&quot; y=&quot;12&quot; width=&quot;36&quot; height=&quot;44&quot; rx=&quot;3&quot;\/><rect class=&quot;ln&quot; x=&quot;24&quot; y=&quot;8&quot; width=&quot;16&quot; height=&quot;8&quot; rx=&quot;2&quot;\/><line class=&quot;ln&quot; x1=&quot;21&quot; y1=&quot;26&quot; x2=&quot;43&quot; y2=&quot;26&quot;\/><line class=&quot;ln&quot; x1=&quot;21&quot; y1=&quot;34&quot; x2=&quot;43&quot; y2=&quot;34&quot;\/><line class=&quot;ln&quot; x1=&quot;21&quot; y1=&quot;42&quot; x2=&quot;35&quot; y2=&quot;42&quot;\/><\/svg>',\n  plane:'<svg viewBox=&quot;0 0 64 64&quot;><path class=&quot;ln&quot; d=&quot;M8 32 L56 10 L34 56 L28 38 Z&quot;\/><line class=&quot;ln&quot; x1=&quot;28&quot; y1=&quot;38&quot; x2=&quot;56&quot; y2=&quot;10&quot;\/><\/svg>',\n  codedenvelope:'<svg viewBox=&quot;0 0 64 64&quot;><rect class=&quot;ln&quot; x=&quot;8&quot; y=&quot;16&quot; width=&quot;48&quot; height=&quot;34&quot; rx=&quot;3&quot;\/><path class=&quot;ln&quot; d=&quot;M10 18 L32 36 L54 18&quot;\/><circle class=&quot;ln&quot; cx=&quot;32&quot; cy=&quot;34&quot; r=&quot;5&quot;\/><line class=&quot;ln&quot; x1=&quot;32&quot; y1=&quot;39&quot; x2=&quot;32&quot; y2=&quot;44&quot;\/><\/svg>',\n  logbook:'<svg viewBox=&quot;0 0 64 64&quot;><path class=&quot;ln&quot; d=&quot;M8 14 C16 10 26 10 32 14 C38 10 48 10 56 14 V50 C48 46 38 46 32 50 C26 46 16 46 8 50 Z&quot;\/><line class=&quot;ln&quot; x1=&quot;32&quot; y1=&quot;14&quot; x2=&quot;32&quot; y2=&quot;50&quot;\/><path class=&quot;ln&quot; d=&quot;M14 24 L18 28 L24 20&quot;\/><path class=&quot;ln&quot; d=&quot;M40 24 L44 28 L50 20&quot;\/><\/svg>',\n  message:'<svg viewBox=&quot;0 0 64 64&quot;><path class=&quot;ln&quot; d=&quot;M10 14 H54 V40 H26 L16 50 V40 H10 Z&quot;\/><line class=&quot;ln&quot; x1=&quot;24&quot; y1=&quot;20&quot; x2=&quot;40&quot; y2=&quot;34&quot;\/><line class=&quot;ln&quot; x1=&quot;40&quot; y1=&quot;20&quot; x2=&quot;24&quot; y2=&quot;34&quot;\/><\/svg>',\n  voicemail:'<svg viewBox=&quot;0 0 64 64&quot;><path class=&quot;ln&quot; d=&quot;M12 14 C12 14 20 12 22 20 C23 25 19 26 20 30 C22 38 26 42 34 44 C38 45 39 41 44 42 C52 44 50 52 50 52 C50 52 40 58 28 50 C16 42 10 30 12 14 Z&quot;\/><circle class=&quot;ln&quot; cx=&quot;46&quot; cy=&quot;18&quot; r=&quot;10&quot;\/><path class=&quot;fl&quot; d=&quot;M43 13 L53 18 L43 23 Z&quot;\/><\/svg>',\n  memo:'<svg viewBox=&quot;0 0 64 64&quot;><path class=&quot;ln&quot; d=&quot;M14 8 H40 L50 18 V56 H14 Z&quot;\/><path class=&quot;ln&quot; d=&quot;M40 8 V18 H50&quot;\/><line class=&quot;ln&quot; x1=&quot;20&quot; y1=&quot;28&quot; x2=&quot;44&quot; y2=&quot;28&quot;\/><line class=&quot;ln&quot; x1=&quot;20&quot; y1=&quot;36&quot; x2=&quot;44&quot; y2=&quot;36&quot;\/><line class=&quot;ln&quot; x1=&quot;20&quot; y1=&quot;44&quot; x2=&quot;36&quot; y2=&quot;44&quot;\/><\/svg>'\n};\n\nfunction sparks(n){\n  let out = '';\n  const positions = [[10,20],[30,70],[55,10],[70,55],[85,25],[45,40]];\n  for(let i=0;i<n;i++){\n    const p = positions[i % positions.length];\n    out += '<span class=&quot;spark&quot; style=&quot;left:' + p[0] + '%; top:' + p[1] + '%; animation-delay:' + (i*0.7) + 's;&quot;><\/span>';\n  }\n  return out;\n}\n\nfunction scene(tone, iconKey, caption){\n  return '<div class=&quot;scene-banner tone-' + tone + '&quot;>' + sparks(6) +\n    '<div class=&quot;mission-icon&quot;>' + icons[iconKey] + '<\/div>' +\n    '<div class=&quot;scene-caption&quot;>' + caption + '<\/div>' +\n  '<\/div>';\n}\n\nconst steps = [\n  { id:'intro', type:'intro' },\n  {\n    id:'l1', type:'spot', order:1, mission:'Level 1', title:'The Perfect Billionaire',\n    tone:'teal', icon:'evidence', caption:'Find the inconsistency',\n    narrative:'Billionaire philanthropist Richard Blackwood was found dead in his mansion. Before you dig into his death, your editor wants you to check his public image first. Click the piece of evidence below that hides an inconsistency.',\n    docs:[\n      { key:'A', label:'Newspaper articles', desc:'Years of glowing coverage praising Blackwood\\'s generosity.', meta:'Archive \u00b7 12 articles', thumb:'press' },\n      { key:'B', label:'Charity speeches', desc:'Blackwood speaking at fundraising galas about giving back.', meta:'Video transcripts', thumb:'podium' },\n      { key:'C', label:'Family photographs', desc:'An official photo says it was taken at a local charity event \u2014 but the caption date places Blackwood abroad that same day.', meta:'Photo taken: supposedly local \u00b7 Blackwood was abroad', thumb:'photo' },\n      { key:'D', label:'Social media posts', desc:'Cheerful posts about his foundation\\'s latest projects.', meta:'Public account', thumb:'socialmedia' },\n      { key:'E', label:'TV interviews', desc:'A relaxed, well-rehearsed interview about his humble beginnings.', meta:'National broadcast', thumb:'tvscreen' }\n    ],\n    correct:'C',\n    feedbackCorrect:'Good eye. That photograph could not have been taken where and when it claims \u2014 someone altered the official timeline. That is your first real clue.',\n    feedbackWrong:'That piece fits Blackwood\\'s public image perfectly \u2014 no contradiction there. Look for the one detail that does not add up.'\n  },\n  {\n    id:'l2', type:'multiselect', order:2, mission:'Level 2', title:'The Missing Employee',\n    tone:'orange', icon:'cctv', caption:'Reconstruct the final day',\n    narrative:'A former assistant disappeared three years ago. The company says &quot;she resigned and moved overseas&quot; \u2014 but something doesn\\'t match. Select every clue that helps reconstruct her final day at the company.',\n    items:[\n      { key:'A', text:'Deleted text messages recovered from a backup server.', correct:true, icon:'message' },\n      { key:'B', text:'An old voicemail she never picked up.', correct:true, icon:'voicemail' },\n      { key:'C', text:'A damaged security badge found in a stairwell.', correct:true, icon:'badge2' },\n      { key:'D', text:'CCTV footage with missing timestamps.', correct:true, icon:'cctv' },\n      { key:'E', text:'A press release announcing a new office location.', correct:false, icon:'memo' },\n      { key:'F', text:'A generic company holiday newsletter.', correct:false, icon:'memo' }\n    ],\n    feedbackCorrect:'Exactly \u2014 these four clues line up. Put together, they suggest she never left the building.',\n    feedbackWrong:'Not every document in the folder is relevant. Re-check which ones actually describe her last day.'\n  },\n  {\n    id:'l3a', type:'choice', order:3, mission:'Level 3', title:'The Whistleblower \u2014 Question 1',\n    tone:'teal', icon:'shield', caption:'Build trust, carefully',\n    narrative:'An anonymous former employee contacts your newspaper: <em>&quot;Blackwood wasn\\'t acting alone.&quot;<\/em> He is terrified. Your first question can build his trust \u2014 or scare him away for good. What do you ask?',\n    options:[\n      {key:'A', text:'&quot;Can you tell me your name for the record?&quot;'},\n      {key:'B', text:'&quot;You said he wasn\\'t acting alone \u2014 can you tell me more, only if you feel safe to?&quot;'},\n      {key:'C', text:'&quot;I need everything right now or I can\\'t help you.&quot;'}\n    ],\n    correct:'B',\n    feedbackCorrect:'Good instinct. You let him lead at his own pace, without pressuring him to identify himself.',\n    feedbackWrong:'That kind of question puts pressure on a frightened source \u2014 exactly what could make him disappear.'\n  },\n  {\n    id:'l3b', type:'choice', order:4, mission:'Level 3', title:'The Whistleblower \u2014 Question 2',\n    tone:'teal', icon:'shield', caption:'Build trust, carefully',\n    narrative:'He starts talking, hesitantly. What do you ask next?',\n    options:[\n      {key:'A', text:'&quot;Do you have any documents that could support what you\\'re telling me?&quot;'},\n      {key:'B', text:'&quot;Are you willing to go on record with your name?&quot;'},\n      {key:'C', text:'&quot;Who else knows you\\'re talking to me?&quot;'}\n    ],\n    correct:'A',\n    feedbackCorrect:'Right \u2014 asking for supporting documents moves the investigation forward without threatening his safety.',\n    feedbackWrong:'That question focuses on exposing him rather than on verifying the story \u2014 likely to make him more afraid, not less.'\n  },\n  {\n    id:'l3c', type:'choice', order:5, mission:'Level 3', title:'The Whistleblower \u2014 Question 3',\n    tone:'teal', icon:'shield', caption:'Build trust, carefully',\n    narrative:'He is close to giving you real material. One last question can close the deal.',\n    options:[\n      {key:'A', text:'&quot;I promise I will protect your identity, no matter what.&quot;'},\n      {key:'B', text:'&quot;This is taking too long \u2014 send me what you have now.&quot;'},\n      {key:'C', text:'&quot;Can I quote you by name in the article?&quot;'}\n    ],\n    correct:'A',\n    feedbackCorrect:'Exactly. Reassuring him about source protection is what finally earns his trust.',\n    feedbackWrong:'That response pressures or exposes him instead of reassuring him \u2014 the opposite of what a frightened source needs to hear.'\n  },\n  {\n    id:'l4', type:'matching', order:6, mission:'Level 4', title:'Following the Money',\n    tone:'orange', icon:'money', caption:'The evidence board',\n    narrative:'You uncover dozens of shell companies. Connect each document type to what it actually reveals about the network.',\n    answerOptions:['Moves money between shell companies','Proves a company legally exists','Shows unpaid or underreported taxes'],\n    pairs:[\n      {desc:'Bank transfers between unrelated shell companies.', correct:'Moves money between shell companies'},\n      {desc:'Official company registration papers.', correct:'Proves a company legally exists'},\n      {desc:'Tax records that don\\'t match declared income.', correct:'Shows unpaid or underreported taxes'}\n    ],\n    takeaway:'Bank transfers move the money, registrations prove the companies exist on paper, and mismatched tax records expose what\\'s really happening. Together: Blackwood wasn\\'t the leader of this network \u2014 he was only the accountant.'\n  },\n  {\n    id:'l5', type:'matching', order:7, mission:'Level 5', title:'Fake News Attack',\n    tone:'mix', icon:'newsflash', caption:'Sort the sources',\n    narrative:'The moment your investigation goes public, television channels attack your newspaper and social media fills with misinformation. Your editor asks: &quot;Can we still prove we\\'re telling the truth?&quot; Classify each item correctly \u2014 every mistake lowers your newspaper\\'s credibility.',\n    answerOptions:['Fact','Opinion','Propaganda','Sponsored content','Manipulated headline','AI-generated image'],\n    pairs:[\n      {desc:'&quot;Bank records confirm a transfer of $2M between the two companies on March 3rd.&quot;', correct:'Fact'},\n      {desc:'&quot;In my view, Blackwood was clearly the smartest businessman of his generation.&quot;', correct:'Opinion'},\n      {desc:'A video claiming your newspaper is &quot;an enemy of the people&quot; and cannot be trusted.', correct:'Propaganda'},\n      {desc:'An article about Blackwood\\'s foundation, paid for and placed by that same foundation.', correct:'Sponsored content'},\n      {desc:'A headline reading &quot;Journalist ADMITS Story Was Invented&quot; \u2014 cut from a sentence that actually said the opposite.', correct:'Manipulated headline'},\n      {desc:'A photo &quot;of the whistleblower&quot; circulating online that was never actually taken by anyone.', correct:'AI-generated image'}\n    ],\n    takeaway:'Facts are verifiable, opinions are personal judgments, propaganda attacks the messenger instead of the message, sponsored content is paid promotion, manipulated headlines twist real quotes, and AI-generated images can fabricate &quot;evidence&quot; that never existed.'\n  },\n  {\n    id:'l6', type:'spot', order:8, mission:'Level 6', title:'The Private Island',\n    tone:'teal', icon:'island', caption:'Officially, a conference center',\n    narrative:'Flight records reveal a mysterious private island. Officially, it was a luxury conference center. Unofficially, nobody knows. Click the piece of evidence that reveals a hidden, undisclosed visitor.',\n    docs:[\n      { key:'A', label:'Guest list', desc:'A polished, official list of attendees for a &quot;leadership retreat.&quot;', meta:'Provided by the venue', thumb:'clipboard' },\n      { key:'B', label:'Flight schedules', desc:'Commercial flight bookings matching the official guest list exactly.', meta:'Public records', thumb:'plane' },\n      { key:'C', label:'A coded invitation', desc:'A private jet manifest lists a passenger under a code name that matches no one on the official guest list.', meta:'Leaked manifest', thumb:'codedenvelope' },\n      { key:'D', label:'Security logs', desc:'Standard entry logs, timestamps consistent with the official schedule.', meta:'Venue archive', thumb:'logbook' },\n      { key:'E', label:'Photographs', desc:'Press photos of the retreat\\'s public sessions.', meta:'Released to media', thumb:'photo' }\n    ],\n    correct:'C',\n    feedbackCorrect:'There it is \u2014 a passenger who was never supposed to be officially linked to this event. The guest list keeps growing.',\n    feedbackWrong:'That document matches the official story perfectly. Look for the one piece that doesn\\'t fit the &quot;conference center&quot; cover.'\n  },\n  {\n    id:'l7a', type:'decode', order:9, mission:'Level 7', title:'The Black Ledger \u2014 Decrypt',\n    tone:'orange', icon:'ledger', caption:'Thousands of files, no order',\n    narrative:'You finally open the encrypted file: emails, phone records, secret calendars, voice recordings \u2014 nothing is organized. One file is protected by a simple letter-shift cipher. Each letter has been shifted forward by 3 (A becomes D, B becomes E, and so on). Decode the mastermind\\'s code name.',\n    cipher:'WKH VKDGRZ',\n    answer:'THE SHADOW',\n    hint:'Shift each letter backward by 3 in the alphabet (D \u2192 A, E \u2192 B...).',\n    feedbackCorrect:'Decrypted: &quot;THE SHADOW&quot; \u2014 a code name used across the network\\'s communications.',\n    feedbackWrong:'Not quite \u2014 remember, shift every letter back by 3 positions in the alphabet.'\n  },\n  {\n    id:'l7b', type:'matching', order:10, mission:'Level 7', title:'The Black Ledger \u2014 Match the Names',\n    tone:'orange', icon:'ledger', caption:'Nicknames to real people',\n    narrative:'The ledger uses code names for the people protecting the network. Match each code name to the role it hides.',\n    answerOptions:['Richard Blackwood','A senior politician','A corporate lawyer'],\n    pairs:[\n      {desc:'&quot;The Accountant&quot; \u2014 kept the books, took the fall, was never the one truly in charge.', correct:'Richard Blackwood'},\n      {desc:'&quot;The Shield&quot; \u2014 used official power to make investigations disappear.', correct:'A senior politician'},\n      {desc:'&quot;The Architect&quot; \u2014 designed the shell companies and the paper trail to hide them.', correct:'A corporate lawyer'}\n    ],\n    takeaway:'Blackwood protected an international network \u2014 he never controlled it. The real structure goes far beyond one billionaire.'\n  },\n  {\n    id:'l8', type:'flavor', order:11, mission:'Level 8', title:'The Cover-Up',\n    tone:'red', icon:'mask', caption:'Who do you trust now?',\n    narrative:'Suddenly, everything turns. Witnesses disappear. Evidence is stolen. Your office is broken into. Someone inside your own newspaper is leaking information \u2014 you no longer know who to trust. What do you do?',\n    options:[\n      {key:'A', text:'Protect your source above everything else.', reaction:'You lock down every channel that could expose your source, even at the cost of moving slower.'},\n      {key:'B', text:'Publish immediately, before more evidence disappears.', reaction:'You rush what you have to print \u2014 some of it solid, some of it still unconfirmed.'},\n      {key:'C', text:'Keep investigating quietly, deeper into the network.', reaction:'You go dark for a while, gathering more proof before making any move.'},\n      {key:'D', text:'Try to expose the leak inside your own newsroom first.', reaction:'You turn the investigation inward, hunting for whoever is feeding information to the network.'}\n    ]\n  },\n  {\n    id:'final', type:'vote', order:12, mission:'Final Level', title:'Publish or Protect?',\n    tone:'mix', icon:'scales', caption:'The public deserves an answer',\n    narrative:'You finally possess enough evidence to expose one of the biggest corruption networks in the country\\'s history. But publishing everything could destroy innocent lives, endanger witnesses, provoke political instability, and expose your own newspaper. Your editor asks one final question: <em>&quot;Is the public ready to know the truth?&quot;<\/em> The newsroom votes.',\n    options:[\n      {key:'A', text:'Publish everything, right now.', ending:'A'},\n      {key:'B', text:'Publish only what is fully verified, and keep digging.', ending:'B'},\n      {key:'C', text:'Hand the story to a contact who promises to protect it.', ending:'C'}\n    ]\n  }\n];\n\nconst endings = {\n  A:{\n    tag:'Ending A', icon:'megaphone', color:'var(--good)',\n    title:'Publish Everything',\n    text:'The investigation becomes international news. Several officials resign. Arrests begin. But your team starts receiving anonymous threats. Truth has a price \u2014 and your newsroom just paid the first installment of it.'\n  },\n  B:{\n    tag:'Ending B', icon:'hourglass', color:'var(--amber)',\n    title:'Wait for More Evidence',\n    text:'The investigation continues, carefully. Months later, even bigger names are exposed. Justice takes longer this way \u2014 but what you eventually publish is unshakeable, and it holds up in court.'\n  },\n  C:{\n    tag:'Ending C', icon:'trap', color:'var(--bad)',\n    title:'Trust the Wrong Person',\n    text:'The contact you trusted was never on your side. A mole inside the newsroom deletes your files overnight. The investigation disappears. The network survives. Case closed \u2014 for now.'\n  }\n};\n\nconst gameplaySteps = steps.filter(s => s.order);\n\nfunction stepPoints(step){\n  if(step.type === 'matching') return step.pairs.length * 3;\n  if(step.type === 'flavor' || step.type === 'vote') return 0;\n  return 3;\n}\nconst maxScore = gameplaySteps.reduce((sum, s) => sum + stepPoints(s), 0);\n\nlet score = 0;\nlet stepIndex = 0;\nlet reviewLog = [];\n\nfunction updateHud(step){\n  document.getElementById('scoreLabel').textContent = 'Score: ' + score;\n  const stepLabel = document.getElementById('stepLabel');\n  const dotsWrap = document.getElementById('dots');\n  if(step &amp;&amp; step.order){\n    stepLabel.textContent = 'Step ' + step.order + ' \/ ' + gameplaySteps.length;\n    dotsWrap.style.display = 'flex';\n    dotsWrap.innerHTML = gameplaySteps.map(s => {\n      let cls = 'dot';\n      if(s.order < step.order) cls += ' done';\n      if(s.order === step.order) cls += ' current';\n      return '<div class=&quot;' + cls + '&quot;><\/div>';\n    }).join('');\n  } else {\n    stepLabel.textContent = '';\n    dotsWrap.style.display = 'none';\n  }\n}\n\nfunction renderStep(){\n  const step = steps[stepIndex];\n  updateHud(step);\n  const stage = document.getElementById('stage');\n  if(step.type === 'intro') stage.innerHTML = introHTML();\n  else if(step.type === 'spot') stage.innerHTML = spotHTML(step);\n  else if(step.type === 'multiselect') stage.innerHTML = multiselectHTML(step);\n  else if(step.type === 'choice') stage.innerHTML = choiceHTML(step);\n  else if(step.type === 'matching') stage.innerHTML = matchingHTML(step);\n  else if(step.type === 'decode') stage.innerHTML = decodeHTML(step);\n  else if(step.type === 'flavor') stage.innerHTML = flavorHTML(step);\n  else if(step.type === 'vote') stage.innerHTML = voteHTML(step);\n  attachHandlers(step);\n  window.scrollTo({top:0, behavior:'smooth'});\n}\n\nfunction introHTML(){\n  return '<div class=&quot;card&quot;>' +\n    scene('mix','badge','Case file \u00b7 Open') +\n    '<div class=&quot;case-number&quot;>THE HERALD \u2014 CONFIDENTIAL<\/div>' +\n    '<h1>The Black Ledger<\/h1>' +\n    '<p class=&quot;subtitle&quot;>&quot;The deeper you investigate, the darker the truth becomes.&quot;<\/p>' +\n    '<div class=&quot;disclaimer&quot;>This is a fictional scenario written for a media-literacy exercise on investigative journalism (1\u00e8re AMC). Any resemblance to real people or organizations is purely coincidental.<\/div>' +\n    '<p class=&quot;narrative&quot;>You are a young investigative journalist at The Herald. Billionaire philanthropist Richard Blackwood is found dead in his mansion. Police call it a suicide. The media moves on. Then your editor receives an anonymous message: <em>&quot;Don\\'t investigate his death. Investigate his life.&quot;<\/em> Attached is a single encrypted file: BLACK LEDGER.<\/p>' +\n    '<p class=&quot;narrative&quot;>Across 12 steps, you will question his public image, reconstruct a disappearance, earn a whistleblower\\'s trust, follow the money, survive a fake news attack, and finally decide how far the truth should go. How deep are you willing to go?<\/p>' +\n    '<div class=&quot;intro-actions&quot;><button class=&quot;start-btn&quot; id=&quot;startBtn&quot;>Open the file<\/button><\/div>' +\n  '<\/div>';\n}\n\nfunction docThumb(key){ return '<div class=&quot;doc-thumb&quot;>' + icons[key] + '<\/div>'; }\n\nfunction spotHTML(step){\n  return '<div class=&quot;card&quot;>' +\n    scene(step.tone, step.icon, step.caption) +\n    '<div class=&quot;tag&quot;>' + step.mission + '<\/div>' +\n    '<h2>' + step.title + '<\/h2>' +\n    '<p class=&quot;narrative&quot;>' + step.narrative + '<\/p>' +\n    '<div class=&quot;docs-grid&quot; id=&quot;docsWrap&quot;>' +\n      step.docs.map(d => '<div class=&quot;doc-card&quot; data-key=&quot;' + d.key + '&quot;>' +\n        docThumb(d.thumb) +\n        '<div class=&quot;doc-tag&quot;>' + d.label + '<\/div>' +\n        '<div>' + d.desc + '<\/div>' +\n        '<div class=&quot;doc-meta&quot;>' + d.meta + '<\/div>' +\n      '<\/div>').join('') +\n    '<\/div>' +\n    '<div class=&quot;feedback&quot; id=&quot;feedback&quot;><\/div>' +\n    '<button class=&quot;continue-btn&quot; id=&quot;continueBtn&quot; style=&quot;display:none&quot;>Continue &amp;rarr;<\/button>' +\n  '<\/div>';\n}\n\nfunction multiselectHTML(step){\n  return '<div class=&quot;card&quot;>' +\n    scene(step.tone, step.icon, step.caption) +\n    '<div class=&quot;tag&quot;>' + step.mission + '<\/div>' +\n    '<h2>' + step.title + '<\/h2>' +\n    '<p class=&quot;narrative&quot;>' + step.narrative + '<\/p>' +\n    '<div class=&quot;checklist&quot; id=&quot;checklistWrap&quot;>' +\n      step.items.map(it => '<label class=&quot;check-item&quot; data-key=&quot;' + it.key + '&quot;><input type=&quot;checkbox&quot; value=&quot;' + it.key + '&quot;><span class=&quot;check-icon&quot;>' + icons[it.icon] + '<\/span><span>' + it.text + '<\/span><\/label>').join('') +\n    '<\/div>' +\n    '<div class=&quot;feedback&quot; id=&quot;feedback&quot;><\/div>' +\n    '<button class=&quot;continue-btn&quot; id=&quot;checkBtn&quot;>Confirm selection<\/button>' +\n    '<button class=&quot;continue-btn&quot; id=&quot;continueBtn&quot; style=&quot;display:none; margin-left:10px;&quot;>Continue &amp;rarr;<\/button>' +\n  '<\/div>';\n}\n\nfunction choiceHTML(step){\n  return '<div class=&quot;card&quot;>' +\n    scene(step.tone, step.icon, step.caption) +\n    '<div class=&quot;tag&quot;>' + step.mission + '<\/div>' +\n    '<h2>' + step.title + '<\/h2>' +\n    '<p class=&quot;narrative&quot;>' + step.narrative + '<\/p>' +\n    '<div class=&quot;options&quot; id=&quot;optionsWrap&quot;>' +\n      step.options.map(o => '<button class=&quot;opt&quot; data-key=&quot;' + o.key + '&quot;><span class=&quot;badge&quot;>' + o.key + '<\/span><span>' + o.text + '<\/span><\/button>').join('') +\n    '<\/div>' +\n    '<div class=&quot;feedback&quot; id=&quot;feedback&quot;><\/div>' +\n    '<button class=&quot;continue-btn&quot; id=&quot;continueBtn&quot; style=&quot;display:none&quot;>Continue &amp;rarr;<\/button>' +\n  '<\/div>';\n}\n\nfunction matchingHTML(step){\n  return '<div class=&quot;card&quot;>' +\n    scene(step.tone, step.icon, step.caption) +\n    '<div class=&quot;tag&quot;>' + step.mission + '<\/div>' +\n    '<h2>' + step.title + '<\/h2>' +\n    '<p class=&quot;narrative&quot;>' + step.narrative + '<\/p>' +\n    '<div class=&quot;match-rows&quot; id=&quot;matchRows&quot;>' +\n      step.pairs.map((p,i) => '<div class=&quot;match-row&quot; data-index=&quot;' + i + '&quot;>' +\n        '<div class=&quot;match-desc&quot;>' + (i+1) + '. ' + p.desc + '<\/div>' +\n        '<select class=&quot;match-select&quot; data-index=&quot;' + i + '&quot;>' +\n          '<option value=&quot;&quot;>\u2014 choose \u2014<\/option>' +\n          step.answerOptions.map(a => '<option value=&quot;' + a + '&quot;>' + a + '<\/option>').join('') +\n        '<\/select>' +\n        '<span class=&quot;match-feedback&quot;><\/span>' +\n      '<\/div>').join('') +\n    '<\/div>' +\n    '<div class=&quot;feedback&quot; id=&quot;feedback&quot;><\/div>' +\n    '<button class=&quot;continue-btn&quot; id=&quot;checkBtn&quot;>Check my answers<\/button>' +\n    '<button class=&quot;continue-btn&quot; id=&quot;continueBtn&quot; style=&quot;display:none; margin-left:10px;&quot;>Continue &amp;rarr;<\/button>' +\n  '<\/div>';\n}\n\nfunction decodeHTML(step){\n  return '<div class=&quot;card&quot;>' +\n    scene(step.tone, step.icon, step.caption) +\n    '<div class=&quot;tag&quot;>' + step.mission + '<\/div>' +\n    '<h2>' + step.title + '<\/h2>' +\n    '<p class=&quot;narrative&quot;>' + step.narrative + '<\/p>' +\n    '<div class=&quot;decode-box&quot;>' +\n      '<div class=&quot;cipher-text&quot;>' + step.cipher + '<\/div>' +\n      '<input type=&quot;text&quot; class=&quot;decode-input&quot; id=&quot;decodeInput&quot; placeholder=&quot;YOUR ANSWER&quot; autocomplete=&quot;off&quot;>' +\n      '<div><button class=&quot;hint-btn&quot; id=&quot;hintBtn&quot;>Need a hint?<\/button><\/div>' +\n    '<\/div>' +\n    '<div class=&quot;feedback&quot; id=&quot;feedback&quot;><\/div>' +\n    '<button class=&quot;continue-btn&quot; id=&quot;checkBtn&quot;>Decrypt<\/button>' +\n    '<button class=&quot;continue-btn&quot; id=&quot;continueBtn&quot; style=&quot;display:none; margin-left:10px;&quot;>Continue &amp;rarr;<\/button>' +\n  '<\/div>';\n}\n\nfunction flavorHTML(step){\n  return '<div class=&quot;card&quot;>' +\n    scene(step.tone, step.icon, step.caption) +\n    '<div class=&quot;tag&quot;>' + step.mission + '<\/div>' +\n    '<h2>' + step.title + '<\/h2>' +\n    '<p class=&quot;narrative&quot;>' + step.narrative + '<\/p>' +\n    '<div class=&quot;options&quot; id=&quot;optionsWrap&quot;>' +\n      step.options.map(o => '<button class=&quot;opt&quot; data-key=&quot;' + o.key + '&quot;><span class=&quot;badge&quot;>' + o.key + '<\/span><span>' + o.text + '<\/span><\/button>').join('') +\n    '<\/div>' +\n    '<div class=&quot;feedback&quot; id=&quot;feedback&quot;><\/div>' +\n    '<button class=&quot;continue-btn&quot; id=&quot;continueBtn&quot; style=&quot;display:none&quot;>Continue &amp;rarr;<\/button>' +\n  '<\/div>';\n}\n\nfunction voteHTML(step){\n  return '<div class=&quot;card&quot;>' +\n    scene(step.tone, step.icon, step.caption) +\n    '<div class=&quot;tag&quot;>' + step.mission + '<\/div>' +\n    '<h2>' + step.title + '<\/h2>' +\n    '<p class=&quot;narrative&quot;>' + step.narrative + '<\/p>' +\n    '<div class=&quot;options&quot; id=&quot;optionsWrap&quot;>' +\n      step.options.map(o => '<button class=&quot;opt&quot; data-key=&quot;' + o.key + '&quot;><span class=&quot;badge&quot;>' + o.key + '<\/span><span>' + o.text + '<\/span><\/button>').join('') +\n    '<\/div>' +\n  '<\/div>';\n}\n\nfunction getTier(){\n  const pct = maxScore > 0 ? score \/ maxScore : 0;\n  if(pct >= 0.85) return { tier:'gold', label:'Gold-level investigation' };\n  if(pct >= 0.65) return { tier:'silver', label:'Silver-level investigation' };\n  if(pct >= 0.40) return { tier:'bronze', label:'Bronze-level investigation' };\n  return { tier:'none', label:'Investigation needs work' };\n}\nconst tierColors = { gold:'var(--gold)', silver:'var(--silver)', bronze:'var(--bronze)', none:'var(--bad)' };\n\nfunction confetti(tier){\n  if(tier !== 'gold') return '';\n  const colors = ['var(--gold)','var(--teal)','var(--orange)','#fff'];\n  let out = '';\n  for(let i=0;i<16;i++){\n    const left = Math.round((i * 6.3) % 100);\n    const delay = (i % 8) * 0.3;\n    out += '<span class=&quot;confetti-piece&quot; style=&quot;left:' + left + '%; background:' + colors[i % colors.length] + '; animation-delay:' + delay + 's;&quot;><\/span>';\n  }\n  return out;\n}\n\nfunction reviewIcon(item){\n  if(item.attempts === 0) return '<span class=&quot;review-icon perfect&quot;>' + icons.check + '<\/span>';\n  if(item.net >= 1) return '<span class=&quot;review-icon retry&quot;>' + icons.retry + '<\/span>';\n  return '<span class=&quot;review-icon low&quot;>' + icons.retry + '<\/span>';\n}\n\nfunction reviewHTML(){\n  return '<div class=&quot;review-list&quot;>' + reviewLog.map(item => {\n    const ptsClass = item.net > 0 ? 'pos' : (item.net === 0 ? 'zero' : 'neg');\n    const ptsText = (item.net > 0 ? '+' : '') + item.net + ' pt' + (Math.abs(item.net) === 1 ? '' : 's');\n    const attemptsNote = item.attempts > 0 ? ' \u2014 needed ' + item.attempts + (item.attempts === 1 ? ' extra try' : ' extra tries') : '';\n    return '<div class=&quot;review-item&quot;>' + reviewIcon(item) +\n      '<div class=&quot;review-body&quot;>' +\n        '<div class=&quot;review-title&quot;><span>' + item.mission + ' \u2014 ' + item.title + '<\/span><span class=&quot;review-pts ' + ptsClass + '&quot;>' + ptsText + '<\/span><\/div>' +\n        '<div class=&quot;review-note&quot;>' + item.takeaway + attemptsNote + '<\/div>' +\n      '<\/div>' +\n    '<\/div>';\n  }).join('') + '<\/div>';\n}\n\nfunction renderEnding(key){\n  const ending = endings[key];\n  const t = getTier();\n  const pct = maxScore > 0 ? Math.max(0, Math.min(1, score \/ maxScore)) : 0;\n  const r = 76;\n  const circumference = 2 * Math.PI * r;\n  const offset = circumference * (1 - pct);\n  const color = tierColors[t.tier];\n  const stage = document.getElementById('stage');\n  document.getElementById('stepLabel').textContent = '';\n  document.getElementById('dots').style.display = 'none';\n  stage.innerHTML = '<div class=&quot;card result-card&quot;>' +\n    confetti(t.tier) +\n    '<div class=&quot;ending-tag ' + key + '&quot;>' + ending.tag + '<\/div>' +\n    '<div class=&quot;medal-icon&quot; style=&quot;color:' + ending.color + '&quot;>' + icons[ending.icon] + '<\/div>' +\n    '<h2 style=&quot;text-align:center;&quot;>' + ending.title + '<\/h2>' +\n    '<p class=&quot;medal-msg&quot;>' + ending.text + '<\/p>' +\n    '<div class=&quot;ring-wrap&quot;>' +\n      '<svg viewBox=&quot;0 0 170 170&quot;>' +\n        '<circle class=&quot;ring-track&quot; cx=&quot;85&quot; cy=&quot;85&quot; r=&quot;' + r + '&quot;\/>' +\n        '<circle class=&quot;ring-fill&quot; cx=&quot;85&quot; cy=&quot;85&quot; r=&quot;' + r + '&quot; stroke=&quot;' + color + '&quot; stroke-dasharray=&quot;' + circumference + '&quot; stroke-dashoffset=&quot;' + offset + '&quot;\/>' +\n      '<\/svg>' +\n      '<div class=&quot;ring-center&quot;><span class=&quot;score-num&quot;>' + score + '<\/span><span class=&quot;score-den&quot;>\/ ' + maxScore + ' pts<\/span><\/div>' +\n    '<\/div>' +\n    '<div class=&quot;medal-title&quot; style=&quot;color:' + color + '&quot;>' + t.label + '<\/div>' +\n    '<h2 style=&quot;font-size:1.05rem; text-align:left;&quot;>Case review<\/h2>' +\n    reviewHTML() +\n    '<p class=&quot;reflection&quot;>The Herald moves on to its next story. How deep would you go next time?<\/p>' +\n    '<button class=&quot;start-btn&quot; id=&quot;restartBtn&quot;>Play Again<\/button>' +\n  '<\/div>';\n  document.getElementById('restartBtn').addEventListener('click', () => {\n    score = 0; stepIndex = 0; reviewLog = [];\n    renderStep();\n  });\n}\n\nfunction goNext(){\n  stepIndex++;\n  renderStep();\n}\n\nfunction attachHandlers(step){\n  if(step.type === 'intro'){\n    document.getElementById('startBtn').addEventListener('click', goNext);\n    return;\n  }\n\n  if(step.type === 'spot'){\n    let solved = false, firstTry = true, wrongAttempts = 0;\n    const feedbackEl = document.getElementById('feedback');\n    const continueBtn = document.getElementById('continueBtn');\n    document.querySelectorAll('#docsWrap .doc-card').forEach(card => {\n      card.addEventListener('click', () => {\n        if(solved) return;\n        const key = card.dataset.key;\n        if(key === step.correct){\n          solved = true;\n          if(firstTry) score += 3;\n          card.classList.add('selected');\n          feedbackEl.textContent = step.feedbackCorrect;\n          feedbackEl.className = 'feedback good';\n          continueBtn.style.display = 'inline-block';\n          reviewLog.push({ mission: step.mission, title: step.title, net:(firstTry?3:0)-wrongAttempts, attempts:wrongAttempts, takeaway: step.feedbackCorrect });\n        } else {\n          firstTry = false; wrongAttempts++; score -= 1;\n          card.classList.add('wrong-pick');\n          setTimeout(() => card.classList.remove('wrong-pick'), 500);\n          feedbackEl.textContent = step.feedbackWrong;\n          feedbackEl.className = 'feedback bad';\n        }\n        updateHud(step);\n      });\n    });\n    continueBtn.addEventListener('click', goNext);\n    return;\n  }\n\n  if(step.type === 'multiselect'){\n    let checked = false, firstTry = true, wrongAttempts = 0;\n    const feedbackEl = document.getElementById('feedback');\n    const checkBtn = document.getElementById('checkBtn');\n    const continueBtn = document.getElementById('continueBtn');\n    checkBtn.addEventListener('click', () => {\n      if(checked) return;\n      const boxes = Array.from(document.querySelectorAll('#checklistWrap input[type=checkbox]'));\n      const selected = boxes.filter(b => b.checked).map(b => b.value);\n      const correctKeys = step.items.filter(i => i.correct).map(i => i.key);\n      const isRight = selected.length === correctKeys.length &amp;&amp; selected.every(k => correctKeys.includes(k));\n      if(isRight){\n        checked = true;\n        if(firstTry) score += 3;\n        document.querySelectorAll('#checklistWrap .check-item').forEach(label => {\n          if(correctKeys.includes(label.dataset.key)){\n            label.classList.add('correct-mark');\n          }else{\n            label.classList.remove('correct-mark', 'wrong-mark');\n          }\n          label.querySelector('input').disabled = true;\n        });\n        feedbackEl.textContent = step.feedbackCorrect;\n        feedbackEl.className = 'feedback good';\n        checkBtn.style.display = 'none';\n        continueBtn.style.display = 'inline-block';\n        continueBtn.disabled = false;\n        setTimeout(() => continueBtn.scrollIntoView({behavior:'smooth', block:'center'}), 80);\n        reviewLog.push({ mission: step.mission, title: step.title, net:(firstTry?3:0)-wrongAttempts, attempts:wrongAttempts, takeaway: step.feedbackCorrect });\n      } else {\n        firstTry = false; wrongAttempts++; score -= 1;\n        feedbackEl.textContent = step.feedbackWrong;\n        feedbackEl.className = 'feedback bad';\n      }\n      updateHud(step);\n    });\n    continueBtn.addEventListener('click', goNext);\n    return;\n  }\n\n  if(step.type === 'choice'){\n    let solved = false, firstTry = true, wrongAttempts = 0;\n    const feedbackEl = document.getElementById('feedback');\n    const continueBtn = document.getElementById('continueBtn');\n    document.querySelectorAll('#optionsWrap .opt').forEach(btn => {\n      btn.addEventListener('click', () => {\n        if(solved) return;\n        const key = btn.dataset.key;\n        if(key === step.correct){\n          solved = true;\n          if(firstTry) score += 3;\n          btn.classList.add('correct');\n          feedbackEl.textContent = step.feedbackCorrect;\n          feedbackEl.className = 'feedback good';\n          continueBtn.style.display = 'inline-block';\n          document.querySelectorAll('#optionsWrap .opt').forEach(b => { if(b !== btn) b.disabled = true; });\n          reviewLog.push({ mission: step.mission, title: step.title, net:(firstTry?3:0)-wrongAttempts, attempts:wrongAttempts, takeaway: step.feedbackCorrect });\n        } else {\n          firstTry = false; wrongAttempts++; score -= 1;\n          btn.classList.add('wrong','shake');\n          btn.disabled = true;\n          setTimeout(() => btn.classList.remove('shake'), 400);\n          feedbackEl.textContent = step.feedbackWrong;\n          feedbackEl.className = 'feedback bad';\n        }\n        updateHud(step);\n      });\n    });\n    continueBtn.addEventListener('click', goNext);\n    return;\n  }\n\n  if(step.type === 'matching'){\n    const rows = Array.from(document.querySelectorAll('.match-row'));\n    const locked = rows.map(() => false);\n    const firstTry = rows.map(() => true);\n    const wrongAttempts = rows.map(() => 0);\n    const checkBtn = document.getElementById('checkBtn');\n    const continueBtn = document.getElementById('continueBtn');\n    const feedbackEl = document.getElementById('feedback');\n    checkBtn.addEventListener('click', () => {\n      let anyEmpty = false;\n      rows.forEach((row, i) => {\n        if(locked[i]) return;\n        const select = row.querySelector('select');\n        const fbSpan = row.querySelector('.match-feedback');\n        if(!select.value){ anyEmpty = true; return; }\n        if(select.value === step.pairs[i].correct){\n          locked[i] = true;\n          if(firstTry[i]) score += 3;\n          row.classList.add('correct'); row.classList.remove('wrong');\n          select.disabled = true;\n          fbSpan.textContent = '\u2713';\n        } else {\n          firstTry[i] = false; wrongAttempts[i]++; score -= 1;\n          row.classList.add('wrong');\n          fbSpan.textContent = '\u2717';\n        }\n      });\n      updateHud(step);\n      if(locked.every(Boolean)){\n        feedbackEl.textContent = 'All matched correctly.';\n        feedbackEl.className = 'feedback good';\n        checkBtn.style.display = 'none';\n        continueBtn.style.display = 'inline-block';\n        const totalNet = firstTry.reduce((sum,f,i) => sum + (f ? 3 : 0) - wrongAttempts[i], 0);\n        const totalAttempts = wrongAttempts.reduce((a,b) => a+b, 0);\n        reviewLog.push({ mission: step.mission, title: step.title, net: totalNet, attempts: totalAttempts, takeaway: step.takeaway });\n      } else if(anyEmpty){\n        feedbackEl.textContent = 'Choose an answer for every row before checking.';\n        feedbackEl.className = 'feedback bad';\n      } else {\n        feedbackEl.textContent = 'Some matches are wrong \u2014 try again.';\n        feedbackEl.className = 'feedback bad';\n      }\n    });\n    continueBtn.addEventListener('click', goNext);\n    return;\n  }\n\n  if(step.type === 'decode'){\n    let solved = false, firstTry = true, wrongAttempts = 0;\n    const feedbackEl = document.getElementById('feedback');\n    const checkBtn = document.getElementById('checkBtn');\n    const continueBtn = document.getElementById('continueBtn');\n    const input = document.getElementById('decodeInput');\n    document.getElementById('hintBtn').addEventListener('click', () => {\n      feedbackEl.textContent = step.hint;\n      feedbackEl.className = 'feedback';\n      feedbackEl.style.color = 'var(--muted)';\n    });\n    checkBtn.addEventListener('click', () => {\n      if(solved) return;\n      const val = (input.value || '').trim().toUpperCase();\n      if(val === step.answer){\n        solved = true;\n        if(firstTry) score += 3;\n        feedbackEl.style.color = '';\n        feedbackEl.textContent = step.feedbackCorrect;\n        feedbackEl.className = 'feedback good';\n        input.disabled = true;\n        checkBtn.style.display = 'none';\n        continueBtn.style.display = 'inline-block';\n        reviewLog.push({ mission: step.mission, title: step.title, net:(firstTry?3:0)-wrongAttempts, attempts:wrongAttempts, takeaway: step.feedbackCorrect });\n      } else {\n        firstTry = false; wrongAttempts++; score -= 1;\n        feedbackEl.style.color = '';\n        feedbackEl.textContent = step.feedbackWrong;\n        feedbackEl.className = 'feedback bad';\n      }\n      updateHud(step);\n    });\n    continueBtn.addEventListener('click', goNext);\n    return;\n  }\n\n  if(step.type === 'flavor'){\n    let picked = false;\n    const feedbackEl = document.getElementById('feedback');\n    const continueBtn = document.getElementById('continueBtn');\n    document.querySelectorAll('#optionsWrap .opt').forEach(btn => {\n      btn.addEventListener('click', () => {\n        if(picked) return;\n        picked = true;\n        const opt = step.options.find(o => o.key === btn.dataset.key);\n        btn.classList.add('flavor-picked');\n        document.querySelectorAll('#optionsWrap .opt').forEach(b => { if(b !== btn) b.disabled = true; });\n        feedbackEl.textContent = opt.reaction;\n        feedbackEl.className = 'feedback';\n        continueBtn.style.display = 'inline-block';\n      });\n    });\n    continueBtn.addEventListener('click', goNext);\n    return;\n  }\n\n  if(step.type === 'vote'){\n    document.querySelectorAll('#optionsWrap .opt').forEach(btn => {\n      btn.addEventListener('click', () => {\n        document.querySelectorAll('#optionsWrap .opt').forEach(b => b.disabled = true);\n        const opt = step.options.find(o => o.key === btn.dataset.key);\n        renderEnding(opt.ending);\n      });\n    });\n  }\n}\n\n\nfunction currentFullscreenElement(){\n  return document.fullscreenElement || document.webkitFullscreenElement || null;\n}\nasync function toggleGameFullscreen(){\n  const root = document.documentElement;\n  try{\n    if(currentFullscreenElement()){\n      if(document.exitFullscreen) await document.exitFullscreen();\n      else if(document.webkitExitFullscreen) document.webkitExitFullscreen();\n    }else{\n      if(root.requestFullscreen) await root.requestFullscreen({navigationUI:'hide'});\n      else if(root.webkitRequestFullscreen) root.webkitRequestFullscreen();\n    }\n  }catch(e){}\n}\nfunction syncFullscreenButton(){\n  const btn = document.getElementById('fullscreenBtn');\n  if(btn) btn.textContent = currentFullscreenElement() ? '\u26f6 Exit full screen' : '\u26f6 Full screen';\n}\ndocument.getElementById('fullscreenBtn').addEventListener('click', toggleGameFullscreen);\ndocument.addEventListener('fullscreenchange', syncFullscreenButton);\ndocument.addEventListener('webkitfullscreenchange', syncFullscreenButton);\n\nrenderStep();\n\n<\/script>\n<\/body>\n<\/html>\n\u00a0\u00bb loading=\u00a0\u00bblazy\u00a0\u00bb><\/iframe><\/div>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-1301","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/pcwallis.malo.wf\/index.php\/wp-json\/wp\/v2\/pages\/1301","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/pcwallis.malo.wf\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/pcwallis.malo.wf\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/pcwallis.malo.wf\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/pcwallis.malo.wf\/index.php\/wp-json\/wp\/v2\/comments?post=1301"}],"version-history":[{"count":6,"href":"https:\/\/pcwallis.malo.wf\/index.php\/wp-json\/wp\/v2\/pages\/1301\/revisions"}],"predecessor-version":[{"id":1332,"href":"https:\/\/pcwallis.malo.wf\/index.php\/wp-json\/wp\/v2\/pages\/1301\/revisions\/1332"}],"wp:attachment":[{"href":"https:\/\/pcwallis.malo.wf\/index.php\/wp-json\/wp\/v2\/media?parent=1301"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}