<?php
/** @var string $baseUrl @var string $assetsUrl @var array $user */
/** @var string $detectedUrl @var string $overrideUrl @var string $selfUrl */
/** @var array $audit @var array $mesh @var array $policy */
$csrf = \PpPhp\Core\Security::CSRF()->Token();
$e = static fn ($s) => htmlspecialchars((string) $s, ENT_QUOTES);
$pip = static fn ($lvl) => $lvl === 'ok' ? 'var(--pp-success,#16a34a)' : ($lvl === 'warn' ? 'var(--pp-warning,#d97706)' : 'var(--pp-danger,#dc2626)');
// A help icon carrying an animated tooltip (what the setting does).
$tip = static fn ($text) => '<span class="cc-tip" tabindex="0" data-tip="' . htmlspecialchars((string) $text, ENT_QUOTES) . '"><i class="bi bi-info-circle"></i></span>';
// A "where it operates" badge, colour-coded by scope.
$where = static fn ($scope) => '<span class="cc-where w-' . htmlspecialchars((string) $scope, ENT_QUOTES) . '">' . htmlspecialchars((string) $scope, ENT_QUOTES) . '</span>';
// Count level rows for a card "peek".
$levels = static function (array $rows): array {
    $c = ['ok' => 0, 'warn' => 0, 'bad' => 0];
    foreach ($rows as $r) { $l = $r['level'] ?? 'ok'; $c[$l === 'ok' ? 'ok' : ($l === 'warn' ? 'warn' : 'bad')]++; }
    return $c;
};
$auditC = $levels($audit ?? []);
$perfC  = $levels($perf ?? []);
$peek = static function (array $c): string {
    $out = [];
    if ($c['bad'])  { $out[] = '<span class="cc-pk bad"><span class="dot"></span>' . $c['bad'] . ' fix</span>'; }
    if ($c['warn']) { $out[] = '<span class="cc-pk warn"><span class="dot"></span>' . $c['warn'] . ' check</span>'; }
    if ($c['ok'])   { $out[] = '<span class="cc-pk ok"><span class="dot"></span>' . $c['ok'] . ' ok</span>'; }
    return implode('', $out);
};
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Core Configuration · <?= htmlspecialchars(\PpPhp\Core\Brand::name()) ?></title>
    <?php include constant('BASE_PATH') . '/core/views/favicon.php'; ?>
    <link rel="stylesheet" href="<?= $e($assetsUrl) ?>/bootstrap/css/bootstrap.min.css">
    <link rel="stylesheet" href="<?= $e($assetsUrl) ?>/icons/bootstrap-icons.css">
    <link rel="stylesheet" href="<?= $e($assetsUrl) ?>/sweetalert2/sweetalert2.min.css">
    <script src="<?= $e($assetsUrl) ?>/sweetalert2/sweetalert2.min.js"></script>
    <?php include constant('BASE_PATH') . '/core/views/theme-head.php'; ?>
    <style>
        body { background: var(--pp-app-bg, #0b1120); color: var(--pp-text, #e5e7eb); }
        /* navbar */
        .ppb-topbar { position: sticky; top: 0; z-index: 20; background: var(--pp-surface, #141824);
                      border-bottom: 1px solid var(--pp-border, #242a3a); padding: .6rem 1.1rem;
                      display: flex; align-items: center; gap: .8rem; backdrop-filter: blur(6px); }
        .ppb-topbar .brand { font-weight: 700; display: flex; align-items: center; gap: .5rem; }
        .ppb-topbar .brand i { color: var(--pp-accent, #818cf8); }
        .ppb-topbar .btn-back { display: inline-flex; align-items: center; gap: .35rem; font-size: .86rem;
                                text-decoration: none; color: var(--pp-text, #e5e7eb); background: var(--pp-app-bg, #0b1120);
                                border: 1px solid var(--pp-border, #242a3a); border-radius: 9px; padding: .35rem .7rem; }
        .ppb-topbar .btn-back:hover { border-color: var(--pp-accent, #818cf8); }
        .ppb-me { margin-left: auto; color: var(--pp-text-muted,#9aa3b2); font-size: .85rem; display: inline-flex; align-items: center; gap: .9rem; }

        /* full-width 2-column grid layout */
        .cc-wrap { max-width: 1240px; margin: 0 auto; padding: 1.3rem clamp(1rem,3vw,2rem) 4rem;
                   display: grid; grid-template-columns: 1fr; gap: 1rem; align-items: start; }
        @media (min-width: 1040px) { .cc-wrap { grid-template-columns: 1fr 1fr; } }
        .cc-full { grid-column: 1 / -1; }
        .cc-head h1 { font-weight: 800; letter-spacing: -.02em; margin: .1rem 0 .1rem; font-size: clamp(1.5rem, 3.2vw, 1.95rem); }
        .cc-sub { color: var(--pp-text-muted, #9aa3b2); font-size: .9rem; }

        /* section group heading */
        .cc-group { grid-column: 1 / -1; display: flex; align-items: center; gap: .7rem; margin: .9rem 0 -.1rem;
                    font-size: .74rem; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; color: var(--pp-text-muted,#9aa3b2); }
        .cc-group i { color: var(--pp-accent,#818cf8); font-size: .95rem; }
        .cc-group::after { content: ''; flex: 1; height: 1px; background: var(--pp-border,#242a3a); }

        .cc-card { background: var(--pp-surface, #141824); border: 1px solid var(--pp-border, #242a3a);
                   border-radius: 16px; box-shadow: 0 1px 2px rgba(0,0,0,.15), 0 8px 26px rgba(0,0,0,.16);
                   transition: border-color .2s; }
        .cc-card:hover { border-color: color-mix(in srgb, var(--pp-accent,#6366f1) 40%, var(--pp-border,#242a3a)); }
        /* non-foldable card keeps its own padding */
        .cc-card.cc-plain { padding: 1.1rem 1.25rem; }

        /* foldable card */
        details.cc-card { padding: 0; }
        .cc-sum { list-style: none; cursor: pointer; display: flex; align-items: center; gap: .75rem; padding: .95rem 1.2rem; user-select: none; }
        .cc-sum::-webkit-details-marker { display: none; }
        .cc-ico { width: 2.1rem; height: 2.1rem; flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
                  border-radius: 10px; font-size: 1.02rem; color: var(--pp-accent, #818cf8);
                  background: color-mix(in srgb, var(--pp-accent,#6366f1) 14%, transparent);
                  animation: ccFloat 3.8s ease-in-out infinite; transition: background .2s, color .2s; }
        details.cc-card[open] > .cc-sum .cc-ico, .cc-card:hover .cc-ico { color: #fff; background: var(--pp-accent,#6366f1); animation: none; }
        .cc-t { font-size: 1rem; font-weight: 700; display: flex; align-items: center; gap: .45rem; flex-wrap: wrap; min-width: 0; }
        .cc-peek { margin-left: auto; display: flex; align-items: center; gap: .4rem; }
        .cc-chev { color: var(--pp-text-muted,#9aa3b2); transition: transform .2s; flex: 0 0 auto; }
        details.cc-card[open] > .cc-sum .cc-chev { transform: rotate(180deg); }
        .cc-body { padding: 0 1.25rem 1.2rem; }
        .cc-body > .cc-note:first-child { margin-top: .2rem; }
        .cc-pk { display: inline-flex; align-items: center; gap: .3rem; font-size: .72rem; font-weight: 600; padding: .1rem .5rem; border-radius: 999px; }
        .cc-pk .dot { width: .5rem; height: .5rem; border-radius: 50%; display: inline-block; }
        .cc-pk.ok   { background: color-mix(in srgb,#16a34a 16%,transparent); color: #34d399; } .cc-pk.ok .dot { background: #16a34a; }
        .cc-pk.warn { background: color-mix(in srgb,#d97706 18%,transparent); color: #fbbf24; } .cc-pk.warn .dot { background: #d97706; }
        .cc-pk.bad  { background: color-mix(in srgb,#dc2626 16%,transparent); color: #f87171; } .cc-pk.bad .dot { background: #dc2626; }
        @keyframes ccFloat { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-3px); } }
        @media (prefers-reduced-motion: reduce) { .cc-ico { animation: none !important; } .cc-chev { transition: none; } }

        .cc-note { color: var(--pp-text-muted,#9aa3b2); font-size: .85rem; margin: 0 0 .8rem; }
        .cc-url { font-family: ui-monospace, Menlo, monospace; font-size: .85rem; word-break: break-all; }
        .cc-row { display: grid; grid-template-columns: auto 1fr auto; gap: .7rem; align-items: center;
                  padding: .6rem 0; border-top: 1px solid var(--pp-border,#242a3a); }
        .cc-row:first-child { border-top: 0; }
        .cc-pip { width: .7rem; height: .7rem; border-radius: 50%; flex: 0 0 auto; }
        .cc-badge { font-size: .72rem; font-weight: 600; font-family: ui-monospace, monospace;
                    padding: .12rem .5rem; border-radius: 999px; white-space: nowrap;
                    background: color-mix(in srgb, var(--pp-accent,#6366f1) 14%, transparent); color: var(--pp-accent,#818cf8); }
        .cc-badge.warn { background: color-mix(in srgb, var(--pp-warning,#d97706) 18%, transparent); color: var(--pp-warning,#d97706); }
        .verdict { font-size: .8rem; margin-top: .4rem; min-height: 1.1rem; }
        .verdict.ok { color: var(--pp-success,#16a34a); } .verdict.bad { color: var(--pp-danger,#dc2626); }

        /* animated tooltip on a help icon */
        .cc-tip { position: relative; display: inline-flex; margin-left: .3rem; color: var(--pp-text-muted,#9aa3b2);
                  cursor: help; outline: none; }
        .cc-tip i { font-size: .85rem; transition: color .15s, transform .15s; }
        .cc-tip:hover i, .cc-tip:focus i { color: var(--pp-accent,#818cf8); transform: scale(1.18); }
        .cc-tip::after {
            content: attr(data-tip);
            position: absolute; bottom: 150%; left: 50%; transform: translateX(-50%) translateY(6px) scale(.96);
            width: max-content; max-width: 280px; padding: .5rem .65rem; border-radius: 9px;
            background: var(--pp-surface-raised, #1e2434); color: var(--pp-text,#e5e7eb);
            border: 1px solid var(--pp-border,#333a4f); box-shadow: 0 10px 30px rgba(0,0,0,.4);
            font-size: .76rem; line-height: 1.45; font-weight: 400; text-transform: none; letter-spacing: 0;
            white-space: normal; text-align: left; z-index: 50;
            opacity: 0; pointer-events: none; transition: opacity .16s ease, transform .16s ease;
        }
        .cc-tip:hover::after, .cc-tip:focus::after { opacity: 1; transform: translateX(-50%) translateY(0) scale(1); }
        @media (prefers-reduced-motion: reduce) { .cc-tip i, .cc-tip::after { transition: none; } }

        /* "where it operates" badges */
        .cc-where { font-family: ui-monospace, monospace; font-size: .62rem; font-weight: 600; letter-spacing: .04em;
                    text-transform: uppercase; padding: .1rem .42rem; border-radius: 999px; margin-left: .1rem;
                    border: 1px solid transparent; vertical-align: middle; }
        .cc-where.w-server  { background: color-mix(in srgb, #6366f1 16%, transparent); color: #818cf8; }
        .cc-where.w-build   { background: color-mix(in srgb, #06b6d4 18%, transparent); color: #22d3ee; }
        .cc-where.w-mesh    { background: color-mix(in srgb, #10b981 18%, transparent); color: #34d399; }
        .cc-where.w-clients { background: color-mix(in srgb, #f59e0b 18%, transparent); color: #fbbf24; }
        .cc-where.w-local   { background: color-mix(in srgb, #8b8fa3 20%, transparent); color: var(--pp-text-muted,#9aa3b2); }
        .cc-legend { display:flex; flex-wrap:wrap; gap:.4rem .9rem; font-size:.72rem; color:var(--pp-text-muted,#9aa3b2); margin-top:.5rem; }
        .cc-toolbar { grid-column:1/-1; display:flex; gap:.5rem; flex-wrap:wrap; margin-top:.2rem; }
        .cc-mini { font-size:.78rem; border:1px solid var(--pp-border,#242a3a); background:var(--pp-app-bg,#0b1120); color:var(--pp-text-muted,#9aa3b2);
                   border-radius:8px; padding:.3rem .6rem; cursor:pointer; display:inline-flex; align-items:center; gap:.35rem; }
        .cc-mini:hover { border-color:var(--pp-accent,#818cf8); color:var(--pp-text,#e5e7eb); }

        /* ── Phone (native-app feel) ─────────────────────────────────
           The 1fr grid track grows to a wide child's min-content (long URLs,
           the address chip, the input-group), pushing the whole column past the
           screen. minmax(0,1fr) + min-width:0 lets it shrink; wide bits wrap. */
        @media (max-width: 767.98px) {
            html, body { overflow-x: hidden; }
            .ppb-topbar { flex-wrap: wrap; gap: .4rem; padding-left: .75rem; padding-right: .75rem; }
            .ppb-topbar .brand { min-width: 0; }
            .ppb-me { gap: .5rem; }
            .ppb-me .btn-back { padding: .2rem .45rem; }
            .cc-wrap { grid-template-columns: minmax(0, 1fr) !important; padding-left: .9rem; padding-right: .9rem; }
            .cc-wrap > *, .cc-card, .cc-body, .cc-sum, .cc-head, .cc-t { min-width: 0; max-width: 100%; }
            .cc-url { word-break: break-all; }
            .cc-row { grid-template-columns: 1fr; }          /* stack label|value|badge rows */
            .input-group { flex-wrap: wrap; }                 /* Override input + Verify + Save wrap instead of clipping */
            .cc-wrap input, .cc-wrap select, .cc-wrap textarea { font-size: 16px; max-width: 100%; }
            .ppb-me { font-size: .78rem; gap: .5rem; }
        }
    </style>
</head>
<body>
<div class="ppb-topbar">
    <a href="<?= $e($baseUrl) ?>/settings" class="btn-back"><i class="bi bi-arrow-left"></i> Settings</a>
    <span class="brand"><i class="bi bi-sliders2"></i> Core Configuration</span>
    <div class="ppb-me">
        <a href="#" class="btn-back" data-guide-open="ccGuide" data-tip="What each Core Configuration setting does"><i class="bi bi-question-circle"></i> Guide</a>
        <?php include constant('BASE_PATH') . '/core/views/theme-toggle.php'; ?>
        <span class="d-none d-sm-inline"><i class="bi bi-person-circle"></i> <?= $e($user['username'] ?? 'owner') ?> · owner</span>
    </div>
</div>
<div class="cc-wrap">
    <div class="cc-full cc-head">
        <h1><i class="bi bi-sliders2" style="color:var(--pp-accent,#818cf8)"></i> Core Configuration</h1>
        <p class="cc-sub"><?= $e(\PpPhp\Core\Brand::name()) ?> reads what it can about this install, checks its own posture, and lets you pin or verify any address. Nothing here is secret — the signing key and passwords live elsewhere. Hover the <i class="bi bi-info-circle"></i> on any setting to see what it does.</p>
        <div class="cc-legend">
            <span>Scope:</span>
            <span><span class="cc-where w-local">local</span> this server</span>
            <span><span class="cc-where w-server">server</span> licence authority</span>
            <span><span class="cc-where w-build">build</span> the build engine</span>
            <span><span class="cc-where w-mesh">mesh</span> failover</span>
            <span><span class="cc-where w-clients">clients</span> distributed installs</span>
        </div>
    </div>

    <div class="cc-toolbar">
        <button type="button" class="cc-mini" id="ccExpandAll"><i class="bi bi-arrows-expand"></i> Expand all</button>
        <button type="button" class="cc-mini" id="ccCollapseAll"><i class="bi bi-arrows-collapse"></i> Collapse all</button>
    </div>

    <!-- ══ Identity & branding ══ -->
    <h3 class="cc-group"><i class="bi bi-fingerprint"></i> Identity &amp; branding</h3>

    <!-- Identity -->
    <details class="cc-card" open>
        <summary class="cc-sum">
            <span class="cc-ico"><i class="bi bi-globe2"></i></span>
            <span class="cc-t">This install's address <?= $where('local') ?></span>
            <span class="cc-peek"><span class="cc-pk ok" style="max-width:11rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap"><?= $e($selfUrl ?: $detectedUrl ?: '—') ?></span><i class="bi bi-chevron-down cc-chev"></i></span>
        </summary>
        <div class="cc-body">
            <p class="cc-note">Auto-detected from how you're reached right now. Leave the override blank to keep it automatic; set it only if you serve behind a proxy or want a fixed canonical URL.</p>
            <div class="mb-2">
                <div class="cc-sub">Detected</div>
                <div class="cc-url"><?= $e($detectedUrl ?: '—') ?></div>
            </div>
            <label class="cc-sub" for="ccSelf">Override <span class="text-muted">(optional)</span></label>
            <div class="input-group input-group-sm">
                <input type="url" class="form-control cc-url" id="ccSelf" value="<?= $e($overrideUrl) ?>" placeholder="<?= $e($detectedUrl) ?>">
                <button class="btn btn-outline-secondary" id="ccVerifySelf" type="button"><i class="bi bi-check2-circle"></i> Verify</button>
                <button class="btn btn-primary" id="ccSaveSelf" type="button">Save</button>
            </div>
            <div class="verdict" id="ccSelfVerdict"></div>
            <div class="cc-sub mt-1">Effective: <span class="cc-url" id="ccEffective"><?= $e($selfUrl ?: '—') ?></span></div>
        </div>
    </details>

    <!-- Migrate / Transfer server -->
    <details class="cc-card" open>
        <summary class="cc-sum">
            <span class="cc-ico"><i class="bi bi-hdd-network"></i></span>
            <span class="cc-t">Migrate / transfer server <?= $where('server') ?></span>
            <span class="cc-peek"><i class="bi bi-chevron-down cc-chev"></i></span>
        </summary>
        <div class="cc-body">
            <p class="cc-note">Moving to a new host? Clone the entire platform and everyone's data to another server over SFTP/FTP, verify it, and switch over — your current server stays live until you flip.</p>
            <a href="<?= $e(rtrim((string) $baseUrl, '/')) ?>/settings/migrate" class="btn btn-primary btn-sm"><i class="bi bi-box-arrow-up-right"></i> Open migration console</a>
        </div>
    </details>

    <!-- Branding -->
    <?php $hasLogo = trim((string)($brand['logo'] ?? '')) !== ''; ?>
    <details class="cc-card cc-full" open>
        <summary class="cc-sum">
            <span class="cc-ico"><i class="bi bi-palette2"></i></span>
            <span class="cc-t">Branding <?= $where('local') ?> <?= $tip('Your product name and logo. Set once here — applied across every platform screen: sign-in, account pages, navbars and footers.') ?></span>
            <span class="cc-peek"><span class="cc-pk ok"><span class="dot"></span><?= $e($brand['name'] ?: 'PPPHP') ?></span><i class="bi bi-chevron-down cc-chev"></i></span>
        </summary>
        <div class="cc-body">
            <p class="cc-note">Leave the logo empty to keep the built-in crystal <?= $e(\PpPhp\Core\PlatformSettings::DEFAULT_BRAND_NAME) ?> logo. A custom logo travels with the install (stored inline).</p>
            <?php if (!empty($brandFlash)): ?><div class="verdict ok" style="display:block"><i class="bi bi-check-circle-fill"></i> Branding updated — applied platform-wide.</div><?php endif; ?>
            <?php if (!empty($brandError)): ?><div class="verdict bad" style="display:block"><i class="bi bi-exclamation-triangle-fill"></i> <?= $e($brandError) ?></div><?php endif; ?>
            <div class="cc-brand-grid" style="display:grid;grid-template-columns:1fr;gap:1.1rem;margin-top:.6rem">
                <form method="post" action="<?= $e(rtrim((string)$baseUrl,'/')) ?>/settings/branding/save" enctype="multipart/form-data" style="display:flex;flex-direction:column;gap:.7rem">
                    <input type="hidden" name="_token" value="<?= $e($csrf) ?>">
                    <div>
                        <label class="cc-sub" for="ccBrandName">Product name</label>
                        <input type="text" class="form-control form-control-sm" id="ccBrandName" name="brand_name" maxlength="40" value="<?= $e($brand['name']) ?>" placeholder="PPPHP" required>
                    </div>
                    <div>
                        <label class="cc-sub" for="ccBrandTag">Tagline <span class="text-muted">(optional)</span></label>
                        <input type="text" class="form-control form-control-sm" id="ccBrandTag" name="brand_tagline" maxlength="80" value="<?= $e($brand['tagline']) ?>" placeholder="PHP Rapid Application Builder">
                    </div>
                    <div>
                        <label class="cc-sub" for="ccBrandLogo">Logo <span class="text-muted">— SVG, PNG, JPG, WEBP or GIF · up to 200&nbsp;KB</span></label>
                        <div style="display:flex;align-items:center;gap:.7rem">
                            <span id="ccBrandCur" style="flex:0 0 auto;width:46px;height:46px;display:grid;place-items:center;border-radius:10px;background:var(--pp-app-bg,#0b1120);border:1px solid var(--pp-border,#242a3a);overflow:hidden">
                                <?php if ($hasLogo): ?><img src="<?= $e($brand['logo']) ?>" alt="" style="max-width:100%;max-height:100%;object-fit:contain"><?php else: ?><i class="bi bi-gem" style="color:var(--pp-accent,#818cf8)"></i><?php endif; ?>
                            </span>
                            <input type="file" class="form-control form-control-sm" id="ccBrandLogo" name="brand_logo" accept="image/svg+xml,image/png,image/jpeg,image/webp,image/gif" style="flex:1">
                        </div>
                        <?php if ($hasLogo): ?>
                            <label class="cc-sub" style="display:flex;align-items:center;gap:.4rem;margin-top:.5rem"><input type="checkbox" name="remove_logo" value="1"> Remove custom logo (use the built-in <?= $e(\PpPhp\Core\PlatformSettings::DEFAULT_BRAND_NAME) ?> logo)</label>
                        <?php endif; ?>
                    </div>
                    <div><button type="submit" class="btn btn-primary btn-sm"><i class="bi bi-check-lg"></i> Save branding</button></div>
                </form>
                <div style="border:1px dashed var(--pp-border,#242a3a);border-radius:12px;padding:1rem;background:var(--pp-app-bg,#0b1120);display:flex;align-items:center;gap:1rem">
                    <div id="ccBrandPreviewLogo" style="min-height:44px;display:flex;align-items:center"><?= \PpPhp\Core\Brand::lockup(40) ?></div>
                    <div style="min-width:0">
                        <div class="cc-sub" style="text-transform:uppercase;letter-spacing:.07em;font-size:.62rem">Live preview</div>
                        <div id="ccBrandPreviewName" style="font-weight:800;font-size:1.02rem"><?= $e($brand['name']) ?></div>
                        <div id="ccBrandPreviewTag" class="cc-sub" style="font-size:.8rem"><?= $e($brand['tagline']) ?></div>
                    </div>
                </div>
            </div>
        </div>
    </details>

    <!-- ══ Health ══ -->
    <h3 class="cc-group"><i class="bi bi-activity"></i> Health &amp; performance</h3>

    <!-- Readiness -->
    <details class="cc-card" open>
        <summary class="cc-sum">
            <span class="cc-ico"><i class="bi bi-shield-check"></i></span>
            <span class="cc-t">Readiness</span>
            <span class="cc-peek"><?= $peek($auditC) ?><i class="bi bi-chevron-down cc-chev"></i></span>
        </summary>
        <div class="cc-body">
            <p class="cc-note">Fast local checks. A red row is worth fixing before it becomes a confusing failure later.</p>
            <?php foreach ($audit as $a): ?>
                <div class="cc-row">
                    <span class="cc-pip" style="background: <?= $pip($a['level']) ?>"></span>
                    <div>
                        <div style="font-weight:600"><?= $e($a['label']) ?></div>
                        <div class="cc-sub"><?= $e($a['note']) ?></div>
                    </div>
                    <span class="cc-sub text-uppercase" style="color: <?= $pip($a['level']) ?>; font-family: ui-monospace,monospace; font-size:.72rem;"><?= $e($a['level'] === 'ok' ? 'ok' : ($a['level'] === 'warn' ? 'check' : 'fix')) ?></span>
                </div>
            <?php endforeach; ?>
        </div>
    </details>

    <!-- Performance posture -->
    <details class="cc-card" open>
        <summary class="cc-sum">
            <span class="cc-ico"><i class="bi bi-speedometer2"></i></span>
            <span class="cc-t">Performance posture <?= $where('local') ?></span>
            <span class="cc-peek"><?= $peek($perfC) ?><i class="bi bi-chevron-down cc-chev"></i></span>
        </summary>
        <div class="cc-body">
            <p class="cc-note">The server-level wins that decide raw speed. OPcache is the single biggest lever — it commonly makes responses 3–5× faster.</p>
            <?php foreach ($perf as $a): ?>
                <div class="cc-row">
                    <span class="cc-pip" style="background: <?= $pip($a['level']) ?>"></span>
                    <div>
                        <div style="font-weight:600"><?= $e($a['label']) ?></div>
                        <div class="cc-sub"><?= $e($a['note']) ?></div>
                    </div>
                    <span class="cc-sub text-uppercase" style="color: <?= $pip($a['level']) ?>; font-family: ui-monospace,monospace; font-size:.72rem;"><?= $e($a['level'] === 'ok' ? 'fast' : 'tune') ?></span>
                </div>
            <?php endforeach; ?>
        </div>
    </details>

    <!-- ══ Mesh ══ -->
    <h3 class="cc-group"><i class="bi bi-hdd-network"></i> Server mesh &amp; failover</h3>

    <!-- Mesh -->
    <details class="cc-card cc-full" open>
        <summary class="cc-sum">
            <span class="cc-ico"><i class="bi bi-diagram-2"></i></span>
            <span class="cc-t">Failover order <?= $where('mesh') ?></span>
            <span class="cc-peek"><span class="cc-pk ok"><span class="dot"></span><?= count($mesh) ?> server<?= count($mesh) === 1 ? '' : 's' ?></span><i class="bi bi-chevron-down cc-chev"></i></span>
        </summary>
        <div class="cc-body">
            <p class="cc-note">Every server holds the same signed data, so any can answer. This is the live try-order — the healthiest server first, a just-failed one or a hairpin last. Clients never stop at a dead server.</p>
            <?php if ($mesh === []): ?>
                <div class="cc-sub">No mesh addresses configured yet.</div>
            <?php else: foreach ($mesh as $i => $m): ?>
                <div class="cc-row" data-url="<?= $e($m['url']) ?>">
                    <span class="cc-badge<?= $m['role'] === 'primary' ? '' : ' warn' ?>"><?= $i + 1 ?> · <?= $e($m['role']) ?></span>
                    <div>
                        <div class="cc-url"><?= $e($m['url']) ?></div>
                        <div class="verdict" data-verdict></div>
                    </div>
                    <div class="d-flex gap-2 justify-content-end">
                        <button class="btn btn-outline-secondary btn-sm cc-verify" type="button" data-tip="Check the licence API answers at this address."><i class="bi bi-check2-circle"></i> Verify</button>
                        <?php if ($m['role'] !== 'primary'): ?>
                            <button class="btn btn-outline-danger btn-sm cc-remove" type="button" data-tip="Remove this address from the mesh."><i class="bi bi-trash"></i></button>
                        <?php endif; ?>
                    </div>
                </div>
            <?php endforeach; endif; ?>
            <?php $hair = array_filter($mesh, static fn ($m) => !empty($m['hairpin'])); if ($hair): ?>
                <div class="cc-sub mt-2" style="color: var(--pp-warning,#d97706)"><i class="bi bi-exclamation-triangle"></i> A server resolves to this machine's own IP (hairpin) — it's ordered last automatically, since a server can't reach itself.</div>
            <?php endif; ?>

            <?php if (isset($meshMirrors)): ?>
            <hr style="border-color:var(--pp-border,#242a3a); margin:1.1rem 0 .85rem;">
            <div class="cc-note"><i class="bi bi-pencil-square"></i> Edit the mesh here — fix a typo or a duplicate, or add / remove a backup. The <b>primary</b> is this install's address (top card); paired backups add themselves. On save, duplicates and the primary are dropped and invalid URLs rejected.</div>
            <label class="cc-sub" for="mMirrors">Backup servers <span class="text-muted">(one URL per line)</span><?= $tip('Manual backup addresses handed to every client for DNS-like failover. Enter the full https:// URL of each. Auto-paired backups appear in the order above and are managed on Licensing → Servers & keys.') . $where('mesh') ?></label>
            <textarea class="form-control form-control-sm cc-url" id="mMirrors" rows="3" placeholder="https://backup.ppradhan.com&#10;https://backup2.ppradhan.com"><?= $e(implode("\n", $meshMirrors)) ?></textarea>
            <div class="d-flex align-items-center gap-3 mt-3">
                <button class="btn btn-primary btn-sm" id="mSave"><i class="bi bi-check2"></i> Save backups</button>
                <span class="verdict" id="mVerdict"></span>
            </div>
            <?php endif; ?>
        </div>
    </details>

    <?php if (is_array($policy) || is_array($tuning) || is_array($resil)): ?>
    <!-- ══ Licensing & builds (authority) ══ -->
    <h3 class="cc-group"><i class="bi bi-shield-lock"></i> Licensing &amp; builds <span class="cc-where w-server" style="margin-left:.2rem">authority</span></h3>
    <?php endif; ?>

    <!-- Policy (authority only) -->
    <?php if (is_array($policy)): ?>
    <details class="cc-card cc-full">
        <summary class="cc-sum">
            <span class="cc-ico"><i class="bi bi-toggles"></i></span>
            <span class="cc-t">Licensing &amp; build policy</span>
            <span class="cc-peek"><span class="cc-pk warn"><span class="dot"></span>affects clients</span><i class="bi bi-chevron-down cc-chev"></i></span>
        </summary>
        <div class="cc-body">
        <p class="cc-note">What every licensed installation and server-side build obeys. Changes to build limits, watermark and HTTPS apply immediately; a new trial length applies to installs that read it (and to clients from their next bundle build). Each setting is tagged with where it takes effect.</p>

        <div class="row g-3">
            <div class="col-sm-4">
                <label class="cc-sub" for="pTrial">Free trial length <span class="text-muted">(days)</span><?= $tip('How long a fresh install runs before it must enter a licence key. After the trial, an unlicensed copy locks completely.') . $where('clients') ?></label>
                <input type="number" min="1" max="3650" class="form-control form-control-sm" id="pTrial" value="<?= (int) ($policy['trialDays'] ?? 3) ?>">
            </div>
            <div class="col-12">
                <label class="cc-sub">Global build limits <span class="text-muted">(blank / 0 = unlimited)</span><?= $tip('The DEFAULT number of server-side builds ONE licence may run in each window. All four are enforced together — a build is refused if any set window is used up. This protects your build servers from a runaway or leaked licence. A plan or an individual licence can override any window; leave a window at 0 for no default cap.') . $where('server') ?></label>
                <div class="row g-2">
                    <?php $__gl = $policy['buildLimits'] ?? ['hour' => 0, 'day' => 0, 'week' => 0, 'month' => 0]; ?>
                    <div class="col-6 col-sm-3">
                        <label class="cc-sub small text-muted" for="pLimHour">Per hour</label>
                        <input type="number" min="0" class="form-control form-control-sm cc-blim" id="pLimHour" data-window="hour" value="<?= (int) ($__gl['hour'] ?? 0) ?>">
                    </div>
                    <div class="col-6 col-sm-3">
                        <label class="cc-sub small text-muted" for="pLimDay">Per day</label>
                        <input type="number" min="0" class="form-control form-control-sm cc-blim" id="pLimDay" data-window="day" value="<?= (int) ($__gl['day'] ?? 0) ?>">
                    </div>
                    <div class="col-6 col-sm-3">
                        <label class="cc-sub small text-muted" for="pLimWeek">Per week</label>
                        <input type="number" min="0" class="form-control form-control-sm cc-blim" id="pLimWeek" data-window="week" value="<?= (int) ($__gl['week'] ?? 0) ?>">
                    </div>
                    <div class="col-6 col-sm-3">
                        <label class="cc-sub small text-muted" for="pLimMonth">Per month</label>
                        <input type="number" min="0" class="form-control form-control-sm cc-blim" id="pLimMonth" data-window="month" value="<?= (int) ($__gl['month'] ?? 0) ?>">
                    </div>
                </div>
            </div>
            <div class="col-sm-8">
                <label class="cc-sub" for="pSupport">Support contact<?= $tip('The email a customer sees if their copy ever locks (expired or invalid licence). Carried inside the signed lease, so changing it reaches every install within a day.') . $where('clients') ?></label>
                <input type="email" class="form-control form-control-sm" id="pSupport" value="<?= $e($policy['support'] ?? '') ?>" placeholder="you@example.com">
            </div>
            <div class="col-sm-4 d-flex flex-column justify-content-end gap-2">
                <div class="form-check form-switch">
                    <input class="form-check-input" type="checkbox" id="pWatermark" <?= !empty($policy['watermark']) ? 'checked' : '' ?>>
                    <label class="form-check-label cc-sub" for="pWatermark">Watermark builds<?= $tip('Embed a hidden per-licence fingerprint in every generated app, so a leaked or pirated build traces back to the licence it was built under.') . $where('build') ?></label>
                </div>
                <div class="form-check form-switch">
                    <input class="form-check-input" type="checkbox" id="pHttps" <?= !empty($policy['httpsOnly']) ? 'checked' : '' ?>>
                    <label class="form-check-label cc-sub" for="pHttps">HTTPS-only activation<?= $tip('Refuse licence activation over plain http:// — activation is the one request that carries the licence key itself. Keep on in production.') . $where('server') ?></label>
                </div>
            </div>
        </div>
        <div class="d-flex align-items-center gap-3 mt-3">
            <button class="btn btn-primary btn-sm" id="pSave">Save policy</button>
            <span class="verdict" id="pVerdict"></span>
        </div>
        </div>
    </details>
    <?php endif; ?>

    <!-- Build performance (authority only) -->
    <?php if (is_array($tuning)): ?>
    <details class="cc-card cc-full">
        <summary class="cc-sum">
            <span class="cc-ico"><i class="bi bi-lightning-charge"></i></span>
            <span class="cc-t">Build performance <?= $where('build') ?></span>
            <span class="cc-peek"><i class="bi bi-chevron-down cc-chev"></i></span>
        </summary>
        <div class="cc-body">
        <p class="cc-note">How the build engine trades speed against portability. Defaults are tuned for lightning-fast rebuilds; every change applies to the next build immediately.</p>
        <div class="row g-3">
            <div class="col-sm-6 d-flex flex-column gap-2">
                <div class="form-check form-switch">
                    <input class="form-check-input" type="checkbox" id="tDelta" <?= !empty($tuning['delta']) ? 'checked' : '' ?>>
                    <label class="form-check-label cc-sub" for="tDelta">Delta rebuilds<?= $tip('On a rebuild, send only the files that changed (plus a delete list) instead of the whole app — a design tweak ships a handful of files instead of ~1000. The client verifies and falls back to a full rebuild if anything mismatches.') . $where('build') ?></label>
                </div>
                <div class="form-check form-switch">
                    <input class="form-check-input" type="checkbox" id="tSlim" <?= !empty($tuning['slim']) ? 'checked' : '' ?>>
                    <label class="form-check-label cc-sub" for="tSlim">Asset slimming<?= $tip('Omit static vendor assets (bootstrap, icons, fonts…) that the client already ships, cutting the transfer by 90%+. Turn off only if you need a fully self-contained bundle.') . $where('build') ?></label>
                </div>
            </div>
            <div class="col-sm-3">
                <label class="cc-sub" for="tCacheDays">Cache retention <span class="text-muted">(days)</span><?= $tip('How long a built ZIP is kept on the server so an unchanged rebuild is a disk read, not a re-generation. Higher = more repeat-build speed, more disk.') . $where('server') ?></label>
                <input type="number" min="1" max="365" class="form-control form-control-sm" id="tCacheDays" value="<?= (int) ($tuning['cacheDays'] ?? 14) ?>">
            </div>
            <div class="col-sm-3">
                <label class="cc-sub" for="tCacheMax">Max cached builds<?= $tip('Upper bound on stored build ZIPs before the oldest are pruned. Caps the build cache\'s disk footprint.') . $where('server') ?></label>
                <input type="number" min="10" max="100000" class="form-control form-control-sm" id="tCacheMax" value="<?= (int) ($tuning['cacheMax'] ?? 400) ?>">
            </div>
        </div>
        <div class="d-flex align-items-center gap-3 mt-3">
            <button class="btn btn-primary btn-sm" id="tSave">Save performance</button>
            <span class="verdict" id="tVerdict"></span>
        </div>
        </div>
    </details>
    <?php endif; ?>

    <!-- Mesh resilience & rate limiting (authority only) -->
    <?php if (is_array($resil)): ?>
    <details class="cc-card cc-full">
        <summary class="cc-sum">
            <span class="cc-ico"><i class="bi bi-diagram-3"></i></span>
            <span class="cc-t">Mesh resilience &amp; rate limiting <?= $where('mesh') ?></span>
            <span class="cc-peek"><i class="bi bi-chevron-down cc-chev"></i></span>
        </summary>
        <div class="cc-body">
        <p class="cc-note">How installs ride out a server outage and how the endpoints defend themselves. The mesh values ride the signed lease, so every install adopts them on its next daily check-in — no client reconfiguration.</p>
        <div class="row g-3">
            <div class="col-sm-4">
                <label class="cc-sub" for="rGrace">Outage grace <span class="text-muted">(hours)</span><?= $tip('How long an ALREADY-activated install keeps running when it cannot reach ANY server in the mesh. Counted from its last successful check; resets on every success. After this it locks with a “contact support” notice.') . $where('clients') ?></label>
                <input type="number" min="1" max="8760" class="form-control form-control-sm" id="rGrace" value="<?= (int) ($resil['graceHours'] ?? 72) ?>">
            </div>
            <div class="col-sm-4">
                <label class="cc-sub" for="rHeart">Check-in every <span class="text-muted">(min)</span><?= $tip('How often an install quietly re-checks with the mesh to confirm its licence, learn new server addresses, and pick up a revocation. Runs at most once per interval on a page load.') . $where('clients') ?></label>
                <input type="number" min="5" max="1440" class="form-control form-control-sm" id="rHeart" value="<?= (int) ($resil['heartbeat'] ?? 60) ?>">
            </div>
            <div class="col-sm-4">
                <label class="cc-sub" for="rTimeout">Network timeout <span class="text-muted">(sec)</span><?= $tip('How long a single call to a server waits before giving up and failing over to the next server in the DNS-like order. Lower = snappier failover; too low can trip on a slow link.') . $where('mesh') ?></label>
                <input type="number" min="3" max="120" class="form-control form-control-sm" id="rTimeout" value="<?= (int) ($resil['netTimeout'] ?? 12) ?>">
            </div>
            <div class="col-sm-4">
                <label class="cc-sub" for="rLimit">Failed-attempt limit<?= $tip('How many FAILED activations/renewals from one IP are allowed before it is throttled (HTTP 429). Counts failures only, so thousands of genuine installs renewing never trip it, while a key-guesser hits it in seconds.') . $where('server') ?></label>
                <input type="number" min="3" max="100000" class="form-control form-control-sm" id="rLimit" value="<?= (int) ($resil['rateLimit'] ?? 30) ?>">
            </div>
            <div class="col-sm-4">
                <label class="cc-sub" for="rWindow">…within <span class="text-muted">(min)</span><?= $tip('The rolling window the failed-attempt count is measured over. After this many minutes with no new failures, the count clears.') . $where('server') ?></label>
                <input type="number" min="1" max="1440" class="form-control form-control-sm" id="rWindow" value="<?= (int) ($resil['rateWindow'] ?? 10) ?>">
            </div>
            <div class="col-sm-4 d-flex align-items-end">
                <div class="form-check form-switch">
                    <input class="form-check-input" type="checkbox" id="rSelfHeal" <?= !empty($resil['selfHeal']) ? 'checked' : '' ?>>
                    <label class="form-check-label cc-sub" for="rSelfHeal">Auto self-heal<?= $tip('When a stored server address stops answering (e.g. its document root moved), the install re-probes the address variants and adopts whichever works — no manual reconfiguration. Recommended on.') . $where('mesh') ?></label>
                </div>
            </div>
        </div>
        <div class="d-flex align-items-center gap-3 mt-3">
            <button class="btn btn-primary btn-sm" id="rSave">Save resilience</button>
            <span class="verdict" id="rVerdict"></span>
        </div>
        </div>
    </details>
    <?php endif; ?>
</div>

<script>
(function () {
    const B = <?= json_encode($baseUrl) ?>, CSRF = <?= json_encode($csrf) ?>;
    async function post(path, body) {
        const r = await fetch(B + path, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': CSRF },
            body: JSON.stringify(Object.assign({ _token: CSRF }, body)) });
        return r.json();
    }
    const show = (el, res) => {
        el.textContent = res.message || '';
        el.className = 'verdict ' + (res.ok === false ? 'bad' : (res.ok ? 'ok' : ''));
    };
    // Live brand preview
    (function () {
        var n = document.getElementById('ccBrandName'), t = document.getElementById('ccBrandTag'), f = document.getElementById('ccBrandLogo');
        var pn = document.getElementById('ccBrandPreviewName'), pt = document.getElementById('ccBrandPreviewTag'), pl = document.getElementById('ccBrandPreviewLogo'), cur = document.getElementById('ccBrandCur');
        if (n) n.addEventListener('input', function () { pn.textContent = n.value || 'PPPHP'; });
        if (t) t.addEventListener('input', function () { pt.textContent = t.value; });
        if (f) f.addEventListener('change', function () {
            var file = f.files && f.files[0]; if (!file) return;
            var r = new FileReader();
            r.onload = function () {
                pl.innerHTML = '<img src="' + r.result + '" alt="" style="max-height:44px;width:auto">';
                cur.innerHTML = '<img src="' + r.result + '" alt="" style="max-width:100%;max-height:100%;object-fit:contain">';
            };
            r.readAsDataURL(file);
        });
    })();
    // Expand / collapse all foldable cards
    document.getElementById('ccExpandAll')?.addEventListener('click', function () {
        document.querySelectorAll('details.cc-card').forEach(function (d) { d.open = true; });
    });
    document.getElementById('ccCollapseAll')?.addEventListener('click', function () {
        document.querySelectorAll('details.cc-card').forEach(function (d) { d.open = false; });
    });
    document.getElementById('ccSaveSelf')?.addEventListener('click', async () => {
        const res = await post('/settings/core-config/save', { self_url: document.getElementById('ccSelf').value });
        show(document.getElementById('ccSelfVerdict'), { ok: res.success, message: res.message });
        if (res.selfUrl) document.getElementById('ccEffective').textContent = res.selfUrl;
    });
    document.getElementById('ccVerifySelf')?.addEventListener('click', async () => {
        const v = document.getElementById('ccSelfVerdict'); v.textContent = 'Checking…'; v.className = 'verdict';
        const url = document.getElementById('ccSelf').value || <?= json_encode($detectedUrl) ?>;
        show(v, await post('/settings/core-config/verify', { url }));
    });
    document.querySelectorAll('.cc-verify').forEach(btn => btn.addEventListener('click', async () => {
        const row = btn.closest('[data-url]'), v = row.querySelector('[data-verdict]');
        v.textContent = 'Checking…'; v.className = 'verdict';
        show(v, await post('/settings/core-config/verify', { url: row.dataset.url }));
    }));
    document.querySelectorAll('.cc-remove').forEach(btn => btn.addEventListener('click', async () => {
        const row = btn.closest('[data-url]'), url = row.dataset.url;
        const ask = await Swal.fire({
            icon: 'warning', title: 'Remove this address?',
            html: 'Remove <code>' + url.replace(/[&<>"]/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c])) + '</code> from the server mesh?',
            showCancelButton: true, confirmButtonText: 'Remove', confirmButtonColor: '#ef4444'
        });
        if (!ask.isConfirmed) return;
        const v = row.querySelector('[data-verdict]'); v.textContent = 'Removing…'; v.className = 'verdict';
        const res = await post('/settings/core-config/mesh-remove', { url });
        if (res.success) {
            row.style.transition = 'opacity .25s'; row.style.opacity = '0';
            setTimeout(() => row.remove(), 260);
        } else { show(v, { ok: false, message: res.message }); }
    }));
    document.getElementById('mSave')?.addEventListener('click', async () => {
        const v = document.getElementById('mVerdict'); v.textContent = 'Saving…'; v.className = 'verdict';
        const res = await post('/settings/core-config/mesh', { mirrors: document.getElementById('mMirrors').value });
        show(v, { ok: res.success, message: res.message });
        if (res.success && Array.isArray(res.mirrors)) document.getElementById('mMirrors').value = res.mirrors.join('\n');
    });
    document.getElementById('pSave')?.addEventListener('click', async () => {
        const v = document.getElementById('pVerdict'); v.textContent = 'Saving…'; v.className = 'verdict';
        const res = await post('/settings/core-config/policy', {
            trial_days:   document.getElementById('pTrial').value,
            build_limits: (function () {
                var bl = {};
                Array.prototype.forEach.call(document.querySelectorAll('.cc-blim'), function (c) {
                    bl[c.dataset.window] = c.value === '' ? 0 : (parseInt(c.value, 10) || 0);
                });
                return bl;
            })(),
            watermark:    document.getElementById('pWatermark').checked,
            https_only:   document.getElementById('pHttps').checked,
            support:      document.getElementById('pSupport').value
        });
        show(v, { ok: res.success, message: res.message });
    });
    document.getElementById('tSave')?.addEventListener('click', async () => {
        const v = document.getElementById('tVerdict'); v.textContent = 'Saving…'; v.className = 'verdict';
        const res = await post('/settings/core-config/policy', {
            delta:      document.getElementById('tDelta').checked,
            slim:       document.getElementById('tSlim').checked,
            cache_days: document.getElementById('tCacheDays').value,
            cache_max:  document.getElementById('tCacheMax').value
        });
        show(v, { ok: res.success, message: res.message });
    });
    document.getElementById('rSave')?.addEventListener('click', async () => {
        const v = document.getElementById('rVerdict'); v.textContent = 'Saving…'; v.className = 'verdict';
        const res = await post('/settings/core-config/policy', {
            runtime_grace: document.getElementById('rGrace').value,
            heartbeat:     document.getElementById('rHeart').value,
            net_timeout:   document.getElementById('rTimeout').value,
            rate_limit:    document.getElementById('rLimit').value,
            rate_window:   document.getElementById('rWindow').value,
            self_heal:     document.getElementById('rSelfHeal').checked
        });
        show(v, { ok: res.success, message: res.message });
    });
})();
</script>
<?php
$ownerGuide = [
    'id' => 'ccGuide',
    'eyebrow' => 'Owner guide',
    'title' => 'Core Configuration',
    'subtitle' => 'What this install knows about itself, and the settings that reach your licensed installs.',
    'icon' => 'sliders2',
    'topics' => [
        ['id' => 'what', 'label' => 'Overview', 'icon' => 'stars',
         'keywords' => 'overview core config scope local server mesh clients',
         'body' => '<p class="og-lead">Core Configuration is the owner\'s control room: it reads what it can about this install, checks its posture, and lets you pin or verify addresses. Nothing here is a secret — the signing key and passwords live elsewhere.</p>'
            . '<p>Each card carries a <b>scope</b> tag: <b>local</b> (this server), <b>server</b> (the licence authority), <b>build</b> (the build engine), <b>mesh</b> (failover), <b>clients</b> (your distributed installs). Cards fold — use <b>Expand all</b> / <b>Collapse all</b>.</p>'],
        ['id' => 'identity', 'label' => 'Address & branding', 'icon' => 'globe2',
         'keywords' => 'self url address override branding logo name migrate',
         'body' => '<h3>This install\'s address</h3><p>Auto-detected from how you\'re reached. Leave the override blank to keep it automatic; set it only behind a proxy or for a fixed canonical URL. <b>Verify</b> checks the licence API answers there.</p>'
            . '<h3>Branding</h3><p>Set the product name, tagline and logo once — applied across every platform screen. Blank logo keeps the built-in crystal mark.</p>'
            . '<h3>Migrate / transfer server</h3><p>Opens the migration console to clone this whole platform to a new host.</p>'],
        ['id' => 'health', 'label' => 'Health & performance', 'icon' => 'activity',
         'keywords' => 'readiness performance opcache checks',
         'body' => '<p><b>Readiness</b> runs fast local checks — a red row is worth fixing before it becomes a confusing failure. <b>Performance posture</b> flags the server-level wins (OPcache is the biggest lever, often 3–5× faster). The card headers show a live count (e.g. "1 fix · 2 ok").</p>'],
        ['id' => 'mesh', 'label' => 'Mesh & failover', 'icon' => 'diagram-2',
         'keywords' => 'mesh failover backup servers mirrors order hairpin',
         'body' => '<p>Every server holds the same signed data, so any can answer. This card shows the live try-order (healthiest first; a just-failed or hairpin server last). Add backup server URLs (one per line) that get handed to clients for DNS-like failover; the primary is this install\'s own address.</p>'],
        ['id' => 'policy', 'label' => 'Licensing & builds', 'icon' => 'shield-lock',
         'keywords' => 'policy trial build limits watermark https support resilience rate limit',
         'body' => '<p>Owner-only. These reach every licensed install and server-side build:</p><ul class="og-list">'
            . '<li><b>Policy</b> — free-trial length, per-licence build limits (hour/day/week/month), support contact, watermark builds, HTTPS-only activation.</li>'
            . '<li><b>Build performance</b> — delta rebuilds, asset slimming, cache retention.</li>'
            . '<li><b>Mesh resilience &amp; rate limiting</b> — outage grace, check-in interval, network timeout, failed-attempt throttle, auto self-heal.</li>'
            . '</ul><div class="og-tip">Build limits, watermark and HTTPS apply immediately; trial length and mesh values ride the signed lease and reach installs on their next daily check-in.</div>'],
    ],
];
include constant('BASE_PATH') . '/core/views/owner-ux.php';
?>
</body>
</html>
