WME - RightClick Functions

Right-click functionality

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(Tôi đã có Trình quản lý tập lệnh người dùng, hãy cài đặt nó!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         WME - RightClick Functions
// @author       GreekCaptain
// @version      1.0.5
// @description  Right-click functionality
// @match        https://www.waze.com/*/editor*
// @match        https://www.waze.com/editor*
// @match        https://beta.waze.com/*/editor*
// @match        https://beta.waze.com/editor*
// @grant        GM_setClipboard
// @grant        unsafeWindow
// @grant        GM_xmlhttpRequest
// @connect      w-tools.org
// @require      https://greatest.deepsurf.us/scripts/24851-wazewrap/code/WazeWrap.js
// @icon         data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADYAAAA2CAYAAACMRWrdAAAXzElEQVR42u2aebRcV3Xmf/ucO9Steq/qjZJlzZZlW7ZlbOMBY2PFjRtiY0hDIhIC2BDoAE7DomGRDlMbM6xOoKGTsBZ0CAsSktBgYRtjBwJm8iwPsuVJlmTNetLTm4d6NdzhnN1/VEkYYyN1eq3u/MFZq9Z7q96ruue7+9vf/va+B463Nm60/BtbG2/S4+5JjvN3A/j3/tX34/u+9cVLs6x1tgRhtVTpHVHbMyYi6sPAWBsJtnst5xCvXkKjgUpgA0nU+SVpu7HcFUXFWCtiLIgINsIEERLGmDCGsISJS5ggUglDgjDC2NAHUZiWyuWxoYHakxeuW3zPOy9bVwft7l30/xSYAfw5F73y2pnJsY9m7dZp3hcYY6f7hhd9ZOCUc28dPGfxwh033th8/gdVVd74gS+UxkfmbU+luWR+ZnrD1Nj4H2dpdqHYABsEqILaCBOWkO7LlquYuIIagwkibBBgghAbRkRRSByH9NbKI0uWLfrvX3rzJX+lnYsJ8qvgghejn3znO+6M9S/7H0cO7nl/q7mAiGQi4mwQThRZ0bz/li9Piohfs+7Csy583e9dHPUNnyxIy8/O7BKRe4BpMYYr3vif5incQVF9sCiyNeJ9j3c56tU4yTGRw0oHiOZtUAUx2CDEGYOJYmwWkDeFBirz09Gydqp/+Udfv/fl73nbpW+5/RO4G1V5PrjgBXNq0ya37txXfHh89MD7W82FPAhCCxKAt95rzWUZkVTWv+bPPv+ZYOmZVx1awBRZRhRaKict5bov/2jcHt77N1/71Ls+89Irz2ne9e2f9Trv+nzhEUmtjSKjxoqaEEl6kSDC5xmt1GP7e4lr/RTtlFLQJZUxWGsxKLEVP3voQCEmeOP//No9ra/eePnbbviEmhvhl4A9LwlvMGz7kr9kw+vPHh3ZfVOzUfelJLEKRlUVMN45nzmNr/jwn3/qsKxev/+hR/itpYF7/fpFbsOqXr+i5PTgVKO3vWTthss2vOryr37oo99beurKpfPTs69C3WprrQ0CI14sXgVchkvb+LDCOVdexhVvfBVX/+6lzLWhUYAJAoLung0qkRUKV9iFqclCguT8V7/jvVs++5IVOzbedJPdtmmTvkjEtgnAxPjBD7VbDRvHUR7YQGxgNM8ycS7XtNUub3jPf3nD9okyg/vvLL750bfZU09dY4MgJClFxAG8M2v5r9yxOf+ZrLz8TR/75D9+/+tfva2cBOcYayLUSZHmFN6gNkYlJqgN8LqP/wknvewcJvZPUR4sU0lCLn7DK0knJnnqzgeRtIk6p40iRxWMIDOjh7Rksw+KcMeZTz/9ohET2OY3Xn9Dz96nH/tiu9WoBNZKaBFVwYM05uZYc+kGY9Zdrc17/1m//tn3md6Bk1BUrDGkaUruHKUkkZeuW21HdjzjR6KhNcXhPRdMHj7Qp94bV2QIXiSuEFSq0m5mcvm73yH9V1wqt3767+XJm/9Fnnpolzz92LMiQ8vYcNV6Obh9hPnxaVAvoBj1lEIxWXNBcG7xGz/0ya99+vo312+44QZz11136VHl67LwBgEYeeqxtc4Vi0SMekScR5wrxLkCgOWXXcXOe+4373zDpYaolyJP2b1rNzt27pQwDKiUIr5502389O7NvPu1l5i8Pq/LX3LBsBHRcqVMFEYYa7XIc8maDSqDA5Qv2sA9X7+TuSeeICzFzEzM0psE7H/gYZ7cekjLAzVKSYkwDKiWI4mtSrvRpGg1vBRpcnj7M6cCbNt2ljxX0juasa1Dw0ZrbjFAGAY+jmJsEKnzaJHlWh4YII8H6c2mOP2M03RmZpZqby/fvukW/ft/+JaWSiW8Ko88+gTbtj9LqVxhTTUg7Rnwtf5BqyogYIOYUinWQLx6Y/XZH96pU48+TLnao+ILDYyiKK35ORmMckqRJRSP0ULr83VN2y3tKRki63zWrNOqz9Y6IH6NKqbtPDLGYG2AV/De4zF4RcJyj+YOaiWjNghR7xmfmOR9730PtWoPU9OzVHt7+NQn/ozxiUnwjoFySCt3gjEgBmMCMJa08ERhhKhy+hWX0NPfq5v/7mZ6B/uRAG145ZyXr+O8l65iy52PMD87SyiOwZ4Ilzvqc7NYTSnbmKxV7wRo0y8X4V8ubEGgXpUsL7RwiooFYxEb4ApPHAnYgDzPyYsC5xx/8bm/lHvvf5C8cOzes48/uPZ6fnb3ZjCWiZk6uSuwUUK1vx8TdDQuiSPCuCS+ucAzP7ybZa+4lDUXreftf/omLrn6Uv7Dda/m+g9cw913bWdq3wgrhhJZvbiH+uwsrZlRVtRyytYxNTFJVp/nuAVaQYyx2DDG2o4k2zDEYzVt5rTnJyEKcYWj2Uqp9Va45pqr9ds33czNt9xGs5Xy26/6d1zzmt/m0Mgojzw7gjed/80VqgNDlEoxDsvUzLwqytiPb2d6714WrVyCOe9Mzlt/JnmzwTf+1wNMbnma0wcNkxOzemR6klWDjt6hkANjOfsOjpMkJQZqPccH5hwaGoe3ioYJoRGC/iU4DLPjRxjZsoWNGy6mmXkS46k3WixfsYIb/uuHmZudZaC/j76+GmFo+eu/u4m99YLF2RgnLR6GIGB+vk57vkEpjhis9dAuPA4l3b6VnU89ycx0i6xwFJNTLO8vsSRSDu4bY7DcYv26iPGZkCf21Gk3G4jPEY1Is6y7+02/xnlYcITUkpw4Uea0hsegeYEOrmDV8hVc8bLzmW6kjI0dYXLScPrpp+ExVPuHcKIcPDTKrbfexpdu28w5r7ua9kOPE1tL0W5RK0c4rziFMLD01yqYKKFdQOqgvXMrQ/1VVq1azNR0nfbcPBeuUfKixBN7C6Zmm/i8jfqia79e0AP/KrAAwAQ0tI++JGRJr2P3XE4rrFJJquyihy/eciel5gwP338/SRyyavUqli9bShBYFhoLHNx/kIcee0ZW/c5bdX7PdhrTc1Rii6gnLMf0lEuIsRhjUOeIxJGUYxYtGmJgoI/R8VkmjoyyuJZRGwrZezhj+755gkCpJDGNAorC0RVZoDg+MGyASEcFp/MqFAHnrraM+hp7zCo0qrFz6TDr2qMkfTtwcxPc+/gudOe4mKSiOjdBXzEj/atP0+pAmamfb6Pa04PVgjCIJAgCdc4TBQGgRFFAnhcYExBbIW81mJ2e5PSThXZqeWpXSp6nLBtK8F4pxREnD/QyMzvH5MxsJ2rOHR+YFVExBnVKlmVkwWIO5T30rzyJM899JSNTOUVzHrN6LeteV+Mn/7KZak9FVBURxS5dS3N6gtWXXMDUlp/IzN5t5LVejIARg7FGOrYTjOkYH2MNRoQtmz3qPUFg9XEx4jEkcYCIYIxVEcF5T7VaZfikk0nLJaJSgjVGji8ehYqxIeVyQt/QIsltyESyjMGLr2H+8DiNnXuo79vHyEVncvq6Uznr1TV9+oHHSaSNT1uSZQX9p52FaU8z9tBdJJUSc9MToHD0+mEYYoyh0WySJAl5lhMEAUEQEIYhQTkRVWg2FljQzg1BRETA2IDx0UN4IO4ZwDlPXApPoB8T0VJSJiglLDRT5kt9euUf/g7P7Jnk6S3PEKbzEmmbQw89Ts+SxbpqzQBT9TOY2H+IKGmqz1IZWlpj37f+UdrNOd73/ut1fq7Orl27eMub38Sff/bzct1b36ztdotFixZz/bvfJZdfcSWfuOGjesONn5EP/uf36p69+0BVvvq1bzA1NU0QhsfcrIjBWkNzYYG40kez2SKKo+NHzFpLu52SLijJ4BKVnkEaYcJlV5zFeetXsu3xZ3XvyDT1w+Ns2z3PxJn9nHnBan1EDbM7d7L2rDWM/uw72jy8V0yc8NSTT3PttW9h9aqVnHbaWt5+3Vv18OhhwiDkmtdcxX3XXqevufoqarUa5XKZarXG1OQkBw8d0rHxcSnFMd57RDpFFgGvivOeoiiwxlIU7gXb/+fVsRRUEQGXtSVvLUi2/4CUxPDTO+6Tl5+zVF567iq54t+fL2943YVig0Se2nFEXrJuUE45/0zJdj9KY2JCepavppyUuPvee+W0tWsZGh6Wb/zDP8n1179Lbrn1e7Jo8SL55Gf+m7zjj94m9z/woGx5bKvUajWCMJA0y6S3tyrVai/OuQ6ozsih8/KdXCyXShLH0YnJvbUBUQBpUeDylLg5zlM//AlLFlflnJeuZsvW3axdMaB5YOTBf76bC37/lfzwscd5dt8O+iuOZzbfz5KXnEc2Pcb8nu04Y/nIRz4mR0YPc2RsnLm5OcZGR7n9u9/j4MgIt9/xA9JWiwu3ncu+Awf5269+nbA7GHJpStZuo1GEqhKEEaq+41/V470SBgHuRIA5VxBFIX39fdgwpulgYnQvN3/2SyxbvZix0Ul2RCW5/A9ey2lnLydv5vT2V5k/cpDZ7dvpW7mK+b07iDRl5UWX0pqZ4p4HH2F49SkE1X5u/u4dDC9fwUjqKS07hfFHH6Q6NMxIDq7cw8233EZYqWCsZeW6swjHj+CyjDAuMXfkcCffVFGvNBoNxIYkSXJ8YCKijWaTVppy0uLF9CUxUaBMzkwx+cQIWX2GBbXc/rm99C1bih9YQd5q0d7+IDSnqbfrpLNTXPD717LkjHU6O3JAkmqVucMHiHtqnFWuUKQpjckxyrU+0vocZ1x5FfsfvI9TLr4MG4ScdPo6lq4/Xx+9+Zuy4sKXc8olr+Dnf/0XHQo+h5JGBFXP0V7x1wIrCkjiEBHD5OQkQRTjPGT1eaLAstCaBTVo3mJy6iAmeBTBo3kT8haCI4gM4zufZmHisGiekacptSVL8X6WA489hA0jGtMTnSiMjbL/kQdJ+gaYOXSA4TWnMTc2RmP2p9K7eAnT+3fTmJokTBK8d3jncM7hvSPL2mACojg4AUsVdOxKFHWKZqvZAAxF2qJoe3ApmueYokVoBM0VQVDvOrKlShhFjG3bincFQRThC8foU4/hiwJjLUJnSHNU5Q499iBBKUGLAjEW1OOdI0zKaJHjioIgLhHGcbf96BqpPMdpQSmJTyTHvIoIqkoYhsSlEoXzLMzP4l0O3iOquKyF954winHeYYztFGDpUCWIIkRiVBUbdOpQEIZ47zHWdpW3s0Hp5o0Jw473E0sQxSiKDSNsHHeVUI99v3rf+d37F3JUL6CKQKPdJgxDKmFEmuU450nKZdqNebx3tNtNBhctpq+/nwN791Aul2k2m2RpBgKBDfDqcc4TBB2z26lFhnI5odVsElgLIlhrO2NxEcQLIIgIXro/cSDS8YR0alhR5Hh/lJIChTsREwzlchlFaDZbhFFMuZxQtGYw5HjnGBwa5oJLLtUsy2R6YoyLLroQVzgdHBoQVzh27NjJihXLGR4e0pmZWTHG4FxBtVoliiKt1xekr69PN33nVsmyrAPumHi98BReRCiKgkUnLWFyYoyiKDqUNlCciLtP04IoDKhWe2m2cyw5UTZOLZhnwXeGLCowOzMjO7c9RX2+zrannyGKI6nX64gIjWaT0dFR9u3bL81WiyzNiOOYSqVMo9kU9UoUR+LVdwuvB6STctpJPDGCYAH/ixmvCJOTk7TbbZJKlSxLUYSkVDk+sDgOUBVGj4xxck+hqweVfZMF+8adiBHCMGRhdobNP/4+VkLiWpUDBw4gIuzatRsUwjBgd1Eg3bsdBLbzEOJoXnXDYo3BWNsZ8HTgYI3pBEkBl4ENurKuiBGKtIl05d4XhZowwuFORO6NSFHnrKE2pRB5bL9nLg1QBCOCAVxQZt07/qPmfpwDN31P1q5dy/z8PO12myAM8M6TJAntVpOBwSHqC3XyLMf7zvsiQp7n9Pf3k+c5MzOzhFGE9548zxFjEISB1/6xDp+yjvmFBgp4sfj5Sca/+2VR78hdQWDsL1H5RYGtHIaBwHBw3PPwXqdRXCIMkJZ3WBvgVOkJ0c++eT23t+p889bv6+mnn8bQ8FAnoQvHzp07WbVqFaOjR6jVqgwODGCsQUToq9VoNJts3foE559/LmmaUe3txXnHjh3Pcs99DxAHoUi1H3v2bzG8qMYy8QwlEVlRsHk8xdz9Xfz0IaiAEU6s0XSIbtnTZGIup7eSiDHgFYwxXZob8jyVP/2Tj5HlIXnu+NGPfkSlUiFtpzjvQOGJJ56kKNyxPku1E62enh7GjhxBEbZufRxjLf19NbIsJ00zwiBARXDtNtObfyCPnryGkhaqaVsyG6tbmJJ84hBRFFJkbRHkxKi4/0ghOTGVsmKMEFhL/pw7IkDulGd2zwOe3t4KixYvOSbBabtNFJeIokjTtC3QyY8sy4iiiDT3DAwvprdaZaFeJ88yxEaUexPKPdBoNMiyDJEQt/V+6tufIu/tlY+//TX6/Xu2sPmBR8DTeSqBEkfBiVExjgN1maJe1eE6eqS+YzxVu82eUC6HoOC79qZUSojiiFarRbvZechZ6enVo2OAXjFUenppNOYplZIOrYuCMAxJkjKFK8iznDCM8EonBlGJ8vBSZhdaLF25mu+98y366tdfx/0PzWPEYaXQ0GRwIgXaeO+dK0A9SVzCGEuj1TqmZOp9Z7rkFe8cNgg4uH9v13mYY8p31LAe1eog6LiOo585ZmSN7ch9Z65BEISIDQkCC2kDac8R2BJ3/vRetS5jcGgYJxbVgrRdMNDnCU3hXxTY0VGjV2kZI1gjUjhHaGxHlkW6VlBJ07YkSZm+gQGdnpyQcqVCu9UmTduAYIOgE0nVjsMAmq0mcRRjw4Ai73jGIAi6TsN267Dg1Ssux7UWRBzqxw4S9AzxnTt+LLf/4MesWTYMjSmhVtF25mRkrMnyMs0Xj9imMxUgTqKxVgOfe2+zLNM8zzFisEbEq6rzXpJymfMuuFhLScLDD9yr688+G+c9fbUazhXs37+f1atPIUlKzM/PUy6XybKUxYsWMz8/T6WnwrPP7uahhx+RcpLgve+EWQS0MwQQ4xR1uKItzB7ChT20SxW9/+EnJAo67Yo1YqygQRiOAWzqYngeFW9UgFBr+1XGDgPLRFWNkaN1VlUV75xWajWMMTzx2BaajQZ79u4ljiLStI01hizLGRsfo9Vs0mh0861S4eDBkY5NCwNarY4f1aOWQo/RVsF30jprQp4qYkTadWXBSCIFWd5W1ZIiYsCMLVt7zj7uvPUYhl85DrFx40a7adMmt/aM87+RthpvUdXCWhs4r6TtNl49adoizzIBxXtPFMfabrelM7d0GBHCKOrYHYUgCPDe84tZokE61NYwDKUz0+iWEmM6Lr/rSEREEYOI6Rx5UBVQdUUulZ5qDhJUemu37n7m4d+FjRY2uRcUj03dRItK8Rddnr01z1IpnOv6uF+0DF69vurq17Jn10727t7FWWefrXOzsxhjyPNC2u2WVqvLAMW5jmI6V6AKpVLM7NwcgQ1wrtBKpUcKV6gRQ57nBIGl0WhSX1joHuEAlQ5BtaM4eO80zzPKlV7pKVe+0AnKL/b/Aqq4ycFG+/TWTQ+fcur6z4sxHyzyPDPGBurVeI4qokdENEkqIiLEccyq1asZHh7Woij0mW3bZO3aUzUMQ3qrVQ4eOEhvbw9Z3mkaFVi5YgUiQpqlao3tWKyBfur1OgD33H2fWGtRURDfaWY76uVdURTlck8Ul5LPPb7l5/dBh2nHO5kjsNHAJrdy1Zl/61zxzsIVeO+LLG2LsUaKoiBL02O00q7rN8YQBJaicJLnuQaBlVIp0Xq9LkaMHu1AgiDAuc7Ncd1erNuaiDGGOI41L3IxHYpq1zh3xx0aJOUeagPDf7Pzyc3vht+zsMnzvHMe8mvOWAngV64++wNFkX4c6EvbbYoi/0UX2z1FQ4eenQ94PTaPFwSveswZdGqcAbT74MMfayDRo1Tv1Eo5auGO+v5uUxpG8UxPte/Tu7dv+UJ3LqrPB3W8s1THwK0564LleaP5h17dla5wq1yRlwrvjKqCYrontUQAr17oektjrDxn2ol2AdHtjtX7Y+8fFZfngun+cKguGGv3hHH8k0rU90+7dj00cvSs17/29NvRacEx/m7YsCFI00Vhnrckz1Mpikycy6U7kxQGuoV+0p3Id0P/r39rIY79hrPPbn/lK1/Jn6Pfv6SA/zfLsGFD8EIj8f+Hy8CJ70H+FReQ/0/AlN+s36zfrH8z638DNlpxm6DohBEAAAAASUVORK5CYII=
// @run-at       document-start
// @license      MIT
// @namespace https://greatest.deepsurf.us/users/1561762
// ==/UserScript==

(() => {
  "use strict";

  const UW = typeof unsafeWindow !== "undefined" ? unsafeWindow : window;
  const SCRIPT_ID = "wme-rightclick-functions";
  const SCRIPT_NAME = "WME - RightClick Functions";
  const SCRIPT_VERSION = "1.0.5";
  const CHANGELOG_SEEN_KEY = `${SCRIPT_ID}:changelogSeenVersion:v2`;
  const CHANGELOG_ITEMS = [
    "New: Compatible with the new WME SDK"
  ];


  const EDITOR_BASE = `${location.origin}${location.pathname}`;
  const GMAPS_BASE = "https://www.google.com/maps?q=";

  if (UW.__WME_RIGHTCLICK_FUNCTIONS__) return;
  UW.__WME_RIGHTCLICK_FUNCTIONS__ = true;

  let sdk = null;
  let enabled = true;
  let lastLonLat = null;

  const EXT_MENU_CONTEXTS = Object.freeze(["map", "segment", "place"]);
  const extMenuProviders = { map: new Map(), segment: new Map(), place: new Map() };

  function normalizeExtMenuContextType(type) {
    const k = String(type || "").trim().toLowerCase();
    return EXT_MENU_CONTEXTS.includes(k) ? k : "";
  }

  function cloneLonLat(ll) {
    if (!ll || !Number.isFinite(ll.lat) || !Number.isFinite(ll.lon)) return null;
    return { lat: Number(ll.lat), lon: Number(ll.lon) };
  }

  function cloneNumArray(arr) {
    return Array.isArray(arr) ? arr.map(v => Number(v)).filter(Number.isFinite) : [];
  }

  function buildExtMenuContext(type, spec = {}) {
    const ctxType = normalizeExtMenuContextType(type);
    const lonLat = cloneLonLat(spec.lonLat || spec.ll || lastLonLat);
    const segmentIds = cloneNumArray(spec.segmentIds);
    const placeIds = cloneNumArray(spec.placeIds);
    const out = {
      type: ctxType || String(type || ""),
      lonLat,
      segmentIds,
      placeIds,
      sdk: sdk || null,
      lastLonLat: cloneLonLat(lastLonLat),
      selection: {
        segmentIds,
        placeIds,
      },
    };
    if (Number.isFinite(spec.clientX)) out.clientX = Number(spec.clientX);
    if (Number.isFinite(spec.clientY)) out.clientY = Number(spec.clientY);
    if (spec.placeInfo && typeof spec.placeInfo === "object") {
      out.placeInfo = {
        has: !!spec.placeInfo.has,
        ids: cloneNumArray(spec.placeInfo.ids),
      };
    }
    return Object.freeze(out);
  }

  function sanitizeExtMenuItems(items, providerId) {
    if (!Array.isArray(items)) return [];
    const out = [];
    for (const item of items) {
      if (!item || typeof item !== "object") continue;
      if (item.type === "sep") {
        out.push({ type: "sep" });
        continue;
      }
      if (typeof item.label !== "string" || !item.label.trim()) continue;

      const clean = {
        label: item.label,
      };

      if (item.sub != null) clean.sub = String(item.sub);
      if (item.disabled != null) clean.disabled = !!item.disabled;
      if (item.submenu != null) clean.submenu = !!item.submenu;
      if (item.submenuKind != null) clean.submenuKind = String(item.submenuKind);
      if (item.submenuHeaderLeft != null) clean.submenuHeaderLeft = String(item.submenuHeaderLeft);
      if (item.submenuHeaderRight != null) clean.submenuHeaderRight = String(item.submenuHeaderRight);
      if (typeof item.onClick === "function") clean.onClick = item.onClick;
      if (typeof item.getSubmenuItems === "function") clean.getSubmenuItems = item.getSubmenuItems;
      if (item.rightButton && typeof item.rightButton === "object" && typeof item.rightButton.onClick === "function") {
        clean.rightButton = {
          label: String(item.rightButton.label || ""),
          onClick: item.rightButton.onClick,
        };
      }
      if (item.iconButton && typeof item.iconButton === "object" && typeof item.iconButton.onClick === "function") {
        clean.iconButton = {
          html: String(item.iconButton.html || ""),
          title: String(item.iconButton.title || ""),
          onClick: item.iconButton.onClick,
        };
      }
      clean._providerId = providerId;
      out.push(clean);
    }
    return out;
  }

  function getExternalMenuItems(type, ctx) {
    const ctxType = normalizeExtMenuContextType(type);
    if (!ctxType) return [];
    const bucket = extMenuProviders[ctxType];
    if (!bucket || !bucket.size) return [];
    const out = [];
    for (const [providerId, providerFn] of bucket.entries()) {
      try {
        if (!isExtensionEnabled(providerId)) continue;
        const items = providerFn(ctx);
        const cleanItems = sanitizeExtMenuItems(items, providerId);
        if (!cleanItems.length) continue;
        if (out.length && out[out.length - 1]?.type !== "sep") out.push({ type: "sep" });
        out.push(...cleanItems);
      } catch (e) {
        console.error(`[${SCRIPT_NAME}] external provider failed: ${providerId}`, e);
      }
    }
    while (out.length && out[0]?.type === "sep") out.shift();
    while (out.length && out[out.length - 1]?.type === "sep") out.pop();
    return out;
  }

  function buildExtensionsMenuItem(type, ctx) {
    const extItems = getExternalMenuItems(type, ctx);
    if (!extItems.length) return null;
    return {
      label: withIcon(ICONS.more, "Extensions"),
      sub: `${listMenuProviders(type).length} provider${listMenuProviders(type).length === 1 ? "" : "s"}`,
      submenu: true,
      submenuKind: "extensions",
      submenuHeaderLeft: "Extensions",
      submenuHeaderRight: "Third-party",
      getSubmenuItems: () => getExternalMenuItems(type, ctx),
    };
  }

  function registerMenuProvider(type, providerId, providerFn) {
    const ctxType = normalizeExtMenuContextType(type);
    if (!ctxType) throw new Error(`Unsupported context type: ${type}`);
    const id = String(providerId || "").trim();
    if (!id) throw new Error("providerId is required");
    if (typeof providerFn !== "function") throw new Error("providerFn must be a function");
    extMenuProviders[ctxType].set(id, providerFn);
    try { refreshSidebarTabContent(); } catch {}
    return id;
  }

  function unregisterMenuProvider(type, providerId) {
    const ctxType = normalizeExtMenuContextType(type);
    if (!ctxType) return false;
    const ok = extMenuProviders[ctxType].delete(String(providerId || "").trim());
    try { refreshSidebarTabContent(); } catch {}
    return ok;
  }

  function listMenuProviders(type) {
    const ctxType = normalizeExtMenuContextType(type);
    if (!ctxType) return [];
    return Array.from(extMenuProviders[ctxType].keys());
  }


  const NATIVE_MENU_SETTINGS_KEY = `${SCRIPT_ID}:nativeMenuSettings:v1`;
  const EXTENSION_SETTINGS_KEY = `${SCRIPT_ID}:extensionSettings:v1`;

  function loadExtensionSettings() {
    try {
      const raw = localStorage.getItem(EXTENSION_SETTINGS_KEY);
      const parsed = raw ? JSON.parse(raw) : null;
      const disabled = parsed && typeof parsed === "object" && parsed.disabled && typeof parsed.disabled === "object" ? parsed.disabled : {};
      return { disabled: { ...disabled } };
    } catch {
      return { disabled: {} };
    }
  }

  function saveExtensionSettings(cfg) {
    try { localStorage.setItem(EXTENSION_SETTINGS_KEY, JSON.stringify({ disabled: { ...(cfg?.disabled || {}) } })); } catch {}
  }

  function isExtensionEnabled(providerId) {
    const id = String(providerId || "").trim();
    if (!id) return true;
    const cfg = loadExtensionSettings();
    return cfg.disabled[id] !== true;
  }

  function setExtensionEnabled(providerId, enabled) {
    const id = String(providerId || "").trim();
    if (!id) return;
    const cfg = loadExtensionSettings();
    cfg.disabled[id] = !enabled;
    if (cfg.disabled[id] !== true) delete cfg.disabled[id];
    saveExtensionSettings(cfg);
  }

  function getAllRegisteredExtensionProviders() {
    const ids = new Set();
    for (const t of Object.keys(extMenuProviders || {})) {
      const bucket = extMenuProviders[t];
      if (!bucket || !bucket.size) continue;
      for (const id of bucket.keys()) ids.add(String(id || "").trim());
    }
    return Array.from(ids).filter(Boolean).sort((a, b) => a.localeCompare(b));
  }

  function getExtensionContextsForProvider(providerId) {
    const id = String(providerId || "").trim();
    if (!id) return [];
    const out = [];
    for (const t of ["segment", "map", "place"]) {
      if (extMenuProviders?.[t]?.has(id)) out.push(t);
    }
    return out;
  }

  function refreshSidebarTabContent() {
    try {
      const pane = document.querySelector('.wmeRcSideWrap');
      if (pane && pane._wmeRcRefresh && typeof pane._wmeRcRefresh === 'function') pane._wmeRcRefresh();
    } catch {}
  }

  function getNativeMenuDefaults(type) {
    const t = normalizeExtMenuContextType(type);
    if (t === "segment") {
      return [
        { id: "copyCoords", label: "Copy coordinates" },
        { id: "copyPermalink", label: "Copy permalink" },
        { id: "refreshHere", label: "Refresh here" },
        { id: "pinPlace", label: "Pin this Place" },
        { id: "endpoints", label: "Endpoints" },
        { id: "jumpSearch", label: "Jump / Search…" },
        { id: "moreTools", label: "More" },
      ];
    }
    if (t === "map") {
      return [
        { id: "copyCoords", label: "Copy coordinates" },
        { id: "pinPlace", label: "Pin this Place" },
        { id: "refreshHere", label: "Refresh here" },
        { id: "copyPermalink", label: "Copy permalink" },
        { id: "openGoogleMaps", label: "Open in Google Maps" },
        { id: "jumpSearch", label: "Jump / Search…" },
      ];
    }
    if (t === "place") {
      return [
        { id: "copyCoords", label: "Copy coordinates" },
        { id: "pinPlace", label: "Pin this Place" },
        { id: "refreshHere", label: "Refresh here" },
        { id: "copyPermalink", label: "Copy permalink" },
        { id: "openGoogleMaps", label: "Open in Google Maps" },
        { id: "jumpSearch", label: "Jump / Search…" },
      ];
    }
    return [];
  }

  function normalizeNativeMenuSettings(type, cfg) {
    const defs = getNativeMenuDefaults(type);
    const validIds = new Set(defs.map(d => d.id));
    const order = [];
    for (const id of Array.isArray(cfg?.order) ? cfg.order : []) {
      const sid = String(id || "").trim();
      if (validIds.has(sid) && !order.includes(sid)) order.push(sid);
    }
    for (const def of defs) {
      if (!order.includes(def.id)) order.push(def.id);
    }
    const hidden = {};
    const rawHidden = cfg && typeof cfg.hidden === "object" ? cfg.hidden : {};
    for (const def of defs) hidden[def.id] = rawHidden[def.id] === true;
    return { order, hidden };
  }

  function loadAllNativeMenuSettings() {
    try {
      const raw = localStorage.getItem(NATIVE_MENU_SETTINGS_KEY);
      const parsed = JSON.parse(raw || "{}") || {};
      return {
        map: normalizeNativeMenuSettings("map", parsed.map),
        segment: normalizeNativeMenuSettings("segment", parsed.segment),
        place: normalizeNativeMenuSettings("place", parsed.place),
      };
    } catch {
      return {
        map: normalizeNativeMenuSettings("map", null),
        segment: normalizeNativeMenuSettings("segment", null),
        place: normalizeNativeMenuSettings("place", null),
      };
    }
  }

  function saveAllNativeMenuSettings(all) {
    const next = {
      map: normalizeNativeMenuSettings("map", all?.map),
      segment: normalizeNativeMenuSettings("segment", all?.segment),
      place: normalizeNativeMenuSettings("place", all?.place),
    };
    try { localStorage.setItem(NATIVE_MENU_SETTINGS_KEY, JSON.stringify(next)); } catch {}
    return next;
  }

  function getNativeMenuSettings(type) {
    const t = normalizeExtMenuContextType(type);
    return loadAllNativeMenuSettings()[t] || normalizeNativeMenuSettings(t, null);
  }

  function makeNativeMenuItem(id, label, item) {
    return { ...(item || {}), _nativeId: String(id || "").trim(), _nativeLabel: String(label || item?.label || id || "") };
  }

  function applyNativeMenuSettings(type, items) {
    const defs = getNativeMenuDefaults(type);
    if (!defs.length) return Array.isArray(items) ? items.slice() : [];
    const cfg = getNativeMenuSettings(type);
    const byId = new Map();
    for (const item of (Array.isArray(items) ? items : [])) {
      if (item && item._nativeId) byId.set(item._nativeId, item);
    }
    const ordered = [];
    for (const id of cfg.order) {
      if (cfg.hidden[id] === true) continue;
      const item = byId.get(id);
      if (item) ordered.push(item);
    }
    const out = [];
    for (const item of ordered) {
      if (!item) continue;
      const clone = { ...item };
      delete clone._nativeId;
      delete clone._nativeLabel;
      out.push(clone);
    }
    return out;
  }

  function buildNativeMenuSettingsEditor() {
    const wrap = document.createElement("div");
    wrap.className = "wmeRcSideCard";
    try { wrap.style.display = "grid"; wrap.style.gap = "10px"; } catch {}

    function parseColorToRgb(str) {
      const s = String(str || "").trim();
      let m = s.match(/^rgba?\(([^)]+)\)$/i);
      if (m) {
        const parts = m[1].split(',').map(v => parseFloat(v.trim()));
        if (parts.length >= 3) return { r: parts[0] || 0, g: parts[1] || 0, b: parts[2] || 0, a: parts.length >= 4 ? parts[3] : 1 };
      }
      m = s.match(/^#([0-9a-f]{3,8})$/i);
      if (m) {
        const hex = m[1];
        if (hex.length === 3) return { r: parseInt(hex[0] + hex[0], 16), g: parseInt(hex[1] + hex[1], 16), b: parseInt(hex[2] + hex[2], 16), a: 1 };
        if (hex.length >= 6) return { r: parseInt(hex.slice(0,2), 16), g: parseInt(hex.slice(2,4), 16), b: parseInt(hex.slice(4,6), 16), a: 1 };
      }
      return null;
    }

    function getAdaptiveTextColor(el) {
      let cur = el;
      while (cur) {
        try {
          const cs = getComputedStyle(cur);
          const bg = parseColorToRgb(cs.backgroundColor);
          if (bg && bg.a !== 0) {
            const lum = (0.2126 * bg.r + 0.7152 * bg.g + 0.0722 * bg.b) / 255;
            return lum > 0.58 ? '#1d2329' : '#f3f7fb';
          }
        } catch {}
        cur = cur.parentElement;
      }
      return '#f3f7fb';
    }

    const fg = getAdaptiveTextColor(wrap.parentElement || document.body);
    try { wrap.style.setProperty('--wmeRcSideFg', fg); wrap.style.color = fg; } catch {}

    const head = document.createElement("div");
    head.innerHTML = `
      <div class="wmeRcSideTitle">Native context menu actions</div>
      <div class="wmeRcSideSub">Hide actions or drag them into a different order by context.</div>
    `;
    wrap.appendChild(head);

    const sections = document.createElement("div");
    try { sections.style.display = "grid"; sections.style.gap = "10px"; } catch {}
    wrap.appendChild(sections);

    const state = loadAllNativeMenuSettings();
    const groups = [
      { id: "segment", label: "Segment" },
      { id: "map", label: "Map" },
      { id: "place", label: "Place" },
    ];

    function saveGroup(groupId, cfg) {
      state[groupId] = normalizeNativeMenuSettings(groupId, cfg);
      saveAllNativeMenuSettings(state);
    }

    function mountSortable(list, groupId, cfg, rerender) {
      let drag = null;
      const rows = () => Array.from(list.querySelectorAll('[data-native-id]'));

      function onMove(ev) {
        if (!drag) return;
        drag.row.style.top = `${Math.round(ev.clientY - drag.offsetY)}px`;
        const others = rows().filter(r => r !== drag.row);
        let placed = false;
        for (const row of others) {
          const rect = row.getBoundingClientRect();
          const midpoint = rect.top + (rect.height / 2);
          if (ev.clientY < midpoint) {
            if (drag.placeholder !== row.previousSibling) list.insertBefore(drag.placeholder, row);
            placed = true;
            break;
          }
        }
        if (!placed) list.appendChild(drag.placeholder);
      }

      function onUp() {
        if (!drag) return;
        try { document.removeEventListener('pointermove', onMove, true); } catch {}
        try { document.removeEventListener('pointerup', onUp, true); } catch {}
        try { document.removeEventListener('pointercancel', onUp, true); } catch {}
        try { document.body.style.userSelect = ''; } catch {}

        const otherIds = rows().filter(r => r !== drag.row).map(r => r.dataset.nativeId);
        const slot = Math.max(0, Array.from(list.children).indexOf(drag.placeholder));
        otherIds.splice(Math.min(slot, otherIds.length), 0, drag.row.dataset.nativeId);
        cfg.order = otherIds;
        saveGroup(groupId, cfg);

        drag.row.style.position = '';
        drag.row.style.left = '';
        drag.row.style.top = '';
        drag.row.style.width = '';
        drag.row.style.height = '';
        drag.row.style.zIndex = '';
        drag.row.style.pointerEvents = '';
        drag.row.style.boxShadow = '';
        drag.row.style.opacity = '';
        drag.row.style.transform = '';
        list.insertBefore(drag.row, drag.placeholder);
        if (drag.placeholder.parentNode) drag.placeholder.parentNode.removeChild(drag.placeholder);
        drag = null;
        rerender();
      }

      rows().forEach(row => {
        const handle = row.querySelector('.wmeRcSideHandle');
        if (!handle) return;
        handle.addEventListener('pointerdown', (ev) => {
          if (ev.button != null && ev.button !== 0) return;
          ev.preventDefault();
          ev.stopPropagation();
          const rect = row.getBoundingClientRect();
          const placeholder = document.createElement('div');
          placeholder.className = 'wmeRcNativePlaceholder';
          placeholder.style.height = `${Math.round(rect.height)}px`;
          placeholder.style.borderRadius = '10px';
          placeholder.style.border = '1px dashed rgba(127,127,127,.35)';
          placeholder.style.background = 'transparent';
          list.insertBefore(placeholder, row.nextSibling);

          drag = { row, placeholder, offsetY: ev.clientY - rect.top };
          row.style.position = 'fixed';
          row.style.left = `${Math.round(rect.left)}px`;
          row.style.top = `${Math.round(rect.top)}px`;
          row.style.width = `${Math.round(rect.width)}px`;
          row.style.height = `${Math.round(rect.height)}px`;
          row.style.zIndex = '999999';
          row.style.pointerEvents = 'none';
          row.style.boxShadow = '0 10px 24px rgba(0,0,0,.22)';
          row.style.opacity = '1';
          row.style.transform = 'none';
          try { document.body.style.userSelect = 'none'; } catch {}

          document.addEventListener('pointermove', onMove, true);
          document.addEventListener('pointerup', onUp, true);
          document.addEventListener('pointercancel', onUp, true);
        });
      });
    }

    function render() {
      sections.innerHTML = "";
      try { wrap.style.setProperty('--wmeRcSideFg', getAdaptiveTextColor(wrap.parentElement || document.body)); } catch {}
      for (const group of groups) {
        const cfg = state[group.id] || normalizeNativeMenuSettings(group.id, null);
        const defs = getNativeMenuDefaults(group.id);
        const orderedDefs = cfg.order.map(id => defs.find(d => d.id === id)).filter(Boolean);

        const card = document.createElement("div");
        try {
          card.style.display = "grid";
          card.style.gap = "8px";
          card.style.padding = "10px";
          card.style.border = "1px solid rgba(127,127,127,.18)";
          card.style.borderRadius = "12px";
          card.style.background = "transparent";
        } catch {}

        const top = document.createElement("div");
        try {
          top.style.display = "flex";
          top.style.alignItems = "center";
          top.style.justifyContent = "space-between";
          top.style.gap = "10px";
        } catch {}

        const titleWrap = document.createElement("div");
        titleWrap.innerHTML = `<div class="wmeRcSideTitle" style="font-size:13px">${group.label}</div>`;

        const reset = document.createElement("button");
        reset.type = "button";
        reset.className = "wmeRcMiniBtn";
        reset.textContent = "Reset";
        reset.addEventListener("click", () => {
          state[group.id] = normalizeNativeMenuSettings(group.id, null);
          saveAllNativeMenuSettings(state);
          render();
        });

        top.appendChild(titleWrap);
        top.appendChild(reset);
        card.appendChild(top);

        const list = document.createElement("div");
        try { list.style.display = "grid"; list.style.gap = "8px"; } catch {}

        orderedDefs.forEach((def) => {
          const row = document.createElement("div");
          row.dataset.nativeId = def.id;
          try {
            row.style.display = "grid";
            row.style.gridTemplateColumns = "1fr auto";
            row.style.gap = "10px";
            row.style.alignItems = "center";
            row.style.padding = "8px 10px";
            row.style.border = "1px solid rgba(127,127,127,.18)";
            row.style.borderRadius = "10px";
            row.style.background = "transparent";
            row.style.transition = "none";
          } catch {}

          const left = document.createElement("label");
          try { left.style.display = "flex"; left.style.alignItems = "center"; left.style.gap = "10px"; left.style.cursor = "pointer"; left.style.minWidth = '0'; } catch {}

          const chk = document.createElement("input");
          chk.type = "checkbox";
          chk.checked = cfg.hidden[def.id] !== true;
          chk.addEventListener("change", () => {
            cfg.hidden[def.id] = !chk.checked;
            state[group.id] = normalizeNativeMenuSettings(group.id, cfg);
            saveAllNativeMenuSettings(state);
          });

          const txt = document.createElement("div");
          txt.className = 'wmeRcHint';
          txt.textContent = def.label;
          try { txt.style.fontWeight = '600'; txt.style.color = 'var(--wmeRcSideFg, inherit)'; txt.style.minWidth = '0'; } catch {}

          left.appendChild(chk);
          left.appendChild(txt);

          const handle = document.createElement('button');
          handle.type = 'button';
          handle.className = 'wmeRcSideHandle';
          handle.textContent = '≡';
          handle.title = 'Drag to reorder';
          try {
            handle.style.border = '0';
            handle.style.background = 'transparent';
            handle.style.color = 'var(--wmeRcSideFg, inherit)';
            handle.style.cursor = 'grab';
            handle.style.fontSize = '18px';
            handle.style.lineHeight = '1';
            handle.style.padding = '0 2px';
            handle.style.opacity = '.8';
            handle.style.touchAction = 'none';
          } catch {}

          row.appendChild(left);
          row.appendChild(handle);
          list.appendChild(row);
        });

        card.appendChild(list);
        sections.appendChild(card);
        mountSortable(list, group.id, cfg, render);
      }
    }

    render();
    return wrap;
  }

  function buildExtensionsSettingsEditor() {
    const wrap = document.createElement("div");
    wrap.className = "wmeRcSideCard";

    const head = document.createElement("div");
    head.innerHTML = `
      <div class="wmeRcSideTitle">Extensions</div>
    `;
    wrap.appendChild(head);

    const note = document.createElement("div");
    note.className = "wmeRcHint";
    note.textContent = "Coming Soon";
    try { note.style.marginTop = "8px"; note.style.color = 'var(--wmeRcSideFg, inherit)'; } catch {}
    wrap.appendChild(note);

    return wrap;
  }

  function buildRcSidebarPanel() {
    const wrap = document.createElement("div");
    wrap.className = "wmeRcSideWrap";

    function parseColorToRgb(s) {
      if (!s || typeof s !== 'string') return null;
      let m = s.match(/^rgba?\(([^)]+)\)$/i);
      if (m) {
        const parts = m[1].split(',').map(v => parseFloat(v.trim()));
        if (parts.length >= 3) return { r: parts[0] || 0, g: parts[1] || 0, b: parts[2] || 0, a: parts.length >= 4 ? parts[3] : 1 };
      }
      m = s.match(/^#([0-9a-f]{3,8})$/i);
      if (m) {
        const hex = m[1];
        if (hex.length === 3) return { r: parseInt(hex[0] + hex[0], 16), g: parseInt(hex[1] + hex[1], 16), b: parseInt(hex[2] + hex[2], 16), a: 1 };
        if (hex.length >= 6) return { r: parseInt(hex.slice(0,2), 16), g: parseInt(hex.slice(2,4), 16), b: parseInt(hex.slice(4,6), 16), a: 1 };
      }
      return null;
    }

    function getAdaptiveTextColor(el) {
      let cur = el;
      while (cur) {
        try {
          const cs = getComputedStyle(cur);
          const bg = parseColorToRgb(cs.backgroundColor);
          if (bg && bg.a !== 0) {
            const lum = (0.2126 * bg.r + 0.7152 * bg.g + 0.0722 * bg.b) / 255;
            return lum > 0.58 ? '#1d2329' : '#f3f7fb';
          }
        } catch {}
        cur = cur.parentElement;
      }
      return '#f3f7fb';
    }

    function applyAdaptiveTextColor() {
      const fg = getAdaptiveTextColor(wrap.parentElement || document.body);
      try { wrap.style.setProperty('--wmeRcSideFg', fg); wrap.style.color = fg; } catch {}
    }

    const mainCard = document.createElement("div");
    mainCard.className = "wmeRcSideCard";
    mainCard.innerHTML = `
      <div class="wmeRcSideRow">
        <div>
          <div class="wmeRcSideTitle">Right-click menu</div>
          <div class="wmeRcSideSub">Shift + right click = default menu</div>
        </div>
        <div class="wmeRcSwitch ${enabled ? "on" : ""}" id="wmeRcSideSwitch">
          <div class="wmeRcKnob"></div>
        </div>
      </div>
    `;
    wrap.appendChild(mainCard);

    const nav = document.createElement('div');
    nav.className = 'wmeRcSideNav';
    nav.innerHTML = `
      <button type="button" class="wmeRcSideNavBtn is-active" data-tab="native">Native actions</button>
      <button type="button" class="wmeRcSideNavBtn" data-tab="extensions">Extensions</button>
    `;
    wrap.appendChild(nav);

    const content = document.createElement('div');
    content.className = 'wmeRcSideTabHost';
    wrap.appendChild(content);

    function renderTab(tab) {
      applyAdaptiveTextColor();
      content.innerHTML = '';
      nav.querySelectorAll('.wmeRcSideNavBtn').forEach(btn => btn.classList.toggle('is-active', btn.dataset.tab === tab));
      content.appendChild(tab === 'extensions' ? buildExtensionsSettingsEditor() : buildNativeMenuSettingsEditor());
      applyAdaptiveTextColor();
    }

    nav.addEventListener('click', (e) => {
      const btn = e.target && e.target.closest ? e.target.closest('.wmeRcSideNavBtn') : null;
      if (!btn) return;
      renderTab(btn.dataset.tab || 'native');
    });

    const sw = mainCard.querySelector('#wmeRcSideSwitch');
    if (sw) {
      sw.addEventListener('click', () => {
        setEnabled(!enabled);
        sw.classList.toggle('on', enabled);
        toast(`${SCRIPT_NAME}: ${enabled ? "ON" : "OFF"}`);
        closeAllMenus();
      });
    }

    wrap._wmeRcRefresh = () => {
      const active = nav.querySelector('.wmeRcSideNavBtn.is-active')?.dataset.tab || 'native';
      renderTab(active);
    };

    renderTab('native');
    return wrap;
  }

  function getCurrentContextSnapshot() {
    return buildExtMenuContext(menuState?.rootType || "", {
      lonLat: menuState?.rootLL || lastLonLat,
      segmentIds: selectedSegmentIds(),
      placeIds: (function() {
        try { return selectedPlaceInfo()?.ids || []; } catch { return []; }
      })(),
    });
  }


  let cachedUserLevel = null;
  let rankIsZeroBased = null;
  let lockIsZeroBased = null;

  let attrClip = null;

  const PIN_KEY = `${SCRIPT_ID}:pinnedPlaces:v1`;
  const PIN_POS_KEY = `${SCRIPT_ID}:pinnedPlacesPos:v1`;

  const PIN_PANEL_SIZE_KEY = `${SCRIPT_ID}:pinsPanelSize:v1`;
  const PIN_PANEL_COLLAPSED_KEY = `${SCRIPT_ID}:pinsPanelCollapsed:v1`;
  const PIN_PANEL_ALWAYS_VISIBLE_EMPTY_KEY = `${SCRIPT_ID}:pinsPanelAlwaysVisibleEmpty:v1`;
  const PIN_BUBBLE_POS_KEY = `${SCRIPT_ID}:pinsBubblePos:v1`;
  const PIN_MINIMIZE_MODE_KEY = `${SCRIPT_ID}:pinsMinimizeMode:v1`;
  const PIN_PANEL_MINIMIZED_KEY = `${SCRIPT_ID}:pinsPanelMinimized:v1`;


  const PIN_NAME_MAX = 28;
  function validatePinName(name, fallback) {
    const s = String(name ?? "").trim();
    const val = s || String(fallback ?? "").trim() || "Pinned place";
    if (!val) return { ok: false, value: "", len: 0, msg: "Pin name can't be empty." };
    if (val.length > PIN_NAME_MAX) {
      return { ok: false, value: val, len: val.length, msg: `Pin name is ${val.length} chars. Max is ${PIN_NAME_MAX}. Rename it.` };
    }
    return { ok: true, value: val, len: val.length, msg: "" };
  }




  let _pinsMem = null;
  let _pinsSaveTimer = null;
  let _pinsLastJson = null;
  const _DEBOUNCE_SAVE_MS = 250;




  function _createAdaptiveLoop(fn, baseMs, opts = {}) {
    let stopped = false;
    let t = null;

    const active = typeof opts.active === "function" ? opts.active : () => true;
    const immediateOnVisible = opts.immediateOnVisible !== false;

    function delay() {

      if (document.hidden) return Math.max(baseMs * 4, 1200);
      return baseMs;
    }

    function tick() {
      if (stopped) return;
      try {
        if (active()) fn();
      } catch {}
      t = setTimeout(tick, delay());
    }


    t = setTimeout(tick, baseMs);


    function onVis() {
      if (stopped) return;
      if (!document.hidden && immediateOnVisible) {
        try { if (active()) fn(); } catch {}
      }
    }
    try { document.addEventListener("visibilitychange", onVis, { passive: true }); } catch {}

    return () => {
      stopped = true;
      try { if (t) clearTimeout(t); } catch {}
      try { document.removeEventListener("visibilitychange", onVis); } catch {}
    };
  }


  function _schedulePinsSaveNow(pins) {
    try {
      if (_pinsSaveTimer) clearTimeout(_pinsSaveTimer);
    } catch {}
    _pinsSaveTimer = setTimeout(() => {
      _pinsSaveTimer = null;
      try {
        const json = JSON.stringify(pins || []);
        if (json !== _pinsLastJson) {
          localStorage.setItem(PIN_KEY, json);
          _pinsLastJson = json;
        }
      } catch {}
    }, _DEBOUNCE_SAVE_MS);
  }

  function _flushPinsSave() {
    try {
      if (_pinsSaveTimer) {
        clearTimeout(_pinsSaveTimer);
        _pinsSaveTimer = null;
      }
    } catch {}
    try {
      const pins = _pinsMem;
      if (!pins) return;
      const json = JSON.stringify(pins || []);
      if (json !== _pinsLastJson) {
        localStorage.setItem(PIN_KEY, json);
        _pinsLastJson = json;
      }
    } catch {}
  }

  let pinsNoClusterUntil = 0;

  const PIN_COLOR_PRESETS = ["#ff8a00","#ff3b30","#007aff","#34c759","#ffd60a","#af52de"];

const PIN_CUSTOM_COLORS_KEY = `${SCRIPT_ID}:pinCustomColors:v1`;
function loadCustomPinColors() {
  try {
    const raw = localStorage.getItem(PIN_CUSTOM_COLORS_KEY);
    const arr = JSON.parse(raw || "[]");
    const out = Array.isArray(arr) ? arr : [];
    return out
      .map(c => normalizePinColor(c))
      .filter(Boolean)
      .slice(0, 4);
  } catch { return []; }
}
function saveCustomPinColors(colors) {
  try {
    const arr = Array.isArray(colors) ? colors : [];
    const norm = arr.map(c => normalizePinColor(c)).filter(Boolean).slice(0, 4);
    localStorage.setItem(PIN_CUSTOM_COLORS_KEY, JSON.stringify(norm));
  } catch {}
}
  function pickRandomPinColor() {
    try { return PIN_COLOR_PRESETS[Math.floor(Math.random() * PIN_COLOR_PRESETS.length)] || "#ff8a00"; }
    catch { return "#ff8a00"; }
  }

  const PIN_GROUPS_KEY = `${SCRIPT_ID}:pinGroups:v1`;
  const PIN_GROUP_FILTER_KEY = `${SCRIPT_ID}:pinGroupFilter:v1`;
  const PIN_LAST_GROUP_KEY = `${SCRIPT_ID}:pinLastGroup:v1`;

  const PIN_GROUP_UI_KEY = `${SCRIPT_ID}:pinGroupUi:v1`;

  function loadPinGroupUi() {
    try {
      if (loadPinGroupUi._cache) return loadPinGroupUi._cache;
      const raw = localStorage.getItem(PIN_GROUP_UI_KEY);
      const obj = JSON.parse(raw || "{}");
      const out = (obj && typeof obj === "object") ? obj : {};
      loadPinGroupUi._cache = out;
      return out;
    } catch {
      loadPinGroupUi._cache = {};
      return {};
    }
  }
  loadPinGroupUi._cache = null;

  function savePinGroupUi(ui) {
    try {
      const out = (ui && typeof ui === "object") ? ui : {};
      loadPinGroupUi._cache = out;


      if (savePinGroupUi._t) clearTimeout(savePinGroupUi._t);
      savePinGroupUi._t = setTimeout(() => {
        try { localStorage.setItem(PIN_GROUP_UI_KEY, JSON.stringify(loadPinGroupUi._cache || {})); } catch {}
        savePinGroupUi._t = null;
      }, 120);
    } catch {}
  }
  savePinGroupUi._t = null;

  function isGroupCollapsed(gid) {
    try {
      const ui = loadPinGroupUi();
      return !!(ui && ui[String(gid)] && ui[String(gid)].collapsed);
    } catch { return false; }
  }
  function setGroupCollapsed(gid, collapsed) {
    try {
      const id = String(gid);
      const ui = loadPinGroupUi();
      ui[id] = { ...(ui[id] || {}), collapsed: !!collapsed };
      savePinGroupUi(ui);
    } catch {}
  }

  function _uid() {
    return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 7)}`;
  }

  function loadPinGroups() {
    try {
      const raw = localStorage.getItem(PIN_GROUPS_KEY);
      const arr = JSON.parse(raw || "[]");
      const groups = Array.isArray(arr) ? arr : [];
      if (!groups.some(g => g && g.id === "default")) {
        groups.unshift({ id: "default", name: "(no folder)", emoji: "" });
      }
      try {
        const def = groups.find(g => g && g.id === "default");
        if (def && (String(def.name || "").trim() === "Default" || !String(def.name || "").trim())) def.name = "(no folder)";
      } catch {}
      return groups
        .filter(g => g && typeof g.id === "string" && typeof g.name === "string" && g.id.trim())
        .map(g => ({ id: String(g.id), name: String(g.name).trim() || "Group", emoji: (typeof g.emoji === "string") ? String(g.emoji) : "" }));
    } catch {
      return [{ id: "default", name: "(no folder)", emoji: "" }];
    }
  }

  function savePinGroups(groups) {
    try { localStorage.setItem(PIN_GROUPS_KEY, JSON.stringify(groups || [])); } catch {}
  }

  function getGroupName(id) {
    const gid = String(id || "default");
    const groups = loadPinGroups();
    const g = groups.find(x => x && x.id === gid);
    return g ? g.name : (gid === "default" ? "(no folder)" : "Group");
  }

  function setDefaultGroupName(name) {
    const nm = String(name || "").trim() || "(no folder)";
    const groups = loadPinGroups();
    const idx = groups.findIndex(g => g && g.id === "default");
    if (idx >= 0) groups[idx] = { ...groups[idx], name: nm };
    else groups.unshift({ id: "default", name: nm, emoji: "" });
    savePinGroups(groups);
  }


  function normalizeGroupId(id) {
    const gid = String(id || "default") || "default";
    const groups = loadPinGroups();
    if (groups.some(g => g && g.id === gid)) return gid;
    return "default";
  }

  function getCurrentGroupFilter() {
    try {
      const v = localStorage.getItem(PIN_GROUP_FILTER_KEY);
      if (!v || v === "all") return "all";
      return normalizeGroupId(v);
    } catch {
      return "all";
    }
  }

  function setCurrentGroupFilter(id) {
    try {
      const v = (id == null || id === "all") ? "all" : normalizeGroupId(id);
      localStorage.setItem(PIN_GROUP_FILTER_KEY, v);
    } catch {}
  }

  function getLastGroup() {
    try {
      const v = localStorage.getItem(PIN_LAST_GROUP_KEY);
      if (!v) return "default";
      return normalizeGroupId(v);
    } catch { return "default"; }
  }

  function setLastGroup(id) {
    try { localStorage.setItem(PIN_LAST_GROUP_KEY, normalizeGroupId(id)); } catch {}
  }

  function createPinGroup(name, emoji) {
    const nm = String(name || "").trim();
    if (!nm) return "default";
    const groups = loadPinGroups();
    const id = _uid();
    groups.push({ id, name: nm, emoji: (typeof emoji === 'string') ? emoji : '' });
    savePinGroups(groups);
    return id;
  }


function getNextPinNumber(pins) {
  let max = 0;
  try {
    for (const p of (pins || [])) {
      const name = String(p && p.name ? p.name : "");
      const m = /^Pin\s*#\s*(\d+)/i.exec(name);
      if (m) {
        const n = Number(m[1]) || 0;
        if (n > max) max = n;
      }
    }
  } catch {}
  return max + 1;
}


const PINS_LAYER_KEY = `${SCRIPT_ID}:pinsLayerVisible:v1`;
function getPinsLayerVisible() {
  try {
    const v = localStorage.getItem(PINS_LAYER_KEY);
    if (v == null) return true;
    return v === "1" || v === "true";
  } catch { return true; }
}
function setPinsLayerVisible(v) {
  try { localStorage.setItem(PINS_LAYER_KEY, v ? "1" : "0"); } catch {}
  try {
    if (pinsOlLayer && typeof pinsOlLayer.setVisibility === "function") {
      const cur = (typeof pinsOlLayer.getVisibility === "function") ? pinsOlLayer.getVisibility() : null;
      if (cur !== !!v) pinsOlLayer.setVisibility(!!v);
    }
  } catch {}
  try { renderPinsMarkers(); } catch {}
}


const PINS_NAMES_KEY = `${SCRIPT_ID}:pinsShowNamesOnMap:v1`;
function getPinsShowNamesOnMap() {
  try {
    const v = localStorage.getItem(PINS_NAMES_KEY);
    if (v == null) return true;
    return v === "1" || v === "true";
  } catch { return true; }
}
function setPinsShowNamesOnMap(v) {
  try { localStorage.setItem(PINS_NAMES_KEY, v ? "1" : "0"); } catch {}
  try { renderPinsMarkers(); } catch {}
}

const PINS_NAMES_MINZOOM_KEY = `${SCRIPT_ID}:pinsNamesMinZoom:v1`;
function getPinsNamesMinZoom(){
  try{
    const v = localStorage.getItem(PINS_NAMES_MINZOOM_KEY);
    if (v == null) return 9;
    const n = Number(v);
    if (!Number.isFinite(n)) return 9;
    return Math.min(22, Math.max(1, Math.round(n)));
  }catch{ return 9; }
}
function setPinsNamesMinZoom(z){
  try{
    const n = Math.min(22, Math.max(1, Math.round(Number(z) || 9)));
    localStorage.setItem(PINS_NAMES_MINZOOM_KEY, String(n));
  }catch{}
  try{ renderPinsMarkers(); }catch{}
}

  let pinsDrag = { active:false, startX:0, startY:0, baseLeft:12, baseTop:12, pointerId:null };
  let pinsPanelEl = null;


  let pinsBubbleEl = null;
  let pinsBubbleDrag = {
    active: false,
    pointerId: null,
    startX: 0,
    startY: 0,
    baseLeft: 0,
    baseTop: 0,
    moved: false,
    movedAt: 0,
  };
  let _pinsBubbleHideT = 0;
  let _pinsPanelMinimizeHideT = 0;
  let _pinsBubbleForceHideUntil = 0;
  let _pinsPanelSuppressDockUntil = 0;

  let _pinsPanelSuppressROUntil = 0;
  let _pinsPanelLastAutoAt = 0;
  const _pinsNow = () => {
    try { return (typeof performance !== "undefined" && performance.now) ? performance.now() : Date.now(); }
    catch { return Date.now(); }
  };
  let pinsCache = [];
  let reminderIntervalId = null;
  const firedReminderKeys = new Set();

  const activeReminderNotices = new Set();
  let bellLoopId = null;
  let missedRemindersChecked = false;


const reminderTimers = new Map();
function clearReminderTimer(pinId){
  try{
    const t = reminderTimers.get(String(pinId));
    if (t) { clearTimeout(t); }
    reminderTimers.delete(String(pinId));
  }catch{}
}
function scheduleReminderTimer(pin){
  try{
    if (!pin || !pin.id) return;
    clearReminderTimer(pin.id);
    if (!pin.reminderAt || pin.reminderDone) return;
    const at = Number(pin.reminderAt);
    if (!Number.isFinite(at)) return;
    const delay = Math.max(0, at - Date.now());
    const d = Math.min(delay, 0x7fffffff);
    const tid = setTimeout(() => { try { triggerReminderById(String(pin.id), at); } catch {} }, d);
    reminderTimers.set(String(pin.id), tid);
  }catch{}
}
function scheduleAllReminderTimers(){
  try{
    const pins = loadPins();
    for (const p of pins) scheduleReminderTimer(p);
  }catch{}
}
function triggerReminderById(pinId, expectedAt){
  try{
    const pins = loadPins();
    const p = pins.find(x => x && x.id === String(pinId));
    if (!p) return;
    const atRaw = p.reminderAt;
    if (atRaw == null) return;
    const at = Number(atRaw);
    if (!Number.isFinite(at) || at <= 0) return;
    if (expectedAt != null && Number(expectedAt) !== at) return;
    if (p.reminderDone) return;
    if (at > Date.now()) { scheduleReminderTimer(p); return; }

    let shown = false;
    try { shown = showReminderNotice([p]) === true; } catch(e){ try { console.error("[WME Pins] showReminderNotice failed", e); } catch{} }
    if (!shown) {
      try { toast(`Reminder: ${p.name || "Pinned place"}`); } catch {}
    }

    try { sendExternalReminderWebhook(p); } catch {}

    p.reminderDone = true;
    p.reminderFiredAt = Date.now();
    savePins(pins);
    renderPinsPanel();
    try { applyPinsPanelMinimizedOnLoad(); } catch {}
    clearReminderTimer(pinId);
  }catch(e){
    try { console.error("[WME Pins] triggerReminderById failed", e); } catch{}
  }
}

  function clearFiredKeysForPin(pinId){
    try{
      const pref=String(pinId)+":";
      for (const k of Array.from(firedReminderKeys)) {
        if (String(k).startsWith(pref)) firedReminderKeys.delete(k);
      }
    }catch{}
  }

  let pinsMapSyncStarted = false;
  let lastBellAt = 0;
  let pinsCountdownTimerId = null;
  let _pinsCountdownTicking = false;
  let _pinCountdownEls = new Map();
  let _pinCountdownModes = new Map();
  let _pinCountdownTipModes = new Map();
  let _pinRowEls = new Map();
  let pinsMarkersEl = null;
  let pinsOlLayer = null;
  const pinsOlMarkers = new Map();
  const pinsOlClusterMarkers = new Map();
  const pinMarkerEls = new Map();
const pinClusterEls = new Map();
  let pinsMarkersIntervalId = null;
const pasteCfg = { speed: true, lock: true, direction: true, elevation: true };
  let speedDirChoice = "BOTH";

  const menuState = {
    root: null,
    sub: null,
    sub2: null,
    subCloseTimer: null,
    outsideCloseHandler: null,
    escHandler: null,
    subContext: null,
    sub2Context: null,
    rootLL: null,
    rootType: null,
    subAnchorRow: null,
    sub2AnchorRow: null,
  };

  const ICONS = {
    copy: svg(`<path d="M8 7h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2zm-2 8H6a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v0H8a4 4 0 0 0-4 4v6z"/>`),
    paste: svg(`<path d="M19 21H9a2 2 0 0 1-2-2V7h2v12h10v2z"/><path d="M16 3H10a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2zm0 14H10V5h6v12z"/>`),
    street: svg(`<path d="M5 3h14v2H5V3zm0 16h14v2H5v-2zm1-7h2v2H6v-2zm4 0h2v2h-2v-2zm4 0h2v2h-2v-2zm4 0h2v2h-2v-2zM4 7h16v10H4V7z"/>`),
    lock: svg(`<path d="M7 10V8a5 5 0 0 1 10 0v2h1a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-6a2 2 0 0 1 2-2h1zm2 0h6V8a3 3 0 0 0-6 0v2z"/>`),
    speed: svg(`<path d="M12 4a9 9 0 1 0 9 9h-2a7 7 0 1 1-7-7V4zm6.5 6.5-5.3 3a2 2 0 1 1-1-1l3-5.3 3.3 3.3z"/>`),
    zoom: svg(`<path d="M10 18a8 8 0 1 1 5.3-2l4.3 4.3-1.4 1.4L13.9 17.4A8 8 0 0 1 10 18zm0-2a6 6 0 1 0 0-12 6 6 0 0 0 0 12z"/><path d="M9 9h2V7h2v2h2v2h-2v2h-2v-2H9V9z"/>`),
    endpoints: svg(`<path d="M7 7a3 3 0 1 1 0 6 3 3 0 0 1 0-6zm10 4a3 3 0 1 1 0 6 3 3 0 0 1 0-6z"/><path d="M9.5 11.5h5v2h-5v-2z"/>`),
    tools: svg(`<path d="M21 7.5 18.5 5l-2 2 2.5 2.5L21 7.5zM3 17.5V21h3.5l10-10-3.5-3.5-10 10z"/><path d="M14.2 6.8 17 9.6"/>`),
    more: svg(`<path d="M5 10a2 2 0 1 0 0 4 2 2 0 0 0 0-4zm7 0a2 2 0 1 0 0 4 2 2 0 0 0 0-4zm7 0a2 2 0 1 0 0 4 2 2 0 0 0 0-4z"/>`),
    minus: svg(`<path d="M6 12h12v2H6z"/>`),
    mapPin: svg(`<path d="M12 22s7-5.2 7-12a7 7 0 0 0-14 0c0 6.8 7 12 7 12zm0-10a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/>`),

    folder: svg(`<path d="M10 4l2 2h8a2 2 0 0 1 2 2v3H2V6a2 2 0 0 1 2-2h6zm14 9v7a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-7h22z"/>`),
    folderPlus: svg(`<path d="M10 4l2 2h8a2 2 0 0 1 2 2v3H2V6a2 2 0 0 1 2-2h6zm14 9v7a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-7h22z"/><path d="M18.5 14.5v6h-2v-6h2z"/><path d="M14.5 18.5h6v-2h-6v2z"/>`),
    link: svg(`<path d="M10.6 13.4a1 1 0 0 0 1.4 1.4l3.6-3.6a3 3 0 0 0-4.2-4.2l-1.6 1.6a1 1 0 1 0 1.4 1.4l1.6-1.6a1 1 0 1 1 1.4 1.4l-3.6 3.6z"/><path d="M13.4 10.6a1 1 0 0 0-1.4-1.4l-3.6 3.6a3 3 0 0 0 4.2 4.2l1.6-1.6a1 1 0 1 0-1.4-1.4l-1.6 1.6a1 1 0 1 1-1.4-1.4l3.6-3.6z"/>`),
    jump: svg(`<path d="M10 16a6 6 0 1 1 4.47-2.01l4.27 4.28-1.41 1.41-4.28-4.27A5.98 5.98 0 0 1 10 16zm0-2a4 4 0 1 0 0-8 4 4 0 0 0 0 8z"/>`),
    refresh: svg(`<path d="M17.65 6.35A7.95 7.95 0 0 0 12 4V1L7 6l5 5V7c2.76 0 5 2.24 5 5a5 5 0 0 1-8.66 3.54l-1.42 1.42A6.98 6.98 0 0 0 19 12c0-2.21-.9-4.21-2.35-5.65z"/>`),
    gear: svg(`<path d="M12 8a4 4 0 1 0 0 8 4 4 0 0 0 0-8zm9 4-2 .7a7.9 7.9 0 0 1-.6 1.5l1.2 1.8-1.8 1.8-1.8-1.2a7.9 7.9 0 0 1-1.5.6L13 21h-2l-.7-2a7.9 7.9 0 0 1-1.5-.6l-1.8 1.2-1.8-1.8 1.2-1.8a7.9 7.9 0 0 1-.6-1.5L3 13v-2l2-.7a7.9 7.9 0 0 1 .6-1.5L4.4 7l1.8-1.8 1.8 1.2a7.9 7.9 0 0 1 1.5-.6L11 3h2l.7 2a7.9 7.9 0 0 1 1.5.6l1.8-1.2L20 7l-1.2 1.8c.25.48.46.98.6 1.5L21 11v2z"/>`),
    properties: svg(`<path d="M4 7h10v2H4V7zm0 8h10v2H4v-2zm12-9h4v2h-4V6zm0 8h4v2h-4v-2z"/><path d="M14 6h2v6h-2V6zm-6 4h2v10H8V10zm10-3h2v10h-2V7z"/>`),
    ext: svg(`<path d="M14 3h7v7h-2V6.4l-9.3 9.3-1.4-1.4L17.6 5H14V3z"/><path d="M5 5h6v2H7v10h10v-4h2v6H5V5z"/>`),
    grip: svg(`<path d="M9 6h2v2H9V6zm4 0h2v2h-2V6zM9 10h2v2H9v-2zm4 0h2v2h-2v-2zM9 14h2v2H9v-2zm4 0h2v2h-2v-2z"/>`),
    trash: svg(`<path d="M9 3h6l1 2h5v2H3V5h5l1-2zm1 6h2v10h-2V9zm4 0h2v10h-2V9zM7 9h2v10H7V9z"/>`),
    bell: svg(`<path d="M12 22a2 2 0 0 0 2-2H10a2 2 0 0 0 2 2zm6-6V11a6 6 0 1 0-12 0v5L4 18v1h16v-1l-2-2z"/>`),
    edit: svg(`<path d="M3 17.25V21h3.75L17.8 9.95l-3.75-3.75L3 17.25z"/><path d="M20.7 7.04a1 1 0 0 0 0-1.41l-2.34-2.34a1 1 0 0 0-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/>`),
    eye: svg(`<path d="M12 5c-7 0-10 7-10 7s3 7 10 7 10-7 10-7-3-7-10-7zm0 12a5 5 0 1 1 0-10 5 5 0 0 1 0 10z"/><path d="M12 9.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5z"/>`),
    arrows: svg(`<path d="M7 7h6V5l4 3-4 3V9H7V7zm10 10h-6v2l-4-3 4-3v2h6v2z"/>`),
    select: svg(`<path d="M3 3h6v2H5v4H3V3zm16 0h2v6h-2V5h-4V3h4zM3 15h2v4h4v2H3v-6zm16 4v-4h2v6h-6v-2h4z"/>`),
    chain: svg(`<path d="M7 12a3 3 0 0 1 3-3h2v2h-2a1 1 0 0 0 0 2h2v2h-2a3 3 0 0 1-3-3zm7-3h2a3 3 0 0 1 0 6h-2v-2h2a1 1 0 0 0 0-2h-2V9z"/><path d="M10 11h4v2h-4v-2z"/>`),
  };

  if (!ICONS.chevDown) {
    ICONS.chevDown = svg(`<path d="M7 10l5 5 5-5z"/>`);
  }

  function svg(pathD) {
    return `<svg viewBox="0 0 24 24" aria-hidden="true">${pathD}</svg>`;
  }

  function withIcon(iconSvg, text, iconClass = "") {
    const cls = iconClass ? `wmeRcI ${iconClass}` : "wmeRcI";
    return `<div class="wmeRcLbl"><span class="${cls}">${iconSvg}</span><span class="wmeRcT">${text}</span></div>`;
  }

  function ensureCSS() {
    if (document.getElementById("wmeRcCss")) return;
    const s = document.createElement("style");
    s.id = "wmeRcCss";
    s.textContent = `
      .wmeRcToast{position:fixed;right:16px;bottom:16px;z-index:2147483647;background:rgba(20,20,22,.90);color:#fff;border:1px solid rgba(255,255,255,.18);border-radius:12px;padding:10px 12px;box-shadow:0 10px 30px rgba(0,0,0,.35);backdrop-filter:blur(10px);font:13px/1.2 system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;opacity:0;transform:translate3d(0,10px,0);transition:.18s ease;max-width:min(560px,calc(100vw - 32px));pointer-events:none;will-change:transform,opacity;}
      .wmeRcToast.show{opacity:1;transform:translate3d(0,0,0);}

      .wmeRcPinHitPill{position:fixed;left:50%;bottom:22px;top:auto;right:auto;z-index:2147483647;
        display:flex;align-items:center;gap:10px;
        max-width:min(520px,calc(100vw - 32px));
        padding:10px 12px;
        background:rgba(20,20,22,.72);color:#fff;border:1px solid rgba(255,255,255,.16);
        border-radius:14px;box-shadow:0 18px 54px rgba(0,0,0,.45);
        backdrop-filter:blur(18px);-webkit-backdrop-filter:blur(18px);
        font:13px/1.2 system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;
        opacity:0;transform:translate3d(-50%,14px,0);transition:.18s ease;pointer-events:auto;}
      .wmeRcPinHitPill.show{opacity:1;transform:translate3d(-50%,0,0);}
      .wmeRcPinHitText{font-weight:900;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0;}
      .wmeRcPinHitClose{width:26px;height:26px;border-radius:10px;border:1px solid rgba(255,255,255,.14);
        background:rgba(255,255,255,.06);display:flex;align-items:center;justify-content:center;cursor:pointer;flex:0 0 auto;transition:.16s ease;user-select:none;}
      .wmeRcPinHitClose:hover{background:rgba(255,255,255,.10);}

      .wmeRcNoticeStack{position:fixed;right:96px;bottom:118px;z-index:2147483647;display:flex;flex-direction:column;gap:10px;align-items:flex-end;pointer-events:none;}
      .wmeRcNotice{position:relative;min-width:260px;max-width:min(440px,calc(100vw - 32px));
        background:rgba(16,16,18,.55);color:#fff;border:1px solid rgba(255,255,255,.14);border-radius:16px;padding:13px 13px 11px;
        box-shadow:0 18px 54px rgba(0,0,0,.48);backdrop-filter:blur(22px) saturate(140%);-webkit-backdrop-filter:blur(22px) saturate(140%);
        font:13px/1.25 system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;
        opacity:0;transform:translate3d(0,10px,0);transition:.22s ease;pointer-events:auto;}
      .wmeRcNotice.show{opacity:1;transform:translate3d(0,0,0);}

      .wmeRcNoticeTitle{font-weight:900;font-size:12px;letter-spacing:.22px;text-transform:uppercase;margin-bottom:6px;opacity:.80;}
      .wmeRcNoticeMsg{font-size:16px;font-weight:900;letter-spacing:.15px;opacity:.98;margin-bottom:6px;line-height:1.15;white-space:normal;word-break:break-word;}
      .wmeRcNoticeNote{font-size:13px;opacity:.95;margin-top:2px;margin-bottom:10px;white-space:normal;word-break:break-word;background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.12);border-radius:12px;padding:8px 10px;}
      .wmeRcNoticeActions{display:flex;gap:8px;justify-content:flex-start;}
      .wmeRcNoticeBtn{
  appearance:none;
  display:flex;align-items:center;justify-content:center;line-height:1;
  border:1px solid rgba(255,255,255,.16);
  background:rgba(255,255,255,.06);
  color:#fff;
  border-radius:12px;
  padding:8px 12px;
  font-weight:900;
  cursor:pointer;
  transition:transform .14s ease, background .14s ease, box-shadow .14s ease, border-color .14s ease;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.06);
}
.wmeRcNoticeBtn:hover{
  background:rgba(255,255,255,.10);
  transform:translate3d(0,-1px,0);
  box-shadow:0 12px 28px rgba(0,0,0,.28), inset 0 1px 0 rgba(255,255,255,.08);
}
.wmeRcNoticeBtn:active{
  background:rgba(255,255,255,.12);
  transform:translate3d(0,0,0) scale(.99);
}
.wmeRcNoticeBtn.primary{
  border-color:rgba(255,255,255,.18);
  background:rgba(255,255,255,.10);
}
.wmeRcNoticeBtn.primary:hover{
  background:rgba(255,255,255,.14);
  transform:translate3d(0,-1px,0);
}

      .wmeRcRemNoteWrap{margin-top:10px;background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.10);border-radius:12px;padding:8px 10px;}
      .wmeRcRemNoteLbl{font-weight:800;font-size:12px;opacity:.85;margin-bottom:6px;}
      .wmeRcRemNoteInp{width:100%;height:92px;resize:none;outline:none;border:none;background:transparent;color:#fff;font:13px/1.35 system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;opacity:.92;overflow:auto;scrollbar-width:none;}
      .wmeRcRemNoteInp::-webkit-scrollbar{width:0;height:0;}
.wmeRcSnoozePop{
  position:fixed;left:0;top:0;z-index:2147483647;
  min-width:260px;max-width:320px;
  padding:12px 12px 12px;
  color:#fff;
  background:linear-gradient(180deg,rgba(22,22,26,.42),rgba(12,12,14,.34));
  border:1px solid rgba(255,255,255,.14);
  border-radius:16px;
  box-shadow:0 22px 60px rgba(0,0,0,.50);
  backdrop-filter:blur(28px) saturate(160%);
  -webkit-backdrop-filter:blur(28px) saturate(160%);
  transform-origin:90% 100%;
  animation:wmeRcSnoozeIn .18s ease-out;
}
@keyframes wmeRcSnoozeIn{from{opacity:0;transform:translate3d(0,8px,0) scale(.985);}to{opacity:1;transform:translate3d(0,0,0) scale(1);}}
.wmeRcSnoozeTitle{
  font-weight:900;font-size:12px;letter-spacing:.2px;
  opacity:.92;margin:0 0 10px;
}
.wmeRcSnoozeGrid{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:10px;}
.wmeRcSnoozeChip{
  cursor:pointer;user-select:none;
  padding:8px 12px;border-radius:999px;
  border:1px solid rgba(255,255,255,.14);
  background:rgba(255,255,255,.05);
  color:#fff;font-weight:900;font-size:12px;letter-spacing:.15px;
  transition:transform .14s ease, background .14s ease, box-shadow .14s ease, border-color .14s ease;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.06);
}
.wmeRcSnoozeChip:hover{
  background:rgba(255,255,255,.10);
  border-color:rgba(255,255,255,.22);
  transform:translate3d(0,-1px,0) scale(1.02);
  box-shadow:0 10px 24px rgba(0,0,0,.28), inset 0 1px 0 rgba(255,255,255,.08);
}
.wmeRcSnoozeChip:active{
  transform:translate3d(0,0,0) scale(.99);
  background:rgba(255,255,255,.12);
}
.wmeRcSnoozeRow{display:flex;gap:8px;align-items:center;}
.wmeRcSnoozeInput{
  flex:1;min-width:0;
  height:34px;line-height:34px;
  border-radius:12px;border:1px solid rgba(255,255,255,.14);
  background:rgba(255,255,255,.05);
  color:#fff !important;
  -webkit-text-fill-color:#fff !important;
  padding:0 12px;
  font-weight:900;outline:none;
  font-variant-numeric:tabular-nums;
  transition:box-shadow .14s ease, border-color .14s ease, background .14s ease;
}
.wmeRcSnoozeInput:focus{
  border-color:rgba(255,255,255,.24);
  background:rgba(255,255,255,.07);
  box-shadow:0 0 0 4px rgba(255,255,255,.06);
}
.wmeRcSnoozeInput::placeholder{color:rgba(255,255,255,.28);font-weight:700;}
.wmeRcSnoozeInput:placeholder-shown{color:rgba(255,255,255,.31) !important;-webkit-text-fill-color:#ffffff50 !important;}
.wmeRcSnoozeInput:not(:placeholder-shown){color:#fff !important;-webkit-text-fill-color:#fff !important;}
.wmeRcSnoozeGo{
  appearance:none;
  height:34px;min-width:70px;
  display:flex;align-items:center;justify-content:center;line-height:1;
  border-radius:12px;
  border:1px solid rgba(var(--pinRGB,255,160,60),.34);
  background:var(--wmeRcPinGrad, linear-gradient(180deg, rgba(255,200,120,.92), rgba(255,140,0,.92)));
  color:#fff;
  padding:0 12px;
  font-weight:950;letter-spacing:.15px;
  cursor:pointer;
  box-shadow:0 10px 26px rgba(0,0,0,.25);
  transition:filter .14s ease,transform .14s ease,border-color .14s ease, box-shadow .14s ease;
}
.wmeRcSnoozeGo:hover{
  filter:brightness(1.05);
  border-color:rgba(var(--pinRGB,255,160,60),.44);
  transform:translate3d(0,-1px,0);
  box-shadow:0 14px 34px rgba(0,0,0,.30);
}
.wmeRcSnoozeGo:active{
  transform:translate3d(0,0,0);
  filter:brightness(.98);
}

      .wmeRcPinCluster{position:absolute;width:34px;height:34px;border-radius:999px;
        display:flex;align-items:center;justify-content:center;
        background:rgba(20,20,22,.82);border:1px solid rgba(255,255,255,.14);
        box-shadow:0 12px 30px rgba(0,0,0,.40);backdrop-filter:blur(10px);
        color:#fff;font-weight:900;font-size:12px;pointer-events:auto;cursor:pointer;
        transform:translate3d(-9999px,-9999px,0);will-change:transform;}
      .wmeRcPinCluster:hover{background:rgba(24,24,26,.86);}

      .wmeRcOlMarker{cursor:pointer !important;position:relative;}
.wmeRcOlMarker img, .wmeRcOlMarker svg, .wmeRcOlMarker canvas{filter:drop-shadow(0 10px 18px rgba(0,0,0,.45));}
      .wmeRcPinLabel{
        position:absolute;
        left:38px;
        top:50%;
        transform:translate3d(0,-50%,0);
        padding:6px 10px;
        border-radius:13px;
        background: rgba(var(--pinRGB,255,160,60), .34);
        border:none;
        box-shadow:0 16px 44px rgba(0,0,0,.42), inset 0 0 0 1px rgba(255,255,255,.10);
        backdrop-filter:blur(16px) saturate(140%);
        -webkit-backdrop-filter:blur(16px) saturate(140%);
        color: var(--wmeRcPinLabelFg, #fff);
        text-shadow: var(--wmeRcPinLabelShadow, 0 6px 18px rgba(0,0,0,.35));
        font:12px/1.2 system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;
        font-weight:600;
        letter-spacing:.12px;
        white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
        max-width:220px;
        pointer-events:none;
        opacity:.98;
      }




      .wmeRcMenu{position:fixed;z-index:2147483647;padding-bottom: 5px;min-width:240px;max-width:340px;background:rgba(20,20,22,.72);color:#fff;border:1px solid rgba(255,255,255,.14);border-radius:14px;box-shadow:0 18px 60px rgba(0,0,0,.45);backdrop-filter:blur(18px);overflow:hidden;font:13px/1.25 system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;}
      .wmeRcMenu.sub{min-width:240px;max-width:340px;border-radius:12px;max-height:380px;overflow-y:auto;overscroll-behavior:contain;scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.22) rgba(255,255,255,.06);}
      .wmeRcMenu.sub::-webkit-scrollbar{width:10px;}
      .wmeRcMenu.sub::-webkit-scrollbar-track{background:rgba(255,255,255,.06);}
      .wmeRcMenu.sub::-webkit-scrollbar-thumb{background:rgba(255,255,255,.22);border-radius:10px;border:2px solid rgba(0,0,0,0);background-clip:padding-box;}
      .wmeRcMenu.sub::-webkit-scrollbar-thumb:hover{background:rgba(255,255,255,.30);}
      .wmeRcHdr{padding:9px 10px;border-bottom:1px solid rgba(255,255,255,.10);opacity:.96;font-size:12px;display:flex;justify-content:space-between;gap:10px;align-items:center;}
      .wmeRcHdrLeft{display:flex;align-items:center;gap:8px;min-width:0;}
      .wmeRcHdrTitle{font-weight:900;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.wmeRcMenu,.wmeRcPins,.wmeRcPins *,.wmeRcPinsPanel,.wmeRcNotice,.wmeRcModal,.wmeRcToast{user-select:none;-webkit-user-select:none;}
      .wmeRcInput,.wmeRcSnoozeInput,textarea{user-select:text;-webkit-user-select:text;}
      .wmeRcMuted{opacity:.65;}
      .wmeRcItem{padding:9px 10px;cursor:pointer;display:flex;justify-content:space-between;gap:10px;align-items:center;transition:.16s ease;}
      .wmeRcItem:hover{background:rgba(255,255,255,.08);transform:translate3d(0,-1px,0);}
      .wmeRcItem.submenuOpen{background:rgba(255,255,255,.12)!important;box-shadow:none!important;transform:none!important;position:relative;}
      .wmeRcItem:active{background:rgba(255,255,255,.12);transform:translate3d(0,0,0);}
      .wmeRcItem.disabled{cursor:default;opacity:.55;}
      .wmeRcItem.disabled:hover{background:transparent;transform:none;}

      .wmeRcItem.sectionHdr{cursor:default;opacity:.70;justify-content:center;}
      .wmeRcItem.sectionHdr:hover{background:transparent;transform:none;}
      .wmeRcItem.sectionHdr .wmeRcLeft{width:100%;display:flex;justify-content:center;}
      .wmeRcItem.sectionHdr .wmeRcLeft>div{font-weight:950;letter-spacing:.2px;opacity:.85;text-align:center;}
      .wmeRcItem.sectionHdr > div:last-child{display:none;}
      .wmeRcItem.selected{background:rgba(255,255,255,.10);}
      .wmeRcLeft{display:flex;flex-direction:column;gap:2px;min-width:0;}
      .wmeRcSubText{display:none;}
      .wmeRcSep{height:1px;background:rgba(255,255,255,.10);margin:6px 0;}
      .wmeRcKbd{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:11px;opacity:.85;border:1px solid rgba(255,255,255,.16);padding:2px 6px;border-radius:8px;height:fit-content;}
      .wmeRcChevron{opacity:.8;padding-left:10px;}
      .wmeRcCheck{opacity:.95;font-size:13px;}
      .wmeRcMiniBtn{cursor:pointer;user-select:none;padding:6px 10px;border-radius:10px;border:1px solid rgba(255,255,255,.16);background:rgba(255,255,255,.06);font-size:12px;line-height:1;white-space:nowrap;transition:.16s ease;}
      .wmeRcMiniBtn:hover{background:rgba(255,255,255,.10);transform:translate3d(0,-1px,0);}
      .wmeRcMiniBtn:active{background:rgba(255,255,255,.14);transform:translate3d(0,0,0);}
      .wmeRcMiniIcon{padding:6px 8px;min-width:34px;display:flex;align-items:center;justify-content:center;font-size:14px;}
      .wmeRcI{width:16px;height:16px;flex:0 0 16px;display:inline-flex;align-items:center;justify-content:center;opacity:.92;}
      .wmeRcI svg{width:16px;height:16px;display:block;fill:currentColor;}
      .wmeRcI.big{width:18px;height:18px;flex:0 0 18px;opacity:.95;}
      .wmeRcI.big svg{width:18px;height:18px;}
            .wmeRcI.xl{width:20px;height:20px;flex:0 0 20px;opacity:.98;}
      .wmeRcI.xl svg{width:20px;height:20px;}
.wmeRcLbl{display:flex;align-items:center;gap:8px;}
      .wmeRcT{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
      .wmeRcSpeedWrap{padding:10px 10px 10px 10px;}
      .wmeRcSpeedTitle{font-size:11px;opacity:.75;margin-bottom:7px;display:flex;justify-content:space-between;gap:10px;align-items:center;}
      .wmeRcSpeedGrid{display:flex;flex-wrap:wrap;gap:6px;align-items:center;}
      .wmeRcSpeedBtn{width:30px;height:30px;border-radius:999px;border:2px solid rgba(255,60,60,.95);background:rgba(255,255,255,.02);color:#fff;display:flex;align-items:center;justify-content:center;cursor:pointer;user-select:none;font-size:11px;line-height:1;transition:.16s ease;position:relative;}
      .wmeRcSpeedBtn:hover{background:rgba(255,60,60,.10);transform:translate3d(0,-1px,0);}
      .wmeRcSpeedBtn:active{background:rgba(255,60,60,.18);transform:translate3d(0,0,0);}
      .wmeRcSpeedBtn svg{width:14px;height:14px;fill:rgba(255,60,60,.95);}
      .wmeRcSpeedBtn.sel{background:rgba(255,60,60,.18);box-shadow:0 0 0 4px rgba(255,60,60,.10);}
      .wmeRcSpeedBtn.sel::after{content:"";position:absolute;width:6px;height:6px;border-radius:99px;background:rgba(255,255,255,.92);bottom:-2px;right:-2px;box-shadow:0 6px 16px rgba(0,0,0,.35);}
      .wmeRcChips{display:flex;gap:6px;}
      .wmeRcChip{cursor:pointer;padding:6px 10px;border-radius:999px;border:1px solid rgba(255,255,255,.16);background:rgba(255,255,255,.06);font-size:12px;opacity:.9;transition:.16s ease;user-select:none;}
      .wmeRcChip:hover{background:rgba(255,255,255,.10);transform:translate3d(0,-1px,0);}
      .wmeRcChip:active{transform:translate3d(0,0,0);}
      .wmeRcChip.on{border-color:rgba(255,255,255,.30);background:rgba(255,255,255,.14);opacity:1;}
      .wmeRcModalBackdrop{position:fixed;inset:0;z-index:2147483647;background:rgba(0,0,0,.35);opacity:0;transition:opacity .16s ease;will-change:opacity;}
      .wmeRcModalBackdrop.show{opacity:1;}
      .wmeRcModal{position:fixed;left:50%;top:50%;transform:translate3d(-50%,-50%,0) scale(.985);opacity:0;z-index:2147483647;width:min(560px,calc(100vw - 24px));background:linear-gradient(180deg,rgba(28,28,32,.78),rgba(18,18,20,.78));border:1px solid rgba(255,255,255,.14);border-radius:16px;box-shadow:0 18px 60px rgba(0,0,0,.48);backdrop-filter:blur(12px);color:#fff;font:13px/1.25 system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;overflow:hidden;transition:opacity .16s ease,transform .16s ease;will-change:transform,opacity;}
      .wmeRcModal.show{opacity:1;transform:translate3d(-50%,-50%,0) scale(1);}
      .wmeRcModal:focus,.wmeRcModal:focus-visible,.wmeRcPins:focus,.wmeRcPins:focus-visible{outline:none !important;}
      .wmeRcModalHdr{padding:12px 12px;border-bottom:1px solid rgba(255,255,255,.10);display:flex;justify-content:space-between;align-items:center;}
      .wmeRcModalTitle{font-weight:800;display:flex;gap:10px;align-items:center;letter-spacing:.2px;white-space:nowrap;}
      .wmeRcModalTitle .wmeRcI{width:18px;height:18px;}
      .wmeRcModalTitle .wmeRcI svg{width:18px;height:18px;}
      .wmeRcModalBody{padding:12px;display:flex;flex-direction:column;gap:10px;}
      .wmeRcHint{opacity:.68;font-size:13px;letter-spacing:.1px;}

      .wmeRcSwitchLabelWrap{display:flex;align-items:center;gap:8px;}

.wmeRcInfoQ{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;border-radius:999px;
  background:rgba(255,255,255,.10);border:1px solid rgba(255,255,255,.16);
  font-weight:700;font-size:12px;opacity:.9;cursor:default;position:relative;flex:0 0 auto;}
.wmeRcInfoQ:hover{background:rgba(255,255,255,.14);}
.wmeRcInfoQ .wmeRcInfoTip{position:absolute;left:calc(100% + 10px);top:50%;transform:translateY(-50%);
  min-width:260px;max-width:360px;
  background:rgba(18,18,20,.92);border:1px solid rgba(255,255,255,.14);
  border-radius:12px;padding:10px 10px;color:rgba(255,255,255,.92);
  box-shadow:0 16px 40px rgba(0,0,0,.55);backdrop-filter:blur(16px);
  font-size:12px;font-weight:400;line-height:1.25;display:none;z-index:2147483647;cursor:default;}
.wmeRcInfoQ:hover .wmeRcInfoTip,.wmeRcInfoQ:focus .wmeRcInfoTip{display:block;}
      .wmeRcModalActions{display:flex;justify-content:flex-end;gap:10px;align-items:center;margin-top:2px;flex-wrap:wrap;}
      .wmeRcModalBtn{cursor:pointer;padding:8px 12px;border-radius:12px;border:1px solid rgba(255,255,255,.16);background:rgba(255,255,255,.06);color:#fff;user-select:none;transition:.16s ease;}
      .wmeRcModalBtn:hover{background:rgba(255,255,255,.10);transform:translate3d(0,-1px,0);}
      .wmeRcModalBtn:active{background:rgba(255,255,255,.14);transform:translate3d(0,0,0);}
      .wmeRcModalBtn.primary{border-color:rgba(255,255,255,.32);background:rgba(255,255,255,.16);box-shadow:0 0 0 4px rgba(255,255,255,.06);font-weight:700;}
      .wmeRcModalBtn.danger{border-color:rgba(255,75,75,.35);background:rgba(255,75,75,.12);}
      .wmeRcModal.wmeRcLightbox{width:min(980px,calc(100vw - 24px));height:min(720px,calc(100vh - 24px));}
      .wmeRcLightbox .wmeRcModalBody{padding:0;display:flex;flex-direction:row;gap:0;height:100%;}
      .wmeRcPmLeft{width:280px;max-width:42vw;border-right:1px solid rgba(255,255,255,.10);padding:12px;display:flex;flex-direction:column;gap:10px;}
      .wmeRcPmRight{flex:1;min-width:0;padding:12px;display:flex;flex-direction:column;gap:10px;}
      .wmeRcPmHdrRow{display:flex;align-items:center;justify-content:space-between;gap:10px;}
      .wmeRcPmSearch{width:100%;}
      .wmeRcPmList{flex:1;min-height:0;overflow:auto;display:flex;flex-direction:column;gap:8px;overscroll-behavior:contain;scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.22) rgba(255,255,255,.06);}
      .wmeRcPmList::-webkit-scrollbar{width:10px;}
      .wmeRcPmList::-webkit-scrollbar-track{background:rgba(255,255,255,.06);}
      .wmeRcPmList::-webkit-scrollbar-thumb{background:rgba(255,255,255,.22);border-radius:10px;border:2px solid rgba(0,0,0,0);background-clip:padding-box;}
      .wmeRcPmCard{position:relative;padding:10px 10px;border-radius:14px;border:1px solid rgba(255,255,255,.10);background:rgba(255,255,255,.04);display:flex;align-items:center;justify-content:space-between;gap:10px;cursor:pointer;transition:.16s ease;}
      .wmeRcPmCard:hover{background:rgba(255,255,255,.07);transform:translate3d(0,-1px,0);}
      .wmeRcPmCard.on{background:rgba(255,255,255,.10);border-color:rgba(255,255,255,.18);}
      .wmeRcPmCardLeft{display:flex;flex-direction:column;gap:2px;min-width:0;}
      .wmeRcPmCardTitle{font-weight:900;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
      .wmeRcPmCardSub{font-size:12px;opacity:.70;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
      .wmeRcPmCardBtns{display:flex;gap:6px;align-items:center;flex:0 0 auto;}
      .wmeRcPmTiny{width:28px;height:28px;border-radius:10px;border:1px solid rgba(255,255,255,.14);background:rgba(255,255,255,.06);display:flex;align-items:center;justify-content:center;cursor:pointer;transition:.16s ease;}
      .wmeRcPmTiny:hover{background:rgba(255,255,255,.10);transform:translate3d(0,-1px,0);}
      .wmeRcPmTiny:active{background:rgba(255,255,255,.14);transform:translate3d(0,0,0);}
      .wmeRcPmRow{padding:10px 10px;border-radius:14px;border:1px solid rgba(255,255,255,.10);background:rgba(255,255,255,.04);display:flex;align-items:center;justify-content:space-between;gap:10px;}
      .wmeRcPmRowLeft{display:flex;flex-direction:column;gap:2px;min-width:0;}
      .wmeRcPmRowTitle{font-weight:900;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%;}
      .wmeRcPmRowSub{font-size:12px;opacity:.70;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
      .wmeRcPmRowBtns{display:flex;gap:6px;align-items:center;flex:0 0 auto;}
      .wmeRcPmSelect{min-width:160px;max-width:220px;}

      .wmeRcWizardHeader{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;}
      .wmeRcWizardHdrLeft{display:flex;flex-direction:column;gap:2px;min-width:0;}
      .wmeRcWizardTitle{font-weight:900;letter-spacing:.2px;}
      .wmeRcWizardSub{font-size:12px;opacity:.7;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:360px;}
      .wmeRcWizardDots{display:flex;gap:6px;align-items:center;flex-wrap:nowrap;}
      .wmeRcDot{width:8px;height:8px;border-radius:999px;background:rgba(255,255,255,.18);box-shadow:inset 0 0 0 1px rgba(255,255,255,.10);}
      .wmeRcDot.on{background:rgba(255,255,255,.70);box-shadow:0 0 0 4px rgba(255,255,255,.10);}
      .wmeRcDot.done{background:rgba(255,255,255,.36);}
      .wmeRcWizardSection{display:flex;flex-direction:column;gap:10px;}
      .wmeRcWizardHint{font-size:12px;opacity:.75;}
      .wmeRcWizardGrid{display:flex;flex-wrap:wrap;gap:8px;align-items:center;}
      .wmeRcWizardBtn{cursor:pointer;padding:8px 12px;border-radius:14px;border:1px solid rgba(255,255,255,.16);background:rgba(255,255,255,.06);transition:.16s ease;user-select:none;}
      .wmeRcWizardBtn:hover{background:rgba(255,255,255,.10);transform:translate3d(0,-1px,0);}
      .wmeRcWizardBtn:active{background:rgba(255,255,255,.14);transform:translate3d(0,0,0);}
      .wmeRcWizardBtn.on{border-color:rgba(255,255,255,.30);background:rgba(255,255,255,.14);box-shadow:0 0 0 4px rgba(255,255,255,.08);}
      .wmeRcWizardList{display:flex;flex-direction:column;gap:8px;}
      .wmeRcWizardRow{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:10px 10px;border:1px solid rgba(255,255,255,.10);background:rgba(255,255,255,.04);border-radius:14px;}
      .wmeRcWizardRowLeft{display:flex;flex-direction:column;gap:2px;min-width:0;}
      .wmeRcWizardRowTitle{font-weight:800;}
      .wmeRcWizardRowSub{font-size:12px;opacity:.7;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
      .wmeRcWizardTri{display:flex;gap:6px;align-items:center;flex-wrap:nowrap;}
      .wmeRcWizardPill{cursor:pointer;padding:6px 10px;border-radius:999px;border:1px solid rgba(255,255,255,.16);background:rgba(255,255,255,.06);font-size:12px;opacity:.95;transition:.16s ease;user-select:none;}
      .wmeRcWizardPill:hover{background:rgba(255,255,255,.10);transform:translate3d(0,-1px,0);}
      .wmeRcWizardPill:active{background:rgba(255,255,255,.14);transform:translate3d(0,0,0);}
      .wmeRcWizardPill.on{border-color:rgba(255,255,255,.30);background:rgba(255,255,255,.14);box-shadow:0 0 0 4px rgba(255,255,255,.08);}

      .wmeRcInput{width:100%;padding:11px 14px;border-radius:14px;border:1px solid rgba(255,255,255,.14);background:rgba(255,255,255,.05);color:#fff;outline:none;font:13px/1.25 ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;box-shadow:inset 0 1px 0 rgba(255,255,255,.05);}

      .wmeRcRow{display:flex;gap:10px;}
      .wmeRcEmojiRow{align-items:stretch;}
      .wmeRcEmojiRow .wmeRcEmojiBtn{height:auto;}
      .wmeRcEmojiRow .wmeRcLenHost{align-self:stretch;}


      .wmeRcSoundPick{position:relative;flex:1 1 auto;min-width:200px;outline:none;}
      .wmeRcSoundBtn{height:38px;border-radius:14px;border:1px solid rgba(255,255,255,.14);background:rgba(0,0,0,.18);display:flex;align-items:center;justify-content:space-between;gap:10px;padding:0 12px;cursor:pointer;user-select:none;}
      .wmeRcSoundBtnLabel{color:rgba(255,255,255,.92);font-size:13px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
      .wmeRcSoundCaret{display:flex;align-items:center;justify-content:center;opacity:.9;}
      .wmeRcSoundCaret svg{width:16px;height:16px;fill:rgba(255,255,255,.85);}
      .wmeRcSoundMenu{position:absolute;left:0;right:0;top:42px;background:rgba(18,18,20,.86);backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);border:1px solid rgba(255,255,255,.16);border-radius:14px;box-shadow:0 20px 60px rgba(0,0,0,.50);padding:6px;display:none;max-height:240px;overflow:auto;z-index:99999;}
      .wmeRcSoundPick[data-open="1"] .wmeRcSoundMenu{display:block;}
      .wmeRcSoundMenu.wmeRcSoundMenuPortal{position:fixed;left:auto;right:auto;top:auto;width:auto;z-index:2147483647;}
      .wmeRcSoundItem{padding:9px 10px;border-radius:10px;color:rgba(255,255,255,.92);font-size:13px;cursor:pointer;user-select:none;transition:.14s ease;}
      .wmeRcSoundItem:hover{background:rgba(255,255,255,.10);}
      .wmeRcSoundMenu::-webkit-scrollbar{width:0;height:0;}
      .wmeRcSoundMenu{scrollbar-width:none;}

      .wmeRcEmojiBtn{flex:0 0 auto;width:40px;height:40px;border-radius:14px;border:1px solid rgba(255,255,255,.16);background:rgba(255,255,255,.06);color:#fff;font-size:18px;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:.15s ease;box-shadow:inset 0 1px 0 rgba(255,255,255,.06);padding:0;margin:0;line-height:1;}
      .wmeRcEmojiBtn:hover{background:rgba(255,255,255,.10);}

      .wmeRcNoEmojiIcon{position:relative;display:inline-flex;align-items:center;justify-content:center;filter:grayscale(1);opacity:.55;font-size:18px;line-height:1;}
      .wmeRcNoEmojiIcon::after{content:"";position:absolute;width:130%;height:2px;background:rgba(255,255,255,.45);transform:rotate(45deg);border-radius:2px;}
      .wmeRcNoEmojiIconSm{font-size:16px;}

      .wmeRcEmojiNone{font-size:12px;opacity:.78;letter-spacing:.1px;}
      .wmeRcEmojiItemNone{font-size:12px;opacity:.9;}

      .wmeRcEmojiPop{margin-top:10px;border-radius:14px;border:1px solid rgba(255,255,255,.12);background:rgba(20,20,22,.70);backdrop-filter:blur(14px);padding:10px;}
      .wmeRcEmojiPop.hidden{display:none;}
      .wmeRcEmojiGrid{display:grid;grid-template-columns:repeat(10, 1fr);gap:6px;}
      .wmeRcEmojiItem{width:100%;aspect-ratio:1/1;border-radius:10px;border:1px solid rgba(255,255,255,.10);background:rgba(255,255,255,.05);color:#fff;font-size:16px;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:.12s ease;}
      .wmeRcEmojiItem:hover{background:rgba(255,255,255,.10);transform:translateY(-1px);}
      .wmeRcInput::-webkit-calendar-picker-indicator{filter:invert(1);opacity:.85;cursor:pointer;}

      .wmeRcLayerRow{padding:4px 2px;display:flex;align-items:center;}
      .wmeRcLayerLabel{display:flex;align-items:center;gap:10px;cursor:pointer;user-select:none;opacity:.92;}
      .wmeRcLayerLabel input{transform:translateY(1px);}

      .wmeRcInput::placeholder{color:rgba(255,255,255,.28);}
      .wmeRcModal .wmeRcInput{color:#fff !important;-webkit-text-fill-color:#fff !important;caret-color:#fff !important;}
      .wmeRcModal .wmeRcInput::placeholder{color:rgba(255,255,255,.30) !important;}
      .wmeRcModal .wmeRcInput:placeholder-shown{color:rgba(255,255,255,.31) !important;-webkit-text-fill-color:#ffffff50 !important;}
      .wmeRcModal .wmeRcInput:not(:placeholder-shown){color:#fff !important;-webkit-text-fill-color:#fff !important;}

      .wmeRcInput.bad{border-color:rgba(255,75,75,.35);background:rgba(255,70,70,.07);box-shadow:0 0 0 2px rgba(255,75,75,.12), inset 0 1px 0 rgba(255,255,255,.06);}

      .wmeRcInput:focus{border-color:rgba(255,255,255,.30);box-shadow:0 0 0 4px rgba(255,255,255,.07),inset 0 1px 0 rgba(255,255,255,.06);}
      .wmeRcToggleGrid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;}
      .wmeRcToggleBtn{cursor:pointer;padding:10px 10px;border-radius:14px;border:1px solid rgba(255,255,255,.16);background:rgba(255,255,255,.06);display:flex;align-items:center;justify-content:space-between;gap:10px;user-select:none;transition:.16s ease;}
      .wmeRcToggleBtn:hover{background:rgba(255,255,255,.10);transform:translate3d(0,-1px,0);}
      .wmeRcToggleBtn:active{transform:translate3d(0,0,0);}
      .wmeRcToggleBtn.off{border-color:rgba(255,75,75,.35);background:rgba(255,75,75,.08);}
      .wmeRcToggleBtn.on{border-color:rgba(60,255,143,.35);background:rgba(60,255,143,.08);}
      .wmeRcToggleLeft{display:flex;flex-direction:column;gap:2px;min-width:0;}
      .wmeRcToggleName{font-weight:800;}
      .wmeRcToggleDesc{font-size:11px;opacity:.70;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
      .wmeRcPill{font-size:11px;border:1px solid rgba(255,255,255,.18);padding:2px 8px;border-radius:999px;opacity:.95;}
      .wmeRcPill.on{border-color:rgba(60,255,143,.35);background:rgba(60,255,143,.12);}
      .wmeRcPill.off{border-color:rgba(255,75,75,.35);background:rgba(255,75,75,.12);}
      .wmeRcActionList{display:flex;flex-direction:column;gap:8px;}
      .wmeRcActionCard{cursor:pointer;padding:10px 10px;border-radius:14px;border:1px solid rgba(255,255,255,.16);background:rgba(255,255,255,.06);display:flex;align-items:center;justify-content:space-between;gap:10px;user-select:none;transition:.16s ease;}
      .wmeRcActionCard:hover{background:rgba(255,255,255,.10);transform:translate3d(0,-1px,0);}
      .wmeRcActionCard:active{background:rgba(255,255,255,.14);transform:translate3d(0,0,0);}
      .wmeRcActionCard.disabled{cursor:default;opacity:.55;}
      .wmeRcActionCard.disabled:hover{background:rgba(255,255,255,.06);transform:none;}
      .wmeRcActionLeft{display:flex;flex-direction:column;gap:2px;min-width:0;}
      .wmeRcActionTitle{font-weight:800;display:flex;gap:8px;align-items:center;}
      .wmeRcActionDesc{font-size:11px;opacity:.70;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
      .wmeRcSideWrap{padding:10px 12px;font-family:system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;color:var(--wmeRcSideFg,inherit);}
      .wmeRcSideCard{background:transparent;border:1px solid rgba(127,127,127,.18);border-radius:12px;box-shadow:none;padding:10px 10px;margin-bottom:10px;}
      .wmeRcSideRow{display:flex;align-items:center;justify-content:space-between;gap:12px;}
      .wmeRcSideTitle,.wmeRcSideSub,.wmeRcSideWrap .wmeRcHint,.wmeRcSideNavBtn,.wmeRcSideWrap .wmeRcMiniBtn,.wmeRcSideWrap label,.wmeRcSideWrap div{color:var(--wmeRcSideFg,inherit);}
      .wmeRcSideTitle{font-weight:800;}
      .wmeRcSideSub{font-size:12px;opacity:.65;margin-top:2px;}
      .wmeRcSideNav{display:flex;gap:8px;margin:0 0 10px;}
      .wmeRcSideNavBtn{appearance:none;border:1px solid rgba(127,127,127,.22);background:transparent;border-radius:999px;padding:5px 12px;font-weight:700;cursor:pointer;transition:background .12s ease,border-color .12s ease,opacity .12s ease;opacity:.86;}
      .wmeRcSideNavBtn:hover{opacity:1;}
      .wmeRcSideNavBtn.is-active{background:rgba(127,127,127,.14);border-color:rgba(127,127,127,.34);opacity:1;}
      .wmeRcSideTabHost{display:grid;gap:10px;}
      .wmeRcSwitch{width:44px;height:26px;border-radius:999px;border:1px solid rgba(0,0,0,.18);background:rgba(0,0,0,.08);position:relative;cursor:pointer;transition:.16s ease;flex:0 0 auto;}
      .wmeRcSwitch.on{background:rgba(60,255,143,.35);border-color:rgba(60,255,143,.55);}
      .wmeRcKnob{width:22px;height:22px;border-radius:999px;background:#fff;position:absolute;top:1px;left:1px;box-shadow:0 8px 16px rgba(0,0,0,.18);transition:.16s ease;}
      .wmeRcSwitch.on .wmeRcKnob{left:21px;}
      .wmeRcTinyToggle{position:fixed;left:12px;bottom:12px;z-index:2147483646;background:rgba(20,20,22,.78);color:#fff;border:1px solid rgba(255,255,255,.18);border-radius:999px;padding:8px 10px;backdrop-filter:blur(10px);font:12px/1.1 system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;display:flex;gap:8px;align-items:center;cursor:pointer;user-select:none;opacity:.35;transition:.16s ease;}
      .wmeRcTinyToggle:hover{opacity:1;transform:translate3d(0,-1px,0);}
      .wmeRcTinyDot{width:10px;height:10px;border-radius:99px;background:#3cff8f;}
      .wmeRcTinyDot.off{background:#ff4b4b;}
      .wmeRcDiffBox{border:1px solid rgba(255,255,255,.14);background:rgba(255,255,255,.04);border-radius:14px;padding:10px 10px;display:flex;flex-direction:column;gap:8px;}
      .wmeRcDiffRow{display:flex;align-items:center;justify-content:space-between;gap:12px;}
      .wmeRcDiffLeft{display:flex;flex-direction:column;gap:2px;min-width:0;}
      .wmeRcDiffName{font-weight:800;}
      .wmeRcDiffDesc{font-size:11px;opacity:.72;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
      .wmeRcDiffRight{font:12px/1.2 ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;opacity:.9;white-space:nowrap;}
      .wmeRcTag{font-size:11px;border:1px solid rgba(255,255,255,.18);padding:2px 8px;border-radius:999px;opacity:.92;}
      .wmeRcTag.ok{border-color:rgba(60,255,143,.35);background:rgba(60,255,143,.12);}
      .wmeRcTag.warn{border-color:rgba(255,188,75,.35);background:rgba(255,188,75,.10);}
      .wmeRcTag.bad{border-color:rgba(255,75,75,.35);background:rgba(255,75,75,.12);}

      .wmeRcPins{position:absolute;left:12px;top:12px;z-index:2147483646;width:260px;max-width:calc(100vw - 24px);
        background:rgba(20,20,22,.52);color:#fff;border:1px solid rgba(255,255,255,.12);border-radius:14px;
        box-shadow:0 16px 48px rgba(0,0,0,.35);backdrop-filter:blur(14px);
        overflow:hidden;font:12px/1.25 system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;
        display:flex;flex-direction:column;resize:vertical;min-height:140px;max-height:calc(100vh - 24px);}
      .wmeRcPins.hidden{display:none;}
      .wmeRcPins.collapsed{resize:none;min-height:0 !important;}
      .wmeRcPins.collapsed .wmeRcPinsList{display:none;}
.wmeRcPinsBubble{
  position:absolute;
  width:46px;height:46px;
  border-radius:999px;
  border:1px solid rgba(255,255,255,.16);
  background:rgba(20,20,22,.55);
  color:#fff;
  backdrop-filter:blur(18px) saturate(150%);
  -webkit-backdrop-filter:blur(18px) saturate(150%);
  box-shadow:0 18px 54px rgba(0,0,0,.38);
  display:flex;align-items:center;justify-content:center;
  cursor:grab;
  user-select:none;-webkit-user-select:none;
  opacity:0;
  transform:scale(.92) translate3d(0,8px,0);
  transition:opacity .22s ease, transform .22s cubic-bezier(.2,.9,.2,1), box-shadow .22s ease;
  z-index:2147483646;
  pointer-events:auto;
}
.wmeRcPinsBubble.show{opacity:1;transform:scale(1) translate3d(0,0,0);}
.wmeRcPinsBubble:active{cursor:grabbing;transform:scale(.98) translate3d(0,0,0);}
.wmeRcPinsBubble svg{width:30px;height:30px;display:block;fill:currentColor;}
.wmeRcPinsBubblePulse{animation:wmeRcPinsBubblePulse 2.6s ease-in-out infinite;}
@keyframes wmeRcPinsBubblePulse{
  0%,100%{box-shadow:0 18px 54px rgba(0,0,0,.38);}
  50%{box-shadow:0 22px 70px rgba(0,0,0,.52);}
}

.wmeRcPins.animOut{opacity:0;transform:scale(.94) translate3d(0,10px,0);pointer-events:none;}
.wmeRcPins.animIn{opacity:1;transform:scale(1) translate3d(0,0,0);}

      .wmeRcPinsHdr{padding:9px 10px;border-bottom:1px solid rgba(255,255,255,.10);display:flex;align-items:center;justify-content:space-between;gap:10px;cursor:move;}
      .wmeRcPinsTitle{font-weight:900;letter-spacing:.2px;white-space:nowrap;}
      .wmeRcPinsCount{opacity:.70;font-size:11px;white-space:nowrap;}
      .wmeRcPinsHdrLeft{display:flex;align-items:center;gap:8px;min-width:0;}
      .wmeRcPinsHdrRight{display:flex;align-items:center;gap:8px;}
      .wmeRcPinsHdrBtn{width:28px;height:28px;border-radius:10px;border:1px solid rgba(255,255,255,.14);background:rgba(255,255,255,.06);display:flex;align-items:center;justify-content:center;cursor:pointer;transition:.16s ease;}
      .wmeRcPinsHdrBtn:hover{background:rgba(255,255,255,.10);}
      .wmeRcPinsFilter{max-width:140px;}
      .wmeRcSelect{border-radius:12px;border:1px solid rgba(255,255,255,.14);background:rgba(255,255,255,.05);color:#fff;outline:none;padding:7px 10px;font-weight:800;font-size:12px;box-shadow:inset 0 1px 0 rgba(255,255,255,.05);}
      .wmeRcSelect:focus{border-color:rgba(255,255,255,.30);box-shadow:0 0 0 4px rgba(255,255,255,.07),inset 0 1px 0 rgba(255,255,255,.06);}
      .wmeRcSelect option{background:#0b0b0e;color:#fff;}
      .wmeRcPinsEmpty{padding:12px 10px;opacity:.7;font-size:12px;}

      .wmeRcPinsList{flex:1 1 auto;min-height:0;overflow:auto;overflow-x:hidden;overscroll-behavior:contain;scrollbar-width:none;-ms-overflow-style:none;}
.wmeRcPinsList::-webkit-scrollbar{width:0;height:0;}


      .wmeRcPinsGroup{border-top:1px solid rgba(255,255,255,.08);}
      .wmeRcPinsGroup:first-child{border-top:none;}
            .wmeRcPinsGroupHdr{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:8px 10px;font-weight:900;letter-spacing:.2px;

              user-select:none;position:sticky;top:0;z-index:3;}
            .wmeRcPinsGroupHdr.wmeRcSticky{background:rgba(0,0,0,.18);}

       .wmeRcPinsGroupName{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:190px;}
      .wmeRcPinsGroupCount{opacity:.65;font-size:11px;flex:0 0 auto;}
      .wmeRcPinsGroupHdr.dropTarget{background:rgba(255,255,255,.09);}

      .wmeRcPinsGroupHdr{cursor:pointer;}
      .wmeRcPinsGroup.collapsed .wmeRcPinsGroupBody{display:none;}
      .wmeRcPinsGroupTop{display:flex;align-items:center;gap:8px;min-width:0;}
      .wmeRcPinsGroupTwisty{width:18px;height:18px;display:flex;align-items:center;justify-content:center;opacity:.75;flex:0 0 auto;transition:transform .16s ease,opacity .16s ease;}
      .wmeRcPinsGroupEmoji{width:18px;height:18px;display:flex;align-items:center;justify-content:center;flex:0 0 auto;font-size:14px;opacity:.92;filter:drop-shadow(0 10px 18px rgba(0,0,0,.30));}

      .wmeRcPinsGroupTwisty svg{width:14px;height:14px;display:block;}
      .wmeRcPinsGroup.collapsed .wmeRcPinsGroupTwisty{transform:rotate(-90deg);opacity:.60;}
      .wmeRcPinsGroupActions{display:flex;align-items:center;gap:6px;flex:0 0 auto;}
      .wmeRcPinsGroupBtn{width:24px;height:24px;border-radius:9px;border:1px solid rgba(255,255,255,.14);background:rgba(255,255,255,.06);display:flex;align-items:center;justify-content:center;cursor:pointer;transition:.16s ease;opacity:.0;pointer-events:none;}
      .wmeRcPinsGroupHdr:hover .wmeRcPinsGroupBtn{opacity:1;pointer-events:auto;}
      .wmeRcPinsGroupBtn:hover{background:rgba(255,255,255,.10);}
          .wmeRcPinsGroupBody{padding:2px 6px 8px 6px;display:flex;flex-direction:column;gap:6px;}
      .wmeRcPinsGroupEmpty{padding:8px 6px;color:rgba(255,255,255,.55);font-size:11px;border:1px dashed rgba(255,255,255,.14);border-radius:12px;text-align:center;background:rgba(255,255,255,.02);}


      .wmeRcPinMarkers{position:absolute;inset:0;z-index:2147483645;pointer-events:none;}
      .wmeRcPinMarker{position:absolute;width:20px;height:20px;transform:translate3d(-9999px,-9999px,0);pointer-events:auto;cursor:pointer;will-change:transform;}
      .wmeRcPinMarker::before{
        content:"";
        position:absolute;left:50%;top:0;
        width:16px;height:16px;border-radius:999px;
        transform:translateX(-50%);
        background:var(--wmeRcPinGrad, linear-gradient(180deg, rgba(255,200,120,.95), rgba(255,140,0,.95)));
        box-shadow:0 10px 24px rgba(0,0,0,.35);
      }
      .wmeRcPinMarker::after{
        content:"";
        position:absolute;left:50%;top:12px;
        width:0;height:0;
        transform:translateX(-50%);
        border-left:6px solid transparent;
        border-right:6px solid transparent;
        border-top:10px solid var(--wmeRcPinTip, rgba(255,140,0,.95));
        filter:drop-shadow(0 10px 18px rgba(0,0,0,.28));
      }
      .wmeRcPinMarkerInner{
        position:absolute;left:50%;top:5px;width:6px;height:6px;border-radius:999px;
        transform:translateX(-50%);
        background:rgba(20,20,22,.75);
        box-shadow:inset 0 1px 0 rgba(255,255,255,.08);
        pointer-events:none;
      }
      .wmeRcPinMarker:hover::before{filter:brightness(1.05);}
      .wmeRcPinMarkerActive::before{
        box-shadow:0 12px 26px rgba(0,0,0,.35), 0 0 18px var(--wmeRcPinGlow, rgba(255,160,60,.34)), 0 0 36px var(--wmeRcPinGlow2, rgba(255,160,60,.22));
        animation:wmeRcMarkerBreath 1.7s ease-in-out infinite;
      }
      @keyframes wmeRcMarkerBreath{
        0%,100%{filter:brightness(1); }
        50%{filter:brightness(1.08); }
      }
      .wmeRcTabs{display:flex;gap:6px;align-items:center;}
      .wmeRcTab{cursor:pointer;padding:7px 10px;border-radius:999px;border:1px solid rgba(255,255,255,.14);background:rgba(255,255,255,.06);font-size:12px;opacity:.90;user-select:none;transition:.16s ease;}
      .wmeRcTab:hover{background:rgba(255,255,255,.10);transform:translate3d(0,-1px,0);}
      .wmeRcTab:active{transform:translate3d(0,0,0);}
      .wmeRcTab.on{border-color:rgba(255,255,255,.30);background:rgba(255,255,255,.14);box-shadow:0 0 0 4px rgba(255,255,255,.08);opacity:1;}
      .wmeRcClockWrap{display:flex;gap:12px;align-items:center;justify-content:space-between;flex-wrap:wrap;}
      .wmeRcClock{width:140px;height:140px;border-radius:999px;position:relative;flex:0 0 auto;
        background:radial-gradient(circle at 30% 25%, rgba(255,255,255,.18), rgba(255,255,255,.02) 55%, rgba(0,0,0,.10));
        border:1px solid rgba(255,255,255,.14);
        box-shadow:inset 0 0 0 1px rgba(255,255,255,.06), 0 16px 48px rgba(0,0,0,.35);
        backdrop-filter:blur(10px);
        user-select:none;
      }
      .wmeRcClock::after{content:"";position:absolute;inset:9px;border-radius:999px;border:1px dashed rgba(255,255,255,.10);opacity:.9;pointer-events:none;}
      .wmeRcClockCenter{position:absolute;left:50%;top:50%;width:9px;height:9px;border-radius:999px;background:rgba(255,255,255,.88);transform:translate(-50%,-50%);box-shadow:0 10px 22px rgba(0,0,0,.35);pointer-events:none;}
      .wmeRcClockHand{position:absolute;left:50%;top:50%;width:2px;height:54px;background:linear-gradient(180deg, rgba(255,255,255,.95), rgba(255,255,255,.35));
        transform-origin:50% 100%;
        transform:translate(-50%,-100%) rotate(0deg);
        border-radius:999px;
        transition:transform .18s ease;
        filter:drop-shadow(0 10px 12px rgba(0,0,0,.35));
        pointer-events:none;
      }
      .wmeRcClockHand::after{content:"";position:absolute;top:-6px;left:50%;width:8px;height:8px;border-radius:999px;background:rgba(255,255,255,.92);transform:translateX(-50%);box-shadow:0 10px 20px rgba(0,0,0,.35);}
      .wmeRcClockHand.min{height:54px;}
      .wmeRcClockHand.hour{width:3px;height:38px;opacity:.95;filter:drop-shadow(0 10px 10px rgba(0,0,0,.32));}

      .wmeRcClockHand.sec{display:none;width:1px;height:70px;opacity:.75;pointer-events:none;
        background:linear-gradient(180deg, rgba(255,255,255,.70), rgba(255,255,255,.18));
        filter:drop-shadow(0 10px 10px rgba(0,0,0,.28));
      }
      .wmeRcClockHand.sec::after{width:6px;height:6px;top:-5px;opacity:.75;}
      .wmeRcPinCountdown{font-variant-numeric:tabular-nums; letter-spacing:.2px; margin-left:6px;}
      .wmeRcPinSubLine{opacity:.86;}
      .wmeRcPinCountdownLine{margin-top:2px; font-variant-numeric:tabular-nums; letter-spacing:.2px;}
      .wmeRcPinCountdownBadge{display:inline-flex;align-items:center;justify-content:center;
        padding:4px 10px;border-radius:999px;border:1px solid rgba(255,255,255,.18);
        background:rgba(255,255,255,.06);box-shadow:inset 0 1px 0 rgba(255,255,255,.06);
        font:700 12px/1 ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;min-width:56px;contain:layout paint;color:rgba(255,255,255,.86);
        cursor:pointer;}
      .wmeRcPinCountdownBadge:empty{display:none;}
      .wmeRcColorRow{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-top:2px;}
      .wmeRcColorSwatch{width:22px;height:22px;border-radius:10px;border:1px solid rgba(255,255,255,.18);background:var(--c);cursor:pointer;box-shadow:0 10px 18px rgba(0,0,0,.22);}
      .wmeRcColorSwatch.sel{box-shadow:0 0 0 3px rgba(255,255,255,.14), 0 10px 18px rgba(0,0,0,.22);outline:2px solid rgba(255,255,255,.26);outline-offset:2px;}
      .wmeRcColorPicker{appearance:none;-webkit-appearance:none;width:32px;height:22px;border-radius:10px;border:1px solid rgba(255,255,255,.18);background:rgba(255,255,255,.06);padding:0;cursor:pointer;}
      .wmeRcColorPicker::-webkit-color-swatch-wrapper{padding:0;}
      .wmeRcColorPicker::-webkit-color-swatch{border:none;border-radius:9px;}

.wmeRcColorPlus{width:32px;height:22px;border-radius:11px;border:1px solid rgba(255,255,255,.18);background:rgba(255,255,255,.06);display:flex;align-items:center;justify-content:center;cursor:pointer;user-select:none}
.wmeRcColorPlus:hover{background:rgba(255,255,255,.10)}
.wmeRcColorSwatch.custom{box-shadow:0 0 0 1px rgba(255,255,255,.10) inset}

.wmeRcColorSep{width:auto; padding:0 6px; color:rgba(255,255,255,.32); font:700 12px/1 system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif; user-select:none;}

.wmeRcColorPop{
  position:fixed; z-index:2147483648;
  width:420px;
  max-width:calc(100vw - 18px);
  border-radius:20px;
  border:1px solid rgba(255,255,255,.10);
  background:rgba(12,15,21,.86);
  backdrop-filter:blur(18px); -webkit-backdrop-filter:blur(18px);
  box-shadow:0 26px 80px rgba(0,0,0,.62);
  padding:14px 14px 12px;
  user-select:none;
}
.wmeRcColorPop *{box-sizing:border-box;}
.wmeRcColorTopRow{display:flex; gap:10px; align-items:center; margin-top:6px; margin-bottom:10px;}
.wmeRcColorSwatchBig{width:34px; height:34px; border-radius:12px; border:1px solid rgba(255,255,255,.14);
  background:var(--c); box-shadow:0 14px 26px rgba(0,0,0,.32);
}
.wmeRcColorStats{display:flex; flex-direction:column; gap:4px; flex:1 1 auto; min-width:0;}
.wmeRcColorStatLine{display:flex; gap:12px; flex-wrap:wrap; align-items:baseline; color:rgba(255,255,255,.86);
  font:600 12px/1.2 system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;
}
.wmeRcColorStatLine span{display:inline-flex; gap:6px; align-items:baseline;}
.wmeRcColorStatKey{opacity:.55; font-weight:700; letter-spacing:.2px;}
.wmeRcColorStatVal{font-variant-numeric:tabular-nums; letter-spacing:.2px;}

.wmeRcPickerRow{display:flex; gap:12px; align-items:stretch; padding-top:6px;}
.wmeRcPickerSV{
  position: relative;
    width: 220px;
    height: 180px;
    border-radius: 16px;
    border: 1px solid rgba(255, 255, 255, .12);
    overflow: hidden;
    background:linear-gradient(to top, rgba(0,0,0,1), rgba(0,0,0,0)),linear-gradient(to right, rgba(255,255,255,1), rgba(255,255,255,0)),var(--hue);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .06), 0 18px 34px rgba(0, 0, 0, .28);
    touch-action: none;
}
.wmeRcPickerSV::before{content:""; position:absolute; inset:0;
  background:linear-gradient(to right, rgba(255,255,255,1), rgba(255,255,255,0));
}
.wmeRcPickerSV::after{content:""; position:absolute; inset:0;
  background:linear-gradient(to top, rgba(0,0,0,1), rgba(0,0,0,0));
}
.wmeRcPickerSV::before,
.wmeRcPickerSV::after{content:none !important;}
.wmeRcPickerSVDot{
  position:absolute;
  width:14px; height:14px;
  border-radius:999px;
  border:2px solid rgba(255,255,255,.92);
  box-shadow:0 10px 20px rgba(0,0,0,.55);
  transform:translate(-7px,-7px);
  pointer-events:none;
}

.wmeRcPickerHue{
  position:relative;
  width:16px; height:180px;
  border-radius:999px;
  border:1px solid rgba(255,255,255,.12);
  overflow:hidden;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.06), 0 18px 34px rgba(0,0,0,.28);
  touch-action:none;
  background:linear-gradient(to bottom,
    #ff0000 0%,
    #ffff00 17%,
    #00ff00 33%,
    #00ffff 50%,
    #0000ff 67%,
    #ff00ff 83%,
    #ff0000 100%
  );
}

.wmeRcPickerSV,
.wmeRcPickerHue{
  border:none !important;
}

.wmeRcPickerSV,.wmeRcPickerHue{outline:none !important;}
.wmeRcPickerHueThumb{
  position:absolute; left:50%;
  width:22px; height:6px;
  border-radius:999px;
  background:rgba(255,255,255,.92);
  box-shadow:0 10px 18px rgba(0,0,0,.55);
  transform:translate(-50%,-50%);
  pointer-events:none;
}

.wmeRcPickerFields{flex:1 1 auto; display:flex; flex-direction:column; gap:8px; min-width:0;}
.wmeRcPickerFieldRow{
  display:flex;
  align-items:center;
  gap:8px;
  min-width:0;
}
.wmeRcPickerFieldLbl,
.wmeRcPickerFieldRow label{
  width:34px;
  flex:0 0 34px;
  color:rgba(255,255,255,.50);
  font:800 11px/1 system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;
  letter-spacing:.3px;
}
.wmeRcPickerField{
  flex:1 1 auto;
  width:100%;
  min-width:0;
  height:34px;
  border-radius:12px;
  border:1px solid rgba(255,255,255,.12);
  background:rgba(255,255,255,.06);
  color:#fff;
  outline:none;
  padding:0 10px;
  font:600 12px/1 system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.06);
  color:#fff !important;
  -webkit-text-fill-color:#fff !important;
  caret-color:#fff !important;
  opacity:1 !important;
  visibility:visible !important;
}
.wmeRcPickerField:focus{
  border-color:rgba(255,255,255,.28);
  background:rgba(255,255,255,.09);
  box-shadow:0 0 0 3px rgba(124,92,255,.18), inset 0 1px 0 rgba(255,255,255,.08);
}
.wmeRcPickerFieldRow:focus-within .wmeRcPickerFieldLbl,
.wmeRcPickerFieldRow:focus-within label{
  color:rgba(255,255,255,.72);
}
.wmeRcPickerField::placeholder{-webkit-text-fill-color:rgba(255,255,255,.36); color:rgba(255,255,255,.36);}


.wmeRcColorActions{display:flex; align-items:center; justify-content:flex-end; gap:10px; padding-top:12px;}
.wmeRcColorSavePreset{
  height:34px; padding:0 14px; border-radius:999px;
  border:1px solid rgba(255,255,255,.12);
  background:rgba(255,255,255,.06);
  color:rgba(255,255,255,.86);
  cursor:pointer; user-select:none;
  display:flex; align-items:center; justify-content:center;
  font:700 12px/1 system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;
  letter-spacing:.2px;
}
.wmeRcColorSavePreset:hover{background:rgba(255,255,255,.10);}

.wmeRcColorDone{
  height:34px; padding:0 14px; border-radius:999px;
  border:1px solid rgba(255,255,255,.12);
  background:rgba(255,255,255,.08);
  color:#fff;
  cursor:pointer; user-select:none;
  font:800 12px/1 system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;
  box-shadow:0 18px 44px rgba(0,0,0,.42);
}
.wmeRcColorDone:hover{filter:brightness(1.04);}

.wmeRcColorDelete{
  height:34px; padding:0 14px; border-radius:999px;
  border:1px solid rgba(255,110,110,.22);
  background:rgba(255,110,110,.10);
  color:rgba(255,255,255,.92);
  cursor:pointer; user-select:none;
  font:800 12px/1 system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;
  box-shadow:0 18px 44px rgba(0,0,0,.32);
}
.wmeRcColorDelete:hover{filter:brightness(1.06);}

.wmeRcSwatchEditTip{
  position:fixed;
  z-index:2147483649;
  padding:6px;
  border-radius:999px;
  border:1px solid rgba(255,255,255,.14);
  background:rgba(20,24,32,.72);
  color:rgba(255,255,255,.90);
  user-select:none;
  backdrop-filter:blur(14px);
  -webkit-backdrop-filter:blur(14px);
  box-shadow:0 18px 44px rgba(0,0,0,.45);
  font:500 11px/1 system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;
  display:inline-flex;
  flex-direction:row;
  flex-wrap:nowrap;
  width:max-content;
  white-space:nowrap;

  gap:6px;
  align-items:center;
}
.wmeRcSwatchEditTipBtn{
  width:28px;
  height:28px;
  border-radius:999px;
  border:none;
  background:transparent;
  color:rgba(255,255,255,.90);
  cursor:pointer;
  display:grid;
  place-items:center;
  padding:0;
}
.wmeRcSwatchEditTipBtn:hover{background:rgba(255,255,255,.10);}
.wmeRcSwatchEditTipBtn:active{background:rgba(255,255,255,.16);}
.wmeRcSwatchEditTipBtn svg{width:14px;height:14px;display:block;opacity:.92;}


.wmeRcColorDone:active{transform:translate3d(0,1px,0);}

.wmeRcPinRowActive{
        background:linear-gradient(180deg, rgba(255,255,255,.10), rgba(255,255,255,.06));
        will-change:box-shadow,background;
        animation:wmeRcPinBreath 1.7s ease-in-out infinite;
      }
      @keyframes wmeRcPinBreath{
        0%,100%{box-shadow:inset 0 0 0 999px rgba(255,160,60,.06), inset 0 0 22px rgba(255,160,60,.22);}
        50%{box-shadow:inset 0 0 0 999px rgba(255,160,60,.10), inset 0 0 34px rgba(255,160,60,.34);}
      }
      .wmeRcPinLeft{cursor:grab;}
      .wmeRcPinRow.dragging .wmeRcPinLeft{cursor:grabbing;}
      .wmeRcPinBtns{
        cursor:default;
        display:flex;
        align-items:center;
        justify-content:flex-end;
        gap:6px;
        position:relative;
        width:28px;
        height:28px;
        flex:0 0 auto;
        transition:width .22s cubic-bezier(.2,.9,.2,1);
      }
      .wmeRcPinRow:hover .wmeRcPinBtns{width:160px;}

      .wmeRcPinCountdown.bell{display:inline-flex; align-items:center; gap:6px; animation:wmeRcBellPulse 1.2s ease-in-out infinite;}
      @keyframes wmeRcBellPulse{0%,100%{transform:scale(1);}50%{transform:scale(1.08);}}

      .wmeRcClockTicks{position:absolute;inset:0;pointer-events:none;opacity:.55;}
      .wmeRcClockTick{position:absolute;left:50%;top:50%;width:2px;height:6px;background:rgba(255,255,255,.35);transform-origin:50% 68px;border-radius:999px;}
      .wmeRcClockNums{position:absolute;inset:0;pointer-events:none;opacity:.80;}
      .wmeRcClockNum{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);font:700 11px/1 system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;letter-spacing:.2px;color:rgba(255,255,255,.82);text-shadow:0 10px 22px rgba(0,0,0,.35);}
      .wmeRcClockValue{display:flex;flex-direction:column;gap:6px;min-width:200px;flex:1 1 auto;}
      .wmeRcBigValue{font:800 28px/1.05 ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;letter-spacing:.3px;}
      .wmeRcEditableValue{cursor:text;display:inline-flex;align-items:baseline;gap:8px;width:fit-content;padding-bottom:2px;border-bottom:1px dashed rgba(255,255,255,.32);}
      .wmeRcEditableValue::after{content:"EDIT";font:900 10px/1 system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;letter-spacing:.6px;text-transform:uppercase;opacity:.55;padding:3px 6px;border-radius:999px;border:1px solid rgba(255,255,255,.14);background:rgba(255,255,255,.06);}
      .wmeRcEditableValue:hover{border-bottom-color:rgba(255,255,255,.60);}
      .wmeRcEditableValue:focus{outline:none;box-shadow:0 0 0 2px rgba(255,255,255,.14);}
      .wmeRcBigValueSub{opacity:.72;font-size:12px;}
      .wmeRcStepper{display:flex;gap:8px;align-items:center;flex-wrap:wrap;}
      .wmeRcStepBtn{cursor:pointer;padding:8px 10px;border-radius:12px;border:1px solid rgba(255,255,255,.16);background:rgba(255,255,255,.06);transition:.16s ease;user-select:none;}
      .wmeRcStepBtn:hover{background:rgba(255,255,255,.10);transform:translate3d(0,-1px,0);}
      .wmeRcStepBtn:active{transform:translate3d(0,0,0);}
      .wmeRcQuickRow{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-top:2px;}
      .wmeRcQuick{cursor:pointer;padding:7px 10px;border-radius:999px;border:1px solid rgba(255,255,255,.14);background:rgba(255,255,255,.06);font-size:12px;opacity:.92;transition:.16s ease;user-select:none;}
      .wmeRcQuick:hover{background:rgba(255,255,255,.10);transform:translate3d(0,-1px,0);}
      .wmeRcQuick:active{transform:translate3d(0,0,0);}
      .wmeRcSplit{display:flex;gap:10px;flex-wrap:wrap;align-items:center;}
      .wmeRcDT{display:flex;gap:10px;flex-wrap:wrap;align-items:center;}
      .wmeRcDT > *{flex:1 1 160px;}

      .wmeRcPickerBtn{appearance:none;-webkit-appearance:none;cursor:pointer;
        display:flex;align-items:center;justify-content:space-between;gap:10px;
        padding:10px 12px;border-radius:14px;
        border:1px solid rgba(255,255,255,.14);
        background:rgba(255,255,255,.05);
        color:#fff;font:800 13px/1.1 system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;
        transition:background .14s ease,border-color .14s ease,box-shadow .14s ease,transform .14s ease;}
      .wmeRcPickerBtn:hover{background:rgba(255,255,255,.09);border-color:rgba(255,255,255,.22);transform:translate3d(0,-1px,0);}
      .wmeRcPickerBtn:active{transform:translate3d(0,0,0);background:rgba(255,255,255,.11);}
      .wmeRcPickerBtn .wmeRcPickerVal{opacity:.86;font-variant-numeric:tabular-nums;letter-spacing:.2px;font-weight:900;}

      .wmeRcPickerPop{position:fixed;left:0;top:0;z-index:2147483647;
        color:#fff;
        min-width:280px;max-width:340px;
        padding:12px;
        background:linear-gradient(180deg,rgba(22,22,26,.46),rgba(12,12,14,.36));
        border:none;border-radius:16px;
        box-shadow:0 22px 60px rgba(0,0,0,.52);
        backdrop-filter:blur(28px) saturate(160%);
        -webkit-backdrop-filter:blur(28px) saturate(160%);
        animation:wmeRcPickerIn .16s ease-out;
      }

      .wmeRcPickerPop{border:none !important;}
      @keyframes wmeRcPickerIn{from{opacity:0;transform:translate3d(0,8px,0) scale(.985);}to{opacity:1;transform:translate3d(0,0,0) scale(1);}}

      .wmeRcCalHdr{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:10px;}
      .wmeRcCalTitle{flex:1;min-width:0;text-align:center;font-weight:950;letter-spacing:.2px;opacity:1;color:rgba(255,255,255,.92);}
      .wmeRcCalNav{appearance:none;border:none;background:rgba(255,255,255,.06);
        border:1px solid rgba(255,255,255,.14);
        width:34px;height:34px;border-radius:12px;color:#fff;cursor:pointer;
        font-weight:950;font-size:18px;line-height:1;
        transition:background .14s ease,transform .14s ease;}
      .wmeRcCalNav:hover{background:rgba(255,255,255,.10);transform:translate3d(0,-1px,0);}
      .wmeRcCalDow{display:grid;grid-template-columns:repeat(7,1fr);gap:6px;margin-bottom:8px;opacity:.70;font-size:11px;font-weight:800;text-align:center;}
      .wmeRcCalGrid{display:grid;grid-template-columns:repeat(7,1fr);gap:6px;}
      .wmeRcCalCell{appearance:none;border:none;cursor:pointer;
        height:34px;border-radius:12px;
        background:rgba(255,255,255,.05);
        border:1px solid rgba(255,255,255,.12);
        color:#fff;font-weight:900;
        transition:background .14s ease,transform .14s ease,border-color .14s ease,box-shadow .14s ease;}
      .wmeRcCalCell.off{background:transparent;border-color:transparent;cursor:default;}
      .wmeRcCalCell:hover{background:rgba(255,255,255,.10);transform:translate3d(0,-1px,0);}
      .wmeRcCalCell.sel{background:rgba(var(--pinRGB,255,160,60),.18);border-color:rgba(var(--pinRGB,255,160,60),.38);box-shadow:0 0 0 4px rgba(var(--pinRGB,255,160,60),.10);}

      .wmeRcTimeHdr{font-weight:950;letter-spacing:.2px;opacity:1;margin-bottom:10px;text-align:center;color:rgba(255,255,255,.92);}
      .wmeRcTimeBody{display:grid;grid-template-columns:1fr 1fr;gap:10px;max-height:220px;overflow:auto;scrollbar-width:thin;}
      .wmeRcTimeCol{display:flex;flex-direction:column;gap:6px;}
      .wmeRcTimeCell{appearance:none;border:none;cursor:pointer;
        padding:8px 10px;border-radius:12px;
        background:rgba(255,255,255,.05);
        border:1px solid rgba(255,255,255,.12);
        color:#fff;font-weight:900;font-variant-numeric:tabular-nums;
        transition:background .14s ease,transform .14s ease,border-color .14s ease;}
      .wmeRcTimeCell:hover{background:rgba(255,255,255,.10);transform:translate3d(0,-1px,0);}
      .wmeRcTimeCell.sel{background:rgba(255,160,60,.18);border-color:rgba(255,160,60,.38);}
      .wmeRcTimeCustom{display:flex;gap:10px;align-items:center;margin-top:10px;}
      .wmeRcTimeMinInp{flex:1;min-width:0;border-radius:14px;border:1px solid rgba(255,255,255,.14);
        background:rgba(255,255,255,.05);color:#fff;padding:10px 12px;font-weight:900;outline:none;}
      .wmeRcTimeOk{appearance:none;border:1px solid rgba(255,160,60,.30);background:rgba(255,160,60,.16);
        color:#fff;border-radius:14px;padding:10px 14px;font-weight:950;cursor:pointer;transition:background .14s ease,transform .14s ease;}
      .wmeRcTimeOk:hover{background:rgba(255,160,60,.22);transform:translate3d(0,-1px,0);}

      .wmeRcTimePop{min-width:300px;max-width:360px;}
      .wmeRcPickerPop.wmeRcTimePop{padding-top:18px;}

      .wmeRcTimeClockBox{display:flex;flex-direction:column;align-items:center;gap:12px;}
      .wmeRcTimeClockLabel{font:900 18px/1.1 ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;
        letter-spacing:.4px; color:rgba(255,255,255,.92);}
      .wmeRcTimeClockInput{background:rgba(255,255,255,.04);border:none;outline:none;
        text-align:center;width:112px;padding:7px 10px;border-radius:12px;
        box-shadow:inset 0 0 0 1px rgba(255,255,255,.12);
        color:rgba(255,255,255,.92);}
      .wmeRcTimeClockInput:focus{box-shadow:inset 0 0 0 1px rgba(255,255,255,.26),0 0 0 4px rgba(255,255,255,.08);}
      .wmeRcTimeClockHelp{opacity:.70;font-size:12px;text-align:center;margin-top:-4px;}
      .wmeRcClockPick{width:160px;height:160px;}
      .wmeRcClockPick .wmeRcClockTick{transform-origin:50% 78px;height:7px;}
      .wmeRcClockPick::after{inset:10px;}
      .wmeRcClockPick .wmeRcClockHand.min{height:62px;}
      .wmeRcClockPick .wmeRcClockHand.hour{height:44px;}
      .wmeRcClockPick .wmeRcClockHand{transition:none !important;}

      .wmeRcClockPick .wmeRcClockNum{font-size:12px;}
      .wmeRcTimeHandTabs{display:flex;gap:6px;align-items:center;justify-content:center;margin-top:2px;flex-wrap:wrap;}
      .wmeRcTimeHandTab{cursor:pointer;padding:7px 10px;border-radius:999px;border:1px solid rgba(255,255,255,.16);background:rgba(255,255,255,.06);
        font-size:12px;opacity:.92;user-select:none;transition:.16s ease;}
      .wmeRcTimeHandTab:hover{background:rgba(255,255,255,.10);transform:translate3d(0,-1px,0);}
      .wmeRcTimeHandTab:active{transform:translate3d(0,0,0);}
      .wmeRcTimeHandTab.on{border-color:rgba(255,255,255,.30);background:rgba(255,255,255,.14);box-shadow:0 0 0 4px rgba(255,255,255,.08);opacity:1;}      .wmeRcTimeDoneRow{display:flex;gap:10px;justify-content:center;align-items:center;margin-top:12px;width:100%;}
      .wmeRcTimeNowBtn{appearance:none;display:inline-flex;align-items:center;justify-content:center;line-height:1;
        height:34px;padding:0 12px;border-radius:12px;font-weight:950;cursor:pointer;
        border:1px solid rgba(255,160,60,.30) !important;
        background:var(--wmeRcPinGrad, linear-gradient(180deg, rgba(255,200,120,.92), rgba(255,140,0,.92))) !important;
        color:#fff !important;
        box-shadow:0 12px 26px rgba(0,0,0,.35);
        transition:filter .14s ease,transform .14s ease;}
      .wmeRcTimeNowBtn:hover{filter:brightness(1.05);transform:translate3d(0,-1px,0);}
      .wmeRcTimeNowBtn:active{transform:translate3d(0,0,0);filter:brightness(.98);}
      .wmeRcTimeDoneBtn{appearance:none;display:inline-flex;align-items:center;justify-content:center;line-height:1;
        height:34px;min-width:76px;padding:0 12px;
        border:1px solid rgba(255,255,255,.16) !important;
        background:rgba(255,255,255,.06) !important;
        color:#fff !important;border-radius:12px;font-weight:950;cursor:pointer;
        transition:background .14s ease,transform .14s ease,border-color .14s ease;}

            .wmeRcTimeResetBtn{min-width:110px;opacity:.9;}
.wmeRcTimeDoneBtn:hover{background:rgba(255,255,255,.10) !important;transform:translate3d(0,-1px,0);}
      .wmeRcTimeDoneBtn:active{transform:translate3d(0,0,0);background:rgba(255,255,255,.12) !important;}


      .wmeRcQuickPlus{font-weight:950;min-width:32px;justify-content:center;}
      .wmeRcQuickPop{position:fixed;left:0;top:0;z-index:2147483647;
        color:#fff;
        min-width:260px;max-width:320px;
        padding:12px;
        background:linear-gradient(180deg,rgba(22,22,26,.46),rgba(12,12,14,.36));
        border:1px solid rgba(255,255,255,.14);
        border-radius:16px;
        box-shadow:0 22px 60px rgba(0,0,0,.52);
        backdrop-filter:blur(28px) saturate(160%);
        -webkit-backdrop-filter:blur(28px) saturate(160%);
        animation:wmeRcPickerIn .16s ease-out;}
      .wmeRcQuickPopTitle{font-weight:950;letter-spacing:.2px;opacity:1;margin-bottom:10px;color:rgba(255,255,255,.92);}
      .wmeRcQuickPopRow{display:flex;gap:10px;align-items:center;}      .wmeRcQuickPopInput{flex:1;min-width:0;border-radius:12px;border:1px solid rgba(255,255,255,.14) !important;
        background:rgba(255,255,255,.05) !important;color:#fff !important;
        padding:0 12px;height:34px;line-height:34px;font-weight:900;outline:none;
        font-variant-numeric:tabular-nums;}
      .wmeRcQuickPopInput::placeholder{color:rgba(255,255,255,.55) !important;}      .wmeRcQuickPopUnits{display:flex;gap:6px;align-items:center;height:34px;}
      .wmeRcQuickPopUnit{appearance:none;display:inline-flex;align-items:center;justify-content:center;line-height:1;
        height:34px;min-width:40px;
        border:1px solid rgba(255,255,255,.16) !important;
        background:rgba(255,255,255,.06) !important;
        color:#fff !important;border-radius:12px;padding:0 12px;font-weight:950;cursor:pointer;
        transition:background .14s ease,transform .14s ease,border-color .14s ease;}

      .wmeRcQuickPopUnit:hover{background:rgba(255,255,255,.10) !important;transform:translate3d(0,-1px,0);}
      .wmeRcQuickPopUnit:active{transform:translate3d(0,0,0);background:rgba(255,255,255,.12) !important;}

      .wmeRcQuickPopUnit.sel{background:rgba(255,255,255,.14);border-color:rgba(255,255,255,.30);box-shadow:0 0 0 4px rgba(255,255,255,.08);}      .wmeRcQuickPopSet{appearance:none;display:inline-flex;align-items:center;justify-content:center;line-height:1;
        height:34px;padding:0 12px;border-radius:12px;font-weight:950;cursor:pointer;
        border:1px solid rgba(255,160,60,.30) !important;
        background:var(--wmeRcPinGrad, linear-gradient(180deg, rgba(255,200,120,.92), rgba(255,140,0,.92))) !important;
        color:#fff !important;
        box-shadow:0 12px 26px rgba(0,0,0,.35);
        transition:filter .14s ease,transform .14s ease,border-color .14s ease;}

      .wmeRcQuickPopSet:hover{filter:brightness(1.05);transform:translate3d(0,-1px,0);}
      .wmeRcQuickPopSet:active{transform:translate3d(0,0,0);filter:brightness(.98);}

      .wmeRcPinRow{
        padding:6px 8px 6px 10px;
        display:flex;align-items:center;justify-content:space-between;gap:8px;
        cursor:pointer;transition:.16s ease;
        position:relative;overflow:hidden;
        border-radius:10px;
        background:rgba(255,255,255,.08);
        border:none;
      }
      .wmeRcPinRow>*{position:relative;z-index:3;}

      .wmeRcPinRow::before{
        content:"";
        position:absolute;left:0;top:0;bottom:0;
        width:0;
        transition:none;
        background:rgb(var(--pinRGB,60,140,255));
        border-top-left-radius:10px;
        border-bottom-left-radius:10px;
        pointer-events:none;
        z-index:2;
      }

      .wmeRcPinRow::after{
        content:"";
        position:absolute;left:0;top:0;bottom:0;
        width:200px;
        background:linear-gradient(90deg,
          rgba(var(--pinRGB,60,140,255),.26) 0%,
          rgba(var(--pinRGB,60,140,255),.12) 35%,
          rgba(var(--pinRGB,60,140,255),0) 72%);
        pointer-events:none;
        z-index:1;
      }

      .wmeRcPinRow:hover{background:rgba(255,255,255,.11);}
      .wmeRcPinRow:hover .wmeRcPinLeft{transform:translateX(6px);}
      .wmeRcPinRow:hover::before{width:5px;}
      .wmeRcPinRow:active{background:rgba(255,255,255,.14);}
      .wmeRcPinLeft{display:flex;flex-direction:column;gap:2px;min-width:0;transition:transform .18s cubic-bezier(.2,.8,.2,1);}
      .wmeRcPinName{font-weight:800;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
      .wmeRcPinSub{font-size:10.5px;opacity:.65;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}

      .wmeRcPinBadName{
        display:inline-flex;align-items:center;justify-content:center;
        height:16px;padding:0 7px;border-radius:999px;
        font-size:10.5px;font-weight:950;letter-spacing:.12px;
        color:rgba(255,95,95,.95);
        border:1px solid rgba(255,95,95,.28);
        background:rgba(255,70,70,.10);
        width:fit-content;
        margin-bottom:4px;
      }
      .wmeRcPinNeedsRename .wmeRcPinName{color:rgba(255,210,210,.98);}

      .wmeRcTooltip{position:fixed;z-index:2147483647;pointer-events:none;opacity:0;transform:translateY(-6px) scale(.98);transition:opacity .14s ease, transform .14s ease;max-width:min(520px,calc(100vw - 24px));
        padding:8px 10px;border-radius:12px;background:rgba(16,16,18,.72);border:1px solid rgba(255,255,255,.14);backdrop-filter:blur(16px);
        color:#fff;font:500 12px/1.15 system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;letter-spacing:.2px;box-shadow:0 18px 34px rgba(0,0,0,.38);}
      .wmeRcTooltip.show{opacity:1;transform:translateY(0) scale(1);}
      .wmeRcPinBtns{display:flex;align-items:center;gap:6px;flex:0 0 auto;position:relative;justify-content:flex-end;width:34px;transition:width .22s cubic-bezier(.2,.9,.2,1);overflow:visible;}
      .wmeRcPinRow:hover .wmeRcPinBtns{width:160px;}

      .wmeRcPinDrag{width:26px;height:26px;border-radius:10px;border:1px solid rgba(255,255,255,.14);
        background:rgba(255,255,255,.04);display:flex;align-items:center;justify-content:center;cursor:grab;flex:0 0 auto;
        opacity:.75;transition:opacity .14s ease,background .14s ease,border-color .14s ease;user-select:none;touch-action:none;}
      .wmeRcPinRow:hover .wmeRcPinDrag{opacity:1;background:rgba(255,255,255,.06);border-color:rgba(255,255,255,.18);}
      .wmeRcPinDrag:active{cursor:grabbing;background:rgba(255,255,255,.08);}
      .wmeRcPinDrag .wmeRcI{width:16px;height:16px;display:flex;align-items:center;justify-content:center;}
      .wmeRcPinPlaceholder{height:36px;border-radius:12px;border:1px dashed rgba(255,255,255,.18);
        background:rgba(255,255,255,.03);margin:2px 0;}
      .wmeRcPinRow.reorderFloat{position:fixed !important;left:0;top:0;z-index:2147483646;
        box-shadow:0 24px 70px rgba(0,0,0,.55);backdrop-filter:blur(18px);-webkit-backdrop-filter:blur(18px);
        cursor:grabbing;}
      .wmeRcPinGhost{z-index:2147483646;box-shadow:0 24px 70px rgba(0,0,0,.55);backdrop-filter:blur(18px);-webkit-backdrop-filter:blur(18px);
        border-radius:12px;border:1px solid rgba(255,255,255,.16);}
      .wmeRcNoSelect, .wmeRcNoSelect *{user-select:none !important;-webkit-user-select:none !important;}
      .wmeRcLimitMsg{display:none;margin-top:8px;font-size:12px;font-weight:800;letter-spacing:.1px;
        color:rgba(255,95,95,.95);background:rgba(255,70,70,.10);border:1px solid rgba(255,70,70,.20);
        border-radius:12px;padding:7px 10px;}

.wmeRcLenHost{position:relative;flex:1 1 auto;min-width:0;display:flex;align-items:center;}
.wmeRcLenHost>.wmeRcInput{flex:1 1 auto;min-width:0;}
.wmeRcInput.wmeRcHasCount{padding-right:72px !important;}
.wmeRcLenCountIn{position:absolute;right:10px;top:50%;transform:translate3d(0,-50%,0);font-size:11px;font-weight:900;opacity:.62;pointer-events:none;font-variant-numeric:tabular-nums;letter-spacing:.2px;}
.wmeRcLenMeta{display:flex;align-items:center;justify-content:flex-end;margin-top:6px;font-size:11px;font-weight:900;opacity:.72;}
.wmeRcLenCount{font-variant-numeric:tabular-nums;letter-spacing:.2px;}
.wmeRcLenWrap .wmeRcLimitMsg{margin-top:6px;}
      .wmeRcPinBtn{cursor:pointer;user-select:none;width:28px;height:28px;border-radius:10px;border:1px solid rgba(255,255,255,.16);background:rgba(255,255,255,.06);display:flex;align-items:center;justify-content:center;transition:.16s ease;position:relative;}
      .wmeRcPinBtn:hover{background:rgba(255,255,255,.10);transform:translate3d(0,-1px,0);}
      .wmeRcPinBtn:active{background:rgba(255,255,255,.14);transform:translate3d(0,0,0);}
      .wmeRcPinBtnEye:hover{transform:none;}
      .wmeRcPinBtnEye:active{transform:none;}
      .wmeRcPinBtn .wmeRcI{width:16px;height:16px;display:flex;align-items:center;justify-content:center;}
      .wmeRcPinBtn svg{width:16px;height:16px;}
      .wmeRcPinBtnHoverOnly{
  z-index:2;
  opacity:0;
  pointer-events:none;
  position:absolute;
  top:0;
  transform:translate3d(10px,0,0) scale(.98);
  transition:
    opacity .16s ease,
    transform .22s cubic-bezier(.2,.9,.2,1),
    background .16s ease,
    box-shadow .16s ease,
    border-color .16s ease;
}

.wmeRcPinRow:hover .wmeRcPinBtnHoverOnly{
  opacity:1;
  pointer-events:auto;
  transform:translate3d(0,0,0) scale(1);
}

.wmeRcPinBtnEye{right:102px;}
.wmeRcPinBtnEdit{right:68px;}
.wmeRcPinBtnTrash{right:34px;}

.wmeRcPinBtnBell{
  position:absolute;
  right:0;
  top:0;
  z-index:3;
  will-change:transform;
}

.wmeRcPinRow:hover .wmeRcPinBtnHoverOnly.wmeRcPinBtnTrash{transition-delay:65ms;}
      .wmeRcPinBtnEye.isHidden::after{content:"";position:absolute;left:6px;right:6px;top:50%;height:2px;background:rgba(255,255,255,.92);transform:translateY(-50%) rotate(-32deg);border-radius:99px;box-shadow:0 2px 10px rgba(0,0,0,.35);opacity:.95;pointer-events:none;}

      .wmeRcPinRow.dragging{opacity:.55;}
      .wmeRcPinRow.dropBefore{box-shadow:inset 0 2px 0 rgba(255,255,255,.32);}
      .wmeRcPinRow.dropAfter{box-shadow:inset 0 -2px 0 rgba(255,255,255,.32);}
      .wmeRcPinDot{width:8px;height:8px;border-radius:99px;background:rgba(255,188,75,.9);box-shadow:0 0 0 4px rgba(255,188,75,.12);margin-left:6px;flex:0 0 auto;}
      .wmeRcPinDot.bell{width:20px;height:20px;border-radius:10px;border:1px solid rgba(255,188,75,.35);background:rgba(255,188,75,.12);box-shadow:0 0 0 4px rgba(255,188,75,.08);display:flex;align-items:center;justify-content:center;margin-left:6px;animation:wmeRcBellPulse 1.2s ease-in-out infinite;}

      .wmeRcSwitch{display:flex;align-items:center;gap:10px;cursor:pointer;user-select:none;}
      .wmeRcSwitch input{position:absolute;opacity:0;pointer-events:none;}
      .wmeRcSwitchTrack{width:38px;height:22px;border-radius:999px;background:rgba(255,255,255,.10);border:1px solid rgba(255,255,255,.16);display:inline-flex;align-items:center;padding:2px;transition:.18s ease;box-shadow:inset 0 0 0 999px rgba(0,0,0,.05);}
      .wmeRcSwitchThumb{width:18px;height:18px;border-radius:999px;background:rgba(255,255,255,.86);transform:translate3d(0,0,0);transition:.18s ease;box-shadow:0 6px 16px rgba(0,0,0,.25);}
      .wmeRcInlineToggle{display:flex;align-items:center;gap:10px;cursor:pointer;user-select:none;}
      .wmeRcInlineToggle input{position:absolute;opacity:0;pointer-events:none;}
      .wmeRcInlineToggle input:checked + .wmeRcSwitchTrack{background:rgba(255,160,60,.28);border-color:rgba(255,160,60,.55);box-shadow:inset 0 0 0 999px rgba(255,160,60,.08);}
      .wmeRcInlineToggle input:checked + .wmeRcSwitchTrack .wmeRcSwitchThumb{transform:translate3d(16px,0,0);}
      .wmeRcSwitchLabel{color:rgba(255,255,255,.90);font-size:12.5px;white-space:nowrap;}
      .wmeRcSwitch input:checked + .wmeRcSwitchTrack{background:rgba(255,160,60,.20);border-color:rgba(255,160,60,.32);}
      .wmeRcSwitch input:checked + .wmeRcSwitchTrack .wmeRcSwitchThumb{transform:translate3d(16px,0,0);}

      .wmeRcPanel, .wmeRcPanel *{-webkit-user-select:none !important;user-select:none !important;}
`;
    document.documentElement.appendChild(s);
  }







(function installInlineToggleClickPolicy(){
  if (UW.__wmeRcInlineToggleClickPolicyInstalled) return;
  UW.__wmeRcInlineToggleClickPolicyInstalled = true;

  const isOnSwitch = (e) => {
    const t = e && e.target;
    if (t && t.closest) {
      if (t.closest(".wmeRcSwitchTrack") || t.closest(".wmeRcSwitchThumb")) return true;
    }
    const path = (e && typeof e.composedPath === "function") ? e.composedPath() : null;
    if (path && path.length) {
      for (const n of path) {
        if (!n || !n.classList) continue;
        if (n.classList.contains("wmeRcSwitchTrack") || n.classList.contains("wmeRcSwitchThumb")) return true;
      }
    }
    return false;
  };

  const handler = (e) => {
    try {
      const t = e && e.target;
      const lbl = t && t.closest && t.closest("label.wmeRcInlineToggle");
      if (!lbl) return;

      const input = lbl.querySelector('input[type="checkbox"]');
      if (!input) return;


      if (isOnSwitch(e)) {

        e.preventDefault();
        e.stopPropagation();

        input.checked = !input.checked;
        try {
          input.dispatchEvent(new Event("change", { bubbles: true }));
        } catch {
          try { const ev = document.createEvent("Event"); ev.initEvent("change", true, false); input.dispatchEvent(ev); } catch {}
        }
        return;
      }


      e.preventDefault();
      e.stopPropagation();
    } catch {}
  };


  try { document.addEventListener("click", handler, true); } catch {}
})();





  function playReminderSoundOnce(soundId, opts = {}) {
    const nowMs = Date.now();
    const force = opts && opts.force === true;
    if (!force && (nowMs - lastBellAt < 700)) return;
    lastBellAt = nowMs;

    try {
      const AudioCtx = window.AudioContext || window.webkitAudioContext;
      if (!AudioCtx) return;

      const ctx = playReminderSoundOnce._ctx || (playReminderSoundOnce._ctx = new AudioCtx());
      try { if (ctx.state === "suspended") ctx.resume(); } catch {}

      const t0 = ctx.currentTime;
      const out = ctx.createGain();
      out.gain.setValueAtTime(0.0001, t0);
      out.connect(ctx.destination);

      const id = (typeof soundId === "string" ? soundId : "") || getReminderSoundId();
      if (id === "mute") return;


      const env = (attack, peak, decay) => {
        out.gain.cancelScheduledValues(t0);
        out.gain.setValueAtTime(0.0001, t0);
        out.gain.exponentialRampToValueAtTime(Math.max(0.0002, peak), t0 + attack);
        out.gain.exponentialRampToValueAtTime(0.0001, t0 + attack + decay);
      };

      const osc = (type, f0, f1, dur) => {
        const o = ctx.createOscillator();
        o.type = type;
        o.frequency.setValueAtTime(f0, t0);
        if (f1 && f1 > 0) {
          try { o.frequency.exponentialRampToValueAtTime(f1, t0 + dur); } catch {}
        }
        o.connect(out);
        o.start(t0);
        o.stop(t0 + dur + 0.02);
        return o;
      };

      if (id === "softBell") {
        env(0.02, 0.22, 1.1);
        osc("triangle", 784, 392, 1.05);
        osc("sine", 1175, 587, 0.9);
        return;
      }

      if (id === "chime") {
        env(0.01, 0.24, 0.75);
        osc("sine", 1046, 880, 0.65);
        osc("sine", 1568, 1320, 0.55);
        return;
      }

      if (id === "doubleDing") {
        env(0.008, 0.26, 0.28);
        osc("sine", 988, 740, 0.25);
        const t1 = t0 + 0.32;
        const out2 = ctx.createGain();
        out2.gain.setValueAtTime(0.0001, t1);
        out2.gain.exponentialRampToValueAtTime(0.22, t1 + 0.01);
        out2.gain.exponentialRampToValueAtTime(0.0001, t1 + 0.32);
        out2.connect(ctx.destination);
        const o2 = ctx.createOscillator();
        o2.type = "sine";
        o2.frequency.setValueAtTime(880, t1);
        o2.frequency.exponentialRampToValueAtTime(660, t1 + 0.28);
        o2.connect(out2);
        o2.start(t1);
        o2.stop(t1 + 0.34);
        return;
      }

      if (id === "digital") {
        env(0.002, 0.18, 0.22);
        osc("square", 880, 880, 0.18);
        const t1 = t0 + 0.22;
        const out2 = ctx.createGain();
        out2.gain.setValueAtTime(0.0001, t1);
        out2.gain.exponentialRampToValueAtTime(0.16, t1 + 0.002);
        out2.gain.exponentialRampToValueAtTime(0.0001, t1 + 0.2);
        out2.connect(ctx.destination);
        const o2 = ctx.createOscillator();
        o2.type = "square";
        o2.frequency.setValueAtTime(660, t1);
        o2.connect(out2);
        o2.start(t1);
        o2.stop(t1 + 0.22);
        return;
      }

      if (id === "alarm") {
        env(0.01, 0.22, 0.55);
        osc("sawtooth", 660, 440, 0.5);
        osc("sine", 990, 660, 0.45);
        return;
      }


      if (id === "alarmFast") {
        env(0.004, 0.18, 0.22);
        osc("square", 880, 880, 0.12);
        const t1 = t0 + 0.16;
        const g1 = ctx.createGain();
        g1.gain.setValueAtTime(0.0001, t1);
        g1.gain.exponentialRampToValueAtTime(0.16, t1 + 0.003);
        g1.gain.exponentialRampToValueAtTime(0.0001, t1 + 0.18);
        g1.connect(ctx.destination);
        const o1 = ctx.createOscillator();
        o1.type = "square";
        o1.frequency.setValueAtTime(880, t1);
        o1.connect(g1);
        o1.start(t1);
        o1.stop(t1 + 0.2);
        const t2 = t0 + 0.34;
        const g2 = ctx.createGain();
        g2.gain.setValueAtTime(0.0001, t2);
        g2.gain.exponentialRampToValueAtTime(0.16, t2 + 0.003);
        g2.gain.exponentialRampToValueAtTime(0.0001, t2 + 0.18);
        g2.connect(ctx.destination);
        const o2 = ctx.createOscillator();
        o2.type = "square";
        o2.frequency.setValueAtTime(740, t2);
        o2.connect(g2);
        o2.start(t2);
        o2.stop(t2 + 0.2);
        return;
      }

      if (id === "alarmPulse") {
        env(0.01, 0.20, 0.65);
        const o = ctx.createOscillator();
        o.type = "sawtooth";
        o.frequency.setValueAtTime(520, t0);
        o.frequency.linearRampToValueAtTime(780, t0 + 0.28);
        o.frequency.linearRampToValueAtTime(520, t0 + 0.56);
        o.connect(out);
        o.start(t0);
        o.stop(t0 + 0.60);
        return;
      }

      if (id === "buzzer") {
        env(0.003, 0.20, 0.35);
        osc("square", 220, 220, 0.28);
        osc("square", 330, 330, 0.22);
        return;
      }

      if (id === "radar") {
        env(0.004, 0.20, 0.55);
        osc("sine", 1568, 1046, 0.45);
        osc("triangle", 784, 523, 0.35);
        return;
      }

      if (id === "wood") {
        env(0.002, 0.18, 0.18);
        osc("triangle", 330, 220, 0.14);
        osc("triangle", 660, 440, 0.12);
        return;
      }

      if (id === "glass") {
        env(0.008, 0.22, 0.95);
        osc("sine", 1760, 880, 0.9);
        osc("sine", 2640, 1320, 0.75);
        return;
      }


      if (id === "church") {
        env(0.01, 0.26, 1.55);
        osc("sine", 659, 330, 1.5);
        osc("sine", 988, 494, 1.2);
        osc("triangle", 1319, 660, 1.0);
        return;
      }

      if (id === "retro") {
        env(0.002, 0.16, 0.18);
        osc("square", 880, 880, 0.10);
        const t1 = t0 + 0.13;
        const g1 = ctx.createGain();
        g1.gain.setValueAtTime(0.0001, t1);
        g1.gain.exponentialRampToValueAtTime(0.14, t1 + 0.002);
        g1.gain.exponentialRampToValueAtTime(0.0001, t1 + 0.12);
        g1.connect(ctx.destination);
        const o1 = ctx.createOscillator();
        o1.type = "square";
        o1.frequency.setValueAtTime(660, t1);
        o1.connect(g1);
        o1.start(t1);
        o1.stop(t1 + 0.14);

        const t2 = t0 + 0.28;
        const g2 = ctx.createGain();
        g2.gain.setValueAtTime(0.0001, t2);
        g2.gain.exponentialRampToValueAtTime(0.13, t2 + 0.002);
        g2.gain.exponentialRampToValueAtTime(0.0001, t2 + 0.12);
        g2.connect(ctx.destination);
        const o2 = ctx.createOscillator();
        o2.type = "square";
        o2.frequency.setValueAtTime(784, t2);
        o2.connect(g2);
        o2.start(t2);
        o2.stop(t2 + 0.14);
        return;
      }

      if (id === "siren") {
        env(0.01, 0.18, 0.75);
        const o = ctx.createOscillator();
        o.type = "sine";
        o.frequency.setValueAtTime(520, t0);
        o.frequency.linearRampToValueAtTime(740, t0 + 0.35);
        o.frequency.linearRampToValueAtTime(520, t0 + 0.7);
        o.connect(out);
        o.start(t0);
        o.stop(t0 + 0.72);
        return;
      }

      if (id === "gong") {
        env(0.01, 0.22, 1.8);
        osc("sine", 196, 98, 1.7);
        osc("triangle", 294, 147, 1.5);
        osc("sine", 392, 196, 1.2);
        return;
      }
      env(0.015, 0.28, 1.25);
      osc("sine", 880, 440, 1.2);
      osc("sine", 1320, 660, 1.0);
    } catch {}
  }


function startBellLoop() {
  try {
    if (bellLoopId) return;
    playReminderSoundOnce(getReminderSoundId());
    bellLoopId = window.setInterval(() => {
      try { playReminderSoundOnce(getReminderSoundId()); } catch {}
    }, 1600);
  } catch {}
}

function stopBellLoop() {
  try {
    if (bellLoopId) {
      window.clearInterval(bellLoopId);
      bellLoopId = null;
    }
  } catch {}
}

  function startPinsMapSync() {
    if (pinsMapSyncStarted) return;
    const ol = UW?.OpenLayers;
    const map = getOlMapBestEffort();
    if (!ol || !map || !map.events?.register) return;
    pinsMapSyncStarted = true;

    let scheduled = 0;
    const scheduleRender = () => {
      if (scheduled) return;
      scheduled = window.setTimeout(() => {
        scheduled = 0;
        try { renderPinsMarkers(); } catch {}
      }, 120);
    };

    try { map.events.register("moveend", map, scheduleRender); } catch {}
    try { map.events.register("zoomend", map, scheduleRender); } catch {}
    try { map.events.register("changelayer", map, scheduleRender); } catch {}

    try {
      map.events.register("mousemove", map, (evt) => {
        try {
          if (!startPinsMapSync._cursorRaf) startPinsMapSync._cursorRaf = 0;
          if (!startPinsMapSync._cursorHit) startPinsMapSync._cursorHit = false;
          if (!startPinsMapSync._cursorLast) startPinsMapSync._cursorLast = null;

          startPinsMapSync._cursorHit = (function() {
            const layer = ensurePinsOlLayer();
            if (layer && typeof layer.getMarkerFromEvent === "function") {
              const mk = layer.getMarkerFromEvent(evt);
              return !!(mk && (mk.pinId || mk.clusterId));
            }
            const t = evt?.target || evt?.srcElement;
            const el = t && t.closest ? t.closest(".wmeRcPinMarker, .wmeRcPinCluster, .wmeRcOlMarker") : null;
            return !!el;
          })();

          if (startPinsMapSync._cursorRaf) return;
          startPinsMapSync._cursorRaf = window.requestAnimationFrame(() => {
            startPinsMapSync._cursorRaf = 0;
            try {
              const div = map.div || map.viewPortDiv || map.layerContainerDiv;
              if (!div) return;
              const hit = !!startPinsMapSync._cursorHit;
              if (startPinsMapSync._cursorLast === hit) return;
              startPinsMapSync._cursorLast = hit;
              div.style.cursor = hit ? "pointer" : "";
            } catch {}
          });
        } catch {}
      });} catch {}
  }


  let _wmeRcPinHitPill = null;
  let _wmeRcPinHitPillTimer = null;

  function showPinHitPill(name) {
    try {
      ensureCSS();
      const label = String(name || "").trim() || "Pinned place";

      if (!_wmeRcPinHitPill || !_wmeRcPinHitPill.isConnected) {
        const el = document.createElement("div");
        el.className = "wmeRcPinHitPill";

        const t = document.createElement("div");
        t.className = "wmeRcPinHitText";
        t.textContent = label;

        const x = document.createElement("div");
        x.className = "wmeRcPinHitClose";
        x.title = "Close";
        x.textContent = "×";
        x.addEventListener("click", (ev) => {
          try { ev.stopPropagation(); ev.preventDefault(); } catch {}
          try { el.classList.remove("show"); } catch {}
          try { if (_wmeRcPinHitPillTimer) { clearTimeout(_wmeRcPinHitPillTimer); _wmeRcPinHitPillTimer = null; } } catch {}
        });

        el.appendChild(t);
        el.appendChild(x);
        (document.body || document.documentElement).appendChild(el);
        _wmeRcPinHitPill = el;
      }

      const txtEl = _wmeRcPinHitPill.querySelector(".wmeRcPinHitText");
      if (txtEl) txtEl.textContent = label;

      try { if (_wmeRcPinHitPillTimer) { clearTimeout(_wmeRcPinHitPillTimer); _wmeRcPinHitPillTimer = null; } } catch {}
      _wmeRcPinHitPill.classList.add("show");

      _wmeRcPinHitPillTimer = setTimeout(() => {
        try { _wmeRcPinHitPill && _wmeRcPinHitPill.classList.remove("show"); } catch {}
        try { _wmeRcPinHitPillTimer = null; } catch {}
      }, 2200);
    } catch {}
  }



  function toast(msg) {
    ensureCSS();
    const el = document.createElement("div");
    el.className = "wmeRcToast";
    el.textContent = msg;
    (document.body || document.documentElement).appendChild(el);
    requestAnimationFrame(() => el.classList.add("show"));
    setTimeout(() => {
      el.classList.remove("show");
      setTimeout(() => el.remove(), 250);
    }, 1400);
  }



function normalizePinColor(c) {
  try {
    if (typeof c !== "string") return "#ff8a00";
    let s = c.trim();
    if (!s) return "#ff8a00";
    if (s[0] !== "#") s = "#" + s;
    if (!/^#[0-9a-fA-F]{6}$/.test(s)) return "#ff8a00";
    return s.toLowerCase();
  } catch {
    return "#ff8a00";
  }
}

function hexToRgb(hex) {
  const h = normalizePinColor(hex).slice(1);
  const r = parseInt(h.slice(0,2), 16);
  const g = parseInt(h.slice(2,4), 16);
  const b = parseInt(h.slice(4,6), 16);
  return { r, g, b };
}

function mixRgb(a, b, t) {
  return {
    r: Math.round(a.r + (b.r - a.r) * t),
    g: Math.round(a.g + (b.g - a.g) * t),
    b: Math.round(a.b + (b.b - a.b) * t),
  };
}

function rgbaStr(rgb, a) {
  return `rgba(${rgb.r},${rgb.g},${rgb.b},${a})`;
}

function applyPinMarkerColors(el, hex) {
  const baseHex = normalizePinColor(hex);
  const base = hexToRgb(baseHex);
  const light = mixRgb(base, { r:255, g:255, b:255 }, 0.35);

  el.style.setProperty("--wmeRcPinGrad", `linear-gradient(180deg, ${rgbaStr(light, .95)}, ${rgbaStr(base, .95)})`);
  el.style.setProperty("--wmeRcPinTip", rgbaStr(base, .95));
  el.style.setProperty("--wmeRcPinGlow", rgbaStr(base, .34));
  el.style.setProperty("--wmeRcPinGlow2", rgbaStr(base, .22));
}


function upsertMapPinLabel(hostEl, pin, show) {
  try {
    if (!hostEl) return;
    const name = (pin && typeof pin.name === "string") ? pin.name.trim() : "";
    let lbl = null;
    try { lbl = hostEl.querySelector(":scope > .wmeRcPinLabel"); } catch { lbl = hostEl.querySelector(".wmeRcPinLabel"); }

    if (!show || !name) {
      if (lbl) lbl.style.display = "none";
      return;
    }

    if (!lbl) {
      lbl = document.createElement("div");
      lbl.className = "wmeRcPinLabel";
      hostEl.appendChild(lbl);
    }

    lbl.style.display = "block";
    lbl.textContent = name;
    lbl.title = name;

    try {
      const c = normalizePinColor((pin && pin.color) || "#ff8a00");
      const rgb = hexToRgb(c);
      if (rgb) {
        lbl.style.setProperty("--pinRGB", `${rgb.r},${rgb.g},${rgb.b}`);
        const luma = (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b) / 255;
        const fg = (luma > 0.86) ? "#111" : "#fff";
        const shadow = (fg === "#111")
          ? "0 6px 18px rgba(255,255,255,.18)"
          : "0 6px 18px rgba(0,0,0,.35)";
        lbl.style.setProperty("--wmeRcPinLabelFg", fg);
        lbl.style.setProperty("--wmeRcPinLabelShadow", shadow);
      }
    } catch {}
  } catch {}
}

function loadPins() {
  try {
    if (_pinsMem && Array.isArray(_pinsMem)) return _pinsMem;

    const raw = localStorage.getItem(PIN_KEY);
    _pinsLastJson = (typeof raw === "string") ? raw : _pinsLastJson;

    const arr = JSON.parse(raw || "[]");
    if (!Array.isArray(arr)) { _pinsMem = []; return _pinsMem; }

    _pinsMem = arr
      .filter((p) => p && Number.isFinite(Number(p.lon)) && Number.isFinite(Number(p.lat)))
      .map((p) => {
        const reminderAt = Number.isFinite(Number(p.reminderAt)) ? Number(p.reminderAt) : null;
        const reminderDone = !!p.reminderDone;
        const reminderFiredAt = Number.isFinite(Number(p.reminderFiredAt)) ? Number(p.reminderFiredAt) : null;

        const reminderType = (p.reminderType === "IN" || p.reminderType === "AT") ? p.reminderType : null;
        const reminderUnit = (p.reminderUnit === "MINUTES" || p.reminderUnit === "HOURS") ? p.reminderUnit : null;
        const reminderValue = Number.isFinite(Number(p.reminderValue)) ? Number(p.reminderValue) : null;
        const reminderNote = (typeof p.reminderNote === "string") ? p.reminderNote : "";

        return ({
          id: String(p.id || ""),
          name: String(p.name || "Pinned place"),
          lon: Number(p.lon),
          lat: Number(p.lat),
          zoom: Number.isFinite(Number(p.zoom)) ? Number(p.zoom) : null,
          createdAt: Number.isFinite(Number(p.createdAt)) ? Number(p.createdAt) : Date.now(),
          color: normalizePinColor(p.color),
          groupId: normalizeGroupId(p.groupId),
          hideOnMap: p.hideOnMap === true,

          reminderAt,
          reminderDone: reminderAt ? reminderDone : false,
          reminderFiredAt: reminderAt ? reminderFiredAt : null,
          reminderType,
          reminderUnit,
          reminderValue,
          reminderNote,
        });
      })
      .filter((p) => p.id);

    return _pinsMem;
  } catch {
    _pinsMem = _pinsMem && Array.isArray(_pinsMem) ? _pinsMem : [];
    return _pinsMem;
  }
}

function savePins(pins) {
  try {
    _pinsMem = Array.isArray(pins) ? pins : [];
    _schedulePinsSaveNow(_pinsMem);
  } catch {}
}

function getCurrentZoomBestEffort() {
  try {
    const z = sdk?.Map?.getZoom?.();
    if (Number.isFinite(z)) return Number(z);
  } catch {}
  try {
    const z = UW?.W?.map?.getZoom?.();
    if (Number.isFinite(z)) return Number(z);
  } catch {}
  return null;
}

function ensurePinsPanel() {
  ensureCSS();
  const mount = () => {
    const mapEl =
      document.querySelector("#map") ||
      document.querySelector("#WazeMap") ||
      document.querySelector(".olMap") ||
      document.querySelector(".wme-map") ||
      null;
    if (!mapEl) return false;

    try {
      const cs = getComputedStyle(mapEl);
      if (cs.position === "static") mapEl.style.position = "relative";
    } catch {}

    if (!pinsPanelEl || !document.contains(pinsPanelEl)) {
      const el = document.createElement("div");
      el.className = "wmeRcPins hidden";

      try {
        el.addEventListener("wheel", (ev) => { try { ev.stopPropagation(); } catch {} }, { passive: true, capture: true });
      } catch {}

      try {
        const pos = JSON.parse(localStorage.getItem(PIN_POS_KEY) || "null");
        if (pos && Number.isFinite(pos.left) && Number.isFinite(pos.top)) {
          el.style.left = `${Math.max(0, pos.left)}px`;
          el.style.top  = `${Math.max(0, pos.top)}px`;
        }
      } catch {}

      try {
        const sz = loadPinsPanelSize();
        if (sz && Number.isFinite(sz.h)) {
          _pinsPanelLastAutoAt = _pinsNow();
          _pinsPanelSuppressROUntil = _pinsPanelLastAutoAt + 900;
          el.style.height = `${Math.round(sz.h)}px`;
          if (sz.manual) el.dataset.manualHeight = "1";
        }
      } catch {}


      try {
        if (loadPinsPanelCollapsed()) {
          el.classList.add("collapsed");
          el.dataset.collapsed = "1";
          try { el.style.resize = "none"; } catch {}
        }
      } catch {}

      const stop = (ev) => { try { ev.stopPropagation(); } catch {} };
      el.addEventListener("mousedown", stop, false);
      el.addEventListener("click", stop, false);
      el.addEventListener("dblclick", stop, false);
      el.addEventListener("contextmenu", stop, false);
      el.addEventListener("touchstart", stop, false);

      el.addEventListener("pointerdown", (ev) => {
        try {
          const hdr = ev.target && ev.target.closest && ev.target.closest(".wmeRcPinsHdr");
          if (!hdr) return;
          if (ev.target && ev.target.closest && (ev.target.closest(".wmeRcSelect") || ev.target.closest(".wmeRcPinsHdrBtn"))) return;
          if (ev.button != null && ev.button !== 0) return;
          ev.preventDefault();
          stop(ev);

          const rect = el.getBoundingClientRect();
          const mapRect = mapEl.getBoundingClientRect();
          pinsDrag.active = true;
          pinsDrag.pointerId = ev.pointerId;
          pinsDrag.startX = ev.clientX;
          pinsDrag.startY = ev.clientY;
          pinsDrag.baseLeft = rect.left - mapRect.left;
          pinsDrag.baseTop  = rect.top  - mapRect.top;

          try { el.setPointerCapture(ev.pointerId); } catch {}
          document.addEventListener("pointermove", onPinsDragMove, true);
          document.addEventListener("pointerup", onPinsDragUp, true);
          document.addEventListener("pointercancel", onPinsDragUp, true);
        } catch {}
      }, false);

      mapEl.appendChild(el);
      pinsPanelEl = el;
      try { _ensurePinsPanelDockObserver(); } catch {}

      try {
        if (!_pinsPanelRO && typeof ResizeObserver !== "undefined") {
          _pinsPanelRO = new ResizeObserver(() => {
            try {
              if (!pinsPanelEl || !document.contains(pinsPanelEl)) return;
              if (pinsPanelEl.classList.contains("collapsed") || (pinsPanelEl.dataset && pinsPanelEl.dataset.collapsed === "1")) return;
              if (_pinsPanelAutoSizing) return;
              if (_pinsNow() < _pinsPanelSuppressROUntil) return;
              const h = Math.round(pinsPanelEl.getBoundingClientRect().height || 0);
              if (!Number.isFinite(h) || h <= 0) return;
              savePinsPanelSize({ h, manual: true, u: 1 });
              pinsPanelEl.dataset.manualHeight = "1";
            } catch {}
          });
          _pinsPanelRO.observe(el);
        }
      } catch {}

    }
    renderPinsPanel();
    try { applyPinsPanelMinimizedOnLoad(); } catch {}
    try { _syncPinsBubbleVisibility(); } catch {}
    try { updatePinsPanelHeightBounds(); } catch {}
    if (!_pinsPanelResizeBound) {
      _pinsPanelResizeBound = true;
      try { window.addEventListener("resize", () => { try { updatePinsPanelHeightBounds(); } catch {} }, true); } catch {}
    }
    startReminderLoop();
    try { scheduleAllReminderTimers(); } catch {}
    return true;
  };

  if (mount()) return;
  const t = setInterval(() => { if (mount()) clearInterval(t); }, 650);
  setTimeout(() => clearInterval(t), 16000);
}


function clamp(n, min, max) { return Math.max(min, Math.min(max, n)); }

function loadPinsPanelSize() {
  try {
    const raw = localStorage.getItem(PIN_PANEL_SIZE_KEY);
    const obj = JSON.parse(raw || "null");
    if (!obj || typeof obj !== "object") return null;
    const h = Number(obj.h);
    const manualRaw = !!obj.manual;
    const u = Number(obj.u || 0);
    const manual = manualRaw && (u === 1);
    if (!Number.isFinite(h) || h <= 0) return null;
    return { h, manual };
  } catch { return null; }
}
function savePinsPanelSize(o) {
  try { localStorage.setItem(PIN_PANEL_SIZE_KEY, JSON.stringify(o || {})); } catch {}
}

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

function loadPinsPanelMinimizedRaw() {
  try {
    const v = localStorage.getItem(PIN_PANEL_MINIMIZED_KEY);
    if (v === null) return null;
    return v === "1";
  } catch { return null; }
}

function savePinsPanelMinimized(v) {
  try { localStorage.setItem(PIN_PANEL_MINIMIZED_KEY, v ? "1" : "0"); } catch {}
}


function applyPinsPanelMinimizedOnLoad() {
  try {
    if (!pinsPanelEl || !document.contains(pinsPanelEl)) return;

    const mode = getPinsMinimizeMode();
    const minimizedRaw = loadPinsPanelMinimizedRaw();
    const minimized = (minimizedRaw === true);

    if (mode !== "bubble") {

      try { _hidePinsBubble(true); } catch {}

      try {
        if (pinsPanelEl.style && pinsPanelEl.style.display === "none") pinsPanelEl.style.display = "block";
      } catch {}
      try { savePinsPanelMinimized(false); } catch {}
      return;
    }


    if (minimized) {
      const mapEl = pinsPanelEl.parentElement || _getMapElForPins();
      try { _ensurePinsBubble(mapEl); } catch {}
      try { pinsPanelEl.style.display = "none"; } catch {}
      try { _showPinsBubble(false); } catch {}
    } else {

      try {
        if (pinsPanelEl.style && pinsPanelEl.style.display === "none") pinsPanelEl.style.display = "block";
      } catch {}
      try { _hidePinsBubble(true); } catch {}
    }
  } catch {}
}

function getPinsMinimizeMode() {
  try {
    const v = String(localStorage.getItem(PIN_MINIMIZE_MODE_KEY) || "");
    return (v === "panel" || v === "bubble") ? v : "bubble";
  } catch { return "bubble"; }
}
function setPinsMinimizeMode(mode) {
  try {
    const v = (mode === "panel") ? "panel" : "bubble";
    localStorage.setItem(PIN_MINIMIZE_MODE_KEY, v);
  } catch {}
}

function loadPinsBubblePos() {
  try {
    const raw = localStorage.getItem(PIN_BUBBLE_POS_KEY);
    const obj = JSON.parse(raw || "null");
    if (!obj || typeof obj !== "object") return null;
    const left = Number(obj.left);
    const top = Number(obj.top);
    if (!Number.isFinite(left) || !Number.isFinite(top)) return null;
    return { left, top };
  } catch { return null; }
}

function savePinsBubblePos(pos) {
  try {
    if (!pos || !Number.isFinite(Number(pos.left)) || !Number.isFinite(Number(pos.top))) return;
    localStorage.setItem(PIN_BUBBLE_POS_KEY, JSON.stringify({ left: Math.round(pos.left), top: Math.round(pos.top) }));
  } catch {}
}


function _getMapElForPins() {
  try { return getMapContainerEl(); } catch { return null; }
}

function _isPinsPanelMinimizedNow() {
  try {



    if (!pinsPanelEl || !document.contains(pinsPanelEl)) return true;


    try { if (loadPinsPanelMinimizedRaw() === true) return true; } catch {}


    if (pinsPanelEl.style && pinsPanelEl.style.display === "none") return true;

    return false;
  } catch { return false; }
}

function _syncPinsBubbleVisibility() {
  try {
    const mode = getPinsMinimizeMode();
    if (mode !== "bubble") {
      _hidePinsBubble(true);
      return;
    }

    try {
      if (_pinsBubbleForceHideUntil && _pinsNow() < _pinsBubbleForceHideUntil) {
        if (!_isPinsPanelMinimizedNow()) { _hidePinsBubble(true); return; }
      }
    } catch {}
    if (_isPinsPanelMinimizedNow()) _showPinsBubble(false);
    else _hidePinsBubble(true);
  } catch {}
}

function onPinsBubbleDragMove(ev) {
  try {
    if (!pinsBubbleDrag.active) return;
    if (pinsBubbleDrag.pointerId != null && ev.pointerId != null && ev.pointerId !== pinsBubbleDrag.pointerId) return;

    const mapEl = _getMapElForPins();
    if (!mapEl || !pinsBubbleEl || !document.contains(pinsBubbleEl)) return;
    const mapRect = mapEl.getBoundingClientRect();

    const dx = ev.clientX - pinsBubbleDrag.startX;
    const dy = ev.clientY - pinsBubbleDrag.startY;
    const left = Math.round(pinsBubbleDrag.baseLeft + dx);
    const top  = Math.round(pinsBubbleDrag.baseTop + dy);

    const maxLeft = Math.max(0, Math.round(mapRect.width - 46));
    const maxTop  = Math.max(0, Math.round(mapRect.height - 46));

    const cl = clamp(left, 0, maxLeft);
    const ct = clamp(top, 0, maxTop);

    pinsBubbleEl.style.left = cl + "px";
    pinsBubbleEl.style.top  = ct + "px";

    if (!pinsBubbleDrag.moved) {
      if (Math.abs(dx) > 3 || Math.abs(dy) > 3) {
        pinsBubbleDrag.moved = true;
        pinsBubbleDrag.movedAt = Date.now();
      }
    }
    savePinsBubblePos({ left: cl, top: ct });
  } catch {}
}

function onPinsBubbleDragUp(ev) {
  try {
    if (!pinsBubbleDrag.active) return;
    if (pinsBubbleDrag.pointerId != null && ev.pointerId != null && ev.pointerId !== pinsBubbleDrag.pointerId) return;

    pinsBubbleDrag.active = false;
    const moved = !!pinsBubbleDrag.moved;
    const movedAt = Number(pinsBubbleDrag.movedAt || 0);

    try { document.removeEventListener("pointermove", onPinsBubbleDragMove, true); } catch {}
    try { document.removeEventListener("pointerup", onPinsBubbleDragUp, true); } catch {}
    try { document.removeEventListener("pointercancel", onPinsBubbleDragUp, true); } catch {}
    try { if (pinsBubbleEl && ev.pointerId != null) pinsBubbleEl.releasePointerCapture(ev.pointerId); } catch {}


    if (!moved) {
      try { _restorePinsPanelFromBubble(); } catch {}
      return;
    }


    pinsBubbleDrag.movedAt = movedAt || Date.now();
  } catch {}
}

function _ensurePinsBubble(mapEl) {
  try {
    if (!mapEl) mapEl = _getMapElForPins();
    if (!mapEl) return null;
    if (pinsBubbleEl && document.contains(pinsBubbleEl)) return pinsBubbleEl;

    const b = document.createElement("div");
    b.className = "wmeRcPinsBubble";
    b.title = "Map Pins";
    b.innerHTML = `<span class="wmeRcI">${ICONS.mapPin}</span>`;

    const pos = loadPinsBubblePos() || null;
    if (pos) {
      b.style.left = `${Math.max(0, Math.round(pos.left))}px`;
      b.style.top  = `${Math.max(0, Math.round(pos.top))}px`;
    } else {
      b.style.left = "12px";
      b.style.top  = "12px";
    }

    const stop = (ev) => { try { ev.stopPropagation(); } catch {} };
    b.addEventListener("mousedown", stop, false);
    b.addEventListener("click", stop, false);
    b.addEventListener("dblclick", stop, false);
    b.addEventListener("contextmenu", stop, false);
    b.addEventListener("touchstart", stop, false);

    b.addEventListener("pointerdown", (ev) => {
      try {
        if (ev.button != null && ev.button !== 0) return;
        ev.preventDefault();
        stop(ev);

        const map = _getMapElForPins();
        if (!map) return;
        const mapRect = map.getBoundingClientRect();
        const r = b.getBoundingClientRect();

        pinsBubbleDrag.active = true;
        pinsBubbleDrag.pointerId = ev.pointerId;
        pinsBubbleDrag.startX = ev.clientX;
        pinsBubbleDrag.startY = ev.clientY;
        pinsBubbleDrag.baseLeft = r.left - mapRect.left;
        pinsBubbleDrag.baseTop  = r.top  - mapRect.top;
        pinsBubbleDrag.moved = false;
        pinsBubbleDrag.movedAt = 0;

        try { b.setPointerCapture(ev.pointerId); } catch {}
        document.addEventListener("pointermove", onPinsBubbleDragMove, true);
        document.addEventListener("pointerup", onPinsBubbleDragUp, true);
        document.addEventListener("pointercancel", onPinsBubbleDragUp, true);
      } catch {}
    }, true);

    mapEl.appendChild(b);
    pinsBubbleEl = b;

    _hidePinsBubble(true);
    return b;
  } catch { return null; }
}

function _showPinsBubble(withPulse) {
  try {
    const mapEl = _getMapElForPins();
    const b = _ensurePinsBubble(mapEl);



    try { _pinsBubbleForceHideUntil = 0; } catch {}
    try { if (_pinsBubbleHideT) { clearTimeout(_pinsBubbleHideT); _pinsBubbleHideT = 0; } } catch {}
    if (!b) return;
    b.style.display = "flex";
    try { b.classList.add("show"); } catch {}
    try { b.classList.toggle("wmeRcPinsBubblePulse", !!withPulse); } catch {}
  } catch {}
}

function _hidePinsBubble(instant) {
  try {
    if (!pinsBubbleEl || !document.contains(pinsBubbleEl)) return;
    const b = pinsBubbleEl;
    try { b.classList.remove("wmeRcPinsBubblePulse"); } catch {}
    if (instant) {
      try { b.classList.remove("show"); } catch {}
      b.style.display = "none";
      return;
    }
    try { b.classList.remove("show"); } catch {}
    if (_pinsBubbleHideT) { try { clearTimeout(_pinsBubbleHideT); } catch {} }
    _pinsBubbleHideT = setTimeout(() => {
      _pinsBubbleHideT = 0;
      try { if (b && document.contains(b)) b.style.display = "none"; } catch {}
    }, 230);
  } catch {}
}

function _setPinsPanelCollapsedFlag(isCollapsed) {
  try {
    if (!pinsPanelEl) return;
    if (isCollapsed) {
      pinsPanelEl.classList.add("collapsed");
      pinsPanelEl.dataset.collapsed = "1";
      try { pinsPanelEl.style.resize = "none"; } catch {}
      savePinsPanelCollapsed(true);
    } else {
      pinsPanelEl.classList.remove("collapsed");
      try { delete pinsPanelEl.dataset.collapsed; } catch {}
      try { pinsPanelEl.style.resize = "vertical"; } catch {}
      savePinsPanelCollapsed(false);
    }
  } catch {}
}

function _minimizePinsPanelInPlace(opts) {
  try {
    if (!pinsPanelEl || !document.contains(pinsPanelEl)) return;


    try {
      pinsPanelEl.classList.remove("animOut");
      pinsPanelEl.classList.remove("animIn");
      pinsPanelEl.style.display = "block";
      pinsPanelEl.style.opacity = "";
      pinsPanelEl.style.transform = "";
      pinsPanelEl.style.pointerEvents = "";
      pinsPanelEl.style.transition = "";
    } catch {}

    const instant = !!(opts && opts.instant);


    try {
      const h = Math.round(pinsPanelEl.getBoundingClientRect().height || 0);
      if (h > 0) pinsPanelEl.dataset.prevExpandedHeight = String(h);
    } catch {}

    _setPinsPanelCollapsedFlag(true);
    try { savePinsPanelMinimized(false); } catch {}
    try { renderPinsPanel(); } catch {}


    try {
      pinsPanelEl.style.display = "block";
      const hdr = pinsPanelEl.querySelector(".wmeRcPinsHdr");
      const targetH = Math.max(44, Math.round((hdr ? hdr.getBoundingClientRect().height : 44) || 44));

      if (instant) {
        pinsPanelEl.style.height = targetH + "px";
      } else {
        const startH = Math.max(targetH, Math.round(pinsPanelEl.getBoundingClientRect().height || targetH));
        pinsPanelEl.style.height = startH + "px";
        pinsPanelEl.style.transition = "height .22s cubic-bezier(.2,.9,.2,1)";
        requestAnimationFrame(() => {
          try { pinsPanelEl.style.height = targetH + "px"; } catch {}
        });
        setTimeout(() => {
          try { if (pinsPanelEl) pinsPanelEl.style.transition = ""; } catch {}
        }, 240);
      }
    } catch {}

    _hidePinsBubble(true);
    try { _schedulePinsPanelAvoidDock(); } catch {}
  } catch {}
}

function _restorePinsPanelFromPanelCollapse() {
  try {
    if (!pinsPanelEl || !document.contains(pinsPanelEl)) return;

    _setPinsPanelCollapsedFlag(false);
    try { savePinsPanelMinimized(false); } catch {}


    try {
      const sz = loadPinsPanelSize();
      const prev = parseInt(String(pinsPanelEl.dataset.prevExpandedHeight || ""), 10);
      const target = (sz && sz.h) ? Math.round(sz.h) : (Number.isFinite(prev) ? prev : 260);

      const startH = Math.max(44, Math.round(pinsPanelEl.getBoundingClientRect().height || 44));
      pinsPanelEl.style.height = startH + "px";
      pinsPanelEl.style.transition = "height .24s cubic-bezier(.2,.9,.2,1)";
      requestAnimationFrame(() => {
        try { pinsPanelEl.style.height = target + "px"; } catch {}
      });
      setTimeout(() => {
        try { if (pinsPanelEl) pinsPanelEl.style.transition = ""; } catch {}
      }, 260);
    } catch {
      try { pinsPanelEl.style.height = ""; } catch {}
    }

    try { renderPinsPanel(); } catch {}
    try { _schedulePinsPanelAvoidDock(); } catch {}
  } catch {}
}


function _minimizePinsPanelToBubble(opts) {
  try {
    if (getPinsMinimizeMode() !== "bubble") { _minimizePinsPanelInPlace(opts); return; }

    if (!pinsPanelEl || !document.contains(pinsPanelEl)) return;
    const mapEl = pinsPanelEl.parentElement || _getMapElForPins();
    if (!mapEl) return;

    _ensurePinsBubble(mapEl);


    try {
      const mapRect = mapEl.getBoundingClientRect();
      const r = pinsPanelEl.getBoundingClientRect();
      const left = Math.round(r.left - mapRect.left);
      const top  = Math.round(r.top  - mapRect.top);
      const existing = loadPinsBubblePos();
      if (!existing) savePinsBubblePos({ left, top });
      if (pinsBubbleEl) {
        const p = loadPinsBubblePos() || { left, top };
        pinsBubbleEl.style.left = `${Math.max(0, Math.round(p.left))}px`;
        pinsBubbleEl.style.top  = `${Math.max(0, Math.round(p.top))}px`;
      }
    } catch {}

    const instant = !!(opts && opts.instant);

    _setPinsPanelCollapsedFlag(true);
    try { savePinsPanelMinimized(true); } catch {}
    try { renderPinsPanel(); } catch {}


    try {
      if (!instant) {
        pinsPanelEl.style.transition = "opacity .22s ease, transform .22s cubic-bezier(.2,.9,.2,1)";
        pinsPanelEl.classList.add("animOut");
        try { if (_pinsPanelMinimizeHideT) { clearTimeout(_pinsPanelMinimizeHideT); _pinsPanelMinimizeHideT = 0; } } catch {}
        _pinsPanelMinimizeHideT = setTimeout(() => {
          _pinsPanelMinimizeHideT = 0;
          try { if (pinsPanelEl) { pinsPanelEl.style.display = "none"; pinsPanelEl.classList.remove("animOut"); pinsPanelEl.style.transition = ""; } } catch {}
        }, 240);
      } else {
        pinsPanelEl.style.display = "none";
      }
    } catch {}

    _showPinsBubble(true);
  } catch {}
}

function _restorePinsPanelFromBubble() {
  try {
    if (!pinsPanelEl || !document.contains(pinsPanelEl)) return;


    try { if (_pinsPanelMinimizeHideT) { clearTimeout(_pinsPanelMinimizeHideT); _pinsPanelMinimizeHideT = 0; } } catch {}


    try { _pinsBubbleForceHideUntil = _pinsNow() + 600; } catch { _pinsBubbleForceHideUntil = Date.now() + 600; }



    try {
      const mapEl = _getMapElForPins();
      if (mapEl) {
        const mapRect = mapEl.getBoundingClientRect();
        const pad = 6;
        const panelRect = pinsPanelEl.getBoundingClientRect();
        const w = Math.round(panelRect.width || 260);

        let target = null;
        try {
          const saved = JSON.parse(localStorage.getItem(PIN_POS_KEY) || "null");
          if (saved && Number.isFinite(saved.left) && Number.isFinite(saved.top)) {
            target = { left: saved.left, top: saved.top };
          }
        } catch {}

        if (!target && pinsBubbleEl && document.contains(pinsBubbleEl)) {
          const br = pinsBubbleEl.getBoundingClientRect();
          target = { left: Math.round(br.left - mapRect.left), top: Math.round(br.top - mapRect.top) };
        }

        if (target) {
          const maxLeft = Math.max(pad, Math.floor(mapRect.width - w - pad));
          const maxTop = Math.max(pad, Math.floor(mapRect.height - 60));
          const left = clamp(Math.round(target.left), pad, maxLeft);
          const top  = clamp(Math.round(target.top),  pad, maxTop);
          pinsPanelEl.style.left = `${left}px`;
          pinsPanelEl.style.top  = `${top}px`;


          try {
            if (target.left !== left || target.top !== top) {
              localStorage.setItem(PIN_POS_KEY, JSON.stringify({ left, top }));
            }
          } catch {}
        }
      }
    } catch {}


    try { _pinsPanelSuppressDockUntil = _pinsNow() + 900; } catch {}


    try { _hidePinsBubble(true); } catch {}

    _setPinsPanelCollapsedFlag(false);
    try { savePinsPanelMinimized(false); } catch {}


    try { renderPinsPanel(); } catch {}

    pinsPanelEl.style.display = "block";
    try {
      pinsPanelEl.style.transition = "opacity .24s ease, transform .24s cubic-bezier(.2,.9,.2,1)";
      pinsPanelEl.classList.add("animOut");
      requestAnimationFrame(() => {
        try {
          pinsPanelEl.classList.remove("animOut");
          pinsPanelEl.classList.add("animIn");
          setTimeout(() => {
            try {
              pinsPanelEl.classList.remove("animIn");
              pinsPanelEl.style.transition = "";
            } catch {}
          }, 260);
        } catch {}
      });
    } catch {}

    try { _schedulePinsPanelAvoidDock(); } catch {}
  } catch {}
}


function savePinsPanelCollapsed(v) {
  try { localStorage.setItem(PIN_PANEL_COLLAPSED_KEY, v ? "1" : "0"); } catch {}
}

function getPinsPanelAlwaysVisibleEmpty() {
  try { return localStorage.getItem(PIN_PANEL_ALWAYS_VISIBLE_EMPTY_KEY) === "1"; } catch { return false; }
}
function setPinsPanelAlwaysVisibleEmpty(v) {
  try { localStorage.setItem(PIN_PANEL_ALWAYS_VISIBLE_EMPTY_KEY, v ? "1" : "0"); } catch {}
}

let _pinsPanelRO = null;
let _pinsPanelAutoSizing = false;

function _pinsPanelGetMapRect() {
  try {
    const mapEl = getMapContainerEl();
    if (!mapEl) return null;
    const mapRect = mapEl.getBoundingClientRect();
    if (!mapRect || !Number.isFinite(mapRect.width) || !Number.isFinite(mapRect.height)) return null;
    return { mapEl, mapRect };
  } catch { return null; }
}

function _pinsPanelFitTopForHeight(desiredH) {
  try {
    if (!pinsPanelEl || !document.contains(pinsPanelEl)) return;
    const info = _pinsPanelGetMapRect();
    if (!info) return;
    const { mapRect } = info;

    const pad = 6;
    const topLimit = pad;
    const bottomLimit = Math.max(pad, Math.floor(mapRect.height - pad));

    let h = Number(desiredH);
    if (!Number.isFinite(h) || h <= 0) {
      h = Math.round(pinsPanelEl.getBoundingClientRect().height || 0);
    }
    const maxPossible = Math.max(160, bottomLimit - topLimit);
    h = clamp(Math.round(h), 140, maxPossible);

    const rect = pinsPanelEl.getBoundingClientRect();
    let top = parseFloat(String(pinsPanelEl.style.top || ""));
    if (!Number.isFinite(top)) top = rect.top - mapRect.top;

    const needTop = bottomLimit - h;
    if (top > needTop) top = needTop;
    if (top < topLimit) top = topLimit;

    pinsPanelEl.style.top = `${Math.round(top)}px`;
  } catch {}
}

function applyPinsPanelHeightAuto() {
  try {
    if (!pinsPanelEl || !document.contains(pinsPanelEl)) return;
    if (pinsPanelEl.classList.contains("collapsed") || (pinsPanelEl.dataset && pinsPanelEl.dataset.collapsed === "1")) return;

    updatePinsPanelHeightBounds();

    const maxH = parseInt(String(pinsPanelEl.style.maxHeight || ""), 10);
    const maxPx = Number.isFinite(maxH) ? maxH : (window.innerHeight - 24);

    _pinsPanelLastAutoAt = _pinsNow();
    _pinsPanelSuppressROUntil = _pinsPanelLastAutoAt + 650;

    _pinsPanelAutoSizing = true;
    pinsPanelEl.style.height = "auto";
    const desired = clamp(Math.round(pinsPanelEl.scrollHeight || 0), 140, maxPx);
    pinsPanelEl.style.height = `${desired}px`;
    savePinsPanelSize({ h: desired, manual: false, u: 0 });
    try { _pinsPanelFitTopForHeight(desired); } catch {}
  } catch {}
  finally { _pinsPanelAutoSizing = false; }
}

function updatePinsPanelHeightBounds() {
  try {
    if (!pinsPanelEl || !document.contains(pinsPanelEl)) return;
    const info = _pinsPanelGetMapRect();
    if (!info) return;
    const { mapRect } = info;

    const pad = 6;
    const topLimit = pad;
    const bottomLimit = Math.max(pad, Math.floor(mapRect.height - pad));
    const maxH = Math.max(160, bottomLimit - topLimit);

    pinsPanelEl.style.maxHeight = `${maxH}px`;

    try {
      const manual = (pinsPanelEl.dataset && pinsPanelEl.dataset.manualHeight === "1");
      const curH = Math.round(pinsPanelEl.getBoundingClientRect().height || 0);
      if (curH > maxH) {
        _pinsPanelLastAutoAt = _pinsNow();
        _pinsPanelSuppressROUntil = _pinsPanelLastAutoAt + 650;
        _pinsPanelAutoSizing = true;
        pinsPanelEl.style.height = `${maxH}px`;
        savePinsPanelSize({ h: maxH, manual: manual, u: manual ? 1 : 0 });
      }
      _pinsPanelFitTopForHeight(Math.min(curH || maxH, maxH));
    } catch {} finally { _pinsPanelAutoSizing = false; }
  } catch {}
}

let _pinsPanelResizeBound = false;
function attachMaxLen(inputEl, maxLen = 32) {
  try {
    if (!inputEl) return null;

    const wrap = document.createElement("div");
    wrap.className = "wmeRcLenHost";

    try { inputEl.classList.add("wmeRcHasCount"); } catch {}

    const count = document.createElement("div");
    count.className = "wmeRcLenCountIn";
    count.textContent = `0/${maxLen}`;

    const msg = document.createElement("div");
    msg.className = "wmeRcLimitMsg";
    msg.textContent = `You can't enter more than ${maxLen} characters`;
    msg.style.display = "none";

    wrap.appendChild(inputEl);
    wrap.appendChild(count);

    let overAttempt = false;

    const update = () => {
      const v = String(inputEl.value || "");
      const shownLen = Math.min(v.length, maxLen);
      count.textContent = `${shownLen}/${maxLen}`;

      if (v.length > maxLen) {
        inputEl.value = v.slice(0, maxLen);
        overAttempt = true;
      }

      if (overAttempt) {
        try { inputEl.classList.add("bad"); } catch {}
        msg.style.display = "block";
      } else {
        try { inputEl.classList.remove("bad"); } catch {}
        msg.style.display = "none";
      }

      if (String(inputEl.value || "").length < maxLen) overAttempt = false;
    };

    inputEl.addEventListener("input", update);
    inputEl.addEventListener("paste", () => setTimeout(update, 0));
    update();

    return { wrap, msg, update };
  } catch {
    return null;
  }
}


function getMapContainerEl() {
  return (
    document.querySelector("#map") ||
    document.querySelector("#WazeMap") ||
    document.querySelector(".olMap") ||
    document.querySelector(".wme-map") ||
    null
  );
}


function makePinSvgDataUri(hex, active) {
  const c = normalizePinColor(hex);
  const glow = active ? 0.55 : 0.28;
  const svg = `
    <svg xmlns="http://www.w3.org/2000/svg" width="34" height="34" viewBox="0 0 34 34">
      <defs>
        <filter id="g" x="-50%" y="-50%" width="200%" height="200%">
          <feGaussianBlur stdDeviation="2.6" result="b"/>
          <feColorMatrix in="b" type="matrix"
            values="1 0 0 0 0
                    0 1 0 0 0
                    0 0 1 0 0
                    0 0 0 ${glow} 0" result="c"/>
          <feMerge>
            <feMergeNode in="c"/>
            <feMergeNode in="SourceGraphic"/>
          </feMerge>
        </filter>
      </defs>
      <g filter="url(#g)">
        <path d="M17 32s10-7.7 10-17a10 10 0 0 0-20 0c0 9.3 10 17 10 17z" fill="${c}"/>
        <circle cx="17" cy="15" r="4.6" fill="rgba(255,255,255,.92)"/>
      </g>
    </svg>`;
  return "data:image/svg+xml;charset=utf-8," + encodeURIComponent(svg.trim());
}

function makeClusterSvgDataUri(count, colors) {
  const n = Math.max(2, Math.min(99, Number(count) || 2));
  const txt = (n >= 99) ? "99+" : String(n);

  const colsIn = Array.isArray(colors) ? colors.filter(Boolean).map(normalizePinColor) : [];
  const uniq = [];
  for (const c of colsIn) { if (!uniq.includes(c)) uniq.push(c); }

  const maxStops = 8;
  let sample = uniq.slice(0, maxStops);
  if (uniq.length > maxStops) {
    sample = [];
    for (let i = 0; i < maxStops; i++) {
      const idx = Math.round((i / (maxStops - 1)) * (uniq.length - 1));
      sample.push(uniq[idx]);
    }
  }
  if (sample.length < 2) sample = ["#2d9cff", "#7c5cff"];

  let ar = 45, ag = 160, ab = 255;
  try {
    const src = colsIn.length ? colsIn : sample;
    let r = 0, g = 0, b = 0;
    for (const c of src) { const o = hexToRgb(c); r += o.r; g += o.g; b += o.b; }
    ar = Math.round(r / src.length);
    ag = Math.round(g / src.length);
    ab = Math.round(b / src.length);
  } catch {}

  const stops = sample.map((c, i) => {
    const off = (sample.length === 1) ? 0 : Math.round((i / (sample.length - 1)) * 100);
    return `<stop offset="${off}%" stop-color="${c}"/>`;
  }).join("");

  const svg = `
    <svg xmlns="http://www.w3.org/2000/svg" width="38" height="38" viewBox="0 0 38 38">
      <defs>
        <filter id="s" x="-45%" y="-45%" width="190%" height="190%">
          <feGaussianBlur stdDeviation="2.6" result="b"/>
          <feColorMatrix in="b" type="matrix"
            values="1 0 0 0 0
                    0 1 0 0 0
                    0 0 1 0 0
                    0 0 0 .55 0" result="c"/>
          <feMerge><feMergeNode in="c"/><feMergeNode in="SourceGraphic"/></feMerge>
        </filter>

        <radialGradient id="g" cx="30%" cy="25%" r="85%">
          ${stops}
        </radialGradient>
      </defs>

      <g filter="url(#s)">
        <circle cx="19" cy="19" r="15.5" fill="url(#g)" stroke="rgba(255,255,255,.18)" stroke-width="1.2"/>
        <circle cx="19" cy="19" r="11.2" fill="rgba(${ar},${ag},${ab},.10)"/>
        <circle cx="19" cy="19" r="15.5" fill="rgba(0,0,0,.12)"/>
      </g>

      <text x="19" y="23" text-anchor="middle" font-family="system-ui,-apple-system,Segoe UI,Roboto,Arial" font-size="12" font-weight="900" fill="#fff">${txt}</text>
    </svg>`;
  return "data:image/svg+xml;charset=utf-8," + encodeURIComponent(svg.trim());
}

function clusterByPixel(items, distPx) {
  const dist2 = (distPx || 36) * (distPx || 36);
  const out = [];
  const used = new Set();
  for (let i = 0; i < items.length; i++) {
    if (used.has(items[i].id)) continue;
    const a = items[i];
    const cluster = [a];
    used.add(a.id);
    for (let j = i + 1; j < items.length; j++) {
      const b = items[j];
      if (used.has(b.id)) continue;
      const dx = (b.px.x - a.px.x);
      const dy = (b.px.y - a.px.y);
      if ((dx*dx + dy*dy) <= dist2) {
        cluster.push(b);
        used.add(b.id);
      }
    }
    out.push(cluster);
  }
  return out;
}

function isRightClickEvt(evt) {
  try {
    const b = (typeof evt.button === "number") ? evt.button : null;
    const w = (typeof evt.which === "number") ? evt.which : null;
    return b === 2 || w === 3;
  } catch {
    return false;
  }
}

function getClientXYFromEvt(evt, map) {
  try {
    const cx = Number(evt.clientX);
    const cy = Number(evt.clientY);
    if (Number.isFinite(cx) && Number.isFinite(cy)) return { x: cx, y: cy };
  } catch {}

  try {
    const xy = evt && evt.xy;
    if (xy && Number.isFinite(xy.x) && Number.isFinite(xy.y)) {
      const div = map && (map.div || map.viewPortDiv || map.layerContainerDiv);
      if (div && typeof div.getBoundingClientRect === "function") {
        const r = div.getBoundingClientRect();
        return { x: r.left + xy.x, y: r.top + xy.y };
      }
    }
  } catch {}

  try {
    const px = Number(evt.pageX);
    const py = Number(evt.pageY);
    if (Number.isFinite(px) && Number.isFinite(py)) return { x: px, y: py };
  } catch {}

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


function ensurePinsOlLayer() {
  try {
    const ol = UW?.OpenLayers;
    const map = getOlMapBestEffort();
    if (!ol || !map || typeof map.addLayer !== "function") return null;

    if (!pinsOlLayer || !map.layers || !map.layers.includes(pinsOlLayer)) {
      pinsOlLayer = new ol.Layer.Markers("Map Pins");
      try { pinsOlLayer.displayInLayerSwitcher = false; } catch {}
      try { pinsOlLayer.isBaseLayer = false; } catch {}
      try { pinsOlLayer.uniqueName = `${SCRIPT_ID}:mapPins`; } catch {}
      try { pinsOlLayer.setZIndex(9999); } catch {}
            try {
        const wm = UW?.W?.map;
        if (wm && typeof wm.addLayer === "function") wm.addLayer(pinsOlLayer);
        else map.addLayer(pinsOlLayer);
      } catch { map.addLayer(pinsOlLayer); }


      try { pinsOlLayer.setVisibility(getPinsLayerVisible()); } catch {}

      try {
        pinsOlLayer.events?.register?.("visibilitychanged", pinsOlLayer, () => {
          try { setPinsLayerVisible(!!pinsOlLayer.getVisibility()); } catch {}
        });
      } catch {}
    }

    try {
      const want = getPinsLayerVisible();
      if (typeof pinsOlLayer.getVisibility === "function" && pinsOlLayer.getVisibility() !== want) {
        pinsOlLayer.setVisibility(want);
      }
    } catch {}

    return pinsOlLayer;
  } catch {
    return null;
  }
}


let mapPinsLayersToggle = null;
let mapPinsLayersObs = null;

function syncMapPinsLayersToggle() {
  try {
    if (mapPinsLayersToggle) mapPinsLayersToggle.checked = !!getPinsLayerVisible();
  } catch {}
}

function findDisplayCheckboxContainer() {
  const wanted = [
    /Satellite\s+imagery/i,
    /Online\s+editors/i,
    /GPS\s+points/i,
    /Map\s+notes/i,
    /Cities/i,
    /Display/i,
  ];

  try {
    const sat = Array.from(document.querySelectorAll("label,span,div")).find((n) => {
      try { return (n.textContent || "").trim() === "Satellite imagery"; } catch { return false; }
    });
    if (sat) {
      let cur = sat;
      for (let i = 0; i < 18 && cur; i++) {
        const cbCount = cur.querySelectorAll?.('input[type="checkbox"]').length || 0;
        if (cbCount >= 6) return cur;
        cur = cur.parentElement;
      }
    }
  } catch {}

  const isVisible = (el) => {
    try {
      const r = el.getBoundingClientRect();
      if (r.width < 80 || r.height < 80) return false;
      const cs = getComputedStyle(el);
      if (cs.display === "none" || cs.visibility === "hidden" || cs.opacity === "0") return false;
      return true;
    } catch { return false; }
  };

  const candidates = Array.from(document.querySelectorAll("div,section,ul")).filter(isVisible);
  for (const el of candidates) {
    const cb = el.querySelectorAll?.('input[type="checkbox"]').length || 0;
    if (cb < 6) continue;
    const t = (el.textContent || "");
    let hits = 0;
    for (const rx of wanted) { if (rx.test(t)) hits++; }
    if (hits >= 2) return el;
  }

  const all = document.querySelectorAll("label,span,div");
  let anchor = null;
  for (const el of all) {
    try {
      const t = (el.textContent || "").trim();
      if (!t) continue;
      if (/Satellite\s+imagery/i.test(t) || /Online\s+editors/i.test(t) || t === "Display") { anchor = el; break; }
    } catch {}
  }
  if (!anchor) return null;

  let el = anchor;
  for (let i = 0; i < 16 && el; i++) {
    const cbCount = el.querySelectorAll?.('input[type="checkbox"]').length || 0;
    if (cbCount >= 6) return el;
    el = el.parentElement;
  }
  return null;
}

function ensureMapPinsToggleInLayers() {
  try {
    const container = findDisplayCheckboxContainer();
    if (!container) return false;

    const existing = container.querySelector('input[data-wme-rc-map-pins-toggle="1"]');
    if (existing) {
      mapPinsLayersToggle = existing;
      syncMapPinsLayersToggle();
      return true;
    }

    const row = document.createElement("div");
    row.className = "wmeRcLayerRow";

    const lab = document.createElement("label");
    lab.className = "wmeRcLayerLabel";

    const cb = document.createElement("input");
    cb.type = "checkbox";
    cb.checked = !!getPinsLayerVisible();
    cb.setAttribute("data-wme-rc-map-pins-toggle", "1");

    cb.addEventListener("change", () => {
      setPinsLayerVisible(!!cb.checked);
      try { renderPinsMarkers(); } catch {}
      syncMapPinsLayersToggle();
    });

    const txt = document.createElement("span");
    txt.textContent = "Map Pins";

    lab.appendChild(cb);
    lab.appendChild(txt);
    row.appendChild(lab);

    container.appendChild(row);

    mapPinsLayersToggle = cb;
    syncMapPinsLayersToggle();
    return true;
  } catch {
    return false;
  }
}

function startMapPinsLayersInjection() {
  if (mapPinsLayersObs) return;

  let tries = 0;
  const iv = setInterval(() => {
    tries++;
    if (ensureMapPinsToggleInLayers() || tries > 20) {
      try { clearInterval(iv); } catch {}
    }
  }, 600);

  try {
    mapPinsLayersObs = new MutationObserver(() => {
      try { ensureMapPinsToggleInLayers(); } catch {}
    });
    mapPinsLayersObs.observe(document.body, { childList: true, subtree: true });
  } catch {}
}


function ensurePinsMarkersLayer() {
  const mapEl = getMapContainerEl();
  if (!mapEl) return null;

  try {
    const cs = getComputedStyle(mapEl);
    if (cs.position === "static") mapEl.style.position = "relative";
  } catch {}

  if (!pinsMarkersEl || !document.contains(pinsMarkersEl)) {
    const el = document.createElement("div");
    el.className = "wmeRcPinMarkers";
    mapEl.appendChild(el);
    pinsMarkersEl = el;
  }
  return pinsMarkersEl;
}

function getOlMapBestEffort() {
  try {
    const m = UW?.W?.map;
    if (!m) return null;
    if (typeof m.getOLMap === "function") return m.getOLMap();
    if (m.olMap && typeof m.olMap.getViewPortPxFromLonLat === "function") return m.olMap;
    if (m.map && typeof m.map.getViewPortPxFromLonLat === "function") return m.map;
    if (typeof m.getViewPortPxFromLonLat === "function") return m;
  } catch {}
  return null;
}

function getMapPixelFromLonLat(lon, lat) {
  try {
    const ol = UW?.OpenLayers;
    const map = getOlMapBestEffort();
    if (ol && map && typeof map.getViewPortPxFromLonLat === "function") {
      let ll = new ol.LonLat(lon, lat);

      try {
        const dst = map.getProjectionObject?.() || map.projection || null;
        const dstCode = String(dst?.projCode || dst?.getCode?.() || dst || "");
        const needsTransform = /900913|3857|102113|102100/i.test(dstCode) && !/4326/i.test(dstCode);

        if (needsTransform && ll && typeof ll.transform === "function") {
          const src = new ol.Projection("EPSG:4326");
          if (dst) ll.transform(src, dst);
        }
      } catch {}

      const px = map.getViewPortPxFromLonLat(ll);
      if (px && isFinite(px.x) && isFinite(px.y)) return { x: px.x, y: px.y };
    }
  } catch {}

  try {
    const fn = sdk?.Map?.getPixelFromLonLat;
    if (typeof fn === "function") {
      const px = fn({ lon, lat });
      if (px && isFinite(px.x) && isFinite(px.y)) return { x: px.x, y: px.y };
    }
  } catch {}

  return null;
}

function updatePinsMarkersPositions() {
  if (pinsOlLayer) return;
  if (!pinsMarkersEl) return;
  if (!getPinsLayerVisible()) return;
  const pins = loadPins();
  const _z = getCurrentZoomBestEffort();
  const _minZ = Number(getPinsNamesMinZoom());
  const showLabels = !!getPinsShowNamesOnMap() && (!Number.isFinite(_z) || _z >= _minZ);

  for (const pin of pins) {
    const el = pinMarkerEls.get(String(pin.id));
    if (!el) continue;


    if (pin.hideOnMap === true) { el.style.display = "none"; continue; }
    el.style.display = "";
    try { upsertMapPinLabel(el, pin, showLabels); } catch {}

    const px = getMapPixelFromLonLat(pin.lon, pin.lat);
    if (!px) continue;

    const x = Math.round(px.x - 10);
    const y = Math.round(px.y - 28);
    el.style.transform = `translate3d(${x}px, ${y}px, 0)`;
    el.classList.toggle("wmeRcPinMarkerActive", !!(pin.reminderAt && !pin.reminderDone));
  }
}


function renderPinsMarkers() {
  const pins = loadPins();

  const olLayer = ensurePinsOlLayer();
  const wantVisible = getPinsLayerVisible();

  if (olLayer) {
    try { cleanupDomPinsMarkers(); } catch {}
    try { if (typeof olLayer.setVisibility === "function") olLayer.setVisibility(!!wantVisible); } catch {}

    if (!wantVisible) {
      try {
        for (const mk of pinsOlMarkers.values()) { try { olLayer.removeMarker(mk); } catch {} }
        pinsOlMarkers.clear();
        for (const mk of pinsOlClusterMarkers.values()) { try { olLayer.removeMarker(mk); } catch {} }
        pinsOlClusterMarkers.clear();
      } catch {}
      return;
    }

    if (!pins || pins.length === 0) {
      try {
        for (const mk of pinsOlMarkers.values()) { try { olLayer.removeMarker(mk); } catch {} }
        pinsOlMarkers.clear();
        for (const mk of pinsOlClusterMarkers.values()) { try { olLayer.removeMarker(mk); } catch {} }
        pinsOlClusterMarkers.clear();
      } catch {}
      return;
    }

    try {
      const ol = UW?.OpenLayers;
      const map = getOlMapBestEffort();
      if (!ol || !map) return;

            const keep = new Set();
            const keepClusters = new Set();
            const clusteredIds = new Set();

            const items = [];
            let zoom = null;
            try {
              if (typeof map.getZoom === "function") zoom = Number(map.getZoom());
              else if (typeof map.zoom === "number") zoom = Number(map.zoom);
            } catch {}
            const doCluster = (Date.now() > pinsNoClusterUntil) && (pins.length > 1) && (Number.isFinite(zoom) ? (zoom < 12) : false);
            const _minZ = Number(getPinsNamesMinZoom());
            const showLabels = !!getPinsShowNamesOnMap() && (!Number.isFinite(zoom) || zoom >= _minZ);

            for (const pin of pins) {
              if (pin.hideOnMap === true) continue;

              const id = String(pin.id);

              let ll = new ol.LonLat(pin.lon, pin.lat);
              try {
                const dst = map.getProjectionObject?.() || map.projection || null;
                const dstCode = String(dst?.projCode || dst?.getCode?.() || dst || "");
                const needsTransform = /900913|3857|102113|102100/i.test(dstCode) && !/4326/i.test(dstCode);
                if (needsTransform && typeof ll.transform === "function") {
                  const src = new ol.Projection("EPSG:4326");
                  if (dst) ll.transform(src, dst);
                }
              } catch {}

              const px = (typeof map.getLayerPxFromLonLat === "function")
                ? map.getLayerPxFromLonLat(ll)
                : map.getViewPortPxFromLonLat(ll);

              if (!px || !isFinite(px.x) || !isFinite(px.y)) continue;
              items.push({ pin, id, ll, px });
            }

            if (doCluster && items.length > 1) {
              const clusters = clusterByPixel(items, 40);
              for (const cl of clusters) {
                if (!cl || cl.length < 2) continue;

                const ids = cl.map((x) => x.id).sort();
                const cid = "c:" + ids.join("|");
                keepClusters.add(cid);
                ids.forEach((x) => clusteredIds.add(x));

                let lonSum = 0, latSum = 0;
                for (const it of cl) { lonSum += Number(it.ll.lon); latSum += Number(it.ll.lat); }
                const llc = new ol.LonLat(lonSum / cl.length, latSum / cl.length);

                const url = makeClusterSvgDataUri(cl.length, cl.map((x) => x?.pin?.color).filter(Boolean));
                let mk = pinsOlClusterMarkers.get(cid);
                const needsRecreate = !mk || !mk.icon || mk.icon.url !== url;

                if (needsRecreate) {
                  try { if (mk) olLayer.removeMarker(mk); } catch {}
                  const size = new ol.Size(38, 38);
                  const offset = new ol.Pixel(-19, -19);
                  const icon = new ol.Icon(url, size, offset);
                  mk = new ol.Marker(llc, icon);
                  mk.clusterId = cid;
                  mk.clusterLat = (cl[0]?.pin?.lat);
                  mk.clusterLon = (cl[0]?.pin?.lon);

                  mk.events?.register?.("mousedown", mk, (evt) => {
                    try { ol.Event.stop(evt); } catch {}
                    try { pinsNoClusterUntil = Date.now() + 1200; } catch {}
                    zoomToClusterItems(cl, llc, map, ol);
                  });

                  try { mk.icon?.imageDiv?.classList?.add("wmeRcOlMarker"); } catch {}

                  try {
                    const div = mk && mk.icon && mk.icon.imageDiv;
                    if (div && !div.__wmeRcClusterCtx) {
                      div.__wmeRcClusterCtx = true;
                      div.style.cursor = "pointer";
                      div.addEventListener("contextmenu", (ev) => {
                        try { ev.preventDefault(); ev.stopPropagation(); if (ev.stopImmediatePropagation) ev.stopImmediatePropagation(); } catch {}
                        try { pinsNoClusterUntil = Date.now() + 1200; } catch {}
                        zoomToClusterItems(cl, llc, map, ol);
                      }, true);
                    }
                  } catch {}

                  olLayer.addMarker(mk);
                  pinsOlClusterMarkers.set(cid, mk);
                } else {
                  try { mk.lonlat = llc; } catch {}
                  try { mk.moveTo?.(map.getLayerPxFromLonLat(llc)); } catch {}
                }
              }

              for (const [cid, mk] of Array.from(pinsOlClusterMarkers.entries())) {
                if (keepClusters.has(cid)) continue;
                try { olLayer.removeMarker(mk); } catch {}
                pinsOlClusterMarkers.delete(cid);
              }
            } else {
              for (const [cid, mk] of Array.from(pinsOlClusterMarkers.entries())) {
                try { olLayer.removeMarker(mk); } catch {}
                pinsOlClusterMarkers.delete(cid);
              }
            }

            for (const it of items) {
              const pin = it.pin;
              const id = it.id;
              if (clusteredIds.has(id)) {
                try {
                  const mkOld = pinsOlMarkers.get(id);
                  if (mkOld) olLayer.removeMarker(mkOld);
                } catch {}
                pinsOlMarkers.delete(id);
                continue;
              }

              keep.add(id);

              const active = !!(pin.reminderAt && !pin.reminderDone);
              const url = makePinSvgDataUri(pin.color, active);

              let mk = pinsOlMarkers.get(id);

              const needsRecreate = !mk || !mk.icon || mk.icon.url !== url;
              if (needsRecreate) {
                try { if (mk) olLayer.removeMarker(mk); } catch {}
                const size = new ol.Size(34, 34);
                const offset = new ol.Pixel(-17, -34);
                const icon = new ol.Icon(url, size, offset);
                mk = new ol.Marker(it.ll, icon);
                mk.pinId = id;

                mk.events?.register?.("mousedown", mk, (evt) => {
                  try { ol.Event.stop(evt); } catch {}
                  try {
                    const p = loadPins().find((x) => String(x.id) === id);
                    if (!p) return;
                    zoomToLonLatExact(p.lon, p.lat, getDefaultPinZoom());
          try { showPinHitPill(p.name || "Pinned place"); } catch {}
                    try { if ((evt && (evt.button === 0 || evt.which === 1)) || !evt) showPinHitPill(p.name || "Pinned place"); } catch {}
                  } catch {}
                });

                try { mk.icon?.imageDiv?.classList?.add("wmeRcOlMarker"); } catch {}

                try {
                  const div = mk && mk.icon && mk.icon.imageDiv;
                  if (div && !div.__wmeRcPinCtx) {
                    div.__wmeRcPinCtx = true;
                    div.style.cursor = "pointer";
                  }
                } catch {}

                try { upsertMapPinLabel(div, pin, showLabels); } catch {}

                olLayer.addMarker(mk);
                pinsOlMarkers.set(id, mk);
              } else {
                try { mk.moveTo?.(map.getLayerPxFromLonLat(it.ll)); } catch {}
                try { mk.lonlat = it.ll; } catch {}
                try { const div = mk && mk.icon && mk.icon.imageDiv; upsertMapPinLabel(div, pin, showLabels); } catch {}
              }
            }
      for (const [id, mk] of Array.from(pinsOlMarkers.entries())) {
        if (keep.has(id)) continue;
        try { olLayer.removeMarker(mk); } catch {}
        pinsOlMarkers.delete(id);
      }
    } catch {}

    return;
  }

  if (!wantVisible) {
    stopPinsMarkersLoop();
    try { for (const el of pinMarkerEls.values()) el.remove(); pinMarkerEls.clear(); } catch {}
    return;
  }
  if (!pins || pins.length === 0) {
    stopPinsMarkersLoop();
    try { for (const el of pinMarkerEls.values()) el.remove(); pinMarkerEls.clear(); } catch {}
    return;
  }

  const layer = ensurePinsMarkersLayer();
  if (!layer) return;

  const _z = getCurrentZoomBestEffort();
  const _minZ = Number(getPinsNamesMinZoom());
  const showLabelsDom = !!getPinsShowNamesOnMap() && (!Number.isFinite(_z) || _z >= _minZ);

  const keep = new Set();
  for (const pin of pins) {
    if (pin.hideOnMap === true) continue;
    const id = String(pin.id);
    keep.add(id);

    let el = pinMarkerEls.get(id);
    if (!el || !document.contains(el)) {
      el = document.createElement("div");
      el.className = "wmeRcPinMarker";
      el.dataset.pinId = id;
      el.title = pin.name || "Pinned place";
      try { applyPinMarkerColors(el, pin.color); } catch {}

      const inner = document.createElement("div");
      inner.className = "wmeRcPinMarkerInner";
      el.appendChild(inner);
      try { upsertMapPinLabel(el, pin, showLabelsDom); } catch {}

      el.addEventListener("click", (ev) => {
        try { ev.stopPropagation(); ev.preventDefault(); } catch {}
        try {
          const p = loadPins().find((x) => String(x.id) === id);
          if (!p) return;
          zoomToLonLatExact(p.lon, p.lat, getDefaultPinZoom());
          try { showPinHitPill(p.name || "Pinned place"); } catch {}
        } catch {}
      });


      layer.appendChild(el);
      pinMarkerEls.set(id, el);
    } else {
      el.title = pin.name || "Pinned place";
      try { applyPinMarkerColors(el, pin.color); } catch {}
      try { upsertMapPinLabel(el, pin, showLabelsDom); } catch {}
    }
  }

  for (const [id, el] of Array.from(pinMarkerEls.entries())) {
    if (keep.has(id)) continue;
    try { el.remove(); } catch {}
    pinMarkerEls.delete(id);
  }

  startPinsMarkersLoop();
}


function startPinsMarkersLoop() {
  if (pinsMarkersIntervalId) return;

  pinsMarkersIntervalId = _createAdaptiveLoop(() => { try { updatePinsMarkersPositions(); } catch {} }, 400, {
    active: () => {
      try {

        if (!pinsMarkersEl || !document.contains(pinsMarkersEl)) return false;
        if (!pinMarkerEls || pinMarkerEls.size === 0) return false;

        if (!getPinsOnMapEnabled()) return false;
        return true;
      } catch { return true; }
    }
  });
  try { updatePinsMarkersPositions(); } catch {}
}

function stopPinsMarkersLoop() {
  if (!pinsMarkersIntervalId) return;
  try { pinsMarkersIntervalId(); } catch {}
  pinsMarkersIntervalId = null;
}

function cleanupDomPinsMarkers() {
  try { stopPinsMarkersLoop(); } catch {}
  try { for (const el of pinMarkerEls.values()) { try { el.remove(); } catch {} } pinMarkerEls.clear(); } catch {}
  try { if (pinsMarkersEl && pinsMarkersEl.parentNode) pinsMarkersEl.parentNode.removeChild(pinsMarkersEl); } catch {}
  pinsMarkersEl = null;
}

function onPinsDragMove(ev) {
  if (!pinsPanelEl || !pinsDrag.active) return;
  if (pinsDrag.pointerId != null && ev.pointerId != null && ev.pointerId !== pinsDrag.pointerId) return;
  try {
    ev.preventDefault();
    ev.stopPropagation();
    ev.stopImmediatePropagation && ev.stopImmediatePropagation();
  } catch {}

  const mapEl =
    document.querySelector("#map") ||
    document.querySelector("#WazeMap") ||
    document.querySelector(".olMap") ||
    document.querySelector(".wme-map") ||
    null;
  if (!mapEl) return;

  const mapRect = mapEl.getBoundingClientRect();
  const r = pinsPanelEl.getBoundingClientRect();
  const dx = ev.clientX - pinsDrag.startX;
  const dy = ev.clientY - pinsDrag.startY;

  let left = pinsDrag.baseLeft + dx;
  let top  = pinsDrag.baseTop  + dy;

  const pad = 6;
  left = clamp(left, pad, Math.max(pad, mapRect.width - r.width - pad));
  top  = clamp(top, pad, Math.max(pad, mapRect.height - r.height - pad));

  pinsPanelEl.style.left = `${left}px`;
  pinsPanelEl.style.top  = `${top}px`;

  try { updatePinsPanelHeightBounds(); } catch {}
}

function onPinsDragUp(ev) {
  if (!pinsPanelEl || !pinsDrag.active) return;
  if (pinsDrag.pointerId != null && ev.pointerId != null && ev.pointerId !== pinsDrag.pointerId) return;

  pinsDrag.active = false;
  try {
    document.removeEventListener("pointermove", onPinsDragMove, true);
    document.removeEventListener("pointerup", onPinsDragUp, true);
    document.removeEventListener("pointercancel", onPinsDragUp, true);
  } catch {}
  try {
    const mapEl =
      document.querySelector("#map") ||
      document.querySelector("#WazeMap") ||
      document.querySelector(".olMap") ||
      document.querySelector(".wme-map") ||
      null;
    if (mapEl) {
      const mapRect = mapEl.getBoundingClientRect();
      const rect = pinsPanelEl.getBoundingClientRect();
      const left = rect.left - mapRect.left;
      const top  = rect.top  - mapRect.top;
      localStorage.setItem(PIN_POS_KEY, JSON.stringify({ left: Math.round(left), top: Math.round(top) }));
    }
  } catch {}
  try { pinsPanelEl.releasePointerCapture(pinsDrag.pointerId); } catch {}
  pinsDrag.pointerId = null;
}


let _wmeRcTooltipEl = null;
let _wmeRcTooltipHideT = null;

function ensurePinNameTooltip() {
  try {
    if (_wmeRcTooltipEl && document.body.contains(_wmeRcTooltipEl)) return _wmeRcTooltipEl;
    const el = document.createElement("div");
    el.id = "wmeRcTooltip";
    el.className = "wmeRcTooltip";
    el.style.display = "none";
    (document.body || document.documentElement).appendChild(el);
    _wmeRcTooltipEl = el;
    return el;
  } catch {
    return null;
  }
}

function showPinNameTooltip(anchorEl, text) {
  try {
    const tip = ensurePinNameTooltip();
    if (!tip || !anchorEl) return;
    try { clearTimeout(_wmeRcTooltipHideT); } catch {}
    tip.textContent = String(text || "");
    tip.style.display = "block";
    try { tip.classList.remove("show"); } catch {}

    const place = () => {
      const r = anchorEl.getBoundingClientRect();
      const vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
      const vh = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0);
      const tr = tip.getBoundingClientRect();
      const w = tr.width || Math.min(520, vw - 24);
      const h = tr.height || 32;

      let left = Math.max(8, Math.min(r.left || 0, vw - w - 8));
      let top = (r.top || 0) - h - 10;
      if (top < 8) top = Math.min(vh - h - 8, (r.bottom || 0) + 10);

      tip.style.left = Math.round(left) + "px";
      tip.style.top = Math.round(top) + "px";
    };

    requestAnimationFrame(() => {
      try { place(); } catch {}
      try { tip.classList.add("show"); } catch {}
    });
  } catch {}
}


let _wmeRcCountdownTipInt = 0;
let _wmeRcCountdownTipAnchor = null;
let _wmeRcCountdownTipAt = 0;

function getPinCountdownTooltipText(reminderAt, nowMs, mode){
  try{
    const at = Number(reminderAt);
    const now = Number(nowMs);
    if (!Number.isFinite(at) || !Number.isFinite(now)) return "";
    const ms = at - now;
    if (!Number.isFinite(ms) || ms <= 0) return "";

    if (mode === 0) return getPinCountdownExact(at, now);

    const totalSec = Math.max(0, Math.floor(ms / 1000));
    const day = 86400;
    const week = 7 * day;
    const month = 30 * day;
    const year = 365 * day;

    let unit = "day";
    let n = Math.floor(totalSec / day);
    if (totalSec >= year){ unit = "year"; n = Math.floor(totalSec / year); }
    else if (totalSec >= month){ unit = "month"; n = Math.floor(totalSec / month); }
    else if (totalSec >= week){ unit = "week"; n = Math.floor(totalSec / week); }
    else { unit = "day"; n = Math.floor(totalSec / day); }

    const rem = totalSec % day;
    const hh = String(Math.floor(rem / 3600)).padStart(2, "0");
    const mi = String(Math.floor((rem % 3600) / 60)).padStart(2, "0");
    const label = `${n} ${unit}${n === 1 ? "" : "s"}`;
    return `${label} ${hh}:${mi}`;
  }catch{ return ""; }
}

function showPinCountdownTooltip(anchorEl, reminderAtMs) {
  try {
    const at = Number(reminderAtMs);
    if (!anchorEl || !Number.isFinite(at)) return;
    _wmeRcCountdownTipAnchor = anchorEl;
    _wmeRcCountdownTipAt = at;

    const tick = () => {
      try {
        const now = Date.now();
        const ms = at - now;
        if (!Number.isFinite(ms) || ms <= 0) {
          hidePinNameTooltip(0);
          return;
        }
        const pinId = String((anchorEl && anchorEl.dataset && anchorEl.dataset.pinCountdown) || "");
        const mode = (pinId && _pinCountdownTipModes && _pinCountdownTipModes.get(pinId)) || 0;
        const t = getPinCountdownTooltipText(at, now, mode);
        const tip = ensurePinNameTooltip();
        if (tip && tip.style.display !== "none") {
          tip.textContent = String(t || "");
        } else {
          showPinNameTooltip(anchorEl, t);
        }
      } catch {}
    };

    tick();

    try { clearInterval(_wmeRcCountdownTipInt); } catch {}
    _wmeRcCountdownTipInt = setInterval(() => {
      try {
        if (_wmeRcCountdownTipAnchor !== anchorEl) return;
        tick();
      } catch {}
    }, 1000);
  } catch {}
}


function hidePinNameTooltip(delay = 80) {
  try {
    try { clearInterval(_wmeRcCountdownTipInt); } catch {}
    _wmeRcCountdownTipInt = 0;
    _wmeRcCountdownTipAnchor = null;
    _wmeRcCountdownTipAt = 0;
    const tip = _wmeRcTooltipEl;
    if (!tip) return;
    try { clearTimeout(_wmeRcTooltipHideT); } catch {}
    _wmeRcTooltipHideT = setTimeout(() => {
      try { tip.classList.remove("show"); } catch {}
      setTimeout(() => { try { tip.style.display = "none"; } catch {} }, 140);
    }, Math.max(0, Number(delay) || 0));
  } catch {}

let _pinsPanelDockObs = null;
let _pinsPanelAvoidDockTimer = 0;
let _pinsPanelAvoidDockRAF = 0;

function _findVisibleLeftDockRect() {
  const candidates = [
    "#sidepanel",
    "#sidebar",
    "#left-sidebar",
    "#leftSidebar",
    "aside",
    "[class*='sidebar']",
    "[class*='SidePanel']",
    "[class*='leftPanel']",
    "[data-testid*='side']",
  ];
  const seen = new Set();
  const els = [];
  for (const sel of candidates) {
    try {
      document.querySelectorAll(sel).forEach(el => { if (el && !seen.has(el)) { seen.add(el); els.push(el); } });
    } catch {}
  }
  let best = null;
  for (const el of els) {
    try {
      const st = getComputedStyle(el);
      if (st.display === "none" || st.visibility === "hidden" || Number(st.opacity) === 0) continue;
      const r = el.getBoundingClientRect();
      if (r.width < 220 || r.height < 200) continue;
      if (r.right < 120) continue;
      if (r.left > 40) continue;
      if (pinsPanelEl && (el === pinsPanelEl || el.contains(pinsPanelEl) || pinsPanelEl.contains(el))) continue;
      const score = (Math.min(r.width, 520) * 2) + Math.min(r.height, 900);
      if (!best || score > best.score) best = { el, r, score };
    } catch {}
  }
  return best ? best.r : null;
}

function _rectsIntersect(a, b, pad) {
  try {
    const p = Number(pad) || 0;
    return !(a.right <= b.left + p || a.left >= b.right - p || a.bottom <= b.top + p || a.top >= b.bottom - p);
  } catch { return false; }
}

function _findVisibleBlockingPopupRects(mapRect) {
  const sels = [
    "[role='dialog']",
    "[aria-modal='true']",
    ".issue-details, .issueDetails, .issue-details__panel, .issue-details__modal",
    ".wme__modal, .wme-modal, .wm-modal, .wm-dialog, .waze-modal",
    ".ReactModal__Content, .ReactModal__Overlay",
    "[class*='modal']",
    "[class*='Modal']",
    "[class*='popup']",
    "[class*='Popup']",
    "[class*='dialog']",
    "[class*='Dialog']",
    "[data-testid*='modal']",
    "[data-testid*='dialog']",
    "[data-testid*='issue']",
  ];

  const seen = new Set();
  const els = [];
  for (const sel of sels) {
    try {
      document.querySelectorAll(sel).forEach(el => {
        if (!el || seen.has(el)) return;
        seen.add(el);
        els.push(el);
      });
    } catch {}
  }

  function _effectiveZIndex(el) {
    try {
      let cur = el;
      for (let i = 0; i < 6 && cur; i++) {
        const st = getComputedStyle(cur);
        const zi = parseInt(st.zIndex, 10);
        if (Number.isFinite(zi)) return zi;
        cur = cur.parentElement;
      }
    } catch {}
    return 0;
  }

  const out = [];
  for (const el of els) {
    try {
      if (!el || !document.contains(el)) continue;
      if (pinsPanelEl && (el === pinsPanelEl || el.contains(pinsPanelEl) || pinsPanelEl.contains(el))) continue;
      if (String(el.className || "").includes("wmeRc")) continue;

      const st = getComputedStyle(el);
      if (st.display === "none" || st.visibility === "hidden" || Number(st.opacity) === 0) continue;

      const r = el.getBoundingClientRect();
      if (r.width < 260 || r.height < 140) continue;

      if (mapRect) {
        if (!_rectsIntersect(r, mapRect, 4)) continue;
      }

      if (mapRect && r.top > mapRect.bottom - 60) continue;

      if (mapRect && r.width > mapRect.width * 0.98 && r.height > mapRect.height * 0.98) continue;

      const z = _effectiveZIndex(el);
      out.push({ r, z, area: r.width * r.height });
    } catch {}
  }

  out.sort((a, b) => (b.z - a.z) || (b.area - a.area));
  return out.map(x => x.r);
}


function _avoidPinsPanelLeftDockNow() {
  try {
    if (!pinsPanelEl || !document.contains(pinsPanelEl)) return;
    if (pinsPanelEl.classList.contains("hidden")) return;

    const mapEl = pinsPanelEl.parentElement;
    if (!mapEl) return;

    const mapRect = mapEl.getBoundingClientRect();
    const margin = 12;

    const panelRect = pinsPanelEl.getBoundingClientRect();
    const panelW = panelRect.width || pinsPanelEl.offsetWidth || 0;
    const panelH = panelRect.height || pinsPanelEl.offsetHeight || 0;
    const mapW = mapRect.width || mapEl.clientWidth || 0;

    const curLeft = panelRect.left - mapRect.left;
    const curTop  = panelRect.top  - mapRect.top;

    const maxLeft = Math.max(0, mapW - panelW - 8);

    let targetLeft = curLeft;

    const dockRect = _findVisibleLeftDockRect();
    if (dockRect) {
      const boundary = Math.max(0, dockRect.right - mapRect.left) + margin;
      if (targetLeft + 6 < boundary) targetLeft = Math.min(maxLeft, Math.max(boundary, targetLeft));
    }

    const popRects = _findVisibleBlockingPopupRects(mapRect);
    if (popRects && popRects.length) {
      for (let iter = 0; iter < 2; iter++) {
        const candRect = {
          left: mapRect.left + targetLeft,
          right: mapRect.left + targetLeft + panelW,
          top: panelRect.top,
          bottom: panelRect.top + panelH,
          width: panelW,
          height: panelH
        };

        let moved = false;
        for (const pr of popRects) {
          if (!_rectsIntersect(candRect, pr, 6)) continue;

          const rightOf = (pr.right - mapRect.left) + margin;
          if (rightOf <= maxLeft) {
            if (targetLeft < rightOf) { targetLeft = rightOf; moved = true; }
            continue;
          }

          const leftOf = (pr.left - mapRect.left) - panelW - margin;
          if (leftOf >= 0) {
            if (targetLeft > leftOf) { targetLeft = leftOf; moved = true; }
          } else {
            targetLeft = maxLeft;
            moved = true;
          }
        }
        if (!moved) break;
        targetLeft = Math.max(0, Math.min(maxLeft, targetLeft));
      }
    }

    targetLeft = Math.round(Math.max(0, Math.min(maxLeft, targetLeft)));

    if (Math.abs(targetLeft - curLeft) > 1) {
      _pinsPanelLastAutoAt = _pinsNow();
      _pinsPanelSuppressROUntil = _pinsPanelLastAutoAt + 650;
      pinsPanelEl.style.left = `${targetLeft}px`;
      try { localStorage.setItem(PIN_POS_KEY, JSON.stringify({ left: targetLeft, top: Math.round(curTop) })); } catch {}
    }
  } catch {}
}

function _schedulePinsPanelAvoidDock() {
  try {
    if (_pinsPanelAvoidDockTimer) clearTimeout(_pinsPanelAvoidDockTimer);
    _pinsPanelAvoidDockTimer = setTimeout(() => {
      _pinsPanelAvoidDockTimer = 0;
      if (_pinsPanelAvoidDockRAF) cancelAnimationFrame(_pinsPanelAvoidDockRAF);
      _pinsPanelAvoidDockRAF = requestAnimationFrame(() => {
        _pinsPanelAvoidDockRAF = 0;
        _avoidPinsPanelLeftDockNow();
      });
    }, 80);
  } catch {}
}

function _ensurePinsPanelDockObserver() {
  try {
    if (_pinsPanelDockObs || typeof MutationObserver === "undefined") return;
    _pinsPanelDockObs = new MutationObserver(() => {
      _schedulePinsPanelAvoidDock();
    });
    _pinsPanelDockObs.observe(document.body, { childList: true, subtree: true, attributes: true });
    window.addEventListener("resize", () => { _schedulePinsPanelAvoidDock(); }, { passive: true });
  } catch {}
}
}

function renderPinsPanel() {
  if (!pinsPanelEl) return;
  const allPins = loadPins();
  pinsCache = allPins;

  try { renderPinsMarkers(); } catch {}

  const alwaysVisibleEmpty = getPinsPanelAlwaysVisibleEmpty();
  pinsPanelEl.classList.toggle("hidden", allPins.length === 0 && !alwaysVisibleEmpty);

  if (allPins.length === 0 && !alwaysVisibleEmpty) {
    stopPinsCountdownLoop();
    pinsPanelEl.innerHTML = "";
    try { _pinCountdownEls.clear(); _pinRowEls.clear(); } catch {}
    try { renderPinsMarkers(); } catch {}
    return;
  }

  if (allPins.length === 0 && alwaysVisibleEmpty) {
    stopPinsCountdownLoop();
    try { _pinCountdownEls.clear(); _pinRowEls.clear(); } catch {}
  }

  pinsPanelEl.innerHTML = "";

  const isCollapsedPinsPanel = !!(pinsPanelEl.classList.contains("collapsed") || (pinsPanelEl.dataset && pinsPanelEl.dataset.collapsed === "1"));

  const hdr = document.createElement("div");
  hdr.className = "wmeRcPinsHdr";

  const titleWrap = document.createElement("div");
  titleWrap.className = "wmeRcPinsHdrLeft";

  const titleEl = document.createElement("div");
  titleEl.className = "wmeRcPinsTitle";
  titleEl.textContent = "Map Pins";

  titleWrap.appendChild(titleEl);



try { titleWrap.style.cursor = "pointer"; } catch {}

const _pinsTitleTap = { down:false, x:0, y:0, t:0 };

const _togglePinsFromTitle = () => {
  try {
    if (!pinsPanelEl) return;
    const isCol = !!(pinsPanelEl && (pinsPanelEl.classList.contains("collapsed") || (pinsPanelEl.dataset && pinsPanelEl.dataset.collapsed === "1") || pinsPanelEl.style.display === "none"));
    if (isCol) {
      const mode = getPinsMinimizeMode();
      if (mode === "panel") _restorePinsPanelFromPanelCollapse();
      else _restorePinsPanelFromBubble();
    } else {
      const mode = getPinsMinimizeMode();
      if (mode === "panel") _minimizePinsPanelInPlace({ instant: false });
      else _minimizePinsPanelToBubble({ instant: false });
    }
    try { _syncPinsBubbleVisibility(); } catch {}

    try { _syncPinsPanelCollapseBtn && _syncPinsPanelCollapseBtn(); } catch {}
  } catch {}
};

const _onPinsTitleDown = (ev) => {
  try {

    if (ev && ev.button != null && ev.button !== 0) return;
    _pinsTitleTap.down = true;
    _pinsTitleTap.x = ev.clientX || 0;
    _pinsTitleTap.y = ev.clientY || 0;
    _pinsTitleTap.t = Date.now();
    try { ev.preventDefault(); } catch {}
    try { ev.stopPropagation(); } catch {}
  } catch {}
};

const _onPinsTitleUp = (ev) => {
  try {
    if (!_pinsTitleTap.down) return;
    _pinsTitleTap.down = false;

    const dx = (ev.clientX || 0) - _pinsTitleTap.x;
    const dy = (ev.clientY || 0) - _pinsTitleTap.y;
    const dist = Math.sqrt(dx*dx + dy*dy);
    const dt = Date.now() - (_pinsTitleTap.t || 0);


    if (dist <= 6 && dt <= 650) _togglePinsFromTitle();

    try { ev.preventDefault(); } catch {}
    try { ev.stopPropagation(); } catch {}
  } catch {}
};


titleWrap.addEventListener("pointerdown", _onPinsTitleDown, true);
titleWrap.addEventListener("pointerup", _onPinsTitleUp, true);
titleWrap.addEventListener("pointercancel", () => { _pinsTitleTap.down = false; }, true);


titleWrap.addEventListener("mousedown", _onPinsTitleDown, true);
titleWrap.addEventListener("mouseup", _onPinsTitleUp, true);

  const rightWrap = document.createElement("div");
  rightWrap.className = "wmeRcPinsHdrRight";

  const countEl = document.createElement("div");
  countEl.className = "wmeRcPinsCount";
  countEl.textContent = String(allPins.length);

  const addGroupBtn = document.createElement("div");
  addGroupBtn.className = "wmeRcPinsHdrBtn";
  addGroupBtn.title = "Create folder";
  addGroupBtn.innerHTML = `<span class="wmeRcI">${ICONS.folderPlus}</span>`;
  addGroupBtn.addEventListener("click", (ev) => {
    try { ev.preventDefault(); } catch {}
    ev.stopPropagation();
    try {
      openGroupNameModal({
          title: "New folder",
          placeholder: "Name",
          okText: "Create",
          onSubmit: (name, emoji) => {
            const groups = loadPinGroups();
            const id = `g-${_uid()}`;
            groups.push({ id, name, emoji: (typeof emoji === 'string') ? emoji : '' });
            savePinGroups(groups);
            renderPinsPanel();
      try { _schedulePinsPanelAvoidDock(); } catch {}
          }
        });
    } catch {}
  });

  const settingsBtn = document.createElement("div");
  settingsBtn.className = "wmeRcPinsHdrBtn";
  settingsBtn.title = "Pin settings";
  settingsBtn.innerHTML = `<span class="wmeRcI">${ICONS.gear}</span>`;
  settingsBtn.addEventListener("click", (ev) => {
    try { ev.preventDefault(); } catch {}
    try { ev.stopPropagation(); } catch {}
    try { openPinsSettingsModal(); } catch {}
  });

  const collapseBtn = document.createElement("div");
  collapseBtn.className = "wmeRcPinsHdrBtn wmeRcPinsCollapseBtn";

  const _pinsMode = getPinsMinimizeMode();
  const _pinsIsCollapsed = !!(pinsPanelEl && (pinsPanelEl.classList.contains("collapsed") || (pinsPanelEl.dataset && pinsPanelEl.dataset.collapsed === "1")));

  if (_pinsMode === "bubble" && !_pinsIsCollapsed) {
    collapseBtn.title = "Minimize";
    collapseBtn.innerHTML = `<span class="wmeRcI" style="display:flex;">${ICONS.minus}</span>`;
  } else {
    collapseBtn.title = _pinsIsCollapsed ? "Expand panel" : "Collapse panel";
    collapseBtn.innerHTML = `<span class="wmeRcI" style="display:flex;transition:transform .16s ease;transform:${_pinsIsCollapsed ? "rotate(180deg)" : "rotate(0deg)"};">${ICONS.chevDown}</span>`;
  }
  collapseBtn.addEventListener("click", (ev) => {
  try { ev.preventDefault(); } catch {}
  try { ev.stopPropagation(); } catch {}
  try {
    const isCol = !!(pinsPanelEl && (pinsPanelEl.classList.contains("collapsed") || (pinsPanelEl.dataset && pinsPanelEl.dataset.collapsed === "1") || pinsPanelEl.style.display === "none"));
    if (!pinsPanelEl) return;
    if (isCol) {
      const mode = getPinsMinimizeMode();
      if (mode === "panel") _restorePinsPanelFromPanelCollapse();
      else _restorePinsPanelFromBubble();
    } else {
      const mode = getPinsMinimizeMode();
      if (mode === "panel") _minimizePinsPanelInPlace({ instant: false });
      else _minimizePinsPanelToBubble({ instant: false });
    }
    try { _syncPinsBubbleVisibility(); } catch {}
  } catch {}
});

  rightWrap.appendChild(countEl);
  rightWrap.appendChild(addGroupBtn);
  rightWrap.appendChild(settingsBtn);
  rightWrap.appendChild(collapseBtn);

  hdr.appendChild(titleWrap);
  hdr.appendChild(rightWrap);

  if (!isCollapsedPinsPanel) {
  const list = document.createElement("div");
  list.className = "wmeRcPinsList";
  try { list.classList.remove("scroll"); } catch {}
  try { pinsPanelEl.classList.remove("scrollMode"); } catch {}
  try {


    if (!list.dataset.scrollBound) {
      list.dataset.scrollBound = "1";
      const stop = (e) => { try { e.stopPropagation(); } catch {} };
      list.addEventListener("wheel", stop, { passive: true });
      list.addEventListener("touchmove", stop, { passive: true });
      list.addEventListener("scroll", () => {
        try {
          const st = (list.scrollTop || 0);
          const hdrs = Array.from(list.querySelectorAll(".wmeRcPinsGroupHdr"));
          let active = null;
          for (const h of hdrs) {

            if ((h.offsetTop || 0) <= st + 1) active = h;
          }
          for (const h of hdrs) h.classList.remove("wmeRcSticky");
          if (active && st > (active.offsetTop || 0)) active.classList.add("wmeRcSticky");
        } catch {}
      }, { passive: true });
      try {
        const st = (list.scrollTop || 0);
        const hdrs = Array.from(list.querySelectorAll(".wmeRcPinsGroupHdr"));
        let active = null;
        for (const h of hdrs) {
          if ((h.offsetTop || 0) <= st + 1) active = h;
        }
        for (const h of hdrs) h.classList.remove("wmeRcSticky");
        if (active && st > (active.offsetTop || 0)) active.classList.add("wmeRcSticky");
      } catch {}

    }
  } catch {}const _pinReorder = { active: false, pid: null, row: null, parent: null, placeholder: null, pointerId: null,
    startY: 0, dy: 0, baseTop: 0, baseLeft: 0, width: 0, height: 0, raf: 0 };

  let _pinDragState = { id: null, overId: null, placeAfter: true, overGroup: null };

  const _getPinDragLayer = () => {
    try {
      let dl = document.getElementById("wmeRcPinDragLayer");
      if (dl) return dl;
      dl = document.createElement("div");
      dl.id = "wmeRcPinDragLayer";
      dl.style.position = "fixed";
      dl.style.left = "0";
      dl.style.top = "0";
      dl.style.right = "0";
      dl.style.bottom = "0";
      dl.style.zIndex = "2147483646";
      dl.style.pointerEvents = "none";
      dl.style.background = "transparent";
      document.body.appendChild(dl);
      return dl;
    } catch {
      return document.body;
    }
  };


  const _clearPinDropMarks = () => {
    try {
      list.querySelectorAll('.wmeRcPinRow.dropBefore,.wmeRcPinRow.dropAfter,.wmeRcPinsGroupHdr.dropTarget')
        .forEach((el) => {
          el.classList.remove('dropBefore');
          el.classList.remove('dropAfter');
          el.classList.remove('dropTarget');
        });
    } catch {}
  };

  const _movePinInArray = (arr, fromId, toId, after) => {
    const fromIdx = arr.findIndex((p) => p.id === String(fromId));
    const toIdxRaw = arr.findIndex((p) => p.id === String(toId));
    if (fromIdx < 0 || toIdxRaw < 0 || fromId === toId) return arr;
    const item = arr[fromIdx];
    const next = arr.slice();
    next.splice(fromIdx, 1);
    const toIdx = next.findIndex((p) => p.id === String(toId));
    const ins = after ? (toIdx + 1) : toIdx;
    next.splice(Math.max(0, ins), 0, item);
    return next;
  };

  const _movePinToGroupEnd = (arr, pinId, groupId) => {
    const pid = String(pinId);
    const gid = normalizeGroupId(groupId);
    const idx = arr.findIndex(p => p && p.id === pid);
    if (idx < 0) return arr;
    const item = { ...arr[idx], groupId: gid };
    const next = arr.slice();
    next.splice(idx, 1);
    let lastIdx = -1;
    for (let i = 0; i < next.length; i++) {
      if (normalizeGroupId(next[i].groupId) === gid) lastIdx = i;
    }
    next.splice(lastIdx + 1, 0, item);
    return next;
  };

  const groups = loadPinGroups();
  const seen = new Set(groups.map(g => g.id));
  for (const p of allPins) {
    const gid = normalizeGroupId(p.groupId);
    if (!seen.has(gid)) {
      groups.push({ id: gid, name: getGroupName(gid) });
      seen.add(gid);
    }
  }

  const buildPinRow = (pin, groupId) => {
    const row = document.createElement("div");
    row.className = "wmeRcPinRow";
    row.dataset.pinId = String(pin.id);
    row.dataset.groupId = normalizeGroupId(groupId);


    try { _pinRowEls.set(String(pin.id), row); } catch {}
    try {
      const base = hexToRgb(pin.color);
      row.style.setProperty("--pinRGB", `${base.r},${base.g},${base.b}`);
    } catch {}

    if (pin.reminderAt && !pin.reminderDone) row.classList.add("wmeRcPinRowActive");

    const left = document.createElement("div");
    left.className = "wmeRcPinLeft";

    const name = document.createElement("div");
    name.className = "wmeRcPinName";
    name.textContent = pin.name || "Pinned place";

    const _pn = String(pin && pin.name ? pin.name : "");
    const _needsRename = (_pn.trim().length > PIN_NAME_MAX);
    if (_needsRename) {
      try { row.classList.add("wmeRcPinNeedsRename"); } catch {}
    }

    name.addEventListener("mouseenter", () => {
      try {
        if ((name.scrollWidth || 0) > (name.clientWidth || 0) + 2) {
          showPinNameTooltip(name, name.textContent || "");
        }
      } catch {}
    });
    name.addEventListener("mouseleave", () => { try { hidePinNameTooltip(0); } catch {} });


    const sub = document.createElement("div");
    sub.className = "wmeRcPinSub";

    const cdLine = document.createElement("div");
    cdLine.className = "wmeRcPinCountdownLine";

    const bad = document.createElement("span");
    bad.className = "wmeRcPinBadName";
    bad.textContent = "Rename";
    if (!_needsRename) bad.style.display = "none";

    const cdSpan = document.createElement("span");
    cdSpan.className = "wmeRcPinCountdownBadge";
    cdSpan.dataset.pinCountdown = pin.id;
    try { _pinCountdownEls.set(String(pin.id), cdSpan); } catch {}
    cdSpan.textContent = getPinCountdownTextMode(pin.reminderAt, Date.now(), _pinCountdownModes.get(pin.id) || 0);



    cdSpan.addEventListener("mouseenter", () => {
      try {
        if (!pin || !pin.reminderAt || pin.reminderDone) return;
        const now = Date.now();
        const ms = Number(pin.reminderAt) - now;
        if (!Number.isFinite(ms) || ms <= 0) return;
        if (ms >= 86400000) {
          showPinCountdownTooltip(cdSpan, pin.reminderAt);
        }
      } catch {}
    });
    cdSpan.addEventListener("mouseleave", () => { try { hidePinNameTooltip(0); } catch {} });
cdSpan.addEventListener("click", (e) => {
            try { e.preventDefault(); } catch {}
            try { e.stopPropagation(); } catch {}
            try { e.stopImmediatePropagation(); } catch {}
            const pid = String(cdSpan.dataset.pinCountdown || "");
            if (!pid) return false;

            const curTip = (_pinCountdownTipModes.get(pid) || 0);
            const nextTip = (curTip + 1) % 2;
            _pinCountdownTipModes.set(pid, nextTip);

            try { if (pin && pin.reminderAt && !pin.reminderDone) showPinCountdownTooltip(cdSpan, pin.reminderAt); } catch {}
            return false;
          });
cdLine.appendChild(cdSpan);
    try { sub.insertBefore(bad, cdLine); } catch { try { sub.appendChild(bad); } catch {} }
    sub.appendChild(cdLine);

    if (!pin.reminderAt || pin.reminderDone) cdLine.style.display = "none";

    left.appendChild(name);
    left.appendChild(sub);

    const btns = document.createElement("div");
    btns.className = "wmeRcPinBtns";

    const mkBtn = (icon, title, onClick) => {
      const b = document.createElement("div");
      b.className = "wmeRcPinBtn";
      b.title = title;
      b.innerHTML = `<span class="wmeRcI">${icon}</span>`;
      b.addEventListener("click", (ev) => { ev.stopPropagation(); onClick(); });
      return b;
    };

    const eyeBtn = mkBtn(ICONS.eye, "Hide on map", () => {
      try {
        const newHide = !(pin.hideOnMap === true);
        updatePin(pin.id, { hideOnMap: newHide });
      } catch {}
    });
    eyeBtn.classList.add("wmeRcPinBtnHoverOnly","wmeRcPinBtnEye");
    if (pin.hideOnMap === true) eyeBtn.classList.add("isHidden");
    btns.appendChild(eyeBtn);

    const editBtn = mkBtn(ICONS.edit, "Edit", () => openRenamePinModal(pin.id));
    editBtn.classList.add("wmeRcPinBtnHoverOnly","wmeRcPinBtnEdit");
    btns.appendChild(editBtn);

    const trashBtn = mkBtn(ICONS.trash, "Remove", () => confirmRemovePin(pin.id));
    trashBtn.classList.add("wmeRcPinBtnHoverOnly","wmeRcPinBtnTrash");
    btns.appendChild(trashBtn);

    const bellBtn = mkBtn(ICONS.bell, "Set reminder", () => openReminderModal(pin.id));
    bellBtn.classList.add("wmeRcPinBtnBell");
    btns.appendChild(bellBtn);
const beginReorder = (ev) => {
      try {
        if (!ev || (ev.button != null && ev.button !== 0)) return;
        ev.preventDefault(); ev.stopPropagation();
      } catch {}
      try {
        if (!_pinReorder || _pinReorder.active) return;

        const sourceBody = row.parentElement;
        if (!sourceBody || !sourceBody.classList.contains("wmeRcPinsGroupBody")) return;

        _pinReorder.active = true;
        _pinReorder.pid = String(pin.id);
        _pinReorder.row = row;
        _pinReorder.sourceBody = sourceBody;
        _pinReorder.currentBody = sourceBody;
        _pinReorder.pointerId = ev.pointerId;

        _pinReorder.sourceGroupId = normalizeGroupId(row.dataset.groupId || sourceBody.dataset.groupId || "");
        _pinReorder.currentGroupId = _pinReorder.sourceGroupId;

        _pinReorder.expandTimer = 0;
        _pinReorder.expandGid = null;
        _pinReorder.dropHdr = null;

        const r = row.getBoundingClientRect();
        _pinReorder.width = r.width;
        _pinReorder.height = r.height;

        _pinReorder.grabX = ev.clientX - r.left;
        _pinReorder.grabY = ev.clientY - r.top;
        _pinReorder.lastX = ev.clientX;
        _pinReorder.lastY = ev.clientY;

        const ph = document.createElement("div");
        ph.className = "wmeRcPinPlaceholder";
        ph.style.height = Math.max(34, Math.round(r.height)) + "px";
        _pinReorder.placeholder = ph;
        try { sourceBody.insertBefore(ph, row); } catch {}
        try { row.remove(); } catch {}

        const ghost = row.cloneNode(true);
        ghost.classList.add("wmeRcPinGhost");
        ghost.classList.remove("reorderFloat");
        ghost.style.position = "fixed";
        ghost.style.left = "0";
        ghost.style.top = "0";
        ghost.style.margin = "0";
        ghost.style.width = Math.round(r.width) + "px";
        ghost.style.height = Math.round(r.height) + "px";
        ghost.style.pointerEvents = "none";
        ghost.style.opacity = "0.95";
        ghost.style.transform = `translate3d(${Math.round(r.left)}px, ${Math.round(r.top)}px, 0)`;
        ghost.style.willChange = "transform";
        _pinReorder.ghost = ghost;

        const dragLayer = _getPinDragLayer();
        try { dragLayer.appendChild(ghost); } catch { try { document.body.appendChild(ghost); } catch {} }

        try { document.documentElement.classList.add("wmeRcNoSelect"); } catch {}
        try { window.addEventListener("pointermove", onReorderMove, true); } catch {}
        try { window.addEventListener("pointerup", endReorder, true); } catch {}
        try { window.addEventListener("pointercancel", endReorder, true); } catch {}
        try { row.setPointerCapture(ev.pointerId); } catch {}

        try {
          const empty = sourceBody.querySelector(".wmeRcPinsGroupEmpty");
          if (empty) empty.style.display = "none";
        } catch {}
      } catch (e) {
        try { console.warn("[Pins] reorder begin failed", e); } catch {}
        try { endReorder(); } catch {}
      }
    };

    const _setDropHdr = (gid) => {
      gid = normalizeGroupId(gid);
      try {
        if (_pinReorder.dropHdr && _pinReorder.dropHdr.dataset && normalizeGroupId(_pinReorder.dropHdr.dataset.groupId) === gid) return;
      } catch {}
      try { if (_pinReorder.dropHdr) _pinReorder.dropHdr.classList.remove("dropTarget"); } catch {}
      _pinReorder.dropHdr = null;
      if (!gid) return;
      let hdr = null;
      try { hdr = list.querySelector(`.wmeRcPinsGroupHdr[data-group-id="${CSS.escape(gid)}"]`); } catch {}
      if (!hdr) { try { hdr = list.querySelector(`.wmeRcPinsGroupHdr[data-group-id="${gid}"]`); } catch {} }
      if (hdr) {
        _pinReorder.dropHdr = hdr;
        try { hdr.classList.add("dropTarget"); } catch {}
      }
    };

    const _findGroupUnderPointer = (x, y) => {
      let el = null;
      try { el = document.elementFromPoint(x, y); } catch {}
      if (!el) return { body: null, gid: null, hdr: null };

      const overRow = el.closest ? el.closest(".wmeRcPinRow") : null;
      if (overRow) {
        const body = overRow.closest ? overRow.closest(".wmeRcPinsGroupBody") : null;
        const gid = normalizeGroupId(overRow.dataset.groupId || (body && body.dataset.groupId) || "");
        const hdr = body && body.previousElementSibling && body.previousElementSibling.classList && body.previousElementSibling.classList.contains("wmeRcPinsGroupHdr")
          ? body.previousElementSibling : null;
        return { body, gid, hdr, row: overRow };
      }

      const overBody = el.closest ? el.closest(".wmeRcPinsGroupBody") : null;
      if (overBody) {
        const gid = normalizeGroupId(overBody.dataset.groupId || "");
        const hdr = overBody.previousElementSibling && overBody.previousElementSibling.classList && overBody.previousElementSibling.classList.contains("wmeRcPinsGroupHdr")
          ? overBody.previousElementSibling : null;
        return { body: overBody, gid, hdr, row: null };
      }

      const overHdr = el.closest ? el.closest(".wmeRcPinsGroupHdr") : null;
      if (overHdr) {
        const gid = normalizeGroupId(overHdr.dataset.groupId || "");
        let body = null;
        try {
          const wrap = overHdr.closest && overHdr.closest(".wmeRcPinsGroup");
          body = wrap ? wrap.querySelector(".wmeRcPinsGroupBody") : null;
        } catch {}
        return { body, gid, hdr: overHdr, row: null };
      }

      return { body: null, gid: null, hdr: null, row: null };
    };

    const _ensureExpandedDuringHover = (gid) => {
      gid = normalizeGroupId(gid);
      if (!gid) return;
      if (_pinReorder.expandGid === gid) return;
      _pinReorder.expandGid = gid;
      try { if (_pinReorder.expandTimer) clearTimeout(_pinReorder.expandTimer); } catch {}
      _pinReorder.expandTimer = setTimeout(() => {
        try {
          const wrap = list.querySelector(`.wmeRcPinsGroup[data-group-id="${CSS.escape(gid)}"]`);
          if (wrap && wrap.classList.contains("collapsed")) {
            wrap.classList.remove("collapsed");
            setGroupCollapsed(gid, false);
          }
        } catch {}
      }, 180);
    };

    const _placePlaceholderInBody = (body, pointerY) => {
      const ph = _pinReorder.placeholder;
      if (!ph || !body) return;

      try {
        const empty = body.querySelector(".wmeRcPinsGroupEmpty");
        if (empty) empty.style.display = "none";
      } catch {}

      const rows = Array.from(body.querySelectorAll(":scope > .wmeRcPinRow"));
      let beforeEl = null;

      for (const r of rows) {
        try {
          const rr = r.getBoundingClientRect();
          const mid = rr.top + rr.height / 2;
          if (pointerY < mid) { beforeEl = r; break; }
        } catch {}
      }

      const key = (body.dataset && body.dataset.groupId ? body.dataset.groupId : "") + "|" + (beforeEl ? beforeEl.dataset.pinId : "END");
      if (_pinReorder.lastPlaceKey === key) return;
      _pinReorder.lastPlaceKey = key;

      try {
        if (beforeEl) body.insertBefore(ph, beforeEl);
        else body.appendChild(ph);
      } catch {}
    };

    const onReorderMove = (ev) => {
      try {
        if (!_pinReorder.active) return;
        if (_pinReorder.pointerId != null && ev.pointerId != null && ev.pointerId !== _pinReorder.pointerId) return;
        try { ev.preventDefault(); } catch {}

        _pinReorder.lastX = ev.clientX;
        _pinReorder.lastY = ev.clientY;

        if (_pinReorder.raf) return;
        _pinReorder.raf = requestAnimationFrame(() => {
          _pinReorder.raf = 0;

          const x = (_pinReorder.lastX ?? 0) - (_pinReorder.grabX ?? 0);
          const y = (_pinReorder.lastY ?? 0) - (_pinReorder.grabY ?? 0);

          try { if (_pinReorder.ghost) _pinReorder.ghost.style.transform = `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`; } catch {}

          const hit = _findGroupUnderPointer(_pinReorder.lastX, _pinReorder.lastY);
          const body = hit.body || _pinReorder.currentBody;
          const gid = normalizeGroupId(hit.gid || (_pinReorder.currentBody && _pinReorder.currentBody.dataset.groupId) || _pinReorder.currentGroupId || "");

          if (gid) {
            _setDropHdr(gid);
            _ensureExpandedDuringHover(gid);
          }

          if (body && body !== _pinReorder.currentBody) {
            _pinReorder.currentBody = body;
            _pinReorder.currentGroupId = gid;
          }

          if (body) {
            _placePlaceholderInBody(body, _pinReorder.lastY);
          }
        });
      } catch {}
    };

    const _syncGroupCount = (gid) => {
      gid = normalizeGroupId(gid);
      if (!gid) return;
      try {
        const body = list.querySelector(`.wmeRcPinsGroupBody[data-group-id="${CSS.escape(gid)}"]`);
        const hdr = list.querySelector(`.wmeRcPinsGroupHdr[data-group-id="${CSS.escape(gid)}"]`);
        if (!body || !hdr) return;
        const c = hdr.querySelector(".wmeRcPinsGroupCount");
        if (c) c.textContent = String(body.querySelectorAll(":scope > .wmeRcPinRow").length);
      } catch {}
    };

    const _syncEmptyHint = (gid) => {
      gid = normalizeGroupId(gid);
      if (!gid) return;
      let body = null;
      try { body = list.querySelector(`.wmeRcPinsGroupBody[data-group-id="${CSS.escape(gid)}"]`); } catch {}
      if (!body) return;
      const hasRows = body.querySelectorAll(":scope > .wmeRcPinRow").length > 0;
      let empty = body.querySelector(".wmeRcPinsGroupEmpty");
      if (hasRows) {
        if (empty) { try { empty.remove(); } catch {} }
      } else {
        if (!empty) {
          empty = document.createElement("div");
          empty.className = "wmeRcPinsGroupEmpty";
          empty.textContent = "Drop pins here";
          body.appendChild(empty);
        }
        try { empty.style.display = ""; } catch {}
      }
    };

    const _persistPinsFromDOM = () => {
      const pins = loadPins();
      const byId = new Map(pins.map(p => [String(p.id), p]));
      const next = [];
      const bodies = Array.from(list.querySelectorAll(".wmeRcPinsGroupBody"));
      for (const body of bodies) {
        const gid = normalizeGroupId(body.dataset.groupId || "");
        const rows = Array.from(body.querySelectorAll(":scope > .wmeRcPinRow"));
        for (const r of rows) {
          const id = String(r.dataset.pinId || "");
          const p = byId.get(id);
          if (!p) continue;
          next.push({ ...p, groupId: gid });
          byId.delete(id);
        }
      }
      for (const p of byId.values()) next.push(p);
      savePins(next);
    };

    const endReorder = (ev) => {
      try {
        if (!_pinReorder.active) return;
        if (_pinReorder.pointerId != null && ev && ev.pointerId != null && ev.pointerId !== _pinReorder.pointerId) return;
      } catch {}

      try {
        window.removeEventListener("pointermove", onReorderMove, true);
        window.removeEventListener("pointerup", endReorder, true);
        window.removeEventListener("pointercancel", endReorder, true);
      } catch {}

      try { if (_pinReorder.raf) cancelAnimationFrame(_pinReorder.raf); } catch {}
      _pinReorder.raf = 0;

      const ph = _pinReorder.placeholder;
      const ghost = _pinReorder.ghost;

      let destBody = _pinReorder.currentBody || _pinReorder.sourceBody;
      if (!destBody || !destBody.classList || !destBody.classList.contains("wmeRcPinsGroupBody")) destBody = _pinReorder.sourceBody;

      const destGid = normalizeGroupId((destBody && destBody.dataset && destBody.dataset.groupId) || _pinReorder.currentGroupId || _pinReorder.sourceGroupId || "");
      const srcGid = normalizeGroupId(_pinReorder.sourceGroupId || "");

      try {
        if (ph && destBody) destBody.insertBefore(row, ph);
        else if (destBody) destBody.appendChild(row);
      } catch {}

      try { if (ph) ph.remove(); } catch {}
      try { if (ghost) ghost.remove(); } catch {}
      try { document.documentElement.classList.remove("wmeRcNoSelect"); } catch {}
      try { if (_pinReorder.dropHdr) _pinReorder.dropHdr.classList.remove("dropTarget"); } catch {}

      try { row.dataset.groupId = destGid; } catch {}

      try { _persistPinsFromDOM(); } catch {}

      try { _syncEmptyHint(srcGid); } catch {}
      try { _syncEmptyHint(destGid); } catch {}
      try { _syncGroupCount(srcGid); } catch {}
      try { _syncGroupCount(destGid); } catch {}

      try {
        _pinReorder.active = false;
        _pinReorder.pid = null;
        _pinReorder.row = null;
        _pinReorder.sourceBody = null;
        _pinReorder.currentBody = null;
        _pinReorder.placeholder = null;
        _pinReorder.ghost = null;
        _pinReorder.pointerId = null;
        _pinReorder.lastPlaceKey = null;
        _pinReorder.sourceGroupId = null;
        _pinReorder.currentGroupId = null;
        try { if (_pinReorder.expandTimer) clearTimeout(_pinReorder.expandTimer); } catch {}
        _pinReorder.expandTimer = 0;
        _pinReorder.expandGid = null;
        _pinReorder.dropHdr = null;
      } catch {}
    };
row.addEventListener("pointerdown", (ev) => {
      try {
        const t = ev && ev.target;
        if (!t) return;
        if (t.closest && t.closest(".wmeRcPinBtns")) return;
        if (t.closest && t.closest("button, a, input, textarea, select, option, label")) return;
      } catch {}

      try {
        if (!ev || (ev.button != null && ev.button !== 0)) return;
        if (_pinReorder && _pinReorder.active) return;

        const pid = ev.pointerId;
        const sx = ev.clientX, sy = ev.clientY;
        const downEv = ev;

        let started = false;

        const cleanup = () => {
          try { window.removeEventListener("pointermove", onArmMove, true); } catch {}
          try { window.removeEventListener("pointerup", onArmEnd, true); } catch {}
          try { window.removeEventListener("pointercancel", onArmEnd, true); } catch {}
        };

        const onArmMove = (mv) => {
          try {
            if (pid != null && mv.pointerId != null && mv.pointerId !== pid) return;

            const dx = mv.clientX - sx;
            const dy = mv.clientY - sy;

            if (!started && (Math.abs(dx) > 6 || Math.abs(dy) > 6)) {
              started = true;
              cleanup();

              beginReorder(downEv);

              try { onReorderMove(mv); } catch {}
            }
          } catch {}
        };

        const onArmEnd = (up) => {
          try {
            if (pid != null && up && up.pointerId != null && up.pointerId !== pid) return;
          } catch {}
          const didStart = started;
          cleanup();

          try {
            if (didStart) return;
            if (_pinReorder && _pinReorder.active) return;

            const t = up && up.target;
            if (!t) return;
            if (t.closest && t.closest(".wmeRcPinBtns")) return;
            if (t.closest && t.closest("button, a, input, textarea, select, option, label")) return;

            const dx = (up.clientX - sx);
            const dy = (up.clientY - sy);
            if (Math.abs(dx) <= 6 && Math.abs(dy) <= 6) {
              try { up.preventDefault(); up.stopPropagation(); } catch {}
              try { if (row.classList.contains("dragging")) return; } catch {}
              if (_needsRename) { toast(`Rename this pin (max ${PIN_NAME_MAX} chars)`); try { openRenamePinModal(pin.id); } catch {} return; }
              jumpToPin(pin);
            }
          } catch {}
        };

        window.addEventListener("pointermove", onArmMove, true);
        window.addEventListener("pointerup", onArmEnd, true);
        window.addEventListener("pointercancel", onArmEnd, true);
      } catch {}
    }, true);

    row.appendChild(left);
    row.appendChild(btns);
    return row;
  };

  for (const g of groups) {
    const gid = normalizeGroupId(g.id);
    const groupWrap = document.createElement("div");
    groupWrap.className = "wmeRcPinsGroup";
    groupWrap.dataset.groupId = gid;

    if (isGroupCollapsed(gid)) groupWrap.classList.add("collapsed");

    const gh = document.createElement("div");
    gh.className = "wmeRcPinsGroupHdr";
    gh.dataset.groupId = gid;

    const groupPins = allPins.filter(p => normalizeGroupId(p.groupId) === gid);

    const topLeft = document.createElement("div");
    topLeft.className = "wmeRcPinsGroupTop";

    const twisty = document.createElement("div");
    twisty.className = "wmeRcPinsGroupTwisty";
    twisty.innerHTML = `<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M7 10l5 5 5-5H7z"/></svg>`;

    const gName = document.createElement("div");
    gName.className = "wmeRcPinsGroupName";
    gName.textContent = g.name || "Folder";

    topLeft.appendChild(twisty);

    const gEmoji = document.createElement("div");
    gEmoji.className = "wmeRcPinsGroupEmoji";
    gEmoji.textContent = (g && typeof g.emoji === "string") ? g.emoji : "";
    if (gEmoji.textContent) topLeft.appendChild(gEmoji);

    topLeft.appendChild(gName);

    const right = document.createElement("div");
    right.className = "wmeRcPinsGroupActions";

    const gCount = document.createElement("div");
    gCount.className = "wmeRcPinsGroupCount";
    gCount.textContent = String(groupPins.length);

    const mkGBtn = (icon, title, onClick) => {
      const b = document.createElement("div");
      b.className = "wmeRcPinsGroupBtn";
      b.title = title;
      b.innerHTML = `<span class="wmeRcI">${icon}</span>`;
      b.addEventListener("click", (ev) => { ev.stopPropagation(); onClick(); });
      return b;
    };

    if (gid !== "default") {
      right.appendChild(mkGBtn(ICONS.edit, "Rename folder", () => {
        openGroupNameModal({
          title: "Rename folder",
          initial: g.name || "",
          initialEmoji: (g && typeof g.emoji === "string") ? g.emoji : "",
          placeholder: "Name",
          okText: "Save",
          onSubmit: (nm, emoji) => {
            const gs = loadPinGroups();
            const idx = gs.findIndex(x => x && x.id === gid);
            if (idx >= 0) {
              gs[idx] = { ...gs[idx], name: nm, emoji: (typeof emoji === 'string') ? emoji : (gs[idx].emoji || '') };
              savePinGroups(gs);
              renderPinsPanel();
            }
          }
        });
      }));
      right.appendChild(mkGBtn(ICONS.trash, "Remove folder", () => {
        openRemoveFolderModal(gid);
      }));
    } else {
      right.appendChild(mkGBtn(ICONS.trash, "Clear pins in default folder", () => {
        try {
          const gnm = String(getGroupName("default") || g?.name || "(no folder)");
          const pins = loadPins();
          const removed = pins.filter(p => normalizeGroupId(p.groupId) === "default").length;
          if (!removed) return;
          openClearDefaultFolderPinsModal({ folderName: gnm, count: removed, onConfirm: () => {
const nextPins = pins.filter(p => normalizeGroupId(p.groupId) !== "default");
          savePins(nextPins);
          try { renderPinsMarkers(); } catch {}
          renderPinsPanel();
          try { _schedulePinsPanelAvoidDock(); } catch {}
        }});
        } catch (e) { console.error(e); }
      }));
    }

    right.appendChild(gCount);

    gh.appendChild(topLeft);
    gh.appendChild(right);

    gh.addEventListener("click", () => {
      if (_pinDragState && _pinDragState.id) return;
      const collapsed = groupWrap.classList.toggle("collapsed");
      setGroupCollapsed(gid, collapsed);
      try { requestAnimationFrame(() => { try { applyPinsPanelHeightAuto(); } catch {} }); } catch { try { applyPinsPanelHeightAuto(); } catch {} }
    });

    const onGroupDragOver = (ev) => {
      if (!_pinDragState.id) return;
      ev.preventDefault();
      try {
        _clearPinDropMarks();
        gh.classList.add("dropTarget");
        _pinDragState.overGroup = gid;
      } catch {}
    };
    gh.addEventListener("dragover", onGroupDragOver);
    gh.addEventListener("dragenter", onGroupDragOver);
    gh.addEventListener("dragleave", () => { try { gh.classList.remove("dropTarget"); } catch {} });
    gh.addEventListener("drop", (ev) => {
      if (!_pinDragState.id) return;
      ev.preventDefault();
      try {
        gh.classList.remove("dropTarget");
        const fromId = _pinDragState.id;
        let nextPins = _movePinToGroupEnd(loadPins(), fromId, gid);
        savePins(nextPins);
        renderPinsPanel();
      } catch (e) { console.error(e); }
    });

    const body = document.createElement("div");
    body.className = "wmeRcPinsGroupBody";
    body.dataset.groupId = gid;

    body.addEventListener("dragover", onGroupDragOver);
    body.addEventListener("dragenter", onGroupDragOver);
    body.addEventListener("dragleave", () => { try { gh.classList.remove("dropTarget"); } catch {} });
    body.addEventListener("drop", (ev) => {
      if (!_pinDragState.id) return;
      ev.preventDefault();
      try {
        gh.classList.remove("dropTarget");
        const fromId = _pinDragState.id;
        let nextPins = _movePinToGroupEnd(loadPins(), fromId, gid);
        savePins(nextPins);
        renderPinsPanel();
      } catch (e) { console.error(e); }
    });

    if (groupPins.length === 0) {
      const empty = document.createElement("div");
      empty.className = "wmeRcPinsGroupEmpty";
      empty.textContent = "Drop pins here";
      body.appendChild(empty);
    } else {
      for (const pin of groupPins) {
        body.appendChild(buildPinRow(pin, gid));
      }
    }

    groupWrap.appendChild(gh);
    groupWrap.appendChild(body);
    list.appendChild(groupWrap);
  }

  pinsPanelEl.appendChild(hdr);
  pinsPanelEl.appendChild(list);
  try { requestAnimationFrame(() => { try { applyPinsPanelHeightAuto(); } catch {} }); } catch { try { applyPinsPanelHeightAuto(); } catch {} }
  } else {
    pinsPanelEl.appendChild(hdr);
    try {
      const hh = Math.round((hdr.getBoundingClientRect().height || 44) + 2);
      _pinsPanelLastAutoAt = _pinsNow();
      _pinsPanelSuppressROUntil = _pinsPanelLastAutoAt + 650;
      _pinsPanelAutoSizing = true;
      pinsPanelEl.style.height = `${hh}px`;
      savePinsPanelSize({ h: hh, manual: true, u: 1 });
      pinsPanelEl.dataset.manualHeight = "1";
    } catch {} finally { _pinsPanelAutoSizing = false; }
  }
  const _hasActiveReminder = allPins.some(p => p && p.reminderAt && !p.reminderDone);
  if (_hasActiveReminder) startPinsCountdownLoop(); else stopPinsCountdownLoop();
}



function stopPinsCountdownLoop() {
  _pinsCountdownTicking = false;
  if (pinsCountdownTimerId) {
    try { clearTimeout(pinsCountdownTimerId); } catch {}
    pinsCountdownTimerId = null;
  }
}

function getPinCountdownText(reminderAt, nowMs) {
  if (!reminderAt) return "";
  const ms = Number(reminderAt) - Number(nowMs);
  if (!Number.isFinite(ms)) return "";
  if (ms <= 0) return "";

  const totalSec = Math.max(0, Math.floor(ms / 1000));

  const day = 86400;
  const week = 7 * day;
  const month = 30 * day;
  const year = 365 * day;

  const plural = (n, w) => `${n} ${w}${n === 1 ? "" : "s"}`;

  if (totalSec >= year) return plural(Math.floor(totalSec / year), "year");
  if (totalSec >= month) return plural(Math.floor(totalSec / month), "month");
  if (totalSec >= week) return plural(Math.floor(totalSec / week), "week");
  if (totalSec >= day) return plural(Math.floor(totalSec / day), "day");

  const h = Math.floor(totalSec / 3600);
  const m = Math.floor((totalSec % 3600) / 60);
  const s = totalSec % 60;

  if (h > 0) return `${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
  return `${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
}


function getPinCountdownExact(reminderAt, nowMs) {
  if (!reminderAt) return "";
  const ms = Number(reminderAt) - Number(nowMs);
  if (!Number.isFinite(ms)) return "";
  const totalSec = Math.max(0, Math.floor(ms / 1000));
  const day = 86400;
  const days = Math.floor(totalSec / day);
  const rem = totalSec % day;
  const h = Math.floor(rem / 3600);
  const m = Math.floor((rem % 3600) / 60);
  const s = rem % 60;
  if (days <= 0) return `${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
  const dword = days === 1 ? "day" : "days";
  return `${days} ${dword} ${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
}

function getPinCountdownTextMode(reminderAt, nowMs, mode) {
  if (!reminderAt) return "";
  const ms = Number(reminderAt) - Number(nowMs);
  if (!Number.isFinite(ms)) return "";
  const totalSec = Math.max(0, Math.floor(ms / 1000));
  const days = Math.floor(totalSec / 86400);
  const hours = Math.floor((totalSec % 86400) / 3600);
  const mins = Math.floor((totalSec % 3600) / 60);

  if (mode === 1) {
    const parts = [];
    if (days > 0) parts.push(days + "d");
    if (hours > 0 || days > 0) parts.push(hours + "h");
    parts.push(mins + "m");
    return parts.join(" ");
  }

  if (mode === 2) {
    const human = getPinCountdownText(reminderAt, nowMs);
    const exact = getPinCountdownExact(reminderAt, nowMs);
    if (!human) return exact;
    if (!exact) return human;
    return human + " - " + exact;
  }

  return getPinCountdownText(reminderAt, nowMs);
}



function updatePinsCountdowns() {
  if (!pinsPanelEl || pinsPanelEl.classList.contains("hidden")) return;

  const now = Date.now();

  const pins = loadPins();
  let hasActive = false;

  for (const p of pins) {
    const id = String(p.id);
    const badge = _pinCountdownEls.get(id);
    if (!badge) continue;

    const line = badge.parentElement;
    const row = _pinRowEls.get(id);

    const active = !!(p.reminderAt && !p.reminderDone);
    if (row) {
      if (active && !row.classList.contains("wmeRcPinRowActive")) row.classList.add("wmeRcPinRowActive");
      if (!active && row.classList.contains("wmeRcPinRowActive")) row.classList.remove("wmeRcPinRowActive");
    }

    if (!p.reminderAt || p.reminderDone) {
      if (line) line.style.display = "none";
      badge.textContent = "";
      continue;
    }

    hasActive = true;
    if (line) line.style.display = "";

    const ms = Number(p.reminderAt) - now;
    if (!Number.isFinite(ms)) continue;

    if (ms <= 0) {
      badge.textContent = "00:00";
      try { checkRemindersNow(); } catch {}
      continue;
    }

    const next = getPinCountdownText(p.reminderAt, now);
    if (badge.textContent !== next) badge.textContent = next;
  }

  if (!hasActive) stopPinsCountdownLoop();
}

function startPinsCountdownLoop() {
  if (_pinsCountdownTicking) return;
  _pinsCountdownTicking = true;

  const tick = () => {
    if (!_pinsCountdownTicking) return;
    try { updatePinsCountdowns(); } catch {}
    const n = Date.now();
    const delay = 1000 - (n % 1000) + 8;
    pinsCountdownTimerId = setTimeout(tick, delay);
  };

  try { updatePinsCountdowns(); } catch {}
  const n0 = Date.now();
  const d0 = 1000 - (n0 % 1000) + 8;
  pinsCountdownTimerId = setTimeout(tick, d0);
}


function removePinNow(pinId) {
  const pins = loadPins().filter((p) => p.id !== String(pinId));
  savePins(pins);
  renderPinsPanel();
  toast("Removed pin");
}

function confirmRemovePin(pinId) {
  try {
    const pin = loadPins().find((p) => p.id === String(pinId));
    const pinName = (pin && pin.name) ? pin.name : "this pin";
    openModal({
      iconSvg: ICONS.trash,
      title: "Delete pin",
      bodyBuilder: ({ body, close, modal }) => {
        const msg = document.createElement("div");
        msg.className = "wmeRcHint";
        msg.textContent = `Are you sure to delete ${pinName}?`;
        body.appendChild(msg);

        const actions = document.createElement("div");
        actions.className = "wmeRcModalActions";

        const btnCancel = document.createElement("div");
        btnCancel.className = "wmeRcModalBtn";
        btnCancel.textContent = "Cancel";
        btnCancel.addEventListener("click", () => close());

        const btnDel = document.createElement("div");
        btnDel.className = "wmeRcModalBtn primary danger";
        btnDel.textContent = "Delete";
        btnDel.addEventListener("click", () => {
          close();
          removePinNow(pinId);
        });

        actions.appendChild(btnCancel);
        actions.appendChild(btnDel);
        body.appendChild(actions);
},
    });
  } catch (e) {
    console.error(e);
  }
}

function updatePin(pinId, patch) {
  const pins = loadPins();
  const i = pins.findIndex((p) => p.id === String(pinId));
  if (i < 0) return;
  pins[i] = { ...pins[i], ...patch };
  try {
    if (patch && Object.prototype.hasOwnProperty.call(patch, 'reminderAt')) {
      clearFiredKeysForPin(pinId);
    } else if (patch && patch.reminderDone === false) {
      clearFiredKeysForPin(pinId);
    }
  } catch {}

  savePins(pins);
  try {
    const p = pins.find(x => x && x.id === String(pinId));
    if (p) scheduleReminderTimer(p);
    else clearReminderTimer(pinId);
  } catch {}
  renderPinsPanel();
  startReminderLoop();
}

function openRenamePinModal(pinId) {
  const pin = loadPins().find((p) => p.id === String(pinId));
  if (!pin) return;

  openModal({
    title: "Edit Pin",
    iconSvg: ICONS.edit,
    bodyBuilder: ({ body, close, modal }) => {
      let chosenColor = normalizePinColor(pin.color);
      const inp = document.createElement("input");
      inp.className = "wmeRcInput";
      inp.placeholder = "Name…";
      inp.value = pin.name || "";
      const pinNameLimitMsg = attachMaxLen(inp, 28);

      const hint = document.createElement("div");
      hint.className = "wmeRcHint";
      hint.textContent = "Tip: keep it short so it fits nicely.";


      const colorLbl = document.createElement("div");
      colorLbl.className = "wmeRcHint";
      colorLbl.textContent = "Marker color";

const colorRow = document.createElement("div");
colorRow.className = "wmeRcColorRow";
try { colorRow.style.setProperty("--pinC", chosenColor); } catch {}

let customColors = loadCustomPinColors();
let editCustomIndex = null;

const swatches = [];
function setColor(hex){
  chosenColor = normalizePinColor(hex);
  for (const s of swatches) s.classList.toggle("sel", s.dataset.c === chosenColor);
  try { colorRow.style.setProperty("--pinC", chosenColor); } catch {}
}

function parseAnyColorLocal(s){
  try{
    const str = String(s || "").trim();
    if (!str) return null;
    if (str.startsWith("#")) return normalizePinColor(str);
    const m = str.match(/^rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})(?:\s*,\s*([0-9.]+)\s*)?\)$/i);
    if (m){
      const r = Math.max(0, Math.min(255, Number(m[1])));
      const g = Math.max(0, Math.min(255, Number(m[2])));
      const b = Math.max(0, Math.min(255, Number(m[3])));
      return normalizePinColor(`#${((1<<24)+(r<<16)+(g<<8)+b).toString(16).slice(1)}`);
    }
    return null;
  }catch{ return null; }
}
function hexToRgbLocal(hex){
  try{
    const h = normalizePinColor(hex);
    const x = h.replace("#","");
    const r = parseInt(x.slice(0,2),16);
    const g = parseInt(x.slice(2,4),16);
    const b = parseInt(x.slice(4,6),16);
    return { r,g,b };
  }catch{ return { r:0,g:0,b:0 }; }
}
const pickTextOnLocal = (hex) => {
  try{
    const h = normalizePinColor(hex) || "#000000";
    const x = h.replace("#","");
    const r = parseInt(x.slice(0,2),16);
    const g = parseInt(x.slice(2,4),16);
    const b = parseInt(x.slice(4,6),16);
    const srgb = [r,g,b].map(v => {
      const c = v/255;
      return c <= 0.03928 ? c/12.92 : ((c+0.055)/1.055)**2.4;
    });
    const L = 0.2126*srgb[0] + 0.7152*srgb[1] + 0.0722*srgb[2];
    return (L > 0.62) ? "#111" : "#fff";
  }catch{ return "#111"; }
};

for (const c of PIN_COLOR_PRESETS){
  const sw = document.createElement("div");
  sw.className = "wmeRcColorSwatch";
  sw.style.setProperty("--c", c);
  sw.dataset.c = normalizePinColor(c);
  sw.title = c;
  sw.addEventListener("click", (e) => { e.preventDefault(); e.stopPropagation(); setColor(c); });
  swatches.push(sw);
  colorRow.appendChild(sw);
}

const plusBtn = document.createElement("div");
plusBtn.className = "wmeRcColorPlus";
plusBtn.textContent = "+";
plusBtn.title = "Custom color";
colorRow.appendChild(plusBtn);

let colorPop = null;

function positionPop(anchorEl){
  try{
    const r = anchorEl.getBoundingClientRect();
    const vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
    const vh = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0);
    const w = 286;
    let left = r.right + 10;
    if (left + w > vw - 10) left = r.left - w - 10;
    left = Math.min(vw - w - 10, Math.max(10, left));
    let top = r.bottom + 8;
    const maxH = 320;
    if (top + maxH + 10 > vh) top = Math.max(10, r.top - 8 - maxH);
    colorPop.style.left = left + "px";
    colorPop.style.top = top + "px";
  }catch{}
}
function closePop(){
  try { if (colorPop) colorPop.remove(); } catch {}
  colorPop = null;
  try { document.removeEventListener("pointerdown", onOutside, true); } catch {}
}
function onOutside(e){
  try{
    if (!colorPop) return;
    if (colorPop.contains(e.target)) return;
    if (plusBtn.contains(e.target)) return;
    closePop();
  }catch{}
}


function openPop({ initial, editIndex, anchorEl }){
  closePop();
  colorPop = document.createElement("div");
  colorPop.className = "wmeRcColorPop";

  let cur = normalizePinColor(initial || chosenColor || "#ff8a00") || "#ff8a00";

  const topRow = document.createElement('div');
  topRow.className = 'wmeRcColorTopRow';

  const swBig = document.createElement('div');
  swBig.className = 'wmeRcColorSwatchBig';

  const stats = document.createElement('div');
  stats.className = 'wmeRcColorStats';

  const stat1 = document.createElement('div');
  stat1.className = 'wmeRcColorStatLine';
  stat1.innerHTML = `<span><span class="wmeRcColorStatKey">HEX</span><span class="wmeRcColorStatVal" data-k="hex">#000000</span></span>
                     <span><span class="wmeRcColorStatKey">RGB</span><span class="wmeRcColorStatVal" data-k="rgb">0, 0, 0</span></span>`;

  const stat2 = document.createElement('div');
  stat2.className = 'wmeRcColorStatLine';
  stat2.innerHTML = `<span><span class="wmeRcColorStatKey">HSL</span><span class="wmeRcColorStatVal" data-k="hsl">0, 0%, 0%</span></span>`;

  stats.appendChild(stat1);
  stats.appendChild(stat2);

  topRow.appendChild(swBig);
  topRow.appendChild(stats);
  colorPop.appendChild(topRow);

  const pickerRow = document.createElement('div');
  pickerRow.className = 'wmeRcPickerRow';

  const sv = document.createElement('div');
  sv.className = 'wmeRcPickerSV';
  const svDot = document.createElement('div');
  svDot.className = 'wmeRcPickerSVDot';
  sv.appendChild(svDot);

  const hue = document.createElement('div');
  hue.className = 'wmeRcPickerHue';
  const hueThumb = document.createElement('div');
  hueThumb.className = 'wmeRcPickerHueThumb';
  hue.appendChild(hueThumb);

  const fields = document.createElement('div');
  fields.className = 'wmeRcPickerFields';

  const mkRow = (lab, input) => {
    const row = document.createElement('div');
    row.className = 'wmeRcPickerFieldRow';
    const l = document.createElement('label');
    l.textContent = lab;
    row.appendChild(l);
    row.appendChild(input);
    return row;
  };

  const hexField = document.createElement('input');
  hexField.className = 'wmeRcPickerField';
  hexField.placeholder = '#RRGGBB';
  hexField.autocomplete = 'off';
  hexField.spellcheck = false;

  const rField = document.createElement('input');
  rField.className = 'wmeRcPickerField';
  rField.placeholder = '0';
  rField.inputMode = 'numeric';
  rField.autocomplete = 'off';

  const gField = document.createElement('input');
  gField.className = 'wmeRcPickerField';
  gField.placeholder = '0';
  gField.inputMode = 'numeric';
  gField.autocomplete = 'off';

  const bField = document.createElement('input');
  bField.className = 'wmeRcPickerField';
  bField.placeholder = '0';
  bField.inputMode = 'numeric';
  bField.autocomplete = 'off';

  fields.appendChild(mkRow('HEX', hexField));
  fields.appendChild(mkRow('R', rField));
  fields.appendChild(mkRow('G', gField));
  fields.appendChild(mkRow('B', bField));

  pickerRow.appendChild(sv);
  pickerRow.appendChild(hue);
  pickerRow.appendChild(fields);
  colorPop.appendChild(pickerRow);

  const actions = document.createElement('div');
  actions.className = 'wmeRcColorActions';

  const btnSavePreset = document.createElement('div');
  btnSavePreset.className = 'wmeRcColorSavePreset';
  btnSavePreset.textContent = 'Save preset';

  const btnDelete = document.createElement('button');
  btnDelete.className = 'wmeRcColorDelete';
  btnDelete.type = 'button';
  btnDelete.textContent = 'Delete';

  const btnDone = document.createElement('button');
  btnDone.className = 'wmeRcColorDone';
  btnDone.type = 'button';
  btnDone.textContent = 'Done';

  const isPresetEdit = Number.isFinite(editIndex) && editIndex != null;
  if (isPresetEdit){
    btnSavePreset.style.display = 'none';
    btnDone.textContent = 'Save';
    actions.appendChild(btnDelete);
  }

  actions.appendChild(btnSavePreset);
  actions.appendChild(btnDone);
  colorPop.appendChild(actions);

  const clamp01 = (x) => Math.max(0, Math.min(1, x));
  const clamp255 = (x) => {
    const n = Number(String(x || '').replace(/[^\d]/g, ''));
    if (!Number.isFinite(n)) return null;
    return Math.max(0, Math.min(255, Math.round(n)));
  };

  const hexToRgb = (hex) => {
    try{
      const h = normalizePinColor(hex);
      const x = h.replace('#','');
      return { r: parseInt(x.slice(0,2),16), g: parseInt(x.slice(2,4),16), b: parseInt(x.slice(4,6),16) };
    }catch{ return { r:0,g:0,b:0 }; }
  };

  const rgbToHex = (r,g,b) => {
    const n = (1<<24) + ((r&255)<<16) + ((g&255)<<8) + (b&255);
    return '#' + n.toString(16).slice(1);
  };

  const rgbToHsl = (r,g,b) => {
    r/=255; g/=255; b/=255;
    const max = Math.max(r,g,b), min = Math.min(r,g,b);
    let h=0,s=0,l=(max+min)/2;
    if (max!==min){
      const d = max-min;
      s = l>0.5 ? d/(2-max-min) : d/(max+min);
      switch(max){
        case r: h = (g-b)/d + (g<b?6:0); break;
        case g: h = (b-r)/d + 2; break;
        case b: h = (r-g)/d + 4; break;
      }
      h/=6;
    }
    return { h: Math.round(h*360), s: Math.round(s*100), l: Math.round(l*100) };
  };

  const rgbToHsv = (r,g,b) => {
    r/=255; g/=255; b/=255;
    const max = Math.max(r,g,b), min = Math.min(r,g,b);
    const d = max-min;
    let h=0;
    if (d!==0){
      if (max===r) h = ((g-b)/d) % 6;
      else if (max===g) h = (b-r)/d + 2;
      else h = (r-g)/d + 4;
      h = Math.round(h*60);
      if (h<0) h += 360;
    }
    const s = max===0 ? 0 : d/max;
    const v = max;
    return { h, s, v };
  };

  const hsvToRgb = (h,s,v) => {
    h = ((h%360)+360)%360;
    const c = v*s;
    const x = c*(1-Math.abs(((h/60)%2)-1));
    const m = v-c;
    let rp=0,gp=0,bp=0;
    if (h<60){rp=c;gp=x;bp=0;}
    else if (h<120){rp=x;gp=c;bp=0;}
    else if (h<180){rp=0;gp=c;bp=x;}
    else if (h<240){rp=0;gp=x;bp=c;}
    else if (h<300){rp=x;gp=0;bp=c;}
    else {rp=c;gp=0;bp=x;}
    return { r: Math.round((rp+m)*255), g: Math.round((gp+m)*255), b: Math.round((bp+m)*255) };
  };

  let hsv = (() => {
    const rgb = hexToRgb(cur);
    return rgbToHsv(rgb.r, rgb.g, rgb.b);
  })();

  const setCur = (hex) => {
    const parsed = parseAnyColorLocal(hex);
    if (!parsed) return false;
    cur = parsed;
    const rgb = hexToRgb(cur);
    hsv = rgbToHsv(rgb.r, rgb.g, rgb.b);
    return true;
  };

  const renderStats = () => {
    const rgb = hexToRgb(cur);
    const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b);
    try{ swBig.style.setProperty('--c', cur); }catch{}
    try{ swBig.style.background = cur; }catch{}
    try{ colorPop.style.setProperty('--hue', `hsl(${hsv.h} 100% 50%)`); }catch{}
    try{
      const br = (rgb.r*299 + rgb.g*587 + rgb.b*114) / 1000;
      btnDone.style.background = cur;
      btnDone.style.color = (br > 160) ? 'rgba(0,0,0,.82)' : '#fff';
    }catch{}

    const hexEl = colorPop.querySelector('[data-k="hex"]');
    const rgbEl = colorPop.querySelector('[data-k="rgb"]');
    const hslEl = colorPop.querySelector('[data-k="hsl"]');
    if (hexEl) hexEl.textContent = cur.toUpperCase();
    if (rgbEl) rgbEl.textContent = `${rgb.r}, ${rgb.g}, ${rgb.b}`;
    if (hslEl) hslEl.textContent = `${hsl.h}, ${hsl.s}%, ${hsl.l}%`;

    hexField.value = cur.toUpperCase();
    rField.value = String(rgb.r);
    gField.value = String(rgb.g);
    bField.value = String(rgb.b);

    const rect = sv.getBoundingClientRect();
    const x = hsv.s * rect.width;
    const y = (1 - hsv.v) * rect.height;
    svDot.style.left = x + 'px';
    svDot.style.top = y + 'px';

    const hrect = hue.getBoundingClientRect();
    hueThumb.style.top = (hsv.h / 360) * hrect.height + 'px';
  };

  const setFromSVEvent = (ev) => {
    const r = sv.getBoundingClientRect();
    const x = clamp01((ev.clientX - r.left) / r.width);
    const y = clamp01((ev.clientY - r.top) / r.height);
    hsv.s = x;
    hsv.v = 1 - y;
    const rgb = hsvToRgb(hsv.h, hsv.s, hsv.v);
    cur = rgbToHex(rgb.r, rgb.g, rgb.b);
    renderStats();
  };

  const setFromHueEvent = (ev) => {
    const r = hue.getBoundingClientRect();
    const y = clamp01((ev.clientY - r.top) / r.height);
    hsv.h = Math.round(y * 360);
    const rgb = hsvToRgb(hsv.h, hsv.s, hsv.v);
    cur = rgbToHex(rgb.r, rgb.g, rgb.b);
    renderStats();
  };

  const bindDrag = (el, onMove) => {
    const onDown = (e) => {
      e.preventDefault(); e.stopPropagation();
      try{ el.setPointerCapture(e.pointerId); }catch{}
      onMove(e);
      const mm = (ev) => onMove(ev);
      const uu = () => {
        try{ el.releasePointerCapture(e.pointerId); }catch{}
        el.removeEventListener('pointermove', mm);
        el.removeEventListener('pointerup', uu);
        el.removeEventListener('pointercancel', uu);
      };
      el.addEventListener('pointermove', mm);
      el.addEventListener('pointerup', uu);
      el.addEventListener('pointercancel', uu);
    };
    el.addEventListener('pointerdown', onDown);
  };

  bindDrag(sv, setFromSVEvent);
  bindDrag(hue, setFromHueEvent);

  hexField.addEventListener('input', () => {
    const v = String(hexField.value || '').trim();
    if (setCur(v)) renderStats();
  });

  const applyRgb = () => {
    const r = clamp255(rField.value);
    const g = clamp255(gField.value);
    const b = clamp255(bField.value);
    if (r == null || g == null || b == null) return;
    setCur(rgbToHex(r,g,b));
    renderStats();
  };
  rField.addEventListener('input', applyRgb);
  gField.addEventListener('input', applyRgb);
  bField.addEventListener('input', applyRgb);

  btnSavePreset.addEventListener('click', (e) => {
    e.preventDefault(); e.stopPropagation();
    const norm = normalizePinColor(cur);
    if (!norm) return;
    const arr = loadCustomPinColors();

    const already = arr.findIndex(c => normalizePinColor(c) === norm);
    if (already !== -1 && !(Number.isFinite(editIndex) && editIndex != null && already === editIndex)) {
      try { toast("That preset already exists."); } catch {}
      return;
    }
if (Number.isFinite(editIndex) && editIndex != null && editIndex >= 0 && editIndex < arr.length){
      arr[editIndex] = norm;
    } else if (arr.length < 4){
      arr.push(norm);
    } else {
      arr[arr.length - 1] = norm;
    }
    saveCustomPinColors(arr);
    customColors = loadCustomPinColors();
    renderCustomSwatches();
    renderStats();
  });

  btnDelete.addEventListener('click', (e) => {
    e.preventDefault(); e.stopPropagation();
    if (!(Number.isFinite(editIndex) && editIndex != null)) { closePop(); return; }
    const arr = loadCustomPinColors();
    if (editIndex >= 0 && editIndex < arr.length){
      arr.splice(editIndex, 1);
      saveCustomPinColors(arr);
      customColors = loadCustomPinColors();
      renderCustomSwatches();
      renderStats();
    }
    closePop();
  });


  btnDone.addEventListener('click', (e) => {
    e.preventDefault(); e.stopPropagation();

    const isEditing = (Number.isFinite(Number(editIndex)) && editIndex != null);
    if (isEditing) {
      const norm = normalizePinColor(cur);
      if (!norm) return;

      let arr = loadCustomPinColors();
      const idx = Number(editIndex);

      const already = arr.findIndex(c => normalizePinColor(c) === norm);
      if (already !== -1 && already !== idx) {
        try { toast("That preset already exists."); } catch {}
        return;
      }

      if (idx >= 0 && idx < arr.length) {
        arr[idx] = norm;
        saveCustomPinColors(arr);
        customColors = loadCustomPinColors();
        renderCustomSwatches();
      }
      try { setColor(norm); } catch {}
      closePop();
      return;
    }

    try { setColor(cur); } catch {}
    closePop();
  });
  renderStats();

  document.body.appendChild(colorPop);
  positionPop(anchorEl || plusBtn);
  document.addEventListener('pointerdown', onOutside, true);
}


let swatchEditTipEl = null;
let swatchEditTipHideT = null;
let swatchEditAnchor = null;
let swatchEditIndex = null;

function ensureSwatchEditTip(){
  if (swatchEditTipEl) return;
  swatchEditTipEl = document.createElement("div");
  swatchEditTipEl.className = "wmeRcSwatchEditTip";
  swatchEditTipEl.style.display = "none";
  swatchEditTipEl.innerHTML = `
    <button type="button" class="wmeRcSwatchEditTipBtn" data-act="edit" aria-label="Edit color">
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
        <path d="M12 20h9"/>
        <path d="M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4 12.5-12.5z"/>
      </svg>
    </button>
    <button type="button" class="wmeRcSwatchEditTipBtn" data-act="del" aria-label="Remove color">
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
        <path d="M3 6h18"/>
        <path d="M8 6V4h8v2"/>
        <path d="M19 6l-1 14H6L5 6"/>
        <path d="M10 11v6"/>
        <path d="M14 11v6"/>
      </svg>
    </button>
  `;
  swatchEditTipEl.addEventListener("pointerenter", () => {
    if (swatchEditTipHideT){ clearTimeout(swatchEditTipHideT); swatchEditTipHideT = null; }
  });
  swatchEditTipEl.addEventListener("pointerleave", () => hideSwatchEditTip());
  swatchEditTipEl.addEventListener("click", (e) => {
    e.preventDefault(); e.stopPropagation();
    const btn = e.target && e.target.closest ? e.target.closest(".wmeRcSwatchEditTipBtn") : null;
    const act = btn && btn.dataset ? btn.dataset.act : null;
    if (!act) return;

    if (!swatchEditAnchor || !Number.isFinite(swatchEditIndex)) return;
    const arr = loadCustomPinColors();

    if (act === "edit"){
      const c = arr[swatchEditIndex];
      if (!c) return;
      openPop({ initial: c, editIndex: swatchEditIndex, anchorEl: swatchEditAnchor });
      return;
    }

    if (act === "del"){
      if (swatchEditIndex < 0 || swatchEditIndex >= arr.length) return;
      arr.splice(swatchEditIndex, 1);
      saveCustomPinColors(arr);
      customColors = loadCustomPinColors();
      renderCustomSwatches();
      hideSwatchEditTip(true);
    }
  });
  document.body.appendChild(swatchEditTipEl);
}


function showSwatchEditTip(anchorEl, idx){
  ensureSwatchEditTip();
  swatchEditAnchor = anchorEl;
  swatchEditIndex = idx;
  if (swatchEditTipHideT){ clearTimeout(swatchEditTipHideT); swatchEditTipHideT = null; }

  const r = anchorEl.getBoundingClientRect();
  const vw = window.innerWidth || document.documentElement.clientWidth || 9999;

  swatchEditTipEl.style.display = "inline-flex";
  swatchEditTipEl.style.visibility = "hidden";
  swatchEditTipEl.style.left = "0px";
  swatchEditTipEl.style.top = "0px";

  const tipW = Math.max(1, swatchEditTipEl.getBoundingClientRect().width || swatchEditTipEl.offsetWidth || 1);
  let left = r.left + (r.width / 2) - (tipW / 2);
  left = Math.max(8, Math.min(vw - tipW - 8, left));
  const top = r.bottom + 6;

  swatchEditTipEl.style.left = left + "px";
  swatchEditTipEl.style.top = top + "px";
  swatchEditTipEl.style.visibility = "visible";
}


function hideSwatchEditTip(immediate){
  if (!swatchEditTipEl) return;
  const doHide = () => { try{ swatchEditTipEl.style.display = "none"; }catch{} };
  if (immediate) return doHide();
  if (swatchEditTipHideT) clearTimeout(swatchEditTipHideT);
  swatchEditTipHideT = setTimeout(doHide, 220);
}


function renderCustomSwatches(){
  try{
    const oldSep = colorRow.querySelector(".wmeRcColorSep");
    if (oldSep) oldSep.remove();
  }catch{}
  for (let i = swatches.length - 1; i >= 0; i--){
    const s = swatches[i];
    if (s && s.dataset && s.dataset.customIndex != null){
      try { s.remove(); } catch {}
      swatches.splice(i, 1);
    }
  }
  if (customColors && customColors.length){
    const sep = document.createElement("div");
    sep.className = "wmeRcColorSep";
    sep.textContent = "|";
    colorRow.insertBefore(sep, plusBtn);
  }
  for (let i = 0; i < customColors.length; i++){
    const c = customColors[i];
    const sw = document.createElement("div");
    sw.className = "wmeRcColorSwatch custom";
    sw.style.setProperty("--c", c);
    sw.dataset.c = normalizePinColor(c);
    sw.dataset.customIndex = String(i);
    sw.title = c;
    sw.addEventListener("click", (e) => {
      e.preventDefault(); e.stopPropagation();
      setColor(c);
      editCustomIndex = i;
      hideSwatchEditTip(true);
      closePop();
    });
    sw.addEventListener("pointerenter", () => showSwatchEditTip(sw, i));
    sw.addEventListener("pointerleave", () => hideSwatchEditTip());
    colorRow.insertBefore(sw, plusBtn);
    swatches.push(sw);
  }
  for (const s of swatches) s.classList.toggle("sel", s.dataset.c === chosenColor);
}

plusBtn.addEventListener("click", (e) => {
  e.preventDefault(); e.stopPropagation();
  editCustomIndex = null;
  openPop({ initial: chosenColor, editIndex: null, anchorEl: plusBtn });
});

renderCustomSwatches();
setColor(chosenColor);

      let chosenGroupId = normalizeGroupId(pin.groupId);

      const groupLbl = document.createElement("div");
      groupLbl.className = "wmeRcHint";
      groupLbl.textContent = "Folder";

      const groupPick = document.createElement("div");
      groupPick.className = "wmeRcSoundPick";
      groupPick.tabIndex = 0;

      const groupBtn = document.createElement("div");
      groupBtn.className = "wmeRcSoundBtn";

      const groupBtnLabel = document.createElement("div");
      groupBtnLabel.className = "wmeRcSoundBtnLabel";

      const groupCaret = document.createElement("div");
      groupCaret.className = "wmeRcSoundCaret";
      groupCaret.innerHTML = ICONS.chevDown || "▾";

      groupBtn.appendChild(groupBtnLabel);
      groupBtn.appendChild(groupCaret);
      groupPick.appendChild(groupBtn);

      const groupMenu = document.createElement("div");
      groupMenu.className = "wmeRcSoundMenu wmeRcSoundMenuPortal";
      groupMenu.style.display = "none";
      try { document.body.appendChild(groupMenu); } catch {}

      const getGroupLabel = (id) => {
        try {
          const gg = loadPinGroups().find(x => x && x.id === id);
          return gg ? (gg.name || "(no folder)") : "(no folder)";
        } catch { return "(no folder)"; }
      };

      const positionGroupMenu = () => {
        try {
          const r = groupBtn.getBoundingClientRect();
          const vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
          const vh = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0);
          const w = Math.max(260, Math.min(r.width || 320, vw - 16));
          let left = Math.max(8, Math.min(r.left, vw - w - 8));
          let top = (r.bottom || 0) + 6;
          const maxH = 240;
          if (top + maxH + 8 > vh) top = Math.max(8, (r.top || 0) - 6 - maxH);
          groupMenu.style.left = left + "px";
          groupMenu.style.top = top + "px";
          groupMenu.style.width = w + "px";
          const avail = Math.max(120, vh - top - 10);
          groupMenu.style.maxHeight = Math.min(maxH, avail) + "px";
        } catch {}
      };

      const toggleGroupMenu = (open) => {
        const isOpen = groupPick.getAttribute("data-open") === "1";
        const next = (typeof open === "boolean") ? open : !isOpen;
        groupPick.setAttribute("data-open", next ? "1" : "0");
        try {
          if (next) {
            positionGroupMenu();
            groupMenu.style.display = "block";
          } else {
            groupMenu.style.display = "none";
          }
        } catch {}
      };

      const rebuildGroupMenu = () => {
        groupMenu.innerHTML = "";
        const gs = loadPinGroups();
        for (const g of gs) {
          const item = document.createElement("div");
          item.className = "wmeRcSoundItem";
          item.textContent = g.name;
          item.dataset.groupId = g.id;
          item.addEventListener("click", (e) => {
            e.preventDefault(); e.stopPropagation();
            chosenGroupId = normalizeGroupId(g.id);
            groupBtnLabel.textContent = getGroupLabel(chosenGroupId);
            toggleGroupMenu(false);
          });
          groupMenu.appendChild(item);
        }

        const newItem = document.createElement("div");
        newItem.className = "wmeRcSoundItem";
        newItem.textContent = "+ New Folder";
        newItem.dataset.groupId = "__new__";
        newItem.addEventListener("click", (e) => {
          e.preventDefault(); e.stopPropagation();
          toggleGroupMenu(false);
          openGroupNameModal({
            title: "New folder",
            placeholder: "Name",
            okText: "Create",
            onCancel: () => {},
            onSubmit: (nm, emoji) => {
              const id = createPinGroup(nm, emoji);
              chosenGroupId = id;
              groupBtnLabel.textContent = getGroupLabel(chosenGroupId);
              rebuildGroupMenu();
            }
          });
        });
        groupMenu.appendChild(newItem);
      };

      groupBtn.addEventListener("click", (e) => {
        e.preventDefault(); e.stopPropagation();
        toggleGroupMenu();
      });

      const onDocDown = (e) => {
        try {
          if (!groupPick.contains(e.target) && !groupMenu.contains(e.target)) {
            groupPick.setAttribute("data-open", "0");
            groupMenu.style.display = "none";
          }
        } catch {}
      };
      document.addEventListener("pointerdown", onDocDown, true);
      try { window.addEventListener("resize", positionGroupMenu, true); } catch {}
      try { window.addEventListener("scroll", positionGroupMenu, true); } catch {}

      const cleanupGroupPick = () => {
        try { document.removeEventListener("pointerdown", onDocDown, true); } catch {}
        try { window.removeEventListener("resize", positionGroupMenu, true); } catch {}
        try { window.removeEventListener("scroll", positionGroupMenu, true); } catch {}
        try { groupMenu.remove(); } catch {}
      };

      try {
        const mo = new MutationObserver(() => {
          try {
            if (!document.body.contains(modal)) {
              cleanupGroupPick();
              mo.disconnect();
            }
          } catch {}
        });
        mo.observe(document.body, { childList: true });
      } catch {}

      rebuildGroupMenu();
      groupBtnLabel.textContent = getGroupLabel(chosenGroupId);

      setColor(chosenColor);


const visWrap = document.createElement("div");
visWrap.className = "wmeRcHint";
visWrap.innerHTML = `
  <label class="wmeRcInlineToggle">
    <input type="checkbox" class="wmeRcVisChk">
    <span class="wmeRcSwitchTrack"><span class="wmeRcSwitchThumb"></span></span>
    <span class="wmeRcSwitchLabel">Show on map</span>
  </label>
`;
const visChk = visWrap.querySelector("input");
visChk.checked = !(pin.hideOnMap === true);

      const actions = document.createElement("div");
      actions.className = "wmeRcModalActions";

      const btnCancel = document.createElement("div");
      btnCancel.className = "wmeRcModalBtn";
      btnCancel.textContent = "Cancel";
      btnCancel.addEventListener("click", () => { try { if (typeof cleanupSoundPick === "function") cleanupSoundPick(); } catch {} close(); });

      const btnSave = document.createElement("div");
      btnSave.className = "wmeRcModalBtn primary";
      btnSave.textContent = "Save";
      btnSave.addEventListener("click", () => {
        const v = validatePinName(inp.value, "Pinned place");
        if (!v.ok) { toast(v.msg); try { inp.focus(); inp.select(); } catch {} return; }
        const name = v.value;
        updatePin(pinId, { name, color: chosenColor, groupId: chosenGroupId, hideOnMap: !((visChk && visChk.checked)) });
        try { setLastGroup(chosenGroupId); } catch {}
        toast("Pin renamed");
        close();
      });

      actions.appendChild(btnCancel);
      actions.appendChild(btnSave);

      body.appendChild(pinNameLimitMsg ? pinNameLimitMsg.wrap : inp);
      if (pinNameLimitMsg && pinNameLimitMsg.msg) body.appendChild(pinNameLimitMsg.msg);
      body.appendChild(colorLbl);
      body.appendChild(colorRow);
      body.appendChild(groupLbl);
      body.appendChild(groupPick);
      body.appendChild(visWrap);
      body.appendChild(actions);
      setTimeout(() => { try { inp.focus(); inp.select(); } catch {} }, 50);
    },
  });
}



function getExternalNotifyCfg() {
  const key = `${SCRIPT_ID}:pinsExternalNotify:v1`;
  try {
    const raw = localStorage.getItem(key);
    const obj = JSON.parse(raw || "{}") || {};
    return {
      enabled: obj.enabled === true,
      url: typeof obj.url === "string" ? obj.url.trim() : "",
    };
  } catch {
    return { enabled: false, url: "" };
  }
}

function setExternalNotifyCfg(cfg) {
  const key = `${SCRIPT_ID}:pinsExternalNotify:v1`;
  try { localStorage.setItem(key, JSON.stringify(cfg || {})); } catch {}
}

const REMINDER_SOUND_OPTIONS = [
  { id: "mute", label: "(no sound)" },
  { id: "bell", label: "Classic bell" },
  { id: "softBell", label: "Soft bell" },
  { id: "church", label: "Church bell" },
  { id: "chime", label: "Chime" },
  { id: "doubleDing", label: "Double ding" },
  { id: "digital", label: "Digital beep" },
  { id: "retro", label: "Retro beep" },
  { id: "alarm", label: "Alarm" },
  { id: "alarmFast", label: "Alarm fast" },
  { id: "alarmPulse", label: "Alarm pulse" },
  { id: "buzzer", label: "Buzzer" },
  { id: "radar", label: "Radar ping" },
  { id: "siren", label: "Soft siren" },
  { id: "gong", label: "Gong" },
  { id: "glass", label: "Glass ping" },
  { id: "wood", label: "Wood knock" },
];

const REMINDER_NOTICE_POSITION_OPTIONS = [
  { id: "right", label: "Right" },
  { id: "left", label: "Left" },
];

function getReminderNoticePosition() {
  const key = `${SCRIPT_ID}:pinsNoticePos:v1`;
  try {
    const v = String(localStorage.getItem(key) || "").trim().toLowerCase();
    if (REMINDER_NOTICE_POSITION_OPTIONS.some(o => o.id === v)) return v;
  } catch {}
  return "right";
}

function setReminderNoticePosition(pos) {
  const key = `${SCRIPT_ID}:pinsNoticePos:v1`;
  const v = REMINDER_NOTICE_POSITION_OPTIONS.some(o => o.id === String(pos).toLowerCase())
    ? String(pos).toLowerCase()
    : "right";
  try { localStorage.setItem(key, v); } catch {}
  try { applyReminderNoticePosition(); } catch {}
}

function getLeftSidebarInsetPx() {
  try {
    const selectors = [
      "#side-panel", "#sidepanel", "#sidePanel", "#sidebar",
      ".side-panel", ".sidepanel", ".sidebar",
      ".wz-sidebar", ".wz-side-panel", ".wz-sidepanel",
      ".wme-sidebar", ".wme-sidepanel", ".wme-side-panel",
      "aside"
    ];
    let inset = 0;
    for (const sel of selectors) {
      const els = document.querySelectorAll(sel);
      for (const el of els) {
        if (!el || !(el instanceof Element)) continue;
        const cs = window.getComputedStyle(el);
        if (!cs || cs.display === "none" || cs.visibility === "hidden" || Number(cs.opacity || "1") < 0.05) continue;
        const r = el.getBoundingClientRect();
        if (!r || r.width < 120 || r.height < 220) continue;
        if (r.left > 80) continue;
        inset = Math.max(inset, r.right || 0);
      }
    }
    inset = Math.max(0, inset);
    if (inset > 0) inset = Math.min(inset, Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0) - 80);
    return inset;
  } catch {
    return 0;
  }
}

function applyReminderNoticePosition() {
  try {
    const stack = document.getElementById("wmeRcNoticeStack");
    if (!stack) return;
    const pos = getReminderNoticePosition();
    const vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
    const inset = getLeftSidebarInsetPx();
    stack.style.bottom = "118px";
    stack.style.transform = "";
    if (pos === "left") {
      stack.style.left = Math.round(inset + 12) + "px";
      stack.style.right = "";
      stack.style.alignItems = "flex-start";
    } else if (pos === "middle") {
      const desired = Math.round((vw - 440) / 2);
      const left = Math.max(Math.round(inset + 12), isFinite(desired) ? desired : Math.round(vw * 0.5 - 220));
      stack.style.left = left + "px";
      stack.style.right = "";
      stack.style.alignItems = "flex-start";
    } else {
      stack.style.right = "96px";
      stack.style.left = "";
      stack.style.alignItems = "flex-end";
    }
  } catch {}
}

try {
  window.addEventListener("resize", () => { try { applyReminderNoticePosition(); } catch {} });
} catch {}
function getReminderSoundId() {
  const key = `${SCRIPT_ID}:pinsReminderSound:v1`;
  try {
    const v = String(localStorage.getItem(key) || "").trim();
    if (REMINDER_SOUND_OPTIONS.some(o => o.id === v)) return v;
  } catch {}
  return "bell";
}

function setReminderSoundId(id) {
  const key = `${SCRIPT_ID}:pinsReminderSound:v1`;
  const v = REMINDER_SOUND_OPTIONS.some(o => o.id === id) ? id : "bell";
  try { localStorage.setItem(key, v); } catch {}
}


async function sendExternalReminderWebhook(pin) {
  try {
    const cfg = getExternalNotifyCfg();
    if (!cfg || !cfg.enabled || !cfg.url) return;
    const note = (pin && typeof pin.reminderNote === "string") ? pin.reminderNote.trim() : "";
    const payload = {
      type: "wme_pin_reminder",
      title: "WME Pin Reminder",
      message: note ? `Reminder: ${pin?.name || "Pin"} — ${note}` : `Reminder: ${pin?.name || "Pin"}`,
      pin: {
        id: String(pin?.id || ""),
        name: String(pin?.name || ""),
        lat: Number(pin?.lat),
        lon: Number(pin?.lon),
        groupId: String(pin?.groupId || "default"),
        when: Number(pin?.reminderAt) || Date.now(),
      },
      ts: Date.now(),
    };
    await fetch(cfg.url, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(payload),
      mode: "cors",
      credentials: "omit",
    }).catch(() => {});
  } catch {}
}

function openPinsSettingsModal() {
  openModal({
    title: "Pin settings",
    iconSvg: ICONS.gear,
    bodyBuilder: ({ body, close, modal }) => {
      const visWrap = document.createElement("div");
      visWrap.className = "wmeRcHint";
      visWrap.innerHTML = `
        <label class="wmeRcInlineToggle">
          <input type="checkbox" class="wmeRcVisAllChk">
          <span class="wmeRcSwitchTrack"><span class="wmeRcSwitchThumb"></span></span>
          <span class="wmeRcSwitchLabel">Pins on map</span>
        </label>
      `;
      const visChk = visWrap.querySelector("input");
      visChk.checked = !!getPinsLayerVisible();
      visChk.addEventListener("change", () => {
        setPinsLayerVisible(!!visChk.checked);
        try { if (namesChk) namesChk.disabled = !getPinsLayerVisible(); } catch {}
        try {
          const zi = modal?.querySelector?.(".wmeRcPinZoomPick");
          if (zi) {
            const dis = !getPinsLayerVisible() || !getPinsShowNamesOnMap();
            try { zi.style.opacity = dis ? ".55" : "1"; } catch {}
            try { zi.style.pointerEvents = dis ? "none" : "auto"; } catch {}
            try { zi.tabIndex = dis ? -1 : 0; } catch {}
          }
        } catch {}
        try { renderPinsMarkers(); } catch {}
      });

      const namesWrap = document.createElement("div");
      namesWrap.className = "wmeRcHint";
      namesWrap.innerHTML = `
        <div style="display:flex;align-items:center;justify-content:space-between;gap:12px;">
          <label class="wmeRcInlineToggle" style="margin:0;">
            <input type="checkbox" class="wmeRcPinNamesChk">
            <span class="wmeRcSwitchTrack"><span class="wmeRcSwitchThumb"></span></span>
            <span class="wmeRcSwitchLabel">Show pin names on map</span>
          </label>

          <div style="display:flex;align-items:center;justify-content:flex-end;gap:10px;min-width:220px;">
            <span style="opacity:.85;white-space:nowrap;">Zoom visibility</span>
            <div class="wmeRcSoundPick wmeRcPinZoomPick" tabindex="0" style="width:120px;max-width:120px;">
              <div class="wmeRcSoundBtn">
                <div class="wmeRcSoundBtnLabel wmeRcPinZoomLbl"></div>
                <div class="wmeRcSoundCaret wmeRcPinZoomCaret"></div>
              </div>
            </div>
          </div>
        </div>
      `;

      const namesChk = namesWrap.querySelector(".wmeRcPinNamesChk");
      const zoomPick = namesWrap.querySelector(".wmeRcPinZoomPick");
      const zoomBtn = namesWrap.querySelector(".wmeRcPinZoomPick .wmeRcSoundBtn");
      const zoomLbl = namesWrap.querySelector(".wmeRcPinZoomLbl");
      const zoomCaret = namesWrap.querySelector(".wmeRcPinZoomCaret");
      try { zoomCaret.innerHTML = ICONS.chevDown; } catch { try { zoomCaret.textContent = "▾"; } catch {} }

      const clampZoom = (n) => Math.max(4, Math.min(22, Number(n) || 9));

      let zoomVal = clampZoom(getPinsNamesMinZoom());
      const applyZoomLabel = () => { try { zoomLbl.textContent = String(zoomVal); } catch {} };

      const isPinsLayerOn = () => !!getPinsLayerVisible();
      const updateZoomDisabled = () => {
        const dis = !isPinsLayerOn() || !namesChk.checked;
        try { zoomPick.style.opacity = dis ? ".55" : "1"; } catch {}
        try { zoomPick.style.pointerEvents = dis ? "none" : "auto"; } catch {}
        try { zoomPick.tabIndex = dis ? -1 : 0; } catch {}
      };

      try { namesChk.checked = !!getPinsShowNamesOnMap(); } catch { namesChk.checked = false; }
      try { namesChk.disabled = !isPinsLayerOn(); } catch {}

      applyZoomLabel();
      updateZoomDisabled();

      const zoomMenu = document.createElement("div");
      zoomMenu.className = "wmeRcSoundMenu wmeRcSoundMenuPortal";
      zoomMenu.style.display = "none";
      try { document.body.appendChild(zoomMenu); } catch {}

      const positionZoomMenu = () => {
        try {
          const r = zoomBtn.getBoundingClientRect();
          const vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
          const vh = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0);
          const w = Math.max(120, Math.min((r.width || 120), 220, vw - 16));
          let left = Math.max(8, Math.min(r.left, vw - w - 8));
          let top = (r.bottom || 0) + 6;

          const maxH = 240;
          if (top + maxH + 8 > vh) top = Math.max(8, (r.top || 0) - 6 - maxH);

          zoomMenu.style.left = left + "px";
          zoomMenu.style.top = top + "px";
          zoomMenu.style.width = w + "px";
          const avail = Math.max(120, vh - top - 10);
          zoomMenu.style.maxHeight = Math.min(maxH, avail) + "px";
          zoomMenu.style.overflow = "auto";
        } catch {}
      };

      const setZoomValue = (n, save = true) => {
        zoomVal = clampZoom(n);
        applyZoomLabel();
        if (save) { try { setPinsNamesMinZoom(zoomVal); } catch {} }
        try { zoomMenu.style.display = "none"; } catch {}
        try { zoomPick.setAttribute("data-open", "0"); } catch {}
      };

      for (let z = 4; z <= 22; z++) {
        const item = document.createElement("div");
        item.className = "wmeRcSoundItem";
        item.textContent = String(z);
        item.dataset.zoom = String(z);
        item.addEventListener("click", (e) => {
          e.preventDefault(); e.stopPropagation();
          setZoomValue(z, true);
        });
        zoomMenu.appendChild(item);
      }

      setZoomValue(zoomVal, false);

      const toggleZoomMenu = (open) => {
        const dis = !isPinsLayerOn() || !namesChk.checked;
        if (dis) return;
        const isOpen = zoomPick.getAttribute("data-open") === "1";
        const next = (typeof open === "boolean") ? open : !isOpen;
        zoomPick.setAttribute("data-open", next ? "1" : "0");
        try {
          if (next) {
            positionZoomMenu();
            zoomMenu.style.display = "block";
          } else {
            zoomMenu.style.display = "none";
          }
        } catch {}
      };

      zoomBtn.addEventListener("click", (e) => {
        e.preventDefault(); e.stopPropagation();
        toggleZoomMenu();
      });

      const onZoomDocDown = (e) => {
        try {
          if (!zoomPick.contains(e.target) && !zoomMenu.contains(e.target)) {
            zoomPick.setAttribute("data-open", "0");
            zoomMenu.style.display = "none";
          }
        } catch {}
      };
      document.addEventListener("pointerdown", onZoomDocDown, true);
      try { pop._wmeRcZoomDown = onZoomDocDown; } catch {}
      try { window.addEventListener("resize", positionZoomMenu, true); } catch {}
      try { window.addEventListener("scroll", positionZoomMenu, true); } catch {}

      const cleanupZoomPick = () => {
        try { document.removeEventListener("pointerdown", onZoomDocDown, true); } catch {}
        try { window.removeEventListener("resize", positionZoomMenu, true); } catch {}
        try { window.removeEventListener("scroll", positionZoomMenu, true); } catch {}
        try { zoomMenu.remove(); } catch {}
      };
      try { pop._wmeRcCleanupZoomPick = cleanupZoomPick; } catch {}

      try {
        const mo = new MutationObserver(() => {
          try {
            if (!document.body.contains(modal)) {
              cleanupZoomPick();
              mo.disconnect();
            }
          } catch {}
        });
        mo.observe(document.body, { childList: true, subtree: true });
      } catch {}


      namesChk.addEventListener("change", () => {
        try { setPinsShowNamesOnMap(!!namesChk.checked); } catch {}
        updateZoomDisabled();
        try { refreshPinsMarkers(true); } catch {}
      });

      body.appendChild(namesWrap);

      const emptyWrap = document.createElement("div");
      emptyWrap.className = "wmeRcHint";
      emptyWrap.innerHTML = `
        <label class="wmeRcInlineToggle">
          <input type="checkbox" class="wmeRcAlwaysVisChk">
          <span class="wmeRcSwitchTrack"><span class="wmeRcSwitchThumb"></span></span>
          <span class="wmeRcSwitchLabelWrap"><span class="wmeRcSwitchLabel">Show panel when no pins</span><span class="wmeRcInfoQ" tabindex="0">?<span class="wmeRcInfoTip">When "disabled", the panel is not shown unless you have a place pinned.</span></span></span>
        </label>
      `;
      const emptyChk = emptyWrap.querySelector("input");
      emptyChk.checked = !!getPinsPanelAlwaysVisibleEmpty();

      const defTitle = document.createElement("div");
      defTitle.className = "wmeRcHint";
      defTitle.textContent = "Default folder name";

      const defRow = document.createElement("div");
      defRow.className = "wmeRcRow";
      try { defRow.style.display = "flex"; defRow.style.gap = "10px"; defRow.style.alignItems = "center"; } catch {}

      const defInp = document.createElement("input");
      defInp.className = "wmeRcInput";
      defInp.type = "text";
      defInp.placeholder = "(no folder)";
      defInp.maxLength = 32;
      defInp.value = String(getGroupName("default") || "(no folder)");
      defInp.addEventListener("keydown", (ev) => {
        if (ev.key === "Enter") { try { ev.preventDefault(); } catch {} }
      });
      defRow.appendChild(defInp);

      const soundTitle = document.createElement("div");
      soundTitle.className = "wmeRcHint";
      soundTitle.textContent = "Reminder sound";

      const posTitle = document.createElement("div");
      posTitle.className = "wmeRcHint";
      posTitle.textContent = "Notification Position";

      const posRow = document.createElement("div");
      posRow.className = "wmeRcRow";
      try { posRow.style.display = "flex"; posRow.style.gap = "10px"; posRow.style.alignItems = "center"; } catch {}

const posPick = document.createElement("div");
posPick.className = "wmeRcSoundPick";
posPick.tabIndex = 0;

const posBtn = document.createElement("div");
posBtn.className = "wmeRcSoundBtn";

const posBtnLabel = document.createElement("div");
posBtnLabel.className = "wmeRcSoundBtnLabel";

const posCaret = document.createElement("div");
posCaret.className = "wmeRcSoundCaret";
posCaret.innerHTML = ICONS.chevDown;

posBtn.appendChild(posBtnLabel);
posBtn.appendChild(posCaret);
posPick.appendChild(posBtn);

const posMenu = document.createElement("div");
posMenu.className = "wmeRcSoundMenu";
posPick.appendChild(posMenu);

let selectedPosId = getReminderNoticePosition();
const getPosLabelFor = (id) => {
  const o = REMINDER_NOTICE_POSITION_OPTIONS.find(x => x.id === id);
  return o ? o.label : "Right";
};
const setSelectedPos = (id) => {
  selectedPosId = REMINDER_NOTICE_POSITION_OPTIONS.some(o => o.id === id) ? id : "right";
  posBtnLabel.textContent = getPosLabelFor(selectedPosId);
  try { setReminderNoticePosition(selectedPosId); } catch {}
  try { posPick.setAttribute("data-open", "0"); } catch {}
  try { if (posMenu && posMenu.parentNode === document.body) { posMenu.style.display = "none"; posMenu.remove(); } } catch {}
};

for (const opt of REMINDER_NOTICE_POSITION_OPTIONS) {
  const item = document.createElement("div");
  item.className = "wmeRcSoundItem";
  item.textContent = opt.label;
  item.dataset.posId = opt.id;
  item.addEventListener("click", (e) => {
    e.preventDefault(); e.stopPropagation();
    setSelectedPos(opt.id);
  });
  posMenu.appendChild(item);
}

setSelectedPos(selectedPosId);

const closePosMenu = () => {
  try { posPick.setAttribute("data-open", "0"); } catch {}
  try {
    if (posMenu && posMenu.parentNode === document.body) { posMenu.style.display = "none"; posMenu.remove(); }
    else if (posMenu) { posMenu.style.display = "none"; }
  } catch {}
  posMenuOpen = false;
};

let posMenuOpen = false;
const openPosMenu = () => {
  try {
    if (!posMenu) return;
    if (posMenu.parentNode !== document.body) {
      try { posMenu.remove(); } catch {}
      document.body.appendChild(posMenu);
    }
    const r = posBtn.getBoundingClientRect();
    const top = Math.round(r.bottom + 6);
    const left = Math.round(r.left);
    const width = Math.round(r.width);
    posMenu.style.position = "fixed";
    posMenu.style.left = left + "px";
    posMenu.style.top = top + "px";
    posMenu.style.width = width + "px";
    posMenu.style.right = "auto";
    posMenu.style.zIndex = "2147483647";
    posMenu.style.display = "block";
    const maxH = Math.max(120, window.innerHeight - top - 12);
    posMenu.style.maxHeight = Math.min(240, maxH) + "px";
    try { posMenu.style.overflow = "hidden"; } catch {}
    try { posPick.setAttribute("data-open", "1"); } catch {}
    posMenuOpen = true;
  } catch {}
};

const togglePosMenu = (open) => {
  const next = (typeof open === "boolean") ? open : !posMenuOpen;
  if (next) openPosMenu();
  else closePosMenu();
};

posBtn.addEventListener("click", (e) => {
  e.preventDefault(); e.stopPropagation();
  togglePosMenu();
});

const onPosDocDown = (e) => {
  try {
    if (!posMenuOpen) return;
    if (posPick.contains(e.target)) return;
    if (posMenu && posMenu.contains(e.target)) return;
    closePosMenu();
  } catch {}
};
document.addEventListener("pointerdown", onPosDocDown, true);

try {
  const reposition = () => { if (posMenuOpen) openPosMenu(); };
  window.addEventListener("resize", reposition, { passive: true });
  window.addEventListener("scroll", reposition, true);
} catch {}

try {
  const mo = new MutationObserver(() => {
    try {
      if (!document.body.contains(modal)) {
        try { document.removeEventListener("pointerdown", onPosDocDown, true); } catch {}
        try { closePosMenu(); } catch {}
        mo.disconnect();
      }
    } catch {}
  });
  mo.observe(document.body, { childList: true });
} catch {}
posRow.appendChild(posPick);


      const soundRow = document.createElement("div");
      soundRow.className = "wmeRcRow";
      try { soundRow.style.display = "flex"; soundRow.style.gap = "10px"; soundRow.style.alignItems = "center"; } catch {}

      const soundPick = document.createElement("div");
      soundPick.className = "wmeRcSoundPick";
      soundPick.tabIndex = 0;

      const soundBtn = document.createElement("div");
      soundBtn.className = "wmeRcSoundBtn";

      const soundBtnLabel = document.createElement("div");
      soundBtnLabel.className = "wmeRcSoundBtnLabel";

      const soundCaret = document.createElement("div");
      soundCaret.className = "wmeRcSoundCaret";
      soundCaret.innerHTML = ICONS.chevDown;

      soundBtn.appendChild(soundBtnLabel);
      soundBtn.appendChild(soundCaret);
      soundPick.appendChild(soundBtn);

      const soundMenu = document.createElement("div");
      soundMenu.className = "wmeRcSoundMenu wmeRcSoundMenuPortal";
      soundMenu.style.display = "none";
      try { document.body.appendChild(soundMenu); } catch {}

      const positionSoundMenu = () => {
        try {
          const r = soundBtn.getBoundingClientRect();
          const vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
          const vh = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0);
          const w = Math.max(220, Math.min(r.width || 260, vw - 16));
          let left = Math.max(8, Math.min(r.left, vw - w - 8));
          let top = (r.bottom || 0) + 6;

          const maxH = 240;
          if (top + maxH + 8 > vh) {
            top = Math.max(8, (r.top || 0) - 6 - maxH);
          }

          soundMenu.style.left = left + "px";
          soundMenu.style.top = top + "px";
          soundMenu.style.width = w + "px";
          const avail = Math.max(120, vh - top - 10);
          soundMenu.style.maxHeight = Math.min(maxH, avail) + "px";
        } catch {}
      };

      let selectedSoundId = getReminderSoundId();
      const getLabelFor = (id) => {
        const o = REMINDER_SOUND_OPTIONS.find(x => x.id === id);
        return o ? o.label : "Classic bell";
      };
      const setSelectedSound = (id, play = false) => {
        selectedSoundId = REMINDER_SOUND_OPTIONS.some(o => o.id === id) ? id : "bell";
        soundBtnLabel.textContent = getLabelFor(selectedSoundId);
        try { soundPick.setAttribute("data-open", "0"); } catch {}
        try { soundMenu.style.display = "none"; } catch {}
        if (play && selectedSoundId !== "mute") { try { playReminderSoundOnce(selectedSoundId, { force: true }); } catch {} }
      };

      for (const opt of REMINDER_SOUND_OPTIONS) {
        const item = document.createElement("div");
        item.className = "wmeRcSoundItem";
        item.textContent = opt.label;
        item.dataset.soundId = opt.id;
        item.addEventListener("click", (e) => {
          e.preventDefault(); e.stopPropagation();
          setSelectedSound(opt.id, true);
        });
        soundMenu.appendChild(item);
      }

      setSelectedSound(selectedSoundId, false);

      const toggleMenu = (open) => {
        const isOpen = soundPick.getAttribute("data-open") === "1";
        const next = (typeof open === "boolean") ? open : !isOpen;
        soundPick.setAttribute("data-open", next ? "1" : "0");
        try {
          if (next) {
            positionSoundMenu();
            soundMenu.style.display = "block";
          } else {
            soundMenu.style.display = "none";
          }
        } catch {}
      };

      soundBtn.addEventListener("click", (e) => {
        e.preventDefault(); e.stopPropagation();
        toggleMenu();
      });

      const onDocDown = (e) => {
        try {
          if (!soundPick.contains(e.target) && !soundMenu.contains(e.target)) { soundPick.setAttribute("data-open", "0"); try { soundMenu.style.display = "none"; } catch {} }
        } catch {}
      };
      document.addEventListener("pointerdown", onDocDown, true);
      try { pop._wmeRcDown = onDocDown; } catch {}
      try { window.addEventListener("resize", positionSoundMenu, true); } catch {}
      try { window.addEventListener("scroll", positionSoundMenu, true); } catch {}

      const cleanupSoundPick = () => {
        try { document.removeEventListener("pointerdown", onDocDown, true); } catch {}
        try { window.removeEventListener("resize", positionSoundMenu, true); } catch {}
        try { window.removeEventListener("scroll", positionSoundMenu, true); } catch {}
        try { soundMenu.remove(); } catch {}
      };

      const soundPrev = document.createElement("div");
      soundPrev.className = "wmeRcMiniBtn";
      soundPrev.textContent = "▶";
      soundPrev.title = "Preview";
      soundPrev.addEventListener("click", (e) => {
        e.preventDefault(); e.stopPropagation();
        if (selectedSoundId === "mute") return;
        try { playReminderSoundOnce(selectedSoundId, { force: true }); } catch {}
      });

      soundRow.appendChild(soundPick);
      soundRow.appendChild(soundPrev);
const actions = document.createElement("div");
      actions.className = "wmeRcModalActions";

      const btnCancel = document.createElement("div");
      btnCancel.className = "wmeRcModalBtn";
      btnCancel.textContent = "Close";
      btnCancel.addEventListener("click", () => { try { if (typeof cleanupSoundPick === "function") cleanupSoundPick(); } catch {} close(); });

      const btnSave = document.createElement("div");
      btnSave.className = "wmeRcModalBtn primary";
      btnSave.textContent = "Save";
      btnSave.addEventListener("click", () => {
        setReminderSoundId(String(selectedSoundId || "bell"));
        try { setPinsPanelAlwaysVisibleEmpty(!!(emptyChk && emptyChk.checked)); } catch {}
        try { setPinsMinimizeMode(String(pendingMinMode || "bubble")); } catch {}
        try { setDefaultGroupName(defInp ? defInp.value : "(no folder)"); } catch {}
        toast("Settings saved");
        try { renderPinsPanel(); } catch {}
        try { if (typeof cleanupSoundPick === "function") cleanupSoundPick(); } catch {}
        close();
      });

      actions.appendChild(btnCancel);
      actions.appendChild(btnSave);

      body.appendChild(visWrap);
      body.appendChild(namesWrap);
      body.appendChild(emptyWrap);




      const minModeTitle = document.createElement("div");
      minModeTitle.className = "wmeRcHint";
      minModeTitle.textContent = "Minimize as";

      let pendingMinMode = "bubble";
      try { pendingMinMode = getPinsMinimizeMode(); } catch { pendingMinMode = "bubble"; }

      const minModeRow = document.createElement("div");
      minModeRow.className = "wmeRcRow";
      try { minModeRow.style.display = "flex"; minModeRow.style.gap = "10px"; minModeRow.style.alignItems = "center"; } catch {}

      const minModePick = document.createElement("div");
      minModePick.className = "wmeRcSoundPick";
      minModePick.tabIndex = 0;

      const minModeBtn = document.createElement("div");
      minModeBtn.className = "wmeRcSoundBtn";

      const minModeBtnLabel = document.createElement("div");
      minModeBtnLabel.className = "wmeRcSoundBtnLabel";

      const minModeCaret = document.createElement("div");
      minModeCaret.className = "wmeRcSoundCaret";
      try { minModeCaret.innerHTML = ICONS.chevDown; } catch { try { minModeCaret.textContent = "▾"; } catch {} }

      minModeBtn.appendChild(minModeBtnLabel);
      minModeBtn.appendChild(minModeCaret);
      minModePick.appendChild(minModeBtn);

      const minModeMenu = document.createElement("div");
      minModeMenu.className = "wmeRcSoundMenu wmeRcSoundMenuPortal";
      minModeMenu.style.display = "none";
      try { document.body.appendChild(minModeMenu); } catch {}

      const setMinModeLabel = () => {
        try { minModeBtnLabel.textContent = (pendingMinMode === "panel") ? "Panel" : "Bubble"; } catch {}
      };
      setMinModeLabel();

      const closeMinModeMenu = () => {
        try { minModePick.setAttribute("data-open", "0"); } catch {}
        try { minModeMenu.style.display = "none"; } catch {}
        minModeMenuOpen = false;
      };

      let minModeMenuOpen = false;
      const openMinModeMenu = () => {
        try {
          const r = minModeBtn.getBoundingClientRect();
          const top = Math.round(r.bottom + 6);
          const left = Math.round(r.left);
          const width = Math.round(r.width);

          minModeMenu.style.position = "fixed";
          minModeMenu.style.left = left + "px";
          minModeMenu.style.top = top + "px";
          minModeMenu.style.width = width + "px";
          minModeMenu.style.right = "auto";
          minModeMenu.style.zIndex = "2147483647";
          minModeMenu.style.display = "block";
          const maxH = Math.max(120, window.innerHeight - top - 12);
          minModeMenu.style.maxHeight = Math.min(240, maxH) + "px";
          try { minModePick.setAttribute("data-open", "1"); } catch {}
          minModeMenuOpen = true;
        } catch {}
      };

      const toggleMinModeMenu = (open) => {
        const next = (typeof open === "boolean") ? open : !minModeMenuOpen;
        if (next) openMinModeMenu();
        else closeMinModeMenu();
      };

      const buildMinModeMenu = () => {
        minModeMenu.innerHTML = "";
        const makeItem = (value, label) => {
          const it = document.createElement("div");
          it.className = "wmeRcSoundItem";
          it.textContent = label;
          it.addEventListener("click", (e) => {
            e.preventDefault(); e.stopPropagation();
            pendingMinMode = (value === "panel") ? "panel" : "bubble";
            setMinModeLabel();
            buildMinModeMenu();
            closeMinModeMenu();
          });
          return it;
        };
        minModeMenu.appendChild(makeItem("bubble", "Bubble"));
        minModeMenu.appendChild(makeItem("panel", "Panel"));
      };
      buildMinModeMenu();

      minModeBtn.addEventListener("click", (e) => {
        e.preventDefault(); e.stopPropagation();
        toggleMinModeMenu();
      });

      const onDocDownMinMode = (e) => {
        try {
          if (!minModeMenuOpen) return;
          if (minModePick.contains(e.target)) return;
          if (minModeMenu && minModeMenu.contains(e.target)) return;
          closeMinModeMenu();
        } catch {}
      };
      document.addEventListener("pointerdown", onDocDownMinMode, true);

      const repositionMinModeMenu = () => { if (minModeMenuOpen) openMinModeMenu(); };
      try { window.addEventListener("resize", repositionMinModeMenu, { passive: true }); } catch {}
      try { window.addEventListener("scroll", repositionMinModeMenu, true); } catch {}

      body.appendChild(minModeTitle);
      minModeRow.appendChild(minModePick);
      body.appendChild(minModeRow);
      body.appendChild(defTitle);
      body.appendChild(defRow);
      body.appendChild(soundTitle);
      body.appendChild(soundRow);
      body.appendChild(posTitle);
      body.appendChild(posRow);
      body.appendChild(actions);
    },
  });
}

function openPinsManagerLightbox() {
  try { ensureCSS(); } catch {}
  openModal({
    title: "Pins manager",
    iconSvg: ICONS.properties || ICONS.tools,
    bodyBuilder: ({ body, close, modal }) => {
      try { modal.classList.add("wmeRcLightbox"); } catch {}
      const groups = () => loadPinGroups();
      const pins = () => loadPins();

      let selGid = normalizeGroupId(getSelectedPinsGroupId() || "default");
      let qFolders = "";
      let qPins = "";

      const root = document.createElement("div");
      root.style.width = "100%";
      root.style.height = "100%";
      root.style.display = "flex";
      root.style.flexDirection = "row";
      root.style.gap = "0";
      root.style.minHeight = "0";

      const left = document.createElement("div");
      left.className = "wmeRcPmLeft";

      const right = document.createElement("div");
      right.className = "wmeRcPmRight";


      const leftHdr = document.createElement("div");
      leftHdr.className = "wmeRcPmHdrRow";

      const leftTitle = document.createElement("div");
      leftTitle.style.fontWeight = "900";
      leftTitle.style.letterSpacing = ".2px";
      leftTitle.textContent = "Folders";

      const addFolderBtn = document.createElement("div");
      addFolderBtn.className = "wmeRcPmTiny";
      addFolderBtn.title = "Add folder";
      addFolderBtn.innerHTML = `<span class="wmeRcI">${ICONS.folderPlus}</span>`;
      addFolderBtn.addEventListener("click", (ev) => {
        try { ev.preventDefault(); ev.stopPropagation(); } catch {}
        openGroupNameModal({
          title: "New folder",
          placeholder: "Name",
          okText: "Create",
          onSubmit: (name, emoji) => {
            const gs = groups();
            const id = `g-${_uid()}`;
            gs.push({ id, name, emoji: (typeof emoji === "string") ? emoji : "" });
            savePinGroups(gs);
            selGid = id;
            try { setSelectedPinsGroupId(id); } catch {}
            render();
          }
        });
      });

      leftHdr.appendChild(leftTitle);
      leftHdr.appendChild(addFolderBtn);

      const folderSearch = document.createElement("input");
      folderSearch.className = "wmeRcInput wmeRcPmSearch";
      folderSearch.placeholder = "Search folders…";
      folderSearch.addEventListener("input", () => { qFolders = String(folderSearch.value || "").trim().toLowerCase(); renderFolders(); });

      const folderList = document.createElement("div");
      folderList.className = "wmeRcPmList";

      left.appendChild(leftHdr);
      left.appendChild(folderSearch);
      left.appendChild(folderList);


      const rightHdr = document.createElement("div");
      rightHdr.className = "wmeRcPmHdrRow";

      const rightTitle = document.createElement("div");
      rightTitle.style.fontWeight = "900";
      rightTitle.style.letterSpacing = ".2px";
      rightTitle.textContent = "Pins";

      const pinSearch = document.createElement("input");
      pinSearch.className = "wmeRcInput wmeRcPmSearch";
      pinSearch.placeholder = "Search pins…";
      pinSearch.addEventListener("input", () => { qPins = String(pinSearch.value || "").trim().toLowerCase(); renderPins(); });

      const pinsList = document.createElement("div");
      pinsList.className = "wmeRcPmList";

      right.appendChild(rightHdr);
      right.appendChild(pinSearch);
      right.appendChild(pinsList);

      root.appendChild(left);
      root.appendChild(right);
      body.appendChild(root);

      function getGroupMeta(gid) {
        gid = normalizeGroupId(gid || "default");
        if (gid === "default") return { id: "default", name: "General", emoji: "" };
        const g = groups().find(x => x && x.id === gid);
        return g || { id: gid, name: "Folder", emoji: "" };
      }

      function countPinsInGroup(gid) {
        gid = normalizeGroupId(gid || "default");
        let c = 0;
        for (const p of pins()) if (normalizeGroupId(p.groupId || "default") === gid) c++;
        return c;
      }

      function mkTiny(icon, title, onClick) {
        const b = document.createElement("div");
        b.className = "wmeRcPmTiny";
        b.title = title;
        b.innerHTML = `<span class="wmeRcI">${icon}</span>`;
        b.addEventListener("click", (ev) => { try { ev.preventDefault(); ev.stopPropagation(); } catch {} onClick && onClick(); });
        return b;
      }

      function renderFolders() {
        folderList.innerHTML = "";
        const gs = groups();


        const allFolderIds = ["default", ...gs.map(g => g.id)];

        for (const gid0 of allFolderIds) {
          const meta = getGroupMeta(gid0);
          const nm = (meta.emoji ? (meta.emoji + " ") : "") + String(meta.name || "");
          const c = countPinsInGroup(gid0);

          if (qFolders) {
            const hay = (String(meta.name || "") + " " + String(meta.emoji || "")).toLowerCase();
            if (!hay.includes(qFolders)) continue;
          }

          const card = document.createElement("div");
          card.className = "wmeRcPmCard" + (normalizeGroupId(gid0) === normalizeGroupId(selGid) ? " on" : "");
          card.addEventListener("click", () => {
            selGid = normalizeGroupId(gid0);
            try { setSelectedPinsGroupId(selGid); } catch {}
            render();
          });

          const l = document.createElement("div");
          l.className = "wmeRcPmCardLeft";

          const t = document.createElement("div");
          t.className = "wmeRcPmCardTitle";
          t.textContent = nm || "Folder";

          const s = document.createElement("div");
          s.className = "wmeRcPmCardSub";
          s.textContent = `${c} pin${c === 1 ? "" : "s"}`;

          l.appendChild(t);
          l.appendChild(s);

          const btns = document.createElement("div");
          btns.className = "wmeRcPmCardBtns";


          if (gid0 !== "default") {
            btns.appendChild(mkTiny(ICONS.edit, "Rename folder", () => {
              const g = gs.find(x => x && x.id === gid0);
              openGroupNameModal({
                title: "Rename folder",
                placeholder: "Name",
                okText: "Save",
                initial: g ? g.name : "",
                initialEmoji: g ? (g.emoji || "") : "",
                onSubmit: (name, emoji) => {
                  const arr = groups();
                  const gg = arr.find(x => x && x.id === gid0);
                  if (gg) { gg.name = name; gg.emoji = (typeof emoji === "string") ? emoji : ""; }
                  savePinGroups(arr);
                  render();
                }
              });
            }));
            btns.appendChild(mkTiny(ICONS.trash, "Remove folder", () => {
              openRemoveFolderModal(gid0);

              setTimeout(() => { try { render(); } catch {} }, 250);
            }));
          } else {
            btns.appendChild(mkTiny(ICONS.trash, "Clear pins in General", () => {
              const c = countPinsInGroup("default");
              openClearDefaultFolderPinsModal({
                folderName: "General",
                count: c,
                onConfirm: () => {
                  const all = pins().filter(p => normalizeGroupId(p.groupId || "default") !== "default");
                  savePins(all);
                  render();
                  try { renderPinsPanel(); } catch {}
                  try { renderPinsMarkers(); } catch {}
                }
              });
            }));
          }

          card.appendChild(l);
          card.appendChild(btns);
          folderList.appendChild(card);
        }
      }

      function renderPins() {
        pinsList.innerHTML = "";
        const gid = normalizeGroupId(selGid || "default");
        const meta = getGroupMeta(gid);


        rightHdr.innerHTML = "";
        const title = document.createElement("div");
        title.style.fontWeight = "900";
        title.style.letterSpacing = ".2px";
        title.textContent = `Pins — ${meta.emoji ? (meta.emoji + " ") : ""}${meta.name || "General"}`;

        const hdrBtns = document.createElement("div");
        hdrBtns.style.display = "flex";
        hdrBtns.style.gap = "8px";
        hdrBtns.style.alignItems = "center";

        const closeBtn = mkTiny(ICONS.chevDown, "Close", () => close());

        try { closeBtn.querySelector("svg").style.transform = "rotate(90deg)"; } catch {}

        hdrBtns.appendChild(closeBtn);

        rightHdr.appendChild(title);
        rightHdr.appendChild(hdrBtns);

        const gs = groups();
        const folderOptions = [{ id: "default", name: "General", emoji: "" }, ...gs.map(g => ({ id: g.id, name: g.name, emoji: g.emoji || "" }))];

        const list = pins().filter(p => normalizeGroupId(p.groupId || "default") === gid);

        const q = qPins;
        const filtered = q ? list.filter(p => String(p.name || "").toLowerCase().includes(q)) : list;

        if (!filtered.length) {
          const empty = document.createElement("div");
          empty.className = "wmeRcHint";
          empty.style.opacity = ".8";
          empty.textContent = q ? "No pins match your search." : "No pins in this folder.";
          pinsList.appendChild(empty);
          return;
        }

        for (const p of filtered) {
          const row = document.createElement("div");
          row.className = "wmeRcPmRow";

          const l = document.createElement("div");
          l.className = "wmeRcPmRowLeft";

          const t = document.createElement("div");
          t.className = "wmeRcPmRowTitle";
          t.textContent = String(p.name || "Pin");

          const sub = document.createElement("div");
          sub.className = "wmeRcPmRowSub";
          const lat = (Number(p.lat) || 0).toFixed(6);
          const lon = (Number(p.lon) || 0).toFixed(6);
          sub.textContent = `${lat}, ${lon}`;

          l.appendChild(t);
          l.appendChild(sub);

          const btns = document.createElement("div");
          btns.className = "wmeRcPmRowBtns";


          const sel = document.createElement("select");
          sel.className = "wmeRcSelect wmeRcPmSelect";
          for (const fo of folderOptions) {
            const o = document.createElement("option");
            o.value = fo.id;
            o.textContent = (fo.emoji ? (fo.emoji + " ") : "") + String(fo.name || (fo.id === "default" ? "General" : "Folder"));
            if (normalizeGroupId(p.groupId || "default") === normalizeGroupId(fo.id)) o.selected = true;
            sel.appendChild(o);
          }
          sel.addEventListener("change", () => {
            const to = normalizeGroupId(sel.value || "default");
            updatePin(p.id, { groupId: to });

            render();
            try { renderPinsPanel(); } catch {}
            try { renderPinsMarkers(); } catch {}
          });

          btns.appendChild(sel);

          btns.appendChild(mkTiny(ICONS.edit, "Rename pin", () => {
            openRenamePinModal(p.id);
            setTimeout(() => { try { render(); } catch {} }, 250);
          }));

          btns.appendChild(mkTiny(ICONS.trash, "Remove pin", () => {
            confirmRemovePin(p.id);
            setTimeout(() => { try { render(); } catch {} }, 250);
          }));

          row.appendChild(l);
          row.appendChild(btns);
          pinsList.appendChild(row);
        }
      }

      function render() {

        const gid = normalizeGroupId(selGid || "default");
        const exists = gid === "default" || groups().some(g => g && g.id === gid);
        if (!exists) selGid = "default";
        renderFolders();
        renderPins();
      }

      render();
    }
  });
}


function openReminderModal(pinId) {
  const pin = loadPins().find((p) => p.id === String(pinId));
  if (!pin) return;

  const now = Date.now();
  const currentTs = pin.reminderAt && pin.reminderAt > now ? pin.reminderAt : null;

  let mode = "IN";
  let unit = (pin.reminderUnit === "HOURS") ? "HOURS" : "MINUTES";
  let inValue = Number.isFinite(pin.reminderValue) ? Math.max(1, Math.round(pin.reminderValue)) : (unit === "HOURS" ? 1 : 30);

  let atDate = "";
  let atTime = "";
  if (!currentTs) {
    try {
      const d0 = new Date(now);
      mode = "AT";
      atDate = `${String(d0.getFullYear())}-${String(d0.getMonth() + 1).padStart(2, "0")}-${String(d0.getDate()).padStart(2, "0")}`;
      atTime = `${String(d0.getHours()).padStart(2, "0")}:${String(d0.getMinutes()).padStart(2, "0")}`;
    } catch {}
  }
  if (currentTs) {
    const d = new Date(currentTs);
    const yyyy = String(d.getFullYear());
    const mm = String(d.getMonth() + 1).padStart(2, "0");
    const dd = String(d.getDate()).padStart(2, "0");
    const hh = String(d.getHours()).padStart(2, "0");
    const mi = String(d.getMinutes()).padStart(2, "0");
    atDate = `${yyyy}-${mm}-${dd}`;
    atTime = `${hh}:${mi}`;
  }
const prettyCurrent = () => {
    try {
      if (!currentTs) return "No reminder set yet.";
      const d = new Date(currentTs);
      return `Current reminder: ${d.toLocaleString()}`;
    } catch {
      return "Reminder set.";
    }
  };

  const getMaxForUnit = () => (unit === "HOURS" ? 48 : 180);
  const getStepForUnit = () => (unit === "HOURS" ? 1 : 1);

  const calcInTargetTs = () => {
    const val = Math.max(1, Math.round(inValue));
    const mins = unit === "HOURS" ? val * 60 : val;
    return Date.now() + mins * 60000;
  };
const calcAtTargetTs = () => {
  try {
    if (!atDate || !atTime) return null;
    if (!/^\d{2}:\d{2}$/.test(atTime)) return null;
    const dt = new Date(`${atDate}T${atTime}:00`);
    const t = dt.getTime();
    return Number.isFinite(t) ? t : null;
  } catch {
    return null;
  }
};

  const fmtPreview = (ts) => {
    try {
      const d = new Date(ts);
      const dd = String(d.getDate()).padStart(2, "0");
      const mo = String(d.getMonth() + 1).padStart(2, "0");
      const yy = String(d.getFullYear());
      const hh = String(d.getHours()).padStart(2, "0");
      const mi = String(d.getMinutes()).padStart(2, "0");
      return `${dd}/${mo}/${yy}, ${hh}:${mi}`;
    } catch {
      return "";
    }
  };

  const makeClock = ({ getValue, setValue, getMax, getStep, labelFn, getTargetTs, getUnitLabel }) => {
    const wrap = document.createElement("div");
    wrap.className = "wmeRcClockWrap";

    const clock = document.createElement("div");
    clock.className = "wmeRcClock";

    const ticks = document.createElement("div");
    ticks.className = "wmeRcClockTicks";
    for (let i = 0; i < 12; i++) {
      const t = document.createElement("div");
      t.className = "wmeRcClockTick";
      t.style.transform = `translate(-50%,-68px) rotate(${i * 30}deg) translate(0, 68px)`;
      ticks.appendChild(t);
    }

    const nums = document.createElement("div");
    nums.className = "wmeRcClockNums";
    for (let n = 1; n <= 12; n++) {
      const sp = document.createElement("div");
      sp.className = "wmeRcClockNum";
      sp.textContent = String(n === 12 ? 12 : n);
      const ang = ((n % 12) / 12) * Math.PI * 2 - (Math.PI / 2);
      const r = 52;
      const x = Math.cos(ang) * r;
      const y = Math.sin(ang) * r;
      sp.style.transform = `translate(-50%,-50%) translate(${x.toFixed(1)}px, ${y.toFixed(1)}px)`;
      nums.appendChild(sp);
    }

    const handMin = document.createElement("div");
    handMin.className = "wmeRcClockHand min";

    const handHour = document.createElement("div");
    handHour.className = "wmeRcClockHand hour";

    const handSec = document.createElement("div");
    handSec.className = "wmeRcClockHand sec";
    const center = document.createElement("div");
    center.className = "wmeRcClockCenter";

    clock.appendChild(ticks);
    clock.appendChild(nums);
    clock.appendChild(handHour);
    clock.appendChild(handSec);
    clock.appendChild(handMin);
    clock.appendChild(center);

    try {
      clock.style.touchAction = "none";
      handMin.style.cursor = "grab";

      let dragging = false;
      let dragPid = null;

      const timeToParts = (t) => {
        const m = String(t || "").match(/^(\d{2}):(\d{2})$/);
        if (!m) return { hh: 0, mm: 0 };
        return { hh: Math.max(0, Math.min(23, parseInt(m[1], 10))), mm: Math.max(0, Math.min(59, parseInt(m[2], 10))) };
      };

      const setMinuteFromClient = (clientX, clientY) => {
        const r = clock.getBoundingClientRect();
        const cx = r.left + r.width / 2;
        const cy = r.top + r.height / 2;
        const dx = clientX - cx;
        const dy = clientY - cy;

        let ang = Math.atan2(dy, dx);
        let deg = (ang * 180 / Math.PI) + 90;
        deg = (deg + 360) % 360;

        const minute = Math.round(deg / 6) % 60;

        const cur = timeToParts(getTime());
        const hh = cur.hh;
        const mm = minute;

        const next = `${String(hh).padStart(2, "0")}:${String(mm).padStart(2, "0")}`;
        if (typeof setTime === "function") setTime(next);
        update();
      };

      const onMove = (ev) => {
        if (!dragging) return;
        if (dragPid != null && ev.pointerId != null && ev.pointerId !== dragPid) return;
        try { ev.preventDefault(); } catch {}
        setMinuteFromClient(ev.clientX, ev.clientY);
      };

      const endDrag = (ev) => {
        if (dragPid != null && ev.pointerId != null && ev.pointerId !== dragPid) return;
        dragging = false;
        dragPid = null;
        try { handMin.releasePointerCapture(ev.pointerId); } catch {}
        try { window.removeEventListener("pointermove", onMove, true); } catch {}
        try { window.removeEventListener("pointerup", endDrag, true); } catch {}
        try { window.removeEventListener("pointercancel", endDrag, true); } catch {}
        try { handMin.style.cursor = "grab"; } catch {}
      };

      handMin.addEventListener("pointerdown", (ev) => {
        try { ev.preventDefault(); ev.stopPropagation(); } catch {}
        dragging = true;
        dragPid = ev.pointerId;
        try { handMin.setPointerCapture(ev.pointerId); } catch {}
        try { handMin.style.cursor = "grabbing"; } catch {}
        setMinuteFromClient(ev.clientX, ev.clientY);
        window.addEventListener("pointermove", onMove, true);
        window.addEventListener("pointerup", endDrag, true);
        window.addEventListener("pointercancel", endDrag, true);
      }, true);
    } catch {}

    const valueBox = document.createElement("div");
    valueBox.className = "wmeRcClockValue";

    const big = document.createElement("div");
    big.className = "wmeRcBigValue wmeRcEditableValue";
    big.title = "Click to edit";

    const sub = document.createElement("div");
    sub.className = "wmeRcBigValueSub";

    const editWrap = document.createElement("div");
    editWrap.style.display = "none";
    editWrap.style.alignItems = "center";
    editWrap.style.gap = "8px";

    const editInp = document.createElement("input");
    editInp.type = "text";
    editInp.inputMode = "numeric";
    editInp.autocomplete = "off";
    editInp.spellcheck = false;
    editInp.className = "wmeRcInput";
    try {
      editInp.style.width = "86px";
      editInp.style.padding = "8px 10px";
      editInp.style.borderRadius = "12px";
      editInp.style.fontWeight = "900";
      editInp.style.fontSize = "22px";
      editInp.style.textAlign = "center";
      editInp.style.color = "#fff";
      editInp.style.background = "rgba(255,255,255,.06)";
      editInp.style.border = "1px solid rgba(255,255,255,.18)";
      editInp.style.boxShadow = "inset 0 1px 0 rgba(255,255,255,.06)";
      editInp.style.outline = "none";
    } catch {}

    const editUnit = document.createElement("div");
    editUnit.style.fontWeight = "900";
    editUnit.style.opacity = ".92";
    editUnit.style.fontSize = "14px";
    editUnit.style.whiteSpace = "nowrap";

    editWrap.appendChild(editInp);
    editWrap.appendChild(editUnit);

    const beginEdit = () => {
      try {
        editUnit.textContent = (typeof getUnitLabel === "function") ? getUnitLabel() : "";
      } catch { editUnit.textContent = ""; }
      const cur = Math.max(1, Math.round(getValue()));
      editInp.value = String(cur);
      editWrap.style.display = "flex";
      big.style.display = "none";
      try { setTimeout(() => { try { editInp.focus(); editInp.select(); } catch {} }, 0); } catch {}
    };

    const endEdit = (commit) => {
      if (commit) {
        const v = parseInt(String(editInp.value || "").replace(/[^0-9]/g, ""), 10);
        if (Number.isFinite(v)) {
          setValue(clamp(v, 1, getMax()));
        }
      }
      editWrap.style.display = "none";
      big.style.display = "";
      update();
    };

    try {
      big.style.cursor = "text";
      big.tabIndex = 0;
      big.addEventListener("click", () => beginEdit());
      big.addEventListener("keydown", (ev) => {
        if (ev.key === "Enter") { try { ev.preventDefault(); } catch {} beginEdit(); }
      });
      editInp.addEventListener("input", () => {
        try { editInp.value = String(editInp.value || "").replace(/[^0-9]/g, ""); } catch {}
      });
      editInp.addEventListener("keydown", (ev) => {
        if (ev.key === "Enter") { try { ev.preventDefault(); } catch {} endEdit(true); }
        else if (ev.key === "Escape") { try { ev.preventDefault(); } catch {} endEdit(false); }
      });
      editInp.addEventListener("blur", () => endEdit(true));
    } catch {}

    const stepper = document.createElement("div");
    stepper.className = "wmeRcStepper";

    const btnMinus = document.createElement("div");
    btnMinus.className = "wmeRcStepBtn";
    btnMinus.textContent = "−";

    const btnPlus = document.createElement("div");
    btnPlus.className = "wmeRcStepBtn";
    btnPlus.textContent = "+";

    stepper.appendChild(btnMinus);
    stepper.appendChild(btnPlus);

    valueBox.appendChild(big);
    valueBox.appendChild(editWrap);
    valueBox.appendChild(sub);
    valueBox.appendChild(stepper);

    wrap.appendChild(clock);
    wrap.appendChild(valueBox);

let _lastMinDeg = null, _minOffsetDeg = 0;
let _lastHourDeg = null, _hourOffsetDeg = 0;

const _applySmoothDeg = (rawDeg, lastDeg, offsetDeg) => {
  let deg = rawDeg + offsetDeg;
  if (lastDeg != null) {
    if (deg < lastDeg - 180) { offsetDeg += 360; deg = rawDeg + offsetDeg; }
    else if (deg > lastDeg + 180) { offsetDeg -= 360; deg = rawDeg + offsetDeg; }
  }
  return { deg, offsetDeg };
};

const setHandsFromTs = (ts) => {
  try {
    const d = new Date(ts);
    const hh = d.getHours();
    const mm = d.getMinutes();
    const ss = d.getSeconds();
    const h = (hh % 12) + (mm / 60);

    const rawH = (h / 12) * 360;
    const rawM = (mm / 60) * 360;

    const rawS = (ss / 60) * 360;

    const hhRes = _applySmoothDeg(rawH, _lastHourDeg, _hourOffsetDeg);
    _hourOffsetDeg = hhRes.offsetDeg;
    _lastHourDeg = hhRes.deg;

    const mmRes = _applySmoothDeg(rawM, _lastMinDeg, _minOffsetDeg);
    _minOffsetDeg = mmRes.offsetDeg;
    _lastMinDeg = mmRes.deg;

    handHour.style.transform = `translate(-50%,-100%) rotate(${hhRes.deg}deg)`;
    handMin.style.transform  = `translate(-50%,-100%) rotate(${mmRes.deg}deg)`;
    try { handSec.style.transform = `translate(-50%,-100%) rotate(${rawS}deg)`; } catch {}
  } catch {
    _lastMinDeg = null; _minOffsetDeg = 0;
    _lastHourDeg = null; _hourOffsetDeg = 0;
    handHour.style.transform = `translate(-50%,-100%) rotate(0deg)`;
    handMin.style.transform  = `translate(-50%,-100%) rotate(0deg)`;
    try { handSec.style.transform = `translate(-50%,-100%) rotate(0deg)`; } catch {}
  }
};

    const fmtTime = (ts) => {
      try {
        const d = new Date(ts);
        const hh = String(d.getHours()).padStart(2, "0");
        const mm = String(d.getMinutes()).padStart(2, "0");
        return `${hh}:${mm}`;
      } catch {
        return "";
      }
    };

    const update = () => {
      const v = clamp(Math.round(getValue()), 1, getMax());
      setValue(v);

      const ts = (typeof getTargetTs === "function") ? getTargetTs() : null;
      if (ts) setHandsFromTs(ts);

      big.textContent = labelFn(v);
      if (ts) sub.textContent = `Will remind at: ${fmtTime(ts)}`;
      else sub.textContent = `Use ± • max ${getMax()}`;
    };

    btnMinus.addEventListener("click", () => {
      const step = getStep();
      setValue(clamp(Math.round(getValue()) - step, 1, getMax()));
      update();
    });
    btnPlus.addEventListener("click", () => {
      const step = getStep();
      setValue(clamp(Math.round(getValue()) + step, 1, getMax()));
      update();
    });

    update();
    return { wrap, update };
  };

  const makeAtPreviewClock = ({ getDate, getTime, getTargetTs, setTime }) => {
    const wrap = document.createElement("div");
    wrap.className = "wmeRcClockWrap";

    const clock = document.createElement("div");
    clock.className = "wmeRcClock";

    const ticks = document.createElement("div");
    ticks.className = "wmeRcClockTicks";
    for (let i = 0; i < 12; i++) {
      const t = document.createElement("div");
      t.className = "wmeRcClockTick";
      t.style.transform = `translate(-50%,-68px) rotate(${i * 30}deg) translate(0, 68px)`;
      ticks.appendChild(t);
    }

    const nums = document.createElement("div");
    nums.className = "wmeRcClockNums";
    for (let n = 1; n <= 12; n++) {
      const sp = document.createElement("div");
      sp.className = "wmeRcClockNum";
      sp.textContent = String(n === 12 ? 12 : n);
      const ang = ((n % 12) / 12) * Math.PI * 2 - (Math.PI / 2);
      const r = 52;
      const x = Math.cos(ang) * r;
      const y = Math.sin(ang) * r;
      sp.style.transform = `translate(-50%,-50%) translate(${x.toFixed(1)}px, ${y.toFixed(1)}px)`;
      nums.appendChild(sp);
    }

    const handMin = document.createElement("div");
    handMin.className = "wmeRcClockHand min";

    const handHour = document.createElement("div");
    handHour.className = "wmeRcClockHand hour";

    const handSec = document.createElement("div");
    handSec.className = "wmeRcClockHand sec";

    const center = document.createElement("div");
    center.className = "wmeRcClockCenter";

    clock.appendChild(ticks);
    clock.appendChild(nums);
    clock.appendChild(handHour);
    clock.appendChild(handSec);
    clock.appendChild(handMin);
    clock.appendChild(center);

    try {
      clock.style.touchAction = "none";
      handMin.style.cursor = "grab";

      let dragging = false;
      let dragPid = null;

      const timeToParts = (t) => {
        const m = String(t || "").match(/^(\d{2}):(\d{2})$/);
        if (!m) return { hh: 0, mm: 0 };
        return { hh: Math.max(0, Math.min(23, parseInt(m[1], 10))), mm: Math.max(0, Math.min(59, parseInt(m[2], 10))) };
      };

      const setMinuteFromClient = (clientX, clientY) => {
        const r = clock.getBoundingClientRect();
        const cx = r.left + r.width / 2;
        const cy = r.top + r.height / 2;
        const dx = clientX - cx;
        const dy = clientY - cy;

        let ang = Math.atan2(dy, dx);
        let deg = (ang * 180 / Math.PI) + 90;
        deg = (deg + 360) % 360;

        const minute = Math.round(deg / 6) % 60;

        const cur = timeToParts(getTime());
        const hh = cur.hh;
        const mm = minute;

        const next = `${String(hh).padStart(2, "0")}:${String(mm).padStart(2, "0")}`;
        if (typeof setTime === "function") setTime(next);
        update();
      };

      const onMove = (ev) => {
        if (!dragging) return;
        if (dragPid != null && ev.pointerId != null && ev.pointerId !== dragPid) return;
        try { ev.preventDefault(); } catch {}
        setMinuteFromClient(ev.clientX, ev.clientY);
      };

      const endDrag = (ev) => {
        if (dragPid != null && ev.pointerId != null && ev.pointerId !== dragPid) return;
        dragging = false;
        dragPid = null;
        try { handMin.releasePointerCapture(ev.pointerId); } catch {}
        try { window.removeEventListener("pointermove", onMove, true); } catch {}
        try { window.removeEventListener("pointerup", endDrag, true); } catch {}
        try { window.removeEventListener("pointercancel", endDrag, true); } catch {}
        try { handMin.style.cursor = "grab"; } catch {}
      };

      handMin.addEventListener("pointerdown", (ev) => {
        try { ev.preventDefault(); ev.stopPropagation(); } catch {}
        dragging = true;
        dragPid = ev.pointerId;
        try { handMin.setPointerCapture(ev.pointerId); } catch {}
        try { handMin.style.cursor = "grabbing"; } catch {}
        setMinuteFromClient(ev.clientX, ev.clientY);
        window.addEventListener("pointermove", onMove, true);
        window.addEventListener("pointerup", endDrag, true);
        window.addEventListener("pointercancel", endDrag, true);
      }, true);
    } catch {}

    const valueBox = document.createElement("div");
    valueBox.className = "wmeRcClockValue";

    const big = document.createElement("div");
    big.className = "wmeRcBigValue";

    const sub = document.createElement("div");
    sub.className = "wmeRcBigValueSub";

    valueBox.appendChild(big);
    valueBox.appendChild(sub);

    wrap.appendChild(clock);
    wrap.appendChild(valueBox);

let _lastMinDeg = null, _minOffsetDeg = 0;
let _lastHourDeg = null, _hourOffsetDeg = 0;

const _applySmoothDeg = (rawDeg, lastDeg, offsetDeg) => {
  let deg = rawDeg + offsetDeg;
  if (lastDeg != null) {
    if (deg < lastDeg - 180) { offsetDeg += 360; deg = rawDeg + offsetDeg; }
    else if (deg > lastDeg + 180) { offsetDeg -= 360; deg = rawDeg + offsetDeg; }
  }
  return { deg, offsetDeg };
};

const setHandsFromTs = (ts) => {
  try {
    const d = new Date(ts);
    const hh = d.getHours();
    const mm = d.getMinutes();
    const ss = d.getSeconds();
    const h = (hh % 12) + (mm / 60);

    const rawH = (h / 12) * 360;
    const rawM = (mm / 60) * 360;

    const rawS = (ss / 60) * 360;

    const hhRes = _applySmoothDeg(rawH, _lastHourDeg, _hourOffsetDeg);
    _hourOffsetDeg = hhRes.offsetDeg;
    _lastHourDeg = hhRes.deg;

    const mmRes = _applySmoothDeg(rawM, _lastMinDeg, _minOffsetDeg);
    _minOffsetDeg = mmRes.offsetDeg;
    _lastMinDeg = mmRes.deg;

    handHour.style.transform = `translate(-50%,-100%) rotate(${hhRes.deg}deg)`;
    handMin.style.transform  = `translate(-50%,-100%) rotate(${mmRes.deg}deg)`;
    try { handSec.style.transform = `translate(-50%,-100%) rotate(${rawS}deg)`; } catch {}
  } catch {
    _lastMinDeg = null; _minOffsetDeg = 0;
    _lastHourDeg = null; _hourOffsetDeg = 0;
    handHour.style.transform = `translate(-50%,-100%) rotate(0deg)`;
    handMin.style.transform  = `translate(-50%,-100%) rotate(0deg)`;
    try { handSec.style.transform = `translate(-50%,-100%) rotate(0deg)`; } catch {}
  }
};

    const fmtTime = (ts) => {
      try {
        const d = new Date(ts);
        const hh = String(d.getHours()).padStart(2, "0");
        const mm = String(d.getMinutes()).padStart(2, "0");
        return `${hh}:${mm}`;
      } catch {
        return "";
      }
    };

    const update = () => {
      const d = getDate();
      const tt = getTime();
  big.textContent = `${d} ${tt || "00:00"}`;

      const ts = (typeof getTargetTs === "function") ? getTargetTs() : calcAtTargetTs();
      if (ts) {
        setHandsFromTs(ts);
        sub.textContent = `Will remind at: ${fmtTime(ts)} • ${fmtPreview(ts)}`;
      } else {
        setHandsFromTs(Date.now());
        sub.textContent = "Pick a date & time";
      }
    };

    update();
    return { wrap, update };
  };

  openModal({
    title: "Reminder",
    iconSvg: ICONS.bell,
    bodyBuilder: ({ body, close, modal }) => {
      try { applyPinMarkerColors(modal, (pin && pin.color) || "#ff8a00"); } catch {}
      try { const c = normalizePinColor((pin && pin.color) || "#ff8a00"); const rgb = hexToRgb(c); if (rgb) modal.style.setProperty("--pinRGB", `${rgb.r},${rgb.g},${rgb.b}`); } catch {}
      const topRow = document.createElement("div");
      topRow.className = "wmeRcWizardHeader";
      topRow.innerHTML = `
        <div class="wmeRcWizardHdrLeft">
          <div class="wmeRcWizardTitle">Set a reminder</div>
          <div class="wmeRcWizardSub">${pin.name || "Pinned place"}</div>
        </div>
      `;

      const tabs = document.createElement("div");
      tabs.className = "wmeRcTabs";

      const tabIn = document.createElement("div");
      tabIn.className = "wmeRcTab";
      tabIn.textContent = "In";

      const tabAt = document.createElement("div");
      tabAt.className = "wmeRcTab";
      tabAt.textContent = "At";

      tabs.appendChild(tabIn);
      tabs.appendChild(tabAt);
      topRow.appendChild(tabs);

      const hint = document.createElement("div");
      hint.className = "wmeRcHint";
      hint.textContent = prettyCurrent();


      let noteVal = String(pin.reminderNote || "");
      const noteBox = document.createElement("div");
      noteBox.className = "wmeRcWizardSection";

      const noteLbl = document.createElement("div");
      noteLbl.className = "wmeRcHint";
      noteLbl.textContent = "Notes";

      const noteInp = document.createElement("textarea");
      noteInp.className = "wmeRcInput";
      noteInp.placeholder = "Add a note…";
      noteInp.value = noteVal;
      noteInp.rows = 4;
      try { noteInp.style.resize = "none"; } catch {}
      try {
        noteInp.style.overflowY = "hidden";
        noteInp.style.minHeight = "88px";
        noteInp.style.maxHeight = "88px";
      } catch {}
      noteInp.addEventListener("input", () => { noteVal = noteInp.value; });

      noteBox.appendChild(noteLbl);
      noteBox.appendChild(noteInp);

      const section = document.createElement("div");
      section.className = "wmeRcWizardSection";

      const unitRow = document.createElement("div");
      unitRow.className = "wmeRcSplit";

      const unitMin = document.createElement("div");
      unitMin.className = "wmeRcWizardBtn";
      unitMin.textContent = "Minutes";

      const unitHr = document.createElement("div");
      unitHr.className = "wmeRcWizardBtn";
      unitHr.textContent = "Hours";

      unitRow.appendChild(unitMin);
      unitRow.appendChild(unitHr);
      const clockIn = makeClock({
        getValue: () => inValue,
        setValue: (v) => { inValue = v; },
        getMax: () => getMaxForUnit(),
        getStep: () => getStepForUnit(),
        getTargetTs: () => calcInTargetTs(),
        getUnitLabel: () => unit === "HOURS" ? "hours" : "minutes",
        labelFn: (v) => unit === "HOURS" ? `${v} hour${v === 1 ? "" : "s"}` : `${v} minute${v === 1 ? "" : "s"}`,
      });

      const quick = document.createElement("div");
      quick.className = "wmeRcQuickRow";
      const mkQuick = (label, nextUnit, nextValue) => {
        const b = document.createElement("div");
        b.className = "wmeRcQuick";
        b.textContent = label;
        b.addEventListener("click", () => {
          unit = nextUnit;
          inValue = nextValue;
          syncUI();
        });
        return b;
      };
      quick.appendChild(mkQuick("2m", "MINUTES", 2));
      quick.appendChild(mkQuick("5m", "MINUTES", 5));
      quick.appendChild(mkQuick("10m", "MINUTES", 10));
      quick.appendChild(mkQuick("15m", "MINUTES", 15));
      quick.appendChild(mkQuick("30m", "MINUTES", 30));
      quick.appendChild(mkQuick("1h", "HOURS", 1));
      quick.appendChild(mkQuick("2h", "HOURS", 2));
      quick.appendChild(mkQuick("4h", "HOURS", 4));

const dt = document.createElement("div");
      dt.className = "wmeRcDT";

      let calPop = null;
      let timePop = null;

      const fmtDateLabel = () => {
        try {
          const parts = String(atDate || "").split("-");
          if (parts.length !== 3) return "Pick date";
          return `${parts[2]}/${parts[1]}/${parts[0]}`;
        } catch {
          return "Pick date";
        }
      };
      const fmtTimeLabel = () => {
        try {
          const s = String(atTime || "");
          if (!s || s.length < 4) return "Pick time";
          return s;
        } catch {
          return "Pick time";
        }
      };

      const dateBtn = document.createElement("button");
      dateBtn.type = "button";
      dateBtn.className = "wmeRcPickerBtn";

      const timeBtn = document.createElement("button");
      timeBtn.type = "button";
      timeBtn.className = "wmeRcPickerBtn";

      const syncPickers = () => {
        try { dateBtn.innerHTML = `<span class="wmeRcPickerK">Date</span><span class="wmeRcPickerV">${fmtDateLabel()}</span>`; } catch {}
        try { timeBtn.innerHTML = `<span class="wmeRcPickerK">Time</span><span class="wmeRcPickerV">${fmtTimeLabel()}</span>`; } catch {}
      };
      syncPickers();

      const closePickers = () => {
        try {
          if (calPop) {
            try { document.removeEventListener("pointerdown", calPop._wmeRcDown, true); } catch {}
            calPop.remove();
          }
        } catch {}
        try {
          if (timePop) {
            try { document.removeEventListener("pointerdown", timePop._wmeRcDown, true); } catch {}
            timePop.remove();
          }
        } catch {}
        calPop = null;
        timePop = null;
      };

      const posPop = (pop, anchorEl) => {
        try {
          const r = anchorEl.getBoundingClientRect();
          const pw = pop.offsetWidth || 320;
          const ph = pop.offsetHeight || 280;
          let left = r.left;
          left = Math.max(12, Math.min(window.innerWidth - pw - 12, left));
          let top = r.bottom + 10;
          if (top + ph > window.innerHeight - 12) top = r.top - ph - 10;
          top = Math.max(12, Math.min(window.innerHeight - ph - 12, top));
          pop.style.left = left + "px";
          pop.style.top  = top + "px";
        } catch {}
      };

      const openCalendar = () => {
        try {
          closePickers();
          calPop = document.createElement("div");
          calPop.className = "wmeRcPickerPop wmeRcCalPop";
          try { applyPinMarkerColors(calPop, (pin && pin.color) || "#ff8a00"); } catch {}
          try { const c = normalizePinColor((pin && pin.color) || "#ff8a00"); const rgb = hexToRgb(c); if (rgb) calPop.style.setProperty("--pinRGB", `${rgb.r},${rgb.g},${rgb.b}`); } catch {}

          const header = document.createElement("div");
          header.className = "wmeRcCalHdr";

          const btnPrev = document.createElement("button");
          btnPrev.type = "button";
          btnPrev.className = "wmeRcCalNav";
          btnPrev.textContent = "‹";

          const btnNext = document.createElement("button");
          btnNext.type = "button";
          btnNext.className = "wmeRcCalNav";
          btnNext.textContent = "›";

          const title = document.createElement("div");
          title.className = "wmeRcCalTitle";

          header.appendChild(btnPrev);
          header.appendChild(title);
          header.appendChild(btnNext);

          const dow = document.createElement("div");
          dow.className = "wmeRcCalDow";
          for (const d of ["M","T","W","T","F","S","S"]) {
            const c = document.createElement("div");
            c.textContent = d;
            dow.appendChild(c);
          }

          const grid = document.createElement("div");
          grid.className = "wmeRcCalGrid";

          const parseYmd = (s) => {
            try {
              const parts = String(s || "").split("-");
              if (parts.length !== 3) return null;
              const y = parseInt(parts[0], 10);
              const m = parseInt(parts[1], 10);
              const d = parseInt(parts[2], 10);
              if (!Number.isFinite(y) || !Number.isFinite(m) || !Number.isFinite(d)) return null;
              return { y, m, d };
            } catch {
              return null;
            }
          };

          let view = (() => {
            const p = parseYmd(atDate);
            if (p) return { y: p.y, m: p.m };
            const n = new Date();
            return { y: n.getFullYear(), m: n.getMonth() + 1 };
          })();

          const toYmd = (y, m, d) => `${String(y).padStart(4, "0")}-${String(m).padStart(2, "0")}-${String(d).padStart(2, "0")}`;

          const render = () => {
            try {
              const dt0 = new Date(view.y, view.m - 1, 1);
              title.textContent = dt0.toLocaleString(undefined, { month: "long", year: "numeric" });
            } catch {
              title.textContent = `${view.m}/${view.y}`;
            }
            grid.innerHTML = "";

            const first = new Date(view.y, view.m - 1, 1);
            const firstDow = (first.getDay() + 6) % 7;
            const daysIn = new Date(view.y, view.m, 0).getDate();

            for (let i = 0; i < firstDow; i++) {
              const off = document.createElement("div");
              off.className = "wmeRcCalCell off";
              grid.appendChild(off);
            }

            const sel = parseYmd(atDate);
            for (let d = 1; d <= daysIn; d++) {
              const b = document.createElement("button");
              b.type = "button";
              b.className = "wmeRcCalCell";
              b.textContent = String(d);
              if (sel && sel.y === view.y && sel.m === view.m && sel.d === d) b.classList.add("sel");
              b.addEventListener("click", (ev) => {
                try { ev.preventDefault(); ev.stopPropagation(); } catch {}
                atDate = toYmd(view.y, view.m, d);
                syncPickers();
                try { clockAt.update(); } catch {}
                closePickers();
              });
              grid.appendChild(b);
            }
          };

          btnPrev.addEventListener("click", (ev) => {
            try { ev.preventDefault(); ev.stopPropagation(); } catch {}
            view.m -= 1;
            if (view.m < 1) { view.m = 12; view.y -= 1; }
            render();
          });
          btnNext.addEventListener("click", (ev) => {
            try { ev.preventDefault(); ev.stopPropagation(); } catch {}
            view.m += 1;
            if (view.m > 12) { view.m = 1; view.y += 1; }
            render();
          });

          calPop.appendChild(header);
          calPop.appendChild(dow);
          calPop.appendChild(grid);

          (document.body || document.documentElement).appendChild(calPop);
          render();
          requestAnimationFrame(() => posPop(calPop, dateBtn));

          const onDown = (ev) => {
            try {
              const t = ev.target;
              if (!calPop) return;
              if (t && (calPop.contains(t) || dateBtn.contains(t))) return;
              closePickers();
            } catch {}
          };
          document.addEventListener("pointerdown", onDown, true);
          try { calPop._wmeRcDown = onDown; } catch {}
        } catch {}
      };

      const openTime = () => {
        try {
          closePickers();

          timePop = document.createElement("div");
          timePop.className = "wmeRcPickerPop wmeRcTimePop";

          try { applyPinMarkerColors(timePop, (pin && pin.color) || "#ff8a00"); } catch {}

          const header = document.createElement("div");
          header.className = "wmeRcTimeHdr";
          header.textContent = "Pick time";

          const box = document.createElement("div");
          box.className = "wmeRcTimeClockBox";

          const label = document.createElement("input");
          label.type = "text";
          label.inputMode = "numeric";
          label.autocomplete = "off";
          label.spellcheck = false;
          label.maxLength = 5;
          label.className = "wmeRcTimeClockLabel wmeRcTimeClockInput";
          label.value = atTime || "00:00";

          const help = document.createElement("div");
          help.className = "wmeRcTimeClockHelp";
          help.textContent = "Drag the clock hands";
          let pickHandFromClient = () => "MIN";

          const clock = document.createElement("div");
          clock.className = "wmeRcClock wmeRcClockPick";
          pickHandFromClient = (clientX, clientY) => {
            try {
              const r = clock.getBoundingClientRect();
              const cx = r.left + r.width / 2;
              const cy = r.top + r.height / 2;
              const dx = clientX - cx;
              const dy = clientY - cy;
              const dist = Math.sqrt(dx*dx + dy*dy);
              const radius = Math.min(r.width, r.height) / 2;
              return (dist < radius * 0.56) ? "HOUR" : "MIN";
            } catch {
              return "MIN";
            }
          };


          const ticks = document.createElement("div");
          ticks.className = "wmeRcClockTicks";
          for (let i = 0; i < 12; i++) {
            const t = document.createElement("div");
            t.className = "wmeRcClockTick";
            t.style.transform = `translate(-50%,-78px) rotate(${i * 30}deg) translate(0, 78px)`;
            ticks.appendChild(t);
          }

          const nums = document.createElement("div");
          nums.className = "wmeRcClockNums";
          for (let n = 1; n <= 12; n++) {
            const sp = document.createElement("div");
            sp.className = "wmeRcClockNum";
            sp.textContent = String(n === 12 ? 12 : n);
            const ang = ((n % 12) / 12) * Math.PI * 2 - (Math.PI / 2);
            const r = 60;
            const x = Math.cos(ang) * r;
            const y = Math.sin(ang) * r;
            sp.style.transform = `translate(-50%,-50%) translate(${x.toFixed(1)}px, ${y.toFixed(1)}px)`;
            nums.appendChild(sp);
          }

          const handMin = document.createElement("div");
          handMin.className = "wmeRcClockHand min";
          handMin.style.pointerEvents = "auto";
          handMin.style.cursor = "grab";

          const handHour = document.createElement("div");
          handHour.className = "wmeRcClockHand hour";
          handHour.style.pointerEvents = "auto";
          handHour.style.cursor = "grab";

          const center = document.createElement("div");
          center.className = "wmeRcClockCenter";

          clock.appendChild(ticks);
          clock.appendChild(nums);
          clock.appendChild(handHour);
          clock.appendChild(handMin);
          clock.appendChild(center);

          const timeToParts = (t) => {
            const m = String(t || "").match(/^(\d{2}):(\d{2})$/);
            if (!m) return { hh: 0, mm: 0 };
            return { hh: Math.max(0, Math.min(23, parseInt(m[1], 10))), mm: Math.max(0, Math.min(59, parseInt(m[2], 10))) };
          };

          const normalizeTimeStr = (s) => {
            try {
              let v = String(s || "").trim();
              if (!v) return null;
              v = v.replace(/\s+/g, "");
              let m = v.match(/^(\d{1,2}):(\d{2})$/);
              if (m) {
                const hh = Math.max(0, Math.min(23, parseInt(m[1], 10)));
                const mm = Math.max(0, Math.min(59, parseInt(m[2], 10)));
                return `${String(hh).padStart(2,"0")}:${String(mm).padStart(2,"0")}`;
              }
              m = v.match(/^(\d{3,4})$/);
              if (m) {
                const raw = m[1].padStart(4, "0");
                const hh = Math.max(0, Math.min(23, parseInt(raw.slice(0,2), 10)));
                const mm = Math.max(0, Math.min(59, parseInt(raw.slice(2,4), 10)));
                return `${String(hh).padStart(2,"0")}:${String(mm).padStart(2,"0")}`;
              }
              return null;
            } catch {
              return null;
            }
          };

          const applyTypedTime = (closeAfter) => {
            try {
              const norm = normalizeTimeStr(label.value);
              if (norm) {
                atTime = norm;
                syncPickers();
                try { clockAt.update(); } catch {}
                updateHands();
              } else {
                try { label.value = atTime || "00:00"; } catch {}
              }
            } catch {}
            if (closeAfter) { try { closePickers(); } catch {} }
          };
try {
  label.style.color = "#fff";
  label.style.caretColor = "#fff";
} catch {}

let _editHH = "00";
let _editMM = "00";
let _editPhase = "H";
let _editIdx = 0;

const _syncEditFromAt = () => {
  const p = timeToParts(atTime || "00:00");
  _editHH = String(p.hh).padStart(2, "0");
  _editMM = String(p.mm).padStart(2, "0");
};

const _renderEdit = () => {
  try { label.value = `${_editHH}:${_editMM}`; } catch {}
};

const _applyEditToAt = () => {
  try {
    let hh = parseInt(_editHH, 10);
    let mm = parseInt(_editMM, 10);
    if (Number.isNaN(hh)) hh = 0;
    if (Number.isNaN(mm)) mm = 0;
    hh = Math.max(0, Math.min(23, hh));
    mm = Math.max(0, Math.min(59, mm));
    _editHH = String(hh).padStart(2, "0");
    _editMM = String(mm).padStart(2, "0");
    atTime = `${_editHH}:${_editMM}`;
    syncPickers();
    try { clockAt.update(); } catch {}
    updateHands();
    _renderEdit();
  } catch {}
};

const _selectHours = () => { try { label.setSelectionRange(0, 2); } catch {} _editPhase = "H"; _editIdx = 0; };
const _selectMinutes = () => { try { label.setSelectionRange(3, 5); } catch {} _editPhase = "M"; _editIdx = 0; };

label.addEventListener("focus", () => {
  _syncEditFromAt();
  _renderEdit();
  _selectHours();
});

label.addEventListener("pointerup", () => {
  try {
    const s = label.selectionStart || 0;
    if (s >= 3) { _editPhase = "M"; _editIdx = Math.min(1, Math.max(0, s - 3)); }
    else { _editPhase = "H"; _editIdx = Math.min(1, Math.max(0, s)); }
  } catch {}
});

label.addEventListener("paste", (ev) => {
  try {
    const txt = (ev.clipboardData && ev.clipboardData.getData("text")) || "";
    const norm = normalizeTimeStr(txt);
    if (norm) {
      try { ev.preventDefault(); ev.stopPropagation(); } catch {}
      atTime = norm;
      _syncEditFromAt();
      _renderEdit();
      _applyEditToAt();
      _selectMinutes();
    }
  } catch {}
});

label.addEventListener("keydown", (ev) => {
  if (!ev) return;
  const k = ev.key;

  if (k === "Escape") { try { ev.preventDefault(); ev.stopPropagation(); } catch {} closePickers(); return; }
  if (k === "Enter") { try { ev.preventDefault(); ev.stopPropagation(); } catch {} _applyEditToAt(); closePickers(); return; }
  if (k === ":") { try { ev.preventDefault(); ev.stopPropagation(); } catch {} _selectMinutes(); return; }

  if (/^[0-9]$/.test(k)) {
    try { ev.preventDefault(); ev.stopPropagation(); } catch {}
    _syncEditFromAt();

    if (_editPhase === "H") {
      _editHH = (_editIdx === 0) ? (k + _editHH[1]) : (_editHH[0] + k);
      _editIdx++;
      _applyEditToAt();
      if (_editIdx >= 2) { _selectMinutes(); return; }
      try { label.setSelectionRange(_editIdx, _editIdx); } catch {}
      return;
    }

    _editMM = (_editIdx === 0) ? (k + _editMM[1]) : (_editMM[0] + k);
    _editIdx++;
    _applyEditToAt();
    if (_editIdx >= 2) { try { label.setSelectionRange(5, 5); } catch {} return; }
    try { label.setSelectionRange(3 + _editIdx, 3 + _editIdx); } catch {}
    return;
  }

  if (k === "Backspace") {
    try { ev.preventDefault(); ev.stopPropagation(); } catch {}
    _syncEditFromAt();

    if (_editPhase === "M") {
      if (_editIdx <= 0) { _selectHours(); try { label.setSelectionRange(1, 1); } catch {} _editIdx = 1; return; }
      _editIdx--;
      _editMM = (_editIdx === 0) ? ("0" + _editMM[1]) : (_editMM[0] + "0");
      _applyEditToAt();
      try { label.setSelectionRange(3 + _editIdx, 3 + _editIdx); } catch {}
      return;
    }

    if (_editIdx <= 0) { _editIdx = 0; _editHH = "00"; _applyEditToAt(); _selectHours(); return; }
    _editIdx--;
    _editHH = (_editIdx === 0) ? ("0" + _editHH[1]) : (_editHH[0] + "0");
    _applyEditToAt();
    try { label.setSelectionRange(_editIdx, _editIdx); } catch {}
    return;
  }

  if (k === "Delete") {
    try {
      const s = label.selectionStart || 0;
      if (s === 2) { ev.preventDefault(); ev.stopPropagation(); _selectMinutes(); return; }
    } catch {}
  }
});

label.addEventListener("blur", () => { try { _applyEditToAt(); } catch {} });


          const cur = timeToParts(atTime || "00:00");

          const setHm = (hh, mm) => {
            atTime = `${String(hh).padStart(2, "0")}:${String(mm).padStart(2, "0")}`;
            syncPickers();
            try { clockAt.update(); } catch {}
            updateHands();
          };

          const updateLabel = () => { try { label.value = atTime || "00:00"; } catch {} };

          const updateHands = () => {
            const p = timeToParts(atTime || "00:00");
            const hh = p.hh;
            const mm = p.mm;

            const h = (hh % 12) + (mm / 60);
            const hDeg = (h / 12) * 360;
            const mDeg = (mm / 60) * 360;

            handHour.style.transform = `translate(-50%,-100%) rotate(${hDeg}deg)`;
            handMin.style.transform  = `translate(-50%,-100%) rotate(${mDeg}deg)`;
            updateLabel();
          };

          const angleFromClient = (clientX, clientY) => {
            const r = clock.getBoundingClientRect();
            const cx = r.left + r.width / 2;
            const cy = r.top + r.height / 2;
            const dx = clientX - cx;
            const dy = clientY - cy;
            let ang = Math.atan2(dy, dx);
            let deg = (ang * 180 / Math.PI) + 90;
            deg = (deg + 360) % 360;
            return deg;
          };

          const setMinuteFromClient = (clientX, clientY) => {
            const deg = angleFromClient(clientX, clientY);
            const minute = Math.round(deg / 6) % 60;
            const p = timeToParts(atTime || "00:00");
            setHm(p.hh, minute);
          };

          const setHourFromClient = (clientX, clientY) => {
            const deg = angleFromClient(clientX, clientY);
            const h12 = Math.round(deg / 30) % 12;
            const p = timeToParts(atTime || "00:00");
            const curH = p.hh;

            const candA = (h12 === 0 ? 12 : h12) % 12;
            const a = candA;
            const b = candA + 12;

            const pick = (Math.abs(a - curH) <= Math.abs(b - curH)) ? a : b;
            setHm(Math.max(0, Math.min(23, pick)), p.mm);
          };

let _dragLastDeg = null;

const startDrag = (ev, forcedMode) => {
  try { ev.preventDefault(); ev.stopPropagation(); } catch {}

  const pid = ev.pointerId;
  try { clock.setPointerCapture(pid); } catch {}

  const dragMode = (() => {
    try {
      if (forcedMode) return forcedMode;
      const t = ev.target;
      if (t === handHour) return "HOUR";
      if (t === handMin) return "MIN";
      return pickHandFromClient(ev.clientX, ev.clientY);
    } catch {
      return "MIN";
    }
  })();

  _dragLastDeg = null;

  try {
    if (dragMode === "HOUR") {
      handHour.style.cursor = "grabbing";
      handMin.style.cursor = "default";
    } else {
      handMin.style.cursor = "grabbing";
      handHour.style.cursor = "default";
    }
  } catch {}

  const apply = (x, y) => {
    if (dragMode === "HOUR") {
      setHourFromClient(x, y);
      return;
    }

    const deg = angleFromClient(x, y);
    const minute = Math.round(deg / 6) % 60;

    const p = timeToParts(atTime || "00:00");
    let hh = p.hh;
    const prevMin = p.mm;

    if (_dragLastDeg != null) {
      const delta = ((deg - _dragLastDeg + 540) % 360) - 180;
      if (delta > 0 && prevMin >= 50 && minute <= 10) hh = (hh + 1) % 24;
      if (delta < 0 && prevMin <= 10 && minute >= 50) hh = (hh + 23) % 24;
    }

    _dragLastDeg = deg;
    setHm(hh, minute);
  };

  const onMove = (e) => {
    if (e.pointerId != null && e.pointerId !== pid) return;
    try { e.preventDefault(); } catch {}
    apply(e.clientX, e.clientY);
  };

  const end = (e) => {
    if (e.pointerId != null && e.pointerId !== pid) return;
    try { clock.releasePointerCapture(pid); } catch {}
    try { window.removeEventListener("pointermove", onMove, true); } catch {}
    try { window.removeEventListener("pointerup", end, true); } catch {}
    try { window.removeEventListener("pointercancel", end, true); } catch {}
    try {
      handMin.style.cursor = "grab";
      handHour.style.cursor = "grab";
    } catch {}
    _dragLastDeg = null;
  };

  apply(ev.clientX, ev.clientY);

  window.addEventListener("pointermove", onMove, true);
  window.addEventListener("pointerup", end, true);
  window.addEventListener("pointercancel", end, true);
};

handMin.addEventListener("pointerdown", (ev) => startDrag(ev, "MIN"), true);
handHour.addEventListener("pointerdown", (ev) => startDrag(ev, "HOUR"), true);

clock.addEventListener("pointerdown", (ev) => {
  try {
    const t = ev.target;
    if (t === handMin || t === handHour) return;
  } catch {}
  try { ev.preventDefault(); ev.stopPropagation(); } catch {}
  const mode = pickHandFromClient(ev.clientX, ev.clientY);
  startDrag(ev, mode);
}, true);

          const doneRow = document.createElement("div");
          doneRow.className = "wmeRcTimeDoneRow";

          const resetBtn = document.createElement("button");
          resetBtn.type = "button";
          resetBtn.className = "wmeRcTimeDoneBtn wmeRcTimeResetBtn";
          resetBtn.textContent = "Reset time";
          resetBtn.addEventListener("click", (ev) => {
            try { ev.preventDefault(); ev.stopPropagation(); } catch {}
            try {
              const d = new Date();
              setHm(d.getHours(), d.getMinutes());
              try { _syncEditFromAt(); _renderEdit(); } catch {}
              try { label.focus(); _selectHours(); } catch {}
            } catch {}
          });

          const doneBtn = document.createElement("button");
          doneBtn.type = "button";
          doneBtn.className = "wmeRcTimeDoneBtn";
          doneBtn.textContent = "Save";
          doneBtn.addEventListener("click", (ev) => { try { ev.preventDefault(); ev.stopPropagation(); } catch {} closePickers(); });

          doneRow.appendChild(resetBtn);
          doneRow.appendChild(doneBtn);

          box.appendChild(label);
          box.appendChild(clock);
          box.appendChild(help);
          box.appendChild(doneRow);

          timePop.appendChild(header);
          timePop.appendChild(box);

          (document.body || document.documentElement).appendChild(timePop);
          updateHands();
          requestAnimationFrame(() => posPop(timePop, timeBtn));

          timePop.addEventListener("keydown", (ev) => {
            if (ev.key === "Enter") { try { ev.preventDefault(); ev.stopPropagation(); } catch {} closePickers(); }
          });

          const onDown = (ev) => {
            try {
              const t = ev.target;
              if (!timePop) return;
              if (t && (timePop.contains(t) || timeBtn.contains(t))) return;
              closePickers();
            } catch {}
          };
          document.addEventListener("pointerdown", onDown, true);
          try { timePop._wmeRcDown = onDown; } catch {}
        } catch {}
      };

      dateBtn.addEventListener("click", (ev) => { try { ev.preventDefault(); ev.stopPropagation(); } catch {} openCalendar(); });
      timeBtn.addEventListener("click", (ev) => { try { ev.preventDefault(); ev.stopPropagation(); } catch {} openTime(); });

      dt.appendChild(dateBtn);
      dt.appendChild(timeBtn);

      const clockAt = makeAtPreviewClock({
        getDate: () => atDate,
        getTime: () => atTime,
        getTargetTs: () => calcAtTargetTs(),
        setTime: (t) => { atTime = String(t || atTime); try { syncPickers(); } catch {} },
      });

      const actions = document.createElement("div");
      actions.className = "wmeRcModalActions";

      const btnCancel = document.createElement("div");
      btnCancel.className = "wmeRcModalBtn";
      btnCancel.textContent = "Cancel";
      btnCancel.addEventListener("click", () => { try { if (typeof cleanupSoundPick === "function") cleanupSoundPick(); } catch {} close(); });

      const btnClear = document.createElement("div");
      btnClear.className = "wmeRcModalBtn danger";
      btnClear.textContent = "Clear";
      btnClear.addEventListener("click", () => {
        updatePin(pinId, { reminderAt: null, reminderType: null, reminderUnit: null, reminderValue: null, reminderDone: false, reminderNote: "" });
        toast("Reminder cleared");
        close();
      });

      const btnSet = document.createElement("div");
      btnSet.className = "wmeRcModalBtn primary";
      btnSet.textContent = "Set";
      btnSet.addEventListener("click", async () => {
        let when = null;

        if (mode === "IN") {
          when = calcInTargetTs();
          updatePin(pinId, { reminderAt: when, reminderType: "IN", reminderUnit: unit, reminderValue: Math.max(1, Math.round(inValue)), reminderDone: false, reminderNote: String(noteVal||"").trim() });
        } else {
          const ts = calcAtTargetTs();
          if (!ts) { toast("Pick a valid date & time"); return; }
          if (ts <= Date.now() + 5000) { toast("Pick a time in the future"); return; }
          when = ts;
          updatePin(pinId, { reminderAt: when, reminderType: "AT", reminderUnit: null, reminderValue: null, reminderDone: false, reminderNote: String(noteVal||"").trim() });
        }

        toast("Reminder set");
        await ensureNotificationPermission();
        renderPinsPanel();
        close();
      });

      actions.appendChild(btnCancel);
      actions.appendChild(btnClear);
      actions.appendChild(btnSet);

      const syncUI = () => {
        tabIn.classList.toggle("on", mode === "IN");
        tabAt.classList.toggle("on", mode === "AT");

        unitMin.classList.toggle("on", unit === "MINUTES");
        unitHr.classList.toggle("on", unit === "HOURS");

        inValue = clamp(Math.round(inValue), 1, getMaxForUnit());

        section.innerHTML = "";
        if (mode === "IN") {
          section.appendChild(unitRow);
          section.appendChild(clockIn.wrap);
          section.appendChild(quick);
          clockIn.update();
        } else {
          section.appendChild(dt);
          section.appendChild(clockAt.wrap);
          clockAt.update();
        }

        try {
          const ts = mode === "IN" ? calcInTargetTs() : calcAtTargetTs();
          if (ts) hint.textContent = `Will remind at: ${fmtPreview(ts)} • ${prettyCurrent()}`;
          else hint.textContent = prettyCurrent();
        } catch {
          hint.textContent = prettyCurrent();
        }
      };

      tabIn.addEventListener("click", () => { mode = "IN"; syncUI(); });
      tabAt.addEventListener("click", () => { mode = "AT"; syncUI(); });

      unitMin.addEventListener("click", () => { unit = "MINUTES"; syncUI(); });
      unitHr.addEventListener("click", () => { unit = "HOURS"; syncUI(); });

      body.appendChild(topRow);
      body.appendChild(section);
      body.appendChild(noteBox);
      body.appendChild(actions);

      syncUI();
    },
  });
}

async function ensureNotificationPermission() {
  try {
    if (!("Notification" in window)) return false;
    if (Notification.permission === "granted") return true;
    if (Notification.permission === "denied") return false;
    const res = await Notification.requestPermission();
    return res === "granted";
  } catch {
    return false;
  }
}

function showReminderNotice(pinsDue, opts) {
  try {
    ensureCSS();
    const _opts = (opts && typeof opts === "object") ? opts : {};
    const _titleText = typeof _opts.title === "string" && _opts.title.trim() ? _opts.title.trim() : "Reminder";
    const _silent = !!_opts.silent;
    const pinsList = Array.isArray(pinsDue) ? pinsDue.filter(Boolean) : (pinsDue ? [pinsDue] : []);
    if (!pinsList.length) return false;
    let stack = document.getElementById("wmeRcNoticeStack");
    if (!stack) {
      stack = document.createElement("div");
      stack.id = "wmeRcNoticeStack";
      stack.className = "wmeRcNoticeStack";
      (document.body || document.documentElement).appendChild(stack);
    }

    try { applyReminderNoticePosition(); } catch {}

    const createNotice = (pin) => {
      const primaryPin = pin;
      const msgText = primaryPin?.name ? String(primaryPin.name) : "Pinned place";

      const wrap = document.createElement("div");
      wrap.className = "wmeRcNotice";
      activeReminderNotices.add(wrap);

      const title = document.createElement("div");
      title.className = "wmeRcNoticeTitle";
      title.textContent = _titleText;

      const msg = document.createElement("div");
      msg.className = "wmeRcNoticeMsg";
      msg.textContent = msgText;

      const noteText = (primaryPin && typeof primaryPin.reminderNote === "string") ? primaryPin.reminderNote.trim() : "";
      const note = document.createElement("div");
      note.className = "wmeRcNoticeNote";
      if (noteText) note.textContent = noteText;

      const actions = document.createElement("div");
      actions.className = "wmeRcNoticeActions";

      const btnSnooze = document.createElement("button");
      btnSnooze.className = "wmeRcNoticeBtn";
      btnSnooze.textContent = "Snooze";

      const btnCancel = document.createElement("button");
      btnCancel.className = "wmeRcNoticeBtn";
      btnCancel.textContent = "Dismiss";

      const btnGo = document.createElement("button");
      btnGo.className = "wmeRcNoticeBtn primary";
      btnGo.textContent = "Take me there";

      try {
        const col = normalizePinColor(primaryPin && primaryPin.color ? primaryPin.color : "#ff8a00");
        const base = hexToRgb(col);
        const light = mixRgb(base, { r:255, g:255, b:255 }, 0.25);
        btnGo.style.background = `linear-gradient(180deg, ${rgbaStr(light, .32)}, ${rgbaStr(base, .28)})`;
        btnGo.style.borderColor = `${rgbaStr(base, .38)}`;
        btnGo.style.boxShadow = `0 10px 28px ${rgbaStr(base, .22)}`;
      } catch {}

      let pop = null;

      const cleanupPop = () => {
        try {
          if (pop) {
            try { window.removeEventListener("resize", pop._wmeRcPos); } catch {}
            try { document.removeEventListener("pointerdown", pop._wmeRcDown, true); } catch {}
            pop.remove();
          }
        } catch {}
        pop = null;
      };

      const maybeStopBell = () => {
        try {
          if (!activeReminderNotices || activeReminderNotices.size === 0) stopBellLoop();
        } catch {}
      };

      const close = () => {
        cleanupPop();
        try { wrap.classList.remove("show"); } catch {}
        setTimeout(() => {
          try { wrap.remove(); } catch {}
          try { activeReminderNotices.delete(wrap); } catch {}
          maybeStopBell();
        }, 220);
      };

      const snoozePin = (minutes) => {
        const mins = Math.max(1, Math.min(240, Number(minutes) || 0));
        if (!mins) return;
        try {
          const now = Date.now();
          const all = loadPins();
          let changed = false;
          for (const p of all) {
            if (!p || String(p.id) !== String(primaryPin.id)) continue;
            p.reminderAt = now + mins * 60000;
            p.reminderDone = false;
            p.reminderFiredAt = null;
            clearFiredKeysForPin(p.id);
            changed = true;
            break;
          }
          if (changed) {
            savePins(all);
            renderPinsPanel();
            try { scheduleAllReminderTimers(); } catch {}
          }
        } catch {}
        close();
      };

      const openSnoozePop = () => {
      try { if (pop) pop.remove(); } catch {}
      pop = document.createElement("div");
      pop.className = "wmeRcSnoozePop";

      try { applyPinMarkerColors(pop, (pin && pin.color) || "#ff8a00"); } catch {}
      try { const c = normalizePinColor((pin && pin.color) || "#ff8a00"); const rgb = hexToRgb(c); if (rgb) pop.style.setProperty("--pinRGB", `${rgb.r},${rgb.g},${rgb.b}`); } catch {}

      const t = document.createElement("div");
      t.className = "wmeRcSnoozeTitle";
      t.textContent = "Snooze for";

      const grid = document.createElement("div");
      grid.className = "wmeRcSnoozeGrid";

      const presets = [2,5,10,15,30];
      for (const m of presets) {
        const c = document.createElement("div");
        c.className = "wmeRcSnoozeChip";
        c.textContent = `${m}m`;
        c.addEventListener("click", (e) => {
          e.preventDefault(); e.stopPropagation();
          snoozePin(m);
        });
        grid.appendChild(c);
      }

      const row = document.createElement("div");
      row.className = "wmeRcSnoozeRow";

      const inp = document.createElement("input");
      inp.className = "wmeRcSnoozeInput";
      inp.type = "text";
      inp.inputMode = "numeric";
      inp.autocomplete = "off";
      inp.maxLength = 3;
      inp.placeholder = "Minutes…";
      inp.addEventListener("input", () => {
        try { inp.value = (inp.value || "").replace(/\D+/g, "").slice(0, 3); } catch {}
      });

      const go = document.createElement("button");
      go.className = "wmeRcSnoozeGo";
      go.textContent = "Set";


      try {
        const col = normalizePinColor((pin && pin.color) ? pin.color : "#ff8a00");
        const base = hexToRgb(col);
        const light = mixRgb(base, { r:255, g:255, b:255 }, 0.25);
        go.style.background = `linear-gradient(180deg, ${rgbaStr(light, .32)}, ${rgbaStr(base, .28)})`;
        go.style.borderColor = `${rgbaStr(base, .38)}`;
        go.style.boxShadow = `0 10px 28px ${rgbaStr(base, .22)}`;
      } catch {}
      const commit = () => {
        const v = Number(inp.value);
        if (!v || v < 1) return;
        snoozePin(v);
      };

      go.addEventListener("click", (e) => { e.preventDefault(); e.stopPropagation(); commit(); });
      inp.addEventListener("keydown", (e) => { if (e.key === "Enter") { e.preventDefault(); e.stopPropagation(); commit(); } });

      row.appendChild(inp);
      row.appendChild(go);

      pop.appendChild(t);
      pop.appendChild(grid);
      pop.appendChild(row);


      (document.body || document.documentElement).appendChild(pop);

      const positionPop = () => {
        try {
          const r = wrap.getBoundingClientRect();
          const pw = pop.offsetWidth || 280;
          const ph = pop.offsetHeight || 220;
          let left = (r.left + r.width) - pw - 12;
          left = Math.max(12, Math.min(window.innerWidth - pw - 12, left));
          let top = r.bottom + 10;
          if (top + ph > window.innerHeight - 12) top = r.top - ph - 10;
          top = Math.max(12, Math.min(window.innerHeight - ph - 12, top));
          pop.style.left = left + "px";
          pop.style.top  = top + "px";
        } catch {}
      };
      requestAnimationFrame(positionPop);
      window.addEventListener("resize", positionPop);
      try { pop._wmeRcPos = positionPop; } catch {}

      const onDocDown = (ev) => {
        try {
          const t = ev.target;
          if (!pop) return;
          if (t && (pop.contains(t) || btnSnooze.contains(t))) return;
          pop.remove();
          pop = null;
          window.removeEventListener("resize", positionPop);
          document.removeEventListener("pointerdown", onDocDown, true);
        } catch {}
      };
      document.addEventListener("pointerdown", onDocDown, true);
      try { pop._wmeRcDown = onDocDown; } catch {}


      setTimeout(() => { try { inp.focus(); } catch {} }, 0);
    };

    btnSnooze.addEventListener("click", (e) => {
      e.preventDefault(); e.stopPropagation();
      openSnoozePop();
    });

    btnGo.addEventListener("click", (e) => {
      e.preventDefault(); e.stopPropagation();
      try {
        if (primaryPin) zoomToLonLatExact(primaryPin.lon, primaryPin.lat, primaryPin.zoom);
      } catch {}
      close();
    });

    btnCancel.addEventListener("click", (e) => {
      e.preventDefault(); e.stopPropagation();
      close();
    });

      actions.appendChild(btnSnooze);
      actions.appendChild(btnCancel);
      actions.appendChild(btnGo);

      wrap.appendChild(title);
      wrap.appendChild(msg);
      if (noteText) wrap.appendChild(note);
      wrap.appendChild(actions);

      stack.appendChild(wrap);
      requestAnimationFrame(() => wrap.classList.add("show"));

      if (!_silent) startBellLoop();

      wrap.addEventListener("click", (e) => {
        if (!pop) return;
        const target = e.target;
        if (target && pop.contains(target)) return;
        try { pop.remove(); } catch {}
        pop = null;
      });
    };

    for (const p of pinsList) createNotice(p);

    return true;
  } catch {
    return false;
  }
}

function fireReminder(pin) {
  try { showReminderNotice(pin); } catch {}
}

function fireReminderBatch(pinsDue) {
  try {
    if (!Array.isArray(pinsDue) || pinsDue.length === 0) return;
    showReminderNotice(pinsDue);
  } catch {}
}



function checkRemindersNow() {
  try {
    const now = Date.now();
    const pins = loadPins();
    const due = [];

    for (const p of pins) {
      if (!p || !p.id) continue;
      const atRaw = p.reminderAt;
      if (atRaw == null) continue;
      const at = Number(atRaw);
      if (!Number.isFinite(at) || at <= 0) continue;
      if (p.reminderDone) continue;
      if (at <= now) {
        due.push(p);
      }
    }

    if (due.length) {
      for (const p of due) {
        try { triggerReminderById(String(p.id), Number(p.reminderAt)); } catch {}
      }
      return;
    }

    try { updatePinsCountdowns(); } catch {}
  } catch(e) {
    try { console.error("[WME Pins] checkRemindersNow failed", e); } catch {}
  }
}


function handleMissedRemindersOnStart(){
  try{
    const now = Date.now();
    const pins = loadPins();
    if (!Array.isArray(pins) || pins.length === 0) return;
    const missed = [];
    const GRACE_MS = 2000;
    let changed = false;
    for (const p of pins){
      if (!p || !p.id) continue;
      const atRaw = p.reminderAt;
      if (atRaw == null) continue;
      const at = Number(atRaw);
      if (!Number.isFinite(at) || at <= 0) continue;
      if (p.reminderDone) continue;
      if (at <= (now - GRACE_MS)){
        missed.push(p);
        p.reminderDone = true;
        p.reminderFiredAt = now;
        p.reminderMissed = true;
        changed = true;
        try { clearReminderTimer(p.id); } catch {}
      }
    }
    if (changed) {
      savePins(pins);
      try { renderPinsPanel(); } catch {}
    }
    if (missed.length){
      try { showReminderNotice(missed, { title: "Missed reminder", silent: true }); } catch {}
    }
  }catch{}
}

function startReminderLoop() {
  if (reminderIntervalId) return;

  if (!missedRemindersChecked) {
    missedRemindersChecked = true;
    try { handleMissedRemindersOnStart(); } catch {}
  }


  reminderIntervalId = _createAdaptiveLoop(() => { try { checkRemindersNow(); } catch {} }, 1500);

  try { checkRemindersNow(); } catch {}
  try { scheduleAllReminderTimers(); } catch {}

  if (!startReminderLoop._bound) {
    startReminderLoop._bound = true;
    try {
      window.addEventListener("focus", () => {
        try { checkRemindersNow(); scheduleAllReminderTimers(); } catch {}
      }, { passive: true });
    } catch {}
    try {
      document.addEventListener("visibilitychange", () => {
        if (!document.hidden) {
          try { checkRemindersNow(); scheduleAllReminderTimers(); } catch {}
        }
      }, { passive: true });
    } catch {}
  }
}
startReminderLoop._bound = false;

function pinThisPlace(kind, ll, segIds) {
  if (!ll || !Number.isFinite(ll.lat) || !Number.isFinite(ll.lon)) {
    toast("Pin: missing coordinates");
    return;
  }

  ensurePinsPanel();

  const pins = loadPins();

  let defaultName = `Pinned ${fmt(ll.lat)}, ${fmt(ll.lon)}`;
  try {
    if (kind === "segment" && Array.isArray(segIds) && segIds.length) {
      const sc = getStreetAndCityForSegmentId(segIds[0]);
      defaultName = `${sc.street}, ${sc.city}`;
      if (segIds.length > 1) defaultName += ` (+${segIds.length - 1})`;
    }
  } catch {}

  const zoom = getCurrentZoomBestEffort();

  openModal({
    title: "Pin this place",
    iconSvg: ICONS.mapPin,
    bodyBuilder: ({ body, close, modal }) => {
      const nameInp = document.createElement("input");
      nameInp.className = "wmeRcInput";
      const nextNum = getNextPinNumber(pins);
      const fallbackName = `Pin #${nextNum}`;

      nameInp.placeholder = fallbackName;
      nameInp.value = "";


const colorRow = document.createElement("div");
colorRow.className = "wmeRcColorRow";
let chosenColor = pickRandomPinColor();

let customColors = loadCustomPinColors();
let editCustomIndex = null;

const swatches = [];
function setColor(hex){
  chosenColor = normalizePinColor(hex);
  for (const s of swatches) s.classList.toggle("sel", s.dataset.c === chosenColor);
  try { colorRow.style.setProperty("--pinC", chosenColor); } catch {}
}


let swatchEditTipEl = null;
let swatchEditTipHideT = null;
let swatchEditAnchor = null;
let swatchEditIndex = null;

function ensureSwatchEditTip(){
  if (swatchEditTipEl) return;
  swatchEditTipEl = document.createElement("div");
  swatchEditTipEl.className = "wmeRcSwatchEditTip";
  swatchEditTipEl.style.display = "none";
  swatchEditTipEl.innerHTML = `
    <button type="button" class="wmeRcSwatchEditTipBtn" data-act="edit" aria-label="Edit color">
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
        <path d="M12 20h9"/>
        <path d="M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4 12.5-12.5z"/>
      </svg>
    </button>
    <button type="button" class="wmeRcSwatchEditTipBtn" data-act="del" aria-label="Remove color">
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
        <path d="M3 6h18"/>
        <path d="M8 6V4h8v2"/>
        <path d="M19 6l-1 14H6L5 6"/>
        <path d="M10 11v6"/>
        <path d="M14 11v6"/>
      </svg>
    </button>
  `;
  swatchEditTipEl.addEventListener("pointerenter", () => {
    if (swatchEditTipHideT){ clearTimeout(swatchEditTipHideT); swatchEditTipHideT = null; }
  });
  swatchEditTipEl.addEventListener("pointerleave", () => hideSwatchEditTip());
  swatchEditTipEl.addEventListener("click", (e) => {
    e.preventDefault(); e.stopPropagation();
    const btn = e.target && e.target.closest ? e.target.closest(".wmeRcSwatchEditTipBtn") : null;
    const act = btn && btn.dataset ? btn.dataset.act : null;
    if (!act) return;

    if (!swatchEditAnchor || !Number.isFinite(swatchEditIndex)) return;
    const arr = loadCustomPinColors();

    if (act === "edit"){
      const c = arr[swatchEditIndex];
      if (!c) return;
      openPop({ initial: c, editIndex: swatchEditIndex, anchorEl: swatchEditAnchor });
      return;
    }

    if (act === "del"){
      if (swatchEditIndex < 0 || swatchEditIndex >= arr.length) return;
      arr.splice(swatchEditIndex, 1);
      saveCustomPinColors(arr);
      customColors = loadCustomPinColors();
      renderCustomSwatches();
      hideSwatchEditTip(true);
    }
  });
  document.body.appendChild(swatchEditTipEl);
}


function showSwatchEditTip(anchorEl, idx){
  ensureSwatchEditTip();
  swatchEditAnchor = anchorEl;
  swatchEditIndex = idx;
  if (swatchEditTipHideT){ clearTimeout(swatchEditTipHideT); swatchEditTipHideT = null; }

  const r = anchorEl.getBoundingClientRect();
  const vw = window.innerWidth || document.documentElement.clientWidth || 9999;

  swatchEditTipEl.style.display = "inline-flex";
  swatchEditTipEl.style.visibility = "hidden";
  swatchEditTipEl.style.left = "0px";
  swatchEditTipEl.style.top = "0px";

  const tipW = Math.max(1, swatchEditTipEl.getBoundingClientRect().width || swatchEditTipEl.offsetWidth || 1);
  let left = r.left + (r.width / 2) - (tipW / 2);
  left = Math.max(8, Math.min(vw - tipW - 8, left));
  const top = r.bottom + 6;

  swatchEditTipEl.style.left = left + "px";
  swatchEditTipEl.style.top = top + "px";
  swatchEditTipEl.style.visibility = "visible";
}


function hideSwatchEditTip(immediate){
  if (!swatchEditTipEl) return;
  const doHide = () => { try{ swatchEditTipEl.style.display = "none"; }catch{} };
  if (immediate) return doHide();
  if (swatchEditTipHideT) clearTimeout(swatchEditTipHideT);
  swatchEditTipHideT = setTimeout(doHide, 220);
}


function addSwatch(c, { custom=false, index=null } = {}){
  const sw = document.createElement("div");
  sw.className = "wmeRcColorSwatch" + (custom ? " custom" : "");
  sw.style.setProperty("--c", c);
  sw.dataset.c = normalizePinColor(c);
  if (custom) sw.dataset.customIndex = String(index);
  sw.title = c;
  sw.addEventListener("click", (e) => {
    e.preventDefault(); e.stopPropagation();
    setColor(c);
    if (custom) {
      editCustomIndex = index;
      hideSwatchEditTip(true);
    }
  });
  if (custom) {
    sw.addEventListener("pointerenter", () => showSwatchEditTip(sw, index));
    sw.addEventListener("pointerleave", () => hideSwatchEditTip());
  }
  swatches.push(sw);
  colorRow.appendChild(sw);
}

for (const c of PIN_COLOR_PRESETS) addSwatch(c);

function renderCustomSwatches(){
  try{
    const oldSep = colorRow.querySelector(".wmeRcColorSep");
    if (oldSep) oldSep.remove();
  }catch{}
  for (let i = swatches.length - 1; i >= 0; i--){
    const s = swatches[i];
    if (s && s.dataset && s.dataset.customIndex != null) {
      try { s.remove(); } catch {}
      swatches.splice(i, 1);
    }
  }
  const afterEl = colorRow.querySelectorAll(".wmeRcColorSwatch").item(PIN_COLOR_PRESETS.length - 1);
  if (customColors && customColors.length){
    const sep = document.createElement("div");
    sep.className = "wmeRcColorSep";
    sep.textContent = "|";
    colorRow.insertBefore(sep, plusBtn);
  }
  for (let i = 0; i < customColors.length; i++){
    const c = customColors[i];
    const sw = document.createElement("div");
    sw.className = "wmeRcColorSwatch custom";
    sw.style.setProperty("--c", c);
    sw.dataset.c = normalizePinColor(c);
    sw.dataset.customIndex = String(i);
    sw.title = c;
    sw.addEventListener("click", (e) => { e.preventDefault(); e.stopPropagation(); setColor(c); });
    sw.addEventListener("pointerenter", () => showSwatchEditTip(sw, i));
    sw.addEventListener("pointerleave", () => hideSwatchEditTip());
    colorRow.insertBefore(sw, plusBtn);
    swatches.push(sw);
  }
  for (const s of swatches) s.classList.toggle("sel", s.dataset.c === chosenColor);
}

const plusBtn = document.createElement("div");
plusBtn.className = "wmeRcColorPlus";
plusBtn.textContent = "+";
plusBtn.title = "Custom color";
plusBtn.addEventListener("click", (e) => {
  e.preventDefault(); e.stopPropagation();
  editCustomIndex = null;
  openCustomColorPop({ initial: chosenColor, editIndex: null });
});
colorRow.appendChild(plusBtn);

let colorPop = null;
function parseAnyColor(s){
  try{
    const str = String(s || "").trim();
    if (!str) return null;
    if (str.startsWith("#")) return normalizePinColor(str);
    const m = str.match(/^rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})(?:\s*,\s*([0-9.]+)\s*)?\)$/i);
    if (m){
      const r = Math.max(0, Math.min(255, Number(m[1])));
      const g = Math.max(0, Math.min(255, Number(m[2])));
      const b = Math.max(0, Math.min(255, Number(m[3])));
      return normalizePinColor(`#${((1<<24)+(r<<16)+(g<<8)+b).toString(16).slice(1)}`);
    }
    return null;
  }catch{ return null; }
}
function hexToRgb(hex){
  try{
    const h = normalizePinColor(hex);
    const x = h.replace("#","");
    const r = parseInt(x.slice(0,2),16);
    const g = parseInt(x.slice(2,4),16);
    const b = parseInt(x.slice(4,6),16);
    return { r,g,b };
  }catch{ return { r:0,g:0,b:0 }; }
}
function positionColorPop(anchorEl){
  try{
    const r = anchorEl.getBoundingClientRect();
    const vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
    const vh = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0);
    const w = 286;
    let left = r.right + 10;
    if (left + w > vw - 10) left = r.left - w - 10;
    left = Math.min(vw - w - 10, Math.max(10, left));
    let top = Math.min(vh - 10 - 220, r.bottom + 8);
    if (top < 10) top = 10;
    colorPop.style.left = left + "px";
    colorPop.style.top = top + "px";
  }catch{}
}
function closeColorPop(){
  try { if (colorPop) colorPop.remove(); } catch {}
  colorPop = null;
  try { document.removeEventListener("pointerdown", onPopOutside, true); } catch {}
}
function onPopOutside(e){
  try{
    if (!colorPop) return;
    if (colorPop.contains(e.target)) return;
    if (plusBtn.contains(e.target)) return;
    closeColorPop();
  }catch{}
}
function openCustomColorPop({ initial, editIndex, anchorEl }){
  closeColorPop();

  colorPop = document.createElement("div");
  colorPop.className = "wmeRcColorPop wmeRcColorPopAdv";
  colorPop.tabIndex = -1;

  let cur = normalizePinColor(initial || chosenColor);
  const isEditingPreset = Number.isFinite(editIndex) && editIndex != null;

  const clamp01 = (v) => Math.max(0, Math.min(1, v));
  const clamp255 = (v) => Math.max(0, Math.min(255, v|0));
  const hexToRgb = (hex) => {
    const h = String(hex||"").trim().replace(/^#/,"");
    if (!/^[0-9a-fA-F]{6}$/.test(h)) return null;
    return { r: parseInt(h.slice(0,2),16), g: parseInt(h.slice(2,4),16), b: parseInt(h.slice(4,6),16) };
  };
  const rgbToHex = (r,g,b) => "#" + [r,g,b].map(v => clamp255(v).toString(16).padStart(2,"0")).join("").toUpperCase();
  const rgbToHsv = (r,g,b) => {
    r/=255; g/=255; b/=255;
    const max = Math.max(r,g,b), min = Math.min(r,g,b);
    const d = max - min;
    let h = 0;
    if (d !== 0){
      if (max === r) h = ((g-b)/d) % 6;
      else if (max === g) h = (b-r)/d + 2;
      else h = (r-g)/d + 4;
      h *= 60;
      if (h < 0) h += 360;
    }
    const s = max === 0 ? 0 : d / max;
    const v = max;
    return { h, s, v };
  };
  const hsvToRgb = (h,s,v) => {
    h = ((h%360)+360)%360;
    s = clamp01(s); v = clamp01(v);
    const c = v * s;
    const x = c * (1 - Math.abs(((h/60)%2) - 1));
    const m = v - c;
    let rp=0,gp=0,bp=0;
    if (h < 60){ rp=c; gp=x; bp=0; }
    else if (h < 120){ rp=x; gp=c; bp=0; }
    else if (h < 180){ rp=0; gp=c; bp=x; }
    else if (h < 240){ rp=0; gp=x; bp=c; }
    else if (h < 300){ rp=x; gp=0; bp=c; }
    else { rp=c; gp=0; bp=x; }
    return { r: Math.round((rp+m)*255), g: Math.round((gp+m)*255), b: Math.round((bp+m)*255) };
  };
  const rgbToHsl = (r,g,b) => {
    r/=255; g/=255; b/=255;
    const max = Math.max(r,g,b), min = Math.min(r,g,b);
    let h=0,s=0;
    const l = (max+min)/2;
    const d = max-min;
    if (d !== 0){
      s = d / (1 - Math.abs(2*l - 1));
      if (max === r) h = ((g-b)/d) % 6;
      else if (max === g) h = (b-r)/d + 2;
      else h = (r-g)/d + 4;
      h *= 60; if (h < 0) h += 360;
    }
    return { h, s, l };
  };

  const topRow = document.createElement("div");
  topRow.className = "wmeRcColorTopRow";

  const sw = document.createElement("div");
  sw.className = "wmeRcColorSwatchBig";
  sw.style.background = cur;

  const stats = document.createElement('div');
  stats.className = 'wmeRcColorStats';

  const stat1 = document.createElement('div');
  stat1.className = 'wmeRcColorStatLine';
  stat1.innerHTML = `<span><span class="wmeRcColorStatKey">HEX</span><span class="wmeRcColorStatVal" data-k="hex">#000000</span></span>
                     <span><span class="wmeRcColorStatKey">RGB</span><span class="wmeRcColorStatVal" data-k="rgb">0, 0, 0</span></span>`;

  const stat2 = document.createElement('div');
  stat2.className = 'wmeRcColorStatLine';
  stat2.innerHTML = `<span><span class="wmeRcColorStatKey">HSL</span><span class="wmeRcColorStatVal" data-k="hsl">0, 0%, 0%</span></span>`;

  stats.appendChild(stat1);
  stats.appendChild(stat2);

  topRow.appendChild(sw);
  topRow.appendChild(stats);

  const pickerRow = document.createElement("div");
  pickerRow.className = "wmeRcPickerRow";

  const sv = document.createElement("div");
  sv.className = "wmeRcPickerSV";
  const svDot = document.createElement("div");
  svDot.className = "wmeRcPickerSVDot";
  sv.appendChild(svDot);

  const hue = document.createElement("div");
  hue.className = "wmeRcPickerHue";
  const hueThumb = document.createElement("div");
  hueThumb.className = "wmeRcPickerHueThumb";
  hue.appendChild(hueThumb);

  const fields = document.createElement("div");
  fields.className = "wmeRcPickerFields";

  const hexRow = document.createElement("div");
  hexRow.className = "wmeRcPickerFieldRow";
  const hexLbl = document.createElement("div");
  hexLbl.className = "wmeRcPickerFieldLbl";
  hexLbl.textContent = "HEX";
  const hexInp = document.createElement("input");
  hexInp.className = "wmeRcPickerField";
  hexInp.inputMode = "text";
  hexInp.autocomplete = "off";
  hexInp.spellcheck = false;
  hexInp.value = cur;
  hexRow.appendChild(hexLbl);
  hexRow.appendChild(hexInp);

  const rRow = document.createElement("div");
  rRow.className = "wmeRcPickerFieldRow";
  const rLbl = document.createElement("div");
  rLbl.className = "wmeRcPickerFieldLbl";
  rLbl.textContent = "R";
  const rInp = document.createElement("input");
  rInp.className = "wmeRcPickerField";
  rInp.inputMode = "numeric";
  rInp.autocomplete = "off";
  rInp.spellcheck = false;
  rRow.appendChild(rLbl);
  rRow.appendChild(rInp);

  const gRow = document.createElement("div");
  gRow.className = "wmeRcPickerFieldRow";
  const gLbl = document.createElement("div");
  gLbl.className = "wmeRcPickerFieldLbl";
  gLbl.textContent = "G";
  const gInp = document.createElement("input");
  gInp.className = "wmeRcPickerField";
  gInp.inputMode = "numeric";
  gInp.autocomplete = "off";
  gInp.spellcheck = false;
  gRow.appendChild(gLbl);
  gRow.appendChild(gInp);

  const bRow = document.createElement("div");
  bRow.className = "wmeRcPickerFieldRow";
  const bLbl = document.createElement("div");
  bLbl.className = "wmeRcPickerFieldLbl";
  bLbl.textContent = "B";
  const bInp = document.createElement("input");
  bInp.className = "wmeRcPickerField";
  bInp.inputMode = "numeric";
  bInp.autocomplete = "off";
  bInp.spellcheck = false;
  bRow.appendChild(bLbl);
  bRow.appendChild(bInp);

  fields.appendChild(hexRow);
  fields.appendChild(rRow);
  fields.appendChild(gRow);
  fields.appendChild(bRow);

  pickerRow.appendChild(sv);
  pickerRow.appendChild(hue);
  pickerRow.appendChild(fields);

  const actions = document.createElement("div");
  actions.className = "wmeRcColorActions";

  const savePresetBtn = document.createElement("button");
  savePresetBtn.type = "button";
  savePresetBtn.className = "wmeRcColorSavePreset";
  savePresetBtn.textContent = isEditingPreset ? "Save" : "Save preset";

  const delBtn = document.createElement("button");
  delBtn.type = "button";
  delBtn.className = "wmeRcColorDelete";
  delBtn.textContent = "Delete";

  const doneBtn = document.createElement("button");
  doneBtn.type = "button";
  doneBtn.className = "wmeRcColorDone";
  doneBtn.textContent = "Done";

  if (isEditingPreset){
    actions.appendChild(delBtn);
    actions.appendChild(savePresetBtn);
  } else {
    actions.appendChild(savePresetBtn);
  }
  actions.appendChild(doneBtn);

  colorPop.appendChild(topRow);
  colorPop.appendChild(pickerRow);
  colorPop.appendChild(actions);

  let hsv;
  const seedRgb = hexToRgb(cur) || { r: 0, g: 122, b: 255 };
  hsv = rgbToHsv(seedRgb.r, seedRgb.g, seedRgb.b);

  function renderSV(){
    const base = hsvToRgb(hsv.h, 1, 1);
    sv.style.background = `linear-gradient(to top, rgba(0,0,0,1), rgba(0,0,0,0)), linear-gradient(to right, rgba(255,255,255,1), rgba(255,255,255,0)), rgb(${base.r},${base.g},${base.b})`;
    svDot.style.left = (hsv.s * 100) + "%";
    svDot.style.top = ((1 - hsv.v) * 100) + "%";
    hueThumb.style.top = (clamp01(hsv.h / 360) * 100) + "%";
  }

  function renderStats(){
    const rgb = hsvToRgb(hsv.h, hsv.s, hsv.v);
    const hex = rgbToHex(rgb.r, rgb.g, rgb.b);
    cur = normalizePinColor(hex);
    sw.style.background = cur;
    hexInp.value = cur;
    rInp.value = String(rgb.r);
    gInp.value = String(rgb.g);
    bInp.value = String(rgb.b);

    try{
      sv.style.boxShadow = `inset 0 1px 0 rgba(255,255,255,.06), 0 18px 34px rgba(0,0,0,.28)`;
      hue.style.boxShadow = `inset 0 1px 0 rgba(255,255,255,.06), 0 18px 34px rgba(0,0,0,.28)`;
    }catch{}
        try{
      const br = (rgb.r*299 + rgb.g*587 + rgb.b*114) / 1000;
      doneBtn.style.background = cur;
      doneBtn.style.color = (br > 160) ? 'rgba(0,0,0,.82)' : '#fff';
    }catch{}

    const hexEl = colorPop.querySelector('[data-k="hex"]');
    const rgbEl = colorPop.querySelector('[data-k="rgb"]');
    const hslEl = colorPop.querySelector('[data-k="hsl"]');
    if (hexEl) hexEl.textContent = cur.toUpperCase();
    if (rgbEl) rgbEl.textContent = `${rgb.r}, ${rgb.g}, ${rgb.b}`;
    const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b);
    if (hslEl) hslEl.textContent = `${Math.round(hsl.h)}, ${Math.round(hsl.s*100)}%, ${Math.round(hsl.l*100)}%`;
}

  function applyHex(hex){
    const rgb = hexToRgb(hex);
    if (!rgb) return;
    hsv = rgbToHsv(rgb.r, rgb.g, rgb.b);
    renderSV();
    renderStats();
  }

  function applyRgb(r,g,b){
    r = clamp255(r); g = clamp255(g); b = clamp255(b);
    hsv = rgbToHsv(r,g,b);
    renderSV();
    renderStats();
  }

  function pointToSV(e){
    const r = sv.getBoundingClientRect();
    const x = clamp01((e.clientX - r.left) / Math.max(1, r.width));
    const y = clamp01((e.clientY - r.top) / Math.max(1, r.height));
    hsv.s = x;
    hsv.v = 1 - y;
    renderSV();
    renderStats();
  }

  function pointToHue(e){
    const r = hue.getBoundingClientRect();
    const y = clamp01((e.clientY - r.top) / Math.max(1, r.height));
    hsv.h = y * 360;
    renderSV();
    renderStats();
  }

  function bindDrag(el, onMove){
    const onDown = (e) => {
      e.preventDefault();
      try { el.setPointerCapture(e.pointerId); } catch {}
      onMove(e);
      const move = (ev) => onMove(ev);
      const up = () => {
        document.removeEventListener("pointermove", move, true);
        document.removeEventListener("pointerup", up, true);
      };
      document.addEventListener("pointermove", move, true);
      document.addEventListener("pointerup", up, true);
    };
    el.addEventListener("pointerdown", onDown);
  }

  bindDrag(sv, pointToSV);
  bindDrag(hue, pointToHue);

  hexInp.addEventListener("input", () => {
    const v = hexInp.value.trim();
    if (/^#?[0-9a-fA-F]{6}$/.test(v)) applyHex(v.startsWith("#")?v:("#"+v));
  });

  const rgbInputHandler = () => {
    const rv = parseInt(rInp.value, 10);
    const gv = parseInt(gInp.value, 10);
    const bv = parseInt(bInp.value, 10);
    if (!Number.isFinite(rv) || !Number.isFinite(gv) || !Number.isFinite(bv)) return;
    applyRgb(rv, gv, bv);
  };
  rInp.addEventListener("input", rgbInputHandler);
  gInp.addEventListener("input", rgbInputHandler);
  bInp.addEventListener("input", rgbInputHandler);

  savePresetBtn.addEventListener("click", (e) => {
    e.preventDefault(); e.stopPropagation();
    const norm = normalizePinColor(cur);
    let arr = loadCustomPinColors();


    const already = arr.findIndex(c => normalizePinColor(c) === norm);
    if (already !== -1 && !(Number.isFinite(editIndex) && editIndex != null && already === editIndex)) {
      try { toast("That preset already exists."); } catch {}
      return;
    }
if (isEditingPreset){
      if (editIndex >= 0 && editIndex < arr.length){
        arr[editIndex] = norm;
        saveCustomPinColors(arr);
        customColors = loadCustomPinColors();
        renderCustomSwatches();
      }
      setColor(norm);
      closeColorPop();
      return;
    }

    if (arr.length >= 4){
      arr[arr.length - 1] = norm;
    } else {
      arr.push(norm);
    }
    saveCustomPinColors(arr);
    customColors = loadCustomPinColors();
    renderCustomSwatches();
    setColor(norm);
    closeColorPop();
  });

  delBtn.addEventListener("click", (e) => {
    e.preventDefault(); e.stopPropagation();
    if (!isEditingPreset) return;
    let arr = loadCustomPinColors();
    if (editIndex >= 0 && editIndex < arr.length){
      arr.splice(editIndex, 1);
      saveCustomPinColors(arr);
      customColors = loadCustomPinColors();
      renderCustomSwatches();
    }
    closeColorPop();
  });

  doneBtn.addEventListener("click", (e) => {
    e.preventDefault(); e.stopPropagation();
    setColor(cur);
    closeColorPop();
  });

  renderSV();
  renderStats();

  document.body.appendChild(colorPop);
  positionColorPop(anchorEl || plusBtn);
  document.addEventListener("pointerdown", onPopOutside, true);
  syncMeta();
}

renderCustomSwatches();
setColor(chosenColor);

      const groups = loadPinGroups();
      let chosenGroupId = getLastGroup();

      const groupLbl = document.createElement("div");
      groupLbl.className = "wmeRcHint";
      groupLbl.textContent = "Folder";

      const groupPick = document.createElement("div");
      groupPick.className = "wmeRcSoundPick";
      groupPick.tabIndex = 0;

      const groupBtn = document.createElement("div");
      groupBtn.className = "wmeRcSoundBtn";

      const groupBtnLabel = document.createElement("div");
      groupBtnLabel.className = "wmeRcSoundBtnLabel";

      const groupCaret = document.createElement("div");
      groupCaret.className = "wmeRcSoundCaret";
      groupCaret.innerHTML = ICONS.chevDown;

      groupBtn.appendChild(groupBtnLabel);
      groupBtn.appendChild(groupCaret);
      groupPick.appendChild(groupBtn);

      const groupMenu = document.createElement("div");
      groupMenu.className = "wmeRcSoundMenu wmeRcSoundMenuPortal";
      groupMenu.style.display = "none";
      try { document.body.appendChild(groupMenu); } catch {}

      const getGroupLabel = (id) => {
        try {
          const gg = loadPinGroups().find(x => x && x.id === id);
          return gg ? (gg.name || "(no folder)") : "(no folder)";
        } catch { return "(no folder)"; }
      };

      const positionGroupMenu = () => {
        try {
          const r = groupBtn.getBoundingClientRect();
          const vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
          const vh = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0);
          const w = Math.max(260, Math.min(r.width || 320, vw - 16));
          let left = Math.max(8, Math.min(r.left, vw - w - 8));
          let top = (r.bottom || 0) + 6;
          const maxH = 240;
          if (top + maxH + 8 > vh) top = Math.max(8, (r.top || 0) - 6 - maxH);
          groupMenu.style.left = left + "px";
          groupMenu.style.top = top + "px";
          groupMenu.style.width = w + "px";
          const avail = Math.max(120, vh - top - 10);
          groupMenu.style.maxHeight = Math.min(maxH, avail) + "px";
        } catch {}
      };

      const rebuildGroupMenu = () => {
        groupMenu.innerHTML = "";
        const gs = loadPinGroups();
        for (const g of gs) {
          const item = document.createElement("div");
          item.className = "wmeRcSoundItem";
          item.textContent = g.name;
          item.dataset.groupId = g.id;
          item.addEventListener("click", (e) => {
            e.preventDefault(); e.stopPropagation();
            chosenGroupId = normalizeGroupId(g.id);
            groupBtnLabel.textContent = getGroupLabel(chosenGroupId);
            try { setLastGroup(chosenGroupId); } catch {}
            toggleGroupMenu(false);
          });
          groupMenu.appendChild(item);
        }
        const newItem = document.createElement("div");
        newItem.className = "wmeRcSoundItem";
        newItem.textContent = "+ New Folder";
        newItem.dataset.groupId = "__new__";
        newItem.addEventListener("click", (e) => {
          e.preventDefault(); e.stopPropagation();
          toggleGroupMenu(false);
          openGroupNameModal({
            title: "New folder",
            placeholder: "Name",
            okText: "Create",
            onCancel: () => {},
            onSubmit: (nm, emoji) => {
              const id = createPinGroup(nm, emoji);
              chosenGroupId = id;
              try { setLastGroup(chosenGroupId); } catch {}
              groupBtnLabel.textContent = getGroupLabel(chosenGroupId);
              rebuildGroupMenu();
            }
          });
        });
        groupMenu.appendChild(newItem);
      };

      const toggleGroupMenu = (open) => {
        const isOpen = groupPick.getAttribute("data-open") === "1";
        const next = (typeof open === "boolean") ? open : !isOpen;
        groupPick.setAttribute("data-open", next ? "1" : "0");
        try {
          if (next) {
            positionGroupMenu();
            groupMenu.style.display = "block";
          } else {
            groupMenu.style.display = "none";
          }
        } catch {}
      };

      groupBtn.addEventListener("click", (e) => {
        e.preventDefault(); e.stopPropagation();
        toggleGroupMenu();
      });

      const onDocDown = (e) => {
        try {
          if (!groupPick.contains(e.target) && !groupMenu.contains(e.target)) {
            groupPick.setAttribute("data-open", "0");
            groupMenu.style.display = "none";
          }
        } catch {}
      };
      document.addEventListener("pointerdown", onDocDown, true);
      try { window.addEventListener("resize", positionGroupMenu, true); } catch {}
      try { window.addEventListener("scroll", positionGroupMenu, true); } catch {}

      const cleanupGroupPick = () => {
        try { document.removeEventListener("pointerdown", onDocDown, true); } catch {}
        try { window.removeEventListener("resize", positionGroupMenu, true); } catch {}
        try { window.removeEventListener("scroll", positionGroupMenu, true); } catch {}
        try { groupMenu.remove(); } catch {}
      };

      try {
        const mo = new MutationObserver(() => {
          try {
            if (!document.body.contains(modal)) {
              cleanupGroupPick();
              mo.disconnect();
            }
          } catch {}
        });
        mo.observe(document.body, { childList: true });
      } catch {}

      rebuildGroupMenu();
      groupBtnLabel.textContent = getGroupLabel(chosenGroupId);


      setColor(chosenColor);

      const remNowWrap = document.createElement("div");
      remNowWrap.className = "wmeRcHint";
      remNowWrap.innerHTML = `
        <label class="wmeRcInlineToggle">
          <input type="checkbox" class="wmeRcRemNowChk">
          <span class="wmeRcSwitchTrack"><span class="wmeRcSwitchThumb"></span></span>
          <span class="wmeRcSwitchLabel">Set a reminder</span>
        </label>
      `;
      const remNowChk = remNowWrap.querySelector("input");
      const actions = document.createElement("div");
      actions.className = "wmeRcModalActions";

      const btnCancel = document.createElement("div");
      btnCancel.className = "wmeRcModalBtn";
      btnCancel.textContent = "Cancel";
      btnCancel.addEventListener("click", () => { try { if (typeof cleanupSoundPick === "function") cleanupSoundPick(); } catch {} close(); });

      const btnPin = document.createElement("div");
      btnPin.className = "wmeRcModalBtn primary";
      btnPin.textContent = "Pin";
      btnPin.addEventListener("click", async () => {
        const v = validatePinName(nameInp.value, fallbackName);
        if (!v.ok) { toast(v.msg); try { nameInp.focus(); nameInp.select(); } catch {} return; }
        const name = v.value;
        const reminderAt = null;

        const id = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
        const newPin = { id, name, color: chosenColor, groupId: chosenGroupId, lon: Number(ll.lon), lat: Number(ll.lat), zoom: Number.isFinite(zoom) ? zoom : null, createdAt: Date.now(), reminderAt, reminderDone: false, reminderType: null, reminderUnit: null, reminderValue: null , reminderNote: "" };

        const cur = loadPins();
        cur.push(newPin);
        savePins(cur);
        try { setLastGroup(chosenGroupId); } catch {}
        renderPinsPanel();
        startReminderLoop();

        toast("Pinned place saved");
        closeAllMenus();
        close();
        if (remNowChk && remNowChk.checked) {
          setTimeout(() => { try { openReminderModal(id); } catch {} }, 60);
        }
      });

      actions.appendChild(btnCancel);
      actions.appendChild(btnPin);

      const nameLimitMsg = attachMaxLen(nameInp, 32);
      body.appendChild(nameLimitMsg ? nameLimitMsg.wrap : nameInp);
      if (nameLimitMsg && nameLimitMsg.msg) body.appendChild(nameLimitMsg.msg);
      body.appendChild(colorRow);
      body.appendChild(groupLbl);
      body.appendChild(groupPick);
      body.appendChild(remNowWrap);
      body.appendChild(actions);
      setTimeout(() => { try { nameInp.focus(); nameInp.select(); } catch {} }, 60);
    },
  });
}


  function clearSubCloseTimer() {
    if (menuState.subCloseTimer) {
      clearTimeout(menuState.subCloseTimer);
      menuState.subCloseTimer = null;
    }
  }

  function closeSubMenu2() {
    try { if (menuState.sub2AnchorRow) menuState.sub2AnchorRow.classList.remove("submenuOpen"); } catch {}
    menuState.sub2AnchorRow = null;
    if (menuState.sub2) menuState.sub2.remove();
    menuState.sub2 = null;
    menuState.sub2Context = null;
  }

  function closeSubMenu() {
    closeSubMenu2();
    try { if (menuState.subAnchorRow) menuState.subAnchorRow.classList.remove("submenuOpen"); } catch {}
    menuState.subAnchorRow = null;
    if (menuState.sub) menuState.sub.remove();
    menuState.sub = null;
    menuState.subContext = null;
  }

  function detachOutsideHandlers() {
    if (menuState.outsideCloseHandler) {
      document.removeEventListener("mousedown", menuState.outsideCloseHandler, true);
      document.removeEventListener("touchstart", menuState.outsideCloseHandler, true);
      document.removeEventListener("contextmenu", menuState.outsideCloseHandler, true);
      menuState.outsideCloseHandler = null;
    }
    if (menuState.escHandler) {
      document.removeEventListener("keydown", menuState.escHandler, true);
      menuState.escHandler = null;
    }
  }

  function closeAllMenus() {
    clearSubCloseTimer();
    closeSubMenu();

    if (menuState.root) menuState.root.remove();
    menuState.sub2 = null;
    menuState.sub = null;
    menuState.root = null;
    menuState.sub2Context = null;
    menuState.subContext = null;
    menuState.selectionSnapshot = null;
    detachOutsideHandlers();
  }

  function scheduleCloseSub(delay = 160, mode = "auto") {
    clearSubCloseTimer();
    menuState.subCloseTimer = setTimeout(() => {
      if (mode === "sub2") {
        closeSubMenu2();
        return;
      }
      if (mode === "all") {
        closeSubMenu2();
        closeSubMenu();
        return;
      }
      if (menuState.sub2) closeSubMenu2();
      else closeSubMenu();
    }, delay);
  }

  function positionMenu(menu, x, y) {
    (document.body || document.documentElement).appendChild(menu);
    const r = menu.getBoundingClientRect();
    const pad = 8;
    let px = x, py = y;
    if (px + r.width + pad > innerWidth) px = Math.max(pad, innerWidth - r.width - pad);
    if (py + r.height + pad > innerHeight) py = Math.max(pad, innerHeight - r.height - pad);
    menu.style.left = `${px}px`;
    menu.style.top = `${py}px`;
  }

  function updateRootRowSub(kind, newSubText) {
    try {
      const root = menuState.root;
      if (!root) return;
      const row = root.querySelector(`.wmeRcItem[data-kind="${CSS.escape(kind)}"]`);
      if (!row) return;
      const sub = row.querySelector(".wmeRcSubText");
      if (!sub) return;
      sub.textContent = String(newSubText ?? "");
    } catch {}
  }

  function refreshActiveSubmenuIf(kind) {
    try {
      const ctx = menuState.subContext;
      if (!ctx || ctx.kind !== kind || !menuState.sub) return;
      const items = ctx.getItems ? ctx.getItems() : [];
      openSubMenuForRow(ctx.anchorEl, items, { kind: ctx.kind, getItems: ctx.getItems, keepContext: true });
    } catch (e) {
      console.error(e);
    }
  }

  function buildSpeedGrid(spec) {
    const wrap = document.createElement("div");
    wrap.className = "wmeRcSpeedWrap";

    const title = document.createElement("div");
    title.className = "wmeRcSpeedTitle";

    const left = document.createElement("div");
    left.style.display = "flex";
    left.style.gap = "8px";
    left.style.alignItems = "center";
    left.innerHTML = `<span class="wmeRcMuted">${spec.titleLeft || "Speed"}</span>`;

    const right = document.createElement("div");
    right.className = "wmeRcMuted";
    right.textContent = spec.titleRight || "";

    title.appendChild(left);
    title.appendChild(right);
    wrap.appendChild(title);

    if (spec.chipsEl) {
      const chipsWrap = document.createElement("div");
      chipsWrap.style.margin = "8px 0 8px 0";
      chipsWrap.appendChild(spec.chipsEl);
      wrap.appendChild(chipsWrap);
    }

    const grid = document.createElement("div");
    grid.className = "wmeRcSpeedGrid";

    for (const btn of (spec.buttons || [])) {
      const b = document.createElement("div");
      b.className = "wmeRcSpeedBtn" + (btn.selected ? " sel" : "");
      b.title = btn.title || "";
      if (btn.html) b.innerHTML = btn.html;
      else b.textContent = btn.text;

      b.addEventListener("click", async (e) => {
        e.preventDefault();
        e.stopPropagation();
        try { await btn.onClick(); }
        catch (err) { console.error(err); toast(String(err?.message || err)); }
      });

      grid.appendChild(b);
    }

    wrap.appendChild(grid);
    return wrap;
  }

  function buildMenuElement(spec) {
    ensureCSS();
    const menu = document.createElement("div");
    menu.className = "wmeRcMenu" + (spec.isSub ? " sub" : "");
    menu.setAttribute("role", "menu");

    menu.addEventListener("mousedown", (e) => { e.stopPropagation(); }, false);
    menu.addEventListener("click", (e) => { e.stopPropagation(); }, false);
    menu.addEventListener("contextmenu", (e) => { e.preventDefault(); e.stopPropagation(); }, true);

    if (spec.headerLeft) {
      const hdr = document.createElement("div");
      hdr.className = "wmeRcHdr";
      hdr.innerHTML = `
        <div class="wmeRcHdrLeft">
          <div class="wmeRcHdrTitle">${spec.headerLeft}</div>
        </div>
        <div class="wmeRcMuted">${spec.headerRight}</div>
      `;
      menu.appendChild(hdr);
    }

    for (const it of spec.items) {
      if (it.type === "speedGrid") { menu.appendChild(buildSpeedGrid(it)); continue; }
      if (it.type === "sep") { const sep = document.createElement("div"); sep.className = "wmeRcSep"; menu.appendChild(sep); continue; }

      const row = document.createElement("div");
      row.className = "wmeRcItem" + (it.disabled ? " disabled" : "") + (it.selected ? " selected" : "");
      if (it.kind === "section") row.className += " sectionHdr";
      row.setAttribute("role", "menuitem");
      if (it.kind) row.dataset.kind = String(it.kind);

      const left = document.createElement("div");
      left.className = "wmeRcLeft";
      left.innerHTML = `<div>${it.label}</div>`;

      const right = document.createElement("div");
      right.style.display = "flex";
      right.style.gap = "8px";
      right.style.alignItems = "center";

      if (it.iconButton && typeof it.iconButton.onClick === "function") {
        const b = document.createElement("div");
        b.className = "wmeRcMiniBtn wmeRcMiniIcon";
        b.title = it.iconButton.title || "";
        b.innerHTML = it.iconButton.html || it.iconButton.label || "⚙";
        b.addEventListener("click", async (e) => {
          e.preventDefault();
          e.stopPropagation();
          try { await it.iconButton.onClick(); }
          catch (err) { console.error(err); toast(String(err?.message || err)); }
        });
        right.appendChild(b);
      }

      if (it.rightButton && typeof it.rightButton.onClick === "function") {
        const b = document.createElement("div");
        b.className = "wmeRcMiniBtn";
        b.innerHTML = it.rightButton.html || it.rightButton.label || "Open";
        b.addEventListener("click", async (e) => {
          e.preventDefault();
          e.stopPropagation();
          try { await it.rightButton.onClick(); }
          catch (err) { console.error(err); toast(String(err?.message || err)); }
        });
        right.appendChild(b);
      } else if (it.check) {
        right.insertAdjacentHTML("beforeend", `<span class="wmeRcCheck">✓</span>`);
      } else if (it.kbd) {
        right.insertAdjacentHTML("beforeend", `<span class="wmeRcKbd">${it.kbd}</span>`);
      } else if (it.submenu) {
        right.insertAdjacentHTML("beforeend", `<span class="wmeRcChevron">›</span>`);
      }

      row.appendChild(left);
      row.appendChild(right);

      if (!it.disabled && typeof it.onClick === "function" && !it.submenu) {
        row.addEventListener("click", async (e) => {
          e.preventDefault(); e.stopPropagation();
          try { await it.onClick(); }
          catch (err) { console.error(err); toast(String(err?.message || err)); }
        });
      }

      if (!it.disabled && it.submenu && typeof it.getSubmenuItems === "function") {
        row.addEventListener("mouseenter", () => {
          if (menuState.subCloseTimer) { clearTimeout(menuState.subCloseTimer); menuState.subCloseTimer = null; }
          let subItems = [];
          try { subItems = it.getSubmenuItems() || []; } catch (e) { console.error(e); }
          openSubMenuForRow(row, subItems, { kind: it.submenuKind || "generic", getItems: it.getSubmenuItems, headerLeft: it.submenuHeaderLeft || "", headerRight: it.submenuHeaderRight || "" });
        });
        row.addEventListener("mouseleave", () => scheduleCloseSub(180));
      }

      menu.appendChild(row);
    }

    if (spec.headerLeft) {
      menu.addEventListener("mouseleave", () => scheduleCloseSub(180, "all"));
      menu.addEventListener("mouseenter", () => {
        if (menuState.subCloseTimer) { clearTimeout(menuState.subCloseTimer); menuState.subCloseTimer = null; }
      });
    }

    return menu;
  }

  function attachOutsideCloseHandlers() {
    const handler = (e) => {
      const root = menuState.root;
      const sub = menuState.sub;
      const sub2 = menuState.sub2;
      const t = e.target;
      if (root && (root === t || root.contains(t))) return;
      if (sub && (sub === t || sub.contains(t))) return;
      if (sub2 && (sub2 === t || sub2.contains(t))) return;

      const snap = menuState.selectionSnapshot;

      let cx = e.clientX, cy = e.clientY;
      if (e.touches && e.touches[0]) { cx = e.touches[0].clientX; cy = e.touches[0].clientY; }

      const isLeftMouse = (e.type === "mousedown") && (e.button === 0);
      const isLeftTouch = (e.type === "touchstart");
      const isLeft = isLeftMouse || isLeftTouch;

      const mapClick = isLeft && Number.isFinite(cx) && Number.isFinite(cy) && isMapClick(cx, cy);
      const hasSnap = Array.isArray(snap) && snap.length;
      const noMods = !(e.shiftKey || e.ctrlKey || e.metaKey || e.altKey);

      const shouldKeepSelection = mapClick && hasSnap && noMods;

      if (shouldKeepSelection) {
        try { e.preventDefault(); } catch {}
        try { e.stopImmediatePropagation(); } catch {}
        try { e.stopPropagation(); } catch {}
      }

      closeAllMenus();

      if (shouldKeepSelection) {
        requestAnimationFrame(() => {
          requestAnimationFrame(() => {
            try { setSelectionToSegmentIdsSilent(snap); } catch {}
          });
        });
      }
    };

    menuState.outsideCloseHandler = handler;
    document.addEventListener("mousedown", handler, true);
    document.addEventListener("contextmenu", handler, true);
    document.addEventListener("touchstart", handler, { capture: true, passive: false });
}


  function openRootMenu(x, y, headerLeft, headerRight, items) {
    closeAllMenus();
    menuState.selectionSnapshot = selectedSegmentIds();
    const root = buildMenuElement({ headerLeft, headerRight, items, isSub: false });
    menuState.root = root;
    positionMenu(root, x, y);
    attachOutsideCloseHandlers();
  }

  function openSubMenuForRow(rowEl, items, opts = {}) {
    const keepCtx = !!opts.keepContext;
    const prevCtx1 = menuState.subContext;
    const prevCtx2 = menuState.sub2Context;

    const parentMenu = rowEl?.closest?.('.wmeRcMenu');
    const fromRoot = !!(parentMenu && menuState.root && parentMenu === menuState.root);

    if (fromRoot) {
      closeSubMenu();
    } else {
      closeSubMenu2();
    }

    if (!items || !items.length) return;

    try { rowEl.classList.add("submenuOpen"); } catch {}

    const sub = buildMenuElement({ headerLeft: opts.headerLeft || "", headerRight: opts.headerRight || "", items, isSub: true });

    if (fromRoot) menuState.subAnchorRow = rowEl;
    else menuState.sub2AnchorRow = rowEl;

    if (fromRoot) menuState.sub = sub;
    else menuState.sub2 = sub;

    const rr = rowEl.getBoundingClientRect();
    const margin = 8;
    let x = rr.right + margin;
    let y = rr.top - 8;

    sub.style.left = "0px";
    sub.style.top = "0px";
    (document.body || document.documentElement).appendChild(sub);
    const sr = sub.getBoundingClientRect();
    sub.remove();

    if (x + sr.width + 8 > innerWidth) x = Math.max(8, rr.left - margin - sr.width);
    if (y + sr.height + 8 > innerHeight) y = Math.max(8, innerHeight - sr.height - 8);

    positionMenu(sub, x, y);

    sub.addEventListener("mouseenter", () => {
      clearSubCloseTimer();
    });

    sub.addEventListener("mouseleave", () => {
      scheduleCloseSub(180, fromRoot ? "auto" : "sub2");
    });

    if (keepCtx) {
      if (fromRoot && prevCtx1) menuState.subContext = prevCtx1;
      else if (!fromRoot && prevCtx2) menuState.sub2Context = prevCtx2;
    } else {
      const ctx = {
        kind: String(opts.kind || "generic"),
        anchorEl: rowEl,
        getItems: typeof opts.getItems === "function" ? opts.getItems : () => items,
      };
      if (fromRoot) menuState.subContext = ctx;
      else menuState.sub2Context = ctx;
    }
  }

  function openModal(spec) {
    ensureCSS();
    closeAllMenus();

    const backdrop = document.createElement("div");
    backdrop.className = "wmeRcModalBackdrop";

    const modal = document.createElement("div");
    modal.className = "wmeRcModal";

    const header = document.createElement("div");
    header.className = "wmeRcModalHdr";
    header.innerHTML = `
      <div class="wmeRcModalTitle"><span class="wmeRcI">${spec.iconSvg || ICONS.gear}</span><span>${spec.title || ""}</span></div>
    `;

    const body = document.createElement("div");
    body.className = "wmeRcModalBody";

    modal.appendChild(header);
    modal.appendChild(body);

    let _onKey = null;

    const close = () => {
      try { if (_onKey) document.removeEventListener("keydown", _onKey, true); } catch {}
      backdrop.classList.remove("show");
      modal.classList.remove("show");
      setTimeout(() => {
        backdrop.remove();
        modal.remove();
      }, 180);
    };
    backdrop.addEventListener("click", (ev) => { try { ev.preventDefault(); ev.stopPropagation(); } catch {} close(); });
    (document.body || document.documentElement).appendChild(backdrop);
    (document.body || document.documentElement).appendChild(modal);

    requestAnimationFrame(() => {
      requestAnimationFrame(() => {
        backdrop.classList.add("show");
        modal.classList.add("show");
      });
    });

    if (typeof spec.bodyBuilder === "function") spec.bodyBuilder({ body, close, modal });

    _onKey = (ev) => {
      try {
        if (!ev) return;
        if (ev.key === "Escape") {
          ev.preventDefault();
          ev.stopPropagation();
          close();
          return;
        }
        if (ev.key !== "Enter") return;
        if (ev.shiftKey || ev.altKey || ev.ctrlKey || ev.metaKey) return;

        const t = ev.target;
        const tag = t && t.tagName ? String(t.tagName).toLowerCase() : "";
        if (tag === "textarea") return;

        let targetBtn = modal.querySelector(".wmeRcModalBtn.primary");
        if (!targetBtn) {
          const btns = Array.from(modal.querySelectorAll(".wmeRcModalBtn"));
          targetBtn = btns[btns.length - 1] || null;
        }
        if (!targetBtn) return;
        ev.preventDefault();
        ev.stopPropagation();
        targetBtn.click();
      } catch {}
    };
    try { document.addEventListener("keydown", _onKey, true); } catch {}

    return { close };
  }

  function openChangelogModal() {
    try { ensureCSS(); } catch {}
    try {
      localStorage.setItem(CHANGELOG_SEEN_KEY, SCRIPT_VERSION);
    } catch {}

    openModal({
      iconSvg: ICONS.tools,
      title: `WME - RightClick Functions — v${SCRIPT_VERSION}`,
      bodyBuilder: ({ body, close }) => {
        const wrap = document.createElement("div");
        wrap.style.display = "flex";
        wrap.style.flexDirection = "column";
        wrap.style.gap = "10px";

        const hint = document.createElement("div");
        hint.className = "wmeRcHint";
        hint.textContent = "Changes in this version:";
        wrap.appendChild(hint);

        const ul = document.createElement("ul");
        ul.style.margin = "0 0 0 18px";
        ul.style.padding = "0";
        ul.style.display = "flex";
        ul.style.flexDirection = "column";
        ul.style.gap = "6px";
        for (const it of (CHANGELOG_ITEMS || [])) {
          const li = document.createElement("li");
          li.textContent = String(it);
          ul.appendChild(li);
        }
        wrap.appendChild(ul);

        const actions = document.createElement("div");
        actions.className = "wmeRcModalActions";

        const ok = document.createElement("div");
        ok.className = "wmeRcModalBtn primary";
        ok.textContent = "OK";
        ok.addEventListener("click", () => close());
        actions.appendChild(ok);

        wrap.appendChild(actions);
        body.appendChild(wrap);
      },
    });
  }

  function maybeShowChangelogOnce() {
    let seen = null;
    try { seen = localStorage.getItem(CHANGELOG_SEEN_KEY); } catch {}
    if (String(seen || "") === String(SCRIPT_VERSION)) return;
    openChangelogModal();
  }


  function openGroupNameModal(opts) {
    try {
      const title = (opts && opts.title) ? String(opts.title) : "New folder";
      const initial = (opts && opts.initial != null) ? String(opts.initial) : "";
      const okText = (opts && opts.okText) ? String(opts.okText) : "Create";
      const cancelText = (opts && opts.cancelText) ? String(opts.cancelText) : "Cancel";
      const placeholder = (opts && opts.placeholder) ? String(opts.placeholder) : "Name";
      const onSubmit = (opts && typeof opts.onSubmit === "function") ? opts.onSubmit : null;
      const onCancel = (opts && typeof opts.onCancel === "function") ? opts.onCancel : null;

      openModal({
        title,
        iconSvg: ICONS.folder,
        bodyBuilder: ({ body, close }) => {
          const inp = document.createElement("input");
          inp.className = "wmeRcInput";
          inp.placeholder = placeholder;
          inp.value = initial;
          const groupNameLimitMsg = attachMaxLen(inp, 32);
          const rowTop = document.createElement("div");
          rowTop.className = "wmeRcRow wmeRcEmojiRow";
          rowTop.style.alignItems = "stretch";

          const emojiBtn = document.createElement("button");
          emojiBtn.type = "button";
          emojiBtn.className = "wmeRcEmojiBtn";
          emojiBtn.title = "Pick emoji";
          let selectedEmoji = (opts && typeof opts.initialEmoji === "string") ? String(opts.initialEmoji) : "";
          emojiBtn.innerHTML = selectedEmoji ? selectedEmoji : "<span class='wmeRcNoEmojiIcon' aria-label='No emoji'>🙂</span>";

          const emojiPop = document.createElement("div");
          emojiPop.className = "wmeRcEmojiPop hidden";
          const emojiGrid = document.createElement("div");
          emojiGrid.className = "wmeRcEmojiGrid";
          const EMOJIS = ["📍","📌","🗺️","🛣️","🛤️","🚧","⛔","🚫","🛑","⚠️","🚦","🚥","📷","🅿️","🚏","🚸","🌉","🌊","🏞️","✈️","🛫","🛬"];
          const setEmoji = (em) => {
            try { selectedEmoji = String(em || ""); } catch { selectedEmoji = ""; }
            try { emojiBtn.innerHTML = selectedEmoji ? selectedEmoji : "<span class='wmeRcNoEmojiIcon' aria-label='No emoji'>🙂</span>"; } catch {}
            try { emojiPop.classList.add("hidden"); } catch {}
          };

          try {
            const bNone = document.createElement("button");
            bNone.type = "button";
            bNone.className = "wmeRcEmojiItem wmeRcEmojiItemNone";
            bNone.innerHTML = "<span class='wmeRcNoEmojiIcon wmeRcNoEmojiIconSm' aria-label='No emoji'>🙂</span>";
            bNone.addEventListener("click", (ev) => { try { ev.preventDefault(); ev.stopPropagation(); } catch {} setEmoji(""); });
            emojiGrid.appendChild(bNone);
          } catch {}

          EMOJIS.forEach((em) => {
            const b = document.createElement("button");
            b.type = "button";
            b.className = "wmeRcEmojiItem";
            b.textContent = em;
            b.addEventListener("click", (ev) => { try { ev.preventDefault(); ev.stopPropagation(); } catch {} setEmoji(em); });
            emojiGrid.appendChild(b);
          });
          emojiPop.appendChild(emojiGrid);

          emojiBtn.addEventListener("click", (ev) => {
            try { ev.preventDefault(); ev.stopPropagation(); } catch {}
            emojiPop.classList.toggle("hidden");
          });

          setTimeout(() => {
            const closePop = (ev) => {
              try {
                if (!emojiPop.contains(ev.target) && ev.target !== emojiBtn) emojiPop.classList.add("hidden");
              } catch {}
            };
            document.addEventListener("mousedown", closePop, true);
            document.addEventListener("keydown", (ev) => { if (ev.key === "Escape") emojiPop.classList.add("hidden"); }, true);
          }, 0);

          rowTop.appendChild(emojiBtn);
          rowTop.appendChild(groupNameLimitMsg ? groupNameLimitMsg.wrap : inp);
          body.appendChild(rowTop);
          if (groupNameLimitMsg && groupNameLimitMsg.msg) body.appendChild(groupNameLimitMsg.msg);
          body.appendChild(emojiPop);

          const hint = document.createElement("div");
          hint.className = "wmeRcHint";
          hint.textContent = "Emojis and symbols are allowed.";

          const actions = document.createElement("div");
          actions.className = "wmeRcModalActions";

          const btnCancel = document.createElement("div");
          btnCancel.className = "wmeRcModalBtn";
          btnCancel.textContent = cancelText;
          btnCancel.addEventListener("click", () => {
            try { onCancel && onCancel(); } catch {}
            close();
          });

          const btnOk = document.createElement("div");
          btnOk.className = "wmeRcModalBtn primary";
          btnOk.textContent = okText;

          const apply = () => {
            const nm = String(inp.value || "").trim();
            if (!nm) return;
            try { onSubmit && onSubmit(nm, selectedEmoji); } catch {}
            close();
          };

          btnOk.addEventListener("click", apply);
          inp.addEventListener("keydown", (e) => {
            if (e.key === "Enter") { e.preventDefault(); apply(); }
            if (e.key === "Escape") { e.preventDefault(); try { onCancel && onCancel(); } catch {}; close(); }
          });

          actions.appendChild(btnCancel);
          actions.appendChild(btnOk);

          body.appendChild(hint);
          body.appendChild(actions);

          setTimeout(() => { try { inp.focus(); inp.select(); } catch {} }, 50);
        }
      });
    } catch(e) {
      try { console.warn("[WME Pins] openGroupNameModal failed", e); } catch {}
      try { opts && typeof opts.onCancel === "function" && opts.onCancel(); } catch {}
    }
  }





function openClearDefaultFolderPinsModal(opts) {
  try {
    const folderName = String(opts?.folderName || "(no folder)");
    const count = Number(opts?.count || 0);
    const onConfirm = (typeof opts?.onConfirm === "function") ? opts.onConfirm : null;
    if (!count) return;

    openModal({
      title: "Clear pins",
      iconSvg: ICONS.trash,
      bodyBuilder: ({ body, close }) => {
        const p = document.createElement("div");
        p.className = "wmeRcHint";
        p.style.opacity = ".92";
        p.textContent = `Clear ${count} pin${count === 1 ? "" : "s"} from "${folderName}"?`;

        const p2 = document.createElement("div");
        p2.className = "wmeRcHint";
        p2.style.opacity = ".75";
        p2.style.marginTop = "6px";
        p2.textContent = "This cannot be undone.";

        const actions = document.createElement("div");
        actions.className = "wmeRcModalActions";

        const btnCancel = document.createElement("div");
        btnCancel.className = "wmeRcModalBtn";
        btnCancel.textContent = "Cancel";
        btnCancel.addEventListener("click", () => close());

        const btnClear = document.createElement("div");
        btnClear.className = "wmeRcModalBtn danger";
        btnClear.textContent = "Clear";
        btnClear.addEventListener("click", () => {
          try { onConfirm && onConfirm(); } catch {}
          close();
        });

        actions.appendChild(btnCancel);
        actions.appendChild(btnClear);

        body.appendChild(p);
        body.appendChild(p2);
        body.appendChild(actions);
      }
    });
  } catch {}
}

function openRemoveFolderModal(gid) {
  try {
    gid = String(gid || "");
    if (!gid || gid === "default") return;

    const g = loadPinGroups().find(x => x && x.id === gid) || { name: "Folder" };
    openModal({
      title: "Remove folder",
      iconSvg: ICONS.trash,
      bodyBuilder: ({ body, close }) => {
        const p = document.createElement("div");
        p.className = "wmeRcHint";
        p.style.opacity = ".92";
        p.textContent = `What do you want to do with "${g.name || "Folder"}"?`;

        const actions = document.createElement("div");
        actions.className = "wmeRcModalActions";

        const btnCancel = document.createElement("div");
        btnCancel.className = "wmeRcModalBtn";
        btnCancel.textContent = "Cancel";
        btnCancel.addEventListener("click", () => close());

        const btnMove = document.createElement("div");
        btnMove.className = "wmeRcModalBtn";
        btnMove.textContent = "Delete folder";
        btnMove.title = "Keeps pins and moves them to (no folder)";
        btnMove.addEventListener("click", () => {
          const ps = loadPins().map(pn => (normalizeGroupId(pn.groupId) === gid) ? ({ ...pn, groupId: "default" }) : pn);
          savePins(ps);
          const gs = loadPinGroups().filter(x => x && x.id !== gid);
          savePinGroups(gs);
          try { if (getCurrentGroupFilter() === gid) setCurrentGroupFilter("all"); } catch {}
          close();
          renderPinsPanel();
        });

        const btnDeleteAll = document.createElement("div");
        btnDeleteAll.className = "wmeRcModalBtn danger";
        btnDeleteAll.textContent = "Delete folder + pins";
        btnDeleteAll.title = "Deletes folder and all pins inside";
        btnDeleteAll.addEventListener("click", () => {
          const ps = loadPins().filter(pn => normalizeGroupId(pn.groupId) !== gid);
          savePins(ps);
          const gs = loadPinGroups().filter(x => x && x.id !== gid);
          savePinGroups(gs);
          try { if (getCurrentGroupFilter() === gid) setCurrentGroupFilter("all"); } catch {}
          close();
          renderPinsPanel();
        });

        actions.appendChild(btnCancel);
        actions.appendChild(btnMove);
        actions.appendChild(btnDeleteAll);

        body.appendChild(p);
        body.appendChild(actions);
      }
    });
  } catch {}
}

function isMapClick(clientX, clientY) {
    try {
      const mapEl = getMapContainerEl();
      if (!mapEl) return false;

      const r = mapEl.getBoundingClientRect();
      if (clientX < r.left || clientX > r.right || clientY < r.top || clientY > r.bottom) return false;


      const stack = (document.elementsFromPoint ? document.elementsFromPoint(clientX, clientY) : [document.elementFromPoint(clientX, clientY)]).filter(Boolean);

      for (const el of stack.slice(0, 12)) {
        if (!el || !el.closest) continue;
        if (isInUpdateRequestPanel(el)) return false;


        if (el.closest(".wmeRcMenu, .wmeRcModal, .wmeRcPinsPanel")) return false;
        if (el.closest("wz-card, [role='dialog'], [role='menu'], .menu, .dropdown, .panel, .sidebar, .tooltip")) return false;
      }


      for (const el of stack) {
        if (!el) continue;
        if (el.tagName === "CANVAS") return true;
        if (el.closest && el.closest("canvas")) return true;
        if (el === mapEl || (el.closest && el.closest("#map, #WazeMap, .olMap, .wme-map"))) return true;
      }
    } catch {}
    return false;
  }

  function isSegmentObjectType(v) {
    if (typeof v === "string") return /segment/i.test(v);
    const OT = sdk?.Editing?.ObjectType;
    if (typeof v === "number" && OT && OT.SEGMENT === v) return true;
    return /segment/i.test(String(v));
  }

  function extractSegmentIdsFromSelection(sel) {
    if (Array.isArray(sel)) {
      return sel
        .filter((o) => isSegmentObjectType(o?.objectType) || /segment/i.test(o?.localizedTypeName || ""))
        .map((o) => Number(o?.objectId ?? o?.id))
        .filter((n) => Number.isFinite(n));
    }
    if (sel && typeof sel === "object") {
      const type = sel.objectType ?? sel.type ?? sel.selectedObjectType;
      const ids = sel.ids ?? sel.objectIds ?? sel.selectedIds;
      if (isSegmentObjectType(type) && Array.isArray(ids)) {
        return ids.map((x) => Number(x)).filter((n) => Number.isFinite(n));
      }
      if (Array.isArray(sel.selection)) return extractSegmentIdsFromSelection(sel.selection);
      if (Array.isArray(sel.selectedItems)) return extractSegmentIdsFromSelection(sel.selectedItems);
      if (Array.isArray(sel.objects)) return extractSegmentIdsFromSelection(sel.objects);
    }
    return [];
  }

  function selectedSegmentIds() {
    try {
      const raw = sdk?.Editing?.getSelection?.();
      const ids = extractSegmentIdsFromSelection(raw);
      if (ids.length) return Array.from(new Set(ids));
    } catch {}

    try {
      const sm = UW?.W?.selectionManager;
      const items = sm?.getSelectedFeatures?.() || sm?.getSelectedItems?.() || [];
      if (Array.isArray(items)) {
        const segIds = items
          .map((x) => Number(x?.attributes?.id ?? x?.model?.attributes?.id ?? x?.id))
          .filter((n) => Number.isFinite(n));
        if (segIds.length) return Array.from(new Set(segIds));
      }
    } catch {}

    return [];
  }


  function _isPlaceObject(sel) {
    try {
      if (!sel) return false;
      if (Array.isArray(sel)) return sel.some(_isPlaceObject);
      if (typeof sel !== "object") return false;

      const t = String(
        sel.objectType ??
        sel.type ??
        sel.selectedObjectType ??
        sel.localizedTypeName ??
        sel.localizedType ??
        sel.name ??
        ""
      );


      if (/venue|place|poi|point\s*of\s*interest/i.test(t)) return true;


      if ("venueId" in sel || "placeId" in sel || "poiId" in sel || "venueID" in sel || "placeID" in sel) return true;


      try {
        const OT = sdk?.Editing?.ObjectType;
        if (typeof sel.objectType === "number" && OT) {
          const cand = [OT.VENUE, OT.PLACE, OT.POI, OT.POINT_OF_INTEREST].filter((v) => v != null);
          if (cand.includes(sel.objectType)) return true;
        }
      } catch {}


      if (Array.isArray(sel.selection)) return _isPlaceObject(sel.selection);
      if (Array.isArray(sel.selectedItems)) return _isPlaceObject(sel.selectedItems);
      if (Array.isArray(sel.objects)) return _isPlaceObject(sel.objects);
      if (Array.isArray(sel.items)) return _isPlaceObject(sel.items);

      return false;
    } catch {
      return false;
    }
  }

  function _extractPlaceIds(sel) {
    try {
      if (!sel) return [];
      if (Array.isArray(sel)) {
        return sel
          .filter((o) => _isPlaceObject(o))
          .map((o) => Number(o?.objectId ?? o?.id))
          .filter((n) => Number.isFinite(n));
      }
      if (sel && typeof sel === "object") {
        const type = sel.objectType ?? sel.type ?? sel.selectedObjectType;
        const ids = sel.ids ?? sel.objectIds ?? sel.selectedIds;
        if (_isPlaceObject({ objectType: type, localizedTypeName: sel.localizedTypeName, type: sel.type, selectedObjectType: sel.selectedObjectType }) && Array.isArray(ids)) {
          return ids.map((x) => Number(x)).filter((n) => Number.isFinite(n));
        }
        if (Array.isArray(sel.selection)) return _extractPlaceIds(sel.selection);
        if (Array.isArray(sel.selectedItems)) return _extractPlaceIds(sel.selectedItems);
        if (Array.isArray(sel.objects)) return _extractPlaceIds(sel.objects);
        if (Array.isArray(sel.items)) return _extractPlaceIds(sel.items);
      }
    } catch {}
    return [];
  }

  function selectedPlaceInfo() {

    try {
      const raw = sdk?.Editing?.getSelection?.();
      const ids = _extractPlaceIds(raw);
      if (ids.length) return { has: true, ids: Array.from(new Set(ids)) };
      if (_isPlaceObject(raw)) return { has: true, ids: [] };
    } catch {}


    try {
      const sm = UW?.W?.selectionManager;
      const items = sm?.getSelectedFeatures?.() || sm?.getSelectedItems?.() || [];
      if (Array.isArray(items) && items.length) {
        for (const it of items) {
          const a = it?.attributes || it?.model?.attributes || it?.data || null;
          const hint = String(a?.type ?? a?.featureType ?? a?.objectType ?? it?.type ?? it?.name ?? "");
          if (/venue|place|poi/i.test(hint)) return { has: true, ids: [] };
        }
      }
    } catch {}

    return { has: false, ids: [] };
  }



  function isUpdateRequestSelection(sel) {
    try {
      if (!sel) return false;
      if (Array.isArray(sel)) return sel.some(isUpdateRequestSelection);
      if (typeof sel !== "object") return false;

      const t = String(
        sel.objectType ??
        sel.type ??
        sel.selectedObjectType ??
        sel.localizedTypeName ??
        sel.localizedType ??
        sel.name ??
        ""
      );

      if (/update\s*request|updaterequest|\bur\b/i.test(t)) return true;


      if (/issue|problem/i.test(t) && /update|request|\bur\b/i.test(t)) return true;


      if ("updateRequestId" in sel || "urId" in sel || "issueId" in sel || "problemId" in sel) return true;


      try {
        const OT = sdk?.Editing?.ObjectType;
        if (typeof sel.objectType === "number" && OT) {
          const cand = [OT.UPDATE_REQUEST, OT.UR, OT.MAP_PROBLEM, OT.ISSUE].filter((v) => v != null);
          if (cand.includes(sel.objectType)) return true;
        }
      } catch {}

      if (Array.isArray(sel.selection) && sel.selection.some(isUpdateRequestSelection)) return true;
      if (Array.isArray(sel.selectedItems) && sel.selectedItems.some(isUpdateRequestSelection)) return true;
      if (Array.isArray(sel.objects) && sel.objects.some(isUpdateRequestSelection)) return true;
    } catch {}
    return false;
  }

    function isUpdateRequestPanelOpen() {



    try {
      const isVisible = (el) => {
        try {
          if (!el) return false;
          const r = el.getBoundingClientRect?.();
          if (!r) return true;
          return r.width > 0 && r.height > 0;
        } catch { return true; }
      };




      try {
        const urHost = document.querySelector(
          "wz-card.mapUpdateRequest, wz-card[class*='mapUpdateRequest'], wz-card[aria-label*='Update request'], wz-card[aria-label*='Αίτημα ενημέρωσης'], wz-card[aria-label*='Αιτημα ενημερωσης']"
        );
        if (urHost && isVisible(urHost)) return true;
      } catch {}

      const findBtn = (re) => {
        try {
          const nodes = document.querySelectorAll("button, [role='button']");
          const limit = Math.min(nodes.length, 250);
          for (let i = 0; i < limit; i++) {
            const t = (nodes[i].textContent || "").trim();
            if (t && re.test(t)) return nodes[i];
          }
        } catch {}
        return null;
      };


      const btnSolved = findBtn(/^\s*mark\s+as\s+solved\s*$/i) || findBtn(/^\s*σήμανση\s+ως\s+λυμ(έ|ε)νο\s*$/i);
      if (btnSolved && isVisible(btnSolved)) return true;

      const btnNotId = findBtn(/^\s*mark\s+as\s+not\s+identified\s*$/i) || findBtn(/^\s*σήμανση\s+ως\s+μη\s+ταυτοποιημ(έ|ε)νο\s*$/i);
      if (btnNotId && isVisible(btnNotId)) return true;


      try {
        const heads = document.querySelectorAll(
          "#sidepanel [role='heading'], #sidebar [role='heading'], [class*='sidepanel'] [role='heading'], [class*='sidebar'] [role='heading'], " +
          "#sidepanel h1, #sidepanel h2, #sidepanel h3, #sidebar h1, #sidebar h2, #sidebar h3"
        );
        const limit = Math.min(heads.length, 120);
        for (let i = 0; i < limit; i++) {
          const el = heads[i];
          if (!isVisible(el)) continue;
          const t = (el.textContent || "").trim();
          if (!t) continue;
          if (/^update\s*request(s)?$/i.test(t)) return true;
          if (/^αίτημα\s+ενημέρωσης$/i.test(t) || /^αιτημα\s+ενημερωσης$/i.test(t)) return true;
        }
      } catch {}


      try {
        const roots = [
          document.querySelector("#sidepanel"),
          document.querySelector("#sidebar"),
          document.querySelector("[class*='sidepanel']"),
          document.querySelector("[class*='sidebar']"),
        ].filter(Boolean);

        for (const root of roots) {
          if (!isVisible(root)) continue;
          const txt = (root.innerText || "").slice(0, 5000);
          if (!txt) continue;
          if (/update\s*request|αίτημα\s+ενημέρωσης|αιτημα\s+ενημερωσης/i.test(txt)) return true;
          if (/mark\s+as\s+solved|mark\s+as\s+not\s+identified/i.test(txt)) return true;
        }
      } catch {}
    } catch {}
    return false;
  }

  function isUrMarkerAtPoint(x, y) {
    try {


      const stack = (document.elementsFromPoint ? document.elementsFromPoint(x, y) : [document.elementFromPoint(x, y)]).filter(Boolean);
      if (!stack || !stack.length) return false;

      const urTextRe = /update\s*request|updaterequest|map[_\s-]?problem|problem\s*report|issue\s*report|user\s*report|\bur\b/i;
      const markerHintRe = /marker|pin|icon|badge|bubble|olmarker|olalphaimg|overlay|layer|feature/i;

      const hasUrSignals = (node) => {
        if (!node || node.nodeType !== 1) return false;


        try {
          const ds = node.dataset || {};
          if (ds.problemId || ds.issueId || ds.reportId || ds.updateRequestId || ds.urId) return true;
          if (node.hasAttribute && (node.hasAttribute("data-problem-id") || node.hasAttribute("data-issue-id") || node.hasAttribute("data-report-id") || node.hasAttribute("data-update-request-id"))) return true;
        } catch {}

        const tag = (node.tagName || "").toUpperCase();


        if (tag === "IMG") {
          const src = String(node.getAttribute("src") || "").toLowerCase();
          const alt = String(node.getAttribute("alt") || "").toLowerCase();
          if (src && (urTextRe.test(src) || /problem|issue|report|ur/.test(src))) return true;
          if (alt && (urTextRe.test(alt) || /problem|issue|report|ur/.test(alt))) return true;
        }


        if (tag === "SVG" || tag === "PATH" || tag === "G") {
          const aria = String((node.getAttribute && (node.getAttribute("aria-label") || node.getAttribute("title") || "")) || "").toLowerCase();
          if (aria && (urTextRe.test(aria) || /problem|issue|report|ur/.test(aria))) return true;
        }


        const aria = String((node.getAttribute && (node.getAttribute("aria-label") || node.getAttribute("title") || node.getAttribute("role") || "")) || "").toLowerCase();
        const cls = (typeof node.className === "string" ? node.className : "") || "";
        const id = node.id || "";
        const combo = (aria + " " + cls + " " + id).toLowerCase();


        if (combo) {
          if (urTextRe.test(combo) && (markerHintRe.test(combo) || tag === "IMG" || tag === "SVG")) return true;
          if (/problem|issue|report/.test(combo) && (markerHintRe.test(combo) || tag === "IMG" || tag === "SVG")) return true;
        }


        try {
          const bg = (node.style && node.style.backgroundImage) ? String(node.style.backgroundImage).toLowerCase() : "";
          if (bg && (urTextRe.test(bg) || /problem|issue|report|ur/.test(bg))) return true;
        } catch {}

        return false;
      };


      for (const el of stack.slice(0, 20)) {
        let cur = el;
        for (let i = 0; cur && i < 8; i++) {
          if (hasUrSignals(cur)) return true;
          cur = cur.parentElement;
        }
      }
    } catch {}
    return false;
  }


  function isInUpdateRequestPanel(target) {
    try {
      if (!target || !target.closest) return false;


      const host = target.closest("wz-card.mapUpdateRequest, .mapUpdateRequest");
      if (host) return true;


      const host2 = target.closest(
        "wz-card[aria-label*='Update request'], wz-card[aria-label*='Update Request'], wz-card[aria-label*='Αίτημα'], " +
        "[data-testid*='update'], [data-testid*='problem'], [data-testid*='mapUpdateRequest']"
      );
      if (host2) return true;
    } catch {}
    return false;
  }

  function shouldAllowNativeContextMenu(e) {
    try {

      if (e && e.shiftKey) return true;


      if (e && isInUpdateRequestPanel(e.target)) return true;


      if (e && isUrMarkerAtPoint(e.clientX, e.clientY)) return true;



      try {
        const sel = (() => { try { return sdk?.Editing?.getSelection?.(); } catch { return null; } })();
        if (isUpdateRequestSelection(sel) && e) {
          const el = document.elementFromPoint(e.clientX, e.clientY);
          const hint = ((el?.className || "") + " " + (el?.id || "")).toLowerCase();
          if (/marker|pin|icon|olmarker|olalphaimg/.test(hint)) return true;
        }
      } catch {}
    } catch {}
    return false;
  }


  function fmt(n) { return Number(n).toFixed(6); }

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

  async function setClipboard(text) {
    try { if (typeof GM_setClipboard === "function") { GM_setClipboard(text); return true; } } catch {}
    try { await navigator.clipboard.writeText(text); return true; } catch {}
    try {
      const ta = document.createElement("textarea");
      ta.value = text;
      ta.style.position = "fixed";
      ta.style.left = "-9999px";
      document.body.appendChild(ta);
      ta.select();
      const ok = document.execCommand("copy");
      ta.remove();
      return ok;
    } catch {}
    return false;
  }

  function detectRankBase() {
    if (rankIsZeroBased !== null) return;
    rankIsZeroBased = !!(UW?.W?.loginManager?.user?.getRank);
  }

  function normalizeToLevel(raw) {
    if (!Number.isFinite(raw)) return null;
    detectRankBase();
    if (raw === 0) return 1;
    if (rankIsZeroBased && raw >= 0 && raw <= 10) return raw + 1;
    return raw;
  }

  function readLevelFromW() {
    try {
      const u = UW?.W?.loginManager?.user;
      if (typeof u?.getRank === "function") {
        const r = u.getRank();
        if (Number.isFinite(r)) return r + 1;
      }
      const candidates = [
        u?.level, u?.attributes?.level,
        u?.rank,  u?.attributes?.rank,
        UW?.W?.user?.level, UW?.W?.user?.rank,
        UW?.W?.app?.user?.level, UW?.W?.app?.user?.rank,
      ].map((x) => Number(x)).filter((x) => Number.isFinite(x));
      for (const c of candidates) {
        const lvl = normalizeToLevel(c);
        if (Number.isFinite(lvl)) return lvl;
      }
    } catch {}
    return null;
  }

  function readLevelFromSdk() {
    try {
      const a = sdk?.UserSession?.getUserInfo?.()?.rank;
      const lvlA = normalizeToLevel(Number(a));
      if (Number.isFinite(lvlA)) return lvlA;
    } catch {}
    try {
      const b = sdk?.WmeState?.getUserInfo?.()?.rank;
      const lvlB = normalizeToLevel(Number(b));
      if (Number.isFinite(lvlB)) return lvlB;
    } catch {}
    return null;
  }

  function getUserLevel() {
    if (Number.isFinite(cachedUserLevel)) return cachedUserLevel;
    const fromW = readLevelFromW();
    if (Number.isFinite(fromW)) return (cachedUserLevel = fromW);
    const fromSdk = readLevelFromSdk();
    if (Number.isFinite(fromSdk)) return (cachedUserLevel = fromSdk);
    return null;
  }

  function decideLockBase(sampleLockRank, userLevel) {
    if (lockIsZeroBased !== null) return lockIsZeroBased;
    if (Number.isFinite(sampleLockRank) && sampleLockRank >= 0 && sampleLockRank <= 6) { lockIsZeroBased = true; return true; }
    if (Number.isFinite(sampleLockRank) && Number.isFinite(userLevel) && userLevel >= 2) {
      if (sampleLockRank === userLevel - 1) { lockIsZeroBased = true; return true; }
    }
    lockIsZeroBased = false;
    return false;
  }

  function lockRankToLevel(lockRank, userLevelMaybe) {
    if (!Number.isFinite(lockRank)) return null;
    return decideLockBase(lockRank, userLevelMaybe) ? lockRank + 1 : lockRank;
  }

  function levelToLockRank(level, lockRankSample, userLevelMaybe) {
    if (!Number.isFinite(level)) return null;
    return decideLockBase(lockRankSample, userLevelMaybe) ? level - 1 : level;
  }

  function sdkSegGetById(segmentId) {
    return (
      sdk?.Segments?.getById?.({ segmentId }) ??
      sdk?.DataModel?.Segments?.getById?.({ segmentId }) ??
      null
    );
  }

  function sdkStreetsGetById(streetId) {
    return (
      sdk?.Streets?.getById?.({ streetId }) ??
      sdk?.DataModel?.Streets?.getById?.({ streetId }) ??
      null
    );
  }
  function sdkCitiesGetById(cityId) {
    return (
      sdk?.Cities?.getById?.({ cityId }) ??
      sdk?.DataModel?.Cities?.getById?.({ cityId }) ??
      null
    );
  }


  function sdkSegUpdateSegment(segmentId, attrs) {
    if (typeof sdk?.Segments?.updateSegment === "function") {
      return sdk.Segments.updateSegment({ segmentId, ...attrs });
    }
    if (typeof sdk?.DataModel?.Segments?.updateSegment === "function") {
      return sdk.DataModel.Segments.updateSegment({ segmentId, ...attrs });
    }
    throw new Error("No SDK Segments.updateSegment available.");
  }


  function segWGetById(id) {
    try {
      const n = Number(id);
      const segs = UW?.W?.model?.segments;
      if (!segs || typeof segs.getObjectById !== "function") return null;
      return segs.getObjectById(n) || null;
    } catch {
      return null;
    }
  }

  function wAddAction(action) {
    try {
      const am = UW?.W?.model?.actionManager;
      if (!am || typeof am.add !== "function") return false;
      am.add(action);
      return true;
    } catch {
      return false;
    }
  }

  function wUpdateObject(modelObj, attrs) {
    try {
      const A = UW?.W?.Action;
      if (!A) return false;
      const C =
        A.UpdateObject ||
        A.UpdateModelObject ||
        A.UpdateSegment ||
        A.UpdateSegmentAttributes ||
        null;
      if (typeof C !== "function") return false;

      try {
        return wAddAction(new C(modelObj, attrs));
      } catch {
        return wAddAction(new C(modelObj, modelObj?.attributes || {}, attrs));
      }
    } catch {
      return false;
    }
  }

  function wSetSegmentDirection(segW, targetMode) {
    const fwd = targetMode === "2" || targetMode === "A";
    const rev = targetMode === "2" || targetMode === "B";
    const attrs = { fwdDirection: fwd, revDirection: rev, isAtoB: fwd, isBtoA: rev };
    return wUpdateObject(segW, attrs);
  }

  function wReverseSegmentDirection(segW) {
    try {
      const A = UW?.W?.Action;
      if (!A) return false;
      const names = [
        "ReverseSegmentDirection",
        "ReverseSegmentsDirection",
        "ReverseSegment",
        "ReverseSegmentGeometry",
      ];
      for (const n of names) {
        const C = A[n];
        if (typeof C !== "function") continue;
        try {
          if (wAddAction(new C(segW))) return true;
        } catch {}
        try {
          if (wAddAction(new C(segW?.attributes?.id ?? segW?.id))) return true;
        } catch {}
      }
      return false;
    } catch {
      return false;
    }
  }

  function getAllLoadedSegmentsW() {
    try {
      const arr = sdk?.DataModel?.Segments?.getAll?.();
      if (Array.isArray(arr) && arr.length) return arr.filter(Boolean);
    } catch {}
    try {
      const arr = sdk?.Segments?.getAll?.();
      if (Array.isArray(arr) && arr.length) return arr.filter(Boolean);
    } catch {}
    try {
      const segs = UW?.W?.model?.segments;
      if (!segs) return [];
      if (typeof segs.getObjectArray === "function") return segs.getObjectArray() || [];
      if (segs.objects && typeof segs.objects === "object") return Object.values(segs.objects).filter(Boolean);
    } catch {}
    return [];
  }

  function segIdFromW(segW) {
    return Number(
      segW?.segmentId ??
      segW?.objectId ??
      segW?.attributes?.id ??
      segW?.id ??
      (typeof segW?.getID === "function" ? segW.getID() : NaN)
    );
  }

  function primaryStreetIdFromW(segW) {
    const a = segW?.attributes || {};
    return a.primaryStreetID ?? a.primaryStreetId ?? a.primaryStreet ?? segW?.primaryStreetId ?? segW?.streetId ?? null;
  }

  function lockRankFromW(segW) {
    const a = segW?.attributes || {};
    const v = a.lockRank ?? segW?.lockRank;
    return Number.isFinite(Number(v)) ? Number(v) : null;
  }

  function dirFromW(segW) {
    const a = segW?.attributes || {};
    const isAtoB = (typeof segW?.isAtoB === "boolean") ? segW.isAtoB :
      (typeof a.isAtoB === "boolean") ? a.isAtoB :
      (typeof a.fwdDirection === "boolean") ? a.fwdDirection : null;

    const isBtoA = (typeof segW?.isBtoA === "boolean") ? segW.isBtoA :
      (typeof a.isBtoA === "boolean") ? a.isBtoA :
      (typeof a.revDirection === "boolean") ? a.revDirection : null;

    const twoWay = (isAtoB === true && isBtoA === true);
    if (twoWay) return { mode: "2", isAtoB: true, isBtoA: true };
    if (isAtoB === true && isBtoA === false) return { mode: "A", isAtoB: true, isBtoA: false };
    if (isAtoB === false && isBtoA === true) return { mode: "B", isAtoB: false, isBtoA: true };
    return { mode: "?", isAtoB, isBtoA };
  }

  function speedFromW(segW) {
    const a = segW?.attributes || {};
    const f = a.fwdSpeedLimit ?? segW?.fwdSpeedLimit;
    const r = a.revSpeedLimit ?? segW?.revSpeedLimit;
    const nf = (f == null ? null : Number(f));
    const nr = (r == null ? null : Number(r));
    return { f: Number.isFinite(nf) ? nf : null, r: Number.isFinite(nr) ? nr : null };
  }


function elevationFromW(segW) {
  const a = segW?.attributes || {};
  const v = a.level ?? a.elevation ?? segW?.level ?? segW?.elevation;
  const n = (v == null ? null : Number(v));
  return Number.isFinite(n) ? n : null;
}

function getSegLevelById(segmentId) {
  try {
    const seg = sdkSegGetById(segmentId);
    if (Number.isFinite(seg?.level)) return Number(seg.level);
    if (Number.isFinite(seg?.elevation)) return Number(seg.elevation);
    if (seg?.attributes && Number.isFinite(seg.attributes.level)) return Number(seg.attributes.level);
    if (seg?.attributes && Number.isFinite(seg.attributes.elevation)) return Number(seg.attributes.elevation);
  } catch {}
  try {
    const segW = UW?.W?.model?.segments?.getObjectById?.(Number(segmentId));
    return segW ? elevationFromW(segW) : null;
  } catch {}
  return null;
}

function setSegLevelById(segmentId, level) {
  const lvl = (level == null ? null : Number(level));
  if (!Number.isFinite(lvl)) return false;

  try {
    sdkSegUpdateSegment(segmentId, { level: lvl });
    return true;
  } catch {}

  try {
    const segW = UW?.W?.model?.segments?.getObjectById?.(Number(segmentId));
    if (!segW) return false;

    const cur = elevationFromW(segW);
    if (cur === lvl) return true;

    const AM = UW?.W?.model?.actionManager;
    const WA = UW?.W?.Action;

    if (AM && WA) {
      if (typeof WA.UpdateObject === "function") { AM.add(new WA.UpdateObject(segW, { level: lvl })); return true; }
      if (typeof WA.UpdateSegment === "function") { AM.add(new WA.UpdateSegment(segW, { level: lvl })); return true; }
    }

    if (typeof segW.setAttribute === "function") { segW.setAttribute("level", lvl); return true; }
    if (segW.attributes) { segW.attributes.level = lvl; return true; }
  } catch {}
  return false;
}


  function setSelectionToSegmentIds(ids) {
    const uniq = Array.from(new Set((ids || []).map(Number).filter(Number.isFinite)));
    if (!uniq.length) { toast("Nothing to select (not loaded)."); return false; }

    try {
      if (sdk?.Editing?.ObjectType?.SEGMENT != null && typeof sdk?.Editing?.setSelection === "function") {
        sdk.Editing.setSelection({
          selection: uniq.map((id) => ({ objectType: sdk.Editing.ObjectType.SEGMENT, objectId: id })),
        });
        toast(`Selected ${uniq.length} segment(s)`);
        return true;
      }
    } catch {}

    try {
      const sm = UW?.W?.selectionManager;
      const segs = UW?.W?.model?.segments;
      if (sm && segs && typeof segs.getObjectById === "function") {
        const models = uniq.map((id) => segs.getObjectById(id)).filter(Boolean);
        if (models.length) {
          if (typeof sm.setSelectedModels === "function") {
            sm.setSelectedModels(models);
            toast(`Selected ${models.length} segment(s)`);
            return true;
          }
          if (typeof sm.setSelectedItems === "function") {
            sm.setSelectedItems(models);
            toast(`Selected ${models.length} segment(s)`);
            return true;
          }
        }
      }
    } catch {}

    toast("Selection API not available (WME changed).");
    return false;
  }

  function setSelectionToSegmentIdsSilent(ids) {
    const uniq = Array.from(new Set((ids || []).map(Number).filter(Number.isFinite)));
    if (!uniq.length) return false;

    try {
      if (sdk?.Editing?.ObjectType?.SEGMENT != null && typeof sdk?.Editing?.setSelection === "function") {
        sdk.Editing.setSelection({
          selection: uniq.map((id) => ({ objectType: sdk.Editing.ObjectType.SEGMENT, objectId: id })),
        });
        return true;
      }
    } catch {}

    try {
      const sm = UW?.W?.selectionManager;
      const segs = UW?.W?.model?.segments;
      if (sm && segs && typeof segs.getObjectById === "function") {
        const models = uniq.map((id) => segs.getObjectById(id)).filter(Boolean);
        if (models.length) {
          if (typeof sm.setSelectedModels === "function") { sm.setSelectedModels(models); return true; }
          if (typeof sm.setSelectedItems === "function") { sm.setSelectedItems(models); return true; }
        }
      }
    } catch {}

    return false;
  }


  function getSegmentsLockInfo(segIds) {
    const locks = [];
    for (const id of segIds) {
      const seg = sdkSegGetById(id);
      const lk = seg?.lockRank;
      if (Number.isFinite(lk)) locks.push(lk);
    }
    if (!locks.length) return { currentRaw: null, currentLevel: null, mixed: false };
    const first = locks[0];
    const mixed = locks.some((x) => x !== first);
    const userLevel = getUserLevel();
    const lvl = mixed ? null : lockRankToLevel(first, userLevel);
    return { currentRaw: first, currentLevel: lvl, mixed };
  }

  async function setLockForSelection(segIds, targetLevel) {
    const userLevel = getUserLevel() ?? 1;
    const lockInfo = getSegmentsLockInfo(segIds);
    const rawToSet = levelToLockRank(targetLevel, lockInfo.currentRaw, userLevel);
    if (!Number.isFinite(rawToSet)) throw new Error("Could not compute lock rank to set.");

    let ok = 0, fail = 0;
    for (const id of segIds) {
      try { await sdkSegUpdateSegment(id, { lockRank: rawToSet }); ok++; }
      catch { fail++; }
    }
    toast(`Lock ${targetLevel} • ${ok}/${segIds.length}${fail ? `, ${fail} failed` : ""}`);
    updateRootRowSub("lock", fail ? "mixed" : `L${targetLevel}`);
    refreshActiveSubmenuIf("lock");
  }

  function buildLockSubmenu(segIds) {
    const userLevel = getUserLevel() ?? 1;
    const maxLevel = Math.min(6, Math.max(1, userLevel));
    const lockInfo = getSegmentsLockInfo(segIds);

    const items = [
      { label: withIcon(ICONS.lock, "Lock level"), sub: lockInfo.mixed ? "mixed" : `current L${lockInfo.currentLevel ?? "?"}`, disabled: true },
      { type: "sep" },
    ];

    for (let lvl = 1; lvl <= maxLevel; lvl++) {
      const isCurrent = (!lockInfo.mixed && lockInfo.currentLevel != null && lockInfo.currentLevel === lvl);
      items.push({
        label: `Lock ${lvl}`,
        selected: isCurrent,
        check: isCurrent,
        onClick: () => setLockForSelection(segIds, lvl),
      });
    }
    return items;
  }

  function dirSummaryForSelection(segIds) {
    const modes = [];
    for (const id of segIds) {
      const seg = sdkSegGetById(id);
      if (!seg) continue;
      const is2 = !!seg.isTwoWay;
      const isA = !!seg.isAtoB;
      const isB = !!seg.isBtoA;
      if (is2) modes.push("2");
      else if (isA) modes.push("A");
      else if (isB) modes.push("B");
      else if (seg.allowNoDirection) modes.push("N");
      else modes.push("?");
    }
    if (!modes.length) return { mode: "?", mixed: false };
    const first = modes[0];
    const mixed = modes.some((m) => m !== first);
    return { mode: mixed ? "M" : first, mixed };
  }

  function modeLabel(mode) {
    if (mode === "2") return "2-way";
    if (mode === "A") return "A→B";
    if (mode === "B") return "B→A";
    if (mode === "N") return "no-dir";
    if (mode === "M") return "mixed";
    return "unknown";
  }

    async function setDirectionForSelection(segIds, targetMode) {
    let ok = 0, fail = 0;
    const dir = targetMode === "2" ? "TWO_WAY" : targetMode === "A" ? "A_TO_B" : targetMode === "B" ? "B_TO_A" : null;

    for (const id of segIds) {
      let done = false;
      if (dir) {
        try {
          await sdkSegUpdateSegment(id, { direction: dir });
          done = true;
        } catch {}
      }

      if (!done) {
        const segW = segWGetById(id);
        if (segW && wSetSegmentDirection(segW, targetMode)) done = true;
      }

      if (done) ok++;
      else fail++;
    }

    toast(`Direction → ${modeLabel(targetMode)} • ${ok}/${segIds.length}${fail ? `, ${fail} failed` : ""}`);
    updateRootRowSub("dir", fail ? "mixed" : modeLabel(targetMode));
    refreshActiveSubmenuIf("dir");
  }

    async function flipDirectionForSelection(segIds) {
    let ok = 0, fail = 0;

    for (const id of segIds) {
      let done = false;

      const segW = segWGetById(id);
      if (segW && wReverseSegmentDirection(segW)) done = true;

      if (!done) {
        try {
          const seg = sdkSegGetById(id);
          if (!seg) throw new Error("missing");
          const next = seg.isAtoB && !seg.isBtoA ? "B_TO_A" : seg.isBtoA && !seg.isAtoB ? "A_TO_B" : null;
          if (!next) throw new Error("not flippable");
          await sdkSegUpdateSegment(id, { direction: next });
          done = true;
        } catch {}
      }

      if (done) ok++;
      else fail++;
    }

    toast(`Flipped direction • ${ok}/${segIds.length}${fail ? `, ${fail} failed` : ""}`);
    const info = dirSummaryForSelection(segIds);
    updateRootRowSub("dir", info.mixed ? "mixed" : modeLabel(info.mode));
    refreshActiveSubmenuIf("dir");
  }

  function buildDirectionSubmenu(segIds) {
    const info = dirSummaryForSelection(segIds);
    const cur = info.mixed ? "M" : info.mode;

    return [
      { label: withIcon(ICONS.arrows, "Direction"), sub: `current: ${modeLabel(cur)}`, disabled: true },
      { type: "sep" },
      { label: withIcon(ICONS.arrows, "Flip direction"), sub: "Swap A→B / B→A (2-way unchanged)", onClick: () => flipDirectionForSelection(segIds) },
      { type: "sep" },
      { label: "Make 2-way", sub: "A→B + B→A", selected: (!info.mixed && info.mode === "2"), check: (!info.mixed && info.mode === "2"), onClick: () => setDirectionForSelection(segIds, "2") },
      { label: "Make 1-way A→B", sub: "Forward only", selected: (!info.mixed && info.mode === "A"), check: (!info.mixed && info.mode === "A"), onClick: () => setDirectionForSelection(segIds, "A") },
      { label: "Make 1-way B→A", sub: "Reverse only", selected: (!info.mixed && info.mode === "B"), check: (!info.mixed && info.mode === "B"), onClick: () => setDirectionForSelection(segIds, "B") },
    ];
  }

  function getSegDirectionMode(seg) {
    if (!seg) return "BOTH";
    if (seg.isTwoWay) return "BOTH";
    if (seg.isAtoB && !seg.isBtoA) return "FWD";
    if (seg.isBtoA && !seg.isAtoB) return "REV";
    return "BOTH";
  }

  function getSegmentsSpeedInfo(segIds) {
    let any = 0, firstKey = null, mixed = false;

    for (const id of segIds) {
      const seg = sdkSegGetById(id);
      if (!seg) continue;
      any++;

      const mode = getSegDirectionMode(seg);
      const f = seg.fwdSpeedLimit;
      const r = seg.revSpeedLimit;

      const key =
        mode === "FWD" ? `F:${f ?? "∅"}` :
        mode === "REV" ? `R:${r ?? "∅"}` :
        `B:${(f ?? "∅")}/${(r ?? "∅")}`;

      if (firstKey === null) firstKey = key;
      else if (firstKey !== key) mixed = true;
      if (mixed) break;
    }

    if (!any) return { summary: "unknown", mixed: true };
    if (mixed) return { summary: "mixed", mixed: true };

    const seg0 = sdkSegGetById(segIds[0]);
    const mode0 = getSegDirectionMode(seg0);

    if (mode0 === "FWD") return { summary: seg0?.fwdSpeedLimit == null ? "none" : `${seg0.fwdSpeedLimit}`, mixed: false };
    if (mode0 === "REV") return { summary: seg0?.revSpeedLimit == null ? "none" : `${seg0.revSpeedLimit}`, mixed: false };

    const a = seg0?.fwdSpeedLimit;
    const b = seg0?.revSpeedLimit;
    return { summary: `${a ?? "∅"}/${b ?? "∅"}`, mixed: false };
  }

  function getCurrentSpeedValueForChoice(segIds, choice) {
    const seg0 = sdkSegGetById(segIds[0]);
    if (!seg0) return { value: null, mixed: true };

    const info = getSegmentsSpeedInfo(segIds);
    if (info.mixed) return { value: null, mixed: true };

    const mode = getSegDirectionMode(seg0);
    if (mode !== "BOTH") {
      const v = (mode === "FWD") ? seg0.fwdSpeedLimit : seg0.revSpeedLimit;
      return { value: (v == null ? null : Number(v)), mixed: false };
    }

    if (choice === "FWD") return { value: seg0.fwdSpeedLimit == null ? null : Number(seg0.fwdSpeedLimit), mixed: false };
    if (choice === "REV") return { value: seg0.revSpeedLimit == null ? null : Number(seg0.revSpeedLimit), mixed: false };

    const f = seg0.fwdSpeedLimit, r = seg0.revSpeedLimit;
    if (f == null && r == null) return { value: null, mixed: false };
    if (f != null && r != null && Number(f) === Number(r)) return { value: Number(f), mixed: false };
    return { value: null, mixed: true };
  }

  async function setSpeedForSelection(segIds, valueOrNull, modeOverride = "AUTO", keepOpen = false) {
    let ok = 0, fail = 0;

    for (const id of segIds) {
      const seg = sdkSegGetById(id);
      if (!seg) { fail++; continue; }

      const mode = getSegDirectionMode(seg);
      const patch = {};

      if (mode !== "BOTH") {
        if (mode === "FWD") patch.fwdSpeedLimit = valueOrNull;
        else patch.revSpeedLimit = valueOrNull;
      } else {
        const eff = (modeOverride === "AUTO" ? "BOTH" : modeOverride);
        if (eff === "FWD") patch.fwdSpeedLimit = valueOrNull;
        else if (eff === "REV") patch.revSpeedLimit = valueOrNull;
        else { patch.fwdSpeedLimit = valueOrNull; patch.revSpeedLimit = valueOrNull; }
      }

      try { sdkSegUpdateSegment(id, patch); ok++; }
      catch { fail++; }
    }

    const label = valueOrNull == null ? "cleared" : `→ ${valueOrNull}`;
    const suffix = (modeOverride !== "AUTO") ? ` (${modeOverride === "BOTH" ? "Both" : modeOverride === "FWD" ? "A→B" : "B→A"})` : "";
    toast(`Speed ${label}${suffix} • ${ok}/${segIds.length}${fail ? `, ${fail} failed` : ""}`);

    if (keepOpen) {
      refreshActiveSubmenuIf("speed");
      const speedSummary = getSegmentsSpeedInfo(segIds).summary;
      updateRootRowSub("speed", speedSummary);
      return;
    }
    closeAllMenus();
  }

  function buildSpeedDirectionChips(isTwoWay, segIds) {
    if (!isTwoWay) return null;

    const wrap = document.createElement("div");
    wrap.className = "wmeRcChips";

    const mk = (id, label) => {
      const b = document.createElement("div");
      b.className = "wmeRcChip" + (speedDirChoice === id ? " on" : "");
      b.textContent = label;
      b.addEventListener("click", (e) => {
        e.preventDefault();
        e.stopPropagation();
        speedDirChoice = id;
        refreshActiveSubmenuIf("speed");
        const speedSummary = getSegmentsSpeedInfo(segIds).summary;
        updateRootRowSub("speed", speedSummary);
      });
      return b;
    };

    wrap.appendChild(mk("BOTH", "Both"));
    wrap.appendChild(mk("FWD", "A→B"));
    wrap.appendChild(mk("REV", "B→A"));
    return wrap;
  }

  function buildSpeedSubmenu(segIds) {
    const seg0 = sdkSegGetById(segIds[0]);
    const isTwoWay = !!seg0?.isTwoWay;

    const speeds = [20,30,40,50,60,70,80,90,100,110,120,130];
    const cur = getCurrentSpeedValueForChoice(segIds, speedDirChoice);
    const curTxt = cur.mixed ? "current: mixed" : (cur.value == null ? "current: none" : `current: ${cur.value}`);
    const chips = buildSpeedDirectionChips(isTwoWay, segIds);

    return [{
      type: "speedGrid",
      titleLeft: isTwoWay ? "Speed limit • choose direction" : "Speed limit",
      titleRight: curTxt,
      chipsEl: chips,
      buttons: [
        ...speeds.map((s) => ({
          text: String(s),
          title: `${s} km/h`,
          selected: (!cur.mixed && cur.value != null && Number(cur.value) === s),
          onClick: () => setSpeedForSelection(segIds, s, isTwoWay ? speedDirChoice : "AUTO", true),
        })),
        {
          html: ICONS.trash,
          title: "Clear speed",
          selected: (!cur.mixed && cur.value == null),
          onClick: () => setSpeedForSelection(segIds, null, isTwoWay ? speedDirChoice : "AUTO", true),
        },
      ],
    }];
  }

  function segmentCoords(segmentId) {
    const seg = sdkSegGetById(segmentId);
    const c = seg?.geometry?.coordinates;
    if (!Array.isArray(c)) return [];
    return c
      .filter((p) => Array.isArray(p) && p.length >= 2)
      .map((p) => [Number(p[0]), Number(p[1])])
      .filter(([lon, lat]) => Number.isFinite(lon) && Number.isFinite(lat));
  }

  function bboxFromSegments(segIds) {
    const coords = [];
    for (const id of segIds) coords.push(...segmentCoords(id));
    if (!coords.length) return null;

    let left = Infinity, bottom = Infinity, right = -Infinity, top = -Infinity;
    for (const [lon, lat] of coords) {
      if (lon < left) left = lon;
      if (lon > right) right = lon;
      if (lat < bottom) bottom = lat;
      if (lat > top) top = lat;
    }
    return isFinite(left) ? [left, bottom, right, top] : null;
  }

  async function zoomToSegments(segIds) {
    const bbox = bboxFromSegments(segIds);
    if (!bbox || typeof sdk?.Map?.zoomToExtent !== "function") throw new Error("Zoom unavailable.");
    sdk.Map.zoomToExtent({ bbox });
  }

  async function actionZoomTo(segIds) {
    try { await zoomToSegments(segIds); toast("Zoomed."); }
    catch { toast("Zoom unavailable."); }
    closeAllMenus();
  }

  function zoomToLonLat(lon, lat) {
    let moved = false;
    try {
      if (typeof sdk?.Map?.zoomToExtent === "function") {
        const d = 0.0009;
        sdk.Map.zoomToExtent({ bbox: [lon - d, lat - d, lon + d, lat + d] });
        moved = true;
      }
    } catch {}
    if (!moved) {
      try {
        if (typeof sdk?.Map?.setCenter === "function") {
          sdk.Map.setCenter({ lon, lat });
          moved = true;
        }
      } catch {}
    }
    return moved;
  }


  function zoomToLonLatExact(lon, lat, zoomLevel) {
    let moved = false;
    const z = Number.isFinite(Number(zoomLevel)) ? Number(zoomLevel) : null;

    try {
      if (typeof sdk?.Map?.setCenter === "function") {
        sdk.Map.setCenter({ lon, lat });
        if (Number.isFinite(z) && typeof sdk?.Map?.setZoom === "function") {
          sdk.Map.setZoom({ zoomLevel: z });
        }
        requestAnimationFrame(() => {
          try {
            sdk?.Map?.setCenter?.({ lon, lat });
            if (Number.isFinite(z)) sdk?.Map?.setZoom?.({ zoomLevel: z });
          } catch {}
        });
        moved = true;
      }
    } catch {}

    if (!moved) {
      try {
        const map = getOlMapBestEffort();
        const ol = UW?.OpenLayers;
        if (map && ol && typeof map.setCenter === "function") {
          let ll = new ol.LonLat(lon, lat);
          try {
            const dst = map.getProjectionObject?.() || map.projection || null;
            const dstCode = String(dst?.projCode || dst?.getCode?.() || dst || "");
            const needsTransform = /900913|3857|102113|102100/i.test(dstCode) && !/4326/i.test(dstCode);
            if (needsTransform && typeof ll.transform === "function") {
              const src = new ol.Projection("EPSG:4326");
              if (dst) ll.transform(src, dst);
            }
          } catch {}
          map.setCenter(ll, Number.isFinite(z) ? z : undefined);
          moved = true;
        }
      } catch {}
    }

    if (!moved) {
      try {
        if (typeof sdk?.Map?.zoomToExtent === "function") {
          const d = 0.0009;
          sdk.Map.zoomToExtent({ bbox: [lon - d, lat - d, lon + d, lat + d] });
          moved = true;
        }
      } catch {}
    }

    return moved;
  }



  function computeDxDyToCenter(map, lon, lat) {
    try {
      const ol = UW?.OpenLayers;
      if (!map || !ol) return null;
      if (typeof map.getLayerPxFromLonLat !== "function" || typeof map.getSize !== "function") return null;

      let ll = new ol.LonLat(lon, lat);
      try {
        const dst = map.getProjectionObject?.() || map.projection || null;
        const dstCode = String(dst?.projCode || dst?.getCode?.() || dst || "");
        const needsTransform = /900913|3857|102113|102100/i.test(dstCode) && !/4326/i.test(dstCode);
        if (needsTransform && typeof ll.transform === "function") {
          const src = new ol.Projection("EPSG:4326");
          if (dst) ll.transform(src, dst);
        }
      } catch {}

      const px = map.getLayerPxFromLonLat(ll);
      const sz = map.getSize();
      if (!px || !sz || !isFinite(px.x) || !isFinite(px.y) || !isFinite(sz.w) || !isFinite(sz.h)) return null;

      const dx = Math.round((sz.w / 2) - px.x);
      const dy = Math.round((sz.h / 2) - px.y);
      return { dx, dy };
    } catch {}
    return null;
  }

  function ensureCenteredAfterMotion(lon, lat) {
    let attempt = 0;
    const maxAttempts = 6;

    const tick = () => {
      try {
        const map = getOlMapBestEffort();
        const r = computeDxDyToCenter(map, lon, lat);
        if (!r) return;
        const { dx, dy } = r;
        if (Math.abs(dx) <= 1 && Math.abs(dy) <= 1) return;
        panByPx(dx, dy);
      } catch {}
      attempt += 1;
      if (attempt <= maxAttempts) {
        setTimeout(tick, 90 + attempt * 70);
      }
    };

    requestAnimationFrame(() => requestAnimationFrame(tick));
  }

let _pinJumpWarmupDone = false;

function getDefaultPinZoom() {
    return 17;
  }

  function centerPinInstant(lon, lat, zoomLevel) {
    const z = Number.isFinite(Number(zoomLevel)) ? Number(zoomLevel) : null;
    try {
      const map = getOlMapBestEffort();
      const ol = UW?.OpenLayers;
      if (map && ol && typeof map.setCenter === "function") {
        let ll = new ol.LonLat(lon, lat);
        try {
          const dst = map.getProjectionObject?.() || map.projection || null;
          const dstCode = String(dst?.projCode || dst?.getCode?.() || dst || "");
          const needsTransform = /900913|3857|102113|102100/i.test(dstCode) && !/4326/i.test(dstCode);
          if (needsTransform && typeof ll.transform === "function") {
            const src = new ol.Projection("EPSG:4326");
            if (dst) ll.transform(src, dst);
          }
        } catch {}

        map.setCenter(ll, Number.isFinite(z) ? z : undefined, true, true);

        try {
          const dx = computeVisibleCenterPanDx();
          if (dx && typeof map.getViewPortPxFromLonLat === "function" && typeof map.getLonLatFromViewPortPx === "function") {
            const cpx = map.getViewPortPxFromLonLat(map.getCenter());
            if (cpx) {
              const newPx = new ol.Pixel(cpx.x - dx, cpx.y);
              const newCenter = map.getLonLatFromViewPortPx(newPx);
              if (newCenter) map.setCenter(newCenter, Number.isFinite(z) ? z : undefined, true, true);
            }
          }
        } catch {}

        return true;
      }
    } catch {}

    try {
      if (typeof sdk?.Map?.setCenter === "function") {
        sdk.Map.setCenter({ lon, lat });
        if (Number.isFinite(z) && typeof sdk?.Map?.setZoom === "function") sdk.Map.setZoom({ zoomLevel: z });
        return true;
      }
    } catch {}

    try {
      if (typeof sdk?.Map?.zoomToExtent === "function") {
        const d = 0.0009;
        sdk.Map.zoomToExtent({ bbox: [lon - d, lat - d, lon + d, lat + d] });
        return true;
      }
    } catch {}

    return false;
  }

  function jumpToPin(pin) {
    try {
      if (!pin) return;
      const z = getDefaultPinZoom();

      try {
        const map = getOlMapBestEffort();
        map && map.updateSize && map.updateSize();
      } catch {}

      centerPinInstant(pin.lon, pin.lat, z);

      if (!_pinJumpWarmupDone) {
        _pinJumpWarmupDone = true;
        const warm = () => {
          try {
            const map = getOlMapBestEffort();
            map && map.updateSize && map.updateSize();
          } catch {}
          centerPinInstant(pin.lon, pin.lat, z);
        };
        setTimeout(warm, 260);
        setTimeout(warm, 820);
      }
    } catch (e) {
      try { console.error(e); } catch {}
      try { toast("Pin: jump failed."); } catch {}
    }
  }  function applyVisibleCenterOffset() {
    const dx = computeVisibleCenterPanDx();
    if (!dx) return;
    requestAnimationFrame(() => requestAnimationFrame(() => { panByPx(dx, 0); }));
  }

  function computeVisibleCenterPanDx() {
    const left = measureSideObstructionPx("left");
    const right = measureSideObstructionPx("right");
    const net = (left - right) / 2;
    const dx = -Math.round(net);
    return Math.abs(dx) >= 10 ? dx : 0;
  }

  function measureSideObstructionPx(side) {
    const mapEl = document.querySelector("#map") || document.querySelector(".olMap") || null;
    const mapRect = mapEl ? mapEl.getBoundingClientRect() : { left: 0, top: 0, right: window.innerWidth, bottom: window.innerHeight, width: window.innerWidth, height: window.innerHeight };

    const x = side === "left" ? Math.max(2, mapRect.left + 4) : Math.min(window.innerWidth - 2, mapRect.right - 4);
    const y = Math.min(window.innerHeight - 2, Math.max(2, mapRect.top + Math.min(360, mapRect.height * 0.35)));

    let stack = [];
    try { stack = document.elementsFromPoint(x, y) || []; } catch {}

    for (const el of stack) {
      if (!el || el === document.documentElement || el === document.body) continue;
      if (mapEl && (el === mapEl || mapEl.contains(el))) continue;

      const r = el.getBoundingClientRect();
      if (r.width < 140 || r.height < 200) continue;
      if (r.bottom <= mapRect.top + 10 || r.top >= mapRect.bottom - 10) continue;

      const overlapsHoriz = !(r.right <= mapRect.left + 2 || r.left >= mapRect.right - 2);
      if (!overlapsHoriz) continue;

      const cs = getComputedStyle(el);
      if (cs.display === "none" || cs.visibility === "hidden") continue;
      if (Number(cs.opacity) === 0) continue;

      if (side === "left") {
        const covered = Math.max(0, Math.min(r.right, mapRect.right) - mapRect.left);
        return covered;
      }
      const covered = Math.max(0, mapRect.right - Math.max(r.left, mapRect.left));
      return covered;
    }

    return 0;
  }

  function panByPx(dx, dy) {
    dx = Math.round(dx || 0);
    dy = Math.round(dy || 0);
    if (!dx && !dy) return false;

    try {
      const olMap = UW?.W?.map;
      if (olMap) {
        if (typeof olMap.pan === "function") { olMap.pan(dx, dy, { animate: false }); return true; }
        if (typeof olMap.moveByPx === "function") { olMap.moveByPx(dx, dy); return true; }
      }
    } catch {}

    try {
      const getLL = sdk?.Map?.getLonLatFromPixel;
      const setC = sdk?.Map?.setCenter;
      if (typeof getLL === "function" && typeof setC === "function") {
        const mapEl = document.querySelector("#map") || document.querySelector(".olMap") || null;
        const r = mapEl ? mapEl.getBoundingClientRect() : { left: 0, top: 0, width: window.innerWidth, height: window.innerHeight };
        const cx = r.left + (r.width / 2);
        const cy = r.top + (r.height / 2);
        const x = Math.round(cx + dx);
        const y = Math.round(cy + dy);
        const ll = getLL({ x, y });
        if (ll && isFinite(ll.lon) && isFinite(ll.lat)) {
          setC({ lon: ll.lon, lat: ll.lat });
          return true;
        }
      }
    } catch {}

    return false;
  }

  function getSegmentEndpoints(segId) {
    const coords = segmentCoords(segId);
    if (!coords.length) return null;
    const a = coords[0];
    const b = coords[coords.length - 1];
    return { a: { lon: a[0], lat: a[1] }, b: { lon: b[0], lat: b[1] } };
  }

  async function goToEndpoint(segId, which) {
    const ep = getSegmentEndpoints(segId);
    if (!ep) { toast("Segment not loaded (zoom in to load)."); return; }
    const p = which === "A" ? ep.a : ep.b;
    const ok = zoomToLonLat(p.lon, p.lat);
    toast(ok ? `Moved to ${which} node` : "Move failed (API mismatch).");
    closeAllMenus();
  }

  function buildEndpointsSubmenu(segIds) {
    if (segIds.length !== 1) return [];
    return [
      { label: "Go to node A", sub: "Start of segment", onClick: () => goToEndpoint(segIds[0], "A") },
      { label: "Go to node B", sub: "End of segment", onClick: () => goToEndpoint(segIds[0], "B") },
    ];
  }

  async function actionCopySegmentIds(segIds) {
    const text = segIds.join(", ");
    await setClipboard(text);
    toast(`Copied ID(s): ${text}`);
    closeAllMenus();
  }

  async function actionCopyStreetName(firstSegId) {
    const seg = sdkSegGetById(firstSegId);
    if (!seg) { toast("Segment not found (zoom in until it loads)."); closeAllMenus(); return; }

    const streetId = seg.primaryStreetId;
    if (streetId == null) { await setClipboard("(no street)"); toast("Copied: (no street)"); closeAllMenus(); return; }

    const street = sdkStreetsGetById(streetId);
    const name = street?.streetName ?? street?.name ?? street?.englishName ?? "(unknown street)";
    await setClipboard(String(name));
    toast(`Copied street: ${name}`);
    closeAllMenus();
  }

  function lonLatFromClick(clientX, clientY) {
    try {
      const fn = sdk?.Map?.getLonLatFromPixel;
      if (typeof fn === "function") {
        const ll = fn({ x: clientX, y: clientY });
        if (ll && isFinite(ll.lon) && isFinite(ll.lat)) return ll;
      }
    } catch {}
    return lastLonLat;
  }


  function gmapsUrlFromLonLat(ll) {
    return `${GMAPS_BASE}${fmt(ll.lat)},${fmt(ll.lon)}`;
  }

  function osmUrlFromLonLat(ll, zoom = 19) {
    const z = Number.isFinite(zoom) ? zoom : 19;
    return `https://www.openstreetmap.org/#map=${z}/${fmt(ll.lat)}/${fmt(ll.lon)}`;
  }

  function wazeLiveMapUrlFromLonLat(ll, zoom = 17) {
    const z = Number.isFinite(zoom) ? zoom : 17;
    return `https://www.waze.com/live-map?zoom=${z}&lat=${fmt(ll.lat)}&lon=${fmt(ll.lon)}`;
  }

  function getZoomLevelBestEffort() {
    try {
      if (typeof sdk?.Map?.getZoom === "function") {
        const z = sdk.Map.getZoom();
        if (Number.isFinite(z)) return z;
      }
    } catch {}
    try {
      const z = UW?.W?.map?.getZoom?.();
      if (Number.isFinite(z)) return z;
    } catch {}
    return null;
  }


  const PERMALINK_SETTINGS_KEY = "wme_rc_permalink_settings_v1";
  const DEFAULT_PERMALINK_SETTINGS = { zoomLocked: false, zoomLevel: null, includeLayers: true };

  function loadPermalinkSettings() {
    try {
      const raw = localStorage.getItem(PERMALINK_SETTINGS_KEY);
      if (!raw) return { ...DEFAULT_PERMALINK_SETTINGS };
      const j = JSON.parse(raw);
      const out = { ...DEFAULT_PERMALINK_SETTINGS, ...(j || {}) };
      if (!Number.isFinite(out.zoomLevel)) out.zoomLevel = null;
      out.zoomLocked = !!out.zoomLocked;
      out.includeLayers = !!out.includeLayers;
      return out;
    } catch {
      return { ...DEFAULT_PERMALINK_SETTINGS };
    }
  }

  function savePermalinkSettings(s) {
    try { localStorage.setItem(PERMALINK_SETTINGS_KEY, JSON.stringify(s || {})); } catch {}
  }

  function getLayerParamsFromCurrentURL() {
    try {
      const cur = new URLSearchParams(location.search);
      const keepKeys = ["layers", "layer", "layersVisibility"];
      const out = {};
      for (const k of keepKeys) {
        if (cur.has(k)) out[k] = cur.get(k);
      }
      return out;
    } catch {
      return {};
    }
  }

  function showPermalinkSettingsModal() {
    const st = loadPermalinkSettings();
    const layerParams = getLayerParamsFromCurrentURL();
    const layerText = Object.keys(layerParams).length
      ? Object.entries(layerParams).map(([k, v]) => `${k}=${v}`).join("  •  ")
      : "(no layer params found in URL)";

    openModal({
      title: "Permalink settings",
      iconSvg: ICONS.chain,
      bodyBuilder: ({ body, close }) => {
        const wrap = document.createElement("div");
        wrap.style.display = "flex";
        wrap.style.flexDirection = "column";
        wrap.style.gap = "12px";

        const hint = document.createElement("div");
        hint.className = "wmeRcHint";
        hint.textContent = "Applies to Copy permalink and Refresh here.";
        wrap.appendChild(hint);

        const zoomRow = document.createElement("div");
        zoomRow.className = "wmeRcRow";
        zoomRow.style.alignItems = "center";
        zoomRow.innerHTML = `
          <div class="wmeRcRowLabel" style="min-width:120px;">Zoom level</div>
          <div style="display:flex;align-items:center;gap:10px;flex:1 1 auto;min-width:0;">
            <label class="wmeRcInlineToggle" style="margin:0;">
              <input type="checkbox" class="wmeRcZoomLock">
              <span class="wmeRcSwitchTrack"><span class="wmeRcSwitchThumb"></span></span>
              <span class="wmeRcSwitchLabel">Lock</span>
            </label>
            <input class="wmeRcInput wmeRcZoomInp" type="number" min="1" max="22" step="1" placeholder="Current" style="max-width:140px;">
          </div>
        `;
        const zoomLock = zoomRow.querySelector(".wmeRcZoomLock");
        const zoomInp = zoomRow.querySelector(".wmeRcZoomInp");
        zoomLock.checked = !!st.zoomLocked;
        zoomInp.value = (Number.isFinite(st.zoomLevel) ? String(st.zoomLevel) : "");
        zoomInp.disabled = !zoomLock.checked;

        zoomLock.addEventListener("change", () => {
          zoomInp.disabled = !zoomLock.checked;
          if (!zoomLock.checked) {
            zoomInp.value = "";
            zoomInp.classList.remove("bad");
          }
        });

        zoomInp.addEventListener("input", () => {
          zoomInp.classList.remove("bad");
        });

        wrap.appendChild(zoomRow);

        const layersWrap = document.createElement("div");
        layersWrap.className = "wmeRcHint";
        layersWrap.innerHTML = `
          <label class="wmeRcInlineToggle" style="margin:0;">
            <input type="checkbox" class="wmeRcLayersChk">
            <span class="wmeRcSwitchTrack"><span class="wmeRcSwitchThumb"></span></span>
            <span class="wmeRcSwitchLabel">Include layer settings</span>
          </label>
          <div style="margin-top:8px;opacity:.72;font-size:12px;line-height:1.25;word-break:break-word;">${escapeHtml(layerText)}</div>
        `;
        const layersChk = layersWrap.querySelector(".wmeRcLayersChk");
        layersChk.checked = !!st.includeLayers;
        wrap.appendChild(layersWrap);

        const actions = document.createElement("div");
        actions.className = "wmeRcModalActions";

        const btnCancel = document.createElement("div");
        btnCancel.className = "wmeRcModalBtn";
        btnCancel.textContent = "Cancel";
        btnCancel.addEventListener("click", () => close());

        const btnSave = document.createElement("div");
        btnSave.className = "wmeRcModalBtn primary";
        btnSave.textContent = "Save";

        btnSave.addEventListener("click", () => {
          const next = { ...DEFAULT_PERMALINK_SETTINGS };
          next.includeLayers = !!layersChk.checked;
          next.zoomLocked = !!zoomLock.checked;

          if (next.zoomLocked) {
            const z = Number(String(zoomInp.value || "").trim());
            if (!Number.isFinite(z) || z < 1 || z > 22) {
              zoomInp.classList.add("bad");
              toast("Zoom level must be 1–22");
              return;
            }
            next.zoomLevel = Math.round(z);
          } else {
            next.zoomLevel = null;
          }

          savePermalinkSettings(next);
          toast("Saved: permalink settings");
          close();
        });

        actions.appendChild(btnCancel);
        actions.appendChild(btnSave);

        wrap.appendChild(actions);
        body.appendChild(wrap);
      },
    });
  }
  function buildPermalink(ll, segIds = null, venuesParam = null) {
    const st = loadPermalinkSettings();
    const z = (st && st.zoomLocked && Number.isFinite(st.zoomLevel)) ? Number(st.zoomLevel) : getZoomLevelBestEffort();

    const cur = new URLSearchParams(location.search);
    const params = new URLSearchParams();

    for (const k of ["env", "tab", "language", "locale", "country"]) {
      if (cur.has(k)) params.set(k, cur.get(k));
    }

    if (st && st.includeLayers) {
      for (const [k, v] of Object.entries(getLayerParamsFromCurrentURL())) {
        if (v != null) params.set(k, String(v));
      }
    }

    params.set("lat", fmt(ll.lat));
    params.set("lon", fmt(ll.lon));

    if (Number.isFinite(z)) params.set("zoomLevel", String(Math.round(z)));

    if (Array.isArray(segIds) && segIds.length) params.set("segments", segIds.join(","));

    const vp = String(venuesParam || "").trim();
    if (vp) params.set("venues", vp);

    const qs = params.toString();
    return qs ? `${EDITOR_BASE}?${qs}` : EDITOR_BASE;
  }


  function _extractVenuesTokenFromValue(val) {
    try {
      const s = String(val || "").trim();
      if (!s) return null;
      const u = new URL(s, location.origin);
      const v = u.searchParams.get("venues");
      const t = String(v || "").trim();
      if (t) return t;
    } catch {}
    return null;
  }

  function _isVenuesTokenString(s) {
    const t = String(s || "").trim();
    if (!t) return false;
    if (!/^\d+\.\d+\.\d+(,\d+\.\d+\.\d+)*$/.test(t)) return false;
    return true;
  }

  function getVenuesParamBestEffort() {
    try {
      const cur = new URL(location.href);
      const v = String(cur.searchParams.get("venues") || "").trim();
      if (_isVenuesTokenString(v)) return v;
    } catch {}

    try {
      const nodes = Array.from(document.querySelectorAll("input, textarea"));
      let best = null;
      for (const el of nodes) {
        const val = String(el && (el.value ?? "") || "").trim();
        if (!val) continue;
        if (val.indexOf("/editor") === -1 || val.indexOf("lat=") === -1 || val.indexOf("lon=") === -1) continue;
        const tok = _extractVenuesTokenFromValue(val);
        if (tok && _isVenuesTokenString(tok)) return tok;
        if (!best) best = val;
      }
      if (best) {
        const tok = _extractVenuesTokenFromValue(best);
        if (tok && _isVenuesTokenString(tok)) return tok;
      }
    } catch {}

    try {
      const nodes = Array.from(document.querySelectorAll("[href]"));
      for (const el of nodes) {
        const href = String(el && el.getAttribute && el.getAttribute("href") || "").trim();
        if (!href) continue;
        if (href.indexOf("/editor") === -1 || href.indexOf("lat=") === -1 || href.indexOf("lon=") === -1) continue;
        const tok = _extractVenuesTokenFromValue(href);
        if (tok && _isVenuesTokenString(tok)) return tok;
      }
    } catch {}

    return null;
  }

  function getPermalinkFromUiPreferVenues() {
    try {
      const nodes = Array.from(document.querySelectorAll("input, textarea"));
      let fallback = null;
      for (const el of nodes) {
        const val = String(el && (el.value ?? "") || "").trim();
        if (!val) continue;
        if (val.indexOf("/editor") === -1 || val.indexOf("lat=") === -1 || val.indexOf("lon=") === -1) continue;
        const tok = _extractVenuesTokenFromValue(val);
        if (tok && _isVenuesTokenString(tok)) return val;
        if (!fallback) fallback = val;
      }
      return fallback;
    } catch {}
    return null;
  }

  function deriveVenuesTokenFromSelection() {
    try {
      const sm = UW?.W?.selectionManager;
      const items = sm?.getSelectedFeatures?.() || sm?.getSelectedItems?.() || [];
      if (Array.isArray(items) && items.length) {
        for (const it of items) {
          const cand = [
            it?.permalink,
            it?.permalinkId,
            it?.permaLinkId,
            it?.attributes?.permalink,
            it?.attributes?.permalinkId,
            it?.attributes?.permaLinkId,
            it?.model?.permalink,
            it?.model?.permalinkId,
            it?.model?.permaLinkId,
            it?.model?.attributes?.permalink,
            it?.model?.attributes?.permalinkId,
            it?.model?.attributes?.permaLinkId,
          ].filter(Boolean);

          for (const c of cand) {
            const tok = _extractVenuesTokenFromValue(c) || (_isVenuesTokenString(c) ? String(c).trim() : null);
            if (tok) return tok;
          }

          const attrs = it?.attributes || it?.model?.attributes || null;
          if (attrs && typeof attrs === "object") {
            for (const v of Object.values(attrs)) {
              if (typeof v === "string") {
                if (_isVenuesTokenString(v)) return v.trim();
                const tok = _extractVenuesTokenFromValue(v);
                if (tok) return tok;
              }
            }
          }
        }
      }
    } catch {}

    try {
      const info = selectedPlaceInfo();
      const ids = Array.isArray(info?.ids) ? info.ids : [];
      if (ids.length) {
        for (const id of ids) {
          try {
            const obj = sdk?.DataModel?.Venues?.getById?.({ venueId: id }) || sdk?.Venues?.getById?.({ venueId: id }) || null;
            const attrs = obj?.attributes || obj?.model?.attributes || obj || null;
            const cand = [
              obj?.permalink,
              obj?.permalinkId,
              obj?.permaLinkId,
              attrs?.permalink,
              attrs?.permalinkId,
              attrs?.permaLinkId,
            ].filter(Boolean);

            for (const c of cand) {
              const tok = _extractVenuesTokenFromValue(c) || (_isVenuesTokenString(c) ? String(c).trim() : null);
              if (tok) return tok;
            }

            if (attrs && typeof attrs === "object") {
              for (const v of Object.values(attrs)) {
                if (typeof v === "string") {
                  if (_isVenuesTokenString(v)) return v.trim();
                  const tok = _extractVenuesTokenFromValue(v);
                  if (tok) return tok;
                }
              }
            }
          } catch {}
        }
      }
    } catch {}

    return null;
  }

  async function copyPermalink(ll, segIds = null) {
    let venues = null;

    try {
      const p = selectedPlaceInfo();
      if (p && p.has) {
        const direct = getPermalinkFromUiPreferVenues();
        const tok = _extractVenuesTokenFromValue(direct);
        if (tok && _isVenuesTokenString(tok)) {
          await setClipboard(String(direct).trim());
          toast("Copied: permalink");
          closeAllMenus();
          return;
        }
      }
    } catch {}

    try {
      const p = selectedPlaceInfo();
      if (p && p.has) {
        venues = getVenuesParamBestEffort() || deriveVenuesTokenFromSelection();
        if (!venues) {
          try {
            const ww = UW?.WazeWrap || (typeof WazeWrap !== "undefined" ? WazeWrap : null);
            const fn = ww?.Util?.getPermalink || ww?.Util?.getPermalinkUrl || ww?.Util?.getPermalinkURL || null;
            if (typeof fn === "function") {
              const out = String(fn() || "").trim();
              const tok = _extractVenuesTokenFromValue(out);
              if (tok) venues = tok;
            }
          } catch {}
        }
      }
    } catch {}

    const url = buildPermalink(ll, segIds, venues);
    await setClipboard(url);
    toast("Copied: permalink");
    closeAllMenus();
  }

async function copyCoordsLatLon(ll) {
    await setClipboard(`${fmt(ll.lat)}, ${fmt(ll.lon)}`);
    toast("Copied: lat,lon");
    closeAllMenus();
  }

  async function copyCoordsGmaps(ll) {
    await setClipboard(gmapsUrlFromLonLat(ll));
    toast("Copied: Google Maps link");
    closeAllMenus();
  }
  function refreshHere(ll, segIds = null) {
    try {
      if (!ll || !Number.isFinite(ll.lat) || !Number.isFinite(ll.lon)) {
        toast("No map position yet. Move mouse on map first.");
        closeAllMenus();
        return;
      }
      const url = buildPermalink(ll, segIds);
      closeAllMenus();
      location.replace(url);
    } catch (e) {
      try { closeAllMenus(); location.reload(); } catch {}
    }
  }


  function parseNumbersFromString(s) {
    return (s.match(/-?\d+(\.\d+)?/g) || []).map(Number).filter(n => Number.isFinite(n));
  }

  function tryParseCoords(input) {
    const nums = parseNumbersFromString(input);
    if (nums.length < 2) return null;
    const a = nums[0], b = nums[1];

    const aIsLat = Math.abs(a) <= 90, bIsLon = Math.abs(b) <= 180;
    const aIsLon = Math.abs(a) <= 180, bIsLat = Math.abs(b) <= 90;

    if (aIsLat && bIsLon) return { lat: a, lon: b };
    if (aIsLon && bIsLat) return { lat: b, lon: a };
    return null;
  }


  function tryParseGoogleMaps(input) {
    try {
      const s = String(input || "").trim();
      if (!s) return null;


      const looksLikeGmaps = /google\.[^\s/]+\/maps|maps\.google\.|\/maps\b|maps\.app\.goo\.gl|goo\.gl\/maps/i.test(s);
      if (!looksLikeGmaps) return null;


      const at = s.match(/@\s*(-?\d+(?:\.\d+)?),\s*(-?\d+(?:\.\d+)?)(?:,\s*(\d+(?:\.\d+)?)z)?/i);
      if (at) {
        const lat = Number(at[1]);
        const lon = Number(at[2]);
        if (Number.isFinite(lat) && Number.isFinite(lon)) return { lat, lon, zoom: null };
      }


      let url = null;
      try { url = new URL(s); } catch { url = null; }
      if (url) {
        const q = url.searchParams.get("q") || url.searchParams.get("query") || url.searchParams.get("ll");
        if (q) {
          const cc = tryParseCoords(q);
          if (cc) return { lat: cc.lat, lon: cc.lon, zoom: null };
        }
      }


      const cc2 = tryParseCoords(s);
      if (cc2) return { lat: cc2.lat, lon: cc2.lon, zoom: null };


      return { needsResolve: true };
    } catch {
      return null;
    }
  }

  function tryParsePermalink(input) {
    if (!/lat=|lon=|zoomLevel=|segments=/i.test(input)) return null;

    const lat = (() => {
      const m = input.match(/(?:\?|&)lat=([-0-9.]+)/i);
      return m ? Number(m[1]) : null;
    })();
    const lon = (() => {
      const m = input.match(/(?:\?|&)lon=([-0-9.]+)/i);
      return m ? Number(m[1]) : null;
    })();
    const zoom = (() => {
      const m = input.match(/(?:\?|&)zoomLevel=([0-9]+)/i);
      return m ? Number(m[1]) : null;
    })();

    if (!Number.isFinite(lat) || !Number.isFinite(lon)) return null;
    return { lat, lon, zoom: Number.isFinite(zoom) ? zoom : null };
  }



  let __rcJumpPinLayer = null;
  let __rcJumpPinMarker = null;
  let __rcJumpPinTimer = null;

  function __rcEnsureJumpPinLayer() {
    try {
      const map = getOlMapBestEffort();
      const ol = UW?.OpenLayers;
      if (!map || !ol) return null;

      if (__rcJumpPinLayer && typeof map.getLayer === "function") {

        return __rcJumpPinLayer;
      }


      const layer = new ol.Layer.Markers(`${SCRIPT_ID}:jumpPin`, { displayInLayerSwitcher: false });
      try { layer.setZIndex?.(99999); } catch {}
      try { map.addLayer(layer); } catch {}
      __rcJumpPinLayer = layer;
      return layer;
    } catch {
      return null;
    }
  }

  function __rcMakeJumpPinSvg(label) {
    const safe = String(label ?? "J").slice(0, 6).toUpperCase();
    const svg = `
      <svg xmlns="http://www.w3.org/2000/svg" width="72" height="72" viewBox="0 0 72 72">
        <defs>
          <filter id="s" x="-20%" y="-20%" width="140%" height="140%">
            <feDropShadow dx="0" dy="2" stdDeviation="2" flood-color="#000" flood-opacity="0.35"/>
          </filter>
        </defs>
        <path filter="url(#s)" d="M36 3C23.3 3 13 13.3 13 26c0 18 23 43 23 43s23-25 23-43C59 13.3 48.7 3 36 3z"
              fill="#fff" stroke="#111" stroke-width="3" />
        <circle cx="36" cy="26" r="15" fill="#fff" stroke="#111" stroke-width="3"/>
        <text x="36" y="31" text-anchor="middle"
              font-family="Inter, system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif"
              font-size="14" font-weight="900" fill="#111">${safe}</text>
      </svg>
    `.trim();
    return "data:image/svg+xml;charset=utf-8," + encodeURIComponent(svg);
  }

  function __rcShowJumpPin(lon, lat, label) {
    try {
      const layer = __rcEnsureJumpPinLayer();
      const map = getOlMapBestEffort();
      const ol = UW?.OpenLayers;
      if (!layer || !map || !ol) return;


      try { if (__rcJumpPinMarker) layer.removeMarker(__rcJumpPinMarker); } catch {}
      __rcJumpPinMarker = null;
      try { if (__rcJumpPinTimer) clearTimeout(__rcJumpPinTimer); } catch {}
      __rcJumpPinTimer = null;


      const ll = new ol.LonLat(Number(lon), Number(lat));
      const iconUrl = __rcMakeJumpPinSvg(label);
      const size = new ol.Size(72, 72);
      const offset = new ol.Pixel(-36, -72);
      const icon = new ol.Icon(iconUrl, size, offset);
      const mk = new ol.Marker(ll, icon);
      layer.addMarker(mk);
      __rcJumpPinMarker = mk;

      __rcJumpPinTimer = setTimeout(() => {
        try {
          if (__rcJumpPinLayer && __rcJumpPinMarker) {
            __rcJumpPinLayer.removeMarker(__rcJumpPinMarker);
          }
        } catch {}
        __rcJumpPinMarker = null;
        __rcJumpPinTimer = null;
      }, 6500);
    } catch {}
  }

  async function __rcWaitFeaturesLoaded() {
    let count = 0;
    return new Promise((resolve) => {
      const interval = setInterval(() => {
        count++;
        let loading = false;
        try {
          const appState = sdk?.WmeState?.getState?.() || sdk?.WmeState?.get?.() || null;
          loading = !!(appState?.loadingFeatures || appState?.loading || appState?.isLoading);
        } catch {}
        if (!loading) {
          loading = !!(UW?.W?.app?.layout?.model?.attributes?.loadingFeatures || UW?.W?.app?.layout?.model?.get?.("loadingFeatures") || UW?.W?.app?.layout?.model?.get?.("loading") === true);
        }
        if (!loading) {
          clearInterval(interval);
          resolve(true);
          return;
        }
        if (count > 120) {
          clearInterval(interval);
          resolve(false);
        }
      }, 100);
    });
  }

  function __rcParseSegmentIdList(input) {
    const s = String(input || "").trim();
    if (!s) return null;
    if (!/^\d+(?:\s*,\s*\d+)*$/.test(s)) return null;
    const ids = s.split(",").map(x => Number(String(x).trim())).filter(n => Number.isFinite(n) && n > 0);
    if (!ids.length) return null;
    return Array.from(new Set(ids));
  }

  async function __rcFindSegmentLonLat(segmentId) {
    const id = Number(segmentId);
    if (!Number.isFinite(id) || id <= 0) return null;


    try {
      const seg = sdkSegGetById(id);
      const geom = seg?.geometry || seg?.geojson || null;
      if (geom) {
        const c = geoCenterFromAny(geom);
        if (c && Number.isFinite(c.lon) && Number.isFinite(c.lat)) return { lon: c.lon, lat: c.lat, source: "loaded" };
      }
    } catch {}


    try {
      const ww = UW?.WazeWrap || unsafeWindow?.WazeWrap || (typeof WazeWrap !== "undefined" ? WazeWrap : null);
      if (ww?.Util?.findSegment) {

        let res = null;
        try { res = await ww.Util.findSegment(safeRegionCode(), String(id)); } catch {}
        if (!res) {
          try { res = await ww.Util.findSegment(safeRegionCode(), id); } catch {}
        }
        if (!res) {
          try { res = await ww.Util.findSegment(null, id); } catch {}
        }
        if (res && Number.isFinite(Number(res.x)) && Number.isFinite(Number(res.y))) {
          return { lon: Number(res.x), lat: Number(res.y), source: "wazewrap" };
        }
      }
    } catch {}



    try {
      if (typeof GM_xmlhttpRequest === "function") {
        const url = `https://w-tools.org/api/SegmentFinder?find=${encodeURIComponent(String(id))}`;
        const res = await new Promise((resolve) => {
          GM_xmlhttpRequest({
            method: "GET",
            url,
            onload: (r) => resolve(r),
            onerror: () => resolve(null),
            ontimeout: () => resolve(null),
            timeout: 8000,
          });
        });
        if (res && (res.status === 200 || res.status === 0) && res.responseText) {
          let j = null;
          try { j = JSON.parse(res.responseText); } catch {}
          if (j && Number.isFinite(Number(j.x)) && Number.isFinite(Number(j.y))) {
            return { lon: Number(j.x), lat: Number(j.y), source: "segmentfinder" };
          }
        }
      }
    } catch {}


    try {
      if (sdk?.Segments?.findSegment) {
        const out = await sdk.Segments.findSegment({ segmentId: id });

        const geom = out?.geometry || out?.geojson || out?.segment?.geometry || null;
        const c = geom ? geoCenterFromAny(geom) : null;
        if (c && Number.isFinite(c.lon) && Number.isFinite(c.lat)) return { lon: c.lon, lat: c.lat, source: "sdk" };
      }
    } catch {}

    return null;
  }

  function safeRegionCode() {

    try {
      const href = String(location.href || "");
      const m = href.match(/\/\/[^/]+\/([a-z]{2,3})\/editor/i);
      if (m && m[1]) return m[1].toUpperCase();
    } catch {}
    try {
      const u = UW?.W?.app?.getAppRegion?.();
      if (u) return String(u).toUpperCase();
    } catch {}
    return null;
  }

  function geoCenterFromAny(geom) {
    try {

      const gj = geom?.type && geom?.coordinates ? geom : (geom?.geometry || null);
      const coords = gj?.coordinates;
      if (!coords) return null;


      if (gj.type === "LineString" && Array.isArray(coords) && coords.length) {
        let minLon = Infinity, minLat = Infinity, maxLon = -Infinity, maxLat = -Infinity;
        for (const p of coords) {
          const lon = Number(p?.[0]); const lat = Number(p?.[1]);
          if (!Number.isFinite(lon) || !Number.isFinite(lat)) continue;
          minLon = Math.min(minLon, lon); maxLon = Math.max(maxLon, lon);
          minLat = Math.min(minLat, lat); maxLat = Math.max(maxLat, lat);
        }
        if (minLon !== Infinity) return { lon: (minLon + maxLon) / 2, lat: (minLat + maxLat) / 2 };
      }


      if (gj.type === "MultiLineString" && Array.isArray(coords) && coords.length) {
        const first = coords[0];
        if (Array.isArray(first) && first.length) {
          let minLon = Infinity, minLat = Infinity, maxLon = -Infinity, maxLat = -Infinity;
          for (const p of first) {
            const lon = Number(p?.[0]); const lat = Number(p?.[1]);
            if (!Number.isFinite(lon) || !Number.isFinite(lat)) continue;
            minLon = Math.min(minLon, lon); maxLon = Math.max(maxLon, lon);
            minLat = Math.min(minLat, lat); maxLat = Math.max(maxLat, lat);
          }
          if (minLon !== Infinity) return { lon: (minLon + maxLon) / 2, lat: (minLat + maxLat) / 2 };
        }
      }
    } catch {}
    return null;
  }

  async function __rcJumpAndSelectSegments(segIds) {
    const ids = Array.from(new Set((segIds || []).map(Number).filter(n => Number.isFinite(n) && n > 0)));
    if (!ids.length) { toast("Invalid segment id."); return false; }


    const loaded = ids.filter(id => !!sdkSegGetById(id));
    if (loaded.length) {
      try { zoomToSegments(loaded); } catch {}
      setSelectionToSegmentIdsSilent(loaded);
      return true;
    }


    const firstId = ids[0];
    const loc = await __rcFindSegmentLonLat(firstId);
    if (!loc) { toast("Segment not found / not accessible."); return false; }

    const label = ids.length > 1 ? `${ids.length}X` : String(firstId).slice(-4);
    __rcShowJumpPin(loc.lon, loc.lat, label);


    try { await jumpToCoords(loc.lat, loc.lon, 18, label); } catch { await jumpToCoords(loc.lat, loc.lon, 18, label); }


    await __rcWaitFeaturesLoaded();


    const nowLoaded = ids.filter(id => !!sdkSegGetById(id));
    if (!nowLoaded.length) { toast("Segment still not loaded (try again)."); return false; }

    try { zoomToSegments(nowLoaded); } catch {}
    setSelectionToSegmentIdsSilent(nowLoaded);
    toast(ids.length === 1 ? `Jumped to segment ${firstId}` : `Jumped to ${nowLoaded.length}/${ids.length} segments`);
    return true;
  }


  async function jumpToCoords(lat, lon, zoomMaybe, label) {
    try {
      if (sdk?.Map?.setCenter && typeof sdk.Map.setCenter === "function") {
        sdk.Map.setCenter({ lon, lat });
      } else if (sdk?.Map?.zoomToExtent) {
        const d = 0.0025;
        sdk.Map.zoomToExtent({ bbox: [lon - d, lat - d, lon + d, lat + d] });
      }
      if (Number.isFinite(zoomMaybe) && typeof sdk?.Map?.setZoom === "function") {
        sdk.Map.setZoom({ zoomLevel: zoomMaybe });
      }      try { __rcShowJumpPin(lon, lat, (label || 'J')); } catch {}

      toast(`Jumped to ${fmt(lat)}, ${fmt(lon)}`);
    } catch (e) {
      console.error(e);
      toast("Jump failed (API mismatch).");
    }
  }

  function showJumpModal(prefill = "") {
    openModal({
      title: "Jump / Search",
      iconSvg: ICONS.jump,
      bodyBuilder: ({ body, close }) => {
        const inp = document.createElement("input");
        inp.className = "wmeRcInput";
        inp.placeholder = "Paste coords, permalink, Google Maps link, or segment ID…";
        inp.value = prefill || "";

        const hint = document.createElement("div");
        hint.className = "wmeRcHint";
        hint.innerHTML = `Examples: <span style="opacity:.92;">37.983, 23.728</span> • <span style="opacity:.92;">259312931</span> • <span style="opacity:.92;">WME permalink</span> • <span style="opacity:.92;">Google Maps link</span>`;

        const actions = document.createElement("div");
        actions.className = "wmeRcModalActions";

        const btnCancel = document.createElement("div");
        btnCancel.className = "wmeRcModalBtn";
        btnCancel.textContent = "Cancel";
        btnCancel.addEventListener("click", () => { try { if (typeof cleanupSoundPick === "function") cleanupSoundPick(); } catch {} close(); });

        const btnGo = document.createElement("div");
        btnGo.className = "wmeRcModalBtn primary";
        btnGo.textContent = "Go";

        const run = async () => {
          const input = (inp.value || "").trim();
          if (!input) { toast("Paste something."); return; }

          const gm = tryParseGoogleMaps(input);
          if (gm && gm.needsResolve) { toast("Google Maps short link can’t be parsed here. Open it and copy the full link with coordinates."); return; }
          if (gm && Number.isFinite(gm.lat) && Number.isFinite(gm.lon)) { await jumpToCoords(gm.lat, gm.lon, gm.zoom); close(); return; }

          const pm = tryParsePermalink(input);
          if (pm) { await jumpToCoords(pm.lat, pm.lon, pm.zoom); close(); return; }

          const cc = tryParseCoords(input);
          if (cc) { await jumpToCoords(cc.lat, cc.lon, null); close(); return; }

          const segIds = __rcParseSegmentIdList(input);
          if (segIds) {
            await __rcJumpAndSelectSegments(segIds);
            close();
            return;
          }

          toast("Couldn’t parse input (coords/permalink/google maps/segment id).");
        };

        btnGo.addEventListener("click", run);
        inp.addEventListener("keydown", (e) => {
          if (e.key === "Enter") { e.preventDefault(); run(); }
        });

        actions.appendChild(btnCancel);
        actions.appendChild(btnGo);

        body.appendChild(inp);
          body.appendChild(actions);

        requestAnimationFrame(() => {
          requestAnimationFrame(() => {
            try { inp.focus(); inp.select(); } catch {}
          });
        });
      }
    });
  }


function pickAttributesFromSegmentId(segmentId) {
  const seg = sdkSegGetById(segmentId);
  const out = {};
  if (Number.isFinite(seg?.lockRank)) out.lockRank = seg.lockRank;
  out.fwdSpeedLimit = (seg?.fwdSpeedLimit == null ? null : Number(seg.fwdSpeedLimit));
  out.revSpeedLimit = (seg?.revSpeedLimit == null ? null : Number(seg.revSpeedLimit));
  if (typeof seg?.isAtoB === "boolean") out.isAtoB = seg.isAtoB;
  if (typeof seg?.isBtoA === "boolean") out.isBtoA = seg.isBtoA;

  const lvl = getSegLevelById(segmentId);
  if (Number.isFinite(lvl)) out.level = lvl;

  return out;
}


  function pasteCfgSummary() {
    const on = [];
    if (pasteCfg.speed) on.push("speed");
    if (pasteCfg.lock) on.push("lock");
    if (pasteCfg.direction) on.push("dir");
    if (pasteCfg.elevation) on.push("elev");
    return on.length ? on.join(", ") : "none";
  }

  function canPasteAnything() {
    return !!(pasteCfg.speed || pasteCfg.lock || pasteCfg.direction || pasteCfg.elevation);
  }


async function actionCopyAttributesFromSelection(segIds) {
  const id = Number(segIds?.[0]);
  if (!Number.isFinite(id)) { toast("Cannot copy attrs: no segment."); closeAllMenus(); return; }

  const seg = sdkSegGetById(id) || segWGetById(id);
  if (!seg) { toast("Cannot copy attrs: segment not loaded."); closeAllMenus(); return; }

  attrClip = pickAttributesFromSegmentId(id);
  toast("Copied attributes");
  closeAllMenus();
}


  function sameValue(a, b) {
    const na = (a == null ? null : a);
    const nb = (b == null ? null : b);
    if (typeof na === "number" && typeof nb === "number") return Number(na) === Number(nb);
    return na === nb;
  }

  function computePasteDiff(segIds) {
    const diff = {
      total: segIds.length,
      missing: 0,
      lock: { change: 0, same: 0, target: null, mixed: false, sampleCur: null },
      speed: { change: 0, same: 0, target: null, mixed: false, sampleCur: null },
      direction: { change: 0, same: 0, target: null, mixed: false, sampleCur: null },
      elevation: { change: 0, same: 0, target: null, mixed: false, sampleCur: null },
      overall: { change: 0, same: 0 }
    };

    const userLevel = getUserLevel() ?? 1;

    const targetLockLevel = ("lockRank" in (attrClip || {})) ? lockRankToLevel(Number(attrClip.lockRank), userLevel) : null;
    diff.lock.target = targetLockLevel != null ? `L${targetLockLevel}` : "—";

    const tf = ("fwdSpeedLimit" in (attrClip || {})) ? (attrClip.fwdSpeedLimit == null ? "∅" : String(attrClip.fwdSpeedLimit)) : "—";
    const tr = ("revSpeedLimit" in (attrClip || {})) ? (attrClip.revSpeedLimit == null ? "∅" : String(attrClip.revSpeedLimit)) : "—";
    diff.speed.target = (tf === "—" && tr === "—") ? "—" : `${tf}/${tr}`;

    const tdA = ("isAtoB" in (attrClip || {})) ? (!!attrClip.isAtoB ? "1" : "0") : "—";
    const tdB = ("isBtoA" in (attrClip || {})) ? (!!attrClip.isBtoA ? "1" : "0") : "—";
    diff.direction.target = (tdA === "—" && tdB === "—") ? "—" : `A→B:${tdA} B→A:${tdB}`;

    diff.elevation.target = ("level" in (attrClip || {})) ? String(attrClip.level ?? "—") : "—";

    let sampleLock = null, sampleSpeed = null, sampleDir = null, sampleElev = null;

    for (const id of segIds) {
      const seg = sdkSegGetById(id);
      if (!seg) { diff.missing++; continue; }

      const curLockLevel = (Number.isFinite(seg.lockRank) ? lockRankToLevel(Number(seg.lockRank), userLevel) : null);
      const curSpeed = `${seg.fwdSpeedLimit == null ? "∅" : seg.fwdSpeedLimit}/${seg.revSpeedLimit == null ? "∅" : seg.revSpeedLimit}`;
      const curDir = `A→B:${seg.isAtoB ? "1" : "0"} B→A:${seg.isBtoA ? "1" : "0"}`;
      const lvlNow = getSegLevelById(id);
      const curElev = (lvlNow == null ? "—" : String(lvlNow));

      if (sampleLock == null) sampleLock = curLockLevel != null ? `L${curLockLevel}` : "—";
      if (sampleSpeed == null) sampleSpeed = curSpeed;
      if (sampleDir == null) sampleDir = curDir;
      if (sampleElev == null) sampleElev = curElev;

      const patch = { lock: false, speed: false, direction: false, elevation: false };

      if ("lockRank" in (attrClip || {})) {
        patch.lock = !sameValue(seg.lockRank, attrClip.lockRank);
      }
      if (("fwdSpeedLimit" in (attrClip || {})) || ("revSpeedLimit" in (attrClip || {}))) {
        const cf = seg.fwdSpeedLimit == null ? null : Number(seg.fwdSpeedLimit);
        const cr = seg.revSpeedLimit == null ? null : Number(seg.revSpeedLimit);
        const tf2 = ("fwdSpeedLimit" in (attrClip || {})) ? (attrClip.fwdSpeedLimit == null ? null : Number(attrClip.fwdSpeedLimit)) : cf;
        const tr2 = ("revSpeedLimit" in (attrClip || {})) ? (attrClip.revSpeedLimit == null ? null : Number(attrClip.revSpeedLimit)) : cr;
        patch.speed = (!sameValue(cf, tf2)) || (!sameValue(cr, tr2));
      }
      if (("isAtoB" in (attrClip || {})) || ("isBtoA" in (attrClip || {}))) {
        const ta = ("isAtoB" in (attrClip || {})) ? !!attrClip.isAtoB : !!seg.isAtoB;
        const tb = ("isBtoA" in (attrClip || {})) ? !!attrClip.isBtoA : !!seg.isBtoA;
        patch.direction = (!sameValue(!!seg.isAtoB, ta)) || (!sameValue(!!seg.isBtoA, tb));
      }
      if ("level" in (attrClip || {})) {
        patch.elevation = !sameValue(getSegLevelById(id), attrClip.level);
      }

      const anyChangeIfSelected = (cfg) => {
        return (cfg.lock && patch.lock) || (cfg.speed && patch.speed) || (cfg.direction && patch.direction) || (cfg.elevation && patch.elevation);
      };

      if (patch.lock) diff.lock.change++; else diff.lock.same++;
      if (patch.speed) diff.speed.change++; else diff.speed.same++;
      if (patch.direction) diff.direction.change++; else diff.direction.same++;
      if (patch.elevation) diff.elevation.change++; else diff.elevation.same++;

      if (anyChangeIfSelected(pasteCfg)) diff.overall.change++;
      else diff.overall.same++;
    }

    diff.lock.sampleCur = sampleLock ?? "—";
    diff.speed.sampleCur = sampleSpeed ?? "—";
    diff.direction.sampleCur = sampleDir ?? "—";
    diff.elevation.sampleCur = sampleElev ?? "—";

    return diff;
  }


async function actionPasteAttributesToSelection(segIds) {
  if (!attrClip) { toast("No copied attributes yet."); closeAllMenus(); return; }
  if (!canPasteAnything()) { toast("Paste selection is empty."); return; }

  let changed = 0, unchanged = 0, fail = 0;

  for (const rawId of segIds) {
    const id = Number(rawId);
    if (!Number.isFinite(id)) { fail++; continue; }

    const seg = sdkSegGetById(id);
    let segmentChanged = false;
    let segmentFailed = false;

    const patch = {};
    if (seg) {
      if (pasteCfg.lock && ("lockRank" in attrClip)) {
        if (!sameValue(seg.lockRank, attrClip.lockRank)) patch.lockRank = attrClip.lockRank;
      }

      if (pasteCfg.speed) {
        if ("fwdSpeedLimit" in attrClip) {
          const cur = (seg.fwdSpeedLimit == null ? null : Number(seg.fwdSpeedLimit));
          if (!sameValue(cur, attrClip.fwdSpeedLimit)) patch.fwdSpeedLimit = attrClip.fwdSpeedLimit;
        }
        if ("revSpeedLimit" in attrClip) {
          const cur = (seg.revSpeedLimit == null ? null : Number(seg.revSpeedLimit));
          if (!sameValue(cur, attrClip.revSpeedLimit)) patch.revSpeedLimit = attrClip.revSpeedLimit;
        }
      }

      if (pasteCfg.direction) {
        if ("isAtoB" in attrClip) {
          if (!sameValue(!!seg.isAtoB, !!attrClip.isAtoB)) patch.isAtoB = !!attrClip.isAtoB;
        }
        if ("isBtoA" in attrClip) {
          if (!sameValue(!!seg.isBtoA, !!attrClip.isBtoA)) patch.isBtoA = !!attrClip.isBtoA;
        }
      }

      const keys = Object.keys(patch);
      if (keys.length) {
        try { sdkSegUpdateSegment(id, patch); segmentChanged = true; }
        catch { segmentFailed = true; }
      }
    } else {
      if (pasteCfg.lock || pasteCfg.speed || pasteCfg.direction) segmentFailed = true;
    }

    if (pasteCfg.elevation && ("level" in (attrClip || {}))) {
      const curLvl = getSegLevelById(id);
      if (!sameValue(curLvl, attrClip.level)) {
        const ok = setSegLevelById(id, attrClip.level);
        if (ok) segmentChanged = true;
        else segmentFailed = true;
      }
    }

    if (segmentFailed) fail++;
    if (segmentChanged) changed++; else unchanged++;
  }

  if (changed === 0 && fail === 0) {
    toast(`Paste Attributes: no changes (already identical) • ${unchanged}/${segIds.length}`);
  } else {
    toast(`Pasted (${pasteCfgSummary()}) • changed ${changed}, unchanged ${unchanged}${fail ? `, failed ${fail}` : ""}`);
  }
  closeAllMenus();
}


  function tagHtml(kind, text) {
    const cls = kind === "ok" ? "ok" : (kind === "bad" ? "bad" : "warn");
    return `<span class="wmeRcTag ${cls}">${text}</span>`;
  }

  function showPasteSelectorModal(segIds) {
    if (!attrClip) { toast("Copy attributes first."); return; }

    openModal({
      title: "Paste Attributes",
      iconSvg: ICONS.gear,
      bodyBuilder: ({ body, close }) => {
        const hint = document.createElement("div");
        hint.className = "wmeRcHint";
        hint.textContent = "Select what will be pasted. Preview shows what will change for the current selection.";

        const summary = document.createElement("div");
        summary.className = "wmeRcHint";

        const grid = document.createElement("div");
        grid.className = "wmeRcToggleGrid";

        const diffBox = document.createElement("div");
        diffBox.className = "wmeRcDiffBox";

        const actions = document.createElement("div");
        actions.className = "wmeRcModalActions";

        const refreshGrid = () => {
          grid.innerHTML = "";
          grid.appendChild(mkToggle("speed", "Speed limits", "Forward/reverse speed"));
          grid.appendChild(mkToggle("lock", "Lock", "Lock rank"));
          grid.appendChild(mkToggle("direction", "Direction", "A→B / B→A"));
          grid.appendChild(mkToggle("elevation", "Elevation", "Elevation (level)"));
        };

        const mkToggle = (key, name, desc) => {
          const on = !!pasteCfg[key];
          const el = document.createElement("div");
          el.className = "wmeRcToggleBtn " + (on ? "on" : "off");
          el.innerHTML = `
            <div class="wmeRcToggleLeft">
              <div class="wmeRcToggleName">${name}</div>
              <div class="wmeRcToggleDesc">${desc}</div>
            </div>
            <div class="wmeRcPill ${on ? "on" : "off"}">${on ? "ON" : "OFF"}</div>
          `;
          el.addEventListener("click", () => {
            pasteCfg[key] = !pasteCfg[key];
            refreshGrid();
            refreshDiff();
            updateSummary();
          });
          return el;
        };

        const refreshDiff = () => {
          const d = computePasteDiff(segIds);
          diffBox.innerHTML = "";

          const overallKind = (!canPasteAnything()) ? "bad" : (d.overall.change > 0 ? "ok" : "warn");
          const overallText = (!canPasteAnything()) ? "select something" : (d.overall.change > 0 ? `${d.overall.change} will change` : "no changes");
          const overall = document.createElement("div");
          overall.className = "wmeRcDiffRow";
          overall.innerHTML = `
            <div class="wmeRcDiffLeft">
              <div class="wmeRcDiffName">Preview</div>
              <div class="wmeRcDiffDesc">${d.total} selected${d.missing ? ` • ${d.missing} not loaded` : ""}</div>
            </div>
            <div class="wmeRcDiffRight">${tagHtml(overallKind, overallText)}</div>
          `;
          diffBox.appendChild(overall);

          diffBox.appendChild(makeDiffRow("Lock", d.lock.sampleCur, d.lock.target, pasteCfg.lock, d.lock.change));
          diffBox.appendChild(makeDiffRow("Speed", d.speed.sampleCur, d.speed.target, pasteCfg.speed, d.speed.change));
          diffBox.appendChild(makeDiffRow("Direction", d.direction.sampleCur, d.direction.target, pasteCfg.direction, d.direction.change));
          diffBox.appendChild(makeDiffRow("Elevation", d.elevation.sampleCur, d.elevation.target, pasteCfg.elevation, d.elevation.change));
        };

        const makeDiffRow = (name, cur, target, enabledNow, changeCount) => {
          const row = document.createElement("div");
          row.className = "wmeRcDiffRow";

          const kind = !enabledNow ? "warn" : (changeCount > 0 ? "ok" : "warn");
          const tag = !enabledNow ? tagHtml("warn", "disabled") : (changeCount > 0 ? tagHtml("ok", `${changeCount} change`) : tagHtml("warn", "no change"));

          row.innerHTML = `
            <div class="wmeRcDiffLeft">
              <div class="wmeRcDiffName">${name}</div>
              <div class="wmeRcDiffDesc">${enabledNow ? "will apply if different" : "not included"}</div>
            </div>
            <div class="wmeRcDiffRight">${cur} → ${target} ${tag}</div>
          `;
          return row;
        };

        const updateSummary = () => {
          summary.textContent = `Selected: ${pasteCfgSummary()}`;
          const ok = !!attrClip && segIds.length > 0 && canPasteAnything();
          btnPasteNow.style.opacity = ok ? "1" : ".55";
          btnPasteNow.style.pointerEvents = ok ? "auto" : "none";
        };

        const btnNone = document.createElement("div");
        btnNone.className = "wmeRcModalBtn";
        btnNone.textContent = "Select none";
        btnNone.addEventListener("click", () => {
          pasteCfg.speed = pasteCfg.lock = pasteCfg.direction = pasteCfg.elevation = false;
          refreshGrid(); refreshDiff(); updateSummary();
        });

        const btnAll = document.createElement("div");
        btnAll.className = "wmeRcModalBtn";
        btnAll.textContent = "Select all";
        btnAll.addEventListener("click", () => {
          pasteCfg.speed = pasteCfg.lock = pasteCfg.direction = pasteCfg.elevation = true;
          refreshGrid(); refreshDiff(); updateSummary();
        });

        const btnPasteNow = document.createElement("div");
        btnPasteNow.className = "wmeRcModalBtn primary";
        btnPasteNow.textContent = "Paste Now";
        btnPasteNow.addEventListener("click", async () => {
          const ok = !!attrClip && segIds.length > 0 && canPasteAnything();
          if (!ok) return;
          close();
          await actionPasteAttributesToSelection(segIds);
        });

        const btnSave = document.createElement("div");
        btnSave.className = "wmeRcModalBtn";
        btnSave.textContent = "Save";
        btnSave.addEventListener("click", () => { toast(`Paste set: ${pasteCfgSummary()}`); close(); });

        actions.appendChild(btnNone);
        actions.appendChild(btnAll);
        actions.appendChild(btnSave);
        actions.appendChild(btnPasteNow);

          body.appendChild(summary);
        body.appendChild(grid);
        body.appendChild(diffBox);
        body.appendChild(actions);

        refreshGrid();
        refreshDiff();
        updateSummary();
      },
    });
  }

  function selectSameStreet(segIds) {
    const seg0 = sdkSegGetById(segIds[0]);
    const streetId = seg0?.primaryStreetId;
    if (streetId == null) { toast("No primary street on this segment."); return; }

    const loaded = getAllLoadedSegmentsW();
    const matchIds = [];
    for (const s of loaded) {
      if (primaryStreetIdFromW(s) == streetId) {
        const id = segIdFromW(s);
        if (Number.isFinite(id)) matchIds.push(id);
      }
    }
    if (!matchIds.length) { toast("No matching segments loaded."); return; }
    setSelectionToSegmentIds(matchIds);
  }

  function selectSameLockInView(segIds) {
    const info = getSegmentsLockInfo(segIds);
    if (info.mixed || info.currentRaw == null) { toast("Selection lock is mixed/unknown."); return; }
    const target = info.currentRaw;

    const loaded = getAllLoadedSegmentsW();
    const matchIds = [];
    for (const s of loaded) {
      const lk = lockRankFromW(s);
      if (lk != null && lk === target) {
        const id = segIdFromW(s);
        if (Number.isFinite(id)) matchIds.push(id);
      }
    }
    if (!matchIds.length) { toast("No matching lock segments loaded."); return; }
    setSelectionToSegmentIds(matchIds);
  }

  function selectSameSpeedInView(segIds) {
    const info = getSegmentsSpeedInfo(segIds);
    if (info.mixed) { toast("Selection speed is mixed."); return; }

    const seg0 = sdkSegGetById(segIds[0]);
    if (!seg0) { toast("Segment not loaded."); return; }

    const mode0 = getSegDirectionMode(seg0);
    const f0 = seg0.fwdSpeedLimit == null ? null : Number(seg0.fwdSpeedLimit);
    const r0 = seg0.revSpeedLimit == null ? null : Number(seg0.revSpeedLimit);

    const loaded = getAllLoadedSegmentsW();
    const matchIds = [];
    for (const s of loaded) {
      const id = segIdFromW(s);
      if (!Number.isFinite(id)) continue;

      const sp = speedFromW(s);
      const d = dirFromW(s);

      const isTwo = d.mode === "2";
      const isA = d.mode === "A";
      const isB = d.mode === "B";

      if (mode0 === "BOTH") {
        if (!isTwo) continue;
        if ((sp.f ?? null) === (f0 ?? null) && (sp.r ?? null) === (r0 ?? null)) matchIds.push(id);
      } else if (mode0 === "FWD") {
        if (!isA) continue;
        if ((sp.f ?? null) === (f0 ?? null)) matchIds.push(id);
      } else if (mode0 === "REV") {
        if (!isB) continue;
        if ((sp.r ?? null) === (r0 ?? null)) matchIds.push(id);
      }
    }

    if (!matchIds.length) { toast("No matching speed segments loaded."); return; }
    setSelectionToSegmentIds(matchIds);
  }

  function nodeIdFromW(segW, which) {
    const a = segW?.attributes || {};
    const key = String(which || "").toLowerCase();
    if (key === "from") return a.fromNodeID ?? a.fromNodeId ?? a.fromNode ?? segW?.fromNodeID ?? segW?.fromNodeId ?? segW?.fromNode ?? null;
    if (key === "to") return a.toNodeID ?? a.toNodeId ?? a.toNode ?? segW?.toNodeID ?? segW?.toNodeId ?? segW?.toNode ?? null;
    return a[which + "NodeID"] ?? a[which + "NodeId"] ?? a[which + "Node"] ?? segW?.[which + "NodeID"] ?? segW?.[which + "NodeId"] ?? segW?.[which + "Node"] ?? null;
  }

  function connectedSegmentsAtNode(nodeId, loadedSegs) {
    const out = [];
    for (const s of loadedSegs) {
      const fromN = nodeIdFromW(s, "from");
      const toN = nodeIdFromW(s, "to");
      if (fromN == nodeId || toN == nodeId) {
        const id = segIdFromW(s);
        if (Number.isFinite(id)) out.push({ seg: s, id });
      }
    }
    return out;
  }

  function selectConnectedChain(segIds) {
    const loaded = getAllLoadedSegmentsW();
    if (!loaded.length) { toast("No segments loaded."); return; }

    const all = new Set();

    for (const startId of segIds) {
      const startW = sdkSegGetById(startId) || segWGetById(startId);
      if (!startW) { all.add(startId); continue; }

      all.add(startId);

      const fromNode = nodeIdFromW(startW, "from");
      const toNode = nodeIdFromW(startW, "to");

      const walk = (nodeId, comingSegId) => {
        let currentNode = nodeId;
        let prevSegId = comingSegId;

        while (currentNode != null) {
          const con = connectedSegmentsAtNode(currentNode, loaded)
            .map(x => x.id)
            .filter(id => id !== prevSegId);

          if (con.length !== 1) break;

          const nextSegId = con[0];
          all.add(nextSegId);

          const nextW = sdkSegGetById(nextSegId) || segWGetById(nextSegId);
          if (!nextW) break;

          const nFrom = nodeIdFromW(nextW, "from");
          const nTo = nodeIdFromW(nextW, "to");
          const nextNode = (nFrom == currentNode) ? nTo : nFrom;

          prevSegId = nextSegId;
          currentNode = nextNode;
        }
      };

      walk(fromNode, startId);
      walk(toNode, startId);
    }

    const ids = Array.from(all).filter(Number.isFinite);
    setSelectionToSegmentIds(ids);
  }

  function showSelectModal(segIds) {
    const lockInfo = getSegmentsLockInfo(segIds);
    const speedInfo = getSegmentsSpeedInfo(segIds);

    openModal({
      title: "Select…",
      iconSvg: ICONS.select,
      bodyBuilder: ({ body, close }) => {
        const hint = document.createElement("div");
        hint.className = "wmeRcHint";
        hint.textContent = "Works on loaded segments only (current view / loaded tiles).";

        const list = document.createElement("div");
        list.className = "wmeRcActionList";

        const add = (title, desc, icon, disabled, fn) => {
          const card = document.createElement("div");
          card.className = "wmeRcActionCard" + (disabled ? " disabled" : "");
          card.innerHTML = `
            <div class="wmeRcActionLeft">
              <div class="wmeRcActionTitle"><span class="wmeRcI">${icon}</span><span>${title}</span></div>
              <div class="wmeRcActionDesc">${desc}</div>
            </div>
            <div class="wmeRcChevron">›</div>
          `;
          if (!disabled) {
            card.addEventListener("click", () => {
              try { fn(); } catch (e) { console.error(e); toast(String(e?.message || e)); }
              close();
            });
          }
          list.appendChild(card);
        };

        add("Same street", "Primary street (in view)", ICONS.street, false, () => selectSameStreet(segIds));
        add("Same lock", lockInfo.mixed ? "Disabled: selection lock is mixed" : "Same lock rank (in view)", ICONS.lock, lockInfo.mixed || lockInfo.currentRaw == null, () => selectSameLockInView(segIds));
          body.appendChild(list);
      }
    });
  }

  function roadTypeNameBestEffort(roadType) {
    const rt = (roadType == null ? null : Number(roadType));
    if (!Number.isFinite(rt)) return null;
    try {
      const rtObj = UW?.W?.model?.roadTypes?.getObjectById?.(rt) || null;
      const name = rtObj?.name || rtObj?.title || rtObj?.localizedName || null;
      if (name) return String(name);
    } catch {}
    return null;
  }

  function getStreetAndCityForSegmentId(segId) {
    let streetName = null;
    let cityName = null;
    try {
      const seg = sdkSegGetById(segId);
      const streetId = seg?.primaryStreetId;
      if (streetId != null) {
        const street = sdkStreetsGetById(streetId);
        streetName = street?.streetName ?? street?.name ?? street?.englishName ?? null;
        cityName = street?.cityName ?? null;
        const cityId = street?.cityId ?? street?.cityID ?? null;
        if (!cityName && cityId != null) {
          try {
            const c = sdkCitiesGetById(Number(cityId)) || null;
            cityName = c?.name ?? c?.cityName ?? null;
          } catch {}
        }
      }
    } catch {}

    if (!streetName) {
      try {
        streetName = getStreetNameForSegmentId(segId);
        if (streetName && String(streetName).startsWith("(")) streetName = null;
      } catch {}
    }

    if (!cityName) {
      try {
        const segW = segWGetById(segId);
        const streetIdW = segW?.primaryStreetID ?? segW?.primaryStreetId ?? segW?.attributes?.primaryStreetID ?? segW?.attributes?.primaryStreetId ?? null;
        if (streetIdW != null) {
          const st = UW?.W?.model?.streets?.getObjectById?.(Number(streetIdW));
          if (!streetName) streetName = st?.name ?? null;
          const cId = st?.cityID ?? st?.cityId ?? st?.attributes?.cityID ?? st?.attributes?.cityId ?? null;
          if (cId != null) {
            const c = sdkCitiesGetById(Number(cId)) || UW?.W?.model?.cities?.getObjectById?.(Number(cId)) || null;
            cityName = c?.name ?? c?.cityName ?? null;
          }
        }
      } catch {}
    }

    return {
      street: streetName ? String(streetName) : "(unknown)",
      city: cityName ? String(cityName) : "(unknown)",
    };
  }

  function summarizeValues(values) {
    const clean = (values || []).map(v => (v == null ? "(unknown)" : String(v)));
    const counts = new Map();
    for (const v of clean) counts.set(v, (counts.get(v) || 0) + 1);
    const uniq = Array.from(counts.keys());
    const mixed = uniq.length > 1;
    if (!mixed) return { mixed: false, text: uniq[0] || "(unknown)", detail: "" };
    const top = Array.from(counts.entries())
      .sort((a, b) => b[1] - a[1])
      .slice(0, 4)
      .map(([k, c]) => `${k} (${c})`);
    const more = counts.size > 4 ? ` +${counts.size - 4} more` : "";
    return { mixed: true, text: `mixed (${counts.size})`, detail: top.join(" • ") + more };
  }

  function buildToolsSubmenuForSegments(segIds, ctxLL) {
    const ll = ctxLL || lastLonLat;
    return [
      { label: withIcon(ICONS.select, "Select…"), sub: "Smart selection tools", onClick: () => showSelectModal(segIds) },
      { type: "sep" },
      { label: withIcon(ICONS.copy, "Copy attributes"), sub: "speed • lock • direction • elev", onClick: () => actionCopyAttributesFromSelection(segIds) },
      {
        label: withIcon(ICONS.tools, "Paste Attributes"),
        sub: attrClip ? `Selected: ${pasteCfgSummary()}` : "Nothing copied yet",
        disabled: !attrClip,
        iconButton: { html: `<span class="wmeRcI">${ICONS.gear}</span>`, title: "Choose what to paste", onClick: () => showPasteSelectorModal(segIds) },
        onClick: () => actionPasteAttributesToSelection(segIds),
      },
    ];
  }
  function getStreetNameForSegmentId(segId) {
    const seg = sdkSegGetById(segId);
    if (!seg) return "(unknown street)";
    const streetId = seg.primaryStreetId;
    if (streetId == null) return "(no street)";
    const street = sdkStreetsGetById(streetId);
    const name = street?.streetName ?? street?.name ?? street?.englishName ?? "(unknown street)";
    return String(name);
  }

  async function actionCopySelectionSummary(segIds, ll) {
    const idsTxt = segIds.join(", ");
    const streetTxt = segIds.length ? getStreetNameForSegmentId(segIds[0]) : "(no segment)";
    const pm = buildPermalink(ll, segIds);
    const gmaps = gmapsUrlFromLonLat(ll);
    const out = `Street: ${streetTxt}\nSegment ID(s): ${idsTxt}\nCoords: ${fmt(ll.lat)}, ${fmt(ll.lon)}\nGoogle Maps: ${gmaps}\nPermalink: ${pm}`;
    await setClipboard(out);
    toast("Copied: selection summary");
    closeAllMenus();
  }

  function buildCopySubmenuForSegments(segIds, ctxLL) {
    const ll = ctxLL || lastLonLat;
    const hasLL = !!(ll && Number.isFinite(ll.lat) && Number.isFinite(ll.lon));
    return [
      { label: withIcon(ICONS.street, "Street name"), sub: "From first selected", onClick: () => actionCopyStreetName(segIds[0]) },
      { label: withIcon(ICONS.copy, "Segment ID"), sub: segIds.length === 1 ? String(segIds[0]) : `${segIds.length} segments`, onClick: () => actionCopySegmentIds(segIds) },
      { type: "sep" },
      { label: withIcon(ICONS.ext, "Google Maps link"), sub: hasLL ? "https://www.google.com/maps?q=…" : "Move mouse on map first", disabled: !hasLL, onClick: () => copyCoordsGmaps(ll) },
      { type: "sep" },
      { label: withIcon(ICONS.copy, "Summary"), sub: hasLL ? "Street • IDs • Links" : "Right-click on map", disabled: !hasLL, onClick: () => actionCopySelectionSummary(segIds, ll) },
    ];
  }




  function openSegmentMenu(x, y, segIds, ctxLL) {
    try { menuState.rootLL = (ctxLL || lastLonLat || null); menuState.rootType = "segment"; } catch {}
    const ll = ctxLL || lastLonLat;
    const hasLL = !!(ll && Number.isFinite(ll.lat) && Number.isFinite(ll.lon));
    const extCtx = buildExtMenuContext("segment", { lonLat: ll, segmentIds: segIds, clientX: x, clientY: y });
    const extMenuItem = buildExtensionsMenuItem("segment", extCtx);
    let items = [
      makeNativeMenuItem("copyCoords", "Copy coordinates", { label: withIcon(ICONS.copy, "Copy coordinates"), sub: hasLL ? `${fmt(ll.lat)}, ${fmt(ll.lon)}` : "Move mouse on map first", disabled: !hasLL, onClick: () => copyCoordsLatLon(ll) }),
      makeNativeMenuItem("copyPermalink", "Copy permalink", { label: withIcon(ICONS.chain, "Copy permalink"), sub: hasLL ? "WME permalink" : "Move mouse on map first", disabled: !hasLL, onClick: () => copyPermalink(ll, segIds) }),
      makeNativeMenuItem("refreshHere", "Refresh here", { label: withIcon(ICONS.refresh, "Refresh here"), sub: hasLL ? "Reload editor at this spot" : "Move mouse on map first", disabled: !hasLL, onClick: () => refreshHere(ll, segIds) }),
      makeNativeMenuItem("pinPlace", "Pin this Place", { label: withIcon(ICONS.mapPin, "Pin this Place"), sub: hasLL ? "Save in pins panel" : "Move mouse on map first", disabled: !hasLL, onClick: () => pinThisPlace("segment", ll, segIds) }),
      makeNativeMenuItem("endpoints", "Endpoints", { label: withIcon(ICONS.endpoints, "Endpoints"), sub: segIds.length === 1 ? "Go to A/B node" : "Select 1 segment", disabled: segIds.length !== 1, submenu: true, submenuKind: "endpoints", submenuHeaderLeft: "Endpoints", submenuHeaderRight: "", getSubmenuItems: () => buildEndpointsSubmenu(segIds) }),
      makeNativeMenuItem("jumpSearch", "Jump / Search…", { label: withIcon(ICONS.jump, "Jump / Search…"), sub: "coords • permalink • google maps • segment id", onClick: () => showJumpModal("") }),
      makeNativeMenuItem("moreTools", "More", { label: withIcon(ICONS.more, "More"), sub: "Tools & utilities", submenu: true, submenuKind: "tools", submenuHeaderLeft: "Tools", submenuHeaderRight: "", getSubmenuItems: () => buildToolsSubmenuForSegments(segIds, ll) }),
    ];
    items = applyNativeMenuSettings("segment", items);
    if (extMenuItem) items.push({ type: "sep" }, extMenuItem);
    openRootMenu(x, y, "Segment", `${segIds.length} selected`, items);
  }

  function openMapMenu(x, y, ll) {
    const has = !!ll;
    try { menuState.rootLL = (ll || lastLonLat || null); menuState.rootType = "map"; } catch {}
    const extCtx = buildExtMenuContext("map", { lonLat: ll, clientX: x, clientY: y });
    const extMenuItem = buildExtensionsMenuItem("map", extCtx);
    let items = [
      makeNativeMenuItem("copyCoords", "Copy coordinates", { label: withIcon(ICONS.copy, "Copy coordinates"), sub: has ? `${fmt(ll.lat)}, ${fmt(ll.lon)}` : "Move mouse on map first", disabled: !has, onClick: () => copyCoordsLatLon(ll) }),
      makeNativeMenuItem("pinPlace", "Pin this Place", { label: withIcon(ICONS.mapPin, "Pin this Place"), sub: has ? "Save in pins panel" : "Move mouse on map first", disabled: !has, onClick: () => pinThisPlace("map", ll, null) }),
      makeNativeMenuItem("refreshHere", "Refresh here", { label: withIcon(ICONS.refresh, "Refresh here"), sub: has ? "Reload editor at this spot" : "Move mouse on map first", disabled: !has, onClick: () => refreshHere(ll, null) }),
      makeNativeMenuItem("copyPermalink", "Copy permalink", { label: withIcon(ICONS.chain, "Copy permalink"), sub: has ? "WME permalink (lat/lon/zoom)" : "Move mouse on map first", disabled: !has, onClick: () => copyPermalink(ll, null) }),
      makeNativeMenuItem("openGoogleMaps", "Open in Google Maps", { label: withIcon(ICONS.ext, "Open in Google Maps"), sub: has ? "https://www.google.com/…" : "Move mouse on map first", disabled: !has,
        onClick: () => { try { UW.open(gmapsUrlFromLonLat(ll), "_blank", "noopener,noreferrer"); } catch (e) { console.error(e); } closeAllMenus(); },
        rightButton: has ? { label: "Copy", onClick: () => copyCoordsGmaps(ll) } : null
      }),
      makeNativeMenuItem("jumpSearch", "Jump / Search…", { label: withIcon(ICONS.jump, "Jump / Search…"), sub: "coords • permalink • google maps • segment id", onClick: () => showJumpModal("") }),
    ];
    items = applyNativeMenuSettings("map", items);
    if (extMenuItem) items.push({ type: "sep" }, extMenuItem);
    openRootMenu(x, y, "Map", "No selection", items);

  }

  function openPlaceMenu(x, y, ll, placeInfo) {
    const has = !!ll;
    try { menuState.rootLL = (ll || lastLonLat || null); menuState.rootType = "place"; } catch {}
    const countTxt = placeInfo && Array.isArray(placeInfo.ids) && placeInfo.ids.length ? `${placeInfo.ids.length} selected` : "Selected";
    const extCtx = buildExtMenuContext("place", { lonLat: ll, placeIds: placeInfo?.ids, placeInfo, clientX: x, clientY: y });
    const extMenuItem = buildExtensionsMenuItem("place", extCtx);
    let items = [
      makeNativeMenuItem("copyCoords", "Copy coordinates", { label: withIcon(ICONS.copy, "Copy coordinates"), sub: has ? `${fmt(ll.lat)}, ${fmt(ll.lon)}` : "Move mouse on map first", disabled: !has, onClick: () => copyCoordsLatLon(ll) }),
      makeNativeMenuItem("pinPlace", "Pin this Place", { label: withIcon(ICONS.mapPin, "Pin this Place"), sub: has ? "Save in pins panel" : "Move mouse on map first", disabled: !has, onClick: () => pinThisPlace("place", ll, null) }),
      makeNativeMenuItem("refreshHere", "Refresh here", { label: withIcon(ICONS.refresh, "Refresh here"), sub: has ? "Reload editor at this spot" : "Move mouse on map first", disabled: !has, onClick: () => refreshHere(ll, null) }),
      makeNativeMenuItem("copyPermalink", "Copy permalink", { label: withIcon(ICONS.chain, "Copy permalink"), sub: has ? "WME permalink (lat/lon/zoom)" : "Move mouse on map first", disabled: !has, onClick: () => copyPermalink(ll, null) }),
      makeNativeMenuItem("openGoogleMaps", "Open in Google Maps", { label: withIcon(ICONS.ext, "Open in Google Maps"), sub: has ? "https://www.google.com/…" : "Move mouse on map first", disabled: !has,
        onClick: () => { try { UW.open(gmapsUrlFromLonLat(ll), "_blank", "noopener,noreferrer"); } catch (e) { console.error(e); } closeAllMenus(); },
        rightButton: has ? { label: "Copy", onClick: () => copyCoordsGmaps(ll) } : null
      }),
      makeNativeMenuItem("jumpSearch", "Jump / Search…", { label: withIcon(ICONS.jump, "Jump / Search…"), sub: "coords • permalink • google maps • segment id", onClick: () => showJumpModal("") }),
    ];
    items = applyNativeMenuSettings("place", items);
    if (extMenuItem) items.push({ type: "sep" }, extMenuItem);
    openRootMenu(x, y, "Place", countTxt, items);
  }


  function hitTestPinAtClientPoint(clientX, clientY, radiusPx = 18) {
    try {
      const t = document.elementFromPoint(clientX, clientY);
      const el = t && t.closest ? t.closest(".wmeRcPinMarker, .wmeRcPinCluster") : null;
      if (el && el.classList.contains("wmeRcPinMarker")) return { type: "pin", pinId: el.dataset.pinId };
      if (el && el.classList.contains("wmeRcPinCluster")) return { type: "cluster", ids: (el.dataset.ids || "").split(",").filter(Boolean), lon: Number(el.dataset.lon), lat: Number(el.dataset.lat) };
    } catch {}

    try {
      const mapEl = getMapContainerEl();
      if (!mapEl) return null;
      const r = mapEl.getBoundingClientRect();
      const x = clientX - r.left;
      const y = clientY - r.top;
      if (x < 0 || y < 0 || x > r.width || y > r.height) return null;

      const pins = loadPins().filter((p) => !(p.hideOnMap === true));
      let best = null;
      let bestD = Infinity;
      for (const p of pins) {
        const px = getMapPixelFromLonLat(p.lon, p.lat);
        if (!px) continue;
        const dx = px.x - x;
        const dy = px.y - y;
        const d = Math.hypot(dx, dy);
        if (d < bestD) { bestD = d; best = p; }
      }
      if (best && bestD <= radiusPx) return { type: "pin", pinId: best.id };
    } catch {}
    return null;
  }




  function zoomToCluster(lat, lon) {
    try {
      const map = getOlMapBestEffort();
      const ol = UW?.OpenLayers;
      if (map && ol && typeof map.getZoom === "function" && typeof map.setCenter === "function") {
        let ll = new ol.LonLat(lon, lat);
        try {
          const dst = map.getProjectionObject?.() || map.projection || null;
          const dstCode = String(dst?.projCode || dst?.getCode?.() || dst || "");
          const needsTransform = /900913|3857|102113|102100/i.test(dstCode) && !/4326/i.test(dstCode);
          if (needsTransform && typeof ll.transform === "function") {
            const src = new ol.Projection("EPSG:4326");
            if (dst) ll.transform(src, dst);
          }
        } catch {}
        const z0 = Number(map.getZoom());
        const z = Number.isFinite(z0) ? z0 : 12;
        map.setCenter(ll, Math.max(z + 3, 14));
        setTimeout(() => { try { renderPinsMarkers(); } catch {} }, 260);
        return;
      }
    } catch {}
    try {
      if (typeof jumpToCoords === "function") jumpToCoords(lat, lon, (getCurrentZoomBestEffort() || 12) + 2);
      else zoomToLonLat(lon, lat);
    } catch {}
  }

  function zoomToClusterItems(cl, llc, map, ol) {
    try {
      let minLon = Infinity, minLat = Infinity, maxLon = -Infinity, maxLat = -Infinity;

      for (const it of (Array.isArray(cl) ? cl : [])) {
        const llx = it?.ll;
        if (!llx) continue;
        const lon = Number(llx.lon);
        const lat = Number(llx.lat);
        if (!Number.isFinite(lon) || !Number.isFinite(lat)) continue;
        minLon = Math.min(minLon, lon);
        maxLon = Math.max(maxLon, lon);
        minLat = Math.min(minLat, lat);
        maxLat = Math.max(maxLat, lat);
      }

      if (Number.isFinite(minLon) && Number.isFinite(minLat) && Number.isFinite(maxLon) && Number.isFinite(maxLat) && typeof ol?.Bounds === "function") {
        const samePoint = Math.abs(maxLon - minLon) < 0.00002 && Math.abs(maxLat - minLat) < 0.00002;
        if (samePoint) {
          const z = (typeof map?.getZoom === "function") ? Number(map.getZoom()) : null;
          const target = Math.max((Number.isFinite(z) ? z : 12) + 2, 16);
          if (typeof map?.setCenter === "function") map.setCenter(llc, target);
        } else {
          const padLon = Math.max((maxLon - minLon) * 0.28, 0.00012);
          const padLat = Math.max((maxLat - minLat) * 0.28, 0.00012);
          const bounds = new ol.Bounds(
            minLon - padLon,
            minLat - padLat,
            maxLon + padLon,
            maxLat + padLat
          );
          if (typeof map?.zoomToExtent === "function") map.zoomToExtent(bounds, true);
        }
      } else {
        const z = (typeof map?.getZoom === "function") ? Number(map.getZoom()) : null;
        const target = Math.max((Number.isFinite(z) ? z : 12) + 3, 15);
        if (typeof map?.setCenter === "function") map.setCenter(llc, target);
      }

      setTimeout(() => { try { renderPinsMarkers(); } catch {} }, 250);
    } catch {}
  }

function onContextMenuCapture(e) {
    if (!enabled) return;
    if (e.shiftKey) return;

    if (!isMapClick(e.clientX, e.clientY)) return;



    if (shouldAllowNativeContextMenu(e)) return;
    e.preventDefault();
    e.stopPropagation();
    if (e.stopImmediatePropagation) e.stopImmediatePropagation();

    const segIds = selectedSegmentIds();
    const placeInfo = (!segIds.length ? selectedPlaceInfo() : { has: false, ids: [] });
    const ll = lonLatFromClick(e.clientX, e.clientY);

    if (segIds.length) openSegmentMenu(e.clientX, e.clientY, segIds, ll);
    else if (placeInfo && placeInfo.has) openPlaceMenu(e.clientX, e.clientY, ll, placeInfo);
    else openMapMenu(e.clientX, e.clientY, ll);
  }

  function onMouseDownCapture(e) {
    if (!enabled) return;
    if (e.button !== 2) return;
    if (e.shiftKey) return;
    if (!isMapClick(e.clientX, e.clientY)) return;


    if (shouldAllowNativeContextMenu(e)) return;
    e.preventDefault();
    e.stopPropagation();
    if (e.stopImmediatePropagation) e.stopImmediatePropagation();
  }

  ensureCSS();
  window.addEventListener("contextmenu", onContextMenuCapture, { capture: true });
  window.addEventListener("mousedown", onMouseDownCapture, { capture: true });

  let tinyFallbackToggleEl = null;
  let sidebarTabApiRef = null;
  let sidebarTabMounted = false;

  function setEnabled(v) {
    enabled = !!v;
    try {
      if (tinyFallbackToggleEl) {
        tinyFallbackToggleEl.querySelector(".wmeRcTinyDot")?.classList.toggle("off", !enabled);
        tinyFallbackToggleEl.querySelector(".wmeRcTinyTxt").textContent = enabled ? "Right-click: ON" : "Right-click: OFF";
      }
    } catch {}
  }

  function createTinyFallbackToggle() {
    if (tinyFallbackToggleEl && document.contains(tinyFallbackToggleEl)) return;
    const el = document.createElement("div");
    el.className = "wmeRcTinyToggle";
    el.innerHTML = `<div class="wmeRcTinyDot"></div><div class="wmeRcTinyTxt">Right-click: ON</div>`;
    el.addEventListener("click", () => {
      setEnabled(!enabled);
      toast(`${SCRIPT_NAME}: ${enabled ? "ON" : "OFF"}`);
      closeAllMenus();
    });
    (document.body || document.documentElement).appendChild(el);
    tinyFallbackToggleEl = el;
    setEnabled(enabled);
  }

  function mountSidebarPanelBestEffort() {
    try {
      const us = UW?.W?.userscripts || UW?.userscripts;
      const registerSidebarTab = us?.registerSidebarTab;
      if (typeof registerSidebarTab === "function") {
        if (!sidebarTabApiRef) sidebarTabApiRef = registerSidebarTab(String(SCRIPT_ID || "wme-rightclick-functions-3"));
        const { tabLabel, tabPane } = sidebarTabApiRef || {};
        if (tabLabel && !tabLabel.dataset.wmeRcInit) {
          tabLabel.dataset.wmeRcInit = "1";
          tabLabel.textContent = "RC";
          tabLabel.title = SCRIPT_NAME;
        }
        if (tabPane && !sidebarTabMounted) {
          sidebarTabMounted = true;
          const wrap = buildRcSidebarPanel();
          tabPane.appendChild(wrap);

          if (tinyFallbackToggleEl) tinyFallbackToggleEl.remove();
          tinyFallbackToggleEl = null;
        }
        return true;
      }
    } catch (e) {
      console.warn("Sidebar tab mount failed:", e);
    }

    try {
      const ww = UW?.WazeWrap;
      const addTab = ww?.Interface?.AddScriptTab;
      if (typeof addTab === "function") {
        const tab = addTab(SCRIPT_NAME);
        if (tab && tab.appendChild) {
          const wrap = buildRcSidebarPanel();
          tab.appendChild(wrap);

          if (tinyFallbackToggleEl) tinyFallbackToggleEl.remove();
          tinyFallbackToggleEl = null;
          return true;
        }
      }
    } catch (e) {
      console.warn("Sidebar mount failed:", e);
    }

    createTinyFallbackToggle();
    return false;
  }

  async function initSdk() {
    try {
      sdk = UW.getWmeSdk({ scriptId: SCRIPT_ID, scriptName: SCRIPT_NAME });
    } catch (err) {
      console.warn(`[${SCRIPT_NAME}] getWmeSdk failed`, err);
      mountSidebarPanelBestEffort();
      return;
    }

    try { maybeShowChangelogOnce(); } catch {}

    try {
      const lvl = getUserLevel();
      if (Number.isFinite(lvl)) cachedUserLevel = lvl;
    } catch {}

    try {
      sdk.Events?.on?.({
        eventName: "wme-map-mouse-move",
        eventHandler: (ev) => {
          if (ev && isFinite(ev.lon) && isFinite(ev.lat)) lastLonLat = { lon: ev.lon, lat: ev.lat };
        },
      });
    } catch {}



    try { ensurePinsPanel(); } catch {}
    try { renderPinsMarkers(); } catch {}
    try {
      let tries = 0;
      const iv = setInterval(() => {
        tries++;
        try { ensurePinsOlLayer(); } catch {}
        try { startPinsMapSync(); } catch {}
        if (pinsOlLayer || tries > 20) { try { clearInterval(iv); } catch {} }
      }, 750);
    } catch {}
    try { startReminderLoop(); } catch {}


        const tryMount = () => mountSidebarPanelBestEffort();
    const t = setInterval(() => {
      if (document.body) {
        tryMount();
        clearInterval(t);
      }
    }, 450);
    setTimeout(() => clearInterval(t), 8000);
  }

  const publicApi = Object.freeze({
    version: SCRIPT_VERSION,
    registerMenuProvider,
    unregisterMenuProvider,
    listMenuProviders,
    getCurrentContext: getCurrentContextSnapshot,
    closeMenu: closeAllMenus,
    openMenu: openRootMenu,
  });

  UW.WmeRightClickFunctions = publicApi;
  UW.WmeRightClickFunctionsApi = publicApi;

  if (typeof UW.dispatchEvent === "function" && typeof UW.CustomEvent === "function") {
    try {
      UW.dispatchEvent(new UW.CustomEvent("wme-rightclick-functions:ready", {
        detail: { api: publicApi, version: SCRIPT_VERSION },
      }));
    } catch {}
  }

  if (UW.SDK_INITIALIZED?.then) UW.SDK_INITIALIZED.then(initSdk);
  else {
    const t = setInterval(() => {
      if (UW.SDK_INITIALIZED?.then) { clearInterval(t); UW.SDK_INITIALIZED.then(initSdk); }
    }, 250);
    setTimeout(() => clearInterval(t), 12000);
  }

})();