<?php
declare(strict_types=1);

ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
error_reporting(E_ALL);

define('CMS_ROOT', dirname(__DIR__));
define('ADMIN_ROOT', __DIR__);
define('CMS_PLUGIN_DIR', CMS_ROOT . '/content/plugins');
define('CMS_THEME_BASE_DIR', CMS_ROOT . '/content/themes');

require_once CMS_ROOT . '/core/config.php';
require_once CMS_ROOT . '/core/db.php';
require_once CMS_ROOT . '/core/platform.php';
require_once CMS_ROOT . '/core/hooks.php';
require_once CMS_ROOT . '/core/shortcodes.php';
require_once CMS_ROOT . '/core/blocks.php';
require_once CMS_ROOT . '/core/plugins.php';

$db = $GLOBALS['db'] ?? db_connection();
$db1 = $GLOBALS['db1'] ?? null;
$GLOBALS['db'] = $db;
$GLOBALS['db1'] = $db1;

if (function_exists('cms_platform_ensure_table_columns')) {
    cms_platform_ensure_table_columns();
}

if (!$db instanceof PDO) {
    die('DB not connected');
}

$sessionPath = CMS_ROOT . '/core/sessions';
if (!is_dir($sessionPath)) {
    @mkdir($sessionPath, 0775, true);
}
if (is_dir($sessionPath) && is_writable($sessionPath)) {
    ini_set('session.save_path', $sessionPath);
    session_save_path($sessionPath);
}

if (session_status() !== PHP_SESSION_ACTIVE) {
    @session_start();
}

header('X-Frame-Options: SAMEORIGIN');
header('X-Content-Type-Options: nosniff');
header('Referrer-Policy: strict-origin-when-cross-origin');
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');

if (!function_exists('admin_csrf_token')) {
    function admin_csrf_token(): string
    {
        if (empty($_SESSION['admin_csrf_token'])) {
            $_SESSION['admin_csrf_token'] = bin2hex(random_bytes(32));
        }

        return $_SESSION['admin_csrf_token'];
    }
}

if (!function_exists('admin_verify_csrf')) {
    function admin_verify_csrf(string $token): bool
    {
        return isset($_SESSION['admin_csrf_token']) && hash_equals($_SESSION['admin_csrf_token'], $token);
    }
}

if (!function_exists('admin_is_authenticated')) {
    function admin_is_authenticated(): bool
    {
        return cms_current_user() !== null;
    }
}

if (!function_exists('admin_redirect')) {
    function admin_redirect(string $location): never
    {
        header('Location: ' . $location);
        exit;
    }
}

if (!function_exists('admin_escape')) {
    function admin_escape(string $value): string
    {
        return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
    }
}

if (!function_exists('admin_is_ajax')) {
    function admin_is_ajax(): bool
    {
        return strtolower((string) ($_SERVER['HTTP_X_REQUESTED_WITH'] ?? '')) === 'xmlhttprequest'
            || (string) ($_POST['response_format'] ?? '') === 'json';
    }
}

if (!function_exists('admin_json_response')) {
    function admin_json_response(array $payload, int $statusCode = 200): never
    {
        http_response_code($statusCode);
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode($payload, JSON_UNESCAPED_SLASHES);
        exit;
    }
}

if (!function_exists('admin_current_config')) {
    function admin_current_config(): array
    {
        static $config = null;
        if (!is_array($config)) {
            $config = cms_load_config(CMS_ROOT);
        }

        return $config;
    }
}

if (!function_exists('admin_refresh_state')) {
    function admin_refresh_state(bool $loadPlugins = true): array
    {
        $config = cms_load_config(CMS_ROOT);
        cms_platform_boot($config);

        $GLOBALS['cms_actions'] = [];
        $GLOBALS['cms_filters'] = [];
        $GLOBALS['cms_shortcodes'] = [];
        $GLOBALS['cms_blocks'] = [];

        require_once CMS_ROOT . '/core/hooks.php';
        require_once CMS_ROOT . '/core/shortcodes.php';
        require_once CMS_ROOT . '/core/blocks.php';
        require_once CMS_ROOT . '/core/plugins.php';

        $activePlugins = cms_get_active_plugins($config);
        if ($loadPlugins) {
            load_plugins(CMS_PLUGIN_DIR, $activePlugins);
            if (function_exists('do_action')) {
                do_action('admin_menu');
            }
        }

        return [
            'config' => $config,
            'availableThemes' => cms_available_themes(CMS_ROOT),
            'themeRegistry' => function_exists('cms_themes_registry') ? cms_themes_registry(CMS_ROOT) : [],
            'pluginRegistry' => cms_get_plugins(CMS_PLUGIN_DIR, $activePlugins),
            'registeredBlocks' => array_keys(get_registered_blocks()),
            'dbAvailable' => db_connection() instanceof PDO,
        ];
    }
}

