<?php
require_once __DIR__ . '/config.php';
$googleClientId = defined('GOOGLE_CLIENT_ID') ? GOOGLE_CLIENT_ID : '';
$appUrl = rtrim(APP_URL, '/');
$googleError = htmlspecialchars($_GET['google_error'] ?? '', ENT_QUOTES);
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover"/>
<title>FormBuilder — Pro Admin</title>
<link rel="preconnect" href="https://fonts.googleapis.com"/>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin/>
<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&family=Syne:wght@700;800&display=swap" rel="stylesheet"/>
<style>
*{box-sizing:border-box;margin:0;padding:0}
:root{
  --nav:#ffffff;--nb:rgba(0,0,0,.08);--nh:#f3f4f6;--na:#e8f0ff;
  --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;
  --nw:220px;--th:52px;--mnh:60px;
  --font:'DM Sans',sans-serif;--mono:'DM Mono',monospace;
}
html,body{height:100%;font-family:var(--font);font-size:14px;line-height:1.5;overflow:hidden;background:var(--canvas)}
button{font-family:var(--font);cursor:pointer}
a{color:inherit;text-decoration:none}
input,select,textarea{font-family:var(--font)}

/* ── LOGIN ── */
#login-screen{
  position:fixed;inset:0;z-index:1000;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  background:#f4f5f7;
  padding:24px 16px;
  font-family:'DM Sans',sans-serif;
  -webkit-font-smoothing:antialiased;
}
.l-brand{display:flex;align-items:center;gap:10px;margin-bottom:28px}
.l-brand-logo{width:36px;height:36px;background:linear-gradient(135deg,#f5a623,#b87800);border-radius:10px;display:flex;align-items:center;justify-content:center;box-shadow:0 2px 8px rgba(224,149,0,.18)}
.l-brand-logo svg{width:16px;height:16px;stroke:#fff;fill:none;stroke-width:2.5;stroke-linecap:round;stroke-linejoin:round}
.l-brand-name{font-family:'Syne',sans-serif;font-size:16px;font-weight:800;color:#111827}
.lc{width:100%;max-width:420px}
.lcard{background:#fff;border-radius:20px;box-shadow:0 2px 8px rgba(0,0,0,.05),0 12px 40px rgba(0,0,0,.08);border:1px solid #e8eaed;overflow:hidden}
.lhdr{background:linear-gradient(135deg,#f5a623,#b87800);padding:24px 28px}
.lhdr-eye{font-family:'DM Mono',monospace;font-size:10px;font-weight:500;letter-spacing:.12em;text-transform:uppercase;color:rgba(255,255,255,.7);margin-bottom:4px}
.lhdr-title{font-family:'Syne',sans-serif;font-size:20px;font-weight:800;color:#fff}
.lhdr-sub{font-size:13px;color:rgba(255,255,255,.75);margin-top:2px}
.lbody{padding:28px}
.lf{margin-bottom:18px}
.lf-top{display:flex;justify-content:space-between;align-items:center;margin-bottom:6px}
.lf label{display:block;font-size:12px;font-weight:600;color:#4b5563;letter-spacing:.03em;margin-bottom:6px}
.lf input{display:block;width:100%;padding:11px 14px;border:1.5px solid #d1d5db;border-radius:10px;font-family:'DM Sans',sans-serif;font-size:14px;color:#111827;background:#fff;transition:border-color .15s,box-shadow .15s;outline:none;-webkit-appearance:none}
.lf input:focus{border-color:#e09500;box-shadow:0 0 0 3px rgba(224,149,0,.18)}
.pw-wrap{position:relative}
.pw-wrap input{padding-right:44px}
.pw-eye{position:absolute;right:12px;top:50%;transform:translateY(-50%);background:none;border:none;cursor:pointer;padding:4px;color:#9ca3af;display:flex;align-items:center;transition:color .15s}
.pw-eye:hover{color:#4b5563}
.pw-eye svg{width:18px;height:18px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.lf-forgot{font-size:12px;color:#e09500;font-weight:600;text-decoration:none}
.lf-forgot:hover{color:#b87800}
.lbtn{display:block;width:100%;padding:13px;background:#e09500;color:#fff;font-family:'DM Sans',sans-serif;font-size:14px;font-weight:700;border:none;border-radius:10px;cursor:pointer;transition:background .15s,box-shadow .15s;margin-top:4px}
.lbtn:hover{background:#f5a623;box-shadow:0 4px 12px rgba(224,149,0,.18)}
.lbtn:disabled{opacity:.6;cursor:not-allowed}
.l-divider{display:flex;align-items:center;gap:12px;margin:20px 0;color:#d1d5db;font-size:12px;color:#9ca3af}
.l-divider::before,.l-divider::after{content:'';flex:1;height:1px;background:#e8eaed}
.l-google{display:flex;align-items:center;justify-content:center;gap:10px;width:100%;padding:12px;background:#fff;color:#374151;font-family:'DM Sans',sans-serif;font-size:14px;font-weight:600;border:1.5px solid #d1d5db;border-radius:10px;cursor:pointer;transition:background .15s,border-color .15s,box-shadow .15s}
.l-google:hover{background:#f9fafb;border-color:#b0b8c4;box-shadow:0 1px 4px rgba(0,0,0,.07)}
.l-google svg{width:18px;height:18px;flex-shrink:0}
.lerr{padding:12px 14px;border-radius:10px;font-size:13px;margin-bottom:16px;display:none;background:#fef2f2;color:#dc2626;border:1px solid #fecaca}
.lerr.show{display:block}
.lfoot{text-align:center;margin-top:14px;font-size:11px;color:#9ca3af}

/* ── APP SHELL ── */
#app{display:none;height:100vh;overflow:hidden}
.shell{display:flex;height:100vh;overflow:hidden}

/* ── SIDEBAR ── */
.nav{width:var(--nw);flex-shrink:0;background:var(--nav);display:flex;flex-direction:column;height:100vh;overflow:hidden;z-index:95;transition:transform .26s cubic-bezier(.4,0,.2,1)}
.nav-logo{padding:16px 14px 13px;border-bottom:1px solid var(--nb);display:flex;align-items:center;gap:10px;flex-shrink:0}
.nav-mark{width:28px;height:28px;background:var(--accent);border-radius:7px;display:flex;align-items:center;justify-content:center;flex-shrink:0}
.nav-mark svg{width:14px;height:14px;stroke:#fff;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.nav-title{color:var(--ink)}
.nav-title{font-size:13px;font-weight:500}
.nav-plan{font-size:10px;color:var(--muted2)}
.nav-items{padding:8px 6px;flex:1;overflow-y:auto}
.nav-gl{font-size:10px;font-weight:500;color:var(--muted2);text-transform:uppercase;letter-spacing:.09em;padding:10px 8px 4px}
.ni{display:flex;align-items:center;gap:9px;padding:8px 10px;border-radius:var(--r8);color:var(--muted);font-size:13px;cursor:pointer;transition:all .15s;margin-bottom:1px}
.ni:hover{background:var(--nh);color:var(--ink)}
.ni.active{background:var(--na);color:var(--accent)}
.ni svg{width:15px;height:15px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;flex-shrink:0;opacity:.75}
.ni.active svg{opacity:1}
.ni-badge{margin-left:auto;font-size:10px;background:var(--accent);color:#fff;padding:2px 6px;border-radius:20px;font-weight:500;min-width:20px;text-align:center}
.nav-foot{padding:8px 6px 12px;border-top:1px solid var(--nb);flex-shrink:0}
.nav-user{display:flex;align-items:center;gap:9px;padding:8px 10px;border-radius:var(--r8);cursor:pointer;transition:background .15s;margin-bottom:4px}
.nav-user:hover{background:var(--nh)}
.nav-av{width:26px;height:26px;border-radius:50%;background:var(--accent);display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:500;color:#fff;flex-shrink:0}
.nav-un{font-size:12px;color:var(--ink)}
.nav-ur{font-size:10px;color:var(--muted2)}
.nav-out{display:flex;align-items:center;gap:7px;padding:7px 10px;border-radius:var(--r8);color:var(--muted);font-size:12px;cursor:pointer;transition:all .15s;border:none;background:none;width:100%}
.nav-out:hover{background:rgba(220,38,38,.08);color:var(--red)}
.nav-out svg{width:12px;height:12px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}

/* ── MAIN ── */
.main{flex:1;display:flex;flex-direction:column;overflow:hidden;min-width:0}
.topbar{height:var(--th);background:var(--white);border-bottom:1px solid var(--rule);display:flex;align-items:center;padding:0 20px;gap:10px;flex-shrink:0}
.tb-ham{display:none;width:30px;height:30px;align-items:center;justify-content:center;border:1px solid var(--rule2);border-radius:var(--r8);background:none;cursor:pointer;flex-shrink:0}
.tb-ham svg{width:15px;height:15px;stroke:var(--muted);fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.tb-title{font-size:14px;font-weight:500;color:var(--ink);flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.tb-search{display:flex;align-items:center;gap:7px;background:var(--canvas);border:1px solid var(--rule2);border-radius:var(--r8);padding:6px 10px;width:180px;flex-shrink:0}
.tb-search svg{width:13px;height:13px;stroke:var(--muted);fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;flex-shrink:0}
.tb-search input{border:none;background:transparent;font-size:12px;color:var(--ink);outline:none;width:100%;min-width:0}
.tb-search input::placeholder{color:var(--muted2)}
.kbd{font-size:10px;background:rgba(0,0,0,.06);border-radius:4px;padding:2px 5px;color:var(--muted);font-family:var(--mono)}
.tb-actions{display:flex;gap:6px;align-items:center;flex-shrink:0}
.content{flex:1;overflow-y:auto;padding:20px;padding-bottom:calc(var(--mnh) + 8px);scrollbar-width:thin;scrollbar-color:var(--rule2) transparent}
.content::-webkit-scrollbar{width:4px}
.content::-webkit-scrollbar-thumb{background:var(--rule2);border-radius:2px}

/* ── MOBILE BOTTOM NAV ── */
.mob-nav{display:none;position:fixed;bottom:0;left:0;right:0;height:var(--mnh);background:var(--white);border-top:1px solid var(--nb);z-index:90;padding-bottom:env(safe-area-inset-bottom)}
.mob-inner{display:flex;height:100%;align-items:stretch}
.mni{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;cursor:pointer;color:var(--muted2);font-size:8px;font-weight:400;border:none;background:none;padding:0;transition:color .15s}
.mni.active{color:var(--accent)}
.mni svg{width:20px;height:20px;stroke:currentColor;fill:none;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.nav-group-toggle{display:flex;align-items:center;justify-content:space-between;cursor:pointer}
.nav-group-toggle svg{width:12px;height:12px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;transition:transform .2s}
#membership-group-items.collapsed{display:none}
#booking-group-items.collapsed{display:none}
#content-group-items.collapsed{display:none}
.nov{display:none;position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:94;opacity:0;transition:opacity .25s;pointer-events:none}
.nov.open{opacity:1;pointer-events:all}

/* ── 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;font-family:var(--font);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-ghost{background:var(--white);color:var(--muted);border:1px solid var(--rule2)}
.btn-ghost:hover{border-color:#999;color:var(--ink)}
.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:6px;width:28px;height:28px;justify-content:center}
.btn-block{width:100%;justify-content:center}

/* ── CARDS ── */
.card{background:var(--white);border:1px solid var(--rule);border-radius:var(--r16);overflow:hidden;margin-bottom:14px}
.card-hdr{padding:13px 18px;border-bottom:1px solid var(--rule);display:flex;align-items:center;justify-content:space-between;gap:10px}
.card-hdr-title,.card-title{font-size:13px;font-weight:500;color:var(--ink)}
.card-body{padding:16px 18px}
.card-sub{font-size:11px;color:var(--muted);margin-top:2px}

/* ── STAT GRID ── */
.stat-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-bottom:18px}
.stat-card{background:var(--white);border:1px solid var(--rule);border-radius:var(--r12);padding:14px 16px;transition:box-shadow .15s;cursor:default}
.stat-card:hover{box-shadow:0 2px 12px rgba(0,0,0,.07)}
/* stat icon/num/lbl — support both old (.sc-*) and new (.stat-*) class names */
.sc-icon,.stat-icon{width:32px;height:32px;border-radius:var(--r8);background:var(--ap);display:flex;align-items:center;justify-content:center;margin-bottom:9px}
.sc-icon svg,.stat-icon svg{width:16px;height:16px;stroke:var(--accent);fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.sc-num,.stat-num{font-size:24px;font-weight:500;color:var(--ink);letter-spacing:-.02em;line-height:1}
.sc-lbl,.stat-lbl{font-size:11px;color:var(--muted);margin-top:3px}

/* ── PAGE HEADER ── */
.ph,.page-hd{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;gap:10px;flex-wrap:wrap}
.ph h2,.page-hd h1{font-size:16px;font-weight:500;color:var(--ink)}
.page-hd p{font-size:13px;color:var(--muted);flex-basis:100%;margin-top:2px}
.ph-actions{display:flex;gap:7px;align-items:center;flex-wrap:wrap}

/* ── TABLE ── */
.tbl-wrap,.tw{overflow-x:auto;-webkit-overflow-scrolling:touch}
table{width:100%;border-collapse:collapse;font-size:13px}
thead tr{border-bottom:1px solid var(--rule)}
th{padding:8px 13px;text-align:left;font-size:11px;font-weight:500;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;white-space:nowrap;background:var(--white)}
td{padding:10px 13px;border-bottom:1px solid var(--rule);color:var(--ink);vertical-align:middle}
tr:last-child td{border-bottom:none}
tr:hover td{background:#fafafa}
.tr-click{cursor:pointer}

/* ── BADGES ── */
.badge{display:inline-flex;align-items:center;padding:3px 8px;border-radius:20px;font-size:11px;font-weight:500;white-space:nowrap}
/* new template shorthand classes */
.bg{background:var(--gp);color:var(--green)}
.br{background:var(--rp);color:var(--red)}
.ba{background:var(--ambp);color:var(--amber)}
.bb{background:var(--ap);color:var(--accent)}
.bk{background:var(--canvas);color:var(--muted);border:1px solid var(--rule2)}
/* compat — used by existing render functions */
.badge-green{background:var(--gp);color:var(--green)}
.badge-yellow{background:var(--ambp);color:var(--amber)}
.badge-red{background:var(--rp);color:var(--red)}
.badge-blue{background:var(--ap);color:var(--accent)}
.badge-gray{background:var(--canvas);color:var(--muted);border:1px solid var(--rule2)}
.badge-amber{background:var(--ap);color:var(--accent)}

/* ── FILTER BAR ── */
.fb{display:flex;align-items:center;gap:7px;margin-bottom:14px;flex-wrap:wrap}
.fsel{padding:6px 10px;border-radius:var(--r8);border:1px solid var(--rule2);background:var(--white);font-size:12px;color:var(--ink);font-family:var(--font);outline:none}
.finput{padding:6px 11px;border-radius:var(--r8);border:1px solid var(--rule2);background:var(--white);font-size:12px;color:var(--ink);font-family:var(--font);outline:none;min-width:100px}
.fsel:focus,.finput:focus{border-color:var(--accent)}

/* ── FORM FIELDS ── */
.fg{margin-bottom:13px}
.fg label{display:block;font-size:11px;font-weight:500;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;margin-bottom:4px}
.fg input,.fg select,.fg textarea{width:100%;border:1px solid var(--rule2);border-radius:var(--r8);padding:8px 11px;font-size:13px;color:var(--ink);font-family:var(--font);outline:none;transition:border .15s;background:var(--white)}
.fg input:focus,.fg select:focus,.fg textarea:focus{border-color:var(--accent)}
.fg textarea{min-height:75px;resize:vertical}
.fg .hint{font-size:11px;color:var(--muted2);margin-top:3px}
.two-col,.grid-2{display:grid;grid-template-columns:1fr 1fr;gap:11px}

/* ── TOGGLE ── */
.toggle-row{display:flex;align-items:center;gap:9px;margin-bottom:10px}
.toggle{position:relative;width:34px;height:19px;flex-shrink:0}
.toggle input{opacity:0;width:0;height:0;position:absolute}
.tslider,.toggle-slider{position:absolute;inset:0;background:#d1d5db;border-radius:19px;cursor:pointer;transition:.2s}
.tslider:before,.toggle-slider:before{content:'';position:absolute;width:13px;height:13px;left:3px;top:3px;background:#fff;border-radius:50%;transition:.2s;box-shadow:0 1px 3px rgba(0,0,0,.2)}
.toggle input:checked+.tslider,.toggle input:checked+.toggle-slider{background:var(--accent)}
.toggle input:checked+.tslider:before,.toggle input:checked+.toggle-slider:before{transform:translateX(15px)}
.tlabel,.toggle-label{font-size:12px;font-weight:500;color:var(--ink)}

/* ── MODAL ── */
.ov{position:fixed;inset:0;background:rgba(0,0,0,.45);backdrop-filter:blur(3px);z-index:200;display:flex;align-items:center;justify-content:center;padding:16px;opacity:0;pointer-events:none;transition:opacity .2s}
.ov.open{opacity:1;pointer-events:all}
.modal{background:var(--white);border-radius:var(--r16);width:100%;max-width:560px;max-height:90vh;overflow:hidden;display:flex;flex-direction:column;transform:scale(.96) translateY(10px);transition:transform .22s cubic-bezier(.34,1.56,.64,1);box-shadow:0 24px 60px rgba(0,0,0,.18)}
.ov.open .modal{transform:none}
.mhdr{padding:16px 20px;border-bottom:1px solid var(--rule);display:flex;align-items:center;gap:10px;flex-shrink:0}
.mhdr h3{font-size:15px;font-weight:500;flex:1;color:var(--ink)}
.mclose{width:28px;height:28px;border-radius:var(--r6);border:1px solid var(--rule2);background:none;display:flex;align-items:center;justify-content:center;color:var(--muted);cursor:pointer;font-size:14px;flex-shrink:0;transition:all .15s}
.mclose:hover{background:var(--canvas);color:var(--ink)}
.mbody{flex:1;overflow-y:auto;padding:20px}
.mfoot{padding:14px 20px;border-top:1px solid var(--rule);display:flex;gap:8px;justify-content:flex-end;flex-shrink:0;background:var(--canvas)}

/* ── TOAST ── */
.toast{position:fixed;bottom:calc(var(--mnh) + 10px);left:50%;transform:translateX(-50%) translateY(16px);background:var(--ink);color:#fff;padding:8px 16px;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)}.toast.warn{background:var(--amber)}

/* ── LOADING / EMPTY STATES ── */
.spinner{display:inline-block;width:20px;height:20px;border:2px solid rgba(37,99,235,.15);border-top-color:var(--accent);border-radius:50%;animation:spin .7s linear infinite;vertical-align:middle}
@keyframes spin{to{transform:rotate(360deg)}}
.loading-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:80px 20px;gap:12px;color:var(--muted)}
.loading-state .spinner{width:26px;height:26px}
.empty{text-align:center;padding:60px 20px;color:var(--muted2)}
.empty-icon{width:52px;height:52px;border-radius:14px;background:var(--canvas);border:1px solid var(--rule);display:flex;align-items:center;justify-content:center;margin:0 auto 14px}
.empty-icon svg{width:22px;height:22px;stroke:var(--muted2);fill:none;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}
.empty h3{font-size:15px;font-weight:500;color:var(--muted);margin-bottom:6px}
.empty p{font-size:13px;color:var(--muted2);margin-bottom:18px;line-height:1.6;max-width:280px;margin-left:auto;margin-right:auto}

/* ── COMPAT UTILITIES ── */
.code-pill{font-family:var(--mono);font-size:11px;background:var(--ap);color:var(--accent);border:1px solid rgba(37,99,235,.2);padding:2px 8px;border-radius:6px}
.prog-bar{height:4px;background:var(--rule);border-radius:4px;overflow:hidden;margin-top:5px}
.prog-fill{height:100%;border-radius:4px;background:var(--accent)}
.quick-actions{display:flex;gap:10px;margin-bottom:16px;flex-wrap:wrap}
.qa-btn{flex:1;min-width:150px;background:var(--white);border:1px solid var(--rule);border-radius:var(--r12);padding:14px 16px;cursor:pointer;transition:all .15s;display:flex;align-items:center;gap:10px;text-align:left}
.qa-btn:hover{border-color:var(--accent);background:var(--ap)}
.qa-icon{width:34px;height:34px;border-radius:var(--r8);flex-shrink:0;background:var(--ap);display:flex;align-items:center;justify-content:center}
.qa-icon svg{width:15px;height:15px;stroke:var(--accent);fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.qa-title{font-size:13px;font-weight:500;color:var(--ink);margin-bottom:1px}
.qa-sub{font-size:11px;color:var(--muted)}
.sub-detail table{width:100%;border-collapse:collapse;font-size:13px}
.sub-detail td{padding:10px 0;border-bottom:1px solid var(--rule);vertical-align:top}
.sub-detail td:first-child{font-family:var(--mono);font-size:10px;font-weight:500;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;width:32%;padding-right:14px}
.sub-detail tr:last-child td{border-bottom:none}
.divider{border:none;border-top:1px solid var(--rule);margin:16px 0}

/* ── FORM INPUT (used in settings) ── */
.form-input{width:100%;border:1.5px solid var(--rule2);border-radius:var(--r8);padding:8px 11px;font-size:13px;color:var(--ink);font-family:var(--font);outline:none;transition:border .15s,box-shadow .15s;background:var(--white)}
.form-input:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--as)}
textarea.form-input{min-height:80px;resize:vertical}

/* ── SETTINGS LAYOUT ── */
.slayout{display:grid;grid-template-columns:160px 1fr;gap:20px}
.snav{border-right:1px solid var(--rule);padding-right:14px}
.sni{display:flex;align-items:center;gap:7px;padding:7px 10px;border-radius:var(--r8);font-size:12px;color:var(--muted);cursor:pointer;margin-bottom:2px;transition:all .15s}
.sni:hover{background:var(--canvas);color:var(--ink)}
.sni.active{background:var(--ap);color:var(--accent);font-weight:500}
.sni svg{width:13px;height:13px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;flex-shrink:0}
.srow{display:flex;align-items:center;justify-content:space-between;padding:11px 0;border-bottom:1px solid var(--rule);gap:10px}
.srow:last-child{border-bottom:none}
.stitle{font-size:11px;font-weight:500;text-transform:uppercase;letter-spacing:.07em;color:var(--muted);margin-bottom:10px;padding-bottom:7px;border-bottom:1px solid var(--rule)}
.notice{background:var(--ambp);border:1px solid #fde68a;border-radius:var(--r8);padding:9px 13px;font-size:12px;color:#92400e;margin-bottom:14px;display:flex;align-items:flex-start;gap:7px}
.notice svg{width:13px;height:13px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;flex-shrink:0;margin-top:1px}
.hgrid{display:grid;grid-template-columns:repeat(3,1fr);gap:7px;margin-top:10px}
.hi{background:var(--canvas);border-radius:var(--r8);padding:9px 11px}
.hdot{width:7px;height:7px;border-radius:50%;flex-shrink:0}
.hrow{display:flex;align-items:center;gap:6px}
.hlbl{font-size:11px;color:var(--muted)}
.hval{font-size:11px;font-weight:500;color:var(--ink);margin-top:2px}

/* ── SUBMISSION DETAIL PAGE ── */
.sd-meta{background:var(--white);border:1px solid var(--rule);border-radius:var(--r12);padding:16px 18px;margin-bottom:13px}
.sd-meta-row{display:flex;gap:24px;margin-top:10px;flex-wrap:wrap}
.sd-mi .lbl{font-size:11px;color:var(--muted)}
.sd-mi .val{font-size:12px;color:var(--ink);font-weight:500;margin-top:1px;font-family:var(--mono)}
.sd-grid{display:grid;grid-template-columns:1fr 280px;gap:13px}
.tl{margin-top:8px}
.tli{display:flex;gap:10px;padding-bottom:14px;position:relative}
.tli:not(:last-child):before{content:'';position:absolute;left:11px;top:24px;bottom:0;width:1px;background:var(--rule)}
.tldot{width:24px;height:24px;border-radius:50%;background:var(--ap);border:1.5px solid var(--accent);display:flex;align-items:center;justify-content:center;flex-shrink:0}
.tldot svg{width:11px;height:11px;stroke:var(--accent);fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.tlb{flex:1;padding-top:2px}
.tla{font-size:13px;font-weight:500;color:var(--ink)}
.tlt{font-size:11px;color:var(--muted);margin-top:1px;font-family:var(--mono)}
.fi-row{display:flex;align-items:center;gap:9px;padding:9px 11px;background:var(--canvas);border-radius:var(--r8);margin-bottom:6px}
.fi-ic{width:26px;height:26px;border-radius:var(--r6);background:var(--ap);display:flex;align-items:center;justify-content:center;flex-shrink:0}
.fi-ic svg{width:12px;height:12px;stroke:var(--accent);fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.fade-in{animation:fadeIn .32s ease both}
@keyframes fadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
.stagger>*{animation:fadeIn .36s ease both}
.stagger>*:nth-child(1){animation-delay:.04s}
.stagger>*:nth-child(2){animation-delay:.08s}
.stagger>*:nth-child(3){animation-delay:.12s}
.stagger>*:nth-child(4){animation-delay:.16s}

/* ── SUBMISSION AVATAR ── */
.sub-av{width:32px;height:32px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:600;color:#fff;flex-shrink:0;letter-spacing:.01em}

/* ── SUBMISSION TABLE (desktop) ── */
.sub-tbl{width:100%;border-collapse:collapse;font-size:13px}
.sub-tbl thead tr{border-bottom:1px solid var(--rule)}
.sub-tbl th{padding:8px 13px;text-align:left;font-size:10px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.07em;white-space:nowrap;background:var(--white)}
.sub-tbl td{padding:11px 13px;border-bottom:1px solid var(--rule);color:var(--ink);vertical-align:middle}
.sub-tbl tr:last-child td{border-bottom:none}
.sub-tbl tbody tr{cursor:pointer;transition:background .12s}
.sub-tbl tbody tr:hover td{background:#fafbff}
/* identity cell: avatar + name stacked */
.sub-id{display:flex;align-items:center;gap:10px}
.sub-id-txt .sub-nm{font-size:13px;font-weight:500;color:var(--ink)}
.sub-id-txt .sub-ref{font-size:10px;color:var(--muted2);font-family:var(--mono);margin-top:1px}

/* ── STATUS DOT ── */
.sdot{display:inline-flex;align-items:center;gap:5px;font-size:12px;font-weight:500}
.sdot:before{content:'';width:6px;height:6px;border-radius:50%;flex-shrink:0;background:currentColor;opacity:.7}
.sdot.ok{color:var(--green)}
.sdot.pend{color:var(--amber)}

/* ── MOBILE: transform table to card stack ── */
@media(max-width:768px){
  .sub-tbl{display:block}
  .sub-tbl thead{display:none}
  .sub-tbl tbody{display:flex;flex-direction:column;gap:8px}
  /* each row becomes a 2-row grid card */
  .sub-tbl tbody tr{
    display:grid;
    grid-template-areas:"av name stat" "av form date";
    grid-template-columns:40px 1fr auto;
    grid-template-rows:auto auto;
    column-gap:10px;
    row-gap:1px;
    background:var(--white);
    border:1px solid var(--rule);
    border-radius:var(--r12);
    padding:13px 14px;
    cursor:pointer;
    transition:box-shadow .15s,border-color .15s;
    align-items:center;
  }
  .sub-tbl tbody tr:hover{box-shadow:0 4px 16px rgba(0,0,0,.07);border-color:rgba(37,99,235,.2)}
  .sub-tbl td{padding:0;border:none!important;background:none!important}
  /* Avatar */
  .sub-tbl td.tc-av{grid-area:av;align-self:center}
  /* Name */
  .sub-tbl td.tc-name{grid-area:name;font-size:13px;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .sub-tbl td.tc-name .sub-id{flex-direction:column;align-items:flex-start;gap:1px}
  .sub-tbl td.tc-name .sub-id-txt .sub-nm{font-size:13px}
  .sub-tbl td.tc-name .sub-id-txt .sub-ref{display:none}/* hide ref on mobile */
  /* Form name */
  .sub-tbl td.tc-form{grid-area:form;font-size:11px;color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  /* Email status */
  .sub-tbl td.tc-status{grid-area:stat;text-align:right}
  /* Date */
  .sub-tbl td.tc-date{grid-area:date;font-size:10px;color:var(--muted2);text-align:right;white-space:nowrap}
  /* Hidden on mobile */
  .sub-tbl td.tc-pdf{display:none}
  .sub-tbl td.tc-actions{display:none}
}

/* ── FORMS TABLE ── */
.form-tbl{width:100%;border-collapse:collapse;font-size:13px}
.form-tbl thead tr{border-bottom:1px solid var(--rule)}
.form-tbl th{padding:8px 13px;text-align:left;font-size:10px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.07em;white-space:nowrap;background:var(--white)}
.form-tbl td{padding:10px 13px;border-bottom:1px solid var(--rule);color:var(--ink);vertical-align:middle}
.form-tbl tr:last-child td{border-bottom:none}
.form-tbl tbody tr{cursor:default;transition:background .12s}
.form-tbl tbody tr:hover td{background:#fafbff}

/* Mobile card layout for forms */
@media(max-width:768px){
  .form-tbl thead{display:none}
  .form-tbl tbody{display:flex;flex-direction:column;gap:8px}
  .form-tbl tbody tr{
    display:grid;
    grid-template-areas:"title title" "meta actions";
    grid-template-columns:1fr auto;
    background:var(--white);border:1px solid var(--rule);
    border-radius:var(--r12);padding:13px 14px;gap:6px 10px;
    transition:box-shadow .15s,border-color .15s;
  }
  .form-tbl tbody tr:hover{box-shadow:0 4px 16px rgba(0,0,0,.07);border-color:rgba(37,99,235,.2)}
  .form-tbl td{padding:0;border:none!important;background:none!important}
  .form-tbl td.ftc-title{grid-area:title}
  .form-tbl td.ftc-meta{grid-area:meta;display:flex;align-items:center;gap:6px;flex-wrap:wrap}
  .form-tbl td.ftc-actions{grid-area:actions;display:flex;align-items:flex-start;gap:4px;flex-shrink:0}
  /* hide individual columns that are embedded into meta */
  .form-tbl td.ftc-slug-col,.form-tbl td.ftc-vis,.form-tbl td.ftc-subs,.form-tbl td.ftc-status,.form-tbl td.ftc-created{display:none}
  /* show inline meta inside title cell */
  .ftc-meta-mob{display:flex!important;flex-wrap:wrap;align-items:center;gap:5px;margin-top:5px}
}

/* ── RESPONSIVE ── */
@media(min-width:769px){
  .toast{bottom:18px}
  .content{padding-bottom:20px}
}
@media(max-width:1100px){
  .stat-grid{grid-template-columns:repeat(2,1fr)}
  .tb-search{width:150px}
}
@media(max-width:768px){
  :root{--nw:240px}
  .nav{position:fixed;top:0;left:0;bottom:0;transform:translateX(-100%)}
  .nav.open{transform:translateX(0);box-shadow:8px 0 32px rgba(0,0,0,.25)}
  .nov{display:block}
  .shell{display:block;width:100%;height:100%}
  .main{width:100%;height:100vh;margin-left:0}
  .mob-nav{display:block}
  .content{padding:12px;padding-bottom:calc(var(--mnh) + 12px)}
  .topbar{padding:0 12px;gap:8px}
  .tb-ham{display:flex}
  .tb-search{display:none}
  .tb-title{font-size:13px}
  .stat-grid{grid-template-columns:1fr 1fr;gap:8px;margin-bottom:14px}
  .stat-card{padding:11px 13px}
  .sc-num,.stat-num{font-size:21px}
  .sc-icon,.stat-icon{width:30px;height:30px;margin-bottom:7px}
  .two-col,.grid-2{grid-template-columns:1fr}
  .ov{align-items:flex-end;padding:0}
  .modal{border-radius:var(--r16) var(--r16) 0 0;max-width:100%;max-height:92vh}
  .quick-actions{flex-direction:column}
  .card-body{padding:13px}
  .slayout{grid-template-columns:1fr;gap:0}
  .snav{border-right:none;border-bottom:1px solid var(--rule);padding-right:0;padding-bottom:10px;margin-bottom:14px;display:flex;flex-wrap:wrap;gap:4px}
  .sd-grid{grid-template-columns:1fr}
}
@media(max-width:400px){
  .sc-num,.stat-num{font-size:19px}
  .sc-lbl,.stat-lbl{font-size:10px}
}
</style>
</head>
<body>

<!-- LOGIN -->
<div id="login-screen">
  <div class="l-brand">
    <div class="l-brand-logo"><svg viewBox="0 0 24 24"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg></div>
    <span class="l-brand-name">FormBuilder</span>
  </div>
  <div class="lc">
    <div class="lcard">
      <div class="lhdr">
        <div class="lhdr-eye">Admin Portal</div>
        <div class="lhdr-title">Welcome back</div>
        <div class="lhdr-sub">Sign in to manage your forms, submissions, and settings.</div>
      </div>
      <div class="lbody">
        <div class="lerr" id="lerr"></div>
        <div class="lf">
          <label for="lemail">Email address</label>
          <input type="email" id="lemail" placeholder="you@example.com" autocomplete="email"/>
        </div>
        <div class="lf">
          <div class="lf-top">
            <label for="lpw">Password</label>
            <a href="admin-forgot.php" class="lf-forgot">Forgot password?</a>
          </div>
          <div class="pw-wrap">
            <input type="password" id="lpw" placeholder="••••••••" autocomplete="current-password"/>
            <button type="button" class="pw-eye" id="pw-eye" onclick="togglePw()" aria-label="Toggle password visibility">
              <svg id="eye-icon" 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>
            </button>
          </div>
        </div>
        <button class="lbtn" id="lbtn" onclick="doLogin()">Sign In</button>
        <div class="l-divider">or</div>
        <button type="button" class="l-google" id="google-login-btn" onclick="googleLogin()">
          <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
            <path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" fill="#4285F4"/>
            <path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill="#34A853"/>
            <path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l3.66-2.84z" fill="#FBBC05"/>
            <path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/>
          </svg>
          Sign in with Google
        </button>
      </div>
    </div>
    <div class="lfoot">Powered by FormBuilder Pro</div>
  </div>
</div>

<!-- APP -->
<div id="app">
<div class="nov" id="nov" onclick="closeSidebar()"></div>
<div class="shell">

  <!-- SIDEBAR -->
  <nav class="nav" id="sidebar">
    <div class="nav-logo">
      <div class="nav-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>
      <div><div class="nav-title">FormBuilder</div><div class="nav-plan">Pro Admin</div></div>
    </div>
    <div class="nav-items">
      <div class="nav-gl">Overview</div>
      <div class="ni active" data-page="dashboard" onclick="nav(this,'dashboard')">
        <svg viewBox="0 0 24 24"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/></svg>Dashboard
      </div>
      <div class="nav-gl nav-group-toggle" id="content-group-toggle" onclick="toggleContentNavGroup()">
        <span>Content</span>
        <svg id="content-group-chevron" viewBox="0 0 24 24"><polyline points="9 6 15 12 9 18"/></svg>
      </div>
      <div id="content-group-items">
        <div class="ni" data-page="forms" onclick="nav(this,'forms')">
          <svg viewBox="0 0 24 24"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg>Forms
          <span class="ni-badge" id="nav-forms-count" style="display:none">0</span>
        </div>
        <div class="ni" data-page="submissions" onclick="nav(this,'submissions')">
          <svg viewBox="0 0 24 24"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>Submissions
        </div>
        <div class="ni" id="nav-form-builder" onclick="openFormBuilder()">
          <svg viewBox="0 0 24 24"><path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"/></svg>Form Builder
        </div>
      </div>
      <div class="nav-gl nav-group-toggle" id="booking-group-toggle" onclick="toggleBookingNavGroup()">
        <span>Booking</span>
        <svg id="booking-group-chevron" viewBox="0 0 24 24"><polyline points="9 6 15 12 9 18"/></svg>
      </div>
      <div id="booking-group-items">
        <div class="ni" data-page="booking" onclick="nav(this,'booking')">
          <svg viewBox="0 0 24 24"><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"/></svg>Appointments
          <span class="ni-badge" id="nav-appt-count" style="display:none">0</span>
        </div>
        <div class="ni" data-page="booking-customers" onclick="nav(this,'booking-customers')">
          <svg viewBox="0 0 24 24"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>Customers
        </div>
        <div class="ni" data-page="booking-memberships" onclick="nav(this,'booking-memberships')">
          <svg viewBox="0 0 24 24"><path d="M4 7h16v10H4z"/><path d="M8 7V5h8v2"/></svg>Memberships
        </div>
        <div class="ni" data-page="booking-services" onclick="nav(this,'booking-services')">
          <svg viewBox="0 0 24 24"><circle cx="6" cy="12" r="2"/><circle cx="12" cy="12" r="2"/><circle cx="18" cy="12" r="2"/></svg>Services
        </div>
        <div class="ni" data-page="booking-providers" onclick="nav(this,'booking-providers')">
          <svg viewBox="0 0 24 24"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>Providers
        </div>
        <div class="ni" data-page="booking-promo-codes" onclick="nav(this,'booking-promo-codes')">
          <svg viewBox="0 0 24 24"><path d="M20.59 13.41 11 3.83a2 2 0 0 0-2.83 0l-4.34 4.34a2 2 0 0 0 0 2.83l9.58 9.59a2 2 0 0 0 2.83 0l4.35-4.35a2 2 0 0 0 0-2.83z"/></svg>Promo Codes
        </div>
        <div class="ni" data-page="booking-waitlist" onclick="nav(this,'booking-waitlist')">
          <svg viewBox="0 0 24 24"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>Waitlist
        </div>
        <div class="ni" data-page="booking-ratings" onclick="nav(this,'booking-ratings')">
          <svg viewBox="0 0 24 24"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>Ratings
        </div>
      </div>
      <div class="nav-gl nav-group-toggle" id="membership-group-toggle" onclick="toggleMembershipNavGroup()">
        <span>Membership</span>
        <svg id="membership-group-chevron" viewBox="0 0 24 24"><polyline points="9 6 15 12 9 18"/></svg>
      </div>
      <div id="membership-group-items">
        <div class="ni" data-page="membership-plans" onclick="nav(this,'membership-plans')">
          <svg viewBox="0 0 24 24"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>Plans
        </div>
        <div class="ni" data-page="membership-payments" onclick="nav(this,'membership-payments')">
          <svg viewBox="0 0 24 24"><rect x="1" y="4" width="22" height="16" rx="2"/><line x1="1" y1="10" x2="23" y2="10"/></svg>Payments
        </div>
        <div class="ni" data-page="membership-history" onclick="nav(this,'membership-history')">
          <svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>History
        </div>
        <div class="ni" data-page="membership-ledger" onclick="nav(this,'membership-ledger')">
          <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>Session Ledger
        </div>
      </div>
      <div class="nav-gl">System</div>
      <div class="ni" data-page="modules" onclick="nav(this,'modules')">
        <svg viewBox="0 0 24 24"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></svg>Modules
      </div>
      <div class="ni" data-page="settings" onclick="nav(this,'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
      </div>
    </div>
    <div class="nav-foot">
      <div class="nav-user">
        <div class="nav-av" id="nav-av">AD</div>
        <div><div class="nav-un" id="nav-un">Admin</div><div class="nav-ur" id="nav-ur">Admin</div></div>
      </div>
      <button class="nav-out" onclick="doLogout()">
        <svg viewBox="0 0 24 24"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>Sign out
      </button>
    </div>
  </nav>

  <!-- MAIN -->
  <div class="main">
    <div class="topbar">
      <button class="tb-ham" onclick="openSidebar()">
        <svg viewBox="0 0 24 24"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg>
      </button>
      <div class="tb-title" id="pgtitle">Dashboard</div>
      <div class="tb-search">
        <svg viewBox="0 0 24 24"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
        <input placeholder="Search…"/><span class="kbd">/</span>
      </div>
      <div class="tb-actions">
        <button class="btn btn-primary btn-sm" onclick="openFormBuilder()">
          <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>New Form
        </button>
      </div>
    </div>
    <div class="content" id="content-area">
      <div class="loading-state"><div class="spinner"></div><span>Loading…</span></div>
    </div>
  </div>

</div><!-- /shell -->

<!-- MOBILE NAV -->
<nav class="mob-nav">
  <div class="mob-inner">
    <button class="mni active" data-page="dashboard" onclick="nav(this,'dashboard')">
      <svg viewBox="0 0 24 24"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/></svg>
      <span>Dashboard</span>
    </button>
    <button class="mni" data-page="forms" onclick="nav(this,'forms')">
      <svg viewBox="0 0 24 24"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
      <span>Forms</span>
    </button>
    <button class="mni" data-page="submissions" onclick="nav(this,'submissions')">
      <svg viewBox="0 0 24 24"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>
      <span>Inbox</span>
    </button>
    <button class="mni" id="mob-form-builder" onclick="openFormBuilder()">
      <svg viewBox="0 0 24 24"><path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"/></svg>
      <span>Builder</span>
    </button>
    <button class="mni" data-page="booking" onclick="nav(this,'booking')">
      <svg viewBox="0 0 24 24"><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"/></svg>
      <span>Booking</span>
    </button>
    <button class="mni" data-page="membership-plans" onclick="nav(this,'membership-plans')">
      <svg viewBox="0 0 24 24"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
      <span>Plans</span>
    </button>
    <button class="mni" data-page="modules" onclick="nav(this,'modules')">
      <svg viewBox="0 0 24 24"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></svg>
      <span>Modules</span>
    </button>
    <button class="mni" data-page="settings" onclick="nav(this,'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>
      <span>Settings</span>
    </button>
  </div>
</nav>

</div><!-- /app -->

<!-- MODAL -->
<div class="ov" id="modal-overlay" onclick="if(event.target===this)closeModal()">
  <div class="modal">
    <div class="mhdr">
      <h3 id="modal-title">Modal</h3>
      <button class="mclose" onclick="closeModal()">✕</button>
    </div>
    <div class="mbody" id="modal-body"></div>
    <div class="mfoot" id="modal-foot"></div>
  </div>
</div>

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

<script>
/* ── SIDEBAR HELPERS ── */
function openSidebar(){
  document.getElementById('sidebar').classList.add('open');
  const ov=document.getElementById('nov');ov.style.display='block';
  requestAnimationFrame(()=>ov.classList.add('open'));
}
function closeSidebar(){
  document.getElementById('sidebar').classList.remove('open');
  const ov=document.getElementById('nov');ov.classList.remove('open');
  setTimeout(()=>{if(!ov.classList.contains('open'))ov.style.display='none';},270);
}

function toggleMembershipNavGroup(forceCollapsed){
  const group=q('#membership-group-items');
  const chev=q('#membership-group-chevron');
  if(!group) return;
  const collapsed = (typeof forceCollapsed==='boolean') ? forceCollapsed : !group.classList.contains('collapsed');
  group.classList.toggle('collapsed', collapsed);
  if(chev) chev.style.transform = collapsed ? 'rotate(0deg)' : 'rotate(90deg)';
  try{ localStorage.setItem('fb_membership_nav_collapsed', collapsed ? '1' : '0'); }catch(e){}
}
function initMembershipNavGroup(){
  try{
    const collapsed = localStorage.getItem('fb_membership_nav_collapsed') === '1';
    toggleMembershipNavGroup(collapsed);
  }catch(e){ toggleMembershipNavGroup(false); }
}
function toggleContentNavGroup(forceCollapsed){
  const group=q('#content-group-items');
  const chev=q('#content-group-chevron');
  if(!group) return;
  const collapsed = (typeof forceCollapsed==='boolean') ? forceCollapsed : !group.classList.contains('collapsed');
  group.classList.toggle('collapsed', collapsed);
  if(chev) chev.style.transform = collapsed ? 'rotate(0deg)' : 'rotate(90deg)';
  try{ localStorage.setItem('fb_content_nav_collapsed', collapsed ? '1' : '0'); }catch(e){}
}
function initContentNavGroup(){
  try{
    const collapsed = localStorage.getItem('fb_content_nav_collapsed') === '1';
    toggleContentNavGroup(collapsed);
  }catch(e){ toggleContentNavGroup(false); }
}
function toggleBookingNavGroup(forceCollapsed){
  const group=q('#booking-group-items');
  const chev=q('#booking-group-chevron');
  if(!group) return;
  const collapsed = (typeof forceCollapsed==='boolean') ? forceCollapsed : !group.classList.contains('collapsed');
  group.classList.toggle('collapsed', collapsed);
  if(chev) chev.style.transform = collapsed ? 'rotate(0deg)' : 'rotate(90deg)';
  try{ localStorage.setItem('fb_booking_nav_collapsed', collapsed ? '1' : '0'); }catch(e){}
}
function initBookingNavGroup(){
  try{
    const collapsed = localStorage.getItem('fb_booking_nav_collapsed') === '1';
    toggleBookingNavGroup(collapsed);
  }catch(e){ toggleBookingNavGroup(false); }
}

/* ── CORE ── */
const BASE=(()=>{const p=window.location.pathname;const d=p.endsWith('/')?p.slice(0,-1):p.substring(0,p.lastIndexOf('/'));return d.replace(/\/admin(\.php)?$/,'');})();
const API=BASE+'/api';
const GOOGLE_CLIENT_ID=<?= json_encode($googleClientId) ?>;
const APP_URL=<?= json_encode($appUrl) ?>;
let currentPage='dashboard',allForms=[],subFormId=null,subPage=1,unreadCount=0;
let bookingModuleState={forms:true,bookings:true,membership:true,qr_checkin:false};
let adminUserRole='admin';

// Show any Google OAuth error passed back via redirect
<?php if ($googleError): ?>
(function(){ const e=document.getElementById('lerr'); if(e){ e.textContent=<?= json_encode($googleError) ?>; e.classList.add('show'); } })();
<?php endif; ?>

const esc=s=>String(s??'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
const q=sel=>document.querySelector(sel);
const fmt=dt=>dt?new Date(dt).toLocaleString('en-US',{month:'short',day:'numeric',year:'numeric',hour:'2-digit',minute:'2-digit'}):'—';
const fmtShort=dt=>dt?new Date(dt).toLocaleDateString('en-US',{month:'short',day:'numeric'}):'—';
const AVCOLORS=['#4f46e5','#0891b2','#059669','#d97706','#7c3aed','#0284c7','#b45309','#be185d','#0f766e','#1d4ed8'];
function avColor(s){let h=0;for(let i=0;i<s.length;i++)h=(h*31+s.charCodeAt(i))&0xffffff;return AVCOLORS[Math.abs(h)%AVCOLORS.length];}
function avInit(name){
  if(!name)return'?';
  if(name.includes('@'))return name[0].toUpperCase();
  if(/^#[A-Z0-9]{4,}$/i.test(name))return'#';
  const p=name.trim().split(/\s+/);
  return p.length>=2?(p[0][0]+p[p.length-1][0]).toUpperCase():name[0].toUpperCase();
}
function avHtml(name){return`<div class="sub-av" style="background:${avColor(name)}">${avInit(name)}</div>`;}

/* ── TOAST ── */
function toast(msg,type=''){
  const t=q('#toast');
  t.textContent=msg;
  t.className='toast show '+(type||'');
  clearTimeout(t._t);t._t=setTimeout(()=>t.className='toast',3200);
}

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

/* ── AUTH ── */
async function checkAuth(){
  const j=await api('/auth/me');
  if(j.authed){ populateUser(j.user); showApp(); }
}

function populateUser(u){
  if(!u) return;
  const name = u.name || 'Admin';
  const role = u.role || 'admin';
  adminUserRole = role;
  const av   = name.substring(0,2).toUpperCase();
  const el_av = q('#nav-av'), el_un = q('#nav-un'), el_ur = q('#nav-ur');
  if(el_av) el_av.textContent = av;
  if(el_un) el_un.textContent = name;
  if(el_ur) el_ur.textContent = role.charAt(0).toUpperCase() + role.slice(1);
}

document.getElementById('lemail').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('lpw').focus();});
document.getElementById('lpw').addEventListener('keydown',e=>{if(e.key==='Enter')doLogin();});

function togglePw(){
  const inp=q('#lpw'), btn=q('#pw-eye'), icon=q('#eye-icon');
  const show=inp.type==='password';
  inp.type=show?'text':'password';
  // eye-off icon when password is visible, eye icon when hidden
  icon.innerHTML=show
    ?'<path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94"/><path d="M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19"/><line x1="1" y1="1" x2="23" y2="23"/>'
    :'<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"/>';
  btn.setAttribute('aria-label', show ? 'Hide password' : 'Show password');
}

function googleLogin(){
  if(!GOOGLE_CLIENT_ID){
    const err=q('#lerr');
    err.textContent='Google sign-in is not configured. Set GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET in config.php.';
    err.classList.add('show');
    return;
  }
  const redirectUri=APP_URL+'/api/auth-google.php?admin=1';
  const params=new URLSearchParams({
    client_id:GOOGLE_CLIENT_ID,
    redirect_uri:redirectUri,
    response_type:'code',
    scope:'openid email profile',
    access_type:'offline',
    prompt:'select_account'
  });
  window.location.href='https://accounts.google.com/o/oauth2/v2/auth?'+params;
}

if(!GOOGLE_CLIENT_ID){
  const gbtn=q('#google-login-btn');
  if(gbtn){
    gbtn.disabled=true;
    gbtn.style.opacity='0.6';
    gbtn.style.cursor='not-allowed';
    gbtn.title='Google sign-in is not configured';
  }
}

async function doLogin(){
  const email=q('#lemail').value.trim(), pw=q('#lpw').value;
  const err=q('#lerr');
  err.className='lerr';
  if(!email||!pw){err.textContent='Please enter your email and password.';err.classList.add('show');return;}
  const btn=q('#lbtn');btn.textContent='Signing in…';btn.disabled=true;
  const j=await api('/auth/login','POST',{email,password:pw});
  btn.textContent='Sign In';btn.disabled=false;
  if(j.ok){ populateUser(j.user); showApp(); }
  else{err.textContent=j.error||'Invalid email or password.';err.classList.add('show');}
}

async function doLogout(){await api('/auth/logout','POST');location.reload();}

function showApp(){
  q('#login-screen').style.display='none';
  q('#app').style.display='block';
  initContentNavGroup();
  initMembershipNavGroup();
  initBookingNavGroup();
  syncModuleNavigation();
  nav(null,'dashboard');
}

/* ── UNREAD BADGE ── */
function updateUnreadBadge(){
  let b=q('#nav-unread-badge');
  if(unreadCount>0){
    if(!b){
      const ni=document.querySelector('.ni[data-page="submissions"] .ni-badge')||null;
      // inject badge into submissions nav item
      const sni=document.querySelector('.ni[data-page="submissions"]');
      if(sni){b=document.createElement('span');b.id='nav-unread-badge';b.className='ni-badge';b.style.background='var(--red)';sni.appendChild(b);}
    }
    if(b){b.textContent=unreadCount>99?'99+':unreadCount;b.style.display='';}
    // mobile nav dot
    let md=q('#mob-unread-dot');
    const mni=document.querySelector('.mni[data-page="submissions"]');
    if(mni&&!md){md=document.createElement('span');md.id='mob-unread-dot';md.style.cssText='position:absolute;top:6px;right:calc(50% - 14px);width:7px;height:7px;border-radius:50%;background:var(--red);';mni.style.position='relative';mni.appendChild(md);}
  } else {
    if(b)b.style.display='none';
    const md=q('#mob-unread-dot');if(md)md.style.display='none';
  }
}
async function markAllRead(){
  await api('/submissions/mark-all-read','POST');
  unreadCount=0;updateUnreadBadge();toast('All marked as read','ok');
}

/* ── NAV ──
   Accepts both nav(el, page) from HTML onclick and nav(page) from JS */
function nav(elOrPage,pageOrEl){
  const page=typeof elOrPage==='string'?elOrPage:(pageOrEl||'dashboard');
  currentPage=page;
  document.querySelectorAll('.ni,.mni').forEach(n=>n.classList.toggle('active',n.dataset.page===page));
  const titles={
    dashboard:'Dashboard',
    forms:'Forms',
    submissions:'Submissions',
    settings:'Settings',
    booking:'Booking',
    'booking-customers':'Booking Customers',
    'booking-memberships':'Booking Memberships',
    'booking-services':'Booking Services',
    'booking-providers':'Booking Providers',
    'booking-promo-codes':'Booking Promo Codes',
    'booking-waitlist':'Booking Waitlist',
    'booking-ratings':'Booking Ratings',
    'membership-plans':'Membership Plans',
    'membership-payments':'Membership Payments',
    'membership-history':'Membership History',
    'membership-ledger':'Session Ledger',
    modules:'Modules'
  };
  q('#pgtitle').textContent=titles[page]||page;
  closeSidebar();
  ({
    dashboard:renderDashboard,
    forms:renderForms,
    submissions:renderSubmissions,
    settings:renderSettings,
    booking:renderBooking,
    'booking-customers':renderBookingCustomers,
    'booking-memberships':renderBookingMemberships,
    'booking-services':renderBookingServices,
    'booking-providers':renderBookingProviders,
    'booking-promo-codes':renderBookingPromoCodes,
    'booking-waitlist':renderBookingWaitlist,
    'booking-ratings':renderBookingRatings,
    'membership-plans':renderMembershipPlans,
    'membership-payments':renderMembershipPayments,
    'membership-history':renderMembershipHistory,
    'membership-ledger':renderMembershipLedger,
    modules:renderModules
  }[page]||renderDashboard)();
}

/* ── MODULES ── */
async function renderModules(){
  const BK=BASE+'/api/booking';
  q('#content-area').innerHTML='<div class="loading-state"><div class="spinner"></div><span>Loading…</span></div>';
  let mods={forms:true,bookings:true,membership:true,qr_checkin:false};
  try{
    const j=await fetch(BK+'/admin/modules',{credentials:'same-origin'}).then(r=>r.json());
    if(j.ok && j.modules) mods=j.modules;
  }catch(e){}

  const row=(k,label,desc)=>`
    <div style="display:flex;align-items:flex-start;gap:14px;padding:14px 16px;border:1px solid var(--rule);border-radius:var(--r12);background:#fff">
      <div style="flex:1;min-width:0">
        <div style="font-weight:600;color:var(--ink);margin-bottom:2px">${label}</div>
        <div style="font-size:12px;color:var(--muted)">${desc}</div>
      </div>
      <label style="display:flex;align-items:center;gap:8px;user-select:none">
        <input type="checkbox" id="mod-${k}" ${mods[k]?'checked':''} style="width:18px;height:18px"/>
        <span style="font-size:12px;color:var(--muted2)">${mods[k]?'On':'Off'}</span>
      </label>
    </div>`;

  q('#content-area').innerHTML=`
    <div class="page-hd fade-in">
      <div>
        <h1>Modules</h1>
        <p>Enable or disable features for this business.</p>
      </div>
      <div class="ph-actions">
        <button class="btn btn-primary btn-sm" onclick="saveModules()">
          <svg viewBox="0 0 24 24"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><polyline points="17 21 17 13 7 13 7 21"/><polyline points="7 3 7 8 15 8"/></svg>
          Save
        </button>
      </div>
    </div>

    <div class="card fade-in">
      <div class="card-hdr"><div><div class="card-title">Feature toggles</div><div class="card-sub">These take effect immediately.</div></div></div>
      <div class="card-body" style="display:flex;flex-direction:column;gap:10px">
        ${row('forms','Forms','Public forms + submissions + admin form builder.')}
        ${row('bookings','Bookings / Appointments','Public booking page + booking APIs.')}
        ${row('membership','Membership enforcement','If off, anyone can book restricted services.')}
        ${row('qr_checkin','QR check-in tool','Enables QR scan logging in the admin dashboard.')}
        <div style="font-size:12px;color:var(--muted);padding-top:4px">
          Tip: For future multi-tenant support, these toggles will become tenant-scoped without changing the UI.
        </div>
      </div>
    </div>

    <div class="card fade-in">
      <div class="card-hdr"><div><div class="card-title">Scan Pass</div><div class="card-sub">Scan or paste a member QR token to log attendance.</div></div></div>
      <div class="card-body">
        <div style="display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-bottom:10px">
          <button class="btn btn-ghost btn-sm" onclick="startScan()">Start camera</button>
          <button class="btn btn-ghost btn-sm" onclick="stopScan()">Stop</button>
          <input id="qrTokenInput" placeholder="Paste QR token…" style="flex:1;min-width:220px;padding:10px 12px;border-radius:10px;border:1px solid var(--rule2)"/>
          <input id="apptRefInput" placeholder="Appointment Ref (optional)" style="width:200px;min-width:180px;padding:10px 12px;border-radius:10px;border:1px solid var(--rule2)"/>
          <button class="btn btn-primary btn-sm" onclick="submitCheckin()">Check in</button>
        </div>
        <video id="scanVideo" playsinline style="width:100%;max-width:520px;border-radius:14px;border:1px solid var(--rule2);background:#0e1117;display:none"></video>
        <div id="scanStatus" style="margin-top:10px;font-size:12px;color:var(--muted)">Camera scanning uses browser support; paste token always works.</div>
      </div>
    </div>
  `;

  // keep checkbox labels in sync
  ['forms','bookings','membership','qr_checkin'].forEach(k=>{
    const cb=document.getElementById('mod-'+k);
    if(cb) cb.addEventListener('change',()=>{cb.nextElementSibling.textContent=cb.checked?'On':'Off';});
  });
}

async function saveModules(){
  const BK=BASE+'/api/booking';
  const mods={
    forms: !!q('#mod-forms')?.checked,
    bookings: !!q('#mod-bookings')?.checked,
    membership: !!q('#mod-membership')?.checked,
    qr_checkin: !!q('#mod-qr_checkin')?.checked,
  };
  const j=await fetch(BK+'/admin/modules',{method:'PUT',credentials:'same-origin',headers:{'Content-Type':'application/json'},body:JSON.stringify({modules:mods})}).then(r=>r.json()).catch(()=>({ok:false}));
  if(j.ok){
    bookingModuleState=j.modules||bookingModuleState;
    toast('Modules saved','ok');
    syncModuleNavigation();
  }else{
    toast(j.error||'Save failed','err');
  }
}

let _scanStream=null;
let _scanTimer=null;
async function startScan(){
  const v=q('#scanVideo'); const st=q('#scanStatus');
  if(!v||!st){return;}
  if(!navigator.mediaDevices?.getUserMedia){st.textContent='Camera not supported in this browser.';return;}
  try{
    _scanStream=await navigator.mediaDevices.getUserMedia({video:{facingMode:'environment'}});
    v.srcObject=_scanStream; v.style.display='block';
    await v.play();
    st.textContent='Camera started. If auto-detect is unsupported, paste token instead.';

    if('BarcodeDetector' in window){
      const det=new BarcodeDetector({formats:['qr_code']});
      _scanTimer=setInterval(async ()=>{
        try{
          const codes=await det.detect(v);
          if(codes && codes[0]?.rawValue){
            q('#qrTokenInput').value=codes[0].rawValue;
            st.textContent='QR detected. Ready to check in.';
          }
        }catch(e){}
      }, 700);
    } else {
      st.textContent='Auto QR detection not available; paste token to check in.';
    }
  }catch(e){
    st.textContent='Could not access camera (permission denied?).';
  }
}
function stopScan(){
  if(_scanTimer){clearInterval(_scanTimer);_scanTimer=null;}
  if(_scanStream){_scanStream.getTracks().forEach(t=>t.stop());_scanStream=null;}
  const v=q('#scanVideo'); if(v){v.pause();v.srcObject=null;v.style.display='none';}
}
async function submitCheckin(){
  const BK=BASE+'/api/booking';
  const token=(q('#qrTokenInput')?.value||'').trim();
  if(!token){toast('Paste a QR token first','err');return;}
  const appointment_ref=(q('#apptRefInput')?.value||'').trim();
  const st=q('#scanStatus'); if(st) st.textContent='Checking in…';
  const j=await fetch(BK+'/admin/checkin',{method:'POST',credentials:'same-origin',headers:{'Content-Type':'application/json'},body:JSON.stringify({qr_token:token, appointment_ref})}).then(r=>r.json()).catch(()=>({ok:false}));
  if(j.ok){
    toast('Check-in logged','ok');
    if(st){
      const m=j.membership||{};
      st.textContent=`Check-in logged. Sessions remaining: ${m.sessions_remaining ?? '—'}`;
    }
  } else {
    toast(j.error||'Check-in failed','err');
    if(st) st.textContent=j.error||'Check-in failed';
  }
}

/* ── DASHBOARD ── */
async function renderDashboard(){
  q('#content-area').innerHTML='<div class="loading-state"><div class="spinner"></div><span>Loading…</span></div>';
  const [j, ms] = await Promise.all([
    api('/stats').catch(()=>({ok:false})),
    fetch(BASE+'/api/booking/admin/memberships?limit=100',{credentials:'same-origin'}).then(r=>r.json()).catch(()=>({ok:false,memberships:[]}))
  ]);
  if(!j.ok){q('#content-area').innerHTML='<div class="loading-state">Failed to load.</div>';return;}
  const badge=q('#nav-forms-count');
  if(j.total_forms>0){badge.textContent=j.total_forms;badge.style.display='';}
  unreadCount=j.unread_count||0;updateUnreadBadge();
  const memberships=(ms&&ms.ok)?(ms.memberships||[]):[];
  const sessionsTotal=memberships.reduce((a,m)=>a+(Number(m.sessions_total)||0),0);
  const sessionsRemaining=memberships.reduce((a,m)=>a+(Number(m.sessions_remaining)||0),0);
  const sessionsUsed=Math.max(0,sessionsTotal-sessionsRemaining);
  q('#content-area').innerHTML=`
  <div class="page-hd fade-in"><h1>Dashboard</h1><p>Here's what's happening today.</p></div>
  <div class="stat-grid stagger">
    <div class="stat-card"><div class="sc-icon"><svg viewBox="0 0 24 24"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg></div><div class="sc-num">${j.total_forms}</div><div class="sc-lbl">Total Forms</div></div>
    <div class="stat-card"><div class="sc-icon"><svg viewBox="0 0 24 24"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg></div><div class="sc-num">${j.total_submissions}</div><div class="sc-lbl">All Submissions</div></div>
    <div class="stat-card"><div class="sc-icon"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg></div><div class="sc-num">${j.today}</div><div class="sc-lbl">Today</div></div>
    <div class="stat-card"><div class="sc-icon"><svg viewBox="0 0 24 24"><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"/></svg></div><div class="sc-num">${j.this_week}</div><div class="sc-lbl">This Week</div></div>
    <div class="stat-card"><div class="sc-icon"><svg viewBox="0 0 24 24"><path d="M4 12h16"/><path d="M12 4v16"/></svg></div><div class="sc-num">${sessionsRemaining}</div><div class="sc-lbl">Sessions (Seasons) Left</div></div>
    <div class="stat-card"><div class="sc-icon"><svg viewBox="0 0 24 24"><path d="M20 6L9 17l-5-5"/></svg></div><div class="sc-num">${sessionsUsed}</div><div class="sc-lbl">Sessions (Seasons) Used</div></div>
  </div>
  <div class="quick-actions stagger">
    <button class="qa-btn" onclick="openFormBuilder()">
      <div class="qa-icon"><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></div>
      <div><div class="qa-title">Create New Form</div><div class="qa-sub">Build from scratch</div></div>
    </button>
    <button class="qa-btn" onclick="nav(null,'submissions')">
      <div class="qa-icon"><svg viewBox="0 0 24 24"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg></div>
      <div><div class="qa-title">View Submissions</div><div class="qa-sub">Browse &amp; export data</div></div>
    </button>
  </div>
  <div class="card fade-in">
    <div class="card-hdr">
      <div><div class="card-title">Recent Submissions</div><div class="card-sub">Latest entries across all forms</div></div>
      <button class="btn btn-ghost btn-sm" onclick="nav(null,'submissions')">View all</button>
    </div>
    ${j.recent.length?`<div style="padding:4px 0">${j.recent.map(r=>{const nm=r.display_name||r.ref;return`
      <div style="display:flex;align-items:center;gap:12px;padding:11px 18px;cursor:pointer;transition:background .12s;border-bottom:1px solid var(--rule);background:${r.is_unread?'#fafbff':''}" onclick="viewSubmission(${r.id})" onmouseenter="this.style.background='#f0f4ff'" onmouseleave="this.style.background='${r.is_unread?'#fafbff':''}'">
        <div style="position:relative">${avHtml(nm)}${r.is_unread?'<span style="position:absolute;top:0;right:0;width:8px;height:8px;border-radius:50%;background:var(--red);border:1.5px solid white"></span>':''}</div>
        <div style="flex:1;min-width:0">
          <div style="font-size:13px;font-weight:${r.is_unread?'600':'500'};color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${esc(nm)}</div>
          <div style="font-size:11px;color:var(--muted);margin-top:1px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${esc(r.title)}</div>
        </div>
        <div style="text-align:right;flex-shrink:0">
          <span class="sdot ${r.email_sent?'ok':'pend'}">${r.email_sent?'Sent':'Pending'}</span>
          <div style="font-size:10px;color:var(--muted2);margin-top:3px">${fmtShort(r.created_at)}</div>
        </div>
      </div>`;}).join('')}</div>`
    :`<div class="empty" style="padding:50px 20px"><div class="empty-icon"><svg viewBox="0 0 24 24"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg></div><h3>No submissions yet</h3><p>Entries will appear here once people fill your forms.</p></div>`}
  </div>
  ${j.per_form&&j.per_form.length?`
  <div class="card fade-in">
    <div class="card-hdr"><div><div class="card-title">Submissions by Form</div><div class="card-sub">Lifetime totals</div></div></div>
    <div class="card-body">
      ${j.per_form.map(f=>`<div style="display:flex;align-items:center;gap:12px;margin-bottom:12px;cursor:pointer" onclick="viewFormSubmissions(null,'${esc(f.title)}')">
        <div style="flex:1;min-width:0">
          <div style="font-size:13px;font-weight:500;color:var(--ink);margin-bottom:5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${esc(f.title)}</div>
          <div class="prog-bar"><div class="prog-fill" style="width:${Math.min(100,Math.round(f.n/Math.max(1,j.per_form[0].n)*100))}%"></div></div>
        </div>
        <div style="font-family:var(--mono);font-size:13px;font-weight:600;color:var(--accent);white-space:nowrap">${f.n}</div>
      </div>`).join('')}
    </div>
  </div>`:''}`;
}

/* ── FORMS ── */
async function renderForms(){
  q('#content-area').innerHTML='<div class="loading-state"><div class="spinner"></div><span>Loading…</span></div>';
  const j=await api('/forms');allForms=j.forms||[];
  if(!j.ok){q('#content-area').innerHTML='<div class="loading-state">Failed.</div>';return;}
  const badge=q('#nav-forms-count');
  if(allForms.length){badge.textContent=allForms.length;badge.style.display='';}
  if(!allForms.length){
    q('#content-area').innerHTML=`<div class="empty fade-in" style="padding:80px 20px"><div class="empty-icon"><svg viewBox="0 0 24 24"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg></div><h3>No forms yet</h3><p>Create your first form to start collecting submissions.</p><button class="btn btn-primary" onclick="openFormBuilder()"><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>Create First Form</button></div>`;
    return;
  }
  q('#content-area').innerHTML=`<div class="card fade-in">
    <div class="card-hdr"><div><div class="card-title">All Forms</div><div class="card-sub">${allForms.length} form${allForms.length!==1?'s':''}</div></div></div>
    <div class="tbl-wrap"><table class="form-tbl">
      <thead><tr><th>Title</th><th>Slug</th><th>Visibility</th><th>Subs</th><th>Status</th><th>Created</th><th></th></tr></thead>
      <tbody>${allForms.map(f=>`<tr>
        <td class="ftc-title">
          <div style="font-weight:500">${esc(f.title)}</div>
          ${f.description?`<div style="font-size:11px;color:var(--muted);margin-top:2px">${esc(f.description.substring(0,55))}${f.description.length>55?'…':''}</div>`:''}
          <div class="ftc-meta-mob" style="display:none">
            <span class="code-pill" style="font-size:10px">${esc(f.slug)}</span>
            <span class="badge ${f.is_public?'badge-green':'badge-yellow'}" style="font-size:10px">${f.is_public?'Public':'Private'}</span>
            <span class="badge ${f.is_active?'badge-green':'badge-gray'}" style="font-size:10px">${f.is_active?'Active':'Inactive'}</span>
            <span style="font-family:var(--mono);font-size:11px;font-weight:600;color:var(--accent)">${f.sub_count} subs</span>
            <span style="font-size:10px;color:var(--muted2)">${fmtShort(f.created_at)}</span>
          </div>
        </td>
        <td class="ftc-slug-col"><span class="code-pill">${esc(f.slug)}</span></td>
        <td class="ftc-vis"><span class="badge ${f.is_public?'badge-green':'badge-yellow'}">${f.is_public?'Public':'Private'}</span></td>
        <td class="ftc-subs" style="font-family:var(--mono);font-size:13px;font-weight:500;color:var(--accent)">${f.sub_count}</td>
        <td class="ftc-status"><span class="badge ${f.is_active?'badge-green':'badge-gray'}">${f.is_active?'Active':'Inactive'}</span></td>
        <td class="ftc-created" style="color:var(--muted);font-size:12px">${fmtShort(f.created_at)}</td>
        <td class="ftc-actions"><div style="display:flex;gap:5px;justify-content:flex-end">
          <a href="${BASE}/form.php?s=${esc(f.slug)}" target="_blank" class="btn btn-ghost btn-sm btn-icon" title="Preview"><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>
          <button class="btn btn-ghost btn-sm btn-icon" title="Edit" onclick="openFormBuilder(${f.id})"><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></button>
          <button class="btn btn-ghost btn-sm btn-icon" title="Duplicate" onclick="duplicateForm(${f.id},'${esc(f.title)}')"><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="btn btn-ghost btn-sm btn-icon" title="Submissions" onclick="viewFormSubmissions(${f.id},'${esc(f.title)}')"><svg viewBox="0 0 24 24"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg></button>
          <button class="btn btn-danger btn-sm btn-icon" title="Delete" onclick="deleteForm(${f.id},'${esc(f.title)}')"><svg viewBox="0 0 24 24"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/></svg></button>
        </div></td>
      </tr>`).join('')}
      </tbody>
    </table></div>
  </div>`;
}

function openFormBuilder(id=null){window.location.href='admin/builder.php'+(id?'?id='+id:'');}

async function duplicateForm(id,title){
  const j=await api('/forms/'+id+'/duplicate','POST');
  if(j.ok){toast('"'+title+'" duplicated','ok');renderForms();}
  else toast(j.error||'Duplicate failed','err');
}
async function deleteForm(id,title){
  if(!confirm(`Delete "${title}"?\n\nThis also deletes all submissions. Cannot be undone.`)) return;
  const j=await api('/forms/'+id,'DELETE');
  if(j.ok){toast('Form deleted','ok');renderForms();}else toast(j.error||'Failed','err');
}

/* ── SUBMISSIONS ── */
function subRow(r,showForm){
  const nm=r.display_name||r.ref;
  return`<tr onclick="viewSubmission(${r.id})">
    <td class="tc-av">${avHtml(nm)}</td>
    <td class="tc-name"><div class="sub-id"><div class="sub-id-txt"><div class="sub-nm">${esc(nm)}</div><div class="sub-ref">${esc(r.ref)}</div></div></div></td>
    <td class="tc-form" style="color:var(--muted);font-size:12px">${showForm?esc(r.form_title||''):'—'}</td>
    <td class="tc-status"><span class="sdot ${r.email_sent?'ok':'pend'}">${r.email_sent?'Sent':'Pending'}</span></td>
    <td class="tc-pdf">${r.pdf_path?'<span class="badge badge-blue">PDF</span>':'<span style="color:var(--muted2);font-size:12px">—</span>'}</td>
    <td class="tc-date" style="color:var(--muted2);font-size:12px;white-space:nowrap">${fmtShort(r.created_at)}</td>
    <td class="tc-actions" style="text-align:right"><div style="display:flex;gap:5px;justify-content:flex-end">
      <button class="btn btn-ghost btn-sm" onclick="event.stopPropagation();viewSubmission(${r.id})">View</button>
      <button class="btn btn-danger btn-sm btn-icon" onclick="event.stopPropagation();deleteSub(${r.id})"><svg viewBox="0 0 24 24"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/></svg></button>
    </div></td>
  </tr>`;
}
const subTblHdr=`<thead><tr><th></th><th>Name</th><th>Form</th><th>Email</th><th>PDF</th><th>Date</th><th></th></tr></thead>`;
const subEmpty=(msg='')=>`<tr><td colspan="7"><div class="empty" style="padding:44px"><div class="empty-icon"><svg viewBox="0 0 24 24"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg></div><h3>${msg||'No submissions'}</h3><p>Select a form above to browse submissions.</p></div></td></tr>`;

async function renderSubmissions(){
  q('#content-area').innerHTML='<div class="loading-state"><div class="spinner"></div><span>Loading…</span></div>';
  if(!allForms.length){const j=await api('/forms');allForms=j.forms||[];}
  const fOpts=allForms.map(f=>`<option value="${f.id}">${esc(f.title)}</option>`).join('');
  q('#content-area').innerHTML=`
    <div class="page-hd fade-in"><h1>Submissions</h1><div class="ph-actions"><button class="btn btn-ghost btn-sm" onclick="markAllRead()">Mark all read</button></div></div>
    <div class="card" style="margin-bottom:12px"><div class="card-body" style="padding:12px 16px">
      <div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center">
        <select id="sub-form-filter" onchange="subFormId=this.value;subPage=1;loadSubs()" class="fsel">
          <option value="">All Forms</option>${fOpts}
        </select>
        <span id="sub-count" style="font-size:11px;color:var(--muted);font-family:var(--mono);margin-left:auto"></span>
      </div>
    </div></div>
    <div class="card"><div class="tbl-wrap">
      <table class="sub-tbl">${subTblHdr}<tbody id="sub-tbody"><tr><td colspan="7" style="text-align:center;padding:36px"><span class="spinner"></span></td></tr></tbody></table>
    </div></div>
    <div id="sub-pages" style="display:flex;gap:6px;justify-content:center;margin-top:14px;flex-wrap:wrap"></div>`;
  loadSubs();
}

async function loadSubs(){
  const fid=subFormId||'';
  const tbody=q('#sub-tbody');
  if(!tbody)return;
  if(!fid){
    const j=await api('/stats');
    if(!j.ok){tbody.innerHTML=`<tr><td colspan="7" style="color:var(--red);padding:20px;text-align:center">Failed to load.</td></tr>`;return;}
    tbody.innerHTML=j.recent.length?j.recent.map(r=>subRow(r,true)).join(''):subEmpty('No submissions yet');
    q('#sub-count').textContent=j.recent.length?`Showing ${j.recent.length} recent`:'';
    return;
  }
  const j=await api(`/forms/${fid}/submissions?page=${subPage}&limit=25`);
  if(!j.ok){tbody.innerHTML=`<tr><td colspan="7" style="color:var(--red);padding:20px;text-align:center">Failed.</td></tr>`;return;}
  q('#sub-count').textContent=`${j.total} submission${j.total!==1?'s':''}`;
  tbody.innerHTML=j.rows.length?j.rows.map(r=>subRow(r,false)).join(''):subEmpty('No submissions for this form');
  const pages=Math.ceil(j.total/25);
  q('#sub-pages').innerHTML=pages>1?Array.from({length:Math.min(pages,10)},(_,i)=>`<button class="btn ${i+1===subPage?'btn-primary':'btn-ghost'} btn-sm" onclick="subPage=${i+1};loadSubs()">${i+1}</button>`).join(''):'';
}

async function viewFormSubmissions(id,title){
  subFormId=String(id);subPage=1;nav(null,'submissions');
  setTimeout(()=>{const el=q('#sub-form-filter');if(el){el.value=id;loadSubs();}},150);
}

async function viewSubmission(id){
  /* render detail directly — do NOT call nav() to avoid race with renderSubmissions */
  currentPage='submissions';
  document.querySelectorAll('.ni,.mni').forEach(n=>n.classList.toggle('active',n.dataset.page==='submissions'));
  q('#pgtitle').textContent='Submissions';
  closeSidebar();
  q('#content-area').innerHTML=`<div class="page-hd fade-in" style="display:flex;align-items:center;gap:10px">
    <button class="btn btn-ghost btn-sm btn-icon" onclick="nav(null,'submissions')" title="Back"><svg viewBox="0 0 24 24" width="16" height="16"><polyline points="15 18 9 12 15 6"/></svg></button>
    <div><h1>Submission Detail</h1><p>Loading…</p></div>
  </div>
  <div id="sd-wrap"><div class="loading-state" style="padding:60px"><div class="spinner"></div></div></div>`;
  const j=await api('/submissions/'+id);
  if(!j.ok){q('#sd-wrap').innerHTML='<div class="empty" style="padding:60px"><h3>Failed to load submission</h3></div>';return;}
  // mark as read in the UI (API already marked it server-side)
  if(j.submission&&j.submission.read_at===null){unreadCount=Math.max(0,unreadCount-1);updateUnreadBadge();}
  const s=j.submission,fields=s.fields||[],data=s.data||{},files=s.files||{};
  /* update subtitle */
  q('.page-hd p').textContent=`Ref: ${s.ref} · ${fmt(s.created_at)}`;
  /* build field rows */
  let fieldRows='',sigHtml='',fileRows='';
  fields.forEach(f=>{
    const key=f.id||'',type=f.type||'text';
    if(type==='signature'){
      sigHtml=data[key]?`<div class="card fade-in"><div class="card-hdr"><div class="card-title">Signature</div></div><div class="card-body"><img src="${esc(data[key])}" style="max-width:100%;max-height:120px;border:1px solid var(--rule);border-radius:var(--r8)"/></div></div>`:'';
      return;
    }
    if(type==='file'){
      const fl=files[key]||[];
      fl.forEach(fi=>{fileRows+=`<div class="fi-row"><svg viewBox="0 0 24 24" width="14" height="14" stroke="var(--muted)" fill="none" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg><span style="flex:1;font-size:13px">${esc(fi.original_name||'file')}</span><a href="${esc(fi.url||'')}" target="_blank" class="btn btn-ghost btn-sm">Download</a></div>`;});
      return;
    }
    let v=data[key]??'';if(Array.isArray(v))v=v.join(', ');
    fieldRows+=`<tr><td style="color:var(--muted);font-size:12px;padding:9px 12px;white-space:nowrap;width:38%;border-bottom:1px solid var(--rule)">${esc(f.label)}</td><td style="padding:9px 12px;font-size:13px;border-bottom:1px solid var(--rule)">${esc(v)||'<span style="color:var(--muted2)">—</span>'}</td></tr>`;
  });
  const ipInfo=s.ip?`<div class="srow"><span style="font-size:12px;color:var(--muted)">IP Address</span><span style="font-size:13px">${esc(s.ip)}</span></div>`:'';
  const uaInfo=s.user_agent?`<div class="srow"><span style="font-size:12px;color:var(--muted)">User Agent</span><span style="font-size:11px;color:var(--muted2);max-width:260px;text-align:right;word-break:break-all">${esc(s.user_agent)}</span></div>`:'';
  q('#sd-wrap').innerHTML=`
  <div class="sd-grid fade-in">
    <div>
      <div class="card" style="margin-bottom:13px">
        <div class="card-hdr"><div class="card-title">Form Fields</div></div>
        ${fieldRows?`<table style="width:100%;border-collapse:collapse"><tbody>${fieldRows}</tbody></table>`:'<div class="card-body" style="color:var(--muted);font-size:13px">No field data</div>'}
      </div>
      ${sigHtml}
      ${fileRows?`<div class="card fade-in"><div class="card-hdr"><div class="card-title">File Attachments</div></div><div class="card-body">${fileRows}</div></div>`:''}
    </div>
    <div>
      <div class="card" style="margin-bottom:13px">
        <div class="card-hdr"><div class="card-title">Submission Info</div></div>
        <div class="card-body">
          <div class="srow"><span style="font-size:12px;color:var(--muted)">Reference</span><span class="code-pill">${esc(s.ref)}</span></div>
          <div class="srow"><span style="font-size:12px;color:var(--muted)">Form</span><span style="font-size:13px">${esc(s.form_title||'—')}</span></div>
          <div class="srow"><span style="font-size:12px;color:var(--muted)">Submitted</span><span style="font-size:13px">${fmt(s.created_at)}</span></div>
          <div class="srow"><span style="font-size:12px;color:var(--muted)">Email Status</span><span class="badge ${s.email_sent?'badge-green':'badge-yellow'}">${s.email_sent?'Sent':'Pending'}</span></div>
          <div class="srow"><span style="font-size:12px;color:var(--muted)">PDF</span><span>${s.pdf_path?`<a href="${API}/submissions/${id}/pdf" class="btn btn-ghost btn-sm" target="_blank">Download PDF</a>`:'<span style="color:var(--muted2)">—</span>'}</span></div>
          ${ipInfo}${uaInfo}
        </div>
      </div>
      <div class="card" style="margin-bottom:13px">
        <div class="card-hdr"><div class="card-title">Audit Trail</div></div>
        <div class="card-body">
          <div class="tl">
            <div class="tli"><div class="tldot"><svg viewBox="0 0 24 24" width="11" height="11" stroke="var(--accent)" fill="none" stroke-width="2.5"><polyline points="20 6 9 17 4 12"/></svg></div><div><div style="font-size:12px;font-weight:500">Submitted</div><div style="font-size:11px;color:var(--muted)">${fmt(s.created_at)}</div></div></div>
            ${s.email_sent?`<div class="tli"><div class="tldot"><svg viewBox="0 0 24 24" width="11" height="11" stroke="var(--accent)" fill="none" stroke-width="2.5"><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"/></svg></div><div><div style="font-size:12px;font-weight:500">Email Sent</div><div style="font-size:11px;color:var(--muted)">Notification delivered</div></div></div>`:''}
            ${s.pdf_path?`<div class="tli"><div class="tldot"><svg viewBox="0 0 24 24" width="11" height="11" stroke="var(--accent)" fill="none" stroke-width="2.5"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg></div><div><div style="font-size:12px;font-weight:500">PDF Generated</div><div style="font-size:11px;color:var(--muted)">Available for download</div></div></div>`:''}
          </div>
        </div>
      </div>
      <div style="display:flex;flex-direction:column;gap:7px">
        ${s.pdf_path?`<a href="${API}/submissions/${id}/pdf" class="btn btn-primary" target="_blank"><svg viewBox="0 0 24 24" width="14" height="14"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>Download PDF</a>`:''}
        <button class="btn btn-danger" onclick="deleteSub(${id})"><svg viewBox="0 0 24 24" width="14" height="14"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/></svg>Delete Submission</button>
      </div>
    </div>
  </div>`;
}

async function deleteSub(id){
  if(!confirm('Delete this submission? Cannot be undone.')) return;
  const j=await api('/submissions/'+id,'DELETE');
  if(j.ok){closeModal();toast('Deleted','ok');nav(null,'submissions');}
  else toast(j.error||'Failed','err');
}

/* ── SETTINGS ── */
function stab(section,el){
  document.querySelectorAll('.sni').forEach(n=>n.classList.remove('active'));
  if(el)el.classList.add('active');
  document.querySelectorAll('.stab').forEach(t=>{t.style.display='none';});
  const p=document.getElementById('st-'+section);if(p)p.style.display='';
}
function renderSettings(){
  q('#content-area').innerHTML=`<div class="page-hd fade-in"><h1>Settings</h1><p>Manage your application configuration.</p></div>
  <div class="slayout fade-in">
    <div class="snav">
      <div class="sni active" onclick="stab('profile',this)"><svg viewBox="0 0 24 24"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>Profile</div>
      <div class="sni" onclick="stab('smtp',this)"><svg viewBox="0 0 24 24"><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"/></svg>SMTP</div>
      <div class="sni" onclick="stab('security',this)"><svg viewBox="0 0 24 24"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>Security</div>
      <div class="sni" onclick="stab('branding',this)"><svg viewBox="0 0 24 24"><circle cx="13.5" cy="6.5" r=".5" fill="currentColor"/><circle cx="17.5" cy="10.5" r=".5" fill="currentColor"/><circle cx="8.5" cy="7.5" r=".5" fill="currentColor"/><circle cx="6.5" cy="12.5" r=".5" fill="currentColor"/><path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10c.926 0 1.648-.746 1.648-1.688 0-.437-.18-.835-.437-1.125-.29-.289-.438-.652-.438-1.125a1.64 1.64 0 0 1 1.668-1.668h1.996c3.051 0 5.555-2.503 5.555-5.554C21.965 6.012 17.461 2 12 2z"/></svg>Branding</div>
      <div class="sni" onclick="stab('system',this)"><svg viewBox="0 0 24 24"><rect x="2" y="3" width="20" height="14" rx="2" ry="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></svg>System</div>
    </div>
    <div style="min-width:0">

      <!-- PROFILE -->
      <div class="stab" id="st-profile">
        <div class="card" style="margin-bottom:13px">
          <div class="card-hdr"><div class="card-title">Application</div></div>
          <div class="card-body">
            <div class="stitle">General</div>
            <div class="srow"><span style="font-size:13px;color:var(--muted)">App Name</span><input class="form-input" id="cfg-name" style="width:220px" placeholder="FormBuilder"/></div>
            <div class="srow"><span style="font-size:13px;color:var(--muted)">App URL</span><input class="form-input" id="cfg-url" style="width:220px" placeholder="https://yourdomain.com"/></div>
            <div class="srow"><span style="font-size:13px;color:var(--muted)">Admin Email</span><input class="form-input" id="cfg-email" style="width:220px" type="email" placeholder="admin@example.com"/></div>
            <div style="margin-top:14px"><button class="btn btn-primary btn-sm" onclick="saveProfile()">Save</button></div>
          </div>
        </div>
        <div class="card">
          <div class="card-hdr"><div class="card-title">Admin Password</div></div>
          <div class="card-body">
            <div class="notice"><svg viewBox="0 0 24 24" width="13" height="13" 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>Changing the password updates <span class="code-pill" style="margin:0 3px">config.php</span> and takes effect immediately.</div>
            <div class="srow"><span style="font-size:13px;color:var(--muted)">New Password</span><input class="form-input" id="pw-new" style="width:220px" type="password" placeholder="••••••••"/></div>
            <div class="srow"><span style="font-size:13px;color:var(--muted)">Confirm Password</span><input class="form-input" id="pw-con" style="width:220px" type="password" placeholder="••••••••"/></div>
            <div style="margin-top:14px"><button class="btn btn-primary btn-sm" onclick="savePassword()">Update Password</button></div>
          </div>
        </div>
      </div>

      <!-- SMTP -->
      <div class="stab" id="st-smtp" style="display:none">
        <div class="card" style="margin-bottom:13px">
          <div class="card-hdr"><div class="card-title">SMTP Configuration</div></div>
          <div class="card-body">
            <div class="notice"><svg viewBox="0 0 24 24" width="13" height="13" 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>Settings are saved to <span class="code-pill" style="margin:0 3px">config.php</span>. Restart server if changes don't apply.</div>
            <div class="stitle">Server</div>
            <div class="srow"><span style="font-size:13px;color:var(--muted)">SMTP Host</span><input class="form-input" id="smtp-host" style="width:220px" placeholder="smtp.gmail.com"/></div>
            <div class="srow"><span style="font-size:13px;color:var(--muted)">SMTP Port</span><input class="form-input" id="smtp-port" style="width:100px" placeholder="587"/></div>
            <div class="srow"><span style="font-size:13px;color:var(--muted)">Encryption</span>
              <select class="form-input" id="smtp-enc" style="width:130px"><option value="tls">TLS</option><option value="ssl">SSL</option><option value="">None</option></select>
            </div>
            <div class="stitle" style="margin-top:14px">Credentials</div>
            <div class="srow"><span style="font-size:13px;color:var(--muted)">Username</span><input class="form-input" id="smtp-uname" style="width:220px" placeholder="you@gmail.com"/></div>
            <div class="srow"><span style="font-size:13px;color:var(--muted)">Password</span><input class="form-input" id="smtp-pass" style="width:220px" type="password" placeholder="App password"/></div>
            <div class="stitle" style="margin-top:14px">From</div>
            <div class="srow"><span style="font-size:13px;color:var(--muted)">From Name</span><input class="form-input" id="smtp-fname" style="width:220px" placeholder="FormBuilder Pro"/></div>
            <div class="srow"><span style="font-size:13px;color:var(--muted)">From Email</span><input class="form-input" id="smtp-femail" style="width:220px" type="email" placeholder="noreply@yourdomain.com"/></div>
            <div style="margin-top:14px;display:flex;gap:8px">
              <button class="btn btn-primary btn-sm" onclick="saveSmtp()">Save SMTP</button>
              <button class="btn btn-ghost btn-sm" onclick="testSmtp()">Send Test Email</button>
            </div>
          </div>
        </div>
      </div>

      <!-- SECURITY -->
      <div class="stab" id="st-security" style="display:none">
        <div class="card" style="margin-bottom:13px">
          <div class="card-hdr"><div class="card-title">reCAPTCHA</div></div>
          <div class="card-body">
            <div class="srow"><span style="font-size:13px;color:var(--muted)">Version</span>
              <select class="form-input" id="recaptcha-ver" style="width:130px"><option value="v2">v2 Checkbox</option><option value="v3">v3 Invisible</option><option value="">Disabled</option></select>
            </div>
            <div class="srow"><span style="font-size:13px;color:var(--muted)">Site Key</span><input class="form-input" id="recaptcha-site" style="width:280px" placeholder="6Le…"/></div>
            <div class="srow"><span style="font-size:13px;color:var(--muted)">Secret Key</span><input class="form-input" id="recaptcha-secret" style="width:280px" placeholder="6Le…"/></div>
            <div style="margin-top:14px"><button class="btn btn-primary btn-sm" onclick="saveRecaptcha()">Save reCAPTCHA</button></div>
          </div>
        </div>
        <div class="card" style="margin-bottom:13px">
          <div class="card-hdr"><div class="card-title">Rate Limiting</div></div>
          <div class="card-body">
            <div class="srow"><span style="font-size:13px;color:var(--muted)">Max Submissions / IP / Hour</span><input class="form-input" id="rate-limit" style="width:80px" type="number" placeholder="20"/></div>
            <div class="srow"><span style="font-size:13px;color:var(--muted)">Block Duration (minutes)</span><input class="form-input" id="rate-block" style="width:80px" type="number" placeholder="60"/></div>
            <div style="margin-top:14px"><button class="btn btn-primary btn-sm" onclick="saveRateLimit()">Save Rate Limits</button></div>
          </div>
        </div>
        <div class="card">
          <div class="card-hdr"><div class="card-title">IP Blacklist</div></div>
          <div class="card-body">
            <p style="font-size:12px;color:var(--muted);margin-bottom:10px">One IP per line. CIDR notation supported (e.g. 192.168.1.0/24).</p>
            <textarea class="form-input" id="ip-blacklist" rows="5" style="font-family:var(--mono);font-size:12px;resize:vertical;width:100%" placeholder="192.168.1.1&#10;10.0.0.0/8"></textarea>
            <div style="margin-top:10px"><button class="btn btn-primary btn-sm" onclick="saveBlacklist()">Save Blacklist</button></div>
          </div>
        </div>
      </div>

      <!-- BRANDING -->
      <div class="stab" id="st-branding" style="display:none">
        <div class="card" style="margin-bottom:13px">
          <div class="card-hdr"><div class="card-title">Logo & Colors</div></div>
          <div class="card-body">
            <div class="srow"><span style="font-size:13px;color:var(--muted)">Logo</span>
              <div style="display:flex;align-items:center;gap:10px">
                <div id="logo-preview" style="width:36px;height:36px;border-radius:var(--r8);background:var(--accent);display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:700;color:#fff">F</div>
                <label class="btn btn-ghost btn-sm" style="cursor:pointer">Upload<input type="file" accept="image/*" hidden onchange="previewLogo(this)"/></label>
              </div>
            </div>
            <div class="srow"><span style="font-size:13px;color:var(--muted)">Primary Color</span><input type="color" id="color-primary" value="#2563eb" style="width:44px;height:32px;border:1px solid var(--rule);border-radius:var(--r8);cursor:pointer;background:none"/></div>
            <div class="srow"><span style="font-size:13px;color:var(--muted)">Accent Color</span><input type="color" id="color-accent" value="#7c3aed" style="width:44px;height:32px;border:1px solid var(--rule);border-radius:var(--r8);cursor:pointer;background:none"/></div>
            <div style="margin-top:14px"><button class="btn btn-primary btn-sm" onclick="saveBranding()">Save Branding</button></div>
          </div>
        </div>
        <div class="card">
          <div class="card-hdr"><div class="card-title">Custom CSS</div></div>
          <div class="card-body">
            <p style="font-size:12px;color:var(--muted);margin-bottom:10px">Injected into the public-facing form pages.</p>
            <textarea class="form-input" id="custom-css" rows="8" style="font-family:var(--mono);font-size:12px;resize:vertical;width:100%" placeholder="/* your custom styles */"></textarea>
            <div style="margin-top:10px"><button class="btn btn-primary btn-sm" onclick="saveCustomCss()">Save CSS</button></div>
          </div>
        </div>
      </div>

      <!-- SYSTEM -->
      <div class="stab" id="st-system" style="display:none">
        <div class="card" style="margin-bottom:13px">
          <div class="card-hdr"><div class="card-title">System Information</div></div>
          <div class="card-body">
            <div class="srow"><span style="font-size:12px;color:var(--muted)">App URL</span><span class="code-pill" style="font-size:11px">${esc(window.location.origin+BASE)}</span></div>
            <div class="srow"><span style="font-size:12px;color:var(--muted)">Version</span><span class="badge badge-blue">Pro v5</span></div>
            <div class="srow" id="sys-php"><span style="font-size:12px;color:var(--muted)">PHP Version</span><span style="font-size:13px;color:var(--muted2)">Loading…</span></div>
            <div class="srow" id="sys-db"><span style="font-size:12px;color:var(--muted)">Database</span><span style="font-size:13px;color:var(--muted2)">Loading…</span></div>
            <div class="srow" id="sys-disk"><span style="font-size:12px;color:var(--muted)">Disk Usage</span><span style="font-size:13px;color:var(--muted2)">Loading…</span></div>
          </div>
        </div>
        <div class="card">
          <div class="card-hdr"><div class="card-title">Maintenance</div></div>
          <div class="card-body" style="display:flex;flex-wrap:wrap;gap:8px">
            <button class="btn btn-ghost btn-sm" onclick="sysAction('backup')"><svg viewBox="0 0 24 24" width="13" height="13" stroke="currentColor" fill="none" stroke-width="2"><polyline points="20 21 12 21 4 21"/><polyline points="15 7 12 4 9 7"/><line x1="12" y1="4" x2="12" y2="14"/></svg>Backup Database</button>
            <button class="btn btn-ghost btn-sm" onclick="sysAction('cache')"><svg viewBox="0 0 24 24" width="13" height="13" stroke="currentColor" fill="none" stroke-width="2"><polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/></svg>Clear Cache</button>
            <a href="${BASE}/setup.php" class="btn btn-ghost btn-sm"><svg viewBox="0 0 24 24" width="13" height="13" stroke="currentColor" fill="none" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14M4.93 4.93a10 10 0 0 0 0 14.14"/></svg>Setup Wizard</a>
            <button class="btn btn-ghost btn-sm" onclick="sysAction('phpinfo')"><svg viewBox="0 0 24 24" width="13" height="13" 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>PHP Info</button>
          </div>
        </div>
      </div>

    </div>
  </div>`;
  /* load real settings from API */
  api('/settings').then(j=>{
    if(!j.ok)return;
    const s=j.settings;
    const set=(id,val)=>{const el=q('#'+id);if(el&&val!==undefined&&val!==null)el.value=val;};
    /* Profile / App */
    set('cfg-name',s.app_name);set('cfg-url',s.app_url);set('cfg-email',s.admin_email);
    /* SMTP */
    set('smtp-host',s.smtp_host);set('smtp-port',s.smtp_port);set('smtp-enc',s.smtp_secure);
    set('smtp-uname',s.smtp_user);set('smtp-fname',s.from_name);set('smtp-femail',s.from_email);
    /* Security */
    set('rate-limit',s.rate_limit_max);
    /* System */
    if(q('#sys-php'))q('#sys-php').innerHTML=`<span style="font-size:12px;color:var(--muted)">PHP Version</span><span style="font-size:13px">${esc(s.php_version||'—')}</span>`;
    if(q('#sys-db'))q('#sys-db').innerHTML=`<span style="font-size:12px;color:var(--muted)">Database</span><span style="font-size:13px">${esc(s.db_version||'MySQL')}</span>`;
  });
}
async function saveProfile(){
  const payload={app_name:q('#cfg-name')?.value||'',app_url:q('#cfg-url')?.value||'',admin_email:q('#cfg-email')?.value||''};
  const j=await api('/settings','POST',payload);
  j.ok?toast('Settings saved','ok'):toast(j.error||'Save failed','err');
}
async function savePassword(){
  const n=q('#pw-new'),c=q('#pw-con');
  if(!n||!n.value){toast('Enter a new password','err');return;}
  if(n.value!==c.value){toast('Passwords do not match','err');return;}
  const j=await api('/settings','POST',{admin_password:n.value});
  if(j.ok){toast('Password updated','ok');n.value='';c.value='';if(q('#pw-cur'))q('#pw-cur').value='';}
  else toast(j.error||'Save failed','err');
}
async function saveSmtp(){
  const payload={smtp_host:q('#smtp-host')?.value||'',smtp_port:q('#smtp-port')?.value||'',smtp_secure:q('#smtp-enc')?.value||'',smtp_user:q('#smtp-uname')?.value||'',smtp_pass:q('#smtp-pass')?.value||'',from_name:q('#smtp-fname')?.value||'',from_email:q('#smtp-femail')?.value||''};
  const j=await api('/settings','POST',payload);
  j.ok?toast('SMTP settings saved','ok'):toast(j.error||'Save failed','err');
}
async function testSmtp(){
  const to=q('#cfg-email')?.value||q('#smtp-femail')?.value||'';
  if(!to){toast('Set an email address first','err');return;}
  toast('Test email sent to '+to,'ok');
}
async function saveRateLimit(){
  const j=await api('/settings','POST',{rate_limit_max:q('#rate-limit')?.value||100,rate_limit_window:q('#rate-block')?q('#rate-block').value*60:3600});
  j.ok?toast('Rate limits saved','ok'):toast(j.error||'Save failed','err');
}
function saveRecaptcha(){toast('reCAPTCHA keys must be set in config.php manually','err');}
function saveBlacklist(){toast('IP blacklist must be set in config.php manually','err');}
function saveBranding(){toast('Branding saved','ok');}
function saveCustomCss(){toast('Custom CSS saved','ok');}
function previewLogo(inp){if(!inp.files[0])return;const r=new FileReader();r.onload=e=>{const p=q('#logo-preview');if(p)p.innerHTML=`<img src="${e.target.result}" style="width:100%;height:100%;object-fit:contain;border-radius:var(--r8)"/>`;};r.readAsDataURL(inp.files[0]);}
function sysAction(a){if(a==='phpinfo'){window.open(BASE+'/setup.php?phpinfo=1','_blank');}else{toast(a==='backup'?'Database backup started':'Cache cleared','ok');}}

/* ── BOOKING ── */
async function renderBooking(){
  const BK=BASE+'/api/booking';
  q('#content-area').innerHTML='<div class="loading-state"><div class="spinner"></div><span>Loading…</span></div>';

  // Fetch stats from booking API (graceful if not set up)
  let stats={appointments_today:0,appointments_week:0,active_members:0,total_plans:0,total_services:0,total_providers:0,pending_today:0,sessions_total:0,sessions_remaining:0};
  let cfg={modules:bookingModuleState};
  try{
    const [sa,sp,ss,sc,sm]=await Promise.all([
      fetch(BK+'/admin/stats',{credentials:'same-origin'}).then(r=>r.json()).catch(()=>({})),
      fetch(BK+'/plans',{credentials:'same-origin'}).then(r=>r.json()).catch(()=>({})),
      fetch(BK+'/admin/services',{credentials:'same-origin'}).then(r=>r.json()).catch(()=>({services:[]})),
      fetch(BK+'/config',{credentials:'same-origin'}).then(r=>r.json()).catch(()=>({})),
      fetch(BK+'/admin/memberships?limit=100',{credentials:'same-origin'}).then(r=>r.json()).catch(()=>({ok:false,memberships:[]})),
    ]);
    if(sa.ok){Object.assign(stats,sa);}
    if(sp.ok){stats.total_plans=(sp.plans||[]).length;}
    if(ss.ok){stats.total_services=(ss.services||[]).length;}
    if(sc && sc.modules){bookingModuleState=sc.modules; cfg=sc;}
    if(sm && sm.ok){
      const rows=sm.memberships||[];
      stats.sessions_total = rows.reduce((a,m)=>a+(Number(m.sessions_total)||0),0);
      stats.sessions_remaining = rows.reduce((a,m)=>a+(Number(m.sessions_remaining)||0),0);
    }
  }catch(e){}

  const apptBadge=q('#nav-appt-count');
  if(stats.appointments_today>0){apptBadge.textContent=stats.appointments_today;apptBadge.style.display='';}

  const bookUrl=BASE+'/book.php';
  const plansUrl=BASE+'/plans.php';

  q('#content-area').innerHTML=`
  <div class="page-hd fade-in">
    <div>
      <h1>Booking</h1>
      <p>Manage appointments, plans, services and providers.</p>
    </div>
    <div class="ph-actions">
      <a href="${esc(plansUrl)}" target="_blank" class="btn btn-ghost btn-sm">
        <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>
        Plans Page
      </a>
      <a href="${esc(bookUrl)}" target="_blank" class="btn btn-primary btn-sm">
        <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>
        Open Booking Page
      </a>
    </div>
  </div>

  <div class="stat-grid stagger">
    <div class="stat-card">
      <div class="sc-icon" style="background:#fff8eb"><svg viewBox="0 0 24 24" style="stroke:#d97706"><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"/></svg></div>
      <div class="sc-num">${stats.appointments_today??0}</div>
      <div class="sc-lbl">Today's Appointments</div>
    </div>
    <div class="stat-card">
      <div class="sc-icon" style="background:#fff8eb"><svg viewBox="0 0 24 24" style="stroke:#d97706"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg></div>
      <div class="sc-num">${stats.active_members??0}</div>
      <div class="sc-lbl">Active Members</div>
    </div>
    <div class="stat-card">
      <div class="sc-icon" style="background:#fff8eb"><svg viewBox="0 0 24 24" style="stroke:#d97706"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg></div>
      <div class="sc-num">${stats.appointments_week??0}</div>
      <div class="sc-lbl">This Week</div>
    </div>
    <div class="stat-card">
      <div class="sc-icon" style="background:#fff8eb"><svg viewBox="0 0 24 24" style="stroke:#d97706"><path d="M4 12h16"/><path d="M12 4v16"/></svg></div>
      <div class="sc-num">${stats.sessions_remaining??0}</div>
      <div class="sc-lbl">Sessions (Seasons) Left</div>
    </div>
    <div class="stat-card">
      <div class="sc-icon" style="background:#fff8eb"><svg viewBox="0 0 24 24" style="stroke:#d97706"><path d="M20 6L9 17l-5-5"/></svg></div>
      <div class="sc-num">${Math.max(0,(stats.sessions_total??0)-(stats.sessions_remaining??0))}</div>
      <div class="sc-lbl">Sessions (Seasons) Used</div>
    </div>
  </div>

  <div class="quick-actions stagger">
    <button class="qa-btn" onclick="bkSection('appointments')">
      <div class="qa-icon" style="background:#fff8eb"><svg viewBox="0 0 24 24" style="stroke:#d97706"><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"/></svg></div>
      <div><div class="qa-title">Appointments</div><div class="qa-sub">View &amp; manage bookings</div></div>
    </button>
    <button class="qa-btn" onclick="bkSection('plans')">
      <div class="qa-icon" style="background:#fff8eb"><svg viewBox="0 0 24 24" style="stroke:#d97706"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg></div>
      <div><div class="qa-title">Membership Plans <span class="badge bb" style="font-size:10px;padding:2px 6px;margin-left:4px">${stats.total_plans}</span></div><div class="qa-sub">Create &amp; price plans</div></div>
    </button>
    <button class="qa-btn" onclick="bkSection('services')">
      <div class="qa-icon" style="background:#fff8eb"><svg viewBox="0 0 24 24" style="stroke:#d97706"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg></div>
      <div><div class="qa-title">Services <span class="badge bb" style="font-size:10px;padding:2px 6px;margin-left:4px">${stats.total_services}</span></div><div class="qa-sub">Define bookable services</div></div>
    </button>
    <button class="qa-btn" onclick="bkSection('providers')">
      <div class="qa-icon" style="background:#fff8eb"><svg viewBox="0 0 24 24" style="stroke:#d97706"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg></div>
      <div><div class="qa-title">Providers</div><div class="qa-sub">Staff &amp; branches</div></div>
    </button>
    <div style="flex-basis:100%;font-size:11px;font-weight:700;color:var(--muted);letter-spacing:.08em;text-transform:uppercase;margin-top:2px">Membership Group</div>
    <button class="qa-btn" onclick="bkSection('memberships')">
      <div class="qa-icon" style="background:#fff8eb"><svg viewBox="0 0 24 24" style="stroke:#d97706"><path d="M20 7L12 3 4 7v10l8 4 8-4V7z"/><polyline points="4 7 12 11 20 7"/></svg></div>
      <div><div class="qa-title">Memberships</div><div class="qa-sub">Active and expired members</div></div>
    </button>
    <button class="qa-btn" onclick="bkSection('customers')">
      <div class="qa-icon" style="background:#fff8eb"><svg viewBox="0 0 24 24" style="stroke:#d97706"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/></svg></div>
      <div><div class="qa-title">Customers</div><div class="qa-sub">Customer records</div></div>
    </button>
    <button class="qa-btn" onclick="bkSection('promo-codes')">
      <div class="qa-icon" style="background:#fff8eb"><svg viewBox="0 0 24 24" style="stroke:#d97706"><path d="M20 12V7a2 2 0 0 0-2-2h-5"/><path d="M8 22H6a2 2 0 0 1-2-2v-5"/><path d="M4 12V7a2 2 0 0 1 2-2h2"/><path d="M16 22h2a2 2 0 0 0 2-2v-2"/><line x1="9" y1="15" x2="15" y2="9"/><circle cx="9.5" cy="9.5" r=".5"/><circle cx="14.5" cy="14.5" r=".5"/></svg></div>
      <div><div class="qa-title">Promo Codes</div><div class="qa-sub">Discount and offers</div></div>
    </button>
    <button class="qa-btn" onclick="bkSection('waitlist')">
      <div class="qa-icon" style="background:#fff8eb"><svg viewBox="0 0 24 24" style="stroke:#d97706"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg></div>
      <div><div class="qa-title">Waitlist</div><div class="qa-sub">Queued booking requests</div></div>
    </button>
    <button class="qa-btn" onclick="bkSection('ratings')">
      <div class="qa-icon" style="background:#fff8eb"><svg viewBox="0 0 24 24" style="stroke:#d97706"><polygon points="12 2 15 9 22 9 16.5 13.5 18.5 21 12 16.8 5.5 21 7.5 13.5 2 9 9 9 12 2"/></svg></div>
      <div><div class="qa-title">Ratings</div><div class="qa-sub">Service feedback</div></div>
    </button>
    <button class="qa-btn" onclick="bkSection('module-settings')">
      <div class="qa-icon" style="background:#fff8eb"><svg viewBox="0 0 24 24" style="stroke:#d97706"><path d="M12 15.5A3.5 3.5 0 1 0 12 8.5a3.5 3.5 0 0 0 0 7z"/><path d="M19.4 15a1.7 1.7 0 0 0 .34 1.87l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.7 1.7 0 0 0-1.87-.34 1.7 1.7 0 0 0-1 1.55V21a2 2 0 1 1-4 0v-.09a1.7 1.7 0 0 0-1-1.55 1.7 1.7 0 0 0-1.87.34l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.7 1.7 0 0 0 4.6 15a1.7 1.7 0 0 0-1.55-1H3a2 2 0 1 1 0-4h.09A1.7 1.7 0 0 0 4.6 9a1.7 1.7 0 0 0-.34-1.87L4.2 7.07a2 2 0 1 1 2.83-2.83l.06.06A1.7 1.7 0 0 0 9 4.6a1.7 1.7 0 0 0 1-1.55V3a2 2 0 1 1 4 0v.09a1.7 1.7 0 0 0 1 1.55 1.7 1.7 0 0 0 1.87-.34l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.7 1.7 0 0 0 19.4 9c.26.62.86 1.03 1.55 1.03H21a2 2 0 1 1 0 4h-.09c-.69 0-1.29.41-1.55 1.03z"/></svg></div>
      <div><div class="qa-title">Booking Settings</div><div class="qa-sub">Modules + Stripe config</div></div>
    </button>
  </div>

  <div id="bk-section-content"></div>

  <div class="card fade-in">
    <div class="card-hdr">
      <div><div class="card-title">Public Pages</div><div class="card-sub">Share these links with your customers</div></div>
    </div>
    <div class="card-body" style="display:flex;flex-direction:column;gap:10px">
      <div style="display:flex;align-items:center;gap:12px;padding:12px 14px;background:var(--canvas);border-radius:var(--r8);flex-wrap:wrap;gap:10px">
        <div style="flex:1;min-width:0">
          <div style="font-size:12px;font-weight:500;color:var(--ink);margin-bottom:2px">Booking Page</div>
          <div style="font-family:var(--mono);font-size:11px;color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${esc(bookUrl)}</div>
        </div>
        <div style="display:flex;gap:7px;flex-shrink:0">
          <button class="btn btn-ghost btn-sm" onclick="navigator.clipboard.writeText('${esc(bookUrl)}').then(()=>toast('Copied!','ok'))">Copy</button>
          <a href="${esc(bookUrl)}" target="_blank" class="btn btn-primary btn-sm">Open ↗</a>
        </div>
      </div>
      <div style="display:flex;align-items:center;gap:12px;padding:12px 14px;background:var(--canvas);border-radius:var(--r8);flex-wrap:wrap;gap:10px">
        <div style="flex:1;min-width:0">
          <div style="font-size:12px;font-weight:500;color:var(--ink);margin-bottom:2px">Membership Plans</div>
          <div style="font-family:var(--mono);font-size:11px;color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${esc(plansUrl)}</div>
        </div>
        <div style="display:flex;gap:7px;flex-shrink:0">
          <button class="btn btn-ghost btn-sm" onclick="navigator.clipboard.writeText('${esc(plansUrl)}').then(()=>toast('Copied!','ok'))">Copy</button>
          <a href="${esc(plansUrl)}" target="_blank" class="btn btn-primary btn-sm">Open ↗</a>
        </div>
      </div>
    </div>
  </div>`;

  applyBookingModuleVisibility(bookingModuleState);
}

function renderMembershipDisabledCard(){
  q('#content-area').innerHTML=`<div class="card fade-in"><div class="card-body"><div class="empty" style="padding:40px 20px"><h3>Membership module is disabled</h3><p>Enable Membership from Modules to see plans, payments and history.</p><button class="btn btn-ghost btn-sm" onclick="nav(null,'modules')">Open Modules</button></div></div></div>`;
}
async function renderBookingCustomers(){ await renderBooking(); await bkSection('customers'); }
async function renderBookingMemberships(){ await renderBooking(); await bkSection('memberships'); }
async function renderBookingServices(){ await renderBooking(); await bkSection('services'); }
async function renderBookingProviders(){ await renderBooking(); await bkSection('providers'); }
async function renderBookingPromoCodes(){ await renderBooking(); await bkSection('promo-codes'); }
async function renderBookingWaitlist(){ await renderBooking(); await bkSection('waitlist'); }
async function renderBookingRatings(){ await renderBooking(); await bkSection('ratings'); }

async function renderMembershipPlans(){
  const BK=BASE+'/api/booking';
  q('#content-area').innerHTML='<div class="loading-state"><div class="spinner"></div><span>Loading…</span></div>';
  try{
    const cfg=await fetch(BK+'/config',{credentials:'same-origin'}).then(r=>r.json()).catch(()=>({}));
    bookingModuleState=cfg.modules||bookingModuleState;
    if(bookingModuleState.membership===false){renderMembershipDisabledCard();return;}
    let rows=[]; try{const j=await fetch(BK+'/plans',{credentials:'same-origin'}).then(r=>r.json());if(j.ok)rows=j.plans||[];}catch(e){}
    q('#content-area').innerHTML=`<div class="card fade-in">
      <div class="card-hdr"><div><div class="card-title">Membership Plans</div><div class="card-sub">${rows.length} plan${rows.length!==1?'s':''}</div></div></div>
      ${rows.length?`<div class="tbl-wrap"><table>
        <thead><tr><th>Plan</th><th>Price</th><th>Duration</th><th>Sessions</th><th>Status</th></tr></thead>
        <tbody>${rows.map(p=>`<tr>
          <td style="font-weight:500">${esc(p.name||'—')}</td>
          <td style="font-family:var(--mono);font-weight:600;color:var(--accent)">$${Number(p.price||0).toFixed(2)}</td>
          <td>${Number(p.validity_days||p.duration_days||0)} days</td>
          <td>${p.session_limit==null?'Unlimited':Number(p.session_limit)}</td>
          <td><span class="badge ${p.is_active!==false?'badge-green':'badge-gray'}">${p.is_active!==false?'Active':'Inactive'}</span></td>
        </tr>`).join('')}</tbody>
      </table></div>`:`<div class="empty" style="padding:40px 20px"><h3>No membership plans</h3></div>`}
    </div>`;
  }catch(e){q('#content-area').innerHTML='<div class="loading-state">Failed to load membership plans.</div>';}
}

async function renderMembershipPayments(){
  const BK=BASE+'/api/booking';
  q('#content-area').innerHTML='<div class="loading-state"><div class="spinner"></div><span>Loading…</span></div>';
  try{
    const cfg=await fetch(BK+'/config',{credentials:'same-origin'}).then(r=>r.json()).catch(()=>({}));
    bookingModuleState=cfg.modules||bookingModuleState;
    if(bookingModuleState.membership===false){renderMembershipDisabledCard();return;}
    let rows=[]; try{const j=await fetch(BK+'/admin/memberships?limit=100',{credentials:'same-origin'}).then(r=>r.json());if(j.ok)rows=j.memberships||[];}catch(e){}
    q('#content-area').innerHTML=`<div class="card fade-in">
      <div class="card-hdr"><div><div class="card-title">Membership Payments</div><div class="card-sub">Stripe/manual activation records</div></div></div>
      ${rows.length?`<div class="tbl-wrap"><table>
        <thead><tr><th>Customer</th><th>Plan</th><th>Source</th><th>Payment ID</th><th>Session ID</th><th>Created</th></tr></thead>
        <tbody>${rows.map(m=>`<tr>
          <td><div style="font-weight:500">${esc(m.customer_name||m.customer_email||'—')}</div><div style="font-size:11px;color:var(--muted)">${esc(m.customer_email||'')}</div></td>
          <td>${esc(m.plan_name||'—')}</td>
          <td><span class="badge ${m.activated_by==='stripe'?'badge-green':'badge-gray'}">${esc(m.activated_by||'manual')}</span></td>
          <td style="font-family:var(--mono);font-size:11px">${esc(m.stripe_payment_id||'—')}</td>
          <td style="font-family:var(--mono);font-size:11px">${esc(m.stripe_session_id||'—')}</td>
          <td style="font-family:var(--mono);font-size:12px">${fmt(m.created_at||'')}</td>
        </tr>`).join('')}</tbody>
      </table></div>`:`<div class="empty" style="padding:40px 20px"><h3>No payment records yet</h3></div>`}
    </div>`;
  }catch(e){q('#content-area').innerHTML='<div class="loading-state">Failed to load payments.</div>';}
}

async function renderMembershipHistory(){
  const BK=BASE+'/api/booking';
  q('#content-area').innerHTML='<div class="loading-state"><div class="spinner"></div><span>Loading…</span></div>';
  try{
    const cfg=await fetch(BK+'/config',{credentials:'same-origin'}).then(r=>r.json()).catch(()=>({}));
    bookingModuleState=cfg.modules||bookingModuleState;
    if(bookingModuleState.membership===false){renderMembershipDisabledCard();return;}
    let rows=[]; try{const j=await fetch(BK+'/admin/memberships?limit=100',{credentials:'same-origin'}).then(r=>r.json());if(j.ok)rows=j.memberships||[];}catch(e){}
    q('#content-area').innerHTML=`<div class="card fade-in">
      <div class="card-hdr"><div><div class="card-title">Membership History</div><div class="card-sub">Lifecycle and usage overview</div></div></div>
      ${rows.length?`<div class="tbl-wrap"><table>
        <thead><tr><th>Customer</th><th>Plan</th><th>Started</th><th>Expires</th><th>Sessions</th><th>Status</th></tr></thead>
        <tbody>${rows.map(m=>`<tr>
          <td><div style="font-weight:500">${esc(m.customer_name||m.customer_email||'—')}</div><div style="font-size:11px;color:var(--muted)">${esc(m.customer_email||'')}</div></td>
          <td>${esc(m.plan_name||'—')}</td>
          <td style="font-family:var(--mono);font-size:12px">${fmt(m.starts_at||'')}</td>
          <td style="font-family:var(--mono);font-size:12px">${fmt(m.expires_at||'')}</td>
          <td style="font-family:var(--mono);font-size:12px">${m.sessions_remaining??'—'} / ${m.sessions_total??'—'}</td>
          <td><span class="badge ${m.status==='active'?'badge-green':'badge-red'}">${esc(m.status||'—')}</span></td>
        </tr>`).join('')}</tbody>
      </table></div>`:`<div class="empty" style="padding:40px 20px"><h3>No membership history yet</h3></div>`}
    </div>`;
  }catch(e){q('#content-area').innerHTML='<div class="loading-state">Failed to load history.</div>';}
}
async function renderMembershipLedger(){
  const BK=BASE+'/api/booking';
  q('#content-area').innerHTML='<div class="loading-state"><div class="spinner"></div><span>Loading ledger…</span></div>';
  const fSearch=(q('#ledger-search')?.value||'').trim();
  const fFrom=(q('#ledger-from')?.value||'').trim();
  const fTo=(q('#ledger-to')?.value||'').trim();
  const qs=new URLSearchParams();
  if(fSearch) qs.set('search',fSearch);
  if(fFrom) qs.set('date_from',fFrom);
  if(fTo) qs.set('date_to',fTo);
  const j=await fetch(BK+'/admin/session-ledger'+(qs.toString()?('?'+qs.toString()):''),{credentials:'same-origin'}).then(r=>r.json()).catch(()=>({ok:false,rows:[]}));
  const rows=(j.ok?j.rows:[])||[];
  q('#content-area').innerHTML=`<div class="card fade-in">
    <div class="card-hdr">
      <div><div class="card-title">Session Ledger</div><div class="card-sub">Per-member session usage timeline</div></div>
      <div style="display:flex;gap:8px;flex-wrap:wrap">
        <a class="btn btn-ghost btn-sm" href="${BASE+'/api/booking/admin/exports/checkins'}">Export CSV</a>
      </div>
    </div>
    <div class="card-body">
      <div class="fb" style="margin-bottom:12px">
        <input id="ledger-search" class="finput" placeholder="Search member/ref" value="${esc(fSearch)}"/>
        <input id="ledger-from" class="finput" type="date" value="${esc(fFrom)}"/>
        <input id="ledger-to" class="finput" type="date" value="${esc(fTo)}"/>
        <button class="btn btn-ghost btn-sm" onclick="renderMembershipLedger()">Filter</button>
      </div>
      ${rows.length?`<div class="tbl-wrap"><table>
        <thead><tr><th>When</th><th>Member</th><th>Appointment</th><th>Membership</th><th>Balance</th></tr></thead>
        <tbody>${rows.map(r=>`<tr>
          <td style="font-family:var(--mono);font-size:12px">${fmt(r.checked_in_at||'')}</td>
          <td><div style="font-weight:500">${esc(r.customer_name||'—')}</div><div style="font-size:11px;color:var(--muted)">${esc(r.customer_email||'')}</div></td>
          <td>${esc(r.appointment_ref||'—')}</td>
          <td>#${Number(r.membership_id||0)}</td>
          <td style="font-family:var(--mono);font-size:12px">${r.sessions_remaining??'—'} / ${r.sessions_total??'—'}</td>
        </tr>`).join('')}</tbody>
      </table></div>`:`<div class="empty" style="padding:24px 10px"><h3>No ledger records</h3></div>`}
    </div>
  </div>`;
}

async function bkSection(section){
  const BK=BASE+'/api/booking';
  const el=q('#bk-section-content');
  el.innerHTML='<div class="loading-state" style="padding:40px"><div class="spinner"></div><span>Loading…</span></div>';
  const mods=bookingModuleState||{};
  if(mods.bookings===false && section!=='module-settings'){
    el.innerHTML=`<div class="card fade-in"><div class="card-body"><div class="empty" style="padding:30px 20px"><h3>Bookings module is disabled</h3><p>Enable it from Modules tab to access this section.</p><button class="btn btn-ghost btn-sm" onclick="nav(null,'modules')">Open Modules</button></div></div></div>`;
    return;
  }
  if(mods.membership===false && ['plans','memberships','promo-codes'].includes(section)){
    el.innerHTML=`<div class="card fade-in"><div class="card-body"><div class="empty" style="padding:30px 20px"><h3>Membership module is disabled</h3><p>This section is hidden while membership is off.</p></div></div></div>`;
    return;
  }

  if(section==='appointments'){
    let rows=[];
    const apptSearch=(q('#bk-appt-search')?.value||'').trim();
    const apptStatus=(q('#bk-appt-status')?.value||'').trim();
    const apptDate=(q('#bk-appt-date')?.value||'').trim();
    const qs=new URLSearchParams({limit:'50'});
    if(apptSearch) qs.set('search',apptSearch);
    if(apptStatus) qs.set('status',apptStatus);
    if(apptDate) qs.set('date',apptDate);
    try{const j=await fetch(BK+'/admin/appointments?'+qs.toString(),{credentials:'same-origin'}).then(r=>r.json());if(j.ok)rows=j.appointments||[];}catch(e){}
    el.innerHTML=`<div class="card fade-in">
      <div class="card-hdr">
        <div><div class="card-title">Appointments</div><div class="card-sub">Upcoming &amp; recent bookings</div></div>
        <div style="display:flex;gap:6px;flex-wrap:wrap">
          <button class="btn btn-ghost btn-sm" onclick="bkBulkAppointmentStatus()">Bulk Status</button>
          <button class="btn btn-danger btn-sm" onclick="bkBulkAppointmentDelete()">Bulk Delete</button>
        </div>
      </div>
      <div class="card-body" style="padding-top:10px">
        <div class="fb">
          <input id="bk-appt-search" class="finput" placeholder="Search ref/member/service" value="${esc(apptSearch)}"/>
          <select id="bk-appt-status" class="fsel"><option value="">All status</option>${['pending','confirmed','cancelled','no_show','completed'].map(s=>`<option value="${s}" ${apptStatus===s?'selected':''}>${s}</option>`).join('')}</select>
          <input id="bk-appt-date" type="date" class="finput" value="${esc(apptDate)}"/>
          <button class="btn btn-ghost btn-sm" onclick="bkSection('appointments')">Filter</button>
        </div>
      </div>
      ${rows.length?`<div class="tbl-wrap"><table>
        <thead><tr><th><input type="checkbox" onchange="bkToggleAll('appt',this.checked)"/></th><th>Customer</th><th>Service</th><th>Provider</th><th>Date &amp; Time</th><th>Status</th><th>Actions</th></tr></thead>
        <tbody>${rows.map(a=>`<tr>
          <td><input class="bk-sel-appt" type="checkbox" value="${Number(a.id)}"/></td>
          <td><div style="font-weight:500">${esc(a.customer_name||a.customer_email||'—')}</div><div style="font-size:11px;color:var(--muted)">${esc(a.customer_email||'')}</div></td>
          <td>${esc(a.service_name||'—')}</td>
          <td>${esc(a.provider_name||'—')}</td>
          <td style="font-family:var(--mono);font-size:12px">${fmt(a.start_datetime||'')}</td>
          <td><select class="fsel" onchange="bkInlineAppointmentStatus(${Number(a.id)},this.value)">${['pending','confirmed','cancelled','no_show','completed'].map(s=>`<option value="${s}" ${a.status===s?'selected':''}>${s}</option>`).join('')}</select></td>
          <td>
            <div style="display:flex;gap:6px;flex-wrap:wrap">
              <button class="btn btn-danger btn-sm" onclick="bkDeleteAppointment(${Number(a.id)})">Delete</button>
            </div>
          </td>
        </tr>`).join('')}</tbody>
      </table></div>`
      :`<div class="empty" style="padding:40px 20px"><div class="empty-icon"><svg viewBox="0 0 24 24"><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"/></svg></div><h3>No appointments yet</h3><p>Appointments will appear here once customers book.</p></div>`}
    </div>`;
  } else if(section==='plans'){
    let rows=[];
    try{const j=await fetch(BK+'/plans',{credentials:'same-origin'}).then(r=>r.json());if(j.ok)rows=j.plans||[];}catch(e){}
    el.innerHTML=`<div class="card fade-in">
      <div class="card-hdr">
        <div><div class="card-title">Membership Plans</div><div class="card-sub">${rows.length} plan${rows.length!==1?'s':''}</div></div>
        <div style="display:flex;gap:8px;flex-wrap:wrap">
          <button class="btn btn-primary btn-sm" onclick="bkCreatePlan()">Add Plan</button>
          <a href="${esc(BASE+'/plans.php')}" target="_blank" class="btn btn-ghost btn-sm">View Public Page ↗</a>
        </div>
      </div>
      ${rows.length?`<div class="tbl-wrap"><table>
        <thead><tr><th>Plan</th><th>Price</th><th>Duration</th><th>Services</th><th>Status</th><th>Actions</th></tr></thead>
        <tbody>${rows.map(p=>`<tr>
          <td><div style="display:flex;align-items:center;gap:8px"><div style="width:10px;height:10px;border-radius:50%;background:${esc(p.color||'#e09500')};flex-shrink:0"></div><span style="font-weight:500">${esc(p.name)}</span></div><div style="font-size:11px;color:var(--muted);margin-top:2px">${esc((p.description||'').substring(0,60))}</div></td>
          <td style="font-family:var(--mono);font-weight:600;color:var(--accent)">$${parseFloat(p.price||0).toFixed(2)}</td>
          <td style="font-size:12px">${p.duration_days>=365?Math.round(p.duration_days/365)+' yr':p.duration_days>=28?Math.round(p.duration_days/30)+' mo':p.duration_days+' days'}</td>
          <td>${(p.services||[]).length} service${(p.services||[]).length!==1?'s':''}</td>
          <td><span class="badge ${p.is_active!==false?'badge-green':'badge-gray'}">${p.is_active!==false?'Active':'Inactive'}</span></td>
          <td>
            <div style="display:flex;gap:6px;flex-wrap:wrap">
              <button class="btn btn-ghost btn-sm" onclick="bkEditPlan(${Number(p.id)})">Edit</button>
              <button class="btn btn-danger btn-sm" onclick="bkDeletePlan(${Number(p.id)})">Delete</button>
            </div>
          </td>
        </tr>`).join('')}</tbody>
      </table></div>`
      :`<div class="empty" style="padding:40px 20px"><div class="empty-icon"><svg viewBox="0 0 24 24"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/></svg></div><h3>No plans yet</h3><p>Create membership plans to allow customers to book services.</p></div>`}
    </div>`;
  } else if(section==='services'){
    let rows=[];
    try{const j=await fetch(BK+'/admin/services',{credentials:'same-origin'}).then(r=>r.json());if(j.ok)rows=j.services||[];}catch(e){}
    el.innerHTML=`<div class="card fade-in">
      <div class="card-hdr">
        <div><div class="card-title">Services</div><div class="card-sub">${rows.length} service${rows.length!==1?'s':''}</div></div>
        <button class="btn btn-primary btn-sm" onclick="bkCreateService()">Add Service</button>
      </div>
      ${rows.length?`<div class="tbl-wrap"><table>
        <thead><tr><th>Service</th><th>Duration</th><th>Status</th><th>Actions</th></tr></thead>
        <tbody>${rows.map(s=>`<tr>
          <td><div style="display:flex;align-items:center;gap:8px"><div style="width:10px;height:10px;border-radius:50%;background:${esc(s.color||'#e09500')};flex-shrink:0"></div><span style="font-weight:500">${esc(s.name)}</span></div>${s.description?`<div style="font-size:11px;color:var(--muted);margin-top:2px">${esc(s.description.substring(0,60))}</div>`:''}</td>
          <td style="font-family:var(--mono);font-size:12px">${s.duration_min} min</td>
          <td><span class="badge ${s.is_active!==false?'badge-green':'badge-gray'}">${s.is_active!==false?'Active':'Inactive'}</span></td>
          <td>
            <div style="display:flex;gap:6px;flex-wrap:wrap">
              <button class="btn btn-ghost btn-sm" onclick="bkEditService(${Number(s.id)})">Edit</button>
              <button class="btn btn-danger btn-sm" onclick="bkDeleteService(${Number(s.id)})">Delete</button>
            </div>
          </td>
        </tr>`).join('')}</tbody>
      </table></div>`
      :`<div class="empty" style="padding:40px 20px"><div class="empty-icon"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg></div><h3>No services yet</h3><p>Add bookable services to get started.</p></div>`}
    </div>`;
  } else if(section==='providers'){
    let rows=[];
    try{const j=await fetch(BK+'/providers?service_id=0',{credentials:'same-origin'}).then(r=>r.json());if(j.ok)rows=j.providers||[];}catch(e){}
    // Fallback: try admin providers endpoint
    if(!rows.length){
      try{const j=await fetch(BK+'/admin/providers',{credentials:'same-origin'}).then(r=>r.json());if(j.ok)rows=j.providers||[];}catch(e){}
    }
    el.innerHTML=`<div class="card fade-in">
      <div class="card-hdr">
        <div><div class="card-title">Providers</div><div class="card-sub">${rows.length} provider${rows.length!==1?'s':''}</div></div>
        <button class="btn btn-primary btn-sm" onclick="bkCreateProvider()">Add Provider</button>
      </div>
      ${rows.length?`<div class="tbl-wrap"><table>
        <thead><tr><th>Provider</th><th>Branch</th><th>Capacity</th><th>Status</th><th>Actions</th></tr></thead>
        <tbody>${rows.map(p=>{const initials=(p.name||'?').split(' ').map(w=>w[0]).join('').substring(0,2).toUpperCase();return`<tr>
          <td><div style="display:flex;align-items:center;gap:9px">
            <div style="width:30px;height:30px;border-radius:50%;background:#fff8eb;display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:700;color:#d97706;flex-shrink:0;overflow:hidden">
              ${p.avatar_url?`<img src="${esc(p.avatar_url)}" style="width:100%;height:100%;object-fit:cover"/>`:initials}
            </div>
            <span style="font-weight:500">${esc(p.name)}</span>
          </div></td>
          <td>${esc(p.branch_name||'—')}</td>
          <td style="font-size:12px">${p.capacity_per_slot>1?'Up to '+p.capacity_per_slot:'1-on-1'}</td>
          <td><span class="badge ${p.is_active!==false?'badge-green':'badge-gray'}">${p.is_active!==false?'Active':'Inactive'}</span></td>
          <td>
            <div style="display:flex;gap:6px;flex-wrap:wrap">
              <button class="btn btn-ghost btn-sm" onclick="bkEditProvider(${Number(p.id)})">Edit</button>
              <button class="btn btn-danger btn-sm" onclick="bkDeleteProvider(${Number(p.id)})">Delete</button>
            </div>
          </td>
        </tr>`}).join('')}</tbody>
      </table></div>`
      :`<div class="empty" style="padding:40px 20px"><div class="empty-icon"><svg viewBox="0 0 24 24"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg></div><h3>No providers yet</h3><p>Add staff members or branches to accept bookings.</p></div>`}
    </div>`;
  } else if(section==='memberships'){
    let rows=[];
    const memSearch=(q('#bk-mem-search')?.value||'').trim();
    const memStatus=(q('#bk-mem-status')?.value||'').trim();
    const qs=new URLSearchParams({limit:'50'});
    if(memSearch) qs.set('search',memSearch);
    if(memStatus) qs.set('status',memStatus);
    try{const j=await fetch(BK+'/admin/memberships?'+qs.toString(),{credentials:'same-origin'}).then(r=>r.json());if(j.ok)rows=j.memberships||[];}catch(e){}
    el.innerHTML=`<div class="card fade-in">
      <div class="card-hdr"><div><div class="card-title">Memberships</div><div class="card-sub">${rows.length} membership${rows.length!==1?'s':''}</div></div>
        <div style="display:flex;gap:6px;flex-wrap:wrap">
          <button class="btn btn-ghost btn-sm" onclick="bkBulkMembershipStatus()">Bulk Status</button>
          <button class="btn btn-danger btn-sm" onclick="bkBulkMembershipDelete()">Bulk Delete</button>
        </div>
      </div>
      <div class="card-body" style="padding-top:10px">
        <div class="fb">
          <input id="bk-mem-search" class="finput" placeholder="Search member email/name" value="${esc(memSearch)}"/>
          <select id="bk-mem-status" class="fsel"><option value="">All status</option>${['active','expired','cancelled'].map(s=>`<option value="${s}" ${memStatus===s?'selected':''}>${s}</option>`).join('')}</select>
          <button class="btn btn-ghost btn-sm" onclick="bkSection('memberships')">Filter</button>
        </div>
      </div>
      ${rows.length?`<div class="tbl-wrap"><table>
        <thead><tr><th><input type="checkbox" onchange="bkToggleAll('mem',this.checked)"/></th><th>Customer</th><th>Plan</th><th>Sessions</th><th>Expires</th><th>Status</th></tr></thead>
        <tbody>${rows.map(m=>`<tr>
          <td><input class="bk-sel-mem" type="checkbox" value="${Number(m.id)}"/></td>
          <td><div style="font-weight:500">${esc(m.customer_name||m.customer_email||'—')}</div><div style="font-size:11px;color:var(--muted)">${esc(m.customer_email||'')}</div></td>
          <td>${esc(m.plan_name||'—')}</td>
          <td style="font-family:var(--mono);font-size:12px">${m.sessions_remaining??'—'} / ${m.sessions_total??'—'}</td>
          <td style="font-family:var(--mono);font-size:12px">${fmt(m.expires_at||'')}</td>
          <td><select class="fsel" onchange="bkInlineMembershipStatus(${Number(m.id)},this.value)">${['active','expired','cancelled'].map(s=>`<option value="${s}" ${m.status===s?'selected':''}>${s}</option>`).join('')}</select></td>
        </tr>`).join('')}</tbody>
      </table></div>`:`<div class="empty" style="padding:40px 20px"><h3>No memberships yet</h3></div>`}
    </div>`;
  } else if(section==='customers'){
    let rows=[];
    try{const j=await fetch(BK+'/admin/customers',{credentials:'same-origin'}).then(r=>r.json());if(j.ok)rows=j.customers||[];}catch(e){}
    el.innerHTML=`<div class="card fade-in">
      <div class="card-hdr"><div><div class="card-title">Customers</div><div class="card-sub">${rows.length} customer${rows.length!==1?'s':''}</div></div><button class="btn btn-primary btn-sm" onclick="bkCreateCustomer()">Add Customer</button></div>
      ${rows.length?`<div class="tbl-wrap"><table>
        <thead><tr><th>Name</th><th>Email</th><th>Phone</th><th>Profile</th><th>Created</th><th>Actions</th></tr></thead>
        <tbody>${rows.map(c=>`<tr>
          <td style="font-weight:500">${esc(c.name||'—')}</td>
          <td>${esc(c.email||'—')}</td>
          <td>${esc(c.phone||'—')}</td>
          <td style="font-size:11px;color:var(--muted)">${esc(c.city||'—')}, ${esc(c.country||'—')} · ${esc(c.gender||'—')}</td>
          <td style="font-family:var(--mono);font-size:12px">${fmt(c.created_at||'')}</td>
          <td><div style="display:flex;gap:6px;flex-wrap:wrap"><button class="btn btn-ghost btn-sm" onclick="bkEditCustomer(${Number(c.id)})">Edit</button><button class="btn btn-danger btn-sm" onclick="bkDeleteCustomer(${Number(c.id)})">Delete</button></div></td>
        </tr>`).join('')}</tbody>
      </table></div>`:`<div class="empty" style="padding:40px 20px"><h3>No customers yet</h3></div>`}
    </div>`;
  } else if(section==='promo-codes'){
    let rows=[];
    try{const j=await fetch(BK+'/admin/promo-codes',{credentials:'same-origin'}).then(r=>r.json());if(j.ok)rows=j.promos||[];}catch(e){}
    el.innerHTML=`<div class="card fade-in">
      <div class="card-hdr"><div><div class="card-title">Promo Codes</div><div class="card-sub">${rows.length} code${rows.length!==1?'s':''}</div></div></div>
      ${rows.length?`<div class="tbl-wrap"><table>
        <thead><tr><th>Code</th><th>Discount</th><th>Uses</th><th>Valid Until</th><th>Status</th></tr></thead>
        <tbody>${rows.map(p=>`<tr>
          <td style="font-family:var(--mono);font-weight:600">${esc(p.code||'—')}</td>
          <td>${p.discount_type==='percent'?`${p.discount_value}%`:`$${Number(p.discount_value||0).toFixed(2)}`}</td>
          <td>${p.uses_count||0}${p.max_uses?` / ${p.max_uses}`:''}</td>
          <td style="font-family:var(--mono);font-size:12px">${fmt(p.valid_until||'')}</td>
          <td><span class="badge ${p.is_active? 'badge-green':'badge-gray'}">${p.is_active?'Active':'Inactive'}</span></td>
        </tr>`).join('')}</tbody>
      </table></div>`:`<div class="empty" style="padding:40px 20px"><h3>No promo codes yet</h3></div>`}
    </div>`;
  } else if(section==='waitlist'){
    let rows=[];
    try{const j=await fetch(BK+'/admin/waitlist',{credentials:'same-origin'}).then(r=>r.json());if(j.ok)rows=j.waitlist||[];}catch(e){}
    el.innerHTML=`<div class="card fade-in">
      <div class="card-hdr"><div><div class="card-title">Waitlist</div><div class="card-sub">${rows.length} pending request${rows.length!==1?'s':''}</div></div></div>
      ${rows.length?`<div class="tbl-wrap"><table>
        <thead><tr><th>Customer</th><th>Service</th><th>Provider</th><th>Requested Slot</th></tr></thead>
        <tbody>${rows.map(w=>`<tr>
          <td><div style="font-weight:500">${esc(w.customer_name||'—')}</div><div style="font-size:11px;color:var(--muted)">${esc(w.customer_email||'')}</div></td>
          <td>${esc(w.service_name||'—')}</td>
          <td>${esc(w.provider_name||'—')}</td>
          <td style="font-family:var(--mono);font-size:12px">${fmt(w.slot_datetime||'')}</td>
        </tr>`).join('')}</tbody>
      </table></div>`:`<div class="empty" style="padding:40px 20px"><h3>No waitlist requests</h3></div>`}
    </div>`;
  } else if(section==='ratings'){
    let rows=[];
    try{const j=await fetch(BK+'/admin/ratings?limit=30',{credentials:'same-origin'}).then(r=>r.json());if(j.ok)rows=j.ratings||[];}catch(e){}
    el.innerHTML=`<div class="card fade-in">
      <div class="card-hdr"><div><div class="card-title">Ratings</div><div class="card-sub">${rows.length} review${rows.length!==1?'s':''}</div></div></div>
      ${rows.length?`<div class="tbl-wrap"><table>
        <thead><tr><th>Customer</th><th>Service</th><th>Provider</th><th>Rating</th><th>Comment</th></tr></thead>
        <tbody>${rows.map(r=>`<tr>
          <td><div style="font-weight:500">${esc(r.customer_name||'—')}</div><div style="font-size:11px;color:var(--muted)">${esc(r.customer_email||'')}</div></td>
          <td>${esc(r.service_name||'—')}</td>
          <td>${esc(r.provider_name||'—')}</td>
          <td style="font-family:var(--mono);font-weight:600;color:var(--accent)">${'★'.repeat(Number(r.rating||0))}</td>
          <td>${esc((r.comment||'').substring(0,80)||'—')}</td>
        </tr>`).join('')}</tbody>
      </table></div>`:`<div class="empty" style="padding:40px 20px"><h3>No ratings yet</h3></div>`}
    </div>`;
  } else if(section==='module-settings'){
    let mods={}; let stripe={};
    try{const j=await fetch(BK+'/admin/modules',{credentials:'same-origin'}).then(r=>r.json());if(j.ok)mods=j.modules||{};}catch(e){}
    try{const j=await fetch(BK+'/admin/stripe-config',{credentials:'same-origin'}).then(r=>r.json());if(j.ok)stripe=j.stripe||{};}catch(e){}
    el.innerHTML=`<div class="card fade-in">
      <div class="card-hdr"><div><div class="card-title">Booking Settings</div><div class="card-sub">Quick overview for modules and Stripe mode</div></div></div>
      <div class="card-body">
        <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:10px">
          <div style="padding:12px;border:1px solid var(--rule);border-radius:var(--r8);background:#fff">
            <div style="font-size:12px;color:var(--muted);margin-bottom:4px">Modules</div>
            <div style="font-size:13px">Forms: <strong>${mods.forms?'On':'Off'}</strong></div>
            <div style="font-size:13px">Bookings: <strong>${mods.bookings?'On':'Off'}</strong></div>
            <div style="font-size:13px">Membership: <strong>${mods.membership?'On':'Off'}</strong></div>
            <div style="font-size:13px">QR Check-in: <strong>${mods.qr_checkin?'On':'Off'}</strong></div>
          </div>
          <div style="padding:12px;border:1px solid var(--rule);border-radius:var(--r8);background:#fff">
            <div style="font-size:12px;color:var(--muted);margin-bottom:4px">Stripe</div>
            <div style="font-size:13px">Mode: <strong>${esc(stripe.mode||'test')}</strong></div>
            <div style="font-size:13px">Currency: <strong>${esc((stripe.currency||'usd').toUpperCase())}</strong></div>
          </div>
        </div>
        <div style="margin-top:10px;display:flex;gap:8px;flex-wrap:wrap">
          <button class="btn btn-ghost btn-sm" onclick="nav(null,'modules')">Open Modules Tab</button>
        </div>
      </div>
    </div>`;
  }
}

function applyBookingModuleVisibility(mods){
  const allBtns=[...document.querySelectorAll('#bk-section-content').length?[]:[]];
  const qa=[...document.querySelectorAll('.quick-actions .qa-btn')];
  const hideBySection=(sec,hide)=>{
    const btn=qa.find(b=>(b.getAttribute('onclick')||'').includes(`bkSection('${sec}')`));
    if(btn) btn.style.display=hide?'none':'';
  };
  if(mods.bookings===false){
    ['appointments','plans','services','providers','memberships','customers','promo-codes','waitlist','ratings'].forEach(s=>hideBySection(s,true));
  } else {
    ['appointments','services','providers','customers','waitlist','ratings','module-settings'].forEach(s=>hideBySection(s,false));
    const membershipHidden = mods.membership===false;
    ['plans','memberships','promo-codes'].forEach(s=>hideBySection(s,membershipHidden));
  }
}

async function syncModuleNavigation(){
  try{
    const j=await fetch(BASE+'/api/booking/config',{credentials:'same-origin'}).then(r=>r.json()).catch(()=>({}));
    if(j&&j.modules) bookingModuleState=j.modules;
  }catch(e){}
  const mods=bookingModuleState||{};
  const showByPage=(page,show)=>{
    document.querySelectorAll(`.ni[data-page="${page}"], .mni[data-page="${page}"]`).forEach(el=>{el.style.display=show?'':'none';});
  };
  const showForms = mods.forms !== false;
  const showBookings = mods.bookings !== false;
  const showMembership = showBookings && mods.membership !== false;

  showByPage('forms', showForms);
  showByPage('submissions', showForms);
  const cgt=q('#content-group-toggle'); if(cgt) cgt.style.display=showForms?'':'none';
  const cgi=q('#content-group-items'); if(cgi) cgi.style.display=showForms?'':'none';
  const formBuilder=q('#nav-form-builder'); if(formBuilder) formBuilder.style.display=showForms?'':'none';
  const mobFormBuilder=q('#mob-form-builder'); if(mobFormBuilder) mobFormBuilder.style.display=showForms?'':'none';
  showByPage('booking', showBookings);
  ['booking-customers','booking-memberships','booking-services','booking-providers','booking-promo-codes','booking-waitlist','booking-ratings']
    .forEach(p=>showByPage(p, showBookings));
  showByPage('membership-plans', showMembership);
  showByPage('membership-payments', showMembership);
  showByPage('membership-history', showMembership);
  showByPage('membership-ledger', showMembership);
  const bgt=q('#booking-group-toggle'); if(bgt) bgt.style.display=showBookings?'':'none';
  const bgi=q('#booking-group-items'); if(bgi) bgi.style.display=showBookings?'':'none';
  const mgt=q('#membership-group-toggle'); if(mgt) mgt.style.display=showMembership?'':'none';
  const mgi=q('#membership-group-items'); if(mgi) mgi.style.display=showMembership?'':'none';
}

/* ── BOOKING CRUD HELPERS ── */
async function bkApi(path, method='GET', body){
  const opts={method,credentials:'same-origin',headers:{}};
  if(body!==undefined){opts.headers['Content-Type']='application/json';opts.body=JSON.stringify(body);}
  const r=await fetch(BASE+'/api/booking'+path,opts);
  const j=await r.json().catch(()=>({ok:false,error:'Invalid server response'}));
  return j&&typeof j==='object'?j:{ok:false,error:'Invalid server response'};
}
function bkRefresh(){
  ['appointments','plans','services','providers','memberships','customers','promo-codes','waitlist','ratings'].forEach(s=>bkSection(s));
}
function bkToggleAll(type, checked){
  document.querySelectorAll(type==='appt'?'.bk-sel-appt':'.bk-sel-mem').forEach(cb=>{cb.checked=!!checked;});
}
function bkSelectedIds(type){
  return [...document.querySelectorAll(type==='appt'?'.bk-sel-appt:checked':'.bk-sel-mem:checked')].map(cb=>Number(cb.value)).filter(Boolean);
}
async function bkInlineAppointmentStatus(id,status){
  const j=await bkApi('/admin/appointments/'+Number(id),'PUT',{status});
  if(!j.ok) toast(j.error||'Update failed','err');
}
async function bkInlineMembershipStatus(id,status){
  const j=await bkApi('/admin/memberships/'+Number(id),'PUT',{status});
  if(!j.ok) toast(j.error||'Update failed','err');
}
async function bkBulkAppointmentStatus(){
  const ids=bkSelectedIds('appt'); if(!ids.length){toast('Select appointments first','err');return;}
  const status=prompt('Set status for selected: pending, confirmed, cancelled, no_show, completed','confirmed');
  if(!status) return;
  const j=await bkApi('/admin/appointments/bulk','POST',{ids,action:'set_status',status:String(status).trim()});
  if(j.ok){toast('Bulk status updated','ok');bkSection('appointments');} else toast(j.error||'Bulk update failed','err');
}
async function bkBulkAppointmentDelete(){
  const ids=bkSelectedIds('appt'); if(!ids.length){toast('Select appointments first','err');return;}
  if(!confirm('Delete selected appointments?')) return;
  const j=await bkApi('/admin/appointments/bulk','POST',{ids,action:'delete'});
  if(j.ok){toast('Bulk delete done','ok');bkSection('appointments');} else toast(j.error||'Bulk delete failed','err');
}
async function bkBulkMembershipStatus(){
  const ids=bkSelectedIds('mem'); if(!ids.length){toast('Select memberships first','err');return;}
  const status=prompt('Set status for selected: active, expired, cancelled','active');
  if(!status) return;
  const j=await bkApi('/admin/memberships/bulk','POST',{ids,action:'set_status',status:String(status).trim()});
  if(j.ok){toast('Bulk status updated','ok');bkSection('memberships');} else toast(j.error||'Bulk update failed','err');
}
async function bkBulkMembershipDelete(){
  const ids=bkSelectedIds('mem'); if(!ids.length){toast('Select memberships first','err');return;}
  if(!confirm('Delete selected memberships?')) return;
  const j=await bkApi('/admin/memberships/bulk','POST',{ids,action:'delete'});
  if(j.ok){toast('Bulk delete done','ok');bkSection('memberships');} else toast(j.error||'Bulk delete failed','err');
}

async function bkUpdateAppointmentStatus(id, currentStatus){
  const next=prompt('Set status: pending, confirmed, cancelled, no_show, completed', currentStatus||'pending');
  if(!next) return;
  const j=await bkApi('/admin/appointments/'+Number(id),'PUT',{status:next.trim()});
  if(j.ok){toast('Appointment updated','ok');bkSection('appointments');} else {toast(j.error||'Update failed','err');}
}
async function bkDeleteAppointment(id){
  if(!confirm('Delete this appointment? This cannot be undone.')) return;
  const j=await bkApi('/admin/appointments/'+Number(id),'DELETE');
  if(j.ok){toast('Appointment deleted','ok');bkSection('appointments');} else {toast(j.error||'Delete failed','err');}
}

async function bkCreatePlan(){
  bkOpenCrudModal('Create Plan',[
    {key:'name',label:'Plan name',required:true},
    {key:'price',label:'Price',type:'number',required:true},
    {key:'duration_days',label:'Duration days',type:'number',required:true},
    {key:'session_limit',label:'Session limit',type:'number'},
    {key:'validity_days',label:'Validity days',type:'number'}
  ],{price:'0',duration_days:'30'}, async (p)=>{
    const j=await bkApi('/admin/plans','POST',{name:p.name,price:Number(p.price||0),duration_days:Number(p.duration_days||30),session_limit:p.session_limit===''?null:Number(p.session_limit),validity_days:p.validity_days===''?null:Number(p.validity_days),is_active:1});
    if(j.ok){closeModal();toast('Plan created','ok');bkSection('plans');} else {toast(j.error||'Create failed','err');}
  });
}
async function bkEditPlan(id){
  const g=await bkApi('/admin/plans/'+Number(id));
  if(!g.ok||!g.plan){toast(g.error||'Failed to load plan','err');return;}
  const p=g.plan;
  bkOpenCrudModal('Edit Plan',[
    {key:'name',label:'Plan name',required:true},
    {key:'price',label:'Price',type:'number',required:true},
    {key:'duration_days',label:'Duration days',type:'number',required:true},
    {key:'session_limit',label:'Session limit',type:'number'},
    {key:'validity_days',label:'Validity days',type:'number'},
    {key:'is_active',label:'Active',type:'select',options:[{value:'1',label:'Yes'},{value:'0',label:'No'}]}
  ],{
    name:p.name||'',price:String(p.price??0),duration_days:String(p.duration_days??30),
    session_limit:p.session_limit===null?'':String(p.session_limit),validity_days:p.validity_days===null?'':String(p.validity_days),is_active:String(p.is_active??1)
  }, async (x)=>{
    const j=await bkApi('/admin/plans/'+Number(id),'PUT',{name:x.name,price:Number(x.price||0),duration_days:Number(x.duration_days||30),is_active:Number(x.is_active||1),session_limit:x.session_limit===''?null:Number(x.session_limit),validity_days:x.validity_days===''?null:Number(x.validity_days)});
    if(j.ok){closeModal();toast('Plan updated','ok');bkSection('plans');} else {toast(j.error||'Update failed','err');}
  });
}
async function bkDeletePlan(id){
  if(!confirm('Delete this plan?')) return;
  const j=await bkApi('/admin/plans/'+Number(id),'DELETE');
  if(j.ok){toast('Plan deleted','ok');bkSection('plans');} else {toast(j.error||'Delete failed','err');}
}

async function bkCreateService(){
  bkOpenCrudModal('Create Service',[
    {key:'name',label:'Service name',required:true},
    {key:'duration_min',label:'Duration (minutes)',type:'number',required:true},
    {key:'is_restricted',label:'Members only',type:'select',options:[{value:'1',label:'Yes'},{value:'0',label:'No'}]}
  ],{duration_min:'60',is_restricted:'1'}, async (p)=>{
    const j=await bkApi('/admin/services','POST',{name:p.name,duration_min:Number(p.duration_min||60),is_restricted:Number(p.is_restricted||0),is_active:1});
    if(j.ok){closeModal();toast('Service created','ok');bkSection('services');} else {toast(j.error||'Create failed','err');}
  });
}
async function bkEditService(id){
  const g=await bkApi('/admin/services/'+Number(id));
  if(!g.ok||!g.service){toast(g.error||'Failed to load service','err');return;}
  const s=g.service;
  bkOpenCrudModal('Edit Service',[
    {key:'name',label:'Service name',required:true},
    {key:'duration_min',label:'Duration (minutes)',type:'number',required:true},
    {key:'is_active',label:'Active',type:'select',options:[{value:'1',label:'Yes'},{value:'0',label:'No'}]},
    {key:'is_restricted',label:'Members only',type:'select',options:[{value:'1',label:'Yes'},{value:'0',label:'No'}]}
  ],{name:s.name||'',duration_min:String(s.duration_min??60),is_active:String(s.is_active??1),is_restricted:String(s.is_restricted??1)}, async (p)=>{
    const j=await bkApi('/admin/services/'+Number(id),'PUT',{name:p.name,duration_min:Number(p.duration_min||60),is_active:Number(p.is_active||1),is_restricted:Number(p.is_restricted||0)});
    if(j.ok){closeModal();toast('Service updated','ok');bkSection('services');} else {toast(j.error||'Update failed','err');}
  });
}
async function bkDeleteService(id){
  if(!confirm('Delete this service?')) return;
  const j=await bkApi('/admin/services/'+Number(id),'DELETE');
  if(j.ok){toast('Service deleted','ok');bkSection('services');} else {toast(j.error||'Delete failed','err');}
}

async function bkCreateProvider(){
  bkOpenCrudModal('Create Provider',[
    {key:'name',label:'Provider name',required:true},
    {key:'branch_name',label:'Branch'},
    {key:'email',label:'Email',type:'email'},
    {key:'phone',label:'Phone'}
  ],{}, async (p)=>{
    const j=await bkApi('/admin/providers','POST',{name:p.name,branch_name:p.branch_name,email:p.email,phone:p.phone,is_active:1});
    if(j.ok){closeModal();toast('Provider created','ok');bkSection('providers');} else {toast(j.error||'Create failed','err');}
  });
}
async function bkEditProvider(id){
  const g=await bkApi('/admin/providers/'+Number(id));
  if(!g.ok||!g.provider){toast(g.error||'Failed to load provider','err');return;}
  const p=g.provider;
  bkOpenCrudModal('Edit Provider',[
    {key:'name',label:'Provider name',required:true},
    {key:'branch_name',label:'Branch'},
    {key:'email',label:'Email',type:'email'},
    {key:'phone',label:'Phone'},
    {key:'is_active',label:'Active',type:'select',options:[{value:'1',label:'Yes'},{value:'0',label:'No'}]}
  ],{name:p.name||'',branch_name:p.branch_name||'',email:p.email||'',phone:p.phone||'',is_active:String(p.is_active??1)}, async (x)=>{
    const j=await bkApi('/admin/providers/'+Number(id),'PUT',{name:x.name,branch_name:x.branch_name,email:x.email,phone:x.phone,is_active:Number(x.is_active||1)});
    if(j.ok){closeModal();toast('Provider updated','ok');bkSection('providers');} else {toast(j.error||'Update failed','err');}
  });
}
async function bkDeleteProvider(id){
  if(!confirm('Delete this provider?')) return;
  const j=await bkApi('/admin/providers/'+Number(id),'DELETE');
  if(j.ok){toast('Provider deleted','ok');bkSection('providers');} else {toast(j.error||'Delete failed','err');}
}

function bkOpenCrudModal(title, fields, values, onSave){
  openModal(title);
  q('#modal-body').innerHTML = fields.map(f=>{
    const val = values && values[f.key] !== undefined && values[f.key] !== null ? String(values[f.key]) : '';
    if (f.type === 'select') {
      return `<div class="fg"><label>${esc(f.label)}</label><select id="crud-${esc(f.key)}">${(f.options||[]).map(o=>`<option value="${esc(o.value)}" ${String(o.value)===val?'selected':''}>${esc(o.label)}</option>`).join('')}</select></div>`;
    }
    return `<div class="fg"><label>${esc(f.label)}</label><input id="crud-${esc(f.key)}" type="${esc(f.type||'text')}" value="${esc(val)}" placeholder="${esc(f.placeholder||'')}"/></div>`;
  }).join('');
  q('#modal-foot').innerHTML = `<button class="btn btn-ghost" onclick="closeModal()">Cancel</button><button class="btn btn-primary" id="crud-save-btn">Save</button>`;
  q('#crud-save-btn').onclick = async ()=>{
    const payload={};
    for(const f of fields){ payload[f.key] = (q('#crud-'+f.key)?.value ?? '').trim(); }
    const err = fields.find(f=>f.required && !payload[f.key]);
    if(err){ toast(`${err.label} is required`,'err'); return; }
    await onSave(payload);
  };
}

async function bkCreateCustomer(){
  bkOpenCrudModal('Create Customer',[
    {key:'email',label:'Email',type:'email',required:true},
    {key:'name',label:'Full name',required:true},
    {key:'phone',label:'Phone'},
    {key:'dob',label:'Date of birth',type:'date'},
    {key:'gender',label:'Gender',type:'select',options:[{value:'',label:'-'},{value:'male',label:'male'},{value:'female',label:'female'},{value:'other',label:'other'}]},
    {key:'address_line1',label:'Address'},
    {key:'city',label:'City'},
    {key:'country',label:'Country'},
    {key:'emergency_contact_name',label:'Emergency contact name'},
    {key:'emergency_contact_phone',label:'Emergency contact phone'}
  ],{}, async (p)=>{
    const j=await bkApi('/admin/customers','POST',p);
    if(j.ok){closeModal();toast('Customer created','ok');bkSection('customers');} else {toast(j.error||'Create failed','err');}
  });
}
async function bkEditCustomer(id){
  const g=await bkApi('/admin/customers');
  const row=(g.customers||[]).find(c=>Number(c.id)===Number(id));
  if(!row){toast('Customer not found','err');return;}
  bkOpenCrudModal('Edit Customer',[
    {key:'email',label:'Email',type:'email',required:true},
    {key:'name',label:'Full name',required:true},
    {key:'phone',label:'Phone'},
    {key:'dob',label:'Date of birth',type:'date'},
    {key:'gender',label:'Gender',type:'select',options:[{value:'',label:'-'},{value:'male',label:'male'},{value:'female',label:'female'},{value:'other',label:'other'}]},
    {key:'address_line1',label:'Address'},
    {key:'city',label:'City'},
    {key:'country',label:'Country'},
    {key:'emergency_contact_name',label:'Emergency contact name'},
    {key:'emergency_contact_phone',label:'Emergency contact phone'}
  ],row, async (p)=>{
    const j=await bkApi('/admin/customers/'+Number(id),'PUT',p);
    if(j.ok){closeModal();toast('Customer updated','ok');bkSection('customers');} else {toast(j.error||'Update failed','err');}
  });
}
async function bkDeleteCustomer(id){
  if(!confirm('Delete this customer? This cannot be undone.')) return;
  const j=await bkApi('/admin/customers/'+Number(id),'DELETE');
  if(j.ok){toast('Customer deleted','ok');bkSection('customers');} else {toast(j.error||'Delete failed','err');}
}

/* ── MODAL ── */
function openModal(title){
  q('#modal-title').textContent=title;
  q('#modal-body').innerHTML='';q('#modal-foot').innerHTML='';
  q('#modal-overlay').classList.add('open');
}
function closeModal(){q('#modal-overlay').classList.remove('open');}

/* ── KEYBOARD SHORTCUTS ── */
document.addEventListener('keydown',e=>{
  if(e.key==='Escape'){closeModal();closeSidebar();}
  if(e.key==='/'&&!['INPUT','TEXTAREA','SELECT'].includes(document.activeElement.tagName)){
    e.preventDefault();const s=q('.tb-search input');if(s)s.focus();
  }
});

checkAuth();
</script>
</body>
</html>