From 3159b4f938d1a24f82ecf2b1d4cfd320182d2d89 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 18 Oct 2026 22:41:16 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=81=20Sync=202026-10-18=2022:41:16?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dns2socks/Makefile | 14 +- dns2socks/src/DNS2SOCKS.sln | 22 + dns2socks/src/DNS2SOCKS/DNS2SOCKS.c | 1778 +++++++++++++++++ dns2socks/src/DNS2SOCKS/DNS2SOCKS.manifest | 24 + dns2socks/src/DNS2SOCKS/DNS2SOCKS.rc | 35 + dns2socks/src/DNS2SOCKS/DNS2SOCKS.vcxproj | 101 + .../src/DNS2SOCKS/DNS2SOCKS.vcxproj.filters | 31 + dns2socks/src/DNS2SOCKS/stdafx.c | 1 + dns2socks/src/DNS2SOCKS/stdafx.h | 43 + dns2socks/src/ReadMe.txt | 315 +++ luci-app-diskman/Makefile | 5 +- luci-app-easymesh/Makefile | 2 +- .../luasrc/view/easymesh/preset_confirm.htm | 71 + luci-app-easymesh/po/zh_Hans/easymesh.po | 449 ++++- luci-app-open-box/Makefile | 4 +- luci-app-passwall/Makefile | 2 +- .../luasrc/controller/passwall.lua | 23 +- luci-app-passwall/luasrc/passwall/api.lua | 9 +- .../luasrc/view/passwall/global/footer.htm | 10 +- .../share/passwall/helper_chinadns_add.lua | 4 - .../usr/share/passwall/helper_dnsmasq.lua | 6 +- .../share/passwall/helper_smartdns_add.lua | 6 +- .../root/usr/share/passwall/iptables.sh | 2 +- .../root/usr/share/passwall/nftables.sh | 2 +- .../root/usr/share/passwall/tasks.sh | 15 +- luci-app-smartsafehub/CHANGELOG.md | 179 ++ luci-app-smartsafehub/Makefile | 4 +- .../frontend/demo-dist/demo.css | 2 +- .../frontend/demo-dist/demo.js | 6 +- .../frontend/package-lock.json | 4 +- luci-app-smartsafehub/frontend/package.json | 2 +- .../frontend/src/api/smartsafehub.ts | 14 +- .../frontend/src/app/App.tsx | 5 +- .../src/components/FirmwareUpdatesCard.tsx | 17 +- .../components/SafeShieldStatisticsPanel.tsx | 148 +- .../src/components/SectionHeading.tsx | 23 + .../src/components/SoftwareUpdatesCard.tsx | 6 - luci-app-smartsafehub/frontend/src/main.tsx | 2 +- .../frontend/src/pages/ActivityPage.tsx | 214 +- .../src/pages/ConnectedDevicesPage.tsx | 78 +- .../frontend/src/pages/HomePage.tsx | 30 +- .../frontend/src/pages/IptvPage.tsx | 10 +- .../frontend/src/pages/LanPage.tsx | 32 +- .../frontend/src/pages/SafeShieldPage.tsx | 329 ++- .../src/pages/SafeShieldRulesPage.tsx | 609 ++++-- .../frontend/src/pages/SettingsPage.tsx | 183 +- .../src/pages/SmartSafeHubAccountPage.tsx | 39 +- .../frontend/src/pages/UpdatePage.tsx | 7 + .../frontend/src/pages/WanPage.tsx | 31 +- .../frontend/src/pages/WifiPage.tsx | 5 + .../frontend/src/styles/app.css | 29 + .../frontend/src/types/rules.ts | 22 + .../root/usr/libexec/smartsafehub-device | 227 ++- .../libexec/smartsafehub-recommended-rules | 149 +- .../rpcd/acl.d/luci-app-smartsafehub.json | 2 + .../root/usr/share/rpcd/ucode/smartsafehub.uc | 13 + .../ucode/smartsafehub/device-registration.uc | 79 +- .../ucode/luci/template/smartsafehub/login.ut | 6 +- .../root/www/luci-static/smartsafehub/app.css | 2 +- .../root/www/luci-static/smartsafehub/app.js | 6 +- luci-app-smartsafehub/spec/contracts_spec.sh | 14 + .../tests/runtime/recommended-revoke.test.mjs | 44 + .../runtime/recommended-sync-stages.test.mjs | 36 + .../tests/runtime/rule-origin.test.mjs | 95 +- .../tests/test-activity-layout.sh | 35 + .../tests/test-activity-ui-contract.sh | 4 +- luci-app-smartsafehub/tests/test-health.sh | 6 +- .../tests/test-recommended-ack.sh | 142 ++ .../tests/test-recommended-rules.sh | 314 ++- .../tests/test-relative-time-contract.sh | 7 +- .../tests/test-rules-ui-contract.sh | 8 + .../tests/test-safeshield-page-contract.sh | 47 +- .../tests/test-section-layout.sh | 63 + .../tests/test-section-refinement.sh | 35 + .../tests/test-settings-ui-contract.sh | 37 +- .../tests/test-static-validation.sh | 1 + luci-app-smartsafehub/tests/test-ui-polish.sh | 27 + .../tests/test-update-ui-contract.sh | 10 + luci-base/root/usr/share/rpcd/ucode/luci | 8 +- luci-mod-status/Makefile | 2 +- .../view/status/include/25_storage.js | 4 +- .../resources/view/status/include/60_wifi.js | 79 +- .../resources/view/status/index.js | 51 +- luci-mod-status/tests/README.md | 12 + luci-mod-status/tests/mount-points.js | 25 + luci-mod-status/tests/overview-async.js | 29 + luci-mod-status/tests/wifi-disabled.js | 21 + redsocks2/Makefile | 4 +- 88 files changed, 5597 insertions(+), 1051 deletions(-) create mode 100644 dns2socks/src/DNS2SOCKS.sln create mode 100644 dns2socks/src/DNS2SOCKS/DNS2SOCKS.c create mode 100644 dns2socks/src/DNS2SOCKS/DNS2SOCKS.manifest create mode 100644 dns2socks/src/DNS2SOCKS/DNS2SOCKS.rc create mode 100644 dns2socks/src/DNS2SOCKS/DNS2SOCKS.vcxproj create mode 100644 dns2socks/src/DNS2SOCKS/DNS2SOCKS.vcxproj.filters create mode 100644 dns2socks/src/DNS2SOCKS/stdafx.c create mode 100644 dns2socks/src/DNS2SOCKS/stdafx.h create mode 100644 dns2socks/src/ReadMe.txt create mode 100644 luci-app-easymesh/luasrc/view/easymesh/preset_confirm.htm create mode 100644 luci-app-smartsafehub/frontend/src/components/SectionHeading.tsx create mode 100644 luci-app-smartsafehub/tests/runtime/recommended-revoke.test.mjs create mode 100644 luci-app-smartsafehub/tests/runtime/recommended-sync-stages.test.mjs create mode 100755 luci-app-smartsafehub/tests/test-activity-layout.sh create mode 100755 luci-app-smartsafehub/tests/test-recommended-ack.sh mode change 100644 => 100755 luci-app-smartsafehub/tests/test-relative-time-contract.sh create mode 100755 luci-app-smartsafehub/tests/test-section-layout.sh create mode 100755 luci-app-smartsafehub/tests/test-section-refinement.sh create mode 100755 luci-app-smartsafehub/tests/test-ui-polish.sh create mode 100644 luci-mod-status/tests/mount-points.js create mode 100644 luci-mod-status/tests/overview-async.js diff --git a/dns2socks/Makefile b/dns2socks/Makefile index 7446140f1..4822f1354 100644 --- a/dns2socks/Makefile +++ b/dns2socks/Makefile @@ -6,12 +6,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=dns2socks PKG_VERSION:=2.1 -PKG_RELEASE:=1 - -PKG_SOURCE:=SourceCode.zip -PKG_SOURCE_URL:=https://github.com/Openwrt-Passwall/openwrt-passwall-packages/releases/download/dns2socks -PKG_SOURCE_DATE:=2020-02-18 -PKG_HASH:=skip +PKG_RELEASE:=2 PKG_MAINTAINER:=ghostmaker PKG_LICENSE:=BSD-3-Clause @@ -19,8 +14,6 @@ PKG_LICENSE_FILE:=LICENSE include $(INCLUDE_DIR)/package.mk -UNZIP_CMD:=unzip -q -d $(PKG_BUILD_DIR) $(DL_DIR)/$(PKG_SOURCE) - define Package/dns2socks SECTION:=net CATEGORY:=Network @@ -35,6 +28,11 @@ define Package/dns2socks/description a SOCKS tunnel like Tor or a HTTP proxy. endef +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) + $(CP) ./src/. $(PKG_BUILD_DIR)/ +endef + define Build/Compile $(TARGET_CC) \ $(TARGET_CFLAGS) \ diff --git a/dns2socks/src/DNS2SOCKS.sln b/dns2socks/src/DNS2SOCKS.sln new file mode 100644 index 000000000..ac6174e71 --- /dev/null +++ b/dns2socks/src/DNS2SOCKS.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Express 14 for Windows Desktop +VisualStudioVersion = 14.0.23107.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DNS2SOCKS", "DNS2SOCKS\DNS2SOCKS.vcxproj", "{F19D3415-2E79-45EF-9893-4D9E2010B976}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F19D3415-2E79-45EF-9893-4D9E2010B976}.Debug|Win32.ActiveCfg = Debug|Win32 + {F19D3415-2E79-45EF-9893-4D9E2010B976}.Debug|Win32.Build.0 = Debug|Win32 + {F19D3415-2E79-45EF-9893-4D9E2010B976}.Release|Win32.ActiveCfg = Release|Win32 + {F19D3415-2E79-45EF-9893-4D9E2010B976}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/dns2socks/src/DNS2SOCKS/DNS2SOCKS.c b/dns2socks/src/DNS2SOCKS/DNS2SOCKS.c new file mode 100644 index 000000000..ffde4a3c8 --- /dev/null +++ b/dns2socks/src/DNS2SOCKS/DNS2SOCKS.c @@ -0,0 +1,1778 @@ +#include "stdafx.h" + +//helpful URLs: +// http://www.networksorcery.com/enp/protocol/dns.htm +// http://www.freesoft.org/CIE/RFC/1035/43.htm +// https://tools.ietf.org/html/rfc2671 +// https://tools.ietf.org/html/rfc3225 +// https://tools.ietf.org/html/rfc7871 + +//application name and version +#define APP_NAME "DNS2SOCKS V2.1" +//first output line in console +#define APP_STRING "\n" APP_NAME " (free software, use parameter /? to display help)\n" +//log file header line +#define LOG_HEADER APP_NAME " log opened" +//date/time format string for ISO 8601 format, but leave away the T delimiter as it's bad to read +#define DATE_TIME_FORMAT "%u-%02u-%02u %02u:%02u:%02u " +//127.0.0.1 for default SOCKS5 server +#define DEFAULT_SOCKS_SERVER "127.0.0.1" +//9050 for default SOCKS5 port +#define DEFAULT_SOCKS_PORT "9050" +//46.182.19.48 for default DNS server supporting TCP (Digitalcourage e.V.) +#define DEFAULT_DNS_SERVER "46.182.19.48" +//127.0.0.1 for local IP address for listening +#define DEFAULT_LISTEN_IP "127.0.0.1" +//53 for default DNS port +#define DEFAULT_DNS_PORT "53" + +//defines for OutputToLog (bits) +#define OUTPUT_LINE_BREAK (1) +#define OUTPUT_DATE_TIME (2) +#define OUTPUT_CONSOLE (4) +#define OUTPUT_ALL (OUTPUT_LINE_BREAK|OUTPUT_DATE_TIME|OUTPUT_CONSOLE) + +//on some systems a failing send command raises a signal; we want to disable that by passing MSG_NOSIGNAL +//some systems (Windows) don't define that -> define it to 0 +#ifndef MSG_NOSIGNAL + #define MSG_NOSIGNAL (0) +#endif + + +union UClient +{ + struct sockaddr_storage sAddr; //address of requesting client for UDP + SOCKET hSock; //socket handle for TCP +}; + +//entry for DNS request and answer (cache entry) +struct SEntry +{ + struct SEntry* psNext; //next list entry or NULL + uint16_t* u16aAnswer; //pointer to answer or NULL + time_t iTime; //time when the answer was deliviered last time + union UClient client; //information on how to send response back to client + unsigned char uAddrLen; //length of used part of sAddr for UDP, sizeof(SOCKET) for TCP + uint16_t u16aRequest[1]; //extended dynamically at malloc for "struct SEntry" (use "uint16_t" to ensure according alignment, first element contains length in big endian format) +}; + +static struct SEntry* g_psFirst=NULL; //list of DNS requests and answers (cache) +static unsigned int g_uCacheCount=0; //amout of entries in list g_psFirst +static int g_bCacheEnabled=1; //!=0 when cache is enabled +static struct sockaddr_storage g_sDnsSrvAddr; //DNS server supporting TCP +static struct sockaddr_storage g_sSocksAddr; //SOCKS5 server +static CRITICAL_SECTION g_sCritSect; //to protect the list g_psFirst and g_uCacheCount +static SOCKET g_hSockUdp; //UDP socket +static unsigned char* g_uaUsrPwd=NULL; //authentication package for SOCKS +static int g_iUsrPwdLen; //length of g_caUsrPwd +static int g_iHttpProxyConnectLen=0; //length of CONNECT command in g_caHttpProxyConnect +static char g_caHttpProxyConnect[300]; //CONNECT command in case of using HTTP proxy +static uint8_t g_u8aEcsOption[8+16]; //EDNS Client Subnet EDNS0 option (maximum necessary bytes reserved, actual amount in g_iEcsOptionLen) +static int g_iEcsOptionLen=0; //actual amount bytes used of g_u8aEcsOption +static int g_bForceEcs=0; //!=0 to replace existing EDNS Client Subnet option by our one + +//OS specific functionality +#ifdef _WIN32 + +//for Windows we can create an own console window, so here we use some OS specific functions; +//also for the file access we use the WIN32 API - we could use the C API as we do for the other OSes +//but as we need to take special care about the line break (\r\n) anyway and WIN32 should be a little +//bit faster, we use WIN32 + +static HANDLE g_hConsole=NULL; //handle for console output +static HANDLE g_hLogFile=INVALID_HANDLE_VALUE; //handle for log file + +//passes a string and its length to a function +#define STRING_AND_LEN(szString) szString, sizeof(szString)-1 + +static char* GetSysError(int iErrNo) +{ + char* szBuffer; + size_t uLen; + + if(!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, NULL, iErrNo, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&szBuffer, 0, NULL)) + { + szBuffer=(char*)LocalAlloc(LMEM_FIXED, 14); + memcpy(szBuffer, "Unknown error", 14); + } + else + { + //remove line breaks at the end + uLen=strlen(szBuffer); + while(uLen>0 && (szBuffer[uLen-1]=='\n' || szBuffer[uLen-1]=='\r')) + szBuffer[--uLen]='\0'; + } + return szBuffer; +} + +static void FreeSysError(char* szString) +{ + LocalFree(szString); +} + +static void OutputToLog(unsigned int uOutputSettingBits, const char* szFormatString, ...) +{ + va_list pArgs; + int iLenPrefix; + int iLen; + DWORD uDummy; + char szBuf[1024]; + + //nothing to do? + if((!g_hConsole || !(uOutputSettingBits&OUTPUT_CONSOLE)) && g_hLogFile==INVALID_HANDLE_VALUE) + return; + //add line break? + if(uOutputSettingBits&OUTPUT_LINE_BREAK) + { + szBuf[0]='\r'; + szBuf[1]='\n'; + iLenPrefix=2; + } + else + iLenPrefix=0; + if(uOutputSettingBits&OUTPUT_DATE_TIME) + { + SYSTEMTIME sTime; + + //add date/time string + GetLocalTime(&sTime); + iLen=_snprintf_s(szBuf+iLenPrefix, ARRAYSIZE(szBuf)-iLenPrefix, _TRUNCATE, DATE_TIME_FORMAT, sTime.wYear, sTime.wMonth, sTime.wDay, sTime.wHour, sTime.wMinute, sTime.wSecond); + if(iLen<0) //error? + return; + iLenPrefix+=iLen; + } + //add log string + va_start(pArgs, szFormatString); + iLen=_vsnprintf_s(szBuf+iLenPrefix, ARRAYSIZE(szBuf)-iLenPrefix, _TRUNCATE, szFormatString, pArgs); + if(iLen<0) //error? + return; + iLen+=iLenPrefix; + //output + if(g_hConsole && (uOutputSettingBits&OUTPUT_CONSOLE)) + { + wchar_t wcBuf[1024]; + + //convert to wide char to avoid trouble with special characters + iLen=MultiByteToWideChar(CP_ACP, 0, szBuf, iLen, wcBuf, ARRAYSIZE(wcBuf)); + if(iLen) + WriteConsoleW(g_hConsole, wcBuf, iLen, &uDummy, NULL); + } + if(g_hLogFile) + WriteFile(g_hLogFile, szBuf, iLen, &uDummy, NULL); +} + +static void OpenLogFile(const char* szFilePath, int bAppend) +{ + g_hLogFile=CreateFile(szFilePath, GENERIC_WRITE, FILE_SHARE_READ, NULL, bAppend?OPEN_ALWAYS:CREATE_ALWAYS, 0, NULL); + if(g_hLogFile==INVALID_HANDLE_VALUE) + { + char* szErrMsg=GetSysError(GetLastError()); + + OutputToLog(OUTPUT_ALL, "Failed to open log file \"%s\": %s", szFilePath, szErrMsg); + FreeSysError(szErrMsg); + } + else + { + //append and no new file was created? + if(bAppend && GetLastError()==ERROR_ALREADY_EXISTS) + { + DWORD uDummy; + + //go to end of file for appending + SetFilePointer(g_hLogFile, 0, NULL, FILE_END); + //add line breaks for appending + WriteFile(g_hLogFile, STRING_AND_LEN("\r\n\r\n"), &uDummy, NULL); + } + //output header string in log + OutputToLog(OUTPUT_DATE_TIME, LOG_HEADER); + } +} + +static void OpenConsole() +{ + if(g_hConsole) //already openend? + return; + AllocConsole(); + g_hConsole=GetStdHandle(STD_OUTPUT_HANDLE); + if(g_hConsole) + { + DWORD uDummy; + + WriteConsole(g_hConsole, STRING_AND_LEN(APP_STRING), &uDummy, NULL); + } +} + +static void OutputFatal(const char* szFormatString, ...) +{ + HANDLE hReadConsole; + va_list pArgs; + int iLen; + DWORD uDummy; + INPUT_RECORD sRecord; + char szBuf[4096]; + + OpenConsole(); + if(!g_hConsole) //console available? + return; + va_start(pArgs, szFormatString); + iLen=_vsnprintf_s(szBuf, ARRAYSIZE(szBuf), _TRUNCATE, szFormatString, pArgs); + if(iLen<0) //error? + return; + //output + WriteConsole(g_hConsole, szBuf, iLen, &uDummy, NULL); + WriteConsole(g_hConsole, STRING_AND_LEN("\nPress any key to close the application..."), &uDummy, NULL); + hReadConsole=GetStdHandle(STD_INPUT_HANDLE); + //wait for key + while(ReadConsoleInput(hReadConsole, &sRecord, 1, &uDummy) && (sRecord.EventType!=KEY_EVENT || !sRecord.Event.KeyEvent.bKeyDown)) + ; +} + +//thread handling for Windows +#define THREAD_FUNCTION(threadFunction, pParam) static DWORD __stdcall threadFunction(LPVOID pParam) + +int ThreadCreate(LPTHREAD_START_ROUTINE pThreadFunction, void* pParam) +{ + DWORD uId; + HANDLE hThread=CreateThread(NULL, 0, pThreadFunction, pParam, 0, &uId); + + if(!hThread) + { + char* szErrMsg=GetSysError(GetLastError()); + + OutputToLog(OUTPUT_ALL, "Creating new thread has failed: %s", szErrMsg); + FreeSysError(szErrMsg); + return 0; //error + } + CloseHandle(hThread); + return 1; //o.k. +} + +#else //#ifdef _WIN32 + +//on other OS create no own console window +static int g_bConsole=0; //console output enabled? +static FILE* g_pLogFile=NULL; //log file pointer + +#define GetSysError(iErrNo) strerror(iErrNo) +#define FreeSysError(szString) //nothing to do + +static void OutputToLog(unsigned int uOutputSettingBits, const char* szFormatString, ...) +{ + va_list pArgs; + int iLenPrefix; + int iLen; + char szBuf[4096]; + + //nothing to do? + if((!g_bConsole || !(uOutputSettingBits&OUTPUT_CONSOLE)) && !g_pLogFile) + return; + //add line break? + if(uOutputSettingBits&OUTPUT_LINE_BREAK) + { + szBuf[0]='\n'; + iLenPrefix=1; + } + else + iLenPrefix=0; + if(uOutputSettingBits&OUTPUT_DATE_TIME) + { + //add date/time string + time_t iTime; + struct tm* psTime; + + if(time(&iTime)==(time_t)-1) + return; //error + psTime=localtime(&iTime); + if(!psTime) //error? + return; + iLen=snprintf(szBuf+iLenPrefix, sizeof(szBuf)-iLenPrefix, DATE_TIME_FORMAT, psTime->tm_year+1900, psTime->tm_mon+1, psTime->tm_mday, psTime->tm_hour, psTime->tm_min, psTime->tm_sec); + if(iLen<0) //error? + return; + iLenPrefix+=iLen; + } + //add log string + va_start(pArgs, szFormatString); + iLen=vsnprintf(szBuf+iLenPrefix, sizeof(szBuf)-iLenPrefix, szFormatString, pArgs); + va_end(pArgs); + if(iLen<0) //error? + return; + iLen+=iLenPrefix; + if(iLen>=sizeof(szBuf)) //truncated? -> make sure to add \0 + { + szBuf[sizeof(szBuf)-1]='\0'; + iLen=sizeof(szBuf)-1; + } + //output + if(g_bConsole && (uOutputSettingBits&OUTPUT_CONSOLE)) + fputs(szBuf, stdout); + if(g_pLogFile) + fwrite(szBuf, 1, iLen, g_pLogFile); +} + +static void OpenLogFile(const char* szFilePath, int bAppend) +{ + g_pLogFile=fopen(szFilePath, bAppend?"ab":"wb"); + if(!g_pLogFile) + OutputToLog(OUTPUT_ALL, "Failed to open log file \"%s\": %s", szFilePath, strerror(errno)); + else + { + //append and file is not empty? + if(bAppend && ftell(g_pLogFile)) + { + //add line breaks for appending + fputs("\n\n", g_pLogFile); + } + setbuf(g_pLogFile, NULL); //disable buffering (no newline needed for actual output) + //output header string in log + OutputToLog(OUTPUT_DATE_TIME, LOG_HEADER); + } +} + +static void OpenConsole() +{ + if(g_bConsole) //already openend? + return; + g_bConsole=1; + setbuf(stdout, NULL); //disable buffering (no newline needed for actual output) + printf(APP_STRING); +} + +//for OutputFatal we can just use a macro to prefix the parameters +#define OutputFatal OpenConsole(); printf + +//thread handling via pthread +#define THREAD_FUNCTION(threadFunction, pParam) static void* threadFunction(void* pParam) + +int ThreadCreate(void* (*pThreadFunction)(void*), void* pParam) +{ + pthread_t hThread; + int iErrNo=pthread_create(&hThread, NULL, pThreadFunction, pParam); + + if(iErrNo) + { + OutputToLog(OUTPUT_ALL, "Creating new thread has failed: error code %d", iErrNo); + return 0; //error + } + pthread_detach(hThread); + return 1; //o.k. +} + +#endif //#ifdef _WIN32 + + +//returns the internal length of a socket address (IPv4 / IPv6) +static unsigned int GetAddrLen(const struct sockaddr_storage* psAddr) +{ + if(psAddr->ss_family==AF_INET) + return sizeof(struct sockaddr_in); + return sizeof(struct sockaddr_in6); +} + +//sends UDP answer +static void SendAnswer(struct SEntry* psEntry) +{ + //ignore error here + //UDP? + if(psEntry->uAddrLen!=sizeof(SOCKET)) + //+2 because DNS on UDP doesn't include the length + sendto(g_hSockUdp, (char*)(psEntry->u16aAnswer+1), ntohs(*psEntry->u16aAnswer), MSG_NOSIGNAL, (struct sockaddr*)&psEntry->client.sAddr, psEntry->uAddrLen); + else + { + //TCP + send(*(SOCKET*)&psEntry->client.hSock, (char*)psEntry->u16aAnswer, ntohs(*psEntry->u16aAnswer)+2, MSG_NOSIGNAL); + closesocket(psEntry->client.hSock); + } +} + +//searches for an entry in the cache list and removes it - also closes a socket and outputs an error message +static void RemoveEntry(struct SEntry* psEntry, SOCKET hSock, int bUseCriticalSection) +{ + struct SEntry** ppsPrev; + struct SEntry* psEntry2; + + //close the socket created by the caller + if(hSock!=SOCKET_ERROR) + closesocket(hSock); + if(bUseCriticalSection) + EnterCriticalSection(&g_sCritSect); + ppsPrev=&g_psFirst; + for(psEntry2=g_psFirst; psEntry2; psEntry2=psEntry2->psNext) + { + //found entry? + if(psEntry==psEntry2) + { + //remove entry from list + *ppsPrev=psEntry->psNext; + --g_uCacheCount; + break; + } + ppsPrev=&psEntry2->psNext; + } + //free the entry + free(psEntry->u16aAnswer); //should be NULL anyway + //close socket in case of TCP + if(psEntry->uAddrLen==sizeof(SOCKET)) + closesocket(psEntry->client.hSock); + free(psEntry); + if(bUseCriticalSection) + LeaveCriticalSection(&g_sCritSect); +} + +//outputs error for TTL check (i32TimeOffset>=0) or searching OPT pseudo-RR (i32TimeOffset<0) +static int InvalidDnsMsgErrorOutput(int32_t i32TimeOffset) +{ + if(i32TimeOffset>=0) + OutputToLog(OUTPUT_ALL, "Invalid DNS answer detected while calculating TTL"); + else + OutputToLog(OUTPUT_ALL, "Invalid DNS requested detected while searching OPT pseudo-RR"); + return 0; //error +} + +//iterates through DNS message parts +//either to update/check TTL (i32TimeOffset>=0) or to find OPT pseudo-RR (i32TimeOffset<0) +//returns 0 on error/TTL expiration, 1 on success or offset of OPT pseudo-RR +static int IterateDnsMessage(uint16_t* u16aMessage, int32_t i32TimeOffset) +{ + int32_t i32TimeToLive; + uint16_t u16ContentLen; + uint16_t u16AmountQuestions; + uint16_t u16Len; + uint8_t* pu8Pos; + uint8_t* pu8MessageEnd; + uint8_t u8NameLen; + + u16Len=ntohs(*u16aMessage); + if(u16Len<=12) + return InvalidDnsMsgErrorOutput(i32TimeOffset); //answer has no useful information + pu8Pos=(uint8_t*)u16aMessage+2; + pu8MessageEnd=pu8Pos+u16Len; + u16AmountQuestions=ntohs(((uint16_t*)pu8Pos)[2]); + pu8Pos+=12; //go behind header + //ignore questions + for(; u16AmountQuestions; --u16AmountQuestions) + { + //ignore name + for(;;) + { + if(pu8Pos>=pu8MessageEnd) + return InvalidDnsMsgErrorOutput(i32TimeOffset); //failed + u8NameLen=*pu8Pos; + if(!u8NameLen) + break; //end of name + if(u8NameLen>=0xc0) //compression used? (reference to other name via offset) + { + ++pu8Pos; //ignore 2nd part of offset + break; + } + pu8Pos+=u8NameLen+1; + } + pu8Pos+=5; //ignore type and class + } + //for finding OPT pseudo-RR the DNS request might end after the questions + if(i32TimeOffset<0 && pu8Pos==pu8MessageEnd) + return 1; //found no OPT pseudo-RR + //for all records + do + { + //ignore name of resource record + for(;;) + { + if(pu8Pos>=pu8MessageEnd) + return InvalidDnsMsgErrorOutput(i32TimeOffset); //failed + u8NameLen=*pu8Pos; + if(!u8NameLen) + break; //end of name + if(u8NameLen>=0xc0) //compression used? (reference to other name via offset) + { + ++pu8Pos; //ignore 2nd part of offset + break; + } + pu8Pos+=u8NameLen+1; + } + pu8Pos+=5; //jump behind type and class + if(pu8Pos>pu8MessageEnd-6) //6: TTL + RDLEN + return InvalidDnsMsgErrorOutput(i32TimeOffset); //failed + //type is OPT pseudo-RR which has no real TTL value? + if(ntohs(*(uint16_t*)(pu8Pos-4))==41) + { + //searching OPT pseudo-RR? + if(i32TimeOffset<0) + return (int)((pu8Pos+6)-(uint8_t*)u16aMessage); //return offset to data + } + else if(i32TimeOffset>=0) + { + //check time to live field (0 means "omitted") + i32TimeToLive=ntohl(*(uint32_t*)pu8Pos); + if(i32TimeToLive>0) + { + i32TimeToLive-=i32TimeOffset; + if(i32TimeToLive<=0) + return 0; //expired + *(uint32_t*)pu8Pos=htonl(i32TimeToLive); //update field + } + else if(i32TimeToLive<0) + return 0; //failed, TTL must be positive; mark as expired, however: no error output in this case + } + //ignore record content + u16ContentLen=ntohs(*(uint16_t*)(pu8Pos+4)); + pu8Pos+=6+u16ContentLen; + if(pu8Pos>pu8MessageEnd) + return InvalidDnsMsgErrorOutput(i32TimeOffset); //failed + } + while(pu8PosiTime==(time_t)-1) + return 1; //getting time failed last time; without a working timer we ignore time to live stuff completely + //calculate amount of seconds since last delivery + if(time(&iCurTime)==(time_t)-1) + return 1; //error, can't get current time -> ignore time to live stuff completely + i32TimeOffset=(int32_t)(iCurTime-psEntry->iTime); + if(i32TimeOffset<0) + return 0; //time increased; overflow? Mark as expired + psEntry->iTime=iCurTime; //store current time for next delivery + return IterateDnsMessage(psEntry->u16aAnswer, i32TimeOffset); +} + +//receives a specific amount of bytes +static int ReceiveBytes(SOCKET hSock, unsigned int uAmount, uint16_t* u16aBuf) +{ + unsigned int uPos=0; + int iLen; + char* szErrMsg; + + for(;;) + { + iLen=recv(hSock, (char*)u16aBuf+uPos, uAmount, 0); + switch(iLen) + { + case SOCKET_ERROR: + szErrMsg=GetSysError(WSAGetLastError()); + OutputToLog(OUTPUT_ALL, "Receiving from SOCKS server has failed: %s", szErrMsg); + FreeSysError(szErrMsg); + return 0; //failed + case 0: + OutputToLog(OUTPUT_ALL, "The SOCKS server has closed the connection unexpectedly"); + return 0; //failed + default: + uAmount-=iLen; + if(!uAmount) + return 1; //succeeded + uPos+=iLen; + } + } +} + +//for sending DNS requests where we add/manipulate the ECS option +//returns 0 on error, -1 to send original message or the new message length otherwise +static int AddEcsOption(uint16_t* u16aRequest, uint16_t* u16aDestBuf) +{ + //try to find OPT pseudo-RR + int iOffset=IterateDnsMessage(u16aRequest, -1); + + switch(iOffset) + { + case 0: //error + return 0; + case 1: //no OPT pseudo-RR + { + //add it + uint16_t u16Len=ntohs(*(uint16_t*)u16aRequest); + uint32_t u32NewLen=u16Len+g_iEcsOptionLen+11; + uint8_t* u8aDest; + + //new length exceeds maximum? + if(u32NewLen>65535) + { + OutputToLog(OUTPUT_ALL, "Cannot add EDNS client subnet info to DNS request as it would exceed the maximum size"); + return 0; + } + //create new DNS request; starts with length as we use TCP + *u16aDestBuf=htons((uint16_t)u32NewLen); + memcpy(u16aDestBuf+1, u16aRequest+1, u16Len); //copy original request + u16aDestBuf[6]=htons(ntohs(u16aDestBuf[6])+1); //increase "additional count" + u8aDest=((uint8_t*)(u16aDestBuf+1))+u16Len; //jump after original message + //add OPT pseudo-RR + u8aDest[0]=0; //name -> empty + u8aDest[1]=0; + u8aDest[2]=41; //type -> OPT pseudo-RR + u8aDest[3]=0x10;//max UDP size: 4096 + u8aDest[4]=0; + *(uint32_t*)(u8aDest+5)=0; //TTL (here: extended RCODE and flags -> all 0 -> no DNSSEC) + *(uint16_t*)(u8aDest+9)=htons((uint16_t)g_iEcsOptionLen); //option length + memcpy(u8aDest+11, g_u8aEcsOption, g_iEcsOptionLen); //add option + return (int)(u32NewLen+2); //+2 for length field + } + default: //found OPT pseudo-RR + { + //try to find EDNS Client Subnet option + uint8_t* pu8Pos=((uint8_t*)u16aRequest)+iOffset; //offset points to option data + uint16_t u16OptLen=ntohs(((uint16_t*)pu8Pos)[-1]); //length before data + uint8_t* pu8OptionsEnd=pu8Pos+u16OptLen; + uint16_t u16Len=ntohs(*(uint16_t*)u16aRequest); + uint8_t* pu8MessageEnd=((uint8_t*)u16aRequest)+2+u16Len; + uint16_t u16LenIncludingOptionsToCopy; + uint16_t u16CurOptLen; + uint32_t u32NewLen; + + //pu8OptionsEnd beyond end of message? -> error + if(pu8OptionsEnd>pu8MessageEnd) + return InvalidDnsMsgErrorOutput(-1); + for(;; pu8Pos+=u16CurOptLen) + { + if(pu8Pos>=pu8OptionsEnd) + { + //no EDNS Client Subnet option -> add it + break; + } + u16CurOptLen=ntohs(*(uint16_t*)(pu8Pos+2))+4; + if(u16CurOptLen<4 || pu8Pos>pu8OptionsEnd-u16CurOptLen) //overflow or beyond end of message? + return InvalidDnsMsgErrorOutput(-1); //failed + //EDNS Client Subnet option? + if(ntohs(*(uint16_t*)pu8Pos)==8) + { + if(!g_bForceEcs) + return -1; //special return value to send original DNS message as we shall not replace ECS + //replace existing option + //correct parameters for lower message creation + pu8OptionsEnd=pu8Pos+u16CurOptLen; //behind EDNS Client Subnet option + u16OptLen-=u16CurOptLen; + u16Len-=u16CurOptLen; + break; + } + } + u32NewLen=u16Len+g_iEcsOptionLen; + //new length exceeds maximum? + if(u32NewLen>65535) + { + OutputToLog(OUTPUT_ALL, "Cannot add EDNS client subnet info to DNS request as it would exceed the maximum size"); + return 0; + } + *u16aDestBuf=htons((uint16_t)u32NewLen); + u16LenIncludingOptionsToCopy=(uint16_t)(pu8Pos-(uint8_t*)u16aRequest); + memcpy(u16aDestBuf+1, u16aRequest+1, u16LenIncludingOptionsToCopy-2); //copy part of original request without length field + memcpy(((uint8_t*)u16aDestBuf)+u16LenIncludingOptionsToCopy, g_u8aEcsOption, g_iEcsOptionLen); //add ECS option + memcpy(((uint8_t*)u16aDestBuf)+u16LenIncludingOptionsToCopy+g_iEcsOptionLen, pu8OptionsEnd, pu8MessageEnd-pu8OptionsEnd); //copy rest of original message + //correct length of options + *(uint16_t*)(((uint8_t*)u16aDestBuf)+iOffset-2)=htons((uint16_t)(u16OptLen+g_iEcsOptionLen)); + return (int)(u32NewLen+2); //+2 for length field + } + } +} + +//sends CONNECT command to HTTP proxy and checks answer +static int HandleHttpProxy(SOCKET hSock, char* caBuf) +{ + char* szErrMsg; + char* szPosEnd; + const char* szPosStatus; + int iRet; + int iPos; + + iRet=send(hSock, g_caHttpProxyConnect, g_iHttpProxyConnectLen, MSG_NOSIGNAL); + if(iRet!=g_iHttpProxyConnectLen) + { + szErrMsg=(iRet==SOCKET_ERROR)?GetSysError(WSAGetLastError()):"Invalid amount of sent bytes"; + OutputToLog(OUTPUT_ALL, "Sending to HTTP proxy has failed: %s", szErrMsg); + if(iRet==SOCKET_ERROR) + FreeSysError(szErrMsg); + return 0; //error + } + //receive answer + iPos=0; + for(;;) + { + iRet=recv(hSock, caBuf+iPos, 2000-iPos, 0); + if(iRet<=0) + { + if(iRet==SOCKET_ERROR) + OutputToLog(OUTPUT_ALL, "The HTTP proxy has closed the connection unexpectedly"); + else + { + szErrMsg=GetSysError(WSAGetLastError()); + OutputToLog(OUTPUT_ALL, "Receiving from HTTP proxy has failed: %s", szErrMsg); + FreeSysError(szErrMsg); + } + return 0; //error + } + iPos+=iRet; + if(iPos<4) + continue; //continue receiving + //check header, must begin with HTTP + if(caBuf[0]=='H' || caBuf[1]=='T' || caBuf[2]=='T' || caBuf[3]=='P') + { + caBuf[iPos]='\0'; //terminate the string + //try to find empty line that marks the end of the HTTP proxy answer + szPosEnd=strstr(caBuf, "\r\n\r\n"); + if(!szPosEnd) + continue; //continue receiving + //extract status code -> scan for space character (also stops on any control character) + szPosStatus=caBuf+4; + while(*(unsigned char*)szPosStatus>' ') + ++szPosStatus; + if(*szPosStatus==' ') + { + //we want: 200 Connection established + if(atoi(++szPosStatus)==200) + break; //header reception complete + //find end of line (must be there, otherwise upper search for empty line would have failed + szPosEnd=strchr(szPosStatus, '\r'); + *szPosEnd='\0'; + OutputToLog(OUTPUT_ALL, "Connecting DNS server has failed: %s", szPosStatus); + return 0; //error + } + } + OutputToLog(OUTPUT_ALL, "Invalid answer from HTTP proxy"); + return 0; //error + } + //so far there shouldn't be an answer from the DNS server (nothing after \r\n\r\n) + if(szPosEnd[4]) + { + OutputToLog(OUTPUT_ALL, "DNS server answered before request"); + return 0; //error + } + return 1; //o.k. +} + +//thread for connecting the SOCKS server and resolving the DNS request +THREAD_FUNCTION(DnsThread, pEntry) +{ + uint16_t u16aBuf[32769]; //max DNS packet length 2(length)+65535(data) bytes - using uint16_t here for alignment + struct SEntry* psEntry=(struct SEntry*)pEntry; + char* szErrMsg; + SOCKET hSock=socket(g_sSocksAddr.ss_family, SOCK_STREAM, IPPROTO_TCP); + int iRet; + int iPos; + int iLen; + + if(hSock==SOCKET_ERROR) + { + szErrMsg=GetSysError(WSAGetLastError()); + OutputToLog(OUTPUT_ALL, "Creating a TCP socket has failed: %s", szErrMsg); + FreeSysError(szErrMsg); + RemoveEntry(psEntry, hSock, g_bCacheEnabled); + return 0; + } + //connect SOCKS server or HTTP proxy + if(connect(hSock, (struct sockaddr*)&g_sSocksAddr, GetAddrLen(&g_sSocksAddr))==SOCKET_ERROR) + { + szErrMsg=GetSysError(WSAGetLastError()); + OutputToLog(OUTPUT_ALL, "Connecting the SOCKS server has failed: %s", szErrMsg); + FreeSysError(szErrMsg); + RemoveEntry(psEntry, hSock, g_bCacheEnabled); + return 0; + } + //using HTTP proxy instead of SOCKS? + if(g_iHttpProxyConnectLen) + { + if(!HandleHttpProxy(hSock, (char*)u16aBuf)) + { + RemoveEntry(psEntry, hSock, g_bCacheEnabled); + return 0; + } + } + else + { + ((uint8_t*)u16aBuf)[0]=5; //version 5 as we use SOCKS5 + ((uint8_t*)u16aBuf)[1]=1; //number of authentication methods supported + ((uint8_t*)u16aBuf)[2]=g_uaUsrPwd?2:0; //user/password authentication or no authentication + iLen=send(hSock, (const char*)u16aBuf, 3, MSG_NOSIGNAL); + if(iLen!=3) + { + szErrMsg=(iLen==SOCKET_ERROR)?GetSysError(WSAGetLastError()):"Invalid amount of sent bytes"; + OutputToLog(OUTPUT_ALL, "Sending to SOCKS server has failed: %s", szErrMsg); + if(iLen==SOCKET_ERROR) + FreeSysError(szErrMsg); + RemoveEntry(psEntry, hSock, g_bCacheEnabled); + return 0; + } + //check respons + if(!ReceiveBytes(hSock, 2, u16aBuf+8)) + { + RemoveEntry(psEntry, hSock, g_bCacheEnabled); + return 0; + } + if(((uint8_t*)u16aBuf)[16]!=5 || ((uint8_t*)u16aBuf)[17]!=((uint8_t*)u16aBuf)[2]) + { + if(((uint8_t*)u16aBuf)[16]!=5) + OutputToLog(OUTPUT_ALL, "The SOCKS server has answered with a SOCKS version number unequal to 5"); + else if(g_uaUsrPwd) + OutputToLog(OUTPUT_ALL, "The SOCKS server does not support user/password authentication"); + else + OutputToLog(OUTPUT_ALL, "The SOCKS server wants an authentication"); + RemoveEntry(psEntry, hSock, g_bCacheEnabled); + return 0; + } + //send authentication if enabled + if(g_uaUsrPwd) + { + iLen=send(hSock, (const char*)g_uaUsrPwd, g_iUsrPwdLen, MSG_NOSIGNAL); + if(iLen!=g_iUsrPwdLen) + { + szErrMsg=(iLen==SOCKET_ERROR)?GetSysError(WSAGetLastError()):"Invalid amount of sent bytes"; + OutputToLog(OUTPUT_ALL, "Sending to SOCKS server has failed: %s", szErrMsg); + if(iLen==SOCKET_ERROR) + FreeSysError(szErrMsg); + RemoveEntry(psEntry, hSock, g_bCacheEnabled); + return 0; + } + //check response + if(!ReceiveBytes(hSock, 2, u16aBuf+8)) + { + RemoveEntry(psEntry, hSock, g_bCacheEnabled); + return 0; + } + if(((uint8_t*)u16aBuf)[17]) //2nd byte of answer must be 0 for "success" + { + OutputToLog(OUTPUT_ALL, "The SOCKS server authentication has failed (error code %u)", (unsigned int)((uint8_t*)u16aBuf)[17]); + RemoveEntry(psEntry, hSock, g_bCacheEnabled); + return 0; + } + } + //connect DNS server via SOCKS, the DNS server must support TCP + ((uint8_t*)u16aBuf)[1]=1; //establish a TCP/IP stream connection + ((uint8_t*)u16aBuf)[2]=0; //reserved, must be 0x00 + switch(g_sDnsSrvAddr.ss_family) + { + case AF_UNSPEC: //use name + ((uint8_t*)u16aBuf)[3]=3; //name + iLen=(int)((struct sockaddr_in6*)&g_sDnsSrvAddr)->sin6_flowinfo; //length in sin6_flowinfo (see ParseIpAndPort) + ((uint8_t*)u16aBuf)[4]=(uint8_t)iLen; //maximum length is 255 + memcpy(((uint8_t*)u16aBuf)+5, *(char**)&((struct sockaddr_in6*)&g_sDnsSrvAddr)->sin6_addr, iLen); //copy name (see ParseIpAndPort) + *(uint16_t*)(((uint8_t*)u16aBuf)+iLen+5)=((struct sockaddr_in6*)&g_sDnsSrvAddr)->sin6_port; //port + iPos=iLen+7; + break; + case AF_INET: //use IPv4 + ((uint8_t*)u16aBuf)[3]=1; //IPv4 address + *(uint32_t*)(u16aBuf+2)=((struct sockaddr_in*)&g_sDnsSrvAddr)->sin_addr.s_addr; //address + *(uint16_t*)(u16aBuf+4)=((struct sockaddr_in*)&g_sDnsSrvAddr)->sin_port; //port + iPos=10; + break; + default: //use IPv6 + ((uint8_t*)u16aBuf)[3]=4; //IPv6 address + memcpy(u16aBuf+2, &((struct sockaddr_in6*)&g_sDnsSrvAddr)->sin6_addr, 16); //address + *(uint16_t*)(u16aBuf+10)=((struct sockaddr_in6*)&g_sDnsSrvAddr)->sin6_port; //port + iPos=22; + } + iLen=send(hSock, (const char*)u16aBuf, iPos, MSG_NOSIGNAL); + if(iLen!=iPos) + { + szErrMsg=(iLen==SOCKET_ERROR)?GetSysError(WSAGetLastError()):"Invalid amount of sent bytes"; + OutputToLog(OUTPUT_ALL, "Connecting through SOCKS server has failed: %s", szErrMsg); + if(iLen==SOCKET_ERROR) + FreeSysError(szErrMsg); + RemoveEntry(psEntry, hSock, g_bCacheEnabled); + return 0; + } + //check expected answer (get first 5 bytes to detect address type) + if(!ReceiveBytes(hSock, 5, u16aBuf)) + { + RemoveEntry(psEntry, hSock, g_bCacheEnabled); + return 0; + } + if(((uint8_t*)u16aBuf)[0]!=5 || ((uint8_t*)u16aBuf)[1]!=0 || (((uint8_t*)u16aBuf)[3]!=1 && ((uint8_t*)u16aBuf)[3]!=3 && ((uint8_t*)u16aBuf)[3]!=4)) + { + szErrMsg="Unexpected answer from SOCKS server"; + //correct version -> try to resolve the error code + if(((uint8_t*)u16aBuf)[0]==5) + switch(((uint8_t*)u16aBuf)[1]) + { + case 2: + szErrMsg="Connection not allowed by ruleset"; + break; + case 3: + szErrMsg="Network unreachable"; + break; + case 4: + szErrMsg="Host unreachable"; + break; + case 5: + szErrMsg="Connection refused by destination host"; + break; + case 6: + szErrMsg="TTL expired"; + break; + case 7: + szErrMsg="Command not supported / protocol error"; + break; + case 8: + szErrMsg="Address type not supported"; + } + OutputToLog(OUTPUT_ALL, "Connecting through SOCKS server has failed: %s", szErrMsg); + RemoveEntry(psEntry, hSock, g_bCacheEnabled); + return 0; + } + //get rest of answer + //all bytes after that are part of the "normal" communication + switch(((uint8_t*)u16aBuf)[3]) + { + case 1: //IPv4 + iLen=5; + break; + case 4: //IPv6 + iLen=17; + break; + default: //name + iLen=2+((uint8_t*)u16aBuf)[4]; //port length plus length of name + } + if(!ReceiveBytes(hSock, iLen, u16aBuf)) + { + RemoveEntry(psEntry, hSock, g_bCacheEnabled); + return 0; + } + } + //send DNS request via SOCKS; TCP variant starts with 2 byte length field + //add ECS? + if(g_iEcsOptionLen) + iPos=AddEcsOption(psEntry->u16aRequest, u16aBuf); + else + iPos=-1; //no ECS -> send original message + switch(iPos) + { + case 0: //error + RemoveEntry(psEntry, hSock, g_bCacheEnabled); + return 0; + case -1: //send original message + iPos=2+ntohs(*psEntry->u16aRequest); + iLen=send(hSock, (const char*)psEntry->u16aRequest, iPos, MSG_NOSIGNAL); + break; + default: //send message created by AddEcsOption + iLen=send(hSock, (const char*)u16aBuf, iPos, MSG_NOSIGNAL); + } + //error or not sent all bytes? + if(iLen!=iPos) + { + szErrMsg=(iLen==SOCKET_ERROR)?GetSysError(WSAGetLastError()):"Invalid amount of sent bytes"; + OutputToLog(OUTPUT_ALL, "Sending through SOCKS server has failed: %s", szErrMsg); + if(iLen==SOCKET_ERROR) + FreeSysError(szErrMsg); + RemoveEntry(psEntry, hSock, g_bCacheEnabled); + return 0; + } + //receive answer + iPos=0; + for(;;) + { + iRet=recv(hSock, (char*)u16aBuf+iPos, sizeof(u16aBuf)-iPos, 0); + if(iRet<=0) + { + szErrMsg=(iRet==SOCKET_ERROR)?GetSysError(WSAGetLastError()):"Server has closed the connection unexpectedly"; + OutputToLog(OUTPUT_ALL, "Broken answer from DNS server: %s", szErrMsg); + if(iRet==SOCKET_ERROR) + FreeSysError(szErrMsg); + RemoveEntry(psEntry, hSock, g_bCacheEnabled); + return 0; + } + iPos+=iRet; + //first 2 bytes contain length (Big Endian) + if(iPos>=2) + { + iLen=2+ntohs(*u16aBuf); //iLen maximum is 2+65535 (smaller than sizeof(u16aBuf)) + if(iPos>=iLen) + break; //answer completely received + } + } + //invalid answer? + if(iPos<=4) + { + OutputToLog(OUTPUT_ALL, "Answer from DNS server too short!"); + RemoveEntry(psEntry, hSock, g_bCacheEnabled); + return 0; + } + closesocket(hSock); + if(g_bCacheEnabled) + { + //store answer in cache + EnterCriticalSection(&g_sCritSect); + psEntry->u16aAnswer=(uint16_t*)malloc(iLen); + memcpy(psEntry->u16aAnswer, u16aBuf, iLen); + //copy ID + psEntry->u16aAnswer[1]=psEntry->u16aRequest[1]; + //remember current time for time to live calculations + psEntry->iTime=time(NULL); + //send DNS answer to original requesting client via UDP or TCP + SendAnswer(psEntry); + LeaveCriticalSection(&g_sCritSect); + } + else + { + //send DNS answer to original requesting client via UDP or TCP + psEntry->u16aAnswer=u16aBuf; + SendAnswer(psEntry); + free(psEntry); + } + return 0; +} + +//searches the cache for the same request and sends the answer if there is a cache hit +//or creates a thread for forwarding the request to the DNS server via SOCKS +static void HandleDnsRequest(uint16_t* u16aRequest, int iLen, void* pClientAddr, socklen_t iAddrLen) +{ + uint8_t* pu8Pos; + uint8_t* pu8End; + struct SEntry* psEntry; + unsigned char uLen; + + //search in cache + EnterCriticalSection(&g_sCritSect); + for(psEntry=g_psFirst; ; psEntry=psEntry->psNext) + { + if(!psEntry) + { + //create new entry (length of struct SEntry up to u16aRequest plus request length plus 2 for length) + psEntry=(struct SEntry*)malloc(((uint8_t*)psEntry->u16aRequest-(uint8_t*)psEntry)+iLen+2); + psEntry->iTime=(time_t)-1; + psEntry->u16aAnswer=NULL; + psEntry->uAddrLen=(unsigned char)iAddrLen; + memcpy(&psEntry->client, pClientAddr, iAddrLen); + *psEntry->u16aRequest=htons((uint16_t)iLen); + memcpy(psEntry->u16aRequest+1, u16aRequest, iLen); + //add entry to cache list in case cache is enabled + psEntry->psNext=g_psFirst; + if(g_bCacheEnabled) + g_psFirst=psEntry; + //create thread to resolve entry + if(ThreadCreate(DnsThread, psEntry)) + { + ++g_uCacheCount; + //output amount of entries and current entry + pu8Pos=(uint8_t*)u16aRequest+12; + pu8End=(uint8_t*)u16aRequest+iLen; + while(pu8Pos=0xc0) //compression used? (reference to other name via offset) + break; //no output in this case + *pu8Pos='.'; //replace length by . + pu8Pos+=uLen+1; + } + } + else + { + //remove entry from cache list + g_psFirst=psEntry->psNext; + free(psEntry); + } + break; + } + //cache hit? (do not compare ID in first 2 bytes of request) + if((uint16_t)iLen==ntohs(*psEntry->u16aRequest) && memcmp(u16aRequest+1, psEntry->u16aRequest+2, iLen-2)==0) + { + //answer already received? + if(psEntry->u16aAnswer) + { + //answer to current address + psEntry->uAddrLen=(unsigned char)iAddrLen; + memcpy(&psEntry->client, pClientAddr, iAddrLen); + //check if expired + if(!CalculateTimeToLive(psEntry)) + { + //expired -> kill last answer + free(psEntry->u16aAnswer); + psEntry->u16aAnswer=NULL; + //copy current ID + psEntry->u16aRequest[1]=*u16aRequest; + //create thread to resolve request again + if(!ThreadCreate(DnsThread, psEntry)) + RemoveEntry(psEntry, (SOCKET)SOCKET_ERROR, 0); + } + else + { + //use current ID + psEntry->u16aAnswer[1]=*u16aRequest; + SendAnswer(psEntry); + } + } + else + { + //copy current ID so the thread uses that one if it gets the answer + psEntry->u16aRequest[1]=*u16aRequest; + //overwrite address; currently we can only handle one request address while waiting for an answer through SOCKS + //current address is TCP? -> need to close it before overwriting it + if(psEntry->uAddrLen==sizeof(SOCKET)) + closesocket(psEntry->client.hSock); + psEntry->uAddrLen=(unsigned char)iAddrLen; + memcpy(&psEntry->client, pClientAddr, iAddrLen); + } + break; + } + } + LeaveCriticalSection(&g_sCritSect); +} + +//outputs an error caused by "bind" and closes the according socket +static void OutputBindError(SOCKET hSock, struct sockaddr_storage* psAddr, int bUdp) +{ + char szAddr[256]; + char szNo[16]; + char* szErrMsg=GetSysError(WSAGetLastError()); + + closesocket(hSock); + if(getnameinfo((struct sockaddr*)psAddr, GetAddrLen(psAddr), szAddr, sizeof(szAddr), szNo, sizeof(szNo), NI_NUMERICHOST|NI_NUMERICSERV)) + { + //should never happen + strcpy(szAddr, "unknown address"); + strcpy(szNo, "unknown"); + } + //UDP is mandatory, TCP is optional + if(bUdp) + { + //need { ... } on *nix as OutputFatal is a multi-line macro there + OutputFatal("\nBinding on %s, UDP port %s has failed: %s\n", szAddr, szNo, szErrMsg); + } + else + OutputToLog(OUTPUT_ALL, "Binding on %s, TCP port %s has failed: %s", szAddr, szNo, szErrMsg); + FreeSysError(szErrMsg); +} + +//thread for receiving DNS requests via TCP +THREAD_FUNCTION(TcpThread, pAddr) +{ + SOCKET hSockServer; + SOCKET hSock; + uint16_t u16aBuf[32769]; //maximum possible DNS request size plus one byte - using uint16_t here for alignment; the actual size is stored in the first 2 bytes which can have a maximum value of 0xffff + socklen_t iAddrLen; + int iCurBufLen; + int iLen; + uint16_t u16ReqLen; + struct sockaddr_storage sAddr; + char* szErrMsg; + + hSockServer=socket(((struct sockaddr_storage*)pAddr)->ss_family, SOCK_STREAM, IPPROTO_TCP); + if(hSockServer==SOCKET_ERROR) + { + szErrMsg=GetSysError(WSAGetLastError()); + OutputToLog(OUTPUT_ALL, "Creating a TCP socket has failed: %s", szErrMsg); + FreeSysError(szErrMsg); + return 0; + } + //bind+listen on local TCP port (get DNS requests) + if(bind(hSockServer, (struct sockaddr*)pAddr, GetAddrLen((struct sockaddr_storage*)pAddr))==SOCKET_ERROR) + { + OutputBindError(hSockServer, (struct sockaddr_storage*)pAddr, 0); + return 0; + } + if(listen(hSockServer, 5)==SOCKET_ERROR) + { + szErrMsg=GetSysError(WSAGetLastError()); + closesocket(hSockServer); + OutputToLog(OUTPUT_ALL, "Listening on TCP socket has failed: %s", szErrMsg); + FreeSysError(szErrMsg); + return 0; + } + //as long as "accept" is working + for(;;) + { + iAddrLen=sizeof(sAddr); + hSock=accept(hSockServer, (struct sockaddr*)&sAddr, &iAddrLen); + if(hSock==SOCKET_ERROR) + { + szErrMsg=GetSysError(WSAGetLastError()); + OutputToLog(OUTPUT_ALL, "Accepting new connection on TCP socket has failed: %s", szErrMsg); + FreeSysError(szErrMsg); + break; + } + //collect the whole DNS request + iCurBufLen=0; + for(;;) + { + iLen=recv(hSock, (char*)u16aBuf+iCurBufLen, sizeof(u16aBuf)-iCurBufLen, 0); + if(iLen<=0) + { + szErrMsg=GetSysError(WSAGetLastError()); + closesocket(hSock); + OutputToLog(OUTPUT_ALL, "DNS request on TCP broken: %s", szErrMsg); + FreeSysError(szErrMsg); + break; + } + iCurBufLen+=iLen; + //got the whole DNS request? + if(iCurBufLen>=2 && (u16ReqLen=ntohs(*u16aBuf))+2>=iCurBufLen) + { + if(u16ReqLen>12) //12 bytes header plus at least one byte of data + HandleDnsRequest(u16aBuf+1, u16ReqLen, &hSock, sizeof(hSock)); //HandleDnsRequest takes care of hSock now + else + closesocket(hSock); + break; + } + } + } + closesocket(hSockServer); + return 0; +} + +//parses parameter /e:IP/Bits +static int ParseEcs(char* szIpAndAmountBits) +{ + struct addrinfo sHint; + struct addrinfo* psResult; + char* szPos=strchr(szIpAndAmountBits, '/'); + int iBits; + int iMaxBits; + + if(!szPos) + return 0; //error + *szPos='\0'; //overwrite '/' for getaddrinfo below + //prepare hint: only numeric values + memset(&sHint, 0, sizeof(sHint)); + sHint.ai_family=AF_UNSPEC; + sHint.ai_flags=AI_NUMERICHOST; + //now resolve it + if(getaddrinfo(szIpAndAmountBits, NULL, &sHint, &psResult)) + { + //some getaddrinfo implementations seem to have some trouble, so try the old IPv4 variant additionally + //use sHint for that + ((struct in_addr*)(g_u8aEcsOption+8))->s_addr=inet_addr(szIpAndAmountBits); + if(INADDR_NONE==((struct in_addr*)(g_u8aEcsOption+8))->s_addr) + { + OutputFatal("\nInvalid address '%s' specified for EDNS Client Subnet!\n", szIpAndAmountBits); + return 0; //error + } + g_u8aEcsOption[5]=1; //FAMILY IPv4 + iMaxBits=32; + } + else + { + switch(psResult->ai_family) + { + case AF_INET: + memcpy(g_u8aEcsOption+8, &((struct sockaddr_in*)psResult->ai_addr)->sin_addr, 4); + g_u8aEcsOption[5]=1; //FAMILY IPv4 + iMaxBits=32; + break; + case AF_INET6: + memcpy(g_u8aEcsOption+8, &((struct sockaddr_in6*)psResult->ai_addr)->sin6_addr, 16); + g_u8aEcsOption[5]=2; //FAMILY IPv6 + iMaxBits=128; + break; + default: + OutputFatal("\nInvalid address '%s' specified for EDNS Client Subnet!\n", szIpAndAmountBits); + return 0; //error + } + } + //check amount of bits to use + iBits=atoi(szPos+1); + if(iBits<=0 || iBits>iMaxBits) + { + OutputFatal("\nInvalid amount of bits '%s' specified for EDNS Client Subnet!\n", szPos+1); + return 0; //error + } + //OPTION-CODE + g_u8aEcsOption[0]=0; + g_u8aEcsOption[1]=8; + //OPTION-LENGTH + g_u8aEcsOption[2]=0; + g_iEcsOptionLen=8+(iBits+7)/8; //round up to amount bytes + g_u8aEcsOption[3]=(uint8_t)(g_iEcsOptionLen-4); + //mask last byte of address + g_u8aEcsOption[g_iEcsOptionLen-1]&=(uint8_t)(0xff<<(7-((iBits+7)&7))); + //set rest to 0 + memset(g_u8aEcsOption+g_iEcsOptionLen, 0, sizeof(g_u8aEcsOption)-g_iEcsOptionLen); + //1st byte of FAMILY always 0 + g_u8aEcsOption[4]=0; + //SOURCE PREFIX-LENGTH + g_u8aEcsOption[6]=(uint8_t)iBits; + //SCOPE PREFIX-LENGTH always 0 + g_u8aEcsOption[7]=0; + return 1; //o.k. +} + +//parses a command line parameter of the format IPv4 or IPv4:port or IPv6 or [IPv6]:port +static int ParseIpAndPort(int iFlag, const char* szParamName, const char* szPort, char* szIpAndPort, struct sockaddr_storage* psAddr) +{ + struct addrinfo sHint; + struct addrinfo* psResult; + char* szPos; + + if(strchr(szIpAndPort, '.')) + { + //seems to be IPv4 + szPos=strchr(szIpAndPort, ':'); + if(szPos) + { + *szPos='\0'; //overwrite ':' for getaddrinfo below + szPort=szPos+1; + } + } + else + { + //seems to be IPv6 + //format [IPv6]:port? + if(*szIpAndPort=='[') + { + ++szIpAndPort; + szPos=strchr(szIpAndPort, ']'); + if(szPos) + { + *szPos='\0'; //overwrite ']' for getaddrinfo below + //is there a port specification? + if(szPos[1]==':') + szPort=szPos+2; + } + } + } + + //prepare hint: only numeric values + memset(&sHint, 0, sizeof(sHint)); + sHint.ai_family=AF_UNSPEC; + sHint.ai_flags=iFlag|AI_NUMERICHOST|AI_NUMERICSERV; + //now resolve it + if(getaddrinfo(szIpAndPort, szPort, &sHint, &psResult)) + { + //some getaddrinfo implementations seem to have some trouble, so try the old IPv4 variant additionally + ((struct sockaddr_in*)psAddr)->sin_addr.s_addr=inet_addr(szIpAndPort); + if(INADDR_NONE!=((struct sockaddr_in*)psAddr)->sin_addr.s_addr) + { + ((struct sockaddr_in*)psAddr)->sin_port=htons((uint16_t)atoi(szPort)); + if(((struct sockaddr_in*)psAddr)->sin_port) + { + ((struct sockaddr_in*)psAddr)->sin_family=AF_INET; + return 1; //o.k. + } + } + //only for the DNS server also support name + if(&g_sDnsSrvAddr==psAddr) + { + size_t uLen=strlen(szIpAndPort); + + if(uLen<256 && uLen) + { + ((struct sockaddr_in6*)&g_sDnsSrvAddr)->sin6_port=htons((uint16_t)atoi(szPort)); + if(((struct sockaddr_in6*)&g_sDnsSrvAddr)->sin6_port) + { + g_sDnsSrvAddr.ss_family=AF_UNSPEC; //this marks usage of name + *(char**)&((struct sockaddr_in6*)&g_sDnsSrvAddr)->sin6_addr=szIpAndPort; //use sin6_addr for pointer to name (128 bit -> large enough, alignment should also be fine) + ((struct sockaddr_in6*)&g_sDnsSrvAddr)->sin6_flowinfo=(uint8_t)uLen; //use sin6_flowinfo for length + return 1; //o.k. + } + } + } + OutputFatal("\nInvalid address '%s' and port '%s' specified for %s!\n", szIpAndPort, szPort, szParamName); + return 0; //error + } + if(psResult->ai_addrlen>sizeof(*psAddr)) + { + //should never happen + OutputFatal("\nAddress '%s' and port '%s' specified for %s too long for internal storage!\n", szIpAndPort, szPort, szParamName); + return 0; //error + } + //copy 1st result + memcpy(psAddr, psResult->ai_addr, psResult->ai_addrlen); + freeaddrinfo(psResult); + return 1; //o.k. +} + +//creates CONNECT command for HTTP proxy containing DNS address +static int CreateHttpProxyConnectCommand() +{ + memcpy(g_caHttpProxyConnect, "CONNECT ", 8); + g_iHttpProxyConnectLen=8; + if(g_sDnsSrvAddr.ss_family==AF_UNSPEC) + { + //sin6_addr contains pointer to name, see ParseIpAndPort (max. length is 255) + int iLenDnsAddr=sprintf(g_caHttpProxyConnect+g_iHttpProxyConnectLen, "%s:%u", *(char**)&((struct sockaddr_in6*)&g_sDnsSrvAddr)->sin6_addr, ntohs(((struct sockaddr_in6*)&g_sDnsSrvAddr)->sin6_port)); + + if(iLenDnsAddr<=0) + return 0; //error + g_iHttpProxyConnectLen+=iLenDnsAddr; + } + else + { + //add IP and port to g_caHttpProxyConnect + char szPort[6]; + int iLenPort; + + if(g_sDnsSrvAddr.ss_family==AF_INET6) + g_caHttpProxyConnect[g_iHttpProxyConnectLen++]='['; //enclose with [] for IPv6 + if(getnameinfo((struct sockaddr*)&g_sDnsSrvAddr, GetAddrLen(&g_sDnsSrvAddr), g_caHttpProxyConnect+g_iHttpProxyConnectLen, 128, szPort, sizeof(szPort), NI_NUMERICHOST|NI_NUMERICSERV)) + return 0; //error + g_iHttpProxyConnectLen+=(int)strlen(g_caHttpProxyConnect+g_iHttpProxyConnectLen); + if(g_sDnsSrvAddr.ss_family==AF_INET6) + g_caHttpProxyConnect[g_iHttpProxyConnectLen++]=']'; //enclose with [] for IPv6 + g_caHttpProxyConnect[g_iHttpProxyConnectLen++]=':'; + iLenPort=(int)strlen(szPort); + memcpy(g_caHttpProxyConnect+g_iHttpProxyConnectLen, szPort, iLenPort); + g_iHttpProxyConnectLen+=iLenPort; + } + //complete command + memcpy(g_caHttpProxyConnect+g_iHttpProxyConnectLen, " HTTP/1.0\r\n\r\n", 13); + g_iHttpProxyConnectLen+=13; + return 1; //o.k. +} + +int main(int iArgCount, char** szaArgs) +{ + static struct sockaddr_storage sAddr; //make it static so the lower array assignment causes no compiler warning + const char* szUser; + const char* szPassword; + char** pszCurArg; + char* szCurArg; + char* szLogFilePath; + char* szErrMsg; + size_t uUserLen; + size_t uPasswordLen; + socklen_t iAddrLen; + int iAddrCount; + int iLen; + int bAppend; //append log file? + int bQuiet; //parameter q specified? + uint16_t u16aBuf[32754]; //max UDP packet length on Windows plus one byte - using uint16_t here for alignment + //struct array for the three addresses passed via command line + struct SAddr + { + struct sockaddr_storage* psAddr; + const char* szDefaultAddress; + const char* szDefaultPort; + const char* szName; + int iFlag; + } saAddresses[3]= + { + { &g_sSocksAddr, DEFAULT_SOCKS_SERVER, DEFAULT_SOCKS_PORT, "SOCKS server", 0 }, + { &g_sDnsSrvAddr, DEFAULT_DNS_SERVER, DEFAULT_DNS_PORT, "DNS server", 0 }, + { &sAddr, DEFAULT_LISTEN_IP, DEFAULT_DNS_PORT, "listening", AI_PASSIVE } //AI_PASSIVE for "getaddrinfo" as we use it for "bind" + }; + + //parse command line - use e.g. "/?" to display the usage + bQuiet=0; + bAppend=0; + iAddrCount=0; + szLogFilePath=NULL; + szUser=NULL; + szPassword=NULL; + for(pszCurArg=szaArgs+1; --iArgCount; ++pszCurArg) + { + szCurArg=*pszCurArg; + //no address parameter? + if(*szCurArg=='-' || *szCurArg=='/') + { + switch(szCurArg[1]) + { + case 'd': //disable cache? + case 'D': + if(!szCurArg[2]) + { + g_bCacheEnabled=0; + continue; //correct parameter, go to next one + } + break; + case 'q': //no console output? + case 'Q': + if(!szCurArg[2]) + { + bQuiet=1; + continue; //correct parameter, go to next one + } + break; + case 'l': //log output? + case 'L': + if(!szLogFilePath) //only allowed once + { + bAppend=(szCurArg[2]=='a' || szCurArg[2]=='A'); //append? + if(szCurArg[2+bAppend]==':') + { + szLogFilePath=szCurArg+3+bAppend; + continue; //correct parameter, go to next one + } + } + break; + case 'u': //user? + case 'U': + if(!szUser && szCurArg[2]==':') //only allowed once and : must be 2nd char + { + szUser=szCurArg+3; + continue; //correct parameter, go to next one + } + break; + case 'p': //password? + case 'P': + if(!szPassword && szCurArg[2]==':') //only allowed once and : must be 2nd char + { + szPassword=szCurArg+3; + continue; //correct parameter, go to next one + } + break; + case 't': //HTTP proxy? + case 'T': + g_iHttpProxyConnectLen=1; //used as boolean here; later it contains the real length of the CONNECT command + continue; //correct parameter, go to next one + case 'e': //EDNS Client Subnet + case 'E': + //only allowed once + if(!g_iEcsOptionLen) + { + g_bForceEcs=(szCurArg[2]=='f' || szCurArg[2]=='F'); //force? + szCurArg+=g_bForceEcs; + if(szCurArg[2]==':' && ParseEcs(szCurArg+3)) + continue; //correct parameter, go to next one + } + } + //wrong/unknown parameter; display help + } + else + { + //try to parse address + if(iAddrCount display usage and stop + OutputFatal("\nDNS2SOCKS tunnels DNS requests via SOCKS5 and caches the answers.\n\n\n" + "Usage:\n\n" + "DNS2SOCKS [/?] [/t] [/d] [/q] [/l[a]:FilePath] [/u:User /p:Password]\n" + " [/e[f]:IP/Bits]\n" + " [Socks5ServerIP[:Port]] [DNSServerIPorName[:Port]] [ListenIP[:Port]]\n\n" + "/? to view this help\n" + "/t to use a HTTP proxy instead of a SOCKS server\n" + " (here: Socks5ServerIP = HttpProxyIP, no support for /u and /p)\n" + "/d to disable the cache\n" + "/q to suppress the text output\n" + "/l:FilePath to create a new log file \"FilePath\"\n" + "/la:FilePath to create a new log file or append to the existing \"FilePath\"\n" + "/u:User user name if your SOCKS server uses user/password authentication\n" + "/p:Password password if your SOCKS server uses user/password authentication\n" + "/e:IP/Bits to enable and specify EDNS client subnet in DNS queries\n" + "/ef:IP/Bits same as above but also replaces EDNS client subnet if it exists\n\n" + "Default Socks5ServerIP:Port = %s:%s\n" + "Default DNSServerIPorName:Port = %s:%s\n" + "Default ListenIP:Port = %s:%s\n", + DEFAULT_SOCKS_SERVER, DEFAULT_SOCKS_PORT, + DEFAULT_DNS_SERVER, DEFAULT_DNS_PORT, + DEFAULT_LISTEN_IP, DEFAULT_DNS_PORT); + return 1; + } + if(szPassword && !szUser) + { + OutputFatal("\nPassword specified but no user!\n"); + return 1; + } + if(!szPassword && szUser) + { + OutputFatal("\nUser specified but no password!\n"); + return 1; + } + if(g_iHttpProxyConnectLen && szPassword) + { + OutputFatal("\nAuthentication not supported for HTTP proxy!\n"); + return 1; + } + if(szUser) + { + uUserLen=strlen(szUser); + if(uUserLen>255) + { + OutputFatal("\nUser exceeds 255 characters!\n"); + return 1; + } + uPasswordLen=strlen(szPassword); + if(uPasswordLen>255) + { + OutputFatal("\nPassword exceeds 255 characters!\n"); + return 1; + } + } + else + { + //initialize uUserLen and uPasswordLen - otherwise VC++ 2010 outputs a wrong warning + uUserLen=0; + uPasswordLen=0; + } + + //fill unspecified addresses with default values + while(iAddrCountsin6_addr); + sprintf((char*)u16aBuf+768, "%hu", ntohs(((struct sockaddr_in6*)&g_sDnsSrvAddr)->sin6_port)); + } + else if(getnameinfo((struct sockaddr*)&g_sDnsSrvAddr, GetAddrLen(&g_sDnsSrvAddr), (char*)u16aBuf+512, 256, (char*)u16aBuf+768, 256, NI_NUMERICHOST|NI_NUMERICSERV)) + { + //should never happen + strcpy((char*)u16aBuf+512, "unknown address"); + strcpy((char*)u16aBuf+768, "unknown"); + } + if(getnameinfo((struct sockaddr*)&sAddr, GetAddrLen(&sAddr), (char*)u16aBuf+1024, 256, (char*)u16aBuf+1280, 256, NI_NUMERICHOST|NI_NUMERICSERV)) + { + //should never happen + strcpy((char*)u16aBuf+1024, "unknown address"); + strcpy((char*)u16aBuf+1280, "unknown"); + } + + //use (char*)u16aBuf+1536 as string buffer to output information about ECS + if(g_iEcsOptionLen) + { + struct sockaddr_storage sEcsAddr; + socklen_t uSockLen; + + memset(&sEcsAddr, 0, sizeof(sEcsAddr)); + //IPv6? + if(g_u8aEcsOption[5]==2) + { + uSockLen=sizeof(struct sockaddr_in6); + ((struct sockaddr_in6*)&sEcsAddr)->sin6_family=AF_INET6; + memcpy(&((struct sockaddr_in6*)&sEcsAddr)->sin6_addr, g_u8aEcsOption+8, 16); + } + else + { + uSockLen=sizeof(struct sockaddr_in); + ((struct sockaddr_in*)&sEcsAddr)->sin_family=AF_INET; + memcpy(&((struct sockaddr_in*)&sEcsAddr)->sin_addr, g_u8aEcsOption+8, 4); + } + if(getnameinfo((struct sockaddr*)&sEcsAddr, uSockLen, (char*)u16aBuf+1536, 256, NULL, 0, NI_NUMERICHOST)) + { + //should never happen + strcpy((char*)u16aBuf+1536, "unknown address"); + } + else + //add amount of bits + sprintf((char*)u16aBuf+1536+strlen((char*)u16aBuf+1536), "/%u", (unsigned int)g_u8aEcsOption[6]); + //add " forced" if g_bForceEcs + if(g_bForceEcs) + strcat((char*)u16aBuf+1536, " forced"); + } + else + strcpy((char*)u16aBuf+1536, "disabled"); + + //output configuration + OutputToLog(OUTPUT_LINE_BREAK|OUTPUT_CONSOLE, "%s %s port %s\n" + "DNS server %s port %s\n" + "listening on %s port %s\n" + "cache %s\n" + "authentication %s\n" + "EDNS client subnet %s\n", + g_iHttpProxyConnectLen?"HTTP proxy ":"SOCKS server", (char*)u16aBuf, (char*)u16aBuf+256, + (char*)u16aBuf+512, (char*)u16aBuf+768, + (char*)u16aBuf+1024, (char*)u16aBuf+1280, + g_bCacheEnabled?"enabled":"disabled", + szUser?"enabled":"disabled", + (char*)u16aBuf+1536); + + InitializeCriticalSection(&g_sCritSect); + + //log file was requested? + if(szLogFilePath) + OpenLogFile(szLogFilePath, bAppend); + + //create authentication package if user/password was specified + if(szUser) + { + g_iUsrPwdLen=(int)(uUserLen+uPasswordLen+3); + g_uaUsrPwd=(unsigned char*)malloc(g_iUsrPwdLen); + g_uaUsrPwd[0]=1; //version 1 + g_uaUsrPwd[1]=(unsigned char)uUserLen; + memcpy(g_uaUsrPwd+2, szUser, uUserLen); + g_uaUsrPwd[uUserLen+2]=(unsigned char)uPasswordLen; + memcpy(g_uaUsrPwd+uUserLen+3, szPassword, uPasswordLen); + } + + //create thread for TCP connection + ThreadCreate(TcpThread, &sAddr); + + //endless loop + for(;;) + { + //receive DNS request + iAddrLen=sizeof(sAddr); + iLen=recvfrom(g_hSockUdp, (char*)u16aBuf, sizeof(u16aBuf), 0, (struct sockaddr*)&sAddr, &iAddrLen); + if(iLen>12) //12 bytes header plus at least one byte of data + HandleDnsRequest(u16aBuf, iLen, &sAddr, iAddrLen); + } + /*no cleanup code as we have an endless loop + we would need something like this: + terminate all threads and clear their resources + free(g_uaUsrPwd); + DeleteCriticalSection(&g_sCritSect); + closesocket(g_hSockUdp); + free all cache entries + close the console + close the log file + return 0;*/ +} + +#ifdef _WIN32 + +//entry function for Windows applications - just forwards to "main" +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLine, int iCmdShow) +{ + WSADATA sData; + char* szErrMsg; + int iErrNo; + + hInstance; + hPrevInstance; + szCmdLine; + iCmdShow; + + iErrNo=WSAStartup(0x202, &sData); + if(iErrNo) + { + szErrMsg=GetSysError(iErrNo); + OutputFatal("\nWSAStartup has failed: %s\n", szErrMsg); + FreeSysError(szErrMsg); + return 1; + } + + //call "main" with the already parsed command line parameters stored in global variables by CRT + iErrNo=main(__argc, __argv); + + WSACleanup(); + return iErrNo; +} + +#endif //#ifdef _WIN32 diff --git a/dns2socks/src/DNS2SOCKS/DNS2SOCKS.manifest b/dns2socks/src/DNS2SOCKS/DNS2SOCKS.manifest new file mode 100644 index 000000000..36ffa7683 --- /dev/null +++ b/dns2socks/src/DNS2SOCKS/DNS2SOCKS.manifest @@ -0,0 +1,24 @@ + + + + + + + + + + + + true + + + + + + + + + + + + \ No newline at end of file diff --git a/dns2socks/src/DNS2SOCKS/DNS2SOCKS.rc b/dns2socks/src/DNS2SOCKS/DNS2SOCKS.rc new file mode 100644 index 000000000..e428aab8b --- /dev/null +++ b/dns2socks/src/DNS2SOCKS/DNS2SOCKS.rc @@ -0,0 +1,35 @@ +#include + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) + +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "DNS2SOCKS.manifest" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 2,1,0,0 + PRODUCTVERSION 2,1,0,0 + FILEFLAGSMASK 0x3FL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x00040004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "FileDescription", "Tunnels DNS requests via SOCKS or HTTP" + VALUE "FileVersion", "2.1.0.0" + VALUE "OriginalFilename", "DNS2SOCKS.exe" + VALUE "ProductVersion", "2.1.0.0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 1200 + END +END \ No newline at end of file diff --git a/dns2socks/src/DNS2SOCKS/DNS2SOCKS.vcxproj b/dns2socks/src/DNS2SOCKS/DNS2SOCKS.vcxproj new file mode 100644 index 000000000..808046f49 --- /dev/null +++ b/dns2socks/src/DNS2SOCKS/DNS2SOCKS.vcxproj @@ -0,0 +1,101 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {F19D3415-2E79-45EF-9893-4D9E2010B976} + Win32Proj + DNS2SOCKS + + + + Application + true + NotSet + v140 + + + Application + false + true + NotSet + v140 + + + + + + + + + + + + + true + false + + + false + false + + + + Use + Level4 + Disabled + WIN32;_DEBUG;%(PreprocessorDefinitions) + true + false + + + Windows + true + ws2_32.lib + + + + + Level4 + Use + MaxSpeed + true + true + WIN32;NDEBUG;%(PreprocessorDefinitions) + true + false + MultiThreaded + + + Windows + false + true + true + ws2_32.lib + + + + + + + + + Create + Create + + + + + + + + + \ No newline at end of file diff --git a/dns2socks/src/DNS2SOCKS/DNS2SOCKS.vcxproj.filters b/dns2socks/src/DNS2SOCKS/DNS2SOCKS.vcxproj.filters new file mode 100644 index 000000000..4545c1252 --- /dev/null +++ b/dns2socks/src/DNS2SOCKS/DNS2SOCKS.vcxproj.filters @@ -0,0 +1,31 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + + + Header Files + + + + + Source Files + + + Source Files + + + + + Source Files + + + \ No newline at end of file diff --git a/dns2socks/src/DNS2SOCKS/stdafx.c b/dns2socks/src/DNS2SOCKS/stdafx.c new file mode 100644 index 000000000..1577c4e3b --- /dev/null +++ b/dns2socks/src/DNS2SOCKS/stdafx.c @@ -0,0 +1 @@ +#include "stdafx.h" \ No newline at end of file diff --git a/dns2socks/src/DNS2SOCKS/stdafx.h b/dns2socks/src/DNS2SOCKS/stdafx.h new file mode 100644 index 000000000..c93f461ac --- /dev/null +++ b/dns2socks/src/DNS2SOCKS/stdafx.h @@ -0,0 +1,43 @@ +#ifndef _STDAFX_H +#define _STDAFX_H + +#ifdef _WIN32 + +//Windows specific includes +#define WINVER 0x501 +#define _CRT_SECURE_NO_WARNINGS +#define _WINSOCK_DEPRECATED_NO_WARNINGS +#include +#include +#include +#include +#include + +#else //#ifdef _WIN32 + +//other OS includes and some tricks to fake Win32 API +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +typedef int SOCKET; +#define SOCKET_ERROR (-1) +#define closesocket(hSock) close(hSock) +#define WSAGetLastError() errno +#define CRITICAL_SECTION pthread_mutex_t +#define InitializeCriticalSection(criticalSection) pthread_mutex_init(criticalSection, NULL) +#define EnterCriticalSection(criticalSection) pthread_mutex_lock(criticalSection) +#define LeaveCriticalSection(criticalSection) pthread_mutex_unlock(criticalSection) + +#endif //#ifdef _WIN32 + +#endif //#ifndef _STDAFX_H diff --git a/dns2socks/src/ReadMe.txt b/dns2socks/src/ReadMe.txt new file mode 100644 index 000000000..bf11fcef1 --- /dev/null +++ b/dns2socks/src/ReadMe.txt @@ -0,0 +1,315 @@ +Description for DNS2SOCKS +------------------------- + +DNS2SOCKS is a command line utility running to forward DNS requests to a +DNS server via a SOCKS or HTTP tunnel. + +I know that this is no new idea, but let me explain why I've coded this: +Windows supports using a SOCKS server for Internet connections, but it +only uses the SOCKS server for the content and not the DNS requests. I +found several articles in the Internet referring this issue. There seem +to be tools that do exactly the same thing as DNS2SOCKS, but they either +need a scripting interpreter or are not available for downloading anymore. + +So I've coded an own tool. It's very(!) simple and doesn't use any +sophisticated technology. + +To use it, just configure your OS to use the DNS server on the local +IP address 127.0.0.1 (IPv4) and/or ::1 (IPv6). On Windows: open the +properties of your network adapter. For IPv4 open the properties of +"Internet Protocol Version 4 (TCP/IPv4)", select "Use the following DNS +server addresses" and enter "127.0.0.1" for the "Preferred DNS server". +For IPv6 open the properties of "Internet Protocol Version 6 +(TCP/IPv6)", select "Use the following DNS server addresses" and enter +"::1" for the "Preferred DNS server". + +After that run your SOCKS server (must support SOCKS protocol version 5, +for example Tor) or HTTP proxy and start DNS2SOCKS using the correct +command line switches (see below). Now all DNS requests of your OS +(triggered by any application) run through DNS2SOCKS and your SOCKS +server or HTTP proxy. + +You can additionally configure Windows to use your SOCKS server for +Internet connections (for the content, not DNS). To do this open the +"Internet Options" of the control panel, select the tab "Connections" and +click on "LAN settings". Check "Use a proxy server for your LAN..." and +click on "Advanced". Enter your SOCKS server address and port in the field +"Socks". Now Internet Explorer and other tools using these settings get +web pages via your SOCKS server. This works with most browsers. However, +you should rely on IPv4 for Tor here as (most?) Tor exit servers currently +don't support IPv6 (see below). +The settings are similar for using a HTTP proxy. + +The command line call for DNS2SOCKS has the following format: + +DNS2SOCKS [/?] [/t] [/d] [/q] [l[a]:FilePath] [/u:User /p:Password] + [/e[f]:IP/Bits] + [Socks5ServIP[:Port]] [DNSServIPorName[:Port]] [ListenIP[:Port]] + +/? or any invalid parameter outputs the usage text +/t to use a HTTP proxy instead of a SOCKS server + (here: Socks5ServIP = HttpProxyIP, no support for /u and /p) +/d disables the cache +/q disables the text output to the console +/l:FilePath creates a new log file "FilePath" +/la:FilePath creates a new log file "FilePath" or appends to the file if + it already exists +/u:User user name if your SOCKS server uses user/password + authentication +/p:Password password if your SOCKS server uses user/password + authentication +/e:IP/Bits to enable and specify EDNS client subnet in DNS queries +/ef:IP/Bits same as above but also replaces EDNS client subnet if it + exists + +The default values for the addresses and ports are (in case you don't +specify the command line arguments): +Default Socks5ServerIP:Port = 127.0.0.1:9050 +Default DNSServerIPorName:Port = 46.182.19.48 +Default ListenIP:Port = 127.0.0.1:53 + +So the SOCKS server runs locally on the TCP port 9050 (Tor's default port; +attention: for Tor Browser Bundle you must change it to 9150). The used +DNS server is 46.182.19.48 (Digitalcourage e.V.). The DNS server must +support TCP on port 53 as Tor doesn't support UDP via SOCKS. DNS2SOCKS +listens on the UDP port 53 of 127.0.0.1 (only locally) - change this to +0.0.0.0 for listening on all available local IPv4 addresses. + +You can launch DNS2SOCKS several times with different settings, for +example to listen on IPv6 addresses additionally. To specify an IPv6 +address, use the typical format like 1234:5678::1234. To add the port +number please embed the IP address in square brackets and add the port +number separated by the colon, e.g. [::1]:1024 + +Hint: In the default configuration Tor only listens on 127.0.0.1 for +incoming requests. You can change this in Tor's configuration file using +the following line +SocksListenAddress 192.168.1.1 +In this example it listens on 192.168.1.1 +Currently Tor doesn't support IPv6 addresses for listening. + +Please note that Tor/Vidalia will output warnings that your application +doesn't resolve host names via Tor. This is not true, but Tor can't know +this as Tor doesn't recognize the tunneled DNS requests. DNS2SOCKS +directly uses the IP address of the DNS server while using SOCKS and also +your application will do this as it gets the IP address from DNS2SOCKS. +Tor expects getting the host names instead of IP addresses and thus +outputs these warnings. + +However, instead of an IP address you can also specify the DNS server's +name instead of its IP address, e.g. +DNS2SOCKS 127.0.0.1 dns2.digitalcourage.de ::1 +Specifying an IPv6 address for the DNS server is also supported by +DNS2SOCKS, but it's not recommended to do this as your current Tor exit +server would need to support IPv6, which it typically doesn't. So it's +better to specify the DNS server name as the exit server can choose IPv4 +or IPv6 automatically this way. Directly specifying an IPv4 address might +be a bit faster; currently all Tor exit servers should support this. + +Use the /e parameter to specify/overwrite the EDNS client subnet in your +DNS queries. This is a hint for the DNS server to reply with IP addresses +close to the specified subnet. It can speed things up. However, please +be aware of the privacy risk. There are three possible variants DNS2SOCKS +handles concerning the DNS requests if /e... is enabled: +1. There is no OPT pseudo-RR -> DNS2SOCKS adds one with the EDNS client + subnet option. It sets the DNSSEC flag to 0 as the original request + didn't have it. +2. There is an OPT pseudo-RR but no EDNS client subnet in it -> DNS2SOCKS + only adds the EDNS client subnet option but doesn't change the rest. +3. There is an OPT pseudo-RR including the EDNS client subnet. Now it + depends what you specify: + /e:... leaves it untouched. + /ef:... overwrites the original EDNS client subnet (f=force). + +As DNS requests running through the SOCKS/HTTP tunnel are very slow, the +calling application might time out before it gets the answer - in this +case just try it again (press "reload" in the browser). + +The output of DNS2SOCKS is very simple. On each new request it outputs +the requested name prefixed by the current number of entries in the +cache (just an increasing number in case the cache is disabled) and a time +stamp. DNS2SOCKS caches DNS requests, so the next time it can serve the +answer faster. The cache is a very simple list. There is no sophisticated +hash algorithm or something like that for the cache and DNS2SOCKS doesn't +really interpret the DNS requests and answers - it just forwards them. + +DNS2SOCKS runs as long as you don't manually stop it. +You can also run several instances of DNS2SOCKS at the same time when +using different local ports or IPv4 and IPv6 at the same time, e.g. use a +batch file and Window's Start command to do this. + +If you think that DNS2SOCKS is not the right tool for you, but you want +to route all network traffic of a specific Windows application through a +SOCKS tunnel, you might want to try my tool InjectSOCKS. + + + +Now about some technical details: +DNS2SOCKS listens on the local UDP and TCP port you specify. In case it +gets a request it first searches the cache for an identical request. +In case of a cache miss or expiration of the entry, the tool creates a new +thread for resolving the request. The new thread opens a TCP connection to +the SOCKS server and forwards the DNS request. This time the DNS request +always runs on TCP as Tor currently doesn't support UDP via SOCKS. So the +DNS server must support TCP. When the thread finally gets the answer, it +forwards it via UDP or TCP to the requesting client and stores it in the +cache. DNS2SOCKS supports user/password authentication (method 0x02) for +SOCKS. + +I've tried to comment the source code as good as possible and you can +compile it using Visual C++ 2015 Express Edition (or any other edition). +I've also tested it on Knoppix and Damn Small Linux and compiled it via +gcc -pthread -Wall -O2 -o DNS2SOCKS DNS2SOCKS.c +It should also run on other *nix variants; maybe with tiny modifications. + +Have fun using this software! + +ghostmaker + + + +License (3-clause BSD License) +------------------------------ + +Copyright (c) 2012, ghostmaker +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of ghostmaker nor the names of its contributors may + be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ghostmaker BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + + + +Changes of version 2.1 (released on 2020-02-18) +----------------------------------------------- + +- Added command line parameter /e... to set/overwrite the EDNS client + subnet in DNS requests +- Changed default DNS server to 46.182.19.48 +- Usage of the MSG_NOSIGNAL flag +- No modification of pseudo TTL field of OPT pseudo-RR anymore when + updating TTL in answer from cache +- Updated to Visual C++ 2015 Express Edition for compiling +- SHA256 for DNS2SOCKS.exe: + f4bb9471f73648a9f4f30cf7e9200eef3280499fb5c9e64a0234c9a4398c9edf + + +Changes of version 2.0 (released on 2015-06-28) +----------------------------------------------- + +- Added command line parameter /t to use a HTTP proxy instead of a SOCKS5 + server (HTTP CONNECT tunneling) +- SHA256 for DNS2SOCKS.exe: + ba841370b0d79b776438eb22a9131e91250025b55db4200db8f66a060cf6047d + + +Changes of version 1.9 (released on 2015-06-18) +----------------------------------------------- + +- Added fallback to old IPv4 parsing via "inet_addr" in case "getaddrinfo" + fails (workaround for erroneous "getaddrinfo" implementation) +- SHA256 for DNS2SOCKS.exe: + 5612bf263136852c39d929691b63a103c166f78bbd606d7947ce1d813c28c560 + + +Changes of version 1.8 (released on 2015-01-01) +----------------------------------------------- + +- Support for IPv6 +- Support for DNS server name instead of IP address +- Added BSD license text to ReadMe.txt +- Minor tweaks +- SHA256 for DNS2SOCKS.exe: + d654659b031ede16224fa37f585c735112a689f46497afc3fca6ccd4cde70b93 + + +Changes of version 1.7 (released on 2013-11-14) +----------------------------------------------- + +- Support for SOCKS5 user/password authentication (method 0x02) +- SHA256 for DNS2SOCKS.exe: + 63c0fd8f9dcd3f3546691892999d868ce74601778111dcf08cdfeb140818d28e + + +Changes of version 1.6 (released on 2013-10-21) +----------------------------------------------- + +- Cache function respects the "time to live" field of DNS answers +- Command line parameter /d to disable the cache +- SHA256 for DNS2SOCKS.exe: + 45a20efacb8fed901299712ae2ee6147ac453d69c349b0766024cb5d3634a0b5 + + +Changes of version 1.5 (released on 2013-10-06) +----------------------------------------------- + +- Removed several compiler warnings that appeared when compiling for + some platforms (tested on Knoppix V7.2.0 64 bit) +- Additional output of system error text in case of system call failure +- Added Windows application manifest "supportedOS" entry for Windows 8.1 +- Bugfix: Output of settings at startup was missing +- SHA256 for DNS2SOCKS.exe: + 8e987da2a57e5be942c21c76e28e185279a527ed67a468dab5757d38dc1e37ea + + +Changes of version 1.4 (released on 2013-01-19) +----------------------------------------------- + +- Command line parameter /l:filePath or /la:filePath to create a log file + for the output (/la for appending) +- Output has a time stamp for each line +- SHA256 for DNS2SOCKS.exe: + cae8aab900897b2c03bd8f3d415de91df596479bca2f83b107455ba68de28793 + + +Changes of version 1.3 (released on 2012-11-03) +----------------------------------------------- + +- DNS requests/responses via TCP additionally to UDP (still always via + TCP for transportation via SOCKS) +- UDP requests/responses can now have the maximum possible size for UDP +- You can specify the local port number to use for UDP and TCP +- You can specify the remote port number to use for contacting the DNS + server +- You can compile the sources on Unix/Linux now and use it like on + Windows. You only need DNS2SOCKS.c and stdafx.h for that and compile + it with a command like + gcc -pthread -Wall -O2 -o DNS2SOCKS DNS2SOCKS.c + (tested on Damn Small Linux; use sudo to run it as otherwise it's + impossible to bind port 53; please be aware that the connect time- + out on Linux is typically several minutes while it is just some + seconds on Windows, so in case connecting to the SOCKS server doesn't + work, it takes very long until you see an error message) +- On Windows DNS2SOCKS is no console application anymore, but it creates + its own new console window at start-up; this way you can start it with + the new /q option to suppress any output and it has no window in this + case - use the Task Manager to terminate it +- SHA1 for DNS2SOCKS.exe: 596015cba187a9184c5b480b8666c0d4b37d9552 + + +Changes of version 1.2 (released on 2012-01-13) +----------------------------------------------- + +You can configure the listen address via the command line now. The default +is 127.0.0.1 instead of 0.0.0.0 of version 1.1. This way the Windows +firewall doesn't show up. \ No newline at end of file diff --git a/luci-app-diskman/Makefile b/luci-app-diskman/Makefile index 20abf875d..109a9d42d 100644 --- a/luci-app-diskman/Makefile +++ b/luci-app-diskman/Makefile @@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-diskman PKG_VERSION:=0.2.13 -PKG_RELEASE:=3 +PKG_RELEASE:=4 PKG_MAINTAINER:=lisaac PKG_LICENSE:=AGPL-3.0 @@ -10,7 +10,8 @@ PKG_LICENSE:=AGPL-3.0 LUCI_TITLE:=Disk Manager interface for LuCI LUCI_DEPENDS:= \ +luci-compat +blkid +e2fsprogs +parted +smartmontools +dosfstools \ - +kmod-fs-msdos +kmod-fs-ntfs3 +kmod-fs-exfat +exfat-mkfs \ + +kmod-fs-msdos +kmod-fs-ntfs3 \ + +LINUX_5_4:kmod-fs-exfat-oot +!LINUX_5_4:kmod-fs-exfat +exfat-mkfs \ +exfat-fsck +kmod-nls-cp932 +kmod-nls-cp936 +kmod-nls-cp950 \ +PACKAGE_$(PKG_NAME)_INCLUDE_btrfs_progs:btrfs-progs \ +PACKAGE_$(PKG_NAME)_INCLUDE_lsblk:lsblk \ diff --git a/luci-app-easymesh/Makefile b/luci-app-easymesh/Makefile index 1e4b74166..318c6cc4d 100755 --- a/luci-app-easymesh/Makefile +++ b/luci-app-easymesh/Makefile @@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-easymesh PKG_VERSION:=2.4.1 -PKG_RELEASE:=6 +PKG_RELEASE:=7 PKG_LICENSE:=GPL-2.0 PKG_MAINTAINER:=Arafat Rahman Zami Mondol diff --git a/luci-app-easymesh/luasrc/view/easymesh/preset_confirm.htm b/luci-app-easymesh/luasrc/view/easymesh/preset_confirm.htm new file mode 100644 index 000000000..8d6df2034 --- /dev/null +++ b/luci-app-easymesh/luasrc/view/easymesh/preset_confirm.htm @@ -0,0 +1,71 @@ + diff --git a/luci-app-easymesh/po/zh_Hans/easymesh.po b/luci-app-easymesh/po/zh_Hans/easymesh.po index 847181b94..7d3b10d29 100755 --- a/luci-app-easymesh/po/zh_Hans/easymesh.po +++ b/luci-app-easymesh/po/zh_Hans/easymesh.po @@ -1,95 +1,420 @@ msgid "" -msgstr "Content-Type: text/plain; charset=UTF-8\n" +msgstr "" +"Project-Id-Version: luci-app-easymesh\n" +"POT-Creation-Date: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: zh_CN\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" -msgid "easymesh" -msgstr "easymesh" +msgid "EasyMesh" +msgstr "EasyMesh" -msgid "Active node" -msgstr "活动节点" +msgid "Configure a Batman-adv mesh network." +msgstr "配置基于 Batman-adv 的 Mesh 网络。" -msgid "IF" -msgstr "IF" +msgid "Active mesh links" +msgstr "活动 Mesh 链路" -msgid "Neighbor" -msgstr "节点邻居设备" +msgid "stale" +msgstr "已失效" -msgid "lastseen" -msgstr "上次连接延时" +msgid "Interface" +msgstr "接口" -msgid "EASY MESH" -msgstr "简单MESH" +msgid "Neighbor MAC" +msgstr "邻居 MAC" -msgid "Settings" -msgstr "设置" +msgid "Last seen" +msgstr "最后在线" -msgid "General Settings" -msgstr "基本设置" +msgid "Latency" +msgstr "延迟" -msgid "Enable" -msgstr "启用" +msgid "RX rate" +msgstr "接收速率" -msgid "role" -msgstr "角色" +msgid "TX rate" +msgstr "发送速率" -msgid "off" +msgid "Priority weight" +msgstr "优先级权重" + +msgid "Wired backhaul details" +msgstr "有线回传详情" + +msgid "State" +msgstr "状态" + +msgid "Cable" +msgstr "网线连接" + +msgid "Link speed" +msgstr "链路速率" + +msgid "In LAN bridge" +msgstr "在 LAN 桥中" + +msgid "Wired backhaul port" +msgstr "有线回传端口" + +msgid "Convenience selector. Mirrors 'Wired backhaul interface' below. Presets that use wired backhaul will remove this port from the LAN bridge. Pick a port NOT used by your uplink or modem. Leave empty to disable wired backhaul entirely." +msgstr "快捷选择器。与下方的“有线回传接口”同步。使用有线回传的预设会将此端口从 LAN 桥接中移除。请选择未被上行链路或光猫占用的端口。留空以完全禁用有线回传。" + +msgid "Preferred wired port" +msgstr "首选有线端口" + +msgid "(none — wired presets will skip)" +msgstr "(无 — 有线预设将跳过)" + +msgid "Boot-time reliability" +msgstr "开机可靠性保障" + +msgid "The init script applies link priorities at boot and 90s later. On some hardware, mesh interfaces appear even later, leaving priority weight at 0. Enable cron fallback to reapply priority every minute. Safe (idempotent) but runs in the background. When disabled, the cron line is commented out in /etc/crontabs/root." +msgstr "初始化脚本会在开机时及 90 秒后应用链路优先级。在部分硬件上,Mesh 接口可能出现得更晚,导致优先级权重保持为 0。启用计划任务(Cron)后备可在后台每分钟重新应用优先级。该操作安全且具备幂等性。禁用后,/etc/crontabs/root 中的对应计划任务将被注释。" + +msgid "Enable cron fallback" +msgstr "启用计划任务后备" + +msgid "Link priority weights" +msgstr "链路优先级权重" + +msgid "Batman-adv throughput_override values in Kbit/s. Higher = preferred. Set to 0 to let batman auto-decide per link. Defaults: wired=1000000, 6G=900000, 5G=600000, 2.4G=30000. Changes apply on next Save & Apply (or 'Reapply EasyMesh Settings')." +msgstr "Batman-adv throughput_override(吞吐量重写)数值,单位为 Kbit/s。数值越高越优先。设为 0 则由 Batman 自动判定链路速率。默认值:有线=1000000,6G=900000,5G=600000,2.4G=30000。更改将在下次“保存并应用”(或“重新应用 EasyMesh 设置”)后生效。" + +msgid "Wired (Kbit/s)" +msgstr "有线 (Kbit/s)" + +msgid "6 GHz wireless (Kbit/s)" +msgstr "6 GHz 无线 (Kbit/s)" + +msgid "5 GHz wireless (Kbit/s)" +msgstr "5 GHz 无线 (Kbit/s)" + +msgid "2.4 GHz wireless (Kbit/s)" +msgstr "2.4 GHz 无线 (Kbit/s)" + +msgid "Mesh VLANs" +msgstr "Mesh VLAN" + +msgid "Extend guest / IoT / hotspot networks across the mesh. Enter a comma-separated list of VLAN IDs (e.g. 10,20,30). The init script creates bat0. as an 8021q device for each ID. If a bridge named br-, br-lan, br-vlan, br-guest, or br-iot already exists, it is auto-added to that bridge. Otherwise, add the bat0. device manually in Network → Interfaces → Devices. Requires 802.1Q-capable switches on the physical wired backhaul." +msgstr "通过 Mesh 网络扩展访客 / 物联网 (IoT) / 热点网络。请输入以英文逗号分隔的 VLAN ID 列表(例如:10,20,30)。初始化脚本会为每个 ID 创建对应的 bat0. 802.1q 设备。如果已存在名为 br-、br-lan、br-vlan、br-guest 或 br-iot 的桥接接口,将自动将其加入该桥接中。否则,请手动前往“网络 → 接口 → 设备”中添加 bat0. 设备。物理有线回传需要交换机支持 802.1Q。" + +msgid "VLAN IDs (comma-separated)" +msgstr "VLAN ID(逗号分隔)" + +msgid "Example: 10,20,30 — Leave empty to disable" +msgstr "示例:10,20,30 — 留空以禁用" + +msgid "Quick Setup" +msgstr "快捷预设" + +msgid "Click a preset. A confirmation dialog will appear. Current config is backed up automatically before applying. Grouped by role: SERVER (has internet) → CLIENT (uses mesh) → NODE (relay only)." +msgstr "点击预设方案。应用前会弹出确认对话框并自动备份当前配置。按节点角色分组:服务端 SERVER(提供网络)→ 客户端 CLIENT(接入 Mesh)→ 节点 NODE(纯中继)。" + +msgid "1. Server — 5 GHz wireless only" +msgstr "1. 服务端 — 仅 5 GHz 无线" + +msgid "[SERVER] Gateway. Extenders join via 5 GHz mesh only." +msgstr "[主路由/服务端] 网关节点。扩展节点仅通过 5 GHz Mesh 接入。" + +msgid "2. Server — 2.4 GHz wireless only" +msgstr "2. 服务端 — 仅 2.4 GHz 无线" + +msgid "[SERVER] Extenders join via 2.4 GHz (longer range)." +msgstr "[主路由/服务端] 扩展节点通过 2.4 GHz 接入(覆盖范围更广)。" + +msgid "3. Server — Dual-band wireless only" +msgstr "3. 服务端 — 仅双频无线" + +msgid "[SERVER] Both radios carry backhaul." +msgstr "[主路由/服务端] 双频射频同时承载回传。" + +msgid "4. Server — Wired backhaul only" +msgstr "4. 服务端 — 仅有线回传" + +msgid "[SERVER] No wireless mesh — extenders connect by cable." +msgstr "[主路由/服务端] 无无线 Mesh — 扩展节点通过网线连接。" + +msgid "5. Server — 5 GHz + Wired" +msgstr "5. 服务端 — 5 GHz + 有线" + +msgid "[SERVER] 5 GHz mesh primary, Ethernet parallel." +msgstr "[主路由/服务端] 5 GHz Mesh 为主,以太网并行传输。" + +msgid "6. Server — 2.4 GHz + Wired" +msgstr "6. 服务端 — 2.4 GHz + 有线" + +msgid "[SERVER] 2.4 GHz mesh plus Ethernet." +msgstr "[主路由/服务端] 2.4 GHz Mesh 配合以太网回传。" + +msgid "7. Server — Dual-band + Wired" +msgstr "7. 服务端 — 双频 + 有线" + +msgid "[SERVER] Maximum redundancy. Both radios + Ethernet." +msgstr "[主路由/服务端] 最大冗余。双频射频 + 以太网同时回传。" + +msgid "8. Client — 5 GHz wireless only" +msgstr "8. 客户端 — 仅 5 GHz 无线" + +msgid "[CLIENT] Extender via 5 GHz. No cable." +msgstr "[子路由/客户端] 通过 5 GHz 扩展,无网线。" + +msgid "9. Client — 2.4 GHz wireless only" +msgstr "9. 客户端 — 仅 2.4 GHz 无线" + +msgid "[CLIENT] Extender via 2.4 GHz." +msgstr "[子路由/客户端] 通过 2.4 GHz 扩展。" + +msgid "10. Client — Dual-band wireless only" +msgstr "10. 客户端 — 仅双频无线" + +msgid "[CLIENT] Extender via both radios." +msgstr "[子路由/客户端] 同时通过两个射频频段扩展。" + +msgid "11. Client — Wired backhaul only" +msgstr "11. 客户端 — 仅有线回传" + +msgid "[CLIENT] Cable only, no wireless mesh." +msgstr "[子路由/客户端] 仅网线连接,无无线 Mesh。" + +msgid "12. Client — 5 GHz + Wired fallback" +msgstr "12. 客户端 — 5 GHz + 有线备用" + +msgid "[CLIENT] 5 GHz primary, Ethernet fallback." +msgstr "[子路由/客户端] 5 GHz 为主,以太网备用。" + +msgid "13. Client — 2.4 GHz + Wired fallback" +msgstr "13. 客户端 — 2.4 GHz + 有线备用" + +msgid "[CLIENT] 2.4 GHz primary, Ethernet fallback." +msgstr "[子路由/客户端] 2.4 GHz 为主,以太网备用。" + +msgid "14. Client — Dual-band + Wired fallback" +msgstr "14. 客户端 — 双频 + 有线备用" + +msgid "[CLIENT] Both radios + Ethernet. Maximum redundancy." +msgstr "[子路由/客户端] 双频射频 + 以太网,提供最大冗余与稳定性。" + +msgid "15. Node — 5 GHz relay" +msgstr "15. 纯中继节点 — 5 GHz 中继" + +msgid "[NODE] Relay only, 5 GHz. No client SSID." +msgstr "[中继节点] 仅作为 5 GHz 中继,不发射终端接入 SSID。" + +msgid "16. Node — Dual-band relay" +msgstr "16. 纯中继节点 — 双频中继" + +msgid "[NODE] Relay only, both radios." +msgstr "[中继节点] 双频中继,不发射终端接入 SSID。" + +msgid "17. Node — Wired relay only" +msgstr "17. 纯中继节点 — 仅有线中继" + +msgid "[NODE] Relay via cable only. No wireless." +msgstr "[中继节点] 仅通过网线中继,无无线回传。" + +msgid "18. Detach wired backhaul" +msgstr "18. 解绑有线回传" + +msgid "Removes Ethernet backhaul, restores port to bridge." +msgstr "移除以太网回传,并将端口恢复加入网络桥接。" + +msgid "19. Disable mesh" +msgstr "19. 禁用 Mesh" + +msgid "Turns off mesh. Radios stay enabled." +msgstr "关闭 Mesh 网络功能,无线射频保持开启。" + +msgid "Mesh settings" +msgstr "Mesh 设置" + +msgid "Enable EasyMesh" +msgstr "启用 EasyMesh" + +msgid "Mesh role" +msgstr "Mesh 角色" + +msgid "Off" msgstr "关闭" -msgid "host MESH" -msgstr "主MESH" +msgid "Server (gateway - has internet)" +msgstr "服务端 (主网关 - 连接外网)" -msgid "son MESH" -msgstr "子MESH" +msgid "Client (uses mesh internet)" +msgstr "客户端 (通过 Mesh 上网)" -msgid "MESH Radio device" -msgstr "MESH无线设备" +msgid "Node (relay only)" +msgstr "节点 (纯中继)" -msgid "The radio device which MESH use" -msgstr "使用MESH组网的无线设备" +msgid "Mesh mode" +msgstr "Mesh 模式" -msgid "AUTO" -msgstr "自动" +msgid "Combined wireless band + wired backhaul. Wired variants use the port selected above." +msgstr "无线频段与有线回传组合。包含有线的选项将使用上方选择的端口。" -msgid "Enable or disable EASY MESH" -msgstr "启用或禁用简单MESH" +msgid "5 GHz wireless only" +msgstr "仅 5 GHz 无线" -msgid "Encryption" -msgstr "加密" +msgid "2.4 GHz wireless only" +msgstr "仅 2.4 GHz 无线" -msgid "Key" -msgstr "密码" +msgid "Dual-band wireless only" +msgstr "仅双频无线" -msgid "inter-access point protocol" -msgstr "接入点内部协议" +msgid "5 GHz wireless + Wired backhaul" +msgstr "5 GHz 无线 + 有线回传" -msgid "Wireless Access Points (APs) running on different vendors can communicate with each other" -msgstr "运行在不同供应商的无线访问点(AP)可以相互交流" +msgid "2.4 GHz wireless + Wired backhaul" +msgstr "2.4 GHz 无线 + 有线回传" -msgid "Threshold for an good RSSI" -msgstr "快速漫游接入值" +msgid "Dual-band wireless + Wired backhaul" +msgstr "双频无线 + 有线回传" -msgid "Threshold for an bad RSSI" -msgstr "快速漫游踢出值" +msgid "Wired only (no wireless mesh)" +msgstr "仅有线 (无无线 Mesh)" -msgid "Threshold for a good RSSI" -msgstr "优良RSSI信号阈值" +msgid "Custom radio" +msgstr "自定义射频设备" -msgid "Threshold for a bad RSSI" -msgstr "不良RSSI信号阈值" +msgid "Custom mesh radio" +msgstr "自定义 Mesh 射频" -msgid "AP MODE Enable" -msgstr "启用AP模式" +msgid "Mesh ID" +msgstr "Mesh 网络名称 (Mesh ID)" -msgid "Enable or disable AP MODE" -msgstr "启用或禁用AP模式" +msgid "Must match on all nodes. Ignored in wired-only mode." +msgstr "所有节点必须保持一致。仅有线模式下忽略此项。" -msgid "IPv4-Address" -msgstr "IPv4 地址" +msgid "Require password (WPA3-SAE)" +msgstr "需要密码 (WPA3-SAE)" -msgid "IPv4 netmask" -msgstr "IPv4 子网掩码" +msgid "Mesh password" +msgstr "Mesh 密码" -msgid "IPv4 gateway" -msgstr "IPv4 网关" +msgid "Must match on all nodes. Only used when 'Require password' is enabled." +msgstr "所有节点必须保持一致。仅在启用“需要密码”时生效。" -msgid "Use custom DNS servers" -msgstr "使用自定义的 DNS 服务器" +msgid "Enable 802.11k/v/r fast roaming" +msgstr "启用 802.11k/v/r 快速漫游" + +msgid "Mobility domain" +msgstr "移动域 (Mobility Domain)" + +msgid "Client WiFi — 2.4 GHz" +msgstr "终端接入 WiFi — 2.4 GHz" + +msgid "Longer range, lower speed. Use a different SSID from 5 GHz." +msgstr "覆盖距离更远,速率较低。建议使用与 5 GHz 不同的 SSID。" + +msgid "Broadcast 2.4 GHz" +msgstr "广播 2.4 GHz" + +msgid "2.4 GHz SSID" +msgstr "2.4 GHz SSID" + +msgid "2.4 GHz security" +msgstr "2.4 GHz 安全模式" + +msgid "None (open)" +msgstr "无 (开放网络)" + +msgid "WPA2-PSK" +msgstr "WPA2-PSK" + +msgid "WPA3-SAE" +msgstr "WPA3-SAE" + +msgid "WPA2/WPA3 transition" +msgstr "WPA2/WPA3 混合模式" + +msgid "2.4 GHz password" +msgstr "2.4 GHz 密码" + +msgid "Client WiFi — 5 GHz" +msgstr "终端接入 WiFi — 5 GHz" + +msgid "Shorter range, much faster. Use a different SSID from 2.4 GHz." +msgstr "速率更高,覆盖距离较近。建议使用与 2.4 GHz 不同的 SSID。" + +msgid "Broadcast 5 GHz" +msgstr "广播 5 GHz" + +msgid "5 GHz SSID" +msgstr "5 GHz SSID" + +msgid "5 GHz security" +msgstr "5 GHz 安全模式" + +msgid "5 GHz password" +msgstr "5 GHz 密码" + +msgid "Client WiFi — shared" +msgstr "终端接入 WiFi — 公共设置" + +msgid "Attach client WiFi to network" +msgstr "将终端 WiFi 绑定至网络接口" + +msgid "AP Mode (Dumb AP)" +msgstr "AP 模式 (胖/瘦 AP 旁路模式)" + +msgid "Enable AP Mode (Dumb AP)" +msgstr "启用 AP 模式 (Dumb AP)" + +msgid "IP mode" +msgstr "IP 地址获取方式" + +msgid "DHCP (from Server)" +msgstr "DHCP (从服务端自动获取)" + +msgid "Static IP" +msgstr "静态 IP" + +msgid "IP address" +msgstr "IP 地址" + +msgid "Netmask" +msgstr "子网掩码" + +msgid "Gateway" +msgstr "网关" + +msgid "DNS server" +msgstr "DNS 服务器" + +msgid "Wired backhaul" +msgstr "有线回传" + +msgid "Optional. Adds a physical Ethernet port as a batman hardif. Required on BOTH sides for a wired link. The port is auto-removed from the LAN bridge on apply." +msgstr "可选功能。将指定的物理以太网端口添加为 Batman 底层接口 (hardif)。建立有线链路时两端节点均需配置。应用时该端口将自动从 LAN 桥接中移除。" + +msgid "Wired backhaul interface" +msgstr "有线回传接口" + +msgid "Disabled" +msgstr "已禁用" + +msgid "Safety and actions" +msgstr "安全与操作" + +msgid "Safe apply (60-second rollback)" +msgstr "安全应用 (60 秒无响应自动回滚)" + +msgid "Reapply EasyMesh Settings" +msgstr "重新应用 EasyMesh 设置" + +msgid "Restore from last backup" +msgstr "从上一次备份恢复" + +msgid "Pair with another router" +msgstr "与其他路由器配对" + +msgid "Enter pairing mode (2 minutes)" +msgstr "进入配对模式 (2 分钟)" + +msgid "WPS-style pairing. Press the WPS button on both routers within 2 minutes. Auto-reverts on timeout." +msgstr "类似 WPS 风格的一键配对。请在 2 分钟内在两台路由器上均触发配对。超时未配对成功将自动恢复原状。" diff --git a/luci-app-open-box/Makefile b/luci-app-open-box/Makefile index 85617493d..da824fc70 100644 --- a/luci-app-open-box/Makefile +++ b/luci-app-open-box/Makefile @@ -1,8 +1,8 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-open-box -PKG_VERSION:=0.1.338 -PKG_RELEASE:=35 +PKG_VERSION:=0.1.339 +PKG_RELEASE:=36 PKG_MAINTAINER:=liandu2024 PKG_LICENSE:=GPL-3.0-or-later diff --git a/luci-app-passwall/Makefile b/luci-app-passwall/Makefile index d4f7d4c3f..bf3db9238 100644 --- a/luci-app-passwall/Makefile +++ b/luci-app-passwall/Makefile @@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-passwall PKG_VERSION:=26.10.16 -PKG_RELEASE:=337 +PKG_RELEASE:=338 PKG_PO_VERSION:=$(PKG_VERSION) PKG_CONFIG_DEPENDS:= \ diff --git a/luci-app-passwall/luasrc/controller/passwall.lua b/luci-app-passwall/luasrc/controller/passwall.lua index cb92fef8a..a04d25181 100644 --- a/luci-app-passwall/luasrc/controller/passwall.lua +++ b/luci-app-passwall/luasrc/controller/passwall.lua @@ -253,6 +253,7 @@ function get_redir_log() local path = api.TMP_PATH .. "/acl/" .. id local function alert(msg) + http.prepare_content("text/html; charset=utf-8") http.write(string.format("", i18n.translate(msg))) end @@ -273,9 +274,8 @@ function get_redir_log() end if fs.access(path .. "/" .. name .. ".log") then - local content = luci.sys.exec("tail -n 5000 ".. path .. "/" .. name .. ".log") - content = content:gsub("\n", "
") - http.write(content) + http.prepare_content("text/plain; charset=utf-8") + http.write(luci.sys.exec("tail -n 5000 " .. util.shellquote(path))) else alert("Not enabled log") end @@ -285,10 +285,10 @@ function get_socks_log() local name = http.formvalue("name") local path = api.TMP_PATH .. "/" .. name .. ".log" if fs.access(path) then - local content = luci.sys.exec("tail -n 5000 ".. path) - content = content:gsub("\n", "
") - http.write(content) + http.prepare_content("text/plain; charset=utf-8") + http.write(luci.sys.exec("tail -n 5000 " .. util.shellquote(path))) else + http.prepare_content("text/html; charset=utf-8") http.write(string.format("", i18n.translate("Not enabled log"))) end end @@ -308,21 +308,22 @@ function get_chinadns_log() end end if fs.access(path) then - local content = luci.sys.exec("tail -n 5000 ".. path) - content = content:gsub("\n", "
") - http.write(content) + http.prepare_content("text/plain; charset=utf-8") + http.write(luci.sys.exec("tail -n 5000 " .. util.shellquote(path))) else + http.prepare_content("text/html; charset=utf-8") http.write(string.format("", i18n.translate("Not enabled log"))) end end function get_smartdns_log() local path = api.TMP_PATH .. "/acl/default/smartdns.log" - http.prepare_content("text/plain; charset=utf-8") if fs.access(path) then + http.prepare_content("text/plain; charset=utf-8") http.write(luci.sys.exec("tail -n 5000 " .. util.shellquote(path))) else - http.write(i18n.translate("Not enabled log")) + http.prepare_content("text/html; charset=utf-8") + http.write(string.format("", i18n.translate("Not enabled log"))) end end diff --git a/luci-app-passwall/luasrc/passwall/api.lua b/luci-app-passwall/luasrc/passwall/api.lua index 91bd68dad..2bbc4431b 100644 --- a/luci-app-passwall/luasrc/passwall/api.lua +++ b/luci-app-passwall/luasrc/passwall/api.lua @@ -270,10 +270,11 @@ end -- 计算字符串 MD5 function md5_string(str) if type(str) ~= "string" then return "" end - local quoted = "'" .. str:gsub("'", "'\\''") .. "'" - local out = sys.exec("printf '%s' " .. quoted .. " | md5sum") - if not out then return "" end - return out:sub(1, 32) + local path = trim(sys.exec("mktemp /tmp/md5.XXXXXX") or "") + if path == "" then return "" end + local md5 = fs.writefile(path, str) == #str and md5_file(path) or "" + fs.unlink(path) + return md5 end --提取URL中的域名和端口(no ip) diff --git a/luci-app-passwall/luasrc/view/passwall/global/footer.htm b/luci-app-passwall/luasrc/view/passwall/global/footer.htm index 0962d5eeb..ad87dba3d 100644 --- a/luci-app-passwall/luasrc/view/passwall/global/footer.htm +++ b/luci-app-passwall/luasrc/view/passwall/global/footer.htm @@ -196,10 +196,9 @@ local sid = "@global[0]" } requestAnimationFrame(waitForDnsSelect); }; - waitForDnsSelect(); const initDnsSelect = (dns_select) => { - if (dns_select.value === "chinadns-ng") { + if (dns_select.value === "chinadns-ng" || dns_select.value === "smartdns") { addLogLink(dns_select); } if (dns_select._dnsLogBinded) return; @@ -210,7 +209,7 @@ local sid = "@global[0]" if (existingLogLink) { existingLogLink.remove(); } - if (dns_select.value === "chinadns-ng") { + if (dns_select.value === "chinadns-ng" || dns_select.value === "smartdns") { addLogLink(dns_select); } }); @@ -225,9 +224,12 @@ local sid = "@global[0]" logLink.href = "#"; logLink.className = "dns-log-link"; logLink.style.marginLeft = "10px"; - logLink.setAttribute("onclick", "window.open('" + '<%=api.url("get_chinadns_log")%>' + "?flag=default', '_blank')"); + const logUrl = select.value === "smartdns" ? '<%=api.url("get_smartdns_log")%>' : '<%=api.url("get_chinadns_log")%>?flag=default'; + logLink.setAttribute("onclick", "window.open('" + logUrl + "', '_blank')"); select.insertAdjacentElement("afterend", logLink); }; + + waitForDnsSelect(); })(); (function () { diff --git a/luci-app-passwall/root/usr/share/passwall/helper_chinadns_add.lua b/luci-app-passwall/root/usr/share/passwall/helper_chinadns_add.lua index 4c6a8e50e..c92a1abe3 100644 --- a/luci-app-passwall/root/usr/share/passwall/helper_chinadns_add.lua +++ b/luci-app-passwall/root/usr/share/passwall/helper_chinadns_add.lua @@ -196,10 +196,6 @@ if not is_file_nonzero(file_vpslist) then api.uci_foreach_c("nodes", function(t) process_address(t.address) process_address(t.download_address) - local dns, _ = api.get_domain_port_from_url(t.domain_resolver_dns or t.domain_resolver_dns_https or "") - if dns and dns ~= "" then - process_address(dns) - end end) f_out:close() end diff --git a/luci-app-passwall/root/usr/share/passwall/helper_dnsmasq.lua b/luci-app-passwall/root/usr/share/passwall/helper_dnsmasq.lua index 004b396a1..66b38a674 100644 --- a/luci-app-passwall/root/usr/share/passwall/helper_dnsmasq.lua +++ b/luci-app-passwall/root/usr/share/passwall/helper_dnsmasq.lua @@ -334,7 +334,7 @@ function add_rule(var) end local function get_dns_config_key() - local address_md5 = api.md5_string(sys.exec([[uci show passwall | grep -E '\.(address|download_address|domain_resolver_dns|domain_resolver_dns_https)=' | cut -d "'" -f 2 | sort -u]])) + local address_md5 = api.md5_string(sys.exec([[uci show passwall | grep -E '\.(address|download_address)=' | cut -d "'" -f 2 | sort -u]])) local new_rules = sys.exec([[ for f in \ /usr/share/passwall/rules/chnlist \ @@ -462,10 +462,6 @@ function add_rule(var) api.uci_foreach_c("nodes", function(t) process_address(t.address) process_address(t.download_address) - local dns, _ = api.get_domain_port_from_url(t.domain_resolver_dns or t.domain_resolver_dns_https or "") - if dns and dns ~= "" then - process_address(dns) - end end) log(string.format(" - 节点列表中的域名(vpslist):%s", fwd_dns or "默认")) end diff --git a/luci-app-passwall/root/usr/share/passwall/helper_smartdns_add.lua b/luci-app-passwall/root/usr/share/passwall/helper_smartdns_add.lua index 4ceec9177..af36cc23f 100644 --- a/luci-app-passwall/root/usr/share/passwall/helper_smartdns_add.lua +++ b/luci-app-passwall/root/usr/share/passwall/helper_smartdns_add.lua @@ -401,10 +401,6 @@ if not is_file_nonzero(file_vpslist) then api.uci_foreach_c("nodes", function(t) process_address(t.address) process_address(t.download_address) - local dns, _ = api.get_domain_port_from_url(t.domain_resolver_dns or t.domain_resolver_dns_https or "") - if dns and dns ~= "" then - process_address(dns) - end end) f_out:close() end @@ -418,6 +414,8 @@ if is_file_nonzero(file_vpslist) then "#6:" .. setflag .. "psw_vps6" } local domain_rules_str = string.format('domain-rules /domain-set:%s/ -nameserver direct', domain_set_name) .. DIRECT_RULE_OPTIONS + -- 节点域名最多缓存 30 秒,到期后重新查询,避免继续返回过期的节点 IP。 + domain_rules_str = domain_rules_str .. " -rr-ttl-max 30 -no-serve-expired" domain_rules_str = domain_rules_str .. " " .. set_type .. " " .. table.concat(sets, ",") table.insert(tmp_lines, domain_rules_str) insert_array_after(config_lines, tmp_lines, "#--9") diff --git a/luci-app-passwall/root/usr/share/passwall/iptables.sh b/luci-app-passwall/root/usr/share/passwall/iptables.sh index 088bd3cc3..bfb6da4f6 100755 --- a/luci-app-passwall/root/usr/share/passwall/iptables.sh +++ b/luci-app-passwall/root/usr/share/passwall/iptables.sh @@ -767,7 +767,7 @@ filter_haproxy() { } filter_vpsip() { - local vps_addrs=$(uci show $CONFIG | grep -E "(\.address=|\.download_address=|\.domain_resolver_dns=|\.domain_resolver_dns_https=)" | cut -d "'" -f 2 | grep -Ev "$EXCLUDE_VPSIP") + local vps_addrs=$(uci show $CONFIG | grep -E "(\.address=|\.download_address=)" | cut -d "'" -f 2 | grep -Ev "$EXCLUDE_VPSIP") local ipv4_addrs=$(echo "$vps_addrs" | grep -Eo "$IPv4_REGEX") [ -n "$ipv4_addrs" ] && { echo "$ipv4_addrs" | sed "s/^/add $IPSET_VPS /" | awk '1; END{print "COMMIT"}' | ipset -! -R diff --git a/luci-app-passwall/root/usr/share/passwall/nftables.sh b/luci-app-passwall/root/usr/share/passwall/nftables.sh index bf8f329d8..86b542dc7 100755 --- a/luci-app-passwall/root/usr/share/passwall/nftables.sh +++ b/luci-app-passwall/root/usr/share/passwall/nftables.sh @@ -816,7 +816,7 @@ filter_vps_addr() { } filter_vpsip() { - local vps_addrs=$(uci show $CONFIG | grep -E "(\.address=|\.download_address=|\.domain_resolver_dns=|\.domain_resolver_dns_https=)" | cut -d "'" -f 2 | grep -Ev "$EXCLUDE_VPSIP") + local vps_addrs=$(uci show $CONFIG | grep -E "(\.address=|\.download_address=)" | cut -d "'" -f 2 | grep -Ev "$EXCLUDE_VPSIP") local ipv4_addrs=$(echo "$vps_addrs" | grep -Eo "$IPv4_REGEX") [ -n "$ipv4_addrs" ] && { echo "$ipv4_addrs" | insert_nftset $NFTSET_VPS diff --git a/luci-app-passwall/root/usr/share/passwall/tasks.sh b/luci-app-passwall/root/usr/share/passwall/tasks.sh index 3f2373645..5a633c496 100755 --- a/luci-app-passwall/root/usr/share/passwall/tasks.sh +++ b/luci-app-passwall/root/usr/share/passwall/tasks.sh @@ -8,10 +8,7 @@ LOCK_FILE=${LOCK_PATH}/${CONFIG}_tasks.lock CFG_UPDATE_INT=0 exec 99>"$LOCK_FILE" -flock -n 99 -if [ "$?" != 0 ]; then - exit 0 -fi +flock -n 99 || exit 0 while true do @@ -23,7 +20,7 @@ do restart_interval_mode=$(expr "$restart_interval_mode" \* 60) if [ -n "$restart_week_mode" ]; then [ "$restart_week_mode" = "8" ] && { - [ "$(expr "$CFG_UPDATE_INT" % "$restart_interval_mode")" -eq 0 ] && { /etc/init.d/$CONFIG restart cron > /dev/null 2>&1 & } + [ "$(expr "$CFG_UPDATE_INT" % "$restart_interval_mode")" -eq 0 ] && { /etc/init.d/$CONFIG restart cron > /dev/null 2>&1 99>&- & } } fi @@ -32,7 +29,7 @@ do rules_update_interval_mode=$(expr "$rules_update_interval_mode" \* 60) if [ -n "$rules_update_week_mode" ]; then [ "$rules_update_week_mode" = "8" ] && { - [ "$(expr "$CFG_UPDATE_INT" % "$rules_update_interval_mode")" -eq 0 ] && { lua $APP_PATH/rule_update.lua log all cron > /dev/null 2>&1 & } + [ "$(expr "$CFG_UPDATE_INT" % "$rules_update_interval_mode")" -eq 0 ] && { lua "$APP_PATH/rule_update.lua" log all cron > /dev/null 2>&1 99>&- & } } fi @@ -53,9 +50,9 @@ do sub_update_week_mode=${name%_*} sub_update_interval_mode=${name#*_} sub_update_interval_mode=$(expr "$sub_update_interval_mode" \* 60) - cfgids=$(echo -n $(cat ${TMP_SUB_PATH}/${name}) | sed 's# #,#g') + cfgids=$(tr '\n' ',' < "${TMP_SUB_PATH}/${name}" | sed 's/,$//') [ "$sub_update_week_mode" = "8" ] && { - [ "$(expr "$CFG_UPDATE_INT" % "$sub_update_interval_mode")" -eq 0 ] && { lua $APP_PATH/subscribe.lua start $cfgids cron > /dev/null 2>&1 & } + [ "$(expr "$CFG_UPDATE_INT" % "$sub_update_interval_mode")" -eq 0 ] && { lua "$APP_PATH/subscribe.lua" start "$cfgids" cron > /dev/null 2>&1 99>&- & } } done rm -rf $TMP_SUB_PATH @@ -65,6 +62,6 @@ do CFG_UPDATE_INT=$(expr "$CFG_UPDATE_INT" + 10) - sleep 600 + sleep 600 99>&- done 2>/dev/null diff --git a/luci-app-smartsafehub/CHANGELOG.md b/luci-app-smartsafehub/CHANGELOG.md index 8ab0adba2..a9d050ba4 100644 --- a/luci-app-smartsafehub/CHANGELOG.md +++ b/luci-app-smartsafehub/CHANGELOG.md @@ -1,5 +1,184 @@ # 변경 기록 +## [0.2.30-r1] - 2026-10-18 + +### 개선 +- 전체적인 화면 레이아웃 다듬었습니다. + +### 수정 +- 추천 사용자 규칙 해제가 안되던 문제를 수정하였습니다. + +## [0.2.29-r14] - 2026-10-18 + +### 개선 +- 펌웨어 업데이트 화면에서 새 펌웨어에 포함된 관리 소프트웨어 버전을 상단에서 바로 확인할 수 있습니다. +- 관리 소프트웨어 자동 업데이트 설정 카드의 불필요한 안내 문구를 줄여 화면을 더 간단하게 정리했습니다. +- SafeShield 상단 보호 데이터와 통계 수집 아이콘 크기를 아래 보호 구성 카드와 같은 기준으로 맞췄습니다. + +### 테스트 +- 펌웨어 카드에 포함된 관리 소프트웨어 버전이 상단에 표시되는지 확인하는 테스트를 보강했습니다. +- SafeShield 상단 아이콘 크기와 배경이 보호 구성 카드와 같은 규칙을 사용하는지 확인하는 테스트를 보강했습니다. + + +## [0.2.29-r13] - 2026-10-18 + +### 개선 +- IPTV 동작 상태 카드에 간단한 안내 문구를 추가했습니다. +- 연결된 기기의 검색창과 필터를 같은 줄에 배치해 화면 공간을 절약했습니다. +- SafeShield 보호 데이터 카드의 비중을 줄이고, 통계 수집 스위치를 보호 활동 제목 옆으로 옮겼습니다. +- 원격 상태 보고의 멤버십 표시와 스위치를 제목 옆으로 옮기고, 개인정보 보호 안내를 바로 아래에 배치했습니다. + +### 테스트 +- 네 화면의 위치와 기존 조작 기능을 확인하는 레이아웃 테스트를 추가했습니다. + + +## [0.2.29-r12] - 2026-10-18 + +### 개선 + +- 최근 활동 화면 상단에서 Cloud 활동 기록을 바로 켜거나 끌 수 있습니다. +- 동기화 상태와 마지막 동기화 시간을 쉽게 확인하고, 자세한 정보는 펼쳐서 볼 수 있습니다. +- 최근 활동을 처음에는 20건만 보여주고, 필요한 경우 이전 기록을 20건씩 추가로 볼 수 있습니다. + +### 테스트 + +- Cloud 설정이 활동 목록 위에 있는지, 접기·펼치기와 활동 더 보기가 동작하는지 확인합니다. +- 계정 연결 안내, 전송 오류, 로컬 활동 기록 유지 안내가 사라지지 않았는지 확인합니다. + +## [0.2.29-r11] - 2026-10-18 + +### 개선 + +- 메뉴별 제목과 안내 문구를 카드 밖에 배치해 화면 구성을 통일했습니다. +- SafeShield 통계, 최근 활동, 네트워크, 연결 기기, 사용자 규칙 등 주요 정보를 더 쉽게 구분할 수 있습니다. +- 보호 전환, 갱신, 저장 등의 조작 버튼은 관련 카드에 남겨두어 사용하기 편하도록 했습니다. +- 모바일에서도 같은 화면 구조를 사용하고 카드 안의 불필요한 겹침을 줄였습니다. + +### 테스트 + +- 메뉴별 제목 배치와 기존 조작 버튼, 설정 저장 안내가 유지되는지 확인합니다. + +## [0.2.29-r10] - 2026-10-18 + +### 개선 + +- 추천 규칙이 공유기에 적용되면 웹사이트에서도 결과를 더 빨리 확인할 수 있습니다. +- 적용 확인 결과를 주기적으로 전달하되 불필요한 반복 전송은 줄입니다. + +### 테스트 + +- 허용 목록 확인 결과의 전송, 최신 규칙 비교 및 중복 보고 방지를 검증합니다. + +## [0.2.29-r9] - 2026-10-18 + +### 수정 + +- SafeShield 차단 목록을 갱신하는 중 추천 규칙 동기화가 잘못 실패로 표시되던 문제를 수정했습니다. +- 추천 규칙을 적용하거나 해제할 때 진행 안내와 실패 안내가 겹쳐 보이던 문제를 수정했습니다. + +### 개선 + +- 추천 규칙을 가져오는 중, SafeShield 갱신 중, 허용 목록 적용·확인 중인 상태를 구분해 보여줍니다. +- SafeShield 갱신과 겹치는 규칙 변경은 기존 규칙을 유지하고 갱신이 끝나면 자동으로 재시도합니다. +- 서버 연결이 일시적으로 지연되어도 이미 확인한 허용 규칙을 유지하고 자동으로 다시 확인합니다. +- 화면을 새로고침하지 않아도 처리 진행 상태와 허용 목록을 자동으로 갱신합니다. + +### 테스트 + +- SafeShield 갱신 중 규칙 변경 보류, 재시도, 기존 사용자 규칙 보존 및 상태 표시를 검증합니다. + +## [0.2.29-r8] - 2026-10-17 + +### 수정 + +- 추천 규칙을 해제하거나 다시 적용하는 중 일시적으로 실패하면 도메인이 직접 추가한 규칙으로 잘못 분류될 수 있던 문제를 수정했습니다. +- 적용 확인이 완료되지 않은 추천 규칙은 전체 내용을 다시 동기화하도록 개선했습니다. + +### 테스트 + +- 갱신 실패 후 재할당, 기존 직접 허용 규칙 보존, 동기화 확인서가 사라진 경우의 복구를 검증합니다. + +## [0.2.29-r7] - 2026-10-17 + +### 추가 + +- 추천 사용자 규칙을 공유기에서도 확인 후 적용 해제할 수 있습니다. + +### 개선 + +- 여러 추천 규칙이 같은 도메인을 사용해도 다른 규칙과 직접 추가한 허용 규칙은 유지합니다. + +### 테스트 + +- 해제 확인, 기기 인증, 공유기 동기화와 데모 접근 차단을 확인합니다. + +## [0.2.29-r6] - 2026-10-17 + +### 수정 + +- 사용자 규칙의 추천 규칙 제목이 다크모드에서 밝게 표시되던 문제를 수정했습니다. +- 다크모드에서 자동 완성된 관리자 비밀번호 입력칸의 배경색을 자연스럽게 표시합니다. + +### 개선 + +- 설정의 세 탭을 동일한 콘텐츠 폭과 카드 디자인으로 정리했습니다. +- 진단, 원격 상태 보고, 개인정보 보호를 각각 보기 쉬운 카드로 분리했습니다. +- 시간 설정과 예약 재부팅을 넓은 화면에서 나란히 표시합니다. +- 모바일에서는 설정 카드를 세로로 배치하여 읽기 편하게 유지합니다. + +### 테스트 + +- 다크모드 추천 규칙, 비밀번호 자동 완성, 설정 탭의 카드 배치를 점검합니다. + +## [0.2.29-r5] - 2026-10-17 + +### 개선 + +- 사용자 규칙에서 추천 허용 도메인을 중복 표시하지 않고, 허용 목록 안에서 직접 추가한 규칙과 추천 규칙을 구분합니다. +- 추천 규칙이 많아지면 목록을 접어두고 필요할 때 펼쳐볼 수 있으며, 검색 결과는 자동으로 보여줍니다. +- 추천 규칙 동기화 상태와 최근 확인 시간을 더 간결하게 표시합니다. + +### 테스트 + +- 기존 사용자 규칙과 추천 규칙의 구분, 삭제 권한, 접기·펼치기 및 데모 격리를 검증합니다. + +## [0.2.29-r4] - 2026-10-17 + +### 개선 + +- 추천 사용자 규칙을 약 30초 간격으로 확인하여 공유기에 더 빠르게 반영합니다. +- 규칙이 변경되지 않으면 목록을 다시 내려받거나 SafeShield에 적용하지 않습니다. +- 기존 계정·라이선스 동기화는 5분 주기를 유지하고, 통신 오류 시 재시도 간격을 늘립니다. +- 사용자 규칙의 '지금 동기화' 버튼도 가벼운 추천 규칙 동기화를 사용합니다. + +### 테스트 + +- 인증된 변경 확인, 적용·해제, 실패 후 재시도 및 데모 격리 동작을 검증합니다. + + +## [0.2.29-r3] - 2026-10-17 + +### 추가 + +- 사용자 규칙 화면에서 추천 규칙을 바로 동기화할 수 있습니다. +- 추천 규칙으로 확인된 허용 도메인과 최근 동기화 상태를 표시합니다. + +### 개선 + +- 동기화 대기, 허용 목록 확인, 동기화 실패를 구분합니다. +- SafeShield 차단 목록 갱신과 추천 규칙 동기화의 차이를 안내합니다. + +### 테스트 + +- 수동 동기화 버튼, 인증 권한 및 데모 격리 상태를 확인합니다. + + +## [0.2.29-r2] - 2026-10-17 + +### 수정 + +- 추천 사용자 규칙을 적용하지 못하는 문제를 수정하였습니다. + ## [0.2.29-r1] - 2026-10-17 ### 추가 diff --git a/luci-app-smartsafehub/Makefile b/luci-app-smartsafehub/Makefile index 367968519..138cb6abc 100644 --- a/luci-app-smartsafehub/Makefile +++ b/luci-app-smartsafehub/Makefile @@ -6,8 +6,8 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-smartsafehub -PKG_VERSION:=0.2.29 -PKG_RELEASE:=9 +PKG_VERSION:=0.2.30 +PKG_RELEASE:=10 PKG_MAINTAINER:=Beomjun Kang PKG_LICENSE:=GPL-3.0-or-later diff --git a/luci-app-smartsafehub/frontend/demo-dist/demo.css b/luci-app-smartsafehub/frontend/demo-dist/demo.css index 1deeb8043..33ff16ce1 100644 --- a/luci-app-smartsafehub/frontend/demo-dist/demo.css +++ b/luci-app-smartsafehub/frontend/demo-dist/demo.css @@ -1,3 +1,3 @@ /*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */ -@layer properties{:host,*,:before,:after{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial}::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial}@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-outline-style:solid;--tw-ease:initial}::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-outline-style:solid;--tw-ease:initial}}}@layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-50:#fef2f2;--color-red-100:#ffe2e2;--color-red-200:#ffcaca;--color-red-300:#ffa3a3;--color-red-700:#bf000f;--color-red-800:#9f0712;--color-red-900:#82181a;--color-red-950:#460809;--color-amber-50:#fffbeb;--color-amber-100:#fef3c6;--color-amber-200:#fee685;--color-amber-300:#ffd236;--color-amber-500:#f99c00;--color-amber-600:#dd7400;--color-amber-700:#b75000;--color-amber-800:#953d00;--color-amber-900:#7b3306;--color-amber-950:#461901;--color-emerald-50:#ecfdf5;--color-emerald-100:#d0fae5;--color-emerald-200:#a4f4cf;--color-emerald-500:#00bb7f;--color-emerald-700:#007956;--color-emerald-800:#005f46;--color-emerald-900:#004e3b;--color-emerald-950:#002c22;--color-teal-50:#f0fdfa;--color-teal-100:#cbfbf1;--color-teal-200:#96f7e4;--color-teal-300:#46ecd5;--color-teal-400:#00d3bd;--color-teal-500:#00baa7;--color-teal-600:#009588;--color-teal-700:#00776e;--color-teal-800:#005f5a;--color-teal-900:#0b4f4a;--color-teal-950:#022f2e;--color-sky-50:#f0f9ff;--color-sky-100:#dff2fe;--color-sky-200:#b8e6fe;--color-sky-600:#0084cc;--color-sky-700:#0069a4;--color-sky-800:#005986;--color-sky-950:#052f4a;--color-blue-50:#eff6ff;--color-blue-200:#bedbff;--color-blue-700:#1447e6;--color-rose-50:#fff1f2;--color-rose-100:#ffe4e6;--color-rose-200:#ffccd3;--color-rose-300:#ffa2ae;--color-rose-600:#e70044;--color-rose-700:#c20039;--color-rose-800:#a30037;--color-rose-900:#8b0836;--color-rose-950:#4d0218;--color-slate-50:#f8fafc;--color-slate-100:#f1f5f9;--color-slate-200:#e2e8f0;--color-slate-300:#cad5e2;--color-slate-400:#90a1b9;--color-slate-500:#62748e;--color-slate-600:#45556c;--color-slate-700:#314158;--color-slate-800:#1d293d;--color-slate-900:#0f172b;--color-slate-950:#020618;--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-sm:24rem;--container-lg:32rem;--container-xl:36rem;--container-2xl:42rem;--container-3xl:48rem;--container-4xl:56rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25 / 1.875);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--font-weight-extrabold:800;--font-weight-black:900;--tracking-tight:-.025em;--tracking-wide:.025em;--leading-relaxed:1.625;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--radius-2xl:1rem;--ease-out:cubic-bezier(0, 0, .2, 1);--animate-spin:spin 1s linear infinite;--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--animate-bounce:bounce 1s infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}@supports (color:color(display-p3 0 0 0)){:root,:host{--color-red-50:color(display-p3 .988669 .951204 .950419);--color-red-100:color(display-p3 .980386 .889727 .887779);--color-red-200:color(display-p3 .969562 .798149 .794299);--color-red-300:color(display-p3 .956922 .651886 .645122);--color-red-700:color(display-p3 .692737 .116232 .104679);--color-red-800:color(display-p3 .569606 .121069 .108493);--color-red-900:color(display-p3 .466906 .133198 .12139);--color-red-950:color(display-p3 .25064 .0550983 .0468818);--color-amber-50:color(display-p3 .997804 .985047 .926312);--color-amber-100:color(display-p3 .989391 .954583 .796328);--color-amber-200:color(display-p3 .979824 .904554 .57325);--color-amber-300:color(display-p3 .974327 .83063 .33298);--color-amber-500:color(display-p3 .93994 .620584 .0585367);--color-amber-600:color(display-p3 .827144 .467166 .0336006);--color-amber-700:color(display-p3 .67989 .32771 .0520514);--color-amber-800:color(display-p3 .547533 .256058 .0728326);--color-amber-900:color(display-p3 .445958 .216422 .0823321);--color-amber-950:color(display-p3 .252662 .109091 .026881);--color-emerald-50:color(display-p3 .936818 .989882 .961937);--color-emerald-100:color(display-p3 .848335 .975974 .901691);--color-emerald-200:color(display-p3 .713164 .947563 .822283);--color-emerald-500:color(display-p3 .267113 .726847 .508397);--color-emerald-700:color(display-p3 .164041 .470229 .343508);--color-emerald-800:color(display-p3 .135396 .371401 .277561);--color-emerald-900:color(display-p3 .117821 .302975 .234501);--color-emerald-950:color(display-p3 .0549113 .170434 .13484);--color-teal-50:color(display-p3 .951444 .990904 .98112);--color-teal-100:color(display-p3 .834533 .980328 .946428);--color-teal-200:color(display-p3 .675869 .957879 .896029);--color-teal-300:color(display-p3 .484989 .914726 .835079);--color-teal-400:color(display-p3 .322787 .822056 .744388);--color-teal-500:color(display-p3 .266569 .721518 .655462);--color-teal-600:color(display-p3 .207114 .579584 .53668);--color-teal-700:color(display-p3 .174094 .461318 .433866);--color-teal-800:color(display-p3 .1457 .367051 .350749);--color-teal-900:color(display-p3 .135344 .303314 .290629);--color-teal-950:color(display-p3 .0664925 .181482 .179465);--color-sky-50:color(display-p3 .946501 .975402 .998336);--color-sky-100:color(display-p3 .889195 .946622 .992447);--color-sky-200:color(display-p3 .758153 .897447 .986665);--color-sky-600:color(display-p3 .162505 .509376 .796082);--color-sky-700:color(display-p3 .130314 .404568 .640847);--color-sky-800:color(display-p3 .120719 .343083 .524405);--color-sky-950:color(display-p3 .0726429 .180783 .28188);--color-blue-50:color(display-p3 .941826 .963151 .995385);--color-blue-200:color(display-p3 .76688 .855207 .987483);--color-blue-700:color(display-p3 .1379 .274983 .867624);--color-rose-50:color(display-p3 .989671 .946597 .949215);--color-rose-100:color(display-p3 .982241 .897232 .902568);--color-rose-200:color(display-p3 .96875 .808776 .827317);--color-rose-300:color(display-p3 .96017 .647703 .683715);--color-rose-600:color(display-p3 .848792 .102011 .269259);--color-rose-700:color(display-p3 .711801 .0770816 .226777);--color-rose-800:color(display-p3 .591248 .0929065 .220097);--color-rose-900:color(display-p3 .498064 .104884 .214595);--color-rose-950:color(display-p3 .275407 .0406065 .0990695);--color-slate-50:color(display-p3 .974377 .979815 .986207);--color-slate-100:color(display-p3 .947345 .959969 .97483);--color-slate-200:color(display-p3 .890322 .909405 .939294);--color-slate-300:color(display-p3 .800294 .834432 .882803);--color-slate-400:color(display-p3 .577446 .629622 .716602);--color-slate-500:color(display-p3 .397645 .452653 .547642);--color-slate-600:color(display-p3 .283418 .332214 .416355);--color-slate-700:color(display-p3 .205992 .253487 .336039);--color-slate-800:color(display-p3 .121994 .158688 .232363);--color-slate-900:color(display-p3 .0639692 .0891152 .163036);--color-slate-950:color(display-p3 .0102437 .0227555 .0878731)}}@supports (color:lab(0% 0 0)){:root,:host{--color-red-50:lab(96.5005% 4.18508 1.52328);--color-red-100:lab(92.243% 10.2865 3.83865);--color-red-200:lab(86.017% 19.8815 7.75869);--color-red-300:lab(76.5514% 36.422 15.5335);--color-red-700:lab(40.4273% 67.2623 53.7441);--color-red-800:lab(33.7174% 55.8993 41.0293);--color-red-900:lab(28.5139% 44.5539 29.0463);--color-red-950:lab(13.003% 29.04 16.7519);--color-amber-50:lab(98.6252% -.635922 8.42309);--color-amber-100:lab(95.916% -1.21653 23.111);--color-amber-200:lab(91.7203% -.505269 49.9084);--color-amber-300:lab(86.4156% 6.13147 78.3961);--color-amber-500:lab(72.7183% 31.8672 97.9407);--color-amber-600:lab(60.3514% 40.5624 87.1228);--color-amber-700:lab(47.2709% 42.9082 69.2966);--color-amber-800:lab(37.8822% 37.1699 52.2718);--color-amber-900:lab(31.2288% 30.2627 40.0378);--color-amber-950:lab(15.8111% 20.9107 23.3752);--color-emerald-50:lab(97.8462% -6.94966 1.85487);--color-emerald-100:lab(94.9004% -17.0769 5.63836);--color-emerald-200:lab(90.2247% -31.039 9.47084);--color-emerald-500:lab(66.9756% -58.27 19.5419);--color-emerald-700:lab(44.4871% -41.0396 11.0361);--color-emerald-800:lab(35.3675% -33.1188 8.04002);--color-emerald-900:lab(28.8637% -26.9249 5.45986);--color-emerald-950:lab(15.0582% -17.9507 2.38369);--color-teal-50:lab(98.3189% -4.74921 -.111711);--color-teal-100:lab(95.1845% -17.4212 -.425422);--color-teal-200:lab(90.7612% -33.1343 -.542295);--color-teal-300:lab(84.8977% -48.1516 -1.3321);--color-teal-400:lab(76.0109% -53.3483 -2.27906);--color-teal-500:lab(67.3859% -49.0983 -2.63511);--color-teal-600:lab(55.0223% -41.0774 -3.90277);--color-teal-700:lab(44.4134% -33.1436 -4.22149);--color-teal-800:lab(35.5975% -26.6648 -4.34487);--color-teal-900:lab(29.506% -21.4706 -3.59886);--color-teal-950:lab(16.6371% -15.3183 -3.81732);--color-sky-50:lab(97.3623% -2.33802 -4.13098);--color-sky-100:lab(94.3709% -4.56053 -8.23453);--color-sky-200:lab(88.6983% -11.3978 -16.8488);--color-sky-600:lab(51.7754% -11.4712 -49.8349);--color-sky-700:lab(41.6013% -9.10804 -42.5647);--color-sky-800:lab(35.164% -9.57692 -34.4068);--color-sky-950:lab(17.8299% -5.31271 -21.1584);--color-blue-50:lab(96.492% -1.14644 -5.11479);--color-blue-200:lab(86.15% -4.04379 -21.0797);--color-blue-700:lab(36.9089% 35.0961 -85.6872);--color-rose-50:lab(96.2369% 4.94155 1.28011);--color-rose-100:lab(92.8221% 9.86832 2.60075);--color-rose-200:lab(86.806% 19.1909 4.07754);--color-rose-300:lab(76.6339% 38.3549 9.68835);--color-rose-600:lab(49.1882% 81.577 36.0311);--color-rose-700:lab(41.1651% 71.6251 30.3087);--color-rose-800:lab(34.6481% 60.802 20.1957);--color-rose-900:lab(29.7104% 51.514 12.6253);--color-rose-950:lab(14.2323% 34.0086 9.80922);--color-slate-50:lab(98.1434% -.369519 -1.05966);--color-slate-100:lab(96.286% -.852436 -2.46847);--color-slate-200:lab(91.7353% -.998765 -4.76968);--color-slate-300:lab(84.7652% -1.94535 -7.93337);--color-slate-400:lab(65.5349% -2.25151 -14.5072);--color-slate-500:lab(48.0876% -2.03595 -16.5814);--color-slate-600:lab(35.5623% -1.74978 -15.4316);--color-slate-700:lab(26.9569% -1.47016 -15.6993);--color-slate-800:lab(16.132% -.318035 -14.6672);--color-slate-900:lab(7.78673% 1.82345 -15.0537);--color-slate-950:lab(1.76974% 1.32743 -9.28855)}}}@layer base{*,:after,:before{box-sizing:border-box;border:0 solid;margin:0;padding:0}::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::-webkit-file-upload-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::-webkit-file-upload-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:-webkit-any([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:-webkit-any([multiple],[size])) optgroup option{padding-inline-start:20px}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::-webkit-file-upload-button{margin-inline-end:4px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block-start:0;padding-block-end:0}::-webkit-datetime-edit-year-field{padding-block-start:0;padding-block-end:0}::-webkit-datetime-edit-month-field{padding-block-start:0;padding-block-end:0}::-webkit-datetime-edit-day-field{padding-block-start:0;padding-block-end:0}::-webkit-datetime-edit-hour-field{padding-block-start:0;padding-block-end:0}::-webkit-datetime-edit-minute-field{padding-block-start:0;padding-block-end:0}::-webkit-datetime-edit-second-field{padding-block-start:0;padding-block-end:0}::-webkit-datetime-edit-millisecond-field{padding-block-start:0;padding-block-end:0}::-webkit-datetime-edit-meridiem-field{padding-block-start:0;padding-block-end:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button{-webkit-appearance:button;appearance:button}input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;appearance:button}::-webkit-file-upload-button{-webkit-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-0{top:0;bottom:0;left:0;right:0}.inset-y-0{top:0;bottom:0}.top-0{top:0}.top-0\.5{top:calc(var(--spacing) * .5)}.top-1{top:var(--spacing)}.top-\[4\.5rem\]{top:4.5rem}.right-0{right:0}.right-0\.5{right:calc(var(--spacing) * .5)}.right-1{right:var(--spacing)}.left-0{left:0}.left-1{left:var(--spacing)}.left-6{left:calc(var(--spacing) * 6)}.isolate{isolation:isolate}.z-20{z-index:20}.z-40{z-index:40}.z-50{z-index:50}.z-\[1000\]{z-index:1000}.m-0{margin:0}.mx-5{margin-inline:calc(var(--spacing) * 5)}.mx-auto{margin-inline-start:auto;margin-inline-end:auto}.my-5{margin-block:calc(var(--spacing) * 5)}.my-6{margin-block:calc(var(--spacing) * 6)}.my-auto{margin-block-start:auto;margin-block-end:auto}.mt-0{margin-top:0}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:var(--spacing)}.mt-1\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-5{margin-top:calc(var(--spacing) * 5)}.mt-6{margin-top:calc(var(--spacing) * 6)}.mt-7{margin-top:calc(var(--spacing) * 7)}.mb-0{margin-bottom:0}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-5{margin-bottom:calc(var(--spacing) * 5)}.ml-0{margin-left:0}.ml-3{margin-left:calc(var(--spacing) * 3)}.line-clamp-2{-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-flex{display:inline-flex}.size-2{width:calc(var(--spacing) * 2);height:calc(var(--spacing) * 2)}.size-2\.5{width:calc(var(--spacing) * 2.5);height:calc(var(--spacing) * 2.5)}.size-3{width:calc(var(--spacing) * 3);height:calc(var(--spacing) * 3)}.size-3\.5{width:calc(var(--spacing) * 3.5);height:calc(var(--spacing) * 3.5)}.size-4{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.size-5{width:calc(var(--spacing) * 5);height:calc(var(--spacing) * 5)}.size-6{width:calc(var(--spacing) * 6);height:calc(var(--spacing) * 6)}.size-7{width:calc(var(--spacing) * 7);height:calc(var(--spacing) * 7)}.size-8{width:calc(var(--spacing) * 8);height:calc(var(--spacing) * 8)}.size-9{width:calc(var(--spacing) * 9);height:calc(var(--spacing) * 9)}.size-10{width:calc(var(--spacing) * 10);height:calc(var(--spacing) * 10)}.size-11{width:calc(var(--spacing) * 11);height:calc(var(--spacing) * 11)}.size-12{width:calc(var(--spacing) * 12);height:calc(var(--spacing) * 12)}.h-1\.5{height:calc(var(--spacing) * 1.5)}.h-2{height:calc(var(--spacing) * 2)}.h-10{height:calc(var(--spacing) * 10)}.h-44{height:calc(var(--spacing) * 44)}.h-\[4\.5rem\]{height:4.5rem}.h-auto{height:auto}.h-full{height:100%}.h-screen{height:100vh}.max-h-40{max-height:calc(var(--spacing) * 40)}.max-h-\[28rem\]{max-height:28rem}.min-h-9{min-height:calc(var(--spacing) * 9)}.min-h-10{min-height:calc(var(--spacing) * 10)}.min-h-11{min-height:calc(var(--spacing) * 11)}.min-h-16{min-height:calc(var(--spacing) * 16)}.min-h-52{min-height:calc(var(--spacing) * 52)}.min-h-\[4\.5rem\]{min-height:4.5rem}.min-h-screen{min-height:100vh}.w-10{width:calc(var(--spacing) * 10)}.w-11{width:calc(var(--spacing) * 11)}.w-12{width:calc(var(--spacing) * 12)}.w-fit{width:-moz-fit-content;width:fit-content}.w-full{width:100%}.max-w-2xl{max-width:var(--container-2xl)}.max-w-3xl{max-width:var(--container-3xl)}.max-w-4xl{max-width:var(--container-4xl)}.max-w-\[68\%\]{max-width:68%}.max-w-\[288px\]{max-width:288px}.max-w-\[1600px\]{max-width:1600px}.max-w-full{max-width:100%}.max-w-lg{max-width:var(--container-lg)}.max-w-sm{max-width:var(--container-sm)}.max-w-xl{max-width:var(--container-xl)}.min-w-0{min-width:0}.min-w-20{min-width:calc(var(--spacing) * 20)}.flex-1{flex:1}.shrink-0{flex-shrink:0}.translate-x-1\/2{--tw-translate-x:calc(1 / 2 * 100%);translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-1\/2{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.rotate-180{rotate:180deg}.animate-bounce{animation:var(--animate-bounce)}.animate-pulse{animation:var(--animate-pulse)}.animate-spin{animation:var(--animate-spin)}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.cursor-wait{cursor:wait}.resize{resize:both}.list-none{list-style-type:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.place-items-center{place-items:center}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-1{gap:var(--spacing)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-5{gap:calc(var(--spacing) * 5)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(var(--spacing) * var(--tw-space-y-reverse));margin-block-end:calc(var(--spacing) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1.5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1.5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 6) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-7>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 7) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 7) * calc(1 - var(--tw-space-y-reverse)))}.gap-x-3{column-gap:calc(var(--spacing) * 3)}.gap-x-5{column-gap:calc(var(--spacing) * 5)}.gap-y-1\.5{row-gap:calc(var(--spacing) * 1.5)}.gap-y-2{row-gap:calc(var(--spacing) * 2)}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-slate-200>:not(:last-child)){border-color:var(--color-slate-200)}.self-start{align-self:flex-start}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overscroll-contain{overscroll-behavior:contain}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-full{border-radius:2147483647px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-0{border-style:var(--tw-border-style);border-width:0}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-amber-200{border-color:var(--color-amber-200)}.border-amber-300{border-color:var(--color-amber-300)}.border-current{border-color:currentColor}.border-emerald-200{border-color:var(--color-emerald-200)}.border-emerald-700{border-color:var(--color-emerald-700)}.border-red-200{border-color:var(--color-red-200)}.border-red-700{border-color:var(--color-red-700)}.border-rose-200{border-color:var(--color-rose-200)}.border-rose-300{border-color:var(--color-rose-300)}.border-sky-100{border-color:var(--color-sky-100)}.border-sky-200{border-color:var(--color-sky-200)}.border-slate-100{border-color:var(--color-slate-100)}.border-slate-200{border-color:var(--color-slate-200)}.border-slate-300{border-color:var(--color-slate-300)}.border-teal-200{border-color:var(--color-teal-200)}.border-teal-300{border-color:var(--color-teal-300)}.border-teal-600{border-color:var(--color-teal-600)}.border-teal-700{border-color:var(--color-teal-700)}.border-white\/80{border-color:#fffc}@supports (color:color-mix(in lab, red, red)){.border-white\/80{border-color:color-mix(in oklab, var(--color-white) 80%, transparent)}}.bg-amber-50{background-color:var(--color-amber-50)}.bg-amber-50\/70{background-color:#fffbebb3}@supports (color:color-mix(in lab, red, red)){.bg-amber-50\/70{background-color:color-mix(in oklab, var(--color-amber-50) 70%, transparent)}}.bg-amber-100{background-color:var(--color-amber-100)}.bg-amber-500{background-color:var(--color-amber-500)}.bg-amber-600{background-color:var(--color-amber-600)}.bg-amber-700{background-color:var(--color-amber-700)}.bg-blue-50{background-color:var(--color-blue-50)}.bg-emerald-50{background-color:var(--color-emerald-50)}.bg-emerald-50\/70{background-color:#ecfdf5b3}@supports (color:color-mix(in lab, red, red)){.bg-emerald-50\/70{background-color:color-mix(in oklab, var(--color-emerald-50) 70%, transparent)}}.bg-emerald-100{background-color:var(--color-emerald-100)}.bg-emerald-500{background-color:var(--color-emerald-500)}.bg-emerald-700{background-color:var(--color-emerald-700)}.bg-red-50{background-color:var(--color-red-50)}.bg-red-100{background-color:var(--color-red-100)}.bg-red-700{background-color:var(--color-red-700)}.bg-rose-50{background-color:var(--color-rose-50)}.bg-rose-50\/70{background-color:#fff1f2b3}@supports (color:color-mix(in lab, red, red)){.bg-rose-50\/70{background-color:color-mix(in oklab, var(--color-rose-50) 70%, transparent)}}.bg-rose-100{background-color:var(--color-rose-100)}.bg-rose-700{background-color:var(--color-rose-700)}.bg-sky-50{background-color:var(--color-sky-50)}.bg-sky-50\/60{background-color:#f0f9ff99}@supports (color:color-mix(in lab, red, red)){.bg-sky-50\/60{background-color:color-mix(in oklab, var(--color-sky-50) 60%, transparent)}}.bg-sky-600{background-color:var(--color-sky-600)}.bg-sky-700{background-color:var(--color-sky-700)}.bg-slate-50{background-color:var(--color-slate-50)}.bg-slate-50\/70{background-color:#f8fafcb3}@supports (color:color-mix(in lab, red, red)){.bg-slate-50\/70{background-color:color-mix(in oklab, var(--color-slate-50) 70%, transparent)}}.bg-slate-100{background-color:var(--color-slate-100)}.bg-slate-200{background-color:var(--color-slate-200)}.bg-slate-300{background-color:var(--color-slate-300)}.bg-slate-400{background-color:var(--color-slate-400)}.bg-slate-900{background-color:var(--color-slate-900)}.bg-slate-950{background-color:var(--color-slate-950)}.bg-slate-950\/70{background-color:#020618b3}@supports (color:color-mix(in lab, red, red)){.bg-slate-950\/70{background-color:color-mix(in oklab, var(--color-slate-950) 70%, transparent)}}.bg-teal-50{background-color:var(--color-teal-50)}.bg-teal-100{background-color:var(--color-teal-100)}.bg-teal-500{background-color:var(--color-teal-500)}.bg-teal-600{background-color:var(--color-teal-600)}.bg-teal-700{background-color:var(--color-teal-700)}.bg-transparent{background-color:#0000}.bg-white{background-color:var(--color-white)}.bg-white\/70{background-color:#ffffffb3}@supports (color:color-mix(in lab, red, red)){.bg-white\/70{background-color:color-mix(in oklab, var(--color-white) 70%, transparent)}}.bg-white\/95{background-color:#fffffff2}@supports (color:color-mix(in lab, red, red)){.bg-white\/95{background-color:color-mix(in oklab, var(--color-white) 95%, transparent)}}.p-0{padding:0}.p-1\.5{padding:calc(var(--spacing) * 1.5)}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-3\.5{padding:calc(var(--spacing) * 3.5)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-3\.5{padding-inline:calc(var(--spacing) * 3.5)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-5{padding-inline:calc(var(--spacing) * 5)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-5{padding-block:calc(var(--spacing) * 5)}.py-10{padding-block:calc(var(--spacing) * 10)}.pt-1{padding-top:var(--spacing)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-3{padding-top:calc(var(--spacing) * 3)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pt-5{padding-top:calc(var(--spacing) * 5)}.pt-6{padding-top:calc(var(--spacing) * 6)}.pr-4{padding-right:calc(var(--spacing) * 4)}.pr-12{padding-right:calc(var(--spacing) * 12)}.pb-1{padding-bottom:var(--spacing)}.pb-3{padding-bottom:calc(var(--spacing) * 3)}.pb-4{padding-bottom:calc(var(--spacing) * 4)}.pb-5{padding-bottom:calc(var(--spacing) * 5)}.pb-8{padding-bottom:calc(var(--spacing) * 8)}.pl-5{padding-left:calc(var(--spacing) * 5)}.pl-9{padding-left:calc(var(--spacing) * 9)}.pl-11{padding-left:calc(var(--spacing) * 11)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.align-middle{vertical-align:middle}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[0\.7rem\]{font-size:.7rem}.text-\[0\.65rem\]{font-size:.65rem}.text-\[0\.68rem\]{font-size:.68rem}.text-\[9px\]{font-size:9px}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[13px\]{font-size:13px}.leading-5{--tw-leading:calc(var(--spacing) * 5);line-height:calc(var(--spacing) * 5)}.leading-6{--tw-leading:calc(var(--spacing) * 6);line-height:calc(var(--spacing) * 6)}.leading-none{--tw-leading:1;line-height:1}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.font-black{--tw-font-weight:var(--font-weight-black);font-weight:var(--font-weight-black)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-extrabold{--tw-font-weight:var(--font-weight-extrabold);font-weight:var(--font-weight-extrabold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-\[0\.08em\]{--tw-tracking:.08em;letter-spacing:.08em}.tracking-\[0\.12em\]{--tw-tracking:.12em;letter-spacing:.12em}.tracking-\[0\.14em\]{--tw-tracking:.14em;letter-spacing:.14em}.tracking-\[0\.16em\]{--tw-tracking:.16em;letter-spacing:.16em}.tracking-\[0\.18em\]{--tw-tracking:.18em;letter-spacing:.18em}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-amber-700{color:var(--color-amber-700)}.text-amber-800{color:var(--color-amber-800)}.text-amber-900{color:var(--color-amber-900)}.text-amber-900\/80{color:#7b3306cc}@supports (color:color-mix(in lab, red, red)){.text-amber-900\/80{color:color-mix(in oklab, var(--color-amber-900) 80%, transparent)}}.text-amber-950{color:var(--color-amber-950)}.text-blue-700{color:var(--color-blue-700)}.text-current{color:currentColor}.text-emerald-700{color:var(--color-emerald-700)}.text-emerald-800{color:var(--color-emerald-800)}.text-emerald-900{color:var(--color-emerald-900)}.text-emerald-950{color:var(--color-emerald-950)}.text-red-700{color:var(--color-red-700)}.text-red-800{color:var(--color-red-800)}.text-red-900{color:var(--color-red-900)}.text-red-950{color:var(--color-red-950)}.text-rose-600{color:var(--color-rose-600)}.text-rose-700{color:var(--color-rose-700)}.text-rose-800{color:var(--color-rose-800)}.text-rose-900{color:var(--color-rose-900)}.text-rose-950{color:var(--color-rose-950)}.text-sky-700{color:var(--color-sky-700)}.text-sky-800{color:var(--color-sky-800)}.text-sky-950{color:var(--color-sky-950)}.text-slate-300{color:var(--color-slate-300)}.text-slate-400{color:var(--color-slate-400)}.text-slate-500{color:var(--color-slate-500)}.text-slate-600{color:var(--color-slate-600)}.text-slate-700{color:var(--color-slate-700)}.text-slate-800{color:var(--color-slate-800)}.text-slate-900{color:var(--color-slate-900)}.text-slate-950{color:var(--color-slate-950)}.text-teal-300{color:var(--color-teal-300)}.text-teal-600{color:var(--color-teal-600)}.text-teal-700{color:var(--color-teal-700)}.text-teal-800{color:var(--color-teal-800)}.text-teal-950{color:var(--color-teal-950)}.text-white{color:var(--color-white)}.uppercase{text-transform:uppercase}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.no-underline{text-decoration-line:none}.underline{text-decoration-line:underline}.underline-offset-2{text-underline-offset:2px}.accent-teal-700{accent-color:var(--color-teal-700)}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.opacity-80{opacity:.8}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-inner{--tw-shadow:inset 0 2px 4px 0 var(--tw-shadow-color,#0000000d);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-1{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-slate-900\/5{--tw-shadow-color:#0f172b0d}@supports (color:color-mix(in lab, red, red)){.shadow-slate-900\/5{--tw-shadow-color:color-mix(in oklab, color-mix(in oklab, var(--color-slate-900) 5%, transparent) var(--tw-shadow-alpha), transparent)}}.shadow-slate-950\/10{--tw-shadow-color:#0206181a}@supports (color:color-mix(in lab, red, red)){.shadow-slate-950\/10{--tw-shadow-color:color-mix(in oklab, color-mix(in oklab, var(--color-slate-950) 10%, transparent) var(--tw-shadow-alpha), transparent)}}.ring-amber-200{--tw-ring-color:var(--color-amber-200)}.ring-blue-200{--tw-ring-color:var(--color-blue-200)}.ring-emerald-200{--tw-ring-color:var(--color-emerald-200)}.ring-red-200{--tw-ring-color:var(--color-red-200)}.ring-rose-200{--tw-ring-color:var(--color-rose-200)}.ring-sky-100{--tw-ring-color:var(--color-sky-100)}.ring-sky-200{--tw-ring-color:var(--color-sky-200)}.ring-slate-100{--tw-ring-color:var(--color-slate-100)}.ring-slate-200{--tw-ring-color:var(--color-slate-200)}.ring-teal-100{--tw-ring-color:var(--color-teal-100)}.ring-teal-200{--tw-ring-color:var(--color-teal-200)}.ring-white{--tw-ring-color:var(--color-white)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.backdrop-blur{--tw-backdrop-blur:blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[left\]{transition-property:left;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[width\]{transition-property:width;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-300{--tw-duration:.3s;transition-duration:.3s}.outline-none{--tw-outline-style:none;outline-style:none}.select-all{-webkit-user-select:all;user-select:all}.ring-inset{--tw-ring-inset:inset}.group-open\:hidden:-webkit-any(:where(.group):-webkit-any([open],:popover-open,:open) *){display:none}.group-open\:hidden:is(:where(.group):is([open],:popover-open,:open) *){display:none}.group-open\:inline-block:-webkit-any(:where(.group):-webkit-any([open],:popover-open,:open) *){display:inline-block}.group-open\:inline-block:is(:where(.group):is([open],:popover-open,:open) *){display:inline-block}.group-open\:inline-flex:-webkit-any(:where(.group):-webkit-any([open],:popover-open,:open) *){display:inline-flex}.group-open\:inline-flex:is(:where(.group):is([open],:popover-open,:open) *){display:inline-flex}.placeholder\:text-slate-400::placeholder{color:var(--color-slate-400)}.first\:mt-0:first-child{margin-top:0}.first\:pt-0:first-child{padding-top:0}.last\:border-b-0:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.last\:pb-0:last-child{padding-bottom:0}@media (hover:hover){.hover\:border-emerald-800:hover{border-color:var(--color-emerald-800)}.hover\:border-red-200:hover{border-color:var(--color-red-200)}.hover\:border-red-300:hover{border-color:var(--color-red-300)}.hover\:border-red-800:hover{border-color:var(--color-red-800)}.hover\:border-slate-300:hover{border-color:var(--color-slate-300)}.hover\:border-teal-200:hover{border-color:var(--color-teal-200)}.hover\:border-teal-300:hover{border-color:var(--color-teal-300)}.hover\:border-teal-400:hover{border-color:var(--color-teal-400)}.hover\:border-teal-800:hover{border-color:var(--color-teal-800)}.hover\:bg-amber-50:hover{background-color:var(--color-amber-50)}.hover\:bg-amber-100:hover{background-color:var(--color-amber-100)}.hover\:bg-amber-700:hover{background-color:var(--color-amber-700)}.hover\:bg-amber-800:hover{background-color:var(--color-amber-800)}.hover\:bg-black\/5:hover{background-color:#0000000d}@supports (color:color-mix(in lab, red, red)){.hover\:bg-black\/5:hover{background-color:color-mix(in oklab, var(--color-black) 5%, transparent)}}.hover\:bg-emerald-800:hover{background-color:var(--color-emerald-800)}.hover\:bg-red-50:hover{background-color:var(--color-red-50)}.hover\:bg-red-800:hover{background-color:var(--color-red-800)}.hover\:bg-rose-50:hover{background-color:var(--color-rose-50)}.hover\:bg-rose-100:hover{background-color:var(--color-rose-100)}.hover\:bg-rose-800:hover{background-color:var(--color-rose-800)}.hover\:bg-sky-800:hover{background-color:var(--color-sky-800)}.hover\:bg-slate-50:hover{background-color:var(--color-slate-50)}.hover\:bg-slate-100:hover{background-color:var(--color-slate-100)}.hover\:bg-slate-800:hover{background-color:var(--color-slate-800)}.hover\:bg-teal-50:hover{background-color:var(--color-teal-50)}.hover\:bg-teal-100:hover{background-color:var(--color-teal-100)}.hover\:bg-teal-800:hover{background-color:var(--color-teal-800)}.hover\:text-red-700:hover{color:var(--color-red-700)}.hover\:text-rose-800:hover{color:var(--color-rose-800)}.hover\:text-slate-800:hover{color:var(--color-slate-800)}.hover\:text-slate-950:hover{color:var(--color-slate-950)}.hover\:text-teal-700:hover{color:var(--color-teal-700)}.hover\:text-teal-800:hover{color:var(--color-teal-800)}.hover\:text-teal-900:hover{color:var(--color-teal-900)}.hover\:opacity-100:hover{opacity:1}.hover\:shadow-md:hover{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}}.focus\:border-teal-500:focus{border-color:var(--color-teal-500)}.focus\:bg-white:focus{background-color:var(--color-white)}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus\:ring-4:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus\:ring-teal-100:focus{--tw-ring-color:var(--color-teal-100)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.focus-visible\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus-visible\:ring-4:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus-visible\:ring-amber-100:focus-visible{--tw-ring-color:var(--color-amber-100)}.focus-visible\:ring-amber-200:focus-visible{--tw-ring-color:var(--color-amber-200)}.focus-visible\:ring-current:focus-visible{--tw-ring-color:currentcolor}.focus-visible\:ring-emerald-100:focus-visible{--tw-ring-color:var(--color-emerald-100)}.focus-visible\:ring-red-100:focus-visible{--tw-ring-color:var(--color-red-100)}.focus-visible\:ring-red-200:focus-visible{--tw-ring-color:var(--color-red-200)}.focus-visible\:ring-teal-100:focus-visible{--tw-ring-color:var(--color-teal-100)}.focus-visible\:ring-teal-200:focus-visible{--tw-ring-color:var(--color-teal-200)}.focus-visible\:ring-teal-500:focus-visible{--tw-ring-color:var(--color-teal-500)}.focus-visible\:outline-2:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px}.focus-visible\:outline-offset-2:focus-visible{outline-offset:2px}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:cursor-wait:disabled{cursor:wait}.disabled\:bg-slate-100:disabled{background-color:var(--color-slate-100)}.disabled\:text-slate-400:disabled{color:var(--color-slate-400)}.disabled\:opacity-50:disabled{opacity:.5}.disabled\:opacity-60:disabled{opacity:.6}@media (min-width:40rem){.sm\:mx-6{margin-inline:calc(var(--spacing) * 6)}.sm\:mb-6{margin-bottom:calc(var(--spacing) * 6)}.sm\:h-52{height:calc(var(--spacing) * 52)}.sm\:min-h-9{min-height:calc(var(--spacing) * 9)}.sm\:min-h-10{min-height:calc(var(--spacing) * 10)}.sm\:w-auto{width:auto}.sm\:max-w-\[392px\]{max-width:392px}.sm\:max-w-\[592px\]{max-width:592px}.sm\:min-w-80{min-width:calc(var(--spacing) * 80)}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.sm\:grid-cols-\[minmax\(0\,1fr\)_auto\]{grid-template-columns:minmax(0,1fr) auto}.sm\:flex-row{flex-direction:row}.sm\:flex-wrap{flex-wrap:wrap}.sm\:items-center{align-items:center}.sm\:items-end{align-items:flex-end}.sm\:items-start{align-items:flex-start}.sm\:justify-between{justify-content:space-between}.sm\:justify-end{justify-content:flex-end}.sm\:gap-4{gap:calc(var(--spacing) * 4)}.sm\:self-auto{align-self:auto}.sm\:p-5{padding:calc(var(--spacing) * 5)}.sm\:p-6{padding:calc(var(--spacing) * 6)}.sm\:p-7{padding:calc(var(--spacing) * 7)}.sm\:p-8{padding:calc(var(--spacing) * 8)}.sm\:px-2{padding-inline:calc(var(--spacing) * 2)}.sm\:px-4{padding-inline:calc(var(--spacing) * 4)}.sm\:px-5{padding-inline:calc(var(--spacing) * 5)}.sm\:px-6{padding-inline:calc(var(--spacing) * 6)}.sm\:pt-2{padding-top:calc(var(--spacing) * 2)}.sm\:pt-6{padding-top:calc(var(--spacing) * 6)}.sm\:pb-6{padding-bottom:calc(var(--spacing) * 6)}.sm\:pb-10{padding-bottom:calc(var(--spacing) * 10)}.sm\:text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.sm\:text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.sm\:text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}}@media (min-width:48rem){.md\:flex{display:flex}.md\:grid{display:grid}.md\:hidden{display:none}.md\:inline-flex{display:inline-flex}.md\:h-\[4\.5rem\]{height:4.5rem}.md\:h-full{height:100%}.md\:min-h-screen{min-height:100vh}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-\[5rem_minmax\(0\,1fr\)\]{grid-template-columns:5rem minmax(0,1fr)}.md\:grid-cols-\[16rem_minmax\(0\,1fr\)\]{grid-template-columns:16rem minmax(0,1fr)}.md\:flex-col{flex-direction:column}.md\:py-0{padding-block-start:0;padding-block-end:0}.md\:transition-\[grid-template-columns\]{transition-property:grid-template-columns;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.md\:duration-200{--tw-duration:.2s;transition-duration:.2s}.md\:ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}}@media (min-width:64rem){.lg\:col-span-2{grid-column:span 2/span 2}.lg\:col-start-2{grid-column-start:2}.lg\:block{display:block}.lg\:max-w-sm{max-width:var(--container-sm)}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:grid-cols-\[minmax\(0\,1fr\)_minmax\(0\,1fr\)\]{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}.lg\:flex-row{flex-direction:row}.lg\:items-center{align-items:center}.lg\:items-start{align-items:flex-start}.lg\:justify-between{justify-content:space-between}.lg\:justify-end{justify-content:flex-end}.lg\:px-8{padding-inline:calc(var(--spacing) * 8)}.lg\:pt-3{padding-top:calc(var(--spacing) * 3)}.lg\:pt-8{padding-top:calc(var(--spacing) * 8)}.lg\:pb-12{padding-bottom:calc(var(--spacing) * 12)}.lg\:whitespace-nowrap{white-space:nowrap}}@media (min-width:80rem){.xl\:col-start-1{grid-column-start:1}.xl\:col-start-2{grid-column-start:2}.xl\:row-span-2{grid-row:span 2/span 2}.xl\:row-start-1{grid-row-start:1}.xl\:row-start-2{grid-row-start:2}.xl\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.xl\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.xl\:grid-cols-\[minmax\(0\,0\.95fr\)_minmax\(0\,1\.05fr\)\]{grid-template-columns:minmax(0,.95fr) minmax(0,1.05fr)}.xl\:grid-cols-\[minmax\(0\,1\.2fr\)_minmax\(18rem\,0\.8fr\)\]{grid-template-columns:minmax(0,1.2fr) minmax(18rem,.8fr)}.xl\:grid-cols-\[minmax\(0\,1\.45fr\)_minmax\(20rem\,0\.75fr\)\]{grid-template-columns:minmax(0,1.45fr) minmax(20rem,.75fr)}.xl\:grid-cols-\[minmax\(0\,1\.55fr\)_minmax\(18rem\,0\.65fr\)\]{grid-template-columns:minmax(0,1.55fr) minmax(18rem,.65fr)}.xl\:self-stretch{align-self:stretch}.xl\:border-t-0{border-top-style:var(--tw-border-style);border-top-width:0}.xl\:border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.xl\:px-10{padding-inline:calc(var(--spacing) * 10)}.xl\:whitespace-nowrap{white-space:nowrap}}.\[\&\:\:-webkit-details-marker\]\:hidden::-webkit-details-marker{display:none}}:host{color:#0f172a;--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light;-webkit-font-smoothing:antialiased;-webkit-tap-highlight-color:transparent;text-rendering:optimizelegibility;width:100%;min-width:0;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Noto Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;display:block}.smartsafehub-shadow-root{width:100%;min-width:0;max-width:100%;display:block}.smartsafehub-shadow-root,.smartsafehub-shadow-root *,.smartsafehub-shadow-root :before,.smartsafehub-shadow-root :after{box-sizing:border-box}.ssh-app{width:100%;min-width:0;max-width:100%;overflow:clip}.ssh-app main,.ssh-app section,.ssh-app article,.ssh-app aside,.ssh-app nav,.ssh-app div{min-width:0}.ssh-product-hero{border-radius:0}.ssh-product-header-action{border-color:#e2e8f0!important}.ssh-product-header-action:hover{border-color:#5eead4!important}.ssh-app a,.ssh-app button,.ssh-app input,.ssh-app textarea{touch-action:manipulation}.ssh-app[data-theme=dark] .ssh-custom-select-trigger:disabled{color:#64748b;background-color:#1e293b;border-color:#334155}.ssh-app[data-theme=dark] .ssh-custom-select-trigger:focus-visible{--tw-ring-color:#134e4aa6}.ssh-app[data-theme=dark] .ssh-custom-select-menu{box-shadow:0 18px 45px #00000059}.ssh-mobile-navigation{padding-left:max(1rem, env(safe-area-inset-left));padding-right:max(1rem, env(safe-area-inset-right))}.ssh-mobile-menu{overscroll-behavior:contain;-webkit-overflow-scrolling:touch;max-height:calc(100dvh - 4.5rem);overflow-y:auto}.ssh-touch-scroll{scrollbar-width:none;overscroll-behavior-inline:contain;-webkit-overflow-scrolling:touch}.ssh-touch-scroll::-webkit-scrollbar{display:none}@media (max-width:767px){.ssh-app{min-height:100dvh;overflow-x:clip}.ssh-product-hero{padding-top:max(1.25rem, env(safe-area-inset-top));padding-left:max(1rem, env(safe-area-inset-left));padding-right:max(1rem, env(safe-area-inset-right))}.ssh-product-main{padding-right:max(1rem, env(safe-area-inset-right));padding-bottom:max(2rem, env(safe-area-inset-bottom));padding-left:max(1rem, env(safe-area-inset-left))}.ssh-app input,.ssh-app textarea{font-size:16px}.ssh-app button,.ssh-app a{min-height:44px}}.ssh-sidebar-toggle{color:#64748b;background:#fff;border:1px solid #e2e8f0;box-shadow:0 1px 3px #0f172a1f}.ssh-sidebar-toggle:hover{color:#0f766e;background:#f8fafc;border-color:#99f6e4}.ssh-app[data-theme=dark] .ssh-sidebar-toggle{color:#94a3b8;background:#1e293b;border-color:#334155;box-shadow:0 1px 3px #00000047}.ssh-app[data-theme=dark] .ssh-sidebar-toggle:hover{color:#5eead4;background:#334155;border-color:#0f766e}.ssh-update-nav-badge{color:#92400e;background:#fef3c7;border:1px solid #f59e0b;border-radius:9999px;justify-content:center;align-items:center;min-width:1.3rem;height:1.3rem;padding:0 .25rem;font-size:.75rem;font-weight:900;line-height:1;display:inline-flex;box-shadow:0 1px 2px #0f172a1f}.ssh-update-nav-badge[data-collapsed=true]{z-index:1;width:auto;min-width:1.25rem;height:1.25rem;padding:0 .25rem;position:absolute;top:.125rem;right:.125rem;box-shadow:0 0 0 2px #fff,0 2px 5px #0f172a33}.ssh-app[data-theme=dark] .ssh-update-nav-badge{color:#422006;background:#fbbf24;border-color:#fcd34d;box-shadow:0 1px 3px #0000004d}.ssh-app[data-theme=dark] .ssh-update-nav-badge[data-collapsed=true]{box-shadow:0 0 0 2px #0f172a,0 2px 6px #00000061}.ssh-update-progress-track{background:#bae6fd;border-radius:9999px;height:.375rem;position:relative;overflow:hidden}.ssh-update-progress-bar{border-radius:inherit;background:#0284c7;width:38%;animation:1.2s ease-in-out infinite ssh-update-progress;position:absolute;top:0;bottom:0;left:0}.ssh-app[data-theme=dark] .ssh-update-progress-track{background:#1e3a5f}.ssh-app[data-theme=dark] .ssh-update-progress-bar{background:#38bdf8}.ssh-app button.ssh-switch-control{box-sizing:border-box;flex:0 0 3rem;width:3rem;min-width:3rem;max-width:3rem;height:1.75rem;min-height:1.75rem;max-height:1.75rem;padding:0}.ssh-switch-thumb{aspect-ratio:1;box-sizing:border-box;background-color:#fff;border-radius:9999px;width:1.25rem;min-width:1.25rem;max-width:1.25rem;height:1.25rem;min-height:1.25rem;max-height:1.25rem}@keyframes ssh-update-progress{0%{transform:translate(-110%)}to{transform:translate(300%)}}.ssh-software-settings-grid{grid-template-columns:repeat(auto-fit,minmax(min(100%,18rem),1fr));gap:1rem;display:grid}.ssh-software-settings-channel{grid-column:1/-1}.ssh-time-select{grid-template-columns:repeat(2,minmax(0,1fr));gap:.5rem;width:100%;min-width:0;max-width:100%;display:grid}.ssh-time-select>*{min-width:0}.ssh-time-select .ssh-custom-select-trigger{gap:.5rem;min-width:0;max-width:100%;padding-inline-start:.75rem;padding-inline-end:.75rem}.ssh-ipv4-segments{grid-template-columns:repeat(4,minmax(0,1fr));gap:.75rem;width:100%;min-width:0;max-width:100%;display:grid}.ssh-ipv4-segment{min-width:0;position:relative}.ssh-ipv4-separator{pointer-events:none;position:absolute;top:50%;right:-.375rem;transform:translate(50%,-50%)}@media (min-width:640px){.ssh-ipv4-segments{gap:1rem}.ssh-ipv4-separator{right:-.5rem}}.ssh-app[data-theme=dark]{color:#f8fafc;--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark;background:#020617}.ssh-app[data-theme=dark] [class~=bg-white]{background-color:#0f172a}.ssh-app[data-theme=dark] [class~=bg-white\/95]{background-color:#0f172af2}.ssh-app[data-theme=dark] [class~=bg-white\/70]{background-color:#0f172ac7}.ssh-app[data-theme=dark] [class~=bg-slate-50]{background-color:#020617}.ssh-app[data-theme=dark] [class~=bg-slate-50\/70]{background-color:#020617b8}.ssh-app[data-theme=dark] [class~=bg-slate-100]{background-color:#1e293b}.ssh-app[data-theme=dark] [class~=bg-slate-200]{background-color:#334155}.ssh-app[data-theme=dark] [class~=bg-slate-300]{background-color:#475569}.ssh-app[data-theme=dark] [class~=bg-slate-400]{background-color:#64748b}.ssh-app[data-theme=dark] [class~=bg-black\/5]{background-color:#ffffff0d}.ssh-app[data-theme=dark] [class~=text-slate-950]{color:#f8fafc}.ssh-app[data-theme=dark] [class~=text-slate-900]{color:#f1f5f9}.ssh-app[data-theme=dark] [class~=text-slate-800]{color:#e2e8f0}.ssh-app[data-theme=dark] [class~=text-slate-700],.ssh-app[data-theme=dark] [class~=text-slate-600]{color:#cbd5e1}.ssh-app[data-theme=dark] [class~=text-slate-500],.ssh-app[data-theme=dark] [class~=text-slate-400]{color:#94a3b8}.ssh-app[data-theme=dark] [class~=text-slate-300]{color:#cbd5e1}.ssh-app[data-theme=dark] [class~=border-slate-100],.ssh-app[data-theme=dark] [class~=border-slate-200],.ssh-app[data-theme=dark] [class~=divide-slate-200]{border-color:#1e293b}.ssh-app[data-theme=dark] [class~=border-slate-300],.ssh-app[data-theme=dark] [class~=border-white\/80]{border-color:#334155}.ssh-app[data-theme=dark] [class~=ring-slate-100],.ssh-app[data-theme=dark] [class~=ring-slate-200],.ssh-app[data-theme=dark] [class~=ring-white]{--tw-ring-color:#334155}.ssh-app[data-theme=dark] [class~=bg-teal-50],.ssh-app[data-theme=dark] [class~=bg-teal-100]{background-color:#0d948829}.ssh-app[data-theme=dark] [class~=text-teal-700],.ssh-app[data-theme=dark] [class~=text-teal-800],.ssh-app[data-theme=dark] [class~=text-teal-950]{color:#5eead4}.ssh-app[data-theme=dark] [class~=border-teal-200],.ssh-app[data-theme=dark] [class~=border-teal-300]{border-color:#115e59}.ssh-app[data-theme=dark] [class~=ring-teal-100],.ssh-app[data-theme=dark] [class~=ring-teal-200]{--tw-ring-color:#134e4a}.ssh-app[data-theme=dark] [class~=bg-sky-50],.ssh-app[data-theme=dark] [class~=bg-sky-50\/60]{background-color:#0ea5e91f}.ssh-app[data-theme=dark] [class~=text-sky-700],.ssh-app[data-theme=dark] [class~=text-sky-800],.ssh-app[data-theme=dark] [class~=text-sky-950]{color:#7dd3fc}.ssh-app[data-theme=dark] [class~=border-sky-200]{border-color:#075985}.ssh-app[data-theme=dark] [class~=ring-sky-100],.ssh-app[data-theme=dark] [class~=ring-sky-200]{--tw-ring-color:#0c4a6e}.ssh-app[data-theme=dark] [class~=bg-emerald-50],.ssh-app[data-theme=dark] [class~=bg-emerald-100]{background-color:#10b98121}.ssh-app[data-theme=dark] [class~=bg-emerald-50\/70]{background-color:#10b9811a}.ssh-app[data-theme=dark] [class~=text-emerald-700],.ssh-app[data-theme=dark] [class~=text-emerald-800],.ssh-app[data-theme=dark] [class~=text-emerald-900],.ssh-app[data-theme=dark] [class~=text-emerald-950]{color:#6ee7b7}.ssh-app[data-theme=dark] [class~=border-emerald-200]{border-color:#065f46}.ssh-app[data-theme=dark] [class~=ring-emerald-100],.ssh-app[data-theme=dark] [class~=ring-emerald-200]{--tw-ring-color:#064e3b}.ssh-app[data-theme=dark] [class~=bg-amber-50],.ssh-app[data-theme=dark] [class~=bg-amber-50\/60],.ssh-app[data-theme=dark] [class~=bg-amber-100],.ssh-app[data-theme=dark] [class~=bg-amber-100\/70]{background-color:#f59e0b21}.ssh-app[data-theme=dark] [class~=bg-amber-50\/70]{background-color:#f59e0b1a}.ssh-app[data-theme=dark] [class~=text-amber-700],.ssh-app[data-theme=dark] [class~=text-amber-800],.ssh-app[data-theme=dark] [class~=text-amber-900],.ssh-app[data-theme=dark] [class~=text-amber-900\/80],.ssh-app[data-theme=dark] [class~=text-amber-950]{color:#fcd34d}.ssh-app[data-theme=dark] [class~=border-amber-200],.ssh-app[data-theme=dark] [class~=border-amber-300]{border-color:#92400e}.ssh-app[data-theme=dark] [class~=ring-amber-100],.ssh-app[data-theme=dark] [class~=ring-amber-200]{--tw-ring-color:#78350f}.ssh-app[data-theme=dark] [class~=bg-red-50],.ssh-app[data-theme=dark] [class~=bg-red-100]{background-color:#ef44441f}.ssh-app[data-theme=dark] [class~=text-red-700],.ssh-app[data-theme=dark] [class~=text-red-800],.ssh-app[data-theme=dark] [class~=text-red-900],.ssh-app[data-theme=dark] [class~=text-red-950]{color:#fca5a5}.ssh-app[data-theme=dark] [class~=border-red-200],.ssh-app[data-theme=dark] [class~=border-red-300]{border-color:#991b1b}.ssh-app[data-theme=dark] [class~=ring-red-100],.ssh-app[data-theme=dark] [class~=ring-red-200]{--tw-ring-color:#7f1d1d}.ssh-app[data-theme=dark] [class~=bg-rose-50]{background-color:#f43f5e1f}.ssh-app[data-theme=dark] [class~=bg-rose-50\/70]{background-color:#f43f5e1a}.ssh-app[data-theme=dark] [class~=text-rose-600],.ssh-app[data-theme=dark] [class~=text-rose-700],.ssh-app[data-theme=dark] [class~=text-rose-800],.ssh-app[data-theme=dark] [class~=text-rose-900],.ssh-app[data-theme=dark] [class~=text-rose-950]{color:#fda4af}.ssh-app[data-theme=dark] [class~=border-rose-200],.ssh-app[data-theme=dark] [class~=border-rose-300]{border-color:#9f1239}.ssh-app[data-theme=dark] [class~=ring-rose-200]{--tw-ring-color:#881337}.ssh-app[data-theme=dark] [class~=bg-blue-50]{background-color:#3b82f61f}.ssh-app[data-theme=dark] [class~=text-blue-700]{color:#93c5fd}.ssh-app[data-theme=dark] [class~=ring-blue-200]{--tw-ring-color:#1e3a8a}.ssh-app[data-theme=dark] .hover\:bg-slate-50:hover{background-color:#1e293b}.ssh-app[data-theme=dark] .hover\:bg-rose-50:hover{background-color:#f43f5e2e}.ssh-app[data-theme=dark] .hover\:text-slate-950:hover{color:#f8fafc}.ssh-app[data-theme=dark] .hover\:text-slate-700:hover{color:#e2e8f0}.ssh-app[data-theme=dark] .hover\:text-rose-800:hover{color:#fecdd3}.ssh-app[data-theme=dark] .hover\:border-slate-300:hover{border-color:#475569}.ssh-app[data-theme=dark] .ssh-product-header-action{color:#cbd5e1;background-color:#0f172a;border-color:#334155!important}.ssh-app[data-theme=dark] .ssh-product-header-action:hover{color:#5eead4;border-color:#0f766e!important}.ssh-app[data-theme=dark] .ssh-product-header-action:focus-visible{--tw-ring-color:#134e4a}.ssh-initial-setup-probe{color:#0f172a;background:#f8fafc;place-items:center;min-height:100dvh;padding:1.5rem;display:grid}.ssh-initial-setup-probe-card{text-align:center;background:#fff;border:1px solid #e2e8f0;border-radius:1.25rem;justify-items:center;gap:.7rem;width:min(100%,28rem);padding:2.25rem;display:grid;box-shadow:0 20px 55px #0f172a14}.ssh-initial-setup-probe-card>svg{color:#0f766e;width:2rem;height:2rem}.ssh-initial-setup-probe-card strong{font-size:1rem;font-weight:900}.ssh-initial-setup-probe-card span{color:#64748b;font-size:.84rem;line-height:1.55}.ssh-initial-setup-probe-card button{color:#fff;cursor:pointer;min-height:2.75rem;font:inherit;background:#0f766e;border:0;border-radius:.8rem;margin-top:.4rem;padding:0 1rem;font-size:.84rem;font-weight:900}.smartsafehub-login-shadow-root{background:#f8fafc;min-height:100dvh}.ssh-login-page,.ssh-login-page *,.ssh-login-page :before,.ssh-login-page :after{box-sizing:border-box}.ssh-login-page{--ssh-login-bg:#f8fafc;--ssh-login-surface:#fff;--ssh-login-surface-soft:#f8fafc;--ssh-login-border:#e2e8f0;--ssh-login-border-strong:#cbd5e1;--ssh-login-text:#0f172a;--ssh-login-muted:#64748b;--ssh-login-muted-soft:#94a3b8;--ssh-login-accent:#0f766e;--ssh-login-accent-hover:#115e59;--ssh-login-accent-soft:#f0fdfa;--ssh-login-accent-ring:#14b8a624;--ssh-login-card-shadow:0 24px 70px #0f172a14, 0 2px 10px #0f172a0a;background:var(--ssh-login-bg);min-height:100dvh;color:var(--ssh-login-text);grid-template-columns:minmax(0,1.08fr) minmax(420px,.92fr);display:grid}.ssh-login-page[data-theme=dark]{--ssh-login-bg:#020617;--ssh-login-surface:#0f172a;--ssh-login-surface-soft:#111827;--ssh-login-border:#1e293b;--ssh-login-border-strong:#334155;--ssh-login-text:#f8fafc;--ssh-login-muted:#94a3b8;--ssh-login-muted-soft:#64748b;--ssh-login-accent:#2dd4bf;--ssh-login-accent-hover:#5eead4;--ssh-login-accent-soft:#14b8a61a;--ssh-login-accent-ring:#2dd4bf2e;--ssh-login-card-shadow:0 28px 72px #0000003d, 0 0 0 1px #94a3b80a}.ssh-login-toast{top:max(1rem, env(safe-area-inset-top));z-index:20;color:#115e59;background:#f0fdfa;border:1px solid #99f6e4;border-radius:1rem;align-items:center;gap:.75rem;width:min(100% - 2rem,34rem);padding:.9rem 1rem;font-size:.88rem;font-weight:800;line-height:1.45;display:flex;position:fixed;left:50%;transform:translate(-50%);box-shadow:0 18px 45px #0f172a29}.ssh-login-toast svg{flex:none;width:1.25rem;height:1.25rem}.ssh-login-page[data-theme=dark] .ssh-login-toast{color:#99f6e4;background:#0f2f2e;border-color:#2dd4bf57;box-shadow:0 18px 45px #00000052}.ssh-login-brand{color:#f8fafc;background:radial-gradient(circle at 16% 14%,#2dd4bf29,#0000 28rem),linear-gradient(145deg,#020617 0%,#0f172a 56%,#042f2e 140%);justify-content:center;align-items:center;min-height:100dvh;padding:clamp(3rem,7vw,7rem);display:flex;position:relative;overflow:hidden}.ssh-login-brand:after{content:"";pointer-events:none;background-image:linear-gradient(#94a3b80e 1px,#0000 1px),linear-gradient(90deg,#94a3b80e 1px,#0000 1px);background-size:44px 44px;position:absolute;top:0;bottom:0;left:0;right:0;-webkit-mask-image:linear-gradient(to bottom right,#000,#0000 78%);mask-image:linear-gradient(to bottom right,#000,#0000 78%)}.ssh-login-brand-orb{filter:blur(1px);pointer-events:none;border-radius:9999px;position:absolute}.ssh-login-brand-orb-one{border:1px solid #2dd4bf29;width:30rem;height:30rem;top:-10rem;right:-8rem;box-shadow:inset 0 0 6rem #14b8a60d}.ssh-login-brand-orb-two{border:1px solid #5eead41c;width:36rem;height:36rem;bottom:-13rem;left:-10rem}.ssh-login-brand-content{z-index:1;width:min(100%,680px);position:relative}.ssh-login-brand-mark,.ssh-login-mobile-mark{color:#5eead4;background:#14b8a621;place-items:center;display:grid;box-shadow:inset 0 0 0 1px #5eead42e}.ssh-login-brand-mark{border-radius:1.5rem;width:4.5rem;height:4.5rem;margin-bottom:2rem}.ssh-login-brand-mark svg{width:2.25rem;height:2.25rem}.ssh-login-eyebrow,.ssh-login-kicker{color:#5eead4;letter-spacing:.22em;text-transform:uppercase;margin:0;font-size:.72rem;font-weight:900}.ssh-login-brand h1{letter-spacing:-.055em;margin:.7rem 0 0;font-size:clamp(2.7rem,5vw,5rem);font-weight:900;line-height:.98}.ssh-password-setup-brand-title-line{display:block}.ssh-login-brand-description{color:#cbd5e1;max-width:39rem;margin:1.6rem 0 0;font-size:clamp(1rem,1.5vw,1.16rem);line-height:1.85}.ssh-login-features{gap:.85rem;max-width:40rem;margin-top:3rem;display:grid}.ssh-login-feature{-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);background:#0f172a75;border:1px solid #94a3b81c;border-radius:1rem;align-items:center;gap:1rem;padding:.95rem 1rem;display:flex}.ssh-login-feature-icon{color:#5eead4;background:#14b8a61c;border-radius:.8rem;flex:none;place-items:center;width:2.65rem;height:2.65rem;display:grid}.ssh-login-feature-icon svg{width:1.25rem;height:1.25rem}.ssh-login-feature-copy{gap:.15rem;min-width:0;display:grid}.ssh-login-feature-copy strong{color:#f8fafc;font-size:.92rem;font-weight:800}.ssh-login-feature-copy>span{color:#94a3b8;font-size:.8rem;line-height:1.45}.ssh-login-panel{background:var(--ssh-login-bg);justify-content:center;align-items:center;min-height:100dvh;padding:clamp(2rem,5vw,5rem);transition:background-color .18s;display:flex;position:relative}.ssh-login-theme-toggle{top:max(1.25rem, env(safe-area-inset-top));right:max(1.25rem, env(safe-area-inset-right));z-index:2;border:1px solid var(--ssh-login-border);background:var(--ssh-login-surface);width:2.75rem;height:2.75rem;color:var(--ssh-login-muted);cursor:pointer;border-radius:.9rem;place-items:center;transition:border-color .15s,background-color .15s,color .15s,transform .15s;display:grid;position:absolute;box-shadow:0 5px 16px #0f172a0f}.ssh-login-theme-toggle svg{width:1.15rem;height:1.15rem}.ssh-login-theme-toggle:hover{border-color:var(--ssh-login-border-strong);color:var(--ssh-login-accent);transform:translateY(-1px)}.ssh-login-panel-inner{flex-direction:column;justify-content:center;width:min(100%,480px);min-height:min(40rem,100dvh - 4rem);display:flex}.ssh-login-card{border:1px solid var(--ssh-login-border);background:var(--ssh-login-surface);width:100%;box-shadow:var(--ssh-login-card-shadow);border-radius:1.5rem;padding:clamp(1.6rem,4vw,2.6rem);transition:background-color .18s,border-color .18s}.ssh-login-card-heading{align-items:flex-start;gap:1rem;display:flex}.ssh-login-card-heading>div{min-width:0}.ssh-login-mobile-mark{color:#fff;background:#0f766e;border-radius:1.1rem;flex:none;width:3.5rem;height:3.5rem;display:none;box-shadow:0 12px 28px #0f766e38}.ssh-login-mobile-mark svg{width:1.8rem;height:1.8rem}.ssh-login-kicker{color:var(--ssh-login-accent);letter-spacing:.18em}.ssh-login-card h2{color:var(--ssh-login-text);letter-spacing:-.035em;margin:.65rem 0 0;font-size:clamp(1.8rem,4vw,2.25rem);font-weight:900;line-height:1.15}.ssh-login-subtitle{color:var(--ssh-login-muted);margin:.7rem 0 0;font-size:.94rem;line-height:1.6}.ssh-login-alert{color:#9f1239;background:#fff1f2;border:1px solid #fecdd3;border-radius:.85rem;margin-top:1.3rem;padding:.85rem 1rem;font-size:.82rem;font-weight:700;line-height:1.55}.ssh-login-page[data-theme=dark] .ssh-login-alert{color:#fecdd3;background:#8813372e;border-color:#f43f5e59}.ssh-login-form{gap:1.1rem;margin-top:1.6rem;display:grid}.ssh-login-field{gap:.5rem;display:grid}.ssh-login-label{color:var(--ssh-login-text);font-size:.82rem;font-weight:800}.ssh-login-input-wrap{border:2px solid var(--ssh-login-border-strong);background:var(--ssh-login-surface-soft);min-height:3.3rem;color:var(--ssh-login-muted);border-radius:.9rem;align-items:center;transition:border-color .15s,background-color .15s,box-shadow .15s;display:flex;overflow:hidden;box-shadow:inset 0 1px 2px #0f172a09}.ssh-login-input-wrap:focus-within{border-color:var(--ssh-login-accent);background:var(--ssh-login-surface);box-shadow:0 0 0 4px var(--ssh-login-accent-ring)}.ssh-login-field-icon{background:var(--ssh-login-accent-soft);width:2.15rem;height:2.15rem;color:var(--ssh-login-accent);border-radius:.65rem;flex:none;place-items:center;margin-left:.55rem;transition:background-color .15s,color .15s,box-shadow .15s;display:grid}.ssh-login-field-icon svg{width:1.05rem;height:1.05rem}.ssh-login-input-wrap:focus-within .ssh-login-field-icon{box-shadow:inset 0 0 0 1px var(--ssh-login-accent-ring)}.ssh-login-input-wrap input{width:100%;min-width:0;height:3.15rem;color:var(--ssh-login-text);font:inherit;background:0 0;border:0;outline:0;padding:0 .8rem;font-size:.94rem;font-weight:700}.ssh-login-input-wrap input::placeholder{color:var(--ssh-login-muted-soft);opacity:.82;font-weight:600}.ssh-login-input-wrap input:-webkit-autofill{-webkit-text-fill-color:var(--ssh-login-text);caret-color:var(--ssh-login-text);box-shadow:0 0 0 1000px var(--ssh-login-surface-soft) inset}.ssh-login-input-wrap input:-webkit-autofill:hover{-webkit-text-fill-color:var(--ssh-login-text);caret-color:var(--ssh-login-text);box-shadow:0 0 0 1000px var(--ssh-login-surface-soft) inset}.ssh-login-input-wrap input:-webkit-autofill:focus{-webkit-text-fill-color:var(--ssh-login-text);caret-color:var(--ssh-login-text);box-shadow:0 0 0 1000px var(--ssh-login-surface-soft) inset}.ssh-login-input-wrap:focus-within input:-webkit-autofill{box-shadow:0 0 0 1000px var(--ssh-login-surface) inset}.ssh-login-input-wrap input:disabled{cursor:wait;opacity:.68}.ssh-login-password-toggle{width:2.35rem;height:2.35rem;color:var(--ssh-login-muted);cursor:pointer;background:0 0;border:1px solid #0000;border-radius:.7rem;flex:none;place-items:center;margin-right:.45rem;transition:border-color .15s,background-color .15s,color .15s;display:grid}.ssh-login-password-toggle svg{width:1.05rem;height:1.05rem}.ssh-login-password-toggle:hover:not(:disabled){border-color:var(--ssh-login-border);background:var(--ssh-login-accent-soft);color:var(--ssh-login-accent)}.ssh-login-password-toggle:disabled{cursor:wait;opacity:.6}.ssh-login-theme-toggle:focus-visible{outline:3px solid var(--ssh-login-accent-ring);outline-offset:2px}.ssh-login-password-toggle:focus-visible{outline:3px solid var(--ssh-login-accent-ring);outline-offset:2px}.ssh-login-submit:focus-visible{outline:3px solid var(--ssh-login-accent-ring);outline-offset:2px}.ssh-login-fallback:focus-visible{outline:3px solid var(--ssh-login-accent-ring);outline-offset:2px}.ssh-login-password-hint{color:var(--ssh-login-muted);margin:-.55rem 0 -.15rem;font-size:.76rem;line-height:1.5}.ssh-password-requirements{gap:.4rem;margin:-.25rem 0 0;padding:0;list-style:none;display:grid}.ssh-password-requirement{color:var(--ssh-login-muted);align-items:center;gap:.5rem;font-size:.78rem;font-weight:700;display:flex}.ssh-password-requirement svg{width:1rem;height:1rem;color:var(--ssh-login-muted-soft);flex:none}.ssh-password-requirement.is-met,.ssh-password-requirement.is-met svg,.ssh-password-match.is-met{color:var(--ssh-login-accent)}.ssh-password-match{color:#be123c;margin:-.5rem 0 -.1rem;font-size:.78rem;font-weight:800}.ssh-password-setup-summary{background:#14b8a617;border:1px solid #5eead42e;border-radius:1rem;align-items:flex-start;gap:.85rem;max-width:40rem;margin-top:2.4rem;padding:1rem 1.1rem;display:flex}.ssh-password-setup-summary>svg{color:#5eead4;flex:none;width:1.3rem;height:1.3rem;margin-top:.1rem}.ssh-password-setup-summary>div{gap:.25rem;display:grid}.ssh-password-setup-summary strong{color:#f8fafc;font-size:.9rem}.ssh-password-setup-summary span{color:#94a3b8;font-size:.8rem;line-height:1.5}.ssh-login-caps{color:#b45309;margin:-.45rem 0 -.15rem;font-size:.78rem;font-weight:800}.ssh-login-page[data-theme=dark] .ssh-login-caps{color:#fbbf24}.ssh-login-submit{color:#fff;cursor:pointer;min-height:3.3rem;font:inherit;background:#0f766e;border:0;border-radius:.9rem;justify-content:center;align-items:center;gap:.65rem;margin-top:.25rem;font-size:.94rem;font-weight:900;transition:background-color .15s,transform .15s,box-shadow .15s;display:inline-flex;box-shadow:0 12px 26px #0f766e33}.ssh-login-submit:hover:not(:disabled){background:#115e59;transform:translateY(-1px);box-shadow:0 16px 30px #0f766e40}.ssh-login-submit:disabled{cursor:wait;opacity:.72}.ssh-login-submit-spinner,.ssh-login-probe-spinner{flex:none;animation:.75s linear infinite ssh-login-spin;display:inline-block}.ssh-login-submit-spinner{color:#fff;width:1rem;height:1rem}.ssh-login-security-note{color:var(--ssh-login-muted);align-items:flex-start;gap:.55rem;margin-top:1.2rem;font-size:.76rem;line-height:1.5;display:flex}.ssh-login-security-note svg{width:1rem;height:1rem;color:var(--ssh-login-accent);flex:none;margin-top:.06rem}.ssh-login-fallback{border:1px solid var(--ssh-login-border-strong);background:var(--ssh-login-surface);min-height:2.7rem;color:var(--ssh-login-muted);border-radius:.8rem;justify-content:center;align-items:center;margin-top:.9rem;font-size:.8rem;font-weight:800;text-decoration:none;display:flex}.ssh-login-fallback:hover{border-color:var(--ssh-login-muted-soft);background:var(--ssh-login-surface-soft);color:var(--ssh-login-text)}.ssh-login-probe{color:var(--ssh-login-muted);text-align:center;justify-items:center;gap:.6rem;padding:2.5rem;display:grid}.ssh-login-probe-spinner{width:2rem;height:2rem;color:var(--ssh-login-accent);margin-bottom:.4rem}.ssh-login-probe strong{color:var(--ssh-login-text);font-size:1rem}.ssh-login-probe>span:last-child{font-size:.82rem}.ssh-login-footer{color:var(--ssh-login-muted-soft);flex-wrap:wrap;justify-content:center;gap:.4rem;margin-top:1.5rem;font-size:.72rem;display:flex}@keyframes ssh-login-spin{to{transform:rotate(360deg)}}@media (max-width:980px){.ssh-login-page{grid-template-columns:1fr}.ssh-login-brand{display:none}.ssh-login-panel{padding:max(1.25rem, env(safe-area-inset-top)) max(1.25rem, env(safe-area-inset-right)) max(1.25rem, env(safe-area-inset-bottom)) max(1.25rem, env(safe-area-inset-left))}.ssh-login-panel-inner{min-height:calc(100dvh - 2.5rem)}.ssh-login-mobile-mark{display:grid}}@media (max-width:520px){.ssh-login-panel{background:var(--ssh-login-surface);align-items:stretch;padding:0}.ssh-login-theme-toggle{top:max(1rem, env(safe-area-inset-top));right:max(1rem, env(safe-area-inset-right));box-shadow:none}.ssh-login-panel-inner{width:100%;min-height:100dvh}.ssh-login-card{min-height:100dvh;padding:max(4.75rem, calc(env(safe-area-inset-top) + 4.25rem)) max(1.25rem, env(safe-area-inset-right)) max(2rem, env(safe-area-inset-bottom)) max(1.25rem, env(safe-area-inset-left));box-shadow:none;border:0;border-radius:0;flex-direction:column;justify-content:center;display:flex}.ssh-login-card-heading{display:block}.ssh-login-mobile-mark{margin-bottom:1.25rem}.ssh-login-input-wrap input{font-size:16px}.ssh-login-footer{display:none}.ssh-login-probe{align-content:center;min-height:100dvh}}@media (prefers-reduced-motion:reduce){.ssh-login-panel,.ssh-login-card,.ssh-login-theme-toggle,.ssh-login-submit,.ssh-login-input-wrap{transition:none}.ssh-login-submit-spinner,.ssh-login-probe-spinner{animation-duration:1.4s}}.ssh-safeshield-plan-badge{isolation:isolate;letter-spacing:.085em;white-space:nowrap;border:1px solid #0000;border-radius:999px;align-items:center;gap:.5rem;min-height:2rem;padding:.3rem .82rem .3rem .36rem;font-size:.75rem;font-weight:950;line-height:1;display:inline-flex;position:relative;overflow:hidden}.ssh-safeshield-plan-badge[data-compact=true]{min-height:1.72rem;padding:.22rem .66rem .22rem .3rem;font-size:.69rem}.ssh-safeshield-plan-badge-mark{z-index:2;border-radius:999px;flex:none;place-items:center;width:1.2rem;height:1.2rem;font-size:.76rem;line-height:1;display:grid;position:relative}.ssh-safeshield-plan-badge[data-compact=true] .ssh-safeshield-plan-badge-mark{width:1.02rem;height:1.02rem;font-size:.66rem}.ssh-safeshield-plan-badge[data-tier=free]{color:#475569;background:linear-gradient(135deg,#fff 0%,#f1f5f9 100%);border-color:#cbd5e1;box-shadow:inset 0 1px #ffffffe6}.ssh-safeshield-plan-badge[data-tier=free] .ssh-safeshield-plan-badge-mark{color:#475569;background:#e2e8f0}.ssh-safeshield-plan-badge[data-tier=pro]{color:#f0fdfa;background:linear-gradient(135deg,#115e59 0%,#0f766e 62%,#0d9488 100%);border-color:#0f766e;box-shadow:0 1px 2px #0f766e29,inset 0 1px #ffffff24}.ssh-safeshield-plan-badge[data-tier=pro] .ssh-safeshield-plan-badge-mark{color:#ccfbf1;box-shadow:none;background:#f0fdfa1f;border:1px solid #99f6e475}.ssh-safeshield-plan-badge[data-tier=ultimate]{color:#fffbeb;background:linear-gradient(135deg,#78350f 0%,#92400e 58%,#b45309 100%);border-color:#b45309;box-shadow:0 1px 2px #78350f2e,inset 0 1px #ffffff1f}.ssh-safeshield-plan-badge[data-tier=ultimate] .ssh-safeshield-plan-badge-mark{color:#fde68a;box-shadow:none;background:#fffbeb1f;border:1px solid #fde68a75}.ssh-safeshield-plan-badge[data-tier=paid]{color:#f0f9ff;background:linear-gradient(135deg,#0c4a6e 0%,#075985 58%,#0369a1 100%);border-color:#0369a1;box-shadow:0 1px 2px #0369a129,inset 0 1px #ffffff1f}.ssh-safeshield-plan-badge[data-tier=paid] .ssh-safeshield-plan-badge-mark{color:#bae6fd;box-shadow:none;background:#f0f9ff1f;border:1px solid #bae6fd75}.ssh-safeshield-plan-caption{color:#64748b;white-space:nowrap;align-items:center;gap:.34rem;font-size:.68rem;font-weight:850;display:inline-flex}.ssh-app[data-theme=dark] .ssh-safeshield-plan-badge[data-tier=free]{color:#cbd5e1;background:#1e293b;border-color:#475569}.ssh-app[data-theme=dark] .ssh-safeshield-plan-badge[data-tier=free] .ssh-safeshield-plan-badge-mark{color:#e2e8f0;background:#475569}.ssh-app[data-theme=dark] .ssh-safeshield-plan-badge[data-tier=pro]{color:#99f6e4;background:linear-gradient(135deg,#042f2e 0%,#134e4a 100%);border-color:#0f766e;box-shadow:inset 0 1px #99f6e414}.ssh-app[data-theme=dark] .ssh-safeshield-plan-badge[data-tier=pro] .ssh-safeshield-plan-badge-mark{color:#99f6e4;background:#0d94881f;border-color:#2dd4bf61}.ssh-app[data-theme=dark] .ssh-safeshield-plan-badge[data-tier=ultimate]{color:#fde68a;background:linear-gradient(135deg,#451a03 0%,#78350f 100%);border-color:#b45309;box-shadow:inset 0 1px #fde68a14}.ssh-app[data-theme=dark] .ssh-safeshield-plan-badge[data-tier=ultimate] .ssh-safeshield-plan-badge-mark{color:#fde68a;background:#92400e29;border-color:#d9770675}.ssh-app[data-theme=dark] .ssh-safeshield-plan-badge[data-tier=paid]{color:#bae6fd;background:linear-gradient(135deg,#082f49 0%,#0c4a6e 100%);border-color:#0369a1;box-shadow:inset 0 1px #bae6fd14}.ssh-app[data-theme=dark] .ssh-safeshield-plan-badge[data-tier=paid] .ssh-safeshield-plan-badge-mark{color:#bae6fd;background:#0369a124;border-color:#0ea5e95c}.ssh-app[data-theme=dark] .ssh-safeshield-plan-caption{color:#94a3b8}.ssh-safeshield-upgrade-card{background:linear-gradient(120deg,#f0fdfa 0%,#ecfeff 58%,#fff7ed 100%);border:1px solid #99f6e4;border-radius:1rem;align-items:center;gap:.85rem;margin-top:.75rem;padding:.9rem 1rem;display:flex;box-shadow:0 10px 24px #0f766e14}.ssh-safeshield-upgrade-mark{color:#fff;background:linear-gradient(135deg,#0f766e,#0891b2);border-radius:.8rem;flex:none;place-items:center;width:2.5rem;height:2.5rem;font-size:1.05rem;display:grid;box-shadow:0 8px 18px #0d948838}.ssh-safeshield-upgrade-copy{flex:1;gap:.2rem;min-width:0;display:grid}.ssh-safeshield-upgrade-meta{color:#0f766e;letter-spacing:.12em;text-transform:uppercase;flex-wrap:wrap;align-items:center;gap:.45rem;font-size:.62rem;font-weight:900;display:flex}.ssh-safeshield-upgrade-soon{letter-spacing:.06em;background:#0f766e1a;border-radius:999px;padding:.18rem .45rem}.ssh-safeshield-upgrade-copy strong{color:#0f172a;font-size:.86rem;line-height:1.35rem}.ssh-safeshield-upgrade-copy>span{color:#64748b;font-size:.74rem;font-weight:600;line-height:1.15rem}.ssh-safeshield-upgrade-action{color:#fff;background:#0f766e;border:1px solid #0f766e;border-radius:.8rem;flex:none;justify-content:center;align-items:center;gap:.4rem;min-height:2.45rem;padding:.55rem .8rem;font-size:.74rem;font-weight:900;text-decoration:none;transition:background-color .15s,border-color .15s,transform .15s;display:inline-flex}.ssh-safeshield-upgrade-action:hover{background:#115e59;border-color:#115e59;transform:translateY(-1px)}.ssh-safeshield-upgrade-action:focus-visible{outline-offset:2px;outline:3px solid #14b8a63d}.ssh-app[data-theme=dark] .ssh-safeshield-upgrade-card{background:linear-gradient(120deg,#0d948821 0%,#0f172afa 58%,#92400e1f 100%);border-color:#134e4a;box-shadow:0 12px 28px #0000002e}.ssh-app[data-theme=dark] .ssh-safeshield-upgrade-mark{background:linear-gradient(135deg,#0d9488,#0891b2);box-shadow:0 8px 20px #2dd4bf21}.ssh-app[data-theme=dark] .ssh-safeshield-upgrade-meta{color:#5eead4}.ssh-app[data-theme=dark] .ssh-safeshield-upgrade-soon{background:#2dd4bf1f}.ssh-app[data-theme=dark] .ssh-safeshield-upgrade-copy strong{color:#f8fafc}.ssh-app[data-theme=dark] .ssh-safeshield-upgrade-copy>span{color:#94a3b8}.ssh-app[data-theme=dark] .ssh-safeshield-upgrade-action{color:#ccfbf1;background:#0d948838;border-color:#2dd4bf}.ssh-app[data-theme=dark] .ssh-safeshield-upgrade-action:hover{background:#0d948852;border-color:#5eead4}@media (max-width:640px){.ssh-safeshield-upgrade-card{flex-wrap:wrap;align-items:flex-start}.ssh-safeshield-upgrade-action{justify-content:center;width:100%}}.ssh-safeshield-refresh{background:#f0fdfab3;border:1px solid #ccfbf1;border-radius:1rem;align-items:center;gap:.875rem;width:27rem;max-width:100%;margin-top:1rem;padding:.75rem .875rem;display:flex}.ssh-safeshield-refresh[data-state=error]{background:#fef2f2c7;border-color:#fecaca}.ssh-safeshield-refresh-copy{min-width:0}.ssh-safeshield-refresh-kicker,.ssh-safeshield-refresh-title,.ssh-safeshield-refresh-description,.ssh-safeshield-refresh-error-code{margin:0}.ssh-safeshield-refresh-kicker{color:#0f766e;letter-spacing:.14em;text-transform:uppercase;font-size:.66rem;font-weight:900}.ssh-safeshield-refresh[data-state=error] .ssh-safeshield-refresh-kicker{color:#b91c1c}.ssh-safeshield-refresh-title{color:#0f172a;margin-top:.25rem;font-size:.875rem;font-weight:800}.ssh-safeshield-refresh-description{color:#475569;margin-top:.25rem;font-size:.75rem;font-weight:600;line-height:1.25rem}.ssh-safeshield-refresh-error-code{overflow-wrap:anywhere;color:#64748b;margin-top:.375rem;font-size:.68rem;font-weight:600}.ssh-safeshield-refresh-error-code code{font:inherit}.ssh-safeshield-refresh-donut{color:#0d9488;place-items:center;width:3.35rem;min-width:3.35rem;height:3.35rem;display:grid;position:relative}.ssh-safeshield-refresh-donut[data-state=error]{color:#dc2626}.ssh-safeshield-refresh-loader-svg{transform-origin:50%;width:100%;height:100%;animation:.82s linear infinite ssh-safeshield-refresh-loader-rotate;position:absolute;top:0;bottom:0;left:0;right:0;transform:rotate(-90deg)}.ssh-safeshield-refresh-loader-svg-error{animation:none}.ssh-safeshield-refresh-loader-track{color:#cbd5e1;stroke:#cbd5e1}.ssh-safeshield-refresh-loader-arc{color:#0f172a;stroke:#0f172a;filter:drop-shadow(0 0 .06rem #0f172a47)}.ssh-safeshield-refresh-donut[data-state=error] .ssh-safeshield-refresh-loader-track{color:#dc2626;stroke:#dc2626}.ssh-safeshield-refresh-donut-label{z-index:1;color:#0f766e;font-variant-numeric:tabular-nums;font-size:.68rem;font-weight:900;position:relative}.ssh-app[data-theme=dark] .ssh-safeshield-refresh{background:#0f766e1f;border-color:#134e4a}.ssh-app[data-theme=dark] .ssh-safeshield-refresh[data-state=error]{background:#7f1d1d2e;border-color:#7f1d1d}.ssh-app[data-theme=dark] .ssh-safeshield-refresh-title{color:#f8fafc}.ssh-app[data-theme=dark] .ssh-safeshield-refresh-description,.ssh-app[data-theme=dark] .ssh-safeshield-refresh-error-code{color:#94a3b8}.ssh-app[data-theme=dark] .ssh-safeshield-refresh-loader-track{color:#334155;stroke:#334155}.ssh-app[data-theme=dark] .ssh-safeshield-refresh-loader-arc{color:#fff;stroke:#fff;filter:drop-shadow(0 0 .08rem #ffffff8c)}.ssh-app[data-theme=dark] .ssh-safeshield-refresh-donut-label{color:#5eead4}.ssh-app[data-theme=dark] .ssh-safeshield-refresh-donut[data-state=error] .ssh-safeshield-refresh-loader-track{color:#ef4444;stroke:#ef4444}@keyframes ssh-safeshield-refresh-loader-rotate{to{transform:rotate(270deg)}}@media (prefers-reduced-motion:reduce){.ssh-safeshield-refresh-loader-svg{animation:none}}.ssh-dashboard-account-banner{background:#f0fdfa;border-color:#99e8df}.ssh-dashboard-account-banner-icon{color:#0f766e;background:#ccfbf1}.ssh-dashboard-account-banner-title{color:#0f172a}.ssh-dashboard-account-banner-description{color:#475569}.ssh-dashboard-account-banner-badge{color:#92400e;background:#fef3c7}.ssh-dashboard-account-banner-action{color:#fff;background:#0f766e}.ssh-dashboard-account-banner-action:hover{color:#fff;background:#115e59}.ssh-dashboard-account-banner-action:focus-visible{outline-color:#0d9488}.ssh-app[data-theme=dark] .ssh-dashboard-account-banner{background:#102927;border-color:#115e59}.ssh-app[data-theme=dark] .ssh-dashboard-account-banner-icon{color:#99f6e4;background:#134e4a}.ssh-app[data-theme=dark] .ssh-dashboard-account-banner-title{color:#f1f5f9}.ssh-app[data-theme=dark] .ssh-dashboard-account-banner-description{color:#cbd5e1}.ssh-app[data-theme=dark] .ssh-dashboard-account-banner-badge{color:#fde68a;background:#78350f}.ssh-app[data-theme=dark] .ssh-dashboard-account-banner-action{color:#fff;background:#0f766e}.ssh-app[data-theme=dark] .ssh-dashboard-account-banner-action:hover{background:#0d9488}.ssh-account-nav-dot{width:10px;height:10px;box-shadow:0 0 0 2px var(--ssh-account-dot-ring,#fff);background-color:#d97706;border-radius:50%;flex:none;margin-left:auto;display:block}.ssh-account-nav-dot-collapsed{margin-left:0;position:absolute;top:5px;right:7px}.ssh-app[data-theme=dark] .ssh-account-nav-dot{--ssh-account-dot-ring:#0f172a;background-color:#fbbf24}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-ease{syntax:"*";inherits:false}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.5}}@keyframes bounce{0%,to{animation-timing-function:cubic-bezier(.8,0,1,1);transform:translateY(-25%)}50%{animation-timing-function:cubic-bezier(0,0,.2,1);transform:none}} +@layer properties{:host,*,:before,:after{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial}::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial}@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-outline-style:solid;--tw-ease:initial}::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-outline-style:solid;--tw-ease:initial}}}@layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-50:#fef2f2;--color-red-100:#ffe2e2;--color-red-200:#ffcaca;--color-red-300:#ffa3a3;--color-red-500:#fb2c36;--color-red-700:#bf000f;--color-red-800:#9f0712;--color-red-900:#82181a;--color-red-950:#460809;--color-amber-50:#fffbeb;--color-amber-100:#fef3c6;--color-amber-200:#fee685;--color-amber-300:#ffd236;--color-amber-500:#f99c00;--color-amber-600:#dd7400;--color-amber-700:#b75000;--color-amber-800:#953d00;--color-amber-900:#7b3306;--color-amber-950:#461901;--color-emerald-50:#ecfdf5;--color-emerald-100:#d0fae5;--color-emerald-200:#a4f4cf;--color-emerald-500:#00bb7f;--color-emerald-700:#007956;--color-emerald-800:#005f46;--color-emerald-900:#004e3b;--color-emerald-950:#002c22;--color-teal-50:#f0fdfa;--color-teal-100:#cbfbf1;--color-teal-200:#96f7e4;--color-teal-300:#46ecd5;--color-teal-400:#00d3bd;--color-teal-500:#00baa7;--color-teal-600:#009588;--color-teal-700:#00776e;--color-teal-800:#005f5a;--color-teal-900:#0b4f4a;--color-teal-950:#022f2e;--color-sky-50:#f0f9ff;--color-sky-100:#dff2fe;--color-sky-200:#b8e6fe;--color-sky-600:#0084cc;--color-sky-700:#0069a4;--color-sky-800:#005986;--color-sky-950:#052f4a;--color-blue-50:#eff6ff;--color-blue-200:#bedbff;--color-blue-700:#1447e6;--color-rose-50:#fff1f2;--color-rose-100:#ffe4e6;--color-rose-200:#ffccd3;--color-rose-300:#ffa2ae;--color-rose-600:#e70044;--color-rose-700:#c20039;--color-rose-800:#a30037;--color-rose-900:#8b0836;--color-rose-950:#4d0218;--color-slate-50:#f8fafc;--color-slate-100:#f1f5f9;--color-slate-200:#e2e8f0;--color-slate-300:#cad5e2;--color-slate-400:#90a1b9;--color-slate-500:#62748e;--color-slate-600:#45556c;--color-slate-700:#314158;--color-slate-800:#1d293d;--color-slate-900:#0f172b;--color-slate-950:#020618;--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-sm:24rem;--container-md:28rem;--container-lg:32rem;--container-xl:36rem;--container-3xl:48rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25 / 1.875);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--font-weight-extrabold:800;--font-weight-black:900;--tracking-tight:-.025em;--tracking-wide:.025em;--leading-relaxed:1.625;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--radius-2xl:1rem;--ease-out:cubic-bezier(0, 0, .2, 1);--animate-spin:spin 1s linear infinite;--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--animate-bounce:bounce 1s infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}@supports (color:color(display-p3 0 0 0)){:root,:host{--color-red-50:color(display-p3 .988669 .951204 .950419);--color-red-100:color(display-p3 .980386 .889727 .887779);--color-red-200:color(display-p3 .969562 .798149 .794299);--color-red-300:color(display-p3 .956922 .651886 .645122);--color-red-500:color(display-p3 .903738 .262579 .253307);--color-red-700:color(display-p3 .692737 .116232 .104679);--color-red-800:color(display-p3 .569606 .121069 .108493);--color-red-900:color(display-p3 .466906 .133198 .12139);--color-red-950:color(display-p3 .25064 .0550983 .0468818);--color-amber-50:color(display-p3 .997804 .985047 .926312);--color-amber-100:color(display-p3 .989391 .954583 .796328);--color-amber-200:color(display-p3 .979824 .904554 .57325);--color-amber-300:color(display-p3 .974327 .83063 .33298);--color-amber-500:color(display-p3 .93994 .620584 .0585367);--color-amber-600:color(display-p3 .827144 .467166 .0336006);--color-amber-700:color(display-p3 .67989 .32771 .0520514);--color-amber-800:color(display-p3 .547533 .256058 .0728326);--color-amber-900:color(display-p3 .445958 .216422 .0823321);--color-amber-950:color(display-p3 .252662 .109091 .026881);--color-emerald-50:color(display-p3 .936818 .989882 .961937);--color-emerald-100:color(display-p3 .848335 .975974 .901691);--color-emerald-200:color(display-p3 .713164 .947563 .822283);--color-emerald-500:color(display-p3 .267113 .726847 .508397);--color-emerald-700:color(display-p3 .164041 .470229 .343508);--color-emerald-800:color(display-p3 .135396 .371401 .277561);--color-emerald-900:color(display-p3 .117821 .302975 .234501);--color-emerald-950:color(display-p3 .0549113 .170434 .13484);--color-teal-50:color(display-p3 .951444 .990904 .98112);--color-teal-100:color(display-p3 .834533 .980328 .946428);--color-teal-200:color(display-p3 .675869 .957879 .896029);--color-teal-300:color(display-p3 .484989 .914726 .835079);--color-teal-400:color(display-p3 .322787 .822056 .744388);--color-teal-500:color(display-p3 .266569 .721518 .655462);--color-teal-600:color(display-p3 .207114 .579584 .53668);--color-teal-700:color(display-p3 .174094 .461318 .433866);--color-teal-800:color(display-p3 .1457 .367051 .350749);--color-teal-900:color(display-p3 .135344 .303314 .290629);--color-teal-950:color(display-p3 .0664925 .181482 .179465);--color-sky-50:color(display-p3 .946501 .975402 .998336);--color-sky-100:color(display-p3 .889195 .946622 .992447);--color-sky-200:color(display-p3 .758153 .897447 .986665);--color-sky-600:color(display-p3 .162505 .509376 .796082);--color-sky-700:color(display-p3 .130314 .404568 .640847);--color-sky-800:color(display-p3 .120719 .343083 .524405);--color-sky-950:color(display-p3 .0726429 .180783 .28188);--color-blue-50:color(display-p3 .941826 .963151 .995385);--color-blue-200:color(display-p3 .76688 .855207 .987483);--color-blue-700:color(display-p3 .1379 .274983 .867624);--color-rose-50:color(display-p3 .989671 .946597 .949215);--color-rose-100:color(display-p3 .982241 .897232 .902568);--color-rose-200:color(display-p3 .96875 .808776 .827317);--color-rose-300:color(display-p3 .96017 .647703 .683715);--color-rose-600:color(display-p3 .848792 .102011 .269259);--color-rose-700:color(display-p3 .711801 .0770816 .226777);--color-rose-800:color(display-p3 .591248 .0929065 .220097);--color-rose-900:color(display-p3 .498064 .104884 .214595);--color-rose-950:color(display-p3 .275407 .0406065 .0990695);--color-slate-50:color(display-p3 .974377 .979815 .986207);--color-slate-100:color(display-p3 .947345 .959969 .97483);--color-slate-200:color(display-p3 .890322 .909405 .939294);--color-slate-300:color(display-p3 .800294 .834432 .882803);--color-slate-400:color(display-p3 .577446 .629622 .716602);--color-slate-500:color(display-p3 .397645 .452653 .547642);--color-slate-600:color(display-p3 .283418 .332214 .416355);--color-slate-700:color(display-p3 .205992 .253487 .336039);--color-slate-800:color(display-p3 .121994 .158688 .232363);--color-slate-900:color(display-p3 .0639692 .0891152 .163036);--color-slate-950:color(display-p3 .0102437 .0227555 .0878731)}}@supports (color:lab(0% 0 0)){:root,:host{--color-red-50:lab(96.5005% 4.18508 1.52328);--color-red-100:lab(92.243% 10.2865 3.83865);--color-red-200:lab(86.017% 19.8815 7.75869);--color-red-300:lab(76.5514% 36.422 15.5335);--color-red-500:lab(55.4814% 75.0732 48.8528);--color-red-700:lab(40.4273% 67.2623 53.7441);--color-red-800:lab(33.7174% 55.8993 41.0293);--color-red-900:lab(28.5139% 44.5539 29.0463);--color-red-950:lab(13.003% 29.04 16.7519);--color-amber-50:lab(98.6252% -.635922 8.42309);--color-amber-100:lab(95.916% -1.21653 23.111);--color-amber-200:lab(91.7203% -.505269 49.9084);--color-amber-300:lab(86.4156% 6.13147 78.3961);--color-amber-500:lab(72.7183% 31.8672 97.9407);--color-amber-600:lab(60.3514% 40.5624 87.1228);--color-amber-700:lab(47.2709% 42.9082 69.2966);--color-amber-800:lab(37.8822% 37.1699 52.2718);--color-amber-900:lab(31.2288% 30.2627 40.0378);--color-amber-950:lab(15.8111% 20.9107 23.3752);--color-emerald-50:lab(97.8462% -6.94966 1.85487);--color-emerald-100:lab(94.9004% -17.0769 5.63836);--color-emerald-200:lab(90.2247% -31.039 9.47084);--color-emerald-500:lab(66.9756% -58.27 19.5419);--color-emerald-700:lab(44.4871% -41.0396 11.0361);--color-emerald-800:lab(35.3675% -33.1188 8.04002);--color-emerald-900:lab(28.8637% -26.9249 5.45986);--color-emerald-950:lab(15.0582% -17.9507 2.38369);--color-teal-50:lab(98.3189% -4.74921 -.111711);--color-teal-100:lab(95.1845% -17.4212 -.425422);--color-teal-200:lab(90.7612% -33.1343 -.542295);--color-teal-300:lab(84.8977% -48.1516 -1.3321);--color-teal-400:lab(76.0109% -53.3483 -2.27906);--color-teal-500:lab(67.3859% -49.0983 -2.63511);--color-teal-600:lab(55.0223% -41.0774 -3.90277);--color-teal-700:lab(44.4134% -33.1436 -4.22149);--color-teal-800:lab(35.5975% -26.6648 -4.34487);--color-teal-900:lab(29.506% -21.4706 -3.59886);--color-teal-950:lab(16.6371% -15.3183 -3.81732);--color-sky-50:lab(97.3623% -2.33802 -4.13098);--color-sky-100:lab(94.3709% -4.56053 -8.23453);--color-sky-200:lab(88.6983% -11.3978 -16.8488);--color-sky-600:lab(51.7754% -11.4712 -49.8349);--color-sky-700:lab(41.6013% -9.10804 -42.5647);--color-sky-800:lab(35.164% -9.57692 -34.4068);--color-sky-950:lab(17.8299% -5.31271 -21.1584);--color-blue-50:lab(96.492% -1.14644 -5.11479);--color-blue-200:lab(86.15% -4.04379 -21.0797);--color-blue-700:lab(36.9089% 35.0961 -85.6872);--color-rose-50:lab(96.2369% 4.94155 1.28011);--color-rose-100:lab(92.8221% 9.86832 2.60075);--color-rose-200:lab(86.806% 19.1909 4.07754);--color-rose-300:lab(76.6339% 38.3549 9.68835);--color-rose-600:lab(49.1882% 81.577 36.0311);--color-rose-700:lab(41.1651% 71.6251 30.3087);--color-rose-800:lab(34.6481% 60.802 20.1957);--color-rose-900:lab(29.7104% 51.514 12.6253);--color-rose-950:lab(14.2323% 34.0086 9.80922);--color-slate-50:lab(98.1434% -.369519 -1.05966);--color-slate-100:lab(96.286% -.852436 -2.46847);--color-slate-200:lab(91.7353% -.998765 -4.76968);--color-slate-300:lab(84.7652% -1.94535 -7.93337);--color-slate-400:lab(65.5349% -2.25151 -14.5072);--color-slate-500:lab(48.0876% -2.03595 -16.5814);--color-slate-600:lab(35.5623% -1.74978 -15.4316);--color-slate-700:lab(26.9569% -1.47016 -15.6993);--color-slate-800:lab(16.132% -.318035 -14.6672);--color-slate-900:lab(7.78673% 1.82345 -15.0537);--color-slate-950:lab(1.76974% 1.32743 -9.28855)}}}@layer base{*,:after,:before{box-sizing:border-box;border:0 solid;margin:0;padding:0}::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::-webkit-file-upload-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::-webkit-file-upload-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:-webkit-any([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:-webkit-any([multiple],[size])) optgroup option{padding-inline-start:20px}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::-webkit-file-upload-button{margin-inline-end:4px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block-start:0;padding-block-end:0}::-webkit-datetime-edit-year-field{padding-block-start:0;padding-block-end:0}::-webkit-datetime-edit-month-field{padding-block-start:0;padding-block-end:0}::-webkit-datetime-edit-day-field{padding-block-start:0;padding-block-end:0}::-webkit-datetime-edit-hour-field{padding-block-start:0;padding-block-end:0}::-webkit-datetime-edit-minute-field{padding-block-start:0;padding-block-end:0}::-webkit-datetime-edit-second-field{padding-block-start:0;padding-block-end:0}::-webkit-datetime-edit-millisecond-field{padding-block-start:0;padding-block-end:0}::-webkit-datetime-edit-meridiem-field{padding-block-start:0;padding-block-end:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button{-webkit-appearance:button;appearance:button}input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;appearance:button}::-webkit-file-upload-button{-webkit-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-0{top:0;bottom:0;left:0;right:0}.inset-y-0{top:0;bottom:0}.top-0{top:0}.top-0\.5{top:calc(var(--spacing) * .5)}.top-1{top:var(--spacing)}.top-\[4\.5rem\]{top:4.5rem}.right-0{right:0}.right-0\.5{right:calc(var(--spacing) * .5)}.right-1{right:var(--spacing)}.left-0{left:0}.left-1{left:var(--spacing)}.left-6{left:calc(var(--spacing) * 6)}.isolate{isolation:isolate}.z-20{z-index:20}.z-40{z-index:40}.z-50{z-index:50}.z-\[1000\]{z-index:1000}.m-0{margin:0}.mx-5{margin-inline:calc(var(--spacing) * 5)}.mx-auto{margin-inline-start:auto;margin-inline-end:auto}.my-5{margin-block:calc(var(--spacing) * 5)}.my-6{margin-block:calc(var(--spacing) * 6)}.my-auto{margin-block-start:auto;margin-block-end:auto}.mt-0{margin-top:0}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:var(--spacing)}.mt-1\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-5{margin-top:calc(var(--spacing) * 5)}.mt-6{margin-top:calc(var(--spacing) * 6)}.mt-7{margin-top:calc(var(--spacing) * 7)}.mb-0{margin-bottom:0}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-5{margin-bottom:calc(var(--spacing) * 5)}.mb-8{margin-bottom:calc(var(--spacing) * 8)}.ml-0{margin-left:0}.ml-1{margin-left:var(--spacing)}.ml-3{margin-left:calc(var(--spacing) * 3)}.line-clamp-2{-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-flex{display:inline-flex}.size-2{width:calc(var(--spacing) * 2);height:calc(var(--spacing) * 2)}.size-2\.5{width:calc(var(--spacing) * 2.5);height:calc(var(--spacing) * 2.5)}.size-3{width:calc(var(--spacing) * 3);height:calc(var(--spacing) * 3)}.size-3\.5{width:calc(var(--spacing) * 3.5);height:calc(var(--spacing) * 3.5)}.size-4{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.size-5{width:calc(var(--spacing) * 5);height:calc(var(--spacing) * 5)}.size-6{width:calc(var(--spacing) * 6);height:calc(var(--spacing) * 6)}.size-7{width:calc(var(--spacing) * 7);height:calc(var(--spacing) * 7)}.size-8{width:calc(var(--spacing) * 8);height:calc(var(--spacing) * 8)}.size-9{width:calc(var(--spacing) * 9);height:calc(var(--spacing) * 9)}.size-10{width:calc(var(--spacing) * 10);height:calc(var(--spacing) * 10)}.size-11{width:calc(var(--spacing) * 11);height:calc(var(--spacing) * 11)}.size-12{width:calc(var(--spacing) * 12);height:calc(var(--spacing) * 12)}.h-1\.5{height:calc(var(--spacing) * 1.5)}.h-2{height:calc(var(--spacing) * 2)}.h-10{height:calc(var(--spacing) * 10)}.h-44{height:calc(var(--spacing) * 44)}.h-\[4\.5rem\]{height:4.5rem}.h-auto{height:auto}.h-full{height:100%}.h-screen{height:100vh}.max-h-40{max-height:calc(var(--spacing) * 40)}.max-h-\[20rem\]{max-height:20rem}.max-h-\[22rem\]{max-height:22rem}.max-h-\[28rem\]{max-height:28rem}.min-h-9{min-height:calc(var(--spacing) * 9)}.min-h-10{min-height:calc(var(--spacing) * 10)}.min-h-11{min-height:calc(var(--spacing) * 11)}.min-h-14{min-height:calc(var(--spacing) * 14)}.min-h-16{min-height:calc(var(--spacing) * 16)}.min-h-52{min-height:calc(var(--spacing) * 52)}.min-h-\[4\.5rem\]{min-height:4.5rem}.min-h-screen{min-height:100vh}.w-10{width:calc(var(--spacing) * 10)}.w-11{width:calc(var(--spacing) * 11)}.w-12{width:calc(var(--spacing) * 12)}.w-fit{width:-moz-fit-content;width:fit-content}.w-full{width:100%}.max-w-3xl{max-width:var(--container-3xl)}.max-w-\[68\%\]{max-width:68%}.max-w-\[288px\]{max-width:288px}.max-w-\[1600px\]{max-width:1600px}.max-w-full{max-width:100%}.max-w-lg{max-width:var(--container-lg)}.max-w-md{max-width:var(--container-md)}.max-w-sm{max-width:var(--container-sm)}.max-w-xl{max-width:var(--container-xl)}.min-w-0{min-width:0}.min-w-20{min-width:calc(var(--spacing) * 20)}.min-w-24{min-width:calc(var(--spacing) * 24)}.flex-1{flex:1}.shrink-0{flex-shrink:0}.translate-x-1\/2{--tw-translate-x:calc(1 / 2 * 100%);translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-1\/2{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.rotate-180{rotate:180deg}.animate-bounce{animation:var(--animate-bounce)}.animate-pulse{animation:var(--animate-pulse)}.animate-spin{animation:var(--animate-spin)}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.cursor-wait{cursor:wait}.resize{resize:both}.list-none{list-style-type:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.place-items-center{place-items:center}.content-start{align-content:flex-start}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.gap-1{gap:var(--spacing)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-5{gap:calc(var(--spacing) * 5)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(var(--spacing) * var(--tw-space-y-reverse));margin-block-end:calc(var(--spacing) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1.5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1.5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 6) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-7>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 7) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 7) * calc(1 - var(--tw-space-y-reverse)))}.gap-x-4{column-gap:calc(var(--spacing) * 4)}.gap-x-5{column-gap:calc(var(--spacing) * 5)}.gap-y-1{row-gap:var(--spacing)}.gap-y-2{row-gap:calc(var(--spacing) * 2)}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-slate-200>:not(:last-child)){border-color:var(--color-slate-200)}.self-start{align-self:flex-start}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overscroll-contain{overscroll-behavior:contain}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-full{border-radius:2147483647px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-0{border-style:var(--tw-border-style);border-width:0}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-amber-200{border-color:var(--color-amber-200)}.border-amber-300{border-color:var(--color-amber-300)}.border-current{border-color:currentColor}.border-emerald-200{border-color:var(--color-emerald-200)}.border-emerald-700{border-color:var(--color-emerald-700)}.border-red-200{border-color:var(--color-red-200)}.border-red-700{border-color:var(--color-red-700)}.border-rose-200{border-color:var(--color-rose-200)}.border-rose-300{border-color:var(--color-rose-300)}.border-sky-100{border-color:var(--color-sky-100)}.border-sky-200{border-color:var(--color-sky-200)}.border-slate-100{border-color:var(--color-slate-100)}.border-slate-200{border-color:var(--color-slate-200)}.border-slate-300{border-color:var(--color-slate-300)}.border-teal-200{border-color:var(--color-teal-200)}.border-teal-300{border-color:var(--color-teal-300)}.border-teal-600{border-color:var(--color-teal-600)}.border-teal-700{border-color:var(--color-teal-700)}.border-white\/80{border-color:#fffc}@supports (color:color-mix(in lab, red, red)){.border-white\/80{border-color:color-mix(in oklab, var(--color-white) 80%, transparent)}}.bg-amber-50{background-color:var(--color-amber-50)}.bg-amber-50\/70{background-color:#fffbebb3}@supports (color:color-mix(in lab, red, red)){.bg-amber-50\/70{background-color:color-mix(in oklab, var(--color-amber-50) 70%, transparent)}}.bg-amber-100{background-color:var(--color-amber-100)}.bg-amber-500{background-color:var(--color-amber-500)}.bg-amber-600{background-color:var(--color-amber-600)}.bg-amber-700{background-color:var(--color-amber-700)}.bg-blue-50{background-color:var(--color-blue-50)}.bg-emerald-50{background-color:var(--color-emerald-50)}.bg-emerald-50\/70{background-color:#ecfdf5b3}@supports (color:color-mix(in lab, red, red)){.bg-emerald-50\/70{background-color:color-mix(in oklab, var(--color-emerald-50) 70%, transparent)}}.bg-emerald-100{background-color:var(--color-emerald-100)}.bg-emerald-500{background-color:var(--color-emerald-500)}.bg-emerald-700{background-color:var(--color-emerald-700)}.bg-red-50{background-color:var(--color-red-50)}.bg-red-100{background-color:var(--color-red-100)}.bg-red-500{background-color:var(--color-red-500)}.bg-red-700{background-color:var(--color-red-700)}.bg-rose-50{background-color:var(--color-rose-50)}.bg-rose-50\/70{background-color:#fff1f2b3}@supports (color:color-mix(in lab, red, red)){.bg-rose-50\/70{background-color:color-mix(in oklab, var(--color-rose-50) 70%, transparent)}}.bg-rose-100{background-color:var(--color-rose-100)}.bg-rose-700{background-color:var(--color-rose-700)}.bg-sky-50{background-color:var(--color-sky-50)}.bg-sky-50\/60{background-color:#f0f9ff99}@supports (color:color-mix(in lab, red, red)){.bg-sky-50\/60{background-color:color-mix(in oklab, var(--color-sky-50) 60%, transparent)}}.bg-sky-600{background-color:var(--color-sky-600)}.bg-sky-700{background-color:var(--color-sky-700)}.bg-slate-50{background-color:var(--color-slate-50)}.bg-slate-50\/70{background-color:#f8fafcb3}@supports (color:color-mix(in lab, red, red)){.bg-slate-50\/70{background-color:color-mix(in oklab, var(--color-slate-50) 70%, transparent)}}.bg-slate-100{background-color:var(--color-slate-100)}.bg-slate-200{background-color:var(--color-slate-200)}.bg-slate-300{background-color:var(--color-slate-300)}.bg-slate-400{background-color:var(--color-slate-400)}.bg-slate-900{background-color:var(--color-slate-900)}.bg-slate-950{background-color:var(--color-slate-950)}.bg-slate-950\/70{background-color:#020618b3}@supports (color:color-mix(in lab, red, red)){.bg-slate-950\/70{background-color:color-mix(in oklab, var(--color-slate-950) 70%, transparent)}}.bg-teal-50{background-color:var(--color-teal-50)}.bg-teal-100{background-color:var(--color-teal-100)}.bg-teal-500{background-color:var(--color-teal-500)}.bg-teal-600{background-color:var(--color-teal-600)}.bg-teal-700{background-color:var(--color-teal-700)}.bg-transparent{background-color:#0000}.bg-white{background-color:var(--color-white)}.bg-white\/70{background-color:#ffffffb3}@supports (color:color-mix(in lab, red, red)){.bg-white\/70{background-color:color-mix(in oklab, var(--color-white) 70%, transparent)}}.bg-white\/95{background-color:#fffffff2}@supports (color:color-mix(in lab, red, red)){.bg-white\/95{background-color:color-mix(in oklab, var(--color-white) 95%, transparent)}}.p-0{padding:0}.p-1\.5{padding:calc(var(--spacing) * 1.5)}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-3\.5{padding:calc(var(--spacing) * 3.5)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-3\.5{padding-inline:calc(var(--spacing) * 3.5)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-5{padding-inline:calc(var(--spacing) * 5)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-5{padding-block:calc(var(--spacing) * 5)}.py-10{padding-block:calc(var(--spacing) * 10)}.pt-1{padding-top:var(--spacing)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-3{padding-top:calc(var(--spacing) * 3)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pt-5{padding-top:calc(var(--spacing) * 5)}.pr-4{padding-right:calc(var(--spacing) * 4)}.pr-12{padding-right:calc(var(--spacing) * 12)}.pb-1{padding-bottom:var(--spacing)}.pb-3{padding-bottom:calc(var(--spacing) * 3)}.pb-4{padding-bottom:calc(var(--spacing) * 4)}.pb-5{padding-bottom:calc(var(--spacing) * 5)}.pb-8{padding-bottom:calc(var(--spacing) * 8)}.pl-5{padding-left:calc(var(--spacing) * 5)}.pl-9{padding-left:calc(var(--spacing) * 9)}.pl-11{padding-left:calc(var(--spacing) * 11)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.align-middle{vertical-align:middle}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[0\.7rem\]{font-size:.7rem}.text-\[0\.65rem\]{font-size:.65rem}.text-\[0\.68rem\]{font-size:.68rem}.text-\[9px\]{font-size:9px}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[13px\]{font-size:13px}.leading-5{--tw-leading:calc(var(--spacing) * 5);line-height:calc(var(--spacing) * 5)}.leading-6{--tw-leading:calc(var(--spacing) * 6);line-height:calc(var(--spacing) * 6)}.leading-none{--tw-leading:1;line-height:1}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.font-black{--tw-font-weight:var(--font-weight-black);font-weight:var(--font-weight-black)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-extrabold{--tw-font-weight:var(--font-weight-extrabold);font-weight:var(--font-weight-extrabold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-\[0\.08em\]{--tw-tracking:.08em;letter-spacing:.08em}.tracking-\[0\.12em\]{--tw-tracking:.12em;letter-spacing:.12em}.tracking-\[0\.14em\]{--tw-tracking:.14em;letter-spacing:.14em}.tracking-\[0\.16em\]{--tw-tracking:.16em;letter-spacing:.16em}.tracking-\[0\.18em\]{--tw-tracking:.18em;letter-spacing:.18em}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-amber-700{color:var(--color-amber-700)}.text-amber-800{color:var(--color-amber-800)}.text-amber-900{color:var(--color-amber-900)}.text-amber-900\/80{color:#7b3306cc}@supports (color:color-mix(in lab, red, red)){.text-amber-900\/80{color:color-mix(in oklab, var(--color-amber-900) 80%, transparent)}}.text-amber-950{color:var(--color-amber-950)}.text-blue-700{color:var(--color-blue-700)}.text-current{color:currentColor}.text-emerald-700{color:var(--color-emerald-700)}.text-emerald-800{color:var(--color-emerald-800)}.text-emerald-900{color:var(--color-emerald-900)}.text-emerald-950{color:var(--color-emerald-950)}.text-red-700{color:var(--color-red-700)}.text-red-800{color:var(--color-red-800)}.text-red-900{color:var(--color-red-900)}.text-red-950{color:var(--color-red-950)}.text-rose-600{color:var(--color-rose-600)}.text-rose-700{color:var(--color-rose-700)}.text-rose-800{color:var(--color-rose-800)}.text-rose-900{color:var(--color-rose-900)}.text-rose-950{color:var(--color-rose-950)}.text-sky-700{color:var(--color-sky-700)}.text-sky-800{color:var(--color-sky-800)}.text-sky-950{color:var(--color-sky-950)}.text-slate-300{color:var(--color-slate-300)}.text-slate-400{color:var(--color-slate-400)}.text-slate-500{color:var(--color-slate-500)}.text-slate-600{color:var(--color-slate-600)}.text-slate-700{color:var(--color-slate-700)}.text-slate-800{color:var(--color-slate-800)}.text-slate-900{color:var(--color-slate-900)}.text-slate-950{color:var(--color-slate-950)}.text-teal-300{color:var(--color-teal-300)}.text-teal-600{color:var(--color-teal-600)}.text-teal-700{color:var(--color-teal-700)}.text-teal-800{color:var(--color-teal-800)}.text-teal-950{color:var(--color-teal-950)}.text-white{color:var(--color-white)}.uppercase{text-transform:uppercase}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.no-underline{text-decoration-line:none}.underline{text-decoration-line:underline}.underline-offset-2{text-underline-offset:2px}.accent-teal-700{accent-color:var(--color-teal-700)}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.opacity-80{opacity:.8}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-inner{--tw-shadow:inset 0 2px 4px 0 var(--tw-shadow-color,#0000000d);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-1{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-slate-900\/5{--tw-shadow-color:#0f172b0d}@supports (color:color-mix(in lab, red, red)){.shadow-slate-900\/5{--tw-shadow-color:color-mix(in oklab, color-mix(in oklab, var(--color-slate-900) 5%, transparent) var(--tw-shadow-alpha), transparent)}}.shadow-slate-950\/10{--tw-shadow-color:#0206181a}@supports (color:color-mix(in lab, red, red)){.shadow-slate-950\/10{--tw-shadow-color:color-mix(in oklab, color-mix(in oklab, var(--color-slate-950) 10%, transparent) var(--tw-shadow-alpha), transparent)}}.ring-amber-200{--tw-ring-color:var(--color-amber-200)}.ring-blue-200{--tw-ring-color:var(--color-blue-200)}.ring-emerald-200{--tw-ring-color:var(--color-emerald-200)}.ring-red-200{--tw-ring-color:var(--color-red-200)}.ring-rose-200{--tw-ring-color:var(--color-rose-200)}.ring-sky-100{--tw-ring-color:var(--color-sky-100)}.ring-sky-200{--tw-ring-color:var(--color-sky-200)}.ring-slate-100{--tw-ring-color:var(--color-slate-100)}.ring-slate-200{--tw-ring-color:var(--color-slate-200)}.ring-teal-100{--tw-ring-color:var(--color-teal-100)}.ring-teal-200{--tw-ring-color:var(--color-teal-200)}.ring-white{--tw-ring-color:var(--color-white)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.backdrop-blur{--tw-backdrop-blur:blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[left\]{transition-property:left;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[width\]{transition-property:width;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-300{--tw-duration:.3s;transition-duration:.3s}.outline-none{--tw-outline-style:none;outline-style:none}.select-all{-webkit-user-select:all;user-select:all}.ring-inset{--tw-ring-inset:inset}.group-open\:hidden:-webkit-any(:where(.group):-webkit-any([open],:popover-open,:open) *){display:none}.group-open\:hidden:is(:where(.group):is([open],:popover-open,:open) *){display:none}.group-open\:inline-block:-webkit-any(:where(.group):-webkit-any([open],:popover-open,:open) *){display:inline-block}.group-open\:inline-block:is(:where(.group):is([open],:popover-open,:open) *){display:inline-block}.group-open\:inline-flex:-webkit-any(:where(.group):-webkit-any([open],:popover-open,:open) *){display:inline-flex}.group-open\:inline-flex:is(:where(.group):is([open],:popover-open,:open) *){display:inline-flex}.placeholder\:text-slate-400::placeholder{color:var(--color-slate-400)}.first\:mt-0:first-child{margin-top:0}.first\:pt-0:first-child{padding-top:0}.last\:border-b-0:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.last\:pb-0:last-child{padding-bottom:0}@media (hover:hover){.hover\:border-emerald-800:hover{border-color:var(--color-emerald-800)}.hover\:border-red-200:hover{border-color:var(--color-red-200)}.hover\:border-red-300:hover{border-color:var(--color-red-300)}.hover\:border-red-800:hover{border-color:var(--color-red-800)}.hover\:border-slate-300:hover{border-color:var(--color-slate-300)}.hover\:border-teal-200:hover{border-color:var(--color-teal-200)}.hover\:border-teal-300:hover{border-color:var(--color-teal-300)}.hover\:border-teal-400:hover{border-color:var(--color-teal-400)}.hover\:border-teal-800:hover{border-color:var(--color-teal-800)}.hover\:bg-amber-50:hover{background-color:var(--color-amber-50)}.hover\:bg-amber-100:hover{background-color:var(--color-amber-100)}.hover\:bg-amber-700:hover{background-color:var(--color-amber-700)}.hover\:bg-amber-800:hover{background-color:var(--color-amber-800)}.hover\:bg-black\/5:hover{background-color:#0000000d}@supports (color:color-mix(in lab, red, red)){.hover\:bg-black\/5:hover{background-color:color-mix(in oklab, var(--color-black) 5%, transparent)}}.hover\:bg-emerald-800:hover{background-color:var(--color-emerald-800)}.hover\:bg-red-50:hover{background-color:var(--color-red-50)}.hover\:bg-red-800:hover{background-color:var(--color-red-800)}.hover\:bg-rose-50:hover{background-color:var(--color-rose-50)}.hover\:bg-rose-100:hover{background-color:var(--color-rose-100)}.hover\:bg-rose-800:hover{background-color:var(--color-rose-800)}.hover\:bg-sky-800:hover{background-color:var(--color-sky-800)}.hover\:bg-slate-50:hover{background-color:var(--color-slate-50)}.hover\:bg-slate-100:hover{background-color:var(--color-slate-100)}.hover\:bg-slate-800:hover{background-color:var(--color-slate-800)}.hover\:bg-teal-50:hover{background-color:var(--color-teal-50)}.hover\:bg-teal-100:hover{background-color:var(--color-teal-100)}.hover\:bg-teal-800:hover{background-color:var(--color-teal-800)}.hover\:text-red-700:hover{color:var(--color-red-700)}.hover\:text-rose-800:hover{color:var(--color-rose-800)}.hover\:text-slate-800:hover{color:var(--color-slate-800)}.hover\:text-slate-950:hover{color:var(--color-slate-950)}.hover\:text-teal-700:hover{color:var(--color-teal-700)}.hover\:text-teal-800:hover{color:var(--color-teal-800)}.hover\:text-teal-900:hover{color:var(--color-teal-900)}.hover\:opacity-100:hover{opacity:1}.hover\:shadow-md:hover{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}}.focus\:border-teal-500:focus{border-color:var(--color-teal-500)}.focus\:bg-white:focus{background-color:var(--color-white)}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus\:ring-4:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus\:ring-teal-100:focus{--tw-ring-color:var(--color-teal-100)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.focus-visible\:rounded:focus-visible{border-radius:.25rem}.focus-visible\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus-visible\:ring-4:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus-visible\:ring-amber-100:focus-visible{--tw-ring-color:var(--color-amber-100)}.focus-visible\:ring-amber-200:focus-visible{--tw-ring-color:var(--color-amber-200)}.focus-visible\:ring-current:focus-visible{--tw-ring-color:currentcolor}.focus-visible\:ring-emerald-100:focus-visible{--tw-ring-color:var(--color-emerald-100)}.focus-visible\:ring-red-100:focus-visible{--tw-ring-color:var(--color-red-100)}.focus-visible\:ring-red-200:focus-visible{--tw-ring-color:var(--color-red-200)}.focus-visible\:ring-teal-100:focus-visible{--tw-ring-color:var(--color-teal-100)}.focus-visible\:ring-teal-200:focus-visible{--tw-ring-color:var(--color-teal-200)}.focus-visible\:ring-teal-500:focus-visible{--tw-ring-color:var(--color-teal-500)}.focus-visible\:outline:focus-visible{outline-style:var(--tw-outline-style);outline-width:1px}.focus-visible\:outline-2:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px}.focus-visible\:outline-offset-2:focus-visible{outline-offset:2px}.focus-visible\:outline-teal-500:focus-visible{outline-color:var(--color-teal-500)}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:cursor-progress:disabled{cursor:progress}.disabled\:cursor-wait:disabled{cursor:wait}.disabled\:bg-red-700:disabled{background-color:var(--color-red-700)}.disabled\:bg-slate-100:disabled{background-color:var(--color-slate-100)}.disabled\:text-slate-400:disabled{color:var(--color-slate-400)}.disabled\:opacity-50:disabled{opacity:.5}.disabled\:opacity-60:disabled{opacity:.6}.disabled\:opacity-80:disabled{opacity:.8}@media (min-width:40rem){.sm\:mx-6{margin-inline:calc(var(--spacing) * 6)}.sm\:mb-6{margin-bottom:calc(var(--spacing) * 6)}.sm\:h-52{height:calc(var(--spacing) * 52)}.sm\:min-h-9{min-height:calc(var(--spacing) * 9)}.sm\:min-h-10{min-height:calc(var(--spacing) * 10)}.sm\:w-auto{width:auto}.sm\:max-w-\[392px\]{max-width:392px}.sm\:max-w-\[592px\]{max-width:592px}.sm\:flex-none{flex:none}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.sm\:grid-cols-\[minmax\(0\,1fr\)_auto\]{grid-template-columns:minmax(0,1fr) auto}.sm\:flex-row{flex-direction:row}.sm\:flex-wrap{flex-wrap:wrap}.sm\:items-center{align-items:center}.sm\:items-end{align-items:flex-end}.sm\:items-start{align-items:flex-start}.sm\:justify-between{justify-content:space-between}.sm\:justify-end{justify-content:flex-end}.sm\:justify-start{justify-content:flex-start}.sm\:gap-4{gap:calc(var(--spacing) * 4)}.sm\:self-auto{align-self:auto}.sm\:p-5{padding:calc(var(--spacing) * 5)}.sm\:p-6{padding:calc(var(--spacing) * 6)}.sm\:p-7{padding:calc(var(--spacing) * 7)}.sm\:p-8{padding:calc(var(--spacing) * 8)}.sm\:px-2{padding-inline:calc(var(--spacing) * 2)}.sm\:px-4{padding-inline:calc(var(--spacing) * 4)}.sm\:px-5{padding-inline:calc(var(--spacing) * 5)}.sm\:px-6{padding-inline:calc(var(--spacing) * 6)}.sm\:pt-2{padding-top:calc(var(--spacing) * 2)}.sm\:pt-6{padding-top:calc(var(--spacing) * 6)}.sm\:pb-6{padding-bottom:calc(var(--spacing) * 6)}.sm\:pb-10{padding-bottom:calc(var(--spacing) * 10)}.sm\:text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.sm\:text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.sm\:text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}}@media (min-width:48rem){.md\:flex{display:flex}.md\:grid{display:grid}.md\:hidden{display:none}.md\:inline-flex{display:inline-flex}.md\:h-\[4\.5rem\]{height:4.5rem}.md\:h-full{height:100%}.md\:min-h-screen{min-height:100vh}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-\[5rem_minmax\(0\,1fr\)\]{grid-template-columns:5rem minmax(0,1fr)}.md\:grid-cols-\[16rem_minmax\(0\,1fr\)\]{grid-template-columns:16rem minmax(0,1fr)}.md\:flex-col{flex-direction:column}.md\:py-0{padding-block-start:0;padding-block-end:0}.md\:transition-\[grid-template-columns\]{transition-property:grid-template-columns;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.md\:duration-200{--tw-duration:.2s;transition-duration:.2s}.md\:ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}}@media (min-width:64rem){.lg\:col-span-2{grid-column:span 2/span 2}.lg\:col-start-2{grid-column-start:2}.lg\:block{display:block}.lg\:w-72{width:calc(var(--spacing) * 72)}.lg\:flex-1{flex:1}.lg\:shrink-0{flex-shrink:0}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:grid-cols-\[minmax\(0\,1fr\)_minmax\(0\,1fr\)\]{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}.lg\:flex-row{flex-direction:row}.lg\:items-center{align-items:center}.lg\:items-start{align-items:flex-start}.lg\:justify-between{justify-content:space-between}.lg\:justify-end{justify-content:flex-end}.lg\:px-8{padding-inline:calc(var(--spacing) * 8)}.lg\:pt-3{padding-top:calc(var(--spacing) * 3)}.lg\:pt-8{padding-top:calc(var(--spacing) * 8)}.lg\:pb-12{padding-bottom:calc(var(--spacing) * 12)}.lg\:whitespace-nowrap{white-space:nowrap}}@media (min-width:80rem){.xl\:col-start-1{grid-column-start:1}.xl\:col-start-2{grid-column-start:2}.xl\:row-span-2{grid-row:span 2/span 2}.xl\:row-start-1{grid-row-start:1}.xl\:row-start-2{grid-row-start:2}.xl\:size-9{width:calc(var(--spacing) * 9);height:calc(var(--spacing) * 9)}.xl\:h-full{height:100%}.xl\:w-80{width:calc(var(--spacing) * 80)}.xl\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.xl\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.xl\:grid-cols-\[minmax\(0\,0\.95fr\)_minmax\(0\,1\.05fr\)\]{grid-template-columns:minmax(0,.95fr) minmax(0,1.05fr)}.xl\:grid-cols-\[minmax\(0\,1\.2fr\)_minmax\(18rem\,0\.8fr\)\]{grid-template-columns:minmax(0,1.2fr) minmax(18rem,.8fr)}.xl\:grid-cols-\[minmax\(0\,1\.08fr\)_minmax\(0\,0\.92fr\)\]{grid-template-columns:minmax(0,1.08fr) minmax(0,.92fr)}.xl\:grid-cols-\[minmax\(0\,1\.45fr\)_minmax\(20rem\,0\.75fr\)\]{grid-template-columns:minmax(0,1.45fr) minmax(20rem,.75fr)}.xl\:grid-cols-\[minmax\(0\,1\.55fr\)_minmax\(18rem\,0\.65fr\)\]{grid-template-columns:minmax(0,1.55fr) minmax(18rem,.65fr)}.xl\:flex-col{flex-direction:column}.xl\:items-stretch{align-items:stretch}.xl\:justify-end{justify-content:flex-end}.xl\:gap-2{gap:calc(var(--spacing) * 2)}.xl\:self-stretch{align-self:stretch}.xl\:border-t-0{border-top-style:var(--tw-border-style);border-top-width:0}.xl\:border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.xl\:p-4{padding:calc(var(--spacing) * 4)}.xl\:px-10{padding-inline:calc(var(--spacing) * 10)}.xl\:text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.xl\:whitespace-nowrap{white-space:nowrap}}@media (min-width:96rem){.\32 xl\:size-10{width:calc(var(--spacing) * 10);height:calc(var(--spacing) * 10)}.\32 xl\:flex-row{flex-direction:row}.\32 xl\:p-5{padding:calc(var(--spacing) * 5)}.\32 xl\:text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}}.\[\&\:\:-webkit-details-marker\]\:hidden::-webkit-details-marker{display:none}}:host{color:#0f172a;--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light;-webkit-font-smoothing:antialiased;-webkit-tap-highlight-color:transparent;text-rendering:optimizelegibility;width:100%;min-width:0;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Noto Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;display:block}.smartsafehub-shadow-root{width:100%;min-width:0;max-width:100%;display:block}.smartsafehub-shadow-root,.smartsafehub-shadow-root *,.smartsafehub-shadow-root :before,.smartsafehub-shadow-root :after{box-sizing:border-box}.ssh-app{width:100%;min-width:0;max-width:100%;overflow:clip}.ssh-app main,.ssh-app section,.ssh-app article,.ssh-app aside,.ssh-app nav,.ssh-app div{min-width:0}.ssh-product-hero{border-radius:0}.ssh-product-header-action{border-color:#e2e8f0!important}.ssh-product-header-action:hover{border-color:#5eead4!important}.ssh-app a,.ssh-app button,.ssh-app input,.ssh-app textarea{touch-action:manipulation}.ssh-app[data-theme=dark] .ssh-custom-select-trigger:disabled{color:#64748b;background-color:#1e293b;border-color:#334155}.ssh-app[data-theme=dark] .ssh-custom-select-trigger:focus-visible{--tw-ring-color:#134e4aa6}.ssh-app[data-theme=dark] .ssh-custom-select-menu{box-shadow:0 18px 45px #00000059}.ssh-mobile-navigation{padding-left:max(1rem, env(safe-area-inset-left));padding-right:max(1rem, env(safe-area-inset-right))}.ssh-mobile-menu{overscroll-behavior:contain;-webkit-overflow-scrolling:touch;max-height:calc(100dvh - 4.5rem);overflow-y:auto}.ssh-touch-scroll{scrollbar-width:none;overscroll-behavior-inline:contain;-webkit-overflow-scrolling:touch}.ssh-touch-scroll::-webkit-scrollbar{display:none}@media (max-width:767px){.ssh-app{min-height:100dvh;overflow-x:clip}.ssh-product-hero{padding-top:max(1.25rem, env(safe-area-inset-top));padding-left:max(1rem, env(safe-area-inset-left));padding-right:max(1rem, env(safe-area-inset-right))}.ssh-product-main{padding-right:max(1rem, env(safe-area-inset-right));padding-bottom:max(2rem, env(safe-area-inset-bottom));padding-left:max(1rem, env(safe-area-inset-left))}.ssh-app input,.ssh-app textarea{font-size:16px}.ssh-app button,.ssh-app a{min-height:44px}}.ssh-sidebar-toggle{color:#64748b;background:#fff;border:1px solid #e2e8f0;box-shadow:0 1px 3px #0f172a1f}.ssh-sidebar-toggle:hover{color:#0f766e;background:#f8fafc;border-color:#99f6e4}.ssh-app[data-theme=dark] .ssh-sidebar-toggle{color:#94a3b8;background:#1e293b;border-color:#334155;box-shadow:0 1px 3px #00000047}.ssh-app[data-theme=dark] .ssh-sidebar-toggle:hover{color:#5eead4;background:#334155;border-color:#0f766e}.ssh-update-nav-badge{color:#92400e;background:#fef3c7;border:1px solid #f59e0b;border-radius:9999px;justify-content:center;align-items:center;min-width:1.3rem;height:1.3rem;padding:0 .25rem;font-size:.75rem;font-weight:900;line-height:1;display:inline-flex;box-shadow:0 1px 2px #0f172a1f}.ssh-update-nav-badge[data-collapsed=true]{z-index:1;width:auto;min-width:1.25rem;height:1.25rem;padding:0 .25rem;position:absolute;top:.125rem;right:.125rem;box-shadow:0 0 0 2px #fff,0 2px 5px #0f172a33}.ssh-app[data-theme=dark] .ssh-update-nav-badge{color:#422006;background:#fbbf24;border-color:#fcd34d;box-shadow:0 1px 3px #0000004d}.ssh-app[data-theme=dark] .ssh-update-nav-badge[data-collapsed=true]{box-shadow:0 0 0 2px #0f172a,0 2px 6px #00000061}.ssh-update-progress-track{background:#bae6fd;border-radius:9999px;height:.375rem;position:relative;overflow:hidden}.ssh-update-progress-bar{border-radius:inherit;background:#0284c7;width:38%;animation:1.2s ease-in-out infinite ssh-update-progress;position:absolute;top:0;bottom:0;left:0}.ssh-app[data-theme=dark] .ssh-update-progress-track{background:#1e3a5f}.ssh-app[data-theme=dark] .ssh-update-progress-bar{background:#38bdf8}.ssh-app button.ssh-switch-control{box-sizing:border-box;flex:0 0 3rem;width:3rem;min-width:3rem;max-width:3rem;height:1.75rem;min-height:1.75rem;max-height:1.75rem;padding:0}.ssh-switch-thumb{aspect-ratio:1;box-sizing:border-box;background-color:#fff;border-radius:9999px;width:1.25rem;min-width:1.25rem;max-width:1.25rem;height:1.25rem;min-height:1.25rem;max-height:1.25rem}@keyframes ssh-update-progress{0%{transform:translate(-110%)}to{transform:translate(300%)}}.ssh-software-settings-grid{grid-template-columns:repeat(auto-fit,minmax(min(100%,18rem),1fr));gap:1rem;display:grid}.ssh-software-settings-channel{grid-column:1/-1}.ssh-time-select{grid-template-columns:repeat(2,minmax(0,1fr));gap:.5rem;width:100%;min-width:0;max-width:100%;display:grid}.ssh-time-select>*{min-width:0}.ssh-time-select .ssh-custom-select-trigger{gap:.5rem;min-width:0;max-width:100%;padding-inline-start:.75rem;padding-inline-end:.75rem}.ssh-ipv4-segments{grid-template-columns:repeat(4,minmax(0,1fr));gap:.75rem;width:100%;min-width:0;max-width:100%;display:grid}.ssh-ipv4-segment{min-width:0;position:relative}.ssh-ipv4-separator{pointer-events:none;position:absolute;top:50%;right:-.375rem;transform:translate(50%,-50%)}@media (min-width:640px){.ssh-ipv4-segments{gap:1rem}.ssh-ipv4-separator{right:-.5rem}}.ssh-app .ssh-rule-group-toggle:hover{background-color:#f1f5f9}.ssh-app[data-theme=dark] .ssh-rule-group-toggle:hover{background-color:#1e293b}.ssh-app[data-theme=dark] .ssh-password-input:-webkit-autofill{-webkit-text-fill-color:#f8fafc;caret-color:#f8fafc;box-shadow:inset 0 0 0 1000px #0f172a}.ssh-app[data-theme=dark] .ssh-password-input:-webkit-autofill:hover{-webkit-text-fill-color:#f8fafc;caret-color:#f8fafc;box-shadow:inset 0 0 0 1000px #0f172a}.ssh-app[data-theme=dark] .ssh-password-input:-webkit-autofill:focus{-webkit-text-fill-color:#f8fafc;caret-color:#f8fafc;box-shadow:inset 0 0 0 1000px #0f172a}.ssh-app[data-theme=light] .ssh-password-input:-webkit-autofill{-webkit-text-fill-color:#0f172a;caret-color:#0f172a;box-shadow:inset 0 0 0 1000px #fff}.ssh-app[data-theme=light] .ssh-password-input:-webkit-autofill:hover{-webkit-text-fill-color:#0f172a;caret-color:#0f172a;box-shadow:inset 0 0 0 1000px #fff}.ssh-app[data-theme=light] .ssh-password-input:-webkit-autofill:focus{-webkit-text-fill-color:#0f172a;caret-color:#0f172a;box-shadow:inset 0 0 0 1000px #fff}.ssh-app[data-theme=dark]{color:#f8fafc;--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark;background:#020617}.ssh-app[data-theme=dark] [class~=bg-white]{background-color:#0f172a}.ssh-app[data-theme=dark] [class~=bg-white\/95]{background-color:#0f172af2}.ssh-app[data-theme=dark] [class~=bg-white\/70]{background-color:#0f172ac7}.ssh-app[data-theme=dark] [class~=bg-slate-50]{background-color:#020617}.ssh-app[data-theme=dark] [class~=bg-slate-50\/70]{background-color:#020617b8}.ssh-app[data-theme=dark] [class~=bg-slate-100]{background-color:#1e293b}.ssh-app[data-theme=dark] [class~=bg-slate-200]{background-color:#334155}.ssh-app[data-theme=dark] [class~=bg-slate-300]{background-color:#475569}.ssh-app[data-theme=dark] [class~=bg-slate-400]{background-color:#64748b}.ssh-app[data-theme=dark] [class~=bg-black\/5]{background-color:#ffffff0d}.ssh-app[data-theme=dark] [class~=text-slate-950]{color:#f8fafc}.ssh-app[data-theme=dark] [class~=text-slate-900]{color:#f1f5f9}.ssh-app[data-theme=dark] [class~=text-slate-800]{color:#e2e8f0}.ssh-app[data-theme=dark] [class~=text-slate-700],.ssh-app[data-theme=dark] [class~=text-slate-600]{color:#cbd5e1}.ssh-app[data-theme=dark] [class~=text-slate-500],.ssh-app[data-theme=dark] [class~=text-slate-400]{color:#94a3b8}.ssh-app[data-theme=dark] [class~=text-slate-300]{color:#cbd5e1}.ssh-app[data-theme=dark] [class~=border-slate-100],.ssh-app[data-theme=dark] [class~=border-slate-200],.ssh-app[data-theme=dark] [class~=divide-slate-200]{border-color:#1e293b}.ssh-app[data-theme=dark] [class~=border-slate-300],.ssh-app[data-theme=dark] [class~=border-white\/80]{border-color:#334155}.ssh-app[data-theme=dark] [class~=ring-slate-100],.ssh-app[data-theme=dark] [class~=ring-slate-200],.ssh-app[data-theme=dark] [class~=ring-white]{--tw-ring-color:#334155}.ssh-app[data-theme=dark] [class~=bg-teal-50],.ssh-app[data-theme=dark] [class~=bg-teal-100]{background-color:#0d948829}.ssh-app[data-theme=dark] [class~=text-teal-700],.ssh-app[data-theme=dark] [class~=text-teal-800],.ssh-app[data-theme=dark] [class~=text-teal-950]{color:#5eead4}.ssh-app[data-theme=dark] [class~=border-teal-200],.ssh-app[data-theme=dark] [class~=border-teal-300]{border-color:#115e59}.ssh-app[data-theme=dark] [class~=ring-teal-100],.ssh-app[data-theme=dark] [class~=ring-teal-200]{--tw-ring-color:#134e4a}.ssh-app[data-theme=dark] [class~=bg-sky-50],.ssh-app[data-theme=dark] [class~=bg-sky-50\/60]{background-color:#0ea5e91f}.ssh-app[data-theme=dark] [class~=text-sky-700],.ssh-app[data-theme=dark] [class~=text-sky-800],.ssh-app[data-theme=dark] [class~=text-sky-950]{color:#7dd3fc}.ssh-app[data-theme=dark] [class~=border-sky-200]{border-color:#075985}.ssh-app[data-theme=dark] [class~=ring-sky-100],.ssh-app[data-theme=dark] [class~=ring-sky-200]{--tw-ring-color:#0c4a6e}.ssh-app[data-theme=dark] [class~=bg-emerald-50],.ssh-app[data-theme=dark] [class~=bg-emerald-100]{background-color:#10b98121}.ssh-app[data-theme=dark] [class~=bg-emerald-50\/70]{background-color:#10b9811a}.ssh-app[data-theme=dark] [class~=text-emerald-700],.ssh-app[data-theme=dark] [class~=text-emerald-800],.ssh-app[data-theme=dark] [class~=text-emerald-900],.ssh-app[data-theme=dark] [class~=text-emerald-950]{color:#6ee7b7}.ssh-app[data-theme=dark] [class~=border-emerald-200]{border-color:#065f46}.ssh-app[data-theme=dark] [class~=ring-emerald-100],.ssh-app[data-theme=dark] [class~=ring-emerald-200]{--tw-ring-color:#064e3b}.ssh-app[data-theme=dark] [class~=bg-amber-50],.ssh-app[data-theme=dark] [class~=bg-amber-50\/60],.ssh-app[data-theme=dark] [class~=bg-amber-100],.ssh-app[data-theme=dark] [class~=bg-amber-100\/70]{background-color:#f59e0b21}.ssh-app[data-theme=dark] [class~=bg-amber-50\/70]{background-color:#f59e0b1a}.ssh-app[data-theme=dark] [class~=text-amber-700],.ssh-app[data-theme=dark] [class~=text-amber-800],.ssh-app[data-theme=dark] [class~=text-amber-900],.ssh-app[data-theme=dark] [class~=text-amber-900\/80],.ssh-app[data-theme=dark] [class~=text-amber-950]{color:#fcd34d}.ssh-app[data-theme=dark] [class~=border-amber-200],.ssh-app[data-theme=dark] [class~=border-amber-300]{border-color:#92400e}.ssh-app[data-theme=dark] [class~=ring-amber-100],.ssh-app[data-theme=dark] [class~=ring-amber-200]{--tw-ring-color:#78350f}.ssh-app[data-theme=dark] [class~=bg-red-50],.ssh-app[data-theme=dark] [class~=bg-red-100]{background-color:#ef44441f}.ssh-app[data-theme=dark] [class~=text-red-700],.ssh-app[data-theme=dark] [class~=text-red-800],.ssh-app[data-theme=dark] [class~=text-red-900],.ssh-app[data-theme=dark] [class~=text-red-950]{color:#fca5a5}.ssh-app[data-theme=dark] [class~=border-red-200],.ssh-app[data-theme=dark] [class~=border-red-300]{border-color:#991b1b}.ssh-app[data-theme=dark] [class~=ring-red-100],.ssh-app[data-theme=dark] [class~=ring-red-200]{--tw-ring-color:#7f1d1d}.ssh-app[data-theme=dark] [class~=bg-rose-50]{background-color:#f43f5e1f}.ssh-app[data-theme=dark] [class~=bg-rose-50\/70]{background-color:#f43f5e1a}.ssh-app[data-theme=dark] [class~=text-rose-600],.ssh-app[data-theme=dark] [class~=text-rose-700],.ssh-app[data-theme=dark] [class~=text-rose-800],.ssh-app[data-theme=dark] [class~=text-rose-900],.ssh-app[data-theme=dark] [class~=text-rose-950]{color:#fda4af}.ssh-app[data-theme=dark] [class~=border-rose-200],.ssh-app[data-theme=dark] [class~=border-rose-300]{border-color:#9f1239}.ssh-app[data-theme=dark] [class~=ring-rose-200]{--tw-ring-color:#881337}.ssh-app[data-theme=dark] [class~=bg-blue-50]{background-color:#3b82f61f}.ssh-app[data-theme=dark] [class~=text-blue-700]{color:#93c5fd}.ssh-app[data-theme=dark] [class~=ring-blue-200]{--tw-ring-color:#1e3a8a}.ssh-app[data-theme=dark] .hover\:bg-slate-50:hover{background-color:#1e293b}.ssh-app[data-theme=dark] .hover\:bg-rose-50:hover{background-color:#f43f5e2e}.ssh-app[data-theme=dark] .hover\:text-slate-950:hover{color:#f8fafc}.ssh-app[data-theme=dark] .hover\:text-slate-700:hover{color:#e2e8f0}.ssh-app[data-theme=dark] .hover\:text-rose-800:hover{color:#fecdd3}.ssh-app[data-theme=dark] .hover\:border-slate-300:hover{border-color:#475569}.ssh-app[data-theme=dark] .ssh-product-header-action{color:#cbd5e1;background-color:#0f172a;border-color:#334155!important}.ssh-app[data-theme=dark] .ssh-product-header-action:hover{color:#5eead4;border-color:#0f766e!important}.ssh-app[data-theme=dark] .ssh-product-header-action:focus-visible{--tw-ring-color:#134e4a}.ssh-initial-setup-probe{color:#0f172a;background:#f8fafc;place-items:center;min-height:100dvh;padding:1.5rem;display:grid}.ssh-initial-setup-probe-card{text-align:center;background:#fff;border:1px solid #e2e8f0;border-radius:1.25rem;justify-items:center;gap:.7rem;width:min(100%,28rem);padding:2.25rem;display:grid;box-shadow:0 20px 55px #0f172a14}.ssh-initial-setup-probe-card>svg{color:#0f766e;width:2rem;height:2rem}.ssh-initial-setup-probe-card strong{font-size:1rem;font-weight:900}.ssh-initial-setup-probe-card span{color:#64748b;font-size:.84rem;line-height:1.55}.ssh-initial-setup-probe-card button{color:#fff;cursor:pointer;min-height:2.75rem;font:inherit;background:#0f766e;border:0;border-radius:.8rem;margin-top:.4rem;padding:0 1rem;font-size:.84rem;font-weight:900}.smartsafehub-login-shadow-root{background:#f8fafc;min-height:100dvh}.ssh-login-page,.ssh-login-page *,.ssh-login-page :before,.ssh-login-page :after{box-sizing:border-box}.ssh-login-page{--ssh-login-bg:#f8fafc;--ssh-login-surface:#fff;--ssh-login-surface-soft:#f8fafc;--ssh-login-border:#e2e8f0;--ssh-login-border-strong:#cbd5e1;--ssh-login-text:#0f172a;--ssh-login-muted:#64748b;--ssh-login-muted-soft:#94a3b8;--ssh-login-accent:#0f766e;--ssh-login-accent-hover:#115e59;--ssh-login-accent-soft:#f0fdfa;--ssh-login-accent-ring:#14b8a624;--ssh-login-card-shadow:0 24px 70px #0f172a14, 0 2px 10px #0f172a0a;background:var(--ssh-login-bg);min-height:100dvh;color:var(--ssh-login-text);grid-template-columns:minmax(0,1.08fr) minmax(420px,.92fr);display:grid}.ssh-login-page[data-theme=dark]{--ssh-login-bg:#020617;--ssh-login-surface:#0f172a;--ssh-login-surface-soft:#111827;--ssh-login-border:#1e293b;--ssh-login-border-strong:#334155;--ssh-login-text:#f8fafc;--ssh-login-muted:#94a3b8;--ssh-login-muted-soft:#64748b;--ssh-login-accent:#2dd4bf;--ssh-login-accent-hover:#5eead4;--ssh-login-accent-soft:#14b8a61a;--ssh-login-accent-ring:#2dd4bf2e;--ssh-login-card-shadow:0 28px 72px #0000003d, 0 0 0 1px #94a3b80a}.ssh-login-toast{top:max(1rem, env(safe-area-inset-top));z-index:20;color:#115e59;background:#f0fdfa;border:1px solid #99f6e4;border-radius:1rem;align-items:center;gap:.75rem;width:min(100% - 2rem,34rem);padding:.9rem 1rem;font-size:.88rem;font-weight:800;line-height:1.45;display:flex;position:fixed;left:50%;transform:translate(-50%);box-shadow:0 18px 45px #0f172a29}.ssh-login-toast svg{flex:none;width:1.25rem;height:1.25rem}.ssh-login-page[data-theme=dark] .ssh-login-toast{color:#99f6e4;background:#0f2f2e;border-color:#2dd4bf57;box-shadow:0 18px 45px #00000052}.ssh-login-brand{color:#f8fafc;background:radial-gradient(circle at 16% 14%,#2dd4bf29,#0000 28rem),linear-gradient(145deg,#020617 0%,#0f172a 56%,#042f2e 140%);justify-content:center;align-items:center;min-height:100dvh;padding:clamp(3rem,7vw,7rem);display:flex;position:relative;overflow:hidden}.ssh-login-brand:after{content:"";pointer-events:none;background-image:linear-gradient(#94a3b80e 1px,#0000 1px),linear-gradient(90deg,#94a3b80e 1px,#0000 1px);background-size:44px 44px;position:absolute;top:0;bottom:0;left:0;right:0;-webkit-mask-image:linear-gradient(to bottom right,#000,#0000 78%);mask-image:linear-gradient(to bottom right,#000,#0000 78%)}.ssh-login-brand-orb{filter:blur(1px);pointer-events:none;border-radius:9999px;position:absolute}.ssh-login-brand-orb-one{border:1px solid #2dd4bf29;width:30rem;height:30rem;top:-10rem;right:-8rem;box-shadow:inset 0 0 6rem #14b8a60d}.ssh-login-brand-orb-two{border:1px solid #5eead41c;width:36rem;height:36rem;bottom:-13rem;left:-10rem}.ssh-login-brand-content{z-index:1;width:min(100%,680px);position:relative}.ssh-login-brand-mark,.ssh-login-mobile-mark{color:#5eead4;background:#14b8a621;place-items:center;display:grid;box-shadow:inset 0 0 0 1px #5eead42e}.ssh-login-brand-mark{border-radius:1.5rem;width:4.5rem;height:4.5rem;margin-bottom:2rem}.ssh-login-brand-mark svg{width:2.25rem;height:2.25rem}.ssh-login-eyebrow,.ssh-login-kicker{color:#5eead4;letter-spacing:.22em;text-transform:uppercase;margin:0;font-size:.72rem;font-weight:900}.ssh-login-brand h1{letter-spacing:-.055em;margin:.7rem 0 0;font-size:clamp(2.7rem,5vw,5rem);font-weight:900;line-height:.98}.ssh-password-setup-brand-title-line{display:block}.ssh-login-brand-description{color:#cbd5e1;max-width:39rem;margin:1.6rem 0 0;font-size:clamp(1rem,1.5vw,1.16rem);line-height:1.85}.ssh-login-features{gap:.85rem;max-width:40rem;margin-top:3rem;display:grid}.ssh-login-feature{-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);background:#0f172a75;border:1px solid #94a3b81c;border-radius:1rem;align-items:center;gap:1rem;padding:.95rem 1rem;display:flex}.ssh-login-feature-icon{color:#5eead4;background:#14b8a61c;border-radius:.8rem;flex:none;place-items:center;width:2.65rem;height:2.65rem;display:grid}.ssh-login-feature-icon svg{width:1.25rem;height:1.25rem}.ssh-login-feature-copy{gap:.15rem;min-width:0;display:grid}.ssh-login-feature-copy strong{color:#f8fafc;font-size:.92rem;font-weight:800}.ssh-login-feature-copy>span{color:#94a3b8;font-size:.8rem;line-height:1.45}.ssh-login-panel{background:var(--ssh-login-bg);justify-content:center;align-items:center;min-height:100dvh;padding:clamp(2rem,5vw,5rem);transition:background-color .18s;display:flex;position:relative}.ssh-login-theme-toggle{top:max(1.25rem, env(safe-area-inset-top));right:max(1.25rem, env(safe-area-inset-right));z-index:2;border:1px solid var(--ssh-login-border);background:var(--ssh-login-surface);width:2.75rem;height:2.75rem;color:var(--ssh-login-muted);cursor:pointer;border-radius:.9rem;place-items:center;transition:border-color .15s,background-color .15s,color .15s,transform .15s;display:grid;position:absolute;box-shadow:0 5px 16px #0f172a0f}.ssh-login-theme-toggle svg{width:1.15rem;height:1.15rem}.ssh-login-theme-toggle:hover{border-color:var(--ssh-login-border-strong);color:var(--ssh-login-accent);transform:translateY(-1px)}.ssh-login-panel-inner{flex-direction:column;justify-content:center;width:min(100%,480px);min-height:min(40rem,100dvh - 4rem);display:flex}.ssh-login-card{border:1px solid var(--ssh-login-border);background:var(--ssh-login-surface);width:100%;box-shadow:var(--ssh-login-card-shadow);border-radius:1.5rem;padding:clamp(1.6rem,4vw,2.6rem);transition:background-color .18s,border-color .18s}.ssh-login-card-heading{align-items:flex-start;gap:1rem;display:flex}.ssh-login-card-heading>div{min-width:0}.ssh-login-mobile-mark{color:#fff;background:#0f766e;border-radius:1.1rem;flex:none;width:3.5rem;height:3.5rem;display:none;box-shadow:0 12px 28px #0f766e38}.ssh-login-mobile-mark svg{width:1.8rem;height:1.8rem}.ssh-login-kicker{color:var(--ssh-login-accent);letter-spacing:.18em}.ssh-login-card h2{color:var(--ssh-login-text);letter-spacing:-.035em;margin:.65rem 0 0;font-size:clamp(1.8rem,4vw,2.25rem);font-weight:900;line-height:1.15}.ssh-login-subtitle{color:var(--ssh-login-muted);margin:.7rem 0 0;font-size:.94rem;line-height:1.6}.ssh-login-alert{color:#9f1239;background:#fff1f2;border:1px solid #fecdd3;border-radius:.85rem;margin-top:1.3rem;padding:.85rem 1rem;font-size:.82rem;font-weight:700;line-height:1.55}.ssh-login-page[data-theme=dark] .ssh-login-alert{color:#fecdd3;background:#8813372e;border-color:#f43f5e59}.ssh-login-form{gap:1.1rem;margin-top:1.6rem;display:grid}.ssh-login-field{gap:.5rem;display:grid}.ssh-login-label{color:var(--ssh-login-text);font-size:.82rem;font-weight:800}.ssh-login-input-wrap{border:2px solid var(--ssh-login-border-strong);background:var(--ssh-login-surface-soft);min-height:3.3rem;color:var(--ssh-login-muted);border-radius:.9rem;align-items:center;transition:border-color .15s,background-color .15s,box-shadow .15s;display:flex;overflow:hidden;box-shadow:inset 0 1px 2px #0f172a09}.ssh-login-input-wrap:focus-within{border-color:var(--ssh-login-accent);background:var(--ssh-login-surface);box-shadow:0 0 0 4px var(--ssh-login-accent-ring)}.ssh-login-field-icon{background:var(--ssh-login-accent-soft);width:2.15rem;height:2.15rem;color:var(--ssh-login-accent);border-radius:.65rem;flex:none;place-items:center;margin-left:.55rem;transition:background-color .15s,color .15s,box-shadow .15s;display:grid}.ssh-login-field-icon svg{width:1.05rem;height:1.05rem}.ssh-login-input-wrap:focus-within .ssh-login-field-icon{box-shadow:inset 0 0 0 1px var(--ssh-login-accent-ring)}.ssh-login-input-wrap input{width:100%;min-width:0;height:3.15rem;color:var(--ssh-login-text);font:inherit;background:0 0;border:0;outline:0;padding:0 .8rem;font-size:.94rem;font-weight:700}.ssh-login-input-wrap input::placeholder{color:var(--ssh-login-muted-soft);opacity:.82;font-weight:600}.ssh-login-input-wrap input:-webkit-autofill{-webkit-text-fill-color:var(--ssh-login-text);caret-color:var(--ssh-login-text);box-shadow:0 0 0 1000px var(--ssh-login-surface-soft) inset}.ssh-login-input-wrap input:-webkit-autofill:hover{-webkit-text-fill-color:var(--ssh-login-text);caret-color:var(--ssh-login-text);box-shadow:0 0 0 1000px var(--ssh-login-surface-soft) inset}.ssh-login-input-wrap input:-webkit-autofill:focus{-webkit-text-fill-color:var(--ssh-login-text);caret-color:var(--ssh-login-text);box-shadow:0 0 0 1000px var(--ssh-login-surface-soft) inset}.ssh-login-input-wrap:focus-within input:-webkit-autofill{box-shadow:0 0 0 1000px var(--ssh-login-surface) inset}.ssh-login-input-wrap input:disabled{cursor:wait;opacity:.68}.ssh-login-password-toggle{width:2.35rem;height:2.35rem;color:var(--ssh-login-muted);cursor:pointer;background:0 0;border:1px solid #0000;border-radius:.7rem;flex:none;place-items:center;margin-right:.45rem;transition:border-color .15s,background-color .15s,color .15s;display:grid}.ssh-login-password-toggle svg{width:1.05rem;height:1.05rem}.ssh-login-password-toggle:hover:not(:disabled){border-color:var(--ssh-login-border);background:var(--ssh-login-accent-soft);color:var(--ssh-login-accent)}.ssh-login-password-toggle:disabled{cursor:wait;opacity:.6}.ssh-login-theme-toggle:focus-visible{outline:3px solid var(--ssh-login-accent-ring);outline-offset:2px}.ssh-login-password-toggle:focus-visible{outline:3px solid var(--ssh-login-accent-ring);outline-offset:2px}.ssh-login-submit:focus-visible{outline:3px solid var(--ssh-login-accent-ring);outline-offset:2px}.ssh-login-fallback:focus-visible{outline:3px solid var(--ssh-login-accent-ring);outline-offset:2px}.ssh-login-password-hint{color:var(--ssh-login-muted);margin:-.55rem 0 -.15rem;font-size:.76rem;line-height:1.5}.ssh-password-requirements{gap:.4rem;margin:-.25rem 0 0;padding:0;list-style:none;display:grid}.ssh-password-requirement{color:var(--ssh-login-muted);align-items:center;gap:.5rem;font-size:.78rem;font-weight:700;display:flex}.ssh-password-requirement svg{width:1rem;height:1rem;color:var(--ssh-login-muted-soft);flex:none}.ssh-password-requirement.is-met,.ssh-password-requirement.is-met svg,.ssh-password-match.is-met{color:var(--ssh-login-accent)}.ssh-password-match{color:#be123c;margin:-.5rem 0 -.1rem;font-size:.78rem;font-weight:800}.ssh-password-setup-summary{background:#14b8a617;border:1px solid #5eead42e;border-radius:1rem;align-items:flex-start;gap:.85rem;max-width:40rem;margin-top:2.4rem;padding:1rem 1.1rem;display:flex}.ssh-password-setup-summary>svg{color:#5eead4;flex:none;width:1.3rem;height:1.3rem;margin-top:.1rem}.ssh-password-setup-summary>div{gap:.25rem;display:grid}.ssh-password-setup-summary strong{color:#f8fafc;font-size:.9rem}.ssh-password-setup-summary span{color:#94a3b8;font-size:.8rem;line-height:1.5}.ssh-login-caps{color:#b45309;margin:-.45rem 0 -.15rem;font-size:.78rem;font-weight:800}.ssh-login-page[data-theme=dark] .ssh-login-caps{color:#fbbf24}.ssh-login-submit{color:#fff;cursor:pointer;min-height:3.3rem;font:inherit;background:#0f766e;border:0;border-radius:.9rem;justify-content:center;align-items:center;gap:.65rem;margin-top:.25rem;font-size:.94rem;font-weight:900;transition:background-color .15s,transform .15s,box-shadow .15s;display:inline-flex;box-shadow:0 12px 26px #0f766e33}.ssh-login-submit:hover:not(:disabled){background:#115e59;transform:translateY(-1px);box-shadow:0 16px 30px #0f766e40}.ssh-login-submit:disabled{cursor:wait;opacity:.72}.ssh-login-submit-spinner,.ssh-login-probe-spinner{flex:none;animation:.75s linear infinite ssh-login-spin;display:inline-block}.ssh-login-submit-spinner{color:#fff;width:1rem;height:1rem}.ssh-login-security-note{color:var(--ssh-login-muted);align-items:flex-start;gap:.55rem;margin-top:1.2rem;font-size:.76rem;line-height:1.5;display:flex}.ssh-login-security-note svg{width:1rem;height:1rem;color:var(--ssh-login-accent);flex:none;margin-top:.06rem}.ssh-login-fallback{border:1px solid var(--ssh-login-border-strong);background:var(--ssh-login-surface);min-height:2.7rem;color:var(--ssh-login-muted);border-radius:.8rem;justify-content:center;align-items:center;margin-top:.9rem;font-size:.8rem;font-weight:800;text-decoration:none;display:flex}.ssh-login-fallback:hover{border-color:var(--ssh-login-muted-soft);background:var(--ssh-login-surface-soft);color:var(--ssh-login-text)}.ssh-login-probe{color:var(--ssh-login-muted);text-align:center;justify-items:center;gap:.6rem;padding:2.5rem;display:grid}.ssh-login-probe-spinner{width:2rem;height:2rem;color:var(--ssh-login-accent);margin-bottom:.4rem}.ssh-login-probe strong{color:var(--ssh-login-text);font-size:1rem}.ssh-login-probe>span:last-child{font-size:.82rem}.ssh-login-footer{color:var(--ssh-login-muted-soft);flex-wrap:wrap;justify-content:center;gap:.4rem;margin-top:1.5rem;font-size:.72rem;display:flex}@keyframes ssh-login-spin{to{transform:rotate(360deg)}}@media (max-width:980px){.ssh-login-page{grid-template-columns:1fr}.ssh-login-brand{display:none}.ssh-login-panel{padding:max(1.25rem, env(safe-area-inset-top)) max(1.25rem, env(safe-area-inset-right)) max(1.25rem, env(safe-area-inset-bottom)) max(1.25rem, env(safe-area-inset-left))}.ssh-login-panel-inner{min-height:calc(100dvh - 2.5rem)}.ssh-login-mobile-mark{display:grid}}@media (max-width:520px){.ssh-login-panel{background:var(--ssh-login-surface);align-items:stretch;padding:0}.ssh-login-theme-toggle{top:max(1rem, env(safe-area-inset-top));right:max(1rem, env(safe-area-inset-right));box-shadow:none}.ssh-login-panel-inner{width:100%;min-height:100dvh}.ssh-login-card{min-height:100dvh;padding:max(4.75rem, calc(env(safe-area-inset-top) + 4.25rem)) max(1.25rem, env(safe-area-inset-right)) max(2rem, env(safe-area-inset-bottom)) max(1.25rem, env(safe-area-inset-left));box-shadow:none;border:0;border-radius:0;flex-direction:column;justify-content:center;display:flex}.ssh-login-card-heading{display:block}.ssh-login-mobile-mark{margin-bottom:1.25rem}.ssh-login-input-wrap input{font-size:16px}.ssh-login-footer{display:none}.ssh-login-probe{align-content:center;min-height:100dvh}}@media (prefers-reduced-motion:reduce){.ssh-login-panel,.ssh-login-card,.ssh-login-theme-toggle,.ssh-login-submit,.ssh-login-input-wrap{transition:none}.ssh-login-submit-spinner,.ssh-login-probe-spinner{animation-duration:1.4s}}.ssh-safeshield-plan-badge{isolation:isolate;letter-spacing:.085em;white-space:nowrap;border:1px solid #0000;border-radius:999px;align-items:center;gap:.5rem;min-height:2rem;padding:.3rem .82rem .3rem .36rem;font-size:.75rem;font-weight:950;line-height:1;display:inline-flex;position:relative;overflow:hidden}.ssh-safeshield-plan-badge[data-compact=true]{min-height:1.72rem;padding:.22rem .66rem .22rem .3rem;font-size:.69rem}.ssh-safeshield-plan-badge-mark{z-index:2;border-radius:999px;flex:none;place-items:center;width:1.2rem;height:1.2rem;font-size:.76rem;line-height:1;display:grid;position:relative}.ssh-safeshield-plan-badge[data-compact=true] .ssh-safeshield-plan-badge-mark{width:1.02rem;height:1.02rem;font-size:.66rem}.ssh-safeshield-plan-badge[data-tier=free]{color:#475569;background:linear-gradient(135deg,#fff 0%,#f1f5f9 100%);border-color:#cbd5e1;box-shadow:inset 0 1px #ffffffe6}.ssh-safeshield-plan-badge[data-tier=free] .ssh-safeshield-plan-badge-mark{color:#475569;background:#e2e8f0}.ssh-safeshield-plan-badge[data-tier=pro]{color:#f0fdfa;background:linear-gradient(135deg,#115e59 0%,#0f766e 62%,#0d9488 100%);border-color:#0f766e;box-shadow:0 1px 2px #0f766e29,inset 0 1px #ffffff24}.ssh-safeshield-plan-badge[data-tier=pro] .ssh-safeshield-plan-badge-mark{color:#ccfbf1;box-shadow:none;background:#f0fdfa1f;border:1px solid #99f6e475}.ssh-safeshield-plan-badge[data-tier=ultimate]{color:#fffbeb;background:linear-gradient(135deg,#78350f 0%,#92400e 58%,#b45309 100%);border-color:#b45309;box-shadow:0 1px 2px #78350f2e,inset 0 1px #ffffff1f}.ssh-safeshield-plan-badge[data-tier=ultimate] .ssh-safeshield-plan-badge-mark{color:#fde68a;box-shadow:none;background:#fffbeb1f;border:1px solid #fde68a75}.ssh-safeshield-plan-badge[data-tier=paid]{color:#f0f9ff;background:linear-gradient(135deg,#0c4a6e 0%,#075985 58%,#0369a1 100%);border-color:#0369a1;box-shadow:0 1px 2px #0369a129,inset 0 1px #ffffff1f}.ssh-safeshield-plan-badge[data-tier=paid] .ssh-safeshield-plan-badge-mark{color:#bae6fd;box-shadow:none;background:#f0f9ff1f;border:1px solid #bae6fd75}.ssh-safeshield-plan-caption{color:#64748b;white-space:nowrap;align-items:center;gap:.34rem;font-size:.68rem;font-weight:850;display:inline-flex}.ssh-app[data-theme=dark] .ssh-safeshield-plan-badge[data-tier=free]{color:#cbd5e1;background:#1e293b;border-color:#475569}.ssh-app[data-theme=dark] .ssh-safeshield-plan-badge[data-tier=free] .ssh-safeshield-plan-badge-mark{color:#e2e8f0;background:#475569}.ssh-app[data-theme=dark] .ssh-safeshield-plan-badge[data-tier=pro]{color:#99f6e4;background:linear-gradient(135deg,#042f2e 0%,#134e4a 100%);border-color:#0f766e;box-shadow:inset 0 1px #99f6e414}.ssh-app[data-theme=dark] .ssh-safeshield-plan-badge[data-tier=pro] .ssh-safeshield-plan-badge-mark{color:#99f6e4;background:#0d94881f;border-color:#2dd4bf61}.ssh-app[data-theme=dark] .ssh-safeshield-plan-badge[data-tier=ultimate]{color:#fde68a;background:linear-gradient(135deg,#451a03 0%,#78350f 100%);border-color:#b45309;box-shadow:inset 0 1px #fde68a14}.ssh-app[data-theme=dark] .ssh-safeshield-plan-badge[data-tier=ultimate] .ssh-safeshield-plan-badge-mark{color:#fde68a;background:#92400e29;border-color:#d9770675}.ssh-app[data-theme=dark] .ssh-safeshield-plan-badge[data-tier=paid]{color:#bae6fd;background:linear-gradient(135deg,#082f49 0%,#0c4a6e 100%);border-color:#0369a1;box-shadow:inset 0 1px #bae6fd14}.ssh-app[data-theme=dark] .ssh-safeshield-plan-badge[data-tier=paid] .ssh-safeshield-plan-badge-mark{color:#bae6fd;background:#0369a124;border-color:#0ea5e95c}.ssh-app[data-theme=dark] .ssh-safeshield-plan-caption{color:#94a3b8}.ssh-safeshield-upgrade-card{background:linear-gradient(120deg,#f0fdfa 0%,#ecfeff 58%,#fff7ed 100%);border:1px solid #99f6e4;border-radius:1rem;align-items:center;gap:.85rem;margin-top:.75rem;padding:.9rem 1rem;display:flex;box-shadow:0 10px 24px #0f766e14}.ssh-safeshield-upgrade-mark{color:#fff;background:linear-gradient(135deg,#0f766e,#0891b2);border-radius:.8rem;flex:none;place-items:center;width:2.5rem;height:2.5rem;font-size:1.05rem;display:grid;box-shadow:0 8px 18px #0d948838}.ssh-safeshield-upgrade-copy{flex:1;gap:.2rem;min-width:0;display:grid}.ssh-safeshield-upgrade-meta{color:#0f766e;letter-spacing:.12em;text-transform:uppercase;flex-wrap:wrap;align-items:center;gap:.45rem;font-size:.62rem;font-weight:900;display:flex}.ssh-safeshield-upgrade-soon{letter-spacing:.06em;background:#0f766e1a;border-radius:999px;padding:.18rem .45rem}.ssh-safeshield-upgrade-copy strong{color:#0f172a;font-size:.86rem;line-height:1.35rem}.ssh-safeshield-upgrade-copy>span{color:#64748b;font-size:.74rem;font-weight:600;line-height:1.15rem}.ssh-safeshield-upgrade-action{color:#fff;background:#0f766e;border:1px solid #0f766e;border-radius:.8rem;flex:none;justify-content:center;align-items:center;gap:.4rem;min-height:2.45rem;padding:.55rem .8rem;font-size:.74rem;font-weight:900;text-decoration:none;transition:background-color .15s,border-color .15s,transform .15s;display:inline-flex}.ssh-safeshield-upgrade-action:hover{background:#115e59;border-color:#115e59;transform:translateY(-1px)}.ssh-safeshield-upgrade-action:focus-visible{outline-offset:2px;outline:3px solid #14b8a63d}.ssh-app[data-theme=dark] .ssh-safeshield-upgrade-card{background:linear-gradient(120deg,#0d948821 0%,#0f172afa 58%,#92400e1f 100%);border-color:#134e4a;box-shadow:0 12px 28px #0000002e}.ssh-app[data-theme=dark] .ssh-safeshield-upgrade-mark{background:linear-gradient(135deg,#0d9488,#0891b2);box-shadow:0 8px 20px #2dd4bf21}.ssh-app[data-theme=dark] .ssh-safeshield-upgrade-meta{color:#5eead4}.ssh-app[data-theme=dark] .ssh-safeshield-upgrade-soon{background:#2dd4bf1f}.ssh-app[data-theme=dark] .ssh-safeshield-upgrade-copy strong{color:#f8fafc}.ssh-app[data-theme=dark] .ssh-safeshield-upgrade-copy>span{color:#94a3b8}.ssh-app[data-theme=dark] .ssh-safeshield-upgrade-action{color:#ccfbf1;background:#0d948838;border-color:#2dd4bf}.ssh-app[data-theme=dark] .ssh-safeshield-upgrade-action:hover{background:#0d948852;border-color:#5eead4}@media (max-width:640px){.ssh-safeshield-upgrade-card{flex-wrap:wrap;align-items:flex-start}.ssh-safeshield-upgrade-action{justify-content:center;width:100%}}.ssh-safeshield-refresh{background:#f0fdfab3;border:1px solid #ccfbf1;border-radius:1rem;align-items:center;gap:.875rem;width:27rem;max-width:100%;margin-top:1rem;padding:.75rem .875rem;display:flex}.ssh-safeshield-refresh[data-state=error]{background:#fef2f2c7;border-color:#fecaca}.ssh-safeshield-refresh-copy{min-width:0}.ssh-safeshield-refresh-kicker,.ssh-safeshield-refresh-title,.ssh-safeshield-refresh-description,.ssh-safeshield-refresh-error-code{margin:0}.ssh-safeshield-refresh-kicker{color:#0f766e;letter-spacing:.14em;text-transform:uppercase;font-size:.66rem;font-weight:900}.ssh-safeshield-refresh[data-state=error] .ssh-safeshield-refresh-kicker{color:#b91c1c}.ssh-safeshield-refresh-title{color:#0f172a;margin-top:.25rem;font-size:.875rem;font-weight:800}.ssh-safeshield-refresh-description{color:#475569;margin-top:.25rem;font-size:.75rem;font-weight:600;line-height:1.25rem}.ssh-safeshield-refresh-error-code{overflow-wrap:anywhere;color:#64748b;margin-top:.375rem;font-size:.68rem;font-weight:600}.ssh-safeshield-refresh-error-code code{font:inherit}.ssh-safeshield-refresh-donut{color:#0d9488;place-items:center;width:3.35rem;min-width:3.35rem;height:3.35rem;display:grid;position:relative}.ssh-safeshield-refresh-donut[data-state=error]{color:#dc2626}.ssh-safeshield-refresh-loader-svg{transform-origin:50%;width:100%;height:100%;animation:.82s linear infinite ssh-safeshield-refresh-loader-rotate;position:absolute;top:0;bottom:0;left:0;right:0;transform:rotate(-90deg)}.ssh-safeshield-refresh-loader-svg-error{animation:none}.ssh-safeshield-refresh-loader-track{color:#cbd5e1;stroke:#cbd5e1}.ssh-safeshield-refresh-loader-arc{color:#0f172a;stroke:#0f172a;filter:drop-shadow(0 0 .06rem #0f172a47)}.ssh-safeshield-refresh-donut[data-state=error] .ssh-safeshield-refresh-loader-track{color:#dc2626;stroke:#dc2626}.ssh-safeshield-refresh-donut-label{z-index:1;color:#0f766e;font-variant-numeric:tabular-nums;font-size:.68rem;font-weight:900;position:relative}.ssh-app[data-theme=dark] .ssh-safeshield-refresh{background:#0f766e1f;border-color:#134e4a}.ssh-app[data-theme=dark] .ssh-safeshield-refresh[data-state=error]{background:#7f1d1d2e;border-color:#7f1d1d}.ssh-app[data-theme=dark] .ssh-safeshield-refresh-title{color:#f8fafc}.ssh-app[data-theme=dark] .ssh-safeshield-refresh-description,.ssh-app[data-theme=dark] .ssh-safeshield-refresh-error-code{color:#94a3b8}.ssh-app[data-theme=dark] .ssh-safeshield-refresh-loader-track{color:#334155;stroke:#334155}.ssh-app[data-theme=dark] .ssh-safeshield-refresh-loader-arc{color:#fff;stroke:#fff;filter:drop-shadow(0 0 .08rem #ffffff8c)}.ssh-app[data-theme=dark] .ssh-safeshield-refresh-donut-label{color:#5eead4}.ssh-app[data-theme=dark] .ssh-safeshield-refresh-donut[data-state=error] .ssh-safeshield-refresh-loader-track{color:#ef4444;stroke:#ef4444}@keyframes ssh-safeshield-refresh-loader-rotate{to{transform:rotate(270deg)}}@media (prefers-reduced-motion:reduce){.ssh-safeshield-refresh-loader-svg{animation:none}}.ssh-dashboard-account-banner{background:#f0fdfa;border-color:#99e8df}.ssh-dashboard-account-banner-icon{color:#0f766e;background:#ccfbf1}.ssh-dashboard-account-banner-title{color:#0f172a}.ssh-dashboard-account-banner-description{color:#475569}.ssh-dashboard-account-banner-badge{color:#92400e;background:#fef3c7}.ssh-dashboard-account-banner-action{color:#fff;background:#0f766e}.ssh-dashboard-account-banner-action:hover{color:#fff;background:#115e59}.ssh-dashboard-account-banner-action:focus-visible{outline-color:#0d9488}.ssh-app[data-theme=dark] .ssh-dashboard-account-banner{background:#102927;border-color:#115e59}.ssh-app[data-theme=dark] .ssh-dashboard-account-banner-icon{color:#99f6e4;background:#134e4a}.ssh-app[data-theme=dark] .ssh-dashboard-account-banner-title{color:#f1f5f9}.ssh-app[data-theme=dark] .ssh-dashboard-account-banner-description{color:#cbd5e1}.ssh-app[data-theme=dark] .ssh-dashboard-account-banner-badge{color:#fde68a;background:#78350f}.ssh-app[data-theme=dark] .ssh-dashboard-account-banner-action{color:#fff;background:#0f766e}.ssh-app[data-theme=dark] .ssh-dashboard-account-banner-action:hover{background:#0d9488}.ssh-account-nav-dot{width:10px;height:10px;box-shadow:0 0 0 2px var(--ssh-account-dot-ring,#fff);background-color:#d97706;border-radius:50%;flex:none;margin-left:auto;display:block}.ssh-account-nav-dot-collapsed{margin-left:0;position:absolute;top:5px;right:7px}.ssh-app[data-theme=dark] .ssh-account-nav-dot{--ssh-account-dot-ring:#0f172a;background-color:#fbbf24}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-ease{syntax:"*";inherits:false}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.5}}@keyframes bounce{0%,to{animation-timing-function:cubic-bezier(.8,0,1,1);transform:translateY(-25%)}50%{animation-timing-function:cubic-bezier(0,0,.2,1);transform:none}} /*$vite$:1*/ \ No newline at end of file diff --git a/luci-app-smartsafehub/frontend/demo-dist/demo.js b/luci-app-smartsafehub/frontend/demo-dist/demo.js index e65e2d2e1..6445716ae 100644 --- a/luci-app-smartsafehub/frontend/demo-dist/demo.js +++ b/luci-app-smartsafehub/frontend/demo-dist/demo.js @@ -1,4 +1,4 @@ -var e,t,n,r,i,a,o,s,c,l,u={},d=[],f=/^m(i|n|o|s|text|space)$/,p=Array.isArray,m=d.slice,h=Object.assign;function g(e){e&&e.parentNode&&e.remove()}function _(n,r,i,a,o){var s={type:n,props:r,key:i,ref:a,__k:null,__:null,__b:0,__e:null,__c:null,constructor:void 0,__v:o||++t,__i:-1,__u:0};return!o&&e.vnode&&e.vnode(s),s}function v(e){return e.children}function y(e,t){this.props=e,this.context=t,this.__g=0}function b(e,t){if(t==null)return e.__?b(e.__,e.__i+1):null;for(var n;tr&&n.sort(a),t=n.shift(),r=n.length,8&t.__g&&(s=void 0,c=void 0,l=(c=(o=t).__v).__e,u=[],d=[],(f=o.__P)&&((s=h({constructor:void 0},c)).__v=c.__v+1,e.vnode&&e.vnode(s),M(f,s,c,o.__n,f.namespaceURI,32&c.__u?[l]:null,u,l||b(c),32&c.__u,d),s.__v=c.__v,s.__.__k[s.__i]=s,te(u,s,d),c.__=c.__e=null,s.__e!=l&&x(s)))}finally{n.length=i=0}}function w(e,t,n,r,i,a,o,s,c,l,f){var p,m,h,g,_,v,y=r.__k||d,b=t.length;for(c=T(n,t,y,c,b),p=0;pc?x--:x++,S=!0)):(~l||(i>g?x--:i>1]+!!u){for(i=n-1,a=n+1;i>=0||a=0?i--:a++])&&!(2&l.__u)&&s==l.key&&c==l.type)return o}return-1}function k(e,t,n){n??(n=``),t[0]==`-`?e.setProperty(t,n):e[t]=n}function A(e,t,n,r,i){var a,o;n:if(t==`style`){if(typeof n==`string`)e.style.cssText=n;else{if(typeof r==`string`&&(e.style.cssText=r=``),r)for(t in r)n&&t in n||k(e.style,t,``);if(n)for(t in n)r&&n[t]==r[t]||k(e.style,t,n[t])}}else if(t[0]==`o`&&t[1]==`n`)(e.__e||(e.__e={}))[t]=n,n&&r||(o=l[t]||(l[t]=j(t)),(e.__a||(e.__a={}))[t]=c,a=t!=(t=t.replace(s,`$1`)),(t=t.slice(2))[0]<`a`&&(t=t.toLowerCase()),n?e.addEventListener(t,o,a):e.removeEventListener(t,o,a));else{if(i==`http://www.w3.org/2000/svg`)t=t.replace(/xlink(H|:h)/,`h`).replace(/sName$/,`s`);else if(t!=`width`&&t!=`height`&&t!=`href`&&t!=`list`&&t!=`form`&&t!=`tabIndex`&&t!=`download`&&t!=`rowSpan`&&t!=`colSpan`&&t!=`role`&&t!=`popover`&&t in e)try{e[t]=n??``;break n}catch{}typeof n==`function`||(n==null||!1===n&&t[4]!=`-`?e.removeAttribute(t):e.setAttribute(t,t==`popover`&&n==1?``:n))}}function j(t){return function(n){if(this.__e){var r=this.__e[t];if(n[o]==null)n[o]=c++;else if(n[o]=n.__.length&&n.__.push({}),n.__[e]}function F(e){return pe=1,Ce(Fe,e)}function Ce(e,t,n){var r=Se(le++,2);if(r.t=e,!r.__c&&(r.__=[n?n(t):Fe(void 0,t),function(e){var t=r.__N?r.__N[0]:r.__[0],n=r.t(t,e);fe(t,n)||(r.__N=[n,r.__[1]],r.__c.setState({}))}],r.__c=N,!N.__f)){N.__f=!0;var i=N.shouldComponentUpdate;N.shouldComponentUpdate=function(e,t,n){var r=this.__H;if(!r)return!0;var a=!1,o=this.props!=e;if(r.__.some(function(e){e.__N&&(a=!0,fe(e.__[0],e.__N[0])||(o=!0))}),i){var s=i.call(this,e,t,n);return a?s||o:s}return!a||o}}return r.__}function I(e,t){var n=Se(le++,3);!P.__s&&Pe(n.__H,t)&&(n.__P=!0,n.__=e,n.u=t,N.__H.__h.push(n))}function we(e){return pe=5,Te(function(){return{current:e}},[])}function Te(e,t){var n=Se(le++,7);return Pe(n.__H,t)&&(n.__=e(),n.__H=t),n.__}function Ee(e,t){return pe=8,Te(function(){return e},t)}function De(){var e=Se(le++,11);if(!e.__){for(var t=N.__v;!t.__m&&t.__;)t=t.__;var n=t.__m||(t.__m=[0,0]);e.__=`P`+n[0]+`-`+n[1]++}return e.__}function Oe(){var e;do{for(;e=he.shift();)try{Me(e)}catch(t){P.__e(t,{__:(e=e.__P)&&e.__v})}for(;e=me.shift();){var t=e.__H;if(e.__P&&t)try{t.__h.some(Me),t.__h.some(Ne),t.__h=[]}catch(n){t.__h=[],P.__e(n,e.__v)}}}while(he.length)}P.__b=function(e){N=null,ge&&ge(e)},P.__=function(e,t){e&&t.__k&&t.__k.__m&&(e.__m=t.__k.__m),xe&&xe(e,t)},P.__r=function(e){_e&&_e(e),le=0;var t=(N=e.__c).__H;t&&(ue==N?N.__h=[]:(t.__h.some(Me),t.__h.some(Ne),le=0),t.__h=[],t.__.some(function(e){e.__N&&(e.__=e.__N),e.u=e.__N=void 0})),ue=N},P.diffed=function(e){ve&&ve(e);var t=e.__c;t&&t.__H&&(t.__H.__h.length&&je(me.push(t)),t.__H.__.some(function(e){e.u&&(e.__H=e.u)})),ue=N=null},P.__c=function(e,t){t.some(function(e){try{e.__h.some(Me),e.__h=e.__h.filter(function(e){return!e.__||Ne(e)})}catch(n){t.some(function(e){e.__h&&(e.__h=[])}),t=[],P.__e(n,e.__v)}}),ye&&ye(e,t)},P.unmount=function(e){be&&be(e);var t,n,r=e.__c;r&&r.__H&&(r.__H.__.some(function(r){try{if(r.__P&&r.__c){if(n===void 0){for(n=e.__;n&&(!n.__c||!n.__c.__P);)n=n.__;n=n&&n.__c}r.__P=n,je(he.push(r))}else Me(r)}catch(e){t=e}}),r.__H=void 0,t&&P.__e(t,r.__v))};var ke=typeof requestAnimationFrame==`function`;function Ae(e){var t,n=function(){clearTimeout(r),ke&&cancelAnimationFrame(t),setTimeout(e)},r=setTimeout(n,35);ke&&(t=requestAnimationFrame(n))}function je(e){e!=1&&de==P.requestAnimationFrame||((de=P.requestAnimationFrame)||Ae)(Oe)}function Me(e){var t=N,n=e.__c;typeof n==`function`&&(e.__c=void 0,n()),N=t}function Ne(e){var t=N;e.__c=e.__(),N=t}function Pe(e,t){return!e||e.length!=t.length||t.some(function(t,n){return!fe(t,e[n])})}function Fe(e,t){return typeof t==`function`?t(e):t}var Ie=Object.fromEntries([{route:`home`,hash:`#home`,label:`대시보드`,title:`대시보드`,description:`네트워크, 장치와 시스템 상태를 한눈에 확인합니다.`},{route:`activity`,hash:`#activity`,label:`최근 활동`,title:`최근 활동`,description:`현재 부팅 이후 인터넷, 보호, 업데이트와 진단 상태 변화를 확인합니다.`},{route:`network`,hash:`#network`,label:`네트워크`,title:`네트워크`,description:`인터넷 연결과 내부 네트워크 주소, DHCP 및 네트워크 충돌을 한 곳에서 관리합니다.`},{route:`wifi`,hash:`#wifi`,label:`Wi-Fi`,title:`Wi-Fi`,description:`기본 무선 네트워크의 이름, 보안과 사용 상태를 관리합니다.`},{route:`iptv`,hash:`#iptv`,label:`IPTV`,title:`IPTV`,description:`SK Broadband와 LG U+ 멀티캐스트 IPTV를 설정합니다.`},{route:`devices`,hash:`#devices`,label:`연결된 기기`,title:`연결된 기기`,description:`네트워크에서 확인된 기기와 연결 방식을 살펴봅니다.`},{route:`safeshield`,hash:`#safeshield`,label:`SafeShield`,title:`SafeShield`,description:`DNS 보호 상태를 확인하고 차단 목록을 관리합니다.`},{route:`rules`,hash:`#rules`,label:`사용자 규칙`,title:`사용자 규칙`,description:`직접 허용하거나 차단할 도메인을 관리합니다.`},{route:`account`,hash:`#account`,label:`SmartSafeHub 계정`,title:`SmartSafeHub 계정`,description:`이 기기의 SmartSafeHub 계정 연결 상태와 구독 플랜을 확인합니다.`},{route:`system`,hash:`#system`,label:`업데이트`,title:`업데이트`,description:`기기의 펌웨어와 관리 소프트웨어 업데이트를 관리합니다.`},{route:`settings`,hash:`#settings`,label:`설정`,title:`설정`,description:`시스템 상태와 시간대를 확인하고 장치 관리 및 진단 기능을 설정합니다.`}].map(e=>[e.route,e])),Le=[`home`,`activity`,`network`,`wifi`,`devices`,`safeshield`,`rules`,`system`],Re=`smartsafehub.theme`;function ze(){if(typeof window>`u`)return`light`;try{let e=window.localStorage.getItem(Re);if(e===`dark`||e===`light`)return e}catch{}return window.matchMedia(`(prefers-color-scheme: dark)`).matches?`dark`:`light`}function Be(e){try{window.localStorage.setItem(Re,e)}catch{}}function Ve(e){let t=document.querySelector(`meta[name="theme-color"]`);t&&(t.content=e===`dark`?`#0f172a`:`#f8fafc`),document.documentElement.style.colorScheme=e,document.body.style.backgroundColor=e===`dark`?`#020617`:`#f8fafc`}var He=0;Array.isArray;function L(t,n,r,i,a,o){n||(n={});var s,c,l=n;if(`ref`in l&&typeof t!=`function`)for(c in l={},n)c==`ref`?s=n[c]:l[c]=n[c];var u={type:t,props:l,key:r,ref:s,__k:null,__:null,__b:0,__e:null,__c:null,constructor:void 0,__v:--He,__i:-1,__u:0};return(a||o)&&(u.__source=a,u.__self=o),e.vnode&&e.vnode(u),u}function R({children:e,title:t,...n}){return L(`svg`,{"aria-hidden":!t||void 0,fill:`none`,role:t?`img`:void 0,stroke:`currentColor`,"stroke-linecap":`round`,"stroke-linejoin":`round`,"stroke-width":`1.8`,viewBox:`0 0 24 24`,...n,children:[t?L(`title`,{children:t}):null,e]})}function Ue(e){return L(R,{...e,children:[L(`path`,{d:`M12 3 5 6v5c0 4.6 2.8 8.1 7 10 4.2-1.9 7-5.4 7-10V6l-7-3Z`}),L(`path`,{d:`m9 12 2 2 4-4`})]})}function We(e){return L(R,{...e,children:[L(`rect`,{height:`7`,rx:`2`,width:`18`,x:`3`,y:`13`}),L(`path`,{d:`M7 16.5h.01M11 16.5h.01M7 10a7 7 0 0 1 10 0M9.5 7.5a3.5 3.5 0 0 1 5 0`})]})}function Ge(e){return L(R,{...e,children:[L(`rect`,{height:`11`,rx:`2`,width:`16`,x:`4`,y:`3`}),L(`path`,{d:`M8 21h8M12 14v7`})]})}function Ke(e){return L(R,{...e,children:[L(`path`,{d:`m3 11 9-8 9 8`}),L(`path`,{d:`M5 10v10h14V10M9 20v-6h6v6`})]})}function qe(e){return L(R,{...e,children:[L(`circle`,{cx:`12`,cy:`8`,r:`4`}),L(`path`,{d:`M4.5 21a7.5 7.5 0 0 1 15 0`})]})}function Je(e){return L(R,{...e,children:[L(`path`,{d:`M12 16V4`}),L(`path`,{d:`m7 9 5-5 5 5`}),L(`path`,{d:`M5 14v5h14v-5`})]})}function Ye(e){return L(R,{...e,children:[L(`circle`,{cx:`12`,cy:`12`,r:`3`}),L(`path`,{d:`M19.4 15a1.7 1.7 0 0 0 .3 1.9l.1.1-2.8 2.8-.1-.1a1.7 1.7 0 0 0-1.9-.3 1.7 1.7 0 0 0-1 1.6v.2h-4V21a1.7 1.7 0 0 0-1-1.6 1.7 1.7 0 0 0-1.9.3l-.1.1L4.2 17l.1-.1a1.7 1.7 0 0 0 .3-1.9A1.7 1.7 0 0 0 3 14H2.8v-4H3a1.7 1.7 0 0 0 1.6-1 1.7 1.7 0 0 0-.3-1.9L4.2 7 7 4.2l.1.1a1.7 1.7 0 0 0 1.9.3A1.7 1.7 0 0 0 10 3V2.8h4V3a1.7 1.7 0 0 0 1 1.6 1.7 1.7 0 0 0 1.9-.3l.1-.1L19.8 7l-.1.1a1.7 1.7 0 0 0-.3 1.9 1.7 1.7 0 0 0 1.6 1h.2v4H21a1.7 1.7 0 0 0-1.6 1Z`})]})}function Xe(e){return L(R,{...e,children:[L(`path`,{d:`M5 9.5a10 10 0 0 1 14 0M8 13a6 6 0 0 1 8 0M11 16.5a2 2 0 0 1 2 0`}),L(`path`,{d:`M12 20h.01`})]})}function Ze(e){return L(R,{...e,children:[L(`path`,{d:`M4 4h6v6H4zM14 4h6v6h-6zM4 14h6v6H4z`}),L(`path`,{d:`M6.5 6.5h1M16.5 6.5h1M6.5 16.5h1`}),L(`path`,{d:`M14 14h2v2h-2zM18 14h2v2h-2zM16 16h2v2h-2zM18 18h2v2h-2zM14 18h1`})]})}function Qe(e){return L(R,{...e,children:[L(`path`,{d:`M7 3v4M17 3v4M5 7h14v4a7 7 0 0 1-14 0V7Z`}),L(`path`,{d:`M12 18v3`})]})}function $e(e){return L(R,{...e,children:[L(`circle`,{cx:`12`,cy:`12`,r:`9`}),L(`path`,{d:`M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18`})]})}function et(e){return L(R,{...e,children:[L(`rect`,{height:`13`,rx:`2`,width:`18`,x:`3`,y:`6`}),L(`path`,{d:`m8 2 4 4 4-4M8 22h8`})]})}function tt(e){return L(R,{...e,children:[L(`circle`,{cx:`12`,cy:`12`,r:`9`}),L(`path`,{d:`M12 7v5l3 2`})]})}function nt(e){return L(R,{...e,children:[L(`rect`,{height:`14`,rx:`2`,width:`14`,x:`5`,y:`5`}),L(`path`,{d:`M9 9h6v6H9zM9 2v3M15 2v3M9 19v3M15 19v3M2 9h3M2 15h3M19 9h3M19 15h3`})]})}function z(e){return L(R,{...e,children:[L(`path`,{d:`M20 11a8 8 0 1 0-2.3 5.7`}),L(`path`,{d:`M20 4v7h-7`})]})}function rt(e){return L(R,{...e,children:[L(`path`,{d:`M12 2v10`}),L(`path`,{d:`M18.4 6.6a8 8 0 1 1-12.8 0`})]})}function it(e){return L(R,{...e,children:[L(`path`,{d:`M12 3v12`}),L(`path`,{d:`m7 10 5 5 5-5`}),L(`path`,{d:`M5 21h14`})]})}function at(e){return L(R,{...e,children:[L(`ellipse`,{cx:`12`,cy:`5`,rx:`8`,ry:`3`}),L(`path`,{d:`M4 5v6c0 1.7 3.6 3 8 3s8-1.3 8-3V5M4 11v6c0 1.7 3.6 3 8 3s8-1.3 8-3v-6`})]})}function ot(e){return L(R,{...e,children:[L(`circle`,{cx:`8`,cy:`15`,r:`4`}),L(`path`,{d:`m11 12 8-8M15 8l2 2M17 6l2 2`})]})}function st(e){return L(R,{...e,children:[L(`path`,{d:`M2.5 12s3.5-6 9.5-6 9.5 6 9.5 6-3.5 6-9.5 6-9.5-6-9.5-6Z`}),L(`circle`,{cx:`12`,cy:`12`,r:`2.5`})]})}function ct(e){return L(R,{...e,children:[L(`path`,{d:`m3 3 18 18`}),L(`path`,{d:`M10.6 6.2A8.8 8.8 0 0 1 12 6c6 0 9.5 6 9.5 6a15.5 15.5 0 0 1-2.6 3.2`}),L(`path`,{d:`M6.2 6.3C3.8 8 2.5 12 2.5 12s3.5 6 9.5 6a9.8 9.8 0 0 0 3.1-.5`}),L(`path`,{d:`M10.3 10.3a2.5 2.5 0 0 0 3.4 3.4`})]})}function lt(e){return L(R,{...e,children:[L(`rect`,{height:`17`,rx:`2`,width:`18`,x:`3`,y:`4`}),L(`path`,{d:`M8 2v4M16 2v4M3 9h18`})]})}function B(e){return L(R,{...e,children:[L(`circle`,{cx:`12`,cy:`12`,r:`9`}),L(`path`,{d:`m8 12 2.5 2.5L16 9`})]})}function V(e){return L(R,{...e,children:[L(`path`,{d:`M10.3 4.2 2.7 17.5A2 2 0 0 0 4.4 20h15.2a2 2 0 0 0 1.7-2.5L13.7 4.2a2 2 0 0 0-3.4 0Z`}),L(`path`,{d:`M12 9v4M12 17h.01`})]})}function ut(e){return L(R,{...e,children:L(`path`,{d:`M12 5v14M5 12h14`})})}function dt(e){return L(R,{...e,children:L(`path`,{d:`M4 7h16M9 7V4h6v3M7 7l1 13h8l1-13M10 11v5M14 11v5`})})}function ft(e){return L(R,{...e,children:[L(`circle`,{cx:`11`,cy:`11`,r:`7`}),L(`path`,{d:`m20 20-4-4`})]})}function pt(e){return L(R,{...e,children:[L(`path`,{d:`M8 6h12M8 12h12M8 18h12`}),L(`path`,{d:`M4 6h.01M4 12h.01M4 18h.01`})]})}function mt(e){return L(R,{...e,children:[L(`path`,{d:`M10 5H6a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h4`}),L(`path`,{d:`M14 8l4 4-4 4M8 12h10`})]})}function ht(e){return L(R,{...e,children:L(`path`,{d:`M4 7h16M4 12h16M4 17h16`})})}function gt(e){return L(R,{...e,children:L(`path`,{d:`m6 9 6 6 6-6`})})}function _t(e){return L(R,{...e,children:L(`path`,{d:`m6 6 12 12M18 6 6 18`})})}function vt(e){return L(R,{...e,children:L(`path`,{d:`m15 18-6-6 6-6`})})}function yt(e){return L(R,{...e,children:L(`path`,{d:`m9 18 6-6-6-6`})})}function bt(e){return L(R,{...e,children:L(`path`,{d:`M20.7 13.2A8.5 8.5 0 1 1 10.8 3.3 6.7 6.7 0 0 0 20.7 13.2Z`})})}function xt(e){return L(R,{...e,children:[L(`circle`,{cx:`12`,cy:`12`,r:`4`}),L(`path`,{d:`M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41`})]})}function St({description:e,loading:t,onRefresh:n,onToggleTheme:r,refreshing:i,theme:a,title:o}){return L(`header`,{class:`ssh-product-hero border-b border-slate-200 bg-white px-4 py-4 sm:px-6 md:h-[4.5rem] md:py-0 lg:px-8 xl:px-10`,children:L(`div`,{class:`flex w-full items-center justify-between gap-4 md:h-full`,children:[L(`div`,{class:`min-w-0`,children:[L(`p`,{class:`m-0 text-[0.68rem] font-extrabold uppercase tracking-[0.18em] text-teal-700`,children:`SmartSafeHub`}),L(`div`,{class:`mt-1 flex min-w-0 items-baseline gap-3`,children:[L(`h1`,{class:`m-0 truncate text-xl font-black tracking-tight text-slate-950 sm:text-2xl`,children:o}),L(`p`,{class:`m-0 hidden truncate text-sm text-slate-500 lg:block`,children:e})]})]}),L(`div`,{class:`flex shrink-0 items-center gap-2`,children:[L(`button`,{"aria-label":a===`dark`?`라이트 모드로 전환`:`다크 모드로 전환`,class:`ssh-product-header-action hidden h-10 w-10 shrink-0 items-center justify-center rounded-xl border border-slate-200 bg-white text-slate-600 shadow-sm transition hover:border-teal-300 hover:text-teal-700 focus:outline-none focus-visible:ring-4 focus-visible:ring-teal-100 md:inline-flex`,onClick:r,title:a===`dark`?`라이트 모드`:`다크 모드`,type:`button`,children:L(a===`dark`?xt:bt,{class:`size-5`})}),L(`button`,{"aria-busy":i,"aria-label":i?`새로고침 중`:`현재 화면 새로고침`,class:`ssh-product-header-action hidden h-10 w-10 shrink-0 items-center justify-center rounded-xl border border-slate-200 bg-white shadow-sm transition hover:border-teal-300 hover:text-teal-700 focus:outline-none focus-visible:ring-4 focus-visible:ring-teal-100 disabled:cursor-wait disabled:opacity-60 md:inline-flex ${i?`text-teal-700`:`text-slate-600`}`,disabled:t||i,onClick:n,title:i?`새로고침 중`:`새로고침`,type:`button`,children:i?L(z,{class:`size-5 animate-spin`}):L(z,{class:`size-5`})})]})]})})}var Ct=`/cgi-bin/luci`;function wt(){let e=window.location.pathname,t=e.indexOf(`/cgi-bin/luci`);return t>=0?e.slice(0,t+13):Ct}function Tt(e){let t=e.startsWith(`/`)?e:`/${e}`;return`${wt()}${t}`}function Et(e){return Tt(e)}var Dt=[{label:`Overview`,routes:[`home`,`activity`]},{label:`Network`,routes:[`network`,`wifi`,`iptv`,`devices`]},{label:`Security`,routes:[`safeshield`,`rules`]},{label:`System`,routes:[`account`,`system`,`settings`]}];function Ot({route:e}){switch(e){case`home`:return L(Ke,{class:`size-5`});case`activity`:return L(tt,{class:`size-5`});case`network`:return L($e,{class:`size-5`});case`wifi`:return L(Xe,{class:`size-5`});case`iptv`:return L(et,{class:`size-5`});case`devices`:return L(Ge,{class:`size-5`});case`safeshield`:return L(Ue,{class:`size-5`});case`rules`:return L(qe,{class:`size-5`});case`account`:return L(ot,{class:`size-5`});case`system`:return L(Je,{class:`size-5`});case`settings`:return L(Ye,{class:`size-5`})}}function kt({theme:e}){return L(e===`dark`?xt:bt,{class:`size-5`})}function At(e){return e===`dark`?`라이트 모드`:`다크 모드`}function jt(e,t=!1){return`relative flex min-h-11 min-w-0 items-center rounded-xl py-2.5 text-sm font-extrabold no-underline transition ${t?`justify-center px-2`:`gap-3 px-3`} ${e?`bg-teal-50 text-teal-800 ring-1 ring-inset ring-teal-200`:`text-slate-600 hover:bg-slate-50 hover:text-slate-950`}`}function Mt({accountRegistered:e,collapsed:t=!1,route:n,updateCount:r,demo:i=!1}){return L(`div`,{class:t?`space-y-3`:`space-y-5`,children:Dt.filter(e=>!i||e.routes.some(e=>Le.includes(e))).map((a,o)=>L(`section`,{"aria-label":a.label,class:t&&o>0?`border-t border-slate-100 pt-3`:void 0,children:[t?null:L(`p`,{class:`mb-2 px-3 text-[0.65rem] font-black uppercase tracking-[0.18em] text-slate-400`,children:a.label}),L(`div`,{class:`space-y-1`,children:a.routes.filter(e=>!i||Le.includes(e)).map(i=>{let a=Ie[i],o=n===i;return L(`a`,{"aria-current":o?`page`:void 0,"aria-label":i===`account`&&e===!1?`${a.label} (계정 연결 필요)`:t?a.label:void 0,class:jt(o,t),href:a.hash,title:i===`account`&&e===!1?`SmartSafeHub 계정 연결 필요`:t?a.label:void 0,children:[L(`span`,{class:`grid size-9 shrink-0 place-items-center rounded-lg ${o?`bg-teal-700 text-white`:`bg-slate-100 text-slate-500`}`,children:L(Ot,{route:i})}),t?null:L(`span`,{class:`flex min-w-0 flex-1 items-center gap-2`,children:[L(`span`,{class:`truncate`,children:a.label}),i===`iptv`?L(`span`,{class:`rounded-full border border-amber-300 bg-amber-100 px-1.5 py-0.5 text-[9px] font-black uppercase tracking-[0.08em] text-amber-800`,children:`Beta`}):null]}),t&&i===`iptv`?L(`span`,{"aria-label":`Beta 기능`,class:`absolute right-0.5 top-0.5 inline-flex size-5 items-center justify-center rounded-full border border-amber-300 bg-amber-100 text-[13px] font-extrabold leading-none text-amber-900 shadow-sm ring-2 ring-white`,children:`β`}):null,i===`account`&&e===!1?L(`span`,{"aria-label":`계정 연결 필요`,class:`ssh-account-nav-dot ${t?`ssh-account-nav-dot-collapsed`:``}`,"aria-hidden":`true`,title:`계정 연결 필요`}):null,i===`system`&&r>0?t?L(`span`,{"aria-label":`${r}개의 업데이트 유형`,class:`ssh-update-nav-badge`,"data-collapsed":`true`,children:r}):L(`span`,{class:`ssh-update-nav-badge`,"data-collapsed":`false`,children:r}):null]},i)})})]},a.label))})}function Nt({accountRegistered:e,collapsed:t,loading:n,onRefresh:r,onToggleCollapsed:i,onToggleTheme:a,refreshing:o,route:s,theme:c,title:l,updateCount:u,demo:d=!1}){let[f,p]=F(!1),m=At(c);return I(()=>{p(!1)},[s]),I(()=>{if(!f)return;let e=e=>{e.key===`Escape`&&p(!1)};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[f]),L(v,{children:[L(`nav`,{"aria-label":`SmartSafeHub 모바일 메뉴`,class:`ssh-mobile-navigation sticky top-0 z-40 border-b border-slate-200 bg-white/95 px-4 shadow-sm shadow-slate-900/5 backdrop-blur md:hidden`,children:[L(`div`,{class:`flex min-h-16 items-center justify-between gap-3`,children:[L(`a`,{class:`flex min-w-0 items-center gap-3 no-underline`,href:`#home`,children:[L(`span`,{class:`grid size-10 shrink-0 place-items-center rounded-xl bg-slate-950 text-teal-300`,children:L(Ue,{class:`size-5`})}),L(`span`,{class:`min-w-0`,children:[L(`strong`,{class:`block truncate text-sm font-black text-slate-950`,children:`SmartSafeHub`}),L(`span`,{class:`block truncate text-xs font-semibold text-slate-500`,children:l})]})]}),L(`div`,{class:`flex shrink-0 items-center gap-2`,children:[L(`button`,{"aria-label":`${m}로 전환`,class:`inline-flex size-11 shrink-0 items-center justify-center rounded-xl border border-slate-200 bg-white text-slate-600 transition hover:border-slate-300 hover:bg-slate-50 hover:text-teal-700 focus:outline-none focus-visible:ring-4 focus-visible:ring-teal-100`,onClick:a,title:m,type:`button`,children:L(kt,{theme:c})}),L(`button`,{"aria-busy":o,"aria-label":o?`새로고침 중`:`현재 화면 새로고침`,class:`inline-flex size-11 shrink-0 items-center justify-center rounded-xl border border-slate-200 bg-white transition hover:border-slate-300 hover:bg-slate-50 hover:text-teal-700 focus:outline-none focus-visible:ring-4 focus-visible:ring-teal-100 disabled:cursor-wait disabled:opacity-60 ${o?`text-teal-700`:`text-slate-600`}`,disabled:n||o,onClick:r,title:o?`새로고침 중`:`새로고침`,type:`button`,children:o?L(z,{class:`size-5 animate-spin`}):L(z,{class:`size-5`})}),L(`button`,{"aria-controls":`smartsafehub-mobile-menu`,"aria-expanded":f,"aria-label":f?`모바일 메뉴 닫기`:`모바일 메뉴 열기`,class:`inline-flex size-11 shrink-0 items-center justify-center rounded-xl border border-slate-200 bg-white text-slate-700 transition hover:bg-slate-50 focus:outline-none focus-visible:ring-4 focus-visible:ring-teal-100`,onClick:()=>p(e=>!e),type:`button`,children:L(f?_t:ht,{class:`size-5`})})]})]}),f?L(`div`,{class:`ssh-mobile-menu border-t border-slate-100 py-4`,id:`smartsafehub-mobile-menu`,children:[L(Mt,{accountRegistered:e,demo:d,route:s,updateCount:u}),d?null:L(`div`,{class:`mt-5 space-y-1 border-t border-slate-100 pt-4`,children:L(`a`,{class:`${jt(!1)} text-rose-700 hover:bg-rose-50 hover:text-rose-800`,href:Et(`/admin/logout`),children:[L(`span`,{class:`grid size-9 shrink-0 place-items-center rounded-lg bg-rose-50 text-rose-600`,children:L(mt,{class:`size-5`})}),`로그아웃`]})})]}):null]}),L(`aside`,{class:`hidden min-h-screen border-r border-slate-200 bg-white md:flex md:flex-col`,"data-collapsed":t?`true`:`false`,children:L(`div`,{class:`sticky top-0 flex h-screen flex-col`,children:[L(`div`,{class:`flex h-[4.5rem] min-h-[4.5rem] shrink-0 items-center border-b border-slate-100 ${t?`justify-center px-2`:`gap-3 px-4`}`,children:[L(`a`,{"aria-label":`SmartSafeHub Dashboard`,class:`flex items-center no-underline ${t?`justify-center`:`min-w-0 flex-1 gap-3`}`,href:`#home`,title:t?`SmartSafeHub`:void 0,children:[L(`span`,{class:`grid size-10 shrink-0 place-items-center rounded-xl bg-slate-950 text-teal-300 shadow-sm shadow-slate-950/10`,children:L(Ue,{class:`size-5`})}),t?null:L(`div`,{class:`min-w-0`,children:[L(`strong`,{class:`block truncate text-sm font-black tracking-tight text-slate-950`,children:`SmartSafeHub`}),L(`span`,{class:`block text-[0.68rem] font-bold uppercase tracking-[0.14em] text-slate-400`,children:`Home Gateway`})]})]}),L(`button`,{"aria-controls":`smartsafehub-desktop-navigation`,"aria-expanded":!t,"aria-label":t?`사이드바 펼치기`:`사이드바 접기`,class:`ssh-sidebar-toggle absolute right-0 top-[4.5rem] z-20 inline-flex size-7 translate-x-1/2 -translate-y-1/2 shrink-0 items-center justify-center rounded-full transition focus:outline-none focus-visible:ring-4 focus-visible:ring-teal-100`,onClick:i,title:t?`사이드바 펼치기`:`사이드바 접기`,type:`button`,children:L(t?yt:vt,{class:`size-4`})})]}),L(`nav`,{"aria-label":`SmartSafeHub 메뉴`,class:`flex-1 overflow-y-auto py-5 ${t?`px-2`:`px-3`}`,id:`smartsafehub-desktop-navigation`,children:L(Mt,{accountRegistered:e,collapsed:t,demo:d,route:s,updateCount:u})}),d?null:L(`div`,{class:`border-t border-slate-100 ${t?`p-2`:`p-3`}`,children:L(`a`,{"aria-label":`SmartSafeHub에서 로그아웃`,class:`${jt(!1,t)} mt-1 text-rose-700 hover:bg-rose-50 hover:text-rose-800`,href:Et(`/admin/logout`),title:t?`로그아웃`:void 0,children:[L(`span`,{class:`grid size-9 shrink-0 place-items-center rounded-lg bg-rose-50 text-rose-600`,children:L(mt,{class:`size-5`})}),t?null:`로그아웃`]})})]})})]})}var Pt=`smartsafehub.sidebar.collapsed`;function Ft(){if(typeof window>`u`)return!1;try{return window.localStorage.getItem(Pt)===`1`}catch{return!1}}function It({accountRegistered:e,children:t,loading:n,onRefresh:r,refreshing:i,route:a,updateCount:o,demo:s=!1}){let c=Ie[a],[l,u]=F(Ft),[d,f]=F(ze);return I(()=>{try{window.localStorage.setItem(Pt,l?`1`:`0`)}catch{}},[l]),I(()=>{Be(d),Ve(d)},[d]),L(`div`,{class:`ssh-app min-h-screen bg-slate-50 text-slate-950 md:grid md:transition-[grid-template-columns] md:duration-200 md:ease-out ${l?`md:grid-cols-[5rem_minmax(0,1fr)]`:`md:grid-cols-[16rem_minmax(0,1fr)]`}`,"data-theme":d,children:[L(Nt,{accountRegistered:e,demo:s,collapsed:l,loading:n,onRefresh:r,onToggleCollapsed:()=>u(e=>!e),onToggleTheme:()=>f(e=>e===`dark`?`light`:`dark`),refreshing:i,route:a,theme:d,title:c.title,updateCount:o}),L(`div`,{class:`min-w-0 md:min-h-screen`,children:[L(St,{description:c.description,loading:n,onRefresh:r,onToggleTheme:()=>f(e=>e===`dark`?`light`:`dark`),refreshing:i,theme:d,title:c.title}),L(`main`,{class:`ssh-product-main w-full max-w-[1600px] bg-slate-50 px-4 pb-8 sm:px-6 sm:pb-10 lg:px-8 lg:pb-12 xl:px-10 ${n?`pt-2 sm:pt-2 lg:pt-3`:`pt-5 sm:pt-6 lg:pt-8`}`,children:t})]})]})}var Lt=new Intl.NumberFormat(`ko-KR`),Rt=new Intl.DateTimeFormat(`ko-KR`,{dateStyle:`medium`,timeStyle:`short`});function zt(e){if(!Number.isFinite(e)||e<0)return`확인 불가`;let t=Math.floor(e/86400),n=Math.floor(e%86400/3600),r=Math.floor(e%3600/60);return t>0?`${t}일 ${n}시간`:n>0?`${n}시간 ${r}분`:`${Math.max(r,1)}분`}function Bt(e){if(!Number.isFinite(e)||e<0)return`확인 불가`;let t=[`B`,`KB`,`MB`,`GB`],n=e,r=0;for(;n>=1024&&r864e10?`기록 없음`:!Number.isFinite(t)||e>t+300||t-e>=604800?Ut(e):Wt(e,t)}function Kt(e){let t=/^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})Z$/.exec(e??``);if(!t)return null;let[,n,r,i,a,o,s]=t,c=Date.UTC(Number(n),Number(r)-1,Number(i),Number(a),Number(o),Number(s)),l=new Date(c);return l.getUTCFullYear()!==Number(n)||l.getUTCMonth()+1!==Number(r)||l.getUTCDate()!==Number(i)||l.getUTCHours()!==Number(a)||l.getUTCMinutes()!==Number(o)||l.getUTCSeconds()!==Number(s)||c<=0?null:c/1e3}function qt(e){if(!Number.isFinite(e)||e<=0)return`확인 불가`;let t=Math.floor(e/3600),n=Math.floor(e%3600/60);return t>0?n>0?`${t}시간 ${n}분`:`${t}시간`:`${n}분`}function Jt(e){let t=Math.max(0,e.total),n=e.free+e.buffered+e.cached,r=Math.min(t,Math.max(0,e.available>0?e.available:n)),i=Math.max(0,t-r);return{used:i,percent:t>0?i/t*100:0}}function Yt(e,t){let n=e.metadata[t];return typeof n==`string`&&n.trim().length>0?n.trim():null}function Xt(e,t){let n=e.metadata[t];return typeof n==`number`&&Number.isFinite(n)?n:null}function Zt(e,t){let n=e.metadata[t];return typeof n==`boolean`?n:null}function Qt(e,t){let n=Yt(e,`error_code`);return n?`${t} (오류 코드: ${n})`:t}function $t(e){let t=Math.max(0,Math.floor(e));if(t<60)return`${t}초`;let n=Math.floor(t/60),r=t%60;if(n<60)return r>0?`${n}분 ${r}초`:`${n}분`;let i=Math.floor(n/60),a=n%60;return a>0?`${i}시간 ${a}분`:`${i}시간`}function en(e){let t=Yt(e,`from_version`),n=Yt(e,`to_version`);return t&&n?`${t} → ${n}`:n?`${n} 버전이 적용되었습니다.`:null}function tn(e){return e?e.toUpperCase():null}function nn(e){switch(e){case`dhcp`:return`자동 IP(DHCP)`;case`pppoe`:return`PPPoE`;case`static`:return`고정 IPv4`;default:return e.toUpperCase()}}function rn(e){switch(e.eventType){case`system.booted`:return{title:`공유기 부팅 감지`,description:`공유기가 켜지거나 재시작된 것을 확인했습니다.`};case`network.internet.disconnected`:return{title:`인터넷 연결 끊김 감지`,description:`공유기에서 인터넷 연결을 확인할 수 없었습니다.`};case`network.internet.recovered`:{let t=Xt(e,`downtime_seconds`);return{title:`인터넷 연결 복구`,description:t!==null&&t>0?`약 ${$t(t)} 동안 인터넷 연결이 확인되지 않았습니다.`:`공유기에서 인터넷 연결을 다시 확인했습니다.`}}case`safeshield.protection.enabled`:return{title:`SafeShield 보호 활성화`,description:`DNS 보호 기능이 활성화되었습니다.`};case`safeshield.protection.disabled`:return{title:`SafeShield 보호 비활성화`,description:`DNS 보호 기능이 비활성화되었습니다.`};case`safeshield.blocklist.updated`:{let t=Xt(e,`domain_count`),n=Yt(e,`artifact_version`),r=[];return t!==null&&t>0&&r.push(`${H(t)}개 도메인 적용`),n&&r.push(`목록 ${n}`),{title:`SafeShield 차단 목록 업데이트 완료`,description:r.length>0?r.join(` · `):`최신 차단 목록을 적용했습니다.`}}case`safeshield.blocklist.update_failed`:return{title:`SafeShield 차단 목록 업데이트 실패`,description:Qt(e,`차단 목록을 업데이트하지 못했습니다.`)};case`safeshield.statistics.enabled`:return{title:`SafeShield 차단 통계 수집 활성화`,description:`기기별 차단 통계 수집을 활성화했습니다.`};case`safeshield.statistics.disabled`:return{title:`SafeShield 차단 통계 수집 비활성화`,description:`기기별 차단 통계 수집을 비활성화했습니다.`};case`safeshield.rule.added`:return{title:Yt(e,`rule_action`)===`allow`?`사용자 허용 규칙 추가`:`사용자 차단 규칙 추가`,description:`SafeShield 사용자 규칙이 변경되었습니다.`};case`safeshield.rule.removed`:return{title:Yt(e,`rule_action`)===`allow`?`사용자 허용 규칙 삭제`:`사용자 차단 규칙 삭제`,description:`SafeShield 사용자 규칙이 변경되었습니다.`};case`settings.scheduled_reboot.updated`:{let t=Zt(e,`enabled`);return{title:`예약 재부팅 설정 변경`,description:t===!0?`예약 재부팅 설정을 저장하고 새 일정에 적용했습니다.`:t===!1?`예약 재부팅을 비활성화했습니다.`:`예약 재부팅 설정을 변경했습니다.`}}case`settings.software_updates.updated`:return{title:`관리 소프트웨어 업데이트 설정 변경`,description:Zt(e,`auto_install`)===!0?`자동 설치를 포함한 업데이트 설정을 저장했습니다.`:`관리 소프트웨어 업데이트 설정을 저장했습니다.`};case`settings.health_reporter.enabled`:return{title:`원격 상태 보고 활성화`,description:`장치 상태를 SmartSafeHub Cloud에 보고하도록 설정했습니다.`};case`settings.health_reporter.disabled`:return{title:`원격 상태 보고 비활성화`,description:`SmartSafeHub Cloud로의 장치 상태 보고를 중지했습니다.`};case`settings.activity_cloud_sync.enabled`:return{title:`Cloud 활동 기록 전송 활성화`,description:`이후 발생하는 활동을 SmartSafeHub Cloud에 전송하도록 설정했습니다.`};case`settings.activity_cloud_sync.disabled`:return{title:`Cloud 활동 기록 전송 비활성화`,description:`Cloud 전송을 중지했습니다. 로컬 최근 활동은 계속 기록됩니다.`};case`settings.timezone.updated`:{let t=Yt(e,`zonename`);return{title:`시간대 설정 변경`,description:t?`공유기 시간대를 ${t}(으)로 변경했습니다.`:`공유기 시간대를 변경했습니다.`}}case`settings.wifi.updated`:return{title:`Wi-Fi 설정 변경`,description:`Wi-Fi 설정을 저장하고 무선 네트워크에 적용했습니다.`};case`settings.wan.updated`:{let t=Yt(e,`protocol`);return{title:`인터넷 설정 변경`,description:t?`${nn(t)} 방식으로 WAN 설정을 변경했습니다.`:`WAN 인터넷 연결 설정을 변경했습니다.`}}case`settings.lan.updated`:return{title:`LAN 설정 변경`,description:`내부 네트워크 및 DHCP 설정을 변경했습니다.`};case`settings.iptv.updated`:{let t=Zt(e,`enabled`),n=Yt(e,`provider`),r=n===`skb`?`SK Broadband`:n===`lgu`?`LG U+`:null;return{title:`IPTV 설정 변경`,description:t===!1?`IPTV 기능을 비활성화했습니다.`:r?`${r} IPTV 설정을 적용했습니다.`:`IPTV 설정을 적용했습니다.`}}case`software.update.completed`:return{title:`관리 소프트웨어 업데이트 완료`,description:en(e)};case`software.update.failed`:return{title:`관리 소프트웨어 업데이트 실패`,description:Qt(e,`관리 소프트웨어를 업데이트하지 못했습니다.`)};case`firmware.update.started`:return{title:`펌웨어 업데이트 시작`,description:`펌웨어 설치를 시작했습니다. 완료 과정에서 공유기가 재부팅됩니다.`};case`firmware.update.failed`:return{title:`펌웨어 업데이트 실패`,description:Qt(e,`펌웨어 설치 준비 또는 검증에 실패했습니다.`)};case`license.activated`:{let t=tn(Yt(e,`to_plan`));return{title:`요금제 활성화`,description:t?`${t} 요금제가 활성화되었습니다.`:`요금제가 활성화되었습니다.`}}case`license.changed`:{let t=tn(Yt(e,`from_plan`)),n=tn(Yt(e,`to_plan`));return{title:`요금제 변경`,description:t&&n?`${t} → ${n}`:n?`${n} 요금제로 변경되었습니다.`:`요금제 상태가 변경되었습니다.`}}case`license.cleared`:return{title:`계정 권한 해제`,description:Yt(e,`reason`)===`manual`?`이 공유기에서 계정 권한 연결을 해제했습니다.`:`서버의 계정 권한 상태가 반영되어 이 공유기의 권한 연결이 해제되었습니다.`};case`health.issue.started`:{let t=Xt(e,`warning_count`)??0,n=Xt(e,`critical_count`)??0,r=[];return n>0&&r.push(`이상 ${H(n)}건`),t>0&&r.push(`주의 ${H(t)}건`),{title:`확인이 필요한 장치 상태 발견`,description:r.length>0?r.join(` · `):`장치 상태에서 확인이 필요한 항목이 발견되었습니다.`}}case`health.issue.changed`:{let t=Xt(e,`warning_count`)??0,n=Xt(e,`critical_count`)??0,r=[];return n>0&&r.push(`이상 ${H(n)}건`),t>0&&r.push(`주의 ${H(t)}건`),{title:`확인이 필요한 장치 상태 변경`,description:r.length>0?r.join(` · `):`확인이 필요한 장치 상태 항목이 변경되었습니다.`}}case`health.issue.resolved`:return{title:`장치 상태 정상으로 복구`,description:`이전에 확인이 필요했던 장치 상태가 정상으로 돌아왔습니다.`};default:return{title:`SmartSafeHub 활동`,description:`장치에서 새로운 활동이 기록되었습니다.`}}}function an(e){return e===`error`?{icon:L(V,{class:`size-4`}),surface:`bg-rose-50 text-rose-700 ring-rose-200`}:e===`warning`?{icon:L(V,{class:`size-4`}),surface:`bg-amber-50 text-amber-800 ring-amber-200`}:e===`success`?{icon:L(B,{class:`size-4`}),surface:`bg-emerald-50 text-emerald-700 ring-emerald-200`}:{icon:L(tt,{class:`size-4`}),surface:`bg-slate-100 text-slate-600 ring-slate-200`}}function on(e){let t=new Date(e*1e3);return new Intl.DateTimeFormat(`ko-KR`,{hour:`2-digit`,minute:`2-digit`,hour12:!1}).format(t)}function sn(e){let t=new Date(e*1e3);return`${t.getFullYear()}-${t.getMonth()+1}-${t.getDate()}`}function cn(e){let t=new Date(e*1e3),n=new Date,r=new Date(n.getFullYear(),n.getMonth(),n.getDate()),i=new Date(t.getFullYear(),t.getMonth(),t.getDate()),a=Math.round((r.getTime()-i.getTime())/864e5);return a===0?`오늘`:a===1?`어제`:new Intl.DateTimeFormat(`ko-KR`,{month:`long`,day:`numeric`,weekday:`short`}).format(t)}function ln({compact:e=!1,events:t}){if(t.length===0)return L(`div`,{class:`rounded-xl border border-slate-200 bg-slate-50 px-4 py-5 text-center`,children:[L(tt,{class:`mx-auto size-5 text-slate-400`}),L(`p`,{class:`mt-2 mb-0 text-sm font-extrabold text-slate-700`,children:`아직 기록된 활동이 없습니다.`}),L(`p`,{class:`mt-1 mb-0 text-xs leading-5 text-slate-500`,children:`상태가 실제로 변경되면 이곳에 시간순으로 기록됩니다.`})]});let n=``;return L(`div`,{class:e?`space-y-2`:`space-y-1`,children:t.map(t=>{let r=rn(t),i=an(t.severity),a=sn(t.occurredAt),o=!e&&a!==n;return n=a,L(`div`,{children:[o?L(`p`,{class:`mt-5 mb-2 first:mt-0 text-xs font-black text-slate-500`,children:cn(t.occurredAt)}):null,L(`article`,{class:`relative flex min-w-0 gap-3 ${e?`border-b border-slate-100 py-3 first:pt-0 last:border-b-0 last:pb-0`:`rounded-xl px-2 py-3 hover:bg-slate-50`}`,children:[L(`div`,{class:`w-12 shrink-0 pt-1 text-xs font-bold tabular-nums text-slate-400`,children:L(`time`,{dateTime:new Date(t.occurredAt*1e3).toISOString(),title:Ut(t.occurredAt),children:on(t.occurredAt)})}),L(`span`,{class:`mt-0.5 grid size-8 shrink-0 place-items-center rounded-full ring-1 ring-inset ${i.surface}`,children:i.icon}),L(`div`,{class:`min-w-0 flex-1`,children:[L(`p`,{class:`m-0 text-sm font-black leading-5 text-slate-950`,children:r.title}),r.description?L(`p`,{class:`mt-1 mb-0 break-words text-xs font-medium leading-5 text-slate-500`,children:r.description}):null]})]})]},t.eventId)})})}function un({error:e,loading:t,onRetry:n}){return t?L(`div`,{class:`rounded-xl border border-slate-200 bg-slate-50 px-4 py-5 text-center`,children:[L(z,{class:`mx-auto size-5 animate-spin text-teal-700`}),L(`p`,{class:`mt-2 mb-0 text-sm font-bold text-slate-500`,children:`최근 활동을 불러오는 중입니다.`})]}):e?L(`div`,{class:`rounded-xl border border-amber-200 bg-amber-50/70 px-4 py-4`,children:L(`div`,{class:`flex items-start gap-3`,children:[L(V,{class:`mt-0.5 size-5 shrink-0 text-amber-700`}),L(`div`,{class:`min-w-0 flex-1`,children:[L(`p`,{class:`m-0 text-sm font-black text-slate-950`,children:`최근 활동을 불러오지 못했습니다.`}),L(`p`,{class:`mt-1 mb-0 text-xs leading-5 text-slate-500`,children:e}),n?L(`button`,{class:`mt-3 inline-flex min-h-9 items-center justify-center rounded-lg border border-amber-300 bg-white px-3 text-xs font-extrabold text-amber-800 transition hover:bg-amber-50`,onClick:n,type:`button`,children:`다시 시도`}):null]})]})}):null}function dn(e){return e.enabled?e.eligible===!1||e.phase===`ineligible`?{label:`계정 연결 필요`,description:`SmartSafeHub 계정을 연결하면 이후 발생하는 활동을 Cloud에 자동으로 동기화할 수 있습니다.`,tone:`bg-slate-100 text-slate-600 ring-slate-200`}:e.phase===`error`?{label:`동기화 확인 필요`,description:`최근 Cloud 전송을 완료하지 못했습니다. 로컬 활동 기록은 계속 보관됩니다.`,tone:`bg-amber-50 text-amber-800 ring-amber-200`}:e.phase===`syncing`?{label:`동기화 중`,description:`새 활동을 SmartSafeHub Cloud에 전송하고 있습니다.`,tone:`bg-teal-50 text-teal-800 ring-teal-200`}:e.eligible===!0?{label:`자동 동기화`,description:`새 활동이 생기면 SmartSafeHub Cloud에 자동으로 안전하게 전송합니다.`,tone:`bg-emerald-50 text-emerald-800 ring-emerald-200`}:{label:`확인 중`,description:`Cloud 활동 기록 사용 가능 여부를 확인하고 있습니다.`,tone:`bg-slate-100 text-slate-600 ring-slate-200`}:e.eligible===!1?{label:`계정 연결 필요`,description:`SmartSafeHub 계정을 연결하면 Free에서도 Cloud 활동 기록을 사용할 수 있습니다.`,tone:`bg-slate-100 text-slate-600 ring-slate-200`}:{label:`Cloud 전송 꺼짐`,description:`활동 기록은 이 공유기에만 저장됩니다. Cloud에는 새로운 활동을 전송하지 않습니다.`,tone:`bg-slate-100 text-slate-700 ring-slate-200`}}function fn(e){if(e.eligible!==!0)return`—`;if(e.retentionDays>0)return`${e.retentionDays}일`;switch(e.plan?.toLowerCase()){case`free`:return`7일`;case`pro`:case`ultimate`:return`90일`;default:return`확인 중`}}function pn({actionError:e,actionMessage:t,cloud:n,onDismissFeedback:r,onSetEnabled:i,saving:a}){let o=dn(n),s=n.plan?.toUpperCase()??null,c=n.eligible===!0,l=n.enabled||c;return L(`section`,{class:`rounded-2xl border border-slate-200 bg-white p-5 shadow-sm shadow-slate-900/5 sm:p-6`,children:[L(`div`,{class:`flex flex-wrap items-start justify-between gap-4`,children:[L(`div`,{class:`min-w-0`,children:[L(`p`,{class:`m-0 text-xs font-extrabold uppercase tracking-[0.16em] text-teal-700`,children:`Cloud history`}),L(`h2`,{class:`mt-2 mb-0 text-base font-black text-slate-950`,children:`Cloud 활동 기록`}),L(`p`,{class:`mt-2 mb-0 max-w-3xl text-sm leading-6 text-slate-500`,children:o.description})]}),L(`div`,{class:`flex shrink-0 items-center gap-3`,children:[L(`span`,{class:`rounded-full px-3 py-1.5 text-xs font-extrabold ring-1 ring-inset ${o.tone}`,children:a?`설정 중`:o.label}),L(`button`,{"aria-checked":n.enabled,"aria-label":`Cloud 활동 기록 전송 사용`,class:`ssh-switch-control relative inline-flex shrink-0 rounded-full border transition focus:outline-none focus-visible:ring-4 focus-visible:ring-teal-100 disabled:cursor-not-allowed disabled:opacity-50 ${n.enabled?`border-teal-600 bg-teal-600`:`border-slate-300 bg-slate-200`}`,disabled:a||!l,onClick:()=>i(!n.enabled),role:`switch`,type:`button`,children:L(`span`,{"aria-hidden":`true`,class:`ssh-switch-thumb absolute top-1 shadow-sm transition-[left] ${n.enabled?`left-6`:`left-1`}`})})]})]}),(e||t)&&L(`div`,{class:`mt-4 flex min-w-0 items-start justify-between gap-3 rounded-xl border px-4 py-3 text-sm font-bold ${e?`border-rose-200 bg-rose-50 text-rose-800`:`border-emerald-200 bg-emerald-50 text-emerald-800`}`,role:e?`alert`:`status`,children:[L(`span`,{class:`min-w-0`,children:e||t}),L(`button`,{class:`shrink-0 rounded-lg px-2 py-1 text-xs font-extrabold hover:bg-black/5`,onClick:r,type:`button`,children:`닫기`})]}),L(`dl`,{class:`mt-5 grid gap-3 sm:grid-cols-4`,children:[L(`div`,{class:`rounded-xl bg-slate-50 px-4 py-3 ring-1 ring-inset ring-slate-200`,children:[L(`dt`,{class:`text-xs font-bold text-slate-500`,children:`멤버십`}),L(`dd`,{class:`mt-1 text-sm font-black text-slate-900`,children:s??(n.eligible===!1?`Free`:`확인 중`)})]}),L(`div`,{class:`rounded-xl bg-slate-50 px-4 py-3 ring-1 ring-inset ring-slate-200`,children:[L(`dt`,{class:`text-xs font-bold text-slate-500`,children:`전송 대기`}),L(`dd`,{class:`mt-1 text-sm font-black text-slate-900`,children:[H(n.pendingEvents),`건`]})]}),L(`div`,{class:`rounded-xl bg-slate-50 px-4 py-3 ring-1 ring-inset ring-slate-200`,children:[L(`dt`,{class:`text-xs font-bold text-slate-500`,children:`마지막 동기화`}),L(`dd`,{class:`mt-1 text-sm font-black text-slate-900`,children:n.lastSuccessAt>0?Wt(n.lastSuccessAt):`아직 없음`})]}),L(`div`,{class:`rounded-xl bg-slate-50 px-4 py-3 ring-1 ring-inset ring-slate-200`,children:[L(`dt`,{class:`text-xs font-bold text-slate-500`,children:`Cloud 보관`}),L(`dd`,{class:`mt-1 text-sm font-black text-slate-900`,children:fn(n)})]})]}),n.enabled&&n.phase===`error`&&n.lastErrorCode?L(`p`,{class:`mt-3 mb-0 text-xs font-semibold text-amber-700`,children:[`오류 코드: `,L(`code`,{children:n.lastErrorCode})]}):null,L(`p`,{class:`mt-3 mb-0 text-xs leading-5 text-slate-500`,children:`SmartSafeHub 계정에 연결된 Free는 최근 7일, Pro와 Ultimate는 최근 90일 동안 Cloud 활동 기록을 보관합니다. 끄면 전송 대기 데이터와 임시 인증 정보를 삭제하며, 다시 켠 뒤 새로 발생한 활동부터 Cloud에 전송합니다. 로컬 최근 활동은 항상 유지됩니다.`})]})}function mn({actionError:e,actionMessage:t,data:n,error:r,loading:i,onDismissActionFeedback:a,onRetry:o,onSetCloudSync:s,savingCloudSync:c}){return L(`div`,{class:`min-w-0 space-y-5`,children:[L(`section`,{"aria-labelledby":`activity-history-title`,class:`min-w-0 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm shadow-slate-900/5 sm:p-6`,children:[L(`div`,{class:`flex flex-wrap items-start justify-between gap-4`,children:[L(`div`,{children:[L(`p`,{class:`m-0 text-xs font-extrabold uppercase tracking-[0.16em] text-teal-700`,children:`Local history`}),L(`h2`,{class:`mt-2 mb-0 text-xl font-black tracking-tight text-slate-950`,id:`activity-history-title`,children:`현재 부팅 이후의 최근 활동`}),L(`p`,{class:`mt-2 mb-0 max-w-3xl text-sm leading-6 text-slate-500`,children:`인터넷, SafeShield, 업데이트, 계정 권한과 주요 설정에서 실제 상태가 변경된 시점만 기록합니다.`})]}),L(`span`,{class:`rounded-full bg-slate-100 px-3 py-1.5 text-xs font-extrabold text-slate-600 ring-1 ring-inset ring-slate-200`,children:[`최대 `,n?.maxEvents??128,`건`]})]}),L(`div`,{class:`mt-5`,children:i||r&&!n?L(un,{error:r,loading:i,onRetry:o}):L(ln,{events:n?.events??[]})})]}),L(pn,{actionError:e,actionMessage:t,cloud:n?.cloud??{enabled:!1,phase:`preparing`,eligible:null,plan:null,retentionDays:0,pendingEvents:0,lastAttemptAt:0,lastSuccessAt:0,lastUploadedCount:0,lastErrorCode:null,nextSyncAt:0},onDismissFeedback:a,onSetEnabled:s,saving:c}),L(`aside`,{class:`rounded-2xl border border-slate-200 bg-slate-50 px-5 py-4`,children:[L(`p`,{class:`m-0 text-sm font-extrabold text-slate-800`,children:`로컬 활동 기록 안내`}),L(`p`,{class:`mt-1 mb-0 text-xs leading-5 text-slate-500`,children:`이 기록은 공유기의 메모리 영역에만 보관되어 재부팅하면 초기화됩니다. 직접 설정을 변경한 작업은 성공 시점에 즉시 기록하고, 인터넷 및 장치 진단처럼 외부 상태를 확인해야 하는 항목은 실제 상태 변화가 관찰된 경우에만 추가합니다.`})]})]})}var hn=Object.assign,gn=/^(-|f[lo].*[^se]$|g.{5,}[^ps]$|z|o[pr]|(W.{5})?[lL]i.*(t|mp)$|an|(bo|s).{4}Im|sca|m.{6}[ds]|ta|c.*[st]$|wido|ini)/;function _n(e,t){var n=ce(e,t);return n.containerInfo=t,n}var vn=Symbol.for(`react.element`),yn=/^(?:accent|alignment|arabic|baseline|cap|clip(?!PathU)|color|dominant|fill|flood|font|glyph(?!R)|horiz|image(?!S)|letter|lighting|marker(?!H|W|U)|overline|paint|pointer|shape|stop|strikethrough|stroke|text(?!L)|transform|underline|unicode|units|v|vector|vert|word|writing|x(?!C))[A-Z]/,bn=/[A-Z0-9]/g,xn=typeof document<`u`,Sn=function(e){return/fil|che|rad/.test(e)};y.prototype.isReactComponent=!0,[`componentWillMount`,`componentWillReceiveProps`,`componentWillUpdate`].forEach(function(e){Object.defineProperty(y.prototype,e,{configurable:!0,get:function(){return this[`UNSAFE_`+e]},set:function(t){Object.defineProperty(this,e,{configurable:!0,writable:!0,value:t})}})});var Cn=e.event;e.event=function(e){return Cn&&(e=Cn(e)),e.persist=function(){},e.isPropagationStopped=function(){return this.cancelBubble},e.isDefaultPrevented=function(){return this.defaultPrevented},e.nativeEvent=e};var wn={configurable:!0,get:function(){return this.class}},Tn=e.vnode;e.vnode=function(e){if(typeof e.type==`string`)(function(e){var t=e.props,n=e.type,r={},i=n.indexOf(`-`)==-1;for(var a in t){var o=t[a];if(!(a==`value`&&`defaultValue`in t&&o==null||xn&&a==`children`&&n==`noscript`||a==`class`||a==`className`)){if(a==`style`&&typeof o==`object`){var s=void 0;for(var c in o)typeof o[c]!=`number`||gn.test(c)||(s||(s=o=hn({},o)),o[c]+=`px`)}else if(a==`defaultValue`&&`value`in t&&t.value==null)a=`value`;else if(a==`download`&&!0===o)o=``;else if(a==`translate`&&o===`no`)o=!1;else if(a[0]==`o`&&a[1]==`n`){var l=a.toLowerCase();l==`ondoubleclick`?a=`ondblclick`:l!=`onchange`||n!=`input`&&n!=`textarea`||Sn(t.type)?l==`onfocus`?a=`onfocusin`:l==`onblur`&&(a=`onfocusout`):l=a=`oninput`,l==`oninput`&&r[a=l]&&(a=`oninputCapture`)}else i&&yn.test(a)?a=a.replace(bn,`-$&`).toLowerCase():o===null&&(o=void 0);r[a]=o}}n==`select`&&(r.multiple&&Array.isArray(r.value)&&(r.value=D(t.children).forEach(function(e){e.props.selected=r.value.indexOf(e.props.value)!=-1})),r.defaultValue!=null&&(r.value=D(t.children).forEach(function(e){e.props.selected=r.multiple?r.defaultValue.indexOf(e.props.value)!=-1:r.defaultValue==e.props.value}))),t.class&&!t.className?(r.class=t.class,Object.defineProperty(r,"className",wn)):t.className&&(r.class=r.className=t.className),e.props=r})(e);else if(typeof e.type==`function`&&(`ref`in e.props&&`prototype`in e.type&&e.type.prototype.render&&(e.ref=e.props.ref,delete e.props.ref),e.type.defaultProps)){var t=hn({},e.props);for(var n in e.type.defaultProps)t[n]===void 0&&(t[n]=e.type.defaultProps[n]);e.props=t}e.ref&&!(`ref`in e.props)&&Object.defineProperty(e.props,"ref",{value:e.ref,configurable:!0,writable:!0}),e.$$typeof=vn,Tn&&Tn(e)};var En=8,Dn=256,On=8;function kn(e){return e.findIndex(e=>!e.disabled)}function An(e){for(let t=e.length-1;t>=0;--t)if(!e[t]?.disabled)return t;return-1}function jn(e,t,n){if(e.length===0)return-1;let r=t;for(let t=0;te.value===t&&!e.disabled);return n>=0?n:kn(e)}function Nn({ariaLabel:e,className:t=``,disabled:n=!1,id:r,onChange:i,options:a,value:o,variant:s=`default`}){let c=De(),l=r??`ssh-custom-select-${c}`,u=`${l}-menu`,d=we(null),f=we(null),p=we(null),m=we([]),[h,g]=F(!1),[_,v]=F(()=>Mn(a,o)),[y,b]=F(`bottom`),[x,S]=F(Dn),[C,w]=F({}),[T,E]=F(null),D=Te(()=>a.find(e=>e.value===o)??a[0]??null,[a,o]),O=()=>{let e=f.current;if(!e)return;let t=e.getBoundingClientRect(),n=Math.min(Dn,Math.max(44,a.length*42+12)),r=window.innerHeight-t.bottom-On,i=t.top-On,o=rr?`top`:`bottom`,s=Math.min(t.width,Math.max(0,window.innerWidth-16)),c=Math.max(On,Math.min(t.left,window.innerWidth-s-On)),l=o===`bottom`?r:i;b(o),S(Math.max(44,Math.min(Dn,Math.floor(l)))),w({left:`${c}px`,width:`${s}px`,...o===`bottom`?{top:`${t.bottom+En}px`}:{bottom:`${window.innerHeight-t.top+En}px`}})},k=(e=!1)=>{g(!1),e&&window.requestAnimationFrame(()=>f.current?.focus())},A=e=>{e<0||(v(e),window.requestAnimationFrame(()=>m.current[e]?.focus()))},j=e=>{let t=a[e];t&&!t.disabled&&(i(t.value),g(!1),window.requestAnimationFrame(()=>f.current?.focus()))},M=e=>{n||a.length===0||(O(),g(!0),A(e??Mn(a,o)))};I(()=>{let e=d.current?.closest(`.ssh-app`);if(e){E(e);return}let t=d.current?.getRootNode();t instanceof ShadowRoot?E(t):typeof document<`u`&&E(document.body)},[]),I(()=>{v(Mn(a,o)),m.current=m.current.slice(0,a.length)},[a,o]),I(()=>{if(!h)return;O();let e=e=>{let t=e.composedPath();d.current&&t.includes(d.current)||p.current&&t.includes(p.current)||k()},t=e=>{e.key===`Escape`&&(e.preventDefault(),k(!0))},n=()=>O();return document.addEventListener(`pointerdown`,e),document.addEventListener(`keydown`,t),window.addEventListener(`resize`,n),window.addEventListener(`scroll`,n,!0),()=>{document.removeEventListener(`pointerdown`,e),document.removeEventListener(`keydown`,t),window.removeEventListener(`resize`,n),window.removeEventListener(`scroll`,n,!0)}},[h,a.length]),I(()=>{n&&h&&k()},[n,h]);let ee=s===`emphasized`?`min-h-11 border-2 border-slate-300 bg-slate-50 px-4 py-2.5 text-sm font-semibold text-slate-950 shadow-inner focus:border-teal-500 focus:bg-white focus:ring-4 focus:ring-teal-100`:`min-h-11 border border-slate-300 bg-slate-50 px-3 py-2.5 text-sm font-bold text-slate-900 focus:border-teal-500 focus:ring-2 focus:ring-teal-100`,te=h?L(`div`,{"aria-label":`${e} 목록`,class:`ssh-custom-select-menu fixed z-[1000] overflow-hidden rounded-xl border border-slate-200 bg-white p-1.5 shadow-xl shadow-slate-950/10`,id:u,ref:p,role:`listbox`,style:C,children:L(`div`,{class:`overflow-y-auto overscroll-contain`,style:{maxHeight:`${x}px`},children:a.map((e,t)=>{let n=e.value===o;return L(`button`,{"aria-selected":n,class:`flex min-h-10 w-full items-center justify-between gap-3 rounded-lg px-3 py-2.5 text-left text-sm font-bold transition ${n?`bg-teal-50 text-teal-800`:`text-slate-700 hover:bg-slate-50`} ${e.disabled?`cursor-not-allowed opacity-50`:``}`,disabled:e.disabled,onClick:()=>j(t),onFocus:()=>v(t),onKeyDown:e=>{switch(e.key){case`ArrowDown`:e.preventDefault(),A(jn(a,t,1));break;case`ArrowUp`:e.preventDefault(),A(jn(a,t,-1));break;case`Home`:e.preventDefault(),A(kn(a));break;case`End`:e.preventDefault(),A(An(a));break;case`Enter`:case` `:e.preventDefault(),j(t);break;case`Escape`:e.preventDefault(),k(!0);break;case`Tab`:g(!1)}},ref:e=>{m.current[t]=e},role:`option`,tabIndex:_===t?0:-1,type:`button`,children:[L(`span`,{class:`min-w-0 truncate`,children:e.label}),n?L(`span`,{"aria-hidden":`true`,class:`size-2 shrink-0 rounded-full bg-teal-500`}):null]},e.value)})})}):null;return L(`div`,{class:`relative ${t}`,ref:d,children:[L(`button`,{"aria-controls":u,"aria-expanded":h,"aria-haspopup":`listbox`,"aria-label":e,class:`ssh-custom-select-trigger flex w-full cursor-pointer items-center justify-between gap-3 rounded-xl text-left outline-none transition disabled:cursor-not-allowed disabled:bg-slate-100 disabled:text-slate-400 disabled:opacity-60 ${ee}`,disabled:n,id:l,onClick:()=>{h?k():(O(),g(!0))},onKeyDown:e=>{switch(e.key){case`ArrowDown`:e.preventDefault(),M(h?jn(a,_,1):Mn(a,o));break;case`ArrowUp`:e.preventDefault(),M(h?jn(a,_,-1):Mn(a,o));break;case`Home`:e.preventDefault(),M(kn(a));break;case`End`:e.preventDefault(),M(An(a));break;case`Enter`:case` `:e.preventDefault(),h?k():M();break;case`Escape`:h&&(e.preventDefault(),k())}},ref:f,type:`button`,children:[L(`span`,{class:`min-w-0 truncate`,children:D?.label??`선택하세요`}),L(gt,{"aria-hidden":`true`,class:`size-4 shrink-0 text-slate-400 transition-transform ${h?`rotate-180`:``}`})]}),te&&T?_n(te,T):null,L(`span`,{class:`sr-only`,"aria-live":`polite`,children:y===`top`?`선택 목록이 위쪽에 열렸습니다.`:``})]})}function Pn(e){let t=e.split(`.`);return[t[0]??``,t[1]??``,t[2]??``,t[3]??``]}function Fn(e){return e.replace(/\D/g,``).slice(0,3)}function In(e){return e.every(e=>e===``)?``:e.join(`.`)}function Ln({disabled:e=!1,label:t,value:n,onChange:r}){let i=Pn(n),a=(e,t)=>{let n=[...i];n[e]=Fn(t),r(In(n))};return L(`div`,{class:`ssh-ipv4-segments mt-2 min-h-11`,"aria-label":`${t} 입력`,children:i.map((n,r)=>L(`div`,{class:`ssh-ipv4-segment`,children:[L(`input`,{"aria-label":`${t} ${r+1}번째 옥텟`,autocomplete:`off`,class:`min-h-11 w-full min-w-0 max-w-full rounded-xl border-2 border-slate-300 bg-slate-50 px-1.5 py-2.5 text-center text-sm font-semibold text-slate-950 shadow-inner outline-none transition focus:border-teal-500 focus:bg-white focus:ring-4 focus:ring-teal-100 disabled:cursor-not-allowed disabled:bg-slate-100 sm:px-2`,disabled:e,inputMode:`numeric`,maxLength:3,onInput:e=>a(r,e.currentTarget.value),pattern:`[0-9]*`,spellcheck:!1,value:n}),rt);function Un(e){return Bn.includes(e)}function Wn(e){switch(e){case`dhcp`:return`자동 IP (DHCP)`;case`pppoe`:return`PPPoE`;case`static`:return`고정 IPv4`;default:return e?e.toUpperCase():`확인 전`}}function Gn(e){return{protocol:e.configuration.protocol,pppoeUsername:e.configuration.pppoe.username,pppoePassword:``,staticAddress:e.configuration.static.address??``,staticPrefixLength:e.configuration.static.prefixLength??24,staticGateway:e.configuration.static.gateway??``,dnsPrimary:e.configuration.static.dns[0]??``,dnsSecondary:e.configuration.static.dns[1]??``}}function Kn(e){return e.trim()}function qn(e,t){let n=Gn(t);return e.protocol===n.protocol?e.protocol===`dhcp`||!Un(e.protocol)?!1:e.protocol===`pppoe`?Kn(e.pppoeUsername)!==n.pppoeUsername||e.pppoePassword.length>0:Kn(e.staticAddress)!==n.staticAddress||e.staticPrefixLength!==n.staticPrefixLength||Kn(e.staticGateway)!==n.staticGateway||Kn(e.dnsPrimary)!==n.dnsPrimary||Kn(e.dnsSecondary)!==n.dnsSecondary:!0}function Jn(e){let t=e.trim().split(`.`);return t.length===4&&t.every(e=>{if(!/^(0|[1-9][0-9]{0,2})$/.test(e))return!1;let t=Number(e);return t>=0&&t<=255})}function Yn(e){let t=e;return Array.from({length:4},()=>{if(t>=8)return t-=8,255;if(t<=0)return 0;let e=256-2**(8-t);return t=0,e}).join(`.`)}function Xn(e){if(!Number.isFinite(e)||e<=0)return`연결 시간 확인 전`;let t=Math.floor(e/86400),n=Math.floor(e%86400/3600),r=Math.floor(e%3600/60);return t>0?`${t}일 ${n}시간 연결됨`:n>0?`${n}시간 ${r}분 연결됨`:`${Math.max(1,r)}분 연결됨`}function Zn({feedback:e,onDismiss:t}){return L(`div`,{"aria-live":`polite`,class:`mb-5 rounded-2xl border px-4 py-4 text-sm ${e.kind===`success`?`border-emerald-200 bg-emerald-50 text-emerald-900`:e.kind===`warning`?`border-amber-200 bg-amber-50 text-amber-950`:`border-red-200 bg-red-50 text-red-900`}`,children:L(`div`,{class:`flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between`,children:[L(`p`,{class:`m-0 font-bold leading-6`,children:e.message}),L(`button`,{class:`min-h-10 shrink-0 rounded-lg border border-current bg-transparent px-3 py-1.5 text-xs font-extrabold`,onClick:t,type:`button`,children:`닫기`})]})})}function Qn({data:e,busy:t,onReconnect:n}){let r=e.status,i=r.connected;return L(`section`,{class:`grid gap-4 lg:grid-cols-3`,"aria-label":`인터넷 연결 상태`,children:[L(`article`,{class:`rounded-2xl border p-5 shadow-sm shadow-slate-900/5 ${i?`border-emerald-200 bg-emerald-50 text-emerald-950`:`border-amber-200 bg-amber-50 text-amber-950`}`,children:[L(`div`,{class:`flex items-start justify-between gap-3`,children:[L(`span`,{class:`grid size-10 place-items-center rounded-xl ${i?`bg-emerald-100 text-emerald-700`:`bg-amber-100 text-amber-700`}`,children:L(i?B:V,{class:`size-5`})}),L(`button`,{class:`inline-flex min-h-10 items-center justify-center gap-2 rounded-xl border border-current bg-transparent px-3 py-2 text-xs font-extrabold transition disabled:cursor-wait disabled:opacity-60`,disabled:t,onClick:n,type:`button`,children:[L(z,{class:`size-4 ${t?`animate-spin`:``}`}),`재연결`]})]}),L(`p`,{class:`mt-4 mb-0 text-xs font-extrabold uppercase tracking-[0.14em] opacity-70`,children:`인터넷 상태`}),L(`p`,{class:`mt-2 mb-0 text-xl font-black`,children:r.pending?`연결 중`:i?`연결됨`:`연결 안 됨`}),L(`p`,{class:`mt-1 mb-0 text-sm font-semibold leading-6 opacity-80`,children:[Wn(r.protocol??e.configuration.protocol),` · `,Xn(r.uptimeSeconds)]})]}),L(`article`,{class:`rounded-2xl border border-slate-200 bg-white p-5 shadow-sm shadow-slate-900/5`,children:[L(`span`,{class:`grid size-10 place-items-center rounded-xl bg-teal-50 text-teal-700`,children:L($e,{class:`size-5`})}),L(`p`,{class:`mt-4 mb-0 text-xs font-extrabold uppercase tracking-[0.14em] text-slate-500`,children:`WAN IPv4`}),L(`p`,{class:`mt-2 mb-0 break-all text-xl font-black text-slate-950`,children:r.address?`${r.address}${r.prefixLength===null?``:`/${r.prefixLength}`}`:`주소 없음`}),L(`p`,{class:`mt-1 mb-0 text-sm font-semibold text-slate-600`,children:r.device?`인터페이스 ${r.device}`:`인터페이스 확인 전`})]}),L(`article`,{class:`rounded-2xl border border-slate-200 bg-white p-5 shadow-sm shadow-slate-900/5`,children:[L(`span`,{class:`grid size-10 place-items-center rounded-xl bg-slate-100 text-slate-700`,children:L(We,{class:`size-5`})}),L(`p`,{class:`mt-4 mb-0 text-xs font-extrabold uppercase tracking-[0.14em] text-slate-500`,children:`Gateway · DNS`}),L(`p`,{class:`mt-2 mb-0 break-all text-base font-black text-slate-950`,children:r.gateway??`Gateway 확인 전`}),L(`p`,{class:`mt-1 mb-0 break-all text-sm font-semibold leading-6 text-slate-600`,children:r.dns.length>0?r.dns.join(` · `):`DNS 확인 전`})]})]})}function $n({action:e,data:t,error:n,feedback:r,loading:i,onDismissFeedback:a,onReconnect:o,onRetry:s,onSave:c}){let[l,u]=F({protocol:`dhcp`,pppoeUsername:``,pppoePassword:``,staticAddress:``,staticPrefixLength:24,staticGateway:``,dnsPrimary:``,dnsSecondary:``}),[d,f]=F(!1),[p,m]=F(!1),[h,g]=F(null);I(()=>{t&&!d&&(u(Gn(t)),g(null))},[t,d]);let _=Te(()=>{let e=[{value:`dhcp`,label:`자동 IP (DHCP)`},{value:`pppoe`,label:`PPPoE`},{value:`static`,label:`고정 IPv4`}];return t&&!t.configuration.supported&&e.unshift({value:t.configuration.protocol,label:`현재 설정 (${t.configuration.protocol})`}),e},[t]);if(i)return L(Rn,{});if(n)return L(zn,{message:n,onRetry:s});if(!t)return null;let y=e!==null,b=e=>{u(n=>{let r={...n,...e};return f(qn(r,t)),r}),g(null)},x=async e=>{if(e.preventDefault(),!Un(l.protocol))g(`DHCP, PPPoE 또는 고정 IPv4 중 하나를 선택해 주세요.`);else{if(l.protocol===`pppoe`){if(!Kn(l.pppoeUsername)){g(`PPPoE 사용자명을 입력해 주세요.`);return}if((t.configuration.protocol!==`pppoe`||!t.configuration.pppoe.passwordConfigured)&&l.pppoePassword.length===0){g(`PPPoE 연결을 시작하려면 비밀번호를 입력해 주세요.`);return}}if(l.protocol===`static`){if(!Jn(l.staticAddress)){g(`고정 IPv4 주소를 올바르게 입력해 주세요.`);return}if(!Jn(l.staticGateway)){g(`기본 게이트웨이를 올바르게 입력해 주세요.`);return}if(!Jn(l.dnsPrimary)){g(`기본 DNS 서버를 올바르게 입력해 주세요.`);return}if(Kn(l.dnsSecondary)&&!Jn(l.dnsSecondary)){g(`보조 DNS 서버를 올바르게 입력해 주세요.`);return}}window.confirm(`인터넷 연결 설정을 적용하면 WAN 연결이 다시 시작되어 인터넷이 잠시 끊길 수 있습니다. 계속하시겠습니까?`)&&await c({protocol:l.protocol,pppoeUsername:Kn(l.pppoeUsername),pppoePassword:l.pppoePassword,pppoePasswordChanged:l.pppoePassword.length>0,staticAddress:Kn(l.staticAddress),staticPrefixLength:l.staticPrefixLength,staticGateway:Kn(l.staticGateway),dnsPrimary:Kn(l.dnsPrimary),dnsSecondary:Kn(l.dnsSecondary)})&&(u(e=>({...e,pppoePassword:``})),f(!1))}};return L(v,{children:[r?L(Zn,{feedback:r,onDismiss:a}):null,L(Qn,{data:t,busy:y,onReconnect:()=>{window.confirm(`WAN 연결을 다시 시작합니다. 인터넷이 잠시 끊길 수 있습니다. 계속하시겠습니까?`)&&o()}}),t.configuration.supported?null:L(`section`,{class:`mt-5 rounded-2xl border border-amber-200 bg-amber-50 p-5 text-amber-950 sm:p-6`,children:L(`div`,{class:`flex items-start gap-3`,children:[L(V,{class:`mt-0.5 size-5 shrink-0 text-amber-700`}),L(`div`,{children:[L(`h2`,{class:`m-0 text-base font-black`,children:`현재 WAN 방식은 직접 관리하지 않습니다`}),L(`p`,{class:`mt-2 mb-0 text-sm font-semibold leading-6 text-amber-900/80`,children:[`현재 설정은 `,t.configuration.protocol,`입니다. 아래에서 DHCP, PPPoE 또는 고정 IPv4를 선택해 저장하면 해당 방식으로 전환합니다.`]})]})]})}),L(`form`,{class:`mt-5 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm shadow-slate-900/5 sm:p-6`,onSubmit:e=>void x(e),children:[L(`div`,{class:`flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between`,children:[L(`div`,{class:`flex min-w-0 items-start gap-3`,children:[L(`span`,{class:`grid size-11 shrink-0 place-items-center rounded-xl bg-teal-50 text-teal-700`,children:L($e,{class:`size-6`})}),L(`div`,{class:`min-w-0`,children:[L(`p`,{class:`m-0 text-xs font-extrabold uppercase tracking-[0.16em] text-slate-500`,children:`Internet`}),L(`h2`,{class:`mt-2 mb-0 text-xl font-black text-slate-950`,children:`인터넷 연결 설정`}),L(`p`,{class:`mt-2 mb-0 text-sm font-semibold leading-6 text-slate-600`,children:`통신사 또는 상위 네트워크 환경에 맞는 WAN 연결 방식을 선택합니다.`})]})]}),d?L(`span`,{"aria-live":`polite`,class:`inline-flex shrink-0 self-start items-center gap-1.5 rounded-full border border-amber-200 bg-amber-50 px-2.5 py-1 text-xs font-extrabold text-amber-800`,role:`status`,children:[L(V,{"aria-hidden":`true`,class:`size-3.5 shrink-0`}),`저장되지 않음`]}):null]}),L(`div`,{class:`mt-6`,children:[L(`span`,{class:`text-sm font-extrabold text-slate-800`,children:`연결 방식`}),L(Nn,{ariaLabel:`WAN 연결 방식`,className:`mt-2 max-w-xl`,disabled:y,id:`wan-protocol`,onChange:e=>b(e===`static`&&!Kn(l.dnsSecondary)?{protocol:e,dnsSecondary:Vn}:{protocol:e}),options:_,value:l.protocol,variant:`emphasized`}),L(`span`,{class:`mt-2 block text-xs font-semibold leading-5 text-slate-500`,children:`대부분의 가정용 인터넷과 상위 공유기 연결은 자동 IP(DHCP)를 사용합니다.`})]}),l.protocol===`pppoe`?L(`div`,{class:`mt-6 grid gap-5 lg:grid-cols-2`,children:[L(`label`,{class:`block`,children:[L(`span`,{class:`text-sm font-extrabold text-slate-800`,children:`PPPoE 사용자명`}),L(`input`,{autocomplete:`username`,class:`mt-2 min-h-11 w-full rounded-xl border-2 border-slate-300 bg-slate-50 px-4 py-2.5 text-sm font-semibold text-slate-950 shadow-inner outline-none transition focus:border-teal-500 focus:bg-white focus:ring-4 focus:ring-teal-100 disabled:cursor-not-allowed disabled:bg-slate-100`,disabled:y,onInput:e=>b({pppoeUsername:e.currentTarget.value}),spellcheck:!1,value:l.pppoeUsername})]}),L(`label`,{class:`block`,children:[L(`span`,{class:`text-sm font-extrabold text-slate-800`,children:`PPPoE 비밀번호`}),L(`span`,{class:`relative mt-2 block`,children:[L(`input`,{autocomplete:`new-password`,class:`min-h-11 w-full rounded-xl border-2 border-slate-300 bg-slate-50 px-4 py-2.5 pr-12 text-sm font-semibold text-slate-950 shadow-inner outline-none transition focus:border-teal-500 focus:bg-white focus:ring-4 focus:ring-teal-100 disabled:cursor-not-allowed disabled:bg-slate-100`,disabled:y,onInput:e=>b({pppoePassword:e.currentTarget.value}),placeholder:t.configuration.protocol===`pppoe`&&t.configuration.pppoe.passwordConfigured?`변경할 때만 입력`:`PPPoE 비밀번호`,...p?{type:`text`}:{type:`password`},value:l.pppoePassword}),L(`button`,{"aria-label":p?`비밀번호 숨기기`:`비밀번호 표시`,class:`absolute inset-y-0 right-1 grid w-10 place-items-center rounded-lg border-0 bg-transparent text-slate-500`,disabled:y,onClick:()=>m(e=>!e),type:`button`,children:L(p?ct:st,{class:`size-5`})})]}),L(`span`,{class:`mt-2 block text-xs text-slate-500`,children:t.configuration.protocol===`pppoe`&&t.configuration.pppoe.passwordConfigured?`비워 두면 현재 저장된 비밀번호를 그대로 사용합니다.`:`통신사에서 제공한 PPPoE 비밀번호를 입력해 주세요.`})]})]}):null,l.protocol===`static`?L(`div`,{class:`mt-6 grid gap-5 lg:grid-cols-2`,children:[L(`label`,{class:`block`,children:[L(`span`,{class:`text-sm font-extrabold text-slate-800`,children:`IPv4 주소`}),L(Ln,{disabled:y,label:`IPv4 주소`,onChange:e=>b({staticAddress:e}),value:l.staticAddress})]}),L(`div`,{class:`block`,children:[L(`span`,{class:`text-sm font-extrabold text-slate-800`,children:`서브넷 마스크`}),L(Nn,{ariaLabel:`WAN 서브넷 마스크`,className:`mt-2`,disabled:y,id:`wan-static-prefix-length`,onChange:e=>b({staticPrefixLength:Number(e)}),options:Hn.map(e=>({value:String(e),label:`/${e} · ${Yn(e)}`})),value:String(l.staticPrefixLength),variant:`emphasized`})]}),L(`label`,{class:`block`,children:[L(`span`,{class:`text-sm font-extrabold text-slate-800`,children:`기본 게이트웨이`}),L(Ln,{disabled:y,label:`기본 게이트웨이`,onChange:e=>b({staticGateway:e}),value:l.staticGateway})]}),L(`label`,{class:`block`,children:[L(`span`,{class:`text-sm font-extrabold text-slate-800`,children:`기본 DNS`}),L(Ln,{disabled:y,label:`기본 DNS`,onChange:e=>b({dnsPrimary:e}),value:l.dnsPrimary})]}),L(`label`,{class:`block lg:col-start-2`,children:[L(`span`,{class:`text-sm font-extrabold text-slate-800`,children:`보조 DNS (선택)`}),L(Ln,{disabled:y,label:`보조 DNS`,onChange:e=>b({dnsSecondary:e}),value:l.dnsSecondary})]})]}):null,l.protocol===`dhcp`?L(`div`,{class:`mt-6 rounded-xl border border-slate-200 bg-slate-50 px-4 py-4 text-sm font-semibold leading-6 text-slate-600`,children:`상위 공유기 또는 통신사 장비에서 IP 주소, 기본 게이트웨이와 DNS 정보를 자동으로 받습니다. 별도의 계정이나 고정 주소가 없다면 이 방식을 사용하세요.`}):null,h?L(`p`,{class:`mt-5 mb-0 rounded-xl bg-red-50 px-4 py-3 text-sm font-bold text-red-800`,children:h}):null,L(`div`,{class:`mt-6 flex flex-col gap-3 border-t border-slate-200 pt-5 sm:flex-row sm:items-center sm:justify-between`,children:[L(`p`,{class:`m-0 text-xs font-semibold leading-5 text-slate-500`,children:`설정 저장 후 WAN만 다시 연결하며 LAN 관리 페이지와 내부 네트워크 설정은 유지됩니다.`}),L(`button`,{class:`inline-flex min-h-11 w-full shrink-0 items-center justify-center gap-2 rounded-xl border-0 bg-teal-700 px-5 py-2.5 text-sm font-extrabold text-white shadow-sm transition hover:bg-teal-800 focus:outline-none focus-visible:ring-4 focus-visible:ring-teal-200 disabled:cursor-wait disabled:opacity-60 sm:w-auto`,disabled:y||!d,type:`submit`,children:[L(B,{class:`size-5 ${e===`saving`?`animate-pulse`:``}`}),e===`saving`?`저장 중`:`설정 저장`]})]})]})]})}var er=[16,20,22,23,24,25,26,27,28],tr=[[`30m`,`30분`],[`1h`,`1시간`],[`6h`,`6시간`],[`12h`,`12시간`],[`24h`,`24시간`],[`3d`,`3일`],[`7d`,`7일`]];function nr(e){let t=e.trim().split(`.`);return t.length===4&&t.every(e=>{if(!/^(0|[1-9][0-9]{0,2})$/.test(e))return!1;let t=Number(e);return t>=0&&t<=255})}function rr(e){let t=Pn(e);return t.slice(0,3).some(e=>e===``||Number(e)>255)?null:t.slice(0,3).join(`.`)}function ir(e){return Pn(e)[3]}function ar(e){let t=rr(e.lan.address),n=ir(e.dhcp.start??``),r=ir(e.dhcp.end??``);return{ipAddress:e.lan.address,prefixLength:e.lan.prefixLength,dhcpEnabled:e.dhcp.enabled,dhcpStart:t&&n?`${t}.${n}`:e.dhcp.start??``,dhcpEnd:t&&r?`${t}.${r}`:e.dhcp.end??``,leaseTime:e.dhcp.leaseTime}}function or({disabled:e,label:t,lanAddress:n,value:r,onChange:i}){let a=rr(n),o=a?.split(`.`)??[``,``,``],s=ir(r),c=e=>{let t=Fn(e);i(a&&t!==``?`${a}.${t}`:t)};return L(`label`,{class:`block`,children:[L(`span`,{class:`text-sm font-extrabold text-slate-800`,children:t}),L(`div`,{class:`ssh-ipv4-segments mt-2 min-h-11`,"aria-label":`${t} 입력`,children:[o.map((e,t)=>L(`div`,{class:`ssh-ipv4-segment`,children:[L(`span`,{class:`grid min-h-11 w-full min-w-0 max-w-full place-items-center rounded-xl border border-slate-200 bg-slate-100 px-1.5 py-2.5 text-center text-sm font-bold text-slate-500 sm:px-2`,children:e||`—`}),L(`span`,{"aria-hidden":`true`,class:`ssh-ipv4-separator text-sm font-black text-slate-400`,children:`.`})]},t)),L(`div`,{class:`ssh-ipv4-segment`,children:L(`input`,{"aria-label":`${t} 마지막 옥텟`,autocomplete:`off`,class:`min-h-11 w-full min-w-0 max-w-full rounded-xl border-2 border-slate-300 bg-slate-50 px-1.5 py-2.5 text-center text-sm font-semibold text-slate-950 shadow-inner outline-none transition focus:border-teal-500 focus:bg-white focus:ring-4 focus:ring-teal-100 disabled:cursor-not-allowed disabled:bg-slate-100 sm:px-2`,disabled:e||!a,inputMode:`numeric`,maxLength:3,onInput:e=>c(e.currentTarget.value),pattern:`[0-9]*`,spellcheck:!1,value:s})})]}),L(`span`,{class:`mt-2 block text-xs text-slate-500`,children:`앞 3개 주소는 공유기 IP 주소와 동일하게 유지됩니다.`})]})}function sr(e){return`${window.location.protocol}//${e}${window.location.pathname}${window.location.search}${window.location.hash}`}function cr({feedback:e,onDismiss:t}){return L(`div`,{"aria-live":`polite`,class:`mb-5 rounded-2xl border px-4 py-4 text-sm ${e.kind===`success`?`border-emerald-200 bg-emerald-50 text-emerald-900`:e.kind===`warning`?`border-amber-200 bg-amber-50 text-amber-950`:`border-red-200 bg-red-50 text-red-900`}`,children:[L(`div`,{class:`flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between`,children:[L(`p`,{class:`m-0 font-bold leading-6`,children:e.message}),L(`button`,{class:`min-h-10 shrink-0 rounded-lg border border-current bg-transparent px-3 py-1.5 text-xs font-extrabold`,onClick:t,type:`button`,children:`닫기`})]}),e.newAddress?L(`a`,{class:`mt-3 inline-flex min-h-10 items-center rounded-lg border border-current px-3 py-2 font-extrabold underline underline-offset-2`,href:sr(e.newAddress),children:[`새 공유기 주소 `,e.newAddress,`로 연결`]}):null]})}function lr({data:e}){let t=e.conflict.detected;return L(`section`,{class:`grid gap-4 lg:grid-cols-3`,"aria-label":`내부 네트워크 상태`,children:[L(`article`,{class:`rounded-2xl border border-slate-200 bg-white p-5 shadow-sm shadow-slate-900/5`,children:[L(`span`,{class:`grid size-10 place-items-center rounded-xl bg-teal-50 text-teal-700`,children:L(We,{class:`size-5`})}),L(`p`,{class:`mt-4 mb-0 text-xs font-extrabold uppercase tracking-[0.14em] text-slate-500`,children:`SmartSafeHub LAN`}),L(`p`,{class:`mt-2 mb-0 text-xl font-black text-slate-950`,children:e.lan.subnet}),L(`p`,{class:`mt-1 mb-0 text-sm font-semibold text-slate-600`,children:[`공유기 주소 `,e.lan.address]})]}),L(`article`,{class:`rounded-2xl border border-slate-200 bg-white p-5 shadow-sm shadow-slate-900/5`,children:[L(`span`,{class:`grid size-10 place-items-center rounded-xl bg-slate-100 text-slate-700`,children:L($e,{class:`size-5`})}),L(`p`,{class:`mt-4 mb-0 text-xs font-extrabold uppercase tracking-[0.14em] text-slate-500`,children:`상위 네트워크`}),L(`p`,{class:`mt-2 mb-0 text-xl font-black text-slate-950`,children:e.wan.subnet??`확인 전`}),L(`p`,{class:`mt-1 mb-0 text-sm font-semibold text-slate-600`,children:e.wan.connected?`WAN ${e.wan.address??`주소 확인 중`}`:`WAN 연결 후 자동으로 확인합니다.`})]}),L(`article`,{class:`rounded-2xl border p-5 shadow-sm shadow-slate-900/5 ${t?`border-red-200 bg-red-50 text-red-950`:`border-emerald-200 bg-emerald-50 text-emerald-950`}`,children:[L(`span`,{class:`grid size-10 place-items-center rounded-xl ${t?`bg-red-100 text-red-700`:`bg-emerald-100 text-emerald-700`}`,children:L(t?V:B,{class:`size-5`})}),L(`p`,{class:`mt-4 mb-0 text-xs font-extrabold uppercase tracking-[0.14em] opacity-70`,children:`대역 충돌`}),L(`p`,{class:`mt-2 mb-0 text-lg font-black`,children:t?`주소 대역이 겹칩니다`:`충돌이 감지되지 않았습니다`}),L(`p`,{class:`mt-1 mb-0 text-sm font-semibold leading-6 opacity-80`,children:t?`상위 네트워크 ${e.conflict.wanSubnet??``}와 다른 LAN 대역이 필요합니다.`:e.wan.connected?`현재 WAN과 LAN이 서로 다른 IPv4 대역을 사용합니다.`:`WAN이 연결되면 상위 네트워크와의 충돌 여부를 확인합니다.`})]})]})}function ur({action:e,data:t,error:n,feedback:r,loading:i,onApplyRecommendation:a,onDismissFeedback:o,onRetry:s,onSave:c}){let[l,u]=F(``),[d,f]=F(24),[p,m]=F(!0),[h,g]=F(``),[_,y]=F(``),[b,x]=F(`12h`),[S,C]=F(!1),[w,T]=F(null),E=(e={})=>{if(!t)return!1;let n=ar(t);return(e.ipAddress??l).trim()!==n.ipAddress||(e.prefixLength??d)!==n.prefixLength||(e.dhcpEnabled??p)!==n.dhcpEnabled||(e.dhcpStart??h).trim()!==n.dhcpStart||(e.dhcpEnd??_).trim()!==n.dhcpEnd||(e.leaseTime??b)!==n.leaseTime},D=e=>{let t=rr(e);if(u(e),!t){C(E({ipAddress:e}));return}let n=ir(h),r=ir(_),i=n?`${t}.${n}`:``,a=r?`${t}.${r}`:``;g(i),y(a),C(E({ipAddress:e,dhcpStart:i,dhcpEnd:a}))},O=e=>{g(e),C(E({dhcpStart:e}))},k=e=>{y(e),C(E({dhcpEnd:e}))},A=e=>{f(e),C(E({prefixLength:e}))},j=e=>{x(e),C(E({leaseTime:e}))},M=e=>{m(e),C(E({dhcpEnabled:e}))};I(()=>{if(!t||S)return;let e=ar(t);u(e.ipAddress),f(e.prefixLength),m(e.dhcpEnabled),g(e.dhcpStart),y(e.dhcpEnd),x(e.leaseTime),T(null)},[t,S]);let ee=Te(()=>{let e=[...er];return e.includes(d)||(e.push(d),e.sort((e,t)=>e-t)),e},[d]),te=Te(()=>{let e=[...tr];return e.some(([e])=>e===b)||e.push([b,`현재 설정 (${b})`]),e},[b]);if(i)return L(Rn,{});if(n)return L(zn,{message:n,onRetry:s});if(!t)return null;let ne=e!==null,re=async e=>{e.preventDefault(),nr(l)?!nr(h)||!nr(_)?T(`DHCP 시작/종료 주소를 올바른 IPv4 형식으로 입력해 주세요.`):window.confirm(`내부 네트워크 설정을 적용하면 연결된 기기가 새 DHCP 정보를 받는 동안 잠시 연결이 끊길 수 있습니다. 계속하시겠습니까?`)&&(T(null),await c({ipAddress:l.trim(),prefixLength:d,dhcpEnabled:p,dhcpStart:h.trim(),dhcpEnd:_.trim(),leaseTime:b})&&C(!1)):T(`공유기 주소를 올바른 IPv4 형식으로 입력해 주세요.`)},ie=async()=>{t.recommendation&&window.confirm(`상위 네트워크와 겹치지 않는 ${t.recommendation.subnet} 대역으로 자동 변경합니다. 현재 연결이 잠시 끊길 수 있습니다. 계속하시겠습니까?`)&&(T(null),await a()&&C(!1))};return L(v,{children:[r?L(cr,{feedback:r,onDismiss:o}):null,L(lr,{data:t}),t.conflict.detected?L(`section`,{class:`mt-5 rounded-2xl border border-amber-200 bg-amber-50 p-5 text-amber-950 sm:p-6`,children:L(`div`,{class:`flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between`,children:[L(`div`,{class:`min-w-0`,children:[L(`p`,{class:`m-0 text-xs font-extrabold uppercase tracking-[0.16em] text-amber-700`,children:`자동 충돌 해결`}),L(`h2`,{class:`mt-2 mb-0 text-lg font-black`,children:`안전한 LAN 대역으로 변경할 수 있습니다`}),L(`p`,{class:`mt-2 mb-0 text-sm font-semibold leading-6 text-amber-900/80`,children:t.recommendation?`${t.recommendation.subnet} · DHCP ${t.recommendation.dhcpStart} ~ ${t.recommendation.dhcpEnd}`:`현재 활성 네트워크와 겹치지 않는 추천 대역을 찾지 못했습니다. 아래에서 직접 설정해 주세요.`})]}),t.recommendation?L(`button`,{class:`inline-flex min-h-11 shrink-0 items-center justify-center gap-2 rounded-xl border-0 bg-amber-700 px-5 py-2.5 text-sm font-extrabold text-white transition hover:bg-amber-800 focus:outline-none focus-visible:ring-4 focus-visible:ring-amber-200 disabled:cursor-wait disabled:opacity-60`,disabled:ne,onClick:()=>void ie(),type:`button`,children:[L(B,{class:`size-5 ${e===`auto`?`animate-pulse`:``}`}),e===`auto`?`변경 중`:`추천 대역으로 자동 변경`]}):null]})}):null,L(`form`,{class:`mt-5 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm shadow-slate-900/5 sm:p-6`,onSubmit:e=>void re(e),children:[L(`div`,{class:`flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between`,children:[L(`div`,{class:`flex min-w-0 items-start gap-3`,children:[L(`span`,{class:`grid size-11 shrink-0 place-items-center rounded-xl bg-teal-50 text-teal-700`,children:L(Qe,{class:`size-6`})}),L(`div`,{class:`min-w-0`,children:[L(`p`,{class:`m-0 text-xs font-extrabold uppercase tracking-[0.16em] text-slate-500`,children:`내부 네트워크`}),L(`h2`,{class:`mt-2 mb-0 text-xl font-black text-slate-950`,children:`LAN 및 DHCP 설정`}),L(`p`,{class:`mt-2 mb-0 text-sm font-semibold leading-6 text-slate-600`,children:`상위 공유기와 다른 주소 대역을 사용해야 안정적으로 라우팅할 수 있습니다.`})]})]}),S?L(`span`,{"aria-live":`polite`,class:`inline-flex shrink-0 self-start items-center gap-1.5 rounded-full border border-amber-200 bg-amber-50 px-2.5 py-1 text-xs font-extrabold text-amber-800`,role:`status`,children:[L(V,{"aria-hidden":`true`,class:`size-3.5 shrink-0`}),`저장되지 않음`]}):null]}),L(`div`,{class:`mt-6 grid gap-5 lg:grid-cols-2`,children:[L(`div`,{class:`block`,children:[L(`span`,{class:`text-sm font-extrabold text-slate-800`,children:`공유기 IP 주소`}),L(Ln,{disabled:ne,label:`공유기 IP 주소`,onChange:D,value:l})]}),L(`div`,{class:`block`,children:[L(`span`,{class:`text-sm font-extrabold text-slate-800`,children:`현재 LAN 대역`}),L(`div`,{class:`mt-2 min-h-11 rounded-xl border border-slate-200 bg-slate-50 px-4 py-3 text-sm font-bold text-slate-700`,children:t.lan.subnet}),L(`span`,{class:`mt-2 block text-xs text-slate-500`,children:`서브넷 마스크는 아래 고급 설정에서 변경할 수 있습니다.`})]}),L(or,{disabled:ne,label:`DHCP 시작 주소`,lanAddress:l,onChange:O,value:h}),L(or,{disabled:ne,label:`DHCP 종료 주소`,lanAddress:l,onChange:k,value:_})]}),L(`details`,{class:`mt-6 rounded-xl border border-slate-200 bg-slate-50 p-4`,children:[L(`summary`,{class:`cursor-pointer text-sm font-black text-slate-900`,children:`고급 DHCP 설정`}),L(`div`,{class:`mt-5 grid gap-5 lg:grid-cols-2`,children:[L(`div`,{class:`block`,children:[L(`span`,{class:`text-sm font-extrabold text-slate-800`,children:`서브넷 마스크`}),L(Nn,{ariaLabel:`서브넷 마스크`,className:`mt-2`,disabled:ne,id:`lan-prefix-length`,onChange:e=>A(Number(e)),options:ee.map(e=>({value:String(e),label:`/${e} · ${e===t.lan.prefixLength?t.lan.netmask:`CIDR /${e}`}`})),value:String(d),variant:`emphasized`}),L(`span`,{class:`mt-2 block text-xs text-slate-500`,children:`일반 가정용 네트워크는 /24 사용을 권장합니다.`})]}),L(`div`,{class:`block`,children:[L(`span`,{class:`text-sm font-extrabold text-slate-800`,children:`DHCP 임대 시간`}),L(Nn,{ariaLabel:`DHCP 임대 시간`,className:`mt-2`,disabled:ne,id:`lan-dhcp-lease-time`,onChange:j,options:te.map(([e,t])=>({value:e,label:t})),value:b,variant:`emphasized`})]}),L(`label`,{class:`inline-flex min-h-11 items-center gap-3 text-sm font-extrabold text-slate-800 lg:col-span-2`,children:[L(`input`,{checked:p,class:`size-5 accent-teal-700`,disabled:ne,onChange:e=>M(e.currentTarget.checked),type:`checkbox`}),`SmartSafeHub DHCP 서버 사용`]}),L(`p`,{class:`m-0 text-xs font-semibold leading-5 text-slate-500 lg:col-span-2`,children:`DHCP를 끄면 SmartSafeHub에 연결되는 장치에 IP 주소를 자동으로 할당하지 않습니다. 별도의 DHCP 서버를 사용하는 고급 구성에서만 비활성화하세요.`})]})]}),w?L(`p`,{class:`mt-5 mb-0 rounded-xl bg-red-50 px-4 py-3 text-sm font-bold text-red-800`,children:w}):null,L(`div`,{class:`mt-6 flex flex-col gap-3 border-t border-slate-200 pt-5 sm:flex-row sm:items-center sm:justify-between`,children:[L(`p`,{class:`m-0 text-xs font-semibold leading-5 text-slate-500`,children:`주소가 바뀌면 현재 관리 페이지 연결이 종료되며 새 IP를 받아 다시 접속해야 합니다.`}),L(`button`,{class:`inline-flex min-h-11 w-full shrink-0 items-center justify-center gap-2 rounded-xl border-0 bg-teal-700 px-5 py-2.5 text-sm font-extrabold text-white shadow-sm transition hover:bg-teal-800 focus:outline-none focus-visible:ring-4 focus-visible:ring-teal-200 disabled:cursor-wait disabled:opacity-60 sm:w-auto`,disabled:ne||!S,type:`submit`,children:[L(B,{class:`size-5 ${e===`saving`?`animate-pulse`:``}`}),e===`saving`?`저장 중`:`설정 저장`]})]})]})]})}var dr={};dr.QR8bitByte=(e,t)=>{var n=t("QRMode");function r(e){this.mode=n.MODE_8BIT_BYTE,this.data=e}r.prototype={getLength:function(){return this.data.length},write:function(e){for(var t=0;t{function n(){this.buffer=[],this.length=0}n.prototype={get:function(e){var t=Math.floor(e/8);return(this.buffer[t]>>>7-e%8&1)==1},put:function(e,t){for(var n=0;n>>t-n-1&1)==1)},getLengthInBits:function(){return this.length},putBit:function(e){var t=Math.floor(this.length/8);this.buffer.length<=t&&this.buffer.push(0),e&&(this.buffer[t]|=128>>>this.length%8),this.length++}},e.exports=n},dr.QRErrorCorrectLevel=(e,t)=>{e.exports={L:1,M:0,Q:3,H:2}},dr.QRMaskPattern=(e,t)=>{e.exports={PATTERN000:0,PATTERN001:1,PATTERN010:2,PATTERN011:3,PATTERN100:4,PATTERN101:5,PATTERN110:6,PATTERN111:7}},dr.QRMath=(e,t)=>{for(var n={glog:function(e){if(e<1)throw Error(`glog(`+e+`)`);return n.LOG_TABLE[e]},gexp:function(e){for(;e<0;)e+=255;for(;e>=256;)e-=255;return n.EXP_TABLE[e]},EXP_TABLE:Array(256),LOG_TABLE:Array(256)},r=0;r<8;r++)n.EXP_TABLE[r]=1<{e.exports={MODE_NUMBER:1,MODE_ALPHA_NUM:2,MODE_8BIT_BYTE:4,MODE_KANJI:8}},dr.QRPolynomial=(e,t)=>{var n=t("QRMath");function r(e,t){if(e.length===void 0)throw Error(e.length+`/`+t);for(var n=0;n{var n=t("QRErrorCorrectLevel");function r(e,t){this.totalCount=e,this.dataCount=t}r.RS_BLOCK_TABLE=[[1,26,19],[1,26,16],[1,26,13],[1,26,9],[1,44,34],[1,44,28],[1,44,22],[1,44,16],[1,70,55],[1,70,44],[2,35,17],[2,35,13],[1,100,80],[2,50,32],[2,50,24],[4,25,9],[1,134,108],[2,67,43],[2,33,15,2,34,16],[2,33,11,2,34,12],[2,86,68],[4,43,27],[4,43,19],[4,43,15],[2,98,78],[4,49,31],[2,32,14,4,33,15],[4,39,13,1,40,14],[2,121,97],[2,60,38,2,61,39],[4,40,18,2,41,19],[4,40,14,2,41,15],[2,146,116],[3,58,36,2,59,37],[4,36,16,4,37,17],[4,36,12,4,37,13],[2,86,68,2,87,69],[4,69,43,1,70,44],[6,43,19,2,44,20],[6,43,15,2,44,16],[4,101,81],[1,80,50,4,81,51],[4,50,22,4,51,23],[3,36,12,8,37,13],[2,116,92,2,117,93],[6,58,36,2,59,37],[4,46,20,6,47,21],[7,42,14,4,43,15],[4,133,107],[8,59,37,1,60,38],[8,44,20,4,45,21],[12,33,11,4,34,12],[3,145,115,1,146,116],[4,64,40,5,65,41],[11,36,16,5,37,17],[11,36,12,5,37,13],[5,109,87,1,110,88],[5,65,41,5,66,42],[5,54,24,7,55,25],[11,36,12],[5,122,98,1,123,99],[7,73,45,3,74,46],[15,43,19,2,44,20],[3,45,15,13,46,16],[1,135,107,5,136,108],[10,74,46,1,75,47],[1,50,22,15,51,23],[2,42,14,17,43,15],[5,150,120,1,151,121],[9,69,43,4,70,44],[17,50,22,1,51,23],[2,42,14,19,43,15],[3,141,113,4,142,114],[3,70,44,11,71,45],[17,47,21,4,48,22],[9,39,13,16,40,14],[3,135,107,5,136,108],[3,67,41,13,68,42],[15,54,24,5,55,25],[15,43,15,10,44,16],[4,144,116,4,145,117],[17,68,42],[17,50,22,6,51,23],[19,46,16,6,47,17],[2,139,111,7,140,112],[17,74,46],[7,54,24,16,55,25],[34,37,13],[4,151,121,5,152,122],[4,75,47,14,76,48],[11,54,24,14,55,25],[16,45,15,14,46,16],[6,147,117,4,148,118],[6,73,45,14,74,46],[11,54,24,16,55,25],[30,46,16,2,47,17],[8,132,106,4,133,107],[8,75,47,13,76,48],[7,54,24,22,55,25],[22,45,15,13,46,16],[10,142,114,2,143,115],[19,74,46,4,75,47],[28,50,22,6,51,23],[33,46,16,4,47,17],[8,152,122,4,153,123],[22,73,45,3,74,46],[8,53,23,26,54,24],[12,45,15,28,46,16],[3,147,117,10,148,118],[3,73,45,23,74,46],[4,54,24,31,55,25],[11,45,15,31,46,16],[7,146,116,7,147,117],[21,73,45,7,74,46],[1,53,23,37,54,24],[19,45,15,26,46,16],[5,145,115,10,146,116],[19,75,47,10,76,48],[15,54,24,25,55,25],[23,45,15,25,46,16],[13,145,115,3,146,116],[2,74,46,29,75,47],[42,54,24,1,55,25],[23,45,15,28,46,16],[17,145,115],[10,74,46,23,75,47],[10,54,24,35,55,25],[19,45,15,35,46,16],[17,145,115,1,146,116],[14,74,46,21,75,47],[29,54,24,19,55,25],[11,45,15,46,46,16],[13,145,115,6,146,116],[14,74,46,23,75,47],[44,54,24,7,55,25],[59,46,16,1,47,17],[12,151,121,7,152,122],[12,75,47,26,76,48],[39,54,24,14,55,25],[22,45,15,41,46,16],[6,151,121,14,152,122],[6,75,47,34,76,48],[46,54,24,10,55,25],[2,45,15,64,46,16],[17,152,122,4,153,123],[29,74,46,14,75,47],[49,54,24,10,55,25],[24,45,15,46,46,16],[4,152,122,18,153,123],[13,74,46,32,75,47],[48,54,24,14,55,25],[42,45,15,32,46,16],[20,147,117,4,148,118],[40,75,47,7,76,48],[43,54,24,22,55,25],[10,45,15,67,46,16],[19,148,118,6,149,119],[18,75,47,31,76,48],[34,54,24,34,55,25],[20,45,15,61,46,16]],r.getRSBlocks=function(e,t){var n=r.getRsBlockTable(e,t);if(n===void 0)throw Error(`bad rs block @ typeNumber:`+e+`/errorCorrectLevel:`+t);for(var i=n.length/3,a=[],o=0;o{var n=t("QRMode"),r=t("QRPolynomial"),i=t("QRMath"),a=t("QRMaskPattern"),o={PATTERN_POSITION_TABLE:[[],[6,18],[6,22],[6,26],[6,30],[6,34],[6,22,38],[6,24,42],[6,26,46],[6,28,50],[6,30,54],[6,32,58],[6,34,62],[6,26,46,66],[6,26,48,70],[6,26,50,74],[6,30,54,78],[6,30,56,82],[6,30,58,86],[6,34,62,90],[6,28,50,72,94],[6,26,50,74,98],[6,30,54,78,102],[6,28,54,80,106],[6,32,58,84,110],[6,30,58,86,114],[6,34,62,90,118],[6,26,50,74,98,122],[6,30,54,78,102,126],[6,26,52,78,104,130],[6,30,56,82,108,134],[6,34,60,86,112,138],[6,30,58,86,114,142],[6,34,62,90,118,146],[6,30,54,78,102,126,150],[6,24,50,76,102,128,154],[6,28,54,80,106,132,158],[6,32,58,84,110,136,162],[6,26,54,82,110,138,166],[6,30,58,86,114,142,170]],G15:1335,G18:7973,G15_MASK:21522,getBCHTypeInfo:function(e){for(var t=e<<10;o.getBCHDigit(t)-o.getBCHDigit(o.G15)>=0;)t^=o.G15<=0;)t^=o.G18<>>=1;return t},getPatternPosition:function(e){return o.PATTERN_POSITION_TABLE[e-1]},getMask:function(e,t,n){switch(e){case a.PATTERN000:return(t+n)%2==0;case a.PATTERN001:return t%2==0;case a.PATTERN010:return n%3==0;case a.PATTERN011:return(t+n)%3==0;case a.PATTERN100:return(Math.floor(t/2)+Math.floor(n/3))%2==0;case a.PATTERN101:return t*n%2+t*n%3==0;case a.PATTERN110:return(t*n%2+t*n%3)%2==0;case a.PATTERN111:return(t*n%3+(t+n)%2)%2==0;default:throw Error(`bad maskPattern:`+e)}},getErrorCorrectPolynomial:function(e){for(var t=new r([1],0),n=0;n5&&(n+=3+a-5)}for(r=0;r{var n=t("QR8bitByte"),r=t("QRUtil"),i=t("QRPolynomial"),a=t("QRRSBlock"),o=t("QRBitBuffer");function s(e,t){this.typeNumber=e,this.errorCorrectLevel=t,this.modules=null,this.moduleCount=0,this.dataCache=null,this.dataList=[]}s.prototype={addData:function(e){var t=new n(e);this.dataList.push(t),this.dataCache=null},isDark:function(e,t){if(e<0||this.moduleCount<=e||t<0||this.moduleCount<=t)throw Error(e+`,`+t);return this.modules[e][t]},getModuleCount:function(){return this.moduleCount},make:function(){if(this.typeNumber<1){var e=1;for(e=1;e<40;e++){for(var t=a.getRSBlocks(e,this.errorCorrectLevel),n=new o,i=0,s=0;s=7&&this.setupTypeNumber(e),this.dataCache===null&&(this.dataCache=s.createData(this.typeNumber,this.errorCorrectLevel,this.dataList)),this.mapData(this.dataCache,t)},setupPositionProbePattern:function(e,t){for(var n=-1;n<=7;n++)if(!(e+n<=-1||this.moduleCount<=e+n))for(var r=-1;r<=7;r++)t+r<=-1||this.moduleCount<=t+r||(0<=n&&n<=6&&(r===0||r===6)||0<=r&&r<=6&&(n===0||n===6)||2<=n&&n<=4&&2<=r&&r<=4?this.modules[e+n][t+r]=!0:this.modules[e+n][t+r]=!1)},getBestMaskPattern:function(){for(var e=0,t=0,n=0;n<8;n++){this.makeImpl(!0,n);var i=r.getLostPoint(this);(n===0||e>i)&&(e=i,t=n)}return t},createMovieClip:function(e,t,n){var r=e.createEmptyMovieClip(t,n),i=1;this.make();for(var a=0;a>i&1)==1,this.modules[Math.floor(i/3)][i%3+this.moduleCount-8-3]=n;for(var a=0;a<18;a++)n=!e&&(t>>a&1)==1,this.modules[a%3+this.moduleCount-8-3][Math.floor(a/3)]=n},setupTypeInfo:function(e,t){for(var n=this.errorCorrectLevel<<3|t,i=r.getBCHTypeInfo(n),a,o=0;o<15;o++)a=!e&&(i>>o&1)==1,o<6?this.modules[o][8]=a:o<8?this.modules[o+1][8]=a:this.modules[this.moduleCount-15+o][8]=a;for(var s=0;s<15;s++)a=!e&&(i>>s&1)==1,s<8?this.modules[8][this.moduleCount-s-1]=a:s<9?this.modules[8][15-s-1+1]=a:this.modules[8][15-s-1]=a;this.modules[this.moduleCount-8][8]=!e},mapData:function(e,t){for(var n=-1,i=this.moduleCount-1,a=7,o=0,s=this.moduleCount-1;s>0;s-=2)for(s===6&&s--;;){for(var c=0;c<2;c++)if(this.modules[i][s-c]===null){var l=!1;o>>a&1)==1),r.getMask(t,i,s-c)&&(l=!l),this.modules[i][s-c]=l,a--,a===-1&&(o++,a=7)}if(i+=n,i<0||this.moduleCount<=i){i-=n,n=-n;break}}}},s.PAD0=236,s.PAD1=17,s.createData=function(e,t,n){for(var i=a.getRSBlocks(e,t),c=new o,l=0;ld*8)throw Error(`code length overflow. (`+c.getLengthInBits()+`>`+d*8+`)`);for(c.getLengthInBits()+4<=d*8&&c.put(0,4);c.getLengthInBits()%8!=0;)c.putBit(!1);for(;!(c.getLengthInBits()>=d*8||(c.put(s.PAD0,8),c.getLengthInBits()>=d*8));)c.put(s.PAD1,8);return s.createBytes(c,i)},s.createBytes=function(e,t){for(var n=0,a=0,o=0,s=Array(t.length),c=Array(t.length),l=0;l=0?m.get(g):0}}for(var _=0,v=0;vString.fromCharCode(e)).join(``),r=new(pr(`index`))(0,pr(`QRErrorCorrectLevel`).M);r.addData(n),r.make();let i=r.getModuleCount();return Array.from({length:i},(e,t)=>Array.from({length:i},(e,n)=>!!r.isDark(t,n)))}function hr(e,t,n){let r=e=>e.replace(/([\\;,:"])/g,`\\$1`);return`WIFI:T:${t===`none`?`nopass`:t===`sae`?`WPA3`:`WPA`};S:${r(e)};P:${r(n)};;`}function gr(e){return`${(e.replace(/[\\/:*?"<>|\x00-\x1f\x7f]/g,`_`).replace(/[. ]+$/g,``).trim()||`Wi-Fi`).slice(0,64)}-QR.png`}function _r(e,t,n=8){let r=(e.length+8)*n;t.fillStyle=`#ffffff`,t.fillRect(0,0,r,r),t.fillStyle=`#000000`,e.forEach((e,r)=>e.forEach((e,i)=>{e&&t.fillRect((i+4)*n,(r+4)*n,n,n)}))}function vr({load:e,onClose:t}){let[n,r]=F(null),[i,a]=F(null),[o,s]=F(null);I(()=>{let t=!0;return e().then(e=>{if(t)try{r({ssid:e.ssid,matrix:mr(hr(e.ssid,e.security,e.password))})}catch{a(`QR 코드를 생성할 수 없습니다.`)}}).catch(()=>{t&&a(`Wi-Fi 정보를 불러오지 못했습니다.`)}),()=>{t=!1,r(null)}},[e]);let c=n?.matrix.length??0;return L(`div`,{class:`fixed inset-0 z-50 flex items-center justify-center bg-slate-950/70 p-4`,role:`presentation`,onClick:e=>{e.target===e.currentTarget&&t()},children:L(`section`,{role:`dialog`,"aria-modal":`true`,"aria-label":`Wi-Fi QR 코드`,class:`w-full max-w-sm rounded-2xl bg-white p-5 text-center text-slate-950 shadow-2xl sm:p-7`,children:[L(`h2`,{class:`m-0 text-xl font-extrabold`,children:`Wi-Fi QR 코드`}),L(`p`,{class:`mt-2 break-all text-sm font-semibold text-slate-600`,children:n?.ssid??`Wi-Fi 정보를 확인하는 중`}),i?L(`p`,{role:`alert`,class:`my-6 text-sm font-bold text-red-700`,children:i}):null,!n&&!i?L(`p`,{class:`my-6 text-sm`,children:`QR 코드를 만드는 중...`}):null,n?L(`div`,{class:`mx-auto my-5 w-full max-w-[288px] rounded-xl border border-slate-200 bg-white p-3`,children:L(`svg`,{class:`block h-auto w-full`,viewBox:`0 0 ${c+8} ${c+8}`,role:`img`,"aria-label":`${n.ssid} Wi-Fi 연결 QR 코드`,"shape-rendering":`crispEdges`,children:[L(`rect`,{width:c+8,height:c+8,fill:`white`}),n.matrix.map((e,t)=>e.map((e,n)=>e?L(`rect`,{x:n+4,y:t+4,width:`1`,height:`1`,fill:`black`},`${t}-${n}`):null))]})}):null,L(`p`,{class:`text-xs leading-5 text-slate-500`,children:[L(`span`,{class:`block`,children:`카메라로 스캔해 Wi-Fi에 연결하세요.`}),L(`span`,{class:`block`,children:`QR 코드에는 비밀번호가 포함됩니다.`})]}),o?L(`p`,{role:`alert`,class:`mt-3 text-sm font-bold text-red-700`,children:o}):null,n?L(`button`,{class:`mt-5 inline-flex min-h-11 w-full items-center justify-center gap-2 rounded-xl bg-teal-700 px-4 font-extrabold text-white transition hover:bg-teal-800 focus:outline-none focus-visible:ring-4 focus-visible:ring-teal-200`,onClick:()=>{if(n){s(null);try{let e=(n.matrix.length+8)*8,t=document.createElement(`canvas`);t.width=e,t.height=e;let r=t.getContext(`2d`);if(!r)throw Error(`Canvas is not supported`);_r(n.matrix,r,8);let i=document.createElement(`a`);i.href=t.toDataURL(`image/png`),i.download=gr(n.ssid),i.click()}catch{s(`QR 코드 이미지를 저장하지 못했습니다. 다시 시도해 주세요.`)}}},type:`button`,children:[L(it,{class:`size-5`}),`QR 코드 다운로드`]}):null,L(`button`,{class:`mt-2 min-h-11 w-full rounded-xl border border-slate-300 bg-white px-4 font-extrabold text-slate-700 transition hover:bg-slate-50`,onClick:t,type:`button`,children:`닫기`})]})})}function yr({guest:e,busy:t,saving:n,onUpdate:r,onLoadQr:i}){let[a,o]=F(!1),[s,c]=F(!1),[l,u]=F(e.ssid),[d,f]=F(``),[p,m]=F(e.enabled),[h,g]=F(null);if(I(()=>{u(e.ssid),m(e.enabled),f(``),g(null)},[e]),!e.supported)return L(`section`,{class:`mt-5 rounded-2xl border border-slate-200 bg-white p-5 sm:p-6`,children:[L(`h2`,{class:`m-0 text-lg font-extrabold text-slate-900`,children:`게스트 Wi-Fi`}),L(`p`,{class:`mt-2 mb-0 text-sm text-slate-600`,children:`2.4GHz 기본 Wi-Fi 장치를 찾지 못해 게스트 Wi-Fi를 사용할 수 없습니다.`})]});let _=l.trim()!==e.ssid||p!==e.enabled||d.length>0,y=t||_||!e.enabled||!e.passwordConfigured,b=async t=>{t.preventDefault();let n=l.trim(),i=new TextEncoder().encode(n).length;i<1||i>32?g(`Wi-Fi 이름은 UTF-8 기준 1~32바이트로 입력해 주세요.`):d&&!(d.length>=8&&d.length<=63||/^[0-9a-fA-F]{64}$/.test(d))?g(`비밀번호는 8~63자 또는 64자리 16진수여야 합니다.`):p&&!e.passwordConfigured&&!d?g(`게스트 전용 비밀번호를 입력해 주세요.`):window.confirm(`게스트 Wi-Fi 설정을 적용하면 무선 연결이 잠시 끊어질 수 있습니다. 계속하시겠습니까?`)&&(g(null),await r({ssid:n,enabled:p,password:d})&&f(``))};return L(v,{children:[L(`form`,{"aria-label":`게스트 Wi-Fi 설정`,class:`mt-5 rounded-2xl border border-slate-200 bg-white p-4 shadow-sm shadow-slate-900/5 sm:p-6`,onSubmit:e=>void b(e),children:[L(`div`,{class:`flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between`,children:[L(`button`,{"aria-controls":`ssh-guest-settings`,"aria-expanded":a,"aria-label":`게스트 Wi-Fi 설정 ${a?`접기`:`펼치기`}`,class:`flex min-w-0 flex-1 items-start gap-3 rounded-xl text-left focus:outline-none focus-visible:ring-4 focus-visible:ring-teal-100`,onClick:()=>o(e=>!e),type:`button`,children:[L(`span`,{class:`grid size-11 shrink-0 place-items-center rounded-xl bg-teal-50 text-teal-700`,children:L(We,{class:`size-6`})}),L(`span`,{class:`min-w-0 flex-1`,children:[L(`span`,{class:`block text-xs font-extrabold uppercase tracking-[0.16em] text-slate-500`,children:`2.4 GHz · 분리 네트워크`}),L(`span`,{class:`mt-2 block text-xl font-extrabold tracking-tight text-slate-950`,children:`게스트 Wi-Fi`}),L(`span`,{class:`mt-2 block break-all text-sm text-slate-600`,children:e.ssid})]}),L(gt,{class:`mt-3 size-5 shrink-0 text-slate-500 transition-transform ${a?`rotate-180`:``}`})]}),L(`div`,{class:`flex flex-wrap items-center gap-2 sm:justify-end`,children:[_&&L(`span`,{"aria-live":`polite`,class:`inline-flex items-center gap-1 rounded-full border border-amber-200 bg-amber-50 px-2.5 py-1 text-xs font-extrabold text-amber-800`,children:[L(V,{class:`size-3.5`}),`저장되지 않음`]}),L(`button`,{"aria-label":`게스트 Wi-Fi QR 코드 보기`,class:`inline-flex min-h-10 items-center gap-2 rounded-xl border border-slate-300 bg-white px-3.5 py-2 text-sm font-extrabold text-slate-800 transition hover:border-teal-300 hover:text-teal-700 focus:outline-none focus-visible:ring-4 focus-visible:ring-teal-100 disabled:cursor-not-allowed disabled:opacity-50`,disabled:y,onClick:()=>c(!0),title:e.enabled?void 0:`게스트 Wi-Fi를 켜고 저장하면 QR 코드를 사용할 수 있습니다.`,type:`button`,children:[L(Ze,{class:`size-4 shrink-0`}),`QR 코드 보기`]}),L(`span`,{class:`rounded-full px-3 py-1.5 text-xs font-extrabold ${e.enabled?`bg-emerald-50 text-emerald-700`:`bg-slate-100 text-slate-600`}`,children:e.enabled?`사용 중`:`꺼짐`})]})]}),L(`div`,{class:a?``:`hidden`,id:`ssh-guest-settings`,children:[L(`p`,{class:`mt-5 mb-0 text-sm leading-6 text-slate-600`,children:`방문자는 인터넷만 이용하며 내부 네트워크와 공유기 관리 화면에는 접근할 수 없습니다.`}),L(`div`,{class:`mt-5 grid gap-5 lg:grid-cols-2`,children:[L(`label`,{class:`block`,children:[L(`span`,{class:`text-sm font-extrabold text-slate-800`,children:`게스트 Wi-Fi 이름 (SSID)`}),L(`input`,{autocomplete:`off`,class:`mt-2 min-h-11 w-full rounded-xl border-2 border-slate-300 bg-slate-50 px-4 py-2.5 text-sm font-semibold text-slate-950 outline-none focus:border-teal-500 focus:bg-white focus:ring-4 focus:ring-teal-100 disabled:opacity-60`,disabled:t,maxLength:32,onInput:e=>u(e.currentTarget.value),value:l})]}),L(`label`,{class:`block`,children:[L(`span`,{class:`text-sm font-extrabold text-slate-800`,children:`게스트 Wi-Fi 비밀번호`}),L(`input`,{autocomplete:`new-password`,class:`mt-2 min-h-11 w-full rounded-xl border-2 border-slate-300 bg-slate-50 px-4 py-2.5 text-sm font-semibold text-slate-950 outline-none focus:border-teal-500 focus:bg-white focus:ring-4 focus:ring-teal-100 disabled:opacity-60`,disabled:t,onInput:e=>f(e.currentTarget.value),placeholder:e.passwordConfigured?`비워 두면 기존 비밀번호 유지`:`8자 이상의 별도 비밀번호 입력`,type:`password`,value:d}),L(`span`,{class:`mt-2 block text-xs text-slate-500`,children:`WPA2/WPA3 혼합 · 기본 Wi-Fi와 다른 비밀번호를 권장합니다.`})]})]}),L(`div`,{class:`mt-5 rounded-xl bg-slate-50 px-4 py-3 text-sm leading-6 text-slate-700`,children:[`내부망 접근 차단 · 게스트 기기 간 통신 차단 · SafeShield DNS 사용`,e.subnet&&L(`span`,{class:`block text-xs text-slate-500`,children:[`게스트 게이트웨이: `,e.subnet]})]}),L(`div`,{class:`mt-5 flex flex-col gap-4 border-t border-slate-200 pt-5 sm:flex-row sm:items-center sm:justify-between`,children:[L(`label`,{class:`inline-flex min-h-11 items-center gap-3 text-sm font-extrabold text-slate-800`,children:[L(`input`,{checked:p,class:`size-5 accent-teal-700`,disabled:t,onChange:e=>m(e.currentTarget.checked),type:`checkbox`}),`게스트 Wi-Fi 사용`]}),L(`button`,{class:`inline-flex min-h-11 w-full items-center justify-center gap-2 rounded-xl bg-teal-700 px-5 py-2.5 text-sm font-extrabold text-white transition hover:bg-teal-800 disabled:opacity-60 sm:w-auto`,disabled:t||!_,type:`submit`,children:[L(B,{class:`size-5 ${n?`animate-pulse`:``}`}),n?`적용 중`:`설정 저장`]})]}),h&&L(`p`,{class:`mt-4 mb-0 rounded-xl bg-red-50 px-4 py-3 text-sm font-bold text-red-800`,children:h})]})]}),s&&L(vr,{load:i,onClose:()=>c(!1)})]})}var br=[{value:`sae-mixed`,label:`WPA2/WPA3 혼합 (권장)`},{value:`sae`,label:`WPA3-Personal`},{value:`psk2`,label:`WPA2-Personal`},{value:`none`,label:`암호화 없음`}];function xr(e){switch(e.security){case`sae-mixed`:return`WPA2/WPA3 혼합`;case`sae`:return`WPA3-Personal`;case`psk2`:return`WPA2-Personal`;case`none`:return`암호화 없음`;default:return`고급 설정 (${e.securityRaw})`}}function Sr(e){return e.length>=8&&e.length<=63||/^[0-9a-fA-F]{64}$/.test(e)}function Cr({network:e,busy:t,saving:n,onUpdate:r,onLoadQr:i}){let[a,o]=F(e.ssid),[s,c]=F(e.enabled),[l,u]=F(e.security===`custom`?`keep`:e.security),[d,f]=F(``),[p,m]=F(!1),h=Ee(()=>i(e.section),[i,e.section]),[g,_]=F(null);I(()=>{o(e.ssid),c(e.enabled),u(e.security===`custom`?`keep`:e.security),f(``),_(null)},[e]);let v=e.security===`custom`&&l===`keep`,y=l===`psk2`||l===`sae-mixed`||l===`sae`,b=e.security===`custom`?`keep`:e.security,x=a.trim()!==e.ssid||s!==e.enabled||l!==b||d.length>0,S=t||x||e.security===`custom`||!e.enabled||e.security!==`none`&&!e.passwordConfigured,C=[...e.security===`custom`?[{value:`keep`,label:`현재 고급 설정 유지 (${e.securityRaw})`}]:[],...br],w=async t=>{t.preventDefault();let n=a.trim(),i=new TextEncoder().encode(n).length;i<1||i>32?_(`SSID는 UTF-8 기준 1~32바이트로 입력해 주세요.`):d&&!Sr(d)?_(`비밀번호는 8~63자 또는 64자리 16진수여야 합니다.`):y&&!e.passwordConfigured&&d.length===0?_(`보안 Wi-Fi를 사용하려면 비밀번호를 입력해 주세요.`):window.confirm(`Wi-Fi 설정을 적용하면 현재 무선 연결이 잠시 끊어질 수 있습니다. 계속하시겠습니까?`)&&(_(null),await r({section:e.section,ssid:n,security:l,password:d,enabled:s})&&f(``))};return L(`form`,{class:`rounded-2xl border border-slate-200 bg-white p-4 shadow-sm shadow-slate-900/5 sm:p-6`,onSubmit:e=>void w(e),children:[L(`div`,{class:`flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between`,children:[L(`div`,{class:`flex min-w-0 items-start gap-3`,children:[L(`span`,{class:`grid size-11 shrink-0 place-items-center rounded-xl bg-teal-50 text-teal-700`,children:L(We,{class:`size-6`})}),L(`div`,{class:`min-w-0`,children:[L(`p`,{class:`m-0 text-xs font-extrabold uppercase tracking-[0.16em] text-slate-500`,children:e.bandLabel}),L(`h2`,{class:`mt-2 mb-0 break-words text-xl font-extrabold tracking-tight text-slate-950`,children:e.ssid||`이름 없는 Wi-Fi`}),L(`p`,{class:`mt-2 mb-0 text-sm text-slate-600`,children:[`채널 `,e.channel??`자동`,` · 연결 기기 `,e.clientCount,`대`]})]})]}),L(`div`,{class:`flex flex-wrap items-center gap-2 sm:justify-end`,children:[x?L(`span`,{"aria-live":`polite`,class:`inline-flex shrink-0 items-center gap-1.5 rounded-full border border-amber-200 bg-amber-50 px-2.5 py-1 text-xs font-extrabold text-amber-800`,role:`status`,children:[L(V,{"aria-hidden":`true`,class:`size-3.5 shrink-0`}),`저장되지 않음`]}):null,L(`button`,{"aria-label":`${e.bandLabel} Wi-Fi QR 코드 보기`,class:`inline-flex min-h-10 items-center gap-2 rounded-xl border border-slate-300 bg-white px-3.5 py-2 text-sm font-extrabold text-slate-800 shadow-sm transition hover:border-teal-300 hover:text-teal-700 focus:outline-none focus-visible:ring-4 focus-visible:ring-teal-100 disabled:cursor-not-allowed disabled:opacity-50`,disabled:S,onClick:()=>m(!0),type:`button`,children:[L(Ze,{class:`size-4 shrink-0`}),`QR 코드 보기`]}),L(`span`,{class:`inline-flex w-fit items-center gap-2 rounded-full px-3 py-1.5 text-xs font-extrabold ring-1 ring-inset ${e.runtimeUp?`bg-emerald-50 text-emerald-700 ring-emerald-200`:`bg-slate-100 text-slate-600 ring-slate-200`}`,children:[L(`span`,{class:`size-2 rounded-full ${e.runtimeUp?`bg-emerald-500`:`bg-slate-400`}`}),e.runtimeUp?`동작 중`:e.enabled?`시작 대기`:`꺼짐`]})]})]}),L(`div`,{class:`mt-6 grid gap-5 lg:grid-cols-2`,children:[L(`label`,{class:`block`,children:[L(`span`,{class:`text-sm font-extrabold text-slate-800`,children:`Wi-Fi 이름 (SSID)`}),L(`input`,{autocomplete:`off`,class:`mt-2 min-h-11 w-full rounded-xl border-2 border-slate-300 bg-slate-50 px-4 py-2.5 text-sm font-semibold text-slate-950 shadow-inner outline-none transition placeholder:text-slate-400 focus:border-teal-500 focus:bg-white focus:ring-4 focus:ring-teal-100 disabled:cursor-not-allowed disabled:bg-slate-100`,disabled:t,maxLength:32,onInput:e=>o(e.currentTarget.value),value:a})]}),L(`div`,{class:`block`,children:[L(`span`,{class:`text-sm font-extrabold text-slate-800`,children:`보안 방식`}),L(Nn,{ariaLabel:`${e.bandLabel} Wi-Fi 보안 방식`,className:`mt-2`,disabled:t,id:`wifi-security-${e.section}`,onChange:e=>{let t=e;u(t),(t===`none`||t===`keep`)&&f(``)},options:C,value:l,variant:`emphasized`}),L(`span`,{class:`mt-2 block text-xs text-slate-500`,children:[`현재: `,xr(e)]})]}),L(`label`,{class:`block lg:col-span-2`,children:[L(`span`,{class:`text-sm font-extrabold text-slate-800`,children:`새 비밀번호`}),L(`input`,{autocomplete:`new-password`,class:`mt-2 min-h-11 w-full rounded-xl border-2 border-slate-300 bg-slate-50 px-4 py-2.5 text-sm font-semibold text-slate-950 shadow-inner outline-none transition placeholder:text-slate-400 focus:border-teal-500 focus:bg-white focus:ring-4 focus:ring-teal-100 disabled:cursor-not-allowed disabled:bg-slate-100`,disabled:t||l===`none`||v,onInput:e=>f(e.currentTarget.value),placeholder:v?`고급 보안 설정은 기존 LuCI에서 변경합니다`:e.passwordConfigured?`비워 두면 기존 비밀번호를 유지합니다`:`8자 이상의 비밀번호를 입력하세요`,type:`password`,value:d}),L(`span`,{class:`mt-2 block text-xs text-slate-500`,children:`저장된 비밀번호는 일반 설정 조회에 노출되지 않습니다. QR 코드에는 비밀번호가 포함됩니다.`})]})]}),L(`div`,{class:`mt-6 flex flex-col gap-4 border-t border-slate-200 pt-5 sm:flex-row sm:items-center sm:justify-between`,children:[L(`label`,{class:`inline-flex min-h-11 items-center gap-3 text-sm font-extrabold text-slate-800`,children:[L(`input`,{checked:s,class:`size-5 accent-teal-700`,disabled:t,onChange:e=>c(e.currentTarget.checked),type:`checkbox`}),`이 Wi-Fi 사용`]}),L(`button`,{class:`inline-flex min-h-11 w-full items-center justify-center gap-2 rounded-xl border-0 bg-teal-700 px-5 py-2.5 text-sm font-extrabold text-white shadow-sm transition hover:bg-teal-800 focus:outline-none focus-visible:ring-4 focus-visible:ring-teal-200 disabled:cursor-wait disabled:opacity-60 sm:w-auto`,disabled:t||!x,type:`submit`,children:[L(B,{class:`size-5 ${n?`animate-pulse`:``}`}),n?`적용 중`:`설정 저장`]})]}),p?L(vr,{load:h,onClose:()=>m(!1)}):null,g?L(`p`,{class:`mt-4 mb-0 rounded-xl bg-red-50 px-4 py-3 text-sm font-bold text-red-800`,children:g}):null]})}function wr({feedback:e,onDismiss:t}){return L(`div`,{"aria-live":`polite`,class:`mb-5 flex flex-col gap-3 rounded-2xl border px-4 py-3 text-sm sm:flex-row sm:items-center sm:justify-between ${e.kind===`success`?`border-emerald-200 bg-emerald-50 text-emerald-900`:e.kind===`warning`?`border-amber-200 bg-amber-50 text-amber-900`:`border-red-200 bg-red-50 text-red-900`}`,children:[L(`p`,{class:`m-0 font-bold leading-6`,children:e.message}),L(`button`,{class:`min-h-11 w-full shrink-0 rounded-lg border border-current bg-transparent sm:min-h-9 sm:w-auto px-3 py-1.5 text-xs font-extrabold`,onClick:t,type:`button`,children:`닫기`})]})}function Tr({data:e,error:t,feedback:n,loading:r,updatingSection:i,onDismissFeedback:a,onRetry:o,onUpdate:s,onUpdateGuest:c,onLoadQr:l,onLoadGuestQr:u}){return r?L(Rn,{}):t?L(zn,{message:t,onRetry:o}):e?L(v,{children:[n?L(wr,{feedback:n,onDismiss:a}):null,L(`section`,{class:`mb-5 rounded-2xl border border-amber-200 bg-amber-50 p-5 text-amber-950 sm:p-6`,children:[L(`p`,{class:`m-0 text-xs font-extrabold uppercase tracking-[0.16em] text-amber-700`,children:`연결 주의`}),L(`h2`,{class:`mt-2 mb-0 text-lg font-extrabold`,children:`Wi-Fi 이름이나 비밀번호를 바꾸면 현재 연결이 끊어집니다`}),L(`p`,{class:`mt-2 mb-0 text-sm leading-6 text-amber-900/80`,children:`변경한 Wi-Fi 이름과 비밀번호로 다시 연결하면 SmartSafeHub를 계속 사용할 수 있습니다. VLAN 등 고급 무선 옵션은 기존 LuCI에서 관리합니다.`})]}),e.networks.length?L(`section`,{"aria-label":`기본 Wi-Fi 설정`,class:`grid gap-5 xl:grid-cols-2`,children:e.networks.map(e=>L(Cr,{busy:i!==null,network:e,onUpdate:s,onLoadQr:l,saving:i===e.section},e.section))}):L(`section`,{class:`rounded-2xl border border-slate-200 bg-white p-5 text-center shadow-sm shadow-slate-900/5 sm:p-8`,children:[L(We,{class:`mx-auto size-10 text-slate-400`}),L(`h2`,{class:`mt-4 mb-0 text-lg font-extrabold text-slate-950`,children:`관리할 기본 Wi-Fi를 찾지 못했습니다`}),L(`p`,{class:`mt-2 mb-0 text-sm leading-6 text-slate-600`,children:`기존 LuCI에서 AP 모드의 무선 네트워크를 먼저 구성해 주세요.`})]}),e.guest&&L(v,{children:[L(`div`,{role:`separator`,"aria-label":`기본 Wi-Fi와 게스트 Wi-Fi 구분`,class:`mt-5 w-full border-t border-slate-200`}),L(yr,{guest:e.guest,busy:i!==null,saving:i===`ssh_guest`,onUpdate:c,onLoadQr:u})]}),e.guestError&&L(`section`,{class:`mt-5 rounded-2xl border border-amber-200 bg-amber-50 p-5 text-sm text-amber-900`,role:`status`,children:[`게스트 Wi-Fi 정보를 불러오지 못했습니다. 패키지를 설치한 직후라면 로그아웃 후 다시 로그인해 주세요.`,L(`button`,{class:`ml-3 underline font-bold`,onClick:o,type:`button`,children:`다시 시도`})]})]}):null}function Er(e){return e.trim().toLowerCase().replace(/\.+$/,``)}function Dr(e){return e.length===0||e.length>253||!e.includes(`.`)||!/^[a-z0-9.-]+$/.test(e)?!1:e.split(`.`).every(e=>e.length>0&&e.length<=63&&/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(e))}function Or({action:e,kind:t,onAddRule:n,onDeleteRule:r,rules:i,recommendedDomains:a,recommendedKnown:o}){let[s,c]=F(``),[l,u]=F(``),[d,f]=F(null),p=t===`allow`,m=e!==null,h=Te(()=>{let e=l.trim().toLowerCase();return e.length>0?i.filter(t=>t.includes(e)):i},[i,l]);async function g(e){e.preventDefault();let r=Er(s);Dr(r)?(f(null),await n(t,r,()=>{c(e=>Er(e)===r?``:e)})):f(`example.com 형식의 도메인을 입력해 주세요. URL, IP 주소, 와일드카드는 사용할 수 없습니다.`)}let _=p?{badge:`bg-emerald-100 text-emerald-800`,button:`border-emerald-700 bg-emerald-700 text-white hover:border-emerald-800 hover:bg-emerald-800 focus-visible:ring-emerald-100`,icon:`bg-emerald-50 text-emerald-700`,label:`허용 목록`,description:`차단 목록에 포함되어도 허용할 도메인과 하위 도메인을 등록합니다.`,empty:`등록된 허용 도메인이 없습니다.`}:{badge:`bg-red-100 text-red-800`,button:`border-red-700 bg-red-700 text-white hover:border-red-800 hover:bg-red-800 focus-visible:ring-red-100`,icon:`bg-red-50 text-red-700`,label:`차단 목록`,description:`SafeShield 기본 목록에 없더라도 직접 차단할 도메인을 등록합니다.`,empty:`등록된 차단 도메인이 없습니다.`};return L(`article`,{class:`min-w-0 rounded-2xl border border-slate-200 bg-white shadow-sm shadow-slate-900/5`,children:[L(`div`,{class:`border-b border-slate-200 p-4 sm:p-6`,children:[L(`div`,{class:`flex items-start justify-between gap-4`,children:L(`div`,{class:`flex min-w-0 items-start gap-3`,children:[L(`span`,{class:`grid size-11 shrink-0 place-items-center rounded-xl ${_.icon}`,children:L(p?B:Ue,{class:`size-6`})}),L(`div`,{class:`min-w-0`,children:[L(`div`,{class:`flex flex-wrap items-center gap-2`,children:[L(`h2`,{class:`m-0 text-xl font-extrabold tracking-tight text-slate-950`,children:_.label}),L(`span`,{class:`rounded-full px-2.5 py-1 text-xs font-extrabold ${_.badge}`,children:i.length})]}),L(`p`,{class:`mt-2 mb-0 text-sm leading-6 text-slate-600`,children:_.description})]})]})}),L(`form`,{class:`mt-5`,onSubmit:g,children:[L(`label`,{class:`block text-xs font-extrabold text-slate-600`,for:`${t}-domain`,children:`새 도메인`}),L(`div`,{class:`mt-2 flex flex-col gap-2 sm:flex-row`,children:[L(`input`,{"aria-describedby":d?`${t}-domain-error`:void 0,"aria-invalid":d?!0:void 0,autocomplete:`off`,class:`min-h-11 min-w-0 flex-1 rounded-xl border-2 border-slate-300 bg-slate-50 px-4 py-2.5 text-sm font-semibold text-slate-950 shadow-inner outline-none transition placeholder:text-slate-400 focus:border-teal-500 focus:bg-white focus:ring-4 focus:ring-teal-100 disabled:cursor-not-allowed disabled:bg-slate-100`,disabled:m,id:`${t}-domain`,onInput:e=>c(e.currentTarget.value),placeholder:`example.com`,spellcheck:!1,type:`text`,value:s}),L(`button`,{class:`inline-flex min-h-11 w-full shrink-0 items-center justify-center gap-2 sm:w-auto rounded-xl border px-4 py-2.5 text-sm font-extrabold transition focus:outline-none focus-visible:ring-4 disabled:cursor-not-allowed disabled:opacity-50 ${_.button}`,disabled:m||s.trim().length===0,type:`submit`,children:[L(ut,{class:`size-4`}),e?.operation===`add`&&e.action===t?`추가 중…`:`추가`]})]}),d?L(`p`,{class:`mt-2 mb-0 text-xs font-semibold leading-5 text-red-700`,id:`${t}-domain-error`,children:d}):null]})]}),L(`div`,{class:`p-4 sm:p-6`,children:[L(`label`,{class:`relative block`,for:`${t}-search`,children:[L(`span`,{class:`sr-only`,children:[_.label,` 검색`]}),L(`span`,{class:`pointer-events-none absolute inset-y-0 left-0 flex w-11 items-center justify-center text-slate-400`,children:L(ft,{class:`size-4`})}),L(`input`,{class:`min-h-11 w-full rounded-xl border-2 border-slate-300 bg-slate-50 py-2.5 pr-4 pl-11 text-sm font-semibold text-slate-950 shadow-inner outline-none transition placeholder:text-slate-400 focus:border-teal-500 focus:bg-white focus:ring-4 focus:ring-teal-100`,id:`${t}-search`,onInput:e=>u(e.currentTarget.value),placeholder:`${_.label} 검색`,type:`search`,value:l})]}),L(`div`,{class:`mt-4 max-h-[28rem] overflow-y-auto rounded-xl border border-slate-200`,children:h.length>0?L(`ul`,{class:`m-0 list-none divide-y divide-slate-200 p-0`,children:h.map(n=>{let i=p&&a.includes(n),s=!p||o?i?`추천 규칙`:`직접 추가`:null,c=e?.operation===`delete`&&e.action===t&&e.domain===n;return L(`li`,{class:`flex min-w-0 items-center justify-between gap-3 px-3.5 py-3`,children:[L(`div`,{class:`flex min-w-0 flex-1 flex-wrap items-center gap-x-3 gap-y-1.5`,children:[L(`code`,{class:`min-w-0 break-all font-mono text-sm font-semibold text-slate-800`,children:n}),s?L(`span`,{class:`shrink-0 rounded-full px-2.5 py-1 text-[11px] font-bold ${i?`bg-teal-50 text-teal-800`:`bg-slate-100 text-slate-600`}`,title:i?`추천 규칙 동기화로 추가된 도메인입니다. 웹사이트에서 적용을 해제해 주세요.`:`공유기에 직접 등록되어 유지되는 규칙입니다.`,children:s}):null]}),L(`button`,{"aria-label":`${n} ${_.label}에서 삭제`,class:`inline-flex size-9 shrink-0 items-center justify-center rounded-lg border border-slate-200 bg-white text-slate-500 transition hover:border-red-200 hover:bg-red-50 hover:text-red-700 focus:outline-none focus-visible:ring-4 focus-visible:ring-red-100 disabled:cursor-not-allowed disabled:opacity-50`,disabled:m||i,onClick:()=>void r(t,n),title:i?`웹사이트에서 적용 해제할 수 있습니다.`:`삭제`,type:`button`,children:L(dt,{class:`size-4 ${c?`animate-pulse`:``}`})})]},n)})}):L(`div`,{class:`px-5 py-10 text-center`,children:[L(pt,{class:`mx-auto size-8 text-slate-300`}),L(`p`,{class:`mt-3 mb-0 text-sm font-bold text-slate-600`,children:l.trim().length>0?`검색 결과가 없습니다.`:_.empty})]})})]})]})}function kr({action:e,data:t,error:n,feedback:r,loading:i,onAddRule:a,onDeleteRule:o,onDismissFeedback:s,onRetry:c,onLoadRecommendedRulesStatus:l}){let[u,d]=F([]),[f,p]=F(!1);return I(()=>{if(!l)return;let e=!0;return l().then(t=>{e&&(d(t.domains),p(!0))}).catch(()=>{e&&p(!1)}),()=>{e=!1}},[t?.allow.join(` -`),l]),i&&!t?L(Rn,{}):n&&!t?L(zn,{message:n,onRetry:c}):t?L(v,{children:[L(`section`,{class:`rounded-2xl border border-slate-200 bg-white p-4 shadow-sm shadow-slate-900/5 sm:p-6`,children:L(`div`,{class:`flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between`,children:[L(`div`,{class:`flex min-w-0 items-start gap-3 sm:gap-4`,children:[L(`span`,{class:`grid size-12 shrink-0 place-items-center rounded-2xl bg-teal-50 text-teal-700`,children:L(pt,{class:`size-7`})}),L(`div`,{class:`min-w-0`,children:[L(`p`,{class:`m-0 text-xs font-extrabold uppercase tracking-[0.16em] text-teal-700`,children:`Local overrides`}),L(`h2`,{class:`mt-2 mb-0 break-words text-xl font-black tracking-tight text-slate-950 sm:text-2xl`,children:`사용자 도메인 규칙`}),L(`p`,{class:`mt-2 mb-0 max-w-3xl text-sm leading-6 text-slate-600`,children:`허용 목록은 오탐을 해제하고, 차단 목록은 원하는 도메인을 SafeShield 보호 목록에 추가합니다.`})]})]}),L(`dl`,{class:`m-0 grid grid-cols-3 gap-2 sm:min-w-80`,children:[L(`div`,{class:`rounded-xl bg-emerald-50 px-3 py-3 text-center`,children:[L(`dt`,{class:`text-[0.68rem] font-extrabold uppercase tracking-wide text-emerald-700`,children:`허용`}),L(`dd`,{class:`mt-1 mb-0 ml-0 text-lg font-black text-emerald-950`,children:t.counts.allow})]}),L(`div`,{class:`rounded-xl bg-red-50 px-3 py-3 text-center`,children:[L(`dt`,{class:`text-[0.68rem] font-extrabold uppercase tracking-wide text-red-700`,children:`차단`}),L(`dd`,{class:`mt-1 mb-0 ml-0 text-lg font-black text-red-950`,children:t.counts.block})]}),L(`div`,{class:`rounded-xl bg-slate-100 px-3 py-3 text-center`,children:[L(`dt`,{class:`text-[0.68rem] font-extrabold uppercase tracking-wide text-slate-600`,children:`전체`}),L(`dd`,{class:`mt-1 mb-0 ml-0 text-lg font-black text-slate-950`,children:t.counts.total})]})]})]})}),L(`section`,{class:`mt-4 rounded-2xl border border-slate-200 bg-white p-4 shadow-sm shadow-slate-900/5 sm:p-5`,"aria-label":`추천 사용자 규칙`,children:L(`div`,{class:`flex flex-col items-start justify-between gap-3 sm:flex-row sm:items-center`,children:[L(`div`,{class:`min-w-0`,children:[L(`div`,{class:`flex flex-wrap items-center gap-2`,children:[L(`span`,{class:`grid size-8 place-items-center rounded-lg bg-teal-50 text-teal-700`,children:L(B,{class:`size-4`})}),L(`h2`,{class:`m-0 text-base font-extrabold text-slate-950`,children:`추천 사용자 규칙`}),f&&u.length>0?L(`span`,{class:`rounded-full bg-teal-50 px-2.5 py-1 text-xs font-bold text-teal-800`,children:[`추천으로 추가 `,u.length,`개`]}):null]}),L(`div`,{class:`mt-2 flex flex-wrap items-center gap-2`,"aria-label":`추천 규칙 분류`,children:[L(`span`,{class:`inline-flex items-center rounded-full bg-teal-50 px-2.5 py-1 text-xs font-bold text-teal-800`,children:`공식 추천`}),L(`span`,{class:`inline-flex items-center rounded-full bg-slate-100 px-2.5 py-1 text-xs font-bold text-slate-700`,children:`커뮤니티`})]}),L(`p`,{class:`mt-2 mb-0 text-sm leading-6 text-slate-600`,children:`SmartSafeHub의 공식 추천과 관리자가 검토한 커뮤니티 허용 규칙을 확인하고, 필요한 규칙을 적용해 보세요.`}),L(`p`,{class:`mt-1 mb-0 text-xs leading-5 text-slate-500`,children:`공유기 선택과 적용 관리는 SmartSafeHub 웹사이트에서 진행합니다. 적용 결과는 다음 기기 동기화 후 반영되며, 연결된 모든 기기에 영향을 줍니다.`})]}),L(`a`,{class:`inline-flex min-h-10 w-full shrink-0 items-center justify-center rounded-xl border border-teal-700 bg-teal-700 px-4 py-2.5 text-sm font-extrabold text-white transition hover:bg-teal-800 sm:w-auto`,href:`https://www.smartsafehub.com/dashboard/recommended-rules/`,target:`_blank`,rel:`noopener noreferrer`,children:`추천 규칙 보기 ↗`})]})}),t.applyLocalOverrides?null:L(`section`,{class:`mt-4 flex items-start gap-3 rounded-2xl border border-amber-200 bg-amber-50 p-4`,role:`alert`,children:[L(V,{class:`mt-0.5 size-5 shrink-0 text-amber-700`}),L(`div`,{children:[L(`p`,{class:`m-0 text-sm font-extrabold text-amber-950`,children:`로컬 규칙 적용이 꺼져 있습니다`}),L(`p`,{class:`mt-1 mb-0 text-sm leading-6 text-amber-800`,children:`규칙은 저장되지만 SafeShield의 apply_local_overrides 설정을 켜기 전까지 DNS에 반영되지 않습니다.`})]})]}),t.safeshieldEnabled?null:L(`section`,{class:`mt-4 flex items-start gap-3 rounded-2xl border border-slate-200 bg-slate-100 p-4`,role:`status`,children:[L(Ue,{class:`mt-0.5 size-5 shrink-0 text-slate-600`}),L(`div`,{children:[L(`p`,{class:`m-0 text-sm font-extrabold text-slate-950`,children:`SafeShield가 비활성화되어 있습니다`}),L(`p`,{class:`mt-1 mb-0 text-sm leading-6 text-slate-600`,children:`규칙을 미리 저장할 수 있으며, SafeShield를 켜고 차단 목록을 갱신하면 적용됩니다.`})]})]}),r?L(`section`,{"aria-live":`polite`,class:`mt-4 flex flex-col items-start gap-3 rounded-2xl border border-teal-200 bg-teal-50 sm:flex-row sm:justify-between p-4`,children:[L(`div`,{class:`flex min-w-0 items-start gap-3`,children:[L(B,{class:`mt-0.5 size-5 shrink-0 text-teal-700`}),L(`p`,{class:`m-0 text-sm font-semibold leading-6 text-teal-950`,children:r})]}),L(`button`,{class:`shrink-0 rounded-lg border-0 bg-transparent px-2 py-1 text-xs font-extrabold text-teal-800 hover:bg-teal-100`,onClick:s,type:`button`,children:`닫기`})]}):null,n&&t?L(`section`,{"aria-live":`assertive`,class:`mt-4 flex items-start gap-3 rounded-2xl border border-red-200 bg-red-50 p-4`,children:[L(V,{class:`mt-0.5 size-5 shrink-0 text-red-700`}),L(`p`,{class:`m-0 text-sm font-semibold leading-6 text-red-900`,children:n})]}):null,L(`section`,{class:`mt-5 grid gap-5 xl:grid-cols-2`,children:[L(Or,{action:e,kind:`allow`,onAddRule:a,onDeleteRule:o,rules:t.allow,recommendedDomains:u,recommendedKnown:f}),L(Or,{action:e,kind:`block`,onAddRule:a,onDeleteRule:o,rules:t.block,recommendedDomains:u,recommendedKnown:f})]}),L(`p`,{class:`mt-5 mb-0 text-xs leading-5 text-slate-500`,children:`규칙 저장과 유효성 검사는 SafeShield API가 관리합니다. 도메인은 소문자로 정규화되고 중복 항목은 하나로 유지됩니다.`})]}):L(zn,{message:`사용자 규칙 응답이 비어 있습니다.`,onRetry:c})}function Ar({timestamp:e,fallback:t=`기록 없음`}){let[n,r]=F(()=>Date.now()/1e3),i=typeof e==`number`&&Number.isFinite(e)&&e>0&&e<=864e10;if(I(()=>{if(!i)return;r(Date.now()/1e3);let e=window.setInterval(()=>r(Date.now()/1e3),6e4);return()=>window.clearInterval(e)},[e,i]),!i||e==null)return L(`span`,{children:t});let a=Ut(e);return L(`span`,{class:`inline-flex max-w-full flex-wrap items-center gap-1`,children:[L(`time`,{class:`min-w-0`,dateTime:new Date(e*1e3).toISOString(),title:a,children:Gt(e,n)}),L(`details`,{class:`group inline-flex max-w-full flex-wrap items-center gap-1 align-middle`,children:[L(`summary`,{"aria-label":`정확한 날짜 및 시간 확인`,class:`inline-flex size-6 cursor-pointer list-none items-center justify-center rounded-full text-slate-400 transition hover:text-teal-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-teal-500 [&::-webkit-details-marker]:hidden`,title:`정확한 날짜 및 시간 확인`,children:L(`svg`,{"aria-hidden":`true`,class:`size-3.5`,fill:`none`,stroke:`currentColor`,strokeWidth:`1.8`,viewBox:`0 0 24 24`,children:[L(`circle`,{cx:`12`,cy:`12`,r:`9`}),L(`path`,{d:`M12 11v5m0-8h.01`,strokeLinecap:`round`})]})}),L(`span`,{class:`hidden max-w-full break-words rounded-md bg-slate-100 px-2 py-1 text-xs font-semibold text-slate-700 group-open:inline-block`,children:a})]})]})}function jr(e,t){if(!e||!t)return`unknown`;if(e===t)return`same`;let n=/^(\d+)\.(\d+)\.(\d+)(?:-r(\d+))?$/,r=n.exec(e),i=n.exec(t);if(!r||!i)return`different`;for(let e=1;e<=4;e+=1){let t=BigInt(r[e]||`0`),n=BigInt(i[e]||`0`);if(t>n)return`newer`;if(t=1048576?`${(e/1048576).toFixed(1)} MB`:`${(e/1024).toFixed(1)} KB`}function Nr(e){switch(e.phase){case`checking`:return`확인 중`;case`downloading`:return`다운로드 중`;case`verifying`:return`검증 중`;case`ready`:return`설치 준비 완료`;case`flashing`:return`설치 중`;case`error`:return`확인 필요`;default:return e.updateAvailable?`업데이트 가능`:e.lastCheckAt?`최신 상태`:`확인 전`}}function Pr(e){return e.phase===`error`?`bg-rose-50 text-rose-700 ring-rose-200`:e.phase===`checking`||e.phase===`downloading`||e.phase===`verifying`||e.phase===`flashing`?`bg-sky-50 text-sky-700 ring-sky-200`:e.phase===`ready`||e.updateAvailable?`bg-amber-50 text-amber-800 ring-amber-200`:e.lastCheckAt?`bg-emerald-50 text-emerald-700 ring-emerald-200`:`bg-slate-100 text-slate-600 ring-slate-200`}function Fr(e){return e.phase===`checking`||e.phase===`downloading`||e.phase===`verifying`||e.phase===`flashing`?L(z,{class:`size-3.5 animate-spin`}):e.phase===`error`?L(V,{class:`size-3.5`}):e.phase===`ready`||e.updateAvailable?L(it,{class:`size-3.5`}):e.lastCheckAt?L(B,{class:`size-3.5`}):L(tt,{class:`size-3.5`})}function Ir({checked:e,disabled:t,onChange:n}){return L(`button`,{"aria-checked":e,"aria-label":`현재 설정 유지`,class:`ssh-switch-control relative inline-flex shrink-0 rounded-full border transition focus:outline-none focus-visible:ring-4 focus-visible:ring-teal-100 disabled:cursor-not-allowed disabled:opacity-50 ${e?`border-teal-600 bg-teal-600`:`border-slate-300 bg-slate-200`}`,disabled:t,onClick:()=>n(!e),role:`switch`,type:`button`,children:L(`span`,{"aria-hidden":`true`,class:`ssh-switch-thumb absolute top-1 shadow-sm transition-[left] ${e?`left-6`:`left-1`}`})})}function Lr({action:e,actionError:t,data:n,software:r,error:i,loading:a,message:o,reconnecting:s,uploadProgress:c,onCheck:l,onDiscard:u,onDismissFeedback:d,onInstall:f,onPrepare:p,onRetry:m,onUpload:h}){let g=we(null),[_,y]=F(null),[b,x]=F(!0),[S,C]=F(!1);I(()=>{n?.prepared&&x(n.prepared.allowBackup)},[n?.prepared]);let w=e!==null||n?.phase===`checking`||n?.phase===`downloading`||n?.phase===`verifying`||n?.phase===`flashing`,T=n?.phase===`ready`?n.prepared:null,E=e===`check`||n?.phase===`checking`,D=e===`prepare`||n?.phase===`downloading`||n?.phase===`verifying`,O=e===`upload`||e===`validate-upload`,k=n?.release?.bundledSoftware?.smartsafehub??null,A=r?.packages.find(e=>e.name===`luci-app-smartsafehub`)?.installedVersion??null,j=jr(k,A),M=async()=>{_&&await h(_)&&(y(null),g.current&&(g.current.value=``))};return L(`article`,{class:`min-w-0 overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm shadow-slate-900/5`,"data-component":`firmware-update-card`,children:[L(`div`,{class:`p-5 sm:p-6`,children:[L(`div`,{class:`flex flex-col gap-5 lg:flex-row lg:items-start lg:justify-between`,children:[L(`div`,{class:`flex min-w-0 gap-4`,children:[L(`span`,{class:`grid size-12 shrink-0 place-items-center rounded-2xl bg-sky-50 text-sky-700`,children:L(We,{class:`size-6`})}),L(`div`,{class:`min-w-0`,children:[L(`p`,{class:`m-0 text-xs font-extrabold uppercase tracking-[0.16em] text-sky-700`,children:`Firmware update`}),L(`h2`,{class:`mt-2 mb-0 text-xl font-black text-slate-950 sm:text-2xl`,children:`펌웨어 업데이트`}),L(`p`,{class:`mt-2 mb-0 text-sm leading-6 text-slate-500 lg:whitespace-nowrap`,children:`시스템 펌웨어를 확인하고 설치합니다. 온라인·수동 설치는 안전성 검증 후 진행됩니다.`})]})]}),n?L(`div`,{class:`flex shrink-0 flex-col gap-2 sm:flex-row lg:justify-end`,children:[L(`span`,{class:`inline-flex min-h-11 items-center justify-center gap-1.5 rounded-xl px-3 py-2 text-xs font-extrabold ring-1 ring-inset ${Pr(n)}`,children:[Fr(n),Nr(n)]}),L(`button`,{class:`inline-flex min-h-11 items-center justify-center gap-2 rounded-xl border border-slate-300 bg-white px-4 py-2.5 text-sm font-extrabold text-slate-800 transition hover:bg-slate-50 disabled:cursor-wait disabled:opacity-60`,disabled:w||T!==null,onClick:l,type:`button`,children:[L(z,{class:`size-4 ${E?`animate-spin`:``}`}),E?`확인 중...`:`펌웨어 확인`]}),n.updateAvailable&&!T?L(`button`,{class:`inline-flex min-h-11 items-center justify-center gap-2 rounded-xl bg-sky-700 px-4 py-2.5 text-sm font-extrabold text-white transition hover:bg-sky-800 disabled:cursor-wait disabled:opacity-60`,disabled:w,onClick:p,type:`button`,children:[D?L(z,{class:`size-4 animate-spin`}):L(it,{class:`size-4`}),D?`준비 중...`:`다운로드 및 검증`]}):null]}):null]}),t||o?L(`div`,{class:`mt-5 flex flex-col gap-3 rounded-xl border px-4 py-3 text-sm font-bold sm:flex-row sm:items-center sm:justify-between ${t?`border-rose-200 bg-rose-50 text-rose-800`:`border-emerald-200 bg-emerald-50 text-emerald-800`}`,children:[L(`span`,{children:t||o}),L(`button`,{class:`shrink-0 self-start rounded-lg px-3 py-1.5 text-xs font-extrabold hover:bg-black/5 sm:self-auto`,onClick:d,type:`button`,children:`닫기`})]}):null,s||n?.phase===`flashing`?L(`div`,{class:`mt-5 rounded-xl border border-amber-200 bg-amber-50 p-4 sm:p-5`,role:`status`,children:L(`div`,{class:`flex gap-3`,children:[L(`span`,{class:`grid size-10 shrink-0 place-items-center rounded-xl bg-white text-amber-700 ring-1 ring-inset ring-amber-200`,children:L(z,{class:`size-5 animate-spin`})}),L(`div`,{children:[L(`strong`,{class:`block text-sm font-black text-amber-950`,children:`펌웨어를 설치하고 재부팅하고 있습니다.`}),L(`p`,{class:`mt-1 mb-0 text-sm leading-6 text-amber-800`,children:`전원을 끄지 마세요. 현재 주소에서 공유기가 다시 응답하면 SmartSafeHub가 자동으로 새로고침됩니다.`})]})]})}):null,i&&!n?L(`div`,{class:`mt-5 rounded-xl border border-rose-200 bg-rose-50 p-4 text-sm text-rose-800`,children:[L(`p`,{class:`m-0 font-bold`,children:i}),L(`button`,{class:`mt-3 rounded-lg border border-rose-300 bg-white px-3 py-2 text-xs font-extrabold text-rose-700`,onClick:m,type:`button`,children:`다시 시도`})]}):a&&!n?L(`div`,{class:`mt-5 flex items-center gap-3 rounded-xl bg-slate-50 p-4 text-sm font-bold text-slate-500`,children:[L(z,{class:`size-4 animate-spin`}),`펌웨어 상태를 확인하고 있습니다.`]}):n?L(v,{children:[n.lastError?L(`div`,{class:`mt-5 rounded-xl border border-rose-200 bg-rose-50 p-4 sm:p-5`,children:L(`div`,{class:`flex gap-3`,children:[L(`span`,{class:`grid size-10 shrink-0 place-items-center rounded-xl bg-white text-rose-700 ring-1 ring-inset ring-rose-200`,children:L(V,{class:`size-5`})}),L(`div`,{class:`min-w-0 flex-1`,children:[L(`strong`,{class:`block text-sm font-black text-rose-950`,children:`펌웨어 작업을 완료하지 못했습니다.`}),L(`p`,{class:`mt-1 mb-0 text-sm leading-6 text-rose-800`,children:n.lastError.message}),L(`details`,{class:`mt-3 rounded-lg border border-rose-200 bg-white p-3`,children:[L(`summary`,{class:`cursor-pointer text-xs font-extrabold text-rose-800`,children:`상세 정보 보기`}),L(`p`,{class:`mt-2 mb-0 break-all font-mono text-[11px] text-slate-600`,children:n.lastError.code})]})]})]})}):null,n.current.metadataAvailable?null:L(`div`,{class:`mt-5 rounded-xl border border-amber-200 bg-amber-50 px-4 py-3 text-xs leading-5 text-amber-900`,children:[L(`strong`,{class:`block font-extrabold`,children:`현재 펌웨어의 버전 정보를 완전히 확인할 수 없습니다.`}),L(`span`,{class:`mt-1 block text-amber-800`,children:`새 펌웨어를 설치하면 이후부터 정확한 설치 버전을 자동으로 확인할 수 있습니다.`}),L(`details`,{class:`mt-2`,children:[L(`summary`,{class:`cursor-pointer font-extrabold text-amber-800`,children:`기술 상세 보기`}),L(`p`,{class:`mt-2 mb-0 break-all font-mono text-[11px] leading-5 text-amber-800`,children:`SmartSafeHub build ID metadata is missing. Future images should include /usr/share/smartsafehub/firmware.json.`})]})]}),L(`dl`,{class:`mt-5 grid overflow-hidden rounded-xl border border-slate-200 bg-slate-200 sm:grid-cols-2 xl:grid-cols-4`,children:[L(`div`,{class:`bg-white p-4`,children:[L(`dt`,{class:`text-[11px] font-black uppercase tracking-[0.16em] text-slate-400`,children:`Device`}),L(`dd`,{class:`mt-2 mb-0 ml-0 break-all text-sm font-black text-slate-950`,children:n.current.deviceCode||n.current.boardName||`미확인`})]}),L(`div`,{class:`bg-white p-4`,children:[L(`dt`,{class:`text-[11px] font-black uppercase tracking-[0.16em] text-slate-400`,children:`Current version`}),L(`dd`,{class:`mt-2 mb-0 ml-0 break-all text-sm font-black text-slate-950`,children:n.current.releaseVersion||`미확인`}),n.current.buildId?L(`span`,{class:`mt-1 block break-all text-[11px] leading-5 text-slate-400`,children:[`빌드 ID `,n.current.buildId]}):n.current.openwrtVersion?L(`span`,{class:`mt-1 block text-[11px] leading-5 text-slate-400`,children:[`OpenWrt `,n.current.openwrtVersion]}):null]}),L(`div`,{class:`bg-white p-4`,children:[L(`dt`,{class:`text-[11px] font-black uppercase tracking-[0.16em] text-slate-400`,children:`Available version`}),L(`dd`,{class:`mt-2 mb-0 ml-0 break-all text-sm font-black ${n.updateAvailable?`text-amber-700`:`text-slate-950`}`,children:n.lastCheckAt?n.release?.version?`${n.release.version}${n.updateAvailable?``:` · 최신`}`:n.updateAvailable?`새 펌웨어`:`최신 버전`:`미확인`}),n.release?.buildId?L(`span`,{class:`mt-1 block break-all text-[11px] leading-5 text-slate-400`,children:[`빌드 ID `,n.release.buildId]}):null]}),L(`div`,{class:`bg-white p-4`,children:[L(`dt`,{class:`text-[11px] font-black uppercase tracking-[0.16em] text-slate-400`,children:`Last check`}),L(`dd`,{class:`mt-2 mb-0 ml-0 text-sm font-black text-slate-950`,children:L(Ar,{fallback:`아직 없음`,timestamp:n.lastCheckAt})})]})]})]}):null,n?.updateAvailable&&n.release?L(`section`,{class:`mt-5 rounded-xl border border-sky-200 bg-sky-50/60 p-4 sm:p-5`,"data-section":`available-firmware`,children:[L(`div`,{class:`flex flex-col gap-2 sm:flex-row sm:items-start sm:justify-between sm:gap-4`,children:[L(`div`,{children:[L(`p`,{class:`m-0 text-xs font-extrabold uppercase tracking-[0.16em] text-sky-700`,children:`Available firmware`}),L(`h3`,{class:`mt-2 mb-0 text-lg font-black text-slate-950`,children:`새 펌웨어가 있습니다.`}),L(`p`,{class:`mt-2 mb-0 text-sm leading-6 text-slate-600`,children:[`펌웨어 `,n.release.version||`미확인`,` · `,Mr(n.release.sysupgrade.sizeBytes),` · `,(n.release.channel||n.settings.channel)===`beta`?`Beta`:`Stable`]})]}),L(`span`,{class:`shrink-0 rounded-full bg-white px-3 py-1.5 text-xs font-extrabold text-sky-700 ring-1 ring-inset ring-sky-200`,children:n.release.version||`Latest`})]}),k?L(`div`,{class:`mt-4 rounded-xl border border-sky-100 bg-white p-4`,"data-section":`firmware-bundled-software`,children:[L(`div`,{class:`flex flex-wrap items-center justify-between gap-2`,children:[L(`span`,{class:`text-xs font-extrabold text-slate-800`,children:`포함된 관리 소프트웨어`}),L(`div`,{class:`flex flex-wrap items-center gap-2`,children:[L(`span`,{class:`text-sm font-black text-slate-950`,children:k}),j===`same`?L(`span`,{class:`rounded-full bg-emerald-50 px-2.5 py-1 text-[11px] font-extrabold text-emerald-700 ring-1 ring-inset ring-emerald-200`,children:`현재 버전과 동일`}):null]})]}),L(`p`,{class:`mt-2 mb-0 text-xs leading-5 text-slate-600`,children:j===`same`?`관리 소프트웨어는 이미 같은 버전입니다. 펌웨어 업데이트는 시스템 구성 요소를 갱신합니다.`:j===`older`?`현재 설치된 관리 소프트웨어(${A})보다 이전 버전이 포함되어 있습니다. 펌웨어 설치 전 버전을 확인해 주세요.`:j===`newer`?`현재 설치된 관리 소프트웨어(${A})보다 새 버전이 포함되어 있습니다.`:A?`현재 설치 버전 ${A} · 펌웨어 포함 버전 ${k}`:`이 펌웨어에 포함된 관리 소프트웨어 버전입니다.`})]}):null,n.release.releaseNotes.length?L(`ul`,{class:`mt-4 mb-0 space-y-2 rounded-xl bg-white p-4 pl-9 text-sm leading-6 text-slate-700 ring-1 ring-inset ring-sky-100`,children:n.release.releaseNotes.map((e,t)=>L(`li`,{children:e},`${t}-${e}`))}):null]}):null,T?L(`section`,{class:`mt-5 rounded-xl border border-amber-200 bg-amber-50 p-4 sm:p-5`,"data-section":`prepared-firmware`,children:L(`div`,{class:`flex gap-4`,children:[L(`span`,{class:`grid size-11 shrink-0 place-items-center rounded-xl bg-white text-amber-700 ring-1 ring-inset ring-amber-200`,children:L(B,{class:`size-5`})}),L(`div`,{class:`min-w-0 flex-1`,children:[L(`p`,{class:`m-0 text-xs font-extrabold uppercase tracking-[0.16em] text-amber-700`,children:`Verified image`}),L(`h3`,{class:`mt-2 mb-0 text-lg font-black text-amber-950`,children:`설치할 펌웨어가 준비되었습니다.`}),L(`p`,{class:`mt-2 mb-0 break-all text-sm leading-6 text-amber-900`,children:[T.filename||`firmware.bin`,` · `,Mr(T.sizeBytes)]}),T.sha256?L(`p`,{class:`mt-2 mb-0 break-all font-mono text-[11px] leading-5 text-amber-800`,children:[`SHA-256 `,T.sha256]}):null,L(`div`,{class:`mt-5 flex items-start justify-between gap-4 rounded-xl border border-amber-200 bg-white p-4`,children:[L(`div`,{children:[L(`strong`,{class:`block text-sm font-black text-slate-950`,children:`현재 설정 유지`}),L(`span`,{class:`mt-1 block text-xs leading-5 text-slate-500`,children:`Wi-Fi, SafeShield, SmartSafeHub 설정을 유지합니다.`}),T.allowBackup?null:L(`span`,{class:`mt-2 block text-xs font-bold text-rose-700`,children:`이 이미지는 설정 유지 업그레이드를 지원하지 않습니다.`})]}),L(Ir,{checked:b,disabled:!T.allowBackup,onChange:x})]}),S?L(`div`,{class:`mt-4 rounded-xl border border-rose-200 bg-rose-50 p-4`,children:[L(`strong`,{class:`block text-sm font-black text-rose-950`,children:`펌웨어 설치를 시작할까요?`}),L(`p`,{class:`mt-1 mb-0 text-xs leading-5 text-rose-800`,children:`설치 중 전원을 끄면 장치가 복구 불가능한 상태가 될 수 있습니다. 이미지 검증은 설치 직전에 다시 수행됩니다.`}),L(`div`,{class:`mt-4 flex flex-wrap justify-end gap-2`,children:[L(`button`,{class:`min-h-10 rounded-lg border border-slate-300 bg-white px-3 py-2 text-xs font-extrabold text-slate-700`,onClick:()=>C(!1),type:`button`,children:`취소`}),L(`button`,{class:`min-h-10 rounded-lg bg-rose-700 px-3 py-2 text-xs font-extrabold text-white`,onClick:()=>{C(!1),f(b)},type:`button`,children:`설치 시작`})]})]}):L(`div`,{class:`mt-4 flex flex-wrap justify-end gap-2`,children:[L(`button`,{class:`min-h-10 rounded-lg border border-slate-300 bg-white px-3 py-2 text-xs font-extrabold text-slate-700`,disabled:e===`discard`,onClick:()=>void u(),type:`button`,children:`파일 삭제`}),L(`button`,{class:`inline-flex min-h-10 items-center gap-2 rounded-lg bg-rose-700 px-4 py-2 text-xs font-extrabold text-white`,onClick:()=>C(!0),type:`button`,children:[L(Je,{class:`size-4`}),`펌웨어 설치`]})]})]})]})}):null]}),L(`details`,{class:`group mx-5 mb-5 rounded-xl border border-slate-200 bg-slate-50/70 sm:mx-6 sm:mb-6`,"data-layout":`firmware-card-subsection`,"data-section":`manual-firmware`,children:[L(`summary`,{class:`flex cursor-pointer list-none items-center justify-between gap-4 px-4 py-4 sm:px-5 [&::-webkit-details-marker]:hidden`,children:[L(`div`,{class:`flex min-w-0 items-center gap-3`,children:[L(`span`,{class:`grid size-10 shrink-0 place-items-center rounded-xl bg-white text-slate-600 ring-1 ring-inset ring-slate-200`,children:L(it,{class:`size-5 rotate-180`})}),L(`div`,{class:`min-w-0`,children:[L(`strong`,{class:`block text-sm font-black text-slate-950`,children:`수동 펌웨어 설치`}),L(`span`,{class:`mt-1 block text-xs leading-5 text-slate-500`,children:`펌웨어 파일(.bin)을 직접 업로드하여 검증한 뒤 설치합니다.`})]})]}),L(`span`,{class:`shrink-0 rounded-full bg-white px-3 py-1.5 text-[11px] font-extrabold text-slate-600 ring-1 ring-inset ring-slate-200 group-open:hidden`,children:`열기`}),L(`span`,{class:`hidden shrink-0 rounded-full bg-white px-3 py-1.5 text-[11px] font-extrabold text-slate-600 ring-1 ring-inset ring-slate-200 group-open:inline-flex`,children:`닫기`})]}),L(`div`,{class:`border-t border-slate-200 px-5 pb-5 pt-3 sm:px-6`,children:[L(`p`,{class:`mt-0 mb-4 text-xs leading-5 text-slate-500 lg:whitespace-nowrap`,children:"이 장치에 맞는 Sysupgrade 이미지(.bin)만 사용하세요. 업로드한 파일은 이미지 검증과 `sysupgrade --test`를 모두 통과해야 설치할 수 있으며 강제 설치는 제공하지 않습니다."}),L(`div`,{class:`rounded-xl border border-slate-200 bg-white p-4 sm:p-5`,"data-layout":`firmware-file-picker`,children:[L(`input`,{accept:`.bin,application/octet-stream`,"aria-label":`펌웨어 파일 선택`,class:`sr-only`,disabled:w||T!==null,onChange:e=>y(e.currentTarget.files?.[0]??null),ref:g,type:`file`}),L(`div`,{class:`flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between`,children:[L(`div`,{class:`flex min-w-0 items-center gap-3`,children:[L(`span`,{class:`grid size-10 shrink-0 place-items-center rounded-xl bg-slate-50 text-slate-500 ring-1 ring-inset ring-slate-200`,children:L(it,{class:`size-5 rotate-180`})}),L(`div`,{class:`min-w-0`,children:[L(`strong`,{class:`block truncate text-sm font-black text-slate-900`,children:_?_.name:`펌웨어 파일을 선택하세요`}),L(`span`,{class:`mt-1 block text-xs leading-5 text-slate-500`,children:_?`${Mr(_.size)} · Sysupgrade 이미지 (.bin)`:`이 장치에 맞는 Sysupgrade 이미지 (.bin)를 선택합니다.`})]})]}),L(`div`,{class:`flex shrink-0 flex-wrap gap-2 sm:justify-end`,children:[L(`button`,{class:`inline-flex min-h-10 items-center justify-center rounded-lg border border-slate-300 bg-white px-3.5 py-2 text-xs font-extrabold text-slate-700 transition hover:bg-slate-50 disabled:cursor-not-allowed disabled:opacity-50`,disabled:w||T!==null,onClick:()=>g.current?.click(),type:`button`,children:_?`다른 파일 선택`:`파일 선택`}),L(`button`,{class:`inline-flex min-h-10 items-center justify-center gap-2 rounded-lg bg-slate-900 px-3.5 py-2 text-xs font-extrabold text-white transition hover:bg-slate-800 disabled:cursor-not-allowed disabled:opacity-50`,disabled:!_||w||T!==null,onClick:()=>void M(),type:`button`,children:[O?L(z,{class:`size-4 animate-spin`}):L(it,{class:`size-4 rotate-180`}),O?`처리 중...`:`업로드 및 검증`]})]})]}),c===null?null:L(`div`,{class:`mt-4 border-t border-slate-100 pt-4`,children:[L(`div`,{class:`mb-2 flex items-center justify-between gap-3 text-xs font-bold text-slate-600`,children:[L(`span`,{children:`업로드 중`}),L(`span`,{children:[c,`%`]})]}),L(`div`,{class:`h-2 overflow-hidden rounded-full bg-slate-200`,children:L(`div`,{class:`h-full rounded-full bg-sky-600 transition-[width]`,style:{width:`${Math.max(0,Math.min(100,c))}%`}})})]})]})]})]})]})}var Rr=Array.from({length:24},(e,t)=>{let n=String(t).padStart(2,`0`);return{value:n,label:`${n}시`}}),zr=Array.from({length:60},(e,t)=>{let n=String(t).padStart(2,`0`);return{value:n,label:`${n}분`}});function Br(e){let t=/^([01]\d|2[0-3]):([0-5]\d)$/.exec(e);return{hour:t?.[1]??`03`,minute:t?.[2]??`00`}}function Vr({ariaLabel:e,className:t=``,disabled:n=!1,id:r,onChange:i,value:a,variant:o=`default`}){let{hour:s,minute:c}=Br(a),l=(e,t)=>{i(`${e}:${t}`)};return L(`div`,{"aria-label":e,class:`ssh-time-select ${t}`.trim(),"data-time-select":r,role:`group`,children:[L(Nn,{ariaLabel:`${e} 시`,disabled:n,id:`${r}-hour`,onChange:e=>l(e,c),options:Rr,value:s,variant:o}),L(Nn,{ariaLabel:`${e} 분`,disabled:n,id:`${r}-minute`,onChange:e=>l(s,e),options:zr,value:c,variant:o})]})}var Hr=7200;function Ur(e,t=Math.floor(Date.now()/1e3)){return!e||!e.settings.checkEnabled||e.phase===`checking`||!e.lastCheckAt?!1:Math.max(0,t-e.lastCheckAt)>Math.max(e.settings.checkIntervalSeconds*2,Hr)}var Wr=[{value:3600,label:`1시간마다`},{value:21600,label:`6시간마다`},{value:43200,label:`12시간마다`},{value:86400,label:`24시간마다`}],Gr=`luci-app-smartsafehub`;function Kr(e){return e?new Date(e*1e3).toLocaleString(`ko-KR`,{dateStyle:`medium`,timeStyle:`short`}):`아직 없음`}function qr(e){return e===`stable`?`Stable`:e===`beta`?`Beta`:`미확인`}function Jr(e){return e===`stable`?`bg-emerald-50 text-emerald-700 ring-emerald-200`:e===`beta`?`bg-amber-50 text-amber-700 ring-amber-200`:`bg-slate-100 text-slate-600 ring-slate-200`}function Yr(e){return e.code===`UPDATES_INDEX_REFRESH_FAILED`?{title:`패키지 저장소를 확인하지 못했습니다.`,description:`OpenWrt 패키지 저장소 동기화 또는 네트워크 상태가 일시적으로 불안정할 수 있습니다. 잠시 후 업데이트 확인을 다시 실행해 주세요.`}:e.code===`UPDATES_INSTALL_FAILED`?{title:`관리 소프트웨어 업데이트 설치에 실패했습니다.`,description:`패키지 설치가 완료되지 않았습니다. 네트워크 연결과 저장 공간을 확인한 뒤 다시 시도해 주세요.`}:{title:`최근 업데이트 작업을 완료하지 못했습니다.`,description:`잠시 후 다시 시도해 주세요. 문제가 반복되면 상세 정보를 확인해 주세요.`}}function Xr(e,t){return e.phase===`checking`?`업데이트 확인 중`:e.phase===`installing`?`업데이트 설치 중`:e.phase===`error`?`확인 필요`:e.updateCount>0?`업데이트 가능`:t?`업데이트 확인 지연`:e.lastCheckAt?`최신 상태`:`확인 전`}function Zr(e,t){return e.phase===`error`?`bg-rose-50 text-rose-700 ring-rose-200`:e.phase===`checking`||e.phase===`installing`?`bg-sky-50 text-sky-700 ring-sky-200`:e.updateCount>0||t?`bg-amber-50 text-amber-800 ring-amber-200`:e.lastCheckAt?`bg-emerald-50 text-emerald-700 ring-emerald-200`:`bg-slate-100 text-slate-600 ring-slate-200`}function Qr(e,t){return e.phase===`checking`||e.phase===`installing`?L(z,{class:`size-3.5 shrink-0 animate-spin`}):e.phase===`error`?L(V,{class:`size-3.5 shrink-0`}):e.updateCount>0?L(it,{class:`size-3.5 shrink-0`}):t?L(V,{class:`size-3.5 shrink-0`}):e.lastCheckAt?L(B,{class:`size-3.5 shrink-0`}):L(tt,{class:`size-3.5 shrink-0`})}function $r({checked:e,disabled:t=!1,label:n,onChange:r}){return L(`button`,{"aria-checked":e,"aria-label":n,class:`ssh-switch-control relative inline-flex shrink-0 rounded-full border transition focus:outline-none focus-visible:ring-4 focus-visible:ring-teal-100 disabled:cursor-not-allowed disabled:opacity-50 ${e?`border-teal-600 bg-teal-600`:`border-slate-300 bg-slate-200`}`,disabled:t,onClick:()=>r(!e),role:`switch`,type:`button`,children:L(`span`,{"aria-hidden":`true`,class:`ssh-switch-thumb absolute top-1 shadow-sm transition-[left] ${e?`left-6`:`left-1`}`})})}function ei({action:e,actionError:t,data:n,firmware:r,error:i,loading:a,message:o,onCheck:s,onDismissFeedback:c,onInstall:l,onRetry:u,onSaveSettings:d}){let[f,p]=F(!0),[m,h]=F(21600),[g,_]=F(!1),[y,b]=F(`03:00`),[x,S]=F(!1),[C,w]=F(!1);I(()=>{n&&!x&&(p(n.settings.checkEnabled),h(n.settings.checkIntervalSeconds),_(n.settings.autoInstall),b(n.settings.autoInstallTime))},[n,x]);let T=e===`check`||n?.phase===`checking`,E=e===`install`||n?.phase===`installing`,D=T||E,O=Ur(n),k=n?.phase===`error`&&n.lastError?.code===`UPDATES_INDEX_REFRESH_FAILED`,A=n?.lastError?Yr(n.lastError):null,j=n?.packages.find(e=>e.name===Gr)??n?.packages[0]??null,M=r?.updateAvailable?r.release?.bundledSoftware?.smartsafehub:null,ee=!(!n?.lastCheckAt||n.updateCount!==0||O||j?.name!==Gr)&&jr(M,j?.installedVersion)===`same`,te=(e={})=>n?(e.checkEnabled??f)!==n.settings.checkEnabled||(e.checkIntervalSeconds??m)!==n.settings.checkIntervalSeconds||(e.autoInstall??g)!==n.settings.autoInstall||(e.autoInstallTime??y)!==n.settings.autoInstallTime:!1,ne=async()=>{await d({checkEnabled:f,checkIntervalSeconds:m,autoInstall:g,autoInstallTime:y})&&S(!1)};return L(`article`,{class:`min-w-0 overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm shadow-slate-900/5 ${n?`grid xl:grid-cols-[minmax(0,0.95fr)_minmax(0,1.05fr)]`:``}`,"data-component":`management-software-update-card`,"data-layout":`management-software-sections`,children:[L(`section`,{class:`min-w-0 xl:col-start-1 xl:row-start-1`,"data-section":`software-update-status`,children:[L(`div`,{class:`p-5 sm:p-6`,children:[L(`div`,{class:`flex flex-col gap-5 lg:flex-row lg:items-start lg:justify-between`,children:[L(`div`,{class:`flex min-w-0 gap-4`,children:[L(`span`,{class:`grid size-12 shrink-0 place-items-center rounded-2xl bg-teal-50 text-teal-700`,children:L(Je,{class:`size-6`})}),L(`div`,{class:`min-w-0`,children:[L(`div`,{class:`flex flex-wrap items-center gap-2`,children:[L(`p`,{class:`m-0 text-xs font-extrabold uppercase tracking-[0.16em] text-teal-700`,children:`Management software`}),n?L(`span`,{class:`inline-flex items-center gap-1.5 rounded-full px-2.5 py-1 text-[11px] font-extrabold ring-1 ring-inset ${Zr(n,O)}`,children:[L(`span`,{"aria-hidden":`true`,class:`flex items-center justify-center`,children:Qr(n,O)}),Xr(n,O)]}):null]}),L(`h2`,{class:`mt-2 mb-0 text-xl font-black text-slate-950 sm:text-2xl`,children:`관리 소프트웨어 업데이트`}),L(`p`,{class:`mt-2 mb-0 text-sm leading-6 text-slate-500 xl:whitespace-nowrap`,children:`새 버전을 확인하고 안전하게 설치합니다.`})]})]}),n?L(`div`,{class:`flex shrink-0 flex-col gap-2 sm:flex-row lg:justify-end`,children:[L(`button`,{class:`inline-flex min-h-11 items-center justify-center gap-2 rounded-xl border border-slate-300 bg-white px-4 py-2.5 text-sm font-extrabold text-slate-800 transition hover:bg-slate-50 disabled:cursor-wait disabled:opacity-60`,disabled:D,onClick:s,type:`button`,children:[L(z,{class:`size-4 ${T?`animate-spin`:``}`}),T?`확인 중...`:`업데이트 확인`]}),n.updateCount>0&&!C?L(`button`,{class:`inline-flex min-h-11 items-center justify-center gap-2 rounded-xl bg-teal-700 px-4 py-2.5 text-sm font-extrabold text-white transition hover:bg-teal-800 disabled:cursor-wait disabled:opacity-60`,disabled:D,onClick:()=>w(!0),type:`button`,children:[E?L(z,{class:`size-4 animate-spin`}):L(it,{class:`size-4`}),E?`설치 중...`:`업데이트 설치`]}):null]}):null]}),(t||o)&&L(`div`,{class:`mt-5 flex flex-col gap-3 rounded-xl border px-4 py-3 text-sm font-bold sm:flex-row sm:items-center sm:justify-between ${t?`border-rose-200 bg-rose-50 text-rose-800`:`border-emerald-200 bg-emerald-50 text-emerald-800`}`,children:[L(`span`,{children:t||o}),L(`button`,{class:`shrink-0 self-start rounded-lg px-3 py-1.5 text-xs font-extrabold hover:bg-black/5 sm:self-auto`,onClick:c,type:`button`,children:`닫기`})]}),E?L(`div`,{"aria-busy":`true`,"aria-live":`polite`,class:`mt-5 rounded-xl border border-sky-200 bg-sky-50 p-4 sm:p-5`,children:[L(`div`,{class:`flex items-start gap-3`,children:[L(`span`,{class:`grid size-10 shrink-0 place-items-center rounded-xl bg-white text-sky-700 ring-1 ring-inset ring-sky-200`,children:L(z,{"aria-hidden":`true`,class:`size-5 animate-spin`})}),L(`div`,{class:`min-w-0`,children:[L(`h3`,{class:`m-0 text-sm font-black text-sky-950`,children:`관리 소프트웨어를 업데이트하고 있습니다.`}),L(`p`,{class:`mt-1 mb-0 text-sm leading-6 text-sky-800`,children:`설치가 완료되면 이 화면이 자동으로 갱신됩니다. 작업 중에는 웹 연결이 잠시 끊길 수 있습니다.`})]})]}),L(`div`,{"aria-hidden":`true`,class:`ssh-update-progress-track mt-4`,children:L(`span`,{class:`ssh-update-progress-bar`})})]}):null,i&&!n?L(`div`,{class:`mt-5 rounded-xl border border-rose-200 bg-rose-50 p-4 text-sm text-rose-800`,children:[L(`p`,{class:`m-0 font-bold`,children:i}),L(`button`,{class:`mt-3 rounded-lg border border-rose-300 bg-white px-3 py-2 text-xs font-extrabold text-rose-700`,onClick:u,type:`button`,children:`다시 시도`})]}):a&&!n?L(`div`,{class:`mt-5 flex items-center gap-3 rounded-xl bg-slate-50 p-4 text-sm font-bold text-slate-500`,children:[L(`span`,{class:`grid size-9 shrink-0 place-items-center rounded-xl bg-white text-teal-700 ring-1 ring-inset ring-slate-200`,children:L(z,{"aria-hidden":`true`,class:`size-4 animate-spin`})}),L(`span`,{children:`관리 소프트웨어 업데이트 상태를 확인하고 있습니다.`})]}):n?L(v,{children:[n.lastError&&A?L(`div`,{class:`mt-5 rounded-xl border border-rose-200 bg-rose-50 p-4 sm:p-5`,children:L(`div`,{class:`flex items-start gap-3`,children:[L(`span`,{class:`grid size-10 shrink-0 place-items-center rounded-xl bg-white text-rose-700 ring-1 ring-inset ring-rose-200`,children:L(V,{class:`size-5`})}),L(`div`,{class:`min-w-0 flex-1`,children:[L(`p`,{class:`m-0 text-sm font-extrabold text-rose-950`,children:A.title}),L(`p`,{class:`mt-1 mb-0 text-sm leading-6 text-rose-800`,children:A.description}),n.lastError.at?L(`p`,{class:`mt-2 mb-0 text-xs font-bold text-rose-700`,children:[`발생 시각 `,Kr(n.lastError.at)]}):null,L(`details`,{class:`mt-3 rounded-lg border border-rose-200 bg-white p-3`,children:[L(`summary`,{class:`cursor-pointer text-xs font-extrabold text-rose-800`,children:`상세 정보 보기`}),L(`div`,{class:`mt-3 grid gap-2 text-xs leading-5 text-slate-600`,children:[L(`p`,{class:`m-0`,children:[`오류 코드 `,L(`strong`,{class:`font-black text-slate-900`,children:n.lastError.code})]}),L(`pre`,{class:`m-0 max-h-40 overflow-auto whitespace-pre-wrap break-words rounded-lg bg-slate-50 p-3 font-mono text-[11px] leading-5 text-slate-700`,children:n.lastError.message})]})]})]})]})}):null,C?L(`div`,{class:`mt-5 flex flex-col gap-3 rounded-xl border border-amber-200 bg-amber-50 p-4 sm:flex-row sm:items-center sm:justify-between`,children:[L(`div`,{class:`min-w-0`,children:[L(`strong`,{class:`block text-sm font-black text-amber-900`,children:`관리 소프트웨어를 지금 업데이트할까요?`}),L(`span`,{class:`mt-1 block text-xs leading-5 text-amber-800`,children:`설치 중 SmartSafeHub 웹 화면 연결이 잠시 끊길 수 있습니다.`})]}),L(`div`,{class:`flex shrink-0 gap-2`,children:[L(`button`,{class:`min-h-10 rounded-lg border border-slate-300 bg-white px-3 py-2 text-xs font-extrabold text-slate-700`,disabled:D,onClick:()=>w(!1),type:`button`,children:`취소`}),L(`button`,{class:`min-h-10 rounded-lg bg-amber-700 px-3 py-2 text-xs font-extrabold text-white disabled:opacity-60`,disabled:D,onClick:()=>{w(!1),l()},type:`button`,children:`설치 시작`})]})]}):null]}):null]}),n?L(`div`,{class:`px-5 pb-5 sm:px-6 sm:pb-6`,children:[L(`h3`,{class:`mb-3 text-sm font-black text-slate-950`,children:`현재 상태`}),L(`dl`,{class:`grid overflow-hidden rounded-xl border border-slate-200 bg-slate-200 sm:grid-cols-2`,children:[L(`div`,{class:`bg-white p-4`,children:[L(`dt`,{class:`text-[11px] font-black uppercase tracking-[0.16em] text-slate-400`,children:`Installed`}),L(`dd`,{class:`mt-2 mb-0 ml-0 break-all text-sm font-black text-slate-950`,children:j?.installedVersion||`미확인`})]}),L(`div`,{class:`bg-white p-4`,children:[L(`dt`,{class:`text-[11px] font-black uppercase tracking-[0.16em] text-slate-400`,children:`Available`}),L(`dd`,{class:`mt-2 mb-0 ml-0 break-all text-sm font-black ${j?.updateAvailable||O?`text-amber-700`:`text-slate-950`}`,children:!n.lastCheckAt||k&&!j?.updateAvailable?`미확인`:j?.updateAvailable&&j.availableVersion?j.availableVersion:O?`확인 필요`:`최신 버전`})]}),L(`div`,{class:`bg-white p-4`,children:[L(`dt`,{class:`text-[11px] font-black uppercase tracking-[0.16em] text-slate-400`,children:`Last check`}),L(`dd`,{class:`mt-2 mb-0 ml-0 text-sm font-black text-slate-950`,children:L(Ar,{fallback:`아직 없음`,timestamp:n.lastCheckAt})})]}),L(`div`,{class:`bg-white p-4`,children:[L(`dt`,{class:`text-[11px] font-black uppercase tracking-[0.16em] text-slate-400`,children:`Auto install`}),L(`dd`,{class:`mt-2 mb-0 ml-0 text-sm font-black text-slate-950`,children:n.settings.autoInstall?`켜짐 · ${n.settings.autoInstallTime}`:`꺼짐`})]})]})]}):null,n&&n.updateCount===0&&n.lastCheckAt?O?L(`section`,{class:`mx-5 mb-5 rounded-xl border border-amber-200 bg-amber-50 p-4 sm:mx-6 sm:mb-6 sm:p-5`,"data-section":`software-update-result`,children:L(`div`,{class:`flex gap-3`,children:[L(`span`,{class:`grid size-10 shrink-0 place-items-center rounded-xl bg-white text-amber-700 ring-1 ring-inset ring-amber-200`,children:L(V,{class:`size-5`})}),L(`div`,{children:[L(`h3`,{class:`m-0 text-base font-black text-amber-950`,children:`업데이트 확인이 지연되고 있습니다.`}),L(`p`,{class:`mt-1 mb-0 text-sm leading-6 text-amber-800`,children:[`마지막 업데이트 확인: `,L(Ar,{timestamp:n.lastCheckAt}),`. 자동 확인 상태를 점검하거나 업데이트 확인을 실행해 주세요.`]})]})]})}):L(`section`,{class:`mx-5 mb-5 rounded-xl border border-emerald-200 bg-emerald-50 p-4 sm:mx-6 sm:mb-6 sm:p-5`,"data-section":`software-update-result`,children:L(`div`,{class:`flex gap-3`,children:[L(`span`,{class:`grid size-10 shrink-0 place-items-center rounded-xl bg-white text-emerald-700`,children:L(B,{class:`size-5`})}),L(`div`,{children:[L(`h3`,{class:`m-0 text-base font-black text-emerald-950`,children:`최신 버전을 사용 중입니다.`}),L(`p`,{class:`mt-1 mb-0 text-sm leading-6 text-emerald-800`,children:[`마지막 업데이트 확인: `,L(Ar,{timestamp:n.lastCheckAt}),`.`]}),ee?L(`p`,{class:`mt-2 mb-0 text-xs leading-5 text-emerald-800`,"data-section":`software-firmware-match`,children:[`새 펌웨어에도 동일한 관리 소프트웨어(`,M,`)가 포함되어 있어 별도로 업데이트할 필요가 없습니다.`]}):null]})]})}):null,n&&n.updateCount===0&&!n.lastCheckAt?L(`section`,{class:`mx-5 mb-5 rounded-xl border border-slate-200 bg-slate-50 p-4 sm:mx-6 sm:mb-6 sm:p-5`,"data-section":`software-update-result`,children:L(`div`,{class:`flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between`,children:[L(`div`,{class:`flex min-w-0 gap-3`,children:[L(`span`,{class:`grid size-10 shrink-0 place-items-center rounded-xl bg-white text-slate-500 ring-1 ring-inset ring-slate-200`,children:L(z,{class:`size-5`})}),L(`div`,{class:`min-w-0`,children:[L(`h3`,{class:`m-0 text-base font-black text-slate-900`,children:`업데이트 상태를 아직 확인하지 않았습니다.`}),L(`p`,{class:`mt-1 mb-0 text-sm leading-6 text-slate-600`,children:`업데이트 확인을 실행하면 현재 설치 버전과 사용 가능한 최신 버전을 확인합니다.`})]})]}),L(`button`,{class:`inline-flex min-h-10 shrink-0 items-center justify-center gap-2 rounded-xl border border-slate-300 bg-white px-4 py-2 text-sm font-extrabold text-slate-800 transition hover:border-teal-300 hover:text-teal-700 disabled:cursor-wait disabled:opacity-60`,disabled:D,onClick:s,type:`button`,children:[L(z,{class:`size-4`}),`지금 확인`]})]})}):null]}),n?L(v,{children:[L(`section`,{class:`min-w-0 border-t border-slate-200 p-5 sm:p-6 xl:col-start-2 xl:row-start-1 xl:row-span-2 xl:self-stretch xl:border-t-0 xl:border-l`,"data-section":`software-update-settings`,children:[L(`div`,{class:`flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between`,children:[L(`div`,{class:`flex min-w-0 gap-4`,children:[L(`span`,{class:`grid size-11 shrink-0 place-items-center rounded-xl bg-slate-100 text-slate-600`,children:L(Ye,{class:`size-5`})}),L(`div`,{class:`min-w-0`,children:[L(`p`,{class:`m-0 text-xs font-extrabold uppercase tracking-[0.16em] text-slate-500`,children:`Settings`}),L(`h3`,{class:`mt-2 mb-0 text-lg font-black text-slate-950`,children:`자동 업데이트`}),L(`p`,{class:`mt-2 mb-0 max-w-3xl text-sm leading-6 text-slate-500`,children:`관리 소프트웨어의 자동 확인과 자동 설치를 설정합니다.`})]})]}),x?L(`span`,{"aria-live":`polite`,class:`inline-flex shrink-0 self-start items-center gap-1.5 rounded-full border border-amber-200 bg-amber-50 px-2.5 py-1 text-xs font-extrabold text-amber-800`,role:`status`,children:[L(V,{"aria-hidden":`true`,class:`size-3.5 shrink-0`}),`저장되지 않음`]}):null]}),L(`div`,{class:`ssh-software-settings-grid mt-4`,children:[L(`div`,{class:`ssh-software-settings-channel rounded-xl border border-slate-200 p-4 sm:p-5`,children:L(`div`,{class:`flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between`,children:[L(`div`,{class:`min-w-0`,children:[L(`strong`,{class:`block text-sm font-black text-slate-950`,children:`업데이트 채널`}),L(`span`,{class:`mt-1 block text-xs leading-5 text-slate-500`,children:`현재 관리 소프트웨어 업데이트를 확인하는 배포 채널입니다.`})]}),L(`span`,{class:`inline-flex min-h-9 shrink-0 items-center justify-center rounded-full px-3 py-1.5 text-xs font-extrabold ring-1 ring-inset ${Jr(n.settings.channel)}`,title:`현재 업데이트 확인 채널`,children:qr(n.settings.channel)})]})}),L(`div`,{class:`min-w-0 overflow-hidden rounded-xl border border-slate-200 p-4 sm:p-5`,children:[L(`div`,{class:`flex min-w-0 items-start justify-between gap-4`,children:[L(`div`,{class:`min-w-0`,children:[L(`strong`,{class:`block text-sm font-black text-slate-950`,children:`자동으로 업데이트 확인`}),L(`span`,{class:`mt-1 block text-xs leading-5 text-slate-500`,children:`선택한 주기에 새 관리 소프트웨어 버전을 확인합니다.`})]}),L($r,{checked:f,label:`자동 업데이트 확인`,onChange:e=>{p(e),S(te({checkEnabled:e}))}})]}),L(`div`,{class:`mt-4 block`,children:[L(`span`,{class:`text-xs font-extrabold text-slate-700`,children:`확인 주기`}),L(Nn,{ariaLabel:`업데이트 확인 주기`,className:`mt-2`,disabled:!f,id:`software-update-check-interval`,onChange:e=>{let t=Number(e);h(t),S(te({checkIntervalSeconds:t}))},options:Wr.map(e=>({value:String(e.value),label:e.label})),value:String(m),variant:`emphasized`})]})]}),L(`div`,{class:`min-w-0 overflow-hidden rounded-xl border border-slate-200 p-4 sm:p-5`,children:[L(`div`,{class:`flex min-w-0 items-start justify-between gap-4`,children:[L(`div`,{class:`min-w-0`,children:[L(`strong`,{class:`block text-sm font-black text-slate-950`,children:`자동으로 업데이트 설치`}),L(`span`,{class:`mt-1 block text-xs leading-5 text-slate-500`,children:`새 버전이 있으면 지정한 시각에 자동으로 설치합니다.`})]}),L($r,{checked:g,label:`자동 업데이트 설치`,onChange:e=>{_(e),S(te({autoInstall:e}))}})]}),L(`div`,{class:`mt-4 min-w-0`,children:[L(`span`,{class:`text-xs font-extrabold text-slate-700`,children:`설치 시각`}),L(Vr,{ariaLabel:`자동 업데이트 설치 시각`,className:`mt-2`,disabled:!g,id:`software-update-auto-install-time`,onChange:e=>{b(e),S(te({autoInstallTime:e}))},value:y,variant:`emphasized`})]})]})]}),g?L(`p`,{class:`mt-4 mb-0 rounded-xl bg-amber-50 px-4 py-3 text-xs font-bold leading-5 text-amber-800`,children:`자동 설치 중에는 SmartSafeHub 웹 화면 연결이 잠시 끊길 수 있습니다.`}):null,L(`div`,{class:`mt-5 flex flex-col gap-3 border-t border-slate-100 pt-5 sm:flex-row sm:items-center sm:justify-between`,children:[L(`p`,{class:`m-0 text-xs leading-5 text-slate-500`,children:[`마지막 설치: `,Kr(n.lastInstallAt)]}),L(`button`,{class:`inline-flex min-h-11 items-center justify-center rounded-xl bg-slate-900 px-4 py-2.5 text-sm font-extrabold text-white transition hover:bg-slate-800 disabled:cursor-not-allowed disabled:opacity-50`,disabled:!x||e===`settings`||D,onClick:()=>void ne(),type:`button`,children:e===`settings`?`저장 중`:`자동 업데이트 설정 저장`})]})]}),n.updateCount>0?L(`section`,{class:`border-t border-slate-200 p-5 sm:p-6 xl:col-start-1 xl:row-start-2`,"data-section":`software-update-release-notes`,children:[L(`div`,{class:`flex flex-col gap-2 sm:flex-row sm:items-start sm:justify-between sm:gap-4`,children:[L(`div`,{children:[L(`p`,{class:`m-0 text-xs font-extrabold uppercase tracking-[0.16em] text-teal-700`,children:`Release notes`}),L(`h3`,{class:`mt-2 mb-0 text-lg font-black text-slate-950`,children:`업데이트 내용`}),L(`p`,{class:`mt-2 mb-0 text-sm leading-6 text-slate-500`,children:`현재 설치된 버전 이후 포함되는 주요 변경 사항입니다.`})]}),n.releaseNotes.length?L(`span`,{class:`shrink-0 rounded-full bg-slate-100 px-3 py-1.5 text-xs font-extrabold text-slate-600`,children:n.releaseNotes.length===1?`1개 릴리즈`:`${n.releaseNotes.length}개 릴리즈`}):null]}),n.releaseNotes.length?L(`div`,{class:`mt-5 grid gap-3`,children:[n.releaseNotes.map(e=>L(`article`,{class:`rounded-xl border border-slate-200 bg-slate-50 p-4 sm:p-5`,children:[L(`div`,{class:`flex flex-col gap-1 sm:flex-row sm:items-center sm:justify-between sm:gap-4`,children:[L(`strong`,{class:`break-all text-sm font-black text-slate-950`,children:e.version}),e.date?L(`span`,{class:`text-xs font-bold text-slate-500`,children:e.date}):null]}),e.summary?L(`p`,{class:`mt-3 mb-0 text-sm leading-6 text-slate-700`,children:e.summary}):null,e.sections.length?L(`details`,{class:`mt-4 rounded-lg border border-slate-200 bg-white p-3`,open:n.releaseNotes.length===1,children:[L(`summary`,{class:`cursor-pointer text-xs font-extrabold text-slate-800`,children:`자세한 변경 사항`}),L(`div`,{class:`mt-3 grid gap-4`,children:e.sections.map(t=>L(`section`,{children:[L(`h4`,{class:`m-0 text-xs font-extrabold text-slate-700`,children:t.title}),L(`ul`,{class:`mt-2 mb-0 space-y-1.5 pl-5 text-xs leading-5 text-slate-600`,children:t.items.map((n,r)=>L(`li`,{children:n},`${e.version}-${t.title}-${r}`))})]},`${e.version}-${t.title}`))})]}):null]},e.version)),n.releaseNotesComplete?null:L(`p`,{class:`m-0 rounded-xl bg-amber-50 px-4 py-3 text-xs font-bold leading-5 text-amber-800`,children:`일부 릴리즈 노트를 불러오지 못했습니다. 업데이트 확인과 설치는 계속 사용할 수 있습니다.`})]}):L(`p`,{class:`mt-4 mb-0 rounded-xl bg-slate-50 px-4 py-3 text-sm text-slate-500`,children:`릴리즈 노트를 불러오지 못했습니다. 업데이트 확인과 설치는 계속 사용할 수 있습니다.`})]}):null]}):null]})}function ti({action:e,actionError:t,data:n,error:r,firmware:i,loading:a,message:o,onCheck:s,onDismissFeedback:c,onInstall:l,onRetry:u,onSaveSettings:d}){return L(`section`,{class:`min-w-0 space-y-5`,children:[L(Lr,{action:i.action,actionError:i.actionError,data:i.data,software:n,error:i.error,loading:i.loading,message:i.message,reconnecting:i.reconnecting,uploadProgress:i.uploadProgress,onCheck:()=>void i.check(),onDiscard:i.discard,onDismissFeedback:i.dismissFeedback,onInstall:i.install,onPrepare:()=>void i.prepare(),onRetry:()=>void i.refresh(),onUpload:i.upload}),L(ei,{firmware:i.data,action:e,actionError:t,data:n,error:r,loading:a,message:o,onCheck:s,onDismissFeedback:c,onInstall:l,onRetry:u,onSaveSettings:d})]})}var ni={"#home":`home`,"#activity":`activity`,"#network":`network`,"#network/internet":`network`,"#network/lan":`network`,"#wan":`network`,"#lan":`network`,"#wifi":`wifi`,"#iptv":`iptv`,"#devices":`devices`,"#safeshield":`safeshield`,"#rules":`rules`,"#account":`account`,"#system":`system`,"#settings":`settings`,"#settings/diagnostics":`settings`,"#settings/time":`settings`,"#settings/management":`settings`};function ri(){return ni[window.location.hash]??`home`}function ii(){let[e,t]=F(ri);return I(()=>{let e=()=>t(ri());return window.addEventListener(`hashchange`,e),()=>window.removeEventListener(`hashchange`,e)},[]),e}function ai(e){return e===`#lan`||e===`#network/lan`?`lan`:`internet`}function oi(e){return e===`#settings/time`?`time`:e===`#settings/management`?`management`:`diagnostics`}function si(){let e=window.location.hash;return{network:ai(e),settings:oi(e)}}function ci(){let[e,t]=F(si);return I(()=>{let e=()=>t(si());return window.addEventListener(`hashchange`,e),()=>window.removeEventListener(`hashchange`,e)},[]),e}function li({active:e,label:t,tabs:n}){return L(`nav`,{"aria-label":t,class:`mb-5 min-w-0`,role:`tablist`,children:L(`div`,{class:`grid w-full min-w-0 gap-2 ${n.length===3?`grid-cols-3 sm:max-w-[592px]`:`grid-cols-2 sm:max-w-[392px]`}`,children:n.map(t=>{let r=t.id===e;return L(`a`,{"aria-controls":`${t.id}-panel`,"aria-selected":r,class:`flex min-h-11 min-w-0 items-center justify-center rounded-xl border px-2 py-2.5 text-center text-[11px] font-extrabold leading-5 transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-teal-500 sm:px-4 sm:text-sm ${r?`border-teal-200 bg-teal-50 text-teal-800`:`border-slate-200 bg-white text-slate-600 hover:border-teal-200 hover:text-teal-700`}`,href:t.href,id:`${t.id}-tab`,role:`tab`,tabIndex:r?0:-1,onKeyDown:e=>{if(![`ArrowLeft`,`ArrowRight`,`Home`,`End`].includes(e.key))return;e.preventDefault();let r=n.findIndex(e=>e.id===t.id),i=e.key===`Home`?0:e.key===`End`?n.length-1:(r+(e.key===`ArrowRight`?1:-1)+n.length)%n.length,a=e.currentTarget.parentElement?.children[i];a?.focus(),a?.click()},children:t.label},t.id)})})})}var ui=[{value:`all`,label:`전체`},{value:`online`,label:`현재 연결`},{value:`wifi`,label:`Wi-Fi`},{value:`ethernet`,label:`유선/기타`},{value:`offline`,label:`최근 기기`}],di=new Intl.Collator(`ko`),fi=new Intl.DateTimeFormat(`ko-KR`,{month:`numeric`,day:`numeric`,hour:`2-digit`,minute:`2-digit`});function pi(e){switch(e){case`wifi`:return`Wi-Fi`;case`ethernet`:return`유선/기타`;default:return`연결 방식 미확인`}}function mi(e){return e.hostname??e.ipv4Address??`이름 없는 기기`}function hi(e){return e===null?null:e>=-50?`매우 좋음`:e>=-60?`좋음`:e>=-70?`보통`:`약함`}function gi(e){if(e===null||e<0)return null;let t=Math.floor(e/86400),n=Math.floor(e%86400/3600),r=Math.floor(e%3600/60);return t>0?`${t}일 ${n}시간`:n>0?`${n}시간 ${r}분`:`${Math.max(r,1)}분`}function _i(e){return e.leaseActive?e.leaseExpiresAt===null?`DHCP 임대 활성`:`DHCP ${fi.format(new Date(e.leaseExpiresAt*1e3))}까지`:null}function vi(e,t){switch(t){case`online`:return e.online;case`wifi`:return e.online&&e.connection===`wifi`;case`ethernet`:return e.online&&e.connection===`ethernet`;case`offline`:return!e.online;default:return!0}}function yi({device:e}){return L(`span`,{class:`inline-flex items-center gap-2 rounded-full px-3 py-1.5 text-xs font-extrabold ring-1 ring-inset ${e.online?`bg-emerald-50 text-emerald-700 ring-emerald-200`:`bg-slate-100 text-slate-600 ring-slate-200`}`,children:[L(`span`,{class:`size-2 rounded-full ${e.online?`bg-emerald-500`:`bg-slate-400`}`}),e.online?`현재 연결`:`최근 임대`]})}function bi({device:e}){let t=hi(e.signalDbm),n=gi(e.connectedSeconds),r=_i(e);return L(`article`,{class:`rounded-2xl border border-slate-200 bg-white p-4 shadow-sm shadow-slate-900/5 sm:p-5`,children:[L(`div`,{class:`flex min-w-0 flex-col items-start gap-4 sm:flex-row sm:justify-between`,children:[L(`div`,{class:`flex min-w-0 items-start gap-3`,children:[L(`span`,{class:`grid size-11 shrink-0 place-items-center rounded-xl ${e.connection===`wifi`?`bg-teal-50 text-teal-700`:e.connection===`ethernet`?`bg-blue-50 text-blue-700`:`bg-slate-100 text-slate-600`}`,children:e.connection===`wifi`?L(Xe,{class:`size-6`}):e.connection===`ethernet`?L(Qe,{class:`size-6`}):L(Ge,{class:`size-6`})}),L(`div`,{class:`min-w-0`,children:[L(`h3`,{class:`m-0 break-words text-base font-extrabold text-slate-950`,children:mi(e)}),L(`p`,{class:`mt-1 mb-0 break-all font-mono text-xs leading-5 text-slate-500`,children:e.mac})]})]}),L(`span`,{class:`shrink-0 self-start`,children:L(yi,{device:e})})]}),L(`dl`,{class:`mt-5 grid gap-3 text-sm sm:grid-cols-2`,children:[L(`div`,{class:`rounded-xl bg-slate-50 p-3.5`,children:[L(`dt`,{class:`text-xs font-bold text-slate-500`,children:`IP 주소`}),L(`dd`,{class:`mt-1.5 mb-0 ml-0 font-extrabold text-slate-900`,children:e.ipv4Address??`확인되지 않음`})]}),L(`div`,{class:`rounded-xl bg-slate-50 p-3.5`,children:[L(`dt`,{class:`text-xs font-bold text-slate-500`,children:`연결 방식`}),L(`dd`,{class:`mt-1.5 mb-0 ml-0 font-extrabold text-slate-900`,children:pi(e.connection)})]})]}),L(`div`,{class:`mt-4 flex flex-wrap gap-2 text-xs font-bold text-slate-600`,children:[e.ssid?L(`span`,{class:`rounded-full bg-teal-50 px-3 py-1.5 text-teal-800`,children:[e.ssid,e.bandLabel?` · ${e.bandLabel}`:``]}):null,t?L(`span`,{class:`rounded-full bg-slate-100 px-3 py-1.5`,children:[`신호 `,t,` · `,e.signalDbm,` dBm`]}):null,n?L(`span`,{class:`rounded-full bg-slate-100 px-3 py-1.5`,children:[`연결 `,n]}):null,r?L(`span`,{class:`rounded-full bg-slate-100 px-3 py-1.5`,children:r}):null,e.interface?L(`span`,{class:`rounded-full bg-slate-100 px-3 py-1.5`,children:e.interface}):null]})]})}function xi({data:e,error:t,loading:n,onRetry:r}){let[i,a]=F(``),[o,s]=F(`all`),c=Te(()=>{if(!e)return[];let t=i.trim().toLocaleLowerCase();return[...e.devices].filter(e=>vi(e,o)).filter(e=>!t||[e.hostname,e.ipv4Address,e.mac,e.ssid].filter(e=>!!e).some(e=>e.toLocaleLowerCase().includes(t))).sort((e,t)=>e.online===t.online?e.connection===t.connection?di.compare(mi(e),mi(t)):e.connection===`wifi`?-1:1:e.online?-1:1)},[e,o,i]);return n?L(Rn,{}):t?L(zn,{message:t,onRetry:r}):e?L(v,{children:[L(`section`,{"aria-label":`연결 기기 요약`,class:`grid gap-4 sm:grid-cols-2 xl:grid-cols-4`,children:[L(Si,{description:`${e.totals.offline}대는 최근 DHCP 임대에서 확인`,icon:L(Ge,{class:`size-6`}),label:`확인된 기기`,value:e.totals.known}),L(Si,{description:`Wi-Fi 또는 ARP에서 현재 확인됨`,icon:L(tt,{class:`size-6`}),label:`현재 연결`,value:e.totals.online}),L(Si,{description:`무선 AP에 연결된 기기`,icon:L(Xe,{class:`size-6`}),label:`Wi-Fi`,value:e.totals.wireless}),L(Si,{description:`LAN ARP에서 확인된 비무선 기기`,icon:L(Qe,{class:`size-6`}),label:`유선/기타`,value:e.totals.ethernet})]}),L(`section`,{class:`mt-5 rounded-2xl border border-slate-200 bg-white p-4 shadow-sm shadow-slate-900/5 sm:p-5`,children:[L(`div`,{class:`flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between`,children:[L(`div`,{children:[L(`p`,{class:`m-0 text-xs font-bold uppercase tracking-[0.16em] text-slate-500`,children:`Device inventory`}),L(`h2`,{class:`mt-2 mb-0 text-xl font-extrabold text-slate-950`,children:`기기 목록`})]}),L(`label`,{class:`relative block w-full lg:max-w-sm`,children:[L(`span`,{class:`sr-only`,children:`기기 검색`}),L(`span`,{class:`pointer-events-none absolute inset-y-0 left-0 flex w-11 items-center justify-center text-slate-400`,children:L(ft,{class:`size-5`})}),L(`input`,{class:`min-h-11 w-full rounded-xl border-2 border-slate-300 bg-slate-50 py-2.5 pr-4 pl-11 text-sm font-semibold text-slate-950 shadow-inner outline-none transition placeholder:text-slate-400 focus:border-teal-500 focus:bg-white focus:ring-4 focus:ring-teal-100`,onInput:e=>a(e.currentTarget.value),placeholder:`이름, IP, MAC 또는 SSID 검색`,type:`search`,value:i})]})]}),L(`div`,{class:`ssh-touch-scroll mt-4 flex gap-2 overflow-x-auto pb-1`,role:`group`,"aria-label":`기기 필터`,children:ui.map(e=>L(`button`,{"aria-pressed":o===e.value,class:`min-h-10 shrink-0 rounded-xl border px-4 py-2 text-sm font-extrabold transition ${o===e.value?`border-teal-700 bg-teal-700 text-white`:`border-slate-200 bg-white text-slate-600 hover:bg-slate-50 hover:text-slate-950`}`,onClick:()=>s(e.value),type:`button`,children:e.label},e.value))})]}),c.length?L(`section`,{"aria-live":`polite`,class:`mt-5 grid gap-4 lg:grid-cols-2`,children:c.map(e=>L(bi,{device:e},e.id))}):L(`section`,{class:`mt-5 rounded-2xl border border-dashed border-slate-300 bg-white p-5 text-center sm:p-8`,children:[L(Ge,{class:`mx-auto size-10 text-slate-400`}),L(`h2`,{class:`mt-4 mb-0 text-lg font-extrabold text-slate-950`,children:`조건에 맞는 기기가 없습니다`}),L(`p`,{class:`mt-2 mb-0 text-sm text-slate-600`,children:`검색어 또는 필터를 변경해 주세요.`})]}),L(`p`,{class:`mt-5 mb-0 rounded-xl bg-slate-100 px-4 py-3 text-xs leading-5 text-slate-600`,children:`유선/기타 상태는 ARP 테이블을 기준으로 하며, 최근 DHCP 임대는 기기가 연결 해제된 뒤에도 일정 시간 남을 수 있습니다.`})]}):null}function Si({description:e,icon:t,label:n,value:r}){return L(`article`,{class:`rounded-2xl border border-slate-200 bg-white p-4 shadow-sm shadow-slate-900/5 sm:p-5`,children:[L(`div`,{class:`flex items-start justify-between gap-4`,children:[L(`div`,{children:[L(`p`,{class:`m-0 text-xs font-bold uppercase tracking-[0.14em] text-slate-500`,children:n}),L(`p`,{class:`mt-3 mb-0 text-3xl font-black tracking-tight text-slate-950`,children:r})]}),L(`span`,{class:`grid size-11 shrink-0 place-items-center rounded-xl bg-teal-50 text-teal-700`,children:t})]}),L(`p`,{class:`mt-3 mb-0 text-xs leading-5 text-slate-500`,children:e})]})}function Ci(e){return e+.5|0}var wi=(e,t,n)=>Math.max(Math.min(e,n),t);function Ti(e){return wi(Ci(e*2.55),0,255)}function Ei(e){return wi(Ci(e*255),0,255)}function Di(e){return wi(Ci(e/2.55)/100,0,1)}function Oi(e){return wi(Ci(e*100),0,100)}var ki={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,A:10,B:11,C:12,D:13,E:14,F:15,a:10,b:11,c:12,d:13,e:14,f:15},Ai=[...`0123456789ABCDEF`],ji=e=>Ai[e&15],Mi=e=>Ai[(e&240)>>4]+Ai[e&15],Ni=e=>(e&240)>>4==(e&15),Pi=e=>Ni(e.r)&&Ni(e.g)&&Ni(e.b)&&Ni(e.a);function Fi(e){var t=e.length,n;return e[0]===`#`&&(t===4||t===5?n={r:255&ki[e[1]]*17,g:255&ki[e[2]]*17,b:255&ki[e[3]]*17,a:t===5?ki[e[4]]*17:255}:(t===7||t===9)&&(n={r:ki[e[1]]<<4|ki[e[2]],g:ki[e[3]]<<4|ki[e[4]],b:ki[e[5]]<<4|ki[e[6]],a:t===9?ki[e[7]]<<4|ki[e[8]]:255})),n}var Ii=(e,t)=>e<255?t(e):``;function Li(e){var t=Pi(e)?ji:Mi;return e?`#`+t(e.r)+t(e.g)+t(e.b)+Ii(e.a,t):void 0}var Ri=/^(hsla?|hwb|hsv)\(\s*([-+.e\d]+)(?:deg)?[\s,]+([-+.e\d]+)%[\s,]+([-+.e\d]+)%(?:[\s,]+([-+.e\d]+)(%)?)?\s*\)$/;function zi(e,t,n){let r=t*Math.min(n,1-n),i=(t,i=(t+e/30)%12)=>n-r*Math.max(Math.min(i-3,9-i,1),-1);return[i(0),i(8),i(4)]}function Bi(e,t,n){let r=(r,i=(r+e/60)%6)=>n-n*t*Math.max(Math.min(i,4-i,1),0);return[r(5),r(3),r(1)]}function Vi(e,t,n){let r=zi(e,1,.5),i;for(t+n>1&&(i=1/(t+n),t*=i,n*=i),i=0;i<3;i++)r[i]*=1-t-n,r[i]+=t;return r}function Hi(e,t,n,r,i){return e===i?(t-n)/r+(t.5?l/(2-i-a):l/(i+a),s=Hi(t,n,r,l,i),s=s*60+.5),[s|0,c||0,o]}function Wi(e,t,n,r){return(Array.isArray(t)?e(t[0],t[1],t[2]):e(t,n,r)).map(Ei)}function Gi(e,t,n){return Wi(zi,e,t,n)}function Ki(e,t,n){return Wi(Vi,e,t,n)}function qi(e,t,n){return Wi(Bi,e,t,n)}function Ji(e){return(e%360+360)%360}function Yi(e){let t=Ri.exec(e),n=255,r;if(!t)return;t[5]!==r&&(n=t[6]?Ti(+t[5]):Ei(+t[5]));let i=Ji(+t[2]),a=t[3]/100,o=t[4]/100;return r=t[1]===`hwb`?Ki(i,a,o):t[1]===`hsv`?qi(i,a,o):Gi(i,a,o),{r:r[0],g:r[1],b:r[2],a:n}}function Xi(e,t){var n=Ui(e);n[0]=Ji(n[0]+t),n=Gi(n),e.r=n[0],e.g=n[1],e.b=n[2]}function Zi(e){if(!e)return;let t=Ui(e),n=t[0],r=Oi(t[1]),i=Oi(t[2]);return e.a<255?`hsla(${n}, ${r}%, ${i}%, ${Di(e.a)})`:`hsl(${n}, ${r}%, ${i}%)`}var Qi={x:`dark`,Z:`light`,Y:`re`,X:`blu`,W:`gr`,V:`medium`,U:`slate`,A:`ee`,T:`ol`,S:`or`,B:`ra`,C:`lateg`,D:`ights`,R:`in`,Q:`turquois`,E:`hi`,P:`ro`,O:`al`,N:`le`,M:`de`,L:`yello`,F:`en`,K:`ch`,G:`arks`,H:`ea`,I:`ightg`,J:`wh`},$i={OiceXe:`f0f8ff`,antiquewEte:`faebd7`,aqua:`ffff`,aquamarRe:`7fffd4`,azuY:`f0ffff`,beige:`f5f5dc`,bisque:`ffe4c4`,black:`0`,blanKedOmond:`ffebcd`,Xe:`ff`,XeviTet:`8a2be2`,bPwn:`a52a2a`,burlywood:`deb887`,caMtXe:`5f9ea0`,KartYuse:`7fff00`,KocTate:`d2691e`,cSO:`ff7f50`,cSnflowerXe:`6495ed`,cSnsilk:`fff8dc`,crimson:`dc143c`,cyan:`ffff`,xXe:`8b`,xcyan:`8b8b`,xgTMnPd:`b8860b`,xWay:`a9a9a9`,xgYF:`6400`,xgYy:`a9a9a9`,xkhaki:`bdb76b`,xmagFta:`8b008b`,xTivegYF:`556b2f`,xSange:`ff8c00`,xScEd:`9932cc`,xYd:`8b0000`,xsOmon:`e9967a`,xsHgYF:`8fbc8f`,xUXe:`483d8b`,xUWay:`2f4f4f`,xUgYy:`2f4f4f`,xQe:`ced1`,xviTet:`9400d3`,dAppRk:`ff1493`,dApskyXe:`bfff`,dimWay:`696969`,dimgYy:`696969`,dodgerXe:`1e90ff`,fiYbrick:`b22222`,flSOwEte:`fffaf0`,foYstWAn:`228b22`,fuKsia:`ff00ff`,gaRsbSo:`dcdcdc`,ghostwEte:`f8f8ff`,gTd:`ffd700`,gTMnPd:`daa520`,Way:`808080`,gYF:`8000`,gYFLw:`adff2f`,gYy:`808080`,honeyMw:`f0fff0`,hotpRk:`ff69b4`,RdianYd:`cd5c5c`,Rdigo:`4b0082`,ivSy:`fffff0`,khaki:`f0e68c`,lavFMr:`e6e6fa`,lavFMrXsh:`fff0f5`,lawngYF:`7cfc00`,NmoncEffon:`fffacd`,ZXe:`add8e6`,ZcSO:`f08080`,Zcyan:`e0ffff`,ZgTMnPdLw:`fafad2`,ZWay:`d3d3d3`,ZgYF:`90ee90`,ZgYy:`d3d3d3`,ZpRk:`ffb6c1`,ZsOmon:`ffa07a`,ZsHgYF:`20b2aa`,ZskyXe:`87cefa`,ZUWay:`778899`,ZUgYy:`778899`,ZstAlXe:`b0c4de`,ZLw:`ffffe0`,lime:`ff00`,limegYF:`32cd32`,lRF:`faf0e6`,magFta:`ff00ff`,maPon:`800000`,VaquamarRe:`66cdaa`,VXe:`cd`,VScEd:`ba55d3`,VpurpN:`9370db`,VsHgYF:`3cb371`,VUXe:`7b68ee`,VsprRggYF:`fa9a`,VQe:`48d1cc`,VviTetYd:`c71585`,midnightXe:`191970`,mRtcYam:`f5fffa`,mistyPse:`ffe4e1`,moccasR:`ffe4b5`,navajowEte:`ffdead`,navy:`80`,Tdlace:`fdf5e6`,Tive:`808000`,TivedBb:`6b8e23`,Sange:`ffa500`,SangeYd:`ff4500`,ScEd:`da70d6`,pOegTMnPd:`eee8aa`,pOegYF:`98fb98`,pOeQe:`afeeee`,pOeviTetYd:`db7093`,papayawEp:`ffefd5`,pHKpuff:`ffdab9`,peru:`cd853f`,pRk:`ffc0cb`,plum:`dda0dd`,powMrXe:`b0e0e6`,purpN:`800080`,YbeccapurpN:`663399`,Yd:`ff0000`,Psybrown:`bc8f8f`,PyOXe:`4169e1`,saddNbPwn:`8b4513`,sOmon:`fa8072`,sandybPwn:`f4a460`,sHgYF:`2e8b57`,sHshell:`fff5ee`,siFna:`a0522d`,silver:`c0c0c0`,skyXe:`87ceeb`,UXe:`6a5acd`,UWay:`708090`,UgYy:`708090`,snow:`fffafa`,sprRggYF:`ff7f`,stAlXe:`4682b4`,tan:`d2b48c`,teO:`8080`,tEstN:`d8bfd8`,tomato:`ff6347`,Qe:`40e0d0`,viTet:`ee82ee`,JHt:`f5deb3`,wEte:`ffffff`,wEtesmoke:`f5f5f5`,Lw:`ffff00`,LwgYF:`9acd32`};function ea(){let e={},t=Object.keys($i),n=Object.keys(Qi),r,i,a,o,s;for(r=0;r>16&255,a>>8&255,a&255]}return e}var ta;function na(e){ta||(ta=ea(),ta.transparent=[0,0,0,0]);let t=ta[e.toLowerCase()];return t&&{r:t[0],g:t[1],b:t[2],a:t.length===4?t[3]:255}}var ra=/^rgba?\(\s*([-+.\d]+)(%)?[\s,]+([-+.e\d]+)(%)?[\s,]+([-+.e\d]+)(%)?(?:[\s,/]+([-+.e\d]+)(%)?)?\s*\)$/;function ia(e){let t=ra.exec(e),n=255,r,i,a;if(t){if(t[7]!==r){let e=+t[7];n=t[8]?Ti(e):wi(e*255,0,255)}return r=+t[1],i=+t[3],a=+t[5],r=255&(t[2]?Ti(r):wi(r,0,255)),i=255&(t[4]?Ti(i):wi(i,0,255)),a=255&(t[6]?Ti(a):wi(a,0,255)),{r,g:i,b:a,a:n}}}function aa(e){return e&&(e.a<255?`rgba(${e.r}, ${e.g}, ${e.b}, ${Di(e.a)})`:`rgb(${e.r}, ${e.g}, ${e.b})`)}var oa=e=>e<=.0031308?e*12.92:e**(1/2.4)*1.055-.055,sa=e=>e<=.04045?e/12.92:((e+.055)/1.055)**2.4;function ca(e,t,n){let r=sa(Di(e.r)),i=sa(Di(e.g)),a=sa(Di(e.b));return{r:Ei(oa(r+n*(sa(Di(t.r))-r))),g:Ei(oa(i+n*(sa(Di(t.g))-i))),b:Ei(oa(a+n*(sa(Di(t.b))-a))),a:e.a+n*(t.a-e.a)}}function la(e,t,n){if(e){let r=Ui(e);r[t]=Math.max(0,Math.min(r[t]+r[t]*n,t===0?360:1)),r=Gi(r),e.r=r[0],e.g=r[1],e.b=r[2]}}function ua(e,t){return e&&Object.assign(t||{},e)}function da(e){var t={r:0,g:0,b:0,a:255};return Array.isArray(e)?e.length>=3&&(t={r:e[0],g:e[1],b:e[2],a:255},e.length>3&&(t.a=Ei(e[3]))):(t=ua(e,{r:0,g:0,b:0,a:1}),t.a=Ei(t.a)),t}function fa(e){return e.charAt(0)===`r`?ia(e):Yi(e)}var pa=class e{constructor(t){if(t instanceof e)return t;let n=typeof t,r;n===`object`?r=da(t):n===`string`&&(r=Fi(t)||na(t)||fa(t)),this._rgb=r,this._valid=!!r}get valid(){return this._valid}get rgb(){var e=ua(this._rgb);return e&&(e.a=Di(e.a)),e}set rgb(e){this._rgb=da(e)}rgbString(){return this._valid?aa(this._rgb):void 0}hexString(){return this._valid?Li(this._rgb):void 0}hslString(){return this._valid?Zi(this._rgb):void 0}mix(e,t){if(e){let n=this.rgb,r=e.rgb,i,a=t===i?.5:t,o=2*a-1,s=n.a-r.a,c=((o*s===-1?o:(o+s)/(1+o*s))+1)/2;i=1-c,n.r=255&c*n.r+i*r.r+.5,n.g=255&c*n.g+i*r.g+.5,n.b=255&c*n.b+i*r.b+.5,n.a=a*n.a+(1-a)*r.a,this.rgb=n}return this}interpolate(e,t){return e&&(this._rgb=ca(this._rgb,e._rgb,t)),this}clone(){return new e(this.rgb)}alpha(e){return this._rgb.a=Ei(e),this}clearer(e){let t=this._rgb;return t.a*=1-e,this}greyscale(){let e=this._rgb;return e.r=e.g=e.b=Ci(e.r*.3+e.g*.59+e.b*.11),this}opaquer(e){let t=this._rgb;return t.a*=1+e,this}negate(){let e=this._rgb;return e.r=255-e.r,e.g=255-e.g,e.b=255-e.b,this}lighten(e){return la(this._rgb,2,e),this}darken(e){return la(this._rgb,2,-e),this}saturate(e){return la(this._rgb,1,e),this}desaturate(e){return la(this._rgb,1,-e),this}rotate(e){return Xi(this._rgb,e),this}};function ma(){}var ha=(()=>{let e=0;return()=>e++})();function U(e){return e==null}function W(e){if(Array.isArray&&Array.isArray(e))return!0;let t=Object.prototype.toString.call(e);return t.slice(0,7)===`[object`&&t.slice(-6)===`Array]`}function G(e){return e!==null&&Object.prototype.toString.call(e)===`[object Object]`}function ga(e){return(typeof e==`number`||e instanceof Number)&&isFinite(+e)}function _a(e,t){return ga(e)?e:t}function K(e,t){return e===void 0?t:e}var va=(e,t)=>typeof e==`string`&&e.endsWith(`%`)?parseFloat(e)/100:+e/t,ya=(e,t)=>typeof e==`string`&&e.endsWith(`%`)?parseFloat(e)/100*t:+e;function q(e,t,n){if(e&&typeof e.call==`function`)return e.apply(n,t)}function J(e,t,n,r){let i,a,o;if(W(e)){if(a=e.length,r)for(i=a-1;i>=0;i--)t.call(n,e[i],i);else for(i=0;ie,x:e=>e.x,y:e=>e.y};function Oa(e){let t=e.split(`.`),n=[],r=``;for(let e of t)r+=e,r.endsWith(`\\`)?r=r.slice(0,-1)+`.`:(n.push(r),r=``);return n}function ka(e){let t=Oa(e);return e=>{for(let n of t){if(n===``)break;e=e&&e[n]}return e}}function Aa(e,t){return(Da[t]||(Da[t]=ka(t)))(e)}function ja(e){return e.charAt(0).toUpperCase()+e.slice(1)}var Ma=e=>e!==void 0,Na=e=>typeof e==`function`,Pa=(e,t)=>{if(e.size!==t.size)return!1;for(let n of e)if(!t.has(n))return!1;return!0};function Fa(e){return e.type===`mouseup`||e.type===`click`||e.type===`contextmenu`}var Y=Math.PI,X=2*Y,Ia=X+Y,La=1/0,Ra=Y/180,Z=Y/2,za=Y/4,Ba=Y*2/3,Va=Math.log10,Ha=Math.sign;function Ua(e,t,n){return Math.abs(e-t)e-t).pop(),t}function Ka(e){return typeof e==`symbol`||typeof e==`object`&&!!e&&!(Symbol.toPrimitive in e||`toString`in e||`valueOf`in e)}function qa(e){return!Ka(e)&&!isNaN(parseFloat(e))&&isFinite(e)}function Ja(e,t){let n=Math.round(e);return n-t<=e&&n+t>=e}function Ya(e,t,n){let r,i,a;for(r=0,i=e.length;rc&&l=Math.min(t,n)-r&&e<=Math.max(t,n)+r}function so(e,t,n){n=n||(n=>e[n]1;)a=i+r>>1,n(a)?i=a:r=a;return{lo:i,hi:r}}var co=(e,t,n,r)=>so(e,n,r?r=>{let i=e[r][t];return ie[r][t]so(e,n,r=>e[r][t]>=n);function uo(e,t,n){let r=0,i=e.length;for(;rr&&e[i-1]>n;)i--;return r>0||i{let n=`_onData`+ja(t),r=e[t];Object.defineProperty(e,t,{configurable:!0,enumerable:!1,value(...t){let i=r.apply(this,t);return e._chartjs.listeners.forEach(e=>{typeof e[n]==`function`&&e[n](...t)}),i}})}))}function mo(e,t){let n=e._chartjs;if(!n)return;let r=n.listeners,i=r.indexOf(t);i!==-1&&r.splice(i,1),!(r.length>0)&&(fo.forEach(t=>{delete e[t]}),delete e._chartjs)}function ho(e){let t=new Set(e);return t.size===e.length?e:Array.from(t)}var go=function(){return typeof window>`u`?function(e){return e()}:window.requestAnimationFrame}();function _o(e,t){let n=[],r=!1;return function(...i){n=i,r||(r=!0,go.call(window,()=>{r=!1,e.apply(t,n)}))}}function vo(e,t){let n;return function(...r){return t?(clearTimeout(n),n=setTimeout(e,t,r)):e.apply(this,r),t}}var yo=e=>e===`start`?`left`:e===`end`?`right`:`center`,bo=(e,t,n)=>e===`start`?t:e===`end`?n:(t+n)/2;function xo(e,t,n){let r=t.length,i=0,a=r;if(e._sorted){let{iScale:o,vScale:s,_parsed:c}=e,l=e.dataset&&e.dataset.options?e.dataset.options.spanGaps:null,u=o.axis,{min:d,max:f,minDefined:p,maxDefined:m}=o.getUserBounds();if(p){if(i=Math.min(co(c,u,d).lo,n?r:co(t,u,o.getPixelForValue(d)).lo),l){let e=c.slice(0,i+1).reverse().findIndex(e=>!U(e[s.axis]));i-=Math.max(0,e)}i=io(i,0,r-1)}if(m){let e=Math.max(co(c,o.axis,f,!0).hi+1,n?0:co(t,u,o.getPixelForValue(f),!0).hi+1);if(l){let t=c.slice(e-1).findIndex(e=>!U(e[s.axis]));e+=Math.max(0,t)}a=io(e,i,r)-i}else a=r-i}return{start:i,count:a}}function So(e){let{xScale:t,yScale:n,_scaleRanges:r}=e,i={xmin:t.min,xmax:t.max,ymin:n.min,ymax:n.max};if(!r)return e._scaleRanges=i,!0;let a=r.xmin!==t.min||r.xmax!==t.max||r.ymin!==n.min||r.ymax!==n.max;return Object.assign(r,i),a}var Co=e=>e===0||e===1,wo=(e,t,n)=>-(2**(10*--e)*Math.sin((e-t)*X/n)),To=(e,t,n)=>2**(-10*e)*Math.sin((e-t)*X/n)+1,Eo={linear:e=>e,easeInQuad:e=>e*e,easeOutQuad:e=>-e*(e-2),easeInOutQuad:e=>(e/=.5)<1?.5*e*e:-.5*(--e*(e-2)-1),easeInCubic:e=>e*e*e,easeOutCubic:e=>--e*e*e+1,easeInOutCubic:e=>(e/=.5)<1?.5*e*e*e:.5*((e-=2)*e*e+2),easeInQuart:e=>e*e*e*e,easeOutQuart:e=>-(--e*e*e*e-1),easeInOutQuart:e=>(e/=.5)<1?.5*e*e*e*e:-.5*((e-=2)*e*e*e-2),easeInQuint:e=>e*e*e*e*e,easeOutQuint:e=>--e*e*e*e*e+1,easeInOutQuint:e=>(e/=.5)<1?.5*e*e*e*e*e:.5*((e-=2)*e*e*e*e+2),easeInSine:e=>-Math.cos(e*Z)+1,easeOutSine:e=>Math.sin(e*Z),easeInOutSine:e=>-.5*(Math.cos(Y*e)-1),easeInExpo:e=>e===0?0:2**(10*(e-1)),easeOutExpo:e=>e===1?1:-(2**(-10*e))+1,easeInOutExpo:e=>Co(e)?e:e<.5?.5*2**(10*(e*2-1)):.5*(-(2**(-10*(e*2-1)))+2),easeInCirc:e=>e>=1?e:-(Math.sqrt(1-e*e)-1),easeOutCirc:e=>Math.sqrt(1- --e*e),easeInOutCirc:e=>(e/=.5)<1?-.5*(Math.sqrt(1-e*e)-1):.5*(Math.sqrt(1-(e-=2)*e)+1),easeInElastic:e=>Co(e)?e:wo(e,.075,.3),easeOutElastic:e=>Co(e)?e:To(e,.075,.3),easeInOutElastic(e){let t=.1125,n=.45;return Co(e)?e:e<.5?.5*wo(e*2,t,n):.5+.5*To(e*2-1,t,n)},easeInBack(e){return e*e*(2.70158*e-1.70158)},easeOutBack(e){return--e*e*(2.70158*e+1.70158)+1},easeInOutBack(e){let t=1.70158;return(e/=.5)<1?.5*(e*e*(((t*=1.525)+1)*e-t)):.5*((e-=2)*e*(((t*=1.525)+1)*e+t)+2)},easeInBounce:e=>1-Eo.easeOutBounce(1-e),easeOutBounce(e){let t=7.5625,n=2.75;return e<1/n?t*e*e:e<2/n?t*(e-=1.5/n)*e+.75:e<2.5/n?t*(e-=2.25/n)*e+.9375:t*(e-=2.625/n)*e+.984375},easeInOutBounce:e=>e<.5?Eo.easeInBounce(e*2)*.5:Eo.easeOutBounce(e*2-1)*.5+.5};function Do(e){if(e&&typeof e==`object`){let t=e.toString();return t===`[object CanvasPattern]`||t===`[object CanvasGradient]`}return!1}function Oo(e){return Do(e)?e:new pa(e)}function ko(e){return Do(e)?e:new pa(e).saturate(.5).darken(.1).hexString()}var Ao=[`x`,`y`,`borderWidth`,`radius`,`tension`],jo=[`color`,`borderColor`,`backgroundColor`];function Mo(e){e.set(`animation`,{delay:void 0,duration:1e3,easing:`easeOutQuart`,fn:void 0,from:void 0,loop:void 0,to:void 0,type:void 0}),e.describe(`animation`,{_fallback:!1,_indexable:!1,_scriptable:e=>e!==`onProgress`&&e!==`onComplete`&&e!==`fn`}),e.set(`animations`,{colors:{type:`color`,properties:jo},numbers:{type:`number`,properties:Ao}}),e.describe(`animations`,{_fallback:`animation`}),e.set(`transitions`,{active:{animation:{duration:400}},resize:{animation:{duration:0}},show:{animations:{colors:{from:`transparent`},visible:{type:`boolean`,duration:0}}},hide:{animations:{colors:{to:`transparent`},visible:{type:`boolean`,easing:`linear`,fn:e=>e|0}}}})}function No(e){e.set(`layout`,{autoPadding:!0,padding:{top:0,right:0,bottom:0,left:0}})}var Po=new Map;function Fo(e,t){t=t||{};let n=e+JSON.stringify(t),r=Po.get(n);return r||(r=new Intl.NumberFormat(e,t),Po.set(n,r)),r}function Io(e,t,n){return Fo(t,n).format(e)}var Lo={values(e){return W(e)?e:``+e},numeric(e,t,n){if(e===0)return`0`;let r=this.chart.options.locale,i,a=e;if(n.length>1){let t=Math.max(Math.abs(n[0].value),Math.abs(n[n.length-1].value));(t<1e-4||t>0x38d7ea4c68000)&&(i=`scientific`),a=Ro(e,n)}let o=Va(Math.abs(a)),s=isNaN(o)?1:Math.max(Math.min(-1*Math.floor(o),20),0),c={notation:i,minimumFractionDigits:s,maximumFractionDigits:s};return Object.assign(c,this.options.ticks.format),Io(e,r,c)},logarithmic(e,t,n){if(e===0)return`0`;let r=n[t].significand||e/10**Math.floor(Va(e));return[1,2,3,5,10,15].includes(r)||t>.8*n.length?Lo.numeric.call(this,e,t,n):``}};function Ro(e,t){let n=t.length>3?t[2].value-t[1].value:t[1].value-t[0].value;return Math.abs(n)>=1&&e!==Math.floor(e)&&(n=e-Math.floor(e)),n}var zo={formatters:Lo};function Bo(e){e.set(`scale`,{display:!0,offset:!1,reverse:!1,beginAtZero:!1,bounds:`ticks`,clip:!0,grace:0,grid:{display:!0,lineWidth:1,drawOnChartArea:!0,drawTicks:!0,tickLength:8,tickWidth:(e,t)=>t.lineWidth,tickColor:(e,t)=>t.color,offset:!1},border:{display:!0,dash:[],dashOffset:0,width:1},title:{display:!1,text:``,padding:{top:4,bottom:4}},ticks:{minRotation:0,maxRotation:50,mirror:!1,textStrokeWidth:0,textStrokeColor:``,padding:3,display:!0,autoSkip:!0,autoSkipPadding:3,labelOffset:0,callback:zo.formatters.values,minor:{},major:{},align:`center`,crossAlign:`near`,showLabelBackdrop:!1,backdropColor:`rgba(255, 255, 255, 0.75)`,backdropPadding:2}}),e.route(`scale.ticks`,`color`,``,`color`),e.route(`scale.grid`,`color`,``,`borderColor`),e.route(`scale.border`,`color`,``,`borderColor`),e.route(`scale.title`,`color`,``,`color`),e.describe(`scale`,{_fallback:!1,_scriptable:e=>!e.startsWith(`before`)&&!e.startsWith(`after`)&&e!==`callback`&&e!==`parser`,_indexable:e=>e!==`borderDash`&&e!==`tickBorderDash`&&e!==`dash`}),e.describe(`scales`,{_fallback:`scale`}),e.describe(`scale.ticks`,{_scriptable:e=>e!==`backdropPadding`&&e!==`callback`,_indexable:e=>e!==`backdropPadding`})}var Vo=Object.create(null),Ho=Object.create(null);function Uo(e,t){if(!t)return e;let n=t.split(`.`);for(let t=0,r=n.length;te.chart.platform.getDevicePixelRatio(),this.elements={},this.events=[`mousemove`,`mouseout`,`click`,`touchstart`,`touchmove`],this.font={family:`'Helvetica Neue', 'Helvetica', 'Arial', sans-serif`,size:12,style:`normal`,lineHeight:1.2,weight:null},this.hover={},this.hoverBackgroundColor=(e,t)=>ko(t.backgroundColor),this.hoverBorderColor=(e,t)=>ko(t.borderColor),this.hoverColor=(e,t)=>ko(t.color),this.indexAxis=`x`,this.interaction={mode:`nearest`,intersect:!0,includeInvisible:!1},this.maintainAspectRatio=!0,this.onHover=null,this.onClick=null,this.parsing=!0,this.plugins={},this.responsive=!0,this.scale=void 0,this.scales={},this.showLine=!0,this.drawActiveElementsOnTop=!0,this.describe(e),this.apply(t)}set(e,t){return Wo(this,e,t)}get(e){return Uo(this,e)}describe(e,t){return Wo(Ho,e,t)}override(e,t){return Wo(Vo,e,t)}route(e,t,n,r){let i=Uo(this,e),a=Uo(this,n),o=`_`+t;Object.defineProperties(i,{[o]:{value:i[t],writable:!0},[t]:{enumerable:!0,get(){let e=this[o],t=a[r];return G(e)?Object.assign({},t,e):K(e,t)},set(e){this[o]=e}}})}apply(e){e.forEach(e=>e(this))}}({_scriptable:e=>!e.startsWith(`on`),_indexable:e=>e!==`events`,hover:{_fallback:`interaction`},interaction:{_scriptable:!1,_indexable:!1}},[Mo,No,Bo]);function Go(e){return!e||U(e.size)||U(e.family)?null:(e.style?e.style+` `:``)+(e.weight?e.weight+` `:``)+e.size+`px `+e.family}function Ko(e,t,n,r,i){let a=t[i];return a||(a=t[i]=e.measureText(i).width,n.push(i)),a>r&&(r=a),r}function qo(e,t,n,r){r=r||{};let i=r.data=r.data||{},a=r.garbageCollect=r.garbageCollect||[];r.font!==t&&(i=r.data={},a=r.garbageCollect=[],r.font=t),e.save(),e.font=t;let o=0,s=n.length,c,l,u,d,f;for(c=0;cn.length){for(c=0;c0&&e.stroke()}}function Qo(e,t,n){return n=n||.5,!t||e&&e.x>t.left-n&&e.xt.top-n&&e.y0&&a.strokeColor!==``,c,l;for(e.save(),e.font=i.string,rs(e,a),c=0;c+e||0;function fs(e,t){let n={},r=G(t),i=r?Object.keys(t):t,a=G(e)?r?n=>K(e[n],e[t[n]]):t=>e[t]:()=>e;for(let e of i)n[e]=ds(a(e));return n}function ps(e){return fs(e,{top:`y`,right:`x`,bottom:`y`,left:`x`})}function ms(e){return fs(e,[`topLeft`,`topRight`,`bottomLeft`,`bottomRight`])}function hs(e){let t=ps(e);return t.width=t.left+t.right,t.height=t.top+t.bottom,t}function gs(e,t){e=e||{},t=t||Q.font;let n=K(e.size,t.size);typeof n==`string`&&(n=parseInt(n,10));let r=K(e.style,t.style);r&&!(``+r).match(ls)&&(console.warn(`Invalid font style specified: "`+r+`"`),r=void 0);let i={family:K(e.family,t.family),lineHeight:us(K(e.lineHeight,t.lineHeight),n),size:n,style:r,weight:K(e.weight,t.weight),string:``};return i.string=Go(i),i}function _s(e,t,n,r){let i=!0,a,o,s;for(a=0,o=e.length;an&&e===0?0:e+t;return{min:o(r,-Math.abs(a)),max:o(i,a)}}function ys(e,t){return Object.assign(Object.create(e),t)}function bs(e,t=[``],n,r,i=()=>e[0]){let a=n||e;return r===void 0&&(r=Is(`_fallback`,e)),new Proxy({[Symbol.toStringTag]:`Object`,_cacheable:!0,_scopes:e,_rootScopes:a,_fallback:r,_getTarget:i,override:n=>bs([n,...e],t,a,r)},{deleteProperty(t,n){return delete t[n],delete t._keys,delete e[0][n],!0},get(n,r){return Ts(n,r,()=>Fs(r,t,e,n))},getOwnPropertyDescriptor(e,t){return Reflect.getOwnPropertyDescriptor(e._scopes[0],t)},getPrototypeOf(){return Reflect.getPrototypeOf(e[0])},has(e,t){return Ls(e).includes(t)},ownKeys(e){return Ls(e)},set(e,t,n){let r=e._storage||(e._storage=i());return e[t]=r[t]=n,delete e._keys,!0}})}function xs(e,t,n,r){let i={_cacheable:!1,_proxy:e,_context:t,_subProxy:n,_stack:new Set,_descriptors:Ss(e,r),setContext:t=>xs(e,t,n,r),override:i=>xs(e.override(i),t,n,r)};return new Proxy(i,{deleteProperty(t,n){return delete t[n],delete e[n],!0},get(e,t,n){return Ts(e,t,()=>Es(e,t,n))},getOwnPropertyDescriptor(t,n){return t._descriptors.allKeys?Reflect.has(e,n)?{enumerable:!0,configurable:!0}:void 0:Reflect.getOwnPropertyDescriptor(e,n)},getPrototypeOf(){return Reflect.getPrototypeOf(e)},has(t,n){return Reflect.has(e,n)},ownKeys(){return Reflect.ownKeys(e)},set(t,n,r){return e[n]=r,delete t[n],!0}})}function Ss(e,t={scriptable:!0,indexable:!0}){let{_scriptable:n=t.scriptable,_indexable:r=t.indexable,_allKeys:i=t.allKeys}=e;return{allKeys:i,scriptable:n,indexable:r,isScriptable:Na(n)?n:()=>n,isIndexable:Na(r)?r:()=>r}}var Cs=(e,t)=>e?e+ja(t):t,ws=(e,t)=>G(t)&&e!==`adapters`&&(Object.getPrototypeOf(t)===null||t.constructor===Object);function Ts(e,t,n){if(Object.prototype.hasOwnProperty.call(e,t)||t===`constructor`)return e[t];let r=n();return e[t]=r,r}function Es(e,t,n){let{_proxy:r,_context:i,_subProxy:a,_descriptors:o}=e,s=r[t];return Na(s)&&o.isScriptable(t)&&(s=Ds(t,s,e,n)),W(s)&&s.length&&(s=Os(t,s,e,o.isIndexable)),ws(t,s)&&(s=xs(s,i,a&&a[t],o)),s}function Ds(e,t,n,r){let{_proxy:i,_context:a,_subProxy:o,_stack:s}=n;if(s.has(e))throw Error(`Recursion detected: `+Array.from(s).join(`->`)+`->`+e);s.add(e);let c=t(a,o||r);return s.delete(e),ws(e,c)&&(c=Ms(i._scopes,i,e,c)),c}function Os(e,t,n,r){let{_proxy:i,_context:a,_subProxy:o,_descriptors:s}=n;if(a.index!==void 0&&r(e))return t[a.index%t.length];if(G(t[0])){let n=t,r=i._scopes.filter(e=>e!==n);t=[];for(let c of n){let n=Ms(r,i,e,c);t.push(xs(n,a,o&&o[e],s))}}return t}function ks(e,t,n){return Na(e)?e(t,n):e}var As=(e,t)=>e===!0?t:typeof e==`string`?Aa(t,e):void 0;function js(e,t,n,r,i){for(let a of t){let t=As(n,a);if(t){e.add(t);let a=ks(t._fallback,n,i);if(a!==void 0&&a!==n&&a!==r)return a}else if(t===!1&&r!==void 0&&n!==r)return null}return!1}function Ms(e,t,n,r){let i=t._rootScopes,a=ks(t._fallback,n,r),o=[...e,...i],s=new Set;s.add(r);let c=Ns(s,o,n,a||n,r);return c===null||a!==void 0&&a!==n&&(c=Ns(s,o,a,c,r),c===null)?!1:bs(Array.from(s),[``],i,a,()=>Ps(t,n,r))}function Ns(e,t,n,r,i){for(;n;)n=js(e,t,n,r,i);return n}function Ps(e,t,n){let r=e._getTarget();t in r||(r[t]={});let i=r[t];return W(i)&&G(n)?n:i||{}}function Fs(e,t,n,r){let i;for(let a of t)if(i=Is(Cs(a,e),n),i!==void 0)return ws(e,i)?Ms(n,r,e,i):i}function Is(e,t){for(let n of t){if(!n)continue;let t=n[e];if(t!==void 0)return t}}function Ls(e){let t=e._keys;return t||(t=e._keys=Rs(e._scopes)),t}function Rs(e){let t=new Set;for(let n of e)for(let e of Object.keys(n).filter(e=>!e.startsWith(`_`)))t.add(e);return Array.from(t)}function zs(e,t,n,r){let{iScale:i}=e,{key:a=`r`}=this._parsing,o=Array(r),s,c,l,u;for(s=0,c=r;ste===`x`?`y`:`x`;function Us(e,t,n,r){let i=e.skip?t:e,a=t,o=n.skip?t:n,s=eo(a,i),c=eo(o,a),l=s/(s+c),u=c/(s+c);l=isNaN(l)?0:l,u=isNaN(u)?0:u;let d=r*l,f=r*u;return{previous:{x:a.x-d*(o.x-i.x),y:a.y-d*(o.y-i.y)},next:{x:a.x+f*(o.x-i.x),y:a.y+f*(o.y-i.y)}}}function Ws(e,t,n){let r=e.length,i,a,o,s,c,l=Vs(e,0);for(let u=0;u!e.skip)),t.cubicInterpolationMode===`monotone`)Ks(e,i);else{let n=r?e[e.length-1]:e[0];for(a=0,o=e.length;ae.ownerDocument.defaultView.getComputedStyle(e,null);function ec(e,t){return $s(e).getPropertyValue(t)}var tc=[`top`,`right`,`bottom`,`left`];function nc(e,t,n){let r={};n=n?`-`+n:``;for(let i=0;i<4;i++){let a=tc[i];r[a]=parseFloat(e[t+`-`+a+n])||0}return r.width=r.left+r.right,r.height=r.top+r.bottom,r}var rc=(e,t,n)=>(e>0||t>0)&&(!n||!n.shadowRoot);function ic(e,t){let n=e.touches,r=n&&n.length?n[0]:e,{offsetX:i,offsetY:a}=r,o=!1,s,c;if(rc(i,a,e.target))s=i,c=a;else{let e=t.getBoundingClientRect();s=r.clientX-e.left,c=r.clientY-e.top,o=!0}return{x:s,y:c,box:o}}function ac(e,t){if(`native`in e)return e;let{canvas:n,currentDevicePixelRatio:r}=t,i=$s(n),a=i.boxSizing===`border-box`,o=nc(i,`padding`),s=nc(i,`border`,`width`),{x:c,y:l,box:u}=ic(e,n),d=o.left+(u&&s.left),f=o.top+(u&&s.top),{width:p,height:m}=t;return a&&(p-=o.width+s.width,m-=o.height+s.height),{x:Math.round((c-d)/p*n.width/r),y:Math.round((l-f)/m*n.height/r)}}function oc(e,t,n){let r,i;if(t===void 0||n===void 0){let a=e&&Zs(e);if(!a)t=e.clientWidth,n=e.clientHeight;else{let e=a.getBoundingClientRect(),o=$s(a),s=nc(o,`border`,`width`),c=nc(o,`padding`);t=e.width-c.width-s.width,n=e.height-c.height-s.height,r=Qs(o.maxWidth,a,`clientWidth`),i=Qs(o.maxHeight,a,`clientHeight`)}}return{width:t,height:n,maxWidth:r||La,maxHeight:i||La}}var sc=e=>Math.round(e*10)/10;function cc(e,t,n,r){let i=$s(e),a=nc(i,`margin`),o=Qs(i.maxWidth,e,`clientWidth`)||La,s=Qs(i.maxHeight,e,`clientHeight`)||La,c=oc(e,t,n),{width:l,height:u}=c;if(i.boxSizing===`content-box`){let e=nc(i,`border`,`width`),t=nc(i,`padding`);l-=t.width+e.width,u-=t.height+e.height}return l=Math.max(0,l-a.width),u=Math.max(0,r?l/r:u-a.height),l=sc(Math.min(l,o,c.maxWidth)),u=sc(Math.min(u,s,c.maxHeight)),l&&!u&&(u=sc(l/2)),(t!==void 0||n!==void 0)&&r&&c.height&&u>c.height&&(u=c.height,l=sc(Math.floor(u*r))),{width:l,height:u}}function lc(e,t,n){let r=t||1,i=sc(e.height*r),a=sc(e.width*r);e.height=sc(e.height),e.width=sc(e.width);let o=e.canvas;return o.style&&(n||!o.style.height&&!o.style.width)&&(o.style.height=`${e.height}px`,o.style.width=`${e.width}px`),e.currentDevicePixelRatio!==r||o.height!==i||o.width!==a?(e.currentDevicePixelRatio=r,o.height=i,o.width=a,e.ctx.setTransform(r,0,0,r,0,0),!0):!1}var uc=function(){let e=!1;try{let t={get passive(){return e=!0,!1}};Xs()&&(window.addEventListener(`test`,null,t),window.removeEventListener(`test`,null,t))}catch{}return e}();function dc(e,t){let n=ec(e,t),r=n&&n.match(/^(\d+)(\.\d+)?px$/);return r?+r[1]:void 0}function fc(e,t,n,r){return{x:e.x+n*(t.x-e.x),y:e.y+n*(t.y-e.y)}}function pc(e,t,n,r){return{x:e.x+n*(t.x-e.x),y:r===`middle`?n<.5?e.y:t.y:r===`after`?n<1?e.y:t.y:n>0?t.y:e.y}}function mc(e,t,n,r){let i={x:e.cp2x,y:e.cp2y},a={x:t.cp1x,y:t.cp1y},o=fc(e,i,n),s=fc(i,a,n),c=fc(a,t,n);return fc(fc(o,s,n),fc(s,c,n),n)}var hc=function(e,t){return{x(n){return e+e+t-n},setWidth(e){t=e},textAlign(e){return e===`center`?e:e===`right`?`left`:`right`},xPlus(e,t){return e-t},leftForLtr(e,t){return e-t}}},gc=function(){return{x(e){return e},setWidth(e){},textAlign(e){return e},xPlus(e,t){return e+t},leftForLtr(e,t){return e}}};function _c(e,t,n){return e?hc(t,n):gc()}function vc(e,t){let n,r;(t===`ltr`||t===`rtl`)&&(n=e.canvas.style,r=[n.getPropertyValue(`direction`),n.getPropertyPriority(`direction`)],n.setProperty(`direction`,t,`important`),e.prevTextDirection=r)}function yc(e,t){t!==void 0&&(delete e.prevTextDirection,e.canvas.style.setProperty(`direction`,t[0],t[1]))}function bc(e){return e===`angle`?{between:ro,compare:to,normalize:no}:{between:oo,compare:(e,t)=>e-t,normalize:e=>e}}function xc({start:e,end:t,count:n,loop:r,style:i}){return{start:e%n,end:t%n,loop:r&&(t-e+1)%n===0,style:i}}function Sc(e,t,n){let{property:r,start:i,end:a}=n,{between:o,normalize:s}=bc(r),c=t.length,{start:l,end:u,loop:d}=e,f,p;if(d){for(l+=c,u+=c,f=0,p=c;fc(i,y,_)&&s(i,y)!==0,x=()=>s(a,_)===0||c(a,y,_),S=()=>h||b(),C=()=>!h||x();for(let e=u,n=u;e<=d;++e)v=t[e%o],!v.skip&&(_=l(v[r]),_!==y&&(h=c(_,i,a),g===null&&S()&&(g=s(_,i)===0?e:n),g!==null&&C()&&(m.push(xc({start:g,end:e,loop:f,count:o,style:p})),g=null),n=e,y=_));return g!==null&&m.push(xc({start:g,end:d,loop:f,count:o,style:p})),m}function wc(e,t){let n=[],r=e.segments;for(let i=0;ii&&e[a%t].skip;)a--;return a%=t,{start:i,end:a}}function Ec(e,t,n,r){let i=e.length,a=[],o=t,s=e[t],c;for(c=t+1;c<=n;++c){let n=e[c%i];n.skip||n.stop?s.skip||(r=!1,a.push({start:t%i,end:(c-1)%i,loop:r}),t=o=n.stop?c:null):(o=c,s.skip&&(t=c)),s=n}return o!==null&&a.push({start:t%i,end:o%i,loop:r}),a}function Dc(e,t){let n=e.points,r=e.options.spanGaps,i=n.length;if(!i)return[];let a=!!e._loop,{start:o,end:s}=Tc(n,i,a,r);return r===!0?Oc(e,[{start:o,end:s,loop:a}],n,t):Oc(e,Ec(n,o,sr({chart:e,initial:t.initial,numSteps:a,currentStep:Math.min(n-t.start,a)}))}_refresh(){this._request||(this._running=!0,this._request=go.call(window,()=>{this._update(),this._request=null,this._running&&this._refresh()}))}_update(e=Date.now()){let t=0;this._charts.forEach((n,r)=>{if(!n.running||!n.items.length)return;let i=n.items,a=i.length-1,o=!1,s;for(;a>=0;--a)s=i[a],s._active?(s._total>n.duration&&(n.duration=s._total),s.tick(e),o=!0):(i[a]=i[i.length-1],i.pop());o&&(r.draw(),this._notify(r,n,e,`progress`)),i.length||(n.running=!1,this._notify(r,n,e,`complete`),n.initial=!1),t+=i.length}),this._lastDate=e,t===0&&(this._running=!1)}_getAnims(e){let t=this._charts,n=t.get(e);return n||(n={running:!1,initial:!0,items:[],listeners:{complete:[],progress:[]}},t.set(e,n)),n}listen(e,t,n){this._getAnims(e).listeners[t].push(n)}add(e,t){t&&t.length&&this._getAnims(e).items.push(...t)}has(e){return this._getAnims(e).items.length>0}start(e){let t=this._charts.get(e);t&&(t.running=!0,t.start=Date.now(),t.duration=t.items.reduce((e,t)=>Math.max(e,t._duration),0),this._refresh())}running(e){if(!this._running)return!1;let t=this._charts.get(e);return!(!t||!t.running||!t.items.length)}stop(e){let t=this._charts.get(e);if(!t||!t.items.length)return;let n=t.items,r=n.length-1;for(;r>=0;--r)n[r].cancel();t.items=[],this._notify(e,t,Date.now(),`complete`)}remove(e){return this._charts.delete(e)}},zc=`transparent`,Bc={boolean(e,t,n){return n>.5?t:e},color(e,t,n){let r=Oo(e||zc),i=r.valid&&Oo(t||zc);return i&&i.valid?i.mix(r,n).hexString():t},number(e,t,n){return e+(t-e)*n}},Vc=class{constructor(e,t,n,r){let i=t[n];r=_s([e.to,r,i,e.from]);let a=_s([e.from,i,r]);this._active=!0,this._fn=e.fn||Bc[e.type||typeof a],this._easing=Eo[e.easing]||Eo.linear,this._start=Math.floor(Date.now()+(e.delay||0)),this._duration=this._total=Math.floor(e.duration),this._loop=!!e.loop,this._target=t,this._prop=n,this._from=a,this._to=r,this._promises=void 0}active(){return this._active}update(e,t,n){if(this._active){this._notify(!1);let r=this._target[this._prop],i=n-this._start,a=this._duration-i;this._start=n,this._duration=Math.floor(Math.max(a,e.duration)),this._total+=i,this._loop=!!e.loop,this._to=_s([e.to,t,r,e.from]),this._from=_s([e.from,r,t])}}cancel(){this._active&&(this.tick(Date.now()),this._active=!1,this._notify(!1))}tick(e){let t=e-this._start,n=this._duration,r=this._prop,i=this._from,a=this._loop,o=this._to,s;this._active=i!==o&&(a||t1?2-s:s,s=this._easing(Math.min(1,Math.max(0,s))),this._target[r]=this._fn(i,o,s)):(this._target[r]=o,this._notify(!0))}wait(){let e=this._promises||(this._promises=[]);return new Promise((t,n)=>{e.push({res:t,rej:n})})}_notify(e){let t=e?`res`:`rej`,n=this._promises||[];for(let e=0;e{let i=e[r];if(!G(i))return;let a={};for(let e of t)a[e]=i[e];(W(i.properties)&&i.properties||[r]).forEach(e=>{(e===r||!n.has(e))&&n.set(e,a)})})}_animateOptions(e,t){let n=t.options,r=Wc(e,n);if(!r)return[];let i=this._createAnimations(r,n);return n.$shared&&Uc(e.options.$animations,n).then(()=>{e.options=n},()=>{}),i}_createAnimations(e,t){let n=this._properties,r=[],i=e.$animations||(e.$animations={}),a=Object.keys(t),o=Date.now(),s;for(s=a.length-1;s>=0;--s){let c=a[s];if(c.charAt(0)===`$`)continue;if(c===`options`){r.push(...this._animateOptions(e,t));continue}let l=t[c],u=i[c],d=n.get(c);if(u){if(d&&u.active()){u.update(d,l,o);continue}u.cancel()}!d||!d.duration?e[c]=l:(i[c]=u=new Vc(d,e,c,l),r.push(u))}return r}update(e,t){if(this._properties.size===0){Object.assign(e,t);return}let n=this._createAnimations(e,t);if(n.length)return Rc.add(this._chart,n),!0}};function Uc(e,t){let n=[],r=Object.keys(t);for(let t=0;t0||!n&&t<0)return i.index}return null}function nl(e,t){let{chart:n,_cachedMeta:r}=e,i=n._stacks||(n._stacks={}),{iScale:a,vScale:o,index:s}=r,c=a.axis,l=o.axis,u=Qc(a,o,r),d=t.length,f;for(let e=0;en[e].axis===t).shift()}function il(e,t){return ys(e,{active:!1,dataset:void 0,datasetIndex:t,index:t,mode:`default`,type:`dataset`})}function al(e,t,n){return ys(e,{active:!1,dataIndex:t,parsed:void 0,raw:void 0,element:n,index:t,mode:`default`,type:`data`})}function ol(e,t){let n=e.controller.index,r=e.vScale&&e.vScale.axis;if(r){t=t||e._parsed;for(let e of t){let t=e._stacks;if(!t||t[r]===void 0||t[r][n]===void 0)return;delete t[r][n],t[r]._visualValues!==void 0&&t[r]._visualValues[n]!==void 0&&delete t[r]._visualValues[n]}}}var sl=e=>e===`reset`||e===`none`,cl=(e,t)=>t?e:Object.assign({},e),ll=(e,t,n)=>e&&!t.hidden&&t._stacked&&{keys:Jc(n,!0),values:null},ul=class{constructor(e,t){this.chart=e,this._ctx=e.ctx,this.index=t,this._cachedDataOpts={},this._cachedMeta=this.getMeta(),this._type=this._cachedMeta.type,this.options=void 0,this._parsing=!1,this._data=void 0,this._objectData=void 0,this._sharedOptions=void 0,this._drawStart=void 0,this._drawCount=void 0,this.enableOptionSharing=!1,this.supportsDecimation=!1,this.$context=void 0,this._syncList=[],this.datasetElementType=new.target.datasetElementType,this.dataElementType=new.target.dataElementType,this.initialize()}initialize(){let e=this._cachedMeta;this.configure(),this.linkScales(),e._stacked=Zc(e.vScale,e),this.addElements(),this.options.fill&&!this.chart.isPluginEnabled(`filler`)&&console.warn(`Tried to use the 'fill' option without the 'Filler' plugin enabled. Please import and register the 'Filler' plugin and make sure it is not disabled in the options`)}updateIndex(e){this.index!==e&&ol(this._cachedMeta),this.index=e}linkScales(){let e=this.chart,t=this._cachedMeta,n=this.getDataset(),r=(e,t,n,r)=>e===`x`?t:e===`r`?r:n,i=t.xAxisID=K(n.xAxisID,rl(e,`x`)),a=t.yAxisID=K(n.yAxisID,rl(e,`y`)),o=t.rAxisID=K(n.rAxisID,rl(e,`r`)),s=t.indexAxis,c=t.iAxisID=r(s,i,a,o),l=t.vAxisID=r(s,a,i,o);t.xScale=this.getScaleForId(i),t.yScale=this.getScaleForId(a),t.rScale=this.getScaleForId(o),t.iScale=this.getScaleForId(c),t.vScale=this.getScaleForId(l)}getDataset(){return this.chart.data.datasets[this.index]}getMeta(){return this.chart.getDatasetMeta(this.index)}getScaleForId(e){return this.chart.scales[e]}_getOtherScale(e){let t=this._cachedMeta;return e===t.iScale?t.vScale:t.iScale}reset(){this._update(`reset`)}_destroy(){let e=this._cachedMeta;this._data&&mo(this._data,this),e._stacked&&ol(e)}_dataCheck(){let e=this.getDataset(),t=e.data||(e.data=[]),n=this._data;if(G(t)){let e=this._cachedMeta;this._data=Xc(t,e)}else if(n!==t){if(n){mo(n,this);let e=this._cachedMeta;ol(e),e._parsed=[]}t&&Object.isExtensible(t)&&po(t,this),this._syncList=[],this._data=t}}addElements(){let e=this._cachedMeta;this._dataCheck(),this.datasetElementType&&(e.dataset=new this.datasetElementType)}buildOrUpdateElements(e){let t=this._cachedMeta,n=this.getDataset(),r=!1;this._dataCheck();let i=t._stacked;t._stacked=Zc(t.vScale,t),t.stack!==n.stack&&(r=!0,ol(t),t.stack=n.stack),this._resyncElements(e),(r||i!==t._stacked)&&(nl(this,t._parsed),t._stacked=Zc(t.vScale,t))}configure(){let e=this.chart.config,t=e.datasetScopeKeys(this._type),n=e.getOptionScopes(this.getDataset(),t,!0);this.options=e.createResolver(n,this.getContext()),this._parsing=this.options.parsing,this._cachedDataOpts={}}parse(e,t){let{_cachedMeta:n,_data:r}=this,{iScale:i,_stacked:a}=n,o=i.axis,s=e===0&&t===r.length||n._sorted,c=e>0&&n._parsed[e-1],l,u,d;if(this._parsing===!1)n._parsed=r,n._sorted=!0,d=r;else{d=W(r[e])?this.parseArrayData(n,r,e,t):G(r[e])?this.parseObjectData(n,r,e,t):this.parsePrimitiveData(n,r,e,t);let i=()=>u[o]===null||c&&u[o]t||u=0;--d)if(!p()){this.updateRangeFromParsed(c,e,f,s);break}}return c}getAllParsedValues(e){let t=this._cachedMeta._parsed,n=[],r,i,a;for(r=0,i=t.length;r=0&&ethis.getContext(n,r,t),u);return p.$shared&&(p.$shared=s,i[a]=Object.freeze(cl(p,s))),p}_resolveAnimations(e,t,n){let r=this.chart,i=this._cachedDataOpts,a=`animation-${t}`,o=i[a];if(o)return o;let s;if(r.options.animation!==!1){let r=this.chart.config,i=r.datasetAnimationScopeKeys(this._type,t),a=r.getOptionScopes(this.getDataset(),i);s=r.createResolver(a,this.getContext(e,n,t))}let c=new Hc(r,s&&s.animations);return s&&s._cacheable&&(i[a]=Object.freeze(c)),c}getSharedOptions(e){if(e.$shared)return this._sharedOptions||(this._sharedOptions=Object.assign({},e))}includeOptions(e,t){return!t||sl(e)||this.chart._animationsDisabled}_getSharedOptions(e,t){let n=this.resolveDataElementOptions(e,t),r=this._sharedOptions,i=this.getSharedOptions(n),a=this.includeOptions(t,i)||i!==r;return this.updateSharedOptions(i,t,n),{sharedOptions:i,includeOptions:a}}updateElement(e,t,n,r){sl(r)?Object.assign(e,n):this._resolveAnimations(t,r).update(e,n)}updateSharedOptions(e,t,n){e&&!sl(t)&&this._resolveAnimations(void 0,t).update(e,n)}_setStyle(e,t,n,r){e.active=r;let i=this.getStyle(t,r);this._resolveAnimations(t,n,r).update(e,{options:!r&&this.getSharedOptions(i)||i})}removeHoverStyle(e,t,n){this._setStyle(e,n,`active`,!1)}setHoverStyle(e,t,n){this._setStyle(e,n,`active`,!0)}_removeDatasetHoverStyle(){let e=this._cachedMeta.dataset;e&&this._setStyle(e,void 0,`active`,!1)}_setDatasetHoverStyle(){let e=this._cachedMeta.dataset;e&&this._setStyle(e,void 0,`active`,!0)}_resyncElements(e){let t=this._data,n=this._cachedMeta.data;for(let[e,t,n]of this._syncList)this[e](t,n);this._syncList=[];let r=n.length,i=t.length,a=Math.min(i,r);a&&this.parse(0,a),i>r?this._insertElements(r,i-r,e):i{for(e.length+=t,o=e.length-1;o>=a;o--)e[o]=e[o-t]};for(s(i),o=e;oe-t))}return e._cache.$bar}function fl(e){let t=e.iScale,n=dl(t,e.type),r=t._length,i,a,o,s,c=()=>{o!==32767&&o!==-32768&&(Ma(s)&&(r=Math.min(r,Math.abs(o-s)||r)),s=o)};for(i=0,a=n.length;i0?i[e-1]:null,s=eMath.abs(s)&&(c=s,l=o),t[n.axis]=l,t._custom={barStart:c,barEnd:l,start:i,end:a,min:o,max:s}}function gl(e,t,n,r){return W(e)?hl(e,t,n,r):t[n.axis]=n.parse(e,r),t}function _l(e,t,n,r){let i=e.iScale,a=e.vScale,o=i.getLabels(),s=i===a,c=[],l,u,d,f;for(l=n,u=n+r;l=n?1:-1):Ha(e)}function bl(e){let t,n,r,i,a;return e.horizontal?(t=e.base>e.x,n=`left`,r=`right`):(t=e.basee.controller.options.grouped),i=n.options.stacked,a=[],o=this._cachedMeta.controller.getParsed(t),s=o&&o[n.axis],c=e=>{let t=e._parsed.find(e=>e[n.axis]===s),r=t&&t[e.vScale.axis];if(U(r)||isNaN(r))return!0};for(let n of r)if(!(t!==void 0&&c(n))&&((i===!1||a.indexOf(n.stack)===-1||i===void 0&&n.stack===void 0)&&a.push(n.stack),n.index===e))break;return a.length||a.push(void 0),a}_getStackCount(e){return this._getStacks(void 0,e).length}_getAxisCount(){return this._getAxis().length}getFirstScaleIdForIndexAxis(){let e=this.chart.scales,t=this.chart.options.indexAxis;return Object.keys(e).filter(n=>e[n].axis===t).shift()}_getAxis(){let e={},t=this.getFirstScaleIdForIndexAxis();for(let n of this.chart.data.datasets)e[K(this.chart.options.indexAxis===`x`?n.xAxisID:n.yAxisID,t)]=!0;return Object.keys(e)}_getStackIndex(e,t,n){let r=this._getStacks(e,n),i=t===void 0?-1:r.indexOf(t);return i===-1?r.length-1:i}_getRuler(){let e=this.options,t=this._cachedMeta,n=t.iScale,r=[],i,a;for(i=0,a=t.data.length;i=0;--n)t=Math.max(t,e[n].size(this.resolveDataElementOptions(n))/2);return t>0&&t}getLabelAndValue(e){let t=this._cachedMeta,n=this.chart.data.labels||[],{xScale:r,yScale:i}=t,a=this.getParsed(e),o=r.getLabelForValue(a.x),s=i.getLabelForValue(a.y),c=a._custom;return{label:n[e]||``,value:`(`+o+`, `+s+(c?`, `+c:``)+`)`}}update(e){let t=this._cachedMeta.data;this.updateElements(t,0,t.length,e)}updateElements(e,t,n,r){let i=r===`reset`,{iScale:a,vScale:o}=this._cachedMeta,{sharedOptions:s,includeOptions:c}=this._getSharedOptions(t,r),l=a.axis,u=o.axis;for(let d=t;dro(e,s,c,!0)?1:Math.max(t,t*n,r,r*n),m=(e,t,r)=>ro(e,s,c,!0)?-1:Math.min(t,t*n,r,r*n),h=p(0,l,d),g=p(Z,u,f),_=m(Y,l,d),v=m(Y+Z,u,f);r=(h-_)/2,i=(g-v)/2,a=-(h+_)/2,o=-(g+v)/2}return{ratioX:r,ratioY:i,offsetX:a,offsetY:o}}var kl=class extends ul{constructor(e,t){super(e,t),this.enableOptionSharing=!0,this.innerRadius=void 0,this.outerRadius=void 0,this.offsetX=void 0,this.offsetY=void 0}linkScales(){}parse(e,t){let n=this.getDataset().data,r=this._cachedMeta;if(this._parsing===!1)r._parsed=n;else{let i=e=>+n[e];if(G(n[e])){let{key:e=`value`}=this._parsing;i=t=>+Aa(n[t],e)}let a,o;for(a=e,o=e+t;a0&&!isNaN(e)?Math.abs(e)/t*X:0}getLabelAndValue(e){let t=this._cachedMeta,n=this.chart,r=n.data.labels||[],i=Io(t._parsed[e],n.options.locale);return{label:r[e]||``,value:i}}getMaxBorderWidth(e){let t=0,n=this.chart,r,i,a,o,s;if(!e){for(r=0,i=n.data.datasets.length;re!==`spacing`,_indexable:e=>e!==`spacing`&&!e.startsWith(`borderDash`)&&!e.startsWith(`hoverBorderDash`)}),$(kl,`overrides`,{aspectRatio:1,plugins:{legend:{labels:{generateLabels(e){let t=e.data,{labels:{pointStyle:n,textAlign:r,color:i,useBorderRadius:a,borderRadius:o}}=e.legend.options;return t.labels.length&&t.datasets.length?t.labels.map((t,s)=>{let c=e.getDatasetMeta(0).controller.getStyle(s);return{text:t,fillStyle:c.backgroundColor,fontColor:i,hidden:!e.getDataVisibility(s),lineDash:c.borderDash,lineDashOffset:c.borderDashOffset,lineJoin:c.borderJoinStyle,lineWidth:c.borderWidth,strokeStyle:c.borderColor,textAlign:r,pointStyle:n,borderRadius:a&&(o||c.borderRadius),index:s}}):[]}},onClick(e,t,n){n.chart.toggleDataVisibility(t.index),n.chart.update()}}}});var Al=class extends ul{initialize(){this.enableOptionSharing=!0,this.supportsDecimation=!0,super.initialize()}update(e){let t=this._cachedMeta,{dataset:n,data:r=[],_dataset:i}=t,a=this.chart._animationsDisabled,{start:o,count:s}=xo(t,r,a);this._drawStart=o,this._drawCount=s,So(t)&&(o=0,s=r.length),n._chart=this.chart,n._datasetIndex=this.index,n._decimated=!!i._decimated,n.points=r;let c=this.resolveDatasetElementOptions(e);this.options.showLine||(c.borderWidth=0),c.segment=this.options.segment,this.updateElement(n,void 0,{animated:!a,options:c},e),this.updateElements(r,o,s,e)}updateElements(e,t,n,r){let i=r===`reset`,{iScale:a,vScale:o,_stacked:s,_dataset:c}=this._cachedMeta,{sharedOptions:l,includeOptions:u}=this._getSharedOptions(t,r),d=a.axis,f=o.axis,{spanGaps:p,segment:m}=this.options,h=qa(p)?p:1/0,g=this.chart._animationsDisabled||i||r===`none`,_=t+n,v=e.length,y=t>0&&this.getParsed(t-1);for(let n=0;n=_){v.skip=!0;continue}let b=this.getParsed(n),x=U(b[f]),S=v[d]=a.getPixelForValue(b[d],n),C=v[f]=i||x?o.getBasePixel():o.getPixelForValue(s?this.applyStack(o,b,s):b[f],n);v.skip=isNaN(S)||isNaN(C)||x,v.stop=n>0&&Math.abs(b[d]-y[d])>h,m&&(v.parsed=b,v.raw=c.data[n]),u&&(v.options=l||this.resolveDataElementOptions(n,p.active?`active`:r)),g||this.updateElement(p,n,v,r),y=b}}getMaxOverflow(){let e=this._cachedMeta,t=e.dataset,n=t.options&&t.options.borderWidth||0,r=e.data||[];if(!r.length)return n;let i=r[0].size(this.resolveDataElementOptions(0)),a=r[r.length-1].size(this.resolveDataElementOptions(r.length-1));return Math.max(n,i,a)/2}draw(){let e=this._cachedMeta;e.dataset.updateControlPoints(this.chart.chartArea,e.iScale.axis),super.draw()}};$(Al,`id`,`line`),$(Al,`defaults`,{datasetElementType:`line`,dataElementType:`point`,showLine:!0,spanGaps:!1}),$(Al,`overrides`,{scales:{_index_:{type:`category`},_value_:{type:`linear`}}});var jl=class extends ul{constructor(e,t){super(e,t),this.innerRadius=void 0,this.outerRadius=void 0}getLabelAndValue(e){let t=this._cachedMeta,n=this.chart,r=n.data.labels||[],i=Io(t._parsed[e].r,n.options.locale);return{label:r[e]||``,value:i}}parseObjectData(e,t,n,r){return zs.bind(this)(e,t,n,r)}update(e){let t=this._cachedMeta.data;this._updateRadius(),this.updateElements(t,0,t.length,e)}getMinMax(){let e=this._cachedMeta,t={min:1/0,max:-1/0};return e.data.forEach((e,n)=>{let r=this.getParsed(n).r;!isNaN(r)&&this.chart.getDataVisibility(n)&&(rt.max&&(t.max=r))}),t}_updateRadius(){let e=this.chart,t=e.chartArea,n=e.options,r=Math.min(t.right-t.left,t.bottom-t.top),i=Math.max(r/2,0),a=(i-Math.max(n.cutoutPercentage?i/100*n.cutoutPercentage:1,0))/e.getVisibleDatasetCount();this.outerRadius=i-a*this.index,this.innerRadius=this.outerRadius-a}updateElements(e,t,n,r){let i=r===`reset`,a=this.chart,o=a.options.animation,s=this._cachedMeta.rScale,c=s.xCenter,l=s.yCenter,u=s.getIndexAngle(0)-.5*Y,d=u,f,p=360/this.countVisibleElements();for(f=0;f{!isNaN(this.getParsed(n).r)&&this.chart.getDataVisibility(n)&&t++}),t}_computeAngle(e,t,n){return this.chart.getDataVisibility(e)?Xa(this.resolveDataElementOptions(e,t).angle||n):0}};$(jl,`id`,`polarArea`),$(jl,`defaults`,{dataElementType:`arc`,animation:{animateRotate:!0,animateScale:!0},animations:{numbers:{type:`number`,properties:[`x`,`y`,`startAngle`,`endAngle`,`innerRadius`,`outerRadius`]}},indexAxis:`r`,startAngle:0}),$(jl,`overrides`,{aspectRatio:1,plugins:{legend:{labels:{generateLabels(e){let t=e.data;if(t.labels.length&&t.datasets.length){let{labels:{pointStyle:n,color:r}}=e.legend.options;return t.labels.map((t,i)=>{let a=e.getDatasetMeta(0).controller.getStyle(i);return{text:t,fillStyle:a.backgroundColor,strokeStyle:a.borderColor,fontColor:r,lineWidth:a.borderWidth,pointStyle:n,hidden:!e.getDataVisibility(i),index:i}})}return[]}},onClick(e,t,n){n.chart.toggleDataVisibility(t.index),n.chart.update()}}},scales:{r:{type:`radialLinear`,angleLines:{display:!1},beginAtZero:!0,grid:{circular:!0},pointLabels:{display:!1},startAngle:0}}});var Ml=class extends kl{};$(Ml,`id`,`pie`),$(Ml,`defaults`,{cutout:0,rotation:0,circumference:360,radius:`100%`});var Nl=class extends ul{getLabelAndValue(e){let t=this._cachedMeta.vScale,n=this.getParsed(e);return{label:t.getLabels()[e],value:``+t.getLabelForValue(n[t.axis])}}parseObjectData(e,t,n,r){return zs.bind(this)(e,t,n,r)}update(e){let t=this._cachedMeta,n=t.dataset,r=t.data||[],i=t.iScale.getLabels();if(n.points=r,e!==`resize`){let t=this.resolveDatasetElementOptions(e);this.options.showLine||(t.borderWidth=0);let a={_loop:!0,_fullLoop:i.length===r.length,options:t};this.updateElement(n,void 0,a,e)}this.updateElements(r,0,r.length,e)}updateElements(e,t,n,r){let i=this._cachedMeta.rScale,a=r===`reset`;for(let o=t;o0&&this.getParsed(t-1);for(let l=t;l0&&Math.abs(n[f]-v[f])>g,h&&(m.parsed=n,m.raw=c.data[l]),d&&(m.options=u||this.resolveDataElementOptions(l,t.active?`active`:r)),_||this.updateElement(t,l,m,r),v=n}this.updateSharedOptions(u,r,l)}getMaxOverflow(){let e=this._cachedMeta,t=e.data||[];if(!this.options.showLine){let e=0;for(let n=t.length-1;n>=0;--n)e=Math.max(e,t[n].size(this.resolveDataElementOptions(n))/2);return e>0&&e}let n=e.dataset,r=n.options&&n.options.borderWidth||0;if(!t.length)return r;let i=t[0].size(this.resolveDataElementOptions(0)),a=t[t.length-1].size(this.resolveDataElementOptions(t.length-1));return Math.max(r,i,a)/2}};$(Pl,`id`,`scatter`),$(Pl,`defaults`,{datasetElementType:!1,dataElementType:`point`,showLine:!1,fill:!1}),$(Pl,`overrides`,{interaction:{mode:`point`},scales:{x:{type:`linear`},y:{type:`linear`}}});function Fl(){throw Error(`This method is not implemented: Check that a complete date adapter is provided.`)}var Il={_date:class e{static override(t){Object.assign(e.prototype,t)}constructor(e){$(this,`options`,void 0),this.options=e||{}}init(){}formats(){return Fl()}parse(){return Fl()}format(){return Fl()}add(){return Fl()}diff(){return Fl()}startOf(){return Fl()}endOf(){return Fl()}}};function Ll(e,t,n,r){let{controller:i,data:a,_sorted:o}=e,s=i._cachedMeta.iScale,c=e.dataset&&e.dataset.options?e.dataset.options.spanGaps:null;if(s&&t===s.axis&&t!==`r`&&o&&a.length){let o=s._reversePixels?lo:co;if(!r){let r=o(a,t,n);if(c){let{vScale:t}=i._cachedMeta,{_parsed:n}=e,a=n.slice(0,r.lo+1).reverse().findIndex(e=>!U(e[t.axis]));r.lo-=Math.max(0,a);let o=n.slice(r.hi).findIndex(e=>!U(e[t.axis]));r.hi+=Math.max(0,o)}return r}if(i._sharedOptions){let e=a[0],r=typeof e.getRange==`function`&&e.getRange(t);if(r){let e=o(a,t,n-r),i=o(a,t,n+r);return{lo:e.lo,hi:i.hi}}}}return{lo:0,hi:a.length-1}}function Rl(e,t,n,r,i){let a=e.getSortedVisibleDatasetMetas(),o=n[t];for(let e=0,n=a.length;e{e[o]&&e[o](t[n],i)&&(a.push({element:e,datasetIndex:r,index:c}),s=s||e.inRange(t.x,t.y,i))}),r&&!s?[]:a}var Gl={evaluateInteractionItems:Rl,modes:{index(e,t,n,r){let i=ac(t,e),a=n.axis||`x`,o=n.includeInvisible||!1,s=n.intersect?Bl(e,i,a,r,o):Ul(e,i,a,!1,r,o),c=[];return s.length?(e.getSortedVisibleDatasetMetas().forEach(e=>{let t=s[0].index,n=e.data[t];n&&!n.skip&&c.push({element:n,datasetIndex:e.index,index:t})}),c):[]},dataset(e,t,n,r){let i=ac(t,e),a=n.axis||`xy`,o=n.includeInvisible||!1,s=n.intersect?Bl(e,i,a,r,o):Ul(e,i,a,!1,r,o);if(s.length>0){let t=s[0].datasetIndex,n=e.getDatasetMeta(t).data;s=[];for(let e=0;ee.pos===t)}function Jl(e,t){return e.filter(e=>Kl.indexOf(e.pos)===-1&&e.box.axis===t)}function Yl(e,t){return e.sort((e,n)=>{let r=t?n:e,i=t?e:n;return r.weight===i.weight?r.index-i.index:r.weight-i.weight})}function Xl(e){let t=[],n,r,i,a,o,s;for(n=0,r=(e||[]).length;ne.box.fullSize),!0),r=Yl(ql(t,`left`),!0),i=Yl(ql(t,`right`)),a=Yl(ql(t,`top`),!0),o=Yl(ql(t,`bottom`)),s=Jl(t,`x`),c=Jl(t,`y`);return{fullSize:n,leftAndTop:r.concat(a),rightAndBottom:i.concat(c).concat(o).concat(s),chartArea:ql(t,`chartArea`),vertical:r.concat(i).concat(c),horizontal:a.concat(o).concat(s)}}function eu(e,t,n,r){return Math.max(e[n],t[n])+Math.max(e[r],t[r])}function tu(e,t){e.top=Math.max(e.top,t.top),e.left=Math.max(e.left,t.left),e.bottom=Math.max(e.bottom,t.bottom),e.right=Math.max(e.right,t.right)}function nu(e,t,n,r){let{pos:i,box:a}=n,o=e.maxPadding;if(!G(i)){n.size&&(e[i]-=n.size);let t=r[n.stack]||{size:0,count:1};t.size=Math.max(t.size,n.horizontal?a.height:a.width),n.size=t.size/t.count,e[i]+=n.size}a.getPadding&&tu(o,a.getPadding());let s=Math.max(0,t.outerWidth-eu(o,e,`left`,`right`)),c=Math.max(0,t.outerHeight-eu(o,e,`top`,`bottom`)),l=s!==e.w,u=c!==e.h;return e.w=s,e.h=c,n.horizontal?{same:l,other:u}:{same:u,other:l}}function ru(e){let t=e.maxPadding;function n(n){let r=Math.max(t[n]-e[n],0);return e[n]+=r,r}e.y+=n(`top`),e.x+=n(`left`),n(`right`),n(`bottom`)}function iu(e,t){let n=t.maxPadding;function r(e){let r={left:0,top:0,right:0,bottom:0};return e.forEach(e=>{r[e]=Math.max(t[e],n[e])}),r}return r(e?[`left`,`right`]:[`top`,`bottom`])}function au(e,t,n,r){let i=[],a,o,s,c,l,u;for(a=0,o=e.length,l=0;a{typeof e.beforeLayout==`function`&&e.beforeLayout()});let u=c.reduce((e,t)=>t.box.options&&t.box.options.display===!1?e:e+1,0)||1,d=Object.freeze({outerWidth:t,outerHeight:n,padding:i,availableWidth:a,availableHeight:o,vBoxMaxWidth:a/2/u,hBoxMaxHeight:o/2}),f=Object.assign({},i);tu(f,hs(r));let p=Object.assign({maxPadding:f,w:a,h:o,x:i.left,y:i.top},i),m=Ql(c.concat(l),d);au(s.fullSize,p,d,m),au(c,p,d,m),au(l,p,d,m)&&au(c,p,d,m),ru(p),su(s.leftAndTop,p,d,m),p.x+=p.w,p.y+=p.h,su(s.rightAndBottom,p,d,m),e.chartArea={left:p.left,top:p.top,right:p.left+p.w,bottom:p.top+p.h,height:p.h,width:p.w},J(s.chartArea,t=>{let n=t.box;Object.assign(n,e.chartArea),n.update(p.w,p.h,{left:0,top:0,right:0,bottom:0})})}},lu=class{acquireContext(e,t){}releaseContext(e){return!1}addEventListener(e,t,n){}removeEventListener(e,t,n){}getDevicePixelRatio(){return 1}getMaximumSize(e,t,n,r){return t=Math.max(0,t||e.width),n=n||e.height,{width:t,height:Math.max(0,r?Math.floor(t/r):n)}}isAttached(e){return!0}updateConfig(e){}},uu=class extends lu{acquireContext(e){return e&&e.getContext&&e.getContext(`2d`)||null}updateConfig(e){e.options.animation=!1}},du=`$chartjs`,fu={touchstart:`mousedown`,touchmove:`mousemove`,touchend:`mouseup`,pointerenter:`mouseenter`,pointerdown:`mousedown`,pointermove:`mousemove`,pointerup:`mouseup`,pointerleave:`mouseout`,pointerout:`mouseout`},pu=e=>e===null||e===``;function mu(e,t){let n=e.style,r=e.getAttribute(`height`),i=e.getAttribute(`width`);if(e[du]={initial:{height:r,width:i,style:{display:n.display,height:n.height,width:n.width}}},n.display=n.display||`block`,n.boxSizing=n.boxSizing||`border-box`,pu(i)){let t=dc(e,`width`);t!==void 0&&(e.width=t)}if(pu(r)){if(e.style.height===``)e.height=e.width/(t||2);else{let t=dc(e,`height`);t!==void 0&&(e.height=t)}}return e}var hu=uc?{passive:!0}:!1;function gu(e,t,n){e&&e.addEventListener(t,n,hu)}function _u(e,t,n){e&&e.canvas&&e.canvas.removeEventListener(t,n,hu)}function vu(e,t){let n=fu[e.type]||e.type,{x:r,y:i}=ac(e,t);return{type:n,chart:t,native:e,x:r===void 0?null:r,y:i===void 0?null:i}}function yu(e,t){for(let n of e)if(n===t||n.contains(t))return!0}function bu(e,t,n){let r=e.canvas,i=new MutationObserver(e=>{let t=!1;for(let n of e)t=t||yu(n.addedNodes,r),t=t&&!yu(n.removedNodes,r);t&&n()});return i.observe(document,{childList:!0,subtree:!0}),i}function xu(e,t,n){let r=e.canvas,i=new MutationObserver(e=>{let t=!1;for(let n of e)t=t||yu(n.removedNodes,r),t=t&&!yu(n.addedNodes,r);t&&n()});return i.observe(document,{childList:!0,subtree:!0}),i}var Su=new Map,Cu=0;function wu(){let e=window.devicePixelRatio;e!==Cu&&(Cu=e,Su.forEach((t,n)=>{n.currentDevicePixelRatio!==e&&t()}))}function Tu(e,t){Su.size||window.addEventListener(`resize`,wu),Su.set(e,t)}function Eu(e){Su.delete(e),Su.size||window.removeEventListener(`resize`,wu)}function Du(e,t,n){let r=e.canvas,i=r&&Zs(r);if(!i)return;let a=_o((e,t)=>{let r=i.clientWidth;n(e,t),r{let t=e[0],n=t.contentRect.width,r=t.contentRect.height;(n!==0||r!==0)&&a(n,r)});return o.observe(i),Tu(e,a),o}function Ou(e,t,n){n&&n.disconnect(),t===`resize`&&Eu(e)}function ku(e,t,n){let r=e.canvas,i=_o(t=>{e.ctx!==null&&n(vu(t,e))},e);return gu(r,t,i),i}var Au=class extends lu{acquireContext(e,t){let n=e&&e.getContext&&e.getContext(`2d`);return n&&n.canvas===e?(mu(e,t),n):null}releaseContext(e){let t=e.canvas;if(!t[du])return!1;let n=t[du].initial;[`height`,`width`].forEach(e=>{let r=n[e];U(r)?t.removeAttribute(e):t.setAttribute(e,r)});let r=n.style||{};return Object.keys(r).forEach(e=>{t.style[e]=r[e]}),t.width=t.width,delete t[du],!0}addEventListener(e,t,n){this.removeEventListener(e,t);let r=e.$proxies||(e.$proxies={});r[t]=({attach:bu,detach:xu,resize:Du}[t]||ku)(e,t,n)}removeEventListener(e,t){let n=e.$proxies||(e.$proxies={}),r=n[t];r&&(({attach:Ou,detach:Ou,resize:Ou}[t]||_u)(e,t,r),n[t]=void 0)}getDevicePixelRatio(){return window.devicePixelRatio}getMaximumSize(e,t,n,r){return cc(e,t,n,r)}isAttached(e){let t=e&&Zs(e);return!!(t&&t.isConnected)}};function ju(e){return!Xs()||typeof OffscreenCanvas<`u`&&e instanceof OffscreenCanvas?uu:Au}var Mu=class{constructor(){$(this,`x`,void 0),$(this,`y`,void 0),$(this,`active`,!1),$(this,`options`,void 0),$(this,`$animations`,void 0)}tooltipPosition(e){let{x:t,y:n}=this.getProps([`x`,`y`],e);return{x:t,y:n}}hasValue(){return qa(this.x)&&qa(this.y)}getProps(e,t){let n=this.$animations;if(!t||!n)return this;let r={};return e.forEach(e=>{r[e]=n[e]&&n[e].active()?n[e]._to:this[e]}),r}};$(Mu,`defaults`,{}),$(Mu,`defaultRoutes`,void 0);function Nu(e,t){let n=e.options.ticks,r=Pu(e),i=Math.min(n.maxTicksLimit||r,r),a=n.major.enabled?Iu(t):[],o=a.length,s=a[0],c=a[o-1],l=[];if(o>i)return Lu(t,l,a,o/i),l;let u=Fu(a,t,i);if(o>0){let e,n,r=o>1?Math.round((c-s)/(o-1)):null;for(Ru(t,l,u,U(r)?0:s-r,s),e=0,n=o-1;ei)return t}return Math.max(i,1)}function Iu(e){let t=[],n,r;for(n=0,r=e.length;ne===`left`?`right`:e===`right`?`left`:e,Vu=(e,t,n)=>t===`top`||t===`left`?e[t]+n:e[t]-n,Hu=(e,t)=>Math.min(t||e,e);function Uu(e,t){let n=[],r=e.length/t,i=e.length,a=0;for(;ao+s)))return c}function Gu(e,t){J(e,e=>{let n=e.gc,r=n.length/2,i;if(r>t){for(i=0;in?n:t,n=r&&t>n?t:n,{min:_a(t,_a(n,t)),max:_a(n,_a(t,n))}}getPadding(){return{left:this.paddingLeft||0,top:this.paddingTop||0,right:this.paddingRight||0,bottom:this.paddingBottom||0}}getTicks(){return this.ticks}getLabels(){let e=this.chart.data;return this.options.labels||(this.isHorizontal()?e.xLabels:e.yLabels)||e.labels||[]}getLabelItems(e=this.chart.chartArea){return this._labelItems||(this._labelItems=this._computeLabelItems(e))}beforeLayout(){this._cache={},this._dataLimitsCached=!1}beforeUpdate(){q(this.options.beforeUpdate,[this])}update(e,t,n){let{beginAtZero:r,grace:i,ticks:a}=this.options,o=a.sampleSize;this.beforeUpdate(),this.maxWidth=e,this.maxHeight=t,this._margins=n=Object.assign({left:0,right:0,top:0,bottom:0},n),this.ticks=null,this._labelSizes=null,this._gridLineItems=null,this._labelItems=null,this.beforeSetDimensions(),this.setDimensions(),this.afterSetDimensions(),this._maxLength=this.isHorizontal()?this.width+n.left+n.right:this.height+n.top+n.bottom,this._dataLimitsCached||(this.beforeDataLimits(),this.determineDataLimits(),this.afterDataLimits(),this._range=vs(this,i,r),this._dataLimitsCached=!0),this.beforeBuildTicks(),this.ticks=this.buildTicks()||[],this.afterBuildTicks();let s=o=i||n<=1||!this.isHorizontal()){this.labelRotation=r;return}let l=this._getLabelSizes(),u=l.widest.width,d=l.highest.height,f=io(this.chart.width-u,0,this.maxWidth);o=e.offset?this.maxWidth/n:f/(n-1),u+6>o&&(o=f/(n-(e.offset?.5:1)),s=this.maxHeight-Ku(e.grid)-t.padding-qu(e.title,this.chart.options.font),c=Math.sqrt(u*u+d*d),a=Za(Math.min(Math.asin(io((l.highest.height+6)/o,-1,1)),Math.asin(io(s/c,-1,1))-Math.asin(io(d/c,-1,1)))),a=Math.max(r,Math.min(i,a))),this.labelRotation=a}afterCalculateLabelRotation(){q(this.options.afterCalculateLabelRotation,[this])}afterAutoSkip(){}beforeFit(){q(this.options.beforeFit,[this])}fit(){let e={width:0,height:0},{chart:t,options:{ticks:n,title:r,grid:i}}=this,a=this._isVisible(),o=this.isHorizontal();if(a){let a=qu(r,t.options.font);if(o?(e.width=this.maxWidth,e.height=Ku(i)+a):(e.height=this.maxHeight,e.width=Ku(i)+a),n.display&&this.ticks.length){let{first:t,last:r,widest:i,highest:a}=this._getLabelSizes(),s=n.padding*2,c=Xa(this.labelRotation),l=Math.cos(c),u=Math.sin(c);if(o){let t=n.mirror?0:u*i.width+l*a.height;e.height=Math.min(this.maxHeight,e.height+t+s)}else{let t=n.mirror?0:l*i.width+u*a.height;e.width=Math.min(this.maxWidth,e.width+t+s)}this._calculatePadding(t,r,u,l)}}this._handleMargins(),o?(this.width=this._length=t.width-this._margins.left-this._margins.right,this.height=e.height):(this.width=e.width,this.height=this._length=t.height-this._margins.top-this._margins.bottom)}_calculatePadding(e,t,n,r){let{ticks:{align:i,padding:a},position:o}=this.options,s=this.labelRotation!==0,c=o!==`top`&&this.axis===`x`;if(this.isHorizontal()){let o=this.getPixelForTick(0)-this.left,l=this.right-this.getPixelForTick(this.ticks.length-1),u=0,d=0;s?c?(u=r*e.width,d=n*t.height):(u=n*e.height,d=r*t.width):i===`start`?d=t.width:i===`end`?u=e.width:i!==`inner`&&(u=e.width/2,d=t.width/2),this.paddingLeft=Math.max((u-o+a)*this.width/(this.width-o),0),this.paddingRight=Math.max((d-l+a)*this.width/(this.width-l),0)}else{let n=t.height/2,r=e.height/2;i===`start`?(n=0,r=e.height):i===`end`&&(n=t.height,r=0),this.paddingTop=n+a,this.paddingBottom=r+a}}_handleMargins(){this._margins&&(this._margins.left=Math.max(this.paddingLeft,this._margins.left),this._margins.top=Math.max(this.paddingTop,this._margins.top),this._margins.right=Math.max(this.paddingRight,this._margins.right),this._margins.bottom=Math.max(this.paddingBottom,this._margins.bottom))}afterFit(){q(this.options.afterFit,[this])}isHorizontal(){let{axis:e,position:t}=this.options;return t===`top`||t===`bottom`||e===`x`}isFullSize(){return this.options.fullSize}_convertTicksToLabels(e){this.beforeTickToLabelConversion(),this.generateTickLabels(e);let t,n;for(t=0,n=e.length;t({width:a[e]||0,height:o[e]||0});return{first:C(0),last:C(t-1),widest:C(x),highest:C(S),widths:a,heights:o}}getLabelForValue(e){return e}getPixelForValue(e,t){return NaN}getValueForPixel(e){}getPixelForTick(e){let t=this.ticks;return e<0||e>t.length-1?null:this.getPixelForValue(t[e].value)}getPixelForDecimal(e){this._reversePixels&&(e=1-e);let t=this._startPixel+e*this._length;return ao(this._alignToPixels?Jo(this.chart,t,0):t)}getDecimalForPixel(e){let t=(e-this._startPixel)/this._length;return this._reversePixels?1-t:t}getBasePixel(){return this.getPixelForValue(this.getBaseValue())}getBaseValue(){let{min:e,max:t}=this;return e<0&&t<0?t:e>0&&t>0?e:0}getContext(e){let t=this.ticks||[];if(e>=0&&eo*r?o/n:s/r:s*r0:!!e}_computeGridLineItems(e){let t=this.axis,n=this.chart,r=this.options,{grid:i,position:a,border:o}=r,s=i.offset,c=this.isHorizontal(),l=this.ticks.length+ +!!s,u=Ku(i),d=[],f=o.setContext(this.getContext()),p=f.display?f.width:0,m=p/2,h=function(e){return Jo(n,e,p)},g,_,v,y,b,x,S,C,w,T,E,D;if(a===`top`)g=h(this.bottom),x=this.bottom-u,C=g-m,T=h(e.top)+m,D=e.bottom;else if(a===`bottom`)g=h(this.top),T=e.top,D=h(e.bottom)-m,x=g+m,C=this.top+u;else if(a===`left`)g=h(this.right),b=this.right-u,S=g-m,w=h(e.left)+m,E=e.right;else if(a===`right`)g=h(this.left),w=e.left,E=h(e.right)-m,b=g+m,S=this.left+u;else if(t===`x`){if(a===`center`)g=h((e.top+e.bottom)/2+.5);else if(G(a)){let e=Object.keys(a)[0],t=a[e];g=h(this.chart.scales[e].getPixelForValue(t))}T=e.top,D=e.bottom,x=g+m,C=x+u}else if(t===`y`){if(a===`center`)g=h((e.left+e.right)/2);else if(G(a)){let e=Object.keys(a)[0],t=a[e];g=h(this.chart.scales[e].getPixelForValue(t))}b=g-m,S=b-u,w=e.left,E=e.right}let O=K(r.ticks.maxTicksLimit,l),k=Math.max(1,Math.ceil(l/O));for(_=0;_0&&(a-=r/2)}f={left:a,top:i,width:r+t.width,height:n+t.height,color:e.backdropColor}}h.push({label:y,font:w,textOffset:D,options:{rotation:m,color:n,strokeColor:s,strokeWidth:l,textAlign:d,textBaseline:O,translation:[b,x],backdrop:f}})}return h}_getXAxisLabelAlignment(){let{position:e,ticks:t}=this.options;if(-Xa(this.labelRotation))return e===`top`?`left`:`right`;let n=`center`;return t.align===`start`?n=`left`:t.align===`end`?n=`right`:t.align===`inner`&&(n=`inner`),n}_getYAxisLabelAlignment(e){let{position:t,ticks:{crossAlign:n,mirror:r,padding:i}}=this.options,a=this._getLabelSizes(),o=e+i,s=a.widest.width,c,l;return t===`left`?r?(l=this.right+i,n===`near`?c=`left`:n===`center`?(c=`center`,l+=s/2):(c=`right`,l+=s)):(l=this.right-o,n===`near`?c=`right`:n===`center`?(c=`center`,l-=s/2):(c=`left`,l=this.left)):t===`right`?r?(l=this.left+i,n===`near`?c=`right`:n===`center`?(c=`center`,l-=s/2):(c=`left`,l-=s)):(l=this.left+o,n===`near`?c=`left`:n===`center`?(c=`center`,l+=s/2):(c=`right`,l=this.right)):c=`right`,{textAlign:c,x:l}}_computeLabelArea(){if(this.options.ticks.mirror)return;let e=this.chart,t=this.options.position;if(t===`left`||t===`right`)return{top:0,left:this.left,bottom:e.height,right:this.right};if(t===`top`||t===`bottom`)return{top:this.top,left:0,bottom:this.bottom,right:e.width}}drawBackground(){let{ctx:e,options:{backgroundColor:t},left:n,top:r,width:i,height:a}=this;t&&(e.save(),e.fillStyle=t,e.fillRect(n,r,i,a),e.restore())}getLineWidthForValue(e){let t=this.options.grid;if(!this._isVisible()||!t.display)return 0;let n=this.ticks.findIndex(t=>t.value===e);return n>=0?t.setContext(this.getContext(n)).lineWidth:0}drawGrid(e){let t=this.options.grid,n=this.ctx,r=this._gridLineItems||(this._gridLineItems=this._computeGridLineItems(e)),i,a,o=(e,t,r)=>{r.width&&r.color&&(n.save(),n.lineWidth=r.width,n.strokeStyle=r.color,n.setLineDash(r.borderDash||[]),n.lineDashOffset=r.borderDashOffset,n.beginPath(),n.moveTo(e.x,e.y),n.lineTo(t.x,t.y),n.stroke(),n.restore())};if(t.display)for(i=0,a=r.length;i{this.draw(e)}}]:[{z:r,draw:e=>{this.drawBackground(),this.drawGrid(e),this.drawTitle()}},{z:i,draw:()=>{this.drawBorder()}},{z:n,draw:e=>{this.drawLabels(e)}}]}getMatchingVisibleMetas(e){let t=this.chart.getSortedVisibleDatasetMetas(),n=this.axis+`AxisID`,r=[],i,a;for(i=0,a=t.length;i{let r=n.split(`.`),i=r.pop(),a=[e].concat(r).join(`.`),o=t[n].split(`.`),s=o.pop(),c=o.join(`.`);Q.route(a,i,c,s)})}function nd(e){return`id`in e&&`defaults`in e}var rd=new class{constructor(){this.controllers=new $u(ul,`datasets`,!0),this.elements=new $u(Mu,`elements`),this.plugins=new $u(Object,`plugins`),this.scales=new $u(Qu,`scales`),this._typedRegistries=[this.controllers,this.scales,this.elements]}add(...e){this._each(`register`,e)}remove(...e){this._each(`unregister`,e)}addControllers(...e){this._each(`register`,e,this.controllers)}addElements(...e){this._each(`register`,e,this.elements)}addPlugins(...e){this._each(`register`,e,this.plugins)}addScales(...e){this._each(`register`,e,this.scales)}getController(e){return this._get(e,this.controllers,`controller`)}getElement(e){return this._get(e,this.elements,`element`)}getPlugin(e){return this._get(e,this.plugins,`plugin`)}getScale(e){return this._get(e,this.scales,`scale`)}removeControllers(...e){this._each(`unregister`,e,this.controllers)}removeElements(...e){this._each(`unregister`,e,this.elements)}removePlugins(...e){this._each(`unregister`,e,this.plugins)}removeScales(...e){this._each(`unregister`,e,this.scales)}_each(e,t,n){[...t].forEach(t=>{let r=n||this._getRegistryForType(t);n||r.isForType(t)||r===this.plugins&&t.id?this._exec(e,r,t):J(t,t=>{let r=n||this._getRegistryForType(t);this._exec(e,r,t)})})}_exec(e,t,n){let r=ja(e);q(n[`before`+r],[],n),t[e](n),q(n[`after`+r],[],n)}_getRegistryForType(e){for(let t=0;te.filter(e=>!t.some(t=>e.plugin.id===t.plugin.id));this._notify(r(t,n),e,`stop`),this._notify(r(n,t),e,`start`)}};function ad(e){let t={},n=[],r=Object.keys(rd.plugins.items);for(let e=0;e1&&fd(e[0].toLowerCase());if(t)return t}throw Error(`Cannot determine type of '${e}' axis. Please provide 'axis' or 'position' option.`)}function hd(e,t,n){if(n[t+`AxisID`]===e)return{axis:t}}function gd(e,t){if(t.data&&t.data.datasets){let n=t.data.datasets.filter(t=>t.xAxisID===e||t.yAxisID===e);if(n.length)return hd(e,`x`,n[0])||hd(e,`y`,n[0])}return{}}function _d(e,t){let n=Vo[e.type]||{scales:{}},r=t.scales||{},i=ld(e.type,t),a=Object.create(null);return Object.keys(r).forEach(t=>{let o=r[t];if(!G(o))return console.error(`Invalid scale configuration for scale: ${t}`);if(o._proxy)return console.warn(`Ignoring resolver passed as options for scale: ${t}`);let s=md(t,o,gd(t,e),Q.scales[o.type]),c=dd(s,i),l=n.scales||{};a[t]=Ta(Object.create(null),[{axis:s},o,l[s],l[c]])}),e.data.datasets.forEach(n=>{let i=n.type||e.type,o=n.indexAxis||ld(i,t),s=(Vo[i]||{}).scales||{};Object.keys(s).forEach(e=>{let t=ud(e,o),i=n[t+`AxisID`]||t;a[i]=a[i]||Object.create(null),Ta(a[i],[{axis:t},r[i],s[e]])})}),Object.keys(a).forEach(e=>{let t=a[e];Ta(t,[Q.scales[t.type],Q.scale])}),a}function vd(e){let t=e.options||(e.options={});t.plugins=K(t.plugins,{}),t.scales=_d(e,t)}function yd(e){return e=e||{},e.datasets=e.datasets||[],e.labels=e.labels||[],e}function bd(e){return e=e||{},e.data=yd(e.data),vd(e),e}var xd=new Map,Sd=new Set;function Cd(e,t){let n=xd.get(e);return n||(n=t(),xd.set(e,n),Sd.add(n)),n}var wd=(e,t,n)=>{let r=Aa(t,n);r!==void 0&&e.add(r)},Td=class{constructor(e){this._config=bd(e),this._scopeCache=new Map,this._resolverCache=new Map}get platform(){return this._config.platform}get type(){return this._config.type}set type(e){this._config.type=e}get data(){return this._config.data}set data(e){this._config.data=yd(e)}get options(){return this._config.options}set options(e){this._config.options=e}get plugins(){return this._config.plugins}update(){let e=this._config;this.clearCache(),vd(e)}clearCache(){this._scopeCache.clear(),this._resolverCache.clear()}datasetScopeKeys(e){return Cd(e,()=>[[`datasets.${e}`,``]])}datasetAnimationScopeKeys(e,t){return Cd(`${e}.transition.${t}`,()=>[[`datasets.${e}.transitions.${t}`,`transitions.${t}`],[`datasets.${e}`,``]])}datasetElementScopeKeys(e,t){return Cd(`${e}-${t}`,()=>[[`datasets.${e}.elements.${t}`,`datasets.${e}`,`elements.${t}`,``]])}pluginScopeKeys(e){let t=e.id,n=this.type;return Cd(`${n}-plugin-${t}`,()=>[[`plugins.${t}`,...e.additionalOptionScopes||[]]])}_cachedScopes(e,t){let n=this._scopeCache,r=n.get(e);return(!r||t)&&(r=new Map,n.set(e,r)),r}getOptionScopes(e,t,n){let{options:r,type:i}=this,a=this._cachedScopes(e,n),o=a.get(t);if(o)return o;let s=new Set;t.forEach(t=>{e&&(s.add(e),t.forEach(t=>wd(s,e,t))),t.forEach(e=>wd(s,r,e)),t.forEach(e=>wd(s,Vo[i]||{},e)),t.forEach(e=>wd(s,Q,e)),t.forEach(e=>wd(s,Ho,e))});let c=Array.from(s);return c.length===0&&c.push(Object.create(null)),Sd.has(t)&&a.set(t,c),c}chartOptionScopes(){let{options:e,type:t}=this;return[e,Vo[t]||{},Q.datasets[t]||{},{type:t},Q,Ho]}resolveNamedOptions(e,t,n,r=[``]){let i={$shared:!0},{resolver:a,subPrefixes:o}=Ed(this._resolverCache,e,r),s=a;if(Od(a,t)){i.$shared=!1,n=Na(n)?n():n;let t=this.createResolver(e,n,o);s=xs(a,n,t)}for(let e of t)i[e]=s[e];return i}createResolver(e,t,n=[``],r){let{resolver:i}=Ed(this._resolverCache,e,n);return G(t)?xs(i,t,void 0,r):i}};function Ed(e,t,n){let r=e.get(t);r||(r=new Map,e.set(t,r));let i=n.join(),a=r.get(i);return a||(a={resolver:bs(t,n),subPrefixes:n.filter(e=>!e.toLowerCase().includes(`hover`))},r.set(i,a)),a}var Dd=e=>G(e)&&Object.getOwnPropertyNames(e).some(t=>Na(e[t]));function Od(e,t){let{isScriptable:n,isIndexable:r}=Ss(e);for(let i of t){let t=n(i),a=r(i),o=(a||t)&&e[i];if(t&&(Na(o)||Dd(o))||a&&W(o))return!0}return!1}var kd=`4.5.1`,Ad=[`top`,`bottom`,`left`,`right`,`chartArea`];function jd(e,t){return e===`top`||e===`bottom`||Ad.indexOf(e)===-1&&t===`x`}function Md(e,t){return function(n,r){return n[e]===r[e]?n[t]-r[t]:n[e]-r[e]}}function Nd(e){let t=e.chart,n=t.options.animation;t.notifyPlugins(`afterRender`),q(n&&n.onComplete,[e],t)}function Pd(e){let t=e.chart,n=t.options.animation;q(n&&n.onProgress,[e],t)}function Fd(e){return Xs()&&typeof e==`string`?e=document.getElementById(e):e&&e.length&&(e=e[0]),e&&e.canvas&&(e=e.canvas),e}var Id={},Ld=e=>{let t=Fd(e);return Object.values(Id).filter(e=>e.canvas===t).pop()};function Rd(e,t,n){let r=Object.keys(e);for(let i of r){let r=+i;if(r>=t){let a=e[i];delete e[i],(n>0||r>t)&&(e[r+n]=a)}}}function zd(e,t,n,r){return!n||e.type===`mouseout`?null:r?t:e}var Bd=class{static register(...e){rd.add(...e),Vd()}static unregister(...e){rd.remove(...e),Vd()}constructor(e,t){let n=this.config=new Td(t),r=Fd(e),i=Ld(r);if(i)throw Error(`Canvas is already in use. Chart with ID '`+i.id+`' must be destroyed before the canvas with ID '`+i.canvas.id+`' can be reused.`);let a=n.createResolver(n.chartOptionScopes(),this.getContext());this.platform=new(n.platform||(ju(r))),this.platform.updateConfig(n);let o=this.platform.acquireContext(r,a.aspectRatio),s=o&&o.canvas,c=s&&s.height,l=s&&s.width;this.id=ha(),this.ctx=o,this.canvas=s,this.width=l,this.height=c,this._options=a,this._aspectRatio=this.aspectRatio,this._layers=[],this._metasets=[],this._stacks=void 0,this.boxes=[],this.currentDevicePixelRatio=void 0,this.chartArea=void 0,this._active=[],this._lastEvent=void 0,this._listeners={},this._responsiveListeners=void 0,this._sortedMetasets=[],this.scales={},this._plugins=new id,this.$proxies={},this._hiddenIndices={},this.attached=!1,this._animationsDisabled=void 0,this.$context=void 0,this._doResize=vo(e=>this.update(e),a.resizeDelay||0),this._dataChanges=[],Id[this.id]=this,!o||!s?console.error(`Failed to create chart: can't acquire context from the given item`):(Rc.listen(this,`complete`,Nd),Rc.listen(this,`progress`,Pd),this._initialize(),this.attached&&this.update())}get aspectRatio(){let{options:{aspectRatio:e,maintainAspectRatio:t},width:n,height:r,_aspectRatio:i}=this;return U(e)?t&&i?i:r?n/r:null:e}get data(){return this.config.data}set data(e){this.config.data=e}get options(){return this._options}set options(e){this.config.options=e}get registry(){return rd}_initialize(){return this.notifyPlugins(`beforeInit`),this.options.responsive?this.resize():lc(this,this.options.devicePixelRatio),this.bindEvents(),this.notifyPlugins(`afterInit`),this}clear(){return Yo(this.canvas,this.ctx),this}stop(){return Rc.stop(this),this}resize(e,t){Rc.running(this)?this._resizeBeforeDraw={width:e,height:t}:this._resize(e,t)}_resize(e,t){let n=this.options,r=this.canvas,i=n.maintainAspectRatio&&this.aspectRatio,a=this.platform.getMaximumSize(r,e,t,i),o=n.devicePixelRatio||this.platform.getDevicePixelRatio(),s=this.width?`resize`:`attach`;this.width=a.width,this.height=a.height,this._aspectRatio=this.aspectRatio,lc(this,o,!0)&&(this.notifyPlugins(`resize`,{size:a}),q(n.onResize,[this,a],this),this.attached&&this._doResize(s)&&this.render())}ensureScalesHaveIDs(){J(this.options.scales||{},(e,t)=>{e.id=t})}buildOrUpdateScales(){let e=this.options,t=e.scales,n=this.scales,r=Object.keys(n).reduce((e,t)=>(e[t]=!1,e),{}),i=[];t&&(i=i.concat(Object.keys(t).map(e=>{let n=t[e],r=md(e,n),i=r===`r`,a=r===`x`;return{options:n,dposition:i?`chartArea`:a?`bottom`:`left`,dtype:i?`radialLinear`:a?`category`:`linear`}}))),J(i,t=>{let i=t.options,a=i.id,o=md(a,i),s=K(i.type,t.dtype);(i.position===void 0||jd(i.position,o)!==jd(t.dposition))&&(i.position=t.dposition),r[a]=!0;let c=null;a in n&&n[a].type===s?c=n[a]:(c=new(rd.getScale(s))({id:a,type:s,ctx:this.ctx,chart:this}),n[c.id]=c),c.init(i,e)}),J(r,(e,t)=>{e||delete n[t]}),J(n,e=>{cu.configure(this,e,e.options),cu.addBox(this,e)})}_updateMetasets(){let e=this._metasets,t=this.data.datasets.length,n=e.length;if(e.sort((e,t)=>e.index-t.index),n>t){for(let e=t;et.length&&delete this._stacks,e.forEach((e,n)=>{t.filter(t=>t===e._dataset).length===0&&this._destroyDatasetMeta(n)})}buildOrUpdateControllers(){let e=[],t=this.data.datasets,n,r;for(this._removeUnreferencedMetasets(),n=0,r=t.length;n{this.getDatasetMeta(t).controller.reset()},this)}reset(){this._resetElements(),this.notifyPlugins(`reset`)}update(e){let t=this.config;t.update();let n=this._options=t.createResolver(t.chartOptionScopes(),this.getContext()),r=this._animationsDisabled=!n.animation;if(this._updateScales(),this._checkEventBindings(),this._updateHiddenIndices(),this._plugins.invalidate(),this.notifyPlugins(`beforeUpdate`,{mode:e,cancelable:!0})===!1)return;let i=this.buildOrUpdateControllers();this.notifyPlugins(`beforeElementsUpdate`);let a=0;for(let e=0,t=this.data.datasets.length;e{e.reset()}),this._updateDatasets(e),this.notifyPlugins(`afterUpdate`,{mode:e}),this._layers.sort(Md(`z`,`_idx`));let{_active:o,_lastEvent:s}=this;s?this._eventHandler(s,!0):o.length&&this._updateHoverStyles(o,o,!0),this.render()}_updateScales(){J(this.scales,e=>{cu.removeBox(this,e)}),this.ensureScalesHaveIDs(),this.buildOrUpdateScales()}_checkEventBindings(){let e=this.options;(!Pa(new Set(Object.keys(this._listeners)),new Set(e.events))||!!this._responsiveListeners!==e.responsive)&&(this.unbindEvents(),this.bindEvents())}_updateHiddenIndices(){let{_hiddenIndices:e}=this,t=this._getUniformDataChanges()||[];for(let{method:n,start:r,count:i}of t)Rd(e,r,n===`_removeElements`?-i:i)}_getUniformDataChanges(){let e=this._dataChanges;if(!e||!e.length)return;this._dataChanges=[];let t=this.data.datasets.length,n=t=>new Set(e.filter(e=>e[0]===t).map((e,t)=>t+`,`+e.splice(1).join(`,`))),r=n(0);for(let e=1;ee.split(`,`)).map(e=>({method:e[1],start:+e[2],count:+e[3]}))}_updateLayout(e){if(this.notifyPlugins(`beforeLayout`,{cancelable:!0})===!1)return;cu.update(this,this.width,this.height,e);let t=this.chartArea,n=t.width<=0||t.height<=0;this._layers=[],J(this.boxes,e=>{n&&e.position===`chartArea`||(e.configure&&e.configure(),this._layers.push(...e._layers()))},this),this._layers.forEach((e,t)=>{e._idx=t}),this.notifyPlugins(`afterLayout`)}_updateDatasets(e){if(this.notifyPlugins(`beforeDatasetsUpdate`,{mode:e,cancelable:!0})!==!1){for(let e=0,t=this.data.datasets.length;e=0;--t)this._drawDataset(e[t]);this.notifyPlugins(`afterDatasetsDraw`)}_drawDataset(e){let t=this.ctx,n={meta:e,index:e.index,cancelable:!0},r=Pc(this,e);this.notifyPlugins(`beforeDatasetDraw`,n)!==!1&&(r&&$o(t,r),e.controller.draw(),r&&es(t),n.cancelable=!1,this.notifyPlugins(`afterDatasetDraw`,n))}isPointInArea(e){return Qo(e,this.chartArea,this._minPadding)}getElementsAtEventForMode(e,t,n,r){let i=Gl.modes[t];return typeof i==`function`?i(this,e,n,r):[]}getDatasetMeta(e){let t=this.data.datasets[e],n=this._metasets,r=n.filter(e=>e&&e._dataset===t).pop();return r||(r={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null,order:t&&t.order||0,index:e,_dataset:t,_parsed:[],_sorted:!1},n.push(r)),r}getContext(){return this.$context||(this.$context=ys(null,{chart:this,type:`chart`}))}getVisibleDatasetCount(){return this.getSortedVisibleDatasetMetas().length}isDatasetVisible(e){let t=this.data.datasets[e];if(!t)return!1;let n=this.getDatasetMeta(e);return typeof n.hidden==`boolean`?!n.hidden:!t.hidden}setDatasetVisibility(e,t){let n=this.getDatasetMeta(e);n.hidden=!t}toggleDataVisibility(e){this._hiddenIndices[e]=!this._hiddenIndices[e]}getDataVisibility(e){return!this._hiddenIndices[e]}_updateVisibility(e,t,n){let r=n?`show`:`hide`,i=this.getDatasetMeta(e),a=i.controller._resolveAnimations(void 0,r);Ma(t)?(i.data[t].hidden=!n,this.update()):(this.setDatasetVisibility(e,n),a.update(i,{visible:n}),this.update(t=>t.datasetIndex===e?r:void 0))}hide(e,t){this._updateVisibility(e,t,!1)}show(e,t){this._updateVisibility(e,t,!0)}_destroyDatasetMeta(e){let t=this._metasets[e];t&&t.controller&&t.controller._destroy(),delete this._metasets[e]}_stop(){let e,t;for(this.stop(),Rc.remove(this),e=0,t=this.data.datasets.length;e{t.addEventListener(this,n,r),e[n]=r},r=(e,t,n)=>{e.offsetX=t,e.offsetY=n,this._eventHandler(e)};J(this.options.events,e=>n(e,r))}bindResponsiveEvents(){this._responsiveListeners||(this._responsiveListeners={});let e=this._responsiveListeners,t=this.platform,n=(n,r)=>{t.addEventListener(this,n,r),e[n]=r},r=(n,r)=>{e[n]&&(t.removeEventListener(this,n,r),delete e[n])},i=(e,t)=>{this.canvas&&this.resize(e,t)},a,o=()=>{r(`attach`,o),this.attached=!0,this.resize(),n(`resize`,i),n(`detach`,a)};a=()=>{this.attached=!1,r(`resize`,i),this._stop(),this._resize(0,0),n(`attach`,o)},t.isAttached(this.canvas)?o():a()}unbindEvents(){J(this._listeners,(e,t)=>{this.platform.removeEventListener(this,t,e)}),this._listeners={},J(this._responsiveListeners,(e,t)=>{this.platform.removeEventListener(this,t,e)}),this._responsiveListeners=void 0}updateHoverStyle(e,t,n){let r=n?`set`:`remove`,i,a,o,s;for(t===`dataset`&&(i=this.getDatasetMeta(e[0].datasetIndex),i.controller[`_`+r+`DatasetHoverStyle`]()),o=0,s=e.length;o{let n=this.getDatasetMeta(e);if(!n)throw Error(`No dataset found at index `+e);return{datasetIndex:e,element:n.data[t],index:t}});ba(n,t)||(this._active=n,this._lastEvent=null,this._updateHoverStyles(n,t))}notifyPlugins(e,t,n){return this._plugins.notify(this,e,t,n)}isPluginEnabled(e){return this._plugins._cache.filter(t=>t.plugin.id===e).length===1}_updateHoverStyles(e,t,n){let r=this.options.hover,i=(e,t)=>e.filter(e=>!t.some(t=>e.datasetIndex===t.datasetIndex&&e.index===t.index)),a=i(t,e),o=n?e:i(e,t);a.length&&this.updateHoverStyle(a,r.mode,!1),o.length&&r.mode&&this.updateHoverStyle(o,r.mode,!0)}_eventHandler(e,t){let n={event:e,replay:t,cancelable:!0,inChartArea:this.isPointInArea(e)},r=t=>(t.options.events||this.options.events).includes(e.native.type);if(this.notifyPlugins(`beforeEvent`,n,r)===!1)return;let i=this._handleEvent(e,t,n.inChartArea);return n.cancelable=!1,this.notifyPlugins(`afterEvent`,n,r),(i||n.changed)&&this.render(),this}_handleEvent(e,t,n){let{_active:r=[],options:i}=this,a=t,o=this._getActiveElements(e,r,n,a),s=Fa(e),c=zd(e,this._lastEvent,n,s);n&&(this._lastEvent=null,q(i.onHover,[e,o,this],this),s&&q(i.onClick,[e,o,this],this));let l=!ba(o,r);return(l||t)&&(this._active=o,this._updateHoverStyles(o,r,t)),this._lastEvent=c,l}_getActiveElements(e,t,n,r){if(e.type===`mouseout`)return[];if(!n)return t;let i=this.options.hover;return this.getElementsAtEventForMode(e,i.mode,i,r)}};$(Bd,`defaults`,Q),$(Bd,`instances`,Id),$(Bd,`overrides`,Vo),$(Bd,`registry`,rd),$(Bd,`version`,kd),$(Bd,`getChart`,Ld);function Vd(){return J(Bd.instances,e=>e._plugins.invalidate())}function Hd(e,t,n){let{startAngle:r,x:i,y:a,outerRadius:o,innerRadius:s,options:c}=t,{borderWidth:l,borderJoinStyle:u}=c,d=Math.min(l/o,no(r-n));if(e.beginPath(),e.arc(i,a,o-l/2,r+d/2,n-d/2),s>0){let t=Math.min(l/s,no(r-n));e.arc(i,a,s+l/2,n-t/2,r+t/2,!0)}else{let t=Math.min(l/2,o*no(r-n));if(u===`round`)e.arc(i,a,t,n-Y/2,r+Y/2,!0);else if(u===`bevel`){let o=2*t*t,s=-o*Math.cos(n+Y/2)+i,c=-o*Math.sin(n+Y/2)+a,l=o*Math.cos(r+Y/2)+i,u=o*Math.sin(r+Y/2)+a;e.lineTo(s,c),e.lineTo(l,u)}}e.closePath(),e.moveTo(0,0),e.rect(0,0,e.canvas.width,e.canvas.height),e.clip(`evenodd`)}function Ud(e,t,n){let{startAngle:r,pixelMargin:i,x:a,y:o,outerRadius:s,innerRadius:c}=t,l=i/s;e.beginPath(),e.arc(a,o,s,r-l,n+l),c>i?(l=i/c,e.arc(a,o,c,n+l,r-l,!0)):e.arc(a,o,i,n+Z,r-Z),e.closePath(),e.clip()}function Wd(e){return fs(e,[`outerStart`,`outerEnd`,`innerStart`,`innerEnd`])}function Gd(e,t,n,r){let i=Wd(e.options.borderRadius),a=(n-t)/2,o=Math.min(a,r*t/2),s=e=>{let t=(n-Math.min(a,e))*r/2;return io(e,0,Math.min(a,t))};return{outerStart:s(i.outerStart),outerEnd:s(i.outerEnd),innerStart:io(i.innerStart,0,o),innerEnd:io(i.innerEnd,0,o)}}function Kd(e,t,n,r){return{x:n+e*Math.cos(t),y:r+e*Math.sin(t)}}function qd(e,t,n,r,i,a){let{x:o,y:s,startAngle:c,pixelMargin:l,innerRadius:u}=t,d=Math.max(t.outerRadius+r+n-l,0),f=u>0?u+r+n+l:0,p=0,m=i-c;if(r){let e=((u>0?u-r:0)+(d>0?d-r:0))/2;p=(m-(e===0?m:m*e/(e+r)))/2}let h=(m-Math.max(.001,m*d-n/Y)/d)/2,g=c+h+p,_=i-h-p,{outerStart:v,outerEnd:y,innerStart:b,innerEnd:x}=Gd(t,f,d,_-g),S=d-v,C=d-y,w=g+v/S,T=_-y/C,E=f+b,D=f+x,O=g+b/E,k=_-x/D;if(e.beginPath(),a){let t=(w+T)/2;if(e.arc(o,s,d,w,t),e.arc(o,s,d,t,T),y>0){let t=Kd(C,T,o,s);e.arc(t.x,t.y,y,T,_+Z)}let n=Kd(D,_,o,s);if(e.lineTo(n.x,n.y),x>0){let t=Kd(D,k,o,s);e.arc(t.x,t.y,x,_+Z,k+Math.PI)}let r=(_-x/f+(g+b/f))/2;if(e.arc(o,s,f,_-x/f,r,!0),e.arc(o,s,f,r,g+b/f,!0),b>0){let t=Kd(E,O,o,s);e.arc(t.x,t.y,b,O+Math.PI,g-Z)}let i=Kd(S,g,o,s);if(e.lineTo(i.x,i.y),v>0){let t=Kd(S,w,o,s);e.arc(t.x,t.y,v,g-Z,w)}}else{e.moveTo(o,s);let t=Math.cos(w)*d+o,n=Math.sin(w)*d+s;e.lineTo(t,n);let r=Math.cos(T)*d+o,i=Math.sin(T)*d+s;e.lineTo(r,i)}e.closePath()}function Jd(e,t,n,r,i){let{fullCircles:a,startAngle:o,circumference:s}=t,c=t.endAngle;if(a){qd(e,t,n,r,c,i);for(let t=0;t=Y&&p===0&&u!==`miter`&&Hd(e,t,h),a||(qd(e,t,n,r,h,i),e.stroke())}var Xd=class extends Mu{constructor(e){super(),$(this,`circumference`,void 0),$(this,`endAngle`,void 0),$(this,`fullCircles`,void 0),$(this,`innerRadius`,void 0),$(this,`outerRadius`,void 0),$(this,`pixelMargin`,void 0),$(this,`startAngle`,void 0),this.options=void 0,this.circumference=void 0,this.startAngle=void 0,this.endAngle=void 0,this.innerRadius=void 0,this.outerRadius=void 0,this.pixelMargin=0,this.fullCircles=0,e&&Object.assign(this,e)}inRange(e,t,n){let{angle:r,distance:i}=$a(this.getProps([`x`,`y`],n),{x:e,y:t}),{startAngle:a,endAngle:o,innerRadius:s,outerRadius:c,circumference:l}=this.getProps([`startAngle`,`endAngle`,`innerRadius`,`outerRadius`,`circumference`],n),u=(this.options.spacing+this.options.borderWidth)/2,d=K(l,o-a),f=ro(r,a,o)&&a!==o,p=d>=X||f,m=oo(i,s+u,c+u);return p&&m}getCenterPoint(e){let{x:t,y:n,startAngle:r,endAngle:i,innerRadius:a,outerRadius:o}=this.getProps([`x`,`y`,`startAngle`,`endAngle`,`innerRadius`,`outerRadius`],e),{offset:s,spacing:c}=this.options,l=(r+i)/2,u=(a+o+c+s)/2;return{x:t+Math.cos(l)*u,y:n+Math.sin(l)*u}}tooltipPosition(e){return this.getCenterPoint(e)}draw(e){let{options:t,circumference:n}=this,r=(t.offset||0)/4,i=(t.spacing||0)/2,a=t.circular;if(this.pixelMargin=t.borderAlign===`inner`?.33:0,this.fullCircles=n>X?Math.floor(n/X):0,n===0||this.innerRadius<0||this.outerRadius<0)return;e.save();let o=(this.startAngle+this.endAngle)/2;e.translate(Math.cos(o)*r,Math.sin(o)*r);let s=r*(1-Math.sin(Math.min(Y,n||0)));e.fillStyle=t.backgroundColor,e.strokeStyle=t.borderColor,Jd(e,this,s,i,a),Yd(e,this,s,i,a),e.restore()}};$(Xd,`id`,`arc`),$(Xd,`defaults`,{borderAlign:`center`,borderColor:`#fff`,borderDash:[],borderDashOffset:0,borderJoinStyle:void 0,borderRadius:0,borderWidth:2,offset:0,spacing:0,angle:void 0,circular:!0,selfJoin:!1}),$(Xd,`defaultRoutes`,{backgroundColor:`backgroundColor`}),$(Xd,`descriptors`,{_scriptable:!0,_indexable:e=>e!==`borderDash`});function Zd(e,t,n=t){e.lineCap=K(n.borderCapStyle,t.borderCapStyle),e.setLineDash(K(n.borderDash,t.borderDash)),e.lineDashOffset=K(n.borderDashOffset,t.borderDashOffset),e.lineJoin=K(n.borderJoinStyle,t.borderJoinStyle),e.lineWidth=K(n.borderWidth,t.borderWidth),e.strokeStyle=K(n.borderColor,t.borderColor)}function Qd(e,t,n){e.lineTo(n.x,n.y)}function $d(e){return e.stepped?ts:e.tension||e.cubicInterpolationMode===`monotone`?ns:Qd}function ef(e,t,n={}){let r=e.length,{start:i=0,end:a=r-1}=n,{start:o,end:s}=t,c=Math.max(i,o),l=Math.min(a,s),u=is&&a>s;return{count:r,start:c,loop:t.loop,ilen:l(o+(l?s-e:e))%a,y=()=>{h!==g&&(e.lineTo(u,g),e.lineTo(u,h),e.lineTo(u,_))};for(c&&(p=i[v(0)],e.moveTo(p.x,p.y)),f=0;f<=s;++f){if(p=i[v(f)],p.skip)continue;let t=p.x,n=p.y,r=t|0;r===m?(ng&&(g=n),u=(d*u+t)/++d):(y(),e.lineTo(t,n),m=r,d=0,h=g=n),_=n}y()}function rf(e){let t=e.options,n=t.borderDash&&t.borderDash.length;return!e._decimated&&!e._loop&&!t.tension&&t.cubicInterpolationMode!==`monotone`&&!t.stepped&&!n?nf:tf}function af(e){return e.stepped?pc:e.tension||e.cubicInterpolationMode===`monotone`?mc:fc}function of(e,t,n,r){let i=t._path;i||(i=t._path=new Path2D,t.path(i,n,r)&&i.closePath()),Zd(e,t.options),e.stroke(i)}function sf(e,t,n,r){let{segments:i,options:a}=t,o=rf(t);for(let s of i)Zd(e,a,s.style),e.beginPath(),o(e,t,s,{start:n,end:n+r-1})&&e.closePath(),e.stroke()}var cf=typeof Path2D==`function`;function lf(e,t,n,r){cf&&!t.options.segment?of(e,t,n,r):sf(e,t,n,r)}var uf=class extends Mu{constructor(e){super(),this.animated=!0,this.options=void 0,this._chart=void 0,this._loop=void 0,this._fullLoop=void 0,this._path=void 0,this._points=void 0,this._segments=void 0,this._decimated=!1,this._pointsUpdated=!1,this._datasetIndex=void 0,e&&Object.assign(this,e)}updateControlPoints(e,t){let n=this.options;if((n.tension||n.cubicInterpolationMode===`monotone`)&&!n.stepped&&!this._pointsUpdated){let r=n.spanGaps?this._loop:this._fullLoop;Ys(this._points,n,e,r,t),this._pointsUpdated=!0}}set points(e){this._points=e,delete this._segments,delete this._path,this._pointsUpdated=!1}get points(){return this._points}get segments(){return this._segments||(this._segments=Dc(this,this.options.segment))}first(){let e=this.segments,t=this.points;return e.length&&t[e[0].start]}last(){let e=this.segments,t=this.points,n=e.length;return n&&t[e[n-1].end]}interpolate(e,t){let n=this.options,r=e[t],i=this.points,a=wc(this,{property:t,start:r,end:r});if(!a.length)return;let o=[],s=af(n),c,l;for(c=0,l=a.length;ce!==`borderDash`&&e!==`fill`});function df(e,t,n,r){let i=e.options,{[n]:a}=e.getProps([n],r);return Math.abs(t-a)e+t)/r.size,y:i/a}},nearest(e,t){if(!e.length)return!1;let n=t.x,r=t.y,i=1/0,a,o,s;for(a=0,o=e.length;ar&&n.sort(a),t=n.shift(),r=n.length,8&t.__g&&(s=void 0,c=void 0,l=(c=(o=t).__v).__e,u=[],d=[],(f=o.__P)&&((s=h({constructor:void 0},c)).__v=c.__v+1,e.vnode&&e.vnode(s),M(f,s,c,o.__n,f.namespaceURI,32&c.__u?[l]:null,u,l||b(c),32&c.__u,d),s.__v=c.__v,s.__.__k[s.__i]=s,te(u,s,d),c.__=c.__e=null,s.__e!=l&&x(s)))}finally{n.length=i=0}}function w(e,t,n,r,i,a,o,s,c,l,f){var p,m,h,g,_,v,y=r.__k||d,b=t.length;for(c=T(n,t,y,c,b),p=0;pc?x--:x++,S=!0)):(~l||(i>g?x--:i>1]+!!u){for(i=n-1,a=n+1;i>=0||a=0?i--:a++])&&!(2&l.__u)&&s==l.key&&c==l.type)return o}return-1}function k(e,t,n){n??(n=``),t[0]==`-`?e.setProperty(t,n):e[t]=n}function A(e,t,n,r,i){var a,o;n:if(t==`style`){if(typeof n==`string`)e.style.cssText=n;else{if(typeof r==`string`&&(e.style.cssText=r=``),r)for(t in r)n&&t in n||k(e.style,t,``);if(n)for(t in n)r&&n[t]==r[t]||k(e.style,t,n[t])}}else if(t[0]==`o`&&t[1]==`n`)(e.__e||(e.__e={}))[t]=n,n&&r||(o=l[t]||(l[t]=j(t)),(e.__a||(e.__a={}))[t]=c,a=t!=(t=t.replace(s,`$1`)),(t=t.slice(2))[0]<`a`&&(t=t.toLowerCase()),n?e.addEventListener(t,o,a):e.removeEventListener(t,o,a));else{if(i==`http://www.w3.org/2000/svg`)t=t.replace(/xlink(H|:h)/,`h`).replace(/sName$/,`s`);else if(t!=`width`&&t!=`height`&&t!=`href`&&t!=`list`&&t!=`form`&&t!=`tabIndex`&&t!=`download`&&t!=`rowSpan`&&t!=`colSpan`&&t!=`role`&&t!=`popover`&&t in e)try{e[t]=n??``;break n}catch{}typeof n==`function`||(n==null||!1===n&&t[4]!=`-`?e.removeAttribute(t):e.setAttribute(t,t==`popover`&&n==1?``:n))}}function j(t){return function(n){if(this.__e){var r=this.__e[t];if(n[o]==null)n[o]=c++;else if(n[o]=n.__.length&&n.__.push({}),n.__[e]}function F(e){return pe=1,Ce(Fe,e)}function Ce(e,t,n){var r=Se(le++,2);if(r.t=e,!r.__c&&(r.__=[n?n(t):Fe(void 0,t),function(e){var t=r.__N?r.__N[0]:r.__[0],n=r.t(t,e);fe(t,n)||(r.__N=[n,r.__[1]],r.__c.setState({}))}],r.__c=N,!N.__f)){N.__f=!0;var i=N.shouldComponentUpdate;N.shouldComponentUpdate=function(e,t,n){var r=this.__H;if(!r)return!0;var a=!1,o=this.props!=e;if(r.__.some(function(e){e.__N&&(a=!0,fe(e.__[0],e.__N[0])||(o=!0))}),i){var s=i.call(this,e,t,n);return a?s||o:s}return!a||o}}return r.__}function I(e,t){var n=Se(le++,3);!P.__s&&Pe(n.__H,t)&&(n.__P=!0,n.__=e,n.u=t,N.__H.__h.push(n))}function we(e){return pe=5,Te(function(){return{current:e}},[])}function Te(e,t){var n=Se(le++,7);return Pe(n.__H,t)&&(n.__=e(),n.__H=t),n.__}function Ee(e,t){return pe=8,Te(function(){return e},t)}function De(){var e=Se(le++,11);if(!e.__){for(var t=N.__v;!t.__m&&t.__;)t=t.__;var n=t.__m||(t.__m=[0,0]);e.__=`P`+n[0]+`-`+n[1]++}return e.__}function Oe(){var e;do{for(;e=he.shift();)try{Me(e)}catch(t){P.__e(t,{__:(e=e.__P)&&e.__v})}for(;e=me.shift();){var t=e.__H;if(e.__P&&t)try{t.__h.some(Me),t.__h.some(Ne),t.__h=[]}catch(n){t.__h=[],P.__e(n,e.__v)}}}while(he.length)}P.__b=function(e){N=null,ge&&ge(e)},P.__=function(e,t){e&&t.__k&&t.__k.__m&&(e.__m=t.__k.__m),xe&&xe(e,t)},P.__r=function(e){_e&&_e(e),le=0;var t=(N=e.__c).__H;t&&(ue==N?N.__h=[]:(t.__h.some(Me),t.__h.some(Ne),le=0),t.__h=[],t.__.some(function(e){e.__N&&(e.__=e.__N),e.u=e.__N=void 0})),ue=N},P.diffed=function(e){ve&&ve(e);var t=e.__c;t&&t.__H&&(t.__H.__h.length&&je(me.push(t)),t.__H.__.some(function(e){e.u&&(e.__H=e.u)})),ue=N=null},P.__c=function(e,t){t.some(function(e){try{e.__h.some(Me),e.__h=e.__h.filter(function(e){return!e.__||Ne(e)})}catch(n){t.some(function(e){e.__h&&(e.__h=[])}),t=[],P.__e(n,e.__v)}}),ye&&ye(e,t)},P.unmount=function(e){be&&be(e);var t,n,r=e.__c;r&&r.__H&&(r.__H.__.some(function(r){try{if(r.__P&&r.__c){if(n===void 0){for(n=e.__;n&&(!n.__c||!n.__c.__P);)n=n.__;n=n&&n.__c}r.__P=n,je(he.push(r))}else Me(r)}catch(e){t=e}}),r.__H=void 0,t&&P.__e(t,r.__v))};var ke=typeof requestAnimationFrame==`function`;function Ae(e){var t,n=function(){clearTimeout(r),ke&&cancelAnimationFrame(t),setTimeout(e)},r=setTimeout(n,35);ke&&(t=requestAnimationFrame(n))}function je(e){e!=1&&de==P.requestAnimationFrame||((de=P.requestAnimationFrame)||Ae)(Oe)}function Me(e){var t=N,n=e.__c;typeof n==`function`&&(e.__c=void 0,n()),N=t}function Ne(e){var t=N;e.__c=e.__(),N=t}function Pe(e,t){return!e||e.length!=t.length||t.some(function(t,n){return!fe(t,e[n])})}function Fe(e,t){return typeof t==`function`?t(e):t}var Ie=Object.fromEntries([{route:`home`,hash:`#home`,label:`대시보드`,title:`대시보드`,description:`네트워크, 장치와 시스템 상태를 한눈에 확인합니다.`},{route:`activity`,hash:`#activity`,label:`최근 활동`,title:`최근 활동`,description:`현재 부팅 이후 인터넷, 보호, 업데이트와 진단 상태 변화를 확인합니다.`},{route:`network`,hash:`#network`,label:`네트워크`,title:`네트워크`,description:`인터넷 연결과 내부 네트워크 주소, DHCP 및 네트워크 충돌을 한 곳에서 관리합니다.`},{route:`wifi`,hash:`#wifi`,label:`Wi-Fi`,title:`Wi-Fi`,description:`기본 무선 네트워크의 이름, 보안과 사용 상태를 관리합니다.`},{route:`iptv`,hash:`#iptv`,label:`IPTV`,title:`IPTV`,description:`SK Broadband와 LG U+ 멀티캐스트 IPTV를 설정합니다.`},{route:`devices`,hash:`#devices`,label:`연결된 기기`,title:`연결된 기기`,description:`네트워크에서 확인된 기기와 연결 방식을 살펴봅니다.`},{route:`safeshield`,hash:`#safeshield`,label:`SafeShield`,title:`SafeShield`,description:`DNS 보호 상태를 확인하고 차단 목록을 관리합니다.`},{route:`rules`,hash:`#rules`,label:`사용자 규칙`,title:`사용자 규칙`,description:`직접 허용하거나 차단할 도메인을 관리합니다.`},{route:`account`,hash:`#account`,label:`SmartSafeHub 계정`,title:`SmartSafeHub 계정`,description:`이 기기의 SmartSafeHub 계정 연결 상태와 구독 플랜을 확인합니다.`},{route:`system`,hash:`#system`,label:`업데이트`,title:`업데이트`,description:`기기의 펌웨어와 관리 소프트웨어 업데이트를 관리합니다.`},{route:`settings`,hash:`#settings`,label:`설정`,title:`설정`,description:`시스템 상태와 시간대를 확인하고 장치 관리 및 진단 기능을 설정합니다.`}].map(e=>[e.route,e])),Le=[`home`,`activity`,`network`,`wifi`,`devices`,`safeshield`,`rules`,`system`],Re=`smartsafehub.theme`;function ze(){if(typeof window>`u`)return`light`;try{let e=window.localStorage.getItem(Re);if(e===`dark`||e===`light`)return e}catch{}return window.matchMedia(`(prefers-color-scheme: dark)`).matches?`dark`:`light`}function Be(e){try{window.localStorage.setItem(Re,e)}catch{}}function Ve(e){let t=document.querySelector(`meta[name="theme-color"]`);t&&(t.content=e===`dark`?`#0f172a`:`#f8fafc`),document.documentElement.style.colorScheme=e,document.body.style.backgroundColor=e===`dark`?`#020617`:`#f8fafc`}var He=0;Array.isArray;function L(t,n,r,i,a,o){n||(n={});var s,c,l=n;if(`ref`in l&&typeof t!=`function`)for(c in l={},n)c==`ref`?s=n[c]:l[c]=n[c];var u={type:t,props:l,key:r,ref:s,__k:null,__:null,__b:0,__e:null,__c:null,constructor:void 0,__v:--He,__i:-1,__u:0};return(a||o)&&(u.__source=a,u.__self=o),e.vnode&&e.vnode(u),u}function R({children:e,title:t,...n}){return L(`svg`,{"aria-hidden":!t||void 0,fill:`none`,role:t?`img`:void 0,stroke:`currentColor`,"stroke-linecap":`round`,"stroke-linejoin":`round`,"stroke-width":`1.8`,viewBox:`0 0 24 24`,...n,children:[t?L(`title`,{children:t}):null,e]})}function Ue(e){return L(R,{...e,children:[L(`path`,{d:`M12 3 5 6v5c0 4.6 2.8 8.1 7 10 4.2-1.9 7-5.4 7-10V6l-7-3Z`}),L(`path`,{d:`m9 12 2 2 4-4`})]})}function We(e){return L(R,{...e,children:[L(`rect`,{height:`7`,rx:`2`,width:`18`,x:`3`,y:`13`}),L(`path`,{d:`M7 16.5h.01M11 16.5h.01M7 10a7 7 0 0 1 10 0M9.5 7.5a3.5 3.5 0 0 1 5 0`})]})}function Ge(e){return L(R,{...e,children:[L(`rect`,{height:`11`,rx:`2`,width:`16`,x:`4`,y:`3`}),L(`path`,{d:`M8 21h8M12 14v7`})]})}function Ke(e){return L(R,{...e,children:[L(`path`,{d:`m3 11 9-8 9 8`}),L(`path`,{d:`M5 10v10h14V10M9 20v-6h6v6`})]})}function qe(e){return L(R,{...e,children:[L(`circle`,{cx:`12`,cy:`8`,r:`4`}),L(`path`,{d:`M4.5 21a7.5 7.5 0 0 1 15 0`})]})}function Je(e){return L(R,{...e,children:[L(`path`,{d:`M12 16V4`}),L(`path`,{d:`m7 9 5-5 5 5`}),L(`path`,{d:`M5 14v5h14v-5`})]})}function Ye(e){return L(R,{...e,children:[L(`circle`,{cx:`12`,cy:`12`,r:`3`}),L(`path`,{d:`M19.4 15a1.7 1.7 0 0 0 .3 1.9l.1.1-2.8 2.8-.1-.1a1.7 1.7 0 0 0-1.9-.3 1.7 1.7 0 0 0-1 1.6v.2h-4V21a1.7 1.7 0 0 0-1-1.6 1.7 1.7 0 0 0-1.9.3l-.1.1L4.2 17l.1-.1a1.7 1.7 0 0 0 .3-1.9A1.7 1.7 0 0 0 3 14H2.8v-4H3a1.7 1.7 0 0 0 1.6-1 1.7 1.7 0 0 0-.3-1.9L4.2 7 7 4.2l.1.1a1.7 1.7 0 0 0 1.9.3A1.7 1.7 0 0 0 10 3V2.8h4V3a1.7 1.7 0 0 0 1 1.6 1.7 1.7 0 0 0 1.9-.3l.1-.1L19.8 7l-.1.1a1.7 1.7 0 0 0-.3 1.9 1.7 1.7 0 0 0 1.6 1h.2v4H21a1.7 1.7 0 0 0-1.6 1Z`})]})}function Xe(e){return L(R,{...e,children:[L(`path`,{d:`M5 9.5a10 10 0 0 1 14 0M8 13a6 6 0 0 1 8 0M11 16.5a2 2 0 0 1 2 0`}),L(`path`,{d:`M12 20h.01`})]})}function Ze(e){return L(R,{...e,children:[L(`path`,{d:`M4 4h6v6H4zM14 4h6v6h-6zM4 14h6v6H4z`}),L(`path`,{d:`M6.5 6.5h1M16.5 6.5h1M6.5 16.5h1`}),L(`path`,{d:`M14 14h2v2h-2zM18 14h2v2h-2zM16 16h2v2h-2zM18 18h2v2h-2zM14 18h1`})]})}function Qe(e){return L(R,{...e,children:[L(`path`,{d:`M7 3v4M17 3v4M5 7h14v4a7 7 0 0 1-14 0V7Z`}),L(`path`,{d:`M12 18v3`})]})}function $e(e){return L(R,{...e,children:[L(`circle`,{cx:`12`,cy:`12`,r:`9`}),L(`path`,{d:`M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18`})]})}function et(e){return L(R,{...e,children:[L(`rect`,{height:`13`,rx:`2`,width:`18`,x:`3`,y:`6`}),L(`path`,{d:`m8 2 4 4 4-4M8 22h8`})]})}function tt(e){return L(R,{...e,children:[L(`circle`,{cx:`12`,cy:`12`,r:`9`}),L(`path`,{d:`M12 7v5l3 2`})]})}function nt(e){return L(R,{...e,children:[L(`rect`,{height:`14`,rx:`2`,width:`14`,x:`5`,y:`5`}),L(`path`,{d:`M9 9h6v6H9zM9 2v3M15 2v3M9 19v3M15 19v3M2 9h3M2 15h3M19 9h3M19 15h3`})]})}function z(e){return L(R,{...e,children:[L(`path`,{d:`M20 11a8 8 0 1 0-2.3 5.7`}),L(`path`,{d:`M20 4v7h-7`})]})}function rt(e){return L(R,{...e,children:[L(`path`,{d:`M12 2v10`}),L(`path`,{d:`M18.4 6.6a8 8 0 1 1-12.8 0`})]})}function it(e){return L(R,{...e,children:[L(`path`,{d:`M12 3v12`}),L(`path`,{d:`m7 10 5 5 5-5`}),L(`path`,{d:`M5 21h14`})]})}function at(e){return L(R,{...e,children:[L(`ellipse`,{cx:`12`,cy:`5`,rx:`8`,ry:`3`}),L(`path`,{d:`M4 5v6c0 1.7 3.6 3 8 3s8-1.3 8-3V5M4 11v6c0 1.7 3.6 3 8 3s8-1.3 8-3v-6`})]})}function ot(e){return L(R,{...e,children:[L(`circle`,{cx:`8`,cy:`15`,r:`4`}),L(`path`,{d:`m11 12 8-8M15 8l2 2M17 6l2 2`})]})}function st(e){return L(R,{...e,children:[L(`path`,{d:`M2.5 12s3.5-6 9.5-6 9.5 6 9.5 6-3.5 6-9.5 6-9.5-6-9.5-6Z`}),L(`circle`,{cx:`12`,cy:`12`,r:`2.5`})]})}function ct(e){return L(R,{...e,children:[L(`path`,{d:`m3 3 18 18`}),L(`path`,{d:`M10.6 6.2A8.8 8.8 0 0 1 12 6c6 0 9.5 6 9.5 6a15.5 15.5 0 0 1-2.6 3.2`}),L(`path`,{d:`M6.2 6.3C3.8 8 2.5 12 2.5 12s3.5 6 9.5 6a9.8 9.8 0 0 0 3.1-.5`}),L(`path`,{d:`M10.3 10.3a2.5 2.5 0 0 0 3.4 3.4`})]})}function lt(e){return L(R,{...e,children:[L(`rect`,{height:`17`,rx:`2`,width:`18`,x:`3`,y:`4`}),L(`path`,{d:`M8 2v4M16 2v4M3 9h18`})]})}function ut(e){return L(R,{...e,children:[L(`circle`,{cx:`12`,cy:`12`,r:`9`}),L(`path`,{d:`m8 12 2.5 2.5L16 9`})]})}function B(e){return L(R,{...e,children:[L(`path`,{d:`M10.3 4.2 2.7 17.5A2 2 0 0 0 4.4 20h15.2a2 2 0 0 0 1.7-2.5L13.7 4.2a2 2 0 0 0-3.4 0Z`}),L(`path`,{d:`M12 9v4M12 17h.01`})]})}function dt(e){return L(R,{...e,children:L(`path`,{d:`M12 5v14M5 12h14`})})}function ft(e){return L(R,{...e,children:L(`path`,{d:`M4 7h16M9 7V4h6v3M7 7l1 13h8l1-13M10 11v5M14 11v5`})})}function pt(e){return L(R,{...e,children:[L(`circle`,{cx:`11`,cy:`11`,r:`7`}),L(`path`,{d:`m20 20-4-4`})]})}function mt(e){return L(R,{...e,children:[L(`path`,{d:`M8 6h12M8 12h12M8 18h12`}),L(`path`,{d:`M4 6h.01M4 12h.01M4 18h.01`})]})}function ht(e){return L(R,{...e,children:[L(`path`,{d:`M10 5H6a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h4`}),L(`path`,{d:`M14 8l4 4-4 4M8 12h10`})]})}function gt(e){return L(R,{...e,children:L(`path`,{d:`M4 7h16M4 12h16M4 17h16`})})}function _t(e){return L(R,{...e,children:L(`path`,{d:`m6 9 6 6 6-6`})})}function vt(e){return L(R,{...e,children:L(`path`,{d:`m6 6 12 12M18 6 6 18`})})}function yt(e){return L(R,{...e,children:L(`path`,{d:`m15 18-6-6 6-6`})})}function bt(e){return L(R,{...e,children:L(`path`,{d:`m9 18 6-6-6-6`})})}function xt(e){return L(R,{...e,children:L(`path`,{d:`M20.7 13.2A8.5 8.5 0 1 1 10.8 3.3 6.7 6.7 0 0 0 20.7 13.2Z`})})}function St(e){return L(R,{...e,children:[L(`circle`,{cx:`12`,cy:`12`,r:`4`}),L(`path`,{d:`M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41`})]})}function Ct({description:e,loading:t,onRefresh:n,onToggleTheme:r,refreshing:i,theme:a,title:o}){return L(`header`,{class:`ssh-product-hero border-b border-slate-200 bg-white px-4 py-4 sm:px-6 md:h-[4.5rem] md:py-0 lg:px-8 xl:px-10`,children:L(`div`,{class:`flex w-full items-center justify-between gap-4 md:h-full`,children:[L(`div`,{class:`min-w-0`,children:[L(`p`,{class:`m-0 text-[0.68rem] font-extrabold uppercase tracking-[0.18em] text-teal-700`,children:`SmartSafeHub`}),L(`div`,{class:`mt-1 flex min-w-0 items-baseline gap-3`,children:[L(`h1`,{class:`m-0 truncate text-xl font-black tracking-tight text-slate-950 sm:text-2xl`,children:o}),L(`p`,{class:`m-0 hidden truncate text-sm text-slate-500 lg:block`,children:e})]})]}),L(`div`,{class:`flex shrink-0 items-center gap-2`,children:[L(`button`,{"aria-label":a===`dark`?`라이트 모드로 전환`:`다크 모드로 전환`,class:`ssh-product-header-action hidden h-10 w-10 shrink-0 items-center justify-center rounded-xl border border-slate-200 bg-white text-slate-600 shadow-sm transition hover:border-teal-300 hover:text-teal-700 focus:outline-none focus-visible:ring-4 focus-visible:ring-teal-100 md:inline-flex`,onClick:r,title:a===`dark`?`라이트 모드`:`다크 모드`,type:`button`,children:L(a===`dark`?St:xt,{class:`size-5`})}),L(`button`,{"aria-busy":i,"aria-label":i?`새로고침 중`:`현재 화면 새로고침`,class:`ssh-product-header-action hidden h-10 w-10 shrink-0 items-center justify-center rounded-xl border border-slate-200 bg-white shadow-sm transition hover:border-teal-300 hover:text-teal-700 focus:outline-none focus-visible:ring-4 focus-visible:ring-teal-100 disabled:cursor-wait disabled:opacity-60 md:inline-flex ${i?`text-teal-700`:`text-slate-600`}`,disabled:t||i,onClick:n,title:i?`새로고침 중`:`새로고침`,type:`button`,children:i?L(z,{class:`size-5 animate-spin`}):L(z,{class:`size-5`})})]})]})})}var wt=`/cgi-bin/luci`;function Tt(){let e=window.location.pathname,t=e.indexOf(`/cgi-bin/luci`);return t>=0?e.slice(0,t+13):wt}function Et(e){let t=e.startsWith(`/`)?e:`/${e}`;return`${Tt()}${t}`}function Dt(e){return Et(e)}var Ot=[{label:`Overview`,routes:[`home`,`activity`]},{label:`Network`,routes:[`network`,`wifi`,`iptv`,`devices`]},{label:`Security`,routes:[`safeshield`,`rules`]},{label:`System`,routes:[`account`,`system`,`settings`]}];function kt({route:e}){switch(e){case`home`:return L(Ke,{class:`size-5`});case`activity`:return L(tt,{class:`size-5`});case`network`:return L($e,{class:`size-5`});case`wifi`:return L(Xe,{class:`size-5`});case`iptv`:return L(et,{class:`size-5`});case`devices`:return L(Ge,{class:`size-5`});case`safeshield`:return L(Ue,{class:`size-5`});case`rules`:return L(qe,{class:`size-5`});case`account`:return L(ot,{class:`size-5`});case`system`:return L(Je,{class:`size-5`});case`settings`:return L(Ye,{class:`size-5`})}}function At({theme:e}){return L(e===`dark`?St:xt,{class:`size-5`})}function jt(e){return e===`dark`?`라이트 모드`:`다크 모드`}function Mt(e,t=!1){return`relative flex min-h-11 min-w-0 items-center rounded-xl py-2.5 text-sm font-extrabold no-underline transition ${t?`justify-center px-2`:`gap-3 px-3`} ${e?`bg-teal-50 text-teal-800 ring-1 ring-inset ring-teal-200`:`text-slate-600 hover:bg-slate-50 hover:text-slate-950`}`}function Nt({accountRegistered:e,collapsed:t=!1,route:n,updateCount:r,demo:i=!1}){return L(`div`,{class:t?`space-y-3`:`space-y-5`,children:Ot.filter(e=>!i||e.routes.some(e=>Le.includes(e))).map((a,o)=>L(`section`,{"aria-label":a.label,class:t&&o>0?`border-t border-slate-100 pt-3`:void 0,children:[t?null:L(`p`,{class:`mb-2 px-3 text-[0.65rem] font-black uppercase tracking-[0.18em] text-slate-400`,children:a.label}),L(`div`,{class:`space-y-1`,children:a.routes.filter(e=>!i||Le.includes(e)).map(i=>{let a=Ie[i],o=n===i;return L(`a`,{"aria-current":o?`page`:void 0,"aria-label":i===`account`&&e===!1?`${a.label} (계정 연결 필요)`:t?a.label:void 0,class:Mt(o,t),href:a.hash,title:i===`account`&&e===!1?`SmartSafeHub 계정 연결 필요`:t?a.label:void 0,children:[L(`span`,{class:`grid size-9 shrink-0 place-items-center rounded-lg ${o?`bg-teal-700 text-white`:`bg-slate-100 text-slate-500`}`,children:L(kt,{route:i})}),t?null:L(`span`,{class:`flex min-w-0 flex-1 items-center gap-2`,children:[L(`span`,{class:`truncate`,children:a.label}),i===`iptv`?L(`span`,{class:`rounded-full border border-amber-300 bg-amber-100 px-1.5 py-0.5 text-[9px] font-black uppercase tracking-[0.08em] text-amber-800`,children:`Beta`}):null]}),t&&i===`iptv`?L(`span`,{"aria-label":`Beta 기능`,class:`absolute right-0.5 top-0.5 inline-flex size-5 items-center justify-center rounded-full border border-amber-300 bg-amber-100 text-[13px] font-extrabold leading-none text-amber-900 shadow-sm ring-2 ring-white`,children:`β`}):null,i===`account`&&e===!1?L(`span`,{"aria-label":`계정 연결 필요`,class:`ssh-account-nav-dot ${t?`ssh-account-nav-dot-collapsed`:``}`,"aria-hidden":`true`,title:`계정 연결 필요`}):null,i===`system`&&r>0?t?L(`span`,{"aria-label":`${r}개의 업데이트 유형`,class:`ssh-update-nav-badge`,"data-collapsed":`true`,children:r}):L(`span`,{class:`ssh-update-nav-badge`,"data-collapsed":`false`,children:r}):null]},i)})})]},a.label))})}function Pt({accountRegistered:e,collapsed:t,loading:n,onRefresh:r,onToggleCollapsed:i,onToggleTheme:a,refreshing:o,route:s,theme:c,title:l,updateCount:u,demo:d=!1}){let[f,p]=F(!1),m=jt(c);return I(()=>{p(!1)},[s]),I(()=>{if(!f)return;let e=e=>{e.key===`Escape`&&p(!1)};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[f]),L(v,{children:[L(`nav`,{"aria-label":`SmartSafeHub 모바일 메뉴`,class:`ssh-mobile-navigation sticky top-0 z-40 border-b border-slate-200 bg-white/95 px-4 shadow-sm shadow-slate-900/5 backdrop-blur md:hidden`,children:[L(`div`,{class:`flex min-h-16 items-center justify-between gap-3`,children:[L(`a`,{class:`flex min-w-0 items-center gap-3 no-underline`,href:`#home`,children:[L(`span`,{class:`grid size-10 shrink-0 place-items-center rounded-xl bg-slate-950 text-teal-300`,children:L(Ue,{class:`size-5`})}),L(`span`,{class:`min-w-0`,children:[L(`strong`,{class:`block truncate text-sm font-black text-slate-950`,children:`SmartSafeHub`}),L(`span`,{class:`block truncate text-xs font-semibold text-slate-500`,children:l})]})]}),L(`div`,{class:`flex shrink-0 items-center gap-2`,children:[L(`button`,{"aria-label":`${m}로 전환`,class:`inline-flex size-11 shrink-0 items-center justify-center rounded-xl border border-slate-200 bg-white text-slate-600 transition hover:border-slate-300 hover:bg-slate-50 hover:text-teal-700 focus:outline-none focus-visible:ring-4 focus-visible:ring-teal-100`,onClick:a,title:m,type:`button`,children:L(At,{theme:c})}),L(`button`,{"aria-busy":o,"aria-label":o?`새로고침 중`:`현재 화면 새로고침`,class:`inline-flex size-11 shrink-0 items-center justify-center rounded-xl border border-slate-200 bg-white transition hover:border-slate-300 hover:bg-slate-50 hover:text-teal-700 focus:outline-none focus-visible:ring-4 focus-visible:ring-teal-100 disabled:cursor-wait disabled:opacity-60 ${o?`text-teal-700`:`text-slate-600`}`,disabled:n||o,onClick:r,title:o?`새로고침 중`:`새로고침`,type:`button`,children:o?L(z,{class:`size-5 animate-spin`}):L(z,{class:`size-5`})}),L(`button`,{"aria-controls":`smartsafehub-mobile-menu`,"aria-expanded":f,"aria-label":f?`모바일 메뉴 닫기`:`모바일 메뉴 열기`,class:`inline-flex size-11 shrink-0 items-center justify-center rounded-xl border border-slate-200 bg-white text-slate-700 transition hover:bg-slate-50 focus:outline-none focus-visible:ring-4 focus-visible:ring-teal-100`,onClick:()=>p(e=>!e),type:`button`,children:L(f?vt:gt,{class:`size-5`})})]})]}),f?L(`div`,{class:`ssh-mobile-menu border-t border-slate-100 py-4`,id:`smartsafehub-mobile-menu`,children:[L(Nt,{accountRegistered:e,demo:d,route:s,updateCount:u}),d?null:L(`div`,{class:`mt-5 space-y-1 border-t border-slate-100 pt-4`,children:L(`a`,{class:`${Mt(!1)} text-rose-700 hover:bg-rose-50 hover:text-rose-800`,href:Dt(`/admin/logout`),children:[L(`span`,{class:`grid size-9 shrink-0 place-items-center rounded-lg bg-rose-50 text-rose-600`,children:L(ht,{class:`size-5`})}),`로그아웃`]})})]}):null]}),L(`aside`,{class:`hidden min-h-screen border-r border-slate-200 bg-white md:flex md:flex-col`,"data-collapsed":t?`true`:`false`,children:L(`div`,{class:`sticky top-0 flex h-screen flex-col`,children:[L(`div`,{class:`flex h-[4.5rem] min-h-[4.5rem] shrink-0 items-center border-b border-slate-100 ${t?`justify-center px-2`:`gap-3 px-4`}`,children:[L(`a`,{"aria-label":`SmartSafeHub Dashboard`,class:`flex items-center no-underline ${t?`justify-center`:`min-w-0 flex-1 gap-3`}`,href:`#home`,title:t?`SmartSafeHub`:void 0,children:[L(`span`,{class:`grid size-10 shrink-0 place-items-center rounded-xl bg-slate-950 text-teal-300 shadow-sm shadow-slate-950/10`,children:L(Ue,{class:`size-5`})}),t?null:L(`div`,{class:`min-w-0`,children:[L(`strong`,{class:`block truncate text-sm font-black tracking-tight text-slate-950`,children:`SmartSafeHub`}),L(`span`,{class:`block text-[0.68rem] font-bold uppercase tracking-[0.14em] text-slate-400`,children:`Home Gateway`})]})]}),L(`button`,{"aria-controls":`smartsafehub-desktop-navigation`,"aria-expanded":!t,"aria-label":t?`사이드바 펼치기`:`사이드바 접기`,class:`ssh-sidebar-toggle absolute right-0 top-[4.5rem] z-20 inline-flex size-7 translate-x-1/2 -translate-y-1/2 shrink-0 items-center justify-center rounded-full transition focus:outline-none focus-visible:ring-4 focus-visible:ring-teal-100`,onClick:i,title:t?`사이드바 펼치기`:`사이드바 접기`,type:`button`,children:L(t?bt:yt,{class:`size-4`})})]}),L(`nav`,{"aria-label":`SmartSafeHub 메뉴`,class:`flex-1 overflow-y-auto py-5 ${t?`px-2`:`px-3`}`,id:`smartsafehub-desktop-navigation`,children:L(Nt,{accountRegistered:e,collapsed:t,demo:d,route:s,updateCount:u})}),d?null:L(`div`,{class:`border-t border-slate-100 ${t?`p-2`:`p-3`}`,children:L(`a`,{"aria-label":`SmartSafeHub에서 로그아웃`,class:`${Mt(!1,t)} mt-1 text-rose-700 hover:bg-rose-50 hover:text-rose-800`,href:Dt(`/admin/logout`),title:t?`로그아웃`:void 0,children:[L(`span`,{class:`grid size-9 shrink-0 place-items-center rounded-lg bg-rose-50 text-rose-600`,children:L(ht,{class:`size-5`})}),t?null:`로그아웃`]})})]})})]})}var Ft=`smartsafehub.sidebar.collapsed`;function It(){if(typeof window>`u`)return!1;try{return window.localStorage.getItem(Ft)===`1`}catch{return!1}}function Lt({accountRegistered:e,children:t,loading:n,onRefresh:r,refreshing:i,route:a,updateCount:o,demo:s=!1}){let c=Ie[a],[l,u]=F(It),[d,f]=F(ze);return I(()=>{try{window.localStorage.setItem(Ft,l?`1`:`0`)}catch{}},[l]),I(()=>{Be(d),Ve(d)},[d]),L(`div`,{class:`ssh-app min-h-screen bg-slate-50 text-slate-950 md:grid md:transition-[grid-template-columns] md:duration-200 md:ease-out ${l?`md:grid-cols-[5rem_minmax(0,1fr)]`:`md:grid-cols-[16rem_minmax(0,1fr)]`}`,"data-theme":d,children:[L(Pt,{accountRegistered:e,demo:s,collapsed:l,loading:n,onRefresh:r,onToggleCollapsed:()=>u(e=>!e),onToggleTheme:()=>f(e=>e===`dark`?`light`:`dark`),refreshing:i,route:a,theme:d,title:c.title,updateCount:o}),L(`div`,{class:`min-w-0 md:min-h-screen`,children:[L(Ct,{description:c.description,loading:n,onRefresh:r,onToggleTheme:()=>f(e=>e===`dark`?`light`:`dark`),refreshing:i,theme:d,title:c.title}),L(`main`,{class:`ssh-product-main w-full max-w-[1600px] bg-slate-50 px-4 pb-8 sm:px-6 sm:pb-10 lg:px-8 lg:pb-12 xl:px-10 ${n?`pt-2 sm:pt-2 lg:pt-3`:`pt-5 sm:pt-6 lg:pt-8`}`,children:t})]})]})}function V({action:e,description:t,eyebrow:n,id:r,title:i}){return L(`div`,{class:`ssh-section-heading mb-4 min-w-0`,children:[L(`p`,{class:`m-0 text-[0.68rem] font-black uppercase tracking-[0.18em] text-teal-700`,children:n}),L(`div`,{class:`mt-2 flex min-w-0 flex-wrap items-center justify-between gap-3`,children:[L(`h2`,{class:`m-0 min-w-0 text-xl font-black tracking-tight text-slate-950`,id:r,children:i}),e?L(`div`,{class:`shrink-0`,children:e}):null]}),L(`p`,{class:`mt-2 mb-0 text-sm leading-6 text-slate-500`,children:t})]})}var Rt=new Intl.NumberFormat(`ko-KR`),zt=new Intl.DateTimeFormat(`ko-KR`,{dateStyle:`medium`,timeStyle:`short`});function Bt(e){if(!Number.isFinite(e)||e<0)return`확인 불가`;let t=Math.floor(e/86400),n=Math.floor(e%86400/3600),r=Math.floor(e%3600/60);return t>0?`${t}일 ${n}시간`:n>0?`${n}시간 ${r}분`:`${Math.max(r,1)}분`}function Vt(e){if(!Number.isFinite(e)||e<0)return`확인 불가`;let t=[`B`,`KB`,`MB`,`GB`],n=e,r=0;for(;n>=1024&&r864e10?`기록 없음`:!Number.isFinite(t)||e>t+300||t-e>=604800?Wt(e):Gt(e,t)}function qt(e){let t=/^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})Z$/.exec(e??``);if(!t)return null;let[,n,r,i,a,o,s]=t,c=Date.UTC(Number(n),Number(r)-1,Number(i),Number(a),Number(o),Number(s)),l=new Date(c);return l.getUTCFullYear()!==Number(n)||l.getUTCMonth()+1!==Number(r)||l.getUTCDate()!==Number(i)||l.getUTCHours()!==Number(a)||l.getUTCMinutes()!==Number(o)||l.getUTCSeconds()!==Number(s)||c<=0?null:c/1e3}function Jt(e){if(!Number.isFinite(e)||e<=0)return`확인 불가`;let t=Math.floor(e/3600),n=Math.floor(e%3600/60);return t>0?n>0?`${t}시간 ${n}분`:`${t}시간`:`${n}분`}function Yt(e){let t=Math.max(0,e.total),n=e.free+e.buffered+e.cached,r=Math.min(t,Math.max(0,e.available>0?e.available:n)),i=Math.max(0,t-r);return{used:i,percent:t>0?i/t*100:0}}function Xt(e,t){let n=e.metadata[t];return typeof n==`string`&&n.trim().length>0?n.trim():null}function Zt(e,t){let n=e.metadata[t];return typeof n==`number`&&Number.isFinite(n)?n:null}function Qt(e,t){let n=e.metadata[t];return typeof n==`boolean`?n:null}function $t(e,t){let n=Xt(e,`error_code`);return n?`${t} (오류 코드: ${n})`:t}function en(e){let t=Math.max(0,Math.floor(e));if(t<60)return`${t}초`;let n=Math.floor(t/60),r=t%60;if(n<60)return r>0?`${n}분 ${r}초`:`${n}분`;let i=Math.floor(n/60),a=n%60;return a>0?`${i}시간 ${a}분`:`${i}시간`}function tn(e){let t=Xt(e,`from_version`),n=Xt(e,`to_version`);return t&&n?`${t} → ${n}`:n?`${n} 버전이 적용되었습니다.`:null}function nn(e){return e?e.toUpperCase():null}function rn(e){switch(e){case`dhcp`:return`자동 IP(DHCP)`;case`pppoe`:return`PPPoE`;case`static`:return`고정 IPv4`;default:return e.toUpperCase()}}function an(e){switch(e.eventType){case`system.booted`:return{title:`공유기 부팅 감지`,description:`공유기가 켜지거나 재시작된 것을 확인했습니다.`};case`network.internet.disconnected`:return{title:`인터넷 연결 끊김 감지`,description:`공유기에서 인터넷 연결을 확인할 수 없었습니다.`};case`network.internet.recovered`:{let t=Zt(e,`downtime_seconds`);return{title:`인터넷 연결 복구`,description:t!==null&&t>0?`약 ${en(t)} 동안 인터넷 연결이 확인되지 않았습니다.`:`공유기에서 인터넷 연결을 다시 확인했습니다.`}}case`safeshield.protection.enabled`:return{title:`SafeShield 보호 활성화`,description:`DNS 보호 기능이 활성화되었습니다.`};case`safeshield.protection.disabled`:return{title:`SafeShield 보호 비활성화`,description:`DNS 보호 기능이 비활성화되었습니다.`};case`safeshield.blocklist.updated`:{let t=Zt(e,`domain_count`),n=Xt(e,`artifact_version`),r=[];return t!==null&&t>0&&r.push(`${H(t)}개 도메인 적용`),n&&r.push(`목록 ${n}`),{title:`SafeShield 차단 목록 업데이트 완료`,description:r.length>0?r.join(` · `):`최신 차단 목록을 적용했습니다.`}}case`safeshield.blocklist.update_failed`:return{title:`SafeShield 차단 목록 업데이트 실패`,description:$t(e,`차단 목록을 업데이트하지 못했습니다.`)};case`safeshield.statistics.enabled`:return{title:`SafeShield 차단 통계 수집 활성화`,description:`기기별 차단 통계 수집을 활성화했습니다.`};case`safeshield.statistics.disabled`:return{title:`SafeShield 차단 통계 수집 비활성화`,description:`기기별 차단 통계 수집을 비활성화했습니다.`};case`safeshield.rule.added`:return{title:Xt(e,`rule_action`)===`allow`?`사용자 허용 규칙 추가`:`사용자 차단 규칙 추가`,description:`SafeShield 사용자 규칙이 변경되었습니다.`};case`safeshield.rule.removed`:return{title:Xt(e,`rule_action`)===`allow`?`사용자 허용 규칙 삭제`:`사용자 차단 규칙 삭제`,description:`SafeShield 사용자 규칙이 변경되었습니다.`};case`settings.scheduled_reboot.updated`:{let t=Qt(e,`enabled`);return{title:`예약 재부팅 설정 변경`,description:t===!0?`예약 재부팅 설정을 저장하고 새 일정에 적용했습니다.`:t===!1?`예약 재부팅을 비활성화했습니다.`:`예약 재부팅 설정을 변경했습니다.`}}case`settings.software_updates.updated`:return{title:`관리 소프트웨어 업데이트 설정 변경`,description:Qt(e,`auto_install`)===!0?`자동 설치를 포함한 업데이트 설정을 저장했습니다.`:`관리 소프트웨어 업데이트 설정을 저장했습니다.`};case`settings.health_reporter.enabled`:return{title:`원격 상태 보고 활성화`,description:`장치 상태를 SmartSafeHub Cloud에 보고하도록 설정했습니다.`};case`settings.health_reporter.disabled`:return{title:`원격 상태 보고 비활성화`,description:`SmartSafeHub Cloud로의 장치 상태 보고를 중지했습니다.`};case`settings.activity_cloud_sync.enabled`:return{title:`Cloud 활동 기록 전송 활성화`,description:`이후 발생하는 활동을 SmartSafeHub Cloud에 전송하도록 설정했습니다.`};case`settings.activity_cloud_sync.disabled`:return{title:`Cloud 활동 기록 전송 비활성화`,description:`Cloud 전송을 중지했습니다. 로컬 최근 활동은 계속 기록됩니다.`};case`settings.timezone.updated`:{let t=Xt(e,`zonename`);return{title:`시간대 설정 변경`,description:t?`공유기 시간대를 ${t}(으)로 변경했습니다.`:`공유기 시간대를 변경했습니다.`}}case`settings.wifi.updated`:return{title:`Wi-Fi 설정 변경`,description:`Wi-Fi 설정을 저장하고 무선 네트워크에 적용했습니다.`};case`settings.wan.updated`:{let t=Xt(e,`protocol`);return{title:`인터넷 설정 변경`,description:t?`${rn(t)} 방식으로 WAN 설정을 변경했습니다.`:`WAN 인터넷 연결 설정을 변경했습니다.`}}case`settings.lan.updated`:return{title:`LAN 설정 변경`,description:`내부 네트워크 및 DHCP 설정을 변경했습니다.`};case`settings.iptv.updated`:{let t=Qt(e,`enabled`),n=Xt(e,`provider`),r=n===`skb`?`SK Broadband`:n===`lgu`?`LG U+`:null;return{title:`IPTV 설정 변경`,description:t===!1?`IPTV 기능을 비활성화했습니다.`:r?`${r} IPTV 설정을 적용했습니다.`:`IPTV 설정을 적용했습니다.`}}case`software.update.completed`:return{title:`관리 소프트웨어 업데이트 완료`,description:tn(e)};case`software.update.failed`:return{title:`관리 소프트웨어 업데이트 실패`,description:$t(e,`관리 소프트웨어를 업데이트하지 못했습니다.`)};case`firmware.update.started`:return{title:`펌웨어 업데이트 시작`,description:`펌웨어 설치를 시작했습니다. 완료 과정에서 공유기가 재부팅됩니다.`};case`firmware.update.failed`:return{title:`펌웨어 업데이트 실패`,description:$t(e,`펌웨어 설치 준비 또는 검증에 실패했습니다.`)};case`license.activated`:{let t=nn(Xt(e,`to_plan`));return{title:`요금제 활성화`,description:t?`${t} 요금제가 활성화되었습니다.`:`요금제가 활성화되었습니다.`}}case`license.changed`:{let t=nn(Xt(e,`from_plan`)),n=nn(Xt(e,`to_plan`));return{title:`요금제 변경`,description:t&&n?`${t} → ${n}`:n?`${n} 요금제로 변경되었습니다.`:`요금제 상태가 변경되었습니다.`}}case`license.cleared`:return{title:`계정 권한 해제`,description:Xt(e,`reason`)===`manual`?`이 공유기에서 계정 권한 연결을 해제했습니다.`:`서버의 계정 권한 상태가 반영되어 이 공유기의 권한 연결이 해제되었습니다.`};case`health.issue.started`:{let t=Zt(e,`warning_count`)??0,n=Zt(e,`critical_count`)??0,r=[];return n>0&&r.push(`이상 ${H(n)}건`),t>0&&r.push(`주의 ${H(t)}건`),{title:`확인이 필요한 장치 상태 발견`,description:r.length>0?r.join(` · `):`장치 상태에서 확인이 필요한 항목이 발견되었습니다.`}}case`health.issue.changed`:{let t=Zt(e,`warning_count`)??0,n=Zt(e,`critical_count`)??0,r=[];return n>0&&r.push(`이상 ${H(n)}건`),t>0&&r.push(`주의 ${H(t)}건`),{title:`확인이 필요한 장치 상태 변경`,description:r.length>0?r.join(` · `):`확인이 필요한 장치 상태 항목이 변경되었습니다.`}}case`health.issue.resolved`:return{title:`장치 상태 정상으로 복구`,description:`이전에 확인이 필요했던 장치 상태가 정상으로 돌아왔습니다.`};default:return{title:`SmartSafeHub 활동`,description:`장치에서 새로운 활동이 기록되었습니다.`}}}function on(e){return e===`error`?{icon:L(B,{class:`size-4`}),surface:`bg-rose-50 text-rose-700 ring-rose-200`}:e===`warning`?{icon:L(B,{class:`size-4`}),surface:`bg-amber-50 text-amber-800 ring-amber-200`}:e===`success`?{icon:L(ut,{class:`size-4`}),surface:`bg-emerald-50 text-emerald-700 ring-emerald-200`}:{icon:L(tt,{class:`size-4`}),surface:`bg-slate-100 text-slate-600 ring-slate-200`}}function sn(e){let t=new Date(e*1e3);return new Intl.DateTimeFormat(`ko-KR`,{hour:`2-digit`,minute:`2-digit`,hour12:!1}).format(t)}function cn(e){let t=new Date(e*1e3);return`${t.getFullYear()}-${t.getMonth()+1}-${t.getDate()}`}function ln(e){let t=new Date(e*1e3),n=new Date,r=new Date(n.getFullYear(),n.getMonth(),n.getDate()),i=new Date(t.getFullYear(),t.getMonth(),t.getDate()),a=Math.round((r.getTime()-i.getTime())/864e5);return a===0?`오늘`:a===1?`어제`:new Intl.DateTimeFormat(`ko-KR`,{month:`long`,day:`numeric`,weekday:`short`}).format(t)}function un({compact:e=!1,events:t}){if(t.length===0)return L(`div`,{class:`rounded-xl border border-slate-200 bg-slate-50 px-4 py-5 text-center`,children:[L(tt,{class:`mx-auto size-5 text-slate-400`}),L(`p`,{class:`mt-2 mb-0 text-sm font-extrabold text-slate-700`,children:`아직 기록된 활동이 없습니다.`}),L(`p`,{class:`mt-1 mb-0 text-xs leading-5 text-slate-500`,children:`상태가 실제로 변경되면 이곳에 시간순으로 기록됩니다.`})]});let n=``;return L(`div`,{class:e?`space-y-2`:`space-y-1`,children:t.map(t=>{let r=an(t),i=on(t.severity),a=cn(t.occurredAt),o=!e&&a!==n;return n=a,L(`div`,{children:[o?L(`p`,{class:`mt-5 mb-2 first:mt-0 text-xs font-black text-slate-500`,children:ln(t.occurredAt)}):null,L(`article`,{class:`relative flex min-w-0 gap-3 ${e?`border-b border-slate-100 py-3 first:pt-0 last:border-b-0 last:pb-0`:`rounded-xl px-2 py-3 hover:bg-slate-50`}`,children:[L(`div`,{class:`w-12 shrink-0 pt-1 text-xs font-bold tabular-nums text-slate-400`,children:L(`time`,{dateTime:new Date(t.occurredAt*1e3).toISOString(),title:Wt(t.occurredAt),children:sn(t.occurredAt)})}),L(`span`,{class:`mt-0.5 grid size-8 shrink-0 place-items-center rounded-full ring-1 ring-inset ${i.surface}`,children:i.icon}),L(`div`,{class:`min-w-0 flex-1`,children:[L(`p`,{class:`m-0 text-sm font-black leading-5 text-slate-950`,children:r.title}),r.description?L(`p`,{class:`mt-1 mb-0 break-words text-xs font-medium leading-5 text-slate-500`,children:r.description}):null]})]})]},t.eventId)})})}function dn({error:e,loading:t,onRetry:n}){return t?L(`div`,{class:`rounded-xl border border-slate-200 bg-slate-50 px-4 py-5 text-center`,children:[L(z,{class:`mx-auto size-5 animate-spin text-teal-700`}),L(`p`,{class:`mt-2 mb-0 text-sm font-bold text-slate-500`,children:`최근 활동을 불러오는 중입니다.`})]}):e?L(`div`,{class:`rounded-xl border border-amber-200 bg-amber-50/70 px-4 py-4`,children:L(`div`,{class:`flex items-start gap-3`,children:[L(B,{class:`mt-0.5 size-5 shrink-0 text-amber-700`}),L(`div`,{class:`min-w-0 flex-1`,children:[L(`p`,{class:`m-0 text-sm font-black text-slate-950`,children:`최근 활동을 불러오지 못했습니다.`}),L(`p`,{class:`mt-1 mb-0 text-xs leading-5 text-slate-500`,children:e}),n?L(`button`,{class:`mt-3 inline-flex min-h-9 items-center justify-center rounded-lg border border-amber-300 bg-white px-3 text-xs font-extrabold text-amber-800 transition hover:bg-amber-50`,onClick:n,type:`button`,children:`다시 시도`}):null]})]})}):null}function fn(e){return e.enabled?e.eligible===!1||e.phase===`ineligible`?{label:`계정 연결 필요`,description:`SmartSafeHub 계정을 연결하면 이후 발생하는 활동을 Cloud에 자동으로 동기화할 수 있습니다.`,tone:`bg-slate-100 text-slate-600 ring-slate-200`}:e.phase===`error`?{label:`동기화 확인 필요`,description:`최근 Cloud 전송을 완료하지 못했습니다. 로컬 활동 기록은 계속 보관됩니다.`,tone:`bg-amber-50 text-amber-800 ring-amber-200`}:e.phase===`syncing`?{label:`동기화 중`,description:`새 활동을 SmartSafeHub Cloud에 전송하고 있습니다.`,tone:`bg-teal-50 text-teal-800 ring-teal-200`}:e.eligible===!0?{label:`자동 동기화`,description:`새 활동이 생기면 SmartSafeHub Cloud에 자동으로 안전하게 전송합니다.`,tone:`bg-emerald-50 text-emerald-800 ring-emerald-200`}:{label:`확인 중`,description:`Cloud 활동 기록 사용 가능 여부를 확인하고 있습니다.`,tone:`bg-slate-100 text-slate-600 ring-slate-200`}:e.eligible===!1?{label:`계정 연결 필요`,description:`SmartSafeHub 계정을 연결하면 Free에서도 Cloud 활동 기록을 사용할 수 있습니다.`,tone:`bg-slate-100 text-slate-600 ring-slate-200`}:{label:`Cloud 전송 꺼짐`,description:`활동 기록은 이 공유기에만 저장됩니다. Cloud에는 새로운 활동을 전송하지 않습니다.`,tone:`bg-slate-100 text-slate-700 ring-slate-200`}}function pn(e){if(e.eligible!==!0)return`—`;if(e.retentionDays>0)return`${e.retentionDays}일`;switch(e.plan?.toLowerCase()){case`free`:return`7일`;case`pro`:case`ultimate`:return`90일`;default:return`확인 중`}}function mn({actionError:e,actionMessage:t,cloud:n,onDismissFeedback:r,onSetEnabled:i,saving:a}){let o=fn(n),s=n.plan?.toUpperCase()??null,c=n.eligible===!0,l=n.enabled||c;return L(`section`,{"aria-label":`Cloud 활동 기록 설정`,class:`rounded-2xl border border-slate-200 bg-white p-4 shadow-sm shadow-slate-900/5 sm:p-5`,children:[L(`div`,{class:`flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between`,children:[L(`div`,{class:`min-w-0 flex-1`,children:[L(`div`,{class:`flex flex-wrap items-center gap-2`,children:L(`span`,{class:`rounded-full px-2.5 py-1 text-xs font-extrabold ring-1 ring-inset ${o.tone}`,children:a?`설정 중`:o.label})}),L(`p`,{class:`mt-2 mb-0 text-sm leading-6 text-slate-600`,children:o.description})]}),L(`div`,{class:`flex shrink-0 items-center justify-end gap-3 sm:justify-start`,children:[L(`span`,{class:`text-xs font-bold text-slate-600`,children:[`Cloud 전송 `,n.enabled?`켜짐`:`꺼짐`]}),L(`button`,{"aria-checked":n.enabled,"aria-label":`Cloud 활동 기록 전송 사용`,class:`ssh-switch-control relative inline-flex shrink-0 rounded-full border transition focus:outline-none focus-visible:ring-4 focus-visible:ring-teal-100 disabled:cursor-not-allowed disabled:opacity-50 ${n.enabled?`border-teal-600 bg-teal-600`:`border-slate-300 bg-slate-200`}`,disabled:a||!l,onClick:()=>i(!n.enabled),role:`switch`,type:`button`,children:L(`span`,{"aria-hidden":`true`,class:`ssh-switch-thumb absolute top-1 shadow-sm transition-[left] ${n.enabled?`left-6`:`left-1`}`})})]})]}),(e||t)&&L(`div`,{class:`mt-4 flex min-w-0 items-start justify-between gap-3 rounded-xl border px-4 py-3 text-sm font-bold ${e?`border-rose-200 bg-rose-50 text-rose-800`:`border-emerald-200 bg-emerald-50 text-emerald-800`}`,role:e?`alert`:`status`,children:[L(`span`,{class:`min-w-0`,children:e||t}),L(`button`,{class:`shrink-0 rounded-lg px-2 py-1 text-xs font-extrabold hover:bg-black/5`,onClick:r,type:`button`,children:`닫기`})]}),n.enabled?L(`p`,{class:`mt-3 mb-0 text-xs leading-5 text-slate-500`,children:`Cloud 전송을 끄면 아직 전송하지 않은 대기 기록이 삭제됩니다. 공유기의 로컬 기록은 유지됩니다.`}):null,L(`div`,{class:`mt-4 flex flex-wrap items-center gap-x-5 gap-y-1 border-t border-slate-200 pt-3 text-xs font-semibold text-slate-500`,children:[L(`span`,{children:[`마지막 동기화: `,n.lastSuccessAt>0?Gt(n.lastSuccessAt):`아직 없음`]}),L(`span`,{children:[`Cloud 보관: `,pn(n)]}),n.pendingEvents>0?L(`span`,{children:[`전송 대기: `,H(n.pendingEvents),`건`]}):null]}),n.enabled&&n.phase===`error`&&n.lastErrorCode?L(`p`,{class:`mt-3 mb-0 text-xs font-semibold text-amber-700`,children:[`오류 코드: `,L(`code`,{children:n.lastErrorCode})]}):null,L(`details`,{class:`mt-3 border-t border-slate-200 pt-3`,children:[L(`summary`,{class:`cursor-pointer text-xs font-extrabold text-teal-700 hover:text-teal-800 focus-visible:rounded focus-visible:outline focus-visible:outline-2 focus-visible:outline-teal-500`,children:`Cloud 동기화 상세 정보`}),L(`dl`,{class:`mt-3 grid gap-3 sm:grid-cols-4`,children:[L(`div`,{class:`rounded-xl bg-slate-50 px-4 py-3 ring-1 ring-inset ring-slate-200`,children:[L(`dt`,{class:`text-xs font-bold text-slate-500`,children:`멤버십`}),L(`dd`,{class:`mt-1 text-sm font-black text-slate-900`,children:s??(n.eligible===!1?`Free`:`확인 중`)})]}),L(`div`,{class:`rounded-xl bg-slate-50 px-4 py-3 ring-1 ring-inset ring-slate-200`,children:[L(`dt`,{class:`text-xs font-bold text-slate-500`,children:`전송 대기`}),L(`dd`,{class:`mt-1 text-sm font-black text-slate-900`,children:[H(n.pendingEvents),`건`]})]}),L(`div`,{class:`rounded-xl bg-slate-50 px-4 py-3 ring-1 ring-inset ring-slate-200`,children:[L(`dt`,{class:`text-xs font-bold text-slate-500`,children:`마지막 동기화`}),L(`dd`,{class:`mt-1 text-sm font-black text-slate-900`,children:n.lastSuccessAt>0?Gt(n.lastSuccessAt):`아직 없음`})]}),L(`div`,{class:`rounded-xl bg-slate-50 px-4 py-3 ring-1 ring-inset ring-slate-200`,children:[L(`dt`,{class:`text-xs font-bold text-slate-500`,children:`Cloud 보관`}),L(`dd`,{class:`mt-1 text-sm font-black text-slate-900`,children:pn(n)})]})]}),L(`p`,{class:`mt-3 mb-0 text-xs leading-5 text-slate-500`,children:`SmartSafeHub 계정에 연결된 Free는 최근 7일, Pro와 Ultimate는 최근 90일 동안 Cloud 활동 기록을 보관합니다. 끄면 전송 대기 데이터와 임시 인증 정보를 삭제하며, 다시 켠 뒤 새로 발생한 활동부터 Cloud에 전송합니다. 로컬 최근 활동은 항상 유지됩니다.`})]})]})}function hn({actionError:e,actionMessage:t,data:n,error:r,loading:i,onDismissActionFeedback:a,onRetry:o,onSetCloudSync:s,savingCloudSync:c}){let[l,u]=F(20),d=n?.events??[],f=d.slice(0,l),p=d.length-f.length;return L(`div`,{class:`min-w-0 space-y-6`,children:[L(`section`,{"aria-labelledby":`activity-cloud-title`,class:`min-w-0`,children:[L(V,{id:`activity-cloud-title`,eyebrow:`Cloud history`,title:`Cloud 활동 기록`,description:`활동 기록을 Cloud에 자동으로 전송할지 설정합니다. 공유기 내 최근 활동은 항상 기록됩니다.`}),L(mn,{actionError:e,actionMessage:t,cloud:n?.cloud??{enabled:!1,phase:`preparing`,eligible:null,plan:null,retentionDays:0,pendingEvents:0,lastAttemptAt:0,lastSuccessAt:0,lastUploadedCount:0,lastErrorCode:null,nextSyncAt:0},onDismissFeedback:a,onSetEnabled:s,saving:c})]}),L(`section`,{"aria-labelledby":`activity-history-title`,class:`min-w-0`,children:[L(V,{id:`activity-history-title`,eyebrow:`Local history`,title:`현재 부팅 이후의 최근 활동`,description:`인터넷, SafeShield, 업데이트, 계정 권한과 주요 설정에서 실제 상태가 변경된 시점만 기록합니다.`,action:L(`span`,{class:`rounded-full bg-slate-100 px-3 py-1.5 text-xs font-extrabold text-slate-600 ring-1 ring-inset ring-slate-200`,children:[`최대 `,n?.maxEvents??128,`건`]})}),L(`div`,{class:`rounded-2xl border border-slate-200 bg-white p-5 shadow-sm shadow-slate-900/5 sm:p-6`,children:i||r&&!n?L(dn,{error:r,loading:i,onRetry:o}):L(v,{children:[L(un,{events:f}),d.length>20?L(`div`,{class:`mt-5 flex flex-wrap items-center justify-between gap-3 border-t border-slate-200 pt-4`,children:[L(`p`,{class:`m-0 text-xs font-semibold text-slate-500`,children:[H(f.length),` / `,H(d.length),`건 표시 중`]}),L(`div`,{class:`flex flex-wrap items-center gap-2`,children:[l>20?L(`button`,{class:`inline-flex min-h-10 items-center justify-center rounded-xl border border-slate-200 bg-white px-4 py-2 text-xs font-extrabold text-slate-700 transition hover:bg-slate-50 focus-visible:outline focus-visible:outline-2 focus-visible:outline-teal-500`,onClick:()=>u(20),type:`button`,children:`최신 20건만 보기`}):null,p>0?L(`button`,{class:`inline-flex min-h-10 items-center justify-center rounded-xl border border-teal-200 bg-teal-50 px-4 py-2 text-xs font-extrabold text-teal-800 transition hover:bg-teal-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-teal-500`,onClick:()=>u(e=>Math.min(e+20,d.length)),type:`button`,children:[`이전 활동 더 보기 (`,H(Math.min(20,p)),`건)`]}):null]})]}):null]})})]}),L(`aside`,{class:`rounded-2xl border border-slate-200 bg-slate-50 px-5 py-4`,children:[L(`p`,{class:`m-0 text-sm font-extrabold text-slate-800`,children:`로컬 활동 기록 안내`}),L(`p`,{class:`mt-1 mb-0 text-xs leading-5 text-slate-500`,children:`이 기록은 공유기의 메모리 영역에만 보관되어 재부팅하면 초기화됩니다. 직접 설정을 변경한 작업은 성공 시점에 즉시 기록하고, 인터넷 및 장치 진단처럼 외부 상태를 확인해야 하는 항목은 실제 상태 변화가 관찰된 경우에만 추가합니다.`})]})]})}var gn=Object.assign,_n=/^(-|f[lo].*[^se]$|g.{5,}[^ps]$|z|o[pr]|(W.{5})?[lL]i.*(t|mp)$|an|(bo|s).{4}Im|sca|m.{6}[ds]|ta|c.*[st]$|wido|ini)/;function vn(e,t){var n=ce(e,t);return n.containerInfo=t,n}var yn=Symbol.for(`react.element`),bn=/^(?:accent|alignment|arabic|baseline|cap|clip(?!PathU)|color|dominant|fill|flood|font|glyph(?!R)|horiz|image(?!S)|letter|lighting|marker(?!H|W|U)|overline|paint|pointer|shape|stop|strikethrough|stroke|text(?!L)|transform|underline|unicode|units|v|vector|vert|word|writing|x(?!C))[A-Z]/,xn=/[A-Z0-9]/g,Sn=typeof document<`u`,Cn=function(e){return/fil|che|rad/.test(e)};y.prototype.isReactComponent=!0,[`componentWillMount`,`componentWillReceiveProps`,`componentWillUpdate`].forEach(function(e){Object.defineProperty(y.prototype,e,{configurable:!0,get:function(){return this[`UNSAFE_`+e]},set:function(t){Object.defineProperty(this,e,{configurable:!0,writable:!0,value:t})}})});var wn=e.event;e.event=function(e){return wn&&(e=wn(e)),e.persist=function(){},e.isPropagationStopped=function(){return this.cancelBubble},e.isDefaultPrevented=function(){return this.defaultPrevented},e.nativeEvent=e};var Tn={configurable:!0,get:function(){return this.class}},En=e.vnode;e.vnode=function(e){if(typeof e.type==`string`)(function(e){var t=e.props,n=e.type,r={},i=n.indexOf(`-`)==-1;for(var a in t){var o=t[a];if(!(a==`value`&&`defaultValue`in t&&o==null||Sn&&a==`children`&&n==`noscript`||a==`class`||a==`className`)){if(a==`style`&&typeof o==`object`){var s=void 0;for(var c in o)typeof o[c]!=`number`||_n.test(c)||(s||(s=o=gn({},o)),o[c]+=`px`)}else if(a==`defaultValue`&&`value`in t&&t.value==null)a=`value`;else if(a==`download`&&!0===o)o=``;else if(a==`translate`&&o===`no`)o=!1;else if(a[0]==`o`&&a[1]==`n`){var l=a.toLowerCase();l==`ondoubleclick`?a=`ondblclick`:l!=`onchange`||n!=`input`&&n!=`textarea`||Cn(t.type)?l==`onfocus`?a=`onfocusin`:l==`onblur`&&(a=`onfocusout`):l=a=`oninput`,l==`oninput`&&r[a=l]&&(a=`oninputCapture`)}else i&&bn.test(a)?a=a.replace(xn,`-$&`).toLowerCase():o===null&&(o=void 0);r[a]=o}}n==`select`&&(r.multiple&&Array.isArray(r.value)&&(r.value=D(t.children).forEach(function(e){e.props.selected=r.value.indexOf(e.props.value)!=-1})),r.defaultValue!=null&&(r.value=D(t.children).forEach(function(e){e.props.selected=r.multiple?r.defaultValue.indexOf(e.props.value)!=-1:r.defaultValue==e.props.value}))),t.class&&!t.className?(r.class=t.class,Object.defineProperty(r,"className",Tn)):t.className&&(r.class=r.className=t.className),e.props=r})(e);else if(typeof e.type==`function`&&(`ref`in e.props&&`prototype`in e.type&&e.type.prototype.render&&(e.ref=e.props.ref,delete e.props.ref),e.type.defaultProps)){var t=gn({},e.props);for(var n in e.type.defaultProps)t[n]===void 0&&(t[n]=e.type.defaultProps[n]);e.props=t}e.ref&&!(`ref`in e.props)&&Object.defineProperty(e.props,"ref",{value:e.ref,configurable:!0,writable:!0}),e.$$typeof=yn,En&&En(e)};var Dn=8,On=256,kn=8;function An(e){return e.findIndex(e=>!e.disabled)}function jn(e){for(let t=e.length-1;t>=0;--t)if(!e[t]?.disabled)return t;return-1}function Mn(e,t,n){if(e.length===0)return-1;let r=t;for(let t=0;te.value===t&&!e.disabled);return n>=0?n:An(e)}function Pn({ariaLabel:e,className:t=``,disabled:n=!1,id:r,onChange:i,options:a,value:o,variant:s=`default`}){let c=De(),l=r??`ssh-custom-select-${c}`,u=`${l}-menu`,d=we(null),f=we(null),p=we(null),m=we([]),[h,g]=F(!1),[_,v]=F(()=>Nn(a,o)),[y,b]=F(`bottom`),[x,S]=F(On),[C,w]=F({}),[T,E]=F(null),D=Te(()=>a.find(e=>e.value===o)??a[0]??null,[a,o]),O=()=>{let e=f.current;if(!e)return;let t=e.getBoundingClientRect(),n=Math.min(On,Math.max(44,a.length*42+12)),r=window.innerHeight-t.bottom-kn,i=t.top-kn,o=rr?`top`:`bottom`,s=Math.min(t.width,Math.max(0,window.innerWidth-16)),c=Math.max(kn,Math.min(t.left,window.innerWidth-s-kn)),l=o===`bottom`?r:i;b(o),S(Math.max(44,Math.min(On,Math.floor(l)))),w({left:`${c}px`,width:`${s}px`,...o===`bottom`?{top:`${t.bottom+Dn}px`}:{bottom:`${window.innerHeight-t.top+Dn}px`}})},k=(e=!1)=>{g(!1),e&&window.requestAnimationFrame(()=>f.current?.focus())},A=e=>{e<0||(v(e),window.requestAnimationFrame(()=>m.current[e]?.focus()))},j=e=>{let t=a[e];t&&!t.disabled&&(i(t.value),g(!1),window.requestAnimationFrame(()=>f.current?.focus()))},M=e=>{n||a.length===0||(O(),g(!0),A(e??Nn(a,o)))};I(()=>{let e=d.current?.closest(`.ssh-app`);if(e){E(e);return}let t=d.current?.getRootNode();t instanceof ShadowRoot?E(t):typeof document<`u`&&E(document.body)},[]),I(()=>{v(Nn(a,o)),m.current=m.current.slice(0,a.length)},[a,o]),I(()=>{if(!h)return;O();let e=e=>{let t=e.composedPath();d.current&&t.includes(d.current)||p.current&&t.includes(p.current)||k()},t=e=>{e.key===`Escape`&&(e.preventDefault(),k(!0))},n=()=>O();return document.addEventListener(`pointerdown`,e),document.addEventListener(`keydown`,t),window.addEventListener(`resize`,n),window.addEventListener(`scroll`,n,!0),()=>{document.removeEventListener(`pointerdown`,e),document.removeEventListener(`keydown`,t),window.removeEventListener(`resize`,n),window.removeEventListener(`scroll`,n,!0)}},[h,a.length]),I(()=>{n&&h&&k()},[n,h]);let ee=s===`emphasized`?`min-h-11 border-2 border-slate-300 bg-slate-50 px-4 py-2.5 text-sm font-semibold text-slate-950 shadow-inner focus:border-teal-500 focus:bg-white focus:ring-4 focus:ring-teal-100`:`min-h-11 border border-slate-300 bg-slate-50 px-3 py-2.5 text-sm font-bold text-slate-900 focus:border-teal-500 focus:ring-2 focus:ring-teal-100`,te=h?L(`div`,{"aria-label":`${e} 목록`,class:`ssh-custom-select-menu fixed z-[1000] overflow-hidden rounded-xl border border-slate-200 bg-white p-1.5 shadow-xl shadow-slate-950/10`,id:u,ref:p,role:`listbox`,style:C,children:L(`div`,{class:`overflow-y-auto overscroll-contain`,style:{maxHeight:`${x}px`},children:a.map((e,t)=>{let n=e.value===o;return L(`button`,{"aria-selected":n,class:`flex min-h-10 w-full items-center justify-between gap-3 rounded-lg px-3 py-2.5 text-left text-sm font-bold transition ${n?`bg-teal-50 text-teal-800`:`text-slate-700 hover:bg-slate-50`} ${e.disabled?`cursor-not-allowed opacity-50`:``}`,disabled:e.disabled,onClick:()=>j(t),onFocus:()=>v(t),onKeyDown:e=>{switch(e.key){case`ArrowDown`:e.preventDefault(),A(Mn(a,t,1));break;case`ArrowUp`:e.preventDefault(),A(Mn(a,t,-1));break;case`Home`:e.preventDefault(),A(An(a));break;case`End`:e.preventDefault(),A(jn(a));break;case`Enter`:case` `:e.preventDefault(),j(t);break;case`Escape`:e.preventDefault(),k(!0);break;case`Tab`:g(!1)}},ref:e=>{m.current[t]=e},role:`option`,tabIndex:_===t?0:-1,type:`button`,children:[L(`span`,{class:`min-w-0 truncate`,children:e.label}),n?L(`span`,{"aria-hidden":`true`,class:`size-2 shrink-0 rounded-full bg-teal-500`}):null]},e.value)})})}):null;return L(`div`,{class:`relative ${t}`,ref:d,children:[L(`button`,{"aria-controls":u,"aria-expanded":h,"aria-haspopup":`listbox`,"aria-label":e,class:`ssh-custom-select-trigger flex w-full cursor-pointer items-center justify-between gap-3 rounded-xl text-left outline-none transition disabled:cursor-not-allowed disabled:bg-slate-100 disabled:text-slate-400 disabled:opacity-60 ${ee}`,disabled:n,id:l,onClick:()=>{h?k():(O(),g(!0))},onKeyDown:e=>{switch(e.key){case`ArrowDown`:e.preventDefault(),M(h?Mn(a,_,1):Nn(a,o));break;case`ArrowUp`:e.preventDefault(),M(h?Mn(a,_,-1):Nn(a,o));break;case`Home`:e.preventDefault(),M(An(a));break;case`End`:e.preventDefault(),M(jn(a));break;case`Enter`:case` `:e.preventDefault(),h?k():M();break;case`Escape`:h&&(e.preventDefault(),k())}},ref:f,type:`button`,children:[L(`span`,{class:`min-w-0 truncate`,children:D?.label??`선택하세요`}),L(_t,{"aria-hidden":`true`,class:`size-4 shrink-0 text-slate-400 transition-transform ${h?`rotate-180`:``}`})]}),te&&T?vn(te,T):null,L(`span`,{class:`sr-only`,"aria-live":`polite`,children:y===`top`?`선택 목록이 위쪽에 열렸습니다.`:``})]})}function Fn(e){let t=e.split(`.`);return[t[0]??``,t[1]??``,t[2]??``,t[3]??``]}function In(e){return e.replace(/\D/g,``).slice(0,3)}function Ln(e){return e.every(e=>e===``)?``:e.join(`.`)}function Rn({disabled:e=!1,label:t,value:n,onChange:r}){let i=Fn(n),a=(e,t)=>{let n=[...i];n[e]=In(t),r(Ln(n))};return L(`div`,{class:`ssh-ipv4-segments mt-2 min-h-11`,"aria-label":`${t} 입력`,children:i.map((n,r)=>L(`div`,{class:`ssh-ipv4-segment`,children:[L(`input`,{"aria-label":`${t} ${r+1}번째 옥텟`,autocomplete:`off`,class:`min-h-11 w-full min-w-0 max-w-full rounded-xl border-2 border-slate-300 bg-slate-50 px-1.5 py-2.5 text-center text-sm font-semibold text-slate-950 shadow-inner outline-none transition focus:border-teal-500 focus:bg-white focus:ring-4 focus:ring-teal-100 disabled:cursor-not-allowed disabled:bg-slate-100 sm:px-2`,disabled:e,inputMode:`numeric`,maxLength:3,onInput:e=>a(r,e.currentTarget.value),pattern:`[0-9]*`,spellcheck:!1,value:n}),rt);function Wn(e){return Vn.includes(e)}function Gn(e){switch(e){case`dhcp`:return`자동 IP (DHCP)`;case`pppoe`:return`PPPoE`;case`static`:return`고정 IPv4`;default:return e?e.toUpperCase():`확인 전`}}function Kn(e){return{protocol:e.configuration.protocol,pppoeUsername:e.configuration.pppoe.username,pppoePassword:``,staticAddress:e.configuration.static.address??``,staticPrefixLength:e.configuration.static.prefixLength??24,staticGateway:e.configuration.static.gateway??``,dnsPrimary:e.configuration.static.dns[0]??``,dnsSecondary:e.configuration.static.dns[1]??``}}function qn(e){return e.trim()}function Jn(e,t){let n=Kn(t);return e.protocol===n.protocol?e.protocol===`dhcp`||!Wn(e.protocol)?!1:e.protocol===`pppoe`?qn(e.pppoeUsername)!==n.pppoeUsername||e.pppoePassword.length>0:qn(e.staticAddress)!==n.staticAddress||e.staticPrefixLength!==n.staticPrefixLength||qn(e.staticGateway)!==n.staticGateway||qn(e.dnsPrimary)!==n.dnsPrimary||qn(e.dnsSecondary)!==n.dnsSecondary:!0}function Yn(e){let t=e.trim().split(`.`);return t.length===4&&t.every(e=>{if(!/^(0|[1-9][0-9]{0,2})$/.test(e))return!1;let t=Number(e);return t>=0&&t<=255})}function Xn(e){let t=e;return Array.from({length:4},()=>{if(t>=8)return t-=8,255;if(t<=0)return 0;let e=256-2**(8-t);return t=0,e}).join(`.`)}function Zn(e){if(!Number.isFinite(e)||e<=0)return`연결 시간 확인 전`;let t=Math.floor(e/86400),n=Math.floor(e%86400/3600),r=Math.floor(e%3600/60);return t>0?`${t}일 ${n}시간 연결됨`:n>0?`${n}시간 ${r}분 연결됨`:`${Math.max(1,r)}분 연결됨`}function Qn({feedback:e,onDismiss:t}){return L(`div`,{"aria-live":`polite`,class:`mb-5 rounded-2xl border px-4 py-4 text-sm ${e.kind===`success`?`border-emerald-200 bg-emerald-50 text-emerald-900`:e.kind===`warning`?`border-amber-200 bg-amber-50 text-amber-950`:`border-red-200 bg-red-50 text-red-900`}`,children:L(`div`,{class:`flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between`,children:[L(`p`,{class:`m-0 font-bold leading-6`,children:e.message}),L(`button`,{class:`min-h-10 shrink-0 rounded-lg border border-current bg-transparent px-3 py-1.5 text-xs font-extrabold`,onClick:t,type:`button`,children:`닫기`})]})})}function $n({data:e,busy:t,onReconnect:n}){let r=e.status,i=r.connected;return L(`section`,{class:`grid gap-4 lg:grid-cols-3`,"aria-label":`인터넷 연결 상태`,children:[L(`article`,{class:`rounded-2xl border p-5 shadow-sm shadow-slate-900/5 ${i?`border-emerald-200 bg-emerald-50 text-emerald-950`:`border-amber-200 bg-amber-50 text-amber-950`}`,children:[L(`div`,{class:`flex items-start justify-between gap-3`,children:[L(`span`,{class:`grid size-10 place-items-center rounded-xl ${i?`bg-emerald-100 text-emerald-700`:`bg-amber-100 text-amber-700`}`,children:L(i?ut:B,{class:`size-5`})}),L(`button`,{class:`inline-flex min-h-10 items-center justify-center gap-2 rounded-xl border border-current bg-transparent px-3 py-2 text-xs font-extrabold transition disabled:cursor-wait disabled:opacity-60`,disabled:t,onClick:n,type:`button`,children:[L(z,{class:`size-4 ${t?`animate-spin`:``}`}),`재연결`]})]}),L(`p`,{class:`mt-4 mb-0 text-xs font-extrabold uppercase tracking-[0.14em] opacity-70`,children:`인터넷 상태`}),L(`p`,{class:`mt-2 mb-0 text-xl font-black`,children:r.pending?`연결 중`:i?`연결됨`:`연결 안 됨`}),L(`p`,{class:`mt-1 mb-0 text-sm font-semibold leading-6 opacity-80`,children:[Gn(r.protocol??e.configuration.protocol),` · `,Zn(r.uptimeSeconds)]})]}),L(`article`,{class:`rounded-2xl border border-slate-200 bg-white p-5 shadow-sm shadow-slate-900/5`,children:[L(`span`,{class:`grid size-10 place-items-center rounded-xl bg-teal-50 text-teal-700`,children:L($e,{class:`size-5`})}),L(`p`,{class:`mt-4 mb-0 text-xs font-extrabold uppercase tracking-[0.14em] text-slate-500`,children:`WAN IPv4`}),L(`p`,{class:`mt-2 mb-0 break-all text-xl font-black text-slate-950`,children:r.address?`${r.address}${r.prefixLength===null?``:`/${r.prefixLength}`}`:`주소 없음`}),L(`p`,{class:`mt-1 mb-0 text-sm font-semibold text-slate-600`,children:r.device?`인터페이스 ${r.device}`:`인터페이스 확인 전`})]}),L(`article`,{class:`rounded-2xl border border-slate-200 bg-white p-5 shadow-sm shadow-slate-900/5`,children:[L(`span`,{class:`grid size-10 place-items-center rounded-xl bg-slate-100 text-slate-700`,children:L(We,{class:`size-5`})}),L(`p`,{class:`mt-4 mb-0 text-xs font-extrabold uppercase tracking-[0.14em] text-slate-500`,children:`Gateway · DNS`}),L(`p`,{class:`mt-2 mb-0 break-all text-base font-black text-slate-950`,children:r.gateway??`Gateway 확인 전`}),L(`p`,{class:`mt-1 mb-0 break-all text-sm font-semibold leading-6 text-slate-600`,children:r.dns.length>0?r.dns.join(` · `):`DNS 확인 전`})]})]})}function er({action:e,data:t,error:n,feedback:r,loading:i,onDismissFeedback:a,onReconnect:o,onRetry:s,onSave:c}){let[l,u]=F({protocol:`dhcp`,pppoeUsername:``,pppoePassword:``,staticAddress:``,staticPrefixLength:24,staticGateway:``,dnsPrimary:``,dnsSecondary:``}),[d,f]=F(!1),[p,m]=F(!1),[h,g]=F(null);I(()=>{t&&!d&&(u(Kn(t)),g(null))},[t,d]);let _=Te(()=>{let e=[{value:`dhcp`,label:`자동 IP (DHCP)`},{value:`pppoe`,label:`PPPoE`},{value:`static`,label:`고정 IPv4`}];return t&&!t.configuration.supported&&e.unshift({value:t.configuration.protocol,label:`현재 설정 (${t.configuration.protocol})`}),e},[t]);if(i)return L(zn,{});if(n)return L(Bn,{message:n,onRetry:s});if(!t)return null;let y=e!==null,b=e=>{u(n=>{let r={...n,...e};return f(Jn(r,t)),r}),g(null)},x=async e=>{if(e.preventDefault(),!Wn(l.protocol))g(`DHCP, PPPoE 또는 고정 IPv4 중 하나를 선택해 주세요.`);else{if(l.protocol===`pppoe`){if(!qn(l.pppoeUsername)){g(`PPPoE 사용자명을 입력해 주세요.`);return}if((t.configuration.protocol!==`pppoe`||!t.configuration.pppoe.passwordConfigured)&&l.pppoePassword.length===0){g(`PPPoE 연결을 시작하려면 비밀번호를 입력해 주세요.`);return}}if(l.protocol===`static`){if(!Yn(l.staticAddress)){g(`고정 IPv4 주소를 올바르게 입력해 주세요.`);return}if(!Yn(l.staticGateway)){g(`기본 게이트웨이를 올바르게 입력해 주세요.`);return}if(!Yn(l.dnsPrimary)){g(`기본 DNS 서버를 올바르게 입력해 주세요.`);return}if(qn(l.dnsSecondary)&&!Yn(l.dnsSecondary)){g(`보조 DNS 서버를 올바르게 입력해 주세요.`);return}}window.confirm(`인터넷 연결 설정을 적용하면 WAN 연결이 다시 시작되어 인터넷이 잠시 끊길 수 있습니다. 계속하시겠습니까?`)&&await c({protocol:l.protocol,pppoeUsername:qn(l.pppoeUsername),pppoePassword:l.pppoePassword,pppoePasswordChanged:l.pppoePassword.length>0,staticAddress:qn(l.staticAddress),staticPrefixLength:l.staticPrefixLength,staticGateway:qn(l.staticGateway),dnsPrimary:qn(l.dnsPrimary),dnsSecondary:qn(l.dnsSecondary)})&&(u(e=>({...e,pppoePassword:``})),f(!1))}};return L(v,{children:[r?L(Qn,{feedback:r,onDismiss:a}):null,L(V,{eyebrow:`Overview`,title:`인터넷 연결 상태`,description:`WAN 연결 상태와 네트워크 정보를 확인합니다.`}),L($n,{data:t,busy:y,onReconnect:()=>{window.confirm(`WAN 연결을 다시 시작합니다. 인터넷이 잠시 끊길 수 있습니다. 계속하시겠습니까?`)&&o()}}),t.configuration.supported?null:L(`section`,{class:`mt-5 rounded-2xl border border-amber-200 bg-amber-50 p-5 text-amber-950 sm:p-6`,children:L(`div`,{class:`flex items-start gap-3`,children:[L(B,{class:`mt-0.5 size-5 shrink-0 text-amber-700`}),L(`div`,{children:[L(`h2`,{class:`m-0 text-base font-black`,children:`현재 WAN 방식은 직접 관리하지 않습니다`}),L(`p`,{class:`mt-2 mb-0 text-sm font-semibold leading-6 text-amber-900/80`,children:[`현재 설정은 `,t.configuration.protocol,`입니다. 아래에서 DHCP, PPPoE 또는 고정 IPv4를 선택해 저장하면 해당 방식으로 전환합니다.`]})]})]})}),L(`div`,{class:`mt-7`,children:L(V,{eyebrow:`Internet`,title:`인터넷 연결 설정`,description:`통신사 또는 상위 네트워크 환경에 맞는 WAN 연결 방식을 선택합니다.`})}),L(`form`,{class:`rounded-2xl border border-slate-200 bg-white p-5 shadow-sm shadow-slate-900/5 sm:p-6`,onSubmit:e=>void x(e),children:[d?L(`div`,{class:`mb-5 flex justify-end`,children:L(`span`,{"aria-live":`polite`,class:`inline-flex shrink-0 self-start items-center gap-1.5 rounded-full border border-amber-200 bg-amber-50 px-2.5 py-1 text-xs font-extrabold text-amber-800`,role:`status`,children:[L(B,{"aria-hidden":`true`,class:`size-3.5 shrink-0`}),`저장되지 않음`]})}):null,L(`div`,{children:[L(`span`,{class:`text-sm font-extrabold text-slate-800`,children:`연결 방식`}),L(Pn,{ariaLabel:`WAN 연결 방식`,className:`mt-2 max-w-xl`,disabled:y,id:`wan-protocol`,onChange:e=>b(e===`static`&&!qn(l.dnsSecondary)?{protocol:e,dnsSecondary:Hn}:{protocol:e}),options:_,value:l.protocol,variant:`emphasized`}),L(`span`,{class:`mt-2 block text-xs font-semibold leading-5 text-slate-500`,children:`대부분의 가정용 인터넷과 상위 공유기 연결은 자동 IP(DHCP)를 사용합니다.`})]}),l.protocol===`pppoe`?L(`div`,{class:`mt-6 grid gap-5 lg:grid-cols-2`,children:[L(`label`,{class:`block`,children:[L(`span`,{class:`text-sm font-extrabold text-slate-800`,children:`PPPoE 사용자명`}),L(`input`,{autocomplete:`username`,class:`mt-2 min-h-11 w-full rounded-xl border-2 border-slate-300 bg-slate-50 px-4 py-2.5 text-sm font-semibold text-slate-950 shadow-inner outline-none transition focus:border-teal-500 focus:bg-white focus:ring-4 focus:ring-teal-100 disabled:cursor-not-allowed disabled:bg-slate-100`,disabled:y,onInput:e=>b({pppoeUsername:e.currentTarget.value}),spellcheck:!1,value:l.pppoeUsername})]}),L(`label`,{class:`block`,children:[L(`span`,{class:`text-sm font-extrabold text-slate-800`,children:`PPPoE 비밀번호`}),L(`span`,{class:`relative mt-2 block`,children:[L(`input`,{autocomplete:`new-password`,class:`min-h-11 w-full rounded-xl border-2 border-slate-300 bg-slate-50 px-4 py-2.5 pr-12 text-sm font-semibold text-slate-950 shadow-inner outline-none transition focus:border-teal-500 focus:bg-white focus:ring-4 focus:ring-teal-100 disabled:cursor-not-allowed disabled:bg-slate-100`,disabled:y,onInput:e=>b({pppoePassword:e.currentTarget.value}),placeholder:t.configuration.protocol===`pppoe`&&t.configuration.pppoe.passwordConfigured?`변경할 때만 입력`:`PPPoE 비밀번호`,...p?{type:`text`}:{type:`password`},value:l.pppoePassword}),L(`button`,{"aria-label":p?`비밀번호 숨기기`:`비밀번호 표시`,class:`absolute inset-y-0 right-1 grid w-10 place-items-center rounded-lg border-0 bg-transparent text-slate-500`,disabled:y,onClick:()=>m(e=>!e),type:`button`,children:L(p?ct:st,{class:`size-5`})})]}),L(`span`,{class:`mt-2 block text-xs text-slate-500`,children:t.configuration.protocol===`pppoe`&&t.configuration.pppoe.passwordConfigured?`비워 두면 현재 저장된 비밀번호를 그대로 사용합니다.`:`통신사에서 제공한 PPPoE 비밀번호를 입력해 주세요.`})]})]}):null,l.protocol===`static`?L(`div`,{class:`mt-6 grid gap-5 lg:grid-cols-2`,children:[L(`label`,{class:`block`,children:[L(`span`,{class:`text-sm font-extrabold text-slate-800`,children:`IPv4 주소`}),L(Rn,{disabled:y,label:`IPv4 주소`,onChange:e=>b({staticAddress:e}),value:l.staticAddress})]}),L(`div`,{class:`block`,children:[L(`span`,{class:`text-sm font-extrabold text-slate-800`,children:`서브넷 마스크`}),L(Pn,{ariaLabel:`WAN 서브넷 마스크`,className:`mt-2`,disabled:y,id:`wan-static-prefix-length`,onChange:e=>b({staticPrefixLength:Number(e)}),options:Un.map(e=>({value:String(e),label:`/${e} · ${Xn(e)}`})),value:String(l.staticPrefixLength),variant:`emphasized`})]}),L(`label`,{class:`block`,children:[L(`span`,{class:`text-sm font-extrabold text-slate-800`,children:`기본 게이트웨이`}),L(Rn,{disabled:y,label:`기본 게이트웨이`,onChange:e=>b({staticGateway:e}),value:l.staticGateway})]}),L(`label`,{class:`block`,children:[L(`span`,{class:`text-sm font-extrabold text-slate-800`,children:`기본 DNS`}),L(Rn,{disabled:y,label:`기본 DNS`,onChange:e=>b({dnsPrimary:e}),value:l.dnsPrimary})]}),L(`label`,{class:`block lg:col-start-2`,children:[L(`span`,{class:`text-sm font-extrabold text-slate-800`,children:`보조 DNS (선택)`}),L(Rn,{disabled:y,label:`보조 DNS`,onChange:e=>b({dnsSecondary:e}),value:l.dnsSecondary})]})]}):null,l.protocol===`dhcp`?L(`div`,{class:`mt-6 rounded-xl border border-slate-200 bg-slate-50 px-4 py-4 text-sm font-semibold leading-6 text-slate-600`,children:`상위 공유기 또는 통신사 장비에서 IP 주소, 기본 게이트웨이와 DNS 정보를 자동으로 받습니다. 별도의 계정이나 고정 주소가 없다면 이 방식을 사용하세요.`}):null,h?L(`p`,{class:`mt-5 mb-0 rounded-xl bg-red-50 px-4 py-3 text-sm font-bold text-red-800`,children:h}):null,L(`div`,{class:`mt-6 flex flex-col gap-3 border-t border-slate-200 pt-5 sm:flex-row sm:items-center sm:justify-between`,children:[L(`p`,{class:`m-0 text-xs font-semibold leading-5 text-slate-500`,children:`설정 저장 후 WAN만 다시 연결하며 LAN 관리 페이지와 내부 네트워크 설정은 유지됩니다.`}),L(`button`,{class:`inline-flex min-h-11 w-full shrink-0 items-center justify-center gap-2 rounded-xl border-0 bg-teal-700 px-5 py-2.5 text-sm font-extrabold text-white shadow-sm transition hover:bg-teal-800 focus:outline-none focus-visible:ring-4 focus-visible:ring-teal-200 disabled:cursor-wait disabled:opacity-60 sm:w-auto`,disabled:y||!d,type:`submit`,children:[L(ut,{class:`size-5 ${e===`saving`?`animate-pulse`:``}`}),e===`saving`?`저장 중`:`설정 저장`]})]})]})]})}var tr=[16,20,22,23,24,25,26,27,28],nr=[[`30m`,`30분`],[`1h`,`1시간`],[`6h`,`6시간`],[`12h`,`12시간`],[`24h`,`24시간`],[`3d`,`3일`],[`7d`,`7일`]];function rr(e){let t=e.trim().split(`.`);return t.length===4&&t.every(e=>{if(!/^(0|[1-9][0-9]{0,2})$/.test(e))return!1;let t=Number(e);return t>=0&&t<=255})}function ir(e){let t=Fn(e);return t.slice(0,3).some(e=>e===``||Number(e)>255)?null:t.slice(0,3).join(`.`)}function ar(e){return Fn(e)[3]}function or(e){let t=ir(e.lan.address),n=ar(e.dhcp.start??``),r=ar(e.dhcp.end??``);return{ipAddress:e.lan.address,prefixLength:e.lan.prefixLength,dhcpEnabled:e.dhcp.enabled,dhcpStart:t&&n?`${t}.${n}`:e.dhcp.start??``,dhcpEnd:t&&r?`${t}.${r}`:e.dhcp.end??``,leaseTime:e.dhcp.leaseTime}}function sr({disabled:e,label:t,lanAddress:n,value:r,onChange:i}){let a=ir(n),o=a?.split(`.`)??[``,``,``],s=ar(r),c=e=>{let t=In(e);i(a&&t!==``?`${a}.${t}`:t)};return L(`label`,{class:`block`,children:[L(`span`,{class:`text-sm font-extrabold text-slate-800`,children:t}),L(`div`,{class:`ssh-ipv4-segments mt-2 min-h-11`,"aria-label":`${t} 입력`,children:[o.map((e,t)=>L(`div`,{class:`ssh-ipv4-segment`,children:[L(`span`,{class:`grid min-h-11 w-full min-w-0 max-w-full place-items-center rounded-xl border border-slate-200 bg-slate-100 px-1.5 py-2.5 text-center text-sm font-bold text-slate-500 sm:px-2`,children:e||`—`}),L(`span`,{"aria-hidden":`true`,class:`ssh-ipv4-separator text-sm font-black text-slate-400`,children:`.`})]},t)),L(`div`,{class:`ssh-ipv4-segment`,children:L(`input`,{"aria-label":`${t} 마지막 옥텟`,autocomplete:`off`,class:`min-h-11 w-full min-w-0 max-w-full rounded-xl border-2 border-slate-300 bg-slate-50 px-1.5 py-2.5 text-center text-sm font-semibold text-slate-950 shadow-inner outline-none transition focus:border-teal-500 focus:bg-white focus:ring-4 focus:ring-teal-100 disabled:cursor-not-allowed disabled:bg-slate-100 sm:px-2`,disabled:e||!a,inputMode:`numeric`,maxLength:3,onInput:e=>c(e.currentTarget.value),pattern:`[0-9]*`,spellcheck:!1,value:s})})]}),L(`span`,{class:`mt-2 block text-xs text-slate-500`,children:`앞 3개 주소는 공유기 IP 주소와 동일하게 유지됩니다.`})]})}function cr(e){return`${window.location.protocol}//${e}${window.location.pathname}${window.location.search}${window.location.hash}`}function lr({feedback:e,onDismiss:t}){return L(`div`,{"aria-live":`polite`,class:`mb-5 rounded-2xl border px-4 py-4 text-sm ${e.kind===`success`?`border-emerald-200 bg-emerald-50 text-emerald-900`:e.kind===`warning`?`border-amber-200 bg-amber-50 text-amber-950`:`border-red-200 bg-red-50 text-red-900`}`,children:[L(`div`,{class:`flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between`,children:[L(`p`,{class:`m-0 font-bold leading-6`,children:e.message}),L(`button`,{class:`min-h-10 shrink-0 rounded-lg border border-current bg-transparent px-3 py-1.5 text-xs font-extrabold`,onClick:t,type:`button`,children:`닫기`})]}),e.newAddress?L(`a`,{class:`mt-3 inline-flex min-h-10 items-center rounded-lg border border-current px-3 py-2 font-extrabold underline underline-offset-2`,href:cr(e.newAddress),children:[`새 공유기 주소 `,e.newAddress,`로 연결`]}):null]})}function ur({data:e}){let t=e.conflict.detected;return L(`section`,{class:`grid gap-4 lg:grid-cols-3`,"aria-label":`내부 네트워크 상태`,children:[L(`article`,{class:`rounded-2xl border border-slate-200 bg-white p-5 shadow-sm shadow-slate-900/5`,children:[L(`span`,{class:`grid size-10 place-items-center rounded-xl bg-teal-50 text-teal-700`,children:L(We,{class:`size-5`})}),L(`p`,{class:`mt-4 mb-0 text-xs font-extrabold uppercase tracking-[0.14em] text-slate-500`,children:`SmartSafeHub LAN`}),L(`p`,{class:`mt-2 mb-0 text-xl font-black text-slate-950`,children:e.lan.subnet}),L(`p`,{class:`mt-1 mb-0 text-sm font-semibold text-slate-600`,children:[`공유기 주소 `,e.lan.address]})]}),L(`article`,{class:`rounded-2xl border border-slate-200 bg-white p-5 shadow-sm shadow-slate-900/5`,children:[L(`span`,{class:`grid size-10 place-items-center rounded-xl bg-slate-100 text-slate-700`,children:L($e,{class:`size-5`})}),L(`p`,{class:`mt-4 mb-0 text-xs font-extrabold uppercase tracking-[0.14em] text-slate-500`,children:`상위 네트워크`}),L(`p`,{class:`mt-2 mb-0 text-xl font-black text-slate-950`,children:e.wan.subnet??`확인 전`}),L(`p`,{class:`mt-1 mb-0 text-sm font-semibold text-slate-600`,children:e.wan.connected?`WAN ${e.wan.address??`주소 확인 중`}`:`WAN 연결 후 자동으로 확인합니다.`})]}),L(`article`,{class:`rounded-2xl border p-5 shadow-sm shadow-slate-900/5 ${t?`border-red-200 bg-red-50 text-red-950`:`border-emerald-200 bg-emerald-50 text-emerald-950`}`,children:[L(`span`,{class:`grid size-10 place-items-center rounded-xl ${t?`bg-red-100 text-red-700`:`bg-emerald-100 text-emerald-700`}`,children:L(t?B:ut,{class:`size-5`})}),L(`p`,{class:`mt-4 mb-0 text-xs font-extrabold uppercase tracking-[0.14em] opacity-70`,children:`대역 충돌`}),L(`p`,{class:`mt-2 mb-0 text-lg font-black`,children:t?`주소 대역이 겹칩니다`:`충돌이 감지되지 않았습니다`}),L(`p`,{class:`mt-1 mb-0 text-sm font-semibold leading-6 opacity-80`,children:t?`상위 네트워크 ${e.conflict.wanSubnet??``}와 다른 LAN 대역이 필요합니다.`:e.wan.connected?`현재 WAN과 LAN이 서로 다른 IPv4 대역을 사용합니다.`:`WAN이 연결되면 상위 네트워크와의 충돌 여부를 확인합니다.`})]})]})}function dr({action:e,data:t,error:n,feedback:r,loading:i,onApplyRecommendation:a,onDismissFeedback:o,onRetry:s,onSave:c}){let[l,u]=F(``),[d,f]=F(24),[p,m]=F(!0),[h,g]=F(``),[_,y]=F(``),[b,x]=F(`12h`),[S,C]=F(!1),[w,T]=F(null),E=(e={})=>{if(!t)return!1;let n=or(t);return(e.ipAddress??l).trim()!==n.ipAddress||(e.prefixLength??d)!==n.prefixLength||(e.dhcpEnabled??p)!==n.dhcpEnabled||(e.dhcpStart??h).trim()!==n.dhcpStart||(e.dhcpEnd??_).trim()!==n.dhcpEnd||(e.leaseTime??b)!==n.leaseTime},D=e=>{let t=ir(e);if(u(e),!t){C(E({ipAddress:e}));return}let n=ar(h),r=ar(_),i=n?`${t}.${n}`:``,a=r?`${t}.${r}`:``;g(i),y(a),C(E({ipAddress:e,dhcpStart:i,dhcpEnd:a}))},O=e=>{g(e),C(E({dhcpStart:e}))},k=e=>{y(e),C(E({dhcpEnd:e}))},A=e=>{f(e),C(E({prefixLength:e}))},j=e=>{x(e),C(E({leaseTime:e}))},M=e=>{m(e),C(E({dhcpEnabled:e}))};I(()=>{if(!t||S)return;let e=or(t);u(e.ipAddress),f(e.prefixLength),m(e.dhcpEnabled),g(e.dhcpStart),y(e.dhcpEnd),x(e.leaseTime),T(null)},[t,S]);let ee=Te(()=>{let e=[...tr];return e.includes(d)||(e.push(d),e.sort((e,t)=>e-t)),e},[d]),te=Te(()=>{let e=[...nr];return e.some(([e])=>e===b)||e.push([b,`현재 설정 (${b})`]),e},[b]);if(i)return L(zn,{});if(n)return L(Bn,{message:n,onRetry:s});if(!t)return null;let ne=e!==null,re=async e=>{e.preventDefault(),rr(l)?!rr(h)||!rr(_)?T(`DHCP 시작/종료 주소를 올바른 IPv4 형식으로 입력해 주세요.`):window.confirm(`내부 네트워크 설정을 적용하면 연결된 기기가 새 DHCP 정보를 받는 동안 잠시 연결이 끊길 수 있습니다. 계속하시겠습니까?`)&&(T(null),await c({ipAddress:l.trim(),prefixLength:d,dhcpEnabled:p,dhcpStart:h.trim(),dhcpEnd:_.trim(),leaseTime:b})&&C(!1)):T(`공유기 주소를 올바른 IPv4 형식으로 입력해 주세요.`)},ie=async()=>{t.recommendation&&window.confirm(`상위 네트워크와 겹치지 않는 ${t.recommendation.subnet} 대역으로 자동 변경합니다. 현재 연결이 잠시 끊길 수 있습니다. 계속하시겠습니까?`)&&(T(null),await a()&&C(!1))};return L(v,{children:[r?L(lr,{feedback:r,onDismiss:o}):null,L(V,{eyebrow:`Overview`,title:`내부 네트워크 상태`,description:`LAN 주소와 DHCP 할당 상태를 확인합니다.`}),L(ur,{data:t}),t.conflict.detected?L(`section`,{class:`mt-5 rounded-2xl border border-amber-200 bg-amber-50 p-5 text-amber-950 sm:p-6`,children:L(`div`,{class:`flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between`,children:[L(`div`,{class:`min-w-0`,children:[L(`p`,{class:`m-0 text-xs font-extrabold uppercase tracking-[0.16em] text-amber-700`,children:`자동 충돌 해결`}),L(`h2`,{class:`mt-2 mb-0 text-lg font-black`,children:`안전한 LAN 대역으로 변경할 수 있습니다`}),L(`p`,{class:`mt-2 mb-0 text-sm font-semibold leading-6 text-amber-900/80`,children:t.recommendation?`${t.recommendation.subnet} · DHCP ${t.recommendation.dhcpStart} ~ ${t.recommendation.dhcpEnd}`:`현재 활성 네트워크와 겹치지 않는 추천 대역을 찾지 못했습니다. 아래에서 직접 설정해 주세요.`})]}),t.recommendation?L(`button`,{class:`inline-flex min-h-11 shrink-0 items-center justify-center gap-2 rounded-xl border-0 bg-amber-700 px-5 py-2.5 text-sm font-extrabold text-white transition hover:bg-amber-800 focus:outline-none focus-visible:ring-4 focus-visible:ring-amber-200 disabled:cursor-wait disabled:opacity-60`,disabled:ne,onClick:()=>void ie(),type:`button`,children:[L(ut,{class:`size-5 ${e===`auto`?`animate-pulse`:``}`}),e===`auto`?`변경 중`:`추천 대역으로 자동 변경`]}):null]})}):null,L(`div`,{class:`mt-7`,children:L(V,{eyebrow:`Local network`,title:`LAN 및 DHCP 설정`,description:`상위 공유기와 다른 주소 대역을 사용해야 안정적으로 라우팅할 수 있습니다.`})}),L(`form`,{class:`rounded-2xl border border-slate-200 bg-white p-5 shadow-sm shadow-slate-900/5 sm:p-6`,onSubmit:e=>void re(e),children:[S?L(`div`,{class:`mb-5 flex justify-end`,children:L(`span`,{"aria-live":`polite`,class:`inline-flex shrink-0 self-start items-center gap-1.5 rounded-full border border-amber-200 bg-amber-50 px-2.5 py-1 text-xs font-extrabold text-amber-800`,role:`status`,children:[L(B,{"aria-hidden":`true`,class:`size-3.5 shrink-0`}),`저장되지 않음`]})}):null,L(`div`,{class:`grid gap-5 lg:grid-cols-2`,children:[L(`div`,{class:`block`,children:[L(`span`,{class:`text-sm font-extrabold text-slate-800`,children:`공유기 IP 주소`}),L(Rn,{disabled:ne,label:`공유기 IP 주소`,onChange:D,value:l})]}),L(`div`,{class:`block`,children:[L(`span`,{class:`text-sm font-extrabold text-slate-800`,children:`현재 LAN 대역`}),L(`div`,{class:`mt-2 min-h-11 rounded-xl border border-slate-200 bg-slate-50 px-4 py-3 text-sm font-bold text-slate-700`,children:t.lan.subnet}),L(`span`,{class:`mt-2 block text-xs text-slate-500`,children:`서브넷 마스크는 아래 고급 설정에서 변경할 수 있습니다.`})]}),L(sr,{disabled:ne,label:`DHCP 시작 주소`,lanAddress:l,onChange:O,value:h}),L(sr,{disabled:ne,label:`DHCP 종료 주소`,lanAddress:l,onChange:k,value:_})]}),L(`details`,{class:`mt-6 rounded-xl border border-slate-200 bg-slate-50 p-4`,children:[L(`summary`,{class:`cursor-pointer text-sm font-black text-slate-900`,children:`고급 DHCP 설정`}),L(`div`,{class:`mt-5 grid gap-5 lg:grid-cols-2`,children:[L(`div`,{class:`block`,children:[L(`span`,{class:`text-sm font-extrabold text-slate-800`,children:`서브넷 마스크`}),L(Pn,{ariaLabel:`서브넷 마스크`,className:`mt-2`,disabled:ne,id:`lan-prefix-length`,onChange:e=>A(Number(e)),options:ee.map(e=>({value:String(e),label:`/${e} · ${e===t.lan.prefixLength?t.lan.netmask:`CIDR /${e}`}`})),value:String(d),variant:`emphasized`}),L(`span`,{class:`mt-2 block text-xs text-slate-500`,children:`일반 가정용 네트워크는 /24 사용을 권장합니다.`})]}),L(`div`,{class:`block`,children:[L(`span`,{class:`text-sm font-extrabold text-slate-800`,children:`DHCP 임대 시간`}),L(Pn,{ariaLabel:`DHCP 임대 시간`,className:`mt-2`,disabled:ne,id:`lan-dhcp-lease-time`,onChange:j,options:te.map(([e,t])=>({value:e,label:t})),value:b,variant:`emphasized`})]}),L(`label`,{class:`inline-flex min-h-11 items-center gap-3 text-sm font-extrabold text-slate-800 lg:col-span-2`,children:[L(`input`,{checked:p,class:`size-5 accent-teal-700`,disabled:ne,onChange:e=>M(e.currentTarget.checked),type:`checkbox`}),`SmartSafeHub DHCP 서버 사용`]}),L(`p`,{class:`m-0 text-xs font-semibold leading-5 text-slate-500 lg:col-span-2`,children:`DHCP를 끄면 SmartSafeHub에 연결되는 장치에 IP 주소를 자동으로 할당하지 않습니다. 별도의 DHCP 서버를 사용하는 고급 구성에서만 비활성화하세요.`})]})]}),w?L(`p`,{class:`mt-5 mb-0 rounded-xl bg-red-50 px-4 py-3 text-sm font-bold text-red-800`,children:w}):null,L(`div`,{class:`mt-6 flex flex-col gap-3 border-t border-slate-200 pt-5 sm:flex-row sm:items-center sm:justify-between`,children:[L(`p`,{class:`m-0 text-xs font-semibold leading-5 text-slate-500`,children:`주소가 바뀌면 현재 관리 페이지 연결이 종료되며 새 IP를 받아 다시 접속해야 합니다.`}),L(`button`,{class:`inline-flex min-h-11 w-full shrink-0 items-center justify-center gap-2 rounded-xl border-0 bg-teal-700 px-5 py-2.5 text-sm font-extrabold text-white shadow-sm transition hover:bg-teal-800 focus:outline-none focus-visible:ring-4 focus-visible:ring-teal-200 disabled:cursor-wait disabled:opacity-60 sm:w-auto`,disabled:ne||!S,type:`submit`,children:[L(ut,{class:`size-5 ${e===`saving`?`animate-pulse`:``}`}),e===`saving`?`저장 중`:`설정 저장`]})]})]})]})}var fr={};fr.QR8bitByte=(e,t)=>{var n=t("QRMode");function r(e){this.mode=n.MODE_8BIT_BYTE,this.data=e}r.prototype={getLength:function(){return this.data.length},write:function(e){for(var t=0;t{function n(){this.buffer=[],this.length=0}n.prototype={get:function(e){var t=Math.floor(e/8);return(this.buffer[t]>>>7-e%8&1)==1},put:function(e,t){for(var n=0;n>>t-n-1&1)==1)},getLengthInBits:function(){return this.length},putBit:function(e){var t=Math.floor(this.length/8);this.buffer.length<=t&&this.buffer.push(0),e&&(this.buffer[t]|=128>>>this.length%8),this.length++}},e.exports=n},fr.QRErrorCorrectLevel=(e,t)=>{e.exports={L:1,M:0,Q:3,H:2}},fr.QRMaskPattern=(e,t)=>{e.exports={PATTERN000:0,PATTERN001:1,PATTERN010:2,PATTERN011:3,PATTERN100:4,PATTERN101:5,PATTERN110:6,PATTERN111:7}},fr.QRMath=(e,t)=>{for(var n={glog:function(e){if(e<1)throw Error(`glog(`+e+`)`);return n.LOG_TABLE[e]},gexp:function(e){for(;e<0;)e+=255;for(;e>=256;)e-=255;return n.EXP_TABLE[e]},EXP_TABLE:Array(256),LOG_TABLE:Array(256)},r=0;r<8;r++)n.EXP_TABLE[r]=1<{e.exports={MODE_NUMBER:1,MODE_ALPHA_NUM:2,MODE_8BIT_BYTE:4,MODE_KANJI:8}},fr.QRPolynomial=(e,t)=>{var n=t("QRMath");function r(e,t){if(e.length===void 0)throw Error(e.length+`/`+t);for(var n=0;n{var n=t("QRErrorCorrectLevel");function r(e,t){this.totalCount=e,this.dataCount=t}r.RS_BLOCK_TABLE=[[1,26,19],[1,26,16],[1,26,13],[1,26,9],[1,44,34],[1,44,28],[1,44,22],[1,44,16],[1,70,55],[1,70,44],[2,35,17],[2,35,13],[1,100,80],[2,50,32],[2,50,24],[4,25,9],[1,134,108],[2,67,43],[2,33,15,2,34,16],[2,33,11,2,34,12],[2,86,68],[4,43,27],[4,43,19],[4,43,15],[2,98,78],[4,49,31],[2,32,14,4,33,15],[4,39,13,1,40,14],[2,121,97],[2,60,38,2,61,39],[4,40,18,2,41,19],[4,40,14,2,41,15],[2,146,116],[3,58,36,2,59,37],[4,36,16,4,37,17],[4,36,12,4,37,13],[2,86,68,2,87,69],[4,69,43,1,70,44],[6,43,19,2,44,20],[6,43,15,2,44,16],[4,101,81],[1,80,50,4,81,51],[4,50,22,4,51,23],[3,36,12,8,37,13],[2,116,92,2,117,93],[6,58,36,2,59,37],[4,46,20,6,47,21],[7,42,14,4,43,15],[4,133,107],[8,59,37,1,60,38],[8,44,20,4,45,21],[12,33,11,4,34,12],[3,145,115,1,146,116],[4,64,40,5,65,41],[11,36,16,5,37,17],[11,36,12,5,37,13],[5,109,87,1,110,88],[5,65,41,5,66,42],[5,54,24,7,55,25],[11,36,12],[5,122,98,1,123,99],[7,73,45,3,74,46],[15,43,19,2,44,20],[3,45,15,13,46,16],[1,135,107,5,136,108],[10,74,46,1,75,47],[1,50,22,15,51,23],[2,42,14,17,43,15],[5,150,120,1,151,121],[9,69,43,4,70,44],[17,50,22,1,51,23],[2,42,14,19,43,15],[3,141,113,4,142,114],[3,70,44,11,71,45],[17,47,21,4,48,22],[9,39,13,16,40,14],[3,135,107,5,136,108],[3,67,41,13,68,42],[15,54,24,5,55,25],[15,43,15,10,44,16],[4,144,116,4,145,117],[17,68,42],[17,50,22,6,51,23],[19,46,16,6,47,17],[2,139,111,7,140,112],[17,74,46],[7,54,24,16,55,25],[34,37,13],[4,151,121,5,152,122],[4,75,47,14,76,48],[11,54,24,14,55,25],[16,45,15,14,46,16],[6,147,117,4,148,118],[6,73,45,14,74,46],[11,54,24,16,55,25],[30,46,16,2,47,17],[8,132,106,4,133,107],[8,75,47,13,76,48],[7,54,24,22,55,25],[22,45,15,13,46,16],[10,142,114,2,143,115],[19,74,46,4,75,47],[28,50,22,6,51,23],[33,46,16,4,47,17],[8,152,122,4,153,123],[22,73,45,3,74,46],[8,53,23,26,54,24],[12,45,15,28,46,16],[3,147,117,10,148,118],[3,73,45,23,74,46],[4,54,24,31,55,25],[11,45,15,31,46,16],[7,146,116,7,147,117],[21,73,45,7,74,46],[1,53,23,37,54,24],[19,45,15,26,46,16],[5,145,115,10,146,116],[19,75,47,10,76,48],[15,54,24,25,55,25],[23,45,15,25,46,16],[13,145,115,3,146,116],[2,74,46,29,75,47],[42,54,24,1,55,25],[23,45,15,28,46,16],[17,145,115],[10,74,46,23,75,47],[10,54,24,35,55,25],[19,45,15,35,46,16],[17,145,115,1,146,116],[14,74,46,21,75,47],[29,54,24,19,55,25],[11,45,15,46,46,16],[13,145,115,6,146,116],[14,74,46,23,75,47],[44,54,24,7,55,25],[59,46,16,1,47,17],[12,151,121,7,152,122],[12,75,47,26,76,48],[39,54,24,14,55,25],[22,45,15,41,46,16],[6,151,121,14,152,122],[6,75,47,34,76,48],[46,54,24,10,55,25],[2,45,15,64,46,16],[17,152,122,4,153,123],[29,74,46,14,75,47],[49,54,24,10,55,25],[24,45,15,46,46,16],[4,152,122,18,153,123],[13,74,46,32,75,47],[48,54,24,14,55,25],[42,45,15,32,46,16],[20,147,117,4,148,118],[40,75,47,7,76,48],[43,54,24,22,55,25],[10,45,15,67,46,16],[19,148,118,6,149,119],[18,75,47,31,76,48],[34,54,24,34,55,25],[20,45,15,61,46,16]],r.getRSBlocks=function(e,t){var n=r.getRsBlockTable(e,t);if(n===void 0)throw Error(`bad rs block @ typeNumber:`+e+`/errorCorrectLevel:`+t);for(var i=n.length/3,a=[],o=0;o{var n=t("QRMode"),r=t("QRPolynomial"),i=t("QRMath"),a=t("QRMaskPattern"),o={PATTERN_POSITION_TABLE:[[],[6,18],[6,22],[6,26],[6,30],[6,34],[6,22,38],[6,24,42],[6,26,46],[6,28,50],[6,30,54],[6,32,58],[6,34,62],[6,26,46,66],[6,26,48,70],[6,26,50,74],[6,30,54,78],[6,30,56,82],[6,30,58,86],[6,34,62,90],[6,28,50,72,94],[6,26,50,74,98],[6,30,54,78,102],[6,28,54,80,106],[6,32,58,84,110],[6,30,58,86,114],[6,34,62,90,118],[6,26,50,74,98,122],[6,30,54,78,102,126],[6,26,52,78,104,130],[6,30,56,82,108,134],[6,34,60,86,112,138],[6,30,58,86,114,142],[6,34,62,90,118,146],[6,30,54,78,102,126,150],[6,24,50,76,102,128,154],[6,28,54,80,106,132,158],[6,32,58,84,110,136,162],[6,26,54,82,110,138,166],[6,30,58,86,114,142,170]],G15:1335,G18:7973,G15_MASK:21522,getBCHTypeInfo:function(e){for(var t=e<<10;o.getBCHDigit(t)-o.getBCHDigit(o.G15)>=0;)t^=o.G15<=0;)t^=o.G18<>>=1;return t},getPatternPosition:function(e){return o.PATTERN_POSITION_TABLE[e-1]},getMask:function(e,t,n){switch(e){case a.PATTERN000:return(t+n)%2==0;case a.PATTERN001:return t%2==0;case a.PATTERN010:return n%3==0;case a.PATTERN011:return(t+n)%3==0;case a.PATTERN100:return(Math.floor(t/2)+Math.floor(n/3))%2==0;case a.PATTERN101:return t*n%2+t*n%3==0;case a.PATTERN110:return(t*n%2+t*n%3)%2==0;case a.PATTERN111:return(t*n%3+(t+n)%2)%2==0;default:throw Error(`bad maskPattern:`+e)}},getErrorCorrectPolynomial:function(e){for(var t=new r([1],0),n=0;n5&&(n+=3+a-5)}for(r=0;r{var n=t("QR8bitByte"),r=t("QRUtil"),i=t("QRPolynomial"),a=t("QRRSBlock"),o=t("QRBitBuffer");function s(e,t){this.typeNumber=e,this.errorCorrectLevel=t,this.modules=null,this.moduleCount=0,this.dataCache=null,this.dataList=[]}s.prototype={addData:function(e){var t=new n(e);this.dataList.push(t),this.dataCache=null},isDark:function(e,t){if(e<0||this.moduleCount<=e||t<0||this.moduleCount<=t)throw Error(e+`,`+t);return this.modules[e][t]},getModuleCount:function(){return this.moduleCount},make:function(){if(this.typeNumber<1){var e=1;for(e=1;e<40;e++){for(var t=a.getRSBlocks(e,this.errorCorrectLevel),n=new o,i=0,s=0;s=7&&this.setupTypeNumber(e),this.dataCache===null&&(this.dataCache=s.createData(this.typeNumber,this.errorCorrectLevel,this.dataList)),this.mapData(this.dataCache,t)},setupPositionProbePattern:function(e,t){for(var n=-1;n<=7;n++)if(!(e+n<=-1||this.moduleCount<=e+n))for(var r=-1;r<=7;r++)t+r<=-1||this.moduleCount<=t+r||(0<=n&&n<=6&&(r===0||r===6)||0<=r&&r<=6&&(n===0||n===6)||2<=n&&n<=4&&2<=r&&r<=4?this.modules[e+n][t+r]=!0:this.modules[e+n][t+r]=!1)},getBestMaskPattern:function(){for(var e=0,t=0,n=0;n<8;n++){this.makeImpl(!0,n);var i=r.getLostPoint(this);(n===0||e>i)&&(e=i,t=n)}return t},createMovieClip:function(e,t,n){var r=e.createEmptyMovieClip(t,n),i=1;this.make();for(var a=0;a>i&1)==1,this.modules[Math.floor(i/3)][i%3+this.moduleCount-8-3]=n;for(var a=0;a<18;a++)n=!e&&(t>>a&1)==1,this.modules[a%3+this.moduleCount-8-3][Math.floor(a/3)]=n},setupTypeInfo:function(e,t){for(var n=this.errorCorrectLevel<<3|t,i=r.getBCHTypeInfo(n),a,o=0;o<15;o++)a=!e&&(i>>o&1)==1,o<6?this.modules[o][8]=a:o<8?this.modules[o+1][8]=a:this.modules[this.moduleCount-15+o][8]=a;for(var s=0;s<15;s++)a=!e&&(i>>s&1)==1,s<8?this.modules[8][this.moduleCount-s-1]=a:s<9?this.modules[8][15-s-1+1]=a:this.modules[8][15-s-1]=a;this.modules[this.moduleCount-8][8]=!e},mapData:function(e,t){for(var n=-1,i=this.moduleCount-1,a=7,o=0,s=this.moduleCount-1;s>0;s-=2)for(s===6&&s--;;){for(var c=0;c<2;c++)if(this.modules[i][s-c]===null){var l=!1;o>>a&1)==1),r.getMask(t,i,s-c)&&(l=!l),this.modules[i][s-c]=l,a--,a===-1&&(o++,a=7)}if(i+=n,i<0||this.moduleCount<=i){i-=n,n=-n;break}}}},s.PAD0=236,s.PAD1=17,s.createData=function(e,t,n){for(var i=a.getRSBlocks(e,t),c=new o,l=0;ld*8)throw Error(`code length overflow. (`+c.getLengthInBits()+`>`+d*8+`)`);for(c.getLengthInBits()+4<=d*8&&c.put(0,4);c.getLengthInBits()%8!=0;)c.putBit(!1);for(;!(c.getLengthInBits()>=d*8||(c.put(s.PAD0,8),c.getLengthInBits()>=d*8));)c.put(s.PAD1,8);return s.createBytes(c,i)},s.createBytes=function(e,t){for(var n=0,a=0,o=0,s=Array(t.length),c=Array(t.length),l=0;l=0?m.get(g):0}}for(var _=0,v=0;vString.fromCharCode(e)).join(``),r=new(mr(`index`))(0,mr(`QRErrorCorrectLevel`).M);r.addData(n),r.make();let i=r.getModuleCount();return Array.from({length:i},(e,t)=>Array.from({length:i},(e,n)=>!!r.isDark(t,n)))}function gr(e,t,n){let r=e=>e.replace(/([\\;,:"])/g,`\\$1`);return`WIFI:T:${t===`none`?`nopass`:t===`sae`?`WPA3`:`WPA`};S:${r(e)};P:${r(n)};;`}function _r(e){return`${(e.replace(/[\\/:*?"<>|\x00-\x1f\x7f]/g,`_`).replace(/[. ]+$/g,``).trim()||`Wi-Fi`).slice(0,64)}-QR.png`}function vr(e,t,n=8){let r=(e.length+8)*n;t.fillStyle=`#ffffff`,t.fillRect(0,0,r,r),t.fillStyle=`#000000`,e.forEach((e,r)=>e.forEach((e,i)=>{e&&t.fillRect((i+4)*n,(r+4)*n,n,n)}))}function yr({load:e,onClose:t}){let[n,r]=F(null),[i,a]=F(null),[o,s]=F(null);I(()=>{let t=!0;return e().then(e=>{if(t)try{r({ssid:e.ssid,matrix:hr(gr(e.ssid,e.security,e.password))})}catch{a(`QR 코드를 생성할 수 없습니다.`)}}).catch(()=>{t&&a(`Wi-Fi 정보를 불러오지 못했습니다.`)}),()=>{t=!1,r(null)}},[e]);let c=n?.matrix.length??0;return L(`div`,{class:`fixed inset-0 z-50 flex items-center justify-center bg-slate-950/70 p-4`,role:`presentation`,onClick:e=>{e.target===e.currentTarget&&t()},children:L(`section`,{role:`dialog`,"aria-modal":`true`,"aria-label":`Wi-Fi QR 코드`,class:`w-full max-w-sm rounded-2xl bg-white p-5 text-center text-slate-950 shadow-2xl sm:p-7`,children:[L(`h2`,{class:`m-0 text-xl font-extrabold`,children:`Wi-Fi QR 코드`}),L(`p`,{class:`mt-2 break-all text-sm font-semibold text-slate-600`,children:n?.ssid??`Wi-Fi 정보를 확인하는 중`}),i?L(`p`,{role:`alert`,class:`my-6 text-sm font-bold text-red-700`,children:i}):null,!n&&!i?L(`p`,{class:`my-6 text-sm`,children:`QR 코드를 만드는 중...`}):null,n?L(`div`,{class:`mx-auto my-5 w-full max-w-[288px] rounded-xl border border-slate-200 bg-white p-3`,children:L(`svg`,{class:`block h-auto w-full`,viewBox:`0 0 ${c+8} ${c+8}`,role:`img`,"aria-label":`${n.ssid} Wi-Fi 연결 QR 코드`,"shape-rendering":`crispEdges`,children:[L(`rect`,{width:c+8,height:c+8,fill:`white`}),n.matrix.map((e,t)=>e.map((e,n)=>e?L(`rect`,{x:n+4,y:t+4,width:`1`,height:`1`,fill:`black`},`${t}-${n}`):null))]})}):null,L(`p`,{class:`text-xs leading-5 text-slate-500`,children:[L(`span`,{class:`block`,children:`카메라로 스캔해 Wi-Fi에 연결하세요.`}),L(`span`,{class:`block`,children:`QR 코드에는 비밀번호가 포함됩니다.`})]}),o?L(`p`,{role:`alert`,class:`mt-3 text-sm font-bold text-red-700`,children:o}):null,n?L(`button`,{class:`mt-5 inline-flex min-h-11 w-full items-center justify-center gap-2 rounded-xl bg-teal-700 px-4 font-extrabold text-white transition hover:bg-teal-800 focus:outline-none focus-visible:ring-4 focus-visible:ring-teal-200`,onClick:()=>{if(n){s(null);try{let e=(n.matrix.length+8)*8,t=document.createElement(`canvas`);t.width=e,t.height=e;let r=t.getContext(`2d`);if(!r)throw Error(`Canvas is not supported`);vr(n.matrix,r,8);let i=document.createElement(`a`);i.href=t.toDataURL(`image/png`),i.download=_r(n.ssid),i.click()}catch{s(`QR 코드 이미지를 저장하지 못했습니다. 다시 시도해 주세요.`)}}},type:`button`,children:[L(it,{class:`size-5`}),`QR 코드 다운로드`]}):null,L(`button`,{class:`mt-2 min-h-11 w-full rounded-xl border border-slate-300 bg-white px-4 font-extrabold text-slate-700 transition hover:bg-slate-50`,onClick:t,type:`button`,children:`닫기`})]})})}function br({guest:e,busy:t,saving:n,onUpdate:r,onLoadQr:i}){let[a,o]=F(!1),[s,c]=F(!1),[l,u]=F(e.ssid),[d,f]=F(``),[p,m]=F(e.enabled),[h,g]=F(null);if(I(()=>{u(e.ssid),m(e.enabled),f(``),g(null)},[e]),!e.supported)return L(`section`,{class:`mt-5 rounded-2xl border border-slate-200 bg-white p-5 sm:p-6`,children:[L(`h2`,{class:`m-0 text-lg font-extrabold text-slate-900`,children:`게스트 Wi-Fi`}),L(`p`,{class:`mt-2 mb-0 text-sm text-slate-600`,children:`2.4GHz 기본 Wi-Fi 장치를 찾지 못해 게스트 Wi-Fi를 사용할 수 없습니다.`})]});let _=l.trim()!==e.ssid||p!==e.enabled||d.length>0,y=t||_||!e.enabled||!e.passwordConfigured,b=async t=>{t.preventDefault();let n=l.trim(),i=new TextEncoder().encode(n).length;i<1||i>32?g(`Wi-Fi 이름은 UTF-8 기준 1~32바이트로 입력해 주세요.`):d&&!(d.length>=8&&d.length<=63||/^[0-9a-fA-F]{64}$/.test(d))?g(`비밀번호는 8~63자 또는 64자리 16진수여야 합니다.`):p&&!e.passwordConfigured&&!d?g(`게스트 전용 비밀번호를 입력해 주세요.`):window.confirm(`게스트 Wi-Fi 설정을 적용하면 무선 연결이 잠시 끊어질 수 있습니다. 계속하시겠습니까?`)&&(g(null),await r({ssid:n,enabled:p,password:d})&&f(``))};return L(v,{children:[L(`form`,{"aria-label":`게스트 Wi-Fi 설정`,class:`mt-5 rounded-2xl border border-slate-200 bg-white p-4 shadow-sm shadow-slate-900/5 sm:p-6`,onSubmit:e=>void b(e),children:[L(`div`,{class:`flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between`,children:[L(`button`,{"aria-controls":`ssh-guest-settings`,"aria-expanded":a,"aria-label":`게스트 Wi-Fi 설정 ${a?`접기`:`펼치기`}`,class:`flex min-w-0 flex-1 items-start gap-3 rounded-xl text-left focus:outline-none focus-visible:ring-4 focus-visible:ring-teal-100`,onClick:()=>o(e=>!e),type:`button`,children:[L(`span`,{class:`grid size-11 shrink-0 place-items-center rounded-xl bg-teal-50 text-teal-700`,children:L(We,{class:`size-6`})}),L(`span`,{class:`min-w-0 flex-1`,children:[L(`span`,{class:`block text-xs font-extrabold uppercase tracking-[0.16em] text-slate-500`,children:`2.4 GHz · 분리 네트워크`}),L(`span`,{class:`mt-2 block text-xl font-extrabold tracking-tight text-slate-950`,children:`게스트 Wi-Fi`}),L(`span`,{class:`mt-2 block break-all text-sm text-slate-600`,children:e.ssid})]}),L(_t,{class:`mt-3 size-5 shrink-0 text-slate-500 transition-transform ${a?`rotate-180`:``}`})]}),L(`div`,{class:`flex flex-wrap items-center gap-2 sm:justify-end`,children:[_&&L(`span`,{"aria-live":`polite`,class:`inline-flex items-center gap-1 rounded-full border border-amber-200 bg-amber-50 px-2.5 py-1 text-xs font-extrabold text-amber-800`,children:[L(B,{class:`size-3.5`}),`저장되지 않음`]}),L(`button`,{"aria-label":`게스트 Wi-Fi QR 코드 보기`,class:`inline-flex min-h-10 items-center gap-2 rounded-xl border border-slate-300 bg-white px-3.5 py-2 text-sm font-extrabold text-slate-800 transition hover:border-teal-300 hover:text-teal-700 focus:outline-none focus-visible:ring-4 focus-visible:ring-teal-100 disabled:cursor-not-allowed disabled:opacity-50`,disabled:y,onClick:()=>c(!0),title:e.enabled?void 0:`게스트 Wi-Fi를 켜고 저장하면 QR 코드를 사용할 수 있습니다.`,type:`button`,children:[L(Ze,{class:`size-4 shrink-0`}),`QR 코드 보기`]}),L(`span`,{class:`rounded-full px-3 py-1.5 text-xs font-extrabold ${e.enabled?`bg-emerald-50 text-emerald-700`:`bg-slate-100 text-slate-600`}`,children:e.enabled?`사용 중`:`꺼짐`})]})]}),L(`div`,{class:a?``:`hidden`,id:`ssh-guest-settings`,children:[L(`p`,{class:`mt-5 mb-0 text-sm leading-6 text-slate-600`,children:`방문자는 인터넷만 이용하며 내부 네트워크와 공유기 관리 화면에는 접근할 수 없습니다.`}),L(`div`,{class:`mt-5 grid gap-5 lg:grid-cols-2`,children:[L(`label`,{class:`block`,children:[L(`span`,{class:`text-sm font-extrabold text-slate-800`,children:`게스트 Wi-Fi 이름 (SSID)`}),L(`input`,{autocomplete:`off`,class:`mt-2 min-h-11 w-full rounded-xl border-2 border-slate-300 bg-slate-50 px-4 py-2.5 text-sm font-semibold text-slate-950 outline-none focus:border-teal-500 focus:bg-white focus:ring-4 focus:ring-teal-100 disabled:opacity-60`,disabled:t,maxLength:32,onInput:e=>u(e.currentTarget.value),value:l})]}),L(`label`,{class:`block`,children:[L(`span`,{class:`text-sm font-extrabold text-slate-800`,children:`게스트 Wi-Fi 비밀번호`}),L(`input`,{autocomplete:`new-password`,class:`mt-2 min-h-11 w-full rounded-xl border-2 border-slate-300 bg-slate-50 px-4 py-2.5 text-sm font-semibold text-slate-950 outline-none focus:border-teal-500 focus:bg-white focus:ring-4 focus:ring-teal-100 disabled:opacity-60`,disabled:t,onInput:e=>f(e.currentTarget.value),placeholder:e.passwordConfigured?`비워 두면 기존 비밀번호 유지`:`8자 이상의 별도 비밀번호 입력`,type:`password`,value:d}),L(`span`,{class:`mt-2 block text-xs text-slate-500`,children:`WPA2/WPA3 혼합 · 기본 Wi-Fi와 다른 비밀번호를 권장합니다.`})]})]}),L(`div`,{class:`mt-5 rounded-xl bg-slate-50 px-4 py-3 text-sm leading-6 text-slate-700`,children:[`내부망 접근 차단 · 게스트 기기 간 통신 차단 · SafeShield DNS 사용`,e.subnet&&L(`span`,{class:`block text-xs text-slate-500`,children:[`게스트 게이트웨이: `,e.subnet]})]}),L(`div`,{class:`mt-5 flex flex-col gap-4 border-t border-slate-200 pt-5 sm:flex-row sm:items-center sm:justify-between`,children:[L(`label`,{class:`inline-flex min-h-11 items-center gap-3 text-sm font-extrabold text-slate-800`,children:[L(`input`,{checked:p,class:`size-5 accent-teal-700`,disabled:t,onChange:e=>m(e.currentTarget.checked),type:`checkbox`}),`게스트 Wi-Fi 사용`]}),L(`button`,{class:`inline-flex min-h-11 w-full items-center justify-center gap-2 rounded-xl bg-teal-700 px-5 py-2.5 text-sm font-extrabold text-white transition hover:bg-teal-800 disabled:opacity-60 sm:w-auto`,disabled:t||!_,type:`submit`,children:[L(ut,{class:`size-5 ${n?`animate-pulse`:``}`}),n?`적용 중`:`설정 저장`]})]}),h&&L(`p`,{class:`mt-4 mb-0 rounded-xl bg-red-50 px-4 py-3 text-sm font-bold text-red-800`,children:h})]})]}),s&&L(yr,{load:i,onClose:()=>c(!1)})]})}var xr=[{value:`sae-mixed`,label:`WPA2/WPA3 혼합 (권장)`},{value:`sae`,label:`WPA3-Personal`},{value:`psk2`,label:`WPA2-Personal`},{value:`none`,label:`암호화 없음`}];function Sr(e){switch(e.security){case`sae-mixed`:return`WPA2/WPA3 혼합`;case`sae`:return`WPA3-Personal`;case`psk2`:return`WPA2-Personal`;case`none`:return`암호화 없음`;default:return`고급 설정 (${e.securityRaw})`}}function Cr(e){return e.length>=8&&e.length<=63||/^[0-9a-fA-F]{64}$/.test(e)}function wr({network:e,busy:t,saving:n,onUpdate:r,onLoadQr:i}){let[a,o]=F(e.ssid),[s,c]=F(e.enabled),[l,u]=F(e.security===`custom`?`keep`:e.security),[d,f]=F(``),[p,m]=F(!1),h=Ee(()=>i(e.section),[i,e.section]),[g,_]=F(null);I(()=>{o(e.ssid),c(e.enabled),u(e.security===`custom`?`keep`:e.security),f(``),_(null)},[e]);let v=e.security===`custom`&&l===`keep`,y=l===`psk2`||l===`sae-mixed`||l===`sae`,b=e.security===`custom`?`keep`:e.security,x=a.trim()!==e.ssid||s!==e.enabled||l!==b||d.length>0,S=t||x||e.security===`custom`||!e.enabled||e.security!==`none`&&!e.passwordConfigured,C=[...e.security===`custom`?[{value:`keep`,label:`현재 고급 설정 유지 (${e.securityRaw})`}]:[],...xr],w=async t=>{t.preventDefault();let n=a.trim(),i=new TextEncoder().encode(n).length;i<1||i>32?_(`SSID는 UTF-8 기준 1~32바이트로 입력해 주세요.`):d&&!Cr(d)?_(`비밀번호는 8~63자 또는 64자리 16진수여야 합니다.`):y&&!e.passwordConfigured&&d.length===0?_(`보안 Wi-Fi를 사용하려면 비밀번호를 입력해 주세요.`):window.confirm(`Wi-Fi 설정을 적용하면 현재 무선 연결이 잠시 끊어질 수 있습니다. 계속하시겠습니까?`)&&(_(null),await r({section:e.section,ssid:n,security:l,password:d,enabled:s})&&f(``))};return L(`form`,{class:`rounded-2xl border border-slate-200 bg-white p-4 shadow-sm shadow-slate-900/5 sm:p-6`,onSubmit:e=>void w(e),children:[L(`div`,{class:`flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between`,children:[L(`div`,{class:`flex min-w-0 items-start gap-3`,children:[L(`span`,{class:`grid size-11 shrink-0 place-items-center rounded-xl bg-teal-50 text-teal-700`,children:L(We,{class:`size-6`})}),L(`div`,{class:`min-w-0`,children:[L(`p`,{class:`m-0 text-xs font-extrabold uppercase tracking-[0.16em] text-slate-500`,children:e.bandLabel}),L(`h2`,{class:`mt-2 mb-0 break-words text-xl font-extrabold tracking-tight text-slate-950`,children:e.ssid||`이름 없는 Wi-Fi`}),L(`p`,{class:`mt-2 mb-0 text-sm text-slate-600`,children:[`채널 `,e.channel??`자동`,` · 연결 기기 `,e.clientCount,`대`]})]})]}),L(`div`,{class:`flex flex-wrap items-center gap-2 sm:justify-end`,children:[x?L(`span`,{"aria-live":`polite`,class:`inline-flex shrink-0 items-center gap-1.5 rounded-full border border-amber-200 bg-amber-50 px-2.5 py-1 text-xs font-extrabold text-amber-800`,role:`status`,children:[L(B,{"aria-hidden":`true`,class:`size-3.5 shrink-0`}),`저장되지 않음`]}):null,L(`button`,{"aria-label":`${e.bandLabel} Wi-Fi QR 코드 보기`,class:`inline-flex min-h-10 items-center gap-2 rounded-xl border border-slate-300 bg-white px-3.5 py-2 text-sm font-extrabold text-slate-800 shadow-sm transition hover:border-teal-300 hover:text-teal-700 focus:outline-none focus-visible:ring-4 focus-visible:ring-teal-100 disabled:cursor-not-allowed disabled:opacity-50`,disabled:S,onClick:()=>m(!0),type:`button`,children:[L(Ze,{class:`size-4 shrink-0`}),`QR 코드 보기`]}),L(`span`,{class:`inline-flex w-fit items-center gap-2 rounded-full px-3 py-1.5 text-xs font-extrabold ring-1 ring-inset ${e.runtimeUp?`bg-emerald-50 text-emerald-700 ring-emerald-200`:`bg-slate-100 text-slate-600 ring-slate-200`}`,children:[L(`span`,{class:`size-2 rounded-full ${e.runtimeUp?`bg-emerald-500`:`bg-slate-400`}`}),e.runtimeUp?`동작 중`:e.enabled?`시작 대기`:`꺼짐`]})]})]}),L(`div`,{class:`mt-6 grid gap-5 lg:grid-cols-2`,children:[L(`label`,{class:`block`,children:[L(`span`,{class:`text-sm font-extrabold text-slate-800`,children:`Wi-Fi 이름 (SSID)`}),L(`input`,{autocomplete:`off`,class:`mt-2 min-h-11 w-full rounded-xl border-2 border-slate-300 bg-slate-50 px-4 py-2.5 text-sm font-semibold text-slate-950 shadow-inner outline-none transition placeholder:text-slate-400 focus:border-teal-500 focus:bg-white focus:ring-4 focus:ring-teal-100 disabled:cursor-not-allowed disabled:bg-slate-100`,disabled:t,maxLength:32,onInput:e=>o(e.currentTarget.value),value:a})]}),L(`div`,{class:`block`,children:[L(`span`,{class:`text-sm font-extrabold text-slate-800`,children:`보안 방식`}),L(Pn,{ariaLabel:`${e.bandLabel} Wi-Fi 보안 방식`,className:`mt-2`,disabled:t,id:`wifi-security-${e.section}`,onChange:e=>{let t=e;u(t),(t===`none`||t===`keep`)&&f(``)},options:C,value:l,variant:`emphasized`}),L(`span`,{class:`mt-2 block text-xs text-slate-500`,children:[`현재: `,Sr(e)]})]}),L(`label`,{class:`block lg:col-span-2`,children:[L(`span`,{class:`text-sm font-extrabold text-slate-800`,children:`새 비밀번호`}),L(`input`,{autocomplete:`new-password`,class:`mt-2 min-h-11 w-full rounded-xl border-2 border-slate-300 bg-slate-50 px-4 py-2.5 text-sm font-semibold text-slate-950 shadow-inner outline-none transition placeholder:text-slate-400 focus:border-teal-500 focus:bg-white focus:ring-4 focus:ring-teal-100 disabled:cursor-not-allowed disabled:bg-slate-100`,disabled:t||l===`none`||v,onInput:e=>f(e.currentTarget.value),placeholder:v?`고급 보안 설정은 기존 LuCI에서 변경합니다`:e.passwordConfigured?`비워 두면 기존 비밀번호를 유지합니다`:`8자 이상의 비밀번호를 입력하세요`,type:`password`,value:d}),L(`span`,{class:`mt-2 block text-xs text-slate-500`,children:`저장된 비밀번호는 일반 설정 조회에 노출되지 않습니다. QR 코드에는 비밀번호가 포함됩니다.`})]})]}),L(`div`,{class:`mt-6 flex flex-col gap-4 border-t border-slate-200 pt-5 sm:flex-row sm:items-center sm:justify-between`,children:[L(`label`,{class:`inline-flex min-h-11 items-center gap-3 text-sm font-extrabold text-slate-800`,children:[L(`input`,{checked:s,class:`size-5 accent-teal-700`,disabled:t,onChange:e=>c(e.currentTarget.checked),type:`checkbox`}),`이 Wi-Fi 사용`]}),L(`button`,{class:`inline-flex min-h-11 w-full items-center justify-center gap-2 rounded-xl border-0 bg-teal-700 px-5 py-2.5 text-sm font-extrabold text-white shadow-sm transition hover:bg-teal-800 focus:outline-none focus-visible:ring-4 focus-visible:ring-teal-200 disabled:cursor-wait disabled:opacity-60 sm:w-auto`,disabled:t||!x,type:`submit`,children:[L(ut,{class:`size-5 ${n?`animate-pulse`:``}`}),n?`적용 중`:`설정 저장`]})]}),p?L(yr,{load:h,onClose:()=>m(!1)}):null,g?L(`p`,{class:`mt-4 mb-0 rounded-xl bg-red-50 px-4 py-3 text-sm font-bold text-red-800`,children:g}):null]})}function Tr({feedback:e,onDismiss:t}){return L(`div`,{"aria-live":`polite`,class:`mb-5 flex flex-col gap-3 rounded-2xl border px-4 py-3 text-sm sm:flex-row sm:items-center sm:justify-between ${e.kind===`success`?`border-emerald-200 bg-emerald-50 text-emerald-900`:e.kind===`warning`?`border-amber-200 bg-amber-50 text-amber-900`:`border-red-200 bg-red-50 text-red-900`}`,children:[L(`p`,{class:`m-0 font-bold leading-6`,children:e.message}),L(`button`,{class:`min-h-11 w-full shrink-0 rounded-lg border border-current bg-transparent sm:min-h-9 sm:w-auto px-3 py-1.5 text-xs font-extrabold`,onClick:t,type:`button`,children:`닫기`})]})}function Er({data:e,error:t,feedback:n,loading:r,updatingSection:i,onDismissFeedback:a,onRetry:o,onUpdate:s,onUpdateGuest:c,onLoadQr:l,onLoadGuestQr:u}){return r?L(zn,{}):t?L(Bn,{message:t,onRetry:o}):e?L(v,{children:[n?L(Tr,{feedback:n,onDismiss:a}):null,L(`section`,{class:`mb-5 rounded-2xl border border-amber-200 bg-amber-50 p-5 text-amber-950 sm:p-6`,children:[L(`p`,{class:`m-0 text-xs font-extrabold uppercase tracking-[0.16em] text-amber-700`,children:`연결 주의`}),L(`h2`,{class:`mt-2 mb-0 text-lg font-extrabold`,children:`Wi-Fi 이름이나 비밀번호를 바꾸면 현재 연결이 끊어집니다`}),L(`p`,{class:`mt-2 mb-0 text-sm leading-6 text-amber-900/80`,children:`변경한 Wi-Fi 이름과 비밀번호로 다시 연결하면 SmartSafeHub를 계속 사용할 수 있습니다. VLAN 등 고급 무선 옵션은 기존 LuCI에서 관리합니다.`})]}),L(V,{eyebrow:`Wireless`,title:`기본 Wi-Fi`,description:`무선 네트워크의 이름, 비밀번호, 보안 방식과 QR 코드를 관리합니다.`}),e.networks.length?L(`section`,{"aria-label":`기본 Wi-Fi 설정`,class:`grid gap-5 xl:grid-cols-2`,children:e.networks.map(e=>L(wr,{busy:i!==null,network:e,onUpdate:s,onLoadQr:l,saving:i===e.section},e.section))}):L(`section`,{class:`rounded-2xl border border-slate-200 bg-white p-5 text-center shadow-sm shadow-slate-900/5 sm:p-8`,children:[L(We,{class:`mx-auto size-10 text-slate-400`}),L(`h2`,{class:`mt-4 mb-0 text-lg font-extrabold text-slate-950`,children:`관리할 기본 Wi-Fi를 찾지 못했습니다`}),L(`p`,{class:`mt-2 mb-0 text-sm leading-6 text-slate-600`,children:`기존 LuCI에서 AP 모드의 무선 네트워크를 먼저 구성해 주세요.`})]}),e.guest&&L(v,{children:[L(`div`,{role:`separator`,"aria-label":`기본 Wi-Fi와 게스트 Wi-Fi 구분`,class:`mt-5 w-full border-t border-slate-200`}),L(`div`,{class:`mt-5`,children:L(V,{eyebrow:`Guest network`,title:`방문자용 네트워크`,description:`기본 네트워크와 분리된 게스트 Wi-Fi를 관리합니다.`})}),L(br,{guest:e.guest,busy:i!==null,saving:i===`ssh_guest`,onUpdate:c,onLoadQr:u})]}),e.guestError&&L(`section`,{class:`mt-5 rounded-2xl border border-amber-200 bg-amber-50 p-5 text-sm text-amber-900`,role:`status`,children:[`게스트 Wi-Fi 정보를 불러오지 못했습니다. 패키지를 설치한 직후라면 로그아웃 후 다시 로그인해 주세요.`,L(`button`,{class:`ml-3 underline font-bold`,onClick:o,type:`button`,children:`다시 시도`})]})]}):null}function Dr({timestamp:e,fallback:t=`기록 없음`}){let[n,r]=F(()=>Date.now()/1e3),i=typeof e==`number`&&Number.isFinite(e)&&e>0&&e<=864e10;if(I(()=>{if(!i)return;r(Date.now()/1e3);let e=window.setInterval(()=>r(Date.now()/1e3),6e4);return()=>window.clearInterval(e)},[e,i]),!i||e==null)return L(`span`,{children:t});let a=Wt(e);return L(`span`,{class:`inline-flex max-w-full flex-wrap items-center gap-1`,children:[L(`time`,{class:`min-w-0`,dateTime:new Date(e*1e3).toISOString(),title:a,children:Kt(e,n)}),L(`details`,{class:`group inline-flex max-w-full flex-wrap items-center gap-1 align-middle`,children:[L(`summary`,{"aria-label":`정확한 날짜 및 시간 확인`,class:`inline-flex size-6 cursor-pointer list-none items-center justify-center rounded-full text-slate-400 transition hover:text-teal-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-teal-500 [&::-webkit-details-marker]:hidden`,title:`정확한 날짜 및 시간 확인`,children:L(`svg`,{"aria-hidden":`true`,class:`size-3.5`,fill:`none`,stroke:`currentColor`,strokeWidth:`1.8`,viewBox:`0 0 24 24`,children:[L(`circle`,{cx:`12`,cy:`12`,r:`9`}),L(`path`,{d:`M12 11v5m0-8h.01`,strokeLinecap:`round`})]})}),L(`span`,{class:`hidden max-w-full break-words rounded-md bg-slate-100 px-2 py-1 text-xs font-semibold text-slate-700 group-open:inline-block`,children:a})]})]})}function Or(e){return e.trim().toLowerCase().replace(/\.+$/,``)}function kr(e){return e.length===0||e.length>253||!e.includes(`.`)||!/^[a-z0-9.-]+$/.test(e)?!1:e.split(`.`).every(e=>e.length>0&&e.length<=63&&/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(e))}function Ar({action:e,kind:t,onAddRule:n,onDeleteRule:r,rules:i,recommendedDomains:a,recommendedKnown:o,recommendedRules:s,onRequestRevoke:c,revokeBusy:l}){let[u,d]=F(``),[f,p]=F(``),[m,h]=F(null),[g,_]=F(!0),[v,y]=F(null),b=t===`allow`,x=e!==null,S=Te(()=>{let e=f.trim().toLowerCase();return e?i.filter(t=>t.includes(e)):i},[i,f]),C=f.trim().length>0,w=b&&o?S.filter(e=>a.includes(e)):[],T=b&&o?S.filter(e=>!a.includes(e)):S,E=b?s.filter(e=>!C||e.title.toLowerCase().includes(f.trim().toLowerCase())||e.domains.some(e=>e.includes(f.trim().toLowerCase()))):[],D=C||(v??(s.length||w.length)<=3),O=C||g;async function k(e){e.preventDefault();let r=Or(u);kr(r)?(h(null),await n(t,r,()=>{d(e=>Or(e)===r?``:e)})):h(`example.com 형식의 도메인을 입력해 주세요. URL, IP 주소, 와일드카드는 사용할 수 없습니다.`)}let A=b?{badge:`bg-emerald-100 text-emerald-800`,button:`border-emerald-700 bg-emerald-700 text-white hover:border-emerald-800 hover:bg-emerald-800 focus-visible:ring-emerald-100`,icon:`bg-emerald-50 text-emerald-700`,label:`허용 목록`,description:`직접 허용하거나 웹사이트 추천으로 추가된 도메인을 관리합니다.`,empty:`등록된 허용 도메인이 없습니다.`}:{badge:`bg-red-100 text-red-800`,button:`border-red-700 bg-red-700 text-white hover:border-red-800 hover:bg-red-800 focus-visible:ring-red-100`,icon:`bg-red-50 text-red-700`,label:`차단 목록`,description:`SafeShield 기본 목록에 없더라도 직접 차단할 도메인을 등록합니다.`,empty:`등록된 차단 도메인이 없습니다.`};function j(n){let i=b&&o&&a.includes(n),s=!b||o?i?`추천 규칙`:`직접 추가`:null,c=e?.operation===`delete`&&e.action===t&&e.domain===n;return L(`li`,{class:`flex min-w-0 items-center justify-between gap-3 px-3.5 py-3`,children:[L(`div`,{class:`flex min-w-0 flex-1 flex-wrap items-center gap-2`,children:L(`code`,{class:`min-w-0 break-all font-mono text-sm font-semibold text-slate-800`,children:n})}),i?L(`span`,{class:`shrink-0 rounded-full bg-teal-50 px-2.5 py-1 text-[11px] font-bold text-teal-800`,title:`추천 규칙의 적용과 해제는 웹사이트에서 관리합니다.`,children:`웹사이트 관리`}):L(`button`,{"aria-label":`${n} ${A.label}에서 삭제`,class:`inline-flex size-9 shrink-0 items-center justify-center rounded-lg border border-slate-200 bg-white text-slate-500 transition hover:border-red-200 hover:bg-red-50 hover:text-red-700 focus:outline-none focus-visible:ring-4 focus-visible:ring-red-100 disabled:cursor-not-allowed disabled:opacity-50`,disabled:x||i,onClick:()=>void r(t,n),title:s===`직접 추가`?`직접 추가한 규칙 삭제`:`삭제`,type:`button`,children:L(ft,{class:`size-4 ${c?`animate-pulse`:``}`})})]},n)}return L(`article`,{class:`min-w-0 overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm shadow-slate-900/5`,children:[L(`div`,{class:`border-b border-slate-200 p-4 sm:p-6`,children:[L(`div`,{class:`flex min-w-0 items-start gap-3`,children:[L(`span`,{class:`grid size-11 shrink-0 place-items-center rounded-xl ${A.icon}`,children:L(b?ut:Ue,{class:`size-6`})}),L(`div`,{class:`min-w-0`,children:[L(`div`,{class:`flex flex-wrap items-center gap-2`,children:[L(`h2`,{class:`m-0 text-xl font-extrabold tracking-tight text-slate-950`,children:A.label}),L(`span`,{class:`rounded-full px-2.5 py-1 text-xs font-extrabold ${A.badge}`,children:i.length})]}),L(`p`,{class:`mt-2 mb-0 text-sm leading-6 text-slate-600`,children:A.description})]})]}),L(`form`,{class:`mt-5`,onSubmit:k,children:[L(`label`,{class:`block text-xs font-extrabold text-slate-600`,for:`${t}-domain`,children:`새 도메인`}),L(`div`,{class:`mt-2 flex flex-col gap-2 sm:flex-row`,children:[L(`input`,{"aria-describedby":m?`${t}-domain-error`:void 0,"aria-invalid":m?!0:void 0,autocomplete:`off`,class:`min-h-11 min-w-0 flex-1 rounded-xl border-2 border-slate-300 bg-slate-50 px-4 py-2.5 text-sm font-semibold text-slate-950 shadow-inner outline-none transition placeholder:text-slate-400 focus:border-teal-500 focus:bg-white focus:ring-4 focus:ring-teal-100 disabled:cursor-not-allowed disabled:bg-slate-100`,disabled:x,id:`${t}-domain`,onInput:e=>d(e.currentTarget.value),placeholder:`example.com`,spellcheck:!1,type:`text`,value:u}),L(`button`,{class:`inline-flex min-h-11 w-full shrink-0 items-center justify-center gap-2 rounded-xl border px-4 py-2.5 text-sm font-extrabold transition focus:outline-none focus-visible:ring-4 disabled:cursor-not-allowed disabled:opacity-50 sm:w-auto ${A.button}`,disabled:x||u.trim().length===0,type:`submit`,children:[L(dt,{class:`size-4`}),e?.operation===`add`&&e.action===t?`추가 중…`:`추가`]})]}),m?L(`p`,{class:`mt-2 mb-0 text-xs font-semibold leading-5 text-red-700`,id:`${t}-domain-error`,children:m}):null]})]}),L(`div`,{class:`p-4 sm:p-6`,children:[L(`label`,{class:`relative block`,for:`${t}-search`,children:[L(`span`,{class:`sr-only`,children:[A.label,` 검색`]}),L(`span`,{class:`pointer-events-none absolute inset-y-0 left-0 flex w-11 items-center justify-center text-slate-400`,children:L(pt,{class:`size-4`})}),L(`input`,{class:`min-h-11 w-full rounded-xl border-2 border-slate-300 bg-slate-50 py-2.5 pr-4 pl-11 text-sm font-semibold text-slate-950 shadow-inner outline-none transition placeholder:text-slate-400 focus:border-teal-500 focus:bg-white focus:ring-4 focus:ring-teal-100`,id:`${t}-search`,onInput:e=>p(e.currentTarget.value),placeholder:`${A.label} 검색`,type:`search`,value:f})]}),b&&o?L(`div`,{class:`mt-4 overflow-hidden rounded-xl border border-slate-200`,children:[L(`section`,{"aria-label":`직접 추가한 허용 규칙`,children:[L(`button`,{"aria-controls":`allow-manual-domains`,"aria-expanded":O,class:`flex min-h-14 w-full items-center justify-between gap-3 ssh-rule-group-toggle bg-slate-50 px-4 py-3 text-left focus-visible:outline-2 focus-visible:outline-teal-500`,onClick:()=>_(!O),type:`button`,children:[L(`span`,{class:`min-w-0 text-sm font-extrabold text-slate-800`,children:[`직접 추가한 규칙 `,L(`span`,{class:`ml-1 rounded-full bg-white px-2 py-0.5 text-xs font-bold text-slate-600`,children:[T.length,C?`개 검색됨`:`개`]})]}),L(`span`,{"aria-hidden":`true`,class:`shrink-0 text-sm font-bold text-slate-500`,children:O?`접기 −`:`펼치기 +`})]}),L(`div`,{hidden:!O,id:`allow-manual-domains`,children:T.length?L(`ul`,{class:`m-0 max-h-[20rem] list-none divide-y divide-slate-200 overflow-y-auto p-0`,children:T.map(j)}):L(`p`,{class:`m-0 px-4 py-5 text-sm text-slate-500`,children:C?`검색 결과가 없습니다.`:`직접 추가한 허용 도메인이 없습니다.`})})]}),L(`section`,{"aria-label":`웹사이트 추천 규칙`,class:`border-t border-slate-200`,children:[L(`button`,{"aria-controls":`allow-recommended-domains`,"aria-expanded":D,class:`flex min-h-14 w-full items-center justify-between gap-3 ssh-rule-group-toggle bg-slate-50 px-4 py-3 text-left focus-visible:outline-2 focus-visible:outline-teal-500`,onClick:()=>y(!D),type:`button`,children:[L(`span`,{class:`min-w-0 text-sm font-extrabold text-slate-800`,children:[`추천 규칙 `,L(`span`,{class:`ml-1 rounded-full bg-white px-2 py-0.5 text-xs font-bold text-teal-800`,children:[!C&&s.length?s.length:w.length,C?`개 검색됨`:`개`]})]}),L(`span`,{"aria-hidden":`true`,class:`shrink-0 text-sm font-bold text-slate-500`,children:D?`접기 −`:`펼치기 +`})]}),L(`div`,{hidden:!D,id:`allow-recommended-domains`,children:[E.length?L(`ul`,{class:`m-0 max-h-[22rem] list-none divide-y divide-slate-200 overflow-y-auto p-0`,children:E.map(e=>L(`li`,{class:`flex min-w-0 flex-wrap items-center justify-between gap-3 px-3.5 py-3`,children:[L(`div`,{class:`min-w-0 flex-1`,children:[L(`strong`,{class:`block break-words text-sm text-slate-900`,children:e.title}),L(`div`,{class:`mt-1 flex flex-wrap gap-1.5`,children:e.domains.map(e=>L(`code`,{class:`break-all text-xs text-slate-600`,children:e},e))})]}),c?L(`button`,{class:`shrink-0 rounded-lg border border-red-200 px-3 py-2 text-xs font-bold text-red-700 hover:bg-red-50 disabled:opacity-50`,disabled:l||x,onClick:()=>c(e),type:`button`,children:`적용 해제`}):L(`span`,{class:`text-xs text-slate-500`,children:`웹사이트 관리`})]},e.id))}):w.length?L(`ul`,{class:`m-0 max-h-[20rem] list-none divide-y divide-slate-200 overflow-y-auto p-0`,children:w.map(j)}):L(`p`,{class:`m-0 px-4 py-5 text-sm text-slate-500`,children:C?`검색 결과가 없습니다.`:`추천으로 추가된 허용 도메인이 없습니다.`}),C?null:L(`p`,{class:`m-0 border-t border-slate-100 px-4 py-3 text-xs leading-5 text-slate-500`,children:`추천 규칙은 이 공유기에서 적용 해제할 수 있습니다. 새 규칙의 적용은 SmartSafeHub 웹사이트에서 관리해 주세요.`})]})]})]}):L(`div`,{class:`mt-4 max-h-[28rem] overflow-y-auto rounded-xl border border-slate-200`,children:S.length?L(`ul`,{class:`m-0 list-none divide-y divide-slate-200 p-0`,children:S.map(j)}):L(`div`,{class:`px-5 py-10 text-center`,children:[L(mt,{class:`mx-auto size-8 text-slate-300`}),L(`p`,{class:`mt-3 mb-0 text-sm font-bold text-slate-600`,children:C?`검색 결과가 없습니다.`:A.empty})]})})]})]})}function jr({action:e,data:t,error:n,feedback:r,loading:i,onAddRule:a,onDeleteRule:o,onDismissFeedback:s,onRetry:c,onLoadRecommendedRulesStatus:l,onSyncRecommendedRules:u,onRevokeRecommendedRule:d}){let[f,p]=F(null),[m,h]=F(!1),[g,_]=F(!1),[y,b]=F(null),[x,S]=F(!1),[C,w]=F(!1),[T,E]=F(!1),D=we(null),O=we(null),k=we(c);k.current=c;let[A,j]=F(null),[M,ee]=F(null),te=f?.domains??[],ne=f?.rules??[],re=Ee(e=>{p(e),h(!0);let t=JSON.stringify([e.revision,e.domains,e.rules.map(e=>e.id),e.syncStatus]);O.current!==null&&O.current!==t&&k.current(),O.current=t,e.syncStatus===`confirmed`&&D.current===null&&ee(null),D.current!==null&&e.syncStatus===`confirmed`&&!e.rules.some(e=>e.id===D.current)&&(D.current=null,w(!1),E(!1),ee(null),j(`추천 규칙 적용 해제를 확인했습니다.`))},[]);I(()=>{if(!l)return;let e=l,t=!0,n=!1;function r(){n||g||x||(n=!0,e().then(e=>{t&&re(e)}).catch(()=>{}).finally(()=>{n=!1}))}r();let i=window.setInterval(r,15e3),a=()=>r(),o=()=>{document.hidden||r()};return window.addEventListener(`focus`,a),document.addEventListener(`visibilitychange`,o),()=>{t=!1,window.clearInterval(i),window.removeEventListener(`focus`,a),document.removeEventListener(`visibilitychange`,o)}},[t?.allow.join(` +`),l,g,x,re]),I(()=>{if(!C)return;let e=window.setTimeout(()=>E(!0),6e4);return()=>window.clearTimeout(e)},[C]);async function ie(){if(!(!u||g||x)){_(!0),ee(null),j(null);try{let e=await u();re(e),j(e.syncStatus===`confirmed`?`서버에서 규칙을 가져와 SafeShield 허용 목록을 확인했습니다.`:`추천 규칙 상태를 확인 중입니다. 진행 결과는 자동으로 갱신됩니다.`),c()}catch{ee(`추천 규칙을 동기화하지 못했습니다. 서버 연결 및 기기 등록 상태를 확인해 주세요.`)}finally{_(!1)}}}async function ae(){if(!y||x||!d)return;let e=y.id;D.current=e,S(!0),w(!0),E(!1),ee(null),j(null);try{let t=await d(e);re(t),b(null),D.current!==null&&j(`적용 해제 요청을 처리했습니다. 공유기의 허용 목록을 확인하고 있습니다.`),k.current()}catch{b(null),ee(`적용 해제 결과를 아직 확인하지 못했습니다. 공유기의 상태를 자동으로 다시 확인합니다.`);try{let e=await l?.();e&&re(e)}catch{}}finally{S(!1)}}let oe=f?.syncStage,se=f?.syncStatus===`pending`,ce=f?.syncStatus===`delayed`,le=f?.syncStatus===`failed`,N=oe===`server_sync`?`서버 동기화 중`:oe===`safeshield_refreshing`?`SafeShield 갱신 중`:oe===`applying`?`추천 규칙 적용 중`:oe===`verifying`?`허용 목록 확인 중`:ce?`재확인 지연`:le?`동기화 실패`:se?`동기화 대기`:`허용 목록 확인`,ue=oe===`safeshield_refreshing`&&se?`SafeShield가 보호 목록을 갱신하고 있습니다. 갱신이 끝나면 추천 규칙을 자동으로 다시 확인합니다.`:oe===`applying`&&se?`추천 규칙을 SafeShield 허용 목록에 반영하고 있습니다.`:oe===`verifying`&&se?`SafeShield 허용 목록에서 추천 규칙 반영 결과를 확인하고 있습니다.`:oe===`server_sync`&&se?`SmartSafeHub 서버에서 최신 추천 규칙을 확인하고 있습니다.`:ce?`서버 응답이 지연되고 있습니다. 이미 확인한 허용 규칙은 유지되며 자동으로 다시 시도합니다.`:le?f?.syncReason===`auth_rejected`?`SmartSafeHub 기기 인증이 거부되었습니다. 공유기 계정 연결 상태를 확인해 주세요.`:f?.syncReason===`api_rejected`?`서버가 추천 규칙 요청을 거부했습니다. Hub API 배포 상태를 확인해 주세요.`:f?.syncReason===`invalid_response`?`서버 응답을 확인할 수 없습니다. 규칙은 유지되며 다음 동기화에서 다시 확인합니다.`:`추천 규칙 반영을 완료하지 못했습니다. 다음 자동 동기화에서 다시 시도합니다.`:se?`추천 규칙의 적용 결과를 기다리고 있습니다.`:null,de=C&&D.current!==null?{tone:`amber`,label:T?`적용 해제 처리 지연`:`적용 해제 확인 중`,description:T?`적용 해제 확인이 지연되고 있습니다. SafeShield 갱신 중이라면 완료 후 자동으로 재확인합니다.`:ue??`적용 해제 상태를 확인하고 있습니다. 확인되면 목록과 상태가 자동 갱신됩니다.`}:se||ce||le?{tone:le?`red`:`amber`,label:N,description:ue??``}:M?{tone:`red`,label:`상태 확인 필요`,description:M}:f?.syncWarning?{tone:`amber`,label:`서버 재확인 지연`,description:`이미 확인한 허용 규칙은 유지되며 자동으로 다시 시도합니다.`}:A?{tone:`green`,label:`허용 목록 확인`,description:A}:null;return i&&!t?L(zn,{}):n&&!t?L(Bn,{message:n,onRetry:c}):t?L(v,{children:[y?L(`div`,{class:`fixed inset-0 z-50 flex items-center justify-center bg-slate-950/70 p-4`,role:`presentation`,onClick:()=>{x||b(null)},children:L(`section`,{"aria-labelledby":`recommended-revoke-title`,"aria-modal":`true`,class:`w-full max-w-md rounded-2xl border border-slate-200 bg-white p-5 shadow-xl`,onClick:e=>e.stopPropagation(),role:`dialog`,children:[L(`h2`,{class:`m-0 text-lg font-extrabold text-slate-950`,id:`recommended-revoke-title`,children:`추천 규칙 적용 해제`}),L(`p`,{class:`mt-3 text-sm text-slate-700`,children:[L(`strong`,{children:y.title}),` 규칙을 이 공유기에서 해제할까요?`]}),L(`p`,{class:`mt-2 text-xs leading-5 text-slate-600`,children:[`포함된 도메인 `,y.domains.length,`개를 다시 확인합니다. 직접 추가한 허용 규칙과 다른 추천 규칙에 필요한 도메인은 유지됩니다.`]}),L(`div`,{class:`mt-5 flex justify-end gap-2`,children:[L(`button`,{class:`rounded-lg border border-slate-200 px-4 py-2 text-sm font-bold text-slate-700 disabled:cursor-wait disabled:opacity-50`,disabled:x,onClick:()=>b(null),type:`button`,children:`취소`}),L(`button`,{class:`inline-flex min-w-24 items-center justify-center gap-2 rounded-lg bg-red-700 px-4 py-2 text-sm font-bold text-white transition hover:bg-red-800 disabled:cursor-progress disabled:bg-red-700 disabled:opacity-80`,"aria-busy":x,disabled:x,onClick:()=>void ae(),type:`button`,children:[x?L(`svg`,{class:`size-4 animate-spin`,"aria-hidden":`true`,viewBox:`0 0 24 24`,fill:`none`,children:[L(`circle`,{cx:`12`,cy:`12`,r:`9`,stroke:`currentColor`,"stroke-width":`3`,opacity:`0.25`}),L(`path`,{d:`M21 12a9 9 0 0 0-9-9`,stroke:`currentColor`,"stroke-width":`3`,"stroke-linecap":`round`})]}):null,L(`span`,{role:`status`,children:x?`해제 중…`:`적용 해제`})]})]})]})}):null,L(V,{eyebrow:`Local overrides`,title:`사용자 도메인 규칙`,description:`허용 목록은 오탐을 해제하고, 차단 목록은 원하는 도메인을 SafeShield 보호 목록에 추가합니다.`}),L(`section`,{"aria-label":`규칙 개수 요약`,class:`min-w-0`,children:L(`dl`,{class:`m-0 grid grid-cols-1 gap-3 sm:grid-cols-3`,children:[L(`div`,{class:`min-w-0 rounded-2xl border border-slate-200 bg-white px-5 py-4 shadow-sm shadow-slate-900/5`,children:[L(`dt`,{class:`text-sm font-extrabold text-emerald-700`,children:`허용 도메인`}),L(`dd`,{class:`mt-2 mb-0 ml-0 text-2xl font-black tracking-tight text-slate-950`,children:[t.counts.allow,L(`span`,{class:`ml-1 text-sm font-bold text-slate-500`,children:`개`})]}),L(`p`,{class:`mt-1 mb-0 text-xs text-slate-500`,children:`차단하지 않도록 등록한 도메인`})]}),L(`div`,{class:`min-w-0 rounded-2xl border border-slate-200 bg-white px-5 py-4 shadow-sm shadow-slate-900/5`,children:[L(`dt`,{class:`text-sm font-extrabold text-red-700`,children:`차단 도메인`}),L(`dd`,{class:`mt-2 mb-0 ml-0 text-2xl font-black tracking-tight text-slate-950`,children:[t.counts.block,L(`span`,{class:`ml-1 text-sm font-bold text-slate-500`,children:`개`})]}),L(`p`,{class:`mt-1 mb-0 text-xs text-slate-500`,children:`추가로 차단하도록 등록한 도메인`})]}),L(`div`,{class:`min-w-0 rounded-2xl border border-slate-200 bg-white px-5 py-4 shadow-sm shadow-slate-900/5`,children:[L(`dt`,{class:`text-sm font-extrabold text-slate-600`,children:`전체 등록 규칙`}),L(`dd`,{class:`mt-2 mb-0 ml-0 text-2xl font-black tracking-tight text-slate-950`,children:[t.counts.total,L(`span`,{class:`ml-1 text-sm font-bold text-slate-500`,children:`개`})]}),L(`p`,{class:`mt-1 mb-0 text-xs text-slate-500`,children:`허용 및 차단 규칙의 합계`})]})]})}),L(`div`,{class:`mt-7`,children:L(V,{eyebrow:`Recommendations`,title:`추천 사용자 규칙`,description:`웹사이트에서 선택한 권장 허용 규칙의 동기화 상태를 확인하고 관리합니다.`})}),L(`section`,{class:`rounded-2xl border border-slate-200 bg-white p-4 shadow-sm shadow-slate-900/5 sm:p-5`,"aria-label":`추천 사용자 규칙`,children:[L(`div`,{class:`flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between`,children:[L(`div`,{class:`min-w-0 flex-1`,children:[L(`div`,{class:`flex flex-wrap items-center gap-2`,children:[L(`span`,{class:`grid size-8 place-items-center rounded-lg bg-teal-50 text-teal-700`,children:L(ut,{class:`size-4`})}),L(`h3`,{class:`m-0 text-base font-extrabold text-slate-950`,children:`동기화 상태`}),l?L(`span`,{"aria-live":`polite`,class:`rounded-full px-2.5 py-1 text-xs font-bold ${f?.syncStatus===`confirmed`?`bg-emerald-100 text-emerald-800`:f?.syncStatus===`failed`?`bg-red-100 text-red-800`:`bg-amber-100 text-amber-800`}`,children:f?.syncStatus===`confirmed`?`허용 목록 확인`:N}):null]}),L(`p`,{class:`mt-2 mb-0 text-sm leading-6 text-slate-600`,children:`웹사이트에서 선택한 허용 규칙을 약 30초마다 확인합니다. 적용 해제는 이 공유기에서도 할 수 있습니다.`}),L(`div`,{class:`mt-2 flex flex-wrap items-center gap-x-4 gap-y-1 text-xs font-semibold text-slate-600`,children:[L(`span`,{children:[`추천으로 추가 `,L(`strong`,{class:`text-teal-800`,children:[m?te.length:`—`,`개 도메인`]})]}),f?.syncStatus===`confirmed`?L(`span`,{children:[`허용 확인 `,L(`strong`,{class:`text-emerald-800`,children:[f.confirmedDomains.length,`개`]})]}):null,f?.lastCheckedAt?L(`span`,{children:[f.syncWarning?`최근 재확인 시도`:`최근 확인`,`: `,L(Dr,{timestamp:f.lastCheckedAt})]}):null]})]}),L(`div`,{class:`flex w-full flex-wrap gap-2 sm:w-auto lg:shrink-0`,children:[u?L(`button`,{class:`inline-flex min-h-10 flex-1 items-center justify-center rounded-xl border border-slate-300 bg-white px-4 py-2.5 text-sm font-extrabold text-slate-800 transition hover:bg-slate-50 disabled:cursor-wait disabled:opacity-60 sm:flex-none`,disabled:g,onClick:()=>void ie(),type:`button`,children:g?`동기화 중…`:`지금 동기화`}):null,L(`a`,{class:`inline-flex min-h-10 flex-1 items-center justify-center rounded-xl border border-teal-700 bg-teal-700 px-4 py-2.5 text-sm font-extrabold text-white transition hover:bg-teal-800 sm:flex-none`,href:`https://www.smartsafehub.com/dashboard/recommended-rules/`,target:`_blank`,rel:`noopener noreferrer`,children:`추천 규칙 보기 ↗`})]})]}),de?L(`div`,{"aria-live":`polite`,class:`mt-3 flex items-start gap-2 rounded-xl px-3 py-2.5 text-sm ${de.tone===`red`?`bg-red-50 text-red-800`:de.tone===`green`?`bg-emerald-50 text-emerald-800`:`bg-amber-50 text-amber-800`}`,role:`status`,children:[L(`span`,{"aria-hidden":`true`,class:`mt-1 size-2 shrink-0 rounded-full ${de.tone===`red`?`bg-red-500`:de.tone===`green`?`bg-emerald-500`:`bg-amber-500`}`}),L(`div`,{class:`min-w-0`,children:[L(`strong`,{children:de.label}),L(`p`,{class:`mt-1 mb-0 leading-5`,children:de.description})]})]}):null,L(`p`,{class:`mt-3 mb-0 text-xs leading-5 text-slate-500`,children:`추천 규칙 동기화는 SafeShield 차단 목록 갱신과 별개입니다. 허용 목록 확인은 DNS 처리나 서비스 재생 성공을 보장하지 않습니다.`})]}),t.applyLocalOverrides?null:L(`section`,{class:`mt-4 flex items-start gap-3 rounded-2xl border border-amber-200 bg-amber-50 p-4`,role:`alert`,children:[L(B,{class:`mt-0.5 size-5 shrink-0 text-amber-700`}),L(`div`,{children:[L(`p`,{class:`m-0 text-sm font-extrabold text-amber-950`,children:`로컬 규칙 적용이 꺼져 있습니다`}),L(`p`,{class:`mt-1 mb-0 text-sm leading-6 text-amber-800`,children:`규칙은 저장되지만 SafeShield의 apply_local_overrides 설정을 켜기 전까지 DNS에 반영되지 않습니다.`})]})]}),t.safeshieldEnabled?null:L(`section`,{class:`mt-4 flex items-start gap-3 rounded-2xl border border-slate-200 bg-slate-100 p-4`,role:`status`,children:[L(Ue,{class:`mt-0.5 size-5 shrink-0 text-slate-600`}),L(`div`,{children:[L(`p`,{class:`m-0 text-sm font-extrabold text-slate-950`,children:`SafeShield가 비활성화되어 있습니다`}),L(`p`,{class:`mt-1 mb-0 text-sm leading-6 text-slate-600`,children:`규칙을 미리 저장할 수 있으며, SafeShield를 켜고 차단 목록을 갱신하면 적용됩니다.`})]})]}),r?L(`section`,{"aria-live":`polite`,class:`mt-4 flex flex-col items-start gap-3 rounded-2xl border border-teal-200 bg-teal-50 sm:flex-row sm:justify-between p-4`,children:[L(`div`,{class:`flex min-w-0 items-start gap-3`,children:[L(ut,{class:`mt-0.5 size-5 shrink-0 text-teal-700`}),L(`p`,{class:`m-0 text-sm font-semibold leading-6 text-teal-950`,children:r})]}),L(`button`,{class:`shrink-0 rounded-lg border-0 bg-transparent px-2 py-1 text-xs font-extrabold text-teal-800 hover:bg-teal-100`,onClick:s,type:`button`,children:`닫기`})]}):null,n&&t?L(`section`,{"aria-live":`assertive`,class:`mt-4 flex items-start gap-3 rounded-2xl border border-red-200 bg-red-50 p-4`,children:[L(B,{class:`mt-0.5 size-5 shrink-0 text-red-700`}),L(`p`,{class:`m-0 text-sm font-semibold leading-6 text-red-900`,children:n})]}):null,L(`section`,{class:`mt-5 grid gap-5 xl:grid-cols-2`,children:[L(Ar,{action:e,kind:`allow`,onAddRule:a,onDeleteRule:o,rules:t.allow,recommendedDomains:te,recommendedKnown:m,recommendedRules:ne,...d?{onRequestRevoke:b}:{},revokeBusy:x}),L(Ar,{action:e,kind:`block`,onAddRule:a,onDeleteRule:o,rules:t.block,recommendedDomains:te,recommendedKnown:m,recommendedRules:ne,revokeBusy:x})]})]}):L(Bn,{message:`사용자 규칙 응답이 비어 있습니다.`,onRetry:c})}function Mr(e,t){if(!e||!t)return`unknown`;if(e===t)return`same`;let n=/^(\d+)\.(\d+)\.(\d+)(?:-r(\d+))?$/,r=n.exec(e),i=n.exec(t);if(!r||!i)return`different`;for(let e=1;e<=4;e+=1){let t=BigInt(r[e]||`0`),n=BigInt(i[e]||`0`);if(t>n)return`newer`;if(t=1048576?`${(e/1048576).toFixed(1)} MB`:`${(e/1024).toFixed(1)} KB`}function Pr(e){switch(e.phase){case`checking`:return`확인 중`;case`downloading`:return`다운로드 중`;case`verifying`:return`검증 중`;case`ready`:return`설치 준비 완료`;case`flashing`:return`설치 중`;case`error`:return`확인 필요`;default:return e.updateAvailable?`업데이트 가능`:e.lastCheckAt?`최신 상태`:`확인 전`}}function Fr(e){return e.phase===`error`?`bg-rose-50 text-rose-700 ring-rose-200`:e.phase===`checking`||e.phase===`downloading`||e.phase===`verifying`||e.phase===`flashing`?`bg-sky-50 text-sky-700 ring-sky-200`:e.phase===`ready`||e.updateAvailable?`bg-amber-50 text-amber-800 ring-amber-200`:e.lastCheckAt?`bg-emerald-50 text-emerald-700 ring-emerald-200`:`bg-slate-100 text-slate-600 ring-slate-200`}function Ir(e){return e.phase===`checking`||e.phase===`downloading`||e.phase===`verifying`||e.phase===`flashing`?L(z,{class:`size-3.5 animate-spin`}):e.phase===`error`?L(B,{class:`size-3.5`}):e.phase===`ready`||e.updateAvailable?L(it,{class:`size-3.5`}):e.lastCheckAt?L(ut,{class:`size-3.5`}):L(tt,{class:`size-3.5`})}function Lr({checked:e,disabled:t,onChange:n}){return L(`button`,{"aria-checked":e,"aria-label":`현재 설정 유지`,class:`ssh-switch-control relative inline-flex shrink-0 rounded-full border transition focus:outline-none focus-visible:ring-4 focus-visible:ring-teal-100 disabled:cursor-not-allowed disabled:opacity-50 ${e?`border-teal-600 bg-teal-600`:`border-slate-300 bg-slate-200`}`,disabled:t,onClick:()=>n(!e),role:`switch`,type:`button`,children:L(`span`,{"aria-hidden":`true`,class:`ssh-switch-thumb absolute top-1 shadow-sm transition-[left] ${e?`left-6`:`left-1`}`})})}function Rr({action:e,actionError:t,data:n,software:r,error:i,loading:a,message:o,reconnecting:s,uploadProgress:c,onCheck:l,onDiscard:u,onDismissFeedback:d,onInstall:f,onPrepare:p,onRetry:m,onUpload:h}){let g=we(null),[_,y]=F(null),[b,x]=F(!0),[S,C]=F(!1);I(()=>{n?.prepared&&x(n.prepared.allowBackup)},[n?.prepared]);let w=e!==null||n?.phase===`checking`||n?.phase===`downloading`||n?.phase===`verifying`||n?.phase===`flashing`,T=n?.phase===`ready`?n.prepared:null,E=e===`check`||n?.phase===`checking`,D=e===`prepare`||n?.phase===`downloading`||n?.phase===`verifying`,O=e===`upload`||e===`validate-upload`,k=n?.release?.bundledSoftware?.smartsafehub??null,A=r?.packages.find(e=>e.name===`luci-app-smartsafehub`)?.installedVersion??null,j=Mr(k,A),M=async()=>{_&&await h(_)&&(y(null),g.current&&(g.current.value=``))};return L(`article`,{class:`min-w-0 overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm shadow-slate-900/5`,"data-component":`firmware-update-card`,children:[L(`div`,{class:`p-5 sm:p-6`,children:[L(`div`,{class:`flex flex-col gap-5 lg:flex-row lg:items-start lg:justify-between`,children:[L(`div`,{class:`flex min-w-0 gap-4`,children:[L(`span`,{class:`grid size-12 shrink-0 place-items-center rounded-2xl bg-sky-50 text-sky-700`,children:L(We,{class:`size-6`})}),L(`div`,{class:`min-w-0`,children:[L(`p`,{class:`m-0 text-xs font-extrabold uppercase tracking-[0.16em] text-sky-700`,children:`Firmware update`}),L(`h2`,{class:`mt-2 mb-0 text-xl font-black text-slate-950 sm:text-2xl`,children:`펌웨어 업데이트`}),L(`p`,{class:`mt-2 mb-0 text-sm leading-6 text-slate-500 lg:whitespace-nowrap`,children:`시스템 펌웨어를 확인하고 설치합니다. 온라인·수동 설치는 안전성 검증 후 진행됩니다.`})]})]}),n?L(`div`,{class:`flex shrink-0 flex-col gap-2 sm:flex-row lg:justify-end`,children:[L(`span`,{class:`inline-flex min-h-11 items-center justify-center gap-1.5 rounded-xl px-3 py-2 text-xs font-extrabold ring-1 ring-inset ${Fr(n)}`,children:[Ir(n),Pr(n)]}),L(`button`,{class:`inline-flex min-h-11 items-center justify-center gap-2 rounded-xl border border-slate-300 bg-white px-4 py-2.5 text-sm font-extrabold text-slate-800 transition hover:bg-slate-50 disabled:cursor-wait disabled:opacity-60`,disabled:w||T!==null,onClick:l,type:`button`,children:[L(z,{class:`size-4 ${E?`animate-spin`:``}`}),E?`확인 중...`:`펌웨어 확인`]}),n.updateAvailable&&!T?L(`button`,{class:`inline-flex min-h-11 items-center justify-center gap-2 rounded-xl bg-sky-700 px-4 py-2.5 text-sm font-extrabold text-white transition hover:bg-sky-800 disabled:cursor-wait disabled:opacity-60`,disabled:w,onClick:p,type:`button`,children:[D?L(z,{class:`size-4 animate-spin`}):L(it,{class:`size-4`}),D?`준비 중...`:`다운로드 및 검증`]}):null]}):null]}),t||o?L(`div`,{class:`mt-5 flex flex-col gap-3 rounded-xl border px-4 py-3 text-sm font-bold sm:flex-row sm:items-center sm:justify-between ${t?`border-rose-200 bg-rose-50 text-rose-800`:`border-emerald-200 bg-emerald-50 text-emerald-800`}`,children:[L(`span`,{children:t||o}),L(`button`,{class:`shrink-0 self-start rounded-lg px-3 py-1.5 text-xs font-extrabold hover:bg-black/5 sm:self-auto`,onClick:d,type:`button`,children:`닫기`})]}):null,s||n?.phase===`flashing`?L(`div`,{class:`mt-5 rounded-xl border border-amber-200 bg-amber-50 p-4 sm:p-5`,role:`status`,children:L(`div`,{class:`flex gap-3`,children:[L(`span`,{class:`grid size-10 shrink-0 place-items-center rounded-xl bg-white text-amber-700 ring-1 ring-inset ring-amber-200`,children:L(z,{class:`size-5 animate-spin`})}),L(`div`,{children:[L(`strong`,{class:`block text-sm font-black text-amber-950`,children:`펌웨어를 설치하고 재부팅하고 있습니다.`}),L(`p`,{class:`mt-1 mb-0 text-sm leading-6 text-amber-800`,children:`전원을 끄지 마세요. 현재 주소에서 공유기가 다시 응답하면 SmartSafeHub가 자동으로 새로고침됩니다.`})]})]})}):null,i&&!n?L(`div`,{class:`mt-5 rounded-xl border border-rose-200 bg-rose-50 p-4 text-sm text-rose-800`,children:[L(`p`,{class:`m-0 font-bold`,children:i}),L(`button`,{class:`mt-3 rounded-lg border border-rose-300 bg-white px-3 py-2 text-xs font-extrabold text-rose-700`,onClick:m,type:`button`,children:`다시 시도`})]}):a&&!n?L(`div`,{class:`mt-5 flex items-center gap-3 rounded-xl bg-slate-50 p-4 text-sm font-bold text-slate-500`,children:[L(z,{class:`size-4 animate-spin`}),`펌웨어 상태를 확인하고 있습니다.`]}):n?L(v,{children:[n.lastError?L(`div`,{class:`mt-5 rounded-xl border border-rose-200 bg-rose-50 p-4 sm:p-5`,children:L(`div`,{class:`flex gap-3`,children:[L(`span`,{class:`grid size-10 shrink-0 place-items-center rounded-xl bg-white text-rose-700 ring-1 ring-inset ring-rose-200`,children:L(B,{class:`size-5`})}),L(`div`,{class:`min-w-0 flex-1`,children:[L(`strong`,{class:`block text-sm font-black text-rose-950`,children:`펌웨어 작업을 완료하지 못했습니다.`}),L(`p`,{class:`mt-1 mb-0 text-sm leading-6 text-rose-800`,children:n.lastError.message}),L(`details`,{class:`mt-3 rounded-lg border border-rose-200 bg-white p-3`,children:[L(`summary`,{class:`cursor-pointer text-xs font-extrabold text-rose-800`,children:`상세 정보 보기`}),L(`p`,{class:`mt-2 mb-0 break-all font-mono text-[11px] text-slate-600`,children:n.lastError.code})]})]})]})}):null,n.current.metadataAvailable?null:L(`div`,{class:`mt-5 rounded-xl border border-amber-200 bg-amber-50 px-4 py-3 text-xs leading-5 text-amber-900`,children:[L(`strong`,{class:`block font-extrabold`,children:`현재 펌웨어의 버전 정보를 완전히 확인할 수 없습니다.`}),L(`span`,{class:`mt-1 block text-amber-800`,children:`새 펌웨어를 설치하면 이후부터 정확한 설치 버전을 자동으로 확인할 수 있습니다.`}),L(`details`,{class:`mt-2`,children:[L(`summary`,{class:`cursor-pointer font-extrabold text-amber-800`,children:`기술 상세 보기`}),L(`p`,{class:`mt-2 mb-0 break-all font-mono text-[11px] leading-5 text-amber-800`,children:`SmartSafeHub build ID metadata is missing. Future images should include /usr/share/smartsafehub/firmware.json.`})]})]}),L(`dl`,{class:`mt-5 grid overflow-hidden rounded-xl border border-slate-200 bg-slate-200 sm:grid-cols-2 xl:grid-cols-4`,children:[L(`div`,{class:`bg-white p-4`,children:[L(`dt`,{class:`text-[11px] font-black uppercase tracking-[0.16em] text-slate-400`,children:`Device`}),L(`dd`,{class:`mt-2 mb-0 ml-0 break-all text-sm font-black text-slate-950`,children:n.current.deviceCode||n.current.boardName||`미확인`})]}),L(`div`,{class:`bg-white p-4`,children:[L(`dt`,{class:`text-[11px] font-black uppercase tracking-[0.16em] text-slate-400`,children:`Current version`}),L(`dd`,{class:`mt-2 mb-0 ml-0 break-all text-sm font-black text-slate-950`,children:n.current.releaseVersion||`미확인`}),n.current.buildId?L(`span`,{class:`mt-1 block break-all text-[11px] leading-5 text-slate-400`,children:[`빌드 ID `,n.current.buildId]}):n.current.openwrtVersion?L(`span`,{class:`mt-1 block text-[11px] leading-5 text-slate-400`,children:[`OpenWrt `,n.current.openwrtVersion]}):null]}),L(`div`,{class:`bg-white p-4`,children:[L(`dt`,{class:`text-[11px] font-black uppercase tracking-[0.16em] text-slate-400`,children:`Available version`}),L(`dd`,{class:`mt-2 mb-0 ml-0 break-all text-sm font-black ${n.updateAvailable?`text-amber-700`:`text-slate-950`}`,children:n.lastCheckAt?n.release?.version?`${n.release.version}${n.updateAvailable?``:` · 최신`}`:n.updateAvailable?`새 펌웨어`:`최신 버전`:`미확인`}),n.release?.buildId?L(`span`,{class:`mt-1 block break-all text-[11px] leading-5 text-slate-400`,children:[`빌드 ID `,n.release.buildId]}):null]}),L(`div`,{class:`bg-white p-4`,children:[L(`dt`,{class:`text-[11px] font-black uppercase tracking-[0.16em] text-slate-400`,children:`Last check`}),L(`dd`,{class:`mt-2 mb-0 ml-0 text-sm font-black text-slate-950`,children:L(Dr,{fallback:`아직 없음`,timestamp:n.lastCheckAt})})]})]})]}):null,n?.updateAvailable&&n.release?L(`section`,{class:`mt-5 rounded-xl border border-sky-200 bg-sky-50/60 p-4 sm:p-5`,"data-section":`available-firmware`,children:[L(`div`,{class:`flex flex-col gap-2 sm:flex-row sm:items-start sm:justify-between sm:gap-4`,children:[L(`div`,{children:[L(`p`,{class:`m-0 text-xs font-extrabold uppercase tracking-[0.16em] text-sky-700`,children:`Available firmware`}),L(`h3`,{class:`mt-2 mb-0 text-lg font-black text-slate-950`,children:`새 펌웨어가 있습니다.`}),L(`p`,{class:`mt-2 mb-0 text-sm leading-6 text-slate-600`,children:[`펌웨어 `,n.release.version||`미확인`,` · `,Nr(n.release.sysupgrade.sizeBytes),` · `,(n.release.channel||n.settings.channel)===`beta`?`Beta`:`Stable`]})]}),L(`div`,{class:`flex shrink-0 flex-wrap items-center justify-start gap-2 sm:justify-end`,children:[L(`span`,{class:`rounded-full bg-white px-3 py-1.5 text-xs font-extrabold text-sky-700 ring-1 ring-inset ring-sky-200`,children:n.release.version||`Latest`}),k?L(`span`,{class:`inline-flex items-center gap-1.5 rounded-full bg-white px-3 py-1.5 text-xs font-extrabold text-slate-700 ring-1 ring-inset ring-sky-200`,"data-section":`firmware-bundled-software-chip`,children:[L(`span`,{class:`text-[11px] uppercase tracking-[0.12em] text-slate-500`,children:`관리 SW`}),L(`span`,{class:`text-slate-950`,children:k})]}):null]})]}),k?L(`div`,{class:`mt-4 rounded-xl border border-sky-100 bg-white p-4`,"data-section":`firmware-bundled-software`,children:[L(`div`,{class:`flex flex-wrap items-center justify-between gap-2`,children:[L(`span`,{class:`text-xs font-extrabold text-slate-800`,children:`포함된 관리 소프트웨어`}),L(`div`,{class:`flex flex-wrap items-center gap-2`,children:[L(`span`,{class:`text-sm font-black text-slate-950`,children:k}),j===`same`?L(`span`,{class:`rounded-full bg-emerald-50 px-2.5 py-1 text-[11px] font-extrabold text-emerald-700 ring-1 ring-inset ring-emerald-200`,children:`현재 버전과 동일`}):null]})]}),L(`p`,{class:`mt-2 mb-0 text-xs leading-5 text-slate-600`,children:j===`same`?`관리 소프트웨어는 이미 같은 버전입니다. 펌웨어 업데이트는 시스템 구성 요소를 갱신합니다.`:j===`older`?`현재 설치된 관리 소프트웨어(${A})보다 이전 버전이 포함되어 있습니다. 펌웨어 설치 전 버전을 확인해 주세요.`:j===`newer`?`현재 설치된 관리 소프트웨어(${A})보다 새 버전이 포함되어 있습니다.`:A?`현재 설치 버전 ${A} · 펌웨어 포함 버전 ${k}`:`이 펌웨어에 포함된 관리 소프트웨어 버전입니다.`})]}):null,n.release.releaseNotes.length?L(`ul`,{class:`mt-4 mb-0 space-y-2 rounded-xl bg-white p-4 pl-9 text-sm leading-6 text-slate-700 ring-1 ring-inset ring-sky-100`,children:n.release.releaseNotes.map((e,t)=>L(`li`,{children:e},`${t}-${e}`))}):null]}):null,T?L(`section`,{class:`mt-5 rounded-xl border border-amber-200 bg-amber-50 p-4 sm:p-5`,"data-section":`prepared-firmware`,children:L(`div`,{class:`flex gap-4`,children:[L(`span`,{class:`grid size-11 shrink-0 place-items-center rounded-xl bg-white text-amber-700 ring-1 ring-inset ring-amber-200`,children:L(ut,{class:`size-5`})}),L(`div`,{class:`min-w-0 flex-1`,children:[L(`p`,{class:`m-0 text-xs font-extrabold uppercase tracking-[0.16em] text-amber-700`,children:`Verified image`}),L(`h3`,{class:`mt-2 mb-0 text-lg font-black text-amber-950`,children:`설치할 펌웨어가 준비되었습니다.`}),L(`p`,{class:`mt-2 mb-0 break-all text-sm leading-6 text-amber-900`,children:[T.filename||`firmware.bin`,` · `,Nr(T.sizeBytes)]}),T.sha256?L(`p`,{class:`mt-2 mb-0 break-all font-mono text-[11px] leading-5 text-amber-800`,children:[`SHA-256 `,T.sha256]}):null,L(`div`,{class:`mt-5 flex items-start justify-between gap-4 rounded-xl border border-amber-200 bg-white p-4`,children:[L(`div`,{children:[L(`strong`,{class:`block text-sm font-black text-slate-950`,children:`현재 설정 유지`}),L(`span`,{class:`mt-1 block text-xs leading-5 text-slate-500`,children:`Wi-Fi, SafeShield, SmartSafeHub 설정을 유지합니다.`}),T.allowBackup?null:L(`span`,{class:`mt-2 block text-xs font-bold text-rose-700`,children:`이 이미지는 설정 유지 업그레이드를 지원하지 않습니다.`})]}),L(Lr,{checked:b,disabled:!T.allowBackup,onChange:x})]}),S?L(`div`,{class:`mt-4 rounded-xl border border-rose-200 bg-rose-50 p-4`,children:[L(`strong`,{class:`block text-sm font-black text-rose-950`,children:`펌웨어 설치를 시작할까요?`}),L(`p`,{class:`mt-1 mb-0 text-xs leading-5 text-rose-800`,children:`설치 중 전원을 끄면 장치가 복구 불가능한 상태가 될 수 있습니다. 이미지 검증은 설치 직전에 다시 수행됩니다.`}),L(`div`,{class:`mt-4 flex flex-wrap justify-end gap-2`,children:[L(`button`,{class:`min-h-10 rounded-lg border border-slate-300 bg-white px-3 py-2 text-xs font-extrabold text-slate-700`,onClick:()=>C(!1),type:`button`,children:`취소`}),L(`button`,{class:`min-h-10 rounded-lg bg-rose-700 px-3 py-2 text-xs font-extrabold text-white`,onClick:()=>{C(!1),f(b)},type:`button`,children:`설치 시작`})]})]}):L(`div`,{class:`mt-4 flex flex-wrap justify-end gap-2`,children:[L(`button`,{class:`min-h-10 rounded-lg border border-slate-300 bg-white px-3 py-2 text-xs font-extrabold text-slate-700`,disabled:e===`discard`,onClick:()=>void u(),type:`button`,children:`파일 삭제`}),L(`button`,{class:`inline-flex min-h-10 items-center gap-2 rounded-lg bg-rose-700 px-4 py-2 text-xs font-extrabold text-white`,onClick:()=>C(!0),type:`button`,children:[L(Je,{class:`size-4`}),`펌웨어 설치`]})]})]})]})}):null]}),L(`details`,{class:`group mx-5 mb-5 rounded-xl border border-slate-200 bg-slate-50/70 sm:mx-6 sm:mb-6`,"data-layout":`firmware-card-subsection`,"data-section":`manual-firmware`,children:[L(`summary`,{class:`flex cursor-pointer list-none items-center justify-between gap-4 px-4 py-4 sm:px-5 [&::-webkit-details-marker]:hidden`,children:[L(`div`,{class:`flex min-w-0 items-center gap-3`,children:[L(`span`,{class:`grid size-10 shrink-0 place-items-center rounded-xl bg-white text-slate-600 ring-1 ring-inset ring-slate-200`,children:L(it,{class:`size-5 rotate-180`})}),L(`div`,{class:`min-w-0`,children:[L(`strong`,{class:`block text-sm font-black text-slate-950`,children:`수동 펌웨어 설치`}),L(`span`,{class:`mt-1 block text-xs leading-5 text-slate-500`,children:`펌웨어 파일(.bin)을 직접 업로드하여 검증한 뒤 설치합니다.`})]})]}),L(`span`,{class:`shrink-0 rounded-full bg-white px-3 py-1.5 text-[11px] font-extrabold text-slate-600 ring-1 ring-inset ring-slate-200 group-open:hidden`,children:`열기`}),L(`span`,{class:`hidden shrink-0 rounded-full bg-white px-3 py-1.5 text-[11px] font-extrabold text-slate-600 ring-1 ring-inset ring-slate-200 group-open:inline-flex`,children:`닫기`})]}),L(`div`,{class:`border-t border-slate-200 px-5 pb-5 pt-3 sm:px-6`,children:[L(`p`,{class:`mt-0 mb-4 text-xs leading-5 text-slate-500 lg:whitespace-nowrap`,children:"이 장치에 맞는 Sysupgrade 이미지(.bin)만 사용하세요. 업로드한 파일은 이미지 검증과 `sysupgrade --test`를 모두 통과해야 설치할 수 있으며 강제 설치는 제공하지 않습니다."}),L(`div`,{class:`rounded-xl border border-slate-200 bg-white p-4 sm:p-5`,"data-layout":`firmware-file-picker`,children:[L(`input`,{accept:`.bin,application/octet-stream`,"aria-label":`펌웨어 파일 선택`,class:`sr-only`,disabled:w||T!==null,onChange:e=>y(e.currentTarget.files?.[0]??null),ref:g,type:`file`}),L(`div`,{class:`flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between`,children:[L(`div`,{class:`flex min-w-0 items-center gap-3`,children:[L(`span`,{class:`grid size-10 shrink-0 place-items-center rounded-xl bg-slate-50 text-slate-500 ring-1 ring-inset ring-slate-200`,children:L(it,{class:`size-5 rotate-180`})}),L(`div`,{class:`min-w-0`,children:[L(`strong`,{class:`block truncate text-sm font-black text-slate-900`,children:_?_.name:`펌웨어 파일을 선택하세요`}),L(`span`,{class:`mt-1 block text-xs leading-5 text-slate-500`,children:_?`${Nr(_.size)} · Sysupgrade 이미지 (.bin)`:`이 장치에 맞는 Sysupgrade 이미지 (.bin)를 선택합니다.`})]})]}),L(`div`,{class:`flex shrink-0 flex-wrap gap-2 sm:justify-end`,children:[L(`button`,{class:`inline-flex min-h-10 items-center justify-center rounded-lg border border-slate-300 bg-white px-3.5 py-2 text-xs font-extrabold text-slate-700 transition hover:bg-slate-50 disabled:cursor-not-allowed disabled:opacity-50`,disabled:w||T!==null,onClick:()=>g.current?.click(),type:`button`,children:_?`다른 파일 선택`:`파일 선택`}),L(`button`,{class:`inline-flex min-h-10 items-center justify-center gap-2 rounded-lg bg-slate-900 px-3.5 py-2 text-xs font-extrabold text-white transition hover:bg-slate-800 disabled:cursor-not-allowed disabled:opacity-50`,disabled:!_||w||T!==null,onClick:()=>void M(),type:`button`,children:[O?L(z,{class:`size-4 animate-spin`}):L(it,{class:`size-4 rotate-180`}),O?`처리 중...`:`업로드 및 검증`]})]})]}),c===null?null:L(`div`,{class:`mt-4 border-t border-slate-100 pt-4`,children:[L(`div`,{class:`mb-2 flex items-center justify-between gap-3 text-xs font-bold text-slate-600`,children:[L(`span`,{children:`업로드 중`}),L(`span`,{children:[c,`%`]})]}),L(`div`,{class:`h-2 overflow-hidden rounded-full bg-slate-200`,children:L(`div`,{class:`h-full rounded-full bg-sky-600 transition-[width]`,style:{width:`${Math.max(0,Math.min(100,c))}%`}})})]})]})]})]})]})}var zr=Array.from({length:24},(e,t)=>{let n=String(t).padStart(2,`0`);return{value:n,label:`${n}시`}}),Br=Array.from({length:60},(e,t)=>{let n=String(t).padStart(2,`0`);return{value:n,label:`${n}분`}});function Vr(e){let t=/^([01]\d|2[0-3]):([0-5]\d)$/.exec(e);return{hour:t?.[1]??`03`,minute:t?.[2]??`00`}}function Hr({ariaLabel:e,className:t=``,disabled:n=!1,id:r,onChange:i,value:a,variant:o=`default`}){let{hour:s,minute:c}=Vr(a),l=(e,t)=>{i(`${e}:${t}`)};return L(`div`,{"aria-label":e,class:`ssh-time-select ${t}`.trim(),"data-time-select":r,role:`group`,children:[L(Pn,{ariaLabel:`${e} 시`,disabled:n,id:`${r}-hour`,onChange:e=>l(e,c),options:zr,value:s,variant:o}),L(Pn,{ariaLabel:`${e} 분`,disabled:n,id:`${r}-minute`,onChange:e=>l(s,e),options:Br,value:c,variant:o})]})}var Ur=7200;function Wr(e,t=Math.floor(Date.now()/1e3)){return!e||!e.settings.checkEnabled||e.phase===`checking`||!e.lastCheckAt?!1:Math.max(0,t-e.lastCheckAt)>Math.max(e.settings.checkIntervalSeconds*2,Ur)}var Gr=[{value:3600,label:`1시간마다`},{value:21600,label:`6시간마다`},{value:43200,label:`12시간마다`},{value:86400,label:`24시간마다`}],Kr=`luci-app-smartsafehub`;function qr(e){return e?new Date(e*1e3).toLocaleString(`ko-KR`,{dateStyle:`medium`,timeStyle:`short`}):`아직 없음`}function Jr(e){return e===`stable`?`Stable`:e===`beta`?`Beta`:`미확인`}function Yr(e){return e===`stable`?`bg-emerald-50 text-emerald-700 ring-emerald-200`:e===`beta`?`bg-amber-50 text-amber-700 ring-amber-200`:`bg-slate-100 text-slate-600 ring-slate-200`}function Xr(e){return e.code===`UPDATES_INDEX_REFRESH_FAILED`?{title:`패키지 저장소를 확인하지 못했습니다.`,description:`OpenWrt 패키지 저장소 동기화 또는 네트워크 상태가 일시적으로 불안정할 수 있습니다. 잠시 후 업데이트 확인을 다시 실행해 주세요.`}:e.code===`UPDATES_INSTALL_FAILED`?{title:`관리 소프트웨어 업데이트 설치에 실패했습니다.`,description:`패키지 설치가 완료되지 않았습니다. 네트워크 연결과 저장 공간을 확인한 뒤 다시 시도해 주세요.`}:{title:`최근 업데이트 작업을 완료하지 못했습니다.`,description:`잠시 후 다시 시도해 주세요. 문제가 반복되면 상세 정보를 확인해 주세요.`}}function Zr(e,t){return e.phase===`checking`?`업데이트 확인 중`:e.phase===`installing`?`업데이트 설치 중`:e.phase===`error`?`확인 필요`:e.updateCount>0?`업데이트 가능`:t?`업데이트 확인 지연`:e.lastCheckAt?`최신 상태`:`확인 전`}function Qr(e,t){return e.phase===`error`?`bg-rose-50 text-rose-700 ring-rose-200`:e.phase===`checking`||e.phase===`installing`?`bg-sky-50 text-sky-700 ring-sky-200`:e.updateCount>0||t?`bg-amber-50 text-amber-800 ring-amber-200`:e.lastCheckAt?`bg-emerald-50 text-emerald-700 ring-emerald-200`:`bg-slate-100 text-slate-600 ring-slate-200`}function $r(e,t){return e.phase===`checking`||e.phase===`installing`?L(z,{class:`size-3.5 shrink-0 animate-spin`}):e.phase===`error`?L(B,{class:`size-3.5 shrink-0`}):e.updateCount>0?L(it,{class:`size-3.5 shrink-0`}):t?L(B,{class:`size-3.5 shrink-0`}):e.lastCheckAt?L(ut,{class:`size-3.5 shrink-0`}):L(tt,{class:`size-3.5 shrink-0`})}function ei({checked:e,disabled:t=!1,label:n,onChange:r}){return L(`button`,{"aria-checked":e,"aria-label":n,class:`ssh-switch-control relative inline-flex shrink-0 rounded-full border transition focus:outline-none focus-visible:ring-4 focus-visible:ring-teal-100 disabled:cursor-not-allowed disabled:opacity-50 ${e?`border-teal-600 bg-teal-600`:`border-slate-300 bg-slate-200`}`,disabled:t,onClick:()=>r(!e),role:`switch`,type:`button`,children:L(`span`,{"aria-hidden":`true`,class:`ssh-switch-thumb absolute top-1 shadow-sm transition-[left] ${e?`left-6`:`left-1`}`})})}function ti({action:e,actionError:t,data:n,firmware:r,error:i,loading:a,message:o,onCheck:s,onDismissFeedback:c,onInstall:l,onRetry:u,onSaveSettings:d}){let[f,p]=F(!0),[m,h]=F(21600),[g,_]=F(!1),[y,b]=F(`03:00`),[x,S]=F(!1),[C,w]=F(!1);I(()=>{n&&!x&&(p(n.settings.checkEnabled),h(n.settings.checkIntervalSeconds),_(n.settings.autoInstall),b(n.settings.autoInstallTime))},[n,x]);let T=e===`check`||n?.phase===`checking`,E=e===`install`||n?.phase===`installing`,D=T||E,O=Wr(n),k=n?.phase===`error`&&n.lastError?.code===`UPDATES_INDEX_REFRESH_FAILED`,A=n?.lastError?Xr(n.lastError):null,j=n?.packages.find(e=>e.name===Kr)??n?.packages[0]??null,M=r?.updateAvailable?r.release?.bundledSoftware?.smartsafehub:null,ee=!(!n?.lastCheckAt||n.updateCount!==0||O||j?.name!==Kr)&&Mr(M,j?.installedVersion)===`same`,te=(e={})=>n?(e.checkEnabled??f)!==n.settings.checkEnabled||(e.checkIntervalSeconds??m)!==n.settings.checkIntervalSeconds||(e.autoInstall??g)!==n.settings.autoInstall||(e.autoInstallTime??y)!==n.settings.autoInstallTime:!1,ne=async()=>{await d({checkEnabled:f,checkIntervalSeconds:m,autoInstall:g,autoInstallTime:y})&&S(!1)};return L(`article`,{class:`min-w-0 overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm shadow-slate-900/5 ${n?`grid xl:grid-cols-[minmax(0,0.95fr)_minmax(0,1.05fr)]`:``}`,"data-component":`management-software-update-card`,"data-layout":`management-software-sections`,children:[L(`section`,{class:`min-w-0 xl:col-start-1 xl:row-start-1`,"data-section":`software-update-status`,children:[L(`div`,{class:`p-5 sm:p-6`,children:[L(`div`,{class:`flex flex-col gap-5 lg:flex-row lg:items-start lg:justify-between`,children:[L(`div`,{class:`flex min-w-0 gap-4`,children:[L(`span`,{class:`grid size-12 shrink-0 place-items-center rounded-2xl bg-teal-50 text-teal-700`,children:L(Je,{class:`size-6`})}),L(`div`,{class:`min-w-0`,children:[L(`div`,{class:`flex flex-wrap items-center gap-2`,children:[L(`p`,{class:`m-0 text-xs font-extrabold uppercase tracking-[0.16em] text-teal-700`,children:`Management software`}),n?L(`span`,{class:`inline-flex items-center gap-1.5 rounded-full px-2.5 py-1 text-[11px] font-extrabold ring-1 ring-inset ${Qr(n,O)}`,children:[L(`span`,{"aria-hidden":`true`,class:`flex items-center justify-center`,children:$r(n,O)}),Zr(n,O)]}):null]}),L(`h2`,{class:`mt-2 mb-0 text-xl font-black text-slate-950 sm:text-2xl`,children:`관리 소프트웨어 업데이트`}),L(`p`,{class:`mt-2 mb-0 text-sm leading-6 text-slate-500 xl:whitespace-nowrap`,children:`새 버전을 확인하고 안전하게 설치합니다.`})]})]}),n?L(`div`,{class:`flex shrink-0 flex-col gap-2 sm:flex-row lg:justify-end`,children:[L(`button`,{class:`inline-flex min-h-11 items-center justify-center gap-2 rounded-xl border border-slate-300 bg-white px-4 py-2.5 text-sm font-extrabold text-slate-800 transition hover:bg-slate-50 disabled:cursor-wait disabled:opacity-60`,disabled:D,onClick:s,type:`button`,children:[L(z,{class:`size-4 ${T?`animate-spin`:``}`}),T?`확인 중...`:`업데이트 확인`]}),n.updateCount>0&&!C?L(`button`,{class:`inline-flex min-h-11 items-center justify-center gap-2 rounded-xl bg-teal-700 px-4 py-2.5 text-sm font-extrabold text-white transition hover:bg-teal-800 disabled:cursor-wait disabled:opacity-60`,disabled:D,onClick:()=>w(!0),type:`button`,children:[E?L(z,{class:`size-4 animate-spin`}):L(it,{class:`size-4`}),E?`설치 중...`:`업데이트 설치`]}):null]}):null]}),(t||o)&&L(`div`,{class:`mt-5 flex flex-col gap-3 rounded-xl border px-4 py-3 text-sm font-bold sm:flex-row sm:items-center sm:justify-between ${t?`border-rose-200 bg-rose-50 text-rose-800`:`border-emerald-200 bg-emerald-50 text-emerald-800`}`,children:[L(`span`,{children:t||o}),L(`button`,{class:`shrink-0 self-start rounded-lg px-3 py-1.5 text-xs font-extrabold hover:bg-black/5 sm:self-auto`,onClick:c,type:`button`,children:`닫기`})]}),E?L(`div`,{"aria-busy":`true`,"aria-live":`polite`,class:`mt-5 rounded-xl border border-sky-200 bg-sky-50 p-4 sm:p-5`,children:[L(`div`,{class:`flex items-start gap-3`,children:[L(`span`,{class:`grid size-10 shrink-0 place-items-center rounded-xl bg-white text-sky-700 ring-1 ring-inset ring-sky-200`,children:L(z,{"aria-hidden":`true`,class:`size-5 animate-spin`})}),L(`div`,{class:`min-w-0`,children:[L(`h3`,{class:`m-0 text-sm font-black text-sky-950`,children:`관리 소프트웨어를 업데이트하고 있습니다.`}),L(`p`,{class:`mt-1 mb-0 text-sm leading-6 text-sky-800`,children:`설치가 완료되면 이 화면이 자동으로 갱신됩니다. 작업 중에는 웹 연결이 잠시 끊길 수 있습니다.`})]})]}),L(`div`,{"aria-hidden":`true`,class:`ssh-update-progress-track mt-4`,children:L(`span`,{class:`ssh-update-progress-bar`})})]}):null,i&&!n?L(`div`,{class:`mt-5 rounded-xl border border-rose-200 bg-rose-50 p-4 text-sm text-rose-800`,children:[L(`p`,{class:`m-0 font-bold`,children:i}),L(`button`,{class:`mt-3 rounded-lg border border-rose-300 bg-white px-3 py-2 text-xs font-extrabold text-rose-700`,onClick:u,type:`button`,children:`다시 시도`})]}):a&&!n?L(`div`,{class:`mt-5 flex items-center gap-3 rounded-xl bg-slate-50 p-4 text-sm font-bold text-slate-500`,children:[L(`span`,{class:`grid size-9 shrink-0 place-items-center rounded-xl bg-white text-teal-700 ring-1 ring-inset ring-slate-200`,children:L(z,{"aria-hidden":`true`,class:`size-4 animate-spin`})}),L(`span`,{children:`관리 소프트웨어 업데이트 상태를 확인하고 있습니다.`})]}):n?L(v,{children:[n.lastError&&A?L(`div`,{class:`mt-5 rounded-xl border border-rose-200 bg-rose-50 p-4 sm:p-5`,children:L(`div`,{class:`flex items-start gap-3`,children:[L(`span`,{class:`grid size-10 shrink-0 place-items-center rounded-xl bg-white text-rose-700 ring-1 ring-inset ring-rose-200`,children:L(B,{class:`size-5`})}),L(`div`,{class:`min-w-0 flex-1`,children:[L(`p`,{class:`m-0 text-sm font-extrabold text-rose-950`,children:A.title}),L(`p`,{class:`mt-1 mb-0 text-sm leading-6 text-rose-800`,children:A.description}),n.lastError.at?L(`p`,{class:`mt-2 mb-0 text-xs font-bold text-rose-700`,children:[`발생 시각 `,qr(n.lastError.at)]}):null,L(`details`,{class:`mt-3 rounded-lg border border-rose-200 bg-white p-3`,children:[L(`summary`,{class:`cursor-pointer text-xs font-extrabold text-rose-800`,children:`상세 정보 보기`}),L(`div`,{class:`mt-3 grid gap-2 text-xs leading-5 text-slate-600`,children:[L(`p`,{class:`m-0`,children:[`오류 코드 `,L(`strong`,{class:`font-black text-slate-900`,children:n.lastError.code})]}),L(`pre`,{class:`m-0 max-h-40 overflow-auto whitespace-pre-wrap break-words rounded-lg bg-slate-50 p-3 font-mono text-[11px] leading-5 text-slate-700`,children:n.lastError.message})]})]})]})]})}):null,C?L(`div`,{class:`mt-5 flex flex-col gap-3 rounded-xl border border-amber-200 bg-amber-50 p-4 sm:flex-row sm:items-center sm:justify-between`,children:[L(`div`,{class:`min-w-0`,children:[L(`strong`,{class:`block text-sm font-black text-amber-900`,children:`관리 소프트웨어를 지금 업데이트할까요?`}),L(`span`,{class:`mt-1 block text-xs leading-5 text-amber-800`,children:`설치 중 SmartSafeHub 웹 화면 연결이 잠시 끊길 수 있습니다.`})]}),L(`div`,{class:`flex shrink-0 gap-2`,children:[L(`button`,{class:`min-h-10 rounded-lg border border-slate-300 bg-white px-3 py-2 text-xs font-extrabold text-slate-700`,disabled:D,onClick:()=>w(!1),type:`button`,children:`취소`}),L(`button`,{class:`min-h-10 rounded-lg bg-amber-700 px-3 py-2 text-xs font-extrabold text-white disabled:opacity-60`,disabled:D,onClick:()=>{w(!1),l()},type:`button`,children:`설치 시작`})]})]}):null]}):null]}),n?L(`div`,{class:`px-5 pb-5 sm:px-6 sm:pb-6`,children:[L(`h3`,{class:`mb-3 text-sm font-black text-slate-950`,children:`현재 상태`}),L(`dl`,{class:`grid overflow-hidden rounded-xl border border-slate-200 bg-slate-200 sm:grid-cols-2`,children:[L(`div`,{class:`bg-white p-4`,children:[L(`dt`,{class:`text-[11px] font-black uppercase tracking-[0.16em] text-slate-400`,children:`Installed`}),L(`dd`,{class:`mt-2 mb-0 ml-0 break-all text-sm font-black text-slate-950`,children:j?.installedVersion||`미확인`})]}),L(`div`,{class:`bg-white p-4`,children:[L(`dt`,{class:`text-[11px] font-black uppercase tracking-[0.16em] text-slate-400`,children:`Available`}),L(`dd`,{class:`mt-2 mb-0 ml-0 break-all text-sm font-black ${j?.updateAvailable||O?`text-amber-700`:`text-slate-950`}`,children:!n.lastCheckAt||k&&!j?.updateAvailable?`미확인`:j?.updateAvailable&&j.availableVersion?j.availableVersion:O?`확인 필요`:`최신 버전`})]}),L(`div`,{class:`bg-white p-4`,children:[L(`dt`,{class:`text-[11px] font-black uppercase tracking-[0.16em] text-slate-400`,children:`Last check`}),L(`dd`,{class:`mt-2 mb-0 ml-0 text-sm font-black text-slate-950`,children:L(Dr,{fallback:`아직 없음`,timestamp:n.lastCheckAt})})]}),L(`div`,{class:`bg-white p-4`,children:[L(`dt`,{class:`text-[11px] font-black uppercase tracking-[0.16em] text-slate-400`,children:`Auto install`}),L(`dd`,{class:`mt-2 mb-0 ml-0 text-sm font-black text-slate-950`,children:n.settings.autoInstall?`켜짐 · ${n.settings.autoInstallTime}`:`꺼짐`})]})]})]}):null,n&&n.updateCount===0&&n.lastCheckAt?O?L(`section`,{class:`mx-5 mb-5 rounded-xl border border-amber-200 bg-amber-50 p-4 sm:mx-6 sm:mb-6 sm:p-5`,"data-section":`software-update-result`,children:L(`div`,{class:`flex gap-3`,children:[L(`span`,{class:`grid size-10 shrink-0 place-items-center rounded-xl bg-white text-amber-700 ring-1 ring-inset ring-amber-200`,children:L(B,{class:`size-5`})}),L(`div`,{children:[L(`h3`,{class:`m-0 text-base font-black text-amber-950`,children:`업데이트 확인이 지연되고 있습니다.`}),L(`p`,{class:`mt-1 mb-0 text-sm leading-6 text-amber-800`,children:[`마지막 업데이트 확인: `,L(Dr,{timestamp:n.lastCheckAt}),`. 자동 확인 상태를 점검하거나 업데이트 확인을 실행해 주세요.`]})]})]})}):L(`section`,{class:`mx-5 mb-5 rounded-xl border border-emerald-200 bg-emerald-50 p-4 sm:mx-6 sm:mb-6 sm:p-5`,"data-section":`software-update-result`,children:L(`div`,{class:`flex gap-3`,children:[L(`span`,{class:`grid size-10 shrink-0 place-items-center rounded-xl bg-white text-emerald-700`,children:L(ut,{class:`size-5`})}),L(`div`,{children:[L(`h3`,{class:`m-0 text-base font-black text-emerald-950`,children:`최신 버전을 사용 중입니다.`}),L(`p`,{class:`mt-1 mb-0 text-sm leading-6 text-emerald-800`,children:[`마지막 업데이트 확인: `,L(Dr,{timestamp:n.lastCheckAt}),`.`]}),ee?L(`p`,{class:`mt-2 mb-0 text-xs leading-5 text-emerald-800`,"data-section":`software-firmware-match`,children:[`새 펌웨어에도 동일한 관리 소프트웨어(`,M,`)가 포함되어 있어 별도로 업데이트할 필요가 없습니다.`]}):null]})]})}):null,n&&n.updateCount===0&&!n.lastCheckAt?L(`section`,{class:`mx-5 mb-5 rounded-xl border border-slate-200 bg-slate-50 p-4 sm:mx-6 sm:mb-6 sm:p-5`,"data-section":`software-update-result`,children:L(`div`,{class:`flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between`,children:[L(`div`,{class:`flex min-w-0 gap-3`,children:[L(`span`,{class:`grid size-10 shrink-0 place-items-center rounded-xl bg-white text-slate-500 ring-1 ring-inset ring-slate-200`,children:L(z,{class:`size-5`})}),L(`div`,{class:`min-w-0`,children:[L(`h3`,{class:`m-0 text-base font-black text-slate-900`,children:`업데이트 상태를 아직 확인하지 않았습니다.`}),L(`p`,{class:`mt-1 mb-0 text-sm leading-6 text-slate-600`,children:`업데이트 확인을 실행하면 현재 설치 버전과 사용 가능한 최신 버전을 확인합니다.`})]})]}),L(`button`,{class:`inline-flex min-h-10 shrink-0 items-center justify-center gap-2 rounded-xl border border-slate-300 bg-white px-4 py-2 text-sm font-extrabold text-slate-800 transition hover:border-teal-300 hover:text-teal-700 disabled:cursor-wait disabled:opacity-60`,disabled:D,onClick:s,type:`button`,children:[L(z,{class:`size-4`}),`지금 확인`]})]})}):null]}),n?L(v,{children:[L(`section`,{class:`min-w-0 border-t border-slate-200 p-5 sm:p-6 xl:col-start-2 xl:row-start-1 xl:row-span-2 xl:self-stretch xl:border-t-0 xl:border-l`,"data-section":`software-update-settings`,children:[L(`div`,{class:`flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between`,children:[L(`div`,{class:`flex min-w-0 gap-4`,children:[L(`span`,{class:`grid size-11 shrink-0 place-items-center rounded-xl bg-slate-100 text-slate-600`,children:L(Ye,{class:`size-5`})}),L(`div`,{class:`min-w-0`,children:[L(`p`,{class:`m-0 text-xs font-extrabold uppercase tracking-[0.16em] text-slate-500`,children:`Settings`}),L(`h3`,{class:`mt-2 mb-0 text-lg font-black text-slate-950`,children:`자동 업데이트`}),L(`p`,{class:`mt-2 mb-0 max-w-3xl text-sm leading-6 text-slate-500`,children:`관리 소프트웨어의 자동 확인과 자동 설치를 설정합니다.`})]})]}),x?L(`span`,{"aria-live":`polite`,class:`inline-flex shrink-0 self-start items-center gap-1.5 rounded-full border border-amber-200 bg-amber-50 px-2.5 py-1 text-xs font-extrabold text-amber-800`,role:`status`,children:[L(B,{"aria-hidden":`true`,class:`size-3.5 shrink-0`}),`저장되지 않음`]}):null]}),L(`div`,{class:`ssh-software-settings-grid mt-4`,children:[L(`div`,{class:`ssh-software-settings-channel rounded-xl border border-slate-200 p-4 sm:p-5`,children:L(`div`,{class:`flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between`,children:[L(`div`,{class:`min-w-0`,children:[L(`strong`,{class:`block text-sm font-black text-slate-950`,children:`업데이트 채널`}),L(`span`,{class:`mt-1 block text-xs leading-5 text-slate-500`,children:`현재 관리 소프트웨어 업데이트를 확인하는 배포 채널입니다.`})]}),L(`span`,{class:`inline-flex min-h-9 shrink-0 items-center justify-center rounded-full px-3 py-1.5 text-xs font-extrabold ring-1 ring-inset ${Yr(n.settings.channel)}`,title:`현재 업데이트 확인 채널`,children:Jr(n.settings.channel)})]})}),L(`div`,{class:`min-w-0 overflow-hidden rounded-xl border border-slate-200 p-4 sm:p-5`,children:[L(`div`,{class:`flex min-w-0 items-start justify-between gap-4`,children:[L(`div`,{class:`min-w-0`,children:L(`strong`,{class:`block text-sm font-black text-slate-950`,children:`자동으로 업데이트 확인`})}),L(ei,{checked:f,label:`자동 업데이트 확인`,onChange:e=>{p(e),S(te({checkEnabled:e}))}})]}),L(`div`,{class:`mt-4 block`,children:[L(`span`,{class:`text-xs font-extrabold text-slate-700`,children:`확인 주기`}),L(Pn,{ariaLabel:`업데이트 확인 주기`,className:`mt-2`,disabled:!f,id:`software-update-check-interval`,onChange:e=>{let t=Number(e);h(t),S(te({checkIntervalSeconds:t}))},options:Gr.map(e=>({value:String(e.value),label:e.label})),value:String(m),variant:`emphasized`})]})]}),L(`div`,{class:`min-w-0 overflow-hidden rounded-xl border border-slate-200 p-4 sm:p-5`,children:[L(`div`,{class:`flex min-w-0 items-start justify-between gap-4`,children:[L(`div`,{class:`min-w-0`,children:L(`strong`,{class:`block text-sm font-black text-slate-950`,children:`자동으로 업데이트 설치`})}),L(ei,{checked:g,label:`자동 업데이트 설치`,onChange:e=>{_(e),S(te({autoInstall:e}))}})]}),L(`div`,{class:`mt-4 min-w-0`,children:[L(`span`,{class:`text-xs font-extrabold text-slate-700`,children:`설치 시각`}),L(Hr,{ariaLabel:`자동 업데이트 설치 시각`,className:`mt-2`,disabled:!g,id:`software-update-auto-install-time`,onChange:e=>{b(e),S(te({autoInstallTime:e}))},value:y,variant:`emphasized`})]})]})]}),g?L(`p`,{class:`mt-4 mb-0 rounded-xl bg-amber-50 px-4 py-3 text-xs font-bold leading-5 text-amber-800`,children:`자동 설치 중에는 SmartSafeHub 웹 화면 연결이 잠시 끊길 수 있습니다.`}):null,L(`div`,{class:`mt-5 flex flex-col gap-3 border-t border-slate-100 pt-5 sm:flex-row sm:items-center sm:justify-between`,children:[L(`p`,{class:`m-0 text-xs leading-5 text-slate-500`,children:[`마지막 설치: `,qr(n.lastInstallAt)]}),L(`button`,{class:`inline-flex min-h-11 items-center justify-center rounded-xl bg-slate-900 px-4 py-2.5 text-sm font-extrabold text-white transition hover:bg-slate-800 disabled:cursor-not-allowed disabled:opacity-50`,disabled:!x||e===`settings`||D,onClick:()=>void ne(),type:`button`,children:e===`settings`?`저장 중`:`자동 업데이트 설정 저장`})]})]}),n.updateCount>0?L(`section`,{class:`border-t border-slate-200 p-5 sm:p-6 xl:col-start-1 xl:row-start-2`,"data-section":`software-update-release-notes`,children:[L(`div`,{class:`flex flex-col gap-2 sm:flex-row sm:items-start sm:justify-between sm:gap-4`,children:[L(`div`,{children:[L(`p`,{class:`m-0 text-xs font-extrabold uppercase tracking-[0.16em] text-teal-700`,children:`Release notes`}),L(`h3`,{class:`mt-2 mb-0 text-lg font-black text-slate-950`,children:`업데이트 내용`}),L(`p`,{class:`mt-2 mb-0 text-sm leading-6 text-slate-500`,children:`현재 설치된 버전 이후 포함되는 주요 변경 사항입니다.`})]}),n.releaseNotes.length?L(`span`,{class:`shrink-0 rounded-full bg-slate-100 px-3 py-1.5 text-xs font-extrabold text-slate-600`,children:n.releaseNotes.length===1?`1개 릴리즈`:`${n.releaseNotes.length}개 릴리즈`}):null]}),n.releaseNotes.length?L(`div`,{class:`mt-5 grid gap-3`,children:[n.releaseNotes.map(e=>L(`article`,{class:`rounded-xl border border-slate-200 bg-slate-50 p-4 sm:p-5`,children:[L(`div`,{class:`flex flex-col gap-1 sm:flex-row sm:items-center sm:justify-between sm:gap-4`,children:[L(`strong`,{class:`break-all text-sm font-black text-slate-950`,children:e.version}),e.date?L(`span`,{class:`text-xs font-bold text-slate-500`,children:e.date}):null]}),e.summary?L(`p`,{class:`mt-3 mb-0 text-sm leading-6 text-slate-700`,children:e.summary}):null,e.sections.length?L(`details`,{class:`mt-4 rounded-lg border border-slate-200 bg-white p-3`,open:n.releaseNotes.length===1,children:[L(`summary`,{class:`cursor-pointer text-xs font-extrabold text-slate-800`,children:`자세한 변경 사항`}),L(`div`,{class:`mt-3 grid gap-4`,children:e.sections.map(t=>L(`section`,{children:[L(`h4`,{class:`m-0 text-xs font-extrabold text-slate-700`,children:t.title}),L(`ul`,{class:`mt-2 mb-0 space-y-1.5 pl-5 text-xs leading-5 text-slate-600`,children:t.items.map((n,r)=>L(`li`,{children:n},`${e.version}-${t.title}-${r}`))})]},`${e.version}-${t.title}`))})]}):null]},e.version)),n.releaseNotesComplete?null:L(`p`,{class:`m-0 rounded-xl bg-amber-50 px-4 py-3 text-xs font-bold leading-5 text-amber-800`,children:`일부 릴리즈 노트를 불러오지 못했습니다. 업데이트 확인과 설치는 계속 사용할 수 있습니다.`})]}):L(`p`,{class:`mt-4 mb-0 rounded-xl bg-slate-50 px-4 py-3 text-sm text-slate-500`,children:`릴리즈 노트를 불러오지 못했습니다. 업데이트 확인과 설치는 계속 사용할 수 있습니다.`})]}):null]}):null]})}function ni({action:e,actionError:t,data:n,error:r,firmware:i,loading:a,message:o,onCheck:s,onDismissFeedback:c,onInstall:l,onRetry:u,onSaveSettings:d}){return L(`section`,{class:`min-w-0 space-y-5`,children:[L(`div`,{children:L(V,{eyebrow:`Firmware`,title:`시스템 펌웨어`,description:`공유기 운영체제의 새 버전을 확인하고 설치합니다.`})}),L(Rr,{action:i.action,actionError:i.actionError,data:i.data,software:n,error:i.error,loading:i.loading,message:i.message,reconnecting:i.reconnecting,uploadProgress:i.uploadProgress,onCheck:()=>void i.check(),onDiscard:i.discard,onDismissFeedback:i.dismissFeedback,onInstall:i.install,onPrepare:()=>void i.prepare(),onRetry:()=>void i.refresh(),onUpload:i.upload}),L(`div`,{class:`pt-2`,children:L(V,{eyebrow:`Software`,title:`관리 소프트웨어`,description:`SmartSafeHub 관리 화면 및 서비스의 업데이트를 확인합니다.`})}),L(ti,{firmware:i.data,action:e,actionError:t,data:n,error:r,loading:a,message:o,onCheck:s,onDismissFeedback:c,onInstall:l,onRetry:u,onSaveSettings:d})]})}var ri={"#home":`home`,"#activity":`activity`,"#network":`network`,"#network/internet":`network`,"#network/lan":`network`,"#wan":`network`,"#lan":`network`,"#wifi":`wifi`,"#iptv":`iptv`,"#devices":`devices`,"#safeshield":`safeshield`,"#rules":`rules`,"#account":`account`,"#system":`system`,"#settings":`settings`,"#settings/diagnostics":`settings`,"#settings/time":`settings`,"#settings/management":`settings`};function ii(){return ri[window.location.hash]??`home`}function ai(){let[e,t]=F(ii);return I(()=>{let e=()=>t(ii());return window.addEventListener(`hashchange`,e),()=>window.removeEventListener(`hashchange`,e)},[]),e}function oi(e){return e===`#lan`||e===`#network/lan`?`lan`:`internet`}function si(e){return e===`#settings/time`?`time`:e===`#settings/management`?`management`:`diagnostics`}function ci(){let e=window.location.hash;return{network:oi(e),settings:si(e)}}function li(){let[e,t]=F(ci);return I(()=>{let e=()=>t(ci());return window.addEventListener(`hashchange`,e),()=>window.removeEventListener(`hashchange`,e)},[]),e}function ui({active:e,label:t,tabs:n}){return L(`nav`,{"aria-label":t,class:`mb-5 min-w-0`,role:`tablist`,children:L(`div`,{class:`grid w-full min-w-0 gap-2 ${n.length===3?`grid-cols-3 sm:max-w-[592px]`:`grid-cols-2 sm:max-w-[392px]`}`,children:n.map(t=>{let r=t.id===e;return L(`a`,{"aria-controls":`${t.id}-panel`,"aria-selected":r,class:`flex min-h-11 min-w-0 items-center justify-center rounded-xl border px-2 py-2.5 text-center text-[11px] font-extrabold leading-5 transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-teal-500 sm:px-4 sm:text-sm ${r?`border-teal-200 bg-teal-50 text-teal-800`:`border-slate-200 bg-white text-slate-600 hover:border-teal-200 hover:text-teal-700`}`,href:t.href,id:`${t.id}-tab`,role:`tab`,tabIndex:r?0:-1,onKeyDown:e=>{if(![`ArrowLeft`,`ArrowRight`,`Home`,`End`].includes(e.key))return;e.preventDefault();let r=n.findIndex(e=>e.id===t.id),i=e.key===`Home`?0:e.key===`End`?n.length-1:(r+(e.key===`ArrowRight`?1:-1)+n.length)%n.length,a=e.currentTarget.parentElement?.children[i];a?.focus(),a?.click()},children:t.label},t.id)})})})}var di=[{value:`all`,label:`전체`},{value:`online`,label:`현재 연결`},{value:`wifi`,label:`Wi-Fi`},{value:`ethernet`,label:`유선/기타`},{value:`offline`,label:`최근 기기`}],fi=new Intl.Collator(`ko`),pi=new Intl.DateTimeFormat(`ko-KR`,{month:`numeric`,day:`numeric`,hour:`2-digit`,minute:`2-digit`});function mi(e){switch(e){case`wifi`:return`Wi-Fi`;case`ethernet`:return`유선/기타`;default:return`연결 방식 미확인`}}function hi(e){return e.hostname??e.ipv4Address??`이름 없는 기기`}function gi(e){return e===null?null:e>=-50?`매우 좋음`:e>=-60?`좋음`:e>=-70?`보통`:`약함`}function _i(e){if(e===null||e<0)return null;let t=Math.floor(e/86400),n=Math.floor(e%86400/3600),r=Math.floor(e%3600/60);return t>0?`${t}일 ${n}시간`:n>0?`${n}시간 ${r}분`:`${Math.max(r,1)}분`}function vi(e){return e.leaseActive?e.leaseExpiresAt===null?`DHCP 임대 활성`:`DHCP ${pi.format(new Date(e.leaseExpiresAt*1e3))}까지`:null}function yi(e,t){switch(t){case`online`:return e.online;case`wifi`:return e.online&&e.connection===`wifi`;case`ethernet`:return e.online&&e.connection===`ethernet`;case`offline`:return!e.online;default:return!0}}function bi({device:e}){return L(`span`,{class:`inline-flex items-center gap-2 rounded-full px-3 py-1.5 text-xs font-extrabold ring-1 ring-inset ${e.online?`bg-emerald-50 text-emerald-700 ring-emerald-200`:`bg-slate-100 text-slate-600 ring-slate-200`}`,children:[L(`span`,{class:`size-2 rounded-full ${e.online?`bg-emerald-500`:`bg-slate-400`}`}),e.online?`현재 연결`:`최근 임대`]})}function xi({device:e}){let t=gi(e.signalDbm),n=_i(e.connectedSeconds),r=vi(e);return L(`article`,{class:`rounded-2xl border border-slate-200 bg-white p-4 shadow-sm shadow-slate-900/5 sm:p-5`,children:[L(`div`,{class:`flex min-w-0 flex-col items-start gap-4 sm:flex-row sm:justify-between`,children:[L(`div`,{class:`flex min-w-0 items-start gap-3`,children:[L(`span`,{class:`grid size-11 shrink-0 place-items-center rounded-xl ${e.connection===`wifi`?`bg-teal-50 text-teal-700`:e.connection===`ethernet`?`bg-blue-50 text-blue-700`:`bg-slate-100 text-slate-600`}`,children:e.connection===`wifi`?L(Xe,{class:`size-6`}):e.connection===`ethernet`?L(Qe,{class:`size-6`}):L(Ge,{class:`size-6`})}),L(`div`,{class:`min-w-0`,children:[L(`h3`,{class:`m-0 break-words text-base font-extrabold text-slate-950`,children:hi(e)}),L(`p`,{class:`mt-1 mb-0 break-all font-mono text-xs leading-5 text-slate-500`,children:e.mac})]})]}),L(`span`,{class:`shrink-0 self-start`,children:L(bi,{device:e})})]}),L(`dl`,{class:`mt-5 grid gap-3 text-sm sm:grid-cols-2`,children:[L(`div`,{class:`rounded-xl bg-slate-50 p-3.5`,children:[L(`dt`,{class:`text-xs font-bold text-slate-500`,children:`IP 주소`}),L(`dd`,{class:`mt-1.5 mb-0 ml-0 font-extrabold text-slate-900`,children:e.ipv4Address??`확인되지 않음`})]}),L(`div`,{class:`rounded-xl bg-slate-50 p-3.5`,children:[L(`dt`,{class:`text-xs font-bold text-slate-500`,children:`연결 방식`}),L(`dd`,{class:`mt-1.5 mb-0 ml-0 font-extrabold text-slate-900`,children:mi(e.connection)})]})]}),L(`div`,{class:`mt-4 flex flex-wrap gap-2 text-xs font-bold text-slate-600`,children:[e.ssid?L(`span`,{class:`rounded-full bg-teal-50 px-3 py-1.5 text-teal-800`,children:[e.ssid,e.bandLabel?` · ${e.bandLabel}`:``]}):null,t?L(`span`,{class:`rounded-full bg-slate-100 px-3 py-1.5`,children:[`신호 `,t,` · `,e.signalDbm,` dBm`]}):null,n?L(`span`,{class:`rounded-full bg-slate-100 px-3 py-1.5`,children:[`연결 `,n]}):null,r?L(`span`,{class:`rounded-full bg-slate-100 px-3 py-1.5`,children:r}):null,e.interface?L(`span`,{class:`rounded-full bg-slate-100 px-3 py-1.5`,children:e.interface}):null]})]})}function Si({data:e,error:t,loading:n,onRetry:r}){let[i,a]=F(``),[o,s]=F(`all`),c=Te(()=>{if(!e)return[];let t=i.trim().toLocaleLowerCase();return[...e.devices].filter(e=>yi(e,o)).filter(e=>!t||[e.hostname,e.ipv4Address,e.mac,e.ssid].filter(e=>!!e).some(e=>e.toLocaleLowerCase().includes(t))).sort((e,t)=>e.online===t.online?e.connection===t.connection?fi.compare(hi(e),hi(t)):e.connection===`wifi`?-1:1:e.online?-1:1)},[e,o,i]);return n?L(zn,{}):t?L(Bn,{message:t,onRetry:r}):e?L(v,{children:[L(V,{eyebrow:`Overview`,title:`연결 기기 현황`,description:`네트워크에 연결되었거나 최근 확인된 기기를 요약합니다.`}),L(`section`,{"aria-label":`연결 기기 요약`,class:`grid gap-4 sm:grid-cols-2 xl:grid-cols-4`,children:[L(Ci,{description:`${e.totals.offline}대는 최근 DHCP 임대에서 확인`,icon:L(Ge,{class:`size-6`}),label:`확인된 기기`,value:e.totals.known}),L(Ci,{description:`Wi-Fi 또는 ARP에서 현재 확인됨`,icon:L(tt,{class:`size-6`}),label:`현재 연결`,value:e.totals.online}),L(Ci,{description:`무선 AP에 연결된 기기`,icon:L(Xe,{class:`size-6`}),label:`Wi-Fi`,value:e.totals.wireless}),L(Ci,{description:`LAN ARP에서 확인된 비무선 기기`,icon:L(Qe,{class:`size-6`}),label:`유선/기타`,value:e.totals.ethernet})]}),L(`section`,{class:`mt-7`,"aria-label":`기기 목록 및 필터`,children:[L(V,{eyebrow:`Device inventory`,title:`기기 목록`,description:`연결된 기기를 검색하고 무선·유선 상태별로 확인합니다.`}),L(`div`,{class:`rounded-2xl border border-slate-200 bg-white p-4 shadow-sm shadow-slate-900/5 sm:p-5`,children:L(`div`,{class:`flex min-w-0 flex-col gap-3 lg:flex-row lg:items-center lg:justify-between`,children:[L(`div`,{class:`ssh-touch-scroll flex min-w-0 gap-2 overflow-x-auto pb-1 lg:flex-1`,role:`group`,"aria-label":`기기 필터`,children:di.map(e=>L(`button`,{"aria-pressed":o===e.value,class:`min-h-10 shrink-0 rounded-xl border px-4 py-2 text-sm font-extrabold transition ${o===e.value?`border-teal-700 bg-teal-700 text-white`:`border-slate-200 bg-white text-slate-600 hover:bg-slate-50 hover:text-slate-950`}`,onClick:()=>s(e.value),type:`button`,children:e.label},e.value))}),L(`label`,{class:`relative block w-full lg:w-72 lg:shrink-0 xl:w-80`,children:[L(`span`,{class:`sr-only`,children:`기기 검색`}),L(`span`,{class:`pointer-events-none absolute inset-y-0 left-0 flex w-11 items-center justify-center text-slate-400`,children:L(pt,{class:`size-5`})}),L(`input`,{class:`min-h-11 w-full rounded-xl border-2 border-slate-300 bg-slate-50 py-2.5 pr-4 pl-11 text-sm font-semibold text-slate-950 shadow-inner outline-none transition placeholder:text-slate-400 focus:border-teal-500 focus:bg-white focus:ring-4 focus:ring-teal-100`,onInput:e=>a(e.currentTarget.value),placeholder:`이름, IP, MAC 또는 SSID 검색`,type:`search`,value:i})]})]})})]}),c.length?L(`section`,{"aria-live":`polite`,class:`mt-5 grid gap-4 lg:grid-cols-2`,children:c.map(e=>L(xi,{device:e},e.id))}):L(`section`,{class:`mt-5 rounded-2xl border border-dashed border-slate-300 bg-white p-5 text-center sm:p-8`,children:[L(Ge,{class:`mx-auto size-10 text-slate-400`}),L(`h2`,{class:`mt-4 mb-0 text-lg font-extrabold text-slate-950`,children:`조건에 맞는 기기가 없습니다`}),L(`p`,{class:`mt-2 mb-0 text-sm text-slate-600`,children:`검색어 또는 필터를 변경해 주세요.`})]}),L(`p`,{class:`mt-5 mb-0 rounded-xl bg-slate-100 px-4 py-3 text-xs leading-5 text-slate-600`,children:`유선/기타 상태는 ARP 테이블을 기준으로 하며, 최근 DHCP 임대는 기기가 연결 해제된 뒤에도 일정 시간 남을 수 있습니다.`})]}):null}function Ci({description:e,icon:t,label:n,value:r}){return L(`article`,{class:`rounded-2xl border border-slate-200 bg-white p-4 shadow-sm shadow-slate-900/5 sm:p-5`,children:[L(`div`,{class:`flex items-start justify-between gap-4`,children:[L(`div`,{children:[L(`p`,{class:`m-0 text-xs font-bold uppercase tracking-[0.14em] text-slate-500`,children:n}),L(`p`,{class:`mt-3 mb-0 text-3xl font-black tracking-tight text-slate-950`,children:r})]}),L(`span`,{class:`grid size-11 shrink-0 place-items-center rounded-xl bg-teal-50 text-teal-700`,children:t})]}),L(`p`,{class:`mt-3 mb-0 text-xs leading-5 text-slate-500`,children:e})]})}function wi(e){return e+.5|0}var Ti=(e,t,n)=>Math.max(Math.min(e,n),t);function Ei(e){return Ti(wi(e*2.55),0,255)}function Di(e){return Ti(wi(e*255),0,255)}function Oi(e){return Ti(wi(e/2.55)/100,0,1)}function ki(e){return Ti(wi(e*100),0,100)}var Ai={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,A:10,B:11,C:12,D:13,E:14,F:15,a:10,b:11,c:12,d:13,e:14,f:15},ji=[...`0123456789ABCDEF`],Mi=e=>ji[e&15],Ni=e=>ji[(e&240)>>4]+ji[e&15],Pi=e=>(e&240)>>4==(e&15),Fi=e=>Pi(e.r)&&Pi(e.g)&&Pi(e.b)&&Pi(e.a);function Ii(e){var t=e.length,n;return e[0]===`#`&&(t===4||t===5?n={r:255&Ai[e[1]]*17,g:255&Ai[e[2]]*17,b:255&Ai[e[3]]*17,a:t===5?Ai[e[4]]*17:255}:(t===7||t===9)&&(n={r:Ai[e[1]]<<4|Ai[e[2]],g:Ai[e[3]]<<4|Ai[e[4]],b:Ai[e[5]]<<4|Ai[e[6]],a:t===9?Ai[e[7]]<<4|Ai[e[8]]:255})),n}var Li=(e,t)=>e<255?t(e):``;function Ri(e){var t=Fi(e)?Mi:Ni;return e?`#`+t(e.r)+t(e.g)+t(e.b)+Li(e.a,t):void 0}var zi=/^(hsla?|hwb|hsv)\(\s*([-+.e\d]+)(?:deg)?[\s,]+([-+.e\d]+)%[\s,]+([-+.e\d]+)%(?:[\s,]+([-+.e\d]+)(%)?)?\s*\)$/;function Bi(e,t,n){let r=t*Math.min(n,1-n),i=(t,i=(t+e/30)%12)=>n-r*Math.max(Math.min(i-3,9-i,1),-1);return[i(0),i(8),i(4)]}function Vi(e,t,n){let r=(r,i=(r+e/60)%6)=>n-n*t*Math.max(Math.min(i,4-i,1),0);return[r(5),r(3),r(1)]}function Hi(e,t,n){let r=Bi(e,1,.5),i;for(t+n>1&&(i=1/(t+n),t*=i,n*=i),i=0;i<3;i++)r[i]*=1-t-n,r[i]+=t;return r}function Ui(e,t,n,r,i){return e===i?(t-n)/r+(t.5?l/(2-i-a):l/(i+a),s=Ui(t,n,r,l,i),s=s*60+.5),[s|0,c||0,o]}function Gi(e,t,n,r){return(Array.isArray(t)?e(t[0],t[1],t[2]):e(t,n,r)).map(Di)}function Ki(e,t,n){return Gi(Bi,e,t,n)}function qi(e,t,n){return Gi(Hi,e,t,n)}function Ji(e,t,n){return Gi(Vi,e,t,n)}function Yi(e){return(e%360+360)%360}function Xi(e){let t=zi.exec(e),n=255,r;if(!t)return;t[5]!==r&&(n=t[6]?Ei(+t[5]):Di(+t[5]));let i=Yi(+t[2]),a=t[3]/100,o=t[4]/100;return r=t[1]===`hwb`?qi(i,a,o):t[1]===`hsv`?Ji(i,a,o):Ki(i,a,o),{r:r[0],g:r[1],b:r[2],a:n}}function Zi(e,t){var n=Wi(e);n[0]=Yi(n[0]+t),n=Ki(n),e.r=n[0],e.g=n[1],e.b=n[2]}function Qi(e){if(!e)return;let t=Wi(e),n=t[0],r=ki(t[1]),i=ki(t[2]);return e.a<255?`hsla(${n}, ${r}%, ${i}%, ${Oi(e.a)})`:`hsl(${n}, ${r}%, ${i}%)`}var $i={x:`dark`,Z:`light`,Y:`re`,X:`blu`,W:`gr`,V:`medium`,U:`slate`,A:`ee`,T:`ol`,S:`or`,B:`ra`,C:`lateg`,D:`ights`,R:`in`,Q:`turquois`,E:`hi`,P:`ro`,O:`al`,N:`le`,M:`de`,L:`yello`,F:`en`,K:`ch`,G:`arks`,H:`ea`,I:`ightg`,J:`wh`},ea={OiceXe:`f0f8ff`,antiquewEte:`faebd7`,aqua:`ffff`,aquamarRe:`7fffd4`,azuY:`f0ffff`,beige:`f5f5dc`,bisque:`ffe4c4`,black:`0`,blanKedOmond:`ffebcd`,Xe:`ff`,XeviTet:`8a2be2`,bPwn:`a52a2a`,burlywood:`deb887`,caMtXe:`5f9ea0`,KartYuse:`7fff00`,KocTate:`d2691e`,cSO:`ff7f50`,cSnflowerXe:`6495ed`,cSnsilk:`fff8dc`,crimson:`dc143c`,cyan:`ffff`,xXe:`8b`,xcyan:`8b8b`,xgTMnPd:`b8860b`,xWay:`a9a9a9`,xgYF:`6400`,xgYy:`a9a9a9`,xkhaki:`bdb76b`,xmagFta:`8b008b`,xTivegYF:`556b2f`,xSange:`ff8c00`,xScEd:`9932cc`,xYd:`8b0000`,xsOmon:`e9967a`,xsHgYF:`8fbc8f`,xUXe:`483d8b`,xUWay:`2f4f4f`,xUgYy:`2f4f4f`,xQe:`ced1`,xviTet:`9400d3`,dAppRk:`ff1493`,dApskyXe:`bfff`,dimWay:`696969`,dimgYy:`696969`,dodgerXe:`1e90ff`,fiYbrick:`b22222`,flSOwEte:`fffaf0`,foYstWAn:`228b22`,fuKsia:`ff00ff`,gaRsbSo:`dcdcdc`,ghostwEte:`f8f8ff`,gTd:`ffd700`,gTMnPd:`daa520`,Way:`808080`,gYF:`8000`,gYFLw:`adff2f`,gYy:`808080`,honeyMw:`f0fff0`,hotpRk:`ff69b4`,RdianYd:`cd5c5c`,Rdigo:`4b0082`,ivSy:`fffff0`,khaki:`f0e68c`,lavFMr:`e6e6fa`,lavFMrXsh:`fff0f5`,lawngYF:`7cfc00`,NmoncEffon:`fffacd`,ZXe:`add8e6`,ZcSO:`f08080`,Zcyan:`e0ffff`,ZgTMnPdLw:`fafad2`,ZWay:`d3d3d3`,ZgYF:`90ee90`,ZgYy:`d3d3d3`,ZpRk:`ffb6c1`,ZsOmon:`ffa07a`,ZsHgYF:`20b2aa`,ZskyXe:`87cefa`,ZUWay:`778899`,ZUgYy:`778899`,ZstAlXe:`b0c4de`,ZLw:`ffffe0`,lime:`ff00`,limegYF:`32cd32`,lRF:`faf0e6`,magFta:`ff00ff`,maPon:`800000`,VaquamarRe:`66cdaa`,VXe:`cd`,VScEd:`ba55d3`,VpurpN:`9370db`,VsHgYF:`3cb371`,VUXe:`7b68ee`,VsprRggYF:`fa9a`,VQe:`48d1cc`,VviTetYd:`c71585`,midnightXe:`191970`,mRtcYam:`f5fffa`,mistyPse:`ffe4e1`,moccasR:`ffe4b5`,navajowEte:`ffdead`,navy:`80`,Tdlace:`fdf5e6`,Tive:`808000`,TivedBb:`6b8e23`,Sange:`ffa500`,SangeYd:`ff4500`,ScEd:`da70d6`,pOegTMnPd:`eee8aa`,pOegYF:`98fb98`,pOeQe:`afeeee`,pOeviTetYd:`db7093`,papayawEp:`ffefd5`,pHKpuff:`ffdab9`,peru:`cd853f`,pRk:`ffc0cb`,plum:`dda0dd`,powMrXe:`b0e0e6`,purpN:`800080`,YbeccapurpN:`663399`,Yd:`ff0000`,Psybrown:`bc8f8f`,PyOXe:`4169e1`,saddNbPwn:`8b4513`,sOmon:`fa8072`,sandybPwn:`f4a460`,sHgYF:`2e8b57`,sHshell:`fff5ee`,siFna:`a0522d`,silver:`c0c0c0`,skyXe:`87ceeb`,UXe:`6a5acd`,UWay:`708090`,UgYy:`708090`,snow:`fffafa`,sprRggYF:`ff7f`,stAlXe:`4682b4`,tan:`d2b48c`,teO:`8080`,tEstN:`d8bfd8`,tomato:`ff6347`,Qe:`40e0d0`,viTet:`ee82ee`,JHt:`f5deb3`,wEte:`ffffff`,wEtesmoke:`f5f5f5`,Lw:`ffff00`,LwgYF:`9acd32`};function ta(){let e={},t=Object.keys(ea),n=Object.keys($i),r,i,a,o,s;for(r=0;r>16&255,a>>8&255,a&255]}return e}var na;function ra(e){na||(na=ta(),na.transparent=[0,0,0,0]);let t=na[e.toLowerCase()];return t&&{r:t[0],g:t[1],b:t[2],a:t.length===4?t[3]:255}}var ia=/^rgba?\(\s*([-+.\d]+)(%)?[\s,]+([-+.e\d]+)(%)?[\s,]+([-+.e\d]+)(%)?(?:[\s,/]+([-+.e\d]+)(%)?)?\s*\)$/;function aa(e){let t=ia.exec(e),n=255,r,i,a;if(t){if(t[7]!==r){let e=+t[7];n=t[8]?Ei(e):Ti(e*255,0,255)}return r=+t[1],i=+t[3],a=+t[5],r=255&(t[2]?Ei(r):Ti(r,0,255)),i=255&(t[4]?Ei(i):Ti(i,0,255)),a=255&(t[6]?Ei(a):Ti(a,0,255)),{r,g:i,b:a,a:n}}}function oa(e){return e&&(e.a<255?`rgba(${e.r}, ${e.g}, ${e.b}, ${Oi(e.a)})`:`rgb(${e.r}, ${e.g}, ${e.b})`)}var sa=e=>e<=.0031308?e*12.92:e**(1/2.4)*1.055-.055,ca=e=>e<=.04045?e/12.92:((e+.055)/1.055)**2.4;function la(e,t,n){let r=ca(Oi(e.r)),i=ca(Oi(e.g)),a=ca(Oi(e.b));return{r:Di(sa(r+n*(ca(Oi(t.r))-r))),g:Di(sa(i+n*(ca(Oi(t.g))-i))),b:Di(sa(a+n*(ca(Oi(t.b))-a))),a:e.a+n*(t.a-e.a)}}function ua(e,t,n){if(e){let r=Wi(e);r[t]=Math.max(0,Math.min(r[t]+r[t]*n,t===0?360:1)),r=Ki(r),e.r=r[0],e.g=r[1],e.b=r[2]}}function da(e,t){return e&&Object.assign(t||{},e)}function fa(e){var t={r:0,g:0,b:0,a:255};return Array.isArray(e)?e.length>=3&&(t={r:e[0],g:e[1],b:e[2],a:255},e.length>3&&(t.a=Di(e[3]))):(t=da(e,{r:0,g:0,b:0,a:1}),t.a=Di(t.a)),t}function pa(e){return e.charAt(0)===`r`?aa(e):Xi(e)}var ma=class e{constructor(t){if(t instanceof e)return t;let n=typeof t,r;n===`object`?r=fa(t):n===`string`&&(r=Ii(t)||ra(t)||pa(t)),this._rgb=r,this._valid=!!r}get valid(){return this._valid}get rgb(){var e=da(this._rgb);return e&&(e.a=Oi(e.a)),e}set rgb(e){this._rgb=fa(e)}rgbString(){return this._valid?oa(this._rgb):void 0}hexString(){return this._valid?Ri(this._rgb):void 0}hslString(){return this._valid?Qi(this._rgb):void 0}mix(e,t){if(e){let n=this.rgb,r=e.rgb,i,a=t===i?.5:t,o=2*a-1,s=n.a-r.a,c=((o*s===-1?o:(o+s)/(1+o*s))+1)/2;i=1-c,n.r=255&c*n.r+i*r.r+.5,n.g=255&c*n.g+i*r.g+.5,n.b=255&c*n.b+i*r.b+.5,n.a=a*n.a+(1-a)*r.a,this.rgb=n}return this}interpolate(e,t){return e&&(this._rgb=la(this._rgb,e._rgb,t)),this}clone(){return new e(this.rgb)}alpha(e){return this._rgb.a=Di(e),this}clearer(e){let t=this._rgb;return t.a*=1-e,this}greyscale(){let e=this._rgb;return e.r=e.g=e.b=wi(e.r*.3+e.g*.59+e.b*.11),this}opaquer(e){let t=this._rgb;return t.a*=1+e,this}negate(){let e=this._rgb;return e.r=255-e.r,e.g=255-e.g,e.b=255-e.b,this}lighten(e){return ua(this._rgb,2,e),this}darken(e){return ua(this._rgb,2,-e),this}saturate(e){return ua(this._rgb,1,e),this}desaturate(e){return ua(this._rgb,1,-e),this}rotate(e){return Zi(this._rgb,e),this}};function ha(){}var ga=(()=>{let e=0;return()=>e++})();function U(e){return e==null}function W(e){if(Array.isArray&&Array.isArray(e))return!0;let t=Object.prototype.toString.call(e);return t.slice(0,7)===`[object`&&t.slice(-6)===`Array]`}function G(e){return e!==null&&Object.prototype.toString.call(e)===`[object Object]`}function _a(e){return(typeof e==`number`||e instanceof Number)&&isFinite(+e)}function va(e,t){return _a(e)?e:t}function K(e,t){return e===void 0?t:e}var ya=(e,t)=>typeof e==`string`&&e.endsWith(`%`)?parseFloat(e)/100:+e/t,ba=(e,t)=>typeof e==`string`&&e.endsWith(`%`)?parseFloat(e)/100*t:+e;function q(e,t,n){if(e&&typeof e.call==`function`)return e.apply(n,t)}function J(e,t,n,r){let i,a,o;if(W(e)){if(a=e.length,r)for(i=a-1;i>=0;i--)t.call(n,e[i],i);else for(i=0;ie,x:e=>e.x,y:e=>e.y};function ka(e){let t=e.split(`.`),n=[],r=``;for(let e of t)r+=e,r.endsWith(`\\`)?r=r.slice(0,-1)+`.`:(n.push(r),r=``);return n}function Aa(e){let t=ka(e);return e=>{for(let n of t){if(n===``)break;e=e&&e[n]}return e}}function ja(e,t){return(Oa[t]||(Oa[t]=Aa(t)))(e)}function Ma(e){return e.charAt(0).toUpperCase()+e.slice(1)}var Na=e=>e!==void 0,Pa=e=>typeof e==`function`,Fa=(e,t)=>{if(e.size!==t.size)return!1;for(let n of e)if(!t.has(n))return!1;return!0};function Ia(e){return e.type===`mouseup`||e.type===`click`||e.type===`contextmenu`}var Y=Math.PI,X=2*Y,La=X+Y,Ra=1/0,za=Y/180,Z=Y/2,Ba=Y/4,Va=Y*2/3,Ha=Math.log10,Ua=Math.sign;function Wa(e,t,n){return Math.abs(e-t)e-t).pop(),t}function qa(e){return typeof e==`symbol`||typeof e==`object`&&!!e&&!(Symbol.toPrimitive in e||`toString`in e||`valueOf`in e)}function Ja(e){return!qa(e)&&!isNaN(parseFloat(e))&&isFinite(e)}function Ya(e,t){let n=Math.round(e);return n-t<=e&&n+t>=e}function Xa(e,t,n){let r,i,a;for(r=0,i=e.length;rc&&l=Math.min(t,n)-r&&e<=Math.max(t,n)+r}function co(e,t,n){n=n||(n=>e[n]1;)a=i+r>>1,n(a)?i=a:r=a;return{lo:i,hi:r}}var lo=(e,t,n,r)=>co(e,n,r?r=>{let i=e[r][t];return ie[r][t]co(e,n,r=>e[r][t]>=n);function fo(e,t,n){let r=0,i=e.length;for(;rr&&e[i-1]>n;)i--;return r>0||i{let n=`_onData`+Ma(t),r=e[t];Object.defineProperty(e,t,{configurable:!0,enumerable:!1,value(...t){let i=r.apply(this,t);return e._chartjs.listeners.forEach(e=>{typeof e[n]==`function`&&e[n](...t)}),i}})}))}function ho(e,t){let n=e._chartjs;if(!n)return;let r=n.listeners,i=r.indexOf(t);i!==-1&&r.splice(i,1),!(r.length>0)&&(po.forEach(t=>{delete e[t]}),delete e._chartjs)}function go(e){let t=new Set(e);return t.size===e.length?e:Array.from(t)}var _o=function(){return typeof window>`u`?function(e){return e()}:window.requestAnimationFrame}();function vo(e,t){let n=[],r=!1;return function(...i){n=i,r||(r=!0,_o.call(window,()=>{r=!1,e.apply(t,n)}))}}function yo(e,t){let n;return function(...r){return t?(clearTimeout(n),n=setTimeout(e,t,r)):e.apply(this,r),t}}var bo=e=>e===`start`?`left`:e===`end`?`right`:`center`,xo=(e,t,n)=>e===`start`?t:e===`end`?n:(t+n)/2;function So(e,t,n){let r=t.length,i=0,a=r;if(e._sorted){let{iScale:o,vScale:s,_parsed:c}=e,l=e.dataset&&e.dataset.options?e.dataset.options.spanGaps:null,u=o.axis,{min:d,max:f,minDefined:p,maxDefined:m}=o.getUserBounds();if(p){if(i=Math.min(lo(c,u,d).lo,n?r:lo(t,u,o.getPixelForValue(d)).lo),l){let e=c.slice(0,i+1).reverse().findIndex(e=>!U(e[s.axis]));i-=Math.max(0,e)}i=ao(i,0,r-1)}if(m){let e=Math.max(lo(c,o.axis,f,!0).hi+1,n?0:lo(t,u,o.getPixelForValue(f),!0).hi+1);if(l){let t=c.slice(e-1).findIndex(e=>!U(e[s.axis]));e+=Math.max(0,t)}a=ao(e,i,r)-i}else a=r-i}return{start:i,count:a}}function Co(e){let{xScale:t,yScale:n,_scaleRanges:r}=e,i={xmin:t.min,xmax:t.max,ymin:n.min,ymax:n.max};if(!r)return e._scaleRanges=i,!0;let a=r.xmin!==t.min||r.xmax!==t.max||r.ymin!==n.min||r.ymax!==n.max;return Object.assign(r,i),a}var wo=e=>e===0||e===1,To=(e,t,n)=>-(2**(10*--e)*Math.sin((e-t)*X/n)),Eo=(e,t,n)=>2**(-10*e)*Math.sin((e-t)*X/n)+1,Do={linear:e=>e,easeInQuad:e=>e*e,easeOutQuad:e=>-e*(e-2),easeInOutQuad:e=>(e/=.5)<1?.5*e*e:-.5*(--e*(e-2)-1),easeInCubic:e=>e*e*e,easeOutCubic:e=>--e*e*e+1,easeInOutCubic:e=>(e/=.5)<1?.5*e*e*e:.5*((e-=2)*e*e+2),easeInQuart:e=>e*e*e*e,easeOutQuart:e=>-(--e*e*e*e-1),easeInOutQuart:e=>(e/=.5)<1?.5*e*e*e*e:-.5*((e-=2)*e*e*e-2),easeInQuint:e=>e*e*e*e*e,easeOutQuint:e=>--e*e*e*e*e+1,easeInOutQuint:e=>(e/=.5)<1?.5*e*e*e*e*e:.5*((e-=2)*e*e*e*e+2),easeInSine:e=>-Math.cos(e*Z)+1,easeOutSine:e=>Math.sin(e*Z),easeInOutSine:e=>-.5*(Math.cos(Y*e)-1),easeInExpo:e=>e===0?0:2**(10*(e-1)),easeOutExpo:e=>e===1?1:-(2**(-10*e))+1,easeInOutExpo:e=>wo(e)?e:e<.5?.5*2**(10*(e*2-1)):.5*(-(2**(-10*(e*2-1)))+2),easeInCirc:e=>e>=1?e:-(Math.sqrt(1-e*e)-1),easeOutCirc:e=>Math.sqrt(1- --e*e),easeInOutCirc:e=>(e/=.5)<1?-.5*(Math.sqrt(1-e*e)-1):.5*(Math.sqrt(1-(e-=2)*e)+1),easeInElastic:e=>wo(e)?e:To(e,.075,.3),easeOutElastic:e=>wo(e)?e:Eo(e,.075,.3),easeInOutElastic(e){let t=.1125,n=.45;return wo(e)?e:e<.5?.5*To(e*2,t,n):.5+.5*Eo(e*2-1,t,n)},easeInBack(e){return e*e*(2.70158*e-1.70158)},easeOutBack(e){return--e*e*(2.70158*e+1.70158)+1},easeInOutBack(e){let t=1.70158;return(e/=.5)<1?.5*(e*e*(((t*=1.525)+1)*e-t)):.5*((e-=2)*e*(((t*=1.525)+1)*e+t)+2)},easeInBounce:e=>1-Do.easeOutBounce(1-e),easeOutBounce(e){let t=7.5625,n=2.75;return e<1/n?t*e*e:e<2/n?t*(e-=1.5/n)*e+.75:e<2.5/n?t*(e-=2.25/n)*e+.9375:t*(e-=2.625/n)*e+.984375},easeInOutBounce:e=>e<.5?Do.easeInBounce(e*2)*.5:Do.easeOutBounce(e*2-1)*.5+.5};function Oo(e){if(e&&typeof e==`object`){let t=e.toString();return t===`[object CanvasPattern]`||t===`[object CanvasGradient]`}return!1}function ko(e){return Oo(e)?e:new ma(e)}function Ao(e){return Oo(e)?e:new ma(e).saturate(.5).darken(.1).hexString()}var jo=[`x`,`y`,`borderWidth`,`radius`,`tension`],Mo=[`color`,`borderColor`,`backgroundColor`];function No(e){e.set(`animation`,{delay:void 0,duration:1e3,easing:`easeOutQuart`,fn:void 0,from:void 0,loop:void 0,to:void 0,type:void 0}),e.describe(`animation`,{_fallback:!1,_indexable:!1,_scriptable:e=>e!==`onProgress`&&e!==`onComplete`&&e!==`fn`}),e.set(`animations`,{colors:{type:`color`,properties:Mo},numbers:{type:`number`,properties:jo}}),e.describe(`animations`,{_fallback:`animation`}),e.set(`transitions`,{active:{animation:{duration:400}},resize:{animation:{duration:0}},show:{animations:{colors:{from:`transparent`},visible:{type:`boolean`,duration:0}}},hide:{animations:{colors:{to:`transparent`},visible:{type:`boolean`,easing:`linear`,fn:e=>e|0}}}})}function Po(e){e.set(`layout`,{autoPadding:!0,padding:{top:0,right:0,bottom:0,left:0}})}var Fo=new Map;function Io(e,t){t=t||{};let n=e+JSON.stringify(t),r=Fo.get(n);return r||(r=new Intl.NumberFormat(e,t),Fo.set(n,r)),r}function Lo(e,t,n){return Io(t,n).format(e)}var Ro={values(e){return W(e)?e:``+e},numeric(e,t,n){if(e===0)return`0`;let r=this.chart.options.locale,i,a=e;if(n.length>1){let t=Math.max(Math.abs(n[0].value),Math.abs(n[n.length-1].value));(t<1e-4||t>0x38d7ea4c68000)&&(i=`scientific`),a=zo(e,n)}let o=Ha(Math.abs(a)),s=isNaN(o)?1:Math.max(Math.min(-1*Math.floor(o),20),0),c={notation:i,minimumFractionDigits:s,maximumFractionDigits:s};return Object.assign(c,this.options.ticks.format),Lo(e,r,c)},logarithmic(e,t,n){if(e===0)return`0`;let r=n[t].significand||e/10**Math.floor(Ha(e));return[1,2,3,5,10,15].includes(r)||t>.8*n.length?Ro.numeric.call(this,e,t,n):``}};function zo(e,t){let n=t.length>3?t[2].value-t[1].value:t[1].value-t[0].value;return Math.abs(n)>=1&&e!==Math.floor(e)&&(n=e-Math.floor(e)),n}var Bo={formatters:Ro};function Vo(e){e.set(`scale`,{display:!0,offset:!1,reverse:!1,beginAtZero:!1,bounds:`ticks`,clip:!0,grace:0,grid:{display:!0,lineWidth:1,drawOnChartArea:!0,drawTicks:!0,tickLength:8,tickWidth:(e,t)=>t.lineWidth,tickColor:(e,t)=>t.color,offset:!1},border:{display:!0,dash:[],dashOffset:0,width:1},title:{display:!1,text:``,padding:{top:4,bottom:4}},ticks:{minRotation:0,maxRotation:50,mirror:!1,textStrokeWidth:0,textStrokeColor:``,padding:3,display:!0,autoSkip:!0,autoSkipPadding:3,labelOffset:0,callback:Bo.formatters.values,minor:{},major:{},align:`center`,crossAlign:`near`,showLabelBackdrop:!1,backdropColor:`rgba(255, 255, 255, 0.75)`,backdropPadding:2}}),e.route(`scale.ticks`,`color`,``,`color`),e.route(`scale.grid`,`color`,``,`borderColor`),e.route(`scale.border`,`color`,``,`borderColor`),e.route(`scale.title`,`color`,``,`color`),e.describe(`scale`,{_fallback:!1,_scriptable:e=>!e.startsWith(`before`)&&!e.startsWith(`after`)&&e!==`callback`&&e!==`parser`,_indexable:e=>e!==`borderDash`&&e!==`tickBorderDash`&&e!==`dash`}),e.describe(`scales`,{_fallback:`scale`}),e.describe(`scale.ticks`,{_scriptable:e=>e!==`backdropPadding`&&e!==`callback`,_indexable:e=>e!==`backdropPadding`})}var Ho=Object.create(null),Uo=Object.create(null);function Wo(e,t){if(!t)return e;let n=t.split(`.`);for(let t=0,r=n.length;te.chart.platform.getDevicePixelRatio(),this.elements={},this.events=[`mousemove`,`mouseout`,`click`,`touchstart`,`touchmove`],this.font={family:`'Helvetica Neue', 'Helvetica', 'Arial', sans-serif`,size:12,style:`normal`,lineHeight:1.2,weight:null},this.hover={},this.hoverBackgroundColor=(e,t)=>Ao(t.backgroundColor),this.hoverBorderColor=(e,t)=>Ao(t.borderColor),this.hoverColor=(e,t)=>Ao(t.color),this.indexAxis=`x`,this.interaction={mode:`nearest`,intersect:!0,includeInvisible:!1},this.maintainAspectRatio=!0,this.onHover=null,this.onClick=null,this.parsing=!0,this.plugins={},this.responsive=!0,this.scale=void 0,this.scales={},this.showLine=!0,this.drawActiveElementsOnTop=!0,this.describe(e),this.apply(t)}set(e,t){return Go(this,e,t)}get(e){return Wo(this,e)}describe(e,t){return Go(Uo,e,t)}override(e,t){return Go(Ho,e,t)}route(e,t,n,r){let i=Wo(this,e),a=Wo(this,n),o=`_`+t;Object.defineProperties(i,{[o]:{value:i[t],writable:!0},[t]:{enumerable:!0,get(){let e=this[o],t=a[r];return G(e)?Object.assign({},t,e):K(e,t)},set(e){this[o]=e}}})}apply(e){e.forEach(e=>e(this))}}({_scriptable:e=>!e.startsWith(`on`),_indexable:e=>e!==`events`,hover:{_fallback:`interaction`},interaction:{_scriptable:!1,_indexable:!1}},[No,Po,Vo]);function Ko(e){return!e||U(e.size)||U(e.family)?null:(e.style?e.style+` `:``)+(e.weight?e.weight+` `:``)+e.size+`px `+e.family}function qo(e,t,n,r,i){let a=t[i];return a||(a=t[i]=e.measureText(i).width,n.push(i)),a>r&&(r=a),r}function Jo(e,t,n,r){r=r||{};let i=r.data=r.data||{},a=r.garbageCollect=r.garbageCollect||[];r.font!==t&&(i=r.data={},a=r.garbageCollect=[],r.font=t),e.save(),e.font=t;let o=0,s=n.length,c,l,u,d,f;for(c=0;cn.length){for(c=0;c0&&e.stroke()}}function $o(e,t,n){return n=n||.5,!t||e&&e.x>t.left-n&&e.xt.top-n&&e.y0&&a.strokeColor!==``,c,l;for(e.save(),e.font=i.string,is(e,a),c=0;c+e||0;function ps(e,t){let n={},r=G(t),i=r?Object.keys(t):t,a=G(e)?r?n=>K(e[n],e[t[n]]):t=>e[t]:()=>e;for(let e of i)n[e]=fs(a(e));return n}function ms(e){return ps(e,{top:`y`,right:`x`,bottom:`y`,left:`x`})}function hs(e){return ps(e,[`topLeft`,`topRight`,`bottomLeft`,`bottomRight`])}function gs(e){let t=ms(e);return t.width=t.left+t.right,t.height=t.top+t.bottom,t}function _s(e,t){e=e||{},t=t||Q.font;let n=K(e.size,t.size);typeof n==`string`&&(n=parseInt(n,10));let r=K(e.style,t.style);r&&!(``+r).match(us)&&(console.warn(`Invalid font style specified: "`+r+`"`),r=void 0);let i={family:K(e.family,t.family),lineHeight:ds(K(e.lineHeight,t.lineHeight),n),size:n,style:r,weight:K(e.weight,t.weight),string:``};return i.string=Ko(i),i}function vs(e,t,n,r){let i=!0,a,o,s;for(a=0,o=e.length;an&&e===0?0:e+t;return{min:o(r,-Math.abs(a)),max:o(i,a)}}function bs(e,t){return Object.assign(Object.create(e),t)}function xs(e,t=[``],n,r,i=()=>e[0]){let a=n||e;return r===void 0&&(r=Ls(`_fallback`,e)),new Proxy({[Symbol.toStringTag]:`Object`,_cacheable:!0,_scopes:e,_rootScopes:a,_fallback:r,_getTarget:i,override:n=>xs([n,...e],t,a,r)},{deleteProperty(t,n){return delete t[n],delete t._keys,delete e[0][n],!0},get(n,r){return Es(n,r,()=>Is(r,t,e,n))},getOwnPropertyDescriptor(e,t){return Reflect.getOwnPropertyDescriptor(e._scopes[0],t)},getPrototypeOf(){return Reflect.getPrototypeOf(e[0])},has(e,t){return Rs(e).includes(t)},ownKeys(e){return Rs(e)},set(e,t,n){let r=e._storage||(e._storage=i());return e[t]=r[t]=n,delete e._keys,!0}})}function Ss(e,t,n,r){let i={_cacheable:!1,_proxy:e,_context:t,_subProxy:n,_stack:new Set,_descriptors:Cs(e,r),setContext:t=>Ss(e,t,n,r),override:i=>Ss(e.override(i),t,n,r)};return new Proxy(i,{deleteProperty(t,n){return delete t[n],delete e[n],!0},get(e,t,n){return Es(e,t,()=>Ds(e,t,n))},getOwnPropertyDescriptor(t,n){return t._descriptors.allKeys?Reflect.has(e,n)?{enumerable:!0,configurable:!0}:void 0:Reflect.getOwnPropertyDescriptor(e,n)},getPrototypeOf(){return Reflect.getPrototypeOf(e)},has(t,n){return Reflect.has(e,n)},ownKeys(){return Reflect.ownKeys(e)},set(t,n,r){return e[n]=r,delete t[n],!0}})}function Cs(e,t={scriptable:!0,indexable:!0}){let{_scriptable:n=t.scriptable,_indexable:r=t.indexable,_allKeys:i=t.allKeys}=e;return{allKeys:i,scriptable:n,indexable:r,isScriptable:Pa(n)?n:()=>n,isIndexable:Pa(r)?r:()=>r}}var ws=(e,t)=>e?e+Ma(t):t,Ts=(e,t)=>G(t)&&e!==`adapters`&&(Object.getPrototypeOf(t)===null||t.constructor===Object);function Es(e,t,n){if(Object.prototype.hasOwnProperty.call(e,t)||t===`constructor`)return e[t];let r=n();return e[t]=r,r}function Ds(e,t,n){let{_proxy:r,_context:i,_subProxy:a,_descriptors:o}=e,s=r[t];return Pa(s)&&o.isScriptable(t)&&(s=Os(t,s,e,n)),W(s)&&s.length&&(s=ks(t,s,e,o.isIndexable)),Ts(t,s)&&(s=Ss(s,i,a&&a[t],o)),s}function Os(e,t,n,r){let{_proxy:i,_context:a,_subProxy:o,_stack:s}=n;if(s.has(e))throw Error(`Recursion detected: `+Array.from(s).join(`->`)+`->`+e);s.add(e);let c=t(a,o||r);return s.delete(e),Ts(e,c)&&(c=Ns(i._scopes,i,e,c)),c}function ks(e,t,n,r){let{_proxy:i,_context:a,_subProxy:o,_descriptors:s}=n;if(a.index!==void 0&&r(e))return t[a.index%t.length];if(G(t[0])){let n=t,r=i._scopes.filter(e=>e!==n);t=[];for(let c of n){let n=Ns(r,i,e,c);t.push(Ss(n,a,o&&o[e],s))}}return t}function As(e,t,n){return Pa(e)?e(t,n):e}var js=(e,t)=>e===!0?t:typeof e==`string`?ja(t,e):void 0;function Ms(e,t,n,r,i){for(let a of t){let t=js(n,a);if(t){e.add(t);let a=As(t._fallback,n,i);if(a!==void 0&&a!==n&&a!==r)return a}else if(t===!1&&r!==void 0&&n!==r)return null}return!1}function Ns(e,t,n,r){let i=t._rootScopes,a=As(t._fallback,n,r),o=[...e,...i],s=new Set;s.add(r);let c=Ps(s,o,n,a||n,r);return c===null||a!==void 0&&a!==n&&(c=Ps(s,o,a,c,r),c===null)?!1:xs(Array.from(s),[``],i,a,()=>Fs(t,n,r))}function Ps(e,t,n,r,i){for(;n;)n=Ms(e,t,n,r,i);return n}function Fs(e,t,n){let r=e._getTarget();t in r||(r[t]={});let i=r[t];return W(i)&&G(n)?n:i||{}}function Is(e,t,n,r){let i;for(let a of t)if(i=Ls(ws(a,e),n),i!==void 0)return Ts(e,i)?Ns(n,r,e,i):i}function Ls(e,t){for(let n of t){if(!n)continue;let t=n[e];if(t!==void 0)return t}}function Rs(e){let t=e._keys;return t||(t=e._keys=zs(e._scopes)),t}function zs(e){let t=new Set;for(let n of e)for(let e of Object.keys(n).filter(e=>!e.startsWith(`_`)))t.add(e);return Array.from(t)}function Bs(e,t,n,r){let{iScale:i}=e,{key:a=`r`}=this._parsing,o=Array(r),s,c,l,u;for(s=0,c=r;ste===`x`?`y`:`x`;function Ws(e,t,n,r){let i=e.skip?t:e,a=t,o=n.skip?t:n,s=to(a,i),c=to(o,a),l=s/(s+c),u=c/(s+c);l=isNaN(l)?0:l,u=isNaN(u)?0:u;let d=r*l,f=r*u;return{previous:{x:a.x-d*(o.x-i.x),y:a.y-d*(o.y-i.y)},next:{x:a.x+f*(o.x-i.x),y:a.y+f*(o.y-i.y)}}}function Gs(e,t,n){let r=e.length,i,a,o,s,c,l=Hs(e,0);for(let u=0;u!e.skip)),t.cubicInterpolationMode===`monotone`)qs(e,i);else{let n=r?e[e.length-1]:e[0];for(a=0,o=e.length;ae.ownerDocument.defaultView.getComputedStyle(e,null);function tc(e,t){return ec(e).getPropertyValue(t)}var nc=[`top`,`right`,`bottom`,`left`];function rc(e,t,n){let r={};n=n?`-`+n:``;for(let i=0;i<4;i++){let a=nc[i];r[a]=parseFloat(e[t+`-`+a+n])||0}return r.width=r.left+r.right,r.height=r.top+r.bottom,r}var ic=(e,t,n)=>(e>0||t>0)&&(!n||!n.shadowRoot);function ac(e,t){let n=e.touches,r=n&&n.length?n[0]:e,{offsetX:i,offsetY:a}=r,o=!1,s,c;if(ic(i,a,e.target))s=i,c=a;else{let e=t.getBoundingClientRect();s=r.clientX-e.left,c=r.clientY-e.top,o=!0}return{x:s,y:c,box:o}}function oc(e,t){if(`native`in e)return e;let{canvas:n,currentDevicePixelRatio:r}=t,i=ec(n),a=i.boxSizing===`border-box`,o=rc(i,`padding`),s=rc(i,`border`,`width`),{x:c,y:l,box:u}=ac(e,n),d=o.left+(u&&s.left),f=o.top+(u&&s.top),{width:p,height:m}=t;return a&&(p-=o.width+s.width,m-=o.height+s.height),{x:Math.round((c-d)/p*n.width/r),y:Math.round((l-f)/m*n.height/r)}}function sc(e,t,n){let r,i;if(t===void 0||n===void 0){let a=e&&Qs(e);if(!a)t=e.clientWidth,n=e.clientHeight;else{let e=a.getBoundingClientRect(),o=ec(a),s=rc(o,`border`,`width`),c=rc(o,`padding`);t=e.width-c.width-s.width,n=e.height-c.height-s.height,r=$s(o.maxWidth,a,`clientWidth`),i=$s(o.maxHeight,a,`clientHeight`)}}return{width:t,height:n,maxWidth:r||Ra,maxHeight:i||Ra}}var cc=e=>Math.round(e*10)/10;function lc(e,t,n,r){let i=ec(e),a=rc(i,`margin`),o=$s(i.maxWidth,e,`clientWidth`)||Ra,s=$s(i.maxHeight,e,`clientHeight`)||Ra,c=sc(e,t,n),{width:l,height:u}=c;if(i.boxSizing===`content-box`){let e=rc(i,`border`,`width`),t=rc(i,`padding`);l-=t.width+e.width,u-=t.height+e.height}return l=Math.max(0,l-a.width),u=Math.max(0,r?l/r:u-a.height),l=cc(Math.min(l,o,c.maxWidth)),u=cc(Math.min(u,s,c.maxHeight)),l&&!u&&(u=cc(l/2)),(t!==void 0||n!==void 0)&&r&&c.height&&u>c.height&&(u=c.height,l=cc(Math.floor(u*r))),{width:l,height:u}}function uc(e,t,n){let r=t||1,i=cc(e.height*r),a=cc(e.width*r);e.height=cc(e.height),e.width=cc(e.width);let o=e.canvas;return o.style&&(n||!o.style.height&&!o.style.width)&&(o.style.height=`${e.height}px`,o.style.width=`${e.width}px`),e.currentDevicePixelRatio!==r||o.height!==i||o.width!==a?(e.currentDevicePixelRatio=r,o.height=i,o.width=a,e.ctx.setTransform(r,0,0,r,0,0),!0):!1}var dc=function(){let e=!1;try{let t={get passive(){return e=!0,!1}};Zs()&&(window.addEventListener(`test`,null,t),window.removeEventListener(`test`,null,t))}catch{}return e}();function fc(e,t){let n=tc(e,t),r=n&&n.match(/^(\d+)(\.\d+)?px$/);return r?+r[1]:void 0}function pc(e,t,n,r){return{x:e.x+n*(t.x-e.x),y:e.y+n*(t.y-e.y)}}function mc(e,t,n,r){return{x:e.x+n*(t.x-e.x),y:r===`middle`?n<.5?e.y:t.y:r===`after`?n<1?e.y:t.y:n>0?t.y:e.y}}function hc(e,t,n,r){let i={x:e.cp2x,y:e.cp2y},a={x:t.cp1x,y:t.cp1y},o=pc(e,i,n),s=pc(i,a,n),c=pc(a,t,n);return pc(pc(o,s,n),pc(s,c,n),n)}var gc=function(e,t){return{x(n){return e+e+t-n},setWidth(e){t=e},textAlign(e){return e===`center`?e:e===`right`?`left`:`right`},xPlus(e,t){return e-t},leftForLtr(e,t){return e-t}}},_c=function(){return{x(e){return e},setWidth(e){},textAlign(e){return e},xPlus(e,t){return e+t},leftForLtr(e,t){return e}}};function vc(e,t,n){return e?gc(t,n):_c()}function yc(e,t){let n,r;(t===`ltr`||t===`rtl`)&&(n=e.canvas.style,r=[n.getPropertyValue(`direction`),n.getPropertyPriority(`direction`)],n.setProperty(`direction`,t,`important`),e.prevTextDirection=r)}function bc(e,t){t!==void 0&&(delete e.prevTextDirection,e.canvas.style.setProperty(`direction`,t[0],t[1]))}function xc(e){return e===`angle`?{between:io,compare:no,normalize:ro}:{between:so,compare:(e,t)=>e-t,normalize:e=>e}}function Sc({start:e,end:t,count:n,loop:r,style:i}){return{start:e%n,end:t%n,loop:r&&(t-e+1)%n===0,style:i}}function Cc(e,t,n){let{property:r,start:i,end:a}=n,{between:o,normalize:s}=xc(r),c=t.length,{start:l,end:u,loop:d}=e,f,p;if(d){for(l+=c,u+=c,f=0,p=c;fc(i,y,_)&&s(i,y)!==0,x=()=>s(a,_)===0||c(a,y,_),S=()=>h||b(),C=()=>!h||x();for(let e=u,n=u;e<=d;++e)v=t[e%o],!v.skip&&(_=l(v[r]),_!==y&&(h=c(_,i,a),g===null&&S()&&(g=s(_,i)===0?e:n),g!==null&&C()&&(m.push(Sc({start:g,end:e,loop:f,count:o,style:p})),g=null),n=e,y=_));return g!==null&&m.push(Sc({start:g,end:d,loop:f,count:o,style:p})),m}function Tc(e,t){let n=[],r=e.segments;for(let i=0;ii&&e[a%t].skip;)a--;return a%=t,{start:i,end:a}}function Dc(e,t,n,r){let i=e.length,a=[],o=t,s=e[t],c;for(c=t+1;c<=n;++c){let n=e[c%i];n.skip||n.stop?s.skip||(r=!1,a.push({start:t%i,end:(c-1)%i,loop:r}),t=o=n.stop?c:null):(o=c,s.skip&&(t=c)),s=n}return o!==null&&a.push({start:t%i,end:o%i,loop:r}),a}function Oc(e,t){let n=e.points,r=e.options.spanGaps,i=n.length;if(!i)return[];let a=!!e._loop,{start:o,end:s}=Ec(n,i,a,r);return r===!0?kc(e,[{start:o,end:s,loop:a}],n,t):kc(e,Dc(n,o,sr({chart:e,initial:t.initial,numSteps:a,currentStep:Math.min(n-t.start,a)}))}_refresh(){this._request||(this._running=!0,this._request=_o.call(window,()=>{this._update(),this._request=null,this._running&&this._refresh()}))}_update(e=Date.now()){let t=0;this._charts.forEach((n,r)=>{if(!n.running||!n.items.length)return;let i=n.items,a=i.length-1,o=!1,s;for(;a>=0;--a)s=i[a],s._active?(s._total>n.duration&&(n.duration=s._total),s.tick(e),o=!0):(i[a]=i[i.length-1],i.pop());o&&(r.draw(),this._notify(r,n,e,`progress`)),i.length||(n.running=!1,this._notify(r,n,e,`complete`),n.initial=!1),t+=i.length}),this._lastDate=e,t===0&&(this._running=!1)}_getAnims(e){let t=this._charts,n=t.get(e);return n||(n={running:!1,initial:!0,items:[],listeners:{complete:[],progress:[]}},t.set(e,n)),n}listen(e,t,n){this._getAnims(e).listeners[t].push(n)}add(e,t){t&&t.length&&this._getAnims(e).items.push(...t)}has(e){return this._getAnims(e).items.length>0}start(e){let t=this._charts.get(e);t&&(t.running=!0,t.start=Date.now(),t.duration=t.items.reduce((e,t)=>Math.max(e,t._duration),0),this._refresh())}running(e){if(!this._running)return!1;let t=this._charts.get(e);return!(!t||!t.running||!t.items.length)}stop(e){let t=this._charts.get(e);if(!t||!t.items.length)return;let n=t.items,r=n.length-1;for(;r>=0;--r)n[r].cancel();t.items=[],this._notify(e,t,Date.now(),`complete`)}remove(e){return this._charts.delete(e)}},Bc=`transparent`,Vc={boolean(e,t,n){return n>.5?t:e},color(e,t,n){let r=ko(e||Bc),i=r.valid&&ko(t||Bc);return i&&i.valid?i.mix(r,n).hexString():t},number(e,t,n){return e+(t-e)*n}},Hc=class{constructor(e,t,n,r){let i=t[n];r=vs([e.to,r,i,e.from]);let a=vs([e.from,i,r]);this._active=!0,this._fn=e.fn||Vc[e.type||typeof a],this._easing=Do[e.easing]||Do.linear,this._start=Math.floor(Date.now()+(e.delay||0)),this._duration=this._total=Math.floor(e.duration),this._loop=!!e.loop,this._target=t,this._prop=n,this._from=a,this._to=r,this._promises=void 0}active(){return this._active}update(e,t,n){if(this._active){this._notify(!1);let r=this._target[this._prop],i=n-this._start,a=this._duration-i;this._start=n,this._duration=Math.floor(Math.max(a,e.duration)),this._total+=i,this._loop=!!e.loop,this._to=vs([e.to,t,r,e.from]),this._from=vs([e.from,r,t])}}cancel(){this._active&&(this.tick(Date.now()),this._active=!1,this._notify(!1))}tick(e){let t=e-this._start,n=this._duration,r=this._prop,i=this._from,a=this._loop,o=this._to,s;this._active=i!==o&&(a||t1?2-s:s,s=this._easing(Math.min(1,Math.max(0,s))),this._target[r]=this._fn(i,o,s)):(this._target[r]=o,this._notify(!0))}wait(){let e=this._promises||(this._promises=[]);return new Promise((t,n)=>{e.push({res:t,rej:n})})}_notify(e){let t=e?`res`:`rej`,n=this._promises||[];for(let e=0;e{let i=e[r];if(!G(i))return;let a={};for(let e of t)a[e]=i[e];(W(i.properties)&&i.properties||[r]).forEach(e=>{(e===r||!n.has(e))&&n.set(e,a)})})}_animateOptions(e,t){let n=t.options,r=Gc(e,n);if(!r)return[];let i=this._createAnimations(r,n);return n.$shared&&Wc(e.options.$animations,n).then(()=>{e.options=n},()=>{}),i}_createAnimations(e,t){let n=this._properties,r=[],i=e.$animations||(e.$animations={}),a=Object.keys(t),o=Date.now(),s;for(s=a.length-1;s>=0;--s){let c=a[s];if(c.charAt(0)===`$`)continue;if(c===`options`){r.push(...this._animateOptions(e,t));continue}let l=t[c],u=i[c],d=n.get(c);if(u){if(d&&u.active()){u.update(d,l,o);continue}u.cancel()}!d||!d.duration?e[c]=l:(i[c]=u=new Hc(d,e,c,l),r.push(u))}return r}update(e,t){if(this._properties.size===0){Object.assign(e,t);return}let n=this._createAnimations(e,t);if(n.length)return zc.add(this._chart,n),!0}};function Wc(e,t){let n=[],r=Object.keys(t);for(let t=0;t0||!n&&t<0)return i.index}return null}function rl(e,t){let{chart:n,_cachedMeta:r}=e,i=n._stacks||(n._stacks={}),{iScale:a,vScale:o,index:s}=r,c=a.axis,l=o.axis,u=$c(a,o,r),d=t.length,f;for(let e=0;en[e].axis===t).shift()}function al(e,t){return bs(e,{active:!1,dataset:void 0,datasetIndex:t,index:t,mode:`default`,type:`dataset`})}function ol(e,t,n){return bs(e,{active:!1,dataIndex:t,parsed:void 0,raw:void 0,element:n,index:t,mode:`default`,type:`data`})}function sl(e,t){let n=e.controller.index,r=e.vScale&&e.vScale.axis;if(r){t=t||e._parsed;for(let e of t){let t=e._stacks;if(!t||t[r]===void 0||t[r][n]===void 0)return;delete t[r][n],t[r]._visualValues!==void 0&&t[r]._visualValues[n]!==void 0&&delete t[r]._visualValues[n]}}}var cl=e=>e===`reset`||e===`none`,ll=(e,t)=>t?e:Object.assign({},e),ul=(e,t,n)=>e&&!t.hidden&&t._stacked&&{keys:Yc(n,!0),values:null},dl=class{constructor(e,t){this.chart=e,this._ctx=e.ctx,this.index=t,this._cachedDataOpts={},this._cachedMeta=this.getMeta(),this._type=this._cachedMeta.type,this.options=void 0,this._parsing=!1,this._data=void 0,this._objectData=void 0,this._sharedOptions=void 0,this._drawStart=void 0,this._drawCount=void 0,this.enableOptionSharing=!1,this.supportsDecimation=!1,this.$context=void 0,this._syncList=[],this.datasetElementType=new.target.datasetElementType,this.dataElementType=new.target.dataElementType,this.initialize()}initialize(){let e=this._cachedMeta;this.configure(),this.linkScales(),e._stacked=Qc(e.vScale,e),this.addElements(),this.options.fill&&!this.chart.isPluginEnabled(`filler`)&&console.warn(`Tried to use the 'fill' option without the 'Filler' plugin enabled. Please import and register the 'Filler' plugin and make sure it is not disabled in the options`)}updateIndex(e){this.index!==e&&sl(this._cachedMeta),this.index=e}linkScales(){let e=this.chart,t=this._cachedMeta,n=this.getDataset(),r=(e,t,n,r)=>e===`x`?t:e===`r`?r:n,i=t.xAxisID=K(n.xAxisID,il(e,`x`)),a=t.yAxisID=K(n.yAxisID,il(e,`y`)),o=t.rAxisID=K(n.rAxisID,il(e,`r`)),s=t.indexAxis,c=t.iAxisID=r(s,i,a,o),l=t.vAxisID=r(s,a,i,o);t.xScale=this.getScaleForId(i),t.yScale=this.getScaleForId(a),t.rScale=this.getScaleForId(o),t.iScale=this.getScaleForId(c),t.vScale=this.getScaleForId(l)}getDataset(){return this.chart.data.datasets[this.index]}getMeta(){return this.chart.getDatasetMeta(this.index)}getScaleForId(e){return this.chart.scales[e]}_getOtherScale(e){let t=this._cachedMeta;return e===t.iScale?t.vScale:t.iScale}reset(){this._update(`reset`)}_destroy(){let e=this._cachedMeta;this._data&&ho(this._data,this),e._stacked&&sl(e)}_dataCheck(){let e=this.getDataset(),t=e.data||(e.data=[]),n=this._data;if(G(t)){let e=this._cachedMeta;this._data=Zc(t,e)}else if(n!==t){if(n){ho(n,this);let e=this._cachedMeta;sl(e),e._parsed=[]}t&&Object.isExtensible(t)&&mo(t,this),this._syncList=[],this._data=t}}addElements(){let e=this._cachedMeta;this._dataCheck(),this.datasetElementType&&(e.dataset=new this.datasetElementType)}buildOrUpdateElements(e){let t=this._cachedMeta,n=this.getDataset(),r=!1;this._dataCheck();let i=t._stacked;t._stacked=Qc(t.vScale,t),t.stack!==n.stack&&(r=!0,sl(t),t.stack=n.stack),this._resyncElements(e),(r||i!==t._stacked)&&(rl(this,t._parsed),t._stacked=Qc(t.vScale,t))}configure(){let e=this.chart.config,t=e.datasetScopeKeys(this._type),n=e.getOptionScopes(this.getDataset(),t,!0);this.options=e.createResolver(n,this.getContext()),this._parsing=this.options.parsing,this._cachedDataOpts={}}parse(e,t){let{_cachedMeta:n,_data:r}=this,{iScale:i,_stacked:a}=n,o=i.axis,s=e===0&&t===r.length||n._sorted,c=e>0&&n._parsed[e-1],l,u,d;if(this._parsing===!1)n._parsed=r,n._sorted=!0,d=r;else{d=W(r[e])?this.parseArrayData(n,r,e,t):G(r[e])?this.parseObjectData(n,r,e,t):this.parsePrimitiveData(n,r,e,t);let i=()=>u[o]===null||c&&u[o]t||u=0;--d)if(!p()){this.updateRangeFromParsed(c,e,f,s);break}}return c}getAllParsedValues(e){let t=this._cachedMeta._parsed,n=[],r,i,a;for(r=0,i=t.length;r=0&&ethis.getContext(n,r,t),u);return p.$shared&&(p.$shared=s,i[a]=Object.freeze(ll(p,s))),p}_resolveAnimations(e,t,n){let r=this.chart,i=this._cachedDataOpts,a=`animation-${t}`,o=i[a];if(o)return o;let s;if(r.options.animation!==!1){let r=this.chart.config,i=r.datasetAnimationScopeKeys(this._type,t),a=r.getOptionScopes(this.getDataset(),i);s=r.createResolver(a,this.getContext(e,n,t))}let c=new Uc(r,s&&s.animations);return s&&s._cacheable&&(i[a]=Object.freeze(c)),c}getSharedOptions(e){if(e.$shared)return this._sharedOptions||(this._sharedOptions=Object.assign({},e))}includeOptions(e,t){return!t||cl(e)||this.chart._animationsDisabled}_getSharedOptions(e,t){let n=this.resolveDataElementOptions(e,t),r=this._sharedOptions,i=this.getSharedOptions(n),a=this.includeOptions(t,i)||i!==r;return this.updateSharedOptions(i,t,n),{sharedOptions:i,includeOptions:a}}updateElement(e,t,n,r){cl(r)?Object.assign(e,n):this._resolveAnimations(t,r).update(e,n)}updateSharedOptions(e,t,n){e&&!cl(t)&&this._resolveAnimations(void 0,t).update(e,n)}_setStyle(e,t,n,r){e.active=r;let i=this.getStyle(t,r);this._resolveAnimations(t,n,r).update(e,{options:!r&&this.getSharedOptions(i)||i})}removeHoverStyle(e,t,n){this._setStyle(e,n,`active`,!1)}setHoverStyle(e,t,n){this._setStyle(e,n,`active`,!0)}_removeDatasetHoverStyle(){let e=this._cachedMeta.dataset;e&&this._setStyle(e,void 0,`active`,!1)}_setDatasetHoverStyle(){let e=this._cachedMeta.dataset;e&&this._setStyle(e,void 0,`active`,!0)}_resyncElements(e){let t=this._data,n=this._cachedMeta.data;for(let[e,t,n]of this._syncList)this[e](t,n);this._syncList=[];let r=n.length,i=t.length,a=Math.min(i,r);a&&this.parse(0,a),i>r?this._insertElements(r,i-r,e):i{for(e.length+=t,o=e.length-1;o>=a;o--)e[o]=e[o-t]};for(s(i),o=e;oe-t))}return e._cache.$bar}function pl(e){let t=e.iScale,n=fl(t,e.type),r=t._length,i,a,o,s,c=()=>{o!==32767&&o!==-32768&&(Na(s)&&(r=Math.min(r,Math.abs(o-s)||r)),s=o)};for(i=0,a=n.length;i0?i[e-1]:null,s=eMath.abs(s)&&(c=s,l=o),t[n.axis]=l,t._custom={barStart:c,barEnd:l,start:i,end:a,min:o,max:s}}function _l(e,t,n,r){return W(e)?gl(e,t,n,r):t[n.axis]=n.parse(e,r),t}function vl(e,t,n,r){let i=e.iScale,a=e.vScale,o=i.getLabels(),s=i===a,c=[],l,u,d,f;for(l=n,u=n+r;l=n?1:-1):Ua(e)}function xl(e){let t,n,r,i,a;return e.horizontal?(t=e.base>e.x,n=`left`,r=`right`):(t=e.basee.controller.options.grouped),i=n.options.stacked,a=[],o=this._cachedMeta.controller.getParsed(t),s=o&&o[n.axis],c=e=>{let t=e._parsed.find(e=>e[n.axis]===s),r=t&&t[e.vScale.axis];if(U(r)||isNaN(r))return!0};for(let n of r)if(!(t!==void 0&&c(n))&&((i===!1||a.indexOf(n.stack)===-1||i===void 0&&n.stack===void 0)&&a.push(n.stack),n.index===e))break;return a.length||a.push(void 0),a}_getStackCount(e){return this._getStacks(void 0,e).length}_getAxisCount(){return this._getAxis().length}getFirstScaleIdForIndexAxis(){let e=this.chart.scales,t=this.chart.options.indexAxis;return Object.keys(e).filter(n=>e[n].axis===t).shift()}_getAxis(){let e={},t=this.getFirstScaleIdForIndexAxis();for(let n of this.chart.data.datasets)e[K(this.chart.options.indexAxis===`x`?n.xAxisID:n.yAxisID,t)]=!0;return Object.keys(e)}_getStackIndex(e,t,n){let r=this._getStacks(e,n),i=t===void 0?-1:r.indexOf(t);return i===-1?r.length-1:i}_getRuler(){let e=this.options,t=this._cachedMeta,n=t.iScale,r=[],i,a;for(i=0,a=t.data.length;i=0;--n)t=Math.max(t,e[n].size(this.resolveDataElementOptions(n))/2);return t>0&&t}getLabelAndValue(e){let t=this._cachedMeta,n=this.chart.data.labels||[],{xScale:r,yScale:i}=t,a=this.getParsed(e),o=r.getLabelForValue(a.x),s=i.getLabelForValue(a.y),c=a._custom;return{label:n[e]||``,value:`(`+o+`, `+s+(c?`, `+c:``)+`)`}}update(e){let t=this._cachedMeta.data;this.updateElements(t,0,t.length,e)}updateElements(e,t,n,r){let i=r===`reset`,{iScale:a,vScale:o}=this._cachedMeta,{sharedOptions:s,includeOptions:c}=this._getSharedOptions(t,r),l=a.axis,u=o.axis;for(let d=t;dio(e,s,c,!0)?1:Math.max(t,t*n,r,r*n),m=(e,t,r)=>io(e,s,c,!0)?-1:Math.min(t,t*n,r,r*n),h=p(0,l,d),g=p(Z,u,f),_=m(Y,l,d),v=m(Y+Z,u,f);r=(h-_)/2,i=(g-v)/2,a=-(h+_)/2,o=-(g+v)/2}return{ratioX:r,ratioY:i,offsetX:a,offsetY:o}}var Al=class extends dl{constructor(e,t){super(e,t),this.enableOptionSharing=!0,this.innerRadius=void 0,this.outerRadius=void 0,this.offsetX=void 0,this.offsetY=void 0}linkScales(){}parse(e,t){let n=this.getDataset().data,r=this._cachedMeta;if(this._parsing===!1)r._parsed=n;else{let i=e=>+n[e];if(G(n[e])){let{key:e=`value`}=this._parsing;i=t=>+ja(n[t],e)}let a,o;for(a=e,o=e+t;a0&&!isNaN(e)?Math.abs(e)/t*X:0}getLabelAndValue(e){let t=this._cachedMeta,n=this.chart,r=n.data.labels||[],i=Lo(t._parsed[e],n.options.locale);return{label:r[e]||``,value:i}}getMaxBorderWidth(e){let t=0,n=this.chart,r,i,a,o,s;if(!e){for(r=0,i=n.data.datasets.length;re!==`spacing`,_indexable:e=>e!==`spacing`&&!e.startsWith(`borderDash`)&&!e.startsWith(`hoverBorderDash`)}),$(Al,`overrides`,{aspectRatio:1,plugins:{legend:{labels:{generateLabels(e){let t=e.data,{labels:{pointStyle:n,textAlign:r,color:i,useBorderRadius:a,borderRadius:o}}=e.legend.options;return t.labels.length&&t.datasets.length?t.labels.map((t,s)=>{let c=e.getDatasetMeta(0).controller.getStyle(s);return{text:t,fillStyle:c.backgroundColor,fontColor:i,hidden:!e.getDataVisibility(s),lineDash:c.borderDash,lineDashOffset:c.borderDashOffset,lineJoin:c.borderJoinStyle,lineWidth:c.borderWidth,strokeStyle:c.borderColor,textAlign:r,pointStyle:n,borderRadius:a&&(o||c.borderRadius),index:s}}):[]}},onClick(e,t,n){n.chart.toggleDataVisibility(t.index),n.chart.update()}}}});var jl=class extends dl{initialize(){this.enableOptionSharing=!0,this.supportsDecimation=!0,super.initialize()}update(e){let t=this._cachedMeta,{dataset:n,data:r=[],_dataset:i}=t,a=this.chart._animationsDisabled,{start:o,count:s}=So(t,r,a);this._drawStart=o,this._drawCount=s,Co(t)&&(o=0,s=r.length),n._chart=this.chart,n._datasetIndex=this.index,n._decimated=!!i._decimated,n.points=r;let c=this.resolveDatasetElementOptions(e);this.options.showLine||(c.borderWidth=0),c.segment=this.options.segment,this.updateElement(n,void 0,{animated:!a,options:c},e),this.updateElements(r,o,s,e)}updateElements(e,t,n,r){let i=r===`reset`,{iScale:a,vScale:o,_stacked:s,_dataset:c}=this._cachedMeta,{sharedOptions:l,includeOptions:u}=this._getSharedOptions(t,r),d=a.axis,f=o.axis,{spanGaps:p,segment:m}=this.options,h=Ja(p)?p:1/0,g=this.chart._animationsDisabled||i||r===`none`,_=t+n,v=e.length,y=t>0&&this.getParsed(t-1);for(let n=0;n=_){v.skip=!0;continue}let b=this.getParsed(n),x=U(b[f]),S=v[d]=a.getPixelForValue(b[d],n),C=v[f]=i||x?o.getBasePixel():o.getPixelForValue(s?this.applyStack(o,b,s):b[f],n);v.skip=isNaN(S)||isNaN(C)||x,v.stop=n>0&&Math.abs(b[d]-y[d])>h,m&&(v.parsed=b,v.raw=c.data[n]),u&&(v.options=l||this.resolveDataElementOptions(n,p.active?`active`:r)),g||this.updateElement(p,n,v,r),y=b}}getMaxOverflow(){let e=this._cachedMeta,t=e.dataset,n=t.options&&t.options.borderWidth||0,r=e.data||[];if(!r.length)return n;let i=r[0].size(this.resolveDataElementOptions(0)),a=r[r.length-1].size(this.resolveDataElementOptions(r.length-1));return Math.max(n,i,a)/2}draw(){let e=this._cachedMeta;e.dataset.updateControlPoints(this.chart.chartArea,e.iScale.axis),super.draw()}};$(jl,`id`,`line`),$(jl,`defaults`,{datasetElementType:`line`,dataElementType:`point`,showLine:!0,spanGaps:!1}),$(jl,`overrides`,{scales:{_index_:{type:`category`},_value_:{type:`linear`}}});var Ml=class extends dl{constructor(e,t){super(e,t),this.innerRadius=void 0,this.outerRadius=void 0}getLabelAndValue(e){let t=this._cachedMeta,n=this.chart,r=n.data.labels||[],i=Lo(t._parsed[e].r,n.options.locale);return{label:r[e]||``,value:i}}parseObjectData(e,t,n,r){return Bs.bind(this)(e,t,n,r)}update(e){let t=this._cachedMeta.data;this._updateRadius(),this.updateElements(t,0,t.length,e)}getMinMax(){let e=this._cachedMeta,t={min:1/0,max:-1/0};return e.data.forEach((e,n)=>{let r=this.getParsed(n).r;!isNaN(r)&&this.chart.getDataVisibility(n)&&(rt.max&&(t.max=r))}),t}_updateRadius(){let e=this.chart,t=e.chartArea,n=e.options,r=Math.min(t.right-t.left,t.bottom-t.top),i=Math.max(r/2,0),a=(i-Math.max(n.cutoutPercentage?i/100*n.cutoutPercentage:1,0))/e.getVisibleDatasetCount();this.outerRadius=i-a*this.index,this.innerRadius=this.outerRadius-a}updateElements(e,t,n,r){let i=r===`reset`,a=this.chart,o=a.options.animation,s=this._cachedMeta.rScale,c=s.xCenter,l=s.yCenter,u=s.getIndexAngle(0)-.5*Y,d=u,f,p=360/this.countVisibleElements();for(f=0;f{!isNaN(this.getParsed(n).r)&&this.chart.getDataVisibility(n)&&t++}),t}_computeAngle(e,t,n){return this.chart.getDataVisibility(e)?Za(this.resolveDataElementOptions(e,t).angle||n):0}};$(Ml,`id`,`polarArea`),$(Ml,`defaults`,{dataElementType:`arc`,animation:{animateRotate:!0,animateScale:!0},animations:{numbers:{type:`number`,properties:[`x`,`y`,`startAngle`,`endAngle`,`innerRadius`,`outerRadius`]}},indexAxis:`r`,startAngle:0}),$(Ml,`overrides`,{aspectRatio:1,plugins:{legend:{labels:{generateLabels(e){let t=e.data;if(t.labels.length&&t.datasets.length){let{labels:{pointStyle:n,color:r}}=e.legend.options;return t.labels.map((t,i)=>{let a=e.getDatasetMeta(0).controller.getStyle(i);return{text:t,fillStyle:a.backgroundColor,strokeStyle:a.borderColor,fontColor:r,lineWidth:a.borderWidth,pointStyle:n,hidden:!e.getDataVisibility(i),index:i}})}return[]}},onClick(e,t,n){n.chart.toggleDataVisibility(t.index),n.chart.update()}}},scales:{r:{type:`radialLinear`,angleLines:{display:!1},beginAtZero:!0,grid:{circular:!0},pointLabels:{display:!1},startAngle:0}}});var Nl=class extends Al{};$(Nl,`id`,`pie`),$(Nl,`defaults`,{cutout:0,rotation:0,circumference:360,radius:`100%`});var Pl=class extends dl{getLabelAndValue(e){let t=this._cachedMeta.vScale,n=this.getParsed(e);return{label:t.getLabels()[e],value:``+t.getLabelForValue(n[t.axis])}}parseObjectData(e,t,n,r){return Bs.bind(this)(e,t,n,r)}update(e){let t=this._cachedMeta,n=t.dataset,r=t.data||[],i=t.iScale.getLabels();if(n.points=r,e!==`resize`){let t=this.resolveDatasetElementOptions(e);this.options.showLine||(t.borderWidth=0);let a={_loop:!0,_fullLoop:i.length===r.length,options:t};this.updateElement(n,void 0,a,e)}this.updateElements(r,0,r.length,e)}updateElements(e,t,n,r){let i=this._cachedMeta.rScale,a=r===`reset`;for(let o=t;o0&&this.getParsed(t-1);for(let l=t;l0&&Math.abs(n[f]-v[f])>g,h&&(m.parsed=n,m.raw=c.data[l]),d&&(m.options=u||this.resolveDataElementOptions(l,t.active?`active`:r)),_||this.updateElement(t,l,m,r),v=n}this.updateSharedOptions(u,r,l)}getMaxOverflow(){let e=this._cachedMeta,t=e.data||[];if(!this.options.showLine){let e=0;for(let n=t.length-1;n>=0;--n)e=Math.max(e,t[n].size(this.resolveDataElementOptions(n))/2);return e>0&&e}let n=e.dataset,r=n.options&&n.options.borderWidth||0;if(!t.length)return r;let i=t[0].size(this.resolveDataElementOptions(0)),a=t[t.length-1].size(this.resolveDataElementOptions(t.length-1));return Math.max(r,i,a)/2}};$(Fl,`id`,`scatter`),$(Fl,`defaults`,{datasetElementType:!1,dataElementType:`point`,showLine:!1,fill:!1}),$(Fl,`overrides`,{interaction:{mode:`point`},scales:{x:{type:`linear`},y:{type:`linear`}}});function Il(){throw Error(`This method is not implemented: Check that a complete date adapter is provided.`)}var Ll={_date:class e{static override(t){Object.assign(e.prototype,t)}constructor(e){$(this,`options`,void 0),this.options=e||{}}init(){}formats(){return Il()}parse(){return Il()}format(){return Il()}add(){return Il()}diff(){return Il()}startOf(){return Il()}endOf(){return Il()}}};function Rl(e,t,n,r){let{controller:i,data:a,_sorted:o}=e,s=i._cachedMeta.iScale,c=e.dataset&&e.dataset.options?e.dataset.options.spanGaps:null;if(s&&t===s.axis&&t!==`r`&&o&&a.length){let o=s._reversePixels?uo:lo;if(!r){let r=o(a,t,n);if(c){let{vScale:t}=i._cachedMeta,{_parsed:n}=e,a=n.slice(0,r.lo+1).reverse().findIndex(e=>!U(e[t.axis]));r.lo-=Math.max(0,a);let o=n.slice(r.hi).findIndex(e=>!U(e[t.axis]));r.hi+=Math.max(0,o)}return r}if(i._sharedOptions){let e=a[0],r=typeof e.getRange==`function`&&e.getRange(t);if(r){let e=o(a,t,n-r),i=o(a,t,n+r);return{lo:e.lo,hi:i.hi}}}}return{lo:0,hi:a.length-1}}function zl(e,t,n,r,i){let a=e.getSortedVisibleDatasetMetas(),o=n[t];for(let e=0,n=a.length;e{e[o]&&e[o](t[n],i)&&(a.push({element:e,datasetIndex:r,index:c}),s=s||e.inRange(t.x,t.y,i))}),r&&!s?[]:a}var Kl={evaluateInteractionItems:zl,modes:{index(e,t,n,r){let i=oc(t,e),a=n.axis||`x`,o=n.includeInvisible||!1,s=n.intersect?Vl(e,i,a,r,o):Wl(e,i,a,!1,r,o),c=[];return s.length?(e.getSortedVisibleDatasetMetas().forEach(e=>{let t=s[0].index,n=e.data[t];n&&!n.skip&&c.push({element:n,datasetIndex:e.index,index:t})}),c):[]},dataset(e,t,n,r){let i=oc(t,e),a=n.axis||`xy`,o=n.includeInvisible||!1,s=n.intersect?Vl(e,i,a,r,o):Wl(e,i,a,!1,r,o);if(s.length>0){let t=s[0].datasetIndex,n=e.getDatasetMeta(t).data;s=[];for(let e=0;ee.pos===t)}function Yl(e,t){return e.filter(e=>ql.indexOf(e.pos)===-1&&e.box.axis===t)}function Xl(e,t){return e.sort((e,n)=>{let r=t?n:e,i=t?e:n;return r.weight===i.weight?r.index-i.index:r.weight-i.weight})}function Zl(e){let t=[],n,r,i,a,o,s;for(n=0,r=(e||[]).length;ne.box.fullSize),!0),r=Xl(Jl(t,`left`),!0),i=Xl(Jl(t,`right`)),a=Xl(Jl(t,`top`),!0),o=Xl(Jl(t,`bottom`)),s=Yl(t,`x`),c=Yl(t,`y`);return{fullSize:n,leftAndTop:r.concat(a),rightAndBottom:i.concat(c).concat(o).concat(s),chartArea:Jl(t,`chartArea`),vertical:r.concat(i).concat(c),horizontal:a.concat(o).concat(s)}}function tu(e,t,n,r){return Math.max(e[n],t[n])+Math.max(e[r],t[r])}function nu(e,t){e.top=Math.max(e.top,t.top),e.left=Math.max(e.left,t.left),e.bottom=Math.max(e.bottom,t.bottom),e.right=Math.max(e.right,t.right)}function ru(e,t,n,r){let{pos:i,box:a}=n,o=e.maxPadding;if(!G(i)){n.size&&(e[i]-=n.size);let t=r[n.stack]||{size:0,count:1};t.size=Math.max(t.size,n.horizontal?a.height:a.width),n.size=t.size/t.count,e[i]+=n.size}a.getPadding&&nu(o,a.getPadding());let s=Math.max(0,t.outerWidth-tu(o,e,`left`,`right`)),c=Math.max(0,t.outerHeight-tu(o,e,`top`,`bottom`)),l=s!==e.w,u=c!==e.h;return e.w=s,e.h=c,n.horizontal?{same:l,other:u}:{same:u,other:l}}function iu(e){let t=e.maxPadding;function n(n){let r=Math.max(t[n]-e[n],0);return e[n]+=r,r}e.y+=n(`top`),e.x+=n(`left`),n(`right`),n(`bottom`)}function au(e,t){let n=t.maxPadding;function r(e){let r={left:0,top:0,right:0,bottom:0};return e.forEach(e=>{r[e]=Math.max(t[e],n[e])}),r}return r(e?[`left`,`right`]:[`top`,`bottom`])}function ou(e,t,n,r){let i=[],a,o,s,c,l,u;for(a=0,o=e.length,l=0;a{typeof e.beforeLayout==`function`&&e.beforeLayout()});let u=c.reduce((e,t)=>t.box.options&&t.box.options.display===!1?e:e+1,0)||1,d=Object.freeze({outerWidth:t,outerHeight:n,padding:i,availableWidth:a,availableHeight:o,vBoxMaxWidth:a/2/u,hBoxMaxHeight:o/2}),f=Object.assign({},i);nu(f,gs(r));let p=Object.assign({maxPadding:f,w:a,h:o,x:i.left,y:i.top},i),m=$l(c.concat(l),d);ou(s.fullSize,p,d,m),ou(c,p,d,m),ou(l,p,d,m)&&ou(c,p,d,m),iu(p),cu(s.leftAndTop,p,d,m),p.x+=p.w,p.y+=p.h,cu(s.rightAndBottom,p,d,m),e.chartArea={left:p.left,top:p.top,right:p.left+p.w,bottom:p.top+p.h,height:p.h,width:p.w},J(s.chartArea,t=>{let n=t.box;Object.assign(n,e.chartArea),n.update(p.w,p.h,{left:0,top:0,right:0,bottom:0})})}},uu=class{acquireContext(e,t){}releaseContext(e){return!1}addEventListener(e,t,n){}removeEventListener(e,t,n){}getDevicePixelRatio(){return 1}getMaximumSize(e,t,n,r){return t=Math.max(0,t||e.width),n=n||e.height,{width:t,height:Math.max(0,r?Math.floor(t/r):n)}}isAttached(e){return!0}updateConfig(e){}},du=class extends uu{acquireContext(e){return e&&e.getContext&&e.getContext(`2d`)||null}updateConfig(e){e.options.animation=!1}},fu=`$chartjs`,pu={touchstart:`mousedown`,touchmove:`mousemove`,touchend:`mouseup`,pointerenter:`mouseenter`,pointerdown:`mousedown`,pointermove:`mousemove`,pointerup:`mouseup`,pointerleave:`mouseout`,pointerout:`mouseout`},mu=e=>e===null||e===``;function hu(e,t){let n=e.style,r=e.getAttribute(`height`),i=e.getAttribute(`width`);if(e[fu]={initial:{height:r,width:i,style:{display:n.display,height:n.height,width:n.width}}},n.display=n.display||`block`,n.boxSizing=n.boxSizing||`border-box`,mu(i)){let t=fc(e,`width`);t!==void 0&&(e.width=t)}if(mu(r)){if(e.style.height===``)e.height=e.width/(t||2);else{let t=fc(e,`height`);t!==void 0&&(e.height=t)}}return e}var gu=dc?{passive:!0}:!1;function _u(e,t,n){e&&e.addEventListener(t,n,gu)}function vu(e,t,n){e&&e.canvas&&e.canvas.removeEventListener(t,n,gu)}function yu(e,t){let n=pu[e.type]||e.type,{x:r,y:i}=oc(e,t);return{type:n,chart:t,native:e,x:r===void 0?null:r,y:i===void 0?null:i}}function bu(e,t){for(let n of e)if(n===t||n.contains(t))return!0}function xu(e,t,n){let r=e.canvas,i=new MutationObserver(e=>{let t=!1;for(let n of e)t=t||bu(n.addedNodes,r),t=t&&!bu(n.removedNodes,r);t&&n()});return i.observe(document,{childList:!0,subtree:!0}),i}function Su(e,t,n){let r=e.canvas,i=new MutationObserver(e=>{let t=!1;for(let n of e)t=t||bu(n.removedNodes,r),t=t&&!bu(n.addedNodes,r);t&&n()});return i.observe(document,{childList:!0,subtree:!0}),i}var Cu=new Map,wu=0;function Tu(){let e=window.devicePixelRatio;e!==wu&&(wu=e,Cu.forEach((t,n)=>{n.currentDevicePixelRatio!==e&&t()}))}function Eu(e,t){Cu.size||window.addEventListener(`resize`,Tu),Cu.set(e,t)}function Du(e){Cu.delete(e),Cu.size||window.removeEventListener(`resize`,Tu)}function Ou(e,t,n){let r=e.canvas,i=r&&Qs(r);if(!i)return;let a=vo((e,t)=>{let r=i.clientWidth;n(e,t),r{let t=e[0],n=t.contentRect.width,r=t.contentRect.height;(n!==0||r!==0)&&a(n,r)});return o.observe(i),Eu(e,a),o}function ku(e,t,n){n&&n.disconnect(),t===`resize`&&Du(e)}function Au(e,t,n){let r=e.canvas,i=vo(t=>{e.ctx!==null&&n(yu(t,e))},e);return _u(r,t,i),i}var ju=class extends uu{acquireContext(e,t){let n=e&&e.getContext&&e.getContext(`2d`);return n&&n.canvas===e?(hu(e,t),n):null}releaseContext(e){let t=e.canvas;if(!t[fu])return!1;let n=t[fu].initial;[`height`,`width`].forEach(e=>{let r=n[e];U(r)?t.removeAttribute(e):t.setAttribute(e,r)});let r=n.style||{};return Object.keys(r).forEach(e=>{t.style[e]=r[e]}),t.width=t.width,delete t[fu],!0}addEventListener(e,t,n){this.removeEventListener(e,t);let r=e.$proxies||(e.$proxies={});r[t]=({attach:xu,detach:Su,resize:Ou}[t]||Au)(e,t,n)}removeEventListener(e,t){let n=e.$proxies||(e.$proxies={}),r=n[t];r&&(({attach:ku,detach:ku,resize:ku}[t]||vu)(e,t,r),n[t]=void 0)}getDevicePixelRatio(){return window.devicePixelRatio}getMaximumSize(e,t,n,r){return lc(e,t,n,r)}isAttached(e){let t=e&&Qs(e);return!!(t&&t.isConnected)}};function Mu(e){return!Zs()||typeof OffscreenCanvas<`u`&&e instanceof OffscreenCanvas?du:ju}var Nu=class{constructor(){$(this,`x`,void 0),$(this,`y`,void 0),$(this,`active`,!1),$(this,`options`,void 0),$(this,`$animations`,void 0)}tooltipPosition(e){let{x:t,y:n}=this.getProps([`x`,`y`],e);return{x:t,y:n}}hasValue(){return Ja(this.x)&&Ja(this.y)}getProps(e,t){let n=this.$animations;if(!t||!n)return this;let r={};return e.forEach(e=>{r[e]=n[e]&&n[e].active()?n[e]._to:this[e]}),r}};$(Nu,`defaults`,{}),$(Nu,`defaultRoutes`,void 0);function Pu(e,t){let n=e.options.ticks,r=Fu(e),i=Math.min(n.maxTicksLimit||r,r),a=n.major.enabled?Lu(t):[],o=a.length,s=a[0],c=a[o-1],l=[];if(o>i)return Ru(t,l,a,o/i),l;let u=Iu(a,t,i);if(o>0){let e,n,r=o>1?Math.round((c-s)/(o-1)):null;for(zu(t,l,u,U(r)?0:s-r,s),e=0,n=o-1;ei)return t}return Math.max(i,1)}function Lu(e){let t=[],n,r;for(n=0,r=e.length;ne===`left`?`right`:e===`right`?`left`:e,Hu=(e,t,n)=>t===`top`||t===`left`?e[t]+n:e[t]-n,Uu=(e,t)=>Math.min(t||e,e);function Wu(e,t){let n=[],r=e.length/t,i=e.length,a=0;for(;ao+s)))return c}function Ku(e,t){J(e,e=>{let n=e.gc,r=n.length/2,i;if(r>t){for(i=0;in?n:t,n=r&&t>n?t:n,{min:va(t,va(n,t)),max:va(n,va(t,n))}}getPadding(){return{left:this.paddingLeft||0,top:this.paddingTop||0,right:this.paddingRight||0,bottom:this.paddingBottom||0}}getTicks(){return this.ticks}getLabels(){let e=this.chart.data;return this.options.labels||(this.isHorizontal()?e.xLabels:e.yLabels)||e.labels||[]}getLabelItems(e=this.chart.chartArea){return this._labelItems||(this._labelItems=this._computeLabelItems(e))}beforeLayout(){this._cache={},this._dataLimitsCached=!1}beforeUpdate(){q(this.options.beforeUpdate,[this])}update(e,t,n){let{beginAtZero:r,grace:i,ticks:a}=this.options,o=a.sampleSize;this.beforeUpdate(),this.maxWidth=e,this.maxHeight=t,this._margins=n=Object.assign({left:0,right:0,top:0,bottom:0},n),this.ticks=null,this._labelSizes=null,this._gridLineItems=null,this._labelItems=null,this.beforeSetDimensions(),this.setDimensions(),this.afterSetDimensions(),this._maxLength=this.isHorizontal()?this.width+n.left+n.right:this.height+n.top+n.bottom,this._dataLimitsCached||(this.beforeDataLimits(),this.determineDataLimits(),this.afterDataLimits(),this._range=ys(this,i,r),this._dataLimitsCached=!0),this.beforeBuildTicks(),this.ticks=this.buildTicks()||[],this.afterBuildTicks();let s=o=i||n<=1||!this.isHorizontal()){this.labelRotation=r;return}let l=this._getLabelSizes(),u=l.widest.width,d=l.highest.height,f=ao(this.chart.width-u,0,this.maxWidth);o=e.offset?this.maxWidth/n:f/(n-1),u+6>o&&(o=f/(n-(e.offset?.5:1)),s=this.maxHeight-qu(e.grid)-t.padding-Ju(e.title,this.chart.options.font),c=Math.sqrt(u*u+d*d),a=Qa(Math.min(Math.asin(ao((l.highest.height+6)/o,-1,1)),Math.asin(ao(s/c,-1,1))-Math.asin(ao(d/c,-1,1)))),a=Math.max(r,Math.min(i,a))),this.labelRotation=a}afterCalculateLabelRotation(){q(this.options.afterCalculateLabelRotation,[this])}afterAutoSkip(){}beforeFit(){q(this.options.beforeFit,[this])}fit(){let e={width:0,height:0},{chart:t,options:{ticks:n,title:r,grid:i}}=this,a=this._isVisible(),o=this.isHorizontal();if(a){let a=Ju(r,t.options.font);if(o?(e.width=this.maxWidth,e.height=qu(i)+a):(e.height=this.maxHeight,e.width=qu(i)+a),n.display&&this.ticks.length){let{first:t,last:r,widest:i,highest:a}=this._getLabelSizes(),s=n.padding*2,c=Za(this.labelRotation),l=Math.cos(c),u=Math.sin(c);if(o){let t=n.mirror?0:u*i.width+l*a.height;e.height=Math.min(this.maxHeight,e.height+t+s)}else{let t=n.mirror?0:l*i.width+u*a.height;e.width=Math.min(this.maxWidth,e.width+t+s)}this._calculatePadding(t,r,u,l)}}this._handleMargins(),o?(this.width=this._length=t.width-this._margins.left-this._margins.right,this.height=e.height):(this.width=e.width,this.height=this._length=t.height-this._margins.top-this._margins.bottom)}_calculatePadding(e,t,n,r){let{ticks:{align:i,padding:a},position:o}=this.options,s=this.labelRotation!==0,c=o!==`top`&&this.axis===`x`;if(this.isHorizontal()){let o=this.getPixelForTick(0)-this.left,l=this.right-this.getPixelForTick(this.ticks.length-1),u=0,d=0;s?c?(u=r*e.width,d=n*t.height):(u=n*e.height,d=r*t.width):i===`start`?d=t.width:i===`end`?u=e.width:i!==`inner`&&(u=e.width/2,d=t.width/2),this.paddingLeft=Math.max((u-o+a)*this.width/(this.width-o),0),this.paddingRight=Math.max((d-l+a)*this.width/(this.width-l),0)}else{let n=t.height/2,r=e.height/2;i===`start`?(n=0,r=e.height):i===`end`&&(n=t.height,r=0),this.paddingTop=n+a,this.paddingBottom=r+a}}_handleMargins(){this._margins&&(this._margins.left=Math.max(this.paddingLeft,this._margins.left),this._margins.top=Math.max(this.paddingTop,this._margins.top),this._margins.right=Math.max(this.paddingRight,this._margins.right),this._margins.bottom=Math.max(this.paddingBottom,this._margins.bottom))}afterFit(){q(this.options.afterFit,[this])}isHorizontal(){let{axis:e,position:t}=this.options;return t===`top`||t===`bottom`||e===`x`}isFullSize(){return this.options.fullSize}_convertTicksToLabels(e){this.beforeTickToLabelConversion(),this.generateTickLabels(e);let t,n;for(t=0,n=e.length;t({width:a[e]||0,height:o[e]||0});return{first:C(0),last:C(t-1),widest:C(x),highest:C(S),widths:a,heights:o}}getLabelForValue(e){return e}getPixelForValue(e,t){return NaN}getValueForPixel(e){}getPixelForTick(e){let t=this.ticks;return e<0||e>t.length-1?null:this.getPixelForValue(t[e].value)}getPixelForDecimal(e){this._reversePixels&&(e=1-e);let t=this._startPixel+e*this._length;return oo(this._alignToPixels?Yo(this.chart,t,0):t)}getDecimalForPixel(e){let t=(e-this._startPixel)/this._length;return this._reversePixels?1-t:t}getBasePixel(){return this.getPixelForValue(this.getBaseValue())}getBaseValue(){let{min:e,max:t}=this;return e<0&&t<0?t:e>0&&t>0?e:0}getContext(e){let t=this.ticks||[];if(e>=0&&eo*r?o/n:s/r:s*r0:!!e}_computeGridLineItems(e){let t=this.axis,n=this.chart,r=this.options,{grid:i,position:a,border:o}=r,s=i.offset,c=this.isHorizontal(),l=this.ticks.length+ +!!s,u=qu(i),d=[],f=o.setContext(this.getContext()),p=f.display?f.width:0,m=p/2,h=function(e){return Yo(n,e,p)},g,_,v,y,b,x,S,C,w,T,E,D;if(a===`top`)g=h(this.bottom),x=this.bottom-u,C=g-m,T=h(e.top)+m,D=e.bottom;else if(a===`bottom`)g=h(this.top),T=e.top,D=h(e.bottom)-m,x=g+m,C=this.top+u;else if(a===`left`)g=h(this.right),b=this.right-u,S=g-m,w=h(e.left)+m,E=e.right;else if(a===`right`)g=h(this.left),w=e.left,E=h(e.right)-m,b=g+m,S=this.left+u;else if(t===`x`){if(a===`center`)g=h((e.top+e.bottom)/2+.5);else if(G(a)){let e=Object.keys(a)[0],t=a[e];g=h(this.chart.scales[e].getPixelForValue(t))}T=e.top,D=e.bottom,x=g+m,C=x+u}else if(t===`y`){if(a===`center`)g=h((e.left+e.right)/2);else if(G(a)){let e=Object.keys(a)[0],t=a[e];g=h(this.chart.scales[e].getPixelForValue(t))}b=g-m,S=b-u,w=e.left,E=e.right}let O=K(r.ticks.maxTicksLimit,l),k=Math.max(1,Math.ceil(l/O));for(_=0;_0&&(a-=r/2)}f={left:a,top:i,width:r+t.width,height:n+t.height,color:e.backdropColor}}h.push({label:y,font:w,textOffset:D,options:{rotation:m,color:n,strokeColor:s,strokeWidth:l,textAlign:d,textBaseline:O,translation:[b,x],backdrop:f}})}return h}_getXAxisLabelAlignment(){let{position:e,ticks:t}=this.options;if(-Za(this.labelRotation))return e===`top`?`left`:`right`;let n=`center`;return t.align===`start`?n=`left`:t.align===`end`?n=`right`:t.align===`inner`&&(n=`inner`),n}_getYAxisLabelAlignment(e){let{position:t,ticks:{crossAlign:n,mirror:r,padding:i}}=this.options,a=this._getLabelSizes(),o=e+i,s=a.widest.width,c,l;return t===`left`?r?(l=this.right+i,n===`near`?c=`left`:n===`center`?(c=`center`,l+=s/2):(c=`right`,l+=s)):(l=this.right-o,n===`near`?c=`right`:n===`center`?(c=`center`,l-=s/2):(c=`left`,l=this.left)):t===`right`?r?(l=this.left+i,n===`near`?c=`right`:n===`center`?(c=`center`,l-=s/2):(c=`left`,l-=s)):(l=this.left+o,n===`near`?c=`left`:n===`center`?(c=`center`,l+=s/2):(c=`right`,l=this.right)):c=`right`,{textAlign:c,x:l}}_computeLabelArea(){if(this.options.ticks.mirror)return;let e=this.chart,t=this.options.position;if(t===`left`||t===`right`)return{top:0,left:this.left,bottom:e.height,right:this.right};if(t===`top`||t===`bottom`)return{top:this.top,left:0,bottom:this.bottom,right:e.width}}drawBackground(){let{ctx:e,options:{backgroundColor:t},left:n,top:r,width:i,height:a}=this;t&&(e.save(),e.fillStyle=t,e.fillRect(n,r,i,a),e.restore())}getLineWidthForValue(e){let t=this.options.grid;if(!this._isVisible()||!t.display)return 0;let n=this.ticks.findIndex(t=>t.value===e);return n>=0?t.setContext(this.getContext(n)).lineWidth:0}drawGrid(e){let t=this.options.grid,n=this.ctx,r=this._gridLineItems||(this._gridLineItems=this._computeGridLineItems(e)),i,a,o=(e,t,r)=>{r.width&&r.color&&(n.save(),n.lineWidth=r.width,n.strokeStyle=r.color,n.setLineDash(r.borderDash||[]),n.lineDashOffset=r.borderDashOffset,n.beginPath(),n.moveTo(e.x,e.y),n.lineTo(t.x,t.y),n.stroke(),n.restore())};if(t.display)for(i=0,a=r.length;i{this.draw(e)}}]:[{z:r,draw:e=>{this.drawBackground(),this.drawGrid(e),this.drawTitle()}},{z:i,draw:()=>{this.drawBorder()}},{z:n,draw:e=>{this.drawLabels(e)}}]}getMatchingVisibleMetas(e){let t=this.chart.getSortedVisibleDatasetMetas(),n=this.axis+`AxisID`,r=[],i,a;for(i=0,a=t.length;i{let r=n.split(`.`),i=r.pop(),a=[e].concat(r).join(`.`),o=t[n].split(`.`),s=o.pop(),c=o.join(`.`);Q.route(a,i,c,s)})}function rd(e){return`id`in e&&`defaults`in e}var id=new class{constructor(){this.controllers=new ed(dl,`datasets`,!0),this.elements=new ed(Nu,`elements`),this.plugins=new ed(Object,`plugins`),this.scales=new ed($u,`scales`),this._typedRegistries=[this.controllers,this.scales,this.elements]}add(...e){this._each(`register`,e)}remove(...e){this._each(`unregister`,e)}addControllers(...e){this._each(`register`,e,this.controllers)}addElements(...e){this._each(`register`,e,this.elements)}addPlugins(...e){this._each(`register`,e,this.plugins)}addScales(...e){this._each(`register`,e,this.scales)}getController(e){return this._get(e,this.controllers,`controller`)}getElement(e){return this._get(e,this.elements,`element`)}getPlugin(e){return this._get(e,this.plugins,`plugin`)}getScale(e){return this._get(e,this.scales,`scale`)}removeControllers(...e){this._each(`unregister`,e,this.controllers)}removeElements(...e){this._each(`unregister`,e,this.elements)}removePlugins(...e){this._each(`unregister`,e,this.plugins)}removeScales(...e){this._each(`unregister`,e,this.scales)}_each(e,t,n){[...t].forEach(t=>{let r=n||this._getRegistryForType(t);n||r.isForType(t)||r===this.plugins&&t.id?this._exec(e,r,t):J(t,t=>{let r=n||this._getRegistryForType(t);this._exec(e,r,t)})})}_exec(e,t,n){let r=Ma(e);q(n[`before`+r],[],n),t[e](n),q(n[`after`+r],[],n)}_getRegistryForType(e){for(let t=0;te.filter(e=>!t.some(t=>e.plugin.id===t.plugin.id));this._notify(r(t,n),e,`stop`),this._notify(r(n,t),e,`start`)}};function od(e){let t={},n=[],r=Object.keys(id.plugins.items);for(let e=0;e1&&pd(e[0].toLowerCase());if(t)return t}throw Error(`Cannot determine type of '${e}' axis. Please provide 'axis' or 'position' option.`)}function gd(e,t,n){if(n[t+`AxisID`]===e)return{axis:t}}function _d(e,t){if(t.data&&t.data.datasets){let n=t.data.datasets.filter(t=>t.xAxisID===e||t.yAxisID===e);if(n.length)return gd(e,`x`,n[0])||gd(e,`y`,n[0])}return{}}function vd(e,t){let n=Ho[e.type]||{scales:{}},r=t.scales||{},i=ud(e.type,t),a=Object.create(null);return Object.keys(r).forEach(t=>{let o=r[t];if(!G(o))return console.error(`Invalid scale configuration for scale: ${t}`);if(o._proxy)return console.warn(`Ignoring resolver passed as options for scale: ${t}`);let s=hd(t,o,_d(t,e),Q.scales[o.type]),c=fd(s,i),l=n.scales||{};a[t]=Ea(Object.create(null),[{axis:s},o,l[s],l[c]])}),e.data.datasets.forEach(n=>{let i=n.type||e.type,o=n.indexAxis||ud(i,t),s=(Ho[i]||{}).scales||{};Object.keys(s).forEach(e=>{let t=dd(e,o),i=n[t+`AxisID`]||t;a[i]=a[i]||Object.create(null),Ea(a[i],[{axis:t},r[i],s[e]])})}),Object.keys(a).forEach(e=>{let t=a[e];Ea(t,[Q.scales[t.type],Q.scale])}),a}function yd(e){let t=e.options||(e.options={});t.plugins=K(t.plugins,{}),t.scales=vd(e,t)}function bd(e){return e=e||{},e.datasets=e.datasets||[],e.labels=e.labels||[],e}function xd(e){return e=e||{},e.data=bd(e.data),yd(e),e}var Sd=new Map,Cd=new Set;function wd(e,t){let n=Sd.get(e);return n||(n=t(),Sd.set(e,n),Cd.add(n)),n}var Td=(e,t,n)=>{let r=ja(t,n);r!==void 0&&e.add(r)},Ed=class{constructor(e){this._config=xd(e),this._scopeCache=new Map,this._resolverCache=new Map}get platform(){return this._config.platform}get type(){return this._config.type}set type(e){this._config.type=e}get data(){return this._config.data}set data(e){this._config.data=bd(e)}get options(){return this._config.options}set options(e){this._config.options=e}get plugins(){return this._config.plugins}update(){let e=this._config;this.clearCache(),yd(e)}clearCache(){this._scopeCache.clear(),this._resolverCache.clear()}datasetScopeKeys(e){return wd(e,()=>[[`datasets.${e}`,``]])}datasetAnimationScopeKeys(e,t){return wd(`${e}.transition.${t}`,()=>[[`datasets.${e}.transitions.${t}`,`transitions.${t}`],[`datasets.${e}`,``]])}datasetElementScopeKeys(e,t){return wd(`${e}-${t}`,()=>[[`datasets.${e}.elements.${t}`,`datasets.${e}`,`elements.${t}`,``]])}pluginScopeKeys(e){let t=e.id,n=this.type;return wd(`${n}-plugin-${t}`,()=>[[`plugins.${t}`,...e.additionalOptionScopes||[]]])}_cachedScopes(e,t){let n=this._scopeCache,r=n.get(e);return(!r||t)&&(r=new Map,n.set(e,r)),r}getOptionScopes(e,t,n){let{options:r,type:i}=this,a=this._cachedScopes(e,n),o=a.get(t);if(o)return o;let s=new Set;t.forEach(t=>{e&&(s.add(e),t.forEach(t=>Td(s,e,t))),t.forEach(e=>Td(s,r,e)),t.forEach(e=>Td(s,Ho[i]||{},e)),t.forEach(e=>Td(s,Q,e)),t.forEach(e=>Td(s,Uo,e))});let c=Array.from(s);return c.length===0&&c.push(Object.create(null)),Cd.has(t)&&a.set(t,c),c}chartOptionScopes(){let{options:e,type:t}=this;return[e,Ho[t]||{},Q.datasets[t]||{},{type:t},Q,Uo]}resolveNamedOptions(e,t,n,r=[``]){let i={$shared:!0},{resolver:a,subPrefixes:o}=Dd(this._resolverCache,e,r),s=a;if(kd(a,t)){i.$shared=!1,n=Pa(n)?n():n;let t=this.createResolver(e,n,o);s=Ss(a,n,t)}for(let e of t)i[e]=s[e];return i}createResolver(e,t,n=[``],r){let{resolver:i}=Dd(this._resolverCache,e,n);return G(t)?Ss(i,t,void 0,r):i}};function Dd(e,t,n){let r=e.get(t);r||(r=new Map,e.set(t,r));let i=n.join(),a=r.get(i);return a||(a={resolver:xs(t,n),subPrefixes:n.filter(e=>!e.toLowerCase().includes(`hover`))},r.set(i,a)),a}var Od=e=>G(e)&&Object.getOwnPropertyNames(e).some(t=>Pa(e[t]));function kd(e,t){let{isScriptable:n,isIndexable:r}=Cs(e);for(let i of t){let t=n(i),a=r(i),o=(a||t)&&e[i];if(t&&(Pa(o)||Od(o))||a&&W(o))return!0}return!1}var Ad=`4.5.1`,jd=[`top`,`bottom`,`left`,`right`,`chartArea`];function Md(e,t){return e===`top`||e===`bottom`||jd.indexOf(e)===-1&&t===`x`}function Nd(e,t){return function(n,r){return n[e]===r[e]?n[t]-r[t]:n[e]-r[e]}}function Pd(e){let t=e.chart,n=t.options.animation;t.notifyPlugins(`afterRender`),q(n&&n.onComplete,[e],t)}function Fd(e){let t=e.chart,n=t.options.animation;q(n&&n.onProgress,[e],t)}function Id(e){return Zs()&&typeof e==`string`?e=document.getElementById(e):e&&e.length&&(e=e[0]),e&&e.canvas&&(e=e.canvas),e}var Ld={},Rd=e=>{let t=Id(e);return Object.values(Ld).filter(e=>e.canvas===t).pop()};function zd(e,t,n){let r=Object.keys(e);for(let i of r){let r=+i;if(r>=t){let a=e[i];delete e[i],(n>0||r>t)&&(e[r+n]=a)}}}function Bd(e,t,n,r){return!n||e.type===`mouseout`?null:r?t:e}var Vd=class{static register(...e){id.add(...e),Hd()}static unregister(...e){id.remove(...e),Hd()}constructor(e,t){let n=this.config=new Ed(t),r=Id(e),i=Rd(r);if(i)throw Error(`Canvas is already in use. Chart with ID '`+i.id+`' must be destroyed before the canvas with ID '`+i.canvas.id+`' can be reused.`);let a=n.createResolver(n.chartOptionScopes(),this.getContext());this.platform=new(n.platform||(Mu(r))),this.platform.updateConfig(n);let o=this.platform.acquireContext(r,a.aspectRatio),s=o&&o.canvas,c=s&&s.height,l=s&&s.width;this.id=ga(),this.ctx=o,this.canvas=s,this.width=l,this.height=c,this._options=a,this._aspectRatio=this.aspectRatio,this._layers=[],this._metasets=[],this._stacks=void 0,this.boxes=[],this.currentDevicePixelRatio=void 0,this.chartArea=void 0,this._active=[],this._lastEvent=void 0,this._listeners={},this._responsiveListeners=void 0,this._sortedMetasets=[],this.scales={},this._plugins=new ad,this.$proxies={},this._hiddenIndices={},this.attached=!1,this._animationsDisabled=void 0,this.$context=void 0,this._doResize=yo(e=>this.update(e),a.resizeDelay||0),this._dataChanges=[],Ld[this.id]=this,!o||!s?console.error(`Failed to create chart: can't acquire context from the given item`):(zc.listen(this,`complete`,Pd),zc.listen(this,`progress`,Fd),this._initialize(),this.attached&&this.update())}get aspectRatio(){let{options:{aspectRatio:e,maintainAspectRatio:t},width:n,height:r,_aspectRatio:i}=this;return U(e)?t&&i?i:r?n/r:null:e}get data(){return this.config.data}set data(e){this.config.data=e}get options(){return this._options}set options(e){this.config.options=e}get registry(){return id}_initialize(){return this.notifyPlugins(`beforeInit`),this.options.responsive?this.resize():uc(this,this.options.devicePixelRatio),this.bindEvents(),this.notifyPlugins(`afterInit`),this}clear(){return Xo(this.canvas,this.ctx),this}stop(){return zc.stop(this),this}resize(e,t){zc.running(this)?this._resizeBeforeDraw={width:e,height:t}:this._resize(e,t)}_resize(e,t){let n=this.options,r=this.canvas,i=n.maintainAspectRatio&&this.aspectRatio,a=this.platform.getMaximumSize(r,e,t,i),o=n.devicePixelRatio||this.platform.getDevicePixelRatio(),s=this.width?`resize`:`attach`;this.width=a.width,this.height=a.height,this._aspectRatio=this.aspectRatio,uc(this,o,!0)&&(this.notifyPlugins(`resize`,{size:a}),q(n.onResize,[this,a],this),this.attached&&this._doResize(s)&&this.render())}ensureScalesHaveIDs(){J(this.options.scales||{},(e,t)=>{e.id=t})}buildOrUpdateScales(){let e=this.options,t=e.scales,n=this.scales,r=Object.keys(n).reduce((e,t)=>(e[t]=!1,e),{}),i=[];t&&(i=i.concat(Object.keys(t).map(e=>{let n=t[e],r=hd(e,n),i=r===`r`,a=r===`x`;return{options:n,dposition:i?`chartArea`:a?`bottom`:`left`,dtype:i?`radialLinear`:a?`category`:`linear`}}))),J(i,t=>{let i=t.options,a=i.id,o=hd(a,i),s=K(i.type,t.dtype);(i.position===void 0||Md(i.position,o)!==Md(t.dposition))&&(i.position=t.dposition),r[a]=!0;let c=null;a in n&&n[a].type===s?c=n[a]:(c=new(id.getScale(s))({id:a,type:s,ctx:this.ctx,chart:this}),n[c.id]=c),c.init(i,e)}),J(r,(e,t)=>{e||delete n[t]}),J(n,e=>{lu.configure(this,e,e.options),lu.addBox(this,e)})}_updateMetasets(){let e=this._metasets,t=this.data.datasets.length,n=e.length;if(e.sort((e,t)=>e.index-t.index),n>t){for(let e=t;et.length&&delete this._stacks,e.forEach((e,n)=>{t.filter(t=>t===e._dataset).length===0&&this._destroyDatasetMeta(n)})}buildOrUpdateControllers(){let e=[],t=this.data.datasets,n,r;for(this._removeUnreferencedMetasets(),n=0,r=t.length;n{this.getDatasetMeta(t).controller.reset()},this)}reset(){this._resetElements(),this.notifyPlugins(`reset`)}update(e){let t=this.config;t.update();let n=this._options=t.createResolver(t.chartOptionScopes(),this.getContext()),r=this._animationsDisabled=!n.animation;if(this._updateScales(),this._checkEventBindings(),this._updateHiddenIndices(),this._plugins.invalidate(),this.notifyPlugins(`beforeUpdate`,{mode:e,cancelable:!0})===!1)return;let i=this.buildOrUpdateControllers();this.notifyPlugins(`beforeElementsUpdate`);let a=0;for(let e=0,t=this.data.datasets.length;e{e.reset()}),this._updateDatasets(e),this.notifyPlugins(`afterUpdate`,{mode:e}),this._layers.sort(Nd(`z`,`_idx`));let{_active:o,_lastEvent:s}=this;s?this._eventHandler(s,!0):o.length&&this._updateHoverStyles(o,o,!0),this.render()}_updateScales(){J(this.scales,e=>{lu.removeBox(this,e)}),this.ensureScalesHaveIDs(),this.buildOrUpdateScales()}_checkEventBindings(){let e=this.options;(!Fa(new Set(Object.keys(this._listeners)),new Set(e.events))||!!this._responsiveListeners!==e.responsive)&&(this.unbindEvents(),this.bindEvents())}_updateHiddenIndices(){let{_hiddenIndices:e}=this,t=this._getUniformDataChanges()||[];for(let{method:n,start:r,count:i}of t)zd(e,r,n===`_removeElements`?-i:i)}_getUniformDataChanges(){let e=this._dataChanges;if(!e||!e.length)return;this._dataChanges=[];let t=this.data.datasets.length,n=t=>new Set(e.filter(e=>e[0]===t).map((e,t)=>t+`,`+e.splice(1).join(`,`))),r=n(0);for(let e=1;ee.split(`,`)).map(e=>({method:e[1],start:+e[2],count:+e[3]}))}_updateLayout(e){if(this.notifyPlugins(`beforeLayout`,{cancelable:!0})===!1)return;lu.update(this,this.width,this.height,e);let t=this.chartArea,n=t.width<=0||t.height<=0;this._layers=[],J(this.boxes,e=>{n&&e.position===`chartArea`||(e.configure&&e.configure(),this._layers.push(...e._layers()))},this),this._layers.forEach((e,t)=>{e._idx=t}),this.notifyPlugins(`afterLayout`)}_updateDatasets(e){if(this.notifyPlugins(`beforeDatasetsUpdate`,{mode:e,cancelable:!0})!==!1){for(let e=0,t=this.data.datasets.length;e=0;--t)this._drawDataset(e[t]);this.notifyPlugins(`afterDatasetsDraw`)}_drawDataset(e){let t=this.ctx,n={meta:e,index:e.index,cancelable:!0},r=Fc(this,e);this.notifyPlugins(`beforeDatasetDraw`,n)!==!1&&(r&&es(t,r),e.controller.draw(),r&&ts(t),n.cancelable=!1,this.notifyPlugins(`afterDatasetDraw`,n))}isPointInArea(e){return $o(e,this.chartArea,this._minPadding)}getElementsAtEventForMode(e,t,n,r){let i=Kl.modes[t];return typeof i==`function`?i(this,e,n,r):[]}getDatasetMeta(e){let t=this.data.datasets[e],n=this._metasets,r=n.filter(e=>e&&e._dataset===t).pop();return r||(r={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null,order:t&&t.order||0,index:e,_dataset:t,_parsed:[],_sorted:!1},n.push(r)),r}getContext(){return this.$context||(this.$context=bs(null,{chart:this,type:`chart`}))}getVisibleDatasetCount(){return this.getSortedVisibleDatasetMetas().length}isDatasetVisible(e){let t=this.data.datasets[e];if(!t)return!1;let n=this.getDatasetMeta(e);return typeof n.hidden==`boolean`?!n.hidden:!t.hidden}setDatasetVisibility(e,t){let n=this.getDatasetMeta(e);n.hidden=!t}toggleDataVisibility(e){this._hiddenIndices[e]=!this._hiddenIndices[e]}getDataVisibility(e){return!this._hiddenIndices[e]}_updateVisibility(e,t,n){let r=n?`show`:`hide`,i=this.getDatasetMeta(e),a=i.controller._resolveAnimations(void 0,r);Na(t)?(i.data[t].hidden=!n,this.update()):(this.setDatasetVisibility(e,n),a.update(i,{visible:n}),this.update(t=>t.datasetIndex===e?r:void 0))}hide(e,t){this._updateVisibility(e,t,!1)}show(e,t){this._updateVisibility(e,t,!0)}_destroyDatasetMeta(e){let t=this._metasets[e];t&&t.controller&&t.controller._destroy(),delete this._metasets[e]}_stop(){let e,t;for(this.stop(),zc.remove(this),e=0,t=this.data.datasets.length;e{t.addEventListener(this,n,r),e[n]=r},r=(e,t,n)=>{e.offsetX=t,e.offsetY=n,this._eventHandler(e)};J(this.options.events,e=>n(e,r))}bindResponsiveEvents(){this._responsiveListeners||(this._responsiveListeners={});let e=this._responsiveListeners,t=this.platform,n=(n,r)=>{t.addEventListener(this,n,r),e[n]=r},r=(n,r)=>{e[n]&&(t.removeEventListener(this,n,r),delete e[n])},i=(e,t)=>{this.canvas&&this.resize(e,t)},a,o=()=>{r(`attach`,o),this.attached=!0,this.resize(),n(`resize`,i),n(`detach`,a)};a=()=>{this.attached=!1,r(`resize`,i),this._stop(),this._resize(0,0),n(`attach`,o)},t.isAttached(this.canvas)?o():a()}unbindEvents(){J(this._listeners,(e,t)=>{this.platform.removeEventListener(this,t,e)}),this._listeners={},J(this._responsiveListeners,(e,t)=>{this.platform.removeEventListener(this,t,e)}),this._responsiveListeners=void 0}updateHoverStyle(e,t,n){let r=n?`set`:`remove`,i,a,o,s;for(t===`dataset`&&(i=this.getDatasetMeta(e[0].datasetIndex),i.controller[`_`+r+`DatasetHoverStyle`]()),o=0,s=e.length;o{let n=this.getDatasetMeta(e);if(!n)throw Error(`No dataset found at index `+e);return{datasetIndex:e,element:n.data[t],index:t}});xa(n,t)||(this._active=n,this._lastEvent=null,this._updateHoverStyles(n,t))}notifyPlugins(e,t,n){return this._plugins.notify(this,e,t,n)}isPluginEnabled(e){return this._plugins._cache.filter(t=>t.plugin.id===e).length===1}_updateHoverStyles(e,t,n){let r=this.options.hover,i=(e,t)=>e.filter(e=>!t.some(t=>e.datasetIndex===t.datasetIndex&&e.index===t.index)),a=i(t,e),o=n?e:i(e,t);a.length&&this.updateHoverStyle(a,r.mode,!1),o.length&&r.mode&&this.updateHoverStyle(o,r.mode,!0)}_eventHandler(e,t){let n={event:e,replay:t,cancelable:!0,inChartArea:this.isPointInArea(e)},r=t=>(t.options.events||this.options.events).includes(e.native.type);if(this.notifyPlugins(`beforeEvent`,n,r)===!1)return;let i=this._handleEvent(e,t,n.inChartArea);return n.cancelable=!1,this.notifyPlugins(`afterEvent`,n,r),(i||n.changed)&&this.render(),this}_handleEvent(e,t,n){let{_active:r=[],options:i}=this,a=t,o=this._getActiveElements(e,r,n,a),s=Ia(e),c=Bd(e,this._lastEvent,n,s);n&&(this._lastEvent=null,q(i.onHover,[e,o,this],this),s&&q(i.onClick,[e,o,this],this));let l=!xa(o,r);return(l||t)&&(this._active=o,this._updateHoverStyles(o,r,t)),this._lastEvent=c,l}_getActiveElements(e,t,n,r){if(e.type===`mouseout`)return[];if(!n)return t;let i=this.options.hover;return this.getElementsAtEventForMode(e,i.mode,i,r)}};$(Vd,`defaults`,Q),$(Vd,`instances`,Ld),$(Vd,`overrides`,Ho),$(Vd,`registry`,id),$(Vd,`version`,Ad),$(Vd,`getChart`,Rd);function Hd(){return J(Vd.instances,e=>e._plugins.invalidate())}function Ud(e,t,n){let{startAngle:r,x:i,y:a,outerRadius:o,innerRadius:s,options:c}=t,{borderWidth:l,borderJoinStyle:u}=c,d=Math.min(l/o,ro(r-n));if(e.beginPath(),e.arc(i,a,o-l/2,r+d/2,n-d/2),s>0){let t=Math.min(l/s,ro(r-n));e.arc(i,a,s+l/2,n-t/2,r+t/2,!0)}else{let t=Math.min(l/2,o*ro(r-n));if(u===`round`)e.arc(i,a,t,n-Y/2,r+Y/2,!0);else if(u===`bevel`){let o=2*t*t,s=-o*Math.cos(n+Y/2)+i,c=-o*Math.sin(n+Y/2)+a,l=o*Math.cos(r+Y/2)+i,u=o*Math.sin(r+Y/2)+a;e.lineTo(s,c),e.lineTo(l,u)}}e.closePath(),e.moveTo(0,0),e.rect(0,0,e.canvas.width,e.canvas.height),e.clip(`evenodd`)}function Wd(e,t,n){let{startAngle:r,pixelMargin:i,x:a,y:o,outerRadius:s,innerRadius:c}=t,l=i/s;e.beginPath(),e.arc(a,o,s,r-l,n+l),c>i?(l=i/c,e.arc(a,o,c,n+l,r-l,!0)):e.arc(a,o,i,n+Z,r-Z),e.closePath(),e.clip()}function Gd(e){return ps(e,[`outerStart`,`outerEnd`,`innerStart`,`innerEnd`])}function Kd(e,t,n,r){let i=Gd(e.options.borderRadius),a=(n-t)/2,o=Math.min(a,r*t/2),s=e=>{let t=(n-Math.min(a,e))*r/2;return ao(e,0,Math.min(a,t))};return{outerStart:s(i.outerStart),outerEnd:s(i.outerEnd),innerStart:ao(i.innerStart,0,o),innerEnd:ao(i.innerEnd,0,o)}}function qd(e,t,n,r){return{x:n+e*Math.cos(t),y:r+e*Math.sin(t)}}function Jd(e,t,n,r,i,a){let{x:o,y:s,startAngle:c,pixelMargin:l,innerRadius:u}=t,d=Math.max(t.outerRadius+r+n-l,0),f=u>0?u+r+n+l:0,p=0,m=i-c;if(r){let e=((u>0?u-r:0)+(d>0?d-r:0))/2;p=(m-(e===0?m:m*e/(e+r)))/2}let h=(m-Math.max(.001,m*d-n/Y)/d)/2,g=c+h+p,_=i-h-p,{outerStart:v,outerEnd:y,innerStart:b,innerEnd:x}=Kd(t,f,d,_-g),S=d-v,C=d-y,w=g+v/S,T=_-y/C,E=f+b,D=f+x,O=g+b/E,k=_-x/D;if(e.beginPath(),a){let t=(w+T)/2;if(e.arc(o,s,d,w,t),e.arc(o,s,d,t,T),y>0){let t=qd(C,T,o,s);e.arc(t.x,t.y,y,T,_+Z)}let n=qd(D,_,o,s);if(e.lineTo(n.x,n.y),x>0){let t=qd(D,k,o,s);e.arc(t.x,t.y,x,_+Z,k+Math.PI)}let r=(_-x/f+(g+b/f))/2;if(e.arc(o,s,f,_-x/f,r,!0),e.arc(o,s,f,r,g+b/f,!0),b>0){let t=qd(E,O,o,s);e.arc(t.x,t.y,b,O+Math.PI,g-Z)}let i=qd(S,g,o,s);if(e.lineTo(i.x,i.y),v>0){let t=qd(S,w,o,s);e.arc(t.x,t.y,v,g-Z,w)}}else{e.moveTo(o,s);let t=Math.cos(w)*d+o,n=Math.sin(w)*d+s;e.lineTo(t,n);let r=Math.cos(T)*d+o,i=Math.sin(T)*d+s;e.lineTo(r,i)}e.closePath()}function Yd(e,t,n,r,i){let{fullCircles:a,startAngle:o,circumference:s}=t,c=t.endAngle;if(a){Jd(e,t,n,r,c,i);for(let t=0;t=Y&&p===0&&u!==`miter`&&Ud(e,t,h),a||(Jd(e,t,n,r,h,i),e.stroke())}var Zd=class extends Nu{constructor(e){super(),$(this,`circumference`,void 0),$(this,`endAngle`,void 0),$(this,`fullCircles`,void 0),$(this,`innerRadius`,void 0),$(this,`outerRadius`,void 0),$(this,`pixelMargin`,void 0),$(this,`startAngle`,void 0),this.options=void 0,this.circumference=void 0,this.startAngle=void 0,this.endAngle=void 0,this.innerRadius=void 0,this.outerRadius=void 0,this.pixelMargin=0,this.fullCircles=0,e&&Object.assign(this,e)}inRange(e,t,n){let{angle:r,distance:i}=eo(this.getProps([`x`,`y`],n),{x:e,y:t}),{startAngle:a,endAngle:o,innerRadius:s,outerRadius:c,circumference:l}=this.getProps([`startAngle`,`endAngle`,`innerRadius`,`outerRadius`,`circumference`],n),u=(this.options.spacing+this.options.borderWidth)/2,d=K(l,o-a),f=io(r,a,o)&&a!==o,p=d>=X||f,m=so(i,s+u,c+u);return p&&m}getCenterPoint(e){let{x:t,y:n,startAngle:r,endAngle:i,innerRadius:a,outerRadius:o}=this.getProps([`x`,`y`,`startAngle`,`endAngle`,`innerRadius`,`outerRadius`],e),{offset:s,spacing:c}=this.options,l=(r+i)/2,u=(a+o+c+s)/2;return{x:t+Math.cos(l)*u,y:n+Math.sin(l)*u}}tooltipPosition(e){return this.getCenterPoint(e)}draw(e){let{options:t,circumference:n}=this,r=(t.offset||0)/4,i=(t.spacing||0)/2,a=t.circular;if(this.pixelMargin=t.borderAlign===`inner`?.33:0,this.fullCircles=n>X?Math.floor(n/X):0,n===0||this.innerRadius<0||this.outerRadius<0)return;e.save();let o=(this.startAngle+this.endAngle)/2;e.translate(Math.cos(o)*r,Math.sin(o)*r);let s=r*(1-Math.sin(Math.min(Y,n||0)));e.fillStyle=t.backgroundColor,e.strokeStyle=t.borderColor,Yd(e,this,s,i,a),Xd(e,this,s,i,a),e.restore()}};$(Zd,`id`,`arc`),$(Zd,`defaults`,{borderAlign:`center`,borderColor:`#fff`,borderDash:[],borderDashOffset:0,borderJoinStyle:void 0,borderRadius:0,borderWidth:2,offset:0,spacing:0,angle:void 0,circular:!0,selfJoin:!1}),$(Zd,`defaultRoutes`,{backgroundColor:`backgroundColor`}),$(Zd,`descriptors`,{_scriptable:!0,_indexable:e=>e!==`borderDash`});function Qd(e,t,n=t){e.lineCap=K(n.borderCapStyle,t.borderCapStyle),e.setLineDash(K(n.borderDash,t.borderDash)),e.lineDashOffset=K(n.borderDashOffset,t.borderDashOffset),e.lineJoin=K(n.borderJoinStyle,t.borderJoinStyle),e.lineWidth=K(n.borderWidth,t.borderWidth),e.strokeStyle=K(n.borderColor,t.borderColor)}function $d(e,t,n){e.lineTo(n.x,n.y)}function ef(e){return e.stepped?ns:e.tension||e.cubicInterpolationMode===`monotone`?rs:$d}function tf(e,t,n={}){let r=e.length,{start:i=0,end:a=r-1}=n,{start:o,end:s}=t,c=Math.max(i,o),l=Math.min(a,s),u=is&&a>s;return{count:r,start:c,loop:t.loop,ilen:l(o+(l?s-e:e))%a,y=()=>{h!==g&&(e.lineTo(u,g),e.lineTo(u,h),e.lineTo(u,_))};for(c&&(p=i[v(0)],e.moveTo(p.x,p.y)),f=0;f<=s;++f){if(p=i[v(f)],p.skip)continue;let t=p.x,n=p.y,r=t|0;r===m?(ng&&(g=n),u=(d*u+t)/++d):(y(),e.lineTo(t,n),m=r,d=0,h=g=n),_=n}y()}function af(e){let t=e.options,n=t.borderDash&&t.borderDash.length;return!e._decimated&&!e._loop&&!t.tension&&t.cubicInterpolationMode!==`monotone`&&!t.stepped&&!n?rf:nf}function of(e){return e.stepped?mc:e.tension||e.cubicInterpolationMode===`monotone`?hc:pc}function sf(e,t,n,r){let i=t._path;i||(i=t._path=new Path2D,t.path(i,n,r)&&i.closePath()),Qd(e,t.options),e.stroke(i)}function cf(e,t,n,r){let{segments:i,options:a}=t,o=af(t);for(let s of i)Qd(e,a,s.style),e.beginPath(),o(e,t,s,{start:n,end:n+r-1})&&e.closePath(),e.stroke()}var lf=typeof Path2D==`function`;function uf(e,t,n,r){lf&&!t.options.segment?sf(e,t,n,r):cf(e,t,n,r)}var df=class extends Nu{constructor(e){super(),this.animated=!0,this.options=void 0,this._chart=void 0,this._loop=void 0,this._fullLoop=void 0,this._path=void 0,this._points=void 0,this._segments=void 0,this._decimated=!1,this._pointsUpdated=!1,this._datasetIndex=void 0,e&&Object.assign(this,e)}updateControlPoints(e,t){let n=this.options;if((n.tension||n.cubicInterpolationMode===`monotone`)&&!n.stepped&&!this._pointsUpdated){let r=n.spanGaps?this._loop:this._fullLoop;Xs(this._points,n,e,r,t),this._pointsUpdated=!0}}set points(e){this._points=e,delete this._segments,delete this._path,this._pointsUpdated=!1}get points(){return this._points}get segments(){return this._segments||(this._segments=Oc(this,this.options.segment))}first(){let e=this.segments,t=this.points;return e.length&&t[e[0].start]}last(){let e=this.segments,t=this.points,n=e.length;return n&&t[e[n-1].end]}interpolate(e,t){let n=this.options,r=e[t],i=this.points,a=Tc(this,{property:t,start:r,end:r});if(!a.length)return;let o=[],s=of(n),c,l;for(c=0,l=a.length;ce!==`borderDash`&&e!==`fill`});function ff(e,t,n,r){let i=e.options,{[n]:a}=e.getProps([n],r);return Math.abs(t-a)e+t)/r.size,y:i/a}},nearest(e,t){if(!e.length)return!1;let n=t.x,r=t.y,i=1/0,a,o,s;for(a=0,o=e.length;a-1?e.split(` -`):e}function Ef(e,t){let{element:n,datasetIndex:r,index:i}=t,a=e.getDatasetMeta(r).controller,{label:o,value:s}=a.getLabelAndValue(i);return{chart:e,label:o,parsed:a.getParsed(i),raw:e.data.datasets[r].data[i],formattedValue:s,dataset:a.getDataset(),dataIndex:i,datasetIndex:r,element:n}}function Df(e,t){let n=e.chart.ctx,{body:r,footer:i,title:a}=e,{boxWidth:o,boxHeight:s}=t,c=gs(t.bodyFont),l=gs(t.titleFont),u=gs(t.footerFont),d=a.length,f=i.length,p=r.length,m=hs(t.padding),h=m.height,g=0,_=r.reduce((e,t)=>e+t.before.length+t.lines.length+t.after.length,0);if(_+=e.beforeBody.length+e.afterBody.length,d&&(h+=d*l.lineHeight+(d-1)*t.titleSpacing+t.titleMarginBottom),_){let e=t.displayColors?Math.max(s,c.lineHeight):c.lineHeight;h+=p*e+(_-p)*c.lineHeight+(_-1)*t.bodySpacing}f&&(h+=t.footerMarginTop+f*u.lineHeight+(f-1)*t.footerSpacing);let v=0,y=function(e){g=Math.max(g,n.measureText(e).width+v)};return n.save(),n.font=l.string,J(e.title,y),n.font=c.string,J(e.beforeBody.concat(e.afterBody),y),v=t.displayColors?o+2+t.boxPadding:0,J(r,e=>{J(e.before,y),J(e.lines,y),J(e.after,y)}),v=0,n.font=u.string,J(e.footer,y),n.restore(),g+=m.width,{width:g,height:h}}function Of(e,t){let{y:n,height:r}=t;return ne.height-r/2?`bottom`:`center`}function kf(e,t,n,r){let{x:i,width:a}=r,o=n.caretSize+n.caretPadding;if(e===`left`&&i+a+o>t.width||e===`right`&&i-a-o<0)return!0}function Af(e,t,n,r){let{x:i,width:a}=n,{width:o,chartArea:{left:s,right:c}}=e,l=`center`;return r===`center`?l=i<=(s+c)/2?`left`:`right`:i<=a/2?l=`left`:i>=o-a/2&&(l=`right`),kf(l,e,t,n)&&(l=`center`),l}function jf(e,t,n){let r=n.yAlign||t.yAlign||Of(e,n);return{xAlign:n.xAlign||t.xAlign||Af(e,t,n,r),yAlign:r}}function Mf(e,t){let{x:n,width:r}=e;return t===`right`?n-=r:t===`center`&&(n-=r/2),n}function Nf(e,t,n){let{y:r,height:i}=e;return t===`top`?r+=n:r-=t===`bottom`?i+n:i/2,r}function Pf(e,t,n,r){let{caretSize:i,caretPadding:a,cornerRadius:o}=e,{xAlign:s,yAlign:c}=n,l=i+a,{topLeft:u,topRight:d,bottomLeft:f,bottomRight:p}=ms(o),m=Mf(t,s),h=Nf(t,c,l);return c===`center`?s===`left`?m+=l:s===`right`&&(m-=l):s===`left`?m-=Math.max(u,f)+i:s===`right`&&(m+=Math.max(d,p)+i),{x:io(m,0,r.width-t.width),y:io(h,0,r.height-t.height)}}function Ff(e,t,n){let r=hs(n.padding);return t===`center`?e.x+e.width/2:t===`right`?e.x+e.width-r.right:e.x+r.left}function If(e){return wf([],Tf(e))}function Lf(e,t,n){return ys(e,{tooltip:t,tooltipItems:n,type:`tooltip`})}function Rf(e,t){let n=t&&t.dataset&&t.dataset.tooltip&&t.dataset.tooltip.callbacks;return n?e.override(n):e}var zf={beforeTitle:ma,title(e){if(e.length>0){let t=e[0],n=t.chart.data.labels,r=n?n.length:0;if(this&&this.options&&this.options.mode===`dataset`)return t.dataset.label||``;if(t.label)return t.label;if(r>0&&t.dataIndex{let t={before:[],lines:[],after:[]},i=Rf(n,e);wf(t.before,Tf(Bf(i,`beforeLabel`,this,e))),wf(t.lines,Bf(i,`label`,this,e)),wf(t.after,Tf(Bf(i,`afterLabel`,this,e))),r.push(t)}),r}getAfterBody(e,t){return If(Bf(t.callbacks,`afterBody`,this,e))}getFooter(e,t){let{callbacks:n}=t,r=Bf(n,`beforeFooter`,this,e),i=Bf(n,`footer`,this,e),a=Bf(n,`afterFooter`,this,e),o=[];return o=wf(o,Tf(r)),o=wf(o,Tf(i)),o=wf(o,Tf(a)),o}_createItems(e){let t=this._active,n=this.chart.data,r=[],i=[],a=[],o=[],s,c;for(s=0,c=t.length;se.filter(t,r,i,n))),e.itemSort&&(o=o.sort((t,r)=>e.itemSort(t,r,n))),J(o,t=>{let n=Rf(e.callbacks,t);r.push(Bf(n,`labelColor`,this,t)),i.push(Bf(n,`labelPointStyle`,this,t)),a.push(Bf(n,`labelTextColor`,this,t))}),this.labelColors=r,this.labelPointStyles=i,this.labelTextColors=a,this.dataPoints=o,o}update(e,t){let n=this.options.setContext(this.getContext()),r=this._active,i,a=[];if(!r.length)this.opacity!==0&&(i={opacity:0});else{let e=Cf[n.position].call(this,r,this._eventPosition);a=this._createItems(n),this.title=this.getTitle(a,n),this.beforeBody=this.getBeforeBody(a,n),this.body=this.getBody(a,n),this.afterBody=this.getAfterBody(a,n),this.footer=this.getFooter(a,n);let t=this._size=Df(this,n),o=Object.assign({},e,t),s=jf(this.chart,n,o),c=Pf(n,o,s,this.chart);this.xAlign=s.xAlign,this.yAlign=s.yAlign,i={opacity:1,x:c.x,y:c.y,width:t.width,height:t.height,caretX:e.x,caretY:e.y}}this._tooltipItems=a,this.$context=void 0,i&&this._resolveAnimations().update(this,i),e&&n.external&&n.external.call(this,{chart:this.chart,tooltip:this,replay:t})}drawCaret(e,t,n,r){let i=this.getCaretPosition(e,n,r);t.lineTo(i.x1,i.y1),t.lineTo(i.x2,i.y2),t.lineTo(i.x3,i.y3)}getCaretPosition(e,t,n){let{xAlign:r,yAlign:i}=this,{caretSize:a,cornerRadius:o}=n,{topLeft:s,topRight:c,bottomLeft:l,bottomRight:u}=ms(o),{x:d,y:f}=e,{width:p,height:m}=t,h,g,_,v,y,b;return i===`center`?(y=f+m/2,r===`left`?(h=d,g=h-a,v=y+a,b=y-a):(h=d+p,g=h+a,v=y-a,b=y+a),_=h):(g=r===`left`?d+Math.max(s,l)+a:r===`right`?d+p-Math.max(c,u)-a:this.caretX,i===`top`?(v=f,y=v-a,h=g-a,_=g+a):(v=f+m,y=v+a,h=g+a,_=g-a),b=v),{x1:h,x2:g,x3:_,y1:v,y2:y,y3:b}}drawTitle(e,t,n){let r=this.title,i=r.length,a,o,s;if(i){let c=_c(n.rtl,this.x,this.width);for(e.x=Ff(this,n.titleAlign,n),t.textAlign=c.textAlign(n.titleAlign),t.textBaseline=`middle`,a=gs(n.titleFont),o=n.titleSpacing,t.fillStyle=n.titleColor,t.font=a.string,s=0;se!==0)?(e.beginPath(),e.fillStyle=i.multiKeyBackground,ss(e,{x:t,y:p,w:c,h:s,radius:o}),e.fill(),e.stroke(),e.fillStyle=a.backgroundColor,e.beginPath(),ss(e,{x:n,y:p+1,w:c-2,h:s-2,radius:o}),e.fill()):(e.fillStyle=i.multiKeyBackground,e.fillRect(t,p,c,s),e.strokeRect(t,p,c,s),e.fillStyle=a.backgroundColor,e.fillRect(n,p+1,c-2,s-2))}e.fillStyle=this.labelTextColors[n]}drawBody(e,t,n){let{body:r}=this,{bodySpacing:i,bodyAlign:a,displayColors:o,boxHeight:s,boxWidth:c,boxPadding:l}=n,u=gs(n.bodyFont),d=u.lineHeight,f=0,p=_c(n.rtl,this.x,this.width),m=function(n){t.fillText(n,p.x(e.x+f),e.y+d/2),e.y+=d+i},h=p.textAlign(a),g,_,v,y,b,x,S;for(t.textAlign=a,t.textBaseline=`middle`,t.font=u.string,e.x=Ff(this,h,n),t.fillStyle=n.bodyColor,J(this.beforeBody,m),f=o&&h!==`right`?a===`center`?c/2+l:c+2+l:0,y=0,x=r.length;y0&&t.stroke()}_updateAnimationTarget(e){let t=this.chart,n=this.$animations,r=n&&n.x,i=n&&n.y;if(r||i){let n=Cf[e.position].call(this,this._active,this._eventPosition);if(!n)return;let a=this._size=Df(this,e),o=Object.assign({},n,this._size),s=jf(t,e,o),c=Pf(e,o,s,t);(r._to!==c.x||i._to!==c.y)&&(this.xAlign=s.xAlign,this.yAlign=s.yAlign,this.width=a.width,this.height=a.height,this.caretX=n.x,this.caretY=n.y,this._resolveAnimations().update(this,c))}}_willRender(){return!!this.opacity}draw(e){let t=this.options.setContext(this.getContext()),n=this.opacity;if(!n)return;this._updateAnimationTarget(t);let r={width:this.width,height:this.height},i={x:this.x,y:this.y};n=Math.abs(n)<.001?0:n;let a=hs(t.padding),o=this.title.length||this.beforeBody.length||this.body.length||this.afterBody.length||this.footer.length;t.enabled&&o&&(e.save(),e.globalAlpha=n,this.drawBackground(i,e,r,t),vc(e,t.textDirection),i.y+=a.top,this.drawTitle(i,e,t),this.drawBody(i,e,t),this.drawFooter(i,e,t),yc(e,t.textDirection),e.restore())}getActiveElements(){return this._active||[]}setActiveElements(e,t){let n=this._active,r=e.map(({datasetIndex:e,index:t})=>{let n=this.chart.getDatasetMeta(e);if(!n)throw Error(`Cannot find a dataset at index `+e);return{datasetIndex:e,element:n.data[t],index:t}}),i=!ba(n,r),a=this._positionChanged(r,t);(i||a)&&(this._active=r,this._eventPosition=t,this._ignoreReplayEvents=!0,this.update(!0))}handleEvent(e,t,n=!0){if(t&&this._ignoreReplayEvents)return!1;this._ignoreReplayEvents=!1;let r=this.options,i=this._active||[],a=this._getActiveElements(e,i,t,n),o=this._positionChanged(a,e),s=t||!ba(a,i)||o;return s&&(this._active=a,(r.enabled||r.external)&&(this._eventPosition={x:e.x,y:e.y},this.update(!0,t))),s}_getActiveElements(e,t,n,r){let i=this.options;if(e.type===`mouseout`)return[];if(!r)return t.filter(e=>this.chart.data.datasets[e.datasetIndex]&&this.chart.getDatasetMeta(e.datasetIndex).controller.getParsed(e.index)!==void 0);let a=this.chart.getElementsAtEventForMode(e,i.mode,i,n);return i.reverse&&a.reverse(),a}_positionChanged(e,t){let{caretX:n,caretY:r,options:i}=this,a=Cf[i.position].call(this,e,t);return a!==!1&&(n!==a.x||r!==a.y)}};$(Vf,`positioners`,Cf);var Hf={id:`tooltip`,_element:Vf,positioners:Cf,afterInit(e,t,n){n&&(e.tooltip=new Vf({chart:e,options:n}))},beforeUpdate(e,t,n){e.tooltip&&e.tooltip.initialize(n)},reset(e,t,n){e.tooltip&&e.tooltip.initialize(n)},afterDraw(e){let t=e.tooltip;if(t&&t._willRender()){let n={tooltip:t};if(e.notifyPlugins(`beforeTooltipDraw`,{...n,cancelable:!0})===!1)return;t.draw(e.ctx),e.notifyPlugins(`afterTooltipDraw`,n)}},afterEvent(e,t){if(e.tooltip){let n=t.replay;e.tooltip.handleEvent(t.event,n,t.inChartArea)&&(t.changed=!0)}},defaults:{enabled:!0,external:null,position:`average`,backgroundColor:`rgba(0,0,0,0.8)`,titleColor:`#fff`,titleFont:{weight:`bold`},titleSpacing:2,titleMarginBottom:6,titleAlign:`left`,bodyColor:`#fff`,bodySpacing:2,bodyFont:{},bodyAlign:`left`,footerColor:`#fff`,footerSpacing:2,footerMarginTop:6,footerFont:{weight:`bold`},footerAlign:`left`,padding:6,caretPadding:2,caretSize:5,cornerRadius:6,boxHeight:(e,t)=>t.bodyFont.size,boxWidth:(e,t)=>t.bodyFont.size,multiKeyBackground:`#fff`,displayColors:!0,boxPadding:0,borderColor:`rgba(0,0,0,0)`,borderWidth:0,animation:{duration:400,easing:`easeOutQuart`},animations:{numbers:{type:`number`,properties:[`x`,`y`,`width`,`height`,`caretX`,`caretY`]},opacity:{easing:`linear`,duration:200}},callbacks:zf},defaultRoutes:{bodyFont:`font`,footerFont:`font`,titleFont:`font`},descriptors:{_scriptable:e=>e!==`filter`&&e!==`itemSort`&&e!==`external`,_indexable:!1,callbacks:{_scriptable:!1,_indexable:!1},animation:{_fallback:!1},animations:{_fallback:`animation`}},additionalOptionScopes:[`interaction`]},Uf=(e,t,n,r)=>(typeof t==`string`?(n=e.push(t)-1,r.unshift({index:n,label:t})):isNaN(t)&&(n=null),n);function Wf(e,t,n,r){let i=e.indexOf(t);return i===-1?Uf(e,t,n,r):i===e.lastIndexOf(t)?i:n}var Gf=(e,t)=>e===null?null:io(Math.round(e),0,t);function Kf(e){let t=this.getLabels();return e>=0&&et.length-1?null:this.getPixelForValue(t[e].value)}getValueForPixel(e){return Math.round(this._startValue+this.getDecimalForPixel(e)*this._valueRange)}getBasePixel(){return this.bottom}};$(qf,`id`,`category`),$(qf,`defaults`,{ticks:{callback:Kf}});function Jf(e,t){let n=[],{bounds:r,step:i,min:a,max:o,precision:s,count:c,maxTicks:l,maxDigits:u,includeBounds:d}=e,f=i||1,p=l-1,{min:m,max:h}=t,g=!U(a),_=!U(o),v=!U(c),y=(h-m)/(u+1),b=Wa((h-m)/p/f)*f,x,S,C,w;if(b<1e-14&&!g&&!_)return[{value:m},{value:h}];w=Math.ceil(h/b)-Math.floor(m/b),w>p&&(b=Wa(w*b/p/f)*f),U(s)||(x=10**s,b=Math.ceil(b*x)/x),r===`ticks`?(S=Math.floor(m/b)*b,C=Math.ceil(h/b)*b):(S=m,C=h),g&&_&&i&&Ja((o-a)/i,b/1e3)?(w=Math.round(Math.min((o-a)/b,l)),b=(o-a)/w,S=a,C=o):v?(S=g?a:S,C=_?o:C,w=c-1,b=(C-S)/w):(w=(C-S)/b,w=Ua(w,Math.round(w),b/1e3)?Math.round(w):Math.ceil(w));let T=Math.max(Qa(b),Qa(S));x=10**(U(s)?T:s),S=Math.round(S*x)/x,C=Math.round(C*x)/x;let E=0;for(g&&(d&&S!==a?(n.push({value:a}),So)break;n.push({value:e})}return _&&d&&C!==o?n.length&&Ua(n[n.length-1].value,o,Yf(o,y,e))?n[n.length-1].value=o:n.push({value:o}):(!_||C===o)&&n.push({value:C}),n}function Yf(e,t,{horizontal:n,minRotation:r}){let i=Xa(r),a=(n?Math.sin(i):Math.cos(i))||.001,o=.75*t*(``+e).length;return Math.min(t/a,o)}var Xf=class extends Qu{constructor(e){super(e),this.start=void 0,this.end=void 0,this._startValue=void 0,this._endValue=void 0,this._valueRange=0}parse(e,t){return U(e)||(typeof e==`number`||e instanceof Number)&&!isFinite(+e)?null:+e}handleTickRangeOptions(){let{beginAtZero:e}=this.options,{minDefined:t,maxDefined:n}=this.getUserBounds(),{min:r,max:i}=this,a=e=>r=t?r:e,o=e=>i=n?i:e;if(e){let e=Ha(r),t=Ha(i);e<0&&t<0?o(0):e>0&&t>0&&a(0)}if(r===i){let t=i===0?1:Math.abs(i*.05);o(i+t),e||a(r-t)}this.min=r,this.max=i}getTickLimit(){let{maxTicksLimit:e,stepSize:t}=this.options.ticks,n;return t?(n=Math.ceil(this.max/t)-Math.floor(this.min/t)+1,n>1e3&&(console.warn(`scales.${this.id}.ticks.stepSize: ${t} would result generating up to ${n} ticks. Limiting to 1000.`),n=1e3)):(n=this.computeTickLimit(),e=e||11),e&&(n=Math.min(e,n)),n}computeTickLimit(){return 1/0}buildTicks(){let e=this.options,t=e.ticks,n=this.getTickLimit();n=Math.max(2,n);let r=Jf({maxTicks:n,bounds:e.bounds,min:e.min,max:e.max,precision:t.precision,step:t.stepSize,count:t.count,maxDigits:this._maxDigits(),horizontal:this.isHorizontal(),minRotation:t.minRotation||0,includeBounds:t.includeBounds!==!1},this._range||this);return e.bounds===`ticks`&&Ya(r,this,`value`),e.reverse?(r.reverse(),this.start=this.max,this.end=this.min):(this.start=this.min,this.end=this.max),r}configure(){let e=this.ticks,t=this.min,n=this.max;if(super.configure(),this.options.offset&&e.length){let r=(n-t)/Math.max(e.length-1,1)/2;t-=r,n+=r}this._startValue=t,this._endValue=n,this._valueRange=n-t}getLabelForValue(e){return Io(e,this.chart.options.locale,this.options.ticks.format)}},Zf=class extends Xf{determineDataLimits(){let{min:e,max:t}=this.getMinMax(!0);this.min=ga(e)?e:0,this.max=ga(t)?t:1,this.handleTickRangeOptions()}computeTickLimit(){let e=this.isHorizontal(),t=e?this.width:this.height,n=Xa(this.options.ticks.minRotation),r=(e?Math.sin(n):Math.cos(n))||.001,i=this._resolveTickFontOptions(0);return Math.ceil(t/Math.min(40,i.lineHeight/r))}getPixelForValue(e){return e===null?NaN:this.getPixelForDecimal((e-this._startValue)/this._valueRange)}getValueForPixel(e){return this._startValue+this.getDecimalForPixel(e)*this._valueRange}};$(Zf,`id`,`linear`),$(Zf,`defaults`,{ticks:{callback:zo.formatters.numeric}});var Qf=e=>Math.floor(Va(e)),$f=(e,t)=>10**(Qf(e)+t);function ep(e){return e/10**Qf(e)==1}function tp(e,t,n){let r=10**n,i=Math.floor(e/r);return Math.ceil(t/r)-i}function np(e,t){let n=Qf(t-e);for(;tp(e,t,n)>10;)n++;for(;tp(e,t,n)<10;)n--;return Math.min(n,Qf(e))}function rp(e,{min:t,max:n}){t=_a(e.min,t);let r=[],i=Qf(t),a=np(t,n),o=a<0?10**Math.abs(a):1,s=10**a,c=i>a?10**i:0,l=Math.round((t-c)*o)/o,u=Math.floor((t-c)/s/10)*s*10,d=Math.floor((l-u)/10**a),f=_a(e.min,Math.round((c+u+d*10**a)*o)/o);for(;f=10?d=d<15?15:20:d++,d>=20&&(a++,d=2,o=a>=0?1:o),f=Math.round((c+u+d*10**a)*o)/o;let p=_a(e.max,f);return r.push({value:p,major:ep(p),significand:d}),r}var ip=class extends Qu{constructor(e){super(e),this.start=void 0,this.end=void 0,this._startValue=void 0,this._valueRange=0}parse(e,t){let n=Xf.prototype.parse.apply(this,[e,t]);if(n===0)this._zero=!0;else return ga(n)&&n>0?n:null}determineDataLimits(){let{min:e,max:t}=this.getMinMax(!0);this.min=ga(e)?Math.max(0,e):null,this.max=ga(t)?Math.max(0,t):null,this.options.beginAtZero&&(this._zero=!0),this._zero&&this.min!==this._suggestedMin&&!ga(this._userMin)&&(this.min=e===$f(this.min,0)?$f(this.min,-1):$f(this.min,0)),this.handleTickRangeOptions()}handleTickRangeOptions(){let{minDefined:e,maxDefined:t}=this.getUserBounds(),n=this.min,r=this.max,i=t=>n=e?n:t,a=e=>r=t?r:e;n===r&&(n<=0?(i(1),a(10)):(i($f(n,-1)),a($f(r,1)))),n<=0&&i($f(r,-1)),r<=0&&a($f(n,1)),this.min=n,this.max=r}buildTicks(){let e=this.options,t=rp({min:this._userMin,max:this._userMax},this);return e.bounds===`ticks`&&Ya(t,this,`value`),e.reverse?(t.reverse(),this.start=this.max,this.end=this.min):(this.start=this.min,this.end=this.max),t}getLabelForValue(e){return e===void 0?`0`:Io(e,this.chart.options.locale,this.options.ticks.format)}configure(){let e=this.min;super.configure(),this._startValue=Va(e),this._valueRange=Va(this.max)-Va(e)}getPixelForValue(e){return(e===void 0||e===0)&&(e=this.min),e===null||isNaN(e)?NaN:this.getPixelForDecimal(e===this.min?0:(Va(e)-this._startValue)/this._valueRange)}getValueForPixel(e){let t=this.getDecimalForPixel(e);return 10**(this._startValue+t*this._valueRange)}};$(ip,`id`,`logarithmic`),$(ip,`defaults`,{ticks:{callback:zo.formatters.logarithmic,major:{enabled:!0}}});function ap(e){let t=e.ticks;if(t.display&&e.display){let e=hs(t.backdropPadding);return K(t.font&&t.font.size,Q.font.size)+e.height}return 0}function op(e,t,n){return n=W(n)?n:[n],{w:qo(e,t.string,n),h:n.length*t.lineHeight}}function sp(e,t,n,r,i){return e===r||e===i?{start:t-n/2,end:t+n/2}:ei?{start:t-n,end:t}:{start:t,end:t+n}}function cp(e){let t={l:e.left+e._padding.left,r:e.right-e._padding.right,t:e.top+e._padding.top,b:e.bottom-e._padding.bottom},n=Object.assign({},t),r=[],i=[],a=e._pointLabels.length,o=e.options.pointLabels,s=o.centerPointLabels?Y/a:0;for(let c=0;ct.r&&(s=(r.end-t.r)/a,e.r=Math.max(e.r,t.r+s)),i.startt.b&&(c=(i.end-t.b)/o,e.b=Math.max(e.b,t.b+c))}function up(e,t,n){let r=e.drawingArea,{extra:i,additionalAngle:a,padding:o,size:s}=n,c=e.getPointPosition(t,r+i+o,a),l=Math.round(Za(no(c.angle+Z))),u=hp(c.y,s.h,l),d=pp(l),f=mp(c.x,s.w,d);return{visible:!0,x:c.x,y:u,textAlign:d,left:f,top:u,right:f+s.w,bottom:u+s.h}}function dp(e,t){if(!t)return!0;let{left:n,top:r,right:i,bottom:a}=e;return!(Qo({x:n,y:r},t)||Qo({x:n,y:a},t)||Qo({x:i,y:r},t)||Qo({x:i,y:a},t))}function fp(e,t,n){let r=[],i=e._pointLabels.length,a=e.options,{centerPointLabels:o,display:s}=a.pointLabels,c={extra:ap(a)/2,additionalAngle:o?Y/i:0},l;for(let a=0;a270||n<90)&&(e-=t),e}function gp(e,t,n){let{left:r,top:i,right:a,bottom:o}=n,{backdropColor:s}=t;if(!U(s)){let n=ms(t.borderRadius),c=hs(t.backdropPadding);e.fillStyle=s;let l=r-c.left,u=i-c.top,d=a-r+c.width,f=o-i+c.height;Object.values(n).some(e=>e!==0)?(e.beginPath(),ss(e,{x:l,y:u,w:d,h:f,radius:n}),e.fill()):e.fillRect(l,u,d,f)}}function _p(e,t){let{ctx:n,options:{pointLabels:r}}=e;for(let i=t-1;i>=0;i--){let t=e._pointLabelItems[i];if(!t.visible)continue;let a=r.setContext(e.getPointLabelContext(i));gp(n,a,t);let o=gs(a.font),{x:s,y:c,textAlign:l}=t;os(n,e._pointLabels[i],s,c+o.lineHeight/2,o,{color:a.color,textAlign:l,textBaseline:`middle`})}}function vp(e,t,n,r){let{ctx:i}=e;if(n)i.arc(e.xCenter,e.yCenter,t,0,X);else{let n=e.getPointPosition(0,t);i.moveTo(n.x,n.y);for(let a=1;a{let n=q(this.options.pointLabels.callback,[e,t],this);return n||n===0?n:``}).filter((e,t)=>this.chart.getDataVisibility(t))}fit(){let e=this.options;e.display&&e.pointLabels.display?cp(this):this.setCenterPoint(0,0,0,0)}setCenterPoint(e,t,n,r){this.xCenter+=Math.floor((e-t)/2),this.yCenter+=Math.floor((n-r)/2),this.drawingArea-=Math.min(this.drawingArea/2,Math.max(e,t,n,r))}getIndexAngle(e){let t=X/(this._pointLabels.length||1),n=this.options.startAngle||0;return no(e*t+Xa(n))}getDistanceFromCenterForValue(e){if(U(e))return NaN;let t=this.drawingArea/(this.max-this.min);return this.options.reverse?(this.max-e)*t:(e-this.min)*t}getValueForDistanceFromCenter(e){if(U(e))return NaN;let t=e/(this.drawingArea/(this.max-this.min));return this.options.reverse?this.max-t:this.min+t}getPointLabelContext(e){let t=this._pointLabels||[];if(e>=0&&e{if(t!==0||t===0&&this.min<0){s=this.getDistanceFromCenterForValue(e.value);let n=this.getContext(t),o=r.setContext(n),c=i.setContext(n);yp(this,o,s,a,c)}}),n.display){for(e.save(),o=a-1;o>=0;o--){let r=n.setContext(this.getPointLabelContext(o)),{color:i,lineWidth:a}=r;a&&i&&(e.lineWidth=a,e.strokeStyle=i,e.setLineDash(r.borderDash),e.lineDashOffset=r.borderDashOffset,s=this.getDistanceFromCenterForValue(t.reverse?this.min:this.max),c=this.getPointPosition(o,s),e.beginPath(),e.moveTo(this.xCenter,this.yCenter),e.lineTo(c.x,c.y),e.stroke())}e.restore()}}drawBorder(){}drawLabels(){let e=this.ctx,t=this.options,n=t.ticks;if(!n.display)return;let r=this.getIndexAngle(0),i,a;e.save(),e.translate(this.xCenter,this.yCenter),e.rotate(r),e.textAlign=`center`,e.textBaseline=`middle`,this.ticks.forEach((r,o)=>{if(o===0&&this.min>=0&&!t.reverse)return;let s=n.setContext(this.getContext(o)),c=gs(s.font);if(i=this.getDistanceFromCenterForValue(this.ticks[o].value),s.showLabelBackdrop){e.font=c.string,a=e.measureText(r.label).width,e.fillStyle=s.backdropColor;let t=hs(s.backdropPadding);e.fillRect(-a/2-t.left,-i-c.size/2-t.top,a+t.width,c.size+t.height)}os(e,r.label,0,-i,c,{color:s.color,strokeColor:s.textStrokeColor,strokeWidth:s.textStrokeWidth})}),e.restore()}drawTitle(){}};$(xp,`id`,`radialLinear`),$(xp,`defaults`,{display:!0,animate:!0,position:`chartArea`,angleLines:{display:!0,lineWidth:1,borderDash:[],borderDashOffset:0},grid:{circular:!1},startAngle:0,ticks:{showLabelBackdrop:!0,callback:zo.formatters.numeric},pointLabels:{backdropColor:void 0,backdropPadding:2,display:!0,font:{size:10},callback(e){return e},padding:5,centerPointLabels:!1}}),$(xp,`defaultRoutes`,{"angleLines.color":`borderColor`,"pointLabels.color":`color`,"ticks.color":`color`}),$(xp,`descriptors`,{angleLines:{_fallback:`grid`}});var Sp={millisecond:{common:!0,size:1,steps:1e3},second:{common:!0,size:1e3,steps:60},minute:{common:!0,size:6e4,steps:60},hour:{common:!0,size:36e5,steps:24},day:{common:!0,size:864e5,steps:30},week:{common:!1,size:6048e5,steps:4},month:{common:!0,size:2628e6,steps:12},quarter:{common:!1,size:7884e6,steps:4},year:{common:!0,size:3154e7}},Cp=Object.keys(Sp);function wp(e,t){return e-t}function Tp(e,t){if(U(t))return null;let n=e._adapter,{parser:r,round:i,isoWeekday:a}=e._parseOpts,o=t;return typeof r==`function`&&(o=r(o)),ga(o)||(o=typeof r==`string`?n.parse(o,r):n.parse(o)),o===null?null:(i&&(o=i===`week`&&(qa(a)||a===!0)?n.startOf(o,`isoWeek`,a):n.startOf(o,i)),+o)}function Ep(e,t,n,r){let i=Cp.length;for(let a=Cp.indexOf(e);a=Cp.indexOf(n);a--){let n=Cp[a];if(Sp[n].common&&e._adapter.diff(i,r,n)>=t-1)return n}return Cp[n?Cp.indexOf(n):0]}function Op(e){for(let t=Cp.indexOf(e)+1,n=Cp.length;t=t?n[r]:n[i];e[a]=!0}}function Ap(e,t,n,r){let i=e._adapter,a=+i.startOf(t[0].value,r),o=t[t.length-1].value,s,c;for(s=a;s<=o;s=+i.add(s,1,r))c=n[s],c>=0&&(t[c].major=!0);return t}function jp(e,t,n){let r=[],i={},a=t.length,o,s;for(o=0;o+e.value))}initOffsets(e=[]){let t=0,n=0,r,i;this.options.offset&&e.length&&(r=this.getDecimalForValue(e[0]),t=e.length===1?1-r:(this.getDecimalForValue(e[1])-r)/2,i=this.getDecimalForValue(e[e.length-1]),n=e.length===1?i:(i-this.getDecimalForValue(e[e.length-2]))/2);let a=e.length<3?.5:.25;t=io(t,0,a),n=io(n,0,a),this._offsets={start:t,end:n,factor:1/(t+1+n)}}_generate(){let e=this._adapter,t=this.min,n=this.max,r=this.options,i=r.time,a=i.unit||Ep(i.minUnit,t,n,this._getLabelCapacity(t)),o=K(r.ticks.stepSize,1),s=a===`week`&&i.isoWeekday,c=qa(s)||s===!0,l={},u=t,d,f;if(c&&(u=+e.startOf(u,`isoWeek`,s)),u=+e.startOf(u,c?`day`:a),e.diff(n,t,a)>1e5*o)throw Error(t+` and `+n+` are too far apart with stepSize of `+o+` `+a);let p=r.ticks.source===`data`&&this.getDataTimestamps();for(d=u,f=0;d+e)}getLabelForValue(e){let t=this._adapter,n=this.options.time;return n.tooltipFormat?t.format(e,n.tooltipFormat):t.format(e,n.displayFormats.datetime)}format(e,t){let n=this.options.time.displayFormats,r=this._unit,i=t||n[r];return this._adapter.format(e,i)}_tickFormatFunction(e,t,n,r){let i=this.options,a=i.ticks.callback;if(a)return q(a,[e,t,n],this);let o=i.time.displayFormats,s=this._unit,c=this._majorUnit,l=s&&o[s],u=c&&o[c],d=n[t],f=c&&u&&d&&d.major;return this._adapter.format(e,r||(f?u:l))}generateTickLabels(e){let t,n,r;for(t=0,n=e.length;t0?o:1}getDataTimestamps(){let e=this._cache.data||[],t,n;if(e.length)return e;let r=this.getMatchingVisibleMetas();if(this._normalized&&r.length)return this._cache.data=r[0].controller.getAllParsedValues(this);for(t=0,n=r.length;t=e[r].pos&&t<=e[i].pos&&({lo:r,hi:i}=co(e,`pos`,t)),{pos:a,time:s}=e[r],{pos:o,time:c}=e[i]):(t>=e[r].time&&t<=e[i].time&&({lo:r,hi:i}=co(e,`time`,t)),{time:a,pos:s}=e[r],{time:o,pos:c}=e[i]);let l=o-a;return l?s+(c-s)*(t-a)/l:s}var Pp=class extends Mp{constructor(e){super(e),this._table=[],this._minPos=void 0,this._tableRange=void 0}initOffsets(){let e=this._getTimestampsForTable(),t=this._table=this.buildLookupTable(e);this._minPos=Np(t,this.min),this._tableRange=Np(t,this.max)-this._minPos,super.initOffsets(e)}buildLookupTable(e){let{min:t,max:n}=this,r=[],i=[],a,o,s,c,l;for(a=0,o=e.length;a=t&&c<=n&&r.push(c);if(r.length<2)return[{time:t,pos:0},{time:n,pos:1}];for(a=0,o=r.length;ae-t)}_getTimestampsForTable(){let e=this._cache.all||[];if(e.length)return e;let t=this.getDataTimestamps(),n=this.getLabelTimestamps();return e=t.length&&n.length?this.normalize(t.concat(n)):t.length?t:n,e=this._cache.all=e,e}getDecimalForValue(e){return(Np(this._table,e)-this._minPos)/this._tableRange}getValueForPixel(e){let t=this._offsets,n=this.getDecimalForPixel(e)/t.factor-t.end;return Np(this._table,n*this._tableRange+this._minPos,!0)}};$(Pp,`id`,`timeseries`),$(Pp,`defaults`,Mp.defaults),Bd.register(El,Sf,qf,Zf,Hf);var Fp=new Intl.DateTimeFormat(`ko-KR`,{hour:`2-digit`,hour12:!1}),Ip=`#94a3b8`,Lp=`rgba(148, 163, 184, 0.18)`,Rp=`#0d9488`,zp=`#0f766e`;function Bp(e){return Fp.format(new Date(e*1e3))}function Vp(e){return e.map((t,n)=>n%3==0||n===e.length-1?Bp(t.bucketStart):``)}function Hp({buckets:e}){let t=we(null),n=we(null),r=we(e);return r.current=e,I(()=>{let e=t.current;if(!e)return;let i=window.matchMedia(`(prefers-reduced-motion: reduce)`).matches,a=window.getComputedStyle(e).fontFamily,o={animation:!i&&{duration:500,easing:`easeOutQuart`},maintainAspectRatio:!1,responsive:!0,interaction:{intersect:!1,mode:`index`},plugins:{legend:{display:!1},tooltip:{displayColors:!1,callbacks:{title:e=>{let t=e[0]?.dataIndex??-1,n=r.current[t];return n?Bp(n.bucketStart):``},label:e=>`차단 ${H(Number(e.raw??0))}`,afterLabel:e=>{let t=r.current[e.dataIndex];return t?`DNS 요청 ${H(t.queries)}`:``}}}},scales:{x:{border:{display:!1},grid:{display:!1},ticks:{autoSkip:!1,color:Ip,font:{family:a,size:11,weight:700},maxRotation:0,minRotation:0}},y:{beginAtZero:!0,border:{display:!1},grid:{color:Lp},ticks:{color:Ip,font:{family:a,size:11,weight:700},maxTicksLimit:4,precision:0}}}};return n.current=new Bd(e,{type:`bar`,data:{labels:Vp(r.current),datasets:[{data:r.current.map(e=>e.blocked),backgroundColor:Rp,hoverBackgroundColor:zp,borderRadius:4,borderSkipped:!1,barPercentage:.82,categoryPercentage:.9,maxBarThickness:24}]},options:o}),()=>{n.current?.destroy(),n.current=null}},[]),I(()=>{let t=n.current;t&&(t.data.labels=Vp(e),t.data.datasets[0].data=e.map(e=>e.blocked),t.update())},[e]),L(`div`,{class:`mt-5 h-44 sm:h-52`,children:L(`canvas`,{"aria-label":`최근 24시간 시간대별 SafeShield 차단 요청`,ref:t,role:`img`,children:`최근 24시간 시간대별 SafeShield 차단 요청 차트`})})}var Up=3600,Wp=24;function Gp(e,t){return e<=0?`0.0%`:`${(t/e*100).toFixed(1)}%`}function Kp(e){let t=e.hourly[e.hourly.length-1];if(t?.bucketStart)return t.bucketStart;let n=e.updatedAt>0?e.updatedAt:Math.floor(Date.now()/1e3);return Math.floor(n/Up)*Up}function qp(e){let t=new Map(e.hourly.map(e=>[e.bucketStart,e])),n=Kp(e)-23*Up;return Array.from({length:Wp},(e,r)=>{let i=n+r*Up;return t.get(i)??{bucketStart:i,queries:0,blocked:0}})}function Jp({children:e}){return L(`div`,{class:`mt-5 flex min-h-52 items-center justify-center rounded-xl border border-slate-200 bg-slate-50 px-5 text-center`,children:L(`p`,{class:`m-0 max-w-lg text-sm font-semibold leading-6 text-slate-500`,children:e})})}function Yp({data:e,error:t,loading:n,nowTimestamp:r,refreshing:i}){let a;if(n&&!e)a=L(Jp,{children:`SafeShield 보호 활동을 불러오는 중입니다.`});else if(t&&!e)a=L(Jp,{children:`SafeShield 보호 활동을 불러오지 못했습니다. SafeShield 페이지에서 상태를 확인해 주세요.`});else if(!e||!e.available)a=L(Jp,{children:`현재 SafeShield에서 차단 통계를 제공하지 않습니다. 통계 기능을 확인해 주세요.`});else if(!e.enabled)a=L(Jp,{children:`통계 수집이 꺼져 있습니다. SafeShield 페이지에서 통계를 켜면 최근 24시간 활동이 표시됩니다.`});else{let t=qp(e),n=t.reduce((e,t)=>({queries:e.queries+t.queries,blocked:e.blocked+t.blocked}),{queries:0,blocked:0});a=L(v,{children:[L(`dl`,{class:`mt-5 grid gap-3 sm:grid-cols-3`,children:[L(`div`,{class:`rounded-xl bg-slate-50 px-4 py-3`,children:[L(`dt`,{class:`text-[0.68rem] font-black uppercase tracking-[0.12em] text-slate-400`,children:`DNS 요청`}),L(`dd`,{class:`mt-1 mb-0 ml-0 text-lg font-black text-slate-950`,children:H(n.queries)})]}),L(`div`,{class:`rounded-xl bg-slate-50 px-4 py-3`,children:[L(`dt`,{class:`text-[0.68rem] font-black uppercase tracking-[0.12em] text-slate-400`,children:`차단`}),L(`dd`,{class:`mt-1 mb-0 ml-0 text-lg font-black text-teal-700`,children:H(n.blocked)})]}),L(`div`,{class:`rounded-xl bg-slate-50 px-4 py-3`,children:[L(`dt`,{class:`text-[0.68rem] font-black uppercase tracking-[0.12em] text-slate-400`,children:`차단율`}),L(`dd`,{class:`mt-1 mb-0 ml-0 text-lg font-black text-slate-950`,children:Gp(n.queries,n.blocked)})]})]}),L(Hp,{buckets:t})]})}return L(`article`,{class:`min-w-0 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm shadow-slate-900/5 sm:p-6`,children:[L(`div`,{class:`min-w-0`,children:[L(`p`,{class:`m-0 text-xs font-extrabold uppercase tracking-[0.16em] text-slate-500`,children:`SafeShield activity`}),L(`div`,{class:`mt-2 flex min-w-0 items-center justify-between gap-3`,children:[L(`h3`,{class:`m-0 min-w-0 text-xl font-black text-slate-950`,children:`최근 24시간 보호 활동`}),L(`a`,{class:`shrink-0 whitespace-nowrap text-xs font-extrabold text-teal-700 no-underline hover:text-teal-800`,href:`#safeshield`,children:`상세 통계 →`})]}),e?.updatedAt?L(`p`,{class:`mt-2 mb-0 text-[0.68rem] font-semibold text-slate-400`,title:Ut(e.updatedAt),children:i?`갱신 중…`:`집계: ${Wt(e.updatedAt,r)}`}):null]}),a,t&&e?L(`p`,{class:`mt-3 mb-0 text-xs font-bold text-amber-700`,children:`최근 통계 갱신에 실패해 마지막 정상 데이터를 표시하고 있습니다.`}):null]})}function Xp(e){return e===`healthy`?`bg-emerald-50 text-emerald-700 ring-emerald-200`:e===`warning`?`bg-amber-50 text-amber-800 ring-amber-200`:`bg-slate-100 text-slate-600 ring-slate-200`}function Zp({detail:e,eyebrow:t,href:n,icon:r,linkLabel:i=`자세히 보기 →`,meta:a,metaState:o,metaWarning:s=!1,state:c=`neutral`,value:l}){let u=L(v,{children:[L(`div`,{class:`flex items-start justify-between gap-4`,children:[L(`p`,{class:`m-0 text-[0.68rem] font-black uppercase tracking-[0.16em] text-slate-500`,children:t}),L(`span`,{class:`grid size-10 shrink-0 place-items-center rounded-xl ring-1 ring-inset ${Xp(c)}`,children:r})]}),L(`p`,{class:`mt-4 mb-0 break-words text-2xl font-black tracking-tight text-slate-950`,children:l}),L(`p`,{class:`mt-2 mb-0 min-h-10 text-sm font-medium leading-5 text-slate-500`,children:e}),a?L(`p`,{class:`mt-3 mb-0 text-xs font-bold ${o===`healthy`?`text-emerald-700`:o===`warning`||s?`text-amber-700`:`text-slate-400`}`,children:a}):null,n?L(`span`,{class:`mt-4 inline-flex text-xs font-extrabold text-teal-700`,children:i}):null]}),d=`min-w-0 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm shadow-slate-900/5 transition`;return n?L(`a`,{class:`${d} no-underline hover:border-slate-300 hover:shadow-md`,href:n,children:u}):L(`article`,{class:d,children:u})}function Qp({action:e,description:t,eyebrow:n,id:r,title:i}){return L(`div`,{class:`mb-4 min-w-0`,children:[L(`p`,{class:`m-0 text-[0.68rem] font-black uppercase tracking-[0.18em] text-teal-700`,children:n}),L(`div`,{class:`mt-2 flex min-w-0 items-center justify-between gap-3`,children:[L(`h2`,{class:`m-0 min-w-0 text-xl font-black tracking-tight text-slate-950`,id:r,children:i}),e?L(`div`,{class:`shrink-0`,children:e}):null]}),L(`p`,{class:`mt-2 mb-0 text-sm leading-6 text-slate-500`,children:t})]})}var $p=6e4;function em(e,t){return!e||!Number.isFinite(e)||e<=0?null:Math.max(0,t-e)}function tm(e,t,n,r){return!t||t<=0?`${e}: ${r}`:L(`span`,{title:Ut(t),children:[e,`: `,Wt(t,n)]})}function nm(e){if(!e)return null;switch(e.toLowerCase()){case`dhcp`:return`DHCP`;case`static`:return`Static`;case`pppoe`:return`PPPoE`;default:return e.toUpperCase()}}function rm(e){if(!e)return!1;let t=e.split(`.`).map(e=>Number(e));if(t.length!==4||t.some(e=>!Number.isInteger(e)||e<0||e>255))return!1;let[n,r]=t;return n===void 0||r===void 0?!1:n===10||n===172&&r>=16&&r<=31||n===192&&r===168}function im({label:e,value:t}){return L(`div`,{class:`flex min-w-0 items-start justify-between gap-4 border-b border-slate-100 py-3 last:border-b-0`,children:[L(`dt`,{class:`text-sm font-semibold text-slate-500`,children:e}),L(`dd`,{class:`m-0 min-w-0 max-w-[68%] break-words text-right text-sm font-extrabold text-slate-950`,children:t})]})}function am(e){return e===`initializing`?{badge:`bg-teal-100 text-teal-800`,icon:`text-teal-700`,label:`준비 중`,panel:`border-teal-200 bg-teal-50`}:e===`critical`?{badge:`bg-rose-100 text-rose-800`,icon:`text-rose-700`,label:`이상`,panel:`border-rose-200 bg-rose-50/70`}:e===`warning`?{badge:`bg-amber-100 text-amber-800`,icon:`text-amber-700`,label:`주의`,panel:`border-amber-200 bg-amber-50/70`}:e===`unknown`?{badge:`bg-slate-200 text-slate-700`,icon:`text-slate-500`,label:`확인 불가`,panel:`border-slate-200 bg-slate-50`}:{badge:`bg-emerald-100 text-emerald-800`,icon:`text-emerald-700`,label:`정상`,panel:`border-emerald-200 bg-emerald-50/70`}}function om({data:e,error:t,loading:n,nowTimestamp:r}){let i=!!(e&&e.generatedAt>0),a=am(i&&e?e.overall:`unknown`),o=i&&e?e.checks.filter(e=>e.status!==`ok`):[],s=o.slice(0,2),c=Math.max(0,o.length-s.length);return L(`div`,{class:`mt-5 border-t border-slate-200 pt-5`,children:[L(`div`,{class:`flex flex-wrap items-center justify-between gap-3`,children:[L(`div`,{class:`flex items-center gap-2`,children:[L(`p`,{class:`m-0 text-sm font-black text-slate-950`,children:`장치 진단`}),i?L(`span`,{class:`rounded-full px-2.5 py-1 text-[11px] font-extrabold ${a.badge}`,children:a.label}):null]}),L(`a`,{class:`text-xs font-extrabold text-teal-700 no-underline hover:text-teal-900`,href:`#settings`,children:`상세 보기 →`})]}),t&&!i?L(`div`,{class:`mt-3 rounded-xl border border-amber-200 bg-amber-50/70 px-4 py-3`,children:L(`div`,{class:`flex items-start gap-2`,children:[L(V,{class:`mt-0.5 size-4 shrink-0 text-amber-700`}),L(`div`,{class:`min-w-0`,children:[L(`p`,{class:`m-0 text-sm font-extrabold text-slate-900`,children:`진단 상태를 확인하지 못했습니다.`}),L(`p`,{class:`mt-1 mb-0 text-xs leading-5 text-slate-500`,children:`설정에서 장치 진단 상태를 다시 확인해 주세요.`})]})]})}):i&&e?L(`div`,{class:`mt-3 rounded-xl border px-4 py-3 ${a.panel}`,children:[L(`div`,{class:`flex min-w-0 items-start gap-3`,children:[e.overall===`ok`?L(B,{class:`mt-0.5 size-5 shrink-0 ${a.icon}`}):e.overall===`initializing`?L(tt,{class:`mt-0.5 size-5 shrink-0 ${a.icon}`}):L(V,{class:`mt-0.5 size-5 shrink-0 ${a.icon}`}),L(`div`,{class:`min-w-0 flex-1`,children:[L(`p`,{class:`m-0 text-sm font-black text-slate-950`,children:e.summary.message}),L(`p`,{class:`mt-1 mb-0 text-xs font-semibold leading-5 text-slate-500`,children:[`마지막 진단: `,Wt(e.generatedAt,r),` · 검사 항목:`,` `,e.summary.total,`개`]})]})]}),s.length>0?L(`div`,{class:`mt-3 grid gap-2 sm:grid-cols-2`,children:s.map(e=>L(`div`,{class:`min-w-0 rounded-lg border border-white/80 bg-white/70 px-3 py-2`,children:[L(`p`,{class:`m-0 text-xs font-extrabold text-slate-900`,children:e.label}),L(`p`,{class:`mt-1 mb-0 line-clamp-2 text-xs leading-5 text-slate-500`,children:e.message})]},e.id))}):null,c>0?L(`p`,{class:`mt-2 mb-0 text-xs font-extrabold text-slate-600`,children:[`외 `,c,`건의 확인 항목이 있습니다.`]}):null]}):L(`div`,{class:`mt-3 rounded-xl border border-slate-200 bg-slate-50 px-4 py-3`,children:[L(`p`,{class:`m-0 text-sm font-extrabold text-slate-900`,children:n?`진단 상태를 확인하고 있습니다.`:`아직 생성된 진단 결과가 없습니다.`}),L(`p`,{class:`mt-1 mb-0 text-xs leading-5 text-slate-500`,children:`진단 결과가 생성되면 현재 장치 상태를 이곳에 요약해서 표시합니다.`})]})]})}function sm(e,t,n){if(t&&!e)return{detail:`보호 상태를 확인하고 있습니다.`,state:`neutral`,value:`확인 중`};if(n&&!e)return{detail:`SafeShield 상태를 불러오지 못했습니다.`,state:`warning`,value:`확인 필요`};if(!e||!e.available)return{detail:`SafeShield 서비스를 사용할 수 없습니다.`,state:`warning`,value:`사용 불가`};if(!e.enabled||e.status===`disabled`)return{detail:`DNS 보호가 비활성화되어 있습니다.`,state:`neutral`,value:`꺼짐`};if(e.status===`running`)return{detail:`차단 목록을 갱신하고 있습니다.`,state:`neutral`,value:`갱신 중`};if(!(e.active&&e.runtime.dnsmasqRunning&&e.runtime.dnsRuntimeOk&&e.health.overall!==`error`&&e.issueCounts.errors===0))return{detail:e.runtime.lastErrorCode?`최근 오류: ${e.runtime.lastErrorCode}`:`DNS 보호 상태를 확인해 주세요.`,state:`warning`,value:`확인 필요`};let r=e.entitlement.plan?.trim(),i=e.blocklist.validLineCount>0?`${H(e.blocklist.validLineCount)}개 차단 규칙`:`차단 목록 적용됨`;return{detail:r?`${i} · ${r.toUpperCase()}`:i,state:`healthy`,value:`보호 중`}}function cm({accountRegistered:e,activity:t,activityError:n,activityLoading:r,data:i,devices:a,devicesError:o,devicesLoading:s,error:c,firmware:l,firmwareError:u,firmwareLoading:d,health:f,healthError:p,healthLoading:m,lan:h,lanError:g,lanLoading:_,loading:v,onRetry:y,safeshield:b,safeshieldError:x,safeshieldLoading:S,statistics:C,statisticsError:w,statisticsLoading:T,statisticsRefreshing:E,updates:D,updatesError:O,updatesLoading:k}){let A=Te(()=>i?Jt(i.runtime.memory):null,[i]),[j,M]=F(()=>Math.floor(Date.now()/1e3));if(I(()=>{let e=window.setInterval(()=>{M(Math.floor(Date.now()/1e3))},$p);return()=>window.clearInterval(e)},[]),v&&!i)return L(Rn,{});if(c&&!i)return L(zn,{message:c,onRetry:y});if(!i)return null;let ee=A?Math.round(A.percent):0,te=sm(b,S,x),ne=D?.packages.find(e=>e.name===`luci-app-smartsafehub`)??D?.packages[0]??null,re=!!(D&&D.updateCount>0),ie=D?re?`${D.updateCount}개 업데이트`:D.lastCheckAt?`최신 상태`:`확인 전`:O?`확인 필요`:`확인 중`,ae=ne?re&&ne.availableVersion?`${ne.installedVersion} → ${ne.availableVersion}`:`설치 버전 ${ne.installedVersion}`:O?`업데이트 상태를 불러오지 못했습니다.`:k?`업데이트 상태를 확인하고 있습니다.`:`업데이트 상태를 확인할 수 없습니다.`,oe=a?`${H(a.totals.online)}대`:s?`확인 중`:`확인 필요`,se=a?`Wi-Fi ${H(a.totals.wireless)}대 · 유선/기타 ${H(a.totals.ethernet)}대`:o||`연결 기기 요약을 불러오고 있습니다.`,ce=nm(i.network.protocol),le=i.network.available?i.network.ipv4Address?`WAN ${i.network.ipv4Address}${ce?` · ${ce}`:``}`:ce?`${ce} · WAN 주소를 받지 못했습니다.`:`WAN 주소를 받지 못했습니다.`:`WAN 인터페이스를 찾을 수 없습니다.`,N=!!h?.conflict.detected,ue=!i.network.up||N||g!==null&&!h?`warning`:h?`healthy`:`neutral`,de=N?`네트워크 충돌`:i.network.up?`정상 연결`:`연결 확인`,fe=i.network.up?N?`⚠ LAN 대역과 충돌합니다`:_&&!h?`네트워크 대역 확인 중`:g&&!h?`네트워크 대역을 확인하지 못했습니다.`:h?`✓ 네트워크 충돌 없음`:`네트워크 대역 확인 필요`:`WAN 연결 상태를 확인해 주세요.`,pe=!i.network.up||N||g!==null&&!h?`warning`:h?`healthy`:`neutral`,me=i.network.ipv4Address?rm(i.network.ipv4Address)?`${i.network.ipv4Address} · 사설 네트워크`:i.network.ipv4Address:`할당되지 않음`,he=em(b?.timestamps.lastSuccess,j),P=b?Math.max(b.timestamps.refreshIntervalS*2,86400):null,ge=!!(b?.enabled&&b.status!==`running`&&he!==null&&P!==null&&he>P),_e=Ur(D,j),ve=!!l?.current.metadataAvailable,ye=d&&!l?`확인 중`:u&&!l?`확인 필요`:ve?l?.current.releaseVersion?`SmartSafeHub ${l.current.releaseVersion}`:`SmartSafeHub 펌웨어`:`${i.software.distribution} ${i.software.version}`,be=d&&!l?`확인 중`:u&&!l?`확인 필요`:ve&&l?.current.buildId||i.software.revision,xe=ve?`빌드 ID`:`리비전`;return L(`div`,{class:`min-w-0 space-y-6`,children:[L(`section`,{"aria-labelledby":`dashboard-overview-title`,children:[L(Qp,{description:`인터넷, SafeShield, 연결 기기와 소프트웨어 상태를 한눈에 확인합니다.`,eyebrow:`Overview`,id:`dashboard-overview-title`,title:`시스템 개요`}),L(`div`,{class:`grid min-w-0 gap-4 sm:grid-cols-2 xl:grid-cols-4`,children:[L(Zp,{detail:le,eyebrow:`Internet`,href:`#network`,icon:L($e,{class:`size-5`}),linkLabel:N?`해결하기 →`:`자세히 보기 →`,meta:fe,metaState:pe,state:ue,value:de}),L(Zp,{detail:te.detail,eyebrow:`SafeShield`,href:`#safeshield`,icon:L(Ue,{class:`size-5`}),meta:tm(ge?`차단 목록 갱신 지연`:`차단 목록 갱신`,b?.timestamps.lastSuccess,j,S?`확인 중`:`기록 없음`),metaWarning:ge,state:te.state===`healthy`&&ge?`warning`:te.state,value:te.value}),L(Zp,{detail:se,eyebrow:`Connected devices`,href:`#devices`,icon:L(Ge,{class:`size-5`}),meta:tm(`목록 확인`,a?.generatedAt,j,s?`확인 중`:`기록 없음`),state:a?`healthy`:o?`warning`:`neutral`,value:oe}),L(Zp,{detail:ae,eyebrow:`Software update`,href:`#system`,icon:L(Je,{class:`size-5`}),meta:D&&!D.settings.checkEnabled?tm(`자동 확인 꺼짐 · 마지막 확인`,D.lastCheckAt,j,`확인 기록 없음`):tm(_e?`업데이트 확인 지연`:`마지막 확인`,D?.lastCheckAt,j,k?`확인 중`:`아직 없음`),metaWarning:_e,state:O||_e||re?`warning`:D?.lastCheckAt?`healthy`:`neutral`,value:ie})]}),e===!1?L(`div`,{class:`ssh-dashboard-account-banner mt-4 flex flex-col gap-4 rounded-2xl border p-4 sm:flex-row sm:items-center sm:justify-between sm:p-5`,role:`status`,children:[L(`div`,{class:`flex min-w-0 items-start gap-3`,children:[L(`span`,{class:`ssh-dashboard-account-banner-icon grid size-10 shrink-0 place-items-center rounded-xl`,"aria-hidden":`true`,children:L(qe,{class:`size-5`})}),L(`div`,{class:`min-w-0`,children:[L(`div`,{class:`flex flex-wrap items-center gap-2`,children:[L(`p`,{class:`ssh-dashboard-account-banner-title m-0 font-bold`,children:`SmartSafeHub 계정을 연결해 주세요`}),L(`span`,{class:`ssh-dashboard-account-banner-badge rounded-full px-2 py-0.5 text-[0.7rem] font-bold`,children:`연결 필요`})]}),L(`p`,{class:`ssh-dashboard-account-banner-description mt-1 mb-0 text-sm leading-relaxed`,children:`계정을 연결하면 웹사이트에서 기기를 관리하고 구독 정보를 확인할 수 있습니다. DNS 보호는 연결하지 않아도 계속 작동합니다.`})]})]}),L(`a`,{href:`#account`,class:`ssh-dashboard-account-banner-action inline-flex min-h-10 shrink-0 items-center justify-center rounded-xl px-4 py-2 text-sm font-bold no-underline transition-colors focus-visible:outline-2 focus-visible:outline-offset-2`,children:`계정 연결하기 →`})]}):null]}),L(`section`,{"aria-labelledby":`dashboard-activity-title`,children:[L(Qp,{description:`최근 SafeShield 차단 활동과 현재 네트워크 연결 구성을 함께 확인합니다.`,eyebrow:`Activity`,id:`dashboard-activity-title`,title:`네트워크 보호 활동`}),L(`div`,{class:`grid min-w-0 gap-4 xl:grid-cols-[minmax(0,1.55fr)_minmax(18rem,0.65fr)]`,children:[L(Yp,{data:C,error:w,loading:T,nowTimestamp:j,refreshing:E}),L(`article`,{class:`min-w-0 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm shadow-slate-900/5 sm:p-6`,children:[L(`div`,{class:`flex items-start justify-between gap-4`,children:[L(`div`,{children:[L(`p`,{class:`m-0 text-xs font-extrabold uppercase tracking-[0.16em] text-slate-500`,children:`Network`}),L(`h3`,{class:`mt-2 mb-0 text-xl font-black text-slate-950`,children:`연결 상태`})]}),L(`span`,{class:`grid size-10 shrink-0 place-items-center rounded-xl ${i.network.up&&!N?`bg-emerald-50 text-emerald-700`:`bg-amber-50 text-amber-800`}`,children:L($e,{class:`size-5`})})]}),L(`dl`,{class:`mt-4 mb-0`,children:[L(im,{label:`인터넷`,value:N?`네트워크 충돌`:i.network.up?`정상 연결`:`연결 확인`}),L(im,{label:`WAN IP`,value:me}),L(im,{label:`프로토콜`,value:ce||`확인되지 않음`}),L(im,{label:`상위 네트워크`,value:h?.wan.subnet||(_?`확인 중`:g?`확인 필요`:`확인되지 않음`)}),L(im,{label:`LAN 네트워크`,value:h?.lan.subnet||(_?`확인 중`:g?`확인 필요`:`확인되지 않음`)}),L(im,{label:`대역 충돌`,value:h?L(`span`,{class:h.conflict.detected?`text-amber-700`:`text-emerald-700`,children:h.conflict.detected?`충돌 감지`:`없음`}):_?`확인 중`:`확인 필요`})]}),L(`a`,{class:`mt-5 inline-flex min-h-10 w-full items-center justify-center rounded-xl border border-slate-200 bg-slate-50 px-4 py-2 text-sm font-extrabold text-slate-700 no-underline transition hover:border-teal-200 hover:bg-teal-50 hover:text-teal-800`,href:`#network`,children:`네트워크 보기`})]})]})]}),L(`section`,{"aria-labelledby":`dashboard-recent-activity-title`,children:[L(Qp,{action:L(`a`,{class:`inline-flex whitespace-nowrap text-xs font-extrabold text-teal-700 no-underline hover:text-teal-900`,href:`#activity`,children:`전체 보기 →`}),description:`인터넷, 보호, 업데이트와 진단에서 실제 상태가 바뀐 시점을 확인합니다.`,eyebrow:`History`,id:`dashboard-recent-activity-title`,title:`최근 활동`}),L(`article`,{class:`min-w-0 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm shadow-slate-900/5 sm:p-6`,children:r||n&&!t?L(un,{error:n,loading:r}):L(ln,{compact:!0,events:(t?.events??[]).slice(0,3)})})]}),L(`section`,{"aria-labelledby":`dashboard-system-title`,children:[L(Qp,{description:`메모리, 시스템 부하와 장치 정보를 빠르게 점검합니다.`,eyebrow:`System`,id:`dashboard-system-title`,title:`시스템 상태`}),L(`div`,{class:`grid min-w-0 gap-4 xl:grid-cols-[minmax(0,1.45fr)_minmax(20rem,0.75fr)]`,children:[L(`article`,{class:`min-w-0 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm shadow-slate-900/5 sm:p-6`,children:[L(`div`,{class:`flex flex-wrap items-start justify-between gap-4`,children:[L(`div`,{children:[L(`p`,{class:`m-0 text-xs font-extrabold uppercase tracking-[0.16em] text-slate-500`,children:`Runtime`}),L(`h3`,{class:`mt-2 mb-0 text-xl font-black text-slate-950`,children:`리소스 사용량`})]}),L(`span`,{class:`inline-flex items-center gap-2 rounded-full px-3 py-1.5 text-xs font-black ring-1 ring-inset ${ee>=90?`bg-amber-50 text-amber-800 ring-amber-200`:`bg-emerald-50 text-emerald-700 ring-emerald-200`}`,children:[L(`span`,{class:`size-2 rounded-full ${ee>=90?`bg-amber-500`:`bg-emerald-500`}`}),ee>=90?`메모리 확인`:`정상 실행`]})]}),L(`div`,{class:`mt-5 grid gap-3 sm:grid-cols-3`,children:[L(`div`,{class:`rounded-xl border border-slate-200 bg-slate-50 p-4`,children:[L(`div`,{class:`flex items-center gap-2 text-slate-500`,children:[L(nt,{class:`size-4`}),L(`span`,{class:`text-xs font-extrabold`,children:`메모리`})]}),L(`p`,{class:`mt-2 mb-0 text-xl font-black text-slate-950`,children:[ee,`%`]}),L(`p`,{class:`mt-1 mb-0 text-xs font-semibold text-slate-500`,children:[A?Bt(A.used):`-`,` /`,` `,Bt(i.runtime.memory.total)]}),L(`div`,{"aria-label":`메모리 사용률 ${ee}%`,class:`mt-3 h-1.5 overflow-hidden rounded-full bg-slate-200`,role:`progressbar`,"aria-valuemin":0,"aria-valuemax":100,"aria-valuenow":ee,children:L(`div`,{class:`h-full rounded-full transition-[width] duration-300 ${ee>=90?`bg-amber-500`:`bg-teal-600`}`,style:{width:`${Math.min(100,ee)}%`}})})]}),L(`div`,{class:`rounded-xl border border-slate-200 bg-slate-50 p-4`,children:[L(`div`,{class:`flex items-center gap-2 text-slate-500`,children:[L(We,{class:`size-4`}),L(`span`,{class:`text-xs font-extrabold`,children:`시스템 부하`})]}),L(`p`,{class:`mt-2 mb-0 text-xl font-black text-slate-950`,children:Vt(i.runtime.load[0])}),L(`p`,{class:`mt-1 mb-0 text-xs font-semibold text-slate-500`,children:[`5분 `,Vt(i.runtime.load[1]),` · 15분`,` `,Vt(i.runtime.load[2])]})]}),L(`div`,{class:`rounded-xl border border-slate-200 bg-slate-50 p-4`,children:[L(`div`,{class:`flex items-center gap-2 text-slate-500`,children:[L(tt,{class:`size-4`}),L(`span`,{class:`text-xs font-extrabold`,children:`실행 시간`})]}),L(`p`,{class:`mt-2 mb-0 text-xl font-black text-slate-950`,children:zt(i.runtime.uptime)}),L(`p`,{class:`mt-1 mb-0 text-xs font-semibold text-slate-500`,children:[`부팅 `,Ht(i.runtime.localtime,i.runtime.uptime)]})]})]}),L(om,{data:f,error:p,loading:m,nowTimestamp:j})]}),L(`article`,{class:`min-w-0 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm shadow-slate-900/5 sm:p-6`,children:[L(`div`,{class:`flex items-start justify-between gap-4`,children:[L(`div`,{children:[L(`p`,{class:`m-0 text-xs font-extrabold uppercase tracking-[0.16em] text-slate-500`,children:`Device`}),L(`h3`,{class:`mt-2 mb-0 text-xl font-black text-slate-950`,children:`장치 정보`})]}),L(`span`,{class:`grid size-10 shrink-0 place-items-center rounded-xl bg-teal-50 text-teal-700`,children:L(We,{class:`size-5`})})]}),L(`dl`,{class:`mt-4 mb-0`,children:[L(im,{label:`모델`,value:i.device.model}),L(im,{label:`호스트 이름`,value:i.device.hostname}),L(im,{label:`펌웨어`,value:L(`span`,{title:ve?l?.current.releaseVersion?`SmartSafeHub 펌웨어 ${l.current.releaseVersion}`:`SmartSafeHub 커스텀 펌웨어`:u||void 0,children:ye})}),L(im,{label:xe,value:be}),L(im,{label:`커널`,value:i.software.kernel}),L(im,{label:`WAN IP`,value:i.network.ipv4Address||`할당되지 않음`})]})]})]})]})]})}var lm=3,um=10;function dm(e,t){return e<=0?`0.0%`:`${(t/e*100).toFixed(1)}%`}function fm(e){return e.id===`other`?`기타 기기`:e.hostname?e.hostname:e.identified?`이름 없는 기기`:`미식별 기기`}function pm(e){return[...e].sort((e,t)=>e.id===`other`?1:t.id===`other`?-1:t.blocked-e.blocked||t.queries-e.queries||fm(e).localeCompare(fm(t),`ko`))}function mm({devices:e,deviceLimit:t,truncated:n}){let r=pm(e),[i,a]=F(!1),[o,s]=F(1),c=Math.max(1,Math.ceil(r.length/um)),l=Math.min(o,c),u=(l-1)*um,d=Math.min(u+um,r.length),f=i?r.slice(u,d):r.slice(0,lm),p=r.length>lm;return I(()=>{s(e=>Math.min(e,c))},[c]),L(`div`,{class:`mt-6 rounded-2xl border border-slate-100 bg-slate-50/70 p-4 sm:p-5`,children:[L(`div`,{class:`flex flex-wrap items-start justify-between gap-3`,children:[L(`div`,{children:[L(`div`,{class:`flex flex-wrap items-center gap-2`,children:[L(`p`,{class:`m-0 text-xs font-bold text-slate-500`,children:`기기별 통계`}),p&&!i?L(`span`,{class:`inline-flex items-center rounded-full bg-teal-50 px-2 py-1 text-[10px] font-extrabold tracking-wide text-teal-700 ring-1 ring-teal-100`,children:`차단 TOP 3`}):null]}),L(`h3`,{class:`mt-1 mb-0 text-base font-extrabold text-slate-950`,children:`어떤 기기에서 차단되었는지 확인`}),L(`p`,{class:`mt-2 mb-0 text-xs leading-5 text-slate-500`,children:`DHCP 정보가 있는 기기는 MAC 주소를 기준으로 식별하며, 기기별 숫자만 현재 부팅 세션 동안 메모리에 집계합니다.`})]}),L(`div`,{class:`flex flex-wrap items-center justify-end gap-2`,children:[L(`span`,{class:`inline-flex items-center gap-2 rounded-full bg-white px-3 py-1.5 text-xs font-extrabold text-slate-600 ring-1 ring-slate-200`,children:[L(Ge,{class:`size-4 text-teal-700`}),H(r.length),`개 항목`]}),p?L(`button`,{"aria-controls":`safeshield-device-statistics-list`,"aria-expanded":i,class:`rounded-full bg-white px-3 py-1.5 text-xs font-extrabold text-teal-700 ring-1 ring-slate-200 transition hover:bg-slate-50`,onClick:()=>{a(e=>!e),s(1)},type:`button`,children:i?`간단히 보기 ↑`:`전체 ${H(r.length)}개 기기 보기 ↓`}):null]})]}),r.length===0?L(`p`,{class:`mt-4 mb-0 rounded-xl bg-white p-4 text-sm leading-6 text-slate-500 ring-1 ring-slate-100`,children:`아직 기기별 DNS 통계가 없습니다. 클라이언트에서 DNS 요청이 발생하면 다음 통계 갱신 시 표시됩니다.`}):L(`div`,{class:`mt-4 grid gap-3`,id:`safeshield-device-statistics-list`,children:f.map(e=>L(`article`,{class:`grid gap-4 rounded-xl bg-white p-4 ring-1 ring-slate-100 sm:grid-cols-[minmax(0,1fr)_auto] sm:items-center`,children:[L(`div`,{class:`min-w-0`,children:L(`div`,{class:`flex min-w-0 flex-wrap items-center gap-2`,children:[L(`span`,{class:`grid size-9 shrink-0 place-items-center rounded-lg bg-teal-50 text-teal-700`,children:L(Ge,{class:`size-4`})}),L(`div`,{class:`min-w-0`,children:[L(`h4`,{class:`m-0 truncate text-sm font-extrabold text-slate-950`,children:fm(e)}),L(`p`,{class:`mt-1 mb-0 break-all text-xs text-slate-500`,children:[e.ip||`IP 미확인`,e.mac?` · ${e.mac}`:``]})]}),L(`span`,{class:`rounded-full px-2 py-1 text-[11px] font-extrabold ${e.identified?`bg-emerald-50 text-emerald-700`:`bg-slate-100 text-slate-600`}`,children:e.id===`other`?`합산`:e.identified?`DHCP 식별`:`IP 임시 식별`})]})}),L(`dl`,{class:`m-0 grid grid-cols-3 gap-2 text-right`,children:[L(`div`,{class:`min-w-20`,children:[L(`dt`,{class:`text-[11px] font-bold text-slate-400`,children:`DNS 요청`}),L(`dd`,{class:`mt-1 mb-0 ml-0 text-sm font-extrabold text-slate-900`,children:H(e.queries)})]}),L(`div`,{class:`min-w-20`,children:[L(`dt`,{class:`text-[11px] font-bold text-slate-400`,children:`차단`}),L(`dd`,{class:`mt-1 mb-0 ml-0 text-sm font-extrabold text-teal-700`,children:H(e.blocked)})]}),L(`div`,{class:`min-w-20`,children:[L(`dt`,{class:`text-[11px] font-bold text-slate-400`,children:`차단율`}),L(`dd`,{class:`mt-1 mb-0 ml-0 text-sm font-extrabold text-slate-900`,children:dm(e.queries,e.blocked)})]})]})]},e.id))}),p&&!i?L(`p`,{class:`mt-3 mb-0 text-xs font-semibold leading-5 text-slate-500`,children:[`전체 `,H(r.length),`개 기기 중 차단 수 기준 상위 `,lm,`개를 표시하고 있습니다.`]}):null,i&&r.length>um?L(`nav`,{"aria-label":`기기별 통계 페이지`,class:`mt-4 flex flex-wrap items-center justify-between gap-3`,children:[L(`p`,{class:`m-0 text-xs font-semibold text-slate-500`,children:[H(u+1),`–`,H(d),` /`,` `,H(r.length),`개`]}),L(`div`,{class:`flex items-center gap-2`,children:[L(`button`,{class:`rounded-lg bg-white px-3 py-2 text-xs font-extrabold text-slate-700 ring-1 ring-slate-200 transition hover:bg-slate-50 disabled:cursor-not-allowed disabled:opacity-50`,disabled:l<=1,onClick:()=>s(e=>Math.max(1,e-1)),type:`button`,children:`이전`}),L(`span`,{"aria-current":`page`,class:`min-w-20 text-center text-xs font-extrabold text-slate-600`,children:[H(l),` / `,H(c)]}),L(`button`,{class:`rounded-lg bg-white px-3 py-2 text-xs font-extrabold text-slate-700 ring-1 ring-slate-200 transition hover:bg-slate-50 disabled:cursor-not-allowed disabled:opacity-50`,disabled:l>=c,onClick:()=>s(e=>Math.min(c,e+1)),type:`button`,children:`다음`})]})]}):null,n?L(`p`,{class:`mt-4 mb-0 rounded-xl border border-amber-200 bg-amber-50 p-3 text-xs font-semibold leading-5 text-amber-800`,children:[`개별 통계는 최대 `,H(t),`개 기기까지 추적합니다. 한도를 초과한 기기의 요청은 기타 기기로 합산됩니다.`]}):null]})}var hm=3600,gm=24;function _m(e,t){return e<=0?`0.0%`:`${(t/e*100).toFixed(1)}%`}function vm(e){let t=e.hourly[e.hourly.length-1];if(t?.bucketStart)return t.bucketStart;let n=e.updatedAt>0?e.updatedAt:Math.floor(Date.now()/1e3);return Math.floor(n/hm)*hm}function ym(e){let t=new Map(e.hourly.map(e=>[e.bucketStart,e])),n=vm(e)-23*hm;return Array.from({length:gm},(e,r)=>{let i=n+r*hm;return t.get(i)??{bucketStart:i,queries:0,blocked:0}})}function bm({emphasized:e=!1,label:t,value:n}){return L(`div`,{class:`rounded-xl border border-slate-100 bg-slate-50 p-4 sm:p-5`,children:[L(`dt`,{class:`text-[0.68rem] font-black uppercase tracking-[0.12em] text-slate-400`,children:t}),L(`dd`,{class:`mt-2 mb-0 ml-0 text-2xl font-black tracking-tight ${e?`text-teal-700`:`text-slate-950`}`,children:n})]})}function xm({action:e,data:t,error:n,loading:r,onRetry:i,onSetEnabled:a,refreshing:o}){if(r&&t===null)return L(`section`,{class:`mt-5 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm shadow-slate-900/5 sm:p-6`,children:L(`p`,{class:`m-0 text-sm font-bold text-slate-500`,children:`차단 통계를 불러오는 중입니다…`})});if(n&&t===null)return L(`section`,{class:`mt-5 rounded-2xl border border-amber-200 bg-amber-50 p-5 sm:p-6`,children:[L(`p`,{class:`m-0 text-sm font-extrabold text-amber-950`,children:`차단 통계를 불러오지 못했습니다.`}),L(`p`,{class:`mt-2 mb-0 text-xs leading-5 text-amber-800`,children:n}),L(`button`,{class:`mt-4 inline-flex min-h-9 items-center justify-center rounded-xl border border-amber-300 bg-white px-3 py-2 text-xs font-extrabold text-amber-900 transition hover:bg-amber-100 focus:outline-none focus-visible:ring-4 focus-visible:ring-amber-100`,onClick:i,type:`button`,children:`다시 시도`})]});if(!t)return L(`section`,{class:`mt-5 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm shadow-slate-900/5 sm:p-6`,children:[L(`p`,{class:`m-0 text-xs font-bold uppercase tracking-[0.16em] text-slate-500`,children:`Activity`}),L(`h2`,{class:`mt-3 mb-0 text-xl font-extrabold text-slate-950`,children:`보호 활동`}),L(`p`,{class:`mt-3 mb-0 text-sm leading-6 text-slate-600`,children:`현재 SafeShield에서 통계 데이터를 제공하지 않습니다. SafeShield 0.3.15 버전 이상이 설치되어 있고, 통계 수집이 활성화되어 있는지 확인해 주세요.`})]});let s=ym(t),c=s[s.length-1],l=s.reduce((e,t)=>({queries:e.queries+t.queries,blocked:e.blocked+t.blocked}),{queries:0,blocked:0}),u=e===`statistics-enable`||e===`statistics-disable`,d=e===`statistics-enable`||e!==`statistics-disable`&&t.enabled,f=u?d?`활성화하는 중…`:`비활성화하는 중…`:t.enabled?t.collectorRunning?`활성화 · 수집 중`:`활성화 · 시작 중`:`비활성화`;return L(`section`,{"aria-busy":u,class:`mt-5 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm shadow-slate-900/5 sm:p-6 ${u?`cursor-wait`:``}`,children:[L(`div`,{class:`flex flex-wrap items-start justify-between gap-4`,children:[L(`div`,{children:[L(`p`,{class:`m-0 text-[0.68rem] font-black uppercase tracking-[0.18em] text-teal-700`,children:`Activity`}),L(`h2`,{class:`mt-2 mb-0 text-xl font-black tracking-tight text-slate-950`,children:`최근 24시간 보호 활동`}),L(`p`,{class:`mt-2 mb-0 text-sm leading-6 text-slate-500`,children:`DNS 요청 원본은 저장하지 않고 숫자만 로컬 메모리에 집계합니다.`})]}),L(`div`,{class:`flex items-center gap-3 rounded-xl border border-slate-100 bg-slate-50 px-3 py-2.5`,children:[L(`span`,{class:`grid size-9 place-items-center rounded-lg bg-slate-100 text-slate-500`,children:L(at,{class:`size-5`})}),L(`div`,{class:`text-right`,children:[L(`p`,{class:`m-0 text-[0.68rem] font-bold text-slate-400`,children:`통계 수집`}),L(`p`,{class:`mt-1 mb-0 text-xs font-extrabold ${u||d?`text-emerald-700`:`text-slate-500`}`,role:`status`,children:f})]}),L(`button`,{"aria-checked":d,"aria-label":d?`차단 통계 수집 끄기`:`차단 통계 수집 켜기`,class:`ssh-switch-control relative inline-flex shrink-0 items-center rounded-full border-0 p-0 transition focus:outline-none focus-visible:ring-4 focus-visible:ring-teal-100 ${u?`cursor-wait opacity-80`:`disabled:cursor-not-allowed disabled:opacity-50`} ${d?`bg-teal-600`:`bg-slate-300`}`,disabled:e!==null,onClick:()=>a(!t.enabled),role:`switch`,type:`button`,children:L(`span`,{"aria-hidden":`true`,class:`ssh-switch-thumb absolute top-1 grid place-items-center shadow-sm transition-all ${d?`left-6`:`left-1`}`,children:u?L(z,{"aria-hidden":`true`,class:`size-3 animate-spin text-teal-600`}):null})})]})]}),t.enabled?null:L(`div`,{class:`mt-5 rounded-xl border border-slate-200 bg-slate-50 px-4 py-3 text-sm leading-6 text-slate-600`,children:`통계 수집이 꺼져 있습니다. 활성화하면 DNS 요청 원본은 저장하지 않고 요청 수와 차단 수만 로컬 메모리에 집계합니다.`}),L(`dl`,{class:`mt-6 grid gap-3 sm:grid-cols-2 xl:grid-cols-4`,children:[L(bm,{label:`최근 24시간 DNS 요청`,value:H(l.queries)}),L(bm,{emphasized:!0,label:`최근 24시간 차단`,value:H(l.blocked)}),L(bm,{label:`최근 24시간 차단율`,value:_m(l.queries,l.blocked)}),L(bm,{label:`현재 시간 차단`,value:H(c?.blocked??0)})]}),L(`div`,{class:`mt-5 rounded-2xl border border-slate-100 bg-slate-50/70 p-4 sm:p-5`,children:[L(`div`,{class:`flex flex-wrap items-end justify-between gap-3`,children:[L(`div`,{children:[L(`p`,{class:`m-0 text-xs font-bold text-slate-500`,children:`최근 24시간`}),L(`h3`,{class:`mt-1 mb-0 text-base font-extrabold text-slate-950`,children:`시간대별 차단 요청`})]}),L(`span`,{class:`text-xs font-bold text-slate-500`,children:o?`갱신 중…`:`마지막 집계 ${Ut(t.updatedAt)}`})]}),L(Hp,{buckets:s})]}),L(mm,{deviceLimit:t.deviceLimit,devices:t.devices,truncated:t.devicesTruncated}),L(`div`,{class:`mt-5 flex flex-wrap gap-x-5 gap-y-2 border-t border-slate-100 pt-4 text-xs leading-5 text-slate-500`,children:[L(`span`,{children:[`수집 누적 DNS 요청 `,H(t.totals.queries)]}),L(`span`,{children:[`수집 누적 차단 `,H(t.totals.blocked)]}),L(`span`,{children:[`수집 시작 `,Ut(t.startedAt)]}),L(`span`,{children:[`보존 `,H(t.retentionHours),`시간`]}),t.volatile?L(`span`,{children:`재부팅 시 초기화되는 메모리 통계`}):null]}),n?L(`p`,{class:`mt-3 mb-0 text-xs font-bold text-amber-700`,children:`최근 자동 갱신에 실패했습니다. 현재 표시된 마지막 정상 통계를 유지합니다.`}):null]})}var Sm=[{label:`갱신 준비`,description:`차단 목록 갱신을 준비하고 있습니다.`,stages:[`starting`,`boot_delay`,`boot_refresh`,`boot_refresh_skipped`,`scheduled_refresh`,`init`]},{label:`최신 차단 목록 확인`,description:`서버에서 최신 차단 목록 정보를 확인하고 있습니다.`,stages:[`resolve_api`]},{label:`차단 목록 다운로드`,description:`최신 차단 목록을 안전하게 다운로드하고 있습니다.`,stages:[`download_artifact`]},{label:`사용자 규칙 적용`,description:`허용·차단 사용자 규칙을 새 차단 목록에 반영하고 있습니다.`,stages:[`local_overrides`,`merge`]},{label:`보호 규칙 적용`,description:`새 차단 규칙을 설치하고 DNS 보호 서비스를 갱신하고 있습니다.`,stages:[`install`,`restart_dnsmasq`]},{label:`보호 상태 확인`,description:`새 차단 목록과 DNS 보호가 정상적으로 동작하는지 확인하고 있습니다.`,stages:[`runtime_check`,`blocklist_verify`]}],Cm=new Map;Sm.forEach((e,t)=>{e.stages.forEach(e=>Cm.set(e,t))});function wm(e,t){return e===`running`||t!==null&&Cm.has(t)}function Tm(e){let t=e===null?0:Cm.get(e)??0,n=Sm[t];return{description:n.description,label:n.label,number:t+1,total:Sm.length}}function Em(e){return(e??``).trim().toLowerCase()}function Dm(e,t){let n=Em(e);if(n.includes(`upgrade`)||n.includes(`version`))return`현재 SafeShield 버전으로는 갱신을 계속할 수 없습니다. 시스템 업데이트에서 SafeShield를 최신 버전으로 업데이트해 주세요.`;if(n.includes(`entitlement`)||n.includes(`license`)||n.includes(`unauthorized`)||n.includes(`forbidden`))return`차단 목록을 사용할 권한을 확인하지 못했습니다. SmartSafeHub 계정과 요금제 상태를 확인해 주세요.`;if(n.includes(`dnsmasq`)||n.includes(`restart`)||n.includes(`runtime`))return`DNS 보호 서비스를 정상적으로 갱신하지 못했습니다. 잠시 후 다시 갱신해 보고 문제가 계속되면 상태 점검 정보를 확인해 주세요.`;if(n.includes(`verify`)||n.includes(`sanity`))return`새 차단 목록을 적용한 뒤 보호 상태 확인을 완료하지 못했습니다. 다시 갱신해 주세요.`;if(n.includes(`download`)||n.includes(`fetch`)||n.includes(`timeout`)||n.includes(`network`)||n.includes(`wan`))return`차단 목록을 다운로드하지 못했습니다. 인터넷 연결을 확인한 뒤 다시 갱신해 주세요.`;if(n.includes(`resolve`)||n.includes(`manifest`)||n.includes(`artifact`)||n.includes(`api`))return`최신 차단 목록 정보를 확인하지 못했습니다. 인터넷 연결을 확인한 뒤 다시 갱신해 주세요.`;if(n.includes(`override`)||n.includes(`allowlist`)||n.includes(`merge`)||n.includes(`rule`))return`사용자 허용·차단 규칙을 새 차단 목록에 반영하지 못했습니다. 사용자 규칙을 확인한 뒤 다시 갱신해 주세요.`;if(n.includes(`install`)||n.includes(`write`)||n.includes(`space`))return`새 차단 규칙을 장치에 적용하지 못했습니다. 저장 공간과 SafeShield 상태를 확인한 뒤 다시 갱신해 주세요.`;let r=Tm(t);return r.number===2?`최신 차단 목록 정보를 확인하는 중 문제가 발생했습니다. 인터넷 연결을 확인한 뒤 다시 갱신해 주세요.`:r.number===3?`차단 목록을 다운로드하는 중 문제가 발생했습니다. 인터넷 연결을 확인한 뒤 다시 갱신해 주세요.`:r.number===4?`사용자 규칙을 적용하는 중 문제가 발생했습니다. 사용자 규칙을 확인한 뒤 다시 갱신해 주세요.`:r.number===5?`새 보호 규칙을 적용하는 중 문제가 발생했습니다. 잠시 후 다시 갱신해 주세요.`:r.number===6?`새 차단 목록의 보호 상태를 확인하지 못했습니다. 다시 갱신해 주세요.`:`차단 목록 갱신을 완료하지 못했습니다. 잠시 후 다시 갱신해 주세요.`}var Om=`https://www.smartsafehub.com/pricing/`;function km({value:e,trueLabel:t=`정상`,falseLabel:n=`확인 필요`}){return L(`span`,{class:`inline-flex items-center gap-2 text-sm font-extrabold ${e?`text-emerald-700`:`text-slate-500`}`,children:[L(`span`,{class:`size-2 rounded-full ${e?`bg-emerald-500`:`bg-slate-300`}`}),e?t:n]})}function Am(e){return!e.enabled||e.status===`disabled`?`disabled`:e.status===`running`?`refreshing`:e.status===`error`||e.health.overall===`error`||e.issueCounts.errors>0?`error`:e.status===`paused`?`paused`:e.active&&e.runtime.dnsmasqRunning&&e.runtime.dnsRuntimeOk?`protecting`:`attention`}function jm(e){return!e.enabled||e.status===`disabled`?`비활성화`:e.active&&e.runtime.dnsmasqRunning&&e.runtime.dnsRuntimeOk?`보호 중`:e.status===`paused`?`일시 중지`:`확인 필요`}function Mm(e){let t=Am(e);return t===`error`&&jm(e)===`보호 중`&&e.runtime.lastErrorCode?`갱신 실패`:{disabled:`비활성화`,refreshing:`갱신 중`,protecting:`보호 중`,paused:`일시 중지`,error:`오류`,attention:`확인 필요`}[t]}function Nm(e){return e?`자동 최적화`:`확인되지 않음`}function Pm(e){let t=Kt(e);return t===null?`확인되지 않음`:L(Ar,{timestamp:t})}function Fm(e){let t=Am(e);return t===`protecting`?e.blocklist.validLineCount>0?`${H(e.blocklist.validLineCount)}개 도메인 차단 규칙으로 DNS를 보호하고 있습니다.`:e.blocklist.installed?`차단 목록이 설치되어 있으며 DNS 보호가 정상적으로 동작하고 있습니다.`:`SafeShield 서비스와 DNS 런타임이 정상적으로 동작하고 있습니다.`:t===`refreshing`?`차단 목록을 최신 상태로 갱신하고 있습니다.`:t===`disabled`?`SafeShield DNS 보호가 비활성화되어 있습니다.`:t===`paused`?`SafeShield 작업이 일시 중지되어 있습니다.`:t===`error`?jm(e)===`보호 중`?`기존 DNS 보호는 유지되고 있지만 최근 차단 목록 갱신을 완료하지 못했습니다.`:`SafeShield 보호 상태에 문제가 발생했습니다. 아래 오류 내용을 확인해 주세요.`:e.active?e.runtime.dnsmasqRunning?e.runtime.dnsRuntimeOk?e.summary.message:`SafeShield DNS 런타임 상태를 확인해야 합니다.`:`dnsmasq가 실행 중이 아니어서 DNS 보호 상태를 확인해야 합니다.`:`SafeShield가 활성화되어 있지만 서비스가 실행 중이 아닙니다.`}function Im(e){return{ok:`정상`,warning:`경고`,error:`오류`,unavailable:`사용 불가`}[e]??e}function Lm({data:e}){let t=Am(e);return L(`span`,{class:`inline-flex items-center rounded-full px-3 py-1.5 text-xs font-extrabold ring-1 ring-inset ${t===`error`?`bg-red-50 text-red-700 ring-red-200`:t===`disabled`?`bg-slate-100 text-slate-700 ring-slate-200`:t===`refreshing`?`bg-blue-50 text-blue-700 ring-blue-200`:t===`paused`||t===`attention`?`bg-amber-50 text-amber-800 ring-amber-200`:`bg-emerald-50 text-emerald-700 ring-emerald-200`}`,children:Mm(e)})}function Rm({failed:e,step:t,total:n}){let r=2*Math.PI*17,i=r*.22,a=r-i;return L(`span`,{"aria-label":e?`갱신 실패 · ${t}/${n} 단계`:`갱신 ${t}/${n} 단계`,class:`ssh-safeshield-refresh-donut`,"data-state":e?`error`:`active`,role:`progressbar`,"aria-valuemin":0,"aria-valuemax":n,"aria-valuenow":t,children:[L(`svg`,{"aria-hidden":`true`,class:e?`ssh-safeshield-refresh-loader-svg ssh-safeshield-refresh-loader-svg-error`:`ssh-safeshield-refresh-loader-svg`,viewBox:`0 0 48 48`,children:[L(`circle`,{class:`ssh-safeshield-refresh-loader-track`,cx:`24`,cy:`24`,fill:`none`,r:17,stroke:`currentColor`,strokeWidth:2.5}),e?null:L(`circle`,{class:`ssh-safeshield-refresh-loader-arc`,cx:`24`,cy:`24`,fill:`none`,r:17,stroke:`currentColor`,strokeDasharray:`${i} ${a}`,strokeLinecap:`round`,strokeWidth:3.5})]}),L(`span`,{class:`ssh-safeshield-refresh-donut-label`,children:e?`!`:`${t}/${n}`})]})}function zm({data:e}){let t=wm(e.status,e.stage),n=Am(e)===`error`;if(!t&&!n)return null;let r=Tm(e.stage),i=n?Dm(e.runtime.lastErrorCode,e.stage):null;return L(`div`,{"aria-live":n?`assertive`:`polite`,class:`ssh-safeshield-refresh`,"data-state":n?`error`:`active`,role:n?`alert`:`status`,children:[L(Rm,{failed:n,step:r.number,total:r.total}),L(`div`,{class:`ssh-safeshield-refresh-copy`,children:[L(`p`,{class:`ssh-safeshield-refresh-kicker`,children:n?`갱신 실패 · ${r.number}/${r.total}`:`갱신 ${r.number}/${r.total}`}),L(`p`,{class:`ssh-safeshield-refresh-title`,children:n?`${r.label} 단계에서 문제가 발생했습니다.`:r.label}),L(`p`,{class:`ssh-safeshield-refresh-description`,children:i??r.description}),n&&e.runtime.lastErrorCode?L(`p`,{class:`ssh-safeshield-refresh-error-code`,children:[`오류 코드: `,L(`code`,{children:e.runtime.lastErrorCode})]}):null]})]})}function Bm({label:e,value:t}){return L(`div`,{class:`bg-white px-5 py-4 sm:px-6`,children:[L(`dt`,{class:`text-[0.68rem] font-black uppercase tracking-[0.14em] text-slate-400`,children:e}),L(`dd`,{class:`mt-2 mb-0 ml-0 text-sm font-extrabold text-slate-950`,title:typeof t==`string`?t:void 0,children:t})]})}function Vm(e){return e?.trim().toUpperCase()||`FREE`}function Hm(e){return e===`FREE`?`free`:e===`PRO`?`pro`:e===`ULTIMATE`?`ultimate`:`paid`}function Um({compact:e=!1,plan:t}){let n=Hm(t),r=n!==`free`;return L(`span`,{class:`ssh-safeshield-plan-badge`,"data-compact":e?`true`:`false`,"data-tier":n,title:r?`${t} 멤버십`:`무료 플랜`,children:[L(`span`,{"aria-hidden":`true`,class:`ssh-safeshield-plan-badge-mark`,children:r?`✦`:`•`}),L(`span`,{children:t})]})}function Wm({plan:e}){let t=Hm(e)===`free`;return L(`div`,{class:`bg-white px-5 py-4 sm:px-6`,children:[L(`dt`,{class:`text-[0.68rem] font-black uppercase tracking-[0.14em] text-slate-400`,children:`Plan`}),L(`dd`,{class:`mt-2 mb-0 ml-0 flex min-w-0 flex-wrap items-center gap-2`,children:[L(Um,{plan:e}),t?L(`span`,{class:`ssh-safeshield-plan-caption`,children:`기본 플랜`}):null]})]})}function Gm(){return L(`div`,{class:`ssh-safeshield-upgrade-card`,children:[L(`span`,{"aria-hidden":`true`,class:`ssh-safeshield-upgrade-mark`,children:`✦`}),L(`div`,{class:`ssh-safeshield-upgrade-copy`,children:[L(`div`,{class:`ssh-safeshield-upgrade-meta`,children:[L(`span`,{children:`SmartSafeHub Membership`}),L(`span`,{class:`ssh-safeshield-upgrade-soon`,children:`Coming soon`})]}),L(`strong`,{children:`더 강력한 보호 기능을 준비하고 있습니다.`}),L(`span`,{children:`프리미엄 보호와 확장된 관리 기능을 미리 확인해 보세요.`})]}),L(`a`,{class:`ssh-safeshield-upgrade-action`,href:Om,rel:`noopener noreferrer`,target:`_blank`,children:[`요금제 보기`,L(`span`,{"aria-hidden":`true`,children:`→`})]})]})}function Km({description:e,eyebrow:t,title:n}){return L(`div`,{class:`mb-4 flex flex-wrap items-end justify-between gap-3`,children:L(`div`,{children:[L(`p`,{class:`m-0 text-[0.68rem] font-black uppercase tracking-[0.18em] text-teal-700`,children:t}),L(`h2`,{class:`mt-2 mb-0 text-xl font-black tracking-tight text-slate-950`,children:n}),L(`p`,{class:`mt-2 mb-0 text-sm leading-6 text-slate-500`,children:e})]})})}function qm({children:e,eyebrow:t,icon:n,title:r}){return L(`article`,{class:`rounded-2xl border border-slate-200 bg-white p-5 shadow-sm shadow-slate-900/5 sm:p-6`,children:[L(`div`,{class:`flex items-start justify-between gap-4`,children:[L(`div`,{children:[L(`p`,{class:`m-0 text-[0.68rem] font-black uppercase tracking-[0.16em] text-slate-400`,children:t}),L(`h3`,{class:`mt-2 mb-0 text-lg font-black tracking-tight text-slate-950`,children:r})]}),L(`span`,{class:`grid size-10 shrink-0 place-items-center rounded-xl bg-slate-100 text-slate-500`,children:n})]}),L(`dl`,{class:`mt-5 mb-0 grid gap-3`,children:e})]})}function Jm({label:e,value:t}){return L(`div`,{class:`flex min-w-0 items-center justify-between gap-4 border-b border-slate-100 pb-3 last:border-b-0 last:pb-0`,children:[L(`dt`,{class:`text-sm font-semibold text-slate-500`,children:e}),L(`dd`,{class:`m-0 min-w-0 text-right text-sm font-extrabold text-slate-950`,children:t})]})}function Ym({error:e,message:t,onDismiss:n}){if(!e&&!t)return null;let r=e!==null;return L(`div`,{"aria-live":r?`assertive`:`polite`,class:`mt-4 flex flex-col items-start gap-3 rounded-2xl border px-4 py-3 text-sm sm:flex-row sm:justify-between ${r?`border-red-200 bg-red-50 text-red-900`:`border-emerald-200 bg-emerald-50 text-emerald-900`}`,role:r?`alert`:`status`,children:[L(`div`,{class:`flex min-w-0 items-start gap-3`,children:[L(r?V:B,{class:`mt-0.5 size-5 shrink-0`}),L(`p`,{class:`m-0 font-semibold leading-6`,children:e??t})]}),L(`button`,{"aria-label":`알림 닫기`,class:`shrink-0 rounded-lg border-0 bg-transparent px-2 py-1 font-black text-current opacity-60 transition hover:opacity-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-current`,onClick:n,type:`button`,children:`×`})]})}function Xm({action:e,actionError:t,actionMessage:n,data:r,error:i,loading:a,statistics:o,statisticsError:s,statisticsLoading:c,statisticsRefreshing:l,onDismissFeedback:u,onRefreshBlocklist:d,onRetry:f,onRetryStatistics:p,onSetEnabled:m,onSetStatisticsEnabled:h}){let[g,_]=F(null);if(I(()=>{r?.available&&r.blocklist.validLineCount>0&&!wm(r.status,r.stage)&&_(r.blocklist.validLineCount)},[r?.available,r?.blocklist.validLineCount,r?.stage,r?.status]),a)return L(Rn,{});if(i)return L(zn,{message:i,onRetry:f});if(!r)return L(Rn,{});if(!r.available)return L(`section`,{class:`rounded-2xl border border-amber-200 bg-amber-50 p-5 sm:p-6`,children:[L(`span`,{class:`grid size-11 place-items-center rounded-xl bg-amber-100 text-amber-800`,children:L(V,{class:`size-6`})}),L(`h2`,{class:`mt-5 mb-0 text-xl font-extrabold text-amber-950`,children:`SafeShield 상태 API를 찾을 수 없습니다`}),L(`p`,{class:`mt-2 mb-0 text-sm leading-6 text-amber-900`,children:`safeshield 패키지와 rpcd ucode 플러그인이 설치되어 있는지 확인해 주세요.`})]});let y=r.enabled,b=Vm(r.entitlement.plan),x=r.status===`running`,S=e!==null,C=(wm(r.status,r.stage)||Am(r)===`error`)&&r.blocklist.validLineCount===0&&g!==null?g:r.blocklist.validLineCount,w=y?e===`disable`?`끄는 중…`:`보호 끄기`:e===`enable`?`켜는 중…`:`보호 켜기`;function T(){(!y||window.confirm(`보호를 끄면, 현재 차단 목록이 제거되어 위험에 노출될 수 있습니다. 그래도 계속하시겠습니까?`))&&m(!y)}return L(v,{children:[L(`section`,{class:`overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm shadow-slate-900/5`,children:[L(`div`,{class:`flex flex-col gap-5 p-5 sm:p-6 lg:flex-row lg:items-start lg:justify-between`,children:[L(`div`,{class:`flex min-w-0 items-start gap-3 sm:gap-4`,children:[L(`span`,{class:`grid size-12 shrink-0 place-items-center rounded-2xl bg-teal-50 text-teal-700`,children:L(Ue,{class:`size-7`})}),L(`div`,{class:`min-w-0`,children:[L(`p`,{class:`m-0 text-[0.68rem] font-black uppercase tracking-[0.18em] text-teal-700`,children:`Protection`}),L(`div`,{class:`mt-2 flex flex-wrap items-center gap-3`,children:[L(`h2`,{class:`m-0 break-words text-xl font-black tracking-tight text-slate-950 sm:text-2xl`,children:`SafeShield 보호 상태`}),L(Lm,{data:r})]}),L(`p`,{class:`mt-3 mb-0 max-w-3xl text-sm leading-6 text-slate-600`,children:Fm(r)}),L(zm,{data:r})]})]}),L(`div`,{class:`flex w-full flex-col gap-2 sm:w-auto sm:flex-row`,children:[L(`button`,{class:`inline-flex min-h-11 items-center justify-center gap-2 rounded-xl border px-4 py-2.5 text-sm font-extrabold transition focus:outline-none focus-visible:ring-4 disabled:cursor-not-allowed disabled:opacity-50 ${r.enabled?`border-red-200 bg-white text-red-700 hover:border-red-300 hover:bg-red-50 focus-visible:ring-red-100`:`border-teal-700 bg-teal-700 text-white hover:border-teal-800 hover:bg-teal-800 focus-visible:ring-teal-100`}`,disabled:S||x,onClick:T,type:`button`,children:[L(rt,{class:`size-4 ${e===`enable`||e===`disable`?`animate-pulse`:``}`}),w]}),L(`button`,{class:`inline-flex min-h-11 items-center justify-center gap-2 rounded-xl border border-slate-200 bg-white px-4 py-2.5 text-sm font-extrabold text-slate-700 transition hover:border-teal-300 hover:bg-teal-50 hover:text-teal-800 focus:outline-none focus-visible:ring-4 focus-visible:ring-teal-100 disabled:cursor-not-allowed disabled:opacity-50`,disabled:!r.enabled||S||x,onClick:d,type:`button`,children:[L(it,{class:`size-4 ${e===`refresh`||x?`animate-bounce`:``}`}),e===`refresh`?`시작 중…`:x?`갱신 중…`:r.status===`error`?`다시 갱신`:`지금 갱신`]})]})]}),L(`div`,{class:`px-5 pb-5 sm:px-6 sm:pb-6`,children:[L(`dl`,{class:`grid overflow-hidden rounded-xl border border-slate-200 bg-slate-200 sm:grid-cols-2 xl:grid-cols-4`,children:[L(Bm,{label:`SafeShield`,value:r.version??`확인되지 않음`}),L(Bm,{label:`Blocklist`,value:`${H(C)}개 도메인`}),L(Bm,{label:`Last refresh`,value:L(Ar,{timestamp:r.timestamps.lastSuccess})}),L(Wm,{plan:b})]}),b===`FREE`?L(Gm,{}):null]})]}),L(Ym,{error:t,message:n,onDismiss:u}),L(xm,{action:e,data:o,error:s,loading:c,onRetry:p,onSetEnabled:h,refreshing:l}),L(`section`,{class:`mt-7`,children:[L(Km,{description:`DNS 보호 런타임, 차단 목록, 갱신 일정과 상태 점검 결과를 한곳에서 확인합니다.`,eyebrow:`Protection details`,title:`보호 구성`}),L(`div`,{class:`grid gap-4 lg:grid-cols-2`,children:[L(qm,{eyebrow:`Runtime`,icon:L(B,{class:`size-5`}),title:`DNS 보호 런타임`,children:[L(Jm,{label:`SafeShield 서비스`,value:L(km,{falseLabel:`중지됨`,trueLabel:`동작 중`,value:r.active})}),L(Jm,{label:`DNS 서비스`,value:L(km,{falseLabel:`중지됨`,value:r.runtime.dnsmasqRunning})}),L(Jm,{label:`SafeShield DNS 연동`,value:L(km,{value:r.runtime.dnsRuntimeOk})}),L(Jm,{label:`차단 목록 갱신`,value:L(km,{falseLabel:`중지됨`,value:r.runtime.refreshdRunning})})]}),L(qm,{eyebrow:`Blocklist`,icon:L(at,{class:`size-5`}),title:`차단 목록`,children:[L(Jm,{label:`적용 규칙`,value:`${H(r.blocklist.validLineCount)}개 도메인`}),L(Jm,{label:`파일 크기`,value:Bt(r.blocklist.fileSizeKb*1024)}),L(Jm,{label:`설치 상태`,value:L(km,{falseLabel:`미설치`,trueLabel:`설치됨`,value:r.blocklist.installed})}),L(Jm,{label:`검증 상태`,value:L(km,{falseLabel:`확인 필요`,value:r.blocklist.verificationOk})})]}),L(qm,{eyebrow:`Refresh schedule`,icon:L(lt,{class:`size-5`}),title:`차단 목록 갱신`,children:[L(Jm,{label:`마지막 성공`,value:L(Ar,{timestamp:r.timestamps.lastSuccess})}),L(Jm,{label:`다음 갱신`,value:Ut(r.timestamps.nextRefreshAt)}),L(Jm,{label:`갱신 주기`,value:qt(r.timestamps.refreshIntervalS)}),L(Jm,{label:`최근 결과`,value:r.runtime.lastResult||(x?`갱신 중`:`확인되지 않음`)})]}),L(qm,{eyebrow:`Health`,icon:L(Ue,{class:`size-5`}),title:`상태 점검`,children:[L(Jm,{label:`전체 상태`,value:Im(r.health.overall)}),L(Jm,{label:`경고`,value:L(`span`,{class:`text-amber-700`,children:H(r.issueCounts.warnings)})}),L(Jm,{label:`오류`,value:L(`span`,{class:`text-red-700`,children:H(r.issueCounts.errors)})}),L(Jm,{label:`최근 오류 코드`,value:r.runtime.lastErrorCode||`없음`})]})]})]}),L(`section`,{class:`mt-7`,children:[L(Km,{description:`현재 적용 중인 보호 데이터와 사용자 규칙을 관리합니다.`,eyebrow:`Settings`,title:`SafeShield 설정`}),L(`div`,{class:`grid gap-4 lg:grid-cols-2`,children:[L(`article`,{class:`rounded-2xl border border-slate-200 bg-white p-5 shadow-sm shadow-slate-900/5`,children:[L(`div`,{class:`flex items-start justify-between gap-4`,children:[L(`div`,{class:`min-w-0`,children:[L(`p`,{class:`m-0 text-[0.68rem] font-black uppercase tracking-[0.16em] text-slate-400`,children:`Protection data`}),L(`div`,{class:`mt-2 flex flex-wrap items-center gap-2`,children:[L(`h3`,{class:`m-0 text-lg font-black tracking-tight text-slate-950`,children:`보호 데이터`}),L(`span`,{"aria-label":`보호 데이터 상태: ${r.artifact.resolved&&r.blocklist.installed&&r.blocklist.verificationOk?`최신`:`확인 필요`}`,class:`inline-flex items-center gap-1.5 rounded-full px-2.5 py-1 text-xs font-extrabold ${r.artifact.resolved&&r.blocklist.installed&&r.blocklist.verificationOk?`bg-emerald-50 text-emerald-700`:`bg-amber-50 text-amber-700`}`,children:[r.artifact.resolved&&r.blocklist.installed&&r.blocklist.verificationOk?L(B,{class:`size-3.5 shrink-0`}):L(V,{class:`size-3.5 shrink-0`}),r.artifact.resolved&&r.blocklist.installed&&r.blocklist.verificationOk?`최신`:`확인 필요`]})]}),L(`p`,{class:`mt-2 mb-0 text-sm leading-6 text-slate-500`,children:`SafeShield가 기기 성능에 맞춰 사용하는 보호 목록의 구성과 최신 상태를 확인합니다.`})]}),L(`span`,{class:`grid size-10 shrink-0 place-items-center rounded-xl bg-slate-100 text-slate-500`,children:L(at,{class:`size-5`})})]}),L(`dl`,{class:`mt-5 mb-0 grid grid-cols-2 gap-3`,children:[L(`div`,{class:`rounded-xl border border-slate-200 bg-slate-50 px-3 py-3`,children:[L(`dt`,{class:`text-xs font-bold text-slate-500`,children:`기기 최적화`}),L(`dd`,{class:`mt-1 mb-0 text-sm font-extrabold text-slate-950`,children:Nm(r.artifact.tier)})]}),L(`div`,{class:`rounded-xl border border-slate-200 bg-slate-50 px-3 py-3`,children:[L(`dt`,{class:`text-xs font-bold text-slate-500`,children:`마지막 업데이트`}),L(`dd`,{class:`mt-1 mb-0 text-sm font-extrabold leading-5 text-slate-950`,children:Pm(r.artifact.version)})]})]}),L(`details`,{class:`mt-4 rounded-xl border border-slate-200 bg-slate-50/70 px-4 py-3`,children:[L(`summary`,{class:`cursor-pointer text-sm font-extrabold text-slate-700`,children:`세부 정보`}),L(`dl`,{class:`mt-3 mb-0 grid gap-3 border-t border-slate-200 pt-3`,children:[L(Jm,{label:`데이터 프로필`,value:r.artifact.tier||`확인되지 않음`}),L(Jm,{label:`데이터 버전`,value:r.artifact.version||`확인되지 않음`})]})]})]}),L(`article`,{class:`rounded-2xl border border-slate-200 bg-white p-5 shadow-sm shadow-slate-900/5`,children:[L(`p`,{class:`m-0 text-[0.68rem] font-black uppercase tracking-[0.16em] text-slate-400`,children:`Custom rules`}),L(`h3`,{class:`mt-2 mb-0 text-lg font-black tracking-tight text-slate-950`,children:`사용자 규칙`}),L(`div`,{class:`mt-4`,children:L(km,{falseLabel:`비활성화`,trueLabel:`활성화`,value:r.localOverrides.enabled})}),L(`p`,{class:`mt-4 mb-0 text-sm font-semibold leading-6 text-slate-500`,children:`허용하거나 차단할 도메인을 직접 관리해 SafeShield 보호 정책에 반영합니다.`}),L(`a`,{class:`mt-5 inline-flex min-h-11 items-center justify-center gap-2 rounded-xl border border-teal-700 bg-teal-700 px-4 py-2 text-sm font-extrabold text-white no-underline shadow-sm transition hover:border-teal-800 hover:bg-teal-800 focus:outline-none focus-visible:ring-4 focus-visible:ring-teal-100`,href:`#rules`,children:[L(pt,{class:`size-4`}),`사용자 규칙 관리`]})]})]})]})]})}var Zm=Math.floor(Date.now()/1e3),Qm=3600,$m={generatedAt:Zm,devices:[{id:`demo-phone`,hostname:`거실 스마트폰`,mac:`02:00:00:00:00:01`,ipv4Address:`192.0.2.101`,connection:`wifi`,online:!0,leaseActive:!0,leaseExpiresAt:Zm+6*Qm,interface:`wlan0`,ssid:`SmartSafeHub`,radio:`radio0`,band:`5g`,bandLabel:`5 GHz`,signalDbm:-43,inactiveMs:250,connectedSeconds:8300},{id:`demo-laptop`,hostname:`작업용 노트북`,mac:`02:00:00:00:00:02`,ipv4Address:`192.0.2.102`,connection:`wifi`,online:!0,leaseActive:!0,leaseExpiresAt:Zm+8*Qm,interface:`wlan0`,ssid:`SmartSafeHub`,radio:`radio0`,band:`5g`,bandLabel:`5 GHz`,signalDbm:-57,inactiveMs:720,connectedSeconds:29200},{id:`demo-tv`,hostname:`거실 TV`,mac:`02:00:00:00:00:03`,ipv4Address:`192.0.2.103`,connection:`ethernet`,online:!0,leaseActive:!0,leaseExpiresAt:Zm+12*Qm,interface:`lan1`,ssid:null,radio:null,band:null,bandLabel:null,signalDbm:null,inactiveMs:null,connectedSeconds:14e4},{id:`demo-tablet`,hostname:`태블릿`,mac:`02:00:00:00:00:04`,ipv4Address:`192.0.2.104`,connection:`wifi`,online:!1,leaseActive:!1,leaseExpiresAt:null,interface:null,ssid:`SmartSafeHub`,radio:`radio1`,band:`2g`,bandLabel:`2.4 GHz`,signalDbm:null,inactiveMs:null,connectedSeconds:null}],totals:{known:4,online:3,wireless:2,ethernet:1,offline:1}},eh={schema:1,scope:`current_boot`,volatile:!0,maxEvents:100,cloud:{enabled:!1,phase:`disabled`,eligible:!1,plan:`free`,retentionDays:0,pendingEvents:0,lastAttemptAt:0,lastSuccessAt:0,lastUploadedCount:0,lastErrorCode:null,nextSyncAt:0},events:[{schema:1,eventId:`demo-event-1`,eventType:`safeshield.blocklist.updated`,severity:`success`,occurredAt:Zm-7200,deviceUuid:null,source:`safeshield`,metadata:{}},{schema:1,eventId:`demo-event-2`,eventType:`system.boot`,severity:`info`,occurredAt:Zm-86400,deviceUuid:null,source:`system`,metadata:{}}]},th={device:{hostname:`SmartRouter`,model:`ipTIME AX3000SM`,boardName:`iptime,ax3000sm`},software:{distribution:`OpenWrt`,version:`25.12`,revision:`demo`,kernel:`6.12`},runtime:{uptime:86400,localtime:Zm,load:[.08,.12,.05],memory:{total:268435456,free:83886080,shared:4194304,buffered:41943040,available:171966464,cached:41943040}},network:{available:!0,up:!0,protocol:`dhcp`,ipv4Address:`198.51.100.20`},activityHistory:eh},nh={lan:{address:`192.168.1.1`,prefixLength:24,netmask:`255.255.255.0`,subnet:`192.168.1.0/24`},dhcp:{enabled:!0,start:`192.168.1.100`,end:`192.168.1.249`,leaseTime:`12h`},wan:{connected:!0,protocol:`dhcp`,address:`198.51.100.20`,prefixLength:24,subnet:`198.51.100.0/24`},conflict:{detected:!1,wanSubnet:null},recommendation:null},rh={available:!0,version:`0.3.25`,enabled:!0,active:!0,status:`idle`,stage:null,summary:{label:`보호 중`,message:`DNS 보호가 정상적으로 동작하고 있습니다.`,severity:`info`},runtime:{refreshdRunning:!0,dnsmasqRunning:!0,dnsRuntimeOk:!0,lastResult:`success`,lastErrorCode:null},entitlement:{plan:`free`,status:`active`},artifact:{resolved:!0,tier:`light`,version:`20261010T010000Z`,uniqueDomains:48e3,rules:48e3},localOverrides:{enabled:!0,allowlistPath:`/etc/safeshield/allowlist`,blocklistPath:`/etc/safeshield/blocklist`},blocklist:{installed:!0,validLineCount:48e3,fileSizeKb:4700,verificationOk:!0},health:{overall:`ok`},issueCounts:{warnings:0,errors:0},timestamps:{lastAttempt:Zm-2*Qm,lastSuccess:Zm-2*Qm,lastFailure:0,lastLocalApply:Zm-2*Qm,lastLocalApplyFailure:0,nextRefreshAt:Zm+21*Qm,refreshIntervalS:82800}},ih=Math.floor(Zm/Qm)*Qm,ah=[{queries:351,blocked:18},{queries:297,blocked:15},{queries:243,blocked:11},{queries:216,blocked:9},{queries:203,blocked:8},{queries:230,blocked:10},{queries:324,blocked:17},{queries:567,blocked:35},{queries:783,blocked:54},{queries:864,blocked:61},{queries:756,blocked:50},{queries:702,blocked:43},{queries:635,blocked:36},{queries:580,blocked:30},{queries:608,blocked:33},{queries:689,blocked:40},{queries:837,blocked:54},{queries:1026,blocked:73},{queries:1256,blocked:99},{queries:1458,blocked:125},{queries:1512,blocked:135},{queries:1323,blocked:111},{queries:1026,blocked:75},{queries:648,blocked:39}],oh=Array.from({length:24},(e,t)=>{let n=ih-(23-t)*Qm,r=ah[new Date(n*1e3).getHours()];return{bucketStart:n,queries:r.queries,blocked:r.blocked}}),sh={enabled:!0,collectorRunning:!0,available:!0,schemaVersion:1,volatile:!0,startedAt:Zm-604800,updatedAt:Zm-30,retentionHours:168,totals:{queries:oh.reduce((e,t)=>e+t.queries,0),blocked:oh.reduce((e,t)=>e+t.blocked,0)},hourly:oh,deviceLimit:128,devicesTruncated:!1,devices:[{id:`demo-phone`,mac:`02:00:00:00:00:01`,ip:`192.0.2.101`,hostname:`거실 스마트폰`,identified:!0,queries:3400,blocked:470},{id:`demo-laptop`,mac:`02:00:00:00:00:02`,ip:`192.0.2.102`,hostname:`작업용 노트북`,identified:!0,queries:2900,blocked:345},{id:`demo-tv`,mac:`02:00:00:00:00:03`,ip:`192.0.2.103`,hostname:`거실 TV`,identified:!0,queries:1200,blocked:185}]},ch={schema:1,generatedAt:Zm,overall:`ok`,summary:{message:`모든 주요 항목이 정상입니다.`,total:3,ok:3,warning:0,critical:0,unknown:0},metrics:{memoryAvailablePercent:64,load1m:.08,overlayAvailablePercent:80},checks:[],reporter:{enabled:!1,eligible:!1,plan:`free`,entitlementStatus:`active`,lastReportAt:0,nextReportAt:0,lastResult:``,lastErrorCode:null}},lh={phase:`idle`,updateCount:0,packages:[],lastCheckAt:Zm-Qm,lastInstallAt:null,lastError:null,releaseNotes:[],releaseNotesComplete:!0,settings:{channel:`stable`,checkEnabled:!0,checkIntervalSeconds:21600,autoInstall:!0,autoInstallTime:`03:00`,repositoryHost:`example.invalid`,updatePackage:`luci-app-smartsafehub`}},uh={phase:`idle`,source:null,lastCheckAt:Zm-Qm,lastPrepareAt:null,lastError:null,prepared:null,current:{deviceCode:`iptime-ax3000sm`,boardName:`iptime,ax3000sm`,buildId:`demo`,releaseVersion:`1.0.2`,openwrtVersion:`25.12`,metadataAvailable:!0},updateAvailable:!1,release:null,settings:{channel:`stable`,apiBaseUrl:`https://www.smartsafehub.com`,checkIntervalSeconds:21600,autoInstall:!1}},dh={configuration:{protocol:`dhcp`,supported:!0,pppoe:{username:``,passwordConfigured:!1},static:{address:null,prefixLength:null,netmask:null,gateway:null,dns:[]}},status:{connected:!0,pending:!1,protocol:`dhcp`,device:`wan`,address:`198.51.100.20`,prefixLength:24,gateway:`198.51.100.1`,dns:[`1.1.1.1`,`8.8.8.8`],uptimeSeconds:86400}},fh={networks:[{section:`demo_5g`,device:`radio0`,band:`5g`,bandLabel:`5 GHz`,ssid:`SmartSafeHub`,enabled:!0,runtimeUp:!0,security:`sae-mixed`,securityRaw:`sae-mixed`,passwordConfigured:!0,channel:`36`,networks:[`lan`],clientCount:2},{section:`demo_2g`,device:`radio1`,band:`2g`,bandLabel:`2.4 GHz`,ssid:`SmartSafeHub-2G`,enabled:!0,runtimeUp:!0,security:`sae-mixed`,securityRaw:`sae-mixed`,passwordConfigured:!0,channel:`6`,networks:[`lan`],clientCount:0}],totalClients:2,guest:{supported:!0,configured:!0,enabled:!0,ssid:`SmartSafeHub-Guest`,passwordConfigured:!0,subnet:`192.0.2.0/24`,radio:`radio1`}},ph={allow:[`trusted.example`,`updates.example`],block:[`ads.example`,`tracker.example`],counts:{allow:2,block:2,total:4},safeshieldEnabled:!0,applyLocalOverrides:!0},mh=async e=>({ssid:fh.networks.find(t=>t.section===e)?.ssid??`SmartSafeHub`,security:`sae-mixed`,password:`demo-only-2026`}),hh=async()=>({ssid:`SmartSafeHub-Guest`,security:`sae-mixed`,password:`guest-demo-only-2026`}),gh=new Set(Le);function _h(){let e=ii(),t=ci(),n=gh.has(e)?e:`home`,[r,i]=F(``);I(()=>{gh.has(e)||(window.history.replaceState(null,``,`${window.location.pathname}${window.location.search}#home`),i(`해당 기능은 현재 데모에서 제공하지 않습니다.`))},[e]);let a=()=>i(`데모에서는 실제 공유기 설정을 변경할 수 없습니다.`),o=()=>i(``),s=()=>i(`예시 데이터가 표시되고 있습니다. 실제 공유기에는 연결되지 않습니다.`),c=async()=>(a(),!1),l={action:null,actionError:null,data:uh,error:null,loading:!1,message:null,reconnecting:!1,uploadProgress:null,check:a,discard:c,dismissFeedback:o,install:c,prepare:a,refresh:s,upload:c},u;switch(n){case`activity`:u=L(mn,{actionError:null,actionMessage:null,data:eh,error:null,loading:!1,onDismissActionFeedback:o,onRetry:s,onSetCloudSync:a,savingCloudSync:!1});break;case`network`:u=L(`div`,{class:`min-w-0`,children:[L(li,{active:t.network,label:`네트워크 설정 메뉴`,tabs:[{id:`internet`,label:`인터넷 연결`,href:`#network`},{id:`lan`,label:`LAN 및 DHCP`,href:`#network/lan`}]}),L(`div`,{"aria-label":`인터넷 연결 설정`,"aria-labelledby":`internet-tab`,hidden:t.network!==`internet`,id:`internet-panel`,role:`tabpanel`,children:L($n,{action:null,data:dh,error:null,feedback:null,loading:!1,onDismissFeedback:o,onReconnect:a,onRetry:s,onSave:c})}),L(`div`,{"aria-label":`내부 네트워크 설정`,"aria-labelledby":`lan-tab`,hidden:t.network!==`lan`,id:`lan-panel`,role:`tabpanel`,children:L(ur,{action:null,data:nh,error:null,feedback:null,loading:!1,onApplyRecommendation:c,onDismissFeedback:o,onRetry:s,onSave:c})})]});break;case`wifi`:u=L(Tr,{data:fh,error:null,feedback:null,loading:!1,updatingSection:null,onDismissFeedback:o,onRetry:s,onUpdate:c,onUpdateGuest:c,onLoadQr:mh,onLoadGuestQr:hh});break;case`rules`:u=L(kr,{action:null,data:ph,error:null,feedback:null,loading:!1,onAddRule:c,onDeleteRule:c,onDismissFeedback:o,onRetry:s});break;case`system`:u=L(ti,{action:null,actionError:null,data:lh,error:null,firmware:l,loading:!1,message:null,onCheck:a,onDismissFeedback:o,onInstall:a,onRetry:s,onSaveSettings:c});break;case`devices`:u=L(xi,{data:$m,error:null,loading:!1,onRetry:s});break;case`safeshield`:u=L(Xm,{action:null,actionError:null,actionMessage:null,data:rh,error:null,loading:!1,statistics:sh,statisticsError:null,statisticsLoading:!1,statisticsRefreshing:!1,onDismissFeedback:o,onRefreshBlocklist:a,onRetry:s,onRetryStatistics:s,onSetEnabled:a,onSetStatisticsEnabled:a});break;default:u=L(cm,{accountRegistered:!1,activity:eh,activityError:null,activityLoading:!1,data:th,devices:$m,devicesError:null,devicesLoading:!1,error:null,firmware:uh,firmwareError:null,firmwareLoading:!1,health:ch,healthError:null,healthLoading:!1,lan:nh,lanError:null,lanLoading:!1,loading:!1,onRetry:s,safeshield:rh,safeshieldError:null,safeshieldLoading:!1,statistics:sh,statisticsError:null,statisticsLoading:!1,statisticsRefreshing:!1,updates:lh,updatesError:null,updatesLoading:!1})}return L(It,{accountRegistered:!1,demo:!0,loading:!1,onRefresh:s,refreshing:!1,route:n,updateCount:0,children:[L(`div`,{role:`status`,class:`mb-5 flex flex-wrap items-center justify-between gap-3 rounded-xl border border-teal-200 bg-teal-50 px-4 py-3 text-sm font-semibold text-teal-800`,children:[L(`span`,{children:`체험 모드 · 표시되는 기기와 보호 통계는 모두 예시입니다.`}),L(`a`,{class:`font-extrabold underline`,href:`/devices/`,children:`지원 기기 살펴보기 →`})]}),r?L(`div`,{role:`alert`,class:`mb-5 flex items-center justify-between gap-3 rounded-xl border border-amber-300 bg-amber-50 p-4 text-sm font-semibold text-amber-900`,children:[r,L(`button`,{"aria-label":`안내 닫기`,class:`font-black`,onClick:o,type:`button`,children:`✕`})]}):null,u]})}var vh=document.getElementById(`smartsafehub-demo-root`);if(vh){let e=vh.attachShadow({mode:`open`}),t=document.createElement(`link`);t.rel=`stylesheet`,t.href=vh.dataset.demoCss||`/static/router-demo/demo.css`,e.appendChild(t);let n=document.createElement(`div`);n.className=`smartsafehub-shadow-root`,e.appendChild(n),oe(L(_h,{}),n)} \ No newline at end of file +`):e}function Df(e,t){let{element:n,datasetIndex:r,index:i}=t,a=e.getDatasetMeta(r).controller,{label:o,value:s}=a.getLabelAndValue(i);return{chart:e,label:o,parsed:a.getParsed(i),raw:e.data.datasets[r].data[i],formattedValue:s,dataset:a.getDataset(),dataIndex:i,datasetIndex:r,element:n}}function Of(e,t){let n=e.chart.ctx,{body:r,footer:i,title:a}=e,{boxWidth:o,boxHeight:s}=t,c=_s(t.bodyFont),l=_s(t.titleFont),u=_s(t.footerFont),d=a.length,f=i.length,p=r.length,m=gs(t.padding),h=m.height,g=0,_=r.reduce((e,t)=>e+t.before.length+t.lines.length+t.after.length,0);if(_+=e.beforeBody.length+e.afterBody.length,d&&(h+=d*l.lineHeight+(d-1)*t.titleSpacing+t.titleMarginBottom),_){let e=t.displayColors?Math.max(s,c.lineHeight):c.lineHeight;h+=p*e+(_-p)*c.lineHeight+(_-1)*t.bodySpacing}f&&(h+=t.footerMarginTop+f*u.lineHeight+(f-1)*t.footerSpacing);let v=0,y=function(e){g=Math.max(g,n.measureText(e).width+v)};return n.save(),n.font=l.string,J(e.title,y),n.font=c.string,J(e.beforeBody.concat(e.afterBody),y),v=t.displayColors?o+2+t.boxPadding:0,J(r,e=>{J(e.before,y),J(e.lines,y),J(e.after,y)}),v=0,n.font=u.string,J(e.footer,y),n.restore(),g+=m.width,{width:g,height:h}}function kf(e,t){let{y:n,height:r}=t;return ne.height-r/2?`bottom`:`center`}function Af(e,t,n,r){let{x:i,width:a}=r,o=n.caretSize+n.caretPadding;if(e===`left`&&i+a+o>t.width||e===`right`&&i-a-o<0)return!0}function jf(e,t,n,r){let{x:i,width:a}=n,{width:o,chartArea:{left:s,right:c}}=e,l=`center`;return r===`center`?l=i<=(s+c)/2?`left`:`right`:i<=a/2?l=`left`:i>=o-a/2&&(l=`right`),Af(l,e,t,n)&&(l=`center`),l}function Mf(e,t,n){let r=n.yAlign||t.yAlign||kf(e,n);return{xAlign:n.xAlign||t.xAlign||jf(e,t,n,r),yAlign:r}}function Nf(e,t){let{x:n,width:r}=e;return t===`right`?n-=r:t===`center`&&(n-=r/2),n}function Pf(e,t,n){let{y:r,height:i}=e;return t===`top`?r+=n:r-=t===`bottom`?i+n:i/2,r}function Ff(e,t,n,r){let{caretSize:i,caretPadding:a,cornerRadius:o}=e,{xAlign:s,yAlign:c}=n,l=i+a,{topLeft:u,topRight:d,bottomLeft:f,bottomRight:p}=hs(o),m=Nf(t,s),h=Pf(t,c,l);return c===`center`?s===`left`?m+=l:s===`right`&&(m-=l):s===`left`?m-=Math.max(u,f)+i:s===`right`&&(m+=Math.max(d,p)+i),{x:ao(m,0,r.width-t.width),y:ao(h,0,r.height-t.height)}}function If(e,t,n){let r=gs(n.padding);return t===`center`?e.x+e.width/2:t===`right`?e.x+e.width-r.right:e.x+r.left}function Lf(e){return Tf([],Ef(e))}function Rf(e,t,n){return bs(e,{tooltip:t,tooltipItems:n,type:`tooltip`})}function zf(e,t){let n=t&&t.dataset&&t.dataset.tooltip&&t.dataset.tooltip.callbacks;return n?e.override(n):e}var Bf={beforeTitle:ha,title(e){if(e.length>0){let t=e[0],n=t.chart.data.labels,r=n?n.length:0;if(this&&this.options&&this.options.mode===`dataset`)return t.dataset.label||``;if(t.label)return t.label;if(r>0&&t.dataIndex{let t={before:[],lines:[],after:[]},i=zf(n,e);Tf(t.before,Ef(Vf(i,`beforeLabel`,this,e))),Tf(t.lines,Vf(i,`label`,this,e)),Tf(t.after,Ef(Vf(i,`afterLabel`,this,e))),r.push(t)}),r}getAfterBody(e,t){return Lf(Vf(t.callbacks,`afterBody`,this,e))}getFooter(e,t){let{callbacks:n}=t,r=Vf(n,`beforeFooter`,this,e),i=Vf(n,`footer`,this,e),a=Vf(n,`afterFooter`,this,e),o=[];return o=Tf(o,Ef(r)),o=Tf(o,Ef(i)),o=Tf(o,Ef(a)),o}_createItems(e){let t=this._active,n=this.chart.data,r=[],i=[],a=[],o=[],s,c;for(s=0,c=t.length;se.filter(t,r,i,n))),e.itemSort&&(o=o.sort((t,r)=>e.itemSort(t,r,n))),J(o,t=>{let n=zf(e.callbacks,t);r.push(Vf(n,`labelColor`,this,t)),i.push(Vf(n,`labelPointStyle`,this,t)),a.push(Vf(n,`labelTextColor`,this,t))}),this.labelColors=r,this.labelPointStyles=i,this.labelTextColors=a,this.dataPoints=o,o}update(e,t){let n=this.options.setContext(this.getContext()),r=this._active,i,a=[];if(!r.length)this.opacity!==0&&(i={opacity:0});else{let e=wf[n.position].call(this,r,this._eventPosition);a=this._createItems(n),this.title=this.getTitle(a,n),this.beforeBody=this.getBeforeBody(a,n),this.body=this.getBody(a,n),this.afterBody=this.getAfterBody(a,n),this.footer=this.getFooter(a,n);let t=this._size=Of(this,n),o=Object.assign({},e,t),s=Mf(this.chart,n,o),c=Ff(n,o,s,this.chart);this.xAlign=s.xAlign,this.yAlign=s.yAlign,i={opacity:1,x:c.x,y:c.y,width:t.width,height:t.height,caretX:e.x,caretY:e.y}}this._tooltipItems=a,this.$context=void 0,i&&this._resolveAnimations().update(this,i),e&&n.external&&n.external.call(this,{chart:this.chart,tooltip:this,replay:t})}drawCaret(e,t,n,r){let i=this.getCaretPosition(e,n,r);t.lineTo(i.x1,i.y1),t.lineTo(i.x2,i.y2),t.lineTo(i.x3,i.y3)}getCaretPosition(e,t,n){let{xAlign:r,yAlign:i}=this,{caretSize:a,cornerRadius:o}=n,{topLeft:s,topRight:c,bottomLeft:l,bottomRight:u}=hs(o),{x:d,y:f}=e,{width:p,height:m}=t,h,g,_,v,y,b;return i===`center`?(y=f+m/2,r===`left`?(h=d,g=h-a,v=y+a,b=y-a):(h=d+p,g=h+a,v=y-a,b=y+a),_=h):(g=r===`left`?d+Math.max(s,l)+a:r===`right`?d+p-Math.max(c,u)-a:this.caretX,i===`top`?(v=f,y=v-a,h=g-a,_=g+a):(v=f+m,y=v+a,h=g+a,_=g-a),b=v),{x1:h,x2:g,x3:_,y1:v,y2:y,y3:b}}drawTitle(e,t,n){let r=this.title,i=r.length,a,o,s;if(i){let c=vc(n.rtl,this.x,this.width);for(e.x=If(this,n.titleAlign,n),t.textAlign=c.textAlign(n.titleAlign),t.textBaseline=`middle`,a=_s(n.titleFont),o=n.titleSpacing,t.fillStyle=n.titleColor,t.font=a.string,s=0;se!==0)?(e.beginPath(),e.fillStyle=i.multiKeyBackground,cs(e,{x:t,y:p,w:c,h:s,radius:o}),e.fill(),e.stroke(),e.fillStyle=a.backgroundColor,e.beginPath(),cs(e,{x:n,y:p+1,w:c-2,h:s-2,radius:o}),e.fill()):(e.fillStyle=i.multiKeyBackground,e.fillRect(t,p,c,s),e.strokeRect(t,p,c,s),e.fillStyle=a.backgroundColor,e.fillRect(n,p+1,c-2,s-2))}e.fillStyle=this.labelTextColors[n]}drawBody(e,t,n){let{body:r}=this,{bodySpacing:i,bodyAlign:a,displayColors:o,boxHeight:s,boxWidth:c,boxPadding:l}=n,u=_s(n.bodyFont),d=u.lineHeight,f=0,p=vc(n.rtl,this.x,this.width),m=function(n){t.fillText(n,p.x(e.x+f),e.y+d/2),e.y+=d+i},h=p.textAlign(a),g,_,v,y,b,x,S;for(t.textAlign=a,t.textBaseline=`middle`,t.font=u.string,e.x=If(this,h,n),t.fillStyle=n.bodyColor,J(this.beforeBody,m),f=o&&h!==`right`?a===`center`?c/2+l:c+2+l:0,y=0,x=r.length;y0&&t.stroke()}_updateAnimationTarget(e){let t=this.chart,n=this.$animations,r=n&&n.x,i=n&&n.y;if(r||i){let n=wf[e.position].call(this,this._active,this._eventPosition);if(!n)return;let a=this._size=Of(this,e),o=Object.assign({},n,this._size),s=Mf(t,e,o),c=Ff(e,o,s,t);(r._to!==c.x||i._to!==c.y)&&(this.xAlign=s.xAlign,this.yAlign=s.yAlign,this.width=a.width,this.height=a.height,this.caretX=n.x,this.caretY=n.y,this._resolveAnimations().update(this,c))}}_willRender(){return!!this.opacity}draw(e){let t=this.options.setContext(this.getContext()),n=this.opacity;if(!n)return;this._updateAnimationTarget(t);let r={width:this.width,height:this.height},i={x:this.x,y:this.y};n=Math.abs(n)<.001?0:n;let a=gs(t.padding),o=this.title.length||this.beforeBody.length||this.body.length||this.afterBody.length||this.footer.length;t.enabled&&o&&(e.save(),e.globalAlpha=n,this.drawBackground(i,e,r,t),yc(e,t.textDirection),i.y+=a.top,this.drawTitle(i,e,t),this.drawBody(i,e,t),this.drawFooter(i,e,t),bc(e,t.textDirection),e.restore())}getActiveElements(){return this._active||[]}setActiveElements(e,t){let n=this._active,r=e.map(({datasetIndex:e,index:t})=>{let n=this.chart.getDatasetMeta(e);if(!n)throw Error(`Cannot find a dataset at index `+e);return{datasetIndex:e,element:n.data[t],index:t}}),i=!xa(n,r),a=this._positionChanged(r,t);(i||a)&&(this._active=r,this._eventPosition=t,this._ignoreReplayEvents=!0,this.update(!0))}handleEvent(e,t,n=!0){if(t&&this._ignoreReplayEvents)return!1;this._ignoreReplayEvents=!1;let r=this.options,i=this._active||[],a=this._getActiveElements(e,i,t,n),o=this._positionChanged(a,e),s=t||!xa(a,i)||o;return s&&(this._active=a,(r.enabled||r.external)&&(this._eventPosition={x:e.x,y:e.y},this.update(!0,t))),s}_getActiveElements(e,t,n,r){let i=this.options;if(e.type===`mouseout`)return[];if(!r)return t.filter(e=>this.chart.data.datasets[e.datasetIndex]&&this.chart.getDatasetMeta(e.datasetIndex).controller.getParsed(e.index)!==void 0);let a=this.chart.getElementsAtEventForMode(e,i.mode,i,n);return i.reverse&&a.reverse(),a}_positionChanged(e,t){let{caretX:n,caretY:r,options:i}=this,a=wf[i.position].call(this,e,t);return a!==!1&&(n!==a.x||r!==a.y)}};$(Hf,`positioners`,wf);var Uf={id:`tooltip`,_element:Hf,positioners:wf,afterInit(e,t,n){n&&(e.tooltip=new Hf({chart:e,options:n}))},beforeUpdate(e,t,n){e.tooltip&&e.tooltip.initialize(n)},reset(e,t,n){e.tooltip&&e.tooltip.initialize(n)},afterDraw(e){let t=e.tooltip;if(t&&t._willRender()){let n={tooltip:t};if(e.notifyPlugins(`beforeTooltipDraw`,{...n,cancelable:!0})===!1)return;t.draw(e.ctx),e.notifyPlugins(`afterTooltipDraw`,n)}},afterEvent(e,t){if(e.tooltip){let n=t.replay;e.tooltip.handleEvent(t.event,n,t.inChartArea)&&(t.changed=!0)}},defaults:{enabled:!0,external:null,position:`average`,backgroundColor:`rgba(0,0,0,0.8)`,titleColor:`#fff`,titleFont:{weight:`bold`},titleSpacing:2,titleMarginBottom:6,titleAlign:`left`,bodyColor:`#fff`,bodySpacing:2,bodyFont:{},bodyAlign:`left`,footerColor:`#fff`,footerSpacing:2,footerMarginTop:6,footerFont:{weight:`bold`},footerAlign:`left`,padding:6,caretPadding:2,caretSize:5,cornerRadius:6,boxHeight:(e,t)=>t.bodyFont.size,boxWidth:(e,t)=>t.bodyFont.size,multiKeyBackground:`#fff`,displayColors:!0,boxPadding:0,borderColor:`rgba(0,0,0,0)`,borderWidth:0,animation:{duration:400,easing:`easeOutQuart`},animations:{numbers:{type:`number`,properties:[`x`,`y`,`width`,`height`,`caretX`,`caretY`]},opacity:{easing:`linear`,duration:200}},callbacks:Bf},defaultRoutes:{bodyFont:`font`,footerFont:`font`,titleFont:`font`},descriptors:{_scriptable:e=>e!==`filter`&&e!==`itemSort`&&e!==`external`,_indexable:!1,callbacks:{_scriptable:!1,_indexable:!1},animation:{_fallback:!1},animations:{_fallback:`animation`}},additionalOptionScopes:[`interaction`]},Wf=(e,t,n,r)=>(typeof t==`string`?(n=e.push(t)-1,r.unshift({index:n,label:t})):isNaN(t)&&(n=null),n);function Gf(e,t,n,r){let i=e.indexOf(t);return i===-1?Wf(e,t,n,r):i===e.lastIndexOf(t)?i:n}var Kf=(e,t)=>e===null?null:ao(Math.round(e),0,t);function qf(e){let t=this.getLabels();return e>=0&&et.length-1?null:this.getPixelForValue(t[e].value)}getValueForPixel(e){return Math.round(this._startValue+this.getDecimalForPixel(e)*this._valueRange)}getBasePixel(){return this.bottom}};$(Jf,`id`,`category`),$(Jf,`defaults`,{ticks:{callback:qf}});function Yf(e,t){let n=[],{bounds:r,step:i,min:a,max:o,precision:s,count:c,maxTicks:l,maxDigits:u,includeBounds:d}=e,f=i||1,p=l-1,{min:m,max:h}=t,g=!U(a),_=!U(o),v=!U(c),y=(h-m)/(u+1),b=Ga((h-m)/p/f)*f,x,S,C,w;if(b<1e-14&&!g&&!_)return[{value:m},{value:h}];w=Math.ceil(h/b)-Math.floor(m/b),w>p&&(b=Ga(w*b/p/f)*f),U(s)||(x=10**s,b=Math.ceil(b*x)/x),r===`ticks`?(S=Math.floor(m/b)*b,C=Math.ceil(h/b)*b):(S=m,C=h),g&&_&&i&&Ya((o-a)/i,b/1e3)?(w=Math.round(Math.min((o-a)/b,l)),b=(o-a)/w,S=a,C=o):v?(S=g?a:S,C=_?o:C,w=c-1,b=(C-S)/w):(w=(C-S)/b,w=Wa(w,Math.round(w),b/1e3)?Math.round(w):Math.ceil(w));let T=Math.max($a(b),$a(S));x=10**(U(s)?T:s),S=Math.round(S*x)/x,C=Math.round(C*x)/x;let E=0;for(g&&(d&&S!==a?(n.push({value:a}),So)break;n.push({value:e})}return _&&d&&C!==o?n.length&&Wa(n[n.length-1].value,o,Xf(o,y,e))?n[n.length-1].value=o:n.push({value:o}):(!_||C===o)&&n.push({value:C}),n}function Xf(e,t,{horizontal:n,minRotation:r}){let i=Za(r),a=(n?Math.sin(i):Math.cos(i))||.001,o=.75*t*(``+e).length;return Math.min(t/a,o)}var Zf=class extends $u{constructor(e){super(e),this.start=void 0,this.end=void 0,this._startValue=void 0,this._endValue=void 0,this._valueRange=0}parse(e,t){return U(e)||(typeof e==`number`||e instanceof Number)&&!isFinite(+e)?null:+e}handleTickRangeOptions(){let{beginAtZero:e}=this.options,{minDefined:t,maxDefined:n}=this.getUserBounds(),{min:r,max:i}=this,a=e=>r=t?r:e,o=e=>i=n?i:e;if(e){let e=Ua(r),t=Ua(i);e<0&&t<0?o(0):e>0&&t>0&&a(0)}if(r===i){let t=i===0?1:Math.abs(i*.05);o(i+t),e||a(r-t)}this.min=r,this.max=i}getTickLimit(){let{maxTicksLimit:e,stepSize:t}=this.options.ticks,n;return t?(n=Math.ceil(this.max/t)-Math.floor(this.min/t)+1,n>1e3&&(console.warn(`scales.${this.id}.ticks.stepSize: ${t} would result generating up to ${n} ticks. Limiting to 1000.`),n=1e3)):(n=this.computeTickLimit(),e=e||11),e&&(n=Math.min(e,n)),n}computeTickLimit(){return 1/0}buildTicks(){let e=this.options,t=e.ticks,n=this.getTickLimit();n=Math.max(2,n);let r=Yf({maxTicks:n,bounds:e.bounds,min:e.min,max:e.max,precision:t.precision,step:t.stepSize,count:t.count,maxDigits:this._maxDigits(),horizontal:this.isHorizontal(),minRotation:t.minRotation||0,includeBounds:t.includeBounds!==!1},this._range||this);return e.bounds===`ticks`&&Xa(r,this,`value`),e.reverse?(r.reverse(),this.start=this.max,this.end=this.min):(this.start=this.min,this.end=this.max),r}configure(){let e=this.ticks,t=this.min,n=this.max;if(super.configure(),this.options.offset&&e.length){let r=(n-t)/Math.max(e.length-1,1)/2;t-=r,n+=r}this._startValue=t,this._endValue=n,this._valueRange=n-t}getLabelForValue(e){return Lo(e,this.chart.options.locale,this.options.ticks.format)}},Qf=class extends Zf{determineDataLimits(){let{min:e,max:t}=this.getMinMax(!0);this.min=_a(e)?e:0,this.max=_a(t)?t:1,this.handleTickRangeOptions()}computeTickLimit(){let e=this.isHorizontal(),t=e?this.width:this.height,n=Za(this.options.ticks.minRotation),r=(e?Math.sin(n):Math.cos(n))||.001,i=this._resolveTickFontOptions(0);return Math.ceil(t/Math.min(40,i.lineHeight/r))}getPixelForValue(e){return e===null?NaN:this.getPixelForDecimal((e-this._startValue)/this._valueRange)}getValueForPixel(e){return this._startValue+this.getDecimalForPixel(e)*this._valueRange}};$(Qf,`id`,`linear`),$(Qf,`defaults`,{ticks:{callback:Bo.formatters.numeric}});var $f=e=>Math.floor(Ha(e)),ep=(e,t)=>10**($f(e)+t);function tp(e){return e/10**$f(e)==1}function np(e,t,n){let r=10**n,i=Math.floor(e/r);return Math.ceil(t/r)-i}function rp(e,t){let n=$f(t-e);for(;np(e,t,n)>10;)n++;for(;np(e,t,n)<10;)n--;return Math.min(n,$f(e))}function ip(e,{min:t,max:n}){t=va(e.min,t);let r=[],i=$f(t),a=rp(t,n),o=a<0?10**Math.abs(a):1,s=10**a,c=i>a?10**i:0,l=Math.round((t-c)*o)/o,u=Math.floor((t-c)/s/10)*s*10,d=Math.floor((l-u)/10**a),f=va(e.min,Math.round((c+u+d*10**a)*o)/o);for(;f=10?d=d<15?15:20:d++,d>=20&&(a++,d=2,o=a>=0?1:o),f=Math.round((c+u+d*10**a)*o)/o;let p=va(e.max,f);return r.push({value:p,major:tp(p),significand:d}),r}var ap=class extends $u{constructor(e){super(e),this.start=void 0,this.end=void 0,this._startValue=void 0,this._valueRange=0}parse(e,t){let n=Zf.prototype.parse.apply(this,[e,t]);if(n===0)this._zero=!0;else return _a(n)&&n>0?n:null}determineDataLimits(){let{min:e,max:t}=this.getMinMax(!0);this.min=_a(e)?Math.max(0,e):null,this.max=_a(t)?Math.max(0,t):null,this.options.beginAtZero&&(this._zero=!0),this._zero&&this.min!==this._suggestedMin&&!_a(this._userMin)&&(this.min=e===ep(this.min,0)?ep(this.min,-1):ep(this.min,0)),this.handleTickRangeOptions()}handleTickRangeOptions(){let{minDefined:e,maxDefined:t}=this.getUserBounds(),n=this.min,r=this.max,i=t=>n=e?n:t,a=e=>r=t?r:e;n===r&&(n<=0?(i(1),a(10)):(i(ep(n,-1)),a(ep(r,1)))),n<=0&&i(ep(r,-1)),r<=0&&a(ep(n,1)),this.min=n,this.max=r}buildTicks(){let e=this.options,t=ip({min:this._userMin,max:this._userMax},this);return e.bounds===`ticks`&&Xa(t,this,`value`),e.reverse?(t.reverse(),this.start=this.max,this.end=this.min):(this.start=this.min,this.end=this.max),t}getLabelForValue(e){return e===void 0?`0`:Lo(e,this.chart.options.locale,this.options.ticks.format)}configure(){let e=this.min;super.configure(),this._startValue=Ha(e),this._valueRange=Ha(this.max)-Ha(e)}getPixelForValue(e){return(e===void 0||e===0)&&(e=this.min),e===null||isNaN(e)?NaN:this.getPixelForDecimal(e===this.min?0:(Ha(e)-this._startValue)/this._valueRange)}getValueForPixel(e){let t=this.getDecimalForPixel(e);return 10**(this._startValue+t*this._valueRange)}};$(ap,`id`,`logarithmic`),$(ap,`defaults`,{ticks:{callback:Bo.formatters.logarithmic,major:{enabled:!0}}});function op(e){let t=e.ticks;if(t.display&&e.display){let e=gs(t.backdropPadding);return K(t.font&&t.font.size,Q.font.size)+e.height}return 0}function sp(e,t,n){return n=W(n)?n:[n],{w:Jo(e,t.string,n),h:n.length*t.lineHeight}}function cp(e,t,n,r,i){return e===r||e===i?{start:t-n/2,end:t+n/2}:ei?{start:t-n,end:t}:{start:t,end:t+n}}function lp(e){let t={l:e.left+e._padding.left,r:e.right-e._padding.right,t:e.top+e._padding.top,b:e.bottom-e._padding.bottom},n=Object.assign({},t),r=[],i=[],a=e._pointLabels.length,o=e.options.pointLabels,s=o.centerPointLabels?Y/a:0;for(let c=0;ct.r&&(s=(r.end-t.r)/a,e.r=Math.max(e.r,t.r+s)),i.startt.b&&(c=(i.end-t.b)/o,e.b=Math.max(e.b,t.b+c))}function dp(e,t,n){let r=e.drawingArea,{extra:i,additionalAngle:a,padding:o,size:s}=n,c=e.getPointPosition(t,r+i+o,a),l=Math.round(Qa(ro(c.angle+Z))),u=gp(c.y,s.h,l),d=mp(l),f=hp(c.x,s.w,d);return{visible:!0,x:c.x,y:u,textAlign:d,left:f,top:u,right:f+s.w,bottom:u+s.h}}function fp(e,t){if(!t)return!0;let{left:n,top:r,right:i,bottom:a}=e;return!($o({x:n,y:r},t)||$o({x:n,y:a},t)||$o({x:i,y:r},t)||$o({x:i,y:a},t))}function pp(e,t,n){let r=[],i=e._pointLabels.length,a=e.options,{centerPointLabels:o,display:s}=a.pointLabels,c={extra:op(a)/2,additionalAngle:o?Y/i:0},l;for(let a=0;a270||n<90)&&(e-=t),e}function _p(e,t,n){let{left:r,top:i,right:a,bottom:o}=n,{backdropColor:s}=t;if(!U(s)){let n=hs(t.borderRadius),c=gs(t.backdropPadding);e.fillStyle=s;let l=r-c.left,u=i-c.top,d=a-r+c.width,f=o-i+c.height;Object.values(n).some(e=>e!==0)?(e.beginPath(),cs(e,{x:l,y:u,w:d,h:f,radius:n}),e.fill()):e.fillRect(l,u,d,f)}}function vp(e,t){let{ctx:n,options:{pointLabels:r}}=e;for(let i=t-1;i>=0;i--){let t=e._pointLabelItems[i];if(!t.visible)continue;let a=r.setContext(e.getPointLabelContext(i));_p(n,a,t);let o=_s(a.font),{x:s,y:c,textAlign:l}=t;ss(n,e._pointLabels[i],s,c+o.lineHeight/2,o,{color:a.color,textAlign:l,textBaseline:`middle`})}}function yp(e,t,n,r){let{ctx:i}=e;if(n)i.arc(e.xCenter,e.yCenter,t,0,X);else{let n=e.getPointPosition(0,t);i.moveTo(n.x,n.y);for(let a=1;a{let n=q(this.options.pointLabels.callback,[e,t],this);return n||n===0?n:``}).filter((e,t)=>this.chart.getDataVisibility(t))}fit(){let e=this.options;e.display&&e.pointLabels.display?lp(this):this.setCenterPoint(0,0,0,0)}setCenterPoint(e,t,n,r){this.xCenter+=Math.floor((e-t)/2),this.yCenter+=Math.floor((n-r)/2),this.drawingArea-=Math.min(this.drawingArea/2,Math.max(e,t,n,r))}getIndexAngle(e){let t=X/(this._pointLabels.length||1),n=this.options.startAngle||0;return ro(e*t+Za(n))}getDistanceFromCenterForValue(e){if(U(e))return NaN;let t=this.drawingArea/(this.max-this.min);return this.options.reverse?(this.max-e)*t:(e-this.min)*t}getValueForDistanceFromCenter(e){if(U(e))return NaN;let t=e/(this.drawingArea/(this.max-this.min));return this.options.reverse?this.max-t:this.min+t}getPointLabelContext(e){let t=this._pointLabels||[];if(e>=0&&e{if(t!==0||t===0&&this.min<0){s=this.getDistanceFromCenterForValue(e.value);let n=this.getContext(t),o=r.setContext(n),c=i.setContext(n);bp(this,o,s,a,c)}}),n.display){for(e.save(),o=a-1;o>=0;o--){let r=n.setContext(this.getPointLabelContext(o)),{color:i,lineWidth:a}=r;a&&i&&(e.lineWidth=a,e.strokeStyle=i,e.setLineDash(r.borderDash),e.lineDashOffset=r.borderDashOffset,s=this.getDistanceFromCenterForValue(t.reverse?this.min:this.max),c=this.getPointPosition(o,s),e.beginPath(),e.moveTo(this.xCenter,this.yCenter),e.lineTo(c.x,c.y),e.stroke())}e.restore()}}drawBorder(){}drawLabels(){let e=this.ctx,t=this.options,n=t.ticks;if(!n.display)return;let r=this.getIndexAngle(0),i,a;e.save(),e.translate(this.xCenter,this.yCenter),e.rotate(r),e.textAlign=`center`,e.textBaseline=`middle`,this.ticks.forEach((r,o)=>{if(o===0&&this.min>=0&&!t.reverse)return;let s=n.setContext(this.getContext(o)),c=_s(s.font);if(i=this.getDistanceFromCenterForValue(this.ticks[o].value),s.showLabelBackdrop){e.font=c.string,a=e.measureText(r.label).width,e.fillStyle=s.backdropColor;let t=gs(s.backdropPadding);e.fillRect(-a/2-t.left,-i-c.size/2-t.top,a+t.width,c.size+t.height)}ss(e,r.label,0,-i,c,{color:s.color,strokeColor:s.textStrokeColor,strokeWidth:s.textStrokeWidth})}),e.restore()}drawTitle(){}};$(Sp,`id`,`radialLinear`),$(Sp,`defaults`,{display:!0,animate:!0,position:`chartArea`,angleLines:{display:!0,lineWidth:1,borderDash:[],borderDashOffset:0},grid:{circular:!1},startAngle:0,ticks:{showLabelBackdrop:!0,callback:Bo.formatters.numeric},pointLabels:{backdropColor:void 0,backdropPadding:2,display:!0,font:{size:10},callback(e){return e},padding:5,centerPointLabels:!1}}),$(Sp,`defaultRoutes`,{"angleLines.color":`borderColor`,"pointLabels.color":`color`,"ticks.color":`color`}),$(Sp,`descriptors`,{angleLines:{_fallback:`grid`}});var Cp={millisecond:{common:!0,size:1,steps:1e3},second:{common:!0,size:1e3,steps:60},minute:{common:!0,size:6e4,steps:60},hour:{common:!0,size:36e5,steps:24},day:{common:!0,size:864e5,steps:30},week:{common:!1,size:6048e5,steps:4},month:{common:!0,size:2628e6,steps:12},quarter:{common:!1,size:7884e6,steps:4},year:{common:!0,size:3154e7}},wp=Object.keys(Cp);function Tp(e,t){return e-t}function Ep(e,t){if(U(t))return null;let n=e._adapter,{parser:r,round:i,isoWeekday:a}=e._parseOpts,o=t;return typeof r==`function`&&(o=r(o)),_a(o)||(o=typeof r==`string`?n.parse(o,r):n.parse(o)),o===null?null:(i&&(o=i===`week`&&(Ja(a)||a===!0)?n.startOf(o,`isoWeek`,a):n.startOf(o,i)),+o)}function Dp(e,t,n,r){let i=wp.length;for(let a=wp.indexOf(e);a=wp.indexOf(n);a--){let n=wp[a];if(Cp[n].common&&e._adapter.diff(i,r,n)>=t-1)return n}return wp[n?wp.indexOf(n):0]}function kp(e){for(let t=wp.indexOf(e)+1,n=wp.length;t=t?n[r]:n[i];e[a]=!0}}function jp(e,t,n,r){let i=e._adapter,a=+i.startOf(t[0].value,r),o=t[t.length-1].value,s,c;for(s=a;s<=o;s=+i.add(s,1,r))c=n[s],c>=0&&(t[c].major=!0);return t}function Mp(e,t,n){let r=[],i={},a=t.length,o,s;for(o=0;o+e.value))}initOffsets(e=[]){let t=0,n=0,r,i;this.options.offset&&e.length&&(r=this.getDecimalForValue(e[0]),t=e.length===1?1-r:(this.getDecimalForValue(e[1])-r)/2,i=this.getDecimalForValue(e[e.length-1]),n=e.length===1?i:(i-this.getDecimalForValue(e[e.length-2]))/2);let a=e.length<3?.5:.25;t=ao(t,0,a),n=ao(n,0,a),this._offsets={start:t,end:n,factor:1/(t+1+n)}}_generate(){let e=this._adapter,t=this.min,n=this.max,r=this.options,i=r.time,a=i.unit||Dp(i.minUnit,t,n,this._getLabelCapacity(t)),o=K(r.ticks.stepSize,1),s=a===`week`&&i.isoWeekday,c=Ja(s)||s===!0,l={},u=t,d,f;if(c&&(u=+e.startOf(u,`isoWeek`,s)),u=+e.startOf(u,c?`day`:a),e.diff(n,t,a)>1e5*o)throw Error(t+` and `+n+` are too far apart with stepSize of `+o+` `+a);let p=r.ticks.source===`data`&&this.getDataTimestamps();for(d=u,f=0;d+e)}getLabelForValue(e){let t=this._adapter,n=this.options.time;return n.tooltipFormat?t.format(e,n.tooltipFormat):t.format(e,n.displayFormats.datetime)}format(e,t){let n=this.options.time.displayFormats,r=this._unit,i=t||n[r];return this._adapter.format(e,i)}_tickFormatFunction(e,t,n,r){let i=this.options,a=i.ticks.callback;if(a)return q(a,[e,t,n],this);let o=i.time.displayFormats,s=this._unit,c=this._majorUnit,l=s&&o[s],u=c&&o[c],d=n[t],f=c&&u&&d&&d.major;return this._adapter.format(e,r||(f?u:l))}generateTickLabels(e){let t,n,r;for(t=0,n=e.length;t0?o:1}getDataTimestamps(){let e=this._cache.data||[],t,n;if(e.length)return e;let r=this.getMatchingVisibleMetas();if(this._normalized&&r.length)return this._cache.data=r[0].controller.getAllParsedValues(this);for(t=0,n=r.length;t=e[r].pos&&t<=e[i].pos&&({lo:r,hi:i}=lo(e,`pos`,t)),{pos:a,time:s}=e[r],{pos:o,time:c}=e[i]):(t>=e[r].time&&t<=e[i].time&&({lo:r,hi:i}=lo(e,`time`,t)),{time:a,pos:s}=e[r],{time:o,pos:c}=e[i]);let l=o-a;return l?s+(c-s)*(t-a)/l:s}var Fp=class extends Np{constructor(e){super(e),this._table=[],this._minPos=void 0,this._tableRange=void 0}initOffsets(){let e=this._getTimestampsForTable(),t=this._table=this.buildLookupTable(e);this._minPos=Pp(t,this.min),this._tableRange=Pp(t,this.max)-this._minPos,super.initOffsets(e)}buildLookupTable(e){let{min:t,max:n}=this,r=[],i=[],a,o,s,c,l;for(a=0,o=e.length;a=t&&c<=n&&r.push(c);if(r.length<2)return[{time:t,pos:0},{time:n,pos:1}];for(a=0,o=r.length;ae-t)}_getTimestampsForTable(){let e=this._cache.all||[];if(e.length)return e;let t=this.getDataTimestamps(),n=this.getLabelTimestamps();return e=t.length&&n.length?this.normalize(t.concat(n)):t.length?t:n,e=this._cache.all=e,e}getDecimalForValue(e){return(Pp(this._table,e)-this._minPos)/this._tableRange}getValueForPixel(e){let t=this._offsets,n=this.getDecimalForPixel(e)/t.factor-t.end;return Pp(this._table,n*this._tableRange+this._minPos,!0)}};$(Fp,`id`,`timeseries`),$(Fp,`defaults`,Np.defaults),Vd.register(Dl,Cf,Jf,Qf,Uf);var Ip=new Intl.DateTimeFormat(`ko-KR`,{hour:`2-digit`,hour12:!1}),Lp=`#94a3b8`,Rp=`rgba(148, 163, 184, 0.18)`,zp=`#0d9488`,Bp=`#0f766e`;function Vp(e){return Ip.format(new Date(e*1e3))}function Hp(e){return e.map((t,n)=>n%3==0||n===e.length-1?Vp(t.bucketStart):``)}function Up({buckets:e}){let t=we(null),n=we(null),r=we(e);return r.current=e,I(()=>{let e=t.current;if(!e)return;let i=window.matchMedia(`(prefers-reduced-motion: reduce)`).matches,a=window.getComputedStyle(e).fontFamily,o={animation:!i&&{duration:500,easing:`easeOutQuart`},maintainAspectRatio:!1,responsive:!0,interaction:{intersect:!1,mode:`index`},plugins:{legend:{display:!1},tooltip:{displayColors:!1,callbacks:{title:e=>{let t=e[0]?.dataIndex??-1,n=r.current[t];return n?Vp(n.bucketStart):``},label:e=>`차단 ${H(Number(e.raw??0))}`,afterLabel:e=>{let t=r.current[e.dataIndex];return t?`DNS 요청 ${H(t.queries)}`:``}}}},scales:{x:{border:{display:!1},grid:{display:!1},ticks:{autoSkip:!1,color:Lp,font:{family:a,size:11,weight:700},maxRotation:0,minRotation:0}},y:{beginAtZero:!0,border:{display:!1},grid:{color:Rp},ticks:{color:Lp,font:{family:a,size:11,weight:700},maxTicksLimit:4,precision:0}}}};return n.current=new Vd(e,{type:`bar`,data:{labels:Hp(r.current),datasets:[{data:r.current.map(e=>e.blocked),backgroundColor:zp,hoverBackgroundColor:Bp,borderRadius:4,borderSkipped:!1,barPercentage:.82,categoryPercentage:.9,maxBarThickness:24}]},options:o}),()=>{n.current?.destroy(),n.current=null}},[]),I(()=>{let t=n.current;t&&(t.data.labels=Hp(e),t.data.datasets[0].data=e.map(e=>e.blocked),t.update())},[e]),L(`div`,{class:`mt-5 h-44 sm:h-52`,children:L(`canvas`,{"aria-label":`최근 24시간 시간대별 SafeShield 차단 요청`,ref:t,role:`img`,children:`최근 24시간 시간대별 SafeShield 차단 요청 차트`})})}var Wp=3600,Gp=24;function Kp(e,t){return e<=0?`0.0%`:`${(t/e*100).toFixed(1)}%`}function qp(e){let t=e.hourly[e.hourly.length-1];if(t?.bucketStart)return t.bucketStart;let n=e.updatedAt>0?e.updatedAt:Math.floor(Date.now()/1e3);return Math.floor(n/Wp)*Wp}function Jp(e){let t=new Map(e.hourly.map(e=>[e.bucketStart,e])),n=qp(e)-23*Wp;return Array.from({length:Gp},(e,r)=>{let i=n+r*Wp;return t.get(i)??{bucketStart:i,queries:0,blocked:0}})}function Yp({children:e}){return L(`div`,{class:`mt-5 flex min-h-52 items-center justify-center rounded-xl border border-slate-200 bg-slate-50 px-5 text-center`,children:L(`p`,{class:`m-0 max-w-lg text-sm font-semibold leading-6 text-slate-500`,children:e})})}function Xp({data:e,error:t,loading:n,nowTimestamp:r,refreshing:i}){let a;if(n&&!e)a=L(Yp,{children:`SafeShield 보호 활동을 불러오는 중입니다.`});else if(t&&!e)a=L(Yp,{children:`SafeShield 보호 활동을 불러오지 못했습니다. SafeShield 페이지에서 상태를 확인해 주세요.`});else if(!e||!e.available)a=L(Yp,{children:`현재 SafeShield에서 차단 통계를 제공하지 않습니다. 통계 기능을 확인해 주세요.`});else if(!e.enabled)a=L(Yp,{children:`통계 수집이 꺼져 있습니다. SafeShield 페이지에서 통계를 켜면 최근 24시간 활동이 표시됩니다.`});else{let t=Jp(e),n=t.reduce((e,t)=>({queries:e.queries+t.queries,blocked:e.blocked+t.blocked}),{queries:0,blocked:0});a=L(v,{children:[L(`dl`,{class:`mt-5 grid gap-3 sm:grid-cols-3`,children:[L(`div`,{class:`rounded-xl bg-slate-50 px-4 py-3`,children:[L(`dt`,{class:`text-[0.68rem] font-black uppercase tracking-[0.12em] text-slate-400`,children:`DNS 요청`}),L(`dd`,{class:`mt-1 mb-0 ml-0 text-lg font-black text-slate-950`,children:H(n.queries)})]}),L(`div`,{class:`rounded-xl bg-slate-50 px-4 py-3`,children:[L(`dt`,{class:`text-[0.68rem] font-black uppercase tracking-[0.12em] text-slate-400`,children:`차단`}),L(`dd`,{class:`mt-1 mb-0 ml-0 text-lg font-black text-teal-700`,children:H(n.blocked)})]}),L(`div`,{class:`rounded-xl bg-slate-50 px-4 py-3`,children:[L(`dt`,{class:`text-[0.68rem] font-black uppercase tracking-[0.12em] text-slate-400`,children:`차단율`}),L(`dd`,{class:`mt-1 mb-0 ml-0 text-lg font-black text-slate-950`,children:Kp(n.queries,n.blocked)})]})]}),L(Up,{buckets:t})]})}return L(`article`,{class:`min-w-0 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm shadow-slate-900/5 sm:p-6`,children:[L(`div`,{class:`min-w-0`,children:[L(`p`,{class:`m-0 text-xs font-extrabold uppercase tracking-[0.16em] text-slate-500`,children:`SafeShield activity`}),L(`div`,{class:`mt-2 flex min-w-0 items-center justify-between gap-3`,children:[L(`h3`,{class:`m-0 min-w-0 text-xl font-black text-slate-950`,children:`최근 24시간 보호 활동`}),L(`a`,{class:`shrink-0 whitespace-nowrap text-xs font-extrabold text-teal-700 no-underline hover:text-teal-800`,href:`#safeshield`,children:`상세 통계 →`})]}),e?.updatedAt?L(`p`,{class:`mt-2 mb-0 text-[0.68rem] font-semibold text-slate-400`,title:Wt(e.updatedAt),children:i?`갱신 중…`:`집계: ${Gt(e.updatedAt,r)}`}):null]}),a,t&&e?L(`p`,{class:`mt-3 mb-0 text-xs font-bold text-amber-700`,children:`최근 통계 갱신에 실패해 마지막 정상 데이터를 표시하고 있습니다.`}):null]})}function Zp(e){return e===`healthy`?`bg-emerald-50 text-emerald-700 ring-emerald-200`:e===`warning`?`bg-amber-50 text-amber-800 ring-amber-200`:`bg-slate-100 text-slate-600 ring-slate-200`}function Qp({detail:e,eyebrow:t,href:n,icon:r,linkLabel:i=`자세히 보기 →`,meta:a,metaState:o,metaWarning:s=!1,state:c=`neutral`,value:l}){let u=L(v,{children:[L(`div`,{class:`flex items-start justify-between gap-4`,children:[L(`p`,{class:`m-0 text-[0.68rem] font-black uppercase tracking-[0.16em] text-slate-500`,children:t}),L(`span`,{class:`grid size-10 shrink-0 place-items-center rounded-xl ring-1 ring-inset ${Zp(c)}`,children:r})]}),L(`p`,{class:`mt-4 mb-0 break-words text-2xl font-black tracking-tight text-slate-950`,children:l}),L(`p`,{class:`mt-2 mb-0 min-h-10 text-sm font-medium leading-5 text-slate-500`,children:e}),a?L(`p`,{class:`mt-3 mb-0 text-xs font-bold ${o===`healthy`?`text-emerald-700`:o===`warning`||s?`text-amber-700`:`text-slate-400`}`,children:a}):null,n?L(`span`,{class:`mt-4 inline-flex text-xs font-extrabold text-teal-700`,children:i}):null]}),d=`min-w-0 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm shadow-slate-900/5 transition`;return n?L(`a`,{class:`${d} no-underline hover:border-slate-300 hover:shadow-md`,href:n,children:u}):L(`article`,{class:d,children:u})}var $p=6e4;function em(e,t){return!e||!Number.isFinite(e)||e<=0?null:Math.max(0,t-e)}function tm(e,t,n,r){return!t||t<=0?`${e}: ${r}`:L(`span`,{title:Wt(t),children:[e,`: `,Gt(t,n)]})}function nm(e){if(!e)return null;switch(e.toLowerCase()){case`dhcp`:return`DHCP`;case`static`:return`Static`;case`pppoe`:return`PPPoE`;default:return e.toUpperCase()}}function rm(e){if(!e)return!1;let t=e.split(`.`).map(e=>Number(e));if(t.length!==4||t.some(e=>!Number.isInteger(e)||e<0||e>255))return!1;let[n,r]=t;return n===void 0||r===void 0?!1:n===10||n===172&&r>=16&&r<=31||n===192&&r===168}function im({label:e,value:t}){return L(`div`,{class:`flex min-w-0 items-start justify-between gap-4 border-b border-slate-100 py-3 last:border-b-0`,children:[L(`dt`,{class:`text-sm font-semibold text-slate-500`,children:e}),L(`dd`,{class:`m-0 min-w-0 max-w-[68%] break-words text-right text-sm font-extrabold text-slate-950`,children:t})]})}function am(e){return e===`initializing`?{badge:`bg-teal-100 text-teal-800`,icon:`text-teal-700`,label:`준비 중`,panel:`border-teal-200 bg-teal-50`}:e===`critical`?{badge:`bg-rose-100 text-rose-800`,icon:`text-rose-700`,label:`이상`,panel:`border-rose-200 bg-rose-50/70`}:e===`warning`?{badge:`bg-amber-100 text-amber-800`,icon:`text-amber-700`,label:`주의`,panel:`border-amber-200 bg-amber-50/70`}:e===`unknown`?{badge:`bg-slate-200 text-slate-700`,icon:`text-slate-500`,label:`확인 불가`,panel:`border-slate-200 bg-slate-50`}:{badge:`bg-emerald-100 text-emerald-800`,icon:`text-emerald-700`,label:`정상`,panel:`border-emerald-200 bg-emerald-50/70`}}function om({data:e,error:t,loading:n,nowTimestamp:r}){let i=!!(e&&e.generatedAt>0),a=am(i&&e?e.overall:`unknown`),o=i&&e?e.checks.filter(e=>e.status!==`ok`):[],s=o.slice(0,2),c=Math.max(0,o.length-s.length);return L(`div`,{class:`mt-5 border-t border-slate-200 pt-5`,children:[L(`div`,{class:`flex flex-wrap items-center justify-between gap-3`,children:[L(`div`,{class:`flex items-center gap-2`,children:[L(`p`,{class:`m-0 text-sm font-black text-slate-950`,children:`장치 진단`}),i?L(`span`,{class:`rounded-full px-2.5 py-1 text-[11px] font-extrabold ${a.badge}`,children:a.label}):null]}),L(`a`,{class:`text-xs font-extrabold text-teal-700 no-underline hover:text-teal-900`,href:`#settings`,children:`상세 보기 →`})]}),t&&!i?L(`div`,{class:`mt-3 rounded-xl border border-amber-200 bg-amber-50/70 px-4 py-3`,children:L(`div`,{class:`flex items-start gap-2`,children:[L(B,{class:`mt-0.5 size-4 shrink-0 text-amber-700`}),L(`div`,{class:`min-w-0`,children:[L(`p`,{class:`m-0 text-sm font-extrabold text-slate-900`,children:`진단 상태를 확인하지 못했습니다.`}),L(`p`,{class:`mt-1 mb-0 text-xs leading-5 text-slate-500`,children:`설정에서 장치 진단 상태를 다시 확인해 주세요.`})]})]})}):i&&e?L(`div`,{class:`mt-3 rounded-xl border px-4 py-3 ${a.panel}`,children:[L(`div`,{class:`flex min-w-0 items-start gap-3`,children:[e.overall===`ok`?L(ut,{class:`mt-0.5 size-5 shrink-0 ${a.icon}`}):e.overall===`initializing`?L(tt,{class:`mt-0.5 size-5 shrink-0 ${a.icon}`}):L(B,{class:`mt-0.5 size-5 shrink-0 ${a.icon}`}),L(`div`,{class:`min-w-0 flex-1`,children:[L(`p`,{class:`m-0 text-sm font-black text-slate-950`,children:e.summary.message}),L(`p`,{class:`mt-1 mb-0 text-xs font-semibold leading-5 text-slate-500`,children:[`마지막 진단: `,Gt(e.generatedAt,r),` · 검사 항목:`,` `,e.summary.total,`개`]})]})]}),s.length>0?L(`div`,{class:`mt-3 grid gap-2 sm:grid-cols-2`,children:s.map(e=>L(`div`,{class:`min-w-0 rounded-lg border border-white/80 bg-white/70 px-3 py-2`,children:[L(`p`,{class:`m-0 text-xs font-extrabold text-slate-900`,children:e.label}),L(`p`,{class:`mt-1 mb-0 line-clamp-2 text-xs leading-5 text-slate-500`,children:e.message})]},e.id))}):null,c>0?L(`p`,{class:`mt-2 mb-0 text-xs font-extrabold text-slate-600`,children:[`외 `,c,`건의 확인 항목이 있습니다.`]}):null]}):L(`div`,{class:`mt-3 rounded-xl border border-slate-200 bg-slate-50 px-4 py-3`,children:[L(`p`,{class:`m-0 text-sm font-extrabold text-slate-900`,children:n?`진단 상태를 확인하고 있습니다.`:`아직 생성된 진단 결과가 없습니다.`}),L(`p`,{class:`mt-1 mb-0 text-xs leading-5 text-slate-500`,children:`진단 결과가 생성되면 현재 장치 상태를 이곳에 요약해서 표시합니다.`})]})]})}function sm(e,t,n){if(t&&!e)return{detail:`보호 상태를 확인하고 있습니다.`,state:`neutral`,value:`확인 중`};if(n&&!e)return{detail:`SafeShield 상태를 불러오지 못했습니다.`,state:`warning`,value:`확인 필요`};if(!e||!e.available)return{detail:`SafeShield 서비스를 사용할 수 없습니다.`,state:`warning`,value:`사용 불가`};if(!e.enabled||e.status===`disabled`)return{detail:`DNS 보호가 비활성화되어 있습니다.`,state:`neutral`,value:`꺼짐`};if(e.status===`running`)return{detail:`차단 목록을 갱신하고 있습니다.`,state:`neutral`,value:`갱신 중`};if(!(e.active&&e.runtime.dnsmasqRunning&&e.runtime.dnsRuntimeOk&&e.health.overall!==`error`&&e.issueCounts.errors===0))return{detail:e.runtime.lastErrorCode?`최근 오류: ${e.runtime.lastErrorCode}`:`DNS 보호 상태를 확인해 주세요.`,state:`warning`,value:`확인 필요`};let r=e.entitlement.plan?.trim(),i=e.blocklist.validLineCount>0?`${H(e.blocklist.validLineCount)}개 차단 규칙`:`차단 목록 적용됨`;return{detail:r?`${i} · ${r.toUpperCase()}`:i,state:`healthy`,value:`보호 중`}}function cm({accountRegistered:e,activity:t,activityError:n,activityLoading:r,data:i,devices:a,devicesError:o,devicesLoading:s,error:c,firmware:l,firmwareError:u,firmwareLoading:d,health:f,healthError:p,healthLoading:m,lan:h,lanError:g,lanLoading:_,loading:v,onRetry:y,safeshield:b,safeshieldError:x,safeshieldLoading:S,statistics:C,statisticsError:w,statisticsLoading:T,statisticsRefreshing:E,updates:D,updatesError:O,updatesLoading:k}){let A=Te(()=>i?Yt(i.runtime.memory):null,[i]),[j,M]=F(()=>Math.floor(Date.now()/1e3));if(I(()=>{let e=window.setInterval(()=>{M(Math.floor(Date.now()/1e3))},$p);return()=>window.clearInterval(e)},[]),v&&!i)return L(zn,{});if(c&&!i)return L(Bn,{message:c,onRetry:y});if(!i)return null;let ee=A?Math.round(A.percent):0,te=sm(b,S,x),ne=D?.packages.find(e=>e.name===`luci-app-smartsafehub`)??D?.packages[0]??null,re=!!(D&&D.updateCount>0),ie=D?re?`${D.updateCount}개 업데이트`:D.lastCheckAt?`최신 상태`:`확인 전`:O?`확인 필요`:`확인 중`,ae=ne?re&&ne.availableVersion?`${ne.installedVersion} → ${ne.availableVersion}`:`설치 버전 ${ne.installedVersion}`:O?`업데이트 상태를 불러오지 못했습니다.`:k?`업데이트 상태를 확인하고 있습니다.`:`업데이트 상태를 확인할 수 없습니다.`,oe=a?`${H(a.totals.online)}대`:s?`확인 중`:`확인 필요`,se=a?`Wi-Fi ${H(a.totals.wireless)}대 · 유선/기타 ${H(a.totals.ethernet)}대`:o||`연결 기기 요약을 불러오고 있습니다.`,ce=nm(i.network.protocol),le=i.network.available?i.network.ipv4Address?`WAN ${i.network.ipv4Address}${ce?` · ${ce}`:``}`:ce?`${ce} · WAN 주소를 받지 못했습니다.`:`WAN 주소를 받지 못했습니다.`:`WAN 인터페이스를 찾을 수 없습니다.`,N=!!h?.conflict.detected,ue=!i.network.up||N||g!==null&&!h?`warning`:h?`healthy`:`neutral`,de=N?`네트워크 충돌`:i.network.up?`정상 연결`:`연결 확인`,fe=i.network.up?N?`⚠ LAN 대역과 충돌합니다`:_&&!h?`네트워크 대역 확인 중`:g&&!h?`네트워크 대역을 확인하지 못했습니다.`:h?`✓ 네트워크 충돌 없음`:`네트워크 대역 확인 필요`:`WAN 연결 상태를 확인해 주세요.`,pe=!i.network.up||N||g!==null&&!h?`warning`:h?`healthy`:`neutral`,me=i.network.ipv4Address?rm(i.network.ipv4Address)?`${i.network.ipv4Address} · 사설 네트워크`:i.network.ipv4Address:`할당되지 않음`,he=em(b?.timestamps.lastSuccess,j),P=b?Math.max(b.timestamps.refreshIntervalS*2,86400):null,ge=!!(b?.enabled&&b.status!==`running`&&he!==null&&P!==null&&he>P),_e=Wr(D,j),ve=!!l?.current.metadataAvailable,ye=d&&!l?`확인 중`:u&&!l?`확인 필요`:ve?l?.current.releaseVersion?`SmartSafeHub ${l.current.releaseVersion}`:`SmartSafeHub 펌웨어`:`${i.software.distribution} ${i.software.version}`,be=d&&!l?`확인 중`:u&&!l?`확인 필요`:ve&&l?.current.buildId||i.software.revision,xe=ve?`빌드 ID`:`리비전`;return L(`div`,{class:`min-w-0 space-y-6`,children:[L(`section`,{"aria-labelledby":`dashboard-overview-title`,children:[L(V,{description:`인터넷, SafeShield, 연결 기기와 소프트웨어 상태를 한눈에 확인합니다.`,eyebrow:`Overview`,id:`dashboard-overview-title`,title:`시스템 개요`}),L(`div`,{class:`grid min-w-0 gap-4 sm:grid-cols-2 xl:grid-cols-4`,children:[L(Qp,{detail:le,eyebrow:`Internet`,href:`#network`,icon:L($e,{class:`size-5`}),linkLabel:N?`해결하기 →`:`자세히 보기 →`,meta:fe,metaState:pe,state:ue,value:de}),L(Qp,{detail:te.detail,eyebrow:`SafeShield`,href:`#safeshield`,icon:L(Ue,{class:`size-5`}),meta:tm(ge?`차단 목록 갱신 지연`:`차단 목록 갱신`,b?.timestamps.lastSuccess,j,S?`확인 중`:`기록 없음`),metaWarning:ge,state:te.state===`healthy`&&ge?`warning`:te.state,value:te.value}),L(Qp,{detail:se,eyebrow:`Connected devices`,href:`#devices`,icon:L(Ge,{class:`size-5`}),meta:tm(`목록 확인`,a?.generatedAt,j,s?`확인 중`:`기록 없음`),state:a?`healthy`:o?`warning`:`neutral`,value:oe}),L(Qp,{detail:ae,eyebrow:`Software update`,href:`#system`,icon:L(Je,{class:`size-5`}),meta:D&&!D.settings.checkEnabled?tm(`자동 확인 꺼짐 · 마지막 확인`,D.lastCheckAt,j,`확인 기록 없음`):tm(_e?`업데이트 확인 지연`:`마지막 확인`,D?.lastCheckAt,j,k?`확인 중`:`아직 없음`),metaWarning:_e,state:O||_e||re?`warning`:D?.lastCheckAt?`healthy`:`neutral`,value:ie})]}),e===!1?L(`div`,{class:`ssh-dashboard-account-banner mt-4 flex flex-col gap-4 rounded-2xl border p-4 sm:flex-row sm:items-center sm:justify-between sm:p-5`,role:`status`,children:[L(`div`,{class:`flex min-w-0 items-start gap-3`,children:[L(`span`,{class:`ssh-dashboard-account-banner-icon grid size-10 shrink-0 place-items-center rounded-xl`,"aria-hidden":`true`,children:L(qe,{class:`size-5`})}),L(`div`,{class:`min-w-0`,children:[L(`div`,{class:`flex flex-wrap items-center gap-2`,children:[L(`p`,{class:`ssh-dashboard-account-banner-title m-0 font-bold`,children:`SmartSafeHub 계정을 연결해 주세요`}),L(`span`,{class:`ssh-dashboard-account-banner-badge rounded-full px-2 py-0.5 text-[0.7rem] font-bold`,children:`연결 필요`})]}),L(`p`,{class:`ssh-dashboard-account-banner-description mt-1 mb-0 text-sm leading-relaxed`,children:`계정을 연결하면 웹사이트에서 기기를 관리하고 구독 정보를 확인할 수 있습니다. DNS 보호는 연결하지 않아도 계속 작동합니다.`})]})]}),L(`a`,{href:`#account`,class:`ssh-dashboard-account-banner-action inline-flex min-h-10 shrink-0 items-center justify-center rounded-xl px-4 py-2 text-sm font-bold no-underline transition-colors focus-visible:outline-2 focus-visible:outline-offset-2`,children:`계정 연결하기 →`})]}):null]}),L(`section`,{"aria-labelledby":`dashboard-activity-title`,children:[L(V,{description:`최근 SafeShield 차단 활동과 현재 네트워크 연결 구성을 함께 확인합니다.`,eyebrow:`Activity`,id:`dashboard-activity-title`,title:`네트워크 보호 활동`}),L(`div`,{class:`grid min-w-0 gap-4 xl:grid-cols-[minmax(0,1.55fr)_minmax(18rem,0.65fr)]`,children:[L(Xp,{data:C,error:w,loading:T,nowTimestamp:j,refreshing:E}),L(`article`,{class:`min-w-0 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm shadow-slate-900/5 sm:p-6`,children:[L(`div`,{class:`flex items-start justify-between gap-4`,children:[L(`div`,{children:[L(`p`,{class:`m-0 text-xs font-extrabold uppercase tracking-[0.16em] text-slate-500`,children:`Network`}),L(`h3`,{class:`mt-2 mb-0 text-xl font-black text-slate-950`,children:`연결 상태`})]}),L(`span`,{class:`grid size-10 shrink-0 place-items-center rounded-xl ${i.network.up&&!N?`bg-emerald-50 text-emerald-700`:`bg-amber-50 text-amber-800`}`,children:L($e,{class:`size-5`})})]}),L(`dl`,{class:`mt-4 mb-0`,children:[L(im,{label:`인터넷`,value:N?`네트워크 충돌`:i.network.up?`정상 연결`:`연결 확인`}),L(im,{label:`WAN IP`,value:me}),L(im,{label:`프로토콜`,value:ce||`확인되지 않음`}),L(im,{label:`상위 네트워크`,value:h?.wan.subnet||(_?`확인 중`:g?`확인 필요`:`확인되지 않음`)}),L(im,{label:`LAN 네트워크`,value:h?.lan.subnet||(_?`확인 중`:g?`확인 필요`:`확인되지 않음`)}),L(im,{label:`대역 충돌`,value:h?L(`span`,{class:h.conflict.detected?`text-amber-700`:`text-emerald-700`,children:h.conflict.detected?`충돌 감지`:`없음`}):_?`확인 중`:`확인 필요`})]}),L(`a`,{class:`mt-5 inline-flex min-h-10 w-full items-center justify-center rounded-xl border border-slate-200 bg-slate-50 px-4 py-2 text-sm font-extrabold text-slate-700 no-underline transition hover:border-teal-200 hover:bg-teal-50 hover:text-teal-800`,href:`#network`,children:`네트워크 보기`})]})]})]}),L(`section`,{"aria-labelledby":`dashboard-recent-activity-title`,children:[L(V,{action:L(`a`,{class:`inline-flex whitespace-nowrap text-xs font-extrabold text-teal-700 no-underline hover:text-teal-900`,href:`#activity`,children:`전체 보기 →`}),description:`인터넷, 보호, 업데이트와 진단에서 실제 상태가 바뀐 시점을 확인합니다.`,eyebrow:`History`,id:`dashboard-recent-activity-title`,title:`최근 활동`}),L(`article`,{class:`min-w-0 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm shadow-slate-900/5 sm:p-6`,children:r||n&&!t?L(dn,{error:n,loading:r}):L(un,{compact:!0,events:(t?.events??[]).slice(0,3)})})]}),L(`section`,{"aria-labelledby":`dashboard-system-title`,children:[L(V,{description:`메모리, 시스템 부하와 장치 정보를 빠르게 점검합니다.`,eyebrow:`System`,id:`dashboard-system-title`,title:`시스템 상태`}),L(`div`,{class:`grid min-w-0 gap-4 xl:grid-cols-[minmax(0,1.45fr)_minmax(20rem,0.75fr)]`,children:[L(`article`,{class:`min-w-0 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm shadow-slate-900/5 sm:p-6`,children:[L(`div`,{class:`flex flex-wrap items-start justify-between gap-4`,children:[L(`div`,{children:[L(`p`,{class:`m-0 text-xs font-extrabold uppercase tracking-[0.16em] text-slate-500`,children:`Runtime`}),L(`h3`,{class:`mt-2 mb-0 text-xl font-black text-slate-950`,children:`리소스 사용량`})]}),L(`span`,{class:`inline-flex items-center gap-2 rounded-full px-3 py-1.5 text-xs font-black ring-1 ring-inset ${ee>=90?`bg-amber-50 text-amber-800 ring-amber-200`:`bg-emerald-50 text-emerald-700 ring-emerald-200`}`,children:[L(`span`,{class:`size-2 rounded-full ${ee>=90?`bg-amber-500`:`bg-emerald-500`}`}),ee>=90?`메모리 확인`:`정상 실행`]})]}),L(`div`,{class:`mt-5 grid gap-3 sm:grid-cols-3`,children:[L(`div`,{class:`rounded-xl border border-slate-200 bg-slate-50 p-4`,children:[L(`div`,{class:`flex items-center gap-2 text-slate-500`,children:[L(nt,{class:`size-4`}),L(`span`,{class:`text-xs font-extrabold`,children:`메모리`})]}),L(`p`,{class:`mt-2 mb-0 text-xl font-black text-slate-950`,children:[ee,`%`]}),L(`p`,{class:`mt-1 mb-0 text-xs font-semibold text-slate-500`,children:[A?Vt(A.used):`-`,` /`,` `,Vt(i.runtime.memory.total)]}),L(`div`,{"aria-label":`메모리 사용률 ${ee}%`,class:`mt-3 h-1.5 overflow-hidden rounded-full bg-slate-200`,role:`progressbar`,"aria-valuemin":0,"aria-valuemax":100,"aria-valuenow":ee,children:L(`div`,{class:`h-full rounded-full transition-[width] duration-300 ${ee>=90?`bg-amber-500`:`bg-teal-600`}`,style:{width:`${Math.min(100,ee)}%`}})})]}),L(`div`,{class:`rounded-xl border border-slate-200 bg-slate-50 p-4`,children:[L(`div`,{class:`flex items-center gap-2 text-slate-500`,children:[L(We,{class:`size-4`}),L(`span`,{class:`text-xs font-extrabold`,children:`시스템 부하`})]}),L(`p`,{class:`mt-2 mb-0 text-xl font-black text-slate-950`,children:Ht(i.runtime.load[0])}),L(`p`,{class:`mt-1 mb-0 text-xs font-semibold text-slate-500`,children:[`5분 `,Ht(i.runtime.load[1]),` · 15분`,` `,Ht(i.runtime.load[2])]})]}),L(`div`,{class:`rounded-xl border border-slate-200 bg-slate-50 p-4`,children:[L(`div`,{class:`flex items-center gap-2 text-slate-500`,children:[L(tt,{class:`size-4`}),L(`span`,{class:`text-xs font-extrabold`,children:`실행 시간`})]}),L(`p`,{class:`mt-2 mb-0 text-xl font-black text-slate-950`,children:Bt(i.runtime.uptime)}),L(`p`,{class:`mt-1 mb-0 text-xs font-semibold text-slate-500`,children:[`부팅 `,Ut(i.runtime.localtime,i.runtime.uptime)]})]})]}),L(om,{data:f,error:p,loading:m,nowTimestamp:j})]}),L(`article`,{class:`min-w-0 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm shadow-slate-900/5 sm:p-6`,children:[L(`div`,{class:`flex items-start justify-between gap-4`,children:[L(`div`,{children:[L(`p`,{class:`m-0 text-xs font-extrabold uppercase tracking-[0.16em] text-slate-500`,children:`Device`}),L(`h3`,{class:`mt-2 mb-0 text-xl font-black text-slate-950`,children:`장치 정보`})]}),L(`span`,{class:`grid size-10 shrink-0 place-items-center rounded-xl bg-teal-50 text-teal-700`,children:L(We,{class:`size-5`})})]}),L(`dl`,{class:`mt-4 mb-0`,children:[L(im,{label:`모델`,value:i.device.model}),L(im,{label:`호스트 이름`,value:i.device.hostname}),L(im,{label:`펌웨어`,value:L(`span`,{title:ve?l?.current.releaseVersion?`SmartSafeHub 펌웨어 ${l.current.releaseVersion}`:`SmartSafeHub 커스텀 펌웨어`:u||void 0,children:ye})}),L(im,{label:xe,value:be}),L(im,{label:`커널`,value:i.software.kernel}),L(im,{label:`WAN IP`,value:i.network.ipv4Address||`할당되지 않음`})]})]})]})]})]})}var lm=3,um=10;function dm(e,t){return e<=0?`0.0%`:`${(t/e*100).toFixed(1)}%`}function fm(e){return e.id===`other`?`기타 기기`:e.hostname?e.hostname:e.identified?`이름 없는 기기`:`미식별 기기`}function pm(e){return[...e].sort((e,t)=>e.id===`other`?1:t.id===`other`?-1:t.blocked-e.blocked||t.queries-e.queries||fm(e).localeCompare(fm(t),`ko`))}function mm({devices:e,deviceLimit:t,truncated:n}){let r=pm(e),[i,a]=F(!1),[o,s]=F(1),c=Math.max(1,Math.ceil(r.length/um)),l=Math.min(o,c),u=(l-1)*um,d=Math.min(u+um,r.length),f=i?r.slice(u,d):r.slice(0,lm),p=r.length>lm;return I(()=>{s(e=>Math.min(e,c))},[c]),L(`div`,{class:`mt-6 rounded-2xl border border-slate-100 bg-slate-50/70 p-4 sm:p-5`,children:[L(`div`,{class:`flex flex-wrap items-start justify-between gap-3`,children:[L(`div`,{children:[L(`div`,{class:`flex flex-wrap items-center gap-2`,children:[L(`p`,{class:`m-0 text-xs font-bold text-slate-500`,children:`기기별 통계`}),p&&!i?L(`span`,{class:`inline-flex items-center rounded-full bg-teal-50 px-2 py-1 text-[10px] font-extrabold tracking-wide text-teal-700 ring-1 ring-teal-100`,children:`차단 TOP 3`}):null]}),L(`h3`,{class:`mt-1 mb-0 text-base font-extrabold text-slate-950`,children:`어떤 기기에서 차단되었는지 확인`}),L(`p`,{class:`mt-2 mb-0 text-xs leading-5 text-slate-500`,children:`DHCP 정보가 있는 기기는 MAC 주소를 기준으로 식별하며, 기기별 숫자만 현재 부팅 세션 동안 메모리에 집계합니다.`})]}),L(`div`,{class:`flex flex-wrap items-center justify-end gap-2`,children:[L(`span`,{class:`inline-flex items-center gap-2 rounded-full bg-white px-3 py-1.5 text-xs font-extrabold text-slate-600 ring-1 ring-slate-200`,children:[L(Ge,{class:`size-4 text-teal-700`}),H(r.length),`개 항목`]}),p?L(`button`,{"aria-controls":`safeshield-device-statistics-list`,"aria-expanded":i,class:`rounded-full bg-white px-3 py-1.5 text-xs font-extrabold text-teal-700 ring-1 ring-slate-200 transition hover:bg-slate-50`,onClick:()=>{a(e=>!e),s(1)},type:`button`,children:i?`간단히 보기 ↑`:`전체 ${H(r.length)}개 기기 보기 ↓`}):null]})]}),r.length===0?L(`p`,{class:`mt-4 mb-0 rounded-xl bg-white p-4 text-sm leading-6 text-slate-500 ring-1 ring-slate-100`,children:`아직 기기별 DNS 통계가 없습니다. 클라이언트에서 DNS 요청이 발생하면 다음 통계 갱신 시 표시됩니다.`}):L(`div`,{class:`mt-4 grid gap-3`,id:`safeshield-device-statistics-list`,children:f.map(e=>L(`article`,{class:`grid gap-4 rounded-xl bg-white p-4 ring-1 ring-slate-100 sm:grid-cols-[minmax(0,1fr)_auto] sm:items-center`,children:[L(`div`,{class:`min-w-0`,children:L(`div`,{class:`flex min-w-0 flex-wrap items-center gap-2`,children:[L(`span`,{class:`grid size-9 shrink-0 place-items-center rounded-lg bg-teal-50 text-teal-700`,children:L(Ge,{class:`size-4`})}),L(`div`,{class:`min-w-0`,children:[L(`h4`,{class:`m-0 truncate text-sm font-extrabold text-slate-950`,children:fm(e)}),L(`p`,{class:`mt-1 mb-0 break-all text-xs text-slate-500`,children:[e.ip||`IP 미확인`,e.mac?` · ${e.mac}`:``]})]}),L(`span`,{class:`rounded-full px-2 py-1 text-[11px] font-extrabold ${e.identified?`bg-emerald-50 text-emerald-700`:`bg-slate-100 text-slate-600`}`,children:e.id===`other`?`합산`:e.identified?`DHCP 식별`:`IP 임시 식별`})]})}),L(`dl`,{class:`m-0 grid grid-cols-3 gap-2 text-right`,children:[L(`div`,{class:`min-w-20`,children:[L(`dt`,{class:`text-[11px] font-bold text-slate-400`,children:`DNS 요청`}),L(`dd`,{class:`mt-1 mb-0 ml-0 text-sm font-extrabold text-slate-900`,children:H(e.queries)})]}),L(`div`,{class:`min-w-20`,children:[L(`dt`,{class:`text-[11px] font-bold text-slate-400`,children:`차단`}),L(`dd`,{class:`mt-1 mb-0 ml-0 text-sm font-extrabold text-teal-700`,children:H(e.blocked)})]}),L(`div`,{class:`min-w-20`,children:[L(`dt`,{class:`text-[11px] font-bold text-slate-400`,children:`차단율`}),L(`dd`,{class:`mt-1 mb-0 ml-0 text-sm font-extrabold text-slate-900`,children:dm(e.queries,e.blocked)})]})]})]},e.id))}),p&&!i?L(`p`,{class:`mt-3 mb-0 text-xs font-semibold leading-5 text-slate-500`,children:[`전체 `,H(r.length),`개 기기 중 차단 수 기준 상위 `,lm,`개를 표시하고 있습니다.`]}):null,i&&r.length>um?L(`nav`,{"aria-label":`기기별 통계 페이지`,class:`mt-4 flex flex-wrap items-center justify-between gap-3`,children:[L(`p`,{class:`m-0 text-xs font-semibold text-slate-500`,children:[H(u+1),`–`,H(d),` /`,` `,H(r.length),`개`]}),L(`div`,{class:`flex items-center gap-2`,children:[L(`button`,{class:`rounded-lg bg-white px-3 py-2 text-xs font-extrabold text-slate-700 ring-1 ring-slate-200 transition hover:bg-slate-50 disabled:cursor-not-allowed disabled:opacity-50`,disabled:l<=1,onClick:()=>s(e=>Math.max(1,e-1)),type:`button`,children:`이전`}),L(`span`,{"aria-current":`page`,class:`min-w-20 text-center text-xs font-extrabold text-slate-600`,children:[H(l),` / `,H(c)]}),L(`button`,{class:`rounded-lg bg-white px-3 py-2 text-xs font-extrabold text-slate-700 ring-1 ring-slate-200 transition hover:bg-slate-50 disabled:cursor-not-allowed disabled:opacity-50`,disabled:l>=c,onClick:()=>s(e=>Math.min(c,e+1)),type:`button`,children:`다음`})]})]}):null,n?L(`p`,{class:`mt-4 mb-0 rounded-xl border border-amber-200 bg-amber-50 p-3 text-xs font-semibold leading-5 text-amber-800`,children:[`개별 통계는 최대 `,H(t),`개 기기까지 추적합니다. 한도를 초과한 기기의 요청은 기타 기기로 합산됩니다.`]}):null]})}var hm=3600,gm=24;function _m(e,t){return e<=0?`0.0%`:`${(t/e*100).toFixed(1)}%`}function vm(e){let t=e.hourly[e.hourly.length-1];if(t?.bucketStart)return t.bucketStart;let n=e.updatedAt>0?e.updatedAt:Math.floor(Date.now()/1e3);return Math.floor(n/hm)*hm}function ym(e){let t=new Map(e.hourly.map(e=>[e.bucketStart,e])),n=vm(e)-23*hm;return Array.from({length:gm},(e,r)=>{let i=n+r*hm;return t.get(i)??{bucketStart:i,queries:0,blocked:0}})}function bm({emphasized:e=!1,label:t,value:n}){return L(`div`,{class:`rounded-xl bg-slate-50 p-4 sm:p-5`,children:[L(`dt`,{class:`text-[0.68rem] font-black uppercase tracking-[0.12em] text-slate-400`,children:t}),L(`dd`,{class:`mt-2 mb-0 ml-0 text-2xl font-black tracking-tight ${e?`text-teal-700`:`text-slate-950`}`,children:n})]})}function xm({action:e,data:t,error:n,loading:r,onRetry:i,onSetEnabled:a,refreshing:o}){if(r&&t===null)return L(v,{children:[L(V,{eyebrow:`Activity`,title:`최근 24시간 보호 활동`,description:`DNS 요청과 차단 현황을 확인합니다.`}),L(`section`,{class:`rounded-2xl border border-slate-200 bg-white p-5 shadow-sm shadow-slate-900/5 sm:p-6`,children:L(`p`,{class:`m-0 text-sm font-bold text-slate-500`,children:`차단 통계를 불러오는 중입니다…`})})]});if(n&&t===null)return L(v,{children:[L(V,{eyebrow:`Activity`,title:`최근 24시간 보호 활동`,description:`DNS 요청과 차단 현황을 확인합니다.`}),L(`section`,{class:`rounded-2xl border border-amber-200 bg-amber-50 p-5 sm:p-6`,children:[L(`p`,{class:`m-0 text-sm font-extrabold text-amber-950`,children:`차단 통계를 불러오지 못했습니다.`}),L(`p`,{class:`mt-2 mb-0 text-xs leading-5 text-amber-800`,children:n}),L(`button`,{class:`mt-4 inline-flex min-h-9 items-center justify-center rounded-xl border border-amber-300 bg-white px-3 py-2 text-xs font-extrabold text-amber-900 transition hover:bg-amber-100 focus:outline-none focus-visible:ring-4 focus-visible:ring-amber-100`,onClick:i,type:`button`,children:`다시 시도`})]})]});if(!t)return L(v,{children:[L(V,{eyebrow:`Activity`,title:`최근 24시간 보호 활동`,description:`DNS 요청과 차단 현황을 확인합니다.`}),L(`section`,{class:`rounded-2xl border border-slate-200 bg-white p-5 shadow-sm shadow-slate-900/5 sm:p-6`,children:L(`p`,{class:`m-0 text-sm leading-6 text-slate-600`,children:`현재 SafeShield에서 통계 데이터를 제공하지 않습니다. SafeShield 0.3.15 버전 이상이 설치되어 있고, 통계 수집이 활성화되어 있는지 확인해 주세요.`})})]});let s=ym(t),c=s[s.length-1],l=s.reduce((e,t)=>({queries:e.queries+t.queries,blocked:e.blocked+t.blocked}),{queries:0,blocked:0}),u=e===`statistics-enable`||e===`statistics-disable`,d=e===`statistics-enable`||e!==`statistics-disable`&&t.enabled,f=u?d?`활성화하는 중…`:`비활성화하는 중…`:t.enabled?t.collectorRunning?`활성화 · 수집 중`:`활성화 · 시작 중`:`비활성화`;return L(v,{children:[L(V,{action:L(`div`,{class:`flex items-center gap-3 rounded-xl border border-slate-100 bg-slate-50 px-3 py-2.5`,children:[L(`span`,{class:`grid size-10 shrink-0 place-items-center rounded-xl bg-slate-100 text-slate-500`,children:L(at,{class:`size-5`})}),L(`div`,{class:`text-right`,children:[L(`p`,{class:`m-0 text-[0.68rem] font-bold text-slate-400`,children:`통계 수집`}),L(`p`,{class:`mt-1 mb-0 text-xs font-extrabold ${u||d?`text-emerald-700`:`text-slate-500`}`,role:`status`,children:f})]}),L(`button`,{"aria-checked":d,"aria-label":d?`차단 통계 수집 끄기`:`차단 통계 수집 켜기`,class:`ssh-switch-control relative inline-flex shrink-0 items-center rounded-full border-0 p-0 transition focus:outline-none focus-visible:ring-4 focus-visible:ring-teal-100 ${u?`cursor-wait opacity-80`:`disabled:cursor-not-allowed disabled:opacity-50`} ${d?`bg-teal-600`:`bg-slate-300`}`,disabled:e!==null,onClick:()=>a(!t.enabled),role:`switch`,type:`button`,children:L(`span`,{"aria-hidden":`true`,class:`ssh-switch-thumb absolute top-1 grid place-items-center shadow-sm transition-all ${d?`left-6`:`left-1`}`,children:u?L(z,{"aria-hidden":`true`,class:`size-3 animate-spin text-teal-600`}):null})})]}),eyebrow:`Activity`,title:`최근 24시간 보호 활동`,description:`DNS 요청 원본은 저장하지 않고 숫자만 로컬 메모리에 집계합니다.`}),L(`section`,{"aria-busy":u,class:`rounded-2xl border border-slate-200 bg-white p-5 shadow-sm shadow-slate-900/5 sm:p-6 ${u?`cursor-wait`:``}`,children:[t.enabled?null:L(`div`,{class:`mb-4 rounded-xl border border-slate-200 bg-slate-50 px-4 py-3 text-sm leading-6 text-slate-600`,children:`통계 수집이 꺼져 있습니다. 활성화하면 DNS 요청 원본은 저장하지 않고 요청 수와 차단 수만 로컬 메모리에 집계합니다.`}),L(`dl`,{class:`mt-2 grid gap-3 sm:grid-cols-2 xl:grid-cols-4`,children:[L(bm,{label:`최근 24시간 DNS 요청`,value:H(l.queries)}),L(bm,{emphasized:!0,label:`최근 24시간 차단`,value:H(l.blocked)}),L(bm,{label:`최근 24시간 차단율`,value:_m(l.queries,l.blocked)}),L(bm,{label:`현재 시간 차단`,value:H(c?.blocked??0)})]}),L(`div`,{class:`mt-5 rounded-xl bg-slate-50/70 p-4 sm:p-5`,children:[L(`div`,{class:`flex flex-wrap items-end justify-between gap-3`,children:[L(`div`,{children:[L(`p`,{class:`m-0 text-xs font-bold text-slate-500`,children:`최근 24시간`}),L(`h3`,{class:`mt-1 mb-0 text-base font-extrabold text-slate-950`,children:`시간대별 차단 요청`})]}),L(`span`,{class:`text-xs font-bold text-slate-500`,children:o?`갱신 중…`:`마지막 집계 ${Wt(t.updatedAt)}`})]}),L(Up,{buckets:s})]}),L(mm,{deviceLimit:t.deviceLimit,devices:t.devices,truncated:t.devicesTruncated}),L(`div`,{class:`mt-5 flex flex-wrap gap-x-5 gap-y-2 border-t border-slate-100 pt-4 text-xs leading-5 text-slate-500`,children:[L(`span`,{children:[`수집 누적 DNS 요청 `,H(t.totals.queries)]}),L(`span`,{children:[`수집 누적 차단 `,H(t.totals.blocked)]}),L(`span`,{children:[`수집 시작 `,Wt(t.startedAt)]}),L(`span`,{children:[`보존 `,H(t.retentionHours),`시간`]}),t.volatile?L(`span`,{children:`재부팅 시 초기화되는 메모리 통계`}):null]}),n?L(`p`,{class:`mt-3 mb-0 text-xs font-bold text-amber-700`,children:`최근 자동 갱신에 실패했습니다. 현재 표시된 마지막 정상 통계를 유지합니다.`}):null]})]})}var Sm=[{label:`갱신 준비`,description:`차단 목록 갱신을 준비하고 있습니다.`,stages:[`starting`,`boot_delay`,`boot_refresh`,`boot_refresh_skipped`,`scheduled_refresh`,`init`]},{label:`최신 차단 목록 확인`,description:`서버에서 최신 차단 목록 정보를 확인하고 있습니다.`,stages:[`resolve_api`]},{label:`차단 목록 다운로드`,description:`최신 차단 목록을 안전하게 다운로드하고 있습니다.`,stages:[`download_artifact`]},{label:`사용자 규칙 적용`,description:`허용·차단 사용자 규칙을 새 차단 목록에 반영하고 있습니다.`,stages:[`local_overrides`,`merge`]},{label:`보호 규칙 적용`,description:`새 차단 규칙을 설치하고 DNS 보호 서비스를 갱신하고 있습니다.`,stages:[`install`,`restart_dnsmasq`]},{label:`보호 상태 확인`,description:`새 차단 목록과 DNS 보호가 정상적으로 동작하는지 확인하고 있습니다.`,stages:[`runtime_check`,`blocklist_verify`]}],Cm=new Map;Sm.forEach((e,t)=>{e.stages.forEach(e=>Cm.set(e,t))});function wm(e,t){return e===`running`||t!==null&&Cm.has(t)}function Tm(e){let t=e===null?0:Cm.get(e)??0,n=Sm[t];return{description:n.description,label:n.label,number:t+1,total:Sm.length}}function Em(e){return(e??``).trim().toLowerCase()}function Dm(e,t){let n=Em(e);if(n.includes(`upgrade`)||n.includes(`version`))return`현재 SafeShield 버전으로는 갱신을 계속할 수 없습니다. 시스템 업데이트에서 SafeShield를 최신 버전으로 업데이트해 주세요.`;if(n.includes(`entitlement`)||n.includes(`license`)||n.includes(`unauthorized`)||n.includes(`forbidden`))return`차단 목록을 사용할 권한을 확인하지 못했습니다. SmartSafeHub 계정과 요금제 상태를 확인해 주세요.`;if(n.includes(`dnsmasq`)||n.includes(`restart`)||n.includes(`runtime`))return`DNS 보호 서비스를 정상적으로 갱신하지 못했습니다. 잠시 후 다시 갱신해 보고 문제가 계속되면 상태 점검 정보를 확인해 주세요.`;if(n.includes(`verify`)||n.includes(`sanity`))return`새 차단 목록을 적용한 뒤 보호 상태 확인을 완료하지 못했습니다. 다시 갱신해 주세요.`;if(n.includes(`download`)||n.includes(`fetch`)||n.includes(`timeout`)||n.includes(`network`)||n.includes(`wan`))return`차단 목록을 다운로드하지 못했습니다. 인터넷 연결을 확인한 뒤 다시 갱신해 주세요.`;if(n.includes(`resolve`)||n.includes(`manifest`)||n.includes(`artifact`)||n.includes(`api`))return`최신 차단 목록 정보를 확인하지 못했습니다. 인터넷 연결을 확인한 뒤 다시 갱신해 주세요.`;if(n.includes(`override`)||n.includes(`allowlist`)||n.includes(`merge`)||n.includes(`rule`))return`사용자 허용·차단 규칙을 새 차단 목록에 반영하지 못했습니다. 사용자 규칙을 확인한 뒤 다시 갱신해 주세요.`;if(n.includes(`install`)||n.includes(`write`)||n.includes(`space`))return`새 차단 규칙을 장치에 적용하지 못했습니다. 저장 공간과 SafeShield 상태를 확인한 뒤 다시 갱신해 주세요.`;let r=Tm(t);return r.number===2?`최신 차단 목록 정보를 확인하는 중 문제가 발생했습니다. 인터넷 연결을 확인한 뒤 다시 갱신해 주세요.`:r.number===3?`차단 목록을 다운로드하는 중 문제가 발생했습니다. 인터넷 연결을 확인한 뒤 다시 갱신해 주세요.`:r.number===4?`사용자 규칙을 적용하는 중 문제가 발생했습니다. 사용자 규칙을 확인한 뒤 다시 갱신해 주세요.`:r.number===5?`새 보호 규칙을 적용하는 중 문제가 발생했습니다. 잠시 후 다시 갱신해 주세요.`:r.number===6?`새 차단 목록의 보호 상태를 확인하지 못했습니다. 다시 갱신해 주세요.`:`차단 목록 갱신을 완료하지 못했습니다. 잠시 후 다시 갱신해 주세요.`}var Om=`https://www.smartsafehub.com/pricing/`;function km({value:e,trueLabel:t=`정상`,falseLabel:n=`확인 필요`}){return L(`span`,{class:`inline-flex items-center gap-2 text-sm font-extrabold ${e?`text-emerald-700`:`text-slate-500`}`,children:[L(`span`,{class:`size-2 rounded-full ${e?`bg-emerald-500`:`bg-slate-300`}`}),e?t:n]})}function Am(e){return!e.enabled||e.status===`disabled`?`disabled`:e.status===`running`?`refreshing`:e.status===`error`||e.health.overall===`error`||e.issueCounts.errors>0?`error`:e.status===`paused`?`paused`:e.active&&e.runtime.dnsmasqRunning&&e.runtime.dnsRuntimeOk?`protecting`:`attention`}function jm(e){return!e.enabled||e.status===`disabled`?`비활성화`:e.active&&e.runtime.dnsmasqRunning&&e.runtime.dnsRuntimeOk?`보호 중`:e.status===`paused`?`일시 중지`:`확인 필요`}function Mm(e){let t=Am(e);return t===`error`&&jm(e)===`보호 중`&&e.runtime.lastErrorCode?`갱신 실패`:{disabled:`비활성화`,refreshing:`갱신 중`,protecting:`보호 중`,paused:`일시 중지`,error:`오류`,attention:`확인 필요`}[t]}function Nm(e){return e?`자동 최적화`:`확인되지 않음`}function Pm(e){let t=qt(e);return t===null?`확인되지 않음`:L(Dr,{timestamp:t})}function Fm(e){let t=Am(e);return t===`protecting`?e.blocklist.validLineCount>0?`${H(e.blocklist.validLineCount)}개 도메인 차단 규칙으로 DNS를 보호하고 있습니다.`:e.blocklist.installed?`차단 목록이 설치되어 있으며 DNS 보호가 정상적으로 동작하고 있습니다.`:`SafeShield 서비스와 DNS 런타임이 정상적으로 동작하고 있습니다.`:t===`refreshing`?`차단 목록을 최신 상태로 갱신하고 있습니다.`:t===`disabled`?`SafeShield DNS 보호가 비활성화되어 있습니다.`:t===`paused`?`SafeShield 작업이 일시 중지되어 있습니다.`:t===`error`?jm(e)===`보호 중`?`기존 DNS 보호는 유지되고 있지만 최근 차단 목록 갱신을 완료하지 못했습니다.`:`SafeShield 보호 상태에 문제가 발생했습니다. 아래 오류 내용을 확인해 주세요.`:e.active?e.runtime.dnsmasqRunning?e.runtime.dnsRuntimeOk?e.summary.message:`SafeShield DNS 런타임 상태를 확인해야 합니다.`:`dnsmasq가 실행 중이 아니어서 DNS 보호 상태를 확인해야 합니다.`:`SafeShield가 활성화되어 있지만 서비스가 실행 중이 아닙니다.`}function Im(e){return{ok:`정상`,warning:`경고`,error:`오류`,unavailable:`사용 불가`}[e]??e}function Lm({data:e}){let t=Am(e);return L(`span`,{class:`inline-flex items-center rounded-full px-3 py-1.5 text-xs font-extrabold ring-1 ring-inset ${t===`error`?`bg-red-50 text-red-700 ring-red-200`:t===`disabled`?`bg-slate-100 text-slate-700 ring-slate-200`:t===`refreshing`?`bg-blue-50 text-blue-700 ring-blue-200`:t===`paused`||t===`attention`?`bg-amber-50 text-amber-800 ring-amber-200`:`bg-emerald-50 text-emerald-700 ring-emerald-200`}`,children:Mm(e)})}function Rm({failed:e,step:t,total:n}){let r=2*Math.PI*17,i=r*.22,a=r-i;return L(`span`,{"aria-label":e?`갱신 실패 · ${t}/${n} 단계`:`갱신 ${t}/${n} 단계`,class:`ssh-safeshield-refresh-donut`,"data-state":e?`error`:`active`,role:`progressbar`,"aria-valuemin":0,"aria-valuemax":n,"aria-valuenow":t,children:[L(`svg`,{"aria-hidden":`true`,class:e?`ssh-safeshield-refresh-loader-svg ssh-safeshield-refresh-loader-svg-error`:`ssh-safeshield-refresh-loader-svg`,viewBox:`0 0 48 48`,children:[L(`circle`,{class:`ssh-safeshield-refresh-loader-track`,cx:`24`,cy:`24`,fill:`none`,r:17,stroke:`currentColor`,strokeWidth:2.5}),e?null:L(`circle`,{class:`ssh-safeshield-refresh-loader-arc`,cx:`24`,cy:`24`,fill:`none`,r:17,stroke:`currentColor`,strokeDasharray:`${i} ${a}`,strokeLinecap:`round`,strokeWidth:3.5})]}),L(`span`,{class:`ssh-safeshield-refresh-donut-label`,children:e?`!`:`${t}/${n}`})]})}function zm({data:e}){let t=wm(e.status,e.stage),n=Am(e)===`error`;if(!t&&!n)return null;let r=Tm(e.stage),i=n?Dm(e.runtime.lastErrorCode,e.stage):null;return L(`div`,{"aria-live":n?`assertive`:`polite`,class:`ssh-safeshield-refresh`,"data-state":n?`error`:`active`,role:n?`alert`:`status`,children:[L(Rm,{failed:n,step:r.number,total:r.total}),L(`div`,{class:`ssh-safeshield-refresh-copy`,children:[L(`p`,{class:`ssh-safeshield-refresh-kicker`,children:n?`갱신 실패 · ${r.number}/${r.total}`:`갱신 ${r.number}/${r.total}`}),L(`p`,{class:`ssh-safeshield-refresh-title`,children:n?`${r.label} 단계에서 문제가 발생했습니다.`:r.label}),L(`p`,{class:`ssh-safeshield-refresh-description`,children:i??r.description}),n&&e.runtime.lastErrorCode?L(`p`,{class:`ssh-safeshield-refresh-error-code`,children:[`오류 코드: `,L(`code`,{children:e.runtime.lastErrorCode})]}):null]})]})}function Bm({label:e,value:t}){return L(`div`,{class:`bg-white px-5 py-4 sm:px-6`,children:[L(`dt`,{class:`text-[0.68rem] font-black uppercase tracking-[0.14em] text-slate-400`,children:e}),L(`dd`,{class:`mt-2 mb-0 ml-0 text-sm font-extrabold text-slate-950`,title:typeof t==`string`?t:void 0,children:t})]})}function Vm(e){return e?.trim().toUpperCase()||`FREE`}function Hm(e){return e===`FREE`?`free`:e===`PRO`?`pro`:e===`ULTIMATE`?`ultimate`:`paid`}function Um({compact:e=!1,plan:t}){let n=Hm(t),r=n!==`free`;return L(`span`,{class:`ssh-safeshield-plan-badge`,"data-compact":e?`true`:`false`,"data-tier":n,title:r?`${t} 멤버십`:`무료 플랜`,children:[L(`span`,{"aria-hidden":`true`,class:`ssh-safeshield-plan-badge-mark`,children:r?`✦`:`•`}),L(`span`,{children:t})]})}function Wm({plan:e}){let t=Hm(e)===`free`;return L(`div`,{class:`bg-white px-5 py-4 sm:px-6`,children:[L(`dt`,{class:`text-[0.68rem] font-black uppercase tracking-[0.14em] text-slate-400`,children:`Plan`}),L(`dd`,{class:`mt-2 mb-0 ml-0 flex min-w-0 flex-wrap items-center gap-2`,children:[L(Um,{plan:e}),t?L(`span`,{class:`ssh-safeshield-plan-caption`,children:`기본 플랜`}):null]})]})}function Gm(){return L(`div`,{class:`ssh-safeshield-upgrade-card`,children:[L(`span`,{"aria-hidden":`true`,class:`ssh-safeshield-upgrade-mark`,children:`✦`}),L(`div`,{class:`ssh-safeshield-upgrade-copy`,children:[L(`div`,{class:`ssh-safeshield-upgrade-meta`,children:[L(`span`,{children:`SmartSafeHub Membership`}),L(`span`,{class:`ssh-safeshield-upgrade-soon`,children:`Coming soon`})]}),L(`strong`,{children:`더 강력한 보호 기능을 준비하고 있습니다.`}),L(`span`,{children:`프리미엄 보호와 확장된 관리 기능을 미리 확인해 보세요.`})]}),L(`a`,{class:`ssh-safeshield-upgrade-action`,href:Om,rel:`noopener noreferrer`,target:`_blank`,children:[`요금제 보기`,L(`span`,{"aria-hidden":`true`,children:`→`})]})]})}function Km({children:e,eyebrow:t,icon:n,title:r}){return L(`article`,{class:`min-w-0 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm shadow-slate-900/5 sm:p-6 xl:p-4 2xl:p-5`,children:[L(`div`,{class:`flex min-w-0 items-start justify-between gap-4 xl:gap-2`,children:[L(`div`,{children:[L(`p`,{class:`m-0 text-[0.68rem] font-black uppercase tracking-[0.16em] text-slate-400`,children:t}),L(`h3`,{class:`mt-2 mb-0 text-lg font-black tracking-tight text-slate-950 xl:text-base 2xl:text-lg`,children:r})]}),L(`span`,{class:`grid size-10 shrink-0 place-items-center rounded-xl bg-slate-100 text-slate-500 xl:size-9 2xl:size-10`,children:n})]}),L(`dl`,{class:`mt-5 mb-0 grid gap-3`,children:e})]})}function qm({label:e,value:t}){return L(`div`,{class:`flex min-w-0 items-center justify-between gap-2 border-b border-slate-100 pb-3 last:border-b-0 last:pb-0`,children:[L(`dt`,{class:`text-sm font-semibold text-slate-500`,children:e}),L(`dd`,{class:`m-0 min-w-0 break-words text-right text-sm font-extrabold text-slate-950`,children:t})]})}function Jm({error:e,message:t,onDismiss:n}){if(!e&&!t)return null;let r=e!==null;return L(`div`,{"aria-live":r?`assertive`:`polite`,class:`mt-4 flex flex-col items-start gap-3 rounded-2xl border px-4 py-3 text-sm sm:flex-row sm:justify-between ${r?`border-red-200 bg-red-50 text-red-900`:`border-emerald-200 bg-emerald-50 text-emerald-900`}`,role:r?`alert`:`status`,children:[L(`div`,{class:`flex min-w-0 items-start gap-3`,children:[L(r?B:ut,{class:`mt-0.5 size-5 shrink-0`}),L(`p`,{class:`m-0 font-semibold leading-6`,children:e??t})]}),L(`button`,{"aria-label":`알림 닫기`,class:`shrink-0 rounded-lg border-0 bg-transparent px-2 py-1 font-black text-current opacity-60 transition hover:opacity-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-current`,onClick:n,type:`button`,children:`×`})]})}function Ym({action:e,actionError:t,actionMessage:n,data:r,error:i,loading:a,statistics:o,statisticsError:s,statisticsLoading:c,statisticsRefreshing:l,onDismissFeedback:u,onRefreshBlocklist:d,onRetry:f,onRetryStatistics:p,onSetEnabled:m,onSetStatisticsEnabled:h}){let[g,_]=F(null);if(I(()=>{r?.available&&r.blocklist.validLineCount>0&&!wm(r.status,r.stage)&&_(r.blocklist.validLineCount)},[r?.available,r?.blocklist.validLineCount,r?.stage,r?.status]),a)return L(zn,{});if(i)return L(Bn,{message:i,onRetry:f});if(!r)return L(zn,{});if(!r.available)return L(`section`,{class:`rounded-2xl border border-amber-200 bg-amber-50 p-5 sm:p-6`,children:[L(`span`,{class:`grid size-11 place-items-center rounded-xl bg-amber-100 text-amber-800`,children:L(B,{class:`size-6`})}),L(`h2`,{class:`mt-5 mb-0 text-xl font-extrabold text-amber-950`,children:`SafeShield 상태 API를 찾을 수 없습니다`}),L(`p`,{class:`mt-2 mb-0 text-sm leading-6 text-amber-900`,children:`safeshield 패키지와 rpcd ucode 플러그인이 설치되어 있는지 확인해 주세요.`})]});let y=r.enabled,b=Vm(r.entitlement.plan),x=r.status===`running`,S=e!==null,C=(wm(r.status,r.stage)||Am(r)===`error`)&&r.blocklist.validLineCount===0&&g!==null?g:r.blocklist.validLineCount,w=y?e===`disable`?`끄는 중…`:`보호 끄기`:e===`enable`?`켜는 중…`:`보호 켜기`;function T(){(!y||window.confirm(`보호를 끄면, 현재 차단 목록이 제거되어 위험에 노출될 수 있습니다. 그래도 계속하시겠습니까?`))&&m(!y)}return L(v,{children:[L(V,{eyebrow:`Protection`,title:`SafeShield 보호 상태`,description:`DNS 보호 상태와 적용 중인 차단 목록을 확인합니다.`}),L(`section`,{class:`mb-8 grid min-w-0 gap-4 xl:grid-cols-[minmax(0,1.08fr)_minmax(0,0.92fr)] xl:items-stretch`,children:[L(`article`,{"aria-label":`보호 상태 요약`,class:`min-w-0 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm shadow-slate-900/5 sm:p-6 xl:h-full`,children:[L(`div`,{class:`flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between xl:flex-col 2xl:flex-row`,children:[L(`div`,{class:`flex min-w-0 items-start gap-3`,children:[L(`span`,{class:`grid size-10 shrink-0 place-items-center rounded-xl bg-teal-50 text-teal-700`,children:L(Ue,{class:`size-5`})}),L(`div`,{class:`min-w-0`,children:[L(`div`,{class:`flex flex-wrap items-center gap-2`,children:[L(`h3`,{class:`m-0 text-base font-black text-slate-950`,children:`현재 보호 상태`}),L(Lm,{data:r})]}),L(`p`,{class:`mt-2 mb-0 text-sm leading-6 text-slate-600`,children:Fm(r)})]})]}),L(`div`,{class:`flex w-full flex-col gap-2 sm:w-auto sm:flex-row sm:flex-wrap xl:justify-end`,children:[L(`button`,{class:`inline-flex min-h-11 items-center justify-center gap-2 rounded-xl border px-4 py-2.5 text-sm font-extrabold transition focus:outline-none focus-visible:ring-4 disabled:cursor-not-allowed disabled:opacity-50 ${r.enabled?`border-red-200 bg-white text-red-700 hover:border-red-300 hover:bg-red-50 focus-visible:ring-red-100`:`border-teal-700 bg-teal-700 text-white hover:border-teal-800 hover:bg-teal-800 focus-visible:ring-teal-100`}`,disabled:S||x,onClick:T,type:`button`,children:[L(rt,{class:`size-4 ${e===`enable`||e===`disable`?`animate-pulse`:``}`}),w]}),L(`button`,{class:`inline-flex min-h-11 items-center justify-center gap-2 rounded-xl border border-slate-200 bg-white px-4 py-2.5 text-sm font-extrabold text-slate-700 transition hover:border-teal-300 hover:bg-teal-50 hover:text-teal-800 focus:outline-none focus-visible:ring-4 focus-visible:ring-teal-100 disabled:cursor-not-allowed disabled:opacity-50`,disabled:!r.enabled||S||x,onClick:d,type:`button`,children:[L(it,{class:`size-4 ${e===`refresh`||x?`animate-bounce`:``}`}),e===`refresh`?`시작 중…`:x?`갱신 중…`:r.status===`error`?`다시 갱신`:`지금 갱신`]})]})]}),L(zm,{data:r}),L(`dl`,{class:`mt-5 mb-0 grid min-w-0 grid-cols-2 overflow-hidden rounded-xl border border-slate-200 bg-slate-200`,children:[L(Bm,{label:`SafeShield`,value:r.version??`확인되지 않음`}),L(Bm,{label:`Blocklist`,value:`${H(C)}개 도메인`}),L(Bm,{label:`Last refresh`,value:L(Dr,{timestamp:r.timestamps.lastSuccess})}),L(Wm,{plan:b})]}),b===`FREE`?L(Gm,{}):null]}),L(`section`,{"aria-label":`보호 데이터 요약`,class:`min-w-0 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm shadow-slate-900/5 sm:p-6 xl:h-full`,children:[L(`div`,{class:`flex flex-wrap items-center justify-between gap-3`,children:[L(`div`,{class:`flex min-w-0 items-center gap-3`,children:[L(`span`,{class:`grid size-10 shrink-0 place-items-center rounded-xl bg-slate-100 text-slate-500`,children:L(at,{class:`size-5`})}),L(`div`,{children:[L(`p`,{class:`m-0 text-[0.65rem] font-black uppercase tracking-[0.16em] text-slate-400`,children:`Protection data`}),L(`h3`,{class:`mt-1 mb-0 text-sm font-black text-slate-950`,children:`보호 데이터`})]})]}),L(`span`,{"aria-label":`보호 데이터 상태: ${r.artifact.resolved&&r.blocklist.installed&&r.blocklist.verificationOk?`최신`:`확인 필요`}`,class:`inline-flex items-center gap-1.5 rounded-full px-2.5 py-1 text-xs font-extrabold ${r.artifact.resolved&&r.blocklist.installed&&r.blocklist.verificationOk?`bg-emerald-50 text-emerald-700`:`bg-amber-50 text-amber-700`}`,children:[r.artifact.resolved&&r.blocklist.installed&&r.blocklist.verificationOk?L(ut,{class:`size-3.5 shrink-0`}):L(B,{class:`size-3.5 shrink-0`}),r.artifact.resolved&&r.blocklist.installed&&r.blocklist.verificationOk?`최신`:`확인 필요`]})]}),L(`p`,{class:`mt-2 mb-0 text-xs leading-5 text-slate-500`,children:`SafeShield가 기기 성능에 맞춰 사용하는 보호 목록의 구성과 최신 상태를 확인합니다.`}),L(`dl`,{class:`mt-3 mb-0 grid grid-cols-1 gap-2 sm:grid-cols-2`,children:[L(`div`,{class:`min-w-0 rounded-lg border border-slate-200 bg-white px-3 py-2.5`,children:[L(`dt`,{class:`text-xs font-bold text-slate-500`,children:`기기 최적화`}),L(`dd`,{class:`mt-1 mb-0 text-sm font-extrabold text-slate-950`,children:Nm(r.artifact.tier)})]}),L(`div`,{class:`min-w-0 rounded-lg border border-slate-200 bg-white px-3 py-2.5`,children:[L(`dt`,{class:`text-xs font-bold text-slate-500`,children:`마지막 업데이트`}),L(`dd`,{class:`mt-1 mb-0 text-sm font-extrabold leading-5 text-slate-950`,children:Pm(r.artifact.version)})]})]}),L(`section`,{"aria-label":`보호 데이터 세부 정보`,class:`mt-3 border-t border-slate-200 pt-3`,children:[L(`h4`,{class:`m-0 text-xs font-extrabold text-slate-600`,children:`세부 정보`}),L(`dl`,{class:`mt-3 mb-0 grid gap-2`,children:[L(qm,{label:`데이터 프로필`,value:r.artifact.tier||`확인되지 않음`}),L(qm,{label:`데이터 버전`,value:r.artifact.version||`확인되지 않음`})]})]})]})]}),L(Jm,{error:t,message:n,onDismiss:u}),L(xm,{action:e,data:o,error:s,loading:c,onRetry:p,onSetEnabled:h,refreshing:l}),L(`section`,{class:`mt-7`,children:[L(V,{description:`DNS 보호 런타임, 차단 목록, 갱신 일정과 상태 점검 결과를 한곳에서 확인합니다.`,eyebrow:`Protection details`,title:`보호 구성`}),L(`div`,{class:`grid min-w-0 grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4`,children:[L(Km,{eyebrow:`Runtime`,icon:L(ut,{class:`size-5`}),title:`DNS 보호 런타임`,children:[L(qm,{label:`SafeShield 서비스`,value:L(km,{falseLabel:`중지됨`,trueLabel:`동작 중`,value:r.active})}),L(qm,{label:`DNS 서비스`,value:L(km,{falseLabel:`중지됨`,value:r.runtime.dnsmasqRunning})}),L(qm,{label:`SafeShield DNS 연동`,value:L(km,{value:r.runtime.dnsRuntimeOk})}),L(qm,{label:`차단 목록 갱신`,value:L(km,{falseLabel:`중지됨`,value:r.runtime.refreshdRunning})})]}),L(Km,{eyebrow:`Blocklist`,icon:L(at,{class:`size-5`}),title:`차단 목록`,children:[L(qm,{label:`적용 규칙`,value:`${H(r.blocklist.validLineCount)}개 도메인`}),L(qm,{label:`파일 크기`,value:Vt(r.blocklist.fileSizeKb*1024)}),L(qm,{label:`설치 상태`,value:L(km,{falseLabel:`미설치`,trueLabel:`설치됨`,value:r.blocklist.installed})}),L(qm,{label:`검증 상태`,value:L(km,{falseLabel:`확인 필요`,value:r.blocklist.verificationOk})})]}),L(Km,{eyebrow:`Refresh schedule`,icon:L(lt,{class:`size-5`}),title:`차단 목록 갱신`,children:[L(qm,{label:`마지막 성공`,value:L(Dr,{timestamp:r.timestamps.lastSuccess})}),L(qm,{label:`다음 갱신`,value:Wt(r.timestamps.nextRefreshAt)}),L(qm,{label:`갱신 주기`,value:Jt(r.timestamps.refreshIntervalS)}),L(qm,{label:`최근 결과`,value:r.runtime.lastResult||(x?`갱신 중`:`확인되지 않음`)})]}),L(Km,{eyebrow:`Health`,icon:L(Ue,{class:`size-5`}),title:`상태 점검`,children:[L(qm,{label:`전체 상태`,value:Im(r.health.overall)}),L(qm,{label:`경고`,value:L(`span`,{class:`text-amber-700`,children:H(r.issueCounts.warnings)})}),L(qm,{label:`오류`,value:L(`span`,{class:`text-red-700`,children:H(r.issueCounts.errors)})}),L(qm,{label:`최근 오류 코드`,value:r.runtime.lastErrorCode||`없음`})]})]})]})]})}var Xm=Math.floor(Date.now()/1e3),Zm=3600,Qm={generatedAt:Xm,devices:[{id:`demo-phone`,hostname:`거실 스마트폰`,mac:`02:00:00:00:00:01`,ipv4Address:`192.0.2.101`,connection:`wifi`,online:!0,leaseActive:!0,leaseExpiresAt:Xm+6*Zm,interface:`wlan0`,ssid:`SmartSafeHub`,radio:`radio0`,band:`5g`,bandLabel:`5 GHz`,signalDbm:-43,inactiveMs:250,connectedSeconds:8300},{id:`demo-laptop`,hostname:`작업용 노트북`,mac:`02:00:00:00:00:02`,ipv4Address:`192.0.2.102`,connection:`wifi`,online:!0,leaseActive:!0,leaseExpiresAt:Xm+8*Zm,interface:`wlan0`,ssid:`SmartSafeHub`,radio:`radio0`,band:`5g`,bandLabel:`5 GHz`,signalDbm:-57,inactiveMs:720,connectedSeconds:29200},{id:`demo-tv`,hostname:`거실 TV`,mac:`02:00:00:00:00:03`,ipv4Address:`192.0.2.103`,connection:`ethernet`,online:!0,leaseActive:!0,leaseExpiresAt:Xm+12*Zm,interface:`lan1`,ssid:null,radio:null,band:null,bandLabel:null,signalDbm:null,inactiveMs:null,connectedSeconds:14e4},{id:`demo-tablet`,hostname:`태블릿`,mac:`02:00:00:00:00:04`,ipv4Address:`192.0.2.104`,connection:`wifi`,online:!1,leaseActive:!1,leaseExpiresAt:null,interface:null,ssid:`SmartSafeHub`,radio:`radio1`,band:`2g`,bandLabel:`2.4 GHz`,signalDbm:null,inactiveMs:null,connectedSeconds:null}],totals:{known:4,online:3,wireless:2,ethernet:1,offline:1}},$m={schema:1,scope:`current_boot`,volatile:!0,maxEvents:100,cloud:{enabled:!1,phase:`disabled`,eligible:!1,plan:`free`,retentionDays:0,pendingEvents:0,lastAttemptAt:0,lastSuccessAt:0,lastUploadedCount:0,lastErrorCode:null,nextSyncAt:0},events:[{schema:1,eventId:`demo-event-1`,eventType:`safeshield.blocklist.updated`,severity:`success`,occurredAt:Xm-7200,deviceUuid:null,source:`safeshield`,metadata:{}},{schema:1,eventId:`demo-event-2`,eventType:`system.boot`,severity:`info`,occurredAt:Xm-86400,deviceUuid:null,source:`system`,metadata:{}}]},eh={device:{hostname:`SmartRouter`,model:`ipTIME AX3000SM`,boardName:`iptime,ax3000sm`},software:{distribution:`OpenWrt`,version:`25.12`,revision:`demo`,kernel:`6.12`},runtime:{uptime:86400,localtime:Xm,load:[.08,.12,.05],memory:{total:268435456,free:83886080,shared:4194304,buffered:41943040,available:171966464,cached:41943040}},network:{available:!0,up:!0,protocol:`dhcp`,ipv4Address:`198.51.100.20`},activityHistory:$m},th={lan:{address:`192.168.1.1`,prefixLength:24,netmask:`255.255.255.0`,subnet:`192.168.1.0/24`},dhcp:{enabled:!0,start:`192.168.1.100`,end:`192.168.1.249`,leaseTime:`12h`},wan:{connected:!0,protocol:`dhcp`,address:`198.51.100.20`,prefixLength:24,subnet:`198.51.100.0/24`},conflict:{detected:!1,wanSubnet:null},recommendation:null},nh={available:!0,version:`0.3.25`,enabled:!0,active:!0,status:`idle`,stage:null,summary:{label:`보호 중`,message:`DNS 보호가 정상적으로 동작하고 있습니다.`,severity:`info`},runtime:{refreshdRunning:!0,dnsmasqRunning:!0,dnsRuntimeOk:!0,lastResult:`success`,lastErrorCode:null},entitlement:{plan:`free`,status:`active`},artifact:{resolved:!0,tier:`light`,version:`20261010T010000Z`,uniqueDomains:48e3,rules:48e3},localOverrides:{enabled:!0,allowlistPath:`/etc/safeshield/allowlist`,blocklistPath:`/etc/safeshield/blocklist`},blocklist:{installed:!0,validLineCount:48e3,fileSizeKb:4700,verificationOk:!0},health:{overall:`ok`},issueCounts:{warnings:0,errors:0},timestamps:{lastAttempt:Xm-2*Zm,lastSuccess:Xm-2*Zm,lastFailure:0,lastLocalApply:Xm-2*Zm,lastLocalApplyFailure:0,nextRefreshAt:Xm+21*Zm,refreshIntervalS:82800}},rh=Math.floor(Xm/Zm)*Zm,ih=[{queries:351,blocked:18},{queries:297,blocked:15},{queries:243,blocked:11},{queries:216,blocked:9},{queries:203,blocked:8},{queries:230,blocked:10},{queries:324,blocked:17},{queries:567,blocked:35},{queries:783,blocked:54},{queries:864,blocked:61},{queries:756,blocked:50},{queries:702,blocked:43},{queries:635,blocked:36},{queries:580,blocked:30},{queries:608,blocked:33},{queries:689,blocked:40},{queries:837,blocked:54},{queries:1026,blocked:73},{queries:1256,blocked:99},{queries:1458,blocked:125},{queries:1512,blocked:135},{queries:1323,blocked:111},{queries:1026,blocked:75},{queries:648,blocked:39}],ah=Array.from({length:24},(e,t)=>{let n=rh-(23-t)*Zm,r=ih[new Date(n*1e3).getHours()];return{bucketStart:n,queries:r.queries,blocked:r.blocked}}),oh={enabled:!0,collectorRunning:!0,available:!0,schemaVersion:1,volatile:!0,startedAt:Xm-604800,updatedAt:Xm-30,retentionHours:168,totals:{queries:ah.reduce((e,t)=>e+t.queries,0),blocked:ah.reduce((e,t)=>e+t.blocked,0)},hourly:ah,deviceLimit:128,devicesTruncated:!1,devices:[{id:`demo-phone`,mac:`02:00:00:00:00:01`,ip:`192.0.2.101`,hostname:`거실 스마트폰`,identified:!0,queries:3400,blocked:470},{id:`demo-laptop`,mac:`02:00:00:00:00:02`,ip:`192.0.2.102`,hostname:`작업용 노트북`,identified:!0,queries:2900,blocked:345},{id:`demo-tv`,mac:`02:00:00:00:00:03`,ip:`192.0.2.103`,hostname:`거실 TV`,identified:!0,queries:1200,blocked:185}]},sh={schema:1,generatedAt:Xm,overall:`ok`,summary:{message:`모든 주요 항목이 정상입니다.`,total:3,ok:3,warning:0,critical:0,unknown:0},metrics:{memoryAvailablePercent:64,load1m:.08,overlayAvailablePercent:80},checks:[],reporter:{enabled:!1,eligible:!1,plan:`free`,entitlementStatus:`active`,lastReportAt:0,nextReportAt:0,lastResult:``,lastErrorCode:null}},ch={phase:`idle`,updateCount:0,packages:[],lastCheckAt:Xm-Zm,lastInstallAt:null,lastError:null,releaseNotes:[],releaseNotesComplete:!0,settings:{channel:`stable`,checkEnabled:!0,checkIntervalSeconds:21600,autoInstall:!0,autoInstallTime:`03:00`,repositoryHost:`example.invalid`,updatePackage:`luci-app-smartsafehub`}},lh={phase:`idle`,source:null,lastCheckAt:Xm-Zm,lastPrepareAt:null,lastError:null,prepared:null,current:{deviceCode:`iptime-ax3000sm`,boardName:`iptime,ax3000sm`,buildId:`demo`,releaseVersion:`1.0.2`,openwrtVersion:`25.12`,metadataAvailable:!0},updateAvailable:!1,release:null,settings:{channel:`stable`,apiBaseUrl:`https://www.smartsafehub.com`,checkIntervalSeconds:21600,autoInstall:!1}},uh={configuration:{protocol:`dhcp`,supported:!0,pppoe:{username:``,passwordConfigured:!1},static:{address:null,prefixLength:null,netmask:null,gateway:null,dns:[]}},status:{connected:!0,pending:!1,protocol:`dhcp`,device:`wan`,address:`198.51.100.20`,prefixLength:24,gateway:`198.51.100.1`,dns:[`1.1.1.1`,`8.8.8.8`],uptimeSeconds:86400}},dh={networks:[{section:`demo_5g`,device:`radio0`,band:`5g`,bandLabel:`5 GHz`,ssid:`SmartSafeHub`,enabled:!0,runtimeUp:!0,security:`sae-mixed`,securityRaw:`sae-mixed`,passwordConfigured:!0,channel:`36`,networks:[`lan`],clientCount:2},{section:`demo_2g`,device:`radio1`,band:`2g`,bandLabel:`2.4 GHz`,ssid:`SmartSafeHub-2G`,enabled:!0,runtimeUp:!0,security:`sae-mixed`,securityRaw:`sae-mixed`,passwordConfigured:!0,channel:`6`,networks:[`lan`],clientCount:0}],totalClients:2,guest:{supported:!0,configured:!0,enabled:!0,ssid:`SmartSafeHub-Guest`,passwordConfigured:!0,subnet:`192.0.2.0/24`,radio:`radio1`}},fh={allow:[`trusted.example`,`updates.example`],block:[`ads.example`,`tracker.example`],counts:{allow:2,block:2,total:4},safeshieldEnabled:!0,applyLocalOverrides:!0},ph=async e=>({ssid:dh.networks.find(t=>t.section===e)?.ssid??`SmartSafeHub`,security:`sae-mixed`,password:`demo-only-2026`}),mh=async()=>({ssid:`SmartSafeHub-Guest`,security:`sae-mixed`,password:`guest-demo-only-2026`}),hh=new Set(Le);function gh(){let e=ai(),t=li(),n=hh.has(e)?e:`home`,[r,i]=F(``);I(()=>{hh.has(e)||(window.history.replaceState(null,``,`${window.location.pathname}${window.location.search}#home`),i(`해당 기능은 현재 데모에서 제공하지 않습니다.`))},[e]);let a=()=>i(`데모에서는 실제 공유기 설정을 변경할 수 없습니다.`),o=()=>i(``),s=()=>i(`예시 데이터가 표시되고 있습니다. 실제 공유기에는 연결되지 않습니다.`),c=async()=>(a(),!1),l={action:null,actionError:null,data:lh,error:null,loading:!1,message:null,reconnecting:!1,uploadProgress:null,check:a,discard:c,dismissFeedback:o,install:c,prepare:a,refresh:s,upload:c},u;switch(n){case`activity`:u=L(hn,{actionError:null,actionMessage:null,data:$m,error:null,loading:!1,onDismissActionFeedback:o,onRetry:s,onSetCloudSync:a,savingCloudSync:!1});break;case`network`:u=L(`div`,{class:`min-w-0`,children:[L(ui,{active:t.network,label:`네트워크 설정 메뉴`,tabs:[{id:`internet`,label:`인터넷 연결`,href:`#network`},{id:`lan`,label:`LAN 및 DHCP`,href:`#network/lan`}]}),L(`div`,{"aria-label":`인터넷 연결 설정`,"aria-labelledby":`internet-tab`,hidden:t.network!==`internet`,id:`internet-panel`,role:`tabpanel`,children:L(er,{action:null,data:uh,error:null,feedback:null,loading:!1,onDismissFeedback:o,onReconnect:a,onRetry:s,onSave:c})}),L(`div`,{"aria-label":`내부 네트워크 설정`,"aria-labelledby":`lan-tab`,hidden:t.network!==`lan`,id:`lan-panel`,role:`tabpanel`,children:L(dr,{action:null,data:th,error:null,feedback:null,loading:!1,onApplyRecommendation:c,onDismissFeedback:o,onRetry:s,onSave:c})})]});break;case`wifi`:u=L(Er,{data:dh,error:null,feedback:null,loading:!1,updatingSection:null,onDismissFeedback:o,onRetry:s,onUpdate:c,onUpdateGuest:c,onLoadQr:ph,onLoadGuestQr:mh});break;case`rules`:u=L(jr,{action:null,data:fh,error:null,feedback:null,loading:!1,onAddRule:c,onDeleteRule:c,onDismissFeedback:o,onRetry:s});break;case`system`:u=L(ni,{action:null,actionError:null,data:ch,error:null,firmware:l,loading:!1,message:null,onCheck:a,onDismissFeedback:o,onInstall:a,onRetry:s,onSaveSettings:c});break;case`devices`:u=L(Si,{data:Qm,error:null,loading:!1,onRetry:s});break;case`safeshield`:u=L(Ym,{action:null,actionError:null,actionMessage:null,data:nh,error:null,loading:!1,statistics:oh,statisticsError:null,statisticsLoading:!1,statisticsRefreshing:!1,onDismissFeedback:o,onRefreshBlocklist:a,onRetry:s,onRetryStatistics:s,onSetEnabled:a,onSetStatisticsEnabled:a});break;default:u=L(cm,{accountRegistered:!1,activity:$m,activityError:null,activityLoading:!1,data:eh,devices:Qm,devicesError:null,devicesLoading:!1,error:null,firmware:lh,firmwareError:null,firmwareLoading:!1,health:sh,healthError:null,healthLoading:!1,lan:th,lanError:null,lanLoading:!1,loading:!1,onRetry:s,safeshield:nh,safeshieldError:null,safeshieldLoading:!1,statistics:oh,statisticsError:null,statisticsLoading:!1,statisticsRefreshing:!1,updates:ch,updatesError:null,updatesLoading:!1})}return L(Lt,{accountRegistered:!1,demo:!0,loading:!1,onRefresh:s,refreshing:!1,route:n,updateCount:0,children:[L(`div`,{role:`status`,class:`mb-5 flex flex-wrap items-center justify-between gap-3 rounded-xl border border-teal-200 bg-teal-50 px-4 py-3 text-sm font-semibold text-teal-800`,children:[L(`span`,{children:`체험 모드 · 표시되는 기기와 보호 통계는 모두 예시입니다.`}),L(`a`,{class:`font-extrabold underline`,href:`/devices/`,children:`지원 기기 살펴보기 →`})]}),r?L(`div`,{role:`alert`,class:`mb-5 flex items-center justify-between gap-3 rounded-xl border border-amber-300 bg-amber-50 p-4 text-sm font-semibold text-amber-900`,children:[r,L(`button`,{"aria-label":`안내 닫기`,class:`font-black`,onClick:o,type:`button`,children:`✕`})]}):null,u]})}var _h=document.getElementById(`smartsafehub-demo-root`);if(_h){let e=_h.attachShadow({mode:`open`}),t=document.createElement(`link`);t.rel=`stylesheet`,t.href=_h.dataset.demoCss||`/static/router-demo/demo.css`,e.appendChild(t);let n=document.createElement(`div`);n.className=`smartsafehub-shadow-root`,e.appendChild(n),oe(L(gh,{}),n)} \ No newline at end of file diff --git a/luci-app-smartsafehub/frontend/package-lock.json b/luci-app-smartsafehub/frontend/package-lock.json index 481b06b29..6a4ee073c 100644 --- a/luci-app-smartsafehub/frontend/package-lock.json +++ b/luci-app-smartsafehub/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "luci-app-smartsafehub-frontend", - "version": "0.2.29", + "version": "0.2.30", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "luci-app-smartsafehub-frontend", - "version": "0.2.29", + "version": "0.2.30", "dependencies": { "chart.js": "4.5.1", "preact": "11.0.1" diff --git a/luci-app-smartsafehub/frontend/package.json b/luci-app-smartsafehub/frontend/package.json index 29f3a53e2..7be743399 100644 --- a/luci-app-smartsafehub/frontend/package.json +++ b/luci-app-smartsafehub/frontend/package.json @@ -1,7 +1,7 @@ { "name": "luci-app-smartsafehub-frontend", "private": true, - "version": "0.2.29", + "version": "0.2.30", "type": "module", "engines": { "node": ">=24.0" diff --git a/luci-app-smartsafehub/frontend/src/api/smartsafehub.ts b/luci-app-smartsafehub/frontend/src/api/smartsafehub.ts index ee12a1cd6..dad5c86bf 100644 --- a/luci-app-smartsafehub/frontend/src/api/smartsafehub.ts +++ b/luci-app-smartsafehub/frontend/src/api/smartsafehub.ts @@ -42,6 +42,7 @@ import type { WanSettingsInput, WanUpdateResult, } from '../types/wan'; +import type { RecommendedRulesStatus } from '../types/rules'; import { callApi } from './rpc'; const API_OBJECT = 'smartsafehub'; @@ -88,15 +89,18 @@ export interface DeviceRegistrationStatus { lastSuccessAt: number; } -export interface RecommendedRulesStatus { - revision: string | null; - domains: string[]; -} - export function fetchRecommendedRulesStatus(): Promise { return callApi(API_OBJECT, 'device_recommended_rules_status', {}, { timeoutMs: 5000 }); } +export function refreshRecommendedRulesStatus(): Promise { + return callApi(API_OBJECT, 'device_recommended_rules_refresh', {}, { timeoutMs: 30000 }); +} + +export function revokeRecommendedRule(ruleId: number): Promise { + return callApi(API_OBJECT, 'device_recommended_rules_revoke', { rule_id: ruleId }, { timeoutMs: 30000 }); +} + export function fetchDeviceRegistrationStatus(): Promise { return callApi(API_OBJECT, 'device_registration_status', {}, { timeoutMs: 5000 }); } diff --git a/luci-app-smartsafehub/frontend/src/app/App.tsx b/luci-app-smartsafehub/frontend/src/app/App.tsx index b97a214e7..2c3528e90 100644 --- a/luci-app-smartsafehub/frontend/src/app/App.tsx +++ b/luci-app-smartsafehub/frontend/src/app/App.tsx @@ -1,7 +1,8 @@ import type { ComponentChildren } from 'preact'; import { useEffect } from 'preact/hooks'; -import { fetchGuestWifiQr, fetchRecommendedRulesStatus, fetchWifiQr, initializeSystemTimezone } from '../api/smartsafehub'; +import { fetchGuestWifiQr, fetchRecommendedRulesStatus, refreshRecommendedRulesStatus, + revokeRecommendedRule, fetchWifiQr, initializeSystemTimezone } from '../api/smartsafehub'; import { AppShell } from '../components/AppShell'; import { PageTabs } from '../components/PageTabs'; @@ -375,6 +376,8 @@ export function App({ onAdministratorPasswordChanged }: AppProps) { onAddRule={rules.addRule} onDeleteRule={rules.deleteRule} onLoadRecommendedRulesStatus={fetchRecommendedRulesStatus} + onSyncRecommendedRules={refreshRecommendedRulesStatus} + onRevokeRecommendedRule={revokeRecommendedRule} onDismissFeedback={rules.dismissFeedback} onRetry={() => void rules.refresh()} /> diff --git a/luci-app-smartsafehub/frontend/src/components/FirmwareUpdatesCard.tsx b/luci-app-smartsafehub/frontend/src/components/FirmwareUpdatesCard.tsx index 5f0a50f0c..107db5516 100644 --- a/luci-app-smartsafehub/frontend/src/components/FirmwareUpdatesCard.tsx +++ b/luci-app-smartsafehub/frontend/src/components/FirmwareUpdatesCard.tsx @@ -394,9 +394,20 @@ export function FirmwareUpdatesCard({ 펌웨어 {data.release.version || '미확인'} · {formatBytes(data.release.sysupgrade.sizeBytes)} · {(data.release.channel || data.settings.channel) === 'beta' ? 'Beta' : 'Stable'}

- - {data.release.version || 'Latest'} - +
+ + {data.release.version || 'Latest'} + + {bundledVersion ? ( + + 관리 SW + {bundledVersion} + + ) : null} +
{bundledVersion ? (
+
{label}
@@ -95,17 +96,22 @@ export function SafeShieldStatisticsPanel({ }: SafeShieldStatisticsPanelProps) { if (loading && data === null) { return ( -
+ <> + +

차단 통계를 불러오는 중입니다…

+ ); } if (error && data === null) { return ( -
+ <> + +

차단 통계를 불러오지 못했습니다.

@@ -118,22 +124,20 @@ export function SafeShieldStatisticsPanel({ 다시 시도
+ ); } if (!data) { return ( -
-

- Activity -

-

- 보호 활동 -

-

- 현재 SafeShield에서 통계 데이터를 제공하지 않습니다. SafeShield 0.3.15 버전 이상이 설치되어 있고, 통계 수집이 활성화되어 있는지 확인해 주세요. -

-
+ <> + +
+

+ 현재 SafeShield에서 통계 데이터를 제공하지 않습니다. SafeShield 0.3.15 버전 이상이 설치되어 있고, 통계 수집이 활성화되어 있는지 확인해 주세요. +

+
+ ); } @@ -165,76 +169,69 @@ export function SafeShieldStatisticsPanel({ : '비활성화'; return ( + <> + + + + +
+

통계 수집

+

+ {collectionStatus} +

+
+ +
+ } + eyebrow="Activity" + title="최근 24시간 보호 활동" + description="DNS 요청 원본은 저장하지 않고 숫자만 로컬 메모리에 집계합니다." + />
-
-
-

- Activity -

-

- 최근 24시간 보호 활동 -

-

- DNS 요청 원본은 저장하지 않고 숫자만 로컬 메모리에 집계합니다. -

-
- -
- - - -
-

통계 수집

-

- {collectionStatus} -

-
- -
-
- {!data.enabled ? ( -
+
통계 수집이 꺼져 있습니다. 활성화하면 DNS 요청 원본은 저장하지 않고 요청 수와 차단 수만 로컬 메모리에 집계합니다.
) : null} -
+
-
+

최근 24시간

@@ -286,5 +283,6 @@ export function SafeShieldStatisticsPanel({

) : null}
+ ); } diff --git a/luci-app-smartsafehub/frontend/src/components/SectionHeading.tsx b/luci-app-smartsafehub/frontend/src/components/SectionHeading.tsx new file mode 100644 index 000000000..b55b52e7f --- /dev/null +++ b/luci-app-smartsafehub/frontend/src/components/SectionHeading.tsx @@ -0,0 +1,23 @@ +import type { ComponentChildren } from 'preact'; + +interface SectionHeadingProps { + action?: ComponentChildren; + description: string; + eyebrow: string; + id?: string; + title: string; +} + +/** Section context is outside the content card; controls stay with their functional card. */ +export function SectionHeading({ action, description, eyebrow, id, title }: SectionHeadingProps) { + return ( +
+

{eyebrow}

+
+

{title}

+ {action ?
{action}
: null} +
+

{description}

+
+ ); +} diff --git a/luci-app-smartsafehub/frontend/src/components/SoftwareUpdatesCard.tsx b/luci-app-smartsafehub/frontend/src/components/SoftwareUpdatesCard.tsx index 896cd8f8a..caafb36d0 100644 --- a/luci-app-smartsafehub/frontend/src/components/SoftwareUpdatesCard.tsx +++ b/luci-app-smartsafehub/frontend/src/components/SoftwareUpdatesCard.tsx @@ -649,9 +649,6 @@ export function SoftwareUpdatesCard({
자동으로 업데이트 확인 - - 선택한 주기에 새 관리 소프트웨어 버전을 확인합니다. -
자동으로 업데이트 설치 - - 새 버전이 있으면 지정한 시각에 자동으로 설치합니다. -
-
-
-

- Cloud history -

-

Cloud 활동 기록

-

{status.description}

+
+
+
+
+ + {saving ? '설정 중' : status.label} + +
+

{status.description}

-
- - {saving ? '설정 중' : status.label} - +
+ Cloud 전송 {cloud.enabled ? '켜짐' : '꺼짐'}
); } @@ -204,61 +221,90 @@ export function ActivityPage({ onSetCloudSync: (enabled: boolean) => void; savingCloudSync: boolean; }) { - return ( -
-
-
-
-

- Local history -

-

- 현재 부팅 이후의 최근 활동 -

-

- 인터넷, SafeShield, 업데이트, 계정 권한과 주요 설정에서 실제 상태가 변경된 시점만 기록합니다. -

-
- - 최대 {data?.maxEvents ?? 128}건 - -
+ const [visibleCount, setVisibleCount] = useState(20); + const events = data?.events ?? []; + const visibleEvents = events.slice(0, visibleCount); + const remainingCount = events.length - visibleEvents.length; -
+ return ( +
+
+ + +
+ +
+ 최대 {data?.maxEvents ?? 128}건} + /> +
{loading || (error && !data) ? ( ) : ( - + <> + + {events.length > 20 ? ( +
+

+ {formatNumber(visibleEvents.length)} / {formatNumber(events.length)}건 표시 중 +

+
+ {visibleCount > 20 ? ( + + ) : null} + {remainingCount > 0 ? ( + + ) : null} +
+
+ ) : null} + )}
- -
-
+
} @@ -824,114 +856,7 @@ export function SafeShieldPage({
-
- -
-
-
-
-

- Protection data -

-
-

- 보호 데이터 -

- - {data.artifact.resolved && data.blocklist.installed && data.blocklist.verificationOk ? ( - - ) : ( - - )} - {data.artifact.resolved && data.blocklist.installed && data.blocklist.verificationOk - ? '최신' - : '확인 필요'} - -
-

- SafeShield가 기기 성능에 맞춰 사용하는 보호 목록의 구성과 최신 상태를 확인합니다. -

-
- - - -
- -
-
-
기기 최적화
-
- {getArtifactOptimizationLabel(data.artifact.tier)} -
-
-
-
마지막 업데이트
-
- {formatArtifactUpdatedAt(data.artifact.version)} -
-
-
- -
- - 세부 정보 - -
- - -
-
-
- -
-

- Custom rules -

-

- 사용자 규칙 -

-
- -
-

- 허용하거나 차단할 도메인을 직접 관리해 SafeShield 보호 정책에 반영합니다. -

- - - 사용자 규칙 관리 - -
-
-
); } diff --git a/luci-app-smartsafehub/frontend/src/pages/SafeShieldRulesPage.tsx b/luci-app-smartsafehub/frontend/src/pages/SafeShieldRulesPage.tsx index 347b2faac..e08a538ae 100644 --- a/luci-app-smartsafehub/frontend/src/pages/SafeShieldRulesPage.tsx +++ b/luci-app-smartsafehub/frontend/src/pages/SafeShieldRulesPage.tsx @@ -1,5 +1,8 @@ +import { SectionHeading } from '../components/SectionHeading'; import type { TargetedEvent, TargetedSubmitEvent } from 'preact'; -import { useEffect, useMemo, useState } from 'preact/hooks'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'preact/hooks'; + +import { RelativeTimestamp } from '../components/RelativeTimestamp'; import { AlertIcon, @@ -12,7 +15,7 @@ import { } from '../components/Icons'; import { ErrorPanel, LoadingPanel } from '../components/StatePanels'; import type { SafeShieldRulesAction } from '../hooks/useSafeShieldRules'; -import type { SafeShieldRuleAction, SafeShieldRules } from '../types/rules'; +import type { RecommendedRuleAssignment, RecommendedRulesStatus, SafeShieldRuleAction, SafeShieldRules } from '../types/rules'; interface SafeShieldRulesPageProps { action: SafeShieldRulesAction | null; @@ -32,7 +35,9 @@ interface SafeShieldRulesPageProps { onDismissFeedback: () => void; onRetry: () => void; // Injected only by the authenticated router app; the public demo has no RPC. - onLoadRecommendedRulesStatus?: () => Promise<{ domains: string[] }>; + onLoadRecommendedRulesStatus?: () => Promise; + onSyncRecommendedRules?: () => Promise; + onRevokeRecommendedRule?: (ruleId: number) => Promise; } interface RuleListCardProps { @@ -50,6 +55,9 @@ interface RuleListCardProps { rules: string[]; recommendedDomains: string[]; recommendedKnown: boolean; + recommendedRules: RecommendedRuleAssignment[]; + onRequestRevoke?: (rule: RecommendedRuleAssignment) => void; + revokeBusy: boolean; } function normalizeDomain(value: string): string { @@ -81,36 +89,48 @@ function RuleListCard({ rules, recommendedDomains, recommendedKnown, + recommendedRules, + onRequestRevoke, + revokeBusy, }: RuleListCardProps) { const [input, setInput] = useState(''); const [search, setSearch] = useState(''); const [validationError, setValidationError] = useState(null); + const [manualExpanded, setManualExpanded] = useState(true); + // Small groups start expanded; larger lists start collapsed. Once the user + // changes it, keep their choice even when the next sync updates the list. + const [recommendedExpanded, setRecommendedExpanded] = useState(null); const isAllow = kind === 'allow'; const busy = action !== null; const filteredRules = useMemo(() => { const query = search.trim().toLowerCase(); - return query.length > 0 - ? rules.filter((domain) => domain.includes(query)) - : rules; + return query ? rules.filter((domain) => domain.includes(query)) : rules; }, [rules, search]); + const searching = search.trim().length > 0; + // Reconciler-owned domains remain separate from user-managed manual entries. + const managedRules = isAllow && recommendedKnown + ? filteredRules.filter((domain) => recommendedDomains.includes(domain)) + : []; + const manualRules = isAllow && recommendedKnown + ? filteredRules.filter((domain) => !recommendedDomains.includes(domain)) + : filteredRules; + const visibleRecommendedRules = isAllow ? recommendedRules.filter((rule) => + !searching || rule.title.toLowerCase().includes(search.trim().toLowerCase()) || + rule.domains.some((domain) => domain.includes(search.trim().toLowerCase())) + ) : []; + const showRecommended = searching || (recommendedExpanded ?? (recommendedRules.length || managedRules.length) <= 3); + const showManual = searching || manualExpanded; - async function handleSubmit( - event: TargetedSubmitEvent, - ): Promise { + async function handleSubmit(event: TargetedSubmitEvent): Promise { event.preventDefault(); const domain = normalizeDomain(input); - if (!isValidDomain(domain)) { - setValidationError( - 'example.com 형식의 도메인을 입력해 주세요. URL, IP 주소, 와일드카드는 사용할 수 없습니다.', - ); + setValidationError('example.com 형식의 도메인을 입력해 주세요. URL, IP 주소, 와일드카드는 사용할 수 없습니다.'); return; } - setValidationError(null); await onAddRule(kind, domain, () => { - // Clear as soon as the server has saved the rule, not after DNS apply. - // Do not erase a newer draft if the input was edited in the meantime. + // Clear when persisted, not after the slower DNS refresh finishes. setInput((current) => normalizeDomain(current) === domain ? '' : current); }); } @@ -118,56 +138,71 @@ function RuleListCard({ const theme = isAllow ? { badge: 'bg-emerald-100 text-emerald-800', - button: - 'border-emerald-700 bg-emerald-700 text-white hover:border-emerald-800 hover:bg-emerald-800 focus-visible:ring-emerald-100', + button: 'border-emerald-700 bg-emerald-700 text-white hover:border-emerald-800 hover:bg-emerald-800 focus-visible:ring-emerald-100', icon: 'bg-emerald-50 text-emerald-700', label: '허용 목록', - description: - '차단 목록에 포함되어도 허용할 도메인과 하위 도메인을 등록합니다.', + description: '직접 허용하거나 웹사이트 추천으로 추가된 도메인을 관리합니다.', empty: '등록된 허용 도메인이 없습니다.', } : { badge: 'bg-red-100 text-red-800', - button: - 'border-red-700 bg-red-700 text-white hover:border-red-800 hover:bg-red-800 focus-visible:ring-red-100', + button: 'border-red-700 bg-red-700 text-white hover:border-red-800 hover:bg-red-800 focus-visible:ring-red-100', icon: 'bg-red-50 text-red-700', label: '차단 목록', description: 'SafeShield 기본 목록에 없더라도 직접 차단할 도메인을 등록합니다.', empty: '등록된 차단 도메인이 없습니다.', }; + function renderRule(domain: string) { + // A manual entry that happens to match a cloud recommendation must + // remain deletable. Only entries actually owned by the reconciler lock. + const cloudManaged = isAllow && recommendedKnown && recommendedDomains.includes(domain); + const ruleOrigin = !isAllow || recommendedKnown + ? (cloudManaged ? '추천 규칙' : '직접 추가') + : null; + const deleting = action?.operation === 'delete' && action.action === kind && action.domain === domain; + return ( +
  • +
    + {domain} +
    + {cloudManaged ? ( + + 웹사이트 관리 + + ) : ( + + )} +
  • + ); + } + return ( -
    +
    -
    -
    - - {isAllow ? ( - - ) : ( - - )} - -
    -
    -

    - {theme.label} -

    - - {rules.length} - -
    -

    - {theme.description} -

    +
    + + {isAllow ? : } + +
    +
    +

    {theme.label}

    + {rules.length}
    +

    {theme.description}

    - - +
    ) => - setInput(event.currentTarget.value) - } + onInput={(event: TargetedEvent) => setInput(event.currentTarget.value)} placeholder="example.com" spellcheck={false} type="text" value={input} />
    - {validationError ? ( -

    - {validationError} -

    - ) : null} + {validationError ?

    {validationError}

    : null}
    -
    - {filteredRules.length > 0 ? ( -
      - {filteredRules.map((domain) => { - // Only domains created by the local recommender are managed. - // A previously manual allow entry stays manual even when the - // same domain appears in an assigned recommendation. - const cloudManaged = isAllow && recommendedDomains.includes(domain); - const ruleOrigin = !isAllow || recommendedKnown - ? (cloudManaged ? '추천 규칙' : '직접 추가') - : null; - const deleting = - action?.operation === 'delete' && - action.action === kind && - action.domain === domain; - - return ( -
    • -
      - - {domain} - - {ruleOrigin ? ( - - {ruleOrigin} - - ) : null} -
      - -
    • - ); - })} -
    - ) : ( -
    - -

    - {search.trim().length > 0 - ? '검색 결과가 없습니다.' - : theme.empty} -

    -
    - )} -
    + {isAllow && recommendedKnown ? ( +
    +
    + + +
    +
    + + +
    +
    + ) : ( +
    + {filteredRules.length ? ( +
      {filteredRules.map(renderRule)}
    + ) : ( +
    + +

    {searching ? '검색 결과가 없습니다.' : theme.empty}

    +
    + )} +
    + )}
    ); @@ -295,25 +332,192 @@ export function SafeShieldRulesPage({ onDismissFeedback, onRetry, onLoadRecommendedRulesStatus, + onSyncRecommendedRules, + onRevokeRecommendedRule, }: SafeShieldRulesPageProps) { - const [recommendedDomains, setRecommendedDomains] = useState([]); + const [recommendedStatus, setRecommendedStatus] = useState(null); const [recommendedKnown, setRecommendedKnown] = useState(false); + const [syncing, setSyncing] = useState(false); + const [revokeTarget, setRevokeTarget] = useState(null); + const [revokeBusy, setRevokeBusy] = useState(false); + const [revokeConfirming, setRevokeConfirming] = useState(false); + const [revokeConfirmationDelayed, setRevokeConfirmationDelayed] = useState(false); + const pendingRevokeId = useRef(null); + const previousStatusSignature = useRef(null); + const onRetryRef = useRef(onRetry); + onRetryRef.current = onRetry; + const [syncMessage, setSyncMessage] = useState(null); + const [syncError, setSyncError] = useState(null); + const recommendedDomains = recommendedStatus?.domains ?? []; + const recommendedRules = recommendedStatus?.rules ?? []; + + // A revoke can reach Hub even if rpcd times out while SafeShield is applying + // the new rules. Only a confirmed local read can settle its final outcome. + // The local SafeShield list also needs a refresh when a background poll + // observes a new recommendation revision, without reloading the whole page. + const acceptRecommendedStatus = useCallback((status: RecommendedRulesStatus): void => { + setRecommendedStatus(status); + setRecommendedKnown(true); + const signature = JSON.stringify([ + status.revision, status.domains, status.rules.map((rule) => rule.id), + status.syncStatus, + ]); + if (previousStatusSignature.current !== null && previousStatusSignature.current !== signature) { + onRetryRef.current(); + } + previousStatusSignature.current = signature; + + if (status.syncStatus === 'confirmed' && pendingRevokeId.current === null) { + setSyncError(null); + } + if ( + pendingRevokeId.current !== null && + status.syncStatus === 'confirmed' && + !status.rules.some((rule) => rule.id === pendingRevokeId.current) + ) { + pendingRevokeId.current = null; + setRevokeConfirming(false); + setRevokeConfirmationDelayed(false); + setSyncError(null); + setSyncMessage('추천 규칙 적용 해제를 확인했습니다.'); + } + }, []); + useEffect(() => { if (!onLoadRecommendedRulesStatus) return; + const loadStatus = onLoadRecommendedRulesStatus; let mounted = true; - void onLoadRecommendedRulesStatus() - .then((status) => { - if (mounted) { - setRecommendedDomains(status.domains); - setRecommendedKnown(true); - } - }) - .catch(() => { - // Older firmware/API errors must not hide or break manual rules. - if (mounted) setRecommendedKnown(false); - }); - return () => { mounted = false; }; - }, [data?.allow.join('\n'), onLoadRecommendedRulesStatus]); + let inFlight = false; + // Read only the local rpcd state. Hub's 30-second poll runs separately. + function updateStatus(): void { + if (inFlight || syncing || revokeBusy) return; + inFlight = true; + void loadStatus() + .then((status) => { + if (!mounted) return; + acceptRecommendedStatus(status); + }) + .catch(() => { + // Retain the last known result on a transient local RPC failure. + // Do not mislabel a known recommendation as a manually added rule. + }) + .finally(() => { inFlight = false; }); + } + updateStatus(); + const timer = window.setInterval(updateStatus, 15_000); + const onFocus = () => updateStatus(); + const onVisible = () => { if (!document.hidden) updateStatus(); }; + window.addEventListener('focus', onFocus); + document.addEventListener('visibilitychange', onVisible); + return () => { + mounted = false; + window.clearInterval(timer); + window.removeEventListener('focus', onFocus); + document.removeEventListener('visibilitychange', onVisible); + }; + }, [data?.allow.join('\n'), onLoadRecommendedRulesStatus, syncing, revokeBusy, acceptRecommendedStatus]); + + // An RPC timeout cannot prove whether Hub accepted the removal. After a + // minute, explain the delay but continue watching for eventual confirmation. + useEffect(() => { + if (!revokeConfirming) return; + const timer = window.setTimeout(() => setRevokeConfirmationDelayed(true), 60_000); + return () => window.clearTimeout(timer); + }, [revokeConfirming]); + + async function handleSyncRecommended(): Promise { + if (!onSyncRecommendedRules || syncing || revokeBusy) return; + setSyncing(true); + setSyncError(null); + setSyncMessage(null); + try { + const status = await onSyncRecommendedRules(); + acceptRecommendedStatus(status); + setSyncMessage(status.syncStatus === 'confirmed' + ? '서버에서 규칙을 가져와 SafeShield 허용 목록을 확인했습니다.' + : '추천 규칙 상태를 확인 중입니다. 진행 결과는 자동으로 갱신됩니다.'); + onRetry(); // Reload the actual SafeShield allow list after reconciliation. + } catch { + setSyncError('추천 규칙을 동기화하지 못했습니다. 서버 연결 및 기기 등록 상태를 확인해 주세요.'); + } finally { + setSyncing(false); + } + } + + async function handleRevokeRecommended(): Promise { + if (!revokeTarget || revokeBusy || !onRevokeRecommendedRule) return; + const ruleId = revokeTarget.id; + pendingRevokeId.current = ruleId; + setRevokeBusy(true); + setRevokeConfirming(true); + setRevokeConfirmationDelayed(false); + setSyncError(null); + setSyncMessage(null); + try { + const status = await onRevokeRecommendedRule(ruleId); + acceptRecommendedStatus(status); + setRevokeTarget(null); + if (pendingRevokeId.current !== null) { + setSyncMessage('적용 해제 요청을 처리했습니다. 공유기의 허용 목록을 확인하고 있습니다.'); + } + onRetryRef.current(); + } catch { + // Hub may have removed the assignment before the local SafeShield apply + // timed out. Keep checking instead of permanently displaying a failure. + setRevokeTarget(null); + setSyncError('적용 해제 결과를 아직 확인하지 못했습니다. 공유기의 상태를 자동으로 다시 확인합니다.'); + try { + const status = await onLoadRecommendedRulesStatus?.(); + if (status) acceptRecommendedStatus(status); + } catch { /* The 15-second local poll will retry. */ } + } finally { + setRevokeBusy(false); + } + } + + // Only one live notice is rendered. In-progress SafeShield refreshes are + // not failures; the 15-second local poll advances the visible stage. + const syncStage = recommendedStatus?.syncStage; + const syncPending = recommendedStatus?.syncStatus === 'pending'; + const syncDelayed = recommendedStatus?.syncStatus === 'delayed'; + const syncFailed = recommendedStatus?.syncStatus === 'failed'; + const stageLabel = syncStage === 'server_sync' ? '서버 동기화 중' + : syncStage === 'safeshield_refreshing' ? 'SafeShield 갱신 중' + : syncStage === 'applying' ? '추천 규칙 적용 중' + : syncStage === 'verifying' ? '허용 목록 확인 중' + : syncDelayed ? '재확인 지연' : syncFailed ? '동기화 실패' : + syncPending ? '동기화 대기' : '허용 목록 확인'; + const stageDescription = syncStage === 'safeshield_refreshing' && syncPending + ? 'SafeShield가 보호 목록을 갱신하고 있습니다. 갱신이 끝나면 추천 규칙을 자동으로 다시 확인합니다.' + : syncStage === 'applying' && syncPending + ? '추천 규칙을 SafeShield 허용 목록에 반영하고 있습니다.' + : syncStage === 'verifying' && syncPending + ? 'SafeShield 허용 목록에서 추천 규칙 반영 결과를 확인하고 있습니다.' + : syncStage === 'server_sync' && syncPending + ? 'SmartSafeHub 서버에서 최신 추천 규칙을 확인하고 있습니다.' + : syncDelayed + ? '서버 응답이 지연되고 있습니다. 이미 확인한 허용 규칙은 유지되며 자동으로 다시 시도합니다.' + : syncFailed + ? recommendedStatus?.syncReason === 'auth_rejected' + ? 'SmartSafeHub 기기 인증이 거부되었습니다. 공유기 계정 연결 상태를 확인해 주세요.' + : recommendedStatus?.syncReason === 'api_rejected' + ? '서버가 추천 규칙 요청을 거부했습니다. Hub API 배포 상태를 확인해 주세요.' + : recommendedStatus?.syncReason === 'invalid_response' + ? '서버 응답을 확인할 수 없습니다. 규칙은 유지되며 다음 동기화에서 다시 확인합니다.' + : '추천 규칙 반영을 완료하지 못했습니다. 다음 자동 동기화에서 다시 시도합니다.' + : syncPending ? '추천 규칙의 적용 결과를 기다리고 있습니다.' : null; + const waitingForRevoke = revokeConfirming && pendingRevokeId.current !== null; + const currentNotice = waitingForRevoke + ? { tone: 'amber', label: revokeConfirmationDelayed ? '적용 해제 처리 지연' : '적용 해제 확인 중', + description: revokeConfirmationDelayed + ? '적용 해제 확인이 지연되고 있습니다. SafeShield 갱신 중이라면 완료 후 자동으로 재확인합니다.' + : stageDescription ?? '적용 해제 상태를 확인하고 있습니다. 확인되면 목록과 상태가 자동 갱신됩니다.' } + : syncPending || syncDelayed || syncFailed + ? { tone: syncFailed ? 'red' : 'amber', label: stageLabel, description: stageDescription ?? '' } + : syncError ? { tone: 'red', label: '상태 확인 필요', description: syncError } + : recommendedStatus?.syncWarning + ? { tone: 'amber', label: '서버 재확인 지연', description: '이미 확인한 허용 규칙은 유지되며 자동으로 다시 시도합니다.' } + : syncMessage ? { tone: 'green', label: '허용 목록 확인', description: syncMessage } : null; if (loading && !data) { return ; @@ -329,60 +533,94 @@ export function SafeShieldRulesPage({ return ( <> -
    -
    -
    - - - -
    -

    - Local overrides -

    -

    - 사용자 도메인 규칙 -

    -

    - 허용 목록은 오탐을 해제하고, 차단 목록은 원하는 도메인을 SafeShield 보호 목록에 추가합니다. -

    + {revokeTarget ? ( + -
    -
    -
    허용
    -
    {data.counts.allow}
    -
    -
    -
    차단
    -
    {data.counts.block}
    -
    -
    -
    전체
    -
    {data.counts.total}
    -
    -
    +
    + ) : null} + +
    +
    +
    +
    허용 도메인
    +
    {data.counts.allow}개
    +

    차단하지 않도록 등록한 도메인

    +
    +
    +
    차단 도메인
    +
    {data.counts.block}개
    +

    추가로 차단하도록 등록한 도메인

    +
    +
    +
    전체 등록 규칙
    +
    {data.counts.total}개
    +

    허용 및 차단 규칙의 합계

    +
    +
    -
    -
    -
    +
    + +
    +
    +
    +
    -

    추천 사용자 규칙

    - {recommendedKnown && recommendedDomains.length > 0 ? ( - 추천으로 추가 {recommendedDomains.length}개 +

    동기화 상태

    + {onLoadRecommendedRulesStatus ? ( + + {recommendedStatus?.syncStatus === 'confirmed' ? '허용 목록 확인' : stageLabel} + ) : null}
    -
    - 공식 추천 - 커뮤니티 +

    웹사이트에서 선택한 허용 규칙을 약 30초마다 확인합니다. 적용 해제는 이 공유기에서도 할 수 있습니다.

    +
    + 추천으로 추가 {recommendedKnown ? recommendedDomains.length : '—'}개 도메인 + {recommendedStatus?.syncStatus === 'confirmed' ? ( + 허용 확인 {recommendedStatus.confirmedDomains.length}개 + ) : null} + {recommendedStatus?.lastCheckedAt ? ( + {recommendedStatus.syncWarning ? '최근 재확인 시도' : '최근 확인'}: + ) : null}
    -

    SmartSafeHub의 공식 추천과 관리자가 검토한 커뮤니티 허용 규칙을 확인하고, 필요한 규칙을 적용해 보세요.

    -

    공유기 선택과 적용 관리는 SmartSafeHub 웹사이트에서 진행합니다. 적용 결과는 다음 기기 동기화 후 반영되며, 연결된 모든 기기에 영향을 줍니다.

    - 추천 규칙 보기 ↗ +
    + {onSyncRecommendedRules ? ( + + ) : null} + 추천 규칙 보기 ↗ +
    + {currentNotice ? ( +
    +
    + ) : null} +

    추천 규칙 동기화는 SafeShield 차단 목록 갱신과 별개입니다. 허용 목록 확인은 DNS 처리나 서비스 재생 성공을 보장하지 않습니다.

    {!data.applyLocalOverrides ? ( @@ -441,6 +679,9 @@ export function SafeShieldRulesPage({ rules={data.allow} recommendedDomains={recommendedDomains} recommendedKnown={recommendedKnown} + recommendedRules={recommendedRules} + {...(onRevokeRecommendedRule ? { onRequestRevoke: setRevokeTarget } : {})} + revokeBusy={revokeBusy} />
    - -

    - 규칙 저장과 유효성 검사는 SafeShield API가 관리합니다. 도메인은 소문자로 정규화되고 중복 항목은 하나로 유지됩니다. -

    ); } diff --git a/luci-app-smartsafehub/frontend/src/pages/SettingsPage.tsx b/luci-app-smartsafehub/frontend/src/pages/SettingsPage.tsx index cd19cf163..6c47e9f3b 100644 --- a/luci-app-smartsafehub/frontend/src/pages/SettingsPage.tsx +++ b/luci-app-smartsafehub/frontend/src/pages/SettingsPage.tsx @@ -1,3 +1,4 @@ +import { SectionHeading } from '../components/SectionHeading'; import type { ComponentChildren } from 'preact'; import { useEffect, useRef, useState } from 'preact/hooks'; @@ -104,25 +105,33 @@ function ActionCard(props: { icon: ComponentChildren; children?: ComponentChildren; className?: string; + titleActions?: ComponentChildren; + headerActions?: ComponentChildren; }) { return (
    -
    -
    - {props.icon} -
    -
    -

    - {props.title} -

    -

    - {props.description} -

    +
    +
    +
    + {props.icon} +
    +
    +
    +

    + {props.title} +

    + {props.titleActions} +
    +

    + {props.description} +

    +
    + {props.headerActions ?
    {props.headerActions}
    : null}
    {props.children}
    @@ -199,6 +208,7 @@ function TimeSettingsCard(props: { selectedTimezone !== props.data.zonename; return ( + <> } @@ -321,6 +331,8 @@ function TimeSettingsCard(props: { )} + +
    - +
    + ); } @@ -456,10 +469,11 @@ function HealthDiagnosticCard(props: { : '로컬 진단은 계속 동작하지만 장치 상태는 SmartSafeHub 서버에 자동으로 전송되지 않습니다.'; return ( + <> } - title="진단 및 지원" + title="장치 진단" > {(props.actionError || props.actionMessage) && (
    -
    -
    -
    -
    -

    원격 상태 보고

    - - {reporterMembershipLabel(reporter)} - -
    -

    - 장치의 최소 상태 정보와 이상 코드만 SmartSafeHub 서버에 전송합니다. 기본값은 꺼짐이며 언제든지 다시 끌 수 있습니다. -

    -
    - -
    - + + )} + +
    + } + title="원격 상태 보고" + titleActions={ + + {reporterMembershipLabel(reporter)} + + } + headerActions={ + + } + > +
    {reporter?.eligible ? ( -
    +
    ) : ( -
    +

    원격 상태 보고는 유료 멤버십 또는 체험 기간에 사용할 수 있습니다.

    @@ -686,20 +702,14 @@ function HealthDiagnosticCard(props: {
    )} -
    -
    - - - -
    -

    개인정보 보호

    -

    - 진단에 필요한 최소한의 상태 정보만 전송하며, 네트워크 식별 정보와 이용 내용은 보내지 않습니다. -

    -
    -
    - -
    +
    + + } + title="개인정보 보호" + > +
    @@ -738,11 +748,9 @@ function HealthDiagnosticCard(props: {
    -
    -
    - - )} +
    + ); } @@ -766,7 +774,7 @@ function PasswordInputField(props: {
    - +
    -

    +

    예약 재부팅 -

    -

    + +

    공유기를 정해진 시간에 자동으로 재부팅합니다. 기본값은 꺼짐이며 필요한 장치에서만 사용하세요.

    @@ -1650,7 +1658,8 @@ export function SettingsPage({ ) : loading && !data ? (

    진단 파일에 필요한 시스템 정보를 확인하고 있습니다.

    ) : null} -
    + +
    -
    -

    Device settings

    -

    시간 및 예약

    -

    시간 기준과 예약 재부팅을 SmartSafeHub에서 직접 관리합니다.

    -
    -
    + +
    -
    -

    - System management -

    -

    시스템 관리

    -

    - 관리자 비밀번호, 설정 백업·복원과 공유기 시스템 도구를 한곳에서 관리합니다. -

    -
    +
    diff --git a/luci-app-smartsafehub/frontend/src/pages/SmartSafeHubAccountPage.tsx b/luci-app-smartsafehub/frontend/src/pages/SmartSafeHubAccountPage.tsx index 4568feffc..8fb41dad9 100644 --- a/luci-app-smartsafehub/frontend/src/pages/SmartSafeHubAccountPage.tsx +++ b/luci-app-smartsafehub/frontend/src/pages/SmartSafeHubAccountPage.tsx @@ -1,7 +1,8 @@ +import { SectionHeading } from '../components/SectionHeading'; import { useEffect, useState } from 'preact/hooks'; import { formatRelativeTime } from '../app/format'; -import { CheckCircleIcon, ClockIcon, CopyIcon, KeyIcon, ReloadIcon } from '../components/Icons'; +import { CheckCircleIcon, ClockIcon, CopyIcon, ReloadIcon } from '../components/Icons'; import { ErrorPanel, LoadingPanel } from '../components/StatePanels'; import type { DeviceRegistrationStatus } from '../api/smartsafehub'; @@ -134,30 +135,23 @@ export function SmartSafeHubAccountPage({ } return ( -
    -
    -
    -
    -

    Account connection

    -

    기기 계정 연결

    -

    - 계정에 연결하면 웹사이트에서 기기를 관리하고 Cloud 기능과 구독 권한을 사용할 수 있습니다. -

    -
    - - - -
    - +
    + +
    +
    {connected ? ( -
    +
    -

    SmartSafeHub 계정에 연결됨

    -

    연결 코드는 더 이상 필요하지 않으며 새 코드도 발급하지 않습니다.

    +

    SmartSafeHub 계정에 연결됨

    +

    이 기기가 계정과 정상적으로 연결되어 있습니다. 연결 코드는 더 이상 필요하지 않습니다.

    웹사이트에서 기기 관리 -

    +

    웹사이트에서 기기 등록을 해제하면 공유기도 다음 계정 상태 동기화에서 자동으로 연결 해제를 반영합니다.

    ) : ( -
    +

    계정 연결 코드

    @@ -223,7 +217,7 @@ export function SmartSafeHubAccountPage({ )}
    -
    +
    ); } diff --git a/luci-app-smartsafehub/frontend/src/pages/UpdatePage.tsx b/luci-app-smartsafehub/frontend/src/pages/UpdatePage.tsx index 5541e0c5e..c30e6cc38 100644 --- a/luci-app-smartsafehub/frontend/src/pages/UpdatePage.tsx +++ b/luci-app-smartsafehub/frontend/src/pages/UpdatePage.tsx @@ -1,3 +1,4 @@ +import { SectionHeading } from '../components/SectionHeading'; import { FirmwareUpdatesCard } from '../components/FirmwareUpdatesCard'; import { SoftwareUpdatesCard } from '../components/SoftwareUpdatesCard'; import type { FirmwareAction } from '../hooks/useFirmwareUpdates'; @@ -59,6 +60,9 @@ export function UpdatePage({ }: UpdatePageProps) { return (
    +
    + +
    void firmware.refresh()} onUpload={firmware.upload} /> +
    + +
    {feedback ? : null} + {!data.configuration.supported ? ( @@ -435,26 +437,15 @@ export function WanPage({
    ) : null} +
    + +
    void submit(event)} > -
    -
    - - - -
    -

    - Internet -

    -

    인터넷 연결 설정

    -

    - 통신사 또는 상위 네트워크 환경에 맞는 WAN 연결 방식을 선택합니다. -

    -
    -
    - {settingsDirty ? ( + {settingsDirty ? ( +
    - ) : null} -
    +
    + ) : null} -
    +
    연결 방식
    + {data.networks.length ? (
    {data.networks.map((network) => ( @@ -420,6 +422,9 @@ export function WifiPage({ {data.guest && ( <>