{"id":858,"date":"2026-03-12T07:47:30","date_gmt":"2026-03-12T07:47:30","guid":{"rendered":"https:\/\/i-cte.org\/robot\/?p=858"},"modified":"2026-03-12T07:47:30","modified_gmt":"2026-03-12T07:47:30","slug":"spanish-greetings-and-introduction","status":"publish","type":"post","link":"https:\/\/i-cte.org\/robot\/spanish-greetings-and-introduction\/","title":{"rendered":"Spanish: Greetings and introduction"},"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: Greetings and Introductions<\/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: 16px;\n      font-weight: bold;\n      color: #28a745;\n      text-align: left;\n      max-height: 280px;\n      overflow-y: auto;\n      line-height: 1.6;\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: 700px;\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: 430px;\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: 320px;\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    .nose-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 {\n        width: 100%;\n        max-width: 100%;\n      }\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\" id=\"top-menu\">\n    <a href=\"#\" class=\"menu-link active\" data-section=\"greetings\">Greetings and introductions<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"names\">Asking names<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"ages\">Asking ages<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"from\">Asking where someone is from<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"feelings\">Asking feelings<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"likes\">Likes and dislikes<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"polite\">Polite expressions<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"goodbye\">Saying goodbye<\/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\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=\"nose\" class=\"nose-path\" d=\"M 150 140 L 142 175 Q 150 181 158 175 L 150 140\" fill=\"none\" stroke=\"white\" stroke-width=\"3\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/>\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 id=\"anger-veins\" opacity=\"0\">\n                  <path d=\"M 250 50 L 260 60 L 255 55 L 265 65\" stroke=\"#ff4444\" stroke-width=\"2.4\" fill=\"none\" \/>\n                <\/g>\n              <\/g>\n            <\/svg>\n          <\/div>\n        <\/div>\n\n        <div class=\"sentence-text\">\n          Listen to the English meaning, then listen to the Spanish sentence twice, and repeat it.\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\">Greetings and introductions<\/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\" aria-label=\"Start Lesson\">Start<\/button>\n          <button id=\"stop-btn\" class=\"stop-button\" aria-label=\"Stop Lesson\">Stop<\/button>\n        <\/div>\n      <\/div>\n    <\/div>\n  <\/div>\n\n  <div class=\"menu\" id=\"bottom-menu\">\n    <a href=\"#\" class=\"menu-link active\" data-section=\"greetings\">Greetings and introductions<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"names\">Asking names<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"ages\">Asking ages<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"from\">Asking where someone is from<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"feelings\">Asking feelings<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"likes\">Likes and dislikes<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"polite\">Polite expressions<\/a>\n    <a href=\"#\" class=\"menu-link\" data-section=\"goodbye\">Saying goodbye<\/a>\n  <\/div>\n\n  <script>\n    const sectionData = {\n      greetings: {\n        title: \"Greetings and introductions\",\n        intro: \"Today we will practice Spanish greetings and introductions.\",\n        lessons: [\n          { spanish: \"Hola, me llamo Ana hoy.\", english: \"Hello, my name is Ana today.\", keywords: [\"hola\", \"llamo\", \"ana\", \"hoy\"] },\n          { spanish: \"Mucho gusto conocerte hoy aqu\u00ed.\", english: \"Nice to meet you here today.\", keywords: [\"mucho\", \"gusto\", \"conocerte\", \"aqu\u00ed\", \"aqui\"] },\n          { spanish: \"Buenos d\u00edas, soy tu amiga.\", english: \"Good morning, I am your friend.\", keywords: [\"buenos\", \"d\u00edas\", \"dias\", \"soy\", \"amiga\"] },\n          { spanish: \"Buenas tardes, estoy muy bien.\", english: \"Good afternoon, I am very well.\", keywords: [\"buenas\", \"tardes\", \"estoy\", \"bien\"] },\n          { spanish: \"Hola, soy nueva aqu\u00ed hoy.\", english: \"Hello, I am new here today.\", keywords: [\"hola\", \"soy\", \"nueva\", \"aqu\u00ed\", \"aqui\", \"hoy\"] }\n        ]\n      },\n      names: {\n        title: \"Asking names\",\n        intro: \"Today we will practice Spanish asking names.\",\n        lessons: [\n          { spanish: \"\u00bfC\u00f3mo te llamas t\u00fa hoy?\", english: \"What is your name today?\", keywords: [\"c\u00f3mo\", \"como\", \"llamas\", \"hoy\"] },\n          { spanish: \"Me llamo Luis esta tarde.\", english: \"My name is Luis this afternoon.\", keywords: [\"llamo\", \"luis\", \"tarde\"] },\n          { spanish: \"\u00bfCu\u00e1l es tu nombre completo?\", english: \"What is your full name?\", keywords: [\"cu\u00e1l\", \"cual\", \"nombre\", \"completo\"] },\n          { spanish: \"Mi nombre es Marta hoy.\", english: \"My name is Marta today.\", keywords: [\"nombre\", \"marta\", \"hoy\"] },\n          { spanish: \"Se llama Pablo tambi\u00e9n aqu\u00ed.\", english: \"His name is Pablo here too.\", keywords: [\"llama\", \"pablo\", \"aqu\u00ed\", \"aqui\"] }\n        ]\n      },\n      ages: {\n        title: \"Asking ages\",\n        intro: \"Today we will practice Spanish asking ages.\",\n        lessons: [\n          { spanish: \"\u00bfCu\u00e1ntos a\u00f1os tienes t\u00fa ahora?\", english: \"How old are you now?\", keywords: [\"cu\u00e1ntos\", \"cuantos\", \"a\u00f1os\", \"tienes\", \"ahora\"] },\n          { spanish: \"Tengo diez a\u00f1os ahora mismo.\", english: \"I am ten years old right now.\", keywords: [\"tengo\", \"diez\", \"a\u00f1os\", \"ahora\"] },\n          { spanish: \"Ella tiene doce a\u00f1os hoy.\", english: \"She is twelve years old today.\", keywords: [\"ella\", \"tiene\", \"doce\", \"a\u00f1os\", \"hoy\"] },\n          { spanish: \"\u00c9l tiene ocho a\u00f1os tambi\u00e9n.\", english: \"He is eight years old too.\", keywords: [\"\u00e9l\", \"el\", \"tiene\", \"ocho\", \"a\u00f1os\"] },\n          { spanish: \"Tengo veinte a\u00f1os este a\u00f1o.\", english: \"I am twenty years old this year.\", keywords: [\"tengo\", \"veinte\", \"a\u00f1os\", \"a\u00f1o\"] }\n        ]\n      },\n      from: {\n        title: \"Asking where someone is from\",\n        intro: \"Today we will practice Spanish asking where someone is from.\",\n        lessons: [\n          { spanish: \"\u00bfDe d\u00f3nde eres t\u00fa exactamente?\", english: \"Where are you from exactly?\", keywords: [\"d\u00f3nde\", \"donde\", \"eres\", \"exactamente\"] },\n          { spanish: \"Soy de Vietnam hoy aqu\u00ed.\", english: \"I am from Vietnam here today.\", keywords: [\"soy\", \"vietnam\", \"hoy\", \"aqu\u00ed\", \"aqui\"] },\n          { spanish: \"Ella es de M\u00e9xico hoy.\", english: \"She is from Mexico today.\", keywords: [\"ella\", \"m\u00e9xico\", \"mexico\", \"hoy\"] },\n          { spanish: \"\u00c9l viene de Jap\u00f3n tambi\u00e9n.\", english: \"He comes from Japan too.\", keywords: [\"\u00e9l\", \"el\", \"viene\", \"jap\u00f3n\", \"japon\"] },\n          { spanish: \"Somos de esta ciudad hoy.\", english: \"We are from this city today.\", keywords: [\"somos\", \"ciudad\", \"hoy\"] }\n        ]\n      },\n      feelings: {\n        title: \"Asking feelings\",\n        intro: \"Today we will practice Spanish asking feelings.\",\n        lessons: [\n          { spanish: \"\u00bfC\u00f3mo est\u00e1s t\u00fa hoy ahora?\", english: \"How are you today now?\", keywords: [\"c\u00f3mo\", \"como\", \"est\u00e1s\", \"estas\", \"hoy\", \"ahora\"] },\n          { spanish: \"Estoy muy bien hoy aqu\u00ed.\", english: \"I am very well here today.\", keywords: [\"estoy\", \"muy\", \"bien\", \"hoy\", \"aqu\u00ed\", \"aqui\"] },\n          { spanish: \"Estoy un poco cansado hoy.\", english: \"I am a little tired today.\", keywords: [\"estoy\", \"poco\", \"cansado\", \"hoy\"] },\n          { spanish: \"Estoy muy feliz esta tarde.\", english: \"I am very happy this afternoon.\", keywords: [\"estoy\", \"muy\", \"feliz\", \"tarde\"] },\n          { spanish: \"Me siento tranquilo hoy tambi\u00e9n.\", english: \"I feel calm today too.\", keywords: [\"siento\", \"tranquilo\", \"hoy\", \"tambi\u00e9n\", \"tambien\"] }\n        ]\n      },\n      likes: {\n        title: \"Likes and dislikes\",\n        intro: \"Today we will practice Spanish likes and dislikes.\",\n        lessons: [\n          { spanish: \"Me gusta la m\u00fasica mucho.\", english: \"I like music very much.\", keywords: [\"gusta\", \"m\u00fasica\", \"musica\", \"mucho\"] },\n          { spanish: \"Me gusta leer libros aqu\u00ed.\", english: \"I like reading books here.\", keywords: [\"gusta\", \"leer\", \"libros\", \"aqu\u00ed\", \"aqui\"] },\n          { spanish: \"No me gusta correr hoy.\", english: \"I do not like running today.\", keywords: [\"no\", \"gusta\", \"correr\", \"hoy\"] },\n          { spanish: \"Me gustan perros muy peque\u00f1os.\", english: \"I like very small dogs.\", keywords: [\"gustan\", \"perros\", \"peque\u00f1os\", \"pequenos\"] },\n          { spanish: \"No quiero eso ahora mismo.\", english: \"I do not want that right now.\", keywords: [\"no\", \"quiero\", \"eso\", \"ahora\"] }\n        ]\n      },\n      polite: {\n        title: \"Polite expressions\",\n        intro: \"Today we will practice Spanish polite expressions.\",\n        lessons: [\n          { spanish: \"Por favor, ay\u00fademe aqu\u00ed hoy.\", english: \"Please help me here today.\", keywords: [\"favor\", \"ay\u00fademe\", \"ayudeme\", \"aqu\u00ed\", \"aqui\", \"hoy\"] },\n          { spanish: \"Muchas gracias por su ayuda.\", english: \"Thank you very much for your help.\", keywords: [\"muchas\", \"gracias\", \"ayuda\"] },\n          { spanish: \"De nada, amigo m\u00edo hoy.\", english: \"You are welcome, my friend, today.\", keywords: [\"nada\", \"amigo\", \"m\u00edo\", \"mio\", \"hoy\"] },\n          { spanish: \"Con permiso, paso por aqu\u00ed.\", english: \"Excuse me, I pass through here.\", keywords: [\"permiso\", \"paso\", \"aqu\u00ed\", \"aqui\"] },\n          { spanish: \"Lo siento mucho hoy tambi\u00e9n.\", english: \"I am very sorry today too.\", keywords: [\"siento\", \"mucho\", \"hoy\", \"tambi\u00e9n\", \"tambien\"] }\n        ]\n      },\n      goodbye: {\n        title: \"Saying goodbye\",\n        intro: \"Today we will practice Spanish saying goodbye.\",\n        lessons: [\n          { spanish: \"Adi\u00f3s, nos vemos ma\u00f1ana aqu\u00ed.\", english: \"Goodbye, see you tomorrow here.\", keywords: [\"adi\u00f3s\", \"adios\", \"vemos\", \"ma\u00f1ana\", \"manana\", \"aqu\u00ed\", \"aqui\"] },\n          { spanish: \"Hasta luego, amigo m\u00edo hoy.\", english: \"See you later, my friend, today.\", keywords: [\"hasta\", \"luego\", \"amigo\", \"m\u00edo\", \"mio\", \"hoy\"] },\n          { spanish: \"Hasta ma\u00f1ana, cu\u00eddate mucho hoy.\", english: \"See you tomorrow, take care today.\", keywords: [\"hasta\", \"ma\u00f1ana\", \"manana\", \"cu\u00eddate\", \"cuidate\", \"mucho\", \"hoy\"] },\n          { spanish: \"Nos vemos pronto, amiga querida.\", english: \"See you soon, dear friend.\", keywords: [\"vemos\", \"pronto\", \"amiga\", \"querida\"] },\n          { spanish: \"Que tengas lindo d\u00eda hoy.\", english: \"Have a lovely day today.\", keywords: [\"tengas\", \"lindo\", \"d\u00eda\", \"dia\", \"hoy\"] }\n        ]\n      }\n    };\n\n    let currentSection = \"greetings\";\n    let lessons = sectionData[currentSection].lessons;\n\n    const positiveFeedback = [\n      \"Very good. You said it clearly.\",\n      \"Excellent. Keep practicing.\",\n      \"Great job. Your Spanish was clear.\",\n      \"Well done. Keep going.\",\n      \"Excellent work. Nice speaking.\"\n    ];\n\n    const constructiveFeedback = [\n      \"Try again and repeat the Spanish sentence clearly.\",\n      \"Good try. Please repeat the sentence one more time.\",\n      \"Listen carefully and say the sentence again.\",\n      \"Try to say all the Spanish words clearly.\",\n      \"Please repeat after the model sentence.\"\n    ];\n\n    const pronunciationFeedback = [\n      \"Good effort. Say it a little more clearly.\",\n      \"Nice try. Focus on the key Spanish words.\",\n      \"Good. Speak a little more slowly.\",\n      \"Your answer is understandable. Try again with clearer pronunciation.\",\n      \"Well done. Keep your speaking steady.\"\n    ];\n\n    const stopKeywords = [\"goodbye\", \"bye\", \"stop\", \"end lesson\", \"adi\u00f3s\", \"adios\"];\n\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 englishVoices = [];\n    let selectedSpanishVoice = null;\n    let selectedEnglishVoice = null;\n\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 faceContainer = document.getElementById('face-container');\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 angerVeins = document.getElementById('anger-veins');\n    const faceSpeechBubble = document.getElementById('face-speech-bubble');\n    const faceSpeechText = document.getElementById('face-speech-text');\n\n    const emotions = {\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        anger: 0,\n        teeth: 0,\n        mouthInnerOpacity: 0,\n        mouthInnerRx: 0,\n        mouthInnerRy: 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        anger: 0,\n        teeth: 0.92,\n        mouthInnerOpacity: 0.25,\n        mouthInnerRx: 24,\n        mouthInnerRy: 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        anger: 0,\n        teeth: 0,\n        mouthInnerOpacity: 0,\n        mouthInnerRx: 0,\n        mouthInnerRy: 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        anger: 0,\n        teeth: 0,\n        mouthInnerOpacity: 0,\n        mouthInnerRx: 0,\n        mouthInnerRy: 0\n      }\n    };\n\n    function setFaceEmotion(emotion) {\n      const preset = emotions[emotion] || emotions.neutral;\n      currentEmotion = emotion;\n\n      mouth.setAttribute('d', preset.mouth);\n      leftEyebrow.setAttribute('d', preset.leftEyebrow);\n      rightEyebrow.setAttribute('d', preset.rightEyebrow);\n\n      leftEyeWhite.setAttribute('ry', preset.eyeRY);\n      rightEyeWhite.setAttribute('ry', preset.eyeRY);\n\n      leftBlush.setAttribute('opacity', preset.blush);\n      rightBlush.setAttribute('opacity', preset.blush);\n\n      leftTear.setAttribute('fill', `rgba(100,200,255,${preset.tears})`);\n      rightTear.setAttribute('fill', `rgba(100,200,255,${preset.tears})`);\n\n      angerVeins.setAttribute('opacity', preset.anger);\n      teeth.setAttribute('opacity', preset.teeth);\n      mouthInner.setAttribute('opacity', preset.mouthInnerOpacity);\n      mouthInner.setAttribute('rx', preset.mouthInnerRx);\n      mouthInner.setAttribute('ry', preset.mouthInnerRy);\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\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        if (Math.random() < 0.22) setTimeout(() => blinkFace(70), 120);\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\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\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\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\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          { d: 'M 108 224 Q 150 236 192 224', rx: 12, ry: 5, teeth: 0, inner: 0.18 }\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          { d: 'M 88 210 Q 150 286 212 210', rx: 28, ry: 12, teeth: 1.0, inner: 0.4 }\n        ],\n        sad: [\n          { d: 'M 112 238 Q 150 212 188 238', rx: 10, ry: 4, teeth: 0, inner: 0.1 },\n          { d: 'M 108 234 Q 150 220 192 234', rx: 12, ry: 5, teeth: 0, inner: 0.14 },\n          { d: 'M 114 240 Q 150 208 186 240', rx: 8, ry: 4, teeth: 0, inner: 0.08 }\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          { d: 'M 106 222 Q 140 236 162 220 Q 184 242 194 226', rx: 14, ry: 6, teeth: 0, inner: 0.18 },\n          { d: 'M 112 225 Q 141 228 161 222 Q 181 234 189 225', rx: 9, ry: 4, teeth: 0, inner: 0.08 }\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      const bobX = (Math.random() * 2 - 1) * 1.5;\n      const bobY = -1.5 + Math.random() * 2.8;\n      const tilt = (Math.random() * 2 - 1) * 2.2;\n\n      faceShell.setAttribute(\n        'transform',\n        `translate(${bobX.toFixed(2)}, ${bobY.toFixed(2)}) rotate(${tilt.toFixed(2)} 150 150)`\n      );\n    }\n\n    function faceSpeak(text, emotion = \"neutral\", durationHint = null) {\n      const clean = (text || \"\").trim();\n      if (!clean) return;\n\n      clearInterval(faceTalkTimer);\n      isFaceTalking = true;\n      setFaceEmotion(emotion);\n\n      const duration = durationHint || Math.max(1600, clean.length * 70);\n      showFaceBubble(clean, duration);\n\n      faceTalkTimer = setInterval(() => talkingFrame(emotion), 120);\n\n      setTimeout(() => {\n        stopFaceTalking();\n      }, duration);\n    }\n\n    document.addEventListener('mousemove', (e) => {\n      if (isFaceTalking) return;\n\n      const svg = document.getElementById('face-svg');\n      const rect = svg.getBoundingClientRect();\n      const centerX = rect.left + rect.width \/ 2;\n      const centerY = rect.top + rect.height \/ 2;\n\n      const dx = (e.clientX - centerX) \/ 45;\n      const dy = (e.clientY - centerY) \/ 45;\n      const moveX = Math.max(-5, Math.min(5, dx));\n      const moveY = Math.max(-4, Math.min(4, dy));\n\n      leftPupil.setAttribute('cx', 90 + moveX);\n      leftPupil.setAttribute('cy', 110 + moveY);\n      rightPupil.setAttribute('cx', 210 + moveX);\n      rightPupil.setAttribute('cy', 110 + moveY);\n\n      leftSparkle.setAttribute('cx', 96 + moveX * 0.6);\n      leftSparkle.setAttribute('cy', 104 + moveY * 0.6);\n      rightSparkle.setAttribute('cx', 216 + moveX * 0.6);\n      rightSparkle.setAttribute('cy', 104 + moveY * 0.6);\n\n      faceContainer.style.transform = `translate(${(moveX * 0.25).toFixed(2)}px, ${(moveY * 0.2).toFixed(2)}px)`;\n    });\n\n    function initialize() {\n      loadVoices();\n      setFaceEmotion('neutral');\n      scheduleBlink();\n      scheduleSaccade();\n      startFaceBreathing();\n      bindSectionMenus();\n\n      if (!speechSynthesisSupported) appendMessage(\"Sorry, your browser does not support speech synthesis.\", \"bot\");\n      if (!speechRecognitionSupported) appendMessage(\"Sorry, your browser does not support speech recognition.\", \"bot\");\n      if (speechRecognitionSupported) createRecognitionInstance();\n\n      updateSectionUI();\n      appendMessage(\"Click Start to begin the Spanish lesson.\", \"bot\");\n\n      document.getElementById('start-btn').onclick = async function() {\n        if (isLessonActive) return;\n        if (!speechSynthesisSupported || !speechRecognitionSupported) return;\n\n        clearChat();\n        clearWordBox();\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        const greetingText = sectionData[currentSection].intro;\n        appendMessage(greetingText, 'bot');\n        faceSpeak(\"Welcome.\", \"happy\");\n        showSynthesisLoader(true);\n\n        speakEnglish(greetingText).then(() => {\n          showSynthesisLoader(false);\n          startLesson();\n        });\n      };\n\n      document.getElementById('stop-btn').onclick = function() {\n        endLesson(true);\n      };\n    }\n\n    function bindSectionMenus() {\n      document.querySelectorAll('.menu-link').forEach(link => {\n        link.addEventListener('click', function(e) {\n          e.preventDefault();\n          const section = this.dataset.section;\n          switchSection(section);\n        });\n      });\n    }\n\n    function switchSection(section) {\n      if (!sectionData[section]) return;\n\n      stopAllAudioAndRecognition();\n      isLessonActive = false;\n      currentListeningItem = null;\n      currentSection = section;\n      lessons = sectionData[currentSection].lessons;\n      currentLessonIndex = 0;\n      retryCount = 0;\n\n      clearChat();\n      clearWordBox();\n      updateSectionUI();\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    async function requestMicrophoneOnce() {\n      if (micAccessGranted) return true;\n\n      if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {\n        appendMessage(\"Your browser does not support microphone access.\", \"bot\");\n        return false;\n      }\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(\"Microphone access was blocked.\", \"sad\");\n        return false;\n      }\n    }\n\n    function createRecognitionInstance() {\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 feedbackType = analyzeResponse(formattedInput, currentListeningItem.keywords);\n        let feedbackMessage = \"\";\n        let faceEmotion = \"neutral\";\n\n        if (feedbackType === \"positive\") {\n          feedbackMessage = pickRandom(positiveFeedback);\n          faceEmotion = \"happy\";\n        } else if (feedbackType === \"pronunciation\") {\n          feedbackMessage = pickRandom(pronunciationFeedback);\n          faceEmotion = \"neutral\";\n        } else {\n          feedbackMessage = pickRandom(constructiveFeedback);\n          faceEmotion = \"confused\";\n        }\n\n        appendMessage(feedbackMessage, 'bot');\n        faceSpeak(feedbackMessage, faceEmotion);\n        showSynthesisLoader(true);\n\n        speakEnglish(feedbackMessage).then(() => {\n          showSynthesisLoader(false);\n          if (!isLessonActive) return;\n          currentLessonIndex++;\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        let errorMessage = \"\";\n\n        switch (event.error) {\n          case \"not-allowed\":\n            micAccessGranted = false;\n            errorMessage = \"Microphone access was blocked. Please allow microphone access and try again.\";\n            appendMessage(errorMessage, 'bot');\n            faceSpeak(errorMessage, \"sad\");\n            break;\n\n          case \"no-speech\":\n            retryCount++;\n            if (retryCount <= maxRetriesPerQuestion) {\n              errorMessage = \"I did not hear you. Please repeat the Spanish sentence again.\";\n              appendMessage(errorMessage, 'bot');\n              faceSpeak(\"Please repeat.\", \"confused\");\n              showSynthesisLoader(true);\n              speakSpanish(\"Por favor, repite.\").then(() => {\n                showSynthesisLoader(false);\n                listenUserResponse(currentListeningItem);\n              });\n            } else {\n              errorMessage = \"Let's go to the next sentence.\";\n              appendMessage(errorMessage, 'bot');\n              faceSpeak(errorMessage, \"neutral\");\n              showSynthesisLoader(true);\n              speakEnglish(errorMessage).then(() => {\n                showSynthesisLoader(false);\n                currentLessonIndex++;\n                startLesson();\n              });\n            }\n            break;\n\n          default:\n            appendMessage(\"There was a speech recognition error. Please try again.\", \"bot\");\n            faceSpeak(\"Please try again.\", \"sad\");\n            showSynthesisLoader(true);\n            speakSpanish(\"Por favor, intenta otra vez.\").then(() => {\n              showSynthesisLoader(false);\n              listenUserResponse(currentListeningItem);\n            });\n            break;\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\n        spanishVoices = voices.filter(v =>\n          \/^es(-|_)\/i.test(v.lang) || \/spanish|espa\u00f1ol\/i.test(v.name)\n        );\n\n        englishVoices = voices.filter(v =>\n          \/^en(-|_)\/i.test(v.lang) || \/english\/i.test(v.name)\n        );\n\n        spanishVoices.sort((a, b) => getSpanishVoicePriority(a) - getSpanishVoicePriority(b));\n        englishVoices.sort((a, b) => getEnglishVoicePriority(a) - getEnglishVoicePriority(b));\n\n        selectedSpanishVoice = spanishVoices[0] || null;\n        selectedEnglishVoice = englishVoices[0] || null;\n\n        populateSpanishVoiceList();\n      };\n\n      fillVoices();\n      if (typeof speechSynthesis !== \"undefined\") speechSynthesis.onvoiceschanged = fillVoices;\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 getEnglishVoicePriority(voice) {\n      const name = voice.name.toLowerCase();\n      if (name.includes(\"google us english\")) return 1;\n      if (name.includes(\"google uk english\")) return 2;\n      if (name.includes(\"microsoft\") && name.includes(\"english\")) return 3;\n      if (name.includes(\"english\")) return 4;\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      retryCount = 0;\n      askCurrentSentence();\n    }\n\n    function askCurrentSentence() {\n      if (!isLessonActive) return;\n\n      const item = lessons[currentLessonIndex];\n      const sentenceNumber = currentLessonIndex + 1;\n\n      const displayText = `Sentence ${sentenceNumber}.\\nEnglish: ${item.english}\\nSpanish: ${item.spanish}`;\n      appendMessage(displayText, 'bot');\n\n      addSentenceToBox(`English: ${item.english}`);\n      addSentenceToBox(`Spanish: ${item.spanish}`);\n      addSentenceToBox(`--------------------`);\n\n      faceSpeak(item.spanish, \"neutral\");\n      showSynthesisLoader(true);\n\n      speakEnglish(`Sentence ${sentenceNumber}. ${item.english}`)\n        .then(() => speakSpanish(item.spanish))\n        .then(() => speakSpanish(item.spanish))\n        .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\n      speechRecognitionActive = true;\n      showRecognitionLoader(true);\n      appendMessage(\"Listening... Please repeat in Spanish.\", \"bot\");\n      faceSpeak(\"Escucha y repite.\", \"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(\"Please try again.\", \"sad\");\n      }\n    }\n\n    function analyzeResponse(userInput, keywords) {\n      const input = userInput.toLowerCase();\n      let matchCount = 0;\n      keywords.forEach(keyword => {\n        if (input.includes(keyword.toLowerCase())) matchCount++;\n      });\n\n      const wordCount = input.split(\/\\s+\/).filter(Boolean).length;\n\n      if (matchCount >= 2 || wordCount >= 4) return \"positive\";\n      if (matchCount >= 1) return \"pronunciation\";\n      return \"constructive\";\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        ? \"Thank you. Goodbye.\"\n        : `Excellent. You finished the ${sectionData[currentSection].title} section. Keep practicing.`;\n\n      appendMessage(message, \"bot\");\n      faceSpeak(message, userStopped ? \"neutral\" : \"happy\");\n      showSynthesisLoader(true);\n\n      if (userStopped) {\n        speakEnglish(\"Thank you. Goodbye.\")\n          .then(() => speakSpanish(\"Adi\u00f3s.\"))\n          .then(() => showSynthesisLoader(false));\n      } else {\n        speakEnglish(message).then(() => showSynthesisLoader(false));\n      }\n    }\n\n    function speakText(text, lang, voiceObj) {\n      return new Promise(resolve => {\n        if (!speechSynthesisSupported) {\n          resolve();\n          return;\n        }\n\n        window.speechSynthesis.cancel();\n\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\n        utterance.onend = () => resolve();\n        utterance.onerror = () => resolve();\n\n        window.speechSynthesis.speak(utterance);\n      });\n    }\n\n    function speakEnglish(text) {\n      return speakText(text, \"en-US\", selectedEnglishVoice);\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: Greetings and Introductions Greetings and introductions Asking names Asking ages Asking where someone is from Asking feelings Likes<\/p>\n","protected":false},"author":1,"featured_media":581,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"colormag_page_layout":"default_layout","footnotes":""},"categories":[63,54],"tags":[],"class_list":["post-858","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-intermediate","category-spanish"],"_links":{"self":[{"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/posts\/858","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=858"}],"version-history":[{"count":1,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/posts\/858\/revisions"}],"predecessor-version":[{"id":859,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/posts\/858\/revisions\/859"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/media\/581"}],"wp:attachment":[{"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/media?parent=858"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/categories?post=858"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/tags?post=858"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}