if (!function_exists('admin_active_theme')) {
    function admin_active_theme(array $config): string
    {
        return cms_active_theme($config);
    }
}

if (!function_exists('admin_theme_dir')) {
    function admin_theme_dir(array $config): string
    {
        return CMS_THEME_BASE_DIR . '/' . admin_active_theme($config);
    }
}

if (!function_exists('admin_theme_allowed_extensions')) {
    function admin_theme_allowed_extensions(): array
    {
        return ['php', 'html', 'css', 'js'];
    }
}

if (!function_exists('admin_theme_upload_extensions')) {
    function admin_theme_upload_extensions(): array
    {
        return ['jpg', 'jpeg', 'png', 'gif', 'webp'];
    }
}

if (!function_exists('admin_theme_disallowed_filenames')) {
    function admin_theme_disallowed_filenames(): array
    {
        return ['.htaccess'];
    }
}

if (!function_exists('admin_theme_dir_realpath')) {
    function admin_theme_dir_realpath(array $config): ?string
    {
        $themeDir = admin_theme_dir($config);
        $realThemeDir = realpath($themeDir);

        if ($realThemeDir === false || !is_dir($realThemeDir)) {
            return null;
        }

        return $realThemeDir;
    }
}

if (!function_exists('admin_path_is_within')) {
    function admin_path_is_within(string $path, string $basePath): bool
    {
        $normalizedPath = str_replace('\\', '/', $path);
        $normalizedBase = rtrim(str_replace('\\', '/', $basePath), '/');

        return $normalizedPath === $normalizedBase || strpos($normalizedPath, $normalizedBase . '/') === 0;
    }
}

if (!function_exists('admin_theme_entries')) {
    function admin_theme_entries(array $config): array
    {
        $realThemeDir = admin_theme_dir_realpath($config);
        if ($realThemeDir === null) {
            return [];
        }

        $iterator = new RecursiveIteratorIterator(
            new RecursiveDirectoryIterator($realThemeDir, FilesystemIterator::SKIP_DOTS),
            RecursiveIteratorIterator::SELF_FIRST
        );

        $allowedExtensions = admin_theme_allowed_extensions();
        $blockedNames = admin_theme_disallowed_filenames();
        $entries = [];

        foreach ($iterator as $entry) {
            if (!$entry instanceof SplFileInfo) {
                continue;
            }

            $fullPath = $entry->getPathname();
            $relativePath = str_replace('\\', '/', substr($fullPath, strlen($realThemeDir) + 1));
            if ($relativePath === '' || $relativePath === false) {
                continue;
            }

            if (preg_match('#(^|/)(backup|backups)(/|$)#i', $relativePath)) {
                continue;
            }

            $basename = basename($relativePath);
            if (in_array($basename, $blockedNames, true)) {
                continue;
            }

            if ($entry->isDir()) {
                $entries[] = [
                    'type' => 'dir',
                    'path' => $relativePath,
                    'name' => $basename,
                ];
                continue;
            }

            $extension = strtolower($entry->getExtension());
            if (!in_array($extension, $allowedExtensions, true)) {
                continue;
            }

            $entries[] = [
                'type' => 'file',
                'path' => $relativePath,
                'name' => $basename,
                'size' => $entry->getSize(),
                'mtime' => $entry->getMTime(),
            ];
        }

        usort($entries, static function (array $left, array $right): int {
            if ($left['type'] !== $right['type']) {
                return $left['type'] === 'dir' ? -1 : 1;
            }

            return strcmp((string) $left['path'], (string) $right['path']);
        });

        return $entries;
    }
}

if (!function_exists('admin_theme_files')) {
    function admin_theme_files(array $config): array
    {
        $entries = admin_theme_entries($config);
        $files = [];
        foreach ($entries as $entry) {
            if (($entry['type'] ?? '') === 'file') {
                $files[] = (string) $entry['path'];
            }
        }

        usort($files, static function (string $left, string $right): int {
            if ($left === 'functions.php') {
                return -1;
            }
            if ($right === 'functions.php') {
                return 1;
            }

            return strcmp($left, $right);
        });

        return $files;
    }
}

