<?php
/**
 * Slate — admin shell header.
 *
 * Renders the layout:
 *   Desktop (≥768px):  dark sidebar on left, light topbar across,
 *                       light off-white canvas under main content
 *   Mobile  (<768px):  topbar across, main content full-width,
 *                       bottom tab bar pinned to bottom of viewport
 *
 * Pages call this with $pageTitle set, then render content, then
 * include footer.php.
 *
 *   require_once dirname(__DIR__).'/../config.php';
 *   Auth::require();
 *   $pageTitle = 'My Page';
 *   require __DIR__ . '/partials/header.php';
 *   // ... page content ...
 *   require __DIR__ . '/partials/footer.php';
 *
 * NAV ITEMS are dynamic. Core declares its items here; plugins extend
 * via the `admin_nav_items` filter. Items are auto-filtered by the
 * current user's permissions and sorted by 'order'. Items optionally
 * carry a 'group' key — items with the same group render under an
 * uppercase section label. Core groups: overview, content, system.
 *
 * MOBILE TAB BAR shows the first 4 items + a "More" button that opens
 * a sheet listing the rest. Desktop sidebar shows all items grouped.
 *
 * TOPBAR slots (filters):
 *   admin_topbar_search   → string of HTML for the centre-right slot
 *                            (e.g. a search input). Empty by default.
 *   admin_topbar_actions  → array of HTML strings rendered as buttons
 *                            in the top-right. The first one is the
 *                            page's primary CTA.
 */

if (!defined('SLATE_ROOT')) exit;
Auth::require();

// Admin pages must never be cached — stale renders after a deploy are
// a real problem (see the Phase 0 hotfix story). Headers must be sent
// BEFORE any output, so this is the right place. Output buffer also
// isn't started yet here.
if (!headers_sent()) {
    header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
    header('Pragma: no-cache');
    header('Expires: 0');
}

require_once SLATE_ROOT . '/includes/breadcrumbs.php';

$user      = Auth::user();
$pageTitle = $pageTitle ?? 'Admin';

// ── Core nav items ───────────────────────────────────────────
// Each item: ['slug', 'label', 'href', 'icon', 'perm', 'order', 'group']
$coreNav = [
    [
        'slug'  => 'dashboard',
        'label' => __('dashboard', 'Dashboard'),
        'href'  => SLATE_URL . '/admin/index.php',
        'icon'  => 'home',
        'perm'  => null,
        'order' => 10,
        'group' => 'overview',
    ],
    [
        'slug'  => 'contact-forms',
        'label' => __('contact_forms', 'Forms'),
        'href'  => SLATE_URL . '/admin/contact_forms.php',
        'icon'  => 'mail',
        'perm'  => 'contact.view',
        'order' => 220,
        'group' => 'content',
    ],
    [
        'slug'  => 'users',
        'label' => __('users', 'Users'),
        'href'  => SLATE_URL . '/admin/users.php',
        'icon'  => 'users',
        'perm'  => 'users.view',
        'order' => 200,
        'group' => 'system',
    ],
    [
        'slug'  => 'roles',
        'label' => __('roles', 'Roles'),
        'href'  => SLATE_URL . '/admin/roles.php',
        'icon'  => 'shield',
        'perm'  => null, // super-admin via short-circuit
        'order' => 210,
        'group' => 'system',
    ],
    [
        'slug'  => 'plugins',
        'label' => __('plugins', 'Plugins'),
        'href'  => SLATE_URL . '/admin/plugins.php',
        'icon'  => 'box',
        'perm'  => 'plugins.manage',
        'order' => 800,
        'group' => 'system',
    ],
    [
        'slug'  => 'audit',
        'label' => __('audit_log', 'Audit log'),
        'href'  => SLATE_URL . '/admin/audit.php',
        'icon'  => 'clipboard-list',
        'perm'  => 'audit.view',
        'order' => 810,
        'group' => 'system',
    ],
    [
        'slug'  => 'settings',
        'label' => __('settings', 'Settings'),
        'href'  => SLATE_URL . '/admin/settings.php',
        'icon'  => 'settings',
        'perm'  => 'settings.view',
        'order' => 900,
        'group' => 'system',
    ],
];

