{"id":884,"date":"2026-03-12T12:37:59","date_gmt":"2026-03-12T12:37:59","guid":{"rendered":"https:\/\/i-cte.org\/robot\/?p=884"},"modified":"2026-03-12T12:37:59","modified_gmt":"2026-03-12T12:37:59","slug":"spanish-interaction-travel","status":"publish","type":"post","link":"https:\/\/i-cte.org\/robot\/spanish-interaction-travel\/","title":{"rendered":"Spanish interaction &#8211; Travel"},"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: Travel 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=\"travel\">Travel<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"airport\">Airport<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"hotel\">Hotel<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"transport\">Transport<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"restaurant\">Restaurant<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"sightseeing\">Sightseeing<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"shopping\">Shopping<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"beach\">Beach<\/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\">Travel<\/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=\"travel\">Travel<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"airport\">Airport<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"hotel\">Hotel<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"transport\">Transport<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"restaurant\">Restaurant<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"sightseeing\">Sightseeing<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"shopping\">Shopping<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"beach\">Beach<\/a>\n  <\/div>\n\n  <script>\n    const sectionData = {\n      travel: {\n        title: \"Travel\",\n        intro: \"Hoy vamos a practicar preguntas sobre viajar.\",\n        items: [\n          {\n            questionEs: \"\u00bfTe gusta viajar?\",\n            questionEn: \"Do you like to travel?\",\n            suggestedAnswer: \"S\u00ed, me gusta viajar.\",\n            keywords: [\"s\u00ed\", \"si\", \"gusta\", \"viajar\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfViajas hoy?\",\n            questionEn: \"Do you travel today?\",\n            suggestedAnswer: \"S\u00ed, viajo hoy.\",\n            keywords: [\"s\u00ed\", \"si\", \"viajo\", \"hoy\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfVas con tu familia?\",\n            questionEn: \"Do you go with your family?\",\n            suggestedAnswer: \"S\u00ed, voy con mi familia.\",\n            keywords: [\"s\u00ed\", \"si\", \"voy\", \"familia\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfTu viaje es largo?\",\n            questionEn: \"Is your trip long?\",\n            suggestedAnswer: \"S\u00ed, mi viaje es largo.\",\n            keywords: [\"s\u00ed\", \"si\", \"viaje\", \"largo\", \"corto\", \"no\"]\n          }\n        ]\n      },\n\n      airport: {\n        title: \"Airport\",\n        intro: \"Hoy vamos a practicar preguntas sobre el aeropuerto.\",\n        items: [\n          {\n            questionEs: \"\u00bfEst\u00e1s en el aeropuerto?\",\n            questionEn: \"Are you at the airport?\",\n            suggestedAnswer: \"S\u00ed, estoy en el aeropuerto.\",\n            keywords: [\"s\u00ed\", \"si\", \"estoy\", \"aeropuerto\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfTienes tu pasaporte?\",\n            questionEn: \"Do you have your passport?\",\n            suggestedAnswer: \"S\u00ed, tengo mi pasaporte.\",\n            keywords: [\"s\u00ed\", \"si\", \"tengo\", \"pasaporte\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfTu vuelo sale hoy?\",\n            questionEn: \"Does your flight leave today?\",\n            suggestedAnswer: \"S\u00ed, mi vuelo sale hoy.\",\n            keywords: [\"s\u00ed\", \"si\", \"vuelo\", \"sale\", \"hoy\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfLlevas una maleta?\",\n            questionEn: \"Do you carry a suitcase?\",\n            suggestedAnswer: \"S\u00ed, llevo una maleta.\",\n            keywords: [\"s\u00ed\", \"si\", \"llevo\", \"maleta\", \"no\"]\n          }\n        ]\n      },\n\n      hotel: {\n        title: \"Hotel\",\n        intro: \"Hoy vamos a practicar preguntas sobre el hotel.\",\n        items: [\n          {\n            questionEs: \"\u00bfEst\u00e1s en un hotel?\",\n            questionEn: \"Are you in a hotel?\",\n            suggestedAnswer: \"S\u00ed, estoy en un hotel.\",\n            keywords: [\"s\u00ed\", \"si\", \"estoy\", \"hotel\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfTu habitaci\u00f3n es grande?\",\n            questionEn: \"Is your room big?\",\n            suggestedAnswer: \"S\u00ed, mi habitaci\u00f3n es grande.\",\n            keywords: [\"s\u00ed\", \"si\", \"habitaci\u00f3n\", \"habitacion\", \"grande\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfDuermes bien en el hotel?\",\n            questionEn: \"Do you sleep well in the hotel?\",\n            suggestedAnswer: \"S\u00ed, duermo bien.\",\n            keywords: [\"s\u00ed\", \"si\", \"duermo\", \"bien\", \"hotel\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfTe gusta el hotel?\",\n            questionEn: \"Do you like the hotel?\",\n            suggestedAnswer: \"S\u00ed, me gusta el hotel.\",\n            keywords: [\"s\u00ed\", \"si\", \"gusta\", \"hotel\", \"no\"]\n          }\n        ]\n      },\n\n      transport: {\n        title: \"Transport\",\n        intro: \"Hoy vamos a practicar preguntas sobre el transporte.\",\n        items: [\n          {\n            questionEs: \"\u00bfVas en autob\u00fas?\",\n            questionEn: \"Do you go by bus?\",\n            suggestedAnswer: \"S\u00ed, voy en autob\u00fas.\",\n            keywords: [\"s\u00ed\", \"si\", \"voy\", \"autob\u00fas\", \"autobus\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfTomas un taxi?\",\n            questionEn: \"Do you take a taxi?\",\n            suggestedAnswer: \"S\u00ed, tomo un taxi.\",\n            keywords: [\"s\u00ed\", \"si\", \"tomo\", \"taxi\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfEl tren llega pronto?\",\n            questionEn: \"Does the train arrive soon?\",\n            suggestedAnswer: \"S\u00ed, llega pronto.\",\n            keywords: [\"s\u00ed\", \"si\", \"tren\", \"llega\", \"pronto\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfCaminas mucho?\",\n            questionEn: \"Do you walk a lot?\",\n            suggestedAnswer: \"S\u00ed, camino mucho.\",\n            keywords: [\"s\u00ed\", \"si\", \"camino\", \"mucho\", \"no\"]\n          }\n        ]\n      },\n\n      restaurant: {\n        title: \"Restaurant\",\n        intro: \"Hoy vamos a practicar preguntas sobre el restaurante.\",\n        items: [\n          {\n            questionEs: \"\u00bfComes en un restaurante?\",\n            questionEn: \"Do you eat in a restaurant?\",\n            suggestedAnswer: \"S\u00ed, como en un restaurante.\",\n            keywords: [\"s\u00ed\", \"si\", \"como\", \"restaurante\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfPides agua?\",\n            questionEn: \"Do you order water?\",\n            suggestedAnswer: \"S\u00ed, pido agua.\",\n            keywords: [\"s\u00ed\", \"si\", \"pido\", \"agua\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfTe gusta la comida?\",\n            questionEn: \"Do you like the food?\",\n            suggestedAnswer: \"S\u00ed, me gusta la comida.\",\n            keywords: [\"s\u00ed\", \"si\", \"gusta\", \"comida\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfLa cuenta es alta?\",\n            questionEn: \"Is the bill high?\",\n            suggestedAnswer: \"No, la cuenta est\u00e1 bien.\",\n            keywords: [\"s\u00ed\", \"si\", \"no\", \"cuenta\", \"alta\", \"bien\"]\n          }\n        ]\n      },\n\n      sightseeing: {\n        title: \"Sightseeing\",\n        intro: \"Hoy vamos a practicar preguntas sobre visitar lugares.\",\n        items: [\n          {\n            questionEs: \"\u00bfVisitas la ciudad?\",\n            questionEn: \"Do you visit the city?\",\n            suggestedAnswer: \"S\u00ed, visito la ciudad.\",\n            keywords: [\"s\u00ed\", \"si\", \"visito\", \"ciudad\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfTomas fotos?\",\n            questionEn: \"Do you take photos?\",\n            suggestedAnswer: \"S\u00ed, tomo fotos.\",\n            keywords: [\"s\u00ed\", \"si\", \"tomo\", \"fotos\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfVes un museo?\",\n            questionEn: \"Do you see a museum?\",\n            suggestedAnswer: \"S\u00ed, veo un museo.\",\n            keywords: [\"s\u00ed\", \"si\", \"veo\", \"museo\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfEl lugar es bonito?\",\n            questionEn: \"Is the place beautiful?\",\n            suggestedAnswer: \"S\u00ed, es muy bonito.\",\n            keywords: [\"s\u00ed\", \"si\", \"bonito\", \"lugar\", \"muy\", \"no\"]\n          }\n        ]\n      },\n\n      shopping: {\n        title: \"Shopping\",\n        intro: \"Hoy vamos a practicar preguntas sobre comprar.\",\n        items: [\n          {\n            questionEs: \"\u00bfCompras recuerdos?\",\n            questionEn: \"Do you buy souvenirs?\",\n            suggestedAnswer: \"S\u00ed, compro recuerdos.\",\n            keywords: [\"s\u00ed\", \"si\", \"compro\", \"recuerdos\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfMiras una tienda?\",\n            questionEn: \"Do you look at a shop?\",\n            suggestedAnswer: \"S\u00ed, miro una tienda.\",\n            keywords: [\"s\u00ed\", \"si\", \"miro\", \"tienda\", \"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: \"\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      },\n\n      beach: {\n        title: \"Beach\",\n        intro: \"Hoy vamos a practicar preguntas sobre la playa.\",\n        items: [\n          {\n            questionEs: \"\u00bfVas a la playa?\",\n            questionEn: \"Do you go to the beach?\",\n            suggestedAnswer: \"S\u00ed, voy a la playa.\",\n            keywords: [\"s\u00ed\", \"si\", \"voy\", \"playa\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfNadas en el mar?\",\n            questionEn: \"Do you swim in the sea?\",\n            suggestedAnswer: \"S\u00ed, nado en el mar.\",\n            keywords: [\"s\u00ed\", \"si\", \"nado\", \"mar\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfHace calor?\",\n            questionEn: \"Is it hot?\",\n            suggestedAnswer: \"S\u00ed, hace calor.\",\n            keywords: [\"s\u00ed\", \"si\", \"hace\", \"calor\", \"no\"]\n          },\n          {\n            questionEs: \"\u00bfDescansas en la playa?\",\n            questionEn: \"Do you rest at the beach?\",\n            suggestedAnswer: \"S\u00ed, descanso en la playa.\",\n            keywords: [\"s\u00ed\", \"si\", \"descanso\", \"playa\", \"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 = \"travel\";\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 travel 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: Travel Q&#038;A Practice Travel Airport Hotel Transport Restaurant Sightseeing Shopping Beach Listen to the question and answer in<\/p>\n","protected":false},"author":1,"featured_media":595,"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-884","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\/884","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=884"}],"version-history":[{"count":1,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/posts\/884\/revisions"}],"predecessor-version":[{"id":885,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/posts\/884\/revisions\/885"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/media\/595"}],"wp:attachment":[{"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/media?parent=884"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/categories?post=884"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/tags?post=884"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}