<?php
// ─────────────────────────────────────────────────────────────
//  admin/builder.php — Full-screen form builder
//  Query params: ?id=123 (edit) | no id (new form)
// ─────────────────────────────────────────────────────────────
require_once dirname(__DIR__) . '/lib.php';
session_start_safe();
if (!admin_logged_in()) { header('Location: ../admin.php'); exit; }
$form_id    = isset($_GET['id']) ? (int)$_GET['id'] : 0;
$page_title = $form_id ? 'Edit Form' : 'New Form';
?><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover"/>
<title><?= htmlspecialchars($page_title) ?> — FormBuilder</title>
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js"></script>
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,300;0,9..40,400;0,9..40,500;0,9..40,600;1,9..40,400&family=DM+Mono:wght@400;500&display=swap" rel="stylesheet"/>
<style>
/* ── Design tokens (match admin.php exactly) ── */
*{box-sizing:border-box;margin:0;padding:0}
:root{
  --canvas:#f5f4f1;--white:#fff;--ink:#111217;--muted:#6b7280;--muted2:#9ca3af;
  --rule:rgba(0,0,0,.08);--rule2:rgba(0,0,0,.13);
  --accent:#2563eb;--ap:#eff6ff;--as:rgba(37,99,235,.12);--ad:#1d4ed8;
  --green:#059669;--gp:#ecfdf5;
  --red:#dc2626;--rp:#fef2f2;
  --amber:#d97706;--ambp:#fffbeb;
  --r6:6px;--r8:8px;--r12:12px;--r16:16px;
  --font:'DM Sans',sans-serif;--mono:'DM Mono',monospace;
  --th:52px;--sb:260px;--insp:300px;
}
html,body{height:100%;font-family:var(--font);font-size:14px;line-height:1.5;background:var(--canvas);color:var(--ink);overflow:hidden;-webkit-font-smoothing:antialiased}
button,input,select,textarea{font-family:var(--font)}
a{color:inherit;text-decoration:none}

/* ── Topbar ── */
.topbar{
  position:fixed;top:0;left:0;right:0;height:var(--th);z-index:100;
  background:var(--white);border-bottom:1px solid var(--rule);
  display:flex;align-items:center;padding:0 16px;gap:10px;
}
.tb-logo{display:flex;align-items:center;gap:8px;flex-shrink:0;text-decoration:none}
.tb-mark{width:28px;height:28px;background:var(--accent);border-radius:7px;display:flex;align-items:center;justify-content:center;flex-shrink:0}
.tb-mark svg{width:14px;height:14px;stroke:#fff;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.tb-name{font-size:13px;font-weight:500;color:var(--ink)}
.tb-divider{width:1px;height:20px;background:var(--rule2);flex-shrink:0;margin:0 2px}
.tb-title-wrap{flex:1;display:flex;align-items:center;gap:10px;min-width:0}
.tb-title-input{
  font-size:14px;font-weight:500;border:none;outline:none;background:transparent;
  color:var(--ink);min-width:140px;max-width:340px;
  padding:4px 8px;border-radius:var(--r6);transition:background .15s;
}
.tb-title-input:hover{background:var(--canvas)}
.tb-title-input:focus{background:var(--ap);color:var(--accent)}
.tb-title-input::placeholder{color:var(--muted2)}
.save-status{display:flex;align-items:center;gap:5px;font-size:11px;color:var(--muted);flex-shrink:0}
.save-dot{width:6px;height:6px;border-radius:50%;background:var(--green);flex-shrink:0;transition:background .2s}
.save-dot.dirty{background:var(--amber)}
.save-dot.saving{background:var(--accent);animation:pulse .8s infinite}
.save-dot.error{background:var(--red)}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.35}}
.tb-actions{display:flex;align-items:center;gap:6px;flex-shrink:0}

