{"id":669,"date":"2026-01-11T07:02:03","date_gmt":"2026-01-11T07:02:03","guid":{"rendered":"https:\/\/i-cte.org\/robot\/?p=669"},"modified":"2026-01-11T07:02:03","modified_gmt":"2026-01-11T07:02:03","slug":"ielts-writing-test-4","status":"publish","type":"post","link":"https:\/\/i-cte.org\/robot\/ielts-writing-test-4\/","title":{"rendered":"IELTs &#8211; Writing &#8211; Test 4"},"content":{"rendered":"\n<!-- \u2705 IELTS WRITING \u2013 TEST 4 (WP-SAFE SINGLE BLOCK) -->\n<div id=\"icte-writing-test4\">\n\n  <!-- \u2705 TOP NAV MENU (GREEN) -->\n  <nav class=\"icte-menu\" aria-label=\"IELTS practice navigation\">\n    <a href=\"https:\/\/i-cte.org\/robot\/ielts-writing-overview\/\" class=\"is-current\">Writing<\/a>\n    <a href=\"https:\/\/i-cte.org\/robot\/ielts-writing-test-1\/\">Test 1<\/a>\n    <a href=\"https:\/\/i-cte.org\/robot\/ielts-writing-test-2\/\">Test 2<\/a>\n    <a href=\"https:\/\/i-cte.org\/robot\/ielts-writing-test-3\/\">Test 3<\/a>\n    <a href=\"https:\/\/i-cte.org\/robot\/ielts-writing-test-4\/\">Test 4<\/a>\n    <a href=\"https:\/\/i-cte.org\/robot\/ielts-listening-overview\/\">Listening<\/a>\n    <a href=\"https:\/\/i-cte.org\/robot\/ielts-reading-overview\/\">Reading<\/a>\n    <a href=\"https:\/\/i-cte.org\/robot\/ielts-speaking-overview\/\">Speaking<\/a>\n  <\/nav>\n\n  <section class=\"icte-ielts\" aria-label=\"IELTS Writing Test 4\">\n\n    <!-- \u2705 HERO -->\n    <section class=\"icte-hero\" aria-label=\"Writing header\">\n      <div class=\"icte-hero__left\">\n        <div class=\"icte-badges\">\n          <span class=\"icte-badge\">IELTS Writing<\/span>\n          <span class=\"icte-badge icte-badge--blue\">Test 4<\/span>\n        <\/div>\n\n        <h2 class=\"icte-hero__title\">IELTS Writing Practice \u2013 Test 4<\/h2>\n        <p class=\"icte-hero__sub\">\n          Choose <strong>Task 1<\/strong> (20 minutes) or <strong>Task 2<\/strong> (40 minutes).\n          Use the <strong>timer<\/strong>, <strong>word count<\/strong>, <strong>band-score panel<\/strong>,\n          <strong>vocabulary bank<\/strong>, <strong>outline<\/strong>, <strong>sample answers<\/strong>, and <strong>feedback<\/strong>.\n        <\/p>\n\n        <div class=\"icte-hero__note\">\n          Tip: Task 1 needs a clear <strong>overview<\/strong> + comparisons. Task 2 needs a clear <strong>position<\/strong> + reasons + examples.\n        <\/div>\n      <\/div>\n\n      <div class=\"icte-hero__right\">\n        <div class=\"icte-robotCard\" aria-label=\"Writing illustration\">\n          <img decoding=\"async\"\n            src=\"https:\/\/i-cte.org\/robot\/wp-content\/uploads\/2026\/01\/4.webp\"\n            alt=\"A friendly robot helping with IELTS writing\"\n            onerror=\"this.style.display='none';\"\n            loading=\"lazy\"\n          \/>\n          <div class=\"icte-robotOverlay\">\n            <div class=\"t1\">Writing Coach<\/div>\n            <div class=\"t2\">Band \u2022 Vocab \u2022 Outline \u2022 Feedback<\/div>\n          <\/div>\n        <\/div>\n\n        <div class=\"icte-heroHint\" role=\"note\">\n          <strong>Note:<\/strong> Draft autosaves in this browser (localStorage).\n        <\/div>\n      <\/div>\n    <\/section>\n\n    <!-- \u2705 TASK SELECT + TIMER -->\n    <section class=\"icte-ielts__panel\" aria-label=\"Choose task\">\n      <div class=\"icte-ielts__panelHead\">\n        <h3 class=\"icte-ielts__h3\">Choose Task<\/h3>\n        <div class=\"icte-ielts__headRight\">\n          <span class=\"icte-pill\" data-el=\"taskPill\">Task: Task 1<\/span>\n          <span class=\"icte-pill\" data-el=\"timePill\">Time: 20:00<\/span>\n          <span class=\"icte-pill\" data-el=\"wordPill\">Words: 0<\/span>\n        <\/div>\n      <\/div>\n\n      <div class=\"icte-ielts__qArea\">\n        <div class=\"icte-modeRow\">\n          <button class=\"icte-btn icte-btn--primary is-active\" type=\"button\" data-action=\"task\" data-task=\"t1\">Task 1<\/button>\n          <button class=\"icte-btn icte-btn--ghost\" type=\"button\" data-action=\"task\" data-task=\"t2\">Task 2<\/button>\n\n          <span class=\"icte-sep\"><\/span>\n\n          <button class=\"icte-btn icte-btn--ghost\" type=\"button\" data-action=\"toggle-panel\" data-panel=\"vocab\">Vocabulary &amp; Phrases<\/button>\n          <button class=\"icte-btn icte-btn--ghost\" type=\"button\" data-action=\"toggle-panel\" data-panel=\"outline\">Suggested Outline<\/button>\n        <\/div>\n\n        <div class=\"icte-timerRow\" style=\"margin-top:.85rem;\">\n          <button class=\"icte-btn icte-btn--info\" type=\"button\" data-action=\"start\">\u25b6 Start<\/button>\n          <button class=\"icte-btn icte-btn--ghost\" type=\"button\" data-action=\"pause\" disabled>\u23f8 Pause<\/button>\n          <button class=\"icte-btn icte-btn--danger\" type=\"button\" data-action=\"reset\">\u21ba Reset Timer<\/button>\n          <span class=\"icte-small\" data-el=\"timerHint\" style=\"opacity:.9\"><\/span>\n        <\/div>\n\n        <div class=\"icte-warn\" data-el=\"timeUpBox\" style=\"display:none;margin-top:.85rem;\">\n          Time is up. You can keep writing, but try to finish and quickly proofread now.\n        <\/div>\n\n        <div class=\"icte-note\" style=\"margin-top:.85rem;\">\n          <strong>Auto band\/feedback is only an estimate<\/strong>. It is not an official IELTS score.\n        <\/div>\n      <\/div>\n    <\/section>\n\n    <!-- \u2705 PROMPT -->\n    <section class=\"icte-ielts__panel\" aria-label=\"Task prompt\">\n      <div class=\"icte-ielts__panelHead\">\n        <h3 class=\"icte-ielts__h3\">Task Prompt<\/h3>\n        <div class=\"icte-ielts__headRight\">\n          <button class=\"icte-btn icte-btn--ghost\" type=\"button\" data-action=\"toggle-prompt\">Hide Prompt<\/button>\n        <\/div>\n      <\/div>\n      <div class=\"icte-ielts__reading\" data-el=\"promptArea\"><\/div>\n    <\/section>\n\n    <!-- \u2705 RESOURCE PANELS -->\n    <section class=\"icte-ielts__panel\" aria-label=\"Learning tools\">\n      <div class=\"icte-ielts__panelHead\">\n        <h3 class=\"icte-ielts__h3\">Learning Tools<\/h3>\n        <div class=\"icte-ielts__headRight\">\n          <span class=\"icte-pill\" data-el=\"openPanelPill\">Open: None<\/span>\n        <\/div>\n      <\/div>\n\n      <div class=\"icte-ielts__qArea\">\n        <div class=\"icte-accordion\">\n\n          <!-- VOCAB -->\n          <div class=\"icte-accItem\" data-panel=\"vocab\">\n            <div class=\"icte-accTop\">\n              <div class=\"icte-accTitle\">Vocabulary &amp; Phrases (study only)<\/div>\n              <div class=\"icte-accHint\">Tip: Use 2\u20134 strong phrases naturally (don\u2019t overuse).<\/div>\n            <\/div>\n            <div class=\"icte-card\" data-el=\"vocabArea\"><\/div>\n          <\/div>\n\n          <!-- OUTLINE -->\n          <div class=\"icte-accItem\" data-panel=\"outline\">\n            <div class=\"icte-accTop\">\n              <div class=\"icte-accTitle\">Suggested Outline (structure to copy)<\/div>\n              <div class=\"icte-accHint\">Tip: Clear structure improves Coherence &amp; Cohesion.<\/div>\n            <\/div>\n            <div class=\"icte-card\" data-el=\"outlineArea\"><\/div>\n          <\/div>\n\n          <!-- SAMPLES -->\n          <div class=\"icte-accItem\" data-panel=\"samples\">\n            <div class=\"icte-accTop\">\n              <div class=\"icte-accTitle\">Sample Answers<\/div>\n              <div class=\"icte-accHint\">Tip: Study structure (don\u2019t memorise).<\/div>\n            <\/div>\n            <div class=\"icte-card\" data-el=\"samplesArea\"><\/div>\n          <\/div>\n\n          <!-- BAND -->\n          <div class=\"icte-accItem\" data-panel=\"band\">\n            <div class=\"icte-accTop\">\n              <div class=\"icte-accTitle\">Band Score Panel (Auto-estimate + Self-adjust)<\/div>\n              <div class=\"icte-accHint\">Uses 4 IELTS criteria. Auto-score is an estimate.<\/div>\n            <\/div>\n\n            <div class=\"icte-scoreWrap\">\n              <div class=\"icte-scoreGrid\">\n                <div class=\"icte-scoreItem\">\n                  <div class=\"k\">Task Achievement \/ Task Response<\/div>\n                  <input class=\"icte-range\" type=\"range\" min=\"0\" max=\"9\" step=\"0.5\" value=\"0\" data-crit=\"ta\">\n                  <div class=\"v\"><span data-el=\"taVal\">0<\/span> \/ 9<\/div>\n                <\/div>\n                <div class=\"icte-scoreItem\">\n                  <div class=\"k\">Coherence &amp; Cohesion<\/div>\n                  <input class=\"icte-range\" type=\"range\" min=\"0\" max=\"9\" step=\"0.5\" value=\"0\" data-crit=\"cc\">\n                  <div class=\"v\"><span data-el=\"ccVal\">0<\/span> \/ 9<\/div>\n                <\/div>\n                <div class=\"icte-scoreItem\">\n                  <div class=\"k\">Lexical Resource<\/div>\n                  <input class=\"icte-range\" type=\"range\" min=\"0\" max=\"9\" step=\"0.5\" value=\"0\" data-crit=\"lr\">\n                  <div class=\"v\"><span data-el=\"lrVal\">0<\/span> \/ 9<\/div>\n                <\/div>\n                <div class=\"icte-scoreItem\">\n                  <div class=\"k\">Grammar Range &amp; Accuracy<\/div>\n                  <input class=\"icte-range\" type=\"range\" min=\"0\" max=\"9\" step=\"0.5\" value=\"0\" data-crit=\"gra\">\n                  <div class=\"v\"><span data-el=\"graVal\">0<\/span> \/ 9<\/div>\n                <\/div>\n              <\/div>\n\n              <div class=\"icte-scoreActions\">\n                <button class=\"icte-btn icte-btn--primary\" type=\"button\" data-action=\"score-writing\">Score Writing<\/button>\n                <button class=\"icte-btn icte-btn--ghost\" type=\"button\" data-action=\"reset-band\">Reset scores<\/button>\n                <span class=\"icte-pill icte-pill--big\" data-el=\"overallBand\">Overall: \u2014<\/span>\n              <\/div>\n\n              <div class=\"icte-note\" style=\"margin-top:.75rem;\">\n                Tip: After scoring, you can still move sliders manually.\n              <\/div>\n            <\/div>\n          <\/div>\n\n        <\/div>\n      <\/div>\n    <\/section>\n\n    <!-- \u2705 WRITING AREA + FEEDBACK -->\n    <section class=\"icte-ielts__panel\" aria-label=\"Write your answer\">\n      <div class=\"icte-ielts__panelHead\">\n        <h3 class=\"icte-ielts__h3\">Your Writing<\/h3>\n        <div class=\"icte-ielts__headRight\">\n          <button class=\"icte-btn icte-btn--ghost\" type=\"button\" data-action=\"copy\">Copy<\/button>\n          <button class=\"icte-btn icte-btn--ghost\" type=\"button\" data-action=\"download\">Download (.txt)<\/button>\n          <span class=\"icte-loader\" data-el=\"loader\" aria-hidden=\"true\"><\/span>\n        <\/div>\n      <\/div>\n\n      <div class=\"icte-ielts__qArea\">\n        <div class=\"icte-grid2\">\n          <div>\n            <div class=\"icte-small\">\n              <strong>Draft (autosaves)<\/strong> \u2014 write your essay here.\n            <\/div>\n\n            <textarea class=\"icte-textarea\" data-el=\"draft\" rows=\"14\" spellcheck=\"true\"\n              placeholder=\"Start writing here...\"><\/textarea>\n\n            <div class=\"icte-miniRow\">\n              <span class=\"icte-small\">Min words: <strong data-el=\"minWords\">150<\/strong><\/span>\n              <span class=\"icte-small\">Paragraphs: <strong data-el=\"paraCount\">0<\/strong><\/span>\n              <span class=\"icte-small\">Sentences: <strong data-el=\"sentCount\">0<\/strong><\/span>\n            <\/div>\n\n            <div class=\"icte-check\" data-el=\"lengthWarn\" style=\"display:none;\">\n              You are under the minimum word count. Add more details\/examples.\n            <\/div>\n\n            <div class=\"icte-actionRow\" style=\"margin-top:.85rem;\">\n              <button class=\"icte-btn icte-btn--ghost\" type=\"button\" data-action=\"toggle-panel\" data-panel=\"samples\">Sample Answers<\/button>\n              <button class=\"icte-btn icte-btn--ghost\" type=\"button\" data-action=\"toggle-panel\" data-panel=\"band\">Band Score<\/button>\n              <button class=\"icte-btn icte-btn--dark\" type=\"button\" data-action=\"clear\" title=\"Clears current task text only\">Clear task<\/button>\n\n              <span class=\"icte-sep\"><\/span>\n\n              <button class=\"icte-btn icte-btn--primary\" type=\"button\" data-action=\"feedback\">Get Feedback<\/button>\n              <button class=\"icte-btn icte-btn--ghost\" type=\"button\" data-action=\"copy-feedback\">Copy Feedback<\/button>\n              <button class=\"icte-btn icte-btn--ghost\" type=\"button\" data-action=\"download-feedback\">Download Feedback (.txt)<\/button>\n            <\/div>\n          <\/div>\n\n          <div>\n            <div class=\"icte-small\"><strong>Feedback<\/strong><\/div>\n            <div class=\"icte-card\" data-el=\"feedbackBox\">\n              <div class=\"icte-small\" data-el=\"feedbackText\" style=\"opacity:.92\">\n                Click <strong>Get Feedback<\/strong> after writing at least 120\u2013150 words.\n              <\/div>\n            <\/div>\n\n            <div class=\"icte-small\" style=\"margin-top:.85rem;\"><strong>Quick Checklist<\/strong><\/div>\n            <div class=\"icte-card\">\n              <ul class=\"icte-ul\">\n                <li><strong>Task 1:<\/strong> Intro + overview + 2 detail paragraphs with comparisons.<\/li>\n                <li><strong>Task 2:<\/strong> Discuss both views + clear opinion + reasons + examples + conclusion.<\/li>\n                <li>Use linking words and topic sentences.<\/li>\n                <li>Proofread: tense, articles, agreement, punctuation.<\/li>\n              <\/ul>\n            <\/div>\n          <\/div>\n        <\/div>\n\n      <\/div>\n    <\/section>\n\n  <\/section>\n\n  <!-- \u2705 CSS -->\n  <style>\n    #icte-writing-test4, #icte-writing-test4 *{ box-sizing:border-box; }\n    #icte-writing-test4{ font-family: Arial, sans-serif; font-size:14px; line-height:1.7; color:#0f172a; width:100%; max-width:100%; }\n\n    #icte-writing-test4 .icte-menu{\n      width:100%;\n      display:flex; flex-wrap:wrap; gap:.5rem; justify-content:center; align-items:center;\n      padding:.8rem .95rem; margin:0 0 1rem 0;\n      background:#16a34a; border-radius:14px; box-shadow:0 2px 8px rgba(0,0,0,.10);\n    }\n    #icte-writing-test4 .icte-menu a{\n      display:inline-block; text-decoration:none; font-weight:900; font-size:1rem; color:#fff;\n      padding:.6rem .9rem; border-radius:999px; border:1px solid rgba(255,255,255,.35);\n      background:rgba(255,255,255,.12);\n    }\n    #icte-writing-test4 .icte-menu a.is-current{ background:rgba(255,255,255,.22); }\n\n    #icte-writing-test4 .icte-ielts__panel{\n      border:1px solid rgba(0,0,0,.10); border-radius:14px; background:rgba(255,255,255,.85);\n      overflow:hidden; margin-bottom:1rem;\n    }\n    #icte-writing-test4 .icte-ielts__panelHead{\n      display:flex; align-items:center; justify-content:space-between; gap:.75rem;\n      padding:.9rem 1rem; border-bottom:1px solid rgba(0,0,0,.08); background:rgba(0,0,0,.03);\n      flex-wrap:wrap;\n    }\n    #icte-writing-test4 .icte-ielts__h3{ margin:0; font-weight:900; font-size:1.1rem; }\n    #icte-writing-test4 .icte-ielts__headRight{ display:flex; gap:.55rem; align-items:center; flex-wrap:wrap; }\n    #icte-writing-test4 .icte-ielts__reading{ padding:1rem 1rem 1.05rem; }\n    #icte-writing-test4 .icte-ielts__qArea{ padding:1rem 1rem 1.05rem; }\n\n    #icte-writing-test4 .icte-btn{\n      appearance:none; border:1px solid transparent; border-radius:12px;\n      padding:.7rem .95rem; font-weight:900; cursor:pointer; font:inherit; font-size:1rem;\n      transition: transform .12s ease, filter .12s ease, box-shadow .12s ease, background-color .12s ease, border-color .12s ease;\n      will-change: transform;\n    }\n    #icte-writing-test4 .icte-btn:hover{ transform: translateY(-1px); filter: brightness(1.03); box-shadow: 0 8px 18px rgba(0,0,0,.10); }\n    #icte-writing-test4 .icte-btn:active{ transform: translateY(0px) scale(0.99); box-shadow: 0 5px 12px rgba(0,0,0,.08); filter: brightness(0.98); }\n    #icte-writing-test4 .icte-btn:focus-visible{ outline: 3px solid rgba(14,165,233,.45); outline-offset: 2px; }\n    #icte-writing-test4 .icte-btn:disabled{ opacity:.55; cursor:not-allowed; }\n\n    #icte-writing-test4 .icte-btn--primary{ background:#16a34a; color:#fff; }\n    #icte-writing-test4 .icte-btn--info{ background:#0ea5e9; color:#fff; }\n    #icte-writing-test4 .icte-btn--danger{ background:#dc2626; color:#fff; }\n    #icte-writing-test4 .icte-btn--dark{ background:#334155; color:#fff; }\n    #icte-writing-test4 .icte-btn--ghost{ background:transparent; border-color:rgba(0,0,0,.20); color:inherit; }\n\n    #icte-writing-test4 .icte-pill{\n      display:inline-block; padding:.3rem .65rem; border-radius:999px;\n      border:1px solid rgba(0,0,0,.12); background:rgba(255,255,255,.75);\n      font-weight:900;\n    }\n    #icte-writing-test4 .icte-pill--big{ padding:.45rem .75rem; }\n\n    #icte-writing-test4 .icte-small{ font-size:1rem; opacity:.88; }\n    #icte-writing-test4 .icte-modeRow, #icte-writing-test4 .icte-timerRow{ display:flex; gap:.6rem; flex-wrap:wrap; align-items:center; }\n    #icte-writing-test4 .icte-grid2{ display:grid; grid-template-columns:1fr 1fr; gap:.85rem; }\n    @media (max-width: 900px){ #icte-writing-test4 .icte-grid2{ grid-template-columns:1fr; } }\n\n    #icte-writing-test4 .icte-sep{ width:1px; height:30px; background:rgba(0,0,0,.12); display:inline-block; margin:0 .2rem; }\n    @media (max-width: 720px){ #icte-writing-test4 .icte-sep{ display:none; } }\n\n    #icte-writing-test4 .icte-textarea{\n      width:100%; border-radius:14px; border:1px solid rgba(0,0,0,.12);\n      background:rgba(255,255,255,.80); padding:.95rem 1rem; font:inherit;\n      font-size:1.05rem; line-height:1.75; resize:vertical; min-height:220px;\n    }\n\n    #icte-writing-test4 .icte-miniRow{ margin-top:.55rem; display:flex; gap:.75rem; flex-wrap:wrap; }\n    #icte-writing-test4 .icte-actionRow{ display:flex; gap:.6rem; flex-wrap:wrap; align-items:center; }\n\n    #icte-writing-test4 .icte-check{\n      margin-top:.75rem; border:1px solid rgba(245,158,11,.35);\n      background:rgba(254,243,199,.65); border-radius:14px; padding:.75rem .85rem;\n      color:#7c2d12; font-weight:900;\n    }\n    #icte-writing-test4 .icte-card{\n      border:1px solid rgba(0,0,0,.10); border-radius:14px;\n      background:rgba(255,255,255,.75); padding:.85rem .95rem;\n    }\n    #icte-writing-test4 .icte-ul{ margin:.4rem 0 0 1.1rem; }\n    #icte-writing-test4 .icte-ul li{ margin:.35rem 0; }\n\n    #icte-writing-test4 .icte-note{\n      border:1px solid rgba(14,165,233,.22);\n      background: rgba(14,165,233,.10);\n      color:#1e3a8a;\n      border-radius:14px;\n      padding:.75rem .85rem;\n    }\n\n    #icte-writing-test4 .icte-accordion{ display:flex; flex-direction:column; gap:.85rem; }\n    #icte-writing-test4 .icte-accItem{\n      border:1px solid rgba(0,0,0,.10);\n      border-radius:14px;\n      background:rgba(255,255,255,.70);\n      padding:.85rem .95rem;\n      display:none !important;\n    }\n    #icte-writing-test4 .icte-accItem.is-open{ display:block !important; }\n    #icte-writing-test4 .icte-accTop{\n      display:flex; align-items:flex-start; justify-content:space-between; gap:.75rem; flex-wrap:wrap;\n      margin-bottom:.65rem;\n    }\n    #icte-writing-test4 .icte-accTitle{ font-weight:1000; font-size:1.05rem; }\n    #icte-writing-test4 .icte-accHint{ opacity:.85; }\n\n    #icte-writing-test4 .icte-chipRow{ display:flex; flex-wrap:wrap; gap:.5rem; margin-top:.45rem; }\n    #icte-writing-test4 .icte-chip{\n      display:inline-flex; gap:.45rem; align-items:center;\n      border:1px solid rgba(0,0,0,.12);\n      background:#fff;\n      border-radius:999px;\n      padding:.35rem .55rem;\n      font-weight:900;\n    }\n\n    #icte-writing-test4 .icte-scoreWrap{\n      border:1px solid rgba(14,165,233,.18);\n      background:rgba(219,234,254,.45);\n      border-radius:14px;\n      padding:.95rem 1rem;\n    }\n    #icte-writing-test4 .icte-scoreGrid{\n      display:grid;\n      grid-template-columns:repeat(2, minmax(0,1fr));\n      gap:.75rem;\n    }\n    @media (max-width: 760px){ #icte-writing-test4 .icte-scoreGrid{ grid-template-columns:1fr; } }\n    #icte-writing-test4 .icte-scoreItem{\n      border:1px solid rgba(0,0,0,.10);\n      background:#fff;\n      border-radius:14px;\n      padding:.75rem .85rem;\n    }\n    #icte-writing-test4 .icte-scoreItem .k{ font-weight:1000; }\n    #icte-writing-test4 .icte-scoreItem .v{ opacity:.88; margin-top:.25rem; font-weight:900; }\n    #icte-writing-test4 .icte-range{ width:100%; }\n    #icte-writing-test4 .icte-scoreActions{ display:flex; gap:.6rem; flex-wrap:wrap; align-items:center; margin-top:.85rem; }\n\n    #icte-writing-test4 .icte-warn{\n      border:1px solid #fecaca; background:#fff5f5; color:#7f1d1d;\n      border-radius:14px; padding:.85rem .95rem;\n    }\n\n    #icte-writing-test4 .icte-promptBox h4{ margin:.1rem 0 .35rem; font-weight:1000; }\n    #icte-writing-test4 .icte-promptImg{\n      margin-top:.75rem; width:100%; max-width:860px; height:auto;\n      border-radius:14px; border:1px solid rgba(0,0,0,.10); display:block;\n    }\n\n    #icte-writing-test4 .icte-loader{\n      width:14px; height:14px; border-radius:50%;\n      border:2px solid rgba(0,0,0,.18);\n      border-top-color: rgba(0,0,0,.55);\n      display:none; animation: icteSpin .8s linear infinite;\n    }\n    @keyframes icteSpin{ to{ transform:rotate(360deg);} }\n\n    \/* Hero *\/\n    #icte-writing-test4 .icte-hero{\n      display:grid;\n      grid-template-columns: 1.1fr .9fr;\n      gap:14px;\n      align-items:stretch;\n      border:1px solid rgba(0,0,0,.10);\n      border-radius:16px;\n      padding:14px;\n      margin-bottom:14px;\n      background:\n        radial-gradient(900px 300px at 20% -10%, rgba(34,197,94,.18), transparent 60%),\n        radial-gradient(900px 300px at 100% 0%, rgba(14,165,233,.14), transparent 55%),\n        rgba(255,255,255,.85);\n      box-shadow:0 10px 24px rgba(0,0,0,.06);\n    }\n    @media (max-width: 980px){ #icte-writing-test4 .icte-hero{ grid-template-columns:1fr; } }\n    #icte-writing-test4 .icte-badges{ display:flex; gap:8px; flex-wrap:wrap; margin-bottom:8px; }\n    #icte-writing-test4 .icte-badge{\n      display:inline-block;\n      padding:.35rem .7rem;\n      border-radius:999px;\n      font-weight:1000;\n      color:#064e3b;\n      background:rgba(34,197,94,.14);\n      border:1px solid rgba(34,197,94,.25);\n    }\n    #icte-writing-test4 .icte-badge--blue{\n      color:#1e3a8a;\n      background:rgba(14,165,233,.12);\n      border-color:rgba(14,165,233,.20);\n    }\n    #icte-writing-test4 .icte-hero__title{ margin:0 0 6px; font-weight:1000; color:#0f5132; font-size:1.35rem; }\n    #icte-writing-test4 .icte-hero__sub{ margin:0; color:#334155; opacity:.95; }\n    #icte-writing-test4 .icte-hero__note{\n      margin-top:10px;\n      color:#334155;\n      padding:.65rem .8rem;\n      border-radius:14px;\n      border:1px solid rgba(34,197,94,.22);\n      background:rgba(240,253,244,.85);\n    }\n    #icte-writing-test4 .icte-robotCard{\n      position:relative;\n      border-radius:16px;\n      overflow:hidden;\n      border:1px solid rgba(0,0,0,.10);\n      background:#f8fafc;\n      min-height: 260px;\n      box-shadow:0 12px 22px rgba(0,0,0,.08);\n    }\n    #icte-writing-test4 .icte-robotCard img{ width:100%; height:100%; object-fit:cover; display:block; }\n    #icte-writing-test4 .icte-robotOverlay{\n      position:absolute; left:0; right:0; bottom:0;\n      padding:12px 14px;\n      background: linear-gradient(180deg, transparent, rgba(2,6,23,.65));\n      color:#fff;\n    }\n    #icte-writing-test4 .icte-robotOverlay .t1{ font-weight:1000; font-size:1.05rem; }\n    #icte-writing-test4 .icte-robotOverlay .t2{ font-size:.95rem; opacity:.92; }\n    #icte-writing-test4 .icte-heroHint{\n      margin-top:10px;\n      border-radius:14px;\n      border:1px solid rgba(14,165,233,.22);\n      background: rgba(14,165,233,.10);\n      color:#1e3a8a;\n      padding:.75rem .85rem;\n    }\n  <\/style>\n\n  <script>\n  (function(){\n    function init(){\n      const root = document.getElementById(\"icte-writing-test4\");\n      if(!root) return;\n      if(root.__icteInited) return;\n      root.__icteInited = true;\n\n      const $ = (sel, base)=> (base||root).querySelector(sel);\n      const $$ = (sel, base)=> Array.from((base||root).querySelectorAll(sel));\n\n      function escapeHtml(str){\n        return (str||\"\")\n          .replace(\/&\/g,\"&amp;\").replace(\/<\/g,\"&lt;\").replace(\/>\/g,\"&gt;\")\n          .replace(\/\"\/g,\"&quot;\").replace(\/'\/g,\"&#039;\");\n      }\n\n      \/\/ \u2705 CHANGE HERE (Task 1 chart image URL in WP Media)\n      const IMG_TASK1 = \"https:\/\/i-cte.org\/robot\/wp-content\/uploads\/2026\/01\/W4.png\";\n      \/\/ \u2705 OPTIONAL (leave empty if no image)\n      const IMG_TASK2 = \"https:\/\/i-cte.org\/robot\/wp-content\/uploads\/2025\/12\/3.webp\";\n\n      const TASKS = {\n        t1:{ key:\"t1\", label:\"Task 1\", seconds:20*60, minWords:150,\n          promptHtml: `\n            <div class=\"icte-promptBox\">\n              <h4>WRITING TASK 1<\/h4>\n              <div>You should spend about <strong>20 minutes<\/strong> on this task.<\/div>\n              <div style=\"margin-top:.55rem;\">\n                The bar chart shows how frequently people in the USA ate in fast food restaurants between 2003 and 2013.\n              <\/div>\n              <div style=\"margin-top:.55rem;\">\n                <strong>Summarise<\/strong> the information by selecting and reporting the main features, and make comparisons where relevant.\n              <\/div>\n              <div style=\"margin-top:.55rem;\"><strong>Write at least 150 words.<\/strong><\/div>\n              ${IMG_TASK1 && IMG_TASK1.indexOf(\"REPLACE_\")===-1 ? `<img decoding=\"async\" class=\"icte-promptImg\" src=\"${escapeHtml(IMG_TASK1)}\" alt=\"Task 1 chart\" onerror=\"this.style.display='none';\" \/>` : ``}\n            <\/div>\n          `\n        },\n        t2:{ key:\"t2\", label:\"Task 2\", seconds:40*60, minWords:250,\n          promptHtml: `\n            <div class=\"icte-promptBox\">\n              <h4>WRITING TASK 2<\/h4>\n              <div>You should spend about <strong>40 minutes<\/strong> on this task.<\/div>\n              <div style=\"margin-top:.55rem;\"><strong>Write about the following topic:<\/strong><\/div>\n              <div style=\"margin-top:.55rem;\">\n                In a number of countries, some people think it is necessary to spend large sums of money on constructing new railway lines for very fast trains between cities.\n                Others believe the money should be spent on improving existing public transport.\n              <\/div>\n              <div style=\"margin-top:.55rem;\"><strong>Discuss both these views and give your own opinion.<\/strong><\/div>\n              <div style=\"margin-top:.55rem;\"><strong>Write at least 250 words.<\/strong><\/div>\n              ${IMG_TASK2 ? `<img decoding=\"async\" class=\"icte-promptImg\" src=\"${escapeHtml(IMG_TASK2)}\" alt=\"Task 2 image\" onerror=\"this.style.display='none';\" \/>` : ``}\n            <\/div>\n          `\n        }\n      };\n\n      const RES = {\n        t1:{\n          vocab:[\n            { cat:\"Task 1 \u2013 Frequency language\", items:[\"once a week\",\"once or twice a month\",\"several times a week\",\"a few times a year\",\"daily\",\"never\"] },\n            { cat:\"Task 1 \u2013 Trend\/reporting verbs\", items:[\"accounted for\",\"stood at\",\"rose slightly\",\"dipped\",\"remained stable\",\"shifted\"] },\n            { cat:\"Task 1 \u2013 Comparison phrases\", items:[\"by contrast\",\"in comparison\",\"a similar share\",\"the most common category\",\"remained rare\"] }\n          ],\n          outline: `\n<strong>Task 1 Outline: Overview\u2013Details<\/strong>\n<ul class=\"icte-ul\">\n  <li><strong>Introduction:<\/strong> Paraphrase what the chart shows.<\/li>\n  <li><strong>Overview:<\/strong> 2 key trends (most common categories; rare categories).<\/li>\n  <li><strong>Body 1:<\/strong> 2003 vs 2006 key figures + changes.<\/li>\n  <li><strong>Body 2:<\/strong> 2013 figures + overall comparison.<\/li>\n<\/ul>`.trim(),\n          sample: `\n<strong>Sample Answer (Task 1)<\/strong>\n<div class=\"icte-small\" style=\"opacity:.85;margin-bottom:.45rem;\">Study structure (do not memorise).<\/div>\n<pre style=\"white-space:pre-wrap;margin:0;\">\nThe bar chart compares how often people in the USA ate at fast-food restaurants in 2003, 2006 and 2013. Overall, the most common frequencies were once a week and once or twice a month, while eating every day or never was rare throughout.\n\nIn 2003, around 31% ate fast food once a week and a similar 30% did so once or twice monthly. About 17% went several times a week, whereas roughly 13% visited only a few times a year. Daily customers accounted for about 4%, and approximately 5% never ate fast food.\n\nBy 2006, weekly eating rose slightly to about 33%, becoming the leading category, while monthly visits dipped to around 25%. The share going several times a week increased to roughly 20%, and \u201ca few times a year\u201d edged up to about 15%. In 2013, the pattern shifted: monthly eating climbed to around 33%, weekly fell to about 28%, and the remaining categories changed little, with daily and never staying at only 3\u20135%.\n<\/pre>`.trim()\n        },\n        t2:{\n          vocab:[\n            { cat:\"Task 2 \u2013 Topic vocabulary\", items:[\"high-speed rail\",\"upgrade existing networks\",\"service frequency\",\"signalling\",\"last-mile connections\",\"accessibility\"] },\n            { cat:\"Task 2 \u2013 Argument language\", items:[\"supports the view that\",\"however\",\"more importantly\",\"in most cases\",\"a stronger priority\",\"overall\"] },\n            { cat:\"Task 2 \u2013 Discuss both views\", items:[\"supporters argue that\",\"others believe that\",\"while this is true\",\"despite this\",\"in my view\"] }\n          ],\n          outline: `\n<strong>Task 2 Outline: Discuss Both Views + Opinion<\/strong>\n<ul class=\"icte-ul\">\n  <li><strong>Introduction:<\/strong> Paraphrase + clear opinion.<\/li>\n  <li><strong>Body 1:<\/strong> Benefits of high-speed rail (time, economy, environment).<\/li>\n  <li><strong>Body 2:<\/strong> Benefits of upgrading existing transport (cost, equity, impact).<\/li>\n  <li><strong>Conclusion:<\/strong> Restate opinion + condition (build new lines selectively).<\/li>\n<\/ul>`.trim(),\n          sample: `\n<strong>Sample Answer (Task 2)<\/strong>\n<div class=\"icte-small\" style=\"opacity:.85;margin-bottom:.45rem;\">Study argument balance (do not memorise).<\/div>\n<pre style=\"white-space:pre-wrap;margin:0;\">\nMany governments face a choice between funding new high-speed rail lines linking major cities and upgrading existing public transport. While ultra-fast trains can transform intercity travel, I believe improving current networks should be the priority in most countries, with new lines built only where demand is proven.\n\nSupporters of high-speed rail argue that it delivers large, long-term returns. Shorter journey times can integrate labour markets, boost tourism and reduce reliance on domestic flights. In densely populated corridors, shifting passengers from cars and planes to electrified rail can also cut congestion and emissions. Moreover, flagship projects may stimulate engineering innovation and regional investment around stations.\n\nHowever, these projects are extremely expensive and often benefit a narrower group of travellers. Ticket prices tend to be high, so the main users are business passengers and higher-income commuters, while suburban and rural residents continue to endure unreliable buses, overcrowded metros and poor last-mile connections. Upgrading existing systems\u2014adding bus lanes, increasing service frequency, modernising signalling and improving accessibility\u2014usually delivers faster, cheaper benefits to far more people. Better local transport also supports equity, because it connects low-income workers to jobs, schools and healthcare.\n\nIn my view, governments should first fix what most citizens use every day, and then consider high-speed routes as a second stage. Where cities are close enough and passenger volumes are strong, a new line may be justified, but it should not come at the expense of basic reliability.\n\nOverall, spending on existing public transport is more likely to maximise social and economic benefits, while selective high-speed expansion can follow once core networks work well for everyone involved.\n<\/pre>`.trim()\n        }\n      };\n\n      \/\/ ===== State =====\n      let currentTaskKey = \"t1\";\n      let timer = null;\n      let running = false;\n      let remaining = TASKS[currentTaskKey].seconds;\n      let promptVisible = true;\n      let openPanel = \"none\";\n      let panelRendered = { vocab:false, outline:false, samples:false };\n\n      const draftEl = $('[data-el=\"draft\"]');\n      const promptArea = $('[data-el=\"promptArea\"]');\n\n      \/\/ \u2705 Prevent sample text from ever being saved\/loaded into Draft\n      function purgeSampleFromDraft(){\n        const sampleMarkers = [\n          \"Sample Answer\",\n          \"The bar chart compares how often people in the USA ate\",\n          \"Many governments face a choice between funding new high-speed rail lines\"\n        ];\n        const current = (draftEl.value || \"\").trim();\n        if(sampleMarkers.some(m => current.includes(m))){\n          draftEl.value = \"\";\n        }\n        try{\n          [\"t1\",\"t2\"].forEach(k=>{\n            const key = \"icte_writing_test4_\" + k;\n            const saved = (localStorage.getItem(key) || \"\").trim();\n            if(sampleMarkers.some(m => saved.includes(m))){\n              localStorage.removeItem(key);\n            }\n          });\n        }catch(e){}\n      }\n      purgeSampleFromDraft();\n\n      \/\/ ===== Band helpers =====\n      const BAND = { ta:\"taVal\", cc:\"ccVal\", lr:\"lrVal\", gra:\"graVal\" };\n      function clamp(n,min,max){ return Math.min(max, Math.max(min,n)); }\n      function roundHalf(n){ return Math.round(n*2)\/2; }\n      function toNum(x){ const n = parseFloat(x); return Number.isFinite(n) ? n : 0; }\n\n      function updateOverall(){\n        const overallEl = $('[data-el=\"overallBand\"]');\n        if(!overallEl) return;\n        const vals = [\"ta\",\"cc\",\"lr\",\"gra\"].map(k=>{\n          const input = root.querySelector(`input[data-crit=\"${k}\"]`);\n          return input ? toNum(input.value) : 0;\n        });\n        const hasAny = vals.some(v=>v>0);\n        if(!hasAny){ overallEl.textContent = \"Overall: \u2014\"; return; }\n        const avg = vals.reduce((a,b)=>a+b,0)\/vals.length;\n        overallEl.textContent = \"Overall: \" + String(roundHalf(avg)).replace(\/\\.0$\/,\"\");\n      }\n      function setCrit(k,val){\n        const input = root.querySelector(`input[data-crit=\"${k}\"]`);\n        const out = root.querySelector(`[data-el=\"${BAND[k]}\"]`);\n        if(!input || !out) return;\n        val = roundHalf(clamp(val,0,9));\n        input.value = String(val);\n        out.textContent = String(val);\n        updateOverall();\n      }\n      function resetBand(){\n        [\"ta\",\"cc\",\"lr\",\"gra\"].forEach(k=>setCrit(k,0));\n        const overallEl = $('[data-el=\"overallBand\"]');\n        if(overallEl) overallEl.textContent = \"Overall: \u2014\";\n      }\n\n      \/\/ ===== Text analytics =====\n      function wordsCount(text){\n        const t = (text||\"\").trim();\n        if(!t) return 0;\n        return t.split(\/\\s+\/).filter(Boolean).length;\n      }\n      function paraCount(text){\n        const blocks = (text||\"\").trim().split(\/\\n\\s*\\n\/).filter(b=>b.trim().length>0);\n        return blocks.length;\n      }\n      function sentCount(text){\n        const t = (text||\"\").replace(\/\\s+\/g,\" \").trim();\n        if(!t) return 0;\n        const m = t.match(\/[.!?]+\/g);\n        return m ? m.length : 0;\n      }\n      function tokenize(text){\n        return (text||\"\").toLowerCase()\n          .replace(\/[^a-z0-9'\\s]\/g,\" \")\n          .split(\/\\s+\/).filter(Boolean);\n      }\n      function varietyScore(text){\n        const toks = tokenize(text);\n        if(toks.length < 60) return 0;\n        const uniq = new Set(toks).size;\n        return uniq \/ toks.length;\n      }\n      function countLinkers(text){\n        const re = \/\\b(however|therefore|moreover|furthermore|in addition|also|nevertheless|in contrast|by contrast|consequently|as a result|for example|for instance|overall|in summary|to conclude|in conclusion|on the other hand|while|whereas)\\b\/gi;\n        const m = (text||\"\").match(re);\n        return m ? m.length : 0;\n      }\n      function avgSentenceLen(text){\n        const s = (text||\"\").split(\/[.!?]+\/).map(x=>x.trim()).filter(Boolean);\n        if(!s.length) return 0;\n        const lens = s.map(x=>wordsCount(x));\n        return lens.reduce((a,b)=>a+b,0)\/lens.length;\n      }\n      function repeatsPenalty(text){\n        \/\/ crude penalty for heavy repetition of common words\n        const toks = tokenize(text);\n        if(toks.length < 80) return 0;\n        const freq = {};\n        toks.forEach(t=>{ freq[t]=(freq[t]||0)+1; });\n        const top = Object.values(freq).sort((a,b)=>b-a)[0] || 0;\n        \/\/ if a single token dominates too much, penalize slightly\n        const ratio = top \/ toks.length;\n        if(ratio > 0.10) return 0.5;\n        if(ratio > 0.12) return 1.0;\n        return 0;\n      }\n\n      \/\/ ===== Better Band Estimation (can reach 9) =====\n      function estimateBand(){\n        const text = draftEl.value || \"\";\n        const w = wordsCount(text);\n        const p = paraCount(text);\n        const s = sentCount(text);\n        const link = countLinkers(text);\n        const asl = avgSentenceLen(text);\n        const v = varietyScore(text);\n        const toks = tokenize(text);\n        const longWords = toks.filter(t=>t.length>=9).length;\n        const minW = TASKS[currentTaskKey].minWords;\n\n        \/\/ Common penalties\n        const underMin = (w > 0 && w < minW);\n        const veryShort = (w > 0 && w < minW * 0.75);\n        const repPen = repeatsPenalty(text);\n\n        \/\/ --- TA\/TR (task coverage + structure signals)\n        let ta = 4.5;\n\n        \/\/ length + paragraphing\n        if(w >= minW) ta += 1.0;\n        if(!underMin && w >= minW + (currentTaskKey===\"t2\" ? 70 : 40)) ta += 0.5;\n\n        if(currentTaskKey===\"t1\"){\n          if(p >= 3) ta += 0.5;\n          if(\/\\b(overall|in general|it is clear that)\\b\/i.test(text)) ta += 1.0;\n          \/\/ years & comparisons (very light heuristic)\n          const yrs = (text.match(\/\\b(2003|2006|2013)\\b\/g)||[]).length;\n          if(yrs >= 2) ta += 0.5;\n          if(\/\\b(whereas|while|by contrast|in comparison|compared with)\\b\/i.test(text)) ta += 0.5;\n        }else{\n          if(p >= 4) ta += 0.5;\n          if(\/\\b(i believe|in my view|i think|my opinion|i would argue)\\b\/i.test(text)) ta += 0.75;\n          if(\/\\b(on the one hand|on the other hand|supporters|others believe)\\b\/i.test(text)) ta += 0.75;\n          if(\/\\b(in conclusion|overall|to conclude)\\b\/i.test(text)) ta += 0.5;\n        }\n\n        \/\/ penalties\n        if(veryShort) ta -= 1.5;\n        else if(underMin) ta -= 0.75;\n\n        ta = roundHalf(clamp(ta,0,9));\n\n        \/\/ --- CC (paragraphing + cohesion signals)\n        let cc = 4.5;\n        if(p >= (currentTaskKey===\"t2\" ? 4 : 3)) cc += 1.0;\n        if(p >= (currentTaskKey===\"t2\" ? 5 : 4)) cc += 0.5;\n        if(link >= 3) cc += 0.5;\n        if(link >= 6) cc += 0.5;\n        if(s >= (currentTaskKey===\"t2\" ? 11 : 8)) cc += 0.5;\n        if(\/\\n\\s*\\n\/.test(text) && \/\\b(first|second|finally|moreover|however)\\b\/i.test(text)) cc += 0.25;\n\n        if(veryShort) cc -= 1.0;\n        else if(underMin) cc -= 0.5;\n\n        cc = roundHalf(clamp(cc,0,9));\n\n        \/\/ --- LR (range + precision proxy)\n        let lr = 4.5;\n        if(v >= 0.44) lr += 0.5;\n        if(v >= 0.50) lr += 0.5;\n        if(v >= 0.56) lr += 0.5;\n        if(longWords >= (currentTaskKey===\"t2\" ? 18 : 12)) lr += 0.5;\n        if(longWords >= (currentTaskKey===\"t2\" ? 28 : 18)) lr += 0.5;\n        lr -= repPen;\n\n        if(veryShort) lr -= 0.75;\n        else if(underMin) lr -= 0.4;\n\n        lr = roundHalf(clamp(lr,0,9));\n\n        \/\/ --- GRA (complexity proxy + sentence control proxy)\n        let gra = 4.5;\n\n        const complex = (text.match(\/\\b(although|whereas|while|because|since|which|who|that|despite|however)\\b\/gi)||[]).length;\n        if(asl >= 12) gra += 0.5;\n        if(asl >= 16) gra += 0.5;\n        if(asl >= 20) gra += 0.25;\n        if(complex >= 4) gra += 0.5;\n        if(complex >= 8) gra += 0.5;\n        if(complex >= 12) gra += 0.25;\n\n        \/\/ crude punctuation control proxy\n        if(\/[,;:]\/.test(text)) gra += 0.25;\n\n        if(veryShort) gra -= 0.75;\n        else if(underMin) gra -= 0.4;\n\n        gra = roundHalf(clamp(gra,0,9));\n\n        return { ta, cc, lr, gra, w, p, s, link, asl, underMin };\n      }\n\n      \/\/ ===== Feedback generator =====\n      function feedbackHTML(){\n        const text = draftEl.value || \"\";\n        const w = wordsCount(text);\n        const minW = TASKS[currentTaskKey].minWords;\n        const p = paraCount(text);\n\n        if(w < 30){\n          return `\n            <div style=\"font-weight:1000;margin-bottom:.35rem;\">Not enough text yet<\/div>\n            <div>Please write a fuller response (at least 120\u2013150 words) and try again.<\/div>\n          `;\n        }\n\n        const est = estimateBand();\n        setCrit(\"ta\", est.ta);\n        setCrit(\"cc\", est.cc);\n        setCrit(\"lr\", est.lr);\n        setCrit(\"gra\", est.gra);\n\n        const overall = roundHalf((est.ta+est.cc+est.lr+est.gra)\/4);\n\n        const lenMsg = (w < minW)\n          ? `<div class=\"icte-check\" style=\"margin-top:.65rem;\">Word count is under the minimum (${w}\/${minW}). Add details\/examples.<\/div>`\n          : `<div style=\"margin-top:.35rem;opacity:.9;\">Word count: <strong>${w}<\/strong> (min ${minW})<\/div>`;\n\n        const paraTip = (currentTaskKey===\"t1\")\n          ? (p < 3 ? \"Use 3\u20134 paragraphs: Introduction + Overview + 1\u20132 detail paragraphs.\" : \"Paragraphing looks OK for Task 1. Ensure you include a clear overview.\")\n          : (p < 4 ? \"Use 4\u20135 paragraphs: Introduction + both views + opinion + Conclusion.\" : \"Paragraphing looks OK for Task 2. Ensure both views + opinion are clear.\");\n\n        const cohesionTip = (est.link < 3)\n          ? \"Add a few natural linking phrases (however, by contrast, therefore) and use referencing (this\/these) to connect ideas.\"\n          : \"Cohesive devices are present. Check that each paragraph has a clear topic sentence and logical progression.\";\n\n        const taskTip = (currentTaskKey===\"t1\")\n          ? \"Task 1: Make sure your overview highlights the biggest changes and most common frequencies.\"\n          : \"Task 2: Discuss both sides fairly, then give a clear opinion with 2\u20133 developed reasons + examples.\";\n\n        const accuracyTip = (est.asl < 12)\n          ? \"Try mixing short and longer sentences. Add a few complex sentences (although\/while\/whereas) but keep them accurate.\"\n          : \"Sentence range looks OK. Proofread for articles, agreement, punctuation, and verb tense consistency.\";\n\n        return `\n          <div style=\"font-weight:1000;font-size:1.05rem;margin-bottom:.35rem;\">\n            Estimated Overall Band: ${String(overall).replace(\/\\.0$\/,\"\")}\n          <\/div>\n          <div style=\"opacity:.92;margin-bottom:.55rem;\">\n            TA\/TR: <strong>${est.ta}<\/strong> \u2022 CC: <strong>${est.cc}<\/strong> \u2022 LR: <strong>${est.lr}<\/strong> \u2022 GRA: <strong>${est.gra}<\/strong>\n          <\/div>\n          ${lenMsg}\n          <div style=\"margin-top:.75rem;\">\n            <div style=\"font-weight:1000;margin-bottom:.25rem;\">What to improve next<\/div>\n            <ul class=\"icte-ul\" style=\"margin-top:.25rem;\">\n              <li>${taskTip}<\/li>\n              <li>${paraTip}<\/li>\n              <li>${cohesionTip}<\/li>\n              <li>${accuracyTip}<\/li>\n            <\/ul>\n          <\/div>\n          <div style=\"margin-top:.65rem;opacity:.9;\">\n            Tip: Click <strong>Band Score<\/strong> to adjust sliders manually after scoring.\n          <\/div>\n        `;\n      }\n\n      \/\/ ===== UI helpers =====\n      function storageKey(taskKey){ return \"icte_writing_test4_\" + taskKey; }\n      function fmtTime(sec){\n        sec = Math.max(0, Math.floor(sec));\n        const m = String(Math.floor(sec \/ 60)).padStart(2,\"0\");\n        const s = String(sec % 60).padStart(2,\"0\");\n        return m + \":\" + s;\n      }\n      function setTaskPill(){ $('[data-el=\"taskPill\"]').textContent = \"Task: \" + TASKS[currentTaskKey].label; }\n      function setTimePill(){ $('[data-el=\"timePill\"]').textContent = \"Time: \" + fmtTime(remaining); }\n      function setWordPill(words){ $('[data-el=\"wordPill\"]').textContent = \"Words: \" + String(words); }\n\n      function renderPrompt(){\n        promptArea.innerHTML = TASKS[currentTaskKey].promptHtml;\n        promptArea.style.display = promptVisible ? \"block\" : \"none\";\n        const btn = root.querySelector('[data-action=\"toggle-prompt\"]');\n        if(btn) btn.textContent = promptVisible ? \"Hide Prompt\" : \"Show Prompt\";\n      }\n\n      function updateStats(){\n        const text = draftEl.value || \"\";\n        const w = wordsCount(text);\n        const p = paraCount(text);\n        const s = sentCount(text);\n\n        setWordPill(w);\n        $('[data-el=\"minWords\"]').textContent = String(TASKS[currentTaskKey].minWords);\n        $('[data-el=\"paraCount\"]').textContent = String(p);\n        $('[data-el=\"sentCount\"]').textContent = String(s);\n\n        const under = w > 0 && w < TASKS[currentTaskKey].minWords;\n        const warn = $('[data-el=\"lengthWarn\"]');\n        if(warn) warn.style.display = under ? \"block\" : \"none\";\n\n        try{ localStorage.setItem(storageKey(currentTaskKey), text); }catch(e){}\n      }\n\n      function loadDraft(){\n        let t = \"\";\n        try{ t = localStorage.getItem(storageKey(currentTaskKey)) || \"\"; }catch(e){}\n        draftEl.value = t;\n        updateStats();\n      }\n\n      function setActiveTaskButton(){\n        $$('button[data-action=\"task\"]').forEach(b => b.classList.remove(\"is-active\"));\n        const active = root.querySelector(`button[data-action=\"task\"][data-task=\"${currentTaskKey}\"]`);\n        if(active) active.classList.add(\"is-active\");\n      }\n\n      function ensurePanelRendered(panel){\n        if(panel === \"vocab\" && !panelRendered.vocab){\n          const vocabArea = $('[data-el=\"vocabArea\"]');\n          vocabArea.innerHTML = \"\";\n          (RES[currentTaskKey].vocab || []).forEach(group => {\n            const wrap = document.createElement(\"div\");\n            wrap.style.marginBottom = \".85rem\";\n\n            const title = document.createElement(\"div\");\n            title.style.fontWeight = \"1000\";\n            title.style.marginBottom = \".35rem\";\n            title.textContent = group.cat;\n\n            const row = document.createElement(\"div\");\n            row.className = \"icte-chipRow\";\n\n            (group.items || []).forEach(item => {\n              const chip = document.createElement(\"span\");\n              chip.className = \"icte-chip\";\n              chip.textContent = item;\n              row.appendChild(chip);\n            });\n\n            wrap.appendChild(title);\n            wrap.appendChild(row);\n            vocabArea.appendChild(wrap);\n          });\n          panelRendered.vocab = true;\n        }\n\n        if(panel === \"outline\" && !panelRendered.outline){\n          $('[data-el=\"outlineArea\"]').innerHTML = RES[currentTaskKey].outline || \"\";\n          panelRendered.outline = true;\n        }\n\n        if(panel === \"samples\" && !panelRendered.samples){\n          $('[data-el=\"samplesArea\"]').innerHTML = RES[currentTaskKey].sample || \"\";\n          panelRendered.samples = true;\n        }\n\n        if(panel === \"band\"){\n          [\"ta\",\"cc\",\"lr\",\"gra\"].forEach(k=>{\n            const input = root.querySelector(`input[data-crit=\"${k}\"]`);\n            const out = root.querySelector(`[data-el=\"${BAND[k]}\"]`);\n            if(input && out) out.textContent = String(toNum(input.value));\n          });\n          updateOverall();\n        }\n      }\n\n      function setOpenPanel(panel){\n        openPanel = panel || \"none\";\n        $('[data-el=\"openPanelPill\"]').textContent = \"Open: \" + (openPanel === \"none\" ? \"None\" : openPanel);\n\n        $$('.icte-accItem').forEach(it => it.classList.remove(\"is-open\"));\n\n        if(openPanel !== \"none\"){\n          const target = root.querySelector(`.icte-accItem[data-panel=\"${openPanel}\"]`);\n          if(target) target.classList.add(\"is-open\");\n          ensurePanelRendered(openPanel);\n        }\n      }\n\n      \/\/ ===== Timer =====\n      function stopTimer(timeUp){\n        running = false;\n        if(timer){ clearInterval(timer); timer = null; }\n        $('[data-action=\"start\"]').disabled = false;\n        $('[data-action=\"pause\"]').disabled = true;\n        if(!timeUp) $('[data-el=\"timerHint\"]').textContent = \"Timer paused.\";\n      }\n      function tick(){\n        if(!running) return;\n        remaining -= 1;\n        setTimePill();\n        if(remaining <= 0){\n          remaining = 0;\n          setTimePill();\n          $('[data-el=\"timeUpBox\"]').style.display = \"block\";\n          $('[data-el=\"timerHint\"]').textContent = \"Time up \u2014 finish and proofread.\";\n          stopTimer(true);\n        }\n      }\n      function startTimer(){\n        if(running) return;\n        running = true;\n        $('[data-action=\"start\"]').disabled = true;\n        $('[data-action=\"pause\"]').disabled = false;\n        $('[data-el=\"timerHint\"]').textContent = \"Timer running...\";\n        timer = setInterval(tick, 1000);\n      }\n      function resetTimer(){\n        stopTimer(false);\n        remaining = TASKS[currentTaskKey].seconds;\n        setTimePill();\n        $('[data-el=\"timeUpBox\"]').style.display = \"none\";\n        $('[data-el=\"timerHint\"]').textContent = \"\";\n      }\n\n      function togglePrompt(){ promptVisible = !promptVisible; renderPrompt(); }\n\n      function switchTask(taskKey){\n        stopTimer(false);\n        currentTaskKey = taskKey;\n        remaining = TASKS[currentTaskKey].seconds;\n\n        setActiveTaskButton();\n        setTaskPill();\n        setTimePill();\n        renderPrompt();\n        loadDraft();\n\n        panelRendered = { vocab:false, outline:false, samples:false };\n        setOpenPanel(\"none\");\n        resetBand();\n\n        const fb = $('[data-el=\"feedbackText\"]');\n        if(fb){\n          fb.innerHTML = 'Click <strong>Get Feedback<\/strong> after writing at least 120\u2013150 words.';\n        }\n\n        $('[data-el=\"timeUpBox\"]').style.display = \"none\";\n        $('[data-el=\"timerHint\"]').textContent = \"\";\n      }\n\n      \/\/ ===== Actions =====\n      function clearTask(){\n        draftEl.value = \"\";\n        try{ localStorage.removeItem(storageKey(currentTaskKey)); }catch(e){}\n        updateStats();\n        resetBand();\n        const fb = $('[data-el=\"feedbackText\"]');\n        if(fb){\n          fb.innerHTML = 'Cleared \u2705 Write a new response, then click <strong>Get Feedback<\/strong>.';\n        }\n      }\n\n      function showLoader(on){\n        const el = $('[data-el=\"loader\"]');\n        if(!el) return;\n        el.style.display = on ? \"inline-block\" : \"none\";\n      }\n\n      function doFeedback(){\n        const w = wordsCount(draftEl.value || \"\");\n        if(w < 30){\n          const fb = $('[data-el=\"feedbackText\"]');\n          if(fb) fb.innerHTML = \"<strong>Please write more text first.<\/strong> (Try at least 120\u2013150 words.)\";\n          return;\n        }\n\n        showLoader(true);\n        setTimeout(()=>{\n          const fb = $('[data-el=\"feedbackText\"]');\n          if(fb) fb.innerHTML = feedbackHTML();\n          showLoader(false);\n        }, 180);\n      }\n\n      function copyText(text){\n        if(!text) return;\n        if(navigator.clipboard && navigator.clipboard.writeText){\n          navigator.clipboard.writeText(text).catch(()=>{});\n          return;\n        }\n        const ta = document.createElement(\"textarea\");\n        ta.value = text;\n        ta.style.position=\"fixed\";\n        ta.style.left=\"-9999px\";\n        document.body.appendChild(ta);\n        ta.select();\n        try{ document.execCommand(\"copy\"); }catch(e){}\n        document.body.removeChild(ta);\n      }\n\n      function downloadText(filename, text){\n        const blob = new Blob([text], {type:\"text\/plain;charset=utf-8\"});\n        const url = URL.createObjectURL(blob);\n        const a = document.createElement(\"a\");\n        a.href = url;\n        a.download = filename;\n        document.body.appendChild(a);\n        a.click();\n        document.body.removeChild(a);\n        URL.revokeObjectURL(url);\n      }\n\n      function getFeedbackPlain(){\n        const fb = $('[data-el=\"feedbackText\"]');\n        if(!fb) return \"\";\n        return fb.innerText || fb.textContent || \"\";\n      }\n\n      \/\/ ===== Events =====\n      draftEl.addEventListener(\"input\", updateStats);\n\n      root.addEventListener(\"input\", (e)=>{\n        const range = e.target.closest('input.icte-range[data-crit]');\n        if(!range) return;\n        const key = range.getAttribute(\"data-crit\");\n        const out = root.querySelector(`[data-el=\"${BAND[key]}\"]`);\n        if(out) out.textContent = String(toNum(range.value));\n        updateOverall();\n      });\n\n      root.addEventListener(\"click\", (e)=>{\n        const btn = e.target.closest(\"[data-action]\");\n        if(!btn) return;\n        const action = btn.getAttribute(\"data-action\");\n\n        if(action === \"task\"){\n          const key = btn.getAttribute(\"data-task\");\n          if(key && TASKS[key]) switchTask(key);\n          return;\n        }\n        if(action === \"toggle-panel\"){\n          const panel = btn.getAttribute(\"data-panel\") || \"none\";\n          setOpenPanel(openPanel === panel ? \"none\" : panel);\n          return;\n        }\n        if(action === \"start\") return startTimer();\n        if(action === \"pause\") return stopTimer(false);\n        if(action === \"reset\") return resetTimer();\n        if(action === \"toggle-prompt\") return togglePrompt();\n\n        if(action === \"clear\") return clearTask();\n        if(action === \"feedback\") return doFeedback();\n\n        if(action === \"score-writing\"){\n          const est = estimateBand();\n          setCrit(\"ta\", est.ta); setCrit(\"cc\", est.cc); setCrit(\"lr\", est.lr); setCrit(\"gra\", est.gra);\n          return;\n        }\n        if(action === \"reset-band\") return resetBand();\n\n        if(action === \"copy\") return copyText(draftEl.value || \"\");\n        if(action === \"download\") return downloadText(`IELTS-Writing-${TASKS[currentTaskKey].label}.txt`, draftEl.value || \"\");\n\n        if(action === \"copy-feedback\") return copyText(getFeedbackPlain());\n        if(action === \"download-feedback\") return downloadText(`IELTS-Feedback-${TASKS[currentTaskKey].label}.txt`, getFeedbackPlain());\n      });\n\n      \/\/ ===== Init =====\n      setTaskPill();\n      setActiveTaskButton();\n      renderPrompt();\n      loadDraft();\n      setTimePill();\n      setWordPill(wordsCount(draftEl.value || \"\"));\n      setOpenPanel(\"none\");\n      resetBand();\n    }\n\n    if(document.readyState === \"loading\"){\n      document.addEventListener(\"DOMContentLoaded\", init);\n    }else{\n      init();\n    }\n  })();\n  <\/script>\n\n<\/div>\n\n","protected":false},"excerpt":{"rendered":"<p>Writing Test 1 Test 2 Test 3 Test 4 Listening Reading Speaking IELTS Writing Test 4 IELTS Writing Practice \u2013<\/p>\n","protected":false},"author":1,"featured_media":596,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"colormag_page_layout":"default_layout","footnotes":""},"categories":[25,33,34],"tags":[],"class_list":["post-669","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ielts","category-test-4","category-writing"],"_links":{"self":[{"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/posts\/669","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=669"}],"version-history":[{"count":1,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/posts\/669\/revisions"}],"predecessor-version":[{"id":670,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/posts\/669\/revisions\/670"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/media\/596"}],"wp:attachment":[{"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/media?parent=669"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/categories?post=669"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/tags?post=669"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}