{"id":762,"date":"2026-03-04T14:57:43","date_gmt":"2026-03-04T14:57:43","guid":{"rendered":"https:\/\/i-cte.org\/robot\/?p=762"},"modified":"2026-03-04T15:17:28","modified_gmt":"2026-03-04T15:17:28","slug":"literature-search-strategy-with-ai","status":"publish","type":"post","link":"https:\/\/i-cte.org\/robot\/literature-search-strategy-with-ai\/","title":{"rendered":"Literature Search Strategy with AI"},"content":{"rendered":"\n<!-- \u2705 ICTE Teacher Micro-Lesson \u2014 Literature Search Strategy with AI\n     WP-safe single block, Multi-speaker Google Voices (2 voices), Conversation + Reading + Quiz + Toolkit + Prompts + Listening + Lab\n     Includes Problem-Solving finale: build & refine a real search string (Scopus\/Google Scholar), Boolean logic, synonyms, inclusion\/exclusion terms.\n-->\n<div id=\"icte-litsearchai\">\n\n  <!-- \u2705 TOP MENU -->\n  <nav class=\"icte-menu\" aria-label=\"AI literature search lesson navigation\">\n    <a href=\"#\" class=\"is-current\" data-view=\"overview\">Overview<\/a>\n    <a href=\"#\" data-view=\"conversation\">Conversation<\/a>\n    <a href=\"#\" data-view=\"reading\">Reading<\/a>\n    <a href=\"#\" data-view=\"toolkit\">Search Toolkit<\/a>\n    <a href=\"#\" data-view=\"prompts\">Prompts<\/a>\n    <a href=\"#\" data-view=\"listening\">Listening<\/a>\n    <a href=\"#\" data-view=\"lab\">Query Builder Lab<\/a>\n    <a href=\"#\" data-view=\"problem\">Problem-solving<\/a>\n    <a href=\"#\" data-view=\"progress\">Progress<\/a>\n  <\/nav>\n\n  <section class=\"icte-shell\" aria-label=\"ICTE AI literature search strategy lesson\">\n\n    <!-- \u2705 Header -->\n    <header class=\"icte-hero\">\n      <div class=\"icte-hero__text\">\n        <div class=\"hero-top\">\n          <!-- Optional banner (replace src if you have a specific image for this lesson) -->\n          <img decoding=\"async\"\n            class=\"hero-img\"\n            src=\"https:\/\/i-cte.org\/robot\/wp-content\/uploads\/2026\/03\/Screenshot-2026-03-04-at-21.54.19.png\"\n            alt=\"Literature search strategy with AI banner\"\n            loading=\"lazy\"\n          \/>\n          <div class=\"hero-title\">\n            <h2>Literature Search Strategy with AI<\/h2>\n            <p class=\"muted\">\n              Design powerful queries using <b>Boolean logic<\/b>, <b>keyword expansion<\/b>, and <b>inclusion\/exclusion terms<\/b>.\n              Build search strings for <b>Google Scholar<\/b> and <b>Scopus<\/b>, then refine based on results.\n              Includes <b>conversation<\/b>, <b>reading<\/b>, <b>quiz<\/b>, <b>prompt examples<\/b>, a <b>two-speaker dialogue<\/b>, a <b>query builder lab<\/b>,\n              and an end-of-lesson <b>problem-solving task<\/b>.\n            <\/p>\n          <\/div>\n        <\/div>\n      <\/div>\n\n      <div class=\"icte-hero__controls\">\n        <div class=\"icte-pill\">\n          <span class=\"dot\" id=\"icteMicDot\" aria-hidden=\"true\"><\/span>\n          <span id=\"icteMicStatus\">Mic: Off<\/span>\n        <\/div>\n\n        <div class=\"icte-row\">\n          <button class=\"btn\" id=\"icteStartVoice\" type=\"button\">Start Voice<\/button>\n          <button class=\"btn ghost\" id=\"icteStopVoice\" type=\"button\">Stop<\/button>\n        <\/div>\n\n        <!-- \u2705 Multi-speaker Google Voices -->\n        <div class=\"grid2\" style=\"margin-top:10px;\">\n          <label class=\"icte-label\">\n            Speaker A (Google)\n            <select id=\"voiceA\" class=\"icte-select\" aria-label=\"Speaker A voice\"><\/select>\n          <\/label>\n\n          <label class=\"icte-label\">\n            Speaker B (Google)\n            <select id=\"voiceB\" class=\"icte-select\" aria-label=\"Speaker B voice\"><\/select>\n          <\/label>\n        <\/div>\n\n        <div class=\"icte-row\" style=\"margin-top:10px;\">\n          <button class=\"btn mini ghost\" type=\"button\" id=\"speakActive\">\ud83d\udd0a Read this page<\/button>\n          <button class=\"btn mini ghost\" type=\"button\" id=\"stopSpeak\">\u23f9 Stop audio<\/button>\n        <\/div>\n\n        <div class=\"icte-small muted\">\n          Tip: For best voice options, use Chrome\/Edge. If voices don\u2019t appear yet, click once on the page and wait 2\u20133 seconds.\n        <\/div>\n      <\/div>\n    <\/header>\n\n    <!-- \u2705 Views -->\n    <main class=\"icte-main\">\n\n      <!-- ===================== -->\n      <!-- \u2705 OVERVIEW -->\n      <!-- ===================== -->\n      <section class=\"view is-active\" data-view=\"overview\" aria-label=\"Lesson overview\">\n        <div class=\"card\">\n          <div class=\"card-h\">\n            <h3>1) Outcomes<\/h3>\n            <div class=\"card-actions\">\n              <button class=\"btn mini\" type=\"button\" data-say=\"overview-instr\">\ud83d\udd0a Read instructions<\/button>\n            <\/div>\n          <\/div>\n\n          <div class=\"grid2\">\n            <div class=\"qitem\">\n              <div class=\"qtext\">By the end, you can\u2026<\/div>\n              <ul class=\"ul\">\n                <li>Turn a topic into a <b>search concept model<\/b> (Concept 1 AND Concept 2 AND context).<\/li>\n                <li>Expand keywords with <b>synonyms<\/b>, <b>variants<\/b>, and <b>related terms<\/b>.<\/li>\n                <li>Write Boolean strings for <b>Google Scholar<\/b> and <b>Scopus<\/b> (TITLE-ABS-KEY).<\/li>\n                <li>Use <b>inclusion\/exclusion<\/b> (NOT) terms safely.<\/li>\n                <li>Refine queries based on noisy or narrow results.<\/li>\n              <\/ul>\n            <\/div>\n\n            <div class=\"qitem\">\n              <div class=\"qtext\">AI safety rules<\/div>\n              <ul class=\"ul\">\n                <li>AI suggests keywords; <b>databases decide<\/b> what exists.<\/li>\n                <li>Don\u2019t trust AI to \u201cknow\u201d indexed coverage; verify by searching.<\/li>\n                <li>Keep a search log: query, date, database, results count, adjustments.<\/li>\n                <li>Avoid excluding too aggressively early (NOT can remove relevant work).<\/li>\n              <\/ul>\n            <\/div>\n          <\/div>\n\n          <div class=\"note\">\n            <b>Workflow:<\/b> Define concepts \u2192 expand keywords \u2192 build Boolean string \u2192 run search \u2192 inspect top 20 results \u2192\n            refine (add phrases, limit fields, add NOT terms, use date\/type filters).\n          <\/div>\n        <\/div>\n      <\/section>\n\n      <!-- ===================== -->\n      <!-- \u2705 CONVERSATION -->\n      <!-- ===================== -->\n      <section class=\"view\" data-view=\"conversation\" aria-label=\"Conversation coach\">\n        <div class=\"card\">\n          <div class=\"card-h\">\n            <h3>2) Conversation (Search Strategy Coach)<\/h3>\n            <div class=\"card-actions\">\n              <button class=\"btn mini\" type=\"button\" data-say=\"conv-instr\">\ud83d\udd0a Read instructions<\/button>\n              <button class=\"btn mini ghost\" type=\"button\" id=\"convHear\">\ud83d\udd0a Read last question<\/button>\n              <button class=\"btn mini ghost\" type=\"button\" id=\"convReset\">Reset<\/button>\n            <\/div>\n          <\/div>\n\n          <p class=\"muted\">\n            Build a high-quality search string step-by-step: <b>concepts<\/b> \u2192 <b>synonyms<\/b> \u2192 <b>Boolean<\/b> \u2192\n            <b>filters<\/b> \u2192 <b>exclusions<\/b>.\n          <\/p>\n\n          <div class=\"chat\" id=\"convChat\" aria-live=\"polite\" aria-label=\"Conversation chat log\"><\/div>\n\n          <div class=\"chatbar\">\n            <input id=\"convText\" class=\"input\" type=\"text\" placeholder=\"Type your answer (or use voice)...\" autocomplete=\"off\" \/>\n            <button class=\"btn\" id=\"convSend\" type=\"button\">Send<\/button>\n          <\/div>\n\n          <div class=\"note\">\n            <b>Tip:<\/b> Start broad with AND\/OR groups. Add NOT only after you inspect irrelevant clusters in results.\n          <\/div>\n        <\/div>\n      <\/section>\n\n      <!-- ===================== -->\n      <!-- \u2705 READING + QUIZ -->\n      <!-- ===================== -->\n      <section class=\"view\" data-view=\"reading\" aria-label=\"Reading and quiz\">\n        <div class=\"card\">\n          <div class=\"card-h\">\n            <h3>3) Reading + Comprehension Quiz<\/h3>\n            <div class=\"card-actions\">\n              <button class=\"btn mini\" type=\"button\" data-say=\"reading-instr\">\ud83d\udd0a Read instructions<\/button>\n              <button class=\"btn mini ghost\" type=\"button\" id=\"readTextBtn\">\ud83d\udd0a Read the text<\/button>\n              <button class=\"btn mini ghost\" type=\"button\" id=\"quizCheck\">Check answers<\/button>\n            <\/div>\n          <\/div>\n\n          <article class=\"reading\" id=\"readingText\" aria-label=\"Reading text about AI-assisted literature search\">\n            <div class=\"reading-title\">Reading: Query design + keyword expansion (with AI support)<\/div>\n\n            <div class=\"reading-p\">\n              <b>1<\/b> A literature search begins with clear concepts. Instead of searching with one long sentence, researchers break the topic into\n              two to four concepts and connect them using <b>AND<\/b>. For each concept, they create a group of synonyms connected by <b>OR<\/b>.\n              This approach improves recall while keeping the topic focused.\n            <\/div>\n\n            <div class=\"reading-p\">\n              <b>2<\/b> AI can help expand keywords by suggesting synonyms, related phrases, spelling variants, acronyms, and common terminology used in a field.\n              However, keyword lists must be checked against real results. If a term yields irrelevant items, it should be removed or used only in a specific field\n              such as title or abstract.\n            <\/div>\n\n            <div class=\"reading-p\">\n              <b>3<\/b> Boolean logic is essential. Parentheses control grouping, quotation marks support phrase searching, and truncation can capture variants (e.g., learn*).\n              Databases differ in syntax. Scopus often supports field searching (e.g., TITLE-ABS-KEY), while Google Scholar is less precise but good for broad discovery.\n            <\/div>\n\n            <div class=\"reading-p\">\n              <b>4<\/b> Inclusion and exclusion terms should be used strategically. Early in a search, exclusions can remove relevant studies by mistake.\n              A safer method is to run a broad search, inspect irrelevant clusters, then add <b>NOT<\/b> terms to remove those clusters. Document each change so your search\n              is transparent and repeatable.\n            <\/div>\n\n            <div class=\"reading-p\">\n              <b>5<\/b> A high-quality search strategy is iterative. Researchers adjust keywords, add phrase forms, restrict to fields, and apply filters (date, document type,\n              language). The goal is not \u201cone perfect query,\u201d but a set of well-documented queries that balance breadth (recall) and focus (precision).\n            <\/div>\n          <\/article>\n\n          <h4 class=\"h4\" style=\"margin-top:12px;\">Comprehension check (choose the best answer)<\/h4>\n          <div id=\"quiz\" class=\"stack\"><\/div>\n          <div class=\"feedback\" id=\"quizFb\" aria-live=\"polite\"><\/div>\n        <\/div>\n      <\/section>\n\n      <!-- ===================== -->\n      <!-- \u2705 TOOLKIT -->\n      <!-- ===================== -->\n      <section class=\"view\" data-view=\"toolkit\" aria-label=\"Search toolkit\">\n        <div class=\"card\">\n          <div class=\"card-h\">\n            <h3>4) Search Toolkit (Boolean, synonyms, inclusion\/exclusion)<\/h3>\n            <div class=\"card-actions\">\n              <button class=\"btn mini\" type=\"button\" data-say=\"toolkit-instr\">\ud83d\udd0a Read instructions<\/button>\n              <button class=\"btn mini ghost\" type=\"button\" id=\"toolkitSpeak\">\ud83d\udd0a Read toolkit<\/button>\n            <\/div>\n          <\/div>\n\n          <div class=\"grid2\">\n            <div class=\"qitem\">\n              <div class=\"qtext\">Concept model (2\u20134 blocks)<\/div>\n              <pre class=\"pre\" id=\"tkConcept\"><\/pre>\n            <\/div>\n\n            <div class=\"qitem\">\n              <div class=\"qtext\">Keyword expansion checklist<\/div>\n              <pre class=\"pre\" id=\"tkExpand\"><\/pre>\n            <\/div>\n          <\/div>\n\n          <div class=\"grid2\" style=\"margin-top:12px;\">\n            <div class=\"qitem\">\n              <div class=\"qtext\">Boolean patterns (cheat sheet)<\/div>\n              <pre class=\"pre\" id=\"tkBoolean\"><\/pre>\n            <\/div>\n\n            <div class=\"qitem\">\n              <div class=\"qtext\">Inclusion\/exclusion strategy<\/div>\n              <pre class=\"pre\" id=\"tkExcl\"><\/pre>\n            <\/div>\n          <\/div>\n\n          <div class=\"qitem\" style=\"margin-top:12px;\">\n            <div class=\"qtext\">Example strings (Google Scholar vs Scopus)<\/div>\n            <pre class=\"pre\" id=\"tkExamples\"><\/pre>\n          <\/div>\n        <\/div>\n      <\/section>\n\n      <!-- ===================== -->\n      <!-- \u2705 PROMPTS -->\n      <!-- ===================== -->\n      <section class=\"view\" data-view=\"prompts\" aria-label=\"Prompts and examples\">\n        <div class=\"card\">\n          <div class=\"card-h\">\n            <h3>5) Prompts + Examples (Copy &#038; Adapt)<\/h3>\n            <div class=\"card-actions\">\n              <button class=\"btn mini\" type=\"button\" data-say=\"prompts-instr\">\ud83d\udd0a Read instructions<\/button>\n              <button class=\"btn mini ghost\" type=\"button\" id=\"promptsSpeak\">\ud83d\udd0a Read prompts<\/button>\n              <button class=\"btn mini ghost\" type=\"button\" id=\"copyPrompts\">Copy all<\/button>\n            <\/div>\n          <\/div>\n\n          <div class=\"note\">\n            Use AI to propose synonyms and query variations, then validate by running searches and checking relevance.\n          <\/div>\n\n          <div class=\"grid2\">\n            <div class=\"qitem\">\n              <div class=\"qtext\">Prompt 1 \u2014 Concept blocks + synonyms<\/div>\n              <pre class=\"pre\" id=\"p1\"><\/pre>\n            <\/div>\n            <div class=\"qitem\">\n              <div class=\"qtext\">Prompt 2 \u2014 Scopus string (TITLE-ABS-KEY)<\/div>\n              <pre class=\"pre\" id=\"p2\"><\/pre>\n            <\/div>\n          <\/div>\n\n          <div class=\"grid2\" style=\"margin-top:12px;\">\n            <div class=\"qitem\">\n              <div class=\"qtext\">Prompt 3 \u2014 Google Scholar strategy (broad + refine)<\/div>\n              <pre class=\"pre\" id=\"p3\"><\/pre>\n            <\/div>\n            <div class=\"qitem\">\n              <div class=\"qtext\">Prompt 4 \u2014 Inclusion\/exclusion terms (safe NOT)<\/div>\n              <pre class=\"pre\" id=\"p4\"><\/pre>\n            <\/div>\n          <\/div>\n\n          <div class=\"qitem\" style=\"margin-top:12px;\">\n            <div class=\"qtext\">Mini example (topic \u2192 blocks \u2192 final string)<\/div>\n            <pre class=\"pre\" id=\"pExample\"><\/pre>\n          <\/div>\n        <\/div>\n      <\/section>\n\n      <!-- ===================== -->\n      <!-- \u2705 LISTENING -->\n      <!-- ===================== -->\n      <section class=\"view\" data-view=\"listening\" aria-label=\"Two-speaker listening\">\n        <div class=\"card\">\n          <div class=\"card-h\">\n            <h3>6) Listening (Two Google Voices) \u2014 \u201cBroad first, refine later\u201d<\/h3>\n            <div class=\"card-actions\">\n              <button class=\"btn mini\" type=\"button\" data-say=\"list-instr\">\ud83d\udd0a Read instructions<\/button>\n              <button class=\"btn mini\" type=\"button\" id=\"listenPlay\">\u25b6\ufe0f Play dialogue<\/button>\n              <button class=\"btn mini ghost\" type=\"button\" id=\"listenStop\">\u23f9 Stop<\/button>\n              <button class=\"btn mini ghost\" type=\"button\" id=\"listenCheck\">Check answers<\/button>\n            <\/div>\n          <\/div>\n\n          <p class=\"muted\">\n            Listen to two researchers discussing keyword expansion, Boolean logic, and safe exclusion terms.\n          <\/p>\n\n          <div id=\"listenQ\" class=\"stack\"><\/div>\n          <div class=\"feedback\" id=\"listenFb\" aria-live=\"polite\"><\/div>\n        <\/div>\n      <\/section>\n\n      <!-- ===================== -->\n      <!-- \u2705 LAB -->\n      <!-- ===================== -->\n      <section class=\"view\" data-view=\"lab\" aria-label=\"Query builder lab\">\n        <div class=\"card\">\n          <div class=\"card-h\">\n            <h3>7) Query Builder Lab (No-code builder + export)<\/h3>\n            <div class=\"card-actions\">\n              <button class=\"btn mini\" type=\"button\" data-say=\"lab-instr\">\ud83d\udd0a Read instructions<\/button>\n              <button class=\"btn mini ghost\" type=\"button\" id=\"labBuild\">Build strings<\/button>\n              <button class=\"btn mini ghost\" type=\"button\" id=\"labCopy\">Copy strings<\/button>\n              <button class=\"btn mini ghost\" type=\"button\" id=\"labScore\">Self-check<\/button>\n            <\/div>\n          <\/div>\n\n          <div class=\"note\">\n            Enter your topic as concept blocks. The builder creates (1) a Google Scholar string and (2) a Scopus TITLE-ABS-KEY string.\n            Then you can copy a <b>Safe AI prompt<\/b> to generate refinements (phrases, synonyms, exclusions) based on real results.\n          <\/div>\n\n          <div class=\"grid2\">\n            <div class=\"qitem\">\n              <div class=\"qtext\">A) Research topic<\/div>\n              <textarea class=\"textarea\" id=\"labTopic\" rows=\"4\" placeholder=\"Example: AI chatbot use AND EFL speaking skills AND higher education\"><\/textarea>\n\n              <div class=\"qtext\" style=\"margin-top:10px;\">B) Concept blocks (one per box)<\/div>\n              <label class=\"icte-label\">Concept 1 (main phenomenon)<\/label>\n              <textarea class=\"textarea\" id=\"c1\" rows=\"3\" placeholder=\"Example: chatbot OR conversational agent OR AI tutor\"><\/textarea>\n\n              <label class=\"icte-label\">Concept 2 (population\/context)<\/label>\n              <textarea class=\"textarea\" id=\"c2\" rows=\"3\" placeholder=\"Example: EFL OR ESL OR second language OR English language learners\"><\/textarea>\n\n              <label class=\"icte-label\">Concept 3 (outcome)<\/label>\n              <textarea class=\"textarea\" id=\"c3\" rows=\"3\" placeholder=\"Example: speaking skill* OR oral proficiency OR speaking performance\"><\/textarea>\n\n              <label class=\"icte-label\">Concept 4 (optional: setting)<\/label>\n              <textarea class=\"textarea\" id=\"c4\" rows=\"3\" placeholder=\"Example: university OR higher education OR tertiary\"><\/textarea>\n            <\/div>\n\n            <div class=\"qitem\">\n              <div class=\"qtext\">C) Refinement tools<\/div>\n\n              <label class=\"icte-label\">Phrase terms (quoted)<\/label>\n              <textarea class=\"textarea\" id=\"phrases\" rows=\"4\" placeholder='Example:\n\"oral proficiency\"\n\"speaking performance\"\n\"conversational agent\"'><\/textarea>\n\n              <label class=\"icte-label\">Exclusion terms (NOT) \u2014 use after checking results<\/label>\n              <textarea class=\"textarea\" id=\"nots\" rows=\"4\" placeholder=\"Example:\nmedical OR diagnosis OR animal\"><\/textarea>\n\n              <label class=\"icte-label\">Filters (log only \u2014 apply in database UI)<\/label>\n              <div class=\"stack\" id=\"labFilters\"><\/div>\n            <\/div>\n          <\/div>\n\n          <div class=\"qitem\" style=\"margin-top:12px;\">\n            <div class=\"qtext\">Generated search strings<\/div>\n            <div class=\"grid2\">\n              <div>\n                <div class=\"icte-label\">Google Scholar (broad)<\/div>\n                <pre class=\"pre\" id=\"outScholar\"><\/pre>\n              <\/div>\n              <div>\n                <div class=\"icte-label\">Scopus (TITLE-ABS-KEY)<\/div>\n                <pre class=\"pre\" id=\"outScopus\"><\/pre>\n              <\/div>\n            <\/div>\n          <\/div>\n\n          <div class=\"qitem\" style=\"margin-top:12px;\">\n            <div class=\"qtext\">Safe AI prompt (refine keywords from real results)<\/div>\n            <pre class=\"pre\" id=\"outPrompt\"><\/pre>\n          <\/div>\n\n          <div class=\"feedback\" id=\"labFb\" aria-live=\"polite\"><\/div>\n        <\/div>\n      <\/section>\n\n      <!-- ===================== -->\n      <!-- \u2705 PROBLEM-SOLVING (REQUIRED) -->\n      <!-- ===================== -->\n      <section class=\"view\" data-view=\"problem\" aria-label=\"Problem solving task\">\n        <div class=\"card\">\n          <div class=\"card-h\">\n            <h3>8) Problem-solving (End-of-lesson task)<\/h3>\n            <div class=\"card-actions\">\n              <button class=\"btn mini\" type=\"button\" data-say=\"problem-instr\">\ud83d\udd0a Read instructions<\/button>\n              <button class=\"btn mini ghost\" type=\"button\" id=\"psCheck\">Check my solution<\/button>\n              <button class=\"btn mini ghost\" type=\"button\" id=\"psReset\">Reset<\/button>\n            <\/div>\n          <\/div>\n\n          <div class=\"note\">\n            <b>Scenario:<\/b> You ran a Google Scholar search and got 42,000 results, many unrelated to education (e.g., medical chatbots).\n            Then you tried Scopus and got only 12 results (too narrow).\n            <br><br>\n            Your task: write a <b>refinement plan<\/b> and produce <b>two improved queries<\/b>:\n            <ul class=\"ul\">\n              <li>Query A (Scholar): reduce noise using phrases + a careful NOT list.<\/li>\n              <li>Query B (Scopus): increase recall using synonym expansion and fewer restrictions.<\/li>\n            <\/ul>\n            Also: list <b>3 inclusion criteria<\/b> and <b>3 exclusion criteria<\/b> you would apply during screening.\n          <\/div>\n\n          <div class=\"grid2\">\n            <div class=\"qitem\">\n              <div class=\"qtext\">A) Refinement plan (4\u20138 bullets)<\/div>\n              <textarea class=\"textarea\" id=\"psPlan\" rows=\"8\" placeholder=\"- Identify irrelevant cluster (medical chatbots)...&#10;- Add phrases like &quot;oral proficiency&quot;...&#10;- Add NOT terms (medical OR diagnosis)...&#10;- In Scopus, remove setting concept or expand synonyms...\"><\/textarea>\n            <\/div>\n\n            <div class=\"qitem\">\n              <div class=\"qtext\">B) Your improved queries<\/div>\n              <textarea class=\"textarea\" id=\"psQueries\" rows=\"8\" placeholder=\"Query A (Scholar): ...&#10;&#10;Query B (Scopus TITLE-ABS-KEY): ...\"><\/textarea>\n            <\/div>\n          <\/div>\n\n          <div class=\"grid2\" style=\"margin-top:12px;\">\n            <div class=\"qitem\">\n              <div class=\"qtext\">C) Inclusion criteria (3 bullets)<\/div>\n              <textarea class=\"textarea\" id=\"psIncl\" rows=\"6\" placeholder=\"- Studies about EFL\/ESL learners...&#10;- Focus on speaking outcomes...&#10;- Empirical studies (quant\/qual\/mixed)...\"><\/textarea>\n            <\/div>\n\n            <div class=\"qitem\">\n              <div class=\"qtext\">D) Exclusion criteria (3 bullets)<\/div>\n              <textarea class=\"textarea\" id=\"psExcl\" rows=\"6\" placeholder=\"- Medical\/clinical chatbot studies...&#10;- Not about language learning...&#10;- Non-empirical opinion pieces (if your review excludes them)...\"><\/textarea>\n            <\/div>\n          <\/div>\n\n          <div class=\"feedback\" id=\"psFb\" aria-live=\"polite\"><\/div>\n        <\/div>\n      <\/section>\n\n      <!-- ===================== -->\n      <!-- \u2705 PROGRESS -->\n      <!-- ===================== -->\n      <section class=\"view\" data-view=\"progress\" aria-label=\"Progress tracking\">\n        <div class=\"card\">\n          <div class=\"card-h\">\n            <h3>Progress<\/h3>\n            <div class=\"card-actions\">\n              <button class=\"btn mini ghost\" type=\"button\" id=\"progressReset\">Clear progress<\/button>\n            <\/div>\n          <\/div>\n\n          <div class=\"progress-grid\">\n            <div class=\"pbox\">\n              <div class=\"pnum\" id=\"pDone\">0<\/div>\n              <div class=\"muted\">Activities completed<\/div>\n            <\/div>\n            <div class=\"pbox\">\n              <div class=\"pnum\" id=\"pScore\">0%<\/div>\n              <div class=\"muted\">Average score<\/div>\n            <\/div>\n            <div class=\"pbox\">\n              <div class=\"pnum\" id=\"pChecks\">0<\/div>\n              <div class=\"muted\">Checklist items used<\/div>\n            <\/div>\n          <\/div>\n\n          <div class=\"note\">Saved locally in your browser (local storage).<\/div>\n\n          <h4 class=\"h4\">Checklist bank<\/h4>\n          <div class=\"bank\" id=\"checksBank\"><\/div>\n        <\/div>\n      <\/section>\n\n    <\/main>\n  <\/section>\n\n  <style>\n    \/* ===== WP-SAFE STYLES (scoped) ===== *\/\n    #icte-litsearchai *{ box-sizing:border-box; }\n    #icte-litsearchai{\n      --green:#28a745;\n      --dark:#132018;\n      --card:#ffffff;\n      --muted:#6b7280;\n      --line:#e5e7eb;\n      --shadow:0 8px 24px rgba(17,24,39,.08);\n      --radius:16px;\n      font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;\n      color:#111827;\n    }\n    #icte-litsearchai .icte-menu{\n      width:100%;\n      background:var(--green);\n      padding:10px 12px;\n      text-align:center;\n      overflow-x:auto;\n      white-space:nowrap;\n      position:sticky;\n      top:0;\n      z-index:999;\n      box-shadow:0 2px 10px rgba(0,0,0,.12);\n      border-radius:12px;\n      margin-bottom:12px;\n    }\n    #icte-litsearchai .icte-menu a{\n      display:inline-block;\n      color:#fff;\n      text-decoration:none;\n      font-weight:900;\n      padding:8px 10px;\n      border-radius:999px;\n      margin:0 3px;\n      opacity:.92;\n      transition:.15s;\n    }\n    #icte-litsearchai .icte-menu a:hover{ opacity:1; background:rgba(255,255,255,.14); }\n    #icte-litsearchai .icte-menu a.is-current{ background:#fff; color:var(--dark); opacity:1; }\n\n    #icte-litsearchai .icte-shell{ max-width:1100px; margin:0 auto; }\n    #icte-litsearchai .icte-hero{\n      display:grid;\n      grid-template-columns: 1.2fr .8fr;\n      gap:14px;\n      background:linear-gradient(135deg,#e9fff0, #ffffff);\n      border:1px solid var(--line);\n      border-radius:var(--radius);\n      padding:16px;\n      box-shadow:var(--shadow);\n      margin-bottom:14px;\n    }\n    @media (max-width: 920px){ #icte-litsearchai .icte-hero{ grid-template-columns:1fr; } }\n    #icte-litsearchai h2{ margin:0 0 6px 0; font-size:22px; }\n    #icte-litsearchai .muted{ color:var(--muted); }\n\n    #icte-litsearchai .hero-top{ display:flex; gap:12px; align-items:center; }\n    #icte-litsearchai .hero-img{\n      width:140px;\n      height:auto;\n      border-radius:14px;\n      border:1px solid var(--line);\n      background:#111;\n      box-shadow:var(--shadow);\n    }\n    @media (max-width: 920px){\n      #icte-litsearchai .hero-top{ flex-direction:column; align-items:flex-start; }\n      #icte-litsearchai .hero-img{ width:100%; max-width:520px; }\n    }\n\n    #icte-litsearchai .icte-hero__controls{\n      border-left:1px dashed var(--line);\n      padding-left:14px;\n    }\n    @media (max-width: 920px){\n      #icte-litsearchai .icte-hero__controls{ border-left:none; padding-left:0; border-top:1px dashed var(--line); padding-top:12px; }\n    }\n\n    #icte-litsearchai .icte-row{ display:flex; gap:8px; flex-wrap:wrap; }\n    #icte-litsearchai .icte-label{ display:block; font-size:12px; font-weight:900; margin-top:8px; }\n    #icte-litsearchai .icte-select{\n      width:100%;\n      padding:10px 10px;\n      border:1px solid var(--line);\n      border-radius:12px;\n      background:#fff;\n      outline:none;\n      margin-top:6px;\n    }\n\n    #icte-litsearchai .icte-pill{\n      display:flex; align-items:center; gap:8px;\n      padding:10px 12px;\n      border:1px solid var(--line);\n      border-radius:999px;\n      background:#fff;\n      margin-bottom:10px;\n      font-weight:900;\n    }\n    #icte-litsearchai .dot{\n      width:10px; height:10px; border-radius:50%;\n      background:#9ca3af;\n      box-shadow:0 0 0 4px rgba(156,163,175,.18);\n    }\n    #icte-litsearchai .dot.on{\n      background:#22c55e;\n      box-shadow:0 0 0 4px rgba(34,197,94,.18);\n    }\n\n    #icte-litsearchai .btn{\n      border:none;\n      padding:10px 12px;\n      border-radius:12px;\n      background:var(--green);\n      color:#fff;\n      font-weight:900;\n      cursor:pointer;\n      transition:.15s;\n    }\n    #icte-litsearchai .btn:hover{ filter:brightness(.95); transform:translateY(-1px); }\n    #icte-litsearchai .btn:active{ transform:translateY(0); }\n    #icte-litsearchai .btn.ghost{\n      background:#fff;\n      color:#111827;\n      border:1px solid var(--line);\n    }\n    #icte-litsearchai .btn.mini{ padding:8px 10px; border-radius:10px; font-size:13px; }\n    #icte-litsearchai .icte-small{ font-size:12px; }\n\n    #icte-litsearchai .card{\n      background:var(--card);\n      border:1px solid var(--line);\n      border-radius:var(--radius);\n      padding:14px;\n      box-shadow:var(--shadow);\n      margin-bottom:14px;\n    }\n    #icte-litsearchai .card-h{\n      display:flex; gap:10px; align-items:flex-start; justify-content:space-between;\n      border-bottom:1px solid var(--line);\n      padding-bottom:10px;\n      margin-bottom:10px;\n    }\n    #icte-litsearchai .card-h h3{ margin:0; font-size:18px; }\n    #icte-litsearchai .card-actions{ display:flex; gap:8px; flex-wrap:wrap; justify-content:flex-end; }\n\n    #icte-litsearchai .view{ display:none; }\n    #icte-litsearchai .view.is-active{ display:block; }\n\n    #icte-litsearchai .grid2{\n      display:grid;\n      grid-template-columns:1fr 1fr;\n      gap:14px;\n      margin-top:10px;\n    }\n    @media (max-width: 920px){ #icte-litsearchai .grid2{ grid-template-columns:1fr; } }\n\n    #icte-litsearchai .stack{ display:flex; flex-direction:column; gap:10px; }\n    #icte-litsearchai .h4{ margin:0 0 6px 0; font-size:15px; }\n    #icte-litsearchai .qitem{\n      padding:10px;\n      border:1px solid var(--line);\n      border-radius:14px;\n      background:#fafafa;\n    }\n    #icte-litsearchai .qtext{ font-weight:900; margin-bottom:8px; }\n\n    #icte-litsearchai .feedback{\n      margin-top:10px;\n      padding:10px 12px;\n      border-radius:14px;\n      border:1px solid var(--line);\n      background:#f9fafb;\n      font-weight:800;\n      display:none;\n      white-space:pre-line;\n    }\n    #icte-litsearchai .feedback.ok{ display:block; border-color:rgba(34,197,94,.35); background:#ecfdf5; }\n    #icte-litsearchai .feedback.bad{ display:block; border-color:rgba(239,68,68,.35); background:#fef2f2; }\n\n    #icte-litsearchai .note{\n      background:#eff6ff;\n      border:1px solid rgba(59,130,246,.22);\n      padding:10px 12px;\n      border-radius:14px;\n      margin:10px 0;\n    }\n\n    #icte-litsearchai .reading{\n      border:1px solid var(--line);\n      border-radius:14px;\n      padding:12px;\n      background:#fff;\n      margin-top:10px;\n    }\n    #icte-litsearchai .reading-title{ font-weight:900; margin-bottom:8px; }\n    #icte-litsearchai .reading-p{ padding:8px 0; border-top:1px dashed var(--line); }\n    #icte-litsearchai .reading-p:first-of-type{ border-top:none; }\n\n    #icte-litsearchai .progress-grid{\n      display:grid;\n      grid-template-columns:repeat(3,1fr);\n      gap:12px;\n      margin-top:10px;\n      margin-bottom:10px;\n    }\n    @media (max-width: 920px){ #icte-litsearchai .progress-grid{ grid-template-columns:1fr; } }\n    #icte-litsearchai .pbox{\n      border:1px solid var(--line);\n      border-radius:16px;\n      background:#fff;\n      padding:12px;\n      text-align:center;\n      box-shadow:var(--shadow);\n    }\n    #icte-litsearchai .pnum{ font-size:28px; font-weight:1000; }\n\n    #icte-litsearchai .bank{\n      border:1px solid var(--line);\n      border-radius:14px;\n      padding:12px;\n      background:#fff;\n      display:flex;\n      flex-wrap:wrap;\n      gap:8px;\n    }\n    #icte-litsearchai .tag{\n      border:1px solid var(--line);\n      background:#f9fafb;\n      padding:6px 10px;\n      border-radius:999px;\n      font-weight:900;\n      font-size:13px;\n    }\n\n    #icte-litsearchai .pre{\n      white-space:pre-wrap;\n      background:#0b1220;\n      color:#e5e7eb;\n      border-radius:12px;\n      padding:12px;\n      border:1px solid rgba(255,255,255,.08);\n      overflow:auto;\n      font-size:13px;\n      line-height:1.45;\n    }\n\n    #icte-litsearchai .ul{ margin:0; padding-left:18px; }\n    #icte-litsearchai .ul li{ margin:6px 0; }\n\n    #icte-litsearchai .textarea{\n      width:100%;\n      padding:12px;\n      border:1px solid var(--line);\n      border-radius:12px;\n      outline:none;\n      resize:vertical;\n    }\n\n    \/* Conversation UI *\/\n    #icte-litsearchai .chat{\n      background:#0b1220;\n      color:#e5e7eb;\n      border-radius:14px;\n      padding:12px;\n      min-height:220px;\n      max-height:420px;\n      overflow:auto;\n      border:1px solid rgba(255,255,255,.08);\n    }\n    #icte-litsearchai .msg{ margin:10px 0; display:flex; gap:10px; align-items:flex-start; }\n    #icte-litsearchai .who{\n      min-width:90px;\n      font-weight:900;\n      font-size:12px;\n      color:#93c5fd;\n      text-transform:uppercase;\n      letter-spacing:.06em;\n    }\n    #icte-litsearchai .bubble{\n      flex:1;\n      background:rgba(255,255,255,.06);\n      padding:10px 10px;\n      border-radius:12px;\n      line-height:1.45;\n      border:1px solid rgba(255,255,255,.06);\n      white-space:pre-line;\n    }\n    #icte-litsearchai .msg.user .who{ color:#86efac; }\n    #icte-litsearchai .msg.user .bubble{ background:rgba(34,197,94,.10); border-color:rgba(34,197,94,.18); }\n\n    #icte-litsearchai .chatbar{\n      margin-top:10px;\n      display:flex;\n      gap:8px;\n      align-items:center;\n    }\n    #icte-litsearchai .input{\n      flex:1;\n      padding:12px 12px;\n      border:1px solid var(--line);\n      border-radius:12px;\n      outline:none;\n    }\n  <\/style>\n\n  <script>\n    (function(){\n      const root = document.getElementById('icte-litsearchai');\n      if(!root) return;\n\n      \/* =========================\n         Helpers\n      ========================= *\/\n      const qs = (sel, el=root) => el.querySelector(sel);\n      const qsa = (sel, el=root) => Array.from(el.querySelectorAll(sel));\n      const clamp = (n,min,max)=>Math.max(min,Math.min(max,n));\n      const norm = (s)=> (s||\"\").toString().trim();\n      const esc = (s)=> (s||\"\").replace(\/[&<>\"']\/g, m=>({ \"&\":\"&amp;\",\"<\":\"&lt;\",\">\":\"&gt;\",'\"':\"&quot;\",\"'\":\"&#039;\" }[m]));\n\n      \/* =========================\n         Navigation\n      ========================= *\/\n      const navLinks = qsa('.icte-menu a');\n      const views = qsa('.view');\n      function showView(name){\n        views.forEach(v=> v.classList.toggle('is-active', v.getAttribute('data-view')===name));\n        navLinks.forEach(a=> a.classList.toggle('is-current', a.getAttribute('data-view')===name));\n        window.scrollTo({top: root.offsetTop - 10, behavior:'smooth'});\n      }\n      navLinks.forEach(a=>{\n        a.addEventListener('click', (e)=>{\n          e.preventDefault();\n          const v = a.getAttribute('data-view');\n          if(v) showView(v);\n        });\n      });\n      function activeViewName(){\n        const v = qs('.view.is-active');\n        return v ? v.getAttribute('data-view') : \"overview\";\n      }\n\n      \/* =========================\n         Multi-speaker Google Voices\n      ========================= *\/\n      const voiceASelect = qs('#voiceA');\n      const voiceBSelect = qs('#voiceB');\n\n      let allVoices = [];\n      let googleVoicesEN = [];\n      let voiceA = null;\n      let voiceB = null;\n\n      function isEnglish(v){ return (v.lang||\"\").toLowerCase().startsWith(\"en\"); }\n      function isGoogle(v){ return (v.name||\"\").toLowerCase().includes(\"google\"); }\n      function looksMaleName(name){\n        const n=(name||\"\").toLowerCase();\n        return n.includes(\"male\")||n.includes(\"david\")||n.includes(\"mark\")||n.includes(\"daniel\");\n      }\n      function looksFemaleName(name){\n        const n=(name||\"\").toLowerCase();\n        return n.includes(\"female\")||n.includes(\"susan\")||n.includes(\"amy\")||n.includes(\"emma\");\n      }\n      function pickDefaultVoices(list){\n        const a = list.find(v=>looksFemaleName(v.name)) || list[0] || null;\n        const b = list.find(v=>v!==a && looksMaleName(v.name)) || list.find(v=>v!==a) || a || null;\n        return {a,b};\n      }\n\n      function loadVoices(){\n        if(!window.speechSynthesis) return;\n        allVoices = speechSynthesis.getVoices() || [];\n        const google = allVoices.filter(v => isEnglish(v) && isGoogle(v));\n        const english = allVoices.filter(v => isEnglish(v));\n        googleVoicesEN = google.length ? google : english;\n\n        const buildSelect = (sel, list)=>{\n          sel.innerHTML=\"\";\n          list.forEach((v,i)=>{\n            const opt=document.createElement(\"option\");\n            opt.value=String(i);\n            opt.textContent=`${v.name} (${v.lang})`;\n            sel.appendChild(opt);\n          });\n        };\n\n        buildSelect(voiceASelect, googleVoicesEN);\n        buildSelect(voiceBSelect, googleVoicesEN);\n\n        const picked = pickDefaultVoices(googleVoicesEN);\n        voiceA = picked.a;\n        voiceB = picked.b;\n\n        voiceASelect.value = String(Math.max(0, googleVoicesEN.indexOf(voiceA)));\n        voiceBSelect.value = String(Math.max(0, googleVoicesEN.indexOf(voiceB)));\n\n        voiceASelect.onchange = ()=>{\n          const idx=parseInt(voiceASelect.value,10);\n          voiceA = googleVoicesEN[idx] || voiceA;\n        };\n        voiceBSelect.onchange = ()=>{\n          const idx=parseInt(voiceBSelect.value,10);\n          voiceB = googleVoicesEN[idx] || voiceB;\n        };\n      }\n      if(window.speechSynthesis){\n        loadVoices();\n        speechSynthesis.onvoiceschanged = loadVoices;\n      }\n\n      function stopSpeak(){ if(window.speechSynthesis) speechSynthesis.cancel(); }\n      function speakAs(role, text, opts){\n        const t = norm(text);\n        if(!t || !window.speechSynthesis) return Promise.resolve();\n        const o = opts || {};\n        const u = new SpeechSynthesisUtterance(t);\n        if(role===\"A\" && voiceA) u.voice = voiceA;\n        if(role===\"B\" && voiceB) u.voice = voiceB;\n        u.rate = o.rate ?? 1.02;\n        u.pitch = o.pitch ?? 1.0;\n        u.volume = o.volume ?? 1.0;\n        return new Promise(resolve=>{\n          u.onend=resolve; u.onerror=resolve;\n          speechSynthesis.speak(u);\n        });\n      }\n      async function speakDialogue(lines){\n        stopSpeak();\n        for(const line of lines){\n          await speakAs(line.role, line.text);\n        }\n      }\n\n      qs('#stopSpeak').addEventListener('click', stopSpeak);\n\n      \/* =========================\n         Mic Speech Recognition\n      ========================= *\/\n      const micDot = qs('#icteMicDot');\n      const micStatus = qs('#icteMicStatus');\n      const btnStartVoice = qs('#icteStartVoice');\n      const btnStopVoice = qs('#icteStopVoice');\n\n      let recognition = null;\n      let listening = false;\n\n      function setMicUI(on){\n        listening = on;\n        micDot.classList.toggle('on', on);\n        micStatus.textContent = on ? \"Mic: Listening\u2026\" : \"Mic: Off\";\n      }\n      function initRecognition(){\n        const SR = window.SpeechRecognition || window.webkitSpeechRecognition;\n        if(!SR) return null;\n        const r = new SR();\n        r.lang = 'en-US';\n        r.continuous = true;\n        r.interimResults = false;\n        r.maxAlternatives = 1;\n        return r;\n      }\n      function startListening(){\n        if(listening) return;\n        recognition = recognition || initRecognition();\n        if(!recognition){\n          alert(\"Speech Recognition is not available in this browser. Use Chrome\/Edge, or type.\");\n          return;\n        }\n        recognition.onresult = (e)=>{\n          const res = e.results[e.results.length - 1];\n          const transcript = res && res[0] ? res[0].transcript : \"\";\n          handleVoiceTranscript(transcript);\n        };\n        recognition.onerror = ()=> setMicUI(false);\n        recognition.onend = ()=>{ if(listening){ try{ recognition.start(); }catch(_){ } } };\n        setMicUI(true);\n        try{ recognition.start(); }catch(_){ }\n      }\n      function stopListening(){\n        setMicUI(false);\n        try{ recognition && recognition.stop(); }catch(_){ }\n      }\n      btnStartVoice.addEventListener('click', startListening);\n      btnStopVoice.addEventListener('click', stopListening);\n\n      \/* =========================\n         Progress (localStorage)\n      ========================= *\/\n      const LS_KEY = \"icte_litsearchai_progress_v1\";\n      const progress = JSON.parse(localStorage.getItem(LS_KEY) || \"{}\");\n\n      function saveProgress(){ localStorage.setItem(LS_KEY, JSON.stringify(progress)); renderProgress(); }\n      function markDone(key, scorePct){\n        progress[key] = { done:true, score: clamp(scorePct,0,100), ts: Date.now() };\n        saveProgress();\n      }\n      function resetProgress(){ localStorage.removeItem(LS_KEY); location.reload(); }\n      qs('#progressReset').addEventListener('click', resetProgress);\n\n      const checks = [\n        {k:\"concepts\", label:\"Clear concept blocks\"},\n        {k:\"synonyms\", label:\"Synonym expansion\"},\n        {k:\"boolean\", label:\"Correct Boolean (AND\/OR)\"},\n        {k:\"phrases\", label:\"Phrase searching\"},\n        {k:\"fields\", label:\"Field restrictions\"},\n        {k:\"not\", label:\"Safe NOT terms\"},\n        {k:\"log\", label:\"Search log\"},\n        {k:\"criteria\", label:\"Inclusion\/exclusion criteria\"}\n      ];\n      qs('#checksBank').innerHTML = checks.map(x=>`<span class=\"tag\">${esc(x.label)}<\/span>`).join(\"\");\n\n      function addCheck(k){\n        progress._checks = progress._checks || {};\n        progress._checks[k] = true;\n        saveProgress();\n      }\n\n      function renderProgress(){\n        const items = Object.values(progress).filter(p=>p && p.done);\n        const done = items.length;\n        const avg = done ? Math.round(items.reduce((s,p)=>s+(p.score||0),0)\/done) : 0;\n        qs('#pDone').textContent = String(done);\n        qs('#pScore').textContent = String(avg) + \"%\";\n        qs('#pChecks').textContent = String(Object.keys(progress._checks || {}).length);\n      }\n\n      \/* =========================\n         Instruction TTS map\n      ========================= *\/\n      const SAY = {\n        \"overview-instr\":\"Overview. Learn concept blocks, synonym expansion, Boolean logic, safe exclusions, and iterative refinement for Scopus and Google Scholar.\",\n        \"conv-instr\":\"Conversation. Build a search strategy step by step: define concepts, add synonyms, build Boolean strings, then refine with phrases and NOT terms.\",\n        \"reading-instr\":\"Reading. Read the text and answer multiple choice questions. Then check your score.\",\n        \"toolkit-instr\":\"Toolkit. Review concept models, keyword expansion, Boolean patterns, and safe inclusion and exclusion strategies.\",\n        \"prompts-instr\":\"Prompts. Copy prompts to generate synonyms and query variations, then validate by running searches in Scholar and Scopus.\",\n        \"list-instr\":\"Listening. Play the dialogue with two speakers and answer the questions.\",\n        \"lab-instr\":\"Lab. Fill concept blocks and build Scholar and Scopus strings, then copy a safe AI prompt to refine based on real results.\",\n        \"problem-instr\":\"Problem-solving. Fix a search that is too broad in Scholar and too narrow in Scopus. Write refined queries and screening criteria.\"\n      };\n      qsa('[data-say]').forEach(btn=>{\n        btn.addEventListener('click', async ()=>{\n          const k = btn.getAttribute('data-say');\n          if(SAY[k]) await speakAs(\"A\", SAY[k]);\n        });\n      });\n\n      qs('#speakActive').addEventListener('click', async ()=>{\n        const v = activeViewName();\n        const map = {\n          overview:\"Overview page. Outcomes and safety rules for AI-assisted literature searching.\",\n          conversation:\"Conversation page. Build your search string step by step.\",\n          reading:\"Reading page. Read and answer quiz questions.\",\n          toolkit:\"Toolkit page. Concept blocks, expansion checklist, Boolean cheat sheet, and inclusion\/exclusion strategy.\",\n          prompts:\"Prompts page. Copy prompts for building Scholar and Scopus strings and safe NOT terms.\",\n          listening:\"Listening page. Two-speaker dialogue on refining queries.\",\n          lab:\"Lab page. Build and export Scholar and Scopus strings.\",\n          problem:\"Problem-solving page. Improve broad and narrow searches and write screening criteria.\",\n          progress:\"Progress page.\"\n        };\n        await speakAs(\"A\", map[v] || \"Literature search strategy lesson.\");\n      });\n\n      \/* =========================\n         Voice transcript routing\n      ========================= *\/\n      function handleVoiceTranscript(t){\n        const text = norm(t);\n        if(!text) return;\n        const v = activeViewName();\n        if(v===\"conversation\"){ handleConversationInput(text); return; }\n        if(v===\"lab\"){\n          const box = qs('#labTopic');\n          if(box){\n            box.value = (box.value ? (box.value + \" \") : \"\") + text;\n            speakAs(\"A\",\"Added to your topic box.\");\n          }\n          return;\n        }\n        if(v===\"problem\"){\n          const box = qs('#psPlan');\n          if(box){\n            box.value = (box.value ? (box.value + \"\\n\") : \"\") + \"- \" + text;\n            speakAs(\"A\",\"Added to your plan.\");\n          }\n          return;\n        }\n        speakAs(\"A\",\"I heard: \" + text);\n      }\n\n      \/* =========================\n         Conversation coach\n      ========================= *\/\n      const convChat = qs('#convChat');\n      const convText = qs('#convText');\n      const convSend = qs('#convSend');\n      const convReset = qs('#convReset');\n      const convHear  = qs('#convHear');\n\n      let convStep = 0;\n      let lastCoachQ = \"\";\n\n      const convSteps = [\n        { bot:\"Step 1: State your topic in one line (Concept 1 + Concept 2 + Outcome).\", check:(a)=> a.split(\/\\s+\/).length>=6, tips:\"Example: AI chatbots AND EFL learners AND speaking skills.\" },\n        { bot:\"Step 2: List 4 synonyms\/variants for Concept 1 (use OR).\", check:(a)=> (a.match(\/\\bor\\b\/gi)||[]).length>=2 || (a.split(\/[,;]\/).length>=4), tips:\"Example: chatbot OR conversational agent OR dialogue system OR AI tutor.\" },\n        { bot:\"Step 3: Build one Boolean block using parentheses for Concept 1.\", check:(a)=> \/\\(.*\\)\/.test(a) && \/\\bor\\b\/i.test(a), tips:\"Use: (term1 OR term2 OR term3).\" },\n        { bot:\"Step 4: Add Concept 2 with AND. (Show two blocks joined by AND).\", check:(a)=> \/\\)\\s*AND\\s*\\(\/i.test(a) || (\/\\band\\b\/i.test(a) && \/\\)\/.test(a)), tips:\"Example: (C1 OR ...) AND (EFL OR ESL ...).\" },\n        { bot:\"Step 5: Add ONE phrase search term in quotes.\", check:(a)=> \/\".+?\"\/.test(a), tips:'Example: \"oral proficiency\".' },\n        { bot:\"Step 6: Suggest ONE safe NOT term cluster (only after checking results).\", check:(a)=> \/\\bNOT\\b\/i.test(a) || \/-(\\w+)\/.test(a), tips:\"Example: NOT (medical OR diagnosis).\" }\n      ];\n\n      function addMsg(who, text){\n        const div = document.createElement('div');\n        div.className = 'msg ' + (who==='You' ? 'user' : 'bot');\n        div.innerHTML = `<div class=\"who\">${esc(who)}<\/div><div class=\"bubble\">${esc(text)}<\/div>`;\n        convChat.appendChild(div);\n        convChat.scrollTop = convChat.scrollHeight;\n      }\n\n      async function coachAsk(){\n        const step = convSteps[convStep];\n        if(!step){\n          addMsg(\"Coach\",\"\u2705 Great. You built concept blocks, used OR\/AND with parentheses, added a phrase, and proposed safe NOT terms.\");\n          markDone(\"conversation\", 100);\n          addCheck(\"concepts\"); addCheck(\"synonyms\"); addCheck(\"boolean\"); addCheck(\"phrases\"); addCheck(\"not\");\n          await speakAs(\"A\",\"Great. You built concept blocks, used Boolean logic, added a phrase, and proposed safe NOT terms.\");\n          return;\n        }\n        lastCoachQ = step.bot;\n        addMsg(\"Coach\", step.bot);\n        await speakAs(\"A\", step.bot);\n      }\n\n      async function handleConversationInput(text){\n        const a = norm(text);\n        if(!a) return;\n        addMsg(\"You\", a);\n\n        if(\/\\bAND\\b\/i.test(a) || \/\\bOR\\b\/i.test(a)) addCheck(\"boolean\");\n        if(\/\\(.*\\)\/.test(a)) addCheck(\"concepts\");\n        if(\/\\bor\\b\/i.test(a)) addCheck(\"synonyms\");\n        if(\/\".+?\"\/.test(a)) addCheck(\"phrases\");\n        if(\/\\bNOT\\b\/i.test(a) || \/-\\w+\/.test(a)) addCheck(\"not\");\n\n        const step = convSteps[convStep];\n        const ok = step.check(a);\n        const msg = ok ? \"\u2705 Good. Next.\" : \"\u26a0\ufe0f Try again. \" + step.tips;\n\n        addMsg(\"Coach\", msg);\n        await speakAs(\"A\", msg);\n\n        if(ok){ convStep++; setTimeout(coachAsk, 250); }\n      }\n\n      convSend.addEventListener('click', ()=>{ handleConversationInput(convText.value); convText.value=\"\"; });\n      convText.addEventListener('keydown', (e)=>{\n        if(e.key===\"Enter\"){ e.preventDefault(); handleConversationInput(convText.value); convText.value=\"\"; }\n      });\n      convHear.addEventListener('click', ()=> speakAs(\"A\", lastCoachQ || \"No question yet.\"));\n      convReset.addEventListener('click', ()=>{\n        convChat.innerHTML=\"\";\n        convStep=0;\n        addMsg(\"Coach\",\"Ready. Let\u2019s build your literature search string.\");\n        coachAsk();\n      });\n\n      addMsg(\"Coach\",\"Ready. Let\u2019s build your literature search string.\");\n      coachAsk();\n\n      \/* =========================\n         Reading TTS\n      ========================= *\/\n      qs('#readTextBtn').addEventListener('click', ()=>{\n        const parts = qsa('#readingText .reading-p').map(p=>p.textContent).join(\" \");\n        speakAs(\"A\", \"Reading. \" + parts);\n      });\n\n      \/* =========================\n         Quiz\n      ========================= *\/\n      const quiz = qs('#quiz');\n      const quizFb = qs('#quizFb');\n\n      const quizItems = [\n        { q:\"1) Why use concept blocks instead of a long sentence?\",\n          ans:\"It improves recall and keeps the topic focused using AND\/OR\",\n          opts:[\n            \"It makes the search shorter but less accurate\",\n            \"It improves recall and keeps the topic focused using AND\/OR\",\n            \"It guarantees only perfect results\",\n            \"It replaces screening and reading\"\n          ]\n        },\n        { q:\"2) In Boolean logic, OR is mainly used to\u2026\",\n          ans:\"Combine synonyms\/variants within the same concept\",\n          opts:[\n            \"Exclude irrelevant studies\",\n            \"Combine synonyms\/variants within the same concept\",\n            \"Restrict to the title field only\",\n            \"Replace parentheses\"\n          ]\n        },\n        { q:\"3) A safe strategy for NOT terms is to\u2026\",\n          ans:\"Add NOT after identifying irrelevant clusters in results\",\n          opts:[\n            \"Use many NOT terms from the start\",\n            \"Add NOT after identifying irrelevant clusters in results\",\n            \"Never use NOT terms\",\n            \"Use NOT to remove key concepts\"\n          ]\n        },\n        { q:\"4) Scopus often supports\u2026\",\n          ans:\"Field searching such as TITLE-ABS-KEY\",\n          opts:[\n            \"Only full-text searching\",\n            \"Field searching such as TITLE-ABS-KEY\",\n            \"No Boolean operators\",\n            \"Only phrase searching\"\n          ]\n        },\n        { q:\"5) A good search strategy is\u2026\",\n          ans:\"Iterative and documented in a search log\",\n          opts:[\n            \"One perfect query with no changes\",\n            \"Iterative and documented in a search log\",\n            \"Only keyword expansion with no screening\",\n            \"Only reading references without searching\"\n          ]\n        }\n      ];\n\n      function renderQuiz(){\n        quiz.innerHTML = quizItems.map((it)=>{\n          const options = ['<option value=\"\">Choose\u2026<\/option>']\n            .concat(it.opts.map(o=>`<option value=\"${esc(o)}\">${esc(o)}<\/option>`))\n            .join(\"\");\n          return `\n            <div class=\"qitem\">\n              <div class=\"qtext\">${esc(it.q)}<\/div>\n              <select class=\"icte-select\" data-ans=\"${esc(it.ans)}\">${options}<\/select>\n            <\/div>\n          `;\n        }).join(\"\");\n      }\n      renderQuiz();\n\n      qs('#quizCheck').addEventListener('click', ()=>{\n        const sels = qsa('#quiz select');\n        let correct = 0;\n        sels.forEach(s=>{\n          const ok = s.value === s.getAttribute('data-ans');\n          s.style.borderColor = ok ? \"rgba(34,197,94,.6)\" : \"rgba(239,68,68,.6)\";\n          if(ok) correct++;\n        });\n        const pct = Math.round((correct \/ sels.length) * 100);\n        quizFb.className = \"feedback \" + (pct>=70 ? \"ok\":\"bad\");\n        quizFb.textContent = `Score: ${correct}\/${sels.length} (${pct}%).`;\n        if(pct>=70){ addCheck(\"boolean\"); addCheck(\"log\"); }\n        markDone(\"reading_quiz\", pct);\n      });\n\n      \/* =========================\n         Toolkit content\n      ========================= *\/\n      qs('#tkConcept').textContent =\n`Concept block model:\n(C1: phenomenon) AND (C2: population\/context) AND (C3: outcome) AND (C4: optional setting)\n\nExample:\n(chatbot OR \"conversational agent\" OR \"dialogue system\")\nAND (EFL OR ESL OR \"second language\")\nAND (\"speaking skill*\" OR \"oral proficiency\")`;\n\n      qs('#tkExpand').textContent =\n`Expand keywords using:\n- Synonyms (chatbot, conversational agent)\n- Variants (learn, learning, learner)\n- Acronyms (CALL, CMC)\n- Related terms (spoken interaction, oral production)\n- Spelling variants (behavior\/behaviour)\n- Phrase forms (\"oral proficiency\")\nValidate by checking real results and top cited papers.`;\n\n      qs('#tkBoolean').textContent =\n`Boolean basics:\n- OR = synonyms (inside parentheses)\n- AND = combine concepts\n- \"phrase\" = exact phrase\n- truncation: learn* (database-dependent)\n- nesting: (A OR B) AND (C OR D)\n- field limits (Scopus): TITLE-ABS-KEY(...)\nGoogle Scholar: simpler; fewer field tools.`;\n\n      qs('#tkExcl').textContent =\n`Safe inclusion\/exclusion:\n1) Start broad \u2192 inspect irrelevant clusters\n2) Add NOT terms only for clearly irrelevant domains\n3) Prefer phrase tightening before NOT\n4) Keep a log of each change:\n   date, database, query, filters, results count, notes`;\n\n      qs('#tkExamples').textContent =\n`Example (Google Scholar):\n(\"conversational agent\" OR chatbot OR \"dialogue system\")\n(EFL OR ESL OR \"second language\")\n(\"oral proficiency\" OR speaking OR \"speaking performance\")\n- medical - diagnosis\n\nExample (Scopus):\nTITLE-ABS-KEY(\n  (chatbot OR \"conversational agent\" OR \"dialogue system\")\n  AND (EFL OR ESL OR \"second language\")\n  AND (\"oral proficiency\" OR speak* OR \"speaking performance\")\n)`;\n\n      qs('#toolkitSpeak').addEventListener('click', ()=>{\n        speakAs(\"A\",\"Toolkit. Use concept blocks, expand synonyms and variants, apply Boolean logic with parentheses, use phrases in quotes, and add NOT terms only after inspecting irrelevant clusters. Keep a search log.\");\n      });\n\n      \/* =========================\n         Prompts content\n      ========================= *\/\n      const p1 = qs('#p1'), p2 = qs('#p2'), p3 = qs('#p3'), p4 = qs('#p4'), pExample = qs('#pExample');\n\n      p1.textContent =\n`PROMPT 1 \u2014 CONCEPT BLOCKS + KEYWORD EXPANSION\nTopic: [PASTE YOUR TOPIC]\nBreak this topic into 2\u20134 concept blocks.\nFor each concept, propose:\n- 8\u201315 synonyms\/variants\n- 5 phrase forms (quoted)\n- 5 \u201ctoo broad \/ risky\u201d terms to avoid\nOutput as:\nConcept | core terms | synonyms\/variants | phrase terms | notes`;\n\n      p2.textContent =\n`PROMPT 2 \u2014 SCOPUS STRING (TITLE-ABS-KEY)\nUsing the concept blocks below, write a Scopus query:\n- Use parentheses and OR within concepts\n- Use AND between concepts\n- Use quotes for phrases\nReturn:\n1) TITLE-ABS-KEY(...) string\n2) A shorter alternative (higher precision)\n3) A broader alternative (higher recall)\nConcept blocks:\n[PASTE C1\u2013C4]`;\n\n      p3.textContent =\n`PROMPT 3 \u2014 GOOGLE SCHOLAR SEARCH PLAN\nCreate a Scholar strategy:\n- 1 broad query\n- 2 refinement queries (with phrases)\n- 1 \u201cexclude cluster\u201d query using -term or NOT\nAlso: suggest what to scan in the first 20 results to decide refinements.\nTopic:\n[PASTE TOPIC]`;\n\n      p4.textContent =\n`PROMPT 4 \u2014 SAFE NOT TERMS\nBased on these irrelevant clusters observed in results:\n[PASTE IRRELEVANT THEMES]\nPropose a conservative NOT list that removes those clusters with minimal risk.\nAlso propose alternatives that tighten precision without using NOT (phrases, adding concept terms).`;\n\n      pExample.textContent =\n`MINI EXAMPLE\n\nTopic:\nAI chatbots for improving EFL speaking skills.\n\nConcept blocks:\nC1 (tool): (chatbot OR \"conversational agent\" OR \"dialogue system\")\nC2 (learners): (EFL OR ESL OR \"second language\" OR \"English language learner*\")\nC3 (outcome): (\"oral proficiency\" OR speaking OR \"speaking performance\" OR speak*)\nC4 (setting): (university OR \"higher education\" OR tertiary)\n\nScopus:\nTITLE-ABS-KEY( C1 AND C2 AND C3 AND C4 )\n\nScholar:\n(C1) (C2) (C3) (C4) -medical -diagnosis`;\n\n      qs('#promptsSpeak').addEventListener('click', ()=>{\n        speakAs(\"A\",\"Prompts page. Copy prompts to generate concept blocks, build Scopus TITLE-ABS-KEY strings, plan Google Scholar refinements, and create safe NOT term lists based on irrelevant clusters.\");\n      });\n\n      qs('#copyPrompts').addEventListener('click', ()=>{\n        const all =\n`PROMPT 1\\n${p1.textContent}\\n\\nPROMPT 2\\n${p2.textContent}\\n\\nPROMPT 3\\n${p3.textContent}\\n\\nPROMPT 4\\n${p4.textContent}\\n\\nMINI EXAMPLE\\n${pExample.textContent}`;\n        navigator.clipboard.writeText(all).then(()=> speakAs(\"A\",\"Copied prompts.\")).catch(()=> alert(\"Clipboard blocked. Copy manually.\"));\n      });\n\n      \/* =========================\n         Listening\n      ========================= *\/\n      const listenFb = qs('#listenFb');\n      const listenQ = qs('#listenQ');\n\n      const dialogue = [\n        {role:\"A\", text:\"My first search in Google Scholar returned tens of thousands of results.\"},\n        {role:\"B\", text:\"That\u2019s normal. Start broad, then refine with phrases and more specific concept terms.\"},\n        {role:\"A\", text:\"When should I add NOT terms?\"},\n        {role:\"B\", text:\"Only after you identify irrelevant clusters, like medical chatbots, and document the change.\"},\n        {role:\"A\", text:\"Scopus gave me too few results. What can I do?\"},\n        {role:\"B\", text:\"Reduce restrictions. Expand synonyms, remove an optional setting block, or broaden outcome terms.\"},\n        {role:\"A\", text:\"So the goal is not one perfect query.\"},\n        {role:\"B\", text:\"Right. It\u2019s an iterative set of queries with a transparent search log.\"}\n      ];\n\n      const listenItems = [\n        {q:\"1) The recommended first step is to\u2026\", ans:\"Start broad and then refine\"},\n        {q:\"2) NOT terms should be added\u2026\", ans:\"After identifying irrelevant clusters\"},\n        {q:\"3) If Scopus results are too few, you can\u2026\", ans:\"Expand synonyms and reduce restrictions\"},\n        {q:\"4) A good strategy includes\u2026\", ans:\"An iterative process with a search log\"}\n      ];\n\n      function renderListenQ(){\n        const optsMap = {\n          \"Start broad and then refine\":[\n            \"Start broad and then refine\",\n            \"Use many NOT terms immediately\",\n            \"Search with full sentences only\"\n          ],\n          \"After identifying irrelevant clusters\":[\n            \"After identifying irrelevant clusters\",\n            \"Before any searching\",\n            \"Never\"\n          ],\n          \"Expand synonyms and reduce restrictions\":[\n            \"Expand synonyms and reduce restrictions\",\n            \"Remove all concept blocks\",\n            \"Use only one keyword\"\n          ],\n          \"An iterative process with a search log\":[\n            \"An iterative process with a search log\",\n            \"One fixed query only\",\n            \"No documentation\"\n          ]\n        };\n\n        listenQ.innerHTML = listenItems.map((it)=>{\n          const options = ['<option value=\"\">Choose\u2026<\/option>']\n            .concat((optsMap[it.ans]||[it.ans]).map(o=>`<option value=\"${esc(o)}\">${esc(o)}<\/option>`)).join(\"\");\n          return `\n            <div class=\"qitem\">\n              <div class=\"qtext\">${esc(it.q)}<\/div>\n              <select class=\"icte-select\" data-ans=\"${esc(it.ans)}\">${options}<\/select>\n            <\/div>\n          `;\n        }).join(\"\");\n      }\n      renderListenQ();\n\n      qs('#listenPlay').addEventListener('click', async ()=>{\n        if(googleVoicesEN.length >= 2 && voiceA === voiceB){\n          voiceB = googleVoicesEN.find(v => v !== voiceA) || voiceB;\n          voiceBSelect.value = String(googleVoicesEN.indexOf(voiceB));\n        }\n        await speakDialogue(dialogue);\n      });\n      qs('#listenStop').addEventListener('click', stopSpeak);\n\n      qs('#listenCheck').addEventListener('click', ()=>{\n        const sels = qsa('#listenQ select');\n        let correct = 0;\n        sels.forEach(s=>{\n          const ok = s.value === s.getAttribute('data-ans');\n          s.style.borderColor = ok ? \"rgba(34,197,94,.6)\" : \"rgba(239,68,68,.6)\";\n          if(ok) correct++;\n        });\n        const pct = Math.round((correct \/ sels.length) * 100);\n        listenFb.className = \"feedback \" + (pct>=70 ? \"ok\":\"bad\");\n        listenFb.textContent = `Score: ${correct}\/${sels.length} (${pct}%).`;\n        if(pct>=70){ addCheck(\"log\"); addCheck(\"not\"); }\n        markDone(\"listening\", pct);\n      });\n\n      \/* =========================\n         Lab filters (log only)\n      ========================= *\/\n      const filters = [\n        {id:\"f_years\", label:\"Year range (e.g., 2015\u20132026)\"},\n        {id:\"f_doc\", label:\"Document type (article, conference paper)\"},\n        {id:\"f_lang\", label:\"Language (English)\"},\n        {id:\"f_subject\", label:\"Subject area (Education, Linguistics)\"},\n        {id:\"f_peer\", label:\"Peer-reviewed \/ journal only (if needed)\"}\n      ];\n      const labFilters = qs('#labFilters');\n      labFilters.innerHTML = filters.map(x=>`\n        <label class=\"opt\">\n          <input type=\"checkbox\" id=\"${esc(x.id)}\" \/>\n          <span><b>${esc(x.label)}<\/b><\/span>\n        <\/label>\n      `).join(\"\");\n\n      function parseOrBlock(txt){\n        \/\/ Accept \"a OR b OR c\" or comma\/newline separated; output (a OR b OR \"phrase\")\n        const raw = (txt||\"\").trim();\n        if(!raw) return \"\";\n        \/\/ split on OR, comma, newline\n        const parts = raw\n          .replace(\/\\s+OR\\s+\/gi,\"|\")\n          .split(\/[|\\n,;]+\/g)\n          .map(s=>s.trim())\n          .filter(Boolean);\n\n        const normParts = [];\n        parts.forEach(p=>{\n          if(!p) return;\n          \/\/ preserve quotes if already\n          let t = p;\n          \/\/ if contains space and not quoted, quote it\n          if(\/\\s\/.test(t) && !\/^\".*\"$\/.test(t)) t = `\"${t}\"`;\n          normParts.push(t);\n        });\n\n        const uniq = Array.from(new Set(normParts));\n        if(!uniq.length) return \"\";\n        return \"(\" + uniq.join(\" OR \") + \")\";\n      }\n\n      function parseNotBlock(txt){\n        const raw = (txt||\"\").trim();\n        if(!raw) return \"\";\n        const parts = raw\n          .replace(\/\\s+OR\\s+\/gi,\"|\")\n          .split(\/[|\\n,;]+\/g)\n          .map(s=>s.trim())\n          .filter(Boolean);\n\n        const uniq = Array.from(new Set(parts));\n        if(!uniq.length) return \"\";\n        \/\/ For Scopus, use AND NOT ( ... OR ... )\n        const inside = uniq.map(t=>{\n          if(\/\\s\/.test(t) && !\/^\".*\"$\/.test(t)) return `\"${t}\"`;\n          return t;\n        }).join(\" OR \");\n        return \"NOT (\" + inside + \")\";\n      }\n\n      const labTopic = qs('#labTopic');\n      const c1 = qs('#c1'), c2 = qs('#c2'), c3 = qs('#c3'), c4 = qs('#c4');\n      const phrases = qs('#phrases');\n      const nots = qs('#nots');\n\n      const outScholar = qs('#outScholar');\n      const outScopus = qs('#outScopus');\n      const outPrompt = qs('#outPrompt');\n      const labFb = qs('#labFb');\n\n      function buildScholarString(){\n        const b1 = parseOrBlock(c1.value);\n        const b2 = parseOrBlock(c2.value);\n        const b3 = parseOrBlock(c3.value);\n        const b4 = parseOrBlock(c4.value);\n\n        \/\/ Scholar: keep it simple; spaces imply AND\n        const blocks = [b1,b2,b3,b4].filter(Boolean);\n        let s = blocks.join(\" \");\n\n        \/\/ phrases: add them (quoted lines)\n        const ph = (phrases.value||\"\").split(\/\\n+\/).map(x=>x.trim()).filter(Boolean);\n        ph.forEach(p=>{\n          const t = \/^\".*\"$\/.test(p) ? p : `\"${p}\"`;\n          s += \" \" + t;\n        });\n\n        \/\/ NOT via -term (Scholar supports -word; -phrase is inconsistent)\n        const nt = (nots.value||\"\").split(\/\\n+\/).join(\" \").trim();\n        if(nt){\n          const words = nt.replace(\/\\s+OR\\s+\/gi,\" \").split(\/[,\\s;]+\/).map(x=>x.trim()).filter(Boolean);\n          words.slice(0,8).forEach(w=>{\n            \/\/ avoid quotes for scholar - operator\n            s += \" -\" + w.replace(\/\"\/g,\"\");\n          });\n          addCheck(\"not\");\n        }\n        return s.trim();\n      }\n\n      function buildScopusString(){\n        const b1 = parseOrBlock(c1.value);\n        const b2 = parseOrBlock(c2.value);\n        const b3 = parseOrBlock(c3.value);\n        const b4 = parseOrBlock(c4.value);\n\n        const blocks = [b1,b2,b3,b4].filter(Boolean);\n        let inside = blocks.join(\" AND \");\n\n        \/\/ merge phrases into a single OR group appended to inside (optional)\n        const ph = (phrases.value||\"\").split(\/\\n+\/).map(x=>x.trim()).filter(Boolean);\n        if(ph.length){\n          const phNorm = ph.map(p => \/^\".*\"$\/.test(p) ? p : `\"${p}\"`);\n          inside += \" AND (\" + phNorm.join(\" OR \") + \")\";\n          addCheck(\"phrases\");\n        }\n\n        const notBlock = parseNotBlock(nots.value);\n        if(notBlock){\n          inside += \" AND \" + notBlock;\n          addCheck(\"not\");\n        }\n\n        return `TITLE-ABS-KEY(${inside})`;\n      }\n\n      function buildSafeAIPrompt(){\n        const selected = filters.filter(f=>qs(\"#\"+f.id).checked).map(f=>\"- \" + f.label).join(\"\\n\") || \"- (No filters selected)\";\n        return `ROLE: You are a research librarian.\n\nTASK:\nHelp me refine my literature search strategy.\n\nINPUTS:\n- Topic: ${norm(labTopic.value) || \"(not provided)\"}\n- Current Scholar query: ${outScholar.textContent || \"(not built yet)\"}\n- Current Scopus query: ${outScopus.textContent || \"(not built yet)\"}\n\nCONSTRAINTS:\n- Do NOT invent citations or claim database coverage.\n- Propose synonym\/phrase expansions and explain why.\n- Suggest conservative NOT terms ONLY after I identify irrelevant clusters.\n\nOUTPUT:\n1) 10\u201320 new keyword variants (grouped by concept)\n2) 3 improved Scholar query versions (broad \/ medium \/ precise)\n3) 3 improved Scopus TITLE-ABS-KEY versions (broad \/ medium \/ precise)\n4) A search-log template (fields: date, database, query, filters, results count, notes)\n5) Suggested filters to apply in the UI:\n${selected}\n\nNOW ASK ME:\n- What irrelevant clusters did you see in results? (so we can add safe NOT terms)`;\n      }\n\n      function labBuild(){\n        const scholar = buildScholarString();\n        const scopus = buildScopusString();\n\n        outScholar.textContent = scholar || \"(Fill concept blocks to generate a Scholar string.)\";\n        outScopus.textContent = scopus || \"(Fill concept blocks to generate a Scopus string.)\";\n        outPrompt.textContent = buildSafeAIPrompt();\n\n        \/\/ basic checklist\n        if((c1.value||\"\").trim() && (c2.value||\"\").trim()) addCheck(\"concepts\");\n        if(\/\\bor\\b\/i.test(c1.value+c2.value+c3.value+c4.value)) addCheck(\"synonyms\");\n        addCheck(\"boolean\");\n\n        labFb.className = \"feedback ok\";\n        labFb.textContent = \"\u2705 Built Scholar + Scopus strings. Next: run searches, inspect top results, then refine keywords and exclusions.\";\n        markDone(\"lab\", 90);\n      }\n\n      function labCopy(){\n        const pack =\n`GOOGLE SCHOLAR QUERY\\n${outScholar.textContent}\\n\\nSCOPUS QUERY\\n${outScopus.textContent}\\n\\nSAFE AI PROMPT\\n${outPrompt.textContent}`;\n        navigator.clipboard.writeText(pack).then(()=>{\n          labFb.className = \"feedback ok\";\n          labFb.textContent = \"\u2705 Copied strings + prompt.\";\n        }).catch(()=>{\n          labFb.className = \"feedback bad\";\n          labFb.textContent = \"\u26a0\ufe0f Clipboard blocked. Copy manually from the boxes.\";\n        });\n      }\n\n      function labSelfCheck(){\n        const hasC1 = (c1.value||\"\").trim().length>0;\n        const hasC2 = (c2.value||\"\").trim().length>0;\n        const hasBoolean = \/\\(\/.test(outScopus.textContent||\"\") && \/\\bAND\\b\/i.test(outScopus.textContent||\"\");\n        const hasSyn = \/\\bOR\\b\/i.test(outScopus.textContent||\"\") || \/\\bOR\\b\/i.test(outScholar.textContent||\"\");\n        const pct = Math.round(((hasC1&&hasC2?1:0)+(hasBoolean?1:0)+(hasSyn?1:0))\/3*100);\n\n        labFb.className = \"feedback \" + (pct>=70 ? \"ok\":\"bad\");\n        labFb.textContent =\n          `Self-check: ${pct}%\\n- At least 2 concept blocks: ${(hasC1&&hasC2)?\"Yes\":\"No\"}\\n- Boolean structure present: ${hasBoolean?\"Yes\":\"No\"}\\n- Synonym expansion (OR): ${hasSyn?\"Yes\":\"No\"}`;\n        markDone(\"lab_selfcheck\", pct);\n      }\n\n      qs('#labBuild').addEventListener('click', labBuild);\n      qs('#labCopy').addEventListener('click', labCopy);\n      qs('#labScore').addEventListener('click', labSelfCheck);\n\n      \/* =========================\n         Problem-solving checker\n      ========================= *\/\n      const psPlan = qs('#psPlan');\n      const psQueries = qs('#psQueries');\n      const psIncl = qs('#psIncl');\n      const psExcl = qs('#psExcl');\n      const psFb = qs('#psFb');\n\n      function countBullets(txt){ return (txt.match(\/^\\s*[-\u2022]\/gm)||[]).length; }\n      function includesTwoQueries(txt){ return \/Query\\s*A\/i.test(txt) && \/Query\\s*B\/i.test(txt); }\n      function hasScholarLike(txt){ return \/-\\w+\/.test(txt) || \/\".+?\"\/.test(txt); }\n      function hasScopusLike(txt){ return \/TITLE-ABS-KEY\\s*\\(\/i.test(txt) && \/\\bAND\\b\/i.test(txt) && \/\\bOR\\b\/i.test(txt); }\n\n      function psCheck(){\n        const plan = norm(psPlan.value);\n        const qsText = norm(psQueries.value);\n        const incl = norm(psIncl.value);\n        const excl = norm(psExcl.value);\n\n        const planOk = countBullets(plan) >= 4 && \/(phrase|NOT|cluster|refine|expand|synonym|field|filter)\/i.test(plan);\n        const inclOk = countBullets(incl) >= 3;\n        const exclOk = countBullets(excl) >= 3;\n        const qOk = includesTwoQueries(qsText) && hasScholarLike(qsText) && hasScopusLike(qsText);\n\n        const pct = Math.round(((planOk?1:0)+(qOk?1:0)+(inclOk?1:0)+(exclOk?1:0))\/4*100);\n\n        psFb.className = \"feedback \" + (pct>=70 ? \"ok\":\"bad\");\n        psFb.textContent =\n          `Check: ${pct}%\\n` +\n          `- Refinement plan (>=4 bullets + strategy terms): ${planOk ? \"Yes\" : \"No\"}\\n` +\n          `- Two improved queries (Scholar + Scopus): ${qOk ? \"Yes\" : \"No\"}\\n` +\n          `- Inclusion criteria (>=3): ${inclOk ? \"Yes\" : \"No\"}\\n` +\n          `- Exclusion criteria (>=3): ${exclOk ? \"Yes\" : \"No\"}`;\n\n        markDone(\"problem_solving\", pct);\n        addCheck(\"criteria\");\n        addCheck(\"log\");\n      }\n\n      function psReset(){\n        psPlan.value = \"\";\n        psQueries.value = \"\";\n        psIncl.value = \"\";\n        psExcl.value = \"\";\n        psFb.className = \"feedback\";\n        psFb.textContent = \"\";\n      }\n\n      qs('#psCheck').addEventListener('click', psCheck);\n      qs('#psReset').addEventListener('click', psReset);\n\n      \/* =========================\n         Buttons already wired: listenStop handled by stopSpeak\n      ========================= *\/\n      qs('#listenStop').addEventListener('click', stopSpeak);\n\n      \/* =========================\n         Init\n      ========================= *\/\n      renderProgress();\n\n    })();\n  <\/script>\n\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Overview Conversation Reading Search Toolkit Prompts Listening Query Builder Lab Problem-solving Progress Literature Search Strategy with AI Design powerful queries<\/p>\n","protected":false},"author":1,"featured_media":764,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"colormag_page_layout":"default_layout","footnotes":""},"categories":[52,45],"tags":[],"class_list":["post-762","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ai-for-research","category-ai-for-teachers"],"_links":{"self":[{"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/posts\/762","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/comments?post=762"}],"version-history":[{"count":2,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/posts\/762\/revisions"}],"predecessor-version":[{"id":766,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/posts\/762\/revisions\/766"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/media\/764"}],"wp:attachment":[{"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/media?parent=762"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/categories?post=762"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/tags?post=762"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}