/* ── Buttons ── */
.btn{display:inline-flex;align-items:center;gap:6px;padding:7px 13px;border-radius:var(--r8);font-size:13px;font-weight:500;border:none;cursor:pointer;transition:all .15s;white-space:nowrap;text-decoration:none}
.btn svg{width:13px;height:13px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;flex-shrink:0}
.btn-primary{background:var(--accent);color:#fff}
.btn-primary:hover{background:var(--ad)}
.btn-primary:disabled{opacity:.5;cursor:not-allowed}
.btn-ghost{background:var(--white);color:var(--muted);border:1px solid var(--rule2)}
.btn-ghost:hover{border-color:var(--accent);color:var(--accent);background:var(--ap)}
.btn-danger{background:var(--rp);color:var(--red);border:1px solid #fecaca}
.btn-danger:hover{background:var(--red);color:#fff;border-color:var(--red)}
.btn-sm{padding:5px 10px;font-size:12px;border-radius:var(--r6)}
.btn-icon{padding:5px;width:28px;height:28px;justify-content:center}

/* ── 3-panel layout ── */
.builder-shell{
  display:grid;
  grid-template-columns:var(--sb) 1fr var(--insp);
  height:100vh;height:100dvh;padding-top:var(--th);
}
.bpanel{height:100%;overflow-y:auto;background:var(--white);scrollbar-width:thin;scrollbar-color:var(--rule2) transparent}
/* canvas-panel is NOT a .bpanel — must explicitly set height so flex children get computed heights */
.canvas-panel{height:100%;}
.bpanel::-webkit-scrollbar{width:4px}
.bpanel::-webkit-scrollbar-thumb{background:var(--rule2);border-radius:2px}
.panel-hdr{
  padding:12px 14px;border-bottom:1px solid var(--rule);
  font-size:10px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);
  position:sticky;top:0;background:var(--white);z-index:10;
  display:flex;align-items:center;justify-content:space-between;
}

/* ── Panel 1: Field Palette ── */
.palette{border-right:1px solid var(--rule)}
.palette-section{padding:10px}
.palette-group-label{font-size:10px;font-weight:600;color:var(--muted2);text-transform:uppercase;letter-spacing:.07em;padding:8px 10px 4px}
.ftb{
  display:flex;align-items:center;gap:9px;width:100%;
  padding:9px 11px;border-radius:var(--r8);border:1px solid var(--rule);
  background:var(--white);font-size:12px;font-weight:500;color:var(--ink);
  cursor:grab;margin-bottom:5px;transition:all .15s;user-select:none;
}
.ftb:hover{border-color:var(--accent);background:var(--ap);color:var(--accent);transform:translateX(2px)}
.ftb svg{width:15px;height:15px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;flex-shrink:0}

/* ── Panel 2: Canvas ── */
.canvas-panel{background:var(--canvas);border-right:1px solid var(--rule);display:flex;flex-direction:column;overflow:hidden}
.canvas-tabs{display:flex;border-bottom:1px solid var(--rule);background:var(--white);flex-shrink:0}
.ctab{
  display:flex;align-items:center;gap:5px;
  padding:11px 16px;font-size:12px;font-weight:500;color:var(--muted);
  border:none;background:none;cursor:pointer;border-bottom:2px solid transparent;transition:all .15s;
}
.ctab svg{width:13px;height:13px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.ctab:hover{color:var(--ink)}
.ctab.active{color:var(--accent);border-bottom-color:var(--accent)}
.canvas-body{flex:1;overflow-y:auto;padding:24px 20px;scrollbar-width:thin;scrollbar-color:var(--rule2) transparent}

/* ── Canvas form card ── */
.canvas-form-card{
  background:var(--white);border-radius:var(--r16);
  border:1px solid var(--rule);
  box-shadow:0 4px 24px rgba(0,0,0,.06);overflow:hidden;
  max-width:600px;margin:0 auto;
}
.canvas-form-hdr{background:var(--accent);padding:22px 24px 18px;color:#fff}
.canvas-form-hdr h2{font-size:18px;font-weight:600;margin-bottom:3px}
.canvas-form-hdr p{font-size:13px;opacity:.82}
.canvas-form-body{padding:20px 24px}

/* ── Canvas field items ── */
.canvas-field{
  padding:12px 14px;border-radius:var(--r8);border:1.5px solid var(--rule);
  background:var(--white);margin-bottom:8px;cursor:pointer;
  transition:all .15s;position:relative;display:flex;align-items:center;gap:10px;
}
.canvas-field:hover{border-color:var(--accent);background:var(--ap)}
.canvas-field.selected{border-color:var(--accent);box-shadow:0 0 0 3px var(--as);background:var(--ap)}
.canvas-field.sortable-ghost{opacity:.4;background:var(--ap)}
.canvas-field.sortable-drag{box-shadow:0 8px 28px rgba(37,99,235,.18);transform:rotate(1deg)}
.cf-drag{cursor:grab;color:var(--muted2);flex-shrink:0}
.cf-drag svg{width:14px;height:14px;stroke:currentColor;fill:none;stroke-width:2}
.cf-body{flex:1;min-width:0}
.cf-label{font-size:13px;font-weight:500;color:var(--ink);display:flex;align-items:center;gap:4px}
.cf-label .req{color:var(--red);font-size:11px}
.cf-type{font-size:10px;color:var(--muted2);text-transform:uppercase;letter-spacing:.05em;margin-top:2px}
.cf-hint{font-size:11px;color:var(--muted);margin-top:3px;font-style:italic}
.cf-actions{display:flex;gap:3px;opacity:0;transition:opacity .15s;flex-shrink:0}
.canvas-field:hover .cf-actions,.canvas-field.selected .cf-actions{opacity:1}
.cf-btn{width:26px;height:26px;border:none;border-radius:var(--r6);background:rgba(0,0,0,.04);color:var(--muted);display:flex;align-items:center;justify-content:center;transition:all .15s;flex-shrink:0}
.cf-btn:hover{background:var(--rule);color:var(--ink)}
.cf-btn.del:hover{background:var(--rp);color:var(--red)}
.cf-btn svg{width:12px;height:12px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.type-pill{font-size:10px;padding:2px 7px;border-radius:20px;background:var(--canvas);color:var(--muted);font-weight:500;letter-spacing:.04em;text-transform:uppercase;flex-shrink:0}
.canvas-drop-zone{
  border:2px dashed var(--rule2);border-radius:var(--r12);padding:36px;
  text-align:center;color:var(--muted);font-size:13px;font-weight:500;
  margin-bottom:10px;transition:all .2s;
}
.canvas-drop-zone.drag-over{border-color:var(--accent);background:var(--ap);color:var(--accent)}
.sortable-placeholder{height:50px;border-radius:var(--r8);border:2px dashed var(--accent);background:var(--ap);margin-bottom:8px}
.canvas-add-row{display:flex;justify-content:center;margin-top:10px}
.canvas-add-btn{
  display:inline-flex;align-items:center;gap:6px;
  padding:8px 18px;border:1.5px dashed var(--rule2);border-radius:var(--r8);
  background:none;font-size:13px;font-weight:500;color:var(--muted);cursor:pointer;transition:all .15s;
}
.canvas-add-btn:hover{border-color:var(--accent);color:var(--accent);background:var(--ap)}
.canvas-add-btn svg{width:14px;height:14px;stroke:currentColor;fill:none;stroke-width:2.5}

/* ── Live preview ── */
.prev-field{margin-bottom:16px}
.prev-label{display:block;font-size:13px;font-weight:500;color:var(--ink);margin-bottom:5px}
.prev-label .req{color:var(--red);margin-left:2px}
.prev-input,.prev-select,.prev-textarea{
  width:100%;border:1.5px solid var(--rule2);border-radius:var(--r8);
  padding:9px 12px;font-size:13px;color:var(--ink);background:var(--white);font-family:var(--font);
}
.prev-input:focus,.prev-select:focus,.prev-textarea:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px var(--as)}
.prev-textarea{resize:vertical;min-height:80px}
.prev-hint{font-size:11px;color:var(--muted);margin-top:4px}
.prev-opts{display:flex;flex-direction:column;gap:6px}
.prev-opt{display:flex;align-items:center;gap:9px;padding:8px 12px;border:1.5px solid var(--rule);border-radius:var(--r8);cursor:pointer}
.prev-opt input{accent-color:var(--accent)}
.prev-file-zone{border:2px dashed var(--rule2);border-radius:var(--r8);padding:24px;text-align:center;color:var(--muted);font-size:13px}
.prev-sig-wrap{border:1.5px solid var(--rule2);border-radius:var(--r8);height:100px;background:#fafafa;display:flex;align-items:center;justify-content:center;color:var(--muted);font-size:13px}
.prev-submit{
  width:100%;padding:12px;background:var(--accent);color:#fff;
  font-size:14px;font-weight:500;border:none;border-radius:var(--r8);margin-top:8px;
  font-family:var(--font);cursor:pointer;transition:background .15s;
}
.prev-submit:hover{background:var(--ad)}

/* ── Panel 3: Inspector ── */
.inspector{border-left:1px solid var(--rule)}
.insp-empty{padding:44px 20px;text-align:center;color:var(--muted2)}
.insp-empty svg{width:36px;height:36px;stroke:var(--rule2);fill:none;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;margin:0 auto 10px;display:block}
.insp-empty p{font-size:12px}
.insp-body{padding:14px}
.insp-section{margin-bottom:18px}
.insp-section-title{font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.08em;color:var(--muted);margin-bottom:10px;padding-bottom:6px;border-bottom:1px solid var(--rule)}
.fg{margin-bottom:13px}
.fg label{display:block;font-size:11px;font-weight:500;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:4px}
.fg input,.fg select,.fg textarea{
  width:100%;border:1.5px solid var(--rule2);border-radius:var(--r8);
  padding:8px 11px;font-size:13px;color:var(--ink);outline:none;transition:border .15s;
  font-family:var(--font);background:var(--white);
}
.fg input:focus,.fg select:focus,.fg textarea:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--as)}
.fg textarea{min-height:70px;resize:vertical}
.fg .hint{font-size:11px;color:var(--muted2);margin-top:3px}
.toggle-row{display:flex;align-items:center;gap:9px;margin-bottom:10px}
.toggle{position:relative;width:36px;height:20px;flex-shrink:0}
.toggle input{opacity:0;width:0;height:0;position:absolute}
.toggle-slider{position:absolute;inset:0;background:#d1d5db;border-radius:20px;cursor:pointer;transition:.2s}
.toggle-slider:before{content:'';position:absolute;width:14px;height:14px;left:3px;top:3px;background:#fff;border-radius:50%;transition:.2s;box-shadow:0 1px 3px rgba(0,0,0,.2)}
.toggle input:checked+.toggle-slider{background:var(--accent)}
.toggle input:checked+.toggle-slider:before{transform:translateX(16px)}
.toggle-label{font-size:13px;font-weight:500;color:var(--ink)}
.opts-editor{border:1.5px solid var(--rule2);border-radius:var(--r8);overflow:hidden}
.opt-row{display:flex;align-items:center;border-bottom:1px solid var(--rule)}
.opt-row:last-child{border-bottom:none}
.opt-row input{border:none;outline:none;padding:8px 11px;font-size:13px;flex:1;font-family:var(--font);background:var(--white)}
.opt-row input:focus{background:var(--ap)}
.opt-del{width:30px;height:30px;border:none;background:none;color:var(--muted2);cursor:pointer;display:flex;align-items:center;justify-content:center;flex-shrink:0}
.opt-del:hover{color:var(--red)}
.opt-del svg{width:12px;height:12px;stroke:currentColor;fill:none;stroke-width:2.5;stroke-linecap:round}
.add-opt-btn{display:flex;align-items:center;gap:5px;padding:7px 11px;width:100%;border:none;background:var(--canvas);color:var(--muted);font-size:12px;font-weight:500;cursor:pointer;transition:all .15s;margin-top:5px;border-radius:var(--r6)}
.add-opt-btn:hover{background:var(--ap);color:var(--accent)}
.add-opt-btn svg{width:12px;height:12px;stroke:currentColor;fill:none;stroke-width:2.5}

/* ── Settings panel ── */
.settings-panel{padding:20px;max-width:580px;margin:0 auto}
.settings-section-title{font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.08em;color:var(--muted);margin-bottom:12px;margin-top:20px;padding-bottom:6px;border-bottom:1px solid var(--rule)}
.settings-section-title:first-child{margin-top:0}

/* ── Share / Embed panel ── */
.url-row{display:flex;gap:7px;align-items:center}
.url-display{flex:1;border:1.5px solid var(--rule2);border-radius:var(--r8);padding:8px 11px;font-size:12px;font-family:var(--mono);color:var(--ink);background:var(--canvas);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.qr-wrap{display:flex;gap:16px;align-items:flex-start;margin-top:10px;flex-wrap:wrap}
.qr-wrap img{border-radius:var(--r8);border:1px solid var(--rule);width:140px;height:140px;flex-shrink:0}
.embed-code{width:100%;border:1.5px solid var(--rule2);border-radius:var(--r8);padding:10px 12px;font-size:11px;font-family:var(--mono);color:var(--ink);background:var(--canvas);resize:none;outline:none;line-height:1.5}
.copy-btn{padding:5px 10px;font-size:11px;border-radius:var(--r6);background:var(--ap);color:var(--accent);border:1px solid rgba(37,99,235,.2);cursor:pointer;font-weight:500;transition:all .15s;white-space:nowrap}
.copy-btn:hover{background:var(--accent);color:#fff}

/* ── Conditional logic ── */
.cond-row{background:var(--canvas);border-radius:var(--r8);padding:10px;margin-bottom:8px;font-size:13px}
.cond-row select{padding:5px 8px;font-size:12px;border:1.5px solid var(--rule2);border-radius:var(--r6);background:var(--white);color:var(--ink);outline:none;font-family:var(--font)}
.cond-row select:focus{border-color:var(--accent)}
.cond-row input{padding:5px 8px;font-size:12px;border:1.5px solid var(--rule2);border-radius:var(--r6);background:var(--white);color:var(--ink);outline:none;width:100%;margin-top:7px;font-family:var(--font)}
.cond-row input:focus{border-color:var(--accent)}
.cond-label{font-size:10px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:6px}

/* ── Toast ── */
.toast{position:fixed;bottom:20px;left:50%;transform:translateX(-50%) translateY(16px);background:var(--ink);color:#fff;padding:8px 18px;border-radius:999px;font-size:12px;font-weight:500;z-index:999;opacity:0;transition:all .28s;pointer-events:none;white-space:nowrap;box-shadow:0 4px 16px rgba(0,0,0,.2)}
.toast.show{opacity:1;transform:translateX(-50%) translateY(0)}
.toast.ok{background:var(--green)}
.toast.err{background:var(--red)}

/* ── Mobile panels (bottom sheet approach) ── */
@media(max-width:1100px){:root{--insp:270px}}
@media(max-width:900px){
  :root{--sb:220px;--insp:0px}
  .builder-shell{grid-template-columns:var(--sb) 1fr 0}
  .inspector{display:none}
}
@media(max-width:700px){
  :root{--sb:0px}
  .builder-shell{
    grid-template-columns:0 1fr 0;
    height:100svh;height:100dvh; /* exclude browser chrome on iOS */
  }
  .palette{display:none}
  .inspector{display:none}
  .canvas-panel{height:100%;display:flex;flex-direction:column}
  .canvas-body{
    flex:1;min-height:0;
    padding:14px 12px 76px;
    overflow-y:auto;-webkit-overflow-scrolling:touch;
  }
  .topbar{padding:0 10px;gap:8px}
  .tb-name{display:none}
  .save-status{display:none}
  /* mobile panel toggle buttons at bottom */
  .mob-panel-bar{display:flex;position:fixed;bottom:0;left:0;right:0;height:52px;
    background:var(--white);border-top:1px solid var(--rule);z-index:90;
    padding:0 8px;gap:4px;align-items:center;justify-content:space-around;
    padding-bottom:env(safe-area-inset-bottom,0px)}
}
@media(min-width:701px){.mob-panel-bar{display:none}}
@media(max-width:420px){
  .tb-title-input{min-width:90px;max-width:150px}
  .btn-sm span{display:none}
}

/* ── Field grid layout ── */
.canvas-fields-grid{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.cf-full{grid-column:1/-1}
.cf-half{grid-column:span 1}
.width-btns{display:flex;gap:5px}
.wbtn{flex:1;padding:6px 8px;border:1.5px solid var(--rule2);border-radius:var(--r6);background:var(--white);font-size:11px;font-weight:500;color:var(--muted);cursor:pointer;transition:all .15s;text-align:center}
.wbtn.active{border-color:var(--accent);background:var(--ap);color:var(--accent)}
.wbtn:hover:not(.active){border-color:var(--muted2);color:var(--ink)}

/* ── Page break field ── */
.canvas-pb{padding:10px 14px;align-items:center}
.canvas-pb.selected{border-color:var(--amber)!important;box-shadow:0 0 0 3px rgba(217,119,6,.12)!important}
.pb-inner{flex:1;display:flex;align-items:center;gap:10px;min-width:0;overflow:hidden}
.pb-line{flex:1;height:1px;background:var(--rule2)}
.pb-label{font-size:11px;font-weight:600;color:var(--amber);white-space:nowrap;
  background:var(--ambp);border:1px solid #fde68a;border-radius:20px;padding:3px 10px;
  display:flex;align-items:center;gap:5px;flex-shrink:0}
.pb-label svg{width:11px;height:11px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}

/* ── Preview multi-step ── */
.prev-fields-grid{display:grid;grid-template-columns:1fr 1fr;gap:8px 16px}
.pf-full{grid-column:1/-1}
.pf-half{grid-column:span 1}
@media(max-width:520px){.prev-fields-grid{grid-template-columns:1fr}.pf-half{grid-column:1/-1}}

.step-progress{display:flex;align-items:center;margin-bottom:18px;padding:0 2px}
.step-dot{width:26px;height:26px;border-radius:50%;display:flex;align-items:center;justify-content:center;
  font-size:11px;font-weight:600;border:2px solid var(--rule2);color:var(--muted);background:var(--white);
  flex-shrink:0;transition:all .2s;line-height:1}
.step-dot.active{border-color:var(--accent);background:var(--accent);color:#fff}
.step-dot.done{border-color:var(--green);background:var(--green);color:#fff}
.step-line{flex:1;height:2px;background:var(--rule);margin:0 -1px;transition:background .2s}
.step-line.done{background:var(--green)}
.step-nav{display:flex;gap:8px;margin-top:16px;padding-top:14px;border-top:1px solid var(--rule)}
.step-nav-back{flex:1;padding:10px;background:var(--canvas);color:var(--ink);font-size:13px;
  font-weight:500;border:1.5px solid var(--rule2);border-radius:var(--r8);cursor:pointer;
  font-family:var(--font);transition:all .15s}
.step-nav-back:hover{border-color:var(--ink)}
.step-nav-next{flex:2;padding:10px;background:var(--accent);color:#fff;font-size:13px;
  font-weight:500;border:none;border-radius:var(--r8);cursor:pointer;font-family:var(--font);transition:background .15s}
.step-nav-next:hover{background:var(--ad)}

/* ── Mobile inspector bottom sheet ── */
.mob-insp-bd{display:none;position:fixed;inset:0;background:rgba(0,0,0,.38);z-index:84}
.mob-insp{position:fixed;bottom:0;left:0;right:0;max-height:72vh;overflow-y:auto;
  background:var(--white);border-radius:var(--r16) var(--r16) 0 0;z-index:86;
  box-shadow:0 -8px 32px rgba(0,0,0,.15);
  transform:translateY(100%);transition:transform .28s cubic-bezier(.4,0,.2,1)}
.mob-insp.open,.mob-insp-bd.open{display:block}
.mob-insp.open{transform:translateY(0)}
.mob-insp-hdr{position:sticky;top:0;background:var(--white);border-bottom:1px solid var(--rule);
  padding:11px 16px;display:flex;align-items:center;justify-content:space-between;z-index:2;
  border-radius:var(--r16) var(--r16) 0 0}
.mob-insp-title{font-size:13px;font-weight:600;color:var(--ink)}
.mob-insp-close{width:26px;height:26px;border:1px solid var(--rule2);border-radius:var(--r6);
  background:none;display:flex;align-items:center;justify-content:center;cursor:pointer;
  color:var(--muted);font-size:16px;line-height:1}

/* ── Mobile builder canvas fill ── */
@media(max-width:700px){
  .canvas-panel{min-height:0}
  .canvas-fields-grid{grid-template-columns:1fr}
  .cf-half{grid-column:1/-1}
}

/* Fade in */
.fade-in{animation:fadeIn .3s ease both}
@keyframes fadeIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
</style>
</head>
<body>

<!-- ── Topbar ── -->
<div class="topbar">
  <a href="../admin.php" class="tb-logo" title="Back to Dashboard">
    <div class="tb-mark"><svg viewBox="0 0 24 24"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18M9 21V9"/></svg></div>
    <span class="tb-name">FormBuilder</span>
  </a>
  <div class="tb-divider"></div>
  <div class="tb-title-wrap">
    <input type="text" class="tb-title-input" id="form-title" placeholder="Untitled Form" maxlength="120" autocomplete="off" oninput="markDirty()"/>
    <div class="save-status" id="save-status">
      <span class="save-dot" id="save-dot"></span>
      <span id="save-text">Saved</span>
    </div>
  </div>
  <div class="tb-actions">
    <a href="../admin.php" class="btn btn-ghost btn-sm">
      <svg viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg>
      <span>Back</span>
    </a>
    <?php if($form_id): ?>
    <button class="btn btn-ghost btn-sm" onclick="duplicateCurrentForm()" title="Duplicate this form">
      <svg viewBox="0 0 24 24"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
      <span>Duplicate</span>
    </button>
    <?php endif; ?>
    <button class="btn btn-ghost btn-sm" onclick="previewForm()" title="Preview public form">
      <svg viewBox="0 0 24 24"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
      <span>Preview</span>
    </button>
    <button class="btn btn-primary btn-sm" id="save-btn" onclick="saveForm()">
      <svg viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>
      Save
    </button>
  </div>
</div>

<!-- ── Builder Shell ── -->
<div class="builder-shell">

  <!-- Panel 1: Field Palette -->
  <div class="bpanel palette" id="palette-panel">
    <div class="panel-hdr">Fields <span style="font-size:9px;color:var(--muted2);font-weight:400;text-transform:none;letter-spacing:0">drag or click</span></div>
    <div class="palette-section" id="field-palette"></div>
  </div>

  <!-- Panel 2: Canvas -->
  <div class="canvas-panel" id="canvas-panel">
    <div class="canvas-tabs">
      <button class="ctab active" id="tab-builder" onclick="switchTab('builder')">
        <svg viewBox="0 0 24 24"><rect x="3" y="3" width="18" height="18" rx="2"/><line x1="3" y1="9" x2="21" y2="9"/><line x1="9" y1="21" x2="9" y2="9"/></svg>
        Builder
      </button>
      <button class="ctab" id="tab-preview" onclick="switchTab('preview')">
        <svg viewBox="0 0 24 24"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
        Preview
      </button>
      <button class="ctab" id="tab-settings" onclick="switchTab('settings')">
        <svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
        Settings
      </button>
    </div>
    <div class="canvas-body" id="canvas-body"></div>
  </div>

  <!-- Panel 3: Inspector -->
  <div class="bpanel inspector" id="inspector-panel">
    <div class="panel-hdr" id="insp-hdr">Properties</div>
    <div id="inspector-body">
      <div class="insp-empty">
        <svg viewBox="0 0 24 24"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
        <p>Click a field to edit its properties</p>
      </div>
    </div>
  </div>

</div>

<!-- Mobile bottom bar (fields / properties toggle) -->
<div class="mob-panel-bar">
  <button class="btn btn-ghost btn-sm" onclick="mobTogglePanel('palette')" id="mob-palette-btn">
    <svg viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
    Add Field
  </button>
  <button class="btn btn-ghost btn-sm" onclick="saveForm()">
    <svg viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>
    Save
  </button>
  <button class="btn btn-ghost btn-sm" id="mob-props-btn" onclick="openMobInspector()" style="display:none">
    <svg viewBox="0 0 24 24"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
    Edit
  </button>
  <button class="btn btn-ghost btn-sm" onclick="switchTab('preview')">
    <svg viewBox="0 0 24 24"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
    Preview
  </button>
</div>

<!-- Mobile inspector bottom sheet -->
<div class="mob-insp-bd" id="mob-insp-bd" onclick="closeMobInsp()"></div>
<div class="mob-insp" id="mob-insp">
  <div class="mob-insp-hdr">
    <div class="mob-insp-title" id="mob-insp-title">Properties</div>
    <button class="mob-insp-close" onclick="closeMobInsp()">&#x2715;</button>
  </div>
  <div id="mob-insp-body" style="padding:4px"></div>
</div>

<div class="toast" id="toast"></div>

<script>
// ─────────────────────────────────────────────────────────────
//  State
// ─────────────────────────────────────────────────────────────
const FORM_ID  = <?= $form_id ?: 'null' ?>;
const BASE_API = (()=>{const p=window.location.pathname;return p.substring(0,p.indexOf('/admin/'))+'/api';})();
const BASE_URL = (()=>{const p=window.location.pathname;return window.location.origin+p.substring(0,p.indexOf('/admin/'));})();

let formState = {
  id:FORM_ID,slug:null,title:'',description:'',fields:[],
  is_public:1,form_password:'',submit_label:'Submit',
  success_url:'',notify_email:'',submission_limit:0,is_active:1,
};
let selectedFieldId=null,dirty=false,saveTimer=null,currentTab='builder',sortable=null;

// ─────────────────────────────────────────────────────────────
//  Field types
// ─────────────────────────────────────────────────────────────
const FIELD_GROUPS = [
  {label:'Text',fields:[
    {t:'text',     label:'Short Text',  icon:'<line x1="17" y1="10" x2="3" y2="10"/><line x1="21" y1="6" x2="3" y2="6"/><line x1="21" y1="14" x2="3" y2="14"/><line x1="13" y1="18" x2="3" y2="18"/>'},
    {t:'textarea', label:'Long Text',   icon:'<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>'},
    {t:'email',    label:'Email',       icon:'<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/>'},
    {t:'phone',    label:'Phone',       icon:'<path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07A19.5 19.5 0 0 1 4.69 14a19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 3.56 3h3a2 2 0 0 1 2 1.72c.127.96.361 1.903.7 2.81a2 2 0 0 1-.45 2.11L7.91 10.09a16 16 0 0 0 6 6l.9-.9a2 2 0 0 1 2.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0 1 22 17z"/>'},
    {t:'number',   label:'Number',      icon:'<line x1="12" y1="1" x2="12" y2="23"/><path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/>'},
    {t:'date',     label:'Date',        icon:'<rect x="3" y="4" width="18" height="18" rx="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/>'},
  ]},
  {label:'Choice',fields:[
    {t:'select',   label:'Dropdown',    icon:'<path d="M6 9l6 6 6-6"/>'},
    {t:'radio',    label:'Radio',       icon:'<circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="3"/>'},
    {t:'checkbox', label:'Checkboxes',  icon:'<polyline points="9 11 12 14 22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/>'},
  ]},
  {label:'Upload & Signature',fields:[
    {t:'file',         label:'File Upload', icon:'<polyline points="16 16 12 12 8 16"/><line x1="12" y1="12" x2="12" y2="21"/><path d="M20.39 18.39A5 5 0 0 0 18 9h-1.26A8 8 0 1 0 3 16.3"/>'},
    {t:'signature',    label:'Signature',   icon:'<path d="M20 20H7L3 16V4a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v9"/><path d="M6.5 17.5L15 9"/>'},
    {t:'type-to-sign', label:'Type-to-Sign',icon:'<path d="M17 3a2.828 2.828 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z"/>'},
  ]},
  {label:'Layout',fields:[
    {t:'page-break',   label:'Page Break / Step', icon:'<line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/>'},
  ]},
];

// ─────────────────────────────────────────────────────────────
//  Init
// ─────────────────────────────────────────────────────────────
async function init(){
  renderPalette();
  if(FORM_ID){
    const j=await apiFetch('/forms/'+FORM_ID);
    if(j.ok){
      const f=j.form;
      formState={
        id:f.id,slug:f.slug||null,title:f.title,description:f.description||'',
        fields:f.fields||[],is_public:f.is_public,
        form_password:f.form_password||'',submit_label:f.submit_label||'Submit',
        success_url:f.success_url||'',notify_email:f.notify_email||'',
        submission_limit:f.submission_limit||0,is_active:f.is_active
      };
      document.getElementById('form-title').value=formState.title;
    }
  }
  renderCanvas();
  setSaveStatus('saved');
  document.addEventListener('keydown',e=>{
    if((e.metaKey||e.ctrlKey)&&e.key==='s'){e.preventDefault();saveForm();}
    if(e.key==='Escape') deselectField();
  });
}

async function apiFetch(path,method='GET',body=null){
  const opts={method,headers:{'Content-Type':'application/json'},credentials:'same-origin'};
  if(body) opts.body=JSON.stringify(body);
  const r=await fetch(BASE_API+path,opts);
  return r.json();
}

// ─────────────────────────────────────────────────────────────
//  Palette
// ─────────────────────────────────────────────────────────────
function renderPalette(){
  document.getElementById('field-palette').innerHTML=FIELD_GROUPS.map(g=>`
    <div class="palette-group-label">${g.label}</div>
    ${g.fields.map(f=>`
      <button class="ftb" draggable="true" data-type="${f.t}"
        ondragstart="paletteDragStart(event,'${f.t}')"
        onclick="addFieldOfType('${f.t}')">
        <svg viewBox="0 0 24 24">${f.icon}</svg>${f.label}
      </button>`).join('')}
  `).join('');
}

let paletteDragging=null;
function paletteDragStart(e,type){paletteDragging=type;e.dataTransfer.setData('text/plain',type);e.dataTransfer.effectAllowed='copy';}

// Mobile palette overlay
let mobPaletteOpen=false;
function mobTogglePanel(panel){
  if(panel==='palette'){
    if(mobPaletteOpen){document.getElementById('palette-panel').style.cssText='';mobPaletteOpen=false;}
    else{
      const el=document.getElementById('palette-panel');
      el.style.cssText='display:block;position:fixed;bottom:52px;left:0;right:0;max-height:55vh;overflow-y:auto;z-index:85;border-top:1px solid var(--rule);box-shadow:0 -8px 24px rgba(0,0,0,.12)';
      mobPaletteOpen=true;
    }
  }
}

// ─────────────────────────────────────────────────────────────
//  Canvas
// ─────────────────────────────────────────────────────────────
function renderCanvas(){
  const body=document.getElementById('canvas-body');
  if(currentTab==='builder') renderBuilderCanvas(body);
  else if(currentTab==='preview') renderPreviewCanvas(body);
  else renderSettingsCanvas(body);
}

function renderBuilderCanvas(body){
  const fields=formState.fields;
  body.innerHTML=`
    <div class="canvas-form-card fade-in">
      <div class="canvas-form-hdr">
        <h2>${esc(formState.title||'Untitled Form')}</h2>
        <p>${esc(formState.description||'Fill in the form below')}</p>
      </div>
      <div class="canvas-form-body">
        ${!fields.length?`<div class="canvas-drop-zone" id="drop-zone"
          ondragover="canvasDragOver(event)" ondrop="canvasDrop(event)" ondragleave="canvasDragLeave(event)">
          <svg width="28" height="28" viewBox="0 0 24 24" style="stroke:var(--muted2);fill:none;stroke-width:1.5;stroke-linecap:round;display:block;margin:0 auto 8px"><rect x="3" y="3" width="18" height="18" rx="2"/><line x1="3" y1="9" x2="21" y2="9"/><line x1="9" y1="21" x2="9" y2="9"/></svg>
          Drag fields here or click a type in the sidebar
        </div>`:''}
        <div id="canvas-fields"></div>
        <div class="canvas-add-row">
          <button class="canvas-add-btn" onclick="showAddFieldMenu()">
            <svg viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
            Add Field
          </button>
        </div>
      </div>
    </div>`;
  refreshCanvasFields();
  initSortable();
}

function refreshCanvasFields(){
  const el=document.getElementById('canvas-fields');
  if(!el) return;
  el.className='canvas-fields-grid';
  el.innerHTML=formState.fields.map(f=>renderCanvasField(f)).join('');
  document.querySelectorAll('.canvas-field').forEach(el=>{
    el.addEventListener('click',e=>{
      if(e.target.closest('.cf-btn')) return;
      selectField(el.dataset.fid);
    });
  });
}

function renderCanvasField(f){
  if(f.type==='page-break') return renderPageBreakField(f);
  const condIcon=f.condition?`<svg width="11" height="11" viewBox="0 0 24 24" style="stroke:var(--amber);fill:none;stroke-width:2;stroke-linecap:round;vertical-align:middle;margin-left:3px" title="Conditional"><polyline points="22 11.08 22 12 21.9 12.9"/><path d="M4.93 4.93a10 10 0 1 0 14.14 14.14"/></svg>`:'';
  const widthCls = f.width==='half' ? 'cf-half' : 'cf-full';
  return`<div class="canvas-field ${widthCls}${f.id===selectedFieldId?' selected':''}" data-fid="${f.id}">
    <span class="cf-drag"><svg viewBox="0 0 24 24"><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/></svg></span>
    <div class="cf-body">
      <div class="cf-label">${esc(f.label||'Unnamed')}${f.required?'<span class="req"> *</span>':''}${condIcon}</div>
      <div class="cf-type">${getTypeLabel(f.type)}${f.width==='half'?' · ½ width':''}</div>
      ${f.hint?`<div class="cf-hint">${esc(f.hint)}</div>`:''}
    </div>
    <span class="type-pill">${f.type}</span>
    <div class="cf-actions">
      <button class="cf-btn" title="Duplicate" onclick="duplicateField('${f.id}')"><svg viewBox="0 0 24 24"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg></button>
      <button class="cf-btn del" title="Delete" onclick="removeField('${f.id}')"><svg viewBox="0 0 24 24"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6M14 11v6M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/></svg></button>
    </div>
  </div>`;
}

function renderPageBreakField(f){
  const pbs=formState.fields.filter(x=>x.type==='page-break');
  const stepIdx=pbs.findIndex(x=>x.id===f.id);
  const stepNum=stepIdx+2;
  return`<div class="canvas-field canvas-pb cf-full${f.id===selectedFieldId?' selected':''}" data-fid="${f.id}">
    <span class="cf-drag"><svg viewBox="0 0 24 24"><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/></svg></span>
    <div class="pb-inner">
      <div class="pb-line"></div>
      <div class="pb-label">
        <svg viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg>
        Step ${stepNum}: ${esc(f.label||'Next Step')}
      </div>
      <div class="pb-line"></div>
    </div>
    <div class="cf-actions">
      <button class="cf-btn del" title="Delete" onclick="removeField('${f.id}')"><svg viewBox="0 0 24 24"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/></svg></button>
    </div>
  </div>`;
}

function initSortable(){
  const el=document.getElementById('canvas-fields');
  if(!el) return;
  if(sortable) sortable.destroy();
  sortable=Sortable.create(el,{
    animation:150,ghostClass:'sortable-ghost',dragClass:'sortable-drag',handle:'.cf-drag',
    onEnd:e=>{
      const fids=[...el.querySelectorAll('.canvas-field')].map(el=>el.dataset.fid);
      formState.fields=fids.map(id=>formState.fields.find(f=>f.id===id)).filter(Boolean);
      markDirty();
    }
  });
}

function canvasDragOver(e){e.preventDefault();e.currentTarget.classList.add('drag-over');}
function canvasDragLeave(e){e.currentTarget.classList.remove('drag-over');}
function canvasDrop(e){e.preventDefault();e.currentTarget.classList.remove('drag-over');const t=e.dataTransfer.getData('text/plain');if(t)addFieldOfType(t);}

// ─────────────────────────────────────────────────────────────
//  Preview tab
// ─────────────────────────────────────────────────────────────
function renderPreviewCanvas(body){
  // Split fields into steps at each page-break
  const steps=[];
  let cur=[];
  formState.fields.forEach(f=>{
    if(f.type==='page-break'){steps.push({label:f.label||'Next Step',fields:cur});cur=[];}
    else cur.push(f);
  });
  steps.push({label:formState.submit_label||'Submit',fields:cur});
  const isMulti=steps.length>1;
  window._pvSteps=steps;
  if(window._pvStep===undefined||window._pvStep>=steps.length) window._pvStep=0;
  const si=window._pvStep;
  const step=steps[si];

  const progressHtml=isMulti?`<div class="step-progress">
    ${steps.map((s,i)=>`
      <div class="step-dot ${i<si?'done':i===si?'active':''}">${i<si?'&#x2713;':i+1}</div>
      ${i<steps.length-1?`<div class="step-line ${i<si?'done':''}"></div>`:''}
    `).join('')}
  </div>`:'';

  const fieldsHtml=step.fields.length
    ?`<div class="prev-fields-grid">${step.fields.map(renderPreviewField).join('')}</div>`
    :'<div style="text-align:center;color:var(--muted);font-size:13px;padding:20px">No fields in this step</div>';

  const navHtml=isMulti?`<div class="step-nav">
    ${si>0?`<button class="step-nav-back" onclick="pvNav(-1)">&#8592; Back</button>`:''}
    ${si<steps.length-1
      ?`<button class="step-nav-next" onclick="pvNav(1)">${esc(steps[si+1].label)} &#8594;</button>`
      :`<button class="prev-submit" style="flex:2">${esc(formState.submit_label||'Submit')}</button>`
    }
  </div>`:step.fields.length?`<button class="prev-submit">${esc(formState.submit_label||'Submit')}</button>`
    :'<div style="text-align:center;color:var(--muted);font-size:13px;padding:20px">No fields yet — add some in the Builder tab</div>';

  body.innerHTML=`
    <div class="canvas-form-card fade-in">
      <div class="canvas-form-hdr">
        <h2>${esc(formState.title||'Untitled Form')}</h2>
        <p>${esc(formState.description||'Fill in the form below')}</p>
      </div>
      <div class="canvas-form-body">
        ${progressHtml}${fieldsHtml}${navHtml}
      </div>
    </div>`;
}

function pvNav(dir){
  const steps=window._pvSteps||[];
  const n=(window._pvStep||0)+dir;
  if(n<0||n>=steps.length) return;
  window._pvStep=n;
  renderPreviewCanvas(document.getElementById('canvas-body'));
}

function renderPreviewField(f){
  if(f.type==='page-break') return '';
  const req=f.required?'<span class="req">*</span>':'';
  const hint=f.hint?`<div class="prev-hint">${esc(f.hint)}</div>`:'';
  const ph=f.placeholder||'';
  const wCls=f.width==='half'?'pf-half':'pf-full';
  let input='';
  if(f.type==='textarea'){
    input=`<textarea class="prev-textarea" placeholder="${esc(ph)}" rows="3"></textarea>`;
  } else if(f.type==='select'){
    const opts=(f.options||[]).map(o=>`<option>${esc(o)}</option>`).join('');
    input=`<select class="prev-select"><option value="">Choose an option…</option>${opts}</select>`;
  } else if(f.type==='radio'||f.type==='checkbox'){
    const it=f.type==='radio'?'radio':'checkbox';
    input=`<div class="prev-opts">${(f.options||[]).map(o=>`<label class="prev-opt"><input type="${it}" name="prev_${f.id}"> <span>${esc(o)}</span></label>`).join('')}</div>`;
  } else if(f.type==='file'){
    input=`<div class="prev-file-zone"><svg width="24" height="24" viewBox="0 0 24 24" style="stroke:var(--muted2);fill:none;stroke-width:1.5;stroke-linecap:round;display:block;margin:0 auto 6px"><polyline points="16 16 12 12 8 16"/><line x1="12" y1="12" x2="12" y2="21"/><path d="M20.39 18.39A5 5 0 0 0 18 9h-1.26A8 8 0 1 0 3 16.3"/></svg>Click to upload or drag &amp; drop</div>`;
  } else if(f.type==='signature'||f.type==='type-to-sign'){
    input=`<div class="prev-sig-wrap">${f.type==='signature'?'Draw your signature':'Type your name to sign'}</div>`;
  } else {
    const it={email:'email',phone:'tel',number:'number',date:'date'}[f.type]||'text';
    input=`<input class="prev-input" type="${it}" placeholder="${esc(ph)}"/>`;
  }
  return`<div class="prev-field ${wCls}"><label class="prev-label">${esc(f.label||'Field')} ${req}</label>${input}${hint}</div>`;
}

// ─────────────────────────────────────────────────────────────
//  Settings tab
// ─────────────────────────────────────────────────────────────
function renderSettingsCanvas(body){
  const s=formState;
  const publicUrl=s.slug?`${BASE_URL}/form.php?s=${encodeURIComponent(s.slug)}`:null;
  const qrUrl=publicUrl?`https://api.qrserver.com/v1/create-qr-code/?data=${encodeURIComponent(publicUrl)}&size=200x200&bgcolor=ffffff&color=111217&margin=8&format=png`:null;
  const iframeSnippet=publicUrl?`<iframe src="${publicUrl}"\n  width="100%" height="650"\n  frameborder="0" style="border:none;border-radius:12px">\n</iframe>`:null;
  const jsSnippet=publicUrl?`<script>\n(function(){\n  var i=document.createElement('iframe');\n  i.src='${publicUrl}';\n  i.style.cssText='width:100%;border:none;min-height:500px;border-radius:12px';\n  i.title='${esc(s.title)}';\n  window.addEventListener('message',function(e){\n    if(e.data&&e.data.type==='fb-height')\n      i.style.height=e.data.height+'px';\n  });\n  document.currentScript.parentNode.insertBefore(i,document.currentScript);\n})();\n<\/script>`:null;

  body.innerHTML=`<div class="settings-panel fade-in">

    <div class="settings-section-title">Form Details</div>
    <div class="fg"><label>Description</label>
      <textarea id="s-desc" placeholder="Shown to visitors at the top of the form" oninput="formState.description=this.value;markDirty()">${esc(s.description||'')}</textarea>
    </div>
    <div class="fg"><label>Submit Button Label</label>
      <input id="s-label" value="${esc(s.submit_label||'Submit')}" oninput="formState.submit_label=this.value;markDirty()"/>
    </div>
    <div class="fg"><label>Success Redirect URL</label>
      <input id="s-url" type="url" value="${esc(s.success_url||'')}" placeholder="https://yoursite.com/thank-you" oninput="formState.success_url=this.value;markDirty()"/>
      <div class="hint">Leave blank to show a built-in success message</div>
    </div>
    <div class="fg"><label>Notification Email</label>
      <input id="s-notify" type="email" value="${esc(s.notify_email||'')}" placeholder="Override the default admin email" oninput="formState.notify_email=this.value;markDirty()"/>
    </div>
    <div class="fg"><label>Submission Limit <span style="font-weight:400;color:var(--muted2)">(0 = unlimited)</span></label>
      <input id="s-limit" type="number" min="0" value="${s.submission_limit||0}" placeholder="0" oninput="formState.submission_limit=parseInt(this.value)||0;markDirty()"/>
      <div class="hint">Form closes automatically when the limit is reached.</div>
    </div>

    <div class="settings-section-title">Access</div>
    <div class="toggle-row" style="margin-bottom:12px">
      <label class="toggle"><input type="checkbox" id="s-pub" ${s.is_public?'checked':''} onchange="formState.is_public=this.checked?1:0;document.getElementById('s-pw-row').style.display=this.checked?'none':'';markDirty()"><span class="toggle-slider"></span></label>
      <span class="toggle-label">Public form — no password required</span>
    </div>
    <div id="s-pw-row" style="${s.is_public?'display:none':''}">
      <div class="fg"><label>Form Password</label>
        <input id="s-pw" type="text" value="${esc(s.form_password||'')}" placeholder="Password visitors must enter" oninput="formState.form_password=this.value;markDirty()"/>
      </div>
    </div>
    <div class="toggle-row" style="margin-bottom:14px">
      <label class="toggle"><input type="checkbox" id="s-active" ${s.is_active?'checked':''} onchange="formState.is_active=this.checked?1:0;markDirty()"><span class="toggle-slider"></span></label>
      <span class="toggle-label">Form is active — accepting submissions</span>
    </div>

    ${publicUrl?`
    <div class="settings-section-title">Share</div>
    <div class="fg"><label>Public Form URL</label>
      <div class="url-row">
        <div class="url-display" id="pub-url-display" title="${publicUrl}">${publicUrl}</div>
        <button class="copy-btn" onclick="copyText('${esc(publicUrl)}','Copied URL!')">Copy</button>
        <a href="${publicUrl}" target="_blank" class="btn btn-ghost btn-sm" title="Open in new tab">
          <svg viewBox="0 0 24 24"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
        </a>
      </div>
    </div>
    <div class="fg"><label>QR Code</label>
      <div class="qr-wrap">
        <img src="${qrUrl}" alt="QR code" loading="lazy"/>
        <div style="flex:1;min-width:140px">
          <p style="font-size:12px;color:var(--muted);margin-bottom:10px;line-height:1.5">Scan to open the form. Print or display on signage, receipts, or presentations.</p>
          <a href="${qrUrl}&download=1" download="form-qr.png" class="btn btn-ghost btn-sm" style="margin-bottom:6px;display:inline-flex">
            <svg viewBox="0 0 24 24"><polyline points="8 17 12 21 16 17"/><line x1="12" y1="12" x2="12" y2="21"/><path d="M20.88 18.09A5 5 0 0 0 18 9h-1.26A8 8 0 1 0 3 16.83"/></svg>
            Download PNG
          </a>
          <button class="btn btn-ghost btn-sm" onclick="copyText('${esc(publicUrl)}','Link copied!')" style="display:inline-flex">
            <svg viewBox="0 0 24 24"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>
            Copy Link
          </button>
        </div>
      </div>
    </div>

    <div class="settings-section-title">Embed</div>
    <div class="fg">
      <label style="display:flex;align-items:center;justify-content:space-between">
        iFrame Snippet
        <button class="copy-btn" onclick="copyText(document.getElementById('embed-iframe').value,'iFrame code copied!')">Copy</button>
      </label>
      <textarea class="embed-code" id="embed-iframe" rows="4" readonly onclick="this.select()">${esc(iframeSnippet)}</textarea>
    </div>
    <div class="fg">
      <label style="display:flex;align-items:center;justify-content:space-between">
        JavaScript Snippet <span style="font-weight:400;color:var(--muted2);font-size:10px">(auto-resizing)</span>
        <button class="copy-btn" onclick="copyText(document.getElementById('embed-js').value,'JS snippet copied!')">Copy</button>
      </label>
      <textarea class="embed-code" id="embed-js" rows="8" readonly onclick="this.select()">${esc(jsSnippet)}</textarea>
    </div>
    `:
    `<div class="settings-section-title">Share & Embed</div>
    <div style="background:var(--ambp);border:1px solid #fde68a;border-radius:var(--r8);padding:12px 14px;font-size:13px;color:#92400e;display:flex;align-items:center;gap:8px">
      <svg viewBox="0 0 24 24" width="14" height="14" stroke="currentColor" fill="none" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
      Save the form first to get the public URL, QR code, and embed snippets.
    </div>`}

  </div>`;
}

function copyText(text,msg='Copied!'){
  navigator.clipboard.writeText(text).then(()=>toast(msg,'ok')).catch(()=>{
    const ta=document.createElement('textarea');ta.value=text;document.body.appendChild(ta);ta.select();document.execCommand('copy');ta.remove();toast(msg,'ok');
  });
}

// ─────────────────────────────────────────────────────────────
//  Tab switching
// ─────────────────────────────────────────────────────────────
function switchTab(tab){
  currentTab=tab;
  document.querySelectorAll('.ctab').forEach(t=>t.classList.remove('active'));
  document.getElementById('tab-'+tab).classList.add('active');
  renderCanvas();
  if(tab!=='builder') deselectField();
  // close mobile palette if open
  if(mobPaletteOpen){document.getElementById('palette-panel').style.cssText='';mobPaletteOpen=false;}
}

// ─────────────────────────────────────────────────────────────
//  Field CRUD
// ─────────────────────────────────────────────────────────────
function genId(){return 'f_'+Math.random().toString(36).slice(2,8);}
function getTypeLabel(t){
  const map={text:'Short Text',textarea:'Long Text',email:'Email',phone:'Phone',number:'Number',
    date:'Date',select:'Dropdown',radio:'Radio Buttons',checkbox:'Checkboxes',
    file:'File Upload',signature:'Draw Signature','type-to-sign':'Type-to-Sign',
    'page-break':'Page Break'};
  return map[t]||t;
}

function addFieldOfType(type){
  const field={
    id:genId(),type,
    label:type==='page-break'?'Next Step':getTypeLabel(type),
    placeholder:'',hint:'',required:false,
    options:['select','radio','checkbox'].includes(type)?['Option 1','Option 2','Option 3']:[],
    multiple:false,maxSizeMB:10,allowedTypes:[],condition:null,
    width:'full',
  };
  formState.fields.push(field);
  markDirty();
  if(currentTab!=='builder') switchTab('builder');
  else{refreshCanvasFields();initSortable();}
  selectField(field.id);
  setTimeout(()=>{const el=document.querySelector(`[data-fid="${field.id}"]`);if(el)el.scrollIntoView({behavior:'smooth',block:'nearest'});},50);
  toast(`${getTypeLabel(type)} added`,'ok');
  // close mobile palette
  if(mobPaletteOpen){document.getElementById('palette-panel').style.cssText='';mobPaletteOpen=false;}
}

function duplicateField(fid){
  const src=formState.fields.find(f=>f.id===fid);if(!src) return;
  const copy={...src,id:genId(),label:src.label+' (copy)'};
  const idx=formState.fields.findIndex(f=>f.id===fid);
  formState.fields.splice(idx+1,0,copy);
  markDirty();refreshCanvasFields();initSortable();selectField(copy.id);
  toast('Field duplicated','ok');
}

function removeField(fid){
  formState.fields=formState.fields.filter(f=>f.id!==fid);
  if(selectedFieldId===fid){selectedFieldId=null;renderInspectorEmpty();}
  markDirty();refreshCanvasFields();initSortable();
}

function showAddFieldMenu(){
  const el=document.getElementById('palette-panel');
  el.style.transition='background .3s';el.style.background='#f0f4ff';
  setTimeout(()=>el.style.background='',600);
}

// ─────────────────────────────────────────────────────────────
//  Inspector
// ─────────────────────────────────────────────────────────────
function setFieldWidth(fid,w){
  const f=formState.fields.find(x=>x.id===fid);if(!f) return;
  f.width=w;markDirty();refreshCanvasFields();renderInspector(fid);
  if(window.innerWidth<=700) syncMobInsp();
}

function selectField(fid){
  selectedFieldId=fid;refreshCanvasFields();renderInspector(fid);
  if(window.innerWidth<=700){
    document.getElementById('mob-props-btn').style.display='flex';
    openMobInspector();
  }
}
function deselectField(){
  selectedFieldId=null;
  document.querySelectorAll('.canvas-field').forEach(el=>el.classList.remove('selected'));
  renderInspectorEmpty();closeMobInsp();
  if(window.innerWidth<=700) document.getElementById('mob-props-btn').style.display='none';
}

function openMobInspector(){
  if(!selectedFieldId) return;
  syncMobInsp();
  document.getElementById('mob-insp-bd').classList.add('open');
  document.getElementById('mob-insp').classList.add('open');
}
function syncMobInsp(){
  const f=formState.fields.find(x=>x.id===selectedFieldId);
  if(!f) return;
  document.getElementById('mob-insp-title').textContent=getTypeLabel(f.type);
  document.getElementById('mob-insp-body').innerHTML=document.getElementById('inspector-body').innerHTML;
}
function closeMobInsp(){
  document.getElementById('mob-insp-bd').classList.remove('open');
  document.getElementById('mob-insp').classList.remove('open');
}

function renderInspectorEmpty(){
  document.getElementById('insp-hdr').textContent='Properties';
  document.getElementById('inspector-body').innerHTML=`<div class="insp-empty">
    <svg viewBox="0 0 24 24"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
    <p>Click a field to edit its properties</p>
  </div>`;
}

function renderInspector(fid){
  const f=formState.fields.find(x=>x.id===fid);
  if(!f){renderInspectorEmpty();return;}
  document.getElementById('insp-hdr').textContent=getTypeLabel(f.type);

  // Page-break has a simplified inspector
  if(f.type==='page-break'){
    document.getElementById('inspector-body').innerHTML=`<div class="insp-body">
      <div class="insp-section">
        <div class="insp-section-title">Step Label</div>
        <div class="fg"><label>Label</label>
          <input id="insp-label" value="${esc(f.label||'')}" oninput="updateField('${fid}','label',this.value)" placeholder="Next Step"/>
          <div class="hint">Shown on the Page Break divider and Next button</div>
        </div>
      </div>
      <div style="padding-top:4px">
        <button class="btn btn-danger btn-sm" style="width:100%;justify-content:center" onclick="removeField('${fid}')">
          <svg viewBox="0 0 24 24"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/></svg>
          Delete Step
        </button>
      </div>
    </div>`;
    return;
  }

  const hasOptions=['select','radio','checkbox'].includes(f.type);
  const hasPlaceholder=!['radio','checkbox','file','signature','type-to-sign'].includes(f.type);
  const isFile=f.type==='file';
  const others=formState.fields.filter(x=>x.id!==fid&&!['signature','type-to-sign','file'].includes(x.type));

  const optsHtml=hasOptions?`<div class="insp-section">
    <div class="insp-section-title">Options</div>
    <div class="opts-editor" id="opts-editor">${renderOpts(f)}</div>
    <button class="add-opt-btn" onclick="addOption('${fid}')">
      <svg viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
      Add option
    </button>
  </div>`:'';

  const condOtherOpts=others.length?others.map(x=>`<option value="${x.id}" ${f.condition?.if===x.id?'selected':''}>${esc(x.label)}</option>`).join(''):'';
  const condHtml=others.length?`<div class="insp-section">
    <div class="insp-section-title">Conditional Logic</div>
    <div class="toggle-row" style="margin-bottom:10px">
      <label class="toggle"><input type="checkbox" id="cond-on" ${f.condition?'checked':''} onchange="toggleCondition('${fid}',this.checked)"><span class="toggle-slider"></span></label>
      <span class="toggle-label" style="font-size:12px">Show/hide based on another field</span>
    </div>
    <div id="cond-editor" style="${f.condition?'':'display:none'}">
      <div class="cond-row">
        <div class="cond-label">Show this field when</div>
        <div style="display:flex;gap:6px;flex-wrap:wrap;align-items:center">
          <select id="cond-if" onchange="updateCondition('${fid}')">
            <option value="">— pick field —</option>${condOtherOpts}
          </select>
          <select id="cond-op" onchange="updateCondition('${fid}')">
            <option value="eq" ${f.condition?.op==='eq'?'selected':''}>equals</option>
            <option value="neq" ${f.condition?.op==='neq'?'selected':''}>does not equal</option>
            <option value="contains" ${f.condition?.op==='contains'?'selected':''}>contains</option>
            <option value="not_contains" ${f.condition?.op==='not_contains'?'selected':''}>does not contain</option>
            <option value="gt" ${f.condition?.op==='gt'?'selected':''}>is greater than</option>
            <option value="lt" ${f.condition?.op==='lt'?'selected':''}>is less than</option>
          </select>
        </div>
        <input type="text" id="cond-val" placeholder="value" value="${esc(f.condition?.val||'')}" oninput="updateCondition('${fid}')"/>
      </div>
    </div>
  </div>`:'';

  document.getElementById('inspector-body').innerHTML=`<div class="insp-body">
    <div class="insp-section">
      <div class="insp-section-title">Label & Display</div>
      <div class="fg"><label>Label</label>
        <input id="insp-label" value="${esc(f.label||'')}" oninput="updateField('${fid}','label',this.value)" placeholder="Field label"/>
      </div>
      ${hasPlaceholder?`<div class="fg"><label>Placeholder</label>
        <input id="insp-ph" value="${esc(f.placeholder||'')}" oninput="updateField('${fid}','placeholder',this.value)" placeholder="Optional placeholder text"/>
      </div>`:''}
      <div class="fg"><label>Hint Text</label>
        <input id="insp-hint" value="${esc(f.hint||'')}" oninput="updateField('${fid}','hint',this.value)" placeholder="Helper text shown below field"/>
      </div>
    </div>

    <div class="insp-section">
      <div class="insp-section-title">Validation</div>
      <div class="toggle-row">
        <label class="toggle"><input type="checkbox" id="insp-req" ${f.required?'checked':''} onchange="updateField('${fid}','required',this.checked)"><span class="toggle-slider"></span></label>
        <span class="toggle-label">Required field</span>
      </div>
      ${isFile?`<div class="fg" style="margin-top:10px"><label>Max File Size (MB)</label>
        <input type="number" id="insp-size" value="${f.maxSizeMB||10}" min="1" max="100" oninput="updateField('${fid}','maxSizeMB',+this.value)"/>
      </div>
      <div class="toggle-row">
        <label class="toggle"><input type="checkbox" id="insp-multi" ${f.multiple?'checked':''} onchange="updateField('${fid}','multiple',this.checked)"><span class="toggle-slider"></span></label>
        <span class="toggle-label">Allow multiple files</span>
      </div>`:''}
    </div>

    ${optsHtml}${condHtml}

    <div class="insp-section">
      <div class="insp-section-title">Layout Width</div>
      <div class="width-btns">
        <button class="wbtn${f.width!=='half'?' active':''}" onclick="setFieldWidth('${fid}','full')">
          <svg width="14" height="10" viewBox="0 0 14 10" style="display:block;margin:0 auto 3px"><rect x="0" y="0" width="14" height="10" rx="2" fill="currentColor"/></svg>
          Full Width
        </button>
        <button class="wbtn${f.width==='half'?' active':''}" onclick="setFieldWidth('${fid}','half')">
          <svg width="14" height="10" viewBox="0 0 14 10" style="display:block;margin:0 auto 3px"><rect x="0" y="0" width="6" height="10" rx="2" fill="currentColor"/><rect x="8" y="0" width="6" height="10" rx="2" fill="currentColor" opacity=".25"/></svg>
          Half Width
        </button>
      </div>
      <div class="hint" style="margin-top:6px">Half-width fields appear side-by-side in 2 columns</div>
    </div>

    <div class="insp-section">
      <div class="insp-section-title">Field ID</div>
      <div style="font-size:11px;color:var(--muted);font-family:var(--mono);background:var(--canvas);padding:7px 10px;border-radius:var(--r6);word-break:break-all">${f.id}</div>
    </div>

    <div style="padding-top:4px">
      <button class="btn btn-danger btn-sm" style="width:100%;justify-content:center" onclick="removeField('${fid}')">
        <svg viewBox="0 0 24 24"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/></svg>
        Delete Field
      </button>
    </div>
  </div>`;
}

function renderOpts(f){
  return(f.options||[]).map((o,i)=>`<div class="opt-row" data-oi="${i}">
    <input value="${esc(o)}" placeholder="Option ${i+1}" oninput="updateOption('${f.id}',${i},this.value)"/>
    <button class="opt-del" onclick="removeOption('${f.id}',${i})"><svg viewBox="0 0 24 24"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg></button>
  </div>`).join('');
}

function updateField(fid,key,val){
  const f=formState.fields.find(x=>x.id===fid);if(!f) return;
  f[key]=val;markDirty();
  const el=document.querySelector(`[data-fid="${fid}"]`);
  if(el){el.outerHTML=renderCanvasField(f);}
  document.querySelectorAll('.canvas-field').forEach(el=>{
    el.addEventListener('click',e=>{if(e.target.closest('.cf-btn')) return;selectField(el.dataset.fid);});
  });
}

function updateOption(fid,idx,val){const f=formState.fields.find(x=>x.id===fid);if(!f) return;f.options[idx]=val;markDirty();}
function addOption(fid){const f=formState.fields.find(x=>x.id===fid);if(!f) return;f.options.push('Option '+(f.options.length+1));markDirty();document.getElementById('opts-editor').innerHTML=renderOpts(f);}
function removeOption(fid,idx){const f=formState.fields.find(x=>x.id===fid);if(!f) return;f.options.splice(idx,1);markDirty();document.getElementById('opts-editor').innerHTML=renderOpts(f);}
function toggleCondition(fid,on){const f=formState.fields.find(x=>x.id===fid);if(!f) return;f.condition=on?{if:'',op:'eq',val:'',action:'show'}:null;document.getElementById('cond-editor').style.display=on?'':'none';markDirty();}
function updateCondition(fid){
  const f=formState.fields.find(x=>x.id===fid);if(!f) return;
  f.condition={if:document.getElementById('cond-if')?.value||'',op:document.getElementById('cond-op')?.value||'eq',val:document.getElementById('cond-val')?.value||'',action:'show'};
  markDirty();refreshCanvasFields();
}

// ─────────────────────────────────────────────────────────────
//  Save
// ─────────────────────────────────────────────────────────────
function markDirty(){dirty=true;setSaveStatus('dirty');clearTimeout(saveTimer);saveTimer=setTimeout(autoSave,3000);}

function setSaveStatus(state){
  const dot=document.getElementById('save-dot'),text=document.getElementById('save-text');
  dot.className='save-dot '+state;
  ({saved:'Saved',dirty:'Unsaved',saving:'Saving…',error:'Failed'}[state]||state);
  text.textContent={saved:'Saved',dirty:'Unsaved changes',saving:'Saving…',error:'Save failed'}[state]||state;
}

async function autoSave(){if(!dirty||!formState.id) return;await doSave(true);}

async function saveForm(){
  const title=document.getElementById('form-title').value.trim();
  if(!title){toast('Please enter a form title','err');document.getElementById('form-title').focus();return;}
  formState.title=title;
  await doSave(false);
}

async function doSave(silent){
  setSaveStatus('saving');
  const btn=document.getElementById('save-btn');btn.disabled=true;
  try{
    const payload={
      title:formState.title,description:formState.description,fields:formState.fields,
      is_public:formState.is_public,form_password:formState.form_password||null,
      submit_label:formState.submit_label||'Submit',success_url:formState.success_url||null,
      notify_email:formState.notify_email||null,
      submission_limit:formState.submission_limit>0?formState.submission_limit:null,
      is_active:formState.is_active??1,
    };
    let j;
    if(formState.id){
      j=await apiFetch('/forms/'+formState.id,'PUT',payload);
    } else {
      j=await apiFetch('/forms','POST',payload);
      if(j.ok){formState.id=j.id;if(j.slug)formState.slug=j.slug;history.replaceState({},'','?id='+j.id);}
    }
    if(j.ok){
      dirty=false;setSaveStatus('saved');
      if(!silent) toast('Form saved','ok');
      const h=document.querySelector('.canvas-form-hdr h2');if(h)h.textContent=formState.title||'Untitled Form';
      if(currentTab==='settings') renderCanvas();
    } else {
      setSaveStatus('error');if(!silent) toast(j.error||'Save failed','err');
    }
  } catch(e){
    setSaveStatus('error');if(!silent) toast('Network error','err');
  }
  btn.disabled=false;
}

// ─────────────────────────────────────────────────────────────
//  Duplicate current form
// ─────────────────────────────────────────────────────────────
async function duplicateCurrentForm(){
  if(!formState.id){toast('Save the form first','err');return;}
  const j=await apiFetch('/forms/'+formState.id+'/duplicate','POST');
  if(j.ok){
    if(confirm(`"${formState.title} (Copy)" created.\n\nOpen the duplicate now?`)){
      window.location.href='builder.php?id='+j.id;
    } else {
      toast('Duplicate created','ok');
    }
  } else {
    toast(j.error||'Duplicate failed','err');
  }
}

// ─────────────────────────────────────────────────────────────
//  Preview in new tab
// ─────────────────────────────────────────────────────────────
function previewForm(){
  if(!formState.slug){toast('Save the form first to preview it','err');return;}
  window.open(BASE_URL+'/form.php?s='+encodeURIComponent(formState.slug),'_blank');
}

// ─────────────────────────────────────────────────────────────
//  Helpers
// ─────────────────────────────────────────────────────────────
function esc(s){return String(s??'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;').replace(/'/g,'&#39;');}

let toastTimer;
function toast(msg,type=''){
  const el=document.getElementById('toast');
  el.textContent=msg;el.className='toast show '+(type||'');
  clearTimeout(toastTimer);toastTimer=setTimeout(()=>el.classList.remove('show'),2800);
}

// ─────────────────────────────────────────────────────────────
//  Boot
// ─────────────────────────────────────────────────────────────
init();
</script>
</body>
</html>