if (!function_exists('admin_resolve_theme_file')) {
    function admin_resolve_theme_file(array $config, string $relativeFile, bool $allowCreate = false): ?string
    {
        $relativeFile = str_replace('\\', '/', trim($relativeFile));
        $relativeFile = ltrim($relativeFile, '/');
        if ($relativeFile === '' || str_contains($relativeFile, '..')) {
            return null;
        }

        $basename = basename($relativeFile);
        if (in_array($basename, admin_theme_disallowed_filenames(), true)) {
            return null;
        }

        $extension = strtolower(pathinfo($relativeFile, PATHINFO_EXTENSION));
        if (!in_array($extension, admin_theme_allowed_extensions(), true)) {
            return null;
        }

        $themeDir = admin_theme_dir($config);
        $realThemeDir = admin_theme_dir_realpath($config);
        if ($realThemeDir === null) {
            return null;
        }

        $fullPath = $themeDir . '/' . $relativeFile;
        $parentDir = dirname($fullPath);
        $realParent = realpath($parentDir);

        if ($realParent === false && $allowCreate) {
            if (!@mkdir($parentDir, 0775, true)) {
                return null;
            }
            $realParent = realpath($parentDir);
        }

        if ($realParent === false || !admin_path_is_within($realParent, $realThemeDir)) {
            return null;
        }

        return $fullPath;
    }
}

if (!function_exists('admin_backup_theme_file')) {
    function admin_backup_theme_file(string $filePath, string $themeDir): bool
    {
        if (!is_file($filePath)) {
            return false;
        }

        $backupDir = $themeDir . '/backups';
        if (!is_dir($backupDir)) {
            @mkdir($backupDir, 0775, true);
        }

        $relative = str_replace(['\\', '/'], '-', ltrim(str_replace($themeDir, '', $filePath), '/\\'));
        $backupPath = $backupDir . '/' . date('Ymd-His') . '-' . $relative . '.bak';

        return @copy($filePath, $backupPath);
    }
}

if (!function_exists('admin_create_theme_folder')) {
    function admin_create_theme_folder(array $config, string $relativeFolder): array
    {
        $relativeFolder = str_replace('\\', '/', trim($relativeFolder));
        $relativeFolder = trim($relativeFolder, '/');
        if ($relativeFolder === '' || str_contains($relativeFolder, '..')) {
            return ['ok' => false, 'message' => 'Folder path is invalid.'];
        }

        $themeDir = admin_theme_dir($config);
        $realThemeDir = admin_theme_dir_realpath($config);
        if ($realThemeDir === null) {
            return ['ok' => false, 'message' => 'Theme directory was not found.'];
        }

        $fullPath = $themeDir . '/' . $relativeFolder;
        $parentDir = dirname($fullPath);
        $realParent = realpath($parentDir);

        if ($realParent === false || !admin_path_is_within($realParent, $realThemeDir)) {
            return ['ok' => false, 'message' => 'Folder path is outside the active theme.'];
        }

        if (is_dir($fullPath)) {
            return ['ok' => false, 'message' => 'That folder already exists.'];
        }

        if (!@mkdir($fullPath, 0775, true)) {
            return ['ok' => false, 'message' => 'Folder could not be created.'];
        }

        return ['ok' => true, 'message' => 'Folder created successfully.'];
    }
}

if (!function_exists('admin_delete_theme_file')) {
    function admin_delete_theme_file(array $config, string $relativeFile): array
    {
        $filePath = admin_resolve_theme_file($config, $relativeFile);
        $themeDir = admin_theme_dir($config);

        if ($filePath === null || !is_file($filePath)) {
            return ['ok' => false, 'message' => 'Theme file was not found.'];
        }

        admin_backup_theme_file($filePath, $themeDir);

        if (!@unlink($filePath)) {
            return ['ok' => false, 'message' => 'Theme file could not be deleted.'];
        }

        return ['ok' => true, 'message' => 'Theme file deleted successfully.'];
    }
}

