{"id":2893,"date":"2026-09-03T08:53:29","date_gmt":"2026-09-03T01:53:29","guid":{"rendered":"https:\/\/thaichristian.ac.th\/?p=2893"},"modified":"2026-09-10T10:18:15","modified_gmt":"2026-09-10T03:18:15","slug":"tcs-swimming-oct","status":"publish","type":"post","link":"https:\/\/thaichristian.ac.th\/th\/tcs-swimming-oct.html","title":{"rendered":"TCS SWIMMING [OCTOBER CAMP 2026]"},"content":{"rendered":"<h1>TCS Swimming [October Camp 2026]<\/h1>\n<p>&nbsp;<\/p>\n<p><a href=\"https:\/\/thaichristian.ac.th\/wp-content\/uploads\/2026\/09\/Swimming_2026_October.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-2905 size-large\" src=\"https:\/\/thaichristian.ac.th\/wp-content\/uploads\/2026\/09\/Swimming_2026_October-1024x1024.png\" alt=\"\" width=\"625\" height=\"625\" srcset=\"https:\/\/thaichristian.ac.th\/wp-content\/uploads\/2026\/09\/Swimming_2026_October-1024x1024.png 1024w, https:\/\/thaichristian.ac.th\/wp-content\/uploads\/2026\/09\/Swimming_2026_October-300x300.png 300w, https:\/\/thaichristian.ac.th\/wp-content\/uploads\/2026\/09\/Swimming_2026_October-150x150.png 150w, https:\/\/thaichristian.ac.th\/wp-content\/uploads\/2026\/09\/Swimming_2026_October-768x768.png 768w, https:\/\/thaichristian.ac.th\/wp-content\/uploads\/2026\/09\/Swimming_2026_October-60x60.png 60w, https:\/\/thaichristian.ac.th\/wp-content\/uploads\/2026\/09\/Swimming_2026_October-390x390.png 390w, https:\/\/thaichristian.ac.th\/wp-content\/uploads\/2026\/09\/Swimming_2026_October-624x624.png 624w, https:\/\/thaichristian.ac.th\/wp-content\/uploads\/2026\/09\/Swimming_2026_October.png 1080w\" sizes=\"auto, (max-width: 625px) 100vw, 625px\" \/><\/a><\/p>\n<p><a href=\"https:\/\/thaichristian.ac.th\/wp-content\/uploads\/2026\/06\/Swimming_2026_T1_Details.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-2846\" src=\"https:\/\/thaichristian.ac.th\/wp-content\/uploads\/2026\/06\/Swimming_2026_T1_Details.png\" alt=\"\" width=\"624\" height=\"523\" srcset=\"https:\/\/thaichristian.ac.th\/wp-content\/uploads\/2026\/06\/Swimming_2026_T1_Details.png 940w, https:\/\/thaichristian.ac.th\/wp-content\/uploads\/2026\/06\/Swimming_2026_T1_Details-300x251.png 300w, https:\/\/thaichristian.ac.th\/wp-content\/uploads\/2026\/06\/Swimming_2026_T1_Details-768x644.png 768w, https:\/\/thaichristian.ac.th\/wp-content\/uploads\/2026\/06\/Swimming_2026_T1_Details-390x327.png 390w, https:\/\/thaichristian.ac.th\/wp-content\/uploads\/2026\/06\/Swimming_2026_T1_Details-624x523.png 624w\" sizes=\"auto, (max-width: 624px) 100vw, 624px\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<p><strong><span style=\"font-size: 14pt;\">Line OpenChat &#8220;TCS SWIMMING (OCTOBER CAMP 2026)&#8221;<\/span><\/strong><br \/>\nScan the QR Code or tap the link below to join.<br \/>\n\u0e2a\u0e41\u0e01\u0e19 QR Code \u0e2b\u0e23\u0e37\u0e2d\u0e01\u0e14\u0e25\u0e34\u0e07\u0e01\u0e4c\u0e14\u0e49\u0e32\u0e19\u0e25\u0e48\u0e32\u0e07\u0e40\u0e1e\u0e37\u0e48\u0e2d\u0e40\u0e02\u0e49\u0e32\u0e23\u0e48\u0e27\u0e21\u0e01\u0e25\u0e38\u0e48\u0e21<br \/>\n<a href=\"https:\/\/line.me\/ti\/g2\/KvPl93JNxDR3pPitKvd__VCOvm1nxZCLK6yi3Q?utm_source=invitation&amp;utm_medium=link_copy&amp;utm_campaign=default\">https:\/\/line.me\/ti\/g2\/KvPl93JNxDR3pPitKvd__VCOvm1nxZCLK6yi3Q?utm_source=invitation&amp;utm_medium=link_copy&amp;utm_campaign=default<\/a><\/p>\n<p><a href=\"https:\/\/thaichristian.ac.th\/wp-content\/uploads\/2026\/09\/Swimming_2026_October_OpenChat.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-2912\" src=\"https:\/\/thaichristian.ac.th\/wp-content\/uploads\/2026\/09\/Swimming_2026_October_OpenChat-300x300.jpg\" alt=\"\" width=\"300\" height=\"300\" srcset=\"https:\/\/thaichristian.ac.th\/wp-content\/uploads\/2026\/09\/Swimming_2026_October_OpenChat-300x300.jpg 300w, https:\/\/thaichristian.ac.th\/wp-content\/uploads\/2026\/09\/Swimming_2026_October_OpenChat-150x150.jpg 150w, https:\/\/thaichristian.ac.th\/wp-content\/uploads\/2026\/09\/Swimming_2026_October_OpenChat-768x768.jpg 768w, https:\/\/thaichristian.ac.th\/wp-content\/uploads\/2026\/09\/Swimming_2026_October_OpenChat-60x60.jpg 60w, https:\/\/thaichristian.ac.th\/wp-content\/uploads\/2026\/09\/Swimming_2026_October_OpenChat-390x390.jpg 390w, https:\/\/thaichristian.ac.th\/wp-content\/uploads\/2026\/09\/Swimming_2026_October_OpenChat-624x624.jpg 624w, https:\/\/thaichristian.ac.th\/wp-content\/uploads\/2026\/09\/Swimming_2026_October_OpenChat.jpg 900w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"text-decoration: underline; font-size: 14pt;\"><span style=\"color: #ff0000;\"><strong>APPLICATION FORM<\/strong><\/span><\/span><\/p>\n<p>&nbsp;<\/p>\n<style>\r\n  .spinner { \r\n    border: 4px solid #f3f3f3; border-top: 4px solid #494BB3; border-radius: 50%; \r\n    width: 24px; height: 24px; animation: spin 1s linear infinite; display: inline-block; vertical-align: middle; margin-right: 8px; \r\n  }\r\n  @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }\r\n  \r\n  #main-form-title, #success-container h2 { font-size: 1.8em !important; line-height: 1.3 !important; }\r\n  .question-container {\r\n    padding: 15px; border: 2px solid transparent; border-radius: 8px;\r\n    transition: background-color 0.3s, border-color 0.3s; margin-bottom: 15px !important; \r\n  }\r\n  .question-container input[type=\"text\"],\r\n  .question-container input[type=\"email\"],\r\n  .question-container input[type=\"number\"],\r\n  .question-container select {\r\n    font-size: 16px !important; font-family: inherit !important; padding: 10px !important; box-sizing: border-box !important; height: auto !important;\r\n  }\r\n  \r\n  .checkbox-group-box { background-color: #f8f9fa; border: 1px solid #dee2e6; border-radius: 6px; padding: 12px; margin-top: 8px; }\r\n  .opt-wrapper {\r\n    margin-bottom: 4px; padding: 4px 6px; border-radius: 6px; border: 1px solid transparent; transition: all 0.2s ease;\r\n    font-size: 16px !important; line-height: 1.4 !important; display: flex !important; align-items: flex-start !important; justify-content: flex-start !important; gap: 10px !important;               \r\n  }\r\n  \r\n  .opt-wrapper input[type=\"checkbox\"],\r\n  .opt-wrapper input[type=\"radio\"] { margin: 3px 0 0 0 !important; flex-shrink: 0 !important; width: 18px !important; height: 18px !important; min-width: 18px !important; display: block !important; appearance: auto !important; cursor: pointer; }\r\n  .selected-opt { background-color: #d4edda !important; border-color: #c3e6cb !important; }\r\n  .disabled-option { color: #a0a0a0; font-style: italic; }\r\n  .disabled-option input { cursor: not-allowed; }\r\n  \r\n  .persistent-highlight { background-color: #fff3cd !important; border-color: #ffb300 !important; }\r\n  \r\n  .custom-file-upload { display: inline-block; padding: 10px 20px; cursor: pointer; background-color: #f0f0f0; border: 1px solid #ccc; border-radius: 5px; font-weight: bold; color: #333; transition: background 0.2s; }\r\n  .custom-file-name { margin-left: 10px; color: #555; font-style: italic; font-size: 0.9em; }\r\n  \r\n  .submit-btn { padding: 12px 35px; font-size: 1.2em; background-color: #28a745; color: white; border: none; border-radius: 8px; cursor: pointer; font-weight: bold; transition: background-color 0.3s, transform 0.1s; }\r\n  .submit-btn:hover:not(.looks-disabled) { background-color: #218838; transform: scale(1.02); }\r\n  .submit-btn.looks-disabled { background-color: #9e9e9e; transform: none; }\r\n<\/style>\r\n\r\n<div id=\"custom-alert-modal\" style=\"display:none; position:fixed !important; z-index:999999 !important; top:0 !important; left:0 !important; width:100vw !important; height:100vh !important; background:rgba(0,0,0,0.6); align-items:center; justify-content:center;\">\r\n  <div style=\"background:white; padding:30px; border-radius:8px; max-width:400px; width:90%; text-align:center; box-shadow:0 10px 25px rgba(0,0,0,0.3); font-family:sans-serif;\">\r\n    <h3 style=\"color:#d9534f; margin-top:0; border-bottom: 2px solid #eee; padding-bottom: 10px;\">Selection Unavailable<\/h3>\r\n    <p id=\"custom-alert-msg\" style=\"font-size:1.1em; color:#333; margin:20px 0;\"><\/p>\r\n    <button id=\"custom-alert-btn\" style=\"background:#494BB3; color:white; border:none; padding:12px 25px; border-radius:5px; font-size:1em; font-weight:bold; cursor:pointer;\">OK<\/button>\r\n  <\/div>\r\n<\/div>\r\n\r\n<div id=\"lightbox-modal\" style=\"display:none; position:fixed !important; z-index:999999 !important; top:0 !important; left:0 !important; width:100vw !important; height:100vh !important; background:rgba(0,0,0,0.9); align-items: center; justify-content: center; overflow: hidden;\">\r\n  <div id=\"lightbox-close\" style=\"position:absolute; top:20px; right:30px; color:white; font-size:30px; font-weight:bold; cursor:pointer; background:rgba(0,0,0,0.7); border-radius:50%; width:45px; height:45px; display:flex; align-items:center; justify-content:center; z-index:1000000;\">&times;<\/div>\r\n  <img id=\"lightbox-img\" style=\"max-width:98% !important; max-height:98% !important; object-fit:contain; transition: transform 0.3s ease; cursor: zoom-in;\">\r\n<\/div>\r\n\r\n<form id=\"data-form\" style=\"max-width: 600px; margin: auto;\">\r\n  <div id=\"form-header-container\" style=\"text-align:center; margin-bottom: 20px;\">\r\n    <h2 id=\"main-form-title\" style=\"margin-bottom: 5px; color: #494BB3;\"><\/h2>\r\n    <div id=\"closed-message\" style=\"display:none; color: red; font-weight: bold; font-size: 1.2em; margin-top: 20px;\">\r\n      Sorry, this form is not open at the moment.<br><span style=\"font-size: 0.85em; font-weight: normal; color: #666;\">\u0e02\u0e2d\u0e2d\u0e20\u0e31\u0e22 \u0e1f\u0e2d\u0e23\u0e4c\u0e21\u0e19\u0e35\u0e49\u0e22\u0e31\u0e07\u0e44\u0e21\u0e48\u0e40\u0e1b\u0e34\u0e14\u0e23\u0e31\u0e1a\u0e01\u0e32\u0e23\u0e15\u0e2d\u0e1a\u0e01\u0e25\u0e31\u0e1a\u0e43\u0e19\u0e02\u0e13\u0e30\u0e19\u0e35\u0e49<\/span>\r\n    <\/div>\r\n  <\/div>\r\n  \r\n  <div id=\"form-error-banner\" style=\"color:red; font-weight:bold; margin-bottom: 15px; text-align: center;\"><\/div>\r\n  \r\n  <div id=\"dynamic-loader\" style=\"text-align: center; padding: 20px; color: #555; font-weight: bold; font-size: 1.1em; background: #f9f9f9; border-radius: 8px; border: 1px dashed #ccc; min-height: 85px; display: flex; align-items: center; justify-content: center;\">\r\n    <div><div class=\"spinner\"><\/div> LOADING FORM...<\/div>\r\n  <\/div>\r\n  \r\n  <div id=\"form-body-wrapper\" style=\"display:none;\">\r\n    <div id=\"dynamic-questions-container\"><\/div>\r\n    \r\n    <div id=\"global-error-warning\" style=\"display:none; color:red; font-weight:bold; margin-bottom: 10px; margin-top: 20px; padding:10px; border:1px solid red; background-color:#ffe6e6; border-radius: 4px;\">\r\n      ERROR: PLEASE REVIEW THE FORM ABOVE AND FIX HIGHLIGHTED ISSUES.\r\n    <\/div>\r\n    \r\n    <div style=\"display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; margin-top: 35px; margin-bottom: 20px;\">\r\n      <button type=\"submit\" id=\"submit-button\" class=\"submit-btn looks-disabled\">&#10003; Submit<\/button>\r\n      <span id=\"incomplete-warning\" style=\"color: gray; font-size: 0.95em; font-weight: bold;\">(Please complete all required questions)<\/span>\r\n    <\/div>\r\n    \r\n    <div id=\"status-message\" style=\"margin-top: 15px; font-weight: bold; color: #494BB3; text-align: center;\"><\/div>\r\n  <\/div>\r\n<\/form>\r\n\r\n<div id=\"success-container\" style=\"display:none; border: 2px solid #4CAF50; padding: 20px; max-width: 600px; margin: auto; border-radius: 8px; font-family: sans-serif;\">\r\n  <h2 style=\"color: #4CAF50; margin-top: 0;\">\u2713 Submission Successful!<\/h2>\r\n  <p id=\"success-msg-desc\">Your form has been received. (Click below to save a copy for your own records.)<\/p>\r\n  <div id=\"receipt-text\" style=\"background: #f4f4f4; border: 1px solid #ddd; padding: 15px; margin-bottom: 15px; font-family: sans-serif; font-size: 15px; max-height: 400px; overflow-y: auto;\"><\/div>\r\n  \r\n  <div style=\"display: flex; flex-direction: column; gap: 10px; align-items: center;\">\r\n    <button id=\"save-receipt-btn\" style=\"background: #008CBA; color: white; padding: 10px 15px; border: none; border-radius: 4px; cursor: pointer; font-size: 1.1em; font-weight:bold; width: 100%; max-width: 300px;\">\ud83d\udce5 Save Receipt as Image<\/button>\r\n    <button id=\"new-form-btn\" style=\"background: #6c757d; color: white; padding: 10px 15px; border: none; border-radius: 4px; cursor: pointer; font-size: 1.1em; font-weight:bold; width: 100%; max-width: 300px; display: none;\">\ud83d\udcc4 NEW FORM<\/button>\r\n  <\/div>\r\n<\/div>\r\n\r\n<script>\r\n  const SCRIPT_URL = \"https:\/\/script.google.com\/macros\/s\/AKfycbxHO99SscjtWr8mkJZi6Ouyn8IYBgQO_AtJtN7foikjixJWfKLsSqwD74V4JHAD6fzVSw\/exec\";\r\n  let formConfig = {}; let isConfigLoaded = false; \r\n  window.currentReceiptData = { total: 0, subtotal: 0, discount: 0, mainItems: [], adjustingItems: [] };\r\n  \r\n  async function fetchWithRetry(url, options = {}, retries = 3, delay = 2000) {\r\n    for (let i = 0; i < retries; i++) {\r\n      try { return await (await fetch(url, options)).json(); } \r\n      catch (err) { if (i === retries - 1) throw err; await new Promise(r => setTimeout(r, delay)); }\r\n    }\r\n  }\r\n  \r\n  function getFormattedDate() {\r\n    const d = new Date(); const day = String(d.getDate()).padStart(2, '0');\r\n    const month = d.toLocaleString('en-US', { month: 'short' });\r\n    return `${day}-${month}-${d.getFullYear()} ${d.toLocaleTimeString('en-GB', { hour12: false })}`;\r\n  }\r\n  \r\n  document.addEventListener('DOMContentLoaded', async () => {\r\n    const form = document.getElementById('data-form');\r\n    const formBody = document.getElementById('form-body-wrapper');\r\n    const dynamicContainer = document.getElementById('dynamic-questions-container');\r\n    const dynamicLoader = document.getElementById('dynamic-loader');\r\n    const submitBtn = document.getElementById('submit-button');\r\n    const warningSpan = document.getElementById('incomplete-warning');\r\n    const globalError = document.getElementById('global-error-warning');\r\n    \r\n    document.getElementById('new-form-btn').addEventListener('click', () => { window.location.reload(); });\r\n    const customAlert = document.getElementById('custom-alert-modal');\r\n    const customAlertMsg = document.getElementById('custom-alert-msg');\r\n    document.getElementById('custom-alert-btn').addEventListener('click', () => customAlert.style.display = 'none');\r\n    \r\n    const lightbox = document.getElementById('lightbox-modal');\r\n    const lightboxImg = document.getElementById('lightbox-img');\r\n    let isZoomed = false;\r\n    const closeLightbox = (e) => { if(e) e.stopPropagation(); lightbox.style.display = 'none'; isZoomed = false; lightboxImg.style.transform = 'scale(1)'; lightboxImg.style.cursor = 'zoom-in'; };\r\n    document.getElementById('lightbox-close').addEventListener('click', closeLightbox); lightbox.addEventListener('click', closeLightbox); \r\n    lightboxImg.addEventListener('click', (e) => { e.stopPropagation(); lightboxImg.style.transform = isZoomed ? 'scale(1)' : 'scale(1.5)'; lightboxImg.style.cursor = isZoomed ? 'zoom-in' : 'zoom-out'; isZoomed = !isZoomed; });\r\n    \r\n    \/\/ --- LOAD CONFIGURATION ---\r\n    try {\r\n      const result = await fetchWithRetry(`${SCRIPT_URL}?action=getConfig`);\r\n      if (result.status !== 'success') throw new Error(result.message);\r\n      \r\n      formConfig = result.config;\r\n      isConfigLoaded = true; \r\n      \r\n      document.getElementById('main-form-title').textContent = formConfig.title;\r\n      if (formConfig.formStatus === 'CLOSED') {\r\n        dynamicLoader.style.display = 'none';\r\n        document.getElementById('closed-message').style.display = 'block';\r\n        return; \r\n      }\r\n      buildDynamicQuestions();\r\n      dynamicLoader.style.display = 'none';\r\n      formBody.style.display = 'block'; \r\n      applyDynamicFiltering();\r\n      \r\n    } catch (err) {\r\n      console.error(err);\r\n      dynamicLoader.style.display = 'none';\r\n      document.getElementById('form-error-banner').innerHTML = `Form temporarily unavailable. Please try again later.<br><span style=\"font-size:0.85em; font-weight:normal; color:#555;\">\u0e1f\u0e2d\u0e23\u0e4c\u0e21\u0e44\u0e21\u0e48\u0e1e\u0e23\u0e49\u0e2d\u0e21\u0e43\u0e0a\u0e49\u0e07\u0e32\u0e19\u0e43\u0e19\u0e02\u0e13\u0e30\u0e19\u0e35\u0e49 \u0e42\u0e1b\u0e23\u0e14\u0e25\u0e2d\u0e07\u0e2d\u0e35\u0e01\u0e04\u0e23\u0e31\u0e49\u0e07\u0e20\u0e32\u0e22\u0e2b\u0e25\u0e31\u0e07<\/span>`;\r\n      submitBtn.style.display = 'none'; \r\n    }\r\n    form.addEventListener('change', applyDynamicFiltering);\r\n    form.addEventListener('input', applyDynamicFiltering);\r\n    \r\n    \/\/ --- HTML UI GENERATORS ---\r\n    function getHeadingHtml(title, desc) {\r\n      let html = `<div style=\"background-color: #494BB3; color: white; padding: 12px; border-radius: 8px; font-weight: bold; font-size: 1.1em; width: 100%; box-sizing: border-box;\">${title}<\/div>`;\r\n      if (desc) html += `<p style=\"font-size:0.9em; color:black; margin-top:5px; white-space: pre-wrap;\">${desc}<\/p>`;\r\n      return html;\r\n    }\r\n    \r\n    function getWaitSpinnerHtml() {\r\n      return `<div style=\"text-align: center; padding: 20px; color: #555; font-weight: bold; font-size: 1.1em; background: #f9f9f9; border-radius: 8px; border: 1px dashed #ccc; box-sizing: border-box;\">\r\n        <div class=\"spinner\"><\/div> Waiting for answers above \/ \u0e01\u0e33\u0e25\u0e31\u0e07\u0e23\u0e2d\u0e04\u0e33\u0e15\u0e2d\u0e1a\u0e14\u0e49\u0e32\u0e19\u0e1a\u0e19\r\n      <\/div>`;\r\n    }\r\n    \r\nfunction isAnswered(container) {\r\n      const inputs = container.querySelectorAll('input, select, textarea');\r\n      \/\/ FIX: If a question is empty of options, it is not answered.\r\n      if (inputs.length === 0) return false; \r\n      \r\n      const type = inputs[0].type;\r\n      if (type === 'radio' || type === 'checkbox') return container.querySelectorAll('input:checked').length > 0;\r\n      if (type === 'file') return inputs[0].files.length > 0;\r\n      if (inputs[0].tagName === 'SELECT') return inputs[0].value.trim() !== '';\r\n      return inputs[0].value.trim() !== '';\r\n    }\r\n    \r\n    \/\/ FIX 2: Replaced previousElementSibling with Array Traversal to avoid DOM wrapper traps\r\n    function checkPreviousRequiredVisible(currentContainer) {\r\n      const allContainers = Array.from(document.querySelectorAll('.question-container'));\r\n      const idx = allContainers.indexOf(currentContainer);\r\n      if (idx <= 0) return true;\r\n      \r\n      for (let i = idx - 1; i >= 0; i--) {\r\n        let curr = allContainers[i];\r\n        if (curr.style.display !== 'none') {\r\n          const t = curr.getAttribute('data-qtitle');\r\n          const cfg = formConfig.questions[t];\r\n          if (cfg && !cfg.isOptional && !['Section_Heading', 'Section_Heading_Wait', 'Display_Total', 'Image'].includes(cfg.type)) {\r\n            if (!isAnswered(curr)) return false;\r\n          }\r\n        }\r\n      }\r\n      return true;\r\n    }\r\n\r\n    \/\/ --- BUILD DOM ELEMENTS ---\r\n    function buildDynamicQuestions() {\r\n      if (!formConfig.questions) return;\r\n      for (const [title, configData] of Object.entries(formConfig.questions)) {\r\n        const wrapper = document.createElement('div');\r\n        wrapper.className = 'question-container';\r\n        wrapper.setAttribute('data-qtitle', title);\r\n        wrapper.setAttribute('data-optional', configData.isOptional ? \"true\" : \"false\"); \r\n        \r\n        const rawType = configData.type;\r\n        if (rawType === 'Section_Heading' || rawType === 'Section_Heading_Wait') {\r\n          wrapper.style.marginTop = '40px'; wrapper.style.padding = '0'; wrapper.style.border = 'none';\r\n          wrapper.innerHTML = getHeadingHtml(title, configData.desc);\r\n          wrapper.classList.add('ui-element');\r\n          dynamicContainer.appendChild(wrapper);\r\n          continue;\r\n        }\r\n        if (rawType === 'Display_Total') {\r\n          wrapper.style.padding = '0'; wrapper.style.border = 'none'; wrapper.style.marginTop = '45px'; \r\n          wrapper.innerHTML = `<div class=\"total-box-container\" style=\"padding: 15px; border: 2px dashed #494BB3; border-radius: 8px; text-align: center; background: #f9f9f9;\">\r\n            <div class=\"itemized-receipt\" style=\"display:none; font-size: 0.85em; margin-bottom: 12px; text-align: left; border-bottom: 1px solid #ccc; padding-bottom: 8px; color: #333;\"><\/div>\r\n            <div style=\"font-size: 1.3em; font-weight: bold; color: #000;\">${title}: \u0e3f<span class=\"calculated-total-val\">0<\/span><\/div>\r\n          <\/div>`;\r\n          wrapper.classList.add('ui-element');\r\n          dynamicContainer.appendChild(wrapper);\r\n          continue;\r\n        }\r\n        if (rawType === 'Image') {\r\n          wrapper.innerHTML = `<h3 style=\"margin-bottom: 5px;\">${title}<\/h3>`;\r\n          if (configData.desc) wrapper.innerHTML += `<p style=\"font-size:0.9em; color:black; margin-top:0; white-space: pre-wrap;\">${configData.desc}<\/p>`;\r\n          configData.options.forEach(opt => {\r\n            if(opt.text) {\r\n              const imgEl = document.createElement('img'); imgEl.src = opt.text;\r\n              imgEl.style.width = '100%'; imgEl.style.height = 'auto'; imgEl.style.borderRadius = '8px';\r\n              imgEl.style.marginBottom = '10px'; imgEl.style.cursor = 'zoom-in'; imgEl.style.border = '1px solid #ccc';\r\n              imgEl.onclick = () => { document.getElementById('lightbox-img').src = opt.text; lightbox.style.display = 'flex'; };\r\n              wrapper.appendChild(imgEl);\r\n            }\r\n          });\r\n          wrapper.classList.add('ui-element');\r\n          dynamicContainer.appendChild(wrapper);\r\n          continue;\r\n        }\r\n        const label = document.createElement('label');\r\n        label.textContent = title; label.style.fontWeight = 'bold';\r\n        \r\n        if (configData.isOptional) {\r\n          const optSpan = document.createElement('span'); optSpan.textContent = ' (Optional)';\r\n          optSpan.style.fontWeight = 'normal'; optSpan.style.color = '#666'; optSpan.style.fontSize = '0.9em';\r\n          label.appendChild(optSpan);\r\n        }\r\n        wrapper.appendChild(label);\r\n        \r\n        if (configData.desc) {\r\n          const desc = document.createElement('p'); desc.textContent = configData.desc;\r\n          desc.style.fontSize = '0.9em'; desc.style.color = 'black'; desc.style.margin = '2px 0 8px 0'; desc.style.whiteSpace = 'pre-wrap'; \r\n          wrapper.appendChild(desc);\r\n        }\r\n        \r\n        const inputContainer = document.createElement('div');\r\n        inputContainer.className = 'q-input-container';\r\n        wrapper.appendChild(inputContainer);\r\n        renderInputForQuestion(wrapper, configData, title);\r\n        dynamicContainer.appendChild(wrapper);\r\n      }\r\n    }\r\n\r\n    function renderInputForQuestion(container, configData, title) {\r\n      const inputWrapper = container.querySelector('.q-input-container');\r\n      let qType = configData.type;\r\n      \r\n      if (qType === 'Attach_Image') {\r\n        const uploadWrapper = document.createElement('div');\r\n        const fileLabel = document.createElement('label'); fileLabel.className = 'custom-file-upload'; fileLabel.innerHTML = '\ud83d\udce4 Choose File...';\r\n        const input = document.createElement('input'); input.type = 'file'; input.name = title; input.accept = \".jpg, .jpeg, .png, image\/jpeg, image\/png\"; input.style.display = 'none'; \r\n        const fileNameDisplay = document.createElement('span'); fileNameDisplay.className = 'custom-file-name'; fileNameDisplay.textContent = 'No file chosen';\r\n        input.addEventListener('change', (e) => { fileNameDisplay.textContent = e.target.files[0] ? e.target.files[0].name : 'No file chosen'; applyDynamicFiltering(); });\r\n        fileLabel.appendChild(input); uploadWrapper.appendChild(fileLabel); uploadWrapper.appendChild(fileNameDisplay); inputWrapper.appendChild(uploadWrapper);\r\n        return;\r\n      }\r\n      \r\n      let regexPattern = null;\r\n      if (qType && qType.startsWith('Input_Text')) {\r\n        const match = qType.match(\/Input_Text\\((.+)\\)\/);\r\n        if (match) regexPattern = match[1]; qType = 'Input_Text'; \r\n      }\r\n      \r\n      if (['Input_Text', 'Input_Number'].includes(qType)) {\r\n        const input = document.createElement('input'); input.type = qType === 'Input_Number' ? 'number' : 'text'; input.name = title; input.style.width = '100%';\r\n        if (qType === 'Input_Text' && title.toLowerCase().includes('email')) input.type = 'email'; \r\n        inputWrapper.appendChild(input);\r\n        \r\n        if (regexPattern) {\r\n          const errorSpan = document.createElement('div'); errorSpan.className = 'regex-error'; errorSpan.style.color = 'red'; errorSpan.style.fontWeight = 'bold'; errorSpan.style.fontSize = '0.85em'; errorSpan.style.marginTop = '4px'; errorSpan.style.display = 'none'; errorSpan.textContent = `ERROR: Please match the required format.`;\r\n          inputWrapper.appendChild(errorSpan);\r\n          input.addEventListener('focus', function() { errorSpan.style.display = 'none'; this.classList.remove('is-invalid'); });\r\n          input.addEventListener('blur', function() {\r\n            if (this.value) {\r\n              try {\r\n                if (!new RegExp(regexPattern).test(this.value)) { errorSpan.style.display = 'block'; this.classList.add('is-invalid'); } \r\n                else { errorSpan.style.display = 'none'; this.classList.remove('is-invalid'); }\r\n              } catch(e) {}\r\n            } else { errorSpan.style.display = 'none'; this.classList.remove('is-invalid'); }\r\n            applyDynamicFiltering();\r\n          });\r\n        }\r\n        return;\r\n      }\r\n      \r\n      if (qType === 'Select_Dropdown') {\r\n        const select = document.createElement('select'); select.name = title; select.style.width = '100%';\r\n        const defaultOpt = document.createElement('option'); defaultOpt.value = \"\"; defaultOpt.textContent = \"--Select--\";\r\n        select.appendChild(defaultOpt);\r\n        \r\n        configData.options.forEach(opt => {\r\n          const optionEl = document.createElement('option'); optionEl.value = opt.text; \r\n          let isOptDisabled = opt.isDisabled || \/\\[UNAVAILABLE\\\/FULL\\]\/i.test(opt.text);\r\n          let optString = opt.text;\r\n          if (isOptDisabled && !optString.includes('[UNAVAILABLE\/FULL]')) { optString += \" (FULL)\"; }\r\n          optionEl.textContent = optString;\r\n          if (isOptDisabled) optionEl.disabled = true;\r\n          if (opt.optFilters && opt.optFilters.length > 0) optionEl.dataset.filters = opt.optFilters.join(',').toLowerCase();\r\n          select.appendChild(optionEl);\r\n        });\r\n        \r\n        inputWrapper.appendChild(select);\r\n        container._optParent = select;\r\n        container._managedOptions = Array.from(select.querySelectorAll('option:not([value=\"\"])'));\r\n        return;\r\n      }\r\n      \r\n      if (['Select_Radio', 'Multiple_Selection', 'Checkbox'].includes(qType)) {\r\n        const isMultiple = qType === 'Multiple_Selection' || qType === 'Checkbox';\r\n        inputWrapper.classList.add('checkbox-group-box'); \r\n        configData.options.forEach(opt => {\r\n          const optDiv = document.createElement('div'); optDiv.className = 'opt-wrapper'; \r\n          const input = document.createElement('input'); input.type = isMultiple ? 'checkbox' : 'radio'; input.name = title; input.value = opt.text; \r\n          \r\n          let extractedDays = [];\r\n          const bracketMatches = opt.text.match(\/\\[(.*?)\\]\/g);\r\n          if (bracketMatches) {\r\n            bracketMatches.forEach(bracket => {\r\n              const dayMatch = bracket.match(\/\\b(MON|TUE|WED|THU|FRI)\\b\/g);\r\n              if (dayMatch) extractedDays.push(...dayMatch);\r\n            });\r\n          }\r\n          if (extractedDays.length > 0) input.dataset.days = [...new Set(extractedDays)].join(',');\r\n          \r\n          const lbl = document.createElement('label'); lbl.style.cursor = 'pointer';\r\n          let highlightedText = opt.text.replace(\/(\\{.*?\\})\/g, '<span style=\"color: #007bff; font-weight: bold;\">$1<\/span>');\r\n          highlightedText = highlightedText.replace(\/\\[UNAVAILABLE\\\/FULL\\]\/gi, '<span style=\"color: darkred; font-weight: normal;\">[UNAVAILABLE\/FULL]<\/span>');\r\n          lbl.innerHTML = highlightedText; \r\n          \r\n          let isOptDisabled = opt.isDisabled || \/\\[UNAVAILABLE\\\/FULL\\]\/i.test(opt.text);\r\n          if (isOptDisabled) { input.disabled = true; lbl.classList.add('disabled-option'); optDiv.style.cursor = 'not-allowed'; lbl.style.cursor = 'not-allowed'; }\r\n          if (opt.optFilters && opt.optFilters.length > 0) optDiv.dataset.filters = opt.optFilters.join(',').toLowerCase();\r\n          \r\n          optDiv.appendChild(input); optDiv.appendChild(lbl); inputWrapper.appendChild(optDiv);\r\n        });\r\n        \r\n        container._optParent = inputWrapper;\r\n        container._managedOptions = Array.from(inputWrapper.querySelectorAll('.opt-wrapper'));\r\n      }\r\n    }\r\n\r\n\/\/ --- TOP-DOWN FILTERING ---\r\n    function applyDynamicFiltering() {\r\n      const activeTokens = new Set();\r\n      let blockAllSubsequent = false;\r\n      const qContainers = document.querySelectorAll('.question-container');\r\n      const allContainersArray = Array.from(qContainers);\r\n      \r\n      qContainers.forEach(container => {\r\n        const title = container.getAttribute('data-qtitle');\r\n        const configData = formConfig.questions[title];\r\n        if (!configData) return;\r\n        \r\n        \/\/ 1. Barrier Active Check\r\n        if (blockAllSubsequent) {\r\n          container.style.display = 'none';\r\n          deselectInputsInContainer(container);\r\n          return;\r\n        }\r\n        \r\n        \/\/ 2. Question Visibility Check\r\n        let showQuestion = true;\r\n        if (configData.questionFilters && configData.questionFilters.length > 0) {\r\n          showQuestion = configData.questionFilters.some(f => activeTokens.has(f.toLowerCase()));\r\n        }\r\n        \r\n        \/\/ 3. Evaluate Barrier Element (Section_Heading_Wait)\r\n        if (configData.type === 'Section_Heading_Wait') {\r\n          let isBarrierActive = !checkPreviousRequiredVisible(container);\r\n          \r\n          if (isBarrierActive) {\r\n            blockAllSubsequent = true;\r\n            container.style.display = 'block';\r\n            container.innerHTML = getWaitSpinnerHtml();\r\n            container.classList.add('is-blocking-barrier'); \/\/ Added flag for validation\r\n            return; \r\n          } else {\r\n            container.classList.remove('is-blocking-barrier');\r\n            container.innerHTML = getHeadingHtml(title, configData.desc);\r\n          }\r\n        }\r\n        \r\n        \/\/ 4. Hide if it doesn't match Question Filters\r\n        if (!showQuestion) {\r\n          container.style.display = 'none';\r\n          deselectInputsInContainer(container);\r\n          return;\r\n        }\r\n        container.style.display = 'block';\r\n        \r\n        \/\/ 5. Option Visibility Logic\r\n        if (container._managedOptions && container._optParent) {\r\n          container._managedOptions.forEach(optNode => {\r\n            let isMatch = true;\r\n            if (optNode.dataset.filters) {\r\n              const rules = optNode.dataset.filters.split(',').map(r => r.trim().toLowerCase()).filter(r => r.length > 0);\r\n              isMatch = rules.some(r => activeTokens.has(r));\r\n            }\r\n            if (isMatch) {\r\n              if (!optNode.parentNode) container._optParent.appendChild(optNode);\r\n            } else {\r\n              if (optNode.parentNode) optNode.parentNode.removeChild(optNode);\r\n              if (optNode.tagName === 'OPTION') {\r\n                if (optNode.selected) { optNode.selected = false; container._optParent.value = \"\"; }\r\n              } else {\r\n                const input = optNode.querySelector('input');\r\n                if (input && input.checked) { input.checked = false; optNode.classList.remove('selected-opt'); }\r\n              }\r\n            }\r\n          });\r\n        }\r\n        \r\n        \/\/ 6. Harvest Active Tokens for Subsequent Questions\r\n        if (configData.isFilterSourceQuestions || configData.isFilterSourceOptions) {\r\n          const activeSelections = container.querySelectorAll('input:checked, option:checked');\r\n          activeSelections.forEach(selection => {\r\n              let val = selection.value || selection.textContent;\r\n              if (val) {\r\n                let cleanVal = val.replace(\/\u0e3f\\s?(-?[\\d,]+)\/g, '').trim().toLowerCase();\r\n                if (cleanVal && cleanVal !== '--select--') activeTokens.add(cleanVal);\r\n              }\r\n          });\r\n        }\r\n      });\r\n      calculateTotalsAndReceipts();\r\n      validateMasterFormState();\r\n    }\r\n    \r\n    function deselectInputsInContainer(container) {\r\n      const inputs = container.querySelectorAll('input:checked, option:checked');\r\n      inputs.forEach(input => {\r\n        if (input.tagName === 'OPTION') { input.selected = false; if (input.parentElement) input.parentElement.value = \"\"; } \r\n        else { input.checked = false; const wrapper = input.closest('.opt-wrapper'); if (wrapper) wrapper.classList.remove('selected-opt'); }\r\n      });\r\n    }\r\n\r\n    \/\/ --- RECEIPT & VALIDATION LOGIC ---\r\n    function calculateTotalsAndReceipts() {\r\n      let total = 0; let subtotal = 0; let discount = 0; \r\n      const mItems = []; const adjItems = [];\r\n      \r\n      const activeInputs = form.querySelectorAll('input[type=\"radio\"]:checked, input[type=\"checkbox\"]:checked, option:checked');\r\n      activeInputs.forEach(el => {\r\n        let text = el.tagName === 'OPTION' ? el.textContent : el.value;\r\n        const match = text.match(\/\u0e3f\\s?(-?[\\d,]+)\/); \r\n        if (match) {\r\n          const val = parseInt(match[1].replace(\/,\/g, ''), 10);\r\n          total += val;\r\n          const cleanDesc = text.replace(\/\\[UNAVAILABLE\\\/FULL\\]\/gi, '').trim();\r\n          \r\n          if (val < 0) { discount += val; adjItems.push({ price: `\u0e3f${val.toLocaleString()}`, desc: cleanDesc, isNegative: true }); } \r\n          else if (text.toLowerCase().includes('deadline')) { adjItems.push({ price: `\u0e3f${val.toLocaleString()}`, desc: cleanDesc, isNegative: false }); } \r\n          else { subtotal += val; mItems.push({ price: `\u0e3f${val.toLocaleString()}`, desc: cleanDesc }); }\r\n        }\r\n      });\r\n      \r\n      window.currentReceiptData = { total: total, subtotal: subtotal, discount: discount, mainItems: mItems, adjustingItems: adjItems };\r\n      \r\n      document.querySelectorAll('.opt-wrapper').forEach(wrapper => {\r\n        const input = wrapper.querySelector('input');\r\n        if (input && input.checked) wrapper.classList.add('selected-opt'); else wrapper.classList.remove('selected-opt');\r\n      });\r\n      \r\n      document.querySelectorAll('.calculated-total-val').forEach(el => {\r\n        const displayTotal = total < 0 ? 0 : total; el.textContent = displayTotal.toLocaleString();\r\n        let parent = el.closest('.total-box-container'); if(!parent) return;\r\n        let note = parent.querySelector('.discount-note');\r\n        if(!note) { note = document.createElement('div'); note.className = 'discount-note'; note.style.fontSize = '0.7em'; note.style.marginTop = '5px'; note.style.color = '#555'; parent.appendChild(note); }\r\n        note.textContent = discount < 0 ? `(Discount applied: \u0e3f${discount.toLocaleString()})` : '';\r\n        let itemizedContainer = parent.querySelector('.itemized-receipt');\r\n        if (itemizedContainer) {\r\n            if (mItems.length > 0 || adjItems.length > 0) {\r\n                itemizedContainer.style.display = 'block';\r\n                let html = '<table style=\"width: 100%; border-collapse: collapse; font-size:0.9em; margin-bottom: 8px;\">';\r\n                mItems.forEach(item => { html += `<tr><td style=\"width: 85px; vertical-align: top; padding-bottom: 6px;\"><strong>${item.price}<\/strong><\/td><td style=\"vertical-align: top; padding-bottom: 6px;\">${item.desc}<\/td><\/tr>`; });\r\n                if (mItems.length > 0 || adjItems.length > 0) html += `<tr><td colspan=\"2\" style=\"text-align: right; border-top: 1px dashed #ccc; padding-top: 6px; padding-bottom: 6px; font-weight: bold; color: #555;\">Subtotal: \u0e3f${subtotal.toLocaleString()}<\/td><\/tr>`;\r\n                adjItems.forEach(item => { const colorStyle = item.isNegative ? 'color: #28a745;' : 'color: #333;'; html += `<tr><td style=\"width: 85px; vertical-align: top; padding-bottom: 6px; ${colorStyle}\"><strong>${item.price}<\/strong><\/td><td style=\"vertical-align: top; padding-bottom: 6px; ${colorStyle}\">${item.desc}<\/td><\/tr>`; });\r\n                html += '<\/table>'; itemizedContainer.innerHTML = html;\r\n            } else { itemizedContainer.style.display = 'none'; itemizedContainer.innerHTML = ''; }\r\n        }\r\n      });\r\n    }\r\n\r\nfunction validateMasterFormState() {\r\n      let isFormValid = true; let isIncomplete = false; \r\n      if (!isConfigLoaded) isFormValid = false;\r\n      \r\n      \/\/ 1. Clean up highlights safely\r\n      document.querySelectorAll('.question-container.skipped-highlight').forEach(c => {\r\n          c.classList.remove('skipped-highlight');\r\n          if (!c.classList.contains('submit-highlight') && !c.classList.contains('clash-error')) {\r\n              c.classList.remove('persistent-highlight');\r\n          }\r\n      });\r\n      document.querySelectorAll('.day-warning-box').forEach(el => el.remove());\r\n      document.querySelectorAll('.question-container.clash-error').forEach(c => c.classList.remove('clash-error', 'persistent-highlight'));\r\n      \r\n      \/\/ Filter out hidden containers (Safely ignores Filter_Source_Questions)\r\n      const visibleContainers = Array.from(document.querySelectorAll('.question-container:not(.ui-element)')).filter(c => c.style.display !== 'none');\r\n      \r\n      \/\/ 2. Bottom-Up Scan to accurately detect skipped questions\r\n      let hasSeenAnsweredQuestionBelow = false;\r\n      \r\n      for (let i = visibleContainers.length - 1; i >= 0; i--) {\r\n          const container = visibleContainers[i];\r\n          const isOptional = container.getAttribute('data-optional') === 'true';\r\n          const answered = isAnswered(container);\r\n          \r\n          \/\/ Determine if there are actual options to select (Handles Filter_Source_Options)\r\n          let interactable = true;\r\n          const inputs = container.querySelectorAll('input, select, textarea');\r\n          if (inputs.length === 0) {\r\n              interactable = false;\r\n          } else if (inputs[0].tagName === 'SELECT') {\r\n              interactable = container.querySelectorAll('option:not([value=\"\"])').length > 0;\r\n          } else if (inputs[0].type === 'radio' || inputs[0].type === 'checkbox') {\r\n              interactable = container.querySelectorAll('input[type=\"radio\"], input[type=\"checkbox\"]').length > 0;\r\n          }\r\n\r\n          if (answered) {\r\n              container.classList.remove('submit-highlight', 'persistent-highlight');\r\n              hasSeenAnsweredQuestionBelow = true;\r\n          } else if (isOptional) {\r\n              container.classList.remove('submit-highlight', 'persistent-highlight');\r\n          } else {\r\n              \/\/ Unanswered and Required\r\n              isIncomplete = true;\r\n              \r\n              if (!interactable) {\r\n                  \/\/ If no options exist yet, don't falsely flag as casually skipped\r\n                  container.classList.remove('submit-highlight', 'persistent-highlight');\r\n              } else if (hasSeenAnsweredQuestionBelow) {\r\n                  \/\/ Valid options exist, and they answered something below it\r\n                  container.classList.add('skipped-highlight', 'persistent-highlight');\r\n              }\r\n          }\r\n      }\r\n\r\n      \/\/ 3. Check for active Section_Heading_Wait barrier\r\n      if (document.querySelector('.is-blocking-barrier')) {\r\n          isIncomplete = true;\r\n      }\r\n      \r\n\t\t\/\/ 4. Validate Clashing Days (Conditional based on new toggle)\r\n      const allowSameDay = formConfig.sameDayBehavior === 'SAME_DAY_ALLOWED';\r\n      \r\n      if (!allowSameDay) {\r\n          const dayMap = {};\r\n          const allDayCheckboxes = document.querySelectorAll('input[type=\"checkbox\"]:checked, input[type=\"radio\"]:checked');\r\n          allDayCheckboxes.forEach(cb => {\r\n            if (cb.dataset.days) {\r\n              const daysArr = cb.dataset.days.split(','); const container = cb.closest('.question-container');\r\n              daysArr.forEach(day => { if (!dayMap[day]) dayMap[day] = []; dayMap[day].push({ cb: cb, container: container, text: cb.value }); });\r\n            }\r\n          });\r\n          \r\n          const containersWithErrors = new Map();\r\n          for (const [day, items] of Object.entries(dayMap)) {\r\n            if (items.length > 1) { \r\n              isFormValid = false;\r\n              items.forEach(item => {\r\n                if (!containersWithErrors.has(item.container)) containersWithErrors.set(item.container, new Set());\r\n                items.forEach(clashingItem => containersWithErrors.get(item.container).add(clashingItem.text));\r\n              });\r\n            }\r\n          }\r\n          \r\n          containersWithErrors.forEach((clashingTexts, container) => {\r\n            container.classList.add('clash-error', 'persistent-highlight');\r\n            const wDiv = document.createElement('div'); wDiv.className = 'day-warning-box'; wDiv.style.color = '#721c24'; wDiv.style.backgroundColor = '#f8d7da'; wDiv.style.border = '2px solid #f5c6cb'; wDiv.style.padding = '12px'; wDiv.style.marginTop = '15px'; wDiv.style.borderRadius = '5px'; wDiv.style.fontWeight = 'bold';\r\n            let errorHtml = `ERROR: SAME DAYS! PLEASE CHOOSE AGAIN.<br><div style=\"margin-top: 8px; font-weight: normal;\">`;\r\n            clashingTexts.forEach(optText => { errorHtml += `<div style=\"padding: 3px 0;\">\u2022 ${optText.replace(\/\\[UNAVAILABLE\\\/FULL\\]\/gi, '').trim()}<\/div>`; });\r\n            errorHtml += `<\/div>`; wDiv.innerHTML = errorHtml; container.querySelector('.q-input-container').appendChild(wDiv);\r\n          });\r\n      }\r\n      \r\n      \/\/ 5. Final State Evaluation\r\n      if (document.querySelectorAll('.is-invalid').length > 0) isFormValid = false;\r\n      if (!isFormValid || isIncomplete) { \r\n        submitBtn.classList.add('looks-disabled'); \r\n        if (!isFormValid && isConfigLoaded) { warningSpan.style.display = 'none'; globalError.style.display = 'block'; } \r\n        else { warningSpan.style.display = 'inline'; globalError.style.display = 'none'; }\r\n      } else { \r\n        submitBtn.classList.remove('looks-disabled'); warningSpan.style.display = 'none'; globalError.style.display = 'none'; \r\n      }\r\n    }\r\n\r\n    submitBtn.addEventListener('click', (e) => {\r\n        if (submitBtn.classList.contains('looks-disabled')) {\r\n            e.preventDefault(); \r\n            const visibleContainers = Array.from(document.querySelectorAll('.question-container:not(.ui-element)')).filter(c => c.style.display !== 'none');\r\n            let firstIssue = null;\r\n            \r\n            for (const container of visibleContainers) {\r\n                const isOptional = container.getAttribute('data-optional') === 'true';\r\n                if (!isAnswered(container) && !isOptional) {\r\n                    container.classList.add('submit-highlight', 'persistent-highlight');\r\n                    if (!firstIssue) firstIssue = container;\r\n                }\r\n            }\r\n            const explicitError = document.querySelector('.is-invalid, .clash-error');\r\n            if (explicitError && !firstIssue) firstIssue = explicitError.closest('.question-container') || explicitError;\r\n            if (firstIssue) (firstIssue.closest('.question-container') || firstIssue).scrollIntoView({ behavior: 'smooth', block: 'center' });\r\n        }\r\n    });\r\n\r\n\/\/ --- FORM SUBMISSION ---\r\n    form.addEventListener('submit', async (e) => {\r\n      e.preventDefault();\r\n      if (submitBtn.classList.contains('looks-disabled')) { submitBtn.click(); return; }\r\n      \r\n      submitBtn.classList.add('looks-disabled');\r\n      document.getElementById('status-message').style.color = '#494BB3';\r\n      document.getElementById('status-message').innerHTML = '<div class=\"spinner\"><\/div> Please wait (confirming availability)...';\r\n      \r\n      \/\/ 1. Establish which containers are actually visible\r\n      const visibleContainers = Array.from(document.querySelectorAll('.question-container:not(.ui-element)')).filter(c => c.style.display !== 'none');\r\n      const visibleTitles = new Set(visibleContainers.map(c => c.getAttribute('data-qtitle')));\r\n\r\n      const formData = new FormData(form); const answers = {};\r\n      for (const [key, rawValue] of formData.entries()) {\r\n        \/\/ 2. Ignore any data from questions that are currently hidden\r\n        if (!visibleTitles.has(key)) continue; \r\n        \r\n        if (rawValue instanceof File) {\r\n            \/\/ Fix for Attach_Image: Log a placeholder so the backend knows this was answered\r\n            if (rawValue.name) answers[key] = \"FILE_ATTACHED\"; \r\n            continue;\r\n        }\r\n\r\n        let value = rawValue;\r\n        if (typeof value === 'string' && value.startsWith('0')) value = \"'\" + value;\r\n        if (answers[key]) { if (!Array.isArray(answers[key])) answers[key] = [answers[key]]; answers[key].push(value); } \r\n        else { answers[key] = value; }\r\n      }\r\n      \r\n      try {\r\n        const checkResult = await fetchWithRetry(`${SCRIPT_URL}?action=getConfig`);\r\n        if (checkResult.status === 'success') {\r\n          const latestConfig = checkResult.config; let hasUnavailable = false; let badQuestion = \"\";\r\n          for (const [qTitle, valArray] of Object.entries(answers)) {\r\n            const configQ = latestConfig.questions[qTitle]; if (!configQ) continue;\r\n            if (!['Select_Dropdown', 'Select_Radio', 'Multiple_Selection', 'Checkbox'].includes(configQ.type)) continue; \r\n            const latestOptions = configQ.options; if (!latestOptions || latestOptions.length === 0) continue;\r\n            const vals = Array.isArray(valArray) ? valArray : [valArray];\r\n            \r\n            for (let v of vals) {\r\n              if (v === \"FILE_ATTACHED\") continue;\r\n              if (v.startsWith(\"'0\")) v = v.substring(1);\r\n              const cleanSubmitted = v.replace(\/\\[UNAVAILABLE\\\/FULL\\]\/gi, '').trim();\r\n              const matchedOpt = latestOptions.find(o => o.text.replace(\/\\[UNAVAILABLE\\\/FULL\\]\/gi, '').trim() === cleanSubmitted);\r\n              if (!matchedOpt || matchedOpt.isDisabled || \/\\[UNAVAILABLE\\\/FULL\\]\/i.test(matchedOpt.text)) { hasUnavailable = true; badQuestion = qTitle; break; }\r\n            }\r\n            if (hasUnavailable) break;\r\n          }\r\n          if (hasUnavailable) {\r\n            document.getElementById('status-message').innerHTML = ''; \r\n            const container = document.querySelector(`.question-container[data-qtitle=\"${CSS.escape(badQuestion)}\"]`);\r\n            if (container) { container.classList.add('persistent-highlight'); container.scrollIntoView({ behavior: 'smooth', block: 'center' }); }\r\n            customAlertMsg.innerHTML = `One of your selections is no longer available for:<br><br><b>\"${badQuestion}\"<\/b><br><br>Please choose again.`; customAlert.style.display = 'flex';\r\n            \r\n            formConfig = latestConfig;\r\n            if (container) {\r\n              const warningDiv = document.createElement('div'); warningDiv.style.color = 'red'; warningDiv.style.fontWeight = 'bold'; warningDiv.style.marginTop = '8px'; warningDiv.textContent = 'Please choose again.';\r\n              container.querySelector('.q-input-container').appendChild(warningDiv);\r\n            }\r\n            globalError.innerHTML = `ERROR: One of your selections is no longer available for \"${badQuestion}\".`; globalError.style.display = 'block'; applyDynamicFiltering();\r\n            return; \r\n          }\r\n        }\r\n      } catch(err) { console.warn(\"Pre-check failed, continuing submission attempt.\", err); }\r\n      \r\n      const payload = { answers: answers };\r\n      let fileAttached = false;\r\n      const fileInput = document.querySelector('input[type=\"file\"]');\r\n      \r\n      \/\/ 3. Only attach file data if its container is visible\r\n      if (fileInput && fileInput.files.length > 0) {\r\n        const fileContainer = fileInput.closest('.question-container');\r\n        if (fileContainer && fileContainer.style.display !== 'none') {\r\n            fileAttached = true;\r\n            const file = fileInput.files[0]; payload.fileName = file.name; payload.fileType = file.type;\r\n            payload.fileData = await new Promise((resolve, reject) => {\r\n              const reader = new FileReader(); reader.onload = () => resolve(reader.result); reader.onerror = reject; reader.readAsDataURL(file);\r\n            });\r\n        }\r\n      }\r\n      \r\n      let isPayloadValid = true; let missingFields = [];\r\n      for (const container of visibleContainers) {\r\n        const qTitle = container.getAttribute('data-qtitle');\r\n        if (qTitle) { \r\n          const configData = formConfig.questions[qTitle];\r\n          if (!configData.isOptional) {\r\n              if (configData.type === 'Attach_Image') { if (!fileAttached) { isPayloadValid = false; missingFields.push(qTitle); } } \r\n              else {\r\n                 const answerData = payload.answers[qTitle];\r\n                 if (answerData === undefined || answerData === null || answerData === '') { isPayloadValid = false; missingFields.push(qTitle); } \r\n                 else if (Array.isArray(answerData) && answerData.length === 0) { isPayloadValid = false; missingFields.push(qTitle); }\r\n              }\r\n          }\r\n        }\r\n      }\r\n      if (!isPayloadValid) {\r\n        document.getElementById('status-message').style.color = 'red';\r\n        document.getElementById('status-message').innerHTML = `<strong>ERROR: Internal verification failed.<\/strong> Missing data for: <i>${missingFields.join(', ')}<\/i>. Please review your answers.`;\r\n        submitBtn.classList.remove('looks-disabled'); return; \r\n      }\r\n      \r\n      try {\r\n        const submitResult = await fetchWithRetry(SCRIPT_URL, { method: 'POST', mode: 'cors', body: JSON.stringify(payload) });\r\n        if (submitResult.status === 'success') {\r\n          form.style.display = 'none';\r\n          const successContainer = document.getElementById('success-container');\r\n          const receiptTextDiv = document.getElementById('receipt-text');\r\n          const saveReceiptBtn = document.getElementById('save-receipt-btn');\r\n          const newFormBtn = document.getElementById('new-form-btn');\r\n          const successMsgP = document.getElementById('success-msg-desc');\r\n          \r\n          const hasTotalField = Object.values(formConfig.questions).some(q => q.type === 'Display_Total');\r\n          const behavior = (formConfig.submissionBehavior || 'RECEIPT').toString().trim().toUpperCase();\r\n\r\n          if (behavior === 'CONFIRMATION') {\r\n             if (successMsgP) successMsgP.textContent = \"Your form has been successfully submitted.\";\r\n             receiptTextDiv.style.display = 'none';\r\n             saveReceiptBtn.style.display = 'none';\r\n             newFormBtn.style.display = 'block';\r\n             \r\n             successContainer.style.display = 'block'; \r\n             \/\/ Scroll smoothly directly to the success message element\r\n             successContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });\r\n\r\n          } else {\r\n             if (successMsgP) successMsgP.textContent = \"Your form has been received. (Click below to save a copy for your own records.)\";\r\n             receiptTextDiv.style.display = 'block';\r\n             saveReceiptBtn.style.display = 'block';\r\n\r\n             let rHTML = `<div style=\"text-align:center; font-size:1.3em; font-weight:bold; margin-bottom:10px; color:#494BB3;\">${formConfig.title}<\/div>`;\r\n             rHTML += `<div style=\"text-align:center; margin-bottom:20px; color:#555;\">======== SUBMISSION RECEIPT ========<\/div>`;\r\n             rHTML += `<div>Date: ${getFormattedDate()}<\/div><br><br>`;\r\n             \r\n             for (const [key, value] of Object.entries(payload.answers)) {\r\n               if (value === \"FILE_ATTACHED\") continue; \/\/ Keep the placeholder hidden from the frontend receipt\r\n               let cleanVal = Array.isArray(value) ? value.map(v => v.startsWith(\"'0\") ? v.substring(1) : v) : (value.startsWith(\"'0\") ? value.substring(1) : value);\r\n               let valStr = Array.isArray(cleanVal) ? cleanVal.join('<br>') : cleanVal;\r\n               rHTML += `<div style=\"margin-bottom: 12px; color:#555;\">${key}:<br><strong style=\"color:#000; font-size:1.1em;\">${valStr}<\/strong><\/div>`;\r\n             }\r\n             \r\n             if (hasTotalField && window.currentReceiptData && (window.currentReceiptData.mainItems.length > 0 || window.currentReceiptData.adjustingItems.length > 0)) {\r\n                 rHTML += `<div style=\"margin-top: 15px; border-top:1px solid #ccc; padding-top:15px; color:#555;\"><div style=\"margin-bottom: 10px;\"><strong>Itemized Summary:<\/strong><\/div><table style=\"width:100%; font-size: 0.95em; margin-bottom: 15px;\">`;\r\n                 window.currentReceiptData.mainItems.forEach(item => { rHTML += `<tr><td style=\"width:75px; vertical-align:top;\"><strong>${item.price}<\/strong><\/td><td style=\"vertical-align:top;\">${item.desc}<\/td><\/tr>`; });\r\n                 if (window.currentReceiptData.mainItems.length > 0 || window.currentReceiptData.adjustingItems.length > 0) rHTML += `<tr><td colspan=\"2\" style=\"text-align:right; border-top:1px dashed #aaa; padding-top:6px; padding-bottom:6px;\"><strong>Subtotal: \u0e3f${window.currentReceiptData.subtotal.toLocaleString()}<\/strong><\/td><\/tr>`;\r\n                 window.currentReceiptData.adjustingItems.forEach(item => { const colorStyle = item.isNegative ? 'color:#28a745;' : 'color:#333;'; rHTML += `<tr><td style=\"width:75px; vertical-align:top; ${colorStyle}\"><strong>${item.price}<\/strong><\/td><td style=\"vertical-align:top; ${colorStyle}\">${item.desc}<\/td><\/tr>`; });\r\n                 rHTML += `<\/table>TOTAL CALCULATED:<br><strong style=\"color:#000; font-size:1.3em;\">\u0e3f${window.currentReceiptData.total.toLocaleString()}<\/strong>`;\r\n                 if (window.currentReceiptData.discount < 0) { rHTML += `<div style=\"font-size:0.85em; color:#555;\">(Discount applied: \u0e3f${window.currentReceiptData.discount.toLocaleString()})<\/div>`; }\r\n                 rHTML += `<\/div>`;\r\n             }\r\n             \r\n             receiptTextDiv.innerHTML = rHTML; \r\n             successContainer.style.display = 'block'; \r\n             \/\/ Scroll smoothly directly to the success message element\r\n             successContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });\r\n             \r\n             saveReceiptBtn.onclick = () => {\r\n               const linesToDraw = []; \r\n               linesToDraw.push({text: formConfig.title, type: 'title'}); \r\n               linesToDraw.push({text: \"======== SUBMISSION RECEIPT ========\", type: 'header'}); \r\n               linesToDraw.push({text: `Date: ${getFormattedDate()}`, type: 'normal'}); \r\n               linesToDraw.push({text: \"\", type: 'normal'});\r\n               \r\n               for (const [key, value] of Object.entries(payload.answers)) {\r\n                 if (value === \"FILE_ATTACHED\") continue;\r\n                 linesToDraw.push({text: key + \":\", type: 'normal'});\r\n                 let cleanVal = Array.isArray(value) ? value.map(v => v.startsWith(\"'0\") ? v.substring(1) : v) : (value.startsWith(\"'0\") ? value.substring(1) : value);\r\n                 const valArr = Array.isArray(cleanVal) ? cleanVal : [cleanVal];\r\n                 valArr.forEach(v => { const vLines = v.split('\\n'); vLines.forEach(vl => linesToDraw.push({text: vl, type: 'bold'})); });\r\n                 linesToDraw.push({text: \"\", type: 'normal'});\r\n               }\r\n               \r\n               if (hasTotalField && window.currentReceiptData && (window.currentReceiptData.mainItems.length > 0 || window.currentReceiptData.adjustingItems.length > 0)) {\r\n                   linesToDraw.push({text: \"======== ITEMIZED SUMMARY ========\", type: 'header'}); \r\n                   linesToDraw.push({text: \"\", type: 'normal'});\r\n                   window.currentReceiptData.mainItems.forEach(item => { linesToDraw.push({text: `${item.price}  ${item.desc}`, type: 'normal'}); });\r\n                   if (window.currentReceiptData.mainItems.length > 0 || window.currentReceiptData.adjustingItems.length > 0) linesToDraw.push({text: `Subtotal: \u0e3f${window.currentReceiptData.subtotal.toLocaleString()}`, type: 'bold'});\r\n                   window.currentReceiptData.adjustingItems.forEach(item => { linesToDraw.push({text: `${item.price}  ${item.desc}`, type: 'normal'}); });\r\n                   linesToDraw.push({text: \"\", type: 'normal'}); \r\n                   linesToDraw.push({text: `TOTAL CALCULATED: \u0e3f${window.currentReceiptData.total.toLocaleString()}`, type: 'title'});\r\n                   if (window.currentReceiptData.discount < 0) linesToDraw.push({text: `(Discount applied: \u0e3f${window.currentReceiptData.discount.toLocaleString()})`, type: 'normal'});\r\n               }\r\n               \r\n               let expectedCanvasHeight = 50;\r\n               linesToDraw.forEach(item => {\r\n                 const maxChars = item.type === 'title' ? 45 : 70;\r\n                 if (item.text.length > maxChars) {\r\n                   const words = item.text.split(' '); let tempLine = '';\r\n                   words.forEach(word => { if ((tempLine + word).length < maxChars) { tempLine += word + ' '; } else { expectedCanvasHeight += 24; tempLine = word + ' '; } });\r\n                   expectedCanvasHeight += 24;\r\n                 } else { expectedCanvasHeight += 24; }\r\n               });\r\n               expectedCanvasHeight += 40; \r\n               const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d');\r\n               canvas.width = 650; canvas.height = expectedCanvasHeight; ctx.fillStyle = \"#ffffff\"; ctx.fillRect(0, 0, canvas.width, canvas.height);\r\n               \r\n               let y = 50;\r\n               linesToDraw.forEach(item => {\r\n                 if (item.type === 'title') { ctx.font = \"bold 20px sans-serif\"; ctx.fillStyle = \"#494BB3\"; }\r\n                 else if (item.type === 'header') { ctx.font = \"bold 16px monospace\"; ctx.fillStyle = \"#555\"; }\r\n                 else if (item.type === 'bold') { ctx.font = \"bold 16px sans-serif\"; ctx.fillStyle = \"#000\"; }\r\n                 else { ctx.font = \"15px sans-serif\"; ctx.fillStyle = \"#555\"; }\r\n                 \r\n                 const maxChars = item.type === 'title' ? 45 : 70; \r\n                 if (item.text.length > maxChars) {\r\n                   const words = item.text.split(' '); let tempLine = '';\r\n                   words.forEach(word => { if ((tempLine + word).length < maxChars) { tempLine += word + ' '; } else { ctx.fillText(tempLine, 25, y); y += 24; tempLine = word + ' '; } });\r\n                   ctx.fillText(tempLine, 25, y);\r\n                 } else { ctx.fillText(item.text, 25, y); }\r\n                 y += 24;\r\n               });\r\n               const link = document.createElement(\"a\"); link.download = \"Registration_Receipt.png\"; link.href = canvas.toDataURL(\"image\/png\"); link.click();\r\n               \r\n               newFormBtn.style.display = 'block';\r\n             };\r\n          }\r\n        } else if (submitResult.status === 'unavailable') {\r\n          const badQuestion = submitResult.question; document.getElementById('status-message').innerHTML = ''; \r\n          const container = document.querySelector(`.question-container[data-qtitle=\"${CSS.escape(badQuestion)}\"]`);\r\n          if (container) { container.classList.add('persistent-highlight'); container.scrollIntoView({ behavior: 'smooth', block: 'center' }); }\r\n          \r\n          customAlertMsg.innerHTML = `One of your selections is no longer available for:<br><br><b>\"${badQuestion}\"<\/b><br><br>Please choose again.`; customAlert.style.display = 'flex';\r\n          const updatedResult = await fetchWithRetry(`${SCRIPT_URL}?action=getConfig`);\r\n          if (updatedResult.status === 'success') { formConfig = updatedResult.config; applyDynamicFiltering(); globalError.innerHTML = `ERROR: One of your selections is no longer available for \"${badQuestion}\".`; globalError.style.display = 'block'; }\r\n        } else { throw new Error(submitResult.message); }\r\n      } catch (err) {\r\n        document.getElementById('status-message').style.color = 'red';\r\n        document.getElementById('status-message').textContent = `Error: Cannot reach server. Please try submitting again.`; submitBtn.classList.remove('looks-disabled'); \r\n      }\r\n    });\r\n  });\r\n<\/script>\r\n\n","protected":false},"excerpt":{"rendered":"<p>TCS Swimming [October Camp 2026] &nbsp; &nbsp; Line OpenChat &#8220;TCS SWIMMING (OCTOBER CAMP 2026)&#8221; Scan the QR Code or tap the link below to join. \u0e2a\u0e41\u0e01\u0e19 QR Code \u0e2b\u0e23\u0e37\u0e2d\u0e01\u0e14\u0e25\u0e34\u0e07\u0e01\u0e4c\u0e14\u0e49\u0e32\u0e19\u0e25\u0e48\u0e32\u0e07\u0e40\u0e1e\u0e37\u0e48\u0e2d\u0e40\u0e02\u0e49\u0e32\u0e23\u0e48\u0e27\u0e21\u0e01\u0e25\u0e38\u0e48\u0e21 https:\/\/line.me\/ti\/g2\/KvPl93JNxDR3pPitKvd__VCOvm1nxZCLK6yi3Q?utm_source=invitation&amp;utm_medium=link_copy&amp;utm_campaign=default &nbsp; APPLICATION FORM &nbsp;<\/p>\n","protected":false},"author":6,"featured_media":2905,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[5],"tags":[],"class_list":["post-2893","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-after-school-camps-teams"],"acf":[],"_links":{"self":[{"href":"https:\/\/thaichristian.ac.th\/th\/wp-json\/wp\/v2\/posts\/2893","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/thaichristian.ac.th\/th\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/thaichristian.ac.th\/th\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/thaichristian.ac.th\/th\/wp-json\/wp\/v2\/users\/6"}],"replies":[{"embeddable":true,"href":"https:\/\/thaichristian.ac.th\/th\/wp-json\/wp\/v2\/comments?post=2893"}],"version-history":[{"count":9,"href":"https:\/\/thaichristian.ac.th\/th\/wp-json\/wp\/v2\/posts\/2893\/revisions"}],"predecessor-version":[{"id":2914,"href":"https:\/\/thaichristian.ac.th\/th\/wp-json\/wp\/v2\/posts\/2893\/revisions\/2914"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/thaichristian.ac.th\/th\/wp-json\/wp\/v2\/media\/2905"}],"wp:attachment":[{"href":"https:\/\/thaichristian.ac.th\/th\/wp-json\/wp\/v2\/media?parent=2893"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/thaichristian.ac.th\/th\/wp-json\/wp\/v2\/categories?post=2893"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/thaichristian.ac.th\/th\/wp-json\/wp\/v2\/tags?post=2893"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}