WME - RightClick Functions

Right-click functionality

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

// ==UserScript==
// @name         WME - RightClick Functions
// @author       GreekCaptain
// @version      1.0.2
// @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://greasyfork.org/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://greasyfork.org/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.2";
  const CHANGELOG_SEEN_KEY = `${SCRIPT_ID}:changelogSeenVersion:v2`;
  const CHANGELOG_ITEMS = [
    "New: Visual and accesibility changes",
  ];


  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;

  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`;

  // --- Pin name validation ---
  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;

  // ---------- Lightweight scheduler (performance) ----------
  // Replaces some hot setInterval loops with an adaptive timeout loop
  // that slows down when the tab is hidden and can be gated by an `active()` predicate.
  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() {
      // Slow down when tab is hidden to cut CPU usage.
      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());
    }

    // Kick off
    t = setTimeout(tick, baseMs);

    // Optional: run once when coming back to foreground
    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;

      // Debounced write to avoid hammering localStorage on rapid UI toggles.
      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; // default ON
    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; // default
    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;

  // Pins bubble minimize state (strict-mode safe declarations)
  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(); // pinId -> timeoutId
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(); // pinId -> 0/1/2
  let _pinCountdownTipModes = new Map(); // pinId -> 0/1/2 (tooltip only)
  let _pinRowEls = new Map();
  let pinsMarkersEl = null;
  let pinsOlLayer = null;
  const pinsOlMarkers = new Map(); // pinId -> OpenLayers.Marker
  const pinsOlClusterMarkers = new Map(); // clusterId -> OpenLayers.Marker
  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);}

      /* Pin click toast (bottom-center) */
      .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);}

      /* Reminder notice stack: bottom-right, keep clear of WME right-side controls */
      .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;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);}
      /* Pins Manager Lightbox */
      .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;}
      /* Sound menu portal (so it isn't clipped by modal overflow) */
      .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:#111;}
      .wmeRcSideCard{background:#fff;border:1px solid rgba(0,0,0,.10);border-radius:12px;box-shadow:0 10px 30px rgba(0,0,0,.08);padding:10px 10px;}
      .wmeRcSideRow{display:flex;align-items:center;justify-content:space-between;gap:12px;}
      .wmeRcSideTitle{font-weight:800;}
      .wmeRcSideSub{font-size:12px;opacity:.65;margin-top:2px;}
      .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;}
/* Pins bubble (minimized state) */
.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);}
}

/* Pins panel open/close animation helpers */
.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;}
            /* Keep the current folder header visible while scrolling through many pins */
            .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);}


      /* Map Pins markers (map) */
      .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); }
      }
      /* Reminder picker (clock) */
      .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%
  );
}

/* FIX8: remove borders around the color picker controls (SV square + hue bar) */
.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;}

      /* Custom date/time pickers */
      .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);}}

      /* Calendar */
      .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);}

      /* Time picker */
      .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);}

      /* Time picker (clock-based, no scrollbar) */
      .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;}


      /* Custom quick preset pop */
      .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;}

      /* colored left edge */
      .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;
      }

      /* inside glow from the left edge (pin color) */
      .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);
  }


  // Inline toggle click policy
// - Clicking the row/label should NOT toggle.
// - Clicking the actual switch (track/thumb) SHOULD toggle.
// We implement this via delegated capture click handling and manual toggling,
// to avoid browser/label retargeting edge-cases.
(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;

      // Only allow toggle when clicking the actual switch.
      if (isOnSwitch(e)) {
        // Prevent the label default toggle (and double-toggles).
        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;
      }

      // Clicked elsewhere on the row/label -> do nothing (and prevent default label toggle).
      e.preventDefault();
      e.stopPropagation();
    } catch {}
  };

  // Capture so we beat the label default behavior.
  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; // left click only
          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") {
      // In panel mode we never show the bubble.
      try { _hidePinsBubble(true); } catch {}
      // If a previous bubble-minimize left the panel hidden, unhide it.
      try {
        if (pinsPanelEl.style && pinsPanelEl.style.display === "none") pinsPanelEl.style.display = "block";
      } catch {}
      try { savePinsPanelMinimized(false); } catch {}
      return;
    }

    // Bubble mode
    if (minimized) {
      const mapEl = pinsPanelEl.parentElement || _getMapElForPins();
      try { _ensurePinsBubble(mapEl); } catch {}
      try { pinsPanelEl.style.display = "none"; } catch {}
      try { _showPinsBubble(false); } catch {}
    } else {
      // Not minimized: show panel, hide bubble
      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 {}
}

// Resolve the WME map container used for pins UI and bubble.
function _getMapElForPins() {
  try { return getMapContainerEl(); } catch { return null; }
}

function _isPinsPanelMinimizedNow() {
  try {
    // IMPORTANT: "collapsed" is a UI state (header-only) and should NOT be treated as
    // "minimized-to-bubble". The bubble should only show when the panel is actually
    // minimized (saved flag) or hidden.
    if (!pinsPanelEl || !document.contains(pinsPanelEl)) return true;

    // If we explicitly saved "minimized", treat it as minimized even during animations.
    try { if (loadPinsPanelMinimizedRaw() === true) return true; } catch {}

    // Fallback: if the element is hidden, it's minimized.
    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;
    }
    // During open animation we force-hide to prevent overlap (but never hide while minimized-to-bubble).
    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 it was a tap (not a drag), restore the panel.
    if (!moved) {
      try { _restorePinsPanelFromBubble(); } catch {}
      return;
    }

    // Ignore the synthetic click right after a drag.
    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);

    // If we just restored from bubble, a short force-hide window may still be active.
    // Clear it so the bubble cannot be hidden while we are minimizing.
    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;

    // Panel-mode collapse should never fade away: hard-reset any "bubble" animation artifacts.
    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);

    // Remember current height so expanding can restore it.
    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 {}

    // Collapse down to header height (still a panel, not a bubble).
    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 {}

    // Restore previous height (or last saved size) if we can.
    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);

    // Default bubble position: wherever the panel currently is.
    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 {}

    // Animate panel out, then hide.
    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;

    // If a previous minimize-to-bubble animation is still pending, cancel its delayed hide.
    try { if (_pinsPanelMinimizeHideT) { clearTimeout(_pinsPanelMinimizeHideT); _pinsPanelMinimizeHideT = 0; } } catch {}

    // Force-hide the bubble during the opening sequence so it never overlaps the header.
    try { _pinsBubbleForceHideUntil = _pinsNow() + 600; } catch { _pinsBubbleForceHideUntil = Date.now() + 600; }

    // Restore the panel at its last saved panel position (not the bubble).
    // Fallback: if no saved panel position exists yet, expand near the bubble.
    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`;

          // Only persist if we had to clamp an existing saved position (viewport changed).
          try {
            if (target.left !== left || target.top !== top) {
              localStorage.setItem(PIN_POS_KEY, JSON.stringify({ left, top }));
            }
          } catch {}
        }
      }
    } catch {}

    // Prevent the auto "avoid left dock" logic from overriding the bubble placement.
    try { _pinsPanelSuppressDockUntil = _pinsNow() + 900; } catch {}

    // Hide bubble immediately so it does not sit on top of the opening panel.
    try { _hidePinsBubble(true); } catch {}

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

    // Render expanded content first (so height/scroll are correct).
    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 {}
                    try {
                      let minLon = Infinity, minLat = Infinity, maxLon = -Infinity, maxLat = -Infinity;
                      for (const it of cl) {
                        const llx = it?.ll;
                        if (!llx) continue;
                        minLon = Math.min(minLon, Number(llx.lon));
                        maxLon = Math.max(maxLon, Number(llx.lon));
                        minLat = Math.min(minLat, Number(llx.lat));
                        maxLat = Math.max(maxLat, Number(llx.lat));
                      }

                      if (isFinite(minLon) && isFinite(minLat) && isFinite(maxLon) && isFinite(maxLat) && typeof ol.Bounds === "function") {
                        const b = new ol.Bounds(minLon, minLat, maxLon, maxLat);
                        try { if (typeof b.scale === "function") b.scale(1.7); } catch {}
                        if (typeof map.zoomToExtent === "function") map.zoomToExtent(b, true);
                        setTimeout(() => {
                          try {
                            if (typeof map.setCenter === "function") map.setCenter(llc, 15);
                          } catch {}
                        }, 90);
                      } 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 {}
                  });

                  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 {}
                        zoomToCluster(Number(mk.clusterLat), Number(mk.clusterLon));
                      }, 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 {}
              }
            }// Remove stale markers
      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;
  // Adaptive loop: slower when tab hidden + runs only when pins are actually visible.
  pinsMarkersIntervalId = _createAdaptiveLoop(() => { try { updatePinsMarkersPositions(); } catch {} }, 400, {
    active: () => {
      try {
        // Only run when we have visible markers layer and at least one marker.
        if (!pinsMarkersEl || !document.contains(pinsMarkersEl)) return false;
        if (!pinMarkerEls || pinMarkerEls.size === 0) return false;
        // If user turned off pins on map, don't tick.
        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; // must be docked left-ish
      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);

// Clicking the title should behave exactly like the collapse/minimize control,
// but without triggering the header drag. Use pointer events (capture) to be robust.
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 {}
    // Ensure the collapse icon/label stays in sync.
    try { _syncPinsPanelCollapseBtn && _syncPinsPanelCollapseBtn(); } catch {}
  } catch {}
};

const _onPinsTitleDown = (ev) => {
  try {
    // Left-click or touch only.
    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);

    // Treat as a tap/click only if there was no drag.
    if (dist <= 6 && dt <= 650) _togglePinsFromTitle();

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

// Capture so a drag handler on the header can't swallow it.
titleWrap.addEventListener("pointerdown", _onPinsTitleDown, true);
titleWrap.addEventListener("pointerup", _onPinsTitleUp, true);
titleWrap.addEventListener("pointercancel", () => { _pinsTitleTap.down = false; }, true);

// Fallback for environments without Pointer Events.
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 {
    // Allow the list to scroll. We only stop propagation so the map below doesn't zoom/pan while
    // you scroll inside the panel.
    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) {
            // offsetTop is relative to the scrolling container's content
            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);


      // Minimize behavior

      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";

      // ----- Left: folders -----
      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);

      // ----- Right: pins -----
      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();

        // Default folder first
        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";

          // Rename (not for default)
          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);
              // openRemoveFolderModal triggers re-render via existing flows; also refresh after a tick
              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);

        // Header: folder name + quick actions
        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());
        // make it an X-like feel by rotating chevron
        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";

          // Move to folder (select)
          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 });
            // stay on current folder view; just refresh
            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() {
        // if selected folder removed, fall back
        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"; // always start on In tab
  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); // 0 at +x, CCW
        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); // 0 at +x, CCW
        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; // monday=0
            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";          // Auto hand selection (no Hour/Minute toggle)
          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"; // H or M
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; // 0..11, where 0=12
            const p = timeToParts(atTime || "00:00");
            const curH = p.hh;

            const candA = (h12 === 0 ? 12 : h12) % 12; // 0..11
            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; // signed shortest step
      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 {}
  }

  // Adaptive loop: slows down when tab is hidden to reduce CPU, but catches up immediately on focus/visible.
  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; // allow newlines

        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: `What’s new — 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;

      // If the click is on UR panel/UI overlay, it's not a map click for our purposes.
      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;

        // Generic UI panels/cards/menus — let native WME handle these.
        if (el.closest(".wmeRcMenu, .wmeRcModal, .wmeRcPinsPanel")) return false;
        if (el.closest("wz-card, [role='dialog'], [role='menu'], .menu, .dropdown, .panel, .sidebar, .tooltip")) return false;
      }

      // A real map click typically hits the map canvas or something within the map container.
      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 [];
  }

  // --- Place / Venue / POI selection detection (header only) ---
  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 ??
        ""
      );

      // Common labels across builds/locales
      if (/venue|place|poi|point\s*of\s*interest/i.test(t)) return true;

      // Explicit id hints
      if ("venueId" in sel || "placeId" in sel || "poiId" in sel || "venueID" in sel || "placeID" in sel) return true;

      // Numeric ObjectType constants when available
      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 {}

      // Nested selection containers
      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() {
    // Returns: { has: boolean, ids: number[] }
    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 {}

    // Best-effort legacy fallback (some builds don't expose venue ids in selection)
    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: [] };
  }


  // If an Update Request (UR) / Issue is active, let WME show its native right-click menu.
  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;

      // Some builds use generic "issue"/"problem" types; look for UR-ish hints.
      if (/issue|problem/i.test(t) && /update|request|\bur\b/i.test(t)) return true;

      // Some selections expose explicit ids.
      if ("updateRequestId" in sel || "urId" in sel || "issueId" in sel || "problemId" in sel) return true;

      // Try numeric ObjectType constants if present.
      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() {
    // Robust, DOM-version-tolerant detection:
    // If the UR panel is open, it almost always contains the action buttons below.
    // We avoid broad `innerText` reads from generic "sidebar" nodes because WME's DOM varies across builds.
    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; }
      };

      // 0) Fast path: the UR side-panel is a `wz-card` with class `mapUpdateRequest` (shadow DOM inside).
      // The action buttons live in the open shadow root, so querying `button` from `document` won't see them.
      // Detect the host element instead.
      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;
      };

      // Strong UR-specific UI controls (English + Greek best effort)
      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;

      // Header title check (Update request / Αίτημα ενημέρωσης)
      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 {}

      // Fallback: narrow scan of likely panel containers (visible only)
      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 {
      // elementFromPoint() can miss overlay icons when a transparent map layer sits on top.
      // elementsFromPoint() gives us the full stack at the cursor.
      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;

        // Strong signals: data-* ids that WME often uses for UR/problem objects.
        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();

        // Images/icons
        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;
        }

        // SVG icons
        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;
        }

        // Textual identifiers
        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();

        // UR-ish text + marker-ish hints, OR very strong "problem/issue/report" words on an icon element
        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;
        }

        // CSS background icons
        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;
      };

      // Check the stack + a few ancestor levels for each element.
      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;

      // Primary: the UR panel host is a <wz-card> with class "mapUpdateRequest" (Shadow DOM inside).
      const host = target.closest("wz-card.mapUpdateRequest, .mapUpdateRequest");
      if (host) return true;

      // Fallbacks: some builds rely on aria labels / testids.
      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 {
      // Shift+RightClick always yields WME native menu.
      if (e && e.shiftKey) return true;

      // Always allow WME native menu inside the Update Request (UR) sidebar panel.
      if (e && isInUpdateRequestPanel(e.target)) return true;

      // Native menu ONLY when the right-click is actually on a UR marker.
      if (e && isUrMarkerAtPoint(e.clientX, e.clientY)) return true;

      // Weak fallback: if selection is an UR and the element under cursor still looks marker-ish.
      // This avoids "sticking" native mode when you right-click empty map while UR panel is open.
      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 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 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?.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 ?? 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 {}
    }
    /* visible-area correction disabled */
    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/?mlat=${fmt(ll.lat)}&mlon=${fmt(ll.lon)}#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;
  }


  // ===== Permalink settings (used by Copy permalink / Refresh here) =====
  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);

        // Zoom lock toggle + input
        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);

        // Include layers toggle
        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) {
    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));
    }

    // Optional: include current layer params (if present)
    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 qs = params.toString();
    return qs ? `${EDITOR_BASE}?${qs}` : EDITOR_BASE;
  }

  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();
  }
  async function copyPermalink(ll, segIds = null) {
    const url = buildPermalink(ll, segIds);
    await setClipboard(url);
    toast("Copied: permalink");
    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;

      // Fast check for common Google Maps hosts/paths
      const looksLikeGmaps = /google\.[^\s/]+\/maps|maps\.google\.|\/maps\b|maps\.app\.goo\.gl|goo\.gl\/maps/i.test(s);
      if (!looksLikeGmaps) return null;

      // Pattern: .../@lat,lon,17z
      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 };
      }

      // Query params: q=lat,lon or ll=lat,lon
      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 };
        }
      }

      // Some links embed coordinates in the path even without @
      const cc2 = tryParseCoords(s);
      if (cc2) return { lat: cc2.lat, lon: cc2.lon, zoom: null };

      // Short links can't be resolved offline (needs a network redirect)
      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 };
  }
  // -------------------------------
  // Jump / Search helpers (segment lookup + custom jump pin)
  // -------------------------------
  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") {
        // keep existing
        return __rcJumpPinLayer;
      }

      // Marker layer (topmost)
      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;

      // remove previous
      try { if (__rcJumpPinMarker) layer.removeMarker(__rcJumpPinMarker); } catch {}
      __rcJumpPinMarker = null;
      try { if (__rcJumpPinTimer) clearTimeout(__rcJumpPinTimer); } catch {}
      __rcJumpPinTimer = null;

      // build marker
      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++;
        const 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;

    // 1) If already loaded, use its geometry center immediately
    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 {}

    // 2) Try WazeWrap SegmentFinder (works even when not loaded)
    try {
      const ww = UW?.WazeWrap || unsafeWindow?.WazeWrap || (typeof WazeWrap !== "undefined" ? WazeWrap : null);
      if (ww?.Util?.findSegment) {
        // some builds want (regionCode, segId); if regionCode is omitted, it still works in most cases
        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 {}


    // 3) Try SegmentFinder directly (w-tools) via GM_xmlhttpRequest
    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 {}

    // 3) Last resort: if SDK has findSegment, try it (API varies)
    try {
      if (sdk?.Segments?.findSegment) {
        const out = await sdk.Segments.findSegment({ segmentId: id });
        // best-effort parse for returned geometry/center
        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() {
    // Best-effort; if unknown, WazeWrap often still succeeds.
    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 {
      // SDK often returns GeoJSON-like { type, coordinates }
      const gj = geom?.type && geom?.coordinates ? geom : (geom?.geometry || null);
      const coords = gj?.coordinates;
      if (!coords) return null;

      // LineString -> average points
      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 };
      }

      // MultiLineString -> first line
      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; }

    // If already loaded: zoom + select
    const loaded = ids.filter(id => !!sdkSegGetById(id));
    if (loaded.length) {
      try { zoomToSegments(loaded); } catch {}
      setSelectionToSegmentIdsSilent(loaded);
      return true;
    }

    // Not loaded: locate first segment, jump, wait, then select
    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);

    // Jump to the located position; z18 like Enhanced Search
    try { await jumpToCoords(loc.lat, loc.lon, 18, label); } catch { await jumpToCoords(loc.lat, loc.lon, 18, label); }

    // Wait for features to load
    await __rcWaitFeaturesLoaded();

    // Now try selecting
    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);
  const segW = UW?.W?.model?.segments?.getObjectById?.(id) || null;
  if (!seg && !segW) { 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 || {};
    return a[which + "NodeID"] ?? a[which + "NodeId"] ?? a[which + "Node"] ?? segW?.[which + "NodeID"] ?? 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 = UW?.W?.model?.segments?.getObjectById?.(startId) || null;
      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 = UW?.W?.model?.segments?.getObjectById?.(nextSegId) || null;
          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 = UW?.W?.model?.cities?.getObjectById?.(Number(cityId));
            cityName = c?.name ?? 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 = UW?.W?.model?.cities?.getObjectById?.(Number(cId));
            cityName = c?.name ?? 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/…" : "Right-click on map", disabled: !hasLL, onClick: () => copyCoordsGmaps(ll), rightButton: hasLL ? { label: "Open", onClick: () => { try { UW.open(gmapsUrlFromLonLat(ll), "_blank", "noopener,noreferrer"); } catch (e) { console.error(e); } closeAllMenus(); } } : null },
      { 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 dirInfo = dirSummaryForSelection(segIds);
    const dirSub = dirInfo.mixed ? "mixed" : modeLabel(dirInfo.mode);

    const ll = ctxLL || lastLonLat;
    const hasLL = !!(ll && Number.isFinite(ll.lat) && Number.isFinite(ll.lon));

    openRootMenu(x, y, "Segment", `${segIds.length} selected`, [
      { label: withIcon(ICONS.copy, "Copy coordinates"), sub: hasLL ? `${fmt(ll.lat)}, ${fmt(ll.lon)}` : "Move mouse on map first", disabled: !hasLL, onClick: () => copyCoordsLatLon(ll) },
      { label: withIcon(ICONS.chain, "Copy permalink"), sub: hasLL ? "WME permalink" : "Move mouse on map first", disabled: !hasLL, onClick: () => copyPermalink(ll, segIds) },
      { label: withIcon(ICONS.refresh, "Refresh here"), sub: hasLL ? "Reload editor at this spot" : "Move mouse on map first", disabled: !hasLL, onClick: () => refreshHere(ll, segIds) },
      { 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) },
      { type: "sep" },
      { 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) },
      { type: "sep" },
      { label: withIcon(ICONS.jump, "Jump / Search…"), sub: "coords • permalink • google maps • segment id", onClick: () => showJumpModal("") },
      { label: withIcon(ICONS.more, "More"), sub: "Tools & utilities", submenu: true, submenuKind: "tools", submenuHeaderLeft: "Tools", submenuHeaderRight: "", getSubmenuItems: () => buildToolsSubmenuForSegments(segIds, ll) },
]);
  }

  function openMapMenu(x, y, ll) {
    const has = !!ll;
    try { menuState.rootLL = (ll || lastLonLat || null); menuState.rootType = "map"; } catch {}

    openRootMenu(x, y, "Map", "No selection", [
      { label: withIcon(ICONS.copy, "Copy coordinates"), sub: has ? `${fmt(ll.lat)}, ${fmt(ll.lon)}` : "Move mouse on map first", disabled: !has, onClick: () => copyCoordsLatLon(ll) },
      { 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) },
      { label: withIcon(ICONS.refresh, "Refresh here"), sub: has ? "Reload editor at this spot" : "Move mouse on map first", disabled: !has, onClick: () => refreshHere(ll, null) },
      { label: withIcon(ICONS.chain, "Copy permalink"), sub: has ? "WME permalink (lat/lon/zoom)" : "Move mouse on map first", disabled: !has, onClick: () => copyPermalink(ll, null) },
      { 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
      },
      { type: "sep" },
      { label: withIcon(ICONS.jump, "Jump / Search…"), sub: "coords • permalink • google maps • segment id", onClick: () => showJumpModal("") },
    ]);

  }

  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";

    // Keep the same actions as the Map menu; only change the header to "Place".
    openRootMenu(x, y, "Place", countTxt, [
      { label: withIcon(ICONS.copy, "Copy coordinates"), sub: has ? `${fmt(ll.lat)}, ${fmt(ll.lon)}` : "Move mouse on map first", disabled: !has, onClick: () => copyCoordsLatLon(ll) },
      { 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) },
      { label: withIcon(ICONS.refresh, "Refresh here"), sub: has ? "Reload editor at this spot" : "Move mouse on map first", disabled: !has, onClick: () => refreshHere(ll, null) },
      { label: withIcon(ICONS.chain, "Copy permalink"), sub: has ? "WME permalink (lat/lon/zoom)" : "Move mouse on map first", disabled: !has, onClick: () => copyPermalink(ll, null) },
      { 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
      },
      { type: "sep" },
      { label: withIcon(ICONS.jump, "Jump / Search…"), sub: "coords • permalink • google maps • segment id", onClick: () => showJumpModal("") },
    ]);
  }


  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 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;

  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 ww = UW?.WazeWrap;
      const addTab = ww?.Interface?.AddScriptTab;
      if (typeof addTab === "function") {
        const tab = addTab(SCRIPT_NAME);
        if (tab && tab.appendChild) {
          const wrap = document.createElement("div");
          wrap.className = "wmeRcSideWrap";
          wrap.innerHTML = `
            <div class="wmeRcSideCard">
              <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>
            </div>

<div class="wmeRcSideCard">
  <div class="wmeRcSideRow">
    <div>
      <div class="wmeRcSideTitle">Pins on map</div>
      <div class="wmeRcSideSub">Show / hide all pinned markers</div>
    </div>
    <div class="wmeRcSwitch ${getPinsLayerVisible() ? "on" : ""}" id="wmeRcPinsLayerSwitch">
      <div class="wmeRcKnob"></div>
    </div>
  </div>
</div>
`;
          tab.appendChild(wrap);

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


const ps = wrap.querySelector("#wmeRcPinsLayerSwitch");
if (ps) {
  const applyPinsSwitch = () => {
    const v = getPinsLayerVisible();
    ps.classList.toggle("on", v);
  };
  applyPinsSwitch();
  ps.addEventListener("click", () => {
    const next = !getPinsLayerVisible();
    setPinsLayerVisible(next);
    ps.classList.toggle("on", next);
    toast(`Pins on map: ${next ? "ON" : "OFF"}`);
  });
}


          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);
  }

  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);
  }

})();