// Plugin nav contributions
$navItems = Hook::applyFilters('admin_nav_items', $coreNav);

// Filter by permissions
$navItems = array_values(array_filter($navItems, function ($item) {
    if (!is_array($item) || empty($item['slug'])) return false;
    $perm = $item['perm'] ?? null;
    if ($perm !== null && $perm !== '' && !Auth::can($perm)) return false;
    return true;
}));

// Sort by order (stable across PHP 8+)
usort($navItems, fn($a, $b) => ($a['order'] ?? 500) <=> ($b['order'] ?? 500));

// ── Active nav slug ──────────────────────────────────────────
$currentNav = $currentNav ?? null;
if (!$currentNav) {
    $script = basename($_SERVER['SCRIPT_NAME'] ?? '');
    $map = [
        'index.php'         => 'dashboard',
        'plugins.php'       => 'plugins',
        'users.php'         => 'users',
        'roles.php'         => 'roles',
        'contact_forms.php' => 'contact-forms',
        'settings.php'      => 'settings',
        'audit.php'         => 'audit',
    ];
    $currentNav = $map[$script] ?? null;
}

// ── Site identity ────────────────────────────────────────────
$siteName     = Database::setting('site_name')     ?: 'Slate';
$brandSublabel = Database::setting('brand_sublabel') ?: __('pro_admin', 'Pro Admin');

// ── Group nav items by 'group' field (preserves first-seen order) ─
// Note: use $_navGroupKey (not $g) so we never clobber a caller's $g variable.
$navGroups = [];
foreach ($navItems as $item) {
    $_navGroupKey = $item['group'] ?? '';
    if ($_navGroupKey === '' || !is_string($_navGroupKey)) $_navGroupKey = 'plugins';
    $_navGroupKey = strtolower(preg_replace('/[^a-z0-9_-]+/i', '-', $_navGroupKey)) ?: 'plugins';
    if (!isset($navGroups[$_navGroupKey])) $navGroups[$_navGroupKey] = [];
    $navGroups[$_navGroupKey][] = $item;
}
unset($_navGroupKey, $item);

// Friendly display label for a group key.
if (!function_exists('slate_admin_group_label')) {
    function slate_admin_group_label(string $key): string {
        // First check for an i18n key (lets plugins translate their own group).
        $translated = __($key, '');
        if ($translated !== '' && $translated !== $key) {
            return strtoupper($translated);
        }
        static $defaults = [
            'overview' => 'Overview',
            'content'  => 'Content',
            'system'   => 'System',
            'plugins'  => 'Plugins',
            'shop'     => 'Shop',
            'media'    => 'Media',
        ];
        $label = $defaults[$key] ?? str_replace(['-', '_'], ' ', $key);
        return strtoupper($label);
    }
}

// ── Split nav for mobile (first 4 in tab bar, rest in "More") ─
$tabBarItems  = array_slice($navItems, 0, 4);
$moreNavItems = array_slice($navItems, 4);

// ── Topbar slots (plugin contributions) ──────────────────────
$topbarSearchHtml = (string)Hook::applyFilters('admin_topbar_search', '');
$topbarActions    = Hook::applyFilters('admin_topbar_actions', []);
if (!is_array($topbarActions)) $topbarActions = [];

/**
 * Inline SVG icon helper — line icons, 24×24 viewBox, single stroke.
 * Sized via CSS (.nav-icon).
 *
 * Defined before HTML output so the sidebar's first call resolves.
 * Unknown names fall back to `circle`.
 */
