Greasy Fork

Greasy Fork is available in English.

World Guessr Cheat Panel [PATCHED] YOU WILL GET BANNED! ( I WILL TRY TO FIX IT )

Adds a draggable panel to World Guessr that reads coordinates already present on the page, shows a live map preview, and allows placing/removing a pin.

当前为 2026-04-27 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         World Guessr Cheat Panel [PATCHED] YOU WILL GET BANNED! ( I WILL TRY TO FIX IT )
// @namespace    http://greasyfork.icu/users/your-user-id
// @version      1.0.0
// @description  Adds a draggable panel to World Guessr that reads coordinates already present on the page, shows a live map preview, and allows placing/removing a pin.
// @author       RandomAccount
// @license      MIT (Chatgpt)
// @match        https://www.worldguessr.com/
// @grant        none
// @run-at       document-idle
// ==/UserScript==

(() => {
  "use strict";

  let lastCoords = null;
  let isOpen = false;
  let isMinimized = false;
  let isDragging = false;
  let isResizing = false;
  let offsetX = 0;
  let offsetY = 0;
  let savedHeight = 720;
  let savedWidth = 420;
  let pinnedLatLng = null;
  let pinEnabled = false;
  let activeTab = "menu";

  document.querySelectorAll(".sgp-root, .sgp-style").forEach((el) => el.remove());

  const root = document.createElement("div");
  root.className = "sgp-root";
  document.body.appendChild(root);

  const style = document.createElement("style");
  style.className = "sgp-style";
  style.textContent = `
    .sgp-root * {
      box-sizing: border-box;
      font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
    }

    .sgp-fab {
      position: fixed;
      bottom: 22px;
      left: 22px;
      z-index: 100001;
      display: flex;
      align-items: center;
      gap: 10px;
      padding: 14px 18px;
      border: 1px solid rgba(255,255,255,0.15);
      border-radius: 18px;
      background: linear-gradient(135deg, rgba(17,24,39,0.96), rgba(31,41,55,0.96));
      color: #fff;
      font-size: 14px;
      font-weight: 700;
      letter-spacing: 0.2px;
      cursor: pointer;
      box-shadow: 0 18px 45px rgba(0,0,0,0.35);
      transition: transform 0.2s ease, box-shadow 0.2s ease, opacity 0.2s ease;
    }

    .sgp-fab:hover {
      transform: translateY(-2px) scale(1.01);
      box-shadow: 0 22px 52px rgba(0,0,0,0.42);
    }

    .sgp-fab-icon {
      width: 28px;
      height: 28px;
      border-radius: 10px;
      display: grid;
      place-items: center;
      background: linear-gradient(135deg, #34d399, #10b981);
      color: #06281d;
      font-size: 15px;
      font-weight: 900;
      box-shadow: 0 0 18px rgba(16,185,129,0.4);
      flex-shrink: 0;
    }

    .sgp-overlay {
      position: fixed;
      inset: 0;
      z-index: 100000;
      background: rgba(2,6,23,0.12);
      opacity: 0;
      pointer-events: none;
      transition: opacity 0.25s ease;
    }

    .sgp-overlay.open {
      opacity: 1;
      pointer-events: auto;
    }

    .sgp-panel {
      position: absolute;
      top: 26px;
      left: 26px;
      width: 420px;
      height: 720px;
      display: flex;
      flex-direction: column;
      border-radius: 28px;
      overflow: hidden;
      background: #f8fafc;
      border: 1px solid rgba(226,232,240,0.95);
      box-shadow:
        0 25px 70px rgba(15,23,42,0.25),
        0 8px 24px rgba(15,23,42,0.12);
      transform: translateX(-14px) scale(0.97);
      opacity: 0;
      transition: transform 0.28s ease, opacity 0.28s ease;
      user-select: none;
    }

    .sgp-overlay.open .sgp-panel {
      transform: translateX(0) scale(1);
      opacity: 1;
    }

    .sgp-header {
      height: 68px;
      min-height: 68px;
      display: flex;
      align-items: center;
      justify-content: space-between;
      padding: 0 14px 0 16px;
      background: linear-gradient(135deg, #0f172a, #1e293b);
      color: white;
      border-bottom: 1px solid rgba(255,255,255,0.08);
      cursor: move;
    }

    .sgp-header-left {
      display: flex;
      align-items: center;
      gap: 12px;
      min-width: 0;
    }

    .sgp-logo {
      width: 40px;
      height: 40px;
      border-radius: 14px;
      display: grid;
      place-items: center;
      font-size: 18px;
      background: linear-gradient(135deg, #34d399, #10b981);
      color: #06281d;
      box-shadow: 0 0 22px rgba(52,211,153,0.35);
      flex-shrink: 0;
    }

    .sgp-title-group {
      min-width: 0;
    }

    .sgp-title {
      font-size: 15px;
      font-weight: 800;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
      line-height: 1.1;
    }

    .sgp-subtitle {
      font-size: 11px;
      opacity: 0.72;
      margin-top: 4px;
      letter-spacing: 0.2px;
    }

    .sgp-actions {
      display: flex;
      align-items: center;
      gap: 8px;
      flex-shrink: 0;
    }

    .sgp-btn {
      width: 34px;
      height: 34px;
      border: none;
      border-radius: 12px;
      background: rgba(255,255,255,0.10);
      color: white;
      cursor: pointer;
      font-size: 15px;
      font-weight: 800;
      transition: all 0.18s ease;
    }

    .sgp-btn:hover {
      background: rgba(255,255,255,0.20);
      transform: scale(1.06);
    }

    .sgp-content {
      flex: 1;
      min-height: 0;
      display: flex;
      flex-direction: column;
      gap: 12px;
      padding: 14px;
      background: #f8fafc;
      overflow: hidden;
    }

    .sgp-tabs {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 10px;
      background: rgba(255,255,255,0.7);
      padding: 6px;
      border-radius: 18px;
      border: 1px solid rgba(226,232,240,0.95);
      box-shadow: 0 10px 24px rgba(15,23,42,0.05);
      flex-shrink: 0;
    }

    .sgp-tab-btn {
      border: none;
      border-radius: 14px;
      padding: 11px 12px;
      font-size: 12px;
      font-weight: 800;
      letter-spacing: 0.02em;
      cursor: pointer;
      background: transparent;
      color: #475569;
      transition: all 0.18s ease;
    }

    .sgp-tab-btn:hover {
      background: rgba(241,245,249,0.95);
      color: #0f172a;
    }

    .sgp-tab-btn.active {
      background: linear-gradient(135deg, #0f172a, #1e293b);
      color: white;
      box-shadow: 0 10px 20px rgba(15,23,42,0.18);
    }

    .sgp-pages {
      flex: 1;
      min-height: 0;
      position: relative;
      overflow: hidden;
    }

    .sgp-page {
      position: absolute;
      inset: 0;
      display: none;
      flex-direction: column;
      gap: 12px;
      overflow-y: auto;
      overflow-x: hidden;
      padding-right: 4px;
      min-height: 0;
    }

    .sgp-page.active {
      display: flex;
    }

    .sgp-page::-webkit-scrollbar {
      width: 8px;
    }

    .sgp-page::-webkit-scrollbar-thumb {
      background: rgba(148,163,184,0.4);
      border-radius: 999px;
    }

    .sgp-page::-webkit-scrollbar-track {
      background: transparent;
    }

    .sgp-menu-card,
    .sgp-map-card,
    .sgp-info-card,
    .sgp-credit-page-card {
      background: #ffffff;
      border: 1px solid rgba(226,232,240,0.95);
      border-radius: 22px;
      box-shadow: 0 10px 30px rgba(15,23,42,0.06);
    }

    .sgp-menu-card {
      padding: 14px;
      display: flex;
      flex-direction: column;
      gap: 14px;
      background: linear-gradient(180deg, rgba(255,255,255,0.98), rgba(248,250,252,0.98));
      flex-shrink: 0;
    }

    .sgp-menu-section {
      display: flex;
      flex-direction: column;
      gap: 10px;
    }

    .sgp-menu-section-title {
      font-size: 11px;
      font-weight: 800;
      letter-spacing: 0.08em;
      text-transform: uppercase;
      color: #64748b;
      padding: 0 2px;
    }

    .sgp-menu-labels,
    .sgp-menu-buttons {
      display: grid;
      gap: 10px;
    }

    .sgp-menu-labels {
      grid-template-columns: repeat(3, 1fr);
    }

    .sgp-menu-buttons {
      grid-template-columns: repeat(2, 1fr);
    }

    .sgp-menu-label-static {
      position: relative;
      padding: 14px 10px 12px;
      border-radius: 18px;
      background: linear-gradient(180deg, #ffffff, #f8fafc);
      border: 1px solid rgba(226,232,240,0.95);
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 9px;
      text-align: center;
      cursor: default;
      box-shadow: 0 8px 22px rgba(15,23,42,0.05);
    }

    .sgp-menu-label-static::after {
      content: "";
      position: absolute;
      inset: 0;
      border-radius: 18px;
      pointer-events: none;
      box-shadow: inset 0 1px 0 rgba(255,255,255,0.75);
    }

    .sgp-menu-item {
      position: relative;
      padding: 14px 10px 12px;
      border-radius: 18px;
      background: linear-gradient(180deg, #ffffff, #f8fafc);
      border: 1px solid rgba(226,232,240,0.95);
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 9px;
      cursor: pointer;
      text-align: center;
      box-shadow: 0 10px 24px rgba(15,23,42,0.07);
      transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease, background 0.18s ease;
    }

    .sgp-menu-item:hover {
      transform: translateY(-2px);
      box-shadow: 0 14px 28px rgba(15,23,42,0.11);
      border-color: rgba(148,163,184,0.45);
      background: linear-gradient(180deg, #ffffff, #f1f5f9);
    }

    .sgp-menu-item:active {
      transform: translateY(0);
    }

    .sgp-pin-btn {
      background: linear-gradient(135deg, #ecfdf5, #d1fae5);
      border: 1px solid rgba(16,185,129,0.22);
      outline: none;
    }

    .sgp-pin-btn:hover {
      background: linear-gradient(135deg, #f0fdf4, #bbf7d0);
      border-color: rgba(16,185,129,0.32);
    }

    .sgp-remove-pin-btn {
      background: linear-gradient(135deg, #fff7ed, #ffedd5);
      border: 1px solid rgba(249,115,22,0.20);
      outline: none;
    }

    .sgp-remove-pin-btn:hover {
      background: linear-gradient(135deg, #fff7ed, #fed7aa);
      border-color: rgba(249,115,22,0.32);
    }

    .sgp-menu-icon {
      width: 42px;
      height: 42px;
      border-radius: 15px;
      display: grid;
      place-items: center;
      background: linear-gradient(135deg, rgba(16,185,129,0.16), rgba(59,130,246,0.14));
      font-size: 19px;
      box-shadow: inset 0 1px 0 rgba(255,255,255,0.7), 0 6px 14px rgba(15,23,42,0.06);
    }

    .sgp-pin-btn .sgp-menu-icon {
      background: linear-gradient(135deg, rgba(16,185,129,0.24), rgba(34,197,94,0.18));
    }

    .sgp-remove-pin-btn .sgp-menu-icon {
      background: linear-gradient(135deg, rgba(249,115,22,0.20), rgba(251,146,60,0.16));
    }

    .sgp-menu-label {
      font-size: 12px;
      font-weight: 800;
      color: #0f172a;
      line-height: 1.25;
    }

    .sgp-menu-label-static .sgp-menu-label {
      color: #334155;
    }

    .sgp-menu-hint {
      font-size: 10px;
      font-weight: 600;
      color: #94a3b8;
      line-height: 1.2;
    }

    .sgp-credit-page-card {
      position: relative;
      overflow: hidden;
      padding: 22px;
      min-height: 100%;
      background:
        radial-gradient(circle at top right, rgba(52,211,153,0.16), transparent 26%),
        radial-gradient(circle at bottom left, rgba(59,130,246,0.14), transparent 28%),
        linear-gradient(135deg, #ffffff, #f8fafc);
      flex-shrink: 0;
    }

    .sgp-credit-page-card::after {
      content: "";
      position: absolute;
      inset: 0;
      pointer-events: none;
      background: linear-gradient(
        120deg,
        rgba(255,255,255,0.22),
        transparent 35%,
        transparent 65%,
        rgba(255,255,255,0.10)
      );
    }

    .sgp-credit-page-inner {
      position: relative;
      z-index: 1;
      display: flex;
      flex-direction: column;
      gap: 18px;
    }

    .sgp-credit-hero {
      text-align: center;
      padding: 8px 6px 2px;
    }

    .sgp-credit-kicker {
      display: inline-flex;
      align-items: center;
      gap: 8px;
      padding: 7px 12px;
      border-radius: 999px;
      font-size: 10px;
      font-weight: 800;
      letter-spacing: 0.08em;
      text-transform: uppercase;
      color: #0f172a;
      background: rgba(255,255,255,0.8);
      border: 1px solid rgba(226,232,240,0.92);
      box-shadow: 0 8px 18px rgba(15,23,42,0.05);
    }

    .sgp-credit-hero-title {
      margin-top: 14px;
      font-size: 24px;
      font-weight: 900;
      letter-spacing: -0.03em;
      color: #0f172a;
    }

    .sgp-credit-hero-subtitle {
      margin-top: 8px;
      font-size: 13px;
      line-height: 1.6;
      color: #475569;
      max-width: 300px;
      margin-left: auto;
      margin-right: auto;
    }

    .sgp-credit-showcase {
      display: grid;
      grid-template-columns: 1fr;
      gap: 14px;
    }

    .sgp-credit-profile {
      display: flex;
      align-items: center;
      gap: 14px;
      padding: 16px;
      border-radius: 22px;
      background: rgba(255,255,255,0.82);
      border: 1px solid rgba(226,232,240,0.95);
      box-shadow: 0 14px 30px rgba(15,23,42,0.07);
      backdrop-filter: blur(8px);
    }

    .sgp-credit-profile-avatar {
      width: 56px;
      height: 56px;
      border-radius: 18px;
      display: grid;
      place-items: center;
      font-size: 26px;
      flex-shrink: 0;
      background: linear-gradient(135deg, rgba(16,185,129,0.18), rgba(59,130,246,0.16));
      box-shadow: inset 0 1px 0 rgba(255,255,255,0.75), 0 10px 18px rgba(15,23,42,0.08);
    }

    .sgp-credit-profile-content {
      min-width: 0;
    }

    .sgp-credit-profile-role {
      font-size: 11px;
      font-weight: 800;
      letter-spacing: 0.08em;
      text-transform: uppercase;
      color: #64748b;
      margin-bottom: 5px;
    }

    .sgp-credit-profile-name {
      font-size: 18px;
      font-weight: 900;
      color: #0f172a;
      line-height: 1.2;
    }

    .sgp-credit-profile-desc {
      margin-top: 6px;
      font-size: 12px;
      line-height: 1.5;
      color: #475569;
    }

    .sgp-info-card {
      padding: 12px 14px;
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 12px;
      flex-shrink: 0;
    }

    .sgp-info-left {
      min-width: 0;
    }

    .sgp-badge {
      display: inline-flex;
      align-items: center;
      gap: 6px;
      padding: 6px 10px;
      border-radius: 999px;
      background: rgba(15,23,42,0.92);
      color: white;
      font-size: 11px;
      font-weight: 700;
      margin-bottom: 8px;
    }

    .sgp-coords {
      font-size: 13px;
      font-weight: 700;
      color: #0f172a;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
      max-width: 220px;
    }

    .sgp-meta {
      font-size: 11px;
      color: #475569;
      margin-top: 4px;
    }

    .sgp-copy {
      padding: 10px 12px;
      border: none;
      border-radius: 14px;
      background: linear-gradient(135deg, #0f172a, #1e293b);
      color: white;
      font-size: 12px;
      font-weight: 700;
      cursor: pointer;
      transition: transform 0.15s ease, opacity 0.15s ease;
      white-space: nowrap;
    }

    .sgp-copy:hover {
      transform: translateY(-1px);
      opacity: 0.95;
    }

    .sgp-map-card {
      position: relative;
      min-height: 320px;
      overflow: hidden;
      display: flex;
      flex-direction: column;
      flex-shrink: 0;
    }

    .sgp-map-topbar {
      height: 42px;
      min-height: 42px;
      display: flex;
      align-items: center;
      justify-content: space-between;
      padding: 0 12px;
      border-bottom: 1px solid rgba(15,23,42,0.06);
      background: #ffffff;
      font-size: 12px;
      font-weight: 700;
      color: #0f172a;
    }

    .sgp-map-status {
      color: #334155;
      font-size: 11px;
      font-weight: 600;
    }

    .sgp-iframe-wrap {
      position: relative;
      flex: 1;
      min-height: 260px;
      background: white;
    }

    .sgp-iframe {
      width: 100%;
      height: 100%;
      border: none;
      background: white;
      display: block;
    }

    .sgp-floating-status {
      position: absolute;
      left: 12px;
      bottom: 12px;
      z-index: 2;
      padding: 8px 10px;
      border-radius: 14px;
      background: rgba(15,23,42,0.78);
      color: white;
      font-size: 11px;
      font-weight: 700;
      box-shadow: 0 10px 20px rgba(0,0,0,0.2);
      max-width: calc(100% - 24px);
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
      pointer-events: none;
    }

    .sgp-game-pin {
      position: absolute;
      width: 26px;
      height: 26px;
      transform: translate(-50%, -100%);
      z-index: 1000;
      pointer-events: none;
      filter: drop-shadow(0 4px 6px rgba(0,0,0,0.35));
      display: grid;
      place-items: center;
      font-size: 26px;
      line-height: 1;
    }

    .sgp-resize {
      position: absolute;
      right: 0;
      bottom: 0;
      width: 24px;
      height: 24px;
      cursor: nwse-resize;
      background:
        linear-gradient(135deg, transparent 0%, transparent 46%, rgba(15,23,42,0.28) 47%, rgba(15,23,42,0.28) 54%, transparent 55%),
        linear-gradient(135deg, transparent 0%, transparent 63%, rgba(15,23,42,0.42) 64%, rgba(15,23,42,0.42) 71%, transparent 72%),
        linear-gradient(135deg, transparent 0%, transparent 80%, rgba(15,23,42,0.6) 81%, rgba(15,23,42,0.6) 88%, transparent 89%);
    }

    .sgp-dock {
      position: fixed;
      left: 22px;
      bottom: 88px;
      z-index: 100002;
      display: none;
      align-items: center;
      gap: 10px;
      padding: 10px 14px;
      border-radius: 16px;
      background: rgba(15,23,42,0.95);
      color: white;
      box-shadow: 0 14px 36px rgba(0,0,0,0.28);
      cursor: pointer;
      font-size: 13px;
      font-weight: 700;
    }

    .sgp-dock.show {
      display: flex;
    }
  `;
  document.head.appendChild(style);

  const fab = document.createElement("button");
  fab.className = "sgp-fab";
  fab.innerHTML = `
    <span class="sgp-fab-icon">🗺</span>
    <span>Open Secret Project</span>
  `;
  root.appendChild(fab);

  const dock = document.createElement("div");
  dock.className = "sgp-dock";
  dock.innerHTML = `<span>🗺</span><span>Secret Project minimized</span>`;
  root.appendChild(dock);

  const overlay = document.createElement("div");
  overlay.className = "sgp-overlay";
  root.appendChild(overlay);

  const panel = document.createElement("div");
  panel.className = "sgp-panel";
  panel.style.width = `${savedWidth}px`;
  panel.style.height = `${savedHeight}px`;
  overlay.appendChild(panel);

  const header = document.createElement("div");
  header.className = "sgp-header";
  panel.appendChild(header);

  const headerLeft = document.createElement("div");
  headerLeft.className = "sgp-header-left";
  header.appendChild(headerLeft);

  const logo = document.createElement("div");
  logo.className = "sgp-logo";
  logo.textContent = "⌖";
  headerLeft.appendChild(logo);

  const titleGroup = document.createElement("div");
  titleGroup.className = "sgp-title-group";
  headerLeft.appendChild(titleGroup);

  const title = document.createElement("div");
  title.className = "sgp-title";
  title.textContent = "Secret Project";
  titleGroup.appendChild(title);

  const subtitle = document.createElement("div");
  subtitle.className = "sgp-subtitle";
  subtitle.textContent = "Location tools • live map • draggable panel";
  titleGroup.appendChild(subtitle);

  const actions = document.createElement("div");
  actions.className = "sgp-actions";
  header.appendChild(actions);

  const minimizeBtn = document.createElement("button");
  minimizeBtn.className = "sgp-btn";
  minimizeBtn.textContent = "–";
  actions.appendChild(minimizeBtn);

  const closeBtn = document.createElement("button");
  closeBtn.className = "sgp-btn";
  closeBtn.textContent = "✕";
  actions.appendChild(closeBtn);

  const content = document.createElement("div");
  content.className = "sgp-content";
  panel.appendChild(content);

  const tabs = document.createElement("div");
  tabs.className = "sgp-tabs";
  tabs.innerHTML = `
    <button class="sgp-tab-btn active" data-tab="menu" type="button">Menu</button>
    <button class="sgp-tab-btn" data-tab="credits" type="button">Credits</button>
  `;
  content.appendChild(tabs);

  const pages = document.createElement("div");
  pages.className = "sgp-pages";
  content.appendChild(pages);

  const menuPage = document.createElement("div");
  menuPage.className = "sgp-page active";
  menuPage.style.overflowY = "auto";
  menuPage.style.overflowX = "hidden";
  pages.appendChild(menuPage);

  const creditsPage = document.createElement("div");
  creditsPage.className = "sgp-page";
  creditsPage.style.overflowY = "auto";
  creditsPage.style.overflowX = "hidden";
  pages.appendChild(creditsPage);

  const menuCard = document.createElement("div");
  menuCard.className = "sgp-menu-card";
  menuCard.innerHTML = `
    <div class="sgp-menu-section">
      <div class="sgp-menu-section-title">Overview</div>
      <div class="sgp-menu-labels">
        <div class="sgp-menu-label-static">
          <div class="sgp-menu-icon">📍</div>
          <div class="sgp-menu-label">Live Coordinates</div>
          <div class="sgp-menu-hint">Auto-detected</div>
        </div>
        <div class="sgp-menu-label-static">
          <div class="sgp-menu-icon">🛰</div>
          <div class="sgp-menu-label">Map Viewer</div>
          <div class="sgp-menu-hint">Preview panel</div>
        </div>
        <div class="sgp-menu-label-static">
          <div class="sgp-menu-icon">🧭</div>
          <div class="sgp-menu-label">Quick Position</div>
          <div class="sgp-menu-hint">Current location</div>
        </div>
      </div>
    </div>

    <div class="sgp-menu-section">
      <div class="sgp-menu-section-title">Actions</div>
      <div class="sgp-menu-buttons">
        <button class="sgp-menu-item sgp-pin-btn" type="button">
          <div class="sgp-menu-icon">📌</div>
          <div class="sgp-menu-label">Add Pin</div>
          <div class="sgp-menu-hint">Place marker</div>
        </button>
        <button class="sgp-menu-item sgp-remove-pin-btn" type="button">
          <div class="sgp-menu-icon">✖</div>
          <div class="sgp-menu-label">Remove Pin</div>
          <div class="sgp-menu-hint">Clear marker</div>
        </button>
      </div>
    </div>
  `;
  menuPage.appendChild(menuCard);

  const infoCard = document.createElement("div");
  infoCard.className = "sgp-info-card";
  infoCard.innerHTML = `
    <div class="sgp-info-left">
      <div class="sgp-badge">● Tracking page data</div>
      <div class="sgp-coords">Waiting for coordinates...</div>
      <div class="sgp-meta">Auto-refresh every 2 seconds</div>
    </div>
  `;
  menuPage.appendChild(infoCard);

  const copyBtn = document.createElement("button");
  copyBtn.className = "sgp-copy";
  copyBtn.textContent = "Copy";
  infoCard.appendChild(copyBtn);

  const mapCard = document.createElement("div");
  mapCard.className = "sgp-map-card";
  menuPage.appendChild(mapCard);

  const mapTopbar = document.createElement("div");
  mapTopbar.className = "sgp-map-topbar";
  mapTopbar.innerHTML = `
    <span>Google Maps preview</span>
    <span class="sgp-map-status">No location detected yet</span>
  `;
  mapCard.appendChild(mapTopbar);

  const iframeWrap = document.createElement("div");
  iframeWrap.className = "sgp-iframe-wrap";
  mapCard.appendChild(iframeWrap);

  const iframe = document.createElement("iframe");
  iframe.className = "sgp-iframe";
  iframe.loading = "lazy";
  iframe.referrerPolicy = "no-referrer-when-downgrade";
  iframe.allowFullscreen = true;
  iframe.src = "about:blank";
  iframeWrap.appendChild(iframe);

  const floatingStatus = document.createElement("div");
  floatingStatus.className = "sgp-floating-status";
  floatingStatus.textContent = "Waiting for coordinates...";
  iframeWrap.appendChild(floatingStatus);

  const creditPageCard = document.createElement("div");
  creditPageCard.className = "sgp-credit-page-card";
  creditPageCard.innerHTML = `
    <div class="sgp-credit-page-inner">
      <div class="sgp-credit-hero">
        <div class="sgp-credit-kicker">✨ Secret Project Credits</div>
        <div class="sgp-credit-hero-title">Built with vision</div>
        <div class="sgp-credit-hero-subtitle">
          A dedicated page honoring the minds behind the experience.
        </div>
      </div>

      <div class="sgp-credit-showcase">
        <div class="sgp-credit-profile">
          <div class="sgp-credit-profile-avatar">👑</div>
          <div class="sgp-credit-profile-content">
            <div class="sgp-credit-profile-role">Founder</div>
            <div class="sgp-credit-profile-name">RandomAccount</div>
            <div class="sgp-credit-profile-desc">
              Visionary behind the concept, direction, and identity of Secret Project.
            </div>
          </div>
        </div>

        <div class="sgp-credit-profile">
          <div class="sgp-credit-profile-avatar">🤖</div>
          <div class="sgp-credit-profile-content">
            <div class="sgp-credit-profile-role">Developer</div>
            <div class="sgp-credit-profile-name">ChatGPT</div>
            <div class="sgp-credit-profile-desc">
              Designed and engineered the interface, logic, layout, and interactive experience.
            </div>
          </div>
        </div>
      </div>
    </div>
  `;
  creditsPage.appendChild(creditPageCard);

  const coordsEl = infoCard.querySelector(".sgp-coords");
  const pinBtn = menuCard.querySelector(".sgp-pin-btn");
  const removePinBtn = menuCard.querySelector(".sgp-remove-pin-btn");
  const mapStatus = mapTopbar.querySelector(".sgp-map-status");
  const tabButtons = tabs.querySelectorAll(".sgp-tab-btn");

  function setActiveTab(tabName) {
    activeTab = tabName;

    tabButtons.forEach((btn) => {
      btn.classList.toggle("active", btn.dataset.tab === tabName);
    });

    menuPage.classList.toggle("active", tabName === "menu");
    creditsPage.classList.toggle("active", tabName === "credits");
  }

  tabButtons.forEach((btn) => {
    btn.addEventListener("click", (event) => {
      event.stopPropagation();
      setActiveTab(btn.dataset.tab);
    });
  });

  const resizeHandle = document.createElement("div");
  resizeHandle.className = "sgp-resize";
  panel.appendChild(resizeHandle);

  function openWindow() {
    overlay.classList.add("open");
    fab.innerHTML = `<span class="sgp-fab-icon">✕</span><span>Close Secret Project</span>`;
    dock.classList.remove("show");
    isOpen = true;
  }

  function closeWindow() {
    overlay.classList.remove("open");
    fab.innerHTML = `<span class="sgp-fab-icon">🗺</span><span>Open Secret Project</span>`;
    dock.classList.remove("show");
    isOpen = false;
    isMinimized = false;
    content.style.display = "flex";
    panel.style.height = `${savedHeight}px`;
    panel.style.width = `${savedWidth}px`;
    minimizeBtn.textContent = "–";
    resizeHandle.style.display = "block";
  }

  function minimizeWindow() {
    if (!isMinimized) {
      savedHeight = panel.offsetHeight;
      savedWidth = panel.offsetWidth;
      content.style.display = "none";
      resizeHandle.style.display = "none";
      panel.style.height = "68px";
      panel.style.width = "320px";
      minimizeBtn.textContent = "▢";
      dock.classList.add("show");
    } else {
      content.style.display = "flex";
      resizeHandle.style.display = "block";
      panel.style.height = `${savedHeight}px`;
      panel.style.width = `${savedWidth}px`;
      minimizeBtn.textContent = "–";
      dock.classList.remove("show");
    }
    isMinimized = !isMinimized;
  }

  fab.addEventListener("click", () => {
    if (isOpen) {
      closeWindow();
    } else {
      openWindow();
    }
  });

  closeBtn.addEventListener("click", (event) => {
    event.stopPropagation();
    closeWindow();
  });

  minimizeBtn.addEventListener("click", (event) => {
    event.stopPropagation();
    minimizeWindow();
  });

  dock.addEventListener("click", () => {
    if (isOpen && isMinimized) {
      minimizeWindow();
    }
  });

  overlay.addEventListener("mousedown", (event) => {
    if (event.target === overlay) {
      closeWindow();
    }
  });

  header.addEventListener("mousedown", (event) => {
    if (event.target === minimizeBtn || event.target === closeBtn) {
      return;
    }

    isDragging = true;
    const rect = panel.getBoundingClientRect();
    offsetX = event.clientX - rect.left;
    offsetY = event.clientY - rect.top;
  });

  resizeHandle.addEventListener("mousedown", (event) => {
    event.stopPropagation();
    isResizing = true;
  });

  document.addEventListener("mousemove", (event) => {
    if (isDragging) {
      const maxLeft = window.innerWidth - panel.offsetWidth - 10;
      const maxTop = window.innerHeight - panel.offsetHeight - 10;
      const newLeft = Math.min(Math.max(10, event.clientX - offsetX), Math.max(10, maxLeft));
      const newTop = Math.min(Math.max(10, event.clientY - offsetY), Math.max(10, maxTop));
      panel.style.left = `${newLeft}px`;
      panel.style.top = `${newTop}px`;
    }

    if (isResizing && !isMinimized) {
      const rect = panel.getBoundingClientRect();
      const newWidth = Math.max(event.clientX - rect.left, 340);
      const newHeight = Math.max(event.clientY - rect.top, 360);
      panel.style.width = `${newWidth}px`;
      panel.style.height = `${newHeight}px`;
      savedWidth = newWidth;
      savedHeight = newHeight;
    }
  });

  document.addEventListener("mouseup", () => {
    isDragging = false;
    isResizing = false;
  });

  copyBtn.addEventListener("click", async () => {
    if (!lastCoords) {
      return;
    }

    try {
      await navigator.clipboard.writeText(lastCoords);
      copyBtn.textContent = "Copied";
      setTimeout(() => {
        copyBtn.textContent = "Copy";
      }, 1200);
    } catch (error) {
      copyBtn.textContent = "Failed";
      setTimeout(() => {
        copyBtn.textContent = "Copy";
      }, 1200);
    }
  });

  pinBtn.addEventListener("click", (event) => {
    event.stopPropagation();
    const latLng = getCurrentLatLng();

    if (!latLng) {
      floatingStatus.textContent = "No coordinates available yet";
      mapStatus.textContent = "Pin failed";
      return;
    }

    pinEnabled = true;
    pinnedLatLng = latLng;
    refreshGamePin();
  });

  removePinBtn.addEventListener("click", (event) => {
    event.stopPropagation();
    pinEnabled = false;
    pinnedLatLng = null;

    document.querySelectorAll(".sgp-game-pin").forEach((pin) => pin.remove());

    mapStatus.textContent = "Pin removed";
    floatingStatus.textContent = lastCoords
      ? `Lat ${lastCoords.split(",")[0]} • Lng ${lastCoords.split(",")[1]}`
      : "Waiting for coordinates...";
  });

  function extractCoords() {
    const html = document.documentElement.innerHTML;
    const patterns = [
      /(-?\d{1,3}\.\d+)\s*,\s*(-?\d{1,3}\.\d+)/,
      /"lat"\s*:\s*(-?\d{1,3}\.\d+).*?"lng"\s*:\s*(-?\d{1,3}\.\d+)/i,
      /"latitude"\s*:\s*(-?\d{1,3}\.\d+).*?"longitude"\s*:\s*(-?\d{1,3}\.\d+)/i
    ];

    for (const pattern of patterns) {
      const match = html.match(pattern);

      if (match) {
        const lat = parseFloat(match[1]);
        const lng = parseFloat(match[2]);

        if (
          !Number.isNaN(lat) &&
          lat >= -90 &&
          lat <= 90 &&
          !Number.isNaN(lng) &&
          lng >= -180 &&
          lng <= 180
        ) {
          return `${lat},${lng}`;
        }
      }
    }

    return null;
  }

  function getCurrentLatLng() {
    if (!lastCoords) {
      return null;
    }

    const [latStr, lngStr] = lastCoords.split(",");
    const lat = parseFloat(latStr);
    const lng = parseFloat(lngStr);

    if (Number.isNaN(lat) || Number.isNaN(lng)) {
      return null;
    }

    return { lat, lng };
  }

  function getOrCreateGamePin(container) {
    let pin = container.querySelector(".sgp-game-pin");

    if (!pin) {
      pin = document.createElement("div");
      pin.className = "sgp-game-pin";
      pin.textContent = "📍";
      container.appendChild(pin);
    }

    return pin;
  }

  function parseTranslateAndScale(transformValue) {
    if (!transformValue || transformValue === "none") {
      return { x: 0, y: 0, scale: 1 };
    }

    const match3d = transformValue.match(
      /translate3d\(([-\d.]+)px,\s*([-\d.]+)px,\s*[-\d.]+px\)(?:\s*scale\(([-\d.]+)\))?/
    );

    if (match3d) {
      return {
        x: parseFloat(match3d[1]) || 0,
        y: parseFloat(match3d[2]) || 0,
        scale: parseFloat(match3d[3]) || 1
      };
    }

    const match2d = transformValue.match(
      /translate\(([-\d.]+)px,\s*([-\d.]+)px\)(?:\s*scale\(([-\d.]+)\))?/
    );

    if (match2d) {
      return {
        x: parseFloat(match2d[1]) || 0,
        y: parseFloat(match2d[2]) || 0,
        scale: parseFloat(match2d[3]) || 1
      };
    }

    return { x: 0, y: 0, scale: 1 };
  }

  function latLngToWorldPixel(lat, lng, zoom) {
    const tileSize = 256;
    const scale = tileSize * Math.pow(2, zoom);
    const x = ((lng + 180) / 360) * scale;
    const sinLat = Math.sin((lat * Math.PI) / 180);
    const clamped = Math.min(Math.max(sinLat, -0.9999), 0.9999);
    const y = (0.5 - Math.log((1 + clamped) / (1 - clamped)) / (4 * Math.PI)) * scale;
    return { x, y };
  }

  function isLeafletMapCandidate(obj, container) {
    return Boolean(
      obj &&
        typeof obj === "object" &&
        typeof obj.latLngToContainerPoint === "function" &&
        typeof obj.containerPointToLatLng === "function" &&
        typeof obj.getZoom === "function" &&
        obj._container === container
    );
  }

  function findLeafletMapNearContainer(container) {
    if (!container) {
      return null;
    }

    const seen = new WeakSet();
    const roots = [container, container.parentElement, container.closest("#miniMapArea"), window];

    function scan(obj, depth) {
      if (!obj || typeof obj !== "object" || seen.has(obj) || depth < 0) {
        return null;
      }

      seen.add(obj);

      if (isLeafletMapCandidate(obj, container)) {
        return obj;
      }

      let keys = [];

      try {
        keys = Object.getOwnPropertyNames(obj);
      } catch (error) {
        return null;
      }

      for (const key of keys) {
        if (key === "parentNode" || key === "children" || key === "childNodes") {
          continue;
        }

        let value;

        try {
          value = obj[key];
        } catch (error) {
          continue;
        }

        if (!value || typeof value !== "object") {
          continue;
        }

        if (isLeafletMapCandidate(value, container)) {
          return value;
        }

        const found = scan(value, depth - 1);
        if (found) {
          return found;
        }
      }

      return null;
    }

    for (const rootNode of roots) {
      const found = scan(rootNode, rootNode === window ? 2 : 4);
      if (found) {
        return found;
      }
    }

    return null;
  }

  function getDomProjectionContext() {
    const mapArea = document.querySelector("#miniMapArea");
    const container = mapArea?.querySelector(".leaflet-container");
    const mapPane = mapArea?.querySelector(".leaflet-map-pane");
    const tileContainer = mapArea?.querySelector(".leaflet-tile-container");

    if (!mapArea || !container || !mapPane || !tileContainer) {
      return null;
    }

    const tiles = Array.from(mapArea.querySelectorAll(".leaflet-tile-pane .leaflet-tile-loaded"));

    if (!tiles.length) {
      return null;
    }

    let bestTile = null;
    let bestScore = Infinity;

    for (const tile of tiles) {
      const src = tile.getAttribute("src") || "";
      const xMatch = src.match(/[?&]x=(-?\d+)/);
      const yMatch = src.match(/[?&]y=(-?\d+)/);
      const zMatch = src.match(/[?&]z=(\d+)/);

      if (!xMatch || !yMatch || !zMatch) {
        continue;
      }

      const tileTransform = parseTranslateAndScale(tile.style.transform);
      const containerTransform = parseTranslateAndScale(tileContainer.style.transform);
      const score = Math.abs((containerTransform.scale || 1) - 1);

      if (score < bestScore) {
        bestScore = score;
        bestTile = {
          tileX: parseInt(xMatch[1], 10),
          tileY: parseInt(yMatch[1], 10),
          zoom: parseInt(zMatch[1], 10),
          tileTransform,
          containerTransform
        };
      }
    }

    if (!bestTile) {
      return null;
    }

    const mapPaneTransform = parseTranslateAndScale(mapPane.style.transform);

    return {
      container,
      zoom: bestTile.zoom,
      tileX: bestTile.tileX,
      tileY: bestTile.tileY,
      tileXOffset: bestTile.tileTransform.x,
      tileYOffset: bestTile.tileTransform.y,
      tileContainerX: bestTile.containerTransform.x,
      tileContainerY: bestTile.containerTransform.y,
      tileScale: bestTile.containerTransform.scale || 1,
      mapPaneX: mapPaneTransform.x,
      mapPaneY: mapPaneTransform.y
    };
  }

  function projectWithDomFallback(latLng) {
    const ctx = getDomProjectionContext();

    if (!ctx) {
      return null;
    }

    const world = latLngToWorldPixel(latLng.lat, latLng.lng, ctx.zoom);
    const tileWorldX = ctx.tileX * 256;
    const tileWorldY = ctx.tileY * 256;

    const pixelX =
      ctx.mapPaneX +
      ctx.tileContainerX +
      ctx.tileXOffset +
      (world.x - tileWorldX) * ctx.tileScale;

    const pixelY =
      ctx.mapPaneY +
      ctx.tileContainerY +
      ctx.tileYOffset +
      (world.y - tileWorldY) * ctx.tileScale;

    return {
      container: ctx.container,
      x: pixelX,
      y: pixelY
    };
  }

  function refreshGamePin() {
    if (!pinEnabled || !pinnedLatLng) {
      return;
    }

    const container = document.querySelector("#miniMapArea .leaflet-container");

    if (!container) {
      mapStatus.textContent = "Pin failed";
      floatingStatus.textContent = "Game map not found";
      return;
    }

    const leafletMap = findLeafletMapNearContainer(container);

    if (leafletMap) {
      try {
        const point = leafletMap.latLngToContainerPoint([pinnedLatLng.lat, pinnedLatLng.lng]);
        const pin = getOrCreateGamePin(container);
        pin.style.left = `${point.x}px`;
        pin.style.top = `${point.y}px`;

        const inView =
          point.x >= 0 &&
          point.y >= 0 &&
          point.x <= container.clientWidth &&
          point.y <= container.clientHeight;

        mapStatus.textContent = inView ? "Pin fixed" : "Pin off-screen";
        floatingStatus.textContent = inView
          ? `Pin fixed on map • ${pinnedLatLng.lat}, ${pinnedLatLng.lng}`
          : `Pin is off-screen • ${pinnedLatLng.lat}, ${pinnedLatLng.lng}`;
        return;
      } catch (error) {
        // Fall back to DOM projection below.
      }
    }

    const projected = projectWithDomFallback(pinnedLatLng);

    if (!projected) {
      mapStatus.textContent = "Pin failed";
      floatingStatus.textContent = "Could not project pin";
      return;
    }

    const pin = getOrCreateGamePin(projected.container);
    pin.style.left = `${projected.x}px`;
    pin.style.top = `${projected.y}px`;

    const inView =
      projected.x >= 0 &&
      projected.y >= 0 &&
      projected.x <= projected.container.clientWidth &&
      projected.y <= projected.container.clientHeight;

    mapStatus.textContent = inView ? "Pin tracking" : "Pin off-screen";
    floatingStatus.textContent = inView
      ? `Pin tracking map • ${pinnedLatLng.lat}, ${pinnedLatLng.lng}`
      : `Pin is off-screen • ${pinnedLatLng.lat}, ${pinnedLatLng.lng}`;
  }

  function updatePreviewIframe(lat, lng) {
    const url = `https://www.google.com/maps?q=${encodeURIComponent(`${lat},${lng}`)}&z=6&output=embed`;

    if (iframe.src !== url) {
      iframe.src = url;
    }
  }

  function updateMap() {
    const coords = extractCoords();

    if (!coords) {
      refreshGamePin();
      return;
    }

    if (coords !== lastCoords) {
      const [lat, lng] = coords.split(",");
      lastCoords = coords;

      coordsEl.textContent = `${lat}, ${lng}`;
      floatingStatus.textContent = `Lat ${lat} • Lng ${lng}`;
      mapStatus.textContent = "Location detected";
      updatePreviewIframe(lat, lng);

      if (pinEnabled) {
        pinnedLatLng = {
          lat: parseFloat(lat),
          lng: parseFloat(lng)
        };
      }
    }

    refreshGamePin();
  }

  setInterval(updateMap, 50);
})();