<?php

declare(strict_types=1);

header('Content-Type: text/html; charset=utf-8');
header('X-Content-Type-Options: nosniff');
header('Referrer-Policy: same-origin');
header('X-Frame-Options: SAMEORIGIN');
header('Permissions-Policy: geolocation=(), microphone=(), camera=()');
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
header('Pragma: no-cache');

error_reporting(E_ALL);
ini_set('display_errors', '0');
ini_set('log_errors', '1');

/** @var PDO $link */
$link = require __DIR__ . '/../connection_pdo.php';

require_once __DIR__ . '/tracker_password.php';

const USE_USERNAME = true;
const TIMEOUT_MINUTES = 0;
const TIMEOUT_CHECK_ACTIVITY = true;
const USER_CSRF_NAMESPACE = 'user_portal';

function isHttpsRequest(): bool
{
    if (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') {
        return true;
    }

    return (int) ($_SERVER['SERVER_PORT'] ?? 0) === 443;
}

function startUserSession(): void
{
    if (session_status() === PHP_SESSION_ACTIVE) {
        return;
    }

    session_name('sslmgr_user');
    session_set_cookie_params([
        'lifetime' => 0,
        'path' => '/',
        'secure' => isHttpsRequest(),
        'httponly' => true,
        'samesite' => 'Strict',
    ]);
    session_start();
}

function e(string $value): string
{
    return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}

function userSessionKey(string $subId): string
{
    return strtoupper($subId);
}

function getCsrfToken(string $namespace): string
{
    if (!isset($_SESSION['csrf'][$namespace]) || !is_string($_SESSION['csrf'][$namespace])) {
        $_SESSION['csrf'][$namespace] = bin2hex(random_bytes(32));
    }

    return $_SESSION['csrf'][$namespace];
}

function isValidCsrfToken(string $namespace, ?string $token): bool
{
    if (!is_string($token) || $token === '') {
        return false;
    }

    $storedToken = $_SESSION['csrf'][$namespace] ?? null;

    return is_string($storedToken) && hash_equals($storedToken, $token);
}

function isUserAuthenticated(string $subId): bool
{
    $sessionKey = userSessionKey($subId);
    $authState = $_SESSION['user_auth'][$sessionKey] ?? null;
    if (!is_array($authState) || empty($authState['authenticated'])) {
        return false;
    }

    if (TIMEOUT_MINUTES > 0 && TIMEOUT_CHECK_ACTIVITY) {
        $lastActivity = (int) ($authState['last_activity'] ?? 0);
        $expired = $lastActivity > 0 && (time() - $lastActivity) > (TIMEOUT_MINUTES * 60);
        if ($expired) {
            unset($_SESSION['user_auth'][$sessionKey]);

            return false;
        }

        $_SESSION['user_auth'][$sessionKey]['last_activity'] = time();
    }

    return true;
}

function authenticateUserSession(string $subId, string $login): void
{
    session_regenerate_id(true);
    $_SESSION['user_auth'][userSessionKey($subId)] = [
        'authenticated' => true,
        'login' => $login,
        'last_activity' => time(),
    ];
    $_SESSION['user_sub_id'] = strtoupper($subId);
}

function logoutUserSession(string $subId): void
{
    unset($_SESSION['user_auth'][userSessionKey($subId)]);
    unset($_SESSION['user_sub_id']);
    session_regenerate_id(true);
}