if (!function_exists('get_php_binary')) {
    function get_php_binary(): string
    {
        static $resolvedBinary = null;
        if ($resolvedBinary !== null) {
            return $resolvedBinary;
        }

        $candidates = [];

        $phpBindir = defined('PHP_BINDIR') ? rtrim((string) PHP_BINDIR, '/\\') : '';
        if ($phpBindir !== '') {
            $candidates[] = $phpBindir . DIRECTORY_SEPARATOR . 'php.exe';
            $candidates[] = $phpBindir . DIRECTORY_SEPARATOR . 'php';
        }

        $phpBinary = defined('PHP_BINARY') ? (string) PHP_BINARY : '';
        if ($phpBinary !== '') {
            $candidates[] = $phpBinary;
        }

        $candidates = array_merge($candidates, [
            '/usr/bin/php',
            '/usr/local/bin/php',
            '/opt/cpanel/ea-php82/root/usr/bin/php',
            '/opt/cpanel/ea-php81/root/usr/bin/php',
            '/opt/cpanel/ea-php80/root/usr/bin/php',
            '/opt/cpanel/ea-php74/root/usr/bin/php',
            'C:/xampp/php/php.exe',
            'C:/xampp/php8/php.exe',
        ]);

        $mampBinaries = glob('C:/MAMP/bin/php/php*/php.exe');
        if (is_array($mampBinaries)) {
            natsort($mampBinaries);
            $mampBinaries = array_reverse($mampBinaries);
            $candidates = array_merge($candidates, $mampBinaries);
        }

        $xamppBinaries = glob('E:/XAMPP/php/php.exe');
        if (is_array($xamppBinaries)) {
            $candidates = array_merge($candidates, $xamppBinaries);
        }

        foreach (['where php 2>NUL', 'command -v php 2>/dev/null', 'which php 2>/dev/null'] as $locatorCommand) {
            $pathBinaries = @shell_exec($locatorCommand);
            if (is_string($pathBinaries) && trim($pathBinaries) !== '') {
                $candidates = array_merge($candidates, preg_split('/\r\n|\r|\n/', trim($pathBinaries)) ?: []);
            }
        }

        foreach (array_unique(array_filter($candidates)) as $candidate) {
            $candidate = trim((string) $candidate, "\" \t\n\r\0\x0B");
            if ($candidate === '') {
                continue;
            }

            if ($candidate === 'php') {
                $resolvedBinary = $candidate;
                return $resolvedBinary;
            }

            if (is_file($candidate) && is_executable($candidate)) {
                $basename = strtolower(basename($candidate));
                if ($basename === 'php.exe' || $basename === 'php') {
                    $resolvedBinary = $candidate;
                    return $resolvedBinary;
                }
            }
        }

        $resolvedBinary = 'php';
        return $resolvedBinary;
    }
}

if (!function_exists('admin_lint_php_code')) {
    function admin_lint_php_code(string $code): array
    {
        $tempFile = tempnam(sys_get_temp_dir(), 'cms-theme-');
        if ($tempFile === false) {
            return ['ok' => false, 'message' => 'PHP lint could not create a temporary file.'];
        }

        $tempPhpFile = $tempFile . '.php';
        @rename($tempFile, $tempPhpFile);
        file_put_contents($tempPhpFile, $code, LOCK_EX);

        $binary = get_php_binary();
        $command = escapeshellarg($binary) . ' -n -l ' . escapeshellarg($tempPhpFile) . ' 2>&1';
        $outputLines = [];
        $exitCode = 1;
        @exec($command, $outputLines, $exitCode);

        @unlink($tempPhpFile);

        $output = trim(implode("\n", array_filter(array_map('trim', $outputLines), static function (string $line): bool {
            return $line !== '';
        })));

        if ($exitCode === 0) {
            return ['ok' => true, 'message' => $output !== '' ? $output : 'No syntax errors detected.'];
        }

        if ($output === '') {
            return ['ok' => false, 'message' => 'PHP lint could not be executed.'];
        }

        $cleanOutput = preg_replace('/\s+in\s+.+?\.php on line\s+(\d+)/i', ' on line $1', $output) ?? $output;
        $cleanOutput = preg_replace('/^PHP\s+(Parse|Fatal)\s+error:\s*/i', '', $cleanOutput) ?? $cleanOutput;
        $cleanOutput = preg_replace('/^Parse error:\s*/i', '', $cleanOutput) ?? $cleanOutput;

        if (preg_match('/on line\s+(\d+)/i', $cleanOutput, $lineMatch)) {
            $lineNumber = (int) $lineMatch[1];
            $message = trim((string) preg_replace('/\s*on line\s+\d+/i', '', $cleanOutput), " \t\n\r\0\x0B.:");
            return ['ok' => false, 'message' => 'Syntax error on line ' . $lineNumber . ': ' . $message];
        }

        return ['ok' => false, 'message' => trim($cleanOutput)];
    }
}

