<?php

declare(strict_types=1);
// Variables from StyleController::index — $themes, $default, $tokenKeys, $baseUrl, $assetsUrl, $project, $user, $navItems
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
    <title><?= htmlspecialchars($project['name'] ?? 'Project') ?> — Style</title>
    <?php include constant('BASE_PATH') . '/core/views/project-head.php'; ?>
    <?= $iconCssHtml ?? '' ?>
    <!-- designer component styles, so the live preview can render real designed pages -->
    <?= \PpPhp\Core\ModuleAssets::style('Designer', 'Views/app/designer.css', '', 'spDesignerCss') ?>
    <style>
        /* Editor-chrome aliases mapped onto the platform theme tokens so the Style
           editor's own UI follows every console theme (light + all darks). The
           preview frame (.pv-*/.sp-pvframe) keeps its own compiled tokens. */
        :root { --sp-border:var(--pp-border,#e2e8f0); --sp-muted:var(--pp-text-muted,#64748b); --sp-bg:var(--pp-app-bg,#f6f9fd); --sp-surface:var(--pp-surface,#fff); --sp-raised:var(--pp-surface-raised,#fff); --sp-text:var(--pp-text,#334155); --sp-primary:var(--pp-accent,#2563eb); --sp-primary-soft:var(--pp-accent-soft,#eff6ff); --sp-tip-bg:#0f172a; --sp-tip-text:#f8fafc; }
        html { height:100%; }
        body.sp-body { margin:0; height:100%; overflow:hidden; background:var(--sp-bg); color:var(--sp-text,#0f172a); font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif; }
        .sp-workspace { display:flex; position:fixed; top:56px; left:0; right:0; bottom:0; --sp-list-w:200px; --sp-editor-w:312px; }
        .sp-list { container-type:inline-size; width:var(--sp-list-w); flex:0 0 auto; border-right:1px solid var(--sp-border); background:var(--sp-surface); overflow:auto; }
        /* Both panel headers share one height token so the sidebar header and the
           editor header line up exactly across the split. */
        :root { --sp-head-h: 52px; }
        .sp-list-head { display:flex; align-items:center; justify-content:space-between; gap:8px;
          height:var(--sp-head-h); padding:0 12px; border-bottom:1px solid var(--sp-border); }
        .sp-head-title { display:flex; align-items:center; gap:8px; min-width:0;
          font-size:13.5px; font-weight:800; letter-spacing:-.01em; }
        .sp-head-title i { color:var(--sp-primary); font-size:15px; flex:0 0 auto; }
        .sp-head-title span { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
        .sp-head-actions { display:flex; align-items:center; gap:6px; }
        /* Icon-only: the list panel is narrow by default and a text label pushed
           the header subtitle onto three lines. The tooltip carries the meaning. */
        .sp-guidebtn { display:inline-flex; align-items:center; justify-content:center; width:30px; height:30px;
          flex:0 0 auto; border-radius:9px; border:1px solid var(--sp-border); background:var(--sp-surface);
          color:var(--sp-muted); font-size:14px; text-decoration:none;
          transition:border-color .13s ease, color .13s ease, background .13s ease; }
        .sp-guidebtn:hover { border-color:var(--sp-primary); color:var(--sp-primary); }

        .sp-resizer { flex:0 0 5px; cursor:col-resize; background:transparent; position:relative; z-index:3; }
        .sp-resizer::after { content:''; position:absolute; inset:0 2px; background:var(--sp-border); opacity:0; transition:opacity .12s; }
        .sp-resizer:hover::after, .sp-resizer.dragging::after { opacity:1; background:var(--sp-primary); }
        body.sp-resizing { cursor:col-resize; user-select:none; }
        /* Header actions: one visual family, both 30px tall so they sit on the
           header's centre line. The Guide is a quiet icon button; New is the
           single primary action. */
        .sp-head-actions { display:flex; align-items:center; gap:6px; flex:0 0 auto; }
        .sp-newbtn { display:inline-flex; align-items:center; gap:6px; height:30px; padding:0 11px;
          border:0; border-radius:9px; background:var(--sp-primary); color:#fff;
          font-size:12px; font-weight:700; line-height:1; cursor:pointer; white-space:nowrap;
          box-shadow:0 1px 3px color-mix(in srgb, var(--sp-primary) 45%, transparent);
          transition:filter .13s ease, transform .13s ease; }
        .sp-newbtn:hover { filter:brightness(1.07); }
        .sp-newbtn:active { transform:translateY(1px); }
        .sp-newbtn:focus-visible { outline:2px solid var(--sp-primary); outline-offset:2px; }
        .sp-newbtn i { font-size:13px; }
        /* Narrow sidebar: the label steps aside, the icon carries it. */
        @container (max-width: 210px) { .sp-newbtn span { display:none; } .sp-newbtn { padding:0 9px; } }
        .sp-group-label { font-size:9.5px; font-weight:800; letter-spacing:.07em; text-transform:uppercase; color:var(--sp-muted); padding:11px 13px 4px; }
        .sp-theme { display:flex; align-items:center; gap:9px; padding:6px 13px; cursor:pointer; border-left:3px solid transparent; }
        .sp-theme:hover { background:#f8fafc; } .sp-theme.active { background:var(--sp-primary-soft); border-left-color:var(--sp-primary); }
        .sp-swatch { position:relative; width:30px; height:30px; border-radius:8px; border:1px solid rgba(0,0,0,.10); flex:0 0 30px; overflow:hidden; box-shadow:0 1px 2px rgba(0,0,0,.10); }
        .sp-sw-win { position:absolute; left:4px; top:5px; right:4px; bottom:5px; border-radius:4px; border:1px solid rgba(0,0,0,.10); overflow:hidden; display:block; }
        .sp-sw-top { position:absolute; left:0; top:0; right:0; height:6px; display:block; }
        .sp-sw-ln { position:absolute; left:3px; right:8px; top:9.5px; height:2px; border-radius:2px; opacity:.9; }
        .sp-sw-ln--sm { top:14px; right:12px; opacity:.5; }
        .sp-swatch[data-glass="1"]::after { content:""; position:absolute; inset:0; background:linear-gradient(135deg,rgba(255,255,255,.55),transparent 55%); pointer-events:none; }
        .sp-theme-meta { min-width:0; flex:1 1 auto; }
        .sp-theme-meta strong { font-size:12.5px; font-weight:650; display:block;
          overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
        .sp-theme-lock { flex:0 0 auto; font-size:9.5px; color:var(--sp-muted); opacity:.55; }
        .sp-theme.active .sp-theme-lock { opacity:.85; }
        .sp-empty-note { padding:10px 14px; color:var(--sp-muted); font-size:11.5px; line-height:1.5; }
        .sp-theme .badge-def { margin-left:auto; font-size:9px; font-weight:800; color:var(--sp-primary); background:var(--sp-primary-soft); border-radius:999px; padding:2px 7px; }
        .sp-main { flex:1; min-width:0; display:flex; overflow:hidden; }
        .sp-editor { width:var(--sp-editor-w); flex:0 0 auto; border-right:1px solid var(--sp-border); background:var(--sp-surface); display:flex; flex-direction:column; overflow:hidden; }
        /* Name + sub on ONE line so this header matches the sidebar's height.
           spMainTabs is deliberately left alone. */
        .sp-ed-head { flex:0 0 auto; padding:0 14px 9px; border-bottom:1px solid var(--sp-border); background:var(--sp-surface); }
        .sp-ed-title { display:flex; align-items:center; gap:9px; min-width:0;
          height:var(--sp-head-h); }
        .sp-ed-body { flex:1; min-height:0; overflow:auto; }
        .sp-ed-head input.sp-name { flex:0 1 auto; min-width:60px; width:auto; max-width:60%;
          font-size:15px; font-weight:800; letter-spacing:-.01em; border:0;
          border-bottom:1px dashed transparent; padding:2px 0; background:transparent; color:inherit; }
        .sp-ed-head input.sp-name:focus { outline:0; border-bottom-color:var(--sp-primary); }
        .sp-ed-head .sp-sub { flex:1 1 auto; min-width:0; font-size:11px; color:var(--sp-muted);
          overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
        /* Palette panel: every token the compiler emits, resolved. Uses the
           console's own --sp-* variables so it follows the module's chrome. */
        /* ── Palette panel ──────────────────────────────────────────────────
           A reference sheet, not just swatches: each token shows its resolved
           value, what it is for, and whether it was authored or derived. */
        .sp-pal-note { font-size:12px; color:var(--sp-muted); margin:0 0 10px; line-height:1.5; }
        .sp-pal-head { display:flex; align-items:flex-start; gap:14px; flex-wrap:wrap;
          padding:13px 15px; margin-bottom:11px; border:1px solid var(--sp-border); border-radius:12px;
          background:linear-gradient(135deg, var(--sp-primary-soft), var(--sp-surface) 72%); }
        .sp-pal-lede { flex:1 1 240px; min-width:0; }
        .sp-pal-lede h3 { margin:0 0 4px; font-size:14px; font-weight:800; letter-spacing:-.01em; }
        .sp-pal-lede p { margin:0; font-size:11.5px; line-height:1.55; color:var(--sp-muted); }
        .sp-pal-stats { display:flex; gap:7px; flex:0 0 auto; }
        .sp-pal-stat { min-width:62px; padding:7px 9px; border-radius:9px; text-align:center;
          background:var(--sp-surface); border:1px solid var(--sp-border); }
        .sp-pal-stat b { display:block; font-size:16px; line-height:1.1; font-weight:800; }
        .sp-pal-stat span { display:block; font-size:9.5px; text-transform:uppercase; letter-spacing:.05em; color:var(--sp-muted); margin-top:2px; }
        .sp-pal-stat.is-authored b { color:var(--sp-primary); }
        .sp-pal-stat.is-derived b { color:var(--sp-muted); }

        .sp-pal-legend { display:flex; flex-wrap:wrap; gap:6px 16px; margin:0 2px 10px; font-size:11px; color:var(--sp-muted); }
        .sp-pal-legend strong { color:var(--sp-text,#0f172a); font-weight:700; }
        .sp-pal-legend em { font-style:normal; font-weight:600; }
        .sp-pal-legend .dot { display:inline-block; width:8px; height:8px; border-radius:50%; margin-right:6px; vertical-align:1px; }
        .sp-pal-legend .dot.authored { background:var(--sp-primary); }
        .sp-pal-legend .dot.derived { background:var(--sp-border); border:1px solid var(--sp-muted); }
        .sp-pal-hint i { margin-right:5px; }

        .sp-pal-tools { margin:0 0 12px; }
        .sp-pal-find { width:100%; padding:8px 11px; font-size:12px; border-radius:9px;
          border:1px solid var(--sp-border); background:var(--sp-surface); color:inherit; }
        .sp-pal-find:focus { outline:none; border-color:var(--sp-primary); box-shadow:0 0 0 3px var(--sp-primary-soft); }

        .sp-pal-group { margin-bottom:18px; }
        .sp-pal-group h4 { display:flex; align-items:center; gap:7px; font-size:11px; text-transform:uppercase;
          letter-spacing:.06em; color:var(--sp-muted); margin:0 0 8px; font-weight:800; }
        .sp-pal-count { font-size:9.5px; font-weight:700; padding:1px 6px; border-radius:20px;
          background:var(--sp-bg); border:1px solid var(--sp-border); letter-spacing:0; }
        .sp-pal-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(215px,1fr)); gap:8px; }

        .sp-pal-item { position:relative; display:flex; align-items:flex-start; gap:10px; width:100%; text-align:left;
          border:1px solid var(--sp-border); border-radius:10px; padding:9px 10px; background:var(--sp-surface);
          color:inherit; font:inherit; cursor:pointer; min-width:0;
          transition:border-color .13s ease, box-shadow .13s ease, transform .13s ease; }
        .sp-pal-item:hover { border-color:var(--sp-primary); transform:translateY(-1px); box-shadow:0 4px 12px rgba(2,6,23,.07); }
        .sp-pal-item:focus-visible { outline:2px solid var(--sp-primary); outline-offset:2px; }
        .sp-pal-item.is-authored { border-left:3px solid var(--sp-primary); padding-left:8px; }
        /* copy confirmation */
        .sp-pal-item::after { content:'Copied'; position:absolute; top:7px; right:8px; font-size:9.5px; font-weight:700;
          padding:2px 6px; border-radius:20px; background:var(--sp-primary); color:#fff; opacity:0; transition:opacity .15s ease; }
        .sp-pal-item.copied::after { opacity:1; }

        .sp-pal-sw { width:30px; height:30px; border-radius:7px; flex:none; border:1px solid var(--sp-border); overflow:hidden;
          background-image:linear-gradient(45deg,rgba(127,127,127,.22) 25%,transparent 25%,transparent 75%,rgba(127,127,127,.22) 75%),
                           linear-gradient(45deg,rgba(127,127,127,.22) 25%,transparent 25%,transparent 75%,rgba(127,127,127,.22) 75%);
          background-size:8px 8px; background-position:0 0,4px 4px; }
        .sp-pal-sw i { display:block; width:100%; height:100%; }
        .sp-pal-item.is-text .sp-pal-sw { display:flex; align-items:center; justify-content:center; background-image:none;
          background:var(--sp-bg); color:var(--sp-muted); }
        .sp-pal-item.is-text .sp-pal-sw em { font-style:normal; font-size:9.5px; font-weight:800; letter-spacing:-.02em; }

        .sp-pal-meta { display:flex; flex-direction:column; gap:2px; min-width:0; flex:1 1 auto; line-height:1.3; }
        .sp-pal-name { display:flex; align-items:center; gap:5px; font-size:11.5px; font-weight:700;
          color:var(--sp-text,#0f172a); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
        .sp-pal-tag { flex:none; font-style:normal; font-size:8px; font-weight:800; line-height:1;
          padding:2px 4px; border-radius:4px; background:var(--sp-primary); color:#fff; }
        .sp-pal-val { display:block; font-size:10px; color:var(--sp-muted); font-family:ui-monospace,Menlo,monospace;
          white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
        .sp-pal-doc { display:block; font-size:10.5px; line-height:1.4; color:var(--sp-muted); margin-top:3px; }
        .sp-pal-empty { font-size:12px; color:var(--sp-muted); text-align:center; padding:22px 0; }
        .sp-tabs { display:inline-flex; gap:4px; background:#eef2f8; border-radius:9px; padding:3px; margin-top:12px; }
        .sp-tabs button { border:0; background:transparent; color:var(--sp-muted); font-size:12px; font-weight:700; padding:6px 14px; border-radius:7px; cursor:pointer; }
        .sp-tabs button.active { background:var(--sp-surface); color:var(--sp-primary); box-shadow:0 1px 2px rgba(15,23,42,.08); }
        .sp-tok-group { font-size:10px; font-weight:800; letter-spacing:.06em; text-transform:uppercase; color:var(--sp-muted); padding:12px 14px 3px; }
        .sp-tok { display:grid; grid-template-columns:92px 1fr; gap:10px; align-items:center; padding:3px 14px; }
        .sp-tok > label { font-size:12px; color:var(--sp-text); font-weight:600; }
        .sp-tok .sp-cin { display:flex; gap:6px; align-items:center; }
        .sp-tok input[type=color] { width:30px; height:30px; border:1px solid var(--sp-border); border-radius:7px; padding:1px; background:var(--sp-surface); cursor:pointer; }
        .sp-tok input[type=text] { flex:1; min-width:0; height:30px; border:1px solid var(--sp-border); border-radius:7px; padding:0 8px; font-size:12px; font-family:ui-monospace,Menlo,monospace; }
        .sp-actions { flex:0 0 auto; display:flex; align-items:center; gap:6px; padding:10px 12px; border-top:1px solid var(--sp-border); background:var(--sp-surface); }
        /* The primary action is compact and single-line: a two-word label used to
           wrap and push the whole footer taller. */
        .sp-actions .sp-btn.primary { flex:0 1 auto; min-width:104px; justify-content:center; white-space:nowrap; }
        .sp-actions .sp-btn.primary span { overflow:hidden; text-overflow:ellipsis; }
        .sp-iconbtn { width:34px; height:34px; flex:0 0 34px; border:1px solid var(--sp-border); border-radius:8px; background:var(--sp-surface); color:var(--sp-muted); cursor:pointer; display:inline-flex; align-items:center; justify-content:center; font-size:14px; }
        .sp-iconbtn:hover { border-color:var(--sp-primary); color:var(--sp-primary); background:var(--sp-primary-soft); }
        .sp-iconbtn.danger:hover { border-color:#fecaca; color:#dc2626; background:#fef2f2; }
        .sp-iconbtn:disabled { opacity:.4; cursor:not-allowed; }
        /* reusable hover tooltip (shown above the element) */
        [data-tip] { position:relative; }
        [data-tip]:hover::after { content:attr(data-tip); position:absolute; bottom:calc(100% + 7px); left:50%; transform:translateX(-50%); background:#0f172a; color:#fff; font-size:11px; font-weight:600; line-height:1; padding:5px 9px; border-radius:6px; white-space:nowrap; z-index:100; pointer-events:none; box-shadow:0 6px 18px rgba(2,6,23,.28); }
        [data-tip]:hover::before { content:''; position:absolute; bottom:calc(100% + 2px); left:50%; transform:translateX(-50%); border:5px solid transparent; border-top-color:#0f172a; z-index:100; pointer-events:none; }
        .sp-mi [data-tip]:hover::after, .sp-menu-toolbar [data-tip]:hover::after,
        .sp-menued-toolbar [data-tip]:hover::after, .sp-menued-head [data-tip]:hover::after { bottom:auto; top:calc(100% + 7px); }
        .sp-mi [data-tip]:hover::before, .sp-menu-toolbar [data-tip]:hover::before,
        .sp-menued-toolbar [data-tip]:hover::before, .sp-menued-head [data-tip]:hover::before { bottom:auto; top:calc(100% + 2px); border-top-color:transparent; border-bottom-color:#0f172a; }
        .sp-btn { border:1px solid var(--sp-border); background:var(--sp-surface); border-radius:9px;
          font-size:12.5px; font-weight:650; line-height:1; padding:0 12px; height:34px; cursor:pointer;
          display:inline-flex; align-items:center; justify-content:center; gap:6px; white-space:nowrap;
          transition:border-color .13s ease, background .13s ease, color .13s ease; }
        .sp-btn:hover { border-color:#bfdbfe; background:#eff6ff; color:var(--sp-primary); }
        .sp-btn.primary { background:var(--sp-primary); border-color:var(--sp-primary); color:#fff; } .sp-btn.primary:hover { filter:brightness(1.05); color:#fff; }
        .sp-btn.danger { color:#dc2626; } .sp-btn.danger:hover { border-color:#fecaca; background:#fef2f2; }
        .sp-btn:disabled { opacity:.5; cursor:not-allowed; }
        .sp-preview-wrap { flex:1; min-width:0; display:flex; flex-direction:column; overflow:hidden; background:#eef2f7; }
        .sp-preview-bar { display:flex; align-items:center; justify-content:space-between; gap:10px; padding:9px 16px; border-bottom:1px solid var(--sp-border); background:var(--sp-surface); flex-wrap:wrap; }
        .sp-pv-left, .sp-pv-right { display:flex; align-items:center; gap:8px; }
        .sp-pv-mode { font-size:11.5px; color:var(--sp-muted); font-weight:600; }
        .sp-seg { display:inline-flex; gap:2px; background:#eef2f8; border:1px solid var(--sp-border); border-radius:8px; padding:2px; }
        .sp-seg button { border:0; background:transparent; color:var(--sp-muted); font-size:12px; font-weight:700; padding:5px 11px; border-radius:6px; cursor:pointer; line-height:1; }
        .sp-seg button.active { background:var(--sp-surface); color:var(--sp-primary); box-shadow:0 1px 2px rgba(15,23,42,.08); }
        .sp-mini { height:30px; border:1px solid var(--sp-border); border-radius:8px; padding:0 8px; font-size:12px; background:var(--sp-surface); color:var(--sp-text); max-width:170px; }
        .sp-mini:disabled { opacity:.45; }
        .sp-preview-scroll { flex:1; overflow:auto; padding:22px; }
        /* NOTE: this frame is `sp-pvframe`, not `sp-preview`. GrapesJS bundles
           Spectrum, which also uses an `sp-` prefix and defines `.sp-preview`
           as a 25x20px colour swatch — it collapsed the whole Sample preview to
           a dot. Real-page mode accidentally escaped it because that mode also
           adds `.ppd-canvas`, whose width won. Renaming removes the clash for
           good rather than relying on specificity. */
        .sp-pvframe { margin:0 auto; transition:max-width .2s; }
        .sp-pvframe.dev-tablet { max-width:768px; } .sp-pvframe.dev-mobile { max-width:390px; }

        /* ---- the live preview uses the compiled --pp-* tokens ---- */
        .sp-pvframe { border-radius:16px; overflow:hidden; box-shadow:0 20px 50px rgba(2,6,23,.14); background:var(--pp-bg); color:var(--pp-text); font-family:var(--pp-font); }
        .sp-pvframe .pv-top { display:flex; align-items:center; gap:12px; padding:14px 20px; background:var(--pp-surface); border-bottom:1px solid var(--pp-border); }
        .pv-logo { width:30px; height:30px; border-radius:8px; background:var(--pp-primary); color:var(--pp-primary-text); display:flex; align-items:center; justify-content:center; font-weight:800; }
        .pv-top nav { display:flex; gap:16px; font-size:13px; } .pv-top nav a { color:var(--pp-muted); text-decoration:none; } .pv-top nav a.on { color:var(--pp-primary); font-weight:600; }
        .pv-avatar { margin-left:auto; width:30px; height:30px; border-radius:50%; background:var(--pp-primary-soft); border:1px solid var(--pp-border); }
        .pv-body { padding:22px; display:grid; grid-template-columns:1.4fr 1fr; gap:18px; }
        .pv-card { background:var(--pp-surface); border:1px solid var(--pp-border); border-radius:var(--pp-radius); box-shadow:var(--pp-shadow); padding:18px; }
        .pv-card h3 { margin:0 0 3px; font-size:16px; } .pv-card .muted { color:var(--pp-muted); font-size:13px; margin:0 0 14px; }
        .pv-btns { display:flex; gap:8px; flex-wrap:wrap; margin-bottom:14px; }
        .pv-btn { border:1px solid var(--pp-border); background:var(--pp-surface); color:var(--pp-text); border-radius:var(--pp-radius-sm); padding:8px 14px; font-size:13px; font-weight:600; cursor:pointer; }
        .pv-btn.primary { background:var(--pp-primary); border-color:var(--pp-primary); color:var(--pp-primary-text); }
        .pv-btn.soft { background:var(--pp-primary-soft); border-color:transparent; color:var(--pp-primary); }
        .pv-btn.danger { background:var(--pp-danger); border-color:var(--pp-danger); color:#fff; }
        .pv-field { margin-bottom:12px; } .pv-field label { display:block; font-size:12px; color:var(--pp-muted); margin-bottom:4px; }
        .pv-field input { width:100%; height:38px; border:1px solid var(--pp-border); border-radius:var(--pp-radius-sm); padding:0 12px; background:var(--pp-bg); color:var(--pp-text); font-family:var(--pp-font); }
        .pv-badges { display:flex; gap:8px; }
        .pv-badge { font-size:11px; font-weight:700; padding:3px 10px; border-radius:999px; }
        .pv-badge.s { background:color-mix(in srgb, var(--pp-success) 16%, transparent); color:var(--pp-success); }
        .pv-badge.w { background:color-mix(in srgb, var(--pp-warning) 16%, transparent); color:var(--pp-warning); }
        .pv-badge.d { background:color-mix(in srgb, var(--pp-danger) 16%, transparent); color:var(--pp-danger); }
        .pv-table { width:100%; border-collapse:collapse; font-size:13px; }
        .pv-table th { text-align:left; font-size:10.5px; text-transform:uppercase; letter-spacing:.04em; color:var(--pp-muted); padding:8px 10px; border-bottom:1px solid var(--pp-border); }
        .pv-table td { padding:9px 10px; border-bottom:1px solid var(--pp-border); }
        .pv-table .chip { font-family:var(--pp-mono); font-size:11px; background:var(--pp-primary-soft); color:var(--pp-primary); border-radius:5px; padding:1px 6px; }
        .pv-stat { display:flex; gap:12px; }
        .pv-stat .s { flex:1; background:var(--pp-surface); border:1px solid var(--pp-border); border-radius:var(--pp-radius); padding:14px; }
        .pv-stat .s b { font-size:22px; } .pv-stat .s span { color:var(--pp-muted); font-size:12px; }

        /* ---- editor tabs + tab bodies ---- */
        /* ── Editor tabs ────────────────────────────────────────────────────
           A single-row segmented control. This used to be a 5-column grid with
           six tabs in it, so Palette always wrapped onto a second line.
           Now it is a flex track that adapts to the panel via a container
           query — the panel is user-resizable, so a viewport media query would
           be measuring the wrong box. */
        .sp-ed-head { container-type: inline-size; }
        .sp-maintabs { display:flex; gap:2px; margin-top:0; padding:3px;
          border-radius:11px; background:var(--sp-track,#eef2f7); border:1px solid var(--sp-border);
          overflow-x:auto; scrollbar-width:none; -ms-overflow-style:none; }
        .sp-maintabs::-webkit-scrollbar { display:none; }
        .sp-maintabs button { flex:1 1 auto; min-width:0; display:inline-flex; align-items:center;
          justify-content:center; gap:6px; padding:7px 8px; border:0; background:transparent;
          color:var(--sp-muted); font-size:11.5px; font-weight:700; line-height:1; white-space:nowrap;
          border-radius:8px; cursor:pointer; transition:background .13s ease, color .13s ease; }
        .sp-maintabs button i { font-size:14px; flex:0 0 auto; }
        .sp-maintabs button span { overflow:hidden; text-overflow:ellipsis; }
        .sp-maintabs button:hover:not(.active) { background:var(--sp-surface); color:var(--sp-text,#0f172a); }
        .sp-maintabs button:focus-visible { outline:2px solid var(--sp-primary); outline-offset:-2px; }
        /* Filled pill — a white pill on the near-white track measured 1.05:1,
           i.e. invisible. --sp-tab-on carries the label colour because the
           primary flips lightness between themes. */
        .sp-maintabs button.active { background:var(--sp-primary); color:var(--sp-tab-on,#fff);
          font-weight:800; box-shadow:0 2px 7px color-mix(in srgb, var(--sp-primary) 42%, transparent); }
        /* Tight panel: icons only — except the active tab, which keeps its label
           so you never lose track of where you are. */
        @container (max-width: 430px) {
          .sp-maintabs button { flex:0 0 auto; padding:7px 9px; }
          .sp-maintabs button span { display:none; }
          .sp-maintabs button.active { flex:1 1 auto; }
          .sp-maintabs button.active span { display:inline; }
        }
        /* No container-query support: fall back to icons only, which always fits. */
        @supports not (container-type: inline-size) {
          .sp-maintabs button span { display:none; }
          .sp-maintabs button.active span { display:inline; }
        }
        /* Light/Dark switch sits centred with a tight top margin — it is a
           mode switch, not a section header. */
        .sp-modetabs { display:flex; width:fit-content; margin:8px auto 10px; }
        /* Style SPA icon browser must sit above the item-editor modal (z 5000) */
        .ppd-icm-backdrop { z-index:6000; }
        .sp-ctl-group { font-size:10px; font-weight:800; letter-spacing:.06em; text-transform:uppercase; color:var(--sp-muted); padding:14px 14px 5px; }
        .sp-ctl { display:grid; grid-template-columns:92px 1fr; gap:9px; align-items:center; padding:4px 14px; }
        .sp-ctl > label { font-size:12px; color:var(--sp-text); font-weight:600; }
        .sp-ctl select, .sp-ctl input[type=text], .sp-ctl input[type=number] { height:30px; border:1px solid var(--sp-border); border-radius:7px; padding:0 8px; font-size:12px; background:var(--sp-surface); width:100%; }
        .sp-ctl .sp-cin { display:flex; gap:6px; align-items:center; }
        .sp-ctl input[type=color] { width:30px; height:30px; border:1px solid var(--sp-border); border-radius:7px; padding:1px; background:var(--sp-surface); }
        .sp-checkrow { display:flex; align-items:center; gap:8px; padding:5px 14px; font-size:12.5px; color:var(--sp-text); cursor:pointer; }
        .sp-checkrow input { width:15px; height:15px; accent-color:var(--sp-primary); }
        .sp-hint { padding:2px 14px 7px; font-size:11px; line-height:1.45; color:#94a3b8; }
        .sp-hint code { background:var(--pp-surface-muted,#f1f5f9); border-radius:4px; padding:1px 4px; font-size:10.5px; color:var(--sp-muted); }
        .sp-cssarea { width:calc(100% - 28px); margin:10px 14px; min-height:280px; border:1px solid var(--sp-border); border-radius:9px; padding:10px; font-family:ui-monospace,Menlo,monospace; font-size:12px; resize:vertical; }
        .sp-code { margin:0 14px 14px; background:#0f172a; color:#e2e8f0; border-radius:9px; padding:11px 13px; font-family:ui-monospace,Menlo,monospace; font-size:11.5px; white-space:pre-wrap; max-height:220px; overflow:auto; }
        .sp-code-head { font-size:10px; font-weight:800; text-transform:uppercase; letter-spacing:.05em; color:var(--sp-muted); padding:12px 14px 4px; display:flex; justify-content:space-between; }

        /* ---- menu tree editor ---- */
        .sp-menu-add { margin:10px 14px; }
        .sp-menu-tree { padding:2px 8px 8px; }
        .sp-mi { display:flex; align-items:center; gap:7px; padding:6px 8px; border:1px solid var(--sp-border); border-radius:8px; background:var(--sp-surface); margin:4px 0; }
        .sp-mi.child { margin-left:22px; }
        .sp-mi .drag { color:#cbd5e1; cursor:grab; }
        .sp-mi .mi-ico { width:24px; text-align:center; color:var(--sp-primary); }
        .sp-mi input { flex:1; min-width:0; border:1px solid transparent; border-radius:6px; padding:3px 6px; font-size:12.5px; background:transparent; }
        .sp-mi input:focus { outline:0; border-color:var(--sp-border); background:#f8fafc; }
        .sp-mi .mi-link { flex:0 0 120px; font-size:11px; color:var(--sp-muted); font-family:ui-monospace,monospace; }
        .sp-mi button { border:0; background:transparent; color:#94a3b8; cursor:pointer; padding:2px 5px; border-radius:5px; }
        .sp-mi button:hover { color:var(--sp-primary); background:var(--sp-primary-soft); }
        .sp-mi button.del:hover { color:#dc2626; background:#fef2f2; }

        /* ---- full app-shell preview ---- */
        .sp-pvframe .pv-shell { display:flex; flex-direction:column; min-height:520px; background:var(--pp-bg); border-radius:14px; overflow:hidden; }
        .pv-topbar { display:flex; align-items:center; gap:14px; height:var(--pp-topbar-h,56px); padding:0 16px; background:var(--pp-topbar-bg,#0f172a); color:var(--pp-topbar-text,#cbd5e1); flex:0 0 auto; }
        /* Must match BuildWriter's .pp-topbar rules exactly — the preview is a
           promise the generated app has to keep. */
        .pv-topbar.grad { background:linear-gradient(100deg, var(--pp-topbar-bg,#0f172a), var(--pp-primary)); }
        .pv-topbar.elev-none { box-shadow:none; }
        .pv-topbar.elev-border { box-shadow:inset 0 -1px 0 var(--pp-topbar-border); }
        .pv-topbar.elev-shadow { box-shadow:inset 0 -1px 0 var(--pp-topbar-border), var(--pp-shadow-sm); }
        .pv-topbar.blur { background:color-mix(in srgb, var(--pp-topbar-bg,#0f172a) 78%, transparent);
          -webkit-backdrop-filter:saturate(180%) blur(12px); backdrop-filter:saturate(180%) blur(12px); }
        .pv-topbar.blur.grad { background:linear-gradient(100deg,
          color-mix(in srgb, var(--pp-topbar-bg,#0f172a) 78%, transparent),
          color-mix(in srgb, var(--pp-primary) 78%, transparent)); }
        /* The preview does not scroll, so "shrink on scroll" is shown as a hint
           badge rather than faked — claiming an effect we can't demonstrate
           would be worse than labelling it. */
        .pv-topbar.shrink { position:relative; }
        .pv-topbar.shrink::after { content:'shrinks on scroll'; position:absolute; right:10px; bottom:-9px;
          font-size:8.5px; font-weight:800; letter-spacing:.04em; text-transform:uppercase;
          padding:2px 6px; border-radius:20px; background:var(--pp-primary); color:var(--pp-primary-text,#fff); }
        .pv-brand { display:flex; align-items:center; gap:9px; font-weight:800; color:var(--pp-logo-text,#fff); min-width:var(--pp-sidebar-w,250px); }
        .pv-brand .pv-logo { width:30px; height:30px; border-radius:8px; background:var(--pp-logo-bg,var(--pp-primary)); color:#fff; display:flex; align-items:center; justify-content:center; font-weight:800; }
        .pv-tb-ico { opacity:.85; font-size:15px; } .pv-tb-crumb { font-weight:600; opacity:.95; }
        .pv-tb-search { margin-left:auto; display:flex; align-items:center; gap:7px;
          background:var(--pp-topbar-hover, rgba(255,255,255,.14)); border-radius:var(--pp-radius-sm,8px);
          padding:6px 11px; font-size:12.5px; min-width:180px; }
        .pv-tb-search span { opacity:.62; }
        .pv-tb-search kbd { margin-left:auto; font-family:var(--pp-mono,monospace); font-size:9.5px; padding:2px 5px;
          border-radius:4px; opacity:.7; background:var(--pp-topbar-active-bg, rgba(255,255,255,.16)); }
        .pv-tb-user { display:flex; align-items:center; gap:8px; font-size:12.5px; opacity:.95; }
        /* Unpinned sidebar scrolls away with the content (see .sidebar-static). */
        .pv-sidebar.static { align-self:flex-start; }
        /* Auto hide is a response to window width, which the fixed-size preview
           cannot reproduce — so it is labelled rather than faked. */
        .pv-sidebar.autohide { position:relative; }
        .pv-sidebar.autohide::after { content:'auto-hides on narrow screens'; position:absolute; left:6px; right:6px; bottom:6px;
          font-size:8.5px; font-weight:800; letter-spacing:.03em; text-transform:uppercase; text-align:center;
          padding:3px 5px; border-radius:6px; background:var(--pp-primary); color:var(--pp-primary-text,#fff); }
        .pv-shell-body { flex:1; display:flex; min-height:0; }
        .pv-shell-body.rtl { flex-direction:row-reverse; }
        .pv-sidebar { flex:0 0 var(--pp-sidebar-w,250px); background:var(--pp-sidebar-bg,#0f172a); color:var(--pp-sidebar-text,#cbd5e1); padding:12px 10px; }
        .pv-sidebar.compact { flex-basis:64px; }
        .pv-sidebar.off { display:none; }
        .pv-menu-item { display:flex; align-items:center; gap:10px; padding:9px 12px; border-radius:8px; font-size:13px; color:var(--pp-sidebar-text,#cbd5e1); margin-bottom:2px; cursor:pointer; }
        .pv-menu-item .mi-i { width:18px; text-align:center; }
        /* Match the real app's shell tokens exactly: hover uses --pp-sidebar-hover,
           the active row uses the tinted --pp-sidebar-active-bg (not solid primary),
           so a theme's surface shell previews the same tinted highlight it builds. */
        .pv-menu-item:hover { background:var(--pp-sidebar-hover,rgba(255,255,255,.10)); color:var(--pp-sidebar-active,#fff); }
        .pv-menu-item.on { background:var(--pp-sidebar-active-bg,var(--pp-sidebar-accent,var(--pp-primary))); color:var(--pp-sidebar-active,#fff); }
        .pv-menu-style-flat .pv-menu-item { border-radius:0; }
        .pv-menu-style-pill .pv-menu-item { border-radius:999px; }
        .pv-sidebar.compact .pv-menu-item span.lbl, .pv-sidebar.compact .pv-menu-sub { display:none; }
        .pv-menu-sub .pv-menu-item { font-size:12.5px; padding-left:34px; opacity:.9; }
        .pv-main { flex:1; min-width:0; overflow:auto; padding:20px; }
        .pv-footer { flex:0 0 auto; padding:11px 18px; background:var(--pp-surface); border-top:1px solid var(--pp-border); color:var(--pp-muted); font-size:12px; text-align:center; }
        .pv-footer.off { display:none; }
        /* grid appearance in the shell preview */
        .pv-grid { --pvg:var(--pp-border); }
        .pv-grid.panel { background:var(--pp-grid-panel,#fff); border:1px solid var(--pp-border); border-radius:var(--pp-radius,12px); padding:10px; box-shadow:var(--pp-shadow); }
        .pv-grid table { width:100%; border-collapse:collapse; font-size:13px; background:var(--pp-grid-bg,#fff); }
        .pv-grid th { text-align:left; font-size:10.5px; text-transform:uppercase; letter-spacing:.03em; color:var(--pp-muted); padding:8px 10px; border-bottom:2px solid var(--pp-border); }
        .pv-grid td { padding:9px 10px; border-bottom:1px solid var(--pp-border); }
        .pv-grid.vborders th, .pv-grid.vborders td { border-right:1px solid var(--pp-border); }
        .pv-grid.zebra tbody tr:nth-child(even) { background:var(--pp-surface-soft); }
        .pv-grid.condensed th, .pv-grid.condensed td { padding:4px 8px; }
        .pv-grid.rollover tbody tr:hover { background:var(--pp-primary-soft); }

        /* ── Responsive sidebar in the device previews ─────────────────────────
           Tablet/mobile widths turn the sidebar into an off-canvas drawer that
           starts collapsed and slides in when the topbar menu button is tapped —
           like the generated app on a small screen (a 250px sidebar otherwise
           swallows a 390px phone preview). Desktop keeps the sidebar docked. */
        .pv-tb-ico { cursor:default; }
        .sp-pvframe.dev-tablet .pv-tb-ico, .sp-pvframe.dev-mobile .pv-tb-ico { cursor:pointer; padding:5px; margin:-5px 0; border-radius:7px; }
        .sp-pvframe.dev-tablet .pv-tb-ico:hover, .sp-pvframe.dev-mobile .pv-tb-ico:hover { background:var(--pp-topbar-hover,rgba(255,255,255,.14)); }
        .sp-pvframe.dev-tablet .pv-shell-body, .sp-pvframe.dev-mobile .pv-shell-body { position:relative; overflow:hidden; }
        .sp-pvframe.dev-tablet .pv-sidebar, .sp-pvframe.dev-mobile .pv-sidebar {
          position:absolute; top:0; bottom:0; left:0; z-index:20; width:min(var(--pp-sidebar-w,250px),78%);
          transform:translateX(-100%); transition:transform .18s ease; box-shadow:2px 0 22px rgba(0,0,0,.3); }
        .sp-pvframe.dev-tablet .pv-shell-body.rtl .pv-sidebar, .sp-pvframe.dev-mobile .pv-shell-body.rtl .pv-sidebar {
          left:auto; right:0; transform:translateX(100%); box-shadow:-2px 0 22px rgba(0,0,0,.3); }
        .sp-pvframe.dev-tablet .pv-shell.pv-nav-open .pv-sidebar, .sp-pvframe.dev-mobile .pv-shell.pv-nav-open .pv-sidebar { transform:translateX(0); }
        .sp-pvframe.dev-tablet .pv-shell.pv-nav-open .pv-shell-body::after, .sp-pvframe.dev-mobile .pv-shell.pv-nav-open .pv-shell-body::after {
          content:''; position:absolute; inset:0; z-index:15; background:rgba(0,0,0,.42); }
        /* Topbar responsiveness — mirrors the app breakpoints. The brand's desktop
           250px min-width (which aligns the logo over the sidebar) is dropped so it
           can't overflow a narrow bar; a topbar-placed menu collapses to the drawer;
           and on mobile (sm) the search shrinks to an icon and the breadcrumb + user
           label are trimmed, exactly like the generated app. */
        .sp-pvframe.dev-tablet .pv-brand, .sp-pvframe.dev-mobile .pv-brand { min-width:0; }
        .sp-pvframe.dev-tablet .pv-topnav, .sp-pvframe.dev-mobile .pv-topnav { display:none; }
        .sp-pvframe.dev-mobile .pv-topbar { gap:10px; padding:0 12px; }
        .sp-pvframe.dev-mobile .pv-tb-crumb { display:none; }
        .sp-pvframe.dev-mobile .pv-tb-search { min-width:0; flex:0 0 auto; padding:7px 9px; }
        .sp-pvframe.dev-mobile .pv-tb-search span, .sp-pvframe.dev-mobile .pv-tb-search kbd { display:none; }
        .sp-pvframe.dev-mobile .pv-tb-user { gap:0; font-size:0; }
        .sp-pvframe.dev-mobile .pv-tb-user i { font-size:16px; }

        /* ---- navbar type selector ---- */
        /* ── Navbar picker ──────────────────────────────────────────────────
           Compact icon tiles in a responsive grid. The long descriptions moved
           out of the tile (they were forcing the whole panel wide) and into a
           hover tooltip, so the picker stays narrow at any panel width. */
        .sp-navbars { display:grid; grid-template-columns:repeat(auto-fit, minmax(86px, 1fr)); gap:7px; padding:4px 14px 8px; }
        .sp-navbar-opt { position:relative; display:flex; flex-direction:column; align-items:center; justify-content:flex-start;
          gap:7px; text-align:center; padding:11px 6px 9px; border:1px solid var(--sp-border); border-radius:11px;
          background:var(--sp-surface); color:inherit; cursor:pointer; transition:border-color .13s ease, box-shadow .13s ease, transform .13s ease; }
        .sp-navbar-opt:hover { border-color:var(--sp-primary); transform:translateY(-1px); }
        .sp-navbar-opt:focus-visible { outline:2px solid var(--sp-primary); outline-offset:2px; }
        .sp-navbar-opt.active { border-color:var(--sp-primary); box-shadow:0 0 0 2px var(--sp-primary-soft); }
        .sp-navbar-opt .nv-ico { width:38px; height:30px; flex:0 0 auto; border-radius:7px; background:var(--sp-primary-soft);
          color:var(--sp-primary); display:flex; align-items:center; justify-content:center; }
        .sp-navbar-opt .nv-ico svg { display:block; }
        .sp-navbar-opt.active .nv-ico { background:var(--sp-primary); color:#fff; }
        .sp-navbar-opt .nv-meta { display:block; min-width:0; width:100%; }
        .sp-navbar-opt .nv-meta strong { display:block; font-size:11.5px; font-weight:600; line-height:1.25;
          white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
        /* active tick — confirms selection without needing a wide highlighted row */
        /* Selected marker — a full check badge, not a dot, so the active tile
           reads at a glance across the whole grid. */
        .sp-navbar-opt .nv-tick { position:absolute; top:-7px; right:-7px; width:22px; height:22px; border-radius:50%;
          background:var(--sp-primary); color:#fff; display:none; align-items:center; justify-content:center;
          box-shadow:0 0 0 2.5px var(--sp-surface), 0 2px 6px rgba(2,6,23,.28); }
        .sp-navbar-opt .nv-tick svg { display:block; width:13px; height:13px; }
        .sp-navbar-opt.active .nv-tick { display:flex; }
        .sp-navbar-opt.active { border-color:var(--sp-primary); border-width:1.5px; padding:10.5px 5.5px 8.5px;
          box-shadow:0 0 0 3px var(--sp-primary-soft); }
        .sp-navbar-opt.active .nv-meta strong { color:var(--sp-primary); }
        /* the badge sits proud of the tile, so the grid needs room for it */
        .sp-navbars { overflow:visible; padding-top:11px; }

        /* Description of the hovered (or selected) navbar type. */
        .sp-navbar-desc { margin:0 14px 10px; padding:7px 10px; min-height:34px; display:flex; align-items:center;
          border-radius:8px; background:var(--sp-bg); border:1px solid var(--sp-border);
          color:var(--sp-muted); font-size:11px; line-height:1.4; }

        /* Locked built-in: the fields stay visible (so you can read the values)
           but are inert, with a subtle wash so the state is obvious. */
        .sp-ed-body.is-locked .sp-ctl input,
        .sp-ed-body.is-locked .sp-ctl select,
        .sp-ed-body.is-locked .sp-ctl textarea,
        .sp-ed-body.is-locked .sp-tok input { opacity:.72; cursor:not-allowed; }
        .sp-ed-body.is-locked .sp-tab:not([data-tab="palette"]) { position:relative; }

        /* "Name your theme" cue on New / Duplicate. */
        .sp-ed-head input.sp-name.is-asking { border-bottom-color:var(--sp-primary);
          animation:sp-name-ask 1.1s ease-in-out 2; }
        @keyframes sp-name-ask {
          0%, 100% { box-shadow:0 0 0 0 transparent; }
          50% { box-shadow:0 3px 0 -1px var(--sp-primary); }
        }
        .sp-ed-head input.sp-name::placeholder { color:var(--sp-muted); opacity:.75; font-weight:600; }

        /* Floating hint bubble. Lives in <body> at position:fixed (see spTip in
           style-spa.js) so the scrolling settings panel can never clip it. */
        .sp-tip { position:fixed; z-index:9000; max-width:250px; padding:8px 11px; border-radius:8px;
          background:var(--sp-tip-bg); color:var(--sp-tip-text);
          font-size:11.5px; font-weight:500; line-height:1.45; text-align:left;
          box-shadow:0 10px 30px rgba(2,6,23,.28); pointer-events:none;
          opacity:0; transform:translateY(-3px); transition:opacity .12s ease, transform .12s ease; }
        .sp-tip.is-on { opacity:1; transform:translateY(0); }
        .sp-tip.is-above { transform:translateY(3px); }
        .sp-tip.is-above.is-on { transform:translateY(0); }

        /* "?" hint chip beside a group heading */
        .sp-ctl-group .sp-hintchip { display:inline-flex; align-items:center; justify-content:center;
          width:14px; height:14px; margin-left:6px; border-radius:50%; border:1px solid var(--sp-border);
          background:var(--sp-surface); color:var(--sp-muted); font-size:9px; font-weight:700; cursor:help; vertical-align:middle; }
        .sp-ctl-group .sp-hintchip:hover { border-color:var(--sp-primary); color:var(--sp-primary); }

        /* ---- topbar horizontal menu + navbar variants ---- */
        .pv-topnav { display:flex; align-items:center; gap:4px; margin-left:8px; }
        .pv-topnav-item { display:inline-flex; align-items:center; gap:6px; padding:7px 12px; border-radius:8px; font-size:13px; color:var(--pp-topbar-text,#cbd5e1); text-decoration:none; cursor:pointer; }
        .pv-topnav-item:hover { background:rgba(255,255,255,.12); } .pv-topnav-item.on { background:rgba(255,255,255,.16); color:#fff; }
        .pv-shell.nb-boxed { max-width:1040px; margin:0 auto; border:1px solid var(--pp-border); }
        .pv-shell.nb-boxed .pv-topbar { margin:10px; border-radius:12px; height:52px; }
        .pv-shell.nb-hero .pv-topbar { flex-direction:column; align-items:flex-start; justify-content:center; gap:8px; padding:16px 22px; }
        .pv-shell.nb-hero .pv-brand { font-size:20px; } .pv-shell.nb-hero .pv-topnav { margin-left:0; }

        /* ---- menu editor tree ---- */
        .sp-menu-toolbar { display:flex; gap:8px; padding:10px 14px 4px; }
        .sp-menu-tree { padding:4px 12px; }
        .sp-menu-hint { font-size:11px; color:var(--sp-muted); padding:6px 14px 12px; } .sp-menu-hint i { margin-right:4px; }
        .sp-menu-empty { padding:20px 14px; color:#94a3b8; font-size:12.5px; text-align:center; }
        .sp-mi { display:flex; align-items:center; gap:8px; padding:8px 10px; border:1px solid var(--sp-border); border-radius:9px; background:var(--sp-surface); margin:5px 0; position:relative; }
        .sp-mi.child { margin-left:26px; background:#fbfdff; }
        .sp-mi.is-sub { border-color:#c7d2fe; background:#f8fafc; }
        .sp-mi.dragging { opacity:.4; }
        .sp-mi.drop-before::before, .sp-mi.drop-after::after { content:''; position:absolute; left:6px; right:6px; height:3px; background:var(--sp-primary); border-radius:2px; }
        .sp-mi.drop-before::before { top:-4px; } .sp-mi.drop-after::after { bottom:-4px; }
        .sp-mi.drop-into { outline:2px dashed var(--sp-primary); outline-offset:1px; }
        .sp-mi .drag { color:#cbd5e1; cursor:grab; }
        .sp-mi .mi-toggle { width:20px; height:20px; flex:0 0 20px; padding:0; border:0; background:transparent; color:var(--sp-muted); cursor:pointer; border-radius:5px; display:flex; align-items:center; justify-content:center; }
        .sp-mi .mi-toggle:hover { background:var(--sp-primary-soft); color:var(--sp-primary); }
        .sp-mi .mi-toggle-sp { width:20px; flex:0 0 20px; }
        .sp-mi .mi-count { flex:0 0 auto; font-size:10px; font-weight:800; color:var(--sp-primary); background:var(--sp-primary-soft); border-radius:999px; padding:1px 7px; }
        .sp-mi .mi-ico { width:24px; text-align:center; color:var(--sp-primary); font-size:15px; }
        .sp-mi .mi-label { flex:1; min-width:0; font-size:13px; font-weight:600; color:var(--sp-text); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
        .sp-mi .mi-link { flex:0 0 auto; max-width:130px; font-size:11px; color:var(--sp-muted); font-family:ui-monospace,monospace; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
        .sp-mi button { border:0; background:transparent; color:#94a3b8; cursor:pointer; padding:3px 6px; border-radius:6px; }
        .sp-mi button:hover { color:var(--sp-primary); background:var(--sp-primary-soft); } .sp-mi button.del:hover { color:#dc2626; background:#fef2f2; }

        /* ---- item editor modal ---- */
        .sp-modal-back { position:fixed; inset:0; z-index:5000; background:rgba(15,23,42,.5); backdrop-filter:blur(2px); display:flex; align-items:center; justify-content:center; padding:24px; }
        .sp-modal-back[hidden] { display:none; }
        .sp-modal { width:min(680px,96vw); max-height:88vh; display:flex; flex-direction:column; background:var(--sp-surface); border-radius:14px; box-shadow:0 24px 70px rgba(2,6,23,.5); overflow:hidden; }
        .sp-modal-head { display:flex; align-items:center; justify-content:space-between; padding:14px 18px; border-bottom:1px solid var(--sp-border); }
        .sp-modal-x { width:30px; height:30px; border:1px solid var(--sp-border); border-radius:8px; background:var(--sp-surface); cursor:pointer; }
        .sp-modal-body { padding:12px 18px; overflow:auto; }
        .sp-modal-body .sp-ctl { grid-template-columns:120px 1fr; padding:8px 0; }
        .sp-modal-body .sp-checkrow { padding:6px 0; }
        .sp-modal-foot { display:flex; justify-content:flex-end; gap:8px; padding:12px 18px; border-top:1px solid var(--sp-border); }
        .sp-radios { display:flex; gap:16px; flex-wrap:wrap; } .sp-radio { display:inline-flex; align-items:center; gap:6px; font-size:13px; cursor:pointer; } .sp-radio input { accent-color:var(--sp-primary); }
        .sp-item-linksec { border-top:1px dashed var(--sp-border); margin-top:8px; padding-top:6px; }
        .sp-icon-prev { width:32px; height:32px; flex:0 0 32px; border:1px solid var(--sp-border); border-radius:7px; display:flex; align-items:center; justify-content:center; color:var(--sp-primary); font-size:15px; }
        .sp-modal-body #it-page, .sp-modal-body #it-sub { flex:1; height:32px; border:1px solid var(--sp-border); border-radius:7px; padding:0 8px; font-size:12.5px; }

        /* ============================================================
           Menu tab → launcher card (opens the full Menu Editor modal)
           ============================================================ */
        .sp-menu-launch { padding:14px; }
        .sp-ml-head { display:flex; align-items:center; gap:11px; margin-bottom:12px; }
        .sp-ml-ic { width:38px; height:38px; flex:0 0 38px; border-radius:10px; background:var(--sp-primary-soft); color:var(--sp-primary); display:flex; align-items:center; justify-content:center; font-size:19px; }
        .sp-ml-meta strong { font-size:14px; display:block; color:var(--sp-text); }
        .sp-ml-meta small { font-size:11px; color:var(--sp-muted); }
        .sp-ml-restricted { white-space:nowrap; color:var(--sp-primary); font-weight:600; }
        .sp-ml-restricted i { margin-right:2px; }
        .sp-menu-launch-list { border:1px solid var(--sp-border); border-radius:11px; overflow:hidden; background:var(--sp-surface); cursor:pointer; transition:border-color .12s, box-shadow .12s; }
        .sp-menu-launch-list:hover { border-color:var(--sp-primary); box-shadow:0 3px 14px var(--sp-shadow); }
        .sp-menu-launch-item { display:flex; align-items:center; gap:9px; padding:8px 12px; border-bottom:1px solid var(--sp-border); font-size:12.5px; color:var(--sp-text); }
        .sp-menu-launch-item:last-child { border-bottom:0; }
        .sp-menu-launch-item .mi-ico { width:20px; text-align:center; color:var(--sp-primary); }
        .sp-menu-launch-item .lbl { flex:1; min-width:0; font-weight:600; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
        .sp-menu-launch-item .sp-ml-lock { color:var(--sp-muted); font-size:12px; }
        .sp-menu-launch-item .sp-ml-count { font-size:10px; font-weight:800; color:var(--sp-primary); background:var(--sp-primary-soft); border-radius:999px; padding:1px 7px; }
        .sp-menu-launch-more { padding:7px 12px; font-size:11px; color:var(--sp-muted); text-align:center; }
        .sp-menu-launch-empty { display:flex; align-items:center; gap:9px; justify-content:center; padding:22px 12px; border:1px dashed var(--sp-border); border-radius:11px; color:var(--sp-muted); font-size:12.5px; }
        .sp-ml-open { width:100%; justify-content:center; margin:12px 0 2px; }

        /* ============================================================
           Menu Editor modal — 3-pane workspace
           ============================================================ */
        .sp-menued-back { position:fixed; inset:0; z-index:5000; background:rgba(15,23,42,.55); backdrop-filter:blur(3px); display:flex; align-items:center; justify-content:center; padding:22px; }
        .sp-menued-back[hidden] { display:none; }
        .sp-menued { position:relative; width:min(1180px,97vw); height:min(860px,90vh); display:flex; flex-direction:column; background:var(--sp-surface); border:1px solid var(--sp-border); border-radius:16px; box-shadow:0 30px 90px rgba(2,6,23,.5); overflow:hidden; color:var(--sp-text); }
        /* explicit button text colour so the modal never inherits the UA
           system `buttontext` (which turns white under color-scheme:dark) */
        .sp-menued .sp-btn { color:var(--sp-text); }
        .sp-menued .sp-btn:hover { color:var(--sp-primary); }
        .sp-menued .sp-btn.primary, .sp-menued .sp-btn.primary:hover { color:#fff; }
        .sp-menued .sp-btn.danger { color:#dc2626; }
        .sp-menued .sp-iconbtn { color:var(--sp-text); }
        .sp-menued .sp-iconbtn:hover { color:var(--sp-primary); }
        .sp-menued-head { display:flex; align-items:center; justify-content:space-between; gap:12px; padding:14px 18px; border-bottom:1px solid var(--sp-border); background:var(--sp-surface); flex:0 0 auto; }
        .sp-menued-title { display:flex; align-items:center; gap:12px; min-width:0; }
        .sp-menued-ic { width:38px; height:38px; flex:0 0 38px; border-radius:10px; background:var(--sp-primary); color:#fff; display:flex; align-items:center; justify-content:center; font-size:18px; box-shadow:0 4px 14px color-mix(in srgb,var(--sp-primary) 45%,transparent); }
        .sp-menued-title-txt strong { font-size:16px; display:block; }
        .sp-menued-title-txt small { font-size:11.5px; color:var(--sp-muted); }
        .sp-menued-head-tools { display:flex; align-items:center; gap:12px; flex:0 0 auto; }
        .sp-menued-asrole { display:inline-flex; align-items:center; gap:7px; font-size:12px; font-weight:600; color:var(--sp-muted); }
        .sp-menued-asrole > i { color:var(--sp-primary); }
        .sp-menued-asrole select { height:30px; }
        .sp-menued .sp-modal-x { width:32px; height:32px; flex:0 0 32px; border:1px solid var(--sp-border); border-radius:9px; background:var(--sp-raised); color:var(--sp-text); cursor:pointer; display:flex; align-items:center; justify-content:center; }
        .sp-menued .sp-modal-x:hover { border-color:var(--sp-primary); color:var(--sp-primary); }

        .sp-menued-body { flex:1 1 auto; min-height:0; display:grid; grid-template-columns:372px minmax(300px,1fr) 260px; }
        .sp-menued-pane { min-height:0; min-width:0; overflow:auto; display:flex; flex-direction:column; }
        .sp-menued-treepane { border-right:1px solid var(--sp-border); }
        .sp-menued-detailpane { border-right:1px solid var(--sp-border); background:var(--sp-bg); }
        .sp-menued-previewpane { background:var(--sp-bg); }

        .sp-menued-toolbar { display:flex; gap:6px; align-items:center; padding:12px 14px 8px; flex:0 0 auto; }
        .sp-menued-toolbar .sp-btn { padding:7px 10px; font-size:12px; }
        .sp-menued-toolbar .sp-iconbtn { width:30px; height:30px; flex-basis:30px; }
        .sp-menued-tbdiv { width:1px; align-self:stretch; margin:4px 3px; background:var(--sp-border); }
        .sp-menued-search { display:flex; align-items:center; gap:7px; margin:0 14px 4px; padding:0 10px; height:32px; border:1px solid var(--sp-border); border-radius:9px; background:var(--sp-input); color:var(--sp-muted); }
        .sp-menued-search input { border:0; outline:0; background:transparent; width:100%; font-size:12.5px; color:var(--sp-text); }
        .sp-menued-treewrap { flex:1 1 auto; min-height:0; overflow:auto; }
        .sp-menued-treewrap .sp-menu-tree { padding:4px 12px 8px; }

        /* tree rows inside the editor: compact + selectable, labels get full width
           (the destination text is hidden here — it lives in the detail pane) */
        .sp-menued .sp-mi { cursor:pointer; padding:5px 8px; gap:6px; margin:3px 0; border-radius:8px; }
        .sp-menued .sp-mi.child { margin-left:20px; }
        .sp-menued .sp-mi.is-selected { border-color:var(--sp-primary); box-shadow:0 0 0 2px var(--sp-primary-soft); }
        .sp-menued .sp-mi .mi-link { display:none; }
        .sp-menued .sp-mi .mi-ico { width:22px; font-size:14px; }
        .sp-menued .sp-mi .mi-label { font-size:12.5px; }
        .sp-menued .sp-mi .drag { font-size:12px; }
        .sp-menued .sp-mi .mi-toggle, .sp-menued .sp-mi .mi-toggle-sp { width:18px; flex-basis:18px; height:18px; }
        .sp-menued .sp-mi button[data-act] { padding:2px 5px; }
        .sp-menued .sp-mi .mi-roles { flex:0 0 auto; display:inline-flex; align-items:center; gap:2px; font-size:10px; font-weight:800; color:var(--sp-primary); background:var(--sp-primary-soft); border-radius:999px; padding:1px 6px; }
        .sp-menued .sp-mi .mi-roles i { font-size:10px; }

        /* centre detail pane */
        .sp-menued-detail-head { display:flex; align-items:center; gap:11px; padding:14px 16px 10px; border-bottom:1px solid var(--sp-border); flex:0 0 auto; }
        .sp-menued-detail-ic { width:34px; height:34px; flex:0 0 34px; border-radius:9px; background:var(--sp-primary-soft); color:var(--sp-primary); display:flex; align-items:center; justify-content:center; font-size:16px; }
        .sp-menued-detail-txt { flex:1; min-width:0; }
        .sp-menued-detail-txt strong { font-size:14px; display:block; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
        .sp-menued-detail-txt small { font-size:11px; color:var(--sp-muted); text-transform:uppercase; letter-spacing:.05em; font-weight:700; }
        .sp-menued-detail-actions { display:flex; gap:6px; flex:0 0 auto; }
        .sp-menued-detail-actions .sp-iconbtn { width:30px; height:30px; flex-basis:30px; }
        .sp-menued-detail-body { flex:1 1 auto; overflow:auto; padding:6px 0 16px; }
        .sp-menued-detail-body .sp-ctl { grid-template-columns:86px 1fr; padding:6px 16px; }
        .sp-menued-detail-body .sp-checkrow { padding:6px 16px; }
        .sp-menued-detail-body .sp-cin { display:flex; gap:6px; align-items:center; }
        .sp-menued-detail-body #it-page, .sp-menued-detail-body #it-sub { flex:1; min-width:0; height:30px; border:1px solid var(--sp-border); border-radius:7px; padding:0 8px; font-size:12px; background:var(--sp-input); color:var(--sp-text); }
        .sp-menued-linksec { border-top:1px dashed var(--sp-border); margin-top:8px; }
        .sp-menued-detail-body .sp-ctl-group { padding:12px 16px 4px; }
        .sp-menued .sp-icon-prev { width:30px; height:30px; flex:0 0 30px; border:1px solid var(--sp-border); border-radius:7px; display:flex; align-items:center; justify-content:center; color:var(--sp-primary); font-size:14px; background:var(--sp-input); }
        .sp-menued-empty { height:100%; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:12px; padding:30px; color:var(--sp-muted); text-align:center; }
        .sp-menued-empty i { font-size:30px; opacity:.45; }
        .sp-menued-empty p { font-size:12.5px; margin:0; line-height:1.6; }

        /* role visibility (tags) */
        .sp-menued-roles { padding:2px 16px 4px; }
        .sp-menued-roles-mode { display:flex; gap:16px; padding:2px 0 6px; }
        .sp-menued-roles-mode .sp-radio { font-size:12.5px; gap:5px; }
        .sp-menued-roles-mode .sp-radio i { color:var(--sp-primary); }
        .sp-tags-wrap[hidden] { display:none; }
        .sp-tags { display:flex; flex-wrap:wrap; gap:6px; align-items:center; padding:7px; border:1px solid var(--sp-border); border-radius:9px; background:var(--sp-input); }
        .sp-tag { display:inline-flex; align-items:center; gap:5px; font-size:12px; font-weight:600; color:var(--sp-primary); background:var(--sp-primary-soft); border-radius:7px; padding:3px 4px 3px 9px; }
        .sp-tag-x { border:0; background:transparent; color:var(--sp-primary); cursor:pointer; font-size:15px; line-height:1; padding:0 3px; border-radius:5px; }
        .sp-tag-x:hover { background:color-mix(in srgb,var(--sp-primary) 22%,transparent); }
        .sp-tags-input { flex:1; min-width:90px; border:0; outline:0; background:transparent; font-size:12.5px; color:var(--sp-text); padding:3px 2px; }
        .sp-tags-suggest { margin-top:8px; }
        .sp-tags-sgroup { display:flex; flex-wrap:wrap; align-items:center; gap:6px; margin-top:6px; }
        .sp-tags-slabel { font-size:9.5px; font-weight:800; letter-spacing:.05em; text-transform:uppercase; color:var(--sp-muted); margin-right:2px; }
        .sp-tag-add { display:inline-flex; align-items:center; gap:3px; font-size:11.5px; font-weight:600; color:var(--sp-muted); background:var(--sp-raised); border:1px solid var(--sp-border); border-radius:999px; padding:3px 10px 3px 7px; cursor:pointer; }
        .sp-tag-add:hover { border-color:var(--sp-primary); color:var(--sp-primary); background:var(--sp-primary-soft); }
        .sp-tag-add i { font-size:12px; }
        .sp-tags-hint { font-size:11px; color:var(--sp-muted); margin-top:8px; line-height:1.5; }
        .sp-tags-hint-dim { display:block; margin-top:2px; opacity:.8; font-style:italic; }

        /* right: live role-aware preview */
        .sp-menued-pv-head { padding:14px 16px 8px; font-size:11px; font-weight:800; text-transform:uppercase; letter-spacing:.05em; color:var(--sp-muted); flex:0 0 auto; }
        .sp-menued-pv-head i { color:var(--sp-primary); margin-right:5px; }
        .sp-mpv { margin:0 14px; padding:10px 8px; border:1px solid var(--sp-border); border-radius:12px; background:var(--sp-surface); flex:0 0 auto; }
        .sp-mpv-brand { display:flex; align-items:center; gap:9px; padding:4px 8px 12px; margin-bottom:4px; border-bottom:1px solid var(--sp-border); font-weight:700; font-size:13px; }
        .sp-mpv-logo { width:26px; height:26px; flex:0 0 26px; border-radius:7px; background:var(--sp-primary); color:#fff; display:flex; align-items:center; justify-content:center; font-size:12px; font-weight:800; }
        .sp-mpv-brand-t { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
        .sp-mpv-nav { display:flex; flex-direction:column; gap:var(--mpv-gap,2px); padding-top:6px; }
        .sp-mpv-item { display:flex; align-items:center; gap:9px; min-height:var(--mpv-h,34px); padding:4px var(--mpv-padx,10px); border-radius:8px; font-size:12.5px; color:var(--sp-text); }
        .sp-mpv-item .mi-i { width:18px; text-align:center; color:var(--sp-muted); font-size:14px; }
        .sp-mpv-item .lbl { flex:1; min-width:0; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
        .sp-mpv-item.on { background:var(--sp-primary-soft); color:var(--sp-primary); font-weight:600; }
        .sp-mpv-item.on .mi-i { color:var(--sp-primary); }
        .sp-mpv-item.sub { margin-left:16px; font-size:12px; color:var(--sp-muted); padding:5px 10px; }
        .sp-mpv-caret { font-size:10px; opacity:.6; }
        .sp-mpv-empty { padding:20px 12px; text-align:center; font-size:12px; color:var(--sp-muted); }
        .sp-menued-pv-hint { padding:12px 16px; font-size:11px; color:var(--sp-muted); line-height:1.5; }

        .sp-menued-foot { display:flex; align-items:center; gap:12px; padding:12px 16px; border-top:1px solid var(--sp-border); background:var(--sp-surface); flex:0 0 auto; }
        .sp-menued-foot-info { font-size:11.5px; color:var(--sp-muted); font-weight:600; }
        .sp-menued-foot-info i { color:var(--sp-primary); margin-right:4px; }
        /* de-emphasised, small — deliberately hard to hit by accident */
        .sp-menued-reset { display:inline-flex; align-items:center; gap:4px; border:1px solid transparent; background:transparent; color:var(--sp-muted); font-size:10.5px; font-weight:600; padding:3px 7px; border-radius:6px; cursor:pointer; opacity:.7; }
        .sp-menued-reset:hover { opacity:1; color:#e11d48; border-color:color-mix(in srgb,#e11d48 42%,transparent); background:color-mix(in srgb,#e11d48 12%,transparent); }
        .sp-menued-reset i { font-size:11px; }
        .sp-menued-undoredo { display:inline-flex; gap:6px; }
        .sp-menued-undoredo .sp-iconbtn { width:32px; height:32px; flex-basis:32px; }

        /* ---- SweetAlert: stack above the Menu Editor + icon modals ---- */
        .swal2-container.sp-swal-top { z-index:9000; }

        /* ---- dividers: shared rule appearance (tree · picker · preview) ---- */
        .sp-div-rule { display:block; width:100%; height:0; }
        .sp-div-line { border-top:1.5px solid var(--sp-border); }
        .sp-div-dashed { border-top:1.5px dashed var(--sp-border); }
        .sp-div-dotted { border-top:2px dotted var(--sp-border); }
        .sp-div-gradient { height:2px; border:0; border-radius:2px; background:linear-gradient(90deg,transparent,var(--sp-border) 22%,var(--sp-border) 78%,transparent); }
        /* divider row in the tree */
        .sp-menued .sp-mi-divider { background:transparent; border-style:dashed; }
        .sp-menued .sp-mi-divider.is-selected { border-style:solid; }
        .sp-mi-divider .mi-div-tag { width:22px; flex:0 0 22px; display:flex; align-items:center; justify-content:center; color:var(--sp-muted); font-size:13px; }
        .sp-mi-divider .mi-div-preview { flex:1; min-width:0; display:flex; align-items:center; }
        .sp-mi-divider .mi-div-heading { font-size:10.5px; font-weight:800; letter-spacing:.07em; text-transform:uppercase; color:var(--sp-muted); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
        /* divider style picker in the detail pane */
        .sp-div-picker { display:grid; grid-template-columns:repeat(auto-fill,minmax(88px,1fr)); gap:8px; padding:4px 16px 8px; }
        .sp-div-opt { display:flex; flex-direction:column; align-items:center; gap:7px; padding:11px 6px 8px; border:1px solid var(--sp-border); border-radius:10px; background:var(--sp-raised); cursor:pointer; transition:border-color .12s, background .12s; }
        .sp-div-opt:hover { border-color:var(--sp-primary); background:var(--sp-primary-soft); }
        .sp-div-opt.is-active { border-color:var(--sp-primary); box-shadow:0 0 0 2px var(--sp-primary-soft); }
        .sp-div-opt-prev { width:100%; height:22px; display:flex; align-items:center; justify-content:center; padding:0 4px; }
        .sp-div-opt-heading { font-size:9px; font-weight:800; letter-spacing:.06em; text-transform:uppercase; color:var(--sp-muted); }
        .sp-div-opt-lbl { font-size:11px; font-weight:600; color:var(--sp-text); }
        /* dividers in the live mini preview */
        .sp-mpv-divider { padding:var(--mpv-divgap,6px) 10px; }
        .sp-mpv-divlabel { font-size:9.5px; font-weight:800; letter-spacing:.07em; text-transform:uppercase; color:var(--sp-muted); padding:calc(var(--mpv-divgap,6px) + 3px) 10px 4px; }
        /* menu spacing dropdown panel */
        .sp-menued-spacepanel { position:absolute; top:62px; left:12px; width:252px; background:var(--sp-surface); border:1px solid var(--sp-border); border-radius:12px; box-shadow:0 20px 55px var(--sp-shadow); padding:12px 14px; z-index:30; }
        .sp-menued-spacepanel[hidden] { display:none; }
        .sp-space-head { display:flex; align-items:center; justify-content:space-between; margin-bottom:6px; }
        .sp-space-head strong { font-size:13px; } .sp-space-head strong i { color:var(--sp-primary); margin-right:4px; }
        .sp-space-reset { border:0; background:transparent; color:var(--sp-primary); font-size:11px; font-weight:700; cursor:pointer; padding:2px 4px; border-radius:5px; }
        .sp-space-reset:hover { background:var(--sp-primary-soft); }
        .sp-space-row { margin:10px 0; }
        .sp-space-row label { display:flex; align-items:center; justify-content:space-between; font-size:12px; font-weight:600; color:var(--sp-text); margin-bottom:5px; }
        .sp-space-val { color:var(--sp-muted); font-weight:600; }
        .sp-space-row input[type=range] { width:100%; accent-color:var(--sp-primary); cursor:pointer; }
        .sp-space-hint { font-size:10.5px; color:var(--sp-muted); line-height:1.5; margin-top:8px; }

        @media (max-width:1080px) {
            .sp-menued-body { grid-template-columns:320px 1fr; }
            .sp-menued-previewpane { display:none; }
        }
        @media (max-width:760px) {
            .sp-menued { height:94vh; width:98vw; }
            .sp-menued-body { grid-template-columns:1fr; grid-auto-rows:minmax(0,auto); overflow:auto; }
            .sp-menued-pane { overflow:visible; }
            .sp-menued-treepane { border-right:0; border-bottom:1px solid var(--sp-border); }
            .sp-menued-treewrap { max-height:240px; }
            .sp-menued-detailpane { border-right:0; }
            .sp-menued-head-tools .sp-menued-asrole span { display:none; }
        }
    </style>
    <!-- Editor-only navbar-theme bridge. The live preview keeps its compiled project theme. -->
    <?= \PpPhp\Core\ModuleAssets::style('Style', 'Views/style-app-theme.css', '', 'spAppThemeCss') ?>
</head>
<body class="sp-body">
    <?php include constant('BASE_PATH') . '/core/views/project-navbar.php'; ?>

    <div class="sp-workspace">
        <aside class="sp-list">
            <div class="sp-list-head">
                <div class="sp-head-title"><i class="bi bi-palette2"></i><span>Themes</span></div>
                <div class="sp-head-actions">
                    <a class="sp-guidebtn" href="<?= htmlspecialchars($baseUrl . '/project/' . rawurlencode($project['slug']) . '/style/guide') ?>"
                       data-sp-tip="Style module guide — tokens, layouts, and how it all reaches the built app.">
                        <i class="bi bi-book"></i></a>
                    <button type="button" class="sp-newbtn" id="spNew" data-sp-tip="Create a new custom theme"><i class="bi bi-plus-lg"></i><span>New</span></button>
                </div>
            </div>
            <div id="spThemeList"></div>
        </aside>
        <div class="sp-resizer" data-resize="list"></div>

        <div class="sp-main">
            <section class="sp-editor">
                <div class="sp-ed-head">
                    <div class="sp-ed-title">
                        <input type="text" class="sp-name" id="spName" placeholder="Theme name">
                        <div class="sp-sub" id="spSub"></div>
                    </div>
                    <div class="sp-maintabs" id="spMainTabs">
                        <button type="button" data-tab="theme" data-sp-tip="Theme — the colours, radius and fonts you author." class="active"><i class="bi bi-palette"></i><span>Theme</span></button>
                        <button type="button" data-tab="layout" data-sp-tip="Layout — navbar type, topbar, sidebar, logo, menu and footer."><i class="bi bi-layout-sidebar"></i><span>Layout</span></button>
                        <button type="button" data-tab="grid" data-sp-tip="Grid — data-table surfaces, header and row striping."><i class="bi bi-table"></i><span>Grid</span></button>
                        <button type="button" data-tab="menu" data-sp-tip="Menu — the navigation tree and per-item role visibility."><i class="bi bi-list-nested"></i><span>Menu</span></button>
                        <button type="button" data-tab="css" data-sp-tip="CSS — free-form custom CSS, appended after the tokens."><i class="bi bi-code-slash"></i><span>CSS</span></button>
                        <button type="button" data-tab="palette" data-sp-tip="Palette — every compiled token, what it is for, and its value."><i class="bi bi-eyedropper"></i><span>Palette</span></button>
                    </div>
                </div>
                <div class="sp-ed-body">
                    <div class="sp-tab" data-tab="theme">
                        <div class="sp-tabs sp-modetabs" id="spModeTabs">
                            <button type="button" data-mode="light" class="active"><i class="bi bi-sun"></i> Light</button>
                            <button type="button" data-mode="dark"><i class="bi bi-moon-stars"></i> Dark</button>
                        </div>
                        <div id="spTokens"></div>
                    </div>
                    <div class="sp-tab" data-tab="layout" hidden><div id="spLayout"></div></div>
                    <div class="sp-tab" data-tab="grid" hidden><div id="spGrid"></div></div>
                    <div class="sp-tab" data-tab="menu" hidden><div id="spMenu"></div></div>
                    <div class="sp-tab" data-tab="css" hidden><div id="spCss"></div></div>
                    <div class="sp-tab" data-tab="palette" hidden><div id="spPalette"></div></div>
                </div>
                <div class="sp-actions">
                    <button type="button" class="sp-btn primary" id="spSave"><i class="bi bi-check-lg"></i> Save</button>
                    <button type="button" class="sp-iconbtn" id="spDup" data-tip="Duplicate" aria-label="Duplicate"><i class="bi bi-files"></i></button>
                    <button type="button" class="sp-iconbtn" id="spDefault" data-tip="Set as default" aria-label="Set as default"><i class="bi bi-star"></i></button>
                    <button type="button" class="sp-iconbtn" id="spExport" data-tip="Export theme JSON" aria-label="Export theme JSON"><i class="bi bi-box-arrow-up"></i></button>
                    <button type="button" class="sp-iconbtn" id="spImport" data-tip="Import theme JSON" aria-label="Import theme JSON"><i class="bi bi-box-arrow-in-down"></i></button>
                    <button type="button" class="sp-iconbtn danger" id="spDelete" data-tip="Delete" aria-label="Delete"><i class="bi bi-trash"></i></button>
                    <input type="file" id="spImportFile" accept="application/json,.json" hidden>
                </div>
            </section>
            <div class="sp-resizer" data-resize="editor"></div>

            <section class="sp-preview-wrap">
                <div class="sp-preview-bar">
                    <div class="sp-pv-left">
                        <div class="sp-seg" id="spSourceSeg">
                            <button type="button" data-src="sample" class="active">Sample</button>
                            <button type="button" data-src="real">Real page</button>
                        </div>
                        <select id="spTable" class="sp-mini" title="Table"></select>
                        <select id="spPage" class="sp-mini" title="Page type"></select>
                    </div>
                    <div class="sp-pv-right">
                        <div class="sp-seg" id="spDeviceSeg">
                            <button type="button" data-dev="desktop" class="active" title="Desktop"><i class="bi bi-display"></i></button>
                            <button type="button" data-dev="tablet" title="Tablet"><i class="bi bi-tablet"></i></button>
                            <button type="button" data-dev="mobile" title="Mobile"><i class="bi bi-phone"></i></button>
                        </div>
                        <span class="sp-pv-mode" id="spPvMode">Light mode</span>
                    </div>
                </div>
                <div class="sp-preview-scroll">
                    <div class="sp-pvframe" id="spPreview" data-src="sample"></div>
                </div>
            </section>
        </div>
    </div>

    <script>
        const baseUrl     = <?= json_encode($baseUrl ?? '') ?>;
        const projectSlug = <?= json_encode($project['slug'] ?? '') ?>;
    </script>
    <?php include constant('BASE_PATH') . '/core/views/project-scripts.php'; ?>
    <script>
        window.PpStyleBootstrap = {
            baseUrl: <?= json_encode($baseUrl ?? '') ?>,
            slug: <?= json_encode($project['slug'] ?? '') ?>,
            themes: <?= json_encode($themes ?? []) ?>,
            default: <?= json_encode($default ?? 'default') ?>,
            tokenKeys: <?= json_encode($tokenKeys ?? []) ?>,
            shellPalettes: <?= json_encode($shellPalettes ?? []) ?>,
            navbarTypes: <?= json_encode($navbarTypes ?? []) ?>,
            menu: <?= json_encode($menu ?? ['items' => []]) ?>,
            roleVocab: <?= json_encode($roleVocab ?? ['groups' => [], 'roles' => []]) ?>,
            projectShell: <?= json_encode($projectShell ?? null) ?>,
            designerPages: <?= json_encode($designerPages ?? new stdClass()) ?>,
            designerTemplates: <?= json_encode($designerTemplates ?? new stdClass()) ?>,
            designerCommon: <?= json_encode($designerCommon ?? new stdClass()) ?>
        };
    </script>
    <!-- designer component registry, reused to render real pages in the live preview -->
    <?= \PpPhp\Core\ModuleAssets::script('Designer', 'Views/app/registry.js') ?>
    <?= \PpPhp\Core\ModuleAssets::script('Style', 'Views/style-spa.js') ?>
</body>
</html>
