{"id":727,"date":"2026-03-04T09:15:56","date_gmt":"2026-03-04T09:15:56","guid":{"rendered":"https:\/\/i-cte.org\/robot\/?p=727"},"modified":"2026-03-04T09:15:56","modified_gmt":"2026-03-04T09:15:56","slug":"sports-and-fitness","status":"publish","type":"post","link":"https:\/\/i-cte.org\/robot\/sports-and-fitness\/","title":{"rendered":"Sports and fitness"},"content":{"rendered":"\n<!-- \u2705 ICTE Chatbot Lesson \u2014 Personal Best (B1) Unit 10 (WP-safe single block) -->\n<div id=\"icte-u10\">\n\n  <!-- \u2705 TOP MENU -->\n  <nav class=\"icte-menu\" aria-label=\"Unit 10 lesson navigation\">\n    <a href=\"#\" class=\"is-current\" data-view=\"conversation\">Conversation<\/a>\n    <a href=\"#\" data-view=\"vocab\">Vocabulary<\/a>\n    <a href=\"#\" data-view=\"grammar\">Grammar<\/a>\n    <a href=\"#\" data-view=\"discussion\">Discussion<\/a>\n    <a href=\"#\" data-view=\"reading\">Reading<\/a>\n    <a href=\"#\" data-view=\"listening\">Listening<\/a>\n    <a href=\"#\" data-view=\"problemsolving\">Problem-solving<\/a>\n    <a href=\"#\" data-view=\"progress\">Progress<\/a>\n  <\/nav>\n\n  <section class=\"icte-shell\" aria-label=\"ICTE Unit 10 practice\">\n\n    <!-- \u2705 Header -->\n    <header class=\"icte-hero\">\n      <div class=\"icte-hero__text\">\n        <h2>Unit 10 \u2014 Sports and fitness<\/h2>\n        <p class=\"muted\">\n          Practice <b>past perfect<\/b>, <b>reported speech<\/b>, <b>sports &#038; competitions<\/b>, <b>parts of the body<\/b>,\n          and speaking skills for <b>making inquiries<\/b>.\n        <\/p>\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        <label class=\"icte-label\">\n          Voice (Narrator)\n          <select id=\"icteVoiceSelect\" class=\"icte-select\" aria-label=\"Voice selection\"><\/select>\n        <\/label>\n\n        <div class=\"icte-small muted\">\n          Tip: If you don\u2019t see voices yet, click anywhere once and wait 2\u20133 seconds.\n        <\/div>\n      <\/div>\n    <\/header>\n\n    <!-- \u2705 Views -->\n    <main class=\"icte-main\">\n\n      <!-- ===================== -->\n      <!-- \u2705 1) CONVERSATION -->\n      <!-- ===================== -->\n      <section class=\"view is-active\" data-view=\"conversation\" aria-label=\"Conversation practice\">\n        <div class=\"card\">\n          <div class=\"card-h\">\n            <h3>1) Conversation (Voice Interactive)<\/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=\"convReset\">Reset<\/button>\n            <\/div>\n          <\/div>\n\n          <p class=\"muted\">\n            Speak your answers. Try to use <b>past perfect<\/b> (had + past participle) and sports vocabulary.\n            Example: <i>I was disappointed because I\u2019d lost the game.<\/i>\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 (optional)...\" autocomplete=\"off\" \/>\n            <button class=\"btn\" id=\"convSend\" type=\"button\">Send<\/button>\n            <button class=\"btn ghost\" id=\"convHear\" type=\"button\">\ud83d\udd0a Read last question<\/button>\n          <\/div>\n\n          <div class=\"muted icte-small\">\n            Targets: past perfect + sports &#038; competitions vocabulary (win, beat, score, medal, trophy, race, referee\/umpire, crowd\u2026).\n          <\/div>\n        <\/div>\n      <\/section>\n\n      <!-- ===================== -->\n      <!-- \u2705 2) VOCABULARY -->\n      <!-- ===================== -->\n      <section class=\"view\" data-view=\"vocab\" aria-label=\"Vocabulary practice\">\n        <div class=\"card\">\n          <div class=\"card-h\">\n            <h3>2) Vocabulary (Sports + Body)<\/h3>\n            <div class=\"card-actions\">\n              <button class=\"btn mini\" type=\"button\" data-say=\"vocab-instr\">\ud83d\udd0a Read instructions<\/button>\n              <button class=\"btn mini ghost\" type=\"button\" id=\"vocabReset\">Reset<\/button>\n            <\/div>\n          <\/div>\n\n          <p class=\"muted\">\n            Match the words to the meanings, then complete the sentences using the same vocabulary.\n          <\/p>\n\n          <div class=\"grid2\">\n            <div>\n              <h4 class=\"h4\">A) Matching<\/h4>\n              <div id=\"vocabMatch\" class=\"stack\"><\/div>\n              <button class=\"btn\" id=\"vocabCheck\" type=\"button\">Check<\/button>\n              <div class=\"feedback\" id=\"vocabFb\" aria-live=\"polite\"><\/div>\n            <\/div>\n\n            <div>\n              <h4 class=\"h4\">B) Use the words (Fill in the blanks)<\/h4>\n              <div id=\"vocabUse\" class=\"stack\"><\/div>\n              <button class=\"btn\" id=\"vocabUseCheck\" type=\"button\">Check<\/button>\n              <div class=\"feedback\" id=\"vocabUseFb\" aria-live=\"polite\"><\/div>\n            <\/div>\n          <\/div>\n        <\/div>\n      <\/section>\n\n      <!-- ===================== -->\n      <!-- \u2705 3) GRAMMAR -->\n      <!-- ===================== -->\n      <section class=\"view\" data-view=\"grammar\" aria-label=\"Grammar practice\">\n        <div class=\"card\">\n          <div class=\"card-h\">\n            <h3>3) Grammar \u2014 Past perfect + Reported speech<\/h3>\n            <div class=\"card-actions\">\n              <button class=\"btn mini\" type=\"button\" data-say=\"gram-instr\">\ud83d\udd0a Read instructions<\/button>\n              <button class=\"btn mini ghost\" type=\"button\" id=\"gramReset\">Reset<\/button>\n            <\/div>\n          <\/div>\n\n          <div class=\"note\">\n            <b>Past perfect:<\/b> had + past participle (earlier past action).<br>\n            <b>Reported speech:<\/b> verbs usually move one tense back (e.g., <i>\u201cI\u2019m tired\u201d \u2192 He said he was tired<\/i>).\n          <\/div>\n\n          <div id=\"gramQuiz\" class=\"stack\"><\/div>\n          <button class=\"btn\" id=\"gramCheck\" type=\"button\">Check<\/button>\n          <div class=\"feedback\" id=\"gramFb\" aria-live=\"polite\"><\/div>\n        <\/div>\n      <\/section>\n\n      <!-- ===================== -->\n      <!-- \u2705 4) DISCUSSION -->\n      <!-- ===================== -->\n      <section class=\"view\" data-view=\"discussion\" aria-label=\"Discussion practice\">\n        <div class=\"card\">\n          <div class=\"card-h\">\n            <h3>4) Discussion (Voice or typing)<\/h3>\n            <div class=\"card-actions\">\n              <button class=\"btn mini\" type=\"button\" data-say=\"disc-instr\">\ud83d\udd0a Read instructions<\/button>\n              <button class=\"btn mini ghost\" type=\"button\" id=\"discNext\">Next question<\/button>\n            <\/div>\n          <\/div>\n\n          <p class=\"muted\">\n            Answer with reasons. Include at least <b>2 vocabulary items<\/b> and at least <b>one<\/b> target grammar form:\n            <b>past perfect<\/b> or <b>reported speech<\/b>.\n          <\/p>\n\n          <div class=\"qa\">\n            <div class=\"q\" id=\"discQ\">Click <b>Next question<\/b> to begin.<\/div>\n            <textarea id=\"discA\" class=\"textarea\" rows=\"4\" placeholder=\"Write your answer here...\"><\/textarea>\n            <div class=\"row\">\n              <button class=\"btn\" id=\"discCheck\" type=\"button\">Check my language<\/button>\n              <button class=\"btn ghost\" id=\"discSpeakQ\" type=\"button\">\ud83d\udd0a Read question<\/button>\n            <\/div>\n            <div class=\"feedback\" id=\"discFb\" aria-live=\"polite\"><\/div>\n          <\/div>\n        <\/div>\n      <\/section>\n\n      <!-- ===================== -->\n      <!-- \u2705 5) READING -->\n      <!-- ===================== -->\n      <section class=\"view\" data-view=\"reading\" aria-label=\"Reading comprehension\">\n        <div class=\"card\">\n          <div class=\"card-h\">\n            <h3>5) Reading \u2014 \u201cThe toughest race ever?\u201d (1904 Marathon)<\/h3>\n            <div class=\"card-actions\">\n              <button class=\"btn mini\" type=\"button\" data-say=\"read-instr\">\ud83d\udd0a Read instructions<\/button>\n              <button class=\"btn mini\" type=\"button\" id=\"readTextBtn\">\ud83d\udd0a Read the text<\/button>\n            <\/div>\n          <\/div>\n\n          <p class=\"muted\">\n            Read the text, then answer the questions (main idea + evidence + past perfect).\n          <\/p>\n\n          <article class=\"reading\" id=\"readingText\">\n            <div class=\"reading-title\">The toughest race ever? (shortened)<\/div>\n            <div class=\"reading-p\"><b>1<\/b> The 1904 Olympic Marathon was held on a very hot day in St. Louis. The roads were dusty and dangerous.<\/div>\n            <div class=\"reading-p\"><b>2<\/b> William Garcia started coughing after he\u2019d breathed in too much dust and had to quit the race.<\/div>\n            <div class=\"reading-p\"><b>3<\/b> Len Tau finished ninth, but wild dogs had chased him for over a mile in the wrong direction.<\/div>\n            <div class=\"reading-p\"><b>4<\/b> Fred Lorz crossed the finish line first, but he\u2019d ridden in a car for 11 miles and was disqualified.<\/div>\n            <div class=\"reading-p\"><b>5<\/b> Tom Hicks finished next. His friends helped him near the end, and he was given the gold medal.<\/div>\n          <\/article>\n\n          <div class=\"grid2\">\n            <div>\n              <h4 class=\"h4\">A) Main idea<\/h4>\n              <div class=\"qitem\">\n                <div class=\"qtext\">1) Why was the marathon \u201cthe toughest race ever\u201d?<\/div>\n                <select class=\"icte-select\" id=\"rQ1\">\n                  <option value=\"\">Choose\u2026<\/option>\n                  <option value=\"A\">A: Because it was cold and easy.<\/option>\n                  <option value=\"B\">B: Because it had heat, dust, cheating, and dangerous conditions.<\/option>\n                  <option value=\"C\">C: Because nobody finished.<\/option>\n                <\/select>\n              <\/div>\n\n              <h4 class=\"h4\" style=\"margin-top:14px;\">B) Evidence (paragraph number)<\/h4>\n              <div id=\"readShort\" class=\"stack\"><\/div>\n\n              <button class=\"btn\" id=\"readCheck\" type=\"button\">Check<\/button>\n              <div class=\"feedback\" id=\"readFb\" aria-live=\"polite\"><\/div>\n            <\/div>\n\n            <div>\n              <h4 class=\"h4\">C) Past perfect check<\/h4>\n              <div id=\"readPP\" class=\"stack\"><\/div>\n              <button class=\"btn\" id=\"readPPCheck\" type=\"button\">Check<\/button>\n              <div class=\"feedback\" id=\"readPPFb\" aria-live=\"polite\"><\/div>\n            <\/div>\n          <\/div>\n        <\/div>\n      <\/section>\n\n      <!-- ===================== -->\n      <!-- \u2705 6) LISTENING -->\n      <!-- ===================== -->\n      <section class=\"view\" data-view=\"listening\" aria-label=\"Listening comprehension\">\n        <div class=\"card\">\n          <div class=\"card-h\">\n            <h3>6) Listening \u2014 Making inquiries (City Fitness)<\/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 listening (TTS)<\/button>\n              <button class=\"btn mini ghost\" type=\"button\" id=\"listenStop\">\u23f9 Stop<\/button>\n            <\/div>\n          <\/div>\n\n          <p class=\"muted\">\n            Click <b>Play listening<\/b>. Listen and complete the web page information (prices + off-peak hours + rules).\n          <\/p>\n\n          <div id=\"listenGaps\" class=\"stack\"><\/div>\n          <button class=\"btn\" id=\"listenCheck\" type=\"button\">Check<\/button>\n          <div class=\"feedback\" id=\"listenFb\" aria-live=\"polite\"><\/div>\n        <\/div>\n      <\/section>\n\n      <!-- ===================== -->\n      <!-- \u2705 7) PROBLEM-SOLVING -->\n      <!-- ===================== -->\n      <section class=\"view\" data-view=\"problemsolving\" aria-label=\"Problem-solving tasks\">\n        <div class=\"card\">\n          <div class=\"card-h\">\n            <h3>7) Problem-solving (Sports + Gym scenarios)<\/h3>\n            <div class=\"card-actions\">\n              <button class=\"btn mini\" type=\"button\" data-say=\"prob-instr\">\ud83d\udd0a Read instructions<\/button>\n              <button class=\"btn mini ghost\" type=\"button\" id=\"probReset\">Reset<\/button>\n            <\/div>\n          <\/div>\n\n          <p class=\"muted\">\n            Choose the best solution. Then write one sentence using <b>past perfect<\/b> or <b>reported speech<\/b>.\n          <\/p>\n\n          <div id=\"probSet\" class=\"stack\"><\/div>\n          <button class=\"btn\" id=\"probCheck\" type=\"button\">Check<\/button>\n          <div class=\"feedback\" id=\"probFb\" aria-live=\"polite\"><\/div>\n        <\/div>\n      <\/section>\n\n      <!-- ===================== -->\n      <!-- \u2705 8) 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=\"pVocab\">0<\/div>\n              <div class=\"muted\">Vocab used (detected)<\/div>\n            <\/div>\n          <\/div>\n\n          <div class=\"note\">\n            Completion is saved in your browser (local storage). If you change device\/browser, progress resets.\n          <\/div>\n\n          <h4 class=\"h4\">Vocabulary Bank (Unit 10)<\/h4>\n          <div class=\"bank\" id=\"vocabBank\"><\/div>\n        <\/div>\n      <\/section>\n\n    <\/main>\n  <\/section>\n\n  <style>\n    \/* ===== WP-SAFE STYLES (scoped) ===== *\/\n    #icte-u10 *{ box-sizing:border-box; }\n    #icte-u10{\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-u10 .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-u10 .icte-menu a{\n      display:inline-block;\n      color:#fff;\n      text-decoration:none;\n      font-weight:700;\n      padding:8px 10px;\n      border-radius:999px;\n      margin:0 3px;\n      opacity:.92;\n      transition:.15s;\n    }\n    #icte-u10 .icte-menu a:hover{ opacity:1; background:rgba(255,255,255,.14); }\n    #icte-u10 .icte-menu a.is-current{ background:#fff; color:var(--dark); opacity:1; }\n\n    #icte-u10 .icte-shell{ max-width:1100px; margin:0 auto; }\n    #icte-u10 .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-u10 .icte-hero{ grid-template-columns:1fr; } }\n\n    #icte-u10 h2{ margin:0 0 6px 0; font-size:22px; }\n    #icte-u10 .muted{ color:var(--muted); }\n\n    #icte-u10 .icte-hero__controls{\n      border-left:1px dashed var(--line);\n      padding-left:14px;\n    }\n    @media (max-width: 920px){\n      #icte-u10 .icte-hero__controls{ border-left:none; padding-left:0; border-top:1px dashed var(--line); padding-top:12px; }\n    }\n\n    #icte-u10 .icte-row{ display:flex; gap:8px; flex-wrap:wrap; }\n    #icte-u10 .icte-label{ display:block; font-size:12px; font-weight:800; margin-top:10px; }\n    #icte-u10 .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-u10 .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:800;\n    }\n    #icte-u10 .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-u10 .dot.on{\n      background:#22c55e;\n      box-shadow:0 0 0 4px rgba(34,197,94,.18);\n    }\n\n    #icte-u10 .btn{\n      border:none;\n      padding:10px 12px;\n      border-radius:12px;\n      background:var(--green);\n      color:#fff;\n      font-weight:800;\n      cursor:pointer;\n      transition:.15s;\n    }\n    #icte-u10 .btn:hover{ filter:brightness(.95); transform:translateY(-1px); }\n    #icte-u10 .btn:active{ transform:translateY(0); }\n    #icte-u10 .btn.ghost{\n      background:#fff;\n      color:#111827;\n      border:1px solid var(--line);\n    }\n    #icte-u10 .btn.mini{ padding:8px 10px; border-radius:10px; font-size:13px; }\n    #icte-u10 .icte-small{ font-size:12px; }\n\n    #icte-u10 .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-u10 .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-u10 .card-h h3{ margin:0; font-size:18px; }\n    #icte-u10 .card-actions{ display:flex; gap:8px; flex-wrap:wrap; justify-content:flex-end; }\n\n    #icte-u10 .view{ display:none; }\n    #icte-u10 .view.is-active{ display:block; }\n\n    #icte-u10 .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-u10 .msg{ margin:10px 0; display:flex; gap:10px; align-items:flex-start; }\n    #icte-u10 .who{\n      min-width:72px;\n      font-weight:900;\n      font-size:12px;\n      color:#93c5fd;\n      text-transform:uppercase;\n      letter-spacing:.06em;\n    }\n    #icte-u10 .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    }\n    #icte-u10 .msg.user .who{ color:#86efac; }\n    #icte-u10 .msg.user .bubble{ background:rgba(34,197,94,.10); border-color:rgba(34,197,94,.18); }\n\n    #icte-u10 .chatbar{\n      margin-top:10px;\n      display:flex;\n      gap:8px;\n      align-items:center;\n    }\n    #icte-u10 .input{\n      flex:1;\n      padding:12px 12px;\n      border:1px solid var(--line);\n      border-radius:12px;\n      outline:none;\n    }\n    #icte-u10 .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    #icte-u10 .grid2{\n      display:grid;\n      grid-template-columns:1fr 1fr;\n      gap:14px;\n      margin-top:10px;\n    }\n    @media (max-width: 920px){ #icte-u10 .grid2{ grid-template-columns:1fr; } }\n\n    #icte-u10 .stack{ display:flex; flex-direction:column; gap:10px; }\n    #icte-u10 .h4{ margin:0 0 6px 0; font-size:15px; }\n    #icte-u10 .qitem{\n      padding:10px;\n      border:1px solid var(--line);\n      border-radius:14px;\n      background:#fafafa;\n    }\n    #icte-u10 .qtext{ font-weight:800; margin-bottom:8px; }\n    #icte-u10 .row{ display:flex; gap:8px; flex-wrap:wrap; margin-top:10px; }\n\n    #icte-u10 .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:700;\n      display:none;\n      white-space:pre-line;\n    }\n    #icte-u10 .feedback.ok{ display:block; border-color:rgba(34,197,94,.35); background:#ecfdf5; }\n    #icte-u10 .feedback.bad{ display:block; border-color:rgba(239,68,68,.35); background:#fef2f2; }\n\n    #icte-u10 .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-u10 .reading{\n      border:1px solid var(--line);\n      border-radius:14px;\n      padding:12px;\n      background:#fff;\n      margin-top:10px;\n    }\n    #icte-u10 .reading-title{ font-weight:900; margin-bottom:8px; }\n    #icte-u10 .reading-p{ padding:8px 0; border-top:1px dashed var(--line); }\n    #icte-u10 .reading-p:first-of-type{ border-top:none; }\n\n    #icte-u10 .qa .q{\n      padding:12px;\n      border-radius:14px;\n      border:1px solid var(--line);\n      background:#fff;\n      font-weight:900;\n    }\n\n    #icte-u10 .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-u10 .progress-grid{ grid-template-columns:1fr; } }\n    #icte-u10 .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-u10 .pnum{ font-size:28px; font-weight:1000; }\n\n    #icte-u10 .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-u10 .tag{\n      border:1px solid var(--line);\n      background:#f9fafb;\n      padding:6px 10px;\n      border-radius:999px;\n      font-weight:800;\n      font-size:13px;\n    }\n  <\/style>\n\n  <script>\n    (function(){\n      const root = document.getElementById('icte-u10');\n      if(!root) return;\n\n      \/* ========= Helpers ========= *\/\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 lower = (s)=> norm(s).toLowerCase();\n      const esc = (s)=> (s||\"\").replace(\/[&<>\"']\/g, m=>({ \"&\":\"&amp;\",\"<\":\"&lt;\",\">\":\"&gt;\",'\"':\"&quot;\",\"'\":\"&#039;\" }[m]));\n      const shuffle = (arr)=> arr.map(v=>({v, r:Math.random()})).sort((a,b)=>a.r-b.r).map(o=>o.v);\n\n      \/* ========= Navigation ========= *\/\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\n      \/* ========= Speech (TTS + ASR) ========= *\/\n      const micDot = qs('#icteMicDot');\n      const micStatus = qs('#icteMicStatus');\n      const voiceSel = qs('#icteVoiceSelect');\n      const btnStartVoice = qs('#icteStartVoice');\n      const btnStopVoice = qs('#icteStopVoice');\n\n      let chosenVoice = null;\n      let recognition = null;\n      let listening = false;\n      let lastPromptSpoken = \"\";\n\n      function loadVoices(){\n        if(!window.speechSynthesis) return;\n        const all = speechSynthesis.getVoices() || [];\n        let list = all.filter(v => (v.lang||\"\").toLowerCase().startsWith(\"en\"));\n        if(list.length === 0) list = all;\n\n        voiceSel.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          voiceSel.appendChild(opt);\n        });\n\n        chosenVoice = list[0] || null;\n        voiceSel.onchange = ()=>{\n          const idx = parseInt(voiceSel.value,10);\n          chosenVoice = list[idx] || chosenVoice;\n        };\n      }\n      if(window.speechSynthesis){\n        loadVoices();\n        speechSynthesis.onvoiceschanged = loadVoices;\n      }\n\n      function speak(text){\n        const t = norm(text);\n        if(!t || !window.speechSynthesis) return;\n        speechSynthesis.cancel();\n        const u = new SpeechSynthesisUtterance(t);\n        if(chosenVoice) u.voice = chosenVoice;\n        u.rate = 1.02; u.pitch = 1.0; u.volume = 1.0;\n        speechSynthesis.speak(u);\n      }\n      function stopSpeak(){ if(window.speechSynthesis) speechSynthesis.cancel(); }\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\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\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 your answers.\");\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 = ()=>{\n          if(listening){ try{ recognition.start(); }catch(_){ } }\n        };\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      \/* ========= Progress ========= *\/\n      const LS_KEY = \"icte_u10_progress_v1\";\n      const progress = JSON.parse(localStorage.getItem(LS_KEY) || \"{}\");\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      \/* ========= Content: Vocabulary ========= *\/\n      const vocabSports = [\n        {word:\"athlete\", def:\"a person who is good at sports\"},\n        {word:\"umpire \/ referee\", def:\"the official who controls a game\"},\n        {word:\"crowd\", def:\"the people watching an event\"},\n        {word:\"trophy\", def:\"a prize cup for winning\"},\n        {word:\"medal\", def:\"a metal prize (gold\/silver\/bronze)\"},\n        {word:\"race\", def:\"a competition to run\/ride fast\"},\n        {word:\"beat\", def:\"win against a person\/team\"},\n        {word:\"win\", def:\"be the best and get the prize\"},\n        {word:\"score\", def:\"get points or a goal\"},\n        {word:\"cheat\", def:\"break rules to win unfairly\"},\n        {word:\"spectator\", def:\"a person who watches a sport\"},\n        {word:\"warm up\", def:\"do easy exercise before harder activity\"}\n      ];\n\n      const vocabBody = [\n        {word:\"shoulder\", def:\"top part of the arm\"},\n        {word:\"neck\", def:\"between head and body\"},\n        {word:\"chest\", def:\"front part of the body above stomach\"},\n        {word:\"wrist\", def:\"joint between hand and arm\"},\n        {word:\"elbow\", def:\"joint in the middle of the arm\"},\n        {word:\"knee\", def:\"joint in the middle of the leg\"},\n        {word:\"muscle\", def:\"tissue that helps you move\"},\n        {word:\"ankle\", def:\"joint between foot and leg\"},\n        {word:\"back\", def:\"the rear of your body\"},\n        {word:\"heart\", def:\"organ that pumps blood\"}\n      ];\n\n      const vocabAll = [...vocabSports, ...vocabBody];\n\n      \/\/ render bank\n      const bank = qs('#vocabBank');\n      bank.innerHTML = vocabAll.map(v=>`<span class=\"tag\" title=\"${esc(v.def)}\">${esc(v.word)}<\/span>`).join(\"\");\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\n        qs('#pDone').textContent = String(done);\n        qs('#pScore').textContent = String(avg) + \"%\";\n\n        const used = new Set();\n        const txt = (progress._vocabUsedText||\"\");\n        vocabAll.forEach(w=>{\n          if(lower(txt).includes(lower(w.word))) used.add(w.word);\n        });\n        qs('#pVocab').textContent = String(used.size);\n      }\n\n      \/* ========= Instruction TTS ========= *\/\n      const SAY = {\n        \"conv-instr\": \"Conversation. Answer using past perfect and sports vocabulary. Try: I was disappointed because I had lost the game.\",\n        \"vocab-instr\": \"Vocabulary. Match sports and body words to meanings, then complete the sentences.\",\n        \"gram-instr\": \"Grammar. Choose the correct past perfect or reported speech form.\",\n        \"disc-instr\": \"Discussion. Use at least two vocabulary items and one past perfect sentence or one reported speech sentence.\",\n        \"read-instr\": \"Reading. Read about the 1904 Olympic marathon. Answer main idea and evidence questions.\",\n        \"list-instr\": \"Listening. Listen to Marc calling City Fitness. Fill in prices, hours, and rules.\",\n        \"prob-instr\": \"Problem-solving. Choose the best option. Then write one sentence using past perfect or reported speech.\"\n      };\n      qsa('[data-say]').forEach(btn=>{\n        btn.addEventListener('click', ()=>{\n          const k = btn.getAttribute('data-say');\n          if(SAY[k]) speak(SAY[k]);\n        });\n      });\n\n      \/* ========= 1) Conversation ========= *\/\n      const convChat = qs('#convChat');\n      const convText = qs('#convText');\n      const convSend = qs('#convSend');\n      const convHear = qs('#convHear');\n      const convReset = qs('#convReset');\n\n      let convStep = 0;\n      const convSteps = [\n        {\n          bot: \"Hi! Welcome to Unit 10. Do you prefer watching sports or taking part in sports? Give one reason.\",\n          check: (a)=> a.length >= 12,\n          tips: \"Give one reason. Use a sport word (game, match, race, crowd, athlete).\"\n        },\n        {\n          bot: \"Tell me about a time you lost. Use past perfect: I was \u2026 because I had \u2026\",\n          check: (a)=> \/\\b(had|hadn\u2019t)\\b\/i.test(a) && \/\\b(was|were)\\b\/i.test(a),\n          tips: \"Example: I was disappointed because I\u2019d lost the match.\"\n        },\n        {\n          bot: \"Use these words in one sentence: crowd + score (simple past + past perfect).\",\n          check: (a)=> \/crowd\/i.test(a) && \/score\/i.test(a) && \/\\bhad\\b\/i.test(a),\n          tips: \"Example: The crowd cheered because he\u2019d scored a goal.\"\n        },\n        {\n          bot: \"Injuries: Say one sentence with a body word (knee\/shoulder\/ankle) and past perfect.\",\n          check: (a)=> \/(knee|shoulder|ankle|wrist|elbow|chest|neck|muscle|back)\/i.test(a) && \/\\bhad\\b\/i.test(a),\n          tips: \"Example: I couldn\u2019t play because I\u2019d hurt my knee.\"\n        },\n        {\n          bot: \"Reported speech: Report this sentence: \u201cI can\u2019t play today.\u201d Start with: He said that\u2026\",\n          check: (a)=> \/^he said\/i.test(a) && \/(couldn\u2019t|could not)\/i.test(a),\n          tips: \"Example: He said that he couldn\u2019t play that day.\"\n        }\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      function botAsk(){\n        const step = convSteps[convStep];\n        if(!step){\n          addMsg(\"Bot\",\"Great work! You finished Unit 10 conversation. Try other sections.\");\n          speak(\"Great work! You finished Unit 10 conversation. Try other sections.\");\n          markDone(\"conversation\", 100);\n          return;\n        }\n        addMsg(\"Bot\", step.bot);\n        lastPromptSpoken = step.bot;\n        speak(step.bot);\n      }\n\n      function scoreAnswer(a){\n        const step = convSteps[convStep];\n        if(!step) return {ok:true, msg:\"\"};\n        const ok = step.check(norm(a));\n        const msg = ok ? \"\u2705 Good. Nice use of the target language.\" : \"\u26a0\ufe0f Try again. \" + step.tips;\n        return {ok, msg};\n      }\n\n      function handleConversationInput(text){\n        const a = norm(text);\n        if(!a) return;\n        addMsg(\"You\", a);\n\n        progress._vocabUsedText = (progress._vocabUsedText||\"\") + \" \" + a;\n        saveProgress();\n\n        const {ok, msg} = scoreAnswer(a);\n        addMsg(\"Bot\", msg);\n        speak(msg);\n\n        if(ok){ convStep++; setTimeout(botAsk, 450); }\n        else { setTimeout(()=> speak(\"Please try again. \" + convSteps[convStep].bot), 450); }\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', ()=> speak(lastPromptSpoken || \"No question yet.\"));\n      convReset.addEventListener('click', ()=>{\n        convChat.innerHTML = \"\";\n        convStep = 0;\n        addMsg(\"Bot\",\"Ready! Start Voice (optional) and answer. Let\u2019s begin.\");\n        botAsk();\n      });\n\n      addMsg(\"Bot\",\"Ready! Start Voice (optional) and answer. Let\u2019s begin.\");\n      botAsk();\n\n      \/* ========= Route voice transcripts ========= *\/\n      function activeViewName(){\n        const v = qs('.view.is-active');\n        return v ? v.getAttribute('data-view') : \"conversation\";\n      }\n      function handleVoiceTranscript(t){\n        const text = norm(t);\n        if(!text) return;\n\n        const v = activeViewName();\n        if(v === \"conversation\"){ handleConversationInput(text); return; }\n        if(v === \"discussion\"){\n          const ta = qs('#discA');\n          ta.value = (ta.value ? (ta.value + \" \") : \"\") + text;\n          speak(\"I heard: \" + text);\n          return;\n        }\n        const input = qs('.view.is-active input.input');\n        if(input){ input.value = text; speak(\"I heard: \" + text); return; }\n        speak(\"I heard: \" + text);\n      }\n\n      \/* ========= 2) Vocabulary ========= *\/\n      const vocabMatch = qs('#vocabMatch');\n      const vocabFb = qs('#vocabFb');\n      const vocabUse = qs('#vocabUse');\n      const vocabUseFb = qs('#vocabUseFb');\n\n      const matchSet = shuffle([\n        {word:\"trophy\", def:\"a prize cup for winning\"},\n        {word:\"medal\", def:\"a metal prize (gold\/silver\/bronze)\"},\n        {word:\"umpire \/ referee\", def:\"the official who controls a game\"},\n        {word:\"crowd\", def:\"the people watching an event\"},\n        {word:\"score\", def:\"get points or a goal\"},\n        {word:\"beat\", def:\"win against a person\/team\"},\n        {word:\"knee\", def:\"joint in the middle of the leg\"},\n        {word:\"wrist\", def:\"joint between hand and arm\"},\n        {word:\"shoulder\", def:\"top part of the arm\"},\n        {word:\"ankle\", def:\"joint between foot and leg\"}\n      ]);\n\n      function renderVocabMatching(){\n        const defs = shuffle(matchSet.map(x=>x.def));\n        vocabMatch.innerHTML = matchSet.map((x,i)=>{\n          const options = ['<option value=\"\">Choose meaning\u2026<\/option>']\n            .concat(defs.map(d=>`<option value=\"${esc(d)}\">${esc(d)}<\/option>`))\n            .join(\"\");\n          return `\n            <div class=\"qitem\">\n              <div class=\"qtext\">${i+1}) <b>${esc(x.word)}<\/b><\/div>\n              <select class=\"icte-select\" data-ans=\"${esc(x.def)}\">${options}<\/select>\n            <\/div>\n          `;\n        }).join(\"\");\n      }\n      renderVocabMatching();\n\n      qs('#vocabCheck').addEventListener('click', ()=>{\n        const sels = qsa('#vocabMatch 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        vocabFb.className = \"feedback \" + (pct>=70 ? \"ok\":\"bad\");\n        vocabFb.textContent = `Score: ${correct}\/${sels.length} (${pct}%).`;\n        markDone(\"vocab_match\", pct);\n      });\n\n      const useItems = [\n        {sent:\"The ____ cheered loudly when the player scored.\", ans:\"crowd\"},\n        {sent:\"She won a gold ____ at the Olympic Games.\", ans:\"medal\"},\n        {sent:\"The team lifted the ____ after they had won the final.\", ans:\"trophy\"},\n        {sent:\"He couldn\u2019t continue because he had hurt his ____.\", ans:\"knee\"},\n        {sent:\"The ____ said the ball was out.\", ans:\"umpire \/ referee\"},\n        {sent:\"They celebrated because their striker had ____ a goal.\", ans:\"scored\"}\n      ];\n\n      function renderVocabUse(){\n        const bank2 = shuffle(useItems.map(x=>x.ans));\n        vocabUse.innerHTML = useItems.map((x,i)=>{\n          const opts = ['<option value=\"\">Choose\u2026<\/option>']\n            .concat(bank2.map(w=>`<option value=\"${esc(w)}\">${esc(w)}<\/option>`)).join(\"\");\n          return `\n            <div class=\"qitem\">\n              <div class=\"qtext\">${i+1}) ${esc(x.sent)}<\/div>\n              <select class=\"icte-select\" data-ans=\"${esc(x.ans)}\">${opts}<\/select>\n            <\/div>\n          `;\n        }).join(\"\");\n      }\n      renderVocabUse();\n\n      qs('#vocabUseCheck').addEventListener('click', ()=>{\n        const sels = qsa('#vocabUse 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        vocabUseFb.className = \"feedback \" + (pct>=70 ? \"ok\":\"bad\");\n        vocabUseFb.textContent = `Score: ${correct}\/${sels.length} (${pct}%).`;\n        markDone(\"vocab_use\", pct);\n      });\n\n      qs('#vocabReset').addEventListener('click', ()=>{\n        renderVocabMatching();\n        renderVocabUse();\n        vocabFb.style.display=\"none\";\n        vocabUseFb.style.display=\"none\";\n      });\n\n      \/* ========= 3) Grammar ========= *\/\n      const gramQuiz = qs('#gramQuiz');\n      const gramFb = qs('#gramFb');\n\n      const gramItems = [\n        {q:\"I was disappointed because I ____ the game. (lose)\", opts:[\"lost\",\"had lost\",\"have lost\"], ans:\"had lost\"},\n        {q:\"They started celebrating because he ____ a goal. (score)\", opts:[\"scored\",\"had scored\",\"has scored\"], ans:\"had scored\"},\n        {q:\"He couldn\u2019t play because he ____ his ankle. (hurt)\", opts:[\"hurt\",\"had hurt\",\"hurts\"], ans:\"had hurt\"},\n        {q:\"When I got to the gym, my friend ____ home. (go)\", opts:[\"went\",\"had gone\",\"has gone\"], ans:\"had gone\"},\n\n        {q:\"Direct: \u201cI can\u2019t do HIIT at my age.\u201d \u2192 Rosa said that she ____ do HIIT at her age.\", opts:[\"can't\",\"couldn\u2019t\",\"won\u2019t\"], ans:\"couldn\u2019t\"},\n        {q:\"Direct: \u201cI go to the gym to meet my friends.\u201d \u2192 Kurt said that he ____ to the gym to meet his friends.\", opts:[\"goes\",\"went\",\"had gone\"], ans:\"went\"},\n        {q:\"Direct: \u201cI\u2019ve tried it.\u201d \u2192 Barry said that he ____ tried it.\", opts:[\"has\",\"had\",\"have\"], ans:\"had\"},\n        {q:\"Direct: \u201cI\u2019ll tell my husband.\u201d \u2192 Jessica said that she ____ tell her husband.\", opts:[\"will\",\"would\",\"is going to\"], ans:\"would\"}\n      ];\n\n      function renderGrammar(){\n        gramQuiz.innerHTML = gramItems.map((it,i)=>{\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\">${i+1}) ${esc(it.q)}<\/div>\n              <select class=\"icte-select\" data-ans=\"${esc(it.ans)}\">${options}<\/select>\n            <\/div>\n          `;\n        }).join(\"\");\n      }\n      renderGrammar();\n\n      qs('#gramCheck').addEventListener('click', ()=>{\n        const sels = qsa('#gramQuiz 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        gramFb.className = \"feedback \" + (pct>=70 ? \"ok\":\"bad\");\n        gramFb.textContent = `Score: ${correct}\/${sels.length} (${pct}%).`;\n        markDone(\"grammar\", pct);\n      });\n\n      qs('#gramReset').addEventListener('click', ()=>{\n        renderGrammar();\n        gramFb.style.display=\"none\";\n      });\n\n      \/* ========= 4) Discussion ========= *\/\n      const discQ = qs('#discQ');\n      const discA = qs('#discA');\n      const discFb = qs('#discFb');\n\n      const discQs = [\n        \"Do you prefer team sports or individual sports? Give reasons using at least two sport words.\",\n        \"Have you ever cheated or seen cheating in sports? Explain using past perfect (had\u2026).\",\n        \"What injuries do athletes often get? Use at least one body word + advice.\",\n        \"Tell a short story: the crowd was excited because\u2026 (use past perfect).\",\n        \"Report what a friend said about the gym or exercise (use: He\/She said that\u2026).\"\n      ];\n      let discIdx = -1;\n\n      function nextDiscQ(){\n        discIdx = (discIdx + 1) % discQs.length;\n        discQ.textContent = discQs[discIdx];\n        discA.value = \"\";\n        discFb.style.display = \"none\";\n        speak(discQs[discIdx]);\n      }\n      qs('#discNext').addEventListener('click', nextDiscQ);\n      qs('#discSpeakQ').addEventListener('click', ()=> speak(discQs[Math.max(0,discIdx)] || \"Click next question to begin.\"));\n\n      function detectTargets(text){\n        const t = lower(text);\n        const vocabHits = vocabAll.filter(v => t.includes(lower(v.word))).map(v=>v.word);\n        const hasPP = \/\\b(had|hadn\u2019t|hadn't)\\b\/i.test(text);\n        const hasRS = \/\\b(said|told)\\b\/i.test(text) && \/\\b(that)\\b\/i.test(text);\n        return { vocabHits, hasPP, hasRS };\n      }\n\n      qs('#discCheck').addEventListener('click', ()=>{\n        const ans = norm(discA.value);\n        if(!ans){\n          discFb.className = \"feedback bad\";\n          discFb.textContent = \"Please write or speak an answer first.\";\n          return;\n        }\n\n        progress._vocabUsedText = (progress._vocabUsedText||\"\") + \" \" + ans;\n        saveProgress();\n\n        const {vocabHits, hasPP, hasRS} = detectTargets(ans);\n        const okV = vocabHits.length >= 2;\n        const okG = hasPP || hasRS;\n\n        let score = 0;\n        if(okV) score += 50;\n        if(okG) score += 50;\n\n        discFb.className = \"feedback \" + (score>=70 ? \"ok\":\"bad\");\n        discFb.textContent =\n          `Detected vocab: ${vocabHits.slice(0,10).join(\", \") || \"none\"}\\n` +\n          `Grammar: ${okG ? \"\u2705 past perfect or reported speech detected\" : \"\u26a0\ufe0f add past perfect (had\u2026) OR reported speech (said that\u2026)\"}\\n` +\n          `Score: ${score}%`;\n\n        markDone(\"discussion\", score);\n      });\n\n      \/* ========= 5) Reading ========= *\/\n      const readFb = qs('#readFb');\n      const readShort = qs('#readShort');\n      const readShortFb = qs('#readPPFb');\n\n      const shortItems = [\n        {q:\"Which paragraph mentions dust and quitting the race?\", ans:\"2\"},\n        {q:\"Which paragraph mentions wild dogs chasing a runner?\", ans:\"3\"},\n        {q:\"Which paragraph mentions cheating and riding in a car?\", ans:\"4\"},\n        {q:\"Which paragraph says Tom Hicks was given the gold medal?\", ans:\"5\"}\n      ];\n\n      function renderReadShort(){\n        readShort.innerHTML = shortItems.map((it,i)=>`\n          <div class=\"qitem\">\n            <div class=\"qtext\">${i+1}) ${esc(it.q)}<\/div>\n            <select class=\"icte-select\" data-ans=\"${esc(it.ans)}\">\n              <option value=\"\">Choose\u2026<\/option>\n              <option>1<\/option><option>2<\/option><option>3<\/option><option>4<\/option><option>5<\/option>\n            <\/select>\n          <\/div>\n        `).join(\"\");\n      }\n      renderReadShort();\n\n      const readPP = qs('#readPP');\n      const readPPFb = qs('#readPPFb');\n\n      const ppItems = [\n        {q:\"William Garcia started coughing after he\u2019d ____ in too much dust.\", ans:\"breathed\"},\n        {q:\"Len Tau was disappointed because dogs had ____ him.\", ans:\"chased\"},\n        {q:\"The crowd celebrated, but Lorz had ____ 11 miles in a car.\", ans:\"ridden\"}\n      ];\n\n      function renderReadPP(){\n        readPP.innerHTML = ppItems.map((it,i)=>`\n          <div class=\"qitem\">\n            <div class=\"qtext\">${i+1}) ${esc(it.q)}<\/div>\n            <select class=\"icte-select\" data-ans=\"${esc(it.ans)}\">\n              <option value=\"\">Choose\u2026<\/option>\n              <option value=\"breathed\">breathed<\/option>\n              <option value=\"chased\">chased<\/option>\n              <option value=\"ridden\">ridden<\/option>\n              <option value=\"ran\">ran<\/option>\n              <option value=\"took\">took<\/option>\n            <\/select>\n          <\/div>\n        `).join(\"\");\n      }\n      renderReadPP();\n\n      qs('#readCheck').addEventListener('click', ()=>{\n        let score = 0;\n        const q1 = qs('#rQ1');\n        const ok1 = q1.value === \"B\";\n        if(ok1) score += 40;\n        q1.style.borderColor = ok1 ? \"rgba(34,197,94,.6)\" : \"rgba(239,68,68,.6)\";\n\n        const sels = qsa('#readShort 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        score += Math.round((correct \/ sels.length) * 60);\n\n        readFb.className = \"feedback \" + (score>=70 ? \"ok\":\"bad\");\n        readFb.textContent = `Score: ${score}%.`;\n        markDone(\"reading\", score);\n      });\n\n      qs('#readPPCheck').addEventListener('click', ()=>{\n        const sels = qsa('#readPP 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        readPPFb.className = \"feedback \" + (pct>=70 ? \"ok\":\"bad\");\n        readPPFb.textContent = `Score: ${correct}\/${sels.length} (${pct}%).`;\n        markDone(\"reading_pp\", pct);\n      });\n\n      qs('#readTextBtn').addEventListener('click', ()=>{\n        const text = qsa('#readingText .reading-p').map(p=>p.textContent).join(\" \");\n        speak(\"Reading text. \" + text);\n      });\n\n      \/* ========= 6) Listening (TTS + gaps) ========= *\/\n      const listeningScript =\n        \"Hello, City Fitness. How can I help you? \" +\n        \"Marc asks about joining. The standard membership is ninety nine dollars per month, with a twelve month contract and no refund once you sign. \" +\n        \"Off peak membership is fifty nine dollars per month with no contract. Off peak hours are Monday to Friday ten a.m. to twelve p.m. and three p.m. to six p.m. \" +\n        \"On weekends, off peak members can enter from two p.m. until closing. \" +\n        \"Off peak members pay an additional fifteen dollars per month for the swimming pool, sauna, and steam room. \" +\n        \"Swimming only membership is thirty dollars per month. Opening hours are six a.m. to nine p.m. Student discount is fifteen percent.\";\n\n      const gapItems = [\n        {q:\"1) Standard membership fee: $____ per month.\", ans:\"99\"},\n        {q:\"2) Minimum contract: ____ months.\", ans:\"12\"},\n        {q:\"3) Off-peak membership fee: $____ per month.\", ans:\"59\"},\n        {q:\"4) Off-peak hours (Mon\u2013Fri): ____ a.m. to 12:00 p.m.\", ans:\"10:00\"},\n        {q:\"5) Off-peak hours (Mon\u2013Fri): 3:00 p.m. to ____ p.m.\", ans:\"6:00\"},\n        {q:\"6) Weekend off-peak access starts at ____ p.m.\", ans:\"2:00\"},\n        {q:\"7) Extra fee for pool\/sauna\/steam room: $____ per month.\", ans:\"15\"},\n        {q:\"8) Swimming-only membership: $____ per month.\", ans:\"30\"},\n        {q:\"9) Opening hours: 6:00 a.m. to ____ p.m.\", ans:\"9:00\"},\n        {q:\"10) Student discount: ____%.\", ans:\"15\"}\n      ];\n\n      const listenGaps = qs('#listenGaps');\n      const listenFb = qs('#listenFb');\n\n      function renderListeningGaps(){\n        const wordBank = shuffle([\"99\",\"12\",\"59\",\"10:00\",\"6:00\",\"2:00\",\"15\",\"30\",\"9:00\",\"15%\"]);\n        \/\/ normalize display and matching\n        const bankNormalized = wordBank.map(w=>w.replace(\"%\",\"\"));\n        listenGaps.innerHTML =\n          `<div class=\"note\"><b>Word bank:<\/b> ${wordBank.map(w=>`<span class=\"tag\">${esc(w)}<\/span>`).join(\" \")}<\/div>` +\n          gapItems.map((it)=>{\n            const opts = ['<option value=\"\">Choose\u2026<\/option>']\n              .concat(bankNormalized.map(w=>`<option value=\"${esc(w)}\">${esc(w)}<\/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)}\">${opts}<\/select>\n              <\/div>\n            `;\n          }).join(\"\");\n      }\n      renderListeningGaps();\n\n      qs('#listenPlay').addEventListener('click', ()=> speak(listeningScript));\n      qs('#listenStop').addEventListener('click', stopSpeak);\n\n      qs('#listenCheck').addEventListener('click', ()=>{\n        const sels = qsa('#listenGaps 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        markDone(\"listening\", pct);\n      });\n\n      \/* ========= 7) Problem-solving ========= *\/\n      const probSet = qs('#probSet');\n      const probFb = qs('#probFb');\n\n      const problems = [\n        {\n          scenario: \"Scenario 1: You lost a match because your team didn\u2019t warm up. What\u2019s the best advice?\",\n          choices: [\n            \"Ignore warm-ups. Start running at full speed immediately.\",\n            \"Do a warm-up before the game and practice regularly.\",\n            \"Cheat so you can win.\"\n          ],\n          correct: 1,\n          followup: \"Write ONE sentence with past perfect (e.g., We lost because we hadn\u2019t warmed up.).\"\n        },\n        {\n          scenario: \"Scenario 2: Your friend hurt his shoulder in training. What should he do?\",\n          choices: [\n            \"Keep training hard with pain.\",\n            \"Rest and see a doctor if needed.\",\n            \"Hide it from the coach.\"\n          ],\n          correct: 1,\n          followup: \"Write ONE reported speech sentence (e.g., He said that he\u2019d hurt his shoulder.).\"\n        },\n        {\n          scenario: \"Scenario 3: You want to join a gym. You need information about off-peak hours. What\u2019s the best question?\",\n          choices: [\n            \"Give me everything now.\",\n            \"Could you tell me when the off-peak hours are?\",\n            \"Why are you so slow?\"\n          ],\n          correct: 1,\n          followup: \"Write ONE polite inquiry sentence using: Could you tell me\u2026 \/ Could I ask\u2026\"\n        }\n      ];\n\n      function renderProblems(){\n        probSet.innerHTML = problems.map((p,idx)=>{\n          const opts = p.choices.map((c,i)=>`\n            <label class=\"opt\" style=\"display:flex;gap:10px;align-items:flex-start;padding:8px 10px;border:1px solid var(--line);border-radius:12px;background:#fff;\">\n              <input type=\"radio\" name=\"prob_${idx}\" value=\"${i}\">\n              <span>${esc(c)}<\/span>\n            <\/label>\n          `).join(\"\");\n          return `\n            <div class=\"qitem\">\n              <div class=\"qtext\">${idx+1}) ${esc(p.scenario)}<\/div>\n              <div class=\"stack\" style=\"gap:8px;margin-top:8px\">${opts}<\/div>\n              <div class=\"muted icte-small\" style=\"margin-top:8px\">${esc(p.followup)}<\/div>\n              <textarea class=\"textarea\" rows=\"2\" data-followup=\"${idx}\" placeholder=\"Write your sentence here...\"><\/textarea>\n            <\/div>\n          `;\n        }).join(\"\");\n      }\n      renderProblems();\n\n      function okFollowup(idx, txt){\n        const t = txt.trim();\n        if(idx === 0) return \/\\b(had|hadn\u2019t|hadn't)\\b\/i.test(t) && t.length >= 10;\n        if(idx === 1) return \/\\b(said|told)\\b\/i.test(t) && (\/\\bhad\\b\/i.test(t) || \/\\b(hurt|injured)\\b\/i.test(t)) && t.length >= 10;\n        return \/\\bcould you\\b|\\bcould i\\b|\\bi\u2019d like to ask\\b|\\bcan i\\b\/i.test(t) && t.length >= 10;\n      }\n\n      qs('#probCheck').addEventListener('click', ()=>{\n        let score = 0;\n        const total = problems.length * 2;\n\n        problems.forEach((p,idx)=>{\n          const sel = qs(`input[name=\"prob_${idx}\"]:checked`);\n          const okChoice = sel && parseInt(sel.value,10) === p.correct;\n          if(okChoice) score++;\n\n          const ta = qs(`textarea[data-followup=\"${idx}\"]`);\n          const txt = norm(ta.value);\n          const okSentence = okFollowup(idx, txt);\n          if(okSentence) score++;\n\n          ta.style.borderColor = okSentence ? \"rgba(34,197,94,.6)\" : \"rgba(239,68,68,.6)\";\n        });\n\n        const pct = Math.round((score \/ total) * 100);\n        probFb.className = \"feedback \" + (pct>=70 ? \"ok\":\"bad\");\n        probFb.textContent =\n          `Score: ${score}\/${total} (${pct}%).\\nTip: Follow-up must match the target grammar for each scenario.`;\n        markDone(\"problemsolving\", pct);\n      });\n\n      qs('#probReset').addEventListener('click', ()=>{\n        renderProblems();\n        probFb.style.display=\"none\";\n      });\n\n      \/* ========= Init ========= *\/\n      renderProgress();\n\n    })();\n  <\/script>\n\n<\/div>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Conversation Vocabulary Grammar Discussion Reading Listening Problem-solving Progress Unit 10 \u2014 Sports and fitness Practice past perfect, reported speech, sports<\/p>\n","protected":false},"author":1,"featured_media":728,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"colormag_page_layout":"default_layout","footnotes":""},"categories":[37],"tags":[],"class_list":["post-727","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-b1"],"_links":{"self":[{"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/posts\/727","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=727"}],"version-history":[{"count":1,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/posts\/727\/revisions"}],"predecessor-version":[{"id":729,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/posts\/727\/revisions\/729"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/media\/728"}],"wp:attachment":[{"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/media?parent=727"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/categories?post=727"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/tags?post=727"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}