{"id":19086,"date":"2026-08-12T13:04:29","date_gmt":"2026-08-12T06:04:29","guid":{"rendered":"https:\/\/uat.vietnamdetox.com\/tong-quan-ve-khoa-nghi-duong\/"},"modified":"2026-09-26T09:25:26","modified_gmt":"2026-09-26T02:25:26","slug":"tong-quan-ve-khoa-nghi-duong","status":"publish","type":"page","link":"https:\/\/uat.vietnamdetox.com\/vi\/tong-quan-ve-khoa-nghi-duong\/","title":{"rendered":"Retreats"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"19086\" class=\"elementor elementor-19086 elementor-19050\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-0c6112f e-flex e-con-boxed e-con e-parent\" data-id=\"0c6112f\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-55437c4 elementor-widget elementor-widget-html\" data-id=\"55437c4\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!--\n  Elementor setup\n  1. Paste this entire file into ONE Elementor HTML widget.\n  2. Paste elementor-retreat-overview-hero.css into that widget's\n     Advanced > Custom CSS field.\n  3. Keep the parent container full width with zero padding and gap.\n\n  The hero images, retreat links and retreat names are loaded from the REST API.\n  When the dynamic retreat-list widget is on the same page, both widgets share\n  one page-level request promise and make only one normal API request together.\n-->\n\n<div class=\"vroh-root\" data-vroh-hero=\"\" data-api-url=\"\/wp-json\/retreats\/v1\/all\">\n\n<header class=\"vroh\" id=\"top\" data-vroh-hero-section=\"\" aria-label=\"Vietnam Detox retreat overview\">\n  <div class=\"vroh__background\" data-vroh-background=\"\" aria-hidden=\"true\">\n    <div class=\"vroh__media-status\" data-vroh-media-status=\"\">\u0110ang t\u1ea3i h\u00ecnh \u1ea3nh kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng...<\/div>\n  <\/div>\n\n  <div class=\"vroh__overlay\" data-vroh-overlay=\"\" aria-hidden=\"true\"><\/div>\n\n  <div class=\"vroh__inner\">\n    <div class=\"vroh__left\">\n      <div class=\"vroh__panel\" data-vroh-panel=\"\">\n        <button class=\"vroh__panel-toggle\" data-vroh-panel-toggle=\"\" type=\"button\" aria-label=\"Hide introduction\" aria-expanded=\"true\">\n          <span class=\"vroh__panel-toggle-label\" data-vroh-toggle-label=\"\">\u1ea8n h\u1ed9p<\/span>\n<span class=\"vroh__panel-toggle-icon\" data-vroh-toggle-icon=\"\" aria-hidden=\"true\">\u00d7<\/span>\n        <\/button>\n\n        <div class=\"vroh__panel-surface\" data-vroh-panel-surface=\"\">\n          <div class=\"vroh__panel-body\" data-vroh-panel-body=\"\">\n            <div class=\"vroh__copy\">\n              <p class=\"vroh__kicker\">Vietnam Detox \u00b7 C\u00e1c kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng nh\u1ecbn \u0103n c\u00f3 h\u01b0\u1edbng d\u1eabn t\u1ea1i Vi\u1ec7t Nam<\/p>\n\n              <h1 class=\"vroh__title\">\n C\u00e1c kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng d\u00e0nh cho nh\u1eefng ng\u01b0\u1eddi \u0111ang <span>g\u00e1nh v\u00e1c qu\u00e1 nhi\u1ec1u.<\/span>\n              <\/h1>\n\n              <p class=\"vroh__location\" data-vroh-location=\"\">\n Nh\u1ecbn \u0103n theo ph\u01b0\u01a1ng ph\u00e1p Buchinger c\u00f3 h\u01b0\u1edbng d\u1eabn t\u1ea1i Vi\u1ec7t Nam \u2014 h\u00e3y so s\u00e1nh c\u00e1c b\u1ed1i c\u1ea3nh hi\u1ec7n t\u1ea1i c\u1ee7a ch\u00fang t\u00f4i, sau \u0111\u00f3 ch\u1ecdn l\u1ef1a \u0111i\u1ec1u ph\u00f9 h\u1ee3p v\u1edbi t\u00ecnh tr\u1ea1ng hi\u1ec7n t\u1ea1i c\u1ee7a b\u1ea1n.\n              <\/p>\n\n              <p class=\"vroh__lede\">C\u00f9ng m\u1ed9t ph\u01b0\u01a1ng ph\u00e1p \u0111\u00e3 \u0111\u01b0\u1ee3c ch\u1ee9ng minh. Nh\u1eefng c\u00e1ch kh\u00e1c nhau \u0111\u1ec3 cu\u1ed1i c\u00f9ng c\u00f3 th\u1ec3 d\u1eebng l\u1ea1i. <\/p>\n\n              <p class=\"vroh__support\">\n D\u00e0nh cho nh\u1eefng ai \u0111ang g\u00e1nh v\u00e1c tr\u00e1ch nhi\u1ec7m trong c\u00f4ng vi\u1ec7c, \u0111\u1ed9i ng\u0169 hay gia \u0111\u00ecnh, v\u00e0 \u0111ang c\u1ea1n ki\u1ec7t n\u0103ng l\u01b0\u1ee3ng d\u00f9 v\u1eabn duy tr\u00ec v\u1ebb ngo\u00e0i \u0111i\u1ec1m t\u0129nh, v\u1eefng v\u00e0ng. Ch\u01b0\u01a1ng tr\u00ecnh k\u00e9o d\u00e0i 5 ng\u00e0y c\u00f3 ng\u01b0\u1eddi h\u01b0\u1edbng d\u1eabn, di\u1ec5n ra trong kh\u00f4ng gian ph\u00f9 h\u1ee3p nh\u1ea5t v\u1edbi b\u1ea1n. \n              <\/p>\n\n              <div class=\"vroh__actions\">\n                <a href=\"#retreats\" class=\"vroh__button vroh__button--primary\">So s\u00e1nh c\u00e1c Kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng<\/a>\n<a href=\"\/vi\/cau-hoi-thuong-gap\/\" class=\"vroh__button vroh__button--outline\">\u0110\u1ecdc C\u00e2u h\u1ecfi th\u01b0\u1eddng g\u1eb7p<\/a>\n              <\/div>\n\n            <\/div>\n\n            <div class=\"vroh__slider-ui\" data-vroh-slider-ui=\"\" aria-label=\"Hero image controls\" hidden=\"\">\n              <div class=\"vroh__slider-progress\" aria-hidden=\"true\">\n                <span class=\"vroh__slider-progress-fill\" data-vroh-progress=\"\"><\/span>\n              <\/div>\n              <div class=\"vroh__slider-dots\" data-vroh-dots=\"\" role=\"tablist\" aria-label=\"Hero slides\"><\/div>\n            <\/div>\n          <\/div>\n        <\/div>\n      <\/div>\n    <\/div>\n\n    <div class=\"vroh__right\" aria-hidden=\"true\"><\/div>\n  <\/div>\n<\/header>\n\n<div class=\"vroh-shortcuts\">\n  <nav class=\"vroh__retreat-links\" data-vroh-retreat-links=\"\" aria-label=\"Individual retreat pages\">\n    <span class=\"vroh__retreat-status\" data-vroh-retreat-status=\"\">\u0110ang t\u1ea3i c\u00e1c kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng...<\/span>\n  <\/nav>\n<\/div>\n\n<\/div>\n\n<script>\n(function() {\n  'use strict';\n\n  function getSharedRetreatApi() {\n    var shared = window.VietnamDetoxRetreatAPI = window.VietnamDetoxRetreatAPI || {};\n    shared.entries = shared.entries || {};\n    shared.version = shared.version || 0;\n\n    if (typeof shared.key !== 'function') {\n      shared.key = function(url) {\n        return new URL(url, window.location.href).href;\n      };\n    }\n\n    if (typeof shared.load !== 'function') {\n      shared.load = function(url, force) {\n        var key = shared.key(url);\n        if (!force && shared.entries[key]) return shared.entries[key].promise;\n\n        var controller = typeof AbortController === 'function' ? new AbortController() : null;\n        var timedOut = false;\n        var timeoutId = window.setTimeout(function() {\n          timedOut = true;\n          if (controller) controller.abort();\n        }, 15000);\n\n        var entry = {};\n        entry.promise = fetch(key, {\n          method: 'GET',\n          credentials: 'same-origin',\n          headers: { Accept: 'application\/json' },\n          signal: controller ? controller.signal : undefined\n        }).then(function(response) {\n          if (!response.ok) throw new Error('API request failed with status ' + response.status);\n          return response.json();\n        }).then(function(payload) {\n          var items = Array.isArray(payload)\n            ? payload\n            : Array.isArray(payload.retreats)\n              ? payload.retreats\n              : Array.isArray(payload.data)\n                ? payload.data\n                : null;\n\n          if (!items) throw new Error('The API returned an unexpected response shape.');\n\n          var result = { key: key, items: items, version: ++shared.version };\n          entry.result = result;\n          window.dispatchEvent(new CustomEvent('vietnamdetox:retreats-loaded', { detail: result }));\n          return result;\n        }).catch(function(error) {\n          if (timedOut) error.vietnamDetoxTimedOut = true;\n          throw error;\n        }).finally(function() {\n          window.clearTimeout(timeoutId);\n        });\n\n        shared.entries[key] = entry;\n        return entry.promise;\n      };\n    }\n\n    return shared;\n  }\n\n  function cleanText(value) {\n    if (value === null || typeof value === 'undefined') return '';\n    var parser = new DOMParser();\n    var parsed = parser.parseFromString(String(value), 'text\/html');\n    return (parsed.body.textContent || '').replace(\/\\s+\/g, ' ').trim();\n  }\n\n  function safeUrl(value, allowImageData) {\n    if (!value) return '';\n    try {\n      var parsed = new URL(String(value), window.location.href);\n      var allowed = parsed.protocol === 'http:' || parsed.protocol === 'https:';\n      if (allowImageData && parsed.protocol === 'data:') allowed = \/^data:image\\\/\/i.test(String(value));\n      return allowed ? parsed.href : '';\n    } catch (error) {\n      return '';\n    }\n  }\n\n  function createElement(tag, className, text) {\n    var element = document.createElement(tag);\n    if (className) element.className = className;\n    if (typeof text === 'string') element.textContent = text;\n    return element;\n  }\n\n  var roots = document.querySelectorAll('[data-vroh-hero]');\n\n  roots.forEach(function(root) {\n    if (root.dataset.vrohReady === 'true') return;\n    root.dataset.vrohReady = 'true';\n\n    var heroSection = root.querySelector('[data-vroh-hero-section]');\n    var panel = root.querySelector('[data-vroh-panel]');\n    var panelSurface = root.querySelector('[data-vroh-panel-surface]');\n    var panelBody = root.querySelector('[data-vroh-panel-body]');\n    var panelToggle = root.querySelector('[data-vroh-panel-toggle]');\n    var toggleLabel = root.querySelector('[data-vroh-toggle-label]');\n    var toggleIcon = root.querySelector('[data-vroh-toggle-icon]');\n    var background = root.querySelector('[data-vroh-background]');\n    var overlay = root.querySelector('[data-vroh-overlay]');\n    var locationCopy = root.querySelector('[data-vroh-location]');\n    var mediaStatus = root.querySelector('[data-vroh-media-status]');\n    var retreatLinks = root.querySelector('[data-vroh-retreat-links]');\n    var retreatStatus = root.querySelector('[data-vroh-retreat-status]');\n    var sliderUi = root.querySelector('[data-vroh-slider-ui]');\n    var dotsContainer = root.querySelector('[data-vroh-dots]');\n    var progress = root.querySelector('[data-vroh-progress]');\n    var endpoint = new URL(root.getAttribute('data-api-url') || '\/wp-json\/retreats\/v1\/all', window.location.href);\n    var language = String(endpoint.searchParams.get('lang') || document.documentElement.lang || 'en').toLowerCase().split(\/[-_]\/)[0];\n    if (language === 'vn') language = 'vi';\n    if (['en', 'vi', 'de'].indexOf(language) < 0) language = 'en';\n    endpoint.searchParams.set('lang', language);\n    var apiUrl = endpoint.href;\n    var translations = {\n      en: {\n        regionLabel: 'Vietnam Detox retreat overview', linksLabel: 'Individual retreat pages',\n        controlsLabel: 'Hero image controls', slidesLabel: 'Hero slides',\n        loadingImages: 'Loading retreat images\u2026', loadingRetreats: 'Loading retreats\u2026',\n        show: 'Show info', hide: 'Hide box', showIntroduction: 'Show introduction', hideIntroduction: 'Hide introduction',\n        noImages: 'No retreat images are currently available.', newRetreats: 'New retreats will be announced soon.',\n        retreatName: 'Vietnam Detox Retreat', showImage: 'Show image for ',\n        timeout: 'Retreat images took too long to load.', unavailable: 'Retreat images are temporarily unavailable.',\n        errorHelp: 'Please use the retreat list below or refresh the page.'\n      },\n      vi: {\n        regionLabel: 'T\u1ed5ng quan c\u00e1c kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng Vietnam Detox', linksLabel: 'Trang th\u00f4ng tin t\u1eebng kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng',\n        controlsLabel: '\u0110i\u1ec1u khi\u1ec3n h\u00ecnh \u1ea3nh gi\u1edbi thi\u1ec7u', slidesLabel: 'H\u00ecnh \u1ea3nh c\u00e1c kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng',\n        loadingImages: '\u0110ang t\u1ea3i h\u00ecnh \u1ea3nh kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng\u2026', loadingRetreats: '\u0110ang t\u1ea3i c\u00e1c kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng\u2026',\n        show: 'Hi\u1ec7n th\u00f4ng tin', hide: '\u1ea8n th\u00f4ng tin', showIntroduction: 'Hi\u1ec7n ph\u1ea7n gi\u1edbi thi\u1ec7u', hideIntroduction: '\u1ea8n ph\u1ea7n gi\u1edbi thi\u1ec7u',\n        noImages: 'Hi\u1ec7n ch\u01b0a c\u00f3 h\u00ecnh \u1ea3nh kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng.', newRetreats: 'C\u00e1c kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng m\u1edbi s\u1ebd s\u1edbm \u0111\u01b0\u1ee3c c\u00f4ng b\u1ed1.',\n        retreatName: 'Kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng Vietnam Detox', showImage: 'Hi\u1ec7n h\u00ecnh \u1ea3nh c\u1ee7a ',\n        timeout: 'T\u1ea3i h\u00ecnh \u1ea3nh kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng m\u1ea5t qu\u00e1 nhi\u1ec1u th\u1eddi gian.', unavailable: 'H\u00ecnh \u1ea3nh kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng t\u1ea1m th\u1eddi kh\u00f4ng kh\u1ea3 d\u1ee5ng.',\n        errorHelp: 'Vui l\u00f2ng xem danh s\u00e1ch kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng b\u00ean d\u01b0\u1edbi ho\u1eb7c t\u1ea3i l\u1ea1i trang.'\n      }\n    };\n    var copy = translations[language] || translations.en;\n    heroSection.setAttribute('aria-label', copy.regionLabel);\n    retreatLinks.setAttribute('aria-label', copy.linksLabel);\n    sliderUi.setAttribute('aria-label', copy.controlsLabel);\n    dotsContainer.setAttribute('aria-label', copy.slidesLabel);\n    if (mediaStatus) mediaStatus.textContent = copy.loadingImages;\n    if (retreatStatus) retreatStatus.textContent = copy.loadingRetreats;\n    var sharedApi = getSharedRetreatApi();\n    var apiKey = sharedApi.key(apiUrl);\n    var mobileQuery = window.matchMedia('(max-width: 767px)');\n    var reducedMotionQuery = window.matchMedia('(prefers-reduced-motion: reduce)');\n    var duration = 8200;\n    var timerId = null;\n    var current = 0;\n    var slides = [];\n    var dots = [];\n    var links = [];\n    var lastVersion = 0;\n\n    function syncPanel() {\n      if (!panel || !panelSurface || !panelToggle || !toggleLabel || !toggleIcon) return;\n      var isCollapsed = panel.classList.contains('is-collapsed');\n      toggleLabel.textContent = isCollapsed ? copy.show : copy.hide;\n      toggleIcon.textContent = isCollapsed ? '+' : '\\u00d7';\n      panelToggle.setAttribute('aria-label', isCollapsed ? copy.showIntroduction : copy.hideIntroduction);\n      panelToggle.setAttribute('aria-expanded', String(!isCollapsed));\n      panelSurface.setAttribute('aria-hidden', String(isCollapsed));\n    }\n\n    function syncMediaLayout() {\n      if (!background || !overlay || !panelBody || !locationCopy) return;\n\n      if (mobileQuery.matches) {\n        if (background.parentElement !== panelBody) {\n          var mobileAnchor = locationCopy.parentElement === panelBody ? locationCopy : sliderUi;\n          panelBody.insertBefore(background, mobileAnchor);\n        }\n      } else if (background.parentElement !== heroSection) {\n        heroSection.insertBefore(background, overlay);\n      }\n    }\n\n    function preloadSlide(index) {\n      var slide = slides[index];\n      if (!slide || !slide.dataset.imageUrl) return;\n      var image = new Image();\n      image.src = slide.dataset.imageUrl;\n    }\n\n    function syncSlides() {\n      slides.forEach(function(slide, index) {\n        slide.classList.toggle('is-active', index === current);\n      });\n\n      dots.forEach(function(dot, index) {\n        var isActive = index === current;\n        dot.classList.toggle('is-active', isActive);\n        dot.setAttribute('aria-selected', String(isActive));\n        dot.tabIndex = isActive ? 0 : -1;\n      });\n\n      links.forEach(function(link, index) {\n        link.classList.toggle('is-active', index === current);\n      });\n    }\n\n    function restartProgress() {\n      if (!progress) return;\n      progress.classList.remove('is-animating');\n\n      if (reducedMotionQuery.matches || slides.length < 2) {\n        progress.style.transform = 'scaleX(1)';\n        return;\n      }\n\n      progress.style.transform = 'scaleX(0)';\n      void progress.offsetWidth;\n      progress.classList.add('is-animating');\n    }\n\n    function clearTimer() {\n      if (timerId === null) return;\n      window.clearTimeout(timerId);\n      timerId = null;\n    }\n\n    function queueNextSlide() {\n      clearTimer();\n      if (reducedMotionQuery.matches || document.hidden || slides.length < 2) return;\n      timerId = window.setTimeout(function() {\n        goToSlide(current + 1);\n      }, duration);\n    }\n\n    function goToSlide(index) {\n      if (!slides.length) return;\n      current = (index + slides.length) % slides.length;\n      syncSlides();\n      restartProgress();\n      preloadSlide((current + 1) % slides.length);\n      queueNextSlide();\n    }\n\n    function bindDynamicControls() {\n      dots.forEach(function(dot) {\n        dot.addEventListener('click', function() {\n          goToSlide(Number(dot.dataset.slideIndex));\n        });\n\n        dot.addEventListener('keydown', function(event) {\n          if (event.key !== 'ArrowRight' && event.key !== 'ArrowLeft') return;\n          event.preventDefault();\n          goToSlide(current + (event.key === 'ArrowRight' ? 1 : -1));\n          dots[current].focus();\n        });\n      });\n\n      links.forEach(function(link) {\n        link.addEventListener('mouseenter', function() {\n          goToSlide(Number(link.dataset.slideIndex));\n        });\n        link.addEventListener('focus', function() {\n          goToSlide(Number(link.dataset.slideIndex));\n        });\n      });\n    }\n\n    function renderRetreats(result) {\n      if (!result || result.key !== apiKey || result.version === lastVersion) return;\n      lastVersion = result.version;\n      clearTimer();\n      background.replaceChildren();\n      retreatLinks.replaceChildren();\n      dotsContainer.replaceChildren();\n\n      var items = result.items.filter(function(item) {\n        return item && typeof item === 'object';\n      });\n\n      if (!items.length) {\n        background.appendChild(createElement('div', 'vroh__media-status', copy.noImages));\n        retreatLinks.appendChild(createElement('span', 'vroh__retreat-status', copy.newRetreats));\n        sliderUi.hidden = true;\n        slides = [];\n        dots = [];\n        links = [];\n        return;\n      }\n\n      items.forEach(function(item, index) {\n        var name = cleanText(item.name) || copy.retreatName;\n        var location = cleanText(item.location) || 'Vietnam Detox';\n        var imageUrl = safeUrl(item.image, true);\n        var pageUrl = safeUrl(item.url, false);\n        var slide = createElement('div', 'vroh__slide');\n        slide.dataset.vrohSlide = '';\n        slide.dataset.imageUrl = imageUrl;\n        if (imageUrl) slide.style.backgroundImage = 'url(\"' + imageUrl.replace(\/\"\/g, '%22') + '\")';\n        else slide.classList.add('is-missing');\n        background.appendChild(slide);\n\n        var dot = createElement('button', 'vroh__slider-dot');\n        dot.type = 'button';\n        dot.setAttribute('role', 'tab');\n        dot.setAttribute('aria-label', copy.showImage + name);\n        dot.setAttribute('aria-selected', 'false');\n        dot.dataset.slideIndex = String(index);\n        dotsContainer.appendChild(dot);\n\n        var link = createElement(pageUrl ? 'a' : 'div', 'vroh__retreat-link');\n        if (pageUrl) link.href = pageUrl;\n        link.dataset.slideIndex = String(index);\n        link.appendChild(createElement('strong', '', name));\n        link.appendChild(createElement('span', '', location));\n        retreatLinks.appendChild(link);\n      });\n\n      slides = Array.prototype.slice.call(background.querySelectorAll('.vroh__slide'));\n      dots = Array.prototype.slice.call(dotsContainer.querySelectorAll('.vroh__slider-dot'));\n      links = Array.prototype.slice.call(retreatLinks.querySelectorAll('.vroh__retreat-link'));\n      current = 0;\n      sliderUi.hidden = slides.length < 2;\n      bindDynamicControls();\n      syncMediaLayout();\n      syncSlides();\n      restartProgress();\n      preloadSlide(slides.length > 1 ? 1 : 0);\n      queueNextSlide();\n    }\n\n    function showLoadError(error) {\n      if (lastVersion) return;\n      var message = error && error.vietnamDetoxTimedOut\n        ? copy.timeout\n        : copy.unavailable;\n\n      if (mediaStatus && mediaStatus.parentElement) mediaStatus.textContent = message;\n      if (retreatStatus && retreatStatus.parentElement) retreatStatus.textContent = copy.errorHelp;\n      sliderUi.hidden = true;\n    }\n\n    if (panelToggle) {\n      panelToggle.addEventListener('click', function() {\n        panel.classList.toggle('is-collapsed');\n        syncPanel();\n      });\n    }\n\n    window.addEventListener('vietnamdetox:retreats-loaded', function(event) {\n      renderRetreats(event.detail);\n    });\n\n    document.addEventListener('visibilitychange', function() {\n      if (document.hidden) clearTimer();\n      else {\n        restartProgress();\n        queueNextSlide();\n      }\n    });\n\n    function handleMotionPreference() {\n      restartProgress();\n      queueNextSlide();\n    }\n\n    if (typeof mobileQuery.addEventListener === 'function') {\n      mobileQuery.addEventListener('change', syncMediaLayout);\n      reducedMotionQuery.addEventListener('change', handleMotionPreference);\n    } else {\n      mobileQuery.addListener(syncMediaLayout);\n      reducedMotionQuery.addListener(handleMotionPreference);\n    }\n\n    syncPanel();\n    syncMediaLayout();\n\n    if (typeof window.fetch !== 'function') {\n      showLoadError(new Error('Fetch is unavailable.'));\n      return;\n    }\n\n    sharedApi.load(apiUrl, false).then(renderRetreats).catch(showLoadError);\n  });\n})();\n<\/script>\n\n<script>\n(function vdHeroHeaderClearance() {\n  \"use strict\";\n  \/\/ The global header is fixed over the hero. Measure how far it reaches into the hero and\n  \/\/ expose that as --vd-hero-clearance, so the hero panel always starts below the header.\n  var hero = document.querySelector(\".vroh\");\n  var headerRoot = document.querySelector(\".elementor-location-header\");\n  if (!hero || !headerRoot || hero.dataset.vdClearance === \"ready\") return;\n  hero.dataset.vdClearance = \"ready\";\n  var measuredWidth = null;\n\n  function update() {\n    var scrollTop = window.scrollY || window.pageYOffset || 0;\n    \/\/ The sticky header shrinks after scrolling; keep the measurement taken at the top.\n    if (scrollTop > 0 && measuredWidth === window.innerWidth) return;\n    var bottom = 0;\n    Array.prototype.forEach.call(headerRoot.children, function (el) {\n      if (el.classList.contains(\"elementor-sticky__spacer\")) return;\n      var style = window.getComputedStyle(el);\n      if (style.display === \"none\" || style.visibility === \"hidden\") return;\n      var rect = el.getBoundingClientRect();\n      if (!rect.height) return;\n      bottom = Math.max(bottom, rect.bottom + (style.position === \"fixed\" ? 0 : scrollTop));\n    });\n    var heroTop = hero.getBoundingClientRect().top + scrollTop;\n    measuredWidth = window.innerWidth;\n    hero.style.setProperty(\"--vd-hero-clearance\", Math.max(0, Math.round(bottom - heroTop)) + \"px\");\n  }\n\n  update();\n  document.addEventListener(\"DOMContentLoaded\", update, { once: true });\n  window.addEventListener(\"load\", update, { once: true });\n  window.addEventListener(\"resize\", update, { passive: true });\n  \/\/ Elementor makes the header sticky after load; re-measure when its classes change.\n  if (window.MutationObserver) {\n    new MutationObserver(update).observe(headerRoot, { attributes: true, attributeFilter: [\"class\"], subtree: true });\n  }\n})();\n<\/script>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-cb05801 e-flex e-con-boxed e-con e-parent\" data-id=\"cb05801\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-0a87492 elementor-widget elementor-widget-html\" data-id=\"0a87492\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!--\n  Elementor setup\n  1. Paste this entire file into ONE Elementor HTML widget.\n  2. Paste elementor-retreat-overview-intro.css into the same widget's\n     Advanced > Custom CSS field.\n\n  This component is static, so no JavaScript is required.\n-->\n\n<section class=\"vroi\" aria-labelledby=\"vroi-title\">\n  <div class=\"vroi__inner\">\n    <span class=\"vroi__label\">C\u00e1c k\u1ef3 ngh\u1ec9 d\u01b0\u1ee1ng s\u1eafp t\u1edbi<\/span>\n\n    <h2 class=\"vroi__title\" id=\"vroi-title\">\n M\u1ed7i d\u1ea1ng ki\u1ec7t s\u1ee9c c\u1ea7n m\u1ed9t c\u00e1ch ph\u1ee5c h\u1ed3i kh\u00e1c nhau.\n    <\/h2>\n\n    <p class=\"vroi__copy\">\n C\u00f3 ng\u01b0\u1eddi c\u1ea7n ngh\u1ec9 ng\u01a1i s\u00e2u; ng\u01b0\u1eddi kh\u00e1c c\u1ea7n m\u1ed9t nh\u1ecbp s\u1ed1ng nh\u1eb9 nh\u00e0ng, c\u00f3 c\u1ea5u tr\u00fac h\u01a1n ho\u1eb7c s\u1ef1 h\u1ed7 tr\u1ee3 v\u1ec1 \u0103n u\u1ed1ng v\u00e0 n\u0103ng l\u01b0\u1ee3ng. M\u1ed7i ng\u01b0\u1eddi c\u00f3 m\u1ed9t con \u0111\u01b0\u1eddng ph\u1ee5c h\u1ed3i kh\u00e1c nhau. \n    <\/p>\n  <\/div>\n<\/section>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-6a936df e-flex e-con-boxed e-con e-parent\" data-id=\"6a936df\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-bc933a5 elementor-widget elementor-widget-html\" data-id=\"bc933a5\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!--\n  Elementor setup\n  1. Paste this entire file into ONE Elementor HTML widget.\n  2. Paste retreat-calendar.css into the same widget's\n     Advanced > Custom CSS field.\n  3. Place this widget between retreat-overview-intro and\n     dynamic-retreat-list.\n  4. The REST endpoint can be changed with the data-api-url attribute below.\n\n  The \"view retreats\" link points at #retreats, which is the section id the\n  dynamic-retreat-list widget renders on this page.\n-->\n\n<section class=\"vrc\" data-vrc=\"\" data-api-url=\"\/wp-json\/retreats\/v1\/all\" aria-labelledby=\"vrc-title\">\n  <div class=\"vrc__wrap\">\n    <div class=\"vrc__panel\">\n\n      <div class=\"vrc__header\">\n        <div>\n          <span class=\"vrc__eyebrow\" data-vrc-eyebrow=\"\">L\u1ecaCH K\u1ef2 NGH\u1ec8 D\u01af\u1ee0NG<\/span>\n          <h2 class=\"vrc__title\" id=\"vrc-title\" data-vrc-heading=\"\">Ch\u1ecdn th\u00e1ng b\u1ea1n mu\u1ed1n \u0111i<\/h2>\n          <p class=\"vrc__copy\" data-vrc-intro=\"\">\n Xem c\u00e1c k\u1ef3 ngh\u1ec9 d\u01b0\u1ee1ng di\u1ec5n ra trong th\u00e1ng ph\u00f9 h\u1ee3p v\u1edbi k\u1ebf ho\u1ea1ch c\u1ee7a b\u1ea1n.\n          <\/p>\n        <\/div>\n\n        <p class=\"vrc__note\" data-vrc-note=\"\">\n L\u1ecbch c\u00f3 th\u1ec3 thay \u0111\u1ed5i. Vui l\u00f2ng x\u00e1c nh\u1eadn tr\u01b0\u1edbc khi \u0111\u1eb7t ch\u1ed7. \n        <\/p>\n      <\/div>\n\n      <div class=\"vrc__divider\" aria-hidden=\"true\"><\/div>\n\n      <div class=\"vrc__months\" data-vrc-months=\"\" aria-label=\"Choose a travel month\"><\/div>\n\n      <div class=\"vrc__results\">\n        <h3 class=\"vrc__results-title\" data-vrc-results-title=\"\">K\u1ef3 ngh\u1ec9 d\u01b0\u1ee1ng hi\u1ec7n c\u00f3<\/h3>\n\n        <div class=\"vrc__list\" data-vrc-list=\"\" aria-live=\"polite\"><\/div>\n\n        <p class=\"vrc__message is-visible\" data-vrc-message=\"\">\u0110ang t\u1ea3i l\u1ecbch k\u1ef3 ngh\u1ec9 d\u01b0\u1ee1ng\u2026<\/p>\n\n        <a href=\"#retreats\" class=\"vrc__action\" data-vrc-action=\"\">Xem c\u00e1c l\u1ef1a ch\u1ecdn ngh\u1ec9 d\u01b0\u1ee1ng<\/a>\n\n        <p class=\"vrc__planning-note\" data-vrc-planning=\"\">\n C\u00e1c k\u1ef3 t\u0129nh t\u00e2m theo l\u1ecbch tr\u00ecnh k\u00e9o d\u00e0i 5 ng\u00e0y 4 \u0111\u00eam. C\u00e1c k\u1ef3 t\u0129nh t\u00e2m ri\u00eang \u0111\u01b0\u1ee3c s\u1eafp x\u1ebfp theo y\u00eau c\u1ea7u c\u00e1 nh\u00e2n. \n        <\/p>\n      <\/div>\n\n    <\/div>\n  <\/div>\n\n  <noscript>\n    <p class=\"vrc__message is-visible\">C\u1ea7n c\u00f3 JavaScript \u0111\u1ec3 t\u1ea3i l\u1ecbch tr\u00ecnh kh\u00f3a tu.<\/p>\n  <\/noscript>\n<\/section>\n\n<script>\n(function () {\n  'use strict';\n\n  var TEXT = {\n    en: {\n      locale: 'en-GB',\n      eyebrow: 'RETREAT DATES',\n      heading: 'Choose your travel month',\n      intro: 'See which retreats are available during the month that fits your plans.',\n      note: 'Dates may change. Please confirm before booking.',\n      planning: 'Scheduled retreats run for five days and four nights. Private Retreats are arranged individually.',\n      action: 'View retreat options',\n      date: 'date',\n      dates: 'dates',\n      noDates: 'No dates yet',\n      loading: 'Loading retreat dates\u2026',\n      noMonth: 'No upcoming retreats are currently available for this month.',\n      error: 'Could not load retreat information. Please try again later.',\n      limited: 'Limited places',\n      available: 'Available retreats'\n    },\n\n    vi: {\n      locale: 'vi-VN',\n      eyebrow: 'L\u1ecaCH RETREAT',\n      heading: 'Ch\u1ecdn th\u00e1ng b\u1ea1n mu\u1ed1n \u0111i',\n      intro: 'Xem nh\u1eefng retreat \u0111ang m\u1edf trong th\u00e1ng ph\u00f9 h\u1ee3p v\u1edbi k\u1ebf ho\u1ea1ch c\u1ee7a b\u1ea1n.',\n      note: 'L\u1ecbch c\u00f3 th\u1ec3 thay \u0111\u1ed5i. Vui l\u00f2ng x\u00e1c nh\u1eadn tr\u01b0\u1edbc khi \u0111\u1eb7t ch\u1ed7.',\n      planning: 'C\u00e1c retreat theo l\u1ecbch k\u00e9o d\u00e0i n\u0103m ng\u00e0y b\u1ed1n \u0111\u00eam. Retreat ri\u00eang t\u01b0 \u0111\u01b0\u1ee3c s\u1eafp x\u1ebfp ri\u00eang theo y\u00eau c\u1ea7u.',\n      action: 'Xem c\u00e1c l\u1ef1a ch\u1ecdn retreat',\n      date: 'l\u1ecbch',\n      dates: 'l\u1ecbch',\n      noDates: 'Ch\u01b0a c\u00f3 l\u1ecbch',\n      loading: '\u0110ang t\u1ea3i l\u1ecbch retreat\u2026',\n      noMonth: 'Kh\u00f4ng c\u00f3 l\u1ecbch retreat trong th\u00e1ng n\u00e0y.',\n      error: 'Kh\u00f4ng th\u1ec3 t\u1ea3i th\u00f4ng tin retreat. Vui l\u00f2ng th\u1eed l\u1ea1i sau.',\n      limited: 'S\u1ed1 ch\u1ed7 c\u00f3 h\u1ea1n',\n      available: 'Retreat hi\u1ec7n c\u00f3'\n    },\n\n    de: {\n      locale: 'de-DE',\n      eyebrow: 'RETREAT-TERMINE',\n      heading: 'W\u00e4hlen Sie Ihren Reisemonat',\n      intro: 'Sehen Sie, welche Retreats in dem Monat verf\u00fcgbar sind, der zu Ihrer Planung passt.',\n      note: 'Termine k\u00f6nnen sich \u00e4ndern. Bitte vor der Buchung best\u00e4tigen.',\n      planning: 'Geplante Retreats dauern f\u00fcnf Tage und vier N\u00e4chte. Private Retreats werden individuell vereinbart.',\n      action: 'Retreats ansehen',\n      date: 'Termin',\n      dates: 'Termine',\n      noDates: 'Noch keine Termine',\n      loading: 'Retreat-Termine werden geladen\u2026',\n      noMonth: 'In diesem Monat sind keine Retreats verf\u00fcgbar.',\n      error: 'Die Retreat-Informationen konnten nicht geladen werden.',\n      limited: 'Begrenzte Pl\u00e4tze',\n      available: 'Verf\u00fcgbare Retreats'\n    }\n  };\n\n  var privateCache = {};\n\n  function toArray(payload) {\n    if (Array.isArray(payload)) return payload;\n    if (payload && Array.isArray(payload.items)) return payload.items;\n    if (payload && Array.isArray(payload.retreats)) return payload.retreats;\n    if (payload && Array.isArray(payload.data)) return payload.data;\n    throw new Error('Unexpected retreat API response');\n  }\n\n  function privateLoad(apiUrl) {\n    if (privateCache[apiUrl]) return privateCache[apiUrl];\n\n    var controller = typeof AbortController === 'function' ? new AbortController() : null;\n    var timeoutId = window.setTimeout(function () {\n      if (controller) controller.abort();\n    }, 15000);\n\n    privateCache[apiUrl] = fetch(apiUrl, {\n      credentials: 'same-origin',\n      headers: { Accept: 'application\/json' },\n      signal: controller ? controller.signal : undefined\n    }).then(function (response) {\n      if (!response.ok) throw new Error('Retreat API returned HTTP ' + response.status);\n      return response.json();\n    }).then(function (payload) {\n      window.clearTimeout(timeoutId);\n      return toArray(payload);\n    }).catch(function (error) {\n      window.clearTimeout(timeoutId);\n      delete privateCache[apiUrl];\n      throw error;\n    });\n\n    return privateCache[apiUrl];\n  }\n\n  \/*\n   * Other Vietnam Detox widgets publish window.VietnamDetoxRetreatAPI, but the\n   * homepage and retreat-list builds use different, incompatible signatures.\n   * Detect the shape before reusing it so one shared request serves both\n   * widgets, and fall back to a private fetch when it is something else.\n   *\/\n  function loadRetreats(apiUrl, language) {\n    var shared = window.VietnamDetoxRetreatAPI;\n\n    if (shared && typeof shared.load === 'function') {\n      try {\n        \/\/ dynamic-retreat-list shape: load(url, force) -> { items: [...] }\n        if (typeof shared.key === 'function' && shared.entries) {\n          return Promise.resolve(shared.load(apiUrl, false)).then(toArray);\n        }\n\n        \/\/ homepage calendar shape: load(path, language) -> [...]\n        if (shared.requests) {\n          return Promise.resolve(shared.load(apiUrl, language)).then(toArray);\n        }\n      } catch (error) {\n        \/\/ Fall through to the private loader.\n      }\n    }\n\n    return privateLoad(apiUrl);\n  }\n\n  function initCalendar(root) {\n    if (root.dataset.vrcReady === 'true') return;\n    root.dataset.vrcReady = 'true';\n\n    var endpoint = new URL(\n      root.getAttribute('data-api-url') || '\/wp-json\/retreats\/v1\/all',\n      window.location.href\n    );\n\n    var language = String(\n      endpoint.searchParams.get('lang') || document.documentElement.lang || 'en'\n    ).toLowerCase().split(\/[-_]\/)[0];\n\n    if (language === 'vn') language = 'vi';\n    if (['en', 'vi', 'de'].indexOf(language) < 0) language = 'en';\n\n    endpoint.searchParams.set('lang', language);\n\n    var apiUrl = endpoint.href;\n    var text = TEXT[language];\n    var locale = text.locale;\n\n    var monthStrip = root.querySelector('[data-vrc-months]');\n    var list = root.querySelector('[data-vrc-list]');\n    var resultsTitle = root.querySelector('[data-vrc-results-title]');\n    var message = root.querySelector('[data-vrc-message]');\n\n    \/\/ Apply the translated static copy.\n    root.querySelector('[data-vrc-eyebrow]').textContent = text.eyebrow;\n    root.querySelector('[data-vrc-heading]').textContent = text.heading;\n    root.querySelector('[data-vrc-intro]').textContent = text.intro;\n    root.querySelector('[data-vrc-note]').textContent = text.note;\n    root.querySelector('[data-vrc-planning]').textContent = text.planning;\n    root.querySelector('[data-vrc-action]').textContent = text.action;\n    resultsTitle.textContent = text.available;\n\n    var today = new Date();\n    today.setHours(0, 0, 0, 0);\n\n    function parseApiDate(value) {\n      if (!value) return null;\n\n      var match = String(value).match(\/^(\\d{1,2})\\\/(\\d{1,2})\\\/(\\d{4})$\/);\n\n      if (match) {\n        var month = Number(match[1]);\n        var day = Number(match[2]);\n        var year = Number(match[3]);\n        var result = new Date(year, month - 1, day);\n\n        if (\n          result.getFullYear() === year &&\n          result.getMonth() === month - 1 &&\n          result.getDate() === day\n        ) {\n          return result;\n        }\n\n        return null;\n      }\n\n      match = String(value).match(\/^(\\d{4})-(\\d{2})-(\\d{2})$\/);\n\n      if (match) {\n        return new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3]));\n      }\n\n      return null;\n    }\n\n    function monthKey(date) {\n      return date.getFullYear() + '-' + String(date.getMonth() + 1).padStart(2, '0');\n    }\n\n    function safeUrl(value) {\n      if (!value || typeof value !== 'string') return null;\n\n      try {\n        var url = new URL(value, window.location.origin);\n        return ['http:', 'https:'].indexOf(url.protocol) >= 0 ? url.href : null;\n      } catch (error) {\n        return null;\n      }\n    }\n\n    function showMessage(copy) {\n      message.textContent = copy || '';\n      message.classList.toggle('is-visible', Boolean(copy));\n    }\n\n    function getUpcomingDates(retreat) {\n      if (!Array.isArray(retreat.dates)) return [];\n\n      return retreat.dates.map(function (schedule) {\n        var start = parseApiDate(schedule.start);\n        return { schedule: schedule, start: start, end: parseApiDate(schedule.end) || start };\n      }).filter(function (item) {\n        return item.start && item.end && item.end >= today;\n      }).sort(function (a, b) {\n        return a.start - b.start;\n      });\n    }\n\n    function render(retreats) {\n      var schedules = [];\n\n      retreats.forEach(function (retreat) {\n        getUpcomingDates(retreat).forEach(function (item) {\n          schedules.push({\n            name: retreat.name || '',\n            location: retreat.location || '',\n            url: retreat.url || '',\n            start: item.start,\n            end: item.end,\n            limited: item.schedule.limited === true\n          });\n        });\n      });\n\n      schedules.sort(function (a, b) {\n        return a.start - b.start;\n      });\n\n      var planningMonths = [];\n\n      for (var index = 0; index < 9; index += 1) {\n        planningMonths.push(\n          monthKey(new Date(today.getFullYear(), today.getMonth() + index, 1))\n        );\n      }\n\n      var counts = {};\n\n      schedules.forEach(function (schedule) {\n        var key = monthKey(schedule.start);\n        counts[key] = (counts[key] || 0) + 1;\n      });\n\n      var selectedMonth = planningMonths.filter(function (key) {\n        return counts[key] > 0;\n      })[0] || null;\n\n      var monthFormatter = new Intl.DateTimeFormat(locale, { month: 'long', year: 'numeric' });\n      var shortMonthFormatter = new Intl.DateTimeFormat(locale, { month: 'short' });\n      var dayFormatter = new Intl.DateTimeFormat(locale, { day: '2-digit' });\n\n      function renderMonthButtons() {\n        monthStrip.replaceChildren();\n\n        planningMonths.forEach(function (key) {\n          var count = counts[key] || 0;\n          var parts = key.split('-');\n          var monthDate = new Date(Number(parts[0]), Number(parts[1]) - 1, 1);\n\n          var button = document.createElement('button');\n          button.type = 'button';\n          button.className = 'vrc__month';\n          button.disabled = count === 0;\n          button.setAttribute('aria-pressed', String(selectedMonth === key));\n\n          if (selectedMonth === key) button.classList.add('is-active');\n\n          var monthName = document.createElement('span');\n          monthName.className = 'vrc__month-name';\n          monthName.textContent = shortMonthFormatter.format(monthDate).toLocaleUpperCase(locale);\n\n          var year = document.createElement('span');\n          year.className = 'vrc__month-year';\n          year.textContent = parts[0];\n\n          var countElement = document.createElement('span');\n          countElement.className = 'vrc__month-count';\n          countElement.textContent = count\n            ? count + ' ' + (count === 1 ? text.date : text.dates)\n            : text.noDates;\n\n          button.append(monthName, year, countElement);\n\n          if (count) {\n            button.addEventListener('click', function () {\n              selectedMonth = key;\n              renderMonthButtons();\n              renderRows();\n            });\n          }\n\n          monthStrip.appendChild(button);\n        });\n      }\n\n      function renderRows() {\n        list.replaceChildren();\n\n        if (!selectedMonth) {\n          resultsTitle.textContent = text.available.toLocaleUpperCase(locale);\n          showMessage(text.noMonth);\n          return;\n        }\n\n        var matches = schedules.filter(function (schedule) {\n          return monthKey(schedule.start) === selectedMonth;\n        });\n\n        var parts = selectedMonth.split('-');\n        var monthDate = new Date(Number(parts[0]), Number(parts[1]) - 1, 1);\n\n        resultsTitle.textContent = monthFormatter.format(monthDate).toLocaleUpperCase(locale);\n        showMessage(matches.length ? '' : text.noMonth);\n\n        matches.forEach(function (schedule) {\n          var row = document.createElement('article');\n          row.className = 'vrc__item';\n\n          var date = document.createElement('span');\n          date.className = 'vrc__date';\n\n          var day = document.createElement('span');\n          day.className = 'vrc__day';\n          day.textContent = dayFormatter.format(schedule.start);\n\n          var month = document.createElement('span');\n          month.className = 'vrc__item-month';\n          month.textContent = shortMonthFormatter.format(schedule.start).toLocaleUpperCase(locale);\n\n          date.append(day, month);\n\n          var retreatUrl = safeUrl(schedule.url);\n          var name = document.createElement(retreatUrl ? 'a' : 'span');\n          name.className = 'vrc__name';\n          name.textContent = schedule.name;\n          if (retreatUrl) name.href = retreatUrl;\n\n          var location = document.createElement('span');\n          location.className = 'vrc__location';\n          location.textContent = schedule.location;\n\n          row.append(date, name, location);\n\n          if (schedule.limited) {\n            var badge = document.createElement('span');\n            badge.className = 'vrc__status';\n            badge.textContent = text.limited;\n            row.appendChild(badge);\n          }\n\n          list.appendChild(row);\n        });\n      }\n\n      renderMonthButtons();\n      renderRows();\n    }\n\n    showMessage(text.loading);\n\n    loadRetreats(apiUrl, language).then(render).catch(function (error) {\n      console.error('Retreat calendar:', error);\n      monthStrip.replaceChildren();\n      list.replaceChildren();\n      showMessage(text.error);\n    });\n  }\n\n  function init() {\n    Array.prototype.forEach.call(document.querySelectorAll('[data-vrc]'), initCalendar);\n  }\n\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', init);\n  } else {\n    init();\n  }\n})();\n<\/script>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-2cc28ec e-flex e-con-boxed e-con e-parent\" data-id=\"2cc28ec\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-218e7d3 elementor-widget elementor-widget-html\" data-id=\"218e7d3\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!--\n  Elementor setup\n  1. Paste this entire file into ONE Elementor HTML widget.\n  2. Paste elementor-dynamic-retreat-list.css into the same widget's\n     Advanced > Custom CSS field.\n  3. The REST endpoint can be changed with the data-api-url attribute below.\n-->\n\n<section class=\"vdrl\" id=\"retreats\" data-vdrl=\"\" data-api-url=\"\/wp-json\/retreats\/v1\/all\" aria-label=\"Upcoming Vietnam Detox retreats\">\n  <div class=\"vdrl__wrap\">\n    <div class=\"vdrl__status\" data-vdrl-status=\"\" role=\"status\" aria-live=\"polite\">\n      <span class=\"vdrl__spinner\" aria-hidden=\"true\"><\/span>\n      <span data-vdrl-status-text=\"\">\u0110ang t\u1ea3i c\u00e1c kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng s\u1eafp t\u1edbi...<\/span>\n    <\/div>\n\n    <div class=\"vdrl__grid\" data-vdrl-grid=\"\" aria-busy=\"true\" aria-label=\"Retreats\">\n      <article class=\"vdrl__skeleton\" aria-hidden=\"true\">\n        <div class=\"vdrl__skeleton-image\"><\/div>\n        <div class=\"vdrl__skeleton-body\"><i><\/i><i><\/i><i><\/i><i><\/i><\/div>\n      <\/article>\n      <article class=\"vdrl__skeleton\" aria-hidden=\"true\">\n        <div class=\"vdrl__skeleton-image\"><\/div>\n        <div class=\"vdrl__skeleton-body\"><i><\/i><i><\/i><i><\/i><i><\/i><\/div>\n      <\/article>\n      <article class=\"vdrl__skeleton\" aria-hidden=\"true\">\n        <div class=\"vdrl__skeleton-image\"><\/div>\n        <div class=\"vdrl__skeleton-body\"><i><\/i><i><\/i><i><\/i><i><\/i><\/div>\n      <\/article>\n    <\/div>\n\n    <noscript>\n      <p class=\"vdrl__noscript\">C\u1ea7n c\u00f3 JavaScript \u0111\u1ec3 t\u1ea3i l\u1ecbch tr\u00ecnh kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng hi\u1ec7n t\u1ea1i.<\/p>\n    <\/noscript>\n  <\/div>\n<\/section>\n\n<script>\n(function() {\n  'use strict';\n\n  function getSharedRetreatApi() {\n    var shared = window.VietnamDetoxRetreatAPI = window.VietnamDetoxRetreatAPI || {};\n    shared.entries = shared.entries || {};\n    shared.version = shared.version || 0;\n\n    if (typeof shared.key !== 'function') {\n      shared.key = function(url) {\n        return new URL(url, window.location.href).href;\n      };\n    }\n\n    if (typeof shared.load !== 'function') {\n      shared.load = function(url, force) {\n        var key = shared.key(url);\n        if (!force && shared.entries[key]) return shared.entries[key].promise;\n\n        var controller = typeof AbortController === 'function' ? new AbortController() : null;\n        var timedOut = false;\n        var timeoutId = window.setTimeout(function() {\n          timedOut = true;\n          if (controller) controller.abort();\n        }, 15000);\n\n        var entry = {};\n        entry.promise = fetch(key, {\n          method: 'GET',\n          credentials: 'same-origin',\n          headers: { Accept: 'application\/json' },\n          signal: controller ? controller.signal : undefined\n        }).then(function(response) {\n          if (!response.ok) throw new Error('API request failed with status ' + response.status);\n          return response.json();\n        }).then(function(payload) {\n          var items = Array.isArray(payload)\n            ? payload\n            : Array.isArray(payload.retreats)\n              ? payload.retreats\n              : Array.isArray(payload.data)\n                ? payload.data\n                : null;\n\n          if (!items) throw new Error('The API returned an unexpected response shape.');\n\n          var result = { key: key, items: items, version: ++shared.version };\n          entry.result = result;\n          window.dispatchEvent(new CustomEvent('vietnamdetox:retreats-loaded', { detail: result }));\n          return result;\n        }).catch(function(error) {\n          if (timedOut) error.vietnamDetoxTimedOut = true;\n          throw error;\n        }).finally(function() {\n          window.clearTimeout(timeoutId);\n        });\n\n        shared.entries[key] = entry;\n        return entry.promise;\n      };\n    }\n\n    return shared;\n  }\n\n  var roots = document.querySelectorAll('[data-vdrl]');\n\n  var profiles = {\n    ANLYT: {\n      accent: '#C43010',\n      tagFallback: 'Y\u00ean T\u1eed',\n      chips: ['Quiet', 'Elevated', 'Deeply reflective'],\n      summary: 'A mountain retreat for guests seeking peace, structure and inner clarity.',\n      bestFor: 'Reflection, mindfulness and a more contemplative reset.',\n      buttonLabel: 'View An Retreat'\n    },\n    AWVH: {\n      accent: '#E06420',\n      tagFallback: 'Hu\u1ebf',\n      chips: ['Thermal', 'Restorative', 'Nature-led'],\n      summary: 'A restorative Hu\u1ebf retreat shaped by mineral springs, quiet nature and a softer pace of recovery.',\n      bestFor: 'Guests seeking deep rest, warm water therapies and a calmer nervous system.',\n      buttonLabel: 'View Y\u00ean Retreat'\n    },\n    'HOI-FRVDAD': {\n      accent: '#D75A20',\n      tagFallback: 'Da Nang',\n      chips: ['Coastal', 'Structured', 'Supported return'],\n      summary: 'A five-day guided fasting and coastal wellness retreat designed as a supported pause with a clear purpose.',\n      bestFor: 'Guests seeking steadier energy, practical guidance and a calmer relationship with food, rest and attention.',\n      buttonLabel: 'View H\u1ed3i Retreat'\n    },\n    PRIVATE: {\n      accent: '#1E3D20',\n      tagFallback: 'Private',\n      chips: ['Flexible', 'Discreet', 'Individual support'],\n      summary: 'A bespoke fasting retreat for guests who need privacy, flexibility and a more individual rhythm.',\n      bestFor: 'Couples, families, private groups and guests who prefer a premium private setting.',\n      buttonLabel: 'Explore Private Retreats',\n      byRequest: true\n    },\n    DEFAULT: {\n      accent: '#1E3D20',\n      tagFallback: 'Vietnam Detox',\n      chips: [],\n      summary: '',\n      bestFor: '',\n      buttonLabel: 'View Retreat'\n    }\n  };\n\n  var translations = {\n    en: {\n      locale: 'en-GB', regionLabel: 'Upcoming Vietnam Detox retreats', gridLabel: 'Retreats',\n      loading: 'Loading upcoming retreats\u2026', retry: 'Try again',\n      empty: 'No retreats are currently available. Please check back soon.',\n      timeout: 'The retreat schedule took too long to load. Please try again.',\n      error: 'We could not load the current retreat schedule. Please try again.',\n      unsupported: 'This browser cannot load the current retreat schedule.',\n      retreatName: 'Vietnam Detox Retreat', summary: 'A guided Vietnam Detox fasting retreat.',\n      datePending: 'Date to be confirmed', limited: 'Limited places', open: 'Open',\n      customDates: 'Custom dates and setting', pricingPage: 'Pricing on retreat page',\n      fromPrice: 'From VND {price}', perPerson: 'per person', byRequest: 'By Request',\n      multipleDates: 'Multiple dates available', nextAvailable: 'Next date available', datesPending: 'Dates to be announced',\n      bestFor: 'Best for', availability: 'Availability', availableByRequest: 'Available by request.',\n      nextDate: 'Next date', otherDate: 'View other date', otherDates: 'View other dates', hideDates: 'Hide other dates',\n      newDates: 'New dates will be announced soon.', profiles: {}\n    },\n    vi: {\n      locale: 'vi-VN', regionLabel: 'C\u00e1c kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng Vietnam Detox s\u1eafp t\u1edbi', gridLabel: 'C\u00e1c kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng',\n      loading: '\u0110ang t\u1ea3i c\u00e1c kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng s\u1eafp t\u1edbi\u2026', retry: 'Th\u1eed l\u1ea1i',\n      empty: 'Hi\u1ec7n ch\u01b0a c\u00f3 kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng n\u00e0o. Vui l\u00f2ng quay l\u1ea1i sau.',\n      timeout: 'T\u1ea3i l\u1ecbch ngh\u1ec9 d\u01b0\u1ee1ng m\u1ea5t qu\u00e1 nhi\u1ec1u th\u1eddi gian. Vui l\u00f2ng th\u1eed l\u1ea1i.',\n      error: 'Kh\u00f4ng th\u1ec3 t\u1ea3i l\u1ecbch ngh\u1ec9 d\u01b0\u1ee1ng hi\u1ec7n t\u1ea1i. Vui l\u00f2ng th\u1eed l\u1ea1i.',\n      unsupported: 'Tr\u00ecnh duy\u1ec7t n\u00e0y kh\u00f4ng th\u1ec3 t\u1ea3i l\u1ecbch ngh\u1ec9 d\u01b0\u1ee1ng hi\u1ec7n t\u1ea1i.',\n      retreatName: 'Kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng Vietnam Detox', summary: 'Kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng nh\u1ecbn \u0103n c\u00f3 h\u01b0\u1edbng d\u1eabn c\u1ee7a Vietnam Detox.',\n      datePending: 'Ng\u00e0y s\u1ebd \u0111\u01b0\u1ee3c x\u00e1c nh\u1eadn', limited: 'S\u1ed1 ch\u1ed7 c\u00f3 h\u1ea1n', open: 'C\u00f2n ch\u1ed7',\n      customDates: 'Ng\u00e0y v\u00e0 \u0111\u1ecba \u0111i\u1ec3m theo y\u00eau c\u1ea7u', pricingPage: 'Xem gi\u00e1 t\u1ea1i trang kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng',\n      fromPrice: 'T\u1eeb {price} VND', perPerson: 'm\u1ed7i ng\u01b0\u1eddi', byRequest: 'Theo y\u00eau c\u1ea7u',\n      multipleDates: 'C\u00f3 nhi\u1ec1u l\u1ecbch kh\u1edfi h\u00e0nh', nextAvailable: '\u0110\u00e3 c\u00f3 l\u1ecbch ti\u1ebfp theo', datesPending: 'L\u1ecbch s\u1ebd \u0111\u01b0\u1ee3c c\u00f4ng b\u1ed1',\n      bestFor: 'Ph\u00f9 h\u1ee3p v\u1edbi', availability: 'L\u1ecbch t\u1ed5 ch\u1ee9c', availableByRequest: 'T\u1ed5 ch\u1ee9c theo y\u00eau c\u1ea7u.',\n      nextDate: 'L\u1ecbch ti\u1ebfp theo', otherDate: 'Xem l\u1ecbch kh\u00e1c', otherDates: 'Xem c\u00e1c l\u1ecbch kh\u00e1c', hideDates: '\u1ea8n c\u00e1c l\u1ecbch kh\u00e1c',\n      newDates: 'L\u1ecbch m\u1edbi s\u1ebd s\u1edbm \u0111\u01b0\u1ee3c c\u00f4ng b\u1ed1.',\n      profiles: {\n        ANLYT: {\n          chips: ['Y\u00ean t\u0129nh', 'Gi\u1eefa n\u00fai r\u1eebng', 'L\u1eafng \u0111\u1ecdng'],\n          summary: 'Kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng gi\u1eefa n\u00fai r\u1eebng d\u00e0nh cho nh\u1eefng ai t\u00ecm ki\u1ebfm s\u1ef1 b\u00ecnh y\u00ean, n\u1ebfp s\u1ed1ng \u0111i\u1ec1u \u0111\u1ed9 v\u00e0 t\u00e2m tr\u00ed s\u00e1ng su\u1ed1t.',\n          bestFor: 'Chi\u00eam nghi\u1ec7m, th\u1ef1c h\u00e0nh ch\u00e1nh ni\u1ec7m v\u00e0 t\u00ecm l\u1ea1i s\u1ef1 c\u00e2n b\u1eb1ng trong t\u0129nh l\u1eb7ng.',\n          buttonLabel: 'Xem kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng An'\n        },\n        AWVH: {\n          chips: ['Su\u1ed1i kho\u00e1ng n\u00f3ng', 'Ph\u1ee5c h\u1ed3i', 'G\u1ea7n g\u0169i thi\u00ean nhi\u00ean'],\n          summary: 'Kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng ph\u1ee5c h\u1ed3i t\u1ea1i Hu\u1ebf v\u1edbi su\u1ed1i kho\u00e1ng, thi\u00ean nhi\u00ean y\u00ean b\u00ecnh v\u00e0 nh\u1ecbp ngh\u1ec9 ng\u01a1i nh\u1eb9 nh\u00e0ng.',\n          bestFor: 'Nh\u1eefng ai c\u1ea7n ngh\u1ec9 ng\u01a1i s\u00e2u, t\u1eadn h\u01b0\u1edfng li\u1ec7u ph\u00e1p n\u01b0\u1edbc \u1ea5m v\u00e0 th\u01b0 gi\u00e3n h\u1ec7 th\u1ea7n kinh.',\n          buttonLabel: 'Xem kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng Y\u00ean'\n        },\n        'HOI-FRVDAD': {\n          tagFallback: '\u0110\u00e0 N\u1eb5ng', chips: ['Ven bi\u1ec3n', 'C\u00f3 l\u1ed9 tr\u00ecnh', '\u0110\u1ed3ng h\u00e0nh sau kh\u00f3a'],\n          summary: 'Kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng nh\u1ecbn \u0103n c\u00f3 h\u01b0\u1edbng d\u1eabn k\u00e9o d\u00e0i n\u0103m ng\u00e0y b\u00ean b\u1edd bi\u1ec3n, \u0111\u01b0\u1ee3c thi\u1ebft k\u1ebf nh\u01b0 m\u1ed9t kho\u1ea3ng d\u1eebng c\u00f3 ch\u1ee7 \u0111\u00edch.',\n          bestFor: 'Nh\u1eefng ai mu\u1ed1n l\u1ea5y l\u1ea1i n\u0103ng l\u01b0\u1ee3ng \u1ed5n \u0111\u1ecbnh, c\u00f3 h\u01b0\u1edbng d\u1eabn th\u1ef1c t\u1ebf v\u00e0 m\u1ed1i quan h\u1ec7 nh\u1eb9 nh\u00e0ng h\u01a1n v\u1edbi \u0103n u\u1ed1ng, ngh\u1ec9 ng\u01a1i v\u00e0 s\u1ef1 t\u1eadp trung.',\n          buttonLabel: 'Xem kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng H\u1ed3i'\n        },\n        PRIVATE: {\n          tagFallback: 'Ri\u00eang t\u01b0', chips: ['Linh ho\u1ea1t', 'K\u00edn \u0111\u00e1o', 'H\u1ed7 tr\u1ee3 c\u00e1 nh\u00e2n'],\n          summary: 'Kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng nh\u1ecbn \u0103n \u0111\u01b0\u1ee3c thi\u1ebft k\u1ebf ri\u00eang cho nh\u1eefng ai c\u1ea7n s\u1ef1 ri\u00eang t\u01b0, linh ho\u1ea1t v\u00e0 nh\u1ecbp sinh ho\u1ea1t ph\u00f9 h\u1ee3p.',\n          bestFor: 'C\u1eb7p \u0111\u00f4i, gia \u0111\u00ecnh, nh\u00f3m ri\u00eang v\u00e0 nh\u1eefng ai y\u00eau th\u00edch kh\u00f4ng gian ngh\u1ec9 d\u01b0\u1ee1ng ri\u00eang t\u01b0 cao c\u1ea5p.',\n          buttonLabel: 'Kh\u00e1m ph\u00e1 kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng ri\u00eang t\u01b0'\n        },\n        DEFAULT: { buttonLabel: 'Xem kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng' }\n      }\n    }\n  };\n\n  function createElement(tag, className, text) {\n    var element = document.createElement(tag);\n    if (className) element.className = className;\n    if (typeof text === 'string') element.textContent = text;\n    return element;\n  }\n\n  function cleanText(value) {\n    if (value === null || typeof value === 'undefined') return '';\n    var parser = new DOMParser();\n    var documentValue = parser.parseFromString(String(value), 'text\/html');\n    return (documentValue.body.textContent || '').replace(\/\\s+\/g, ' ').trim();\n  }\n\n  function summarize(value, maximum) {\n    var text = cleanText(value);\n    if (!text || text.length <= maximum) return text;\n    var shortened = text.slice(0, maximum + 1).replace(\/\\s+\\S*$\/, '').trim();\n    return shortened + '\u2026';\n  }\n\n  function safeUrl(value, allowImageData) {\n    if (!value) return '';\n    try {\n      var parsed = new URL(String(value), window.location.href);\n      var allowed = parsed.protocol === 'http:' || parsed.protocol === 'https:';\n      if (allowImageData && parsed.protocol === 'data:') allowed = \/^data:image\\\/\/i.test(String(value));\n      return allowed ? parsed.href : '';\n    } catch (error) {\n      return '';\n    }\n  }\n\n  function parseApiDate(value) {\n    if (!value) return null;\n    var parts = String(value).match(\/^(\\d{1,2})\\\/(\\d{1,2})\\\/(\\d{4})$\/);\n    if (!parts) return null;\n    var date = new Date(Number(parts[3]), Number(parts[1]) - 1, Number(parts[2]), 12, 0, 0, 0);\n    return Number.isNaN(date.getTime()) ? null : date;\n  }\n\n  function formatDateRange(item, copy) {\n    var start = item._start;\n    var end = item._end;\n    if (!start || !end) return copy.datePending;\n    var formatter = new Intl.DateTimeFormat(copy.locale, { day: 'numeric', month: 'long', year: 'numeric' });\n    return typeof formatter.formatRange === 'function' && start <= end\n      ? formatter.formatRange(start, end)\n      : formatter.format(start) + '\u2013' + formatter.format(end);\n  }\n\n  function availabilityText(item, copy) {\n    return item && item.limited === true ? copy.limited : copy.open;\n  }\n\n  \/\/ Card order follows the design, not the order the API happens to return.\n  var displayOrder = ['ANLYT', 'HOI-FRVDAD', 'AWVH', 'PRIVATE'];\n\n  function profileKeyFor(item) {\n    var sku = cleanText(item && item.retreat_sku).toUpperCase();\n    var name = cleanText(item && item.name);\n    return item && item.is_private === true ? 'PRIVATE'\n      : profiles[sku] ? sku : \/private|corporate\/i.test(name) ? 'PRIVATE' : 'DEFAULT';\n  }\n\n  function profileFor(item, copy) {\n    var key = profileKeyFor(item);\n    return Object.assign({}, profiles[key], copy.profiles[key] || {});\n  }\n\n  \/\/ Anything not named in displayOrder keeps its API order, after the known cards.\n  function orderItems(items) {\n    return items.map(function(item, index) {\n      var rank = displayOrder.indexOf(profileKeyFor(item));\n      return { item: item, index: index, rank: rank < 0 ? displayOrder.length : rank };\n    }).sort(function(a, b) {\n      return a.rank - b.rank || a.index - b.index;\n    }).map(function(entry) {\n      return entry.item;\n    });\n  }\n\n  function upcomingDates(item, byRequest) {\n    if (byRequest || !Array.isArray(item.dates)) return [];\n\n    var today = new Date();\n    today.setHours(0, 0, 0, 0);\n\n    return item.dates.map(function(date) {\n      var normalized = Object.assign({}, date);\n      normalized._start = parseApiDate(date.start);\n      normalized._end = parseApiDate(date.end) || normalized._start;\n      return normalized;\n    }).filter(function(date) {\n      return !date._end || date._end >= today;\n    }).sort(function(a, b) {\n      if (!a._start && !b._start) return 0;\n      if (!a._start) return 1;\n      if (!b._start) return -1;\n      return a._start - b._start;\n    });\n  }\n\n  function priceDetails(item, byRequest, copy) {\n    if (byRequest) return { main: copy.customDates, suffix: '' };\n    if (!Array.isArray(item.packages) || !item.packages.length) {\n      return { main: copy.pricingPage, suffix: '' };\n    }\n\n    var perPersonPrices = item.packages.map(function(packageItem) {\n      var price = Number(packageItem.price);\n      var people = Math.max(1, Number(packageItem.person) || 1);\n      return Number.isFinite(price) && price > 0 ? price \/ people : null;\n    }).filter(function(price) {\n      return price !== null;\n    });\n\n    if (!perPersonPrices.length) return { main: copy.pricingPage, suffix: '' };\n\n    var lowest = Math.round(Math.min.apply(Math, perPersonPrices));\n    return {\n      main: copy.fromPrice.replace('{price}', new Intl.NumberFormat(copy.locale, { maximumFractionDigits: 0 }).format(lowest)),\n      suffix: copy.perPerson\n    };\n  }\n\n  function appendLabelValue(parent, labelText, valueText) {\n    var line = createElement('div', 'vdrl__date-line');\n    line.appendChild(createElement('span', 'vdrl__meta-label', labelText));\n    line.appendChild(createElement('span', 'vdrl__date-value', valueText));\n    parent.appendChild(line);\n  }\n\n  function createCard(item, instanceId, cardIndex, copy) {\n    var profile = profileFor(item, copy);\n    var byRequest = profile.byRequest === true;\n    var dates = upcomingDates(item, byRequest);\n    var name = cleanText(item.name) || copy.retreatName;\n    var location = cleanText(item.location) || profile.tagFallback;\n    var linkUrl = safeUrl(item.url, false);\n    var imageUrl = safeUrl(item.image, true);\n    var summary = summarize(item.description, 210) || profile.summary || copy.summary;\n    var price = priceDetails(item, byRequest, copy);\n    var card = createElement('article', 'vdrl__card');\n    var media = createElement('div', 'vdrl__media');\n\n    card.style.setProperty('--vdrl-card-accent', profile.accent);\n\n    if (imageUrl) {\n      var image = createElement('img', 'vdrl__image');\n      image.src = imageUrl;\n      image.alt = name;\n      image.loading = 'lazy';\n      image.decoding = 'async';\n      image.addEventListener('error', function() {\n        media.classList.add('is-missing');\n        image.remove();\n      }, { once: true });\n      media.appendChild(image);\n    } else {\n      media.classList.add('is-missing');\n    }\n\n    var badgeText = byRequest\n      ? copy.byRequest\n      : dates.length > 1\n        ? copy.multipleDates\n        : dates.length === 1\n          ? copy.nextAvailable\n          : copy.datesPending;\n\n    media.appendChild(createElement('div', 'vdrl__badge', badgeText));\n    card.appendChild(media);\n    card.appendChild(createElement('div', 'vdrl__accent'));\n\n    var body = createElement('div', 'vdrl__body');\n    var top = createElement('div', 'vdrl__top');\n    top.appendChild(createElement('span', 'vdrl__tag', location));\n\n    var heading = createElement('h3', 'vdrl__title');\n    if (linkUrl) {\n      var headingLink = createElement('a', '', name);\n      headingLink.href = linkUrl;\n      heading.appendChild(headingLink);\n    } else {\n      heading.textContent = name;\n    }\n    top.appendChild(heading);\n\n    if (profile.chips.length) {\n      var chipRow = createElement('div', 'vdrl__chips');\n      profile.chips.forEach(function(chipText) {\n        chipRow.appendChild(createElement('span', 'vdrl__chip', chipText));\n      });\n      top.appendChild(chipRow);\n    }\n    body.appendChild(top);\n\n    var summaryBlock = createElement('div', 'vdrl__summary');\n    summaryBlock.appendChild(createElement('p', '', summary));\n    body.appendChild(summaryBlock);\n\n    if (profile.bestFor) {\n      var meta = createElement('div', 'vdrl__meta');\n      meta.appendChild(createElement('span', 'vdrl__meta-label', copy.bestFor));\n      meta.appendChild(createElement('p', 'vdrl__meta-value', profile.bestFor));\n      body.appendChild(meta);\n    }\n\n    var dateBlock = createElement('div', 'vdrl__dates');\n    if (byRequest) {\n      appendLabelValue(dateBlock, copy.availability, copy.availableByRequest);\n    } else if (dates.length) {\n      appendLabelValue(dateBlock, copy.nextDate, formatDateRange(dates[0], copy) + ' \u00b7 ' + availabilityText(dates[0], copy));\n\n      var earlyBirdNote = cleanText(item.earlyBirdNote);\n      if (earlyBirdNote) dateBlock.appendChild(createElement('p', 'vdrl__early-bird', earlyBirdNote));\n\n      if (dates.length > 1) {\n        var itemId = String(item.id || cardIndex).replace(\/[^a-zA-Z0-9_-]\/g, '');\n        var datePanelId = 'vdrl-dates-' + instanceId + '-' + itemId;\n        var extras = createElement('div', 'vdrl__extra-dates is-collapsed');\n        extras.id = datePanelId;\n        extras.setAttribute('aria-hidden', 'true');\n        var list = createElement('ul');\n\n        dates.slice(1).forEach(function(date) {\n          list.appendChild(createElement('li', '', formatDateRange(date, copy) + ' \u00b7 ' + availabilityText(date, copy)));\n        });\n\n        extras.appendChild(list);\n        dateBlock.appendChild(extras);\n\n        var toggleLabel = dates.length === 2 ? copy.otherDate : copy.otherDates;\n        var toggle = createElement('button', 'vdrl__dates-toggle', toggleLabel);\n        toggle.type = 'button';\n        toggle.setAttribute('aria-controls', datePanelId);\n        toggle.setAttribute('aria-expanded', 'false');\n        toggle.addEventListener('click', function() {\n          var isCollapsed = extras.classList.toggle('is-collapsed');\n          extras.setAttribute('aria-hidden', String(isCollapsed));\n          toggle.setAttribute('aria-expanded', String(!isCollapsed));\n          toggle.firstChild.nodeValue = isCollapsed ? toggleLabel : copy.hideDates;\n        });\n        dateBlock.appendChild(toggle);\n      }\n    } else {\n      appendLabelValue(dateBlock, copy.availability, copy.newDates);\n    }\n    body.appendChild(dateBlock);\n\n    var footer = createElement('div', 'vdrl__footer');\n    var priceElement = createElement('div', 'vdrl__price', price.main);\n    if (price.suffix) priceElement.appendChild(createElement('span', '', ' ' + price.suffix));\n    footer.appendChild(priceElement);\n\n    if (linkUrl) {\n      var button = createElement('a', 'vdrl__button', profile.buttonLabel);\n      button.href = linkUrl;\n      footer.appendChild(button);\n    }\n\n    body.appendChild(footer);\n    card.appendChild(body);\n    return card;\n  }\n\n  roots.forEach(function(root) {\n    if (root.dataset.vdrlReady === 'true') return;\n    root.dataset.vdrlReady = 'true';\n\n    window.__vdrlInstanceCounter = (window.__vdrlInstanceCounter || 0) + 1;\n    var instanceId = window.__vdrlInstanceCounter;\n    var grid = root.querySelector('[data-vdrl-grid]');\n    var status = root.querySelector('[data-vdrl-status]');\n    var endpoint = new URL(root.getAttribute('data-api-url') || '\/wp-json\/retreats\/v1\/all', window.location.href);\n    var language = String(endpoint.searchParams.get('lang') || document.documentElement.lang || 'en').toLowerCase().split(\/[-_]\/)[0];\n    if (language === 'vn') language = 'vi';\n    if (['en', 'vi', 'de'].indexOf(language) < 0) language = 'en';\n    endpoint.searchParams.set('lang', language);\n    var apiUrl = endpoint.href;\n    var copy = translations[language] || translations.en;\n    root.setAttribute('aria-label', copy.regionLabel);\n    grid.setAttribute('aria-label', copy.gridLabel);\n    var sharedApi = getSharedRetreatApi();\n    var apiKey = sharedApi.key(apiUrl);\n    var lastVersion = 0;\n\n    function showLoading() {\n      status.hidden = false;\n      status.className = 'vdrl__status';\n      status.setAttribute('role', 'status');\n      status.replaceChildren();\n      status.appendChild(createElement('span', 'vdrl__spinner'));\n      status.appendChild(createElement('span', '', copy.loading));\n      grid.hidden = false;\n      grid.setAttribute('aria-busy', 'true');\n      grid.innerHTML = '<article class=\"vdrl__skeleton\" aria-hidden=\"true\"><div class=\"vdrl__skeleton-image\"><\/div><div class=\"vdrl__skeleton-body\"><i><\/i><i><\/i><i><\/i><i><\/i><\/div><\/article><article class=\"vdrl__skeleton\" aria-hidden=\"true\"><div class=\"vdrl__skeleton-image\"><\/div><div class=\"vdrl__skeleton-body\"><i><\/i><i><\/i><i><\/i><i><\/i><\/div><\/article><article class=\"vdrl__skeleton\" aria-hidden=\"true\"><div class=\"vdrl__skeleton-image\"><\/div><div class=\"vdrl__skeleton-body\"><i><\/i><i><\/i><i><\/i><i><\/i><\/div><\/article>';\n    }\n\n    function showMessage(message, type, allowRetry) {\n      grid.replaceChildren();\n      grid.hidden = true;\n      grid.removeAttribute('aria-busy');\n      status.hidden = false;\n      status.className = 'vdrl__status vdrl__status--' + type;\n      status.setAttribute('role', type === 'error' ? 'alert' : 'status');\n      status.replaceChildren(createElement('p', '', message));\n\n      if (allowRetry) {\n        var retry = createElement('button', 'vdrl__retry', copy.retry);\n        retry.type = 'button';\n        retry.addEventListener('click', function() {\n          loadRetreats(true);\n        });\n        status.appendChild(retry);\n      }\n    }\n\n    function renderResult(result) {\n      if (!result || result.key !== apiKey || result.version === lastVersion) return;\n      lastVersion = result.version;\n      var items = orderItems(result.items || []);\n\n      if (!items.length) {\n        showMessage(copy.empty, 'empty', false);\n        return;\n      }\n\n      var fragment = document.createDocumentFragment();\n      items.forEach(function(item, index) {\n        fragment.appendChild(createCard(item || {}, instanceId, index, copy));\n      });\n\n      grid.replaceChildren(fragment);\n      grid.hidden = false;\n      grid.removeAttribute('aria-busy');\n      status.hidden = true;\n    }\n\n    function showLoadError(error) {\n      if (lastVersion) return;\n      if (error && error.vietnamDetoxTimedOut) {\n        showMessage(copy.timeout, 'error', true);\n        return;\n      }\n      showMessage(copy.error, 'error', true);\n    }\n\n    function loadRetreats(force) {\n      showLoading();\n\n      if (typeof window.fetch !== 'function') {\n        showMessage(copy.unsupported, 'error', false);\n        return;\n      }\n\n      sharedApi.load(apiUrl, force === true).then(renderResult).catch(showLoadError);\n    }\n\n    window.addEventListener('vietnamdetox:retreats-loaded', function(event) {\n      renderResult(event.detail);\n    });\n\n    loadRetreats(false);\n  });\n})();\n<\/script>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-ebf20ab e-con-full vbook__booking-card e-flex e-con e-parent\" data-id=\"ebf20ab\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-f127263 elementor-widget elementor-widget-html\" data-id=\"f127263\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!--\n  Elementor setup\n  1. Paste this file into an HTML widget inside the outer CTA container.\n  2. Add a separate Bookly Booking form or Elementor Shortcode widget in the\n     vrdc__booking-card inner container below this HTML widget.\n  3. Paste elementor-retreat-decision-cta.css into the OUTER container's\n     Advanced > Custom CSS field.\n\n  The HTML widget cannot run WordPress shortcodes. Use [bookly-form] only in\n  Elementor's Shortcode widget, not in this HTML widget.\n\n  Button links are language aware. Each button carries one data-vrdc-href-<lang>\n  attribute per language; the script picks the one matching <html lang>. Edit those\n  attributes rather than the href when a translated URL changes.\n-->\n\n<section class=\"vrdc\" data-vrdc=\"\" aria-labelledby=\"vrdc-title\">\n  <div class=\"vrdc__inner\">\n    <h2 class=\"vrdc__title\" id=\"vrdc-title\">B\u1ea1n kh\u00f4ng ch\u1eafc kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng n\u00e0o ph\u00f9 h\u1ee3p v\u1edbi m\u00ecnh?<\/h2>\n\n    <p class=\"vrdc__copy\">\n H\u00e3y kh\u00e1m ph\u00e1 c\u00e1c trang th\u00f4ng tin v\u1ec1 t\u1eebng ch\u01b0\u01a1ng tr\u00ecnh t\u0129nh t\u00e2m ho\u1eb7c \u0111\u1eb7t l\u1ecbch tr\u00f2 chuy\u1ec7n mi\u1ec5n ph\u00ed v\u1edbi Dieter tr\u01b0\u1edbc khi \u0111\u01b0a ra quy\u1ebft \u0111\u1ecbnh.\n    <\/p>\n\n    <div class=\"vrdc__buttons\">\n      <a href=\"https:\/\/uat.vietnamdetox.com\/vi\/cuoc-goi-tham-khao\/\" class=\"vrdc__button vrdc__button--primary\" data-vrdc-href-en=\"\/discovery-call\/\" data-vrdc-href-vi=\"\/vi\/cuoc-goi-tham-khao\/\">\u0110\u1eb7t m\u1ed9t cu\u1ed9c g\u1ecdi t\u01b0 v\u1ea5n C\u00e2u h\u1ecfi th\u01b0\u1eddng g\u1eb7p<\/a>\n\n<a href=\"https:\/\/uat.vietnamdetox.com\/vi\/cau-hoi-thuong-gap\/\" class=\"vrdc__button vrdc__button--outline\" data-vrdc-href-en=\"\/faq\/\" data-vrdc-href-vi=\"\/vi\/cau-hoi-thuong-gap\/\">FAQ<\/a>\n    <\/div>\n  <\/div>\n<\/section>\n\n<script>\n(function () {\n  'use strict';\n\n  var SUPPORTED = ['en', 'vi'];\n\n  function applyLanguage(root) {\n    if (root.dataset.vrdcReady === 'true') return;\n    root.dataset.vrdcReady = 'true';\n\n    var language = String(document.documentElement.lang || 'en')\n      .toLowerCase()\n      .split(\/[-_]\/)[0];\n\n    if (language === 'vn') language = 'vi';\n    if (SUPPORTED.indexOf(language) < 0) language = 'en';\n\n    var attribute = 'data-vrdc-href-' + language;\n\n    Array.prototype.forEach.call(\n      root.querySelectorAll('.vrdc__button'),\n      function (button) {\n        var href = button.getAttribute(attribute);\n        if (href) button.setAttribute('href', href);\n      }\n    );\n  }\n\n  function init() {\n    Array.prototype.forEach.call(document.querySelectorAll('[data-vrdc]'), applyLanguage);\n  }\n\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', init);\n  } else {\n    init();\n  }\n})();\n<\/script>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>\u0110ang t\u1ea3i h\u00ecnh \u1ea3nh kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng&#8230; \u1ea8n h\u1ed9p \u00d7 Vietnam Detox \u00b7 C\u00e1c kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng nh\u1ecbn \u0103n c\u00f3 h\u01b0\u1edbng d\u1eabn t\u1ea1i Vi\u1ec7t Nam C\u00e1c kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng d\u00e0nh cho nh\u1eefng ng\u01b0\u1eddi \u0111ang g\u00e1nh v\u00e1c qu\u00e1 nhi\u1ec1u. Nh\u1ecbn \u0103n theo ph\u01b0\u01a1ng ph\u00e1p Buchinger c\u00f3 h\u01b0\u1edbng d\u1eabn t\u1ea1i Vi\u1ec7t Nam \u2014 h\u00e3y so s\u00e1nh c\u00e1c [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_acf_changed":false,"footnotes":""},"class_list":["post-19086","page","type-page","status-publish","hentry"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.5 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Retreats - Vietnamdetox<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/uat.vietnamdetox.com\/vi\/tong-quan-ve-khoa-nghi-duong\/\" \/>\n<meta property=\"og:locale\" content=\"vi_VN\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Retreats - Vietnamdetox\" \/>\n<meta property=\"og:description\" content=\"\u0110ang t\u1ea3i h\u00ecnh \u1ea3nh kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng&#8230; \u1ea8n h\u1ed9p \u00d7 Vietnam Detox \u00b7 C\u00e1c kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng nh\u1ecbn \u0103n c\u00f3 h\u01b0\u1edbng d\u1eabn t\u1ea1i Vi\u1ec7t Nam C\u00e1c kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng d\u00e0nh cho nh\u1eefng ng\u01b0\u1eddi \u0111ang g\u00e1nh v\u00e1c qu\u00e1 nhi\u1ec1u. Nh\u1ecbn \u0103n theo ph\u01b0\u01a1ng ph\u00e1p Buchinger c\u00f3 h\u01b0\u1edbng d\u1eabn t\u1ea1i Vi\u1ec7t Nam \u2014 h\u00e3y so s\u00e1nh c\u00e1c [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/uat.vietnamdetox.com\/vi\/tong-quan-ve-khoa-nghi-duong\/\" \/>\n<meta property=\"og:site_name\" content=\"Vietnamdetox\" \/>\n<meta property=\"article:modified_time\" content=\"2026-09-26T02:25:26+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"\u01af\u1edbc t\u00ednh th\u1eddi gian \u0111\u1ecdc\" \/>\n\t<meta name=\"twitter:data1\" content=\"6 ph\u00fat\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/uat.vietnamdetox.com\\\/vi\\\/tong-quan-ve-khoa-nghi-duong\\\/\",\"url\":\"https:\\\/\\\/uat.vietnamdetox.com\\\/vi\\\/tong-quan-ve-khoa-nghi-duong\\\/\",\"name\":\"Retreats - Vietnamdetox\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/uat.vietnamdetox.com\\\/vi\\\/#website\"},\"datePublished\":\"2026-08-12T06:04:29+00:00\",\"dateModified\":\"2026-09-26T02:25:26+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/uat.vietnamdetox.com\\\/vi\\\/tong-quan-ve-khoa-nghi-duong\\\/#breadcrumb\"},\"inLanguage\":\"vi\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/uat.vietnamdetox.com\\\/vi\\\/tong-quan-ve-khoa-nghi-duong\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/uat.vietnamdetox.com\\\/vi\\\/tong-quan-ve-khoa-nghi-duong\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/uat.vietnamdetox.com\\\/vi\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Retreats\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/uat.vietnamdetox.com\\\/vi\\\/#website\",\"url\":\"https:\\\/\\\/uat.vietnamdetox.com\\\/vi\\\/\",\"name\":\"Staging Vietnamdetox\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/uat.vietnamdetox.com\\\/vi\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/uat.vietnamdetox.com\\\/vi\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"vi\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/uat.vietnamdetox.com\\\/vi\\\/#organization\",\"name\":\"Staging Vietnamdetox\",\"url\":\"https:\\\/\\\/uat.vietnamdetox.com\\\/vi\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"vi\",\"@id\":\"https:\\\/\\\/uat.vietnamdetox.com\\\/vi\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/uat.vietnamdetox.com\\\/wp-content\\\/uploads\\\/2026\\\/05\\\/logo-icon-color_cutout.svg\",\"contentUrl\":\"https:\\\/\\\/uat.vietnamdetox.com\\\/wp-content\\\/uploads\\\/2026\\\/05\\\/logo-icon-color_cutout.svg\",\"width\":314,\"height\":435,\"caption\":\"Staging Vietnamdetox\"},\"image\":{\"@id\":\"https:\\\/\\\/uat.vietnamdetox.com\\\/vi\\\/#\\\/schema\\\/logo\\\/image\\\/\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Retreats - Vietnamdetox","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/uat.vietnamdetox.com\/vi\/tong-quan-ve-khoa-nghi-duong\/","og_locale":"vi_VN","og_type":"article","og_title":"Retreats - Vietnamdetox","og_description":"\u0110ang t\u1ea3i h\u00ecnh \u1ea3nh kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng&#8230; \u1ea8n h\u1ed9p \u00d7 Vietnam Detox \u00b7 C\u00e1c kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng nh\u1ecbn \u0103n c\u00f3 h\u01b0\u1edbng d\u1eabn t\u1ea1i Vi\u1ec7t Nam C\u00e1c kh\u00f3a ngh\u1ec9 d\u01b0\u1ee1ng d\u00e0nh cho nh\u1eefng ng\u01b0\u1eddi \u0111ang g\u00e1nh v\u00e1c qu\u00e1 nhi\u1ec1u. Nh\u1ecbn \u0103n theo ph\u01b0\u01a1ng ph\u00e1p Buchinger c\u00f3 h\u01b0\u1edbng d\u1eabn t\u1ea1i Vi\u1ec7t Nam \u2014 h\u00e3y so s\u00e1nh c\u00e1c [&hellip;]","og_url":"https:\/\/uat.vietnamdetox.com\/vi\/tong-quan-ve-khoa-nghi-duong\/","og_site_name":"Vietnamdetox","article_modified_time":"2026-09-26T02:25:26+00:00","twitter_card":"summary_large_image","twitter_misc":{"\u01af\u1edbc t\u00ednh th\u1eddi gian \u0111\u1ecdc":"6 ph\u00fat"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/uat.vietnamdetox.com\/vi\/tong-quan-ve-khoa-nghi-duong\/","url":"https:\/\/uat.vietnamdetox.com\/vi\/tong-quan-ve-khoa-nghi-duong\/","name":"Retreats - Vietnamdetox","isPartOf":{"@id":"https:\/\/uat.vietnamdetox.com\/vi\/#website"},"datePublished":"2026-08-12T06:04:29+00:00","dateModified":"2026-09-26T02:25:26+00:00","breadcrumb":{"@id":"https:\/\/uat.vietnamdetox.com\/vi\/tong-quan-ve-khoa-nghi-duong\/#breadcrumb"},"inLanguage":"vi","potentialAction":[{"@type":"ReadAction","target":["https:\/\/uat.vietnamdetox.com\/vi\/tong-quan-ve-khoa-nghi-duong\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/uat.vietnamdetox.com\/vi\/tong-quan-ve-khoa-nghi-duong\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/uat.vietnamdetox.com\/vi\/"},{"@type":"ListItem","position":2,"name":"Retreats"}]},{"@type":"WebSite","@id":"https:\/\/uat.vietnamdetox.com\/vi\/#website","url":"https:\/\/uat.vietnamdetox.com\/vi\/","name":"Staging Vietnamdetox","description":"","publisher":{"@id":"https:\/\/uat.vietnamdetox.com\/vi\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/uat.vietnamdetox.com\/vi\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"vi"},{"@type":"Organization","@id":"https:\/\/uat.vietnamdetox.com\/vi\/#organization","name":"Staging Vietnamdetox","url":"https:\/\/uat.vietnamdetox.com\/vi\/","logo":{"@type":"ImageObject","inLanguage":"vi","@id":"https:\/\/uat.vietnamdetox.com\/vi\/#\/schema\/logo\/image\/","url":"https:\/\/uat.vietnamdetox.com\/wp-content\/uploads\/2026\/05\/logo-icon-color_cutout.svg","contentUrl":"https:\/\/uat.vietnamdetox.com\/wp-content\/uploads\/2026\/05\/logo-icon-color_cutout.svg","width":314,"height":435,"caption":"Staging Vietnamdetox"},"image":{"@id":"https:\/\/uat.vietnamdetox.com\/vi\/#\/schema\/logo\/image\/"}}]}},"_hostinger_reach_plugin_has_subscription_block":false,"_hostinger_reach_plugin_is_elementor":false,"_links":{"self":[{"href":"https:\/\/uat.vietnamdetox.com\/vi\/wp-json\/wp\/v2\/pages\/19086","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/uat.vietnamdetox.com\/vi\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/uat.vietnamdetox.com\/vi\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/uat.vietnamdetox.com\/vi\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/uat.vietnamdetox.com\/vi\/wp-json\/wp\/v2\/comments?post=19086"}],"version-history":[{"count":19,"href":"https:\/\/uat.vietnamdetox.com\/vi\/wp-json\/wp\/v2\/pages\/19086\/revisions"}],"predecessor-version":[{"id":21947,"href":"https:\/\/uat.vietnamdetox.com\/vi\/wp-json\/wp\/v2\/pages\/19086\/revisions\/21947"}],"wp:attachment":[{"href":"https:\/\/uat.vietnamdetox.com\/vi\/wp-json\/wp\/v2\/media?parent=19086"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}