if (!function_exists('admin_theme_upload_file')) {
    function admin_theme_upload_file(array $config, array $file, string $relativeFolder = ''): array
    {
        if (($file['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) {
            return ['ok' => false, 'message' => 'Upload failed.'];
        }

        $originalName = (string) ($file['name'] ?? '');
        $safeName = preg_replace('/[^A-Za-z0-9._-]/', '-', basename($originalName)) ?: '';
        if ($safeName === '') {
            return ['ok' => false, 'message' => 'Uploaded file name is invalid.'];
        }

        $extension = strtolower(pathinfo($safeName, PATHINFO_EXTENSION));
        if (!in_array($extension, admin_theme_upload_extensions(), true)) {
            return ['ok' => false, 'message' => 'Only image files are allowed in the theme editor upload.'];
        }

        $baseThemePath = admin_theme_dir($config);
        $realBaseThemePath = realpath($baseThemePath);
        if ($realBaseThemePath === false || !is_dir($realBaseThemePath)) {
            return ['ok' => false, 'message' => 'Active theme directory could not be resolved.'];
        }

        $targetDirectory = $realBaseThemePath . '/assets/images';
        if (!is_dir($targetDirectory) && !@mkdir($targetDirectory, 0777, true)) {
            return ['ok' => false, 'message' => 'Upload folder could not be created: ' . $targetDirectory];
        }

        $realTargetDirectory = realpath($targetDirectory);
        if ($realTargetDirectory === false) {
            return ['ok' => false, 'message' => 'Upload folder could not be resolved: ' . $targetDirectory];
        }

        if (!admin_path_is_within($realTargetDirectory, $realBaseThemePath)) {
            return ['ok' => false, 'message' => 'Upload target is outside the active theme or not allowed. Resolved path: ' . $realTargetDirectory];
        }

        if (!is_uploaded_file((string) ($file['tmp_name'] ?? ''))) {
            return ['ok' => false, 'message' => 'Uploaded file could not be verified.'];
        }

        $targetFileName = pathinfo($safeName, PATHINFO_FILENAME) . '-' . date('YmdHis') . '-' . substr(bin2hex(random_bytes(4)), 0, 8) . '.' . $extension;
        $targetPath = $realTargetDirectory . '/' . $targetFileName;
        if (!move_uploaded_file((string) $file['tmp_name'], $targetPath)) {
            return ['ok' => false, 'message' => 'Uploaded file could not be moved into the theme. Resolved path: ' . $targetPath];
        }

        $relativeTarget = ltrim(str_replace('\\', '/', substr($targetPath, strlen($realBaseThemePath))), '/');
        $themeSlug = admin_active_theme($config);
        $fileUrl = '/content/themes/' . rawurlencode($themeSlug) . '/' . str_replace('%2F', '/', rawurlencode($relativeTarget));

        return ['ok' => true, 'message' => 'File uploaded successfully.', 'path' => $relativeTarget, 'url' => $fileUrl];
    }
}

if (!function_exists('admin_plugin_template')) {
    function admin_plugin_template(string $pluginName, string $pluginSlug): string
    {
        return "<?php\n/*\nPlugin Name: {$pluginName}\nPlugin Slug: {$pluginSlug}\nDescription: New plugin created from admin.\nVersion: 1.0\n*/\n\nadd_action('before_render', function (): void {\n    echo '<div>Hello from plugin</div>';\n}, 10);\n";
    }
}

if (!function_exists('admin_create_plugin')) {
    function admin_create_plugin(string $pluginName, string $pluginSlug, bool $activateNow = false): array
    {
        $pluginName = trim($pluginName);
        $pluginSlug = strtolower(trim($pluginSlug));
        $pluginSlug = preg_replace('/[^a-z0-9_-]/', '-', $pluginSlug) ?: '';

        if ($pluginName === '' || $pluginSlug === '') {
            return ['ok' => false, 'message' => 'Plugin name and slug are required.'];
        }

        $pluginDir = CMS_PLUGIN_DIR . '/' . $pluginSlug;
        $pluginFile = $pluginDir . '/plugin.php';

        if (is_dir($pluginDir) || is_file($pluginFile)) {
            return ['ok' => false, 'message' => 'A plugin with that slug already exists.'];
        }

        if (!@mkdir($pluginDir, 0775, true)) {
            return ['ok' => false, 'message' => 'Plugin folder could not be created.'];
        }

        $template = admin_plugin_template($pluginName, $pluginSlug);
        if (file_put_contents($pluginFile, $template, LOCK_EX) === false) {
            return ['ok' => false, 'message' => 'Plugin file could not be written.'];
        }

        if ($activateNow) {
            $config = cms_load_config(CMS_ROOT);
            $activePlugins = is_array($config['active_plugins'] ?? null) ? $config['active_plugins'] : [];
            if (!in_array($pluginSlug, $activePlugins, true)) {
                $activePlugins[] = $pluginSlug;
                $config['active_plugins'] = $activePlugins;
                cms_save_config($config, CMS_ROOT);
            }
            cms_set_plugin_status($pluginSlug, true);
        }

        return ['ok' => true, 'message' => 'Plugin created successfully.', 'slug' => $pluginSlug];
    }
}

if (!function_exists('admin_site_title')) {
    function admin_site_title(array $config): string
    {
        return cms_site_title_value($config);
    }
}

if (!function_exists('admin_nav_items')) {
    function admin_nav_items(): array
    {
        return [
            'dashboard' => ['label' => 'Dashboard', 'href' => '/admin/'],
            'settings' => ['label' => 'Settings', 'href' => '/admin/?route=settings'],
            'layout' => ['label' => 'Layout Builder', 'href' => '/admin/?route=layout'],
            'plugins' => ['label' => 'Plugins', 'href' => '/admin/plugins.php'],
            'pages' => ['label' => 'Pages', 'href' => '/admin/pages.php'],
            'menus' => ['label' => 'Menus', 'href' => '/admin/menus.php'],
            'media' => ['label' => 'Media', 'href' => '/admin/media.php'],
            'theme-editor' => ['label' => 'Theme Editor', 'href' => '/admin/theme-editor.php'],
            'logout' => ['label' => 'Logout', 'href' => '/admin/?route=logout'],
        ];
    }
}

if (!function_exists('admin_shared_styles')) {
    function admin_shared_styles(string $extraCss = ''): string
    {
        $css = <<<CSS
<style>
:root {
    --admin-bg: #f5f1e8;
    --admin-panel: #fffaf2;
    --admin-surface: #ffffff;
    --admin-line: #ddcfb8;
    --admin-text: #2d2419;
    --admin-muted: #6f6252;
    --admin-accent: #0f766e;
    --admin-accent-strong: #115e59;
    --admin-sidebar: #1e1812;
    --admin-sidebar-text: #efe3cf;
    --admin-shadow: 0 16px 40px rgba(44, 36, 27, 0.08);
}
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
    margin: 0;
    font-family: Georgia, 'Times New Roman', serif;
    background: linear-gradient(180deg, #f7f2ea 0%, #ecdfcb 100%);
    color: var(--admin-text);
}
a { color: inherit; text-decoration: none; }
.app {
    display: grid;
    grid-template-columns: 272px minmax(0, 1fr);
    min-height: 100vh;
}
.sidebar {
    background: var(--admin-sidebar);
    color: var(--admin-sidebar-text);
    padding: 24px 18px;
    position: sticky;
    top: 0;
    height: 100vh;
    overflow: auto;
}
.brand {
    margin: 0 0 18px;
    font-size: 28px;
    line-height: 1.1;
}
.brand-subtitle {
    color: rgba(239, 227, 207, 0.68);
    font: 600 12px/1.4 Arial, sans-serif;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    margin-bottom: 18px;
}
.nav {
    display: grid;
    gap: 8px;
}
.nav a {
    display: block;
    padding: 12px 14px;
    border-radius: 14px;
    color: #e6d8bf;
    font: 600 15px/1.3 Arial, sans-serif;
    transition: background .2s ease, color .2s ease, transform .2s ease;
}
.nav a.active,
.nav a:hover {
    background: rgba(255,255,255,.09);
    color: #fff;
    transform: translateX(2px);
}
.content {
    padding: 28px;
    min-width: 0;
}
.page-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 16px;
    margin-bottom: 18px;
    flex-wrap: wrap;
}
.page-title {
    margin: 0;
    font-size: clamp(30px, 4vw, 38px);
    line-height: 1.05;
}
.page-subtitle {
    color: var(--admin-muted);
    margin-top: 8px;
    font: 500 14px/1.5 Arial, sans-serif;
}
.panel,
.login-card {
    background: var(--admin-panel);
    border: 1px solid var(--admin-line);
    border-radius: 22px;
    padding: 22px;
    box-shadow: var(--admin-shadow);
}
.grid {
    display: grid;
    gap: 18px;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}
.stat-card,
.card {
    border: 1px solid var(--admin-line);
    border-radius: 18px;
    padding: 18px;
    background: var(--admin-surface);
}
.stat-label {
    color: var(--admin-muted);
    font: 700 12px/1.2 Arial, sans-serif;
    text-transform: uppercase;
    letter-spacing: .08em;
}
.stat-value {
    font-size: clamp(20px, 3vw, 30px);
    margin-top: 8px;
}
.form-row {
    display: grid;
    gap: 8px;
    margin-bottom: 16px;
}
label,
.subtle-label {
    font: 600 14px/1.4 Arial, sans-serif;
    color: var(--admin-text);
}
input[type='text'],
input[type='password'],
input[type='file'],
select,
textarea {
    width: 100%;
    padding: 12px 14px;
    border: 1px solid var(--admin-line);
    border-radius: 14px;
    background: #fff;
    color: var(--admin-text);
    font: 15px/1.5 Arial, sans-serif;
}
textarea {
    min-height: 170px;
    resize: vertical;
}
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    border: 0;
    border-radius: 14px;
    padding: 12px 18px;
    background: var(--admin-accent);
    color: #fff;
    cursor: pointer;
    font: 700 14px/1.2 Arial, sans-serif;
    text-decoration: none;
    transition: background .2s ease, transform .2s ease;
}
.btn:hover {
    background: var(--admin-accent-strong);
    transform: translateY(-1px);
}
.btn.secondary {
    background: #eadfcf;
    color: var(--admin-text);
}
.btn.danger {
    background: #b91c1c;
}
.btn.danger:hover {
    background: #991b1b;
}
.message {
    border-radius: 14px;
    padding: 12px 14px;
    margin-bottom: 12px;
    font: 600 14px/1.5 Arial, sans-serif;
}
.success { background: #ddf7e8; color: #14532d; }
.error { background: #fae4dc; color: #7c2d12; }
.inline-actions {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
    align-items: center;
}
.mobile-nav-toggle {
    display: none;
}
@media (max-width: 980px) {
    .app {
        grid-template-columns: 1fr;
    }
    .sidebar {
        position: static;
        height: auto;
        padding: 18px 14px 14px;
        border-bottom: 1px solid rgba(255,255,255,.06);
    }
    .brand {
        font-size: 24px;
        margin-bottom: 8px;
    }
    .brand-subtitle {
        margin-bottom: 12px;
    }
    .nav {
        display: flex;
        gap: 10px;
        overflow-x: auto;
        padding-bottom: 4px;
        scrollbar-width: thin;
    }
    .nav a {
        flex: 0 0 auto;
        white-space: nowrap;
        padding: 11px 13px;
        font-size: 14px;
    }
    .content {
        padding: 18px 14px 26px;
    }
    .panel,
    .login-card,
    .stat-card,
    .card {
        padding: 16px;
        border-radius: 18px;
    }
    .inline-actions,
    .page-header {
        align-items: stretch;
    }
    .inline-actions .btn,
    .inline-actions a.btn {
        flex: 1 1 180px;
    }
}
@media (max-width: 640px) {
    .grid {
        grid-template-columns: 1fr;
    }
    .btn,
    .btn.secondary,
    .btn.danger {
        width: 100%;
    }
    .inline-actions .btn,
    .inline-actions a.btn {
        flex-basis: 100%;
    }
}
</style>
CSS;

        if ($extraCss !== '') {
            $css .= "\n<style>\n" . $extraCss . "\n</style>";
        }

        return $css;
    }
}

if (!function_exists('admin_render_sidebar')) {
    function admin_render_sidebar(string $active = ''): void
    {
        $items = admin_nav_items();
        echo '<aside class="sidebar">';
        echo '<h1 class="brand">CMS Admin</h1>';
        echo '<div class="brand-subtitle">Custom CMS Control Panel</div>';
        echo '<nav class="nav">';
        foreach ($items as $key => $item) {
            $activeClass = $key === $active ? 'active' : '';
            echo '<a href="' . admin_escape((string) $item['href']) . '" class="' . $activeClass . '">' . admin_escape((string) $item['label']) . '</a>';
        }
        echo '</nav>';
        echo '</aside>';
    }
}

if (!function_exists('admin_resolve_plugin_slug')) {
    function admin_resolve_plugin_slug(string $pluginSlug): string
    {
        $pluginSlug = strtolower(trim($pluginSlug));

        return preg_replace('/[^a-z0-9_-]/', '', $pluginSlug) ?: '';
    }
}

if (!function_exists('admin_resolve_plugin_file')) {
    function admin_resolve_plugin_file(string $pluginSlug): ?string
    {
        $pluginSlug = admin_resolve_plugin_slug($pluginSlug);
        if ($pluginSlug === '') {
            return null;
        }

        $pluginDir = CMS_PLUGIN_DIR . '/' . $pluginSlug;
        $pluginFile = $pluginDir . '/plugin.php';
        $realPluginsDir = realpath(CMS_PLUGIN_DIR);
        $realPluginDir = realpath($pluginDir);

        if ($realPluginsDir === false || $realPluginDir === false || !is_dir($pluginDir) || !is_file($pluginFile)) {
            return null;
        }

        if (!admin_path_is_within($realPluginDir, $realPluginsDir)) {
            return null;
        }

        return $pluginFile;
    }
}

if (!function_exists('admin_backup_plugin_file')) {
    function admin_backup_plugin_file(string $pluginSlug, string $pluginFile): bool
    {
        $pluginSlug = admin_resolve_plugin_slug($pluginSlug);
        if ($pluginSlug === '' || !is_file($pluginFile)) {
            return false;
        }

        $backupDir = CMS_PLUGIN_DIR . '/' . $pluginSlug . '/backup';
        if (!is_dir($backupDir)) {
            @mkdir($backupDir, 0775, true);
        }

        $backupPath = $backupDir . '/plugin_' . date('Ymd_His') . '.php';

        return @copy($pluginFile, $backupPath);
    }
}

if (!function_exists('admin_plugin_allowed_extensions')) {
    function admin_plugin_allowed_extensions(): array
    {
        return ['php', 'html', 'js', 'css'];
    }
}

if (!function_exists('admin_plugin_dir')) {
    function admin_plugin_dir(string $pluginSlug): ?string
    {
        $pluginSlug = admin_resolve_plugin_slug($pluginSlug);
        if ($pluginSlug === '') {
            return null;
        }

        $pluginDir = CMS_PLUGIN_DIR . '/' . $pluginSlug;
        $realPluginsDir = realpath(CMS_PLUGIN_DIR);
        $realPluginDir = realpath($pluginDir);

        if ($realPluginsDir === false || $realPluginDir === false || !admin_path_is_within($realPluginDir, $realPluginsDir)) {
            return null;
        }

        return $pluginDir;
    }
}

if (!function_exists('admin_plugin_files')) {
    function admin_plugin_files(string $pluginSlug): array
    {
        $pluginDir = admin_plugin_dir($pluginSlug);
        if ($pluginDir === null || !is_dir($pluginDir)) {
            return [];
        }

        $allowedExtensions = admin_plugin_allowed_extensions();
        $iterator = new RecursiveIteratorIterator(
            new RecursiveDirectoryIterator($pluginDir, FilesystemIterator::SKIP_DOTS)
        );

        $files = [];
        foreach ($iterator as $fileInfo) {
            if (!$fileInfo instanceof SplFileInfo || !$fileInfo->isFile()) {
                continue;
            }

            $extension = strtolower($fileInfo->getExtension());
            if (!in_array($extension, $allowedExtensions, true)) {
                continue;
            }

            $fullPath = $fileInfo->getPathname();
            if (strpos(str_replace('\\', '/', $fullPath), str_replace('\\', '/', $pluginDir . '/backup/')) === 0) {
                continue;
            }

            $relative = str_replace('\\', '/', substr($fullPath, strlen($pluginDir) + 1));
            $files[] = $relative;
        }

        usort($files, static function (string $left, string $right): int {
            if ($left === 'plugin.php') {
                return -1;
            }
            if ($right === 'plugin.php') {
                return 1;
            }

            return strcmp($left, $right);
        });

        return $files;
    }
}

if (!function_exists('admin_resolve_plugin_relative_file')) {
    function admin_resolve_plugin_relative_file(string $pluginSlug, string $relativePath): ?string
    {
        $pluginDir = admin_plugin_dir($pluginSlug);
        if ($pluginDir === null) {
            return null;
        }

        $relativePath = str_replace('\\', '/', trim($relativePath));
        $relativePath = ltrim($relativePath, '/');
        if ($relativePath === '' || str_contains($relativePath, '..')) {
            return null;
        }

        $extension = strtolower(pathinfo($relativePath, PATHINFO_EXTENSION));
        if (!in_array($extension, admin_plugin_allowed_extensions(), true)) {
            return null;
        }

        $fullPath = $pluginDir . '/' . $relativePath;
        $parentDir = dirname($fullPath);
        $realPluginDir = realpath($pluginDir);
        $realParentDir = realpath($parentDir);

        if ($realPluginDir === false) {
            return null;
        }

        if ($realParentDir === false) {
            if (!@mkdir($parentDir, 0775, true)) {
                return null;
            }
            $realParentDir = realpath($parentDir);
        }

        if ($realParentDir === false || !admin_path_is_within($realParentDir, $realPluginDir)) {
            return null;
        }

        return $fullPath;
    }
}
