{"id":886,"date":"2026-03-12T12:46:28","date_gmt":"2026-03-12T12:46:28","guid":{"rendered":"https:\/\/i-cte.org\/robot\/?p=886"},"modified":"2026-03-12T12:46:28","modified_gmt":"2026-03-12T12:46:28","slug":"spanish-interaction-shopping","status":"publish","type":"post","link":"https:\/\/i-cte.org\/robot\/spanish-interaction-shopping\/","title":{"rendered":"Spanish interaction &#8211; Shopping"},"content":{"rendered":"\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\" \/>\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" \/>\n  <title>Learn Spanish: Shopping Q&#038;A Practice<\/title>\n  <style>\n    * { box-sizing: border-box; }\n\n    body {\n      font-family: Arial, sans-serif;\n      background-color: #f4f6f8;\n      margin: 0;\n      padding: 0;\n      display: flex;\n      flex-direction: column;\n      min-height: 100vh;\n    }\n\n    .menu {\n      width: 100%;\n      background-color: #28a745;\n      padding: 10px;\n      text-align: center;\n      box-shadow: 0 2px 5px rgba(0,0,0,0.1);\n      margin-bottom: 10px;\n      overflow-x: auto;\n      white-space: nowrap;\n    }\n\n    .menu a {\n      color: #fff;\n      text-decoration: none;\n      margin: 0 8px;\n      font-weight: bold;\n      font-size: 14px;\n      display: inline-block;\n      cursor: pointer;\n      padding: 5px 8px;\n      border-radius: 6px;\n    }\n\n    .menu a.active {\n      background: #fff;\n      color: #28a745;\n    }\n\n    .menu a:hover,\n    .menu a:focus {\n      text-decoration: underline;\n      outline: 2px dashed #fff;\n      outline-offset: 4px;\n    }\n\n    .container {\n      display: flex;\n      flex-direction: column;\n      align-items: center;\n      padding: 10px;\n      flex: 1 0 auto;\n      width: 100%;\n    }\n\n    .image-chat-wrapper {\n      display: flex;\n      flex-direction: column;\n      width: 100%;\n      max-width: 1120px;\n      flex: 1;\n      gap: 16px;\n    }\n\n    .image-container {\n      flex: 1;\n      text-align: center;\n      margin-bottom: 15px;\n      background: #ffffff;\n      border-radius: 15px;\n      box-shadow: 0 0 8px rgba(0,0,0,0.1);\n      padding: 14px;\n    }\n\n    .sentence-text {\n      margin: 10px 0;\n      font-weight: bold;\n      color: #333;\n      font-size: 16px;\n    }\n\n    .word-box {\n      margin-top: 10px;\n      padding: 10px;\n      background-color: #fff;\n      border: 2px solid #28a745;\n      border-radius: 8px;\n      box-shadow: 0 0 8px rgba(0,0,0,0.1);\n      font-size: 15px;\n      font-weight: bold;\n      color: #28a745;\n      text-align: left;\n      max-height: 300px;\n      overflow-y: auto;\n      line-height: 1.7;\n    }\n\n    .chat-container {\n      background-color: #28a745;\n      border-radius: 15px;\n      box-shadow: 0 0 8px rgba(0,0,0,0.1);\n      overflow: hidden;\n      display: flex;\n      flex-direction: column;\n      max-height: 760px;\n      flex: 1;\n    }\n\n    .chat-header {\n      background-color: #1e7e34;\n      color: #fff;\n      padding: 12px;\n      text-align: center;\n      border-bottom: 1px solid #155724;\n      position: relative;\n    }\n\n    .chat-header h2 {\n      margin: 0;\n      font-size: 18px;\n    }\n\n    .chat-header .loader {\n      position: absolute;\n      right: 20px;\n      top: 50%;\n      transform: translateY(-50%);\n      display: none;\n    }\n\n    .chat-messages {\n      padding: 12px;\n      overflow-y: auto;\n      flex: 1;\n      background-color: #fff;\n      max-height: 470px;\n    }\n\n    .message {\n      margin-bottom: 10px;\n      padding: 10px;\n      border-radius: 6px;\n      font-size: 14px;\n      line-height: 1.5;\n      white-space: pre-line;\n    }\n\n    .user-message {\n      background-color: #ffc107;\n      color: #fff;\n      text-align: right;\n    }\n\n    .bot-message {\n      background-color: #17a2b8;\n      color: #fff;\n      text-align: left;\n    }\n\n    .chat-input {\n      display: flex;\n      align-items: center;\n      background-color: #c3e6cb;\n      padding: 10px;\n      justify-content: space-between;\n      flex-wrap: wrap;\n      gap: 8px;\n    }\n\n    .send-button, .stop-button {\n      background-color: #17a2b8;\n      color: #fff;\n      font-weight: bold;\n      border: none;\n      border-radius: 5px;\n      padding: 12px 16px;\n      cursor: pointer;\n      transition: background-color 0.3s;\n      flex: 1;\n      margin: 4px;\n      max-width: 170px;\n      font-size: 16px;\n    }\n\n    .stop-button { background-color: #dc3545; }\n\n    .send-button:hover,\n    .stop-button:hover,\n    .send-button:focus,\n    .stop-button:focus {\n      background-color: #218838;\n      outline: none;\n    }\n\n    .voice-selection {\n      margin: 10px 0;\n      width: 100%;\n      max-width: 320px;\n    }\n\n    .voice-selection label {\n      display: block;\n      margin-bottom: 5px;\n      font-weight: bold;\n      color: #333;\n    }\n\n    .voice-selection select {\n      width: 100%;\n      padding: 8px;\n      border-radius: 5px;\n      border: 1px solid #ccc;\n      font-size: 14px;\n    }\n\n    .loader {\n      border: 4px solid #f3f3f3;\n      border-top: 4px solid #17a2b8;\n      border-radius: 50%;\n      width: 20px;\n      height: 20px;\n      animation: spin 1s linear infinite;\n      display: inline-block;\n      margin-left: 10px;\n    }\n\n    @keyframes spin {\n      0% { transform: translateY(-50%) rotate(0deg); }\n      100% { transform: translateY(-50%) rotate(360deg); }\n    }\n\n    .face-panel {\n      width: 100%;\n      min-height: 330px;\n      display: flex;\n      flex-direction: column;\n      align-items: center;\n      justify-content: center;\n      background: radial-gradient(circle at center, #111 0%, #000 70%);\n      border-radius: 15px;\n      padding: 16px 10px;\n      overflow: hidden;\n      position: relative;\n    }\n\n    .speech-bubble {\n      position: relative;\n      background: white;\n      color: #111;\n      border-radius: 20px;\n      padding: 12px 18px;\n      max-width: 340px;\n      opacity: 0;\n      transform: translateY(10px) scale(0.92);\n      transition: all 0.25s ease;\n      box-shadow: 0 10px 30px rgba(255,255,255,0.08);\n      margin-bottom: 12px;\n      font-weight: 600;\n    }\n\n    .speech-bubble.visible {\n      opacity: 1;\n      transform: translateY(0) scale(1);\n    }\n\n    .speech-bubble::after {\n      content: '';\n      position: absolute;\n      bottom: -15px;\n      left: 50%;\n      transform: translateX(-50%);\n      border: 10px solid transparent;\n      border-top-color: white;\n    }\n\n    .face-container {\n      filter: drop-shadow(0 0 45px rgba(255,255,255,0.08));\n      transition: transform 0.15s ease;\n      will-change: transform;\n    }\n\n    .face-shell {\n      transition: transform 0.2s ease;\n      transform-origin: 150px 150px;\n    }\n\n    .eye-white,\n    .pupil,\n    .eyebrow,\n    .mouth-path,\n    .cheek,\n    .tear,\n    .eyelid,\n    .sparkle {\n      transition: all 0.18s ease;\n    }\n\n    @media (orientation: portrait) {\n      .image-chat-wrapper { flex-direction: column; }\n      .image-container, .chat-container { width: 100%; max-width: 100%; }\n    }\n\n    @media (orientation: landscape) and (min-width: 700px) {\n      .image-chat-wrapper {\n        flex-direction: row;\n        justify-content: space-between;\n        gap: 20px;\n      }\n\n      .image-container, .chat-container {\n        width: 48%;\n        max-width: 48%;\n      }\n\n      .word-box { max-height: 240px; }\n      .chat-header h2 { font-size: 20px; }\n      .message { font-size: 15px; }\n      .send-button, .stop-button {\n        font-size: 18px;\n        padding: 14px 20px;\n        max-width: 200px;\n      }\n    }\n\n    @media (max-width: 768px) {\n      .chat-header h2 { font-size: 16px; }\n      .message { font-size: 14px; }\n      .send-button, .stop-button {\n        font-size: 16px;\n        padding: 12px 16px;\n        max-width: 150px;\n      }\n      .word-box { font-size: 15px; }\n      .voice-selection select { font-size: 14px; }\n    }\n  <\/style>\n<\/head>\n<body>\n  <div class=\"menu\">\n    <a href=\"#\" class=\"menu-link active\" data-section=\"shopping\">Shopping<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"clothes\">Clothes<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"supermarket\">Supermarket<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"fruit\">Fruit and vegetables<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"paying\">Paying<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"sizes\">Sizes and colors<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"market\">Market<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"gifts\">Gifts<\/a>\n  <\/div>\n\n  <div class=\"container\">\n    <div class=\"image-chat-wrapper\">\n      <div class=\"image-container\">\n        <div class=\"face-panel\">\n          <div id=\"face-speech-bubble\" class=\"speech-bubble\">\n            <p id=\"face-speech-text\"><\/p>\n          <\/div>\n\n          <div class=\"face-container\" id=\"face-container\">\n            <svg width=\"300\" height=\"300\" viewBox=\"0 0 300 300\" id=\"face-svg\" aria-label=\"Expressive face\">\n              <defs>\n                <radialGradient id=\"faceGlow\" cx=\"50%\" cy=\"40%\" r=\"70%\">\n                  <stop offset=\"0%\" stop-color=\"rgba(255,255,255,0.12)\" \/>\n                  <stop offset=\"100%\" stop-color=\"rgba(255,255,255,0.02)\" \/>\n                <\/radialGradient>\n                <radialGradient id=\"cheekGlow\" cx=\"50%\" cy=\"50%\" r=\"60%\">\n                  <stop offset=\"0%\" stop-color=\"rgba(255,160,160,0.8)\" \/>\n                  <stop offset=\"100%\" stop-color=\"rgba(255,160,160,0)\" \/>\n                <\/radialGradient>\n              <\/defs>\n\n              <g id=\"face-shell\" class=\"face-shell\">\n                <ellipse cx=\"150\" cy=\"150\" rx=\"120\" ry=\"140\" fill=\"url(#faceGlow)\" stroke=\"rgba(255,255,255,0.15)\" stroke-width=\"2\"\/>\n                <path id=\"left-eyebrow\" class=\"eyebrow\" d=\"M 60 80 Q 90 75 120 80\" fill=\"none\" stroke=\"white\" stroke-width=\"4\" stroke-linecap=\"round\"\/>\n                <path id=\"right-eyebrow\" class=\"eyebrow\" d=\"M 180 80 Q 210 75 240 80\" fill=\"none\" stroke=\"white\" stroke-width=\"4\" stroke-linecap=\"round\"\/>\n                <ellipse id=\"left-blush\" class=\"cheek\" cx=\"70\" cy=\"165\" rx=\"24\" ry=\"12\" fill=\"url(#cheekGlow)\" opacity=\"0\"\/>\n                <ellipse id=\"right-blush\" class=\"cheek\" cx=\"230\" cy=\"165\" rx=\"24\" ry=\"12\" fill=\"url(#cheekGlow)\" opacity=\"0\"\/>\n\n                <g id=\"left-eye-group\">\n                  <ellipse id=\"left-eye-white\" class=\"eye-white\" cx=\"90\" cy=\"110\" rx=\"30\" ry=\"24\" fill=\"white\"\/>\n                  <ellipse id=\"left-eye-lid\" class=\"eyelid\" cx=\"90\" cy=\"110\" rx=\"30\" ry=\"0\" fill=\"black\"\/>\n                  <circle id=\"left-pupil\" class=\"pupil\" cx=\"90\" cy=\"110\" r=\"11.5\" fill=\"black\"\/>\n                  <circle id=\"left-sparkle\" class=\"sparkle\" cx=\"96\" cy=\"104\" r=\"4.2\" fill=\"white\" opacity=\"0.95\"\/>\n                <\/g>\n\n                <g id=\"right-eye-group\">\n                  <ellipse id=\"right-eye-white\" class=\"eye-white\" cx=\"210\" cy=\"110\" rx=\"30\" ry=\"24\" fill=\"white\"\/>\n                  <ellipse id=\"right-eye-lid\" class=\"eyelid\" cx=\"210\" cy=\"110\" rx=\"30\" ry=\"0\" fill=\"black\"\/>\n                  <circle id=\"right-pupil\" class=\"pupil\" cx=\"210\" cy=\"110\" r=\"11.5\" fill=\"black\"\/>\n                  <circle id=\"right-sparkle\" class=\"sparkle\" cx=\"216\" cy=\"104\" r=\"4.2\" fill=\"white\" opacity=\"0.95\"\/>\n                <\/g>\n\n                <path id=\"mouth\" class=\"mouth-path\" d=\"M 100 220 Q 150 232 200 220\" fill=\"none\" stroke=\"white\" stroke-width=\"5\" stroke-linecap=\"round\"\/>\n                <ellipse id=\"mouth-inner\" cx=\"150\" cy=\"225\" rx=\"0\" ry=\"0\" fill=\"rgba(120,0,0,0.75)\" opacity=\"0\"\/>\n                <rect id=\"teeth\" x=\"122\" y=\"220\" width=\"56\" height=\"10\" rx=\"4\" fill=\"white\" opacity=\"0\"\/>\n                <path id=\"left-tear\" class=\"tear\" d=\"M 75 140 Q 69 160 75 171 Q 81 160 75 140\" fill=\"rgba(100,200,255,0)\" \/>\n                <path id=\"right-tear\" class=\"tear\" d=\"M 225 140 Q 219 160 225 171 Q 231 160 225 140\" fill=\"rgba(100,200,255,0)\" \/>\n              <\/g>\n            <\/svg>\n          <\/div>\n        <\/div>\n\n        <div class=\"sentence-text\">\n          Listen to the question and answer in simple Spanish.\n        <\/div>\n        <div id=\"word-box\" class=\"word-box\"><\/div>\n      <\/div>\n\n      <div class=\"chat-container\">\n        <div class=\"chat-header\">\n          <h2 id=\"section-title\">Shopping<\/h2>\n          <div class=\"loader\" id=\"synthesis-loader\"><\/div>\n        <\/div>\n        <div class=\"chat-messages\" id=\"chat-messages\"><\/div>\n        <div class=\"chat-input\">\n          <div class=\"voice-selection\">\n            <label for=\"spanish-voice-select\">Choose Spanish Voice:<\/label>\n            <select id=\"spanish-voice-select\" aria-label=\"Select Spanish Voice\">\n              <option value=\"\">Loading Spanish voices&#8230;<\/option>\n            <\/select>\n          <\/div>\n          <button id=\"start-btn\" class=\"send-button\">Start<\/button>\n          <button id=\"stop-btn\" class=\"stop-button\">Stop<\/button>\n        <\/div>\n      <\/div>\n    <\/div>\n  <\/div>\n\n  <div class=\"menu\">\n    <a href=\"#\" class=\"menu-link active\" data-section=\"shopping\">Shopping<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"clothes\">Clothes<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"supermarket\">Supermarket<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"fruit\">Fruit and vegetables<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"paying\">Paying<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"sizes\">Sizes and colors<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"market\">Market<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"gifts\">Gifts<\/a>\n  <\/div>\n\n  <script>\n    const sectionData = {\n      shopping: {\n        title: \"Shopping\",\n        intro: \"Hoy vamos a practicar preguntas sobre comprar.\",\n        items: [\n          {\n            questionEs: \"\u00bfTe gusta comprar?\",\n            questionEn: \"Do you like shopping?\",\n            suggestedAnswer: \"S\u00ed, me gusta comprar.\",\n            keywords: [\"s\u00ed\", \"si\", \"gusta\", \"comprar\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfCompras hoy?\",\n            questionEn: \"Do you shop today?\",\n            suggestedAnswer: \"S\u00ed, compro hoy.\",\n            keywords: [\"s\u00ed\", \"si\", \"compro\", \"hoy\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfVas a una tienda?\",\n            questionEn: \"Do you go to a store?\",\n            suggestedAnswer: \"S\u00ed, voy a una tienda.\",\n            keywords: [\"s\u00ed\", \"si\", \"voy\", \"tienda\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfCompras muchas cosas?\",\n            questionEn: \"Do you buy many things?\",\n            suggestedAnswer: \"S\u00ed, compro muchas cosas.\",\n            keywords: [\"s\u00ed\", \"si\", \"compro\", \"muchas\", \"cosas\", \"no\"]\n          }\n        ]\n      },\n\n      clothes: {\n        title: \"Clothes\",\n        intro: \"Hoy vamos a practicar preguntas sobre ropa.\",\n        items: [\n          {\n            questionEs: \"\u00bfCompras ropa?\",\n            questionEn: \"Do you buy clothes?\",\n            suggestedAnswer: \"S\u00ed, compro ropa.\",\n            keywords: [\"s\u00ed\", \"si\", \"compro\", \"ropa\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfTe gusta esta camisa?\",\n            questionEn: \"Do you like this shirt?\",\n            suggestedAnswer: \"S\u00ed, me gusta esta camisa.\",\n            keywords: [\"s\u00ed\", \"si\", \"gusta\", \"camisa\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfNecesitas zapatos?\",\n            questionEn: \"Do you need shoes?\",\n            suggestedAnswer: \"S\u00ed, necesito zapatos.\",\n            keywords: [\"s\u00ed\", \"si\", \"necesito\", \"zapatos\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfLa chaqueta es bonita?\",\n            questionEn: \"Is the jacket nice?\",\n            suggestedAnswer: \"S\u00ed, la chaqueta es bonita.\",\n            keywords: [\"s\u00ed\", \"si\", \"chaqueta\", \"bonita\", \"no\"]\n          }\n        ]\n      },\n\n      supermarket: {\n        title: \"Supermarket\",\n        intro: \"Hoy vamos a practicar preguntas sobre el supermercado.\",\n        items: [\n          {\n            questionEs: \"\u00bfVas al supermercado?\",\n            questionEn: \"Do you go to the supermarket?\",\n            suggestedAnswer: \"S\u00ed, voy al supermercado.\",\n            keywords: [\"s\u00ed\", \"si\", \"voy\", \"supermercado\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfCompras leche?\",\n            questionEn: \"Do you buy milk?\",\n            suggestedAnswer: \"S\u00ed, compro leche.\",\n            keywords: [\"s\u00ed\", \"si\", \"compro\", \"leche\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfNecesitas pan?\",\n            questionEn: \"Do you need bread?\",\n            suggestedAnswer: \"S\u00ed, necesito pan.\",\n            keywords: [\"s\u00ed\", \"si\", \"necesito\", \"pan\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfLlevas una cesta?\",\n            questionEn: \"Do you carry a basket?\",\n            suggestedAnswer: \"S\u00ed, llevo una cesta.\",\n            keywords: [\"s\u00ed\", \"si\", \"llevo\", \"cesta\", \"no\"]\n          }\n        ]\n      },\n\n      fruit: {\n        title: \"Fruit and vegetables\",\n        intro: \"Hoy vamos a practicar preguntas sobre frutas y verduras.\",\n        items: [\n          {\n            questionEs: \"\u00bfCompras fruta?\",\n            questionEn: \"Do you buy fruit?\",\n            suggestedAnswer: \"S\u00ed, compro fruta.\",\n            keywords: [\"s\u00ed\", \"si\", \"compro\", \"fruta\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfTe gustan las manzanas?\",\n            questionEn: \"Do you like apples?\",\n            suggestedAnswer: \"S\u00ed, me gustan las manzanas.\",\n            keywords: [\"s\u00ed\", \"si\", \"gustan\", \"manzanas\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfCompras tomates?\",\n            questionEn: \"Do you buy tomatoes?\",\n            suggestedAnswer: \"S\u00ed, compro tomates.\",\n            keywords: [\"s\u00ed\", \"si\", \"compro\", \"tomates\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfLas verduras son frescas?\",\n            questionEn: \"Are the vegetables fresh?\",\n            suggestedAnswer: \"S\u00ed, las verduras son frescas.\",\n            keywords: [\"s\u00ed\", \"si\", \"verduras\", \"frescas\", \"no\"]\n          }\n        ]\n      },\n\n      paying: {\n        title: \"Paying\",\n        intro: \"Hoy vamos a practicar preguntas sobre pagar.\",\n        items: [\n          {\n            questionEs: \"\u00bfPagas con tarjeta?\",\n            questionEn: \"Do you pay with card?\",\n            suggestedAnswer: \"S\u00ed, pago con tarjeta.\",\n            keywords: [\"s\u00ed\", \"si\", \"pago\", \"tarjeta\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfPagas en efectivo?\",\n            questionEn: \"Do you pay in cash?\",\n            suggestedAnswer: \"S\u00ed, pago en efectivo.\",\n            keywords: [\"s\u00ed\", \"si\", \"pago\", \"efectivo\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfEl precio es bueno?\",\n            questionEn: \"Is the price good?\",\n            suggestedAnswer: \"S\u00ed, el precio es bueno.\",\n            keywords: [\"s\u00ed\", \"si\", \"precio\", \"bueno\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfLa cuenta es alta?\",\n            questionEn: \"Is the bill high?\",\n            suggestedAnswer: \"No, la cuenta no es alta.\",\n            keywords: [\"s\u00ed\", \"si\", \"no\", \"cuenta\", \"alta\"]\n          }\n        ]\n      },\n\n      sizes: {\n        title: \"Sizes and colors\",\n        intro: \"Hoy vamos a practicar preguntas sobre tallas y colores.\",\n        items: [\n          {\n            questionEs: \"\u00bfQuieres talla grande?\",\n            questionEn: \"Do you want a big size?\",\n            suggestedAnswer: \"S\u00ed, quiero talla grande.\",\n            keywords: [\"s\u00ed\", \"si\", \"quiero\", \"talla\", \"grande\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfTe gusta el color azul?\",\n            questionEn: \"Do you like the color blue?\",\n            suggestedAnswer: \"S\u00ed, me gusta el color azul.\",\n            keywords: [\"s\u00ed\", \"si\", \"gusta\", \"azul\", \"color\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfBuscas color negro?\",\n            questionEn: \"Are you looking for black color?\",\n            suggestedAnswer: \"S\u00ed, busco color negro.\",\n            keywords: [\"s\u00ed\", \"si\", \"busco\", \"negro\", \"color\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfLa talla peque\u00f1a est\u00e1 bien?\",\n            questionEn: \"Is the small size okay?\",\n            suggestedAnswer: \"S\u00ed, la talla peque\u00f1a est\u00e1 bien.\",\n            keywords: [\"s\u00ed\", \"si\", \"talla\", \"peque\u00f1a\", \"pequena\", \"bien\", \"no\"]\n          }\n        ]\n      },\n\n      market: {\n        title: \"Market\",\n        intro: \"Hoy vamos a practicar preguntas sobre el mercado.\",\n        items: [\n          {\n            questionEs: \"\u00bfVas al mercado?\",\n            questionEn: \"Do you go to the market?\",\n            suggestedAnswer: \"S\u00ed, voy al mercado.\",\n            keywords: [\"s\u00ed\", \"si\", \"voy\", \"mercado\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfCompras pescado?\",\n            questionEn: \"Do you buy fish?\",\n            suggestedAnswer: \"S\u00ed, compro pescado.\",\n            keywords: [\"s\u00ed\", \"si\", \"compro\", \"pescado\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfHablas con el vendedor?\",\n            questionEn: \"Do you speak with the seller?\",\n            suggestedAnswer: \"S\u00ed, hablo con el vendedor.\",\n            keywords: [\"s\u00ed\", \"si\", \"hablo\", \"vendedor\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfEl mercado est\u00e1 cerca?\",\n            questionEn: \"Is the market near?\",\n            suggestedAnswer: \"S\u00ed, el mercado est\u00e1 cerca.\",\n            keywords: [\"s\u00ed\", \"si\", \"mercado\", \"cerca\", \"no\"]\n          }\n        ]\n      },\n\n      gifts: {\n        title: \"Gifts\",\n        intro: \"Hoy vamos a practicar preguntas sobre regalos.\",\n        items: [\n          {\n            questionEs: \"\u00bfCompras un regalo?\",\n            questionEn: \"Do you buy a gift?\",\n            suggestedAnswer: \"S\u00ed, compro un regalo.\",\n            keywords: [\"s\u00ed\", \"si\", \"compro\", \"regalo\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfEs para tu amigo?\",\n            questionEn: \"Is it for your friend?\",\n            suggestedAnswer: \"S\u00ed, es para mi amigo.\",\n            keywords: [\"s\u00ed\", \"si\", \"amigo\", \"para\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfEl regalo es peque\u00f1o?\",\n            questionEn: \"Is the gift small?\",\n            suggestedAnswer: \"S\u00ed, el regalo es peque\u00f1o.\",\n            keywords: [\"s\u00ed\", \"si\", \"regalo\", \"peque\u00f1o\", \"pequeno\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfTe gusta este regalo?\",\n            questionEn: \"Do you like this gift?\",\n            suggestedAnswer: \"S\u00ed, me gusta este regalo.\",\n            keywords: [\"s\u00ed\", \"si\", \"gusta\", \"regalo\", \"no\"]\n          }\n        ]\n      }\n    };\n\n    const positiveFeedback = [\n      \"Muy bien. Tu respuesta es clara.\",\n      \"Excelente. Respondiste bien.\",\n      \"Muy bien hecho. Tu respuesta es correcta.\",\n      \"Buen trabajo. La respuesta coincide con la pregunta.\",\n      \"Muy bien. Usaste palabras correctas.\"\n    ];\n\n    const partialFeedback = [\n      \"Bien. Tu respuesta est\u00e1 bien, pero puedes decir un poco m\u00e1s.\",\n      \"Buen intento. Usa una frase un poco m\u00e1s completa.\",\n      \"Vas bien. A\u00f1ade una palabra clave m\u00e1s.\",\n      \"Bien. Responde con una frase m\u00e1s clara.\",\n      \"Buen trabajo. Puedes mejorar con m\u00e1s informaci\u00f3n.\"\n    ];\n\n    const constructiveFeedback = [\n      \"Intenta responder con palabras de la pregunta.\",\n      \"Escucha otra vez y usa una respuesta simple en espa\u00f1ol.\",\n      \"Trata de usar palabras clave importantes.\",\n      \"Tu respuesta necesita m\u00e1s relaci\u00f3n con la pregunta.\",\n      \"Usa una oraci\u00f3n corta y clara en espa\u00f1ol.\"\n    ];\n\n    const stopKeywords = [\"goodbye\", \"bye\", \"stop\", \"end lesson\", \"adi\u00f3s\", \"adios\"];\n\n    let currentSection = \"shopping\";\n    let lessons = sectionData[currentSection].items;\n    let currentLessonIndex = 0;\n    let isLessonActive = false;\n    let speechRecognitionActive = false;\n    let pendingNextQuestionTimeout = null;\n    let retryCount = 0;\n    const maxRetriesPerQuestion = 2;\n\n    const speechSynthesisSupported = 'speechSynthesis' in window;\n    const SpeechRecognitionClass = window.SpeechRecognition || window.webkitSpeechRecognition;\n    const speechRecognitionSupported = !!SpeechRecognitionClass;\n\n    let voices = [];\n    let spanishVoices = [];\n    let selectedSpanishVoice = null;\n    let recognition = null;\n    let micAccessGranted = false;\n    let currentListeningItem = null;\n\n    let currentEmotion = 'neutral';\n    let isFaceTalking = false;\n    let faceTalkTimer = null;\n    let faceBubbleTimer = null;\n    let blinkTimer = null;\n    let saccadeTimer = null;\n    let breatheRAF = null;\n\n    const faceShell = document.getElementById('face-shell');\n    const mouth = document.getElementById('mouth');\n    const mouthInner = document.getElementById('mouth-inner');\n    const teeth = document.getElementById('teeth');\n    const leftEyeWhite = document.getElementById('left-eye-white');\n    const rightEyeWhite = document.getElementById('right-eye-white');\n    const leftLid = document.getElementById('left-eye-lid');\n    const rightLid = document.getElementById('right-eye-lid');\n    const leftPupil = document.getElementById('left-pupil');\n    const rightPupil = document.getElementById('right-pupil');\n    const leftSparkle = document.getElementById('left-sparkle');\n    const rightSparkle = document.getElementById('right-sparkle');\n    const leftEyebrow = document.getElementById('left-eyebrow');\n    const rightEyebrow = document.getElementById('right-eyebrow');\n    const leftBlush = document.getElementById('left-blush');\n    const rightBlush = document.getElementById('right-blush');\n    const leftTear = document.getElementById('left-tear');\n    const rightTear = document.getElementById('right-tear');\n    const faceSpeechBubble = document.getElementById('face-speech-bubble');\n    const faceSpeechText = document.getElementById('face-speech-text');\n\n    function initialize() {\n      loadVoices();\n      setFaceEmotion('neutral');\n      scheduleBlink();\n      scheduleSaccade();\n      startFaceBreathing();\n      bindSectionMenus();\n      createRecognitionInstance();\n      updateSectionUI();\n      clearWordBox();\n      addSectionHelp();\n\n      appendMessage(\"Click Start to begin the Spanish speaking practice.\", \"bot\");\n\n      document.getElementById('start-btn').onclick = async function() {\n        if (isLessonActive) return;\n        if (!speechSynthesisSupported || !speechRecognitionSupported) {\n          appendMessage(\"Your browser needs speech synthesis and speech recognition.\", \"bot\");\n          return;\n        }\n\n        clearChat();\n        clearWordBox();\n        addSectionHelp();\n        stopAllAudioAndRecognition();\n        clearPendingTimeout();\n\n        const micOk = await requestMicrophoneOnce();\n        if (!micOk) {\n          appendMessage(\"Microphone access is required for speaking practice.\", \"bot\");\n          return;\n        }\n\n        isLessonActive = true;\n        currentLessonIndex = 0;\n        retryCount = 0;\n\n        appendMessage(sectionData[currentSection].intro, 'bot');\n        faceSpeak(sectionData[currentSection].intro, \"happy\");\n        showSynthesisLoader(true);\n\n        speakSpanish(sectionData[currentSection].intro).then(() => {\n          showSynthesisLoader(false);\n          startLesson();\n        });\n      };\n\n      document.getElementById('stop-btn').onclick = function() {\n        endLesson(true);\n      };\n    }\n\n    function setFaceEmotion(emotion) {\n      const presets = {\n        neutral: {\n          mouth: 'M 100 220 Q 150 232 200 220',\n          leftEyebrow: 'M 60 80 Q 90 75 120 80',\n          rightEyebrow: 'M 180 80 Q 210 75 240 80',\n          eyeRY: 24,\n          blush: 0,\n          tears: 0,\n          teeth: 0,\n          innerOpacity: 0,\n          innerRx: 0,\n          innerRy: 0\n        },\n        happy: {\n          mouth: 'M 92 212 Q 150 278 208 212',\n          leftEyebrow: 'M 60 75 Q 90 66 120 75',\n          rightEyebrow: 'M 180 75 Q 210 66 240 75',\n          eyeRY: 17,\n          blush: 0.48,\n          tears: 0,\n          teeth: 0.92,\n          innerOpacity: 0.25,\n          innerRx: 24,\n          innerRy: 8\n        },\n        sad: {\n          mouth: 'M 112 240 Q 150 203 188 240',\n          leftEyebrow: 'M 68 72 Q 90 88 120 76',\n          rightEyebrow: 'M 180 76 Q 210 88 232 72',\n          eyeRY: 15,\n          blush: 0,\n          tears: 0.78,\n          teeth: 0,\n          innerOpacity: 0,\n          innerRx: 0,\n          innerRy: 0\n        },\n        confused: {\n          mouth: 'M 110 224 Q 140 230 160 221 Q 182 236 190 226',\n          leftEyebrow: 'M 60 86 Q 90 68 120 80',\n          rightEyebrow: 'M 180 70 Q 210 86 240 76',\n          eyeRY: 22,\n          blush: 0,\n          tears: 0,\n          teeth: 0,\n          innerOpacity: 0,\n          innerRx: 0,\n          innerRy: 0\n        }\n      };\n\n      const preset = presets[emotion] || presets.neutral;\n      currentEmotion = emotion;\n      mouth.setAttribute('d', preset.mouth);\n      leftEyebrow.setAttribute('d', preset.leftEyebrow);\n      rightEyebrow.setAttribute('d', preset.rightEyebrow);\n      leftEyeWhite.setAttribute('ry', preset.eyeRY);\n      rightEyeWhite.setAttribute('ry', preset.eyeRY);\n      leftBlush.setAttribute('opacity', preset.blush);\n      rightBlush.setAttribute('opacity', preset.blush);\n      leftTear.setAttribute('fill', `rgba(100,200,255,${preset.tears})`);\n      rightTear.setAttribute('fill', `rgba(100,200,255,${preset.tears})`);\n      teeth.setAttribute('opacity', preset.teeth);\n      mouthInner.setAttribute('opacity', preset.innerOpacity);\n      mouthInner.setAttribute('rx', preset.innerRx);\n      mouthInner.setAttribute('ry', preset.innerRy);\n    }\n\n    function blinkFace(duration = 110) {\n      const currentL = parseFloat(leftEyeWhite.getAttribute('ry'));\n      const currentR = parseFloat(rightEyeWhite.getAttribute('ry'));\n      leftLid.setAttribute('ry', currentL + 1);\n      rightLid.setAttribute('ry', currentR + 1);\n      setTimeout(() => {\n        leftLid.setAttribute('ry', 0);\n        rightLid.setAttribute('ry', 0);\n      }, duration);\n    }\n\n    function scheduleBlink() {\n      clearTimeout(blinkTimer);\n      const next = 1500 + Math.random() * 3500;\n      blinkTimer = setTimeout(() => {\n        blinkFace(90 + Math.random() * 80);\n        scheduleBlink();\n      }, next);\n    }\n\n    function idleSaccade() {\n      if (isFaceTalking) return;\n      const dx = (Math.random() * 2 - 1) * 4.5;\n      const dy = (Math.random() * 2 - 1) * 3.2;\n      leftPupil.setAttribute('cx', 90 + dx);\n      leftPupil.setAttribute('cy', 110 + dy);\n      rightPupil.setAttribute('cx', 210 + dx);\n      rightPupil.setAttribute('cy', 110 + dy);\n      leftSparkle.setAttribute('cx', 96 + dx * 0.6);\n      leftSparkle.setAttribute('cy', 104 + dy * 0.6);\n      rightSparkle.setAttribute('cx', 216 + dx * 0.6);\n      rightSparkle.setAttribute('cy', 104 + dy * 0.6);\n    }\n\n    function scheduleSaccade() {\n      clearTimeout(saccadeTimer);\n      const next = 900 + Math.random() * 1800;\n      saccadeTimer = setTimeout(() => {\n        idleSaccade();\n        scheduleSaccade();\n      }, next);\n    }\n\n    function startFaceBreathing() {\n      let t0 = performance.now();\n      function frame(now) {\n        const t = (now - t0) \/ 1000;\n        const breathe = Math.sin(t * 1.8) * 1.8;\n        const sway = Math.sin(t * 0.9) * 1.4;\n        const tilt = Math.sin(t * 0.75) * 0.8;\n        if (!isFaceTalking) {\n          faceShell.setAttribute(\n            'transform',\n            `translate(${sway.toFixed(2)}, ${breathe.toFixed(2)}) rotate(${tilt.toFixed(2)} 150 150)`\n          );\n        }\n        breatheRAF = requestAnimationFrame(frame);\n      }\n      cancelAnimationFrame(breatheRAF);\n      breatheRAF = requestAnimationFrame(frame);\n    }\n\n    function showFaceBubble(text, duration) {\n      faceSpeechText.textContent = text;\n      faceSpeechBubble.classList.add('visible');\n      clearTimeout(faceBubbleTimer);\n      faceBubbleTimer = setTimeout(() => {\n        faceSpeechBubble.classList.remove('visible');\n      }, duration + 600);\n    }\n\n    function stopFaceTalking() {\n      isFaceTalking = false;\n      clearInterval(faceTalkTimer);\n      faceTalkTimer = null;\n      setFaceEmotion(currentEmotion);\n    }\n\n    function talkingFrame(emotion) {\n      const shapes = {\n        neutral: [\n          { d: 'M 102 220 Q 150 240 198 220', rx: 14, ry: 7, teeth: 0.1, inner: 0.25 },\n          { d: 'M 96 214 Q 150 262 204 214', rx: 22, ry: 11, teeth: 0.28, inner: 0.45 }\n        ],\n        happy: [\n          { d: 'M 92 212 Q 150 280 208 212', rx: 24, ry: 10, teeth: 0.9, inner: 0.35 },\n          { d: 'M 96 216 Q 150 266 204 216', rx: 18, ry: 8, teeth: 0.75, inner: 0.28 }\n        ],\n        sad: [\n          { d: 'M 112 238 Q 150 212 188 238', rx: 10, ry: 4, teeth: 0, inner: 0.1 }\n        ],\n        confused: [\n          { d: 'M 110 224 Q 140 230 160 221 Q 182 236 190 226', rx: 10, ry: 4, teeth: 0, inner: 0.1 }\n        ]\n      };\n\n      const list = shapes[emotion] || shapes.neutral;\n      const chosen = list[Math.floor(Math.random() * list.length)];\n      mouth.setAttribute('d', chosen.d);\n      mouthInner.setAttribute('opacity', chosen.inner);\n      mouthInner.setAttribute('rx', chosen.rx);\n      mouthInner.setAttribute('ry', chosen.ry);\n      teeth.setAttribute('opacity', chosen.teeth);\n    }\n\n    function faceSpeak(text, emotion = \"neutral\", durationHint = null) {\n      const clean = (text || \"\").trim();\n      if (!clean) return;\n      clearInterval(faceTalkTimer);\n      isFaceTalking = true;\n      setFaceEmotion(emotion);\n      const duration = durationHint || Math.max(1600, clean.length * 70);\n      showFaceBubble(clean, duration);\n      faceTalkTimer = setInterval(() => talkingFrame(emotion), 120);\n      setTimeout(() => stopFaceTalking(), duration);\n    }\n\n    function bindSectionMenus() {\n      document.querySelectorAll('.menu-link').forEach(link => {\n        link.addEventListener('click', function(e) {\n          e.preventDefault();\n          switchSection(this.dataset.section);\n        });\n      });\n    }\n\n    function switchSection(section) {\n      if (!sectionData[section]) return;\n      stopAllAudioAndRecognition();\n      isLessonActive = false;\n      currentListeningItem = null;\n      currentSection = section;\n      lessons = sectionData[currentSection].items;\n      currentLessonIndex = 0;\n      retryCount = 0;\n      clearChat();\n      clearWordBox();\n      updateSectionUI();\n      addSectionHelp();\n      appendMessage(`Section changed to ${sectionData[currentSection].title}. Click Start to begin.`, 'bot');\n    }\n\n    function updateSectionUI() {\n      document.getElementById('section-title').textContent = sectionData[currentSection].title;\n      document.querySelectorAll('.menu-link').forEach(link => {\n        link.classList.toggle('active', link.dataset.section === currentSection);\n      });\n    }\n\n    function addSectionHelp() {\n      addSentenceToBox(`Section: ${sectionData[currentSection].title}`);\n      addSentenceToBox(`Answer the questions in Spanish.`);\n      addSentenceToBox(`Use simple Spanish words for this shopping topic.`);\n      addSentenceToBox(`Say a short and clear answer.`);\n      addSentenceToBox(`--------------------`);\n    }\n\n    async function requestMicrophoneOnce() {\n      if (micAccessGranted) return true;\n      if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {\n        appendMessage(\"Your browser does not support microphone access.\", \"bot\");\n        return false;\n      }\n      try {\n        const stream = await navigator.mediaDevices.getUserMedia({ audio: true });\n        stream.getTracks().forEach(track => track.stop());\n        micAccessGranted = true;\n        return true;\n      } catch (error) {\n        appendMessage(\"Microphone access was blocked. Please allow microphone access and try again.\", \"bot\");\n        faceSpeak(\"Micr\u00f3fono bloqueado.\", \"sad\");\n        return false;\n      }\n    }\n\n    function createRecognitionInstance() {\n      if (!speechRecognitionSupported) return;\n\n      recognition = new SpeechRecognitionClass();\n      recognition.lang = 'es-ES';\n      recognition.interimResults = false;\n      recognition.continuous = false;\n      recognition.maxAlternatives = 1;\n\n      recognition.onresult = function(event) {\n        if (!isLessonActive || !speechRecognitionActive || !currentListeningItem) return;\n\n        const transcript = event.results[0][0].transcript.trim();\n        const formattedInput = formatSentence(transcript);\n\n        speechRecognitionActive = false;\n        showRecognitionLoader(false);\n        appendMessage(formattedInput, 'user');\n\n        if (checkStopKeywords(formattedInput.toLowerCase())) {\n          endLesson(true);\n          return;\n        }\n\n        const feedback = analyzeResponse(formattedInput, currentListeningItem.keywords);\n        appendMessage(feedback.message, 'bot');\n        faceSpeak(feedback.shortFaceText, feedback.emotion);\n        showSynthesisLoader(true);\n\n        speakSpanish(feedback.message).then(() => {\n          showSynthesisLoader(false);\n          if (!isLessonActive) return;\n\n          if (feedback.level === \"low\" && retryCount < maxRetriesPerQuestion) {\n            retryCount++;\n            clearPendingTimeout();\n            pendingNextQuestionTimeout = setTimeout(() => {\n              appendMessage(\"Vamos otra vez con la misma pregunta.\", \"bot\");\n              faceSpeak(\"Otra vez.\", \"neutral\", 1200);\n              askCurrentQuestion();\n            }, 1200);\n            return;\n          }\n\n          currentLessonIndex++;\n          retryCount = 0;\n          clearPendingTimeout();\n          pendingNextQuestionTimeout = setTimeout(() => startLesson(), 1600);\n        });\n      };\n\n      recognition.onspeechend = function() {\n        if (speechRecognitionActive && recognition) {\n          try { recognition.stop(); } catch (e) {}\n        }\n      };\n\n      recognition.onerror = function(event) {\n        speechRecognitionActive = false;\n        showRecognitionLoader(false);\n        if (!isLessonActive || !currentListeningItem) return;\n\n        if (event.error === \"not-allowed\") {\n          micAccessGranted = false;\n          const msg = \"Microphone access was blocked. Please allow microphone access and try again.\";\n          appendMessage(msg, \"bot\");\n          faceSpeak(\"Micr\u00f3fono bloqueado.\", \"sad\");\n          return;\n        }\n\n        if (event.error === \"no-speech\") {\n          retryCount++;\n          if (retryCount <= maxRetriesPerQuestion) {\n            const msg = \"No escuch\u00e9 tu respuesta. Intenta otra vez en espa\u00f1ol.\";\n            appendMessage(msg, \"bot\");\n            faceSpeak(\"Intenta otra vez.\", \"confused\");\n            showSynthesisLoader(true);\n            speakSpanish(msg).then(() => {\n              showSynthesisLoader(false);\n              listenUserResponse(currentListeningItem);\n            });\n          } else {\n            const msg = \"Vamos a la siguiente pregunta.\";\n            appendMessage(msg, \"bot\");\n            faceSpeak(\"Siguiente pregunta.\", \"neutral\");\n            showSynthesisLoader(true);\n            speakSpanish(msg).then(() => {\n              showSynthesisLoader(false);\n              currentLessonIndex++;\n              retryCount = 0;\n              startLesson();\n            });\n          }\n          return;\n        }\n\n        const msg = \"Hubo un error de voz. Intenta responder otra vez.\";\n        appendMessage(msg, \"bot\");\n        faceSpeak(\"Intenta otra vez.\", \"sad\");\n        showSynthesisLoader(true);\n        speakSpanish(msg).then(() => {\n          showSynthesisLoader(false);\n          listenUserResponse(currentListeningItem);\n        });\n      };\n\n      recognition.onend = function() {\n        speechRecognitionActive = false;\n        showRecognitionLoader(false);\n      };\n    }\n\n    function loadVoices() {\n      const fillVoices = () => {\n        voices = window.speechSynthesis.getVoices();\n        spanishVoices = voices.filter(v =>\n          \/^es(-|_)\/i.test(v.lang) || \/spanish|espa\u00f1ol\/i.test(v.name)\n        );\n\n        spanishVoices.sort((a, b) => getSpanishVoicePriority(a) - getSpanishVoicePriority(b));\n        selectedSpanishVoice = spanishVoices[0] || null;\n        populateSpanishVoiceList();\n      };\n\n      fillVoices();\n      if (typeof speechSynthesis !== \"undefined\") {\n        speechSynthesis.onvoiceschanged = fillVoices;\n      }\n    }\n\n    function getSpanishVoicePriority(voice) {\n      const name = voice.name.toLowerCase();\n      if (name.includes(\"google\") && (name.includes(\"espa\u00f1ol\") || name.includes(\"spanish\"))) return 1;\n      if (name.includes(\"microsoft\") && (name.includes(\"espa\u00f1ol\") || name.includes(\"spanish\"))) return 2;\n      if (name.includes(\"spanish\") || name.includes(\"espa\u00f1ol\")) return 3;\n      return 10;\n    }\n\n    function populateSpanishVoiceList() {\n      const voiceSelect = document.getElementById('spanish-voice-select');\n      voiceSelect.innerHTML = '';\n\n      if (!spanishVoices.length) {\n        const option = document.createElement('option');\n        option.value = \"\";\n        option.textContent = \"No Spanish voices available\";\n        voiceSelect.appendChild(option);\n        voiceSelect.disabled = true;\n        selectedSpanishVoice = null;\n        return;\n      }\n\n      spanishVoices.forEach((voice, index) => {\n        const option = document.createElement('option');\n        option.value = String(index);\n        option.textContent = `${voice.name} (${voice.lang})`;\n        voiceSelect.appendChild(option);\n      });\n\n      selectedSpanishVoice = spanishVoices[0];\n      voiceSelect.value = \"0\";\n      voiceSelect.disabled = false;\n\n      voiceSelect.onchange = () => {\n        const index = parseInt(voiceSelect.value, 10);\n        selectedSpanishVoice = spanishVoices[index] || spanishVoices[0];\n      };\n    }\n\n    function startLesson() {\n      if (!isLessonActive) return;\n      if (currentLessonIndex >= lessons.length) {\n        endLesson(false);\n        return;\n      }\n      askCurrentQuestion();\n    }\n\n    function askCurrentQuestion() {\n      if (!isLessonActive) return;\n\n      const item = lessons[currentLessonIndex];\n      const questionNumber = currentLessonIndex + 1;\n\n      const displayText = `Question ${questionNumber}\\nSpanish: ${item.questionEs}\\nEnglish: ${item.questionEn}`;\n      appendMessage(displayText, 'bot');\n\n      addSentenceToBox(`Question: ${item.questionEs}`);\n      addSentenceToBox(`Meaning: ${item.questionEn}`);\n      addSentenceToBox(`Sample answer: ${item.suggestedAnswer}`);\n      addSentenceToBox(`--------------------`);\n\n      faceSpeak(item.questionEs, \"neutral\");\n      showSynthesisLoader(true);\n\n      speakSpanish(item.questionEs).then(() => {\n        showSynthesisLoader(false);\n        listenUserResponse(item);\n      });\n    }\n\n    function listenUserResponse(item) {\n      if (!isLessonActive || !recognition) return;\n\n      stopRecognitionOnly();\n      currentListeningItem = item;\n      recognition.lang = 'es-ES';\n      speechRecognitionActive = true;\n      showRecognitionLoader(true);\n      appendMessage(\"Listening... Answer with simple Spanish.\", \"bot\");\n      faceSpeak(\"Responde en espa\u00f1ol.\", \"neutral\", 1400);\n\n      try {\n        recognition.start();\n      } catch (err) {\n        speechRecognitionActive = false;\n        showRecognitionLoader(false);\n        appendMessage(\"The microphone could not start. Please try again.\", \"bot\");\n        faceSpeak(\"Intenta otra vez.\", \"sad\");\n      }\n    }\n\n    function containsKeyword(input, keyword) {\n      const escaped = keyword.replace(\/[.*+?^${}()|[\\]\\\\]\/g, \"\\\\$&\");\n      const pattern = new RegExp(`\\\\b${escaped}\\\\b`, \"i\");\n      return pattern.test(input);\n    }\n\n    function analyzeResponse(userInput, keywords) {\n      const input = userInput.toLowerCase();\n      let matchCount = 0;\n\n      keywords.forEach(keyword => {\n        if (containsKeyword(input, keyword.toLowerCase())) matchCount++;\n      });\n\n      const wordCount = input.split(\/\\s+\/).filter(Boolean).length;\n\n      if (matchCount >= 2 || (matchCount >= 1 && wordCount >= 4)) {\n        return {\n          level: \"high\",\n          message: pickRandom(positiveFeedback),\n          emotion: \"happy\",\n          shortFaceText: \"Muy bien\"\n        };\n      }\n\n      if (matchCount === 1 || wordCount >= 3) {\n        return {\n          level: \"mid\",\n          message: pickRandom(partialFeedback),\n          emotion: \"neutral\",\n          shortFaceText: \"Bien\"\n        };\n      }\n\n      return {\n        level: \"low\",\n        message: pickRandom(constructiveFeedback),\n        emotion: \"confused\",\n        shortFaceText: \"Otra vez\"\n      };\n    }\n\n    function checkStopKeywords(userInput) {\n      const pattern = new RegExp(\"\\\\b(\" + stopKeywords.map(escapeRegex).join(\"|\") + \")\\\\b\", \"i\");\n      return pattern.test(userInput);\n    }\n\n    function formatSentence(text) {\n      if (!text) return \"\";\n      text = text.trim();\n      text = text.charAt(0).toUpperCase() + text.slice(1);\n      if (!\/[.?!]$\/.test(text)) text += \".\";\n      return text;\n    }\n\n    function addSentenceToBox(sentence) {\n      const wordBox = document.getElementById('word-box');\n      const sentenceElement = document.createElement('div');\n      sentenceElement.innerText = sentence;\n      wordBox.appendChild(sentenceElement);\n      wordBox.scrollTop = wordBox.scrollHeight;\n    }\n\n    function clearWordBox() {\n      document.getElementById('word-box').innerHTML = \"\";\n    }\n\n    function clearChat() {\n      document.getElementById('chat-messages').innerHTML = \"\";\n    }\n\n    function endLesson(userStopped = false) {\n      stopAllAudioAndRecognition();\n      isLessonActive = false;\n      currentListeningItem = null;\n\n      const message = userStopped\n        ? \"Gracias. Adi\u00f3s.\"\n        : `Excelente. Terminaste la secci\u00f3n ${sectionData[currentSection].title}. Sigue practicando.`;\n\n      appendMessage(message, \"bot\");\n      faceSpeak(message, userStopped ? \"neutral\" : \"happy\");\n      showSynthesisLoader(true);\n      speakSpanish(message).then(() => showSynthesisLoader(false));\n    }\n\n    function speakText(text, lang, voiceObj) {\n      return new Promise(resolve => {\n        if (!speechSynthesisSupported) {\n          resolve();\n          return;\n        }\n        window.speechSynthesis.cancel();\n        const utterance = new SpeechSynthesisUtterance(text);\n        utterance.lang = lang;\n        utterance.rate = 0.9;\n        utterance.pitch = 1.0;\n        if (voiceObj) utterance.voice = voiceObj;\n        utterance.onend = () => resolve();\n        utterance.onerror = () => resolve();\n        window.speechSynthesis.speak(utterance);\n      });\n    }\n\n    function speakSpanish(text) {\n      return speakText(text, \"es-ES\", selectedSpanishVoice);\n    }\n\n    function appendMessage(text, sender) {\n      const messageContainer = document.getElementById('chat-messages');\n      const messageElement = document.createElement('div');\n      messageElement.classList.add('message');\n      messageElement.classList.add(sender === 'bot' ? 'bot-message' : 'user-message');\n      messageElement.innerText = text;\n      messageContainer.appendChild(messageElement);\n      messageContainer.scrollTop = messageContainer.scrollHeight;\n    }\n\n    function showSynthesisLoader(show) {\n      const loader = document.getElementById('synthesis-loader');\n      loader.style.display = show ? 'inline-block' : 'none';\n      loader.style.borderTop = '4px solid #17a2b8';\n    }\n\n    function showRecognitionLoader(show) {\n      const loader = document.getElementById('synthesis-loader');\n      loader.style.display = show ? 'inline-block' : 'none';\n      loader.style.borderTop = show ? '4px solid #ffc107' : '4px solid #17a2b8';\n    }\n\n    function clearPendingTimeout() {\n      if (pendingNextQuestionTimeout) {\n        clearTimeout(pendingNextQuestionTimeout);\n        pendingNextQuestionTimeout = null;\n      }\n    }\n\n    function stopRecognitionOnly() {\n      if (recognition) {\n        try { recognition.stop(); } catch (e) {}\n      }\n      speechRecognitionActive = false;\n      showRecognitionLoader(false);\n    }\n\n    function stopAllAudioAndRecognition() {\n      clearPendingTimeout();\n      stopRecognitionOnly();\n      if (speechSynthesisSupported) window.speechSynthesis.cancel();\n      showSynthesisLoader(false);\n      stopFaceTalking();\n      faceSpeechBubble.classList.remove('visible');\n    }\n\n    function pickRandom(arr) {\n      return arr[Math.floor(Math.random() * arr.length)];\n    }\n\n    function escapeRegex(str) {\n      return str.replace(\/[.*+?^${}()|[\\]\\\\]\/g, \"\\\\$&\");\n    }\n\n    window.onload = initialize;\n  <\/script>\n<\/body>\n<\/html>\n","protected":false},"excerpt":{"rendered":"<p>Learn Spanish: Shopping Q&#038;A Practice Shopping Clothes Supermarket Fruit and vegetables Paying Sizes and colors Market Gifts Listen to the<\/p>\n","protected":false},"author":1,"featured_media":594,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"colormag_page_layout":"default_layout","footnotes":""},"categories":[65,54],"tags":[],"class_list":["post-886","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-advanced","category-spanish"],"_links":{"self":[{"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/posts\/886","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=886"}],"version-history":[{"count":1,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/posts\/886\/revisions"}],"predecessor-version":[{"id":887,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/posts\/886\/revisions\/887"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/media\/594"}],"wp:attachment":[{"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/media?parent=886"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/categories?post=886"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/tags?post=886"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}