const $ = (sel, root = document) => root.querySelector(sel);
const $$ = (sel, root = document) => [...root.querySelectorAll(sel)];

const ADMIN_BLANK_FAVICON =
  "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'/>";

/** Admin sekmesinde ana site / eski CDN favicon görünmesin. */
function ensureAdminNoFavicon() {
  if (typeof document === "undefined") return;
  document.querySelectorAll("link[rel]").forEach((el) => {
    const r = (el.getAttribute("rel") || "").toLowerCase();
    if (!r.includes("icon") && !r.includes("apple-touch")) return;
    const href = el.getAttribute("href") || "";
    if (el.hasAttribute("data-admin-favicon")) return;
    if (href && href !== ADMIN_BLANK_FAVICON) el.remove();
  });
  let link = document.querySelector("link[data-admin-favicon]");
  if (!link) {
    link = document.createElement("link");
    link.rel = "icon";
    link.setAttribute("data-admin-favicon", "1");
    document.head.appendChild(link);
  }
  link.href = ADMIN_BLANK_FAVICON;
}

ensureAdminNoFavicon();

const ADMIN_THEME_STORAGE_KEY = "mocker-admin-theme";

function normalizeAdminTheme(value) {
  return value === "dark" || value === "light" ? value : null;
}

function getAdminTheme() {
  const attr = normalizeAdminTheme(document.documentElement.getAttribute("data-admin-theme"));
  if (attr) return attr;
  try {
    return normalizeAdminTheme(localStorage.getItem(ADMIN_THEME_STORAGE_KEY)) || "light";
  } catch {
    return "light";
  }
}

function applyAdminTheme(theme, persist = true) {
  const t = normalizeAdminTheme(theme) || "light";
  document.documentElement.setAttribute("data-admin-theme", t);
  document.documentElement.style.colorScheme = t;
  const meta = document.querySelector('meta[name="theme-color"]');
  if (meta) meta.setAttribute("content", t === "dark" ? "#181f2e" : "#ffffff");
  const metaScheme = document.querySelector('meta[name="color-scheme"]');
  if (metaScheme) metaScheme.setAttribute("content", t);
  if (persist) {
    try {
      localStorage.setItem(ADMIN_THEME_STORAGE_KEY, t);
    } catch {
      /* ignore */
    }
  }
  const isDark = t === "dark";
  document.querySelectorAll("[data-admin-theme-toggle]").forEach((btn) => {
    btn.setAttribute("aria-pressed", isDark ? "true" : "false");
    btn.setAttribute("title", isDark ? "Açık temaya geç" : "Koyu temaya geç");
    const label = btn.querySelector(".admin-theme-toggle__label");
    if (label) label.textContent = isDark ? "Açık tema" : "Koyu tema";
  });
}

function toggleAdminTheme() {
  applyAdminTheme(getAdminTheme() === "dark" ? "light" : "dark");
}

function adminThemeToggleHtml({ compact = false } = {}) {
  const cls = compact
    ? "admin-theme-toggle admin-theme-toggle--compact"
    : "admin-theme-toggle";
  const label = compact
    ? ""
    : `<span class="admin-theme-toggle__label">Koyu tema</span>`;
  return `<button type="button" class="${cls}" data-admin-theme-toggle aria-pressed="false" title="Tema değiştir">
    <span class="admin-theme-toggle__ico-wrap" aria-hidden="true">
      <svg class="admin-theme-toggle__icon admin-theme-toggle__icon--sun" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"/></svg>
      <svg class="admin-theme-toggle__icon admin-theme-toggle__icon--moon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"/></svg>
    </span>
    ${label}
  </button>`;
}

function bindAdminThemeToggle(root = document) {
  root.querySelectorAll("[data-admin-theme-toggle]").forEach((btn) => {
    if (btn.dataset.adminThemeBound === "1") return;
    btn.dataset.adminThemeBound = "1";
    btn.addEventListener("click", () => toggleAdminTheme());
  });
  applyAdminTheme(getAdminTheme(), false);
}

applyAdminTheme(getAdminTheme(), false);

function escapeHtml(s) {
  return String(s)
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&#39;");
}

function formatTs(ts) {
  if (!ts) return "—";
  try {
    return new Date(Number(ts) * 1000).toLocaleString("tr-TR", {
      dateStyle: "short",
      timeStyle: "short",
    });
  } catch {
    return "—";
  }
}

function adminWorkflowStatusBadge(status) {
  const s = String(status || "pending");
  if (s === "approved") return '<span class="admin-badge admin-badge--approved">Onaylandı</span>';
  if (s === "rejected") return '<span class="admin-badge admin-badge--rejected">Reddedildi</span>';
  return '<span class="admin-badge admin-badge--pending">Beklemede</span>';
}

function adminActiveBadge(active) {
  return active
    ? '<span class="admin-badge admin-badge--active">Aktif</span>'
    : '<span class="admin-badge admin-badge--inactive">Pasif</span>';
}

function adminCategoryBadge(category) {
  const c = String(category || "other");
  if (c === "sponsor") return '<span class="admin-badge admin-badge--info">Sponsor</span>';
  return '<span class="admin-badge admin-badge--violet">Diğer</span>';
}

function adminSearchClearBtn(clearId) {
  return `<button type="button" id="${clearId}" class="admin-data-search-clear admin-icon-btn-close" aria-label="Aramayı temizle"><svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/></svg></button>`;
}

function adminListToolbar({ searchId, label, placeholder, value, clearId, countText, extra = "" }) {
  const q = value || "";
  return `<div class="admin-data-toolbar">
    <div class="admin-data-toolbar__main">
      <label class="admin-label" for="${searchId}">${escapeHtml(label)}</label>
      <div class="admin-data-toolbar__field">
        <input id="${searchId}" type="search" class="admin-input" placeholder="${escapeHtml(placeholder)}" value="${escapeHtml(q)}" autocomplete="off" />
        ${q && clearId ? adminSearchClearBtn(clearId) : ""}
      </div>
    </div>
    <div class="admin-data-toolbar__side">
      <span class="admin-data-count">${countText}</span>
      ${extra}
    </div>
  </div>`;
}

function adminWorkflowActions(status, btnClass, idAttr, idValue) {
  const approve = `<button type="button" class="admin-action-btn admin-action-btn--approve ${btnClass}" ${idAttr}="${idValue}" data-status="approved">Onayla</button>`;
  const reject = `<button type="button" class="admin-action-btn admin-action-btn--reject ${btnClass}" ${idAttr}="${idValue}" data-status="rejected">Reddet</button>`;
  if (status === "approved") return reject;
  if (status === "rejected") return approve;
  return `<div class="admin-action-group">${approve}${reject}</div>`;
}

function adminToggleUserBtn(active, userId) {
  return `<button type="button" class="btn-toggle-user-active admin-action-btn ${active ? "admin-action-btn--toggle-off" : "admin-action-btn--toggle-on"}" data-user-id="${userId}" data-next-active="${active ? "0" : "1"}">${active ? "Pasife çek" : "Aktife al"}</button>`;
}

function adminDataTableShell(theadCells, tbodyHtml, minWidth = "960px") {
  return `<div class="admin-data-panel">
    <div class="admin-table-wrap admin-data-table-wrap">
      <table class="admin-data-table" style="min-width:${minWidth}">
        <thead><tr>${theadCells}</tr></thead>
        <tbody>${tbodyHtml}</tbody>
      </table>
    </div>
  </div>`;
}

function cmsModalCloseFromClick(e, closeAttr, closeFn) {
  const key = closeAttr || "data-close-modal";
  const t = e.target;
  if (t && typeof t.closest === "function" && t.closest(`[${key}]`)) {
    closeFn();
  }
}

function adminCmsModalShell({ backdropId, closeAttr, titleId, eyebrow, title, lead, bodyHtml, saveId, cancelId }) {
  const closeKey = closeAttr || "data-close-modal";
  return `
    <div id="${backdropId}" class="admin-cms-modal" role="dialog" aria-modal="true" aria-labelledby="${titleId}">
      <div class="admin-cms-modal__backdrop" ${closeKey}></div>
      <div class="admin-cms-modal__panel">
        <header class="admin-cms-modal__head">
          <div class="admin-cms-modal__head-text">
            <span class="admin-cms-modal__eyebrow">${escapeHtml(eyebrow)}</span>
            <h2 id="${titleId}" class="admin-cms-modal__title">${escapeHtml(title)}</h2>
            <p class="admin-cms-modal__lead">${lead}</p>
          </div>
          <button type="button" class="admin-cms-modal__close admin-icon-btn-close" ${closeKey} aria-label="Kapat">
            <svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/></svg>
          </button>
        </header>
        <div class="admin-cms-modal__body">${bodyHtml}</div>
        <footer class="admin-cms-modal__foot">
          <button type="button" id="${cancelId}" class="admin-btn admin-btn-ghost admin-cms-modal__btn-cancel">İptal</button>
          <button type="button" id="${saveId}" class="admin-btn admin-btn-primary admin-cms-modal__btn-save">Kaydet</button>
        </footer>
      </div>
    </div>`;
}

function bindCmsModalLogoPreview(inputSel, imgSel, wrapSel) {
  const input = $(inputSel);
  const img = $(imgSel);
  const wrap = $(wrapSel);
  if (!input || !img || !wrap) return;
  const update = () => {
    const v = String(input.value || "").trim();
    if (/^https?:\/\//i.test(v)) {
      img.src = v;
      wrap.hidden = false;
    } else {
      wrap.hidden = true;
      img.removeAttribute("src");
    }
  };
  input.addEventListener("input", update);
  update();
}

function adminCmsLogoHtml(logoUrl, title, fallbackClass, emptyLabel = "Logo yok") {
  const initial = (title || "?").slice(0, 2).toUpperCase();
  const url = logoUrl && String(logoUrl).trim();
  if (url) {
    return `<div class="admin-cms-card__logo"><img src="${escapeHtml(url)}" alt="" loading="lazy" decoding="async" onerror="this.style.display='none';this.parentElement.querySelector('.${fallbackClass}')?.classList.remove('hidden')"/><span class="${fallbackClass} hidden text-xs font-black text-slate-400">${escapeHtml(initial)}</span></div>`;
  }
  return `<div class="admin-cms-card__logo admin-cms-card__logo--empty">${escapeHtml(emptyLabel)}</div>`;
}

const ADMIN_CMS_EDIT_ICON = `<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/></svg>`;

const ADMIN_CMS_DRAG_ICON = `<svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-width="2" d="M4 8h16M4 16h16"/></svg>`;

function socialIconPreviewHtml(icon, name) {
  const frag = String(icon || "").trim();
  const initial = (name || "?").slice(0, 1).toUpperCase();
  if (frag) {
    return `<div class="admin-cms-card__logo admin-cms-card__logo--icon"><svg class="admin-cms-social-icon" viewBox="0 0 24 24" aria-hidden="true">${frag}</svg></div>`;
  }
  return `<div class="admin-cms-card__logo admin-cms-card__logo--icon admin-cms-card__logo--empty">${escapeHtml(initial)}</div>`;
}

function unixToDatetimeLocal(ts) {
  const n = Number(ts);
  if (!Number.isFinite(n) || n <= 0) return "";
  const d = new Date(n * 1000);
  const pad = (x) => String(x).padStart(2, "0");
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
}

function datetimeLocalToUnix(val) {
  const s = String(val || "").trim();
  if (!s) return null;
  const ms = Date.parse(s);
  if (!Number.isFinite(ms)) return null;
  return Math.floor(ms / 1000);
}

function closeAdminDrawer() {
  document.body.classList.remove("admin-drawer-open");
  const openBtn = $("#admin-drawer-open");
  if (openBtn) openBtn.setAttribute("aria-expanded", "false");
  const bd = $("#admin-drawer-backdrop");
  if (bd) bd.setAttribute("aria-hidden", "true");
  const prev = adminDrawerPreviousFocus;
  adminDrawerPreviousFocus = null;
  if (prev && typeof prev.focus === "function") {
    try {
      prev.focus();
    } catch {
      /* ignore */
    }
  }
}

function openAdminDrawer() {
  adminDrawerPreviousFocus = document.activeElement;
  document.body.classList.add("admin-drawer-open");
  const openBtn = $("#admin-drawer-open");
  if (openBtn) openBtn.setAttribute("aria-expanded", "true");
  const bd = $("#admin-drawer-backdrop");
  if (bd) bd.setAttribute("aria-hidden", "false");
  requestAnimationFrame(() => {
    const side = $("#admin-sidebar");
    const first =
      side?.querySelector(".tab-btn") ||
      side?.querySelector("button,a[href]") ||
      $("#admin-drawer-close");
    if (first && typeof first.focus === "function") {
      try {
        first.focus();
      } catch {
        /* ignore */
      }
    }
  });
}

function isAdminMobileDrawerViewport() {
  return typeof window.matchMedia === "function" && window.matchMedia("(max-width: 1023px)").matches;
}

let adminDrawerEscapeBound = false;
/** @type {Element | null} */
let adminDrawerPreviousFocus = null;

const ADMIN_TOAST_MAX = 5;
const ADMIN_TOAST_MS = 4200;

function ensureAdminToastStack() {
  let root = $("#admin-toast-stack");
  if (!root) {
    root = document.createElement("div");
    root.id = "admin-toast-stack";
    root.className = "admin-toast-stack";
    root.setAttribute("aria-live", "polite");
    root.setAttribute("aria-relevant", "additions text");
    document.body.appendChild(root);
  }
  return root;
}

function toast(msg, type = "ok") {
  const stack = ensureAdminToastStack();
  while (stack.children.length >= ADMIN_TOAST_MAX) {
    stack.firstChild?.remove();
  }
  const el = document.createElement("div");
  const kind = type === "err" ? "err" : "ok";
  el.className = `admin-toast admin-toast--${kind}`;
  el.setAttribute("role", "status");
  el.innerHTML = `<span class="admin-toast__text">${escapeHtml(String(msg))}</span><button type="button" class="admin-toast__dismiss" aria-label="Kapat">×</button>`;
  const dismissBtn = /** @type {HTMLButtonElement | null} */ (el.querySelector(".admin-toast__dismiss"));
  let done = false;
  const finish = () => {
    if (done) return;
    done = true;
    el.classList.add("admin-toast--out");
    window.setTimeout(() => {
      el.remove();
    }, 260);
  };
  const tid = window.setTimeout(finish, ADMIN_TOAST_MS);
  dismissBtn?.addEventListener("click", () => {
    window.clearTimeout(tid);
    finish();
  });
  stack.appendChild(el);
}

function ensureAdminDrawerEscapeListener() {
  if (adminDrawerEscapeBound) return;
  adminDrawerEscapeBound = true;
  document.addEventListener("keydown", (e) => {
    if (e.key !== "Escape") return;
    if (!document.body.classList.contains("admin-drawer-open")) return;
    closeAdminDrawer();
  });
}

function formatDashMetric(v) {
  if (v === "—" || v === null || v === undefined) return "—";
  const n = Number(v);
  if (!Number.isFinite(n)) return escapeHtml(String(v));
  return escapeHtml(n.toLocaleString("tr-TR"));
}

function dashCell(val) {
  if (val === "—" || val === null || val === undefined) return "—";
  if (typeof val === "number" && Number.isFinite(val)) return formatDashMetric(val);
  if (typeof val === "boolean") return val ? "Evet" : "Hayır";
  return escapeHtml(String(val));
}

function dashKpiIcon(kind) {
  const c =
    'class="admin-dash-kpi-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.65" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"';
  switch (kind) {
    case "users":
      return `<svg ${c}><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.87M16 3.13a4 4 0 0 1 0 7.75"/></svg>`;
    case "userPlus":
      return `<svg ${c}><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><line x1="19" y1="8" x2="19" y2="14"/><line x1="22" y1="11" x2="16" y2="11"/></svg>`;
    case "logIn":
      return `<svg ${c}><path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4"/><polyline points="10 17 15 12 10 7"/><line x1="15" y1="12" x2="3" y2="12"/></svg>`;
    case "activity":
      return `<svg ${c}><path d="M22 12h-4l-3 9L9 3l-3 9H2"/></svg>`;
    case "zap":
      return `<svg ${c} fill="none"><polygon fill="none" points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>`;
    case "cart":
      return `<svg ${c}><circle cx="9" cy="21" r="1"/><circle cx="20" cy="21" r="1"/><path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"/></svg>`;
    case "coins":
      return `<svg ${c}><path d="M12 2v20"/><path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/></svg>`;
    case "message":
      return `<svg ${c}><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>`;
    case "rotate":
      return `<svg ${c}><path d="M23 4v6h-6"/><path d="M1 20v-6h6"/><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>`;
    default:
      return `<svg ${c}><circle cx="12" cy="12" r="10"/></svg>`;
  }
}

function iconSvg(kind, cls = "h-4 w-4") {
  const c = `class="${escapeHtml(cls)}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"`;
  switch (kind) {
    case "cart":
      return `<svg ${c}><circle cx="9" cy="20" r="1"/><circle cx="18" cy="20" r="1"/><path d="M2 3h3l2.2 11.2a2 2 0 0 0 2 1.6h8.6a2 2 0 0 0 2-1.6L22 7H6.2"/></svg>`;
    case "ticket":
      return `<svg ${c}><path d="M4 8a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v2a2 2 0 0 0 0 4v2a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-2a2 2 0 0 0 0-4z"/><path d="M10 6v12"/></svg>`;
    case "sparkles":
      return `<svg ${c}><path d="M12 3l1.8 4.2L18 9l-4.2 1.8L12 15l-1.8-4.2L6 9l4.2-1.8L12 3z"/><path d="M5 16l.9 2.1L8 19l-2.1.9L5 22l-.9-2.1L2 19l2.1-.9L5 16z"/><path d="M19 14l.9 2.1L22 17l-2.1.9L19 20l-.9-2.1L16 17l2.1-.9L19 14z"/></svg>`;
    case "x-circle":
      return `<svg ${c}><circle cx="12" cy="12" r="9"/><path d="M9 9l6 6M15 9l-6 6"/></svg>`;
    case "link":
      return `<svg ${c}><path d="M10 13a5 5 0 0 1 0-7l1.5-1.5a5 5 0 0 1 7 7L17 13"/><path d="M14 11a5 5 0 0 1 0 7L12.5 19.5a5 5 0 0 1-7-7L7 11"/></svg>`;
    case "user":
      return `<svg ${c}><circle cx="12" cy="8" r="4"/><path d="M4 20a8 8 0 0 1 16 0"/></svg>`;
    case "bell":
    default:
      return `<svg ${c}><path d="M15 17H5l1.2-1.2A2 2 0 0 0 7 14.4V11a5 5 0 1 1 10 0v3.4a2 2 0 0 0 .8 1.6L19 17h-4"/><path d="M10 19a2 2 0 0 0 4 0"/></svg>`;
  }
}

let adminCsrfToken = null;

let adminSidebarNavScrollTop = 0;
let adminSidebarSearchQuery = "";
/** @type {ReturnType<typeof setInterval> | null} */
let adminChatPollTimer = null;
/** @type {ReturnType<typeof setInterval> | null} */
let adminAlertsPollTimer = null;
let adminAlertsLastTotal = 0;
let adminAlertsBootstrapped = false;
let adminAlertsAudioCtx = null;
let adminAlertsDocClickBound = false;

function captureAdminSidebarSearchQuery() {
  const input = document.getElementById("admin-sidebar-search-input");
  if (input) adminSidebarSearchQuery = String(input.value || "");
}

function filterAdminSidebarNav(query) {
  const q = String(query || "").trim().toLowerCase();
  const nav =
    document.querySelector("#admin-sidebar .admin-sidebar__nav") || $("#admin-sidebar nav");
  if (!nav) return;

  let totalVisible = 0;
  nav.querySelectorAll(".admin-nav-group").forEach((group) => {
    let groupVisible = 0;
    const groupLabel =
      group.querySelector(".admin-nav-group__trigger .admin-nav-tab__label")?.textContent?.trim().toLowerCase() ||
      group.querySelector(".admin-nav-group__trigger-label")?.textContent?.trim().toLowerCase() ||
      "";

    group.querySelectorAll("button.tab-btn[data-tab]").forEach((btn) => {
      const tab = String(btn.getAttribute("data-tab") || "").toLowerCase();
      const label =
        btn.querySelector(".admin-nav-tab__label")?.textContent?.trim().toLowerCase() || "";
      const haystack = `${label} ${tab} ${tab.replace(/-/g, " ")} ${groupLabel}`;
      const match = !q || haystack.includes(q);
      btn.hidden = !match;
      btn.classList.toggle("admin-nav-tab--search-hidden", !match);
      if (match) {
        groupVisible++;
        totalVisible++;
      }
    });

    const hideGroup = !!q && groupVisible === 0;
    group.hidden = hideGroup;
    group.classList.toggle("admin-sidebar__section--search-hidden", hideGroup);

    if (q && groupVisible > 0) {
      group.classList.add("admin-nav-group--open");
      const trigger = group.querySelector("[data-nav-group-toggle]");
      if (trigger) trigger.setAttribute("aria-expanded", "true");
    }
  });

  const emptyEl = document.getElementById("admin-sidebar-search-empty");
  if (emptyEl) emptyEl.hidden = !q || totalVisible > 0;
}

function bindAdminSidebarSearch() {
  const searchInput = document.getElementById("admin-sidebar-search-input");
  const searchClear = document.getElementById("admin-sidebar-search-clear");
  if (!searchInput || searchInput.dataset.bound === "1") return;
  searchInput.dataset.bound = "1";

  if (adminSidebarSearchQuery) {
    searchInput.value = adminSidebarSearchQuery;
    if (searchClear) searchClear.hidden = false;
    filterAdminSidebarNav(adminSidebarSearchQuery);
  }

  searchInput.addEventListener("input", () => {
    adminSidebarSearchQuery = searchInput.value;
    if (searchClear) searchClear.hidden = !adminSidebarSearchQuery.trim();
    filterAdminSidebarNav(adminSidebarSearchQuery);
  });

  searchInput.addEventListener("keydown", (ev) => {
    if (ev.key === "Escape") {
      adminSidebarSearchQuery = "";
      searchInput.value = "";
      filterAdminSidebarNav("");
      if (searchClear) searchClear.hidden = true;
      searchInput.blur();
      return;
    }
    if (ev.key === "Enter") {
      const first = document.querySelector(
        "#admin-sidebar button.tab-btn[data-tab]:not([hidden])"
      );
      if (first) {
        ev.preventDefault();
        first.click();
        if (isAdminMobileDrawerViewport()) closeAdminDrawer();
      }
    }
  });

  searchClear?.addEventListener("click", () => {
    adminSidebarSearchQuery = "";
    searchInput.value = "";
    filterAdminSidebarNav("");
    searchClear.hidden = true;
    searchInput.focus();
  });
}

function captureAdminSidebarNavScroll() {
  const nav =
    document.querySelector("#admin-sidebar .admin-sidebar__nav") || $("#admin-sidebar nav");
  if (nav) adminSidebarNavScrollTop = nav.scrollTop;
}

function restoreAdminSidebarNavScroll() {
  const nav =
    document.querySelector("#admin-sidebar .admin-sidebar__nav") || $("#admin-sidebar nav");
  if (nav) {
    const max = Math.max(0, nav.scrollHeight - nav.clientHeight);
    nav.scrollTop = Math.min(adminSidebarNavScrollTop, max);
  }
}

const NAV_TAB_ICON_SVG = {
  dashboard:
    '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zm10 0a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zm10 0a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z"/>',
  audit:
    '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"/>',
  staff:
    '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"/>',
  members:
    '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0z"/>',
  "market-requests":
    '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z"/>',
  complaints:
    '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 8h10M7 12h4m1 8l-4-4H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-3l-4 4z"/>',
  "affiliate-requests":
    '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"/>',
  "ip-clashes":
    '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9"/>',
  chat:
    '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"/>',
  sponsors:
    '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"/>',
  market:
    '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"/>',
  social:
    '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"/>',
  site:
    '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/>',
  maintenance:
    '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>',
  popup:
    '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6 6 0 10-12 0v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"/>',
  "legal-pages":
    '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>',
  css:
    '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"/>',
  wheel:
    '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/>',
  "games-covers":
    '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>',
  "special-odds":
    '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"/>',
  "promo-codes":
    '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"/>',
  "sponsor-promo-codes":
    '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 14l2 2 4-4"/>',
  bonuses:
    '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 11.25v8.25a1.5 1.5 0 01-1.5 1.5H5.25a1.5 1.5 0 01-1.5-1.5v-8.25M12 4.875A2.625 2.625 0 109.375 7.5H12m0 0V2.25m0 5.25h2.625A2.625 2.625 0 1117.625 7.5H12m0 0v6.75m0-3v3.75m-8.25-3h16.5"/>',
  "telegram-invites":
    '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8"/>',
  "telegram-broadcast":
    '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5.882V19.24a1.76 1.76 0 01-3.417.592l-2.147-6.15M18 13a3 3 0 100-6M5.436 13.683A4.001 4.001 0 017 6h1.832c4.1 0 7.625-1.234 9.168-3v14c-1.543-1.766-5.067-3-9.168-3H7a3.988 3.988 0 01-1.564-.317z"/>',
  levels:
    '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3l2.65 5.373 5.93.862-4.29 4.182 1.012 5.905L12 16.5l-5.302 2.822 1.012-5.905-4.29-4.182 5.93-.862L12 3z"/>',
  "site-events":
    '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 012.25-2.25h13.5A2.25 2.25 0 0121 7.5v11.25m-18 0A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75m-18 0v-7.5A2.25 2.25 0 015.25 9h13.5A2.25 2.25 0 0121 11.25v7.5"/>',
  "site-news":
    '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 7.5H8.25A2.25 2.25 0 006 5.25v-4.5A2.25 2.25 0 018.25 3h7.5A2.25 2.25 0 0118 5.25v4.5A2.25 2.25 0 0115.75 12H12m-7.5 4.5h7.5m-7.5 3H12M9 6h6m-3 3v6.75"/>',
  "site-notifications":
    '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6 6 0 10-12 0v3.159c0 .538-.214 1.055-.595 1.436L13 20H4l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"/>',
  tickets:
    '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16.5 6v.75m0 3v.75m0 3v.75m0 3V18m-9-5.25h5.25M7.5 15h7.5m-7.5 3h7.5m3-9H6.75a2.25 2.25 0 00-2.25 2.25v10.5A2.25 2.25 0 006.75 21h10.5a2.25 2.25 0 002.25-2.25V9.75A2.25 2.25 0 0017.25 7.5z"/>',
};

function navSidebarIconSvg(tab) {
  const d = NAV_TAB_ICON_SVG[tab] || NAV_TAB_ICON_SVG.dashboard;
  return `<svg class="admin-nav-tab__svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" aria-hidden="true">${d}</svg>`;
}

function navSidebarLabelRow(tab, labelHtml) {
  return `<span class="admin-nav-tab__row"><span class="admin-nav-tab__icon" aria-hidden="true">${navSidebarIconSvg(tab)}</span><span class="admin-nav-tab__mid">${labelHtml}</span></span>`;
}

function navTabClass(tabName, activeTab, layout = "sidebar") {
  const active = tabName === activeTab;
  const accent =
    tabName === "dashboard"
      ? "emerald"
      : tabName === "staff"
        ? "rose"
        : tabName === "members" || tabName === "market-requests" || tabName === "ip-clashes" || tabName === "complaints" || tabName === "affiliate-requests"
          ? "violet"
          : tabName === "wheel" || tabName === "games-covers" || tabName === "levels" || tabName === "promo-codes" || tabName === "sponsor-promo-codes" || tabName === "bonuses" || tabName === "telegram-invites" || tabName === "telegram-broadcast" || tabName === "special-odds" || tabName === "tickets" || tabName === "site-events" || tabName === "site-news" || tabName === "site-notifications" || tabName === "maintenance" || tabName === "legal-pages"
              ? "cyan"
              : "sky";
  const parts = ["tab-btn", "admin-nav-tab"];
  if (layout === "dock") parts.push("admin-nav-tab--dock");
  if (active) {
    parts.push("admin-nav-tab--active", `admin-nav-tab--${accent}`);
  }
  return parts.join(" ");
}

/** Sidebar: üst grup + alt sekme (section label yok). */
const ADMIN_NAV_GROUPS = [
  {
    id: "overview",
    label: "Dashboard",
    icon: "dashboard",
    children: [
      { tab: "dashboard", label: "Kontrol paneli" },
      { tab: "audit", label: "Denetim günlüğü" },
    ],
  },
  {
    id: "members",
    label: "Üyeler",
    icon: "members",
    children: [
      { tab: "members", label: "Üye listesi" },
      { tab: "complaints", label: "Şikayetler", badge: "complaints" },
      { tab: "affiliate-requests", label: "Affiliate talepleri", badge: "affiliate" },
      { tab: "ip-clashes", label: "IP çakışmaları" },
      { tab: "chat", label: "Sohbet moderasyonu" },
    ],
  },
  {
    id: "market",
    label: "Market",
    icon: "market",
    children: [
      { tab: "market", label: "Market düzenlemesi" },
      { tab: "market-requests", label: "Market talepleri" },
    ],
  },
  {
    id: "sponsors",
    label: "Sponsorlar",
    icon: "sponsors",
    children: [
      { tab: "sponsors", label: "Sponsor kartları" },
      { tab: "bonuses", label: "Site bonusları" },
      { tab: "sponsor-promo-codes", label: "Site promo kodları" },
    ],
  },
  {
    id: "site",
    label: "Site",
    icon: "site",
    children: [
      { tab: "social", label: "Sosyal linkler" },
      { tab: "site", label: "İletişim & site" },
      { tab: "maintenance", label: "Bakım modu" },
      { tab: "popup", label: "Açılış popup" },
      { tab: "legal-pages", label: "Yasal sayfalar" },
      { tab: "gamdom-turnuva", label: "Gamdom çevrim turnuvası" },
      { tab: "css", label: "Özel CSS" },
    ],
  },
  {
    id: "games",
    label: "Oyunlar",
    icon: "games-covers",
    children: [
      { tab: "wheel", label: "Şans çarkı" },
      { tab: "games-covers", label: "Oyun ayarları" },
      { tab: "levels", label: "Seviye sistemi" },
      { tab: "promo-codes", label: "Promo kodlar" },
      { tab: "special-odds", label: "Özel oranlar" },
    ],
  },
  {
    id: "content",
    label: "İçerik",
    icon: "tickets",
    children: [
      { tab: "tickets", label: "Biletler" },
      { tab: "site-events", label: "Etkinlikler" },
      { tab: "site-news", label: "Haberler" },
      { tab: "site-notifications", label: "Bildirimler" },
    ],
  },
  {
    id: "telegram",
    label: "Telegram",
    icon: "telegram-invites",
    children: [
      { tab: "telegram-invites", label: "Telegram davet" },
      { tab: "telegram-broadcast", label: "Telegram yayın" },
    ],
  },
  {
    id: "staff",
    label: "Ekip",
    icon: "staff",
    superOnly: true,
    children: [{ tab: "staff", label: "Ekip & izinler" }],
  },
];

function adminNavTabToGroupId(tab) {
  for (const g of ADMIN_NAV_GROUPS) {
    if (g.children.some((c) => c.tab === tab)) return g.id;
  }
  return null;
}

function loadSidebarNavOpenState() {
  try {
    const raw = localStorage.getItem("admin_nav_open");
    if (raw) {
      const parsed = JSON.parse(raw);
      if (parsed && typeof parsed === "object") return parsed;
    }
  } catch {
    /* ignore */
  }
  return {};
}

function saveSidebarNavOpenState() {
  if (!state.sidebarNavOpen || typeof state.sidebarNavOpen !== "object") return;
  try {
    localStorage.setItem("admin_nav_open", JSON.stringify(state.sidebarNavOpen));
  } catch {
    /* ignore */
  }
}

function ensureSidebarNavOpenState() {
  if (!state.sidebarNavOpen || typeof state.sidebarNavOpen !== "object") {
    state.sidebarNavOpen = loadSidebarNavOpenState();
  }
}

function ensureSidebarNavOpenForTab(tab) {
  ensureSidebarNavOpenState();
  const gid = adminNavTabToGroupId(tab);
  if (gid) state.sidebarNavOpen[gid] = true;
}

function isSidebarNavGroupOpen(groupId, activeTab) {
  ensureSidebarNavOpenState();
  const group = ADMIN_NAV_GROUPS.find((g) => g.id === groupId);
  if (!group) return false;
  const hasActiveChild = group.children.some((c) => c.tab === activeTab && canPerm(c.tab));
  const stored = state.sidebarNavOpen[groupId];
  if (stored === true) return true;
  if (stored === false) return false;
  return hasActiveChild;
}

function adminNavChildBadgeHtml(badgeKey, ctx) {
  if (badgeKey === "complaints" && ctx.pendingComplaintsCount > 0) {
    return `<span class="admin-nav-tab__badge">${ctx.pendingComplaintsCount}</span>`;
  }
  if (badgeKey === "affiliate" && ctx.pendingAffiliateCount > 0) {
    return `<span class="admin-nav-tab__badge">${ctx.pendingAffiliateCount}</span>`;
  }
  return "";
}

function adminNavGroupHtml(group, activeTab, ctx, layout = "sidebar") {
  if (group.superOnly && !state.super) return "";
  const visibleChildren = group.children.filter((c) => canPerm(c.tab));
  if (!visibleChildren.length) return "";

  const open = isSidebarNavGroupOpen(group.id, activeTab);
  const hasActiveChild = visibleChildren.some((c) => c.tab === activeTab);
  const isDock = layout === "dock";

  const items = visibleChildren
    .map((c) => navBtn(c.tab, c.label, activeTab, layout, { child: !isDock, badgeHtml: adminNavChildBadgeHtml(c.badge, ctx) }))
    .join("");

  const chevron = `<span class="admin-nav-group__chevron" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7"/></svg></span>`;

  if (isDock) {
    return `<div class="admin-nav-group admin-nav-group--dock${open ? " admin-nav-group--open" : ""}${hasActiveChild ? " admin-nav-group--active-branch" : ""}" data-nav-group="${escapeHtml(group.id)}">
      <button type="button" class="admin-nav-group__trigger admin-nav-group__trigger--dock" data-nav-group-toggle="${escapeHtml(group.id)}" aria-expanded="${open ? "true" : "false"}">
        <span class="admin-nav-group__trigger-label">${escapeHtml(group.label)}</span>${chevron}
      </button>
      <div class="admin-nav-group__items admin-nav-group__items--dock">${items}</div>
    </div>`;
  }

  return `<div class="admin-nav-group admin-sidebar__section${open ? " admin-nav-group--open" : ""}${hasActiveChild ? " admin-nav-group--active-branch" : ""}" data-nav-group="${escapeHtml(group.id)}">
    <button type="button" class="admin-nav-group__trigger" data-nav-group-toggle="${escapeHtml(group.id)}" aria-expanded="${open ? "true" : "false"}" title="${escapeHtml(group.label)}">
      ${navSidebarLabelRow(group.icon, `<span class="admin-nav-tab__label">${escapeHtml(group.label)}</span>`)}
      ${chevron}
    </button>
    <div class="admin-nav-group__items admin-sidebar__section-items">${items}</div>
  </div>`;
}

function adminSidebarNavHtml(activeTab, ctx) {
  ensureSidebarNavOpenForTab(activeTab);
  return ADMIN_NAV_GROUPS.map((g) => adminNavGroupHtml(g, activeTab, ctx, "sidebar")).join("");
}

function adminMobileDockNavHtml(activeTab, ctx) {
  ensureSidebarNavOpenForTab(activeTab);
  return `<div class="admin-mobile-dock__groups">${ADMIN_NAV_GROUPS.map((g) => adminNavGroupHtml(g, activeTab, ctx, "dock")).join("")}</div>`;
}

const ADMIN_SIDEBAR_COLLAPSED_KEY = "admin_sidebar_collapsed";
let adminSidebarCollapseResizeBound = false;

function loadAdminSidebarCollapsed() {
  try {
    return localStorage.getItem(ADMIN_SIDEBAR_COLLAPSED_KEY) === "1";
  } catch {
    return false;
  }
}

function saveAdminSidebarCollapsed(collapsed) {
  try {
    localStorage.setItem(ADMIN_SIDEBAR_COLLAPSED_KEY, collapsed ? "1" : "0");
  } catch {
    /* ignore */
  }
}

function applyAdminSidebarCollapsed(collapsed) {
  if (isAdminMobileDrawerViewport()) {
    document.body.classList.remove("admin-sidebar-collapsed");
    return;
  }
  document.body.classList.toggle("admin-sidebar-collapsed", !!collapsed);
  const btn = document.getElementById("admin-sidebar-collapse-toggle");
  const icon = btn?.querySelector("svg");
  const isCollapsed = document.body.classList.contains("admin-sidebar-collapsed");
  if (btn) {
    btn.setAttribute("aria-expanded", isCollapsed ? "false" : "true");
    const label = isCollapsed ? "Kenar çubuğunu genişlet" : "Kenar çubuğunu daralt";
    btn.setAttribute("aria-label", label);
    btn.setAttribute("title", label);
  }
  if (icon) {
    icon.style.transform = isCollapsed ? "rotate(180deg)" : "";
  }
}

function syncAdminSidebarCollapsedFromStorage() {
  applyAdminSidebarCollapsed(loadAdminSidebarCollapsed());
}

function bindAdminSidebarCollapseToggle() {
  syncAdminSidebarCollapsedFromStorage();
  const btn = document.getElementById("admin-sidebar-collapse-toggle");
  if (btn && btn.dataset.bound !== "1") {
    btn.dataset.bound = "1";
    btn.addEventListener("click", () => {
      if (!window.matchMedia("(min-width: 1024px)").matches) return;
      const next = !document.body.classList.contains("admin-sidebar-collapsed");
      applyAdminSidebarCollapsed(next);
      saveAdminSidebarCollapsed(next);
    });
  }
  if (!adminSidebarCollapseResizeBound) {
    adminSidebarCollapseResizeBound = true;
    window.addEventListener("resize", () => {
      if (isAdminMobileDrawerViewport()) {
        document.body.classList.remove("admin-sidebar-collapsed");
      } else {
        applyAdminSidebarCollapsed(loadAdminSidebarCollapsed());
      }
    });
  }
}

function bindAdminSidebarNavGroups() {
  document.querySelectorAll("[data-nav-group-toggle]").forEach((btn) => {
    if (btn.dataset.navGroupBound === "1") return;
    btn.dataset.navGroupBound = "1";
    btn.addEventListener("click", (e) => {
      e.preventDefault();
      e.stopPropagation();
      const gid = btn.getAttribute("data-nav-group-toggle");
      if (!gid) return;
      ensureSidebarNavOpenState();
      const open = isSidebarNavGroupOpen(gid, state.tab);
      state.sidebarNavOpen[gid] = !open;
      saveSidebarNavOpenState();
      const group = btn.closest(".admin-nav-group");
      if (group) {
        group.classList.toggle("admin-nav-group--open", !open);
        btn.setAttribute("aria-expanded", !open ? "true" : "false");
      }
    });
  });
}

function buildAdminApiUrl(pathClean) {
  const clean = String(pathClean).replace(/^\/+/, "").replace(/^api\/?/i, "");
  const injected = typeof window !== "undefined" && window.__ADMIN_API_BASE__;
  if (typeof injected === "string" && injected.length > 0) {
    const base = injected.endsWith("/") ? injected : `${injected}/`;
    return new URL(clean, new URL(base, window.location.origin).href).href;
  }
  return new URL(`api/${clean}`, window.location.href).href;
}

async function api(path, opts = {}) {
  const pathClean = path.replace(/^\/+/, "").replace(/^api\/?/i, "");
  const url = buildAdminApiUrl(pathClean);
  const { headers: optHeaders, ...restOpts } = opts;
  const method = String(restOpts.method || "GET").toUpperCase();
  const isFormData = typeof FormData !== "undefined" && restOpts.body instanceof FormData;
  const baseHeaders = {
    Accept: "application/json",
    ...(isFormData ? {} : { "Content-Type": "application/json" }),
    ...(optHeaders || {}),
  };
  if (["POST", "PUT", "PATCH", "DELETE"].includes(method) && adminCsrfToken) {
    baseHeaders["X-CSRF-Token"] = adminCsrfToken;
  }
  let r;
  try {
    r = await fetch(url, {
      credentials: "same-origin",
      ...restOpts,
      headers: baseHeaders,
    });
  } catch (err) {
    const msg =
      err instanceof TypeError && /fetch|network|load failed/i.test(String(err.message))
        ? "Sunucuya ulaşılamadı. Admini PHP ile aç (örn. php -S localhost:8080); dosyayı çift tıklayıp file:// ile açmayın."
        : err.message || String(err);
    throw new Error(msg);
  }
  const text = await r.text();
  let j = {};
  try {
    j = text ? JSON.parse(text) : {};
  } catch {
    throw new Error(
      `Sunucu yanıtı geçersiz (HTTP ${r.status}). PHP hatası veya yanlış URL; tarayıcı önbelleğini temizlemeyi dene.`
    );
  }
  if (!r.ok) {
    const msg =
      (typeof j.message === "string" && j.message.trim()) ||
      (typeof j.error === "string" && j.error.trim()) ||
      `HTTP ${r.status}`;
    const err = new Error(msg);
    err.body = j;
    throw err;
  }
  return j;
}

const ICON_PRESETS = {
  telegram:
    '<path fill="currentColor" d="M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.48.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z"/>',
  kick: '<path fill="currentColor" d="M3 4h4v16H3V4zm6 0h3.5L18 12l-5.5 8H9l4.2-8L9 4zm8.5 0H21v16h-3.5V4z"/>',
  youtube:
    '<path fill="currentColor" d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"/>',
  x: '<path fill="currentColor" d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/>',
  instagram:
    '<path fill="currentColor" d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zM12 0C8.741 0 8.333.014 7.053.072 2.695.272.273 2.69.073 7.052.014 8.333 0 8.741 0 12c0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98C8.333 23.986 8.741 24 12 24c3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98C15.668.014 15.259 0 12 0zm0 5.838a6.162 6.162 0 1 0 0 12.324 6.162 6.162 0 0 0 0-12.324zM12 16a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm6.406-11.845a1.44 1.44 0 1 0 0 2.881 1.44 1.44 0 0 0 0-2.881z"/>',
  whatsapp:
    '<path fill="currentColor" d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.435 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z"/>',
};

let state = {
  sponsors: [],
  social: [],
  site: {},

  popup: {},

  gamdomTurnuva: {},

  legalPages: {},

  legalPagesActiveSlug: "privacy",

  market: [],

  members: [],

  marketRequests: [],

  marketRequestsQuery: "",

  complaints: [],

  complaintsQuery: "",

  affiliateRequests: [],

  affiliateRequestsQuery: "",

  specialOddsMatches: [],

  membersFilter: "",

  membersFilterMinCoins: "",

  membersFilterMaxCoins: "",

  membersFilterShop: "all",

  membersFilterMultiIp: "all",

  membersFilterTags: { suspicious: false, vip: false, banned: false },

  membersSource: null,
  membersLoadInfo: null,
  membersSync: null,
  membersExternalError: null,
  membersLoading: false,
  membersPage: 0,

  ipClashQuery: "",
  tab: "dashboard",

  dashboardFrom: 0,
  dashboardTo: 0,

  dashboardPreset: "today",

  dashboardStats: null,

  editSponsorId: null,

  editMarketId: null,

  editPasswordUserId: null,

  viewMemberProfileUserId: null,

  memberProfileDetail: null,

  memberProfileActiveTab: "overview",

  memberProfileBlackjackPage: 0,

  memberProfileSlotPage: 0,

  wheelConfig: null,
  gamesCovers: null,
  gamesSettings: null,

  super: false,
  adminKind: "master",
  adminUsername: null,
  adminDisplayName: null,

  permissions: [],

  operators: [],

  staffEditOp: null,

  promoCodes: [],

  sponsorPromoCodes: [],

  editSponsorPromoId: null,

  bonuses: [],

  editBonusId: null,

  telegramInvites: null,
  telegramBroadcast: null,
  levelSystem: null,
  telegramBroadcastSending: false,
  telegramBroadcastLastResult: null,
  ticketEvents: [],
  ticketView: "list",
  siteEvents: [],
  siteNews: [],
  siteNewsEditId: null,
  siteNotifications: [],
  headerAlerts: { totalPending: 0, sections: [], items: [], serverTime: 0 },
  headerAlertsOpen: false,
  headerAlertsSound: true,
  myAdminProfile: null,
  loginTwofaRequired: false,
  loginTwofaChallenge: null,
  sponsorOptions: [],
  joinGuides: null,
  siteEventView: "list",
  siteEventEditId: null,
  siteEventId: null,
  siteEventSubTab: "requests",
  siteEventDetail: null,
  siteEventRequestFilter: { q: "", status: "all" },
  ticketEventId: null,
  ticketSubTab: "requests",
  ticketDetail: null,
  ticketRequestFilter: { q: "", status: "all" },

  auditLog: [],

  auditTotal: 0,

  auditDatePreset: "all",

  auditDateFrom: "",

  auditDateTo: "",

  chatMessages: [],

  chatMutes: [],

  chatSystemUserId: 0,

  authenticated: false,
  adminGateRequired: false,
};

const PERM_DEFS = [
  { id: "dashboard", label: "Dashboard" },
  { id: "audit", label: "Denetim günlüğü" },
  { id: "members", label: "Üye listesi" },
  { id: "member-twofa", label: "Üye 2FA kapatma" },
  { id: "market-requests", label: "Market talepleri" },
  { id: "complaints", label: "Şikayetler" },
  { id: "affiliate-requests", label: "Affiliate talepleri" },
  { id: "ip-clashes", label: "IP çakışmaları" },
  { id: "chat", label: "Moderatör (sohbet)" },
  { id: "sponsors", label: "Sponsor kartları" },
  { id: "sponsor-promo-codes", label: "Site promo kodları" },
  { id: "bonuses", label: "Site bonusları" },
  { id: "market", label: "Market (CMS)" },
  { id: "social", label: "Sosyal linkler" },
  { id: "site", label: "İletişim & site" },
  { id: "maintenance", label: "Bakım modu" },
  { id: "wheel", label: "Şans çarkı" },
  { id: "games-covers", label: "Oyun ayarları" },
  { id: "promo-codes", label: "Promo kodlar" },
  { id: "telegram-invites", label: "Telegram davet" },
  { id: "telegram-broadcast", label: "Telegram yayın" },
  { id: "special-odds", label: "Özel oranlar" },
  { id: "tickets", label: "Biletler" },
  { id: "events", label: "Etkinlikler" },
  { id: "news", label: "Haberler" },
  { id: "notifications", label: "Bildirimler" },
];

const MEMBER_ADMIN_TAG_DEFS = [
  { id: "suspicious", label: "Şüpheli" },
  { id: "vip", label: "VIP" },
  { id: "banned", label: "Yasaklı" },
];

function memberAdminTagLabel(tagId) {
  const d = MEMBER_ADMIN_TAG_DEFS.find((x) => x.id === tagId);
  return d ? d.label : tagId;
}

function memberAdminTagsBadgesHtml(tags) {
  const arr = Array.isArray(tags) ? tags : [];
  if (!arr.length) {
    return `<span class="admin-members-tag admin-members-tag--muted">—</span>`;
  }
  return arr
    .map((id) => {
      const cls =
        id === "banned"
          ? "admin-members-tag--danger"
          : id === "vip"
            ? "admin-members-tag--vip"
            : "admin-members-tag--warn";
      const lab = memberAdminTagLabel(String(id));
      return `<span class="admin-members-tag ${cls}">${escapeHtml(lab)}</span>`;
    })
    .join("");
}

function memberProfileHeroTagsHtml(tags) {
  const arr = Array.isArray(tags) ? tags : [];
  if (!arr.length) return "";
  return `<div class="admin-member-profile__hero-tags mt-2 flex flex-wrap gap-1.5">${arr
    .map((id) => {
      const cls =
        id === "banned"
          ? "admin-members-tag--danger"
          : id === "vip"
            ? "admin-members-tag--vip"
            : "admin-members-tag--warn";
      return `<span class="admin-members-tag ${cls} text-xs">${escapeHtml(memberAdminTagLabel(String(id)))}</span>`;
    })
    .join("")}</div>`;
}

function memberLevelDataFromUser(u) {
  if (!u || typeof u !== "object") return null;
  if (u.level && typeof u.level === "object") return u.level;
  return null;
}

function memberLevelListCellHtml(u) {
  const lv = memberLevelDataFromUser(u);
  if (!lv) {
    return `<span class="admin-members-level admin-members-level--muted">—</span>`;
  }
  if (lv.enabled === false) {
    return `<span class="admin-members-level admin-members-level--muted" title="Seviye sistemi kapalı">Kapalı</span>`;
  }
  const level = Math.max(0, Number(lv.level) || 0);
  const xp = Math.max(0, Number(lv.xpTotal) || 0);
  return `<span class="admin-members-level" title="Toplam ${xp} XP">
    <strong class="admin-members-level__no">Lv.${level}</strong>
    <span class="admin-members-level__xp">${xp} XP</span>
  </span>`;
}

function memberLevelHeroBadgeHtml(level) {
  const lv = level && typeof level === "object" ? level : {};
  if (lv.enabled === false) return "";
  const levelNo = Math.max(0, Number(lv.level) || 0);
  const xp = Math.max(0, Number(lv.xpTotal) || 0);
  return `<p class="admin-member-profile__hero-level mt-2">
    <span class="admin-members-level admin-members-level--hero" title="Toplam ${xp} XP">
      <strong class="admin-members-level__no">Lv.${levelNo}</strong>
      <span class="admin-members-level__xp">${xp} XP</span>
    </span>
  </p>`;
}

function memberLevelProfileBlockHtml(level) {
  const lv = level && typeof level === "object" ? level : {};
  if (lv.enabled === false) {
    return `<article class="admin-member-level-card admin-member-level-card--off">
      <p class="admin-member-level-card__kicker">Seviye sistemi</p>
      <p class="admin-member-level-card__off">Site genelinde seviye sistemi şu an kapalı.</p>
    </article>`;
  }
  const levelNo = Math.max(0, Number(lv.level) || 0);
  const xpTotal = Math.max(0, Number(lv.xpTotal) || 0);
  const progress = Math.max(0, Math.min(100, Number(lv.progressPercent) || 0));
  const xpToNext = Math.max(0, Number(lv.xpToNext) || 0);
  const xpInLevel = Math.max(0, Number(lv.xpInLevel) || 0);
  const xpNeeded = Math.max(0, Number(lv.xpNeeded) || 0);
  const nextLevel = lv.nextLevel != null ? Number(lv.nextLevel) : null;
  const progressHint =
    nextLevel != null && xpToNext > 0
      ? `Seviye ${nextLevel} için ${xpToNext} XP kaldı (${xpInLevel} / ${xpNeeded})`
      : levelNo > 0
        ? "Maksimum seviyeye ulaşıldı"
        : "Henüz seviye kazanılmadı";
  return `<article class="admin-member-level-card">
    <div class="admin-member-level-card__head">
      <div>
        <p class="admin-member-level-card__kicker">Seviye</p>
        <p class="admin-member-level-card__level">Seviye <strong>${levelNo}</strong></p>
      </div>
      <span class="admin-member-level-card__xp-total tabular-nums">${xpTotal} XP</span>
    </div>
    <div class="admin-member-level-card__meter" role="presentation">
      <span class="admin-member-level-card__meter-fill" style="width:${progress}%"></span>
    </div>
    <p class="admin-member-level-card__hint">${escapeHtml(progressHint)}</p>
  </article>`;
}

const TAB_ORDER = [
  "my-profile",
  "dashboard",
  "audit",
  "members",
  "market-requests",
  "complaints",
  "affiliate-requests",
  "ip-clashes",
  "chat",
  "sponsors",
  "sponsor-promo-codes",
  "bonuses",
  "market",
  "social",
  "site",
  "maintenance",
  "popup",
  "legal-pages",
  "gamdom-turnuva",
  "css",
  "wheel",
  "games-covers",
  "levels",
  "promo-codes",
  "telegram-invites",
  "telegram-broadcast",
  "special-odds",
  "tickets",
  "site-events",
  "site-news",
  "site-notifications",
  "staff",
];

function canPerm(key) {
  if (key === "my-profile") return state.authenticated === true;
  if (key === "staff") return state.super === true;
  if (key === "sponsor-promo-codes") return canPerm("sponsors");
  if (key === "bonuses") {
    if (state.super) return true;
    return state.permissions.includes("bonuses") || state.permissions.includes("sponsors");
  }
  if (key === "site-events") return canPerm("events");
  if (key === "site-news") return canPerm("news");
  if (key === "site-notifications") return canPerm("notifications");
  if (key === "maintenance" || key === "css" || key === "popup" || key === "legal-pages" || key === "gamdom-turnuva" || key === "games-covers" || key === "levels") return canPerm("site");
  if (state.super) return true;
  return state.permissions.includes(key);
}

function firstAllowedTab() {
  for (const t of TAB_ORDER) {
    if (!canPerm(t)) continue;
    return t;
  }
  return "dashboard";
}

function ensureAllowedTab() {
  if (TAB_ORDER.includes(state.tab) && canPerm(state.tab)) return;
  state.tab = firstAllowedTab();
}

let adminHashSyncInternal = false;
let adminHashListenerInstalled = false;

function parseAdminTabFromHash() {
  const raw = window.location.hash.replace(/^#/, "").trim();
  if (!raw) return null;
  try {
    const t = decodeURIComponent(raw);
    return TAB_ORDER.includes(t) ? t : null;
  } catch {
    return null;
  }
}

function syncAdminUrlToTab() {
  if (!canPerm(state.tab)) return;
  const next = "#" + encodeURIComponent(state.tab);
  if (window.location.hash === next) return;
  adminHashSyncInternal = true;
  history.replaceState(null, document.title, window.location.pathname + window.location.search + next);
  queueMicrotask(() => {
    adminHashSyncInternal = false;
  });
}

async function loadDataForAdminTab(next) {
  if (next === "my-profile") {
    await loadMyAdminProfile();
    return;
  }
  if (next === "members" || next === "ip-clashes") {
    try {
      await loadMembers();
    } catch (e) {
      toast(e.message || "Üyeler yüklenemedi", "err");
    }
  }
  if (next === "complaints") {
    try {
      await loadComplaints();
    } catch (e) {
      toast(e.message || "Şikayetler yüklenemedi", "err");
      state.complaints = [];
    }
  }
  if (next === "affiliate-requests") {
    try {
      await loadAffiliateRequests();
    } catch (e) {
      toast(e.message || "Affiliate talepleri yüklenemedi", "err");
      state.affiliateRequests = [];
    }
  }
  if (next === "market-requests") {
    try {
      await loadMarketRequests();
    } catch (e) {
      toast(e.message || "Talepler yüklenemedi", "err");
      state.marketRequests = [];
    }
  }
  if (next === "dashboard") {
    try {
      await Promise.all([
        loadDashboardStats(),
        canUseAdminHeaderAlerts() ? loadAdminHeaderAlerts({ silent: true }) : Promise.resolve(),
      ]);
    } catch (e) {
      toast(e.message || "Özet yüklenemedi", "err");
      state.dashboardStats = null;
    }
  }
  if (next === "audit") {
    try {
      await loadAuditLog();
    } catch (e) {
      toast(e.message || "Denetim günlüğü yüklenemedi", "err");
      state.auditLog = [];
      state.auditTotal = 0;
    }
  }
  if (next === "staff" && state.super) {
    try {
      await loadStaff();
    } catch (e) {
      toast(e.message || "Personel listesi yüklenemedi", "err");
      state.operators = [];
    }
  }
  if (next === "promo-codes") {
    try {
      await loadPromoCodes();
    } catch (e) {
      toast(e.message || "Promo kodlar yüklenemedi", "err");
      state.promoCodes = [];
    }
  }
  if (next === "telegram-invites") {
    try {
      await loadTelegramInvites();
    } catch (e) {
      toast(e.message || "Telegram davet yüklenemedi", "err");
      state.telegramInvites = null;
    }
  }
  if (next === "telegram-broadcast") {
    try {
      await loadTelegramBroadcast();
    } catch (e) {
      toast(e.message || "Telegram yayın yüklenemedi", "err");
      state.telegramBroadcast = null;
    }
  }
  if (next === "levels") {
    try {
      await loadLevelSystem();
    } catch (e) {
      toast(e.message || "Seviye sistemi yüklenemedi", "err");
      state.levelSystem = null;
    }
  }
  if (next === "special-odds") {
    try {
      await loadSpecialOdds();
    } catch (e) {
      toast(e.message || "Özel oranlar yüklenemedi", "err");
      state.specialOddsMatches = [];
    }
  }
  if (next === "site-events") {
    try {
      await loadSiteEvents();
      await loadJoinGuides();
    } catch {
      state.siteEvents = [];
    }
  }
  if (next === "site-news") {
    try {
      await loadSiteNews();
    } catch {
      state.siteNews = [];
    }
  }
  if (next === "site-notifications") {
    try {
      await loadSiteNotifications();
    } catch {
      state.siteNotifications = [];
    }
  }
  if (next === "tickets") {
    try {
      await loadTicketEvents();
      await loadJoinGuides();
    } catch (e) {
      toast(e.message || "Biletler yüklenemedi", "err");
      state.ticketEvents = [];
    }
  }
  if (next === "games-covers" && canPerm("site")) {
    try {
      await loadGamesCovers();
    } catch (e) {
      toast(e.message || "Oyun görselleri yüklenemedi", "err");
      state.gamesCovers = null;
    }
  }
  if (next === "chat") {
    try {
      await loadAdminChat();
    } catch (e) {
      toast(e.message || "Sohbet yüklenemedi", "err");
      state.chatMessages = [];
      state.chatMutes = [];
    }
  }
}

function installAdminHashListener() {
  if (adminHashListenerInstalled) return;
  adminHashListenerInstalled = true;
  window.addEventListener("hashchange", async () => {
    if (adminHashSyncInternal) return;
    const t = parseAdminTabFromHash();
    if (!t || !canPerm(t)) {
      adminHashSyncInternal = true;
      history.replaceState(
        null,
        document.title,
        window.location.pathname + window.location.search + "#" + encodeURIComponent(state.tab)
      );
      queueMicrotask(() => {
        adminHashSyncInternal = false;
      });
      return;
    }
    if (t === state.tab) return;
    captureCurrentTab();
    state.editSponsorId = null;
    state.editMarketId = null;
    state.editPasswordUserId = null;
    state.viewMemberProfileUserId = null;
    state.memberProfileDetail = null;
    state.memberProfileActiveTab = "overview";
    state.memberProfileBlackjackPage = 0;
    state.memberProfileSlotPage = 0;
    state.tab = t;
    try {
      await loadDataForAdminTab(t);
    } catch (e) {
      toast(e.message || "Sekme yüklenemedi", "err");
    }
    renderDashboard();
  });
}

function newSponsor() {
  return {
    id: String(Date.now()),
    title: "Yeni sponsor",
    logo: "",
    promo: ["Öne çıkan satır 1", "Satır 2"],
    description: "",
    url: "https://example.com",
    badge: null,
  };
}

function newSocial() {
  return {
    id: "s" + Date.now(),
    name: "Yeni bağlantı",
    href: "https://t.me/alpingoco",
    icon: ICON_PRESETS.telegram,
  };
}

function newMarket() {
  return {
    id: "m-" + Date.now(),
    title: "Yeni ürün",
    logo: "",
    description: "",
    price: 100,
    stock: 10,
  };
}

function newSponsorPromoCode() {
  return {
    id: "spc-" + Date.now(),
    sponsorId: "",
    sponsorTitle: "",
    code: "",
    label: "",
    description: "",
    expiresAt: null,
    url: "",
    isActive: true,
    createdAt: Math.floor(Date.now() / 1000),
  };
}

function newBonus() {
  return {
    id: "bonus-" + Date.now(),
    sponsorId: "",
    sponsorTitle: "",
    title: "",
    badge: "",
    description: "",
    promoCode: "",
    expiresAt: null,
    url: "",
    isActive: true,
    createdAt: Math.floor(Date.now() / 1000),
  };
}

function resetAdminAuthState() {
  state.authenticated = false;
  state.super = false;
  state.adminKind = "master";
  state.adminUsername = null;
  state.adminDisplayName = null;
  state.permissions = [];
  state.headerAlerts = { totalPending: 0, sections: [], items: [], serverTime: 0 };
  state.headerAlertsOpen = false;
  state.myAdminProfile = null;
  state.loginTwofaRequired = false;
  state.loginTwofaChallenge = null;
  adminAlertsLastTotal = 0;
  adminAlertsBootstrapped = false;
  if (adminAlertsPollTimer != null) {
    clearInterval(adminAlertsPollTimer);
    adminAlertsPollTimer = null;
  }
}

async function loadSession() {
  state.adminGateRequired = false;
  try {
    const j = await api("session.php", { method: "GET" });
    if (typeof j.csrfToken === "string" && j.csrfToken.length > 0) {
      adminCsrfToken = j.csrfToken;
    }
    if (!j.authenticated) {
      resetAdminAuthState();
      return null;
    }
    state.authenticated = true;
    state.super = j.super === true;
    state.adminKind = j.kind || "master";
    state.adminUsername = j.username ?? null;
    state.adminDisplayName = j.displayName ?? null;
    state.permissions = Array.isArray(j.permissions) ? j.permissions : [];
    return j;
  } catch (e) {
    resetAdminAuthState();
    if (e?.body?.error === "not_found") {
      state.adminGateRequired = true;
    }
    return null;
  }
}

async function loadStaff() {
  const j = await api("admin_staff.php", { method: "GET" });
  state.operators = Array.isArray(j.operators) ? j.operators : [];
}

function emptyLocaleBlock() {
  return {
    telegramUrl: "",
    contactIntro: "",
    contactCardTitle: "",
    contactCardSubtitle: "",
    contactButton: "",
    contactEyebrow: "",
    contactHeading: "",
  };
}

function migrateFlatSiteForAdmin(flat) {
  const tg = typeof flat.telegramUrl === "string" ? flat.telegramUrl : "";
  const intro = typeof flat.contactIntro === "string" ? flat.contactIntro : "";
  const ctitle = typeof flat.contactCardTitle === "string" ? flat.contactCardTitle : "";
  const csub = typeof flat.contactCardSubtitle === "string" ? flat.contactCardSubtitle : "";
  const cbtn = typeof flat.contactButton === "string" ? flat.contactButton : "Telegram'da yaz";
  const css = typeof flat.customCss === "string" ? flat.customCss : "";
  const logoUrl = typeof flat.logoUrl === "string" ? flat.logoUrl : "";
  const faviconUrl = typeof flat.faviconUrl === "string" ? flat.faviconUrl : "";
  const ogImageUrl = typeof flat.ogImageUrl === "string" ? flat.ogImageUrl : "";
  return {
    customCss: css,
    logoUrl,
    faviconUrl,
    ogImageUrl,
    defaultLocale: "tr",
    maintenance: normalizeMaintenanceForAdmin(flat.maintenance),
    youtube: normalizeYoutubeForAdmin(flat.youtube),
    locales: {
      tr: {
        telegramUrl: tg,
        contactIntro: intro,
        contactCardTitle: ctitle,
        contactCardSubtitle: csub,
        contactButton: cbtn,
        contactEyebrow: "İletişim",
        contactHeading: "İletişim",
      },
      en: {
        telegramUrl: tg,
        contactIntro: "",
        contactCardTitle: "",
        contactCardSubtitle: "",
        contactButton: "Message on Telegram",
        contactEyebrow: "Contact",
        contactHeading: "Contact",
      },
    },
  };
}

const MAINTENANCE_DEFAULT_COPY = {
  tr: {
    eyebrow: "Bakım modu",
    title: "Site bakımda",
    message:
      "Mocker şu an kısa süreli bakım çalışması yapıyor. En kısa sürede yeniden hizmetinizdeyiz.",
  },
  en: {
    eyebrow: "Maintenance",
    title: "Site under maintenance",
    message:
      "Mocker is undergoing brief maintenance. We will be back online as soon as possible.",
  },
};

function normalizeMaintenanceForAdmin(raw) {
  const pick = (b, k, fb = "") => (typeof b[k] === "string" ? b[k] : fb);
  if (!raw || typeof raw !== "object") {
    return {
      enabled: false,
      imageUrl: "",
      locales: {
        tr: { ...MAINTENANCE_DEFAULT_COPY.tr },
        en: { ...MAINTENANCE_DEFAULT_COPY.en },
      },
    };
  }
  const locs = raw.locales && typeof raw.locales === "object" ? raw.locales : {};
  const trRaw = locs.tr && typeof locs.tr === "object" ? locs.tr : {};
  const enRaw = locs.en && typeof locs.en === "object" ? locs.en : {};
  return {
    enabled: Boolean(raw.enabled),
    imageUrl: typeof raw.imageUrl === "string" ? raw.imageUrl.trim() : "",
    locales: {
      tr: {
        eyebrow: pick(trRaw, "eyebrow", MAINTENANCE_DEFAULT_COPY.tr.eyebrow),
        title: pick(trRaw, "title", MAINTENANCE_DEFAULT_COPY.tr.title),
        message: pick(trRaw, "message", MAINTENANCE_DEFAULT_COPY.tr.message),
      },
      en: {
        eyebrow: pick(enRaw, "eyebrow", MAINTENANCE_DEFAULT_COPY.en.eyebrow),
        title: pick(enRaw, "title", MAINTENANCE_DEFAULT_COPY.en.title),
        message: pick(enRaw, "message", MAINTENANCE_DEFAULT_COPY.en.message),
      },
    },
  };
}

function normalizeYoutubeForAdmin(raw) {
  const yt = raw && typeof raw === "object" ? raw : {};
  let channelId = typeof yt.channelId === "string" ? yt.channelId.trim() : "";
  let handle = typeof yt.handle === "string" ? yt.handle.trim().replace(/^@+/, "") : "";
  let channelUrl = typeof yt.channelUrl === "string" ? yt.channelUrl.trim() : "";
  let maxVideos = parseInt(yt.maxVideos, 10);
  if (!Number.isFinite(maxVideos) || maxVideos < 1) maxVideos = 4;
  if (maxVideos > 12) maxVideos = 12;
  if (!channelUrl && handle) channelUrl = `https://www.youtube.com/@${handle}`;
  if (!channelUrl && channelId) channelUrl = `https://www.youtube.com/channel/${channelId}`;
  return { channelId, handle, channelUrl, maxVideos };
}

function youtubeAdminDisplayInput(yt) {
  const c = normalizeYoutubeForAdmin(yt);
  if (c.channelUrl) return c.channelUrl;
  if (c.handle) return `@${c.handle}`;
  if (c.channelId) return c.channelId;
  return "";
}

function parseYoutubeAdminInput(input, maxVideos = 4) {
  const raw = String(input || "").trim();
  const mv = Math.max(1, Math.min(12, Number(maxVideos) || 4));
  const out = { channelId: "", handle: "", channelUrl: "", maxVideos: mv };
  if (!raw) return out;
  if (/^UC[a-zA-Z0-9_-]{22}$/.test(raw)) {
    out.channelId = raw;
    out.channelUrl = `https://www.youtube.com/channel/${raw}`;
    return out;
  }
  if (/^https?:\/\//i.test(raw)) {
    out.channelUrl = raw.split("?")[0].replace(/\/+$/, "");
    const h = raw.match(/youtube\.com\/@([^/?#]+)/i);
    const id = raw.match(/youtube\.com\/channel\/(UC[a-zA-Z0-9_-]{22})/i);
    if (h) out.handle = decodeURIComponent(h[1]);
    if (id) out.channelId = id[1];
    return out;
  }
  const handle = raw.replace(/^@+/, "");
  out.handle = handle;
  out.channelUrl = `https://www.youtube.com/@${handle}`;
  return out;
}

const JOIN_GUIDE_ADMIN_DEFAULTS = {
  events: {
    tr: {
      title: "Nasıl katılırım?",
      steps: [
        "{site} sitesine git ve hesabınla giriş yap.",
        "Sitedeki etkinlik / kampanya alanından Mocker etkinliğine talebini ilet.",
        "Onaylanan talepler etkinlik havuzuna eklenir.",
        "Sonuçlar burada ilan edilir; kazananlar aşağıda listelenir.",
      ],
    },
    en: {
      title: "How to join",
      steps: [
        "Go to {site} and log in with your account.",
        "Submit your request for the Mocker event from the partner site.",
        "Approved requests are added to the event pool.",
        "Results are published here; winners are listed below.",
      ],
    },
  },
  tickets: {
    tr: {
      title: "Nasıl katılırım?",
      steps: [
        "{site} sitesine git ve hesabınla giriş yap.",
        "Sitedeki bilet / çekiliş alanından Mocker etkinliğine talebini ilet.",
        "Onaylanan talepler çekiliş havuzuna eklenir.",
        "Sonuçlar burada ilan edilir; kazananlar aşağıda listelenir.",
      ],
    },
    en: {
      title: "How to join",
      steps: [
        "Go to {site} and sign in with your account.",
        "Submit your request for this Mocker event in the site's ticket / draw section.",
        "Approved requests are added to the draw pool.",
        "Results are published here; winners are listed below.",
      ],
    },
  },
};

function normalizeJoinGuideLocale(raw, kind, loc) {
  const defs = JOIN_GUIDE_ADMIN_DEFAULTS[kind]?.[loc] || JOIN_GUIDE_ADMIN_DEFAULTS.events.tr;
  const pick = (v, fb) => (typeof v === "string" && v.trim() !== "" ? v.trim() : fb);
  const stepsIn = Array.isArray(raw?.steps) ? raw.steps : [];
  const steps = [0, 1, 2, 3].map((i) => pick(stepsIn[i], defs.steps[i] || ""));
  return { title: pick(raw?.title, defs.title), steps };
}

function normalizeJoinGuidesForAdmin(raw) {
  const src = raw && typeof raw === "object" ? raw : {};
  const out = {};
  for (const kind of ["events", "tickets"]) {
    const block = src[kind] && typeof src[kind] === "object" ? src[kind] : {};
    const locs = block.locales && typeof block.locales === "object" ? block.locales : {};
    out[kind] = {
      locales: {
        tr: normalizeJoinGuideLocale(locs.tr, kind, "tr"),
        en: normalizeJoinGuideLocale(locs.en, kind, "en"),
      },
    };
  }
  return out;
}

function siteJoinGuideLocaleBlockHtml(kind, pre, label, block) {
  const L = block || normalizeJoinGuideLocale({}, kind, pre);
  const steps = (L.steps || []).map(
    (step, i) => `
      <div class="admin-cms-card__field admin-cms-card__field--full">
        <label class="admin-label" for="site-join-${kind}-${pre}-step-${i + 1}">Adım ${i + 1}</label>
        <textarea id="site-join-${kind}-${pre}-step-${i + 1}" class="admin-input admin-cms-modal__textarea" rows="2">${escapeHtml(step)}</textarea>
      </div>`,
  );
  return `
    <div class="admin-cms-card__locale-block">
      <p class="admin-cms-card__locale-label">${escapeHtml(label)}</p>
      <div class="admin-cms-card__field admin-cms-card__field--full">
        <label class="admin-label" for="site-join-${kind}-${pre}-title">Bölüm başlığı</label>
        <input id="site-join-${kind}-${pre}-title" class="admin-input" value="${escapeHtml(L.title || "")}" autocomplete="off" />
      </div>
      ${steps.join("")}
    </div>`;
}

function siteJoinGuideAdminPanelHtml(kind) {
  const guides = state.joinGuides || normalizeJoinGuidesForAdmin({});
  const g = guides[kind] || normalizeJoinGuidesForAdmin({})[kind];
  const title =
    kind === "tickets"
      ? "Biletler — Nasıl katılırım (sitede)"
      : "Etkinlikler — Nasıl katılırım (sitede)";
  const lead =
    kind === "tickets"
      ? "Üye «Bilet talep et» dediğinde «Nasıl katılırım» sekmesinde görünür."
      : "Üye «Katılım talep et» dediğinde «Nasıl katılırım» sekmesinde görünür.";
  return `
    <article class="admin-panel admin-join-guide-panel overflow-hidden p-0 mb-6">
      <div class="admin-ticket-create-panel__head flex flex-wrap items-start justify-between gap-3">
        <div>
          <h2 class="admin-section__title text-lg">${escapeHtml(title)}</h2>
          <p class="mt-1 text-sm text-slate-500">${escapeHtml(lead)} <code class="admin-code">{site}</code> partner adıyla değiştirilir.</p>
        </div>
        <button type="button" class="admin-btn admin-btn-primary btn-save-join-guide" data-join-guide-kind="${escapeHtml(kind)}">Metinleri kaydet</button>
      </div>
      <div class="admin-ticket-create-form border-t border-white/10">
        <div class="admin-cms-card__fields admin-cms-card__fields--2col admin-cms-card__fields--locales p-4 sm:p-6">
          ${siteJoinGuideLocaleBlockHtml(kind, "tr", "Türkçe (TR)", g?.locales?.tr)}
          ${siteJoinGuideLocaleBlockHtml(kind, "en", "English (EN)", g?.locales?.en)}
        </div>
      </div>
    </article>`;
}

async function loadJoinGuides() {
  try {
    const j = await api("admin_join_guides.php", { method: "GET" });
    if (j?.ok && j.joinGuides) {
      state.joinGuides = normalizeJoinGuidesForAdmin(j.joinGuides);
      return;
    }
  } catch {
    /* */
  }
  state.joinGuides = normalizeJoinGuidesForAdmin(state.site?.joinGuides || {});
}

function readJoinGuideFromDom(kind) {
  return {
    locales: {
      tr: {
        title: $(`#site-join-${kind}-tr-title`)?.value.trim() ?? "",
        steps: [1, 2, 3, 4].map((n) => $(`#site-join-${kind}-tr-step-${n}`)?.value.trim() ?? ""),
      },
      en: {
        title: $(`#site-join-${kind}-en-title`)?.value.trim() ?? "",
        steps: [1, 2, 3, 4].map((n) => $(`#site-join-${kind}-en-step-${n}`)?.value.trim() ?? ""),
      },
    },
  };
}

async function saveJoinGuideFromDom(kind) {
  const guide = readJoinGuideFromDom(kind);
  const j = await api("admin_join_guides.php", {
    method: "POST",
    body: JSON.stringify({ kind, guide }),
  });
  if (!j?.ok) throw new Error(j?.error || "save");
  const saved = j.guide && typeof j.guide === "object" ? j.guide : guide;
  state.joinGuides = state.joinGuides || normalizeJoinGuidesForAdmin({});
  state.joinGuides[kind] = {
    locales: {
      tr: normalizeJoinGuideLocale(saved.locales?.tr, kind, "tr"),
      en: normalizeJoinGuideLocale(saved.locales?.en, kind, "en"),
    },
  };
  if (state.site && typeof state.site === "object") {
    state.site = normalizeSiteForAdmin({ ...state.site, joinGuides: state.joinGuides });
  }
}

function normalizeSiteForAdmin(raw) {
  if (!raw || typeof raw !== "object") return migrateFlatSiteForAdmin({});
  const o =  (raw);
  if (o.locales && typeof o.locales === "object") {
    const locs =  (o.locales);
    const trRaw = locs.tr && typeof locs.tr === "object" ?  (locs.tr) : {};
    const enRaw = locs.en && typeof locs.en === "object" ?  (locs.en) : {};
    const pick = (b, k, fb = "") => (typeof b[k] === "string" ? b[k] : fb);
    const d = emptyLocaleBlock();
    return {
      customCss: typeof o.customCss === "string" ? o.customCss : "",
      logoUrl: typeof o.logoUrl === "string" ? o.logoUrl.trim() : "",
      faviconUrl: typeof o.faviconUrl === "string" ? o.faviconUrl.trim() : "",
      ogImageUrl: typeof o.ogImageUrl === "string" ? o.ogImageUrl.trim() : "",
      defaultLocale: o.defaultLocale === "en" ? "en" : "tr",
      maintenance: normalizeMaintenanceForAdmin(o.maintenance),
      youtube: normalizeYoutubeForAdmin(o.youtube),
      joinGuides: normalizeJoinGuidesForAdmin(o.joinGuides),
      locales: {
        tr: {
          telegramUrl: pick(trRaw, "telegramUrl", d.telegramUrl),
          contactIntro: pick(trRaw, "contactIntro", d.contactIntro),
          contactCardTitle: pick(trRaw, "contactCardTitle", d.contactCardTitle),
          contactCardSubtitle: pick(trRaw, "contactCardSubtitle", d.contactCardSubtitle),
          contactButton: pick(trRaw, "contactButton", "Telegram'da yaz"),
          contactEyebrow: pick(trRaw, "contactEyebrow", "İletişim"),
          contactHeading: pick(trRaw, "contactHeading", "İletişim"),
        },
        en: {
          telegramUrl: pick(enRaw, "telegramUrl", pick(trRaw, "telegramUrl", "")),
          contactIntro: pick(enRaw, "contactIntro", d.contactIntro),
          contactCardTitle: pick(enRaw, "contactCardTitle", d.contactCardTitle),
          contactCardSubtitle: pick(enRaw, "contactCardSubtitle", d.contactCardSubtitle),
          contactButton: pick(enRaw, "contactButton", "Message on Telegram"),
          contactEyebrow: pick(enRaw, "contactEyebrow", "Contact"),
          contactHeading: pick(enRaw, "contactHeading", "Contact"),
        },
      },
    };
  }
  return migrateFlatSiteForAdmin(o);
}

async function loadAll() {
  if (!canPerm("sponsors") && !canPerm("bonuses") && !canPerm("social") && !canPerm("site") && !canPerm("market")) {
    state.sponsors = [];
    state.social = [];
    state.site = {};
    state.popup = {};
    state.gamdomTurnuva = {};
    state.legalPages = normalizeLegalPagesForAdmin({});
    state.market = [];
    return;
  }
  const j = await api("data.php", { method: "GET" });
  state.sponsors = Array.isArray(j.sponsors) ? j.sponsors : [];
  state.social = Array.isArray(j.social) ? j.social : [];
  state.site = normalizeSiteForAdmin(j.site && typeof j.site === "object" ? j.site : {});
  state.popup = j.popup && typeof j.popup === "object" ? j.popup : {};
  state.gamdomTurnuva = j.gamdomTurnuva && typeof j.gamdomTurnuva === "object" ? j.gamdomTurnuva : {};
  state.legalPages = normalizeLegalPagesForAdmin(
    j.legalPages && typeof j.legalPages === "object" ? j.legalPages : {},
  );
  state.market = Array.isArray(j.market) ? j.market : [];
  state.sponsorPromoCodes = Array.isArray(j.sponsorPromoCodes) ? j.sponsorPromoCodes : [];
  state.bonuses = Array.isArray(j.bonuses) ? j.bonuses : [];
}

async function loadGamesCovers() {
  const j = await api("admin_games.php", { method: "GET" });
  if (j && j.ok && j.covers && typeof j.covers === "object") {
    state.gamesCovers = j.covers;
  } else {
    state.gamesCovers = { blackjack: "", slot: "", roulette: "" };
  }
  if (j && j.ok && j.settings && typeof j.settings === "object") {
    state.gamesSettings = j.settings;
  } else {
    state.gamesSettings = {
      winChancePercent: { slot: 100, roulette: 100 },
    };
  }
}

const GAMES_COVER_PLACEHOLDER =
  "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";

const GAMES_WIN_BADGE = { slot: "SL", roulette: "RL" };

function gamesWinChanceFieldHtml(key, title, hint) {
  const pct = state.gamesSettings?.winChancePercent || {};
  const val = Math.max(0, Math.min(100, parseInt(String(pct[key] ?? 100), 10) || 100));
  const badge = GAMES_WIN_BADGE[key] || title.slice(0, 2).toUpperCase();
  return `
    <article class="admin-games-win-card" data-game="${escapeHtml(key)}">
      <header class="admin-games-win-card__head">
        <span class="admin-games-win-card__badge" aria-hidden="true">${escapeHtml(badge)}</span>
        <div class="admin-games-win-card__intro">
          <h4 class="admin-games-win-card__title">${escapeHtml(title)}</h4>
          <p class="admin-games-win-card__hint">${escapeHtml(hint)}</p>
        </div>
        <span class="admin-games-win-card__pct" id="games-win-chance-display-${key}">${val}%</span>
      </header>
      <div class="admin-games-win-card__meter" role="presentation">
        <span class="admin-games-win-card__meter-fill" id="games-win-chance-meter-${key}" style="width:${val}%"></span>
      </div>
      <label class="admin-games-win-card__controls" for="games-win-chance-${key}">
        <span class="admin-label">Kazanç olasılığı</span>
        <div class="admin-games-win-card__row">
          <input
            type="range"
            id="games-win-chance-range-${key}"
            class="admin-games-win-card__range"
            min="0"
            max="100"
            step="1"
            value="${val}"
            aria-valuemin="0"
            aria-valuemax="100"
            aria-valuenow="${val}"
          />
          <input
            type="number"
            id="games-win-chance-${key}"
            class="admin-input admin-games-win-card__number"
            min="0"
            max="100"
            step="1"
            value="${val}"
          />
          <span class="admin-games-win-card__suffix">%</span>
        </div>
      </label>
      <p class="admin-games-win-card__note"><strong>100</strong> = normal şans · <strong>30</strong> ≈ her 10 turda ortalama 3 kazanç.</p>
    </article>`;
}

function gamesCoversPanelHtml() {
  return `
    <div class="admin-cms-page admin-games-page">
      <header class="admin-data-page__head">
        <h2 class="admin-data-page__title">Oyun ayarları</h2>

      </header>
      ${cmsPanelToolbarHtml()}
      <div class="admin-games-layout">
        <section class="admin-games-section" aria-labelledby="games-section-win-title">
          <header class="admin-games-section__head">
            <div class="admin-games-section__copy">
              <p class="admin-games-section__kicker">Olasılık</p>
              <h3 class="admin-games-section__title" id="games-section-win-title">Kazanç ayarları</h3>
              <p class="admin-games-section__lead">Yalnızca kazanç sayılan sonuçlar bu orana bağlıdır.</p>
            </div>
            <span class="admin-games-section__pill">Blackjack hariç</span>
          </header>
          <div class="admin-games-section__body">
            <p class="admin-games-callout">Blackjack tüm kartlar açık oynandığı için klasik kurallara göre çalışır; bu ayardan etkilenmez.</p>
            <div class="admin-games-covers-grid admin-games-covers-grid--win">
              ${gamesWinChanceFieldHtml("slot", "Slot", "Çizgi ödemesi ve jackpot")}
              ${gamesWinChanceFieldHtml("roulette", "Rulet", "İsabet eden bahisler")}
            </div>
          </div>
        </section>
        <section class="admin-games-section" aria-labelledby="games-section-covers-title">
          <header class="admin-games-section__head">
            <div class="admin-games-section__copy">
              <p class="admin-games-section__kicker">Görsel</p>
              <h3 class="admin-games-section__title" id="games-section-covers-title">Kapak görselleri</h3>
              <p class="admin-games-section__lead">Oyunlar sayfasındaki kart üst görselleri — 16∶10 oran.</p>
            </div>
          </header>
          <div class="admin-games-section__body">
            <p class="admin-games-callout">Boş bırakırsan varsayılan görsel kullanılır. URL yazarken önizleme anında güncellenir.</p>
            <div class="admin-games-covers-grid admin-games-covers-grid--covers">
              ${gamesCoverFieldHtml("blackjack", "Blackjack", "Mocker Blackjack kartı")}
              ${gamesCoverFieldHtml("slot", "Slot", "Mocker Slot kartı")}
              ${gamesCoverFieldHtml("roulette", "Rulet", "Mocker Rulet kartı")}
            </div>
          </div>
        </section>
      </div>
    </div>`;
}

function syncGamesWinChanceVisual(key, value) {
  const v = Math.max(0, Math.min(100, value));
  const meter = $(`#games-win-chance-meter-${key}`);
  const display = $(`#games-win-chance-display-${key}`);
  const range = $(`#games-win-chance-range-${key}`);
  if (meter) meter.style.width = `${v}%`;
  if (display) display.textContent = `${v}%`;
  if (range) range.setAttribute("aria-valuenow", String(v));
  const card = document.querySelector(`.admin-games-win-card[data-game="${key}"]`);
  card?.classList.toggle("admin-games-win-card--muted", v < 40);
}

function bindGamesWinChanceInputs() {
  ["slot", "roulette"].forEach((key) => {
    const num = $(`#games-win-chance-${key}`);
    const range = $(`#games-win-chance-range-${key}`);
    if (!num || !range) return;
    const sync = (from) => {
      let v = parseInt(String(from === "range" ? range.value : num.value), 10);
      if (!Number.isFinite(v)) v = 100;
      v = Math.max(0, Math.min(100, v));
      num.value = String(v);
      range.value = String(v);
      syncGamesWinChanceVisual(key, v);
    };
    num.addEventListener("input", () => sync("num"));
    range.addEventListener("input", () => sync("range"));
    syncGamesWinChanceVisual(key, parseInt(num.value, 10) || 100);
  });
}

function readGamesWinChanceFromDom() {
  const out = { slot: 100, roulette: 100 };
  ["slot", "roulette"].forEach((key) => {
    const raw = parseInt($(`#games-win-chance-${key}`)?.value || "100", 10);
    out[key] = Number.isFinite(raw) ? Math.max(0, Math.min(100, raw)) : 100;
  });
  return out;
}

function gamesCoverFieldHtml(key, title, hint) {
  const c = state.gamesCovers || {};
  const url = String(c[key] || "").trim();
  const hasCover = url.length > 0;
  const previewSrc = url || GAMES_COVER_PLACEHOLDER;
  const shortKey = key === "blackjack" ? "BJ" : key === "roulette" ? "RL" : "SL";
  return `
    <article class="admin-games-cover-card" data-game-cover="${escapeHtml(key)}">
      <header class="admin-games-cover-card__head">
        <span class="admin-games-cover-card__badge" aria-hidden="true">${escapeHtml(shortKey)}</span>
        <div class="admin-games-cover-card__intro">
          <h4 class="admin-games-cover-card__title">${escapeHtml(title)}</h4>
          <p class="admin-games-cover-card__hint">${escapeHtml(hint)}</p>
        </div>
      </header>
      <div class="admin-games-cover-card__body">
        <div class="admin-games-cover-card__preview-wrap">
          <div class="admin-games-cover-card__preview${hasCover ? "" : " admin-games-cover-card__preview--empty"}">
            <img id="games-cover-preview-${key}" src="${escapeHtml(previewSrc)}" alt="" class="admin-games-cover-card__img" loading="lazy" decoding="async" />
            ${hasCover ? "" : `<span class="admin-games-cover-card__empty">Önizleme</span>`}
            <span class="admin-games-cover-card__ratio">16∶10</span>
          </div>
        </div>
        <label class="admin-games-cover-card__field" for="games-cover-url-${key}">
          <span class="admin-label">Görsel URL</span>
          <input type="url" id="games-cover-url-${key}" class="admin-input admin-games-cover-card__input font-mono text-xs" value="${escapeHtml(url)}" placeholder="https://cdn.example.com/kapak.jpg" autocomplete="off" />
          <span class="admin-games-cover-card__field-hint"><code>https://</code> ile başlayan doğrudan görsel linki.</span>
        </label>
      </div>
    </article>`;
}

function syncGamesCoverPreview(key) {
  const input = $(`#games-cover-url-${key}`);
  const img = $(`#games-cover-preview-${key}`);
  const card = document.querySelector(`[data-game-cover="${key}"]`);
  const preview = card?.querySelector(".admin-games-cover-card__preview");
  if (!input || !img) return;
  const v = String(input.value || "").trim();
  img.src = v || GAMES_COVER_PLACEHOLDER;
  if (preview) {
    preview.classList.toggle("admin-games-cover-card__preview--empty", !v);
    let empty = preview.querySelector(".admin-games-cover-card__empty");
    if (!v) {
      if (!empty) {
        empty = document.createElement("span");
        empty.className = "admin-games-cover-card__empty";
        empty.textContent = "Önizleme";
        preview.insertBefore(empty, preview.querySelector(".admin-games-cover-card__ratio"));
      }
    } else if (empty) {
      empty.remove();
    }
  }
}

function bindGamesCoversPreview() {
  ["blackjack", "slot", "roulette"].forEach((key) => {
    const input = $(`#games-cover-url-${key}`);
    if (!input) return;
    input.addEventListener("input", () => syncGamesCoverPreview(key));
    syncGamesCoverPreview(key);
  });
}

async function loadWheelConfig() {
  try {
    const j = await api("admin_wheel.php", { method: "GET" });
    if (j && j.ok) {
      const pr =
        Array.isArray(j.prizes) && j.prizes.length === 6
          ? j.prizes.map((x) => {
            const n = Math.floor(Number(x));
            return Number.isFinite(n) && n >= 0 ? Math.min(99999999, n) : 0;
          })
          : [10, 25, 50, 75, 100, 0];
      state.wheelConfig = {
        cooldown_hours: Math.max(1, Math.min(168, Number(j.cooldown_hours) || 24)),
        prizes: pr,
        enabled: j.enabled !== false,
      };
    }
  } catch {

  }
  if (!state.wheelConfig) {
    state.wheelConfig = { cooldown_hours: 24, prizes: [10, 25, 50, 75, 100, 0], enabled: true };
  }
}

async function loadPromoCodes() {
  const j = await api("admin_promo_codes.php", { method: "GET" });
  state.promoCodes = Array.isArray(j.codes) ? j.codes : [];
}

async function loadTelegramInvites() {
  const j = await api("admin_telegram_invites.php", { method: "GET" });
  state.telegramInvites = j && j.ok ? j : null;
}

async function loadTelegramBroadcast() {
  const j = await api("admin_telegram_broadcast.php", { method: "GET" });
  if (j && j.ok) {
    state.telegramBroadcast = {
      subscriberCount: Number(j.subscriberCount) || 0,
      telegramConfigured: !!j.telegramConfigured,
    };
  } else {
    state.telegramBroadcast = null;
  }
}

async function loadLevelSystem() {
  const j = await api("admin_levels.php", { method: "GET" });
  state.levelSystem = j && j.ok ? j : null;
}

async function loadSiteEvents() {
  const j = await api("admin_events.php", { method: "GET" });
  state.siteEvents = j && j.ok && Array.isArray(j.events) ? j.events : [];
  if (j && j.ok && Array.isArray(j.sponsors)) state.sponsorOptions = j.sponsors;
}

async function loadSiteNews() {
  const j = await api("admin_news.php", { method: "GET" });
  state.siteNews = j && j.ok && Array.isArray(j.items) ? j.items : [];
}

function tsToDatetimeLocal(ts) {
  if (!ts || !Number.isFinite(ts)) return "";
  const d = new Date(ts * 1000);
  const pad = (n) => String(n).padStart(2, "0");
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
}

function readSiteNewsFormFields() {
  const title = String(document.getElementById("site-news-title")?.value || "").trim();
  const summary = String(document.getElementById("site-news-summary")?.value || "").trim();
  const body = String(document.getElementById("site-news-body")?.value || "").trim();
  const imageUrl = String(document.getElementById("site-news-image")?.value || "").trim();
  const sortOrder = Number(document.getElementById("site-news-sort")?.value || 0);
  const isPublished = Boolean(document.getElementById("site-news-published-flag")?.checked);
  const publishedRaw = String(document.getElementById("site-news-published-at")?.value || "").trim();
  let publishedAt = null;
  if (publishedRaw) {
    const t = Math.floor(new Date(publishedRaw).getTime() / 1000);
    if (Number.isFinite(t) && t > 0) publishedAt = t;
  }
  return { title, summary, body, imageUrl, sortOrder, isPublished, publishedAt };
}

function resetSiteNewsForm() {
  state.siteNewsEditId = null;
  const title = document.getElementById("site-news-title");
  const summary = document.getElementById("site-news-summary");
  const body = document.getElementById("site-news-body");
  const image = document.getElementById("site-news-image");
  const sort = document.getElementById("site-news-sort");
  const pubAt = document.getElementById("site-news-published-at");
  const pubFlag = document.getElementById("site-news-published-flag");
  if (title) title.value = "";
  if (summary) summary.value = "";
  if (body) body.value = "";
  if (image) image.value = "";
  if (sort) sort.value = "0";
  if (pubAt) pubAt.value = "";
  if (pubFlag) pubFlag.checked = true;
}

function siteNewsPanelHtml() {
  const editing = state.siteNewsEditId ? state.siteNews.find((n) => n.id === state.siteNewsEditId) : null;
  const rows = (state.siteNews || [])
    .map((n) => {
      const pubBadge = n.isPublished
        ? `<span class="text-emerald-400">Yayında</span>`
        : `<span class="text-slate-500">Taslak</span>`;
      const thumb = n.imageUrl
        ? `<img src="${escapeHtml(n.imageUrl)}" alt="" class="h-10 w-14 rounded object-cover" loading="lazy" />`
        : `<span class="text-xs text-slate-500">—</span>`;
      return `<tr class="admin-table-row">
        <td class="px-3 py-2">${thumb}</td>
        <td class="px-3 py-2">
          <p class="font-medium text-slate-200">${escapeHtml(String(n.title || ""))}</p>
          <p class="mt-0.5 max-w-md truncate text-xs text-slate-500">${escapeHtml(String(n.summary || ""))}</p>
        </td>
        <td class="px-3 py-2 text-xs text-slate-400">${escapeHtml(formatTs(n.publishedAt))}</td>
        <td class="px-3 py-2 tabular-nums">${Number(n.sortOrder) || 0}</td>
        <td class="px-3 py-2">${pubBadge}</td>
        <td class="px-3 py-2 text-right whitespace-nowrap">
          <button type="button" class="admin-btn admin-btn-soft px-2 py-1 text-xs site-news-edit" data-news-id="${n.id}">Düzenle</button>
          <button type="button" class="admin-action-btn admin-action-btn--reject px-2 py-1 text-xs site-news-delete" data-news-id="${n.id}">Sil</button>
        </td>
      </tr>`;
    })
    .join("");

  return `
    <div class="max-w-5xl space-y-6">
      <div class="admin-panel admin-panel--padded">
        <h3 class="admin-section__title text-lg">${editing ? "Haber düzenle" : "Yeni haber"}</h3>
        <p class="mt-1 text-sm text-slate-500">Sitede yatay kart olarak listelenir; <strong>Detaylar</strong> butonu popup açar. Kapak URL yazarken önizleme anında güncellenir.</p>
        <div class="mt-4 grid gap-4 sm:grid-cols-2">
          <div class="sm:col-span-2">
            <label class="admin-label" for="site-news-title">Başlık *</label>
            <input id="site-news-title" class="admin-input w-full" value="${escapeHtml(String(editing?.title || ""))}" placeholder="500.000₺ Ödül Havuzu" />
          </div>
          <div class="sm:col-span-2">
            <label class="admin-label" for="site-news-summary">Kısa açıklama (kart)</label>
            <textarea id="site-news-summary" rows="2" class="admin-input w-full resize-y" placeholder="Kartta görünen özet metin…">${escapeHtml(String(editing?.summary || ""))}</textarea>
          </div>
          <div class="sm:col-span-2">
            <label class="admin-label" for="site-news-body">Detay metni (popup)</label>
            <textarea id="site-news-body" rows="5" class="admin-input w-full resize-y" placeholder="Detaylar butonunda açılan tam metin…">${escapeHtml(String(editing?.body || ""))}</textarea>
          </div>
          <div class="sm:col-span-2">
            <section class="admin-ticket-create-section admin-ticket-create-section--cover">
              <h3 class="admin-ticket-create-section__title">Kapak &amp; bağlantı</h3>
              ${siteNewsCoverFieldHtml(editing?.imageUrl)}
            </section>
          </div>
          <div>
            <label class="admin-label" for="site-news-published-at">Yayın tarihi</label>
            <input id="site-news-published-at" type="datetime-local" class="admin-input w-full" value="${escapeHtml(tsToDatetimeLocal(editing?.publishedAt))}" />
          </div>
          <div>
            <label class="admin-label" for="site-news-sort">Sıralama (yüksek = üstte)</label>
            <input id="site-news-sort" type="number" class="admin-input w-full tabular-nums" value="${Number(editing?.sortOrder) || 0}" />
          </div>
          <div class="sm:col-span-2 flex items-center gap-2">
            <input id="site-news-published-flag" type="checkbox" class="admin-checkbox" ${editing && !editing.isPublished ? "" : "checked"} />
            <label for="site-news-published-flag" class="text-sm text-slate-400">Sitede yayınla</label>
          </div>
        </div>
        <div class="mt-4 flex flex-wrap gap-2">
          <button type="button" id="btn-site-news-save" class="admin-btn admin-btn-primary px-5 py-2.5">${editing ? "Güncelle" : "Haber ekle"}</button>
          ${editing ? `<button type="button" id="btn-site-news-cancel-edit" class="admin-btn admin-btn-ghost px-4 py-2.5">İptal</button>` : ""}
        </div>
      </div>
      <div class="admin-panel admin-panel--padded">
        <div class="flex flex-wrap items-center justify-between gap-3">
          <h3 class="admin-section__title text-lg">Kayıtlı haberler</h3>
          <button type="button" id="btn-site-news-refresh" class="admin-btn admin-btn-ghost text-xs">Yenile</button>
        </div>
        <div class="mt-4 overflow-x-auto">
          <table class="w-full min-w-[720px] text-left text-sm">
            <thead>
              <tr class="admin-table-row text-slate-500">
                <th class="px-3 py-2 font-semibold">Görsel</th>
                <th class="px-3 py-2 font-semibold">Başlık</th>
                <th class="px-3 py-2 font-semibold">Tarih</th>
                <th class="px-3 py-2 font-semibold">Sıra</th>
                <th class="px-3 py-2 font-semibold">Durum</th>
                <th class="px-3 py-2 font-semibold text-right">İşlem</th>
              </tr>
            </thead>
            <tbody>${rows || `<tr><td colspan="6" class="px-3 py-8 text-center text-slate-500">Henüz haber yok.</td></tr>`}</tbody>
          </table>
        </div>
      </div>
    </div>`;
}

async function loadSiteNotifications() {
  const j = await api("admin_notifications.php", { method: "GET" });
  state.siteNotifications = j && j.ok && Array.isArray(j.items) ? j.items : [];
}

function siteNotificationsPanelHtml() {
  const rows = (state.siteNotifications || [])
    .map((n) => {
      const pubBadge = n.isPublished
        ? `<span class="text-emerald-400">Yayında</span>`
        : `<span class="text-slate-500">Taslak</span>`;
      const link = n.linkUrl
        ? `<span class="text-xs text-sky-400/90">${escapeHtml(String(n.linkUrl))}</span>`
        : `<span class="text-xs text-slate-500">—</span>`;
      return `<tr class="admin-table-row">
        <td class="px-3 py-2">
          <p class="font-medium text-slate-200">${escapeHtml(String(n.title || ""))}</p>
          <p class="mt-0.5 max-w-lg text-xs text-slate-500 line-clamp-2">${escapeHtml(String(n.body || ""))}</p>
        </td>
        <td class="px-3 py-2">${link}</td>
        <td class="px-3 py-2 text-xs text-slate-400">${escapeHtml(formatTs(n.createdAt))}</td>
        <td class="px-3 py-2">${pubBadge}</td>
        <td class="px-3 py-2 text-right whitespace-nowrap">
          <button type="button" class="admin-btn admin-btn-soft px-2 py-1 text-xs site-notif-toggle" data-notif-id="${n.id}" data-published="${n.isPublished ? "0" : "1"}">${n.isPublished ? "Gizle" : "Yayınla"}</button>
          <button type="button" class="admin-action-btn admin-action-btn--reject px-2 py-1 text-xs site-notif-delete" data-notif-id="${n.id}">Sil</button>
        </td>
      </tr>`;
    })
    .join("");

  return `
    <div class="max-w-4xl space-y-6">
      <div class="admin-panel admin-panel--padded">
        <h3 class="admin-section__title text-lg">Yeni bildirim gönder</h3>
        <p class="mt-1 text-sm text-slate-500">Giriş yapmış tüm üyelerin header çanında görünür. İsteğe bağlı bağlantı: <code class="text-sky-300/90">#/market</code> veya <code class="text-sky-300/90">https://…</code></p>
        <div class="mt-4 grid gap-4">
          <div>
            <label class="admin-label" for="site-notif-title">Başlık *</label>
            <input id="site-notif-title" class="admin-input w-full" placeholder="Yeni etkinlik başladı!" />
          </div>
          <div>
            <label class="admin-label" for="site-notif-body">Mesaj *</label>
            <textarea id="site-notif-body" rows="4" class="admin-input w-full resize-y" placeholder="Detaylı açıklama…"></textarea>
          </div>
          <div>
            <label class="admin-label" for="site-notif-link">Bağlantı (isteğe bağlı)</label>
            <input id="site-notif-link" class="admin-input w-full" placeholder="#/etkinlikler veya https://…" />
          </div>
          <div class="flex items-center gap-2">
            <input id="site-notif-published" type="checkbox" class="admin-checkbox" checked />
            <label for="site-notif-published" class="text-sm text-slate-400">Hemen yayınla (üyelere göster)</label>
          </div>
        </div>
        <div class="mt-4">
          <button type="button" id="btn-site-notif-send" class="admin-btn admin-btn-primary px-5 py-2.5">Bildirimi gönder</button>
        </div>
      </div>
      <div class="admin-panel admin-panel--padded">
        <div class="flex flex-wrap items-center justify-between gap-3">
          <h3 class="admin-section__title text-lg">Gönderilen bildirimler</h3>
          <button type="button" id="btn-site-notif-refresh" class="admin-btn admin-btn-ghost text-xs">Yenile</button>
        </div>
        <div class="mt-4 overflow-x-auto">
          <table class="w-full min-w-[640px] text-left text-sm">
            <thead>
              <tr class="admin-table-row text-slate-500">
                <th class="px-3 py-2 font-semibold">İçerik</th>
                <th class="px-3 py-2 font-semibold">Bağlantı</th>
                <th class="px-3 py-2 font-semibold">Tarih</th>
                <th class="px-3 py-2 font-semibold">Durum</th>
                <th class="px-3 py-2 font-semibold text-right">İşlem</th>
              </tr>
            </thead>
            <tbody>${rows || `<tr><td colspan="5" class="px-3 py-8 text-center text-slate-500">Henüz bildirim yok.</td></tr>`}</tbody>
          </table>
        </div>
      </div>
    </div>`;
}

async function loadSiteEventDetail(eventId) {
  const id = Number(eventId);
  if (!id) {
    state.siteEventDetail = null;
    return;
  }
  const j = await api(`admin_events.php?eventId=${id}`, { method: "GET" });
  if (!j || !j.ok) throw new Error(j?.error || "load");
  state.siteEventDetail = {
    event: j.event,
    requests: Array.isArray(j.requests) ? j.requests : [],
    entries: Array.isArray(j.entries) ? j.entries : [],
  };
  state.siteEventId = id;
}

function ticketEventListRowHtml(ev, openClass, deleteClass, editClass = "") {
  const thumb = ev.logoUrl
    ? `<img class="admin-ticket-event-row__thumb" src="${escapeHtml(ev.logoUrl)}" alt="" loading="lazy" />`
    : `<div class="admin-ticket-event-row__thumb flex items-center justify-center text-xs font-bold text-slate-400">ALP</div>`;
  const pending = Number(ev.pendingRequests) || 0;
  const editBtn = editClass
    ? `<button type="button" class="${editClass} admin-btn admin-btn-soft admin-ticket-event-row-edit" data-event-id="${ev.id}" title="Düzenle">Düzenle</button>`
    : "";
  return `<div class="admin-ticket-event-row-wrap">
    <button type="button" class="admin-ticket-event-row ${openClass}" data-event-id="${ev.id}">
      <div class="flex min-w-0 flex-1 items-center gap-3">
        ${thumb}
        <div class="min-w-0 text-left">
          <p class="admin-section__title truncate">${escapeHtml(ev.title)}</p>
          <p class="mt-0.5 text-xs text-slate-500">${escapeHtml(ev.sponsorTitle || "—")} · ${escapeHtml(ticketStatusLabel(ev.status))}</p>
        </div>
      </div>
      <div class="flex flex-wrap items-center gap-3 text-xs text-slate-500">
        <span><strong class="text-slate-700">${Number(ev.participantCount) || 0}</strong> katılım</span>
        <span><strong class="text-slate-700">${Number(ev.entryCount) || 0}</strong> bilet</span>
        ${pending ? `<span class="admin-ticket-badge admin-ticket-badge--pending">${pending} bekleyen</span>` : ""}
      </div>
    </button>
    <div class="admin-ticket-event-row-actions">
      ${editBtn}
      <button type="button" class="${deleteClass} admin-action-btn admin-action-btn--reject admin-ticket-event-row-delete" data-event-id="${ev.id}" title="Sil">Sil</button>
    </div>
  </div>`;
}

function resetSiteEventForm() {
  state.siteEventEditId = null;
}

function siteEventsListHtml() {
  const events = Array.isArray(state.siteEvents) ? state.siteEvents : [];
  const rows = events
    .map((ev) => ticketEventListRowHtml(ev, "site-event-open-event", "site-event-delete", "site-event-edit"))
    .join("");
  const editing = state.siteEventEditId
    ? events.find((e) => Number(e.id) === Number(state.siteEventEditId))
    : null;

  return `
    <div class="admin-tickets-wrap space-y-6">
      ${siteJoinGuideAdminPanelHtml("events")}
      <div class="admin-panel admin-ticket-create-panel overflow-hidden p-0">
        <div class="admin-ticket-create-panel__head">
          <div>
            <h2 class="admin-section__title text-lg">${editing ? "Etkinlik düzenle" : "Yeni etkinlik"}</h2>
            <p class="mt-1 text-sm text-slate-500">${editing ? "Değişiklikleri kaydedin; sonuçlandırılmış etkinlikler düzenlenemez." : "Temel bilgileri doldurun; kapak görseli sitede kartın üstünde görünür."}</p>
          </div>
        </div>
        <form id="site-event-create-form" class="admin-ticket-create-form">
          <section class="admin-ticket-create-section">
            <h3 class="admin-ticket-create-section__title">Temel bilgiler</h3>
            <div class="admin-ticket-create-section__grid">
              <label class="admin-field admin-field--full">
                <span class="admin-label">Başlık *</span>
                <input name="title" required class="admin-input w-full" placeholder="750 Bin TL Ödül Havuzu" value="${escapeHtml(String(editing?.title || ""))}" />
              </label>
              <label class="admin-field admin-field--full">
                <span class="admin-label">Açıklama (kurallar)</span>
                <textarea name="description" rows="3" class="admin-input w-full resize-y" placeholder="Üyeler «Bilet talep et» dediğinde gösterilecek kurallar…">${escapeHtml(String(editing?.description || ""))}</textarea>
              </label>
              ${ticketEventSponsorSelectHtml(editing?.sponsorId || "", editing)}
              <label class="admin-field">
                <span class="admin-label">Ödül açıklaması</span>
                <input name="prizeDescription" class="admin-input w-full" placeholder="Örn. 5000₺ nakit" value="${escapeHtml(String(editing?.prizeDescription || ""))}" />
              </label>
            </div>
          </section>

          <section class="admin-ticket-create-section admin-ticket-create-section--cover">
            <h3 class="admin-ticket-create-section__title">Kapak &amp; bağlantı</h3>
            ${ticketCoverFieldHtml("create", editing?.logoUrl || "", true)}
            <label class="admin-field admin-field--full admin-ticket-create-site-url">
              <span class="admin-label">Partner site URL</span>
              <input name="siteUrl" class="admin-input w-full" type="url" placeholder="https://partner.com/giris" value="${escapeHtml(String(editing?.siteUrl || ""))}" />
            </label>
          </section>

          <section class="admin-ticket-create-section">
            <h3 class="admin-ticket-create-section__title">Çekiliş &amp; talep kuralları</h3>
            <div class="admin-ticket-create-section__grid admin-ticket-create-section__grid--4">
              <label class="admin-field">
                <span class="admin-label">Bilet kapasitesi</span>
                <input name="maxTotalTickets" type="number" min="0" value="${editing != null ? Number(editing.maxTotalTickets) || 0 : 2000}" class="admin-input w-full" title="0 = sınırsız" />
                <span class="admin-field__hint">0 = sınırsız</span>
              </label>
              <label class="admin-field">
                <span class="admin-label">Katılım / yatırım (TRY)</span>
                <input name="investmentPerTicket" type="number" min="1" step="1" value="${editing != null ? Number(editing.investmentPerTicket) || 1000 : 1000}" class="admin-input w-full" />
                <span class="admin-field__hint">Örn. 1000 TRY = 1 bilet</span>
              </label>
              <label class="admin-field">
                <span class="admin-label">Kazanan sayısı</span>
                <input name="winnerCount" type="number" min="1" max="500" value="${editing != null ? Number(editing.winnerCount) || 1 : 1}" class="admin-input w-full" />
              </label>
              <label class="admin-field">
                <span class="admin-label">Kişi başı max bilet</span>
                <input name="maxEntriesPerUser" type="number" min="1" max="100" value="${editing != null ? Number(editing.maxEntriesPerUser) || 20 : 20}" class="admin-input w-full" />
              </label>
              <label class="admin-field">
                <span class="admin-label">Bitiş tarihi</span>
                <input name="endsAt" type="datetime-local" class="admin-input w-full" value="${escapeHtml(tsToDatetimeLocal(editing?.endsAt))}" />
                <span class="admin-field__hint">Boş = süresiz</span>
              </label>
            </div>
          </section>

          <div class="admin-ticket-create-form__actions">
            ${editing ? `<button type="button" id="site-event-edit-cancel" class="admin-btn admin-btn-ghost px-5 py-2.5">İptal</button>` : ""}
            <button type="submit" class="admin-btn admin-btn-primary px-6 py-2.5">${editing ? "Değişiklikleri kaydet" : "Etkinlik oluştur"}</button>
          </div>
        </form>
      </div>
      <div class="flex flex-wrap items-center justify-between gap-3">
        <div>
          <h3 class="admin-section__title text-sm">Etkinlikler</h3>
          <p class="mt-0.5 text-xs text-slate-500">Satıra tıklayarak talepleri ve biletleri yönetin.</p>
        </div>
        <button type="button" id="btn-site-events-refresh" class="admin-btn admin-btn-ghost text-xs">Yenile</button>
      </div>
      <div class="admin-ticket-events-grid">${rows || '<p class="text-sm text-slate-500">Henüz etkinlik yok.</p>'}</div>
    </div>`;
}

function filteredSiteEventRequests(requests) {
  const list = Array.isArray(requests) ? requests : [];
  const q = String(state.siteEventRequestFilter?.q || "").trim().toLowerCase();
  const st = String(state.siteEventRequestFilter?.status || "all");
  return list.filter((r) => {
    if (st !== "all" && String(r.status) !== st) return false;
    if (!q) return true;
    const hay = [
      r.id,
      r.userId,
      r.username,
      r.siteUsername,
      r.ticketRef,
      r.investmentAmount,
    ]
      .join(" ")
      .toLowerCase();
    return hay.includes(q);
  });
}

function siteEventRequestsTableHtml(requests) {
  const list = filteredSiteEventRequests(requests);
  if (!list.length) {
    return `<p class="p-8 text-center text-sm text-slate-500">Talep bulunamadı.</p>`;
  }
  const rows = list
    .map((r) => {
      const st = String(r.status || "pending");
      const actions =
        st === "pending"
          ? `<div class="admin-site-event-actions">
              <button type="button" class="admin-ticket-btn-approve site-event-req-approve" data-request-id="${r.id}" data-eligible="${r.ticketsEligible}">+ Onayla</button>
              <button type="button" class="admin-ticket-btn-reject site-event-req-reject" data-request-id="${r.id}" title="Reddet">✕ Reddet</button>
            </div>`
          : "—";
      const granted =
        Number(r.ticketsGranted) > 0
          ? `${r.ticketsGranted} adet`
          : '<span class="text-slate-400">—</span>';
      return `<tr>
        <td class="col-check"><input type="checkbox" class="admin-checkbox ticket-req-cb" data-request-id="${r.id}" /></td>
        <td class="font-mono text-xs text-slate-500">${r.id}</td>
        <td><a href="#" class="admin-ticket-user-link ticket-goto-member" data-user-id="${r.userId}">${escapeHtml(r.username || "—")}</a></td>
        <td>${escapeHtml(r.siteUsername || "—")}</td>
        <td class="admin-ticket-amount">${formatTry(r.investmentAmount)}</td>
        <td class="text-xs text-slate-600">${escapeHtml(formatTs(r.investmentAt))}</td>
        <td>${Number(r.ticketsEligible) || 0} adet</td>
        <td>${granted}</td>
        <td class="text-xs text-slate-500">${escapeHtml(formatTs(r.createdAt))}</td>
        <td>${ticketRequestStatusBadge(st)}</td>
        <td>${actions}</td>
        <td class="max-w-[8rem] truncate text-xs text-slate-500" title="${escapeHtml(r.adminNote || "")}">${escapeHtml(r.adminNote || "—")}</td>
      </tr>`;
    })
    .join("");
  return `<div class="admin-ticket-table-wrap">
    <table class="admin-ticket-table">
      <thead><tr>
        <th class="col-check"><input type="checkbox" class="admin-checkbox" id="ticket-req-select-all" /></th>
        <th>ID</th>
        <th>Kullanıcı adı</th>
        <th>Site kullanıcı adı</th>
        <th>Yatırım tutarı</th>
        <th>Yatırım tarihi</th>
        <th>Alabileceği bilet</th>
        <th>Verilen bilet</th>
        <th>Tarih</th>
        <th>Durum</th>
        <th>İşlem</th>
        <th>Not</th>
      </tr></thead>
      <tbody>${rows}</tbody>
    </table>
  </div>`;
}

function ticketEntriesTableHtml(entries, opts = {}) {
  const list = Array.isArray(entries) ? entries : [];
  if (!list.length) return `<p class="p-6 text-center text-sm text-slate-500">Kayıt yok.</p>`;
  const rows = list
    .map(
      (e) => `<tr>
        <td class="font-mono text-xs">${e.id}</td>
        <td>${escapeHtml(e.username)} <span class="text-slate-400">#${e.userId}</span></td>
        <td class="font-mono text-xs font-semibold">${escapeHtml(e.ticketCode)}</td>
        <td class="text-xs text-slate-500">${escapeHtml(formatTs(e.createdAt))}</td>
        ${opts.showWinner ? `<td>${e.isWinner ? '<span class="text-emerald-600 font-semibold">Kazanan</span>' : "—"}</td>` : ""}
      </tr>`,
    )
    .join("");
  return `<div class="admin-ticket-table-wrap">
    <table class="admin-ticket-table" style="min-width:720px">
      <thead><tr>
        <th>ID</th><th>Üye</th><th>Bilet no</th><th>Tarih</th>${opts.showWinner ? "<th>Durum</th>" : ""}
      </tr></thead>
      <tbody>${rows}</tbody>
    </table>
  </div>`;
}

function ticketLeaderboardHtml(entries) {
  const map = new Map();
  for (const e of entries || []) {
    const key = e.userId;
    if (!map.has(key)) {
      map.set(key, { userId: e.userId, username: e.username, count: 0 });
    }
    map.get(key).count += 1;
  }
  const sorted = [...map.values()].sort((a, b) => b.count - a.count).slice(0, 50);
  if (!sorted.length) return `<p class="p-6 text-sm text-slate-500">Henüz bilet yok.</p>`;
  const rows = sorted
    .map(
      (r, i) => `<tr>
        <td>${i + 1}</td>
        <td>${escapeHtml(r.username)}</td>
        <td class="font-semibold tabular-nums">${r.count}</td>
      </tr>`,
    )
    .join("");
  return `<div class="admin-ticket-table-wrap">
    <table class="admin-ticket-table" style="min-width:400px">
      <thead><tr><th>#</th><th>Üye</th><th>Bilet sayısı</th></tr></thead>
      <tbody>${rows}</tbody>
    </table>
  </div>`;
}

function siteEventDetailHtml() {
  const d = state.siteEventDetail;
  if (!d || !d.event) {
    return `<p class="text-sm text-slate-500">Etkinlik yüklenemedi.</p>`;
  }
  const ev = d.event;
  const cover = ev.logoUrl
    ? `<img src="${escapeHtml(ev.logoUrl)}" alt="" />`
    : "";
  const cap = Number(ev.maxTotalTickets) || 0;
  const capLabel = cap > 0 ? String(cap) : "∞";
  const pending = Number(ev.pendingRequests) || 0;
  const sub = state.siteEventSubTab || "requests";
  const tabs = [
    { id: "info", label: "Bilgiler" },
    { id: "given", label: "Onaylanan katılımlar" },
    { id: "requests", label: "Katılım talepleri", badge: pending },
    { id: "table", label: "Katılım tablosu" },
    { id: "manual", label: "Manuel talep ekle" },
    { id: "leaderboard", label: "Katılım liderliği" },
  ];
  const tabBtns = tabs
    .map((t) => {
      const active = sub === t.id ? " admin-ticket-tab--active" : "";
      const badge = t.badge
        ? `<span class="admin-ticket-tab__badge">${t.badge}</span>`
        : "";
      return `<button type="button" class="admin-ticket-tab site-event-subtab${active}" data-subtab="${t.id}">${escapeHtml(t.label)}${badge}</button>`;
    })
    .join("");

  let panel = "";
  if (sub === "requests") {
    const f = state.siteEventRequestFilter || {};
    panel = `
      <div class="admin-ticket-toolbar">
        <label class="text-sm">
          <span class="mb-1 block text-[11px] font-semibold uppercase text-slate-500">Kullanıcı ara</span>
          <input type="search" id="site-event-req-search" class="admin-input" placeholder="Kullanıcı adı…" value="${escapeHtml(f.q || "")}" />
        </label>
        <label class="text-sm">
          <span class="mb-1 block text-[11px] font-semibold uppercase text-slate-500">Site kullanıcı adı</span>
          <input type="search" class="admin-input" placeholder="Site kullanıcı adı…" disabled />
        </label>
        <div class="admin-ticket-status-filters" role="group" aria-label="Durum">
          ${["all", "pending", "approved", "rejected"]
            .map((s) => {
              const labels = { all: "Tümü", pending: "Bekliyor", approved: "Onaylandı", rejected: "Reddedildi" };
              const act = (f.status || "all") === s ? " admin-ticket-filter-chip--active" : "";
              return `<button type="button" class="admin-ticket-filter-chip site-event-req-filter${act}" data-status="${s}">${labels[s]}</button>`;
            })
            .join("")}
        </div>
        <button type="button" id="site-event-req-search-btn" class="admin-btn admin-btn-primary text-sm">Ara</button>
        <button type="button" id="site-event-req-csv" class="admin-btn admin-btn-soft text-sm ml-auto">İşlemleri indir (.CSV)</button>
      </div>
      ${siteEventRequestsTableHtml(d.requests)}`;
  } else if (sub === "given") {
    panel = `<div class="admin-ticket-panel-body">${ticketEntriesTableHtml(d.entries)}</div>`;
  } else if (sub === "table") {
    panel = `<div class="admin-ticket-panel-body">${ticketEntriesTableHtml(d.entries, { showWinner: ev.status === "drawn" })}</div>`;
  } else if (sub === "leaderboard") {
    panel = `<div class="admin-ticket-panel-body">${ticketLeaderboardHtml(d.entries)}</div>`;
  } else if (sub === "manual") {
    panel = `<div class="admin-ticket-panel-body">
      <form id="site-event-manual-form" class="admin-inset max-w-lg space-y-3 p-4">
        <p class="admin-section__title text-sm">Manuel talep</p>
        <label class="block text-sm"><span class="mb-1 block text-slate-500">Üye ID *</span><input name="userId" type="number" min="1" required class="admin-input w-full" /></label>
        <label class="block text-sm"><span class="mb-1 block text-slate-500">Site kullanıcı adı *</span><input name="siteUsername" required class="admin-input w-full" /></label>
        <label class="block text-sm"><span class="mb-1 block text-slate-500">Yatırım (TRY) *</span><input name="investmentAmount" type="number" min="1" step="0.01" required class="admin-input w-full" /></label>
        <label class="block text-sm"><span class="mb-1 block text-slate-500">Bilet adedi (boş=otomatik)</span><input name="ticketsEligible" type="number" min="1" class="admin-input w-full" /></label>
        <label class="flex items-center gap-2 text-sm"><input type="checkbox" name="autoApprove" class="admin-checkbox" checked /><span>Ekler eklemez onayla ve bilet ver</span></label>
        <button type="submit" class="admin-btn admin-btn-primary">Talep ekle</button>
      </form>
    </div>`;
  } else {
    const ends = ev.endsAt ? formatTs(ev.endsAt) : "—";
    panel = `<div class="admin-ticket-panel-body space-y-4">
      <p class="text-sm text-slate-600">${escapeHtml(ev.description || "")}</p>
      <dl class="grid gap-2 text-sm sm:grid-cols-2">
        <div><dt class="text-slate-500">Sponsor</dt><dd class="font-medium">${escapeHtml(ev.sponsorTitle || "—")}</dd></div>
        <div><dt class="text-slate-500">Durum</dt><dd>${escapeHtml(ticketStatusLabel(ev.status))}</dd></div>
        <div><dt class="text-slate-500">Ödül</dt><dd>${escapeHtml(ev.prizeDescription || "—")}</dd></div>
        <div><dt class="text-slate-500">Katılım / yatırım</dt><dd>${formatTry(ev.investmentPerTicket || 1000)} / katılım</dd></div>
        <div><dt class="text-slate-500">Bitiş</dt><dd>${escapeHtml(ends)}</dd></div>
      </dl>
      ${ev.status !== "drawn"
        ? `<div class="flex flex-wrap gap-2 pt-2">
            ${ev.status === "open"
              ? `<button type="button" class="site-event-act admin-btn admin-btn-ghost text-xs" data-act="close" data-id="${ev.id}">Kapat</button>`
              : `<button type="button" class="site-event-act admin-btn admin-btn-ghost text-xs" data-act="open" data-id="${ev.id}">Yeniden aç</button>`}
            <button type="button" class="site-event-act admin-btn admin-btn-primary text-xs" data-act="draw" data-id="${ev.id}" data-winners="${ev.winnerCount}">Sonuçlandır</button>
          </div>`
        : ""}
    </div>`;
  }

  return `
    <div class="admin-tickets-wrap">
      <div class="admin-ticket-detail-head">
        <button type="button" id="site-event-back-list" class="admin-ticket-back admin-btn admin-btn-ghost text-sm">← Etkinlik listesi</button>
        <button type="button" class="site-event-act admin-action-btn admin-action-btn--reject" data-act="delete" data-id="${ev.id}">Etkinliği sil</button>
      </div>
      <div class="admin-panel overflow-hidden p-0">
        <div class="admin-ticket-hero">
          <div class="admin-ticket-hero__main">
            <div class="admin-ticket-hero__cover">${cover}</div>
            <div>
              <h2 class="admin-ticket-hero__title">${escapeHtml(ev.title)} ★</h2>
              <div class="admin-ticket-hero__stats">
                <div class="admin-ticket-hero__stat"><span>Toplam katılım</span><strong>${Number(ev.participantCount) || 0}</strong></div>
                <div class="admin-ticket-hero__stat"><span>Toplam katılım</span><strong>${Number(ev.entryCount) || 0}</strong></div>
                <div class="admin-ticket-hero__stat"><span>Bilet kapasitesi</span><strong>${capLabel}</strong></div>
              </div>
            </div>
          </div>
          <div class="admin-ticket-hero__invest">
            <p class="admin-ticket-hero__invest-label">Yatırım toplamı</p>
            <p class="admin-ticket-hero__invest-value">${formatTry(ev.totalInvestment || 0)}</p>
          </div>
        </div>
        <nav class="admin-ticket-tabs">${tabBtns}</nav>
        ${panel}
      </div>
    </div>`;
}

function siteEventsPanelHtml() {
  if (state.siteEventView === "detail" && state.siteEventId) {
    return siteEventDetailHtml();
  }
  return siteEventsListHtml();
}

function telegramBroadcastPanelHtml() {
  const data = state.telegramBroadcast;
  const sending = state.telegramBroadcastSending === true;
  const last = state.telegramBroadcastLastResult;
  const loaded = data !== null && data !== undefined;
  const configured = loaded ? !!data.telegramConfigured : true;
  const subs = loaded ? (Number(data.subscriberCount) || 0) : 0;

  const configWarning = !configured
    ? `<div class="admin-tg-bc-warn" role="alert">
        <strong>Bot ayarlanmamış.</strong>
        <span><code>api/config.local.php</code> içinde <code>SITE_TELEGRAM_BOT_TOKEN</code> ve <code>SITE_TELEGRAM_BOT_USERNAME</code> tanımlı olmalı.</span>
      </div>`
    : "";

  const noSubs = configured && subs === 0
    ? `<div class="admin-tg-bc-info" role="status">
        <strong>Henüz alıcı yok.</strong>
        <span>Üyelerin botu Telegram'da <code>/start</code> ile başlatması gerekir; başlatan herkes bu listeye eklenir.</span>
      </div>`
    : "";

  const lastResultBlock = last
    ? `<div class="admin-tg-bc-result ${last.failed > 0 ? "admin-tg-bc-result--mixed" : "admin-tg-bc-result--ok"}" role="status">
        <p class="admin-tg-bc-result__title">Son gönderim</p>
        <p class="admin-tg-bc-result__line">
          <strong>${Number(last.sent) || 0}</strong> başarılı ·
          <strong>${Number(last.failed) || 0}</strong> başarısız ·
          toplam ${Number(last.total) || 0} alıcı
        </p>
        ${last.telegram_error ? `<p class="admin-tg-bc-result__err">Telegram: ${escapeHtml(String(last.telegram_error))}</p>` : ""}
      </div>`
    : "";

  const submitDisabled = sending || !configured ? " disabled" : "";

  return `
    <div class="admin-tg-bc-wrap space-y-4">
      <div class="admin-tg-bc-stats admin-panel">
        <div class="admin-tg-bc-stats__cell">
          <p class="admin-tg-bc-stats__label">Botu başlatan üye sayısı</p>
          <p class="admin-tg-bc-stats__value tabular-nums">${loaded ? subs : "…"}</p>
          <p class="admin-tg-bc-stats__hint">Bu sayı, bota özel sohbette en az bir kez yazan / <code>/start</code> diyen kullanıcıları yansıtır.</p>
        </div>
        <div class="admin-tg-bc-stats__cell admin-tg-bc-stats__cell--right">
          <p class="admin-tg-bc-stats__label">Bot durumu</p>
          <p class="admin-tg-bc-stats__value ${loaded ? (configured ? "text-emerald-600" : "text-rose-500") : ""}">
            ${loaded ? (configured ? "✓ Yapılandırılmış" : "✕ Eksik") : "Kontrol ediliyor…"}
          </p>
        </div>
      </div>

      ${configWarning}
      ${noSubs}

      <form id="tg-broadcast-form" class="admin-panel admin-tg-bc-form" enctype="multipart/form-data">
        <div class="admin-tg-bc-form__head">
          <h3 class="admin-section__title text-base">Toplu duyuru gönder</h3>
          <p class="admin-tg-bc-form__hint">Görsel + mesajı tek seferde gönder; mesajı boş bırakırsan sadece görsel gider, görsel olmazsa sadece metin.</p>
        </div>

        <div class="admin-tg-bc-form__body">
          <label class="admin-field admin-field--full">
            <span class="admin-label">Mesaj <span class="admin-tg-bc-counter" id="tg-bc-counter">0/4096</span></span>
            <textarea name="text" id="tg-bc-text" rows="6" class="admin-input w-full resize-y" maxlength="4096" placeholder="Üyelere göndereceğin duyuru…"></textarea>
            <span class="admin-field__hint">Görsel eklendiğinde caption olarak gider (en fazla 1024 karakter). Görselsiz mesajlar 4096 karaktere kadar.</span>
          </label>

          <fieldset class="admin-tg-bc-image">
            <legend class="admin-label">Görsel (opsiyonel)</legend>
            <div class="admin-tg-bc-image__grid">
              <label class="admin-field">
                <span class="admin-label admin-label--xs">Dosya yükle</span>
                <input type="file" name="photo" id="tg-bc-photo" accept="image/jpeg,image/png,image/gif,image/webp" class="admin-input" />
                <span class="admin-field__hint">JPEG / PNG / GIF / WebP, max 5 MB. Yüklersen URL alanı yok sayılır.</span>
              </label>
              <label class="admin-field">
                <span class="admin-label admin-label--xs">VEYA URL</span>
                <input type="url" name="photo_url" id="tg-bc-photo-url" class="admin-input w-full" placeholder="https://cdn.example.com/banner.jpg" />
                <span class="admin-field__hint">https:// ile başlayan doğrudan görsel linki.</span>
              </label>
            </div>
            <div class="admin-tg-bc-image__preview" id="tg-bc-preview-wrap" hidden>
              <img id="tg-bc-preview" alt="" />
              <button type="button" id="tg-bc-preview-clear" class="admin-btn admin-btn-ghost text-xs">Görseli temizle</button>
            </div>
          </fieldset>

          <div class="admin-tg-bc-options">
            <label class="admin-tg-bc-toggle">
              <input type="checkbox" name="parse_mode_html" id="tg-bc-parse-html" />
              <span>HTML formatı kullan (<code>&lt;b&gt;</code>, <code>&lt;a&gt;</code> vs.)</span>
            </label>
            <label class="admin-tg-bc-toggle">
              <input type="checkbox" name="disable_link_preview" id="tg-bc-no-preview" />
              <span>Mesajdaki link önizlemesini kapat</span>
            </label>
          </div>

          <fieldset class="admin-tg-bc-image">
            <legend class="admin-label">Buton (opsiyonel)</legend>
            <div class="admin-tg-bc-image__grid">
              <label class="admin-field">
                <span class="admin-label admin-label--xs">Buton metni</span>
                <input type="text" id="tg-bc-button-text" name="button_text" class="admin-input w-full" maxlength="64" placeholder="Siteyi Aç" />
                <span class="admin-field__hint">Boş bırakırsan buton eklenmez.</span>
              </label>
              <label class="admin-field">
                <span class="admin-label admin-label--xs">Buton URL</span>
                <input type="url" id="tg-bc-button-url" name="button_url" class="admin-input w-full" placeholder="https://mocker27.com/" />
                <span class="admin-field__hint">Buton metni girildiyse URL de zorunlu.</span>
              </label>
            </div>
          </fieldset>
        </div>

        <div class="admin-tg-bc-form__actions">
          <button type="submit" id="tg-bc-submit" class="admin-btn admin-btn-primary px-6 py-2.5"${submitDisabled}>
            ${sending ? "Gönderiliyor…" : `Gönder (${subs} alıcı)`}
          </button>
          <button type="button" id="tg-bc-clear" class="admin-btn admin-btn-ghost text-sm">Formu temizle</button>
        </div>

        ${lastResultBlock}
      </form>
    </div>`;
}

function levelSystemPanelHtml() {
  const d = state.levelSystem?.settings || {};
  const levels = Array.isArray(d.levels) ? d.levels : [];
  const top = Array.isArray(state.levelSystem?.topUsers) ? state.levelSystem.topUsers : [];
  const rows = levels.length
    ? levels
        .map(
          (lv, i) => `<tr class="admin-table-row">
            <td class="px-3 py-2">${i + 1}</td>
            <td class="px-3 py-2"><input data-level-row="${i}" data-key="xp_required" type="number" min="0" class="admin-input w-full tabular-nums" value="${Number(lv.xp_required) || 0}" /></td>
            <td class="px-3 py-2"><input data-level-row="${i}" data-key="coin_reward" type="number" min="0" class="admin-input w-full tabular-nums" value="${Number(lv.coin_reward) || 0}" /></td>
          </tr>`
        )
        .join("")
    : "";
  const topRows = top.length
    ? top
        .map(
          (u, i) => `<tr class="admin-table-row">
            <td class="px-3 py-2">${i + 1}</td>
            <td class="px-3 py-2">${escapeHtml(String(u.username || "—"))}</td>
            <td class="px-3 py-2 tabular-nums">Lv.${Number(u.level) || 0}</td>
            <td class="px-3 py-2 tabular-nums">${Number(u.xp) || 0}</td>
          </tr>`
        )
        .join("")
    : `<tr><td colspan="4" class="px-3 py-3 text-sm text-slate-500">Henüz seviye verisi yok.</td></tr>`;
  return `<div class="admin-panel admin-panel--padded space-y-5">
    <div class="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
      <label class="admin-field">
        <span class="admin-label">Sistem aktif</span>
        <input id="levels-enabled" type="checkbox" ${d.enabled ? "checked" : ""} />
      </label>
      <label class="admin-field">
        <span class="admin-label">Oyun turu XP</span>
        <input id="levels-xp-game-round" type="number" min="0" class="admin-input tabular-nums" value="${Number(d.xp_game_round) || 0}" />
      </label>
      <label class="admin-field">
        <span class="admin-label">Bilet katılım XP</span>
        <input id="levels-xp-ticket-join" type="number" min="0" class="admin-input tabular-nums" value="${Number(d.xp_ticket_join) || 0}" />
      </label>
      <label class="admin-field">
        <span class="admin-label">Etkinlik talep XP</span>
        <input id="levels-xp-ticket-request" type="number" min="0" class="admin-input tabular-nums" value="${Number(d.xp_ticket_request) || 0}" />
      </label>
      <label class="admin-field">
        <span class="admin-label">Site günlük aktif XP</span>
        <input id="levels-xp-site-active-daily" type="number" min="0" class="admin-input tabular-nums" value="${Number(d.xp_site_active_daily) || 0}" />
      </label>
      <label class="admin-field">
        <span class="admin-label">Telegram mesaj XP</span>
        <input id="levels-xp-telegram-message" type="number" min="0" class="admin-input tabular-nums" value="${Number(d.xp_telegram_message) || 0}" />
      </label>
      <label class="admin-field">
        <span class="admin-label">Telegram günlük XP tavanı</span>
        <input id="levels-telegram-daily-cap" type="number" min="1" class="admin-input tabular-nums" value="${Number(d.telegram_daily_cap) || 1}" />
      </label>
    </div>
    <div>
      <div class="flex items-center justify-between gap-3 mb-2">
        <h3 class="admin-section__title text-base">Seviye atlama</h3>
        <button type="button" id="btn-levels-add-row" class="admin-btn admin-btn-ghost px-3 py-1.5 text-sm">+ Seviye ekle</button>
      </div>
      <p class="mb-2 text-xs text-slate-500">Her satır: o seviyeye çıkmak için gereken XP (toplam değil). Örn. Seviye 1 = 1000 XP, Seviye 2 = 1500 XP daha.</p>
      <div class="admin-table-wrap">
        <table class="admin-data-table" style="min-width:520px">
          <thead><tr><th>Seviye</th><th>Bu seviye için XP</th><th>Atlama ödülü (coin)</th></tr></thead>
          <tbody id="levels-rows">${rows}</tbody>
        </table>
      </div>
    </div>
    <div class="flex items-center gap-2">
      <button type="button" id="btn-levels-save" class="admin-btn admin-btn-primary px-5 py-2">Seviye ayarlarını kaydet</button>
      <button type="button" id="btn-levels-refresh" class="admin-btn admin-btn-soft px-4 py-2">Yenile</button>
    </div>
    <div>
      <h3 class="admin-section__title text-base mb-2">En yüksek seviye oyuncular</h3>
      <div class="admin-table-wrap">
        <table class="admin-data-table" style="min-width:520px">
          <thead><tr><th>#</th><th>Kullanıcı</th><th>Seviye</th><th>XP</th></tr></thead>
          <tbody>${topRows}</tbody>
        </table>
      </div>
    </div>
  </div>`;
}

async function loadTicketEvents() {
  const j = await api("admin_tickets.php", { method: "GET" });
  state.ticketEvents = j && j.ok && Array.isArray(j.events) ? j.events : [];
  if (j && j.ok && Array.isArray(j.sponsors)) state.sponsorOptions = j.sponsors;
}

async function loadTicketEventDetail(eventId) {
  const id = Number(eventId);
  if (!id) {
    state.ticketDetail = null;
    return;
  }
  const j = await api(`admin_tickets.php?eventId=${id}`, { method: "GET" });
  if (!j || !j.ok) throw new Error(j?.error || "load");
  state.ticketDetail = {
    event: j.event,
    requests: Array.isArray(j.requests) ? j.requests : [],
    entries: Array.isArray(j.entries) ? j.entries : [],
  };
  state.ticketEventId = id;
}

function formatTry(amount) {
  const n = Number(amount);
  if (!Number.isFinite(n)) return "—";
  return `${new Intl.NumberFormat("tr-TR", { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(n)} TRY`;
}

function ticketRequestStatusLabel(st) {
  if (st === "pending") return "Bekliyor";
  if (st === "approved") return "Onaylandı";
  if (st === "rejected") return "Reddedildi";
  return st;
}

function ticketRequestStatusBadge(st) {
  const cls =
    st === "approved"
      ? "admin-ticket-badge--approved"
      : st === "rejected"
        ? "admin-ticket-badge--rejected"
        : "admin-ticket-badge--pending";
  return `<span class="admin-ticket-badge ${cls}">${escapeHtml(ticketRequestStatusLabel(st))}</span>`;
}

function ticketStatusLabel(st) {
  if (st === "open") return "Açık";
  if (st === "closed") return "Kapalı";
  if (st === "drawn") return "Sonuçlandı";
  return st;
}

function getEventSponsorOptions(extra = null) {
  const base = state.sponsorOptions?.length
    ? state.sponsorOptions
    : (state.sponsors || []).map((s) => ({
        id: s.id,
        title: s.title,
        logo: s.logo,
        url: s.url,
      }));
  const list = Array.isArray(base) ? [...base] : [];
  if (extra) {
    const id = String(extra.id || "").trim();
    if (id && !list.some((s) => String(s.id) === id)) {
      list.unshift({
        id,
        title: String(extra.title || id).trim() || id,
        logo: String(extra.logo || ""),
        url: String(extra.url || ""),
      });
    }
  }
  return list;
}

function ticketEventSponsorSelectHtml(selectedId = "", eventForExtra = null) {
  const extra =
    eventForExtra && (eventForExtra.sponsorId || eventForExtra.sponsorTitle)
      ? {
          id: eventForExtra.sponsorId || "",
          title: eventForExtra.sponsorTitle || eventForExtra.sponsorId || "",
          url: eventForExtra.siteUrl || "",
        }
      : null;
  const sponsors = getEventSponsorOptions(extra);
  const opts = sponsors
    .map((s) => {
      const sel = String(s.id) === String(selectedId) ? " selected" : "";
      return `<option value="${escapeHtml(String(s.id))}"${sel}>${escapeHtml(String(s.title || s.id))}</option>`;
    })
    .join("");
  return `<label class="admin-field">
    <span class="admin-label">Sponsor *</span>
    <select name="sponsorId" class="admin-input w-full ticket-event-sponsor-select" required>
      <option value="">— Sponsor seç —</option>
      ${opts}
    </select>
    <span class="admin-field__hint">Sponsor kartları sekmesindeki siteler listelenir.</span>
  </label>`;
}

function bindEventCreateSponsorAutofill() {
  const list = getEventSponsorOptions();
  document
    .querySelectorAll("form#site-event-create-form select[name=\"sponsorId\"], form#ticket-event-create-form select[name=\"sponsorId\"]")
    .forEach((sel) => {
      if (sel.dataset.boundSponsor === "1") return;
      sel.dataset.boundSponsor = "1";
      sel.addEventListener("change", () => {
        const sp = list.find((s) => String(s.id) === String(sel.value));
        const siteInput = sel.closest("form")?.querySelector('[name="siteUrl"]');
        if (sp?.url && siteInput) siteInput.value = String(sp.url);
      });
    });
}

const TICKET_COVER_PLACEHOLDER =
  "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";

function siteNewsCoverFieldHtml(imageUrl) {
  const coverUrl = String(imageUrl || "");
  const hasCover = coverUrl.length > 0;
  const previewSrc = coverUrl || TICKET_COVER_PLACEHOLDER;
  const emptyOverlay = hasCover
    ? ""
    : `<span class="admin-ticket-cover__empty" aria-hidden="true">Önizleme</span>`;
  return `
    <div class="admin-ticket-cover admin-ticket-cover--news" data-cover-id="news">
      <div class="admin-ticket-cover__preview-col">
        <div class="admin-ticket-cover__preview${hasCover ? "" : " admin-ticket-cover__preview--empty"}">
          <img id="site-news-cover-preview" src="${escapeHtml(previewSrc)}" alt="" class="admin-ticket-cover__img" loading="lazy" decoding="async" />
          ${emptyOverlay}
          <span class="admin-ticket-cover__ratio">16∶10</span>
        </div>
      </div>
      <div class="admin-ticket-cover__fields">
        <p class="admin-ticket-cover__title">Kapak görseli</p>
        <p class="admin-ticket-cover__hint">Sitedeki haber kartının üst alanı. <span class="admin-code">https://</span> ile başlayan doğrudan görsel linki.</p>
        <label class="admin-ticket-cover__url-label">
          <span class="sr-only">Kapak görseli URL</span>
          <input type="url" id="site-news-image" class="admin-input ticket-cover-url-input font-mono text-xs" data-preview="site-news-cover-preview" value="${escapeHtml(coverUrl)}" placeholder="https://cdn.example.com/haber-kapak.jpg" autocomplete="off" />
        </label>
      </div>
    </div>`;
}

function ticketCoverFieldHtml(id, url, editable) {
  const coverUrl = String(url || "");
  const hasCover = coverUrl.length > 0;
  const previewSrc = coverUrl || TICKET_COVER_PLACEHOLDER;
  const inputId = `ticket-cover-url-${id}`;
  const previewId = `ticket-cover-preview-${id}`;
  const saveBtn =
    editable && id !== "create"
      ? `<button type="button" class="ticket-act admin-btn admin-btn-ghost mt-2 w-full text-xs" data-act="save-cover" data-id="${id}">Görseli kaydet</button>`
      : "";
  const emptyOverlay = hasCover
    ? ""
    : `<span class="admin-ticket-cover__empty" aria-hidden="true">Önizleme</span>`;
  return `
    <div class="admin-ticket-cover" data-cover-id="${escapeHtml(id)}">
      <div class="admin-ticket-cover__preview-col">
        <div class="admin-ticket-cover__preview${hasCover ? "" : " admin-ticket-cover__preview--empty"}">
          <img id="${previewId}" src="${escapeHtml(previewSrc)}" alt="" class="admin-ticket-cover__img" loading="lazy" decoding="async" />
          ${emptyOverlay}
          <span class="admin-ticket-cover__ratio">16∶10</span>
        </div>
      </div>
      <div class="admin-ticket-cover__fields">
        <p class="admin-ticket-cover__title">Kart görseli</p>
        <p class="admin-ticket-cover__hint">Sitedeki bilet kartının üst kapak alanı. <span class="admin-code">https://</span> ile başlayan doğrudan görsel linki.</p>
        <label class="admin-ticket-cover__url-label">
          <span class="sr-only">Görsel URL</span>
          <input type="url" id="${inputId}" name="${id === "create" ? "logoUrl" : ""}" class="admin-input ticket-cover-url-input font-mono text-xs" data-preview="${previewId}" value="${escapeHtml(coverUrl)}" placeholder="https://cdn.example.com/bilet-kapak.jpg" autocomplete="off" ${editable ? "" : "readonly"} />
        </label>
        ${saveBtn}
      </div>
    </div>`;
}

function bindTelegramBroadcastForm() {
  const form = document.getElementById("tg-broadcast-form");
  if (!form || form.dataset.tgBcBound === "1") return;
  form.dataset.tgBcBound = "1";

  const textEl = document.getElementById("tg-bc-text");
  const counterEl = document.getElementById("tg-bc-counter");
  const photoEl = document.getElementById("tg-bc-photo");
  const photoUrlEl = document.getElementById("tg-bc-photo-url");
  const previewWrap = document.getElementById("tg-bc-preview-wrap");
  const previewImg = document.getElementById("tg-bc-preview");
  const previewClear = document.getElementById("tg-bc-preview-clear");
  const submitBtn = document.getElementById("tg-bc-submit");
  const clearBtn = document.getElementById("tg-bc-clear");
  const buttonTextEl = document.getElementById("tg-bc-button-text");
  const buttonUrlEl = document.getElementById("tg-bc-button-url");

  const updateCounter = () => {
    if (!counterEl || !textEl) return;
    const len = String(textEl.value || "").length;
    const hasPhoto = !!(photoEl?.files?.length) || !!String(photoUrlEl?.value || "").trim();
    const max = hasPhoto ? 1024 : 4096;
    counterEl.textContent = `${len}/${max}`;
    counterEl.classList.toggle("admin-tg-bc-counter--over", len > max);
    if (textEl.maxLength !== max) textEl.maxLength = max;
  };

  const showFilePreview = (file) => {
    if (!previewWrap || !previewImg) return;
    if (!file) {
      previewWrap.hidden = true;
      previewImg.removeAttribute("src");
      return;
    }
    const reader = new FileReader();
    reader.onload = (ev) => {
      previewImg.src = String(ev.target?.result || "");
      previewWrap.hidden = false;
    };
    reader.readAsDataURL(file);
  };

  const showUrlPreview = (url) => {
    if (!previewWrap || !previewImg) return;
    const u = String(url || "").trim();
    if (!u || !/^https?:\/\//i.test(u)) {
      if (!photoEl?.files?.length) {
        previewWrap.hidden = true;
        previewImg.removeAttribute("src");
      }
      return;
    }
    previewImg.src = u;
    previewWrap.hidden = false;
  };

  textEl?.addEventListener("input", updateCounter);

  photoEl?.addEventListener("change", () => {
    const f = photoEl.files?.[0];
    if (f && photoUrlEl) photoUrlEl.value = "";
    showFilePreview(f || null);
    updateCounter();
  });

  photoUrlEl?.addEventListener("input", () => {
    if (photoEl?.files?.length) photoEl.value = "";
    showUrlPreview(photoUrlEl.value);
    updateCounter();
  });

  previewClear?.addEventListener("click", () => {
    if (photoEl) photoEl.value = "";
    if (photoUrlEl) photoUrlEl.value = "";
    if (previewWrap) previewWrap.hidden = true;
    if (previewImg) previewImg.removeAttribute("src");
    updateCounter();
  });

  clearBtn?.addEventListener("click", () => {
    form.reset();
    if (previewWrap) previewWrap.hidden = true;
    if (previewImg) previewImg.removeAttribute("src");
    updateCounter();
  });

  form.addEventListener("submit", async (e) => {
    e.preventDefault();
    if (state.telegramBroadcastSending) return;

    const text = String(textEl?.value || "").trim();
    const hasFile = !!(photoEl?.files?.length);
    const photoUrl = String(photoUrlEl?.value || "").trim();
    const hasPhoto = hasFile || photoUrl !== "";
    const buttonText = String(buttonTextEl?.value || "").trim();
    const buttonUrl = String(buttonUrlEl?.value || "").trim();

    if (!text && !hasPhoto) {
      toast("Mesaj veya görsel eklemelisin.", "err");
      return;
    }
    if ((buttonText && !buttonUrl) || (!buttonText && buttonUrl)) {
      toast("Buton için metin ve URL birlikte girilmeli.", "err");
      return;
    }

    const subs = Number(state.telegramBroadcast?.subscriberCount) || 0;
    if (subs === 0) {
      toast("Henüz botu başlatan üye yok.", "err");
      return;
    }

    const confirmMsg = `Bu mesaj ${subs} kişiye gönderilecek. Devam etmek istiyor musun?`;
    if (!window.confirm(confirmMsg)) return;

    state.telegramBroadcastSending = true;
    if (submitBtn) {
      submitBtn.disabled = true;
      submitBtn.textContent = "Gönderiliyor…";
    }
    toast("Gönderme işlemi başlatıldı. Lütfen sayfayı açık halde bırakın.");

    const fd = new FormData();
    if (text) fd.append("text", text);
    if (hasFile && photoEl?.files?.[0]) {
      fd.append("photo", photoEl.files[0]);
    } else if (photoUrl) {
      fd.append("photo_url", photoUrl);
    }
    if (document.getElementById("tg-bc-parse-html")?.checked) {
      fd.append("parse_mode", "HTML");
    }
    if (document.getElementById("tg-bc-no-preview")?.checked) {
      fd.append("disable_link_preview", "1");
    }
    if (buttonText && buttonUrl) {
      fd.append("button_text", buttonText);
      fd.append("button_url", buttonUrl);
    }

    try {
      const j = await api("admin_telegram_broadcast.php", { method: "POST", body: fd });
      if (!j || j.ok !== true) {
        const err = j?.detail || j?.error || "send_failed";
        throw new Error(String(err));
      }
      state.telegramBroadcastLastResult = {
        sent: Number(j.sent) || 0,
        failed: Number(j.failed) || 0,
        total: Number(j.total) || 0,
        telegram_error: j.telegram_error || null,
      };
      const okCount = state.telegramBroadcastLastResult.sent;
      const failCount = state.telegramBroadcastLastResult.failed;
      if (failCount === 0) {
        toast(`Duyuru ${okCount} kişiye gönderildi.`, "ok");
      } else {
        toast(`Gönderildi: ${okCount} başarılı, ${failCount} başarısız.`, okCount > 0 ? "ok" : "err");
      }
      form.reset();
      if (previewWrap) previewWrap.hidden = true;
      if (previewImg) previewImg.removeAttribute("src");
    } catch (err) {
      toast(err?.message || "Gönderim başarısız", "err");
    } finally {
      state.telegramBroadcastSending = false;
      renderDashboard();
    }
  });

  updateCounter();
}

function bindTicketCoverPreviews() {
  $$(".ticket-cover-url-input").forEach((input) => {
    const previewId = input.getAttribute("data-preview");
    const img = previewId ? $(`#${previewId}`) : null;
    const wrap = input.closest(".admin-ticket-cover");
    const preview = wrap?.querySelector(".admin-ticket-cover__preview");
    if (!img) return;
    const sync = () => {
      const v = String(input.value || "").trim();
      img.src = v || TICKET_COVER_PLACEHOLDER;
      if (preview) {
        preview.classList.toggle("admin-ticket-cover__preview--empty", !v);
        const empty = preview.querySelector(".admin-ticket-cover__empty");
        if (empty) empty.hidden = Boolean(v);
        else if (!v) {
          const span = document.createElement("span");
          span.className = "admin-ticket-cover__empty";
          span.setAttribute("aria-hidden", "true");
          span.textContent = "Önizleme";
          preview.appendChild(span);
        }
      }
    };
    input.addEventListener("input", sync);
    sync();
  });
}

function ticketEventsListHtml() {
  const events = Array.isArray(state.ticketEvents) ? state.ticketEvents : [];
  const rows = events.map((ev) => ticketEventListRowHtml(ev, "ticket-open-event", "ticket-delete-event")).join("");

  return `
    <div class="admin-tickets-wrap space-y-6">
      ${siteJoinGuideAdminPanelHtml("tickets")}
      <div class="admin-panel admin-ticket-create-panel overflow-hidden p-0">
        <div class="admin-ticket-create-panel__head">
          <div>
            <h2 class="admin-section__title text-lg">Yeni bilet etkinliği</h2>
            <p class="mt-1 text-sm text-slate-500">Temel bilgileri doldurun; kapak görseli sitede kartın üstünde görünür.</p>
          </div>
        </div>
        <form id="ticket-event-create-form" class="admin-ticket-create-form">
          <section class="admin-ticket-create-section">
            <h3 class="admin-ticket-create-section__title">Temel bilgiler</h3>
            <div class="admin-ticket-create-section__grid">
              <label class="admin-field admin-field--full">
                <span class="admin-label">Başlık *</span>
                <input name="title" required class="admin-input w-full" placeholder="750 Bin TL Ödül Havuzu" />
              </label>
              <label class="admin-field admin-field--full">
                <span class="admin-label">Açıklama (kurallar)</span>
                <textarea name="description" rows="3" class="admin-input w-full resize-y" placeholder="Üyeler «Bilet talep et» dediğinde gösterilecek kurallar…"></textarea>
              </label>
              ${ticketEventSponsorSelectHtml()}
              <label class="admin-field">
                <span class="admin-label">Ödül açıklaması</span>
                <input name="prizeDescription" class="admin-input w-full" placeholder="Örn. 5000₺ nakit" />
              </label>
            </div>
          </section>

          <section class="admin-ticket-create-section admin-ticket-create-section--cover">
            <h3 class="admin-ticket-create-section__title">Kapak &amp; bağlantı</h3>
            ${ticketCoverFieldHtml("create", "", true)}
            <label class="admin-field admin-field--full admin-ticket-create-site-url">
              <span class="admin-label">Partner site URL</span>
              <input name="siteUrl" class="admin-input w-full" type="url" placeholder="https://partner.com/giris" />
            </label>
          </section>

          <section class="admin-ticket-create-section">
            <h3 class="admin-ticket-create-section__title">Çekiliş &amp; talep kuralları</h3>
            <div class="admin-ticket-create-section__grid admin-ticket-create-section__grid--4">
              <label class="admin-field">
                <span class="admin-label">Bilet kapasitesi</span>
                <input name="maxTotalTickets" type="number" min="0" value="2000" class="admin-input w-full" title="0 = sınırsız" />
                <span class="admin-field__hint">0 = sınırsız</span>
              </label>
              <label class="admin-field">
                <span class="admin-label">Bilet / yatırım (TRY)</span>
                <input name="investmentPerTicket" type="number" min="1" step="1" value="1000" class="admin-input w-full" />
                <span class="admin-field__hint">Örn. 1000 TRY = 1 bilet</span>
              </label>
              <label class="admin-field">
                <span class="admin-label">Kazanan sayısı</span>
                <input name="winnerCount" type="number" min="1" max="500" value="1" class="admin-input w-full" />
              </label>
              <label class="admin-field">
                <span class="admin-label">Kişi başı max bilet</span>
                <input name="maxEntriesPerUser" type="number" min="1" max="100" value="20" class="admin-input w-full" />
              </label>
              <label class="admin-field">
                <span class="admin-label">Bitiş tarihi</span>
                <input name="endsAt" type="datetime-local" class="admin-input w-full" />
                <span class="admin-field__hint">Boş = süresiz</span>
              </label>
            </div>
          </section>

          <div class="admin-ticket-create-form__actions">
            <button type="submit" class="admin-btn admin-btn-primary px-6 py-2.5">Bilet etkinliği oluştur</button>
          </div>
        </form>
      </div>
      <div class="flex flex-wrap items-center justify-between gap-3">
        <div>
          <h3 class="admin-section__title text-sm">Bilet etkinlikleri</h3>
          <p class="mt-0.5 text-xs text-slate-500">Satıra tıklayarak talepleri ve biletleri yönetin.</p>
        </div>
        <button type="button" id="btn-tickets-refresh" class="admin-btn admin-btn-ghost text-xs">Yenile</button>
      </div>
      <div class="admin-ticket-events-grid">${rows || '<p class="text-sm text-slate-500">Henüz bilet etkinliği yok.</p>'}</div>
    </div>`;
}

function filteredTicketRequests(requests) {
  const list = Array.isArray(requests) ? requests : [];
  const q = String(state.ticketRequestFilter?.q || "").trim().toLowerCase();
  const st = String(state.ticketRequestFilter?.status || "all");
  return list.filter((r) => {
    if (st !== "all" && String(r.status) !== st) return false;
    if (!q) return true;
    const hay = [
      r.id,
      r.userId,
      r.username,
      r.siteUsername,
      r.ticketRef,
      r.investmentAmount,
    ]
      .join(" ")
      .toLowerCase();
    return hay.includes(q);
  });
}

function ticketRequestsTableHtml(requests) {
  const list = filteredTicketRequests(requests);
  if (!list.length) {
    return `<p class="p-8 text-center text-sm text-slate-500">Talep bulunamadı.</p>`;
  }
  const rows = list
    .map((r) => {
      const st = String(r.status || "pending");
      const actions =
        st === "pending"
          ? `<div class="admin-ticket-actions">
              <button type="button" class="admin-ticket-btn-approve ticket-req-approve" data-request-id="${r.id}" data-eligible="${r.ticketsEligible}">+ Onayla</button>
              <button type="button" class="admin-ticket-btn-reject ticket-req-reject" data-request-id="${r.id}" title="Reddet">✕ Reddet</button>
            </div>`
          : "—";
      const granted =
        Number(r.ticketsGranted) > 0
          ? `${r.ticketsGranted} adet`
          : '<span class="text-slate-400">—</span>';
      return `<tr>
        <td class="col-check"><input type="checkbox" class="admin-checkbox ticket-req-cb" data-request-id="${r.id}" /></td>
        <td class="font-mono text-xs text-slate-500">${r.id}</td>
        <td><a href="#" class="admin-ticket-user-link ticket-goto-member" data-user-id="${r.userId}">${escapeHtml(r.username || "—")}</a></td>
        <td>${escapeHtml(r.siteUsername || "—")}</td>
        <td class="admin-ticket-amount">${formatTry(r.investmentAmount)}</td>
        <td class="text-xs text-slate-600">${escapeHtml(formatTs(r.investmentAt))}</td>
        <td>${Number(r.ticketsEligible) || 0} adet</td>
        <td>${granted}</td>
        <td class="text-xs text-slate-500">${escapeHtml(formatTs(r.createdAt))}</td>
        <td>${ticketRequestStatusBadge(st)}</td>
        <td>${actions}</td>
        <td class="max-w-[8rem] truncate text-xs text-slate-500" title="${escapeHtml(r.adminNote || "")}">${escapeHtml(r.adminNote || "—")}</td>
      </tr>`;
    })
    .join("");
  return `<div class="admin-ticket-table-wrap">
    <table class="admin-ticket-table">
      <thead><tr>
        <th class="col-check"><input type="checkbox" class="admin-checkbox" id="ticket-req-select-all" /></th>
        <th>ID</th>
        <th>Kullanıcı adı</th>
        <th>Site kullanıcı adı</th>
        <th>Yatırım tutarı</th>
        <th>Yatırım tarihi</th>
        <th>Alabileceği bilet</th>
        <th>Verilen bilet</th>
        <th>Tarih</th>
        <th>Durum</th>
        <th>İşlem</th>
        <th>Not</th>
      </tr></thead>
      <tbody>${rows}</tbody>
    </table>
  </div>`;
}

function ticketEventDetailHtml() {
  const d = state.ticketDetail;
  if (!d || !d.event) {
    return `<p class="text-sm text-slate-500">Etkinlik yüklenemedi.</p>`;
  }
  const ev = d.event;
  const cover = ev.logoUrl
    ? `<img src="${escapeHtml(ev.logoUrl)}" alt="" />`
    : "";
  const cap = Number(ev.maxTotalTickets) || 0;
  const capLabel = cap > 0 ? String(cap) : "∞";
  const pending = Number(ev.pendingRequests) || 0;
  const sub = state.ticketSubTab || "requests";
  const tabs = [
    { id: "info", label: "Bilgiler" },
    { id: "given", label: "Verilen biletler" },
    { id: "requests", label: "Bilet talepleri", badge: pending },
    { id: "table", label: "Bilet tablosu" },
    { id: "manual", label: "Manuel talep ekle" },
    { id: "leaderboard", label: "Bilet liderliği" },
  ];
  const tabBtns = tabs
    .map((t) => {
      const active = sub === t.id ? " admin-ticket-tab--active" : "";
      const badge = t.badge
        ? `<span class="admin-ticket-tab__badge">${t.badge}</span>`
        : "";
      return `<button type="button" class="admin-ticket-tab ticket-subtab${active}" data-subtab="${t.id}">${escapeHtml(t.label)}${badge}</button>`;
    })
    .join("");

  let panel = "";
  if (sub === "requests") {
    const f = state.ticketRequestFilter || {};
    panel = `
      <div class="admin-ticket-toolbar">
        <label class="text-sm">
          <span class="mb-1 block text-[11px] font-semibold uppercase text-slate-500">Kullanıcı ara</span>
          <input type="search" id="ticket-req-search" class="admin-input" placeholder="Kullanıcı adı…" value="${escapeHtml(f.q || "")}" />
        </label>
        <label class="text-sm">
          <span class="mb-1 block text-[11px] font-semibold uppercase text-slate-500">Site kullanıcı adı</span>
          <input type="search" class="admin-input" placeholder="Site kullanıcı adı…" disabled />
        </label>
        <div class="admin-ticket-status-filters" role="group" aria-label="Durum">
          ${["all", "pending", "approved", "rejected"]
            .map((s) => {
              const labels = { all: "Tümü", pending: "Bekliyor", approved: "Onaylandı", rejected: "Reddedildi" };
              const act = (f.status || "all") === s ? " admin-ticket-filter-chip--active" : "";
              return `<button type="button" class="admin-ticket-filter-chip ticket-req-filter${act}" data-status="${s}">${labels[s]}</button>`;
            })
            .join("")}
        </div>
        <button type="button" id="ticket-req-search-btn" class="admin-btn admin-btn-primary text-sm">Ara</button>
        <button type="button" id="ticket-req-csv" class="admin-btn admin-btn-soft text-sm ml-auto">İşlemleri indir (.CSV)</button>
      </div>
      ${ticketRequestsTableHtml(d.requests)}`;
  } else if (sub === "given") {
    panel = `<div class="admin-ticket-panel-body">${ticketEntriesTableHtml(d.entries)}</div>`;
  } else if (sub === "table") {
    panel = `<div class="admin-ticket-panel-body">${ticketEntriesTableHtml(d.entries, { showWinner: ev.status === "drawn" })}</div>`;
  } else if (sub === "leaderboard") {
    panel = `<div class="admin-ticket-panel-body">${ticketLeaderboardHtml(d.entries)}</div>`;
  } else if (sub === "manual") {
    panel = `<div class="admin-ticket-panel-body">
      <form id="ticket-manual-form" class="admin-inset max-w-lg space-y-3 p-4">
        <p class="admin-section__title text-sm">Manuel talep</p>
        <label class="block text-sm"><span class="mb-1 block text-slate-500">Üye ID *</span><input name="userId" type="number" min="1" required class="admin-input w-full" /></label>
        <label class="block text-sm"><span class="mb-1 block text-slate-500">Site kullanıcı adı *</span><input name="siteUsername" required class="admin-input w-full" /></label>
        <label class="block text-sm"><span class="mb-1 block text-slate-500">Yatırım (TRY) *</span><input name="investmentAmount" type="number" min="1" step="0.01" required class="admin-input w-full" /></label>
        <label class="block text-sm"><span class="mb-1 block text-slate-500">Bilet adedi (boş=otomatik)</span><input name="ticketsEligible" type="number" min="1" class="admin-input w-full" /></label>
        <label class="flex items-center gap-2 text-sm"><input type="checkbox" name="autoApprove" class="admin-checkbox" checked /><span>Ekler eklemez onayla ve bilet ver</span></label>
        <button type="submit" class="admin-btn admin-btn-primary">Talep ekle</button>
      </form>
    </div>`;
  } else {
    const ends = ev.endsAt ? formatTs(ev.endsAt) : "—";
    panel = `<div class="admin-ticket-panel-body space-y-4">
      <p class="text-sm text-slate-600">${escapeHtml(ev.description || "")}</p>
      <dl class="grid gap-2 text-sm sm:grid-cols-2">
        <div><dt class="text-slate-500">Sponsor</dt><dd class="font-medium">${escapeHtml(ev.sponsorTitle || "—")}</dd></div>
        <div><dt class="text-slate-500">Durum</dt><dd>${escapeHtml(ticketStatusLabel(ev.status))}</dd></div>
        <div><dt class="text-slate-500">Ödül</dt><dd>${escapeHtml(ev.prizeDescription || "—")}</dd></div>
        <div><dt class="text-slate-500">Bilet / yatırım</dt><dd>${formatTry(ev.investmentPerTicket || 1000)} / bilet</dd></div>
        <div><dt class="text-slate-500">Bitiş</dt><dd>${escapeHtml(ends)}</dd></div>
      </dl>
      ${ev.status !== "drawn"
        ? `<div class="flex flex-wrap gap-2 pt-2">
            ${ev.status === "open"
              ? `<button type="button" class="ticket-act admin-btn admin-btn-ghost text-xs" data-act="close" data-id="${ev.id}">Kapat</button>`
              : `<button type="button" class="ticket-act admin-btn admin-btn-ghost text-xs" data-act="open" data-id="${ev.id}">Yeniden aç</button>`}
            <button type="button" class="ticket-act admin-btn admin-btn-primary text-xs" data-act="draw" data-id="${ev.id}" data-winners="${ev.winnerCount}">Sonuçlandır</button>
          </div>`
        : ""}
    </div>`;
  }

  return `
    <div class="admin-tickets-wrap">
      <div class="admin-ticket-detail-head">
        <button type="button" id="ticket-back-list" class="admin-ticket-back admin-btn admin-btn-ghost text-sm">← Bilet listesi</button>
        <button type="button" class="ticket-act admin-action-btn admin-action-btn--reject" data-act="delete" data-id="${ev.id}">Etkinliği sil</button>
      </div>
      <div class="admin-panel overflow-hidden p-0">
        <div class="admin-ticket-hero">
          <div class="admin-ticket-hero__main">
            <div class="admin-ticket-hero__cover">${cover}</div>
            <div>
              <h2 class="admin-ticket-hero__title">${escapeHtml(ev.title)} ★</h2>
              <div class="admin-ticket-hero__stats">
                <div class="admin-ticket-hero__stat"><span>Toplam katılım</span><strong>${Number(ev.participantCount) || 0}</strong></div>
                <div class="admin-ticket-hero__stat"><span>Toplam bilet</span><strong>${Number(ev.entryCount) || 0}</strong></div>
                <div class="admin-ticket-hero__stat"><span>Bilet kapasitesi</span><strong>${capLabel}</strong></div>
              </div>
            </div>
          </div>
          <div class="admin-ticket-hero__invest">
            <p class="admin-ticket-hero__invest-label">Bilet yatırımları toplamı</p>
            <p class="admin-ticket-hero__invest-value">${formatTry(ev.totalInvestment || 0)}</p>
          </div>
        </div>
        <nav class="admin-ticket-tabs">${tabBtns}</nav>
        ${panel}
      </div>
    </div>`;
}

function ticketsPanelHtml() {
  if (state.ticketView === "detail" && state.ticketEventId) {
    return ticketEventDetailHtml();
  }
  return ticketEventsListHtml();
}

function telegramInvitesPanelHtml() {
  const d = state.telegramInvites || {};
  const s = d.settings || {};
  const botOk = Boolean(d.botConfigured);
  const botUser = d.botUsername ? `@${d.botUsername}` : "—";
  const lb = Array.isArray(d.leaderboard) ? d.leaderboard : [];
  const events = Array.isArray(d.events) ? d.events : [];
  const lbRows = lb
    .map(
      (r, i) => `<tr>
        <td class="admin-data-td--mono">${i + 1}</td>
        <td class="admin-data-td--user">${escapeHtml(r.username)}</td>
        <td class="admin-data-td--coin">${r.inviteCount}</td>
        <td class="admin-data-td--coin">${r.coinsEarned}</td>
      </tr>`
    )
    .join("");
  const evRows = events
    .slice(0, 50)
    .map(
      (e) => `<tr>
        <td class="admin-data-td--muted">${formatTs(e.createdAt)}</td>
        <td class="admin-data-td--user">${escapeHtml(e.inviterUsername)}</td>
        <td class="admin-data-td--user">${escapeHtml(e.inviteeUsername || String(e.inviteeTelegramUserId))}</td>
        <td class="admin-data-td--coin">${e.coinsAwarded}</td>
      </tr>`
    )
    .join("");
  const botBadge = botOk
    ? `<span class="admin-badge admin-badge--active">${escapeHtml(botUser)}</span>`
    : `<span class="admin-badge admin-badge--inactive">Yapılandırılmamış</span>`;
  const widgetDomain = d.widgetDomain ? escapeHtml(String(d.widgetDomain)) : "—";
  return `
    <div class="admin-cms-page">
      <header class="admin-data-page__head">
        <h2 class="admin-data-page__title">Telegram davet</h2>

      </header>
      <div class="admin-cms-info">
        <strong>Giriş / kayıt butonu (Login Widget):</strong> Sitede «Bot domain invalid» görünüyorsa @BotFather → <code>/setdomain</code> → ${botOk ? escapeHtml(botUser) : "botunuzu"} seçin → domain olarak <code>${widgetDomain}</code> yazın.
        Site hem <code>www</code> hem köks domain ile açılıyorsa ikisini de ekleyin.
      </div>
      <div class="admin-cms-info">Davet eden üyeler coin kazanır; ayarları kaydettikten sonra kampanyayı aktif edin.</div>
      <div class="admin-tg-stats">
        <span class="admin-tg-stats__label">Bot: ${botBadge}</span>
        <span class="admin-tg-stats__label">Toplam davet: <strong>${Number(d.totalInvites) || 0}</strong></span>
        <span class="admin-tg-stats__label">Durum: ${adminActiveBadge(Boolean(s.isActive))}</span>
      </div>
      <div class="admin-cms-card admin-tg-settings">
        <form id="tg-invite-settings-form" class="admin-tg-settings__form">
          <label class="admin-tg-settings__toggle">
            <input type="checkbox" name="isActive" ${s.isActive ? "checked" : ""} />
            Kampanya aktif
          </label>
          <label class="admin-label" for="tg-group-chat-id">Grup chat ID (ör. -1001234567890)</label>
          <input id="tg-group-chat-id" name="groupChatId" type="text" class="admin-input" value="${escapeHtml(String(s.groupChatId || ""))}" placeholder="-100..." />
          <label class="admin-label" for="tg-coins-per-invite">Davet başına coin</label>
          <input id="tg-coins-per-invite" name="coinsPerInvite" type="number" min="1" max="100000" class="admin-input" value="${Number(s.coinsPerInvite) || 50}" />
          <button type="submit" class="admin-btn admin-btn-primary">Kaydet</button>
        </form>
      </div>
      <div class="admin-tg-grid">
        <div class="admin-data-panel admin-tg-panel">
          <div class="admin-tg-panel__head">
            <h3 class="admin-tg-panel__title">Top 10 davetçi</h3>
          </div>
          <div class="admin-tg-table-wrap">
            <table class="admin-data-table">
              <thead><tr><th>#</th><th>Üye</th><th>Davet</th><th>Coin</th></tr></thead>
              <tbody>${lbRows || "<tr><td colspan=\"4\" class=\"admin-empty admin-empty--muted\">Henüz kayıt yok</td></tr>"}</tbody>
            </table>
          </div>
        </div>
        <div class="admin-data-panel admin-tg-panel">
          <div class="admin-tg-panel__head">
            <h3 class="admin-tg-panel__title">Son davetler</h3>
          </div>
          <div class="admin-tg-table-wrap">
            <table class="admin-data-table">
              <thead><tr><th>Tarih</th><th>Davet eden</th><th>Gelen</th><th>Coin</th></tr></thead>
              <tbody>${evRows || "<tr><td colspan=\"4\" class=\"admin-empty admin-empty--muted\">Kayıt yok</td></tr>"}</tbody>
            </table>
          </div>
        </div>
      </div>
    </div>`;
}

let membersSearchDebounce = null;

const MEMBERS_PAGE_SIZE = 20;
const MEMBERS_SYNC_PAGE_KEY = "mocker_members_sync_page";

let membersLoadInFlight = null;

function isMembersExternalSource() {
  return false;
}

function membersExternalTotal() {
  const sync = state.membersSync;
  if (sync && typeof sync.remoteTotal === "number" && sync.remoteTotal > 0) return sync.remoteTotal;
  const load = state.membersLoadInfo;
  if (load && typeof load.total === "number") return load.total;
  return state.members.length;
}

function buildMembersApiQuery(page) {
  const params = new URLSearchParams();
  params.set("page", String(Math.max(1, page || 1)));
  const q = (state.membersFilter || "").trim();
  if (q) params.set("username", q);
  return params.toString();
}

function setMembersLoadProgress(fetched, total, pagesFetched, lastPage, complete) {
  state.membersLoadInfo = {
    fetched,
    total: total > 0 ? total : fetched,
    complete: !!complete,
    pages_fetched: pagesFetched,
    last_page: lastPage,
  };
}

function membersMissingCount() {
  const sync = state.membersSync;
  if (sync && typeof sync.missingCount === "number" && sync.metaTrustworthy !== false) {
    return Math.max(0, Number(sync.missingCount));
  }
  if (sync && sync.metaTrustworthy === false && (Number(sync.syncedCount) || 0) > 0) {
    return 1;
  }
  if (sync && sync.remoteTotal > 0) {
    return Math.max(0, Number(sync.remoteTotal) - Number(sync.syncedCount || 0));
  }
  const load = state.membersLoadInfo;
  const fetched = (state.members || []).length;
  const total = load && Number(load.total) > 0 ? Number(load.total) : fetched;
  return Math.max(0, total - fetched);
}

function membersSyncIncomplete() {
  const sync = state.membersSync;
  if (!sync || !sync.externalEnabled) return false;
  if (sync.metaTrustworthy === false || sync.needsRescan) return true;
  const synced = Number(sync.syncedCount) || 0;
  const remote = Number(sync.remoteTotal) || 0;
  if (remote >= 100 && synced < remote) return true;
  const lp = Number(sync.lastPage) || 0;
  const lsp = Number(sync.lastSyncedPage) || 0;
  if (lp > 1 && lsp > lp) return true;
  if (sync.syncComplete === true) {
    return membersMissingCount() > 50;
  }
  return true;
}

async function fetchMembersSyncPage(page) {
  let lastErr = null;
  for (let attempt = 0; attempt < 4; attempt++) {
    try {
      const j = await api(`admin_members_sync.php?${buildMembersApiQuery(page)}`, { method: "GET" });
      if (j && j.ok !== false) return j;
      lastErr = new Error(j?.message || j?.error || "sync_failed");
    } catch (e) {
      lastErr = e instanceof Error ? e : new Error(String(e));
    }
    if (attempt < 3) {
      await new Promise((r) => setTimeout(r, 800 * (attempt + 1)));
    }
  }
  throw lastErr || new Error("sync_failed");
}

async function syncExternalMembersToDatabase(opts = {}) {
  const forceFresh = opts.fresh === true;
  if (forceFresh) {
    const resetStatus = await api("admin_members_sync.php?reset=1");
    state.membersSync = resetStatus;
  }
  const status = forceFresh ? state.membersSync : await api("admin_members_sync.php?status=1");
  if (!forceFresh) state.membersSync = status;
  if (!status.externalEnabled) return status;

  const incomplete = membersSyncIncomplete() || Number(status.missingCount) > 0;
  if (!forceFresh && !opts.resume && status.syncComplete && !incomplete) {
    return status;
  }

  let page = 1;
  if (!forceFresh) {
    const missing = Number(status.missingCount) || 0;
    page =
      missing > 0
        ? 1
        : Math.max(1, Number(status.resumePage) || Number(status.lastSyncedPage) + 1 || 1);
  }
  let lastPage = Math.max(Number(status.lastPage) || 1, 1);
  if (status.needsRescan || status.metaTrustworthy === false || page > lastPage) {
    page = 1;
    lastPage = Math.max(lastPage, page);
  }
  toast(
    incomplete
      ? `Eksik ${membersMissingCount().toLocaleString("tr-TR")} üye — sayfa ${page} den senkron devam`
      : "Senkron başlıyor…",
    "ok"
  );
  try {
    sessionStorage.setItem(MEMBERS_SYNC_PAGE_KEY, String(page));
  } catch {
  }

  lastPage = Math.max(lastPage, Number(status.lastPage) || 1, page);
  let remoteTotal = Math.max(0, Number(status.remoteTotal) || 0);
  let lastSynced = Number(status.syncedCount) || 0;

  while (page <= lastPage) {
    let j;
    try {
      j = await fetchMembersSyncPage(page);
    } catch (e) {
      const body = e && typeof e === "object" && e.body ? e.body : null;
      if (body && typeof body.synced_total === "number") {
        lastSynced = body.synced_total;
        state.membersSync = {
          externalEnabled: true,
          syncedCount: lastSynced,
          remoteTotal: body.remoteTotal || remoteTotal,
          lastPage,
          lastSyncedPage: Number(body.lastSyncedPage) || page - 1,
          resumePage: Number(body.resumePage) || page,
          syncComplete: false,
        };
        setMembersLoadProgress(lastSynced, remoteTotal, page - 1, lastPage, false);
        await loadLocalMembersFromDb();
        renderDashboard();
      }
      throw e;
    }
    lastPage = Math.max(lastPage, Number(j.pagination?.last_page) || lastPage);
    remoteTotal = Math.max(remoteTotal, Number(j.remoteTotal || j.pagination?.total) || 0);
    const synced = Number(j.synced_total) || 0;
    lastSynced = synced;
    const complete = !!j.syncComplete && synced >= remoteTotal;
    setMembersLoadProgress(synced, remoteTotal, page, lastPage, complete);
    state.membersSync = {
      externalEnabled: true,
      syncedCount: synced,
      remoteTotal,
      lastPage,
      lastSyncedPage: page,
      resumePage: complete ? 1 : page + 1,
      missingCount: Math.max(0, remoteTotal - synced),
      metaTrustworthy: remoteTotal >= 100 && lastPage > 1,
      syncComplete: complete,
    };
    try {
      sessionStorage.setItem(MEMBERS_SYNC_PAGE_KEY, String(complete ? 1 : page + 1));
    } catch {
    }
    await loadLocalMembersFromDb();
    if (page % 2 === 0 || complete || page >= lastPage) renderDashboard();
    if (complete) break;
    page++;
  }

  return state.membersSync;
}

async function loadLocalMembersFromDb() {
  const j = await api("admin_users.php");
  if (!j || j.ok === false) throw new Error(j?.error || "load_failed");
  state.members = Array.isArray(j.users) ? j.users : [];
  state.membersSource = "local";
  if (j.sync && typeof j.sync === "object") {
    state.membersSync = j.sync;
  }
  const synced = Number(state.membersSync?.syncedCount) || state.members.length;
  const remote = Number(state.membersSync?.remoteTotal) || state.members.length;
  const complete = !!state.membersSync?.syncComplete;
  setMembersLoadProgress(
    synced,
    remote,
    Number(state.membersSync?.lastSyncedPage) || 0,
    Number(state.membersSync?.lastPage) || 0,
    complete
  );
}

async function reloadMembers(opts = {}) {
  try {
    await loadMembers(opts);
  } catch (e) {
    toast(e.message || "Üyeler yüklenemedi", "err");
  }
  renderDashboard();
}

/** @param {{ resume?: boolean, fresh?: boolean }} [opts] */
async function loadMembers(opts = {}) {
  if (membersLoadInFlight && !opts.fresh) {
    return membersLoadInFlight;
  }

  const task = (async () => {
    state.membersLoading = true;
    state.membersExternalError = null;
    try {
      const wantApiSync = opts.fresh === true || opts.resume === true;
      if (wantApiSync) {
        const status = await api("admin_members_sync.php?status=1");
        state.membersSync = status;
        if (status.externalEnabled) {
          await syncExternalMembersToDatabase(opts);
        } else if (opts.fresh || opts.resume) {
          toast("Harici API senkronu kapalı — liste SQLite veritabanından geliyor.", "ok");
        }
      }
      resetMembersListPage();
      await loadLocalMembersFromDb();
      if (state.tab === "members" && !state.viewMemberProfileUserId) {
        renderMembersTabPartial();
      } else {
        renderDashboard();
      }
    } catch (e) {
      state.membersExternalError = e instanceof Error ? e.message : String(e);
      try {
        await loadLocalMembersFromDb();
        renderDashboard();
      } catch {
      }
    } finally {
      state.membersLoading = false;
      if (state.tab === "members" && !state.viewMemberProfileUserId) {
        const kpiEl = document.getElementById("members-kpi-mount");
        if (kpiEl) renderMembersTabPartial();
      }
    }
  })();

  membersLoadInFlight = task;
  try {
    await task;
  } finally {
    if (membersLoadInFlight === task) membersLoadInFlight = null;
  }
}

function getAuditLogUnixRange() {
  const preset = state.auditDatePreset || "all";
  const now = new Date();
  if (preset === "all") {
    return { from: null, to: null };
  }
  if (preset === "today") {
    const start = new Date(now.getFullYear(), now.getMonth(), now.getDate());
    const end = new Date(start);
    end.setHours(23, 59, 59, 999);
    return { from: Math.floor(start.getTime() / 1000), to: Math.floor(end.getTime() / 1000) };
  }
  if (preset === "yesterday") {
    const start = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1);
    const end = new Date(start);
    end.setHours(23, 59, 59, 999);
    return { from: Math.floor(start.getTime() / 1000), to: Math.floor(end.getTime() / 1000) };
  }
  if (preset === "custom") {
    const a = (state.auditDateFrom || "").trim();
    const b = (state.auditDateTo || "").trim();
    if (!a || !b) {
      return { from: null, to: null };
    }
    const d1 = new Date(a + "T00:00:00");
    const d2 = new Date(b + "T23:59:59");
    const from = Math.floor(d1.getTime() / 1000);
    const to = Math.floor(d2.getTime() / 1000);
    if (!Number.isFinite(from) || !Number.isFinite(to) || to < from) {
      return { from: null, to: null };
    }
    return { from, to };
  }
  return { from: null, to: null };
}

function ensureAuditCustomDateDefaults() {
  const now = new Date();
  const to = now.toISOString().slice(0, 10);
  const fromD = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 7);
  const from = fromD.toISOString().slice(0, 10);
  if (!state.auditDateFrom) state.auditDateFrom = from;
  if (!state.auditDateTo) state.auditDateTo = to;
}

function auditLogFilterDescription() {
  const p = state.auditDatePreset || "all";
  if (p === "all") return "Tüm zamanlar";
  if (p === "today") return "Bugün";
  if (p === "yesterday") return "Dün";
  if (p === "custom") {
    const a = state.auditDateFrom || "";
    const b = state.auditDateTo || "";
    return a && b ? `${a} — ${b}` : "Tarih aralığı (başlangıç ve bitiş seçin)";
  }
  return "";
}

async function loadAuditLog() {
  const { from, to } = getAuditLogUnixRange();
  let q = "admin_users.php?auditLog=1&limit=500";
  if (from != null && to != null) {
    q += `&from=${from}&to=${to}`;
  }
  const j = await api(q, { method: "GET" });
  state.auditLog = Array.isArray(j.entries) ? j.entries : [];
  state.auditTotal = typeof j.total === "number" ? j.total : state.auditLog.length;
}

const AUDIT_ACTION_LABELS = {
  user_meta: "İç not / etiket",
  user_coins: "Coin puanı",
  user_password_reset: "Şifre sıfırlama",
  user_profile: "Profil (ad / e-posta)",
  user_status: "Üyelik durumu (aktif/pasif)",
  user_delete: "Üye silme",
  user_wheel: "Şans çarkı (hakkı)",
};

function auditLogPanelHtml() {
  const preset = state.auditDatePreset || "all";
  const customVisible = preset === "custom";
  const fd = escapeHtml(state.auditDateFrom || "");
  const td = escapeHtml(state.auditDateTo || "");
  const toolbar = `<div class="admin-audit-toolbar admin-glass mb-4 max-w-7xl rounded-2xl border border-white/[0.08] p-4 sm:p-5">
    <p class="admin-members-toolbar__eyebrow mb-3">Tarih</p>
    <div class="flex flex-wrap items-end gap-3 sm:gap-4">
      <div class="min-w-[11rem]">
        <label class="admin-members-field-label" for="audit-date-preset">Aralık</label>
        <select id="audit-date-preset" class="admin-input w-full min-w-0">
          <option value="all" ${preset === "all" ? "selected" : ""}>Tümü</option>
          <option value="today" ${preset === "today" ? "selected" : ""}>Bugün</option>
          <option value="yesterday" ${preset === "yesterday" ? "selected" : ""}>Dün</option>
          <option value="custom" ${preset === "custom" ? "selected" : ""}>Tarih aralığı</option>
        </select>
      </div>
      <div id="audit-date-custom-wrap" class="flex flex-wrap items-end gap-3 ${customVisible ? "" : "hidden"}">
        <div>
          <label class="admin-members-field-label" for="audit-date-from">Başlangıç</label>
          <input id="audit-date-from" type="date" class="admin-input w-[11rem] tabular-nums" value="${fd}" autocomplete="off" />
        </div>
        <div>
          <label class="admin-members-field-label" for="audit-date-to">Bitiş</label>
          <input id="audit-date-to" type="date" class="admin-input w-[11rem] tabular-nums" value="${td}" autocomplete="off" />
        </div>
        <button type="button" id="audit-date-apply" class="admin-btn admin-btn-primary px-4 py-2">Uygula</button>
      </div>
    </div>
    <p class="mt-3 text-xs text-slate-500">Seçili: <strong class="text-slate-400">${escapeHtml(auditLogFilterDescription())}</strong></p>
  </div>`;

  const rows = state.auditLog || [];
  if (!rows.length) {
    return `${toolbar}<p class="admin-empty admin-empty--muted">Bu aralıkta kayıt yok veya liste yüklenemedi. Farklı tarih seç veya <strong>Yenile</strong> ile tekrar dene.</p>`;
  }
  const tr = rows
    .map((e) => {
      const who = e.actorDisplayName
        ? `${escapeHtml(String(e.actorDisplayName))} <span class="text-slate-500">(${escapeHtml(String(e.actorUsername))})</span>`
        : escapeHtml(String(e.actorUsername || "—"));
      const kind = e.actorKind === "staff" ? "Personel" : "Ana yönetici";
      const act = AUDIT_ACTION_LABELS[e.action] || e.action;
      const tgt = e.targetUserId != null ? `<span class="tabular-nums">#${e.targetUserId}</span>` : "—";
      let det = "";
      try {
        det = JSON.stringify(e.details && typeof e.details === "object" ? e.details : {}, null, 0);
      } catch {
        det = "{}";
      }
      const ip = e.ip ? escapeHtml(String(e.ip)) : "—";
      const ts = formatTs(e.createdAt);
      const reqMethod = escapeHtml(String(e.requestMethod || "GET").toUpperCase());
      const reqPath = escapeHtml(String(e.requestPath || "—"));
      const reqQuery = String(e.requestQuery || "").trim();
      const reqLine = reqQuery ? `${reqPath}?${escapeHtml(reqQuery)}` : reqPath;
      const ua = e.userAgent ? escapeHtml(String(e.userAgent)) : "—";
      return `<tr class="admin-table-row">
      <td class="px-3 py-2.5 align-top font-mono text-[11px] text-slate-500 whitespace-nowrap">${ts}</td>
      <td class="px-3 py-2.5 align-top text-sm text-slate-200">${who}<div class="mt-0.5 text-[11px] text-slate-500">${escapeHtml(kind)}</div></td>
      <td class="px-3 py-2.5 align-top text-sm text-amber-200/95">${escapeHtml(String(act))}</td>
      <td class="px-3 py-2.5 align-top font-mono text-xs text-slate-300">${tgt}</td>
      <td class="px-3 py-2.5 align-top text-xs text-slate-400">
        <div><span class="inline-flex items-center rounded border border-white/15 px-1.5 py-0.5 font-mono text-[10px] text-slate-300">${reqMethod}</span></div>
        <div class="mt-1 font-mono text-[11px] break-all">${reqLine}</div>
      </td>
      <td class="px-3 py-2.5 align-top text-xs text-slate-400 max-w-xl"><pre class="audit-log-details m-0 whitespace-pre-wrap break-words font-mono text-[11px] leading-relaxed">${escapeHtml(det)}</pre></td>
      <td class="px-3 py-2.5 align-top font-mono text-[11px] text-slate-500">${ip}</td>
      <td class="px-3 py-2.5 align-top text-[11px] text-slate-500 max-w-[16rem] break-words" title="${ua}">${ua}</td>
    </tr>`;
    })
    .join("");
  const total = state.auditTotal != null ? state.auditTotal : rows.length;
  return `${toolbar}<div class="admin-glass max-w-7xl rounded-2xl p-4 sm:p-5">
    <div class="overflow-x-auto">
    <table class="w-full min-w-[1080px] border-collapse text-left">
      <thead>
        <tr class="border-b border-white/15 text-[11px] font-semibold uppercase tracking-wide text-slate-500">
          <th scope="col" class="px-3 py-2">Zaman</th>
          <th scope="col" class="px-3 py-2">Kim</th>
          <th scope="col" class="px-3 py-2">İşlem</th>
          <th scope="col" class="px-3 py-2">Üye</th>
          <th scope="col" class="px-3 py-2">İstek</th>
          <th scope="col" class="px-3 py-2">Detay (JSON)</th>
          <th scope="col" class="px-3 py-2">IP</th>
          <th scope="col" class="px-3 py-2">Cihaz (UA)</th>
        </tr>
      </thead>
      <tbody>${tr}</tbody>
    </table>
    </div>
    <p class="mt-4 text-xs text-slate-500">Bu filtreye uyan toplam <strong class="text-slate-400">${total}</strong> kayıt; bu ekranda en fazla <strong class="text-slate-400">${rows.length}</strong> satır (limit 500).</p>
  </div>`;
}

async function loadMarketRequests() {
  const j = await api("admin_purchases.php", { method: "GET" });
  state.marketRequests = Array.isArray(j.purchases) ? j.purchases : [];
}

async function loadComplaints() {
  const j = await api("admin_complaints.php", { method: "GET" });
  state.complaints = Array.isArray(j.complaints) ? j.complaints : [];
}

async function loadAffiliateRequests() {
  const j = await api("admin_affiliate_requests.php", { method: "GET" });
  state.affiliateRequests = Array.isArray(j.requests) ? j.requests : [];
}

async function loadSpecialOdds() {
  const j = await api("admin_special_odds.php", { method: "GET" });
  state.specialOddsMatches = Array.isArray(j.matches) ? j.matches : [];
}

function specialOddsPanelHtml() {
  const rows = state.specialOddsMatches || [];
      const tr = rows
    .map((m) => {
      const ko = formatTs(m.kickoffAt);
      const st = String(m.status || "");
      const score =
        m.homeGoals != null && m.awayGoals != null ? `${m.homeGoals}-${m.awayGoals}` : "—";
      const ap = String(m.adminPick || "home").toLowerCase();
      const pickLab = ap === "draw" ? "X" : ap === "away" ? "2" : "1";
      const oo = Number(m.oddsOffered != null ? m.oddsOffered : m.oddsHome) || 0;
      const pred = String(m.predictionText || "").trim();
      const predCell = pred
        ? `<span class="line-clamp-2 block max-w-[220px] text-xs text-slate-400" title="${escapeHtml(pred)}">${escapeHtml(pred.length > 120 ? `${pred.slice(0, 120)}…` : pred)}</span>`
        : "—";
      const actions =
        st === "open"
          ? `<div class="flex flex-wrap gap-1">
          <button type="button" class="admin-btn-so-sync rounded border border-sky-500/30 bg-sky-500/10 px-2 py-1 text-xs text-sky-200" data-so-id="${m.id}">API senkron</button>
          <button type="button" class="admin-btn-so-settle rounded border border-emerald-500/30 bg-emerald-500/10 px-2 py-1 text-xs text-emerald-200" title="Gerçek sonuç 1" data-so-settle="${m.id}" data-result="home">1</button>
          <button type="button" class="admin-btn-so-settle rounded border border-emerald-500/30 bg-emerald-500/10 px-2 py-1 text-xs text-emerald-200" title="Gerçek sonuç X" data-so-settle="${m.id}" data-result="draw">X</button>
          <button type="button" class="admin-btn-so-settle rounded border border-emerald-500/30 bg-emerald-500/10 px-2 py-1 text-xs text-emerald-200" title="Gerçek sonuç 2" data-so-settle="${m.id}" data-result="away">2</button>
          <button type="button" class="admin-btn-so-cancel rounded border border-amber-500/30 bg-amber-500/10 px-2 py-1 text-xs text-amber-200" data-so-cancel="${m.id}">İptal</button>
          <button type="button" class="admin-btn-so-del rounded border border-rose-500/30 bg-rose-500/10 px-2 py-1 text-xs text-rose-200" data-so-del="${m.id}">Sil</button>
        </div>`
          : escapeHtml(st);
      return `<tr class="border-b border-white/10">
        <td class="px-3 py-2 font-mono text-xs text-slate-500">${m.id}</td>
        <td class="px-3 py-2 text-sm text-slate-200">${escapeHtml(String(m.homeName || ""))} — ${escapeHtml(String(m.awayName || ""))}</td>
        <td class="px-3 py-2 font-mono text-xs text-slate-400">${escapeHtml(String(m.externalEventId || ""))}</td>
        <td class="px-3 py-2 text-xs text-slate-400">${ko}</td>
        <td class="px-3 py-2 text-xs text-slate-300">${predCell}</td>
        <td class="px-3 py-2 font-mono text-xs text-slate-300">${pickLab} @ ${oo.toFixed(2)}</td>
        <td class="px-3 py-2 font-mono text-xs text-slate-400">${Number(m.maxParticipants) > 0 ? Number(m.maxParticipants) : "∞"}</td>
        <td class="px-3 py-2 text-xs text-slate-400">${escapeHtml(st)} ${m.result ? `(${escapeHtml(String(m.result))})` : ""} ${score !== "—" ? score : ""}</td>
        <td class="px-3 py-2">${actions}</td>
      </tr>`;
    })
    .join("");
  const todayIso = new Date().toISOString().slice(0, 10);
  return `<div class="admin-data-page space-y-6 max-w-6xl">
    <header class="admin-data-page__head">
      <h2 class="admin-data-page__title">Özel oranlar</h2>
    </header>
    <div class="admin-panel p-4 sm:p-5">
      <h3 class="mb-2 admin-section__title text-sm">API'den maç seç</h3>
      <p class="mb-3 text-xs text-slate-500">Tarih seç → <strong class="text-slate-400">Maçları getir</strong> → <strong>Seç</strong> → tahmin metni, tek oran ve satır (1/X/2) girip kaydet.</p>
      <div class="flex flex-wrap items-end gap-3">
        <div>
          <label class="mb-1 block text-[11px] uppercase text-slate-500">Tarih</label>
          <input id="so-api-date" type="date" class="admin-input" value="${todayIso}" />
        </div>
        <button type="button" id="btn-so-fetch-api" class="admin-btn admin-btn-soft px-4 py-2">Maçları getir</button>
      </div>
      <p id="so-api-msg" class="mt-2 text-xs text-slate-500" role="status"></p>
      <div class="admin-table-wrap mt-3 max-h-80 overflow-auto rounded-xl border border-white/10">
        <table class="w-full min-w-[640px] text-left text-sm">
          <thead class="sticky top-0 border-b border-white/10 bg-[#131b28] text-[11px] uppercase text-slate-500">
            <tr>
              <th class="px-3 py-2">API ID</th>
              <th class="px-3 py-2">Maç</th>
              <th class="px-3 py-2">Başlama</th>
              <th class="px-3 py-2">İşlem</th>
            </tr>
          </thead>
          <tbody id="so-api-tbody">
            <tr><td colspan="4" class="px-3 py-6 text-center text-slate-500">Tarih seçip maçları getir.</td></tr>
          </tbody>
        </table>
      </div>
    </div>
    <div class="admin-panel p-4 sm:p-5">
      <h3 class="mb-3 admin-section__title text-sm">Yeni maç (veya API listesinden seç)</h3>
      <div class="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
        <div><label class="mb-1 block text-[11px] uppercase text-slate-500">API maç ID</label><input id="so-ext-id" class="admin-input w-full" placeholder="Listeden seç veya yapıştır" /></div>
        <div><label class="mb-1 block text-[11px] uppercase text-slate-500">Ev</label><input id="so-home" class="admin-input w-full" /></div>
        <div><label class="mb-1 block text-[11px] uppercase text-slate-500">Dep</label><input id="so-away" class="admin-input w-full" /></div>
        <div><label class="mb-1 block text-[11px] uppercase text-slate-500">Logo ev (URL)</label><input id="so-logo-h" class="admin-input w-full" placeholder="https://example.com/logo.png" /></div>
        <div><label class="mb-1 block text-[11px] uppercase text-slate-500">Logo dep (URL)</label><input id="so-logo-a" class="admin-input w-full" placeholder="https://example.com/logo.png" /></div>
        <div><label class="mb-1 block text-[11px] uppercase text-slate-500">Başlama</label><input id="so-kickoff" type="datetime-local" class="admin-input w-full" /></div>
        <div class="sm:col-span-2"><label class="mb-1 block text-[11px] uppercase text-slate-500">Tahmin metni (üyelere görünür)</label><textarea id="so-prediction" class="admin-input min-h-[72px] w-full resize-y" placeholder="Örn: Ev sahibi kazanır, 2.5 üst gibi…"></textarea></div>
        <div><label class="mb-1 block text-[11px] uppercase text-slate-500">Satır (bahsin bağlı olduğu sonuç)</label><select id="so-admin-pick" class="admin-input w-full"><option value="home">1 — Ev</option><option value="draw">X — Beraberlik</option><option value="away">2 — Deplasman</option></select></div>
        <div><label class="mb-1 block text-[11px] uppercase text-slate-500">Oran (tek)</label><input id="so-odds-one" type="number" step="0.01" min="1.01" class="admin-input w-full" value="2" /></div>
        <div><label class="mb-1 block text-[11px] uppercase text-slate-500">Max katılımcı</label><input id="so-max-participants" type="number" min="0" step="1" class="admin-input w-full" value="0" /><p class="mt-0.5 text-[10px] text-slate-500">0 = sınırsız</p></div>
      </div>
      <button type="button" id="btn-so-create" class="admin-btn admin-btn-primary mt-4 px-4 py-2">Maç ekle</button>
    </div>
    <div class="flex flex-wrap items-center gap-2">
      <button type="button" id="btn-so-refresh" class="admin-btn admin-btn-soft px-4 py-2">Yenile</button>
      <button type="button" id="btn-so-sync-all" class="admin-btn admin-btn-soft px-4 py-2">Tümünü API ile senkron</button>
    </div>
    <div class="admin-table-wrap overflow-x-auto">
      <table class="w-full min-w-[1120px] text-left text-sm">
        <thead class="border-b border-white/10 text-xs uppercase text-slate-500">
          <tr>
            <th class="px-3 py-2">ID</th>
            <th class="px-3 py-2">Maç</th>
            <th class="px-3 py-2">Ext ID</th>
            <th class="px-3 py-2">Başlama</th>
            <th class="px-3 py-2">Tahmin</th>
            <th class="px-3 py-2">Satır @ oran</th>
            <th class="px-3 py-2">Max</th>
            <th class="px-3 py-2">Durum</th>
            <th class="px-3 py-2">İşlem</th>
          </tr>
        </thead>
        <tbody>${tr || `<tr><td colspan="7" class="px-3 py-6 text-center text-slate-500">Henüz maç yok.</td></tr>`}</tbody>
      </table>
    </div>
  </div>`;
}

function initDashboardRangeToday() {
  const now = new Date();
  const start = new Date(now.getFullYear(), now.getMonth(), now.getDate());
  state.dashboardFrom = Math.floor(start.getTime() / 1000);
  state.dashboardTo = Math.floor(Date.now() / 1000);
  state.dashboardPreset = "today";
}

function applyDashboardPreset(key) {
  if (key === "custom") {
    state.dashboardPreset = "custom";
    return;
  }
  const now = new Date();
  state.dashboardPreset = key;
  if (key === "today") {
    initDashboardRangeToday();
    return;
  }
  if (key === "yesterday") {
    const start = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1);
    state.dashboardFrom = Math.floor(start.getTime() / 1000);
    state.dashboardTo = state.dashboardFrom + 86400 - 1;
    return;
  }
  if (key === "7d") {
    state.dashboardTo = Math.floor(Date.now() / 1000);
    state.dashboardFrom = state.dashboardTo - 7 * 86400;
    return;
  }
  if (key === "month") {
    const start = new Date(now.getFullYear(), now.getMonth(), 1);
    state.dashboardFrom = Math.floor(start.getTime() / 1000);
    state.dashboardTo = Math.floor(Date.now() / 1000);
  }
}

function unixToDateInputLocal(ts) {
  const d = new Date(Number(ts) * 1000);
  const y = d.getFullYear();
  const m = String(d.getMonth() + 1).padStart(2, "0");
  const day = String(d.getDate()).padStart(2, "0");
  return `${y}-${m}-${day}`;
}

function dateInputToUnixStart(iso) {
  const [y, m, d] = String(iso || "")
    .split("-")
    .map((x) => parseInt(x, 10));
  if (!y || !m || !d) return 0;
  return Math.floor(new Date(y, m - 1, d).getTime() / 1000);
}

function dateInputToUnixEndOfDay(iso) {
  const start = dateInputToUnixStart(iso);
  if (!start) return 0;
  return start + 86400 - 1;
}

async function loadDashboardStats() {
  const j = await api(`admin_stats.php?from=${state.dashboardFrom}&to=${state.dashboardTo}`, { method: "GET" });
  state.dashboardStats = j;
}

function dashClickRankTableHtml(rows) {
  if (!Array.isArray(rows) || rows.length === 0) {
    return `<p class="text-sm text-slate-500">Bu dönemde tıklama yok.</p>`;
  }
  return `<div class="admin-table-wrap admin-data-table-wrap mt-2">
    <table class="admin-data-table" style="min-width:100%">
      <thead><tr><th>İçerik</th><th class="text-right">Tıklama</th></tr></thead>
      <tbody>${rows
        .map(
          (row) =>
            `<tr class="admin-table-row"><td class="max-w-[14rem] truncate" title="${escapeHtml(row.label || row.itemId || "—")}">${escapeHtml(row.label || row.itemId || "—")}</td><td class="text-right font-semibold tabular-nums">${Number(row.clicks || 0).toLocaleString("tr-TR")}</td></tr>`,
        )
        .join("")}</tbody>
    </table>
  </div>`;
}

function formatDashDayLabel(ts) {
  if (!ts) return "—";
  try {
    return new Date(Number(ts) * 1000).toLocaleDateString("tr-TR", {
      day: "numeric",
      month: "short",
    });
  } catch {
    return "—";
  }
}

function formatPendingAgeLabel(ageSec) {
  const sec = Math.max(0, Number(ageSec) || 0);
  if (sec < 60) return "Az önce";
  if (sec < 3600) return `${Math.max(1, Math.floor(sec / 60))} dk`;
  if (sec < 86400) return `${Math.floor(sec / 3600)} saat`;
  const days = Math.floor(sec / 86400);
  return days === 1 ? "1 gün" : `${days} gün`;
}

function pendingSlaLabel(sla, oldestAgeHours) {
  const s = String(sla || "ok");
  if (s === "critical") return "3+ gün bekliyor";
  if (s === "warn") return "24+ saat bekliyor";
  const h = Number(oldestAgeHours) || 0;
  if (h > 0) return `En eski: ${formatPendingAgeLabel(h * 3600)}`;
  return "Güncel";
}

function dashboardPendingPanelHtml() {
  if (!canUseAdminHeaderAlerts()) return "";
  const alerts = state.headerAlerts || {};
  const total = Math.max(0, Number(alerts.totalPending) || 0);
  const critical = Math.max(0, Number(alerts.criticalCount) || 0);
  const warn = Math.max(0, Number(alerts.warnCount) || 0);
  const sections = Array.isArray(alerts.sections) ? alerts.sections : [];
  const items = Array.isArray(alerts.items) ? alerts.items : [];
  const rules = alerts.slaRules || { warnHours: 24, criticalHours: 72 };

  const sectionCards = sections
    .map((sec) => {
      const count = Math.max(0, Number(sec.count) || 0);
      const sla = String(sec.sla || "ok");
      const tab = escapeHtml(String(sec.tab || ""));
      const label = escapeHtml(String(sec.label || "Kuyruk"));
      const disabled = count === 0 ? " admin-dash-pending-card--empty" : "";
      const slaHint =
        count > 0 ? escapeHtml(pendingSlaLabel(sla, sec.oldestAgeHours)) : "Bekleyen yok";
      return `<button type="button" class="admin-dash-pending-card admin-dash-pending-card--sla-${sla}${disabled}" data-admin-alert-tab="${tab}" ${count === 0 ? "disabled" : ""}>
        <span class="admin-dash-pending-card__count">${count}</span>
        <span class="admin-dash-pending-card__label">${label}</span>
        <span class="admin-dash-pending-card__sla">${slaHint}</span>
      </button>`;
    })
    .join("");

  const queueItems = items.slice(0, 10);
  const listHtml = queueItems.length
    ? queueItems
        .map((it) => {
          const tab = escapeHtml(String(it.tab || ""));
          const title = escapeHtml(String(it.title || "Talep"));
          const msg = escapeHtml(String(it.message || ""));
          const sla = String(it.sla || "ok");
          const ageLabel = escapeHtml(formatPendingAgeLabel(Number(it.ageSec) || 0));
          return `<button type="button" class="admin-dash-pending-row admin-dash-pending-row--sla-${sla}" data-admin-alert-tab="${tab}">
            <span class="admin-dash-pending-row__ico">${adminAlertItemIcon(it.type)}</span>
            <span class="admin-dash-pending-row__main">
              <span class="admin-dash-pending-row__title">${title}</span>
              <span class="admin-dash-pending-row__msg">${msg}</span>
            </span>
            <span class="admin-pending-age admin-pending-age--${sla}">${ageLabel}</span>
          </button>`;
        })
        .join("")
    : `<p class="admin-dash-pending-queue__empty">Şu an bekleyen iş yok. Yeni talepler burada ve sağ üst bildirimde görünür.</p>`;

  const summaryTone =
    critical > 0 ? "critical" : warn > 0 ? "warn" : total > 0 ? "active" : "clear";

  return `
    <section class="admin-dash-pending admin-dash-pending--${summaryTone}" aria-labelledby="dash-pending-title">
      <div class="admin-dash-pending__head">
        <div>
          <p class="admin-dash-kicker">Operasyon</p>
          <h3 class="admin-dash-pending__title" id="dash-pending-title">Bekleyen işler</h3>
          <p class="admin-dash-pending__desc">Onay bekleyen talepler — önce en uzun bekleyenler listelenir. Sarı: ${rules.warnHours}+ saat, kırmızı: ${rules.criticalHours}+ saat.</p>
        </div>
        <div class="admin-dash-pending__actions">
          <span class="admin-dash-pending-total" title="Toplam bekleyen">${total}</span>
          ${critical > 0 ? `<span class="admin-dash-pending-flag admin-dash-pending-flag--critical">${critical} acil</span>` : ""}
          ${warn > 0 && critical === 0 ? `<span class="admin-dash-pending-flag admin-dash-pending-flag--warn">${warn} gecikmiş</span>` : ""}
          ${warn > 0 && critical > 0 ? `<span class="admin-dash-pending-flag admin-dash-pending-flag--warn">${warn} gecikmiş</span>` : ""}
          <button type="button" id="dash-pending-refresh" class="admin-btn admin-btn-ghost admin-btn-sm">Yenile</button>
        </div>
      </div>
      <div class="admin-dash-pending__grid">${sectionCards}</div>
      <div class="admin-dash-pending-queue">
        <p class="admin-dash-pending-queue__label">Öncelikli kuyruk</p>
        <div class="admin-dash-pending-queue__list">${listHtml}</div>
      </div>
    </section>`;
}

function dashboardPanelHtml() {
  const s = state.dashboardStats;
  const preset = state.dashboardPreset;
  const presetClass = (key) =>
    preset === key ? "admin-dash-preset-btn admin-dash-preset-btn--active" : "admin-dash-preset-btn";

  const fromLabel = formatTs(state.dashboardFrom);
  const toLabel = formatTs(state.dashboardTo);
  const spanSec = Math.max(0, Number(state.dashboardTo) - Number(state.dashboardFrom) + 1);
  const rangeDays = Math.max(1, Math.ceil(spanSec / 86400));

  const stat = s && s.ok !== false ? s : null;
  const totalUsers = stat ? Number(stat.totalUsers ?? 0) : "—";
  const newU = stat ? Number(stat.newUsersInRange ?? 0) : "—";
  const logins = stat ? Number(stat.loginsInRange ?? 0) : "—";
  const active = stat ? Number(stat.activeInRange ?? 0) : "—";
  const last24 = stat ? Number(stat.activeLast24h ?? 0) : "—";
  const purN = stat ? Number(stat.purchasesCount ?? 0) : "—";
  const purCoins = stat ? Number(stat.purchasesCoinsSum ?? 0) : "—";

  const totalCoinsSum = stat ? Number(stat.totalCoinsSum ?? 0) : "—";
  const usersActiveAccount = stat ? Number(stat.usersActiveAccount ?? 0) : "—";
  const usersInactiveAccount = stat ? Number(stat.usersInactiveAccount ?? 0) : "—";
  const usersNeverLoggedIn = stat ? Number(stat.usersNeverLoggedIn ?? 0) : "—";
  const pb = stat && stat.purchasesByStatus && typeof stat.purchasesByStatus === "object" ? stat.purchasesByStatus : {};
  const purPending = stat ? Number(pb.pending ?? 0) : "—";
  const purApproved = stat ? Number(pb.approved ?? 0) : "—";
  const purRejected = stat ? Number(pb.rejected ?? 0) : "—";
  const cb = stat && stat.complaintsByStatus && typeof stat.complaintsByStatus === "object" ? stat.complaintsByStatus : {};
  const complaintsInRange = stat ? Number(stat.complaintsInRange ?? 0) : "—";
  const compPending = stat ? Number(cb.pending ?? 0) : "—";
  const compApproved = stat ? Number(cb.approved ?? 0) : "—";
  const compRejected = stat ? Number(cb.rejected ?? 0) : "—";
  const wheelSpinsCount = stat ? Number(stat.wheelSpinsCount ?? 0) : "—";
  const wheelCoinsAwarded = stat ? Number(stat.wheelCoinsAwarded ?? 0) : "—";
  const generatedAtLabel = stat && stat.generatedAt != null ? formatTs(stat.generatedAt) : "—";
  const clickStats = stat?.clickStats && typeof stat.clickStats === "object" ? stat.clickStats : {};
  const sponsorClickRows = Array.isArray(clickStats.sponsors) ? clickStats.sponsors : [];
  const bonusClickRows = Array.isArray(clickStats.bonuses) ? clickStats.bonuses : [];
  const promoClickRows = Array.isArray(clickStats.promos) ? clickStats.promos : [];

  const series = stat && Array.isArray(stat.series) ? stat.series : [];
  let chartBlock = "";
  if (series.length > 0) {
    const maxVal = Math.max(
      1,
      ...series.map((row) =>
        Math.max(
          Number(row.newUsers || 0),
          Number(row.logins || 0),
          Number(row.active || 0),
          Number(row.purchases || 0),
        ),
      ),
    );
    const bars = series
      .map((row) => {
        const dayStart = Number(row.dayStart || 0);
        const lab = formatDashDayLabel(dayStart);
        const hA = Math.round((Number(row.active || 0) / maxVal) * 100);
        const hL = Math.round((Number(row.logins || 0) / maxVal) * 100);
        const hN = Math.round((Number(row.newUsers || 0) / maxVal) * 100);
        const hP = Math.round((Number(row.purchases || 0) / maxVal) * 100);
        return `<div class="admin-dash-chart-col" title="${escapeHtml(lab)}">
          <div class="admin-dash-chart-bars">
            <span class="admin-dash-bar admin-dash-bar--active" style="height:${hA}%"></span>
            <span class="admin-dash-bar admin-dash-bar--login" style="height:${hL}%"></span>
            <span class="admin-dash-bar admin-dash-bar--new" style="height:${hN}%"></span>
            <span class="admin-dash-bar admin-dash-bar--pur" style="height:${hP}%"></span>
          </div>
          <span class="admin-dash-chart-x">${escapeHtml(lab)}</span>
        </div>`;
      })
      .join("");
    chartBlock = `
      <div class="admin-dash-chart-panel">
        <div class="admin-dash-chart-panel__head">
          <div class="admin-dash-chart-panel__intro">
            <p class="admin-dash-chart-panel__eyebrow">Görselleştirme</p>
            <p class="admin-dash-chart-panel__title">Günlük trend</p>
            <p class="admin-dash-chart-panel__sub">Aktif kullanıcı, giriş, yeni üyelik ve market işlemi — günlük dağılım (en fazla 62 gün).</p>
          </div>
          <div class="admin-dash-legend">
            <span class="admin-dash-legend__item"><span class="admin-dash-legend__dot admin-dash-legend__dot--active" aria-hidden="true"></span>Aktif</span>
            <span class="admin-dash-legend__item"><span class="admin-dash-legend__dot admin-dash-legend__dot--login" aria-hidden="true"></span>Giriş</span>
            <span class="admin-dash-legend__item"><span class="admin-dash-legend__dot admin-dash-legend__dot--new" aria-hidden="true"></span>Yeni üye</span>
            <span class="admin-dash-legend__item"><span class="admin-dash-legend__dot admin-dash-legend__dot--pur" aria-hidden="true"></span>Satın alma</span>
          </div>
        </div>
        <div class="admin-dash-chart-surface">
          <div class="admin-dash-chart">${bars}</div>
        </div>
      </div>`;
  } else if (stat) {
    chartBlock = `
      <div class="admin-dash-chart-panel admin-dash-chart-panel--empty">
        <p class="admin-dash-chart-panel__empty-title">Grafik için veri yok</p>
        <p class="admin-dash-chart-panel__empty-sub">Seçilen dönemde günlük seri oluşturulamadı. Tarih aralığını genişletmeyi deneyin.</p>
      </div>`;
  }

  const detailTableRowsHtml = stat
    ? [
      ["Üretim zamanı (sunucu)", generatedAtLabel],
      ["Aralık — başlangıç", formatTs(state.dashboardFrom)],
      ["Aralık — bitiş", formatTs(state.dashboardTo)],
      ["Toplam üye", totalUsers],
      ["Toplam coin (tüm üyeler)", totalCoinsSum],
      ["Aktif hesap", usersActiveAccount],
      ["Pasif hesap", usersInactiveAccount],
      ["Hiç giriş yapmamış", usersNeverLoggedIn],
      ["Dönemde yeni üye", newU],
      ["Dönemde giriş", logins],
      ["Dönemde aktif (site)", active],
      ["Son 24 saat aktif", last24],
      ["Market işlem adedi", purN],
      ["Market coin hacmi", purCoins],
      ["Market — bekleyen", purPending],
      ["Market — onaylı", purApproved],
      ["Market — red", purRejected],
      ["Şikayet toplam", complaintsInRange],
      ["Şikayet — bekleyen", compPending],
      ["Şikayet — onaylı", compApproved],
      ["Şikayet — red", compRejected],
      ["Çark dönüşü (adet)", wheelSpinsCount],
      ["Çarktan verilen coin", wheelCoinsAwarded],
    ]
      .map(
        ([k, v]) =>
          `<tr><td class="admin-dash-table__k">${escapeHtml(k)}</td><td class="admin-dash-table__v">${dashCell(v)}</td></tr>`,
      )
      .join("")
    : "";

  return `
    <div class="admin-dash-page">
      <header class="admin-dash-hero" aria-labelledby="dash-hero-title">
        <div class="admin-dash-hero__grid">
          <div class="admin-dash-hero__copy">
            <p class="admin-dash-hero__eyebrow" id="dash-hero-eyebrow">Genel bakış</p>
            <h2 class="admin-dash-hero__title" id="dash-hero-title">Kontrol paneli</h2>

          </div>
          <div class="admin-dash-hero__chips" role="list">
            <span class="admin-dash-chip" role="listitem">Özet</span>
            <span class="admin-dash-chip admin-dash-chip--muted" role="listitem">${escapeHtml(String(rangeDays))} gün</span>
          </div>
        </div>
      </header>

      ${dashboardPendingPanelHtml()}

      <section class="admin-dash-toolbar" aria-labelledby="dash-filter-title">
        <div class="admin-dash-toolbar__intro">
          <span class="admin-dash-kicker">Filtre</span>
          <h3 class="admin-dash-toolbar__title" id="dash-filter-title">Tarih aralığı</h3>

        </div>
        <div class="admin-dash-toolbar__body">
          <div class="admin-dash-preset-group" role="group" aria-label="Hızlı tarih">
            <button type="button" data-dash-preset="today" class="${presetClass("today")}">Bugün</button>
            <button type="button" data-dash-preset="yesterday" class="${presetClass("yesterday")}">Dün</button>
            <button type="button" data-dash-preset="7d" class="${presetClass("7d")}">Son 7 gün</button>
            <button type="button" data-dash-preset="month" class="${presetClass("month")}">Bu ay</button>
            <button type="button" data-dash-preset="custom" class="${presetClass("custom")}">Özel</button>
          </div>
          <div class="admin-dash-toolbar__custom">
            <div class="admin-dash-field">
              <label class="admin-dash-field__label" for="dash-date-from">Başlangıç</label>
              <input id="dash-date-from" type="date" class="admin-input admin-dash-field__input" value="${escapeHtml(unixToDateInputLocal(state.dashboardFrom))}" />
            </div>
            <div class="admin-dash-field">
              <label class="admin-dash-field__label" for="dash-date-to">Bitiş</label>
              <input id="dash-date-to" type="date" class="admin-input admin-dash-field__input" value="${escapeHtml(unixToDateInputLocal(state.dashboardTo))}" />
            </div>
            <button type="button" id="dash-apply-custom" class="admin-btn admin-btn-soft admin-dash-apply">Uygula</button>
          </div>
        </div>
        <p class="admin-dash-range-hint">
          <span class="admin-dash-range-hint__label">Uygulanan aralık</span>
          <span class="admin-dash-range-hint__val">${escapeHtml(fromLabel)}</span>
          <span class="admin-dash-range-hint__sep">→</span>
          <span class="admin-dash-range-hint__val">${escapeHtml(toLabel)}</span>
        </p>
      </section>

      ${stat ? `<div class="admin-dash-snapshot" role="status">
        <span class="admin-dash-snapshot__text"><strong>Veri zamanı</strong> · ${escapeHtml(generatedAtLabel)}</span>
      </div>` : ""}

      <div class="admin-dash-section-head">
        <h3 class="admin-dash-section-title">Üyelik &amp; etkileşim</h3>
        <p class="admin-dash-section-desc">Veritabanı ve seçilen dönem içindeki hareket</p>
      </div>
      <div class="admin-dash-kpi-grid admin-dash-kpi-grid--4">
        <div class="admin-dash-stat admin-dash-stat--accent-line admin-dash-stat--tone-sky">
          <div class="admin-dash-stat__inner">
            <div class="admin-dash-kpi-icon-wrap">${dashKpiIcon("users")}</div>
            <div class="admin-dash-stat__body">
              <p class="admin-dash-stat-label">Toplam üye</p>
              <p class="admin-dash-stat-value">${formatDashMetric(totalUsers)}</p>
              <p class="admin-dash-stat-hint">Kayıtlı hesap sayısı</p>
            </div>
          </div>
        </div>
        <div class="admin-dash-stat admin-dash-stat--accent-line admin-dash-stat--tone-violet">
          <div class="admin-dash-stat__inner">
            <div class="admin-dash-kpi-icon-wrap">${dashKpiIcon("userPlus")}</div>
            <div class="admin-dash-stat__body">
              <p class="admin-dash-stat-label">Dönemde yeni kayıt</p>
              <p class="admin-dash-stat-value">${formatDashMetric(newU)}</p>
              <p class="admin-dash-stat-hint">Bu tarihler arasında üye olanlar</p>
            </div>
          </div>
        </div>
        <div class="admin-dash-stat admin-dash-stat--accent-line admin-dash-stat--tone-cyan">
          <div class="admin-dash-stat__inner">
            <div class="admin-dash-kpi-icon-wrap">${dashKpiIcon("logIn")}</div>
            <div class="admin-dash-stat__body">
              <p class="admin-dash-stat-label">Dönemde giriş</p>
              <p class="admin-dash-stat-value">${formatDashMetric(logins)}</p>
              <p class="admin-dash-stat-hint">Son giriş zamanı aralıkta</p>
            </div>
          </div>
        </div>
        <div class="admin-dash-stat admin-dash-stat--accent-line admin-dash-stat--tone-emerald">
          <div class="admin-dash-stat__inner">
            <div class="admin-dash-kpi-icon-wrap">${dashKpiIcon("activity")}</div>
            <div class="admin-dash-stat__body">
              <p class="admin-dash-stat-label">Dönemde aktif (site)</p>
              <p class="admin-dash-stat-value">${formatDashMetric(active)}</p>
              <p class="admin-dash-stat-hint">Son görülme aralıkta</p>
            </div>
          </div>
        </div>
      </div>

      <div class="admin-dash-section-head">
        <h3 class="admin-dash-section-title">Anlık &amp; market</h3>
        <p class="admin-dash-section-desc">Son 24 saat ve coin ekonomisi</p>
      </div>
      <div class="admin-dash-kpi-grid admin-dash-kpi-grid--3">
        <div class="admin-dash-stat admin-dash-stat--highlight">
          <div class="admin-dash-stat__inner">
            <div class="admin-dash-kpi-icon-wrap admin-dash-kpi-icon-wrap--on-dark">${dashKpiIcon("zap")}</div>
            <div class="admin-dash-stat__body">
              <p class="admin-dash-stat-label">Son 24 saat aktif</p>
              <p class="admin-dash-stat-value">${formatDashMetric(last24)}</p>
              <p class="admin-dash-stat-hint">Yakın zamanda sitede görülen üye</p>
            </div>
          </div>
        </div>
        <div class="admin-dash-stat admin-dash-stat--accent-line admin-dash-stat--tone-amber">
          <div class="admin-dash-stat__inner">
            <div class="admin-dash-kpi-icon-wrap">${dashKpiIcon("cart")}</div>
            <div class="admin-dash-stat__body">
              <p class="admin-dash-stat-label">Market işlemi</p>
              <p class="admin-dash-stat-value">${formatDashMetric(purN)}</p>
              <p class="admin-dash-stat-hint">Adet · seçilen dönem</p>
            </div>
          </div>
        </div>
        <div class="admin-dash-stat admin-dash-stat--accent-line admin-dash-stat--tone-amber">
          <div class="admin-dash-stat__inner">
            <div class="admin-dash-kpi-icon-wrap">${dashKpiIcon("coins")}</div>
            <div class="admin-dash-stat__body">
              <p class="admin-dash-stat-label">Coin hacmi</p>
              <p class="admin-dash-stat-value">${formatDashMetric(purCoins)}</p>
              <p class="admin-dash-stat-hint">Harcanan toplam coin</p>
            </div>
          </div>
        </div>
      </div>

      <div class="admin-dash-section-head">
        <h3 class="admin-dash-section-title">Ekonomi &amp; hesap sağlığı</h3>
        <p class="admin-dash-section-desc">Coin havuzu ve hesap durumları (anlık, tarihten bağımsız)</p>
      </div>
      <div class="admin-dash-kpi-grid admin-dash-kpi-grid--4">
        <div class="admin-dash-stat admin-dash-stat--accent-line admin-dash-stat--tone-sky">
          <div class="admin-dash-stat__inner">
            <div class="admin-dash-kpi-icon-wrap">${dashKpiIcon("coins")}</div>
            <div class="admin-dash-stat__body">
              <p class="admin-dash-stat-label">Toplam coin (havuz)</p>
              <p class="admin-dash-stat-value">${formatDashMetric(totalCoinsSum)}</p>
              <p class="admin-dash-stat-hint">Tüm üyelerin cüzdan toplamı</p>
            </div>
          </div>
        </div>
        <div class="admin-dash-stat admin-dash-stat--accent-line admin-dash-stat--tone-emerald">
          <div class="admin-dash-stat__inner">
            <div class="admin-dash-kpi-icon-wrap">${dashKpiIcon("users")}</div>
            <div class="admin-dash-stat__body">
              <p class="admin-dash-stat-label">Aktif hesap</p>
              <p class="admin-dash-stat-value">${formatDashMetric(usersActiveAccount)}</p>
              <p class="admin-dash-stat-hint">Girişe açık üyeler</p>
            </div>
          </div>
        </div>
        <div class="admin-dash-stat admin-dash-stat--accent-line admin-dash-stat--tone-amber">
          <div class="admin-dash-stat__inner">
            <div class="admin-dash-kpi-icon-wrap">${dashKpiIcon("users")}</div>
            <div class="admin-dash-stat__body">
              <p class="admin-dash-stat-label">Pasif hesap</p>
              <p class="admin-dash-stat-value">${formatDashMetric(usersInactiveAccount)}</p>
              <p class="admin-dash-stat-hint">Yönetici tarafından kapatılan</p>
            </div>
          </div>
        </div>
        <div class="admin-dash-stat admin-dash-stat--accent-line admin-dash-stat--tone-violet">
          <div class="admin-dash-stat__inner">
            <div class="admin-dash-kpi-icon-wrap">${dashKpiIcon("logIn")}</div>
            <div class="admin-dash-stat__body">
              <p class="admin-dash-stat-label">Hiç giriş yok</p>
              <p class="admin-dash-stat-value">${formatDashMetric(usersNeverLoggedIn)}</p>
              <p class="admin-dash-stat-hint">Kayıtlı ama oturum açmamış</p>
            </div>
          </div>
        </div>
      </div>

      <div class="admin-dash-section-head">
        <h3 class="admin-dash-section-title">Market talepleri — durum</h3>
        <p class="admin-dash-section-desc">Seçilen dönemde oluşan taleplerin onay dağılımı</p>
      </div>
      <div class="admin-dash-kpi-grid admin-dash-kpi-grid--3">
        <div class="admin-dash-stat admin-dash-stat--accent-line admin-dash-stat--tone-amber">
          <div class="admin-dash-stat__inner">
            <div class="admin-dash-kpi-icon-wrap">${dashKpiIcon("cart")}</div>
            <div class="admin-dash-stat__body">
              <p class="admin-dash-stat-label">Bekleyen</p>
              <p class="admin-dash-stat-value">${formatDashMetric(purPending)}</p>
              <p class="admin-dash-stat-hint">Onay bekleyen talep</p>
            </div>
          </div>
        </div>
        <div class="admin-dash-stat admin-dash-stat--accent-line admin-dash-stat--tone-emerald">
          <div class="admin-dash-stat__inner">
            <div class="admin-dash-kpi-icon-wrap">${dashKpiIcon("cart")}</div>
            <div class="admin-dash-stat__body">
              <p class="admin-dash-stat-label">Onaylı</p>
              <p class="admin-dash-stat-value">${formatDashMetric(purApproved)}</p>
              <p class="admin-dash-stat-hint">Tamamlanan talep</p>
            </div>
          </div>
        </div>
        <div class="admin-dash-stat admin-dash-stat--accent-line admin-dash-stat--tone-rose">
          <div class="admin-dash-stat__inner">
            <div class="admin-dash-kpi-icon-wrap">${dashKpiIcon("cart")}</div>
            <div class="admin-dash-stat__body">
              <p class="admin-dash-stat-label">Reddedilen</p>
              <p class="admin-dash-stat-value">${formatDashMetric(purRejected)}</p>
              <p class="admin-dash-stat-hint">İptal / red</p>
            </div>
          </div>
        </div>
      </div>

      <div class="admin-dash-section-head">
        <h3 class="admin-dash-section-title">Şikayetler &amp; şans çarkı</h3>
        <p class="admin-dash-section-desc">Şikayet akışı ve çark ödülleri (seçilen dönem)</p>
      </div>
      <div class="admin-dash-kpi-grid admin-dash-kpi-grid--4">
        <div class="admin-dash-stat admin-dash-stat--accent-line admin-dash-stat--tone-sky">
          <div class="admin-dash-stat__inner">
            <div class="admin-dash-kpi-icon-wrap">${dashKpiIcon("message")}</div>
            <div class="admin-dash-stat__body">
              <p class="admin-dash-stat-label">Şikayet toplam</p>
              <p class="admin-dash-stat-value">${formatDashMetric(complaintsInRange)}</p>
              <p class="admin-dash-stat-hint">Dönemdeki kayıtlar</p>
            </div>
          </div>
        </div>
        <div class="admin-dash-stat admin-dash-stat--accent-line admin-dash-stat--tone-amber">
          <div class="admin-dash-stat__inner">
            <div class="admin-dash-kpi-icon-wrap">${dashKpiIcon("message")}</div>
            <div class="admin-dash-stat__body">
              <p class="admin-dash-stat-label">Şikayet bekleyen</p>
              <p class="admin-dash-stat-value">${formatDashMetric(compPending)}</p>
              <p class="admin-dash-stat-hint">İnceleme bekliyor</p>
            </div>
          </div>
        </div>
        <div class="admin-dash-stat admin-dash-stat--accent-line admin-dash-stat--tone-emerald">
          <div class="admin-dash-stat__inner">
            <div class="admin-dash-kpi-icon-wrap">${dashKpiIcon("message")}</div>
            <div class="admin-dash-stat__body">
              <p class="admin-dash-stat-label">Şikayet onaylı</p>
              <p class="admin-dash-stat-value">${formatDashMetric(compApproved)}</p>
              <p class="admin-dash-stat-hint">İşlenmiş</p>
            </div>
          </div>
        </div>
        <div class="admin-dash-stat admin-dash-stat--accent-line admin-dash-stat--tone-rose">
          <div class="admin-dash-stat__inner">
            <div class="admin-dash-kpi-icon-wrap">${dashKpiIcon("message")}</div>
            <div class="admin-dash-stat__body">
              <p class="admin-dash-stat-label">Şikayet red</p>
              <p class="admin-dash-stat-value">${formatDashMetric(compRejected)}</p>
              <p class="admin-dash-stat-hint">Kapatılan</p>
            </div>
          </div>
        </div>
      </div>
      <div class="admin-dash-kpi-grid admin-dash-kpi-grid--2">
        <div class="admin-dash-stat admin-dash-stat--accent-line admin-dash-stat--tone-cyan">
          <div class="admin-dash-stat__inner">
            <div class="admin-dash-kpi-icon-wrap">${dashKpiIcon("rotate")}</div>
            <div class="admin-dash-stat__body">
              <p class="admin-dash-stat-label">Çark dönüşü</p>
              <p class="admin-dash-stat-value">${formatDashMetric(wheelSpinsCount)}</p>
              <p class="admin-dash-stat-hint">Dönemdeki spin sayısı</p>
            </div>
          </div>
        </div>
        <div class="admin-dash-stat admin-dash-stat--accent-line admin-dash-stat--tone-violet">
          <div class="admin-dash-stat__inner">
            <div class="admin-dash-kpi-icon-wrap">${dashKpiIcon("coins")}</div>
            <div class="admin-dash-stat__body">
              <p class="admin-dash-stat-label">Çarktan verilen coin</p>
              <p class="admin-dash-stat-value">${formatDashMetric(wheelCoinsAwarded)}</p>
              <p class="admin-dash-stat-hint">Ödül toplamı</p>
            </div>
          </div>
        </div>
      </div>

      ${!stat ? `<div class="admin-dash-alert" role="alert">Özet verisi yüklenemedi veya boş. Üstteki <strong>Yenile</strong> ile tekrar deneyin.</div>` : ""}
      ${chartBlock}

      <div class="admin-dash-section-head mt-8">
        <h3 class="admin-dash-section-title">Dış tıklamalar</h3>
        <p class="admin-dash-section-desc">Sponsor, bonus ve promo &quot;Siteye Git&quot; tıklamaları (seçilen dönem)</p>
      </div>
      <div class="grid gap-5 lg:grid-cols-3">
        <section class="admin-inset p-4">
          <h4 class="text-sm font-semibold text-slate-200">Sponsorlar</h4>
          ${dashClickRankTableHtml(sponsorClickRows)}
        </section>
        <section class="admin-inset p-4">
          <h4 class="text-sm font-semibold text-slate-200">Bonuslar</h4>
          ${dashClickRankTableHtml(bonusClickRows)}
        </section>
        <section class="admin-inset p-4">
          <h4 class="text-sm font-semibold text-slate-200">Promo kodları</h4>
          ${dashClickRankTableHtml(promoClickRows)}
        </section>
      </div>
      ${stat
      ? `<div class="admin-dash-table-panel">
        <div class="admin-dash-table-panel__head">
          <p class="admin-dash-table-panel__eyebrow">Veri dökümü</p>
          <h3 class="admin-dash-table-panel__title">Tüm metrikler</h3>
          <p class="admin-dash-table-panel__sub">Aynı verilerin satır satır listesi — ekran görüntüsü veya kontrol için.</p>
        </div>
        <div class="admin-dash-table-scroll">
          <table class="admin-dash-table">
            <thead>
              <tr><th scope="col">Metrik</th><th scope="col">Değer</th></tr>
            </thead>
            <tbody>${detailTableRowsHtml}</tbody>
          </table>
        </div>
      </div>`
      : ""
    }
    </div>`;
}

async function saveType(type, data) {
  await api("save.php", { method: "POST", body: JSON.stringify({ type, data }) });
}

const CMS_NO_SAVE_TABS = [
  "dashboard",
  "audit",
  "staff",
  "members",
  "market-requests",
  "complaints",
  "affiliate-requests",
  "ip-clashes",
  "promo-codes",
  "telegram-invites",
  "telegram-broadcast",
  "special-odds",
  "tickets",
  "site-events",
  "site-news",
  "site-notifications",
  "my-profile",
  "chat",
  "levels",
];

function cmsPanelSaveBtnHtml() {
  return `<button type="button" class="btn-cms-panel-save admin-btn admin-btn-primary">Değişiklikleri kaydet</button>`;
}

function cmsToolbarAddBtnHtml(id, label) {
  return `<button type="button" id="${escapeHtml(id)}" class="admin-btn admin-btn-soft admin-cms-toolbar__add">${escapeHtml(label)}</button>`;
}

function cmsPanelToolbarHtml(leftHtml = "") {
  const hasStart = Boolean(String(leftHtml || "").trim());
  return `
      <div class="admin-cms-toolbar${hasStart ? "" : " admin-cms-toolbar--end"}">
        ${hasStart ? `<div class="admin-cms-toolbar__cluster">${leftHtml}</div>` : ""}
        ${cmsPanelSaveBtnHtml()}
      </div>`;
}

async function saveActiveCmsTab() {
  if (CMS_NO_SAVE_TABS.includes(state.tab)) return;
  captureCurrentTab();
  try {
    if (state.tab === "sponsors") {
      const data = state.sponsors;
      await saveType("sponsors", data);
      state.sponsors = data;
      toast("Sponsorlar kaydedildi");
    } else if (state.tab === "sponsor-promo-codes") {
      const data = state.sponsorPromoCodes || [];
      await saveType("sponsor_promo_codes", data);
      state.sponsorPromoCodes = data;
      toast("Site promo kodları kaydedildi");
    } else if (state.tab === "bonuses") {
      const data = state.bonuses || [];
      await saveType("bonuses", data);
      state.bonuses = data;
      toast("Site bonusları kaydedildi");
    } else if (state.tab === "market") {
      const data = state.market;
      await saveType("market", data);
      state.market = data;
      toast("Market kaydedildi");
    } else if (state.tab === "social") {
      const data = readSocialFromDom();
      await saveType("social", data);
      state.social = data;
      toast("Sosyal linkler kaydedildi");
    } else if (state.tab === "games-covers") {
      const covers = {
        blackjack: String($("#games-cover-url-blackjack")?.value || "").trim(),
        slot: String($("#games-cover-url-slot")?.value || "").trim(),
        roulette: String($("#games-cover-url-roulette")?.value || "").trim(),
      };
      const winChancePercent = readGamesWinChanceFromDom();
      const res = await api("admin_games.php", {
        method: "POST",
        body: JSON.stringify({
          covers,
          settings: { winChancePercent },
        }),
      });
      if (res && res.covers) {
        state.gamesCovers = res.covers;
      }
      if (res && res.settings) {
        state.gamesSettings = res.settings;
      }
      toast("Oyun ayarları kaydedildi");
    } else if (state.tab === "wheel") {
      const hEl = $("#wheel-cooldown-hours");
      const h = Math.max(1, Math.min(168, parseInt(hEl?.value || "24", 10) || 24));
      const prizes = [0, 1, 2, 3, 4, 5].map((i) => {
        const raw = parseInt($(`#wheel-prize-${i}`)?.value || "0", 10);
        const v = Number.isFinite(raw) ? raw : 0;
        return Math.max(0, Math.min(99999999, v));
      });
      await api("admin_wheel.php", {
        method: "POST",
        body: JSON.stringify({
          cooldown_hours: h,
          prizes,
          enabled: state.wheelConfig?.enabled !== false,
        }),
      });
      await loadWheelConfig();
      toast("Şans çarkı ayarları kaydedildi");
    } else if (state.tab === "css") {
      const ccss = $("#site-custom-css");
      const base = normalizeSiteForAdmin(state.site || {});
      const data = {
        ...base,
        customCss: ccss ? String(ccss.value || "") : "",
      };
      await saveType("site", data);
      state.site = data;
      toast("Özel CSS kaydedildi");
    } else if (state.tab === "popup") {
      readPopupFromDomIfVisible();
      const data = state.popup;
      if (data.enabled) {
        const iu = String(data.imageUrl || "").trim();
        if (!iu || !/^https?:\/\//i.test(iu)) {
          toast("Popup açıkken geçerli bir görsel URL (https://...) girin.", "err");
          return;
        }
      }
      await saveType("popup", data);
      state.popup = data;
      toast("Popup ayarları kaydedildi");
    } else if (state.tab === "legal-pages") {
      readLegalPagesFromDomIfVisible();
      const data = normalizeLegalPagesForAdmin(state.legalPages || {});
      await saveType("legal_pages", data);
      state.legalPages = data;
      toast("Yasal sayfalar kaydedildi");
    } else if (state.tab === "gamdom-turnuva") {
      readGamdomTurnuvaFromDomIfVisible();
      const data = state.gamdomTurnuva;
      try {
        if ($("#gamdom-entries-json")?.value?.trim()) {
          const parsed = JSON.parse($("#gamdom-entries-json").value);
          if (!Array.isArray(parsed)) throw new Error("Sıralama JSON bir dizi olmalı");
        }
      } catch (err) {
        toast(err?.message || "Sıralama JSON geçersiz", "err");
        return;
      }
      await saveType("gamdom_turnuva", data);
      state.gamdomTurnuva = data;
      toast("Gamdom turnuva ayarları kaydedildi");
    } else if (state.tab === "site" || state.tab === "maintenance") {
      readSiteFromDomIfVisible();
      const data = normalizeSiteForAdmin(state.site || {});
      await saveType("site", data);
      state.site = data;
      toast(state.tab === "maintenance" ? "Bakım modu ayarları kaydedildi" : "Site ayarları kaydedildi");
    }
  } catch (e) {
    toast(e.message || "Kayıt başarısız", "err");
  }
}

function renderLogin() {
  const keepTwofaRequired = state.loginTwofaRequired === true;
  const keepTwofaChallenge = state.loginTwofaChallenge;
  ensureAdminNoFavicon();
  resetAdminAuthState();
  state.loginTwofaRequired = keepTwofaRequired;
  state.loginTwofaChallenge = keepTwofaChallenge;
  const gateHint = state.adminGateRequired
    ? `<p class="admin-notice">Panel gizli anahtar ile korunuyor. Önce <code class="admin-code text-xs">admin.php?k=ANAHTAR</code> adresini açın; ardından buradan giriş yapın.</p>`
    : "";
  const twofaBlock = state.loginTwofaRequired
    ? `<div>
            <label class="admin-label" for="admin-login-2fa">Authenticator kodu</label>
            <input id="admin-login-2fa" type="text" name="twofaCode" inputmode="numeric" pattern="[0-9\\- ]*" class="admin-input" autocomplete="one-time-code" required placeholder="123456 veya 1234-5678" />
            <p class="mt-2 text-xs text-slate-500">Google Authenticator kodu veya yedek kod gir.</p>
          </div>`
    : "";
  const passwordBlock = state.loginTwofaRequired
    ? ""
    : `<div>
            <label class="admin-label" for="admin-login-pw">Şifre</label>
            <input id="admin-login-pw" type="password" name="password" class="admin-input" autocomplete="current-password" required placeholder="••••••••" />
          </div>`;
  $("#app").innerHTML = `
    <div class="admin-login-wrap">
      <div class="admin-login-card">
        <div class="admin-login-icon" aria-hidden="true">
          <svg class="h-7 w-7" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.75" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"/></svg>
        </div>
        <div class="text-center">
          <p class="admin-login-eyebrow">Mocker</p>
          <h1 class="admin-login-title">Yönetim konsolu</h1>
          <p class="mt-2 text-sm text-slate-500">Devam etmek için yönetici şifresini gir.</p>
        </div>
        ${gateHint}
        <form id="login-form" class="mt-8 space-y-5">
          <div>
            <label class="admin-label" for="admin-login-user">Personel kullanıcı adı (opsiyonel)</label>
            <input id="admin-login-user" type="text" name="username" class="admin-input" autocomplete="username" placeholder="Boş = ana yönetici şifresi" />
          </div>
          ${passwordBlock}
          ${twofaBlock}
          <button type="submit" class="admin-btn admin-btn-primary w-full py-3 text-[15px]">
            ${state.loginTwofaRequired ? "Kodu doğrula" : "Güvenli giriş"}
          </button>
        </form>
        <p class="admin-login-footer">
          ${adminThemeToggleHtml()}
          <a href="/#/">← Ana siteye dön</a>
        </p>
      </div>
    </div>`;

  document.title = "Mocker — Giriş";

  bindAdminThemeToggle($("#app"));
  if (state.loginTwofaRequired) $("#admin-login-2fa")?.focus();
  else $("#admin-login-pw")?.focus();

  $("#login-form").addEventListener("submit", async (e) => {
    e.preventDefault();
    const fd = new FormData(e.target);
    const username = String(fd.get("username") || "").trim();
    const password = fd.get("password");
    const twofaCode = String(fd.get("twofaCode") || "").trim();
    const payload = state.loginTwofaRequired
      ? { username, password, twofaCode, twofaChallenge: state.loginTwofaChallenge || "" }
      : { username, password };
    try {
      const res = await api("login.php", { method: "POST", body: JSON.stringify(payload) });
      if (res?.requires2fa && typeof res.twofaChallenge === "string") {
        state.loginTwofaRequired = true;
        state.loginTwofaChallenge = res.twofaChallenge;
        renderLogin();
        toast("Authenticator kodunu gir", "ok");
        return;
      }
      state.loginTwofaRequired = false;
      state.loginTwofaChallenge = null;
      toast("Giriş başarılı");
      await main();
    } catch (err) {
      if (err.body?.error === "not_found") {
        state.adminGateRequired = true;
        renderLogin();
        toast("Önce admin.php?k=ANAHTAR ile panel erişimini açın", "err");
      } else if (err.body?.error === "setup_required") {
        toast("Önce setup.php ile yapılandırın", "err");
      } else if (err.body?.error === "csrf") {
        toast("Oturum süresi doldu; sayfayı yenileyip tekrar deneyin", "err");
      } else if (
        err.body?.error === "twofa_invalid" ||
        err.body?.error === "twofa_invalid_challenge" ||
        err.body?.error === "twofa_missing_challenge" ||
        err.body?.error === "twofa_expired"
      ) {
        if (err.body?.error === "twofa_expired") {
          state.loginTwofaRequired = false;
          state.loginTwofaChallenge = null;
          renderLogin();
          toast("2FA süresi doldu, tekrar giriş yap", "err");
        } else {
          toast("2FA kodu hatalı", "err");
        }
      } else {
        toast("Şifre hatalı", "err");
      }
    }
  });
}

function sponsorPreviewLine(s) {
  if (Array.isArray(s.promo) && s.promo[0]) return String(s.promo[0]);
  const d = (s.description || "").trim();
  return d.length > 90 ? `${d.slice(0, 90)}…` : d || "—";
}

function normalizeSponsorBadgeAdmin(badge) {
  if (badge == null || badge === "") return "ana-sponsorlar";
  const s = String(badge).trim();
  if (s === "ana-sponsorlar") return s;
  if (s === "oynadigim" || s === "guvenilir" || s === "Önerilen" || s === "SPONSORLAR" || s === "Güvenilir") {
    return "ana-sponsorlar";
  }
  return "ana-sponsorlar";
}

function sponsorsPanelHtml() {
  const list = state.sponsors || [];
  const cards = list.map((s, i) => sponsorPreviewCardHtml(s, i)).join("");
  return `
    <div class="admin-cms-page">
      <header class="admin-data-page__head">
        <h2 class="admin-data-page__title">Sponsor kartları</h2>

      </header>
      ${cmsPanelToolbarHtml(`<span class="admin-data-count"><strong>${list.length}</strong> kart</span>${cmsToolbarAddBtnHtml("btn-add-sponsor", "Kart ekle")}`)}
      <div id="sponsor-list" class="admin-cms-grid">
        ${cards || `<p class="admin-cms-empty admin-empty admin-empty--muted">Henüz sponsor kartı yok. <strong>Kart ekle</strong> ile başlayın.</p>`}
      </div>
    </div>`;
}

function marketPanelHtml() {
  const list = state.market || [];
  const cards = list.map((m, i) => marketPreviewCardHtml(m, i)).join("");
  return `
    <div class="admin-cms-page">
      <header class="admin-data-page__head">
        <h2 class="admin-data-page__title">Market ürünleri</h2>

      </header>
      ${cmsPanelToolbarHtml(`<span class="admin-data-count"><strong>${list.length}</strong> ürün</span>${cmsToolbarAddBtnHtml("btn-add-market", "Ürün ekle")}`)}
      <div id="market-list" class="admin-cms-grid">
        ${cards || `<p class="admin-cms-empty admin-empty admin-empty--muted">Henüz market ürünü yok. <strong>Ürün ekle</strong> ile başlayın.</p>`}
      </div>
    </div>`;
}

function sponsorBadgePreview(s) {
  return `<span class="admin-badge admin-badge--info">Ana Sponsorlar</span>`;
}

function sponsorPreviewCardHtml(s, i) {
  const logoBlock = adminCmsLogoHtml(s.logo, s.title, "sp-logo-fb");
  return `
    <article class="admin-cms-card sponsor-preview-card" data-id="${escapeHtml(s.id)}">
      <div class="admin-cms-card__top">
        <button type="button" class="admin-cms-card__drag drag-handle" title="Sırayı değiştir" aria-label="Sürükle">${ADMIN_CMS_DRAG_ICON}</button>
        ${logoBlock}
        <div class="admin-cms-card__content">
          <span class="admin-cms-card__eyebrow">Kart ${i + 1}</span>
          <div class="admin-cms-card__title-row">
            <h3 class="admin-cms-card__title">${escapeHtml(s.title)}</h3>
            ${sponsorBadgePreview(s)}
          </div>
          <p class="admin-cms-card__desc">${escapeHtml(sponsorPreviewLine(s))}</p>
        </div>
      </div>
      <div class="admin-cms-card__actions">
        <button type="button" class="btn-edit-sponsor admin-btn admin-btn-primary admin-cms-card__btn-main">${ADMIN_CMS_EDIT_ICON} Düzenle</button>
        <button type="button" class="del-sponsor admin-action-btn admin-action-btn--reject">Sil</button>
      </div>
    </article>`;
}

function sponsorModalHtml(s) {
  const promoText = Array.isArray(s.promo) ? s.promo.join("\n") : "";
  const isNew = String(s.title || "").trim() === "Yeni sponsor";
  const modalTitle = isNew ? "Yeni kart ekle" : "Kartı düzenle";
  const body = `
      <section class="admin-cms-modal__section">
        <h3 class="admin-cms-modal__section-title">Temel bilgiler</h3>
        <div class="admin-cms-modal__fields">
          <div class="admin-cms-modal__field">
            <label class="admin-label" for="modal-fld-title">Başlık</label>
            <input id="modal-fld-title" class="admin-input" value="${escapeHtml(s.title)}" autocomplete="off" />
          </div>
          <div class="admin-cms-modal__field">
            <label class="admin-label" for="modal-fld-logo">Logo — CDN URL</label>
            <div class="admin-cms-modal__logo-row">
              <div class="admin-cms-modal__logo-preview" id="sponsor-modal-logo-preview" hidden>
                <img id="sponsor-modal-logo-img" alt="" loading="lazy" decoding="async" />
              </div>
              <input id="modal-fld-logo" class="admin-input" value="${escapeHtml(s.logo)}" placeholder="https://example.com/logo.png" autocomplete="off" />
            </div>
            <p class="admin-cms-modal__hint">Tam <code>https://</code> ile başlamalı.</p>
          </div>
          <div class="admin-cms-modal__field">
            <label class="admin-label" for="modal-fld-url">Site linki</label>
            <input id="modal-fld-url" class="admin-input" value="${escapeHtml(s.url)}" autocomplete="off" />
          </div>
        </div>
      </section>
      <section class="admin-cms-modal__section">
        <h3 class="admin-cms-modal__section-title">Görünüm &amp; içerik</h3>
        <div class="admin-cms-modal__fields">
          <input type="hidden" id="modal-fld-badge" value="ana-sponsorlar" />
          <div class="admin-cms-modal__field">
            <label class="admin-label" for="modal-fld-desc">Kısa açıklama</label>
            <textarea id="modal-fld-desc" class="admin-input admin-cms-modal__textarea" rows="2">${escapeHtml(s.description || "")}</textarea>
          </div>
          <div class="admin-cms-modal__field">
            <label class="admin-label" for="modal-fld-promo">Promo satırları (satır satır)</label>
            <textarea id="modal-fld-promo" class="admin-input admin-cms-modal__textarea admin-cms-modal__textarea--mono" rows="5">${escapeHtml(promoText)}</textarea>
            <p class="admin-cms-modal__hint">Her satır kartta ayrı promo maddesi olarak görünür.</p>
          </div>
        </div>
      </section>`;
  return adminCmsModalShell({
    backdropId: "sponsor-edit-backdrop",
    closeAttr: "data-close-modal",
    titleId: "sponsor-modal-title",
    eyebrow: "Sponsor kartı",
    title: modalTitle,
    lead: "Ana sitedeki sponsor kartı bu bilgilerle oluşturulur. Sitede yayınlamak için sekmedeki <strong>Değişiklikleri kaydet</strong> gerekir.",
    bodyHtml: body,
    saveId: "sponsor-modal-save",
    cancelId: "sponsor-modal-cancel",
  });
}

function marketPreviewLine(m) {
  const d = (m.description || "").trim();
  return d.length > 120 ? `${d.slice(0, 120)}…` : d || "—";
}

function marketPreviewCardHtml(m, i) {
  const price = Number(m.price) || 0;
  const stock = Math.max(0, parseInt(String(m.stock ?? 0), 10) || 0);
  const logoBlock = adminCmsLogoHtml(m.logo, m.title, "mk-logo-fb", "Görsel yok");
  return `
    <article class="admin-cms-card market-preview-card" data-id="${escapeHtml(m.id)}">
      <div class="admin-cms-card__top">
        <button type="button" class="admin-cms-card__drag drag-handle-market" title="Sırayı değiştir" aria-label="Sürükle">${ADMIN_CMS_DRAG_ICON}</button>
        ${logoBlock}
        <div class="admin-cms-card__content">
          <span class="admin-cms-card__eyebrow">Ürün ${i + 1}</span>
          <h3 class="admin-cms-card__title">${escapeHtml(m.title)}</h3>
          <p class="admin-cms-card__code">${escapeHtml(String(m.id))}</p>
          <p class="admin-cms-card__desc">${escapeHtml(marketPreviewLine(m))}</p>
        </div>
      </div>
      <p class="admin-cms-card__price">${price} coin · Stok: ${stock}</p>
      <div class="admin-cms-card__actions">
        <button type="button" class="btn-edit-market admin-btn admin-btn-primary admin-cms-card__btn-main">${ADMIN_CMS_EDIT_ICON} Düzenle</button>
        <button type="button" class="del-market admin-action-btn admin-action-btn--reject">Sil</button>
      </div>
    </article>`;
}

function marketModalHtml(m) {
  const isNew = String(m.title || "").trim() === "Yeni ürün";
  const modalTitle = isNew ? "Yeni ürün ekle" : "Ürünü düzenle";
  const body = `
      <section class="admin-cms-modal__section">
        <h3 class="admin-cms-modal__section-title">Ürün bilgisi</h3>
        <div class="admin-cms-modal__fields admin-cms-modal__fields--2col">
          <div class="admin-cms-modal__field">
            <label class="admin-label" for="market-modal-fld-id">Ürün kodu (id)</label>
            <input id="market-modal-fld-id" class="admin-input font-mono text-sm" value="${escapeHtml(String(m.id))}" autocomplete="off" />
            <p class="admin-cms-modal__hint">Satın alma kayıtlarında kullanılır; mümkünse değiştirmeyin.</p>
          </div>
          <div class="admin-cms-modal__field">
            <label class="admin-label" for="market-modal-fld-price">Coin fiyatı</label>
            <input id="market-modal-fld-price" type="number" min="1" step="1" class="admin-input tabular-nums" value="${escapeHtml(String(Number(m.price) || 0))}" />
          </div>
          <div class="admin-cms-modal__field">
            <label class="admin-label" for="market-modal-fld-stock">Stok</label>
            <input id="market-modal-fld-stock" type="number" min="0" step="1" class="admin-input tabular-nums" value="${escapeHtml(String(Math.max(0, parseInt(String(m.stock ?? 0), 10) || 0)))}" />
            <p class="admin-cms-modal__hint">Satın alma anında düşer (onay/red fark etmez). 0 = tükendi.</p>
          </div>
          <div class="admin-cms-modal__field admin-cms-modal__field--full">
            <label class="admin-label" for="market-modal-fld-title">Başlık</label>
            <input id="market-modal-fld-title" class="admin-input" value="${escapeHtml(m.title)}" autocomplete="off" />
          </div>
        </div>
      </section>
      <section class="admin-cms-modal__section">
        <h3 class="admin-cms-modal__section-title">Görsel &amp; açıklama</h3>
        <div class="admin-cms-modal__fields">
          <div class="admin-cms-modal__field">
            <label class="admin-label" for="market-modal-fld-logo">Görsel — CDN / resim URL</label>
            <div class="admin-cms-modal__logo-row">
              <div class="admin-cms-modal__logo-preview" id="market-modal-logo-preview" hidden>
                <img id="market-modal-logo-img" alt="" loading="lazy" decoding="async" />
              </div>
              <input id="market-modal-fld-logo" class="admin-input" value="${escapeHtml(m.logo || "")}" placeholder="https://example.com/logo.png" autocomplete="off" />
            </div>
            <p class="admin-cms-modal__hint">Boş bırakılabilir. Tam <code>https://</code> ile başlamalı.</p>
          </div>
          <div class="admin-cms-modal__field">
            <label class="admin-label" for="market-modal-fld-desc">Açıklama</label>
            <textarea id="market-modal-fld-desc" class="admin-input admin-cms-modal__textarea" rows="5">${escapeHtml(m.description || "")}</textarea>
          </div>
        </div>
      </section>`;
  return adminCmsModalShell({
    backdropId: "market-edit-backdrop",
    closeAttr: "data-close-market-modal",
    titleId: "market-modal-title",
    eyebrow: "Market ürünü",
    title: modalTitle,
    lead: "Oyuncular bu ürünü coin ile satın alır. Sitede yayınlamak için sekmedeki <strong>Değişiklikleri kaydet</strong> gerekir.",
    bodyHtml: body,
    saveId: "market-modal-save",
    cancelId: "market-modal-cancel",
  });
}

function passwordModalHtml(u) {
  return `
    <div id="password-edit-backdrop" class="fixed inset-0 z-[60] flex items-end justify-center p-4 sm:items-center" role="dialog" aria-modal="true" aria-labelledby="password-modal-title">
      <div class="admin-modal-backdrop absolute inset-0" data-close-password-modal></div>
      <div class="admin-glass relative z-10 w-full max-w-md rounded-2xl border border-white/[0.1] p-6 shadow-2xl">
        <div class="flex items-start justify-between gap-4 admin-table-row pb-4">
          <div>
            <p class="text-[10px] font-semibold uppercase tracking-[0.2em] text-violet-400">Şifre sıfırlama</p>
            <h2 id="password-modal-title" class="mt-1 text-lg admin-section__title font-bold">Yeni şifre belirle</h2>
            <p class="mt-1 text-sm text-slate-400">${escapeHtml(String(u.username))} <span class="text-slate-500">· ID ${u.id}</span></p>
          </div>
          <button type="button" class="admin-icon-btn-close" data-close-password-modal aria-label="Kapat">
            <svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/></svg>
          </button>
        </div>
        <p id="pw-modal-err" class="mt-4 text-sm text-red-300" role="alert"></p>
        <div class="mt-4 space-y-4">
          <div>
            <label class="admin-label" for="pw-modal-new">Yeni şifre</label>
            <input id="pw-modal-new" type="password" autocomplete="new-password" class="admin-input" placeholder="En az 8 karakter" />
          </div>
          <div>
            <label class="admin-label" for="pw-modal-confirm">Şifre tekrar</label>
            <input id="pw-modal-confirm" type="password" autocomplete="new-password" class="admin-input" placeholder="Tekrar gir" />
          </div>
        </div>
        <div class="mt-6 flex flex-wrap gap-2 border-t border-white/[0.06] pt-5">
          <button type="button" id="password-modal-save" class="flex-1 rounded-xl bg-violet-600 py-3 admin-section__title text-sm shadow-lg shadow-violet-500/25 hover:bg-violet-500">Kaydet</button>
          <button type="button" id="password-modal-cancel" class="rounded-xl border border-white/10 px-5 py-3 text-sm font-medium text-slate-400 hover:bg-white/5">İptal</button>
        </div>
      </div>
    </div>`;
}

const MEMBER_CRYPTO_ROWS = [
  ["BTC", "btc"],
  ["ETH", "eth"],
  ["USDT ERC-20", "usdtErc20"],
  ["USDT TRC-20", "usdtTrc20"],
  ["LTC", "ltc"],
  ["TRX", "trx"],
  ["DOGE", "doge"],
  ["Diğer", "other"],
];

function memberProfileCryptoCell(crypto, key) {
  const c = crypto || {};
  const v = c[key];
  const s = v != null && String(v).trim() !== "" ? String(v).trim() : "";
  return s ? `<span class="break-all font-mono text-xs text-slate-200">${escapeHtml(s)}</span>` : `<span class="text-slate-600">—</span>`;
}

function wheelPanelHtml() {
  const w = state.wheelConfig || { cooldown_hours: 24, prizes: [10, 25, 50, 75, 100, 0], enabled: true };
  const p = w.prizes || [10, 25, 50, 75, 100, 0];
  const labels = ["Dilim 1", "Dilim 2", "Dilim 3", "Dilim 4", "Dilim 5", "Dilim 6 (Pas = 0)"];
  const wheelOn = w.enabled !== false;
  const prizeInputs = [0, 1, 2, 3, 4, 5]
    .map((i) => {
      const val = p[i] != null ? Number(p[i]) : i === 5 ? 0 : 10;
      const v = Number.isFinite(val) && val >= 0 ? Math.min(99999999, Math.floor(val)) : 0;
      return `<div>
            <p class="mb-1 text-[10px] font-semibold uppercase text-slate-500">${labels[i]}</p>
            <input id="wheel-prize-${i}" type="number" min="0" max="99999999" step="1" class="admin-input tabular-nums" value="${escapeHtml(String(v))}" />
          </div>`;
    })
    .join("");
  return `
    <div class="admin-glass max-w-xl rounded-2xl p-6 space-y-6">
      <div class="rounded-xl border border-white/10 bg-white/[0.04] p-4">
        <p class="admin-label">Çark durumu</p>
        <p class="mt-1 text-sm text-slate-300">
          Site şu an: <strong class="text-slate-100">${wheelOn ? "Aktif" : "Pasif"}</strong>
        </p>
        <p class="mt-2 text-xs text-slate-500">
          Pasifken çark sayfasında ziyaretçilere bilgilendirme metni gösterilir; çevirme yapılamaz.
        </p>
        <div class="mt-4 flex flex-wrap gap-2">
          ${wheelOn
      ? `<button type="button" id="wheel-disable-btn" class="admin-btn admin-btn-soft px-4 py-2 text-sm">Çarkı pasife al</button>`
      : `<button type="button" id="wheel-enable-btn" class="admin-btn admin-btn-primary px-4 py-2 text-sm">Çarkı aktifleştir</button>`
    }
        </div>
      </div>
      <div>
        <label class="admin-label">Bekleme süresi (saat)</label>
        <p class="mt-1 text-xs text-slate-500">Üyeler iki çevirme arasında bu kadar bekler (1–168).</p>
        <input id="wheel-cooldown-hours" type="number" min="1" max="168" step="1" class="admin-input mt-2 w-36 tabular-nums" value="${escapeHtml(String(w.cooldown_hours))}" />
      </div>
      <div>
        <label class="admin-label">Altı dilim ödülü (Mocker Coin)</label>
        <p class="mt-1 text-xs text-slate-500">Eşit şans; <strong class="text-slate-400">0</strong> = Pas (coin eklenmez).</p>
        <div class="mt-3 grid grid-cols-1 gap-4 sm:grid-cols-3">
          ${prizeInputs}
        </div>
      </div>
      <div class="flex flex-wrap items-center justify-between gap-3 pt-2 border-t border-white/10">
        <p class="text-xs text-slate-500">Kayıt: <code class="text-slate-400">data/wheel.json</code></p>
        ${cmsPanelSaveBtnHtml()}
      </div>
    </div>`;
}

function promoPanelHtml() {
  const rows = (state.promoCodes || [])
    .map((c) => {
      const maxStr = c.maxUses == null ? "∞" : `${c.usesCount}/${c.maxUses}`;
      const expStr = c.expiresAt == null ? "—" : formatTs(c.expiresAt);
      const activeBadge = c.isActive
        ? `<span class="text-emerald-400">Aktif</span>`
        : `<span class="text-slate-500">Pasif</span>`;
      return `<tr class="admin-table-row">
        <td class="px-3 py-2 font-mono text-sm">${escapeHtml(String(c.code))}</td>
        <td class="px-3 py-2 tabular-nums">${escapeHtml(String(c.coins))}</td>
        <td class="px-3 py-2 tabular-nums">${escapeHtml(maxStr)}</td>
        <td class="px-3 py-2 text-xs text-slate-400">${escapeHtml(expStr)}</td>
        <td class="px-3 py-2">${activeBadge}</td>
        <td class="px-3 py-2 text-right whitespace-nowrap">
          <button type="button" class="admin-btn admin-btn-soft px-2 py-1 text-xs" data-promo-toggle="${c.id}" data-promo-active="${c.isActive ? "1" : "0"}">${c.isActive ? "Pasifleştir" : "Aktifleştir"}</button>
          <button type="button" class="admin-action-btn admin-action-btn--reject px-2 py-1 text-xs" data-promo-delete="${c.id}">Sil</button>
        </td>
      </tr>`;
    })
    .join("");
  return `
    <div class="max-w-5xl space-y-6">
      <div class="admin-panel admin-panel--padded">
        <h3 class="admin-section__title text-lg">Yeni promo kod</h3>
        <p class="mt-1 text-sm text-slate-500">Üyeler profilden kodu girer; tanımladığın kadar coin eklenir. Aynı kodu her üye yalnızca bir kez kullanabilir.</p>
        <div class="mt-4 grid gap-4 sm:grid-cols-2">
          <div class="sm:col-span-2">
            <label class="admin-label" for="promo-new-code">Kod (4–64 karakter)</label>
            <input id="promo-new-code" class="admin-input font-mono uppercase" placeholder="ORNEK2026" maxlength="64" autocomplete="off" />
          </div>
          <div>
            <label class="admin-label" for="promo-new-coins">Verilecek coin</label>
            <input id="promo-new-coins" type="number" min="1" max="10000000" step="1" class="admin-input tabular-nums" value="100" />
          </div>
          <div>
            <label class="admin-label" for="promo-new-max">Toplam kullanım limiti (boş = sınırsız)</label>
            <input id="promo-new-max" type="number" min="1" step="1" class="admin-input tabular-nums" placeholder="Örn. 500" />
          </div>
          <div class="sm:col-span-2">
            <label class="admin-label" for="promo-new-expires">Bitiş tarihi (isteğe bağlı)</label>
            <input id="promo-new-expires" type="datetime-local" class="admin-input max-w-md" />
          </div>
        </div>
        <div class="mt-4">
          <button type="button" id="btn-promo-create" class="admin-btn admin-btn-primary px-5 py-2.5">Kodu oluştur</button>
        </div>
      </div>
      <div class="admin-panel admin-panel--padded">
        <div class="flex flex-wrap items-center justify-between gap-3">
          <h3 class="admin-section__title text-lg">Kayıtlı kodlar</h3>
          ${(state.promoCodes || []).length ? `<p class="text-xs text-slate-500">Önceki siteden kalan kodları üstte <strong class="text-slate-400">Tüm kodları sil</strong> ile temizleyebilirsin.</p>` : ""}
        </div>
        <div class="mt-4 overflow-x-auto">
          <table class="w-full min-w-[640px] text-left text-sm">
            <thead>
              <tr class="admin-table-row text-slate-500">
                <th class="px-3 py-2 font-semibold">Kod</th>
                <th class="px-3 py-2 font-semibold">Coin</th>
                <th class="px-3 py-2 font-semibold">Kullanım</th>
                <th class="px-3 py-2 font-semibold">Bitiş</th>
                <th class="px-3 py-2 font-semibold">Durum</th>
                <th class="px-3 py-2 font-semibold text-right">İşlem</th>
              </tr>
            </thead>
            <tbody>${rows ||
    `<tr><td colspan="6" class="px-3 py-8 text-center text-slate-500">Henüz kod yok.</td></tr>`
    }</tbody>
          </table>
        </div>
      </div>
    </div>`;
}

function sponsorPromoPreviewLine(row) {
  const code = String(row.code || "").trim();
  const label = String(row.label || "").trim();
  if (label && code) return `${label}: ${code}`;
  return code || label || "—";
}

function sponsorPromoPreviewCardHtml(row, i) {
  const active = row.isActive !== false;
  const expStr =
    row.expiresAt == null || row.expiresAt === ""
      ? "Süresiz"
      : formatTs(row.expiresAt);
  const statusBadge = active
    ? `<span class="admin-badge admin-badge--active">Yayında</span>`
    : `<span class="admin-badge admin-badge--inactive">Gizli</span>`;
  return `
    <article class="admin-cms-card admin-cms-card--promo sponsor-preview-card" data-spc-id="${escapeHtml(row.id)}">
      <div class="admin-cms-card__top">
        <div class="admin-cms-card__content">
          <span class="admin-cms-card__eyebrow">Kayıt ${i + 1}</span>
          <h3 class="admin-cms-card__title">${escapeHtml(row.sponsorTitle || "—")}</h3>
          <p class="admin-cms-card__code">${escapeHtml(String(row.code || ""))}</p>
          <p class="admin-cms-card__desc">${escapeHtml(sponsorPromoPreviewLine(row))}</p>
          <div class="admin-cms-card__status-row">
            <span class="admin-cms-card__meta">Bitiş: ${escapeHtml(expStr)}</span>
            ${statusBadge}
          </div>
        </div>
      </div>
      <div class="admin-cms-card__actions">
        <button type="button" class="btn-edit-spc admin-btn admin-btn-primary admin-cms-card__btn-main">${ADMIN_CMS_EDIT_ICON} Düzenle</button>
        <button type="button" class="del-spc admin-action-btn admin-action-btn--reject">Sil</button>
      </div>
    </article>`;
}

function sponsorPromoCodeModalHtml(row) {
  const sponsors = state.sponsors || [];
  const sponsorOpts = sponsors
    .map((s) => {
      const sel = s.id === row.sponsorId ? " selected" : "";
      return `<option value="${escapeHtml(s.id)}"${sel}>${escapeHtml(s.title)}</option>`;
    })
    .join("");
  const expLocal = unixToDatetimeLocal(row.expiresAt);
  const activeChecked = row.isActive !== false ? " checked" : "";
  const isNew = !String(row.code || "").trim();
  const modalTitle = isNew ? "Yeni promosyon kodu" : "Promosyon kodunu düzenle";
  const body = `
      <section class="admin-cms-modal__section">
        <h3 class="admin-cms-modal__section-title">Site &amp; kod</h3>
        <div class="admin-cms-modal__fields">
          <div class="admin-cms-modal__field">
            <label class="admin-label" for="spc-modal-sponsor">Site (sponsor)</label>
            <select id="spc-modal-sponsor" class="admin-input">
              <option value="">— Sponsor seç —</option>
              ${sponsorOpts}
            </select>
            <p class="admin-cms-modal__hint">Önce <strong>Sponsor kartları</strong> sekmesinde site tanımlı olmalı.</p>
          </div>
          <div class="admin-cms-modal__field">
            <label class="admin-label" for="spc-modal-code">Promo kod</label>
            <input id="spc-modal-code" class="admin-input font-mono uppercase" value="${escapeHtml(String(row.code || ""))}" maxlength="128" autocomplete="off" />
          </div>
        </div>
      </section>
      <section class="admin-cms-modal__section">
        <h3 class="admin-cms-modal__section-title">Kampanya metni</h3>
        <div class="admin-cms-modal__fields">
          <div class="admin-cms-modal__field">
            <label class="admin-label" for="spc-modal-label">Kampanya başlığı (isteğe bağlı)</label>
            <input id="spc-modal-label" class="admin-input" value="${escapeHtml(String(row.label || ""))}" placeholder="Örn. Hoşgeldin bonusu" autocomplete="off" />
          </div>
          <div class="admin-cms-modal__field">
            <label class="admin-label" for="spc-modal-desc">Açıklama (isteğe bağlı)</label>
            <textarea id="spc-modal-desc" class="admin-input admin-cms-modal__textarea" rows="3" placeholder="Üyelere görünecek kısa bilgi">${escapeHtml(String(row.description || ""))}</textarea>
          </div>
        </div>
      </section>
      <section class="admin-cms-modal__section">
        <h3 class="admin-cms-modal__section-title">Yayın ayarları</h3>
        <div class="admin-cms-modal__fields">
          <div class="admin-cms-modal__field">
            <label class="admin-label" for="spc-modal-expires">Bitiş tarihi (isteğe bağlı)</label>
            <input id="spc-modal-expires" type="datetime-local" class="admin-input" value="${escapeHtml(expLocal)}" />
            <p class="admin-cms-modal__hint">Boş bırakılırsa süresiz sayılır.</p>
          </div>
          <div class="admin-cms-modal__field">
            <label class="admin-label" for="spc-modal-url">Site linki (isteğe bağlı)</label>
            <input id="spc-modal-url" class="admin-input" value="${escapeHtml(String(row.url || ""))}" placeholder="Boş bırakılırsa sponsor kartındaki link" autocomplete="off" />
          </div>
          <label class="admin-tg-settings__toggle admin-cms-modal__toggle">
            <input type="checkbox" id="spc-modal-active"${activeChecked} />
            Yayında (sitede göster)
          </label>
        </div>
      </section>`;
  return adminCmsModalShell({
    backdropId: "spc-edit-backdrop",
    closeAttr: "data-close-spc-modal",
    titleId: "spc-modal-title",
    eyebrow: "Site promosyon kodu",
    title: modalTitle,
    lead: "Ana sitede <strong>Promosyon Kodları</strong> sayfasında yayınlanır. Sitede yayınlamak için sekmedeki <strong>Değişiklikleri kaydet</strong> gerekir.",
    bodyHtml: body,
    saveId: "spc-modal-save",
    cancelId: "spc-modal-cancel",
  });
}

function sponsorPromoCodesPanelHtml() {
  const list = state.sponsorPromoCodes || [];
  const cards = list.map((row, i) => sponsorPromoPreviewCardHtml(row, i)).join("");
  return `
    <div class="admin-cms-page">
      <header class="admin-data-page__head">
        <h2 class="admin-data-page__title">Site promosyon kodları</h2>

      </header>
      <div class="admin-cms-info">
        Önce <strong>Sponsor kartları</strong> sekmesinde siteleri tanımlayın. <strong>Değişiklikleri kaydet</strong> ile <code>data/sponsor_promo_codes.json</code> dosyasına yazılır.
        Üye coin kodları için <strong>Promo kodlar</strong> sekmesini kullanın.
      </div>
      ${cmsPanelToolbarHtml(`<span class="admin-data-count"><strong>${list.length}</strong> kayıt</span>${cmsToolbarAddBtnHtml("btn-add-spc", "Kod ekle")}`)}
      <div id="spc-list" class="admin-cms-grid">
        ${cards || `<p class="admin-cms-empty admin-empty admin-empty--muted">Henüz kayıt yok. <strong>Kod ekle</strong> ile başlayın.</p>`}
      </div>
    </div>`;
}

function bonusPreviewLine(row) {
  const title = String(row.title || "").trim();
  const badge = String(row.badge || "").trim();
  if (badge && title) return `${badge} · ${title}`;
  return title || badge || "—";
}

function bonusPreviewCardHtml(row, i) {
  const active = row.isActive !== false;
  const expStr =
    row.expiresAt == null || row.expiresAt === ""
      ? "Süresiz"
      : formatTs(row.expiresAt);
  const statusBadge = active
    ? `<span class="admin-badge admin-badge--active">Yayında</span>`
    : `<span class="admin-badge admin-badge--inactive">Gizli</span>`;
  return `
    <article class="admin-cms-card admin-cms-card--promo bonus-preview-card" data-bonus-id="${escapeHtml(row.id)}">
      <div class="admin-cms-card__top">
        <div class="admin-cms-card__content">
          <span class="admin-cms-card__eyebrow">Kayıt ${i + 1}</span>
          <h3 class="admin-cms-card__title">${escapeHtml(row.sponsorTitle || "—")}</h3>
          <p class="admin-cms-card__code">${escapeHtml(String(row.title || ""))}</p>
          <p class="admin-cms-card__desc">${escapeHtml(bonusPreviewLine(row))}</p>
          <div class="admin-cms-card__status-row">
            <span class="admin-cms-card__meta">Bitiş: ${escapeHtml(expStr)}</span>
            ${statusBadge}
          </div>
        </div>
      </div>
      <div class="admin-cms-card__actions">
        <button type="button" class="btn-edit-bonus admin-btn admin-btn-primary admin-cms-card__btn-main">${ADMIN_CMS_EDIT_ICON} Düzenle</button>
        <button type="button" class="del-bonus admin-action-btn admin-action-btn--reject">Sil</button>
      </div>
    </article>`;
}

function bonusModalHtml(row) {
  const sponsors = state.sponsors || [];
  const sponsorOpts = sponsors
    .map((s) => {
      const sel = s.id === row.sponsorId ? " selected" : "";
      return `<option value="${escapeHtml(s.id)}"${sel}>${escapeHtml(s.title)}</option>`;
    })
    .join("");
  const expLocal = unixToDatetimeLocal(row.expiresAt);
  const activeChecked = row.isActive !== false ? " checked" : "";
  const isNew = !String(row.title || "").trim();
  const modalTitle = isNew ? "Yeni bonus" : "Bonusu düzenle";
  const body = `
      <section class="admin-cms-modal__section">
        <h3 class="admin-cms-modal__section-title">Site &amp; bonus</h3>
        <div class="admin-cms-modal__fields">
          <div class="admin-cms-modal__field">
            <label class="admin-label" for="bonus-modal-sponsor">Site (sponsor)</label>
            <select id="bonus-modal-sponsor" class="admin-input">
              <option value="">— Sponsor seç —</option>
              ${sponsorOpts}
            </select>
            <p class="admin-cms-modal__hint">Önce <strong>Sponsor kartları</strong> sekmesinde site tanımlı olmalı.</p>
          </div>
          <div class="admin-cms-modal__field">
            <label class="admin-label" for="bonus-modal-title">Bonus başlığı</label>
            <input id="bonus-modal-title" class="admin-input" value="${escapeHtml(String(row.title || ""))}" maxlength="200" placeholder="Örn. 500 TL Deneme Bonusu" autocomplete="off" />
          </div>
          <div class="admin-cms-modal__field">
            <label class="admin-label" for="bonus-modal-badge">Etiket (isteğe bağlı)</label>
            <input id="bonus-modal-badge" class="admin-input" value="${escapeHtml(String(row.badge || ""))}" placeholder="Örn. Yeni üyeler" autocomplete="off" />
          </div>
        </div>
      </section>
      <section class="admin-cms-modal__section">
        <h3 class="admin-cms-modal__section-title">Kampanya metni</h3>
        <div class="admin-cms-modal__fields">
          <div class="admin-cms-modal__field">
            <label class="admin-label" for="bonus-modal-desc">Açıklama (isteğe bağlı)</label>
            <textarea id="bonus-modal-desc" class="admin-input admin-cms-modal__textarea" rows="3" placeholder="Üyelere görünecek kısa bilgi">${escapeHtml(String(row.description || ""))}</textarea>
          </div>
          <div class="admin-cms-modal__field">
            <label class="admin-label" for="bonus-modal-promo">Promo kod (isteğe bağlı)</label>
            <input id="bonus-modal-promo" class="admin-input font-mono uppercase" value="${escapeHtml(String(row.promoCode || ""))}" maxlength="128" autocomplete="off" placeholder="Varsa kopyalanabilir kod" />
          </div>
        </div>
      </section>
      <section class="admin-cms-modal__section">
        <h3 class="admin-cms-modal__section-title">Yayın ayarları</h3>
        <div class="admin-cms-modal__fields">
          <div class="admin-cms-modal__field">
            <label class="admin-label" for="bonus-modal-expires">Bitiş tarihi (isteğe bağlı)</label>
            <input id="bonus-modal-expires" type="datetime-local" class="admin-input" value="${escapeHtml(expLocal)}" />
            <p class="admin-cms-modal__hint">Boş bırakılırsa süresiz sayılır.</p>
          </div>
          <div class="admin-cms-modal__field">
            <label class="admin-label" for="bonus-modal-url">Site linki (isteğe bağlı)</label>
            <input id="bonus-modal-url" class="admin-input" value="${escapeHtml(String(row.url || ""))}" placeholder="Boş bırakılırsa sponsor kartındaki link" autocomplete="off" />
          </div>
          <label class="admin-tg-settings__toggle admin-cms-modal__toggle">
            <input type="checkbox" id="bonus-modal-active"${activeChecked} />
            Yayında (sitede göster)
          </label>
        </div>
      </section>`;
  return adminCmsModalShell({
    backdropId: "bonus-edit-backdrop",
    closeAttr: "data-close-bonus-modal",
    titleId: "bonus-modal-title-el",
    eyebrow: "Site bonusu",
    title: modalTitle,
    lead: "Ana sitede <strong>Bonuslar</strong> sayfasında yayınlanır. Sitede göstermek için sekmedeki <strong>Değişiklikleri kaydet</strong> gerekir.",
    bodyHtml: body,
    saveId: "bonus-modal-save",
    cancelId: "bonus-modal-cancel",
  });
}

function bonusesPanelHtml() {
  const list = state.bonuses || [];
  const cards = list.map((row, i) => bonusPreviewCardHtml(row, i)).join("");
  return `
    <div class="admin-cms-page">
      <header class="admin-data-page__head">
        <h2 class="admin-data-page__title">Site bonusları</h2>
      </header>
      <div class="admin-cms-info">
        Önce <strong>Sponsor kartları</strong> sekmesinde siteleri tanımlayın. <strong>Değişiklikleri kaydet</strong> ile <code>data/bonuses.json</code> dosyasına yazılır.
        Sitede sidebar <strong>Bonuslar</strong> sayfasında listelenir.
      </div>
      ${cmsPanelToolbarHtml(`<span class="admin-data-count"><strong>${list.length}</strong> kayıt</span>${cmsToolbarAddBtnHtml("btn-add-bonus", "Bonus ekle")}`)}
      <div id="bonus-list" class="admin-cms-grid">
        ${cards || `<p class="admin-cms-empty admin-empty admin-empty--muted">Henüz kayıt yok. <strong>Bonus ekle</strong> ile başlayın.</p>`}
      </div>
    </div>`;
}

function memberProfileBjOutcomeLabel(o) {
  const x = String(o || "");
  if (x === "win") return "Kazandı";
  if (x === "lose") return "Kaybetti";
  if (x === "push") return "Berabere";
  if (x === "blackjack") return "Blackjack";
  return x;
}

const ADMIN_SLOT_SYM = {
  cherry: "🍒",
  lemon: "🍋",
  bell: "🔔",
  diamond: "💎",
  seven: "7️⃣",
};

function memberProfileSlotReelsDisplay(reels) {
  if (!Array.isArray(reels)) return "—";
  return reels.map((id) => ADMIN_SLOT_SYM[String(id)] || String(id)).join(" ");
}

function memberProfileDetailPageHtml(d) {
  const u = d.user || {};
  const memberLevel = d.level || u.level || {};
  const crypto = d.crypto || {};
  const purchases = Array.isArray(d.purchases) ? d.purchases : [];
  const sponsorAccounts = Array.isArray(d.sponsorAccounts) ? d.sponsorAccounts : [];
  const blackjackRounds = Array.isArray(d.blackjackRounds) ? d.blackjackRounds : [];
  const slotRounds = Array.isArray(d.slotRounds) ? d.slotRounds : [];
  const promoRedemptions = Array.isArray(d.promoRedemptions) ? d.promoRedemptions : [];
  const ticketEntries = Array.isArray(d.ticketEntries) ? d.ticketEntries : [];
  const ticketRequests = Array.isArray(d.ticketRequests) ? d.ticketRequests : [];
  const tabIds = new Set(["overview", "profile-info", "ip", "wheel", "telegram", "crypto", "sponsor", "purchases", "promo", "tickets", "blackjack", "slot"]);
  let activeTab = state.memberProfileActiveTab || "overview";
  if (!tabIds.has(activeTab)) {
    activeTab = "overview";
    state.memberProfileActiveTab = activeTab;
  }

  const cryptoGrid = MEMBER_CRYPTO_ROWS.map(
    ([label, key]) => `
    <div class="admin-inset admin-inset--compact px-3 py-2.5">
      <p class="text-[10px] font-semibold uppercase tracking-wide text-slate-500">${escapeHtml(label)}</p>
      <div class="mt-1.5 min-h-[1.25rem]">${memberProfileCryptoCell(crypto, key)}</div>
    </div>`
  ).join("");
  const prRows = purchases
    .map(
      (p) => `
    <tr class="admin-table-row">
      <td class="px-3 py-2 font-mono text-[11px] text-slate-500">${escapeHtml(String(p.itemId ?? ""))}</td>
      <td class="px-3 py-2 text-slate-200">${escapeHtml(String(p.itemTitle ?? ""))}</td>
      <td class="px-3 py-2 tabular-nums text-amber-200/90">${Number(p.price) || 0}</td>
      <td class="px-3 py-2 text-xs text-slate-500">${formatTs(p.createdAt)}</td>
    </tr>`
    )
    .join("");
  const prBlock =
    purchases.length > 0
      ? `<div class="admin-member-table-wrap">
      <table class="w-full min-w-[560px] text-left text-xs">
        <thead class="admin-member-table-head">
          <tr>
            <th class="px-3 py-2 font-medium">Ürün kodu</th>
            <th class="px-3 py-2 font-medium">Ürün</th>
            <th class="px-3 py-2 font-medium">Coin</th>
            <th class="px-3 py-2 font-medium">Tarih</th>
          </tr>
        </thead>
        <tbody>${prRows}</tbody>
      </table>
    </div>`
      : `<p class="admin-inset px-4 py-6 text-center text-sm text-slate-500">Market işlemi yok.</p>`;
  const sponsorRows = sponsorAccounts
    .map(
      (s) => `
    <tr class="admin-table-row">
      <td class="px-3 py-2 text-slate-200">${escapeHtml(String(s.sponsorTitle || s.sponsorId || "—"))}</td>
      <td class="px-3 py-2 font-mono text-xs text-slate-200 break-all">${escapeHtml(String(s.accountValue || "—"))}</td>
      <td class="px-3 py-2 text-xs text-slate-500">${formatTs(s.updatedAt)}</td>
    </tr>`
    )
    .join("");
  const sponsorBlock =
    sponsorAccounts.length > 0
      ? `<div class="admin-member-table-wrap">
      <table class="w-full min-w-[520px] text-left text-xs">
        <thead class="admin-member-table-head">
          <tr>
            <th class="px-3 py-2 font-medium">Sponsor</th>
            <th class="px-3 py-2 font-medium">Üye ID / kullanıcı adı</th>
            <th class="px-3 py-2 font-medium">Güncelleme</th>
          </tr>
        </thead>
        <tbody>${sponsorRows}</tbody>
      </table>
    </div>`
      : `<p class="admin-inset px-4 py-6 text-center text-sm text-slate-500">Sponsor hesap bilgisi yok.</p>`;

  const tg = d.telegram || {};
  const tgVerified = tg.verified === true;
  const tgName = tg.username != null && String(tg.username).trim() !== "" ? String(tg.username) : "";
  const tgUid = tg.telegramUserId != null ? Number(tg.telegramUserId) : 0;
  const tgPending = tg.pendingExpiresAt != null ? Number(tg.pendingExpiresAt) : NaN;
  const telegramInner = tgVerified
    ? `<p><span class="font-semibold text-emerald-300">Doğrulanmış</span></p>
              <p class="mt-2 text-xs text-slate-400">Kullanıcı adı: <strong class="text-slate-200">${tgName ? "@" + escapeHtml(tgName) : "—"}</strong></p>
              <p class="mt-1 font-mono text-[11px] text-slate-500">Telegram kullanıcı ID: <span class="text-slate-400">${Number.isFinite(tgUid) && tgUid > 0 ? escapeHtml(String(tgUid)) : "—"}</span></p>
              <p class="mt-1 text-xs text-slate-500">Doğrulama zamanı: ${formatTs(tg.verifiedAt)}</p>`
    : `<p><span class="font-semibold text-slate-400">Doğrulanmamış</span></p>${Number.isFinite(tgPending) && tgPending > Date.now() / 1000
      ? `<p class="mt-2 text-xs text-amber-200/90">Bekleyen bağlantı süresi (yaklaşık): ${formatTs(tgPending)}</p>`
      : `<p class="mt-2 text-xs text-slate-500">Üye henüz botta Start ile tamamlamadı veya süre doldu.</p>`
    }`;
  const telegramBlock = `<div class="admin-member-profile__card text-sm">${telegramInner}</div>`;

  const uid = Number(u.id) || 0;
  const wHours = u.wheelCooldownHours != null ? Number(u.wheelCooldownHours) : 24;
  const wCan = u.wheelCanSpin === true;
  const wheelBlock = `<div class="admin-member-profile__card">
            <div class="flex flex-wrap items-center gap-2 text-sm">
              <span class="text-slate-500">Durum:</span>
              ${wCan ? `<span class="font-semibold text-emerald-300">Çevirebilir</span>` : `<span class="font-semibold text-amber-200/95">Beklemede</span>`}
              <span class="text-xs text-slate-500">· Kural: ${wHours} sa</span>
            </div>
            <p class="mt-2 text-xs text-slate-500">Son çevirme: ${formatTs(u.wheelLastSpinAt)} · Sonraki sıra: ${wCan ? "—" : formatTs(u.wheelNextSpinAt)}</p>
            <div class="mt-3 flex flex-wrap gap-2">
              <button type="button" id="member-wheel-allow" data-user-id="${uid}" class="admin-btn admin-btn-soft px-3 py-2 text-sm">Hakkı ver (beklemeyi sıfırla)</button>
              <button type="button" id="member-wheel-lock" data-user-id="${uid}" class="admin-btn admin-btn-ghost px-3 py-2 text-sm">Beklemeye al (hakkı kapat)</button>
            </div>
          </div>`;

  const nPur = purchases.length;
  const nSp = sponsorAccounts.length;
  const nPromo = promoRedemptions.length;
  const nTicketItems = ticketEntries.length + ticketRequests.length;
  const regIp = String(u.registerIp || "").trim();
  const lastIp = String(u.lastLoginIp || "").trim();
  const sameRegCnt = Number(u.sameRegisterIpCount) || 0;
  const ipsMatch = regIp !== "" && lastIp !== "" && regIp === lastIp;
  const ipCopyBtn = (val) =>
    val
      ? `<button type="button" class="admin-member-profile__ip-copy" data-copy-ip="${encodeURIComponent(val)}" title="Panoya kopyala" aria-label="IP kopyala">
          <svg class="admin-member-profile__ip-copy-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"/></svg>
          Kopyala
        </button>`
      : `<span class="admin-member-profile__ip-copy admin-member-profile__ip-copy--off">—</span>`;
  const ipBlock = `<div class="admin-member-profile__ip-grid">
            <div class="admin-member-profile__ip-card">
              <div class="admin-member-profile__ip-card-head">
                <span class="admin-member-profile__ip-label">Kayıt IP</span>
                ${ipCopyBtn(regIp)}
              </div>
              <p class="admin-member-profile__ip-value">${regIp ? escapeHtml(regIp) : "—"}</p>
              <p class="admin-member-profile__ip-note">Üyelik oluşturulurken kaydedilen adres.</p>
            </div>
            <div class="admin-member-profile__ip-card">
              <div class="admin-member-profile__ip-card-head">
                <span class="admin-member-profile__ip-label">Son giriş IP</span>
                ${ipCopyBtn(lastIp)}
              </div>
              <p class="admin-member-profile__ip-value">${lastIp ? escapeHtml(lastIp) : "—"}</p>
              <p class="admin-member-profile__ip-note">Son başarılı oturumda sunucunun gördüğü IP (güncel bağlantı değilse farklı olabilir).</p>
            </div>
          </div>
          <p class="admin-member-profile__ip-compare ${!regIp && !lastIp ? "text-slate-500" : ipsMatch ? "text-emerald-200/90" : "text-slate-300"
    }">
            ${!regIp && !lastIp
      ? "Henüz kayıtlı IP bilgisi yok."
      : ipsMatch
        ? "Kayıt ve son giriş IP’si aynı."
        : regIp && lastIp
          ? "Kayıt IP’si ile son giriş IP’si farklı — üye farklı ağ veya cihaz kullanmış olabilir."
          : "Eksik IP alanları üye veya sunucu kaydına bağlıdır."
    }
          </p>
          ${sameRegCnt > 1
      ? `<div class="admin-member-profile__ip-warn" role="status">
              <strong class="block text-amber-100">Aynı kayıt IP’si: ${sameRegCnt} hesap</strong>
              <p class="mt-1 text-xs text-amber-100/85">Bu kayıt IP adresiyle açılmış toplam üye sayısı. Birden fazlaysa çoklu hesap veya paylaşımlı bağlantı ihtimali vardır.</p>
            </div>`
      : `<div class="admin-member-profile__ip-ok text-sm text-slate-500">Bu kayıt IP’si için tek hesap (veya IP boş).</div>`
    }`;

  const nBj = blackjackRounds.length;
  const bjNetTotal = blackjackRounds.reduce(
    (s, r) => s + ((Number(r.payout) || 0) - (Number(r.stake) || 0)),
    0
  );
  const BJ_PER_PAGE = 10;
  const bjTotalPages = Math.max(1, Math.ceil(nBj / BJ_PER_PAGE));
  let bjPage = Number(state.memberProfileBlackjackPage) || 0;
  if (bjPage < 0) bjPage = 0;
  if (bjPage > bjTotalPages - 1) bjPage = bjTotalPages - 1;
  state.memberProfileBlackjackPage = bjPage;
  const bjStart = bjPage * BJ_PER_PAGE;
  const bjSlice = blackjackRounds.slice(bjStart, bjStart + BJ_PER_PAGE);
  const bjRows = bjSlice
    .map((r) => {
      const stake = Number(r.stake) || 0;
      const payout = Number(r.payout) || 0;
      const net = payout - stake;
      const netCls = net > 0 ? "text-emerald-300" : net < 0 ? "text-rose-300" : "text-slate-400";
      const netStr = net > 0 ? `+${net}` : String(net);
      return `
    <tr class="admin-table-row">
      <td class="px-3 py-2 text-xs text-slate-500">${formatTs(r.createdAt)}</td>
      <td class="px-3 py-2 tabular-nums text-slate-200">${Number(r.bet) || 0}</td>
      <td class="px-3 py-2 text-center text-xs text-slate-400">${r.doubled ? "Evet" : "—"}</td>
      <td class="px-3 py-2 text-slate-200">${escapeHtml(memberProfileBjOutcomeLabel(String(r.outcome ?? "")))}</td>
      <td class="px-3 py-2 tabular-nums text-slate-400">${stake}</td>
      <td class="px-3 py-2 tabular-nums text-slate-400">${payout}</td>
      <td class="px-3 py-2 tabular-nums font-semibold ${netCls}">${netStr}</td>
    </tr>`;
    })
    .join("");
  const bjBlock =
    blackjackRounds.length > 0
      ? `<p class="mb-3 text-sm text-slate-400">Toplam <strong class="text-slate-300">${blackjackRounds.length}</strong> el · gösterilen <strong class="text-slate-300">${bjStart + 1}-${Math.min(bjStart + BJ_PER_PAGE, blackjackRounds.length)}</strong> · net: <strong class="${bjNetTotal >= 0 ? "text-emerald-300" : "text-rose-300"}">${bjNetTotal >= 0 ? "+" : ""}${bjNetTotal}</strong> coin <span class="text-xs text-slate-600">(el başına: ödenen − risk)</span></p>
      <div class="mb-3 flex items-center justify-end gap-2">
        <button type="button" data-bj-page="prev" class="admin-pager-btn transition disabled:cursor-not-allowed disabled:opacity-35"${bjPage <= 0 ? " disabled" : ""} aria-label="Önceki sayfa">
          <svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"/></svg>
        </button>
        <span class="text-xs text-slate-400">Sayfa ${bjPage + 1}/${bjTotalPages}</span>
        <button type="button" data-bj-page="next" class="admin-pager-btn transition disabled:cursor-not-allowed disabled:opacity-35"${bjPage >= bjTotalPages - 1 ? " disabled" : ""} aria-label="Sonraki sayfa">
          <svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/></svg>
        </button>
      </div>
      <div class="admin-member-table-wrap">
      <table class="w-full min-w-[720px] text-left text-xs">
        <thead class="admin-member-table-head">
          <tr>
            <th class="px-3 py-2 font-medium">Tarih</th>
            <th class="px-3 py-2 font-medium">Bahis</th>
            <th class="px-3 py-2 font-medium text-center">Çift</th>
            <th class="px-3 py-2 font-medium">Sonuç</th>
            <th class="px-3 py-2 font-medium">Risk</th>
            <th class="px-3 py-2 font-medium">Ödeme</th>
            <th class="px-3 py-2 font-medium">Net</th>
          </tr>
        </thead>
        <tbody>${bjRows}</tbody>
      </table>
    </div>`
      : `<p class="admin-inset px-4 py-6 text-center text-sm text-slate-500">Kayıtlı blackjack eli yok. Geçmiş yalnızca bu özellik eklendikten sonra biten ellerden oluşur.</p>`;

  const nSlot = slotRounds.length;
  const slotNetTotal = slotRounds.reduce((s, r) => s + ((Number(r.payout) || 0) - (Number(r.bet) || 0)), 0);
  const SLOT_PER_PAGE = 10;
  const slotTotalPages = Math.max(1, Math.ceil(nSlot / SLOT_PER_PAGE));
  let slotPage = Number(state.memberProfileSlotPage) || 0;
  if (slotPage < 0) slotPage = 0;
  if (slotPage > slotTotalPages - 1) slotPage = slotTotalPages - 1;
  state.memberProfileSlotPage = slotPage;
  const slotStart = slotPage * SLOT_PER_PAGE;
  const slotSlice = slotRounds.slice(slotStart, slotStart + SLOT_PER_PAGE);
  const slotRows = slotSlice
    .map((r) => {
      const bet = Number(r.bet) || 0;
      const payout = Number(r.payout) || 0;
      const net = payout - bet;
      const netCls = net > 0 ? "text-emerald-300" : net < 0 ? "text-rose-300" : "text-slate-400";
      const netStr = net > 0 ? `+${net}` : String(net);
      const mult = Number(r.multiplier) || 0;
      const reelsStr = memberProfileSlotReelsDisplay(r.reels);
      return `
    <tr class="admin-table-row">
      <td class="px-3 py-2 text-xs text-slate-500">${formatTs(r.createdAt)}</td>
      <td class="px-3 py-2 tabular-nums text-slate-200">${bet}</td>
      <td class="px-3 py-2 text-lg leading-none">${reelsStr}</td>
      <td class="px-3 py-2 tabular-nums text-slate-300">${mult > 0 ? `${mult}×` : "—"}</td>
      <td class="px-3 py-2 tabular-nums text-slate-400">${payout}</td>
      <td class="px-3 py-2 tabular-nums font-semibold ${netCls}">${netStr}</td>
    </tr>`;
    })
    .join("");
  const slotBlock =
    slotRounds.length > 0
      ? `<p class="mb-3 text-sm text-slate-400">Toplam <strong class="text-slate-300">${slotRounds.length}</strong> çevirme · gösterilen <strong class="text-slate-300">${slotStart + 1}-${Math.min(slotStart + SLOT_PER_PAGE, slotRounds.length)}</strong> · net: <strong class="${slotNetTotal >= 0 ? "text-emerald-300" : "text-rose-300"}">${slotNetTotal >= 0 ? "+" : ""}${slotNetTotal}</strong> coin <span class="text-xs text-slate-600">(ödeme − bahis)</span></p>
      <div class="mb-3 flex items-center justify-end gap-2">
        <button type="button" data-slot-page="prev" class="admin-pager-btn transition disabled:cursor-not-allowed disabled:opacity-35"${slotPage <= 0 ? " disabled" : ""} aria-label="Önceki sayfa">
          <svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"/></svg>
        </button>
        <span class="text-xs text-slate-400">Sayfa ${slotPage + 1}/${slotTotalPages}</span>
        <button type="button" data-slot-page="next" class="admin-pager-btn transition disabled:cursor-not-allowed disabled:opacity-35"${slotPage >= slotTotalPages - 1 ? " disabled" : ""} aria-label="Sonraki sayfa">
          <svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/></svg>
        </button>
      </div>
      <div class="admin-member-table-wrap">
      <table class="w-full min-w-[640px] text-left text-xs">
        <thead class="admin-member-table-head">
          <tr>
            <th class="px-3 py-2 font-medium">Tarih</th>
            <th class="px-3 py-2 font-medium">Bahis</th>
            <th class="px-3 py-2 font-medium">Makaralar</th>
            <th class="px-3 py-2 font-medium">Çarpan</th>
            <th class="px-3 py-2 font-medium">Ödeme</th>
            <th class="px-3 py-2 font-medium">Net</th>
          </tr>
        </thead>
        <tbody>${slotRows}</tbody>
      </table>
    </div>`
      : `<p class="admin-inset px-4 py-6 text-center text-sm text-slate-500">Kayıtlı slot çevirmesi yok.</p>`;

  const promoRows = promoRedemptions
    .map(
      (r) => `
    <tr class="admin-table-row">
      <td class="px-3 py-2 font-mono text-xs text-slate-300">${escapeHtml(String(r.code || "—"))}</td>
      <td class="px-3 py-2 tabular-nums text-emerald-300">+${Number(r.coinsAwarded) || 0}</td>
      <td class="px-3 py-2 text-xs text-slate-500">${formatTs(r.redeemedAt)}</td>
    </tr>`
    )
    .join("");
  const promoBlock =
    promoRedemptions.length > 0
      ? `<div class="admin-member-table-wrap">
      <table class="w-full min-w-[520px] text-left text-xs">
        <thead class="admin-member-table-head">
          <tr>
            <th class="px-3 py-2 font-medium">Promo kod</th>
            <th class="px-3 py-2 font-medium">Eklenen coin</th>
            <th class="px-3 py-2 font-medium">Kullanım tarihi</th>
          </tr>
        </thead>
        <tbody>${promoRows}</tbody>
      </table>
    </div>`
      : `<p class="admin-inset px-4 py-6 text-center text-sm text-slate-500">Bu üyede promo kod kullanımı yok.</p>`;

  const memberTicketEventLabel = (row) => {
    const t = String(row.eventTitle || row.sponsorTitle || "").trim();
    return t || `Etkinlik #${row.eventId ?? "?"}`;
  };

  const ticketEntryRows = ticketEntries
    .map((e) => {
      const win = e.isWinner
        ? `<span class="admin-ticket-badge admin-ticket-badge--approved">Kazanan</span>`
        : `<span class="text-slate-500">—</span>`;
      return `
    <tr class="admin-table-row">
      <td class="px-3 py-2 font-mono text-xs text-accent-bright/95">${escapeHtml(String(e.ticketCode || ""))}</td>
      <td class="px-3 py-2 text-slate-200">${escapeHtml(memberTicketEventLabel(e))}</td>
      <td class="px-3 py-2 text-xs text-slate-500">${escapeHtml(ticketStatusLabel(String(e.eventStatus || "")))}</td>
      <td class="px-3 py-2">${win}</td>
      <td class="px-3 py-2 text-xs text-slate-500">${formatTs(e.createdAt)}</td>
    </tr>`;
    })
    .join("");
  const ticketEntriesBlock =
    ticketEntries.length > 0
      ? `<div class="admin-member-table-wrap">
      <table class="w-full min-w-[640px] text-left text-xs">
        <thead class="admin-member-table-head">
          <tr>
            <th class="px-3 py-2 font-medium">Bilet kodu</th>
            <th class="px-3 py-2 font-medium">Etkinlik</th>
            <th class="px-3 py-2 font-medium">Etkinlik durumu</th>
            <th class="px-3 py-2 font-medium">Sonuç</th>
            <th class="px-3 py-2 font-medium">Tanımlanma</th>
          </tr>
        </thead>
        <tbody>${ticketEntryRows}</tbody>
      </table>
    </div>`
      : `<p class="admin-inset px-4 py-6 text-center text-sm text-slate-500">Tanımlı bilet yok.</p>`;

  const ticketRequestRows = ticketRequests
    .map((r) => {
      const invAt = r.investmentAt ? formatTs(r.investmentAt) : "—";
      const note =
        r.adminNote && String(r.adminNote).trim()
          ? `<p class="mt-1 text-[10px] text-slate-500">${escapeHtml(String(r.adminNote).trim())}</p>`
          : "";
      return `
    <tr class="admin-table-row">
      <td class="px-3 py-2 text-slate-200">${escapeHtml(memberTicketEventLabel(r))}</td>
      <td class="px-3 py-2 font-mono text-xs text-slate-300">${escapeHtml(String(r.siteUsername || "—"))}</td>
      <td class="px-3 py-2 tabular-nums text-slate-200">${Number(r.investmentAmount) || 0} TRY</td>
      <td class="px-3 py-2 text-xs text-slate-500">${invAt}</td>
      <td class="px-3 py-2 tabular-nums text-slate-300">${Number(r.ticketsEligible) || 0} / ${Number(r.ticketsGranted) || 0}</td>
      <td class="px-3 py-2">${ticketRequestStatusBadge(String(r.status || ""))}${note}</td>
      <td class="px-3 py-2 text-xs text-slate-500">${formatTs(r.createdAt)}</td>
    </tr>`;
    })
    .join("");
  const ticketRequestsBlock =
    ticketRequests.length > 0
      ? `<div class="admin-member-table-wrap">
      <table class="w-full min-w-[720px] text-left text-xs">
        <thead class="admin-member-table-head">
          <tr>
            <th class="px-3 py-2 font-medium">Etkinlik</th>
            <th class="px-3 py-2 font-medium">Site kullanıcı adı</th>
            <th class="px-3 py-2 font-medium">Yatırım</th>
            <th class="px-3 py-2 font-medium">Yatırım tarihi</th>
            <th class="px-3 py-2 font-medium">Hak / verilen</th>
            <th class="px-3 py-2 font-medium">Talep durumu</th>
            <th class="px-3 py-2 font-medium">Gönderim</th>
          </tr>
        </thead>
        <tbody>${ticketRequestRows}</tbody>
      </table>
    </div>`
      : `<p class="admin-inset px-4 py-6 text-center text-sm text-slate-500">Bilet talebi yok.</p>`;

  const ticketsBlock =
    ticketEntries.length === 0 && ticketRequests.length === 0
      ? `<p class="admin-inset px-4 py-8 text-center text-sm text-slate-500">Bu üyenin bilet kaydı veya talebi bulunmuyor.</p>`
      : `<div class="space-y-8">
          <div>
            <h4 class="mb-2 text-sm font-semibold text-slate-300">Tanımlı biletler <span class="font-normal text-slate-500">(${ticketEntries.length})</span></h4>
            ${ticketEntriesBlock}
          </div>
          <div>
            <h4 class="mb-2 text-sm font-semibold text-slate-300">Bilet talepleri <span class="font-normal text-slate-500">(${ticketRequests.length})</span></h4>
            ${ticketRequestsBlock}
          </div>
        </div>`;

  const tabDefs = [
    { id: "overview", label: "Özet" },
    { id: "profile-info", label: "Profil bilgileri" },
    { id: "ip", label: sameRegCnt > 1 ? `IP (${sameRegCnt})` : "IP" },
    { id: "wheel", label: "Çark" },
    { id: "telegram", label: tgVerified ? "Telegram ✓" : "Telegram" },
    { id: "crypto", label: "Kripto" },
    { id: "sponsor", label: nSp ? `Sponsor (${nSp})` : "Sponsor" },
    { id: "purchases", label: nPur ? `Market (${nPur})` : "Market" },
    { id: "promo", label: nPromo ? `Promo (${nPromo})` : "Promo" },
    { id: "tickets", label: nTicketItems ? `Biletler (${nTicketItems})` : "Biletler" },
    { id: "blackjack", label: nBj ? `Blackjack (${nBj})` : "Blackjack" },
    { id: "slot", label: nSlot ? `Slot (${nSlot})` : "Slot" },
  ];
  const tabNavHtml = tabDefs
    .map(({ id, label }) => {
      const isActive = activeTab === id;
      return `<button type="button" role="tab" id="member-tab-btn-${id}" aria-selected="${isActive}" aria-controls="member-tab-panel-${id}" data-member-tab="${id}" class="admin-member-profile__tab${isActive ? " is-active" : ""}">${escapeHtml(label)}</button>`;
    })
    .join("");

  const displayInitial = escapeHtml(
    (String(u.username || "?").trim().charAt(0) || "?").toUpperCase()
  );
  const adminTagsUser = Array.isArray(u.adminTags) ? u.adminTags : [];
  const staffMetaBlock = `
    <div class="admin-member-profile-pro__staff" aria-labelledby="member-profile-section-staff-meta">
      <h3 id="member-profile-section-staff-meta" class="admin-member-profile__surface-title">İç not ve etiketler</h3>
      <p class="admin-member-profile__surface-desc">Yalnızca bu panelde görünür; üyeye gösterilmez.</p>
      <div class="mb-4 flex flex-wrap gap-4">
        ${MEMBER_ADMIN_TAG_DEFS.map(
    (d) => `
        <label class="flex cursor-pointer items-center gap-2 text-sm text-slate-300">
          <input type="checkbox" class="admin-checkbox member-profile-tag-cb" data-member-tag="${d.id}" ${adminTagsUser.includes(d.id) ? "checked" : ""} />
          <span>${escapeHtml(d.label)}</span>
        </label>`
  ).join("")}
      </div>
      <div>
        <label class="admin-label" for="member-profile-admin-note">İç not</label>
        <textarea id="member-profile-admin-note" class="admin-input min-h-[88px] resize-y text-sm" rows="3" maxlength="4000" placeholder="Kısa notlar…">${escapeHtml(String(u.adminNote ?? ""))}</textarea>
      </div>
      <div class="admin-member-profile__surface-actions mt-4">
        <button type="button" id="member-profile-meta-save" class="admin-member-profile__btn-primary" data-user-id="${uid}">
          Notu ve etiketleri kaydet
        </button>
      </div>
    </div>`;

  const overviewBlock = `
  <div class="admin-member-profile__metrics">
    <div class="admin-member-profile__metric">
      <span class="admin-member-profile__metric-label">Seviye</span>
      <span class="admin-member-profile__metric-value tabular-nums">${memberLevel.enabled === false ? "—" : `Lv.${Math.max(0, Number(memberLevel.level) || 0)}`}</span>
    </div>
    <div class="admin-member-profile__metric">
      <span class="admin-member-profile__metric-label">Toplam XP</span>
      <span class="admin-member-profile__metric-value tabular-nums">${memberLevel.enabled === false ? "—" : Math.max(0, Number(memberLevel.xpTotal) || 0)}</span>
    </div>
    <div class="admin-member-profile__metric">
      <span class="admin-member-profile__metric-label">Toplam harcama</span>
      <span class="admin-member-profile__metric-value tabular-nums">${Number(u.totalSpent) || 0}</span>
    </div>
    <div class="admin-member-profile__metric">
      <span class="admin-member-profile__metric-label">Market işlemi</span>
      <span class="admin-member-profile__metric-value tabular-nums">${Number(u.purchaseCount) || 0}</span>
    </div>
    <div class="admin-member-profile__metric">
      <span class="admin-member-profile__metric-label">Kayıt</span>
      <span class="admin-member-profile__metric-value admin-member-profile__metric-value--sm">${formatTs(u.createdAt)}</span>
    </div>
  </div>
  ${memberLevelProfileBlockHtml(memberLevel)}`;

  const profileInfoBlock = `
  <div class="admin-member-profile-pro">
    <div class="admin-member-profile-pro__stats">
      <article class="admin-member-profile-pro__stat">
        <span class="admin-member-profile-pro__stat-label">Durum</span>
        <span class="admin-members-status ${u.isActive !== false ? "admin-members-status--on" : "admin-members-status--off"}">${u.isActive !== false ? "Aktif" : "Pasif"}</span>
      </article>
      <article class="admin-member-profile-pro__stat">
        <span class="admin-member-profile-pro__stat-label">2FA</span>
        <strong class="admin-member-profile-pro__stat-value ${u.twofaEnabled ? "text-emerald-300" : "text-slate-400"}">${u.twofaEnabled ? "Açık" : "Kapalı"}</strong>
      </article>
      <article class="admin-member-profile-pro__stat">
        <span class="admin-member-profile-pro__stat-label">Coin</span>
        <strong class="admin-member-profile-pro__stat-value tabular-nums">${Number(u.coins) || 0}</strong>
      </article>
      <article class="admin-member-profile-pro__stat">
        <span class="admin-member-profile-pro__stat-label">Seviye</span>
        <strong class="admin-member-profile-pro__stat-value tabular-nums">${memberLevel.enabled === false ? "—" : `Lv.${Math.max(0, Number(memberLevel.level) || 0)}`}</strong>
      </article>
      <article class="admin-member-profile-pro__stat">
        <span class="admin-member-profile-pro__stat-label">Toplam XP</span>
        <strong class="admin-member-profile-pro__stat-value tabular-nums">${memberLevel.enabled === false ? "—" : Math.max(0, Number(memberLevel.xpTotal) || 0)}</strong>
      </article>
      <article class="admin-member-profile-pro__stat">
        <span class="admin-member-profile-pro__stat-label">Toplam harcama</span>
        <strong class="admin-member-profile-pro__stat-value tabular-nums">${Number(u.totalSpent) || 0}</strong>
      </article>
      <article class="admin-member-profile-pro__stat">
        <span class="admin-member-profile-pro__stat-label">Market işlemi</span>
        <strong class="admin-member-profile-pro__stat-value tabular-nums">${Number(u.purchaseCount) || 0}</strong>
      </article>
    </div>
    <div class="admin-member-profile-pro__grid">
      <section class="admin-member-profile__surface admin-member-profile-pro__card" aria-labelledby="member-profile-section-identity">
        <h3 id="member-profile-section-identity" class="admin-member-profile__surface-title">Profil bilgileri</h3>
        <p class="admin-member-profile__surface-desc">Kullanıcı adı ve e-posta giriş için kullanılır; başka üyeyle çakışamaz.</p>
        <div class="admin-member-profile__field-grid">
          <div>
            <label class="admin-label" for="member-profile-fld-username">Kullanıcı adı</label>
            <input id="member-profile-fld-username" type="text" autocomplete="off" class="admin-input" value="${escapeHtml(String(u.username ?? ""))}" maxlength="64" />
          </div>
          <div>
            <label class="admin-label" for="member-profile-fld-email">E-posta</label>
            <input id="member-profile-fld-email" type="email" autocomplete="off" class="admin-input" value="${escapeHtml(String(u.email ?? ""))}" />
          </div>
        </div>
        <div class="admin-member-profile__surface-actions">
          <button type="button" id="member-profile-save" class="admin-member-profile__btn-primary" data-user-id="${uid}">
            <svg class="admin-member-profile__btn-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/></svg>
            Profili kaydet
          </button>
        </div>
        ${staffMetaBlock}
      </section>
      <section class="admin-member-profile__surface admin-member-profile-pro__card" aria-labelledby="member-profile-section-coin-security">
        <h3 id="member-profile-section-coin-security" class="admin-member-profile__surface-title">Bakiye ve güvenlik</h3>
        <p class="admin-member-profile__surface-desc">Coin işlemleri, şifre sıfırlama ve üyelik yönetimi.</p>
        <div class="member-profile-coin-root admin-member-profile__coin-block">
          <label class="admin-label" for="member-profile-coin-amt">İşlem miktarı</label>
          <div class="admin-member-profile__coin-row">
            <input id="member-profile-coin-amt" type="number" min="1" step="1" class="admin-input admin-coin-amt admin-member-profile__coin-input tabular-nums" value="10" title="Eklenecek veya düşürülecek miktar" aria-label="İşlem miktarı" />
            <div class="admin-member-profile__coin-actions">
              <button type="button" class="admin-member-profile__btn-coin-add admin-coin-add" data-user-id="${uid}">Puan ekle</button>
              <button type="button" class="admin-member-profile__btn-coin-sub admin-coin-sub" data-user-id="${uid}">Puan düşür</button>
            </div>
          </div>
          <div class="admin-member-profile__quick-amt" role="group" aria-label="Hızlı miktar">
            ${[10, 25, 50, 100].map((n) => `<button type="button" class="member-profile-quick-amt" data-amt="${n}">${n}</button>`).join("")}
          </div>
        </div>
        <div class="admin-member-profile-pro__divider"></div>
        <button type="button" class="btn-set-password admin-member-profile__btn-password" data-user-id="${uid}">
          <svg class="admin-member-profile__btn-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z"/></svg>
          Şifre sıfırla
        </button>
        ${
          u.twofaEnabled
            ? canPerm("member-twofa")
              ? `<button type="button" id="member-profile-twofa-disable" class="admin-btn admin-btn-ghost mt-3 w-full justify-center text-amber-300 hover:bg-amber-950/30 sm:w-auto" data-user-id="${uid}">Üye 2FA Kapat</button>`
              : `<p class="mt-3 text-xs text-slate-500">2FA açık. Kapatmak için «Üye 2FA kapatma» yetkisi gerekir.</p>`
            : `<p class="mt-3 text-xs text-slate-500">2FA kapalı. Üye profilinden kendisi açabilir.</p>`
        }
        <div class="admin-member-profile__account-actions">
          <button type="button" class="btn-toggle-user-active admin-members-btn-sm ${u.isActive !== false ? "admin-members-btn-sm--toggle-off" : "admin-members-btn-sm--toggle-on"}" data-user-id="${uid}" data-next-active="${u.isActive !== false ? "0" : "1"}">
            ${u.isActive !== false ? "Pasife al" : "Aktife al"}
          </button>
          <button type="button" class="btn-member-delete admin-members-btn-sm admin-members-btn-sm--danger" data-user-id="${uid}" data-username="${escapeHtml(String(u.username || ""))}">
            Üyeyi sil
          </button>
        </div>
      </section>
    </div>
  </div>`;

  const ph = (id) => (activeTab === id ? "" : " hidden");

  return `
    <div id="member-detail-root" class="admin-member-profile mx-auto w-full max-w-5xl pb-2">
      <div class="admin-panel overflow-hidden">
        <div class="admin-member-profile__hero admin-member-profile__hero--premium border-b border-[var(--admin-border)] p-5 sm:p-6">
          <button type="button" id="member-profile-back" class="admin-member-profile__back admin-btn admin-btn-ghost mb-5 inline-flex items-center gap-2 px-4 py-2.5 text-sm">
            <svg class="h-4 w-4 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"/></svg>
            Üye listesine dön
          </button>
          <div class="admin-member-profile__hero-layout">
            <div class="admin-member-profile__avatar" aria-hidden="true">${displayInitial}</div>
            <div class="admin-member-profile__hero-main min-w-0">
              <p class="admin-login-eyebrow text-[10px]">Üye kartı</p>
              <h2 id="member-profile-title" class="mt-1 truncate admin-section__title text-xl font-bold sm:text-2xl">${escapeHtml(String(u.username ?? ""))}</h2>
              ${memberProfileHeroTagsHtml(u.adminTags)}
              ${memberLevelHeroBadgeHtml(memberLevel)}
              <p class="mt-2 text-sm text-slate-400">
                <span class="font-mono text-slate-500">#${escapeHtml(String(u.id ?? ""))}</span>
                <span class="text-slate-600"> · </span>
                <span class="break-all text-slate-300">${escapeHtml(String(u.email ?? ""))}</span>
              </p>
            </div>
            <div class="admin-member-profile__balance-card">
              <p class="admin-member-profile__balance-label">Güncel puan</p>
              <p class="admin-member-profile__balance-value tabular-nums">${Number(u.coins) || 0}<span class="admin-member-profile__balance-unit"> coin</span></p>
            </div>
          </div>
        </div>
        <nav class="admin-member-profile__tabbar" role="tablist" aria-label="Profil bölümleri">
          ${tabNavHtml}
        </nav>
        <div class="admin-member-profile__panels px-4 pb-8 pt-4 sm:px-6">
          <section role="tabpanel" id="member-tab-panel-overview" aria-labelledby="member-tab-btn-overview"${ph("overview")} class="admin-member-profile__panel">${overviewBlock}</section>
          <section role="tabpanel" id="member-tab-panel-profile-info" aria-labelledby="member-tab-btn-profile-info"${ph("profile-info")} class="admin-member-profile__panel">${profileInfoBlock}</section>
          <section role="tabpanel" id="member-tab-panel-ip" aria-labelledby="member-tab-btn-ip"${ph("ip")} class="admin-member-profile__panel">
            <p class="admin-member-profile__panel-kicker">IP adresleri</p>
            ${ipBlock}
          </section>
          <section role="tabpanel" id="member-tab-panel-wheel" aria-labelledby="member-tab-btn-wheel"${ph("wheel")} class="admin-member-profile__panel">${wheelBlock}</section>
          <section role="tabpanel" id="member-tab-panel-telegram" aria-labelledby="member-tab-btn-telegram"${ph("telegram")} class="admin-member-profile__panel">${telegramBlock}</section>
          <section role="tabpanel" id="member-tab-panel-crypto" aria-labelledby="member-tab-btn-crypto"${ph("crypto")} class="admin-member-profile__panel">
            <p class="admin-member-profile__panel-kicker">Cüzdan adresleri</p>
            <div class="mt-3 grid gap-2 sm:grid-cols-2 lg:grid-cols-4">${cryptoGrid}</div>
          </section>
          <section role="tabpanel" id="member-tab-panel-sponsor" aria-labelledby="member-tab-btn-sponsor"${ph("sponsor")} class="admin-member-profile__panel">
            <p class="admin-member-profile__panel-kicker">Sponsor hesap bilgileri</p>
            <div class="mt-3">${sponsorBlock}</div>
          </section>
          <section role="tabpanel" id="member-tab-panel-purchases" aria-labelledby="member-tab-btn-purchases"${ph("purchases")} class="admin-member-profile__panel">
            <p class="admin-member-profile__panel-kicker">Satın alınanlar</p>
            <div class="mt-3">${prBlock}</div>
          </section>
          <section role="tabpanel" id="member-tab-panel-promo" aria-labelledby="member-tab-btn-promo"${ph("promo")} class="admin-member-profile__panel">
            <p class="admin-member-profile__panel-kicker">Promo kod kullanımları</p>
            <div class="mt-3">${promoBlock}</div>
          </section>
          <section role="tabpanel" id="member-tab-panel-tickets" aria-labelledby="member-tab-btn-tickets"${ph("tickets")} class="admin-member-profile__panel">
            <p class="admin-member-profile__panel-kicker">Çekiliş biletleri ve talepler</p>
            <div class="mt-3">${ticketsBlock}</div>
          </section>
          <section role="tabpanel" id="member-tab-panel-blackjack" aria-labelledby="member-tab-btn-blackjack"${ph("blackjack")} class="admin-member-profile__panel">
            <p class="admin-member-profile__panel-kicker">Blackjack — oyun geçmişi</p>
            <div class="mt-3">${bjBlock}</div>
          </section>
          <section role="tabpanel" id="member-tab-panel-slot" aria-labelledby="member-tab-btn-slot"${ph("slot")} class="admin-member-profile__panel">
            <p class="admin-member-profile__panel-kicker">Mocker Slot — oyun geçmişi</p>
            <div class="mt-3">${slotBlock}</div>
          </section>
        </div>
      </div>
    </div>`;
}

function socialCardHtml(s, i) {
  const href = String(s.href || "").trim();
  const hrefShort = href.length > 48 ? href.slice(0, 48) + "…" : href || "—";
  const presetBtns = Object.keys(ICON_PRESETS)
    .map(
      (k) =>
        `<button type="button" class="admin-cms-card__preset preset-icon" data-preset="${k}">${escapeHtml(k)}</button>`
    )
    .join("");
  return `
    <article class="admin-cms-card social-card-edit" data-id="${escapeHtml(s.id)}">
      <div class="admin-cms-card__top">
        <button type="button" class="admin-cms-card__drag drag-handle-social" title="Sırayı değiştir" aria-label="Sürükle">${ADMIN_CMS_DRAG_ICON}</button>
        ${socialIconPreviewHtml(s.icon, s.name)}
        <div class="admin-cms-card__content">
          <span class="admin-cms-card__eyebrow">Link ${i + 1}</span>
          <h3 class="admin-cms-card__title">${escapeHtml(s.name || "Başlıksız")}</h3>
          <p class="admin-cms-card__code">${escapeHtml(hrefShort)}</p>
        </div>
      </div>
      <div class="admin-cms-card__body">
        <div class="admin-cms-card__fields">
          <div class="admin-cms-card__field">
            <label class="admin-label">Kimlik (id)</label>
            <input class="admin-input fld-sid font-mono text-sm" value="${escapeHtml(s.id)}" autocomplete="off" />
          </div>
          <div class="admin-cms-card__field">
            <label class="admin-label">Görünen ad</label>
            <input class="admin-input fld-sname" value="${escapeHtml(s.name)}" autocomplete="off" />
          </div>
          <div class="admin-cms-card__field admin-cms-card__field--full">
            <label class="admin-label">URL</label>
            <input class="admin-input fld-shref" value="${escapeHtml(s.href)}" autocomplete="off" />
          </div>
          <div class="admin-cms-card__field admin-cms-card__field--full">
            <label class="admin-label">SVG içeriği (path vb.)</label>
            <textarea class="admin-input fld-sicon admin-cms-modal__textarea admin-cms-modal__textarea--mono" rows="4">${escapeHtml(s.icon || "")}</textarea>
          </div>
        </div>
        <div class="admin-cms-card__presets">
          <span class="admin-cms-card__presets-label">Hızlı ikon</span>
          <div class="admin-cms-card__presets-row">${presetBtns}</div>
        </div>
      </div>
      <div class="admin-cms-card__actions">
        <button type="button" class="del-social admin-action-btn admin-action-btn--reject">Sil</button>
      </div>
    </article>`;
}

function socialPanelHtml() {
  const list = state.social || [];
  const cards = list.map((s, i) => socialCardHtml(s, i)).join("");
  return `
    <div class="admin-cms-page">
      <header class="admin-data-page__head">
        <h2 class="admin-data-page__title">Sosyal linkler</h2>

      </header>
      ${cmsPanelToolbarHtml(`<span class="admin-data-count"><strong>${list.length}</strong> bağlantı</span>${cmsToolbarAddBtnHtml("btn-add-social", "Link ekle")}`)}
      <div id="social-list" class="admin-cms-grid">
        ${cards || `<p class="admin-cms-empty admin-empty admin-empty--muted">Henüz sosyal link yok. <strong>Link ekle</strong> ile başlayın.</p>`}
      </div>
    </div>`;
}

function siteLocaleFieldsHtml(pre, label, loc) {
  const L = loc || {};
  return `
        <article class="admin-cms-card admin-cms-card--settings">
          <header class="admin-cms-card__section-head">
            <h3 class="admin-cms-card__section-title">${escapeHtml(label)}</h3>
            <p class="admin-cms-card__section-lead">İletişim sayfası metinleri</p>
          </header>
          <div class="admin-cms-card__body">
            <div class="admin-cms-card__fields admin-cms-card__fields--2col">
              <div class="admin-cms-card__field admin-cms-card__field--full">
                <label class="admin-label" for="site-${pre}-telegram">Telegram URL</label>
                <input id="site-${pre}-telegram" class="admin-input" value="${escapeHtml(L.telegramUrl || "")}" autocomplete="off" />
              </div>
              <div class="admin-cms-card__field admin-cms-card__field--full">
                <label class="admin-label" for="site-${pre}-intro">Üst paragraf</label>
                <textarea id="site-${pre}-intro" class="admin-input admin-cms-modal__textarea" rows="3">${escapeHtml(L.contactIntro || "")}</textarea>
              </div>
              <div class="admin-cms-card__field">
                <label class="admin-label" for="site-${pre}-eyebrow">Üst rozet</label>
                <input id="site-${pre}-eyebrow" class="admin-input" value="${escapeHtml(L.contactEyebrow || "")}" autocomplete="off" />
              </div>
              <div class="admin-cms-card__field">
                <label class="admin-label" for="site-${pre}-heading">Sayfa başlığı (H1)</label>
                <input id="site-${pre}-heading" class="admin-input" value="${escapeHtml(L.contactHeading || "")}" autocomplete="off" />
              </div>
              <div class="admin-cms-card__field">
                <label class="admin-label" for="site-${pre}-ctitle">Kart başlığı</label>
                <input id="site-${pre}-ctitle" class="admin-input" value="${escapeHtml(L.contactCardTitle || "")}" autocomplete="off" />
              </div>
              <div class="admin-cms-card__field">
                <label class="admin-label" for="site-${pre}-csub">Kart alt metni</label>
                <input id="site-${pre}-csub" class="admin-input" value="${escapeHtml(L.contactCardSubtitle || "")}" autocomplete="off" />
              </div>
              <div class="admin-cms-card__field admin-cms-card__field--full">
                <label class="admin-label" for="site-${pre}-cbtn">Buton yazısı</label>
                <input id="site-${pre}-cbtn" class="admin-input" value="${escapeHtml(L.contactButton || "")}" autocomplete="off" />
              </div>
            </div>
          </div>
        </article>`;
}

const LEGAL_PAGE_DEFS = [
  { slug: "privacy", label: "Gizlilik ve Güvenlik", path: "#/gizlilik-guvenlik" },
  { slug: "terms", label: "Kurallar ve Şartlar", path: "#/kurallar-sartlar" },
  { slug: "cookies", label: "Çerez Politikası", path: "#/cerez-politikasi" },
];

const LEGAL_SLUGS = LEGAL_PAGE_DEFS.map((d) => d.slug);

function emptyLegalLocale() {
  return { title: "", intro: "", sections: [] };
}

function emptyLegalPages() {
  const o = {};
  for (const slug of LEGAL_SLUGS) {
    o[slug] = { locales: { tr: emptyLegalLocale(), en: emptyLegalLocale() } };
  }
  return o;
}

function normalizeLegalLocale(loc) {
  const sections = Array.isArray(loc?.sections) ? loc.sections : [];
  if (!sections.length && Array.isArray(loc?.paragraphs)) {
    for (const p of loc.paragraphs) {
      const body = String(p || "").trim();
      if (body) sections.push({ title: "", body });
    }
  }
  return {
    title: String(loc?.title || "").trim(),
    intro: String(loc?.intro || "").trim(),
    sections: sections
      .map((s) => ({
        title: String(s?.title || "").trim(),
        body: String(s?.body || "").trim(),
      }))
      .filter((s) => s.title || s.body),
  };
}

function normalizeLegalPagesForAdmin(raw) {
  const base = emptyLegalPages();
  if (!raw || typeof raw !== "object") return base;
  for (const slug of LEGAL_SLUGS) {
    const bucket = raw[slug];
    if (!bucket?.locales || typeof bucket.locales !== "object") continue;
    for (const loc of ["tr", "en"]) {
      base[slug].locales[loc] = normalizeLegalLocale(bucket.locales[loc]);
    }
  }
  return base;
}

function legalSectionRowHtml(slug, locale, idx, sec) {
  return `
    <div class="admin-legal-section admin-inset admin-inset--compact space-y-2 p-3" data-legal-section>
      <div class="flex items-center justify-between gap-2">
        <span class="text-[10px] font-bold uppercase tracking-wider text-slate-500">Bölüm ${idx + 1}</span>
        <button type="button" class="admin-btn admin-btn-ghost px-2 py-1 text-xs text-red-300 btn-legal-rm-section" data-slug="${slug}" data-locale="${locale}" data-idx="${idx}">Sil</button>
      </div>
      <div>
        <label class="admin-label">Bölüm başlığı</label>
        <input type="text" class="admin-input legal-sec-title" value="${escapeHtml(sec.title || "")}" maxlength="120" autocomplete="off" />
      </div>
      <div>
        <label class="admin-label">Bölüm metni</label>
        <textarea class="admin-input legal-sec-body min-h-[88px] resize-y" rows="4" maxlength="8000">${escapeHtml(sec.body || "")}</textarea>
      </div>
    </div>`;
}

function legalPageSectionsHtml(slug, locale, sections) {
  const arr = Array.isArray(sections) ? sections : [];
  if (!arr.length) {
    return `<p class="text-xs text-slate-500">Henüz bölüm yok. «Bölüm ekle» ile başlayın.</p>`;
  }
  return arr.map((sec, idx) => legalSectionRowHtml(slug, locale, idx, sec)).join("");
}

function legalLocaleBlockHtml(slug, locale, label, loc) {
  const L = loc || emptyLegalLocale();
  return `
    <article class="admin-cms-card admin-cms-card--settings">
      <header class="admin-cms-card__section-head">
        <h3 class="admin-cms-card__section-title">${escapeHtml(label)}</h3>
      </header>
      <div class="admin-cms-card__body space-y-3">
        <div>
          <label class="admin-label" for="legal-${slug}-${locale}-title">Sayfa başlığı</label>
          <input id="legal-${slug}-${locale}-title" class="admin-input" value="${escapeHtml(L.title || "")}" maxlength="200" autocomplete="off" />
        </div>
        <div>
          <label class="admin-label" for="legal-${slug}-${locale}-intro">Kısa özet (hero altı)</label>
          <textarea id="legal-${slug}-${locale}-intro" class="admin-input min-h-[72px] resize-y" rows="2" maxlength="500">${escapeHtml(L.intro || "")}</textarea>
        </div>
        <div class="space-y-2">
          <div class="flex flex-wrap items-center justify-between gap-2">
            <p class="text-sm font-semibold text-slate-400">İçerik bölümleri</p>
            <button type="button" class="admin-btn admin-btn-ghost px-3 py-1.5 text-xs btn-legal-add-section" data-slug="${slug}" data-locale="${locale}">+ Bölüm ekle</button>
          </div>
          <div id="legal-${slug}-${locale}-sections" class="space-y-2">
            ${legalPageSectionsHtml(slug, locale, L.sections)}
          </div>
        </div>
      </div>
    </article>`;
}

function legalPagesPanelHtml(data) {
  const pages = normalizeLegalPagesForAdmin(data);
  const active = LEGAL_SLUGS.includes(state.legalPagesActiveSlug) ? state.legalPagesActiveSlug : "privacy";
  const tabs = LEGAL_PAGE_DEFS.map((d) => {
    const on = d.slug === active;
    return `<button type="button" class="admin-ticket-tab legal-page-tab${on ? " admin-ticket-tab--active" : ""}" data-legal-slug="${d.slug}">${escapeHtml(d.label)}</button>`;
  }).join("");
  const panels = LEGAL_PAGE_DEFS.map((d) => {
    const bucket = pages[d.slug] || { locales: { tr: emptyLegalLocale(), en: emptyLegalLocale() } };
    const hidden = d.slug !== active ? " hidden" : "";
    return `
      <div id="legal-panel-${d.slug}" class="legal-page-panel space-y-4${hidden}" data-legal-panel="${d.slug}">
        <p class="text-xs text-slate-500">Site önizleme: <a href="${d.path}" target="_blank" rel="noopener" class="text-accent-bright hover:underline">${escapeHtml(d.path)}</a></p>
        <div class="admin-cms-card__fields admin-cms-card__fields--2col admin-cms-card__fields--locales">
          ${legalLocaleBlockHtml(d.slug, "tr", "Türkçe (TR)", bucket.locales?.tr)}
          ${legalLocaleBlockHtml(d.slug, "en", "English (EN)", bucket.locales?.en)}
        </div>
      </div>`;
  }).join("");
  return `
    <div class="admin-cms-page max-w-5xl">
      <header class="admin-data-page__head">
        <h2 class="admin-data-page__title">Yasal sayfalar</h2>
        <p class="mt-1 text-sm text-slate-500">Footer’daki Gizlilik, Kurallar ve Çerez sayfaları. <strong>Değişiklikleri kaydet</strong> ile <code class="admin-code">data/legal-pages.json</code> dosyasına yazılır.</p>
      </header>
      ${cmsPanelToolbarHtml()}
      <div class="admin-ticket-tabs flex flex-wrap gap-1 pb-2">${tabs}</div>
      <div class="admin-panel admin-panel--padded space-y-4">${panels}</div>
    </div>`;
}

function readLegalPagesFromDomIfVisible() {
  if (!$("#legal-privacy-tr-title")) return;
  const out = emptyLegalPages();
  for (const slug of LEGAL_SLUGS) {
    for (const loc of ["tr", "en"]) {
      out[slug].locales[loc].title = $(`#legal-${slug}-${loc}-title`)?.value.trim() || "";
      out[slug].locales[loc].intro = $(`#legal-${slug}-${loc}-intro`)?.value.trim() || "";
      const wrap = $(`#legal-${slug}-${loc}-sections`);
      if (!wrap) continue;
      const sections = [];
      wrap.querySelectorAll("[data-legal-section]").forEach((el) => {
        const title = el.querySelector(".legal-sec-title")?.value.trim() || "";
        const body = el.querySelector(".legal-sec-body")?.value.trim() || "";
        if (title || body) sections.push({ title, body });
      });
      out[slug].locales[loc].sections = sections;
    }
  }
  state.legalPages = out;
}

function bindLegalPagesPanel() {
  $$(".legal-page-tab").forEach((btn) => {
    if (btn.dataset.legalTabBound === "1") return;
    btn.dataset.legalTabBound = "1";
    btn.addEventListener("click", () => {
      readLegalPagesFromDomIfVisible();
      state.legalPagesActiveSlug = btn.getAttribute("data-legal-slug") || "privacy";
      renderDashboard();
    });
  });
  $$(".btn-legal-add-section").forEach((btn) => {
    if (btn.dataset.legalAddBound === "1") return;
    btn.dataset.legalAddBound = "1";
    btn.addEventListener("click", () => {
      readLegalPagesFromDomIfVisible();
      const slug = btn.getAttribute("data-slug") || "privacy";
      const locale = btn.getAttribute("data-locale") || "tr";
      const pages = normalizeLegalPagesForAdmin(state.legalPages);
      const loc = pages[slug]?.locales?.[locale];
      if (!loc) return;
      if (!Array.isArray(loc.sections)) loc.sections = [];
      loc.sections.push({ title: "", body: "" });
      state.legalPages = pages;
      renderDashboard();
    });
  });
  $$(".btn-legal-rm-section").forEach((btn) => {
    if (btn.dataset.legalRmBound === "1") return;
    btn.dataset.legalRmBound = "1";
    btn.addEventListener("click", () => {
      readLegalPagesFromDomIfVisible();
      const slug = btn.getAttribute("data-slug") || "";
      const locale = btn.getAttribute("data-locale") || "";
      const idx = parseInt(btn.getAttribute("data-idx") || "-1", 10);
      const pages = normalizeLegalPagesForAdmin(state.legalPages);
      const secs = pages[slug]?.locales?.[locale]?.sections;
      if (!Array.isArray(secs) || idx < 0 || idx >= secs.length) return;
      secs.splice(idx, 1);
      state.legalPages = pages;
      renderDashboard();
    });
  });
}

function siteContactPanelHtml(si) {
  const s = normalizeSiteForAdmin(si || {});
  const yt = s.youtube || normalizeYoutubeForAdmin({});
  const ytMax = yt.maxVideos || 4;
  return `
    <div class="admin-cms-page">
      <header class="admin-data-page__head">
        <h2 class="admin-data-page__title">İletişim &amp; site</h2>

      </header>
      ${cmsPanelToolbarHtml()}
      <div class="admin-cms-settings-grid">
        <article class="admin-cms-card admin-cms-card--settings">
          <header class="admin-cms-card__section-head">
            <h3 class="admin-cms-card__section-title">Ana sayfa — sosyal medya akışı</h3>
            <p class="admin-cms-card__section-lead">Ana sayfadaki «Sosyal medya akışları» bölümü bu kanalın <strong>canlı yayın</strong> ve <strong>geçmiş yayın</strong> videolarını listeler (Shorts gösterilmez). En güvenilir sonuç için <code class="admin-code">api/config.local.php</code> içinde <code class="admin-code">MOCKER_YOUTUBE_API_KEY</code> tanımlayın.</p>
          </header>
          <div class="admin-cms-card__body">
            <div class="admin-cms-card__fields">
              <div class="admin-cms-card__field admin-cms-card__field--full">
                <label class="admin-label" for="site-youtube-channel">YouTube kanalı</label>
                <input id="site-youtube-channel" type="text" class="admin-input font-mono text-sm" value="${escapeHtml(youtubeAdminDisplayInput(yt))}" placeholder="https://www.youtube.com/@kanal veya @kullanici" autocomplete="off" />
                <p class="admin-cms-modal__hint">Kanal linki, @kullanıcı adı veya <code>UC…</code> kanal kimliği. Kaydettikten sonra önbellek temizlenir; ana sayfa kısa sürede güncellenir.</p>
              </div>
              <div class="admin-cms-card__field">
                <label class="admin-label" for="site-youtube-max">Listelenecek video sayısı</label>
                <select id="site-youtube-max" class="admin-input">
                  ${[4, 6, 8, 12]
                    .map((n) => `<option value="${n}"${ytMax === n ? " selected" : ""}>${n} video</option>`)
                    .join("")}
                </select>
              </div>
            </div>
            <p id="site-youtube-status" class="admin-cms-modal__hint mt-3" hidden role="status"></p>
            <button type="button" id="btn-site-youtube-preview" class="admin-btn admin-btn-soft mt-3">Kaydedilmiş akışı kontrol et</button>
          </div>
        </article>
        <article class="admin-cms-card admin-cms-card--settings">
          <header class="admin-cms-card__section-head">
            <h3 class="admin-cms-card__section-title">Logo ve favicon</h3>
            <p class="admin-cms-card__section-lead">Boş bırakırsan varsayılanlar kullanılır. OG görseli boşsa site logosu sosyal önizlemede kullanılır (1200×630 önerilir).</p>
          </header>
          <div class="admin-cms-card__body">
            <div class="admin-cms-card__fields">
              <div class="admin-cms-card__field">
                <label class="admin-label" for="site-brand-logo-url">Site logosu</label>
                <input id="site-brand-logo-url" type="url" class="admin-input" value="${escapeHtml(s.logoUrl || "")}" placeholder="https://example.com/logo.png" autocomplete="off" />
              </div>
              <div class="admin-cms-card__field">
                <label class="admin-label" for="site-brand-favicon-url">Favicon</label>
                <input id="site-brand-favicon-url" type="url" class="admin-input" value="${escapeHtml(s.faviconUrl || "")}" placeholder="https://example.com/favicon.png" autocomplete="off" />
              </div>
              <div class="admin-cms-card__field">
                <label class="admin-label" for="site-brand-og-url">OG / sosyal paylaşım görseli</label>
                <input id="site-brand-og-url" type="url" class="admin-input" value="${escapeHtml(s.ogImageUrl || "")}" placeholder="https://example.com/og.png" autocomplete="off" />
              </div>
              <div class="admin-cms-card__field">
                <label class="admin-label" for="site-default-locale">Varsayılan dil (yeni ziyaretçi)</label>
                <select id="site-default-locale" class="admin-input">
                  <option value="tr" ${s.defaultLocale !== "en" ? "selected" : ""}>Türkçe</option>
                  <option value="en" ${s.defaultLocale === "en" ? "selected" : ""}>English</option>
                </select>
              </div>
            </div>
          </div>
        </article>
        ${siteLocaleFieldsHtml("tr", "Türkçe (TR)", s.locales?.tr)}
        ${siteLocaleFieldsHtml("en", "English (EN)", s.locales?.en)}
      </div>
    </div>`;
}

function maintenancePanelHtml(mt) {
  const m = mt || normalizeMaintenanceForAdmin({});
  return `
    <div class="admin-cms-page">
      <header class="admin-data-page__head">
        <h2 class="admin-data-page__title">Bakım modu</h2>

      </header>
      <div class="admin-cms-toolbar">
        <span class="admin-data-count">Durum: <strong>${m.enabled ? "Aktif" : "Kapalı"}</strong></span>
        <span class="admin-status-pill ${m.enabled ? "admin-status-pill--on" : ""}">${m.enabled ? "Ziyaretçilere gösteriliyor" : "Site normal"}</span>
        ${cmsPanelSaveBtnHtml()}
      </div>
      <article class="admin-cms-card admin-cms-card--settings ${m.enabled ? "admin-cms-card--maintenance-on" : ""}">
        <header class="admin-cms-card__section-head">
          <h3 class="admin-cms-card__section-title">Bakım ayarları</h3>
          <p class="admin-cms-card__section-lead">Kutuyu işaretleyip kaydettiğinde anında uygulanır.</p>
        </header>
        <div class="admin-cms-card__body">
          <label class="admin-tg-settings__toggle admin-cms-modal__toggle">
            <input type="checkbox" id="site-maintenance-enabled" class="admin-checkbox"${m.enabled ? " checked" : ""} />
            Bakım modunu aç (ziyaretçilere göster)
          </label>
          <div class="admin-cms-card__fields">
            <div class="admin-cms-card__field admin-cms-card__field--full">
              <label class="admin-label" for="site-maintenance-image-url">Bakım görseli (https)</label>
              <input id="site-maintenance-image-url" type="url" class="admin-input" value="${escapeHtml(m.imageUrl || "")}" placeholder="https://example.com/maintenance.png" autocomplete="off" />
              <p class="admin-cms-modal__hint">Boş bırakırsan site logosu kullanılır.</p>
            </div>
          </div>
          <div class="admin-cms-card__fields admin-cms-card__fields--2col admin-cms-card__fields--locales">
            <div class="admin-cms-card__locale-block">
              <p class="admin-cms-card__locale-label">Türkçe metin</p>
              <div class="admin-cms-card__field">
                <label class="admin-label" for="site-maint-tr-eyebrow">Üst etiket</label>
                <input id="site-maint-tr-eyebrow" class="admin-input" value="${escapeHtml(m.locales?.tr?.eyebrow || "")}" placeholder="Bakım modu" autocomplete="off" />
              </div>
              <div class="admin-cms-card__field">
                <label class="admin-label" for="site-maint-tr-title">Başlık</label>
                <input id="site-maint-tr-title" class="admin-input" value="${escapeHtml(m.locales?.tr?.title || "")}" placeholder="Site bakımda" autocomplete="off" />
              </div>
              <div class="admin-cms-card__field">
                <label class="admin-label" for="site-maint-tr-message">Açıklama</label>
                <textarea id="site-maint-tr-message" class="admin-input admin-cms-modal__textarea" rows="3">${escapeHtml(m.locales?.tr?.message || "")}</textarea>
              </div>
            </div>
            <div class="admin-cms-card__locale-block">
              <p class="admin-cms-card__locale-label">English copy</p>
              <div class="admin-cms-card__field">
                <label class="admin-label" for="site-maint-en-eyebrow">Eyebrow</label>
                <input id="site-maint-en-eyebrow" class="admin-input" value="${escapeHtml(m.locales?.en?.eyebrow || "")}" placeholder="Maintenance" autocomplete="off" />
              </div>
              <div class="admin-cms-card__field">
                <label class="admin-label" for="site-maint-en-title">Title</label>
                <input id="site-maint-en-title" class="admin-input" value="${escapeHtml(m.locales?.en?.title || "")}" placeholder="Site under maintenance" autocomplete="off" />
              </div>
              <div class="admin-cms-card__field">
                <label class="admin-label" for="site-maint-en-message">Message</label>
                <textarea id="site-maint-en-message" class="admin-input admin-cms-modal__textarea" rows="3">${escapeHtml(m.locales?.en?.message || "")}</textarea>
              </div>
            </div>
          </div>
        </div>
      </article>
    </div>`;
}

function readSocialFromDom() {
  return $$(".social-card-edit").map((card) => ({
    id: $(".fld-sid", card).value.trim(),
    name: $(".fld-sname", card).value.trim(),
    href: $(".fld-shref", card).value.trim(),
    icon: $(".fld-sicon", card).value,
  }));
}
function getFilteredMembers() {
  const raw = state.members || [];
  const minStr = (state.membersFilterMinCoins || "").trim();
  const maxStr = (state.membersFilterMaxCoins || "").trim();
  const minC = minStr === "" ? null : parseInt(minStr, 10);
  const maxC = maxStr === "" ? null : parseInt(maxStr, 10);
  const external = isMembersExternalSource();

  let list = raw.filter((u) => String(u.username || "").toLowerCase() !== "__chat_system");
  if (!external) {
    const q = (state.membersFilter || "").trim().toLowerCase();
    if (q) {
      list = list.filter((u) => {
        const id = String(u.id);
        const un = String(u.username || "").toLowerCase();
        const em = String(u.email || "").toLowerCase();
        const rip = String(u.registerIp || "").toLowerCase();
        const lip = String(u.lastLoginIp || "").toLowerCase();
        const note = String(u.adminNote || "").toLowerCase();
        return (
          id.includes(q) || un.includes(q) || em.includes(q) || rip.includes(q) || lip.includes(q) || note.includes(q)
        );
      });
    }
  }
  if (minC !== null && !Number.isNaN(minC)) {
    list = list.filter((u) => (Number(u.coins) || 0) >= minC);
  }
  if (maxC !== null && !Number.isNaN(maxC)) {
    list = list.filter((u) => (Number(u.coins) || 0) <= maxC);
  }
  if (external) {
    return list;
  }

  const shop = state.membersFilterShop || "all";
  const multiIp = state.membersFilterMultiIp || "all";
  if (shop === "yes") {
    list = list.filter((u) => (Number(u.purchaseCount) || 0) > 0);
  } else if (shop === "no") {
    list = list.filter((u) => (Number(u.purchaseCount) || 0) === 0);
  }
  if (multiIp === "yes") {
    list = list.filter((u) => (Number(u.sameRegisterIpCount) || 0) > 1);
  } else if (multiIp === "no") {
    list = list.filter((u) => (Number(u.sameRegisterIpCount) || 0) <= 1);
  }
  const ft = state.membersFilterTags || { suspicious: false, vip: false, banned: false };
  const activeTagKeys = [];
  if (ft.suspicious) activeTagKeys.push("suspicious");
  if (ft.vip) activeTagKeys.push("vip");
  if (ft.banned) activeTagKeys.push("banned");
  if (activeTagKeys.length > 0) {
    list = list.filter((u) => {
      const tags = Array.isArray(u.adminTags) ? u.adminTags : [];
      return activeTagKeys.some((k) => tags.includes(k));
    });
  }
  return list;
}

function hasMemberFiltersActive() {
  const base =
    (state.membersFilter || "").trim() !== "" ||
    (state.membersFilterMinCoins || "").trim() !== "" ||
    (state.membersFilterMaxCoins || "").trim() !== "";
  if (isMembersExternalSource()) {
    return base;
  }
  return (
    base ||
    (state.membersFilterShop && state.membersFilterShop !== "all") ||
    (state.membersFilterMultiIp && state.membersFilterMultiIp !== "all") ||
    !!(state.membersFilterTags && (state.membersFilterTags.suspicious || state.membersFilterTags.vip || state.membersFilterTags.banned))
  );
}

function membersExternalFiltersIgnored() {
  if (!isMembersExternalSource()) return false;
  const shop = state.membersFilterShop || "all";
  const multiIp = state.membersFilterMultiIp || "all";
  const ft = state.membersFilterTags || { suspicious: false, vip: false, banned: false };
  return (
    shop !== "all" ||
    multiIp !== "all" ||
    ft.suspicious ||
    ft.vip ||
    ft.banned
  );
}

function membersLoadedRowCount() {
  return getFilteredMembers().length;
}

/** Üye KPI’larında sistem hesabı sayılmaz. */
function membersForKpis() {
  return (state.members || []).filter((u) => String(u.username || "").toLowerCase() !== "__chat_system");
}

function computeMembersKpis() {
  const list = membersForKpis();
  let totalCoins = 0;
  let active = 0;
  let withShop = 0;
  for (const u of list) {
    totalCoins += Number(u.coins) || 0;
    if (u.isActive !== false) active++;
    if ((Number(u.purchaseCount) || 0) > 0) withShop++;
  }
  const total = list.length;
  const filtered = membersLoadedRowCount();
  const avgCoin = total > 0 ? Math.round(totalCoins / total) : 0;
  return { total, totalCoins, active, inactive: total - active, withShop, filtered, avgCoin };
}

function membersKpiCard(label, value, hint, tone, iconKind, highlight = false) {
  const hl = highlight ? " admin-dash-stat--highlight" : "";
  const toneCls = highlight ? "" : ` admin-dash-stat--tone-${tone}`;
  const iconWrap = highlight ? " admin-dash-kpi-icon-wrap--on-dark" : "";
  return `<div class="admin-dash-stat admin-dash-stat--accent-line${toneCls}${hl}">
    <div class="admin-dash-stat__inner">
      <div class="admin-dash-kpi-icon-wrap${iconWrap}">${dashKpiIcon(iconKind)}</div>
      <div class="admin-dash-stat__body">
        <p class="admin-dash-stat-label">${escapeHtml(label)}</p>
        <p class="admin-dash-stat-value">${formatDashMetric(value)}</p>
        ${hint ? `<p class="admin-dash-stat-hint">${escapeHtml(hint)}</p>` : ""}
      </div>
    </div>
  </div>`;
}

function membersLoadStatsBannerHtml() {
  const fetched = membersForKpis().length;
  const load = state.membersLoadInfo;
  const total = load && Number(load.total) > 0 ? Number(load.total) : fetched;
  const pagesDone = load ? Number(load.pages_fetched) || 0 : 0;
  const pagesTotal = load ? Number(load.last_page) || 0 : 0;

  if (state.membersLoading) {
    const syncedToDb = load && Number(load.fetched) > 0 ? Number(load.fetched) : fetched;
    const pct = total > 0 ? Math.min(100, Math.round((syncedToDb / total) * 100)) : null;
    return `<section class="admin-members-kpi admin-members-kpi--loading" role="status" aria-live="polite">
      <div class="admin-members-kpi__loading">
        <span class="admin-members-kpi__spinner" aria-hidden="true"></span>
        <div>
          <p class="admin-members-kpi__loading-title">Üyeler yükleniyor</p>
          <p class="admin-members-kpi__loading-meta">${pagesTotal > 0 ? `Sayfa ${pagesDone} / ${pagesTotal}` : "Veritabanından okunuyor…"}${pct !== null ? ` · %${pct}` : ""}</p>
        </div>
      </div>
    </section>`;
  }

  if (state.membersExternalError && fetched === 0) {
    return `<section class="admin-members-kpi admin-members-kpi--error" role="alert">
      <p class="admin-members-kpi__error-text">Üyeler yüklenemedi: ${escapeHtml(String(state.membersExternalError))}</p>
      <button type="button" id="members-reload-btn" class="admin-btn admin-btn--sm admin-btn--ghost">Tekrar dene</button>
    </section>`;
  }

  if (fetched === 0) {
    return `<section class="admin-members-kpi admin-members-kpi--error" role="alert">
      <p class="admin-members-kpi__error-text">Henüz üye kaydı yok.${state.membersExternalError ? ` ${escapeHtml(String(state.membersExternalError))}` : ""}</p>
      <button type="button" id="members-reload-btn" class="admin-btn admin-btn--sm admin-btn--ghost">Yenile</button>
    </section>`;
  }

  const sync = state.membersSync;
  const apiOn = !!(sync && sync.externalEnabled);
  const complete = apiOn ? !!(sync?.syncComplete || (load && load.complete)) : true;
  const missing = apiOn ? membersMissingCount() : 0;
  const kpi = computeMembersKpis();
  const filteredHint =
    kpi.filtered !== kpi.total
      ? `Filtre sonucu · toplam ${kpi.total.toLocaleString("tr-TR")} kayıt`
      : "Tüm kayıtlı üyeler";
  const resumeBtn =
    apiOn && !complete && !state.membersLoading
      ? `<div class="admin-members-kpi__actions">
          <button type="button" id="members-resume-btn" class="admin-btn admin-btn--sm">Kalanı senkronize et (${missing.toLocaleString("tr-TR")})</button>
          <button type="button" id="members-fresh-btn" class="admin-btn admin-btn--sm admin-btn--ghost">Baştan senkronize</button>
        </div>`
      : "";

  return `<section class="admin-members-kpi" aria-label="Üye özeti">
    <div class="admin-dash-kpi-grid admin-dash-kpi-grid--4 admin-members-kpi__grid">
      ${membersKpiCard("Toplam kayıtlı üye", kpi.total, "Veritabanındaki üye sayısı", "sky", "users", true)}
      ${membersKpiCard("Toplam coin", kpi.totalCoins, "Tüm üye cüzdanları toplamı", "amber", "coins")}
      ${membersKpiCard("Aktif hesap", kpi.active, `${kpi.inactive.toLocaleString("tr-TR")} pasif`, "emerald", "users")}
      ${membersKpiCard("Tabloda gösterilen", kpi.filtered, filteredHint, "violet", "activity")}
    </div>
    ${resumeBtn}
  </section>`;
}

function getIpClashGroups() {
  const map = new Map();
  for (const u of state.members || []) {
    const ip = String(u.registerIp || "").trim();
    if (!ip) continue;
    if (!map.has(ip)) map.set(ip, []);
    map.get(ip).push(u);
  }
  const q = (state.ipClashQuery || "").trim().toLowerCase();

  let groups = [];
  for (const [ip, users] of map.entries()) {
    if (!Array.isArray(users) || users.length < 2) continue;
    if (q) {
      const matchIp = ip.toLowerCase().includes(q);
      const matchUser = users.some((u) => String(u.username || "").toLowerCase().includes(q));
      if (!matchIp && !matchUser) continue;
    }
    groups.push({ ip, users: [...users].sort((a, b) => (b.createdAt || 0) - (a.createdAt || 0)) });
  }
  groups.sort((a, b) => b.users.length - a.users.length || a.ip.localeCompare(b.ip, "tr"));
  return groups;
}

function ipClashesPanelHtml() {
  const groups = getIpClashGroups();
  const q = state.ipClashQuery || "";
  const totalUsers = groups.reduce((sum, g) => sum + g.users.length, 0);
  const cards = groups
    .map((g) => {
      const rows = g.users
        .map((u) => {
          const active = !!u.isActive;
          return `<tr>
            <td class="admin-data-td--user">${escapeHtml(String(u.username || ""))}</td>
            <td class="admin-data-td--muted">${escapeHtml(String(u.email || ""))}</td>
            <td class="admin-data-td--coin">${Number(u.coins) || 0}</td>
            <td class="admin-data-td--muted">${formatTs(u.createdAt)}</td>
            <td>${adminActiveBadge(active)}</td>
            <td class="admin-data-td--actions">${adminToggleUserBtn(active, u.id)}</td>
          </tr>`;
        })
        .join("");
      return `<article class="admin-ip-card">
        <div class="admin-ip-card__head">
          <div class="min-w-0">
            <p class="admin-ip-card__label">Kayıt IP</p>
            <p class="admin-ip-card__ip">${escapeHtml(g.ip)}</p>
          </div>
          <span class="admin-badge admin-badge--warn">${g.users.length} hesap</span>
        </div>
        <div class="admin-table-wrap admin-data-table-wrap">
          <table class="admin-data-table" style="min-width:760px">
            <thead><tr>
              <th>Kullanıcı</th><th>E-posta</th><th>Coin</th><th>Kayıt</th><th>Durum</th><th class="text-right">İşlem</th>
            </tr></thead>
            <tbody>${rows}</tbody>
          </table>
        </div>
      </article>`;
    })
    .join("");
  const toolbar = adminListToolbar({
    searchId: "ip-clash-search",
    label: "Ara (IP / kullanıcı adı)",
    placeholder: "Örn: 31.155.134.162 veya kullanıcı adı",
    value: q,
    clearId: "ip-clash-search-clear",
    countText: `<strong>${groups.length}</strong> IP grubu · <strong>${totalUsers}</strong> hesap`,
  });
  return `<div class="admin-data-page">
    <header class="admin-data-page__head">
      <h2 class="admin-data-page__title">IP çakışmaları</h2>

    </header>
    ${toolbar}
    ${groups.length === 0
      ? `<p class="admin-empty admin-empty--muted">IP çakışması bulunamadı.</p>`
      : `<div class="admin-ip-grid">${cards}</div>`
    }
  </div>`;
}

function getFilteredMarketRequests() {
  const list = state.marketRequests || [];
  const q = (state.marketRequestsQuery || "").trim().toLowerCase();
  if (!q) return list;
  return list.filter((r) => {
    const id = String(r.id ?? "").toLowerCase();
    const uid = String(r.userId ?? "").toLowerCase();
    const username = String(r.username ?? "").toLowerCase();
    const email = String(r.email ?? "").toLowerCase();
    const itemId = String(r.itemId ?? "").toLowerCase();
    const itemTitle = String(r.itemTitle ?? "").toLowerCase();
    const status = String(r.status ?? "").toLowerCase();
    return (
      id.includes(q) ||
      uid.includes(q) ||
      username.includes(q) ||
      email.includes(q) ||
      itemId.includes(q) ||
      itemTitle.includes(q) ||
      status.includes(q)
    );
  });
}

function csvCell(v) {
  const s = String(v ?? "");
  if (/[",;\r\n]/.test(s)) {
    return `"${s.replace(/"/g, '""')}"`;
  }
  return s;
}

function downloadMembersCsv(rows, label) {
  const sep = ";";
  const header = [
    "ID",
    "Kullanıcı adı",
    "E-posta",
    "Etiketler",
    "İç not",
    "Kayıt IP",
    "Son giriş IP",
    "Aynı kayıt IP hesap sayısı",
    "Coin",
    "Kayıt tarihi",
    "Toplam harcama",
    "İşlem sayısı",
  ];
  const lines = [header.join(sep)];
  for (const u of rows) {
    const tagLabels = MEMBER_ADMIN_TAG_DEFS.filter((d) => (Array.isArray(u.adminTags) ? u.adminTags : []).includes(d.id))
      .map((d) => d.label)
      .join(", ");
    lines.push(
      [
        u.id,
        csvCell(u.username),
        csvCell(u.email),
        csvCell(tagLabels),
        csvCell(u.adminNote ?? ""),
        csvCell(u.registerIp ?? ""),
        csvCell(u.lastLoginIp ?? ""),
        Number(u.sameRegisterIpCount) || 0,
        Number(u.coins) || 0,
        csvCell(formatTs(u.createdAt)),
        Number(u.totalSpent) || 0,
        Number(u.purchaseCount) || 0,
      ].join(sep)
    );
  }
  const bom = "\uFEFF";
  const blob = new Blob([bom + lines.join("\r\n")], { type: "text/csv;charset=utf-8" });
  const a = document.createElement("a");
  a.href = URL.createObjectURL(blob);
  const safe = label.replace(/[^a-z0-9_-]+/gi, "-").toLowerCase();
  a.download = `Mocker-uyeler-${safe}-${new Date().toISOString().slice(0, 10)}.csv`;
  a.click();
  URL.revokeObjectURL(a.href);
  toast(`${rows.length} satır CSV indirildi (Excel ile açılır).`);
}

function downloadMarketRequestsCsv(rows) {
  const sep = ";";
  const header = ["Kayıt ID", "Tarih", "Üye ID", "Kullanıcı adı", "E-posta", "Ürün kodu", "Ürün adı", "Coin", "Durum"];
  const lines = [header.join(sep)];
  for (const r of rows) {
    lines.push(
      [
        r.id,
        csvCell(formatTs(r.createdAt)),
        r.userId,
        csvCell(r.username),
        csvCell(r.email),
        csvCell(r.itemId),
        csvCell(r.itemTitle),
        Number(r.price) || 0,
        csvCell(String(r.status || "pending")),
      ].join(sep)
    );
  }
  const bom = "\uFEFF";
  const blob = new Blob([bom + lines.join("\r\n")], { type: "text/csv;charset=utf-8" });
  const a = document.createElement("a");
  a.href = URL.createObjectURL(blob);
  a.download = `Mocker-market-talepleri-${new Date().toISOString().slice(0, 10)}.csv`;
  a.click();
  URL.revokeObjectURL(a.href);
  toast(`${rows.length} satır CSV indirildi.`);
}

function marketRequestsPanelHtml() {
  const list = getFilteredMarketRequests();
  const q = state.marketRequestsQuery || "";
  const toolbar = adminListToolbar({
    searchId: "market-requests-search",
    label: "Ara (kullanıcı / e-posta / ürün / durum)",
    placeholder: "Örn: kullanıcı adı, e-posta, ürün kodu…",
    value: q,
    clearId: "market-requests-search-clear",
    countText: `<strong>${list.length}</strong> talep listeleniyor`,
  });
  const rows = list
    .map((r) => {
      const status = String(r.status || "pending");
      return `<tr>
        <td class="admin-data-td--mono">${r.id}</td>
        <td class="admin-data-td--muted">${escapeHtml(formatTs(r.createdAt))}</td>
        <td class="admin-data-td--mono">${r.userId}</td>
        <td class="admin-data-td--user">${escapeHtml(String(r.username ?? ""))}</td>
        <td class="admin-data-td--muted">${escapeHtml(String(r.email ?? ""))}</td>
        <td class="admin-data-td--mono">${escapeHtml(String(r.itemId ?? ""))}</td>
        <td>${escapeHtml(String(r.itemTitle ?? ""))}</td>
        <td class="admin-data-td--coin">${Number(r.price) || 0}</td>
        <td>${adminWorkflowStatusBadge(status)}</td>
        <td class="admin-data-td--actions">${adminWorkflowActions(status, "btn-purchase-status", "data-purchase-id", r.id)}</td>
      </tr>`;
    })
    .join("");
  const table = list.length
    ? adminDataTableShell(
        `<th>Kayıt</th><th>Tarih</th><th>Üye</th><th>Kullanıcı</th><th>E-posta</th><th>Ürün kodu</th><th>Ürün</th><th>Coin</th><th>Durum</th><th>İşlem</th>`,
        rows,
        "1180px"
      )
    : `<p class="admin-empty admin-empty--muted">Aramaya uygun market talebi bulunamadı.</p>`;
  return `<div class="admin-data-page">
    <header class="admin-data-page__head">
      <h2 class="admin-data-page__title">Market talepleri</h2>

    </header>
    ${toolbar}
    ${table}
  </div>`;
}

function getFilteredComplaints() {
  const list = state.complaints || [];
  const q = (state.complaintsQuery || "").trim().toLowerCase();
  if (!q) return list;
  return list.filter((r) => {
    const id = String(r.id ?? "").toLowerCase();
    const uid = String(r.userId ?? "").toLowerCase();
    const username = String(r.username ?? "").toLowerCase();
    const email = String(r.email ?? "").toLowerCase();
    const category = String(r.category ?? "").toLowerCase();
    const sponsor = String(r.sponsorTitle || r.sponsorId || "").toLowerCase();
    const status = String(r.status ?? "").toLowerCase();
    const msg = String(r.message ?? "").toLowerCase();
    return (
      id.includes(q) ||
      uid.includes(q) ||
      username.includes(q) ||
      email.includes(q) ||
      category.includes(q) ||
      sponsor.includes(q) ||
      status.includes(q) ||
      msg.includes(q)
    );
  });
}

function complaintsPanelHtml() {
  const list = getFilteredComplaints();
  const q = state.complaintsQuery || "";
  const toolbar = adminListToolbar({
    searchId: "complaints-search",
    label: "Ara (kullanıcı / e-posta / sponsor / mesaj)",
    placeholder: "Kullanıcı, sponsor veya mesaj…",
    value: q,
    clearId: "complaints-search-clear",
    countText: `<strong>${list.length}</strong> kayıt listeleniyor`,
  });
  const rows = list
    .map((r) => {
      const status = String(r.status || "pending");
      const category = String(r.category || "other");
      return `<tr>
        <td class="admin-data-td--mono">${r.id}</td>
        <td class="admin-data-td--muted">${escapeHtml(formatTs(r.createdAt))}</td>
        <td class="admin-data-td--mono">${r.userId}</td>
        <td class="admin-data-td--user">${escapeHtml(String(r.username ?? ""))}</td>
        <td class="admin-data-td--muted">${escapeHtml(String(r.email ?? ""))}</td>
        <td>${adminCategoryBadge(category)}</td>
        <td class="admin-data-td--muted">${escapeHtml(String(r.sponsorTitle || r.sponsorId || "—"))}</td>
        <td class="admin-data-td--msg">${escapeHtml(String(r.message ?? ""))}</td>
        <td>${adminWorkflowStatusBadge(status)}</td>
        <td class="admin-data-td--actions">${adminWorkflowActions(status, "btn-complaint-status", "data-complaint-id", r.id)}</td>
      </tr>`;
    })
    .join("");
  const table = list.length
    ? adminDataTableShell(
        `<th>ID</th><th>Tarih</th><th>Üye ID</th><th>Kullanıcı</th><th>E-posta</th><th>Kategori</th><th>Sponsor</th><th>Mesaj</th><th>Durum</th><th>İşlem</th>`,
        rows,
        "1480px"
      )
    : `<p class="admin-empty admin-empty--muted">Kayıtlı şikayet/öneri bulunamadı.</p>`;
  return `<div class="admin-data-page">
    <header class="admin-data-page__head">
      <h2 class="admin-data-page__title">Şikayetler & öneriler</h2>

    </header>
    ${toolbar}
    ${table}
  </div>`;
}

function getFilteredAffiliateRequests() {
  const list = state.affiliateRequests || [];
  const q = (state.affiliateRequestsQuery || "").trim().toLowerCase();
  if (!q) return list;
  return list.filter((r) => {
    const id = String(r.id ?? "").toLowerCase();
    const uid = String(r.userId ?? "").toLowerCase();
    const username = String(r.username ?? "").toLowerCase();
    const email = String(r.email ?? "").toLowerCase();
    const sponsor = String(r.sponsorTitle || r.sponsorId || "").toLowerCase();
    const inv = String(r.investmentDate ?? "").toLowerCase();
    const status = String(r.status ?? "").toLowerCase();
    const msg = String(r.message ?? "").toLowerCase();
    return (
      id.includes(q) ||
      uid.includes(q) ||
      username.includes(q) ||
      email.includes(q) ||
      sponsor.includes(q) ||
      inv.includes(q) ||
      status.includes(q) ||
      msg.includes(q)
    );
  });
}

function affiliateRequestsPanelHtml() {
  const list = getFilteredAffiliateRequests();
  const q = state.affiliateRequestsQuery || "";
  const toolbar = adminListToolbar({
    searchId: "affiliate-requests-search",
    label: "Ara (kullanıcı / sponsor / açıklama)",
    placeholder: "Kullanıcı, sponsor veya açıklama…",
    value: q,
    clearId: "affiliate-requests-search-clear",
    countText: `<strong>${list.length}</strong> kayıt listeleniyor`,
  });
  const rows = list
    .map((r) => {
      const status = String(r.status || "pending");
      const inv = r.investmentDate ? escapeHtml(String(r.investmentDate)) : "—";
      return `<tr>
        <td class="admin-data-td--mono">${r.id}</td>
        <td class="admin-data-td--muted">${escapeHtml(formatTs(r.createdAt))}</td>
        <td class="admin-data-td--mono">${r.userId}</td>
        <td class="admin-data-td--user">${escapeHtml(String(r.username ?? ""))}</td>
        <td class="admin-data-td--muted">${escapeHtml(String(r.email ?? ""))}</td>
        <td class="admin-data-td--muted">${escapeHtml(String(r.sponsorTitle || r.sponsorId || "—"))}</td>
        <td class="admin-data-td--muted">${inv}</td>
        <td class="admin-data-td--msg">${escapeHtml(String(r.message ?? ""))}</td>
        <td>${adminWorkflowStatusBadge(status)}</td>
        <td class="admin-data-td--actions">${adminWorkflowActions(status, "btn-affiliate-status", "data-request-id", r.id)}</td>
      </tr>`;
    })
    .join("");
  const table = list.length
    ? adminDataTableShell(
        `<th>ID</th><th>Tarih</th><th>Üye ID</th><th>Kullanıcı</th><th>E-posta</th><th>Sponsor</th><th>Yatırım tarihi</th><th>Açıklama</th><th>Durum</th><th>İşlem</th>`,
        rows,
        "1580px"
      )
    : `<p class="admin-empty admin-empty--muted">Kayıtlı affiliate talebi yok.</p>`;
  return `<div class="admin-data-page">
    <header class="admin-data-page__head">
      <h2 class="admin-data-page__title">Affiliate talepleri</h2>

    </header>
    ${toolbar}
    ${table}
  </div>`;
}


function membersPaginationHtml(total, page, pageSize) {
  if (total <= 0) return "";
  const totalPages = Math.max(1, Math.ceil(total / pageSize));
  const safePage = Math.min(Math.max(0, page), totalPages - 1);
  const start = safePage * pageSize + 1;
  const end = Math.min(total, (safePage + 1) * pageSize);
  if (total <= pageSize) {
    return `<p class="admin-members-pagination__meta">${total.toLocaleString("tr-TR")} üye</p>`;
  }
  return `<nav class="admin-members-pagination" aria-label="Üye listesi sayfaları">
    <p class="admin-members-pagination__meta"><strong>${start.toLocaleString("tr-TR")}–${end.toLocaleString("tr-TR")}</strong> / ${total.toLocaleString("tr-TR")} üye · Sayfa <strong>${safePage + 1}</strong> / ${totalPages}</p>
    <div class="admin-members-pagination__actions">
      <button type="button" id="members-page-prev" class="admin-btn admin-btn--sm admin-btn--ghost" ${safePage <= 0 ? "disabled" : ""}>Önceki</button>
      <button type="button" id="members-page-next" class="admin-btn admin-btn--sm admin-btn--ghost" ${safePage >= totalPages - 1 ? "disabled" : ""}>Sonraki</button>
    </div>
  </nav>`;
}

/** Üye sekmesinde tablo + KPI’yı yeniler (tüm paneli değil). */
function renderMembersTabPartial(opts = {}) {
  const kpiEl = document.getElementById("members-kpi-mount");
  const listEl = document.getElementById("members-list-mount");
  if (!kpiEl && !listEl) {
    if (state.tab === "members" && !state.viewMemberProfileUserId) renderDashboard();
    return;
  }
  if (kpiEl) kpiEl.innerHTML = membersLoadStatsBannerHtml();
  if (listEl) listEl.innerHTML = membersPanelHtml();
  if (opts.focusSearch) restoreMembersFilterFocus("members-search");
  else if (opts.focusInputId) restoreMembersFilterFocus(opts.focusInputId);
}

function resetMembersListPage() {
  state.membersPage = 0;
}

/** @type {false | "document"} */
let membersTabDelegationBound = false;

async function openMemberProfileById(id) {
  captureCurrentTab();
  state.editSponsorId = null;
  state.editMarketId = null;
  state.editPasswordUserId = null;
  const uid = parseInt(String(id), 10);
  if (!uid) return;
  try {
    const data = await api(`admin_user_detail.php?userId=${uid}`);
    if (!data || data.ok !== true) {
      toast("Profil yüklenemedi.", "err");
      return;
    }
    state.viewMemberProfileUserId = uid;
    state.memberProfileDetail = data;
    state.memberProfileActiveTab = "overview";
    state.memberProfileBlackjackPage = 0;
    state.memberProfileSlotPage = 0;
    renderDashboard();
    requestAnimationFrame(() => {
      const main = document.querySelector(".admin-main");
      if (main) main.scrollTop = 0;
    });
  } catch (e) {
    toast(e.message || "Profil yüklenemedi", "err");
  }
}

function onMembersTabDelegatedClick(e) {
  const tabEl = document.getElementById("tab-members");
  if (!tabEl || tabEl.classList.contains("hidden")) return;
  if (!e.target.closest("#tab-members")) return;

  const profileBtn = e.target.closest(".btn-member-profile");
  if (profileBtn) {
    e.preventDefault();
    const id = parseInt(profileBtn.getAttribute("data-user-id") || "0", 10);
    if (id) void openMemberProfileById(id);
    return;
  }
  const prevBtn = e.target.closest("#members-page-prev");
  const nextBtn = e.target.closest("#members-page-next");
  if (prevBtn && !prevBtn.disabled) {
    e.preventDefault();
    state.membersPage = Math.max(0, (Number(state.membersPage) || 0) - 1);
    renderMembersTabPartial();
    document.getElementById("members-list-mount")?.scrollIntoView({ behavior: "smooth", block: "nearest" });
    return;
  }
  if (nextBtn && !nextBtn.disabled) {
    e.preventDefault();
    state.membersPage = (Number(state.membersPage) || 0) + 1;
    renderMembersTabPartial();
    document.getElementById("members-list-mount")?.scrollIntoView({ behavior: "smooth", block: "nearest" });
  }
}

/** Panel her render'da DOM yenilendiği için document üzerinde (bir kez). */
function ensureMembersTabDelegation() {
  if (membersTabDelegationBound === "document") return;
  membersTabDelegationBound = "document";
  document.addEventListener("click", onMembersTabDelegatedClick);
}

function restoreMembersFilterFocus(inputId) {
  requestAnimationFrame(() => {
    const el = document.getElementById(inputId);
    if (!el || typeof el.focus !== "function") return;
    el.focus();
    if (el instanceof HTMLInputElement && (el.type === "number" || el.type === "search" || el.type === "text")) {
      const len = el.value.length;
      try {
        el.setSelectionRange(len, len);
      } catch {
      }
    }
  });
}

function membersPanelHtml() {
  const raw = state.members || [];
  const list = getFilteredMembers();
  const showPhoneCol = raw.some((u) => String(u.phone || "").trim() !== "");
  if (state.membersLoading && !raw.length) {
    return `<p class="admin-empty admin-empty--muted">Üye listesi yükleniyor…</p>`;
  }
  if (state.membersExternalError && !raw.length) {
    const errHint = String(state.membersExternalError).includes("external_http")
      ? " Bearer token süresini kontrol edin."
      : "";
    return `<p class="admin-empty admin-empty--warn">Senkron hatası: ${escapeHtml(String(state.membersExternalError))}.${errHint} <button type="button" id="members-resume-btn-inline" class="admin-link-btn">Kaldığı yerden devam</button></p>`;
  }
  if (!raw.length) {
    return `<p class="admin-empty admin-empty--muted">Henüz kayıtlı oyuncu yok.</p>`;
  }
  if (!list.length) {
    if (membersExternalFiltersIgnored()) {
      return `<p class="admin-empty admin-empty--warn">Etiket, market ve IP filtreleri harici listede uygulanmaz. <button type="button" id="members-filter-reset-inline" class="admin-link-btn">Filtreleri sıfırla</button></p>`;
    }
    return `<p class="admin-empty admin-empty--warn">Arama kriterine uygun oyuncu bulunamadı.</p>`;
  }

  const total = list.length;
  const pageSize = MEMBERS_PAGE_SIZE;
  const totalPages = Math.max(1, Math.ceil(total / pageSize));
  let page = Number(state.membersPage) || 0;
  if (page >= totalPages) page = totalPages - 1;
  if (page < 0) page = 0;
  state.membersPage = page;
  const pageList = list.slice(page * pageSize, page * pageSize + pageSize);

  const rows = pageList
    .map((u) => {
      const active = u.isActive !== false;
      const sameCnt = Number(u.sameRegisterIpCount) || 0;
      const multiCell =
        sameCnt > 1
          ? `<span class="admin-members-tag admin-members-tag--warn" title="Bu kayıt IP adresiyle açılmış toplam hesap sayısı">${sameCnt}</span>`
          : `<span class="admin-members-tag admin-members-tag--muted">${String(u.registerIp || "").trim() !== "" ? "1" : "—"}</span>`;

      const fromRx = !!(u && u.external);
      const phoneCell = showPhoneCol
        ? `<td class="admin-members-phone">${escapeHtml(String(u.phone || "—"))}</td>`
        : "";
      return `
      <tr class="admin-members-row">
        <td>
          <button type="button" class="btn-member-profile" data-user-id="${u.id}" data-external="${fromRx ? "1" : "0"}" title="${fromRx ? "RX kaynaklı (veritabanında)" : "Üye profili"}" aria-label="Üye profili">
            <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"/></svg>
          </button>
        </td>
        <td class="admin-members-mono">${u.id}</td>
        <td><strong>${escapeHtml(u.username)}</strong></td>
        <td class="admin-members-tags-cell">${memberAdminTagsBadgesHtml(u.adminTags)}</td>
        <td class="admin-members-email">${escapeHtml(u.email)}</td>
        ${phoneCell}
        <td class="admin-members-ip admin-members-mono" title="${escapeHtml(String(u.registerIp || ""))}">${escapeHtml(String(u.registerIp || "—"))}</td>
        <td class="text-center">${multiCell}</td>
        <td class="admin-members-coins">${u.coins}</td>
        <td class="admin-members-level-cell">${memberLevelListCellHtml(u)}</td>
        <td>
          ${
            active
              ? '<span class="admin-members-status admin-members-status--on">Aktif</span>'
              : '<span class="admin-members-status admin-members-status--off">Pasif</span>'
          }
        </td>
        <td class="admin-members-mono">${formatTs(u.createdAt)}</td>
        <td class="tabular-nums">${u.totalSpent}</td>
        <td class="tabular-nums">${u.purchaseCount}</td>
      </tr>`;
    })
    .join("");

  const phoneTh = showPhoneCol ? `<th scope="col">Telefon</th>` : "";
  const pagination = membersPaginationHtml(total, page, pageSize);

  return `<div class="admin-data-panel">
    <div class="admin-table-wrap admin-data-table-wrap admin-members-table-wrap">
    <table class="admin-members-table admin-data-table">
      <thead>
        <tr>
          <th scope="col" class="text-center" title="Üye profili">Profil</th>
          <th scope="col">ID</th>
          <th scope="col">Kullanıcı</th>
          <th scope="col" title="Şüpheli, VIP, Yasaklı">Etiket</th>
          <th scope="col">E-posta</th>
          ${phoneTh}
          <th scope="col" title="Kayıt olduğu IP">Kayıt IP</th>
          <th scope="col" class="text-center" title="Bu kayıt IP ile açılmış toplam hesap (çoklu şüphe)">Aynı IP</th>
          <th scope="col">Coin</th>
          <th scope="col" title="Site seviye sistemi">Seviye</th>
          <th scope="col">Üyelik</th>
          <th scope="col">Kayıt</th>
          <th scope="col">Toplam harcama</th>
          <th scope="col">İşlem</th>
        </tr>
      </thead>
      <tbody>${rows}</tbody>
    </table>
    </div>
    ${pagination}
  </div>`;
}

/** @param {any} op */
function staffEditModalHtml(op) {
  const selected = op.isSuper ? PERM_DEFS.map((p) => p.id) : op.permissions || [];
  const twofaEnabled = op.twofaEnabled === true;
  const twofaCanEnable = op.twofaCanEnable === true;
  const permCols = PERM_DEFS.map((p) => {
    const sel = selected.includes(p.id) ? "checked" : "";
    return `<label class="flex cursor-pointer items-center gap-2 admin-inset admin-inset--compact px-3 py-2 text-sm text-slate-600">
      <input type="checkbox" class="staff-edit-perm" value="${escapeHtml(p.id)}" ${sel} />
      <span>${escapeHtml(p.label)}</span>
    </label>`;
  }).join("");
  return `
  <div class="admin-modal-overlay fixed inset-0 z-[80] flex items-center justify-center p-4" role="dialog" aria-modal="true">
    <div class="admin-glass relative max-h-[90vh] w-full max-w-lg overflow-y-auto rounded-2xl p-6 shadow-2xl">
      <div class="mb-4 flex items-start justify-between gap-3">
        <div>
          <p class="text-[10px] font-bold uppercase tracking-[0.2em] text-sky-400/90">Personel</p>
          <h2 class="mt-1 text-xl admin-section__title font-bold">Düzenle: ${escapeHtml(op.username)}</h2>
        </div>
        <button type="button" id="staff-edit-close" class="admin-icon-btn-close" aria-label="Kapat">✕</button>
      </div>
      <form id="staff-edit-form" class="space-y-4">
        <input type="hidden" name="id" value="${op.id}" />
        <div>
          <label class="admin-label" for="staff-edit-user">Kullanıcı adı</label>
          <input id="staff-edit-user" name="username" class="admin-input" value="${escapeHtml(op.username)}" required />
        </div>
        <div>
          <label class="admin-label" for="staff-edit-pass">Yeni şifre (boş bırak → değiştirme)</label>
          <input id="staff-edit-pass" name="password" type="password" class="admin-input" autocomplete="new-password" placeholder="••••••••" />
        </div>
        <div>
          <label class="admin-label" for="staff-edit-display">Görünen ad</label>
          <input id="staff-edit-display" name="displayName" class="admin-input" value="${escapeHtml(op.displayName)}" required />
        </div>
        <label class="flex cursor-pointer items-center gap-2 text-sm text-slate-300">
          <input type="checkbox" id="staff-edit-super" name="isSuper" ${op.isSuper ? "checked" : ""} />
          <span>Tam yetkili</span>
        </label>
        <label class="flex cursor-pointer items-center gap-2 text-sm text-slate-300">
          <input type="checkbox" id="staff-edit-twofa-enabled" name="twofaEnabled" ${twofaEnabled ? "checked" : ""} />
          <span>2FA zorunlu (Authenticator)</span>
        </label>
        ${
          !twofaEnabled && !twofaCanEnable
            ? `<p class="text-xs text-amber-300">Bu hesap henüz 2FA kurmadığı için buradan aktifleştirilemez. Önce personel kendi profilinde 2FA kurmalıdır.</p>`
            : ""
        }
        <div id="staff-edit-perm-wrap" class="grid gap-2 sm:grid-cols-2 ${op.isSuper ? "hidden" : ""}">
          ${permCols}
        </div>
        <div class="flex flex-wrap gap-2 pt-2">
          <button type="submit" class="admin-btn admin-btn-primary px-5 py-2.5">Kaydet</button>
          <button type="button" id="staff-edit-twofa-reset" class="admin-btn admin-btn-ghost px-5 py-2.5 text-amber-300 hover:bg-amber-950/30">2FA Sıfırla</button>
          <button type="button" id="staff-edit-cancel" class="admin-btn admin-btn-ghost px-5 py-2.5">İptal</button>
        </div>
      </form>
    </div>
  </div>`;
}

function staffPanelHtml() {
  const ops = state.operators || [];
  const permFormCols = PERM_DEFS.map(
    (p) =>
      `<label class="flex cursor-pointer items-center gap-2 admin-inset admin-inset--compact px-3 py-2 text-sm text-slate-600 hover:bg-[var(--admin-overlay)]">
        <input type="checkbox" class="staff-new-perm" value="${escapeHtml(p.id)}" />
        <span>${escapeHtml(p.label)}</span>
      </label>`
  ).join("");
  const rows = ops
    .map((op) => {
      const permLabel = op.isSuper
        ? "Tam yetki"
        : (op.permissions || [])
            .map((x) => PERM_DEFS.find((d) => d.id === x)?.label || x)
            .join(", ") || "—";
      return `<tr class="admin-table-row">
        <td class="px-3 py-2.5 admin-section__title font-medium">${escapeHtml(op.username)}</td>
        <td class="px-3 py-2.5 text-slate-400">${escapeHtml(op.displayName)}</td>
        <td class="px-3 py-2.5 text-slate-400">${op.isSuper ? "Evet" : "Hayır"}</td>
        <td class="px-3 py-2.5 text-slate-400">${op.twofaEnabled ? "Açık" : "Kapalı"}</td>
        <td class="max-w-xs px-3 py-2.5 text-xs text-slate-500">${escapeHtml(permLabel)}</td>
        <td class="px-3 py-2.5 text-right">
          <button type="button" class="admin-btn admin-btn-ghost px-3 py-1.5 text-sm btn-staff-edit" data-id="${op.id}">Düzenle</button>
          <button type="button" class="admin-btn admin-btn-ghost px-3 py-1.5 text-sm text-red-300 hover:bg-red-950/30 btn-staff-delete" data-id="${op.id}">Sil</button>
        </td>
      </tr>`;
    })
    .join("");
  return `
  <div class="max-w-5xl space-y-6">
    <div class="admin-panel admin-panel--padded">
      <h3 class="admin-section__title text-lg">Yeni personel</h3>
      <p class="mt-1 text-sm text-slate-500">Personel, ana şifre yerine kullanıcı adı + şifre ile giriş yapar. İzinleri aşağıdan seç veya tam yetkili işaretle.</p>
      <form id="staff-new-form" class="mt-6 space-y-4">
        <div class="grid gap-4 sm:grid-cols-2">
          <div>
            <label class="admin-label" for="staff-new-user">Kullanıcı adı</label>
            <input id="staff-new-user" name="username" class="admin-input" autocomplete="off" placeholder="ornek_personel" required />
          </div>
          <div>
            <label class="admin-label" for="staff-new-pass">Şifre</label>
            <input id="staff-new-pass" name="password" type="password" class="admin-input" autocomplete="new-password" placeholder="En az 8 karakter" required />
          </div>
          <div class="sm:col-span-2">
            <label class="admin-label" for="staff-new-display">Görünen ad</label>
            <input id="staff-new-display" name="displayName" class="admin-input" placeholder="Destek" required />
          </div>
        </div>
        <label class="flex cursor-pointer items-center gap-2 text-sm text-slate-300">
          <input type="checkbox" id="staff-new-super" name="isSuper" />
          <span>Tam yetkili (tüm izinler)</span>
        </label>
        <div id="staff-new-perm-wrap" class="grid gap-2 sm:grid-cols-2">
          ${permFormCols}
        </div>
        <button type="submit" class="admin-btn admin-btn-primary px-5 py-2.5">Personel oluştur</button>
      </form>
    </div>
    <div class="admin-panel admin-panel--padded">
      <h3 class="admin-section__title text-lg">Kayıtlı hesaplar</h3>
      <div class="mt-4 overflow-x-auto">
        <table class="w-full min-w-[640px] text-left text-sm">
          <thead>
            <tr class="admin-table-row text-slate-500">
              <th class="px-3 py-2 font-semibold">Kullanıcı</th>
              <th class="px-3 py-2 font-semibold">Görünen ad</th>
              <th class="px-3 py-2 font-semibold">Tam yetki</th>
              <th class="px-3 py-2 font-semibold">2FA</th>
              <th class="px-3 py-2 font-semibold">İzinler</th>
              <th class="px-3 py-2 font-semibold text-right">İşlem</th>
            </tr>
          </thead>
          <tbody>${
            rows ||
            `<tr><td colspan="6" class="px-3 py-8 text-center text-slate-500">Henüz personel yok.</td></tr>`
          }</tbody>
        </table>
      </div>
    </div>
  </div>`;
}

function adminChatMessageRowsHtml() {
  const msgs = state.chatMessages || [];
  if (!msgs.length) {
    return `<tr><td colspan="4" class="px-4 py-10 text-center text-sm admin-chat-meta">Henüz mesaj yok.</td></tr>`;
  }
  const sysUid = Number(state.chatSystemUserId) || 0;
  return msgs
    .map((m) => {
      const del = m.deleted === true;
      const nameEsc = escapeHtml(String(m.displayName || m.username || ""));
      const bodyEsc = escapeHtml(String(m.body || ""));
      const mgmt = m.fromManagement === true;
      const uid = Number(m.userId);
      const muteBtn =
        sysUid > 0 && uid === sysUid
          ? ""
          : `<button type="button" class="admin-btn admin-btn-ghost px-2.5 py-1.5 text-xs btn-admin-chat-mute-user" data-user-id="${uid}">Sustur</button>`;
      return `<tr class="admin-chat-tr align-top ${del ? "opacity-[0.65]" : ""}">
        <td class="whitespace-nowrap px-3 py-2.5 align-top tabular-nums admin-chat-meta">${escapeHtml(formatTs(m.createdAt))}</td>
        <td class="px-3 py-2.5 align-top text-sm">
          <span class="admin-chat-sender font-medium">${nameEsc}</span>
          <span class="admin-chat-id"> #${uid}</span>
          ${mgmt ? `<span class="admin-chat-badge ml-1.5 inline-block rounded-md px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wide">Moderatör</span>` : ""}
        </td>
        <td class="max-w-lg px-3 py-2.5 align-top admin-chat-body">
          <div class="whitespace-pre-wrap break-words">${del ? `<span class="admin-chat-deleted line-through">${bodyEsc}</span> <span class="admin-chat-deleted-label">silindi</span>` : bodyEsc}</div>
        </td>
        <td class="whitespace-nowrap px-3 py-2.5 text-right align-top">
          ${
            del
              ? "—"
              : `<button type="button" class="admin-btn admin-btn-ghost px-2.5 py-1.5 text-xs btn-admin-chat-del" data-msg-id="${Number(m.id)}">Sil</button>
                 ${muteBtn}`
          }
        </td>
      </tr>`;
    })
    .join("");
}

function adminChatMuteRowsHtml() {
  const mutes = state.chatMutes || [];
  if (!mutes.length) {
    return `<tr><td colspan="3" class="px-4 py-6 text-center text-xs admin-chat-meta">Aktif susturma yok.</td></tr>`;
  }
  return mutes
    .map((mu) => {
      const active = mu.active === true;
      const un = escapeHtml(String(mu.username || ""));
      return `<tr class="admin-chat-tr text-sm">
        <td class="px-3 py-2">${un} <span class="admin-chat-id">#${Number(mu.userId)}</span></td>
        <td class="px-3 py-2 tabular-nums admin-chat-meta ${active ? "admin-chat-mute-active font-medium" : ""}">${escapeHtml(formatTs(mu.mutedUntil))}${active ? "" : " (doldu)"}</td>
        <td class="px-3 py-2 text-right">${active ? `<button type="button" class="admin-btn admin-btn-ghost px-2.5 py-1 text-xs btn-admin-chat-unmute" data-user-id="${Number(mu.userId)}">Kaldır</button>` : "—"}</td>
      </tr>`;
    })
    .join("");
}

function adminChatPanelHtml() {
  return `
  <div class="admin-chat-layout grid w-full max-w-7xl gap-6 xl:grid-cols-[1fr_minmax(260px,320px)]">
    <div class="admin-glass admin-chat-main min-h-[420px] overflow-hidden rounded-2xl">
      <div class="admin-chat-divider border-b px-5 py-4">
        <h3 class="admin-chat-title text-base font-semibold">Genel sohbet</h3>
        <p class="mt-0.5 text-xs admin-chat-meta">Üye mesajlarını sil, kullanıcıyı sustur veya yönetim duyurusu gönder. Üstteki <strong class="font-medium">Yenile</strong> ile listeyi güncelle.</p>
      </div>
      <div class="admin-chat-scroll max-h-[min(52vh,560px)] overflow-x-auto overflow-y-auto">
        <table class="w-full min-w-[720px] text-left text-sm">
          <thead class="admin-chat-thead sticky top-0 z-[1]">
            <tr class="admin-chat-divider border-b text-xs uppercase tracking-wide admin-chat-meta">
              <th scope="col" class="px-3 py-2.5 font-semibold">Zaman</th>
              <th scope="col" class="px-3 py-2.5 font-semibold">Gönderen</th>
              <th scope="col" class="px-3 py-2.5 font-semibold">Mesaj</th>
              <th scope="col" class="px-3 py-2.5 text-right font-semibold">İşlem</th>
            </tr>
          </thead>
          <tbody id="admin-chat-feed-body">${adminChatMessageRowsHtml()}</tbody>
        </table>
      </div>
      <div class="admin-chat-divider border-t p-5">
        <label class="admin-label" for="admin-chat-announce">Yönetim mesajı (herkese görünür)</label>
        <p class="mb-2 text-xs admin-chat-meta">Ana sohbette <span class="admin-chat-accent font-medium">Moderatör</span> etiketi ve isim görünür.</p>
        <div class="flex flex-col gap-3 sm:flex-row sm:items-end">
          <textarea id="admin-chat-announce" class="admin-input min-h-[88px] flex-1 resize-y text-sm" maxlength="600" rows="3" placeholder="Duyuru metni…"></textarea>
          <button type="button" id="btn-admin-chat-announce" class="admin-btn admin-btn-primary shrink-0 px-5 py-2.5">Gönder</button>
        </div>
      </div>
    </div>
    <div class="space-y-4">
      <div class="admin-glass admin-chat-side rounded-2xl p-5">
        <h4 class="admin-chat-title text-sm font-semibold">Hızlı sustur</h4>
        <p class="mt-1 text-xs admin-chat-meta">Üye ID ile süreli sustur (dakika). Süre dolunca otomatik açılır.</p>
        <div class="mt-4 grid gap-3">
          <div>
            <label class="admin-label" for="admin-chat-mute-uid">Üye ID</label>
            <input id="admin-chat-mute-uid" type="number" min="1" step="1" class="admin-input tabular-nums" placeholder="Örn. 42" />
          </div>
          <div>
            <label class="admin-label" for="admin-chat-mute-min">Süre (dakika)</label>
            <select id="admin-chat-mute-min" class="admin-input">
              <option value="15">15 dk</option>
              <option value="60" selected>1 saat</option>
              <option value="360">6 saat</option>
              <option value="1440">24 saat</option>
              <option value="10080">7 gün</option>
            </select>
          </div>
          <button type="button" id="btn-admin-chat-mute-apply" class="admin-btn admin-btn-primary w-full py-2.5">Sustur</button>
        </div>
      </div>
      <div class="admin-glass admin-chat-side rounded-2xl p-5">
        <h4 class="admin-chat-title text-sm font-semibold">Susturmalar</h4>
        <p class="mt-1 text-xs admin-chat-meta">Bitiş zamanı ve kaldırma.</p>
        <div class="admin-chat-mutes-wrap mt-3 max-h-[280px] overflow-x-auto overflow-y-auto rounded-xl border">
          <table class="w-full min-w-[260px] text-left text-sm">
            <thead class="admin-chat-thead sticky top-0 text-xs uppercase tracking-wide admin-chat-meta">
              <tr class="admin-chat-divider border-b">
                <th scope="col" class="px-3 py-2 font-semibold">Üye</th>
                <th scope="col" class="px-3 py-2 font-semibold">Bitiş</th>
                <th scope="col" class="px-3 py-2 text-right font-semibold"></th>
              </tr>
            </thead>
            <tbody id="admin-chat-mutes-body">${adminChatMuteRowsHtml()}</tbody>
          </table>
        </div>
      </div>
    </div>
  </div>`;
}


function navBtn(tab, label, activeTab, layout = "sidebar", opts = {}) {
  if (!canPerm(tab)) return "";
  const child = opts.child === true;
  const badgeHtml = opts.badgeHtml || "";
  const labelInner = `<span class="admin-nav-tab__label">${escapeHtml(label)}</span>${badgeHtml}`;
  const inner =
    layout === "sidebar"
      ? navSidebarLabelRow(tab, labelInner)
      : `${escapeHtml(label)}${badgeHtml}`;
  const cls = navTabClass(tab, activeTab, layout) + (child && layout === "sidebar" ? " admin-nav-tab--child" : "");
  const titleAttr = layout === "sidebar" ? ` title="${escapeHtml(label)}"` : "";
  return `<button type="button" data-tab="${escapeHtml(tab)}" class="${cls}"${titleAttr}>${inner}</button>`;
}

async function loadAdminChat(opts = {}) {
  const silent = opts.silent === true;
  try {
    const j = await api("admin_chat.php", { method: "GET" });
    if (typeof j.csrfToken === "string" && j.csrfToken.length > 0) {
      adminCsrfToken = j.csrfToken;
    }
    state.chatMessages = Array.isArray(j.messages) ? j.messages : [];
    state.chatMutes = Array.isArray(j.mutes) ? j.mutes : [];
    state.chatSystemUserId = typeof j.systemUserId === "number" ? j.systemUserId : 0;
    if (silent) {
      const feed = $("#admin-chat-feed-body");
      if (feed) feed.innerHTML = adminChatMessageRowsHtml();
      const muteEl = $("#admin-chat-mutes-body");
      if (muteEl) muteEl.innerHTML = adminChatMuteRowsHtml();
    }
  } catch (e) {
    if (!silent) {
      throw e;
    }
  }
}

function syncAdminChatPolling() {
  if (adminChatPollTimer != null) {
    clearInterval(adminChatPollTimer);
    adminChatPollTimer = null;
  }
  if (state.tab !== "chat" || !canPerm("chat")) return;
  adminChatPollTimer = window.setInterval(() => {
    void loadAdminChat({ silent: true });
  }, 6000);
}

function canUseAdminHeaderAlerts() {
  return (
    canPerm("market-requests") ||
    canPerm("tickets") ||
    canPerm("site-events") ||
    canPerm("complaints") ||
    canPerm("affiliate-requests")
  );
}

function playAdminAlertSound() {
  if (!state.headerAlertsSound) return;
  try {
    const Ctx = window.AudioContext || window.webkitAudioContext;
    if (!Ctx) return;
    if (!adminAlertsAudioCtx) adminAlertsAudioCtx = new Ctx();
    const ctx = adminAlertsAudioCtx;
    if (!ctx) return;
    if (ctx.state === "suspended") {
      void ctx.resume();
    }
    const now = ctx.currentTime;
    const makeTone = (freq, start, dur, vol) => {
      const osc = ctx.createOscillator();
      const gain = ctx.createGain();
      osc.type = "sine";
      osc.frequency.setValueAtTime(freq, start);
      gain.gain.setValueAtTime(0.0001, start);
      gain.gain.exponentialRampToValueAtTime(vol, start + 0.015);
      gain.gain.exponentialRampToValueAtTime(0.0001, start + dur);
      osc.connect(gain);
      gain.connect(ctx.destination);
      osc.start(start);
      osc.stop(start + dur + 0.02);
    };
    makeTone(880, now + 0.01, 0.14, 0.06);
    makeTone(1174, now + 0.2, 0.16, 0.055);
  } catch {
    /* ignore audio failures */
  }
}

function adminAlertItemIcon(type) {
  switch (String(type || "")) {
    case "market-request":
      return iconSvg("cart", "h-4 w-4");
    case "ticket-request":
      return iconSvg("ticket", "h-4 w-4");
    case "event-request":
      return iconSvg("sparkles", "h-4 w-4");
    case "complaint":
      return iconSvg("x-circle", "h-4 w-4");
    case "affiliate-request":
      return iconSvg("link", "h-4 w-4");
    default:
      return iconSvg("bell", "h-4 w-4");
  }
}

function adminHeaderAlertsButtonHtml() {
  if (!canUseAdminHeaderAlerts()) return "";
  const total = Math.max(0, Number(state.headerAlerts?.totalPending) || 0);
  const sections = Array.isArray(state.headerAlerts?.sections) ? state.headerAlerts.sections : [];
  const items = Array.isArray(state.headerAlerts?.items) ? state.headerAlerts.items : [];
  const secHtml = sections
    .filter((s) => Number(s?.count) > 0)
    .map(
      (s) =>
        `<button type="button" class="admin-alerts__chip" data-admin-alert-tab="${escapeHtml(
          String(s.tab || "")
        )}">${escapeHtml(String(s.label || "Bildirim"))}<span class="admin-alerts__chip-count">${Number(
          s.count
        )}</span></button>`
    )
    .join("");
  const itemHtml = items.length
    ? items
        .slice(0, 12)
        .map((it) => {
          const tab = escapeHtml(String(it.tab || ""));
          const title = escapeHtml(String(it.title || "Bildirim"));
          const msg = escapeHtml(String(it.message || ""));
          const ageLabel = formatPendingAgeLabel(Number(it.ageSec) || 0);
          const sla = String(it.sla || "ok");
          return `<button type="button" class="admin-alerts__item admin-alerts__item--sla-${sla}" data-admin-alert-tab="${tab}">
            <span class="admin-alerts__item-ico">${adminAlertItemIcon(it.type)}</span>
            <span class="admin-alerts__item-main">
              <span class="admin-alerts__item-title">${title}</span>
              <span class="admin-alerts__item-msg">${msg}</span>
            </span>
            <span class="admin-alerts__item-meta">
              <span class="admin-pending-age admin-pending-age--${sla}">${escapeHtml(ageLabel)}</span>
            </span>
          </button>`;
        })
        .join("")
    : `<div class="admin-alerts__empty">Bekleyen admin bildirimi yok.</div>`;
  return `<div class="admin-alerts ${state.headerAlertsOpen ? "admin-alerts--open" : ""}" id="admin-header-alerts">
    <button type="button" id="admin-alerts-toggle" class="admin-icon-btn admin-alerts__toggle" aria-label="Bildirimler" aria-expanded="${
      state.headerAlertsOpen ? "true" : "false"
    }">
      ${iconSvg("bell", "h-5 w-5")}
      ${
        total > 0
          ? `<span class="admin-alerts__badge" aria-label="${total} bekleyen bildirim">${total > 99 ? "99+" : total}</span>`
          : ""
      }
    </button>
    <div class="admin-alerts__panel" role="menu" aria-hidden="${state.headerAlertsOpen ? "false" : "true"}">
      <div class="admin-alerts__head">
        <strong class="admin-alerts__title">Bildirimler</strong>
        <label class="admin-alerts__sound">
          <input type="checkbox" id="admin-alerts-sound" ${state.headerAlertsSound ? "checked" : ""} />
          <span>Ses</span>
        </label>
      </div>
      ${secHtml ? `<div class="admin-alerts__chips">${secHtml}</div>` : ""}
      <div class="admin-alerts__list">${itemHtml}</div>
    </div>
  </div>`;
}

function updateAdminHeaderAlertsUi() {
  const host = document.getElementById("admin-header-alerts-host");
  if (!host) return;
  host.innerHTML = adminHeaderAlertsButtonHtml();
}

async function loadAdminHeaderAlerts(opts = {}) {
  if (!canUseAdminHeaderAlerts()) return;
  const silent = opts.silent === true;
  try {
    const j = await api("admin_header_alerts.php", { method: "GET" });
    const total = Math.max(0, Number(j?.totalPending) || 0);
    state.headerAlerts = {
      totalPending: total,
      criticalCount: Math.max(0, Number(j?.criticalCount) || 0),
      warnCount: Math.max(0, Number(j?.warnCount) || 0),
      sections: Array.isArray(j?.sections) ? j.sections : [],
      items: Array.isArray(j?.items) ? j.items : [],
      serverTime: Number(j?.serverTime) || 0,
      slaRules: j?.slaRules && typeof j.slaRules === "object" ? j.slaRules : { warnHours: 24, criticalHours: 72 },
    };
    const prev = adminAlertsLastTotal;
    if (adminAlertsBootstrapped && total > prev) {
      playAdminAlertSound();
    }
    adminAlertsLastTotal = total;
    adminAlertsBootstrapped = true;
    if (silent) updateAdminHeaderAlertsUi();
  } catch {
    if (!silent) throw new Error("Admin bildirimleri yüklenemedi");
  }
}

function syncAdminAlertsPolling() {
  if (adminAlertsPollTimer != null) {
    clearInterval(adminAlertsPollTimer);
    adminAlertsPollTimer = null;
  }
  if (!state.authenticated || !canUseAdminHeaderAlerts()) return;
  adminAlertsPollTimer = window.setInterval(() => {
    void loadAdminHeaderAlerts({ silent: true });
  }, 10000);
}

async function loadMyAdminProfile() {
  const j = await api("admin_my_profile.php", { method: "GET" });
  state.myAdminProfile = j?.profile && typeof j.profile === "object" ? j.profile : null;
}

function myAdminProfileModalHtml() {
  const p = state.myAdminProfile || {};
  const displayName = escapeHtml(String(p.displayName || ""));
  const username = escapeHtml(String(p.username || ""));
  const editable = p.editable === true;
  const passwordEditable = p.passwordEditable === true;
  const isStaff = p.kind === "staff";
  const canManage2fa = p.kind === "staff" || p.kind === "master";
  const twofaEnabled = p.twofaEnabled === true;
  const twofaBackupCodesCount = Math.max(0, Number(p.twofaBackupCodesCount) || 0);
  const kindText = isStaff ? "Personel hesabı" : "Ana yönetici";
  const kindBadgeClass = isStaff ? "admin-my-profile__badge--staff" : "admin-my-profile__badge--master";
  const msg = String(p.message || "");
  const initials = escapeHtml(String((p.displayName || p.username || "A")).trim().slice(0, 2).toUpperCase());
  const usernameLine = username ? (isStaff ? `@${username}` : username) : "—";
  const editStatus = editable ? "Düzenlenebilir" : "Salt okunur";
  const pwdStatus = passwordEditable ? "Değiştirilebilir" : "Sabit";
  const twofaStatus = twofaEnabled ? "Aktif" : "Kapalı";
  return `<div class="admin-my-profile-page admin-cms-page">
      <header class="admin-data-page__head">
        <h2 id="admin-my-profile-title" class="admin-data-page__title">Profilim</h2>

      </header>
      ${msg ? `<div class="admin-my-profile-notice" role="status">${escapeHtml(msg)}</div>` : ""}
      <div class="admin-my-profile-layout">
        <aside class="admin-my-profile-card admin-my-profile-card--identity">
          <div class="admin-my-profile-card__banner" aria-hidden="true"></div>
          <div class="admin-my-profile-card__identity-body">
            <div class="admin-my-profile__avatar" aria-hidden="true">${initials}</div>
            <div class="admin-my-profile__identity-text">
              <p class="admin-my-profile__name">${displayName || username || "—"}</p>
              <p class="admin-my-profile__username">${escapeHtml(usernameLine)}</p>
              <span class="admin-my-profile__badge ${kindBadgeClass}">${escapeHtml(kindText)}</span>
            </div>
            <dl class="admin-my-profile__meta">
              <div class="admin-my-profile__meta-row">
                <dt>Hesap</dt>
                <dd><span class="admin-members-status admin-members-status--on">Aktif</span></dd>
              </div>
              <div class="admin-my-profile__meta-row">
                <dt>Profil</dt>
                <dd>${escapeHtml(editStatus)}</dd>
              </div>
              <div class="admin-my-profile__meta-row">
                <dt>Şifre</dt>
                <dd>${escapeHtml(pwdStatus)}</dd>
              </div>
              <div class="admin-my-profile__meta-row">
                <dt>2FA</dt>
                <dd>${escapeHtml(twofaStatus)}</dd>
              </div>
            </dl>
          </div>
        </aside>
        <div class="admin-my-profile-main">
          <section class="admin-my-profile-card" aria-labelledby="admin-my-profile-section-info">
            <header class="admin-my-profile-card__head">
              <div>
                <p class="admin-my-profile-card__kicker">Kimlik</p>
                <h3 id="admin-my-profile-section-info" class="admin-my-profile-card__title">Profil bilgileri</h3>

              </div>
            </header>
            <form id="admin-my-profile-form" class="admin-my-profile-form">
              <div class="admin-my-profile-form__grid">
                <div class="admin-my-profile-form__field">
                  <label class="admin-label" for="my-admin-username">Kullanıcı adı</label>
                  <input id="my-admin-username" class="admin-input w-full font-mono text-sm" value="${username}" disabled autocomplete="username" />
                </div>
                <div class="admin-my-profile-form__field">
                  <label class="admin-label" for="my-admin-display-name">Görünen ad</label>
                  <input id="my-admin-display-name" class="admin-input w-full" value="${displayName}" ${editable ? "" : "disabled"} autocomplete="name" placeholder="Ad Soyad" />
                </div>
              </div>
              <div class="admin-my-profile-form__actions">
                <button type="submit" id="btn-my-admin-profile-save" class="admin-btn admin-btn-primary px-5 py-2.5" ${editable ? "" : "disabled"}>Profili kaydet</button>
              </div>
            </form>
          </section>
          <section class="admin-my-profile-card" aria-labelledby="admin-my-profile-section-password">
            <header class="admin-my-profile-card__head">
              <div>
                <p class="admin-my-profile-card__kicker">Güvenlik</p>
                <h3 id="admin-my-profile-section-password" class="admin-my-profile-card__title">Şifre değiştir</h3>

              </div>
            </header>
            <form id="admin-my-password-form" class="admin-my-profile-form">
              <div class="admin-my-profile-form__grid admin-my-profile-form__grid--stack">
                <div class="admin-my-profile-form__field">
                  <label class="admin-label" for="my-admin-current-password">Mevcut şifre</label>
                  <input id="my-admin-current-password" type="password" class="admin-input w-full" autocomplete="current-password" ${passwordEditable ? "" : "disabled"} />
                </div>
                <div class="admin-my-profile-form__field">
                  <label class="admin-label" for="my-admin-new-password">Yeni şifre</label>
                  <input id="my-admin-new-password" type="password" class="admin-input w-full" minlength="8" autocomplete="new-password" ${passwordEditable ? "" : "disabled"} />
                </div>
                <div class="admin-my-profile-form__field">
                  <label class="admin-label" for="my-admin-new-password-repeat">Yeni şifre (tekrar)</label>
                  <input id="my-admin-new-password-repeat" type="password" class="admin-input w-full" minlength="8" autocomplete="new-password" ${passwordEditable ? "" : "disabled"} />
                </div>
              </div>
              <aside class="admin-my-profile-rules" aria-label="Şifre gereksinimleri">
                <p class="admin-my-profile-rules__title">Şifre gereksinimleri</p>
                <ul class="admin-my-profile-rules__list">
                  <li>En az 8 karakter</li>
                  <li>Büyük harf, küçük harf ve rakam içermeli</li>
                </ul>
              </aside>
              <div class="admin-my-profile-form__actions">
                <button type="submit" id="btn-my-admin-password-save" class="admin-btn admin-btn-soft px-5 py-2.5" ${passwordEditable ? "" : "disabled"}>Şifreyi güncelle</button>
              </div>
            </form>
          </section>
          <section class="admin-my-profile-card" aria-labelledby="admin-my-profile-section-2fa">
            <header class="admin-my-profile-card__head">
              <div>
                <p class="admin-my-profile-card__kicker">Güvenlik</p>
                <h3 id="admin-my-profile-section-2fa" class="admin-my-profile-card__title">Authenticator (2FA)</h3>
              </div>
            </header>
            <div class="admin-my-profile-form">
              <p class="text-sm text-slate-500">Durum: <strong>${escapeHtml(twofaStatus)}</strong>${twofaEnabled ? ` · ${twofaBackupCodesCount} yedek kod` : ""}</p>
              <div id="admin-2fa-setup-box" class="mt-3 hidden rounded-xl border border-white/10 bg-white/[0.03] p-3">
                <p class="text-xs text-slate-400">QR kodu Authenticator ile tara, sonra 6 haneli kodu onayla.</p>
                <img id="admin-2fa-qr" alt="2FA QR" class="mt-2 h-[220px] w-[220px] max-w-full rounded-lg border border-white/10 bg-white p-2" />
                <p class="mt-2 text-[11px] text-slate-400 break-all" id="admin-2fa-secret"></p>
                <div class="mt-2 flex gap-2">
                  <input id="admin-2fa-verify-code" type="text" class="admin-input w-full" inputmode="numeric" placeholder="123456" />
                  <button type="button" id="btn-admin-2fa-confirm" class="admin-btn admin-btn-primary px-4">Onayla</button>
                </div>
              </div>
              <div class="mt-3 flex flex-wrap gap-2">
                <button type="button" id="btn-admin-2fa-start" class="admin-btn admin-btn-soft px-4 py-2.5" ${canManage2fa ? "" : "disabled"}>2FA Kur</button>
                <button type="button" id="btn-admin-2fa-regenerate" class="admin-btn admin-btn-soft px-4 py-2.5" ${twofaEnabled && canManage2fa ? "" : "disabled"}>Yedek Kod Yenile</button>
                <button type="button" id="btn-admin-2fa-disable" class="admin-btn admin-btn-soft px-4 py-2.5" ${twofaEnabled && canManage2fa ? "" : "disabled"}>2FA Kapat</button>
              </div>
            </div>
          </section>
        </div>
      </div>
    </div>`;
}

function renderDashboard() {
  if (!state.authenticated) {
    renderLogin();
    return;
  }
  ensureAllowedTab();
  const tab = state.tab;
  if (state.editSponsorId && !state.sponsors.some((s) => s.id === state.editSponsorId)) {
    state.editSponsorId = null;
  }
  if (state.editMarketId && !state.market.some((m) => m.id === state.editMarketId)) {
    state.editMarketId = null;
  }
  if (state.editSponsorPromoId && !(state.sponsorPromoCodes || []).some((r) => r.id === state.editSponsorPromoId)) {
    state.editSponsorPromoId = null;
  }
  if (state.editBonusId && !(state.bonuses || []).some((r) => r.id === state.editBonusId)) {
    state.editBonusId = null;
  }
  if (state.editPasswordUserId != null && !state.members.some((m) => m.id === state.editPasswordUserId)) {
    state.editPasswordUserId = null;
  }
  if (state.viewMemberProfileUserId != null && !state.members.some((m) => m.id === state.viewMemberProfileUserId)) {
    state.viewMemberProfileUserId = null;
    state.memberProfileDetail = null;
    state.memberProfileActiveTab = "overview";
    state.memberProfileBlackjackPage = 0;
    state.memberProfileSlotPage = 0;
  }
  const showMemberDetailPage =
    tab === "members" && state.viewMemberProfileUserId != null && state.memberProfileDetail;
  let modalOverlay = "";
  if (state.editPasswordUserId != null) {
    const pu = state.members.find((m) => m.id === state.editPasswordUserId);
    if (pu) modalOverlay = passwordModalHtml(pu);
    else state.editPasswordUserId = null;
  } else if (state.editMarketId) {
    const mp = state.market.find((m) => m.id === state.editMarketId);
    if (mp) modalOverlay = marketModalHtml(mp);
    else state.editMarketId = null;
  } else if (state.editSponsorId) {
    const sp = state.sponsors.find((s) => s.id === state.editSponsorId);
    if (sp) modalOverlay = sponsorModalHtml(sp);
    else state.editSponsorId = null;
  } else if (state.editSponsorPromoId) {
    const row = (state.sponsorPromoCodes || []).find((r) => r.id === state.editSponsorPromoId);
    if (row) modalOverlay = sponsorPromoCodeModalHtml(row);
    else state.editSponsorPromoId = null;
  } else if (state.editBonusId) {
    const row = (state.bonuses || []).find((r) => r.id === state.editBonusId);
    if (row) modalOverlay = bonusModalHtml(row);
    else state.editBonusId = null;
  } else if (state.staffEditOp) {
    modalOverlay = staffEditModalHtml(state.staffEditOp);
  }
  const si = normalizeSiteForAdmin(state.site || {});
  const adminBrandLogoUrl = String(si.logoUrl || "").trim();
  const adminBrandLogo = adminBrandLogoUrl
    ? `<img class="admin-sidebar-brand__logo-img" src="${escapeHtml(adminBrandLogoUrl)}" alt="Mocker" loading="lazy" decoding="async" />`
    : `<span class="admin-sidebar-brand__logo-fallback">Mocker</span>`;
  const mt = si.maintenance || normalizeMaintenanceForAdmin({});
  const po = state.popup && typeof state.popup === "object" ? state.popup : {};
  const gt = state.gamdomTurnuva && typeof state.gamdomTurnuva === "object" ? state.gamdomTurnuva : {};
  const lp = normalizeLegalPagesForAdmin(state.legalPages || {});
  const membersFilterActive =
    (state.membersFilter || "").trim() !== "" ||
    (state.membersFilterMinCoins || "").trim() !== "" ||
    (state.membersFilterMaxCoins || "").trim() !== "" ||
    state.membersFilterShop !== "all" ||
    state.membersFilterMultiIp !== "all" ||
    Object.values(state.membersFilterTags || {}).some(Boolean);
  const kpiPreview = tab === "members" && !showMemberDetailPage ? computeMembersKpis() : null;
  const membersStatsLine =
    kpiPreview && membersFilterActive && kpiPreview.filtered !== kpiPreview.total
      ? `Gösterilen <span class="admin-members-stats__num">${kpiPreview.filtered.toLocaleString("tr-TR")}</span> / <span class="admin-members-stats__num">${kpiPreview.total.toLocaleString("tr-TR")}</span>`
      : "";
  const membersSearchPlaceholder = "Kullanıcı, e-posta, ID, IP, iç not…";
  const pendingComplaintsCount = (state.complaints || []).filter((c) => String(c?.status || "pending") === "pending").length;
  const pendingAffiliateCount = (state.affiliateRequests || []).filter((c) => String(c?.status || "pending") === "pending").length;
  const navCtx = { pendingComplaintsCount, pendingAffiliateCount };
  const pageTitle =
    tab === "my-profile"
      ? "Profilim"
      : tab === "dashboard"
      ? "Kontrol paneli"
      : tab === "audit"
        ? "Denetim günlüğü"
        : tab === "staff"
          ? "Ekip & izinler"
          : tab === "sponsors"
            ? "Sponsor kartları"
            : tab === "market"
              ? "Market düzenlemesi"
              : tab === "social"
                ? "Sosyal linkler"
                : tab === "site"
                  ? "İletişim & site"
                  : tab === "maintenance"
                    ? "Bakım modu"
                  : tab === "popup"
                    ? "Açılış popup"
                    : tab === "legal-pages"
                      ? "Yasal sayfalar"
                    : tab === "gamdom-turnuva"
                      ? "Gamdom çevrim turnuvası"
                    : tab === "members"
                    ? showMemberDetailPage
                      ? `Üye: ${escapeHtml(String(state.memberProfileDetail.user?.username ?? ""))}`
                      : "Üye listesi"
                    : tab === "market-requests"
                      ? "Market talepleri"
                      : tab === "complaints"
                        ? "Şikayetler"
                        : tab === "affiliate-requests"
                          ? "Affiliate talepleri"
                          : tab === "ip-clashes"
                            ? "IP çakışmaları"
                            : tab === "chat"
                              ? "Sohbet moderasyonu"
                              : tab === "wheel"
                              ? "Şans çarkı"
                              : tab === "games-covers"
                                ? "Oyun ayarları"
                              : tab === "levels"
                                ? "Seviye sistemi"
                              : tab === "promo-codes"
                                ? "Promo kodlar"
                                : tab === "sponsor-promo-codes"
                                  ? "Site promo kodları"
                                : tab === "bonuses"
                                  ? "Site bonusları"
                                : tab === "telegram-invites"
                                  ? "Telegram davet"
                                : tab === "telegram-broadcast"
                                  ? "Telegram yayın"
                                : tab === "special-odds"
                                  ? "Özel oranlar"
                                : tab === "tickets"
                                  ? "Biletler"
                                : tab === "site-events"
                                  ? "Etkinlikler"
                                : tab === "site-news"
                                  ? "Haberler"
                                : tab === "site-notifications"
                                  ? "Bildirimler"
                                : tab === "css"
                                  ? "Özel CSS"
                                  : "Panel";

  captureAdminSidebarNavScroll();
  captureAdminSidebarSearchQuery();
  ensureAdminNoFavicon();

  $("#app").innerHTML = `
    <div class="admin-app">
      <div class="admin-drawer-backdrop" id="admin-drawer-backdrop" aria-hidden="true"></div>
      <aside id="admin-sidebar" class="admin-sidebar">
        <div class="admin-sidebar__topbar">
          <button type="button" class="admin-icon-btn" id="admin-drawer-close" aria-label="Menüyü kapat">
            <svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/></svg>
          </button>
        </div>
        <div class="admin-sidebar-head">
          <a href="#dashboard" class="admin-sidebar-brand" aria-label="Dashboard'a git">
            <div class="admin-logo-mark admin-logo-mark--image" aria-hidden="true">${adminBrandLogo}</div>
          </a>
          <button type="button" id="admin-sidebar-collapse-toggle" class="admin-icon-btn admin-sidebar-collapse-toggle" aria-label="Kenar çubuğunu daralt" aria-expanded="true" title="Kenar çubuğunu daralt">
            <svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"/></svg>
          </button>
        </div>
        <nav class="admin-sidebar__nav" aria-label="Yönetim menüsü">
          ${adminSidebarNavHtml(tab, navCtx)}
        </nav>
        <div class="admin-sidebar-footer">
          <div class="admin-sidebar-footer__actions">
            <a href="/#/" class="admin-sidebar-footer__link" title="Ana siteye dön" aria-label="Ana siteye dön">
              <svg class="admin-sidebar-footer__ico" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"/></svg>
              <span>Ana siteye dön</span>
            </a>
            <button type="button" id="btn-logout" class="admin-sidebar-footer__btn" title="Oturumu kapat" aria-label="Oturumu kapat">
              <svg class="admin-sidebar-footer__ico" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"/></svg>
              <span>Oturumu kapat</span>
            </button>
          </div>
          <p class="admin-sidebar-hint" role="note">Esc: menü veya açık pencere</p>
        </div>
      </aside>
      <div class="admin-main-column">
        <header class="admin-topbar">
          <div class="admin-topbar__lead">
            <button type="button" id="admin-drawer-open" class="admin-icon-btn admin-drawer-toggle" aria-label="Menüyü aç" aria-expanded="false" aria-controls="admin-sidebar">
              <svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/></svg>
            </button>
          <div class="admin-topbar__text">
            <div class="admin-topbar-search">
              <div class="admin-topbar-search__field">
                <span class="admin-topbar-search__icon" aria-hidden="true">
                  <svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/></svg>
                </span>
                <input
                  type="search"
                  id="admin-sidebar-search-input"
                  class="admin-topbar-search__input admin-input"
                  placeholder="Menüde ara…"
                  aria-label="Menüde ara"
                  autocomplete="off"
                  spellcheck="false"
                />
                <button type="button" id="admin-sidebar-search-clear" class="admin-topbar-search__clear" hidden aria-label="Aramayı temizle">
                  <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/></svg>
                </button>
              </div>
              <p id="admin-sidebar-search-empty" class="admin-topbar-search__empty" hidden role="status">Sonuç bulunamadı.</p>
            </div>
          </div>
          </div>
          <div class="admin-topbar__actions">
            <div id="admin-header-alerts-host">${adminHeaderAlertsButtonHtml()}</div>
            ${adminThemeToggleHtml({ compact: true })}
            <button type="button" id="btn-my-admin-profile-open" class="admin-icon-btn" aria-label="Profilim" title="Profilim">
              ${iconSvg("user", "h-5 w-5")}
            </button>
          </div>
        </header>
        <main class="admin-main">
          <div id="tab-dashboard" class="tab-panel w-full ${tab === "dashboard" ? "" : "hidden"}">
            ${dashboardPanelHtml()}
          </div>
          <div id="tab-my-profile" class="tab-panel w-full ${tab === "my-profile" ? "" : "hidden"}">
            ${myAdminProfileModalHtml()}
          </div>
          <div id="tab-audit" class="tab-panel w-full max-w-7xl ${tab === "audit" ? "" : "hidden"}">
            ${auditLogPanelHtml()}
          </div>
          <div id="tab-sponsor-promo-codes" class="tab-panel w-full ${tab === "sponsor-promo-codes" ? "" : "hidden"}">
            ${sponsorPromoCodesPanelHtml()}
          </div>
          <div id="tab-bonuses" class="tab-panel w-full ${tab === "bonuses" ? "" : "hidden"}">
            ${bonusesPanelHtml()}
          </div>
          <div id="tab-sponsors" class="tab-panel w-full ${tab === "sponsors" ? "" : "hidden"}">
            ${sponsorsPanelHtml()}
          </div>
          <div id="tab-market" class="tab-panel w-full ${tab === "market" ? "" : "hidden"}">
            ${marketPanelHtml()}
          </div>
          <div id="tab-social" class="tab-panel w-full ${tab === "social" ? "" : "hidden"}">
            ${socialPanelHtml()}
          </div>
          <div id="tab-members" class="tab-panel admin-members-page w-full ${tab === "members" ? "" : "hidden"}">
            ${showMemberDetailPage
      ? memberProfileDetailPageHtml(state.memberProfileDetail)
      : `
            <header class="admin-data-page__head admin-members-page-head">
              <h2 class="admin-data-page__title">Üye listesi</h2>
            </header>
            <div id="members-kpi-mount">${membersLoadStatsBannerHtml()}</div>
            <div class="admin-members-toolbar">
              <header class="admin-members-toolbar__head">
                <div class="admin-members-toolbar__head-text">
                  <span class="admin-members-toolbar__eyebrow">Filtre &amp; arama</span>
                  ${membersStatsLine ? `<p class="admin-members-toolbar__stats" aria-live="polite">${membersStatsLine}</p>` : ""}
                </div>
                <button type="button" id="members-filter-reset" class="admin-members-toolbar__reset">Sıfırla</button>
              </header>
              <div class="admin-members-toolbar__body">
                <div class="admin-members-search">
                  <label class="admin-members-field-label" for="members-search">Ara</label>
                  <div class="admin-members-search__field">
                    <svg class="admin-members-search__icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/></svg>
                    <input id="members-search" type="search" enterkeyhint="search" class="admin-input admin-members-search__input" placeholder="${escapeHtml(membersSearchPlaceholder)}" value="${escapeHtml(state.membersFilter)}" autocomplete="off" />
                    ${state.membersFilter ? `<button type="button" id="members-search-clear" class="admin-members-search__clear" title="Aramayı temizle" aria-label="Aramayı temizle"><svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/></svg></button>` : ""}
                  </div>
                </div>
                <div class="admin-members-filters">
                  <div class="admin-members-filter-cell">
                    <label class="admin-members-field-label" for="members-filter-min">Min</label>
                    <input id="members-filter-min" type="number" min="0" step="1" inputmode="numeric" class="admin-input tabular-nums" placeholder="—" value="${escapeHtml(state.membersFilterMinCoins)}" title="Minimum coin" />
                  </div>
                  <div class="admin-members-filter-cell">
                    <label class="admin-members-field-label" for="members-filter-max">Max</label>
                    <input id="members-filter-max" type="number" min="0" step="1" inputmode="numeric" class="admin-input tabular-nums" placeholder="—" value="${escapeHtml(state.membersFilterMaxCoins)}" title="Maksimum coin" />
                  </div>
                  <div class="admin-members-filter-cell admin-members-filter-cell--wide">
                    <label class="admin-members-field-label" for="members-filter-shop">Market</label>
                    <select id="members-filter-shop" class="admin-input" title="Market işlemi">
                      <option value="all" ${state.membersFilterShop === "all" ? "selected" : ""}>Tümü</option>
                      <option value="yes" ${state.membersFilterShop === "yes" ? "selected" : ""}>≥1 işlem</option>
                      <option value="no" ${state.membersFilterShop === "no" ? "selected" : ""}>İşlem yok</option>
                    </select>
                  </div>
                  <div class="admin-members-filter-cell admin-members-filter-cell--grow">
                    <label class="admin-members-field-label" for="members-filter-multi-ip">Aynı IP</label>
                    <select id="members-filter-multi-ip" class="admin-input" title="Çoklu hesap (kayıt IP)">
                      <option value="all" ${state.membersFilterMultiIp === "all" ? "selected" : ""}>Tümü</option>
                      <option value="yes" ${state.membersFilterMultiIp === "yes" ? "selected" : ""}>≥2 hesap</option>
                      <option value="no" ${state.membersFilterMultiIp === "no" ? "selected" : ""}>Tek / boş</option>
                    </select>
                  </div>
                </div>
                <div class="admin-members-filter-tags-row">
                  <span class="admin-members-field-label">Etiket (işaretlenenlerden en az biri)</span>
                  <div class="admin-members-filter-tags" role="group" aria-label="Etiket filtresi">
                    ${MEMBER_ADMIN_TAG_DEFS.map((d) => {
        const mft = state.membersFilterTags || { suspicious: false, vip: false, banned: false };
        const checked = !!mft[d.id];
        return `<label class="admin-members-tag-filter"><input type="checkbox" id="members-filter-tag-${d.id}" data-members-filter-tag="${d.id}" ${checked ? "checked" : ""} /> ${escapeHtml(d.label)}</label>`;
      }).join("")}
                  </div>
                </div>
              </div>
            </div>
            <div id="members-list-mount">${membersPanelHtml()}</div>
            `
    }
          </div>
          <div id="tab-market-requests" class="tab-panel w-full ${tab === "market-requests" ? "" : "hidden"}">
            ${marketRequestsPanelHtml()}
          </div>
          <div id="tab-complaints" class="tab-panel w-full ${tab === "complaints" ? "" : "hidden"}">
            ${complaintsPanelHtml()}
          </div>
          <div id="tab-affiliate-requests" class="tab-panel w-full ${tab === "affiliate-requests" ? "" : "hidden"}">
            ${affiliateRequestsPanelHtml()}
          </div>
          <div id="tab-ip-clashes" class="tab-panel w-full ${tab === "ip-clashes" ? "" : "hidden"}">
            ${ipClashesPanelHtml()}
          </div>
          <div id="tab-chat" class="tab-panel w-full max-w-7xl ${tab === "chat" ? "" : "hidden"}">
            ${canPerm("chat") ? adminChatPanelHtml() : `<p class="text-sm text-slate-500">Bu sekme için izin yok.</p>`}
          </div>
          <div id="tab-site" class="tab-panel w-full ${tab === "site" ? "" : "hidden"}">
            ${siteContactPanelHtml(si)}
          </div>
          <div id="tab-maintenance" class="tab-panel w-full ${tab === "maintenance" ? "" : "hidden"}">
            ${maintenancePanelHtml(mt)}
          </div>
          <div id="tab-gamdom-turnuva" class="tab-panel max-w-4xl ${tab === "gamdom-turnuva" ? "" : "hidden"}">
            ${gamdomTurnuvaPanelHtml(gt)}
          </div>
          <div id="tab-legal-pages" class="tab-panel w-full ${tab === "legal-pages" ? "" : "hidden"}">
            ${legalPagesPanelHtml(lp)}
          </div>
          <div id="tab-popup" class="tab-panel max-w-4xl ${tab === "popup" ? "" : "hidden"}">
            ${cmsPanelToolbarHtml(`<span class="admin-data-count text-sm text-slate-500">Ana site açılış popup</span>`)}
            <div class="admin-panel admin-panel--padded space-y-4">
              <label class="flex cursor-pointer items-start gap-3">
                <input type="checkbox" id="popup-enabled" class="admin-checkbox" ${po.enabled ? "checked" : ""} />
                <span class="text-sm text-slate-600">Açılış popup aktif (görsel zorunlu)</span>
              </label>
              <div>
                <label class="admin-label" for="popup-title">Popup başlığı (isteğe bağlı)</label>
                <input id="popup-title" class="admin-input" value="${escapeHtml(po.title || "")}" placeholder="Örn. Yeni etkinlik duyurusu" maxlength="120" />
              </div>
              <div>
                <label class="admin-label" for="popup-image-url">Görsel URL (https)</label>
                <input id="popup-image-url" class="admin-input" value="${escapeHtml(po.imageUrl || "")}" placeholder="https://example.com/popup.png" />
              </div>
              <div>
                <label class="admin-label" for="popup-link-url">Görsel linki (isteğe bağlı)</label>
                <p class="mb-1 text-xs text-slate-500">Boş bırakırsan görsel tıklanmaz. Harici sayfa için <code class="admin-code">https://</code> kullanın.</p>
                <input id="popup-link-url" class="admin-input" value="${escapeHtml(po.linkUrl || "")}" placeholder="Boş veya https://example.com" />
              </div>
              <div class="grid gap-4 sm:grid-cols-2">
                <div>
                  <label class="admin-label" for="popup-start">Başlangıç (isteğe bağlı)</label>
                  <p class="mb-1 text-xs text-slate-500">Boş = hemen geçerli</p>
                  <input id="popup-start" type="datetime-local" class="admin-input" value="${escapeHtml(unixToDatetimeLocal(po.startAt))}" />
                </div>
                <div>
                  <label class="admin-label" for="popup-end">Bitiş (isteğe bağlı)</label>
                  <p class="mb-1 text-xs text-slate-500">Boş = süresiz</p>
                  <input id="popup-end" type="datetime-local" class="admin-input" value="${escapeHtml(unixToDatetimeLocal(po.endAt))}" />
                </div>
              </div>
              <div>
                <label class="admin-label" for="popup-frequency">«Kapat» sonrası tekrar gösterme</label>
                <select id="popup-frequency" class="admin-input">
                  <option value="session" ${po.frequency === "day" ? "" : "selected"}>Bu oturumda bir kez (yeni sekme = tekrar)</option>
                  <option value="day" ${po.frequency === "day" ? "selected" : ""}>Günde en fazla bir kez</option>
                </select>
                <p class="mt-1 text-xs text-slate-500">«Bir daha gösterme» kalıcıdır (aynı içerik); admin’de görsel/başlık değişince tekrar çıkabilir.</p>
              </div>
            </div>
          </div>
          <div id="tab-css" class="tab-panel max-w-4xl ${tab === "css" ? "" : "hidden"}">
            ${cmsPanelToolbarHtml()}
            <div class="admin-panel admin-panel--padded space-y-4">
              <div>
                <label class="admin-label">Özel CSS (site geneli)</label>
                <p class="mb-2 text-xs leading-relaxed text-slate-500">
                  Tailwind ve sabit stillerden sonra uygulanır. Site genelinde <code class="text-accent-bright/90">k-</code> önekli sınıflar kullanılır (ör. <code class="text-accent-bright/90">.k-btn--primary</code>, <code class="text-accent-bright/90">.k-nav__item</code>, <code class="text-accent-bright/90">.k-input</code>). Tam liste: <code class="text-accent-bright/90">js/constants/site-hooks.js</code> ve <code class="text-accent-bright/90">index.html</code> içindeki yorum. En fazla 200.000 karakter.
                </p>
                <textarea id="site-custom-css" class="admin-input min-h-[320px] resize-y font-mono text-xs leading-relaxed" spellcheck="false" rows="18" placeholder=" ">${escapeHtml(si.customCss || "")}</textarea>
              </div>
            </div>
          </div>
          <div id="tab-wheel" class="tab-panel max-w-2xl ${tab === "wheel" ? "" : "hidden"}">
            ${wheelPanelHtml()}
          </div>
          <div id="tab-games-covers" class="tab-panel w-full ${tab === "games-covers" ? "" : "hidden"}">
            ${gamesCoversPanelHtml()}
          </div>
          <div id="tab-levels" class="tab-panel w-full ${tab === "levels" ? "" : "hidden"}">
            ${levelSystemPanelHtml()}
          </div>
          <div id="tab-special-odds" class="tab-panel w-full ${tab === "special-odds" ? "" : "hidden"}">
            ${specialOddsPanelHtml()}
          </div>
          <div id="tab-panel-promo-codes" class="tab-panel w-full ${tab === "promo-codes" ? "" : "hidden"}">
            ${promoPanelHtml()}
          </div>
          <div id="tab-panel-telegram-invites" class="tab-panel w-full ${tab === "telegram-invites" ? "" : "hidden"}">
            ${telegramInvitesPanelHtml()}
          </div>
          <div id="tab-panel-telegram-broadcast" class="tab-panel w-full ${tab === "telegram-broadcast" ? "" : "hidden"}">
            ${canPerm("telegram-broadcast") ? telegramBroadcastPanelHtml() : ""}
          </div>
          <div id="tab-site-events" class="tab-panel tab-panel--full admin-tickets-wrap ${tab === "site-events" ? "" : "hidden"}">
            ${canPerm("site-events") ? siteEventsPanelHtml() : ""}
          </div>
          <div id="tab-site-news" class="tab-panel w-full ${tab === "site-news" ? "" : "hidden"}">
            ${canPerm("site-news") ? siteNewsPanelHtml() : ""}
          </div>
          <div id="tab-site-notifications" class="tab-panel w-full ${tab === "site-notifications" ? "" : "hidden"}">
            ${canPerm("site-notifications") ? siteNotificationsPanelHtml() : ""}
          </div>
          <div id="tab-tickets" class="tab-panel tab-panel--full admin-tickets-wrap ${tab === "tickets" ? "" : "hidden"}">
            ${canPerm("tickets") ? ticketsPanelHtml() : ""}
          </div>
          <div id="tab-staff" class="tab-panel max-w-6xl ${tab === "staff" ? "" : "hidden"}">
            ${staffPanelHtml()}
          </div>
        </main>
      </div>
    </div>
    <div class="admin-mobile-dock">
      ${adminMobileDockNavHtml(tab, navCtx)}
    </div>
    ${modalOverlay}`;

  document.title = `Mocker — ${pageTitle}`;

  syncAdminUrlToTab();
  bindDashboard();
  ensureMembersTabDelegation();
  if (tab === "games-covers") {
    bindGamesCoversPreview();
    bindGamesWinChanceInputs();
  }
  if (tab === "tickets" || tab === "site-events") {
    bindTicketCoverPreviews();
    bindEventCreateSponsorAutofill();
  }
  if (tab === "site-news") {
    bindTicketCoverPreviews();
  }
  if (tab === "gamdom-turnuva") {
    bindTicketCoverPreviews();
  }
  syncAdminChatPolling();
  syncAdminAlertsPolling();

  queueMicrotask(() => {
    requestAnimationFrame(() => {
      restoreAdminSidebarNavScroll();
      requestAnimationFrame(() => restoreAdminSidebarNavScroll());
    });
  });
}

function applySponsorModalToState() {
  const id = state.editSponsorId;
  if (!id) return;
  const idx = state.sponsors.findIndex((x) => x.id === id);
  if (idx === -1) {
    state.editSponsorId = null;
    renderDashboard();
    return;
  }
  const promoRaw = $("#modal-fld-promo").value;
  const promo = promoRaw
    .split("\n")
    .map((x) => x.trim())
    .filter(Boolean);
  const badge = "ana-sponsorlar";
  state.sponsors[idx] = {
    ...state.sponsors[idx],
    title: $("#modal-fld-title").value.trim(),
    logo: $("#modal-fld-logo").value.trim(),
    url: $("#modal-fld-url").value.trim(),
    description: $("#modal-fld-desc").value.trim(),
    promo,
    badge,
  };
  state.editSponsorId = null;
  renderDashboard();
  toast("Kart kaydedildi — sitede yayınlamak için Değişiklikleri kaydet");
}

function closeSponsorModal() {
  state.editSponsorId = null;
  renderDashboard();
}

function applyMarketModalToState() {
  const oldId = state.editMarketId;
  if (!oldId) return;
  const idx = state.market.findIndex((x) => x.id === oldId);
  if (idx === -1) {
    state.editMarketId = null;
    renderDashboard();
    return;
  }
  const newId = $("#market-modal-fld-id")?.value.trim() || "";
  const title = $("#market-modal-fld-title")?.value.trim() || "";
  const logo = $("#market-modal-fld-logo")?.value.trim() || "";
  const desc = $("#market-modal-fld-desc")?.value.trim() || "";
  const price = Math.max(1, parseInt(String($("#market-modal-fld-price")?.value || "0"), 10) || 0);
  const stock = Math.max(0, parseInt(String($("#market-modal-fld-stock")?.value || "0"), 10) || 0);
  if (!newId || !title) {
    toast("Ürün kodu ve başlık zorunlu.", "err");
    return;
  }
  if (logo !== "" && !/^https?:\/\//i.test(logo)) {
    toast("Görsel adresi http:// veya https:// ile başlamalı.", "err");
    return;
  }
  if (newId !== oldId && state.market.some((m) => m.id === newId)) {
    toast("Bu ürün kodu zaten kullanılıyor.", "err");
    return;
  }
  const row = { ...state.market[idx], id: newId, title, logo, description: desc, price, stock };
  state.market[idx] = row;
  state.editMarketId = null;
  renderDashboard();
  toast("Ürün kaydedildi — sitede yayınlamak için Değişiklikleri kaydet");
}

function closeMarketModal() {
  state.editMarketId = null;
  renderDashboard();
}

function applySponsorPromoModalToState() {
  const editId = state.editSponsorPromoId;
  if (!editId) return;
  const idx = (state.sponsorPromoCodes || []).findIndex((x) => x.id === editId);
  const sponsorId = String($("#spc-modal-sponsor")?.value || "").trim();
  const sponsor = (state.sponsors || []).find((s) => s.id === sponsorId);
  const sponsorTitle = sponsor ? String(sponsor.title || "").trim() : "";
  const code = String($("#spc-modal-code")?.value || "").trim();
  const label = String($("#spc-modal-label")?.value || "").trim();
  const description = String($("#spc-modal-desc")?.value || "").trim();
  const url = String($("#spc-modal-url")?.value || "").trim();
  const isActive = Boolean($("#spc-modal-active")?.checked);
  const expVal = String($("#spc-modal-expires")?.value || "").trim();
  let expiresAt = null;
  if (expVal) {
    const t = new Date(expVal).getTime();
    if (!Number.isNaN(t)) expiresAt = Math.floor(t / 1000);
  }
  if (!sponsorId || !sponsorTitle) {
    toast("Sponsor seçmelisin.", "err");
    return;
  }
  if (!code || code.length < 2) {
    toast("Promo kod en az 2 karakter olmalı.", "err");
    return;
  }
  if (url !== "" && !/^https?:\/\//i.test(url)) {
    toast("Link http:// veya https:// ile başlamalı.", "err");
    return;
  }
  const base =
    idx >= 0
      ? { ...state.sponsorPromoCodes[idx] }
      : { ...newSponsorPromoCode(), id: editId };
  const row = {
    ...base,
    sponsorId,
    sponsorTitle,
    code,
    label,
    description,
    expiresAt,
    url,
    isActive,
    createdAt: base.createdAt || Math.floor(Date.now() / 1000),
  };
  if (idx >= 0) {
    state.sponsorPromoCodes[idx] = row;
  } else {
    if (!state.sponsorPromoCodes) state.sponsorPromoCodes = [];
    state.sponsorPromoCodes.push(row);
  }
  state.editSponsorPromoId = null;
  renderDashboard();
  toast("Kayıt güncellendi — sitede yayınlamak için Değişiklikleri kaydet");
}

function closeSponsorPromoModal() {
  const editId = state.editSponsorPromoId;
  if (editId) {
    const row = (state.sponsorPromoCodes || []).find((r) => r.id === editId);
    if (row && !String(row.code || "").trim()) {
      state.sponsorPromoCodes = (state.sponsorPromoCodes || []).filter((x) => x.id !== editId);
    }
  }
  state.editSponsorPromoId = null;
  renderDashboard();
}

function applyBonusModalToState() {
  const editId = state.editBonusId;
  if (!editId) return;
  const idx = (state.bonuses || []).findIndex((x) => x.id === editId);
  const sponsorId = String($("#bonus-modal-sponsor")?.value || "").trim();
  const sponsor = (state.sponsors || []).find((s) => s.id === sponsorId);
  const sponsorTitle = sponsor ? String(sponsor.title || "").trim() : "";
  const title = String($("#bonus-modal-title")?.value || "").trim();
  const badge = String($("#bonus-modal-badge")?.value || "").trim();
  const description = String($("#bonus-modal-desc")?.value || "").trim();
  const promoCode = String($("#bonus-modal-promo")?.value || "").trim();
  const url = String($("#bonus-modal-url")?.value || "").trim();
  const isActive = Boolean($("#bonus-modal-active")?.checked);
  const expVal = String($("#bonus-modal-expires")?.value || "").trim();
  let expiresAt = null;
  if (expVal) {
    const t = new Date(expVal).getTime();
    if (!Number.isNaN(t)) expiresAt = Math.floor(t / 1000);
  }
  if (!sponsorId || !sponsorTitle) {
    toast("Sponsor seçmelisin.", "err");
    return;
  }
  if (!title || title.length < 2) {
    toast("Bonus başlığı en az 2 karakter olmalı.", "err");
    return;
  }
  if (url !== "" && !/^https?:\/\//i.test(url)) {
    toast("Link http:// veya https:// ile başlamalı.", "err");
    return;
  }
  const base = idx >= 0 ? { ...state.bonuses[idx] } : { ...newBonus(), id: editId };
  const row = {
    ...base,
    sponsorId,
    sponsorTitle,
    title,
    badge,
    description,
    promoCode,
    expiresAt,
    url,
    isActive,
    createdAt: base.createdAt || Math.floor(Date.now() / 1000),
  };
  if (idx >= 0) {
    state.bonuses[idx] = row;
  } else {
    if (!state.bonuses) state.bonuses = [];
    state.bonuses.push(row);
  }
  state.editBonusId = null;
  renderDashboard();
  toast("Bonus güncellendi — sitede yayınlamak için Değişiklikleri kaydet");
}

function closeBonusModal() {
  const editId = state.editBonusId;
  if (editId) {
    const row = (state.bonuses || []).find((r) => r.id === editId);
    if (row && !String(row.title || "").trim()) {
      state.bonuses = (state.bonuses || []).filter((x) => x.id !== editId);
    }
  }
  state.editBonusId = null;
  renderDashboard();
}

function closePasswordModal() {
  state.editPasswordUserId = null;
  renderDashboard();
}

function closeMemberProfileModal() {
  state.viewMemberProfileUserId = null;
  state.memberProfileDetail = null;
  state.memberProfileActiveTab = "overview";
  state.memberProfileBlackjackPage = 0;
  state.memberProfileSlotPage = 0;
  renderDashboard();
}

async function applyPasswordModal() {
  const uid = state.editPasswordUserId;
  if (uid == null) return;
  const p1 = $("#pw-modal-new")?.value || "";
  const p2 = $("#pw-modal-confirm")?.value || "";
  const errEl = $("#pw-modal-err");
  if (errEl) errEl.textContent = "";
  if (p1.length < 8) {
    if (errEl) errEl.textContent = "Şifre en az 8 karakter olmalı.";
    return;
  }
  if (p1 !== p2) {
    if (errEl) errEl.textContent = "Şifreler eşleşmiyor.";
    return;
  }
  try {
    await api("admin_user_password.php", {
      method: "POST",
      body: JSON.stringify({ userId: uid, password: p1 }),
    });
    state.editPasswordUserId = null;
    renderDashboard();
    toast("Şifre güncellendi.");
  } catch (e) {
    const code = e.body?.error;
    const msg =
      code === "password"
        ? "Şifre en az 8 karakter olmalı."
        : code === "not_found"
          ? "Kullanıcı bulunamadı."
          : e.message || "Şifre kaydedilemedi";
    toast(msg, "err");
  }
}

function bindDashboard() {
  ensureAdminDrawerEscapeListener();
  $("#admin-drawer-open")?.addEventListener("click", () => openAdminDrawer());
  $("#admin-drawer-close")?.addEventListener("click", () => closeAdminDrawer());
  $("#admin-drawer-backdrop")?.addEventListener("click", () => closeAdminDrawer());

  bindAdminSidebarSearch();
  bindAdminSidebarNavGroups();
  bindAdminSidebarCollapseToggle();
  bindAdminThemeToggle();

  $("#btn-my-admin-profile-open")?.addEventListener("click", async () => {
    try {
      state.tab = "my-profile";
      await loadDataForAdminTab("my-profile");
      renderDashboard();
    } catch (e) {
      toast(e?.message || "Profil bilgisi yüklenemedi", "err");
    }
  });
  $("#admin-my-profile-form")?.addEventListener("submit", async (e) => {
    e.preventDefault();
    const displayName = String($("#my-admin-display-name")?.value || "").trim();
    try {
      await api("admin_my_profile.php", {
        method: "POST",
        body: JSON.stringify({ action: "update_profile", displayName }),
      });
      await loadSession();
      await loadMyAdminProfile();
      toast("Profil bilgileri güncellendi", "ok");
      renderDashboard();
    } catch (err) {
      toast(err?.message || "Profil kaydedilemedi", "err");
    }
  });
  $("#admin-my-password-form")?.addEventListener("submit", async (e) => {
    e.preventDefault();
    const currentPassword = String($("#my-admin-current-password")?.value || "");
    const newPassword = String($("#my-admin-new-password")?.value || "");
    const newPasswordRepeat = String($("#my-admin-new-password-repeat")?.value || "");
    if (newPassword !== newPasswordRepeat) {
      toast("Yeni şifre tekrarı uyuşmuyor", "err");
      return;
    }
    try {
      await api("admin_my_profile.php", {
        method: "POST",
        body: JSON.stringify({ action: "update_password", currentPassword, newPassword }),
      });
      const cp = $("#my-admin-current-password");
      const np = $("#my-admin-new-password");
      const nr = $("#my-admin-new-password-repeat");
      if (cp) cp.value = "";
      if (np) np.value = "";
      if (nr) nr.value = "";
      toast("Şifre güncellendi", "ok");
    } catch (err) {
      toast(err?.message || "Şifre güncellenemedi", "err");
    }
  });
  $("#btn-admin-2fa-start")?.addEventListener("click", async () => {
    try {
      const j = await api("admin_2fa.php", { method: "POST", body: JSON.stringify({ action: "start_setup" }) });
      const box = $("#admin-2fa-setup-box");
      const qr = $("#admin-2fa-qr");
      const secret = $("#admin-2fa-secret");
      if (box) box.classList.remove("hidden");
      if (qr && j?.setup?.qrUrl) qr.src = String(j.setup.qrUrl);
      if (secret && j?.setup?.secret) secret.textContent = `Secret: ${String(j.setup.secret)}`;
      toast("QR kod hazır", "ok");
    } catch (err) {
      toast(err?.message || "2FA kurulumu başlatılamadı", "err");
    }
  });
  $("#btn-admin-2fa-confirm")?.addEventListener("click", async () => {
    const code = String($("#admin-2fa-verify-code")?.value || "").trim();
    if (!code) {
      toast("2FA kodunu gir", "err");
      return;
    }
    try {
      const j = await api("admin_2fa.php", { method: "POST", body: JSON.stringify({ action: "confirm_setup", code }) });
      const codes = Array.isArray(j?.backupCodes) ? j.backupCodes : [];
      if (codes.length) alert(`2FA yedek kodların:\n\n${codes.join("\n")}\n\nBu kodları güvenli yerde sakla.`);
      await loadMyAdminProfile();
      renderDashboard();
      toast("2FA aktif edildi", "ok");
    } catch (err) {
      toast(err?.message || "2FA doğrulanamadı", "err");
    }
  });
  $("#btn-admin-2fa-regenerate")?.addEventListener("click", async () => {
    const code = prompt("Yedek kodları yenilemek için Authenticator kodunu gir:");
    if (!code) return;
    try {
      const j = await api("admin_2fa.php", { method: "POST", body: JSON.stringify({ action: "regenerate_backup", code }) });
      const codes = Array.isArray(j?.backupCodes) ? j.backupCodes : [];
      if (codes.length) alert(`Yeni yedek kodların:\n\n${codes.join("\n")}\n\nEski kodlar artık geçersiz.`);
      await loadMyAdminProfile();
      renderDashboard();
      toast("Yedek kodlar yenilendi", "ok");
    } catch (err) {
      toast(err?.message || "Yedek kodlar yenilenemedi", "err");
    }
  });
  $("#btn-admin-2fa-disable")?.addEventListener("click", async () => {
    const code = prompt("2FA kapatmak için Authenticator veya yedek kod gir:");
    if (!code) return;
    try {
      await api("admin_2fa.php", { method: "POST", body: JSON.stringify({ action: "disable", code }) });
      await loadMyAdminProfile();
      renderDashboard();
      toast("2FA kapatıldı", "ok");
    } catch (err) {
      toast(err?.message || "2FA kapatılamadı", "err");
    }
  });

  $("#admin-header-alerts-host")?.addEventListener("click", async (e) => {
    const t = e.target;
    if (!(t instanceof Element)) return;
    const toggleBtn = t.closest("#admin-alerts-toggle");
    if (toggleBtn) {
      e.preventDefault();
      e.stopPropagation();
      state.headerAlertsOpen = !state.headerAlertsOpen;
      updateAdminHeaderAlertsUi();
      return;
    }
    const tabBtn = t.closest("[data-admin-alert-tab]");
    if (!tabBtn) return;
    const next = String(tabBtn.getAttribute("data-admin-alert-tab") || "");
    if (!next || !canPerm(next)) return;
    state.headerAlertsOpen = false;
    state.tab = next;
    try {
      await loadDataForAdminTab(next);
    } catch (err) {
      toast(err?.message || "Sekme yüklenemedi", "err");
    }
    renderDashboard();
  });
  $("#admin-header-alerts-host")?.addEventListener("change", (e) => {
    const t = e.target;
    if (!(t instanceof HTMLInputElement)) return;
    if (t.id !== "admin-alerts-sound") return;
    state.headerAlertsSound = t.checked;
  });
  if (!adminAlertsDocClickBound) {
    adminAlertsDocClickBound = true;
    document.addEventListener("click", (e) => {
      const root = document.getElementById("admin-header-alerts");
      if (!root || !state.headerAlertsOpen) return;
      const t = e.target;
      if (t instanceof Element && root.contains(t)) return;
      state.headerAlertsOpen = false;
      updateAdminHeaderAlertsUi();
    });
  }

  $("#btn-site-youtube-preview")?.addEventListener("click", async () => {
    const statusEl = $("#site-youtube-status");
    if (statusEl) {
      statusEl.hidden = false;
      statusEl.textContent = "YouTube akışı kontrol ediliyor…";
    }
    try {
      const j = await api(`youtube_feed.php?t=${Date.now()}`);
      if (j?.ok && Array.isArray(j.videos) && j.videos.length) {
        const stale = j.stale ? " (önbellek)" : "";
        const url = String(j.channelUrl || "").trim();
        if (statusEl) {
          statusEl.textContent = `${j.videos.length} video yüklendi${stale}${url ? ` — ${url}` : ""}. Önce Kaydet ile kanalı güncellediysen birkaç saniye bekleyip tekrar dene.`;
        }
      } else {
        if (statusEl) statusEl.textContent = "Akış alınamadı. Kanalı kaydettikten sonra tekrar dene veya kanal linkini kontrol et.";
      }
    } catch (e) {
      if (statusEl) statusEl.textContent = e.message || "Akış kontrol edilemedi.";
    }
  });

  $$(".tab-btn").forEach((btn) => {
    btn.addEventListener("click", async () => {
      captureCurrentTab();
      state.editSponsorId = null;
      state.editMarketId = null;
      state.editPasswordUserId = null;
      state.viewMemberProfileUserId = null;
      state.memberProfileDetail = null;
      state.memberProfileActiveTab = "overview";
      const next = btn.getAttribute("data-tab");
      if (!next) return;
      ensureSidebarNavOpenForTab(next);
      saveSidebarNavOpenState();
      state.tab = next;
      try {
        await loadDataForAdminTab(next);
      } catch (e) {
        toast(e.message || "Sekme yüklenemedi", "err");
      }
      if (isAdminMobileDrawerViewport()) closeAdminDrawer();
      renderDashboard();
    });
  });

  const list = $("#sponsor-list");
  if (list && window.Sortable) {
    Sortable.create(list, {
      handle: ".drag-handle",
      animation: 150,
      ghostClass: "sortable-ghost",
      onEnd: () => {
        const ids = [...list.querySelectorAll(".sponsor-preview-card")].map((el) => el.getAttribute("data-id"));
        const map = new Map(state.sponsors.map((s) => [s.id, s]));
        state.sponsors = ids.map((i) => map.get(i)).filter(Boolean);
      },
    });
  }
  const mlist = $("#market-list");
  if (mlist && window.Sortable) {
    Sortable.create(mlist, {
      handle: ".drag-handle-market",
      animation: 150,
      ghostClass: "sortable-ghost",
      onEnd: () => {
        const ids = [...mlist.querySelectorAll(".market-preview-card")].map((el) => el.getAttribute("data-id"));
        const map = new Map(state.market.map((m) => [m.id, m]));
        state.market = ids.map((i) => map.get(i)).filter(Boolean);
      },
    });
  }
  const slist = $("#social-list");
  if (slist && window.Sortable) {
    Sortable.create(slist, {
      handle: ".drag-handle-social",
      animation: 150,
      ghostClass: "sortable-ghost",
    });
  }

  $("#btn-add-sponsor")?.addEventListener("click", () => {
    captureCurrentTab();
    const ns = newSponsor();
    state.sponsors.push(ns);
    state.editSponsorId = ns.id;
    renderDashboard();
  });

  $("#btn-add-market")?.addEventListener("click", () => {
    captureCurrentTab();
    const nm = newMarket();
    state.market.push(nm);
    state.editMarketId = nm.id;
    renderDashboard();
  });

  $("#btn-add-spc")?.addEventListener("click", () => {
    captureCurrentTab();
    const row = newSponsorPromoCode();
    if (!state.sponsorPromoCodes) state.sponsorPromoCodes = [];
    state.sponsorPromoCodes.push(row);
    state.editSponsorPromoId = row.id;
    renderDashboard();
  });

  $("#btn-add-bonus")?.addEventListener("click", () => {
    captureCurrentTab();
    const row = newBonus();
    if (!state.bonuses) state.bonuses = [];
    state.bonuses.push(row);
    state.editBonusId = row.id;
    renderDashboard();
  });

  $("#btn-add-social")?.addEventListener("click", () => {
    captureCurrentTab();
    state.social.push(newSocial());
    renderDashboard();
  });

  $("#btn-refresh-dashboard")?.addEventListener("click", async () => {
    try {
      await loadDashboardStats();
      renderDashboard();
      toast("Özet güncellendi");
    } catch (e) {
      toast(e.message || "Özet yüklenemedi", "err");
    }
  });

  $("#btn-refresh-audit")?.addEventListener("click", async () => {
    try {
      await loadAuditLog();
      renderDashboard();
      toast("Denetim günlüğü güncellendi");
    } catch (e) {
      toast(e.message || "Yüklenemedi", "err");
    }
  });

  $("#btn-refresh-admin-chat")?.addEventListener("click", async () => {
    try {
      await loadAdminChat();
      renderDashboard();
      toast("Sohbet güncellendi");
    } catch (e) {
      toast(e.message || "Yüklenemedi", "err");
    }
  });

  $("#tab-chat")?.addEventListener("click", async (e) => {
    const t = e.target;
    if (!(t instanceof Element)) return;
    const del = t.closest(".btn-admin-chat-del");
    if (del) {
      const id = Number(del.getAttribute("data-msg-id"));
      if (!Number.isFinite(id) || id <= 0) return;
      if (!window.confirm("Bu mesaj silinsin mi? Üyeler artık görmez.")) return;
      try {
        const j = await api("admin_chat.php", {
          method: "POST",
          body: JSON.stringify({ action: "delete", messageId: id }),
        });
        if (typeof j.csrfToken === "string" && j.csrfToken) adminCsrfToken = j.csrfToken;
        await loadAdminChat();
        renderDashboard();
        toast("Mesaj silindi.");
      } catch (err) {
        toast(err.message || "İşlem başarısız", "err");
      }
      return;
    }
    const muteUser = t.closest(".btn-admin-chat-mute-user");
    if (muteUser) {
      const uid = Number(muteUser.getAttribute("data-user-id"));
      if (!Number.isFinite(uid) || uid <= 0) return;
      const minEl = $("#admin-chat-mute-min");
      const minutes = minEl && "value" in minEl ? Number(minEl.value) || 60 : 60;
      try {
        const j = await api("admin_chat.php", {
          method: "POST",
          body: JSON.stringify({ action: "mute", userId: uid, minutes }),
        });
        if (typeof j.csrfToken === "string" && j.csrfToken) adminCsrfToken = j.csrfToken;
        await loadAdminChat();
        renderDashboard();
        toast("Kullanıcı susturuldu.");
      } catch (err) {
        toast(err.message || "İşlem başarısız", "err");
      }
      return;
    }
    const unmute = t.closest(".btn-admin-chat-unmute");
    if (unmute) {
      const uid = Number(unmute.getAttribute("data-user-id"));
      if (!Number.isFinite(uid) || uid <= 0) return;
      try {
        const j = await api("admin_chat.php", {
          method: "POST",
          body: JSON.stringify({ action: "unmute", userId: uid }),
        });
        if (typeof j.csrfToken === "string" && j.csrfToken) adminCsrfToken = j.csrfToken;
        await loadAdminChat();
        renderDashboard();
        toast("Susturma kaldırıldı.");
      } catch (err) {
        toast(err.message || "İşlem başarısız", "err");
      }
    }
  });

  $("#btn-admin-chat-announce")?.addEventListener("click", async () => {
    const ta = $("#admin-chat-announce");
    const body = ta && "value" in ta ? String(ta.value || "").trim() : "";
    if (!body) {
      toast("Mesaj yaz.", "err");
      return;
    }
    try {
      const j = await api("admin_chat.php", {
        method: "POST",
        body: JSON.stringify({ action: "announce", body }),
      });
      if (typeof j.csrfToken === "string" && j.csrfToken) adminCsrfToken = j.csrfToken;
      if (ta instanceof HTMLTextAreaElement) ta.value = "";
      await loadAdminChat();
      renderDashboard();
      toast("Yönetim mesajı gönderildi.");
    } catch (e) {
      toast(e.message || "Gönderilemedi", "err");
    }
  });

  $("#btn-admin-chat-mute-apply")?.addEventListener("click", async () => {
    const uidEl = $("#admin-chat-mute-uid");
    const minEl = $("#admin-chat-mute-min");
    const uid = uidEl && "value" in uidEl ? Number(uidEl.value) : 0;
    const minutes = minEl && "value" in minEl ? Number(minEl.value) || 60 : 60;
    if (!Number.isFinite(uid) || uid < 1) {
      toast("Geçerli üye ID gir.", "err");
      return;
    }
    try {
      const j = await api("admin_chat.php", {
        method: "POST",
        body: JSON.stringify({ action: "mute", userId: uid, minutes }),
      });
      if (typeof j.csrfToken === "string" && j.csrfToken) adminCsrfToken = j.csrfToken;
      if (uidEl instanceof HTMLInputElement) uidEl.value = "";
      await loadAdminChat();
      renderDashboard();
      toast("Kullanıcı susturuldu.");
    } catch (e) {
      toast(e.message || "İşlem başarısız", "err");
    }
  });

  $("#audit-date-preset")?.addEventListener("change", async (e) => {
    const v = (e.target).value;
    if (v === "all" || v === "today" || v === "yesterday" || v === "custom") {
      state.auditDatePreset = v;
      if (v === "custom") ensureAuditCustomDateDefaults();
      try {
        if (v === "custom") {
          const { from, to } = getAuditLogUnixRange();
          if (from == null || to == null) {
            renderDashboard();
            return;
          }
        }
        await loadAuditLog();
      } catch (err) {
        toast(err.message || "Yüklenemedi", "err");
      }
      renderDashboard();
    }
  });

  $("#audit-date-apply")?.addEventListener("click", async () => {
    const a = ($("#audit-date-from"));
    const b = ($("#audit-date-to"));
    state.auditDateFrom = a?.value || "";
    state.auditDateTo = b?.value || "";
    state.auditDatePreset = "custom";
    const { from, to } = getAuditLogUnixRange();
    if (from == null || to == null) {
      toast("Başlangıç ve bitiş tarihini seç.", "err");
      return;
    }
    try {
      await loadAuditLog();
      renderDashboard();
      toast("Filtre uygulandı.");
    } catch (err) {
      toast(err.message || "Yüklenemedi", "err");
    }
  });

  $("#btn-purge-promo-codes")?.addEventListener("click", async () => {
    const n = (state.promoCodes || []).length;
    if (!n) {
      toast("Silinecek kod yok.", "err");
      return;
    }
    const ok = window.confirm(
      `Listedeki ${n} promo kodunun tamamı silinecek (kullanım kayıtları dahil). Emin misin?`
    );
    if (!ok) return;
    try {
      await api("admin_promo_codes.php", {
        method: "POST",
        body: JSON.stringify({ action: "purge_all" }),
      });
      toast("Tüm promo kodları silindi");
      await loadPromoCodes();
      renderDashboard();
    } catch (err) {
      toast(err.message || "Silinemedi", "err");
    }
  });

  $("#btn-refresh-promo-codes")?.addEventListener("click", async () => {
    try {
      await loadPromoCodes();
      renderDashboard();
      toast("Promo kodlar güncellendi");
    } catch (e) {
      toast(e.message || "Yüklenemedi", "err");
    }
  });

  $("#btn-refresh-telegram-invites")?.addEventListener("click", async () => {
    try {
      await loadTelegramInvites();
      renderDashboard();
      toast("Telegram davet verisi güncellendi");
    } catch (e) {
      toast(e.message || "Yüklenemedi", "err");
    }
  });

  $("#btn-refresh-telegram-broadcast")?.addEventListener("click", async () => {
    try {
      await loadTelegramBroadcast();
      renderDashboard();
      toast("Telegram yayın bilgisi güncellendi");
    } catch (e) {
      toast(e.message || "Yüklenemedi", "err");
    }
  });

  function collectLevelRowsFromDom() {
    const rows = [];
    $$("#levels-rows tr").forEach((tr, idx) => {
      const xp = Number(tr.querySelector('input[data-key="xp_required"]')?.value || 0);
      const coin = Number(tr.querySelector('input[data-key="coin_reward"]')?.value || 0);
      rows.push({
        level: idx + 1,
        xp_required: Math.max(0, Math.floor(xp)),
        coin_reward: Math.max(0, Math.floor(coin)),
      });
    });
    return rows;
  }

  $("#btn-levels-add-row")?.addEventListener("click", () => {
    const tbody = document.getElementById("levels-rows");
    if (!tbody) return;
    const idx = tbody.querySelectorAll("tr").length;
    const prevXp = Number(tbody.querySelector("tr:last-child input[data-key='xp_required']")?.value || 500);
    const prevCoin = Number(tbody.querySelector("tr:last-child input[data-key='coin_reward']")?.value || 0);
    const tr = document.createElement("tr");
    tr.className = "admin-table-row";
    tr.innerHTML = `<td class="px-3 py-2">${idx + 1}</td>
      <td class="px-3 py-2"><input data-level-row="${idx}" data-key="xp_required" type="number" min="0" class="admin-input w-full tabular-nums" value="${Math.max(0, prevXp + 500)}" /></td>
      <td class="px-3 py-2"><input data-level-row="${idx}" data-key="coin_reward" type="number" min="0" class="admin-input w-full tabular-nums" value="${Math.max(0, prevCoin + 50)}" /></td>`;
    tbody.appendChild(tr);
  });

  $("#btn-levels-refresh")?.addEventListener("click", async () => {
    try {
      await loadLevelSystem();
      renderDashboard();
      toast("Seviye sistemi verisi güncellendi");
    } catch (e) {
      toast(e.message || "Yüklenemedi", "err");
    }
  });
  $("#btn-levels-refresh-top")?.addEventListener("click", async () => {
    try {
      await loadLevelSystem();
      renderDashboard();
      toast("Seviye sistemi verisi güncellendi");
    } catch (e) {
      toast(e.message || "Yüklenemedi", "err");
    }
  });

  $("#btn-levels-save")?.addEventListener("click", async () => {
    const payload = {
      enabled: !!$("#levels-enabled")?.checked,
      xp_game_round: Number($("#levels-xp-game-round")?.value || 0),
      xp_ticket_join: Number($("#levels-xp-ticket-join")?.value || 0),
      xp_ticket_request: Number($("#levels-xp-ticket-request")?.value || 0),
      xp_site_active_daily: Number($("#levels-xp-site-active-daily")?.value || 0),
      xp_telegram_message: Number($("#levels-xp-telegram-message")?.value || 0),
      telegram_daily_cap: Number($("#levels-telegram-daily-cap")?.value || 1),
      levels: collectLevelRowsFromDom(),
    };
    if (!payload.levels.length) {
      toast("En az bir seviye satırı gerekli", "err");
      return;
    }
    try {
      const j = await api("admin_levels.php", { method: "POST", body: JSON.stringify(payload) });
      if (!j || !j.ok) throw new Error(j?.error || "save");
      await loadLevelSystem();
      renderDashboard();
      toast("Seviye ayarları kaydedildi");
    } catch (e) {
      toast(e.message || "Kaydedilemedi", "err");
    }
  });

  bindTelegramBroadcastForm();

  $("#site-event-create-form")?.addEventListener("submit", async (e) => {
    e.preventDefault();
    const form = e.currentTarget;
    const fd = new FormData(form);
    const endsRaw = String(fd.get("endsAt") || "").trim();
    let endsAt = null;
    if (endsRaw) {
      const t = Math.floor(new Date(endsRaw).getTime() / 1000);
      if (Number.isFinite(t) && t > 0) endsAt = t;
    }
    const payload = {
      title: String(fd.get("title") || "").trim(),
      description: String(fd.get("description") || "").trim(),
      sponsorId: String(fd.get("sponsorId") || "").trim(),
      logoUrl: String(fd.get("logoUrl") || "").trim(),
      siteUrl: String(fd.get("siteUrl") || "").trim(),
      prizeDescription: String(fd.get("prizeDescription") || "").trim(),
      winnerCount: Number(fd.get("winnerCount") || 1),
      ticketCost: 0,
      maxEntriesPerUser: Number(fd.get("maxEntriesPerUser") || 20),
      maxTotalTickets: Number(fd.get("maxTotalTickets") || 0),
      investmentPerTicket: Number(fd.get("investmentPerTicket") || 1000),
      endsAt,
    };
    const editingId = state.siteEventEditId ? Number(state.siteEventEditId) : 0;
    try {
      if (editingId > 0) {
        await api("admin_events.php", {
          method: "POST",
          body: JSON.stringify({ action: "update", eventId: editingId, ...payload }),
        });
        resetSiteEventForm();
        state.siteEventView = "list";
        await loadSiteEvents();
        renderDashboard();
        bindTicketCoverPreviews();
        bindEventCreateSponsorAutofill();
        toast("Etkinlik güncellendi");
      } else {
        await api("admin_events.php", {
          method: "POST",
          body: JSON.stringify({ action: "create", ...payload }),
        });
        form.reset();
        resetSiteEventForm();
        state.siteEventView = "list";
        await loadSiteEvents();
        renderDashboard();
        bindTicketCoverPreviews();
        bindEventCreateSponsorAutofill();
        toast("Etkinlik oluşturuldu");
      }
    } catch (err) {
      toast(err.message || (editingId > 0 ? "Güncellenemedi" : "Oluşturulamadı"), "err");
    }
  });

  $("#btn-site-events-refresh")?.addEventListener("click", async () => {
    try {
      await loadSiteEvents();
      if (state.siteEventView === "detail" && state.siteEventId) {
        await loadSiteEventDetail(state.siteEventId);
      }
      renderDashboard();
      toast("Güncellendi");
    } catch (e) {
      toast(e.message || "Yüklenemedi", "err");
    }
  });

  async function refreshSiteNewsPanel() {
    await loadSiteNews();
    renderDashboard();
    toast("Haberler güncellendi");
  }

  $("#btn-site-news-refresh")?.addEventListener("click", async () => {
    try {
      await refreshSiteNewsPanel();
    } catch (e) {
      toast(e.message || "Yüklenemedi", "err");
    }
  });

  $("#btn-site-news-refresh-top")?.addEventListener("click", async () => {
    try {
      await refreshSiteNewsPanel();
    } catch (e) {
      toast(e.message || "Yüklenemedi", "err");
    }
  });

  $("#btn-site-news-save")?.addEventListener("click", async () => {
    const fields = readSiteNewsFormFields();
    if (!fields.title) {
      toast("Başlık gerekli", "err");
      return;
    }
    const payload = {
      action: state.siteNewsEditId ? "update" : "create",
      title: fields.title,
      summary: fields.summary,
      body: fields.body,
      imageUrl: fields.imageUrl,
      sortOrder: fields.sortOrder,
      isPublished: fields.isPublished,
    };
    if (fields.publishedAt) payload.publishedAt = fields.publishedAt;
    if (state.siteNewsEditId) payload.id = state.siteNewsEditId;
    try {
      await api("admin_news.php", { method: "POST", body: JSON.stringify(payload) });
      toast(state.siteNewsEditId ? "Haber güncellendi" : "Haber eklendi");
      resetSiteNewsForm();
      await loadSiteNews();
      renderDashboard();
    } catch (e) {
      toast(e.message || "Kaydedilemedi", "err");
    }
  });

  $("#btn-site-news-cancel-edit")?.addEventListener("click", () => {
    resetSiteNewsForm();
    renderDashboard();
  });

  document.getElementById("tab-site-news")?.addEventListener("click", async (ev) => {
    const editBtn = ev.target.closest(".site-news-edit");
    if (editBtn) {
      const id = parseInt(editBtn.getAttribute("data-news-id") || "0", 10);
      if (!id) return;
      state.siteNewsEditId = id;
      renderDashboard();
      return;
    }
    const delBtn = ev.target.closest(".site-news-delete");
    if (delBtn) {
      const id = parseInt(delBtn.getAttribute("data-news-id") || "0", 10);
      if (!id) return;
      if (!window.confirm("Bu haber silinsin mi?")) return;
      try {
        await api("admin_news.php", { method: "POST", body: JSON.stringify({ action: "delete", id }) });
        toast("Haber silindi");
        if (state.siteNewsEditId === id) resetSiteNewsForm();
        await loadSiteNews();
        renderDashboard();
      } catch (e) {
        toast(e.message || "Silinemedi", "err");
      }
    }
  });

  async function refreshSiteNotificationsPanel() {
    await loadSiteNotifications();
    renderDashboard();
    toast("Bildirimler güncellendi");
  }

  $("#btn-site-notif-refresh")?.addEventListener("click", async () => {
    try {
      await refreshSiteNotificationsPanel();
    } catch (e) {
      toast(e.message || "Yüklenemedi", "err");
    }
  });

  $("#btn-site-notif-refresh-top")?.addEventListener("click", async () => {
    try {
      await refreshSiteNotificationsPanel();
    } catch (e) {
      toast(e.message || "Yüklenemedi", "err");
    }
  });

  $("#btn-site-notif-send")?.addEventListener("click", async () => {
    const title = String(document.getElementById("site-notif-title")?.value || "").trim();
    const body = String(document.getElementById("site-notif-body")?.value || "").trim();
    const linkUrl = String(document.getElementById("site-notif-link")?.value || "").trim();
    const isPublished = Boolean(document.getElementById("site-notif-published")?.checked);
    if (!title || !body) {
      toast("Başlık ve mesaj zorunlu", "err");
      return;
    }
    try {
      await api("admin_notifications.php", {
        method: "POST",
        body: JSON.stringify({ action: "create", title, body, linkUrl, isPublished }),
      });
      toast("Bildirim gönderildi");
      document.getElementById("site-notif-title").value = "";
      document.getElementById("site-notif-body").value = "";
      document.getElementById("site-notif-link").value = "";
      await loadSiteNotifications();
      renderDashboard();
    } catch (e) {
      toast(e.message || "Gönderilemedi", "err");
    }
  });

  document.getElementById("tab-site-notifications")?.addEventListener("click", async (ev) => {
    const delBtn = ev.target.closest(".site-notif-delete");
    if (delBtn) {
      const id = parseInt(delBtn.getAttribute("data-notif-id") || "0", 10);
      if (!id) return;
      if (!window.confirm("Bu bildirim silinsin mi?")) return;
      try {
        await api("admin_notifications.php", { method: "POST", body: JSON.stringify({ action: "delete", id }) });
        toast("Bildirim silindi");
        await loadSiteNotifications();
        renderDashboard();
      } catch (e) {
        toast(e.message || "Silinemedi", "err");
      }
      return;
    }
    const toggleBtn = ev.target.closest(".site-notif-toggle");
    if (toggleBtn) {
      const id = parseInt(toggleBtn.getAttribute("data-notif-id") || "0", 10);
      const isPublished = toggleBtn.getAttribute("data-published") === "1";
      if (!id) return;
      try {
        await api("admin_notifications.php", {
          method: "POST",
          body: JSON.stringify({ action: "toggle_publish", id, isPublished }),
        });
        await loadSiteNotifications();
        renderDashboard();
        toast(isPublished ? "Yayınlandı" : "Gizlendi");
      } catch (e) {
        toast(e.message || "Güncellenemedi", "err");
      }
    }
  });

  $("#ticket-event-create-form")?.addEventListener("submit", async (e) => {
    e.preventDefault();
    const form = e.currentTarget;
    const fd = new FormData(form);
    const endsRaw = String(fd.get("endsAt") || "").trim();
    let endsAt = null;
    if (endsRaw) {
      const t = Math.floor(new Date(endsRaw).getTime() / 1000);
      if (Number.isFinite(t) && t > 0) endsAt = t;
    }
    try {
      await api("admin_tickets.php", {
        method: "POST",
        body: JSON.stringify({
          action: "create",
          title: String(fd.get("title") || "").trim(),
          description: String(fd.get("description") || "").trim(),
          sponsorId: String(fd.get("sponsorId") || "").trim(),
          logoUrl: String(fd.get("logoUrl") || "").trim(),
          siteUrl: String(fd.get("siteUrl") || "").trim(),
          prizeDescription: String(fd.get("prizeDescription") || "").trim(),
          winnerCount: Number(fd.get("winnerCount") || 1),
          ticketCost: 0,
          maxEntriesPerUser: Number(fd.get("maxEntriesPerUser") || 20),
          maxTotalTickets: Number(fd.get("maxTotalTickets") || 0),
          investmentPerTicket: Number(fd.get("investmentPerTicket") || 1000),
          endsAt,
        }),
      });
      form.reset();
      state.ticketView = "list";
      await loadTicketEvents();
      renderDashboard();
      toast("Bilet etkinliği oluşturuldu");
    } catch (err) {
      toast(err.message || "Oluşturulamadı", "err");
    }
  });

  $("#btn-tickets-refresh")?.addEventListener("click", async () => {
    try {
      await loadTicketEvents();
      if (state.ticketView === "detail" && state.ticketEventId) {
        await loadTicketEventDetail(state.ticketEventId);
      }
      renderDashboard();
      toast("Güncellendi");
    } catch (e) {
      toast(e.message || "Yüklenemedi", "err");
    }
  });

  document.getElementById("tab-tickets")?.addEventListener("click", async (ev) => {
    const listDel = ev.target.closest(".ticket-delete-event");
    if (listDel) {
      ev.preventDefault();
      ev.stopPropagation();
      const eid = parseInt(listDel.getAttribute("data-event-id") || "0", 10);
      if (!eid) return;
      if (!confirm("Bu etkinlik siteden kaldırılır; talepler ve biletler silinir. Emin misin?")) return;
      try {
        await api("admin_tickets.php", { method: "POST", body: JSON.stringify({ action: "delete", eventId: eid }) });
        if (state.ticketEventId === eid) {
          state.ticketView = "list";
          state.ticketEventId = null;
          state.ticketDetail = null;
        }
        await loadTicketEvents();
        renderDashboard();
        bindTicketCoverPreviews();
        bindEventCreateSponsorAutofill();
        toast("Bilet etkinliği silindi");
      } catch (err) {
        toast(err.message || "Silinemedi", "err");
      }
      return;
    }

    const openEv = ev.target.closest(".ticket-open-event");
    if (openEv) {
      const eid = parseInt(openEv.getAttribute("data-event-id") || "0", 10);
      if (!eid) return;
      try {
        state.ticketView = "detail";
        state.ticketEventId = eid;
        state.ticketSubTab = "requests";
        state.ticketRequestFilter = { q: "", status: "all" };
        await loadTicketEventDetail(eid);
        renderDashboard();
        bindTicketCoverPreviews();
      } catch (err) {
        toast(err.message || "Yüklenemedi", "err");
      }
      return;
    }

    if (ev.target.closest("#ticket-back-list")) {
      state.ticketView = "list";
      state.ticketEventId = null;
      state.ticketDetail = null;
      renderDashboard();
      bindTicketCoverPreviews();
      return;
    }

    const subTab = ev.target.closest(".ticket-subtab");
    if (subTab) {
      state.ticketSubTab = subTab.getAttribute("data-subtab") || "requests";
      renderDashboard();
      return;
    }

    const filt = ev.target.closest(".ticket-req-filter");
    if (filt) {
      state.ticketRequestFilter = state.ticketRequestFilter || {};
      state.ticketRequestFilter.status = filt.getAttribute("data-status") || "all";
      renderDashboard();
      return;
    }

    if (ev.target.closest("#ticket-req-search-btn")) {
      const inp = $("#ticket-req-search");
      state.ticketRequestFilter = state.ticketRequestFilter || {};
      state.ticketRequestFilter.q = inp && "value" in inp ? String(inp.value || "").trim() : "";
      renderDashboard();
      return;
    }

    const approve = ev.target.closest(".ticket-req-approve");
    if (approve) {
      const rid = parseInt(approve.getAttribute("data-request-id") || "0", 10);
      const elig = parseInt(approve.getAttribute("data-eligible") || "1", 10);
      if (!rid) return;
      if (!confirm(`${elig} bilet verilsin mi?`)) return;
      try {
        const j = await api("admin_tickets.php", {
          method: "POST",
          body: JSON.stringify({ action: "approve_request", requestId: rid }),
        });
        await loadTicketEventDetail(state.ticketEventId);
        await loadTicketEvents();
        renderDashboard();
        toast(`Onaylandı — ${j.granted ?? 0} bilet verildi`);
      } catch (err) {
        toast(err.message || "Onaylanamadı", "err");
      }
      return;
    }

    const reject = ev.target.closest(".ticket-req-reject");
    if (reject) {
      const rid = parseInt(reject.getAttribute("data-request-id") || "0", 10);
      if (!rid) return;
      const note = window.prompt("Red notu (isteğe bağlı):", "") ?? "";
      try {
        await api("admin_tickets.php", {
          method: "POST",
          body: JSON.stringify({ action: "reject_request", requestId: rid, adminNote: note }),
        });
        await loadTicketEventDetail(state.ticketEventId);
        await loadTicketEvents();
        renderDashboard();
        toast("Talep reddedildi");
      } catch (err) {
        toast(err.message || "Reddedilemedi", "err");
      }
      return;
    }

    if (ev.target.closest("#ticket-req-csv")) {
      const reqs = filteredTicketRequests(state.ticketDetail?.requests || []);
      const header = "ID;Kullanıcı;Site;Yatırım;Yatırım Tarihi;Bilet;Durum;Not\n";
      const lines = reqs.map((r) =>
        [
          r.id,
          r.username,
          r.siteUsername,
          r.investmentAmount,
          formatTs(r.investmentAt),
          r.ticketsEligible,
          r.status,
          (r.adminNote || "").replace(/;/g, ","),
        ].join(";"),
      );
      const blob = new Blob(["\uFEFF" + header + lines.join("\n")], { type: "text/csv;charset=utf-8" });
      const a = document.createElement("a");
      a.href = URL.createObjectURL(blob);
      a.download = `bilet-talepleri-${state.ticketEventId || "export"}.csv`;
      a.click();
      URL.revokeObjectURL(a.href);
      return;
    }

    const btn = ev.target.closest(".ticket-act");
    if (!btn) return;
    const act = btn.getAttribute("data-act");
    const id = parseInt(btn.getAttribute("data-id") || "0", 10) || state.ticketEventId;
    if (!id || !act) return;
    if (act === "draw") {
      const n = parseInt(btn.getAttribute("data-winners") || "1", 10);
      if (!confirm(`${n} kazanan için rastgele çekiliş yapılsın mı? Bu işlem geri alınamaz.`)) return;
      try {
        const j = await api("admin_tickets.php", {
          method: "POST",
          body: JSON.stringify({ action: "draw", eventId: id, winnerCount: n }),
        });
        await loadTicketEventDetail(id);
        await loadTicketEvents();
        renderDashboard();
        toast(`Çekiliş tamam: ${j.winnerCount ?? 0} kazanan`);
      } catch (err) {
        toast(err.message || "Çekiliş başarısız", "err");
      }
      return;
    }
    if (act === "delete") {
      if (!confirm("Bu etkinlik siteden kaldırılır; talepler ve biletler silinir. Emin misin?")) return;
      try {
        await api("admin_tickets.php", { method: "POST", body: JSON.stringify({ action: "delete", eventId: id }) });
        state.ticketView = "list";
        state.ticketEventId = null;
        await loadTicketEvents();
        renderDashboard();
        toast("Silindi");
      } catch (err) {
        toast(err.message || "Silinemedi", "err");
      }
      return;
    }
    if (act === "save-cover") {
      const logoUrl = String($(`#ticket-cover-url-${id}`)?.value || "").trim();
      try {
        await api("admin_tickets.php", {
          method: "POST",
          body: JSON.stringify({ action: "save_cover", eventId: id, logoUrl }),
        });
        await loadTicketEvents();
        if (state.ticketEventId === id) await loadTicketEventDetail(id);
        renderDashboard();
        toast("Kart görseli kaydedildi");
      } catch (err) {
        toast(err.message || "Kaydedilemedi", "err");
      }
      return;
    }
    if (act === "open" || act === "close") {
      try {
        await api("admin_tickets.php", {
          method: "POST",
          body: JSON.stringify({ action: "set_status", eventId: id, status: act === "open" ? "open" : "closed" }),
        });
        await loadTicketEventDetail(id);
        await loadTicketEvents();
        renderDashboard();
        toast(act === "open" ? "Etkinlik açıldı" : "Etkinlik kapatıldı");
      } catch (err) {
        toast(err.message || "Güncellenemedi", "err");
      }
    }
  });

  document.getElementById("tab-site-events")?.addEventListener("click", async (ev) => {
    if (ev.target.closest("#site-event-edit-cancel")) {
      resetSiteEventForm();
      const form = document.getElementById("site-event-create-form");
      if (form) form.reset();
      renderDashboard();
      bindTicketCoverPreviews();
      bindEventCreateSponsorAutofill();
      return;
    }

    const editBtn = ev.target.closest(".site-event-edit");
    if (editBtn) {
      ev.preventDefault();
      ev.stopPropagation();
      const eid = parseInt(editBtn.getAttribute("data-event-id") || "0", 10);
      if (!eid) return;
      const found = (state.siteEvents || []).find((e) => Number(e.id) === eid);
      if (found?.status === "drawn") {
        toast("Sonuçlandırılmış etkinlik düzenlenemez", "err");
        return;
      }
      try {
        await loadSiteEvents();
      } catch {
        /* listeyi mevcut state ile doldur */
      }
      if (!getEventSponsorOptions().length && canPerm("sponsors")) {
        try {
          await loadAll();
        } catch {
          /* */
        }
      }
      state.siteEventEditId = eid;
      state.siteEventView = "list";
      renderDashboard();
      bindTicketCoverPreviews();
      bindEventCreateSponsorAutofill();
      document.getElementById("site-event-create-form")?.closest(".admin-ticket-create-panel")?.scrollIntoView({
        behavior: "smooth",
        block: "start",
      });
      return;
    }

    const listDel = ev.target.closest(".site-event-delete");
    if (listDel) {
      ev.preventDefault();
      ev.stopPropagation();
      const eid = parseInt(listDel.getAttribute("data-event-id") || "0", 10);
      if (!eid) return;
      if (!confirm("Bu etkinlik siteden kaldırılır; talepler ve biletler silinir. Emin misin?")) return;
      try {
        await api("admin_events.php", { method: "POST", body: JSON.stringify({ action: "delete", eventId: eid }) });
        if (state.siteEventId === eid) {
          state.siteEventView = "list";
          state.siteEventId = null;
          state.siteEventDetail = null;
        }
        if (Number(state.siteEventEditId) === eid) {
          resetSiteEventForm();
        }
        await loadSiteEvents();
        renderDashboard();
        bindTicketCoverPreviews();
        bindEventCreateSponsorAutofill();
        toast("Etkinlik silindi");
      } catch (err) {
        toast(err.message || "Silinemedi", "err");
      }
      return;
    }

    const openEv = ev.target.closest(".site-event-open-event");
    if (openEv) {
      const eid = parseInt(openEv.getAttribute("data-event-id") || "0", 10);
      if (!eid) return;
      try {
        state.siteEventView = "detail";
        state.siteEventId = eid;
        state.siteEventSubTab = "requests";
        state.siteEventRequestFilter = { q: "", status: "all" };
        await loadSiteEventDetail(eid);
        renderDashboard();
        bindTicketCoverPreviews();
      } catch (err) {
        toast(err.message || "Yüklenemedi", "err");
      }
      return;
    }

    if (ev.target.closest("#site-event-back-list")) {
      state.siteEventView = "list";
      state.siteEventId = null;
      state.siteEventDetail = null;
      renderDashboard();
      bindTicketCoverPreviews();
      return;
    }

    const subTab = ev.target.closest(".site-event-subtab");
    if (subTab) {
      state.siteEventSubTab = subTab.getAttribute("data-subtab") || "requests";
      renderDashboard();
      return;
    }

    const filt = ev.target.closest(".site-event-req-filter");
    if (filt) {
      state.siteEventRequestFilter = state.siteEventRequestFilter || {};
      state.siteEventRequestFilter.status = filt.getAttribute("data-status") || "all";
      renderDashboard();
      return;
    }

    if (ev.target.closest("#site-event-req-search-btn")) {
      const inp = $("#site-event-req-search");
      state.siteEventRequestFilter = state.siteEventRequestFilter || {};
      state.siteEventRequestFilter.q = inp && "value" in inp ? String(inp.value || "").trim() : "";
      renderDashboard();
      return;
    }

    const approve = ev.target.closest(".site-event-req-approve");
    if (approve) {
      const rid = parseInt(approve.getAttribute("data-request-id") || "0", 10);
      const elig = parseInt(approve.getAttribute("data-eligible") || "1", 10);
      if (!rid) return;
      if (!confirm(`${elig} katılım onaylansın mı?`)) return;
      try {
        const j = await api("admin_events.php", {
          method: "POST",
          body: JSON.stringify({ action: "approve_request", requestId: rid }),
        });
        await loadSiteEventDetail(state.siteEventId);
        await loadSiteEvents();
        renderDashboard();
        toast(`Onaylandı — ${j.granted ?? 0} katılım onaylandı`);
      } catch (err) {
        toast(err.message || "Onaylanamadı", "err");
      }
      return;
    }

    const reject = ev.target.closest(".site-event-req-reject");
    if (reject) {
      const rid = parseInt(reject.getAttribute("data-request-id") || "0", 10);
      if (!rid) return;
      const note = window.prompt("Red notu (isteğe bağlı):", "") ?? "";
      try {
        await api("admin_events.php", {
          method: "POST",
          body: JSON.stringify({ action: "reject_request", requestId: rid, adminNote: note }),
        });
        await loadSiteEventDetail(state.siteEventId);
        await loadSiteEvents();
        renderDashboard();
        toast("Talep reddedildi");
      } catch (err) {
        toast(err.message || "Reddedilemedi", "err");
      }
      return;
    }

    if (ev.target.closest("#site-event-req-csv")) {
      const reqs = filteredSiteEventRequests(state.siteEventDetail?.requests || []);
      const header = "ID;Kullanıcı;Site;Yatırım;Yatırım Tarihi;Bilet;Durum;Not\n";
      const lines = reqs.map((r) =>
        [
          r.id,
          r.username,
          r.siteUsername,
          r.investmentAmount,
          formatTs(r.investmentAt),
          r.ticketsEligible,
          r.status,
          (r.adminNote || "").replace(/;/g, ","),
        ].join(";"),
      );
      const blob = new Blob(["\uFEFF" + header + lines.join("\n")], { type: "text/csv;charset=utf-8" });
      const a = document.createElement("a");
      a.href = URL.createObjectURL(blob);
      a.download = `etkinlik-talepleri-${state.siteEventId || "export"}.csv`;
      a.click();
      URL.revokeObjectURL(a.href);
      return;
    }

    const btn = ev.target.closest(".site-event-act");
    if (!btn) return;
    const act = btn.getAttribute("data-act");
    const id = parseInt(btn.getAttribute("data-id") || "0", 10) || state.siteEventId;
    if (!id || !act) return;
    if (act === "draw") {
      const n = parseInt(btn.getAttribute("data-winners") || "1", 10);
      if (!confirm(`${n} kazanan için rastgele çekiliş yapılsın mı? Bu işlem geri alınamaz.`)) return;
      try {
        const j = await api("admin_events.php", {
          method: "POST",
          body: JSON.stringify({ action: "draw", eventId: id, winnerCount: n }),
        });
        await loadSiteEventDetail(id);
        await loadSiteEvents();
        renderDashboard();
        toast(`Çekiliş tamam: ${j.winnerCount ?? 0} kazanan`);
      } catch (err) {
        toast(err.message || "Çekiliş başarısız", "err");
      }
      return;
    }
    if (act === "delete") {
      if (!confirm("Bu etkinlik siteden kaldırılır; talepler ve biletler silinir. Emin misin?")) return;
      try {
        await api("admin_events.php", { method: "POST", body: JSON.stringify({ action: "delete", eventId: id }) });
        state.siteEventView = "list";
        state.siteEventId = null;
        await loadSiteEvents();
        renderDashboard();
        toast("Silindi");
      } catch (err) {
        toast(err.message || "Silinemedi", "err");
      }
      return;
    }
    if (act === "save-cover") {
      const logoUrl = String($(`#ticket-cover-url-${id}`)?.value || "").trim();
      try {
        await api("admin_events.php", {
          method: "POST",
          body: JSON.stringify({ action: "save_cover", eventId: id, logoUrl }),
        });
        await loadSiteEvents();
        if (state.siteEventId === id) await loadSiteEventDetail(id);
        renderDashboard();
        toast("Kart görseli kaydedildi");
      } catch (err) {
        toast(err.message || "Kaydedilemedi", "err");
      }
      return;
    }
    if (act === "open" || act === "close") {
      try {
        await api("admin_events.php", {
          method: "POST",
          body: JSON.stringify({ action: "set_status", eventId: id, status: act === "open" ? "open" : "closed" }),
        });
        await loadSiteEventDetail(id);
        await loadSiteEvents();
        renderDashboard();
        toast(act === "open" ? "Etkinlik açıldı" : "Etkinlik kapatıldı");
      } catch (err) {
        toast(err.message || "Güncellenemedi", "err");
      }
    }
  });

  $("#ticket-manual-form")?.addEventListener("submit", async (e) => {
    e.preventDefault();
    const fd = new FormData(e.currentTarget);
    const eid = state.ticketEventId;
    if (!eid) return;
    try {
      const j = await api("admin_tickets.php", {
        method: "POST",
        body: JSON.stringify({
          action: "manual_request",
          eventId: eid,
          userId: Number(fd.get("userId")),
          siteUsername: String(fd.get("siteUsername") || "").trim(),
          investmentAmount: Number(fd.get("investmentAmount")),
          ticketsEligible: Number(fd.get("ticketsEligible") || 0) || undefined,
          autoApprove: fd.get("autoApprove") === "on",
        }),
      });
      e.currentTarget.reset();
      await loadTicketEventDetail(eid);
      await loadTicketEvents();
      renderDashboard();
      toast(j.granted ? `Talep eklendi, ${j.granted} bilet verildi` : "Talep eklendi");
    } catch (err) {
      toast(err.message || "Eklenemedi", "err");
    }
  });

  $("#site-event-manual-form")?.addEventListener("submit", async (e) => {
    e.preventDefault();
    const fd = new FormData(e.currentTarget);
    const eid = state.siteEventId;
    if (!eid) return;
    try {
      const j = await api("admin_events.php", {
        method: "POST",
        body: JSON.stringify({
          action: "manual_request",
          eventId: eid,
          userId: Number(fd.get("userId")),
          siteUsername: String(fd.get("siteUsername") || "").trim(),
          investmentAmount: Number(fd.get("investmentAmount")),
          ticketsEligible: Number(fd.get("ticketsEligible") || 0) || undefined,
          autoApprove: fd.get("autoApprove") === "on",
        }),
      });
      e.currentTarget.reset();
      await loadSiteEventDetail(eid);
      await loadSiteEvents();
      renderDashboard();
      toast(j.granted ? `Talep eklendi, ${j.granted} katılım onaylandı` : "Talep eklendi");
    } catch (err) {
      toast(err.message || "Eklenemedi", "err");
    }
  });

  $("#tg-invite-settings-form")?.addEventListener("submit", async (e) => {
    e.preventDefault();
    const form = (e.currentTarget);
    const fd = new FormData(form);
    try {
      await api("admin_telegram_invites.php", {
        method: "POST",
        body: JSON.stringify({
          action: "save_settings",
          isActive: fd.get("isActive") === "on",
          groupChatId: String(fd.get("groupChatId") || "").trim(),
          coinsPerInvite: Number(fd.get("coinsPerInvite") || 50),
        }),
      });
      await loadTelegramInvites();
      renderDashboard();
      toast("Telegram davet ayarları kaydedildi");
    } catch (err) {
      toast(err.message || "Kaydedilemedi", "err");
    }
  });

  async function doSpecialOddsSyncAll() {
    const j = await api("admin_special_odds.php", {
      method: "POST",
      body: JSON.stringify({ action: "sync_all" }),
    });
    await loadSpecialOdds();
    renderDashboard();
    toast(`Senkron: ${j.checked ?? 0} maç, ${j.settled ?? 0} sonuçlandı`);
  }

  $("#btn-so-refresh")?.addEventListener("click", async () => {
    try {
      await loadSpecialOdds();
      renderDashboard();
      toast("Liste güncellendi");
    } catch (e) {
      toast(e.message || "Yüklenemedi", "err");
    }
  });
  $("#btn-so-sync-all")?.addEventListener("click", async () => {
    try {
      await doSpecialOddsSyncAll();
    } catch (e) {
      toast(e.message || "Senkron başarısız", "err");
    }
  });
  $("#btn-so-top-refresh")?.addEventListener("click", async () => {
    try {
      await loadSpecialOdds();
      renderDashboard();
      toast("Liste güncellendi");
    } catch (e) {
      toast(e.message || "Yüklenemedi", "err");
    }
  });
  $("#btn-so-top-sync-all")?.addEventListener("click", async () => {
    try {
      await doSpecialOddsSyncAll();
    } catch (e) {
      toast(e.message || "Senkron başarısız", "err");
    }
  });

  $("#btn-so-fetch-api")?.addEventListener("click", async () => {
    const dateEl = ($("#so-api-date"));
    const msg = $("#so-api-msg");
    const tbody = $("#so-api-tbody");
    const d = String(dateEl?.value || "").trim();
    if (!d || !tbody) return;
    if (msg) {
      msg.textContent = "Yükleniyor…";
    }
    try {
      const j = await api(`admin_sport_events.php?date=${encodeURIComponent(d)}`, { method: "GET" });
      const evs = Array.isArray(j.events) ? j.events : [];
      if (msg) {
        msg.textContent = evs.length ? `${evs.length} maç bulundu (${j.path || ""})` : "Bu tarihte parse edilen maç yok — API yanıtı farklı olabilir; config’de SPORT_SCHEDULE_PATH_TEMPLATE kontrol et.";
      }
      const rows = evs
        .map((e) => {
          const ts = formatTs(e.startTs);
          const payload = encodeURIComponent(
            JSON.stringify({
              id: String(e.id ?? ""),
              homeName: String(e.homeName ?? ""),
              awayName: String(e.awayName ?? ""),
              startTs: e.startTs != null ? Number(e.startTs) : null,
              logoHomeUrl: String(e.logoHomeUrl ?? ""),
              logoAwayUrl: String(e.logoAwayUrl ?? ""),
            }),
          );
          return `<tr class="border-b border-white/10">
            <td class="px-3 py-2 font-mono text-xs text-slate-400">${escapeHtml(String(e.id ?? ""))}</td>
            <td class="px-3 py-2 text-slate-200">${escapeHtml(String(e.homeName ?? ""))} — ${escapeHtml(String(e.awayName ?? ""))}</td>
            <td class="px-3 py-2 text-xs text-slate-500">${escapeHtml(ts)}</td>
            <td class="px-3 py-2"><button type="button" class="admin-btn admin-btn-primary px-3 py-1.5 text-xs admin-so-use-event" data-ev="${payload}">Seç</button></td>
          </tr>`;
        })
        .join("");
      tbody.innerHTML =
        rows || `<tr><td colspan="4" class="px-3 py-6 text-center text-slate-500">Maç yok.</td></tr>`;
    } catch (e) {
      if (msg) {
        msg.textContent = e.body?.error === "no_api_key" ? "API anahtarı yok (config.local.php)." : e.message || "İstek başarısız";
      }
      toast(e.message || "API listesi alınamadı", "err");
    }
  });

  $("#btn-so-create")?.addEventListener("click", async () => {
    const externalEventId = String($("#so-ext-id")?.value || "").trim();
    const homeName = String($("#so-home")?.value || "").trim();
    const awayName = String($("#so-away")?.value || "").trim();
    const kickoffAt = datetimeLocalToUnix(String($("#so-kickoff")?.value || ""));
    const predictionText = String($("#so-prediction")?.value || "").trim();
    const adminPick = String($("#so-admin-pick")?.value || "home").trim();
    const oddsOffered = parseFloat(String($("#so-odds-one")?.value || "0"));
    const maxParticipants = Math.max(0, parseInt(String($("#so-max-participants")?.value || "0"), 10) || 0);
    const logoHomeUrl = String($("#so-logo-h")?.value || "").trim();
    const logoAwayUrl = String($("#so-logo-a")?.value || "").trim();
    if (!externalEventId || !homeName || !awayName || !kickoffAt) {
      toast("API maç ID, ev, dep ve başlama zorunlu.", "err");
      return;
    }
    if (!["home", "draw", "away"].includes(adminPick)) {
      toast("Satır (1/X/2) seç.", "err");
      return;
    }
    if (!Number.isFinite(oddsOffered) || oddsOffered < 1.01) {
      toast("Geçerli bir oran gir (min 1.01).", "err");
      return;
    }
    try {
      await api("admin_special_odds.php", {
        method: "POST",
        body: JSON.stringify({
          action: "create",
          externalEventId,
          homeName,
          awayName,
          logoHomeUrl,
          logoAwayUrl,
          kickoffAt,
          predictionText,
          adminPick,
          oddsOffered,
          maxParticipants,
        }),
      });
      await loadSpecialOdds();
      renderDashboard();
      toast("Maç eklendi");
    } catch (e) {
      toast(e.message || "Eklenemedi", "err");
    }
  });

  document.body.addEventListener("click", async (ev) => {
    const t = ev.target;
    if (!(t instanceof Element)) return;
    const useEv = t.closest(".admin-so-use-event");
    if (useEv) {
      const raw = useEv.getAttribute("data-ev");
      if (!raw) return;
      let o;
      try {
        o = JSON.parse(decodeURIComponent(raw));
      } catch {
        return;
      }
      const ext = $("#so-ext-id");
      const home = $("#so-home");
      const away = $("#so-away");
      const lh = $("#so-logo-h");
      const la = $("#so-logo-a");
      const kick = ($("#so-kickoff"));
      if (ext) ext.value = String(o.id || "");
      if (home) home.value = String(o.homeName || "");
      if (away) away.value = String(o.awayName || "");
      if (lh) lh.value = String(o.logoHomeUrl || "");
      if (la) la.value = String(o.logoAwayUrl || "");
      if (kick && o.startTs != null && Number.isFinite(Number(o.startTs))) {
        kick.value = unixToDatetimeLocal(Number(o.startTs));
      }
      const pred = $("#so-prediction");
      if (pred) pred.value = "";
      toast("Maç forma işlendi — tahmin, oran ve satırı girip «Maç ekle».");
      return;
    }
    const sync = t.closest(".admin-btn-so-sync");
    if (sync) {
      const id = parseInt(sync.getAttribute("data-so-id") || "0", 10);
      if (!id) return;
      try {
        const j = await api("admin_special_odds.php", {
          method: "POST",
          body: JSON.stringify({ action: "sync_one", id }),
        });
        await loadSpecialOdds();
        renderDashboard();
        toast(j.settled ? "Maç sonuçlandı" : "Senkron tamam (veya maç henüz bitmedi)");
      } catch (e) {
        toast(e.message || "Senkron hatası", "err");
      }
      return;
    }
    const st = t.closest(".admin-btn-so-settle");
    if (st) {
      const id = parseInt(st.getAttribute("data-so-settle") || "0", 10);
      const result = String(st.getAttribute("data-result") || "").trim();
      if (!id || !result) return;
      if (!window.confirm("Bu maçı manuel sonuçlandırmak istiyor musun?")) return;
      try {
        await api("admin_special_odds.php", {
          method: "POST",
          body: JSON.stringify({ action: "settle_manual", id, result }),
        });
        await loadSpecialOdds();
        renderDashboard();
        toast("Sonuç girildi");
      } catch (e) {
        toast(e.message || "Hata", "err");
      }
      return;
    }
    const ca = t.closest(".admin-btn-so-cancel");
    if (ca) {
      const id = parseInt(ca.getAttribute("data-so-cancel") || "0", 10);
      if (!id) return;
      if (!window.confirm("Maçı iptal edilsin mi? Bekleyen bahisler iade edilir.")) return;
      try {
        await api("admin_special_odds.php", {
          method: "POST",
          body: JSON.stringify({ action: "cancel", id }),
        });
        await loadSpecialOdds();
        renderDashboard();
        toast("İptal edildi");
      } catch (e) {
        toast(e.message || "Hata", "err");
      }
      return;
    }
    const del = t.closest(".admin-btn-so-del");
    if (del) {
      const id = parseInt(del.getAttribute("data-so-del") || "0", 10);
      if (!id) return;
      if (!window.confirm("Maçı sil? (Bahis yoksa)")) return;
      try {
        await api("admin_special_odds.php", {
          method: "POST",
          body: JSON.stringify({ action: "delete", id }),
        });
        await loadSpecialOdds();
        renderDashboard();
        toast("Silindi");
      } catch (e) {
        const code = e.body?.error;
        toast(code === "has_bets" ? "Önce bahis var; silinemez." : e.message || "Hata", "err");
      }
    }
  });

  $("#btn-promo-create")?.addEventListener("click", async () => {
    const code = String($("#promo-new-code")?.value || "").trim();
    const coins = Math.max(1, parseInt(String($("#promo-new-coins")?.value || "0"), 10) || 0);
    const maxRaw = String($("#promo-new-max")?.value || "").trim();
    const maxUses = maxRaw === "" ? null : Math.max(1, parseInt(maxRaw, 10) || 0);
    const expVal = String($("#promo-new-expires")?.value || "").trim();
    let expiresAt = null;
    if (expVal) {
      const t = new Date(expVal).getTime();
      if (!Number.isNaN(t)) expiresAt = Math.floor(t / 1000);
    }
    try {
      await api("admin_promo_codes.php", {
        method: "POST",
        body: JSON.stringify({
          action: "create",
          code,
          coins,
          maxUses,
          expiresAt,
        }),
      });
      toast("Promo kodu oluşturuldu");
      const el = $("#promo-new-code");
      if (el) el.value = "";
      await loadPromoCodes();
      renderDashboard();
    } catch (e) {
      const code = e.body?.error;
      const msg =
        code === "duplicate"
          ? "Bu kod zaten var."
          : code === "code"
            ? "Kod 4–64 karakter olmalı."
            : code === "coins"
              ? "Geçerli coin miktarı gir."
              : e.message || "Oluşturulamadı";
      toast(msg, "err");
    }
  });

  $("#tab-panel-promo-codes")?.addEventListener("click", async (e) => {
    const tBtn = e.target.closest("[data-promo-toggle]");
    if (tBtn) {
      const id = parseInt(tBtn.getAttribute("data-promo-toggle") || "0", 10);
      const cur = tBtn.getAttribute("data-promo-active") === "1";
      try {
        await api("admin_promo_codes.php", {
          method: "POST",
          body: JSON.stringify({ action: "update", id, isActive: !cur }),
        });
        toast(cur ? "Pasifleştirildi" : "Aktifleştirildi");
        await loadPromoCodes();
        renderDashboard();
      } catch (err) {
        toast(err.message || "İşlem başarısız", "err");
      }
      return;
    }
    const dBtn = e.target.closest("[data-promo-delete]");
    if (dBtn) {
      const id = parseInt(dBtn.getAttribute("data-promo-delete") || "0", 10);
      if (!id) return;
      if (!window.confirm("Bu promo kodu kalıcı olarak silinsin mi?")) return;
      try {
        await api("admin_promo_codes.php", {
          method: "POST",
          body: JSON.stringify({ action: "delete", id }),
        });
        toast("Kod silindi");
        await loadPromoCodes();
        renderDashboard();
      } catch (err) {
        const code = err.body?.error;
        toast(err.message || "Silinemedi", "err");
      }
    }
  });

  $$("[data-dash-preset]").forEach((btn) => {
    btn.addEventListener("click", async () => {
      const key = btn.getAttribute("data-dash-preset");
      if (!key) return;
      applyDashboardPreset(key);
      try {
        await loadDashboardStats();
        renderDashboard();
      } catch (e) {
        toast(e.message || "Özet yüklenemedi", "err");
        state.dashboardStats = null;
        renderDashboard();
      }
    });
  });

  $("#dash-apply-custom")?.addEventListener("click", async () => {
    const df = $("#dash-date-from")?.value;
    const dt = $("#dash-date-to")?.value;
    const a = dateInputToUnixStart(df);
    const b = dateInputToUnixEndOfDay(dt);
    if (!a || !b || a > b) {
      toast("Geçerli başlangıç ve bitiş tarihi seç.", "err");
      return;
    }
    state.dashboardFrom = a;
    state.dashboardTo = b;
    state.dashboardPreset = "custom";
    try {
      await loadDashboardStats();
      renderDashboard();
    } catch (e) {
      toast(e.message || "Özet yüklenemedi", "err");
      state.dashboardStats = null;
      renderDashboard();
    }
  });

  $("#dash-pending-refresh")?.addEventListener("click", async () => {
    try {
      await loadAdminHeaderAlerts({ silent: true });
      renderDashboard();
      toast("Bekleyen işler güncellendi");
    } catch (e) {
      toast(e.message || "Bekleyen işler yüklenemedi", "err");
    }
  });

  $$(".btn-edit-sponsor").forEach((btn) => {
    btn.addEventListener("click", () => {
      const card = btn.closest(".sponsor-preview-card");
      const id = card?.getAttribute("data-id");
      if (!id) return;
      state.editSponsorId = id;
      renderDashboard();
    });
  });

  $$(".del-sponsor").forEach((btn) => {
    btn.addEventListener("click", () => {
      const card = btn.closest(".sponsor-preview-card");
      const id = card?.getAttribute("data-id");
      if (!id || !confirm("Bu kartı silmek istediğine emin misin?")) return;
      if (state.editSponsorId === id) state.editSponsorId = null;
      captureCurrentTab();
      state.sponsors = state.sponsors.filter((x) => x.id !== id);
      renderDashboard();
    });
  });

  $$(".btn-edit-spc").forEach((btn) => {
    btn.addEventListener("click", () => {
      const card = btn.closest("[data-spc-id]");
      const id = card?.getAttribute("data-spc-id");
      if (!id) return;
      state.editSponsorPromoId = id;
      renderDashboard();
    });
  });

  $$(".del-spc").forEach((btn) => {
    btn.addEventListener("click", () => {
      const card = btn.closest("[data-spc-id]");
      const id = card?.getAttribute("data-spc-id");
      if (!id || !confirm("Bu promosyon kodunu silmek istediğine emin misin?")) return;
      if (state.editSponsorPromoId === id) state.editSponsorPromoId = null;
      captureCurrentTab();
      state.sponsorPromoCodes = (state.sponsorPromoCodes || []).filter((x) => x.id !== id);
      renderDashboard();
    });
  });

  $$(".btn-edit-bonus").forEach((btn) => {
    btn.addEventListener("click", () => {
      const card = btn.closest("[data-bonus-id]");
      const id = card?.getAttribute("data-bonus-id");
      if (!id) return;
      state.editBonusId = id;
      renderDashboard();
    });
  });

  $$(".del-bonus").forEach((btn) => {
    btn.addEventListener("click", () => {
      const card = btn.closest("[data-bonus-id]");
      const id = card?.getAttribute("data-bonus-id");
      if (!id || !confirm("Bu bonus kaydını silmek istediğine emin misin?")) return;
      if (state.editBonusId === id) state.editBonusId = null;
      captureCurrentTab();
      state.bonuses = (state.bonuses || []).filter((x) => x.id !== id);
      renderDashboard();
    });
  });

  $$(".btn-edit-market").forEach((btn) => {
    btn.addEventListener("click", () => {
      const card = btn.closest(".market-preview-card");
      const id = card?.getAttribute("data-id");
      if (!id) return;
      state.editMarketId = id;
      renderDashboard();
    });
  });

  $$(".del-market").forEach((btn) => {
    btn.addEventListener("click", () => {
      const card = btn.closest(".market-preview-card");
      const id = card?.getAttribute("data-id");
      if (!id || !confirm("Bu ürünü silmek istediğine emin misin?")) return;
      if (state.editMarketId === id) state.editMarketId = null;
      captureCurrentTab();
      state.market = state.market.filter((x) => x.id !== id);
      renderDashboard();
    });
  });

  $$(".del-social").forEach((btn) => {
    btn.addEventListener("click", () => {
      const card = btn.closest(".social-card-edit");
      const id = card?.getAttribute("data-id");
      if (!id || !confirm("Bu bağlantıyı silmek istediğine emin misin?")) return;
      captureCurrentTab();
      state.social = state.social.filter((x) => x.id !== id);
      renderDashboard();
    });
  });

  $$(".preset-icon").forEach((btn) => {
    btn.addEventListener("click", () => {
      const k = btn.getAttribute("data-preset");
      const frag = ICON_PRESETS[k];
      if (!frag) return;
      const card = btn.closest(".social-card-edit");
      const ta = card?.querySelector(".fld-sicon");
      if (ta) ta.value = frag;
    });
  });

  $("#sponsor-modal-save")?.addEventListener("click", () => applySponsorModalToState());
  $("#sponsor-modal-cancel")?.addEventListener("click", () => closeSponsorModal());
  $("#sponsor-edit-backdrop")?.addEventListener("click", (e) => {
    cmsModalCloseFromClick(e, "data-close-modal", closeSponsorModal);
  });

  $("#market-modal-save")?.addEventListener("click", () => applyMarketModalToState());
  $("#market-modal-cancel")?.addEventListener("click", () => closeMarketModal());
  $("#spc-modal-save")?.addEventListener("click", () => applySponsorPromoModalToState());
  $("#spc-modal-cancel")?.addEventListener("click", () => closeSponsorPromoModal());
  $("#spc-edit-backdrop")?.addEventListener("click", (e) => {
    cmsModalCloseFromClick(e, "data-close-spc-modal", closeSponsorPromoModal);
  });
  $("#bonus-modal-save")?.addEventListener("click", () => applyBonusModalToState());
  $("#bonus-modal-cancel")?.addEventListener("click", () => closeBonusModal());
  $("#bonus-edit-backdrop")?.addEventListener("click", (e) => {
    cmsModalCloseFromClick(e, "data-close-bonus-modal", closeBonusModal);
  });
  $("#market-edit-backdrop")?.addEventListener("click", (e) => {
    cmsModalCloseFromClick(e, "data-close-market-modal", closeMarketModal);
  });

  $$(".btn-set-password").forEach((btn) => {
    btn.addEventListener("click", () => {
      captureCurrentTab();
      state.editSponsorId = null;
      state.editMarketId = null;
      const id = parseInt(btn.getAttribute("data-user-id") || "0", 10);
      if (!id) return;
      state.editPasswordUserId = id;
      renderDashboard();
    });
  });

  $("#member-profile-twofa-disable")?.addEventListener("click", async () => {
    const id = parseInt($("#member-profile-twofa-disable")?.getAttribute("data-user-id") || "0", 10);
    if (!id) return;
    if (!canPerm("member-twofa")) {
      toast("Yetkiniz yok.", "err");
      return;
    }
    if (!window.confirm("Bu üyenin 2FA korumasını kapatmak istediğinize emin misiniz?")) return;
    try {
      await api("admin_user_twofa.php", {
        method: "POST",
        body: JSON.stringify({ userId: id, action: "disable" }),
      });
      toast("Üye 2FA kapatıldı.");
      await loadMembers();
      const data = await api(`admin_user_detail.php?userId=${id}`);
      if (data && data.ok === true) state.memberProfileDetail = data;
      renderDashboard();
    } catch (e) {
      toast(e.message || "2FA kapatılamadı.", "err");
    }
  });

  $("#password-modal-save")?.addEventListener("click", () => void applyPasswordModal());
  $("#password-modal-cancel")?.addEventListener("click", () => closePasswordModal());
  $("#password-edit-backdrop")?.addEventListener("click", (e) => {
    cmsModalCloseFromClick(e, "data-close-password-modal", closePasswordModal);
  });

  $("#member-profile-back")?.addEventListener("click", () => closeMemberProfileModal());

  $("#member-profile-meta-save")?.addEventListener("click", async () => {
    const sid = parseInt($("#member-profile-meta-save")?.getAttribute("data-user-id") || "0", 10);
    const id = state.viewMemberProfileUserId ?? sid;
    if (!id) return;
    const note = ($("#member-profile-admin-note")?.value || "").trim();
    const tags = MEMBER_ADMIN_TAG_DEFS.map((d) => d.id).filter((tid) => {
      const cb = document.querySelector(`.member-profile-tag-cb[data-member-tag="${tid}"]`);
      return cb instanceof HTMLInputElement && cb.checked;
    });
    try {
      await api("admin_user_meta.php", {
        method: "POST",
        body: JSON.stringify({ userId: id, adminNote: note, adminTags: tags }),
      });
      toast("İç not ve etiketler kaydedildi.");
      await loadMembers();
      const data = await api(`admin_user_detail.php?userId=${id}`);
      if (data && data.ok === true) state.memberProfileDetail = data;
      renderDashboard();
    } catch (e) {
      const code = e.body?.error;
      const msg =
        code === "note_length"
          ? "İç not en fazla 4000 karakter olabilir."
          : e.message || "Kaydedilemedi.";
      toast(msg, "err");
    }
  });

  $("#member-profile-save")?.addEventListener("click", async () => {
    const sid = parseInt($("#member-profile-save")?.getAttribute("data-user-id") || "0", 10);
    const id = state.viewMemberProfileUserId ?? sid;
    if (!id) return;
    const username = ($("#member-profile-fld-username")?.value || "").trim();
    const email = ($("#member-profile-fld-email")?.value || "").trim();
    if (username.length < 2) {
      toast("Kullanıcı adı en az 2 karakter olmalı.", "err");
      return;
    }
    if (!email) {
      toast("E-posta gerekli.", "err");
      return;
    }
    try {
      await api("admin_user_profile.php", {
        method: "POST",
        body: JSON.stringify({ userId: id, username, email }),
      });
      toast("Profil bilgileri güncellendi.");
      await loadMembers();
      const data = await api(`admin_user_detail.php?userId=${id}`);
      if (data && data.ok === true) state.memberProfileDetail = data;
      renderDashboard();
    } catch (e) {
      const code = e.body?.error;
      const msg =
        code === "username"
          ? "Kullanıcı adı 2–64 karakter olmalı."
          : code === "email"
            ? "Geçerli bir e-posta girin."
            : code === "username_taken"
              ? "Bu kullanıcı adı başka üyede kayıtlı."
              : code === "email_taken"
                ? "Bu e-posta başka üyede kayıtlı."
                : e.message || "Kaydedilemedi.";
      toast(msg, "err");
    }
  });

  $("#member-detail-root")?.addEventListener("click", (e) => {
    const el = e.target;
    if (!el || typeof el.closest !== "function") return;
    const root = document.getElementById("member-detail-root");
    if (!root?.contains(el)) return;
    const quickAmt = el.closest(".member-profile-quick-amt");
    if (quickAmt && root.contains(quickAmt)) {
      e.preventDefault();
      const raw = quickAmt.getAttribute("data-amt") || "";
      const inp = root.querySelector(".member-profile-coin-root .admin-coin-amt");
      if (inp) inp.value = raw;
      return;
    }
    const copyIpBtn = el.closest("[data-copy-ip]");
    if (copyIpBtn && root.contains(copyIpBtn)) {
      e.preventDefault();
      const enc = copyIpBtn.getAttribute("data-copy-ip") || "";
      let ip = "";
      try {
        ip = decodeURIComponent(enc);
      } catch {
        ip = enc;
      }
      if (!ip.trim()) {
        toast("Kopyalanacak IP yok.", "err");
        return;
      }
      void navigator.clipboard.writeText(ip).then(
        () => toast("IP panoya kopyalandı"),
        () => toast("Kopyalanamadı (tarayıcı izni).", "err")
      );
      return;
    }
    const bjPageBtn = el.closest("[data-bj-page]");
    if (bjPageBtn && root.contains(bjPageBtn)) {
      e.preventDefault();
      const dir = bjPageBtn.getAttribute("data-bj-page");
      if (dir === "prev") state.memberProfileBlackjackPage = Math.max(0, (Number(state.memberProfileBlackjackPage) || 0) - 1);
      else if (dir === "next") state.memberProfileBlackjackPage = (Number(state.memberProfileBlackjackPage) || 0) + 1;
      renderDashboard();
      return;
    }
    const slotPageBtn = el.closest("[data-slot-page]");
    if (slotPageBtn && root.contains(slotPageBtn)) {
      e.preventDefault();
      const dir = slotPageBtn.getAttribute("data-slot-page");
      if (dir === "prev") state.memberProfileSlotPage = Math.max(0, (Number(state.memberProfileSlotPage) || 0) - 1);
      else if (dir === "next") state.memberProfileSlotPage = (Number(state.memberProfileSlotPage) || 0) + 1;
      renderDashboard();
      return;
    }
    const btn = el.closest("[data-member-tab]");
    if (!btn || !root.contains(btn)) return;
    const tab = btn.getAttribute("data-member-tab");
    if (!tab) return;
    const ok = ["overview", "profile-info", "ip", "wheel", "telegram", "crypto", "sponsor", "purchases", "promo", "tickets", "blackjack", "slot"].includes(tab);
    if (!ok || state.memberProfileActiveTab === tab) return;
    state.memberProfileActiveTab = tab;
    renderDashboard();
  });

  $("#member-wheel-allow")?.addEventListener("click", async () => {
    const id = parseInt($("#member-wheel-allow")?.getAttribute("data-user-id") || "0", 10);
    if (!id) return;
    try {
      await api("admin_user_wheel.php", { method: "POST", body: JSON.stringify({ userId: id, action: "allow" }) });
      toast("Çevirme hakkı verildi (bekleme sıfırlandı)");
      const data = await api(`admin_user_detail.php?userId=${id}`);
      state.memberProfileDetail = data;
      renderDashboard();
    } catch (e) {
      toast(e.message || "İşlem başarısız", "err");
    }
  });
  $("#member-wheel-lock")?.addEventListener("click", async () => {
    const id = parseInt($("#member-wheel-lock")?.getAttribute("data-user-id") || "0", 10);
    if (!id) return;
    try {
      await api("admin_user_wheel.php", { method: "POST", body: JSON.stringify({ userId: id, action: "lock" }) });
      toast("Üye beklemede (son çevirme şimdi sayıldı)");
      const data = await api(`admin_user_detail.php?userId=${id}`);
      state.memberProfileDetail = data;
      renderDashboard();
    } catch (e) {
      toast(e.message || "İşlem başarısız", "err");
    }
  });

  if ($("#password-edit-backdrop")) {
    requestAnimationFrame(() => $("#pw-modal-new")?.focus());
  }

  if ($("#sponsor-edit-backdrop")) {
    requestAnimationFrame(() => {
      $("#modal-fld-title")?.focus();
      bindCmsModalLogoPreview("#modal-fld-logo", "#sponsor-modal-logo-img", "#sponsor-modal-logo-preview");
    });
  }
  if ($("#market-edit-backdrop")) {
    requestAnimationFrame(() => {
      $("#market-modal-fld-title")?.focus();
      bindCmsModalLogoPreview("#market-modal-fld-logo", "#market-modal-logo-img", "#market-modal-logo-preview");
    });
  }
  if ($("#spc-edit-backdrop")) {
    requestAnimationFrame(() => $("#spc-modal-sponsor")?.focus());
  }

  $("#btn-refresh-members")?.addEventListener("click", async () => {
    try {
      await loadMembers();
      const uid = state.viewMemberProfileUserId;
      if (uid != null) {
        const data = await api(`admin_user_detail.php?userId=${uid}`);
        if (data && data.ok === true) state.memberProfileDetail = data;
        else {
          state.viewMemberProfileUserId = null;
          state.memberProfileDetail = null;
        }
      }
      toast(uid != null ? "Üye verisi yenilendi" : "Üye listesi yenilendi");
      renderDashboard();
    } catch (e) {
      toast(e.message || "Yenileme başarısız", "err");
    }
  });

  $("#btn-refresh-ip-clashes")?.addEventListener("click", async () => {
    try {
      await loadMembers();
      renderDashboard();
      toast("IP çakışmaları güncellendi");
    } catch (e) {
      toast(e.message || "Yenileme başarısız", "err");
    }
  });

  $("#btn-refresh-market-requests")?.addEventListener("click", async () => {
    try {
      await loadMarketRequests();
      toast("Market talepleri yenilendi");
      renderDashboard();
    } catch (e) {
      toast(e.message || "Yenileme başarısız", "err");
    }
  });

  $("#btn-refresh-complaints")?.addEventListener("click", async () => {
    try {
      await loadComplaints();
      renderDashboard();
      toast("Şikayetler yenilendi");
    } catch (e) {
      toast(e.message || "Yenileme başarısız", "err");
    }
  });

  $("#btn-refresh-affiliate-requests")?.addEventListener("click", async () => {
    try {
      await loadAffiliateRequests();
      renderDashboard();
      toast("Affiliate talepleri yenilendi");
    } catch (e) {
      toast(e.message || "Yenileme başarısız", "err");
    }
  });

  $("#market-requests-search")?.addEventListener("input", (e) => {
    const t = (e.target);
    state.marketRequestsQuery = t.value || "";
    renderDashboard();
    requestAnimationFrame(() => {
      const el = ($("#market-requests-search"));
      if (el) {
        el.focus();
        const len = el.value.length;
        el.setSelectionRange(len, len);
      }
    });
  });

  $("#market-requests-search-clear")?.addEventListener("click", () => {
    state.marketRequestsQuery = "";
    renderDashboard();
    requestAnimationFrame(() => $("#market-requests-search")?.focus());
  });

  $("#complaints-search")?.addEventListener("input", (e) => {
    const t = (e.target);
    state.complaintsQuery = t.value || "";
    renderDashboard();
    requestAnimationFrame(() => {
      const el = ($("#complaints-search"));
      if (el) {
        el.focus();
        const len = el.value.length;
        el.setSelectionRange(len, len);
      }
    });
  });

  $("#complaints-search-clear")?.addEventListener("click", () => {
    state.complaintsQuery = "";
    renderDashboard();
    requestAnimationFrame(() => $("#complaints-search")?.focus());
  });

  $("#affiliate-requests-search")?.addEventListener("input", (e) => {
    const t = (e.target);
    state.affiliateRequestsQuery = t.value || "";
    renderDashboard();
    requestAnimationFrame(() => {
      const el = ($("#affiliate-requests-search"));
      if (el) {
        el.focus();
        const len = el.value.length;
        el.setSelectionRange(len, len);
      }
    });
  });

  $("#affiliate-requests-search-clear")?.addEventListener("click", () => {
    state.affiliateRequestsQuery = "";
    renderDashboard();
    requestAnimationFrame(() => $("#affiliate-requests-search")?.focus());
  });

  $$(".btn-complaint-status").forEach((btn) => {
    btn.addEventListener("click", async () => {
      const complaintId = parseInt(btn.getAttribute("data-complaint-id") || "0", 10);
      const status = String(btn.getAttribute("data-status") || "");
      if (!complaintId || !["approved", "rejected", "pending"].includes(status)) return;
      try {
        await api("admin_complaint_status.php", {
          method: "POST",
          body: JSON.stringify({ complaintId, status }),
        });
        await loadComplaints();
        renderDashboard();
        toast(status === "approved" ? "Şikayet onaylandı" : status === "rejected" ? "Şikayet reddedildi" : "Durum güncellendi");
      } catch (e) {
        toast(e.message || "Durum güncellenemedi", "err");
      }
    });
  });

  $$(".btn-affiliate-status").forEach((btn) => {
    btn.addEventListener("click", async () => {
      const requestId = parseInt(btn.getAttribute("data-request-id") || "0", 10);
      const status = String(btn.getAttribute("data-status") || "");
      if (!requestId || !["approved", "rejected", "pending"].includes(status)) return;
      try {
        await api("admin_affiliate_request_status.php", {
          method: "POST",
          body: JSON.stringify({ requestId, status }),
        });
        await loadAffiliateRequests();
        renderDashboard();
        toast(status === "approved" ? "Talep onaylandı" : status === "rejected" ? "Talep reddedildi" : "Durum güncellendi");
      } catch (e) {
        toast(e.message || "Durum güncellenemedi", "err");
      }
    });
  });

  $$(".btn-purchase-status").forEach((btn) => {
    btn.addEventListener("click", async () => {
      const purchaseId = parseInt(btn.getAttribute("data-purchase-id") || "0", 10);
      const status = String(btn.getAttribute("data-status") || "");
      if (!purchaseId || !["approved", "rejected", "pending"].includes(status)) return;
      try {
        await api("admin_purchase_status.php", {
          method: "POST",
          body: JSON.stringify({ purchaseId, status }),
        });
        await loadMarketRequests();
        renderDashboard();
        toast(status === "approved" ? "Satın alma onaylandı" : status === "rejected" ? "Satın alma reddedildi" : "Durum güncellendi");
      } catch (e) {
        toast(e.message || "Durum güncellenemedi", "err");
      }
    });
  });

  $("#btn-export-market-requests-csv")?.addEventListener("click", () => {
    const rows = state.marketRequests || [];
    if (!rows.length) {
      toast("Dışa aktarılacak kayıt yok.", "err");
      return;
    }
    downloadMarketRequestsCsv(rows);
  });

  $$(".admin-coin-add").forEach((btn) => {
    btn.addEventListener("click", async () => {
      const id = parseInt(btn.getAttribute("data-user-id") || "0", 10);
      const rootCoin = btn.closest(".member-profile-coin-root");
      const wrap = rootCoin || btn.closest("div");
      const input = rootCoin?.querySelector(".admin-coin-amt") ?? wrap?.querySelector(".admin-coin-amt");
      const amt = Math.max(1, parseInt(String(input?.value || "0"), 10) || 0);
      if (!id || !amt) {
        toast("Geçerli miktar gir (min. 1).", "err");
        return;
      }
      try {
        await api("admin_user_coins.php", {
          method: "POST",
          body: JSON.stringify({ userId: id, delta: amt }),
        });
        toast(`+${amt} coin eklendi`);
        await loadMembers();
        if (state.viewMemberProfileUserId === id) {
          try {
            const data = await api(`admin_user_detail.php?userId=${id}`);
            if (data && data.ok === true) state.memberProfileDetail = data;
          } catch {
          }
        }
        renderDashboard();
      } catch (e) {
        toast(e.message || "İşlem başarısız", "err");
      }
    });
  });

  $("#members-search")?.addEventListener("input", (e) => {
    const t = e.target;
    state.membersFilter = t.value || "";
    resetMembersListPage();
    if (isMembersExternalSource()) {
      clearTimeout(membersSearchDebounce);
      membersSearchDebounce = setTimeout(() => {
        reloadMembers();
      }, 450);
      renderMembersTabPartial({ focusSearch: true });
      return;
    }
    clearTimeout(membersSearchDebounce);
    membersSearchDebounce = setTimeout(() => {
      renderMembersTabPartial({ focusSearch: true });
    }, 180);
  });

  $("#members-search-clear")?.addEventListener("click", () => {
    state.membersFilter = "";
    resetMembersListPage();
    if (isMembersExternalSource()) {
      reloadMembers();
      return;
    }
    renderMembersTabPartial({ focusSearch: true });
  });

  $("#members-filter-min")?.addEventListener("input", (e) => {
    state.membersFilterMinCoins = (e.target).value;
    resetMembersListPage();
    renderMembersTabPartial({ focusInputId: "members-filter-min" });
  });

  $("#members-filter-max")?.addEventListener("input", (e) => {
    state.membersFilterMaxCoins = (e.target).value;
    resetMembersListPage();
    renderMembersTabPartial({ focusInputId: "members-filter-max" });
  });

  $("#members-filter-shop")?.addEventListener("change", (e) => {
    const v = (e.target).value;
    if (v === "all" || v === "yes" || v === "no") state.membersFilterShop = v;
    resetMembersListPage();
    renderMembersTabPartial();
  });

  $("#members-filter-multi-ip")?.addEventListener("change", (e) => {
    const v = (e.target).value;
    if (v === "all" || v === "yes" || v === "no") state.membersFilterMultiIp = v;
    resetMembersListPage();
    renderMembersTabPartial();
  });

  $("#members-reload-btn")?.addEventListener("click", () => {
    reloadMembers({ resume: true });
  });

  $("#members-fresh-btn")?.addEventListener("click", () => {
    reloadMembers({ fresh: true });
  });

  $("#members-resume-btn")?.addEventListener("click", () => {
    reloadMembers({ resume: true });
  });

  $("#members-resume-btn-inline")?.addEventListener("click", () => {
    reloadMembers({ resume: true });
  });

  $("#members-filter-reset")?.addEventListener("click", () => {
    state.membersFilter = "";
    state.membersFilterMinCoins = "";
    state.membersFilterMaxCoins = "";
    state.membersFilterShop = "all";
    state.membersFilterMultiIp = "all";
    state.membersFilterTags = { suspicious: false, vip: false, banned: false };
    resetMembersListPage();
    if (isMembersExternalSource()) {
      reloadMembers();
      return;
    }
    renderMembersTabPartial({ focusSearch: true });
  });

  $("#members-filter-reset-inline")?.addEventListener("click", () => {
    state.membersFilterShop = "all";
    state.membersFilterMultiIp = "all";
    state.membersFilterTags = { suspicious: false, vip: false, banned: false };
    resetMembersListPage();
    renderMembersTabPartial();
  });

  $$("[data-members-filter-tag]").forEach((el) => {
    el.addEventListener("change", (e) => {
      const t = (e.target);
      const key = t.getAttribute("data-members-filter-tag");
      if (!key) return;
      if (!state.membersFilterTags) state.membersFilterTags = { suspicious: false, vip: false, banned: false };
      if (key in state.membersFilterTags) state.membersFilterTags[key] = t.checked;
      resetMembersListPage();
      renderMembersTabPartial();
    });
  });

  $("#ip-clash-search")?.addEventListener("input", (e) => {
    const t = (e.target);
    state.ipClashQuery = t.value || "";
    renderDashboard();
    requestAnimationFrame(() => {
      const el = ($("#ip-clash-search"));
      if (el) {
        el.focus();
        const len = el.value.length;
        el.setSelectionRange(len, len);
      }
    });
  });

  $("#ip-clash-search-clear")?.addEventListener("click", () => {
    state.ipClashQuery = "";
    renderDashboard();
    requestAnimationFrame(() => $("#ip-clash-search")?.focus());
  });

  $$(".btn-toggle-user-active").forEach((btn) => {
    btn.addEventListener("click", async () => {
      const userId = parseInt(btn.getAttribute("data-user-id") || "0", 10);
      const nextActive = (btn.getAttribute("data-next-active") || "1") === "1";
      if (!userId) return;
      try {
        await api("admin_user_status.php", {
          method: "POST",
          body: JSON.stringify({ userId, active: nextActive }),
        });
        await loadMembers();
        renderDashboard();
        toast(nextActive ? "Kullanıcı aktifleştirildi" : "Kullanıcı pasife çekildi");
      } catch (e) {
        const code = e.body?.error;
        const msg =
          code === "not_found"
            ? "Kullanıcı bulunamadı."
            : code === "unauthorized"
              ? "Yetki hatası."
              : e.message || "Durum güncellenemedi";
        toast(msg, "err");
      }
    });
  });

  $$(".btn-member-delete").forEach((btn) => {
    btn.addEventListener("click", async () => {
      const userId = parseInt(btn.getAttribute("data-user-id") || "0", 10);
      const uname = btn.getAttribute("data-username") || String(userId);
      if (!userId) return;
      const ok = window.confirm(
        `Bu üyeliği kalıcı olarak silmek istediğinize emin misiniz?\n\n${uname}\n\nİlişkili satın alma ve çark kayıtları da silinir.`
      );
      if (!ok) return;
      try {
        await api("admin_user_delete.php", { method: "POST", body: JSON.stringify({ userId }) });
        toast("Üyelik silindi");
        await loadMembers();
        renderDashboard();
      } catch (e) {
        const code = e.body?.error;
        const msg =
          code === "not_found"
            ? "Kullanıcı bulunamadı."
            : e.message || "Silinemedi";
        toast(msg, "err");
      }
    });
  });

  $("#btn-export-members-filtered")?.addEventListener("click", () => {
    const rows = getFilteredMembers();
    if (!rows.length) {
      toast("Filtreye uyan üye yok.", "err");
      return;
    }
    downloadMembersCsv(rows, "filtreli");
  });

  $("#btn-export-members-all")?.addEventListener("click", () => {
    const rows = state.members || [];
    if (!rows.length) {
      toast("Kayıtlı üye yok.", "err");
      return;
    }
    downloadMembersCsv(rows, "tum");
  });

  $$(".admin-coin-sub").forEach((btn) => {
    btn.addEventListener("click", async () => {
      const id = parseInt(btn.getAttribute("data-user-id") || "0", 10);
      const rootCoin = btn.closest(".member-profile-coin-root");
      const wrap = rootCoin || btn.closest("div");
      const input = rootCoin?.querySelector(".admin-coin-amt") ?? wrap?.querySelector(".admin-coin-amt");
      const amt = Math.max(1, parseInt(String(input?.value || "0"), 10) || 0);
      if (!id || !amt) {
        toast("Geçerli miktar gir (min. 1).", "err");
        return;
      }
      try {
        await api("admin_user_coins.php", {
          method: "POST",
          body: JSON.stringify({ userId: id, delta: -amt }),
        });
        toast(`−${amt} coin düşüldü`);
        await loadMembers();
        if (state.viewMemberProfileUserId === id) {
          try {
            const data = await api(`admin_user_detail.php?userId=${id}`);
            if (data && data.ok === true) state.memberProfileDetail = data;
          } catch {
          }
        }
        renderDashboard();
      } catch (e) {
        toast(e.message || "İşlem başarısız", "err");
      }
    });
  });

  let maintenanceToggleTimer = null;
  $("#site-maintenance-enabled")?.addEventListener("change", () => {
    if (state.tab !== "maintenance") return;
    clearTimeout(maintenanceToggleTimer);
    maintenanceToggleTimer = setTimeout(async () => {
      try {
        readSiteFromDomIfVisible();
        const data = normalizeSiteForAdmin(state.site || {});
        await saveType("site", data);
        state.site = data;
        toast(
          data.maintenance?.enabled
            ? "Bakım modu açıldı — ziyaretçiler bakım ekranını görür"
            : "Bakım modu kapatıldı — site normal açılır"
        );
        renderDashboard();
      } catch (e) {
        toast(e.message || "Kayıt başarısız", "err");
      }
    }, 350);
  });

  $("#wheel-disable-btn")?.addEventListener("click", async () => {
    try {
      await api("admin_wheel.php", { method: "POST", body: JSON.stringify({ enabled: false }) });
      await loadWheelConfig();
      toast("Çark pasife alındı");
      renderDashboard();
    } catch (e) {
      toast(e.message || "İşlem başarısız", "err");
    }
  });

  $("#wheel-enable-btn")?.addEventListener("click", async () => {
    try {
      await api("admin_wheel.php", { method: "POST", body: JSON.stringify({ enabled: true }) });
      await loadWheelConfig();
      toast("Çark aktifleştirildi");
      renderDashboard();
    } catch (e) {
      toast(e.message || "İşlem başarısız", "err");
    }
  });

  $("#btn-logout")?.addEventListener("click", async () => {
    try {
      await api("logout.php", { method: "POST", body: JSON.stringify({}) });
    } catch {
      /* sunucu yanıt vermese de yerel oturumu kapat */
    }
    resetAdminAuthState();
    adminCsrfToken = null;
    toast("Çıkış yapıldı");
    renderLogin();
  });

  $("#btn-refresh-staff")?.addEventListener("click", async () => {
    try {
      await loadStaff();
      renderDashboard();
      toast("Liste güncellendi");
    } catch (e) {
      toast(e.message || "Yenilenemedi", "err");
    }
  });

  $("#staff-new-super")?.addEventListener("change", (e) => {
    const sup = (e.target).checked;
    const w = $("#staff-new-perm-wrap");
    if (w) w.classList.toggle("hidden", sup);
  });

  $("#staff-new-form")?.addEventListener("submit", async (e) => {
    e.preventDefault();
    const u = $("#staff-new-user")?.value.trim() || "";
    const pw = $("#staff-new-pass")?.value || "";
    const dn = $("#staff-new-display")?.value.trim() || "";
    const isSuper = $("#staff-new-super")?.checked || false;
    const perms = isSuper
      ? []
      : $$(".staff-new-perm")
        .filter((cb) => (cb).checked)
        .map((cb) => (cb).value);
    if (!isSuper && perms.length === 0) {
      toast("Tam yetkili değilse en az bir izin seç.", "err");
      return;
    }
    try {
      await api("admin_staff.php", {
        method: "POST",
        body: JSON.stringify({ username: u, password: pw, displayName: dn, isSuper, permissions: perms }),
      });
      toast("Personel oluşturuldu");
      await loadStaff();
      const w = $("#staff-new-perm-wrap");
      if (w) w.classList.remove("hidden");
      renderDashboard();
    } catch (err) {
      const code = err.body?.error;
      const msg =
        code === "duplicate"
          ? "Bu kullanıcı adı zaten var."
          : code === "username"
            ? "Kullanıcı adı 2–32 karakter; harf, rakam, _"
            : code === "password"
              ? "Şifre en az 8 karakter."
              : code === "permissions"
                ? "En az bir izin seç veya tam yetkili işaretle."
                : err.message || "Kayıt başarısız";
      toast(msg, "err");
    }
  });

  $$(".btn-staff-edit").forEach((btn) => {
    btn.addEventListener("click", () => {
      const id = parseInt(btn.getAttribute("data-id") || "0", 10);
      const op = (state.operators || []).find((x) => x.id === id);
      if (!op) return;
      state.staffEditOp = op;
      renderDashboard();
    });
  });

  $$(".btn-staff-delete").forEach((btn) => {
    btn.addEventListener("click", async () => {
      const id = parseInt(btn.getAttribute("data-id") || "0", 10);
      if (!id) return;
      if (!confirm("Bu personel hesabını silmek istediğine emin misin?")) return;
      try {
        await api("admin_staff.php", {
          method: "DELETE",
          body: JSON.stringify({ id }),
        });
        toast("Silindi");
        await loadStaff();
        renderDashboard();
      } catch (err) {
        const code = err.body?.error;
        const msg =
          code === "self_delete"
            ? "Kendi hesabını silemezsin."
            : err.message || "Silinemedi";
        toast(msg, "err");
      }
    });
  });

  $("#staff-edit-form")?.addEventListener("submit", async (e) => {
    e.preventDefault();
    const op = state.staffEditOp;
    if (!op) return;
    const id = op.id;
    const username = $("#staff-edit-user")?.value.trim() || "";
    const displayName = $("#staff-edit-display")?.value.trim() || "";
    const pw = $("#staff-edit-pass")?.value || "";
    const isSuper = $("#staff-edit-super")?.checked || false;
    const twofaEnabled = $("#staff-edit-twofa-enabled")?.checked || false;
    const perms = isSuper
      ? []
      : $$(".staff-edit-perm")
        .filter((cb) => (cb).checked)
        .map((cb) => (cb).value);
    if (!isSuper && perms.length === 0) {
      toast("Tam yetkili değilse en az bir izin seç.", "err");
      return;
    }
    try {
      const payload = { id, username, displayName, isSuper, permissions: perms, twofaEnabled };
      if (pw !== "") Object.assign(payload, { password: pw });
      const res = await api("admin_staff.php", { method: "PUT", body: JSON.stringify(payload) });
      if (res?.autoSetup?.secret) {
        const backupCodes = Array.isArray(res.autoSetup.backupCodes) ? res.autoSetup.backupCodes : [];
        const qrUrl = String(res.autoSetup.qrUrl || "").trim();
        const secretText = String(res.autoSetup.secret || "").trim();
        const setupMsg =
          `Bu personel için 2FA otomatik kuruldu.\n\n` +
          `Secret: ${secretText}\n` +
          `${qrUrl ? `QR: ${qrUrl}\n` : ""}\n` +
          `Yedek Kodlar:\n${backupCodes.join("\n")}\n\n` +
          `Bu bilgileri personele güvenli şekilde ilet.`;
        alert(setupMsg);
      }
      toast("Güncellendi");
      state.staffEditOp = null;
      await loadStaff();
      renderDashboard();
    } catch (err) {
      const code = err.body?.error;
      const msg =
        code === "duplicate"
          ? "Bu kullanıcı adı zaten var."
          : code === "password"
            ? "Şifre en az 8 karakter."
            : code === "twofa_not_setup"
              ? "Bu hesapta 2FA kurulumu yok. Önce kullanıcı kendi profilinden 2FA kurmalı."
            : err.message || "Kayıt başarısız";
      toast(msg, "err");
    }
  });

  $("#staff-edit-close")?.addEventListener("click", () => {
    state.staffEditOp = null;
    renderDashboard();
  });
  $("#staff-edit-cancel")?.addEventListener("click", () => {
    state.staffEditOp = null;
    renderDashboard();
  });
  $("#staff-edit-twofa-reset")?.addEventListener("click", async () => {
    const op = state.staffEditOp;
    if (!op) return;
    const ok = confirm("Bu hesap için 2FA tamamen sıfırlansın mı? Kullanıcı yeniden kurulum yapmak zorunda kalacak.");
    if (!ok) return;
    try {
      await api("admin_staff.php", {
        method: "PUT",
        body: JSON.stringify({ id: op.id, twofaReset: true }),
      });
      toast("2FA sıfırlandı. Kullanıcı tekrar kurulum yapmalı.", "ok");
      state.staffEditOp = null;
      await loadStaff();
      renderDashboard();
    } catch (err) {
      toast(err?.message || "2FA sıfırlanamadı", "err");
    }
  });

  $("#staff-edit-super")?.addEventListener("change", (e) => {
    const sup = (e.target).checked;
    const w = $("#staff-edit-perm-wrap");
    if (w) w.classList.toggle("hidden", sup);
  });

  bindLegalPagesPanel();
}

function readSiteFromDomIfVisible() {
  const prev = normalizeSiteForAdmin(state.site || {});
  if (!$("#site-tr-telegram") && !$("#site-maintenance-enabled") && !$("#site-brand-logo-url")) return;
  const cssEl = $("#site-custom-css");
  const customCss = cssEl
    ? String(cssEl.value || "")
    : typeof prev.customCss === "string"
      ? prev.customCss
      : "";
  const dl = $("#site-default-locale")?.value === "en" ? "en" : "tr";
  const readLoc = (pre) => ({
    telegramUrl: $(`#site-${pre}-telegram`)?.value.trim() ?? "",
    contactIntro: $(`#site-${pre}-intro`)?.value.trim() ?? "",
    contactCardTitle: $(`#site-${pre}-ctitle`)?.value.trim() ?? "",
    contactCardSubtitle: $(`#site-${pre}-csub`)?.value.trim() ?? "",
    contactButton: $(`#site-${pre}-cbtn`)?.value.trim() ?? "",
    contactEyebrow: $(`#site-${pre}-eyebrow`)?.value.trim() ?? "",
    contactHeading: $(`#site-${pre}-heading`)?.value.trim() ?? "",
  });
  const readMaintLoc = (pre) => ({
    eyebrow: $(`#site-maint-${pre}-eyebrow`)?.value.trim() ?? "",
    title: $(`#site-maint-${pre}-title`)?.value.trim() ?? "",
    message: $(`#site-maint-${pre}-message`)?.value.trim() ?? "",
  });
  let next = { ...prev, customCss };
  if ($("#site-brand-logo-url")) {
    next.logoUrl = $("#site-brand-logo-url")?.value.trim() ?? "";
    next.faviconUrl = $("#site-brand-favicon-url")?.value.trim() ?? "";
    next.ogImageUrl = $("#site-brand-og-url")?.value.trim() ?? "";
    next.defaultLocale = dl;
    next.locales = { tr: readLoc("tr"), en: readLoc("en") };
  }
  if ($("#site-youtube-channel")) {
    const max = parseInt($("#site-youtube-max")?.value || "4", 10) || 4;
    next.youtube = parseYoutubeAdminInput($("#site-youtube-channel")?.value || "", max);
  }
  if ($("#site-maintenance-enabled")) {
    next.maintenance = {
      enabled: Boolean($("#site-maintenance-enabled")?.checked),
      imageUrl: $("#site-maintenance-image-url")?.value.trim() ?? "",
      locales: { tr: readMaintLoc("tr"), en: readMaintLoc("en") },
    };
  }
  state.site = next;
}

function gamdomLogoFieldHtml(logoUrl) {
  const coverUrl = logoUrl && String(logoUrl).trim() ? String(logoUrl).trim() : "";
  const previewSrc = coverUrl || "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
  const hasCover = !!coverUrl;
  const emptyInner = hasCover
    ? ""
    : `<span class="admin-ticket-cover__empty" aria-hidden="true">Logo yok</span>`;
  return `
    <div class="admin-ticket-cover admin-ticket-cover--gamdom" data-cover-id="gamdom-logo">
      <div class="admin-ticket-cover__preview-col">
        <div class="admin-ticket-cover__preview admin-ticket-cover__preview--logo${hasCover ? "" : " admin-ticket-cover__preview--empty"}">
          <img id="gamdom-logo-preview" src="${escapeHtml(previewSrc)}" alt="" class="admin-ticket-cover__img" loading="lazy" decoding="async" />
          ${emptyInner}
        </div>
      </div>
      <div class="admin-ticket-cover__fields">
        <p class="admin-ticket-cover__title">Turnuva logosu</p>
        <p class="admin-ticket-cover__hint">Sayfanın üstündeki &quot;Gamdom&quot; yazısı yerine bu logo gösterilir. PNG veya WebP önerilir (şeffaf arka plan).</p>
        <label class="admin-ticket-cover__url-label">
          <span class="admin-label">Görsel adresi</span>
          <input type="url" id="gamdom-logo-url" class="admin-input ticket-cover-url-input font-mono text-xs" data-preview="gamdom-logo-preview" value="${escapeHtml(coverUrl)}" placeholder="https://… veya dosya yükleyin" autocomplete="off" />
        </label>
        <div class="mt-2 flex flex-wrap items-center gap-2">
          <label class="admin-btn admin-btn-soft cursor-pointer">
            Dosya seç
            <input type="file" id="gamdom-logo-file" class="sr-only" accept="image/png,image/jpeg,image/webp,image/gif,image/svg+xml" />
          </label>
          <button type="button" class="admin-btn admin-btn-secondary btn-gamdom-logo-upload">Logoyu yükle</button>
        </div>
      </div>
    </div>`;
}

function gamdomTurnuvaPanelHtml(gt) {
  const cfg = gt && typeof gt === "object" ? gt : {};
  const api = cfg.api && typeof cfg.api === "object" ? cfg.api : {};
  const periods = Array.isArray(cfg.periods) && cfg.periods.length ? cfg.periods : [{ id: "default", label: "Güncel dönem" }];
  const activeId = String(cfg.activePeriodId || periods[0]?.id || "default");
  const periodsData = cfg.periodsData && typeof cfg.periodsData === "object" ? cfg.periodsData : {};
  const bucket = periodsData[activeId] && typeof periodsData[activeId] === "object" ? periodsData[activeId] : { entries: [] };
  const entriesJson = JSON.stringify(Array.isArray(bucket.entries) ? bucket.entries : [], null, 2);
  const periodsLines = periods.map((p) => `${p.id}|${p.label || p.id}`).join("\n");
  return `
    ${cmsPanelToolbarHtml(`<span class="admin-data-count text-sm text-slate-500">Gamdom çevrim turnuvası · sidebar menü</span>`)}
    <div class="admin-panel admin-panel--padded space-y-4">
      <label class="flex cursor-pointer items-start gap-3">
        <input type="checkbox" id="gamdom-enabled" class="admin-checkbox" ${cfg.enabled ? "checked" : ""} />
        <span class="text-sm text-slate-600">Turnuva aktif (kapalıyken sidebar menüsü görünmez)</span>
      </label>
      <div class="grid gap-4 sm:grid-cols-2">
        <div>
          <label class="admin-label" for="gamdom-title">Başlık</label>
          <input id="gamdom-title" class="admin-input" value="${escapeHtml(cfg.title || "Gamdom Çevrim Etkinliği")}" />
        </div>
        <div>
          <label class="admin-label" for="gamdom-prize">Ödül havuzu metni</label>
          <input id="gamdom-prize" class="admin-input" value="${escapeHtml(cfg.prizePool || "")}" placeholder="500,000.00₺" />
        </div>
      </div>
      ${gamdomLogoFieldHtml(cfg.logoUrl || "")}
      <div class="grid gap-4 sm:grid-cols-2">
        <div>
          <label class="admin-label" for="gamdom-cta-label">Katılım butonu metni</label>
          <input id="gamdom-cta-label" class="admin-input" value="${escapeHtml(cfg.ctaLabel || "")}" />
        </div>
        <div>
          <label class="admin-label" for="gamdom-cta-url">Katılım linki</label>
          <input id="gamdom-cta-url" class="admin-input" value="${escapeHtml(cfg.ctaUrl || "")}" placeholder="https://..." />
        </div>
      </div>
      <div>
        <label class="admin-label" for="gamdom-rules-title">Kurallar popup başlığı</label>
        <input id="gamdom-rules-title" class="admin-input" value="${escapeHtml(cfg.rulesTitle || "Kurallar ve Şartlar")}" />
      </div>
      <div>
        <label class="admin-label" for="gamdom-rules-text">Kurallar ve şartlar (popup içeriği)</label>
        <p class="mb-1 text-xs text-slate-500">Her satır yeni paragraf olarak gösterilir. Siteye kaydettikten sonra turnuva sayfasında &quot;Kurallar ve Şartlar&quot; butonu popup açar.</p>
        <textarea id="gamdom-rules-text" class="admin-input min-h-[12rem] text-sm leading-relaxed" rows="14" placeholder="Turnuva kurallarını buraya yazın…">${escapeHtml(cfg.rulesText || "")}</textarea>
      </div>
      <div>
        <label class="admin-label" for="gamdom-rules-url">Kurallar linki (isteğe bağlı yedek)</label>
        <input id="gamdom-rules-url" class="admin-input" value="${escapeHtml(cfg.rulesUrl || "")}" placeholder="https://… — metin boşsa popup’ta link gösterilir" />
      </div>
      <div>
        <label class="admin-label" for="gamdom-end">Bitiş (geri sayım)</label>
        <input id="gamdom-end" type="datetime-local" class="admin-input" value="${escapeHtml(unixToDatetimeLocal(cfg.endAt))}" />
      </div>
      <div class="admin-panel admin-panel--padded space-y-3 border border-slate-200/80 bg-slate-50/50">
        <p class="text-sm font-semibold text-slate-700">Dönemler (ay sekmeleri)</p>
        <p class="text-xs text-slate-500">Her satır: <code class="admin-code">id|Etiket</code> — örn. <code class="admin-code">2026-06|Haziran 2026</code></p>
        <textarea id="gamdom-periods-lines" class="admin-input min-h-[5rem] font-mono text-xs" rows="4">${escapeHtml(periodsLines)}</textarea>
        <div>
          <label class="admin-label" for="gamdom-active-period">Düzenlenen / varsayılan dönem ID</label>
          <input id="gamdom-active-period" class="admin-input" value="${escapeHtml(activeId)}" />
        </div>
      </div>
      <div>
        <label class="admin-label" for="gamdom-entries-json">Sıralama (seçili dönem, en fazla 50)</label>
        <p class="mb-1 text-xs text-slate-500">JSON dizi: <code class="admin-code">username</code>, <code class="admin-code">wager</code>, <code class="admin-code">wagerDisplay</code>, <code class="admin-code">reward</code>, <code class="admin-code">hidden</code> (true = gizli kullanıcı)</p>
        <textarea id="gamdom-entries-json" class="admin-input min-h-[14rem] font-mono text-xs" rows="12">${escapeHtml(entriesJson)}</textarea>
      </div>
      <div class="admin-panel admin-panel--padded space-y-3 border border-slate-200/80">
        <p class="text-sm font-semibold text-slate-700">Gamdom wager-events API</p>
        <label class="flex cursor-pointer items-start gap-3">
          <input type="checkbox" id="gamdom-api-enabled" class="admin-checkbox" ${api.enabled ? "checked" : ""} />
          <span class="text-sm text-slate-600">Canlı API (sitede anlık sıralama — JSON yedek için senkronize edebilirsiniz)</span>
        </label>
        <div>
          <label class="admin-label" for="gamdom-api-url">API URL</label>
          <input id="gamdom-api-url" class="admin-input" value="${escapeHtml(api.url || "https://api.rxhbgdnjdkgfdjshsdghsf.net/api/client/wager-events-api/gamdom")}" />
        </div>
        <div class="grid gap-4 sm:grid-cols-2">
          <div>
            <label class="admin-label" for="gamdom-api-client">X-Client başlığı</label>
            <input id="gamdom-api-client" class="admin-input" value="${escapeHtml(api.clientId || api.xClient || "mocker.com")}" placeholder="mocker.com" />
          </div>
          <div>
            <label class="admin-label" for="gamdom-api-origin">Origin (Referer)</label>
            <input id="gamdom-api-origin" class="admin-input" value="${escapeHtml(api.origin || "https://mocker27.com")}" />
          </div>
        </div>
        <div>
          <label class="admin-label" for="gamdom-api-key">Bearer token (isteğe bağlı)</label>
          <input id="gamdom-api-key" class="admin-input" type="password" autocomplete="off" value="${escapeHtml(api.apiKey || "")}" />
        </div>
        <div>
          <label class="admin-label" for="gamdom-api-month-param">Ay sorgu parametresi</label>
          <input id="gamdom-api-month-param" class="admin-input" value="${escapeHtml(api.monthParam || "month")}" placeholder="month" />
          <p class="mt-1 text-xs text-slate-500">Örn. <code class="admin-code">?month=2026-06</code> — yanıttaki <code class="admin-code">availableMonths</code> sekmeleri doldurur.</p>
        </div>
        <button type="button" class="admin-btn admin-btn-secondary btn-gamdom-sync">API'den JSON yedeğe çek</button>
        <p class="text-xs text-slate-500">Yanıt: <code class="admin-code">{"status":true,"data":{"entries":[...],"availableMonths":[...]}}</code> — position, username, points, reward, user_id -1 = gizli.</p>
      </div>
    </div>`;
}

function readGamdomTurnuvaFromDomIfVisible() {
  if (!$("#gamdom-enabled")) return;
  const prev = state.gamdomTurnuva && typeof state.gamdomTurnuva === "object" ? state.gamdomTurnuva : {};
  const periodsData = prev.periodsData && typeof prev.periodsData === "object" ? { ...prev.periodsData } : {};

  const periodsRaw = String($("#gamdom-periods-lines")?.value || "").trim();
  const periods = [];
  for (const line of periodsRaw.split("\n")) {
    const t = line.trim();
    if (!t) continue;
    const [idRaw, ...labelParts] = t.split("|");
    const id = String(idRaw || "").replace(/[^a-zA-Z0-9_-]/g, "");
    if (!id) continue;
    const label = labelParts.join("|").trim() || id;
    periods.push({ id, label });
  }
  if (!periods.length) periods.push({ id: "default", label: "Güncel dönem" });

  let activePeriodId = String($("#gamdom-active-period")?.value || "").replace(/[^a-zA-Z0-9_-]/g, "");
  if (!activePeriodId || !periods.some((p) => p.id === activePeriodId)) {
    activePeriodId = periods[0].id;
  }

  let entries = [];
  try {
    const raw = String($("#gamdom-entries-json")?.value || "").trim();
    if (raw) {
      const parsed = JSON.parse(raw);
      if (Array.isArray(parsed)) entries = parsed;
    }
  } catch {
    entries = periodsData[activePeriodId]?.entries || [];
  }
  periodsData[activePeriodId] = { entries };

  for (const p of periods) {
    if (!periodsData[p.id]) periodsData[p.id] = { entries: [] };
  }

  const endVal = $("#gamdom-end")?.value?.trim() || "";

  state.gamdomTurnuva = {
    enabled: $("#gamdom-enabled").checked,
    title: $("#gamdom-title")?.value.trim() ?? "",
    prizePool: $("#gamdom-prize")?.value.trim() ?? "",
    logoUrl: $("#gamdom-logo-url")?.value.trim() ?? "",
    ctaLabel: $("#gamdom-cta-label")?.value.trim() ?? "",
    ctaUrl: $("#gamdom-cta-url")?.value.trim() ?? "",
    rulesUrl: $("#gamdom-rules-url")?.value.trim() ?? "",
    rulesText: $("#gamdom-rules-text")?.value.trim() ?? "",
    rulesTitle: $("#gamdom-rules-title")?.value.trim() ?? "Kurallar ve Şartlar",
    endAt: endVal ? datetimeLocalToUnix(endVal) : null,
    activePeriodId,
    periods,
    periodsData,
    api: {
      enabled: $("#gamdom-api-enabled")?.checked ?? false,
      url: $("#gamdom-api-url")?.value.trim() ?? "",
      clientId: $("#gamdom-api-client")?.value.trim() ?? "mocker.com",
      origin: $("#gamdom-api-origin")?.value.trim() ?? "",
      apiKey: $("#gamdom-api-key")?.value.trim() ?? "",
      method: "GET",
      monthParam: $("#gamdom-api-month-param")?.value.trim() || "month",
    },
  };
}

function readPopupFromDomIfVisible() {
  if (!$("#popup-enabled")) return;
  const startVal = $("#popup-start")?.value?.trim() || "";
  const endVal = $("#popup-end")?.value?.trim() || "";
  state.popup = {
    enabled: ($("#popup-enabled")).checked,
    title: $("#popup-title")?.value.trim() ?? "",
    imageUrl: $("#popup-image-url")?.value.trim() ?? "",
    linkUrl: $("#popup-link-url")?.value.trim() ?? "",
    startAt: startVal ? datetimeLocalToUnix(startVal) : null,
    endAt: endVal ? datetimeLocalToUnix(endVal) : null,
    frequency: $("#popup-frequency")?.value === "day" ? "day" : "session",
  };
}

function readCssFromDomIfVisible() {
  const cssEl = $("#site-custom-css");
  if (!cssEl) return;
  const base = normalizeSiteForAdmin(state.site || {});
  state.site = {
    ...base,
    customCss: String(cssEl.value || ""),
  };
}

function captureCurrentTab() {
  if (state.tab === "social" && $("#social-list")) state.social = readSocialFromDom();
  if ((state.tab === "site" || state.tab === "maintenance") && ($("#site-tr-telegram") || $("#site-maintenance-enabled"))) {
    readSiteFromDomIfVisible();
  }
  if (state.tab === "popup" && $("#popup-enabled")) readPopupFromDomIfVisible();
  if (state.tab === "legal-pages" && $("#legal-privacy-tr-title")) readLegalPagesFromDomIfVisible();
  if (state.tab === "gamdom-turnuva" && $("#gamdom-enabled")) readGamdomTurnuvaFromDomIfVisible();
  if (state.tab === "css" && $("#site-custom-css")) readCssFromDomIfVisible();
  if (state.tab === "games-covers" && $("#games-cover-url-blackjack")) {
    state.gamesCovers = {
      blackjack: String($("#games-cover-url-blackjack")?.value || "").trim(),
      slot: String($("#games-cover-url-slot")?.value || "").trim(),
      roulette: String($("#games-cover-url-roulette")?.value || "").trim(),
    };
  }
}

async function main() {
  const sess = await loadSession();
  if (!sess) {
    renderLogin();
    return;
  }
  state.authenticated = true;
  ensureMembersTabDelegation();
  installAdminHashListener();
  const ht = parseAdminTabFromHash();
  if (ht && canPerm(ht)) {
    state.tab = ht;
  }
  if (!canPerm(state.tab)) {
    state.tab = firstAllowedTab();
  }
  try {
    await loadAll();
  } catch (e) {
    const err = e.body?.error;
    if (err === "forbidden") {
      state.sponsors = [];
      state.social = [];
      state.site = {};
      state.popup = {};
      state.market = [];
    } else {
      toast("Veri yüklenemedi — dosya izinlerini ve data/*.json yollarını kontrol et.", "err");
      state.sponsors = [];
      state.social = [];
      state.site = {};
      state.popup = {};
      state.market = [];
    }
    state.marketRequests = [];
    state.complaints = [];
    state.affiliateRequests = [];
  }
  if (canPerm("complaints")) {
    try {
      await loadComplaints();
    } catch {
      state.complaints = [];
    }
  } else {
    state.complaints = [];
  }
  if (canPerm("affiliate-requests")) {
    try {
      await loadAffiliateRequests();
    } catch {
      state.affiliateRequests = [];
    }
  } else {
    state.affiliateRequests = [];
  }
  if (canPerm("special-odds")) {
    try {
      await loadSpecialOdds();
    } catch {
      state.specialOddsMatches = [];
    }
  } else {
    state.specialOddsMatches = [];
  }
  if (canPerm("tickets")) {
    try {
      await loadTicketEvents();
      if (state.tab === "tickets") await loadJoinGuides();
    } catch {
      state.ticketEvents = [];
    }
  } else {
    state.ticketEvents = [];
  }
  if (canPerm("site-events") && state.tab === "site-events") {
    try {
      await loadSiteEvents();
      await loadJoinGuides();
    } catch {
      state.siteEvents = [];
    }
  } else if (canPerm("site-events")) {
    state.siteEvents = state.siteEvents || [];
  }
  if (canPerm("site-news") && state.tab === "site-news") {
    try {
      await loadSiteNews();
    } catch {
      state.siteNews = [];
    }
  } else if (canPerm("site-news")) {
    state.siteNews = state.siteNews || [];
  }
  if (canPerm("site-notifications") && state.tab === "site-notifications") {
    try {
      await loadSiteNotifications();
    } catch {
      state.siteNotifications = [];
    }
  } else if (canPerm("site-notifications")) {
    state.siteNotifications = state.siteNotifications || [];
  }
  if (canPerm("wheel")) {
    await loadWheelConfig();
  } else {
    state.wheelConfig = { cooldown_hours: 24, prizes: [10, 25, 50, 75, 100, 0], enabled: true };
  }
  if (canPerm("games-covers")) {
    try {
      await loadGamesCovers();
    } catch {
      state.gamesCovers = null;
    }
  }
  if (canPerm("dashboard")) {
    initDashboardRangeToday();
    try {
      await loadDashboardStats();
    } catch (e) {
      toast(e.message || "Özet yüklenemedi", "err");
      state.dashboardStats = null;
    }
  } else {
    state.dashboardStats = null;
  }
  if (state.super) {
    try {
      await loadStaff();
    } catch {
      state.operators = [];
    }
  }
  if (state.tab === "members" || state.tab === "ip-clashes") {
    try {
      await loadMembers();
    } catch {
      state.members = [];
    }
  }
  if (state.tab === "market-requests") {
    try {
      await loadMarketRequests();
    } catch {
      state.marketRequests = [];
    }
  }
  if (canPerm("promo-codes") && state.tab === "promo-codes") {
    try {
      await loadPromoCodes();
    } catch {
      state.promoCodes = [];
    }
  }
  if (canPerm("telegram-invites") && state.tab === "telegram-invites") {
    try {
      await loadTelegramInvites();
    } catch {
      state.telegramInvites = null;
    }
  }
  if (canPerm("telegram-broadcast") && state.tab === "telegram-broadcast") {
    try {
      await loadTelegramBroadcast();
    } catch {
      state.telegramBroadcast = null;
    }
  }
  if (canPerm("audit") && state.tab === "audit") {
    try {
      await loadAuditLog();
    } catch {
      state.auditLog = [];
      state.auditTotal = 0;
    }
  }
  if (canPerm("chat") && state.tab === "chat") {
    try {
      await loadAdminChat();
    } catch {
      state.chatMessages = [];
      state.chatMutes = [];
    }
  }
  try {
    await loadMyAdminProfile();
  } catch {
    state.myAdminProfile = null;
  }
  if (canUseAdminHeaderAlerts()) {
    try {
      await loadAdminHeaderAlerts();
    } catch {
      state.headerAlerts = { totalPending: 0, sections: [], items: [], serverTime: 0 };
    }
  } else {
    state.headerAlerts = { totalPending: 0, sections: [], items: [], serverTime: 0 };
  }
  try {
    renderDashboard();
  } catch (e) {
    console.error(e);
    const app = $("#app");
    if (app) {
      app.innerHTML = `<div class="admin-glass mx-auto max-w-lg rounded-2xl p-8 text-center">
        <p class="admin-section__title text-lg">Panel yüklenemedi</p>
        <p class="mt-2 text-sm text-slate-400">${escapeHtml(e?.message || String(e))}</p>
        <button type="button" class="admin-btn admin-btn-primary mt-6 px-6 py-2.5" onclick="location.reload()">Yeniden dene</button>
      </div>`;
    }
    toast("Panel yüklenemedi — konsolu kontrol edin.", "err");
  }
}

main().catch((e) => {
  console.error(e);
  resetAdminAuthState();
  renderLogin();
  try {
    toast(e?.message || "Başlatma hatası", "err");
  } catch {
    /* ignore */
  }
});

document.addEventListener("click", (e) => {
  if (!state.authenticated) return;
  const joinBtn = e.target instanceof Element ? e.target.closest(".btn-save-join-guide") : null;
  if (joinBtn) {
    e.preventDefault();
    const kind = String(joinBtn.getAttribute("data-join-guide-kind") || "");
    if (kind !== "events" && kind !== "tickets") return;
    void (async () => {
      try {
        await saveJoinGuideFromDom(kind);
        toast(
          kind === "tickets" ? "Bilet «Nasıl katılırım» kaydedildi" : "Etkinlik «Nasıl katılırım» kaydedildi",
          "ok",
        );
      } catch (err) {
        toast(err?.message || "Metinler kaydedilemedi", "err");
      }
    })();
    return;
  }
  const uploadGamdomLogo = e.target instanceof Element ? e.target.closest(".btn-gamdom-logo-upload") : null;
  if (uploadGamdomLogo) {
    e.preventDefault();
    void (async () => {
      const fileEl = $("#gamdom-logo-file");
      const file = fileEl?.files?.[0];
      if (!file) {
        toast("Önce bir logo dosyası seçin.", "err");
        return;
      }
      const fd = new FormData();
      fd.append("logo", file);
      try {
        const j = await api("admin_gamdom_logo.php", { method: "POST", body: fd });
        if (!j?.ok || !j.logoUrl) throw new Error(j?.detail || j?.error || "Yükleme başarısız");
        const urlInput = $("#gamdom-logo-url");
        if (urlInput) {
          urlInput.value = j.logoUrl;
          urlInput.dispatchEvent(new Event("input", { bubbles: true }));
        }
        if (fileEl) fileEl.value = "";
        readGamdomTurnuvaFromDomIfVisible();
        toast("Logo yüklendi. Turnuva ayarlarını kaydetmeyi unutmayın.", "ok");
      } catch (err) {
        toast(err?.message || "Logo yüklenemedi", "err");
      }
    })();
    return;
  }
  const syncGamdom = e.target instanceof Element ? e.target.closest(".btn-gamdom-sync") : null;
  if (syncGamdom) {
    e.preventDefault();
    void (async () => {
      readGamdomTurnuvaFromDomIfVisible();
      const periodId = state.gamdomTurnuva?.activePeriodId || "default";
      try {
        const j = await api("admin_gamdom_turnuva_sync.php", {
          method: "POST",
          body: JSON.stringify({
            periodId,
            apiUrl: state.gamdomTurnuva?.api?.url,
            apiKey: state.gamdomTurnuva?.api?.apiKey,
            clientId: state.gamdomTurnuva?.api?.clientId,
          }),
        });
        if (!j?.ok) throw new Error(j?.error || "Senkron başarısız");
        await loadAll();
        renderDashboard();
        toast(`API'den ${j.count ?? 0} kayıt alındı`, "ok");
      } catch (err) {
        toast(err?.message || "API senkron hatası", "err");
      }
    })();
    return;
  }
  if (!e.target.closest(".btn-cms-panel-save")) return;
  e.preventDefault();
  void saveActiveCmsTab();
});

document.addEventListener("keydown", (e) => {
  if (e.key !== "Escape") return;
  if (state.staffEditOp != null) {
    e.preventDefault();
    state.staffEditOp = null;
    renderDashboard();
    return;
  }
  if (state.viewMemberProfileUserId != null) {
    e.preventDefault();
    state.viewMemberProfileUserId = null;
    state.memberProfileDetail = null;
    state.memberProfileActiveTab = "overview";
    state.memberProfileBlackjackPage = 0;
    state.memberProfileSlotPage = 0;
    renderDashboard();
    return;
  }
  if (state.editPasswordUserId != null) {
    e.preventDefault();
    state.editPasswordUserId = null;
    renderDashboard();
    return;
  }
  if (state.editMarketId) {
    e.preventDefault();
    state.editMarketId = null;
    renderDashboard();
    return;
  }
  if (state.editSponsorPromoId) {
    e.preventDefault();
    closeSponsorPromoModal();
    return;
  }
  if (state.editBonusId) {
    e.preventDefault();
    closeBonusModal();
    return;
  }
  if (!state.editSponsorId) return;
  e.preventDefault();
  state.editSponsorId = null;
  renderDashboard();
});