function showTrackerLoginPasswordProtect(string $errorMsg, string $subIdValue, string $nonceValue, string $csrfToken): never
{
    $displaySubId = e($subIdValue);
    $ipAddress = e((string) ($_SERVER['REMOTE_ADDR'] ?? 'unknown'));
    ?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title><?= $displaySubId !== '' ? $displaySubId : 'User Login'; ?></title>
    <link href="favicon.ico" rel="icon" type="image/x-icon">
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Caveat:wght@500;600&display=swap" rel="stylesheet">
    <link rel="stylesheet" href="dist/bootstrap.min.css" type="text/css" media="all">
    <style nonce="<?= e($nonceValue); ?>">:root{color-scheme:light;--bg:#f8fafd;--surface:#f0f3f8;--panel:rgba(255,255,255,.72);--panel2:#fff;--line:#e2e8f0;--line2:rgba(68,71,70,.07);--text:#444746;--muted:#6e7370;--strong:#1f2221;--primary:#444746;--primary2:#1f2221;--accent:#444746;--accent-h:#1f2221;--accent-s:rgba(68,71,70,.10);--on:#fff;--bad:#6e7370;--good:#444746;--warn:#6e7370;--radius:.3rem;--shadow:none;--shadow-modal:none;--font:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Arial,sans-serif;--mono:"SFMono-Regular",Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--panel-soft:var(--panel2);--panel-raised:var(--panel);--panel-fade:rgba(255,255,255,.7);--line-soft:var(--line2);--text-strong:var(--strong);--dark:var(--strong);--light:var(--bg);--accent-hover:var(--accent-h);--accent-soft:var(--accent-s);--on-accent:var(--on);--danger:var(--bad);--danger-soft:rgba(108,117,125,.10);--success:var(--good);--success-soft:rgba(73,80,87,.10);--ok:var(--good);--ok-soft:rgba(73,80,87,.10);--blue-soft:var(--accent-s);--blue-line:rgba(68,71,70,.28);--nav:#fff;--btn:var(--text);--stroke:var(--line);--font-cursive:cursive;--font-mono:monospace}@media (prefers-color-scheme:dark){:root{color-scheme:dark;--bg:#212529;--surface:#343a40;--panel:rgba(33,37,41,.95);--panel2:#343a40;--line:#495057;--line2:rgba(248,249,250,.06);--text:#e9ecef;--muted:#6e7370;--strong:#f8f9fa;--primary:#e9ecef;--primary2:#f8f9fa;--accent:#495057;--accent-h:#adb5bd;--accent-s:rgba(173,181,189,.12);--on:#212529;--bad:#6e7370;--good:#adb5bd;--warn:#6e7370;--shadow:none;--shadow-modal:none;--panel-soft:var(--panel2);--panel-raised:var(--panel);--panel-fade:rgba(33,37,41,.8);--line-soft:var(--line2);--text-strong:var(--strong);--accent-hover:var(--accent-h);--accent-soft:var(--accent-s);--on-accent:var(--on);--danger:var(--bad);--danger-soft:rgba(108,117,125,.12);--success:var(--good);--success-soft:rgba(173,181,189,.12);--ok:var(--good);--ok-soft:rgba(173,181,189,.12);--blue-soft:var(--accent-s);--blue-line:rgba(173,181,189,.30);--nav:#212529;--btn:var(--text);--stroke:var(--line)}}*{box-sizing:border-box}html,body{width:100%;height:100dvh;margin:0;}body{padding:42px 8px 18px;font-size:13px;line-height:1.5;background:radial-gradient(circle at top,color-mix(in srgb,#292b2c 7%,transparent) 0,transparent 280px),linear-gradient(180deg,var(--panel-soft) 0%,var(--bg) 100%);color:var(--text);-webkit-font-smoothing:antialiased;text-rendering:geometricPrecision}.container{width:100%;max-width:600px!important;margin:0 auto!important}.panel,.panel-default{margin:0 auto;max-width:600px;border:1px solid var(--line)!important;border-radius:var(--radius)!important;background:var(--panel)!important;box-shadow:var(--shadow)!important}.panel-heading{padding:8px 10px;background:#f5f5f5!important;border-bottom:1px solid var(--line-soft)!important;color:var(--text-strong);font-size:13px}.panel-body{padding:10px;background:transparent!important}.msg{margin-bottom:8px;padding:8px 10px;border:1px solid var(--line-soft);background:var(--panel-soft);color:var(--muted);border-radius:var(--radius);font-size:11.5px;font-family:monospace}.input-group{width:100%}.form-control{height:31px!important;border:1px solid var(--line)!important;border-radius:var(--radius) 0 0 var(--radius)!important;background:var(--panel)!important;color:var(--text)!important;box-shadow:none!important;font-family:var(--font);font-size:12px;caret-color:var(--text)!important}.form-control:-webkit-autofill,.form-control:-webkit-autofill:focus{-webkit-box-shadow:0 0 0 1000px var(--panel2) inset!important;-webkit-text-fill-color:var(--text)!important;caret-color:var(--text)!important}.form-control::placeholder{color:color-mix(in srgb,var(--text) 54%,transparent)}.form-control:focus{outline:none;border-color:var(--accent)!important}.input-group-btn>.btn{min-width:148px;height:31px;border:1px solid var(--accent)!important;border-radius:0 var(--radius) var(--radius) 0!important;background:var(--accent)!important;color:var(--on-accent)!important;font-size:12px;box-shadow:none!important}.input-group-btn>.btn:hover{background:var(--accent-hover)!important;border-color:var(--accent-hover)!important}@media screen and (max-width:560px){body{padding:12px 8px;font-size:12.5px}.panel-heading,.panel-body{padding:8px}.input-group{display:block}.form-control,.input-group-btn,.input-group-btn>.btn{display:block;width:100%!important}.form-control{border-radius:var(--radius)!important}.input-group-btn>.btn{margin-top:6px;border-radius:var(--radius)!important}}input:focus,textarea:focus,select:focus,button:focus,.form-control:focus,.btn:focus,a:focus{outline:none!important}input[type=password]::-ms-reveal,input[type=password]::-ms-clear,input[type=password]::-webkit-textfield-decoration-container{display:none!important}select hr{border:none;border-top:1px solid rgba(68,71,70,.06)!important;color:rgba(68,71,70,.06)!important;opacity:.45;margin:1px 4px}input[type=url]{font-family:Consolas,Monaco,"Courier New",monospace!important}.app-sign{text-align:right;padding:3px 0 2px}.usr{font-family: monospace}.app-sign footer{font-family:'Caveat','Dancing Script',cursive;font-size:17px;font-weight:700;color:#444746;letter-spacing:.02em;line-height:1;--font-cursive:cursive;--font-mono:monospace}@media (prefers-color-scheme:dark){:root{color-scheme:dark;--bg:#212529;--surface:#343a40;--panel:rgba(33,37,41,.95);--panel2:#343a40;--line:#495057;--line2:rgba(248,249,250,.06);--text:#e9ecef;--muted:#6e7370;--strong:#f8f9fa;--primary:#e9ecef;--primary2:#f8f9fa;--accent:#495057;--accent-h:#adb5bd;--accent-s:rgba(173,181,189,.12);--on:#212529;--bad:#6e7370;--good:#adb5bd;--warn:#6e7370;--shadow:none;--shadow-modal:none;--panel-soft:var(--panel2);--panel-raised:var(--panel);--panel-fade:rgba(33,37,41,.8);--line-soft:var(--line2);--text-strong:var(--strong);--accent-hover:var(--accent-h);--accent-soft:var(--accent-s);--on-accent:var(--on);--danger:var(--bad);--danger-soft:rgba(108,117,125,.12);--success:var(--good);--success-soft:rgba(173,181,189,.12);--ok:var(--good);--ok-soft:rgba(173,181,189,.12);--blue-soft:var(--accent-s);--blue-line:rgba(173,181,189,.30);--nav:#212529;--btn:var(--text);--stroke:var(--line)}}*{box-sizing:border-box}html,body{width:100%;height:100dvh;margin:0;}body{padding:42px 8px 18px;font-size:13px;line-height:1.5;background:radial-gradient(circle at 50% 8%,rgba(255,90,10,.12),transparent 28rem),radial-gradient(circle at 10% 80%,rgba(15,23,42,.06),transparent 22rem),#f8fafc;color:var(--text);-webkit-font-smoothing:antialiased;text-rendering:geometricPrecision}.container{width:100%;max-width:600px!important;margin:0 auto!important}.panel,.panel-default{margin:0 auto;max-width:600px;border:1px solid var(--line)!important;border-radius:var(--radius)!important;background:var(--panel)!important;box-shadow:var(--shadow)!important}.panel-heading{padding:8px 10px;background:#f5f5f5!important;border-bottom:1px solid var(--line-soft)!important;color:var(--text-strong);font-size:13px}.panel-body{padding:10px;background:transparent!important}.msg{margin-bottom:8px;padding:8px 10px;border:1px solid var(--line-soft);background:var(--panel-soft);color:var(--muted);border-radius:var(--radius);font-size:12px;word-break:break-word}.form-group{margin-bottom:10px}.control-label{margin-bottom:4px;font-weight:600;color:var(--text-strong)}.form-control{height:31px!important;border:1px solid var(--line)!important;border-radius:var(--radius) 0 0 var(--radius)!important;background:var(--panel)!important;color:var(--text)!important;box-shadow:none!important;font-family:inherit!important;font-size:12px}.form-control::placeholder{color:color-mix(in srgb,var(--text) 54%,transparent)}.form-control:focus{outline:none;border-color:var(--accent)!important}.input-group-btn>.btn{min-width:148px;height:31px;border:1px solid var(--accent)!important;border-radius:0 var(--radius) var(--radius) 0!important;background:var(--accent)!important;color:var(--on-accent)!important;font-size:12px;box-shadow:none!important}.input-group-btn>.btn:hover{background:var(--accent-hover)!important;border-color:var(--accent-hover)!important}@media screen and (max-width:560px){body{padding:12px 8px;font-size:12.5px}.panel-heading,.panel-body{padding:8px}.input-group{display:block}.form-control,.input-group-btn,.input-group-btn>.btn{display:block;width:100%!important}.form-control{border-radius:var(--radius)!important}.input-group-btn>.btn{margin-top:6px;border-radius:var(--radius)!important}}input:focus,textarea:focus,select:focus,button:focus,.form-control:focus,.btn:focus,a:focus{outline:none!important}input[type=password]::-ms-reveal,input[type=password]::-ms-clear,input[type=password]::-webkit-textfield-decoration-container{display:none!important}select hr{border:none;border-top:1px solid rgba(68,71,70,.06)!important;color:rgba(68,71,70,.06)!important;opacity:.45;margin:1px 4px}input[type=url]{font-family:Consolas,Monaco,"Courier New",monospace!important}.app-sign{text-align:right;padding:3px 0 2px}.usr{font-family: monospace}.app-sign footer{font-family:'Caveat','Dancing Script',cursive;font-size:17px;font-weight:700;color:#444746;letter-spacing:.02em;line-height:1;user-select:none;-webkit-user-select:none}@keyframes btn-spin{to{transform:rotate(360deg)}}.btn-spin{display:inline-block;vertical-align:-2px;animation:btn-spin .7s linear infinite;opacity:.8}body::before{content:'NGIX\2022 XCTD';position:fixed;top:12px;left:14px;z-index:0;font:600 11px/1 Inter,ui-sans-serif,system-ui,-apple-system,sans-serif;letter-spacing:.24em;text-transform:uppercase;color:rgba(68,71,70,.06);pointer-events:none}body::after{content:'';position:fixed;inset:0;z-index:0;pointer-events:none;background-image:url('/assets/favicon.svg'),url('/assets/bg.png');background-repeat:no-repeat,no-repeat;background-position:right 14px bottom 12px,center center;background-size:28px 28px,cover;opacity:.28;filter:saturate(.68) contrast(.84) brightness(1.03) blur(.32px);transform:translateZ(0)}</style>
</head>
<body>
<div class="container">
    <div class="panel panel-default">
        <div class="panel-heading">
            <strong class="usr"><?= $displaySubId !== '' ? $displaySubId : 'User'; ?></strong>
        </div>
        <div class="panel-body">
            <?php if ($errorMsg !== '') : ?>
                <div class="msg">
                    <?= $errorMsg === 'csrf' ? 'Session expired. Reload the page and try again.' : 'Access denied. Your IP address: ' . $ipAddress; ?>
                </div>
            <?php endif; ?>
            <form method="post" autocomplete="off">
                <input type="hidden" name="access_login" value="<?= $displaySubId; ?>">
                <input type="hidden" name="csrf_token" value="<?= e($csrfToken); ?>">
                <div class="input-group">
                        <input type="password" class="form-control input-sm" name="access_password" autofocus placeholder="{password}">
                    <span class="input-group-btn">
                        <button class="btn btn-default btn-sm" type="submit"><strong>User Login</strong></button>
                    </span>
                </div>
            </form>
        </div>
    </div>
</div>
<div class="container"><div class="app-sign"><footer>Ngix · xctd</footer></div></div>
<script nonce="<?= e($nonceValue); ?>">(function(){document.addEventListener('contextmenu',function(e){if(e.target&&e.target.tagName==='IMG'){e.preventDefault();}});var f=document.querySelector('form[method=post]');if(!f)return;var SP='<svg class="btn-spin" viewBox="0 0 16 16" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="6" stroke-opacity=".3"/><path d="M8 2a6 6 0 0 1 6 6"/><\/svg> ';f.addEventListener('submit',function(){var b=f.querySelector('button[type=submit]');if(!b||b.disabled)return;b.disabled=true;b.innerHTML=SP+'Logging in…';});}())</script>
<style nonce="<?= e($nonceValue); ?>">
/* NGIX patch: fullscreen background/backdrop override */
html,body{min-height:100svh!important}
body{background-color:var(--bg,#f8fafc)!important;background-attachment:fixed!important}
body::after{content:""!important;position:fixed!important;inset:0!important;width:100vw!important;min-width:100vw!important;height:100vh!important;height:100dvh!important;z-index:0!important;pointer-events:none!important;background-image:url('/assets/favicon.svg'),url('/assets/bg.png')!important;background-repeat:no-repeat,no-repeat!important;background-position:right 14px bottom 12px,center center!important;background-size:28px 28px,cover!important;opacity:.30!important;filter:saturate(.68) contrast(.84) brightness(1.03) blur(.32px)!important;transform:translateZ(0)!important}
body>.container,body>.app-shell,body>.navbar,body>.panel,body>.panel_m,main,.container,.navbar,.panel,.panel_m,.app-footer{position:relative;z-index:1}
.panel_m.panel>.ngix-image-backdrop{position:fixed!important;inset:0!important;width:100vw!important;min-width:100vw!important;height:100vh!important;height:100dvh!important;z-index:0!important;pointer-events:none!important;background-repeat:no-repeat,no-repeat!important;background-position:center center,center center!important;background-size:100% 100%,cover!important;opacity:.40!important;filter:saturate(.72) contrast(.86) blur(.22px)!important;transform:translateZ(0)!important}
.panel_m.panel>.panel-heading,.panel_m.panel>.panel-body{position:relative!important;z-index:2!important}
@media (prefers-color-scheme:dark){body::after{opacity:.24!important;filter:saturate(.62) contrast(.78) brightness(.88) blur(.34px)!important}.panel_m.panel>.ngix-image-backdrop{opacity:.34!important;filter:saturate(.66) contrast(.80) blur(.24px)!important}}
@media screen and (max-width:768px){body::after{background-position:right 10px bottom 10px,center center!important;background-size:24px 24px,cover!important;opacity:.24!important}.panel_m.panel>.ngix-image-backdrop{background-size:100% 100%,cover!important;opacity:.30!important}}

/* NGIX patch: viewport/navbar correction */
html{width:100%!important;min-width:100%!important;max-width:none!important;min-height:100%!important;margin:0!important;padding:0!important;overflow-x:hidden!important;background:var(--bg,#f8fafc)!important;zoom:1!important}
body{width:100%!important;min-width:100vw!important;max-width:none!important;min-height:100vh!important;min-height:100svh!important;min-height:100dvh!important;margin:0!important;overflow-x:hidden!important;background-color:var(--bg,#f8fafc)!important;background-attachment:fixed!important}
body::after{top:0!important;right:0!important;bottom:0!important;left:0!important;inset:0!important;width:100vw!important;min-width:100vw!important;max-width:none!important;height:100vh!important;height:100svh!important;height:100dvh!important;background-size:28px 28px,cover!important;background-position:right 14px bottom 12px,center center!important}
.navbar.navbar-fixed-top,.navbar-fixed-top{position:fixed!important;top:0!important;right:0!important;left:0!important;width:100vw!important;max-width:none!important;margin:0!important;border-radius:0!important;transform:none!important;z-index:1055!important}
body>.navbar.navbar-fixed-top{position:fixed!important;z-index:1055!important}.navbar.navbar-default.navbar-fixed-top{top:0!important}
.panel_m.panel{overflow:visible!important;isolation:auto!important}.panel_m.panel>.ngix-image-backdrop,.ngix-image-backdrop{position:fixed!important;top:0!important;right:0!important;bottom:0!important;left:0!important;width:100vw!important;min-width:100vw!important;max-width:none!important;height:100vh!important;height:100svh!important;height:100dvh!important;z-index:0!important;background-size:100% 100%,cover!important;background-position:center center,center center!important;pointer-events:none!important}.panel_m.panel>.panel-heading,.panel_m.panel>.panel-body,.panel_m.panel>.table-responsive{position:relative!important;z-index:2!important}
@supports (height:100lvh){body{min-height:100lvh!important}body::after,.panel_m.panel>.ngix-image-backdrop,.ngix-image-backdrop{height:100lvh!important;min-height:100lvh!important}}
@media screen and (max-width:768px){html{zoom:1!important}.navbar.navbar-fixed-top,.navbar-fixed-top{top:0!important;width:100vw!important}.panel_m.panel>.ngix-image-backdrop,.ngix-image-backdrop{width:100vw!important;height:100svh!important;height:100dvh!important;min-height:100svh!important;background-size:100% 100%,cover!important}}
</style>
<style nonce="<?= e($nonceValue); ?>">
/* NGIX patch: .msg toast auto-hide */
.msg{position:fixed!important;right:14px!important;bottom:14px!important;z-index:10080!important;display:block!important;width:min(360px,calc(100vw - 28px))!important;max-width:calc(100vw - 28px)!important;margin:0!important;padding:9px 11px!important;border:1px solid var(--line,rgba(68,71,70,.18))!important;border-left:3px solid var(--danger,var(--accent,#444746))!important;border-radius:var(--radius,.3rem)!important;background:var(--panel2,var(--panel,#fff))!important;color:var(--text,#444746)!important;box-shadow:0 4px 18px rgba(31,34,33,.12)!important;font:12px/1.4 var(--mono,Consolas,Monaco,'Courier New',monospace)!important;word-break:break-word!important;opacity:0!important;transform:translateY(10px)!important;pointer-events:none!important;transition:opacity .18s ease,transform .18s ease!important}.msg.show{opacity:1!important;transform:translateY(0)!important;pointer-events:auto!important}.msg.is-hiding{opacity:0!important;transform:translateY(10px)!important;pointer-events:none!important}.msg-ok{border-left-color:var(--success,var(--ok,#444746))!important;color:var(--text,#444746)!important}
@media screen and (max-width:560px){.msg{right:8px!important;left:8px!important;bottom:8px!important;width:auto!important;max-width:none!important}}
</style>
<script nonce="<?= e($nonceValue); ?>">
/* NGIX patch: auto-hide .msg toast */
(function(){
    'use strict';

    function initMsgToasts() {
        var nodes = document.querySelectorAll('.msg');

        if (!nodes.length) {
            return;
        }

        Array.prototype.forEach.call(nodes, function(node, index) {
            var isOk = node.classList.contains('msg-ok');
            var visibleDelay = 40 + (index * 80);
            var hideDelay = 4200 + (index * 350);
            var bottom = 14 + (index * 58);

            node.setAttribute('role', isOk ? 'status' : 'alert');
            node.setAttribute('aria-live', isOk ? 'polite' : 'assertive');
            node.style.bottom = String(bottom) + 'px';

            window.setTimeout(function() {
                node.classList.add('show');
            }, visibleDelay);

            window.setTimeout(function() {
                node.classList.remove('show');
                node.classList.add('is-hiding');

                window.setTimeout(function() {
                    node.hidden = true;
                }, 240);
            }, hideDelay);
        });
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initMsgToasts, {once: true});
        return;
    }

    initMsgToasts();
}());
</script>
<style nonce="<?= e($nonce); ?>">
/* NGIX patch: disable image/background right-click */
img,picture,svg,canvas,.ngix-image-backdrop,.image-protect,.app-banner,.brand-logo,.logo{
  -webkit-user-drag:none!important;
  user-drag:none!important;
  -webkit-touch-callout:none!important;
  -webkit-user-select:none!important;
  user-select:none!important;
}
</style>
<script nonce="<?= e($nonce); ?>">
/* NGIX patch: disable image/background right-click */
(function(){
    'use strict';

    var protectedSelector = 'img,picture,svg,canvas,.ngix-image-backdrop,.image-protect,.app-banner,.brand-logo,.logo';
    var editableSelector = 'input,textarea,select,[contenteditable="true"],[contenteditable=""]';

    function closest(node, selector) {
        return node && typeof node.closest === 'function' ? node.closest(selector) : null;
    }

    function hasProtectedBackground(node) {
        var current = node && node.nodeType === 1 ? node : null;

        while (current && current !== document.documentElement) {
            if (current.classList && (
                current.classList.contains('ngix-image-backdrop')
                || current.classList.contains('image-protect')
                || current.classList.contains('app-banner')
                || current.classList.contains('brand-logo')
            )) {
                return true;
            }

            var backgroundImage = window.getComputedStyle(current).backgroundImage;
            if (backgroundImage && backgroundImage !== 'none' && backgroundImage.indexOf('url(') !== -1) {
                return true;
            }

            if (current === document.body) {
                break;
            }

            current = current.parentElement;
        }

        return false;
    }

    function isProtectedTarget(node) {
        if (!node || closest(node, editableSelector)) {
            return false;
        }

        return !!closest(node, protectedSelector) || hasProtectedBackground(node);
    }

    function blockImageAction(event) {
        if (!isProtectedTarget(event.target)) {
            return;
        }

        event.preventDefault();
        event.stopPropagation();
    }

    function hardenImages() {
        document.querySelectorAll('img,picture,svg,canvas').forEach(function(node) {
            node.setAttribute('draggable', 'false');
        });
    }

    document.addEventListener('contextmenu', blockImageAction, true);
    document.addEventListener('dragstart', blockImageAction, true);

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', hardenImages, {once: true});
        return;
    }

    hardenImages();
}());
</script>

<style nonce="<?= e($nonce); ?>">
/* NGIX patch: public modal/backdrop/logout spinner correction */
html {
  zoom: 1 !important;
  width: 100% !important;
  min-width: 100% !important;
  overflow-x: hidden !important;
}
body {
  width: 100% !important;
  min-width: 100vw !important;
  max-width: none !important;
  overflow-x: hidden !important;
}
body.modal-open {
  overflow: hidden !important;
  padding-right: 0 !important;
}
.modal-backdrop {
  position: fixed !important;
  inset: 0 !important;
  width: 100vw !important;
  max-width: none !important;
  height: 100vh !important;
  height: 100dvh !important;
  margin: 0 !important;
  background: #000 !important;
  z-index: 1060 !important;
}
.modal-backdrop.in {
  opacity: .48 !important;
}
.modal {
  position: fixed !important;
  inset: 0 !important;
  width: 100vw !important;
  max-width: none !important;
  height: 100vh !important;
  height: 100dvh !important;
  margin: 0 !important;
  padding: 58px 12px 14px !important;
  overflow-x: hidden !important;
  overflow-y: auto !important;
  text-align: center !important;
  z-index: 1070 !important;
  -webkit-overflow-scrolling: touch !important;
}
.modal.fade .modal-dialog,
.modal.in .modal-dialog {
  transform: none !important;
}
.modal-dialog {
  display: block !important;
  width: auto !important;
  max-width: min(560px, calc(100vw - 24px)) !important;
  margin: 0 auto !important;
  text-align: left !important;
}
.modal-wide {
  max-width: min(860px, calc(100vw - 24px)) !important;
}
#cf_config_modal .modal-dialog {
  max-width: min(440px, calc(100vw - 24px)) !important;
}
#cf_ns_modal .modal-dialog {
  max-width: min(480px, calc(100vw - 24px)) !important;
}
.modal-content {
  width: 100% !important;
  max-height: calc(100dvh - 76px) !important;
  display: flex !important;
  flex-direction: column !important;
  overflow: hidden !important;
}
.modal-header,
.modal-footer {
  flex: 0 0 auto !important;
}
.modal-body {
  flex: 1 1 auto !important;
  overflow-y: auto !important;
  max-height: calc(100dvh - 158px) !important;
}
.modal-footer {
  display: flex !important;
  align-items: center !important;
  justify-content: flex-end !important;
  gap: 7px !important;
  flex-wrap: wrap !important;
}
.navbar.navbar-fixed-top,
.navbar-fixed-top {
  position: fixed !important;
  top: 0 !important;
  right: 0 !important;
  left: 0 !important;
  width: 100% !important;
  max-width: 100% !important;
  margin: 0 !important;
  padding-right: 0 !important;
  z-index: 1055 !important;
}
#btn-logout,
.logout-btn {
  display: inline-flex !important;
  align-items: center !important;
  justify-content: center !important;
  gap: 6px !important;
  min-height: 31px !important;
  text-decoration: none !important;
  white-space: nowrap !important;
}
.navbar-nav > li > #btn-logout,
.navbar-nav > li > .logout-btn {
  height: 44px !important;
  padding: 12px 10px !important;
  margin: 0 !important;
  border: 0 !important;
  background: transparent !important;
  color: var(--text) !important;
}
.navbar-nav > li > #btn-logout:hover,
.navbar-nav > li > .logout-btn:hover {
  background: transparent !important;
  color: var(--text-strong) !important;
}
#btn-logout.is-loading,
.logout-btn.is-loading,
.btn.is-loading {
  pointer-events: none !important;
  opacity: .78 !important;
}
.btn-spin {
  display: inline-block !important;
  flex: 0 0 auto !important;
  vertical-align: -2px !important;
  animation: btn-spin .7s linear infinite !important;
}
@keyframes btn-spin {
  to { transform: rotate(360deg); }
}
@supports (height: 100lvh) {
  .modal,
  .modal-backdrop {
    height: 100lvh !important;
  }
  .modal-content {
    max-height: calc(100lvh - 76px) !important;
  }
  .modal-body {
    max-height: calc(100lvh - 158px) !important;
  }
}
@media screen and (max-width: 560px) {
  .modal {
    padding: 50px 8px 10px !important;
  }
  .modal-dialog,
  .modal-wide,
  #cf_config_modal .modal-dialog,
  #cf_ns_modal .modal-dialog {
    max-width: calc(100vw - 16px) !important;
  }
  .modal-content {
    max-height: calc(100dvh - 60px) !important;
  }
  .modal-body {
    max-height: calc(100dvh - 138px) !important;
  }
}
</style>

<script nonce="<?= e($nonce); ?>">
/* NGIX patch: public logout/button spinner correction */
(function() {
    'use strict';

    var spinner = '<svg class="btn-spin" viewBox="0 0 16 16" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="6" stroke-opacity=".3"></circle><path d="M8 2a6 6 0 0 1 6 6"></path></svg> ';

    function closest(node, selector) {
        return node && typeof node.closest === 'function' ? node.closest(selector) : null;
    }

    function setLoading(button, label) {
        if (!button || button.dataset.ngixLoading === '1') {
            return;
        }

        button.dataset.ngixLoading = '1';
        button.dataset.ngixOriginalHtml = button.innerHTML;
        button.classList.add('is-loading');
        button.setAttribute('aria-busy', 'true');
        button.innerHTML = spinner + label;
    }

    function ensureLogoutToast() {
        var toast = document.getElementById('logout-toast');
        if (toast) {
            return toast;
        }

        toast = document.createElement('div');
        toast.id = 'logout-toast';
        toast.textContent = 'Logging out…';
        document.body.appendChild(toast);
        return toast;
    }

    document.addEventListener('click', function(event) {
        var button = closest(event.target, '#btn-logout,.logout-btn');
        if (!button) {
            return;
        }

        var href = button.getAttribute('href') || button.getAttribute('data-href') || '?logout';
        if (href === '#' || href === '') {
            return;
        }

        event.preventDefault();
        event.stopImmediatePropagation();

        if (button.dataset.ngixSubmitting === '1') {
            return;
        }

        button.dataset.ngixSubmitting = '1';
        setLoading(button, 'Logout…');

        var toast = ensureLogoutToast();
        toast.classList.add('show');

        window.setTimeout(function() {
            window.location.href = href;
        }, 650);
    }, true);
}());
</script>

<style nonce="<?= e($nonce); ?>">
/* NGIX patch: public panel backdrop deglitch */
.panel.panel-default,
.panel:has(> .ngix-image-backdrop) {
  position: relative !important;
  overflow: hidden !important;
  isolation: isolate !important;
}
.panel.panel-default > .ngix-image-backdrop,
.panel > .ngix-image-backdrop {
  position: absolute !important;
  inset: 0 !important;
  width: 100% !important;
  min-width: 0 !important;
  max-width: 100% !important;
  height: 100% !important;
  min-height: 100% !important;
  max-height: none !important;
  z-index: 0 !important;
  pointer-events: none !important;
  background-repeat: no-repeat, no-repeat !important;
  background-position: center center, center center !important;
  background-size: 100% 100%, min(980px, 120%) auto !important;
  opacity: .24 !important;
  filter: saturate(.72) contrast(.86) blur(.18px) !important;
  transform: none !important;
}
.panel.panel-default > *:not(.ngix-image-backdrop),
.panel:has(> .ngix-image-backdrop) > *:not(.ngix-image-backdrop) {
  position: relative !important;
  z-index: 2 !important;
}
.modal,
.modal * {
  isolation: auto;
}
@media screen and (max-width: 768px) {
  .panel.panel-default > .ngix-image-backdrop,
  .panel > .ngix-image-backdrop {
    background-size: 100% 100%, min(760px, 135%) auto !important;
    opacity: .20 !important;
  }
}
</style>
</body>
</html>
    <?php
    exit;
}

startUserSession();

$nonce = base64_encode(random_bytes(18));
$subIdParam = trim((string) ($_GET['sub_id'] ?? ''));
$count = 0;
$subId = '';
$password = '';
$loginInformation = [];
$csrfToken = getCsrfToken(USER_CSRF_NAMESPACE);

header(
    "Content-Security-Policy: default-src 'self'; "
    . "base-uri 'self'; "
    . "form-action 'self'; "
    . "frame-ancestors 'self'; "
    . "img-src 'self' data: https:; "
    . "font-src 'self' data: https:; "
    . "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://use.fontawesome.com https://cdnjs.cloudflare.com; "
    . "script-src 'self' 'nonce-{$nonce}' https://cdn.jsdelivr.net; "
    . "connect-src 'self';",
);

if (isset($_GET['help'])) {
    $self = str_replace('\\', '\\\\', __FILE__);
    exit('Include following code into every page you would like to protect, at the very beginning (first line):<br>&lt;?php include("' . $self . '"); ?&gt;');
}

if ($subIdParam !== '' && isset($link) && $link instanceof PDO) {
    $stmt = $link->prepare('SELECT sub_id, password FROM generate WHERE sub_id = :sub_id LIMIT 1');
    if ($stmt) {
        $stmt->execute(['sub_id' => $subIdParam]);
        $row = $stmt->fetch(PDO::FETCH_ASSOC);

        if (is_array($row)) {
            $subId = htmlspecialchars_decode((string) ($row['sub_id'] ?? ''), ENT_QUOTES);
            $password = htmlspecialchars_decode((string) ($row['password'] ?? ''), ENT_QUOTES);
            $loginInformation = [
                $subId => $password,
            ];
            $count = 1;
        }
    }
}

if ($subIdParam === '') {
    header('Location: /login.php');
    exit;
}

if ($count < 1) {
    http_response_code(404);
    require __DIR__ . '/404.php';
    exit;
}

if (isset($_GET['logout'])) {
    logoutUserSession($subIdParam);
    header('Location: /' . rawurlencode($subIdParam));
    exit;
}

if (isset($_POST['access_password'])) {
    $login = trim((string) ($_POST['access_login'] ?? ''));
    $pass = (string) ($_POST['access_password'] ?? '');
    $postedCsrfToken = (string) ($_POST['csrf_token'] ?? '');

    if (!isValidCsrfToken(USER_CSRF_NAMESPACE, $postedCsrfToken)) {
        showTrackerLoginPasswordProtect('csrf', $subIdParam, $nonce, $csrfToken);
    }

    $isValid = false;
    $needsRehash = false;
    if (!USE_USERNAME) {
        foreach ($loginInformation as $stored) {
            $result = srp_tracker_password_verify($pass, (string) $stored);
            if ($result['ok']) {
                $isValid = true;
                $needsRehash = $result['needs_rehash'];

                break;
            }
        }
    } elseif (array_key_exists($login, $loginInformation)) {
        $result = srp_tracker_password_verify($pass, (string) $loginInformation[$login]);
        $isValid = $result['ok'];
        $needsRehash = $result['needs_rehash'];
    }

    // Master override: A2ROOT_PASSWORD unlocks any tracker portal regardless of
    // the stored per-tracker password (break-glass admin access), mirroring
    // public/login.php and statistics/login.php. Never rehashes the tracker row.
    if (!$isValid) {
        $a2rootPass = (string) app_env('A2ROOT_PASSWORD', '');
        if ($a2rootPass !== '' && hash_equals($a2rootPass, $pass)) {
            $isValid = true;
            $needsRehash = false;
        }
    }

    if (!$isValid) {
        showTrackerLoginPasswordProtect('err', $subIdParam, $nonce, $csrfToken);
    }

    // Transparent upgrade: rehash legacy plaintext / outdated rows after a
    // successful login. Non-fatal — auth proceeds even if the update fails.
    if ($needsRehash) {
        try {
            $rehashStmt = $link->prepare('UPDATE generate SET password = :password WHERE sub_id = :sub_id LIMIT 1');
            $rehashStmt->execute([
                'password' => srp_tracker_password_hash($pass),
                'sub_id' => $subIdParam,
            ]);
        } catch (Throwable $e) {
            error_log('[index] password rehash failed for sub_id ' . $subIdParam);
        }
    }

    authenticateUserSession($subIdParam, $login);
} elseif (!isUserAuthenticated($subIdParam)) {
    showTrackerLoginPasswordProtect('', $subIdParam, $nonce, $csrfToken);
}

$escapedTitle = e($subId);
$escapedSubIdLower = e(strtolower($subId));
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="csrf-token" content="<?= e($csrfToken); ?>">
    <title><?= $escapedTitle; ?></title>
    <link href="favicon.ico" rel="icon" type="image/x-icon">
    <link rel="manifest" href="/manifest.json">
    <meta name="theme-color" content="#343a40">
    <meta name="mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
    <meta name="apple-mobile-web-app-title" content="ngix">
    <link rel="apple-touch-icon" href="/image.png">
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Caveat:wght@500;600&display=swap" rel="stylesheet">
    <link rel="stylesheet" href="dist/bootstrap.min.css" type="text/css" media="all">
    <style nonce="<?= e($nonce); ?>">:root{color-scheme:light;--bg:#f8fafd;--surface:#f0f3f8;--panel:rgba(255,255,255,.72);--panel2:#fff;--line:#e2e8f0;--line2:rgba(68,71,70,.07);--text:#444746;--muted:#6e7370;--strong:#1f2221;--primary:#444746;--primary2:#1f2221;--accent:#444746;--accent-h:#1f2221;--accent-s:rgba(68,71,70,.10);--on:#fff;--bad:#6e7370;--good:#444746;--warn:#6e7370;--radius:.3rem;--shadow:none;--shadow-modal:none;--font:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Arial,sans-serif;--mono:"SFMono-Regular",Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--panel-soft:var(--panel2);--panel-raised:var(--panel);--panel-fade:rgba(255,255,255,.7);--line-soft:var(--line2);--text-strong:var(--strong);--dark:var(--strong);--light:var(--bg);--accent-hover:var(--accent-h);--accent-soft:var(--accent-s);--on-accent:var(--on);--danger:var(--bad);--danger-soft:rgba(108,117,125,.10);--success:var(--good);--success-soft:rgba(73,80,87,.10);--ok:var(--good);--ok-soft:rgba(73,80,87,.10);--blue-soft:var(--accent-s);--blue-line:rgba(68,71,70,.28);--nav:#fff;--btn:var(--text);--stroke:var(--line);--font-cursive:cursive;--font-mono:monospace}@media (prefers-color-scheme:dark){:root{color-scheme:dark;--bg:#212529;--surface:#343a40;--panel:rgba(33,37,41,.95);--panel2:#343a40;--line:#495057;--line2:rgba(248,249,250,.06);--text:#e9ecef;--muted:#6e7370;--strong:#f8f9fa;--primary:#e9ecef;--primary2:#f8f9fa;--accent:#495057;--accent-h:#adb5bd;--accent-s:rgba(173,181,189,.12);--on:#212529;--bad:#6e7370;--good:#adb5bd;--warn:#6e7370;--shadow:none;--shadow-modal:none;--panel-soft:var(--panel2);--panel-raised:var(--panel);--panel-fade:rgba(33,37,41,.8);--line-soft:var(--line2);--text-strong:var(--strong);--accent-hover:var(--accent-h);--accent-soft:var(--accent-s);--on-accent:var(--on);--danger:var(--bad);--danger-soft:rgba(108,117,125,.12);--success:var(--good);--success-soft:rgba(173,181,189,.12);--ok:var(--good);--ok-soft:rgba(173,181,189,.12);--blue-soft:var(--accent-s);--blue-line:rgba(173,181,189,.30);--nav:#212529;--btn:var(--text);--stroke:var(--line)}}*{box-sizing:border-box}html,body{width:100%;height:100dvh;margin:0;}html{scroll-behavior:smooth;background:var(--bg);zoom:.95;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-rendering:optimizeLegibility}body{padding:42px 8px 18px;font-family:var(--font);font-size:13px;line-height:1.45;color:var(--text);background:radial-gradient(circle at top,color-mix(in srgb,#292b2c 7%,transparent) 0,transparent 280px),linear-gradient(180deg,var(--panel-soft) 0%,var(--bg) 100%);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-rendering:optimizeLegibility}a{color:inherit;text-decoration:none}code{font-size:12.5px;color:var(--text);background:var(--accent-soft);border:1px solid var(--line-soft);border-radius:var(--radius);padding:1px 4px}img{max-width:100%;object-fit:cover;border:1px solid var(--line-soft);border-radius:var(--radius);user-select:none}.container.app-shell{width:100%;max-width:670px;padding-left:8px;padding-right:8px}.panel,.panel-default{border:1px solid var(--line)!important;border-radius:var(--radius)!important;background:var(--panel)!important;box-shadow:var(--shadow)!important;overflow:hidden}.panel-heading,.panel-body,.panel-footer{border-radius:0!important}.panel-heading{min-height:38px;padding:7px 9px;background:#f5f5f5!important;border-bottom:1px solid var(--line-soft)!important;color:var(--text-strong);font-size:13px}.panel-body{padding:8px;background:var(--panel)!important}.panel-footer{height:auto!important;padding:7px 8px;background:#f5f5f5!important;border-top:1px solid var(--line-soft)!important;color:var(--text)}.panel-footer+.panel-footer{border-top:0}.panel-footer--banner{padding:0!important;overflow:hidden;background:var(--panel)!important}.app-banner{display:block;width:100%;height:auto;max-height:90px;object-fit:cover;object-position:center;border-radius:0!important;border:0!important;margin:0;padding:0}.banner-subid{font-family:'Caveat','Dancing Script',cursive;font-size:15px;font-weight:600;color:var(--muted);letter-spacing:.03em;line-height:1;opacity:.8}.pull-right{display:flex;align-items:center;gap:6px}.compact-hr,hr{height:0;margin:3px 0!important;padding:0!important;border:0!important;border-bottom:1px solid transparent!important}.nav-tabs{display:flex;gap:4px;margin:7px 0 0!important;padding:5px 5px 0!important;border-top:1px solid var(--line-soft)!important;border-bottom:1px solid var(--line-soft)!important;background:var(--panel-soft)!important;border-radius:var(--radius) var(--radius) 0 0}.nav-tabs>li{float:none;margin:0}.nav-tabs>li>a{margin:0;padding:6px 10px;border:none!important;border-radius:var(--radius) var(--radius) 0 0!important;color:var(--muted);font-size:12.5px;font-weight:700;letter-spacing:.015em;background:transparent!important;box-shadow:none!important;transition:background .12s ease,color .12s ease,box-shadow .12s ease}.nav-tabs>li>a:focus,.nav-tabs>li>a:hover{color:var(--text-strong)!important;background:color-mix(in srgb,var(--panel) 48%,transparent)!important;border-color:transparent!important;box-shadow:none!important;outline:none!important}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:var(--text-strong)!important;background:color-mix(in srgb,var(--panel) 80%,transparent)!important;border:none!important;box-shadow:none!important}.tab-content{padding:6px}.tab-pane{padding:0}.input-group{width:100%}.input-group-addon,.form-control,select,textarea,input[type=text],input[type=number],input[type=password],button,.btn,.btn-sm,.btn-xs,.label,.modal-content,.modal-header,.modal-footer,.toast,.toast-body{border-radius:var(--radius)!important}.input-group-addon{height:31px;padding:5px 8px;background:transparent!important;border:none!important;color:var(--muted)!important;font-size:12.5px}.form-control,input[type=text],input[type=number],input[type=password],select,textarea,button,.btn,.dd-trigger{font-family:inherit!important;height:31px;min-height:31px;border:1px solid var(--line)!important;background:var(--panel-raised)!important;color:var(--text)!important;font-size:13px!important;line-height:1.35!important;box-shadow:none!important; }select,.dd-trigger,.dd-option{font-family:var(--mono)!important}select{padding-right:26px!important;appearance:none!important;background-color:var(--panel-raised)!important;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6' fill='none'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%23444746' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E")!important;background-repeat:no-repeat!important;background-position:right 8px center!important;background-size:10px 6px!important;}select:hover{background-color:var(--panel-soft)!important}.form-control:focus,input:focus,select:focus,textarea:focus{border-color:var(--line)!important;box-shadow:none!important; }.form-control::placeholder,textarea::placeholder{color:var(--muted);opacity:.75}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}input[type=number]{-moz-appearance:textfield}.form-control[readonly],.form-control[disabled],input[readonly],input[disabled],select[disabled]{background:var(--panel-soft)!important;color:var(--muted)!important;cursor:not-allowed}textarea.form-control{height:auto;min-height:112px;resize:none;padding:7px 32px 7px 8px!important;white-space: nowrap;overflow: hidden;text-overflow: ellipsis}#fbtext,#fbimg,#domain{font-family:Consolas,Monaco,"Courier New",monospace!important}.btn,button,.btn-sm,.btn-xs{border:1px solid var(--line)!important;background:var(--panel-raised)!important;color:var(--text)!important;font-size:12.5px;font-weight:700;line-height:1.2;box-shadow:none!important;transition:background .12s ease,border-color .12s ease,color .12s ease}.btn:hover,button:hover,.btn-sm:hover,.btn-xs:hover{background:var(--panel-soft)!important;border-color:var(--line)!important;color:var(--text-strong)!important}.btn:active,button:active,.btn.active,.btn-primary{background:var(--accent)!important;border-color:var(--accent)!important;color:var(--on-accent)!important}.btn-primary:hover{background:var(--accent-hover)!important;border-color:var(--accent-hover)!important;color:var(--on-accent)!important}.btn[disabled],button[disabled]{opacity:.65;cursor:not-allowed}.btn-sm,.input-sm{height:31px;padding:5px 8px}.btn-xs{height:25px;padding:3px 7px}.btn-group-justified{display:flex;width:100%;gap:4px}.btn-group-justified>.btn,.btn-group-justified>a{display:flex!important;align-items:center;justify-content:center;flex:1 1 0;min-width:0;padding:6px 7px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.notActive{background:var(--panel-raised)!important;color:var(--muted)!important}.save,.active.save{background:var(--accent)!important;color:var(--on-accent)!important;border-color:var(--accent)!important}.error{background:var(--danger-soft)!important;border-color:color-mix(in srgb,var(--danger) 32%,var(--line))!important;color:var(--danger)!important}.success{background:var(--success-soft)!important;border-color:color-mix(in srgb,var(--success) 32%,var(--line))!important;color:var(--success)!important}textarea.success,textarea.error{background:var(--panel-raised)!important}#limitgen.success{background:var(--panel-raised)!important;border-color:var(--line)!important;color:var(--text)!important}#limitgen.success:focus{border-color:var(--line)!important;box-shadow:none!important}.max{min-width:145px;text-align:left}.app-sign{text-align:right;padding:3px 0 2px}.app-sign footer{font-family:'Caveat','Dancing Script',cursive;font-size:17px;font-weight:700;color:#444746;text-decoration:none;letter-spacing:.02em;line-height:1;user-select:none;-webkit-user-select:none}.maxi{min-width:40px;text-align:center}.og-field{display:flex!important;align-items:stretch!important;width:100%!important;margin:2px 0!important;padding:0!important;border:0!important}.og-field .form-control{display:block!important;flex:1 1 auto!important;width:auto!important;min-width:0!important;height:31px!important;border-left:0!important;border-radius:0 var(--radius) var(--radius) 0!important}.og-icon{display:flex!important;align-items:center!important;justify-content:center!important;flex:0 0 38px!important;width:38px!important;min-width:38px!important;height:31px!important;padding:0!important;color:var(--muted)!important;background:transparent!important;border-radius:var(--radius) 0 0 var(--radius)!important}.og-icon svg{display:block;width:14px!important;height:14px!important;stroke:currentColor;fill:none;stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round}.btn-copy-pos{position:absolute;top:6px;right:6px}.btn-copy{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;padding:0;border:none!important;background:var(--panel-soft)!important;color:var(--muted)!important;border-radius:var(--radius)!important}.btn-copy:hover{background:var(--panel-soft)!important;color:var(--text-strong)!important}.table{width:100%;margin:4px 0 0;border-collapse:separate;border-spacing:0;border:1px solid var(--line);border-radius:var(--radius);overflow:hidden;background:var(--panel)!important;table-layout:auto}.table>thead>tr>th{position:sticky;top:0;z-index:20;padding:7px 8px!important;border-bottom:1px solid var(--line)!important;background:var(--panel-soft)!important;color:var(--muted)!important;font-size:11.5px!important;line-height:1.2;text-transform:uppercase;letter-spacing:.035em;text-align:left}.table>tbody>tr>td{padding:6px 8px!important;border-top:1px solid var(--line-soft)!important;color:var(--text);font-size:12.5px;vertical-align:middle;word-break:break-word;background:var(--panel)!important;text-align:left}.table-hover>tbody>tr:hover>td{background:var(--panel-soft)!important}.table-row-removing>td{background:var(--danger-soft)!important;color:var(--danger)!important;opacity:.75}.post-colon,#limitgen{font-size:12px!important}#limitgen{height:100px!important;white-space:nowrap!important;overflow:hidden!important;text-overflow:ellipsis!important;resize:none!important;font-family:Consolas,Monaco,"Courier New",monospace!important;font-size:12px!important}.table-fixed thead,.table-fixed thead th{position:sticky;top:0;z-index:20;background:var(--panel-soft)!important;color:var(--muted)!important}.btn-sm-danger,.btn-danger{border-color:color-mix(in srgb,var(--danger) 32%,var(--line))!important;background:var(--danger-soft)!important;color:var(--danger)!important}.btn-sm-danger:hover,.btn-danger:hover{border-color:color-mix(in srgb,var(--danger) 45%,var(--line))!important;background:color-mix(in srgb,var(--danger-soft) 80%,var(--panel))!important;color:var(--danger)!important}.app-modal{position:fixed;inset:0;z-index:1050;display:none;align-items:center;justify-content:center;padding:14px;background:color-mix(in srgb,#292b2c 44%,transparent)}.app-modal.is-open{display:flex}.app-modal__dialog{width:100%;max-width:340px}.app-modal__content{border:1px solid var(--line);border-radius:var(--radius);background:var(--panel2);color:var(--text-strong);box-shadow:var(--shadow-modal);overflow:hidden}.app-modal__header,.app-modal__footer{display:flex;align-items:center;gap:8px;padding:9px 10px;background:var(--panel-soft);border-color:var(--line-soft)}.app-modal__header{justify-content:space-between;border-bottom:1px solid var(--line)}.app-modal__footer{justify-content:flex-end;border-top:1px solid var(--line)}.app-modal__title{margin:0;font-size:14px;font-weight:700;color:var(--text-strong)}.app-modal__body{padding:10px;background:var(--panel2)}.app-modal__note{margin:0;color:var(--text-strong);font-size:12.5px}.close{height:24px;width:24px;border:0!important;background:transparent!important;color:var(--muted)!important;font-size:20px;line-height:20px}.close:hover{color:var(--text-strong)!important;background:transparent!important}.toastbox{position:fixed;right:14px;bottom:14px;z-index:999999;display:flex;flex-direction:column;gap:6px;align-items:flex-end;pointer-events:none}.toastitem{display:inline-flex;align-items:center;gap:9px;max-width:300px;min-width:180px;padding:9px 12px 9px 10px;border:1px solid var(--line);border-left:3px solid var(--line);border-radius:var(--radius)!important;background:var(--panel2)!important;color:var(--text);box-shadow:0 2px 10px rgba(0,0,0,.09);font-size:12.5px;font-weight:600;line-height:1.35;pointer-events:none;opacity:0;transform:translateX(10px);transition:opacity .2s ease,transform .2s ease}.toastitem.is-show{opacity:1;transform:translateX(0)}.toastitem--hide{opacity:0!important;transform:translateX(10px)!important}.toastitem--success{border-left-color:#6a8f6a}.toastitem--error{border-left-color:#8f6a6a;background:var(--danger-soft)!important;border-color:color-mix(in srgb,var(--danger) 32%,var(--line))!important}.toastitem svg{width:14px;height:14px;flex:0 0 14px;color:var(--muted)}.toastitem--success svg{color:#6a8f6a}.toastitem--error svg{color:#8f6a6a}.toastitem span{display:block;min-width:0}#sm{padding:6px;background:var(--panel-soft)!important;border:1px solid var(--line-soft);border-radius:var(--radius)}#shortgen{max-width:none}.generate-controls{display:flex;gap:4px;align-items:stretch}.control-limit{flex:1 1 0;min-width:0}.control-landing{flex:0 0 120px;width:120px}.control-debug{flex:0 0 100px;width:100px}.control-shortener{flex:0 0 120px;width:120px}#genurl{height:31px;padding:5px 10px;background:var(--accent)!important;color:var(--on-accent)!important;border-color:var(--accent)!important;white-space:nowrap;flex:0 0 auto}#genurl:hover{background:var(--accent-hover)!important;border-color:var(--accent-hover)!important}.textarea-wrap{position:relative}.btn-copy-float{position:absolute;top:6px;right:6px;display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;padding:0;border:none!important;background:var(--panel-soft)!important;color:var(--muted)!important;border-radius:var(--radius)!important;box-shadow:none!important;cursor:pointer;line-height:1}.btn-copy-float:hover{background:var(--panel-soft)!important;color:var(--text-strong)!important}.btn-copy-float.copied,.btn-copy-domain.copied{background:var(--success-soft)!important;color:var(--success)!important;border-color:color-mix(in srgb,var(--success) 32%,var(--line))!important;transition:background .1s ease,color .1s ease}.ns-info{margin:8px 0 6px;padding:7px 10px;background:var(--panel-soft)!important;border:1px solid var(--line-soft)!important;border-radius:var(--radius)!important;display:flex;align-items:flex-start;gap:8px;color:var(--text)}.ns-info svg{flex:0 0 15px;margin-top:2px;color:var(--muted)}.ns-info__row{display:grid;grid-template-columns:1fr 1fr;gap:3px 16px;font-size:13px;line-height:1.35;color:var(--text);width:100%}.ns-info__label{font-weight:700;color:var(--text-strong);grid-column:1/-1}.ns-info__item{display:flex;align-items:center;gap:6px}.ns-info__idx{font-size:10.5px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.04em;white-space:nowrap;min-width:28px}.ns-info code{font-family:var(--mono);font-size:12px;letter-spacing:.02em;background:transparent!important;border:0!important;padding:0;color:var(--text-strong);display:inline;user-select:all}#domain{border-left:0!important}#addondom{height:31px;padding:5px 10px;background:var(--accent)!important;color:var(--on-accent)!important;border-color:var(--accent)!important;white-space:nowrap}#addondom:hover{background:var(--accent-hover)!important;border-color:var(--accent-hover)!important}#btn-logout{background:#444746!important;border-color:#444746!important;color:#fff!important}#btn-logout:hover{background:#1f2221!important;border-color:#1f2221!important;color:#fff!important}@media screen and (max-width:768px){body{padding-top:6px;font-size:12.5px}.container.app-shell,.container{width:100%!important;max-width:100%!important;padding-left:6px!important;padding-right:6px!important}.panel-heading,.panel-body,.panel-footer{padding:7px}.panel-footer--banner{padding:0!important}.panel-heading .pull-right{float:none!important;justify-content:flex-end;margin-top:0}.nav-tabs{gap:3px;padding:4px 4px 0!important}.nav-tabs>li{flex:1 1 0}.nav-tabs>li>a{text-align:center;padding:6px 4px;font-size:11px}.tab-content{padding:5px}.input-group:not(.og-field){display:block}.input-group:not(.og-field)>.form-control,.input-group:not(.og-field)>.input-group-addon,.input-group:not(.og-field)>.input-group-btn,.input-group:not(.og-field)>.input-group-btn>.btn{display:block;width:100%!important}.input-group:not(.og-field)>.input-group-addon{border-bottom:0!important;border-radius:var(--radius) var(--radius) 0 0!important}.input-group:not(.og-field)>.form-control{border-radius:0 0 var(--radius) var(--radius)!important}.input-group-btn>.btn,#addondom{margin-top:5px;width:100%!important}.panel-footer .pull-left,.panel-footer .text-right{float:none!important;text-align:left!important}.panel-footer select{width:100%!important;margin-bottom:5px}.btn-group-justified{display:grid;grid-template-columns:repeat(2,minmax(0,1fr))}.generate-controls{display:grid!important;grid-template-columns:1fr 1fr;gap:5px!important}.generate-controls>*{width:100%!important;min-width:0!important;flex:auto!important}#genurl{grid-column:1/-1}.table{display:block;overflow-x:auto;white-space:nowrap}.table>thead,.table>tbody,.table>thead>tr,.table>tbody>tr{width:100%}.toastbox{right:8px;left:8px;bottom:8px;align-items:stretch}.toastitem{max-width:none;width:100%}}@media screen and (max-width:500px){body{font-size:12px}.btn-group-justified{grid-template-columns:1fr}.app-banner{max-height:72px}.generate-controls{grid-template-columns:1fr}textarea.form-control{min-height:104px}.table>thead>tr>th,.table>tbody>tr>td{padding:6px!important;font-size:10.5px}.ns-info__row{font-size:11.5px;grid-template-columns:1fr}}.cf-badge{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;padding:0;border-radius:var(--radius);border:1px solid var(--line-soft);background:var(--panel-soft);color:var(--muted);flex-shrink:0}.cf-badge--active{border-color:color-mix(in srgb,var(--success) 32%,var(--line));background:var(--success-soft);color:var(--success)}.cf-badge--pending{border-color:color-mix(in srgb,var(--accent) 22%,var(--line));background:var(--accent-soft);color:var(--accent)}.cf-badge--error{border-color:color-mix(in srgb,var(--danger) 32%,var(--line));background:var(--danger-soft);color:var(--danger)}.cf-badge--loading{border-color:color-mix(in srgb,var(--accent) 28%,var(--line));background:var(--accent-soft);color:var(--accent);width:24px;height:24px}.addon-toolbar{display:flex;align-items:center;justify-content:space-between;padding:3px 1px 2px;margin-bottom:1px}.domain-count-badge{font-size:12px;color:var(--muted);font-weight:600}.btn-copy-domain{display:inline-flex;align-items:center;padding:1px 4px;height:20px;vertical-align:middle}.cf-ns-list{margin:4px 0 0;padding:5px 8px;list-style:none;background:var(--panel-soft);border:1px solid var(--line-soft);border-radius:var(--radius)}.cf-ns-list li{font-size:12px;padding:3px 0;color:var(--text);font-family:var(--mono);letter-spacing:.02em;user-select:all;cursor:pointer}.cf-ns-list li:hover{color:var(--text-strong)}#table_domain{table-layout:fixed;width:100%;min-width:0}#table_domain th:nth-child(1),#table_domain td:nth-child(1){width:auto}#table_domain th:nth-child(2),#table_domain td:nth-child(2){width:90px}#table_domain th:nth-child(3),#table_domain td:nth-child(3){width:72px;white-space:nowrap}.td-cf-cell{display:flex;align-items:center;gap:4px}#table_domain th:nth-child(4),#table_domain td:nth-child(4){width:72px;text-align:right;white-space:nowrap}.td-dom{display:inline;font-size:11.5px;font-weight:600;color:var(--text);font-family:monospace}.td-subid{display:block;font-size:11.5px;color:var(--muted);font-family:monospace}.cf-modal-status{display:flex;align-items:center;gap:8px;margin-bottom:6px;font-size:12.5px;font-weight:600;flex-wrap:wrap}.cf-config-bar{display:flex;align-items:center;gap:6px;padding:3px 1px 2px;margin-bottom:1px}#cf-sync-all{margin-left:auto}.cf-config-label{font-size:11.5px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.04em}.cf-source-badge--admin{border-color:color-mix(in srgb,var(--accent) 22%,var(--line));background:var(--accent-soft);color:var(--accent)}.cf-config-panel{display:none;background:var(--panel-soft);border:1px solid var(--line-soft);border-radius:var(--radius);padding:7px 8px;margin-bottom:4px}.cf-config-panel.is-open{display:block}.cf-config-form .input-group{margin-bottom:4px}.cf-config-row{margin-bottom:4px}.cf-config-addon{width:90px!important;font-size:12px!important}.cf-config-actions{display:flex;align-items:center;gap:5px;margin-top:4px}.cf-config-status{font-size:12px;font-weight:600;color:var(--muted)}.cf-toggle-row{margin-top:6px}.cf-toggle-label{display:flex;align-items:center;gap:6px;cursor:pointer;font-size:12px;color:var(--muted)}.cf-toggle-checkbox{width:14px;height:14px;cursor:pointer}.is-hidden{display:none!important}#cf-modal-note{margin-bottom:0}#cf-modal-note:empty{display:none}#cf-modal-ns{display:none}#cf-modal-ns.ns-visible{display:block}*:focus,*:focus-visible{outline:none!important;box-shadow:none!important}.form-control{outline:none}.label{border:1px solid var(--line)!important}.cf-perm-head{display:flex;align-items:center;justify-content:space-between;gap:8px}.cf-perm-toggle{height:24px;padding:3px 8px!important;font-size:10.5px!important;white-space:nowrap}.cf-perm-body{display:none;margin-top:5px}.cf-perm-body.is-open{display:block}.cf-perm-info{margin-top:8px;padding-top:6px;border-top:1px solid var(--line-soft)}.cf-perm-label{display:block;font-size:12px;font-weight:700;color:var(--text-strong);text-transform:uppercase;letter-spacing:.04em;margin-bottom:6px}.cf-perm-table{display:grid;gap:0;border:1px solid var(--line);border-radius:var(--radius);overflow:hidden;margin-top:6px;font-family:var(--mono)}.cf-perm-row{display:grid;grid-template-columns:72px 1fr 52px;border-bottom:1px solid var(--line-soft)}.cf-perm-row:last-child{border-bottom:0}.cf-perm-row--head{background:var(--panel-soft)}.cf-perm-row span{padding:5px 9px;font-size:12.5px;display:flex;align-items:center;color:var(--text-strong)}.cf-perm-row--head span{font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:var(--muted);font-family:var(--font)}.cf-perm-row .cf-perm-access{justify-content:center;font-weight:700;color:var(--text-strong)}.cf-perm-row .cf-perm-scope{color:var(--text);font-size:12px}.cf-token-warn{padding:7px 9px;background:var(--danger-soft);border:1px solid color-mix(in srgb,var(--danger) 28%,var(--line));border-radius:var(--radius);color:var(--danger);font-size:12px;font-weight:700;margin-bottom:8px;line-height:1.4}.cf-token-row{display:flex;align-items:stretch;gap:5px;margin-bottom:6px}.cf-token-val{flex:1;min-width:0;font-size:10.5px;word-break:break-all;padding:5px 7px;background:var(--panel-soft);border:1px solid var(--line);border-radius:var(--radius);color:var(--text);overflow-x:auto;white-space:pre}.cf-token-note{font-size:11.5px;color:var(--muted);margin:0;line-height:1.4}.cf-perm-hint{margin-top:6px;font-size:12px;color:var(--text);line-height:1.5}.dd-wrap{position:relative;display:inline-block;width:100%}.dd-trigger{width:100%!important;display:flex!important;align-items:center!important;gap:.5rem!important;background:var(--panel-raised)!important;border:1px solid var(--line)!important;color:var(--text)!important;border-radius:var(--radius)!important;padding:0 .5rem!important;font-size:12px!important;cursor:pointer!important;text-align:left!important;transition:border-color .15s!important;height:31px!important;box-shadow:none!important;}.dd-trigger:focus{outline:none!important;border-color:var(--line)!important}.dd-trigger-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dd-chevron{flex-shrink:0;transition:transform .15s}.dd-wrap.is-open .dd-chevron{transform:rotate(180deg)}.dd-menu{position:absolute;top:calc(100% + 2px);left:0;right:0;background:#fff;border:1px solid var(--line);border-radius:var(--radius);z-index:9999;overflow:hidden;display:none;padding:2px;list-style:none;margin:0}.dd-wrap.is-open .dd-menu{display:block}.dd-trigger:hover{background:var(--panel-soft)!important;border-color:var(--line)!important}.dd-option{display:flex;align-items:center;gap:.5rem;padding:5px 9px;font-size:12px;cursor:pointer;color:var(--text);background:#fff;border-radius:var(--radius);transition:background .1s}.dd-option:hover{background:#f5f5f5;color:var(--text-strong)}.dd-option[aria-selected]{background:#eee;color:var(--text-strong);font-weight:600}.dd-option[aria-selected]::after{content:"";display:block;flex-shrink:0;width:11px;height:8px;background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='8' viewBox='0 0 11 8' fill='none'%3E%3Cpath d='M1 4l3 3 6-6' stroke='%23444746' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") no-repeat center/contain;margin-left:4px}@media(prefers-color-scheme:dark){.dd-option[aria-selected]::after{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='8' viewBox='0 0 11 8' fill='none'%3E%3Cpath d='M1 4l3 3 6-6' stroke='%23adb5bd' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E")}}.dd-option-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dd-trigger *,.dd-option *{pointer-events:none}.dd-sep{border-top:1px solid rgba(68,71,70,.06);margin:2px 4px}.domain-selector-row{display:flex;align-items:center;justify-content:space-between;gap:6px;width:100%}.domain-selector-col{flex:1 1 0;min-width:0;max-width:calc(50% - 3px)}.domain-selector-col--right{margin-left:auto}.domain-selector-col .dd-wrap{width:100%}.domain-selector-col .dd-menu{max-height:184px;overflow:auto}.domain-selector-col .dd-trigger{justify-content:space-between}.domain-selector-col .dd-trigger-label{text-align:left}.domain-picker.is-passive .dd-trigger{background:var(--panel-soft)!important;color:var(--muted)!important}.domain-picker.is-passive .dd-trigger-label{color:var(--muted)!important}#locrandom-wrap .dd-option[data-val="global_off"],#locdom-wrap .dd-option[data-val="user_off"]{display:none!important}@media screen and (max-width:560px){.domain-selector-row{display:grid;grid-template-columns:1fr;gap:5px}.domain-selector-col{max-width:none;width:100%}.domain-selector-col--right{margin-left:0}}#deleteConfirmToast{position:fixed;bottom:20px;left:50%;transform:translateX(-50%);z-index:1060;display:none;background:var(--panel2);border:1px solid var(--line);border-radius:10px;box-shadow:0 6px 28px rgba(0,0,0,.13);padding:13px 16px;max-width:580px;width:calc(100% - 28px)}#deleteConfirmToast.is-open{display:block}.dct-inner{display:flex;align-items:center;gap:12px}.dct-icon{flex:0 0 26px;width:26px;height:26px;border-radius:50%;background:var(--strong);display:inline-flex;align-items:center;justify-content:center;color:#fff;flex-shrink:0}.dct-icon svg{width:13px;height:13px;fill:currentColor;display:block}.dct-msg{flex:1;margin:0;font-size:13px;font-weight:500;color:var(--text-strong);line-height:1.4;min-width:0}.dct-actions{display:flex;align-items:center;gap:6px;flex-shrink:0}.dct-btn-delete{background:var(--strong)!important;border-color:var(--strong)!important;color:#fff!important;font-weight:700!important}.dct-btn-delete:hover{background:var(--accent)!important;border-color:var(--accent)!important;color:#fff!important}@media screen and (max-width:560px){#deleteConfirmToast{bottom:8px;width:calc(100% - 16px)}.dct-inner{flex-wrap:wrap;gap:10px}.dct-msg{order:1;flex-basis:100%}.dct-actions{order:2;width:100%;justify-content:flex-end}}.td-cf-cell .cf-badge,.td-cf-cell .cf-sync-btn,#table_domain td .delete.btn{border:0!important;background:transparent!important;box-shadow:none!important}.td-cf-cell .cf-sync-btn:hover,#table_domain td .delete.btn:hover{background:transparent!important;border:0!important}.td-cf-cell .cf-badge,.td-cf-cell .cf-sync-btn,#table_domain td .delete.btn{width:30px!important;height:30px!important;padding:0!important;display:inline-flex!important;align-items:center!important;justify-content:center!important;vertical-align:middle!important}.td-cf-cell .cf-badge svg,.td-cf-cell .cf-sync-btn svg,#table_domain td .delete.btn svg{width:14px!important;height:14px!important}.td-cf-cell{justify-content:center!important}#table_domain td:nth-child(4){text-align:center!important}.--font-cursive:cursive;--font-mono:monospace}@media (prefers-color-scheme:dark){:root{color-scheme:dark;--bg:#212529;--surface:#343a40;--panel:rgba(33,37,41,.95);--panel2:#343a40;--line:#495057;--line2:rgba(248,249,250,.06);--text:#e9ecef;--muted:#6e7370;--strong:#f8f9fa;--primary:#e9ecef;--primary2:#f8f9fa;--accent:#495057;--accent-h:#adb5bd;--accent-s:rgba(173,181,189,.12);--on:#212529;--bad:#6e7370;--good:#adb5bd;--warn:#6e7370;--shadow:none;--shadow-modal:none;--panel-soft:var(--panel2);--panel-raised:var(--panel);--panel-fade:rgba(33,37,41,.8);--line-soft:var(--line2);--text-strong:var(--strong);--accent-hover:var(--accent-h);--accent-soft:var(--accent-s);--on-accent:var(--on);--danger:var(--bad);--danger-soft:rgba(108,117,125,.12);--success:var(--good);--success-soft:rgba(173,181,189,.12);--ok:var(--good);--ok-soft:rgba(173,181,189,.12);--blue-soft:var(--accent-s);--blue-line:rgba(173,181,189,.30);--nav:#212529;--btn:var(--text);--stroke:var(--line)}}*{box-sizing:border-box}html,body{width:100%;height:100dvh;margin:0;}html{scroll-behavior:smooth;background:var(--bg);zoom:.95;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-rendering:optimizeLegibility}body{padding:42px 8px 18px;font-family:var(--font);font-size:13px;line-height:1.45;color:var(--text);background:radial-gradient(circle at 50% 8%,rgba(255,90,10,.12),transparent 28rem),radial-gradient(circle at 10% 80%,rgba(15,23,42,.06),transparent 22rem),#f8fafc;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-rendering:optimizeLegibility}a{color:inherit;text-decoration:none}code{font-size:12.5px;color:var(--text);background:var(--accent-soft);border:1px solid var(--line-soft);border-radius:var(--radius);padding:1px 4px}img{max-width:100%;object-fit:cover;border:1px solid var(--line-soft);border-radius:var(--radius);user-select:none}.container.app-shell{width:100%;max-width:670px;padding-left:8px;padding-right:8px}.panel,.panel-default{margin:0 auto 0;max-width:670px;border:1px solid var(--line)!important;border-radius:var(--radius)!important;overflow:hidden}.panel-heading{padding:10px 12px;font-size:13px;background:#f5f5f5!important;border-bottom:1px solid var(--line-soft)!important;color:var(--text-strong)}.panel-body{padding:10px}.table td a,.table td span{word-break:break-word}input:focus,textarea:focus,select:focus,button:focus,.form-control:focus,.btn:focus,a:focus{outline:none!important}.form-control:focus,input:focus,select:focus,textarea:focus{box-shadow:none!important}.bootgrid-header .search .search-field:focus,.bootgrid-header .search .form-control:focus{box-shadow:none!important}*:focus,*:focus-visible{outline:none!important;box-shadow:none!important}.navbar-nav>li>a{line-height:20px!important}.navbar-btn.btn-link{height:44px!important;margin:0!important;padding:12px 10px!important;border:0!important;background:transparent!important;color:var(--text)!important;text-decoration:none!important;box-shadow:none!important}.navbar-btn.btn-link:hover{background:transparent!important;color:var(--text-strong)!important}.table>tfoot>tr>th{padding:7px 12px!important;border-top:1px solid var(--line)!important;border-bottom:0!important;background:#f5f5f5!important;color:var(--text-strong)!important;font-size:11px!important;font-weight:600;text-transform:uppercase;letter-spacing:.06em}.table-responsive{border:0!important;overflow-x:auto!important;overflow-y:visible!important}.bootgrid-header.search,.bootgrid-header .search{width:100%!important;display:flex!important}.search>.form-control{display:block!important;width:auto!important}.search>.btn{display:block!important}.pagination-compact{margin:0!important}.pagination>li>a,.pagination>li>span{border:1px solid var(--line)!important;background:var(--panel)!important;color:var(--text)!important}.pagination>.active>a{background:var(--accent)!important;color:var(--on-accent)!important;border-color:var(--accent)!important}.td-action{text-align:right!important;white-space:nowrap!important}.td-empty{text-align:center!important;padding:16px!important;color:var(--text)!important}.th-action{text-align:right!important}.app-sign{text-align:right;padding:3px 0 2px}.app-sign footer{font-family:'Caveat','Dancing Script',cursive;font-size:17px;font-weight:600;color:#444746;text-decoration:none;letter-spacing:.02em;line-height:1}.ddt{position:relative;display:inline-block}.ddt-btn{min-width:52px!important;height:31px!important;padding:4px 8px!important;gap:4px!important;justify-content:space-between!important}.ddt-menu{display:none;position:absolute;top:calc(100% + 3px);left:0;z-index:3000;min-width:62px;padding:3px;list-style:none;margin:0;border:1px solid var(--line);border-radius:var(--radius);background:linear-gradient(180deg,var(--panel-soft) 0%,var(--bg) 100%)!important;box-shadow:var(--shadow-modal)}.ddt-menu.open{display:block}.ddt-menu li a{display:block;padding:5px 10px;border-radius:var(--radius);color:var(--text);font-size:12px;font-weight:600;line-height:1.3;text-decoration:none;white-space:nowrap;transition:background .1s;background:#fff!important}.ddt-menu li+li{border-top:1px solid rgba(68,71,70,.06);margin:2px 4px 0}.ddt-menu li a:hover{background:#444746!important;color:#fff!important}.ddt-menu li a.ddt-active{background:var(--accent)!important;color:var(--on-accent)!important}.ddt-caret{margin-left:3px;opacity:.5;vertical-align:middle}.table-scroll{overflow-x:auto}.td-cf-cell{display:inline-flex!important;align-items:center!important;justify-content:center!important;gap:5px!important;vertical-align:middle!important}.cf-badge,.td-cf-cell .cf-badge,.td-cf-cell .cf-sync-btn,#table_domain td .delete.btn{width:30px!important;height:30px!important;padding:0!important;display:inline-flex!important;align-items:center!important;justify-content:center!important;vertical-align:middle!important}.td-cf-cell .cf-badge svg,.td-cf-cell .cf-sync-btn svg,#table_domain td .delete.btn svg{width:14px!important;height:14px!important}.td-cf-cell{justify-content:center!important}#table_domain td:nth-child(4){text-align:center!important}.tab-content{padding:0!important}@keyframes btn-spin{to{transform:rotate(360deg)}}.btn-spin{display:inline-block;vertical-align:-2px;animation:btn-spin .7s linear infinite;opacity:.8}body::before{content:'NGIX\2022 XCTD';position:fixed;top:12px;left:14px;z-index:0;font:600 11px/1 Inter,ui-sans-serif,system-ui,-apple-system,sans-serif;letter-spacing:.24em;text-transform:uppercase;color:rgba(68,71,70,.06);pointer-events:none}body::after{content:'';position:fixed;inset:0;z-index:0;pointer-events:none;background-image:url('/assets/favicon.svg'),url('/assets/bg.png');background-repeat:no-repeat,no-repeat;background-position:right 14px bottom 12px,center center;background-size:28px 28px,cover;opacity:.28;filter:saturate(.68) contrast(.84) brightness(1.03) blur(.32px);transform:translateZ(0)}</style>
</head>
<body>
<div id="toastbox" class="toastbox" aria-live="polite" aria-atomic="true"></div>
<div class="container app-shell">
    <div class="panel panel-default">
        <div class="panel-heading">
            <div class="pull-right">
<a href="?logout&sub_id=<?= rawurlencode($subIdParam); ?>" class="btn btn-xs logout-btn" id="btn-logout">Logout</a>
            </div>
        </div>
        <div class="panel-body">
            <div class="panel-footer panel-footer--banner">
                <img src="banner.png" alt="Banner" class="app-banner">
            </div>
            <ul class="nav nav-tabs">
                <li id="tabgen" class="active"><a href="#gen" data-toggle="tab"><strong>GENERATE</strong></a></li>
                <li id="tabaddon"><a href="#addon" data-toggle="tab" aria-controls="addon"><strong>ADDDOMAIN</strong></a></li>
            </ul>
            <div id="myTabContent" class="tab-content">
                <div class="panel-footer" id="sm">
                    <div class="input-group">
                        <div id="radioBtn" class="btn-group btn-group-justified btn-block">
                            <?php
                            $networkStmt = $link->prepare("SELECT DISTINCT network FROM offering WHERE network IS NOT NULL AND network <> '' ORDER BY network ASC");
$networkStmt->execute();
$networkFound = false;
while ($networkRow = $networkStmt->fetch(PDO::FETCH_ASSOC)) {
    $network = (string) ($networkRow['network'] ?? '');
    if ($network === '') {
        continue;
    }
    $networkFound = true;
    echo '<a type="button" class="btn btn-default btn-sm notActive" data-toggle="user_lp" data-title="' . e($network) . '"><strong>' . e($network) . '</strong></a>';
}
if (!$networkFound) {
    echo '<span class="btn btn-default btn-sm disabled">No Network</span>';
}
?>
                        </div>
                    </div>
                </div>
                <hr class="compact-hr">
                <div class="tab-pane fade active in" id="gen">
                    <div class="panel-footer">
                        <div class="domain-selector-row">
                            <div class="domain-selector-col domain-selector-col--left">
                                <div class="dd-wrap domain-picker" id="locrandom-wrap">
                                    <button type="button" class="dd-trigger form-control input-sm" id="locrandom-trigger" aria-expanded="false" aria-haspopup="listbox" aria-controls="locrandom-menu">
                                        <span class="dd-trigger-label" id="locrandom-label">[RANDOM GLOBAL DOMAIN]</span>
                                        <svg class="dd-chevron" width="10" height="6" viewBox="0 0 10 6" fill="none" aria-hidden="true"><path d="M1 1l4 4 4-4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
                                    </button>
                                    <ul class="dd-menu" id="locrandom-menu" role="listbox" aria-labelledby="locrandom-trigger">
                                        <li class="dd-option" data-val="global" role="option" aria-selected="true"><span class="dd-option-label">[RANDOM GLOBAL DOMAIN]</span></li>
                                        <li class="dd-option" data-val="global_off" role="option"><span class="dd-option-label">[GLOBAL DOMAIN OFF]</span></li>
                                        <?php
            $globalDomainOptions = [];
$globalDomainResult = $link->query("SELECT domain FROM addondomain WHERE sub_domain='GLOBAL'");
if ($globalDomainResult) {
    while ($domainRow = $globalDomainResult->fetch(PDO::FETCH_ASSOC)) {
        $domainValue = trim((string) ($domainRow['domain'] ?? ''));
        if ($domainValue === '') {
            continue;
        }
        $globalDomainOptions[] = $domainValue;
        echo '<li class="dd-option" data-val="' . e($domainValue) . '" role="option"><span class="dd-option-label">' . e($domainValue) . '</span></li>';
    }
}
?>
                                    </ul>
                                    <select id="locrandom" name="locrandom" hidden>
                                        <option value="global" selected>[RANDOM GLOBAL DOMAIN]</option>
                                        <option value="global_off">[GLOBAL DOMAIN OFF]</option>
                                        <?php foreach ($globalDomainOptions as $domainValue) : ?>
                                            <option value="<?= e($domainValue); ?>"><?= e($domainValue); ?></option>
                                        <?php endforeach; ?>
                                    </select>
                                </div>
                            </div>
                            <div class="domain-selector-col domain-selector-col--right">
                                <div class="dd-wrap domain-picker" id="locdom-wrap">
                                    <button type="button" class="dd-trigger form-control input-sm" id="locdom-trigger" aria-expanded="false" aria-haspopup="listbox" aria-controls="locdom-menu">
                                        <span class="dd-trigger-label" id="locdom-label">[USER DOMAIN OFF]</span>
                                        <svg class="dd-chevron" width="10" height="6" viewBox="0 0 10 6" fill="none" aria-hidden="true"><path d="M1 1l4 4 4-4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
                                    </button>
                                    <ul class="dd-menu" id="locdom-menu" role="listbox" aria-labelledby="locdom-trigger">
                                        <li class="dd-option" data-val="user_off" role="option" aria-selected="true"><span class="dd-option-label">[USER DOMAIN OFF]</span></li>
                                        <li class="dd-option" data-val="u_rand" role="option"><span class="dd-option-label">[RANDOM USER DOMAIN]</span></li>
                                    </ul>
                                    <select id="locdom" name="locdom" hidden>
                                        <option value="user_off" selected>[USER DOMAIN OFF]</option>
                                        <option value="u_rand">[RANDOM USER DOMAIN]</option>
                                    </select>
                                </div>
                            </div>
                        </div>
                    </div>
                    <hr class="compact-hr">
                    <div class="panel-footer">
                        <div class="input-group og-field">
                            <span class="input-group-addon maxi og-icon" title="Title" aria-label="Title"><svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><rect x="5" y="4" width="14" height="16" rx="2"></rect><path d="M8 9h8"></path><path d="M8 13h8"></path><path d="M8 17h5"></path></svg></span>
                            <input class="form-control input-sm js-autoselect" id="fbtext" name="fbtext" autocomplete="off" placeholder="{og:title}" type="text">
                        </div>
                        <div class="input-group og-field">
                            <span class="input-group-addon maxi og-icon" title="Image URL" aria-label="Image URL"><svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><rect x="4" y="5" width="16" height="14" rx="2"></rect><circle cx="9" cy="10" r="1.5"></circle><path d="M7 17l4.2-4.2a1.3 1.3 0 0 1 1.8 0L17 17"></path></svg></span>
                            <input class="form-control input-sm js-autoselect" id="fbimg" name="fbimg" autocomplete="off" placeholder="{og:image}" type="text">
                        </div>
                    </div>
                    <hr class="compact-hr">
                    <div class="panel-footer">
                        <div class="generate-controls">
                            <input class="form-control input-sm js-autoselect js-readlock control-limit" id="shortgen" name="shortgen" placeholder="{limitgen}" type="number" min="1" max="50" step="1" value="1">
                            <div class="dd-wrap control-landing">
                                <button type="button" class="dd-trigger form-control input-sm"><span class="dd-trigger-label">Direct</span><svg class="dd-chevron" width="10" height="6" viewBox="0 0 10 6" fill="none" aria-hidden="true"><path d="M1 1l4 4 4-4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg></button>
                                <ul class="dd-menu">
                                    <li class="dd-option" data-val="direct" aria-selected="true"><span class="dd-option-label">Direct</span></li>
                                    <li class="dd-sep"></li>
                                    <li class="dd-option" data-val="landing"><span class="dd-option-label">Landing page</span></li>
                                </ul>
                                <select id="lg" name="lg" hidden><option value="direct" selected>Direct</option><option value="landing">Landing page</option></select>
                            </div>
                            <div class="dd-wrap control-debug">
                                <button type="button" class="dd-trigger form-control input-sm"><span class="dd-trigger-label">Default</span><svg class="dd-chevron" width="10" height="6" viewBox="0 0 10 6" fill="none" aria-hidden="true"><path d="M1 1l4 4 4-4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg></button>
                                <ul class="dd-menu">
                                    <li class="dd-option" data-val="a" aria-selected="true"><span class="dd-option-label">Default</span></li>
                                    <li class="dd-sep"></li>
                                    <li class="dd-option" data-val="b"><span class="dd-option-label">l.fb.com</span></li>
                                    <li class="dd-sep"></li>
                                    <li class="dd-option" data-val="f"><span class="dd-option-label">l.wl.co</span></li>
                                </ul>
                                <select id="net_pick" name="net_pick" hidden><option value="a" selected>Default</option><option value="b">l.fb.com</option><option value="f">l.wl.co</option></select>
                            </div>
                            <div class="dd-wrap control-shortener">
                                <button type="button" class="dd-trigger form-control input-sm"><span class="dd-trigger-label">Default URL</span><svg class="dd-chevron" width="10" height="6" viewBox="0 0 10 6" fill="none" aria-hidden="true"><path d="M1 1l4 4 4-4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg></button>
                                <ul class="dd-menu">
                                    <li class="dd-option" data-val="def" aria-selected="true"><span class="dd-option-label">Default URL</span></li>
                                    <li class="dd-sep"></li>
                                    <li class="dd-option" data-val="long"><span class="dd-option-label">Long URL</span></li>
                                    <li class="dd-sep"></li>
                                    <li class="dd-option" data-val="ixgeu"><span class="dd-option-label">shortlink</span></li>
                                    <li class="dd-sep"></li>
                                    <li class="dd-option" data-val="ixg"><span class="dd-option-label">ixg.llc</span></li>
                                    <li class="dd-sep"></li>
                                    <li class="dd-option" data-val="turl"><span class="dd-option-label">tinyurl.com</span></li>
                                </ul>
                                <select id="uri" name="uri" hidden><option value="def" selected>Default URL</option><option value="long">Long URL</option><option value="ixg">ixg.llc</option><option value="turl">tinyurl.com</option><option value="ixgeu">shortlink</option></select>
                            </div>
                            <button class="btn btn-default btn-sm" data-loading-text="Generating..." id="genurl" type="button">
                                GENERATE URL
                            </button>
                        </div>
                        <hr class="compact-hr">
                        <div class="input-group">
                            <span class="input-group-addon max"><strong class="rg-source-0">Result URL</strong></span>
                        </div>
                        <div class="textarea-wrap">
                            <textarea class="form-control input-sm js-autoselect" id="limitgen" name="limitgen" placeholder="https://..." rows="1"></textarea>
                            <button id="copy-limitgen" type="button" title="Copy" class="btn-copy-float" tabindex="-1">
                                <svg viewBox="0 0 24 24" width="15" height="15" aria-hidden="true"><path fill="currentColor" d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg>
                            </button>
                        </div>
                    </div>
                </div>
                <div class="tab-pane fade" id="addon">
                    <?php
                    $nsList = [];
foreach (['NS1', 'NS2', 'NS3', 'NS4', 'NS5', 'NS6'] as $k) {
    $v = trim(app_env($k, ''));
    if ($v !== '') {
        $nsList[] = $v;
    }
}
if (empty($nsList)) {
    foreach (['CF_NS1', 'CF_NS2', 'CF_NS3', 'CF_NS4'] as $k) {
        $v = trim(app_env($k, ''));
        if ($v !== '') {
            $nsList[] = $v;
        }
    }
}
$nsList = array_values(array_unique($nsList));
if (!empty($nsList)) : ?>
                    <div class="ns-info">
                        <svg viewBox="0 0 24 24" width="15" height="15" aria-hidden="true"><path fill="currentColor" d="M12 2a10 10 0 1 0 0 20A10 10 0 0 0 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/></svg>
                        <div class="ns-info__row">
                            <span class="ns-info__label">Nameserver</span>
                            <?php foreach ($nsList as $i => $ns) : ?>
                            <span class="ns-info__item"><span class="ns-info__idx">NS <?= $i + 1; ?></span><code><?= e($ns); ?></code></span>
                            <?php endforeach; ?>
                        </div>
                    </div>
                    <?php endif; ?>
                    <div class="panel-footer">
                        <div class="input-group">
                            <span class="input-group-addon maxi">+</span>
                            <input type="hidden" class="form-control input-sm" id="userid" name="userid" value="<?= $escapedTitle; ?>">
                            <input autocomplete="off" type="text" class="form-control input-sm js-autoselect" id="domain" name="domain" placeholder="{addon_domain} domain.tld">
                            <span class="input-group-btn btn-block">
                                <button class="btn btn-default btn-sm" id="addondom" type="button" data-loading-text="Adding...">Addon Domain</button>
                            </span>
                        </div>
                        <div class="input-group cf-toggle-row">
                            <label class="cf-toggle-label">
                                <input type="checkbox" id="cf-enabled" class="cf-toggle-checkbox" checked>
                                <span>With Cloudflare — zone + DNS auto-provision</span>
                            </label>
                        </div>
                    </div>
                    <hr class="compact-hr">
                    <div class="cf-config-bar">
                        <button type="button" class="btn btn-xs" id="cf-config-toggle" aria-expanded="false" aria-controls="cf-config-panel">Show CF Settings</button>
                    </div>
                    <div class="cf-config-panel" id="cf-config-panel">
                        <div class="cf-config-form">
                            <div class="input-group cf-config-row">
                                <span class="input-group-addon cf-config-addon">API Token</span>
                                <input type="text" class="form-control input-sm" id="cf-input-token" autocomplete="off" placeholder="Bearer token dari CF dashboard">
                            </div>
                            <div class="input-group cf-config-row">
                                <span class="input-group-addon cf-config-addon">Account ID</span>
                                <input type="text" class="form-control input-sm" id="cf-input-account" autocomplete="off" placeholder="Auto-detect atau isi manual">
                                <div class="input-group-btn">
                                    <button type="button" class="btn btn-xs" id="cf-detect-account" title="Auto-detect Account ID dari token">⟳</button>
                                </div>
                            </div>
                            <div class="cf-config-actions">
                                <button type="button" class="btn btn-xs btn-primary" id="cf-config-save">Simpan</button>
                                <button type="button" class="btn btn-xs btn-danger" id="cf-config-clear">Hapus</button>
                                <button type="button" class="btn btn-xs" id="cf-create-token" title="Use a Create Additional Tokens creator token to generate the operational CF token">Create Additional Tokens</button>
                                <span class="cf-config-status" id="cf-config-status"></span>
                                <button type="button" class="btn btn-xs" id="cf-sync-all">Sync All CF</button>
                            </div>
                        </div>
                        <div class="cf-perm-info">
                            <div class="cf-perm-head">
                                <span class="cf-perm-label">Required token permissions:</span>
                                <button type="button" class="btn btn-xs cf-perm-toggle" data-target="cf-perm-body-user" aria-expanded="false">Show</button>
                            </div>
                            <div class="cf-perm-body" id="cf-perm-body-user">
                            <div class="cf-perm-table">
                                <div class="cf-perm-row cf-perm-row--head"><span>Scope</span><span>Permission</span><span>Access</span></div>
                                <div class="cf-perm-row"><span class="cf-perm-scope">Account</span><span>Account Settings</span><span class="cf-perm-access">Read</span></div>
                                <div class="cf-perm-row"><span class="cf-perm-scope">Account</span><span>Zone</span><span class="cf-perm-access">Edit</span></div>
                                <div class="cf-perm-row"><span class="cf-perm-scope">Zone</span><span>Zone</span><span class="cf-perm-access">Read</span></div>
                                <div class="cf-perm-row"><span class="cf-perm-scope">Zone</span><span>Zone</span><span class="cf-perm-access">Edit</span></div>
                                <div class="cf-perm-row"><span class="cf-perm-scope">Zone</span><span>Zone Settings</span><span class="cf-perm-access">Edit</span></div>
                                <div class="cf-perm-row"><span class="cf-perm-scope">Zone</span><span>Zone Settings</span><span class="cf-perm-access">Read</span></div>
                                <div class="cf-perm-row"><span class="cf-perm-scope">Zone</span><span>SSL and Certificates</span><span class="cf-perm-access">Edit</span></div>
                                <div class="cf-perm-row"><span class="cf-perm-scope">Zone</span><span>DNS</span><span class="cf-perm-access">Edit</span></div>
                                <div class="cf-perm-row"><span class="cf-perm-scope">Zone</span><span>Cache Purge</span><span class="cf-perm-access">Purge</span></div>
                                <div class="cf-perm-row"><span class="cf-perm-scope">Zone</span><span>Managed Headers</span><span class="cf-perm-access">Edit</span></div>
                                <div class="cf-perm-row"><span class="cf-perm-scope">Zone</span><span>Transform Rules</span><span class="cf-perm-access">Edit</span></div>
                                <div class="cf-perm-row"><span class="cf-perm-scope">Zone</span><span>Zone WAF</span><span class="cf-perm-access">Edit</span></div>
                            </div>
                            </div>
                        </div>
                    </div>
                    <table id="table_domain" class="responsive nowrap unstackable table table-hover table-fixed" cellspacing="0">
                        <thead>
                            <tr>
                                <th class="rg-source col-xs-6">DOMAIN</th>
                                <th class="rg-source col-xs-2">SUBID</th>
                                <th class="rg-source col-xs-2">CF</th>
                                <th class="rg-source col-xs-2">ACTION</th>
                            </tr>
                        </thead>
                        <tbody></tbody>
                    </table>
                </div>
            </div>
        </div>
        <input type="hidden" class="form-control input-sm" id="user_lp" name="user_lp">
        <input type="hidden" id="pick" name="pick" class="form-control input-sm" value="0">
        <input type="hidden" id="deb" name="deb" class="form-control input-sm" value="a">
        <input type="hidden" id="urlgen" name="urlgen" class="form-control input-sm" value="https://{sub}.global/{click_id}">
        <input type="hidden" id="pickurl" name="pickurl" class="form-control input-sm" value="0">
        <input type="hidden" id="sub_id" name="sub_id" class="form-control input-sm" value="<?= $escapedSubIdLower; ?>">
    </div>
    <hr class="compact-hr">
    <div class="app-sign"><footer>Ngix · xctd</footer></div>
    <div class="app-sign" style="margin-top:1px"><span class="banner-subid"><?= $escapedTitle; ?></span></div>

<div id="deleteConfirmToast" role="alertdialog" aria-modal="true" aria-labelledby="deleteConfirmMsg">
    <div class="dct-inner">
        <span class="dct-icon" aria-hidden="true"><svg viewBox="0 0 24 24" aria-hidden="true"><path fill="currentColor" d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg></span>
        <p class="dct-msg" id="deleteConfirmMsg"></p>
        <div class="dct-actions">
            <button type="button" class="btn btn-default btn-sm js-modal-close">Batal</button>
            <button type="button" class="btn btn-sm dct-btn-delete" id="confirm_delete_domain">Hapus</button>
        </div>
    </div>
    <input type="hidden" id="delete_domain_target" value="">
</div>
<div class="app-modal" id="cfResultModal" role="dialog" aria-modal="true" aria-labelledby="cfResultTitle" hidden>
    <div class="app-modal__dialog" role="document">
        <div class="app-modal__content">
            <div class="app-modal__header">
                <h4 class="app-modal__title" id="cfResultTitle">Cloudflare Zone</h4>
                <button type="button" class="close js-modal-close" aria-label="Close"><span aria-hidden="true">&times;</span></button>
            </div>
            <div class="app-modal__body">
                <div class="cf-modal-status">
                    <span id="cf-modal-domain" class="td-dom"></span>
                    <span class="cf-badge" id="cf-modal-badge"></span>
                </div>
                <p class="app-modal__note" id="cf-modal-note"></p>
                <ul class="cf-ns-list" id="cf-modal-ns"></ul>
            </div>
            <div class="app-modal__footer">
                <button type="button" class="btn btn-default btn-sm js-modal-close">Close</button>
            </div>
        </div>
    </div>
</div>
<div class="app-modal" id="cfNewTokenModal" role="dialog" aria-modal="true" aria-labelledby="cfNewTokenTitle" hidden>
    <div class="app-modal__dialog" role="document">
        <div class="app-modal__content">
            <div class="app-modal__header">
                <h4 class="app-modal__title" id="cfNewTokenTitle">New Cloudflare Additional Token</h4>
                <button type="button" class="close js-cf-newtoken-close" aria-label="Close"><span aria-hidden="true">&times;</span></button>
            </div>
            <div class="app-modal__body">
                <div class="cf-token-warn">⚠ Save this token now — it will not be shown again.</div>
                <div class="cf-token-row">
                    <pre id="cf-new-token-val" class="cf-token-val"></pre>
                    <button type="button" class="btn btn-xs" id="cf-copy-new-token">Copy</button>
                </div>
                <p class="cf-token-note">Token has been saved as your operational CF token. Copy it now if you need an external backup.</p>
            </div>
            <div class="app-modal__footer">
                <button type="button" class="btn btn-sm js-cf-newtoken-close">Close</button>
            </div>
        </div>
    </div>
</div>
</div>
<script nonce="<?= e($nonce); ?>" src="dist/jquery.min.js" type="text/javascript"></script>
<script nonce="<?= e($nonce); ?>" src="dist/bootstrap.min.js"></script>
<script nonce="<?= e($nonce); ?>">
(function ($) {
    'use strict';

    var csrfMeta = document.querySelector('meta[name="csrf-token"]');
    var CSRF_TOKEN = csrfMeta ? String(csrfMeta.getAttribute('content') || '') : '';

    if (CSRF_TOKEN === '') {
        CSRF_TOKEN = <?= json_encode($csrfToken, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT); ?>;
    }

    $.ajaxSetup({
        beforeSend: function (xhr, settings) {
            var method = String(settings.type || settings.method || 'GET').toUpperCase();
            if (method === 'POST' || method === 'PUT' || method === 'PATCH' || method === 'DELETE') {
                xhr.setRequestHeader('X-CSRF-Token', CSRF_TOKEN);
            }
        }
    });

    $(document).ajaxError(function (event, jqxhr) {
        if (jqxhr.status === 419) {
            showToast('error', 'Session expired. Reloading…');
            setTimeout(function () { location.reload(); }, 1500);
        }
    });

    window.NGIX_CSRF_TOKEN = CSRF_TOKEN;


    // PermissionToggleInit
    $(document).on('click', '.cf-perm-toggle', function () {
        var targetId = $(this).attr('data-target');
        var panel = document.getElementById(targetId);
        if (!panel) {
            return;
        }
        var isOpen = panel.classList.toggle('is-open');
        this.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
        this.textContent = isOpen ? 'Hide' : 'Show';
    });

    var SVG_SYNC = '<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10"/><path d="M20.49 15a9 9 0 0 1-14.85 3.36L1 14"/></svg>';
    var SVG_CF_ACTIVE  = '<svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg>';
    var SVG_CF_PENDING = '<svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>';
    var SVG_CF_ERROR   = '<svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>';
    var SVG_SPINNER    = '<svg class="btn-spin" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" aria-hidden="true"><circle cx="12" cy="12" r="10" stroke-opacity=".28"/><path d="M12 2a10 10 0 0 1 10 10" stroke-opacity=".92"/></svg>';

    $(document).on('focus', '.js-autoselect', function () { this.select(); });
    $(document).on('selectstart paste cut dragstart drop', '.js-readlock', function (e) { e.preventDefault(); return false; });

    $(document).on('click', '#copy-limitgen', function (e) {
        e.preventDefault();
        e.stopPropagation();
        var input = document.getElementById('limitgen');
        if (!input || !input.value) { showToast('error', 'No URL to copy.'); return; }
        var val = input.value;
        var $btn = $(this);
        var done = function () {
            showToast('success', 'URL copied.');
            $btn.addClass('copied');
            input.select();
            setTimeout(function () {
                $btn.removeClass('copied');
                input.setSelectionRange(0, 0);
                input.blur();
            }, 1200);
        };
        var fail = function () { showToast('error', 'Copy failed.'); };
        if (navigator.clipboard && navigator.clipboard.writeText) {
            navigator.clipboard.writeText(val).then(done, function () {
                try {
                    input.select();
                    document.execCommand('copy');
                    done();
                } catch (err) { fail(); }
            });
        } else {
            try {
                input.select();
                document.execCommand('copy');
                done();
            } catch (err) { fail(); }
        }
    });

    function showToast(type, message) {
        var icon = type === 'success'
            ? '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="currentColor" d="M9 16.17 4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>'
            : '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="currentColor" d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>';
        var item = $('<div class="toastitem toastitem--' + type + '"></div>');
        item.append(icon);
        item.append($('<span></span>').text(message));
        $('#toastbox').append(item);
        item[0].getBoundingClientRect();
        item.addClass('is-show');
        window.setTimeout(function () {
            item.removeClass('is-show').addClass('toastitem--hide');
            window.setTimeout(function () {
                item.remove();
            }, 250);
        }, 2600);
    }

    function openDeleteModal(domain) {
        var msg = domain
            ? 'Hapus ' + domain + ', semua short URL turunannya, dan bersihkan addon/subdomain/wildcard di cPanel + Cloudflare?'
            : 'Hapus domain ini, semua short URL turunannya, dan bersihkan addon/subdomain/wildcard di cPanel + Cloudflare?';
        $('#deleteConfirmMsg').text(msg);
        $('#deleteConfirmToast').addClass('is-open');
    }

    function closeDeleteModal() {
        $('#deleteConfirmToast').removeClass('is-open');
    }

    function findDomainRow(domain) {
        return $('#table_domain tbody tr').filter(function () {
            return $(this).attr('data-domain') === domain;
        });
    }

    function hasDomainOption(domain) {
        return $('#locdom option').filter(function () {
            return $(this).val() === domain;
        }).length > 0;
    }

    function syncDropdownState(selectId) {
        var select = $('#' + selectId);
        var wrap = select.closest('.dd-wrap');
        var selectedValue = $.trim(select.val() || '');
        var selectedText = $.trim(select.find('option:selected').text() || '');
        var matchedOption = wrap.find('.dd-option').filter(function () {
            var value = $(this).attr('data-val') || $(this).attr('data-value') || '';
            return value === selectedValue;
        }).first();

        if (selectedText !== '') {
            wrap.find('.dd-trigger-label').text(selectedText);
        }

        wrap.find('.dd-option').removeAttr('aria-selected');
        if (matchedOption.length > 0) {
            matchedOption.attr('aria-selected', 'true');
        }
    }

    function setDropdownValue(selectId, value, fireChange) {
        var select = $('#' + selectId);
        var option = select.find('option').filter(function () {
            return $(this).val() === value;
        }).first();

        if (select.length === 0 || option.length === 0) {
            return;
        }

        select.val(value);
        syncDropdownState(selectId);

        if (fireChange) {
            select.trigger('change');
        }
    }

    function appendDomainOption(domain) {
        if (hasDomainOption(domain)) {
            return;
        }

        $('#locdom').append(
            $('<option></option>')
                .val(domain)
                .attr('data-dynamic', '1')
                .text(domain)
        );

        $('#locdom-menu').append(
            $('<li></li>')
                .addClass('dd-option')
                .attr('data-val', domain)
                .attr('data-dynamic', '1')
                .attr('role', 'option')
                .append($('<span></span>').addClass('dd-option-label').text(domain))
        );
    }

    function removeDomainOption(domain) {
        $('#locdom option').filter(function () {
            return $(this).val() === domain || $(this).attr('name') === domain;
        }).remove();

        $('#locdom-menu .dd-option').filter(function () {
            var value = $(this).attr('data-val') || $(this).attr('data-value') || '';
            return value === domain;
        }).remove();

        if ($('#locdom').val() === null || $('#locdom').val() === '') {
            setDropdownValue('locdom', $('#locrandom').val() === 'global_off' ? 'u_rand' : 'user_off', false);
        } else {
            syncDropdownState('locdom');
        }
    }

    function isValidDomainInput(domain) {
        return /^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$/i.test(domain);
    }

    function preventManualInput(selector) {
        $(selector).on('keypress', function (e) {
            e.preventDefault();
            return false;
        });
    }

    function randomString(len, charSet) {
        var chars = charSet || 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
        var output = '';
        var index = 0;
        for (index = 0; index < len; index += 1) {
            output += chars.charAt(Math.floor(Math.random() * chars.length));
        }
        return output;
    }

    function applyWrapper(rawUrl, mode) {
        var cleanUrl = $.trim(rawUrl);
        if (cleanUrl === '') {
            return '';
        }
        if (mode === 'b') {
            return 'https://l.facebook.com/l.php?u=' + encodeURIComponent(cleanUrl) + '&h=' + randomString(7) + '&s=1';
        }
        if (mode === 'f') {
            return 'https://l.wl.co/l?u=' + encodeURIComponent(cleanUrl);
        }
        return cleanUrl;
    }

    function isLikelyShortenerErrorText(value) {
        var cleanValue = $.trim(String(value || ''));
        if (cleanValue === '') {
            return true;
        }
        return /^(error|failed|invalid|unauthorized|forbidden|method not allowed|database insert failed)(\b|[:,])/i.test(cleanValue)
            || /database\s+insert\s+failed/i.test(cleanValue);
    }

    function isValidGeneratedHttpUrl(value) {
        var cleanValue = $.trim(String(value || ''));
        var parsed;
        if (cleanValue === '' || isLikelyShortenerErrorText(cleanValue)) {
            return false;
        }
        if (!/^https?:\/\//i.test(cleanValue)) {
            return false;
        }
        try {
            parsed = new URL(cleanValue);
            return parsed.protocol === 'http:' || parsed.protocol === 'https:';
        } catch (err) {
            return false;
        }
    }

    function pickFirstValidUrl(row, keys) {
        var index;
        var candidate;
        if (!row) {
            return '';
        }
        for (index = 0; index < keys.length; index += 1) {
            candidate = $.trim(row[keys[index]] || '');
            if (isValidGeneratedHttpUrl(candidate)) {
                return candidate;
            }
        }
        return '';
    }

    function pickShortenerUrl(data) {
        var parsed;

        if ($.isArray(data)) {
            return data.length > 0 ? pickFirstValidUrl(data[0], ['short_url', 'shortURL', 'shorturl', 'l']) : '';
        }

        if (typeof data === 'string') {
            if (isValidGeneratedHttpUrl(data)) {
                return $.trim(data);
            }

            try {
                parsed = JSON.parse(data);
                return pickShortenerUrl(parsed);
            } catch (err) {
                return '';
            }
        }

        if (data && typeof data === 'object') {
            return pickFirstValidUrl(data, ['short_url', 'shortURL', 'shorturl', 'l']);
        }

        return '';
    }

    function safeArrayResponse(data) {
        if ($.isArray(data)) {
            return data;
        }
        if (typeof data === 'string') {
            try {
                var parsed = JSON.parse(data);
                return $.isArray(parsed) ? parsed : [];
            } catch (err) {
                return [];
            }
        }
        return [];
    }

    function resetGenerateOutputs() {
        $('#limitgen').val('').removeClass('error success');
    }

    function initFirstNetwork() {
        var firstButton = $('#radioBtn a').first();
        if (firstButton.length === 0) {
            return;
        }
        firstButton.removeClass('notActive').addClass('active save');
        $('#user_lp').val(firstButton.data('title') || '');
    }

    function buildDomainUrlTemplate(value) {
        return 'https://{sub}.' + value + '/{click_id}';
    }

    function activateGlobalDomain(value) {
        var cleanValue = $.trim(value || '');
        if (cleanValue === '' || cleanValue === 'global_off') {
            cleanValue = 'global';
        }

        $('#urlgen').val(buildDomainUrlTemplate(cleanValue));
        setDropdownValue('locrandom', cleanValue, false);
        setDropdownValue('locdom', 'user_off', false);
        $('#locrandom-wrap').removeClass('is-passive');
        $('#locdom-wrap').addClass('is-passive');
    }

    function activateUserDomain(value) {
        var cleanValue = $.trim(value || '');
        if (cleanValue === '' || cleanValue === 'user_off') {
            cleanValue = 'u_rand';
        }

        $('#urlgen').val(buildDomainUrlTemplate(cleanValue));
        setDropdownValue('locdom', cleanValue, false);
        setDropdownValue('locrandom', 'global_off', false);
        $('#locdom-wrap').removeClass('is-passive');
        $('#locrandom-wrap').addClass('is-passive');
    }

    function updateUrlTemplateFromGlobal(value) {
        var cleanValue = $.trim(value || '');
        if (cleanValue === 'global_off') {
            activateUserDomain($('#locdom').val());
            return;
        }

        activateGlobalDomain(cleanValue);
    }

    function updateUrlTemplateFromUser(value) {
        var cleanValue = $.trim(value || '');
        if (cleanValue === 'user_off') {
            activateGlobalDomain($('#locrandom').val());
            return;
        }

        activateUserDomain(cleanValue);
    }

    function initDomainSelectorFlow() {
        activateGlobalDomain('global');
    }

    var cfStatusCache = {};

    function updateDomainCount() {
        var c = $('#table_domain tbody tr').length;
        $('#domain-count').text(c + (c === 1 ? ' domain' : ' domains'));
    }

    function updateCfBadge(domain, data) {
        var badge = $('.cf-badge[data-domain="' + domain + '"]');
        badge.removeClass('cf-badge--active cf-badge--pending cf-badge--error cf-badge--loading is-hidden');
        if (!data || !data.ok) {
            badge.addClass('cf-badge--error').html(SVG_CF_ERROR);
            cfStatusCache[domain] = 'error';
            return;
        }
        var status = (data.status || 'pending').toLowerCase();
        cfStatusCache[domain] = status;
        if (status === 'active') {
            badge.addClass('cf-badge--active').html(SVG_CF_ACTIVE);
        } else {
            badge.addClass('cf-badge--pending').html(SVG_CF_PENDING);
        }
    }

    function openCfModal(domain, data) {
        var status = (data.status || 'pending').toLowerCase();
        $('#cf-modal-domain').text(domain);
        var mb = $('#cf-modal-badge').html('').removeClass('cf-badge--active cf-badge--pending cf-badge--error');
        if (status === 'active') {
            mb.addClass('cf-badge--active').html(SVG_CF_ACTIVE);
        } else if (!data || !data.ok) {
            mb.addClass('cf-badge--error').html(SVG_CF_ERROR);
        } else {
            mb.addClass('cf-badge--pending').html(SVG_CF_PENDING);
        }
        var ns = data.name_servers || [];
        var nsList = $('#cf-modal-ns').empty();
        var note = '';
        if (!data || !data.ok) {
            note = 'Gagal menambahkan zone Cloudflare.';
        } else if (status === 'active') {
            note = 'Zone ' + (data.already_exists ? 'sudah aktif' : 'aktif') + ' di Cloudflare.';
        } else if (ns.length > 0) {
            note = (data.already_exists ? 'Zone sudah ada. ' : 'Zone ditambahkan. ') + 'Arahkan nameserver domain ke:';
        } else {
            note = data.already_exists ? 'Zone sudah ada di akun Cloudflare.' : '';
        }
        $('#cf-modal-note').text(note);
        if (ns.length > 0) {
            $.each(ns, function (i, n) { nsList.append($('<li>').attr('title', 'Klik untuk salin').text(n).on('click', function(){ if(navigator.clipboard){navigator.clipboard.writeText(n);} })); });
            nsList.addClass('ns-visible');
        } else {
            nsList.removeClass('ns-visible');
        }
        $('#cfResultModal').prop('hidden', false).addClass('is-open');
    }

    function closeCfModal() {
        $('#cfResultModal').removeClass('is-open').prop('hidden', true);
    }

    function syncDomainCf(domain, onDone) {
        // Short-circuit: skip AJAX entirely if domain is already active.
        // The backend would also skip heavy operations, but avoiding the
        // round-trip is cheaper and keeps the badge visually stable.
        if (cfStatusCache[domain] === 'active') {
            if (typeof onDone === 'function') {
                onDone(null, { ok: true, status: 'active', already_exists: true, skipped: true });
            }
            return;
        }
        var badge = $('.cf-badge[data-domain="' + domain + '"]');
        var syncBtn = $('.cf-sync-btn[data-domain="' + domain + '"]');
        badge.removeClass('cf-badge--active cf-badge--pending cf-badge--error').addClass('cf-badge--loading').html(SVG_SPINNER);
        syncBtn.prop('disabled', true).html(SVG_SPINNER);
        $.ajax({
            url: '/api/cf_sync.php',
            type: 'post',
            dataType: 'json',
            data: { domain: domain, sub_domain: $.trim($('#userid').val()) }
        }).done(function (data) {
            updateCfBadge(domain, data);
            if (typeof onDone === 'function') { onDone(null, data); }
        }).fail(function () {
            badge.removeClass('cf-badge--loading').addClass('cf-badge--error').html(SVG_CF_ERROR);
            if (typeof onDone === 'function') { onDone('failed', null); }
        }).always(function () {
            badge.removeClass('cf-badge--loading');
            syncBtn.prop('disabled', false).html(SVG_SYNC);
        });
    }

    function appendDomainRow(rowData) {
        if (!rowData || !rowData.domain) {
            return;
        }
        if (findDomainRow(rowData.domain).length > 0) {
            return;
        }
        var SVG_TRASH = '<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14H6L5 6"/><path d="M10 11v6"/><path d="M14 11v6"/><path d="M9 6V4h6v2"/></svg>';
        var row = $('<tr></tr>').attr('data-domain', rowData.domain);
        var domainCell = $('<td></td>');
        var domSpan = $('<span class="td-dom"></span>').text(rowData.domain);
        var copyBtn = $('').attr('data-domain', rowData.domain).html('<svg viewBox="0 0 24 24" width="11" height="11" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>');
        domainCell.append(domSpan).append(' ').append(copyBtn);
        row.append(domainCell);
        row.append($('<td></td>').append($('<span class="td-subid"></span>').text(rowData.sub_domain || '')));
        // Seed cache from DB data so the badge is correct on initial page load
        // (cfStatusCache is only populated via updateCfBadge after a sync)
        if (rowData.cf_status && !cfStatusCache[rowData.domain]) {
            cfStatusCache[rowData.domain] = rowData.cf_status.toLowerCase();
        }
        var cfStatus = cfStatusCache[rowData.domain] || '';
        var badge = $('<span class="cf-badge"></span>').attr('data-domain', rowData.domain);
        if (cfStatus === 'active') { badge.addClass('cf-badge--active').html(SVG_CF_ACTIVE); }
        else if (cfStatus === 'pending') { badge.addClass('cf-badge--pending').html(SVG_CF_PENDING); }
        else if (cfStatus === 'error') { badge.addClass('cf-badge--error').html(SVG_CF_ERROR); }
        else { badge.addClass('is-hidden'); }
        var cfSyncBtn = $('<button type="button" class="btn btn-xs cf-sync-btn" title="Sync CF"></button>').attr('data-domain', rowData.domain).html(SVG_SYNC);
        row.append($('<td class="td-cf-cell"></td>').append(badge).append(cfSyncBtn));
        var actionCell = $('<td></td>');
        var deleteButton = $('<button type="button" class="delete btn btn-danger btn-xs" title="Delete"></button>');
        deleteButton.attr('data-domain', rowData.domain).html(SVG_TRASH);
        actionCell.append(deleteButton);
        row.append(actionCell);
        $('#table_domain tbody').append(row);
        updateDomainCount();

        appendDomainOption(rowData.domain);
    }

    function drawTable(data) {
        var index = 0;
        var selectedLocdom = $.trim($('#locdom').val() || '');
        $('#table_domain tbody').empty();
        $('#locdom option[data-dynamic="1"]').remove();
        $('#locdom-menu .dd-option[data-dynamic="1"]').remove();
        for (index = 0; index < data.length; index += 1) {
            appendDomainRow(data[index]);
        }
        if (selectedLocdom !== '' && hasDomainOption(selectedLocdom)) {
            setDropdownValue('locdom', selectedLocdom, false);
        } else {
            setDropdownValue('locdom', $('#locrandom').val() === 'global_off' ? 'u_rand' : 'user_off', false);
        }
        updateDomainCount();
    }

    function loadUserDomains() {
        $.ajax({
            url: '/api/user_domain.php',
            type: 'get',
            dataType: 'json',
            data: {
                sub_domain: $.trim($('#userid').val()),
                domain: $.trim($('#domain').val())
            }
        }).done(function (data) {
            if ($.isArray(data)) {
                drawTable(data);
            }
        });
    }

    function loadCfConfig() {
        $.ajax({
            url: '/api/user_cf_config.php',
            type: 'get',
            dataType: 'json'
        }).done(function (data) {
            if (data && data.has_token) {
                $('#cf-input-token').val(data.token_masked || '').attr('placeholder', 'Token tersimpan');
                $('#cf-input-account').val(data.account_id || '');
            } else {
                $('#cf-input-token').attr('placeholder', 'Bearer token dari CF dashboard');
                $('#cf-input-account').val('');
            }
        });
    }

    function setCfConfigPanelState(isOpen) {
        var panel = $('#cf-config-panel');
        var toggle = $('#cf-config-toggle');
        panel.toggleClass('is-open', isOpen);
        toggle.attr('aria-expanded', isOpen ? 'true' : 'false');
        toggle.text(isOpen ? 'Hide CF Settings' : 'Show CF Settings');
        if (isOpen) {
            loadCfConfig();
        }
    }

    $('#cf-config-toggle').on('click', function () {
        setCfConfigPanelState(!$('#cf-config-panel').hasClass('is-open'));
    });

    var SVG_BTN_SPIN = '<svg class="btn-spin" viewBox="0 0 16 16" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="6" stroke-opacity=".3"/><path d="M8 2a6 6 0 0 1 6 6"/></svg> ';
    function btnLoad(btn, label) {
        var $b = $(btn);
        if (!$b.data('__oh')) { $b.data('__oh', $b.html()); }
        $b.prop('disabled', true).html(SVG_BTN_SPIN + (label || ''));
    }
    function btnReset(btn) {
        var $b = $(btn);
        var oh = $b.data('__oh');
        if (oh !== undefined) { $b.prop('disabled', false).html(oh); $b.removeData('__oh'); }
        else { $b.prop('disabled', false); }
    }

    // ── Auto-detect Account ID dari token ────────────────────────────────────
    $('#cf-detect-account').on('click', function () {
        var token = $.trim($('#cf-input-token').val());
        var status = $('#cf-config-status');
        if (token === '') { status.text('Isi API Token dulu.'); return; }
        var btn = $(this); btnLoad(btn, '...');
        status.text('Mendeteksi...');
        $.ajax({
            url: '/api/user_cf_config.php',
            type: 'post',
            dataType: 'json',
            data: { action: 'detect', cf_token: token }
        }).done(function (data) {
            if (data && data.ok && data.account_id) {
                $('#cf-input-account').val(data.account_id);
                status.text('✓ ' + (data.account_name || data.account_id));
            } else {
                status.text((data && data.err) ? data.err : 'Gagal detect.');
            }
        }).fail(function () {
            status.text('Request gagal.');
        }).always(function () {
            btnReset(btn);
        });
    });

    $('#cf-config-save').on('click', function () {
        var token = $.trim($('#cf-input-token').val());
        var accountId = $.trim($('#cf-input-account').val());
        var status = $('#cf-config-status');
        if (token === '') { status.text('Token wajib diisi.'); return; }
        var _savebtn = $(this); btnLoad(_savebtn, 'Saving…');
        status.text('Menyimpan...');
        $.ajax({
            url: '/api/user_cf_config.php',
            type: 'post',
            dataType: 'json',
            data: { action: 'save', cf_token: token, cf_account_id: accountId }
        }).done(function (data) {
            if (data && data.ok) {
                status.text('Tersimpan.');
                loadCfConfig();
                showToast('success', 'CF token disimpan.');
            } else {
                status.text('Gagal menyimpan.');
            }
        }).fail(function () { status.text('Error.'); }).always(function () { btnReset(_savebtn); });
    });

    $('#cf-config-clear').on('click', function () {
        var status = $('#cf-config-status');
        var _clearbtn = $(this); btnLoad(_clearbtn, 'Clearing…');
        status.text('Menghapus...');
        $.ajax({
            url: '/api/user_cf_config.php',
            type: 'post',
            dataType: 'json',
            data: { action: 'clear' }
        }).done(function (data) {
            if (data && data.ok) {
                status.text('Dihapus, pakai Admin CF.');
                $('#cf-input-token').val('');
                $('#cf-input-account').val('');
                loadCfConfig();
                showToast('success', 'CF token dihapus, fallback ke admin.');
            }
        }).fail(function () { status.text('Error.'); }).always(function () { btnReset(_clearbtn); });
    });

    // ── Create Additional Tokens ─────────────────────────────────────────────
    function openCfNewTokenModal(token) {
        $('#cf-new-token-val').text(token);
        $('#cfNewTokenModal').prop('hidden', false).addClass('is-open');
    }

    function closeCfNewTokenModal() {
        $('#cfNewTokenModal').removeClass('is-open').prop('hidden', true);
        // Clear sensitive value from DOM immediately on close.
        setTimeout(function () { $('#cf-new-token-val').text(''); }, 300);
    }

    function saveCreatedCfToken(token, accountId, done, fail) {
        $.ajax({
            url: '/api/user_cf_config.php',
            type: 'post',
            dataType: 'json',
            data: {
                action: 'save',
                cf_token: token,
                cf_account_id: accountId || '',
                csrf_token: CSRF_TOKEN
            }
        }).done(function (data) {
            if (data && data.ok) {
                if (typeof done === 'function') {
                    done(data);
                }
                return;
            }
            if (typeof fail === 'function') {
                fail(data);
            }
        }).fail(function (xhr) {
            if (typeof fail === 'function') {
                fail(xhr);
            }
        });
    }

    $('#cf-create-token').on('click', function () {
        var creatorToken = $.trim($('#cf-input-token').val());
        var accountId = $.trim($('#cf-input-account').val());
        var btn = $(this);
        var status = $('#cf-config-status');

        if (creatorToken === '') {
            status.text('Paste token dari template Create Additional Tokens dulu.');
            showToast('error', 'Creator token required.');
            return;
        }

        if ($('#cf-input-token').attr('data-masked') === '1' || creatorToken.indexOf('•') !== -1 || creatorToken.indexOf('*') !== -1) {
            status.text('Masked token tidak bisa dipakai. Paste creator token asli.');
            showToast('error', 'Paste the real creator token first.');
            return;
        }

        btnLoad(btn, 'Generating…');
        status.text('Creating additional token…');

        $.ajax({
            url: '/api/cf_create_token.php',
            type: 'post',
            dataType: 'json',
            data: {
                creator_token: creatorToken,
                cf_account_id: accountId,
                csrf_token: CSRF_TOKEN
            }
        }).done(function (data) {
            if (!data || !data.ok || !data.token) {
                var err = (data && data.err) ? data.err : 'error';
                var missingList = (data && data.missing && data.missing.length) ? data.missing
                    : (data && data.missing_rows && data.missing_rows.length)
                        ? data.missing_rows.map(function (r) { return r.scope + ' — ' + r.permission + ':' + r.access; })
                        : [];
                if (missingList.length) {
                    err += ': ' + missingList.join(', ');
                }
                status.text('Failed: ' + err);
                showToast('error', 'Token creation failed: ' + err);
                return;
            }

            status.text('Token created. Saving operational token…');
            $('#cf-input-token').val('').attr('type', 'password').removeAttr('data-masked');
            if (data.account_id) {
                $('#cf-input-account').val(data.account_id);
            }

            saveCreatedCfToken(data.token, data.account_id || accountId, function () {
                status.text('Additional token created & saved.');
                loadCfConfig();
                openCfNewTokenModal(data.token);
                showToast('success', 'CF additional token created.');
            }, function () {
                status.text('Token created, but save failed. Copy token now.');
                openCfNewTokenModal(data.token);
                showToast('error', 'Token created but save failed.');
            });
        }).fail(function (xhr) {
            var msg = 'Request failed.';
            if (xhr && xhr.responseJSON && xhr.responseJSON.err) {
                msg = xhr.responseJSON.err;
            }
            status.text(msg);
            showToast('error', 'CF token creation request failed.');
        }).always(function () {
            btnReset(btn);
        });
    });

    $('#cf-copy-new-token').on('click', function () {
        var val = $('#cf-new-token-val').text();
        if (!val) { return; }
        var btn = $(this);
        var done = function () {
            showToast('success', 'Token copied to clipboard.');
            btn.text('Copied!');
            setTimeout(function () { btn.text('Copy'); }, 1500);
        };
        var fail = function () { showToast('error', 'Copy failed — select the token manually.'); };
        if (navigator.clipboard && navigator.clipboard.writeText) {
            navigator.clipboard.writeText(val).then(done, fail);
        } else {
            var el = document.createElement('textarea');
            el.value = val;
            el.style.position = 'fixed';
            el.style.opacity = '0';
            document.body.appendChild(el);
            el.select();
            try { document.execCommand('copy'); done(); } catch (e) { fail(); }
            document.body.removeChild(el);
        }
    });

    $(document).on('click', '.js-cf-newtoken-close', function () {
        closeCfNewTokenModal();
    });

    $(document).ready(function () {
        initFirstNetwork();
        preventManualInput('#urlgen,#pick,#limitgen');
        loadCfConfig();

        $('img').on('contextmenu', function () {
            return false;
        });

        $(document).on('click', '.js-modal-close', function () {
            closeDeleteModal();
            closeCfModal();
            return false;
        });

        $(document).on('keydown', function (e) {
            if (e.key === 'Escape') {
                closeDeleteModal();
                closeCfModal();
            }
        });

        $('#radioBtn').on('click', 'a', function () {
            var sel = $(this).data('title') || '';
            var tog = $(this).data('toggle') || '';
            if (tog !== '') {
                $('#' + tog).val(sel);
            }
            $('a[data-toggle="' + tog + '"]').removeClass('active save').addClass('notActive');
            $(this).removeClass('notActive').addClass('active save');
        });


        $('#locrandom').on('change', function () {
            updateUrlTemplateFromGlobal($.trim(this.value || ''));
        });

        $('#locdom').on('change', function () {
            updateUrlTemplateFromUser($.trim(this.value || ''));
        });

        $('#tabgen, #tabaddon').on('click', function () {
            resetGenerateOutputs();
        });

        $('#genurl').on('click', function () {
            var url = $.trim($('#urlgen').val());
            var button = $(this);
            var uri = $.trim($('#uri').val());
            var prm = $.trim($('#net_pick').val());
            var limitRaw = parseInt($.trim($('#shortgen').val()), 10);
            var limit = (isNaN(limitRaw) || limitRaw < 1) ? 1 : Math.min(limitRaw, 50);

            if (url === '') {
                $('#shortgen').addClass('error');
                $('#limitgen').val('* Missing URL template').addClass('error').removeClass('success');
                showToast('error', 'URL template is missing.');
                return false;
            }

            btnLoad(button, 'Generating…');
            $('#shortgen').removeClass('error');
            $('#limitgen').val('generating ' + limit + '...').removeClass('error success');

            var results = [];
            var failCount = 0;

            function applyShortener(generatedUrl, done) {
                if (uri === 'ixg') {
                    $.post('/api/ixg.php', { longurl: generatedUrl, prm: prm, csrf_token: CSRF_TOKEN })
                        .done(function (data) {
                            var out = pickShortenerUrl(data);
                            done(out !== '' ? out : null);
                        })
                        .fail(function () { done(null); });
                } else if (uri === 'turl') {
                    $.post('/api/tinyurl.php', { longurl: generatedUrl, prm: prm, csrf_token: CSRF_TOKEN })
                        .done(function (data) {
                            var out = pickShortenerUrl(data);
                            done(out !== '' ? out : null);
                        })
                        .fail(function () { done(null); });
                } else if (uri === 'ixgeu') {
                    $.post('/api/ixgeu.php', { longurl: generatedUrl, prm: prm })
                        .done(function (data) {
                            var rows = safeArrayResponse(data);
                            var out = rows.length > 0 ? pickFirstValidUrl(rows[0], ['l', 'shorturl']) : '';
                            done(out !== '' ? out : null);
                        })
                        .fail(function () { done(null); });
                } else {
                    var wrappedUrl = applyWrapper(generatedUrl, prm);
                    done(isValidGeneratedHttpUrl(wrappedUrl) ? wrappedUrl : null);
                }
            }

            function generateStep(remaining) {
                if (remaining === 0) {
                    if (results.length === 0) {
                        $('#limitgen').val('* All requests failed').addClass('error').removeClass('success');
                        showToast('error', 'Generate failed.');
                    } else {
                        $('#limitgen').val(results.join('\n')).addClass('success').removeClass('error');
                        showToast('success', results.length + ' URL' + (results.length !== 1 ? 's' : '') + ' generated.');
                    }
                    btnReset(button);
                    return;
                }

                $.post('/api/generate_api.php', {
                    longurl: url,
                    sub_id: $.trim($('#sub_id').val()),
                    user_lp: $.trim($('#user_lp').val()),
                    csrf_token: CSRF_TOKEN,
                    noshorten: (uri === 'long' ? '1' : '0'),

                    fbimg: $.trim($('#fbimg').val()),
                    fbtext: $.trim($('#fbtext').val()),
                    lg: $.trim($('#lg').val())
                }).done(function (data) {
                    var rows = safeArrayResponse(data);
                    var generatedUrl = rows.length > 0 ? pickFirstValidUrl(rows[0], ['shorturl', 'l']) : '';
                    if (!generatedUrl || generatedUrl === 'https://') {
                        failCount++;
                        generateStep(remaining - 1);
                        return;
                    }
                    applyShortener(generatedUrl, function (finalUrl) {
                        if (finalUrl) {
                            results.push(finalUrl);
                        } else {
                            failCount++;
                        }
                        generateStep(remaining - 1);
                    });
                }).fail(function () {
                    failCount++;
                    generateStep(remaining - 1);
                });
            }

            generateStep(limit);
            return false;
        });

        $('#addondom').on('click', function () {
            var domain = $.trim($('#domain').val());
            var userid = $.trim($('#userid').val());
            var button = $(this);

            if (domain === '') {
                showToast('error', 'Addon domain is required.');
                return false;
            }

            if (!isValidDomainInput(domain)) {
                showToast('error', 'Invalid domain format.');
                return false;
            }

            btnLoad(button, 'Adding…');
            $.ajax({
                url: '/api/call.php',
                type: 'post',
                dataType: 'json',
                data: {
                    sub_domain: userid,
                    domain: domain
                }
            }).done(function (resp) {
                var rows = safeArrayResponse(resp);
                if (rows.length === 0 || !rows[0] || !rows[0].domain || !rows[0].userid) {
                    showToast('error', 'Addon domain response is invalid.');
                    btnReset(button);
                    return;
                }

                $.ajax({
                    url: '/api/user_insert_domain.php',
                    type: 'post',
                    dataType: 'json',
                    data: {
                        domain: rows[0].domain,
                        sub_domain: rows[0].userid
                    }
                }).done(function (data) {
                    if ($.isArray(data)) {
                        drawTable(data);
                        var newDomain = rows[0].domain;
                        $('#domain').val('');
                        if ($('#cf-enabled').is(':checked')) {
                            showToast('success', 'Addon domain saved. Syncing CF...');
                            syncDomainCf(newDomain, function (err, cfData) {
                                if (err) {
                                    showToast('error', 'CF sync failed for ' + newDomain);
                                } else if (cfData && cfData.ok) {
                                    openCfModal(newDomain, cfData);
                                }
                            });
                        } else {
                            showToast('success', 'Addon domain saved.');
                        }
                    } else {
                        showToast('error', 'Addon domain save failed.');
                    }
                    btnReset(button);
                }).fail(function () {
                    showToast('error', 'Addon domain insert failed.');
                    btnReset(button);
                });
            }).fail(function (xhr) {
                var msg = 'Addon domain request failed.';
                if (xhr && xhr.responseJSON && xhr.responseJSON.error) {
                    msg += ' ' + xhr.responseJSON.error;
                } else if (xhr && xhr.responseText) {
                    try {
                        var parsedErr = JSON.parse(xhr.responseText);
                        if (parsedErr && parsedErr.error) {
                            msg += ' ' + parsedErr.error;
                        }
                    } catch (err) {}
                }
                showToast('error', msg);
                button.button('reset');
            });

            return false;
        });

        $(document).on('click', '.delete', function () {
            var buttonId = $(this).attr('data-domain') || '';
            if (buttonId === '') {
                showToast('error', 'Invalid domain target.');
                return false;
            }
            $('#delete_domain_target').val(buttonId);
            openDeleteModal(buttonId);
            return false;
        });

        $('#confirm_delete_domain').on('click', function () {
            var buttonId = $.trim($('#delete_domain_target').val());
            var rowNode = findDomainRow(buttonId);
            if (buttonId === '') {
                showToast('error', 'Invalid domain target.');
                return false;
            }
            var _delbtn = $(this); btnLoad(_delbtn, 'Deleting…');
            $.ajax({
                url: '/api/user_del_domain.php',
                type: 'post',
                dataType: 'json',
                data: {
                    domain: buttonId
                }
            }).done(function () {
                removeDomainOption(buttonId);
                rowNode.addClass('table-row-removing');
                window.setTimeout(function () {
                    rowNode.remove();
                }, 260);
                closeDeleteModal();
                $('#delete_domain_target').val('');
                showToast('success', 'Domain deleted.');
            }).fail(function () {
                closeDeleteModal();
                showToast('error', 'Delete request failed.');
            }).always(function () { btnReset(_delbtn); });
            return false;
        });

        $(document).on('click', '.cf-sync-btn', function () {
            var domain = $(this).attr('data-domain') || '';
            if (domain === '') { return; }
            syncDomainCf(domain, function (err, data) {
                if (err) { showToast('error', 'CF sync failed.'); return; }
                if (data && data.skipped) {
                    showToast('success', domain + ' sudah aktif di Cloudflare.');
                    return;
                }
                if (data && data.ok) {
                    openCfModal(domain, data);
                } else {
                    showToast('error', 'CF error: ' + ((data && data.err) ? data.err : 'unknown'));
                }
            });
        });

        $(document).on('click', '.btn-copy-domain', function () {
            var domain = $(this).attr('data-domain') || '';
            if (domain === '') { return; }
            var $btn = $(this);
            var done = function () {
                showToast('success', 'Copied: ' + domain);
                $btn.addClass('copied');
                setTimeout(function () { $btn.removeClass('copied'); }, 1200);
            };
            var fail = function () { showToast('error', 'Copy failed.'); };
            if (navigator.clipboard && navigator.clipboard.writeText) {
                navigator.clipboard.writeText(domain).then(done, fail);
            } else {
                var el = document.createElement('textarea');
                el.value = domain;
                document.body.appendChild(el);
                el.select();
                try { document.execCommand('copy'); done(); } catch (e) { fail(); }
                document.body.removeChild(el);
            }
        });

        $('#cf-sync-all').on('click', function () {
            var domains = [];
            $('#table_domain tbody tr').each(function () {
                var d = $(this).attr('data-domain') || '';
                if (d !== '') { domains.push(d); }
            });
            if (domains.length === 0) { showToast('error', 'No domains to sync.'); return; }
            var btn = $(this); btnLoad(btn, 'Syncing…');
            var idx = 0;
            var ok = 0;
            function next() {
                if (idx >= domains.length) {
                    btnReset(btn);
                    showToast('success', ok + '/' + domains.length + ' synced to CF.');
                    return;
                }
                var d = domains[idx++];
                syncDomainCf(d, function (err) {
                    if (!err) { ok++; }
                    window.setTimeout(next, 400);
                });
            }
            next();
        });

        $(document).on('click', '.app-modal', function (e) {
            if (e.target === this) {
                e.preventDefault();
                e.stopPropagation();
                return false;
            }
        });

        $(document).on('click', '.app-modal__content', function (e) {
            e.stopPropagation();
        });

        initDomainSelectorFlow();
        loadUserDomains();
    });
}(jQuery));
</script>
<script nonce="<?= e($nonce); ?>">
(function($){
    function ddCloseAll() {
        document.querySelectorAll('.dd-wrap.is-open').forEach(function(wrap) {
            wrap.classList.remove('is-open');
            var trigger = wrap.querySelector('.dd-trigger');
            if (trigger) {
                trigger.setAttribute('aria-expanded', 'false');
            }
        });
    }

    function getItemValue(item) {
        return item.getAttribute('data-val') || item.getAttribute('data-value') || '';
    }

    function selectDropdownItem(wrap, item) {
        var label = wrap.querySelector('.dd-trigger-label');
        var optionLabel = item.querySelector('.dd-option-label');
        var select = wrap.querySelector('select');
        var value = getItemValue(item);

        wrap.querySelectorAll('.dd-option').forEach(function(option) {
            option.removeAttribute('aria-selected');
        });
        item.setAttribute('aria-selected', 'true');

        if (label && optionLabel) {
            label.textContent = optionLabel.textContent.trim();
        }

        if (select) {
            select.value = value;
            $(select).trigger('change');
        }

        ddCloseAll();
    }

    document.addEventListener('click', function(event) {
        var trigger = event.target.closest('.dd-trigger');
        var item = event.target.closest('.dd-option');
        var wrap;

        if (trigger) {
            wrap = trigger.closest('.dd-wrap');
            if (!wrap) {
                return;
            }

            event.preventDefault();
            event.stopPropagation();

            if (wrap.classList.contains('is-open')) {
                ddCloseAll();
                return;
            }

            ddCloseAll();
            wrap.classList.add('is-open');
            trigger.setAttribute('aria-expanded', 'true');
            return;
        }

        if (item) {
            wrap = item.closest('.dd-wrap');
            if (!wrap) {
                return;
            }

            event.preventDefault();
            event.stopPropagation();
            selectDropdownItem(wrap, item);
            return;
        }

        if (!event.target.closest('.dd-wrap')) {
            ddCloseAll();
        }
    });

    document.addEventListener('keydown', function(event) {
        if (event.key === 'Escape') {
            ddCloseAll();
        }
    });
})(jQuery);
if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('/sw.js');
}
document.addEventListener('click', function(e) {
    var target = e.target;
    var btn = target && typeof target.closest === 'function' ? target.closest('#btn-logout') : null;

    if (!btn) {
        return;
    }

    e.preventDefault();

    var href = btn.getAttribute('href') || btn.getAttribute('data-href') || '/logout';
    var toast = document.getElementById('logout-toast');

    if (toast) {
        toast.classList.add('show');
    }

    window.setTimeout(function() {
        window.location.href = href;
    }, 900);
});
</script>
<style nonce="<?= e($nonce); ?>">#logout-toast{position:fixed;right:12px;bottom:12px;z-index:10060;display:none;padding:8px 10px;border:1px solid var(--line);border-radius:var(--radius);background:var(--panel);color:var(--text);box-shadow:var(--shadow-modal);font-size:12px}#logout-toast.show{display:block}</style>
<div id="logout-toast">Logging out…</div>
<style nonce="<?= e($nonce); ?>">
/* NGIX patch: fullscreen background/backdrop override main shell */
html,body{min-height:100svh!important}
body{background-color:var(--bg,#f8fafc)!important;background-attachment:fixed!important}
body::after{content:""!important;position:fixed!important;inset:0!important;width:100vw!important;min-width:100vw!important;height:100vh!important;height:100dvh!important;z-index:0!important;pointer-events:none!important;background-image:url('/assets/favicon.svg'),url('/assets/bg.png')!important;background-repeat:no-repeat,no-repeat!important;background-position:right 14px bottom 12px,center center!important;background-size:28px 28px,cover!important;opacity:.30!important;filter:saturate(.68) contrast(.84) brightness(1.03) blur(.32px)!important;transform:translateZ(0)!important}
body>.container,body>.app-shell,body>.navbar,body>.panel,body>.panel_m,main,.container,.navbar,.panel,.panel_m,.app-footer{position:relative;z-index:1}
.panel_m.panel>.ngix-image-backdrop{position:fixed!important;inset:0!important;width:100vw!important;min-width:100vw!important;height:100vh!important;height:100dvh!important;z-index:0!important;pointer-events:none!important;background-repeat:no-repeat,no-repeat!important;background-position:center center,center center!important;background-size:100% 100%,cover!important;opacity:.40!important;filter:saturate(.72) contrast(.86) blur(.22px)!important;transform:translateZ(0)!important}
.panel_m.panel>.panel-heading,.panel_m.panel>.panel-body{position:relative!important;z-index:2!important}
@media (prefers-color-scheme:dark){body::after{opacity:.24!important;filter:saturate(.62) contrast(.78) brightness(.88) blur(.34px)!important}.panel_m.panel>.ngix-image-backdrop{opacity:.34!important;filter:saturate(.66) contrast(.80) blur(.24px)!important}}
@media screen and (max-width:768px){body::after{background-position:right 10px bottom 10px,center center!important;background-size:24px 24px,cover!important;opacity:.24!important}.panel_m.panel>.ngix-image-backdrop{background-size:100% 100%,cover!important;opacity:.30!important}}
</style>

<style nonce="<?= e($nonce); ?>">
/* NGIX patch: public modal/backdrop/logout spinner correction */
html {
  zoom: 1 !important;
  width: 100% !important;
  min-width: 100% !important;
  overflow-x: hidden !important;
}
body {
  width: 100% !important;
  min-width: 100vw !important;
  max-width: none !important;
  overflow-x: hidden !important;
}
body.modal-open {
  overflow: hidden !important;
  padding-right: 0 !important;
}
.modal-backdrop {
  position: fixed !important;
  inset: 0 !important;
  width: 100vw !important;
  max-width: none !important;
  height: 100vh !important;
  height: 100dvh !important;
  margin: 0 !important;
  background: #000 !important;
  z-index: 1060 !important;
}
.modal-backdrop.in {
  opacity: .48 !important;
}
.modal {
  position: fixed !important;
  inset: 0 !important;
  width: 100vw !important;
  max-width: none !important;
  height: 100vh !important;
  height: 100dvh !important;
  margin: 0 !important;
  padding: 58px 12px 14px !important;
  overflow-x: hidden !important;
  overflow-y: auto !important;
  text-align: center !important;
  z-index: 1070 !important;
  -webkit-overflow-scrolling: touch !important;
}
.modal.fade .modal-dialog,
.modal.in .modal-dialog {
  transform: none !important;
}
.modal-dialog {
  display: block !important;
  width: auto !important;
  max-width: min(560px, calc(100vw - 24px)) !important;
  margin: 0 auto !important;
  text-align: left !important;
}
.modal-wide {
  max-width: min(860px, calc(100vw - 24px)) !important;
}
#cf_config_modal .modal-dialog {
  max-width: min(440px, calc(100vw - 24px)) !important;
}
#cf_ns_modal .modal-dialog {
  max-width: min(480px, calc(100vw - 24px)) !important;
}
.modal-content {
  width: 100% !important;
  max-height: calc(100dvh - 76px) !important;
  display: flex !important;
  flex-direction: column !important;
  overflow: hidden !important;
}
.modal-header,
.modal-footer {
  flex: 0 0 auto !important;
}
.modal-body {
  flex: 1 1 auto !important;
  overflow-y: auto !important;
  max-height: calc(100dvh - 158px) !important;
}
.modal-footer {
  display: flex !important;
  align-items: center !important;
  justify-content: flex-end !important;
  gap: 7px !important;
  flex-wrap: wrap !important;
}
.navbar.navbar-fixed-top,
.navbar-fixed-top {
  position: fixed !important;
  top: 0 !important;
  right: 0 !important;
  left: 0 !important;
  width: 100% !important;
  max-width: 100% !important;
  margin: 0 !important;
  padding-right: 0 !important;
  z-index: 1055 !important;
}
#btn-logout,
.logout-btn {
  display: inline-flex !important;
  align-items: center !important;
  justify-content: center !important;
  gap: 6px !important;
  min-height: 31px !important;
  text-decoration: none !important;
  white-space: nowrap !important;
}
.navbar-nav > li > #btn-logout,
.navbar-nav > li > .logout-btn {
  height: 44px !important;
  padding: 12px 10px !important;
  margin: 0 !important;
  border: 0 !important;
  background: transparent !important;
  color: var(--text) !important;
}
.navbar-nav > li > #btn-logout:hover,
.navbar-nav > li > .logout-btn:hover {
  background: transparent !important;
  color: var(--text-strong) !important;
}
#btn-logout.is-loading,
.logout-btn.is-loading,
.btn.is-loading {
  pointer-events: none !important;
  opacity: .78 !important;
}
.btn-spin {
  display: inline-block !important;
  flex: 0 0 auto !important;
  vertical-align: -2px !important;
  animation: btn-spin .7s linear infinite !important;
}
@keyframes btn-spin {
  to { transform: rotate(360deg); }
}
@supports (height: 100lvh) {
  .modal,
  .modal-backdrop {
    height: 100lvh !important;
  }
  .modal-content {
    max-height: calc(100lvh - 76px) !important;
  }
  .modal-body {
    max-height: calc(100lvh - 158px) !important;
  }
}
@media screen and (max-width: 560px) {
  .modal {
    padding: 50px 8px 10px !important;
  }
  .modal-dialog,
  .modal-wide,
  #cf_config_modal .modal-dialog,
  #cf_ns_modal .modal-dialog {
    max-width: calc(100vw - 16px) !important;
  }
  .modal-content {
    max-height: calc(100dvh - 60px) !important;
  }
  .modal-body {
    max-height: calc(100dvh - 138px) !important;
  }
}
</style>

<script nonce="<?= e($nonce); ?>">
/* NGIX patch: public logout/button spinner correction */
(function() {
    'use strict';

    var spinner = '<svg class="btn-spin" viewBox="0 0 16 16" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="6" stroke-opacity=".3"></circle><path d="M8 2a6 6 0 0 1 6 6"></path></svg> ';

    function closest(node, selector) {
        return node && typeof node.closest === 'function' ? node.closest(selector) : null;
    }

    function setLoading(button, label) {
        if (!button || button.dataset.ngixLoading === '1') {
            return;
        }

        button.dataset.ngixLoading = '1';
        button.dataset.ngixOriginalHtml = button.innerHTML;
        button.classList.add('is-loading');
        button.setAttribute('aria-busy', 'true');
        button.innerHTML = spinner + label;
    }

    function ensureLogoutToast() {
        var toast = document.getElementById('logout-toast');
        if (toast) {
            return toast;
        }

        toast = document.createElement('div');
        toast.id = 'logout-toast';
        toast.textContent = 'Logging out…';
        document.body.appendChild(toast);
        return toast;
    }

    document.addEventListener('click', function(event) {
        var button = closest(event.target, '#btn-logout,.logout-btn');
        if (!button) {
            return;
        }

        var href = button.getAttribute('href') || button.getAttribute('data-href') || '?logout';
        if (href === '#' || href === '') {
            return;
        }

        event.preventDefault();
        event.stopImmediatePropagation();

        if (button.dataset.ngixSubmitting === '1') {
            return;
        }

        button.dataset.ngixSubmitting = '1';
        setLoading(button, 'Logout…');

        var toast = ensureLogoutToast();
        toast.classList.add('show');

        window.setTimeout(function() {
            window.location.href = href;
        }, 650);
    }, true);
}());
</script>

<style nonce="<?= e($nonce); ?>">
/* NGIX patch: public panel backdrop deglitch */
.panel.panel-default,
.panel:has(> .ngix-image-backdrop) {
  position: relative !important;
  overflow: hidden !important;
  isolation: isolate !important;
}
.panel.panel-default > .ngix-image-backdrop,
.panel > .ngix-image-backdrop {
  position: absolute !important;
  inset: 0 !important;
  width: 100% !important;
  min-width: 0 !important;
  max-width: 100% !important;
  height: 100% !important;
  min-height: 100% !important;
  max-height: none !important;
  z-index: 0 !important;
  pointer-events: none !important;
  background-repeat: no-repeat, no-repeat !important;
  background-position: center center, center center !important;
  background-size: 100% 100%, min(980px, 120%) auto !important;
  opacity: .24 !important;
  filter: saturate(.72) contrast(.86) blur(.18px) !important;
  transform: none !important;
}
.panel.panel-default > *:not(.ngix-image-backdrop),
.panel:has(> .ngix-image-backdrop) > *:not(.ngix-image-backdrop) {
  position: relative !important;
  z-index: 2 !important;
}
.modal,
.modal * {
  isolation: auto;
}
@media screen and (max-width: 768px) {
  .panel.panel-default > .ngix-image-backdrop,
  .panel > .ngix-image-backdrop {
    background-size: 100% 100%, min(760px, 135%) auto !important;
    opacity: .20 !important;
  }
}
</style>
</body>
</html>
