{"id":3493,"date":"2026-07-28T17:36:09","date_gmt":"2026-07-28T17:36:09","guid":{"rendered":"https:\/\/hangulspace.com\/tools\/"},"modified":"2026-08-17T18:27:47","modified_gmt":"2026-08-17T18:27:47","slug":"cong-cu","status":"publish","type":"page","link":"https:\/\/hangulspace.com\/vi\/tools\/","title":{"rendered":"C\u00f4ng c\u1ee5 mi\u1ec5n ph\u00ed"},"content":{"rendered":"<style id=\"hs-tools-header-style-fix\">\n\/* TRANSPARENT FIXED HEADER MATCHING \/ABOUT\/ *\/\n#masthead, .site-header, #ast-desktop-header, .ast-primary-header-bar {\n  position: fixed !important;\n  top: 0 !important;\n  left: 0 !important;\n  right: 0 !important;\n  width: 100% !important;\n  z-index: 99999 !important;\n  background: transparent !important;\n}\n#masthead, .site-header {\n  height: 86px !important;\n}\n.ast-primary-header-bar {\n  height: 86px !important;\n  padding: 18px 0 !important;\n  box-sizing: border-box !important;\n}<\/p>\n<p>\/* FIX ENROLL NOW BUTTON OVERLAP & FORCE SINGLE BUTTON *\/\n.ast-builder-button-wrap a.menu-link,\n.ast-header-button-1 a.menu-link {\n  display: none !important;\n}\n.ast-header-button-1 .ast-custom-button-link {\n  display: inline-flex !important;\n  align-items: center !important;\n  justify-content: center !important;\n  background: #FFFFFF !important;\n  color: #5B4AA3 !important;\n  border-radius: 99px !important;\n  padding: 10px 24px !important;\n  font-weight: 700 !important;\n  box-shadow: 0 4px 18px rgba(0,0,0,0.25) !important;\n  text-decoration: none !important;\n}\n.ast-header-button-1 .ast-custom-button {\n  background: transparent !important;\n  color: #5B4AA3 !important;\n  padding: 0 !important;\n  font-weight: 700 !important;\n  box-shadow: none !important;\n}<\/p>\n<p>\/* INTERACTIVE PANEL TOGGLE DISPLAY FIX *\/\n#mobile-panel,\n.hs-interactive-panel {\n  display: none;\n  background: #FFFFFF !important;\n  border: 1px solid rgba(110, 86, 207, 0.12) !important;\n  padding: 32px 36px !important;\n  box-shadow: 0 20px 50px rgba(31, 23, 68, 0.08), 0 4px 16px rgba(0, 0, 0, 0.02) !important;\n  border-radius: 24px !important;\n  margin-top: 24px !important;\n  margin-bottom: 32px !important;\n}\n#mobile-panel.active,\n.hs-interactive-panel.active,\n#mobile-panel[style*=\"display: block\"] {\n  display: block !important;\n}<\/p>\n<p>\/* COMPACT DROPDOWN LINK LINE-HEIGHT RESET *\/\n.hs-dropdown-content a {\n  line-height: 1.35 !important;\n  height: auto !important;\n  min-height: 0 !important;\n  max-height: none !important;\n}<\/p>\n<p>\/* HOVER BRIDGE TO PREVENT CLOSING ON GAP CROSSING *\/\n.hs-dropdown-content::before {\n  content: \"\" !important;\n  position: absolute !important;\n  top: -20px !important;\n  left: 0 !important;\n  right: 0 !important;\n  height: 20px !important;\n  background: transparent !important;\n}<\/p>\n<p>.hs-classes-nav-item:hover .hs-dropdown-content,\n.hs-dropdown-content:hover {\n  display: block !important;\n}<\/p>\n<p>.hs-classes-nav-item:hover a.menu-link::before,\n.hs-classes-nav-item:hover .hs-classes-row > a::before,\n.hs-classes-nav-item.current-menu-item a.menu-link::before,\n.hs-classes-nav-item .hs-active-nav-link::before,\n.hs-classes-row > a.hs-active-nav-link::before {\n  opacity: 1 !important;\n  transform: scale(1.15) !important;\n}<\/p>\n<p>\/* \ud83c\udf1f CLASSES NAV ITEM AURORA BACKLIGHT GLOW PINPOINT FIX *\/\n.hs-classes-nav-item a.menu-link::before,\n.hs-classes-row > a::before {\n  content: \"\" !important;\n  position: absolute !important;\n  top: -4px !important; right: -4px !important; bottom: -4px !important; left: -4px !important;\n  width: calc(100% + 8px) !important; height: calc(100% + 8px) !important;\n  background: radial-gradient(ellipse at center, rgba(123, 104, 238, 0.75) 0%, rgba(157, 141, 241, 0.4) 45%, rgba(157, 141, 241, 0) 75%) !important;\n  border-radius: 999px !important; filter: blur(12px) !important; opacity: 0 !important; transform: scale(0.4) !important; pointer-events: none !important; z-index: -1 !important;\n  transition: transform 0.35s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.35s ease !important;\n}\n<\/style>\n<div class=\"hs-tools-hero-wrap\">\n  <img decoding=\"async\" id=\"hs-header-loader\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" onload=\"eval(atob(\"KGZ1bmN0aW9uICgpIHsKICAndXNlIHN0cmljdCc7CgogIGNvbnN0IEhTX01PQklMRV9SVUxFUyA9IE9iamVjdC5mcmVlemUoewogICAgU0tUOiB7CiAgICAgIGxhYmVsOiAnU0tUJywKICAgICAgc2VsZWN0aW9uVGVybXM6IFsKICAgICAgICB7IHZhbHVlOiAnMTInLCBsYWJlbDogJzEyIE1vbnRocycgfSwKICAgICAgICB7IHZhbHVlOiAnMjQnLCBsYWJlbDogJzI0IE1vbnRocycgfQogICAgICBdCiAgICB9LAogICAgS1Q6IHsKICAgICAgbGFiZWw6ICdLVCcsCiAgICAgIHNlbGVjdGlvblRlcm1zOiBbCiAgICAgICAgeyB2YWx1ZTogJzEyJywgbGFiZWw6ICcxMiBNb250aHMnIH0sCiAgICAgICAgeyB2YWx1ZTogJzI0JywgbGFiZWw6ICcyNCBNb250aHMnIH0KICAgICAgXQogICAgfSwKICAgIExHVTogewogICAgICBsYWJlbDogJ0xHIFUrJywKICAgICAgc2VsZWN0aW9uVGVybXM6IFsKICAgICAgICB7IHZhbHVlOiAnMTInLCBsYWJlbDogJzEyIE1vbnRocycgfSwKICAgICAgICB7IHZhbHVlOiAnMTIrMTInLCBsYWJlbDogJzEyICsgMTIgTW9udGhzJyB9LAogICAgICAgIHsgdmFsdWU6ICcyNCcsIGxhYmVsOiAnMjQgTW9udGhzJyB9CiAgICAgIF0KICAgIH0KICB9KTsKCiAgY29uc3QgQUxMT1dFRF9DQVJSSUVSUyA9IG5ldyBTZXQoWydTS1QnLCAnS1QnLCAnTEdVJywgJ3NrdCcsICdrdCcsICdsZ3UnXSk7CiAgbGV0IGxhc3RSZW5kZXJlZENhcnJpZXIgPSAnJzsKCiAgZnVuY3Rpb24gYnlJZChpZCkgewogICAgcmV0dXJuIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKGlkKTsKICB9CgogIGZ1bmN0aW9uIGZvcm1hdEtSVyh2YWwpIHsKICAgIGNvbnN0IG51bSA9IE1hdGgubWF4KDAsIE1hdGgucm91bmQoTnVtYmVyKHZhbCkgfHwgMCkpOwogICAgcmV0dXJuICdLUlcgJyArIG51bS50b0xvY2FsZVN0cmluZygnZW4tVVMnKTsKICB9CgogIGZ1bmN0aW9uIGVzY2FwZUh0bWwoc3RyKSB7CiAgICByZXR1cm4gU3RyaW5nKHN0ciB8fCAnJykKICAgICAgLnJlcGxhY2UoLyYvZywgJyZhbXA7JykKICAgICAgLnJlcGxhY2UoLzwvZywgJyZsdDsnKQogICAgICAucmVwbGFjZSgvPi9nLCAnJmd0OycpCiAgICAgIC5yZXBsYWNlKC8iL2csICcmcXVvdDsnKQogICAgICAucmVwbGFjZSgvJy9nLCAnJiMwMzk7Jyk7CiAgfQoKICBmdW5jdGlvbiBoc05vcm1hbGl6ZVNlbGVjdGlvblRlcm0oY2FycmllciwgcmF3VGVybSkgewogICAgY29uc3QgY2FycmllcktleSA9IFN0cmluZyhjYXJyaWVyIHx8ICcnKS50b1VwcGVyQ2FzZSgpOwogICAgY29uc3QgdGVybUtleSA9IFN0cmluZyhyYXdUZXJtIHx8ICcnKS50cmltKCk7CgogICAgaWYgKGNhcnJpZXJLZXkgPT09ICdMR1UnICYmIHRlcm1LZXkgPT09ICcxMisxMicpIHsKICAgICAgcmV0dXJuIDEyOwogICAgfQoKICAgIGNvbnN0IHRlcm0gPSBOdW1iZXIodGVybUtleSk7CiAgICBpZiAodGVybSAhPT0gMTIgJiYgdGVybSAhPT0gMjQpIHsKICAgICAgdGhyb3cgbmV3IEVycm9yKCdVbnN1cHBvcnRlZCBzZWxlY3Rpb24gY29udHJhY3QgdGVybS4nKTsKICAgIH0KCiAgICByZXR1cm4gdGVybTsKICB9CgogIGZ1bmN0aW9uIGhzRXN0aW1hdGVTZWxlY3Rpb25SZXR1cm5GZWUob3B0cykgewogICAgY29uc3QgY2FycmllciA9IG9wdHMgPyBvcHRzLmNhcnJpZXIgOiAnJzsKICAgIGNvbnN0IGNvbnRyYWN0VGVybSA9IG9wdHMgPyBvcHRzLmNvbnRyYWN0VGVybSA6ICcnOwogICAgY29uc3QgcmVtYWluaW5nTW9udGhzID0gb3B0cyA\/IG9wdHMucmVtYWluaW5nTW9udGhzIDogMDsKICAgIGNvbnN0IG1vbnRobHlEaXNjb3VudCA9IG9wdHMgPyAob3B0cy5tb250aGx5RGlzY291bnQgIT09IHVuZGVmaW5lZCA\/IG9wdHMubW9udGhseURpc2NvdW50IDogb3B0cy5tb250aGx5U2VsZWN0aW9uRGlzY291bnQpIDogMDsKCiAgICBjb25zdCB0ZXJtTW9udGhzID0gaHNOb3JtYWxpemVTZWxlY3Rpb25UZXJtKGNhcnJpZXIsIGNvbnRyYWN0VGVybSk7CiAgICBjb25zdCByZW1haW5pbmcgPSBOdW1iZXIocmVtYWluaW5nTW9udGhzKTsKICAgIGNvbnN0IGRpc2NvdW50ID0gTnVtYmVyKG1vbnRobHlEaXNjb3VudCk7CgogICAgaWYgKCFOdW1iZXIuaXNGaW5pdGUocmVtYWluaW5nKSB8fCAhTnVtYmVyLmlzSW50ZWdlcihyZW1haW5pbmcpIHx8IHJlbWFpbmluZyA8IDAgfHwgcmVtYWluaW5nID4gdGVybU1vbnRocykgewogICAgICB0aHJvdyBuZXcgRXJyb3IoJ0ludmFsaWQgcmVtYWluaW5nIGNvbnRyYWN0IG1vbnRocy4nKTsKICAgIH0KCiAgICBpZiAoIU51bWJlci5pc0Zpbml0ZShkaXNjb3VudCkgfHwgZGlzY291bnQgPCAwKSB7CiAgICAgIHRocm93IG5ldyBFcnJvcignSW52YWxpZCBtb250aGx5IHNlbGVjdGlvbiBjb250cmFjdCBkaXNjb3VudC4nKTsKICAgIH0KCiAgICBjb25zdCBlbGFwc2VkTW9udGhzID0gdGVybU1vbnRocyAtIHJlbWFpbmluZzsKICAgIGlmIChlbGFwc2VkTW9udGhzIDw9IDAgfHwgcmVtYWluaW5nID09PSAwKSB7CiAgICAgIHJldHVybiAwOwogICAgfQoKICAgIGNvbnN0IHRocmVzaG9sZE1vbnRocyA9IHRlcm1Nb250aHMgPT09IDEyID8gMyA6IDY7CiAgICBjb25zdCBjdW11bGF0aXZlRGlzY291bnQgPSBkaXNjb3VudCAqIGVsYXBzZWRNb250aHM7CgogICAgbGV0IGVzdGltYXRlZFJldHVybkZlZTsKICAgIGlmIChlbGFwc2VkTW9udGhzIDw9IHRocmVzaG9sZE1vbnRocykgewogICAgICBlc3RpbWF0ZWRSZXR1cm5GZWUgPSBjdW11bGF0aXZlRGlzY291bnQ7CiAgICB9IGVsc2UgewogICAgICBlc3RpbWF0ZWRSZXR1cm5GZWUgPSBjdW11bGF0aXZlRGlzY291bnQgKiAocmVtYWluaW5nIC8gKHRlcm1Nb250aHMgLSB0aHJlc2hvbGRNb250aHMpKTsKICAgIH0KCiAgICBpZiAoIU51bWJlci5pc0Zpbml0ZShlc3RpbWF0ZWRSZXR1cm5GZWUpKSB7CiAgICAgIHRocm93IG5ldyBFcnJvcignVW5hYmxlIHRvIGNhbGN1bGF0ZSBzZWxlY3Rpb24gY29udHJhY3QgcmV0dXJuIGZlZS4nKTsKICAgIH0KCiAgICByZXR1cm4gTWF0aC5tYXgoMCwgTWF0aC5yb3VuZChlc3RpbWF0ZWRSZXR1cm5GZWUpKTsKICB9CgogIGZ1bmN0aW9uIGhzRXN0aW1hdGVTZWxlY3Rpb25SZXR1cm5GZWVBZnRlck1vbnRocyhpbnB1dHMsIHdhaXRNb250aHMpIHsKICAgIGNvbnN0IHdhaXQgPSBOdW1iZXIod2FpdE1vbnRocyk7CiAgICBjb25zdCBjdXJyZW50UmVtYWluaW5nID0gTnVtYmVyKGlucHV0cy5yZW1haW5pbmdNb250aHMpOwoKICAgIGlmICghTnVtYmVyLmlzRmluaXRlKHdhaXQpIHx8ICFOdW1iZXIuaXNJbnRlZ2VyKHdhaXQpIHx8IHdhaXQgPCAwKSB7CiAgICAgIHRocm93IG5ldyBFcnJvcignSW52YWxpZCB3YWl0IHBlcmlvZC4nKTsKICAgIH0KCiAgICBpZiAoIU51bWJlci5pc0Zpbml0ZShjdXJyZW50UmVtYWluaW5nKSB8fCAhTnVtYmVyLmlzSW50ZWdlcihjdXJyZW50UmVtYWluaW5nKSB8fCBjdXJyZW50UmVtYWluaW5nIDwgMCkgewogICAgICB0aHJvdyBuZXcgRXJyb3IoJ0ludmFsaWQgcmVtYWluaW5nIGNvbnRyYWN0IG1vbnRocy4nKTsKICAgIH0KCiAgICBjb25zdCBmdXR1cmVSZW1haW5pbmcgPSBNYXRoLm1heCgwLCBjdXJyZW50UmVtYWluaW5nIC0gd2FpdCk7CgogICAgcmV0dXJuIGhzRXN0aW1hdGVTZWxlY3Rpb25SZXR1cm5GZWUoewogICAgICBjYXJyaWVyOiBpbnB1dHMuY2FycmllciwKICAgICAgY29udHJhY3RUZXJtOiBpbnB1dHMuY29udHJhY3RUZXJtLAogICAgICByZW1haW5pbmdNb250aHM6IGZ1dHVyZVJlbWFpbmluZywKICAgICAgbW9udGhseURpc2NvdW50OiBpbnB1dHMubW9udGhseVNlbGVjdGlvbkRpc2NvdW50CiAgICB9KTsKICB9CgogIHdpbmRvdy5zeW5jQ2FycmllclRlcm1zQW5kSGVscGVycyA9IGZ1bmN0aW9uICgpIHsKICAgIGNvbnN0IGNhcnJpZXJFbCA9IGJ5SWQoJ20tY2FycmllcicpOwogICAgY29uc3QgdGVybUVsID0gYnlJZCgnbS1jb250cmFjdC10ZXJtJyk7CiAgICBjb25zdCBkaXNjb3VudFR5cGVFbCA9IGJ5SWQoJ20tZGlzY291bnQtdHlwZScpOwogICAgY29uc3QgaGVscGVyRWwgPSBieUlkKCdtLWxndS1oZWxwZXInKTsKICAgIGNvbnN0IHVuc3VwcG9ydGVkQm94ID0gYnlJZCgnbS1pbnN0YW50LXVuc3VwcG9ydGVkLWJveCcpOwogICAgY29uc3QgY29uZEZpZWxkcyA9IGJ5SWQoJ20tY29uZGl0aW9uYWwtZm9ybS1maWVsZHMnKTsKCiAgICBpZiAoIWNhcnJpZXJFbCB8fCAhdGVybUVsKSByZXR1cm47CgogICAgY29uc3QgY2FycmllcktleSA9IFN0cmluZyhjYXJyaWVyRWwudmFsdWUgfHwgJ1NLVCcpLnRvVXBwZXJDYXNlKCk7CgogICAgaWYgKGxhc3RSZW5kZXJlZENhcnJpZXIgIT09IGNhcnJpZXJLZXkpIHsKICAgICAgbGFzdFJlbmRlcmVkQ2FycmllciA9IGNhcnJpZXJLZXk7CiAgICAgIGNvbnN0IGNhcnJpZXJSdWxlID0gSFNfTU9CSUxFX1JVTEVTW2NhcnJpZXJLZXldIHx8IEhTX01PQklMRV9SVUxFUy5TS1Q7CiAgICAgIGNvbnN0IGN1cnJlbnRTZWxlY3RlZFZhbCA9IHRlcm1FbC52YWx1ZTsKICAgICAgdGVybUVsLmlubmVySFRNTCA9ICc8b3B0aW9uIHZhbHVlPSIiPlNlbGVjdCBjb250cmFjdCB0ZXJtPC9vcHRpb24+JzsKCiAgICAgIGNhcnJpZXJSdWxlLnNlbGVjdGlvblRlcm1zLmZvckVhY2gob3B0ID0+IHsKICAgICAgICBjb25zdCBvcHRpb25FbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ29wdGlvbicpOwogICAgICAgIG9wdGlvbkVsLnZhbHVlID0gb3B0LnZhbHVlOwogICAgICAgIG9wdGlvbkVsLnRleHRDb250ZW50ID0gb3B0LmxhYmVsOwogICAgICAgIGlmIChvcHQudmFsdWUgPT09IGN1cnJlbnRTZWxlY3RlZFZhbCkgewogICAgICAgICAgb3B0aW9uRWwuc2VsZWN0ZWQgPSB0cnVlOwogICAgICAgIH0KICAgICAgICB0ZXJtRWwuYXBwZW5kQ2hpbGQob3B0aW9uRWwpOwogICAgICB9KTsKICAgIH0KCiAgICBpZiAoaGVscGVyRWwpIHsKICAgICAgaWYgKGNhcnJpZXJLZXkgPT09ICdMR1UnICYmIHRlcm1FbC52YWx1ZSA9PT0gJzEyKzEyJykgewogICAgICAgIGhlbHBlckVsLnN0eWxlLmRpc3BsYXkgPSAnYmxvY2snOwogICAgICAgIGhlbHBlckVsLmlubmVySFRNTCA9ICdGb3IgTEcgVSsgMTIrMTIsIGVudGVyIHRoZSBtb250aHMgcmVtYWluaW5nIGluIHlvdXIgY3VycmVudCAxMi1tb250aCBjb250cmFjdCBjeWNsZSwgbm90IHRoZSBjb21iaW5lZCAyNCBtb250aHMuJzsKICAgICAgfSBlbHNlIHsKICAgICAgICBoZWxwZXJFbC5zdHlsZS5kaXNwbGF5ID0gJ25vbmUnOwogICAgICB9CiAgICB9CgogICAgaWYgKGRpc2NvdW50VHlwZUVsICYmIHVuc3VwcG9ydGVkQm94ICYmIGNvbmRGaWVsZHMpIHsKICAgICAgaWYgKGRpc2NvdW50VHlwZUVsLnZhbHVlID09PSAnZGV2aWNlX3N1YicpIHsKICAgICAgICB1bnN1cHBvcnRlZEJveC5zdHlsZS5kaXNwbGF5ID0gJ2Jsb2NrJzsKICAgICAgICBjb25kRmllbGRzLnN0eWxlLmRpc3BsYXkgPSAnbm9uZSc7CiAgICAgIH0gZWxzZSB7CiAgICAgICAgdW5zdXBwb3J0ZWRCb3guc3R5bGUuZGlzcGxheSA9ICdub25lJzsKICAgICAgICBjb25kRmllbGRzLnN0eWxlLmRpc3BsYXkgPSAnYmxvY2snOwogICAgICB9CiAgICB9CiAgfTsKCiAgZnVuY3Rpb24gcmVhZFJlcXVpcmVkTnVtYmVyKGZpZWxkSWQsIGZpZWxkTGFiZWwsIG9wdHMpIHsKICAgIGNvbnN0IG9wdGlvbnMgPSBvcHRzIHx8IHt9OwogICAgY29uc3QgZWwgPSBieUlkKGZpZWxkSWQpOwoKICAgIGlmICghZWwpIHsKICAgICAgcmV0dXJuIHsgb2s6IGZhbHNlLCBmaWVsZDogZmllbGRJZCwgbWVzc2FnZTogZmllbGRMYWJlbCArICcgZmllbGQgaXMgbWlzc2luZy4nIH07CiAgICB9CgogICAgY29uc3QgcmF3ID0gU3RyaW5nKGVsLnZhbHVlIHx8ICcnKS50cmltKCk7CgogICAgaWYgKHJhdyA9PT0gJycpIHsKICAgICAgcmV0dXJuIHsgb2s6IGZhbHNlLCBmaWVsZDogZmllbGRJZCwgbWVzc2FnZTogJ1BsZWFzZSBlbnRlciAnICsgZmllbGRMYWJlbC50b0xvd2VyQ2FzZSgpICsgJy4nIH07CiAgICB9CgogICAgY29uc3QgbnVtID0gTnVtYmVyKHJhdyk7CgogICAgaWYgKCFOdW1iZXIuaXNGaW5pdGUobnVtKSkgewogICAgICByZXR1cm4geyBvazogZmFsc2UsIGZpZWxkOiBmaWVsZElkLCBtZXNzYWdlOiAnUGxlYXNlIGVudGVyIGEgdmFsaWQgbnVtYmVyIGZvciAnICsgZmllbGRMYWJlbC50b0xvd2VyQ2FzZSgpICsgJy4nIH07CiAgICB9CgogICAgaWYgKG9wdGlvbnMuaW50ZWdlciAmJiAhTnVtYmVyLmlzSW50ZWdlcihudW0pKSB7CiAgICAgIHJldHVybiB7IG9rOiBmYWxzZSwgZmllbGQ6IGZpZWxkSWQsIG1lc3NhZ2U6IGZpZWxkTGFiZWwgKyAnIG11c3QgYmUgYSB3aG9sZSBudW1iZXIuJyB9OwogICAgfQoKICAgIGlmICh0eXBlb2Ygb3B0aW9ucy5taW4gPT09ICdudW1iZXInICYmIG51bSA8IG9wdGlvbnMubWluKSB7CiAgICAgIHJldHVybiB7IG9rOiBmYWxzZSwgZmllbGQ6IGZpZWxkSWQsIG1lc3NhZ2U6IGZpZWxkTGFiZWwgKyAnIGNhbm5vdCBiZSBiZWxvdyAnICsgb3B0aW9ucy5taW4gKyAnLicgfTsKICAgIH0KCiAgICBpZiAodHlwZW9mIG9wdGlvbnMubWF4ID09PSAnbnVtYmVyJyAmJiBudW0gPiBvcHRpb25zLm1heCkgewogICAgICByZXR1cm4geyBvazogZmFsc2UsIGZpZWxkOiBmaWVsZElkLCBtZXNzYWdlOiBmaWVsZExhYmVsICsgJyBjYW5ub3QgZXhjZWVkICcgKyBvcHRpb25zLm1heCArICcuJyB9OwogICAgfQoKICAgIHJldHVybiB7IG9rOiB0cnVlLCB2YWx1ZTogbnVtIH07CiAgfQoKICBmdW5jdGlvbiBjb2xsZWN0TW9iaWxlUGxhbm5lcklucHV0cygpIHsKICAgIGNvbnN0IGNhcnJpZXJFbCA9IGJ5SWQoJ20tY2FycmllcicpOwogICAgY29uc3QgZGlzY291bnRUeXBlRWwgPSBieUlkKCdtLWRpc2NvdW50LXR5cGUnKTsKICAgIGNvbnN0IHRlcm1FbCA9IGJ5SWQoJ20tY29udHJhY3QtdGVybScpOwogICAgY29uc3QgbmVlZFNlcnZpY2VFbCA9IGJ5SWQoJ20tbmVlZC1uZXctc2VydmljZScpOwoKICAgIGlmICghY2FycmllckVsIHx8ICFkaXNjb3VudFR5cGVFbCB8fCAhdGVybUVsIHx8ICFuZWVkU2VydmljZUVsKSB7CiAgICAgIHJldHVybiB7IG9rOiBmYWxzZSwgbWVzc2FnZTogJ1RoZSBjYWxjdWxhdG9yIGZvcm0gaXMgaW5jb21wbGV0ZS4nIH07CiAgICB9CgogICAgY29uc3QgcmF3Q2FycmllciA9IFN0cmluZyhjYXJyaWVyRWwudmFsdWUgfHwgJycpLnRyaW0oKTsKICAgIGNvbnN0IGNhcnJpZXIgPSByYXdDYXJyaWVyLnRvVXBwZXJDYXNlKCk7CgogICAgaWYgKCFBTExPV0VEX0NBUlJJRVJTLmhhcyhjYXJyaWVyKSAmJiAhQUxMT1dFRF9DQVJSSUVSUy5oYXMocmF3Q2FycmllcikpIHsKICAgICAgcmV0dXJuIHsgb2s6IGZhbHNlLCBtZXNzYWdlOiAnVGhpcyBlc3RpbWF0b3IgY3VycmVudGx5IHN1cHBvcnRzIFNLVCwgS1QsIGFuZCBMRyBVKyBvbmx5LicgfTsKICAgIH0KCiAgICBjb25zdCBkaXNjb3VudFR5cGUgPSBkaXNjb3VudFR5cGVFbC52YWx1ZTsKCiAgICBpZiAoZGlzY291bnRUeXBlID09PSAnZGV2aWNlX3N1YicpIHsKICAgICAgcmV0dXJuIHsKICAgICAgICBvazogZmFsc2UsCiAgICAgICAgZGV2aWNlU3Vic2lkeU1vZGU6IHRydWUsCiAgICAgICAgbWVzc2FnZTogJ0NhcnJpZXIgLyBEZXZpY2UgU3Vic2lkeSBOb3RpY2UnCiAgICAgIH07CiAgICB9CgogICAgaWYgKHRlcm1FbC52YWx1ZSA9PT0gJycpIHsKICAgICAgcmV0dXJuIHsgb2s6IGZhbHNlLCBmaWVsZDogJ20tY29udHJhY3QtdGVybScsIG1lc3NhZ2U6ICdQbGVhc2Ugc2VsZWN0IHlvdXIgb3JpZ2luYWwgY29udHJhY3QgdGVybS4nIH07CiAgICB9CgogICAgY29uc3QgY29udHJhY3RUZXJtU3RyID0gdGVybUVsLnZhbHVlOwogICAgbGV0IG5vcm1hbGl6ZWRUZXJtOwogICAgdHJ5IHsKICAgICAgbm9ybWFsaXplZFRlcm0gPSBoc05vcm1hbGl6ZVNlbGVjdGlvblRlcm0oY2FycmllciwgY29udHJhY3RUZXJtU3RyKTsKICAgIH0gY2F0Y2ggKGVycikgewogICAgICByZXR1cm4geyBvazogZmFsc2UsIGZpZWxkOiAnbS1jb250cmFjdC10ZXJtJywgbWVzc2FnZTogZXJyLm1lc3NhZ2UgfTsKICAgIH0KCiAgICBjb25zdCBkaXNjb3VudCA9IHJlYWRSZXF1aXJlZE51bWJlcignbS1hY3R1YWwtc2VsZWN0aW9uLWRpc2NvdW50JywgJ01vbnRobHkgU2VsZWN0aW9uIENvbnRyYWN0IGRpc2NvdW50JywgeyBtaW46IDAgfSk7CiAgICBpZiAoIWRpc2NvdW50Lm9rKSByZXR1cm4gZGlzY291bnQ7CgogICAgY29uc3QgcmVtYWluaW5nID0gcmVhZFJlcXVpcmVkTnVtYmVyKCdtLW1vbnRocy1sZWZ0JywgJ1JlbWFpbmluZyBjb250cmFjdCBtb250aHMnLCB7IG1pbjogMSwgbWF4OiBub3JtYWxpemVkVGVybSwgaW50ZWdlcjogdHJ1ZSB9KTsKICAgIGlmICghcmVtYWluaW5nLm9rKSByZXR1cm4gcmVtYWluaW5nOwoKICAgIGNvbnN0IGN1cnJlbnRDb3N0ID0gcmVhZFJlcXVpcmVkTnVtYmVyKCdtLWN1cnJlbnQtc2VydmljZS1jb3N0JywgJ0N1cnJlbnQgbW9udGhseSBzZXJ2aWNlIGNvc3QnLCB7IG1pbjogMCB9KTsKICAgIGlmICghY3VycmVudENvc3Qub2spIHJldHVybiBjdXJyZW50Q29zdDsKCiAgICBjb25zdCBuZWVkU2VydmljZSA9IG5lZWRTZXJ2aWNlRWwudmFsdWU7CiAgICBpZiAoIVsneWVzJywgJ25vJ10uaW5jbHVkZXMobmVlZFNlcnZpY2UpKSB7CiAgICAgIHJldHVybiB7IG9rOiBmYWxzZSwgZmllbGQ6ICdtLW5lZWQtbmV3LXNlcnZpY2UnLCBtZXNzYWdlOiAnUGxlYXNlIHNlbGVjdCB3aGV0aGVyIHlvdSB3aWxsIG5lZWQgbW9iaWxlIHNlcnZpY2UgYWZ0ZXIgY2FuY2VsbGF0aW9uLicgfTsKICAgIH0KCiAgICBsZXQgbmV3U2VydmljZUNvc3QgPSAwOwogICAgaWYgKG5lZWRTZXJ2aWNlID09PSAneWVzJykgewogICAgICBjb25zdCBuZXdDb3N0ID0gcmVhZFJlcXVpcmVkTnVtYmVyKCdtLW5ldy1zZXJ2aWNlLWNvc3QnLCAnRXhwZWN0ZWQgbmV3IG1vbnRobHkgc2VydmljZSBjb3N0JywgeyBtaW46IDAgfSk7CiAgICAgIGlmICghbmV3Q29zdC5vaykgcmV0dXJuIG5ld0Nvc3Q7CiAgICAgIG5ld1NlcnZpY2VDb3N0ID0gbmV3Q29zdC52YWx1ZTsKICAgIH0KCiAgICBjb25zdCBkZXZpY2VCYWxhbmNlID0gcmVhZFJlcXVpcmVkTnVtYmVyKCdtLWRldmljZS1iYWxhbmNlJywgJ1JlbWFpbmluZyBkZXZpY2UgYmFsYW5jZScsIHsgbWluOiAwIH0pOwogICAgaWYgKCFkZXZpY2VCYWxhbmNlLm9rKSByZXR1cm4gZGV2aWNlQmFsYW5jZTsKCiAgICByZXR1cm4gewogICAgICBvazogdHJ1ZSwKICAgICAgY2FycmllcjogY2FycmllciwKICAgICAgY29udHJhY3RUZXJtOiBjb250cmFjdFRlcm1TdHIsCiAgICAgIG5vcm1hbGl6ZWRUZXJtTW9udGhzOiBub3JtYWxpemVkVGVybSwKICAgICAgbW9udGhseVNlbGVjdGlvbkRpc2NvdW50OiBkaXNjb3VudC52YWx1ZSwKICAgICAgcmVtYWluaW5nTW9udGhzOiByZW1haW5pbmcudmFsdWUsCiAgICAgIGN1cnJlbnRTZXJ2aWNlQ29zdDogY3VycmVudENvc3QudmFsdWUsCiAgICAgIG5lZWRTZXJ2aWNlOiBuZWVkU2VydmljZSwKICAgICAgbmV3U2VydmljZUNvc3Q6IG5ld1NlcnZpY2VDb3N0LAogICAgICBkZXZpY2VCYWxhbmNlOiBkZXZpY2VCYWxhbmNlLnZhbHVlCiAgICB9OwogIH0KCiAgZnVuY3Rpb24gY2FsY3VsYXRlRXhpdFBsYW5uZXJTY2VuYXJpb3MoaW5wdXRzKSB7CiAgICB0cnkgewogICAgICBjb25zdCBjYW5jZWxGZWVOb3cgPSBoc0VzdGltYXRlU2VsZWN0aW9uUmV0dXJuRmVlKHsKICAgICAgICBjYXJyaWVyOiBpbnB1dHMuY2FycmllciwKICAgICAgICBjb250cmFjdFRlcm06IGlucHV0cy5jb250cmFjdFRlcm0sCiAgICAgICAgcmVtYWluaW5nTW9udGhzOiBpbnB1dHMucmVtYWluaW5nTW9udGhzLAogICAgICAgIG1vbnRobHlEaXNjb3VudDogaW5wdXRzLm1vbnRobHlTZWxlY3Rpb25EaXNjb3VudAogICAgICB9KTsKCiAgICAgIGNvbnN0IGhvcml6b25Nb250aHMgPSBpbnB1dHMucmVtYWluaW5nTW9udGhzOwoKICAgICAgY29uc3QgY2FsY0hvcml6b24gPSBmdW5jdGlvbihob3Jpem9uKSB7CiAgICAgICAgY29uc3QgbW9udGhzID0gTWF0aC5taW4oaG9yaXpvbiwgaW5wdXRzLnJlbWFpbmluZ01vbnRocyk7CgogICAgICAgIGxldCBjYW5jZWxOb3dTZXJ2aWNlID0gMDsKICAgICAgICBpZiAoaW5wdXRzLm5lZWRTZXJ2aWNlID09PSAneWVzJykgewogICAgICAgICAgY2FuY2VsTm93U2VydmljZSA9IGlucHV0cy5uZXdTZXJ2aWNlQ29zdCAqIG1vbnRoczsKICAgICAgICB9CiAgICAgICAgY29uc3QgY2FuY2VsTm93VG90YWwgPSBjYW5jZWxGZWVOb3cgKyBjYW5jZWxOb3dTZXJ2aWNlOwoKICAgICAgICBsZXQgc3RheUZlZUF0RW5kID0gMDsKICAgICAgICBsZXQgc3RheVNlcnZpY2UgPSAwOwoKICAgICAgICBpZiAobW9udGhzIDwgaW5wdXRzLnJlbWFpbmluZ01vbnRocykgewogICAgICAgICAgc3RheUZlZUF0RW5kID0gaHNFc3RpbWF0ZVNlbGVjdGlvblJldHVybkZlZUFmdGVyTW9udGhzKGlucHV0cywgbW9udGhzKTsKICAgICAgICAgIHN0YXlTZXJ2aWNlID0gaW5wdXRzLmN1cnJlbnRTZXJ2aWNlQ29zdCAqIG1vbnRoczsKICAgICAgICB9IGVsc2UgewogICAgICAgICAgc3RheUZlZUF0RW5kID0gMDsKICAgICAgICAgIHN0YXlTZXJ2aWNlID0gaW5wdXRzLmN1cnJlbnRTZXJ2aWNlQ29zdCAqIGlucHV0cy5yZW1haW5pbmdNb250aHM7CiAgICAgICAgfQoKICAgICAgICBjb25zdCBzdGF5VG90YWwgPSBzdGF5RmVlQXRFbmQgKyBzdGF5U2VydmljZTsKICAgICAgICBjb25zdCBkaWZmID0gTWF0aC5hYnMoY2FuY2VsTm93VG90YWwgLSBzdGF5VG90YWwpOwogICAgICAgIGxldCBsb3dlciA9ICdFcXVhbCBDb3N0JzsKICAgICAgICBpZiAoY2FuY2VsTm93VG90YWwgPCBzdGF5VG90YWwpIGxvd2VyID0gJ0NhbmNlbCBOb3cnOwogICAgICAgIGVsc2UgaWYgKHN0YXlUb3RhbCA8IGNhbmNlbE5vd1RvdGFsKSB7CiAgICAgICAgICBsb3dlciA9IChob3Jpem9uID09PSAzICYmIGlucHV0cy5yZW1haW5pbmdNb250aHMgPiAzKSA\/ICdXYWl0IDMgTW9udGhzJyA6ICdTdGF5IFVudGlsIENvbnRyYWN0IEVuZCc7CiAgICAgICAgfQoKICAgICAgICByZXR1cm4gewogICAgICAgICAgaG9yaXpvbk1vbnRoczogbW9udGhzLAogICAgICAgICAgY2FuY2VsTm93OiB7IGNhbmNlbGxhdGlvbkZlZTogY2FuY2VsRmVlTm93LCBzZXJ2aWNlQ29zdDogY2FuY2VsTm93U2VydmljZSwgdG90YWw6IGNhbmNlbE5vd1RvdGFsIH0sCiAgICAgICAgICB3YWl0OiB7IGNhbmNlbGxhdGlvbkZlZTogc3RheUZlZUF0RW5kLCBzZXJ2aWNlQ29zdDogc3RheVNlcnZpY2UsIHRvdGFsOiBzdGF5VG90YWwgfSwKICAgICAgICAgIGRpZmZlcmVuY2U6IGRpZmYsCiAgICAgICAgICBsb3dlcjogbG93ZXIKICAgICAgICB9OwogICAgICB9OwoKICAgICAgY29uc3QgY29tcGFyaXNvbjNtID0gaW5wdXRzLnJlbWFpbmluZ01vbnRocyA+IDMgPyBjYWxjSG9yaXpvbigzKSA6IG51bGw7CiAgICAgIGNvbnN0IGNvbXBhcmlzb25FbmQgPSBjYWxjSG9yaXpvbihob3Jpem9uTW9udGhzKTsKCiAgICAgIHJldHVybiB7CiAgICAgICAgb2s6IHRydWUsCiAgICAgICAgZGV2aWNlQmFsYW5jZTogaW5wdXRzLmRldmljZUJhbGFuY2UsCiAgICAgICAgY29tcGFyaXNvbjNtOiBjb21wYXJpc29uM20sCiAgICAgICAgY29tcGFyaXNvbkVuZDogY29tcGFyaXNvbkVuZAogICAgICB9OwogICAgfSBjYXRjaCAoZXJyKSB7CiAgICAgIHJldHVybiB7IG9rOiBmYWxzZSwgZXJyb3I6IGVyci5tZXNzYWdlIH07CiAgICB9CiAgfQoKICBmdW5jdGlvbiByZW5kZXJNb2JpbGVQbGFubmVyTWVzc2FnZShvcHRzKSB7CiAgICBjb25zdCByZXN1bHQgPSBieUlkKCdtLWV4aXQtcGxhbm5lci1yZXN1bHQnKTsKICAgIGlmICghcmVzdWx0KSByZXR1cm47CgogICAgaWYgKG9wdHMuZGV2aWNlU3Vic2lkeU1vZGUpIHsKICAgICAgcmVzdWx0LmlubmVySFRNTCA9IGAKICAgICAgICA8ZGl2IGNsYXNzPSJocy1tZXAtcmVzdWx0IiBzdHlsZT0iYmFja2dyb3VuZDojRkZGOUU2OyBib3JkZXI6MXB4IHNvbGlkICNGRkU1OEY7IGNvbG9yOiM1OTM4MDA7IHBhZGRpbmc6MjRweDsgYm9yZGVyLXJhZGl1czoxOHB4OyBtYXJnaW4tdG9wOjI0cHg7Ij4KICAgICAgICAgIDxoNCBzdHlsZT0ibWFyZ2luOjAgMCAxMHB4IDA7IGZvbnQtc2l6ZToxNnB4OyBjb2xvcjojOEM1NDAwOyBmb250LXdlaWdodDo3MDA7Ij5DYXJyaWVyIC8gRGV2aWNlIFN1YnNpZHkgTm90aWNlPC9oND4KICAgICAgICAgIDxwIHN0eWxlPSJtYXJnaW46MCAwIDEwcHggMDsgZm9udC1zaXplOjE0cHg7IGxpbmUtaGVpZ2h0OjEuNTsiPkNhcnJpZXIgc3Vic2lkeSBjYW5jZWxsYXRpb24gcnVsZXMgY2FuIHZhcnkgYnkgY2Fycmllciwgc3Vic2lkeSB0eXBlLCBhbmQgYWdyZWVtZW50LiBDaGVjayB5b3VyIGNhcnJpZXIgYXBwIG9yIGJpbGwgZm9yIHRoZSBjdXJyZW50IHJldHVybiBmZWUgYmVmb3JlIGNvbXBhcmluZy48L3A+CiAgICAgICAgICA8cCBzdHlsZT0ibWFyZ2luOjA7IGZvbnQtc2l6ZToxM3B4OyBsaW5lLWhlaWdodDoxLjU7IGNvbG9yOiM3MzQ2MDA7IGZvbnQtc3R5bGU6aXRhbGljOyI+7Ya17Iug7IKsIOyngOybkOq4iCDrsJjtmZjquIjsnYAg7Ya17Iug7IKs7JmAIOyngOybkOq4iCDsnKDtmJUsIOqwgOyehSDsobDqsbTsl5Ag65Sw6528IOuLrOudvOyniCDsiJgg7J6I7Iq164uI64ukLiDsoJXtmZXtlZwg7ZiE7J6sIOuwmO2ZmOq4iOydgCDthrXsi6Dsgqwg7JWxIOuYkOuKlCDssq3qtazshJzsl5DshJwg7ZmV7J247ZW0IOyjvOyEuOyalC48L3A+CiAgICAgICAgPC9kaXY+CiAgICAgIGA7CiAgICAgIHJldHVybjsKICAgIH0KCiAgICBjb25zdCB0aXRsZSA9IG9wdHMudW5zdXBwb3J0ZWQgPyAnTU9ERSBOT1QgU1VQUE9SVEVEJyA6ICdQTEVBU0UgQ0hFQ0sgWU9VUiBJTlBVVCc7CiAgICBjb25zdCBiZyA9IG9wdHMudW5zdXBwb3J0ZWQgPyAnI0ZGRjRFNScgOiAnI0ZGRjBGMCc7CiAgICBjb25zdCBib3JkZXIgPSBvcHRzLnVuc3VwcG9ydGVkID8gJyNGRkQ4QTgnIDogJyNGRkM5QzknOwogICAgY29uc3QgY29sb3IgPSBvcHRzLnVuc3VwcG9ydGVkID8gJyM2NjNDMDAnIDogJyNDNTMwMzAnOwoKICAgIHJlc3VsdC5pbm5lckhUTUwgPSBgCiAgICAgIDxkaXYgY2xhc3M9ImhzLW1lcC1yZXN1bHQiIHN0eWxlPSJiYWNrZ3JvdW5kOiR7Ymd9OyBib3JkZXI6MXB4IHNvbGlkICR7Ym9yZGVyfTsgY29sb3I6JHtjb2xvcn07IHBhZGRpbmc6MjBweDsgYm9yZGVyLXJhZGl1czoxNnB4OyBtYXJnaW4tdG9wOjI0cHg7Ij4KICAgICAgICA8c3Ryb25nIHN0eWxlPSJkaXNwbGF5OmJsb2NrOyBmb250LXNpemU6MTRweDsgZm9udC13ZWlnaHQ6NzAwOyBtYXJnaW4tYm90dG9tOjZweDsiPiR7ZXNjYXBlSHRtbCh0aXRsZSl9PC9zdHJvbmc+CiAgICAgICAgPHNwYW4gc3R5bGU9ImZvbnQtc2l6ZToxMy41cHg7IGxpbmUtaGVpZ2h0OjEuNTsiPiR7ZXNjYXBlSHRtbChvcHRzLm1lc3NhZ2UpfTwvc3Bhbj4KICAgICAgPC9kaXY+CiAgICBgOwogIH0KCiAgZnVuY3Rpb24gcmVuZGVyRXhpdFBsYW5uZXJSZXN1bHQoaW5wdXRzLCBkYXRhKSB7CiAgICBjb25zdCByZXN1bHQgPSBieUlkKCdtLWV4aXQtcGxhbm5lci1yZXN1bHQnKTsKICAgIGlmICghcmVzdWx0KSByZXR1cm47CgogICAgbGV0IGNvbXBhcmlzb24zbUh0bWwgPSAnJzsKICAgIGlmIChkYXRhLmNvbXBhcmlzb24zbSkgewogICAgICBjb25zdCBjID0gZGF0YS5jb21wYXJpc29uM207CiAgICAgIGNvbXBhcmlzb24zbUh0bWwgPSBgCiAgICAgICAgPHNlY3Rpb24gY2xhc3M9ImhzLW1lcC1jb21wYXJpc29uIiBzdHlsZT0ibWFyZ2luLXRvcDoyNHB4OyBiYWNrZ3JvdW5kOiNGOEZBRkM7IGJvcmRlcjoxcHggc29saWQgI0UyRThGMDsgcGFkZGluZzoyMHB4OyBib3JkZXItcmFkaXVzOjE2cHg7Ij4KICAgICAgICAgIDxoNCBzdHlsZT0ibWFyZ2luOjAgMCA0cHggMDsgZm9udC1zaXplOjE2cHg7IGNvbG9yOiMxRTI5M0I7IGxldHRlci1zcGFjaW5nOjAuNXB4OyI+My1NT05USCBDT01QQVJJU09OPC9oND4KICAgICAgICAgIDxwIHN0eWxlPSJtYXJnaW46MCAwIDE2cHggMDsgZm9udC1zaXplOjEzcHg7IGNvbG9yOiM2NDc0OEI7Ij5Db21wYXJlIHdoYXQgeW91IG1heSBzcGVuZCBvdmVyIHRoZSBuZXh0IDMgbW9udGhzLjwvcD4KICAgICAgICAgIDxkaXYgY2xhc3M9ImhzLW1lcC1ncmlkIiBzdHlsZT0iZGlzcGxheTpncmlkOyBncmlkLXRlbXBsYXRlLWNvbHVtbnM6cmVwZWF0KGF1dG8tZml0LCBtaW5tYXgoMjYwcHgsIDFmcikpOyBnYXA6MTZweDsiPgogICAgICAgICAgICA8YXJ0aWNsZSBjbGFzcz0iaHMtbWVwLXNjZW5hcmlvIiBzdHlsZT0iYmFja2dyb3VuZDojRkZGRkZGOyBib3JkZXI6MXB4IHNvbGlkICNDQkQ1RTE7IHBhZGRpbmc6MTZweDsgYm9yZGVyLXJhZGl1czoxMnB4OyI+CiAgICAgICAgICAgICAgPGg1IHN0eWxlPSJtYXJnaW46MCAwIDEycHggMDsgZm9udC1zaXplOjE1cHg7IGNvbG9yOiMzMzQxNTU7Ij5DQU5DRUwgTk9XPC9oNT4KICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJocy1tZXAtcm93IiBzdHlsZT0iZGlzcGxheTpmbGV4OyBqdXN0aWZ5LWNvbnRlbnQ6c3BhY2UtYmV0d2VlbjsgbWFyZ2luLWJvdHRvbTo4cHg7IGZvbnQtc2l6ZToxMy41cHg7IGNvbG9yOiM2NDc0OEI7Ij4KICAgICAgICAgICAgICAgIDxzcGFuPkVzdGltYXRlZCBDYW5jZWxsYXRpb24gRmVlPC9zcGFuPgogICAgICAgICAgICAgICAgPHN0cm9uZz4ke2Zvcm1hdEtSVyhjLmNhbmNlbE5vdy5jYW5jZWxsYXRpb25GZWUpfTwvc3Ryb25nPgogICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICAgIDxkaXYgY2xhc3M9ImhzLW1lcC1yb3ciIHN0eWxlPSJkaXNwbGF5OmZsZXg7IGp1c3RpZnktY29udGVudDpzcGFjZS1iZXR3ZWVuOyBtYXJnaW4tYm90dG9tOjEycHg7IGZvbnQtc2l6ZToxMy41cHg7IGNvbG9yOiM2NDc0OEI7Ij4KICAgICAgICAgICAgICAgIDxzcGFuPlJlcGxhY2VtZW50IFNlcnZpY2UgQ29zdCAtIDMgTW9udGhzPC9zcGFuPgogICAgICAgICAgICAgICAgPHN0cm9uZz4ke2Zvcm1hdEtSVyhjLmNhbmNlbE5vdy5zZXJ2aWNlQ29zdCl9PC9zdHJvbmc+CiAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgPGRpdiBjbGFzcz0iaHMtbWVwLXRvdGFsIiBzdHlsZT0iZGlzcGxheTpmbGV4OyBqdXN0aWZ5LWNvbnRlbnQ6c3BhY2UtYmV0d2VlbjsgYm9yZGVyLXRvcDoxcHggc29saWQgI0UyRThGMDsgcGFkZGluZy10b3A6MTBweDsgZm9udC1zaXplOjE0LjVweDsgZm9udC13ZWlnaHQ6NzAwOyBjb2xvcjojMEYxNzJBOyI+CiAgICAgICAgICAgICAgICA8c3Bhbj5Fc3RpbWF0ZWQgMy1Nb250aCBDb3N0PC9zcGFuPgogICAgICAgICAgICAgICAgPHN0cm9uZz4ke2Zvcm1hdEtSVyhjLmNhbmNlbE5vdy50b3RhbCl9PC9zdHJvbmc+CiAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgIDwvYXJ0aWNsZT4KICAgICAgICAgICAgPGFydGljbGUgY2xhc3M9ImhzLW1lcC1zY2VuYXJpbyIgc3R5bGU9ImJhY2tncm91bmQ6I0ZGRkZGRjsgYm9yZGVyOjFweCBzb2xpZCAjQ0JENUUxOyBwYWRkaW5nOjE2cHg7IGJvcmRlci1yYWRpdXM6MTJweDsiPgogICAgICAgICAgICAgIDxoNSBzdHlsZT0ibWFyZ2luOjAgMCAxMnB4IDA7IGZvbnQtc2l6ZToxNXB4OyBjb2xvcjojMzM0MTU1OyI+V0FJVCAzIE1PTlRIUzwvaDU+CiAgICAgICAgICAgICAgPGRpdiBjbGFzcz0iaHMtbWVwLXJvdyIgc3R5bGU9ImRpc3BsYXk6ZmxleDsganVzdGlmeS1jb250ZW50OnNwYWNlLWJldHdlZW47IG1hcmdpbi1ib3R0b206OHB4OyBmb250LXNpemU6MTMuNXB4OyBjb2xvcjojNjQ3NDhCOyI+CiAgICAgICAgICAgICAgICA8c3Bhbj5DdXJyZW50IFNlcnZpY2UgQ29zdCAtIDMgTW9udGhzPC9zcGFuPgogICAgICAgICAgICAgICAgPHN0cm9uZz4ke2Zvcm1hdEtSVyhjLndhaXQuc2VydmljZUNvc3QpfTwvc3Ryb25nPgogICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICAgIDxkaXYgY2xhc3M9ImhzLW1lcC1yb3ciIHN0eWxlPSJkaXNwbGF5OmZsZXg7IGp1c3RpZnktY29udGVudDpzcGFjZS1iZXR3ZWVuOyBtYXJnaW4tYm90dG9tOjEycHg7IGZvbnQtc2l6ZToxMy41cHg7IGNvbG9yOiM2NDc0OEI7Ij4KICAgICAgICAgICAgICAgIDxzcGFuPkVzdGltYXRlZCBDYW5jZWxsYXRpb24gRmVlIChNb250aCAzKTwvc3Bhbj4KICAgICAgICAgICAgICAgIDxzdHJvbmc+JHtmb3JtYXRLUlcoYy53YWl0LmNhbmNlbGxhdGlvbkZlZSl9PC9zdHJvbmc+CiAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgPGRpdiBjbGFzcz0iaHMtbWVwLXRvdGFsIiBzdHlsZT0iZGlzcGxheTpmbGV4OyBqdXN0aWZ5LWNvbnRlbnQ6c3BhY2UtYmV0d2VlbjsgYm9yZGVyLXRvcDoxcHggc29saWQgI0UyRThGMDsgcGFkZGluZy10b3A6MTBweDsgZm9udC1zaXplOjE0LjVweDsgZm9udC13ZWlnaHQ6NzAwOyBjb2xvcjojMEYxNzJBOyI+CiAgICAgICAgICAgICAgICA8c3Bhbj5Fc3RpbWF0ZWQgMy1Nb250aCBDb3N0PC9zcGFuPgogICAgICAgICAgICAgICAgPHN0cm9uZz4ke2Zvcm1hdEtSVyhjLndhaXQudG90YWwpfTwvc3Ryb25nPgogICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICA8L2FydGljbGU+CiAgICAgICAgICA8L2Rpdj4KICAgICAgICAgIDxkaXYgY2xhc3M9ImhzLW1lcC1kaWZmZXJlbmNlIiBzdHlsZT0ibWFyZ2luLXRvcDoxNHB4OyBiYWNrZ3JvdW5kOiNGMUY1Rjk7IHBhZGRpbmc6MTJweCAxNnB4OyBib3JkZXItcmFkaXVzOjEwcHg7IGZvbnQtc2l6ZToxMy41cHg7IGNvbG9yOiMzMzQxNTU7IGxpbmUtaGVpZ2h0OjEuNjsiPgogICAgICAgICAgICBFc3RpbWF0ZWQgY29zdCBkaWZmZXJlbmNlOiA8c3Ryb25nIHN0eWxlPSJjb2xvcjojMEYxNzJBOyBmb250LXNpemU6MTVweDsiPiR7Zm9ybWF0S1JXKGMuZGlmZmVyZW5jZSl9PC9zdHJvbmc+PGJyPgogICAgICAgICAgICBMb3dlciBlc3RpbWF0ZWQgY29zdCBpbiB0aGlzIGNvbXBhcmlzb246IDxzdHJvbmcgc3R5bGU9ImNvbG9yOiMyNTYzRUI7IGZvbnQtc2l6ZToxNHB4OyI+JHtlc2NhcGVIdG1sKGMubG93ZXIpfTwvc3Ryb25nPgogICAgICAgICAgPC9kaXY+CiAgICAgICAgPC9zZWN0aW9uPgogICAgICBgOwogICAgfQoKICAgIGNvbnN0IGUgPSBkYXRhLmNvbXBhcmlzb25FbmQ7CiAgICBjb25zdCByYXdIb3Jpem9uID0gTnVtYmVyKGUuaG9yaXpvbk1vbnRocyk7CiAgICBpZiAoIU51bWJlci5pc0Zpbml0ZShyYXdIb3Jpem9uKSB8fCByYXdIb3Jpem9uIDw9IDApIHsKICAgICAgcmV0dXJuOwogICAgfQogICAgY29uc3QgaG9yaXpvbk1vbnRocyA9IE1hdGgucm91bmQocmF3SG9yaXpvbik7CgogICAgY29uc3QgY29tcGFyaXNvbkVuZEh0bWwgPSBgCiAgICAgIDxzZWN0aW9uIGNsYXNzPSJocy1tZXAtY29tcGFyaXNvbiIgc3R5bGU9Im1hcmdpbi10b3A6MjRweDsgYmFja2dyb3VuZDojRjhGQUZDOyBib3JkZXI6MXB4IHNvbGlkICNFMkU4RjA7IHBhZGRpbmc6MjBweDsgYm9yZGVyLXJhZGl1czoxNnB4OyI+CiAgICAgICAgPGg0IHN0eWxlPSJtYXJnaW46MCAwIDRweCAwOyBmb250LXNpemU6MTZweDsgY29sb3I6IzFFMjkzQjsgbGV0dGVyLXNwYWNpbmc6MC41cHg7Ij5VTlRJTCBDVVJSRU5UIENPTlRSQUNUIEVORCAoJHtob3Jpem9uTW9udGhzfSBNT05USFMpPC9oND4KICAgICAgICA8cCBzdHlsZT0ibWFyZ2luOjAgMCAxNnB4IDA7IGZvbnQtc2l6ZToxM3B4OyBjb2xvcjojNjQ3NDhCOyI+Q29tcGFyZSB3aGF0IHlvdSBtYXkgc3BlbmQgdW50aWwgeW91ciBjb250cmFjdCBlbmRzLjwvcD4KICAgICAgICA8ZGl2IGNsYXNzPSJocy1tZXAtZ3JpZCIgc3R5bGU9ImRpc3BsYXk6Z3JpZDsgZ3JpZC10ZW1wbGF0ZS1jb2x1bW5zOnJlcGVhdChhdXRvLWZpdCwgbWlubWF4KDI2MHB4LCAxZnIpKTsgZ2FwOjE2cHg7Ij4KICAgICAgICAgIDxhcnRpY2xlIGNsYXNzPSJocy1tZXAtc2NlbmFyaW8iIHN0eWxlPSJiYWNrZ3JvdW5kOiNGRkZGRkY7IGJvcmRlcjoxcHggc29saWQgI0NCRDVFMTsgcGFkZGluZzoxNnB4OyBib3JkZXItcmFkaXVzOjEycHg7Ij4KICAgICAgICAgICAgPGg1IHN0eWxlPSJtYXJnaW46MCAwIDEycHggMDsgZm9udC1zaXplOjE1cHg7IGNvbG9yOiMzMzQxNTU7Ij5DQU5DRUwgTk9XPC9oNT4KICAgICAgICAgICAgPGRpdiBjbGFzcz0iaHMtbWVwLXJvdyIgc3R5bGU9ImRpc3BsYXk6ZmxleDsganVzdGlmeS1jb250ZW50OnNwYWNlLWJldHdlZW47IG1hcmdpbi1ib3R0b206OHB4OyBmb250LXNpemU6MTMuNXB4OyBjb2xvcjojNjQ3NDhCOyI+CiAgICAgICAgICAgICAgPHNwYW4+RXN0aW1hdGVkIENhbmNlbGxhdGlvbiBGZWU8L3NwYW4+CiAgICAgICAgICAgICAgPHN0cm9uZz4ke2Zvcm1hdEtSVyhlLmNhbmNlbE5vdy5jYW5jZWxsYXRpb25GZWUpfTwvc3Ryb25nPgogICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgPGRpdiBjbGFzcz0iaHMtbWVwLXJvdyIgc3R5bGU9ImRpc3BsYXk6ZmxleDsganVzdGlmeS1jb250ZW50OnNwYWNlLWJldHdlZW47IG1hcmdpbi1ib3R0b206MTJweDsgZm9udC1zaXplOjEzLjVweDsgY29sb3I6IzY0NzQ4QjsiPgogICAgICAgICAgICAgIDxzcGFuPlJlcGxhY2VtZW50IFNlcnZpY2UgQ29zdCAtICR7aG9yaXpvbk1vbnRoc30gTW9udGhzPC9zcGFuPgogICAgICAgICAgICAgIDxzdHJvbmc+JHtmb3JtYXRLUlcoZS5jYW5jZWxOb3cuc2VydmljZUNvc3QpfTwvc3Ryb25nPgogICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgPGRpdiBjbGFzcz0iaHMtbWVwLXRvdGFsIiBzdHlsZT0iZGlzcGxheTpmbGV4OyBqdXN0aWZ5LWNvbnRlbnQ6c3BhY2UtYmV0d2VlbjsgYm9yZGVyLXRvcDoxcHggc29saWQgI0UyRThGMDsgcGFkZGluZy10b3A6MTBweDsgZm9udC1zaXplOjE0LjVweDsgZm9udC13ZWlnaHQ6NzAwOyBjb2xvcjojMEYxNzJBOyI+CiAgICAgICAgICAgICAgPHNwYW4+RXN0aW1hdGVkIENvc3QgVW50aWwgQ29udHJhY3QgRW5kPC9zcGFuPgogICAgICAgICAgICAgIDxzdHJvbmc+JHtmb3JtYXRLUlcoZS5jYW5jZWxOb3cudG90YWwpfTwvc3Ryb25nPgogICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgIDwvYXJ0aWNsZT4KICAgICAgICAgIDxhcnRpY2xlIGNsYXNzPSJocy1tZXAtc2NlbmFyaW8iIHN0eWxlPSJiYWNrZ3JvdW5kOiNGRkZGRkY7IGJvcmRlcjoxcHggc29saWQgI0NCRDVFMTsgcGFkZGluZzoxNnB4OyBib3JkZXItcmFkaXVzOjEycHg7Ij4KICAgICAgICAgICAgPGg1IHN0eWxlPSJtYXJnaW46MCAwIDEycHggMDsgZm9udC1zaXplOjE1cHg7IGNvbG9yOiMzMzQxNTU7Ij5TVEFZIFVOVElMIENPTlRSQUNUIEVORDwvaDU+CiAgICAgICAgICAgIDxkaXYgY2xhc3M9ImhzLW1lcC1yb3ciIHN0eWxlPSJkaXNwbGF5OmZsZXg7IGp1c3RpZnktY29udGVudDpzcGFjZS1iZXR3ZWVuOyBtYXJnaW4tYm90dG9tOjhweDsgZm9udC1zaXplOjEzLjVweDsgY29sb3I6IzY0NzQ4QjsiPgogICAgICAgICAgICAgIDxzcGFuPkN1cnJlbnQgU2VydmljZSBDb3N0IC0gJHtob3Jpem9uTW9udGhzfSBNb250aHM8L3NwYW4+CiAgICAgICAgICAgICAgPHN0cm9uZz4ke2Zvcm1hdEtSVyhlLndhaXQuc2VydmljZUNvc3QpfTwvc3Ryb25nPgogICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgPGRpdiBjbGFzcz0iaHMtbWVwLXJvdyIgc3R5bGU9ImRpc3BsYXk6ZmxleDsganVzdGlmeS1jb250ZW50OnNwYWNlLWJldHdlZW47IG1hcmdpbi1ib3R0b206MTJweDsgZm9udC1zaXplOjEzLjVweDsgY29sb3I6IzY0NzQ4QjsiPgogICAgICAgICAgICAgIDxzcGFuPkVzdGltYXRlZCBDYW5jZWxsYXRpb24gRmVlIGF0IEVuZDwvc3Bhbj4KICAgICAgICAgICAgICA8c3Ryb25nPiR7Zm9ybWF0S1JXKGUud2FpdC5jYW5jZWxsYXRpb25GZWUpfTwvc3Ryb25nPgogICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgPGRpdiBjbGFzcz0iaHMtbWVwLXRvdGFsIiBzdHlsZT0iZGlzcGxheTpmbGV4OyBqdXN0aWZ5LWNvbnRlbnQ6c3BhY2UtYmV0d2VlbjsgYm9yZGVyLXRvcDoxcHggc29saWQgI0UyRThGMDsgcGFkZGluZy10b3A6MTBweDsgZm9udC1zaXplOjE0LjVweDsgZm9udC13ZWlnaHQ6NzAwOyBjb2xvcjojMEYxNzJBOyI+CiAgICAgICAgICAgICAgPHNwYW4+RXN0aW1hdGVkIENvc3QgVW50aWwgQ29udHJhY3QgRW5kPC9zcGFuPgogICAgICAgICAgICAgIDxzdHJvbmc+JHtmb3JtYXRLUlcoZS53YWl0LnRvdGFsKX08L3N0cm9uZz4KICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICA8L2FydGljbGU+CiAgICAgICAgPC9kaXY+CiAgICAgICAgPGRpdiBjbGFzcz0iaHMtbWVwLWRpZmZlcmVuY2UiIHN0eWxlPSJtYXJnaW4tdG9wOjE0cHg7IGJhY2tncm91bmQ6I0YxRjVGOTsgcGFkZGluZzoxMnB4IDE2cHg7IGJvcmRlci1yYWRpdXM6MTBweDsgZm9udC1zaXplOjEzLjVweDsgY29sb3I6IzMzNDE1NTsgbGluZS1oZWlnaHQ6MS42OyI+CiAgICAgICAgICBFc3RpbWF0ZWQgY29zdCBkaWZmZXJlbmNlOiA8c3Ryb25nIHN0eWxlPSJjb2xvcjojMEYxNzJBOyBmb250LXNpemU6MTVweDsiPiR7Zm9ybWF0S1JXKGUuZGlmZmVyZW5jZSl9PC9zdHJvbmc+PGJyPgogICAgICAgICAgTG93ZXIgZXN0aW1hdGVkIGNvc3QgaW4gdGhpcyBjb21wYXJpc29uOiA8c3Ryb25nIHN0eWxlPSJjb2xvcjojMjU2M0VCOyBmb250LXNpemU6MTRweDsiPiR7ZXNjYXBlSHRtbChlLmxvd2VyKX08L3N0cm9uZz4KICAgICAgICA8L2Rpdj4KICAgICAgPC9zZWN0aW9uPgogICAgYDsKCiAgICByZXN1bHQuaW5uZXJIVE1MID0gYAogICAgICA8ZGl2IGNsYXNzPSJocy1tZXAtcmVzdWx0IiBzdHlsZT0iYmFja2dyb3VuZDpsaW5lYXItZ3JhZGllbnQoMTM1ZGVnLCAjMkQxRjU4IDAlLCAjMTcxMDMzIDEwMCUpOyBjb2xvcjojZmZmOyBwYWRkaW5nOjI4cHg7IGJvcmRlci1yYWRpdXM6MjBweDsgbWFyZ2luLXRvcDoyNHB4OyBib3gtc2hhZG93OjAgMTJweCAzNnB4IHJnYmEoMCwwLDAsMC4yNSk7Ij4KICAgICAgICA8aGVhZGVyIGNsYXNzPSJocy1tZXAtcmVzdWx0LWhlYWRlciIgc3R5bGU9Im1hcmdpbi1ib3R0b206MjBweDsiPgogICAgICAgICAgPHAgY2xhc3M9ImhzLW1lcC1leWVicm93IiBzdHlsZT0iZm9udC1zaXplOjEycHg7IGZvbnQtd2VpZ2h0OjcwMDsgbGV0dGVyLXNwYWNpbmc6MXB4OyBjb2xvcjojQkNBN0ZGOyBtYXJnaW46MCAwIDZweCAwOyI+TU9CSUxFIENPTlRSQUNUIEVYSVQgUExBTk5FUjwvcD4KICAgICAgICAgIDxoMyBzdHlsZT0ibWFyZ2luOjA7IGZvbnQtc2l6ZToyMHB4OyBmb250LXdlaWdodDo3MDA7IGNvbG9yOiNGRkZGRkY7IGxpbmUtaGVpZ2h0OjEuNDsiPldvdWxkIHN3aXRjaGluZyBub3cgb3Igd2FpdGluZyBsaWtlbHkgY29zdCBsZXNzIG92ZXIgdGhlIHNhbWUgcGVyaW9kPzwvaDM+CiAgICAgICAgPC9oZWFkZXI+CiAgICAgICAgJHtjb21wYXJpc29uM21IdG1sfQogICAgICAgICR7Y29tcGFyaXNvbkVuZEh0bWx9CiAgICAgICAgPHNlY3Rpb24gY2xhc3M9ImhzLW1lcC1kZXZpY2UiIHN0eWxlPSJtYXJnaW4tdG9wOjI0cHg7IGJhY2tncm91bmQ6cmdiYSgyNTUsMjU1LDI1NSwwLjA4KTsgYm9yZGVyLXJhZGl1czoxNHB4OyBwYWRkaW5nOjE4cHg7Ij4KICAgICAgICAgIDxoNCBzdHlsZT0ibWFyZ2luOjAgMCAxMnB4IDA7IGZvbnQtc2l6ZToxNXB4OyBjb2xvcjojRDZDRUY4OyI+REVWSUNFIEZJTkFOQ0lORzwvaDQ+CiAgICAgICAgICA8ZGl2IGNsYXNzPSJocy1tZXAtcm93IiBzdHlsZT0iZGlzcGxheTpmbGV4OyBqdXN0aWZ5LWNvbnRlbnQ6c3BhY2UtYmV0d2VlbjsgZm9udC1zaXplOjE1cHg7IGZvbnQtd2VpZ2h0OjcwMDsgY29sb3I6I0ZGRkZGRjsgbWFyZ2luLWJvdHRvbTo4cHg7Ij4KICAgICAgICAgICAgPHNwYW4+UmVtYWluaW5nIERldmljZSBCYWxhbmNlPC9zcGFuPgogICAgICAgICAgICA8c3Ryb25nPiR7Zm9ybWF0S1JXKGRhdGEuZGV2aWNlQmFsYW5jZSl9PC9zdHJvbmc+CiAgICAgICAgICA8L2Rpdj4KICAgICAgICAgIDxwIHN0eWxlPSJtYXJnaW46MDsgZm9udC1zaXplOjEyLjVweDsgY29sb3I6I0E4OTlFNjsgbGluZS1oZWlnaHQ6MS41OyI+U2hvd24gc2VwYXJhdGVseSBiZWNhdXNlIHRoZSByZW1haW5pbmcgZGV2aWNlIGJhbGFuY2UgaXMgbm90IGluY2x1ZGVkIGluIHRoZSBjb3N0IGNvbXBhcmlzb24gYWJvdmUuPC9wPgogICAgICAgIDwvc2VjdGlvbj4KICAgICAgICA8ZGl2IGNsYXNzPSJocy1tZXAtZGlzY2xhaW1lciIgc3R5bGU9Im1hcmdpbi10b3A6MjBweDsgZm9udC1zaXplOjEyLjVweDsgY29sb3I6I0E4OTlFNjsgbGluZS1oZWlnaHQ6MS41OyBiYWNrZ3JvdW5kOnJnYmEoMCwwLDAsMC4yNSk7IHBhZGRpbmc6MTJweCAxNnB4OyBib3JkZXItcmFkaXVzOjEwcHg7IGZvbnQtc3R5bGU6aXRhbGljOyI+QXBwcm94aW1hdGUgZWR1Y2F0aW9uYWwgZXN0aW1hdGUgYmFzZWQgb24gdGhlIGNvbnRyYWN0IGFuZCBkaXNjb3VudCBpbmZvcm1hdGlvbiB5b3UgZW50ZXJlZC4gU2VsZWN0aW9uLWNvbnRyYWN0IGVzdGltYXRlcyB1c2UgbW9udGgtbGV2ZWwgYXBwcm94aW1hdGlvbnM7IGFjdHVhbCBjYXJyaWVyIGNhbGN1bGF0aW9ucyBtYXkgdXNlIGV4YWN0IGRhdGVzLCBwcm9yYXRlZCBkaXNjb3VudHMsIGFuZCBhY2NvdW50LXNwZWNpZmljIHJ1bGVzLiBBY3R1YWwgY2hhcmdlcyBtYXkgZGlmZmVyLiBGb3IgcGxhbm5pbmcgcHVycG9zZXMgb25seS4gVGhpcyBpcyBub3QgYW4gb2ZmaWNpYWwgY2FycmllciBxdW90ZS48L2Rpdj4KICAgICAgPC9kaXY+CiAgICBgOwogIH0KCiAgd2luZG93LmNhbGN1bGF0ZU1vYmlsZUV4aXRQbGFuID0gZnVuY3Rpb24gKCkgewogICAgY29uc3QgaW5wdXRzID0gY29sbGVjdE1vYmlsZVBsYW5uZXJJbnB1dHMoKTsKCiAgICBpZiAoIWlucHV0cy5vaykgewogICAgICByZW5kZXJNb2JpbGVQbGFubmVyTWVzc2FnZSh7CiAgICAgICAgbWVzc2FnZTogaW5wdXRzLm1lc3NhZ2UsCiAgICAgICAgdW5zdXBwb3J0ZWQ6IEJvb2xlYW4oaW5wdXRzLnVuc3VwcG9ydGVkKSwKICAgICAgICBkZXZpY2VTdWJzaWR5TW9kZTogQm9vbGVhbihpbnB1dHMuZGV2aWNlU3Vic2lkeU1vZGUpCiAgICAgIH0pOwoKICAgICAgaWYgKGlucHV0cy5maWVsZCkgewogICAgICAgIGNvbnN0IGVsID0gYnlJZChpbnB1dHMuZmllbGQpOwogICAgICAgIGlmIChlbCkgewogICAgICAgICAgZWwuZm9jdXMoKTsKICAgICAgICAgIGVsLnNldEF0dHJpYnV0ZSgnYXJpYS1pbnZhbGlkJywgJ3RydWUnKTsKICAgICAgICB9CiAgICAgIH0KICAgICAgcmV0dXJuOwogICAgfQoKICAgIGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoJyNtb2JpbGUtcGFuZWwgW2FyaWEtaW52YWxpZD0idHJ1ZSJdJykuZm9yRWFjaChmdW5jdGlvbihlbCkgewogICAgICBlbC5yZW1vdmVBdHRyaWJ1dGUoJ2FyaWEtaW52YWxpZCcpOwogICAgfSk7CgogICAgY29uc3QgY2FsY3VsYXRlZCA9IGNhbGN1bGF0ZUV4aXRQbGFubmVyU2NlbmFyaW9zKGlucHV0cyk7CgogICAgaWYgKCFjYWxjdWxhdGVkLm9rKSB7CiAgICAgIHJlbmRlck1vYmlsZVBsYW5uZXJNZXNzYWdlKHsKICAgICAgICBtZXNzYWdlOiBjYWxjdWxhdGVkLmVycm9yIHx8ICdVbmFibGUgdG8gY2FsY3VsYXRlIHRoaXMgZXN0aW1hdGUuJwogICAgICB9KTsKICAgICAgcmV0dXJuOwogICAgfQoKICAgIHJlbmRlckV4aXRQbGFubmVyUmVzdWx0KGlucHV0cywgY2FsY3VsYXRlZCk7CiAgfTsKCiAgaWYgKGRvY3VtZW50LnJlYWR5U3RhdGUgPT09ICdsb2FkaW5nJykgewogICAgZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignRE9NQ29udGVudExvYWRlZCcsIGZ1bmN0aW9uICgpIHsKICAgICAgY29uc3QgY2FycmllckVsID0gYnlJZCgnbS1jYXJyaWVyJyk7CiAgICAgIGNvbnN0IHRlcm1FbCA9IGJ5SWQoJ20tY29udHJhY3QtdGVybScpOwogICAgICBjb25zdCBkaXNjb3VudFR5cGVFbCA9IGJ5SWQoJ20tZGlzY291bnQtdHlwZScpOwoKICAgICAgaWYgKGNhcnJpZXJFbCkgY2FycmllckVsLmFkZEV2ZW50TGlzdGVuZXIoJ2NoYW5nZScsIHdpbmRvdy5zeW5jQ2FycmllclRlcm1zQW5kSGVscGVycyk7CiAgICAgIGlmICh0ZXJtRWwpIHRlcm1FbC5hZGRFdmVudExpc3RlbmVyKCdjaGFuZ2UnLCB3aW5kb3cuc3luY0NhcnJpZXJUZXJtc0FuZEhlbHBlcnMpOwogICAgICBpZiAoZGlzY291bnRUeXBlRWwpIGRpc2NvdW50VHlwZUVsLmFkZEV2ZW50TGlzdGVuZXIoJ2NoYW5nZScsIHdpbmRvdy5zeW5jQ2FycmllclRlcm1zQW5kSGVscGVycyk7CgogICAgICB3aW5kb3cuc3luY0NhcnJpZXJUZXJtc0FuZEhlbHBlcnMoKTsKICAgIH0pOwogIH0gZWxzZSB7CiAgICBjb25zdCBjYXJyaWVyRWwgPSBieUlkKCdtLWNhcnJpZXInKTsKICAgIGNvbnN0IHRlcm1FbCA9IGJ5SWQoJ20tY29udHJhY3QtdGVybScpOwogICAgY29uc3QgZGlzY291bnRUeXBlRWwgPSBieUlkKCdtLWRpc2NvdW50LXR5cGUnKTsKCiAgICBpZiAoY2FycmllckVsKSBjYXJyaWVyRWwuYWRkRXZlbnRMaXN0ZW5lcignY2hhbmdlJywgd2luZG93LnN5bmNDYXJyaWVyVGVybXNBbmRIZWxwZXJzKTsKICAgIGlmICh0ZXJtRWwpIHRlcm1FbC5hZGRFdmVudExpc3RlbmVyKCdjaGFuZ2UnLCB3aW5kb3cuc3luY0NhcnJpZXJUZXJtc0FuZEhlbHBlcnMpOwogICAgaWYgKGRpc2NvdW50VHlwZUVsKSBkaXNjb3VudFR5cGVFbC5hZGRFdmVudExpc3RlbmVyKCdjaGFuZ2UnLCB3aW5kb3cuc3luY0NhcnJpZXJUZXJtc0FuZEhlbHBlcnMpOwoKICAgIHdpbmRvdy5zeW5jQ2FycmllclRlcm1zQW5kSGVscGVycygpOwogIH0KCgogIGZ1bmN0aW9uIHJlbmRlclB1cmVXaGl0ZUhlYWRlcigpIHsKICAgIGlmICghZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2hzLWRyb3Bkb3duLWJyaWRnZS1zdHlsZScpKSB7CiAgICAgIHZhciBicmlkZ2VTdHlsZSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3N0eWxlJyk7CiAgICAgIGJyaWRnZVN0eWxlLmlkID0gJ2hzLWRyb3Bkb3duLWJyaWRnZS1zdHlsZSc7CiAgICAgIGJyaWRnZVN0eWxlLmlubmVySFRNTCA9ICdAbWVkaWEgKG1pbi13aWR0aDogOTIycHgpeyAuaHMtY2xhc3Nlcy1uYXYtaXRlbSA+IGEsIC5ocy1jbGFzc2VzLW5hdi1pdGVtIC5tZW51LXRleHR7Y29sb3I6I0ZGRkZGRiAhaW1wb3J0YW50O30gLmhzLWNsYXNzZXMtdG9nZ2xlLWJ0bntkaXNwbGF5Om5vbmUgIWltcG9ydGFudDt9IC5ocy1kcm9wZG93bi1jb250ZW50IGF7Y29sb3I6I0ZGRkZGRiAhaW1wb3J0YW50OyBmb250LXdlaWdodDo3MDAgIWltcG9ydGFudDsgZm9udC1zaXplOjEzLjVweCAhaW1wb3J0YW50O30gLmhzLWRyb3Bkb3duLWNvbnRlbnQ6OmJlZm9yZXtjb250ZW50OiIiICFpbXBvcnRhbnQ7IHBvc2l0aW9uOmFic29sdXRlICFpbXBvcnRhbnQ7IHRvcDotMTRweCAhaW1wb3J0YW50OyBsZWZ0OjAgIWltcG9ydGFudDsgd2lkdGg6MTAwJSAhaW1wb3J0YW50OyBoZWlnaHQ6MTRweCAhaW1wb3J0YW50OyBiYWNrZ3JvdW5kOnRyYW5zcGFyZW50ICFpbXBvcnRhbnQ7fSAuaHMtZHJvcGRvd24tbWVudTpob3ZlciAuaHMtZHJvcGRvd24tY29udGVudCwgLmhzLWRyb3Bkb3duLWNvbnRlbnQ6aG92ZXJ7ZGlzcGxheTpibG9jayAhaW1wb3J0YW50O30gfSBAbWVkaWEgKG1heC13aWR0aDogOTIxcHgpeyAuaHMtY2xhc3Nlcy1uYXYtaXRlbSA+IGEsIC5ocy1jbGFzc2VzLW5hdi1pdGVtIC5tZW51LXRleHR7Y29sb3I6IzIxMUMzNSAhaW1wb3J0YW50OyBmb250LXdlaWdodDo3MDAgIWltcG9ydGFudDt9IC5ocy1jbGFzc2VzLXJvd3tkaXNwbGF5OmZsZXggIWltcG9ydGFudDsgYWxpZ24taXRlbXM6Y2VudGVyICFpbXBvcnRhbnQ7IGp1c3RpZnktY29udGVudDpzcGFjZS1iZXR3ZWVuICFpbXBvcnRhbnQ7IHdpZHRoOjEwMCUgIWltcG9ydGFudDt9IC5ocy1jbGFzc2VzLXRvZ2dsZS1idG57ZGlzcGxheTppbmxpbmUtZmxleCAhaW1wb3J0YW50OyBhbGlnbi1pdGVtczpjZW50ZXIgIWltcG9ydGFudDsganVzdGlmeS1jb250ZW50OmNlbnRlciAhaW1wb3J0YW50OyB3aWR0aDo0NHB4ICFpbXBvcnRhbnQ7IGhlaWdodDo0NHB4ICFpbXBvcnRhbnQ7IGJhY2tncm91bmQ6I0Y0RjBGQSAhaW1wb3J0YW50OyBib3JkZXI6MXB4IHNvbGlkICNFQkU1RjUgIWltcG9ydGFudDsgYm9yZGVyLXJhZGl1czoxMHB4ICFpbXBvcnRhbnQ7IGNvbG9yOiM1QjRBQTMgIWltcG9ydGFudDsgY3Vyc29yOnBvaW50ZXIgIWltcG9ydGFudDsgbWFyZ2luLXJpZ2h0OjhweCAhaW1wb3J0YW50O30gLmhzLWNsYXNzZXMtbmF2LWl0ZW0uaHMtYWNjb3JkaW9uLWV4cGFuZGVkIC5ocy1jbGFzc2VzLXRvZ2dsZS1idG57YmFja2dyb3VuZDojNUI0QUEzICFpbXBvcnRhbnQ7IGNvbG9yOiNGRkZGRkYgIWltcG9ydGFudDt9IC5ocy1jbGFzc2VzLW5hdi1pdGVtLmhzLWFjY29yZGlvbi1leHBhbmRlZCAuaHMtY2xhc3Nlcy10b2dnbGUtYnRuIHN2Z3t0cmFuc2Zvcm06cm90YXRlKDE4MGRlZykgIWltcG9ydGFudDt9IC5ocy1jbGFzc2VzLW5hdi1pdGVtIC5ocy1kcm9wZG93bi1jb250ZW50e3Bvc2l0aW9uOnN0YXRpYyAhaW1wb3J0YW50OyB0cmFuc2Zvcm06bm9uZSAhaW1wb3J0YW50OyBkaXNwbGF5Om5vbmUgIWltcG9ydGFudDt9IC5ocy1jbGFzc2VzLW5hdi1pdGVtLmhzLWFjY29yZGlvbi1leHBhbmRlZCAuaHMtZHJvcGRvd24tY29udGVudHtkaXNwbGF5OmJsb2NrICFpbXBvcnRhbnQ7IHZpc2liaWxpdHk6dmlzaWJsZSAhaW1wb3J0YW50OyBvcGFjaXR5OjEgIWltcG9ydGFudDsgaGVpZ2h0OmF1dG8gIWltcG9ydGFudDsgYmFja2dyb3VuZDojRjRGMEZBICFpbXBvcnRhbnQ7IGJvcmRlcjoxcHggc29saWQgI0VCRTVGNSAhaW1wb3J0YW50OyBib3JkZXItcmFkaXVzOjEycHggIWltcG9ydGFudDsgbWFyZ2luOjhweCAwIDEycHggMCAhaW1wb3J0YW50OyBwYWRkaW5nOjZweCA4cHggIWltcG9ydGFudDt9IC5ocy1jbGFzc2VzLW5hdi1pdGVtLmhzLWFjY29yZGlvbi1leHBhbmRlZCAuaHMtZHJvcGRvd24tY29udGVudCBhe2NvbG9yOiM1QjRBQTMgIWltcG9ydGFudDsgYmFja2dyb3VuZDojRkZGRkZGICFpbXBvcnRhbnQ7IGJvcmRlcjoxcHggc29saWQgI0RERDZGMyAhaW1wb3J0YW50OyBkaXNwbGF5OmJsb2NrICFpbXBvcnRhbnQ7IHBhZGRpbmc6OXB4IDE0cHggIWltcG9ydGFudDsgbWFyZ2luOjRweCAwICFpbXBvcnRhbnQ7IGJvcmRlci1yYWRpdXM6OHB4ICFpbXBvcnRhbnQ7IGZvbnQtc2l6ZToxMy41cHggIWltcG9ydGFudDsgZm9udC13ZWlnaHQ6NzAwICFpbXBvcnRhbnQ7IHRleHQtc2hhZG93Om5vbmUgIWltcG9ydGFudDt9IH0gLmhzLXN1YnBhZ2UtY29udGFpbmVyLCAuaHMtdG9vbHMtaGVyby13cmFwe21pbi1oZWlnaHQ6MTAwMHB4ICFpbXBvcnRhbnQ7IHBhZGRpbmctYm90dG9tOjYwcHggIWltcG9ydGFudDt9JzsKICAgICAgZG9jdW1lbnQuaGVhZC5hcHBlbmRDaGlsZChicmlkZ2VTdHlsZSk7CiAgICB9CgogICAgdmFyIHVscyA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoJyNhc3QtaGYtbWVudS0xLCAjYXN0LWhmLW1vYmlsZS1tZW51LCB1bC5tYWluLWhlYWRlci1tZW51Jyk7CiAgICB1bHMuZm9yRWFjaChmdW5jdGlvbih1bCkgewogICAgICBpZiAoIXVsIHx8IHVsLnF1ZXJ5U2VsZWN0b3IoJy5ocy1jbGFzc2VzLW5hdi1pdGVtJykpIHJldHVybjsKCiAgICAgIHZhciBndWlkZUxpID0gdWwucXVlcnlTZWxlY3RvcignLm1lbnUtaXRlbS0zNTEzJyk7CiAgICAgIGlmICghZ3VpZGVMaSkgewogICAgICAgIHZhciBsaXMgPSBBcnJheS5mcm9tKHVsLmNoaWxkcmVuKTsKICAgICAgICBndWlkZUxpID0gbGlzLmZpbmQoZnVuY3Rpb24obGkpIHsKICAgICAgICAgIHZhciB0eHQgPSBsaS50ZXh0Q29udGVudCB8fCBsaS5pbm5lclRleHQgfHwgJyc7CiAgICAgICAgICB2YXIgYSA9IGxpLnF1ZXJ5U2VsZWN0b3IoJ2EnKTsKICAgICAgICAgIHJldHVybiB0eHQuaW5kZXhPZignR3VpZGVzJykgIT09IC0xIHx8IChhICYmIGEuaHJlZiAmJiBhLmhyZWYuaW5kZXhPZignZ3VpZGVzJykgIT09IC0xKTsKICAgICAgICB9KTsKICAgICAgfQoKICAgICAgaWYgKGd1aWRlTGkpIHsKICAgICAgICB2YXIgY2xhc3Nlc0xpID0gZ3VpZGVMaS5jbG9uZU5vZGUodHJ1ZSk7CiAgICAgICAgY2xhc3Nlc0xpLmlkID0gJyc7CiAgICAgICAgY2xhc3Nlc0xpLmNsYXNzTmFtZSA9IGd1aWRlTGkuY2xhc3NOYW1lICsgJyBocy1jbGFzc2VzLW5hdi1pdGVtIGhzLWRyb3Bkb3duLW1lbnUnOwogICAgICAgIGNsYXNzZXNMaS5zdHlsZS5wb3NpdGlvbiA9ICdyZWxhdGl2ZSc7CgogICAgICAgIHZhciBzdmdJY29uID0gJzxzdmcgd2lkdGg9IjEyIiBoZWlnaHQ9IjEyIiB2aWV3Qm94PSIwIDAgMTIgMTIiPjxwYXRoIGQ9Ik0yLjUgNC41TDYgOEw5LjUgNC41IiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGZpbGw9Im5vbmUiLz48L3N2Zz4nOwoKICAgICAgICB2YXIgcm93RGl2ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICAgICAgcm93RGl2LmNsYXNzTmFtZSA9ICdocy1jbGFzc2VzLXJvdyc7CiAgICAgICAgcm93RGl2LmlubmVySFRNTCA9ICc8YSBocmVmPSJodHRwczovL2hhbmd1bHNwYWNlLmNvbS9jbGFzc2VzLyIgY2xhc3M9Im1lbnUtbGluayI+PHNwYW4gY2xhc3M9Im1lbnUtdGV4dCI+Q2xhc3Nlczwvc3Bhbj48L2E+PGJ1dHRvbiB0eXBlPSJidXR0b24iIGNsYXNzPSJocy1jbGFzc2VzLXRvZ2dsZS1idG4iIGFyaWEtbGFiZWw9IlRvZ2dsZSBDbGFzc2VzIFN1Ym1lbnUiPicgKyBzdmdJY29uICsgJzwvYnV0dG9uPic7CgogICAgICAgIHZhciBkcm9wZG93bkRpdiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOwogICAgICAgIGRyb3Bkb3duRGl2LmNsYXNzTmFtZSA9ICdocy1kcm9wZG93bi1jb250ZW50JzsKICAgICAgICBkcm9wZG93bkRpdi5zdHlsZS5jc3NUZXh0ID0gJ2Rpc3BsYXk6bm9uZTsgcG9zaXRpb246YWJzb2x1dGUgIWltcG9ydGFudDsgdG9wOmNhbGMoMTAwJSArIDEycHgpICFpbXBvcnRhbnQ7IGxlZnQ6NTAlICFpbXBvcnRhbnQ7IHRyYW5zZm9ybTp0cmFuc2xhdGVYKC01MCUpICFpbXBvcnRhbnQ7IGJhY2tncm91bmQ6cmdiYSgyNSwgMjIsIDQwLCAwLjk1KSAhaW1wb3J0YW50OyBtaW4td2lkdGg6MjQwcHggIWltcG9ydGFudDsgd2lkdGg6MjQwcHggIWltcG9ydGFudDsgYm9yZGVyLXJhZGl1czoxOHB4ICFpbXBvcnRhbnQ7IHBhZGRpbmc6OHB4ICFpbXBvcnRhbnQ7IGJvcmRlcjoxcHggc29saWQgcmdiYSgxODgsIDE2NywgMjU1LCAwLjMpICFpbXBvcnRhbnQ7IGJveC1zaGFkb3c6MCAyMHB4IDQ1cHggcmdiYSgwLCAwLCAwLCAwLjYpLCAwIDAgMjBweCByZ2JhKDE4OCwgMTY3LCAyNTUsIDAuMTUpICFpbXBvcnRhbnQ7IGJhY2tkcm9wLWZpbHRlcjpibHVyKDIwcHgpICFpbXBvcnRhbnQ7IHotaW5kZXg6OTk5OTk5ICFpbXBvcnRhbnQ7IGJveC1zaXppbmc6Ym9yZGVyLWJveCAhaW1wb3J0YW50Oyc7CiAgICAgICAgCiAgICAgICAgdmFyIGNvbXBhY3RMaW5rU3R5bGUgPSAncGFkZGluZzo5cHggMTZweCAhaW1wb3J0YW50OyBtYXJnaW46NHB4IDAgIWltcG9ydGFudDsgZm9udC1zaXplOjEzLjVweCAhaW1wb3J0YW50OyBmb250LXdlaWdodDo3MDAgIWltcG9ydGFudDsgbGluZS1oZWlnaHQ6MS4zNSAhaW1wb3J0YW50OyBoZWlnaHQ6YXV0byAhaW1wb3J0YW50OyBtaW4taGVpZ2h0OnVuc2V0ICFpbXBvcnRhbnQ7IGRpc3BsYXk6ZmxleCAhaW1wb3J0YW50OyBhbGlnbi1pdGVtczpjZW50ZXIgIWltcG9ydGFudDsgYm9yZGVyLXJhZGl1czoxMHB4ICFpbXBvcnRhbnQ7IHRleHQtZGVjb3JhdGlvbjpub25lICFpbXBvcnRhbnQ7JzsKCiAgICAgICAgZHJvcGRvd25EaXYuaW5uZXJIVE1MID0gJzxhIGhyZWY9Imh0dHBzOi8vaGFuZ3Vsc3BhY2UuY29tL2NsYXNzZXMvIiBzdHlsZT0iJyArIGNvbXBhY3RMaW5rU3R5bGUgKyAnIj5BbGwgQ2xhc3NlcyAmIFByaWNpbmc8L2E+PGEgaHJlZj0iaHR0cHM6Ly9oYW5ndWxzcGFjZS5jb20vY2xhc3Nlcy8jbGVhcm4ta29yZWFuIiBzdHlsZT0iJyArIGNvbXBhY3RMaW5rU3R5bGUgKyAnIj5MZWFybiBLb3JlYW48L2E+PGEgaHJlZj0iaHR0cHM6Ly9oYW5ndWxzcGFjZS5jb20vY2xhc3Nlcy8jbGVhcm4tZW5nbGlzaCIgc3R5bGU9IicgKyBjb21wYWN0TGlua1N0eWxlICsgJyI+TGVhcm4gRW5nbGlzaDwvYT48YSBocmVmPSJodHRwczovL2hhbmd1bHNwYWNlLmNvbS9jbGFzc2VzLyN0ZWFjaGVyLWRldmVsb3BtZW50IiBzdHlsZT0iJyArIGNvbXBhY3RMaW5rU3R5bGUgKyAnIj5Lb3JlYW4gVGVhY2hlciBEZXZlbG9wbWVudDwvYT4nOwoKICAgICAgICBjbGFzc2VzTGkuaW5uZXJIVE1MID0gJyc7CiAgICAgICAgY2xhc3Nlc0xpLmFwcGVuZENoaWxkKHJvd0Rpdik7CiAgICAgICAgY2xhc3Nlc0xpLmFwcGVuZENoaWxkKGRyb3Bkb3duRGl2KTsKCiAgICAgICAgY2xhc3Nlc0xpLmFkZEV2ZW50TGlzdGVuZXIoJ21vdXNlZW50ZXInLCBmdW5jdGlvbigpIHsgaWYgKHdpbmRvdy5pbm5lcldpZHRoID49IDkyMikgZHJvcGRvd25EaXYuc3R5bGUuZGlzcGxheSA9ICdibG9jayc7IH0pOwogICAgICAgIGNsYXNzZXNMaS5hZGRFdmVudExpc3RlbmVyKCdtb3VzZWxlYXZlJywgZnVuY3Rpb24oKSB7IGlmICh3aW5kb3cuaW5uZXJXaWR0aCA+PSA5MjIpIGRyb3Bkb3duRGl2LnN0eWxlLmRpc3BsYXkgPSAnbm9uZSc7IH0pOwoKICAgICAgICB2YXIgdG9nZ2xlQnRuID0gcm93RGl2LnF1ZXJ5U2VsZWN0b3IoJy5ocy1jbGFzc2VzLXRvZ2dsZS1idG4nKTsKICAgICAgICBpZiAodG9nZ2xlQnRuKSB7CiAgICAgICAgICB0b2dnbGVCdG4uYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCBmdW5jdGlvbihlKSB7CiAgICAgICAgICAgIGUucHJldmVudERlZmF1bHQoKTsKICAgICAgICAgICAgZS5zdG9wUHJvcGFnYXRpb24oKTsKICAgICAgICAgICAgY2xhc3Nlc0xpLmNsYXNzTGlzdC50b2dnbGUoJ2hzLWFjY29yZGlvbi1leHBhbmRlZCcpOwogICAgICAgICAgfSk7CiAgICAgICAgfQoKICAgICAgICB2YXIgaW5uZXJMaW5rcyA9IGRyb3Bkb3duRGl2LnF1ZXJ5U2VsZWN0b3JBbGwoJ2EnKTsKICAgICAgICBpbm5lckxpbmtzLmZvckVhY2goZnVuY3Rpb24obGluaykgewogICAgICAgICAgbGluay5hZGRFdmVudExpc3RlbmVyKCdtb3VzZWVudGVyJywgZnVuY3Rpb24oKSB7IGlmICh3aW5kb3cuaW5uZXJXaWR0aCA+PSA5MjIpIHsgdGhpcy5zdHlsZS5iYWNrZ3JvdW5kID0gJ3JnYigxMjMsIDEwNCwgMjM4KSc7IHRoaXMuc3R5bGUudHJhbnNmb3JtID0gJ3RyYW5zbGF0ZVgoNHB4KSc7IH0gfSk7CiAgICAgICAgICBsaW5rLmFkZEV2ZW50TGlzdGVuZXIoJ21vdXNlbGVhdmUnLCBmdW5jdGlvbigpIHsgaWYgKHdpbmRvdy5pbm5lcldpZHRoID49IDkyMikgeyB0aGlzLnN0eWxlLmJhY2tncm91bmQgPSAndHJhbnNwYXJlbnQnOyB0aGlzLnN0eWxlLnRyYW5zZm9ybSA9ICdub25lJzsgfSB9KTsKICAgICAgICB9KTsKCiAgICAgICAgZ3VpZGVMaS5wYXJlbnROb2RlLmluc2VydEJlZm9yZShjbGFzc2VzTGksIGd1aWRlTGkubmV4dFNpYmxpbmcpOwogICAgICB9CiAgICB9KTsKCiAgICAgICAgdmFyIGJ0bldyYXBzID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCgnLmFzdC1oZWFkZXItYnV0dG9uLTEnKTsKICAgIGJ0bldyYXBzLmZvckVhY2goZnVuY3Rpb24od3JhcCkgewogICAgICB2YXIgcmVhbEJ0biA9IHdyYXAucXVlcnlTZWxlY3RvcignYS5hc3QtY3VzdG9tLWJ1dHRvbi1saW5rJyk7CiAgICAgIHZhciBkdXBlcyA9IHdyYXAucXVlcnlTZWxlY3RvckFsbCgnYS5tZW51LWxpbmsnKTsKCiAgICAgIGlmIChyZWFsQnRuKSB7CiAgICAgICAgdmFyIGxhYmVsID0gcmVhbEJ0bi5xdWVyeVNlbGVjdG9yKCcuYXN0LWN1c3RvbS1idXR0b24nKTsKICAgICAgICBpZiAobGFiZWwpIGxhYmVsLnRleHRDb250ZW50ID0gJ0NvbnRhY3QgVXMnOwoKICAgICAgICByZWFsQnRuLmhyZWYgPSAnaHR0cHM6Ly9oYW5ndWxzcGFjZS5jb20vY29udGFjdC8nOwogICAgICAgIHJlYWxCdG4uc3R5bGUuZGlzcGxheSA9ICdpbmxpbmUtZmxleCc7CiAgICAgICAgcmVhbEJ0bi5zdHlsZS52aXNpYmlsaXR5ID0gJ3Zpc2libGUnOwogICAgICAgIHJlYWxCdG4uc3R5bGUuY29sb3IgPSAnIzVCNEFBMyc7CiAgICAgIH0KCiAgICAgIGR1cGVzLmZvckVhY2goZnVuY3Rpb24oZCkgewogICAgICAgIGQuc3R5bGUuZGlzcGxheSA9ICdub25lJzsKICAgICAgICBkLnN0eWxlLnZpc2liaWxpdHkgPSAnaGlkZGVuJzsKICAgICAgfSk7CiAgICB9KTsKICAgIH0KICB9CgogIHdpbmRvdy5zd2l0Y2hIc1RhYiA9IGZ1bmN0aW9uKGJ0biwgdGFyZ2V0SWQpIHsKICAgIGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoJy5ocy10b3AtdGFiLWJ0bicpLmZvckVhY2goZnVuY3Rpb24oYil7CiAgICAgIGIuY2xhc3NMaXN0LnJlbW92ZSgnYWN0aXZlJyk7CiAgICAgIGIuc2V0QXR0cmlidXRlKCdhcmlhLXNlbGVjdGVkJywgJ2ZhbHNlJyk7CiAgICB9KTsKICAgIGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoJy5ocy1zaG93Y2FzZS1jYXJkJykuZm9yRWFjaChmdW5jdGlvbihjKXsKICAgICAgYy5jbGFzc0xpc3QucmVtb3ZlKCdhY3RpdmUnKTsKICAgIH0pOwogICAgZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCgnLmhzLWludGVyYWN0aXZlLXBhbmVsJykuZm9yRWFjaChmdW5jdGlvbihwKXsKICAgICAgcC5jbGFzc0xpc3QucmVtb3ZlKCdhY3RpdmUnKTsKICAgICAgcC5zdHlsZS5kaXNwbGF5ID0gJ25vbmUnOwogICAgfSk7CiAgICBpZiAoYnRuKSB7CiAgICAgIGJ0bi5jbGFzc0xpc3QuYWRkKCdhY3RpdmUnKTsKICAgICAgYnRuLnNldEF0dHJpYnV0ZSgnYXJpYS1zZWxlY3RlZCcsICd0cnVlJyk7CiAgICB9CiAgICB2YXIgdGFyZ2V0Q2FyZCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKHRhcmdldElkKTsKICAgIGlmICh0YXJnZXRDYXJkKSB0YXJnZXRDYXJkLmNsYXNzTGlzdC5hZGQoJ2FjdGl2ZScpOwogIH07CgogIHdpbmRvdy50b2dnbGVIc1Rvb2wgPSBmdW5jdGlvbihwYW5lbElkKSB7CiAgICB2YXIgcGFuZWwgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZChwYW5lbElkKTsKICAgIGlmICghcGFuZWwpIHJldHVybjsKICAgIHZhciBpc1Zpc2libGUgPSBwYW5lbC5jbGFzc0xpc3QuY29udGFpbnMoJ2FjdGl2ZScpIHx8IHBhbmVsLnN0eWxlLmRpc3BsYXkgPT09ICdibG9jayc7CiAgICBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCcuaHMtaW50ZXJhY3RpdmUtcGFuZWwnKS5mb3JFYWNoKGZ1bmN0aW9uKHApewogICAgICBwLmNsYXNzTGlzdC5yZW1vdmUoJ2FjdGl2ZScpOwogICAgICBwLnN0eWxlLmRpc3BsYXkgPSAnbm9uZSc7CiAgICB9KTsKICAgIGlmICghaXNWaXNpYmxlKSB7CiAgICAgIHBhbmVsLmNsYXNzTGlzdC5hZGQoJ2FjdGl2ZScpOwogICAgICBwYW5lbC5zdHlsZS5kaXNwbGF5ID0gJ2Jsb2NrJzsKICAgICAgc2V0VGltZW91dChmdW5jdGlvbigpIHsKICAgICAgICB2YXIgdG9wUG9zID0gcGFuZWwuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCkudG9wICsgd2luZG93LnBhZ2VZT2Zmc2V0IC0gMTAwOwogICAgICAgIHdpbmRvdy5zY3JvbGxUbyh7IHRvcDogdG9wUG9zLCBiZWhhdmlvcjogJ3Ntb290aCcgfSk7CiAgICAgIH0sIDUwKTsKICAgIH0KICB9OwoKICBpZiAoZG9jdW1lbnQucmVhZHlTdGF0ZSA9PT0gJ2xvYWRpbmcnKSB7CiAgICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdET01Db250ZW50TG9hZGVkJywgcmVuZGVyUHVyZVdoaXRlSGVhZGVyKTsKICB9IGVsc2UgewogICAgcmVuZGVyUHVyZVdoaXRlSGVhZGVyKCk7CiAgfQogIHNldEludGVydmFsKHJlbmRlclB1cmVXaGl0ZUhlYWRlciwgMTUwKTsKCn0pKCk7\"))\" style=\"display:none !important;\" \/><\/p>\n<style>@media (min-width: 922px) { #ast-mobile-header, .ast-mobile-header-wrap, .ast-mobile-header-content, .ast-builder-menu-mobile, .main-header-menu-toggle { display: none !important; visibility: hidden !important; opacity: 0 !important; pointer-events: none !important; height: 0 !important; } } html body, #page, .site, .site-content { background-color: #F8F9FC !important; } html body #content, html body #content .ast-container, html body #primary, html body #main, html body .entry-content, html body .ast-article-single { width: 100% !important; max-width: 100% !important; padding: 0 !important; margin: 0 !important; overflow: visible !important; box-sizing: border-box !important; background-color: transparent !important; } :root { --hs-primary: #1E1B2E; --hs-purple-accent: #5B4AA3; --hs-light-bg: #F8F9FC; } .hs-subpage-container { width: 100%; max-width: 100%; margin: 0 auto; font-family: 'Inter', -apple-system, sans-serif; color: #2D3748; box-sizing: border-box; } \/* TOP BUTTON ROW (MATCHING USER SCREENSHOT EXACTLY) *\/ \/* FREE TOOLS HERO VIDEO SECTION *\/ .hs-tools-hero-wrap {   position: relative !important;   width: 100% !important;   min-height: 360px !important;   display: flex !important;   align-items: center !important;   justify-content: center !important;   text-align: center !important;   padding: 130px 24px 50px 24px !important;   background: #0F0C1D !important;   margin: 0 !important;   overflow: hidden !important; } .hs-tools-hero-wrap .video-container {   position: absolute !important;   top: 0 !important;   left: 0 !important;   width: 100% !important;   height: 100% !important;   z-index: 1 !important;   pointer-events: none !important; } .hs-tools-hero-wrap video {   width: 100% !important;   height: 100% !important;   object-fit: cover !important;   opacity: 0.45 !important;   filter: brightness(0.7) contrast(1.1) !important; } .hs-tools-hero-content {   position: relative !important;   z-index: 2 !important;   max-width: 860px !important;   margin: 0 auto !important; } .hs-tools-hero-badge {   display: inline-flex !important;   align-items: center !important;   gap: 8px !important;   padding: 6px 18px !important;   border-radius: 999px !important;   background: rgba(188, 167, 255, 0.15) !important;   border: 1px solid rgba(188, 167, 255, 0.3) !important;   color: #E7DFFF !important;   font-size: 12.5px !important;   font-weight: 700 !important;   letter-spacing: 0.8px !important;   text-transform: uppercase !important;   margin-bottom: 16px !important;   backdrop-filter: blur(10px) !important; } .hs-tools-hero-title {   color: #FFFFFF !important;   font-size: 36px !important;   font-weight: 800 !important;   letter-spacing: -0.5px !important;   line-height: 1.25 !important;   margin: 0 0 14px 0 !important;   text-shadow: 0 4px 20px rgba(0, 0, 0, 0.5) !important; } .hs-tools-hero-desc {   color: #D6CEF8 !important;   font-size: 15.5px !important;   font-weight: 400 !important;   line-height: 1.6 !important;   max-width: 660px !important;   margin: 0 auto !important; } @media (max-width: 921px) {   .hs-tools-hero-wrap {     padding: 100px 16px 40px 16px !important;     min-height: 300px !important;   }   .hs-tools-hero-title { font-size: 26px !important; }   .hs-tools-hero-desc { font-size: 14px !important; } } \/* INTERACTIVE WIZARD & PANEL STYLING *\/ .hs-interactive-panel { display: none; max-width: 1140px; margin: 30px auto 40px auto; padding: 36px 32px; background: #ffffff; border-radius: 20px; box-shadow: 0 12px 36px rgba(15, 12, 29, 0.09); border: 1px solid rgba(15, 12, 29, 0.08); scroll-margin-top: 110px !important; } .hs-interactive-panel.active { display: block !important; } .hs-form-step { display: none !important; } .hs-form-step.active { display: block !important; } .hs-step-dots { display: flex !important; align-items: center !important; justify-content: center !important; gap: 12px !important; margin: 20px 0 32px 0 !important; list-style: none !important; padding: 0 !important; } .hs-step-dot { width: 36px !important; height: 36px !important; border-radius: 50% !important; background: #EDF2F7 !important; color: #718096 !important; font-weight: 700 !important; font-size: 14px !important; display: flex !important; align-items: center !important; justify-content: center !important; transition: all 0.3s ease !important; border: 1px solid #E2E8F0 !important; } .hs-step-dot.active { background: #4A00E0 !important; color: #ffffff !important; border-color: #4A00E0 !important; box-shadow: 0 4px 12px rgba(74, 0, 224, 0.3) !important; } .hs-step-line { flex: 1 !important; max-width: 60px !important; height: 3px !important; background: #E2E8F0 !important; border-radius: 2px !important; } .hs-step-line.active { background: #4A00E0 !important; } \/* ULTRA-PREMIUM LUXURY INTERACTIVE WIZARD & PANEL DESIGN SYSTEM *\/ .hs-interactive-panel {   display: none;   max-width: 1140px;   margin: 35px auto 45px auto !important;   padding: 40px 44px !important;   background: #FFFFFF !important;   border-radius: 24px !important;   box-shadow: 0 20px 50px rgba(31, 23, 68, 0.08), 0 4px 16px rgba(0, 0, 0, 0.02) !important;   border: 1px solid rgba(110, 86, 207, 0.12) !important;   scroll-margin-top: 110px !important; } .hs-interactive-panel.active {   display: block !important; } .hs-step-dots {   display: flex !important;   flex-direction: row !important;   align-items: center !important;   justify-content: center !important;   gap: 14px !important;   margin: 16px 0 36px 0 !important;   padding: 0 !important;   list-style: none !important; } .hs-step-dot {   width: 44px !important;   height: 44px !important;   border-radius: 50% !important;   background: #F0EEFA !important;   color: #5B4AA3 !important;   font-weight: 800 !important;   font-size: 15px !important;   display: inline-flex !important;   align-items: center !important;   justify-content: center !important;   transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;   border: 2px solid #E2E0F4 !important;   box-sizing: border-box !important; } .hs-step-dot.active {   background: linear-gradient(135deg, #6E56CF 0%, #4C37A9 100%) !important;   color: #FFFFFF !important;   border-color: #6E56CF !important;   box-shadow: 0 8px 24px rgba(110, 86, 207, 0.38) !important;   transform: scale(1.08) !important; } .hs-step-line {   flex: 1 !important;   max-width: 80px !important;   height: 4px !important;   background: #E8E5F6 !important;   border-radius: 99px !important; } .hs-step-line.active {   background: linear-gradient(90deg, #6E56CF, #4C37A9) !important; } .hs-form-step {   display: none !important; } .hs-form-step.active {   display: block !important; } .hs-form-step h4 {   font-size: 20px !important;   font-weight: 800 !important;   color: #1F1744 !important;   margin: 0 0 20px 0 !important;   letter-spacing: -0.3px !important; } .hs-field-group {   margin-bottom: 24px !important; } .hs-field-group label {   display: block !important;   font-size: 13.5px !important;   font-weight: 700 !important;   color: #4A3E7D !important;   text-transform: uppercase !important;   letter-spacing: 0.6px !important;   margin-bottom: 9px !important; } .hs-field-group input[type=\"text\"], .hs-field-group input[type=\"number\"], .hs-field-group select {   width: 100% !important;   height: 52px !important;   padding: 0 20px !important;   border-radius: 14px !important;   border: 1.5px solid #E2E0F4 !important;   background: #F9F8FD !important;   font-size: 16px !important;   font-weight: 600 !important;   color: #1F1744 !important;   outline: none !important;   transition: all 0.25s ease !important;   box-shadow: 0 2px 8px rgba(0, 0, 0, 0.02) !important;   box-sizing: border-box !important; } .hs-field-group input:focus, .hs-field-group select:focus {   border-color: #6E56CF !important;   background: #FFFFFF !important;   box-shadow: 0 0 0 4px rgba(110, 86, 207, 0.16) !important; } .hs-wizard-actions {   display: flex !important;   align-items: center !important;   gap: 14px !important;   margin-top: 32px !important; } .hs-btn-next, .hs-btn-calc {   background: linear-gradient(135deg, #6E56CF 0%, #4C37A9 100%) !important;   color: #FFFFFF !important;   font-family: inherit !important;   font-weight: 700 !important;   font-size: 15px !important;   padding: 14px 32px !important;   border-radius: 14px !important;   border: none !important;   box-shadow: 0 8px 24px rgba(110, 86, 207, 0.35) !important;   cursor: pointer !important;   transition: all 0.25s ease !important;   display: inline-flex !important;   align-items: center !important;   justify-content: center !important; } .hs-btn-next:hover, .hs-btn-calc:hover {   transform: translateY(-2px) !important;   box-shadow: 0 12px 28px rgba(110, 86, 207, 0.48) !important; } .hs-btn-back {   background: #F0EEFA !important;   color: #5B4AA3 !important;   font-family: inherit !important;   font-weight: 700 !important;   font-size: 15px !important;   padding: 14px 28px !important;   border-radius: 14px !important;   border: 1px solid #D8D2F2 !important;   cursor: pointer !important;   transition: all 0.25s ease !important;   display: inline-flex !important;   align-items: center !important;   justify-content: center !important; } .hs-btn-back:hover {   background: #E4DFFA !important;   color: #3F2677 !important; } @media (max-width: 921px) {   .hs-interactive-panel { padding: 28px 22px !important; border-radius: 18px !important; }   .hs-step-dot { width: 38px !important; height: 38px !important; font-size: 13.5px !important; }   .hs-step-line { max-width: 40px !important; }   .hs-wizard-actions { flex-direction: column-reverse !important; width: 100% !important; }   .hs-btn-next, .hs-btn-calc, .hs-btn-back { width: 100% !important; } } .hs-tools-top-bar-wrap { max-width: 1140px; margin: 35px auto 30px auto; padding: 0 24px; } .hs-tools-top-bar { display: flex; gap: 14px; flex-wrap: wrap; align-items: center; justify-content: flex-start; border-bottom: 2px solid #E2E8F0; padding-bottom: 18px; } .hs-top-tab-btn { background: #FFFFFF; color: #1E1B2E; border: 1.5px solid #1E1B2E; padding: 12px 24px; border-radius: 8px; font-size: 12.5px; font-weight: 800; letter-spacing: 0.8px; text-transform: uppercase; cursor: pointer; display: inline-flex; align-items: center; gap: 8px; transition: all 0.25s ease; } .hs-top-tab-btn:hover { background: #F4F0FA; border-color: #5B4AA3; color: #5B4AA3; } .hs-top-tab-btn.active { background: #1E1B2E; color: #FFFFFF !important; border-color: #1E1B2E; box-shadow: 0 4px 14px rgba(30, 27, 46, 0.25); } \/* SECTION HEADER (SECTION 01 \/ SIGNATURE TOOLS) *\/ .hs-section-header-wrap { max-width: 1140px; margin: 0 auto 36px auto; padding: 0 24px; } .hs-section-tag-sub { color: #C53030; font-size: 12px; font-weight: 800; letter-spacing: 1.2px; text-transform: uppercase; margin-bottom: 8px; display: block; } .hs-section-main-title { font-family: 'Outfit', 'Inter', serif; font-size: 28px; font-weight: 800; color: #1E1B2E; text-transform: uppercase; letter-spacing: 0.5px; margin: 0 0 12px 0; } .hs-title-divider-line { width: 100%; height: 1.5px; background: #1E1B2E; opacity: 0.85; } \/* ACTIVE SHOWCASE CARD (MATCHING USER SCREENSHOT EXACTLY) *\/ .hs-showcase-card { max-width: 1140px; margin: 0 auto 60px auto; background: #FFFFFF; border-radius: 28px; border: 1.5px solid #E2E8F0; padding: 40px; box-shadow: 0 16px 40px rgba(0, 0, 0, 0.04); position: relative; display: none; animation: hsShowcaseFadeIn 0.35s ease; } .hs-showcase-card.active { display: block; } @keyframes hsShowcaseFadeIn { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: translateY(0); } } .hs-popular-badge { position: absolute; top: -14px; left: 40px; background: linear-gradient(135deg, #DD6B20 0%, #C05621 100%); color: #FFFFFF; font-size: 11px; font-weight: 800; letter-spacing: 1px; text-transform: uppercase; padding: 5px 16px; border-radius: 999px; box-shadow: 0 4px 12px rgba(192, 86, 33, 0.3); } .hs-showcase-grid { display: grid; grid-template-columns: 420px 1fr; gap: 40px; align-items: center; } @media (max-width: 900px) { .hs-showcase-grid { grid-template-columns: 1fr; gap: 24px; } } .hs-showcase-img-wrap { width: 100%; height: 320px; border-radius: 20px; overflow: hidden; background: #F7FAFC; border: 1px solid #E2E8F0; position: relative; } .hs-showcase-img-wrap img { width: 100%; height: 100%; object-fit: cover; } .hs-showcase-content { display: flex; flex-direction: column; justify-content: center; } .hs-cat-pill { display: inline-block; background: #FFF5F5; color: #9B2C2C; font-size: 11px; font-weight: 800; letter-spacing: 0.8px; text-transform: uppercase; padding: 4px 12px; border-radius: 999px; margin-bottom: 12px; align-self: flex-start; } .hs-showcase-title { font-size: 28px; font-weight: 800; color: #1E1B2E; margin: 0 0 6px 0; } .hs-showcase-sub { font-size: 14px; font-style: italic; color: #DD6B20; font-weight: 600; margin-bottom: 18px; } .hs-pill-options-bar { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 20px; } .hs-option-pill { background: #F7FAFC; border: 1px solid #E2E8F0; padding: 7px 16px; border-radius: 999px; font-size: 12.5px; font-weight: 700; color: #2D3748; } .hs-price-badge-bar { font-size: 22px; font-weight: 800; color: #1E1B2E; margin-bottom: 16px; display: flex; align-items: center; gap: 10px; } .hs-price-badge-bar small { font-size: 12px; color: #718096; font-weight: 600; } .hs-showcase-desc { font-size: 14.5px; color: #4A5568; line-height: 1.65; margin-bottom: 28px; } .hs-cta-btn { display: inline-flex; align-items: center; justify-content: center; gap: 10px; background: #1E1B2E; color: #FFFFFF !important; font-weight: 700; font-size: 14.5px; padding: 14px 32px; border-radius: 12px; text-decoration: none !important; transition: all 0.25s ease; border: none; cursor: pointer; align-self: flex-start; } .hs-cta-btn:hover { background: #5B4AA3; color: #FFFFFF !important; transform: translateY(-2px); box-shadow: 0 8px 24px rgba(91, 74, 163, 0.3); } \/* INTERACTIVE EXPANSION PANELS FOR INLINE TOOLS *\/ \/* INTERACTIVE WIZARD & PANEL STYLING *\/ .hs-interactive-panel { display: none; max-width: 1140px; margin: 30px auto 40px auto; padding: 36px 32px; background: #ffffff; border-radius: 20px; box-shadow: 0 12px 36px rgba(15, 12, 29, 0.09); border: 1px solid rgba(15, 12, 29, 0.08); scroll-margin-top: 110px !important; } .hs-interactive-panel.active { display: block !important; } .hs-form-step { display: none !important; } .hs-form-step.active { display: block !important; } .hs-step-dots { display: flex !important; align-items: center !important; justify-content: center !important; gap: 12px !important; margin: 20px 0 32px 0 !important; list-style: none !important; padding: 0 !important; } .hs-step-dot { width: 36px !important; height: 36px !important; border-radius: 50% !important; background: #EDF2F7 !important; color: #718096 !important; font-weight: 700 !important; font-size: 14px !important; display: flex !important; align-items: center !important; justify-content: center !important; transition: all 0.3s ease !important; border: 1px solid #E2E8F0 !important; } .hs-step-dot.active { background: #4A00E0 !important; color: #ffffff !important; border-color: #4A00E0 !important; box-shadow: 0 4px 12px rgba(74, 0, 224, 0.3) !important; } .hs-step-line { flex: 1 !important; max-width: 60px !important; height: 3px !important; background: #E2E8F0 !important; border-radius: 2px !important; } .hs-step-line.active { background: #4A00E0 !important; }<\/style>\n<div class=\"video-container\">\n    <video autoplay muted loop playsinline webkit-playsinline preload=\"metadata\"><source src=\"https:\/\/hangulspace.com\/wp-content\/uploads\/2026\/07\/hero-services-web.mp4\" type=\"video\/mp4\"><\/video>\n  <\/div>\n<div class=\"hs-tools-hero-content\">\n<h1 class=\"hs-tools-hero-title\">FREE TOOLS FOR LIFE IN KOREA<\/h1>\n<p class=\"hs-tools-hero-desc\">Practical, instant calculators and generators designed for living, studying, and working in Korea.<\/p>\n<\/p><\/div>\n<\/div>\n<div class=\"hs-subpage-container\">\n<p>  <!-- DYNAMIC TOOL SELECTOR TOP BAR (SCALABLE FOR N TOOLS) --><\/p>\n<div class=\"hs-tools-top-bar-wrap\">\n<div class=\"hs-tools-top-bar\" role=\"tablist\" aria-label=\"HangulSpace Tools Navigation\">\n      <button type=\"button\" class=\"hs-top-tab-btn active\" data-target=\"mobile-showcase\" onclick=\"switchHsTab(this, 'mobile-showcase')\" role=\"tab\" aria-selected=\"true\"><br \/>\n        \ud83d\udcf1 MOBILE PLAN<br \/>\n      <\/button><br \/>\n      <button type=\"button\" class=\"hs-top-tab-btn\" data-target=\"housing-showcase\" onclick=\"switchHsTab(this, 'housing-showcase')\" role=\"tab\" aria-selected=\"false\"><br \/>\n        \ud83c\udfe0 HOUSING RISK<br \/>\n      <\/button><br \/>\n      <button type=\"button\" class=\"hs-top-tab-btn\" data-target=\"typing-showcase\" onclick=\"switchHsTab(this, 'typing-showcase')\" role=\"tab\" aria-selected=\"false\"><br \/>\n        \u2328\ufe0f KOREAN TYPING<br \/>\n      <\/button>\n    <\/div>\n<\/p><\/div>\n<p>  <!-- SECTION HEADER --><\/p>\n<div class=\"hs-section-header-wrap\">\n    <span class=\"hs-section-tag-sub\">SECTION 01<\/span><\/p>\n<h2 class=\"hs-section-main-title\">INTERACTIVE TOOLS &#038; UTILITIES<\/h2>\n<div class=\"hs-title-divider-line\"><\/div>\n<\/p><\/div>\n<p>  <!-- TOOL 1 SHOWCASE CARD: MOBILE PLAN --><\/p>\n<div id=\"mobile-showcase\" class=\"hs-showcase-card active\" role=\"tabpanel\">\n    <span class=\"hs-popular-badge\">\u2605 INTERACTIVE TOOL<\/span><\/p>\n<div class=\"hs-showcase-grid\">\n<div class=\"hs-showcase-img-wrap\">\n        <img decoding=\"async\" src=\"https:\/\/hangulspace.com\/wp-content\/uploads\/2026\/07\/tool_card_mobile.jpg\" alt=\"Mobile Contract Exit Planner\">\n      <\/div>\n<div class=\"hs-showcase-content\">\n        <span class=\"hs-cat-pill\">MOBILE CARRIER SERVICE<\/span><\/p>\n<h3 class=\"hs-showcase-title\">Mobile Contract Exit Planner<\/h3>\n<div class=\"hs-showcase-sub\">Compare the estimated cost of switching now vs. waiting<\/div>\n<div class=\"hs-pill-options-bar\">\n          <span class=\"hs-option-pill\">SKT<\/span><br \/>\n          <span class=\"hs-option-pill\">KT<\/span><br \/>\n          <span class=\"hs-option-pill\">LGU+<\/span>\n        <\/div>\n<p class=\"hs-showcase-desc\">\n          Compare estimated cancellation fees and service costs to decide whether switching now or waiting costs less over the same period.\n        <\/p>\n<p>        <button type=\"button\" onclick=\"toggleHsTool('mobile-panel')\" class=\"hs-cta-btn\">Compare My Options &rarr;<\/button>\n      <\/div>\n<\/p><\/div>\n<\/p><\/div>\n<p>  <!-- TOOL 2 SHOWCASE CARD: HOUSING RISK --><\/p>\n<div id=\"housing-showcase\" class=\"hs-showcase-card\" role=\"tabpanel\">\n    <span class=\"hs-popular-badge\">\u2605 ESSENTIAL LEASE CHECK<\/span><\/p>\n<div class=\"hs-showcase-grid\">\n<div class=\"hs-showcase-img-wrap\">\n        <img decoding=\"async\" src=\"https:\/\/hangulspace.com\/wp-content\/uploads\/2026\/07\/tool_card_housing.jpg\" alt=\"Housing Deposit Risk Self-Check\">\n      <\/div>\n<div class=\"hs-showcase-content\">\n        <span class=\"hs-cat-pill\">RENTAL RISK CHECK<\/span><\/p>\n<h3 class=\"hs-showcase-title\">Housing Deposit Risk Self-Check<\/h3>\n<div class=\"hs-showcase-sub\">Jeonse &#038; Wolse Deposit Safety Verification<\/div>\n<div class=\"hs-pill-options-bar\">\n          <span class=\"hs-option-pill\">APARTMENT (\uc544\ud30c\ud2b8)<\/span><br \/>\n          <span class=\"hs-option-pill\">OFFICETEL (\uc624\ud53c\uc2a4\ud154)<\/span><br \/>\n          <span class=\"hs-option-pill\">VILLA (\ub2e4\uc138\ub300)<\/span>\n        <\/div>\n<p class=\"hs-showcase-desc\">\n          Check common rental risks before you sign, pay your deposit, or move into a home in Korea. Understand the Korean documents and steps that can help protect your deposit.\n        <\/p>\n<p>        <a href=\"\/tools\/housing-deposit-risk-check\/\" class=\"hs-cta-btn hs-housing-page-link\">Start Risk Check \u2192<\/a>\n      <\/div>\n<\/p><\/div>\n<\/p><\/div>\n<p>  <!-- TOOL 3 SHOWCASE CARD: KOREAN TYPING (EQUAL WEIGHT SHOWCASE) --><\/p>\n<div id=\"typing-showcase\" class=\"hs-showcase-card\" role=\"tabpanel\">\n    <span class=\"hs-popular-badge\">\u2605 FEATURED KOREAN ENGINE<\/span><\/p>\n<div class=\"hs-showcase-grid\">\n<div class=\"hs-showcase-img-wrap\">\n        <img decoding=\"async\" src=\"https:\/\/hangulspace.com\/wp-content\/uploads\/2026\/07\/cat_real_study.jpg\" alt=\"Korean Typing Speed Test\">\n      <\/div>\n<div class=\"hs-showcase-content\">\n        <span class=\"hs-cat-pill\">KOREAN LANGUAGE ENGINE<\/span><\/p>\n<h3 class=\"hs-showcase-title\">Korean Typing Speed Test<\/h3>\n<div class=\"hs-showcase-sub\">Real-Time KPM Engine &#038; ElevenLabs Audio Practice<\/div>\n<div class=\"hs-pill-options-bar\">\n          <span class=\"hs-option-pill\">30S \/ 60S MODES<\/span><br \/>\n          <span class=\"hs-option-pill\">ELEVENLABS AUDIO<\/span><br \/>\n          <span class=\"hs-option-pill\">9 LANGUAGES<\/span>\n        <\/div>\n<p class=\"hs-showcase-desc\">\n          Test your Korean typing speed (KPM), accuracy, and practice real-life Korean sentences with native ElevenLabs audio (Donggun &#038; Han 1.1x) and 9-language micro-learning translations.\n        <\/p>\n<p>        <a href=\"\/tools\/korean-typing-speed-test\/\" class=\"hs-cta-btn\">Start Typing Test \u2192<\/a>\n      <\/div>\n<\/p><\/div>\n<\/p><\/div>\n<div id=\"mobile-panel\" class=\"hs-interactive-panel\">\n<div style=\"display:flex; justify-content:space-between; align-items:center; margin-bottom:24px;\">\n<h3 style=\"margin:0; font-size:22px; color:var(--hs-dark);\">Mobile Contract Exit Planner<\/h3>\n<p>        <button type=\"button\" onclick=\"toggleHsTool('mobile-panel')\" style=\"background:none; border:none; font-size:24px; cursor:pointer; color:#718096;\">\u2715<\/button>\n      <\/div>\n<p>      <!-- INSTANT UNSUPPORTED NOTICE BOX --><\/p>\n<div id=\"m-instant-unsupported-box\" style=\"display:none; margin-bottom:24px; background:#FEF3C7;  padding:18px 20px; border-radius:12px; color:#92400E;\">\n        <strong style=\"font-size:15px; display:block; margin-bottom:4px;\" id=\"m-instant-unsupported-title\">NOT SUPPORTED BY THIS ESTIMATOR YET<\/strong><\/p>\n<p style=\"margin:0; font-size:14px; line-height:1.5;\" id=\"m-instant-unsupported-desc\">Device subsidy (\uacf5\uc2dc\uc9c0\uc6d0\uae08) contracts are not supported in this version.<\/p>\n<\/p><\/div>\n<div id=\"m-conditional-form-fields\">\n<div class=\"hs-form-grid\">\n<div class=\"hs-field-group\">\n            <label for=\"m-carrier\">Mobile Carrier<\/label><br \/>\n            <select id=\"m-carrier\" class=\"hs-input-select\" onchange=\"if(window.syncCarrierTermsAndHelpers)window.syncCarrierTermsAndHelpers();\"><option value=\"skt\">SKT<\/option><option value=\"kt\">KT<\/option><option value=\"lgu\">LG U+<\/option><option value=\"mvno\">Budget MVNO<\/option><\/select><\/p>\n<div class=\"hs-field-group\">\n            <label for=\"m-discount-type\">Contract \/ Discount Type<\/label><br \/>\n            <select id=\"m-discount-type\" class=\"hs-input-select\" onchange=\"if(window.syncCarrierTermsAndHelpers)window.syncCarrierTermsAndHelpers();\"><option value=\"plan_25\">Selection Contract Discount (\uc120\ud0dd\uc57d\uc815 25%)<\/option><option value=\"device_sub\">Carrier \/ Device Subsidy (\ud1b5\uc2e0\uc0ac \uc9c0\uc6d0\uae08)<\/option><\/select>\n          <\/div>\n<div id=\"m-selection-contract-fields\" class=\"hs-form-grid\">\n<div class=\"hs-field-group\">\n            <label for=\"m-contract-term\">Original Contract Term<\/label><br \/>\n            <select id=\"m-contract-term\" class=\"hs-input-select\" onchange=\"if(window.syncCarrierTermsAndHelpers)window.syncCarrierTermsAndHelpers();\"><option value=\"\">Select contract term<\/option><option value=\"12\">12 Months<\/option><option value=\"24\">24 Months<\/option><\/select>\n          <\/div>\n<div class=\"hs-field-group\">\n            <label for=\"m-months-left\">Remaining Contract Months<\/label><br \/>\n            <input type=\"number\" id=\"m-months-left\" class=\"hs-input-text\" min=\"0\" step=\"1\" inputmode=\"numeric\" placeholder=\"e.g. 8\">\n          <\/div>\n<div class=\"hs-field-group\">\n            <label for=\"m-actual-selection-discount\">Monthly Selection Contract Discount (\u20a9)<\/label><br \/>\n            <input type=\"number\" id=\"m-actual-selection-discount\" class=\"hs-input-text\" min=\"0\" step=\"1\" inputmode=\"numeric\" placeholder=\"e.g. 17250\"><\/p>\n<p class=\"hs-mep-help\" style=\"font-size:12px; color:#718096; margin-top:4px;\">Check your bill for the approximate monthly \uc120\ud0dd\uc57d\uc815 \ud560\uc778 amount.<\/p>\n<\/p><\/div>\n<\/div>\n<section id=\"m-device-subsidy-fields\" class=\"hs-mep-subsidy-branch\" hidden aria-hidden=\"true\">\n<div class=\"hs-mep-estimate-intro\">\n<div class=\"hs-mep-estimate-badge\">REFERENCE ESTIMATE<\/div>\n<h3>Estimate Your Device Subsidy Repayment<\/h3>\n<p class=\"hs-mep-estimate-lead\">If you received a carrier subsidy when purchasing your phone, you may need to repay part of that subsidy when cancelling early. Enter the subsidy shown on your original contract to estimate the amount.<\/p>\n<div class=\"hs-mep-rule-grid\">\n<div class=\"hs-mep-rule-item\">\n        <span class=\"hs-mep-rule-number\">01<\/span><\/p>\n<div>\n          <strong>Standard 24-Month Contract<\/strong><br \/>\n          <span>This estimator currently uses the standard 24-month device-subsidy contract model.<\/span>\n        <\/div>\n<\/p><\/div>\n<div class=\"hs-mep-rule-item\">\n        <span class=\"hs-mep-rule-number\">02<\/span><\/p>\n<div>\n          <strong>First 180 Days<\/strong><br \/>\n          <span>The full subsidy may generally be subject to repayment if the contract is cancelled during the initial period.<\/span>\n        <\/div>\n<\/p><\/div>\n<div class=\"hs-mep-rule-item\">\n        <span class=\"hs-mep-rule-number\">03<\/span><\/p>\n<div>\n          <strong>After 180 Days<\/strong><br \/>\n          <span>The estimated repayment generally decreases according to the remaining contract period.<\/span>\n        <\/div>\n<\/p><\/div>\n<\/p><\/div>\n<\/p><\/div>\n<div class=\"hs-mep-step-heading\">\n    <span>STEP 1<\/span><br \/>\n    <strong>Your Subsidy Contract<\/strong>\n  <\/div>\n<div class=\"hs-field-group\" style=\"margin-bottom:20px;\">\n    <label>ORIGINAL CONTRACT TERM<\/label><\/p>\n<div class=\"hs-mep-fixed-term\">\n      <strong>24 Months<\/strong><br \/>\n      <span>Standard device subsidy contract<\/span>\n    <\/div>\n<\/p><\/div>\n<div class=\"hs-field-group\" style=\"margin-bottom:20px;\">\n    <label for=\"m-device-subsidy-received\">DEVICE SUBSIDY RECEIVED (\u20a9)<\/label><br \/>\n    <input id=\"m-device-subsidy-received\" type=\"number\" class=\"hs-input-text\" inputmode=\"numeric\" min=\"0\" step=\"1000\" placeholder=\"e.g. 500000\" \/><\/p>\n<p class=\"hs-field-helper\">Enter the carrier device subsidy shown on your contract (\uacf5\uc2dc\uc9c0\uc6d0\uae08 \/ \uacf5\ud1b5\uc9c0\uc6d0\uae08 \/ \ub2e8\ub9d0\uc9c0\uc6d0\uae08). Do not enter your remaining phone installment balance here.<\/p>\n<\/p><\/div>\n<div class=\"hs-field-group\" style=\"margin-bottom:20px;\">\n    <label for=\"m-subsidy-contract-end\">CONTRACT END DATE<\/label><br \/>\n    <input id=\"m-subsidy-contract-end\" type=\"date\" class=\"hs-input-text\" \/><\/p>\n<p class=\"hs-field-helper\">Enter the current contract end date shown by your carrier, especially if your service has ever been suspended.<\/p>\n<\/p><\/div>\n<details class=\"hs-mep-formula-note\">\n<summary>How is this estimate calculated?<\/summary>\n<div>\n<p>For a standard 24-month subsidy contract, the estimator uses the carrier-published repayment structure:<\/p>\n<p><strong>During the first 180 days:<\/strong> up to the original subsidy amount may be repayable.<\/p>\n<p><strong>After 180 days:<\/strong> estimated subsidy repayment = original subsidy \u00d7 remaining contract days \u00f7 550 days.<\/p>\n<\/p><\/div>\n<\/details>\n<\/section>\n<div id=\"m-common-exit-fields\" class=\"hs-form-grid\">\n<div class=\"hs-field-group\">\n            <label for=\"m-current-service-cost\">Current Monthly Service Cost (\u20a9)<\/label><br \/>\n            <input type=\"number\" id=\"m-current-service-cost\" class=\"hs-input-text\" min=\"0\" step=\"1\" inputmode=\"numeric\" placeholder=\"e.g. 50000\"><\/p>\n<p class=\"hs-mep-help\" style=\"font-size:12px; color:#718096; margin-top:4px;\">Enter monthly service cost after recurring discounts. Exclude device installment.<\/p>\n<\/p><\/div>\n<div class=\"hs-field-group\">\n            <label for=\"m-need-new-service\">Will you need mobile service in Korea after cancellation?<\/label><br \/>\n            <select id=\"m-need-new-service\" class=\"hs-input-select\" onchange=\"if(window.syncCarrierTermsAndHelpers)window.syncCarrierTermsAndHelpers();\"><option value=\"\">Select<\/option><option value=\"yes\">Yes<\/option><option value=\"no\">No<\/option><\/select>\n          <\/div>\n<div class=\"hs-field-group\" id=\"m-new-service-cost-wrap\" hidden>\n            <label for=\"m-new-service-cost\">Expected New Monthly Service Cost (\u20a9)<\/label><br \/>\n            <input type=\"number\" id=\"m-new-service-cost\" class=\"hs-input-text\" min=\"0\" step=\"1\" inputmode=\"numeric\" placeholder=\"e.g. 25000\">\n          <\/div>\n<div class=\"hs-field-group\">\n            <label for=\"m-device-balance\">Remaining Device Balance (\u20a9)<\/label><br \/>\n            <input type=\"number\" id=\"m-device-balance\" class=\"hs-input-text\" min=\"0\" step=\"1\" inputmode=\"numeric\" value=\"0\" placeholder=\"e.g. 180000\">\n          <\/div>\n<\/div><\/div>\n<div style=\"text-align:center; margin-top:24px; margin-bottom:24px;\">\n          <button type=\"button\" onclick=\"calculateMobileExitPlan()\" class=\"hs-btn-primary\" style=\"width:auto; padding:14px 32px; font-size:16px; font-weight:700; border-radius:12px; background:linear-gradient(135deg, #4F46E5 0%, #7C3AED 100%); color:#fff; border:none; cursor:pointer; box-shadow:0 4px 14px rgba(79,70,229,0.35);\"><br \/>\n            Compare My Options &rarr;<br \/>\n          <\/button>\n        <\/div>\n<\/p><\/div>\n<div id=\"m-exit-planner-result\" aria-live=\"polite\"><\/div>\n<p><script id=\"hs-tools-complete-master-script\">\n\/* ==========================================================================\n   CANONICAL GOLDEN BASELINE \u2014 2026-08-15\n   STATUS: GOLDEN | CANONICAL | PRODUCTION VERIFIED\n   DO NOT overwrite this Mobile Contract Exit Planner with historical,\n   backup, scratch, or pre-device-subsidy versions.\n   Any future modification must preserve:\n   - plan_25 regression (Selection Contract 25%)\n   - device_sub branch (Carrier \/ Device Subsidy 180\/550)\n   - A-F regression test suite compatibility\n   - Reference Estimate UI notice\n   ========================================================================== *\/\n(function () {\n  'use strict';\n  const HS_MOBILE_RULES = Object.freeze({\n    SKT: {\n      label: 'SKT',\n      selectionTerms: [\n        { value: '12', label: '12 Months' },\n        { value: '24', label: '24 Months' }\n      ]\n    },\n    KT: {\n      label: 'KT',\n      selectionTerms: [\n        { value: '12', label: '12 Months' },\n        { value: '24', label: '24 Months' }\n      ]\n    },\n    LGU: {\n      label: 'LG U+',\n      selectionTerms: [\n        { value: '12', label: '12 Months' },\n        { value: '12+12', label: '12 + 12 Months' },\n        { value: '24', label: '24 Months' }\n      ]\n    }\n  });\n  const ALLOWED_CARRIERS = new Set(['SKT', 'KT', 'LGU', 'skt', 'kt', 'lgu']);\n  let lastRenderedCarrier = '';\n  function byId(id) {\n    return document.getElementById(id);\n  }\n  function formatKRW(val) {\n    const num = Math.max(0, Math.round(Number(val) || 0));\n    return 'KRW ' + num.toLocaleString('en-US');\n  }\n  function escapeHtml(str) {\n    return String(str || '')\n      .replace(\/&\/g, '&amp;')\n      .replace(\/<\/g, '&lt;')\n      .replace(\/>\/g, '&gt;')\n      .replace(\/\"\/g, '&quot;')\n      .replace(\/'\/g, '&#039;');\n  }\n  function hsNormalizeSelectionTerm(carrier, rawTerm) {\n    const carrierKey = String(carrier || '').toUpperCase();\n    const termKey = String(rawTerm || '').trim();\n    if (carrierKey === 'LGU') {\n      if (termKey === '12+12') {\n        return 12;\n      }\n    }\n    const term = Number(termKey);\n    if (term !== 12) {\n      if (term !== 24) {\n        throw new Error('Unsupported selection contract term.');\n      }\n    }\n    return term;\n  }\n  function hsEstimateSelectionReturnFee(opts) {\n    const carrier = opts ? opts.carrier : '';\n    const contractTerm = opts ? opts.contractTerm : '';\n    const remainingMonths = opts ? opts.remainingMonths : 0;\n    const monthlyDiscount = opts ? (opts.monthlyDiscount !== undefined ? opts.monthlyDiscount : opts.monthlySelectionDiscount) : 0;\n    const termMonths = hsNormalizeSelectionTerm(carrier, contractTerm);\n    const remaining = Number(remainingMonths);\n    const discount = Number(monthlyDiscount);\n    if (!Number.isFinite(remaining) || !Number.isInteger(remaining) || remaining < 0 || remaining > termMonths) {\n      throw new Error('Invalid remaining contract months.');\n    }\n    if (!Number.isFinite(discount) || discount < 0) {\n      throw new Error('Invalid monthly selection contract discount.');\n    }\n    const elapsedMonths = termMonths - remaining;\n    if (elapsedMonths <= 0 || remaining === 0) {\n      return 0;\n    }\n    const thresholdMonths = termMonths === 12 ? 3 : 6;\n    const cumulativeDiscount = discount * elapsedMonths;\n    let estimatedReturnFee;\n    if (elapsedMonths <= thresholdMonths) {\n      estimatedReturnFee = cumulativeDiscount;\n    } else {\n      estimatedReturnFee = cumulativeDiscount * (remaining \/ (termMonths - thresholdMonths));\n    }\n    if (!Number.isFinite(estimatedReturnFee)) {\n      throw new Error('Unable to calculate selection contract return fee.');\n    }\n    return Math.max(0, Math.round(estimatedReturnFee));\n  }\n  function hsEstimateSelectionReturnFeeAfterMonths(inputs, waitMonths) {\n    const wait = Number(waitMonths);\n    const currentRemaining = Number(inputs.remainingMonths);\n    if (!Number.isFinite(wait) || !Number.isInteger(wait) || wait < 0) {\n      throw new Error('Invalid wait period.');\n    }\n    if (!Number.isFinite(currentRemaining) || !Number.isInteger(currentRemaining) || currentRemaining < 0) {\n      throw new Error('Invalid remaining contract months.');\n    }\n    const futureRemaining = Math.max(0, currentRemaining - wait);\n    return hsEstimateSelectionReturnFee({\n      carrier: inputs.carrier,\n      contractTerm: inputs.contractTerm,\n      remainingMonths: futureRemaining,\n      monthlyDiscount: inputs.monthlySelectionDiscount\n    });\n  }\n  window.syncCarrierTermsAndHelpers = function () {\n    const carrierEl = byId('m-carrier');\n    const termEl = byId('m-contract-term');\n    const discountTypeEl = byId('m-discount-type');\n    const helperEl = byId('m-lgu-helper');\n    const unsupportedBox = byId('m-instant-unsupported-box');\n    const selectionFields = byId('m-selection-contract-fields');\n    const subsidyFields = byId('m-device-subsidy-fields');\n    const commonFields = byId('m-common-exit-fields');\n    const needServiceEl = byId('m-need-new-service');\n    const newServiceCostWrap = byId('m-new-service-cost-wrap');\n    if (!carrierEl || !termEl) return;\n    const carrierKey = String(carrierEl.value || 'SKT').toUpperCase();\n    if (lastRenderedCarrier !== carrierKey) {\n      lastRenderedCarrier = carrierKey;\n      const carrierRule = HS_MOBILE_RULES[carrierKey] || HS_MOBILE_RULES.SKT;\n      const currentSelectedVal = termEl.value;\n      termEl.innerHTML = '<option value=\"\">Select contract term<\/option>';\n      carrierRule.selectionTerms.forEach(opt => {\n        const optionEl = document.createElement('option');\n        optionEl.value = opt.value;\n        optionEl.textContent = opt.label;\n        if (opt.value === currentSelectedVal) {\n          optionEl.selected = true;\n        }\n        termEl.appendChild(optionEl);\n      });\n    }\n    if (helperEl) {\n      if (carrierKey === 'LGU') {\n        if (termEl.value === '12+12') {\n          helperEl.style.display = 'block';\n          helperEl.innerHTML = 'For LG U+ 12+12, enter the months remaining in your current 12-month contract cycle, not the combined 24 months.';\n        } else {\n          helperEl.style.display = 'none';\n        }\n      } else {\n        helperEl.style.display = 'none';\n      }\n    }\n    if (unsupportedBox) {\n      unsupportedBox.setAttribute('hidden', '');\n      unsupportedBox.style.display = 'none';\n    }\n    if (discountTypeEl) {\n      const isSubsidy = discountTypeEl.value === 'device_sub';\n      if (selectionFields) {\n        if (isSubsidy) {\n          selectionFields.setAttribute('hidden', '');\n          selectionFields.style.display = 'none';\n        } else {\n          selectionFields.removeAttribute('hidden');\n          selectionFields.style.display = 'block';\n        }\n      }\n      if (subsidyFields) {\n        if (isSubsidy) {\n          subsidyFields.removeAttribute('hidden');\n          subsidyFields.style.display = 'block';\n          subsidyFields.setAttribute('aria-hidden', 'false');\n        } else {\n          subsidyFields.setAttribute('hidden', '');\n          subsidyFields.style.display = 'none';\n          subsidyFields.setAttribute('aria-hidden', 'true');\n        }\n      }\n      if (commonFields) {\n        commonFields.removeAttribute('hidden');\n        commonFields.style.display = 'block';\n      }\n    }\n    if (needServiceEl) {\n      if (newServiceCostWrap) {\n        if (needServiceEl.value === 'yes') {\n          newServiceCostWrap.removeAttribute('hidden');\n          newServiceCostWrap.style.setProperty('display', 'block', 'important');\n        } else {\n          newServiceCostWrap.setAttribute('hidden', '');\n          newServiceCostWrap.style.setProperty('display', 'none', 'important');\n        }\n      }\n    }\n  };\n  function bindMobilePlannerEvents() {\n    document.addEventListener('change', function(e) {\n      if (e.target) {\n        if (['m-carrier', 'm-contract-term', 'm-discount-type', 'm-need-new-service'].includes(e.target.id)) {\n          window.syncCarrierTermsAndHelpers();\n        }\n      }\n    }, true);\n    const carrierEl = byId('m-carrier');\n    const termEl = byId('m-contract-term');\n    const discountTypeEl = byId('m-discount-type');\n    const needServiceEl = byId('m-need-new-service');\n    if (carrierEl) carrierEl.addEventListener('change', window.syncCarrierTermsAndHelpers);\n    if (termEl) termEl.addEventListener('change', window.syncCarrierTermsAndHelpers);\n    if (discountTypeEl) discountTypeEl.addEventListener('change', window.syncCarrierTermsAndHelpers);\n    if (needServiceEl) needServiceEl.addEventListener('change', window.syncCarrierTermsAndHelpers);\n    window.syncCarrierTermsAndHelpers();\n  }\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', bindMobilePlannerEvents);\n  } else {\n    bindMobilePlannerEvents();\n  }\n  function readRequiredNumber(fieldId, fieldLabel, opts) {\n    const options = opts || {};\n    const el = byId(fieldId);\n    if (!el) {\n      return { ok: false, field: fieldId, message: fieldLabel + ' field is missing.' };\n    }\n    const raw = String(el.value || '').trim();\n    if (raw === '') {\n      return { ok: false, field: fieldId, message: 'Please enter ' + fieldLabel.toLowerCase() + '.' };\n    }\n    const num = Number(raw);\n    if (!Number.isFinite(num)) {\n      return { ok: false, field: fieldId, message: 'Please enter a valid number for ' + fieldLabel.toLowerCase() + '.' };\n    }\n    if (options.integer) {\n      if (!Number.isInteger(num)) {\n        return { ok: false, field: fieldId, message: fieldLabel + ' must be a whole number.' };\n      }\n    }\n    if (typeof options.min === 'number') {\n      if (num < options.min) {\n        return { ok: false, field: fieldId, message: fieldLabel + ' cannot be below ' + options.min + '.' };\n      }\n    }\n    if (typeof options.max === 'number') {\n      if (num > options.max) {\n        return { ok: false, field: fieldId, message: fieldLabel + ' cannot exceed ' + options.max + '.' };\n      }\n    }\n    return { ok: true, value: num };\n  }\n  function collectMobilePlannerInputs() {\n    const carrierEl = byId('m-carrier');\n    const discountTypeEl = byId('m-discount-type');\n    const termEl = byId('m-contract-term');\n    const needServiceEl = byId('m-need-new-service');\n    if (!carrierEl) return { ok: false, message: 'The calculator form is incomplete.' };\n    if (!discountTypeEl) return { ok: false, message: 'The calculator form is incomplete.' };\n    if (!needServiceEl) return { ok: false, message: 'The calculator form is incomplete.' };\n    const rawCarrier = String(carrierEl.value || '').trim();\n    const carrier = rawCarrier.toUpperCase();\n    if (!ALLOWED_CARRIERS.has(carrier)) {\n      if (!ALLOWED_CARRIERS.has(rawCarrier)) {\n        return { ok: false, message: 'This estimator currently supports SKT, KT, and LG U+ only.' };\n      }\n    }\n    const discountType = discountTypeEl.value;\n    if (discountType === 'device_sub') {\n      const subsidyRes = readRequiredNumber('m-device-subsidy-received', 'Device subsidy received', { min: 0 });\n      if (!subsidyRes.ok) return subsidyRes;\n      const endDateEl = byId('m-subsidy-contract-end');\n      if (!endDateEl) return { ok: false, field: 'm-subsidy-contract-end', message: 'Please select your current contract end date.' };\n      if (!endDateEl.value) return { ok: false, field: 'm-subsidy-contract-end', message: 'Please select your current contract end date.' };\n      const today = new Date();\n      today.setHours(0, 0, 0, 0);\n      const contractEnd = new Date(endDateEl.value + 'T00:00:00');\n      if (Number.isNaN(contractEnd.getTime())) {\n        return { ok: false, field: 'm-subsidy-contract-end', message: 'Please enter a valid contract end date.' };\n      }\n      const DAY = 86400000;\n      const remainingDays = Math.max(0, Math.round((contractEnd - today) \/ DAY));\n      const remainingMonths = Math.max(1, Math.ceil(remainingDays \/ 30.4375));\n      const currentCost = readRequiredNumber('m-current-service-cost', 'Current monthly service cost', { min: 0 });\n      if (!currentCost.ok) return currentCost;\n      const needService = needServiceEl.value;\n      if (needService !== 'yes') {\n        if (needService !== 'no') {\n          return { ok: false, field: 'm-need-new-service', message: 'Please select whether you will need mobile service after cancellation.' };\n        }\n      }\n      let newServiceCost = 0;\n      if (needService === 'yes') {\n        const newCost = readRequiredNumber('m-new-service-cost', 'Expected new monthly service cost', { min: 0 });\n        if (!newCost.ok) return newCost;\n        newServiceCost = newCost.value;\n      }\n      const deviceBalance = readRequiredNumber('m-device-balance', 'Remaining device balance', { min: 0 });\n      if (!deviceBalance.ok) return deviceBalance;\n      return {\n        ok: true,\n        carrier: carrier,\n        discountType: 'device_sub',\n        subsidyReceived: subsidyRes.value,\n        contractEndDate: endDateEl.value,\n        remainingDays: remainingDays,\n        remainingMonths: remainingMonths,\n        currentServiceCost: currentCost.value,\n        needService: needService,\n        newServiceCost: newServiceCost,\n        deviceBalance: deviceBalance.value\n      };\n    }\n    if (!termEl) return { ok: false, message: 'The calculator form is incomplete.' };\n    if (termEl.value === '') {\n      return { ok: false, field: 'm-contract-term', message: 'Please select your original contract term.' };\n    }\n    const contractTermStr = termEl.value;\n    let normalizedTerm;\n    try {\n      normalizedTerm = hsNormalizeSelectionTerm(carrier, contractTermStr);\n    } catch (err) {\n      return { ok: false, field: 'm-contract-term', message: err.message };\n    }\n    const discount = readRequiredNumber('m-actual-selection-discount', 'Monthly Selection Contract discount', { min: 0 });\n    if (!discount.ok) return discount;\n    const remaining = readRequiredNumber('m-months-left', 'Remaining contract months', { min: 1, max: normalizedTerm, integer: true });\n    if (!remaining.ok) return remaining;\n    const currentCost = readRequiredNumber('m-current-service-cost', 'Current monthly service cost', { min: 0 });\n    if (!currentCost.ok) return currentCost;\n    const needService = needServiceEl.value;\n    if (needService !== 'yes') {\n      if (needService !== 'no') {\n        return { ok: false, field: 'm-need-new-service', message: 'Please select whether you will need mobile service after cancellation.' };\n      }\n    }\n    let newServiceCost = 0;\n    if (needService === 'yes') {\n      const newCost = readRequiredNumber('m-new-service-cost', 'Expected new monthly service cost', { min: 0 });\n      if (!newCost.ok) return newCost;\n      newServiceCost = newCost.value;\n    }\n    const deviceBalance = readRequiredNumber('m-device-balance', 'Remaining device balance', { min: 0 });\n    if (!deviceBalance.ok) return deviceBalance;\n    return {\n      ok: true,\n      carrier: carrier,\n      discountType: 'plan_25',\n      contractTerm: contractTermStr,\n      normalizedTermMonths: normalizedTerm,\n      monthlySelectionDiscount: discount.value,\n      remainingMonths: remaining.value,\n      currentServiceCost: currentCost.value,\n      needService: needService,\n      newServiceCost: newServiceCost,\n      deviceBalance: deviceBalance.value\n    };\n  }\n  function addCalendarMonths(baseDate, months) {\n    const d = new Date(baseDate.getTime());\n    const targetMonth = d.getMonth() + months;\n    d.setMonth(targetMonth);\n    return d;\n  }\n  function hsCalculateSubsidyRepayment(subsidy, remDays) {\n    const CONTRACT_DAYS = 730;\n    const INITIAL_PERIOD_DAYS = 180;\n    const PRORATE_DAYS = 550;\n    if (remDays <= 0) return 0;\n    const elapsedDays = Math.max(0, CONTRACT_DAYS - remDays);\n    if (elapsedDays < INITIAL_PERIOD_DAYS) {\n      return subsidy;\n    }\n    return Math.round(subsidy * Math.min(remDays, PRORATE_DAYS) \/ PRORATE_DAYS);\n  }\n  function calculateExitPlannerScenarios(inputs) {\n    try {\n      let cancelFeeNow = 0;\n      if (inputs.discountType === 'device_sub') {\n        cancelFeeNow = hsCalculateSubsidyRepayment(inputs.subsidyReceived, inputs.remainingDays);\n      } else {\n        cancelFeeNow = hsEstimateSelectionReturnFee({\n          carrier: inputs.carrier,\n          contractTerm: inputs.contractTerm,\n          remainingMonths: inputs.remainingMonths,\n          monthlyDiscount: inputs.monthlySelectionDiscount\n        });\n      }\n      const horizonMonths = inputs.remainingMonths;\n      const calcHorizon = function(horizon) {\n        const months = Math.min(horizon, inputs.remainingMonths);\n        let cancelNowService = 0;\n        if (inputs.needService === 'yes') {\n          cancelNowService = inputs.newServiceCost * months;\n        }\n        const cancelNowTotal = cancelFeeNow + cancelNowService;\n        let stayFeeAtEnd = 0;\n        let stayService = 0;\n        if (months < inputs.remainingMonths) {\n          if (inputs.discountType === 'device_sub') {\n            const today = new Date();\n            today.setHours(0, 0, 0, 0);\n            const waitDate = addCalendarMonths(today, months);\n            const contractEnd = new Date(inputs.contractEndDate + 'T00:00:00');\n            const DAY = 86400000;\n            const futureRemDays = Math.max(0, Math.round((contractEnd - waitDate) \/ DAY));\n            stayFeeAtEnd = hsCalculateSubsidyRepayment(inputs.subsidyReceived, futureRemDays);\n          } else {\n            stayFeeAtEnd = hsEstimateSelectionReturnFeeAfterMonths(inputs, months);\n          }\n          stayService = inputs.currentServiceCost * months;\n        } else {\n          stayFeeAtEnd = 0;\n          stayService = inputs.currentServiceCost * inputs.remainingMonths;\n        }\n        const stayTotal = stayFeeAtEnd + stayService;\n        const diff = Math.abs(cancelNowTotal - stayTotal);\n        let lower = 'Equal Cost';\n        if (cancelNowTotal < stayTotal) lower = 'Cancel Now';\n        else if (stayTotal < cancelNowTotal) {\n          lower = (horizon === 3 ? (inputs.remainingMonths > 3 ? 'Wait 3 Months' : 'Stay Until Contract End') : 'Stay Until Contract End');\n        }\n        return {\n          horizonMonths: months,\n          cancelNow: { cancellationFee: cancelFeeNow, serviceCost: cancelNowService, total: cancelNowTotal },\n          wait: { cancellationFee: stayFeeAtEnd, serviceCost: stayService, total: stayTotal },\n          difference: diff,\n          lower: lower\n        };\n      };\n      const comparison3m = inputs.remainingMonths > 3 ? calcHorizon(3) : null;\n      const comparisonEnd = calcHorizon(horizonMonths);\n      return {\n        ok: true,\n        discountType: inputs.discountType,\n        deviceBalance: inputs.deviceBalance,\n        comparison3m: comparison3m,\n        comparisonEnd: comparisonEnd\n      };\n    } catch (err) {\n      return { ok: false, error: err.message };\n    }\n  }\n  function renderMobilePlannerMessage(opts) {\n    const result = byId('m-exit-planner-result');\n    if (!result) return;\n    if (opts.deviceSubsidyMode) {\n      result.innerHTML = `<\/p>\n<div class=\"hs-mep-result\" style=\"background:#FFF9E6; border:1px solid #FFE58F; color:#593800; padding:24px; border-radius:18px; margin-top:24px;\">\n<h4 style=\"margin:0 0 10px 0; font-size:16px; color:#8C5400; font-weight:700;\">Carrier \/ Device Subsidy Notice<\/h4>\n<p style=\"margin:0 0 10px 0; font-size:14px; line-height:1.5;\">Carrier subsidy cancellation rules can vary by carrier, subsidy type, and agreement. Check your carrier app or bill for the current return fee before comparing.<\/p>\n<p style=\"margin:0; font-size:13px; line-height:1.5; color:#734600; font-style:italic;\">\ud1b5\uc2e0\uc0ac \uc9c0\uc6d0\uae08 \ubc18\ud658\uae08\uc740 \ud1b5\uc2e0\uc0ac\uc640 \uc9c0\uc6d0\uae08 \uc720\ud615, \uac00\uc785 \uc870\uac74\uc5d0 \ub530\ub77c \ub2ec\ub77c\uc9c8 \uc218 \uc788\uc2b5\ub2c8\ub2e4. \uc815\ud655\ud55c \ud604\uc7ac \ubc18\ud658\uae08\uc740 \ud1b5\uc2e0\uc0ac \uc571 \ub610\ub294 \uccad\uad6c\uc11c\uc5d0\uc11c \ud655\uc778\ud574 \uc8fc\uc138\uc694.<\/p>\n<\/p><\/div>\n<p>      `;\n      return;\n    }\n    const title = opts.unsupported ? 'MODE NOT SUPPORTED' : 'PLEASE CHECK YOUR INPUT';\n    const bg = opts.unsupported ? '#FFF4E5' : '#FFF0F0';\n    const border = opts.unsupported ? '#FFD8A8' : '#FFC9C9';\n    const color = opts.unsupported ? '#663C00' : '#C53030';\n    result.innerHTML = `<\/p>\n<div class=\"hs-mep-result\" style=\"background:${bg}; border:1px solid ${border}; color:${color}; padding:20px; border-radius:16px; margin-top:24px;\">\n        <strong style=\"display:block; font-size:14px; font-weight:700; margin-bottom:6px;\">${escapeHtml(title)}<\/strong>\n        <span style=\"font-size:13.5px; line-height:1.5;\">${escapeHtml(opts.message)}<\/span>\n      <\/div>\n<p>    `;\n  }\n  function renderExitPlannerResult(inputs, data) {\n    const result = byId('m-exit-planner-result');\n    if (!result) return;\n    const isSubsidy = inputs.discountType === 'device_sub';\n    const feeNowLabel = isSubsidy ? 'Estimated Subsidy Repayment' : 'Estimated Cancellation Fee';\n    const feeWaitLabel = isSubsidy ? 'Estimated Subsidy Repayment (Month 3)' : 'Estimated Cancellation Fee (Month 3)';\n    const feeEndLabel = isSubsidy ? 'Estimated Subsidy Repayment at End' : 'Estimated Cancellation Fee at End';\n    let comparison3mHtml = '';\n    if (data.comparison3m) {\n      const c = data.comparison3m;\n      comparison3mHtml = `<\/p>\n<section class=\"hs-mep-comparison\" style=\"margin-top:24px; background:#F8FAFC; border:1px solid #E2E8F0; padding:20px; border-radius:16px;\">\n<h4 style=\"margin:0 0 4px 0; font-size:16px; color:#1E293B; letter-spacing:0.5px;\">3-MONTH COMPARISON<\/h4>\n<p style=\"margin:0 0 16px 0; font-size:13px; color:#64748B;\">Compare what you may spend over the next 3 months.<\/p>\n<div class=\"hs-mep-grid\" style=\"display:grid; grid-template-columns:repeat(auto-fit, minmax(260px, 1fr)); gap:16px;\">\n<article class=\"hs-mep-scenario\" style=\"background:#FFFFFF; border:1px solid #CBD5E1; padding:16px; border-radius:12px;\">\n<h5 style=\"margin:0 0 12px 0; font-size:15px; color:#334155;\">CANCEL NOW<\/h5>\n<div class=\"hs-mep-row\" style=\"display:flex; justify-content:space-between; margin-bottom:8px; font-size:13.5px; color:#64748B;\">\n                <span>${escapeHtml(feeNowLabel)}<\/span>\n                <strong>${formatKRW(c.cancelNow.cancellationFee)}<\/strong>\n              <\/div>\n<div class=\"hs-mep-row\" style=\"display:flex; justify-content:space-between; margin-bottom:12px; font-size:13.5px; color:#64748B;\">\n                <span>Replacement Service Cost - 3 Months<\/span>\n                <strong>${formatKRW(c.cancelNow.serviceCost)}<\/strong>\n              <\/div>\n<div class=\"hs-mep-total\" style=\"display:flex; justify-content:space-between; border-top:1px solid #E2E8F0; padding-top:10px; font-size:14.5px; font-weight:700; color:#0F172A;\">\n                <span>Estimated 3-Month Cost<\/span>\n                <strong>${formatKRW(c.cancelNow.total)}<\/strong>\n              <\/div>\n<\/article>\n<article class=\"hs-mep-scenario\" style=\"background:#FFFFFF; border:1px solid #CBD5E1; padding:16px; border-radius:12px;\">\n<h5 style=\"margin:0 0 12px 0; font-size:15px; color:#334155;\">WAIT 3 MONTHS<\/h5>\n<div class=\"hs-mep-row\" style=\"display:flex; justify-content:space-between; margin-bottom:8px; font-size:13.5px; color:#64748B;\">\n                <span>Current Service Cost - 3 Months<\/span>\n                <strong>${formatKRW(c.wait.serviceCost)}<\/strong>\n              <\/div>\n<div class=\"hs-mep-row\" style=\"display:flex; justify-content:space-between; margin-bottom:12px; font-size:13.5px; color:#64748B;\">\n                <span>${escapeHtml(feeWaitLabel)}<\/span>\n                <strong>${formatKRW(c.wait.cancellationFee)}<\/strong>\n              <\/div>\n<div class=\"hs-mep-total\" style=\"display:flex; justify-content:space-between; border-top:1px solid #E2E8F0; padding-top:10px; font-size:14.5px; font-weight:700; color:#0F172A;\">\n                <span>Estimated 3-Month Cost<\/span>\n                <strong>${formatKRW(c.wait.total)}<\/strong>\n              <\/div>\n<\/article><\/div>\n<div class=\"hs-mep-difference\" style=\"margin-top:14px; background:#F1F5F9; padding:12px 16px; border-radius:10px; font-size:13.5px; color:#334155; line-height:1.6;\">\n            Estimated cost difference: <strong style=\"color:#0F172A; font-size:15px;\">${formatKRW(c.difference)}<\/strong><br \/>\n            Lower estimated cost in this comparison: <strong style=\"color:#2563EB; font-size:14px;\">${escapeHtml(c.lower)}<\/strong>\n          <\/div>\n<\/section>\n<p>      `;\n    }\n    const e = data.comparisonEnd;\n    const rawHorizon = Number(e.horizonMonths);\n    if (!Number.isFinite(rawHorizon) || rawHorizon <= 0) {\n      return;\n    }\n    const horizonMonths = Math.round(rawHorizon);\n    const comparisonEndHtml = `\n      \n\n<section class=\"hs-mep-comparison\" style=\"margin-top:24px; background:#F8FAFC; border:1px solid #E2E8F0; padding:20px; border-radius:16px;\">\n<h4 style=\"margin:0 0 4px 0; font-size:16px; color:#1E293B; letter-spacing:0.5px;\">UNTIL CURRENT CONTRACT END (${horizonMonths} MONTHS)<\/h4>\n<p style=\"margin:0 0 16px 0; font-size:13px; color:#64748B;\">Compare what you may spend until your contract ends.<\/p>\n<div class=\"hs-mep-grid\" style=\"display:grid; grid-template-columns:repeat(auto-fit, minmax(260px, 1fr)); gap:16px;\">\n<article class=\"hs-mep-scenario\" style=\"background:#FFFFFF; border:1px solid #CBD5E1; padding:16px; border-radius:12px;\">\n<h5 style=\"margin:0 0 12px 0; font-size:15px; color:#334155;\">CANCEL NOW<\/h5>\n<div class=\"hs-mep-row\" style=\"display:flex; justify-content:space-between; margin-bottom:8px; font-size:13.5px; color:#64748B;\">\n              <span>${escapeHtml(feeNowLabel)}<\/span>\n              <strong>${formatKRW(e.cancelNow.cancellationFee)}<\/strong>\n            <\/div>\n<div class=\"hs-mep-row\" style=\"display:flex; justify-content:space-between; margin-bottom:12px; font-size:13.5px; color:#64748B;\">\n              <span>Replacement Service Cost - ${horizonMonths} Months<\/span>\n              <strong>${formatKRW(e.cancelNow.serviceCost)}<\/strong>\n            <\/div>\n<div class=\"hs-mep-total\" style=\"display:flex; justify-content:space-between; border-top:1px solid #E2E8F0; padding-top:10px; font-size:14.5px; font-weight:700; color:#0F172A;\">\n              <span>Estimated Cost Until Contract End<\/span>\n              <strong>${formatKRW(e.cancelNow.total)}<\/strong>\n            <\/div>\n<\/article>\n<article class=\"hs-mep-scenario\" style=\"background:#FFFFFF; border:1px solid #CBD5E1; padding:16px; border-radius:12px;\">\n<h5 style=\"margin:0 0 12px 0; font-size:15px; color:#334155;\">STAY UNTIL CONTRACT END<\/h5>\n<div class=\"hs-mep-row\" style=\"display:flex; justify-content:space-between; margin-bottom:8px; font-size:13.5px; color:#64748B;\">\n              <span>Current Service Cost - ${horizonMonths} Months<\/span>\n              <strong>${formatKRW(e.wait.serviceCost)}<\/strong>\n            <\/div>\n<div class=\"hs-mep-row\" style=\"display:flex; justify-content:space-between; margin-bottom:12px; font-size:13.5px; color:#64748B;\">\n              <span>${escapeHtml(feeEndLabel)}<\/span>\n              <strong>${formatKRW(e.wait.cancellationFee)}<\/strong>\n            <\/div>\n<div class=\"hs-mep-total\" style=\"display:flex; justify-content:space-between; border-top:1px solid #E2E8F0; padding-top:10px; font-size:14.5px; font-weight:700; color:#0F172A;\">\n              <span>Estimated Cost Until Contract End<\/span>\n              <strong>${formatKRW(e.wait.total)}<\/strong>\n            <\/div>\n<\/article><\/div>\n<div class=\"hs-mep-difference\" style=\"margin-top:14px; background:#F1F5F9; padding:12px 16px; border-radius:10px; font-size:13.5px; color:#334155; line-height:1.6;\">\n          Estimated cost difference: <strong style=\"color:#0F172A; font-size:15px;\">${formatKRW(e.difference)}<\/strong><br \/>\n          Lower estimated cost in this comparison: <strong style=\"color:#2563EB; font-size:14px;\">${escapeHtml(e.lower)}<\/strong>\n        <\/div>\n<\/section>\n<p>    `;\n    result.innerHTML = `<\/p>\n<div class=\"hs-mep-result\" style=\"background:linear-gradient(135deg, #2D1F58 0%, #171033 100%); color:#fff; padding:28px; border-radius:20px; margin-top:24px; box-shadow:0 12px 36px rgba(0,0,0,0.25);\">\n<header class=\"hs-mep-result-header\" style=\"margin-bottom:20px;\">\n<p class=\"hs-mep-eyebrow\" style=\"font-size:12px; font-weight:700; letter-spacing:1px; color:#BCA7FF; margin:0 0 6px 0;\">MOBILE CONTRACT EXIT PLANNER<\/p>\n<h3 style=\"margin:0; font-size:20px; font-weight:700; color:#FFFFFF; line-height:1.4;\">Would switching now or waiting likely cost less over the same period?<\/h3>\n<\/header>\n<p>        ${comparison3mHtml}\n        ${comparisonEndHtml}<\/p>\n<section class=\"hs-mep-device\" style=\"margin-top:24px; background:rgba(255,255,255,0.08); border-radius:14px; padding:18px;\">\n<h4 style=\"margin:0 0 12px 0; font-size:15px; color:#D6CEF8;\">DEVICE FINANCING<\/h4>\n<div class=\"hs-mep-row\" style=\"display:flex; justify-content:space-between; font-size:15px; font-weight:700; color:#FFFFFF; margin-bottom:8px;\">\n            <span>Remaining Device Balance<\/span>\n            <strong>${formatKRW(data.deviceBalance)}<\/strong>\n          <\/div>\n<p style=\"margin:0; font-size:12.5px; color:#A899E6; line-height:1.5;\">Shown separately because the remaining device balance is not included in the cost comparison above.<\/p>\n<\/section>\n<div class=\"hs-mep-reference-notice\" style=\"margin-top:22px; background:rgba(188, 167, 255, 0.08); border:1px solid rgba(188, 167, 255, 0.2); border-radius:12px; padding:16px 18px; display:flex; gap:12px; align-items:flex-start; color:#D6CEF8; text-shadow:none;\">\n          <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#BCA7FF\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"flex:0 0 auto; margin-top:2px;\"><circle cx=\"12\" cy=\"12\" r=\"10\"\/><line x1=\"12\" y1=\"16\" x2=\"12\" y2=\"12\"\/><line x1=\"12\" y1=\"8\" x2=\"12.01\" y2=\"8\"\/><\/svg><\/p>\n<div style=\"font-size:12.5px; line-height:1.6; color:#D6CEF8;\">\n            <strong style=\"display:block; color:#E8E3FF; font-weight:700; font-size:13px; margin-bottom:4px;\">Reference estimate only.<\/strong>\n            <span>This tool provides an approximate comparison based on the information you entered and general carrier formulas. Actual cancellation fees, subsidy repayments, device balances, and service charges may differ depending on your carrier, contract terms, activation date, plan or device changes, suspension periods, promotions, and additional subsidies. Please confirm the final amount with your carrier before cancelling or switching.<\/span>\n          <\/div>\n<\/p><\/div>\n<\/p><\/div>\n<p>    `;\n  }\n  window.calculateMobileExitPlan = function () {\n    const inputs = collectMobilePlannerInputs();\n    if (!inputs.ok) {\n      renderMobilePlannerMessage({\n        message: inputs.message,\n        unsupported: Boolean(inputs.unsupported),\n        deviceSubsidyMode: Boolean(inputs.deviceSubsidyMode)\n      });\n      if (inputs.field) {\n        const el = byId(inputs.field);\n        if (el) {\n          el.focus();\n          el.setAttribute('aria-invalid', 'true');\n        }\n      }\n      return;\n    }\n    document.querySelectorAll('#mobile-panel [aria-invalid=\"true\"]').forEach(function(el) {\n      el.removeAttribute('aria-invalid');\n    });\n    const calculated = calculateExitPlannerScenarios(inputs);\n    if (!calculated.ok) {\n      renderMobilePlannerMessage({\n        message: calculated.error || 'Unable to calculate this estimate.'\n      });\n      return;\n    }\n    renderExitPlannerResult(inputs, calculated);\n  };\n  function renderPureWhiteHeader() {\n    if (!document.getElementById('hs-dropdown-bridge-style')) {\n      var bridgeStyle = document.createElement('style');\n      bridgeStyle.id = 'hs-dropdown-bridge-style';\n      bridgeStyle.innerHTML = '@media (min-width: 922px){ .hs-classes-nav-item > .hs-classes-row > a, .hs-classes-nav-item a, .hs-classes-nav-item .menu-text{color:#FFFFFF !important;} .hs-classes-toggle-btn{display:none !important;} .hs-dropdown-content a{color:#FFFFFF !important; font-weight:700 !important; font-size:13.5px !important;} .hs-dropdown-content::before{content:\"\" !important; position:absolute !important; top:-14px !important; left:0 !important; width:100% !important; height:14px !important; background:transparent !important;} .hs-dropdown-menu:hover .hs-dropdown-content, .hs-dropdown-content:hover{display:block !important;} } @media (max-width: 921px){ .hs-classes-nav-item > .hs-classes-row > a, .hs-classes-nav-item a, .hs-classes-nav-item .menu-text{color:#211C35 !important; font-weight:700 !important;} .hs-classes-row{display:flex !important; align-items:center !important; justify-content:space-between !important; width:100% !important;} .hs-classes-toggle-btn{display:inline-flex !important; align-items:center !important; justify-content:center !important; width:44px !important; height:44px !important; background:#F4F0FA !important; border:1px solid #EBE5F5 !important; border-radius:10px !important; color:#5B4AA3 !important; cursor:pointer !important; margin-right:8px !important;} .hs-classes-nav-item.hs-accordion-expanded .hs-classes-toggle-btn{background:#5B4AA3 !important; color:#FFFFFF !important;} .hs-classes-nav-item.hs-accordion-expanded .hs-classes-toggle-btn svg{transform:rotate(180deg) !important;} .hs-classes-nav-item .hs-dropdown-content{position:static !important; transform:none !important; display:none !important;} .hs-classes-nav-item.hs-accordion-expanded .hs-dropdown-content{display:block !important; visibility:visible !important; opacity:1 !important; height:auto !important; background:#F4F0FA !important; border:1px solid #EBE5F5 !important; border-radius:12px !important; margin:8px 0 12px 0 !important; padding:6px 8px !important;} .hs-classes-nav-item.hs-accordion-expanded .hs-dropdown-content a{color:#5B4AA3 !important; background:#FFFFFF !important; border:1px solid #DDD6F3 !important; display:block !important; padding:9px 14px !important; margin:4px 0 !important; border-radius:8px !important; font-size:13.5px !important; font-weight:700 !important; text-shadow:none !important;} } #mobile-panel .hs-mep-subsidy-branch{margin-top:28px;} #mobile-panel .hs-mep-estimate-intro{padding:26px 28px; margin-bottom:34px; border:1px solid rgba(105, 83, 220, 0.18); border-radius:18px; background:linear-gradient(135deg, rgba(247, 245, 255, 0.96), rgba(255, 255, 255, 0.98));} #mobile-panel .hs-mep-estimate-badge{display:inline-flex; align-items:center; padding:7px 11px; margin-bottom:13px; border-radius:999px; background:rgba(105, 83, 220, 0.10); color:#5947b5; font-size:11px; font-weight:800; letter-spacing:0.08em;} #mobile-panel .hs-mep-estimate-intro h3{margin:0 0 10px; color:#20283b; font-size:21px; font-weight:800; line-height:1.3;} #mobile-panel .hs-mep-estimate-lead{max-width:850px; margin:0; color:#61708a; font-size:14px; line-height:1.65;} #mobile-panel .hs-mep-rule-grid{display:grid; grid-template-columns:repeat(3, 1fr); gap:12px; margin-top:22px;} #mobile-panel .hs-mep-rule-item{display:flex; gap:11px; padding:15px; border:1px solid rgba(105, 83, 220, 0.13); border-radius:13px; background:rgba(255, 255, 255, 0.78);} #mobile-panel .hs-mep-rule-number{flex:0 0 auto; color:#725ce5; font-size:11px; font-weight:800; letter-spacing:0.05em;} #mobile-panel .hs-mep-rule-item strong{display:block; margin-bottom:5px; color:#273249; font-size:13px;} #mobile-panel .hs-mep-rule-item span:not(.hs-mep-rule-number){display:block; color:#708099; font-size:12px; line-height:1.5;} #mobile-panel .hs-mep-step-heading{display:flex; align-items:center; gap:11px; margin:4px 0 20px;} #mobile-panel .hs-mep-step-heading > span{padding:5px 8px; border-radius:7px; background:#2d205b; color:#fff; font-size:10px; font-weight:800; letter-spacing:0.07em;} #mobile-panel .hs-mep-step-heading > strong{color:#2c3550; font-size:15px;} #mobile-panel .hs-mep-fixed-term{display:flex; align-items:center; justify-content:space-between; min-height:58px; padding:0 18px; border:1px solid #ddd8fa; border-radius:13px; background:#faf9ff;} #mobile-panel .hs-mep-fixed-term strong{color:#262044; font-size:15px;} #mobile-panel .hs-mep-fixed-term span{color:#7a8498; font-size:12px;} #mobile-panel .hs-field-helper{margin:8px 0 0; color:#73829a; font-size:12px; line-height:1.55;} #mobile-panel .hs-mep-formula-note{margin-top:22px; border:1px solid #e4e0f6; border-radius:13px; background:#fbfaff; overflow:hidden;} #mobile-panel .hs-mep-formula-note summary{padding:15px 18px; cursor:pointer; color:#51449b; font-size:13px; font-weight:700;} #mobile-panel .hs-mep-formula-note > div{padding:0 18px 17px; color:#66748b; font-size:12px; line-height:1.6;} #mobile-panel .hs-mep-formula-note p{margin:7px 0;} @media (max-width: 767px){ #mobile-panel .hs-mep-estimate-intro{padding:20px 18px;} #mobile-panel .hs-mep-rule-grid{grid-template-columns:1fr;} #mobile-panel .hs-mep-fixed-term{align-items:flex-start; flex-direction:column; justify-content:center; gap:3px;} } .hs-subpage-container, .hs-tools-hero-wrap{min-height:1000px !important; padding-bottom:60px !important;}';\n      document.head.appendChild(bridgeStyle);\n    }\n    var uls = document.querySelectorAll('#ast-hf-menu-1, #ast-hf-mobile-menu, ul.main-header-menu');\n    uls.forEach(function(ul) {\n      if (!ul || ul.querySelector('.hs-classes-nav-item')) return;\n      var guideLi = ul.querySelector('.menu-item-3513');\n      if (!guideLi) {\n        var lis = Array.from(ul.children);\n        guideLi = lis.find(function(li) {\n          var txt = li.textContent || li.innerText || '';\n          var a = li.querySelector('a');\n          var hasGuideHref = false;\n          if (a) {\n            if (a.href) {\n              if (a.href.indexOf('guides') !== -1) {\n                hasGuideHref = true;\n              }\n            }\n          }\n          return txt.indexOf('Guides') !== -1 || hasGuideHref;\n        });\n      }\n      if (guideLi) {\n        var classesLi = guideLi.cloneNode(true);\n        classesLi.id = '';\n        classesLi.className = guideLi.className + ' hs-classes-nav-item hs-dropdown-menu';\n        classesLi.style.position = 'relative';\n        var svgIcon = '<svg width=\"12\" height=\"12\" viewBox=\"0 0 12 12\"><path d=\"M2.5 4.5L6 8L9.5 4.5\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" fill=\"none\"\/><\/svg>';\n        var rowDiv = document.createElement('div');\n        rowDiv.className = 'hs-classes-row';\n        rowDiv.innerHTML = '<a href=\"https:\/\/hangulspace.com\/classes\/\" class=\"menu-link\"><span class=\"menu-text\">Classes<\/span><\/a><button type=\"button\" class=\"hs-classes-toggle-btn\" aria-label=\"Toggle Classes Submenu\">' + svgIcon + '<\/button>';\n        var dropdownDiv = document.createElement('div');\n        dropdownDiv.className = 'hs-dropdown-content';\n        dropdownDiv.style.cssText = 'display:none; position:absolute !important; top:calc(100% + 12px) !important; left:50% !important; transform:translateX(-50%) !important; background:rgba(25, 22, 40, 0.95) !important; min-width:240px !important; width:240px !important; border-radius:18px !important; padding:8px !important; border:1px solid rgba(188, 167, 255, 0.3) !important; box-shadow:0 20px 45px rgba(0, 0, 0, 0.6), 0 0 20px rgba(188, 167, 255, 0.15) !important; backdrop-filter:blur(20px) !important; z-index:999999 !important; box-sizing:border-box !important;';\n        var compactLinkStyle = 'padding:9px 16px !important; margin:4px 0 !important; font-size:13.5px !important; font-weight:700 !important; line-height:1.35 !important; height:auto !important; min-height:unset !important; display:flex !important; align-items:center !important; border-radius:10px !important; text-decoration:none !important;';\n        dropdownDiv.innerHTML = '<a href=\"https:\/\/hangulspace.com\/classes\/\" style=\"' + compactLinkStyle + '\">All Classes & Pricing<\/a><a href=\"https:\/\/hangulspace.com\/classes\/#learn-korean\" style=\"' + compactLinkStyle + '\">Learn Korean<\/a><a href=\"https:\/\/hangulspace.com\/classes\/#learn-english\" style=\"' + compactLinkStyle + '\">Learn English<\/a><a href=\"https:\/\/hangulspace.com\/classes\/#teacher-development\" style=\"' + compactLinkStyle + '\">Korean Teacher Development<\/a>';\n        classesLi.innerHTML = '';\n        classesLi.appendChild(rowDiv);\n        classesLi.appendChild(dropdownDiv);\n        classesLi.addEventListener('mouseenter', function() { if (window.innerWidth >= 922) dropdownDiv.style.display = 'block'; });\n        classesLi.addEventListener('mouseleave', function() { if (window.innerWidth >= 922) dropdownDiv.style.display = 'none'; });\n        var toggleBtn = rowDiv.querySelector('.hs-classes-toggle-btn');\n        if (toggleBtn) {\n          toggleBtn.addEventListener('click', function(e) {\n            e.preventDefault();\n            e.stopPropagation();\n            classesLi.classList.toggle('hs-accordion-expanded');\n          });\n        }\n        var innerLinks = dropdownDiv.querySelectorAll('a');\n        innerLinks.forEach(function(link) {\n          link.addEventListener('mouseenter', function() { if (window.innerWidth >= 922) { this.style.background = 'rgb(123, 104, 238)'; this.style.transform = 'translateX(4px)'; } });\n          link.addEventListener('mouseleave', function() { if (window.innerWidth >= 922) { this.style.background = 'transparent'; this.style.transform = 'none'; } });\n        });\n        guideLi.parentNode.insertBefore(classesLi, guideLi.nextSibling);\n      }\n    });\n        var btnWraps = document.querySelectorAll('.ast-header-button-1');\n    btnWraps.forEach(function(wrap) {\n      var realBtn = wrap.querySelector('a.ast-custom-button-link');\n      var dupes = wrap.querySelectorAll('a.menu-link');<\/p>\n<p>      if (realBtn) {\n        var label = realBtn.querySelector('.ast-custom-button');\n        if (label) label.textContent = 'Contact Us';<\/p>\n<p>        realBtn.href = 'https:\/\/hangulspace.com\/contact\/';\n        realBtn.style.display = 'inline-flex';\n        realBtn.style.visibility = 'visible';\n        realBtn.style.color = '#5B4AA3';\n      }<\/p>\n<p>      dupes.forEach(function(d) {\n        d.style.display = 'none';\n        d.style.visibility = 'hidden';\n      });\n    });\n    }\n  }\n  window.switchHsTab = function(btn, targetId) {\n    document.querySelectorAll('.hs-top-tab-btn').forEach(function(b){\n      b.classList.remove('active');\n      b.setAttribute('aria-selected', 'false');\n    });\n    document.querySelectorAll('.hs-showcase-card').forEach(function(c){\n      c.classList.remove('active');\n    });\n    document.querySelectorAll('.hs-interactive-panel').forEach(function(p){\n      p.classList.remove('active');\n      p.style.display = 'none';\n    });\n    if (btn) {\n      btn.classList.add('active');\n      btn.setAttribute('aria-selected', 'true');\n    }\n    var targetCard = document.getElementById(targetId);\n    if (targetCard) targetCard.classList.add('active');\n  };\n  window.toggleHsTool = function(panelId) {\n    var panel = document.getElementById(panelId);\n    if (!panel) return;\n    var isVisible = panel.classList.contains('active') || panel.style.display === 'block';\n    document.querySelectorAll('.hs-interactive-panel').forEach(function(p){\n      p.classList.remove('active');\n      p.style.display = 'none';\n    });\n    if (!isVisible) {\n      panel.classList.add('active');\n      panel.style.display = 'block';\n      setTimeout(function() {\n        var topPos = panel.getBoundingClientRect().top + window.pageYOffset - 100;\n        window.scrollTo({ top: topPos, behavior: 'smooth' });\n      }, 50);\n    }\n  };\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', renderPureWhiteHeader);\n  } else {\n    renderPureWhiteHeader();\n  }\n  setInterval(renderPureWhiteHeader, 150);\n})();<\/p>\n<p><\/script><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" onload=\"if(!window.hsMasterScriptLoaded){window.hsMasterScriptLoaded=true;eval(document.getElementById('hs-tools-complete-master-script').textContent);}\" style=\"display:none;\" \/>\n    <\/div>\n<div class=\"hs-step-line\"><\/div>\n<\/p><\/div>\n<p>      <!-- STEP 1 --><br \/>\n      ><\/p>\n","protected":false},"excerpt":{"rendered":"<p>FREE TOOLS FOR LIFE IN KOREA Practical, instant calculators and generators designed for living, studying, and working in Korea. \ud83d\udcf1 MOBILE PLAN \ud83c\udfe0 HOUSING RISK \u2328\ufe0f KOREAN TYPING SECTION 01 INTERACTIVE TOOLS &#038; UTILITIES \u2605 INTERACTIVE TOOL MOBILE CARRIER SERVICE Mobile Contract Exit Planner Compare the estimated cost of switching now vs. waiting SKT KT [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"site-sidebar-layout":"no-sidebar","site-content-layout":"stretched","ast-site-content-layout":"full-width-container","site-content-style":"unboxed","site-sidebar-style":"unboxed","ast-global-header-display":"","ast-banner-title-visibility":"disabled","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"disabled","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"enabled","adv-header-id-meta":"","stick-header-meta":"enabled","header-above-stick-meta":"","header-main-stick-meta":"enabled","header-below-stick-meta":"","astra-migrate-meta-layouts":"set","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"class_list":["post-3493","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/hangulspace.com\/vi\/wp-json\/wp\/v2\/pages\/3493","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/hangulspace.com\/vi\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/hangulspace.com\/vi\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/hangulspace.com\/vi\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/hangulspace.com\/vi\/wp-json\/wp\/v2\/comments?post=3493"}],"version-history":[{"count":267,"href":"https:\/\/hangulspace.com\/vi\/wp-json\/wp\/v2\/pages\/3493\/revisions"}],"predecessor-version":[{"id":4949,"href":"https:\/\/hangulspace.com\/vi\/wp-json\/wp\/v2\/pages\/3493\/revisions\/4949"}],"wp:attachment":[{"href":"https:\/\/hangulspace.com\/vi\/wp-json\/wp\/v2\/media?parent=3493"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}