if (!function_exists('slate_admin_nav_icon')) {
    function slate_admin_nav_icon(string $name): string {
        $icons = [
            'home'           => '<path d="M3 12L12 4l9 8M5 10v10h4v-6h6v6h4V10"/>',
            'users'          => '<path d="M17 11a4 4 0 1 0-8 0 4 4 0 0 0 8 0zM3 21v-2a5 5 0 0 1 5-5h10a5 5 0 0 1 5 5v2"/>',
            'shield'         => '<path d="M12 3l8 4v6c0 5-3.5 9-8 10-4.5-1-8-5-8-10V7l8-4z"/>',
            'box'            => '<path d="M21 8l-9-5-9 5v8l9 5 9-5V8zM3 8l9 5 9-5M12 13v9"/>',
            'mail'           => '<rect x="3" y="5" width="18" height="14" rx="2"/><path d="M3 7l9 6 9-6"/>',
            'settings'       => '<circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.7 1.7 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.7 1.7 0 0 0-1.8-.3 1.7 1.7 0 0 0-1 1.5V21a2 2 0 0 1-4 0v-.1a1.7 1.7 0 0 0-1-1.5 1.7 1.7 0 0 0-1.8.3l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.7 1.7 0 0 0 .3-1.8 1.7 1.7 0 0 0-1.5-1H3a2 2 0 0 1 0-4h.1A1.7 1.7 0 0 0 4.6 9a1.7 1.7 0 0 0-.3-1.8l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.7 1.7 0 0 0 1.8.3H9a1.7 1.7 0 0 0 1-1.5V3a2 2 0 0 1 4 0v.1a1.7 1.7 0 0 0 1 1.5 1.7 1.7 0 0 0 1.8-.3l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.7 1.7 0 0 0-.3 1.8V9a1.7 1.7 0 0 0 1.5 1H21a2 2 0 0 1 0 4h-.1a1.7 1.7 0 0 0-1.5 1z"/>',
            'star'           => '<path d="M12 2l3 7 7 .8-5.4 4.8L18 22l-6-3.5L6 22l1.4-7.4L2 9.8 9 9l3-7z"/>',
            'circle'         => '<circle cx="12" cy="12" r="9"/>',
            'logout'         => '<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4M16 17l5-5-5-5M21 12H9"/>',
            'more'           => '<circle cx="12" cy="12" r="1.5"/><circle cx="5" cy="12" r="1.5"/><circle cx="19" cy="12" r="1.5"/>',
            'search'         => '<circle cx="11" cy="11" r="7"/><path d="M21 21l-4.3-4.3"/>',
            // Commerce / content
            'shopping-bag'   => '<path d="M6 2L3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4zM3 6h18M16 10a4 4 0 0 1-8 0"/>',
            'clipboard-list' => '<rect x="8" y="2" width="8" height="4" rx="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/><path d="M9 12h.01M9 16h.01M13 12h2M13 16h2"/>',
            'package'        => '<path d="M16.5 9.4L7.5 4.21M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16zM3.27 6.96L12 12l8.73-5.04M12 22V12"/>',
            'folder'         => '<path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/>',
            'tag'            => '<path d="M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z"/><circle cx="7" cy="7" r="1.5"/>',
            'bar-chart-2'    => '<line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/><line x1="6" y1="20" x2="6" y2="14"/>',
            // Extended set added Phase 0 (was: featureless "circle" fallback)
            'truck'          => '<path d="M1 3h15v13H1z"/><path d="M16 8h4l3 3v5h-7"/><circle cx="5.5" cy="18.5" r="2.5"/><circle cx="18.5" cy="18.5" r="2.5"/>',
            'image'          => '<rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><path d="M21 15l-5-5L5 21"/>',
            'edit-3'         => '<path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4z"/>',
            'send'           => '<line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/>',
            'bar-chart-3'    => '<path d="M3 3v18h18"/><path d="M7 16V9"/><path d="M11 16V5"/><path d="M15 16v-7"/><path d="M19 16v-4"/>',
            'calendar'       => '<rect x="3" y="4" width="18" height="18" rx="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/>',
            'credit-card'    => '<rect x="2" y="5" width="20" height="14" rx="2"/><line x1="2" y1="10" x2="22" y2="10"/>',
        ];
        $path = $icons[$name] ?? $icons['circle'];
        return '<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" '
             . 'stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" '
             . 'aria-hidden="true">' . $path . '</svg>';
    }
}
?>
<!DOCTYPE html>
<html lang="<?= e(I18n::currentLocale()) ?>">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
    <meta name="theme-color" content="#0E1117">
    <title><?= e($pageTitle) ?> — <?= e($siteName) ?></title>
    <meta name="csrf" content="<?= e(csrf_token()) ?>">
    <?php slate_ui_emit_css(); ?>
    <?php require SLATE_ROOT . '/includes/a11y_head.php'; ?>
    <style>
    /* ─── Layout ──────────────────────────────────────────── */
    body {
        min-height: 100vh;
        min-height: 100dvh;
        background: var(--bg);
    }
    .app-layout {
        display: grid;
        grid-template-columns: 1fr;
        grid-template-rows: auto 1fr;
        min-height: 100vh;
        min-height: 100dvh;
    }

    /* ─── Topbar ──────────────────────────────────────────── */
    .topbar {
        position: sticky;
        top: 0;
        z-index: 30;
        height: var(--topbar-height);
        background: rgba(245, 244, 241, 0.82);
        backdrop-filter: saturate(180%) blur(12px);
        -webkit-backdrop-filter: saturate(180%) blur(12px);
        display: flex;
        align-items: center;
        gap: 12px;
        padding: 0 20px 0 24px;
    }
    .topbar-title {
        font-family: var(--font-display);
        font-size: 15px;
        font-weight: 600;
        margin: 0;
        letter-spacing: -0.01em;
        color: var(--text);
        flex: 0 1 auto;
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
    .topbar-search-slot {
        flex: 1 1 auto;
        min-width: 0;
        display: flex;
        justify-content: center;
    }
    .topbar-search-slot:empty { flex: 1 1 auto; }
    .topbar-search {
        display: flex;
        align-items: center;
        gap: 8px;
        padding: 6px 10px 6px 11px;
        border-radius: var(--radius);
        background: var(--surface);
        border: 1px solid var(--border);
        width: 100%;
        max-width: 360px;
        color: var(--muted);
        transition: border-color 140ms ease, box-shadow 140ms ease;
    }
    .topbar-search:focus-within {
        border-color: var(--accent);
        box-shadow: 0 0 0 3px var(--ring);
    }
    .topbar-search input {
        border: 0;
        background: transparent;
        outline: none;
        flex: 1;
        font-size: 13px;
        color: var(--text);
        font-family: inherit;
        min-width: 0;
    }
    .topbar-search input::placeholder { color: var(--subtle); }
    .topbar-search kbd {
        font-family: var(--font-mono);
        font-size: 10.5px;
        padding: 1px 5px;
        background: var(--surface-2);
        color: var(--muted);
        border-radius: 4px;
        border: 1px solid var(--border);
    }
    .topbar-actions {
        display: flex;
        align-items: center;
        gap: var(--space-2);
        flex-shrink: 0;
    }
    .topbar-icon-btn {
        width: 32px;
        height: 32px;
        border-radius: var(--radius-sm);
        background: transparent;
        border: 0;
        color: var(--muted);
        display: grid;
        place-items: center;
        cursor: pointer;
        position: relative;
        transition: background 140ms ease, color 140ms ease;
    }
    .topbar-icon-btn:hover { background: var(--surface-2); color: var(--text); }
    .topbar-icon-btn-dot {
        position: absolute;
        top: 6px; right: 6px;
        width: 6px; height: 6px;
        border-radius: 999px;
        background: var(--danger);
        box-shadow: 0 0 0 2px var(--bg);
    }
    .topbar-user {
        display: flex;
        align-items: center;
        gap: 8px;
        color: var(--muted);
        font-size: 12.5px;
    }
    .topbar-user-name {
        font-weight: 500;
        color: var(--text-2);
    }
    .topbar-avatar {
        width: 28px; height: 28px;
        border-radius: 999px;
        background: linear-gradient(140deg, #3B82F6, #1D4ED8);
        color: #fff;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        font-weight: 600;
        font-size: 11.5px;
        flex-shrink: 0;
    }

    main.content {
        padding: 16px 14px 28px;
        padding-bottom: calc(28px + var(--tabbar-height) + env(safe-area-inset-bottom, 0));
        max-width: var(--content-max);
        width: 100%;
        min-width: 0;
    }

    /* ─── Sidebar (desktop only) ──────────────────────────── */
    .sidebar { display: none; }

    @media (min-width: 768px) {
        .app-layout {
            grid-template-columns: var(--sidebar-width) 1fr;
            grid-template-rows: auto 1fr;
            grid-template-areas:
                "sidebar topbar"
                "sidebar main";
        }
        .topbar { grid-area: topbar; }
        main.content {
            grid-area: main;
            padding: 24px 32px 56px;
        }
        .sidebar {
            grid-area: sidebar;
            display: flex; flex-direction: column;
            background: var(--sidebar-bg);
            color: var(--sidebar-text);
            padding: 14px 10px 10px;
            position: sticky;
            top: 0;
            height: 100vh;
            height: 100dvh;
            overflow-y: auto;
        }

        /* Brand row */
        .sidebar-brand {
            display: flex;
            align-items: center;
            gap: 10px;
            padding: 4px 6px 14px;
            margin-bottom: 8px;
            border-bottom: 1px solid var(--sidebar-border);
            color: #fff;
            text-decoration: none;
        }
        .sidebar-brand:hover { color: #fff; text-decoration: none; }
        .sidebar-brand-mark {
            width: 30px;
            height: 30px;
            border-radius: var(--radius);
            background: var(--accent);
            color: var(--on-accent);
            display: grid;
            place-items: center;
            font-family: var(--font-display);
            font-size: 14px;
            font-weight: 700;
            letter-spacing: -0.04em;
            flex-shrink: 0;
            box-shadow: 0 1px 2px rgba(0,0,0,0.25), inset 0 1px 0 rgba(255,255,255,0.18);
        }
        .sidebar-brand-text {
            min-width: 0;
            flex: 1;
            line-height: 1.15;
        }
        .sidebar-brand-name {
            display: block;
            font-family: var(--font-display);
            font-size: 15px;
            font-weight: 700;
            letter-spacing: -0.02em;
            color: #fff;
        }
        .sidebar-brand-sub {
            display: block;
            font-size: 10.5px;
            letter-spacing: 0.08em;
            text-transform: uppercase;
            color: var(--sidebar-subtle);
            font-weight: 600;
            margin-top: 2px;
        }

        /* Section labels */
        .nav-section-label {
            font-size: 11px;
            letter-spacing: 0.06em;
            text-transform: uppercase;
            color: var(--sidebar-subtle);
            padding: 14px 10px 6px;
            font-weight: 600;
        }
        .nav-section-label:first-of-type { padding-top: 6px; }

        .sidebar-nav { list-style: none; padding: 0; margin: 0 0 2px; }
        .sidebar-item a {
            display: flex;
            align-items: center;
            gap: 10px;
            padding: 7px 10px;
            color: var(--sidebar-text);
            text-decoration: none;
            font-size: 13px;
            font-weight: 500;
            border-radius: var(--radius-sm);
            transition: background 0.12s ease, color 0.12s ease;
        }
        .sidebar-item a:hover {
            background: var(--sidebar-hover);
            color: #fff;
            text-decoration: none;
        }
        .sidebar-item.is-active a {
            background: var(--sidebar-active);
            color: #fff;
        }
        .sidebar-item.is-active a .nav-icon { color: #fff; }
        .nav-icon {
            width: 17px; height: 17px;
            flex-shrink: 0;
            color: var(--sidebar-muted);
            transition: color 0.12s ease;
        }
        .sidebar-item a:hover .nav-icon { color: #fff; }

        .sidebar-footer {
            margin-top: auto;
            padding: 10px 8px 4px;
            border-top: 1px solid var(--sidebar-border);
            display: flex;
            align-items: center;
            gap: 10px;
        }
        .sidebar-footer .sidebar-user-info { min-width: 0; flex: 1; }
        .sidebar-footer .sidebar-user-name {
            font-size: 13px;
            font-weight: 600;
            color: #fff;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
            line-height: 1.2;
        }
        .sidebar-footer .sidebar-user-role {
            font-size: 11px;
            color: var(--sidebar-muted);
            line-height: 1.3;
        }
        .sidebar-footer-logout {
            width: 32px;
            height: 32px;
            border-radius: var(--radius-sm);
            color: var(--sidebar-muted);
            display: grid;
            place-items: center;
            text-decoration: none;
            transition: background 0.12s, color 0.12s;
        }
        .sidebar-footer-logout:hover { background: var(--sidebar-hover); color: #fff; }
    }

    /* ─── Bottom tab bar (mobile only) ────────────────────── */
    .tabbar {
        position: fixed;
        bottom: 0; left: 0; right: 0;
        height: var(--tabbar-height);
        padding-bottom: env(safe-area-inset-bottom, 0);
        background: rgba(245, 244, 241, 0.94);
        backdrop-filter: saturate(180%) blur(20px);
        -webkit-backdrop-filter: saturate(180%) blur(20px);
        border-top: 1px solid var(--border-strong);
        display: grid;
        grid-template-columns: repeat(5, 1fr);
        z-index: 30;
        padding-left: 6px;
        padding-right: 6px;
    }
    .tabbar-item {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 2px;
        color: var(--subtle);
        text-decoration: none;
        font-size: 10px;
        font-weight: 500;
        line-height: 1;
        padding: 6px 4px;
        border: none;
        background: transparent;
        cursor: pointer;
        border-radius: var(--radius);
        -webkit-tap-highlight-color: transparent;
        transition: color 0.12s ease;
    }
    .tabbar-item:hover { color: var(--text-2); text-decoration: none; }
    .tabbar-item.is-active { color: var(--accent); }
    .tabbar-item.is-active .nav-icon { color: var(--accent); }
    .tabbar-item .nav-icon { width: 22px; height: 22px; color: var(--subtle); }
    .tabbar-item-label {
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
        max-width: 100%;
    }

    @media (min-width: 768px) {
        .tabbar { display: none; }
        main.content { padding-bottom: 56px; }
    }
    @media (max-width: 767px) {
        .topbar { height: 56px; background: rgba(245,244,241,0.92); padding: 0 14px; }
        .topbar-title { font-size: 15px; font-weight: 600; }
        .topbar-search-slot { display: none; }
        .topbar-actions .btn { padding: 7px 10px; }
    }

    /* ─── More sheet (mobile overflow nav) ────────────────── */
    .sheet-overlay {
        position: fixed;
        inset: 0;
        background: var(--overlay);
        z-index: 40;
        opacity: 0;
        pointer-events: none;
        transition: opacity 0.2s ease;
    }
    .sheet-overlay.is-open {
        opacity: 1;
        pointer-events: auto;
    }
    .sheet {
        position: fixed;
        bottom: 6px; left: 6px; right: 6px;
        background: var(--surface);
        border: 1px solid var(--border);
        border-radius: var(--radius-2xl);
        box-shadow: var(--shadow-lg);
        z-index: 50;
        transform: translateY(110%);
        transition: transform 0.32s cubic-bezier(.22,1.2,.36,1);
        padding-bottom: env(safe-area-inset-bottom, 0);
        max-height: 80vh;
        overflow-y: auto;
    }
    .sheet.is-open { transform: translateY(0); }
    .sheet-handle {
        width: 38px; height: 4px;
        background: var(--faint);
        border-radius: 999px;
        margin: 12px auto;
    }
    .sheet-header {
        padding: 0 var(--space-5) var(--space-3);
        font-size: 15px;
        font-weight: 600;
        text-align: center;
        color: var(--text);
        letter-spacing: -0.01em;
        font-family: var(--font-display);
    }
    .sheet-nav { list-style: none; margin: 0; padding: var(--space-2) var(--space-3) var(--space-4); }
    .sheet-nav li { margin-bottom: 2px; }
    .sheet-nav a {
        display: flex; align-items: center; gap: 12px;
        padding: 12px 14px;
        color: var(--text);
        text-decoration: none;
        font-size: 14px;
        font-weight: 500;
        border-radius: var(--radius);
        min-height: 48px;
    }
    .sheet-nav a:hover { background: var(--surface-2); text-decoration: none; }
    .sheet-nav a.is-active { background: var(--accent-soft); color: var(--accent-deep); }
    .sheet-nav a.is-active .nav-icon { color: var(--accent); }
    .sheet-nav .nav-icon { width: 20px; height: 20px; color: var(--muted); }

    @media (max-width: 480px) {
        .topbar-user-name { display: none; }
    }
    </style>
</head>
<body>
    <a class="skip-link" href="#content"><?= __('skip_to_content', 'Skip to main content') ?></a>

    <div class="app-layout">

        <!-- ─── Sidebar (desktop) ────────────────────────── -->
        <aside class="sidebar" aria-label="<?= __('primary_navigation', 'Primary navigation') ?>">
            <a href="<?= e(SLATE_URL) ?>/admin/" class="sidebar-brand">
                <span class="sidebar-brand-mark" aria-hidden="true"><?= e(mb_strtoupper(mb_substr($siteName, 0, 1))) ?></span>
                <span class="sidebar-brand-text">
                    <span class="sidebar-brand-name"><?= e($siteName) ?></span>
                    <span class="sidebar-brand-sub"><?= e($brandSublabel) ?></span>
                </span>
            </a>

            <?php foreach ($navGroups as $groupKey => $groupItems): ?>
                <div class="nav-section-label"><?= e(slate_admin_group_label((string)$groupKey)) ?></div>
                <ul class="sidebar-nav">
                    <?php foreach ($groupItems as $item):
                        $isActive = ($currentNav === ($item['slug'] ?? null));
                    ?>
                        <li class="sidebar-item <?= $isActive ? 'is-active' : '' ?>">
                            <a href="<?= e($item['href']) ?>"
                               <?= $isActive ? 'aria-current="page"' : '' ?>>
                                <?= slate_admin_nav_icon($item['icon'] ?? 'circle') ?>
                                <span><?= e($item['label']) ?></span>
                            </a>
                        </li>
                    <?php endforeach; ?>
                </ul>
            <?php endforeach; ?>

            <div class="sidebar-footer">
                <span class="topbar-avatar" aria-hidden="true">
                    <?= e(mb_strtoupper(mb_substr($user['name'] ?? 'A', 0, 1))) ?>
                </span>
                <div class="sidebar-user-info">
                    <div class="sidebar-user-name"><?= e($user['name']) ?></div>
                    <div class="sidebar-user-role"><?= e($user['role'] ?? __('administrator', 'Administrator')) ?></div>
                </div>
                <a href="<?= e(SLATE_URL) ?>/admin/logout.php"
                   class="sidebar-footer-logout"
                   title="<?= __('logout', 'Log out') ?>"
                   aria-label="<?= __('logout', 'Log out') ?>">
                    <?= slate_admin_nav_icon('logout') ?>
                </a>
            </div>
        </aside>

        <!-- ─── Topbar ───────────────────────────────────── -->
        <header class="topbar">
            <h1 class="topbar-title"><?= e($pageTitle) ?></h1>
            <div class="topbar-search-slot"><?= $topbarSearchHtml ?></div>
            <div class="topbar-actions">
                <?php foreach ($topbarActions as $actionHtml): ?>
                    <?= $actionHtml ?>
                <?php endforeach; ?>
            </div>
        </header>

        <!-- ─── Main content ─────────────────────────────── -->
        <main class="content" id="content">