<?php

declare(strict_types=1);

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

const ADMIN_CSRF_NAMESPACE = 'admin_panel';

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

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

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

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

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

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

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

function adminHasValidCsrf(?string $token): bool
{
    $storedToken = $_SESSION['csrf'][ADMIN_CSRF_NAMESPACE] ?? null;

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

function adminRequestCsrfToken(): ?string
{
    $headerToken = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? null;
    if (is_string($headerToken) && $headerToken !== '') {
        return $headerToken;
    }

    $postedToken = $_POST['csrf_token'] ?? null;

    return is_string($postedToken) ? $postedToken : null;
}

function normalizeAddonDomain(string $value): string
{
    $domain = strtolower(trim($value));
    $domain = preg_replace('/^https?:\/\//i', '', $domain) ?? '';
    $domain = trim($domain, " \t\n\r\0\x0B./");

    if ($domain === '' || strlen($domain) > 253) {
        return '';
    }

    if (preg_match('/[\/\\\\:@?#\[\]]/', $domain) === 1) {
        return '';
    }

    if (preg_match('/^(localhost|localdomain)$/i', $domain) === 1) {
        return '';
    }

    if (filter_var($domain, FILTER_VALIDATE_IP) !== false) {
        return '';
    }

    if (preg_match('/^(?=.{1,253}$)(?!-)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$/', $domain) !== 1) {
        return '';
    }

    return $domain;
}

function normalizeCpanelRedirectDir(string $value): string
{
    $dir = trim(str_replace('\\', '/', $value));
    $dir = trim($dir, "/ \t\n\r\0\x0B");
    if ($dir === '') {
        $dir = 'public_html';
    }

    return str_ends_with(strtolower($dir), '/redirect') ? $dir : $dir . '/redirect';
}

function addondom(string $domain): array
{
    $cpanelUser        = app_required_env('CPANEL_USER');
    $cpanelPort        = (int) app_env('CPANEL_PORT', '2083');
    $cpanelHost        = app_detect_cpanel_host(app_env('CPANEL_HOST', 'localhost'), $cpanelPort);
    $cpanelSubdomainDirEnv = app_env('CPANEL_SUBDOMAIN_DIR', '');
    $cpanelSubdomainDir = normalizeCpanelRedirectDir(
        ($cpanelSubdomainDirEnv !== '' && $cpanelSubdomainDirEnv !== null)
            ? $cpanelSubdomainDirEnv
            : 'public_html',
    );
    $token             = app_env('CPANEL_API_TOKEN', '');

    $xmlApi = new xmlapi($cpanelHost);
    $xmlApi->set_port($cpanelPort);
    $xmlApi->set_output('json');
    $xmlApi->set_debug(0);

    if ($token !== '') {
        $xmlApi->token_auth($cpanelUser, $token);
    } else {
        $xmlApi->password_auth($cpanelUser, app_required_env('CPANEL_PASSWORD'));
    }

    $parkResult = $xmlApi->api2_query($cpanelUser, 'Park', 'park', [
        'domain' => $domain,
    ]);

    // Always attempt wildcard — even if park was already done previously
    $wildcardResult = $xmlApi->api2_query($cpanelUser, 'SubDomain', 'addsubdomain', [
        'domain'     => '*',
        'rootdomain' => $domain,
        'dir'        => $cpanelSubdomainDir,
    ]);

    // If wildcard failed (e.g. subdomain already exists), try removing then re-adding
    $wildcardOk = is_array($wildcardResult)
        && isset($wildcardResult['cpanelresult']['data'][0]['result'])
        && (int) $wildcardResult['cpanelresult']['data'][0]['result'] === 1;

    if (!$wildcardOk) {
        $xmlApi->api2_query($cpanelUser, 'SubDomain', 'delsubdomain', [
            'domain' => '*.' . $domain,
        ]);
        $wildcardResult = $xmlApi->api2_query($cpanelUser, 'SubDomain', 'addsubdomain', [
            'domain'     => '*',
            'rootdomain' => $domain,
            'dir'        => $cpanelSubdomainDir,
        ]);
    }

    return [
        'park'     => $parkResult,
        'wildcard' => $wildcardResult,
    ];
}

function showLoginPasswordProtect(string $errorMsg, string $nonce, string $csrfToken): never
{
    header('Location: /login.php');
    exit;
}

startAdminSession();

include_once __DIR__ . '/../password.login.php';
include_once __DIR__ . '/../xmlapi.php';
/** @var PDO $pdo */
$pdo = require __DIR__ . '/../../connection_pdo.php';
include_once dirname(__DIR__, 2) . '/env.php';
load_env_file(dirname(__DIR__, 2) . '/.env');

$nonce = base64_encode(random_bytes(18));
$csrfToken = adminCsrfToken();

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');
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}'; "
    . "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 (isset($_GET['logout'])) {
    unset($_SESSION['admin_authenticated']);
    session_regenerate_id(true);
    header('Location: /login.php');
    exit;
}

if (empty($_SESSION['admin_authenticated'])) {
    showLoginPasswordProtect('', $nonce, $csrfToken);
}

if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'cpanel_delete') {
    header('Content-Type: application/json; charset=utf-8');

    if (!adminHasValidCsrf(adminRequestCsrfToken())) {
        http_response_code(419);
        echo json_encode(['ok' => false, 'err' => 'invalid-csrf']);
        exit;
    }

    $domain = normalizeAddonDomain((string) ($_POST['domain'] ?? ''));
    if ($domain === '') {
        http_response_code(422);
        echo json_encode(['ok' => false, 'err' => 'invalid-domain']);
        exit;
    }

    try {
        $cpanelUser        = app_required_env('CPANEL_USER');
        $cpanelPort        = (int) app_env('CPANEL_PORT', '2083');
        $cpanelHost        = app_detect_cpanel_host(app_env('CPANEL_HOST', 'localhost'), $cpanelPort);
        $token             = app_env('CPANEL_API_TOKEN', '');

        $xmlApi = new xmlapi($cpanelHost);
        $xmlApi->set_port($cpanelPort);
        $xmlApi->set_output('json');
        $xmlApi->set_debug(0);

        if ($token !== '') {
            $xmlApi->token_auth($cpanelUser, $token);
        } else {
            $xmlApi->password_auth($cpanelUser, app_required_env('CPANEL_PASSWORD'));
        }

        $delWildcard = $xmlApi->api2_query($cpanelUser, 'SubDomain', 'delsubdomain', [
            'domain' => '*.' . $domain,
        ]);
        $unpark = $xmlApi->api2_query($cpanelUser, 'Park', 'unpark', [
            'domain' => $domain,
        ]);

        echo json_encode(['ok' => true, 'domain' => $domain, 'del_wildcard' => $delWildcard, 'unpark' => $unpark]);
    } catch (Throwable $e) {
        http_response_code(500);
        echo json_encode(['ok' => false, 'err' => 'cpanel-request-failed']);
    }
    exit;
}

