<?php
declare(strict_types=1);
// Variables: $baseUrl, $assetsUrl, $project, $user, $navItems, $pageTitle
// This module uses its own bundled CodeMirror editor, not the GrapesJS visual
// builder — so skip loading GrapesJS (~1.16 MB) for a much faster page load.
$skipGrapes = true;
?>
<!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') ?> — Custom Files</title>
    <?php include constant('BASE_PATH') . '/core/views/project-head.php'; ?>
    <style>
        :root {
            --cf-border:#e2e8f0; --cf-muted:#64748b; --cf-bg:#f6f9fd; --cf-surface:#fff;
            --cf-primary:#2563eb; --cf-psoft:#eff6ff; --cf-tree-w:305px;
        }
        html,body { height:100%; margin:0; }
        body.cf-body { overflow:hidden; background:var(--cf-bg); color:#0f172a;
            font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif; }
        .cf-workspace { display:flex; position:fixed; top:56px; left:0; right:0; bottom:0; }

        /* left: file tree — fixed width, toggleable */
        .cf-tree { width:var(--cf-tree-w); min-width:var(--cf-tree-w); max-width:var(--cf-tree-w);
            flex:0 0 var(--cf-tree-w); border-right:1px solid var(--cf-border);
            background:var(--cf-surface); display:flex; flex-direction:column; overflow:hidden; }
        .cf-workspace.tree-hidden .cf-tree { display:none; }
        .cf-tree-head { padding:10px 12px; border-bottom:1px solid var(--cf-border); flex:0 0 auto; }
        .cf-tree-head strong { font-size:13.5px; display:block; }
        .cf-tree-head small { font-size:11px; color:var(--cf-muted); }
        .cf-toolbar { display:flex; gap:4px; padding:8px 10px; border-bottom:1px solid var(--cf-border);
            flex-wrap:wrap; flex:0 0 auto; }
        .cf-tbtn { display:inline-flex; align-items:center; gap:5px; font-size:11.5px; font-weight:600;
            color:#334155; border:1px solid var(--cf-border); border-radius:6px; background:#fff;
            padding:4px 8px; cursor:pointer; }
        .cf-tbtn:hover { border-color:#bfdbfe; background:var(--cf-psoft); color:var(--cf-primary); }
        .cf-tbtn i { font-size:13px; }
        .cf-tree-body { flex:1; overflow:auto; padding:6px 0 30px; }

        .cf-node { display:flex; align-items:center; gap:6px; padding:4px 10px; cursor:pointer;
            font-size:12.5px; border-left:2px solid transparent; user-select:none; white-space:nowrap; }
        .cf-node:hover { background:#f8fafc; }
        .cf-node.active { background:var(--cf-psoft); border-left-color:var(--cf-primary); color:var(--cf-primary); }
        .cf-node .cf-caret { width:12px; flex:0 0 12px; color:#94a3b8; font-size:10px; text-align:center; }
        .cf-node .cf-ic { width:16px; flex:0 0 16px; text-align:center; font-size:13px; }
        .cf-node .cf-nm { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; }
        .cf-node .cf-badge { font-size:9px; font-weight:700; color:#b45309; background:#fef3c7;
            border-radius:4px; padding:0 4px; }
        .cf-node .cf-acts { display:none; gap:2px; }
        .cf-node:hover .cf-acts { display:flex; }
        .cf-actbtn { border:0; background:transparent; color:#94a3b8; cursor:pointer; font-size:12px; padding:1px 3px; border-radius:4px; }
        .cf-actbtn:hover { color:#dc2626; background:#fef2f2; }
        .cf-actbtn.rn:hover { color:var(--cf-primary); background:var(--cf-psoft); }
        .cf-empty-tree { padding:24px 14px; text-align:center; color:#94a3b8; font-size:12px; }
        .cf-node.cf-drop { background:#dbeafe; box-shadow:inset 0 0 0 1px var(--cf-primary); }
        .cf-node.dragging { opacity:.5; }

        /* tree filter/search box */
        .cf-search { display:flex; align-items:center; gap:6px; padding:6px 10px; border-bottom:1px solid var(--cf-border); flex:0 0 auto; }
        .cf-search i { color:var(--cf-muted); font-size:12px; }
        .cf-search input { flex:1; min-width:0; border:0; outline:0; background:transparent; font-size:12px; color:var(--cf-text); }
        .cf-search input::placeholder { color:var(--cf-muted); }
        .cf-tree-body.is-rootdrop { box-shadow:inset 0 0 0 2px var(--cf-primary); }

        /* current-folder location bar (where new items land) — theme-driven */
        .cf-loc { display:flex; align-items:center; flex-wrap:wrap; gap:1px; padding:5px 9px;
            border-bottom:1px solid var(--cf-border); flex:0 0 auto; background:var(--cf-surface); }
        .cf-loc-seg { border:0; background:transparent; color:var(--cf-muted); cursor:pointer; font-size:11px;
            padding:2px 5px; border-radius:5px; display:inline-flex; align-items:center; gap:4px;
            max-width:130px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
        .cf-loc-seg:hover { background:var(--cf-psoft); color:var(--cf-primary); }
        .cf-loc-seg.here { color:var(--cf-text); font-weight:700; cursor:default; }
        .cf-loc-seg.here:hover { background:transparent; }
        .cf-loc-seg i { font-size:12px; }
        .cf-loc-sep { color:var(--cf-muted); opacity:.5; font-size:9px; }

        /* selected-folder highlight (distinct from the open-file .active) */
        .cf-node.curdir { background:var(--cf-hover); }
        .cf-node.curdir .cf-nm { font-weight:600; color:var(--cf-text); }

        /* inline create / rename input (VS Code-style, in-tree) — theme-driven */
        .cf-node.cf-editing, .cf-node.cf-editing:hover { cursor:default; background:var(--cf-surface); }
        .cf-edit-input { flex:1; min-width:0; border:1px solid var(--cf-primary); border-radius:4px;
            padding:1px 5px; margin:-1px 0; font:inherit; font-size:12.5px; line-height:18px; color:var(--cf-text);
            outline:0; box-shadow:0 0 0 2px var(--cf-psoft); background:var(--cf-input); }
        .cf-edit-input::placeholder { color:var(--cf-muted); }

        /* right: editor / preview */
        .cf-main { flex:1; min-width:0; display:flex; flex-direction:column; overflow:hidden; background:#1e1e2e; }
        .cf-head { display:flex; align-items:center; justify-content:space-between; gap:10px;
            padding:9px 14px; background:#181825; border-bottom:1px solid #313244; flex:0 0 auto; }
        .cf-crumb { font-size:12px; color:#cdd6f4; font-family:ui-monospace,Menlo,monospace; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
        .cf-crumb .cf-dim { color:#6c7086; }
        .cf-head-right { display:flex; align-items:center; gap:9px; flex:0 0 auto; }
        /* "Saved / Unsaved" status pill */
        .cf-status { font-size:10.5px; font-weight:700; letter-spacing:.03em; text-transform:uppercase;
            padding:4px 10px; border-radius:999px; line-height:1; }
        .cf-status.saved { color:#059669; background:color-mix(in srgb, #10b981 15%, transparent); }
        .cf-status.dirty { color:#d97706; background:color-mix(in srgb, #f59e0b 18%, transparent); }
        html[data-pp-theme="dark"] .cf-status.saved { color:#6ee7b7; }
        html[data-pp-theme="dark"] .cf-status.dirty { color:#fcd34d; }
        /* Check + Save action buttons — 34px tall, theme-driven (colours from the bridge) */
        .cf-savebtn, .cf-checkbtn { display:inline-flex; align-items:center; gap:6px; height:34px; padding:0 14px;
            font-size:12.5px; font-weight:700; border-radius:9px; cursor:pointer; transition:.15s;
            border:1px solid transparent; }
        .cf-savebtn i, .cf-checkbtn i { font-size:14px; }
        .cf-savebtn { color:#cba6f7; border-color:#4c1d95; background:#1e1b2e; }
        .cf-savebtn:hover { background:#2d2040; border-color:#7c3aed; color:#ddd6fe; }
        .cf-checkbtn { color:#94e2d5; border-color:#0e5c52; background:#132724; }
        .cf-checkbtn:hover { background:#173330; border-color:#0d9488; color:#a7f3d0; }
        .cf-savebtn:disabled, .cf-checkbtn:disabled { opacity:.45; cursor:default; }
        .cf-savebtn:hover:not(:disabled), .cf-checkbtn:hover:not(:disabled) { transform:translateY(-1px); }
        .cf-checkbtn.busy i { animation: cfspin 1s linear infinite; }
        @keyframes cfspin { to { transform: rotate(360deg); } }

        /* problems panel */
        .cf-problems { flex:0 0 auto; max-height:180px; display:none; flex-direction:column;
            background:#181825; border-top:1px solid #313244; overflow:hidden; }
        .cf-problems.open { display:flex; }
        .cf-problems-head { display:flex; align-items:center; gap:8px; padding:7px 14px;
            font-size:11px; font-weight:800; letter-spacing:.05em; text-transform:uppercase;
            color:#f38ba8; border-bottom:1px solid #313244; flex:0 0 auto; }
        .cf-problems-head.clean { color:#a6e3a1; }
        .cf-problems-head .cf-ptool { margin-left:auto; font-weight:600; text-transform:none;
            letter-spacing:0; color:#6c7086; font-family:ui-monospace,Menlo,monospace; font-size:10.5px; }
        .cf-problems-head .cf-pclose { border:0; background:transparent; color:#6c7086; cursor:pointer; font-size:13px; }
        .cf-problems-head .cf-pclose:hover { color:#cdd6f4; }
        .cf-problems-body { flex:1; overflow-y:auto; padding:4px 0 8px; }
        .cf-problem { display:flex; align-items:baseline; gap:9px; padding:4px 14px; cursor:pointer;
            font-size:12px; color:#cdd6f4; }
        .cf-problem:hover { background:#1e1e2e; }
        .cf-problem .cf-pline { flex:0 0 auto; min-width:38px; text-align:right;
            font-family:ui-monospace,Menlo,monospace; font-size:11px; color:#f9e2af; }
        .cf-problem .cf-psev { flex:0 0 auto; font-size:12px; }
        .cf-problem .cf-psev.error { color:#f38ba8; } .cf-problem .cf-psev.warning { color:#f9e2af; }
        .cf-problem .cf-pmsg { color:#a6adc8; }


        /* guide pill (tree head) */
        .cf-help-btn { flex:0 0 auto; display:inline-flex; align-items:center; gap:4px; cursor:pointer;
            font-size:11px; font-weight:700; color:#6d28d9; background:#ede9fe; border:1px solid #ddd6fe;
            border-radius:999px; padding:3px 9px; white-space:nowrap; transition:.15s; }
        .cf-help-btn:hover { background:#6d28d9; color:#fff; border-color:#6d28d9; }

        /* responsive: stack panels on narrow screens */
        @media (max-width: 900px) {
            .cf-workspace { flex-direction:column; }
            .cf-tree { width:100% !important; min-width:0 !important; max-width:none !important;
                height:44%; flex:0 0 44%; border-right:0; border-bottom:1px solid var(--cf-border); }
            .cf-workspace.tree-hidden .cf-tree { display:none; }
            .cf-main { flex:1; min-height:0; }
            .cf-info { display:none; }
        }

        .cf-editor-wrap { flex:1; min-height:0; display:flex; }

        .cf-blank, .cf-preview { flex:1; display:flex; flex-direction:column; align-items:center; justify-content:center;
            gap:12px; color:#6c7086; text-align:center; padding:40px; }
        .cf-blank i { font-size:46px; color:#45475a; }
        .cf-preview img { max-width:80%; max-height:70%; border-radius:8px; box-shadow:0 8px 30px rgba(0,0,0,.4); background:#fff; }
        .cf-preview .cf-pmeta { font-size:12px; color:#a6adc8; font-family:ui-monospace,Menlo,monospace; }

        /* ── media preview (image / video / audio / pdf) — served, any size ── */
        .cf-preview.fill { padding:0; gap:0; align-items:stretch; justify-content:stretch; }
        .cf-media { flex:1; width:100%; min-height:0; display:flex; flex-direction:column; }
        .cf-media-stage { flex:1; min-height:0; display:flex; align-items:center; justify-content:center; padding:18px;
            overflow:auto; background:repeating-conic-gradient(#e5e7eb 0 25%, #f8fafc 0 50%) 0 0 / 24px 24px; }
        html[data-pp-theme="dark"] .cf-media-stage { background:repeating-conic-gradient(#111827 0 25%, #0b1120 0 50%) 0 0 / 24px 24px; }
        /* !important: beat the bridge's generic `.cf-preview img` rule so transparent
           images show the checkerboard through them and can fill the stage. */
        .cf-media-img { max-width:100% !important; max-height:100% !important; object-fit:contain;
            border-radius:4px !important; background:transparent !important;
            box-shadow:0 6px 26px rgba(0,0,0,.28) !important; image-rendering:auto; }
        .cf-media-video { max-width:100%; max-height:100%; border-radius:6px; background:#000; box-shadow:0 6px 26px rgba(0,0,0,.4); }
        .cf-media-audio { display:flex; flex-direction:column; align-items:center; gap:14px; color:#94a3b8; }
        .cf-media-audio i { font-size:46px; color:#94a3b8; }
        .cf-media-audio audio { width:min(460px, 80vw); }
        .cf-media-pdf { flex:1; width:100%; border:0; background:#fff; }
        .cf-media-meta { flex:0 0 auto; padding:7px 14px; font-size:11.5px; text-align:center; white-space:nowrap;
            overflow:hidden; text-overflow:ellipsis; font-family:ui-monospace,Menlo,Consolas,monospace;
            color:var(--cf-code-muted,#a6adc8); background:var(--cf-code-raised,#181825);
            border-top:1px solid var(--cf-code-border,#313244); }

        /* toast */
        .cf-toast { position:fixed; bottom:20px; right:20px; background:#45475a; color:#cdd6f4;
            border-radius:10px; padding:10px 16px; font-size:12.5px; z-index:9999; box-shadow:0 8px 24px rgba(0,0,0,.4);
            transform:translateY(80px); opacity:0; transition:.2s; pointer-events:none; }
        .cf-toast.show { transform:none; opacity:1; }
        .cf-toast.ok { background:#1e3a2f; color:#a6e3a1; } .cf-toast.err { background:#3d1a1a; color:#f38ba8; }

        /* info banner — compact single line */
        .cf-info { display:flex; gap:6px; align-items:center; padding:2px 14px; min-height:22px;
            background:#eff6ff; border-bottom:1px solid #dbeafe; color:#1e40af; font-size:10.5px;
            line-height:1.3; overflow:hidden; white-space:nowrap; text-overflow:ellipsis; }
        .cf-info i { color:#2563eb; font-size:11px; flex:0 0 auto; }
        .cf-info span { overflow:hidden; text-overflow:ellipsis; }
        .cf-info code { background:#dbeafe; padding:0 4px; border-radius:3px; font-size:10px; }

        /* ── Phase 1/2: tabs, syntax-highlight overlay, find bar, context menu ── */
        /* open-file tabs */
        /* open-file tabs — theme-driven (VS Code-style raised bar) */
        .cf-tabs { display:none; align-items:stretch; gap:0; background:var(--cf-code-raised,#181825);
            border-bottom:1px solid var(--cf-code-border,#313244); overflow-x:auto; flex:0 0 auto; scrollbar-width:thin; }
        .cf-tabs.show { display:flex; }
        .cf-tab { display:inline-flex; align-items:center; gap:7px; padding:7px 10px 7px 12px; cursor:pointer;
            font-size:12px; color:var(--cf-code-muted,#a6adc8); border-right:1px solid var(--cf-code-border,#313244);
            border-top:2px solid transparent; white-space:nowrap; max-width:220px; background:transparent; transition:background .12s; }
        .cf-tab:hover { background:var(--cf-code-bg,#1e1e2e); }
        .cf-tab.active { background:var(--cf-code-bg,#1e1e2e); color:var(--cf-code-text,#f5e0dc); border-top-color:var(--cf-code-accent,#cba6f7); }
        .cf-tab i.cf-tab-ic { font-size:13px; flex:0 0 auto; }
        .cf-tab .cf-tab-nm { overflow:hidden; text-overflow:ellipsis; }
        .cf-tab .cf-tab-dot { width:7px; height:7px; border-radius:50%; background:#f59e0b; flex:0 0 auto; display:none; }
        .cf-tab.dirty .cf-tab-dot { display:block; }
        .cf-tab .cf-tab-x { border:0; background:transparent; color:var(--cf-code-subtle,#6c7086); font-size:13px; cursor:pointer;
            border-radius:4px; width:17px; height:17px; display:flex; align-items:center; justify-content:center; flex:0 0 auto; }
        .cf-tab.dirty .cf-tab-x { display:none; }
        .cf-tab:hover .cf-tab-x { display:flex; }
        .cf-tab:hover .cf-tab-dot { display:none; }
        .cf-tab .cf-tab-x:hover { background:var(--cf-code-soft,#45475a); color:var(--cf-code-text,#f5e0dc); }

        /* editor host (CodeMirror mounts into .cf-code-area) */
        .cf-code { flex:1; min-height:0; display:flex; }

        /* find & replace bar */
        .cf-find { position:absolute; top:8px; right:16px; z-index:6; display:none;
            background:#232338; border:1px solid #45475a; border-radius:10px; padding:8px;
            box-shadow:0 12px 30px rgba(0,0,0,.5); width:340px; }
        .cf-find.open { display:block; }
        .cf-find-row { display:flex; align-items:center; gap:6px; margin:2px 0; }
        .cf-find input { flex:1; background:#1e1e2e; border:1px solid #313244; border-radius:6px;
            color:#cdd6f4; font:12px ui-monospace,Menlo,monospace; padding:5px 8px; outline:0; }
        .cf-find input:focus { border-color:#cba6f7; }
        .cf-find-btn { border:1px solid #45475a; background:#313244; color:#cdd6f4; border-radius:6px;
            font-size:11px; font-weight:700; padding:5px 8px; cursor:pointer; white-space:nowrap; }
        .cf-find-btn:hover { background:#45475a; }
        .cf-find-count { font-size:11px; color:#6c7086; min-width:56px; text-align:right;
            font-family:ui-monospace,Menlo,monospace; }
        .cf-find-x { margin-left:auto; }

        /* right-click context menu */
        .cf-ctx { position:fixed; z-index:10060; min-width:180px; background:var(--cf-surface); border:1px solid var(--cf-border);
            border-radius:10px; box-shadow:0 16px 44px rgba(2,6,23,.22); padding:5px; display:none;
            font-size:12.5px; color:var(--cf-text); }
        .cf-ctx.open { display:block; }
        .cf-ctx button { width:100%; display:flex; align-items:center; gap:9px; border:0; background:transparent;
            padding:7px 10px; border-radius:7px; cursor:pointer; color:inherit; font-size:12.5px; text-align:left; }
        .cf-ctx button:hover { background:var(--cf-psoft); color:var(--cf-primary); }
        .cf-ctx button.danger:hover { background:color-mix(in srgb, #dc2626 14%, var(--cf-surface)); color:#ef4444; }
        .cf-ctx button i { width:16px; flex:0 0 16px; font-size:13px; }
        .cf-ctx hr { border:0; border-top:1px solid var(--cf-border); margin:4px 2px; }

        /* dark SweetAlert2 to match the editor */
        .cf-swal-popup { background:#1e1e2e; color:#cdd6f4; border:1px solid #313244; border-radius:14px;
            box-shadow:0 24px 60px rgba(0,0,0,.55); }
        .cf-swal-popup .swal2-title { color:#f5e0dc; font-size:18px; }
        .cf-swal-popup .swal2-html-container { color:#a6adc8; font-size:13.5px; }
        .cf-swal-popup .swal2-input { background:#181825; border:1px solid #45475a; color:#cdd6f4;
            border-radius:8px; font-family:ui-monospace,Menlo,monospace; }
        .cf-swal-popup .swal2-input:focus { border-color:#cba6f7; box-shadow:none; }
        .cf-swal-btn { border:0; border-radius:8px; padding:9px 18px; font-size:13px; font-weight:700; cursor:pointer; }
        .cf-swal-confirm { background:#cba6f7; color:#1e1e2e; }
        .cf-swal-confirm.is-danger { background:#dc2626; color:#fff; }
        .cf-swal-cancel { background:#313244; color:#cdd6f4; }
        .cf-swal-btn:hover { filter:brightness(1.08); }

        /* ── Phase 3: live preview ── */
        .cf-viewseg { display:inline-flex; height:34px; border:1px solid var(--cf-code-border,#313244); border-radius:9px; overflow:hidden; }
        .cf-viewseg button { border:0; background:var(--cf-code-raised,#1e1e2e); color:var(--cf-code-muted,#7f849c);
            padding:0 13px; cursor:pointer; font-size:16px; line-height:1; transition:.15s;
            border-right:1px solid var(--cf-code-border,#313244); display:flex; align-items:center; }
        .cf-viewseg button:last-child { border-right:0; }
        .cf-viewseg button:disabled { opacity:.4; cursor:default; }
        .cf-viewseg button:disabled:hover { color:var(--cf-code-muted,#7f849c); background:var(--cf-code-raised,#1e1e2e); }
        .cf-viewseg button:not(:disabled):hover { color:var(--cf-code-text,#cdd6f4); background:color-mix(in srgb, var(--cf-code-accent,#cba6f7) 12%, var(--cf-code-raised,#1e1e2e)); }
        .cf-viewseg button.active { background:color-mix(in srgb, var(--cf-code-accent,#cba6f7) 22%, var(--cf-code-raised,#1e1e2e)); color:var(--cf-code-accent,#cba6f7); }
        .cf-iconbtn { display:inline-flex; align-items:center; justify-content:center; width:34px; height:34px; padding:0;
            border:1px solid var(--cf-code-border,#313244); background:var(--cf-code-raised,#1e1e2e);
            color:var(--cf-code-muted,#94a3b8); border-radius:9px; font-size:15px; cursor:pointer; transition:.15s; }
        .cf-iconbtn:hover { color:var(--cf-code-accent,#cdd6f4); border-color:var(--cf-code-accent,#45475a);
            background:color-mix(in srgb, var(--cf-code-accent,#60a5fa) 12%, var(--cf-code-raised,#1e1e2e)); transform:translateY(-1px); }
        .cf-live { flex:1; min-width:0; display:flex; flex-direction:column; background:#fff; border-left:1px solid var(--cf-code-border,#313244); position:relative; }
        .cf-live iframe { flex:1; border:0; width:100%; height:100%; background:#fff; }
        .cf-live-flag { position:absolute; top:8px; left:50%; transform:translateX(-50%); z-index:4;
            display:inline-flex; align-items:center; gap:6px; font-size:11px; font-weight:600;
            color:#fff; background:rgba(71,85,105,.94); border:1px solid rgba(148,163,184,.5);
            border-radius:999px; padding:3px 12px; box-shadow:0 4px 14px rgba(0,0,0,.3); cursor:pointer; }
        .cf-live-flag:hover { filter:brightness(1.1); }
        .cf-live-flag.ok   { background:rgba(21,94,64,.94); border-color:rgba(52,211,153,.5); }
        .cf-live-flag.err  { background:rgba(153,27,27,.94); border-color:rgba(248,113,113,.55); }
        .cf-live-flag.warn { background:rgba(180,83,9,.94); border-color:rgba(245,158,11,.55); }
        /* responsive preview device sizes */
        .cf-live.size-md, .cf-live.size-sm { background:var(--cf-code-bg,#0b0f1a); align-items:center; justify-content:flex-start; overflow:auto; padding:16px 0; }
        .cf-live.size-md iframe, .cf-live.size-sm iframe { flex:0 0 auto; height:100%; border-radius:10px;
            box-shadow:0 10px 40px rgba(0,0,0,.4); border:1px solid var(--cf-code-border,#313244); background:#fff; }
        .cf-live.size-md iframe { width:768px; max-width:calc(100% - 32px); }
        .cf-live.size-sm iframe { width:390px; max-width:calc(100% - 32px); }

        /* basic fallback editor (only used if CodeMirror can't load) */
        .cf-fallback-editor { position:absolute; inset:0; width:100%; height:100%; border:0; outline:0; resize:none;
            padding:14px; box-sizing:border-box; background:var(--cf-code-bg); color:var(--cf-code-text);
            font:13px/1.55 ui-monospace,Menlo,Consolas,monospace; tab-size:4; }
        .cf-live-note { flex:1; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:12px;
            color:#94a3b8; text-align:center; padding:40px; background:#181825; }
        .cf-live-note i { font-size:44px; color:#45475a; }
        .cf-live-note a { color:#cba6f7; font-weight:700; text-decoration:none; }
        /* markdown preview */
        .cf-md { flex:1; overflow:auto; padding:26px 34px; background:#fff; color:#1e293b;
            font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif; line-height:1.65; }
        .cf-md h1 { font-size:26px; margin:.2em 0 .5em; border-bottom:1px solid #e2e8f0; padding-bottom:.3em; }
        .cf-md h2 { font-size:21px; margin:1em 0 .4em; border-bottom:1px solid #eef2f7; padding-bottom:.2em; }
        .cf-md h3 { font-size:17px; margin:1em 0 .3em; }
        .cf-md p, .cf-md ul, .cf-md ol { margin:.6em 0; }
        .cf-md code { background:#f1f5f9; color:#7c3aed; padding:1px 5px; border-radius:4px; font-size:.9em;
            font-family:ui-monospace,Menlo,monospace; }
        .cf-md pre { background:#1e1e2e; color:#cdd6f4; padding:14px 16px; border-radius:9px; overflow:auto; }
        .cf-md pre code { background:none; color:inherit; padding:0; }
        .cf-md a { color:#2563eb; } .cf-md blockquote { border-left:3px solid #cbd5e1; margin:.6em 0; padding:.2em 14px; color:#64748b; }
        .cf-md img { max-width:100%; } .cf-md table { border-collapse:collapse; } .cf-md td, .cf-md th { border:1px solid #e2e8f0; padding:5px 9px; }
        .cf-md hr { border:0; border-top:1px solid #e2e8f0; }

        /* ── sidebar show/hide toggle (in editor head) ── */
        .cf-head-left { display:flex; align-items:center; gap:8px; min-width:0; flex:1; }
        .cf-toggle { flex:0 0 auto; width:34px; height:34px; padding:0; border:1px solid var(--cf-code-border);
            background:var(--cf-code-raised); color:var(--cf-code-muted); border-radius:9px; cursor:pointer;
            font-size:15px; line-height:1; display:inline-flex; align-items:center; justify-content:center; transition:.15s; }
        .cf-toggle:hover { color:var(--cf-code-accent); border-color:var(--cf-code-accent);
            background:color-mix(in srgb, var(--cf-code-accent) 12%, var(--cf-code-raised)); }

        /* ── custom tooltips (theme-adopting; replaces the native title chip) ── */
        body.cf-body .cf-tip { position:fixed; z-index:10090; pointer-events:none; max-width:280px;
            background:var(--cf-text,#0f172a); color:var(--cf-surface,#fff);
            font-size:11.5px; font-weight:600; line-height:1.4; padding:5px 10px; border-radius:7px;
            box-shadow:0 8px 24px rgba(2,6,23,.3); white-space:normal;
            opacity:0; transform:translateY(3px); transition:opacity .12s ease, transform .12s ease; }
        body.cf-body .cf-tip.show { opacity:1; transform:translateY(0); }
        body.cf-body .cf-tip::after { content:''; position:absolute; width:8px; height:8px; background:inherit;
            transform:rotate(45deg); left:calc(var(--tip-ax, 50%) - 4px); }
        body.cf-body .cf-tip.below::after { top:-4px; }
        body.cf-body .cf-tip.above::after { bottom:-4px; }

        /* ── CodeMirror editor (theme-driven via --cf-code-* and --cf-tok-*) ── */
        :root {
            --cf-tok-com:#6a737d; --cf-tok-key:#7c3aed; --cf-tok-str:#0a7d33; --cf-tok-num:#b45309;
            --cf-tok-fn:#0b6bcb; --cf-tok-var:#953800; --cf-tok-tag:#116329; --cf-tok-atr:#0b6bcb;
            --cf-tok-prop:#0b6bcb; --cf-tok-punc:#57606a; --cf-tok-atom:#0b6bcb;
        }
        .cf-code-area { flex:1; min-width:0; position:relative; overflow:hidden; }
        .cf-code-area .CodeMirror { position:absolute; inset:0; height:100%; width:100%;
            font:13px/1.55 ui-monospace,Menlo,Consolas,monospace; background:var(--cf-code-bg); color:var(--cf-code-text); }
        .cf-code-area .CodeMirror-gutters { background:var(--cf-code-bg); border-right:1px solid var(--cf-code-border); }
        .cf-code-area .CodeMirror-linenumber { color:var(--cf-code-subtle); padding:0 6px 0 8px; }
        .cf-code-area .CodeMirror-cursor { border-left:1.6px solid var(--cf-code-accent); }
        .cf-code-area .CodeMirror-selected { background:var(--cf-code-selection); }
        .cf-code-area .CodeMirror-focused .CodeMirror-selected { background:var(--cf-code-selection); }
        .cf-code-area .CodeMirror-activeline-background { background:color-mix(in srgb, var(--cf-code-accent) 7%, transparent); }
        .cf-code-area .CodeMirror-matchingbracket { color:var(--cf-code-accent) !important; font-weight:700; border-bottom:1px solid var(--cf-code-accent); }
        .cf-code-area .cm-comment { color:var(--cf-tok-com); font-style:italic; }
        .cf-code-area .cm-keyword { color:var(--cf-tok-key); }
        .cf-code-area .cm-string, .cf-code-area .cm-string-2 { color:var(--cf-tok-str); }
        .cf-code-area .cm-number { color:var(--cf-tok-num); }
        .cf-code-area .cm-def, .cf-code-area .cm-variable-2 { color:var(--cf-tok-fn); }
        .cf-code-area .cm-variable { color:var(--cf-code-text); }
        .cf-code-area .cm-variable-3, .cf-code-area .cm-type { color:var(--cf-tok-var); }
        .cf-code-area .cm-tag { color:var(--cf-tok-tag); }
        .cf-code-area .cm-bracket { color:var(--cf-tok-punc); }
        .cf-code-area .cm-attribute { color:var(--cf-tok-atr); }
        .cf-code-area .cm-property { color:var(--cf-tok-prop); }
        .cf-code-area .cm-atom, .cf-code-area .cm-builtin { color:var(--cf-tok-atom); }
        .cf-code-area .cm-operator { color:var(--cf-code-text); }
        .cf-code-area .cm-meta { color:var(--cf-tok-key); }
        .cf-code-area .cm-qualifier { color:var(--cf-tok-tag); }
        .cf-code-area .CodeMirror-scrollbar-filler { background:transparent; }

        /* ── inline error linting (red gutter + underline + line tint) ── */
        .cf-code-area .CodeMirror-lint-markers { width:16px; }
        .cf-code-area .CodeMirror-lint-marker-error,
        .cf-code-area .CodeMirror-lint-marker-warning { cursor:pointer; }
        .cf-code-area .CodeMirror-lint-mark-error { text-decoration:none;
            border-bottom:2px wavy #ef4444; background:rgba(239,68,68,.10); }
        .cf-code-area .CodeMirror-lint-mark-warning { border-bottom:2px wavy #f59e0b; background:rgba(245,158,11,.10); }
        .cf-code-area .cf-error-line { background:rgba(239,68,68,.12) !important; }
        .cf-code-area .cf-warn-line { background:rgba(245,158,11,.10) !important; }
        .cf-code-area .CodeMirror-lint-marker-error { color:#ef4444; }
        /* lint tooltip (appended to body) */
        body.cf-body .CodeMirror-lint-tooltip { background:var(--cf-code-raised); border:1px solid var(--cf-code-border);
            color:var(--cf-code-text); border-radius:8px; box-shadow:0 12px 30px rgba(2,6,23,.35); font-size:12px;
            font-family:ui-monospace,Menlo,Consolas,monospace; padding:6px 10px; z-index:10075; }
        body.cf-body .CodeMirror-lint-message-error { padding-left:20px; background-position:0 center; }

        /* ── full screen (maximize the editor pane) ── */
        .cf-main.cf-fs { position:fixed; inset:0; z-index:9998; }
        .cf-main:fullscreen, .cf-main:-webkit-full-screen { background:var(--cf-code-bg); width:100%; height:100%; }

        /* IntelliSense (show-hint) dropdown — appended to <body>, theme via body.cf-body scope */
        body.cf-body .CodeMirror-hints { background:var(--cf-code-raised); border:1px solid var(--cf-code-border);
            box-shadow:0 14px 34px rgba(2,6,23,.32); border-radius:9px; padding:4px; z-index:10070;
            font-family:ui-monospace,Menlo,Consolas,monospace; font-size:12px; max-height:22em; }
        body.cf-body .CodeMirror-hint { color:var(--cf-code-text); padding:4px 12px 4px 10px; border-radius:6px; }
        body.cf-body li.CodeMirror-hint-active { background:var(--cf-code-accent); color:#fff; }

        /* ── compact status bar ── */
        .cf-statusbar { flex:0 0 auto; display:flex; align-items:center; gap:14px; height:24px;
            padding:0 12px; background:var(--cf-code-raised); border-top:1px solid var(--cf-code-border);
            color:var(--cf-code-muted); font-size:11px; font-family:ui-monospace,Menlo,Consolas,monospace;
            white-space:nowrap; overflow:hidden; }
        .cf-statusbar .cf-sb-sep { flex:1; }
        .cf-statusbar .cf-sb-item { display:inline-flex; align-items:center; gap:5px; }
        .cf-statusbar .cf-sb-item i { font-size:11px; opacity:.85; }
        .cf-statusbar .cf-sb-lang { color:var(--cf-code-accent); font-weight:700; }
        .cf-statusbar .cf-sb-path { color:var(--cf-code-subtle); overflow:hidden; text-overflow:ellipsis; max-width:40%; }
        .cf-statusbar button.cf-sb-item { border:0; background:transparent; color:inherit; cursor:pointer;
            font:inherit; padding:0; }
        .cf-statusbar button.cf-sb-item:hover { color:var(--cf-code-text); }
    </style>
    <?= \PpPhp\Core\ModuleAssets::style('CustomFiles', 'Views/custom-files-app-theme.css', '', 'cfAppThemeCss') ?>
    <link href="<?= htmlspecialchars($assetsUrl ?? '') ?>/sweetalert2/sweetalert2.min.css" rel="stylesheet">
    <script src="<?= htmlspecialchars($assetsUrl ?? '') ?>/sweetalert2/sweetalert2.min.js"></script>
<?php
    // Cache-bust the CodeMirror bundle: its URL is otherwise un-versioned, so a
    // browser would keep serving a stale copy after the bundle changes. Version by
    // the file's mtime+size so any edit forces a fresh download automatically.
    $cmVer = '1';
    foreach ([
        constant('BASE_PATH') . '/public/assets/codemirror/codemirror.bundle.js',
        constant('BASE_PATH') . '/assets/codemirror/codemirror.bundle.js',
    ] as $__cm) {
        if (is_file($__cm)) { $cmVer = filemtime($__cm) . '-' . filesize($__cm); break; }
    }
?>
    <link href="<?= htmlspecialchars($assetsUrl ?? '') ?>/codemirror/codemirror.bundle.css?v=<?= htmlspecialchars($cmVer) ?>" rel="stylesheet">
    <script src="<?= htmlspecialchars($assetsUrl ?? '') ?>/codemirror/codemirror.bundle.js?v=<?= htmlspecialchars($cmVer) ?>"></script>
</head>
<body class="cf-body">
    <?php include constant('BASE_PATH') . '/core/views/project-navbar.php'; ?>

    <div class="cf-workspace">
        <!-- LEFT: file tree -->
        <div class="cf-tree">
            <div class="cf-tree-head" style="display:flex; align-items:flex-start; gap:8px;">
                <div style="flex:1; min-width:0;">
                    <strong>Custom Files</strong>
                    <small>Copied into your app at build time</small>
                </div>
                <button type="button" class="cf-help-btn pp-guide-trigger" data-cf-help-open title="Open the Custom Files guide" aria-label="Open Custom Files guide">
                    <i class="bi bi-question-circle-fill" aria-hidden="true"></i><span>Guide</span>
                </button>
            </div>
            <div class="cf-toolbar">
                <button class="cf-tbtn" id="cfNewFile" title="New file"><i class="bi bi-file-earmark-plus"></i> File</button>
                <button class="cf-tbtn" id="cfNewFolder" title="New folder"><i class="bi bi-folder-plus"></i> Folder</button>
                <button class="cf-tbtn" id="cfUpload" title="Upload files"><i class="bi bi-upload"></i> Upload</button>
                <button class="cf-tbtn" id="cfDownloadZip" title="Download all files as a ZIP"><i class="bi bi-file-earmark-zip"></i></button>
                <button class="cf-tbtn" id="cfRefresh" title="Refresh"><i class="bi bi-arrow-clockwise"></i></button>
                <input type="file" id="cfUploadInput" multiple style="display:none">
            </div>
            <div class="cf-loc" id="cfLoc"></div>
            <div class="cf-search">
                <i class="bi bi-search"></i>
                <input type="search" id="cfSearch" placeholder="Filter files…" autocomplete="off" spellcheck="false">
            </div>
            <div class="cf-tree-body" id="cfTree"></div>
        </div>

        <!-- RIGHT: editor / preview -->
        <div class="cf-main">
            <div class="cf-head">
                <div class="cf-head-left">
                    <button class="cf-toggle" id="cfTreeToggle" title="Show / hide the file sidebar" aria-label="Toggle sidebar"><i class="bi bi-layout-sidebar"></i></button>
                    <div class="cf-crumb" id="cfCrumb"><span class="cf-dim">Select a file to edit</span></div>
                </div>
                <div class="cf-head-right">
                    <span class="cf-status" id="cfStatus"></span>
                    <div class="cf-viewseg" id="cfUndoRedo" style="display:none">
                        <button id="cfUndo" title="Undo (⌘/Ctrl+Z)" disabled><i class="bi bi-arrow-counterclockwise"></i></button>
                        <button id="cfRedo" title="Redo (⌘/Ctrl+Shift+Z)" disabled><i class="bi bi-arrow-clockwise"></i></button>
                    </div>
                    <div class="cf-viewseg" id="cfViewSeg" style="display:none">
                        <button data-view="edit" class="active" title="Code — editor only"><i class="bi bi-code-slash"></i></button>
                        <button data-view="split" title="Code + Preview (split)"><i class="bi bi-layout-split"></i></button>
                        <button data-view="preview" title="Preview only"><i class="bi bi-eye"></i></button>
                    </div>
                    <div class="cf-viewseg cf-pvsize" id="cfPvSize" style="display:none">
                        <button data-size="lg" class="active" title="Desktop preview (full width)"><i class="bi bi-display"></i></button>
                        <button data-size="md" title="Tablet preview (768px)"><i class="bi bi-tablet-landscape"></i></button>
                        <button data-size="sm" title="Mobile preview (390px)"><i class="bi bi-phone"></i></button>
                    </div>
                    <button class="cf-iconbtn" id="cfPreviewOpen" style="display:none" title="Open the live preview in a new browser tab"><i class="bi bi-box-arrow-up-right"></i></button>
                    <button class="cf-iconbtn" id="cfFullscreen" title="Full screen"><i class="bi bi-fullscreen"></i></button>
                    <button class="cf-checkbtn" id="cfCheck" disabled title="Check this file for syntax errors">
                        <i class="bi bi-bug"></i> Check
                    </button>
                    <button class="cf-savebtn" id="cfSave" disabled title="Save changes (⌘/Ctrl+S)"><i class="bi bi-check2"></i> Save</button>
                </div>
            </div>
            <div class="cf-info">
                <i class="bi bi-info-circle-fill"></i>
                <span>Files here are served from your app root, e.g. <code>site/index.html</code> → <code>/site/index.html</code>. Reserved system paths are protected.</span>
            </div>
            <div class="cf-tabs" id="cfTabs"></div>
            <div class="cf-editor-wrap" id="cfEditorWrap">
                <div class="cf-blank" id="cfBlank">
                    <i class="bi bi-folder2-open"></i>
                    <p>Create, upload, or select a file on the left.<br>Build your app to publish these files.</p>
                </div>
                <div class="cf-code" id="cfCode" style="display:none">
                    <div class="cf-code-area" id="cfCodeArea">
                        <div class="cf-find" id="cfFind">
                            <div class="cf-find-row">
                                <input type="text" id="cfFindInput" placeholder="Find" aria-label="Find">
                                <span class="cf-find-count" id="cfFindCount"></span>
                                <button class="cf-find-btn" id="cfFindPrev" title="Previous (Shift+Enter)">&#8593;</button>
                                <button class="cf-find-btn" id="cfFindNext" title="Next (Enter)">&#8595;</button>
                                <button class="cf-find-btn cf-find-x" id="cfFindClose" title="Close (Esc)">&times;</button>
                            </div>
                            <div class="cf-find-row">
                                <input type="text" id="cfReplaceInput" placeholder="Replace" aria-label="Replace">
                                <button class="cf-find-btn" id="cfReplaceOne" title="Replace the current match">Replace</button>
                                <button class="cf-find-btn" id="cfReplaceAll" title="Replace every match in this file">All</button>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="cf-live" id="cfLive" style="display:none">
                    <div class="cf-live-flag" id="cfLiveFlag" style="display:none" title="Click to re-run"><i class="bi bi-lightning-charge-fill"></i> PHP ran in a sandbox</div>
                    <iframe id="cfLiveFrame" title="Live preview" sandbox="allow-scripts allow-forms allow-popups allow-modals allow-pointer-lock"></iframe>
                    <div class="cf-md" id="cfLiveMd" style="display:none"></div>
                    <div class="cf-live-note" id="cfLiveNote" style="display:none"></div>
                </div>
                <div class="cf-preview" id="cfPreview" style="display:none"></div>
            </div>
            <!-- problems panel (code check results) -->
            <div class="cf-problems" id="cfProblems">
                <div class="cf-problems-head" id="cfProblemsHead">
                    <i class="bi bi-bug-fill"></i> <span id="cfProblemsTitle">Problems</span>
                    <span class="cf-ptool" id="cfProblemsTool"></span>
                    <button class="cf-pclose" id="cfProblemsClose" title="Close">&times;</button>
                </div>
                <div class="cf-problems-body" id="cfProblemsBody"></div>
            </div>
            <!-- compact status bar -->
            <div class="cf-statusbar" id="cfStatusbar" style="display:none">
                <span class="cf-sb-item cf-sb-lang" id="cfSbLang" title="Language">Plain Text</span>
                <span class="cf-sb-item cf-sb-path" id="cfSbPath" title="Served at this URL in your built app"></span>
                <span class="cf-sb-sep"></span>
                <span class="cf-sb-item" id="cfSbSel" title="Selection" style="display:none"></span>
                <button class="cf-sb-item" id="cfSbPos" title="Go to line (click)"><i class="bi bi-cursor-text"></i><span id="cfSbPosTxt">Ln 1, Col 1</span></button>
                <span class="cf-sb-item" id="cfSbLines" title="Total lines"></span>
                <span class="cf-sb-item" id="cfSbSize" title="File size"></span>
                <span class="cf-sb-item" id="cfSbEnc" title="Encoding · indentation">UTF-8 · Spaces: 4</span>
            </div>
        </div>
    </div>

    <div class="cf-toast" id="cfToast"></div>
    <div class="cf-ctx" id="cfCtx"></div>

    <!-- in-app Help & Guide (self-contained partial) -->
    <?php include __DIR__ . '/partials/help.php'; ?>

    <script>
    const baseUrl     = <?= json_encode($baseUrl ?? '') ?>;
    const projectSlug = <?= json_encode($project['slug'] ?? '') ?>;
    window.PpCustomFilesBootstrap = {
        slug: <?= json_encode($project['slug'] ?? '') ?>,
        baseUrl: <?= json_encode($baseUrl) ?>,
        apiBase: <?= json_encode(($baseUrl ?? '') . '/project/' . ($project['slug'] ?? '') . '/custom-files') ?>,
    };
    </script>
    <?php include constant('BASE_PATH') . '/core/views/project-scripts.php'; ?>
    <?= \PpPhp\Core\ModuleAssets::script('CustomFiles', 'Views/custom-files-spa.js') ?>
</body>
</html>