if (
    $_SERVER['REQUEST_METHOD'] === 'POST'
    && (
        (isset($_POST['action']) && $_POST['action'] === 'cpanel_add')
        || isset($_POST['url'])
    )
) {
    header('Content-Type: application/json; charset=utf-8');

    if (!adminHasValidCsrf(adminRequestCsrfToken())) {
        http_response_code(419);
        echo json_encode(['ok' => false, 'err' => 'invalid-csrf']);
        exit;
    }

    $domainInput = (string) ($_POST['domain'] ?? ($_POST['url'] ?? ''));
    $domain = normalizeAddonDomain($domainInput);
    if ($domain === '') {
        http_response_code(422);
        echo json_encode(['ok' => false, 'err' => 'invalid-domain']);
        exit;
    }

    try {
        echo json_encode([
            'ok' => true,
            'domain' => $domain,
            'addondom' => addondom($domain),
        ]);
    } catch (Throwable $e) {
        http_response_code(500);
        echo json_encode(['ok' => false, 'err' => 'cpanel-request-failed']);
    }
    exit;
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>ADMIN PANEL</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="csrf-token" content="<?= adminEsc($csrfToken); ?>">
    <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">
    <link href="../dist/flags.css" rel="stylesheet">
    <style nonce="<?= adminEsc($nonce); ?>">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}.app-sign{text-align:right;padding:3px 0 2px}.app-sign footer{font-family:'Caveat','Dancing Script',cursive;font-size:14px;font-weight:600;color:#444746;text-decoration:none;letter-spacing:.02em;line-height:1;opacity:.85}input[type=url]{font-family:Consolas,Monaco,"Courier New",monospace!important}</style>

    <style nonce="<?= adminEsc($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);--inverse:var(--strong);--faded:var(--bg);--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{background:var(--bg);zoom:1.1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-rendering:optimizeLegibility}body{font-family:var(--font);padding:58px 8px 18px!important;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!important;color:var(--text)!important;font-family:monospace!important;font-size:13px;line-height:1.45;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-rendering:optimizeLegibility}a{color:inherit;text-decoration:none}.container{width:100%;max-width:1180px;margin:0 auto}code{font-family:Consolas,Monaco,'Courier New',monospace;font-size:11.5px;color:var(--text);background:var(--panel-soft);border:1px solid var(--line-soft);border-radius:var(--radius);padding:1px 4px}.navbar.navbar-default{min-height:44px;border:0!important;border-bottom:0!important;background:#fff!important;box-shadow:var(--shadow)!important}.navbar .container{max-width:1180px}.navbar-brand{height:44px!important;padding:12px 10px!important;color:var(--text-strong)!important;font-size:13px;line-height:20px}.navbar-nav>li>a{padding-top:12px!important;padding-bottom:12px!important;color:var(--text)!important;font-size:12px}.navbar-nav>li>a:hover,.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{background:var(--accent)!important;color:var(--on-accent)!important}.navbar-toggle{margin-top:5px!important;margin-bottom:5px!important;border-color:var(--line)!important;border-radius:var(--radius)!important;background:var(--panel)!important}.navbar-toggle .icon-bar{background:var(--text)!important}.panel,.panel-default,.well,.modal-content{border: 1px solid var(--line) !important;border-radius:var(--radius)!important;background:var(--panel)!important;color:var(--text)!important;box-shadow:var(--shadow)!important;overflow:hidden}.panel-heading,.panel-footer,.modal-header,.modal-footer{padding:8px 10px!important;background:#fff!important;border:0!important;color:var(--text-strong)!important}.panel-body,.modal-body{padding:10px!important;background:#fff!important;color:var(--text)!important}.pull-right{display:flex;align-items:center;gap:6px}.modal-content{box-shadow:var(--shadow-modal)!important}.modal-title{font-size:14px;font-weight:700;color:var(--text-strong)}.close{color:var(--text)!important;opacity:.78;text-shadow:none}.form-group{margin-bottom:8px}.control-label,label{margin-bottom:4px;color:var(--text-strong);font-size:12px}.form-control,input[type=text],input[type=number],input[type=password],input[type=search],select,textarea{height:31px;min-height:31px;border:1px solid var(--line)!important;border-radius:var(--radius)!important;background:var(--panel)!important;color:var(--text)!important;font-family:monospace!important;font-size:13px!important;line-height:1.4!important;box-shadow:none!important; }.form-control:focus,input:focus,select:focus,textarea:focus{outline:none;border-color:var(--line)!important;box-shadow:none!important}.form-control::placeholder,textarea::placeholder{color:color-mix(in srgb,var(--text) 54%,transparent)}.form-control[readonly],input[readonly],textarea[readonly]{background:var(--panel-soft)!important;color:var(--text)!important}textarea.form-control{height:auto;min-height:114px;resize:none}.input-group-addon{height:31px;padding:5px 8px;background:transparent!important;border-color:var(--line)!important;color:var(--text)!important;border-radius:var(--radius)!important;font-size:11.5px}.btn,button,.btn-sm,.btn-sm{display:inline-flex;align-items:center;justify-content:center;gap:5px;border:1px solid var(--line)!important;border-radius:var(--radius)!important;background:var(--panel)!important;color:var(--text)!important;font-size:11.5px;font-weight:700;line-height:1.2;box-shadow:none!important;outline:none;transition:background .12s ease,border-color .12s ease,color .12s ease}.btn:hover,button:hover,.btn-sm:hover,.btn-sm:hover{background:var(--panel-soft)!important;border-color:var(--line)!important;color:var(--text-strong)!important}.btn-primary,.btn-primary:focus,.btn-primary:active,.btn.active{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-danger,.btn-danger:focus{background:var(--danger-soft)!important;border-color:rgba(108,117,125,.34)!important;color:var(--danger)!important}.btn-danger:hover{background:rgba(108,117,125,.16)!important;color:var(--danger)!important}.glyphicon{top:1px;color:currentColor}.action-ico{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0}.action-ico svg{display:block}.table-responsive{border:0!important}.table{width:100%;margin:4px 0 0;border-collapse:separate;border-spacing:0;border:0;border-radius:var(--radius);overflow:hidden;background:var(--panel)!important}.table>thead>tr>th{position:sticky;top:0;z-index:20;padding:7px 8px!important;border-bottom:1px solid var(--line)!important;background:#f5f5f5!important;color:var(--text-strong)!important;font-size:10.5px!important;line-height:1.2;text-transform:uppercase;letter-spacing:.035em;white-space:nowrap}.table>tbody>tr>td{padding:6px 8px!important;border-top:1px solid var(--line-soft)!important;color:var(--text);font-size:11.5px;vertical-align:middle;word-break:break-word;background:var(--panel)!important}.table-hover>tbody>tr:hover>td{background:var(--panel-soft)!important}.bootgrid-table th>.column-header-anchor{color:var(--text-strong)!important}.bootgrid-header,.bootgrid-footer{margin:6px 0!important;color:var(--text)!important}.bootgrid-header .search .form-control{height:31px}.bootgrid-header .actionBar{text-align:right}.pagination>li>a,.pagination>li>span{border-color:var(--line)!important;background:var(--panel)!important;color:var(--text)!important}.pagination>.active>a,.pagination>.active>span{background:var(--accent)!important;border-color:var(--accent)!important;color:var(--on-accent)!important}.dropdown-menu{border:0!important;border-radius:var(--radius)!important;background:#fff!important;box-shadow:var(--shadow-modal)!important}.dropdown-menu>li>a{color:var(--text)!important;font-size:12px}.dropdown-menu>li>a:hover{background:var(--panel-soft)!important;color:var(--text-strong)!important}.alert{border-radius:var(--radius)!important}.alert-danger,.error{background:var(--danger-soft)!important;border-color:rgba(108,117,125,.28)!important;color:var(--danger)!important}.alert-success,.success{background:var(--blue-soft)!important;border-color:var(--blue-line)!important;color:var(--text)!important}.label{border-radius:var(--radius)!important}.label-default{background:var(--panel-soft)!important;color:var(--text)!important;border:1px solid var(--line)}blockquote{margin:0 0 10px;padding:7px 10px;border-left:2px solid var(--line)!important;background:var(--panel-fade);border-radius:var(--radius);font-size:12px}.text-warning{margin:0 0 2px;color:var(--text-strong)!important;font-weight:700}.text-muted{color:var(--text)!important}.flag{border-radius:var(--radius)}.admin-toast-root{position:fixed;right:14px;bottom:14px;z-index:10050;display:flex;flex-direction:column;gap:5px;align-items:flex-end}.admin-toast{font-family:var(--mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);max-width:300px;min-width:160px;padding:9px 12px;border:1px solid var(--line);border-left:3px solid var(--accent);border-radius:var(--radius);background:var(--panel2);color:var(--text);box-shadow:0 2px 12px rgba(0,0,0,.10);font-size:12px;line-height:1.4;animation:_t-in .16s ease both}.admin-toast strong{display:block;font-weight:700;color:var(--text-strong);margin-bottom:1px}.admin-toast--error{border-left-color:var(--danger);background:var(--danger-soft);color:var(--danger)}.admin-toast--error strong{color:var(--danger)}.admin-toast--success{border-left-color:var(--accent);background:var(--panel2)}.admin-toast--info{border-left-color:var(--muted)}.admin-toast--removing{animation:_t-out .16s ease both!important}@keyframes _t-in{from{opacity:0;transform:translateX(12px)}to{opacity:1;transform:translateX(0)}}@keyframes _t-out{to{opacity:0;transform:translateX(12px)}}.admin-fallback-backdrop{position:fixed;inset:0;z-index:10040;display:flex;align-items:center;justify-content:center;padding:12px;background:rgba(0,0,0,.36)}.admin-fallback-dialog{width:min(420px,100%);border:1px solid var(--line);border-radius:var(--radius);background:#fff;color:var(--text);box-shadow:var(--shadow-modal);overflow:hidden}.admin-fallback-head{padding:10px;border-bottom:1px solid var(--line-soft);font-weight:800;color:var(--text-strong);background:#fff}.admin-fallback-body{padding:10px;font-size:12px;background:#fff}.admin-fallback-actions{display:flex;gap:6px;justify-content:flex-end;padding:10px;border-top:1px solid var(--line-soft);background:#fff}@media screen and (max-width:768px){body{padding:58px 6px 12px!important;font-size:12.5px}.container{width:100%!important;max-width:100%!important;padding-left:6px!important;padding-right:6px!important}.panel-heading,.panel-body,.panel-footer{padding:7px!important;background:#fff!important}.navbar .container{padding-left:8px!important;padding-right:8px!important}.navbar-collapse{border-color:var(--line)!important;background:var(--panel)!important}.pull-right{float:none!important;justify-content:flex-end}.input-group{display:block}.input-group>.form-control,.input-group>.input-group-addon,.input-group>.input-group-btn,.input-group-btn>.btn{display:block;width:100%!important}.input-group-addon{border-bottom:0!important;border-radius:var(--radius) var(--radius) 0 0!important}.input-group>.form-control{border-radius:0 0 var(--radius) var(--radius)!important}.input-group-btn>.btn{margin-top:5px}.table{display:block;overflow-x:auto;white-space:nowrap}.modal-dialog{width:auto!important;margin:8px}}#tbl_domains{table-layout:fixed;width:100%;min-width:820px}#tbl_domains th:nth-child(1),#tbl_domains td:nth-child(1){width:50px;text-align:center;padding-left:4px!important;padding-right:4px!important}#tbl_domains th:nth-child(2),#tbl_domains td:nth-child(2){width:130px}#tbl_domains th:nth-child(4),#tbl_domains td:nth-child(4){width:140px}.td-num{display:block;font-size:11.5px;font-weight:600;font-family:Consolas,Monaco,monospace;color:color-mix(in srgb,var(--text) 44%,transparent);text-align:center}.td-subdomain{display:inline-block;padding:2px 8px;border-radius:.3rem!important;width:100%;border:1px solid var(--line);background:var(--panel-soft);font-family:Consolas,Monaco,monospace;font-size:11.5px;font-weight:700;letter-spacing:.03em;white-space:nowrap}.td-domain{display:block;max-width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-family:Consolas,Monaco,monospace;font-size:11.5px;font-weight:600}.mono-input{font-family:Consolas,Monaco,'Courier New',monospace!important;font-size:12px!important}.ns-card footer{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.ns-card code{background:transparent;border:0;padding:0;color:var(--text-strong)}.cf-badge{display:inline-flex;align-items:center;gap:4px;padding:2px 7px;border-radius:20px;font-size:10.5px;font-weight:600;letter-spacing:.02em;border:1px solid var(--line);background:var(--panel-soft);color:var(--text)}.cf-badge::before{content:'';display:inline-block;width:7px;height:7px;border-radius:50%;background:currentColor;opacity:.5}.cf-badge--active{border-radius:.3rem!important;width:100%;border-color:rgba(38,121,78,.3);color:#6c757d}.cf-badge--active::before{opacity:1}.cf-badge--pending{border-radius:.3rem!important;width:100%;border-color:var(--line);color:var(--text)}.cf-badge--pending::before{opacity:1}.cf-badge--moved{border-radius:.3rem!important;width:100%;border-color:var(--line);color:var(--text)}.cf-badge--moved::before{opacity:1}.cf-badge--none{border-radius:.3rem!important;width:100%;opacity:.62}.btn-cf{display:inline-flex!important;align-items:center!important;justify-content:center!important;width:28px!important;height:26px!important;padding:0!important;margin:0 1px!important;border:1px solid var(--line)!important;border-radius:var(--radius)!important;background:var(--panel)!important;color:var(--text)!important;font-size:0!important;line-height:1!important;vertical-align:middle!important;cursor:pointer!important}.btn-cf:hover{background:var(--panel-soft)!important;border-color:var(--line)!important;color:var(--text)!important}.btn-cf svg{display:block!important;width:14px!important;height:14px!important;pointer-events:none!important}.cf-badge--moved{color:#6c757d}@keyframes cf-spin{to{transform:rotate(360deg)}}@keyframes cf-pulse{0%,100%{opacity:.8}50%{opacity:.35}}.btn-cf--loading{animation:cf-pulse .75s ease-in-out infinite!important;cursor:not-allowed!important;pointer-events:none!important}.btn-cf--loading svg{animation:cf-spin .65s linear infinite}.btn-syncall--loading svg{animation:cf-spin .7s linear infinite}
    /* bootgrid toolbar search/select fix */.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{background:color-mix(in srgb,var(--accent) 5%,transparent)!important;color:var(--text-strong)!important;box-shadow:none!important}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{background:transparent!important;color:var(--text-strong)!important;box-shadow:none!important;border-bottom:2px solid var(--accent)!important;padding-bottom:10px!important}.panel,.panel-default{overflow:visible!important}.panel-body{overflow:visible!important}.table-responsive{overflow-x:auto!important;overflow-y:visible!important}.bootgrid-header{position:relative!important;z-index:80!important;display:block!important;margin:8px 0 7px!important;color:var(--text)!important}.bootgrid-header .actionBar{display:flex!important;align-items:center!important;justify-content:flex-end!important;gap:8px!important;float:none!important;width:100%!important;margin:0!important;text-align:right!important;white-space:nowrap!important}.bootgrid-header .search{position:relative!important;display:inline-flex!important;align-items:center!important;float:none!important;width:220px!important;min-width:220px!important;max-width:260px!important;margin:0!important;vertical-align:middle!important}.bootgrid-header .search .input-group{display:flex!important;align-items:stretch!important;width:100%!important;border-collapse:separate!important}.bootgrid-header .search .input-group-addon{display:flex!important;align-items:center!important;justify-content:center!important;flex:0 0 34px!important;width:34px!important;min-width:34px!important;height:32px!important;padding:0!important;border:1px solid var(--line)!important;border-right:0!important;border-radius:var(--radius) 0 0 var(--radius)!important;background:var(--panel-soft)!important;color:var(--text)!important;line-height:1!important}.bootgrid-header .search .input-group-addon .glyphicon{top:0!important;font-size:12px!important}.bootgrid-header .search .search-field,.bootgrid-header .search .form-control{display:block!important;flex:1 1 auto!important;width:100%!important;height:32px!important;min-height:32px!important;padding:5px 9px!important;border:1px solid var(--line)!important;border-left:0!important;border-radius:0 var(--radius) var(--radius) 0!important;background:var(--panel)!important;color:var(--text)!important;font-size:12px!important;line-height:1.4!important;box-shadow:none!important}.bootgrid-header .search .search-field:focus,.bootgrid-header .search .form-control:focus{border-color:var(--line)!important;border-left:0!important;box-shadow:none!important}.bootgrid-header .actions{position:relative!important;display:inline-flex!important;align-items:center!important;gap:6px!important;float:none!important;margin:0!important;vertical-align:middle!important}.bootgrid-header .actions>.btn-group{position:relative!important;display:inline-flex!important;float:none!important;margin:0!important;vertical-align:middle!important}.bootgrid-header .actions .btn,.bootgrid-header .actions .dropdown-toggle{display:inline-flex!important;align-items:center!important;justify-content:center!important;gap:6px!important;height:32px!important;min-width:38px!important;padding:5px 10px!important;line-height:1.2!important;border-radius:var(--radius)!important}.bootgrid-header .actions .dropdown-toggle{min-width:64px!important}.bootgrid-header .actions .dropdown-toggle .caret{margin-left:2px!important}.bootgrid-header .actions .dropdown-menu{position:absolute!important;top:calc(100% + 4px)!important;right:0!important;left:auto!important;z-index:3000!important;display:none;min-width:112px!important;width:112px!important;margin:0!important;padding:4px!important;border:1px solid var(--line)!important;border-radius:var(--radius)!important;background:#fff!important;box-shadow:var(--shadow-modal)!important;list-style:none!important}.bootgrid-header .actions .open>.dropdown-menu{display:block!important}.bootgrid-header .actions .dropdown-menu>li{display:block!important;float:none!important;width:100%!important;margin:0!important;padding:0!important;text-align:left!important}.bootgrid-header .actions .dropdown-menu>li>a{display:block!important;width:100%!important;min-width:0!important;padding:6px 9px!important;border-radius:var(--radius)!important;background:transparent!important;color:var(--text)!important;font-size:12px!important;line-height:1.25!important;text-align:left!important;white-space:nowrap!important}.bootgrid-header .actions .dropdown-menu>li>a:hover,.bootgrid-header .actions .dropdown-menu>li>a:focus{background:var(--panel-soft)!important;color:var(--text-strong)!important}.bootgrid-header .actions .dropdown-menu>.active>a,.bootgrid-header .actions .dropdown-menu>.active>a:hover,.bootgrid-header .actions .dropdown-menu>.active>a:focus{background:var(--accent-soft)!important;color:var(--text-strong)!important;box-shadow:none!important}.bootgrid-footer{position:relative!important;z-index:20!important}.bootgrid-footer .pagination{margin:0!important}@media screen and (max-width:768px){.bootgrid-header .actionBar{align-items:stretch!important;justify-content:stretch!important;flex-wrap:wrap!important;gap:6px!important}.bootgrid-header .search{width:100%!important;min-width:0!important;max-width:none!important;flex:1 0 100%!important}.bootgrid-header .actions{margin-left:auto!important}.bootgrid-header .actions .dropdown-menu{right:0!important;left:auto!important}}
    input:focus,textarea:focus,select:focus,button:focus,.form-control:focus,.btn:focus,a:focus{outline:none!important}
/* ngix xctd image backdrop */
.panel.panel-default:has(> .ngix-image-backdrop){position:relative!important;overflow:hidden!important;isolation:isolate!important;background:rgba(255,255,255,.74)!important}
.panel.panel-default>.ngix-image-backdrop{position:absolute!important;inset:0!important;z-index:0!important;pointer-events:none!important;background-image:linear-gradient(rgba(255,255,255,.82),rgba(255,255,255,.82)),url('/dist/ngix-backdrop.png')!important;background-repeat:no-repeat,no-repeat!important;background-position:center center,center center!important;background-size:100% 100%,cover!important;opacity:.42!important;filter:saturate(.72) contrast(.86) blur(.22px)!important;transform:translateZ(0)!important}
.panel.panel-default:has(> .ngix-image-backdrop)>*:not(.ngix-image-backdrop){position:relative!important;}
.panel.panel-default:has(> .ngix-image-backdrop)>.tbl-toolbar,.panel.panel-default:has(> .ngix-image-backdrop)>.tbl-footer{background:rgba(245,245,245,.82)!important}
.panel.panel-default:has(> .ngix-image-backdrop)>.tbl-toolbar{backdrop-filter:saturate(110%) blur(1px)!important}
.panel.panel-default:has(> .ngix-image-backdrop) .table{position:relative!important;z-index:3!important;background:rgba(255,255,255,.46)!important}
.panel.panel-default:has(> .ngix-image-backdrop) .table>thead>tr>th{background:rgba(245,245,245,.86)!important}
.panel.panel-default:has(> .ngix-image-backdrop) .table>tbody>tr>td{background:rgba(255,255,255,.30)!important}
.panel.panel-default:has(> .ngix-image-backdrop) .table.table-hover>tbody>tr:hover>td{background:rgba(245,245,245,.72)!important}
@media screen and (max-width:768px){.panel.panel-default>.ngix-image-backdrop{background-size:100% 100%,cover!important;background-position:center center,center center!important;opacity:.34!important}}</style>
    <style nonce="<?= adminEsc($nonce); ?>">/* final action icon render fix */#tbl_domains th:last-child,#tbl_domains td:last-child{text-align:right!important;white-space:nowrap!important;width:184px!important;min-width:184px!important}.bootgrid-table td:last-child{overflow:visible!important}.action-btn,.bootgrid-table .command-edit,.bootgrid-table .command-delete{display:inline-flex!important;align-items:center!important;justify-content:center!important;width:32px!important;height:26px!important;min-width:32px!important;min-height:26px!important;padding:0!important;margin:0 2px!important;border:1px solid var(--line)!important;border-radius:var(--radius)!important;background:var(--panel)!important;color:var(--text)!important;font-size:0!important;line-height:1!important;text-indent:0!important;vertical-align:middle!important;opacity:1!important;visibility:visible!important;box-shadow:none!important;overflow:visible!important}.action-btn:hover,.bootgrid-table .command-edit:hover{background:var(--accent-soft)!important;border-color:var(--line)!important;color:var(--text-strong)!important}.bootgrid-table .command-delete{color:var(--danger)!important}.bootgrid-table .command-delete:hover{background:var(--danger-soft)!important;border-color:color-mix(in srgb,var(--danger) 34%,var(--line))!important;color:var(--danger)!important}.bootgrid-table .command-edit[disabled],.bootgrid-table .command-delete[disabled]{opacity:.58!important;cursor:not-allowed!important}.action-btn .action-ico,.bootgrid-table .command-edit .action-ico,.bootgrid-table .command-delete .action-ico{display:inline-flex!important;align-items:center!important;justify-content:center!important;width:16px!important;height:16px!important;min-width:16px!important;min-height:16px!important;color:currentColor!important;opacity:1!important;visibility:visible!important;overflow:visible!important;pointer-events:none!important}.action-btn svg,.bootgrid-table .command-edit svg,.bootgrid-table .command-delete svg{display:block!important;width:16px!important;height:16px!important;min-width:16px!important;min-height:16px!important;overflow:visible!important;color:currentColor!important;opacity:1!important;visibility:visible!important;pointer-events:none!important}.action-btn svg *,.bootgrid-table .command-edit svg *,.bootgrid-table .command-delete svg *{vector-effect:non-scaling-stroke!important;stroke:currentColor!important;stroke-width:2!important;stroke-linecap:round!important;stroke-linejoin:round!important;fill:none!important;opacity:1!important;visibility:visible!important}.btn-icon svg,.btn-primary svg{display:block!important;width:13px!important;height:13px!important;fill:currentColor!important;color:currentColor!important;opacity:1!important;visibility:visible!important}input:focus,textarea:focus,select:focus,button:focus,.form-control:focus,.btn:focus,a:focus{outline:none!important}.cf-toggle-row{margin:8px 0 0;padding:7px 0 0;border-top:1px solid var(--line-soft)}.cf-toggle-label{display:flex!important;align-items:center;gap:8px;cursor:pointer;user-select:none;font-size:12px;font-weight:400;margin:0;color:var(--text)}.cf-switch{position:relative;display:inline-block;width:34px;height:18px;flex-shrink:0}.cf-switch input{opacity:0;width:0;height:0;position:absolute}.cf-switch-track{position:absolute;inset:0;border-radius:9px;background:var(--line);transition:background .15s;cursor:pointer}.cf-switch-track::before{content:'';position:absolute;width:12px;height:12px;left:3px;top:3px;border-radius:50%;background:var(--panel);transition:transform .15s;box-shadow:none}.cf-switch input:checked~.cf-switch-track{background:var(--accent)}.cf-switch input:checked~.cf-switch-track::before{transform:translateX(16px)}.cf-permission-head{display:flex;align-items:center;justify-content:space-between;gap:8px}.cf-permission-toggle{height:24px;padding:3px 8px!important;font-size:10.5px!important;white-space:nowrap}.cf-permission-body{display:none;margin-top:7px}.cf-permission-body.is-open{display:block;max-height:192px;overflow-y:auto}.cf-permission-panel{margin:-4px 0 12px;padding:9px 10px;border:1px solid var(--line-soft);border-radius:var(--radius);background:var(--panel-soft);font-size:11px;line-height:1.6}.cf-permission-title{font-weight:700;margin-bottom:5px;color:var(--text-strong)}.cf-permission-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}</style>
    <style nonce="<?= adminEsc($nonce); ?>">
    .table>thead>tr>th{font-family:var(--font-mono-old)!important;font-size:10.5px!important}.table>tbody>tr>td{font-family:var(--font-mono-old)!important;font-size:12px!important}.form-control,input,select,textarea,.btn,button{font-family:var(--font-mono-old)!important;font-size:11.5px!important}
    .table{width:100%;border-collapse:collapse;font-size:12.5px;margin:0!important;border:0!important;border-radius:0!important;background:transparent!important}
    .table>thead>tr>th{height:34px;padding:0 12px!important;text-align:left;font-size:11px;font-weight:600;color:var(--text-strong);text-transform:uppercase;letter-spacing:.06em;border-bottom:1px solid var(--line)!important;border-top:0!important;background:var(--panel-soft)!important;white-space:nowrap;position:static}
    .table>tbody>tr>td{padding:6px 12px!important;vertical-align:middle;border-bottom:1px solid var(--line-soft)!important;border-top:0!important;font-size:12.5px;font-weight:400;color:var(--text)!important;word-break:break-word;background:transparent!important}
    .table>tbody>tr:last-child>td{border-bottom:0!important}
    .table-hover>tbody>tr:hover>td{background:var(--panel-soft)!important}
    .table-striped>tbody>tr:nth-of-type(odd)>td{background:transparent!important}
    .tbl-toolbar{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:8px 12px;flex-wrap:wrap;border-bottom:1px solid #eee;background:#f5f5f5}
    .tbl-toolbar-left{display:flex;align-items:center;gap:6px}
    .tbl-footer{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:8px 12px;border-top:1px solid #eee;font-size:12px;color:var(--text);flex-wrap:wrap;background:#f5f5f5}.pagination-compact{margin:0!important}.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}.ddt-full{display:block;width:100%}.ddt-full .ddt-btn{width:100%!important;min-width:0!important;justify-content:space-between!important;font-weight:400!important;font-size:13px!important;background:var(--panel)!important;border-color:var(--line)!important;color:var(--text)!important}.ddt-full .ddt-menu{width:100%;max-height:210px;overflow-y:auto}.ddt-placeholder .ddt-label{color:color-mix(in srgb,var(--text) 54%,transparent)!important}
    .form-control:focus,input:focus,select:focus,textarea:focus{outline:none;color:var(--text-strong)!important}
    .panel,.panel-default{box-shadow:none!important}
    .modal-content{box-shadow:none!important;overflow:visible!important}
    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}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>
<script nonce="<?= adminEsc($nonce); ?>" src="../dist/jquery-1.11.1.min.js"></script>
<script nonce="<?= adminEsc($nonce); ?>" src="../dist/bootstrap.min.js"></script>
<script nonce="<?= adminEsc($nonce); ?>">
(function ($) {
    'use strict';

    $.ajaxSetup({
        headers: {
            'X-CSRF-Token': <?= json_encode($csrfToken, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT); ?>,
            'X-CSRF-Context': 'admin_panel'
        }
    });
}(jQuery));
</script>
<script nonce="<?= adminEsc($nonce); ?>">
(function (window, document) {
    'use strict';

    function ensureToastRoot() {
        var root = document.querySelector('.admin-toast-root');
        if (root) {
            return root;
        }
        root = document.createElement('div');
        root.className = 'admin-toast-root';
        root.setAttribute('aria-live', 'polite');
        document.body.appendChild(root);
        return root;
    }

    function showToast(type, title, text) {
        var root = ensureToastRoot();
        var item = document.createElement('div');
        var normalizedType = type === 'error' ? 'error' : (type === 'success' ? 'success' : 'info');
        item.className = 'admin-toast admin-toast--' + normalizedType;
        if (title) {
            var strong = document.createElement('strong');
            strong.textContent = title;
            item.appendChild(strong);
        }
        if (text) {
            var span = document.createElement('span');
            span.textContent = text;
            item.appendChild(span);
        }
        root.appendChild(item);
        window.setTimeout(function () {
            item.classList.add('admin-toast--removing');
            window.setTimeout(function () {
                if (item.parentNode) item.parentNode.removeChild(item);
            }, 180);
        }, 2600);
    }

    function confirmDialog(options) {
        return new Promise(function (resolve) {
            var backdrop = document.createElement('div');
            backdrop.className = 'admin-fallback-backdrop';

            var dialog = document.createElement('div');
            dialog.className = 'admin-fallback-dialog';

            var head = document.createElement('div');
            head.className = 'admin-fallback-head';
            head.textContent = options.title || 'Confirm action';

            var body = document.createElement('div');
            body.className = 'admin-fallback-body';
            body.textContent = options.text || 'Continue?';

            var actions = document.createElement('div');
            actions.className = 'admin-fallback-actions';

            var cancel = document.createElement('button');
            cancel.type = 'button';
            cancel.className = 'btn btn-default btn-sm';
            cancel.textContent = options.cancelButtonText || 'Cancel';

            var ok = document.createElement('button');
            ok.type = 'button';
            ok.className = 'btn btn-primary btn-sm';
            ok.textContent = options.confirmButtonText || 'OK';

            function close(value) {
                if (backdrop.parentNode) {
                    backdrop.parentNode.removeChild(backdrop);
                }
                resolve({ value: value, isConfirmed: value });
            }

            cancel.addEventListener('click', function () {
                close(false);
            });
            ok.addEventListener('click', function () {
                close(true);
            });

            actions.appendChild(cancel);
            actions.appendChild(ok);
            dialog.appendChild(head);
            dialog.appendChild(body);
            dialog.appendChild(actions);
            backdrop.appendChild(dialog);
            document.body.appendChild(backdrop);
            ok.focus();
        });
    }

    window.showToast = showToast;

    if (!window.Swal || typeof window.Swal.fire !== 'function') {
        window.Swal = {
            fire: function (argOne, argTwo, argThree) {
                var options = typeof argOne === 'object' ? argOne : {
                    title: String(argOne || ''),
                    text: String(argTwo || ''),
                    type: String(argThree || 'info')
                };

                if (options.showCancelButton) {
                    return confirmDialog(options);
                }

                showToast(options.type || options.icon || 'info', options.title || '', options.text || '');
                return {
                    then: function (callback) {
                        if (typeof callback === 'function') {
                            window.setTimeout(function () {
                                callback({ value: true, isConfirmed: true });
                            }, 0);
                        }
                        return this;
                    }
                };
            }
        };
    }
}(window, document));
</script>

<div role="navigation" class="navbar navbar-default navbar-fixed-top">
        <div class="container">
            <div class="navbar-header">
                <button data-target=".navbar-collapse" data-toggle="collapse" class="navbar-toggle" type="button">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a href="#" class="navbar-brand"><strong>Admin Panel</strong></a>
            </div>
            <div class="navbar-collapse collapse navbar-right">
                <ul class="nav navbar-nav">
                    <li><a href="/dashboard/"><strong>Dashboard</strong></a></li>
                    <li><a href="/campaigns/"><strong>Campaigns</strong></a></li>
                    <li class="active"><a href="#"><strong>Addon Domain</strong></a></li>
                    <li><a href="?logout" id="btn-logout" class="logout-btn"><strong>Logout</strong></a></li>
                </ul>
            </div>
            <!--/.nav-collapse -->
        </div>
    </div>

        <div class="container">
            <div class="panel panel-default">
                <div class="ngix-image-backdrop" aria-hidden="true"></div>
                <div class="tbl-toolbar">
                    <div class="tbl-toolbar-left">
                        <div class="input-group" style="width:200px;">
                            <span class="input-group-addon"><svg viewBox="0 0 24 24" width="13" height="13" stroke="currentColor" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="11" r="7"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg></span>
                            <input type="text" class="form-control" id="tbl-search" placeholder="Search...">
                        </div>
                        <div class="ddt" id="ddt-rowcount">
                            <button type="button" class="btn btn-sm ddt-btn" id="ddt-rowcount-btn"><span class="ddt-label">25</span><svg class="ddt-caret" viewBox="0 0 10 6" width="10" height="6" fill="currentColor" aria-hidden="true" focusable="false"><path d="M0 0l5 6 5-6z"/></svg></button>
                            <ul class="ddt-menu" id="ddt-rowcount-menu">
                                <li><a href="#" data-val="25" class="ddt-active">25</a></li>
                                <li><a href="#" data-val="50">50</a></li>
                                <li><a href="#" data-val="-1">All</a></li>
                            </ul>
                        </div>
                        <input type="hidden" id="tbl-rowcount" value="25">
                    </div>
                    <div style="display:inline-flex;gap:6px;align-items:center;">
                    <button type="button" class="btn btn-sm btn-default btn-icon" id="command-cf-config" title="Cloudflare Configuration">
                        <svg viewBox="0 0 24 24" width="13" height="13" stroke="currentColor" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="3"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14M4.93 4.93a10 10 0 0 0 0 14.14"/><path d="M15.54 8.46a5 5 0 0 1 0 7.07M8.46 8.46a5 5 0 0 0 0 7.07"/></svg> CF Config</button>
                    <button type="button" class="btn btn-sm btn-default btn-icon" id="command-sync-all" title="Sync all domains to Cloudflare">
                        <svg viewBox="0 0 24 24" width="13" height="13" stroke="currentColor" fill="none" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M21.5 2v6h-6"/><path d="M2.5 12A10 10 0 0 1 20 6.3"/><path d="M2.5 22v-6h6"/><path d="M21.5 12A10 10 0 0 1 4 17.7"/></svg> Sync All CF</button>
                    <button type="button" class="btn btn-sm btn-primary btn-icon" id="command-add" data-row-id="0">
                        <svg viewBox="0 0 24 24" width="13" height="13" stroke="currentColor" fill="none" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M12 5v14"/><path d="M5 12h14"/></svg> Addon Domain</button>
                    </div>
                </div>
                <div style="overflow-x:auto;">
                    <table id="tbl_domains" class="table table-hover">
                        <thead>
                            <tr>
                                <th>Empid</th>
                                <th>Domain Title</th>
                                <th>Domain Name</th>
                                <th>CF Status</th>
                                <th style="text-align:right;">Action</th>
                            </tr>
                        </thead>
                        <tbody></tbody>
                    </table>
                </div>
                <div class="tbl-footer">
                    <span id="tbl-info"></span>
                    <ul class="pagination pagination-compact" id="tbl-pagination"></ul>
                </div>
            </div>
            <div class="app-sign"><footer>Ngix · xctd</footer></div>
        </div>
        <div id="add_model" class="modal fade" data-keyboard="false" data-backdrop="static">
            <div class="modal-dialog">
                <div class="modal-content">
                    <div class="modal-header">
                        <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
                        <h4 class="modal-title">Addon Domain</h4>
                    </div>

                    <div class="modal-body">
                        <blockquote class="ns-card">
                            <p class="text-warning">Nameserver:</p>
                            <div class="text-muted" id="ns-info-list">
                                <span style="opacity:.6;">Memuat…</span>
                            </div>
                        </blockquote>
                        <form method="post" id="frm_add">
                            <input type="hidden" value="add" name="action" id="action">
                            <div class="form-group">
                                <label for="adddomain" class="control-label">Domain Title: use [GLOBAL DOMAIN] for submit into global domain</label>
                                <hr style="margin:1px;padding:1px;border:0;border-bottom:0px">
                                <div class="ddt ddt-full" id="ddt-adddomain">
                                    <button type="button" class="btn btn-sm ddt-btn" id="ddt-adddomain-btn" aria-haspopup="listbox"><span class="ddt-label">[GLOBAL DOMAIN]</span><svg class="ddt-caret" viewBox="0 0 10 6" width="10" height="6" fill="currentColor" aria-hidden="true" focusable="false"><path d="M0 0l5 6 5-6z"/></svg></button>
                                    <ul class="ddt-menu" id="ddt-adddomain-menu" role="listbox">
                                        <li><a href="#" data-val="global" class="ddt-active">[GLOBAL DOMAIN]</a></li>
                                        <?php
                                        $stmtGen = $pdo->query('SELECT sub_id FROM generate ORDER BY sub_id ASC');
if ($stmtGen !== false) {
    while ($rowGen = $stmtGen->fetch(PDO::FETCH_ASSOC)) {
        $sid = htmlspecialchars(
            (string) $rowGen['sub_id'],
            ENT_QUOTES | ENT_SUBSTITUTE,
            'UTF-8',
        );
        echo '<li><a href="#" data-val="' . $sid . '">' . $sid . '</a></li>';
    }
}
?>
                                    </ul>
                                </div>
                                <input type="hidden" id="adddomain" value="global">
                                <input readonly="readonly" type="hidden" class="form-control" id="sub_domain" name="sub_domain" required="true" />
                            </div>
                            <div class="form-group">
                                <label for="domain" class="control-label">Domain Name: <span class="text-muted">(1&ndash;5 domain, satu per baris)</span></label>
                                <textarea placeholder="example.com&#10;example.net" class="form-control mono-input" id="domain" name="domain" rows="5" required="true" autofocus></textarea>
                            </div>
                            <div class="cf-toggle-row">
                                <label for="cf-add-toggle" class="cf-toggle-label">
                                    <span class="cf-switch">
                                        <input type="checkbox" id="cf-add-toggle" checked>
                                        <span class="cf-switch-track"></span>
                                    </span>
                                    <span>Auto-integrasi: cPanel wildcard &rarr; <strong>Cloudflare</strong> (zone, security, settings, DNS)</span>
                                </label>
                            </div>
                        </form>
                    </div>
                    <div id="add-cf-panel" style="display:none;">
                        <div id="add-cf-loader" style="display:none;text-align:center;padding:22px 0 14px;">
                            <svg style="animation:cf-spin .8s linear infinite;color:var(--muted);" viewBox="0 0 24 24" width="24" height="24" stroke="currentColor" fill="none" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83"/></svg>
                            <div id="add-cf-status-text" style="margin-top:6px;font-size:12px;color:var(--muted);">CF Sync…</div>
                        </div>
                        <blockquote class="ns-card" id="add-cf-ns-block" style="display:none;">
                            <p class="text-warning">Nameserver Cloudflare:</p>
                            <div id="add-cf-ns-panel"></div>
                        </blockquote>
                    </div>
                    <div class="modal-footer">
                        <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
                        <button type="button" id="btn_add" class="btn btn-primary">Save</button>
                    </div>
                </div>
            </div>
        </div>
        <div id="edit_model" class="modal fade" data-keyboard="false" data-backdrop="static">
            <div class="modal-dialog">
                <div class="modal-content">
                    <div class="modal-header">
                        <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
                        <h4 class="modal-title">Edit Domain</h4>
                    </div>
                    <div class="modal-body">
                        <form method="post" id="frm_edit">
                            <input type="hidden" value="edit" name="action" id="action">
                            <input type="hidden" value="0" name="edit_id" id="edit_id">

                            <div class="form-group">
                                <label for="edit_sub_domain" class="control-label">Domain Title:</label>
                                <input readonly="readonly" type="text" class="form-control" id="edit_sub_domain" name="edit_sub_domain" />
                            </div>
                            <div class="form-group">
                                <label for="edit_domain" class="control-label">Domain Name:</label>
                                <input type="text" class="form-control" id="edit_domain" name="edit_domain" required="true" autofocus/>
                            </div>
                    </div>
                    <div class="modal-footer">
                        <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
                        <button type="button" id="btn_edit" class="btn btn-primary">Save</button>
                    </div>
                    </form>
                </div>
            </div>
        </div>
        <div id="cf_config_modal" class="modal fade" data-keyboard="true" data-backdrop="static">
            <div class="modal-dialog" style="max-width:440px;">
                <div class="modal-content">
                    <div class="modal-header">
                        <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
                        <h4 class="modal-title">
                            <svg viewBox="0 0 24 24" width="15" height="15" stroke="currentColor" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right:6px;vertical-align:-2px;" aria-hidden="true"><circle cx="12" cy="12" r="3"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14M4.93 4.93a10 10 0 0 0 0 14.14"/><path d="M15.54 8.46a5 5 0 0 1 0 7.07M8.46 8.46a5 5 0 0 0 0 7.07"/></svg>
                            Cloudflare Configuration
                        </h4>
                    </div>
                    <div class="modal-body">
                        <p style="margin:0 0 12px;font-size:11.5px;color:var(--text);opacity:.8;">Perubahan langsung disimpan ke <code>.env</code>. Refresh halaman setelah simpan agar efektif.</p>
                        <div class="form-group">
                            <label class="control-label">CF API Token</label>
                            <div style="display:flex;gap:6px;align-items:center;">
                                <input type="password" class="form-control" id="cfg_cf_token" placeholder="Kosongkan = tidak diubah" autocomplete="new-password">
                                <button type="button" class="btn btn-sm btn-default" id="cfg_cf_token_toggle" title="Tampilkan/sembunyikan" style="flex-shrink:0;width:32px;height:31px;padding:0;">
                                    <svg viewBox="0 0 24 24" width="13" height="13" stroke="currentColor" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
                                </button>
                            </div>
                            <small style="font-size:11px;color:var(--text);opacity:.68;">Untuk auto-create token, paste token dari template <strong>Create Additional Tokens</strong> lalu klik tombol Create Additional Tokens.</small>
                        </div>
                        <div class="cf-permission-panel">
                            <div class="cf-permission-head">
                                <div class="cf-permission-title">Permission yang dibutuhkan (Custom Token):</div>
                                <button type="button" class="btn btn-sm btn-default cf-permission-toggle" data-target="cf-permission-body-admin" aria-expanded="false">Show</button>
                            </div>
                            <div class="cf-permission-body" id="cf-permission-body-admin">
                            <div class="cf-permission-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>DNS</span><span class="cf-perm-access">Read</span></div>
                                <div class="cf-perm-row"><span class="cf-perm-scope">Zone</span><span>Cache Rules</span><span class="cf-perm-access">Edit</span></div>
                                <div class="cf-perm-row"><span class="cf-perm-scope">Zone</span><span>Cache Rules</span><span class="cf-perm-access">Read</span></div>
                                <div class="cf-perm-row"><span class="cf-perm-scope">Account</span><span>Rulesets</span><span class="cf-perm-access">Edit</span></div>
                                <div class="cf-perm-row"><span class="cf-perm-scope">Account</span><span>Filter Lists</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 class="form-group">
                            <label class="control-label">CF Account ID</label>
                            <input type="text" class="form-control" id="cfg_cf_account" placeholder="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx">
                        </div>
                        <div class="form-group">
                            <label class="control-label">Server IP <small style="font-weight:400;opacity:.7;">(untuk auto DNS A record)</small></label>
                            <input type="text" class="form-control" id="cfg_cf_ip" placeholder="1.2.3.4">
                        </div>
                        <div class="form-group" style="margin-bottom:4px;">
                            <label class="control-label">Default Nameservers <small style="font-weight:400;opacity:.7;">(default fallback jika domain belum sync)</small></label>
                            <input type="text" class="form-control mono-input" id="cfg_cf_ns1" placeholder="ns1.example.com" style="margin-bottom:5px;">
                            <input type="text" class="form-control mono-input" id="cfg_cf_ns2" placeholder="ns2.example.com" style="margin-bottom:5px;">
                            <input type="text" class="form-control mono-input" id="cfg_cf_ns3" placeholder="ns3.example.com (opsional)" style="margin-bottom:5px;">
                            <input type="text" class="form-control mono-input" id="cfg_cf_ns4" placeholder="ns4.example.com (opsional)">
                        </div>
                        <div id="cfg_status" style="display:none;margin-top:8px;padding:7px 10px;border-radius:var(--radius);font-size:12px;"></div>
                    </div>
                    <div class="modal-footer">
                        <button type="button" class="btn btn-default btn-sm" data-dismiss="modal">Close</button>
                        <button type="button" class="btn btn-default btn-sm" id="cfg_create_additional_token">Create Additional Tokens</button>
                        <button type="button" class="btn btn-primary btn-sm" id="cfg_save_btn">
                            <svg viewBox="0 0 24 24" width="13" height="13" stroke="currentColor" fill="none" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><polyline points="17 21 17 13 7 13 7 21"/><polyline points="7 3 7 8 15 8"/></svg>
                            Simpan
                        </button>
                    </div>
                </div>
            </div>
        </div>

        <div id="cf_ns_modal" class="modal fade" data-keyboard="false" data-backdrop="static">
            <div class="modal-dialog" style="max-width:480px;">
                <div class="modal-content">
                    <div class="modal-header">
                        <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
                        <h4 class="modal-title">Cloudflare Nameservers</h4>
                    </div>
                    <div class="modal-body">
                        <p style="margin:0 0 8px;font-size:12px;color:var(--text);">
                            Point your domain <strong class="cf-ns-domain"></strong> to these nameservers at your domain registrar:
                        </p>
                        <ul class="cf-ns-list" style="margin:0;padding-left:18px;list-style:disc;"></ul>
                        <p style="margin:10px 0 0;font-size:11.5px;color:var(--text);opacity:.72;">
                            DNS propagation may take up to 24 hours. Run <em>Sync</em> again after pointing NS to refresh status.
                        </p>
                    </div>
                    <div class="modal-footer">
                        <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
                    </div>
                </div>
            </div>
        </div>
    <script nonce="<?= adminEsc($nonce); ?>">
    $(document).ready(function() {
        var ngixButtonSpinner = '<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> ';
        // PermissionToggleFixedInit
        $(document).on('click', '.cf-permission-toggle', function(e) {
            e.preventDefault();
            e.stopPropagation();

            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';
        });

        function svgIcon(type) {
            var a = ' width="14" height="14" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" focusable="false"';
            if (type === 'delete') {
                return '<span class="action-ico" aria-hidden="true"><svg' + a + '><path d="M4 7h16"/><path d="M10 11v6"/><path d="M14 11v6"/><path d="M6 7l1 13h10l1-13"/><path d="M9 7V4h6v3"/></svg></span>';
            }
            return '<span class="action-ico" aria-hidden="true"><svg' + a + '><path d="M4 20h4l11-11-4-4L4 16v4z"/><path d="M14 6l4 4"/></svg></span>';
        }
        function escHtml(v) {
            return String(v === null || v === undefined ? '' : v)
                .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
        }

        var state = { current: 1, rowCount: 25, search: '' };

        $("#adddomain").on('change', function() { $("#sub_domain").val(this.value); });
        $("#sub_domain").val('global');
        (function(){var btn=document.getElementById('ddt-adddomain-btn'),menu=document.getElementById('ddt-adddomain-menu'),inp=document.getElementById('adddomain');if(!btn||!menu||!inp)return;btn.addEventListener('click',function(e){e.stopPropagation();menu.classList.toggle('open');});menu.addEventListener('click',function(e){var a=e.target.closest?e.target.closest('a'):(e.target.tagName==='A'?e.target:null);if(!a)return;e.preventDefault();var v=a.getAttribute('data-val');menu.querySelectorAll('a').forEach(function(el){el.classList.remove('ddt-active');});a.classList.add('ddt-active');btn.querySelector('.ddt-label').textContent=a.textContent.trim();inp.value=v;menu.classList.remove('open');$(inp).trigger('change');});document.addEventListener('click',function(){menu.classList.remove('open');});})();

        function cfStatusBadge(row) {
            var status = (row.cf_zone_id && row.cf_zone_id !== '') ? (row.cf_status || 'unknown') : '';
            if (status === '') {
                return '<span class="cf-badge cf-badge--none" title="Not synced">— none</span>';
            }
            var cls = 'cf-badge';
            var label = status;
            if (status === 'active') { cls += ' cf-badge--active'; label = '<svg viewBox="0 0 24 24" width="10" height="10" stroke="currentColor" fill="none" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-1px" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg> aktif'; }
            else if (status === 'pending') { cls += ' cf-badge--pending'; label = '<svg viewBox="0 0 24 24" width="10" height="10" stroke="currentColor" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-1px" aria-hidden="true"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg> pending'; }
            else if (status === 'moved') { cls += ' cf-badge--moved'; label = '<svg viewBox="0 0 24 24" width="10" height="10" stroke="currentColor" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-1px" aria-hidden="true"><line x1="7" y1="17" x2="17" y2="7"/><polyline points="7 7 17 7 17 17"/></svg> moved'; }
            return '<span class="' + cls + '" title="Zone: ' + escHtml(row.cf_zone_id || '') + '">' + label + '</span>';
        }

        function cfSvg(type) {
            var a = ' width="14" height="14" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" focusable="false"';
            if (type === 'sync') return '<svg' + a + '><path d="M21.5 2v6h-6"/><path d="M2.5 12A10 10 0 0 1 20 6.3"/><path d="M2.5 22v-6h6"/><path d="M21.5 12A10 10 0 0 1 4 17.7"/></svg>';
            if (type === 'purge') return '<svg' + a + '><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>';
            if (type === 'origin') return '<svg' + a + '><rect x="4" y="10" width="16" height="10" rx="2"/><path d="M8 10V7a4 4 0 0 1 8 0v3"/><path d="M12 14v3"/></svg>';
            if (type === 'ns') return '<svg' + a + '><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>';
            return '';
        }

        function renderRow(row, idx) {
            var hasZone = row.cf_zone_id && row.cf_zone_id !== '';
            var cfStatus = hasZone ? (row.cf_status || '') : '';
            return '<tr data-row-id="' + escHtml(row.id) + '" data-cf-status="' + escHtml(cfStatus) + '" data-cf-cert="' + (String(row.cf_cert_installed) === '1' ? '1' : '0') + '">' +
                '<td><span class="td-num">' + (idx + 1) + '</span></td>' +
                '<td><span class="td-subdomain">' + escHtml(row.sub_domain) + '</span></td>' +
                '<td><span class="td-domain" title="' + escHtml(row.domain) + '">' + escHtml(row.domain) + '</span></td>' +
                '<td>' + cfStatusBadge(row) + '</td>' +
                '<td style="text-align:right;white-space:nowrap;">' +
                    '<button type="button" class="btn-cf command-cf-sync" data-row-id="' + escHtml(row.id) + '" data-domain="' + escHtml(row.domain) + '" title="Sync to Cloudflare">' + cfSvg('sync') + '</button>' +
                    (hasZone ? '<button type="button" class="btn-cf command-cf-purge" data-row-id="' + escHtml(row.id) + '" data-domain="' + escHtml(row.domain) + '" title="Purge CF Cache">' + cfSvg('purge') + '</button>' : '<button type="button" class="btn-cf" disabled style="opacity:.3;cursor:default;" title="No zone yet">' + cfSvg('purge') + '</button>') +
                    (hasZone ? '<button type="button" class="btn-cf command-cf-origin-cert" data-row-id="' + escHtml(row.id) + '" data-domain="' + escHtml(row.domain) + '" title="Install CF Origin Certificate">' + cfSvg('origin') + '</button>' : '<button type="button" class="btn-cf" disabled style="opacity:.3;cursor:default;" title="No zone yet">' + cfSvg('origin') + '</button>') +
                    (hasZone ? '<button type="button" class="btn-cf command-cf-ns" data-row-id="' + escHtml(row.id) + '" data-domain="' + escHtml(row.domain) + '" data-ns="' + escHtml(row.cf_ns || '[]') + '" title="View NS records">' + cfSvg('ns') + '</button>' : '') +
                    ' <button type="button" class="btn btn-sm btn-default action-btn command-delete" data-row-id="' + escHtml(row.id) + '" data-domain="' + escHtml(row.domain) + '" data-zone-id="' + escHtml(row.cf_zone_id || '') + '" title="Delete" aria-label="Delete">' + svgIcon('delete') + '</button>' +
                '</td></tr>';
        }

        function renderPagination(total, current, rowCount) {
            if (rowCount === -1 || total === 0) { $('#tbl-pagination').empty(); return; }
            var pages = Math.ceil(total / rowCount);
            if (pages <= 1) { $('#tbl-pagination').empty(); return; }
            var html = '';
            html += '<li class="' + (current <= 1 ? 'disabled' : '') + '"><a href="#" data-page="' + (current - 1) + '">&laquo;</a></li>';
            var start = Math.max(1, current - 2), end = Math.min(pages, current + 2);
            if (start > 1) { html += '<li><a href="#" data-page="1">1</a></li>' + (start > 2 ? '<li class="disabled"><span>…</span></li>' : ''); }
            for (var p = start; p <= end; p++) {
                html += '<li class="' + (p === current ? 'active' : '') + '"><a href="#" data-page="' + p + '">' + p + '</a></li>';
            }
            if (end < pages) { html += (end < pages - 1 ? '<li class="disabled"><span>…</span></li>' : '') + '<li><a href="#" data-page="' + pages + '">' + pages + '</a></li>'; }
            html += '<li class="' + (current >= pages ? 'disabled' : '') + '"><a href="#" data-page="' + (current + 1) + '">&raquo;</a></li>';
            $('#tbl-pagination').html(html);
            $('#tbl-pagination').off('click', 'a').on('click', 'a', function(e) {
                e.preventDefault();
                var pg = parseInt($(this).data('page'));
                if (!isNaN(pg) && pg >= 1 && pg <= pages && pg !== state.current) {
                    state.current = pg; loadData();
                }
            });
        }

        function cfRequest(action, rowId, extra, btn) {
            if (btn) { $(btn).prop('disabled', true).addClass('btn-cf--loading'); }
            var data = $.extend({ action: action, id: rowId }, extra || {});
            return $.ajax({ type: 'POST', url: 'cf.php', data: data, dataType: 'json' })
                .always(function() { if (btn) { $(btn).prop('disabled', false).removeClass('btn-cf--loading'); } });
        }

        function cfUpdateRowBadge(rowId, cfStatus, zoneId, cfNs) {
            var tr = $('#tbl_domains tbody tr[data-row-id="' + rowId + '"]');
            if (!tr.length) { return; }
            var fakeRow = { cf_zone_id: zoneId, cf_status: cfStatus, cf_ns: cfNs };
            tr.find('td:nth-child(4)').html(cfStatusBadge(fakeRow));
            tr.attr('data-cf-status', (zoneId && zoneId !== '') ? (cfStatus || '') : '');
        }

        function cfSync(rowId, domain, btn) {
            // Show NS modal immediately with spinner
            $('#cf_ns_modal .cf-ns-domain').text(domain);
            $('#cf_ns_modal .cf-ns-list').html(
                '<li style="text-align:center;padding:18px 0;list-style:none;">' +
                '<svg style="animation:cf-spin .8s linear infinite;color:var(--muted);" viewBox="0 0 24 24" width="28" height="28" stroke="currentColor" fill="none" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83"/></svg>' +
                '<div style="margin-top:10px;font-size:12px;color:var(--muted);">Syncing to Cloudflare…</div>' +
                '</li>'
            );
            $('#cf_ns_modal').modal('show');

            cfRequest('zone_add', rowId, {}, btn)
                .done(function(data) {
                    if (data && data.ok) {
                        var dnsInfo = '';
                        if (data.dns_log && data.dns_log.length) {
                            var errs = data.dns_log.filter(function(l){ return l.indexOf('error:') === 0; }).length;
                            dnsInfo = ' · DNS: ' + (data.dns_log.length - errs) + ' ok' + (errs ? ', ' + errs + ' err' : '');
                        }
                        showToast('success', 'CF Zone OK', (data.cf_status || '') + ' — ' + domain + dnsInfo);
                        if (data.cf_warnings) { showToast('error', 'CF: ' + data.cf_warnings + ' operasi gagal', (data.cf_warning_sample && data.cf_warning_sample[0]) || 'cek error_log — kemungkinan izin token'); }
                        cfUpdateRowBadge(rowId, data.cf_status, data.zone_id, JSON.stringify(data.cf_ns || []));
                        // Replace spinner with NS info in the already-open modal
                        cfShowNsInPlace(rowId, domain, JSON.stringify(data.cf_ns || []));
                        if (data.cf_status === 'active') { cfAutoInstallCertOnce(rowId, domain); }
                    } else {
                        showToast('error', 'CF Error', (data && data.err) || 'zone_add failed');
                        $('#cf_ns_modal').modal('hide');
                    }
                })
                .fail(function() {
                    showToast('error', 'CF Sync Failed', domain);
                    $('#cf_ns_modal').modal('hide');
                });
        }

        function cfPurge(rowId, domain, btn) {
            cfRequest('purge_cache', rowId, {}, btn)
                .done(function(data) {
                    if (data && data.ok) { showToast('success', 'Cache Purged', domain); }
                    else { showToast('error', 'Purge Failed', (data && data.err) || ''); }
                })
                .fail(function() { showToast('error', 'Purge Failed', domain); });
        }

        function cfOriginCertInstall(rowId, domain, btn) {
            showToast('info', 'Installing Origin Certificate…', domain);
            cfRequest('origin_cert_install', rowId, {}, btn)
                .done(function(data) {
                    if (data && data.ok) {
                        var cert = data.origin_cert || {};
                        showToast('success', 'Origin Certificate Installed', (cert.ssl_mode || 'strict') + ' — ' + domain);
                    } else {
                        showToast('error', 'Origin Cert Failed', (data && data.err) || domain);
                    }
                })
                .fail(function(xhr) {
                    var msg = domain;
                    if (xhr && xhr.responseJSON && xhr.responseJSON.err) { msg = xhr.responseJSON.err; }
                    showToast('error', 'Origin Cert Failed', msg);
                });
        }

        // ── Auto CF status poll + one-time origin cert on first 'active' ──────
        var CF_POLL_INTERVAL_MS = 45000; // re-check pending zones every 45s
        var cfPollBusy = false;

        // Durable per-browser guard so auto-cert runs at most once per domain.
        // (DB has no cert column; this is client-side state — see CLAUDE.md.)
        function cfCertDoneMap() {
            try { return JSON.parse(localStorage.getItem('srp_addon_cf_cert_done') || '{}') || {}; }
            catch (e) { return {}; }
        }
        function cfCertDoneHas(domain) {
            return !!cfCertDoneMap()[String(domain).toLowerCase()];
        }
        function cfCertDoneSet(domain, done) {
            try {
                var m = cfCertDoneMap(), key = String(domain).toLowerCase();
                if (done) { m[key] = 1; } else { delete m[key]; }
                localStorage.setItem('srp_addon_cf_cert_done', JSON.stringify(m));
            } catch (e) {}
        }

        // Mark domains already 'active' at render time as handled WITHOUT
        // installing, so auto-cert only fires for domains that reach 'active'
        // afterwards (genuinely "first time" from the panel). Prevents
        // retroactively re-issuing certs for the existing active fleet.
        function cfSeedActiveAsHandled() {
            $('#tbl_domains tbody tr[data-row-id]').each(function() {
                var $tr = $(this);
                var domain = $tr.find('.command-cf-sync').data('domain') || '';
                if (!domain) { return; }
                // Server truth wins: cert recorded installed → durable guard.
                if (($tr.attr('data-cf-cert') || '') === '1') { cfCertDoneSet(domain, true); return; }
                // Anti-retroactive: suppress domains already 'active' at load so
                // auto-cert only fires for ones that reach active afterwards.
                if (($tr.attr('data-cf-status') || '') === 'active' && !cfCertDoneHas(domain)) {
                    cfCertDoneSet(domain, true);
                }
            });
        }

        // Install the CF origin certificate exactly once for a domain that has
        // just become active. Guard is set before the call to block re-entry;
        // rolled back on failure so a later poll/sync can retry.
        function cfAutoInstallCertOnce(rowId, domain) {
            if (!domain || cfCertDoneHas(domain)) { return; }
            cfCertDoneSet(domain, true);
            showToast('info', 'Auto SSL…', 'Origin cert untuk ' + domain);
            cfRequest('origin_cert_install', rowId, { auto: '1' })
                .done(function(data) {
                    if (data && data.ok) {
                        $('#tbl_domains tbody tr[data-row-id="' + rowId + '"]').attr('data-cf-cert', '1');
                        if (data.already_installed) { return; }
                        var cert = data.origin_cert || {};
                        showToast('success', 'Auto SSL OK', (cert.ssl_mode || 'strict') + ' — ' + domain);
                    } else {
                        cfCertDoneSet(domain, false);
                        showToast('error', 'Auto SSL Gagal', (data && data.err) || domain);
                    }
                })
                .fail(function(xhr) {
                    cfCertDoneSet(domain, false);
                    showToast('error', 'Auto SSL Gagal', (xhr && xhr.responseJSON && xhr.responseJSON.err) || domain);
                });
        }

        // Refresh every 'pending' zone once; flip badges; auto-cert on active.
        function cfRefreshPendingOnce() {
            if (cfPollBusy) { return; }
            var pending = [];
            $('#tbl_domains tbody tr[data-row-id]').each(function() {
                if (($(this).attr('data-cf-status') || '') === 'pending') {
                    var id = $(this).data('row-id');
                    var domain = $(this).find('.command-cf-sync').data('domain') || '';
                    if (id) { pending.push({ id: id, domain: domain }); }
                }
            });
            if (!pending.length) { return; }
            cfPollBusy = true;
            var i = 0;
            function nextPoll() {
                if (i >= pending.length) { cfPollBusy = false; return; }
                var p = pending[i]; i++;
                cfRequest('refresh_status', p.id, {})
                    .done(function(data) {
                        if (data && data.ok) {
                            cfUpdateRowBadge(p.id, data.cf_status, data.zone_id, JSON.stringify(data.cf_ns || []));
                            if (data.cf_cert_installed) {
                                $('#tbl_domains tbody tr[data-row-id="' + p.id + '"]').attr('data-cf-cert', '1');
                                cfCertDoneSet(p.domain, true);
                            }
                            if (data.cf_status === 'active') { cfAutoInstallCertOnce(p.id, p.domain); }
                        }
                    })
                    .always(function() { nextPoll(); });
            }
            nextPoll();
        }

        setInterval(cfRefreshPendingOnce, CF_POLL_INTERVAL_MS);

        function normalizeNsList(nsList) {
            if (!Array.isArray(nsList)) {
                return [];
            }
            return nsList.map(function(ns) {
                return String(ns || '').trim();
            }).filter(function(ns) {
                return ns !== '';
            });
        }

        function cfShowNs(rowId, domain, nsJson) {
            var ns = [];
            try { ns = normalizeNsList(JSON.parse(nsJson || '[]')); } catch(e) {}
            var isFallback = false;
            if (!ns.length && cfDefaultNs.length) {
                ns = normalizeNsList(cfDefaultNs);
                isFallback = true;
            }
            var nsHtml = ns.length
                ? ns.map(function(n) { return '<li style="font-family:Consolas,Monaco,\'Courier New\',monospace;font-size:12px;padding:2px 0;">' + escHtml(n) + '</li>'; }).join('')
                : '<li style="color:var(--text);opacity:.6;">No NS records stored — run sync first.</li>';
            if (isFallback) {
                nsHtml += '<li style="margin-top:6px;font-size:11px;color:var(--text);opacity:.6;list-style:none;">⚠ Fallback dari env — jalankan Sync untuk NS spesifik domain ini.</li>';
            }
            $('#cf_ns_modal .cf-ns-domain').text(domain);
            $('#cf_ns_modal .cf-ns-list').html(nsHtml);
            $('#cf_ns_modal').modal('show');
        }

        // Update the already-open NS modal in place (no re-show flicker).
        function cfShowNsInPlace(rowId, domain, nsJson) {
            var ns = [];
            try { ns = normalizeNsList(JSON.parse(nsJson || '[]')); } catch(e) {}
            var isFallback = false;
            if (!ns.length && cfDefaultNs.length) {
                ns = normalizeNsList(cfDefaultNs);
                isFallback = true;
            }
            var nsHtml = ns.length
                ? ns.map(function(n) { return '<li style="font-family:Consolas,Monaco,\'Courier New\',monospace;font-size:12px;padding:2px 0;">' + escHtml(n) + '</li>'; }).join('')
                : '<li style="color:var(--text);opacity:.6;">No NS records stored — run sync to retrieve them.</li>';
            if (isFallback) {
                nsHtml += '<li style="margin-top:6px;font-size:11px;color:var(--text);opacity:.6;list-style:none;">⚠ Fallback dari env — jalankan Sync lagi nanti untuk NS spesifik domain ini.</li>';
            }
            // Fade transition: spinner out → NS info in
            var $list = $('#cf_ns_modal .cf-ns-list');
            $list.css({ opacity: '0', transition: 'opacity .15s ease' });
            setTimeout(function() {
                $list.html(nsHtml);
                $list.css({ opacity: '1' });
            }, 160);
        }

        function bindRowActions() {
            $('#tbl_domains').find('.command-delete').off('click').on('click', function() {
                var $btn = $(this);
                var rowId = $btn.data('row-id');
                var domain = $btn.data('domain') || '';
                var zoneId = $btn.data('zone-id') || '';
                var $tr = $btn.closest('tr');
                Swal.fire({
                    title: 'Are you sure?',
                    text: "You won't be able to revert this!",
                    type: 'warning',
                    showCancelButton: true,
                    allowOutsideClick: false,
                    confirmButtonColor: '#6c757d',
                    cancelButtonColor: '#6c757d',
                    confirmButtonText: 'Yes, delete it!'
                }).then(function(result) {
                    if (!result.value) { return; }
                    $tr.find('button').prop('disabled', true);
                    $tr.find('td:last-child').html(
                        '<span style="display:inline-flex;align-items:center;gap:5px;padding:0 6px;color:var(--muted);font-size:11px;">' +
                        '<svg style="animation:cf-spin .7s linear infinite" viewBox="0 0 24 24" width="13" height="13" stroke="currentColor" fill="none" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83"/></svg>' +
                        'Deleting…</span>'
                    );
                    $tr.css('opacity', '0.5');
                    $.ajax({
                        type: 'POST', url: 'response.php',
                        data: { id: rowId, action: 'delete' },
                        dataType: 'json'
                    }).done(function() {
                        var pending = [];
                        if (domain !== '') {
                            pending.push($.ajax({
                                type: 'POST', url: '',
                                data: { action: 'cpanel_delete', domain: domain },
                                dataType: 'json'
                            }));
                        }
                        if (domain !== '' && zoneId !== '') {
                            pending.push($.ajax({
                                type: 'POST', url: 'cf.php',
                                data: { action: 'zone_delete', zone_id: zoneId },
                                dataType: 'json'
                            }));
                        }
                        (pending.length ? $.when.apply($, pending) : $.when()).always(function() {
                            $tr.fadeOut(180, function() {
                                $(this).remove();
                                showToast('success', 'Deleted', domain || 'Domain removed.');
                                loadData();
                            });
                        });
                    }).fail(function() {
                        $tr.css('opacity', '');
                        loadData();
                        showToast('error', 'Delete Failed', domain);
                    });
                });
            });

            $('#tbl_domains').find('.command-cpanel-sync').off('click').on('click', function() {
                var btn = this;
                var domain = $(btn).data('domain');
                if (!domain) { return; }
                showToast('info', 'cPanel Sync…', domain);
                cpanelSync(domain, btn)
                    .done(function(res) {
                        if (res && res.ok) {
                            showToast('success', 'cPanel Synced', domain);
                        } else {
                            showToast('error', 'cPanel Sync Failed', (res && res.err) || domain);
                        }
                    })
                    .fail(function() { showToast('error', 'cPanel Sync Error', domain); });
            });

            $('#tbl_domains').find('.command-cf-sync').off('click').on('click', function() {
                var btn = this;
                var rowId = $(btn).data('row-id');
                var domain = $(btn).data('domain');
                if (($(btn).closest('tr').attr('data-cf-status') || '') === 'active') {
                    showToast('info', 'Sudah aktif', domain + ' — sync dilewati');
                    return;
                }
                cfSync(rowId, domain, btn);
            });

            $('#tbl_domains').find('.command-cf-purge').off('click').on('click', function() {
                var btn = this;
                var rowId = $(btn).data('row-id');
                var domain = $(btn).data('domain');
                cfPurge(rowId, domain, btn);
            });

            $('#tbl_domains').find('.command-cf-origin-cert').off('click').on('click', function() {
                var btn = this;
                var rowId = $(btn).data('row-id');
                var domain = $(btn).data('domain');
                cfOriginCertInstall(rowId, domain, btn);
            });

            $('#tbl_domains').find('.command-cf-ns').off('click').on('click', function() {
                var rowId = $(this).data('row-id');
                var domain = $(this).data('domain');
                var nsJson = $(this).data('ns') || '[]';
                cfShowNs(rowId, domain, nsJson);
            });
        }

        function loadData() {
            $.ajax({
                type: 'POST', url: 'response.php',
                data: { current: state.current, rowCount: state.rowCount, searchPhrase: state.search },
                dataType: 'json',
                success: function(data) {
                    var rows = data.rows || [], total = data.total || 0;
                    var html = '';
                    for (var i = 0; i < rows.length; i++) { html += renderRow(rows[i], i); }
                    $('#tbl_domains tbody').html(html || '<tr><td colspan="5" style="text-align:center;padding:18px;color:var(--text);">No data</td></tr>');
                    var from = total === 0 ? 0 : ((state.current - 1) * (state.rowCount === -1 ? total : state.rowCount)) + 1;
                    var to = state.rowCount === -1 ? total : Math.min(state.current * state.rowCount, total);
                    $('#tbl-info').text('Showing ' + from + ' to ' + to + ' of ' + total + ' entries');
                    renderPagination(total, state.current, state.rowCount);
                    bindRowActions();
                    cfSeedActiveAsHandled();
                    cfRefreshPendingOnce();
                }
            });
        }

        var searchTimer;
        $('#tbl-search').on('input', function() {
            clearTimeout(searchTimer);
            var val = $(this).val();
            searchTimer = setTimeout(function() { state.search = val; state.current = 1; loadData(); }, 300);
        });
        $('#tbl-rowcount').on('change', function() {
            state.rowCount = parseInt(this.value); state.current = 1; loadData();
        });
        (function(){var btn=document.getElementById('ddt-rowcount-btn'),menu=document.getElementById('ddt-rowcount-menu'),inp=document.getElementById('tbl-rowcount');if(!btn||!menu||!inp)return;btn.addEventListener('click',function(e){e.stopPropagation();menu.classList.toggle('open');});menu.addEventListener('click',function(e){var a=e.target.closest?e.target.closest('a'):(e.target.tagName==='A'?e.target:null);if(!a)return;e.preventDefault();var v=a.getAttribute('data-val');menu.querySelectorAll('a').forEach(function(el){el.classList.remove('ddt-active');});a.classList.add('ddt-active');btn.querySelector('.ddt-label').textContent=a.textContent;inp.value=v;menu.classList.remove('open');$(inp).trigger('change');});document.addEventListener('click',function(){menu.classList.remove('open');});})();

        function cpanelSync(domain, btn) {
            if (btn) { $(btn).prop('disabled', true).addClass('btn-cf--loading'); }
            return $.ajax({
                type: 'POST',
                url: 'cpanel-add.php',
                dataType: 'json',
                data: {
                    action: 'cpanel_add',
                    domain: domain,
                    url: domain,
                    csrf_token: $('meta[name="csrf-token"]').attr('content') || ''
                }
            })
                .always(function() { if (btn) { $(btn).prop('disabled', false).removeClass('btn-cf--loading'); } });
        }

        function ajaxAction(action) {
            var addon = $('#domain').val().trim();
            // Auto-integration: on add, run cPanel wildcard → Cloudflare zone
            // (security + settings + DNS) when the CF toggle is checked.
            // Toggle defaults to on; user may uncheck to skip CF for this add.
            var withCf = action === 'add' && $('#cf-add-toggle').is(':checked');
            var data = $('#frm_' + action).serializeArray();
            var btn = action === 'add' ? $('#btn_add') : $('#btn_edit');

            if (action === 'add') {
                data.push({ name: 'sync_cf', value: withCf ? '1' : '0' });
                showToast('info', 'Adding Domain…', addon);
            }

            btn.prop('disabled', true);
            $.ajax({
                type: 'POST', url: 'response.php',
                data: data, dataType: 'json'
            }).done(function(res) {
                if (!(res && res.ok)) {
                    showToast('error', 'Save Failed', (res && res.err) || addon || 'Request failed');
                    return;
                }

                var newId = (res.id) ? parseInt(res.id, 10) : 0;

                if (action !== 'add' || addon === '') {
                    $('#' + action + '_model').modal('hide');
                    loadData();
                    showToast('success', 'Saved', 'Domain updated.');
                    return;
                }

                if (newId < 1) {
                    $('#' + action + '_model').modal('hide');
                    showToast('error', 'Add Failed', 'missing-domain-id');
                    return;
                }

                // Keep modal open during cPanel + CF sync so user sees progress.
                // Show CF loader immediately if CF is enabled.
                if (withCf) {
                    $('#add-cf-panel').show();
                    $('#add-cf-loader').show();
                    $('#add-cf-ns-block').hide();
                    $('#add-cf-status-text').text('Memulai cPanel + Cloudflare…');
                }

                cpanelSync(res.domain || addon)
                    .done(function(cpData) {
                        if (!(cpData && cpData.ok)) {
                            showToast('error', 'cPanel Sync Failed', (cpData && cpData.err) || (res.domain || addon));
                            if (!withCf) { $('#' + action + '_model').modal('hide'); }
                            return;
                        }

                        if (!withCf) {
                            $('#' + action + '_model').modal('hide');
                            loadData();
                            showToast('success', 'cPanel OK', 'Wildcard enabled — ' + (res.domain || addon));
                            return;
                        }

                        // CF panel should already be visible — update status
                        $('#add-cf-status-text').text('Syncing to Cloudflare…');

                        cfRequest('zone_add', newId, {})
                            .done(function(cfData) {
                                if (cfData && cfData.ok) {
                                    var dnsInfo = '';
                                    if (cfData.dns_log && cfData.dns_log.length) {
                                        var errs = cfData.dns_log.filter(function(l) {
                                            return String(l).indexOf('error:') === 0;
                                        }).length;
                                        dnsInfo = ' · DNS: ' + (cfData.dns_log.length - errs) + ' ok' + (errs ? ', ' + errs + ' err' : '');
                                    }
                                    $('#add-cf-status-text').text('CF Zone: ' + (cfData.cf_status || '') + dnsInfo);
                                    showToast('success', 'CF Zone OK', (cfData.cf_status || '') + ' — ' + (res.domain || addon) + dnsInfo);
                                    if (cfData.cf_warnings) { showToast('error', 'CF: ' + cfData.cf_warnings + ' operasi gagal', (cfData.cf_warning_sample && cfData.cf_warning_sample[0]) || 'cek error_log — kemungkinan izin token'); }
                                    cfUpdateRowBadge(newId, cfData.cf_status, cfData.zone_id, JSON.stringify(cfData.cf_ns || []));

                                    // Replace loader with NS info
                                    $('#add-cf-loader').hide();
                                    $('#add-cf-ns-block').show();
                                    var nsList = [];
                                    try { nsList = normalizeNsList(JSON.parse(cfData.cf_ns || '[]')); } catch(e) {}
                                    var nsHtml = nsList.length
                                        ? nsList.map(function(n) { return '<code style="display:block;font-size:11.5px;padding:1px 0;">' + escHtml(n) + '</code>'; }).join('')
                                        : '<span style="opacity:.6;font-size:11.5px;">Menunggu NS dari Cloudflare — cek lagi nanti.</span>';
                                    $('#add-cf-ns-panel').html(nsHtml);

                                    loadData();
                                    // Auto-close modal after 2.5s so user can read NS info
                                    setTimeout(function() { $('#add_model').modal('hide'); }, 2500);
                                } else {
                                    $('#add-cf-loader').hide();
                                    $('#add-cf-ns-block').hide();
                                    $('#add-cf-status-text').text('CF Sync Failed: ' + ((cfData && cfData.err) || 'unknown'));
                                    showToast('error', 'CF Error', (cfData && cfData.err) || 'cf-sync-failed');
                                    // Keep modal open so user sees the error
                                }
                            })
                            .fail(function(xhr) {
                                var msg = 'cf-sync-failed';
                                if (xhr && xhr.responseJSON && xhr.responseJSON.err) {
                                    msg = xhr.responseJSON.err;
                                }
                                $('#add-cf-loader').hide();
                                $('#add-cf-ns-block').hide();
                                $('#add-cf-status-text').text('CF Sync Error: ' + msg);
                                showToast('error', 'CF Sync Error', msg);
                            });
                    })
                    .fail(function(xhr) {
                        var msg = res.domain || addon;
                        if (xhr && xhr.responseJSON && xhr.responseJSON.err) {
                            msg = xhr.responseJSON.err;
                        } else {
                            msg = 'cpanel-request-failed';
                        }
                        $('#add-cf-loader').hide();
                        $('#add-cf-status-text').text('cPanel Error: ' + msg);
                        showToast('error', 'cPanel Sync Error', msg);
                    });
            }).fail(function(xhr) {
                var msg = 'Request failed';
                if (xhr && xhr.responseJSON && xhr.responseJSON.err) {
                    msg = xhr.responseJSON.err;
                }
                showToast('error', action === 'add' ? 'Add Failed' : 'Save Failed', msg);
            }).always(function() {
                btn.prop('disabled', false);
            });
        }

        $('#command-sync-all').on('click', function() {
            var btn = this;
            var syncSvg = '<svg viewBox="0 0 24 24" width="13" height="13" stroke="currentColor" fill="none" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21.5 2v6h-6"/><path d="M2.5 12A10 10 0 0 1 20 6.3"/><path d="M2.5 22v-6h6"/><path d="M21.5 12A10 10 0 0 1 4 17.7"/></svg>';
            $(btn).prop('disabled', true).addClass('btn-syncall--loading').html(syncSvg + ' Syncing…');
            var rows = [], skipped = 0;
            $('#tbl_domains tbody tr[data-row-id]').each(function() {
                var rowId = $(this).data('row-id');
                var domain = $(this).find('.command-cf-sync').data('domain') || '';
                if (($(this).attr('data-cf-status') || '') === 'active') { skipped++; return; }
                if (rowId && domain) { rows.push({ id: rowId, domain: domain }); }
            });
            if (rows.length === 0) {
                showToast('info', skipped ? 'Semua aktif' : 'No rows', skipped ? skipped + ' domain sudah aktif — dilewati.' : 'Load data first.');
                $(btn).prop('disabled', false).removeClass('btn-syncall--loading').html('<svg viewBox="0 0 24 24" width="13" height="13" stroke="currentColor" fill="none" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21.5 2v6h-6"/><path d="M2.5 12A10 10 0 0 1 20 6.3"/><path d="M2.5 22v-6h6"/><path d="M21.5 12A10 10 0 0 1 4 17.7"/></svg> Sync All CF');
                return;
            }
            var done = 0;
            function syncNext() {
                if (done >= rows.length) {
                    showToast('success', 'Sync All Done', done + ' domain diproses' + (skipped ? ', ' + skipped + ' aktif dilewati' : '') + '.');
                    $(btn).prop('disabled', false).removeClass('btn-syncall--loading').html('<svg viewBox="0 0 24 24" width="13" height="13" stroke="currentColor" fill="none" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21.5 2v6h-6"/><path d="M2.5 12A10 10 0 0 1 20 6.3"/><path d="M2.5 22v-6h6"/><path d="M21.5 12A10 10 0 0 1 4 17.7"/></svg> Sync All CF');
                    loadData();
                    return;
                }
                var r = rows[done];
                done++;
                $(btn).html(syncSvg + ' Syncing ' + done + '/' + rows.length + '…');
                cfRequest('zone_add', r.id, {})
                    .done(function(data) {
                        if (data && data.ok) {
                            cfUpdateRowBadge(r.id, data.cf_status, data.zone_id, JSON.stringify(data.cf_ns || []));
                            if (data.cf_status === 'active') { cfAutoInstallCertOnce(r.id, r.domain); }
                        }
                    })
                    .always(function() { syncNext(); });
            }
            syncNext();
        });

        var cpanelNsCache = null;
        var cpanelNsLoading = false;

        function loadCpanelNs() {
            if (cpanelNsCache !== null) {
                renderCpanelNs(cpanelNsCache);
                return;
            }
            $('#ns-info-list').html('<span style="opacity:.6;">Memuat…</span>');
            $.ajax({ type: 'GET', url: 'cpanel-ns.php', dataType: 'json' })
                .done(function(data) {
                    cpanelNsCache = (data && data.ok && data.ns && data.ns.length) ? data.ns : [];
                    renderCpanelNs(cpanelNsCache);
                })
                .fail(function() {
                    $('#ns-info-list').html('<span style="opacity:.6;">Gagal memuat nameserver.</span>');
                });
        }

        function renderCpanelNs(nsList) {
            nsList = normalizeNsList(nsList);
            if (!nsList || !nsList.length) {
                $('#ns-info-list').html('<span style="opacity:.6;">Nameserver tidak ditemukan.</span>');
                return;
            }
            var mid = Math.ceil(nsList.length / 2);
            function col(items, offset) {
                return items.map(function(ns, i) {
                    return 'NS' + (offset + i + 1) + ': <code>' + escHtml(ns) + '</code>';
                }).join('<hr style="margin:1px;padding:0;border:0;">');
            }
            var left  = col(nsList.slice(0, mid), 0);
            var right = col(nsList.slice(mid), mid);
            var html = '<div style="display:flex;gap:24px;flex-wrap:wrap;">'
                + '<div style="flex:1;min-width:200px;">' + left + '</div>'
                + '<div style="flex:1;min-width:200px;">' + right + '</div>'
                + '</div>';
            $('#ns-info-list').html(html);
        }

        $("#command-add").on('click', function() {
            $("#domain").val('');
            var cfOn = $('#cf-add-toggle').is(':checked');
            // Show/hide CF panel based on toggle state
            $('#add-cf-panel').css('display', cfOn ? 'block' : 'none');
            $('#add-cf-loader').css('display', 'none');
            $('#add-cf-ns-block').css('display', 'none');
            $('#add-cf-ns-panel').empty();
            loadCpanelNs();
            $('#add_model').modal('show');
        });

        // Toggle CF panel visibility when checkbox changes
        $('#cf-add-toggle').on('change', function() {
            $('#add-cf-panel').css('display', this.checked ? 'block' : 'none');
            if (!this.checked) {
                $('#add-cf-loader').css('display', 'none');
                $('#add-cf-ns-block').css('display', 'none');
            }
        });
        $('#add_model').on('hidden.bs.modal', function() {
            $('#domain').val('');
            resetAddModal();
        });
        var ADD_MAX_DOMAINS = 5;

        // Parse the multi-domain textarea: split on whitespace/comma, strip
        // protocol/path, lowercase, drop empties, dedupe (server remains the
        // authority on per-domain validity).
        function parseDomainList(raw) {
            var seen = {}, out = [];
            String(raw || '').split(/[\s,]+/).forEach(function(d) {
                d = d.trim().toLowerCase().replace(/^https?:\/\//, '').replace(/[\/\\].*$/, '');
                if (d === '' || seen[d]) { return; }
                seen[d] = true;
                out.push(d);
            });
            return out;
        }

        // Run one domain through insert → cpanel wildcard → optional CF zone.
        // Always resolves (never rejects) so a batch continues past failures.
        function addDomainPipeline(domain, withCf) {
            var dfd = $.Deferred();
            showToast('info', 'Adding Domain…', domain);
            $.ajax({
                type: 'POST', url: 'response.php', dataType: 'json',
                data: { action: 'add', sub_domain: $('#sub_domain').val(), domain: domain, sync_cf: withCf ? '1' : '0' }
            }).done(function(res) {
                if (!(res && res.ok)) {
                    showToast('error', 'Save Failed', (res && res.err) || domain || 'Request failed');
                    dfd.resolve({ domain: domain, ok: false });
                    return;
                }
                var newId = res.id ? parseInt(res.id, 10) : 0;
                var realDomain = res.domain || domain;
                if (newId < 1) {
                    showToast('error', 'Add Failed', 'missing-domain-id');
                    dfd.resolve({ domain: realDomain, ok: false });
                    return;
                }
                showToast('info', 'cPanel Sync…', realDomain);
                cpanelSync(realDomain).done(function(cpData) {
                    if (!(cpData && cpData.ok)) {
                        showToast('error', 'cPanel Sync Failed', (cpData && cpData.err) || realDomain);
                        dfd.resolve({ domain: realDomain, ok: false });
                        return;
                    }
                    showToast('success', 'cPanel OK', 'Wildcard enabled — ' + realDomain);
                    if (!withCf) { dfd.resolve({ domain: realDomain, ok: true }); return; }

                    // Show CF loader in the add modal
                    $('#ns-info-list').closest('blockquote').hide();
                    $('#frm_add').hide();
                    $('#add-cf-panel').css('display', 'block');
                    $('#add-cf-ns-block').hide();
                    $('#add-cf-status-text').text('CF Sync: ' + realDomain + '…');
                    $('#add-cf-loader').css('display', 'block');
                    cfRequest('zone_add', newId, {}).done(function(cfData) {
                        $('#add-cf-loader').hide();
                        if (cfData && cfData.ok) {
                            var dnsInfo = '';
                            if (cfData.dns_log && cfData.dns_log.length) {
                                var errs = cfData.dns_log.filter(function(l) { return String(l).indexOf('error:') === 0; }).length;
                                dnsInfo = ' · DNS: ' + (cfData.dns_log.length - errs) + ' ok' + (errs ? ', ' + errs + ' err' : '');
                            }
                            showToast('success', 'CF Zone OK', (cfData.cf_status || '') + ' — ' + realDomain + dnsInfo);
                            if (cfData.cf_warnings) { showToast('error', 'CF: ' + cfData.cf_warnings + ' operasi gagal', (cfData.cf_warning_sample && cfData.cf_warning_sample[0]) || 'cek error_log — kemungkinan izin token'); }
                            cfUpdateRowBadge(newId, cfData.cf_status, cfData.zone_id, JSON.stringify(cfData.cf_ns || []));
                            var cfNsList = normalizeNsList(cfData.cf_ns || []);
                            var isFallback = false;
                            if (!cfNsList.length && cfDefaultNs.length) { cfNsList = normalizeNsList(cfDefaultNs); isFallback = true; }
                            var nsItems = cfNsList.length
                                ? cfNsList.map(function(n) {
                                    return '<li style="font-family:Consolas,Monaco,\'Courier New\',monospace;font-size:12px;padding:2px 0;">' + escHtml(n) + '</li>';
                                }).join('')
                                : '<li style="color:var(--muted);opacity:.6;">No NS records stored — run sync first.</li>';
                            if (isFallback) { nsItems += '<li style="margin-top:6px;font-size:11px;color:var(--muted);opacity:.6;list-style:none;">⚠ Fallback dari env — jalankan Sync untuk NS spesifik domain ini.</li>'; }
                            $('#add-cf-ns-panel').append(
                                '<div style="margin:0 0 4px;font-size:11px;font-weight:600;">' + escHtml(realDomain) + '</div>'
                                + '<ul style="margin:0 0 8px;padding-left:16px;">' + nsItems + '</ul>'
                            );
                            $('#add-cf-ns-block').show();
                            if (cfData.cf_status === 'active') { cfAutoInstallCertOnce(newId, realDomain); }
                            dfd.resolve({ domain: realDomain, ok: true, nsShown: true });
                        } else {
                            showToast('error', 'CF Error', (cfData && cfData.err) || 'cf-sync-failed');
                            dfd.resolve({ domain: realDomain, ok: false });
                        }
                    }).fail(function(xhr) {
                        $('#add-cf-loader').hide();
                        showToast('error', 'CF Sync Error', (xhr && xhr.responseJSON && xhr.responseJSON.err) || 'cf-sync-failed');
                        dfd.resolve({ domain: realDomain, ok: false });
                    });
                }).fail(function(xhr) {
                    showToast('error', 'cPanel Sync Error', (xhr && xhr.responseJSON && xhr.responseJSON.err) || 'cpanel-request-failed');
                    dfd.resolve({ domain: realDomain, ok: false });
                });
            }).fail(function(xhr) {
                showToast('error', 'Add Failed', (xhr && xhr.responseJSON && xhr.responseJSON.err) || 'Request failed');
                dfd.resolve({ domain: domain, ok: false });
            });
            return dfd.promise();
        }

        function resetAddModal() {
            $('#frm_add').show();
            $('#ns-info-list').closest('blockquote').show();
            $('#add-cf-panel').css('display', 'none');
            $('#add-cf-loader').css('display', 'none');
            $('#add-cf-ns-block').css('display', 'none');
            $('#add-cf-ns-panel').html('');
        }

        // Process the validated list one domain at a time (sequential, like
        // Sync All) to avoid hammering cPanel/Cloudflare.
        function runAddBatch(domains, withCf) {
            var btn = $('#btn_add'), i = 0, ok = 0, fail = 0, nsShown = false;
            btn.prop('disabled', true);
            function next() {
                if (i >= domains.length) {
                    btn.prop('disabled', false).text('Save');
                    loadData();
                    showToast(fail ? 'error' : 'success', 'Add Selesai', ok + ' sukses' + (fail ? ', ' + fail + ' gagal' : '') + ' dari ' + domains.length + ' domain.');
                    if (!nsShown) { $('#add_model').modal('hide'); }
                    return;
                }
                var d = domains[i]; i++;
                btn.text('Saving ' + i + '/' + domains.length + '…');
                addDomainPipeline(d, withCf).always(function(r) {
                    if (r && r.ok) { ok++; if (r.nsShown) { nsShown = true; } } else { fail++; }
                    next();
                });
            }
            next();
        }

        $("#btn_add").on('click', function() {
            var domains = parseDomainList($("#domain").val());
            if (domains.length < 1) {
                Swal.fire({ allowOutsideClick: false, type: 'error', title: 'Oops...', text: 'Masukkan minimal 1 domain.' });
                return false;
            }
            if (domains.length > ADD_MAX_DOMAINS) {
                Swal.fire({ allowOutsideClick: false, type: 'error', title: 'Terlalu banyak', text: 'Maksimal ' + ADD_MAX_DOMAINS + ' domain per submit (Anda memasukkan ' + domains.length + ').' });
                return false;
            }
            runAddBatch(domains, $('#cf-add-toggle').is(':checked'));
        });
        $("#btn_edit").on('click', function() { ajaxAction('edit'); });

        loadData();

        // ── CF Config modal ──────────────────────────────────────────────
        var cfDefaultNs = [];

        $('#command-cf-config').on('click', function() {
            $('#cfg_status').hide().text('');
            $('#cfg_cf_token').val('').attr('type', 'password').attr('placeholder', 'Token baru…').removeAttr('data-masked');
            $('#cfg_cf_account').val('');
            $('#cfg_cf_ip').val('');
            $('#cfg_cf_ns1').val('');
            $('#cfg_cf_ns2').val('');
            $('#cfg_cf_ns3').val('');
            $('#cfg_cf_ns4').val('');
            $.ajax({ type: 'GET', url: 'cf.php', data: { action: 'config_get' }, dataType: 'json' })
                .done(function(data) {
                    if (data && data.ok && data.config) {
                        var masked = data.config.CF_API_TOKEN || '';
                        if (masked !== '') {
                            $('#cfg_cf_token').val(masked).attr('data-masked', '1').attr('placeholder', '');
                        } else {
                            $('#cfg_cf_token').attr('placeholder', 'Belum diset');
                        }
                        $('#cfg_cf_account').val(data.config.CF_ACCOUNT_ID || '');
                        $('#cfg_cf_ip').val(data.config.CF_SERVER_IP || '');
                        $('#cfg_cf_ns1').val(data.config.CF_NS1 || '');
                        $('#cfg_cf_ns2').val(data.config.CF_NS2 || '');
                        $('#cfg_cf_ns3').val(data.config.CF_NS3 || '');
                        $('#cfg_cf_ns4').val(data.config.CF_NS4 || '');
                        cfDefaultNs = normalizeNsList([data.config.CF_NS1, data.config.CF_NS2, data.config.CF_NS3, data.config.CF_NS4]);
                    }
                });
            $('#cf_config_modal').modal('show');
        });

        $('#cfg_cf_token_toggle').on('click', function() {
            var inp = $('#cfg_cf_token');
            inp.attr('type', inp.attr('type') === 'password' ? 'text' : 'password');
        });

        $('#cfg_cf_token').on('input', function() {
            $(this).removeAttr('data-masked');
        });


        // CreateAdditionalTokensInit
        $('#cfg_create_additional_token').on('click', function() {
            var btn = $(this);
            var btnOriginalHtml = btn.html();
            btn.prop('disabled', true).html(ngixButtonSpinner + 'Generating…');
            var creatorToken = $('#cfg_cf_token').val().trim();
            var accountId = $('#cfg_cf_account').val().trim();
            var st = $('#cfg_status').show();

            if (creatorToken === '' || $('#cfg_cf_token').attr('data-masked') === '1' || creatorToken.indexOf('•') !== -1 || creatorToken.indexOf('*') !== -1) {
                st.removeClass().text('Paste token asli dari template Create Additional Tokens dulu.');
                btn.prop('disabled', false).html(btnOriginalHtml || 'Create Additional Tokens');
                return;
            }

            st.text('Creating additional token…');

            $.ajax({
                type: 'POST',
                url: '/api/cf_create_token.php',
                data: {
                    creator_token: creatorToken,
                    cf_account_id: accountId,
                    csrf_context: 'admin_panel',
                    csrf_token: <?= json_encode($csrfToken, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT); ?>
                },
                dataType: 'json'
            }).done(function(data) {
                if (typeof data === 'string') {
                    try { data = JSON.parse(data); } catch (e) {}
                }

                if (!data || data.ok !== true || !data.token) {
                    var err = (data && (data.err || data.error)) ? (data.err || data.error) : 'Gagal membuat token';
                    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(', ');
                    }
                    st.text(err);
                    showToast('error', 'CF Token Failed', err);
                    return;
                }

                var optionalMissingList = (data.optional_missing_rows && data.optional_missing_rows.length)
                    ? data.optional_missing_rows.map(function(r) { return r.scope + ' — ' + r.permission + ':' + r.access; })
                    : [];

                $('#cfg_cf_token').val(data.token).attr('type', 'password').removeAttr('data-masked');
                if (data.account_id) {
                    $('#cfg_cf_account').val(data.account_id);
                }

                var payload = {
                    action: 'config_save',
                    CF_API_TOKEN: data.token,
                    CF_ACCOUNT_ID: data.account_id || accountId,
                    CF_SERVER_IP: $('#cfg_cf_ip').val().trim(),
                    CF_NS1: $('#cfg_cf_ns1').val().trim(),
                    CF_NS2: $('#cfg_cf_ns2').val().trim(),
                    CF_NS3: $('#cfg_cf_ns3').val().trim(),
                    CF_NS4: $('#cfg_cf_ns4').val().trim()
                };

                $.ajax({ type: 'POST', url: 'cf.php', data: payload, dataType: 'json' })
                    .done(function(saveData) {
                        if (saveData && saveData.ok) {
                            st.html('<strong>Additional token created & saved.</strong>' + (optionalMissingList.length ? '<div style="margin-top:5px;font-size:11px;opacity:.75;">Optional unavailable: ' + escHtml(optionalMissingList.join(', ')) + '</div>' : '') + '<div class="cfg-token-copy-box"><strong>Copy now if needed:</strong>' + escHtml(data.token) + '</div>');
                            showToast('success', 'CF Token Created', optionalMissingList.length ? 'Saved with optional permission warnings.' : 'Additional token saved.');
                        } else {
                            st.html('<strong>Token created, save failed. Copy now:</strong>' + (optionalMissingList.length ? '<div style="margin-top:5px;font-size:11px;opacity:.75;">Optional unavailable: ' + escHtml(optionalMissingList.join(', ')) + '</div>' : '') + '<div class="cfg-token-copy-box">' + escHtml(data.token) + '</div>');
                            showToast('error', 'CF Token Save Failed', 'Copy token manually.');
                        }
                    })
                    .fail(function() {
                        st.html('<strong>Token created, save request failed. Copy now:</strong>' + (optionalMissingList.length ? '<div style="margin-top:5px;font-size:11px;opacity:.75;">Optional unavailable: ' + escHtml(optionalMissingList.join(', ')) + '</div>' : '') + '<div class="cfg-token-copy-box">' + escHtml(data.token) + '</div>');
                        showToast('error', 'CF Token Save Error', 'Copy token manually.');
                    });
            }).fail(function(xhr) {
                var response = xhr && xhr.responseJSON ? xhr.responseJSON : null;
                if (!response && xhr && typeof xhr.responseText === 'string' && xhr.responseText !== '') {
                    try { response = JSON.parse(xhr.responseText); } catch (e) {}
                }

                if (response && response.ok === true && response.token) {
                    $('#cfg_cf_token').val(response.token).attr('type', 'password').removeAttr('data-masked');
                    if (response.account_id) { $('#cfg_cf_account').val(response.account_id); }
                    st.html('<strong>Additional token created.</strong><div class="cfg-token-copy-box"><strong>Copy now if needed:</strong>' + escHtml(response.token) + '</div>');
                    showToast('success', 'CF Token Created', 'Token created; HTTP status was inconsistent.');
                    return;
                }

                var msg = 'Request gagal';
                if (response && (response.err || response.error)) {
                    msg = response.err || response.error;
                }
                st.text(msg);
                showToast('error', 'CF Token Error', msg);
            }).always(function() {
                btn.prop('disabled', false).html(btnOriginalHtml || 'Create Additional Tokens');
            });
        });

        $('#cfg_save_btn').on('click', function() {
            var btn = $(this);
            var btnOriginalHtml = btn.html();
            btn.prop('disabled', true).html(ngixButtonSpinner + 'Simpan');
            var payload = {
                action:         'config_save',
                CF_API_TOKEN:   $('#cfg_cf_token').val().trim(),
                CF_ACCOUNT_ID:  $('#cfg_cf_account').val().trim(),
                CF_SERVER_IP:   $('#cfg_cf_ip').val().trim(),
                CF_NS1:         $('#cfg_cf_ns1').val().trim(),
                CF_NS2:         $('#cfg_cf_ns2').val().trim(),
                CF_NS3:         $('#cfg_cf_ns3').val().trim(),
                CF_NS4:         $('#cfg_cf_ns4').val().trim()
            };
            $.ajax({ type: 'POST', url: 'cf.php', data: payload, dataType: 'json' })
                .done(function(data) {
                    var st = $('#cfg_status').show();
                    if (data && data.ok) {
                        var saved = data.saved && data.saved.length ? data.saved.join(', ') : 'Tidak ada perubahan';
                        st.css({ background: 'var(--blue-soft)', border: '1px solid var(--blue-line)', color: 'var(--text)' }).html('<strong>Tersimpan:</strong> ' + escHtml(saved));
                        if (data.saved && data.saved.length) {
                            showToast('success', 'CF Config Saved', saved);
                        }
                    } else {
                        st.css({ background: 'var(--danger-soft)', border: '1px solid rgba(108,117,125,.28)', color: 'var(--danger)' }).text((data && data.err) || 'Gagal menyimpan');
                    }
                })
                .fail(function() {
                    $('#cfg_status').show().css({ background: 'var(--danger-soft)', color: 'var(--danger)' }).text('Request gagal');
                })
                .always(function() { btn.prop('disabled', false).html(btnOriginalHtml); });
        });
    });
    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="<?= adminEsc($nonce); ?>">.cfg-token-copy-box{margin-top:8px;padding:7px 9px;border:1px solid var(--line);border-radius:var(--radius);background:var(--panel-soft);font-size:11px;line-height:1.4;word-break:break-all}.cfg-token-copy-box strong{display:block;margin-bottom:3px;color:var(--text-strong)}#logout-toast{position:fixed;right:14px;bottom:14px;z-index:10060;display:none;padding:9px 12px;border:1px solid var(--line);border-left:3px solid var(--accent);border-radius:var(--radius);background:var(--panel2);color:var(--text);box-shadow:0 2px 12px rgba(0,0,0,.10);font-size:12px}#logout-toast.show{display:block;animation:_t-in .16s ease both}input:focus,textarea:focus,select:focus,button:focus,.form-control:focus,.btn:focus,a:focus{outline:none!important}/* ── Extended components ── */.card{border:1px solid var(--line);background:var(--panel);border-radius:var(--radius)}.btn-ghost{background:var(--faded)!important;color:var(--inverse)!important;border-color:var(--line)!important;box-shadow:none!important}.btn-ghost:hover{background:var(--panel-soft)!important;color:var(--text-strong)!important}.chip{display:inline-flex;align-items:center;padding:2px 8px;border:1px solid var(--line-soft);background:var(--panel-soft);border-radius:var(--radius);font-size:11px;color:var(--text);line-height:1.4}.toast{position:fixed;right:18px;bottom:18px;z-index:10070;display:none;align-items:center;gap:8px;background:var(--inverse);color:var(--faded);padding:10px 12px;font-size:12px;border:1px solid var(--line-soft);border-radius:var(--radius);box-shadow:var(--shadow-modal)}.toast.show{display:flex}canvas{display:block;width:100%;height:430px;border:1px solid var(--line);background:var(--panel);border-radius:var(--radius)}</style>
<style nonce="<?= adminEsc($nonce); ?>">.admin-toast-root{right:14px!important;bottom:14px!important;gap:6px!important;z-index:10070!important}.admin-toast{font-family:var(--mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);position:relative!important;display:grid!important;grid-template-columns:3px minmax(0,1fr)!important;column-gap:9px!important;align-items:start!important;min-width:190px!important;max-width:320px!important;padding:8px 10px 8px 0!important;border:1px solid var(--line)!important;border-radius:var(--radius)!important;background:#fff!important;color:var(--text)!important;box-shadow:0 4px 14px rgba(31,34,33,.08)!important;font-size:12px!important;line-height:1.35!important}.admin-toast:before{content:"";grid-row:1/3;width:3px;align-self:stretch;background:var(--muted);border-radius:var(--radius) 0 0 var(--radius)}.admin-toast strong{margin:0 0 1px!important;color:var(--text-strong)!important;font-size:12px!important;line-height:1.3}.admin-toast span{min-width:0;color:var(--text)!important;opacity:.84;word-break:break-word}.admin-toast--success:before{background:var(--success)!important}.admin-toast--error:before{background:var(--danger)!important}.admin-toast--info:before{background:var(--muted)!important}.admin-toast--removing{animation:_t-out .14s ease both!important}.toast{background:#fff!important;color:var(--text)!important;border:1px solid var(--line)!important;box-shadow:0 4px 14px rgba(31,34,33,.08)!important}#logout-toast{right:14px!important;bottom:14px!important;padding:8px 10px!important;border:1px solid var(--line)!important;border-left:3px solid var(--accent)!important;border-radius:var(--radius)!important;background:#fff!important;color:var(--text-strong)!important;box-shadow:0 4px 14px rgba(31,34,33,.08)!important;font-size:12px!important;line-height:1.35!important}
/* rows-per-page (.ddt) dropdown fix: tbl-toolbar has backdrop-filter (a stacking context) that traps the menu's z-index behind the table (z-index:3); lift the whole toolbar above the table so the open menu renders on top */
.tbl-toolbar{position:relative!important;z-index:30!important}</style>
<div id="logout-toast">Logging out…</div>
<style nonce="<?= adminEsc($nonce); ?>">
/* 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="<?= adminEsc($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="<?= adminEsc($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="<?= adminEsc($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;
  border-bottom: 1px solid var(--line) !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="<?= adminEsc($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="<?= adminEsc($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>
<style nonce="<?= adminEsc($nonce); ?>">
/* NGIX patch: public modal/dropdown z-order final fix */
html,
body {
  overflow-x: hidden !important;
}
body.modal-open {
  overflow: hidden !important;
  padding-right: 0 !important;
}
/* Bootstrap backdrop is attached to body. Keep it below the modal, but above the page. */
.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: 12000 !important;
}
.modal-backdrop.in,
.modal-backdrop.fade.in {
  opacity: .48 !important;
}
/* Modals are moved to body by JS below; this prevents parent stacking contexts from putting the backdrop above them. */
.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: 12010 !important;
  -webkit-overflow-scrolling: touch !important;
}
.modal.in,
.modal.show {
  display: block !important;
}
.modal.fade .modal-dialog,
.modal.in .modal-dialog,
.modal.show .modal-dialog {
  transform: none !important;
}
.modal-dialog {
  position: relative !important;
  z-index: 12020 !important;
  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 {
  position: relative !important;
  z-index: 12030 !important;
  width: 100% !important;
  max-height: none !important;
  display: block !important;
  overflow: visible !important;
}
.modal-header,
.modal-footer,
.modal-body {
  position: relative !important;
}
.modal-body {
  max-height: none !important;
  overflow: visible !important;
}
.modal-footer {
  display: flex !important;
  align-items: center !important;
  justify-content: flex-end !important;
  gap: 7px !important;
  flex-wrap: wrap !important;
}
/* Dropdowns must not be clipped by panel/modal overflow. */
.panel,
.panel-default,
.panel.panel-default,
.panel:has(> .ngix-image-backdrop),
.modal-content,
.modal-body,
.bootgrid-header,
.bootgrid-header .actionBar,
.bootgrid-header .actions,
.tbl-toolbar,
.ddt,
.dropdown,
.btn-group {
  overflow: visible !important;
}
.tbl-toolbar,
.bootgrid-header,
.bootgrid-header .actionBar,
.bootgrid-header .actions,
.ddt.open,
.dropdown.open,
.btn-group.open {
  position: relative !important;
  z-index: 12100 !important;
}
.dropdown-menu,
.open > .dropdown-menu,
.ddt-menu,
.ddt-menu.open,
.modal .dropdown-menu,
.modal .open > .dropdown-menu,
.modal .ddt-menu,
.modal .ddt-menu.open,
.bootgrid-header .actions .dropdown-menu,
.bootgrid-header .actions .open > .dropdown-menu {
  z-index: 12120 !important;
  overflow: visible !important;
}
.modal .dropdown-menu,
.modal .ddt-menu,
.modal .ddt-menu.open {
  z-index: 12140 !important;
}
/* The decorative panel backdrop stays scoped to its panel and never sits above controls/dropdowns. */
.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;
  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: .22 !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;
}
#logout-toast,
.admin-toast-root,
.toast {
  z-index: 13000 !important;
}
@supports (height: 100lvh) {
  .modal,
  .modal-backdrop {
    height: 100lvh !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;
  }
}
</style>
<script nonce="<?= adminEsc($nonce); ?>">
/* NGIX patch: move public modals to body to avoid backdrop covering modal */
(function() {
    'use strict';

    function moveModalsToBody() {
        document.querySelectorAll('.modal').forEach(function(modal) {
            if (modal.parentElement !== document.body) {
                document.body.appendChild(modal);
            }
        });
    }

    function normalizeDropdowns() {
        document.addEventListener('click', function(event) {
            var button = event.target && typeof event.target.closest === 'function'
                ? event.target.closest('.ddt-btn,[data-toggle="dropdown"]')
                : null;

            if (!button) {
                return;
            }

            var holder = button.closest('.ddt,.dropdown,.btn-group,.actions');
            if (holder) {
                holder.style.position = 'relative';
                holder.style.zIndex = '12100';
            }
        }, true);
    }

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

    moveModalsToBody();
    normalizeDropdowns();
}());
</script>
</body></html>
