{
  "version": 3,
  "sources": ["/cdn/shop/t/7/assets/design-my-book.js"],
  "sourcesContent": ["/* design-my-book.js */\n\n/* =========================\n   MODE / NAVIGATION FIXES\n   ========================= */\n\n/* session ID + per-session book counter for PDF filenames */\nconst DMB_SESSION_ID = Date.now().toString(36);\nlet dmbBookCounter = 0;\n\n/* Polaroid frame geometry \u2014 uniform white border on three sides + a thicker\n   bottom strip (the classic look). Returns inset values in the object's own\n   coordinate units (= builder px; multiply by scale for the PDF canvas).\n   Declared at top level so BOTH IIFEs (builder render + PDF render) can use it. */\nfunction dmbPolaroidInsetVals(w, h) {\n  w = w || 0; h = h || 0;\n  var pad = Math.max(6, Math.min(w, h) * 0.07);\n  var bottom = Math.max(pad * 2.4, h * 0.17);\n  return { top: pad, left: pad, right: pad, bottom: bottom };\n}\n\n\nconst DMB_MAP_LIBRARY = {\n  lebanon: `\n    <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 120 240\" class=\"dmb-country-map\" data-map-name=\"lebanon\">\n      <path d=\"M55 10 L75 20 L80 40 L72 60 L85 85 L78 110 L88 140 L80 170 L70 205 L58 230 L45 220 L42 190 L35 160 L30 130 L36 100 L32 75 L40 50 L48 25 Z\"></path>\n    </svg>\n  `,\n  afghanistan: `\n    <svg\n      xmlns:mapsvg=\"http://mapsvg.com\"\n      xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n      xmlns:cc=\"http://creativecommons.org/ns#\"\n      xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"\n      xmlns:svg=\"http://www.w3.org/2000/svg\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      mapsvg:geoViewBox=\"60.517000 38.490877 74.889862 29.377200\"\n      version=\"1.1\"\n      id=\"svg2\"\n      width=\"699.60461\"\n      height=\"532.77234\">\n      <path\n        id=\"AF-PIA\"\n        title=\"Paktya\"\n        \n        d=\"m 456.68715,266.76119 -2.93,-1.73 -0.34,-0.13 -0.3,-0.07 -0.27,-0.02 -0.26,0.08 -0.29,0.19 -1.69,2.07 -2.7,1.69 -0.54,0.2 -0.58,-0.02 -0.17,-0.21 -0.01,-0.39 0.06,-0.43 0.03,-0.36 -0.07,-0.29 -0.17,-0.2 -0.38,-0.22 -0.44,-0.17 -1.65,-0.4 -2.16,-0.16 -0.6,0.06 -0.82,-0.01 -0.47,0.08 -0.46,0.15 -1.07,0.67 -0.31,0.32 -0.39,1.18 -0.72,1.5 -0.5,0.7 -0.82,0.93 -0.54,0.48 -0.59,0.34 -1.89,1.66 -0.27,0.28 -2.59,2.75 -0.35,0.71 -0.27,0.36 -1.54,1.06 -4.85,2.28 -4.71,3.54 -0.66,0.51 -0.6,0.63 -0.76,1.56 -0.54,1.6 -0.93,1.68 -0.64,0.56 -0.32,0.22 -0.56,0.28 -0.65,0.22 -0.76,0.12 -0.58,0 -1,-0.42 -0.76,-0.14 -7.19,-0.06 -0.17,2.12 0.09,2.06 0.12,1.08 0.19,0.7 0.58,1.09 0.43,1.05 0.11,0.57 -0.06,0.5 -1.18,2.61 -0.28,1 3.54,-1.31 1.25,-0.21 0.91,0.51 0.51,0.1 0.36,-0.09 0.37,-0.16 0.27,-0.03 0.25,0.04 1.08,0.49 0.27,0.21 0.19,0.21 0.52,1.02 0.77,1.06 0.16,0.32 0.07,0.39 0.01,0.6 -0.2,2.74 0.04,1.18 0.42,3.41 -0.02,0.31 -0.05,0.28 -0.01,0.49 0.19,0.64 0.25,1.32 0.23,0.38 0.14,0.03 0.16,-0.01 0.31,-0.05 0.72,-0.3 0.68,-0.43 1.31,-1.25 1.97,-1.17 2.39,-1.08 1.66,-1.05 0.4,-0.41 0.02,-0.16 -0.02,-0.16 -0.04,-0.18 -0.04,-0.24 -0.01,-0.36 0.11,-0.15 0.15,-0.07 0.29,0.02 0.32,0.08 0.19,0.09 0.2,0.15 0.14,0.14 0.39,0.32 1.46,1.16 1.01,0.2 0.88,-0.13 2.22,-0.79 0.78,-0.08 0.34,0.17 0.19,0.12 0.97,0.32 0.24,-1.19 0.16,-0.28 0.66,-0.74 0.85,-1.03 0.2,-0.72 0.13,-0.63 0.25,-0.39 2.62,-2.35 1.12,-0.67 0.31,-0.38 0.46,-0.73 0.27,-0.69 1.14,-3.7 -0.82,-0.89 -0.05,-0.71 0.27,-0.69 0.78,-1.51 0.26,-0.71 1.49,-1.25 0.74,-0.46 0.73,-0.36 2.99,-0.91 3.34,-0.33 0.13,0.02 0.07,0.04 0.8,0.62 1.46,-2.22 0.26,-0.23 0.45,-0.3 0.38,-0.05 1.15,-0.41 0.8,-0.51 1.12,-0.49 1.4,-0.85 1.2,-0.39 0.33,-0.22 0.81,-0.9 0.23,-0.46 0.13,-0.41 0.01,-0.85 -1.16,-0.16 -0.73,-0.46 -0.5,-1.12 -0.34,-1.14 -0.43,-0.88 -1.15,-1.7 -0.43,-0.93 -0.65,-2.21 -0.43,-0.77 -1.42,-1.43 -0.3,-0.86 0.64,-0.85 0.86,-0.89 0.09,-0.87 -0.16,-0.88 0.1,-0.92 0.81,-0.82 0.78,-0.27 -0.38,-0.55 z\"\n        />\n      <path\n        id=\"AF-WAR\"\n        title=\"Wardak\"\n        \n        d=\"m 384.03715,232.72119 -0.92,-0.15 -0.28,-0.08 -0.21,-0.27 -0.17,-0.26 -0.31,-1.73 -0.08,-0.89 -0.14,-0.46 -0.24,-0.56 -2.5,-2.54 -0.21,-0.48 -0.2,-0.67 -0.1,-1.16 -0.31,-1.11 -5.59,2.91 -1.37,1.03 -0.7,1.16 -1.01,1.1 -0.53,0.44 -0.39,0.16 -0.54,-0.31 -0.36,-0.1 -0.42,0.01 -0.56,0.13 -0.35,0.22 -0.33,0.36 -0.32,0.49 -0.95,1.09 -0.2,0.3 -0.25,0.74 -1.09,2.32 -2.98,-0.34 -8.92,-2.53 -1.13,-0.2 -1.83,0.25 -1.39,-0.04 -0.8,-0.12 -0.93,-0.28 -1.49,-0.14 -0.4,-0.12 -0.23,-0.23 -0.24,-0.3 -0.39,-0.32 -0.93,-0.41 -0.55,-0.17 -0.45,-0.07 -0.29,0.04 -0.29,0.1 -1.37,0.84 -0.31,0.13 -4.25,0.86 -2.7,0.37 -0.4,0.14 -0.48,0.24 -0.17,0.23 -0.25,0.64 0.48,1.64 0.51,0.73 1.45,1.22 0.28,0.42 0.11,0.45 -0.06,1.02 -0.09,0.35 -0.17,0.25 -0.84,0.67 -0.24,0.12 -0.27,0.07 -1.3,0.14 -0.31,0.12 -0.3,0.19 -0.25,0.25 -0.24,0.33 -0.25,0.44 -0.24,0.58 -0.31,0.94 0.01,0.72 0.12,0.5 0.27,0.35 0.31,0.25 0.34,0.18 0.33,0.13 3.38,0.31 2.13,-0.11 0.49,0.04 0.45,0.14 0.49,0.38 0.24,0.39 0.13,0.41 0.05,0.37 -0.01,0.38 -0.06,0.31 -0.12,0.23 -0.15,0.18 -0.21,0.13 -0.18,0.09 -0.39,0.11 -1.08,0.61 -0.11,0.23 -0.03,0.31 0.18,0.15 0.28,0.1 1.56,0.19 0.6,0.17 4.78,2.18 0.57,0.45 0.17,0.34 -0.48,0.51 -0.7,0.95 -0.15,0.32 -0.21,0.57 -0.13,0.19 -0.2,0.16 -3.9,1.51 -0.34,0.33 -0.32,0.5 -0.3,1.19 0,0.62 0.2,0.5 0.87,0.81 2.29,0.31 0.81,-0.01 1.51,-0.52 0.24,-0.18 0.1,-0.19 0.29,-1.34 0.19,-0.29 0.32,-0.32 0.62,-0.49 0.43,-0.23 0.39,-0.13 3.29,-0.7 1.22,-0.06 1.49,0.34 1.8,0.69 1.5,1.04 1.22,1.16 0.53,0.27 0.66,0.16 2.24,0.17 0.64,-0.04 0.74,-0.15 3.09,-1.08 1.39,-0.74 0.7,-0.58 0.26,-0.09 0.34,-0.19 0.16,-0.14 0.69,-0.45 1.06,0.25 0.72,0.49 1.46,1.38 0.39,0.6 0.25,0.55 0.48,2.62 0.03,0.45 -0.08,0.85 0.02,0.49 0.06,0.51 0.16,0.64 0.05,0.43 -0.02,0.43 -0.07,0.38 0.03,0.51 0.12,0.59 0.43,1.46 0.03,0.82 0.35,1 0.49,0.32 0.61,0.17 3.03,1.23 0.25,0.2 0.5,0.86 2.33,1.45 0.71,0.77 0.18,0.29 4.14,4.37 0.32,0.28 0.29,0.15 0.24,0.01 0.48,-0.04 0.43,-0.1 1.08,-0.04 0.45,1.28 -0.01,0.33 -0.05,0.46 -0.4,1.17 -0.23,0.96 -0.01,0.51 0.07,0.37 0.09,0.13 0.13,0.15 0.43,0.3 0.62,0.28 1.94,0.48 0.51,0 0.8,-0.09 0.42,-0.17 4.1,-2.41 0.33,-0.27 2.05,-2.55 1.16,-1.14 1.63,-1.85 0.2,-0.34 0.38,-0.87 0.32,-2.09 -0.08,-0.44 -0.17,-0.54 -0.5,-0.63 -0.08,-0.16 -0.02,-0.09 0.12,-0.16 0.64,-1.24 0.55,-0.75 1.19,-1.02 0.66,-0.36 0.69,-0.28 2.33,-0.48 0.43,-0.15 0.2,-0.17 0.06,-0.23 0,-0.11 -0.09,-0.33 -0.7,-1.75 -0.21,-0.24 -0.51,-0.44 -0.61,-0.4 -1.12,-1.01 -0.47,-0.69 -0.24,-0.66 0.17,-2.1 0.46,-1.99 1.03,-2.24 0.86,-1.15 1.03,-0.96 0.67,-0.46 1.43,-3.4 0.96,-1.5 1.27,-1.46 -0.26,-0.89 -0.85,-2.86 -1.66,-1.24 -0.34,-0.17 -1.55,-0.4 -0.27,-0.12 -0.21,-0.23 -0.17,-0.38 -0.25,-0.77 -0.22,-0.51 -0.21,-0.37 -0.18,-0.5 -0.09,-0.71 0.08,-1.61 0.5,-1.96 -0.08,-1.3 0.6,-2.04 -0.62,0.18 -0.76,-0.14 -1.49,-0.67 -1.68,-0.48 -0.71,-0.36 -0.38,-0.05 -1.47,0.09 -0.64,-0.07 -0.7,-0.27 -0.87,-0.67 -0.24,-0.14 -0.21,-0.09 -0.85,-0.23 -0.68,-0.31 -3.58,-0.88 -0.48,0.01 -0.69,0.29 -2.86,1.88 -1.49,0.75 -0.8,0.25 -0.26,0.16 -0.41,0.43 -0.25,0.13 -0.34,0.04 z\"\n        />\n      <path\n        id=\"AF-SAM\"\n        title=\"Samangan\"\n        \n        d=\"m 368.52715,117.15119 -9.66,-1.37 -2.34,-0.29 -1.12,-0.37 -0.64,-0.41 -0.28,-0.24 -1.51,-0.95 -2.77,-1.36 -0.68,-0.19 -0.78,0.05 -0.74,0.18 -1.4,0.57 -0.67,0.18 -5.53,-0.52 -0.69,0.25 -0.68,1.67 -0.51,0.48 -2.21,0.67 -0.98,0.49 -0.47,0.38 -0.43,0.44 -0.15,0.24 -0.09,0.15 -0.01,0.15 0.04,0.14 0.08,0.15 0.84,1.13 1.3,2.73 0.06,0.46 0.02,0.66 -1.14,3.19 -0.7,2.89 -0.06,2.7 -0.34,2.19 -0.23,0.39 -0.34,0.48 -0.76,0.62 -0.44,0.47 -0.2,0.36 -0.05,0.29 0.05,0.19 0.09,0.28 0.09,0.4 0,0.4 -0.1,0.8 -0.23,0.45 -0.34,0.38 -1.01,0.94 -0.81,0.98 -0.48,0.46 -0.39,0.3 -0.69,0.21 -2.57,0.39 -0.9,0.29 -4.57,3.51 -0.98,0.96 -0.01,0.1 0.01,0.13 0.05,0.2 0.11,0.19 0.17,0.2 0.3,0.23 0.12,0.11 0.08,0.16 0.04,0.24 0.03,0.63 0.03,0.13 0.18,0.39 0.31,0.5 1.33,1.78 0.11,0.45 0.02,0.53 -0.13,1.17 -0.21,0.68 -0.25,0.5 -0.31,0.25 -0.79,0.42 -0.38,0.33 -0.2,0.41 -0.16,0.61 -0.22,0.4 -0.25,0.29 -0.61,0.47 -0.65,0.78 -1.85,2.22 -1.02,0.78 -1.81,0.87 -0.44,0.43 -0.36,0.44 -0.81,1.47 -0.2,0.56 -0.11,0.51 -0.07,0.49 0.06,0.65 4.59,2.16 1.11,0.98 0.15,0.68 0.02,0.23 0.01,0.18 -0.04,0.15 -0.06,0.17 -0.12,0.19 -0.19,0.18 -0.27,0.18 -0.91,0.4 -0.24,0.21 -0.17,0.26 -0.05,0.39 0.14,0.21 1.08,0.57 0.15,0.16 0.07,0.15 -0.07,0.5 -0.01,0.31 0.07,0.79 -0.03,0.23 -0.06,0.17 -0.14,0.06 -0.68,0.17 -0.15,0.07 -0.09,0.08 -0.03,0.13 0.01,0.24 0.28,1.53 0.02,0.43 -0.01,0.52 -0.07,0.68 -0.11,0.5 -0.17,0.44 -0.23,0.42 -0.29,0.4 -2.29,2.31 -0.83,0.68 -0.9,0.57 -1.55,0.7 -0.76,0.47 -0.59,0.6 2.65,1.06 1.32,0.26 4.87,0.03 1.36,-0.32 4.37,-1.96 3.78,-0.97 4.53,-1.76 0.55,-0.08 0.43,0.04 0.27,0.12 0.6,0.14 0.97,0.06 1.9,0.53 5.23,-2.14 3.01,-0.62 1.22,-0.02 6.41,1.5 0.64,0.06 0.52,-0.04 2.64,-0.89 0.48,-0.02 0.36,0.1 0.24,0.23 0.4,0.58 0.26,0.25 0.39,0.23 0.39,0.05 4.82,-0.73 1.95,0.11 4.26,-0.93 2.62,-2.73 0.18,-0.38 0.03,-0.56 0.1,-0.52 0.22,-0.56 1.08,-1.41 0.63,-1.1 0.26,-1.8 -0.58,-0.78 -0.22,-0.21 -1.25,-0.73 -0.1,-0.55 0.02,-0.29 0.05,-0.39 0.24,-0.82 0.36,-0.82 6.98,-9.35 0.2,-0.41 0.2,-1.92 0.18,-0.69 0.23,-2.31 0,-0.38 -0.06,-0.81 0.03,-0.38 0.12,-0.37 0.34,-0.82 0.22,-0.4 0.33,-0.38 1.45,-0.72 4.51,-3.99 0.97,-1.11 2.14,-3.65 -0.11,-0.5 -0.35,-0.62 -0.3,-0.39 -1.18,-1.14 -3.23,-2.46 -2.04,-1.09 -0.55,-0.48 -2.27,-3.04 -3.35,-5.52 -2.35,-3.55 -0.23,-0.48 -0.36,-0.94 -0.21,-0.37 -0.69,-0.92 -0.14,-0.34 -0.04,-0.36 0.15,-0.54 0.36,-0.8 0.21,-0.34 0.3,-0.29 0.4,-0.22 1.12,-0.31 0.71,-0.52 -1.59,-0.46 -0.86,-0.87 -3.93,1.61 -1.37,0.27 z\"\n        />\n      <path\n        id=\"AF-PAR\"\n        title=\"Parwan\"\n        \n        d=\"m 423.87715,186.02119 -1.73,-0.41 -1.15,0.98 -0.46,0.66 -0.4,0.35 -0.32,0.19 -0.33,0.1 -2.45,1.11 -0.53,0.35 -0.38,0.39 -0.83,1.55 -0.46,0.69 -0.32,0.33 -0.33,0.2 -2.05,0.59 -0.42,0.28 -0.22,0.24 -0.35,0.73 -2.17,1.48 -0.39,0.09 -0.49,0.04 -2.01,-1.41 -1.88,0.55 -0.28,0.18 -0.44,0.33 -0.47,0.48 -0.49,0.8 -0.79,1.06 -0.73,1.36 -0.37,0.43 -0.09,0.05 -3.59,2.75 -0.69,0.41 -2.22,0.49 -0.29,0.18 -0.14,0.23 0.02,0.18 0.07,0.3 0.27,0.57 0.09,0.29 0.03,0.2 -0.18,0.65 -1.34,0.73 -1,0.21 -2.48,0.12 -2.98,-0.26 -3.22,0.46 -0.7,-0.08 -3.56,-1 -0.33,0.03 -0.16,0.16 -0.03,0.24 -0.06,0.29 -0.17,0.29 -0.3,0.37 -0.56,0.44 -0.69,0.43 -1.35,0.46 -1.04,0.55 -0.37,0.5 2.55,2.75 0.63,1.74 0.04,1.51 0.15,1.32 -0.06,1.76 -0.25,1.81 0.12,0.31 0.12,0.18 0.92,0.52 0.57,0.48 0.31,1.11 0.1,1.16 0.2,0.67 0.21,0.48 2.5,2.54 0.24,0.56 0.14,0.46 0.08,0.89 0.31,1.73 0.17,0.26 0.21,0.27 0.28,0.08 0.92,0.15 0.34,-0.04 0.25,-0.13 0.41,-0.43 0.26,-0.16 0.8,-0.25 1.49,-0.75 2.86,-1.88 0.69,-0.29 0.48,-0.01 3.58,0.88 0.68,0.31 0.85,0.23 0.21,0.09 0.24,0.14 0.87,0.67 0.7,0.27 0.64,0.07 1.47,-0.09 0.38,0.05 0.71,0.36 1.68,0.48 1.49,0.67 0.76,0.14 0.62,-0.18 0.63,-2.32 -0.2,-2.52 1.2,-2.59 0.62,-0.55 0.17,-0.8 1.39,-4.2 1.84,-2.95 0.17,-0.2 3.51,0.9 0.71,-0.05 4.42,0.29 1.44,-0.35 0.94,0.15 0.85,0.39 0.86,0.65 0.56,0.52 2.06,1.23 0.38,1.57 1.59,2.58 0.33,0.74 0.51,0.47 0.99,0.2 0.85,0.73 1.22,2.85 0.6,2.67 0.76,2.41 1.03,0.18 1.94,-0.6 0.67,-0.48 0.62,-0.71 0.21,-0.6 0.14,-0.63 0.24,-0.67 0.43,-0.27 0.59,0 1.7,0.36 0.4,-1.86 0.12,-0.36 0,-0.43 -0.52,-0.29 -0.28,-0.25 -0.3,-0.37 -0.28,-0.53 -0.32,-0.33 -0.25,-0.43 -0.3,-0.75 -0.19,-0.78 -0.05,-1.05 0.21,-2.95 -0.04,-1.23 0.2,-2.45 -0.26,-1.35 -1.61,-0.55 -2.12,-0.82 -2.93,-0.15 -1.4,-1.49 -0.91,-1.23 -0.64,-0.48 -0.77,-0.45 -1.35,-0.3 -1.28,-0.66 -0.75,-0.9 -0.19,-1.15 0.09,-1.15 0.38,-3.29 -0.23,-2.27 -1.83,0.26 -0.07,-2.07 0.19,-1.05 0.13,-6.69 0.58,-2.67 0.04,-1.49 -0.78,-1.14 -0.32,-0.23 -0.31,0.1 -0.65,0.03 z\"\n        />\n      <path\n        id=\"AF-LOG\"\n        title=\"Logar\"\n        \n        d=\"m 435.57715,261.02119 -1,-1.89 -1.05,-1.38 -0.35,-0.56 -0.29,-0.76 -0.13,-0.48 -0.25,-0.55 -0.34,-0.57 -1.47,-1.9 -0.94,-0.82 -0.64,-0.65 -0.58,-0.8 -1.17,-2.19 -0.56,-0.41 -3.99,2.7 -0.26,0.29 -0.3,0.18 -0.32,0.03 -0.85,-0.41 -0.77,0.12 -0.79,0.32 -0.7,0.08 -7.17,-2.44 -1.27,1.46 -0.96,1.5 -1.43,3.4 -0.67,0.46 -1.03,0.96 -0.86,1.15 -1.03,2.24 -0.46,1.99 -0.17,2.1 0.24,0.66 0.47,0.69 1.12,1.01 0.61,0.4 0.51,0.44 0.21,0.24 0.7,1.75 0.09,0.33 0,0.11 -0.06,0.23 -0.2,0.17 -0.43,0.15 -2.33,0.48 -0.69,0.28 -0.66,0.36 -1.19,1.02 -0.55,0.75 -0.64,1.24 -0.12,0.16 0.02,0.09 0.08,0.16 0.5,0.63 0.17,0.54 0.08,0.44 -0.32,2.09 -0.38,0.87 -0.2,0.34 -1.63,1.85 1.46,0.7 0.19,0.23 0.24,0.4 0.07,0.38 0.38,1.18 0.45,1 0.16,0.47 0.03,0.3 -0.12,0.08 -0.4,0.08 -0.19,0.1 -0.2,0.19 -0.2,0.4 -0.17,0.46 -0.1,0.62 0.1,0.31 0.2,0.24 0.24,0.17 0.24,0.2 0.06,0.22 -0.03,0.67 0.04,0.2 0.35,0.43 0.14,1.28 0,0.01 7.19,0.06 0.76,0.14 1,0.42 0.58,0 0.76,-0.12 0.65,-0.22 0.56,-0.28 0.32,-0.22 0.64,-0.56 0.93,-1.68 0.54,-1.6 0.76,-1.56 0.6,-0.63 0.66,-0.51 4.71,-3.54 4.85,-2.28 1.54,-1.06 0.27,-0.36 0.35,-0.71 2.59,-2.75 0.27,-0.28 1.89,-1.66 0.59,-0.34 0.54,-0.48 0.82,-0.93 0.5,-0.7 0.72,-1.5 0.39,-1.18 0.31,-0.32 1.07,-0.67 0.46,-0.15 0.47,-0.08 0.82,0.01 0.6,-0.06 2.16,0.16 1.65,0.4 0.44,0.17 0.38,0.22 0.17,0.2 0.07,0.29 -0.03,0.36 -0.06,0.43 0.01,0.39 0.17,0.21 0.58,0.02 0.54,-0.2 2.7,-1.69 1.69,-2.07 0.29,-0.19 0.26,-0.08 0.27,0.02 0.3,0.07 0.34,0.13 2.93,1.73 -0.86,-1.24 -0.5,-1.99 -0.52,-0.87 -0.4,-0.43 -0.74,-0.94 -0.24,-0.83 -0.09,-0.17 -0.13,-0.17 -0.36,-0.37 -0.13,-0.16 -0.12,-0.21 -0.17,-0.47 -0.5,-0.85 -0.21,-0.61 -0.12,-0.24 -0.18,-0.17 -0.22,-0.15 -0.55,-0.26 -0.13,-0.09 -0.11,-0.18 -0.15,-0.27 -0.1,-0.13 -0.14,-0.12 -0.42,-0.23 -0.09,-0.08 -0.13,-0.14 -0.34,-0.45 -0.23,-0.19 -0.35,-0.09 -0.48,0.08 -1.03,0.35 -5.7,2.66 -3.61,2.53 -0.92,-0.19 -0.63,0.79 -0.16,0.13 -0.35,0.01 z\"\n        />\n      <path\n        id=\"AF-LAG\"\n        title=\"Laghman\"\n        \n        d=\"m 463.39715,204.55119 -0.44,-0.37 -0.48,-1 -0.14,-2.33 -0.19,-0.59 -0.3,-0.64 -1.26,-2.02 -0.28,-1.03 -0.35,0.91 -0.38,0.7 -1.82,2.46 -0.77,0.7 -1.49,0.95 -1.57,0.78 -0.36,0.22 -0.53,0.23 -0.3,0.06 -0.17,0.15 -0.17,0.34 -0.1,0.64 -0.49,1.05 0.75,2.31 1.69,1.33 0.25,0.47 0.31,0.78 0.17,0.8 0.36,1.05 1.48,2.4 0.32,0.85 0.45,2.09 -0.92,1.37 1.54,4.34 0.01,0.67 -0.06,0.88 -0.27,0.4 -0.52,0.55 -1.97,1.76 -0.5,0.82 -0.35,0.74 -0.07,0.55 0.05,0.94 -0.11,2.37 -0.22,0.5 -0.19,0.32 -0.92,1.03 0.05,1.34 0.72,1.88 -0.08,0.64 -0.32,0.83 -0.4,0.81 -0.49,1.25 -0.16,0.81 -0.05,0.81 0.02,0.49 0.16,1.36 1.47,0.48 5.22,-0.6 2.74,-0.53 2.83,0.61 0.82,-0.17 2.88,-1.34 5.13,-0.88 2.74,-0.8 2.43,-1.23 0.62,-0.54 1.35,-1.02 1.24,-2.58 0.09,-0.83 0.03,-1.02 -0.79,-3.9 1.18,-1 2.59,-0.98 0.54,-0.39 0.35,-2.37 0.32,-0.69 0.51,-0.65 1.52,-1.17 1.7,-0.87 1.46,-0.46 0.21,-1.86 0.02,-2.33 -0.14,-1.11 -0.28,-1.09 -0.6,-1.14 -0.27,-0.7 -0.04,-0.57 0.01,-0.54 -0.07,-0.63 -0.23,-0.71 -0.6,-0.79 -0.9,1.26 -2.31,1.94 -0.57,0.32 -2.57,1.08 -2.52,0.73 -0.69,0.02 -0.66,-0.18 -2.28,-1.39 -2.22,-0.35 -0.55,-0.36 -1,-2.17 -1.06,-1.14 -1.27,-1.74 -0.67,-2.52 -1.31,-1.24 -3.88,-0.69 -1.66,0.08 z\"\n        />\n      <path\n        id=\"AF-PAN\"\n        title=\"Panjshayr\"\n        \n        d=\"m 474.91715,159.94119 -3.03,-1.36 -1.36,-1.08 -0.29,-0.07 -0.03,0.19 0.1,0.46 -0.03,0.55 -0.3,0.69 -0.48,0.66 -0.64,0.72 -0.62,0.55 -1.11,0.54 -3.32,0.38 -2.02,0.81 -0.81,0.46 -0.19,0.02 -0.09,-0.12 -0.04,-0.41 -0.04,-0.17 -0.1,-0.1 -0.12,-0.08 -0.63,-0.22 -0.6,1.57 -0.21,0.23 -0.34,0.28 -1.5,0.71 -0.38,0.46 -0.91,1.83 -0.43,0.55 -0.43,0.35 -0.74,0.21 -0.69,0.07 -0.72,-0.03 -1.28,1.93 -0.43,1.2 -0.19,0.94 -0.12,1.24 -0.61,3.07 -0.24,0.74 -0.47,0.7 -2.42,0.71 -6.11,3.99 -0.82,0.35 -2.61,-0.35 -2.2,0.26 -0.55,0.18 -0.33,0.26 -0.43,0.76 -1.31,1.27 -1.36,-0.09 -0.7,0.25 -0.58,0.44 -0.42,0.22 -1.56,-0.06 -2.14,0.66 -0.04,1.49 -0.58,2.67 -0.13,6.69 -0.19,1.05 0.07,2.07 1.83,-0.26 0.78,-0.39 0.4,-0.81 0.69,0.25 0.66,0.46 2.51,0.19 2.07,0.78 0.94,-0.05 1.02,-0.28 0.66,-0.04 0.95,0.37 2.39,-0.09 0.55,0.15 3.35,-0.96 2.17,0.25 1.54,0.35 0.89,0.49 0.67,0.63 1.61,2.27 1.06,1.22 0.49,-1.05 0.1,-0.64 0.17,-0.34 0.17,-0.15 0.3,-0.06 0.53,-0.23 0.36,-0.22 1.57,-0.78 1.49,-0.95 0.77,-0.7 1.82,-2.46 0.38,-0.7 0.35,-0.91 0.12,-3.15 -0.23,-0.6 -0.47,-0.9 -1.95,-1.72 0.08,-0.76 0.4,-0.64 0.5,-0.5 0.83,-0.53 3.61,-1.89 1.13,-1.58 0.22,-0.6 0.83,-1.42 0.25,-0.05 0.27,0.14 0.49,0.16 0.69,-0.02 1.13,-0.35 0.79,-0.62 0.61,-0.69 0.42,-0.77 0.26,-0.64 0.05,-0.44 -0.11,-0.29 -0.17,-0.24 -0.13,-0.27 -0.02,-0.68 0.08,-0.31 0.32,-0.23 1.2,-0.69 0.37,-0.54 0.23,-0.78 0.2,-2.82 0.56,-1.46 0.35,-0.78 0.01,-0.66 0.35,-2.97 0.23,-1.02 0.17,-2.64 0.94,-2.68 z\"\n        />\n      <path\n        id=\"AF-KAP\"\n        title=\"Kapisa\"\n        \n        d=\"m 434.00715,201.45119 -2.07,-0.78 -2.51,-0.19 -0.66,-0.46 -0.69,-0.25 -0.4,0.81 -0.78,0.39 0.23,2.27 -0.38,3.29 -0.09,1.15 0.19,1.15 0.75,0.9 1.28,0.66 1.35,0.3 0.77,0.45 0.64,0.48 0.91,1.23 1.4,1.49 2.93,0.15 2.12,0.82 1.61,0.55 0.26,1.35 -0.2,2.45 0.04,1.23 -0.21,2.95 0.05,1.05 0.19,0.78 0.3,0.75 0.25,0.43 0.32,0.33 0.28,0.53 0.3,0.37 0.28,0.25 0.52,0.29 0,0.43 -0.12,0.36 -0.4,1.86 1.08,0.59 0.54,0 2.53,-0.65 1.42,0 0.33,-0.39 0.24,-0.64 0.16,-0.78 2.21,-5.73 0.85,-0.74 2.25,-1.11 0.66,-0.24 0.36,-0.3 0.11,-0.25 0.45,-0.79 1.01,-1.03 0.92,-1.37 -0.45,-2.09 -0.32,-0.85 -1.48,-2.4 -0.36,-1.05 -0.17,-0.8 -0.31,-0.78 -0.25,-0.47 -1.69,-1.33 -0.75,-2.31 -1.06,-1.22 -1.61,-2.27 -0.67,-0.63 -0.89,-0.49 -1.54,-0.35 -2.17,-0.25 -3.35,0.96 -0.55,-0.15 -2.39,0.09 -0.95,-0.37 -0.66,0.04 -1.02,0.28 -0.94,0.05 z\"\n        />\n      <path\n        id=\"AF-KAB\"\n        title=\"Kabul\"\n        \n        d=\"m 415.82715,217.64119 -3.51,-0.9 -0.17,0.2 -1.84,2.95 -1.39,4.2 -0.17,0.8 -0.62,0.55 -1.2,2.59 0.2,2.52 -0.63,2.32 -0.6,2.04 0.08,1.3 -0.5,1.96 -0.08,1.61 0.09,0.71 0.18,0.5 0.21,0.37 0.22,0.51 0.25,0.77 0.17,0.38 0.21,0.23 0.27,0.12 1.55,0.4 0.34,0.17 1.66,1.24 0.85,2.86 0.26,0.89 7.17,2.44 0.7,-0.08 0.79,-0.32 0.77,-0.12 0.85,0.41 0.32,-0.03 0.3,-0.18 0.26,-0.29 3.99,-2.7 0.56,0.41 1.17,2.19 0.58,0.8 0.64,0.65 0.94,0.82 1.47,1.9 0.34,0.57 0.25,0.55 0.13,0.48 0.29,0.76 0.35,0.56 1.05,1.38 1,1.89 0.35,-0.01 0.16,-0.13 0.63,-0.79 -0.32,-2.12 0.06,-0.42 0.15,-0.2 0.29,-0.25 0.79,-0.44 2.43,-1.8 2.99,-3.1 1.94,-2.6 0.35,-0.57 0.11,-0.5 0,-0.31 -0.22,-0.82 -0.03,-0.27 0,-0.21 0.04,-0.13 1.62,-0.38 5.62,-0.67 -0.16,-1.36 -0.02,-0.49 0.05,-0.81 0.16,-0.81 0.49,-1.25 0.4,-0.81 0.32,-0.83 0.08,-0.64 -0.72,-1.88 -0.05,-1.34 0.92,-1.03 0.19,-0.32 0.22,-0.5 0.11,-2.37 -0.05,-0.94 0.07,-0.55 0.35,-0.74 0.5,-0.82 1.97,-1.76 0.52,-0.55 0.27,-0.4 0.06,-0.88 -0.01,-0.67 -1.54,-4.34 -1.01,1.03 -0.45,0.79 -0.11,0.25 -0.36,0.3 -0.66,0.24 -2.25,1.11 -0.85,0.74 -2.21,5.73 -0.16,0.78 -0.24,0.64 -0.33,0.39 -1.42,0 -2.53,0.65 -0.54,0 -1.08,-0.59 -1.7,-0.36 -0.59,0 -0.43,0.27 -0.24,0.67 -0.14,0.63 -0.21,0.6 -0.62,0.71 -0.67,0.48 -1.94,0.6 -1.03,-0.18 -0.76,-2.41 -0.6,-2.67 -1.22,-2.85 -0.85,-0.73 -0.99,-0.2 -0.51,-0.47 -0.33,-0.74 -1.59,-2.58 -0.38,-1.57 -2.06,-1.23 -0.56,-0.52 -0.86,-0.65 -0.85,-0.39 -0.94,-0.15 -1.44,0.35 -4.42,-0.29 -0.71,0.05 z\"\n        />\n      <path\n        id=\"AF-BGL\"\n        title=\"Baghlan\"\n        \n        d=\"m 380.28715,118.00119 -0.45,-0.14 -1.21,-0.7 -2.35,-0.56 -0.71,0.52 -1.12,0.31 -0.4,0.22 -0.3,0.29 -0.21,0.34 -0.36,0.8 -0.15,0.54 0.04,0.36 0.14,0.34 0.69,0.92 0.21,0.37 0.36,0.94 0.23,0.48 2.35,3.55 3.35,5.52 2.27,3.04 0.55,0.48 2.04,1.09 3.23,2.46 1.18,1.14 0.3,0.39 0.35,0.62 0.11,0.5 -2.14,3.65 -0.97,1.11 -4.51,3.99 -1.45,0.72 -0.33,0.38 -0.22,0.4 -0.34,0.82 -0.12,0.37 -0.03,0.38 0.06,0.81 0,0.38 -0.23,2.31 -0.18,0.69 -0.2,1.92 -0.2,0.41 -6.98,9.35 -0.36,0.82 -0.24,0.82 -0.05,0.39 -0.02,0.29 0.1,0.55 1.25,0.73 0.22,0.21 0.58,0.78 -0.26,1.8 -0.63,1.1 -1.08,1.41 -0.22,0.56 -0.1,0.52 -0.03,0.56 -0.18,0.38 -2.62,2.73 -0.13,1.53 0.19,1.04 0.73,2.01 0.06,0.49 -0.06,0.44 -0.37,0.84 -0.17,0.63 0.02,0.53 0.38,1.21 -0.04,0.4 -0.18,0.35 -0.2,0.27 -0.19,0.48 -0.17,0.63 -0.27,1.72 -0.14,0.47 -0.34,0.74 -0.13,0.54 -0.32,0.69 -1.24,0.68 -0.29,0.25 -0.34,0.37 -0.24,0.35 -0.27,0.53 -0.11,0.39 -0.22,1.29 0,0.35 0.07,0.32 0.12,0.3 0.09,0.32 0.04,0.35 0,0.35 -0.03,0.37 0,0.35 0.21,1.99 0.12,0.46 0.17,0.26 0.25,0.18 1.48,0.45 0.16,0.11 0.15,0.17 0.18,0.16 0.22,0.12 0.28,0.03 0.34,-0.04 1.13,-0.27 0.34,0 0.35,0.08 0.32,0.17 1.7,1.45 0.23,0.09 0.24,0 0.21,-0.03 0.3,-0.14 0.37,-0.5 1.04,-0.55 1.35,-0.46 0.69,-0.43 0.56,-0.44 0.3,-0.37 0.17,-0.29 0.06,-0.29 0.03,-0.24 0.16,-0.16 0.33,-0.03 3.56,1 0.7,0.08 3.22,-0.46 2.98,0.26 2.48,-0.12 1,-0.21 1.34,-0.73 0.18,-0.65 -0.03,-0.2 -0.09,-0.29 -0.27,-0.57 -0.07,-0.3 -0.02,-0.18 0.14,-0.23 0.29,-0.18 2.22,-0.49 0.69,-0.41 3.59,-2.75 0.09,-0.05 0.37,-0.43 0.73,-1.36 0.79,-1.06 0.49,-0.8 0.47,-0.48 0.44,-0.33 0.28,-0.18 1.88,-0.55 2.01,1.41 0.49,-0.04 0.39,-0.09 2.17,-1.48 0.35,-0.73 0.22,-0.24 0.42,-0.28 2.05,-0.59 0.33,-0.2 0.32,-0.33 0.46,-0.69 0.83,-1.55 0.38,-0.39 0.53,-0.35 2.45,-1.11 0.33,-0.1 0.32,-0.19 0.4,-0.35 0.46,-0.66 1.15,-0.98 1.73,0.41 0.65,-0.03 0.31,-0.1 0.32,0.23 0.78,1.14 2.14,-0.66 1.56,0.06 0.42,-0.22 0.58,-0.44 0.7,-0.25 1.36,0.09 1.31,-1.27 0.43,-0.76 0.33,-0.26 0.55,-0.18 2.2,-0.26 2.61,0.35 0.82,-0.35 6.11,-3.99 2.42,-0.71 0.47,-0.7 0.24,-0.74 0.61,-3.07 0.12,-1.24 0.19,-0.94 0.43,-1.2 1.28,-1.93 0.72,0.03 0.69,-0.07 0.74,-0.21 0.43,-0.35 0.43,-0.55 0.91,-1.83 0.38,-0.46 1.5,-0.71 0.34,-0.28 0.21,-0.23 0.6,-1.57 -0.23,-4.62 -0.22,-0.51 -0.36,-0.57 -0.71,-0.93 -0.29,-0.47 -0.1,-0.44 0.04,-0.42 -0.02,-0.11 -0.05,-0.17 -0.12,-0.2 -0.18,-0.21 -0.38,-0.34 -0.28,-0.4 -0.5,-1.11 -0.09,-0.32 -0.02,-0.17 0,-0.31 -0.04,-0.19 -0.09,-0.26 -0.85,-1.65 -1,-1.24 -2.9,-2.69 -0.5,-6.88 -0.58,-1.17 -0.65,-0.17 -1.23,0.11 -0.17,-0.02 -0.18,-0.06 -0.19,-0.11 -0.2,-0.21 -0.19,-0.39 -0.18,-0.55 -0.09,-0.91 0,-1.22 -0.07,-0.6 -0.17,-0.71 -0.79,-2.2 -0.5,-0.19 -1.16,1.06 -1.55,0.91 -2.93,1.17 -0.75,0.11 -0.79,0.23 -0.65,0.39 -3.16,2.51 -2.05,0.62 -1.71,-0.08 -0.4,0.1 -0.33,0.16 -0.19,0.16 -0.15,0.1 -0.11,-0.03 -0.13,-0.11 -0.23,-0.31 -0.23,-0.14 -0.39,0 -0.26,0.1 -0.25,0.16 -0.38,0.34 -0.15,0.07 -0.18,-0.09 -0.23,-0.27 -0.45,-0.79 -0.24,-0.56 -0.28,-0.83 -0.19,-0.28 -0.29,-0.17 -0.62,-0.09 -0.81,-0.22 -0.59,-0.83 -0.67,-0.98 -0.18,-0.33 -0.22,-0.33 -0.39,-0.28 -0.61,-0.28 -1.85,-0.48 -0.88,-0.13 -1.96,-0.71 -0.94,-0.6 -1.39,-1.24 -0.59,-0.78 -0.33,-0.36 -0.57,-0.44 -0.06,-0.07 -0.05,-0.12 -0.05,-0.13 -0.59,-0.27 -2.32,-0.44 -1.2,-0.24 -0.24,0.04 -0.27,0.12 -1.35,0.85 -0.35,0.15 -0.33,0.08 -0.44,-0.12 -0.52,-0.31 -2.12,-1.68 -3.08,-3.89 -0.6,-1.34 -2.98,0.43 -0.78,0.3 -0.5,0.07 -0.56,-0.04 -4.11,-1.17 -7.91,-0.55 -2.58,0.13 z\"\n        />\n      <path\n        id=\"AF-TAK\"\n        title=\"Takhar\"\n        \n        d=\"m 461.85715,57.031194 -0.27,-0.26 -0.19,-0.41 -0.25,-0.31 -0.63,-0.12 -1.02,-0.38 -0.53,-0.87 -0.45,-0.97 -0.8,-0.71 -1.14,-0.06 -2.04,1.04 -0.98,0.28 -0.48,0.22 -0.51,0.44 -0.5,0.26 -0.51,-0.28 -0.35,-0.26 -1.08,-0.33 -0.39,-0.05 -1.2,0.15 -2.05,0.89 -1.08,0.21 -6.61,-0.6 -0.97,0.43 -0.84,2.57 -1.05,1.03 -2.15,1.58 -0.17,0.29 -0.3,0.73 -0.19,0.31 -0.38,0.35 -0.69,0.49 -0.25,0.34 -0.3,0.96 -0.1,1.14 0.04,2.33 0.09,0.22 0.45,0.12 0.14,0.27 -0.01,0.33 -0.12,0.09 -0.13,0.04 -0.06,0.18 0.02,0.57 0.07,0.57 0.15,0.52 0.25,0.42 0.41,0.91 0.32,2.1 0.4,0.55 -0.39,1.34 0.82,0.38 0.89,0.19 -0.15,0.77 -1.67,2.81 -0.8,0.78 -1.96,1.25 -0.77,0.92 -0.53,0.49 -0.57,0.22 -0.17,0.03 0.07,0.21 1.33,1.54 0.06,0.28 0.03,0.37 -0.91,1.2 -0.11,0.23 -0.33,1.41 0,0.57 0.07,0.59 0.15,0.8 0.02,0.5 -0.58,8.51 -0.56,1.699996 -0.53,0.64 -0.18,0.1 -0.13,0.05 -2.23,0.26 -0.66,0.72 -0.04,0.22 -0.04,0.42 0.18,1.24 -0.05,1.28 -0.07,0.4 -0.32,0.4 -0.07,0.13 -0.02,0.18 0.03,0.26 0.04,0.1 0.43,0.77 0.2,0.57 0.06,0.49 -0.02,0.45 -0.08,0.54 0.07,0.23 0.16,0.1 0.25,0.04 0.1,0.03 0.12,0.09 0.13,0.14 0.31,0.52 0.57,1.24 0.04,0.55 -0.06,0.52 -0.93,2.03 -0.36,1.34 -0.16,0.42 -0.2,0.3 -0.21,0.27 -1.37,1.13 -0.13,0.16 -0.09,0.16 -0.05,0.17 -0.07,0.15 -0.11,0.13 -0.21,0.13 -0.58,0.24 -0.76,0.72 -0.04,0.2 0.01,0.25 0.41,0.58 0.49,0.85 0.53,2.18 0.6,1.03 0.44,0.57 0.16,0.46 0.02,0.54 -0.07,0.6 0.03,0.81 0.18,0.63 0.45,0.89 0.2,0.63 0.11,0.48 0.01,0.97 0.59,0.83 0.81,0.22 0.62,0.09 0.29,0.17 0.19,0.28 0.28,0.83 0.24,0.56 0.45,0.79 0.23,0.27 0.18,0.09 0.15,-0.07 0.38,-0.34 0.25,-0.16 0.26,-0.1 0.39,0 0.23,0.14 0.23,0.31 0.13,0.11 0.11,0.03 0.15,-0.1 0.19,-0.16 0.33,-0.16 0.4,-0.1 1.71,0.08 2.05,-0.62 3.16,-2.51 0.65,-0.39 0.79,-0.23 0.75,-0.11 2.93,-1.17 1.55,-0.91 1.16,-1.06 0.5,0.19 0.79,2.2 0.17,0.71 0.07,0.6 0,1.22 0.09,0.91 0.18,0.55 0.19,0.39 0.2,0.21 0.19,0.11 0.18,0.06 0.17,0.02 1.23,-0.11 0.65,0.17 0.58,1.17 0.5,6.88 2.9,2.69 1,1.24 0.85,1.65 0.09,0.26 0.04,0.19 0,0.31 0.02,0.17 0.09,0.32 0.5,1.11 0.28,0.4 0.38,0.34 0.18,0.21 0.12,0.2 0.05,0.17 0.02,0.11 -0.04,0.42 0.1,0.44 0.29,0.47 0.71,0.93 0.36,0.57 0.22,0.51 0.23,4.62 0.63,0.22 0.12,0.08 0.1,0.1 0.04,0.17 0.04,0.41 0.09,0.12 0.19,-0.02 0.81,-0.46 2.02,-0.81 3.32,-0.38 1.11,-0.54 0.62,-0.55 0.64,-0.72 0.48,-0.66 0.3,-0.69 0.03,-0.55 -0.1,-0.46 0.03,-0.19 0.29,0.07 1.36,1.08 3.03,1.36 0.48,-0.41 0.17,-0.22 0.2,-0.31 0.31,-0.63 0.16,-0.18 0.17,-0.12 1.1,-0.48 2.11,-1.35 0.54,-0.5 0.91,-1.15 0.29,-0.23 2.46,-1.27 0.24,-0.35 0.08,-0.47 -0.29,-0.71 -1.01,-1.47 -0.24,-0.45 -0.14,-0.47 0.01,-0.47 0.25,-0.54 0.42,-0.6 0.06,-0.29 -0.05,-0.57 0.04,-0.49 0.5,-1.7 0.19,-1.01 0.11,-0.36 0.13,-0.29 0.57,-0.85 0.13,-1.72 -0.24,-6.34 -0.4,-0.6 -0.21,-0.21 -0.4,-0.24 -0.54,-0.17 -1.67,-0.16 -1.84,0.17 -0.33,-0.01 -3.16,-1.03 -1.62,-0.33 -0.35,0 -1,0.25 -0.53,-0.28 -0.72,-0.65 -3.38,-4.04 -2.74,-2.09 -1.94,-1.07 -0.23,-0.29 -0.13,-0.47 0.13,-2.16 -0.05,-0.58 -0.16,-0.62 -0.62,-1.57 -0.25,-1.48 -0.36,-0.94 -0.23,-1.42 -0.03,-2.21 0.29,-2.67 -0.07,-13.239996 -0.06,-0.48 0.21,-0.27 0.61,-0.49 0.39,-0.18 0.28,-0.21 0.12,-0.25 0.09,-0.53 0.02,-0.35 0.7,-1.2 1.33,-0.51 0.38,-0.29 0.21,-0.2 0.21,-0.89 1.15,-1.93 -1.15,-1.98 -0.78,-0.96 -1.82,-1.38 -0.39,-0.56 -0.28,-0.75 -0.06,-1.19 0.18,-1.39 -0.05,-0.66 -0.21,-0.78 -1.12,-2.93 -0.13,-1.64 0.3,-3.47 0.38,-1.59 0.23,-0.48 0.55,-0.76 0.25,-0.26 0.1,-0.22 -0.07,-0.17 -0.41,-0.2 -0.41,-0.1 -0.43,-0.05 -0.27,-0.08 -0.18,-0.64 -0.14,-1.21 -0.18,-6.32 0.29,-1.19 -0.02,-0.06 z\"\n        />\n      <path\n        id=\"AF-NAN\"\n        title=\"Nangarhar\"\n        \n        d=\"m 495.44715,234.78119 -0.35,-0.19 -0.27,-0.17 -1.51,-2.01 -0.26,-1.09 0,-0.93 0.22,-1.4 0.23,-0.89 0.33,-0.71 0.12,-0.47 0.09,-0.88 0,-0.32 -0.05,-0.35 -0.22,-0.72 -0.18,-0.44 -0.79,-1.08 -1.3,-0.76 -1.46,0.46 -1.7,0.87 -1.52,1.17 -0.51,0.65 -0.32,0.69 -0.35,2.37 -0.54,0.39 -2.59,0.98 -1.18,1 0.79,3.9 -0.03,1.02 -0.09,0.83 -1.24,2.58 -1.35,1.02 -0.62,0.54 -2.43,1.23 -2.74,0.8 -5.13,0.88 -2.88,1.34 -0.82,0.17 -2.83,-0.61 -2.74,0.53 -5.22,0.6 -1.47,-0.48 -5.62,0.67 -1.62,0.38 -0.04,0.13 0,0.21 0.03,0.27 0.22,0.82 0,0.31 -0.11,0.5 -0.35,0.57 -1.94,2.6 -2.99,3.1 -2.43,1.8 -0.79,0.44 -0.29,0.25 -0.15,0.2 -0.06,0.42 0.32,2.12 0.92,0.19 3.61,-2.53 5.7,-2.66 1.03,-0.35 0.48,-0.08 0.35,0.09 0.23,0.19 0.34,0.45 0.13,0.14 0.09,0.08 0.42,0.23 0.14,0.12 0.1,0.13 0.15,0.27 0.11,0.18 0.13,0.09 0.55,0.26 0.22,0.15 0.18,0.17 0.12,0.24 0.21,0.61 0.5,0.85 0.17,0.47 0.12,0.21 0.13,0.16 0.36,0.37 0.13,0.17 0.09,0.17 0.24,0.83 0.74,0.94 0.4,0.43 0.52,0.87 0.5,1.99 0.86,1.24 0.38,0.55 0.52,-0.18 2.57,-0.4 1.64,0.11 10.47,3.69 2.76,0.27 1.66,0.84 0.89,0.26 3.92,0.17 4,0.86 1.48,0.06 6.51,-0.94 6.55,0.07 3.44,-0.66 0.86,-1.72 0.21,-0.75 0.49,-0.14 1.3,0.42 0.9,0.08 0.69,-0.23 0.57,-0.53 0.55,-0.79 1,-0.35 2.38,-0.5 0.77,-0.72 0.22,-1.1 -0.16,-0.93 -0.05,-0.96 0.54,-1.17 1.35,-1.56 0.3,-0.78 0.07,-1.41 -0.14,-1.18 -0.47,-2.05 0.01,-1.07 1.42,-4.09 -0.21,-1.45 -3.45,-1.93 -1.51,-1.45 -1.37,-1.69 -1,-1.51 -0.47,-1.11 -0.27,-1.31 0.07,-1.3 0.56,-1.04 0.71,-0.33 -0.06,0.01 -5.9,0.43 -1.81,-0.26 -2.55,1.11 -1.39,0.14 -0.47,-1.42 0.19,-0.28 0.29,-0.36 0.65,-0.37 0.27,-0.23 0.15,-0.23 0.06,-0.25 -0.22,-0.24 -0.41,-0.25 -1.01,-0.24 -0.55,-0.05 -0.44,0.07 -0.49,0.3 -0.21,0.09 z\"\n        />\n      <path\n        id=\"AF-KDZ\"\n        title=\"Kunduz\"\n        \n        d=\"m 425.06715,84.321194 -4.59,-3.11 -0.25,-0.12 -1.08,-0.74 -0.45,-0.51 -0.86,-2.16 -0.88,-0.74 -1.1,-0.73 -0.97,-0.88 -0.71,-2.22 -0.76,-1.07 -0.98,-0.9 -0.96,-0.54 -3.31,-0.41 -0.17,-0.08 -0.28,-0.06 -0.13,0.21 0.25,0.72 0.31,0.4 0.79,0.59 0.22,0.26 0.17,0.88 -0.25,0.55 -0.54,0.14 -0.7,-0.32 -0.65,-0.86 -0.43,-0.98 -0.55,-0.78 -1.01,-0.26 -1.07,0.35 -0.18,0.69 0.58,1.84 0.12,1.3 -0.2,0.59 -0.58,-0.04 -1.8,-1.17 -0.57,-0.27 -0.63,-0.06 -1,0.1 -1.89,-0.27 -0.88,0.04 -0.55,0.65 0.25,0.24 0.13,0.27 0.28,0.74 -1.09,0.17 -0.6,0.88 -0.27,0.98 -0.19,0.46 -0.87,0.13 -1.82,0.58 -0.97,0.12 -0.23,0.27 -0.67,1.17 -0.43,0.26 -0.52,0.05 -2.3,0.52 -3.01,0.23 0.69,1.42 -0.32,0.91 -0.95,0.48 -1.26,0.13 -0.96,-0.22 -0.94,-0.37 -0.94,-0.08 -0.92,0.67 -0.36,1.01 0.04,1.23 0.32,2.55 -0.51,0.87 -1.17,-0.01 -1.99,-0.67 -1.27,0.18 -0.9,0.75 -0.77,0.8 -0.9,0.38 -0.6,0.38 -3.21,2.77 -0.49,0.33 -0.59,0.24 -0.38,-0.01 -0.01,0.08 0.47,2.51 0.1,3.479996 0.09,0.51 2.61,6.4 1.3,2.3 0.3,0.35 0.78,0.57 0.97,0.92 0.67,0.9 0.32,0.66 0.37,1.43 0.86,0.87 1.59,0.46 2.35,0.56 1.21,0.7 0.45,0.14 2.58,-0.13 7.91,0.55 4.11,1.17 0.56,0.04 0.5,-0.07 0.78,-0.3 2.98,-0.43 0.6,1.34 3.08,3.89 2.12,1.68 0.52,0.31 0.44,0.12 0.33,-0.08 0.35,-0.15 1.35,-0.85 0.27,-0.12 0.24,-0.04 1.2,0.24 2.32,0.44 0.59,0.27 0.05,0.13 0.05,0.12 0.06,0.07 0.57,0.44 0.33,0.36 0.59,0.78 1.39,1.24 0.94,0.6 1.96,0.71 0.88,0.13 1.85,0.48 0.61,0.28 0.39,0.28 0.22,0.33 0.18,0.33 0.67,0.98 -0.01,-0.97 -0.11,-0.48 -0.2,-0.63 -0.45,-0.89 -0.18,-0.63 -0.03,-0.81 0.07,-0.6 -0.02,-0.54 -0.16,-0.46 -0.44,-0.57 -0.6,-1.03 -0.53,-2.18 -0.49,-0.85 -0.41,-0.58 -0.01,-0.25 0.04,-0.2 0.76,-0.72 0.58,-0.24 0.21,-0.13 0.11,-0.13 0.07,-0.15 0.05,-0.17 0.09,-0.16 0.13,-0.16 1.37,-1.13 0.21,-0.27 0.2,-0.3 0.16,-0.42 0.36,-1.34 0.93,-2.03 0.06,-0.52 -0.04,-0.55 -0.57,-1.24 -0.31,-0.52 -0.13,-0.14 -0.12,-0.09 -0.1,-0.03 -0.25,-0.04 -0.16,-0.1 -0.07,-0.23 0.08,-0.54 0.02,-0.45 -0.06,-0.49 -0.2,-0.57 -0.43,-0.77 -0.04,-0.1 -0.03,-0.26 0.02,-0.18 0.07,-0.13 0.32,-0.4 0.07,-0.4 0.05,-1.28 -0.18,-1.24 0.04,-0.42 0.04,-0.22 0.66,-0.72 2.23,-0.26 0.13,-0.05 0.18,-0.1 0.53,-0.64 0.56,-1.699996 0.58,-8.51 -0.02,-0.5 -0.15,-0.8 -0.07,-0.59 0,-0.57 0.33,-1.41 0.11,-0.23 0.91,-1.2 -0.03,-0.37 -0.06,-0.28 -1.33,-1.54 -0.07,-0.21 -1.12,0.24 -0.96,0.46 -0.98,0.09 z\"\n        />\n      <path\n        id=\"AF-KNR\"\n        title=\"Kunar\"\n        \n        d=\"m 531.75715,214.23119 0.71,-0.29 0.47,-0.41 0.34,-0.63 0.42,-1.37 0.39,-0.63 0,0 0,0 0,-0.01 0.53,-0.38 0.34,-0.48 0.08,-0.55 -0.26,-0.6 0.07,-2.6 1.26,-1.57 3.35,-2.35 1.22,-1.89 0.42,-1.16 -0.2,-0.8 -1.48,-1.2 -2.69,-3.11 -0.87,-1.5 0.08,-1.61 1.49,-1.94 2.37,-2.07 0.48,-0.88 0.07,-1.16 -0.41,-0.8 -0.64,-0.73 -0.36,-0.52 -0.33,0.61 -0.34,0.4 -0.48,0.42 -1.04,0.72 -0.61,0.29 -1.01,0.34 -0.4,0.27 -0.35,0.6 -0.33,0.37 -1.13,0.52 -0.22,0.28 -0.17,0.41 -0.42,1.64 -0.45,0.34 -2.1,0.2 -0.87,-0.21 -0.73,-0.62 -0.66,0.09 -5.77,2.2 -1.26,0.9 -0.68,1 -0.57,1.01 -0.27,0.96 0,2.11 -1.05,1.36 -0.26,0.45 -0.28,0.66 -0.39,2.1 -0.46,1.17 -0.24,0.49 -0.51,0.35 -0.39,-0.8 -0.2,-0.27 -0.25,-0.15 -0.47,0.23 -0.43,0.27 -1.96,2.4 -1.46,-0.43 -0.39,-0.17 -0.68,-0.12 -0.74,0 -3.28,0.56 -0.58,0.18 -0.56,0.27 -0.66,0.08 -0.48,-0.02 -2.71,-0.92 -2.22,-0.39 -0.78,-0.25 -0.93,-0.44 -1.43,-0.92 -1.68,-0.78 -1.13,3.33 -0.34,0.78 -2.29,4.04 0.6,0.79 0.23,0.71 0.07,0.63 -0.01,0.54 0.04,0.57 0.27,0.7 0.6,1.14 0.28,1.09 0.14,1.11 -0.02,2.33 -0.21,1.86 1.3,0.76 0.79,1.08 0.18,0.44 0.22,0.72 0.05,0.35 0,0.32 -0.09,0.88 -0.12,0.47 -0.33,0.71 -0.23,0.89 -0.22,1.4 0,0.93 0.26,1.09 1.51,2.01 0.27,0.17 0.35,0.19 0.21,-0.09 0.49,-0.3 0.44,-0.07 0.55,0.05 1.01,0.24 0.41,0.25 0.22,0.24 -0.06,0.25 -0.15,0.23 -0.27,0.23 -0.65,0.37 -0.29,0.36 -0.19,0.28 0.47,1.42 1.39,-0.14 2.55,-1.11 1.81,0.26 5.9,-0.43 0.06,-0.01 0.12,-0.05 1.03,-0.01 1.97,0.31 0.88,-0.44 0.53,-1.18 0.16,-1.44 -0.18,-1.22 -0.36,-1.31 0.06,-0.9 0.51,-0.69 3.41,-2.8 1.87,-0.99 0.66,-0.65 2.55,-3.67 0.74,-2.01 0.9,-1.82 1.72,-1.33 5.84,-2.38 z\"\n        />\n      <path\n        id=\"AF-NUR\"\n        title=\"Nuristan\"\n        \n        d=\"m 538.93715,182.99119 -0.27,-0.4 -0.29,-1.33 0.21,-1.13 0.33,-1.07 0.03,-1.15 -0.45,-0.88 -0.8,-0.6 -2.67,-1.32 -0.97,-0.79 -0.46,-1.01 0.61,-1.2 0.88,-1.13 0.28,-1.09 -0.21,-1.06 -0.61,-1.04 -0.33,-0.33 -0.71,-0.57 -0.28,-0.42 -0.22,-0.67 0,-0.54 0.05,-0.52 -0.06,-0.59 -0.31,-0.89 -1.62,-2.93 -0.7,-0.9 -2.2,-1.58 -0.51,-0.9 -0.21,-1.97 -0.72,-0.85 -1.41,-0.6 -1.37,-0.31 -1.3,-0.54 -1.24,-1.35 -0.68,-0.52 -1.75,-0.97 -0.53,-0.49 -0.05,-0.22 0,0 -0.75,0.63 -0.54,0.62 -0.47,0.72 -0.7,1.31 -0.4,0.64 -0.38,0.45 -0.63,0.49 -0.26,0.46 -0.23,0.67 -0.38,2.23 -0.1,1.11 -0.15,0.61 -0.24,0.62 -1.41,2.16 -0.42,0.81 -0.22,0.32 -0.28,0.23 -0.71,0.38 -0.72,0.28 -0.29,0.18 -0.26,0.37 -0.19,0.54 -0.16,0.93 0.14,0.67 0.22,0.37 1.12,0.65 0.34,0.28 0.29,0.32 0.24,0.36 -0.03,0.43 -0.28,0.45 -1.4,1.28 -0.93,1.66 -1.78,0.81 -0.93,-0.09 -0.34,-0.09 -0.34,-0.03 -0.3,0.12 -0.29,0.28 -0.44,1.01 -0.13,0.51 0.05,0.38 0.08,0.41 0.03,0.35 -0.06,0.66 0.06,0.3 0.27,0.62 0.07,0.33 0.09,0.73 0.18,0.8 0.03,0.42 -0.06,0.4 -0.12,0.32 -0.58,0.92 -0.12,0.27 -0.38,0.13 -0.63,-0.05 -2.44,-0.6 -1.73,0.25 -1.35,-0.52 -0.77,-0.81 -0.98,-1.29 -0.32,-0.25 -0.44,0.08 -0.49,0.38 -0.84,1.11 -0.22,0.56 -0.04,0.49 0.05,0.3 -0.02,0.31 -0.08,0.31 -0.12,0.32 -0.34,0.39 -0.48,0.37 -1.04,0.55 -1.29,0.46 -1.62,-0.23 -0.27,-0.75 -0.12,-0.87 -0.02,-0.51 0.04,-0.97 0.07,-0.52 0.29,-1.01 0.49,-1.21 0.1,-0.41 0.04,-0.45 -0.08,-0.48 -0.2,-0.44 -0.36,-0.51 -2.12,-2.34 -0.16,-0.42 -0.08,-0.4 -0.1,-0.83 -0.12,-0.37 -0.16,-0.34 -0.35,-0.32 -0.48,-0.28 -2.24,-0.75 -1.35,-0.72 -0.56,-0.11 -0.6,0.06 -0.88,0.39 -0.53,0.36 -1.16,1.06 -1.19,0.37 -3.05,-0.59 -0.56,1.46 -0.2,2.82 -0.23,0.78 -0.37,0.54 -1.2,0.69 -0.32,0.23 -0.08,0.31 0.02,0.68 0.13,0.27 0.17,0.24 0.11,0.29 -0.05,0.44 -0.26,0.64 -0.42,0.77 -0.61,0.69 -0.79,0.62 -1.13,0.35 -0.69,0.02 -0.49,-0.16 -0.27,-0.14 -0.25,0.05 -0.83,1.42 -0.22,0.6 -1.13,1.58 -3.61,1.89 -0.83,0.53 -0.5,0.5 -0.4,0.64 -0.08,0.76 1.95,1.72 0.47,0.9 0.23,0.6 -0.12,3.15 0.28,1.03 1.26,2.02 0.3,0.64 0.19,0.59 0.14,2.33 0.48,1 0.44,0.37 1.66,-0.08 3.88,0.69 1.31,1.24 0.67,2.52 1.27,1.74 1.06,1.14 1,2.17 0.55,0.36 2.22,0.35 2.28,1.39 0.66,0.18 0.69,-0.02 2.52,-0.73 2.57,-1.08 0.57,-0.32 2.31,-1.94 0.9,-1.26 2.29,-4.04 0.34,-0.78 1.13,-3.33 1.68,0.78 1.43,0.92 0.93,0.44 0.78,0.25 2.22,0.39 2.71,0.92 0.48,0.02 0.66,-0.08 0.56,-0.27 0.58,-0.18 3.28,-0.56 0.74,0 0.68,0.12 0.39,0.17 1.46,0.43 1.96,-2.4 0.43,-0.27 0.47,-0.23 0.25,0.15 0.2,0.27 0.39,0.8 0.51,-0.35 0.24,-0.49 0.46,-1.17 0.39,-2.1 0.28,-0.66 0.26,-0.45 1.05,-1.36 0,-2.11 0.27,-0.96 0.57,-1.01 0.68,-1 1.26,-0.9 5.77,-2.2 0.66,-0.09 0.73,0.62 0.87,0.21 2.1,-0.2 0.45,-0.34 0.42,-1.64 0.17,-0.41 0.22,-0.28 1.13,-0.52 0.33,-0.37 0.35,-0.6 0.4,-0.27 1.01,-0.34 0.61,-0.29 1.04,-0.72 0.48,-0.42 0.34,-0.4 0.33,-0.61 z\"\n        />\n      <path\n        id=\"AF-BDS\"\n        title=\"Badakhshan\"\n        \n        d=\"m 508.89715,0.25119373 -1.13,0.04 -0.35,0.44 0.16,1.40999997 -0.51,0.63 -1.16,-0.29 -2,-0.96 -0.57,0.09 -0.28,0.27 -0.18,0.28 -0.33,0.17 -0.43,-0.03 -0.79,-0.27 -0.44,-0.09 -1.53,0 -0.79,0.18 -0.33,0.43 -0.6,1.06 -2.84,0.3 -0.89,0.57 0,0.42 0.96,0.74 -0.39,0.72 -1.71,1.29 -0.45,0.25 -1.11,-0.07 -0.45,0.26 -0.2,0.55 -0.01,0.64 0.05,0.63 0,0.5200003 -0.66,2.11 -0.49,0.25 -0.66,0.17 -0.59,0.34 -0.45,1.52 -1.42,2.82 -1.26,3.4 -0.6,1.04 -0.5,0.5 -1.65,0.72 -0.51,0.38 -2.15,2.23 -2.17,2.9 -0.41,0.3 -0.94,0.5 -0.23,0.13 -0.25,0.32 -0.24,0.55 -0.53,0.23 -0.58,0.11 -0.33,0.19 -0.17,0.57 -0.02,0.52 0.17,0.51 0.38,0.49 -1.13,0.43 -1.14,0.19 -1.09,-0.22 -0.97,-0.81 -0.57,1.56 0.23,1.89 0.68,1.79 0.82,1.28 0.55,0.26 1.6,0.5 0.33,0.52 0.13,0.87 0.32,0.81 0.44,0.65 0.46,0.43 -0.26,0.71 0.1,0.85 0.49,1.79 0.06,0.9 -0.12,0.54 -0.17,0.49 -0.1,0.77 -0.61,0.89 -0.23,0.17 -0.2,0.22 0.01,0.49 0.07,0.56 -0.06,0.41 -0.28,0.45 -0.1,0.13 -0.25,0.25 -0.16,-0.06 -0.94,0.04 -0.09,0.02 -0.71,1.79 -0.12,0.53 -0.32,0.2 -1.69,2.12 -1.4,0.56 -1.46,-0.27 -1.44,-0.52 -1.34,-0.23 0.31,0.46 -1.51,-0.28 -0.45,-0.18 -0.17,-0.15 0.02,0.06 -0.29,1.19 0.18,6.32 0.14,1.21 0.18,0.64 0.27,0.08 0.43,0.05 0.41,0.1 0.41,0.2 0.07,0.17 -0.1,0.22 -0.25,0.26 -0.55,0.76 -0.23,0.48 -0.38,1.59 -0.3,3.47 0.13,1.64 1.12,2.93 0.21,0.78 0.05,0.66 -0.18,1.39 0.06,1.19 0.28,0.75 0.39,0.56 1.82,1.38 0.78,0.96 1.15,1.98 -1.15,1.93 -0.21,0.89 -0.21,0.2 -0.38,0.29 -1.33,0.51 -0.7,1.2 -0.02,0.35 -0.09,0.53 -0.12,0.25 -0.28,0.21 -0.39,0.18 -0.61,0.49 -0.21,0.27 0.06,0.48 0.07,13.239996 -0.29,2.67 0.03,2.21 0.23,1.42 0.36,0.94 0.25,1.48 0.62,1.57 0.16,0.62 0.05,0.58 -0.13,2.16 0.13,0.47 0.23,0.29 1.94,1.07 2.74,2.09 3.38,4.04 0.72,0.65 0.53,0.28 1,-0.25 0.35,0 1.62,0.33 3.16,1.03 0.33,0.01 1.84,-0.17 1.67,0.16 0.54,0.17 0.4,0.24 0.21,0.21 0.4,0.6 0.24,6.34 -0.13,1.72 -0.57,0.85 -0.13,0.29 -0.11,0.36 -0.19,1.01 -0.5,1.7 -0.04,0.49 0.05,0.57 -0.06,0.29 -0.42,0.6 -0.25,0.54 -0.01,0.47 0.14,0.47 0.24,0.45 1.01,1.47 0.29,0.71 -0.08,0.47 -0.24,0.35 -2.46,1.27 -0.29,0.23 -0.91,1.15 -0.54,0.5 -2.11,1.35 -1.1,0.48 -0.17,0.12 -0.16,0.18 -0.31,0.63 -0.2,0.31 -0.17,0.22 -0.48,0.41 -0.94,2.68 -0.17,2.64 -0.23,1.02 -0.35,2.97 -0.01,0.66 -0.35,0.78 3.05,0.59 1.19,-0.37 1.16,-1.06 0.53,-0.36 0.88,-0.39 0.6,-0.06 0.56,0.11 1.35,0.72 2.24,0.75 0.48,0.28 0.35,0.32 0.16,0.34 0.12,0.37 0.1,0.83 0.08,0.4 0.16,0.42 2.12,2.34 0.36,0.51 0.2,0.44 0.08,0.48 -0.04,0.45 -0.1,0.41 -0.49,1.21 -0.29,1.01 -0.07,0.52 -0.04,0.97 0.02,0.51 0.12,0.87 0.27,0.75 1.62,0.23 1.29,-0.46 1.04,-0.55 0.48,-0.37 0.34,-0.39 0.12,-0.32 0.08,-0.31 0.02,-0.31 -0.05,-0.3 0.04,-0.49 0.22,-0.56 0.84,-1.11 0.49,-0.38 0.44,-0.08 0.32,0.25 0.98,1.29 0.77,0.81 1.35,0.52 1.73,-0.25 2.44,0.6 0.63,0.05 0.38,-0.13 0.12,-0.27 0.58,-0.92 0.12,-0.32 0.06,-0.4 -0.03,-0.42 -0.18,-0.8 -0.09,-0.73 -0.07,-0.33 -0.27,-0.62 -0.06,-0.3 0.06,-0.66 -0.03,-0.35 -0.08,-0.41 -0.05,-0.38 0.13,-0.51 0.44,-1.01 0.29,-0.28 0.3,-0.12 0.34,0.03 0.34,0.09 0.93,0.09 1.78,-0.81 0.93,-1.66 1.4,-1.28 0.28,-0.45 0.03,-0.43 -0.24,-0.36 -0.29,-0.32 -0.34,-0.28 -1.12,-0.65 -0.22,-0.37 -0.14,-0.67 0.16,-0.93 0.19,-0.54 0.26,-0.37 0.29,-0.18 0.72,-0.28 0.71,-0.38 0.28,-0.23 0.22,-0.32 0.42,-0.81 1.41,-2.16 0.24,-0.62 0.15,-0.61 0.1,-1.11 0.38,-2.23 0.23,-0.67 0.26,-0.46 0.63,-0.49 0.38,-0.45 0.4,-0.64 0.7,-1.31 0.47,-0.72 0.54,-0.62 0.75,-0.63 0,0 -0.19,-0.9 0.46,-0.93 1.56,-1.58 0.24,-0.56 0.25,-1.27 0.26,-0.44 1.9,-1.25 1.48,-0.7 0.45,-0.33 0.34,-0.56 0.24,-1.28 0.25,-0.4 3.07,-1.08 0.99,-0.77 3.7,-4.16 0.81,-0.55 0.91,-0.33 1.18,-0.25 0.94,-0.51 -0.27,-0.79 -0.51,-0.93 0.24,-0.92 1.22,-1.21 0.39,-0.64 0.41,-0.97 0.58,-1.86 0.47,-0.52 0.91,-0.1 0.97,0.41 2.8,1.89 1.42,1.54 0.72,0.29 0.77,-0.05 0.76,-0.28 0.57,-0.67 -0.14,-0.82 -0.82,-1.64 -0.07,-1.9 1,-0.65 3.14,-0.21 0.79,-0.29 0.62,-0.54 0.59,-0.63 0.67,-0.55 2.3,-1.33 0.78,-0.81 1.01,-0.57 1.99,-0.49 0.78,-0.74 0.06,-0.55 -0.2,-0.51 -0.08,-0.5 0.42,-0.51 0.57,-0.32 1.22,-0.4 2.82,-0.39 0.85,-0.5 -0.34,-0.99 -0.73,-1.17 0.19,-0.77 0.78,-0.56 2.13,-0.91 4.29,-0.99 2.14,-0.13 2,-0.66 2.22,0.15 1.02,-0.28 3.04,-2.58 2.35,-1.21 3.12,-0.75 3.21,-0.23 4.05,0.6 4.29,-0.22 1.41,-0.39 1.18,-0.629996 1.2,-0.3 1.5,0.619996 0.66,0.03 0.58,-0.279996 1.16,-0.81 0.79,-0.29 7.24,-0.77 1.53,0.16 1.42,0.38 0.72,0.09 0.69,-0.08 2.43,-0.86 2.32,0.17 3.21,-0.44 1.5,0.22 1.58,0.26 6.34,-1.09 3.37,0.14 0.07,0.01 0.01,0 0.03,0 2.96,0.13 2.97,0.55 1.5,0.63 3.97,2.519996 1.45,0.37 1.43,0.55 1.41,0.01 2.86,-0.95 0.47,-0.61 0.23,-2.079996 0.32,-0.84 0.69,-0.54 0.8,-0.2 3.18,0.4 1.17,-0.43 2.37,-1.94 4.05,-2.58 1.26,-1.05 2.01,-0.56 1.04,-0.07 0.95,0.31 0.18,0.14 0.16,0.18 0.88,1.31 0.95,0.85 0.78,-0.23 0.33,-1.93 0.19,-0.46 0.06,-0.45 -0.09,-0.41 -0.25,-0.36 -1.12,-0.52 -0.65,-1.02 -0.57,-1.18 -0.85,-1.02 -3.02,-1.5 -1.54,-1.14 -0.8,-1.29 0.11,-1.17 0.68,-0.31 1.84,0.25 1.03,-0.15 0.73,-0.42 0.54,-0.76 0.43,-1.23 0.52,-0.97 0.62,-0.34 1.56,-0.04 2.12,-0.6 0.68,-0.08 0.32,0.16 0.6,0.64 0.29,0.12 0.41,-0.15 0.19,-0.22 0.13,-0.24 0.63,-0.61 0.16,-0.4 0.23,-0.29 0.97,-0.1 0.26,-0.15 1.09,-1.01 0.51,-0.64 0.59,-0.42 0.77,0.1 0.32,0.47 1.91,4.19 0.52,0.34 0.92,-0.09 3.84,-0.95 -1.44,-0.82 -2.22,-3.8 -1.37,-1.48 -6.21,-3.83 -1.41,0.78 -4.93,0.33 -0.42,0.01 -0.41,-0.1 -1.75,-0.54 -1.06,-0.46 -0.91,0.08 -0.85,0.19 -1.41,-0.26 -0.54,-0.02 -0.49,-0.16 -0.7,-1.19 -0.48,-0.31 -0.55,0.04 -0.56,0.39 -0.26,-0.79 -0.57,1.63 -1.18,0.16 -1.41,-0.38 -1.28,0.02 -0.79,0.82 -0.08,2.1 -0.89,1.05 -1.19,0.51 -5.39,1.08 -3.65,1.34 -1.03,0.22 -2.73,1.29 -2.08,0.24 -0.98,0.3 -1.84,1.72 -0.69,0.16 -0.74,-0.19 -1.1,-0.1 -0.48,0.18 -0.79,0.61 -0.52,0.04 -0.33,-0.26 -0.69,-0.95 -0.37,-0.34 -1,-0.08 -2.05,0.64 -0.78,-0.47 -0.21,-1.27 0.62,-1.2 1.02,-0.89 1.02,-0.37 1.86,-0.19 0.71,-0.35 0.83,-0.7 0.82,-0.97 0.32,-0.88 0.01,-2.57 0.06,-0.5 0.31,-1.54 -1.73,-0.21 -2.1,0.04 -3.37,-0.9 -5.81,-2.15 -2.17,0.07 -3.01,1.67 -0.81,-0.24 -0.81,-0.49 -1.16,-0.15 -1.2,0.12 -0.99,0.34 -0.76,0.57 -2.37,2.55 -0.53,0.25 -1.04,-0.4 -0.43,0.15 -1.85,1.46 -0.76,0.94 -0.83,1.78 -0.56,0.84 -0.98,0.67 -3.49,0.35 -3.46,2.1 -1.07,1.29 -1.2,0.42 -2.28,0.43 -1.93,1.19 -1.44,1.99 -2.26,4.72 -2.02,3.19 -0.3,1.17 -0.43,0.57 -7.23,1.08 -1.63,0.83 -3.35,-0.62 -2.18,0.45 -4.91,2 -1.91,1.32 -0.48,0.54 -0.7,1.07 -0.45,0.46 -1.64,0.95 -1.27,1.4 -14.07,10.509996 -1.89,0.79 -2.38,0.45 -2.36,0 -2.28,-0.51 -2.03,-1.07 -1.64,-1.72 -0.65,-1.06 -0.53,-1.14 -0.37,-1.24 -0.33,-2.79 -0.49,-1.219996 -2.75,-4.48 -0.42,-1.09 -0.15,-1.32 0,-2.49 -0.17,-0.71 -0.84,-1.31 -0.29,-0.65 -0.1,-0.74 0.1,-3.67 0.38,-2.5 0.28,-0.93 0.34,-0.55 -0.18,-1.46 1.79,-3.07 0.04,-1.7 0.29,-0.77 0.13,-1.28 -0.09,-2.57 -0.36,-2.35 0.15,-0.87 0.55,-1.37 0.47,-2.45 -0.69,-4.93 0.22,-2.68 0.17,-0.35 0.59,-0.81 0.23,-0.51 0.13,-0.62 0.23,-1.89 0.56,-1.91 0.08,-0.6 -0.11,-0.67 -0.45,-1.31 -0.08,-0.57 0.4,-1.1 1.79,-1.17 0.76,-1.08 0.19,-1.11 0.04,-1.46 -0.08,-1.34 -0.15,-0.74 0.36,-0.43 -1.47,-1.83 -1.47,-1 -1.74,-0.11 -5.92,2.04 -0.88,0.68 -0.96,0.53 -1.04,-0.45 -1.84,-1.5 -0.52,-0.07 -0.37,0.11 -0.25,-0.13 -0.18,-0.79 0.06,-0.65 0.49,-1.39 0.32,-1.57 1.45,-2.73 0.66,-2.52 0.9,-1.75 0.31,-1.8 0.88,-2.67 0.26,-1.4 -0.31,-2.74 -1.15,-1.82 -1.66,-1.11 -4,-1.6800003 -2.99,-3.12 -1.87,-1.38 -1.36,-0.7 -0.6,-0.14 -0.59,0.02 -0.38,0.17 -0.35,0.03 -0.5,-0.42 -0.32,-0.51 -0.41,-1.13 -0.28,-0.49 -0.49,-0.55 -0.5,-0.42999997 -0.56,-0.33 -0.62,-0.17 z\"\n        />\n      <path\n        id=\"AF-PKA\"\n        title=\"Paktika\"\n        \n        d=\"m 408.15715,303.86119 -0.51,-0.1 -0.91,-0.51 -1.25,0.21 -3.54,1.31 -0.11,0.09 -3.38,3.02 -0.56,0.8 -0.51,1.62 -0.21,1.07 -0.31,0.95 -0.47,0.95 -0.56,0.86 -2.89,3.31 -4.54,3.4 -0.58,0.75 -0.37,0.73 -2.21,6.69 -2,2.12 -0.35,0.72 -0.6,0.99 -3.35,3.27 -0.58,0.5 -0.48,0.44 -0.09,0.23 -0.11,0.56 -0.08,0.92 -0.36,0.62 -1.28,1.33 -1.97,1.53 -0.64,0.37 -1.29,0.51 -1.29,0.27 -2.94,0.22 -0.63,0.15 -0.39,-0.04 -0.47,-0.19 -2,-1.28 -4.15,-4.11 -2.38,2.84 -1.95,3.51 -0.13,0.35 -0.05,0.25 0.02,0.26 1.03,1.16 4.49,3.43 0.39,1.35 0.45,0.61 1.42,0.94 2.14,2.09 0.25,0.17 1.02,0.46 0.16,0.12 0.18,0.19 0.19,0.37 0.96,3.03 0.32,1.79 0.2,0.63 0.09,0.46 -0.02,0.38 -0.4,1.07 -0.15,0.6 -0.01,0.63 0.35,2.35 -0.1,2.13 -0.48,0.68 -0.14,0.35 -0.09,0.46 0.1,0.66 0.64,2.39 0.06,0.49 0.01,0.49 -0.32,1.59 -1.3,3.03 0.83,2.7 0.37,2.12 0.03,0.43 -0.01,0.18 -0.15,0.54 -0.58,1.56 -0.86,2 -1.45,2.22 -0.19,0.43 -0.03,0.32 -0.02,0.35 0.05,0.88 0.15,1.14 0.13,0.57 0.15,0.52 0.23,0.52 0.26,0.44 0.37,0.43 0.86,0.71 1.79,1.07 0.34,0.25 0.01,0 0.3,-0.55 1.03,-2.43 0.62,-0.76 1,-0.07 1.24,0.44 1.07,0.59 1.23,1.01 0.51,0.76 0.6,0.6 1.06,0.16 1.92,-0.09 1.92,0.15 5.09,1.83 1.79,0.3 0.83,-0.27 1.05,-0.63 0.63,-0.72 -0.44,-0.51 -0.94,-0.13 -2.9,0.07 -1.06,-0.17 -0.84,-0.38 -0.17,-0.56 0.94,-0.74 2.1,-1.12 1.15,-0.4 1.08,-0.02 1.63,0.64 2.83,1.61 2.73,0.51 0.61,0.34 0.32,0.7 0.15,2.31 0.36,0.83 1.25,1.47 2.24,3.24 1.28,0.92 1.93,-0.23 3.08,-1.58 1.65,-0.53 1.76,0.12 1.29,-0.55 1.75,-1.25 1.53,-1.41 0.64,-1.02 0.74,-0.47 0.69,-0.79 5.31,-8.23 1.32,-1.43 2.32,-1.75 0.12,-0.21 0.17,-0.32 -0.13,-0.74 -1.52,-4.35 -0.95,-5.42 0.04,-1.24 0.37,-2.42 0.02,-2.43 0.34,-2.45 0.05,-1.25 -0.18,-1.2 -1.25,-3.21 -0.51,-2.47 0.23,-2.38 0.91,-2.18 1.47,-1.85 0.98,-0.64 2.01,-0.7 0.86,-0.72 2.56,-3.86 0.58,-1.12 0.15,-0.71 -0.48,-1.91 -0.09,-1.01 -0.13,-0.44 -0.34,-0.25 -0.41,-0.17 -0.43,-0.29 -0.34,-0.38 -0.25,-0.45 -0.09,-1.13 0.53,-0.77 1.64,-1.24 1.19,-1.67 1.25,-0.95 0.29,-0.27 0.48,-1.68 -0.91,-6.26 0.47,-0.99 -0.02,0 -4.56,-1.67 -1.86,-0.18 -0.22,-1.41 1.45,-1.65 1.42,-2.08 0.14,-0.47 0.02,-0.62 -0.19,-1.35 -0.19,-0.61 -0.18,-0.45 -0.09,-0.14 0.02,-0.65 0.53,-2.23 -0.97,-0.32 -0.19,-0.12 -0.34,-0.17 -0.78,0.08 -2.22,0.79 -0.88,0.13 -1.01,-0.2 -1.46,-1.16 -0.39,-0.32 -0.14,-0.14 -0.2,-0.15 -0.19,-0.09 -0.32,-0.08 -0.29,-0.02 -0.15,0.07 -0.11,0.15 0.01,0.36 0.04,0.24 0.04,0.18 0.02,0.16 -0.02,0.16 -0.4,0.41 -1.66,1.05 -2.39,1.08 -1.97,1.17 -1.31,1.25 -0.68,0.43 -0.72,0.3 -0.31,0.05 -0.16,0.01 -0.14,-0.03 -0.23,-0.38 -0.25,-1.32 -0.19,-0.64 0.01,-0.49 0.05,-0.28 0.02,-0.31 -0.42,-3.41 -0.04,-1.18 0.2,-2.74 -0.01,-0.6 -0.07,-0.39 -0.16,-0.32 -0.77,-1.06 -0.52,-1.02 -0.19,-0.21 -0.27,-0.21 -1.08,-0.49 -0.25,-0.04 -0.27,0.03 -0.37,0.16 -0.36,0.09 z\"\n        />\n      <path\n        id=\"AF-KHO\"\n        title=\"Khost\"\n        \n        d=\"m 464.83715,285.70119 -2.61,-1.13 -0.74,-0.1 -0.01,0.85 -0.13,0.41 -0.23,0.46 -0.81,0.9 -0.33,0.22 -1.2,0.39 -1.4,0.85 -1.12,0.49 -0.8,0.51 -1.15,0.41 -0.38,0.05 -0.45,0.3 -0.26,0.23 -1.46,2.22 -0.8,-0.62 -0.07,-0.04 -0.13,-0.02 -3.34,0.33 -2.99,0.91 -0.73,0.36 -0.74,0.46 -1.49,1.25 -0.26,0.71 -0.78,1.51 -0.27,0.69 0.05,0.71 0.82,0.89 -1.14,3.7 -0.27,0.69 -0.46,0.73 -0.31,0.38 -1.12,0.67 -2.62,2.35 -0.25,0.39 -0.13,0.63 -0.2,0.72 -0.85,1.03 -0.66,0.74 -0.16,0.28 -0.24,1.19 -0.53,2.23 -0.02,0.65 0.09,0.14 0.18,0.45 0.19,0.61 0.19,1.35 -0.02,0.62 -0.14,0.47 -1.42,2.08 -1.45,1.65 0.22,1.41 1.86,0.18 4.56,1.67 0.02,0 0.46,-0.98 1.29,-1.64 1.61,-1.06 1.96,-0.26 0.99,0.02 1.57,-0.3 0.46,0.14 0.42,0.2 0.44,0.08 0.88,-0.22 2.28,-1.65 1.88,-0.31 3.25,1.62 2.05,-0.15 3.63,-2.22 0.48,-0.1 1.4,0.12 0.59,-0.26 0.37,-0.52 1.63,-3.09 0.57,-0.23 1.16,0.39 1.05,0.09 0.94,-0.54 8.23,-6.95 0.35,-1.91 -0.77,-1.8 -2.56,-2.89 -0.25,-0.43 -0.36,-0.93 -0.34,-0.31 -1.35,-0.45 -0.39,-0.25 -0.67,-0.89 -0.42,-1.05 -0.18,-1.12 0.08,-1.09 0.99,-3.68 0.03,-1.41 -0.18,-0.38 -0.38,-0.27 -0.8,-0.39 -0.44,-0.37 -0.21,-0.3 -0.29,-0.9 -0.41,-2.31 -0.48,-0.63 -2.07,0.37 z\"\n        />\n      <path\n        id=\"AF-GHA\"\n        title=\"Ghazni\"\n        \n        d=\"m 358.21715,260.97119 -2.24,-0.17 -0.66,-0.16 -0.53,-0.27 -1.22,-1.16 -1.5,-1.04 -1.8,-0.69 -1.49,-0.34 -1.22,0.06 -3.29,0.7 -0.39,0.13 -0.43,0.23 -0.62,0.49 -0.32,0.32 -0.19,0.29 -0.29,1.34 -0.1,0.19 -0.24,0.18 -1.51,0.52 -0.81,0.01 -2.29,-0.31 -1.99,1.49 -0.63,0.8 -0.38,0.91 -0.21,0.4 -0.88,2.37 0.89,0.73 0.09,0.37 0.02,0.34 -0.31,0.6 -0.49,1.58 -0.14,0.24 -0.15,0.19 -0.52,0.45 -0.3,0.44 -0.03,0.29 0.12,0.24 0.54,0.35 0.7,0.35 0.08,0.06 0.25,0.39 0.34,0.88 0.66,2.12 -0.02,0.4 -0.17,0.3 -1.02,0.56 -0.36,0.25 -0.33,0.36 -1.66,2.39 -1.03,0.95 -3.06,2.66 -0.44,0.54 -0.55,0.82 -0.36,0.73 -0.76,1.02 -1.03,0.74 -0.98,0.52 -4.61,1.38 -0.27,0.03 -1.2,-0.05 -2.73,0.58 -4.31,1.76 -0.53,0.32 -0.21,0.19 -0.5,0.57 -0.68,1.71 -0.05,0.34 0.01,1.08 1.43,4.23 0.06,1.58 -0.18,0.48 -0.12,0.5 -0.1,0.65 -0.11,2.43 -0.05,0.32 -0.09,0.25 -0.14,0.24 -0.37,0.9 -0.35,2.68 0.73,1.85 1.26,0.23 4.95,-0.25 0.82,0.21 0.29,0.42 0.25,0.63 0.11,0.86 -0.02,0.56 -0.12,0.39 -0.16,0.17 -0.82,0.43 -0.27,0.22 -0.19,0.26 -0.36,0.64 -0.28,0.32 -1.69,1.23 -0.2,0.2 -0.11,0.21 -0.04,0.19 -0.09,0.68 -0.05,0.17 -0.15,0.82 0.08,1.62 -0.02,0.45 -0.1,0.31 -0.08,0.66 -0.02,0.94 0.22,2.07 0.22,1.03 0.33,0.91 0.4,0.59 0.44,0.42 0.59,0.28 2.34,0.54 0.58,0.23 1.5,0.01 0.26,-0.17 0.28,-0.21 0.12,-0.27 0.09,-0.3 0.61,-1.1 0.82,-1.17 0.33,-0.57 0.15,-0.42 -0.06,-0.24 -0.13,-0.26 -0.64,-1 -0.23,-0.44 -0.15,-0.42 -0.03,-0.51 0.09,-0.29 0.18,-0.2 0.24,-0.12 0.65,-0.21 1.34,-0.09 1.42,0.3 0.4,0.17 0.27,0.19 0.12,0.22 0.08,0.26 0.11,1.16 0.12,0.53 0.22,0.54 0.48,0.77 0.31,0.3 0.24,0.14 0.19,0.01 1.51,-0.2 0.49,-0.01 0.21,0.04 0.18,0.13 0.1,0.17 0.06,0.2 0.02,0.19 -0.03,0.43 0.05,0.25 0.15,0.12 0.68,0.21 0.28,0.2 0.42,0.4 0.22,0.11 0.29,-0.01 0.32,-0.1 1.51,0.01 0.32,-0.08 0.32,-0.13 0.25,-0.17 0.24,-0.24 0.43,-0.58 0.58,-0.96 0.79,-0.97 0.21,-0.16 0.3,-0.17 0.55,-0.08 0.24,0.05 0.11,0.14 -0.01,0.19 -0.06,0.2 -0.06,0.15 -0.19,0.33 -0.08,0.18 -0.04,0.15 0,0.13 0.03,0.06 0.05,0.08 0.28,0.31 0.19,0.27 0.09,0.14 0.13,0.27 0.11,0.33 0.08,0.37 0.01,0.33 -0.05,0.3 -0.12,0.27 -0.18,0.23 -0.2,0.21 -0.27,0.19 -1.85,1.03 -0.21,0.21 -0.11,0.27 0.04,0.32 0.25,0.45 0.3,0.22 1.7,0.69 0.39,0.3 0.26,0.3 0.12,0.24 0.05,0.2 -0.02,0.18 -0.08,0.15 -0.14,0.11 -0.21,0.07 -0.7,0.05 -0.17,0.07 -0.15,0.14 -0.14,0.2 -0.13,0.25 -0.1,0.27 -0.06,0.29 0,0.62 0.24,0.69 1.26,1.86 1.23,1.15 0.13,0.21 0.1,0.53 0.14,0.21 7.38,7.42 0.49,0.34 1.61,0.68 0.13,0.16 -0.06,0.19 -3.29,3.67 3.08,2.27 0.78,0.97 0.4,1.94 0.13,0.19 0.14,0.1 1.87,0.31 0.36,0.16 1.58,1.02 0.44,0.43 0.24,0.38 0.02,0.3 -0.05,0.31 -0.12,0.31 -0.22,0.34 -0.64,0.71 -0.82,0.76 -0.86,0.61 -1.33,0.66 -0.25,0.06 -0.2,0.02 -0.16,0 -0.48,-0.08 -0.74,-0.22 -0.18,0 -0.12,0.04 -0.19,0.12 -0.23,0.21 -0.28,0.32 -0.27,0.51 -0.19,0.59 -0.11,0.84 0.08,0.5 0.16,0.34 0.22,0.13 0.19,0.05 0.16,0 1.39,-0.43 0.28,-0.05 0.19,0 0.18,0.04 0.27,0.09 0.33,0.24 0.39,0.38 0.51,0.68 0.2,0.48 0.06,0.54 -0.34,2.19 -0.02,0.52 0.09,0.3 0.14,0.19 0.48,0.34 0.7,0.48 0.31,0.13 3.02,0.44 0.92,0.28 0.66,0.29 0.95,0.77 1.33,0.86 0.76,0.67 0.62,0.45 0.46,0.19 0.45,0.09 3.18,-0.08 0.3,-0.15 1.93,-1.29 1.3,-3.03 0.32,-1.59 -0.01,-0.49 -0.06,-0.49 -0.64,-2.39 -0.1,-0.66 0.09,-0.46 0.14,-0.35 0.48,-0.68 0.1,-2.13 -0.35,-2.35 0.01,-0.63 0.15,-0.6 0.4,-1.07 0.02,-0.38 -0.09,-0.46 -0.2,-0.63 -0.32,-1.79 -0.96,-3.03 -0.19,-0.37 -0.18,-0.19 -0.16,-0.12 -1.02,-0.46 -0.25,-0.17 -2.14,-2.09 -1.42,-0.94 -0.45,-0.61 -0.39,-1.35 -4.49,-3.43 -1.03,-1.16 -0.02,-0.26 0.05,-0.25 0.13,-0.35 1.95,-3.51 2.38,-2.84 4.15,4.11 2,1.28 0.47,0.19 0.39,0.04 0.63,-0.15 2.94,-0.22 1.29,-0.27 1.29,-0.51 0.64,-0.37 1.97,-1.53 1.28,-1.33 0.36,-0.62 0.08,-0.92 0.11,-0.56 0.09,-0.23 0.48,-0.44 0.58,-0.5 3.35,-3.27 0.6,-0.99 0.35,-0.72 2,-2.12 2.21,-6.69 0.37,-0.73 0.58,-0.75 4.54,-3.4 2.89,-3.31 0.56,-0.86 0.47,-0.95 0.31,-0.95 0.21,-1.07 0.51,-1.62 0.56,-0.8 3.38,-3.02 0.11,-0.09 0.28,-1 1.18,-2.61 0.06,-0.5 -0.11,-0.57 -0.43,-1.05 -0.58,-1.09 -0.19,-0.7 -0.12,-1.08 -0.09,-2.06 0.17,-2.12 0,-0.01 -0.14,-1.28 -0.35,-0.43 -0.04,-0.2 0.03,-0.67 -0.06,-0.22 -0.24,-0.2 -0.24,-0.17 -0.2,-0.24 -0.1,-0.31 0.1,-0.62 0.17,-0.46 0.2,-0.4 0.2,-0.19 0.19,-0.1 0.4,-0.08 0.12,-0.08 -0.03,-0.3 -0.16,-0.47 -0.45,-1 -0.38,-1.18 -0.07,-0.38 -0.24,-0.4 -0.19,-0.23 -1.46,-0.7 -1.16,1.14 -2.05,2.55 -0.33,0.27 -4.1,2.41 -0.42,0.17 -0.8,0.09 -0.51,0 -1.94,-0.48 -0.62,-0.28 -0.43,-0.3 -0.13,-0.15 -0.09,-0.13 -0.07,-0.37 0.01,-0.51 0.23,-0.96 0.4,-1.17 0.05,-0.46 0.01,-0.33 -0.45,-1.28 -1.08,0.04 -0.43,0.1 -0.48,0.04 -0.24,-0.01 -0.29,-0.15 -0.32,-0.28 -4.14,-4.37 -0.18,-0.29 -0.71,-0.77 -2.33,-1.45 -0.5,-0.86 -0.25,-0.2 -3.03,-1.23 -0.61,-0.17 -0.49,-0.32 -0.35,-1 -0.03,-0.82 -0.43,-1.46 -0.12,-0.59 -0.03,-0.51 0.07,-0.38 0.02,-0.43 -0.05,-0.43 -0.16,-0.64 -0.06,-0.51 -0.02,-0.49 0.08,-0.85 -0.03,-0.45 -0.48,-2.62 -0.25,-0.55 -0.39,-0.6 -1.46,-1.38 -0.72,-0.49 -1.06,-0.25 -0.69,0.45 -0.16,0.14 -0.34,0.19 -0.26,0.09 -0.7,0.58 -1.39,0.74 -3.09,1.08 -0.74,0.15 -0.64,0.04 z\"\n        />\n      <path\n        id=\"AF-ZAB\"\n        title=\"Zabul\"\n        \n        d=\"m 326.08715,328.39119 -0.19,-0.01 -0.24,-0.14 -0.31,-0.3 -0.48,-0.77 -0.22,-0.54 -0.12,-0.53 -0.11,-1.16 -0.08,-0.26 -0.12,-0.22 -0.27,-0.19 -0.4,-0.17 -1.42,-0.3 -1.34,0.09 -0.65,0.21 -0.24,0.12 -0.18,0.2 -0.09,0.29 0.03,0.51 0.15,0.42 0.23,0.44 0.64,1 0.13,0.26 0.06,0.24 -0.15,0.42 -0.33,0.57 -0.82,1.17 -0.61,1.1 -0.09,0.3 -0.12,0.27 -0.28,0.21 -0.26,0.17 -1.5,-0.01 -0.2,3.41 -0.07,0.42 -0.16,0.59 -0.21,0.32 -0.33,0.6 -0.2,0.24 -0.26,0.15 -0.24,0.08 -0.37,0.25 -0.21,0.1 -0.33,0.04 -0.38,-0.04 -0.41,-0.09 -0.27,-0.11 -0.52,-0.33 -0.43,-0.17 -0.57,0.06 -0.86,0.67 -0.32,0.18 -0.29,0.08 -1.19,-0.06 -1.49,0.11 -0.68,0.17 -0.55,0.29 -0.32,0.32 -3.17,2.02 -0.83,0.66 -0.75,0.76 -0.78,1.06 -0.15,0.26 -0.08,0.27 -0.02,0.28 0.12,0.67 0,0.33 -0.33,0.45 -0.54,0.37 -1.14,0.55 -0.63,0.16 -0.43,0.01 -0.2,-0.12 -0.1,-0.1 -0.06,-0.09 -0.06,-0.19 -0.02,-0.25 0.08,-0.38 1.09,-3.35 0.11,-0.51 0.01,-0.31 -0.06,-0.18 -0.15,-0.14 -0.76,-0.08 -2.14,0.23 -1.46,2.77 -0.78,0.69 -2.38,0.35 -2.31,1.15 -0.4,0.38 -0.58,1.01 -0.62,0.84 -2.38,2.68 -0.43,0.63 -0.04,0.36 0.02,0.4 -0.01,0.5 -0.06,0.57 -0.58,1.18 -0.13,0.33 0.02,0.25 0.13,0.33 0.17,0.34 0.45,0.68 1.55,0.62 0.4,0.04 0.64,-0.01 0.35,-0.16 0.28,-0.24 1.02,-1.57 0.56,-1.36 0.2,-0.38 0.33,-0.45 0.21,-0.18 0.2,-0.06 0.16,0.05 0.17,0.07 0.18,0.05 0.2,-0.01 0.52,-0.09 0.35,0.05 0.2,0.15 0.14,0.2 0.18,0.38 0.12,0.15 0.15,0.14 0.56,0.37 0.33,0.31 0.16,0.09 0.48,0.15 0.35,0.19 0.24,0.28 0.11,0.32 -0.02,0.32 -0.19,0.55 -0.03,0.28 0.07,0.97 -0.01,0.33 -0.03,0.35 -0.55,2.62 -0.11,0.29 -0.48,0.94 -0.64,1.11 -0.2,0.18 -0.45,0.29 -1.74,0.89 -0.52,0.4 -0.36,0.4 -0.65,1.85 -0.27,0.56 -0.56,1 -0.78,0.96 -1.13,0.9 -1.2,1.24 -0.25,0.4 -0.29,0.56 -0.16,0.65 0,0.46 0.09,0.47 0.23,0.59 0.06,0.29 0.06,0.73 0.11,0.4 0.05,0.11 0.03,0.15 0.07,1.27 0.22,1.27 -0.03,0.29 -0.11,0.31 -0.67,1.19 -0.62,1.49 -0.05,0.36 0.08,0.28 0.21,0.17 0.54,0.36 0.42,0.35 0.34,1.23 0.03,1.45 -0.11,0.39 -0.59,1.44 -1.01,1.45 -0.45,0.54 -0.2,0.14 -0.25,0.14 -1.79,0.53 -1.18,0.64 -0.66,0.45 -1.61,1.54 0,1.41 0.14,0.44 0.26,0.56 0.22,0.27 0.18,0.17 1.57,1.03 3.91,4.12 0.11,0.06 0.31,0.01 1.94,-1.37 1.16,-1.23 0.62,-0.49 0.95,-0.57 0.9,-0.38 0.99,-0.2 0.23,0 0.2,0.08 0.21,0.13 0.51,0.07 0.72,0.02 2.16,-0.47 0.52,-0.23 6.17,-5.14 0.57,-0.61 0.21,-0.07 0.18,0.08 0.34,0.46 0.2,0.22 2.28,1.88 0.75,0.78 2.16,1.12 1.93,0.62 0.47,0.21 0.4,0.33 0.4,0.4 0.87,0.72 1.09,0.59 0.77,0.57 0.45,0.22 0.4,0.13 0.28,0.01 1.3,0.3 2.22,0.49 1.07,-0.13 3.41,-1.01 1.01,-0.59 2.07,-0.78 1.33,2.19 0.56,1.72 0.11,0.2 0.13,0.07 0.7,0.07 0.31,0.08 0.36,0.16 0.16,0.11 0.07,0.08 0.67,1.46 0.34,0.42 0.33,0.29 0.52,0.26 0.45,0.17 0.1,0.03 0.11,0 0.12,-0.08 0.17,-0.19 0.26,-0.68 0.14,-0.22 0.2,-0.12 0.79,0.21 2.16,1.24 1.03,1.9 0.19,0.87 -0.07,0.23 -0.4,0.8 -0.13,0.42 0.01,0.26 0.08,0.15 0.26,0.12 0.25,0.07 0.28,0.04 0.26,0.01 0.27,-0.02 1.53,-0.35 0.29,-0.02 3.29,0.15 0.8,-0.09 0.73,-0.18 2.6,-0.98 1.13,-0.02 1.8,0.18 0.39,0.09 2.45,1.23 0.02,0.01 1.59,-1.13 2.99,-3.37 1.35,-0.67 0.67,-0.03 1.47,0.25 1.97,-0.11 0.52,-0.28 0.55,-0.78 0.81,-0.66 2.55,-1.42 0.27,-0.4 0.16,-1.21 0.24,-0.51 1.6,-1.48 0.24,-0.44 -0.01,0 -0.34,-0.25 -1.79,-1.07 -0.86,-0.71 -0.37,-0.43 -0.26,-0.44 -0.23,-0.52 -0.15,-0.52 -0.13,-0.57 -0.15,-1.14 -0.05,-0.88 0.02,-0.35 0.03,-0.32 0.19,-0.43 1.45,-2.22 0.86,-2 0.58,-1.56 0.15,-0.54 0.01,-0.18 -0.03,-0.43 -0.37,-2.12 -0.83,-2.7 -1.93,1.29 -0.3,0.15 -3.18,0.08 -0.45,-0.09 -0.46,-0.19 -0.62,-0.45 -0.76,-0.67 -1.33,-0.86 -0.95,-0.77 -0.66,-0.29 -0.92,-0.28 -3.02,-0.44 -0.31,-0.13 -0.7,-0.48 -0.48,-0.34 -0.14,-0.19 -0.09,-0.3 0.02,-0.52 0.34,-2.19 -0.06,-0.54 -0.2,-0.48 -0.51,-0.68 -0.39,-0.38 -0.33,-0.24 -0.27,-0.09 -0.18,-0.04 -0.19,0 -0.28,0.05 -1.39,0.43 -0.16,0 -0.19,-0.05 -0.22,-0.13 -0.16,-0.34 -0.08,-0.5 0.11,-0.84 0.19,-0.59 0.27,-0.51 0.28,-0.32 0.23,-0.21 0.19,-0.12 0.12,-0.04 0.18,0 0.74,0.22 0.48,0.08 0.16,0 0.2,-0.02 0.25,-0.06 1.33,-0.66 0.86,-0.61 0.82,-0.76 0.64,-0.71 0.22,-0.34 0.12,-0.31 0.05,-0.31 -0.02,-0.3 -0.24,-0.38 -0.44,-0.43 -1.58,-1.02 -0.36,-0.16 -1.87,-0.31 -0.14,-0.1 -0.13,-0.19 -0.4,-1.94 -0.78,-0.97 -3.08,-2.27 3.29,-3.67 0.06,-0.19 -0.13,-0.16 -1.61,-0.68 -0.49,-0.34 -7.38,-7.42 -0.14,-0.21 -0.1,-0.53 -0.13,-0.21 -1.23,-1.15 -1.26,-1.86 -0.24,-0.69 0,-0.62 0.06,-0.29 0.1,-0.27 0.13,-0.25 0.14,-0.2 0.15,-0.14 0.17,-0.07 0.7,-0.05 0.21,-0.07 0.14,-0.11 0.08,-0.15 0.02,-0.18 -0.05,-0.2 -0.12,-0.24 -0.26,-0.3 -0.39,-0.3 -1.7,-0.69 -0.3,-0.22 -0.25,-0.45 -0.04,-0.32 0.11,-0.27 0.21,-0.21 1.85,-1.03 0.27,-0.19 0.2,-0.21 0.18,-0.23 0.12,-0.27 0.05,-0.3 -0.01,-0.33 -0.08,-0.37 -0.11,-0.33 -0.13,-0.27 -0.09,-0.14 -0.19,-0.27 -0.28,-0.31 -0.05,-0.08 -0.03,-0.06 0,-0.13 0.04,-0.15 0.08,-0.18 0.19,-0.33 0.06,-0.15 0.06,-0.2 0.01,-0.19 -0.11,-0.14 -0.24,-0.05 -0.55,0.08 -0.3,0.17 -0.21,0.16 -0.79,0.97 -0.58,0.96 -0.43,0.58 -0.24,0.24 -0.25,0.17 -0.32,0.13 -0.32,0.08 -1.51,-0.01 -0.32,0.1 -0.29,0.01 -0.22,-0.11 -0.42,-0.4 -0.28,-0.2 -0.68,-0.21 -0.15,-0.12 -0.05,-0.25 0.03,-0.43 -0.02,-0.19 -0.06,-0.2 -0.1,-0.17 -0.18,-0.13 -0.21,-0.04 -0.49,0.01 -1.51,0.2 z\"\n        />\n      <path\n        id=\"AF-KAN\"\n        title=\"Kandahar\"\n        \n        d=\"m 252.11715,352.75119 -3.57,-0.68 -0.41,0.03 -0.31,0.07 -0.11,0.05 -0.14,0.11 -0.33,0.31 -1.72,2.18 -0.49,0.83 -0.23,0.67 -0.14,0.91 -0.18,0.68 -0.44,0.82 -0.37,0.47 -0.35,0.32 -0.21,0.12 -0.69,0.26 -0.84,0.42 -0.15,0.13 -0.11,0.13 -0.07,0.35 0,0.56 0.11,1.24 0.3,1.2 -0.01,0.24 -0.27,0.23 -0.46,0.26 -0.18,0.14 -0.09,0.09 -0.07,0.11 -0.08,0.23 -0.25,1 -0.16,0.31 -0.16,0.24 -0.11,0.14 0.01,0.29 0.03,0.24 0.76,1.5 -0.28,0.27 -0.42,0.12 -0.68,0.04 -0.64,-0.11 -0.71,-0.27 -1.84,-1.04 -0.75,-0.26 -2.59,0.13 -0.56,-0.08 -0.42,-0.23 -0.7,-0.87 -0.47,-0.13 -0.78,2.96 -0.02,0.26 -0.11,0.21 -0.19,0.16 -0.85,0.45 -1.39,1.29 -0.19,0.81 -0.04,0.36 -0.09,0.43 -0.39,0.95 -0.06,0.32 -0.06,0.63 -0.1,0.25 -0.2,0.22 -0.4,0.22 -0.16,0.13 -0.11,0.17 0.11,0.19 0.2,0.16 0.2,0.21 0,0.32 -0.13,0.46 -0.36,0.83 -0.71,2.6 -0.26,0.48 -0.33,0.22 -0.75,0.11 -0.97,-0.01 -0.47,-0.1 -0.4,-0.18 -0.79,-0.54 -0.39,-0.16 -0.74,-0.08 -0.44,0.11 -0.42,0.24 -0.81,0.63 -0.9,0.48 -0.28,0.09 -0.25,0.14 -0.25,0.25 -0.26,0.35 -0.35,0.64 -0.39,1.04 -0.04,1.93 -0.43,2.07 -0.26,0.52 -0.76,0.83 -2.14,2.09 -0.34,0.43 -0.84,0.71 -0.15,0.16 -0.12,0.27 -0.16,0.45 -0.92,4.8 -0.03,0.62 0.13,0.4 0.46,0.45 0.35,0.28 0.31,0.18 0.11,0.16 0.02,0.1 -0.4,0.74 -0.76,5.01 -0.09,0.31 -0.76,1.79 -1.47,5.52 -0.65,3.21 -0.69,8.84 -0.16,1.96 -2.17,28.09 -0.66,18.2 -1.43,10.8 -6.34,36.22 -0.02,0.3 0,0.01 8.94,0.08 6.62,0.05 8.06,1.47 2.42,0.08 7.03,-2.06 2.82,-0.83 2.82,-0.82 2.82,-0.83 2.81,-0.82 2.81,-0.82 2.82,-0.83 2.81,-0.83 2.82,-0.82 2.82,-0.83 2.81,-0.82 2.82,-0.83 2.82,-0.82 2.81,-0.83 2.82,-0.82 2.82,-0.83 2.81,-0.83 4.13,-1.21 3.86,-2.79 1.29,-1.71 1,-1.72 0.29,-0.17 0.41,-0.14 0.18,-0.26 -0.39,-0.53 -1.5,-1.16 -1.99,-2.03 -1.71,-1.2 -0.29,-0.75 0.12,-0.89 0.71,-2.12 3.13,-6.4 0.21,-1.07 -0.11,-3.4 0.91,-7.43 -0.11,-1.15 -0.28,-1.15 -0.37,-0.72 -1.17,-1.52 -0.83,-2.24 0.15,-2.45 4.77,-18.15 0.44,-0.78 0.82,-0.45 6.56,-1.34 1.09,-0.49 4.56,-4.7 0.93,-1.3 1.64,-6.39 1.18,-0.82 1.85,-0.26 1.25,-0.96 1.13,-1.3 1.45,-1.27 3.27,-1.61 1.82,-0.53 2.85,-0.06 0.71,0.38 0.35,0.79 0.1,1.26 -0.13,0.46 -0.27,0.35 -0.19,0.35 0.07,0.45 0.37,0.3 0.6,0.2 1.11,0.2 0.98,0.02 1.86,-0.47 0.97,-0.05 0.97,0.3 1.77,0.98 0.99,0.36 0.81,0.09 2.51,-0.12 3.11,0.28 0.89,-0.16 3.36,-1.44 2.88,-0.39 1.79,-0.78 2.59,-0.49 0.9,-0.33 3.09,-2 0.64,-0.58 0.67,-0.49 0.78,-0.23 1.96,0.07 0.75,-0.34 0.5,-1.07 -0.23,-2.38 -1.75,-0.57 -4.03,0.54 -0.96,-0.28 -0.97,-0.6 -0.7,-0.85 -0.28,-2.22 -0.64,-0.99 -0.71,-0.86 -0.35,-0.85 0.62,-1 1.53,-0.04 3.15,0.7 1.5,-0.15 1.47,-0.6 1.05,-0.74 -0.02,-0.01 -2.45,-1.23 -0.39,-0.09 -1.8,-0.18 -1.13,0.02 -2.6,0.98 -0.73,0.18 -0.8,0.09 -3.29,-0.15 -0.29,0.02 -1.53,0.35 -0.27,0.02 -0.26,-0.01 -0.28,-0.04 -0.25,-0.07 -0.26,-0.12 -0.08,-0.15 -0.01,-0.26 0.13,-0.42 0.4,-0.8 0.07,-0.23 -0.19,-0.87 -1.03,-1.9 -2.16,-1.24 -0.79,-0.21 -0.2,0.12 -0.14,0.22 -0.26,0.68 -0.17,0.19 -0.12,0.08 -0.11,0 -0.1,-0.03 -0.45,-0.17 -0.52,-0.26 -0.33,-0.29 -0.34,-0.42 -0.67,-1.46 -0.07,-0.08 -0.16,-0.11 -0.36,-0.16 -0.31,-0.08 -0.7,-0.07 -0.13,-0.07 -0.11,-0.2 -0.56,-1.72 -1.33,-2.19 -2.07,0.78 -1.01,0.59 -3.41,1.01 -1.07,0.13 -2.22,-0.49 -1.3,-0.3 -0.28,-0.01 -0.4,-0.13 -0.45,-0.22 -0.77,-0.57 -1.09,-0.59 -0.87,-0.72 -0.4,-0.4 -0.4,-0.33 -0.47,-0.21 -1.93,-0.62 -2.16,-1.12 -0.75,-0.78 -2.28,-1.88 -0.2,-0.22 -0.34,-0.46 -0.18,-0.08 -0.21,0.07 -0.57,0.61 -6.17,5.14 -0.52,0.23 -2.16,0.47 -0.72,-0.02 -0.51,-0.07 -0.21,-0.13 -0.2,-0.08 -0.23,0 -0.99,0.2 -0.9,0.38 -0.95,0.57 -0.62,0.49 -1.16,1.23 -1.94,1.37 -0.31,-0.01 -0.11,-0.06 -3.91,-4.12 -1.57,-1.03 -0.18,-0.17 -0.22,-0.27 -0.26,-0.56 -0.14,-0.44 0,-1.41 1.61,-1.54 0.66,-0.45 1.18,-0.64 1.79,-0.53 0.25,-0.14 0.2,-0.14 0.45,-0.54 1.01,-1.45 0.59,-1.44 0.11,-0.39 -0.03,-1.45 -0.34,-1.23 -0.42,-0.35 -0.54,-0.36 -0.21,-0.17 -0.08,-0.28 0.05,-0.36 0.62,-1.49 0.67,-1.19 0.11,-0.31 0.03,-0.29 -0.22,-1.27 -0.07,-1.27 -0.03,-0.15 -0.05,-0.11 -0.11,-0.4 -0.06,-0.73 -0.06,-0.29 -0.23,-0.59 -0.09,-0.47 0,-0.46 0.16,-0.65 0.29,-0.56 0.25,-0.4 1.2,-1.24 1.13,-0.9 0.78,-0.96 0.56,-1 0.27,-0.56 0.65,-1.85 0.36,-0.4 0.52,-0.4 1.74,-0.89 0.45,-0.29 0.2,-0.18 0.64,-1.11 0.48,-0.94 0.11,-0.29 0.55,-2.62 0.03,-0.35 0.01,-0.33 -0.07,-0.97 0.03,-0.28 0.19,-0.55 0.02,-0.32 -0.11,-0.32 -0.24,-0.28 -0.35,-0.19 -0.48,-0.15 -0.16,-0.09 -0.33,-0.31 -0.56,-0.37 -0.15,-0.14 -0.12,-0.15 -0.18,-0.38 -0.14,-0.2 -0.2,-0.15 -0.35,-0.05 -0.52,0.09 -0.2,0.01 -0.18,-0.05 -0.17,-0.07 -0.16,-0.05 -0.2,0.06 -0.21,0.18 -0.33,0.45 -0.2,0.38 -0.56,1.36 -1.02,1.57 -0.28,0.24 -0.35,0.16 -0.64,0.01 -0.4,-0.04 -1.55,-0.62 -1.16,0.76 -0.26,0.25 -0.52,0.23 -3.19,0.51 -0.53,0.15 -0.54,-0.13 -0.22,-0.2 -0.12,-0.2 -0.09,-0.14 -0.17,0 -0.23,0.13 -0.27,0.37 -0.1,0.29 -0.02,0.35 0.02,0.59 -0.02,0.17 -0.03,0.18 -0.11,0.28 -0.29,0.24 -1.58,0.27 -3,-0.35 -0.77,0.08 -0.46,-0.05 -0.43,-0.16 -0.37,-0.38 -0.51,-0.72 -0.08,-0.2 -0.11,-0.41 -0.08,-0.18 -0.63,-0.24 -2.26,-0.19 -2.54,-0.32 -0.82,-0.24 -0.37,-0.05 -1.11,0.12 -0.25,0 -1.57,-0.51 -0.43,-0.09 -0.35,-0.01 -0.51,0.13 -0.46,0.21 -0.23,0.14 -0.18,0.18 -0.17,0.2 -0.6,1.02 -0.18,0.24 -0.2,0.2 -0.23,0.15 -0.24,0.11 -0.79,0.2 -0.21,0 -0.15,-0.02 -0.05,-0.21 -0.04,-0.39 0.11,-1.6 -0.29,-3.29 0.1,-0.75 0.16,-0.5 0.13,-0.24 0.03,-0.18 -0.09,-0.21 -0.14,-0.04 -0.14,-0.01 -0.81,0.29 -0.62,0.04 z\"\n        />\n      <path\n        id=\"AF-DAY\"\n        title=\"Daykundi\"\n        \n        d=\"m 283.39715,247.33119 -0.27,-0.04 -0.26,-0.13 -0.63,-0.6 -0.19,-0.14 -0.41,-0.22 -0.53,-0.2 -2.55,-0.46 -0.62,0.03 -0.4,0.18 -0.32,0.69 -0.17,0.26 -0.99,1.14 -0.36,0.28 -0.51,0.26 -0.5,0.05 -1.28,-0.17 -1.8,0.34 -0.32,-0.03 -0.97,-0.18 -1.81,-0.06 -0.35,0.06 -0.1,0.1 -0.66,0.82 -0.4,0.35 -0.54,0.35 -0.87,0.43 -0.58,0.15 -0.53,0.02 -3.08,-0.62 -6.45,-0.42 -3.66,-0.67 -1.83,-0.14 -4.5,0.17 -0.5,0.25 -0.5,1.89 -0.05,0.89 0.07,0.51 -0.12,0.47 -0.68,1.73 -0.22,0.85 -0.25,1.39 -0.04,0.54 0.04,0.37 0.1,0.11 0.13,0.13 0.14,0.16 0.11,0.3 -0.02,0.28 -0.6,1.16 -0.08,0.41 -0.01,0.46 0.03,0.64 -0.27,1.16 -0.28,0.86 -0.14,1.52 0.06,0.23 0.18,0.15 0.12,0.11 0.13,0.17 0.12,0.2 0.12,0.27 0.26,0.78 0.11,0.2 0.14,0.13 0.15,0.07 0.21,0.03 0.63,0.04 0.18,0.23 0.12,0.21 0.32,1.75 -0.27,1.38 -0.4,0.63 -0.58,0.33 -0.74,1.14 -0.3,1.08 -0.26,0.52 -0.19,0.28 -0.17,0.04 -0.88,0.1 -0.21,0.06 -0.15,0.07 -0.14,0.08 -0.25,0.2 -1.17,0.65 -0.22,0.23 -0.15,0.28 -0.06,0.36 -0.01,0.21 0.04,0.17 0.04,0.15 0.1,0.26 0.06,0.21 0.1,0.79 0.06,0.16 0.09,0.15 0.15,0.16 1.3,0.8 0.16,0.17 0.1,0.21 0.07,0.24 0.05,0.23 0.06,0.16 0.09,0.12 0.18,0.19 0.12,0.08 0.12,0.06 0.3,0.07 0.31,0.14 0.15,0.1 0.11,0.11 0.2,0.35 0.31,0.34 0.3,0.58 0.3,1.74 0.42,3.23 0.43,0.94 1.02,0.47 0.78,0.68 0.64,0.73 0.3,0.55 0.18,0.63 -0.03,0.28 -0.12,0.24 -1.22,0.93 -0.75,0.43 -0.68,0.31 -0.26,0.15 -1.61,1.43 -1.44,1.76 -1.6,1.33 -0.13,0.15 -0.1,0.15 -0.12,0.13 -0.14,0.11 -0.15,0.06 -0.16,0.04 -0.64,0.07 -0.18,0.05 -0.14,0.08 -0.11,0.11 -0.69,1.05 -0.12,0.28 0,0.19 0.1,0.11 0.22,0.17 0.05,0.17 0.02,0.13 -0.05,0.84 -1.92,1.03 -0.92,0.32 -1.34,0.27 -1.07,0.43 -0.76,0.5 -1.32,1.04 1,1.13 0.9,0.49 1.64,0.17 0.26,0.12 0.1,0.24 -0.04,0.28 -0.11,0.31 -0.11,0.26 -0.05,0.28 0.16,0.52 1.11,2.02 0.48,0.64 0.49,0.49 0.64,0.53 0.27,0.38 0.18,0.43 0.02,0.99 -0.06,0.47 -0.09,0.38 -0.12,0.32 -0.04,0.25 -0.01,0.63 -0.05,0.39 -0.19,0.67 -0.03,0.27 0.01,0.56 -0.1,0.51 -0.04,0.35 0.01,0.43 0.04,0.34 -0.01,0.84 1.5,0.32 1.43,0.95 0.41,0.19 0.28,0 0.12,-0.27 0.09,-0.36 0.17,-0.36 0.28,-0.26 0.63,-0.18 0.56,0.14 0.52,0.26 0.67,0.45 0.26,0.01 0.1,-0.19 -0.05,-0.49 -0.48,-1.73 -0.44,-1.19 -0.07,-0.29 0.12,-0.26 0.41,-0.09 3.95,0.19 0.52,0.1 0.47,0.15 0.45,0.21 2.85,1.88 0.34,0.06 0.33,-0.06 1.03,-0.41 0.49,-0.09 0.54,-0.04 0.44,0.04 1.64,0.37 0.35,-0.03 0.29,-0.13 0.2,-0.24 0.58,-1.02 0.47,-0.44 0.44,-0.11 0.3,0.02 0.45,0.41 1.84,1.91 1.25,0.7 6.07,1.38 1.46,0.36 0.77,-0.08 0.23,-0.2 0.23,-0.33 0.19,-0.44 0.05,-0.39 -0.01,-0.42 -0.07,-0.42 -0.02,-0.45 0.06,-0.57 0.62,-2.67 0.32,-0.88 0.36,-0.45 0.33,-0.31 1.36,-0.74 0.93,-0.29 1.45,-0.69 5.87,-4.42 0.96,-0.5 0.61,-0.2 0.41,-0.06 0.21,0.06 1.07,0.6 0.42,0.18 0.58,-0.04 0.73,-0.19 2.32,-0.93 0.68,-0.19 0.4,-0.01 0.31,0.04 0.2,0.08 0.17,0.14 0.64,1.02 0.13,0.31 0.08,0.33 0.04,0.32 0.07,0.29 0.21,0.42 0.49,0.79 0.58,0.72 1.36,0.34 3.45,-1.89 1.29,-0.45 1.01,-0.04 0.81,-0.17 0.53,-0.33 0.55,-0.53 0.98,-1.15 0.31,-0.18 0.17,0 0.15,0.22 0.18,1.15 0.26,0.97 0.19,0.24 0.33,0.16 0.8,0.06 0.6,-0.09 1.62,-0.53 0.35,-2.68 0.37,-0.9 0.14,-0.24 0.09,-0.25 0.05,-0.32 0.11,-2.43 0.1,-0.65 0.12,-0.5 0.18,-0.48 -0.06,-1.58 -1.43,-4.23 -0.01,-1.08 0.05,-0.34 0.68,-1.71 0.5,-0.57 0.21,-0.19 0.53,-0.32 4.31,-1.76 2.73,-0.58 1.2,0.05 0.27,-0.03 4.61,-1.38 0.98,-0.52 1.03,-0.74 0.76,-1.02 0.36,-0.73 0.55,-0.82 0.44,-0.54 3.06,-2.66 1.03,-0.95 1.66,-2.39 0.33,-0.36 0.36,-0.25 1.02,-0.56 0.17,-0.3 0.02,-0.4 -0.66,-2.12 -0.34,-0.88 -0.25,-0.39 -0.08,-0.06 -0.7,-0.35 -0.54,-0.35 -0.12,-0.24 0.03,-0.29 0.3,-0.44 0.52,-0.45 0.15,-0.19 0.14,-0.24 0.49,-1.58 0.31,-0.6 -0.02,-0.34 -0.09,-0.37 -0.89,-0.73 -0.7,0.86 -0.43,0.36 -0.44,0.29 -4.03,0.64 -1.62,0.46 -0.26,0.21 -0.2,0.22 -0.17,0.23 -0.37,0.73 -0.16,0.19 -0.35,0.28 -0.13,0.15 -0.12,0.09 -0.15,-0.02 -0.74,-0.87 -0.2,-0.17 -0.1,-0.05 -0.27,-0.03 -0.45,0.04 -0.79,0.22 -0.49,0.19 -2.75,1.69 -0.29,0.06 -0.36,-0.01 -1.83,-0.78 -0.25,-0.17 -0.08,-0.1 -0.12,-0.18 -0.12,-0.24 -0.16,-0.24 -0.24,-0.2 -0.37,-0.18 -8.61,-1.75 -0.82,0 -1.46,0.27 -0.64,0.27 -0.45,0.29 -0.2,0.2 -0.23,0.13 -0.59,0.15 -1.27,0.55 -1.06,-0.14 -2.78,-1.5 -2.78,-2.7 -0.5,-0.58 -0.18,-0.29 -0.13,-0.57 -0.02,-0.82 0.15,-1.74 0.33,-1.03 1.18,-2.54 0.11,-0.6 0.02,-0.38 -0.72,-1.88 -0.22,-2.17 0.08,-0.3 0.17,-0.31 0.3,-0.32 0.65,-0.56 0.26,-0.14 0.51,-0.18 0.22,-0.11 0.15,-0.17 0.09,-0.24 -0.02,-0.35 -0.1,-0.32 -0.29,-0.35 -0.36,-0.26 -0.61,-0.3 -1.54,-0.58 -1.73,-0.08 -0.35,-0.63 -0.48,-0.61 0.02,-0.28 0.04,-0.21 0.89,-1.22 -1.1,-0.43 -6.08,0.96 -0.44,0.18 -0.26,0.04 z\"\n        />\n      <path\n        id=\"AF-URU\"\n        title=\"Uruzgan\"\n        \n        d=\"m 294.63715,313.85119 -1.36,-0.34 -0.58,-0.72 -0.49,-0.79 -0.21,-0.42 -0.07,-0.29 -0.04,-0.32 -0.08,-0.33 -0.13,-0.31 -0.64,-1.02 -0.17,-0.14 -0.2,-0.08 -0.31,-0.04 -0.4,0.01 -0.68,0.19 -2.32,0.93 -0.73,0.19 -0.58,0.04 -0.42,-0.18 -1.07,-0.6 -0.21,-0.06 -0.41,0.06 -0.61,0.2 -0.96,0.5 -5.87,4.42 -1.45,0.69 -0.93,0.29 -1.36,0.74 -0.33,0.31 -0.36,0.45 -0.32,0.88 -0.62,2.67 -0.06,0.57 0.02,0.45 0.07,0.42 0.01,0.42 -0.05,0.39 -0.19,0.44 -0.23,0.33 -0.23,0.2 -0.77,0.08 -1.46,-0.36 -6.07,-1.38 -1.25,-0.7 -1.84,-1.91 -0.45,-0.41 -0.3,-0.02 -0.44,0.11 -0.47,0.44 -0.58,1.02 -0.2,0.24 -0.29,0.13 -0.35,0.03 -1.64,-0.37 -0.44,-0.04 -0.54,0.04 -0.49,0.09 -1.03,0.41 -0.33,0.06 -0.34,-0.06 -2.85,-1.88 -0.45,-0.21 -0.47,-0.15 -0.52,-0.1 -3.95,-0.19 -0.41,0.09 -0.12,0.26 0.07,0.29 0.44,1.19 0.48,1.73 0.05,0.49 -0.1,0.19 -0.26,-0.01 -0.67,-0.45 -0.52,-0.26 -0.56,-0.14 -0.63,0.18 -0.28,0.26 -0.17,0.36 -0.09,0.36 -0.12,0.27 -0.28,0 -0.41,-0.19 -1.43,-0.95 -1.5,-0.32 -0.89,-0.13 -0.26,0.13 -0.38,0.28 -1.09,1.07 -0.27,0.34 -0.11,0.3 0.14,0.47 0.04,0.21 -0.02,0.29 -0.04,0.32 -0.18,0.49 -0.86,0.9 -0.57,0.46 -0.52,0.74 -2.88,5.22 -0.05,0.26 0,0.23 0.07,0.2 0.15,0.19 0.19,0.17 0.4,0.24 0.19,0.16 0.12,0.17 0.09,0.25 0.05,0.27 0.03,0.31 -0.05,0.51 -0.55,2.33 -0.05,0.46 0.03,0.32 0.08,0.23 0.15,0.24 0.12,0.51 0.13,0.39 -0.6,3.08 -0.87,1.9 0.07,0.16 0.14,0.19 0.35,0.05 0.37,0.2 0.48,0.34 0.21,0.06 0.21,0.01 0.75,-0.18 0.32,0.07 0.18,0.21 0.21,0.44 0.2,0.26 0.2,0.13 0.22,0.04 0.54,0.23 0.19,0.61 0.56,2.59 0.34,1.09 0.08,0.78 -0.08,0.48 -0.39,0.68 -0.73,0.89 -0.24,0.44 -0.06,0.28 0.05,0.25 0.1,0.2 0.16,0.46 0.13,0.63 0.14,1.26 0,0.71 -0.06,0.55 -0.15,0.78 -0.1,0.33 -0.15,0.32 -0.46,0.74 -0.4,1.31 -0.59,3.2 0.47,0.13 0.7,0.87 0.42,0.23 0.56,0.08 2.59,-0.13 0.75,0.26 1.84,1.04 0.71,0.27 0.64,0.11 0.68,-0.04 0.42,-0.12 0.28,-0.27 -0.76,-1.5 -0.03,-0.24 -0.01,-0.29 0.11,-0.14 0.16,-0.24 0.16,-0.31 0.25,-1 0.08,-0.23 0.07,-0.11 0.09,-0.09 0.18,-0.14 0.46,-0.26 0.27,-0.23 0.01,-0.24 -0.3,-1.2 -0.11,-1.24 0,-0.56 0.07,-0.35 0.11,-0.13 0.15,-0.13 0.84,-0.42 0.69,-0.26 0.21,-0.12 0.35,-0.32 0.37,-0.47 0.44,-0.82 0.18,-0.68 0.14,-0.91 0.23,-0.67 0.49,-0.83 1.72,-2.18 0.33,-0.31 0.14,-0.11 0.11,-0.05 0.31,-0.07 0.41,-0.03 3.57,0.68 0.62,-0.04 0.81,-0.29 0.14,0.01 0.14,0.04 0.09,0.21 -0.03,0.18 -0.13,0.24 -0.16,0.5 -0.1,0.75 0.29,3.29 -0.11,1.6 0.04,0.39 0.05,0.21 0.15,0.02 0.21,0 0.79,-0.2 0.24,-0.11 0.23,-0.15 0.2,-0.2 0.18,-0.24 0.6,-1.02 0.17,-0.2 0.18,-0.18 0.23,-0.14 0.46,-0.21 0.51,-0.13 0.35,0.01 0.43,0.09 1.57,0.51 0.25,0 1.11,-0.12 0.37,0.05 0.82,0.24 2.54,0.32 2.26,0.19 0.63,0.24 0.08,0.18 0.11,0.41 0.08,0.2 0.51,0.72 0.37,0.38 0.43,0.16 0.46,0.05 0.77,-0.08 3,0.35 1.58,-0.27 0.29,-0.24 0.11,-0.28 0.03,-0.18 0.02,-0.17 -0.02,-0.59 0.02,-0.35 0.1,-0.29 0.27,-0.37 0.23,-0.13 0.17,0 0.09,0.14 0.12,0.2 0.22,0.2 0.54,0.13 0.53,-0.15 3.19,-0.51 0.52,-0.23 0.26,-0.25 1.16,-0.76 -0.45,-0.68 -0.17,-0.34 -0.13,-0.33 -0.02,-0.25 0.13,-0.33 0.58,-1.18 0.06,-0.57 0.01,-0.5 -0.02,-0.4 0.04,-0.36 0.43,-0.63 2.38,-2.68 0.62,-0.84 0.58,-1.01 0.4,-0.38 2.31,-1.15 2.38,-0.35 0.78,-0.69 1.46,-2.77 2.14,-0.23 0.76,0.08 0.15,0.14 0.06,0.18 -0.01,0.31 -0.11,0.51 -1.09,3.35 -0.08,0.38 0.02,0.25 0.06,0.19 0.06,0.09 0.1,0.1 0.2,0.12 0.43,-0.01 0.63,-0.16 1.14,-0.55 0.54,-0.37 0.33,-0.45 0,-0.33 -0.12,-0.67 0.02,-0.28 0.08,-0.27 0.15,-0.26 0.78,-1.06 0.75,-0.76 0.83,-0.66 3.17,-2.02 0.32,-0.32 0.55,-0.29 0.68,-0.17 1.49,-0.11 1.19,0.06 0.29,-0.08 0.32,-0.18 0.86,-0.67 0.57,-0.06 0.43,0.17 0.52,0.33 0.27,0.11 0.41,0.09 0.38,0.04 0.33,-0.04 0.21,-0.1 0.37,-0.25 0.24,-0.08 0.26,-0.15 0.2,-0.24 0.33,-0.6 0.21,-0.32 0.16,-0.59 0.07,-0.42 0.2,-3.41 -0.58,-0.23 -2.34,-0.54 -0.59,-0.28 -0.44,-0.42 -0.4,-0.59 -0.33,-0.91 -0.22,-1.03 -0.22,-2.07 0.02,-0.94 0.08,-0.66 0.1,-0.31 0.02,-0.45 -0.08,-1.62 0.15,-0.82 0.05,-0.17 0.09,-0.68 0.04,-0.19 0.11,-0.21 0.2,-0.2 1.69,-1.23 0.28,-0.32 0.36,-0.64 0.19,-0.26 0.27,-0.22 0.82,-0.43 0.16,-0.17 0.12,-0.39 0.02,-0.56 -0.11,-0.86 -0.25,-0.63 -0.29,-0.42 -0.82,-0.21 -4.95,0.25 -1.26,-0.23 -0.73,-1.85 -1.62,0.53 -0.6,0.09 -0.8,-0.06 -0.33,-0.16 -0.19,-0.24 -0.26,-0.97 -0.18,-1.15 -0.15,-0.22 -0.17,0 -0.31,0.18 -0.98,1.15 -0.55,0.53 -0.53,0.33 -0.81,0.17 -1.01,0.04 -1.29,0.45 -3.45,1.89 z\"\n        />\n      <path\n        id=\"AF-NIM\"\n        title=\"Nimroz\"\n        \n        d=\"m 146.70715,373.68119 -9.07,-2.23 -4.93,-0.26 -2.3,-0.61 -0.28,0 -0.34,0.03 -0.42,0.18 -0.45,0.3 -1.05,1.15 -0.42,0.65 -0.33,0.69 -0.47,1.31 -0.12,0.66 -0.13,4.25 -0.08,0.63 -0.2,0.68 -0.3,0.6 -0.47,0.7 -0.68,0.79 -0.89,0.84 -1.76,1.27 -0.78,0.35 -2.64,0.72 -2.7,0 -0.43,0.09 -4.43,1.7 -0.35,0.2 -0.99,0.71 -0.67,-0.31 -0.49,-0.31 -3.62,-4.09 -2.13,-2.43 -0.34,-0.23 -0.47,-0.24 -0.59,-0.2 -1.579997,-0.3 -0.97,-0.04 -0.64,0.04 -5.42,1.62 -20.14,1.28 -0.22,0.33 -0.06,0.14 -0.07,0.2 0,0.26 0.05,0.31 0.18,0.32 0.11,0.85 0.3,1.42 -0.03,0.47 -0.21,0.45 -0.66,0.62 -3.04,1.59 -0.43,0.4 -0.33,0.52 -0.21,0.66 -0.06,0.38 0.03,0.29 0.36,1.27 0.19,0.93 0,0.32 -0.02,0.28 -0.07,0.35 -0.35,0.61 -0.62,0.81 -1.48,1.62 -0.77,0.65 -1.18,1.34 -2.22,4.22 -1.57,2.07 -0.42,0.23 -0.59,0.26 -1.33,-0.12 -0.28,0.02 -0.24,0.08 -0.18,0.28 -0.1,0.43 0,0.77 0.17,0.82 0.09,0.16 0.29,0.44 0.14,0.29 0.09,0.37 -0.08,0.61 -0.26,0.49 -0.72,0.7 -0.45,0.21 -0.33,0.04 -0.13,-0.09 -0.11,-0.14 -0.08,-0.15 -0.05,-0.07 -0.07,-0.07 -0.11,0.01 -0.11,0.1 -0.19,0.25 -0.09,-0.04 -0.15,-0.41 -0.12,-0.2 -0.13,-0.12 -0.1,-0.07 -0.11,-0.03 -0.03,0.23 0.06,0.45 0.34,1.09 0.33,0.46 0.27,0.29 0.12,0.06 0.06,0.06 0.03,0.1 0,0.19 -0.07,0.25 -0.21,0.48 -0.05,0.33 0.12,0.41 0.12,0.33 -0.03,0.41 -0.38,0.54 -0.44,1.16 -0.19,0.46 -0.01,0.02 2.67,0.39 1.24,0.49 0.96,0.76 1.73,2.21 0.34,1.05 -0.34,2.44 0.02,1.13 0.47,1.16 1.3,2.15 0.41,1.28 0.08,1.66 0.12,0.59 0.85,1.8 0.59,1.85 0.24,1.12 0,1.11 -0.34,1.2 -0.93,1.83 0.21,0.66 0.18,3.87 -0.29,0.16 -0.12,0.4 -0.07,1.02 0.07,0.06 0.12,0.3 -0.83,0.88 -4,4.85 -6.13,7.42 -5.56,6.72 0,0.01 -3.94,4.74 -5.25,6.29 -2.74,3.29 -4.01,4.8 -7.5,8.96 -0.01,0.01 -3.69,4.33 -2.8,3.28 1.53,0.71 3.47,1.12 4.03,1.3 4.05,1.31 4.03,1.31 4.04,1.3 4.04,1.31 4.03,1.3 4.05,1.31 4.03,1.3 4.04,1.3 4.04,1.31 4.04,1.3 4.04,1.3 4.03,1.31 4.04,1.3 4.04,1.3 4.03,1.31 4.61,1.48 5,0.95 6.069997,-0.57 0,-0.02 5.74,-27.27 0.07,-2.49 -0.26,-2.58 0.06,-0.49 0.2,-0.5 1.26,-1.75 0.33,-0.66 0.17,-0.84 0.35,-4.01 -0.09,-2.8 0.17,-0.55 0.28,-0.42 0.3,-0.24 0.37,-0.18 0.42,-0.11 0.53,-0.08 0.41,-0.1 1.65,-0.95 1.97,-1.96 1.46,-1.83 2.05,-3.2 1.2,-2.29 0.65,-1.73 0.54,-1.1 0.61,-0.8 2.56,-1.89 0.75,-0.78 0.25,-0.61 0.04,-0.55 -0.13,-0.44 -0.2,-0.45 -0.26,-0.39 -0.66,-0.81 -0.3,-0.51 -0.16,-0.38 -0.06,-0.94 1.15,-1.73 0.02,-0.28 -0.04,-0.32 -0.69,-0.42 -0.36,-0.31 -0.33,-0.63 0.03,-0.42 0.29,-0.48 0.44,-0.45 1.64,-2.17 0.5,-0.51 0.48,-0.36 0.9,-0.49 0.41,-0.35 0.15,-0.38 0.01,-0.54 -0.46,-2.71 -0.05,-3.87 -0.15,-2.07 0.62,-0.73 1.89,-0.85 0.63,-0.43 0.88,-0.79 0.39,-0.53 0.48,-1.13 2.02,-3.87 0.38,-0.51 0.48,-0.38 1.05,-0.39 0.87,-1.08 0.12,-0.91 -0.03,-0.52 0.29,-4.99 -0.05,-0.65 -0.15,-0.39 -0.2,-0.41 -0.56,-0.87 -0.7,-2.11 0.12,-0.94 1.1,-1.34 0.54,-1.04 0.08,-5.98 2.58,-18.5 1.06,-3.16 2.01,-1.61 0.53,-0.62 0.77,-1.11 1.73,-3.62 0.27,-0.81 0.1,-0.63 -0.34,-1.32 -0.02,-0.48 0.13,-0.27 0.73,-0.73 0.71,-0.88 0.18,-0.48 -0.03,-0.37 -0.69,-0.68 -0.12,-0.24 -0.04,-0.27 0,-0.28 -0.05,-0.3 -0.13,-0.3 -0.38,-0.59 -0.07,-0.22 -0.03,-0.29 0.01,-0.5 -0.2,-0.84 -0.19,-2.01 -1.15,0.07 z\"\n        />\n      <path\n        id=\"AF-HEL\"\n        title=\"Helmand\"\n        \n        d=\"m 174.82715,532.50119 1.32,-0.54 1.77,-3.46 1.1,-1.44 1.71,-0.87 13.09,-3.93 1.06,0.01 0,-0.01 0.02,-0.3 6.34,-36.22 1.43,-10.8 0.66,-18.2 2.17,-28.09 0.16,-1.96 0.69,-8.84 0.65,-3.21 1.47,-5.52 0.76,-1.79 0.09,-0.31 0.76,-5.01 0.4,-0.74 -0.02,-0.1 -0.11,-0.16 -0.31,-0.18 -0.35,-0.28 -0.46,-0.45 -0.13,-0.4 0.03,-0.62 0.92,-4.8 0.16,-0.45 0.12,-0.27 0.15,-0.16 0.84,-0.71 0.34,-0.43 2.14,-2.09 0.76,-0.83 0.26,-0.52 0.43,-2.07 0.04,-1.93 0.39,-1.04 0.35,-0.64 0.26,-0.35 0.25,-0.25 0.25,-0.14 0.28,-0.09 0.9,-0.48 0.81,-0.63 0.42,-0.24 0.44,-0.11 0.74,0.08 0.39,0.16 0.79,0.54 0.4,0.18 0.47,0.1 0.97,0.01 0.75,-0.11 0.33,-0.22 0.26,-0.48 0.71,-2.6 0.36,-0.83 0.13,-0.46 0,-0.32 -0.2,-0.21 -0.2,-0.16 -0.11,-0.19 0.11,-0.17 0.16,-0.13 0.4,-0.22 0.2,-0.22 0.1,-0.25 0.06,-0.63 0.06,-0.32 0.39,-0.95 0.09,-0.43 0.04,-0.36 0.19,-0.81 1.39,-1.29 0.85,-0.45 0.19,-0.16 0.11,-0.21 0.02,-0.26 0.78,-2.96 0.59,-3.2 0.4,-1.31 0.46,-0.74 0.15,-0.32 0.1,-0.33 0.15,-0.78 0.06,-0.55 0,-0.71 -0.14,-1.26 -0.13,-0.63 -0.16,-0.46 -0.1,-0.2 -0.05,-0.25 0.06,-0.28 0.24,-0.44 0.73,-0.89 0.39,-0.68 0.08,-0.48 -0.08,-0.78 -0.34,-1.09 -0.56,-2.59 -0.19,-0.61 -0.54,-0.23 -0.22,-0.04 -0.2,-0.13 -0.2,-0.26 -0.21,-0.44 -0.18,-0.21 -0.32,-0.07 -0.75,0.18 -0.21,-0.01 -0.21,-0.06 -0.48,-0.34 -0.37,-0.2 -0.35,-0.05 -0.14,-0.19 -0.07,-0.16 0.87,-1.9 0.6,-3.08 -0.13,-0.39 -0.12,-0.51 -0.15,-0.24 -0.08,-0.23 -0.03,-0.32 0.05,-0.46 0.55,-2.33 0.05,-0.51 -0.03,-0.31 -0.05,-0.27 -0.09,-0.25 -0.12,-0.17 -0.19,-0.16 -0.4,-0.24 -0.19,-0.17 -0.15,-0.19 -0.07,-0.2 0,-0.23 0.05,-0.26 2.88,-5.22 0.52,-0.74 0.57,-0.46 0.86,-0.9 0.18,-0.49 0.04,-0.32 0.02,-0.29 -0.04,-0.21 -0.14,-0.47 0.11,-0.3 0.27,-0.34 1.09,-1.07 0.38,-0.28 0.26,-0.13 0.89,0.13 0.01,-0.84 -0.04,-0.34 -0.01,-0.43 0.04,-0.35 0.1,-0.51 -0.01,-0.56 0.03,-0.27 0.19,-0.67 0.05,-0.39 0.01,-0.63 0.04,-0.25 0.12,-0.32 0.09,-0.38 0.06,-0.47 -0.02,-0.99 -0.18,-0.43 -0.27,-0.38 -0.64,-0.53 -0.49,-0.49 -0.48,-0.64 -1.11,-2.02 -0.16,-0.52 0.05,-0.28 0.11,-0.26 0.11,-0.31 0.04,-0.28 -0.1,-0.24 -0.26,-0.12 -1.64,-0.17 -0.9,-0.49 -1,-1.13 -1.81,-0.17 -0.53,0.03 -0.59,0.16 -2.09,1.33 -0.81,0.71 -0.55,0.64 -0.24,0.22 -4.22,1.99 -0.13,0.16 -0.11,0.13 -1.33,0.92 -0.48,0.48 -0.36,0.17 -0.43,0.02 -0.88,-0.18 -0.41,-0.21 -0.34,-0.21 -0.29,-0.07 -0.36,0.16 -0.64,0.56 -1.58,1.73 -0.34,0.31 -0.3,0.17 -0.35,0.15 -0.3,0.08 -0.34,0 -0.65,-0.11 -0.26,-0.24 -0.14,-0.19 -0.13,-1.43 -0.04,-0.2 -0.07,-0.2 -0.08,-0.17 -0.61,0.04 -2.17,1.07 0.37,0.59 0.15,0.17 0.12,0.18 0.01,0.17 -0.12,0.21 -1.56,1.4 -0.2,0.38 -0.13,0.49 0,0.79 0.05,0.41 0.07,0.25 0.79,0.43 -4.38,3.36 -0.54,0.49 -0.38,0.6 -0.42,0.52 -0.94,0.96 -0.48,0.59 -0.29,0.59 0.01,0.28 0.09,0.17 0.12,0.08 0.15,0.05 0.09,0.11 0.06,0.18 -0.09,0.32 -0.22,0.41 -0.51,0.76 -0.46,1.02 -0.24,0.43 -0.06,0.23 -0.01,0.12 0.04,0.06 0.15,0.11 1.16,0.48 0.27,0.17 0.19,0.17 0.08,0.2 -0.03,0.24 -0.22,0.29 -0.35,0.27 -6.32,2.49 -1.13,0.2 -0.39,0.16 -0.49,0.34 -0.49,0.44 -0.58,0.88 -0.77,0.82 -0.77,1.17 -0.17,0.34 -0.03,0.22 0.03,0.22 0.06,0.22 -0.23,0.37 -0.49,0.47 -1.99,1.41 -0.81,0.9 -0.46,0.89 -0.15,0.65 0.05,0.24 0.09,0.13 0.17,0.1 0.69,0.23 0.98,0.57 0.2,0.17 0.09,0.21 -0.05,0.29 -0.3,0.65 -0.07,0.3 0.01,0.35 0.05,0.38 -0.43,0.49 -0.84,0.59 -2.26,1.08 -1.61,1.02 -0.49,0.15 -2.49,0.1 -3.49,0.71 -0.51,0.03 -0.33,-0.07 -0.09,-0.17 -0.07,-0.22 -0.08,-0.44 -0.05,-0.08 -0.06,-0.05 -0.09,-0.02 -0.15,0.02 -0.2,0.07 -0.31,0.15 -0.47,0.4 -0.2,0.19 -0.17,0.41 -0.04,1.18 0.09,1.3 0.07,0.27 0.11,0.24 0.48,0.52 0.19,0.39 0.2,0.56 0.16,0.57 0.03,0.52 -0.13,0.44 -0.31,0.3 -0.73,0.34 -0.69,0.42 -0.7,0.61 -0.35,0.47 -0.26,0.43 -0.28,0.3 -0.26,0.12 -0.39,-0.16 -0.43,-0.43 -0.23,0.02 -0.37,0.23 -3.44,2.78 -0.27,0.31 -0.17,0.32 -0.06,0.34 -0.03,0.42 -0.14,0.32 -0.3,0.19 -0.69,0.06 -1.55,-0.4 -0.47,-0.02 -0.4,0.06 -1.11,0.35 -0.3,0.03 -0.34,0.09 -0.47,0.24 -2.68,2.81 -0.57,0.48 -0.87,0.54 -3.79,1.82 -1.15,0.95 0.19,2.01 0.2,0.84 -0.01,0.5 0.03,0.29 0.07,0.22 0.38,0.59 0.13,0.3 0.05,0.3 0,0.28 0.04,0.27 0.12,0.24 0.69,0.68 0.03,0.37 -0.18,0.48 -0.71,0.88 -0.73,0.73 -0.13,0.27 0.02,0.48 0.34,1.32 -0.1,0.63 -0.27,0.81 -1.73,3.62 -0.77,1.11 -0.53,0.62 -2.01,1.61 -1.06,3.16 -2.58,18.5 -0.08,5.98 -0.54,1.04 -1.1,1.34 -0.12,0.94 0.7,2.11 0.56,0.87 0.2,0.41 0.15,0.39 0.05,0.65 -0.29,4.99 0.03,0.52 -0.12,0.91 -0.87,1.08 -1.05,0.39 -0.48,0.38 -0.38,0.51 -2.02,3.87 -0.48,1.13 -0.39,0.53 -0.88,0.79 -0.63,0.43 -1.89,0.85 -0.62,0.73 0.15,2.07 0.05,3.87 0.46,2.71 -0.01,0.54 -0.15,0.38 -0.41,0.35 -0.9,0.49 -0.48,0.36 -0.5,0.51 -1.64,2.17 -0.44,0.45 -0.29,0.48 -0.03,0.42 0.33,0.63 0.36,0.31 0.69,0.42 0.04,0.32 -0.02,0.28 -1.15,1.73 0.06,0.94 0.16,0.38 0.3,0.51 0.66,0.81 0.26,0.39 0.2,0.45 0.13,0.44 -0.04,0.55 -0.25,0.61 -0.75,0.78 -2.56,1.89 -0.61,0.8 -0.54,1.1 -0.65,1.73 -1.2,2.29 -2.05,3.2 -1.46,1.83 -1.97,1.96 -1.65,0.95 -0.41,0.1 -0.53,0.08 -0.42,0.11 -0.37,0.18 -0.3,0.24 -0.28,0.42 -0.17,0.55 0.09,2.8 -0.35,4.01 -0.17,0.84 -0.33,0.66 -1.26,1.75 -0.2,0.5 -0.06,0.49 0.26,2.58 -0.07,2.49 -5.74,27.27 0,0.02 1.39,-0.13 2.38,-0.23 2.38,-0.22 2.37,-0.23 2.38,-0.22 2.38,-0.22 2.38,-0.23 2.38,-0.22 2.38,-0.23 2.37,-0.23 2.38,-0.22 2.38,-0.22 2.38,-0.23 2.37,-0.22 2.38,-0.23 2.38,-0.22 2.39,-0.23 7.4,-0.69 10.64,2.05 8.92,1.73 5.54,2.39 z\"\n        />\n      <path\n        id=\"AF-FRA\"\n        title=\"Farah\"\n        \n        d=\"m 181.77715,318.36119 -0.3,-0.3 -0.37,-0.58 -1.43,-2.83 -0.38,-0.51 -0.68,-0.55 -0.17,-0.19 -0.15,-0.32 -0.17,-0.54 -0.18,-0.27 -0.32,-0.18 -0.53,-0.01 -0.41,-0.14 -0.67,-0.29 -0.16,-0.2 -0.14,-0.3 -0.02,-0.53 0.07,-0.62 0,-0.24 -0.14,-0.67 -0.05,-0.34 0.03,-0.94 -0.15,-0.47 -1.49,-3.51 -2.76,-4.53 -0.8,-0.75 -1.76,-0.95 -1.22,0.13 -2.18,1.02 -0.92,0.69 -0.69,0.63 -0.96,1.2 -0.76,1.19 -0.61,0.53 -0.78,0.3 -3.26,0.34 -2.05,0.47 -1.54,0.62 -0.99,0.6 -1.86,1.53 -1.95,0.78 -2.53,0.5 -9.82,0.4 -3.68,1.16 -0.67,0.09 -1.63,-0.05 -1.6,-0.67 -0.84,0.65 -1.1,0.52 -0.37,0.28 -0.46,0.75 -0.54,1.15 -0.84,2.3 -0.29,1.04 -0.12,0.74 0.02,0.27 -0.13,0.37 -0.12,0.22 -1.3,1.31 -4.24,1.59 -0.17,-0.04 -0.15,-0.07 -0.06,-0.06 -0.07,-0.11 -0.03,-0.14 0,-0.18 0.04,-0.19 0.12,-0.42 0.03,-0.14 -0.06,-0.08 -0.15,-0.01 -1.18,0.21 -4.03,1.43 -0.73,0.16 -0.64,0.07 -1.92,-0.13 -0.26,0.11 -0.29,0.27 -0.32,0.57 -0.12,0.54 -0.18,0.44 -0.26,0.41 -0.54,0.46 -0.73,0.42 -1.97,0.66 -2.5,0.48 -0.41,0.01 -0.37,-0.05 -0.3,-0.08 -1.29,-0.7 -0.219997,-0.08 -0.22,-0.06 -0.29,0.01 -0.4,0.06 -0.63,0.21 -1.99,1.01 -1.42,0.96 -1.25,1.04 -0.78,0.49 -0.31,0.29 -0.32,0.48 -0.29,0.94 -0.06,0.62 0.03,0.47 0.14,0.63 0.02,0.31 -0.03,0.29 -0.11,0.42 -1.13,2.44 -0.18,0.49 -0.04,0.37 0.14,0.89 0,0.15 -0.17,0.13 -0.34,0.06 -0.94,-0.15 -1.1,-0.04 -1.84,0.36 -3.97,0.41 -1.42,0.31 -0.77,0.31 -0.36,0.26 -1.47,1.36 -0.72,0.39 -0.41,0.17 -0.68,0.07 -0.71,-0.07 -0.33,-0.07 -0.29,-0.13 -0.2,-0.29 -0.13,-0.26 -0.17,-0.51 -0.11,-0.21 -0.29,-0.45 -0.08,-0.19 -0.04,-0.24 -0.01,-0.21 0.03,-0.25 0,-0.22 -0.03,-0.22 -0.28,-0.84 -0.12,-0.7 -0.1,-1.54 -0.1,-0.16 -0.16,-0.11 -0.3,-0.05 -0.4,0.02 -0.79,0.35 -2.46,1.81 -1.28,1.47 -0.53,0.57 -0.31,0.23 -0.39,0.2 -0.32,0.06 -0.24,-0.02 -0.22,-0.08 -0.24,-0.15 -0.41,-0.33 -1.8,-1.84 -0.51,-0.91 -0.32,-0.42 -0.36,-0.21 -0.5,-0.11 -2.89,-0.11 -0.52,-0.16 -0.49,-0.21 -0.39,-0.29 -0.3,-0.68 -0.1,-0.94 0.06,-2.05 0.27,-1.89 0.28,-0.88 0.21,-0.33 0.28,-0.26 1.08,-0.66 0.28,-0.24 0.2,-0.25 0.14,-0.28 0.13,-0.37 0.17,-0.32 0.22,-0.28 0.64,-0.51 1.63,-1.66 0.53,-0.75 0.31,-0.36 0.47,-0.43 1.33,-1.02 0.46,-0.6 0.04,-0.64 -2.19,-3.52 -1.9,-2.09 -0.23,-0.47 0.01,-0.52 0.15,-0.26 0.45,-0.41 0.22,-0.25 0.4,-0.61 0.24,-0.25 0.7,-0.52 0.88,-0.91 0.25,-0.39 0.13,-0.38 0.08,-0.46 0.02,-2.48 0.05,-0.5 0.12,-0.38 0.18,-0.27 0.7,-0.68 0.22,-0.31 0.17,-0.43 0.07,-0.48 0.01,-0.6 -0.1,-0.41 -0.47,-0.39 -3.21,-0.74 -3.65,-0.27 -1.48,0.1 -0.88,-0.07 -0.66,-0.74 -2.31,-0.48 -2.22,-0.1 -1.09,0.21 -0.5,0.28 -0.34,0.44 -0.24,0.57 -0.31,0.56 -0.41,0.4 -0.57,0.35 -1.33,0.46 -0.66,0.06 -0.52,-0.05 -1.01,-0.45 -0.56,-0.14 -0.89,0 -6.99,0.75 -3.29,-0.27 -6.42,-1.45 -2.77,-1.21 -1.71,-0.2 -0.97,-0.11 -0.38,0.64 -0.71,0.26 -2.3,0.16 -0.86,0.57 -0.14,0.84 0.17,0.97 0.09,1 -0.27,1.16 -0.51,0.66 -1.59,0.99 -1.4,1.25 -1.84,2.51 -2.3599997,3.21 -2.64,3.57 -2.34,3.18 -0.3,0.81 0.07,4.58 0.71,3.69 1.39,4.74 1.64,5.6 1.69,5.76 1.7799997,6.06 1.97,6.66 2.14,7.97 1.65,6.14 -0.1,4.67 -0.65,4.56 -0.29,0.95 -1.27,1.64 -0.38,0.88 0.17,0.35 0.4,0.24 0.29,0.32 -0.42,1.26 0.1,0.59 0.24,0.56 0.19,0.61 0.09,1.24 -0.36,3.53 0.1,2.77 0.68,5.28 -0.62,4.21 0.11,1.33 0.73,2.76 0.57,5.33 1.62,0.68 4.89,0.72 8.13,1.19 0.08,0.01 8.23,1.21 9.51,1.39 5.59,0.82 0.01,-0.02 0.19,-0.46 0.44,-1.16 0.38,-0.54 0.03,-0.41 -0.12,-0.33 -0.12,-0.41 0.05,-0.33 0.21,-0.48 0.07,-0.25 0,-0.19 -0.03,-0.1 -0.06,-0.06 -0.12,-0.06 -0.27,-0.29 -0.33,-0.46 -0.34,-1.09 -0.06,-0.45 0.03,-0.23 0.11,0.03 0.1,0.07 0.13,0.12 0.12,0.2 0.15,0.41 0.09,0.04 0.19,-0.25 0.11,-0.1 0.11,-0.01 0.07,0.07 0.05,0.07 0.08,0.15 0.11,0.14 0.13,0.09 0.33,-0.04 0.45,-0.21 0.72,-0.7 0.26,-0.49 0.08,-0.61 -0.09,-0.37 -0.14,-0.29 -0.29,-0.44 -0.09,-0.16 -0.17,-0.82 0,-0.77 0.1,-0.43 0.18,-0.28 0.24,-0.08 0.28,-0.02 1.33,0.12 0.59,-0.26 0.42,-0.23 1.57,-2.07 2.22,-4.22 1.18,-1.34 0.77,-0.65 1.48,-1.62 0.62,-0.81 0.35,-0.61 0.07,-0.35 0.02,-0.28 0,-0.32 -0.19,-0.93 -0.36,-1.27 -0.03,-0.29 0.06,-0.38 0.21,-0.66 0.33,-0.52 0.43,-0.4 3.04,-1.59 0.66,-0.62 0.21,-0.45 0.03,-0.47 -0.3,-1.42 -0.11,-0.85 -0.18,-0.32 -0.05,-0.31 0,-0.26 0.07,-0.2 0.06,-0.14 0.22,-0.33 20.14,-1.28 5.42,-1.62 0.64,-0.04 0.97,0.04 1.579997,0.3 0.59,0.2 0.47,0.24 0.34,0.23 2.13,2.43 3.62,4.09 0.49,0.31 0.67,0.31 0.99,-0.71 0.35,-0.2 4.43,-1.7 0.43,-0.09 2.7,0 2.64,-0.72 0.78,-0.35 1.76,-1.27 0.89,-0.84 0.68,-0.79 0.47,-0.7 0.3,-0.6 0.2,-0.68 0.08,-0.63 0.13,-4.25 0.12,-0.66 0.47,-1.31 0.33,-0.69 0.42,-0.65 1.05,-1.15 0.45,-0.3 0.42,-0.18 0.34,-0.03 0.28,0 2.3,0.61 4.93,0.26 9.07,2.23 1.15,-0.07 1.15,-0.95 3.79,-1.82 0.87,-0.54 0.57,-0.48 2.68,-2.81 0.47,-0.24 0.34,-0.09 0.3,-0.03 1.11,-0.35 0.4,-0.06 0.47,0.02 1.55,0.4 0.69,-0.06 0.3,-0.19 0.14,-0.32 0.03,-0.42 0.06,-0.34 0.17,-0.32 0.27,-0.31 3.44,-2.78 0.37,-0.23 0.23,-0.02 0.43,0.43 0.39,0.16 0.26,-0.12 0.28,-0.3 0.26,-0.43 0.35,-0.47 0.7,-0.61 0.69,-0.42 0.73,-0.34 0.31,-0.3 0.13,-0.44 -0.03,-0.52 -0.16,-0.57 -0.2,-0.56 -0.19,-0.39 -0.48,-0.52 -0.11,-0.24 -0.07,-0.27 -0.09,-1.3 0.04,-1.18 0.17,-0.41 0.2,-0.19 0.47,-0.4 0.31,-0.15 0.2,-0.07 0.15,-0.02 0.09,0.02 0.06,0.05 0.05,0.08 0.08,0.44 0.07,0.22 0.09,0.17 0.33,0.07 0.51,-0.03 3.49,-0.71 2.49,-0.1 0.49,-0.15 1.61,-1.02 2.26,-1.08 0.84,-0.59 0.43,-0.49 -0.05,-0.38 -0.01,-0.35 0.07,-0.3 0.3,-0.65 0.05,-0.29 -0.09,-0.21 -0.2,-0.17 -0.98,-0.57 -0.69,-0.23 -0.17,-0.1 -0.09,-0.13 -0.05,-0.24 0.15,-0.65 0.46,-0.89 0.81,-0.9 1.99,-1.41 0.49,-0.47 0.23,-0.37 -0.06,-0.22 -0.03,-0.22 0.03,-0.22 0.17,-0.34 0.77,-1.17 0.77,-0.82 0.58,-0.88 0.49,-0.44 0.49,-0.34 0.39,-0.16 1.13,-0.2 6.32,-2.49 0.35,-0.27 0.22,-0.29 0.03,-0.24 -0.08,-0.2 -0.19,-0.17 -0.27,-0.17 -1.16,-0.48 -0.15,-0.11 -0.04,-0.06 0.01,-0.12 0.06,-0.23 0.24,-0.43 0.46,-1.02 0.51,-0.76 0.22,-0.41 0.09,-0.32 -0.06,-0.18 -0.09,-0.11 -0.15,-0.05 -0.12,-0.08 -0.09,-0.17 -0.01,-0.28 0.29,-0.59 0.48,-0.59 0.94,-0.96 0.42,-0.52 0.38,-0.6 0.54,-0.49 4.38,-3.36 -0.79,-0.43 -0.07,-0.25 -0.05,-0.41 0,-0.79 0.13,-0.49 0.2,-0.38 1.56,-1.4 0.12,-0.21 -0.01,-0.17 -0.12,-0.18 -0.15,-0.17 -0.37,-0.59 -1.51,-1.12 -0.36,-0.74 -0.05,-0.35 -0.06,-0.3 -0.08,-0.24 -0.12,-0.19 -0.33,-0.16 -0.55,-0.13 -1.68,-0.03 -0.34,0.05 -0.14,0.05 -0.16,0.02 -0.3,-0.07 -0.39,-0.16 -0.6,-0.42 -0.44,-0.49 -0.15,-0.23 -0.18,-0.53 -0.07,-0.15 -0.11,-0.11 -0.11,-0.08 -0.16,-0.07 -0.55,-0.07 -0.83,0.19 -0.54,0.57 -1.11,2.92 -0.73,1.29 -0.45,0.56 -0.49,0.46 -0.63,0.48 -0.92,0.57 -0.85,0.4 -1.34,0.3 -0.25,0.11 -0.22,0.16 -0.11,0.14 -0.03,0.13 0.03,0.12 0.12,0.17 1.01,1.06 0.14,0.23 0.09,0.24 -0.02,0.18 -0.08,0.16 -0.15,0.12 -0.21,0.1 -0.29,0.08 -1.32,0.08 -0.42,0.08 -0.17,0.07 -0.21,0.13 -0.53,0.46 -0.14,0.05 -0.24,-0.04 -2.09,-1.15 -1.12,-1.07 -0.27,-0.15 -0.35,0 -0.67,0.26 -0.21,0.04 z\"\n        />\n      <path\n        id=\"AF-SAR\"\n        title=\"Sari Pul\"\n        \n        d=\"m 294.82715,140.68119 -0.77,-0.15 -0.31,-0.18 -2.01,-1.64 -1.48,-0.75 -0.19,-0.25 -0.04,-0.2 0.1,-0.32 0.1,-0.23 0.62,-0.79 1.59,-2.47 0.27,-0.52 0.53,-0.82 0.13,-0.26 0.25,-0.76 1.16,-2.51 0.15,-0.47 0.24,-1.29 0.09,-0.33 0.12,-0.28 0.85,-1.56 0.54,-0.77 0.23,-0.27 1.15,-1.05 0.33,-0.48 0.56,-1.01 0.18,-0.23 0.44,-0.46 0.18,-0.25 0.61,-1.17 0.1,-0.27 0.09,-0.38 0.01,-0.2 -0.02,-0.24 -0.12,-0.34 -0.18,-0.25 -0.21,-0.21 -0.24,-0.17 -1.41,-0.5 -5.24,-1.04 -1.86,0.94 -0.29,0.09 -0.62,0.08 -0.64,-0.04 -1.49,0.1 -3.65,1.04 -5.66,0.94 -4.63,0.27 -1.96,-0.56 -1.31,0.15 -1.15,0.45 -2.06,1.34 -0.26,0.24 -0.16,0.18 -0.47,0.73 -0.5,0.57 -0.47,0.25 -4.42,0.92 -1.36,0.6 -2.92,2.23 -2.48,2.53 -1.19,0.84 -1.08,0.36 -5.31,0.7 -2.57,0.01 -0.79,0.5 -0.2,0.32 -0.23,0.57 -0.04,0.53 0.03,0.54 0.32,1.96 0.01,0.5 -0.87,9.67 1.14,1.08 0.59,0.91 0.21,0.51 0.18,0.64 0.08,0.49 0.01,0.41 -0.54,1.46 -0.05,1.17 0.15,0.47 0.29,0.22 6.23,-0.94 0.43,-0.15 0.32,-0.15 0.4,-0.33 1.24,-1.01 0.77,-0.24 0.32,0.03 0.32,0.08 0.27,0.16 0.28,0.23 0.2,0.26 0.11,0.25 0.01,0.19 -0.04,0.13 -0.33,0.48 -0.06,0.14 0,0.09 0.08,0.5 -0.02,0.12 -0.06,0.13 -0.12,0.17 -0.15,0.16 -0.11,0.23 -0.06,0.27 0.21,0.52 0.25,0.27 0.43,0.24 0.17,0.14 0.1,0.14 0.21,0.88 0.47,1.16 0.11,0.16 0.15,0.16 0.33,0.06 0.08,0.07 0.05,0.18 0.03,0.28 0.48,2.18 0.01,0.49 0.03,0.14 0.03,0.06 0.13,0.01 0.16,0.04 0.24,0.13 0.05,0.13 -0.05,0.16 -0.38,0.48 -0.18,0.34 -0.13,0.38 -0.12,0.65 -0.11,0.32 -0.1,0.2 -0.08,0.06 -0.84,0.29 -1.65,0.26 -1.28,-0.29 -0.86,-0.1 -0.23,0.01 -0.27,0.03 -0.35,0.15 -0.1,0.2 0.02,0.23 0.23,0.43 0.06,0.31 -0.05,0.37 -0.19,0.9 -0.18,0.43 -0.29,0.49 -0.23,0.28 -1.1,1.78 -0.93,1.23 -0.59,0.31 -0.24,0.02 -0.26,-0.02 -1.51,-0.41 -1.52,-0.15 -0.47,0.06 -0.35,0.13 -1.53,1.22 -0.29,0.34 -0.17,0.3 -0.02,0.37 0.03,0.41 -0.02,0.12 -0.06,0.18 -0.25,0.54 -0.14,0.47 -0.01,0.2 0,0.15 0.05,0.22 0.62,2.01 0.09,0.4 0,0.14 -0.01,0.12 -0.1,0.24 -1.5,2.93 -0.62,1.85 -0.22,0.36 -0.26,0.26 -0.68,0.41 -0.16,0.2 -0.09,0.23 -0.17,1.47 -0.09,1.68 0.2,1.12 -0.03,0.55 -0.15,1.12 0.03,0.68 0.16,0.9 0.55,1.86 0.17,0.45 0.21,0.38 1.08,1.32 0.51,0.87 0.47,0.19 2.86,-0.03 1.48,-0.29 0.46,-0.14 0.32,-0.16 0.53,-0.53 0.19,-0.14 0.2,-0.1 1.67,-0.55 0.35,-0.06 0.21,0.01 0.02,0.08 -0.01,0.1 -0.11,0.35 -0.05,0.21 -0.02,0.19 0,0.16 0.03,0.15 0.08,0.14 0.1,0.15 0.15,0.14 0.17,0.11 0.72,0.39 0.14,0.11 0.15,0.13 0.13,0.15 0.11,0.17 0.11,0.17 0.07,0.2 0.12,0.51 0.21,0.29 0.26,0.11 0.52,0.06 0.36,0.14 0.7,0.38 0.23,0.06 0.18,-0.01 0.78,-0.2 0.17,0.02 0.12,0.1 0.07,0.17 0.04,0.21 0,0.2 -0.1,0.65 0,0.26 0.06,0.34 0.09,0.31 0.12,0.32 0.64,1.27 0.26,0.41 0.52,0.59 0.39,0.29 0.35,0.19 5.01,1.73 1.5,0.15 2.43,0.55 0.31,-0.01 0.12,-0.12 0.24,-0.1 0.36,-0.05 0.65,0.05 0.35,0.11 0.29,0.19 0.18,0.25 0.16,0.3 0.13,0.31 0.08,0.28 0.03,0.25 0.01,0.27 -0.06,1.23 -0.04,0.28 -0.07,0.23 -0.08,0.15 -0.13,0.1 -0.54,0.22 -0.18,0.14 -0.01,0.25 0.16,0.31 0.91,0.79 0.16,0.18 -0.02,0.11 -0.08,0.14 -0.37,0.49 -0.13,0.33 0.02,0.37 0.82,0.23 0.4,0.39 0.32,0.53 0.39,0.87 1.38,4.83 0.06,0.37 -0.05,0.2 -0.34,0.79 -0.09,0.4 -0.03,0.51 0.17,0.27 0.15,0.1 0.17,-0.03 0.18,-0.11 0.46,-0.47 1.25,-1.62 0.15,-0.11 0.17,-0.07 0.66,-0.16 0.17,-0.08 0.12,-0.13 0.08,-0.15 0,-0.18 -0.02,-0.18 -0.05,-0.2 -0.03,-0.21 0.04,-0.2 0.09,-0.14 0.15,-0.11 0.21,-0.06 0.89,-0.08 0.15,-0.05 0.11,-0.1 0.09,-0.16 0.06,-0.2 0.08,-0.53 0.07,-0.12 0.15,-0.09 0.65,-0.15 0.21,-0.08 0.17,-0.09 0.09,-0.1 0.04,-0.12 -0.02,-0.12 -0.21,-0.47 -0.06,-0.21 -0.03,-0.22 0.02,-0.23 0.1,-0.24 0.19,-0.22 0.33,-0.2 0.22,-0.02 0.32,0.15 0.37,0.25 0.87,0.41 0.64,0.18 0.87,0.12 0.41,0.01 0.33,-0.03 0.22,-0.08 0.47,-0.27 0.94,-0.4 -0.44,-0.19 -0.33,-0.26 -0.12,-0.13 -0.36,-0.46 -0.15,-0.27 -0.12,-0.34 -0.06,-0.43 -0.02,-0.48 0.12,-1.55 0.09,-0.43 0.11,-0.35 0.14,-0.34 0.2,-0.71 0.11,-1.49 0.42,-2.4 -0.01,-0.37 -0.09,-0.35 -0.17,-0.25 -0.45,-0.43 -0.15,-0.19 -0.09,-0.22 -0.01,-0.23 0.23,-1.56 0.09,-0.33 0.32,-0.74 0.22,-0.4 0.24,-0.35 0.34,-0.38 0.27,-0.22 0.31,-0.19 0.76,-0.05 6.95,0.83 1.01,-0.09 1.67,-0.61 2.48,-1.01 0.35,-0.08 0.95,-0.05 0.3,-0.25 0.34,-0.47 3.14,-5.82 2.41,-2.99 0.53,-0.44 1.31,-0.27 0.59,-0.6 0.76,-0.47 1.55,-0.7 0.9,-0.57 0.83,-0.68 2.29,-2.31 0.29,-0.4 0.23,-0.42 0.17,-0.44 0.11,-0.5 0.07,-0.68 0.01,-0.52 -0.02,-0.43 -0.28,-1.53 -0.01,-0.24 0.03,-0.13 0.09,-0.08 0.15,-0.07 0.68,-0.17 0.14,-0.06 0.06,-0.17 0.03,-0.23 -0.07,-0.79 0.01,-0.31 0.07,-0.5 -0.07,-0.15 -0.15,-0.16 -1.08,-0.57 -0.14,-0.21 0.05,-0.39 0.17,-0.26 0.24,-0.21 0.91,-0.4 0.27,-0.18 0.19,-0.18 0.12,-0.19 0.06,-0.17 0.04,-0.15 -0.01,-0.18 -0.02,-0.23 -0.15,-0.68 -1.11,-0.98 -4.59,-2.16 -2.41,1.44 -0.77,0.37 -0.7,0.2 -0.41,0.21 -0.39,0.25 -0.42,0.12 -5.32,0.45 -0.28,-0.05 -0.26,-0.21 -0.15,-0.23 -0.24,-0.56 -0.07,-0.15 -0.09,-0.13 -0.08,-0.08 -0.06,-0.05 -0.07,-0.02 -6.93,1.31 -0.73,-0.06 -0.11,-0.29 1.05,-3.08 0.52,-0.95 0.27,-0.4 0.46,-0.55 0.42,-0.39 0.22,-0.14 0.69,-0.29 0.18,-0.12 0.13,-0.12 0.09,-0.12 0.05,-0.11 0.35,-1.09 1.31,-2.73 0.36,-1.06 0.05,-0.79 -0.07,-1.03 -0.87,-5.96 -0.17,-0.62 -0.72,-1.06 -0.04,-0.81 0.04,-0.73 0.04,-0.27 0.07,-0.24 0.07,-0.15 0.33,-0.46 0.06,-0.15 0.03,-0.17 -0.01,-0.17 -0.13,-0.54 -0.04,-0.36 -0.01,-2.3 0.08,-0.31 0.19,-0.49 0.02,-0.26 -0.02,-0.31 -0.18,-0.87 -0.14,-0.22 -0.24,-0.2 -0.49,-0.1 -1.17,0.04 z\"\n        />\n      <path\n        id=\"AF-GHO\"\n        title=\"Ghor\"\n        \n        d=\"m 254.48715,199.21119 -0.23,-0.06 -0.7,-0.38 -0.36,-0.14 -0.52,-0.06 -0.26,-0.11 -0.21,-0.29 -0.12,-0.51 -0.07,-0.2 -0.11,-0.17 -0.11,-0.17 -0.13,-0.15 -0.15,-0.13 -0.14,-0.11 -0.72,-0.39 -0.17,-0.11 -0.15,-0.14 -0.1,-0.15 -0.08,-0.14 -0.03,-0.15 0,-0.16 0.02,-0.19 0.05,-0.21 0.11,-0.35 0.01,-0.1 -0.02,-0.08 -0.21,-0.01 -0.35,0.06 -1.67,0.55 -0.2,0.1 -0.19,0.14 -0.53,0.53 -0.32,0.16 -0.46,0.14 -1.48,0.29 -2.86,0.03 -0.47,-0.19 -0.51,-0.87 -1.59,1.16 -1.51,1.62 -0.27,0.19 -2.38,0.93 -0.7,0.03 -0.39,-0.17 -0.21,-0.13 -0.19,-0.16 -0.21,-0.06 -0.25,0.01 -0.84,0.4 -0.12,0.04 -0.11,0.06 -0.09,0.05 -0.12,0 -0.17,-0.07 -0.25,-0.27 -0.03,-0.23 0.06,-0.21 0.21,-0.32 0.06,-0.16 0.01,-0.19 -0.06,-0.22 -0.47,-0.86 -0.19,-0.25 -0.24,-0.22 -0.29,-0.11 -0.27,0.01 -0.32,0.23 -0.35,0.38 -0.18,0.11 -0.58,0.17 -0.15,-0.02 -0.89,-0.44 -0.18,-0.12 -0.47,-0.4 -0.27,-0.19 -0.6,-0.3 -0.49,-0.33 -0.36,-0.11 -0.48,0 -0.84,0.28 -0.66,0.37 -0.39,0.32 -1.34,0.76 -1.75,0.39 -0.14,0.06 -0.13,0.01 -0.09,-0.07 -0.1,-0.26 -0.01,-0.25 0.02,-0.23 -0.02,-0.18 -0.08,-0.11 -0.28,-0.04 -0.43,0.08 -3.65,1.41 -0.34,0.06 -0.34,-0.06 -0.54,-0.26 -1.1,-1.09 -0.33,-0.14 -0.5,-0.06 -2.33,0.3 -3.69,1.28 0.97,2.04 0.72,0.72 2.07,1.33 0.32,0.14 0.31,0.06 1.21,-0.11 0.6,0.18 0.49,0.35 0.46,0.44 0.7,0.83 1.2,1.78 0.39,0.4 2.98,2.14 0.64,0.81 0.36,0.7 0.07,0.52 0.22,0.64 0.39,0.72 0.83,1.19 0.33,0.69 0.13,0.64 -0.09,0.6 -0.47,2 -0.08,0.62 0.01,0.55 0.09,1.05 -0.04,0.49 -0.3,1.47 -0.24,0.78 -0.48,1.08 -0.17,0.25 -0.22,0.19 -0.18,0.1 -0.32,0.4 -1.48,0.72 -8.65,2.39 -3.21,1.61 -0.59,0.17 -0.5,0 -0.39,-0.14 -0.69,-0.04 -0.4,-0.15 -0.78,-0.4 -5.94,-0.24 -1,0.31 -0.61,0.33 -0.4,0.47 -0.28,0.37 -0.11,0.25 -0.18,0.62 -0.79,5.02 -0.02,1.58 0.09,0.39 0.17,0.53 0.74,1.72 0.11,0.82 -0.04,0.48 -0.19,0.24 -0.16,0.09 -0.2,0.03 -0.64,-0.16 -0.84,-0.07 -0.24,-0.1 -0.18,-0.18 -0.25,-0.41 -0.45,-0.9 -0.23,-0.12 -0.35,-0.06 -0.35,0.17 -0.99,0.67 -0.86,0.44 -1.06,0.11 -0.52,0.18 -0.36,0.24 -0.31,0.34 -0.56,0.36 -0.64,0.16 -0.62,0.06 -1,-0.09 -0.73,-0.17 -0.58,-0.07 -0.45,0.07 -0.69,0.27 -0.32,0.23 -0.19,0.26 -0.08,0.34 -0.04,0.43 0,0.52 0.22,1.69 -0.02,0.3 -0.13,0.78 -0.03,0.41 0.04,0.47 0.09,0.44 0.06,0.52 -0.03,0.42 -0.25,0.66 -0.1,0.38 0.04,0.33 0.14,0.23 0.41,0.55 0.23,0.36 0.16,0.4 0.08,0.54 -0.07,0.7 -0.35,0.92 -0.31,0.43 -0.33,0.24 -0.65,0.05 -0.49,0.16 -0.68,0.42 -0.72,0.8 -0.47,0.38 -0.4,0.22 -0.36,0.06 -1.18,-0.05 -0.15,0.03 -0.19,0.06 -0.22,0.03 -0.3,0.01 -0.33,-0.05 -0.37,-0.12 -2.18,-1.13 -0.14,-0.02 -0.09,0.04 -0.36,0.58 -0.31,0.66 -0.67,0.35 -6.15,-0.12 -0.79,-0.25 -0.84,-0.83 -0.66,-0.51 -0.8,-0.48 -1.42,-0.57 -0.76,-0.21 -4.12,-0.36 -0.82,0.03 -0.42,0.06 -6.67,0.27 -0.8,3.38 -1.32,2.53 -2.84,4.25 -0.22,0.65 0.07,0.59 1.95,3.3 0.27,0.64 0.2,0.92 0.17,0.51 0.22,0.43 0.36,0.37 1.54,1.29 0.92,1.11 0.6,0.88 0.39,0.77 0.68,2.47 0.25,0.43 1.37,1.41 0.51,0.83 0.04,0.5 -0.27,0.46 -0.46,0.31 -0.56,0.3 -0.46,0.19 -0.42,0.1 -0.84,0.08 -0.21,0.07 -0.2,0.14 -0.2,0.18 -0.19,0.25 -0.18,0.39 -0.05,0.3 0.05,0.29 0.65,2 0.08,0.55 0,0.33 -0.04,0.15 -0.72,1.45 -1.11,0.8 -1.32,0.44 -4.69,0.44 -5.44,1.52 -0.76,0.34 -0.04,0.09 -0.15,0.21 -0.26,0.27 -0.8,0.55 -0.67,0.29 -0.82,0.19 -1.95,0.87 -1.97,1.49 -0.66,0.69 -0.14,0.31 -0.05,0.36 0.03,0.21 0.09,0.19 0.45,0.4 0.21,0.26 0.19,0.33 0.12,0.47 -0.04,0.38 -0.23,0.69 0.14,0.62 0.34,0.67 0.99,1.37 0.34,0.8 0.25,0.97 0,1.13 0.19,2.18 0.3,1.06 1.6,0.67 1.63,0.05 0.67,-0.09 3.68,-1.16 9.82,-0.4 2.53,-0.5 1.95,-0.78 1.86,-1.53 0.99,-0.6 1.54,-0.62 2.05,-0.47 3.26,-0.34 0.78,-0.3 0.61,-0.53 0.76,-1.19 0.96,-1.2 0.69,-0.63 0.92,-0.69 2.18,-1.02 1.22,-0.13 1.76,0.95 0.8,0.75 2.76,4.53 1.49,3.51 0.15,0.47 -0.03,0.94 0.05,0.34 0.14,0.67 0,0.24 -0.07,0.62 0.02,0.53 0.14,0.3 0.16,0.2 0.67,0.29 0.41,0.14 0.53,0.01 0.32,0.18 0.18,0.27 0.17,0.54 0.15,0.32 0.17,0.19 0.68,0.55 0.38,0.51 1.43,2.83 0.37,0.58 0.3,0.3 0.21,-0.04 0.67,-0.26 0.35,0 0.27,0.15 1.12,1.07 2.09,1.15 0.24,0.04 0.14,-0.05 0.53,-0.46 0.21,-0.13 0.17,-0.07 0.42,-0.08 1.32,-0.08 0.29,-0.08 0.21,-0.1 0.15,-0.12 0.08,-0.16 0.02,-0.18 -0.09,-0.24 -0.14,-0.23 -1.01,-1.06 -0.12,-0.17 -0.03,-0.12 0.03,-0.13 0.11,-0.14 0.22,-0.16 0.25,-0.11 1.34,-0.3 0.85,-0.4 0.92,-0.57 0.63,-0.48 0.49,-0.46 0.45,-0.56 0.73,-1.29 1.11,-2.92 0.54,-0.57 0.83,-0.19 0.55,0.07 0.16,0.07 0.11,0.08 0.11,0.11 0.07,0.15 0.18,0.53 0.15,0.23 0.44,0.49 0.6,0.42 0.39,0.16 0.3,0.07 0.16,-0.02 0.14,-0.05 0.34,-0.05 1.68,0.03 0.55,0.13 0.33,0.16 0.12,0.19 0.08,0.24 0.06,0.3 0.05,0.35 0.36,0.74 1.51,1.12 2.17,-1.07 0.61,-0.04 0.08,0.17 0.07,0.2 0.04,0.2 0.13,1.43 0.14,0.19 0.26,0.24 0.65,0.11 0.34,0 0.3,-0.08 0.35,-0.15 0.3,-0.17 0.34,-0.31 1.58,-1.73 0.64,-0.56 0.36,-0.16 0.29,0.07 0.34,0.21 0.41,0.21 0.88,0.18 0.43,-0.02 0.36,-0.17 0.48,-0.48 1.33,-0.92 0.11,-0.13 0.13,-0.16 4.22,-1.99 0.24,-0.22 0.55,-0.64 0.81,-0.71 2.09,-1.33 0.59,-0.16 0.53,-0.03 1.81,0.17 1.32,-1.04 0.76,-0.5 1.07,-0.43 1.34,-0.27 0.92,-0.32 1.92,-1.03 0.05,-0.84 -0.02,-0.13 -0.05,-0.17 -0.22,-0.17 -0.1,-0.11 0,-0.19 0.12,-0.28 0.69,-1.05 0.11,-0.11 0.14,-0.08 0.18,-0.05 0.64,-0.07 0.16,-0.04 0.15,-0.06 0.14,-0.11 0.12,-0.13 0.1,-0.15 0.13,-0.15 1.6,-1.33 1.44,-1.76 1.61,-1.43 0.26,-0.15 0.68,-0.31 0.75,-0.43 1.22,-0.93 0.12,-0.24 0.03,-0.28 -0.18,-0.63 -0.3,-0.55 -0.64,-0.73 -0.78,-0.68 -1.02,-0.47 -0.43,-0.94 -0.42,-3.23 -0.3,-1.74 -0.3,-0.58 -0.31,-0.34 -0.2,-0.35 -0.11,-0.11 -0.15,-0.1 -0.31,-0.14 -0.3,-0.07 -0.12,-0.06 -0.12,-0.08 -0.18,-0.19 -0.09,-0.12 -0.06,-0.16 -0.05,-0.23 -0.07,-0.24 -0.1,-0.21 -0.16,-0.17 -1.3,-0.8 -0.15,-0.16 -0.09,-0.15 -0.06,-0.16 -0.1,-0.79 -0.06,-0.21 -0.1,-0.26 -0.04,-0.15 -0.04,-0.17 0.01,-0.21 0.06,-0.36 0.15,-0.28 0.22,-0.23 1.17,-0.65 0.25,-0.2 0.14,-0.08 0.15,-0.07 0.21,-0.06 0.88,-0.1 0.17,-0.04 0.19,-0.28 0.26,-0.52 0.3,-1.08 0.74,-1.14 0.58,-0.33 0.4,-0.63 0.27,-1.38 -0.32,-1.75 -0.12,-0.21 -0.18,-0.23 -0.63,-0.04 -0.21,-0.03 -0.15,-0.07 -0.14,-0.13 -0.11,-0.2 -0.26,-0.78 -0.12,-0.27 -0.12,-0.2 -0.13,-0.17 -0.12,-0.11 -0.18,-0.15 -0.06,-0.23 0.14,-1.52 0.28,-0.86 0.27,-1.16 -0.03,-0.64 0.01,-0.46 0.08,-0.41 0.6,-1.16 0.02,-0.28 -0.11,-0.3 -0.14,-0.16 -0.13,-0.13 -0.1,-0.11 -0.04,-0.37 0.04,-0.54 0.25,-1.39 0.22,-0.85 0.68,-1.73 0.12,-0.47 -0.07,-0.51 0.05,-0.89 0.5,-1.89 0.5,-0.25 4.5,-0.17 1.83,0.14 3.66,0.67 6.45,0.42 3.08,0.62 0.53,-0.02 0.58,-0.15 0.87,-0.43 0.54,-0.35 0.4,-0.35 0.66,-0.82 0.1,-0.1 0.35,-0.06 1.81,0.06 0.97,0.18 0.32,0.03 1.8,-0.34 1.28,0.17 0.5,-0.05 0.51,-0.26 0.36,-0.28 0.99,-1.14 0.17,-0.26 0.32,-0.69 0.4,-0.18 0.62,-0.03 2.55,0.46 0.53,0.2 0.41,0.22 0.19,0.14 0.63,0.6 0.26,0.13 0.27,0.04 0.26,-0.04 0.44,-0.18 6.08,-0.96 1.1,0.43 2.97,-2.09 0.46,-0.23 2.65,-0.77 0.22,-0.01 0.21,0.02 1.53,0.46 0.42,-0.19 0.64,-0.42 2.97,-2.54 0.07,-0.69 -0.85,-0.74 -0.21,-0.28 -0.25,-0.65 -0.04,-0.31 0.16,-0.66 0.11,-0.75 0.04,-0.12 0.13,-0.2 1.45,-1.33 0.78,-0.94 0.27,-0.44 0.15,-0.4 0.04,-0.4 -0.06,-0.43 -0.15,-0.48 -0.23,-0.46 -0.5,-0.58 -0.61,-0.53 -1.67,-1.05 -0.33,-0.13 -0.21,0.02 -0.68,0.18 -0.2,0.02 -0.19,-0.02 -0.18,-0.03 -0.83,-0.46 -0.12,-0.03 -0.11,0.03 -0.37,0.2 -0.17,0 -0.11,-0.13 -0.01,-0.42 0.15,-0.31 0.35,-0.44 0.04,-0.16 -0.18,-0.22 -0.34,-0.27 -0.76,-0.43 -0.49,-0.34 -0.44,-0.43 -0.48,-0.8 -0.24,-0.63 -0.44,-1.77 -0.26,-0.33 -1.42,-1.38 -0.81,-0.92 -0.16,-0.23 -0.14,-0.18 -4.02,-3.02 -1.34,-0.65 -2.2,-0.46 -0.94,0.4 -0.47,0.27 -0.22,0.08 -0.33,0.03 -0.41,-0.01 -0.87,-0.12 -0.64,-0.18 -0.87,-0.41 -0.37,-0.25 -0.32,-0.15 -0.22,0.02 -0.33,0.2 -0.19,0.22 -0.1,0.24 -0.02,0.23 0.03,0.22 0.06,0.21 0.21,0.47 0.02,0.12 -0.04,0.12 -0.09,0.1 -0.17,0.09 -0.21,0.08 -0.65,0.15 -0.15,0.09 -0.07,0.12 -0.08,0.53 -0.06,0.2 -0.09,0.16 -0.11,0.1 -0.15,0.05 -0.89,0.08 -0.21,0.06 -0.15,0.11 -0.09,0.14 -0.04,0.2 0.03,0.21 0.05,0.2 0.02,0.18 0,0.18 -0.08,0.15 -0.12,0.13 -0.17,0.08 -0.66,0.16 -0.17,0.07 -0.15,0.11 -1.25,1.62 -0.46,0.47 -0.18,0.11 -0.17,0.03 -0.15,-0.1 -0.17,-0.27 0.03,-0.51 0.09,-0.4 0.34,-0.79 0.05,-0.2 -0.06,-0.37 -1.38,-4.83 -0.39,-0.87 -0.32,-0.53 -0.4,-0.39 -0.82,-0.23 -0.02,-0.37 0.13,-0.33 0.37,-0.49 0.08,-0.14 0.02,-0.11 -0.16,-0.18 -0.91,-0.79 -0.16,-0.31 0.01,-0.25 0.18,-0.14 0.54,-0.22 0.13,-0.1 0.08,-0.15 0.07,-0.23 0.04,-0.28 0.06,-1.23 -0.01,-0.27 -0.03,-0.25 -0.08,-0.28 -0.13,-0.31 -0.16,-0.3 -0.18,-0.25 -0.29,-0.19 -0.35,-0.11 -0.65,-0.05 -0.36,0.05 -0.24,0.1 -0.12,0.12 -0.31,0.01 -2.43,-0.55 -1.5,-0.15 -5.01,-1.73 -0.35,-0.19 -0.39,-0.29 -0.52,-0.59 -0.26,-0.41 -0.64,-1.27 -0.12,-0.32 -0.09,-0.31 -0.06,-0.34 0,-0.26 0.1,-0.65 0,-0.2 -0.04,-0.21 -0.07,-0.17 -0.12,-0.1 -0.17,-0.02 -0.78,0.2 -0.18,0.01 z\"\n        />\n      <path\n        id=\"AF-JOW\"\n        title=\"Jawzjan\"\n        \n        d=\"m 276.12715,67.771194 -0.57,-0.08 -0.97,-0.5 -0.84,-0.83 -1.35,-1.82 -0.88,-0.76 -10.89,-4.1 -0.93,-0.68 -0.33,-0.4 -0.39,-0.58 -0.82,-1.84 -0.38,-0.25 -0.96,-0.22 -0.74,-0.33 -0.12,0.87 -0.3,1.6 -0.64,0.54 -2,-0.02 -0.84,0.19 -0.59,0.35 -0.49,0.63 -0.5,0.99 0,0.01 0,0 -0.01,0.01 -0.6,1.2 -0.49,1.3 -0.23,1.37 0.39,2.62 0.02,1.35 -0.26,1.24 -0.62,0.86 -1.36,1.4 -2.25,3.1 -1.72,0.91 -0.65,0.02 -0.27,0.43 -5.6,10.01 -1.36,4.49 -0.33,1.88 -0.03,0.89 0.03,0.86 0.4,1.81 2.51,3.22 0.56,2.559996 0.15,0.32 0.31,0.52 0.36,0.38 0.68,0.9 0.3,0.57 0.19,0.51 0.03,0.33 -0.02,0.26 -0.34,1.47 -0.42,1.85 -0.6,2.64 -0.15,0.32 -0.29,0.48 -0.28,0.23 -0.37,0.38 -0.1,0.22 0.01,0.23 0.15,0.29 0.21,0.26 0.28,0.26 1.23,0.68 0.29,0.25 0.25,0.28 0.26,0.44 0.06,0.29 0.01,0.21 -0.51,1.91 -0.3,1.88 0.03,0.54 0.13,0.46 0.75,1.22 0.6,1.91 0.3,1.34 0.2,0.63 0.54,1.27 -0.98,0.43 -0.23,0.45 -0.04,0.28 -0.1,0.09 -0.15,0.02 -0.54,-0.09 -8,0.29 -0.94,0.26 -0.26,0.22 -0.37,0.44 -0.17,0.4 -1.49,5.68 0.01,2.81 0.05,0.48 0.07,0.39 0.11,0.33 0.07,0.37 0.03,0.44 -0.03,0.7 -0.08,0.37 -0.16,0.31 -0.4,0.59 -0.33,0.62 -0.17,0.21 -0.39,0.36 -0.21,0.15 -0.52,0.58 0.37,2.03 0.23,0.77 -0.08,0.28 -0.18,0.4 -0.63,0.87 -0.31,0.33 -0.25,0.23 -0.2,0.14 -0.96,0.44 -0.77,0.58 -0.31,0.29 -0.19,0.28 -0.05,0.29 -0.03,0.31 0.02,0.29 0.07,0.24 0.1,0.2 0.23,0.32 0.26,0.23 1.41,1.24 0.83,0.32 0.59,0.07 0.33,0 0.26,-0.07 0.5,-0.38 0.44,-0.23 1.95,-0.22 0.52,-0.13 0.24,-0.01 0.18,0.06 0.09,0.13 0.05,0.16 0.06,0.42 0.15,0.26 0.16,0.08 0.16,-0.03 0.48,-0.26 0.47,-0.16 0.26,0 0.17,0.03 0.41,0.31 0.26,0.16 2.23,0.82 2.32,0.32 3.32,-0.48 0.24,-0.09 0.21,-0.14 0.33,-0.4 0.26,-0.45 0.25,-0.33 0.09,-0.16 0.05,-0.16 0.02,-0.19 0.08,-0.21 0.15,-0.18 0.35,-0.19 0.26,-0.04 0.17,0.03 0.15,0.11 0.14,0.16 0.23,0.43 0.22,0.3 0.35,0.37 0.49,0.39 0.26,0.13 0.2,0 0.15,-0.09 0.49,-0.41 0.17,-0.11 1.37,-0.44 0.44,-0.23 0.84,-0.56 0.8,-0.39 0.18,-0.12 0.17,-0.13 0.16,-0.18 0.14,-0.2 0.13,-0.25 0.09,-0.25 0.11,-0.58 0.04,-0.4 -0.4,0.33 -0.32,0.15 -0.43,0.15 -6.23,0.94 -0.29,-0.22 -0.15,-0.47 0.05,-1.17 0.54,-1.46 -0.01,-0.41 -0.08,-0.49 -0.18,-0.64 -0.21,-0.51 -0.59,-0.91 -1.14,-1.08 0.87,-9.67 -0.01,-0.5 -0.32,-1.96 -0.03,-0.54 0.04,-0.53 0.23,-0.57 0.2,-0.32 0.79,-0.5 2.57,-0.01 5.31,-0.7 1.08,-0.36 1.19,-0.84 2.48,-2.53 2.92,-2.23 1.36,-0.6 4.42,-0.92 0.47,-0.25 0.5,-0.57 0.47,-0.73 0.16,-0.18 0.26,-0.24 2.06,-1.34 1.15,-0.45 1.31,-0.15 1.96,0.56 4.63,-0.27 5.66,-0.94 3.65,-1.04 1.49,-0.1 0.64,0.04 0.62,-0.08 0.29,-0.09 1.86,-0.94 -1.15,-0.98 -1.15,-1.17 -0.43,-0.82 -0.26,-0.94 -0.16,-3.19 -0.62,-0.63 -0.44,-0.28 -0.88,-0.74 -0.24,-0.42 0.01,-0.41 0.41,-0.64 0.46,-0.35 1.67,-0.74 0.2,-0.05 0.23,0.01 0.23,0.05 1.01,0.4 0.25,0.02 0.31,-0.04 0.32,-0.12 0.81,-0.54 0.22,-0.06 0.78,-0.11 0.24,-0.11 0.14,-0.33 0.06,-0.53 -0.43,-1.81 -0.25,-0.6 -0.49,-0.749996 -0.16,-0.65 -0.03,-0.42 0.08,-0.25 0.05,-0.22 0,-0.26 -0.17,-0.37 -0.31,-0.47 -1.17,-1.39 -1.32,-2.21 -0.72,-1.21 -0.23,-1.07 0.22,-1.44 0.41,-1.71 0.99,-3.04 0.11,-0.25 0.05,-0.11 0.14,-0.5 0.06,-0.36 -0.41,-1.05 -2.28,-2.45 -0.66,-0.93 -0.1,-0.19 -0.16,-0.91 -0.02,-0.48 -0.04,-0.25 -0.09,-0.29 -0.12,-0.25 -0.23,-0.36 -0.19,-0.57 -0.27,-0.62 -0.36,-0.4 -0.47,-0.36 -1.7,-0.98 -0.45,-0.16 -0.72,-0.04 -3.02,-0.84 -0.52,-0.22 -0.12,-0.1 -0.12,-0.12 -0.17,-0.22 -0.11,-0.2 -0.05,-0.39 -0.02,-0.32 0.11,-1.95 0.03,-0.41 -0.43,-0.22 -0.46,-0.13 -0.68,-0.57 -0.52,-0.13 -1.6,0.08 z\"\n        />\n      <path\n        id=\"AF-FYB\"\n        title=\"Faryab\"\n        \n        d=\"m 241.16715,129.07119 0.15,-0.02 0.1,-0.09 0.04,-0.28 0.23,-0.45 0.98,-0.43 -0.54,-1.27 -0.2,-0.63 -0.3,-1.34 -0.6,-1.91 -0.75,-1.22 -0.13,-0.46 -0.03,-0.54 0.3,-1.88 0.51,-1.91 -0.01,-0.21 -0.06,-0.29 -0.26,-0.44 -0.25,-0.28 -0.29,-0.25 -1.23,-0.68 -0.28,-0.26 -0.21,-0.26 -0.15,-0.29 -0.01,-0.23 0.1,-0.22 0.37,-0.38 0.28,-0.23 0.29,-0.48 0.15,-0.32 0.6,-2.64 0.42,-1.85 0.34,-1.47 0.02,-0.26 -0.03,-0.33 -0.19,-0.51 -0.3,-0.57 -0.68,-0.9 -0.36,-0.38 -0.31,-0.52 -0.15,-0.32 -0.56,-2.559996 -2.51,-3.22 -0.4,-1.81 -0.03,-0.86 0.03,-0.89 0.33,-1.88 1.36,-4.49 5.6,-10.01 0.27,-0.43 -20.61,0.53 -3.59,1.19 -8.78,4.76 -1.42,1.06 -0.9,1.55 -0.22,2.37 0.92,4.62 0.2,2.37 -0.65,2.05 -1.78,3.28 -5.2,9.569996 -0.8,1.93 -0.62,2.39 -0.16,1.25 0.01,1.22 0.23,1.23 0.68,2.31 0.13,1.25 -0.22,2.4 -0.56,2.22 -0.86,2.09 -3.81,5.91 -0.99,1.19 -1.61,1.3 -1.63,0.61 -3.57,0.84 -0.89,0.37 -0.74,0.49 -0.57,0.73 -0.77,2.42 -0.48,0.4 -0.6,-0.14 -0.81,-0.34 -2,-0.32 -1.8,0.33 -1.71,0.86 -3.21,2.45 -0.61,0.77 -0.41,0.96 0.01,1.07 0.44,2.84 -0.02,0.75 -4.61,-1.81 -1.36,-0.29 -0.76,0.12 1.42,0.84 0.54,1.8 0.03,2.63 -0.18,2.35 -0.85,3.87 -0.42,1.28 -0.12,1.05 0.07,1.25 -0.07,0.29 -0.71,1.15 -0.52,1.45 -0.11,0.67 0.09,0.46 0.3,0.19 0.34,0.1 0.55,0.1 0.64,0.26 0.46,0.26 0.47,0.35 0.83,0.83 0.58,0.74 0.65,1.16 0.41,1.19 0.2,1 0.22,4.18 -0.02,0.58 -0.06,0.5 -0.14,0.59 0,0.33 0.07,0.23 0.19,0.22 0.24,0.2 0.24,0.17 0.52,0.28 7.71,0.55 2.96,0.56 0.31,0.31 0.21,0.36 0.15,0.36 0.06,0.34 -0.01,0.29 -0.11,0.27 -0.06,0.12 -0.33,0.44 -0.03,0.46 1.77,1.04 0.52,0.18 0.39,0.07 0.89,-0.27 0.54,-0.05 1.76,-0.58 2.12,-0.31 0.4,0.37 0.06,0.58 -0.01,0.76 -0.18,1.33 -0.46,1.81 -0.04,0.59 0.03,0.93 -0.26,2.17 -0.03,0.99 0.11,0.77 0.14,0.28 0.27,0.17 4.63,1.54 4.96,0.68 0.9,-0.01 1.44,-0.26 1.62,-0.06 1.14,-0.25 0.52,-0.24 3.69,-1.28 2.33,-0.3 0.5,0.06 0.33,0.14 1.1,1.09 0.54,0.26 0.34,0.06 0.34,-0.06 3.65,-1.41 0.43,-0.08 0.28,0.04 0.08,0.11 0.02,0.18 -0.02,0.23 0.01,0.25 0.1,0.26 0.09,0.07 0.13,-0.01 0.14,-0.06 1.75,-0.39 1.34,-0.76 0.39,-0.32 0.66,-0.37 0.84,-0.28 0.48,0 0.36,0.11 0.49,0.33 0.6,0.3 0.27,0.19 0.47,0.4 0.18,0.12 0.89,0.44 0.15,0.02 0.58,-0.17 0.18,-0.11 0.35,-0.38 0.32,-0.23 0.27,-0.01 0.29,0.11 0.24,0.22 0.19,0.25 0.47,0.86 0.06,0.22 -0.01,0.19 -0.06,0.16 -0.21,0.32 -0.06,0.21 0.03,0.23 0.25,0.27 0.17,0.07 0.12,0 0.09,-0.05 0.11,-0.06 0.12,-0.04 0.84,-0.4 0.25,-0.01 0.21,0.06 0.19,0.16 0.21,0.13 0.39,0.17 0.7,-0.03 2.38,-0.93 0.27,-0.19 1.51,-1.62 1.59,-1.16 -1.08,-1.32 -0.21,-0.38 -0.17,-0.45 -0.55,-1.86 -0.16,-0.9 -0.03,-0.68 0.15,-1.12 0.03,-0.55 -0.2,-1.12 0.09,-1.68 0.17,-1.47 0.09,-0.23 0.16,-0.2 0.68,-0.41 0.26,-0.26 0.22,-0.36 0.62,-1.85 1.5,-2.93 0.1,-0.24 0.01,-0.12 0,-0.14 -0.09,-0.4 -0.62,-2.01 -0.05,-0.22 0,-0.15 0.01,-0.2 0.14,-0.47 0.25,-0.54 0.06,-0.18 0.02,-0.12 -0.03,-0.41 0.02,-0.37 0.17,-0.3 0.29,-0.34 1.53,-1.22 0.35,-0.13 0.47,-0.06 1.52,0.15 1.51,0.41 0.26,0.02 0.24,-0.02 0.59,-0.31 0.93,-1.23 1.1,-1.78 0.23,-0.28 0.29,-0.49 0.18,-0.43 0.19,-0.9 0.05,-0.37 -0.06,-0.31 -0.23,-0.43 -0.02,-0.23 0.1,-0.2 0.35,-0.15 0.27,-0.03 0.23,-0.01 0.86,0.1 1.28,0.29 1.65,-0.26 0.84,-0.29 0.08,-0.06 0.1,-0.2 0.11,-0.32 0.12,-0.65 0.13,-0.38 0.18,-0.34 0.38,-0.48 0.05,-0.16 -0.05,-0.13 -0.24,-0.13 -0.16,-0.04 -0.13,-0.01 -0.03,-0.06 -0.03,-0.14 -0.01,-0.49 -0.48,-2.18 -0.03,-0.28 -0.05,-0.18 -0.08,-0.07 -0.33,-0.06 -0.15,-0.16 -0.11,-0.16 -0.47,-1.16 -0.21,-0.88 -0.1,-0.14 -0.17,-0.14 -0.43,-0.24 -0.25,-0.27 -0.21,-0.52 0.06,-0.27 0.11,-0.23 0.15,-0.16 0.12,-0.17 0.06,-0.13 0.02,-0.12 -0.08,-0.5 0,-0.09 0.06,-0.14 0.33,-0.48 0.04,-0.13 -0.01,-0.19 -0.11,-0.25 -0.2,-0.26 -0.28,-0.23 -0.27,-0.16 -0.32,-0.08 -0.32,-0.03 -0.77,0.24 -1.24,1.01 -0.04,0.4 -0.11,0.58 -0.09,0.25 -0.13,0.25 -0.14,0.2 -0.16,0.18 -0.17,0.13 -0.18,0.12 -0.8,0.39 -0.84,0.56 -0.44,0.23 -1.37,0.44 -0.17,0.11 -0.49,0.41 -0.15,0.09 -0.2,0 -0.26,-0.13 -0.49,-0.39 -0.35,-0.37 -0.22,-0.3 -0.23,-0.43 -0.14,-0.16 -0.15,-0.11 -0.17,-0.03 -0.26,0.04 -0.35,0.19 -0.15,0.18 -0.08,0.21 -0.02,0.19 -0.05,0.16 -0.09,0.16 -0.25,0.33 -0.26,0.45 -0.33,0.4 -0.21,0.14 -0.24,0.09 -3.32,0.48 -2.32,-0.32 -2.23,-0.82 -0.26,-0.16 -0.41,-0.31 -0.17,-0.03 -0.26,0 -0.47,0.16 -0.48,0.26 -0.16,0.03 -0.16,-0.08 -0.15,-0.26 -0.06,-0.42 -0.05,-0.16 -0.09,-0.13 -0.18,-0.06 -0.24,0.01 -0.52,0.13 -1.95,0.22 -0.44,0.23 -0.5,0.38 -0.26,0.07 -0.33,0 -0.59,-0.07 -0.83,-0.32 -1.41,-1.24 -0.26,-0.23 -0.23,-0.32 -0.1,-0.2 -0.07,-0.24 -0.02,-0.29 0.03,-0.31 0.05,-0.29 0.19,-0.28 0.31,-0.29 0.77,-0.58 0.96,-0.44 0.2,-0.14 0.25,-0.23 0.31,-0.33 0.63,-0.87 0.18,-0.4 0.08,-0.28 -0.23,-0.77 -0.37,-2.03 0.52,-0.58 0.21,-0.15 0.39,-0.36 0.17,-0.21 0.33,-0.62 0.4,-0.59 0.16,-0.31 0.08,-0.37 0.03,-0.7 -0.03,-0.44 -0.07,-0.37 -0.11,-0.33 -0.07,-0.39 -0.05,-0.48 -0.01,-2.81 1.49,-5.68 0.17,-0.4 0.37,-0.44 0.26,-0.22 0.94,-0.26 8,-0.29 0.54,0.09 z\"\n        />\n      <path\n        id=\"AF-BAL\"\n        title=\"Balkh\"\n        \n        d=\"m 328.71715,79.381194 -1.08,-0.44 -0.56,-0.94 -0.35,-1.13 -0.49,-1.04 -0.97,-0.9 -2.15,-0.83 -0.98,-0.62 -0.46,-0.93 -0.8,-2.65 -0.6,-0.55 -1.01,-0.3 -1.94,-1.4 -0.89,-0.42 -2.34,-0.05 -4.45,1.06 -6.35,0.28 -0.51,0.19 -0.95,0.83 -0.52,0.19 -1.31,-0.05 -0.6,-0.11 -3.21,-1.37 -1.16,-0.13 -1.24,0.1 -0.94,0.3 -0.81,0.52 -0.86,0.74 -0.74,0.84 -0.41,0.32 -0.51,0.09 -0.6,-0.3 -0.73,-1.09 -0.49,-0.24 -1.16,0.15 -3.34,1.1 -1.27,-0.12 -0.98,-0.74 -0.8,-0.83 -0.34,-0.17 -0.03,0.41 -0.11,1.95 0.02,0.32 0.05,0.39 0.11,0.2 0.17,0.22 0.12,0.12 0.12,0.1 0.52,0.22 3.02,0.84 0.72,0.04 0.45,0.16 1.7,0.98 0.47,0.36 0.36,0.4 0.27,0.62 0.19,0.57 0.23,0.36 0.12,0.25 0.09,0.29 0.04,0.25 0.02,0.48 0.16,0.91 0.1,0.19 0.66,0.93 2.28,2.45 0.41,1.05 -0.06,0.36 -0.14,0.5 -0.05,0.11 -0.11,0.25 -0.99,3.04 -0.41,1.71 -0.22,1.44 0.23,1.07 0.72,1.21 1.32,2.21 1.17,1.39 0.31,0.47 0.17,0.37 0,0.26 -0.05,0.22 -0.08,0.25 0.03,0.42 0.16,0.65 0.49,0.749996 0.25,0.6 0.43,1.81 -0.06,0.53 -0.14,0.33 -0.24,0.11 -0.78,0.11 -0.22,0.06 -0.81,0.54 -0.32,0.12 -0.31,0.04 -0.25,-0.02 -1.01,-0.4 -0.23,-0.05 -0.23,-0.01 -0.2,0.05 -1.67,0.74 -0.46,0.35 -0.41,0.64 -0.01,0.41 0.24,0.42 0.88,0.74 0.44,0.28 0.62,0.63 0.16,3.19 0.26,0.94 0.43,0.82 1.15,1.17 1.15,0.98 5.24,1.04 1.41,0.5 0.24,0.17 0.21,0.21 0.18,0.25 0.12,0.34 0.02,0.24 -0.01,0.2 -0.09,0.38 -0.1,0.27 -0.61,1.17 -0.18,0.25 -0.44,0.46 -0.18,0.23 -0.56,1.01 -0.33,0.48 -1.15,1.05 -0.23,0.27 -0.54,0.77 -0.85,1.56 -0.12,0.28 -0.09,0.33 -0.24,1.29 -0.15,0.47 -1.16,2.51 -0.25,0.76 -0.13,0.26 -0.53,0.82 -0.27,0.52 -1.59,2.47 -0.62,0.79 -0.1,0.23 -0.1,0.32 0.04,0.2 0.19,0.25 1.48,0.75 2.01,1.64 0.31,0.18 0.77,0.15 1.17,-0.04 0.49,0.1 0.24,0.2 0.14,0.22 0.18,0.87 0.02,0.31 -0.02,0.26 -0.19,0.49 -0.08,0.31 0.01,2.3 0.04,0.36 0.13,0.54 0.01,0.17 -0.03,0.17 -0.06,0.15 -0.33,0.46 -0.07,0.15 -0.07,0.24 -0.04,0.27 -0.04,0.73 0.04,0.81 0.72,1.06 0.17,0.62 0.87,5.96 0.07,1.03 -0.05,0.79 -0.36,1.06 -1.31,2.73 -0.35,1.09 -0.05,0.11 -0.09,0.12 -0.13,0.12 -0.18,0.12 -0.69,0.29 -0.22,0.14 -0.42,0.39 -0.46,0.55 -0.27,0.4 -0.52,0.95 -1.05,3.08 0.11,0.29 0.73,0.06 6.93,-1.31 0.07,0.02 0.06,0.05 0.08,0.08 0.09,0.13 0.07,0.15 0.24,0.56 0.15,0.23 0.26,0.21 0.28,0.05 5.32,-0.45 0.42,-0.12 0.39,-0.25 0.41,-0.21 0.7,-0.2 0.77,-0.37 2.41,-1.44 -0.06,-0.65 0.07,-0.49 0.11,-0.51 0.2,-0.56 0.81,-1.47 0.36,-0.44 0.44,-0.43 1.81,-0.87 1.02,-0.78 1.85,-2.22 0.65,-0.78 0.61,-0.47 0.25,-0.29 0.22,-0.4 0.16,-0.61 0.2,-0.41 0.38,-0.33 0.79,-0.42 0.31,-0.25 0.25,-0.5 0.21,-0.68 0.13,-1.17 -0.02,-0.53 -0.11,-0.45 -1.33,-1.78 -0.31,-0.5 -0.18,-0.39 -0.03,-0.13 -0.03,-0.63 -0.04,-0.24 -0.08,-0.16 -0.12,-0.11 -0.3,-0.23 -0.17,-0.2 -0.11,-0.19 -0.05,-0.2 -0.01,-0.13 0.01,-0.1 0.98,-0.96 4.57,-3.51 0.9,-0.29 2.57,-0.39 0.69,-0.21 0.39,-0.3 0.48,-0.46 0.81,-0.98 1.01,-0.94 0.34,-0.38 0.23,-0.45 0.1,-0.8 0,-0.4 -0.09,-0.4 -0.09,-0.28 -0.05,-0.19 0.05,-0.29 0.2,-0.36 0.44,-0.47 0.76,-0.62 0.34,-0.48 0.23,-0.39 0.34,-2.19 0.06,-2.7 0.7,-2.89 1.14,-3.19 -0.02,-0.66 -0.06,-0.46 -1.3,-2.73 -0.84,-1.13 -0.08,-0.15 -0.04,-0.14 0.01,-0.15 0.09,-0.15 0.15,-0.24 0.43,-0.44 0.47,-0.38 0.98,-0.49 2.21,-0.67 0.51,-0.48 0.68,-1.67 0.69,-0.25 5.53,0.52 0.67,-0.18 1.4,-0.57 0.74,-0.18 0.78,-0.05 0.68,0.19 2.77,1.36 1.51,0.95 0.28,0.24 0.64,0.41 1.12,0.37 2.34,0.29 9.66,1.37 1.37,-0.27 3.93,-1.61 -0.37,-1.43 -0.32,-0.66 -0.67,-0.9 -0.97,-0.92 -0.78,-0.57 -0.3,-0.35 -1.3,-2.3 -2.61,-6.4 -0.09,-0.51 -0.1,-3.479996 -0.47,-2.51 0.01,-0.08 -0.22,-0.01 -0.44,-0.32 -0.25,-0.47 -0.19,-0.54 -0.29,-0.5 -1.66,-2.07 -0.44,-0.39 -1.14,-0.6 -0.53,-0.46 -0.77,-1.25 -0.51,-1.08 -0.67,-0.75 -2.2,-0.53 -1.2,-0.61 -1.1,-0.81 -0.6,-0.82 -0.05,-0.91 0.41,-3.89 -0.22,-1.14 -0.57,-0.81 -0.85,-0.52 -1.01,-0.25 -1.73,0.08 -0.61,-0.08 -0.54,-0.27 -1.06,-0.81 -0.55,-0.17 -0.56,0.07 -0.92,0.28 -0.51,0.07 -0.3,0.28 -0.3,0.6 -0.37,0.58 -0.53,0.2 -0.79,-0.71 -0.92,-2.52 -1.11,-0.55 -1.01,0.28 -0.89,0.68 -1.59,1.61 -0.97,0.72 -0.97,0.51 -1.05,0.3 -1.18,0.1 -1.24,0.28 -1.85,1.2 -1.09,0.22 z\"\n        />\n      <path\n        id=\"AF-BAM\"\n        title=\"Bamyan\"\n        \n        d=\"m 352.23715,184.22119 -0.64,-0.06 -6.41,-1.5 -1.22,0.02 -3.01,0.62 -5.23,2.14 -1.9,-0.53 -0.97,-0.06 -0.6,-0.14 -0.27,-0.12 -0.43,-0.04 -0.55,0.08 -4.53,1.76 -3.78,0.97 -4.37,1.96 -1.36,0.32 -4.87,-0.03 -1.32,-0.26 -2.65,-1.06 -1.31,0.27 -0.53,0.44 -2.41,2.99 -3.14,5.82 -0.34,0.47 -0.3,0.25 -0.95,0.05 -0.35,0.08 -2.48,1.01 -1.67,0.61 -1.01,0.09 -6.95,-0.83 -0.76,0.05 -0.31,0.19 -0.27,0.22 -0.34,0.38 -0.24,0.35 -0.22,0.4 -0.32,0.74 -0.09,0.33 -0.23,1.56 0.01,0.23 0.09,0.22 0.15,0.19 0.45,0.43 0.17,0.25 0.09,0.35 0.01,0.37 -0.42,2.4 -0.11,1.49 -0.2,0.71 -0.14,0.34 -0.11,0.35 -0.09,0.43 -0.12,1.55 0.02,0.48 0.06,0.43 0.12,0.34 0.15,0.27 0.36,0.46 0.12,0.13 0.33,0.26 0.44,0.19 2.2,0.46 1.34,0.65 4.02,3.02 0.14,0.18 0.16,0.23 0.81,0.92 1.42,1.38 0.26,0.33 0.44,1.77 0.24,0.63 0.48,0.8 0.44,0.43 0.49,0.34 0.76,0.43 0.34,0.27 0.18,0.22 -0.04,0.16 -0.35,0.44 -0.15,0.31 0.01,0.42 0.11,0.13 0.17,0 0.37,-0.2 0.11,-0.03 0.12,0.03 0.83,0.46 0.18,0.03 0.19,0.02 0.2,-0.02 0.68,-0.18 0.21,-0.02 0.33,0.13 1.67,1.05 0.61,0.53 0.5,0.58 0.23,0.46 0.15,0.48 0.06,0.43 -0.04,0.4 -0.15,0.4 -0.27,0.44 -0.78,0.94 -1.45,1.33 -0.13,0.2 -0.04,0.12 -0.11,0.75 -0.16,0.66 0.04,0.31 0.25,0.65 0.21,0.28 0.85,0.74 -0.07,0.69 -2.97,2.54 -0.64,0.42 -0.42,0.19 -1.53,-0.46 -0.21,-0.02 -0.22,0.01 -2.65,0.77 -0.46,0.23 -2.97,2.09 -0.89,1.22 -0.04,0.21 -0.02,0.28 0.48,0.61 0.35,0.63 1.73,0.08 1.54,0.58 0.61,0.3 0.36,0.26 0.29,0.35 0.1,0.32 0.02,0.35 -0.09,0.24 -0.15,0.17 -0.22,0.11 -0.51,0.18 -0.26,0.14 -0.65,0.56 -0.3,0.32 -0.17,0.31 -0.08,0.3 0.22,2.17 0.72,1.88 -0.02,0.38 -0.11,0.6 -1.18,2.54 -0.33,1.03 -0.15,1.74 0.02,0.82 0.13,0.57 0.18,0.29 0.5,0.58 2.78,2.7 2.78,1.5 1.06,0.14 1.27,-0.55 0.59,-0.15 0.23,-0.13 0.2,-0.2 0.45,-0.29 0.64,-0.27 1.46,-0.27 0.82,0 8.61,1.75 0.37,0.18 0.24,0.2 0.16,0.24 0.12,0.24 0.12,0.18 0.08,0.1 0.25,0.17 1.83,0.78 0.36,0.01 0.29,-0.06 2.75,-1.69 0.49,-0.19 0.79,-0.22 0.45,-0.04 0.27,0.03 0.1,0.05 0.2,0.17 0.74,0.87 0.15,0.02 0.12,-0.09 0.13,-0.15 0.35,-0.28 0.16,-0.19 0.37,-0.73 0.17,-0.23 0.2,-0.22 0.26,-0.21 1.62,-0.46 4.03,-0.64 0.44,-0.29 0.43,-0.36 0.7,-0.86 0.88,-2.37 0.21,-0.4 0.38,-0.91 0.63,-0.8 1.99,-1.49 -0.87,-0.81 -0.2,-0.5 0,-0.62 0.3,-1.19 0.32,-0.5 0.34,-0.33 3.9,-1.51 0.2,-0.16 0.13,-0.19 0.21,-0.57 0.15,-0.32 0.7,-0.95 0.48,-0.51 -0.17,-0.34 -0.57,-0.45 -4.78,-2.18 -0.6,-0.17 -1.56,-0.19 -0.28,-0.1 -0.18,-0.15 0.03,-0.31 0.11,-0.23 1.08,-0.61 0.39,-0.11 0.18,-0.09 0.21,-0.13 0.15,-0.18 0.12,-0.23 0.06,-0.31 0.01,-0.38 -0.05,-0.37 -0.13,-0.41 -0.24,-0.39 -0.49,-0.38 -0.45,-0.14 -0.49,-0.04 -2.13,0.11 -3.38,-0.31 -0.33,-0.13 -0.34,-0.18 -0.31,-0.25 -0.27,-0.35 -0.12,-0.5 -0.01,-0.72 0.31,-0.94 0.24,-0.58 0.25,-0.44 0.24,-0.33 0.25,-0.25 0.3,-0.19 0.31,-0.12 1.3,-0.14 0.27,-0.07 0.24,-0.12 0.84,-0.67 0.17,-0.25 0.09,-0.35 0.06,-1.02 -0.11,-0.45 -0.28,-0.42 -1.45,-1.22 -0.51,-0.73 -0.48,-1.64 0.25,-0.64 0.17,-0.23 0.48,-0.24 0.4,-0.14 2.7,-0.37 4.25,-0.86 0.31,-0.13 1.37,-0.84 0.29,-0.1 0.29,-0.04 0.45,0.07 0.55,0.17 0.93,0.41 0.39,0.32 0.24,0.3 0.23,0.23 0.4,0.12 1.49,0.14 0.93,0.28 0.8,0.12 1.39,0.04 1.83,-0.25 1.13,0.2 8.92,2.53 2.98,0.34 1.09,-2.32 0.25,-0.74 0.2,-0.3 0.95,-1.09 0.32,-0.49 0.33,-0.36 0.35,-0.22 0.56,-0.13 0.42,-0.01 0.36,0.1 0.54,0.31 0.39,-0.16 0.53,-0.44 1.01,-1.1 0.7,-1.16 1.37,-1.03 5.59,-2.91 -0.57,-0.48 -0.92,-0.52 -0.12,-0.18 -0.12,-0.31 0.25,-1.81 0.06,-1.76 -0.15,-1.32 -0.04,-1.51 -0.63,-1.74 -2.55,-2.75 -0.3,0.14 -0.21,0.03 -0.24,0 -0.23,-0.09 -1.7,-1.45 -0.32,-0.17 -0.35,-0.08 -0.34,0 -1.13,0.27 -0.34,0.04 -0.28,-0.03 -0.22,-0.12 -0.18,-0.16 -0.15,-0.17 -0.16,-0.11 -1.48,-0.45 -0.25,-0.18 -0.17,-0.26 -0.12,-0.46 -0.21,-1.99 0,-0.35 0.03,-0.37 0,-0.35 -0.04,-0.35 -0.09,-0.32 -0.12,-0.3 -0.07,-0.32 0,-0.35 0.22,-1.29 0.11,-0.39 0.27,-0.53 0.24,-0.35 0.34,-0.37 0.29,-0.25 1.24,-0.68 0.32,-0.69 0.13,-0.54 0.34,-0.74 0.14,-0.47 0.27,-1.72 0.17,-0.63 0.19,-0.48 0.2,-0.27 0.18,-0.35 0.04,-0.4 -0.38,-1.21 -0.02,-0.53 0.17,-0.63 0.37,-0.84 0.06,-0.44 -0.06,-0.49 -0.73,-2.01 -0.19,-1.04 0.13,-1.53 -4.26,0.93 -1.95,-0.11 -4.82,0.73 -0.39,-0.05 -0.39,-0.23 -0.26,-0.25 -0.4,-0.58 -0.24,-0.23 -0.36,-0.1 -0.48,0.02 -2.64,0.89 -0.52,0.04 z\"\n        />\n      <path\n        id=\"AF-HER\"\n        title=\"Hirat\"\n        \n        d=\"m 42.797153,175.00119 -0.6,-0.53 0.41,-0.67 0.04,-0.16 -0.45,-0.41 -0.36,-0.18 -0.41,0 -2.54,0.49 -0.66,0.25 0.35,0.55 0.52,2.45 0.12,1.2 -0.31,1.24 -0.64,1.12 -1.14,1.39 -0.54,1.15 -0.42,1.87 -0.26,0.6 -1.02,1.38 -0.28,0.66 -0.23,1.22 0.23,3.06 -0.39,1.05 -0.93,0.54 -1.16,0.19 -1.03,0.03 -0.7,0.57 -0.28,1.27 -0.04,1.27 0.03,0.57 0.27,0.26 0.24,0.57 -0.02,0.57 -0.49,0.26 -0.29,0.27 0.25,0.59 1.73,2.33 0.13,0.25 -0.09,0.68 -0.13,0.52 0.07,0.49 0.51,0.54 -1.17,1.65 -0.35,0.85 -0.29,2.45 -0.71,2.22 -0.16,1.16 -0.15,0.57 -0.69,1.11 -0.15,0.74 0.05,0.61 0.21,1.1 0.04,0.51 -0.21,2.09 -0.39,1.95 -1.49,0.5 -0.27,0.97 -0.94,1.7 -1.54,1.2 -0.88,1.03 -0.06,1.39 0.16,1.53 -0.19,1.43 -0.51,0.68 -0.56,0.13 -0.63,-0.05 -0.68,0.11 -0.65,0.38 -0.32,0.39 -0.24,0.47 -2.21,2.52 -0.94,0.65 -1.21,0.34 -2.66,0.36 -1.21,0.62 -0.71,1.24 0.52,0.07 0.41,0.24 0.32,0.4 0.41,1.41 0.43,0.41 1.19,0.54 0.59,0.54 0.47,0.68 0.77,1.53 3.59,4.69 0.56,1.1 -3.62,0.21 -4.93,0.31 -3.4199997,0.2 0.33,1.25 -0.76,0.83 -2.3,1.23 -1.7,1.77 -1.53,1.99 -1.36000003,2.76 -0.29,2.62 0.08,0.79 0.53,5.07 1.39000003,8.93 -0.11,2.29 -1.50000003,3.39 -0.39,1.91 1.21000003,4.25 3.06,2.95 3.91,1.62 3.7999997,0.3 3.57,-0.19 1.87,0.13 1.63,0.42 0.75,0.44 0.82,0.73 0.43,0.84 -0.05,0.09 0.97,0.11 1.71,0.2 2.77,1.21 6.42,1.45 3.29,0.27 6.99,-0.75 0.89,0 0.56,0.14 1.01,0.45 0.52,0.05 0.66,-0.06 1.33,-0.46 0.57,-0.35 0.41,-0.4 0.31,-0.56 0.24,-0.57 0.34,-0.44 0.5,-0.28 1.09,-0.21 2.22,0.1 2.31,0.48 0.66,0.74 0.88,0.07 1.48,-0.1 3.65,0.27 3.21,0.74 0.47,0.39 0.1,0.41 -0.01,0.6 -0.07,0.48 -0.17,0.43 -0.22,0.31 -0.7,0.68 -0.18,0.27 -0.12,0.38 -0.05,0.5 -0.02,2.48 -0.08,0.46 -0.13,0.38 -0.25,0.39 -0.88,0.91 -0.7,0.52 -0.24,0.25 -0.4,0.61 -0.22,0.25 -0.45,0.41 -0.15,0.26 -0.01,0.52 0.23,0.47 1.9,2.09 2.19,3.52 -0.04,0.64 -0.46,0.6 -1.33,1.02 -0.47,0.43 -0.31,0.36 -0.53,0.75 -1.63,1.66 -0.64,0.51 -0.22,0.28 -0.17,0.32 -0.13,0.37 -0.14,0.28 -0.2,0.25 -0.28,0.24 -1.08,0.66 -0.28,0.26 -0.21,0.33 -0.28,0.88 -0.27,1.89 -0.06,2.05 0.1,0.94 0.3,0.68 0.39,0.29 0.49,0.21 0.52,0.16 2.89,0.11 0.5,0.11 0.36,0.21 0.32,0.42 0.51,0.91 1.8,1.84 0.41,0.33 0.24,0.15 0.22,0.08 0.24,0.02 0.32,-0.06 0.39,-0.2 0.31,-0.23 0.53,-0.57 1.28,-1.47 2.46,-1.81 0.79,-0.35 0.4,-0.02 0.3,0.05 0.16,0.11 0.1,0.16 0.1,1.54 0.12,0.7 0.28,0.84 0.03,0.22 0,0.22 -0.03,0.25 0.01,0.21 0.04,0.24 0.08,0.19 0.29,0.45 0.11,0.21 0.17,0.51 0.13,0.26 0.2,0.29 0.29,0.13 0.33,0.07 0.71,0.07 0.68,-0.07 0.41,-0.17 0.72,-0.39 1.47,-1.36 0.36,-0.26 0.77,-0.31 1.42,-0.31 3.97,-0.41 1.84,-0.36 1.1,0.04 0.94,0.15 0.34,-0.06 0.17,-0.13 0,-0.15 -0.14,-0.89 0.04,-0.37 0.18,-0.49 1.13,-2.44 0.11,-0.42 0.03,-0.29 -0.02,-0.31 -0.14,-0.63 -0.03,-0.47 0.06,-0.62 0.29,-0.94 0.32,-0.48 0.31,-0.29 0.78,-0.49 1.25,-1.04 1.42,-0.96 1.99,-1.01 0.63,-0.21 0.4,-0.06 0.29,-0.01 0.22,0.06 0.219997,0.08 1.29,0.7 0.3,0.08 0.37,0.05 0.41,-0.01 2.5,-0.48 1.97,-0.66 0.73,-0.42 0.54,-0.46 0.26,-0.41 0.18,-0.44 0.12,-0.54 0.32,-0.57 0.29,-0.27 0.26,-0.11 1.92,0.13 0.64,-0.07 0.73,-0.16 4.03,-1.43 1.18,-0.21 0.15,0.01 0.06,0.08 -0.03,0.14 -0.12,0.42 -0.04,0.19 0,0.18 0.03,0.14 0.07,0.11 0.06,0.06 0.15,0.07 0.17,0.04 4.24,-1.59 1.3,-1.31 0.12,-0.22 0.13,-0.37 -0.02,-0.27 0.12,-0.74 0.29,-1.04 0.84,-2.3 0.54,-1.15 0.46,-0.75 0.37,-0.28 1.1,-0.52 0.84,-0.65 -0.3,-1.06 -0.19,-2.18 0,-1.13 -0.25,-0.97 -0.34,-0.8 -0.99,-1.37 -0.34,-0.67 -0.14,-0.62 0.23,-0.69 0.04,-0.38 -0.12,-0.47 -0.19,-0.33 -0.21,-0.26 -0.45,-0.4 -0.09,-0.19 -0.03,-0.21 0.05,-0.36 0.14,-0.31 0.66,-0.69 1.97,-1.49 1.95,-0.87 0.82,-0.19 0.67,-0.29 0.8,-0.55 0.26,-0.27 0.15,-0.21 0.04,-0.09 0.76,-0.34 5.44,-1.52 4.69,-0.44 1.32,-0.44 1.11,-0.8 0.72,-1.45 0.04,-0.15 0,-0.33 -0.08,-0.55 -0.65,-2 -0.05,-0.29 0.05,-0.3 0.18,-0.39 0.19,-0.25 0.2,-0.18 0.2,-0.14 0.21,-0.07 0.84,-0.08 0.42,-0.1 0.46,-0.19 0.56,-0.3 0.46,-0.31 0.27,-0.46 -0.04,-0.5 -0.51,-0.83 -1.37,-1.41 -0.25,-0.43 -0.68,-2.47 -0.39,-0.77 -0.6,-0.88 -0.92,-1.11 -1.54,-1.29 -0.36,-0.37 -0.22,-0.43 -0.17,-0.51 -0.2,-0.92 -0.27,-0.64 -1.95,-3.3 -0.07,-0.59 0.22,-0.65 2.84,-4.25 1.32,-2.53 0.8,-3.38 6.67,-0.27 0.42,-0.06 0.82,-0.03 4.12,0.36 0.76,0.21 1.42,0.57 0.8,0.48 0.66,0.51 0.84,0.83 0.79,0.25 6.15,0.12 0.67,-0.35 0.31,-0.66 0.36,-0.58 0.09,-0.04 0.14,0.02 2.18,1.13 0.37,0.12 0.33,0.05 0.3,-0.01 0.22,-0.03 0.19,-0.06 0.15,-0.03 1.18,0.05 0.36,-0.06 0.4,-0.22 0.47,-0.38 0.72,-0.8 0.68,-0.42 0.49,-0.16 0.65,-0.05 0.33,-0.24 0.31,-0.43 0.35,-0.92 0.07,-0.7 -0.08,-0.54 -0.16,-0.4 -0.23,-0.36 -0.41,-0.55 -0.14,-0.23 -0.04,-0.33 0.1,-0.38 0.25,-0.66 0.03,-0.42 -0.06,-0.52 -0.09,-0.44 -0.04,-0.47 0.03,-0.41 0.13,-0.78 0.02,-0.3 -0.22,-1.69 0,-0.52 0.04,-0.43 0.08,-0.34 0.19,-0.26 0.32,-0.23 0.69,-0.27 0.45,-0.07 0.58,0.07 0.73,0.17 1,0.09 0.62,-0.06 0.64,-0.16 0.56,-0.36 0.31,-0.34 0.36,-0.24 0.52,-0.18 1.06,-0.11 0.86,-0.44 0.99,-0.67 0.35,-0.17 0.35,0.06 0.23,0.12 0.45,0.9 0.25,0.41 0.18,0.18 0.24,0.1 0.84,0.07 0.64,0.16 0.2,-0.03 0.16,-0.09 0.19,-0.24 0.04,-0.48 -0.11,-0.82 -0.74,-1.72 -0.17,-0.53 -0.09,-0.39 0.02,-1.58 0.79,-5.02 -5.37,0.39 -0.33,-0.06 -1.26,-0.55 -0.56,-0.18 -0.58,-0.1 -4.75,-0.08 -0.46,0.05 -0.59,0.22 -0.71,0.36 -1.21,0.84 -0.51,0.52 -0.3,0.43 -0.25,1.03 -0.09,0.18 -0.1,0.14 -0.25,0.17 -0.31,0.11 -1.14,0.16 -0.5,0.18 -3.26,2.62 -10.01,3.01 -0.96,0.1 -0.41,-0.12 -0.61,-0.44 -0.4,-0.37 -1.43,-0.81 -3.71,-1.09 -0.74,0.35 -0.79,0.23 -0.53,-0.01 -2.8,-0.48 -0.88,-0.28 -2,-1.08 -0.49,-0.4 -0.7,-0.72 -1.61,-0.87 -3.52,-1.29 -4.06,-0.46 -1.57,-0.14 -0.54,0.11 -0.41,0.25 -1.73,1.5 -0.76,0.16 -3.03,-1.46 -1.56,-0.32 -1.42,-0.28 -0.65,0.02 -0.09,-0.02 -0.08,-0.05 -0.05,-0.15 0.07,-1.62 -0.33,-0.84 -0.25,-0.39 -0.18,-0.16 -0.15,-0.17 -0.73,-1.18 -0.22,-0.2 -0.24,-0.08 -0.91,0.11 -1.83,-0.01 -0.41,-0.12 -0.59,-0.23 -0.64,-0.4 -0.54,-0.42 -0.43,-0.45 -0.33,-0.28 -0.31,-0.18 -1.38,-0.39 -0.36,-0.15 -0.51,-0.29 -1.28,-1.01 -1.68,-1.64 -0.39,-0.27 -0.31,-0.1 -0.22,-0.01 -0.31,0.06 -0.44,0.02 -0.36,-0.03 -2.19,-0.93 -0.16,-0.12 -0.14,-0.21 -0.17,-0.38 -0.34,-1.09 -0.11,-0.61 -0.05,-0.7 0.61,-6.63 0.36,-2.13 0.88,-2.97 0.49,-1.15 4.08,-6.85 1.88,-5.47 0.01,-0.5 -3.04,1.49 -1.27,1.29 -0.61,0.85 -0.63,0.62 -0.73,0.45 -3.52,1.53 -0.8,0.19 -0.43,-0.07 -1.43,-0.6 -0.52,0.02 -0.48,0.14 -0.489997,0.04 -0.53,-0.29 -3.18,-2.95 -1.31,0.07 -1.58,1.47 -4.72,6.44 -0.77,0.4 -0.51,-0.61 -0.91,-2.14 -0.29,-0.91 0.08,-2.4 -0.07,-1.04 -0.33,-1.05 -0.52,-0.94 -0.71,-0.74 -2.67,-1.18 -1.34,-0.9 -4.97,-4.92 -1.28,-0.89 -1.44,-0.71 -2.83,-0.06 -5.84,2.05 -2.88,0.22 -6.54,-1 -3.2,-1.3 -2.23,-2.49 -3.16,-2.9 -0.4,-0.18 -0.33,-0.15 -0.55,-0.09 -0.39,-0.22 -0.31,-0.35 -0.32,-0.47 -0.01,-0.24 0.19,-0.64 -0.02,-0.26 -0.77,-0.69 -0.11,-0.03 -0.07,0.02 z\"\n        />\n      <path\n        id=\"AF-BDG\"\n        title=\"Badghis\"\n        \n        d=\"m 185.50715,186.97119 -0.39,-0.07 -0.52,-0.18 -1.77,-1.04 0.03,-0.46 0.33,-0.44 0.06,-0.12 0.11,-0.27 0.01,-0.29 -0.06,-0.34 -0.15,-0.36 -0.21,-0.36 -0.31,-0.31 -2.96,-0.56 -7.71,-0.55 -0.52,-0.28 -0.24,-0.17 -0.24,-0.2 -0.19,-0.22 -0.07,-0.23 0,-0.33 0.14,-0.59 0.06,-0.5 0.02,-0.58 -0.22,-4.18 -0.2,-1 -0.41,-1.19 -0.65,-1.16 -0.58,-0.74 -0.83,-0.83 -0.47,-0.35 -0.46,-0.26 -0.64,-0.26 -0.55,-0.1 -0.34,-0.1 -0.3,-0.19 -0.09,-0.46 0.11,-0.67 0.52,-1.45 0.71,-1.15 0.07,-0.29 -0.07,-1.25 0.12,-1.05 0.42,-1.28 0.85,-3.87 0.18,-2.35 -0.03,-2.63 -0.54,-1.8 -1.42,-0.84 -0.8,0.13 -1.45,0.45 -4.56,2.68 -1.95,0.51 -5.99,0.37 -0.98,0.22 -0.9,0.5 -0.67,0.7 -1.15,1.57 -0.72,0.65 -3.92,1.85 -4.24,0.88 -10.55,-0.24 -1.03,0.23 -0.75,0.78 -0.21,1.04 0.34,0.76 0.48,0.76 0.22,1.06 0.31,0.78 2.48,2.25 0.68,0.92 0.32,0.27 1.23,0.47 0.25,0.31 -0.04,0.41 -0.29,0.55 -1.34,1 -3.73,1.22 -1.28,1.27 -0.08,0.89 0.11,3.04 0.16,0.73 0.81,0.66 0.17,0.63 -0.5,1.87 -0.21,2.25 -0.29,1.11 -0.65,0.68 -0.96,0.26 -2.01,0.17 -0.98,0.23 -3.26,1.57 -4.39,3.5 -0.22,0.11 -0.01,0.5 -1.88,5.47 -4.08,6.85 -0.49,1.15 -0.88,2.97 -0.36,2.13 -0.61,6.63 0.05,0.7 0.11,0.61 0.34,1.09 0.17,0.38 0.14,0.21 0.16,0.12 2.19,0.93 0.36,0.03 0.44,-0.02 0.31,-0.06 0.22,0.01 0.31,0.1 0.39,0.27 1.68,1.64 1.28,1.01 0.51,0.29 0.36,0.15 1.38,0.39 0.31,0.18 0.33,0.28 0.43,0.45 0.54,0.42 0.64,0.4 0.59,0.23 0.41,0.12 1.83,0.01 0.91,-0.11 0.24,0.08 0.22,0.2 0.73,1.18 0.15,0.17 0.18,0.16 0.25,0.39 0.33,0.84 -0.07,1.62 0.05,0.15 0.08,0.05 0.09,0.02 0.65,-0.02 1.42,0.28 1.56,0.32 3.03,1.46 0.76,-0.16 1.73,-1.5 0.41,-0.25 0.54,-0.11 1.57,0.14 4.06,0.46 3.52,1.29 1.61,0.87 0.7,0.72 0.49,0.4 2,1.08 0.88,0.28 2.8,0.48 0.53,0.01 0.79,-0.23 0.74,-0.35 3.71,1.09 1.43,0.81 0.4,0.37 0.61,0.44 0.41,0.12 0.96,-0.1 10.01,-3.01 3.26,-2.62 0.5,-0.18 1.14,-0.16 0.31,-0.11 0.25,-0.17 0.1,-0.14 0.09,-0.18 0.25,-1.03 0.3,-0.43 0.51,-0.52 1.21,-0.84 0.71,-0.36 0.59,-0.22 0.46,-0.05 4.75,0.08 0.58,0.1 0.56,0.18 1.26,0.55 0.33,0.06 5.37,-0.39 0.18,-0.62 0.11,-0.25 0.28,-0.37 0.4,-0.47 0.61,-0.33 1,-0.31 5.94,0.24 0.78,0.4 0.4,0.15 0.69,0.04 0.39,0.14 0.5,0 0.59,-0.17 3.21,-1.61 8.65,-2.39 1.48,-0.72 0.32,-0.4 0.18,-0.1 0.22,-0.19 0.17,-0.25 0.48,-1.08 0.24,-0.78 0.3,-1.47 0.04,-0.49 -0.09,-1.05 -0.01,-0.55 0.08,-0.62 0.47,-2 0.09,-0.6 -0.13,-0.64 -0.33,-0.69 -0.83,-1.19 -0.39,-0.72 -0.22,-0.64 -0.07,-0.52 -0.36,-0.7 -0.64,-0.81 -2.98,-2.14 -0.39,-0.4 -1.2,-1.78 -0.7,-0.83 -0.46,-0.44 -0.49,-0.35 -0.6,-0.18 -1.21,0.11 -0.31,-0.06 -0.32,-0.14 -2.07,-1.33 -0.72,-0.72 -0.97,-2.04 -0.52,0.24 -1.14,0.25 -1.62,0.06 -1.44,0.26 -0.9,0.01 -4.96,-0.68 -4.63,-1.54 -0.27,-0.17 -0.14,-0.28 -0.11,-0.77 0.03,-0.99 0.26,-2.17 -0.03,-0.93 0.04,-0.59 0.46,-1.81 0.18,-1.33 0.01,-0.76 -0.06,-0.58 -0.4,-0.37 -2.12,0.31 -1.76,0.58 -0.54,0.05 -0.89,0.27 z\"\n        />\n    </svg>\n\n  `,\n  france: `\n    <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 200 180\" class=\"dmb-country-map\" data-map-name=\"france\">\n      <path d=\"M60 20 L120 30 L150 70 L135 130 L90 160 L40 135 L25 85 Z\"></path>\n    </svg>\n  `\n};\n\n(function () {\n  const MODE_KEY = \"dmb_mode\";\n  const COVER_KEY = \"dmb_selected_cover\";\n\n  function setStoredMode(mode) {\n    try {\n      if (!mode) localStorage.removeItem(MODE_KEY);\n      else localStorage.setItem(MODE_KEY, mode);\n    } catch {}\n  }\n\n  function clearCoverState() {\n    try { localStorage.removeItem(COVER_KEY); } catch {}\n    try { sessionStorage.removeItem(COVER_KEY); } catch {}\n  }\n\n  function onCustomizeClick(e) {\n    if (e) {\n      e.preventDefault();\n      e.stopPropagation();\n      if (e.stopImmediatePropagation) e.stopImmediatePropagation();\n    }\n\n    clearCoverState();\n    setStoredMode(\"customize\");\n    // Start a BRAND-NEW custom book: mint a fresh design id and carry it in the URL\n    // so this book has its OWN save key. Without this, every \"from scratch\" book\n    // reused one shared id (\"dmb_active_design_book\") \u2014 so a new book offered the\n    // PREVIOUS book's saved design + photos, and a stale local copy survived even\n    // after deleting it from Saved Designs. Now a new book restores nothing (empty\n    // key); only opening a SPECIFIC saved design (?design=<id>) offers a restore.\n    var _newId = \"d\" + Date.now().toString(36) + Math.random().toString(36).slice(2, 6);\n    try { localStorage.setItem(\"dmb_active_design_book\", _newId); } catch (err) {}\n    window.location.assign(\"/pages/design-my-book?step=upload&design=\" + encodeURIComponent(_newId));\n    return false;\n  }\n\n  function onBrowseClick(e) {\n    if (e) {\n      e.preventDefault();\n      e.stopPropagation();\n      if (e.stopImmediatePropagation) e.stopImmediatePropagation();\n    }\n\n    setStoredMode(\"browse\");\n    window.location.assign(\"/collections\");\n    return false;\n  }\n\n  function hookButtonsDirect() {\n    const browseBtn =\n      document.getElementById(\"browseCollectionBtn\") ||\n      document.querySelector('#step1 a[href*=\"/collections\"]');\n\n    const customizeBtn =\n      document.getElementById(\"customizeOrderBtn\") ||\n      document.querySelector('#step1 a[href*=\"mode=customize\"]');\n\n    if (browseBtn) browseBtn.addEventListener(\"click\", onBrowseClick, true);\n    if (customizeBtn) customizeBtn.addEventListener(\"click\", onCustomizeClick, true);\n  }\n\n  function enforceUrlWinsOverStorage() {\n    try {\n      const qs = new URLSearchParams(window.location.search || \"\");\n      const urlMode = qs.get(\"mode\");\n      if (urlMode) setStoredMode(urlMode);\n    } catch {}\n  }\n\n  function enforceCustomizeClearsCover() {\n    try {\n      const qs = new URLSearchParams(window.location.search || \"\");\n      if (qs.get(\"mode\") === \"customize\") clearCoverState();\n    } catch {}\n  }\n\n  window.addEventListener(\"pageshow\", (e) => {\n    if (e.persisted) {\n      hookButtonsDirect();\n      enforceUrlWinsOverStorage();\n      enforceCustomizeClearsCover();\n    }\n  });\n\n  function renderBuilderCanvas() {\n    if (builderMode === \"cover\") {\n      renderCoverCanvas();\n    } else {\n      renderSpread();\n    }\n    syncBuilderModeButtons();\n    setCanvasZoom(currentZoom);\n  }\n\n  function openCoverMode() {\n    builderMode = \"cover\";\n    syncBuilderModeButtons();\n    renderBuilderCanvas();\n    activateLeftTab(\"cover\");\n  }\n\n  function initDefaultBuilderPreview() {\n    if (!builderStep) return;\n\n    initUnifiedTabs();\n    initCoverDesigner();\n\n    builderMode = \"cover\";\n    syncBuilderModeButtons();\n    renderCoverCanvas();\n    renderThumbsRail();\n\n    if (bookMeta) {\n      const selectedPages = Number(pagesEl?.value || 0);\n      if (selectedPages > 0) {\n        bookMeta.textContent = `${selectedPages} pages (${Math.max(0, selectedPages - 1)} to design) \u2022 ${editor.photos.length} photos`;\n      } else {\n        bookMeta.textContent = \"Start by choosing pages, photos, and customizing your cover.\";\n      }\n    }\n  }\n\n  function getObjectListForTarget(targetType, pageNumber, zone) {\n    if (targetType === \"cover\") {\n      if (!Array.isArray(editor.coverObjects)) migrateCoverObjectsToFlat();\n      return editor.coverObjects;\n    }\n\n    if (!editor.pageObjects || typeof editor.pageObjects !== \"object\") {\n      editor.pageObjects = {};\n    }\n\n    // WARNING: Do NOT use `if (!p)` here \u2014 Number(0) = 0 and !0 = true,\n    // which would silently return [] for page 0 (the special inner-cover page),\n    // making all maps/stickers/text on page 0 invisible in the builder.\n    if (pageNumber == null || !Number.isFinite(Number(pageNumber))) return [];\n    const p = Number(pageNumber);\n\n    if (!Array.isArray(editor.pageObjects[p])) {\n      editor.pageObjects[p] = [];\n    }\n\n    return editor.pageObjects[p];\n  }\n\n  function renderFreeFrameObjects(container, meta) {\n    const targetType = meta?.targetType || \"page\";\n    const pageNumber = meta?.pageNumber ?? null;\n    const zone = meta?.zone || null;\n    // Pages: z >= 60 keeps frames above the spread overlay (z:50).\n    // Cover: use own z-value \u2014 drops handled by peek-under method, not z-index.\n    const minFrameZ = targetType === \"cover\" ? 0 : 60;\n\n    const rawObjs = getObjectListForTarget(targetType, pageNumber, zone);\n\n    const objs = Array.isArray(rawObjs)\n      ? rawObjs.slice().sort((a, b) => Number(a.z || 0) - Number(b.z || 0))\n      : [];\n\n    objs.forEach((obj) => {\n      if (obj.type === \"text\") {\n        // Skip empty text \u2014 cleared text boxes are removed from state in\n        // savePageTextState/saveCoverTextNodeState; this is a safety net so stale\n        // empty objects left in older saved sessions never reappear after a re-render.\n        if (!obj.text || !obj.text.trim()) return;\n        const txt = document.createElement(\"div\");\n        txt.id = obj.id;\n        txt.textContent = obj.text || \"\";\n        txt.classList.add(\"dmb-editable-text\");\n        txt.style.position = \"absolute\";\n        txt.style.left = obj.left || \"40px\";\n        txt.style.top = obj.top || \"40px\";\n        if (obj.width) {\n          txt.style.width = obj.width;\n          // An explicit width outranks .dmb-editable-text's min-width:120px, which\n          // exists only so a brand-new EMPTY box is big enough to tap. Without this\n          // release, a box narrowed below 120px with the side grip snapped back wide\n          // on the next re-render. (2026-09-14)\n          txt.style.minWidth = \"0\";\n        }\n        txt.style.fontSize = obj.fontSize || \"28px\";\n        txt.style.fontWeight = obj.fontWeight || \"800\";\n        txt.style.color = obj.color || \"#666\";\n        txt.style.fontFamily = obj.fontFamily || \"'Londrina Solid', sans-serif\";\n        if (obj.textAlign) txt.style.textAlign = obj.textAlign;\n        if (obj.textDecoration) txt.style.textDecoration = obj.textDecoration;\n        if (obj.whiteSpace) txt.style.whiteSpace = obj.whiteSpace;\n        if (obj.transform) txt.style.transform = obj.transform;\n        if (obj.rotate) txt.dataset.rotate = obj.rotate;\n        txt.dataset.z = obj.z || String(Date.now());\n        txt.style.zIndex = txt.dataset.z;\n        txt.dataset.pageNumber = String(pageNumber);\n        txt.style.padding = \"2px 6px\";\n        txt.style.background = \"transparent\";\n        txt.style.outline = \"none\";\n        attachTextNodeHandlers(txt);\n        container.appendChild(txt);\n        return;\n      }\n\n      if (obj.type !== \"photo-frame\") return;\n\n      const frame = document.createElement(\"div\");\n      frame.className = \"dmb-photo-frame-object\";\n      frame.dataset.objectId = obj.id;\n      frame.style.left = `${obj.x}px`;\n      frame.style.top = `${obj.y}px`;\n      frame.style.width = `${obj.w}px`;\n      frame.style.height = `${obj.h}px`;\n      const z = obj.z || 10;\n      // On pages: z >= 60 (above spread overlay z:50). On cover: z >= 100001 (above cover drag overlay z:100000).\n      frame.style.zIndex = String(Math.max(z, minFrameZ));\n      frame.dataset.z = String(z);\n      if (obj.rotation) frame.style.transform = `rotate(${obj.rotation}deg)`;\n      if (obj.coverZone) dmbApplyZoneClip(frame, obj);\n\n      // \u2500\u2500 Polaroid frame styling: white mat + thicker bottom + light shadow \u2500\u2500\n      const _isPoly = !!obj.polaroid;\n      let _polyInsetCss = null;\n      if (_isPoly) {\n        const _pi = dmbPolaroidInsetVals(obj.w, obj.h);\n        _polyInsetCss = `${_pi.top}px ${_pi.right}px ${_pi.bottom}px ${_pi.left}px`;\n        frame.classList.add(\"dmb-polaroid\");\n        frame.style.background = \"#ffffff\";\n        frame.style.borderRadius = \"2px\";\n        frame.style.boxShadow = (obj.shadow !== false) ? \"0 6px 16px rgba(0,0,0,0.18)\" : \"none\";\n      }\n\n      if (obj.photoId) {\n        const ph = getPhotoById(obj.photoId);\n        if (ph) {\n          // Overflow visible so buttons escape; inner clip keeps photo contained\n          frame.style.overflow = \"visible\";\n          const photoClip = document.createElement(\"div\");\n          photoClip.style.cssText = `position:absolute;inset:${_polyInsetCss || \"0\"};overflow:hidden;${_isPoly ? \"\" : \"border-radius:inherit;\"}pointer-events:none;`;\n          const img = document.createElement(\"img\");\n          img.src = ph.url; img.alt = \"\"; img.draggable = false;\n          img.style.cssText = \"position:absolute;inset:0;width:100%;height:100%;object-fit:cover;pointer-events:none;\";\n          if (obj.frameShape) applyDmbFrameShape(img, obj.frameShape);\n          photoClip.appendChild(img);\n          frame.appendChild(photoClip);\n\n          // Adaptive button layout based on frame width\n          const _btnSVGs = [\n            { aria:\"Remove photo\",  title:\"Remove photo\",  svg:`<svg viewBox=\"0 0 14 14\" width=\"13\" height=\"13\" aria-hidden=\"true\"><path d=\"M2 2l10 10M12 2L2 12\" stroke=\"#fff\" stroke-width=\"2.2\" stroke-linecap=\"round\"/></svg>`, fn:(e)=>{ e.preventDefault();e.stopPropagation();pushUndoState(\"Remove Photo\");obj.photoId=null;if(builderMode===\"cover\")renderCoverCanvas();else renderSpread();renderThumbsRail();renderPhotoTray(); }},\n            { aria:\"Rotate\",        title:\"Rotate 90\u00B0 anti-clockwise\", svg:`<svg viewBox=\"0 0 24 24\" width=\"13\" height=\"13\" aria-hidden=\"true\"><path d=\"M12 4a8 8 0 1 0 7.2 4.5\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\"/><path d=\"M19.5 4.5v5h-5\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>`, fn:(e)=>{ e.preventDefault();e.stopPropagation();pushUndoState(\"Rotate Photo\");obj.rotation=((obj.rotation||0)-90+360)%360;if(builderMode===\"cover\")renderCoverCanvas();else renderSpread();renderThumbsRail(); }},\n            { aria:\"Crop\",          title:\"Crop photo\",    svg:`<svg viewBox=\"0 0 24 24\" width=\"13\" height=\"13\" aria-hidden=\"true\"><path d=\"M7 3v11a3 3 0 0 0 3 3h11\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\"/><path d=\"M17 21V10a3 3 0 0 0-3-3H3\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\"/></svg>`, fn:(e)=>{ e.preventDefault();e.stopPropagation();openCropModal(obj.photoId, { get:()=>obj.photoId, set:(id)=>{ obj.photoId=id; } }); }},\n            { aria:\"Frame shape\",   title:\"Frame shape\",   svg:`<svg viewBox=\"0 0 22 22\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"#fff\" stroke-width=\"1.8\"><circle cx=\"11\" cy=\"11\" r=\"9\"/><polygon points=\"11,4 13,8.5 18,9 14.5,12.5 15.5,17.5 11,15 6.5,17.5 7.5,12.5 4,9 9,8.5\" fill=\"#fff\" stroke=\"none\" opacity=\"0.7\"/></svg>`, fn:(e)=>{ e.preventDefault();e.stopPropagation();showDmbFramePicker(e.currentTarget,obj.frameShape||\"\",(s)=>{ pushUndoState(\"Frame Shape\");obj.frameShape=s;applyDmbFrameShape(img,s); }); }}\n          ];\n          const _bs = \"border-radius:999px;border:none;background:rgba(0,0,0,0.72);color:#fff;cursor:pointer;display:flex;align-items:center;justify-content:center;padding:0;\";\n          const fw = obj.w || 100;\n\n          if (fw >= 130) {\n            // Wide: single row top-right\n            _btnSVGs.forEach((d, i) => {\n              const b = document.createElement(\"button\"); b.type=\"button\";\n              b.setAttribute(\"aria-label\",d.aria); b.title=d.title; b.innerHTML=d.svg;\n              b.style.cssText=`position:absolute;top:4px;right:${4+i*26}px;width:24px;height:24px;z-index:11;${_bs}`;\n              b.addEventListener(\"click\",d.fn); frame.appendChild(b);\n            });\n          } else if (fw >= 65) {\n            // Medium: 2\u00D72 grid top-right\n            _btnSVGs.forEach((d, i) => {\n              const r=Math.floor(i/2), c=i%2;\n              const b = document.createElement(\"button\"); b.type=\"button\";\n              b.setAttribute(\"aria-label\",d.aria); b.title=d.title; b.innerHTML=d.svg;\n              b.style.cssText=`position:absolute;top:${4+r*26}px;right:${4+c*26}px;width:24px;height:24px;z-index:11;${_bs}`;\n              b.addEventListener(\"click\",d.fn); frame.appendChild(b);\n            });\n          } else {\n            // Narrow: single \u22EF toggle\n            const mb = document.createElement(\"button\"); mb.type=\"button\"; mb.title=\"Options\";\n            mb.innerHTML=\"\u00B7\u00B7\u00B7\";\n            mb.style.cssText=`position:absolute;top:2px;right:2px;width:22px;height:22px;z-index:12;font-size:13px;line-height:1;${_bs}`;\n            const pop = document.createElement(\"div\");\n            pop.style.cssText=\"position:absolute;top:26px;right:0;background:rgba(20,20,20,0.92);border-radius:8px;padding:4px;display:none;flex-direction:column;gap:2px;z-index:20;min-width:110px;\";\n            _btnSVGs.forEach(d => {\n              const b = document.createElement(\"button\"); b.type=\"button\";\n              b.innerHTML=`<span style=\"display:flex;align-items:center;gap:6px;\">${d.svg}<span style=\"font-size:11px;font-weight:700;color:#fff;\">${d.aria}</span></span>`;\n              b.style.cssText=\"background:none;border:none;cursor:pointer;padding:4px 8px;border-radius:6px;width:100%;text-align:left;\";\n              b.addEventListener(\"click\",(ev)=>{ pop.style.display=\"none\"; d.fn(ev); });\n              pop.appendChild(b);\n            });\n            mb.addEventListener(\"click\",(e)=>{ e.preventDefault();e.stopPropagation(); const _open=pop.style.display===\"none\"; document.querySelectorAll(\".dmb-frame-popup-open\").forEach(p=>{p.style.display=\"none\";p.classList.remove(\"dmb-frame-popup-open\");}); if(_open){pop.style.display=\"flex\";pop.classList.add(\"dmb-frame-popup-open\");} });\n            frame.appendChild(mb); frame.appendChild(pop);\n          }\n        }\n      } else {\n        const plus = document.createElement(\"div\");\n        plus.className = \"dmb-photo-frame-plus\";\n        plus.innerHTML = '<span style=\"font-size:22px;display:block;\">+</span><span style=\"font-size:11px;opacity:.7;\">Drop photo here</span>';\n        if (_isPoly) {\n          plus.style.position = \"absolute\";\n          plus.style.inset = _polyInsetCss;\n          plus.style.background = \"#f3efe6\";\n          plus.style.display = \"flex\";\n          plus.style.flexDirection = \"column\";\n          plus.style.alignItems = \"center\";\n          plus.style.justifyContent = \"center\";\n        }\n        frame.appendChild(plus);\n      }\n\n      const handles = [\n        [\"tl\", \"0\", \"0\"],\n        [\"tm\", \"50%\", \"0\"],\n        [\"tr\", \"100%\", \"0\"],\n        [\"ml\", \"0\", \"50%\"],\n        [\"mr\", \"100%\", \"50%\"],\n        [\"bl\", \"0\", \"100%\"],\n        [\"bm\", \"50%\", \"100%\"],\n        [\"br\", \"100%\", \"100%\"]\n      ];\n\n      handles.forEach(([pos, left, top]) => {\n        const h = document.createElement(\"div\");\n        h.className = `dmb-frame-handle dmb-frame-handle-${pos}`;\n        h.dataset.handle = pos;\n        h.style.left = left;\n        h.style.top = top;\n        frame.appendChild(h);\n      });\n\n      /* \u2500\u2500 Frame rotation handle (Fix 4) \u2500\u2500 */\n      const _fRotHandle = document.createElement(\"div\");\n      _fRotHandle.className = \"dmb-map-rotate-handle dmb-map-rot-float\";\n      _fRotHandle.innerHTML = \"&#8635;\";\n      _fRotHandle.title = \"Rotate frame\";\n      _fRotHandle.style.display = \"none\";\n      document.body.appendChild(_fRotHandle);\n      frame._dmbRotHandle = _fRotHandle;\n\n      function _updateFrameRotPos() {\n        if (!frame.isConnected) { _fRotHandle.remove(); return; }\n        const _rr = frame.getBoundingClientRect();\n        _fRotHandle.style.left = (_rr.right + 14) + \"px\";\n        _fRotHandle.style.top = (_rr.bottom + 14) + \"px\";\n      }\n      requestAnimationFrame(_updateFrameRotPos);\n\n      _fRotHandle.addEventListener(\"pointerdown\", (e) => {\n        e.preventDefault(); e.stopPropagation();\n        const _startRot = obj.rotation || 0;\n        const _rr2 = frame.getBoundingClientRect();\n        const _cx = _rr2.left + _rr2.width / 2, _cy = _rr2.top + _rr2.height / 2;\n        const _startAng = Math.atan2(e.clientY - _cy, e.clientX - _cx) * 180 / Math.PI;\n        const _onMove = (ev) => {\n          const _ang = Math.atan2(ev.clientY - _cy, ev.clientX - _cx) * 180 / Math.PI;\n          obj.rotation = _dmbSnapAngle(_startRot + (_ang - _startAng));\n          frame.style.transform = `rotate(${obj.rotation}deg)`;\n          _updateFrameRotPos();\n          showRotDegTooltip(obj.rotation, ev.clientX, ev.clientY);\n        };\n        const _onUp = () => {\n          hideRotDegTooltip();\n          document.removeEventListener(\"pointermove\", _onMove);\n          document.removeEventListener(\"pointerup\", _onUp);\n          pushUndoState(\"Rotate Frame\");\n        };\n        document.addEventListener(\"pointermove\", _onMove);\n        document.addEventListener(\"pointerup\", _onUp);\n      });\n\n      frame.addEventListener(\"click\", (e) => {\n        e.stopPropagation();\n        showFrameToolbarFor(frame, obj, {\n          targetType,\n          pageNumber,\n          zone\n        });\n      });\n\n      frame.addEventListener(\"dragover\", (e) => {\n        e.preventDefault();\n        e.stopPropagation();\n        const payload = getDragPayload(e);\n        e.dataTransfer.dropEffect = (payload && payload.type === \"placed\") ? \"move\" : \"copy\";\n      });\n\n      frame.addEventListener(\"drop\", (e) => {\n        e.preventDefault();\n        e.stopPropagation();\n        const payload = getDragPayload(e);\n        if (!payload || !payload.photoId) return;\n\n        pushUndoState(\"Drop Photo In Frame\");\n        obj.photoId = payload.photoId;\n        renderBuilderCanvas();\n        renderPhotoTray();\n        renderThumbsRail();\n      });\n\n      let dragMode = null;\n      let sx = 0, sy = 0, ox = 0, oy = 0, ow = 0, oh = 0;\n\n      frame.addEventListener(\"pointerdown\", (e) => {\n        if (e.target.closest(\".dmb-frame-handle\")) return;\n        // Let action buttons (crop, remove, rotate, etc.) receive their click events\n        // normally \u2014 pointerdown.preventDefault() would suppress click for the whole\n        // pointer sequence, making inline buttons unresponsive.\n        if (e.target.closest(\"button\")) return;\n        e.preventDefault();\n        e.stopPropagation();\n\n        showFrameToolbarFor(frame, obj, {\n          targetType,\n          pageNumber,\n          zone\n        });\n\n        dragMode = \"move\";\n        sx = e.clientX;\n        sy = e.clientY;\n        ox = obj.x;\n        oy = obj.y;\n\n        const onMove = (ev) => {\n          if (dragMode !== \"move\") return;\n          obj.x = ox + (ev.clientX - sx);\n          obj.y = Math.max(0, oy + (ev.clientY - sy));\n          frame.style.left = `${obj.x}px`;\n          frame.style.top = `${obj.y}px`;\n          showFrameToolbarFor(frame, obj, {\n            targetType,\n            pageNumber,\n            zone\n          });\n        };\n\n        const onUp = () => {\n          dragMode = null;\n          document.removeEventListener(\"pointermove\", onMove);\n          document.removeEventListener(\"pointerup\", onUp);\n        };\n\n        document.addEventListener(\"pointermove\", onMove);\n        document.addEventListener(\"pointerup\", onUp);\n      });\n\n      frame.querySelectorAll(\".dmb-frame-handle\").forEach((handle) => {\n        handle.addEventListener(\"pointerdown\", (e) => {\n          e.preventDefault();\n          e.stopPropagation();\n\n          showFrameToolbarFor(frame, obj, {\n            targetType,\n            pageNumber,\n            zone\n          });\n\n          dragMode = handle.dataset.handle;\n          sx = e.clientX;\n          sy = e.clientY;\n          ox = obj.x;\n          oy = obj.y;\n          ow = obj.w;\n          oh = obj.h;\n\n          const onMove = (ev) => {\n            const dx = ev.clientX - sx;\n            const dy = ev.clientY - sy;\n\n            if (dragMode.includes(\"r\")) obj.w = Math.max(60, ow + dx);\n            if (dragMode.includes(\"b\")) obj.h = Math.max(60, oh + dy);\n\n            if (dragMode.includes(\"l\")) {\n              const nextW = Math.max(60, ow - dx);\n              const consumed = ow - nextW;\n              obj.x = ox + consumed;\n              obj.w = nextW;\n            }\n\n            if (dragMode.includes(\"t\")) {\n              const nextH = Math.max(60, oh - dy);\n              const consumed = oh - nextH;\n              obj.y = oy + consumed;\n              obj.h = nextH;\n            }\n\n            frame.style.left = `${obj.x}px`;\n            frame.style.top = `${obj.y}px`;\n            frame.style.width = `${obj.w}px`;\n            frame.style.height = `${obj.h}px`;\n            showFrameToolbarFor(frame, obj, {\n              targetType,\n              pageNumber,\n              zone\n            });\n          };\n\n          const onUp = () => {\n            dragMode = null;\n            document.removeEventListener(\"pointermove\", onMove);\n            document.removeEventListener(\"pointerup\", onUp);\n          };\n\n          document.addEventListener(\"pointermove\", onMove);\n          document.addEventListener(\"pointerup\", onUp);\n        });\n      });\n\n      container.appendChild(frame);\n    });\n  }\n  function getActiveEditablePageIndex() {\n    if (typeof builderMode !== \"undefined\" && builderMode === \"cover\") return null;\n\n    const spread = Number(editor?.currentSpread || 0);\n    const leftPage = spread * 2 + 1;\n    const rightPage = leftPage + 1;\n\n    if (leftPage >= 2 && leftPage <= editor.totalPages) return leftPage;\n    if (rightPage >= 2 && rightPage <= editor.totalPages) return rightPage;\n\n    return null;\n  }\n\n  document.addEventListener(\"DOMContentLoaded\", () => {\n    hookButtonsDirect();\n    enforceUrlWinsOverStorage();\n    enforceCustomizeClearsCover();\n  });\n\n  document.addEventListener(\n    \"click\",\n    (e) => {\n      const btn = e.target && e.target.closest ? e.target.closest(\".dmb-select-design-btn\") : null;\n      if (!btn) return;\n\n      e.preventDefault();\n      e.stopPropagation();\n      if (e.stopImmediatePropagation) e.stopImmediatePropagation();\n\n      const href = btn.getAttribute(\"data-href\") || btn.getAttribute(\"href\");\n      if (href) window.location.href = href;\n    },\n    true\n  );\n})();\n\n/* =========================\n   MAIN DMB APP\n   ========================= */\n(() => {\n  /* \u2500\u2500 STEP 2 - RENDER MODE predicate + accounting \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   * Armed ONLY by snippets/dmb-render-mode.liquid, which requires ?render=1 AND a\n   * 32-hex job token - so this can never trip on a customer URL. Every guard that\n   * uses it is one line and a no-op for customers.\n   * FUNCTION DECLARATIONS on purpose: they hoist above the eval-time IIFEs in this\n   * module (the sessionStorage write and the guest-session lifecycle both run before\n   * any branch and both need this). var state avoids a TDZ during module evaluation.\n   * The accounting object exists because the PDF engine wraps EVERY photo/sticker/\n   * logo draw in a swallowing try/catch: without it, a 404 or a CORS failure yields a\n   * \"successful\" book with blank areas and no signal at all. */\n  function _dmbRenderMode() {\n    try { return window.DMB_RENDER_MODE === true; } catch (e) { return false; }\n  }\n  function _dmbRenderLiveText() {\n    try { return !!(window.DMB_RENDER_MODE && window.__DMB_RENDER_LIVETEXT); } catch (e) { return false; }\n  }\n  var _DMB_RM = {\n    token: \"\", job: \"\", proxy: \"\", crops: \"prefer-r2\", hash: false, livetext: false,\n    exclude: null, from: -1, to: -1, part: 0, parts: 1, staged: 0, count: 0, hb: null,\n    // INCREMENTAL RENDER: fps[i] = content fingerprint of PDF page i (from the mint);\n    // reuse = indices the Worker already holds a staged image for. Those pages are\n    // NOT rendered - the loop emits a numbering placeholder and moves on.\n    fps: null, reuse: null,\n    notes: { photo_missing: [], master_miss: [], crop_fallback: [], crop_rebake: 0 },\n    fonts: { loaded: [], missing: [], system: [] },\n  };\n  function _dmbRenderNote(kind, id) {\n    try {\n      if (kind === \"crop_rebake\") { _DMB_RM.notes.crop_rebake++; return; }\n      var a = _DMB_RM.notes[kind];\n      if (a && a.length < 200 && a.indexOf(id) === -1) a.push(id);\n    } catch (e) {}\n  }\n\n  // ---------- constants ----------\n  // Base prices keyed by SIZE \u2192 page count. A4 is the original size; A5 is the\n  // cheaper size. (old = compare-at / \"was\"; new = current selling price \u2014 these\n  // mirror the Shopify variant compare_at_price / price so the builder total\n  // matches checkout.) A4 values are unchanged from before, so non-size code\n  // paths (e.g. magazine, which stays \"A4\") behave exactly as they used to.\n  const PRICES = {\n    A4: {\n      \"24\": { old: 50,    new: 35 },\n      \"30\": { old: 55,    new: 38.50 },\n      \"40\": { old: 65,    new: 45.50 },\n      \"50\": { old: 75,    new: 52.50 },\n      \"76\": { old: 93.50, new: 65.45 },\n      \"100\":{ old: 120,   new: 84 }\n    },\n    A5: {\n      \"24\": { old: 47.50, new: 33.25 },\n      \"30\": { old: 52.50, new: 36.75 },\n      \"40\": { old: 62.50, new: 43.75 },\n      \"50\": { old: 72.50, new: 50.75 },\n      \"76\": { old: 85,    new: 59.50 },\n      \"100\":{ old: 100,   new: 70 }\n    }\n  };\n\n  // \u2500\u2500 Extra spreads pricing \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // Each extra spread (2 pages) is priced by the \"spread\" product. The base price\n  // comes from PRICES above; extra spread cost is added on top in updatePrice()\n  // and in addDesignedBookToCart() (via DMB_EXTRA_SPREAD_VARIANT_ID line items).\n  // Price comes LIVE from window.DMB_EXTRA_SPREAD_PRICE (exposed by the Liquid from\n  // the product's real price), so editing that product updates every label. Fallback\n  // 1.99 only if the product/price can't be read.\n  const EXTRA_SPREAD_PRICE = (typeof window.DMB_EXTRA_SPREAD_PRICE === \"number\" && window.DMB_EXTRA_SPREAD_PRICE > 0)\n    ? window.DMB_EXTRA_SPREAD_PRICE\n    : 1.99;\n\n  const PHOTO_LIMITS = {\n    \"24\": { min: 23, max: 32 },\n    \"30\": { min: 29, max: 38 },\n    \"40\": { min: 39, max: 48 },\n    \"50\": { min: 49, max: 58 },\n    \"76\": { min: 75, max: 84 },\n    \"100\":{ min: 99, max: 108 }\n  };\n\n  // \u2500\u2500 Book variants (pages \u00D7 size \u2192 Shopify variant id) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // Fallback map (the original hardcoded ids). The liquid emits the LIVE list\n  // (window.DMB_BOOK_VARIANTS) straight from the \"custom-photo-book\" product, and\n  // _dmbIngestBookVariants() below merges it in \u2014 so ADDING a page-count or size\n  // variant in Shopify admin makes it appear in the builder with the correct price\n  // and checkout variant, with NO code change. If the product can't be read, this\n  // fallback keeps the original sizes working.\n  const BOOK_VARIANT_IDS = {\n    \"24\": { A4: 48404302856407, A5: 48892347941079 },\n    \"30\": { A4: 48350688051415, A5: 48892348104919 },\n    \"40\": { A4: 48350688084183, A5: 48892348268759 },\n    \"50\": { A4: 48350688116951, A5: 48892348432599 },\n    \"76\": { A4: 48892348596439, A5: 48892348629207 },\n    \"100\":{ A4: 48892348793047, A5: 48892348825815 }\n  };\n\n  (function _dmbIngestBookVariants() {\n    var list = window.DMB_BOOK_VARIANTS;\n    if (!Array.isArray(list)) return;   // product unreadable \u2192 keep hardcoded fallbacks\n    list.forEach(function (v) {\n      if (!v) return;\n      var pages = String(v.pages == null ? \"\" : v.pages).trim();\n      var size  = String(v.size  == null ? \"\" : v.size ).trim().toUpperCase();\n      // Defensive: if the option order is ever swapped in admin (size in option1),\n      // detect it \u2014 pages must be numeric, size must look like \"A4\"/\"A5\".\n      if (!/^\\d+$/.test(pages) && /^\\d+$/.test(size)) { var t = pages; pages = size; size = t; }\n      if (!/^\\d+$/.test(pages)) return;\n      if (size !== \"A4\" && size !== \"A5\") return;\n      var priceNew = Number(v.price);\n      var priceOld = Number(v.compare);\n      if (!(priceOld > 0)) priceOld = priceNew;   // no compare-at \u2192 no strike-through\n      if (priceNew > 0) {\n        if (!PRICES[size]) PRICES[size] = {};\n        PRICES[size][pages] = { old: priceOld, new: priceNew };\n      }\n      // PHOTO_LIMITS is only kept for parity; derive new sizes to match the existing\n      // pattern (min = pages \u2212 1, max = pages + 8) without disturbing hardcoded ones.\n      if (!PHOTO_LIMITS[pages]) {\n        PHOTO_LIMITS[pages] = { min: Math.max(1, Number(pages) - 1), max: Number(pages) + 8 };\n      }\n      if (v.id) { (BOOK_VARIANT_IDS[pages] = BOOK_VARIANT_IDS[pages] || {})[size] = v.id; }\n    });\n  })();\n\n  // Minimum photos to start designing \u2014 a FLAT 20, regardless of page count\n  // (small or large book, the requirement is the same).\n  const MIN_PHOTOS = 20;\n\n  const SPECIAL_PAGE_TEXT = \"Our special first page design\";\n  const LOCKED_PAGE_NUMBER = 1;\n  const PORTRAIT_MAX_RATIO = 0.85;\n  let spreadPhotoDragState = null; // { type:\"pan\"|\"resize\", si, startX, startY, startPanX, startPanY, startZoom }\n\n  // ---------- page templates ----------\n  const TEMPLATE_NAMES = {\n    t1: \"Triple Stack\",\n    t2: \"Inset Full\",\n    t3: \"Diagonal Duo\",\n    t4: \"Tall Left Split Right\",\n    t5: \"Center Panel Bands\",\n    t6: \"2x2 Grid Top\",\n    t7: \"3x3 Grid\",\n    t8: \"Centered Duo\",\n    t9: \"Triple Bands\",\n    t10: \"Floating lower\",\n    t11: \"Triple Stack Right\",\n    t12: \"Floating Exact Center\",\n    t13: \"Floating Upper\",\n    t14: \"2x2 Upper\",\n    t15: \"Banner Top + Full\",\n    t16: \"Wide Top + 4 Mixed\",\n    t17: \"Asymmetric 5\",\n    t18: \"Full + Bottom 3\",\n    t19: \"2 Column Full\",\n    t20: \"Left 4 + Right Full\",\n    t21: \"Left Full + Right 4\",\n    t22: \"Top Wide + 2 Portrait\",\n    t23: \"2x2 Full Bleed\",\n    t24: \"Almost Full Page\",\n    t25: \"Top 3 + Mixed Rows\",\n    t26: \"Top 3 + Stack + Full\",\n    t27: \"3 Center Bands\",\n    t28: \"2 Portraits Centered\",\n    t29: \"Right Column 3\",\n    t30: \"Side by Side\",\n    t31: \"Top and Bottom\",\n    t32: \"Two Portraits\",\n    t33: \"Big Top, Wide Bottom\",\n    t34: \"Full-Bleed Halves\",\n    t35: \"Wide Center\",\n    tfull: \"Full Page\"\n  };\n\n  const TEMPLATE_DEFS = {\n    // Single frame covering the ENTIRE page/cover (full bleed) \u2014 drop one photo to\n    // fill the whole page. Listed first so it's easy to find.\n    tfull: [\n      { key: \"S1\", x: 0, y: 0, w: 100, h: 100 }\n    ],\n    // \u2500\u2500 Added layouts (2-photo mostly \u2014 that category was thin) \u2500\u2500\n    t30: [\n      { key: \"S1\", x: 6,  y: 6, w: 42, h: 88 },\n      { key: \"S2\", x: 52, y: 6, w: 42, h: 88 }\n    ],\n    t31: [\n      { key: \"S1\", x: 6, y: 6,  w: 88, h: 42 },\n      { key: \"S2\", x: 6, y: 52, w: 88, h: 42 }\n    ],\n    t32: [\n      { key: \"S1\", x: 8,  y: 20, w: 40, h: 60 },\n      { key: \"S2\", x: 52, y: 20, w: 40, h: 60 }\n    ],\n    t33: [\n      { key: \"S1\", x: 6, y: 6,  w: 88, h: 54 },\n      { key: \"S2\", x: 6, y: 64, w: 88, h: 30 }\n    ],\n    t34: [\n      { key: \"S1\", x: 0, y: 0,  w: 100, h: 50 },\n      { key: \"S2\", x: 0, y: 50, w: 100, h: 50 }\n    ],\n    t35: [\n      { key: \"S1\", x: 4, y: 26, w: 92, h: 48 }\n    ],\n    t1: [\n      { key: \"S1\", x: 28, y: 8,  w: 44, h: 24 },\n      { key: \"S2\", x: 28, y: 35, w: 44, h: 24 },\n      { key: \"S3\", x: 28, y: 62, w: 44, h: 24 }\n    ],\n    t2: [\n      { key: \"S1\", x: 8, y: 8, w: 84, h: 84 }\n    ],\n    t3: [\n      { key: \"S1\", x: 10, y: 12, w: 42, h: 28 },\n      { key: \"S2\", x: 48, y: 60, w: 42, h: 28 }\n    ],\n    t4: [\n      { key: \"S1\", x: 0,  y: 0,  w: 48, h: 100 },\n      { key: \"S2\", x: 48, y: 0,  w: 52, h: 48 },\n      { key: \"S3\", x: 48, y: 48, w: 52, h: 52 }\n    ],\n    t5: [\n      { key: \"S1\", x: 20, y: 21, w: 60, h: 58 }\n    ],\n    t6: [\n      { key: \"S1\", x: 14, y: 22, w: 32, h: 24 },\n      { key: \"S2\", x: 54, y: 22, w: 32, h: 24 },\n      { key: \"S3\", x: 14, y: 54, w: 32, h: 24 },\n      { key: \"S4\", x: 54, y: 54, w: 32, h: 24 }\n    ],\n    t7: [\n      { key: \"S1\", x: 2,  y: 8,  w: 31, h: 26 },\n      { key: \"S2\", x: 34, y: 8,  w: 32, h: 26 },\n      { key: \"S3\", x: 67, y: 8,  w: 31, h: 26 },\n      { key: \"S4\", x: 2,  y: 36, w: 31, h: 26 },\n      { key: \"S5\", x: 34, y: 36, w: 32, h: 26 },\n      { key: \"S6\", x: 67, y: 36, w: 31, h: 26 },\n      { key: \"S7\", x: 2,  y: 64, w: 31, h: 26 },\n      { key: \"S8\", x: 34, y: 64, w: 32, h: 26 },\n      { key: \"S9\", x: 67, y: 64, w: 31, h: 26 }\n    ],\n    t8: [\n      { key: \"S1\", x: 24, y: 10, w: 52, h: 36 },\n      { key: \"S2\", x: 24, y: 49, w: 52, h: 38 }\n    ],\n    t9: [\n      { key: \"S1\", x: 0, y: 0,  w: 100, h: 31 },\n      { key: \"S2\", x: 0, y: 31, w: 100, h: 31 },\n      { key: \"S3\", x: 0, y: 62, w: 100, h: 38 }\n    ],\n    t10: [\n      { key: \"S1\", x: 24, y: 46, w: 52, h: 28 }\n    ],\n    t11: [\n      { key: \"S1\", x: 50, y: 8,  w: 44, h: 24 },\n      { key: \"S2\", x: 50, y: 35, w: 44, h: 24 },\n      { key: \"S3\", x: 50, y: 62, w: 44, h: 24 }\n    ],\n    t12: [\n      { key: \"S1\", x: 24, y: 36, w: 52, h: 28 }\n    ],\n    t13: [\n      { key: \"S1\", x: 24, y: 16, w: 52, h: 28 }\n    ],\n    // \u2500\u2500 User-designed layouts (1\u201316) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // 1: 2\u00D72 grid, upper portion \u2013 all frames same size, h-gap = v-gap = 10%\n    t14: [\n      { key: \"S1\", x:  9, y: 13, w: 36, h: 24 },\n      { key: \"S2\", x: 55, y: 13, w: 36, h: 24 },\n      { key: \"S3\", x:  9, y: 47, w: 36, h: 24 },\n      { key: \"S4\", x: 55, y: 47, w: 36, h: 24 }\n    ],\n    // 2: 3 equal frames top row + 1 large frame filling rest of page\n    t15: [\n      { key: \"S1\", x:  0, y:  0, w: 31, h: 28 },\n      { key: \"S2\", x: 32, y:  0, w: 32, h: 28 },\n      { key: \"S3\", x: 65, y:  0, w: 35, h: 28 },\n      { key: \"S4\", x:  0, y: 29, w:100, h: 71 }\n    ],\n    // 3: 1 wide centered top + 2 rows of 2 asymmetric frames (wide-narrow / narrow-wide)\n    t16: [\n      { key: \"S1\", x: 22, y:  7, w: 66, h: 29 },\n      { key: \"S2\", x: 22, y: 37, w: 44, h: 22 },\n      { key: \"S3\", x: 67, y: 37, w: 21, h: 22 },\n      { key: \"S4\", x: 22, y: 60, w: 21, h: 21 },\n      { key: \"S5\", x: 44, y: 60, w: 44, h: 21 }\n    ],\n    // 4: asymmetric 5-frame \u2013 left col 2 frames, right col 3 frames\n    t17: [\n      { key: \"S1\", x:  9, y:  7, w: 40, h: 39 },\n      { key: \"S2\", x: 51, y:  7, w: 41, h: 17 },\n      { key: \"S3\", x: 51, y: 25, w: 41, h: 17 },\n      { key: \"S4\", x:  9, y: 47, w: 40, h: 27 },\n      { key: \"S5\", x: 51, y: 43, w: 41, h: 31 }\n    ],\n    // 5: 1 large full-bleed top + 3 equal frames bottom row\n    t18: [\n      { key: \"S1\", x:  0, y:  0, w:100, h: 78 },\n      { key: \"S2\", x:  0, y: 79, w: 32, h: 21 },\n      { key: \"S3\", x: 33, y: 79, w: 32, h: 21 },\n      { key: \"S4\", x: 67, y: 79, w: 33, h: 21 }\n    ],\n    // 6: 2 equal full-height columns, full bleed\n    t19: [\n      { key: \"S1\", x:  0, y:  0, w: 50, h:100 },\n      { key: \"S2\", x: 51, y:  0, w: 49, h:100 }\n    ],\n    // 7: left col 4 stacked + right col 1 tall full-bleed\n    t20: [\n      { key: \"S1\", x:  0, y:  0, w: 32, h: 24 },\n      { key: \"S2\", x:  0, y: 25, w: 32, h: 24 },\n      { key: \"S3\", x:  0, y: 50, w: 32, h: 24 },\n      { key: \"S4\", x:  0, y: 75, w: 32, h: 25 },\n      { key: \"S5\", x: 34, y:  0, w: 66, h:100 }\n    ],\n    // 8: left col 1 tall full-bleed + right col 4 stacked\n    t21: [\n      { key: \"S1\", x:  0, y:  0, w: 67, h:100 },\n      { key: \"S2\", x: 69, y:  0, w: 31, h: 24 },\n      { key: \"S3\", x: 69, y: 25, w: 31, h: 24 },\n      { key: \"S4\", x: 69, y: 50, w: 31, h: 24 },\n      { key: \"S5\", x: 69, y: 75, w: 31, h: 25 }\n    ],\n    // 9: 1 wide landscape top (with margins) + 2 portrait frames below\n    t22: [\n      { key: \"S1\", x:  7, y:  6, w: 86, h: 43 },\n      { key: \"S2\", x:  7, y: 51, w: 41, h: 43 },\n      { key: \"S3\", x: 51, y: 51, w: 42, h: 43 }\n    ],\n    // 10: 2\u00D72 full-bleed grid\n    t23: [\n      { key: \"S1\", x:  0, y:  0, w: 49, h: 49 },\n      { key: \"S2\", x: 51, y:  0, w: 49, h: 49 },\n      { key: \"S3\", x:  0, y: 51, w: 49, h: 49 },\n      { key: \"S4\", x: 51, y: 51, w: 49, h: 49 }\n    ],\n    // 11: single large frame, equal margin top and bottom, small right margin\n    t24: [\n      { key: \"S1\", x:  0, y:  6, w: 91, h: 88 }\n    ],\n    // 12: top row 3 equal + large bottom-left + 3 stacked bottom-right\n    t25: [\n      { key: \"S1\", x:  0, y:  0, w: 32, h: 28 },\n      { key: \"S2\", x: 33, y:  0, w: 32, h: 28 },\n      { key: \"S3\", x: 66, y:  0, w: 34, h: 28 },\n      { key: \"S4\", x:  0, y: 29, w: 65, h: 71 },\n      { key: \"S5\", x: 66, y: 29, w: 34, h: 23 },\n      { key: \"S6\", x: 66, y: 53, w: 34, h: 23 },\n      { key: \"S7\", x: 66, y: 77, w: 34, h: 23 }\n    ],\n    // 13: top row 3 equal + 3 stacked bottom-left + large bottom-right\n    t26: [\n      { key: \"S1\", x:  0, y:  0, w: 32, h: 28 },\n      { key: \"S2\", x: 33, y:  0, w: 32, h: 28 },\n      { key: \"S3\", x: 66, y:  0, w: 34, h: 28 },\n      { key: \"S4\", x:  0, y: 29, w: 32, h: 23 },\n      { key: \"S5\", x:  0, y: 53, w: 32, h: 23 },\n      { key: \"S6\", x:  0, y: 77, w: 32, h: 23 },\n      { key: \"S7\", x: 33, y: 29, w: 67, h: 71 }\n    ],\n    // 14: 3 horizontal bands, centered with side margins\n    t27: [\n      { key: \"S1\", x: 18, y:  0, w: 64, h: 31 },\n      { key: \"S2\", x: 18, y: 33, w: 64, h: 31 },\n      { key: \"S3\", x: 18, y: 66, w: 64, h: 34 }\n    ],\n    // 15: 2 portrait frames side by side, centered horizontally and vertically\n    t28: [\n      { key: \"S1\", x:  4, y: 22, w: 44, h: 57 },\n      { key: \"S2\", x: 52, y: 22, w: 44, h: 57 }\n    ],\n    // 16: right column only \u2013 3 stacked frames\n    t29: [\n      { key: \"S1\", x: 51, y:  0, w: 49, h: 31 },\n      { key: \"S2\", x: 51, y: 33, w: 49, h: 31 },\n      { key: \"S3\", x: 51, y: 66, w: 49, h: 34 }\n    ]\n  };\n\n  // \u2500\u2500 Photo Frame Shapes \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  var DMB_FRAME_SHAPES = [\n    { id: \"none\",    label: \"None\" },\n    { id: \"circle\",  label: \"Circle\" },\n    { id: \"rounded\", label: \"Rounded\" },\n    { id: \"soft\",    label: \"Soft\" },\n    { id: \"heart\",   label: \"Heart\" },\n    { id: \"star\",    label: \"Star\" },\n    { id: \"diamond\", label: \"Diamond\" },\n    { id: \"hexagon\", label: \"Hexagon\" },\n  ];\n\n  function ensureDmbClipPaths() {\n    if (document.getElementById(\"dmb-clip-defs\")) return;\n    var svg = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\");\n    svg.id = \"dmb-clip-defs\";\n    svg.setAttribute(\"width\", \"0\"); svg.setAttribute(\"height\", \"0\");\n    svg.style.cssText = \"position:absolute;width:0;height:0;overflow:hidden;pointer-events:none;\";\n    svg.innerHTML = '<defs>' +\n      '<clipPath id=\"dmb-clip-heart\" clipPathUnits=\"objectBoundingBox\"><path d=\"M0.5,0.85 C0.2,0.72 0,0.52 0,0.35 C0,0.14 0.16,0.04 0.3,0.04 C0.4,0.04 0.48,0.13 0.5,0.18 C0.52,0.13 0.6,0.04 0.7,0.04 C0.84,0.04 1,0.14 1,0.35 C1,0.52 0.8,0.72 0.5,0.85Z\"/></clipPath>' +\n      '<clipPath id=\"dmb-clip-star\" clipPathUnits=\"objectBoundingBox\"><polygon points=\"0.5,0.02 0.61,0.37 0.98,0.37 0.69,0.59 0.79,0.94 0.5,0.72 0.21,0.94 0.31,0.59 0.02,0.37 0.39,0.37\"/></clipPath>' +\n      '<clipPath id=\"dmb-clip-diamond\" clipPathUnits=\"objectBoundingBox\"><polygon points=\"0.5,0 1,0.5 0.5,1 0,0.5\"/></clipPath>' +\n      '<clipPath id=\"dmb-clip-hexagon\" clipPathUnits=\"objectBoundingBox\"><polygon points=\"0.25,0.02 0.75,0.02 1,0.5 0.75,0.98 0.25,0.98 0,0.5\"/></clipPath>' +\n      '</defs>';\n    document.body.appendChild(svg);\n  }\n\n  function applyDmbFrameShape(el, shapeId) {\n    el.style.borderRadius = \"\";\n    el.style.clipPath = \"\";\n    if (!shapeId || shapeId === \"none\") return;\n    if (shapeId === \"circle\")       { el.style.borderRadius = \"50%\"; }\n    else if (shapeId === \"rounded\") { el.style.borderRadius = \"20%\"; }\n    else if (shapeId === \"soft\")    { el.style.borderRadius = \"15px\"; }\n    else { ensureDmbClipPaths(); el.style.clipPath = \"url(#dmb-clip-\" + shapeId + \")\"; }\n  }\n\n  function showDmbFramePicker(anchorEl, currentShape, onSelect) {\n    var existing = document.getElementById(\"dmb-frame-shape-picker\");\n    if (existing) { existing.remove(); return; }\n    ensureDmbClipPaths();\n    var picker = document.createElement(\"div\");\n    picker.id = \"dmb-frame-shape-picker\";\n    picker.style.cssText = \"position:fixed;z-index:99999;background:#1e1e1e;border:1px solid #444;border-radius:12px;padding:10px;box-shadow:0 8px 32px rgba(0,0,0,0.7);display:grid;grid-template-columns:repeat(4,1fr);gap:8px;\";\n    DMB_FRAME_SHAPES.forEach(function(shape) {\n      var btn = document.createElement(\"button\");\n      btn.type = \"button\"; btn.title = shape.label;\n      var active = (shape.id === (currentShape || \"none\"));\n      btn.style.cssText = \"width:52px;height:60px;border:2px solid \" + (active ? \"#a9b4ff\" : \"#333\") + \";border-radius:8px;background:#2a2a2a;cursor:pointer;padding:5px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;\";\n      var preview = document.createElement(\"div\");\n      preview.style.cssText = \"width:32px;height:32px;background:linear-gradient(135deg,#c084fc,#818cf8);overflow:hidden;flex-shrink:0;\";\n      applyDmbFrameShape(preview, shape.id);\n      var lbl = document.createElement(\"span\");\n      lbl.style.cssText = \"font-size:8px;color:#aaa;font-weight:700;line-height:1;text-transform:uppercase;letter-spacing:0.3px;\";\n      lbl.textContent = shape.label;\n      btn.appendChild(preview); btn.appendChild(lbl);\n      btn.addEventListener(\"click\", function(e) {\n        e.stopPropagation();\n        picker.remove();\n        document.removeEventListener(\"pointerdown\", outsideClose);\n        onSelect(shape.id === \"none\" ? \"\" : shape.id);\n      });\n      picker.appendChild(btn);\n    });\n    var rect = anchorEl.getBoundingClientRect();\n    var left = Math.min(rect.left - 80, window.innerWidth - 260);\n    picker.style.left = Math.max(8, left) + \"px\";\n    picker.style.top = (rect.bottom + 8) + \"px\";\n    document.body.appendChild(picker);\n    function outsideClose(e) {\n      if (!picker.contains(e.target) && e.target !== anchorEl) {\n        picker.remove();\n        document.removeEventListener(\"pointerdown\", outsideClose);\n      }\n    }\n    setTimeout(function() { document.addEventListener(\"pointerdown\", outsideClose); }, 60);\n  }\n\n  // Rounded-rectangle path with SEPARATE x and y corner radii, at (x,y).\n  // CSS border-radius is per-AXIS: on a 200x120 box, `border-radius:50%` is an\n  // ELLIPSE 200x120, not a 120px circle. Canvas arc()/quadraticCurveTo() cannot\n  // express that, which is why the PDF used to square off wide frames.\n  function _dmbRoundRectPath(ctx, x, y, w, h, rx, ry) {\n    rx = Math.max(0, Math.min(rx, w / 2));\n    ry = Math.max(0, Math.min(ry, h / 2));\n    if (rx <= 0 || ry <= 0) { ctx.rect(x, y, w, h); return; }\n    var K = Math.PI / 2;\n    ctx.moveTo(x + rx, y);\n    ctx.lineTo(x + w - rx, y);\n    ctx.ellipse(x + w - rx, y + ry,     rx, ry, 0, -K, 0);\n    ctx.lineTo(x + w, y + h - ry);\n    ctx.ellipse(x + w - rx, y + h - ry, rx, ry, 0, 0, K);\n    ctx.lineTo(x + rx, y + h);\n    ctx.ellipse(x + rx,     y + h - ry, rx, ry, 0, K, Math.PI);\n    ctx.lineTo(x, y + ry);\n    ctx.ellipse(x + rx,     y + ry,     rx, ry, 0, Math.PI, 3 * K);\n    ctx.closePath();\n  }\n\n  // Clip the canvas to a photo-frame shape, matching applyDmbFrameShape (1194)\n  // pixel for pixel.\n  //   pxScale = canvas px per BUILDER px (PAGE_SCALE 9.19 / FACE_SCALE 8.27).\n  // Only \"soft\" needs it: on screen that shape is a FIXED 15 CSS px radius, so\n  // the printed radius must be 15 * pxScale. Every other shape is expressed as a\n  // fraction of the box and scales on its own.\n  function applyCanvasFrameClip(ctx, shapeId, w, h, pxScale) {\n    if (!shapeId || shapeId === \"none\") return;\n    var P = (typeof pxScale === \"number\" && pxScale > 0) ? pxScale : 1;\n    ctx.beginPath();\n    if (shapeId === \"circle\") {\n      // screen: border-radius:50%  ->  an ellipse filling the box\n      ctx.ellipse(w / 2, h / 2, w / 2, h / 2, 0, 0, Math.PI * 2);\n    } else if (shapeId === \"rounded\") {\n      // screen: border-radius:20%  ->  20% of EACH axis (elliptical corners)\n      _dmbRoundRectPath(ctx, 0, 0, w, h, w * 0.2, h * 0.2);\n    } else if (shapeId === \"soft\") {\n      // screen: border-radius:15px ->  a fixed 15 CSS px on both axes\n      _dmbRoundRectPath(ctx, 0, 0, w, h, 15 * P, 15 * P);\n    } else if (shapeId === \"heart\") {\n      ctx.moveTo(w*0.5,h*0.85);\n      ctx.bezierCurveTo(w*0.2,h*0.72, 0,h*0.52, 0,h*0.35);\n      ctx.bezierCurveTo(0,h*0.14, w*0.16,h*0.04, w*0.3,h*0.04);\n      ctx.bezierCurveTo(w*0.4,h*0.04, w*0.48,h*0.13, w*0.5,h*0.18);\n      ctx.bezierCurveTo(w*0.52,h*0.13, w*0.6,h*0.04, w*0.7,h*0.04);\n      ctx.bezierCurveTo(w*0.84,h*0.04, w,h*0.14, w,h*0.35);\n      ctx.bezierCurveTo(w,h*0.52, w*0.8,h*0.72, w*0.5,h*0.85); ctx.closePath();\n    } else if (shapeId === \"star\") {\n      var sp = [[0.5,0.02],[0.61,0.37],[0.98,0.37],[0.69,0.59],[0.79,0.94],[0.5,0.72],[0.21,0.94],[0.31,0.59],[0.02,0.37],[0.39,0.37]];\n      sp.forEach(function(p,i){ i===0?ctx.moveTo(p[0]*w,p[1]*h):ctx.lineTo(p[0]*w,p[1]*h); }); ctx.closePath();\n    } else if (shapeId === \"diamond\") {\n      ctx.moveTo(w*0.5,0); ctx.lineTo(w,h*0.5); ctx.lineTo(w*0.5,h); ctx.lineTo(0,h*0.5); ctx.closePath();\n    } else if (shapeId === \"hexagon\") {\n      var hp = [[0.25,0.02],[0.75,0.02],[1,0.5],[0.75,0.98],[0.25,0.98],[0,0.5]];\n      hp.forEach(function(p,i){ i===0?ctx.moveTo(p[0]*w,p[1]*h):ctx.lineTo(p[0]*w,p[1]*h); }); ctx.closePath();\n    }\n    ctx.clip();\n  }\n  function getSlotFrameShape(pageNumber, slot) {\n    return (editor.pageFrameShapes && editor.pageFrameShapes[pageNumber] && editor.pageFrameShapes[pageNumber][slot]) || \"\";\n  }\n  function setSlotFrameShape(pageNumber, slot, shapeId) {\n    if (!editor.pageFrameShapes) editor.pageFrameShapes = {};\n    if (!editor.pageFrameShapes[pageNumber]) editor.pageFrameShapes[pageNumber] = {};\n    editor.pageFrameShapes[pageNumber][slot] = shapeId || \"\";\n  }\n\n  // ---------- helpers ----------\n  // Local renderBuilderCanvas \u2014 delegates to the actual render functions in this IIFE\n  function renderBuilderCanvas() {\n    document.querySelectorAll(\".dmb-map-rot-float\").forEach((el) => el.remove());\n    if (builderMode === \"cover\") renderCoverCanvas();\n    else renderSpread();\n    if (typeof syncBuilderModeButtons === \"function\") syncBuilderModeButtons();\n    if (typeof setCanvasZoom === \"function\") setCanvasZoom(currentZoom);\n    if (typeof syncPhotoTrayHeight === \"function\") syncPhotoTrayHeight();\n    // Pulse the preview button to remind customer to review after each edit\n    const _pb = document.getElementById(\"previewBtn\");\n    if (_pb) _pb.classList.add(\"dmb-preview-needs-check\");\n    // Show photo quality notice the very first time a photo lands on any page\n    _maybeShowPhotoQualityToast();\n  }\n\n  // \u2500\u2500 Fix 3: 45\u00B0 magnetic snap rotation helper \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // Returns the raw angle snapped to the nearest 45\u00B0 multiple when within\n  // SNAP_DEG degrees of it, otherwise returns the raw angle unchanged.\n  // Works for all rotation handlers: text, maps, stickers, shapes, icons.\n  // \"Breakaway\" is natural: the angle is only snapped while the raw value stays\n  // inside the snap zone; rotate past 5\u00B0 away and it instantly releases.\n  const _ROT_SNAP_DEG = 5;\n  function _dmbSnapAngle(rawDeg) {\n    // Normalise to 0\u2013360 to find the nearest 45\u00B0 multiple\n    const norm    = ((rawDeg % 360) + 360) % 360;\n    const nearest = Math.round(norm / 45) * 45; // 0, 45, 90 \u2026 360\n    const diff    = norm - nearest;\n    return Math.abs(diff) <= _ROT_SNAP_DEG ? rawDeg - diff : rawDeg;\n  }\n\n  // \u2500\u2500 First-photo quality toast \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // Shows once per session the moment the customer places their first photo.\n  // Disappears automatically after 4 s with a fade-out.\n  // Uses sessionStorage so it never re-appears during the same browser session.\n  let _photoQualityToastShown = false;\n  function _maybeShowPhotoQualityToast() {\n    if (_dmbRenderMode()) return;   // render mode: no UI, no sessionStorage\n    if (_photoQualityToastShown) return;\n    if (sessionStorage.getItem(\"dmb_photo_quality_toast_shown\")) { _photoQualityToastShown = true; return; }\n    // Check if at least one page slot has a photo\n    const slots = editor.pageSlots || {};\n    const hasPhoto = Object.values(slots).some(function(s) { return s && (s.A || s.B); });\n    if (!hasPhoto) return;\n    _photoQualityToastShown = true;\n    sessionStorage.setItem(\"dmb_photo_quality_toast_shown\", \"1\");\n    const toast = document.getElementById(\"dmbPhotoQualityToast\");\n    if (!toast) return;\n    // Add a close (\u2715) button once \u2014 the toast now STAYS until the customer\n    // dismisses it (no auto-dismiss timer). One handler covers book/magazine/card.\n    if (!toast.querySelector(\".dmb-toast-close\")) {\n      const _x = document.createElement(\"button\");\n      _x.type = \"button\";\n      _x.className = \"dmb-toast-close\";\n      _x.setAttribute(\"aria-label\", \"Close\");\n      _x.textContent = \"\u2715\";\n      // pointer-events:auto \u2014 the toast itself is pointer-events:none (so it never\n      // blocks the builder), which would also swallow clicks on this \u2715; re-enable\n      // just the button so it can be closed.\n      _x.style.cssText = \"background:transparent;border:none;color:inherit;opacity:.55;font-size:15px;line-height:1;cursor:pointer;padding:4px;margin-left:8px;align-self:flex-start;flex-shrink:0;pointer-events:auto;\";\n      _x.addEventListener(\"click\", function () {\n        toast.classList.remove(\"dmb-toast-visible\");\n        setTimeout(function () { toast.style.display = \"none\"; }, 500);\n      });\n      toast.appendChild(_x);\n    }\n    // Animate in (closes only via the \u2715)\n    toast.style.display = \"flex\";\n    requestAnimationFrame(function() {\n      requestAnimationFrame(function() { toast.classList.add(\"dmb-toast-visible\"); });\n    });\n  }\n\n  let DMB_CURRENT_DRAG = null;\n  const $ = (id) => document.getElementById(id);\n  const fmtMoney = (n) => \"$\" + Number(n).toFixed(2);\n  const isLockedPage = (p) => !window.DMB_MAGAZINE_MODE && p === LOCKED_PAGE_NUMBER;\n\n  function getActivePageNumberForMap() {\n    if (!editor.totalPages) return null;\n\n    if (builderMode === \"cover\") return \"cover\";\n\n    if (editor.currentSpread === -1) return 0;\n\n    const leftPage = editor.currentSpread * 2 + 1;\n    const rightPage = leftPage + 1;\n\n    // Honour lastToolbarSide, exactly like insertShapeIntoActivePage (8922) and the\n    // per-page Add text / Add shape / Add polaroid buttons. This used to return the\n    // LEFT page unconditionally, so adding a map while working on a right-hand page\n    // dropped it on the facing page instead. On a phone - which shows ONE page at a\n    // time - that reads as \"Insert did nothing\", because the map landed on a page\n    // that is not on screen.\n    if (lastToolbarSide === \"right\" && rightPage <= editor.totalPages && !isLockedPage(rightPage)) return rightPage;\n    if (leftPage  <= editor.totalPages && !isLockedPage(leftPage))  return leftPage;\n    if (rightPage <= editor.totalPages && !isLockedPage(rightPage)) return rightPage;\n    return null;\n  }\n\n  // Set by the per-page \"Add map\" button so the picker knows its target without\n  // asking. Cleared every time the map modal opens, so a ribbon-opened picker\n  // always falls back to asking.\n  let _dmbMapTargetPage = null;\n\n  // Which page should this map go on? A spread has TWO pages and the old code\n  // silently picked the left one. Ask, unless the answer is already unambiguous.\n  // Returns a page number, \"cover\", or null when there is nowhere to put it / the\n  // customer cancelled.\n  async function _dmbResolveMapTargetPage() {\n    if (builderMode === \"cover\") return \"cover\";\n    if (_dmbMapTargetPage != null) return _dmbMapTargetPage;   // per-page button\n    if (!editor.totalPages) return null;\n    if (editor.currentSpread === -1) return 0;\n\n    // PHONE ONLY. Desktop shows both pages of the spread side by side, so the\n    // destination is obvious and a dialog is pure friction - it just uses the\n    // page the customer last worked on (lastToolbarSide). The phone shows ONE\n    // page at a time, which is where a silent wrong-page insert looks like\n    // nothing happened, so that is the only case worth interrupting.\n    if (!(window.innerWidth <= 749)) return getActivePageNumberForMap();\n\n    const lp = editor.currentSpread * 2 + 1, rp = lp + 1;\n    const opts = [];\n    if (lp <= editor.totalPages && !isLockedPage(lp)) opts.push(lp);\n    if (rp <= editor.totalPages && !isLockedPage(rp)) opts.push(rp);\n    if (!opts.length) return null;\n    if (opts.length === 1) return opts[0];\n\n    const choice = await showChoiceDialog(\n      \"Which page?\",\n      \"You are looking at a spread. Choose the page this map should go on.\",\n      opts.map(function (p) { return { id: String(p), label: \"Page \" + p }; })\n          .concat([{ id: \"cancel\", label: \"Cancel\" }])\n    );\n    if (!choice || choice === \"cancel\") return null;\n    return parseInt(choice, 10);\n  }\n\n  function uid() {\n    return \"p_\" + Math.random().toString(36).slice(2) + Date.now().toString(36);\n  }\n  // Unique object id. Date.now() alone collides when two objects are added in the\n  // same millisecond (e.g. two sticker image onloads firing together) \u2192 they'd share\n  // an id and one could overwrite/hide the other. The random suffix prevents that.\n  function _dmbOid(prefix) {\n    return prefix + Date.now().toString(36) + Math.random().toString(36).slice(2, 6);\n  }\n\n  function syncInputFiles(inputEl, filesArr) {\n    if (!inputEl) return;\n    const dt = new DataTransfer();\n    filesArr.forEach((f) => dt.items.add(f));\n    inputEl.files = dt.files;\n  }\n\n  function blobToBase64(blob) {\n    return new Promise((resolve, reject) => {\n      const r = new FileReader();\n      r.onload = () => resolve(String(r.result || \"\").split(\",\")[1] || \"\");\n      r.onerror = reject;\n      r.readAsDataURL(blob);\n    });\n  }\n\n  // \u2500\u2500 Mobile image compression \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // Resizes oversized photos before they enter the builder, preventing iOS/Safari\n  // from crashing due to canvas memory limits when processing large camera images.\n  // 2500 px max keeps full print quality (300 Dpi on an 8\" side) while cutting\n  // a 48 MP iPhone photo from ~15 MB down to ~1\u20132 MB.\n  // Read a JPEG's pixel dimensions straight from its header bytes \u2014 NO pixel\n  // decode. Lets us decide the downscale target WITHOUT ever fully decoding a huge\n  // photo (a 10 MB / 48 MP JPEG would be ~195 MB as a full bitmap \u2014 decoding 60 of\n  // those in a row is the phone OOM). iOS Safari uploads camera photos as JPEG\n  // (HEIC is auto-converted through a file input), so this fast path covers the\n  // common case. Returns {w,h} or null. Scans the first 256 KB for the SOF marker.\n  async function _dmbReadJpegSize(file) {\n    try {\n      const buf = new Uint8Array(await file.slice(0, 256 * 1024).arrayBuffer());\n      if (buf.length < 4 || buf[0] !== 0xFF || buf[1] !== 0xD8) return null;   // not JPEG\n      let i = 2;\n      while (i + 9 < buf.length) {\n        if (buf[i] !== 0xFF) { i++; continue; }\n        let marker = buf[i + 1];\n        while (marker === 0xFF && i + 1 < buf.length) { i++; marker = buf[i + 1]; }  // skip fill bytes\n        // Standalone markers (no length): RSTn, SOI, EOI, TEM.\n        if (marker === 0xD8 || marker === 0xD9 || (marker >= 0xD0 && marker <= 0xD7) || marker === 0x01) { i += 2; continue; }\n        // SOF0..SOF15 carry the frame size \u2014 but C4 (DHT), C8 (JPG), CC (DAC) are NOT SOF.\n        if (marker >= 0xC0 && marker <= 0xCF && marker !== 0xC4 && marker !== 0xC8 && marker !== 0xCC) {\n          const h = (buf[i + 5] << 8) | buf[i + 6];\n          const w = (buf[i + 7] << 8) | buf[i + 8];\n          return (w > 0 && h > 0) ? { w, h } : null;\n        }\n        const len = (buf[i + 2] << 8) | buf[i + 3];   // segment length (includes these 2 bytes)\n        if (len < 2) return null;\n        i += 2 + len;\n      }\n    } catch (e) {}\n    return null;\n  }\n\n  async function compressImageIfNeeded(file) {\n    const MAX_DIM = 2500;\n    const QUALITY  = 0.88;\n    if (typeof createImageBitmap !== \"function\") return file;   // very old browser: keep original\n\n    // FAST PATH (JPEG): read dimensions from the header (bytes, no decode).\n    const hdr = await _dmbReadJpegSize(file);\n    if (hdr && hdr.w <= MAX_DIM && hdr.h <= MAX_DIM) return file;   // small enough \u2014 no decode at all\n\n    // Build a REDUCED-resolution decode target so a huge photo is never fully\n    // decoded. createImageBitmap with resizeWidth/Height maps to iOS ImageIO's\n    // scaled decode (kCGImageSourceThumbnailMaxPixelSize), so peak memory is the\n    // 2500 px result (~25 MB), not the 48 MP original (~195 MB).\n    // Cap ONLY the longer edge and let the browser preserve the TRUE aspect \u2014 never\n    // force BOTH dims from the header, because a mis-read/edge-case header (e.g. some\n    // iPhone ultra-wide 0.5\u00D7 JPEGs) would then stretch the photo. imageOrientation\n    // bakes EXIF rotation so the saved file needs no orientation flag.\n    const target = { resizeQuality: \"high\", imageOrientation: \"from-image\" };\n    if (hdr) {\n      if (hdr.w >= hdr.h) target.resizeWidth = Math.min(hdr.w, MAX_DIM);\n      else                target.resizeHeight = Math.min(hdr.h, MAX_DIM);\n    } else {\n      target.resizeWidth = MAX_DIM;\n    }\n\n    let bmp;\n    try { bmp = await createImageBitmap(file, target); }\n    catch (e) {\n      try { bmp = await createImageBitmap(file, { imageOrientation: \"from-image\" }); }\n      catch (e1) { try { bmp = await createImageBitmap(file); } catch (e2) { return file; } }\n    }\n    try {\n      let w = bmp.width || 0, h = bmp.height || 0;\n      if (!w || !h) return file;\n      // Aspect-preserving safety cap: if the decode came back larger than MAX_DIM on\n      // its long edge, scale BOTH edges by the same factor (never distort).\n      if (Math.max(w, h) > MAX_DIM) {\n        const s = MAX_DIM / Math.max(w, h);\n        w = Math.max(1, Math.round(w * s));\n        h = Math.max(1, Math.round(h * s));\n      }\n      const canvas = document.createElement(\"canvas\");\n      canvas.width = w; canvas.height = h;\n      const ctx = canvas.getContext(\"2d\");\n      try { ctx.imageSmoothingQuality = \"high\"; } catch (e) {}\n      ctx.drawImage(bmp, 0, 0, w, h);\n      const blob = await new Promise((res) => canvas.toBlob(res, \"image/jpeg\", QUALITY));\n      canvas.width = 0; canvas.height = 0;                       // free canvas memory now\n      if (!blob) return file;\n      return new File([blob], file.name, { type: \"image/jpeg\", lastModified: file.lastModified });\n    } catch (e) {\n      return file;\n    } finally {\n      try { if (bmp && bmp.close) bmp.close(); } catch (e) {}    // release the decode immediately\n    }\n  }\n\n  async function compressFilesForMobile(files) {\n    // PRINT-MASTER pipeline (flag ON): desktop AND mobile \u2014 every photo becomes a\n    // ~3500px master (attached as f._dmbMaster, uploaded to R2 then freed) + a\n    // 2500px editing proxy. Still strictly ONE AT A TIME (parallel decode = iOS OOM).\n    if (_dmbPrintMasterOn()) {\n      const out = [];\n      for (const f of files) out.push(await _dmbMakePrintDerivatives(f));\n      return out;\n    }\n    if (window.innerWidth > 749) return files;   // desktop \u2014 use originals\n    // Process ONE AT A TIME \u2014 parallel (Promise.all) loads all originals into\n    // memory simultaneously, which is what causes the iOS crash.\n    const result = [];\n    for (const f of files) {\n      result.push(await compressImageIfNeeded(f));\n    }\n    return result;\n  }\n\n  // \u2500\u2500 Print-master derivatives (flag-gated; see _dmbPrintMasterOn) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // ONE memory-safe scaled decode \u2192 a ~3500px PRINT MASTER (kept as original\n  // bytes when the photo is already \u22643500px \u2014 zero re-encode loss) + a 2500px\n  // editing PROXY. The proxy is what enters the builder (ph.file / R2 photos/);\n  // the master rides along as proxy._dmbMaster until its own R2 upload frees it.\n  async function _dmbMakePrintDerivatives(file) {\n    try {\n      // QR-relay photos USED to bail out here with no master at all, on the theory\n      // that the phone had already re-encoded them so any master would be \"fake\n      // quality\". Half right: they are re-encoded once, but the phone sends them at\n      // up to 4096px (sections/phone-upload.liquid: `var MAX = 4096` at q0.92) -\n      // LARGER than _DMB_MASTER_DIM, and well above the 2480x3508 a full-bleed A4\n      // page needs at 300 DPI. So the bail-out threw away print-grade pixels AND\n      // made every proof read \"0 of N photos at full print quality\" for a book that\n      // would have printed perfectly well - alarming a customer at the moment of\n      // payment about a problem that did not exist.\n      //\n      // They now take the SAME path as every other photo. The master keeps the\n      // phone's bytes verbatim (see masterBlob below) because a second re-encode is\n      // the only step here that would genuinely lose anything, and the 2500px\n      // editing proxy is still derived through the one memory-safe scaled decode -\n      // so this is no heavier on a phone than any other import. (2026-09-14)\n      if (file && file._dmbRelay) _dmbMasterStats.relay++;\n      if (typeof createImageBitmap !== \"function\") return file;\n      const hdr = await _dmbReadJpegSize(file);\n      const longEdge = hdr ? Math.max(hdr.w, hdr.h) : 0;\n      // Already <= proxy size -> the photo IS its own full-res master; keep the\n      // exact original bytes, nothing to derive (matches the legacy fast path).\n      // Register it as its own master anyway so photos-orig/{pid} exists for EVERY\n      // imported photo. The render path, the cover draw-by-id branch and the\n      // print-quality counter all treat \"has a master\" as the invariant, and without\n      // this a small photo is indistinguishable from one whose master never uploaded\n      // - which is why a book of sub-2500px photos reported 0 at full print quality.\n      if (hdr && longEdge <= _DMB_PROXY_DIM) {\n        try { file._dmbMaster = file; _dmbMasterStats.queued++; } catch (e) {}\n        return file;\n      }\n      // Decode ONCE, scaled to the master size (iOS ImageIO scaled decode \u2014 never\n      // materialises the full 48MP bitmap).\n      const target = { resizeQuality: \"high\", imageOrientation: \"from-image\" };\n      if (hdr) {\n        if (hdr.w >= hdr.h) target.resizeWidth = Math.min(hdr.w, _DMB_MASTER_DIM);\n        else                target.resizeHeight = Math.min(hdr.h, _DMB_MASTER_DIM);\n      }\n      // No else: asking for resizeWidth = _DMB_MASTER_DIM when the header could not\n      // be read (PNG, or any non-JPEG _dmbReadJpegSize cannot parse) does not CAP the\n      // decode - it RESIZES to exactly 3500, so a 900px screenshot was upscaled ~4x\n      // and that upscale was then re-encoded and stored as its \"print master\".\n      // Inventing pixels wastes bytes and makes the master a lie. Decode at natural\n      // size instead; the explicit cap a few lines below still brings anything\n      // genuinely larger than _DMB_MASTER_DIM back down. (2026-09-14)\n      let bmp = null, imgEl = null;\n      try { bmp = await createImageBitmap(file, target); }\n      catch (e0) {\n        try { bmp = await createImageBitmap(file, { imageOrientation: \"from-image\" }); }\n        catch (e1) { try { bmp = await createImageBitmap(file); } catch (e2) { bmp = null; } }\n      }\n      if (!bmp) {\n        // createImageBitmap refused the file (iOS Safari does this for some camera\n        // JPEGs / HEIC). Decode through an <img> instead - Safari renders those\n        // natively - so the photo still gets a real master rather than silently\n        // dropping to the proxy-only legacy path with no master at all.\n        imgEl = await new Promise(function (res) {\n          try {\n            const u = URL.createObjectURL(file);\n            const im = new Image();\n            let done = false;\n            const fin = function (ok) { if (done) return; done = true; try { URL.revokeObjectURL(u); } catch (e) {} res(ok ? im : null); };\n            im.onload = function () { fin(true); };\n            im.onerror = function () { fin(false); };\n            setTimeout(function () { fin(false); }, 20000);\n            im.src = u;\n          } catch (e) { res(null); }\n        });\n        if (!imgEl || !imgEl.naturalWidth) {\n          _dmbMasterStats.decode_failed++;\n          return _dmbIsMobileUpload() ? await compressImageIfNeeded(file) : file;\n        }\n      }\n      const src = bmp || imgEl;\n      try {\n        let w = bmp ? bmp.width : imgEl.naturalWidth, h = bmp ? bmp.height : imgEl.naturalHeight;\n        if (Math.max(w, h) > _DMB_MASTER_DIM) {          // decode ignored the resize \u2192 cap manually\n          const s = _DMB_MASTER_DIM / Math.max(w, h);\n          w = Math.max(1, Math.round(w * s)); h = Math.max(1, Math.round(h * s));\n        }\n        const mc = document.createElement(\"canvas\");\n        mc.width = w; mc.height = h;\n        const mctx = mc.getContext(\"2d\");\n        try { mctx.imageSmoothingQuality = \"high\"; } catch (e) {}\n        mctx.drawImage(src, 0, 0, w, h);\n        // Master: exact original bytes when the photo is already \u2264 master size\n        // (no re-encode loss), else the 3500px re-encode.\n        // A relay photo is ALREADY a q0.92 encode from the phone; re-encoding it\n        // down to 3500px would be the one step that actually costs quality, and it\n        // is bigger than the master target anyway. Keep its bytes exactly as sent.\n        const masterBlob = ((file && file._dmbRelay) || (hdr && longEdge <= _DMB_MASTER_DIM))\n          ? file\n          : await new Promise((res) => mc.toBlob(res, \"image/jpeg\", _DMB_MASTER_Q));\n        // Proxy: downscaled from the master canvas \u2014 no second decode of the file.\n        const ps = _DMB_PROXY_DIM / Math.max(w, h);\n        const pw = Math.max(1, Math.round(w * ps)), ph2 = Math.max(1, Math.round(h * ps));\n        const pc = document.createElement(\"canvas\");\n        pc.width = pw; pc.height = ph2;\n        const pctx = pc.getContext(\"2d\");\n        try { pctx.imageSmoothingQuality = \"high\"; } catch (e) {}\n        pctx.drawImage(mc, 0, 0, pw, ph2);\n        const proxyBlob = await new Promise((res) => pc.toBlob(res, \"image/jpeg\", _DMB_PROXY_Q));\n        mc.width = 0; mc.height = 0; pc.width = 0; pc.height = 0;   // free canvas memory now\n        if (!proxyBlob) return await compressImageIfNeeded(file);   // fallback: legacy single derivative\n        const proxy = new File([proxyBlob], file.name, { type: \"image/jpeg\", lastModified: file.lastModified });\n        if (masterBlob) { proxy._dmbMaster = masterBlob; _dmbMasterStats.queued++; }\n        return proxy;\n      } finally {\n        try { if (bmp && bmp.close) bmp.close(); } catch (e) {}     // release the decode immediately\n      }\n    } catch (e) {\n      try { return _dmbIsMobileUpload() ? await compressImageIfNeeded(file) : file; } catch (e2) { return file; }\n    }\n  }\n\n  // \u2500\u2500 Apps Script URL + HTTP helper \u2014 module-level so both uploadPdfToGoogleDrive\n  // and addDesignedBookToCart can call postToScript (e.g. for trash_file).\n  const DMB_APPS_SCRIPT_URL = \"https://script.google.com/macros/s/AKfycbwzcCe9WkOMqPiSc58NJOMEPbDfLOH3q_C_4leRgoaK5Xg5BSLh2dpjvCRbzZF8/exec\";\n\n  async function postToScript(payload, attempts = 3) {\n    let lastErr;\n    for (let i = 1; i <= attempts; i++) {\n      try {\n        const res = await fetch(DMB_APPS_SCRIPT_URL, {\n          method: \"POST\",\n          headers: { \"Content-Type\": \"text/plain\" },\n          body: JSON.stringify(payload)\n        });\n        const data = await res.json();\n        if (!data.ok) throw new Error(data.error || \"Script error\");\n        return data;\n      } catch (err) {\n        lastErr = err;\n        if (i < attempts) await new Promise(r => setTimeout(r, i * 1500));\n      }\n    }\n    throw lastErr;\n  }\n\n  // Expose the Drive uploader + Apps Script poster so the native card editor\n  // (card-editor.js) reuses the EXACT same book upload pipeline (upload to Drive \u2192\n  // _pdf_file_id line-item property \u2192 Apps Script renames with the order # at\n  // checkout). `label` marks the card's file so it's distinct from the book's.\n  window.__dmbUploadPdf   = function (blob, label) { return uploadPdfToGoogleDrive(blob, label); };\n  window.__dmbPostToScript = function (payload, attempts) { return postToScript(payload, attempts); };\n  // Expose the customer's uploaded photo library so the native card editor can\n  // offer \"pick from your uploaded photos\" instead of a fresh device upload.\n  window.__dmbGetPhotos = function () {\n    try { return (editor.photos || []).map(function (p) { return { id: p.id, url: p.url, file: p.file || null, filename: p.filename || (p.file && p.file.name) || \"\" }; }); }\n    catch (e) { return []; }\n  };\n  // Let the card editor trigger the same \"upload from phone\" QR flow the book uses.\n  window.__dmbOpenPhoneUpload = function () { try { openPhoneUploadModal(); } catch (e) {} };\n\n  async function uploadPdfToGoogleDrive(pdfBlob, label) {\n    const _lbl = label ? (String(label).replace(/[^a-z0-9]+/gi, \"_\") + \"_\") : \"\";\n    const filename = `memories_nutshell_${_lbl}${DMB_SESSION_ID}-${++dmbBookCounter}.pdf`;\n\n    // \u2500\u2500 Architecture: Drive Resumable Upload \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // We use Google Drive's native resumable-upload API (via Apps Script as proxy).\n    // Each chunk is forwarded directly from browser \u2192 Apps Script \u2192 Drive byte-range.\n    // This eliminates ALL of the old failure modes:\n    //   \u2713 No temp files in Drive (no \"Missing chunk N\" race condition)\n    //   \u2713 No in-memory base64 concatenation (no OOM for large books)\n    //   \u2713 No 6-minute Apps Script timeout during finalize\n    //   \u2713 Works for any size PDF (1 GB+)\n    //   \u2713 If a chunk fails, Drive reports exactly where to resume\n    //\n    // CHUNK_BYTES must be a multiple of 256 KB as required by Drive's resumable API.\n    const CHUNK_BYTES = 3 * 256 * 1024;  // 768 KB \u2014 valid multiple of 256 KB\n    const totalSize   = pdfBlob.size;\n    const totalChunks = Math.ceil(totalSize / CHUNK_BYTES);\n\n    // \u2500\u2500 Progress helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    const _progWrap  = document.getElementById(\"dmbUploadProgressWrap\");\n    const _progBar   = document.getElementById(\"dmbUploadProgressBar\");\n    const _progLabel = document.getElementById(\"dmbUploadProgressLabel\");\n    const _progMsg   = document.getElementById(\"dmbPdfOverlayMsg\");\n    const _uploadMsgs = [\n      \"Sending your memories our way\",\n      \"Packaging your precious moments\",\n      \"Almost there, hang tight\",\n      \"Wrapping up your story\",\n      \"Delivering your book with care\",\n      \"Just a little more\",\n      \"Your memories are on their way\",\n      \"Sealing every beautiful moment\"\n    ];\n    function _setProgress(done, total, phase) {\n      const pct = Math.round((done / total) * 100);\n      if (_progWrap)  _progWrap.style.display  = \"block\";\n      if (_progBar)   _progBar.style.width      = pct + \"%\";\n      if (_progLabel) _progLabel.textContent    = phase || _uploadMsgs[Math.floor((done / total) * (_uploadMsgs.length - 1))];\n      if (_progMsg)   _progMsg.textContent      = (\"Sending your \" + (window.DMB_MAGAZINE_MODE ? \"magazine\" : \"book\"));\n    }\n    function _hideProgress() {\n      if (_progWrap) _progWrap.style.display = \"none\";\n    }\n\n    // \u2500\u2500 Step 1: Initialise the Drive resumable-upload session \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    _setProgress(0, totalChunks + 1, \"Preparing to send\");\n    const initRes    = await postToScript({ action: \"init_upload\", filename, totalSize });\n    const sessionUri = initRes.sessionUri;\n\n    // \u2500\u2500 Step 2: Send chunks \u2014 each forwarded directly to Drive \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    let byteStart = 0;\n    for (let i = 0; i < totalChunks; i++) {\n      _setProgress(i + 1, totalChunks + 1, _uploadMsgs[i % _uploadMsgs.length]);\n      const slice    = pdfBlob.slice(byteStart, byteStart + CHUNK_BYTES);\n      const b64chunk = await blobToBase64(slice);\n      const raw      = b64chunk.includes(\",\") ? b64chunk.split(\",\")[1] : b64chunk;\n\n      const chunkRes = await postToScript({\n        action:     \"upload_chunk\",\n        sessionUri,           // Apps Script forwards this chunk to Drive directly\n        data:       raw,\n        byteStart,\n        totalSize\n      });\n\n      byteStart += slice.size;\n\n      if (chunkRes.complete) {\n        // Drive confirmed the upload is fully complete on this chunk.\n        _setProgress(totalChunks + 1, totalChunks + 1, \"Your book is safe with us! \uD83C\uDF89\");\n        if (_progMsg) _progMsg.textContent = \"Almost ready\";\n        _hideProgress();\n        return chunkRes;  // { ok, id, viewUrl, downloadUrl }\n      }\n    }\n\n    // If we exit the loop without seeing complete:true, something went wrong.\n    _hideProgress();\n    throw new Error(\"Upload finished all chunks but Drive did not confirm completion.\");\n  }\n\n  // \u2500\u2500 Streaming print path (large books) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // A big book (e.g. 300 pages) can exhaust a phone's memory if the whole PDF is\n  // built in the tab. For large books we instead render page-by-page (the exact\n  // same pixels \u2014 see __dmbBuildPdfStreaming/addCanvasPage), upload each finished\n  // page to R2 via the Worker, then ask the Cloud Run \"stapler\" to assemble the\n  // final PDF and store it in Drive \u2014 identical output, but peak memory \u2248 one page.\n  // Falls back to the in-browser path (below, in Add to Cart) on any failure.\n  const _DMB_STREAM_MIN_PAGES = (typeof window.DMB_STREAM_MIN_PAGES === \"number\")\n    ? window.DMB_STREAM_MIN_PAGES : 0;   // 0 = ALL books stream (server saves to Drive,\n    // bypassing the browser\u2192Apps Script upload a 3rd-party app is CORS-blocking). Set a\n    // higher number via window.DMB_STREAM_MIN_PAGES to restrict streaming to big books.\n\n  function _dmbDataUrlToBlob(durl) {\n    const comma = durl.indexOf(\",\");\n    const bin = atob(durl.slice(comma + 1));\n    const bytes = new Uint8Array(bin.length);\n    for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);\n    return new Blob([bytes], { type: \"image/jpeg\" });\n  }\n\n  // \u2500\u2500 Proof previews \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // The proof screen is the last thing a customer sees before paying, and it used to\n  // download the PRINT-resolution page for every sheet: an A4 page at 250-300 DPI and\n  // JPEG 0.96 is 2-3 MB, over a workers.dev link measured at 0.42 MB/s. That is several\n  // seconds per page turn, on the one screen where hesitation costs an order.\n  //\n  // So each page is ALSO encoded small, off the very same canvas, in the same pass. It\n  // is not a re-render and not a different source - it is the identical image, scaled -\n  // so the proof still shows exactly what will print. 1600px on the long edge is sharp\n  // at 2x on any sheet the proof screen can display, and lands ~15x smaller.\n  const _DMB_PROOF_PREV_DIM = 1600, _DMB_PROOF_PREV_Q = 0.78;\n\n  // Best effort, deliberately: a missing preview costs that page a slower proof (the\n  // Worker serves the full-resolution page instead), while a failed ORDER because a\n  // thumbnail would not upload would be absurd. One short attempt, never throws.\n  async function _dmbUploadRenderPreview(proxy, index, blob, fp, qsBase) {\n    try {\n      // Two callers, two URL shapes. The renderer stages with a job token; the browser\n      // fallback stages with the design's own id + sid. Both land in the same /v/ prefix\n      // on the Worker, and handleRenderProofPage reads whichever exists.\n      const u = (_dmbRenderMode() && _DMB_RM && _DMB_RM.token)\n        ? (proxy + \"/render-stage?t=\" + encodeURIComponent(_DMB_RM.token) +\n           \"&i=\" + index + \"&prev=1\" + (fp ? \"&fp=\" + encodeURIComponent(fp) : \"\"))\n        : (qsBase ? (proxy + \"/render-page?\" + qsBase + \"&i=\" + index + \"&prev=1\") : \"\");\n      if (!u) return false;\n      const ctrl = new AbortController();\n      const timer = setTimeout(function () { try { ctrl.abort(); } catch (e) {} }, 20000);\n      const r = await fetch(u, { method: \"POST\", headers: { \"content-type\": \"image/jpeg\" }, body: blob, signal: ctrl.signal });\n      clearTimeout(timer);\n      return !!(r && r.ok);\n    } catch (e) { return false; }\n  }\n\n  // Upload one finished page; retry a few times (a dropped page must not fail the order).\n  async function _dmbUploadRenderPage(proxy, qsBase, index, blob, fp, wMm, hMm) {\n    // fp: content fingerprint -> the Worker stores the page under p/<fp>.jpg (content-\n    // addressed, reusable by a later job); no fp -> the legacy positional <i>.jpg.\n    const _fpq = fp ? (\"&fp=\" + encodeURIComponent(fp) + \"&w=\" + encodeURIComponent(String(wMm || \"\")) + \"&h=\" + encodeURIComponent(String(hMm || \"\"))) : \"\";\n    const u = (_dmbRenderMode() && _DMB_RM.token)\n      ? (proxy + \"/render-stage?t=\" + encodeURIComponent(_DMB_RM.token) + \"&i=\" + index + _fpq)\n      : (proxy + \"/render-page?\" + qsBase + \"&i=\" + index);\n    let lastErr = null;\n    // Slow-connection hardening: each attempt gets a generous timeout so a STALLED upload\n    // aborts and retries instead of hanging the whole order forever, and we allow many\n    // retries so a flaky ~2 Mbps link still gets every page through. A page (~200\u2013500 KB at\n    // 250 DPI / JPEG 0.85) uploads in a few seconds even at 2 Mbps; 90 s tolerates a very\n    // congested link before a single attempt is abandoned.\n    const PER_ATTEMPT_MS = 90000;\n    const MAX_ATTEMPTS   = 8;\n    for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {\n      let ctrl = null, timer = null;\n      try {\n        ctrl = new AbortController();\n        timer = setTimeout(() => { try { ctrl.abort(); } catch (e) {} }, PER_ATTEMPT_MS);\n        const r = await fetch(u, { method: \"POST\", headers: { \"content-type\": \"image/jpeg\" }, body: blob, credentials: \"same-origin\", signal: ctrl.signal });\n        if (timer) clearTimeout(timer);\n        if (r.ok) return true;\n        lastErr = new Error(\"HTTP \" + r.status);\n      } catch (e) { if (timer) clearTimeout(timer); lastErr = e; }\n      // Capped backoff: keep retrying a flaky link without long stalls between tries.\n      await new Promise((res) => setTimeout(res, Math.min(attempt * 1200, 6000)));\n    }\n    throw new Error(\"page \" + index + \" upload failed after \" + MAX_ATTEMPTS + \" tries: \" + (lastErr && lastErr.message));\n  }\n\n  // Assemble the staged pages into one PDF \u2014 SAME-ORIGIN (through the App Proxy), so\n  // nothing is cross-origin for a customer's extensions/ad-blockers to interfere with.\n  // Start returns instantly; the stapler does the work in the background and calls the\n  // Worker back; we poll for the result. Each request is short. Returns { id, viewUrl, downloadUrl }.\n  async function _dmbAssembleViaStapler(proxy, qsBase, pages, onStatus) {\n    if (typeof onStatus === \"function\") onStatus((\"Binding your \" + (window.DMB_MAGAZINE_MODE ? \"magazine\" : \"book\")));\n    const startRes = await fetch(proxy + \"/assemble?\" + qsBase, {\n      method: \"POST\", headers: { \"content-type\": \"application/json\" }, credentials: \"same-origin\",\n      body: JSON.stringify({ pages: pages }),\n    });\n    let startData = null; try { startData = await startRes.json(); } catch (e) {}\n    if (!startRes.ok || !startData || startData.status !== \"pending\") {\n      throw new Error(\"assemble start failed: \" + ((startData && startData.error) || (\"status \" + startRes.status)));\n    }\n    const deadline = Date.now() + 12 * 60 * 1000;   // big books can take a few minutes\n    let ticks = 0;\n    while (Date.now() < deadline) {\n      await new Promise((r) => setTimeout(r, 3000));\n      let st = null;\n      try { st = await (await fetch(proxy + \"/assemble-status?\" + qsBase, { credentials: \"same-origin\" })).json(); }\n      catch (e) { continue; }   // transient \u2014 keep polling\n      if (st && st.status === \"done\" && st.id) return st;\n      if (st && st.status === \"error\") throw new Error(\"assemble failed: \" + (st.error || \"unknown\"));\n      if (typeof onStatus === \"function\" && (++ticks % 4 === 0)) onStatus((\"Binding your \" + (window.DMB_MAGAZINE_MODE ? \"magazine\" : \"book\")));\n    }\n    throw new Error(\"assemble timed out\");\n  }\n\n  // Render \u2192 upload each page \u2192 assemble. Returns { id, viewUrl, downloadUrl } shaped\n  // exactly like uploadPdfToGoogleDrive so the Add-to-Cart code is unchanged after this.\n  async function _dmbStreamingBuildAndUpload(label, excludePages, onProgress, onStatus, onHide) {\n    const proxy = window.DMB_DESIGNS_PROXY || \"/apps/designs\";\n    const designId = getCustomerSaveKey().replace(/^dmb_progress_/, \"\");\n    // A session id gives guests a stable owner; logged-in customers use their customer\n    // id (from the App-Proxy signature). render-page + assemble share the SAME owner \u2192 token.\n    let sid = \"\"; try { sid = _dmbPhotoBackup.guestId() || \"\"; } catch (e) {}\n    if (!sid) sid = \"s\" + Date.now().toString(36) + Math.random().toString(36).slice(2, 8);\n    const qsBase = \"id=\" + encodeURIComponent(designId) + \"&sid=\" + encodeURIComponent(sid);\n\n    const manifest = await window.__dmbBuildPdfStreaming(async function (pg) {\n      const blob = _dmbDataUrlToBlob(pg.jpeg);\n      await _dmbUploadRenderPage(proxy, qsBase, pg.index, blob);\n      // Proof preview, best effort and AFTER the real page is safely staged. This path\n      // renders in the customer's own tab, so the small copy comes off the very same\n      // canvas the printable page came from - same image, scaled.\n      if (pg.preview) {\n        try { await _dmbUploadRenderPreview(proxy, pg.index, _dmbDataUrlToBlob(pg.preview), \"\", qsBase); } catch (e) {}\n      }\n    }, onProgress, excludePages, { wantPreview: true });\n\n    const pages = (manifest && manifest.pages ? manifest.pages : [])\n      .map((p) => ({ index: p.index, wMm: p.wMm, hMm: p.hMm }));\n    if (!pages.length) throw new Error(\"streaming produced no pages\");\n\n    // \u2500\u2500 PROOF, on the fallback path too \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // The proof used to live ONLY inside the server render. That render fails SOFT by\n    // design so an order is never blocked, which meant any server-side problem silently\n    // dropped the customer onto this path - and this path bound the book and added it to\n    // the cart without ever showing them a page. The owner's rule is that nobody pays\n    // for a book they have not seen, and a rule that quietly disappears on the error\n    // path is not a rule.\n    //\n    // It can be the SAME screen because this path stages the same way: every page is\n    // already in R2 under the design's own token when we get here, and the Worker's\n    // handleRenderProofPage falls back to the positional key when there is no active\n    // render job. Nothing is assembled until they approve. (2026-09-18)\n    if (typeof _dmbSrProofScreen === \"function\") {\n      try { if (onHide) onHide(); } catch (e) {}\n      let _choice = \"approve\";\n      try {\n        _choice = await _dmbSrProofScreen(pages, qsBase, null);\n      } catch (e) {\n        // A broken proof must not cost the order. Bind what they already approved by\n        // getting this far, exactly as this path behaved before the proof existed.\n        _choice = \"approve\";\n      }\n      if (_choice !== \"approve\") return \"__dmb_proof_rejected__\";\n      if (typeof onStatus === \"function\") {\n        try { onStatus(\"Binding your \" + (window.DMB_MAGAZINE_MODE ? \"magazine\" : \"book\")); } catch (e) {}\n      }\n    }\n\n    // The stapler deletes the staged pages after assembly (via the Worker callback).\n    const result = await _dmbAssembleViaStapler(proxy, qsBase, pages, onStatus);\n    return { id: result.id, viewUrl: result.viewUrl, downloadUrl: result.downloadUrl };\n  }\n\n  function mergeFilesKeepOld(existingArr, newlyPickedArr) {\n    const out = Array.isArray(existingArr) ? existingArr.slice() : [];\n    const newArr = Array.isArray(newlyPickedArr) ? newlyPickedArr : [];\n\n    const key = (f) => `${f.name}__${f.size}__${f.lastModified}`;\n    const seen = new Set(out.map(key));\n\n    newArr.forEach((f) => {\n      const k = key(f);\n      if (!seen.has(k)) {\n        out.push(f);\n        seen.add(k);\n        // Start decoding this photo's dimensions in the background now (upload\n        // step), so the builder/cover doesn't have to wait for it on open.\n        try { _dmbWarmMeta(f); } catch (e) {}\n        // INSTANT BACKUP: stream this photo to R2 the moment it's picked \u2014\n        // a phone-tab crash during the upload step can no longer lose it.\n        try { _dmbInstantBackup.enqueue(f); } catch (e) {}\n      }\n    });\n\n    return out;\n  }\n\n  function showReuseNotice() {\n    let n = document.getElementById(\"dmbReuseNotice\");\n\n    if (!n) {\n      n = document.createElement(\"div\");\n      n.id = \"dmbReuseNotice\";\n      n.style.position = \"fixed\";\n      n.style.left = \"50%\";\n      n.style.bottom = \"20px\";\n      n.style.transform = \"translateX(-50%)\";\n      n.style.zIndex = \"99999\";\n      n.style.background = \"rgba(17,17,17,0.92)\";\n      n.style.color = \"#fff\";\n      n.style.padding = \"10px 14px\";\n      n.style.borderRadius = \"999px\";\n      n.style.fontSize = \"12px\";\n      n.style.fontWeight = \"800\";\n      n.style.boxShadow = \"0 8px 24px rgba(0,0,0,0.2)\";\n      document.body.appendChild(n);\n    }\n\n    n.textContent = \"You have used this photo before (reused).\";\n    n.style.display = \"block\";\n    n.style.opacity = \"1\";\n\n    clearTimeout(showReuseNotice._t);\n    showReuseNotice._t = setTimeout(() => {\n      if (n) n.style.opacity = \"0\";\n      setTimeout(() => { if (n) n.style.display = \"none\"; }, 180);\n    }, 1400);\n  }\n\n  function enforceMax(filesArr, maxAllowed) {\n    if (!maxAllowed) return;\n    if (filesArr.length > maxAllowed) filesArr.length = maxAllowed;\n  }\n\n  function setDragPayload(e, payload) {\n    DMB_CURRENT_DRAG = payload;\n    const json = JSON.stringify(payload);\n    e.dataTransfer.setData(\"application/x-dmb\", json);\n    e.dataTransfer.setData(\"text/x-dmb\", json);\n    e.dataTransfer.setData(\"text/plain\", json);\n    e.dataTransfer.effectAllowed = \"copyMove\";\n  }\n\n  function getDragPayload(e) {\n    const dt = e.dataTransfer;\n    if (!dt) return DMB_CURRENT_DRAG;\n\n    let raw =\n      dt.getData(\"application/x-dmb\") ||\n      dt.getData(\"text/x-dmb\") ||\n      dt.getData(\"text/plain\") ||\n      \"\";\n\n    raw = (raw || \"\").trim();\n    if (!raw) return DMB_CURRENT_DRAG;\n\n    if (raw[0] !== \"{\") return { type: \"photo\", photoId: raw };\n\n    try {\n      return JSON.parse(raw);\n    } catch {\n      return DMB_CURRENT_DRAG;\n    }\n  }\n\n  // \u2500\u2500 Touch drag-and-drop for photos \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // HTML5 draggable=true only works with a mouse; on a phone/iPad a drag never\n  // starts and the long-press selects the whole UI instead. This reuses the\n  // existing drop handlers by dispatching a synthetic drop at the finger point:\n  // getDragPayload() falls back to the DMB_CURRENT_DRAG global when there's no real\n  // dataTransfer, and every drop reads payload.photoId (set here) before ever\n  // touching dataTransfer, so a fake dataTransfer + clientX/Y is enough.\n  function _dmbSyntheticDnD(type, x, y) {\n    const el = document.elementFromPoint(x, y);\n    if (!el) return null;\n    const ev = new Event(type, { bubbles: true, cancelable: true });\n    const fakeDT = { getData: function () { return \"\"; }, setData: function () {}, clearData: function () {},\n      setDragImage: function () {}, dropEffect: \"copy\", effectAllowed: \"copyMove\", files: [], items: [], types: [] };\n    try { Object.defineProperty(ev, \"dataTransfer\", { value: fakeDT, configurable: true }); } catch (e) {}\n    try { Object.defineProperty(ev, \"clientX\", { value: x, configurable: true }); } catch (e) {}\n    try { Object.defineProperty(ev, \"clientY\", { value: y, configurable: true }); } catch (e) {}\n    el.dispatchEvent(ev);\n    return el;\n  }\n\n  // Wire a pointer-based drag onto a photo-tray thumbnail (touch only; mouse keeps\n  // native HTML5 drag). A finger press-and-move lifts a floating ghost that follows\n  // the finger; releasing over a page/cover frame drops the photo in.\n  function _dmbWireThumbTouchDrag(t, ph) {\n    let sx = 0, sy = 0, pid = null, dragging = false, ghost = null;\n    function cleanup() {\n      document.removeEventListener(\"pointermove\", onMove);\n      document.removeEventListener(\"pointerup\", onUp);\n      document.removeEventListener(\"pointercancel\", onUp);\n    }\n    function onMove(e) {\n      if (e.pointerId !== pid) return;\n      if (!dragging) {\n        const dx = e.clientX - sx, dy = e.clientY - sy;\n        const adx = Math.abs(dx), ady = Math.abs(dy);\n        // Start the drag on the FIRST few px of a vertical(-ish) move so it feels\n        // instant \u2014 no hold, no dead zone. `touch-action:pan-x` already routes a\n        // clearly-horizontal swipe to the tray scroll, but bail here too as a guard.\n        if (adx < 5 && ady < 5) return;                          // ignore micro-jitter only\n        if (adx > ady) { cleanup(); pid = null; return; }        // horizontal \u2192 tray scroll\n        dragging = true;                                         // vertical \u2192 lift NOW\n        DMB_CURRENT_DRAG = { type: \"photo\", photoId: ph.id };\n        document.body.classList.add(\"dmb-dragging-photo\");\n        ghost = document.createElement(\"div\");\n        ghost.className = \"dmb-drag-ghost\";\n        const im = document.createElement(\"img\");\n        const _tImg = t.querySelector(\"img\");\n        im.src = ph.thumbUrl || (typeof _dmbThumbCache !== \"undefined\" && _dmbThumbCache[ph.id]) || (_tImg && _tImg.src) || \"\";\n        ghost.appendChild(im);\n        document.body.appendChild(ghost);\n      }\n      e.preventDefault();\n      if (ghost) { ghost.style.left = e.clientX + \"px\"; ghost.style.top = e.clientY + \"px\"; }\n    }\n    function onUp(e) {\n      if (e.pointerId !== pid) return;\n      cleanup();\n      if (!dragging) { pid = null; return; }   // was a tap \u2192 thumb's own handler opens the menu\n      e.preventDefault();\n      const x = e.clientX, y = e.clientY;\n      if (ghost) ghost.style.display = \"none\";   // so elementFromPoint sees the drop target\n      try { _dmbSyntheticDnD(\"dragover\", x, y); } catch (_) {}\n      try { _dmbSyntheticDnD(\"drop\", x, y); } catch (_) {}\n      if (ghost) { ghost.remove(); ghost = null; }\n      document.body.classList.remove(\"dmb-dragging-photo\");\n      DMB_CURRENT_DRAG = null;\n      dragging = false; pid = null;\n    }\n    t.addEventListener(\"pointerdown\", function (e) {\n      if (e.pointerType === \"mouse\") return;                       // desktop uses native draggable\n      if (e.target.closest && e.target.closest(\".dmb-photo-hover-menu\")) return;\n      pid = e.pointerId; sx = e.clientX; sy = e.clientY; dragging = false;\n      document.addEventListener(\"pointermove\", onMove, { passive: false });\n      document.addEventListener(\"pointerup\", onUp);\n      document.addEventListener(\"pointercancel\", onUp);\n    });\n  }\n\n\n  function syncCoverInputsFromState() {\n    if (coverColorInput) coverColorInput.value = coverState.color || \"#ffffff\";\n    if (coverColorSwatch) coverColorSwatch.style.background = coverState.color || \"#ffffff\";\n    updateCoverPreview();\n    if (typeof renderCoverTextList === \"function\") renderCoverTextList();\n  }\n\n  function renderCoverTextList() {\n    const list = document.getElementById(\"dmbCoverTextList\");\n    if (!list) return;\n    list.innerHTML = \"\";\n    if (!Array.isArray(editor.coverUserTexts) || !editor.coverUserTexts.length) {\n      const empty = document.createElement(\"div\");\n      empty.className = \"dmb-cover-text-empty\";\n      empty.textContent = 'No text added yet. Click \"Add text\" on the cover.';\n      list.appendChild(empty);\n      return;\n    }\n    editor.coverUserTexts.forEach(({ id, zone }) => {\n      const saved = coverState.textStyles?.[id];\n      const row = document.createElement(\"div\");\n      row.className = \"dmb-cover-text-row\";\n\n      const inp = document.createElement(\"input\");\n      inp.type = \"text\";\n      inp.className = \"dmb-input\";\n      inp.value = saved?.text || \"\";\n      inp.placeholder = \"Enter text\u2026\";\n      inp.setAttribute(\"data-text-id\", id);\n      inp.addEventListener(\"input\", () => {\n        if (!coverState.textStyles) coverState.textStyles = {};\n        if (!coverState.textStyles[id]) coverState.textStyles[id] = {};\n        coverState.textStyles[id].text = inp.value;\n        const liveNode = document.getElementById(id);\n        if (liveNode) { liveNode.textContent = inp.value; }\n      });\n\n      const zoneLabel = document.createElement(\"span\");\n      zoneLabel.className = \"dmb-cover-text-zone\";\n      zoneLabel.textContent = zone;\n\n      const delBtn = document.createElement(\"button\");\n      delBtn.type = \"button\";\n      delBtn.className = \"dmb-cover-text-del\";\n      delBtn.title = \"Remove text\";\n      delBtn.innerHTML = \"&#x2715;\";\n      delBtn.addEventListener(\"click\", () => {\n        pushUndoState(\"Delete Cover Text\");\n        editor.coverUserTexts = editor.coverUserTexts.filter((t) => t.id !== id);\n        if (coverState.textStyles) delete coverState.textStyles[id];\n        const liveNode = document.getElementById(id);\n        if (liveNode) liveNode.remove();\n        renderCoverTextList();\n        saveCoverDesignToStorage();\n      });\n\n      row.appendChild(inp);\n      row.appendChild(zoneLabel);\n      row.appendChild(delBtn);\n      list.appendChild(row);\n    });\n  }\n\n  // \u2500\u2500 Magazine page/spread plan \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // Magazines use a FIXED layout per size (not ceil(pages/2)): page 0 (single,\n  // editable) + N two-page spreads + 1 trailing single (editable) page. N is\n  // looked up by the selected \"pages\" variant value, giving:\n  //   totalPages   = 2\u00B7N + 1   (pages 1..2N are the N pairs; page 2N+1 is the single\n  //                             trailing page \u2014 odd, so it auto-renders alone)\n  //   totalSpreads = N + 1      (N pairs + the trailing single-page spread; page 0 is\n  //                             \"Spread 0\" shown on top of these)\n  // Price/variant still key off the dropdown value (the size), so this is layout-only.\n  // Books \u2014 and any magazine size not in the table \u2014 keep the old ceil(pages/2) model.\n  // Keyed by the actual custom-magazine \"pages\" option values (24/30/42/50/70/90).\n  // The 40/76/100 aliases are kept harmless in case a variant uses those numbers \u2014\n  // an unlisted size falls back to ceil(pages/2), which adds one extra spread.\n  var MAG_SPREADS_BY_SIZE = {\n    24: 11, 30: 15, 42: 19, 50: 25, 70: 33, 90: 43,\n    40: 19, 76: 33, 100: 43,\n  };\n\n  function _dmbPagePlan(selectedPages) {\n    var sp = Number(selectedPages) || 0;\n    if (window.DMB_MAGAZINE_MODE) {\n      var n = MAG_SPREADS_BY_SIZE[sp];\n      if (n) return { totalPages: n * 2 + 1, totalSpreads: n + 1 };\n    }\n    // Books, or an unknown magazine size \u2192 previous behavior (pairs only, no trailing single).\n    return { totalPages: sp, totalSpreads: Math.max(1, Math.ceil(sp / 2)) };\n  }\n\n  function initEmptyBookFromPages() {\n    const selectedPages = Number(pagesEl?.value || 0);\n    if (!selectedPages) return;\n\n    // Reset extra spreads whenever base page count changes\n    editor.extraSpreads = 0;\n    const _plan = _dmbPagePlan(selectedPages);\n    editor.totalPages = _plan.totalPages;\n    editor.editablePages = Math.max(0, editor.totalPages - 1);\n    editor.totalSpreads = _plan.totalSpreads;\n    editor.currentSpread = 0;\n\n    // Page-count changes are NON-DESTRUCTIVE. Changing the count must NOT wipe the\n    // customer's work: keep the cover (page-count-independent) AND every existing\n    // page's content (shapes, photos, text, templates, backgrounds). We only ADD\n    // empty structure for newly-added pages and TRIM pages that no longer exist.\n    // (Previously this reset all page maps + the cover to {} on every change, so a\n    // shape/photo/text vanished whenever the customer adjusted the page count.)\n    editor.pageSlots       = editor.pageSlots       || {};\n    editor.pageRot         = editor.pageRot         || {};\n    editor.pageTemplate    = editor.pageTemplate    || {};\n    editor.pageFrames      = editor.pageFrames      || {};\n    editor.pageFrameRot    = editor.pageFrameRot    || {};\n    editor.pageFrameShapes = editor.pageFrameShapes || {};\n    editor.pageObjects     = editor.pageObjects     || {};\n    editor.pageBackgrounds = editor.pageBackgrounds || {};\n    if (!Array.isArray(editor.coverObjects)) editor.coverObjects = [];\n\n    // Trim any content that belongs to pages beyond the new page count.\n    [editor.pageSlots, editor.pageRot, editor.pageTemplate, editor.pageFrames,\n     editor.pageFrameRot, editor.pageFrameShapes, editor.pageObjects, editor.pageBackgrounds\n    ].forEach(function (map) {\n      Object.keys(map).forEach(function (k) {\n        if (Number(k) > editor.totalPages) delete map[k];\n      });\n    });\n\n    // Add empty structure for any newly-added pages; leave existing pages intact.\n    for (let p = 1; p <= editor.totalPages; p++) {\n      if (!(p in editor.pageSlots))    editor.pageSlots[p]    = { A: null, B: null };\n      if (!(p in editor.pageRot))      editor.pageRot[p]      = { A: 0, B: 0 };\n      if (!(p in editor.pageTemplate)) editor.pageTemplate[p] = null;\n      if (!(p in editor.pageFrames))   editor.pageFrames[p]   = {};\n      if (!(p in editor.pageFrameRot)) editor.pageFrameRot[p] = {};\n      if (!Array.isArray(editor.pageObjects[p])) editor.pageObjects[p] = [];\n    }\n\n    if (bookMeta) {\n      bookMeta.textContent = `${selectedPages} pages (${editor.editablePages} to design) \u2022 ${editor.photos.filter((p) => !_dmbIsCropDerived(p)).length} photos`;\n    }\n\n    // Show the \"Add spread\" button now that a page count is chosen\n    _syncSpreadAdderUI();\n\n    renderThumbsRail();\n\n    if (builderMode === \"cover\") {\n      renderCoverCanvas();\n    } else {\n      renderSpread();\n    }\n  }\n\n  // \u2500\u2500 Extra spreads: add / remove 2 pages at +$1.99 each \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // Callers: \"+\" and \"\u2212\" buttons in the spread adder row.\n  // WARNING: always call updatePrice() + refreshPhotoRule() after changing\n  //          editor.extraSpreads so the displayed price + photo rule stay in sync.\n\n  function addExtraSpread() {\n    if (!pagesEl?.value) { alert(\"Please select a page count first.\"); return; }\n    pushUndoState(\"Add Extra Spread\");   // snapshot BEFORE mutating so undo restores the prior state\n    const newL = editor.totalPages + 1;\n    const newR = editor.totalPages + 2;\n    // Initialise the two new pages exactly like initEmptyBookFromPages does\n    [newL, newR].forEach(p => {\n      editor.pageSlots[p]     = { A: null, B: null };\n      editor.pageRot[p]       = { A: 0, B: 0 };\n      editor.pageTemplate[p]  = null;\n      editor.pageFrames[p]    = {};\n      editor.pageFrameRot[p]  = {};\n      editor.pageObjects[p]   = [];\n    });\n    editor.totalPages    += 2;\n    editor.editablePages += 2;\n    editor.totalSpreads  += 1;\n    editor.extraSpreads  += 1;\n    updatePrice();\n    refreshPhotoRule();\n    _syncSpreadAdderUI();\n    renderThumbsRail();\n    renderBuilderCanvas();\n  }\n\n  function removeExtraSpread() {\n    if ((editor.extraSpreads || 0) <= 0) return;\n    pushUndoState(\"Remove Extra Spread\");   // snapshot BEFORE mutating\n    const remL = editor.totalPages - 1;\n    const remR = editor.totalPages;\n    // Navigate away from the spread being removed\n    const maxAllowedSpread = Math.max(0, editor.totalSpreads - 2);\n    if (editor.currentSpread >= maxAllowedSpread) {\n      editor.currentSpread = Math.max(0, maxAllowedSpread - 1);\n    }\n    // Clean up removed pages\n    [remL, remR].forEach(p => {\n      delete editor.pageSlots[p];\n      delete editor.pageRot[p];\n      delete editor.pageTemplate[p];\n      delete editor.pageFrames[p];\n      delete editor.pageFrameRot[p];\n      delete editor.pageObjects[p];\n    });\n    editor.totalPages    -= 2;\n    editor.editablePages -= 2;\n    editor.totalSpreads  -= 1;\n    editor.extraSpreads  -= 1;\n    updatePrice();\n    refreshPhotoRule();\n    _syncSpreadAdderUI();\n    renderThumbsRail();\n    renderBuilderCanvas();\n  }\n\n  // Sync the spread adder row: update counter, cost label, and button states.\n  // The row is always visible inside the builder \u2014 no show/hide needed here.\n  function _syncSpreadAdderUI() {\n    const countEl   = $(\"dmbSpreadCountEl\");\n    const costEl    = $(\"dmbSpreadCostEl\");\n    const removeBtn = $(\"dmbRemoveSpreadBtn\");\n    const n = editor.extraSpreads || 0;\n    if (countEl) countEl.textContent = n;\n    if (costEl) {\n      if (n > 0) {\n        costEl.textContent = \"+\" + fmtMoney(n * EXTRA_SPREAD_PRICE);\n        costEl.style.display = \"inline\";\n      } else {\n        costEl.style.display = \"none\";\n      }\n    }\n    if (removeBtn) removeBtn.disabled = n <= 0;\n  }\n\n\n  // \u2500\u2500 Recently-used colors \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // Shared, persisted palette of colors the customer has applied (cover / page\n  // background + text). Every color picker shows these as one-tap swatches, with\n  // the current color highlighted. A swatch is applied by writing the value onto\n  // the real <input type=color> and dispatching input/change, so the existing\n  // apply logic runs unchanged.\n  const DMB_RECENT_COLORS_KEY = \"dmbRecentColors\";\n  function dmbGetRecentColors() {\n    try { const a = JSON.parse(localStorage.getItem(DMB_RECENT_COLORS_KEY) || \"[]\"); return Array.isArray(a) ? a : []; }\n    catch (e) { return []; }\n  }\n  function _dmbNormHex(c) {\n    if (!c) return \"\";\n    c = String(c).trim().toLowerCase();\n    if (/^#[0-9a-f]{6}$/.test(c)) return c;\n    if (/^#[0-9a-f]{3}$/.test(c)) return \"#\" + c[1] + c[1] + c[2] + c[2] + c[3] + c[3];\n    const m = c.match(/^rgba?\\(([^)]+)\\)/);\n    if (m) {\n      const p = m[1].split(\",\").map((x) => parseInt(x, 10));\n      if (p.length >= 3 && p.slice(0, 3).every((n) => !isNaN(n))) {\n        return \"#\" + p.slice(0, 3).map((n) => Math.max(0, Math.min(255, n)).toString(16).padStart(2, \"0\")).join(\"\");\n      }\n    }\n    return \"\";\n  }\n  function dmbAddRecentColor(c) {\n    const hex = _dmbNormHex(c);\n    if (!hex) return;\n    let list = dmbGetRecentColors().filter((x) => x !== hex);\n    list.unshift(hex);\n    list = list.slice(0, 10);\n    try { localStorage.setItem(DMB_RECENT_COLORS_KEY, JSON.stringify(list)); } catch (e) {}\n    dmbRefreshRecentSwatches();\n  }\n  function dmbRefreshRecentSwatches() {\n    document.querySelectorAll(\".dmb-recent-swatches\").forEach((strip) => {\n      if (strip._dmbRender) strip._dmbRender();\n    });\n  }\n  // Bring a design's saved colour palette onto THIS device (merge synced-first with any\n  // local palette). The palette rides inside the design snapshot so it syncs cross-device.\n  function _dmbApplyRecentColors(snap) {\n    try {\n      const rc = snap && snap.recentColors;\n      if (!Array.isArray(rc) || !rc.length) return;\n      const merged = [];\n      rc.concat(dmbGetRecentColors()).forEach((h) => { const n = _dmbNormHex(h); if (n && merged.indexOf(n) === -1) merged.push(n); });\n      localStorage.setItem(DMB_RECENT_COLORS_KEY, JSON.stringify(merged.slice(0, 10)));\n      dmbRefreshRecentSwatches();\n    } catch (e) {}\n  }\n  // Attach a recent-colors swatch strip to a color <input>. `container` is where the\n  // strip is appended (defaults to the input's parent). Idempotent per input.\n  function dmbEnhanceColorInput(input, container) {\n    if (!input || input._dmbColorEnhanced) return;\n    const host = container || input.parentElement;\n    if (!host) return;\n    input._dmbColorEnhanced = true;\n    // A background strip (cover / page) defers to the selected text: if a text box\n    // is selected, clicking a swatch colors the TEXT, not the background. The text\n    // toolbar's own strip always colors text.\n    const isBackground = input.id !== \"dmbTextColor\";\n    const strip = document.createElement(\"div\");\n    strip.className = \"dmb-recent-swatches\";\n    host.appendChild(strip);\n    const render = () => {\n      const colors = dmbGetRecentColors();\n      const cur = _dmbNormHex(input.value);\n      strip.innerHTML = \"\";\n      colors.forEach((hex) => {\n        const b = document.createElement(\"button\");\n        b.type = \"button\"; b.className = \"dmb-recent-swatch\"; b.title = hex;\n        b.style.background = hex;\n        if (hex === cur) b.classList.add(\"active\");\n        b.addEventListener(\"click\", (e) => {\n          e.preventDefault(); e.stopPropagation();\n          // Text selected + background strip \u2192 recolor the text instead.\n          if (isBackground && typeof selectedTextNode !== \"undefined\" && selectedTextNode) {\n            const tc = document.getElementById(\"dmbTextColor\");\n            if (tc) {\n              tc.value = hex;\n              tc.dispatchEvent(new Event(\"input\", { bubbles: true }));\n              tc.dispatchEvent(new Event(\"change\", { bubbles: true }));\n            }\n          } else {\n            input.value = hex;\n            input.dispatchEvent(new Event(\"input\", { bubbles: true }));\n            input.dispatchEvent(new Event(\"change\", { bubbles: true }));\n          }\n          dmbRefreshRecentSwatches();\n        });\n        strip.appendChild(b);\n      });\n      strip.style.display = colors.length ? \"\" : \"none\";\n    };\n    strip._dmbRender = render;\n    input.addEventListener(\"change\", () => dmbAddRecentColor(input.value));\n    // NOTE: do NOT seed the palette with the input's default/current colour \u2014 the\n    // saved palette must only ever hold colours the user actively CHOSE, never\n    // auto-generated defaults (per request). It grows only on a real 'change'.\n    render();\n  }\n\n  // iOS-safe colour picker. iOS Safari ignores a programmatic .click() on a\n  // hidden <input type=color>, so on phones the \"\uFF0B Custom colour\u2026\" buttons never\n  // opened the native wheel. Fix: overlay the REAL input transparently over its\n  // button so a genuine finger tap lands on the input itself and opens the picker\n  // (same pattern the cover-background swatch already uses). Mobile only \u2014 desktop\n  // keeps the .click() path. Idempotent per button. Listeners survive appendChild.\n  function _dmbColorTapFix(customEl, inputEl) {\n    try {\n      if (!customEl || !inputEl) return;\n      if (!(window.matchMedia && window.matchMedia(\"(max-width: 749px), (orientation: portrait) and (pointer: coarse) and (max-width: 1024px)\").matches)) return;\n      if (customEl._dmbColorTapWired) return;\n      customEl._dmbColorTapWired = true;\n      customEl.style.position = \"relative\";\n      if (inputEl.parentElement !== customEl) customEl.appendChild(inputEl);\n      inputEl.classList.add(\"dmb-color-overlay\");\n    } catch (e) {}\n  }\n\n  // \u2500\u2500 Text toolbar: colour-wheel + icon dropdowns \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // Tint the colour-wheel button's centre dot with the current text colour.\n  function _dmbSyncTextColorDot() {\n    const dot = document.getElementById(\"dmbTextColorDot\");\n    const inp = document.getElementById(\"dmbTextColor\");\n    if (dot && inp) dot.style.background = inp.value || \"#000000\";\n  }\n  // Same, for the frame toolbar's icon colour-wheel.\n  function _dmbSyncFrameIconDot() {\n    const dot = document.getElementById(\"dmbFrameIconColorDot\");\n    const inp = document.getElementById(\"dmbFrameIconColorInput\");\n    if (dot && inp) dot.style.background = inp.value || \"#000000\";\n  }\n  // Shape edit bar's Fill + Border colour-wheels. Fill shows a red \"no-fill\" slash\n  // when Outline-only is on.\n  function _dmbSyncShapeDots() {\n    const fEl = document.getElementById(\"dmbShapeFill\");\n    const sEl = document.getElementById(\"dmbShapeStroke\");\n    const fDot = document.getElementById(\"dmbShapeFillDot\");\n    const sDot = document.getElementById(\"dmbShapeStrokeDot\");\n    const outlineOnly = !!document.getElementById(\"dmbShapeOutlineOnly\")?.checked;\n    if (fDot) fDot.style.background = outlineOnly\n      ? \"linear-gradient(45deg, transparent 43%, #e5484d 43%, #e5484d 57%, transparent 57%), #fff\"\n      : ((fEl && fEl.value) || \"#6366f1\");\n    if (sDot && sEl) sDot.style.background = sEl.value || \"#000000\";\n  }\n  // Map edit bar's Fill + Border colour-wheels (Fill shows a \"no-fill\" slash when\n  // Transparent is on).\n  function _dmbSyncMapDots() {\n    const fEl = document.getElementById(\"dmbEditFill\");\n    const sEl = document.getElementById(\"dmbEditStroke\");\n    const fDot = document.getElementById(\"dmbEditFillDot\");\n    const sDot = document.getElementById(\"dmbEditStrokeDot\");\n    const transp = !!document.getElementById(\"dmbEditTransparentFill\")?.checked;\n    if (fDot) fDot.style.background = transp\n      ? \"linear-gradient(45deg, transparent 43%, #e5484d 43%, #e5484d 57%, transparent 57%), #fff\"\n      : ((fEl && fEl.value) || \"#d9d9d9\");\n    if (sDot && sEl) sDot.style.background = sEl.value || \"#111111\";\n  }\n  // Reflect the 3 hidden map checkboxes onto the visible Fill-mode segmented control\n  // (Solid / Outline / None) and the Borders toggle.\n  function _dmbSyncMapModeUI() {\n    const outline = !!document.getElementById(\"dmbEditShowOutline\")?.checked;\n    const transp  = !!document.getElementById(\"dmbEditTransparentFill\")?.checked;\n    const noBord  = !!document.getElementById(\"dmbEditHideProvinces\")?.checked;\n    const mode = outline ? \"outline\" : (transp ? \"none\" : \"solid\");\n    document.querySelectorAll(\"#dmbEditFillMode .dmb-map-seg-btn\").forEach((b) => {\n      b.classList.toggle(\"is-on\", b.dataset.fillmode === mode);\n    });\n    const bt = document.getElementById(\"dmbEditBordersBtn\");\n    if (bt) bt.setAttribute(\"aria-pressed\", noBord ? \"false\" : \"true\");\n  }\n  // Dock an object edit bar INTO the fixed ribbon (the sticky header strip),\n  // flowing inline in the context zone. The bar drops its own floating chrome\n  // (position/background/border) via the `.dmb-in-ribbon` class and rides along\n  // with the sticky ribbon on scroll \u2014 no manual repositioning needed.\n  function _dmbDockBar(bar) {\n    if (!bar) return;\n    // On phones the desktop top ribbon (.dmb-stage-header) is display:none, so the\n    // object menu docks into the mobile icon ribbon under the page-nav row instead.\n    const _mob = window.matchMedia && window.matchMedia(\"(max-width: 749px), (orientation: portrait) and (pointer: coarse) and (max-width: 1024px)\").matches;\n    const slot = (_mob && document.getElementById(\"dmbMshRibbon\")) ||\n                 document.getElementById(\"dmbRibbonContext\");\n    if (slot && bar.parentNode !== slot) slot.appendChild(bar);\n    bar.classList.add(\"dmb-in-ribbon\");\n    bar.style.position = \"\";\n    bar.style.left = \"\"; bar.style.top = \"\"; bar.style.right = \"\"; bar.style.bottom = \"\";\n    // Clear the inline display so the `.dmb-in-ribbon` layout (flex) governs the\n    // OPEN state; close/drag-hide re-set inline display:none, which then wins.\n    bar.style.display = \"\";\n    _dmbUpdateRibbonPlaceholder();\n  }\n  // Show the greyed \"select an object to edit\" placeholder only when no object\n  // bar is currently docked/open in the ribbon.\n  function _dmbUpdateRibbonPlaceholder() {\n    const ph = document.getElementById(\"dmbRibbonPlaceholder\");\n    if (!ph) return;\n    const _shown = (id) => { const b = document.getElementById(id); return b && b.style.display !== \"none\" && b.classList.contains(\"dmb-in-ribbon\"); };\n    const _open  = (id) => { const b = document.getElementById(id); return b && b.classList.contains(\"is-open\"); };\n    const anyOpen = _shown(\"dmbMapEditBar\") || _shown(\"dmbShapeEditBar\") ||\n                    _open(\"dmbTextToolbar\") || _open(\"dmbFrameToolbar\");\n    ph.hidden = !!anyOpen;\n    // Mobile: hide the idle Add/Page row while an object menu is docked in its place.\n    const idle = document.getElementById(\"dmbMshRibbonIdle\");\n    if (idle) idle.hidden = !!anyOpen;\n  }\n  // Mirror the hidden <select id=dmbTextAlign> onto the icon toggle button.\n  function _dmbSyncAlignIcon() {\n    const sel = document.getElementById(\"dmbTextAlign\");\n    const icon = document.getElementById(\"dmbTextAlignIcon\");\n    const menu = document.getElementById(\"dmbTextAlignMenu\");\n    if (!sel || !icon || !menu) return;\n    const val = sel.value || \"left\";\n    const opt = menu.querySelector('.dmb-align-opt[data-align=\"' + val + '\"]');\n    if (opt) icon.innerHTML = opt.innerHTML;\n    menu.querySelectorAll(\".dmb-align-opt\").forEach((b) =>\n      b.classList.toggle(\"active\", b.getAttribute(\"data-align\") === val));\n  }\n  // Close every text-toolbar popup except the one passed in.\n  function _dmbCloseTextMenus(except) {\n    [\"dmbTextColorPop\", \"dmbTextAlignMenu\", \"dmbTextMoreMenu\"].forEach((id) => {\n      const el = document.getElementById(id);\n      if (el && el !== except) el.hidden = true;\n    });\n  }\n\n  function initUnifiedTabs() {\n    const tabs = Array.from(document.querySelectorAll(\".dmb-icon-tab\"));\n    const panels = Array.from(document.querySelectorAll(\".dmb-side-panel\"));\n    const title = document.getElementById(\"dmbLeftTitle\");\n\n    const labels = {\n      images: \"Pictures\",\n      cover: \"Cover\",\n      layouts: \"Layouts\",\n      backgrounds: \"Backgrounds\",\n      stickers: \"Stickers\",\n      text: \"Text\"\n    };\n\n    tabs.forEach((tab) => {\n      tab.addEventListener(\"click\", () => {\n        const key = tab.getAttribute(\"data-tab\");\n        const _mshMobile = !!(window.matchMedia && window.matchMedia(\"(max-width: 749px), (orientation: portrait) and (pointer: coarse) and (max-width: 1024px)\").matches);\n\n        tabs.forEach((t) => t.classList.remove(\"active\"));\n        panels.forEach((p) => p.classList.remove(\"active\"));\n        tab.classList.add(\"active\");\n        const panel = document.querySelector(`.dmb-side-panel[data-panel=\"${key}\"]`);\n        if (panel) panel.classList.add(\"active\");\n        if (title) title.textContent = labels[key] || \"Panel\";\n        // Mark the active tab on the panel so CSS can hide the title bar on the Pictures\n        // tab (desktop) while keeping it on the other tabs.\n        try { const _lp = tab.closest(\".dmb-builder-shell\")?.querySelector(\".dmb-builder-leftpanel\") || document.querySelector(\".dmb-builder-leftpanel\"); if (_lp) _lp.setAttribute(\"data-active-tab\", key); } catch (e) {}\n        if (key === \"cover\") {\n          builderMode = \"cover\";\n          if (typeof syncBuilderModeButtons === \"function\") syncBuilderModeButtons();\n          if (typeof renderBuilderCanvas === \"function\") renderBuilderCanvas();\n        }\n        // Layouts tab: render the inline panel (target + categories + A4 thumbnails,\n        // direct apply \u2014 no popup) on BOTH mobile and desktop, for pages AND cover.\n        if (key === \"layouts\") {\n          if (_mshMobile) {\n            _mshBuildLayoutsPanel();\n          } else {\n            const t = (typeof window._dmbPendingLayoutTarget === \"string\") ? window._dmbPendingLayoutTarget : \"left\";\n            window._dmbPendingLayoutTarget = null;\n            _dmbBuildDesktopLayoutsPanel(t);\n          }\n        }\n      });\n    });\n\n    // Initial state: mark the active tab (Pictures by default) so the CSS hides its title.\n    try {\n      const _activeTab = document.querySelector(\".dmb-icon-tab.active\");\n      const _activeKey = _activeTab ? _activeTab.getAttribute(\"data-tab\") : \"images\";\n      const _lp0 = document.querySelector(\".dmb-builder-leftpanel\");\n      if (_lp0) _lp0.setAttribute(\"data-active-tab\", _activeKey);\n    } catch (e) {}\n  }\n\n  // opts.ignoreSearch / opts.ignoreUsedFilter let a CALLER opt out of the tray's\n  // view state. \"Do the Magic\" needs both: the search box and the \"Hide used\"\n  // toggle are how the customer is LOOKING at their photos, and they must never\n  // decide which photos end up in the book. With the search box filled, Magic\n  // used to silently lay out only the matching photos and leave the rest behind.\n  function applyPhotoFilters(list, opts) {\n    let arr = Array.isArray(list) ? list.slice() : [];\n    const _ig = opts || {};\n\n    // Derived crop photos are internal placement bytes \u2014 never show them in the tray.\n    arr = arr.filter((p) => !_dmbIsCropDerived(p));\n\n    if (!_ig.ignoreSearch && currentPhotoSearch.trim()) {\n      const q = currentPhotoSearch.trim().toLowerCase();\n      arr = arr.filter((p) => {\n        const filename = String(p.filename || p.file?.name || \"\").toLowerCase();\n        const dateTaken = String(p.dateTaken || \"\").toLowerCase();\n        const tags = Array.isArray(p.tags) ? p.tags.join(\" \").toLowerCase() : \"\";\n        return filename.includes(q) || dateTaken.includes(q) || tags.includes(q);\n      });\n    }\n\n    if (!_ig.ignoreUsedFilter && hideUsedPhotos) {\n      const used = getUsedPhotoIds();\n      arr = arr.filter((p) => !used.has(p.id));\n    }\n\n    if (currentPhotoSort === \"latest\") {\n      arr.sort((a, b) => (b.addedIndex || 0) - (a.addedIndex || 0));\n    } else if (currentPhotoSort === \"date\") {\n      arr.sort((a, b) => String(a.dateTaken || \"\").localeCompare(String(b.dateTaken || \"\")));\n    } else if (currentPhotoSort === \"filename\") {\n      arr.sort((a, b) => String(a.filename || \"\").localeCompare(String(b.filename || \"\")));\n    } else if (currentPhotoSort === \"portrait\") {\n      arr.sort((a, b) => (a.ratio || 1) - (b.ratio || 1));\n    } else if (currentPhotoSort === \"landscape\") {\n      arr.sort((a, b) => (b.ratio || 1) - (a.ratio || 1));\n    } else {\n      arr.sort((a, b) => (a.addedIndex || 0) - (b.addedIndex || 0));\n    }\n\n    return arr;\n  }\n\n\n  function setCanvasZoom(scale) {\n    // Phone shell: the canvas is always auto-fit AND CENTRED (translate(-50%,-50%)).\n    // Any plain scale() here uses transform-origin:top-left and would knock the\n    // canvas off-centre (the iPad-portrait \"half off-screen\" bug), so delegate every\n    // zoom to _mshFitSpread, which owns the centred fit.\n    try {\n      if (window.matchMedia && window.matchMedia(\"(max-width: 749px), (orientation: portrait) and (pointer: coarse) and (max-width: 1024px)\").matches) { _mshFitSpread(); return; }\n    } catch (e) {}\n\n    currentZoom = Math.max(0.25, Math.min(2, scale));\n\n    if (spreadCanvas) {\n      spreadCanvas.style.transform = `scale(${currentZoom})`;\n      spreadCanvas.style.transformOrigin = \"top left\";\n    }\n\n    // Collapse the viewport box to the scaled size (transform:scale leaves a\n    // full-size layout box \u2192 dead space below a zoomed-out book).\n    _dmbCollapseSpreadVp();\n\n    const zoomLabel =\n      document.getElementById(\"zoomValue\") ||\n      document.getElementById(\"zoomLabel\");\n\n    if (zoomLabel) {\n      zoomLabel.textContent = `${Math.round(currentZoom * 100)}%`;\n    }\n\n    const zoomRange = document.getElementById(\"zoomRange\");\n    if (zoomRange) {\n      zoomRange.value = String(Math.round(currentZoom * 100));\n    }\n\n    // Re-match the second column's bottom to the (now-shorter) builder box.\n    if (typeof syncPhotoTrayHeight === \"function\") syncPhotoTrayHeight();\n  }\n\n  // Desktop: shrink the cover just enough that the WHOLE cover plus its Front/Back\n  // face labels fits the canvas height \u2014 no vertical scroll. Phones are handled by\n  // _mshFitSpread. Measures the cover's true (transform-independent) height via\n  // offsetHeight, which already includes the label-reserve margin, against the\n  // height the canvas column can give it. Only shrinks (fit \u2264 1); deterministic, so\n  // it converges and never fights itself across re-renders.\n  function _dmbFitCoverIntoView() {\n    try {\n      if (window.matchMedia && window.matchMedia(\"(max-width: 749px), (orientation: portrait) and (pointer: coarse) and (max-width: 1024px)\").matches) return;\n      if (builderMode !== \"cover\") return;\n      var cv = document.getElementById(\"spreadCanvas\");\n      var stageEl = document.querySelector(\".dmb-canvas-stage\");\n      var wrapCol = document.querySelector(\".dmb-canvas-wrap\");\n      if (!cv || !stageEl || !wrapCol) return;\n      var header = stageEl.querySelector(\".dmb-stage-header\");\n      var bottom = stageEl.querySelector(\".dmb-stage-bottombar\");\n      var cs = getComputedStyle(stageEl);\n      var pad = (parseFloat(cs.paddingTop) || 0) + (parseFloat(cs.paddingBottom) || 0);\n      var gap = (parseFloat(cs.rowGap || cs.gap) || 0) * 2;\n      var chrome = (header ? header.offsetHeight : 0) + (bottom ? bottom.offsetHeight : 0) + pad + gap + 10;\n      var availH = (wrapCol.clientHeight || 0) - chrome;\n      var availW = (wrapCol.clientWidth || 0) - 20;\n      var needH = cv.offsetHeight, needW = cv.offsetWidth;   // offsetHeight includes the label margin\n      if (availH < 80 || needH < 80) return;\n      var fit = Math.min(1, availH / needH, (needW > 0 && availW > 0) ? availW / needW : 1);\n      fit = Math.max(0.5, fit);\n      if (Math.abs(fit - currentZoom) > 0.015) setCanvasZoom(fit);\n    } catch (e) {}\n  }\n\n  // Collapse the spread viewport to the SCALED canvas size so a zoomed-out book\n  // leaves no dead space below it. A MutationObserver re-applies this the moment\n  // any render resets the viewport style (forceSpreadLayout for pages,\n  // renderCoverCanvas for the cover set height:auto) \u2014 it runs before the browser\n  // paints, so the gap never flashes when a photo is dropped.\n  let _dmbVpCollapsing = false;\n  let _dmbVpCollapsed = false;\n  function _dmbCollapseSpreadVp() {\n    // Use getElementById (not the `spreadViewport`/`spreadCanvas` consts, which\n    // are declared later in this IIFE) so this is safe to reference from the\n    // top-level observer setup below without a temporal-dead-zone error.\n    const vp = document.getElementById(\"spreadViewport\");\n    const cv = document.getElementById(\"spreadCanvas\");\n    if (!vp || !cv) return;\n    // Only collapse when zoomed OUT (<100%): transform:scale leaves a full-size\n    // layout box (dead space below a shrunk book). At 100%+ there's none, so do\n    // NOTHING and let the normal layout stand \u2014 forcing a fixed size there added\n    // stray horizontal/vertical scroll bars (e.g. on the laptop).\n    if (currentZoom >= 1) {\n      if (_dmbVpCollapsed) {\n        _dmbVpCollapsing = true;\n        vp.style.width = \"\";\n        vp.style.height = \"\";\n        vp.style.boxSizing = \"\";\n        vp.style.overflow = \"\";\n        _dmbVpCollapsing = false;\n        _dmbVpCollapsed = false;\n      }\n      return;\n    }\n    const w = cv.offsetWidth, h = cv.offsetHeight;\n    if (!w || !h) return;\n    const tw = Math.round(w * currentZoom) + \"px\";\n    const th = Math.round(h * currentZoom) + \"px\";\n    if (_dmbVpCollapsed && vp.style.width === tw && vp.style.height === th) return;\n    _dmbVpCollapsing = true;\n    vp.style.boxSizing = \"content-box\";   // width/height = the visible (scaled) canvas\n    vp.style.overflow = \"hidden\";          // clip the dead space \u2014 no scroll bars\n    vp.style.width = tw;\n    vp.style.height = th;\n    _dmbVpCollapsing = false;\n    _dmbVpCollapsed = true;\n    if (typeof syncPhotoTrayHeight === \"function\") syncPhotoTrayHeight();\n  }\n  (function _dmbInitVpObserver() {\n    const vp = document.getElementById(\"spreadViewport\");\n    if (vp && \"MutationObserver\" in window) {\n      new MutationObserver(() => { if (!_dmbVpCollapsing) _dmbCollapseSpreadVp(); })\n        .observe(vp, { attributes: true, attributeFilter: [\"style\"] });\n    }\n  })();\n\n  function openPagesMode() {\n    builderMode = \"pages\";\n    syncBuilderModeButtons();\n    renderBuilderCanvas();\n  }\n\n  function saveProjectLocal() {\n    saveProjectLocalWithMessage();\n  }\n\n  // Per-mode key for the transient \"last session\" project snapshot so book,\n  // magazine and card sessions never overwrite each other's in-progress work.\n  function _dmbProjKey() {\n    return \"dmb_saved_project_v1_\" + (window.DMB_CARD_MODE ? \"card\" : window.DMB_MAGAZINE_MODE ? \"mag\" : \"book\");\n  }\n\n  function loadProjectLocal() {\n    try {\n      const raw = localStorage.getItem(_dmbProjKey());\n      if (!raw) return null;\n      return JSON.parse(raw);\n    } catch {\n      return null;\n    }\n  }\n\n  function initProjectMenu() {\n    const btn = $(\"projectMenuBtn\");\n    if (!btn) return;\n    btn.onclick = openProjectMenuPrompt;\n  }\n\n  function initHistoryButton() {\n    const btn = $(\"historyBtn\");\n    if (!btn) return;\n    btn.onclick = openHistoryPanel;\n  }\n\n  function initPhotoImportMenu() {\n    // handled by the single delegated click + change listeners at the bottom\n  }\n  \n  function initPhotoSearchSortControls() {\n    const sort = $(\"photoSortSelect\");\n    const hide = $(\"hideUsedPhotosToggle\");\n\n    if (sort) {\n      sort.addEventListener(\"change\", () => {\n        currentPhotoSort = sort.value || \"earliest\";\n        renderPhotoTray();\n      });\n    }\n\n    if (hide) {\n      hide.addEventListener(\"change\", () => {\n        hideUsedPhotos = !!hide.checked;\n        renderPhotoTray();\n      });\n    }\n  }\n\n  function refreshBuilderPhotos() {\n    // Use the concurrency-capped, tiny-decode meta reader (_dmbWarmMeta) \u2014 NOT a\n    // Promise.all over full-res getImageMetaFromFile, which decoded every photo at\n    // full resolution at once and crashed phones on large uploads.\n    // Time-box EVERY meta read and swallow rejections, so the library is rebuilt\n    // and the tray repaints even when one photo's decode misbehaves. Before this,\n    // a single unsettled promise meant editor.photos was never rebuilt: the newly\n    // added photos silently never reached the tray, while the \"N photos selected\"\n    // counter (which reads bookFiles) had already counted them. A photo whose\n    // ratio could not be read is still added - it just starts at ratio 1.\n    const _metaJobs = bookFiles.map((f) =>\n      _dmbSettleWithin(_dmbWarmMeta(f), _DMB_META_TIMEOUT + 2000, { w: 1, h: 1, ratio: 1 }));\n    Promise.all(_metaJobs).catch(() => bookFiles.map(() => ({ w: 1, h: 1, ratio: 1 }))).then((metas) => {\n      // Preserve any derived crop photos (they aren't in bookFiles) so cropped placements\n      // don't lose their bytes when the library is rebuilt after adding more photos.\n      const _derivedKeep = (editor.photos || []).filter(_dmbIsCropDerived);\n      // Carry per-photo state (R2-confirmed mark, cached thumb) across the rebuild\n      // by pid \u2014 without this, every rebuild wiped _r2, which made the light-memory\n      // release stop working after each \"add more photos\" batch.\n      const _prevById = {};\n      try { (editor.photos || []).forEach((p) => { if (p && p.id) _prevById[p.id] = p; }); } catch (e) {}\n      // Print-master path in light-mem: a rebuild must NOT hand a fresh object URL\n      // back to a photo that was released (off-spread + safely on R2) \u2014 that would\n      // re-pin hundreds of blobs right after we freed them. Off-spread released\n      // photos stay lazy (url \"\") and re-materialize on demand (recovery/render).\n      const _keep = (_dmbPrintMasterOn() && _dmbLightMem()) ? _dmbCurrentSpreadPhotoIds() : null;\n      editor.photos = bookFiles.map((f, i) => {\n        // Prefer the id assigned at PICK time (f._dmbPid, set by the instant\n        // R2 backup) so the builder, backups and restores all agree on one id\n        // per photo and nothing is ever uploaded twice.\n        const pid = f._dmbPid || editor.photos[i]?.id || uid();\n        // Positional fallback ONLY when that entry really is this photo \u2014 the old\n        // array's tail holds derived crops, which a new file must never inherit from.\n        const prev = _prevById[pid] || ((editor.photos[i] && editor.photos[i].id === pid) ? editor.photos[i] : {});\n        const lazy = !!(_keep && prev._r2 && !_keep.has(pid));\n        return {\n          id: pid,\n          file: f,\n          url: prev.url || (lazy ? \"\" : URL.createObjectURL(f)),\n          ratio: metas[i]?.ratio ?? prev.ratio ?? 1,\n          filename: String(f.name || \"\").toLowerCase(),\n          dateTaken: metas[i]?.date || prev.dateTaken || \"\",\n          tags: prev.tags || [],\n          addedIndex: i,\n          _r2: prev._r2 || false,\n          thumbUrl: prev.thumbUrl || \"\"\n        };\n      }).concat(_derivedKeep);\n\n      renderPhotoTray();\n\n      if (editor.totalPages > 0 && editor.totalSpreads > 0) {\n        renderThumbsRail();\n        renderBuilderCanvas();\n      }\n    });\n  }\n\n  // True when a page already carries *designed* content from a template (or the\n  // user): overlay objects (text, stickers, maps, shapes, icons), a chosen page\n  // layout, or pre-placed frames. A bare photo slot does NOT count \u2014 that's just\n  // an auto-fill the Magic step is free to redistribute. Used to decide whether\n  // the Magic auto-fill should leave a page alone.\n  function _dmbPageHasContent(p) {\n    try {\n      if (Array.isArray(editor.pageObjects?.[p]) && editor.pageObjects[p].length) return true;\n      const fr = editor.pageFrames?.[p];\n      if (fr && Object.keys(fr).length) return true;\n      if (editor.pageTemplate?.[p]) return true;\n    } catch (e) {}\n    return false;\n  }\n\n  // Thorough \"is this page occupied?\" check for the Magic auto-fill \u2014 unlike\n  // _dmbPageHasContent it ALSO detects hand-placed slot photos (A/B) and full-spread\n  // photos, so Magic never overwrites a page the customer designed by hand.\n  function _dmbPageOccupied(p) {\n    try {\n      const sl = editor.pageSlots?.[p];\n      if (sl && (sl.A || sl.B)) return true;\n      if (Array.isArray(editor.pageObjects?.[p]) && editor.pageObjects[p].length) return true;\n      const fr = editor.pageFrames?.[p];\n      if (fr && Object.values(fr).some((v) => v)) return true;\n      if (editor.pageTemplate?.[p]) return true;\n      const si = Math.floor((p - 1) / 2);\n      if (p >= 1 && editor.spreadPhoto?.[si] && editor.spreadPhoto[si].photoId) return true;\n      // Half of the facing page's object is on this page. Magic treats an unoccupied\n      // page as free to fill, so without this it lays photo frames straight over the\n      // far half of a shape or map that spans the spread.\n      if (_dmbPartnerOverhang(p)) return true;\n    } catch (e) {}\n    return false;\n  }\n\n  // \"Do the Magic\" \u2014 a VARIED, magazine-style auto-layout that uses EVERY photo.\n  //\n  // The contract (owner, 2026-09-13): the customer picked a page count and\n  // uploaded a set of photos; Magic must place ALL of them into those pages and\n  // may NOT add pages. Not one photo is left over.\n  //\n  // That is why the work is split into \"decide the budget\" and then \"place\". The\n  // old version walked pages one at a time and stopped at `p <= totalPages`,\n  // silently abandoning whatever was still queued - so anyone with more photos\n  // than pages simply lost the tail. Now every usable page is handed a QUOTA\n  // whose total equals the number of photos, so completeness is arithmetic\n  // rather than a hope.\n  //\n  // Layouts, chosen by how many photos a page has to carry:\n  //   1 photo  \u2192 full-page portrait, a full-SPREAD landscape (only when there is\n  //              genuine slack), or the whole photo housed in an aspect-matched\n  //              centered frame so nothing is cropped\n  //   2 photos \u2192 the two stacked page slots (A / B)\n  //   3+       \u2192 a tidy grid of free photo-frames, last row centered\n  // All of them use existing structures (pageSlots / spreadPhoto / free\n  // photo-frames), so they save, restore and render in the PDF exactly like\n  // hand placement.\n  function smartCreateBook() {\n    if (!editor.totalPages || !editor.photos.length) return;\n    if (!editor.spreadPhoto) editor.spreadPhoto = {};   // spreads write editor.spreadPhoto[...]\n\n    const dims = getPageDims();\n\n    // Classify a photo by aspect ratio (w / h).\n    const kind = (ph) => {\n      const r = (ph && ph.ratio) || 1;\n      if (r <= 0.92) return \"portrait\";\n      if (r >= 2.0)  return \"pano\";\n      if (r >= 1.2)  return \"landscape\";\n      return \"square\";\n    };\n\n    // Tagged _magic so a re-run can tell its own frames from the customer's work.\n    const _frame = (ph, pageNum, x, y, w, h, i) => ({\n      id: uid(), type: \"photo-frame\", targetType: \"page\", page: pageNum, zone: null,\n      x: Math.round(x), y: Math.round(y), w: Math.round(w), h: Math.round(h),\n      z: Date.now() + (i || 0), photoId: ph.id, _magic: true,\n    });\n\n    // Cover inside an aspect-MATCHED frame shows the whole photo (no crop).\n    // \u2500\u2500 Fit: how much of a photo actually survives in a box \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // Photos are drawn object-fit:cover, so a cell that does not match the photo's\n    // shape CROPS the overflow. The visible fraction is min(pa,ca)/max(pa,ca) -\n    // 1.0 when the aspects match, falling away as they diverge. Every layout\n    // decision below maximises it: the owner's rule is \"pick the layout from the\n    // picture's dimensions, keep the maximum of the picture showing\".\n    const _fit = (a, b) => (a > 0 && b > 0) ? Math.min(a, b) / Math.max(a, b) : 0;\n    const PAGE_A   = dims.w / dims.h;                  // a single page\n    const SPREAD_A = (dims.w * 2) / dims.h;            // both pages of a spread\n    const GM = Math.round(dims.w * 0.055);             // ~5mm safe margin\n    const GG = Math.max(4, Math.round(dims.w * 0.025));// gutter\n    const cellOf = (cols, rows) => ({\n      cw: (dims.w - 2 * GM - (cols - 1) * GG) / cols,\n      ch: (dims.h - 2 * GM - (rows - 1) * GG) / rows,\n    });\n\n    // Shrink a frame to the photo's OWN aspect inside its cell, centered, so the\n    // whole picture shows. Skipped when the cell already matches within 10%,\n    // where filling it edge to edge looks better than a letterboxed gap.\n    const fitBox = (pa, x, y, w, h) => {\n      const ca = w / h;\n      if (_fit(pa, ca) >= 0.9) return { x: x, y: y, w: w, h: h };\n      let fw, fh;\n      if (pa > ca) { fw = w; fh = w / pa; } else { fh = h; fw = h * pa; }\n      return { x: x + (w - fw) / 2, y: y + (h - fh) / 2, w: fw, h: fh };\n    };\n\n    // Choose the grid whose CELLS best match the photos that will sit in them.\n    // Two landscapes want one column of two rows; two tall portraits want two\n    // columns of one row. Guessing from the page's orientation alone (what this\n    // used to do) got both of those wrong.\n    const bestGrid = (list) => {\n      const n = list.length;\n      let best = null;\n      for (let cols = 1; cols <= n; cols++) {\n        const rows = Math.ceil(n / cols);\n        if (cols * rows - n >= cols) continue;           // would leave a whole row empty\n        const c = cellOf(cols, rows), ca = c.cw / c.ch;\n        let score = 0;\n        list.forEach((ph) => { score += _fit((ph && ph.ratio) || 1, ca); });\n        score = score / n - Math.abs(cols - rows) * 0.02;   // nudge towards balance\n        if (!best || score > best.score) best = { cols: cols, rows: rows, score: score };\n      }\n      return best || { cols: 1, rows: n };\n    };\n\n    // The whole photo, housed in a frame cut to its own aspect, centered.\n    const centeredFrame = (ph, pageNum) => {\n      const r = (ph && ph.ratio) || 1;\n      const maxW = dims.w * 0.82, maxH = dims.h * 0.82;\n      let fw, fh;\n      if (r >= (maxW / maxH)) { fw = maxW; fh = maxW / r; }\n      else                    { fh = maxH; fw = maxH * r; }\n      return _frame(ph, pageNum, (dims.w - fw) / 2, (dims.h - fh) / 2, fw, fh, 0);\n    };\n\n    // Grid for a page carrying 2+, shaped by bestGrid and fitted per cell. A short\n    // last row is centered so an odd count doesn't leave a corner hole.\n    const gridFrames = (list, pageNum) => {\n      const n = list.length;\n      const g = bestGrid(list);\n      const c = cellOf(g.cols, g.rows);\n      return list.map((ph, k) => {\n        const r = Math.floor(k / g.cols);\n        const inRow = Math.min(g.cols, n - r * g.cols);\n        const col = k - r * g.cols;\n        const rowW = inRow * c.cw + (inRow - 1) * GG;\n        const b = fitBox((ph && ph.ratio) || 1,\n          (dims.w - rowW) / 2 + col * (c.cw + GG), GM + r * (c.ch + GG), c.cw, c.ch);\n        return _frame(ph, pageNum, b.x, b.y, b.w, b.h, k);\n      });\n    };\n\n    // Magazine \"hero\": one photo across the top, the rest in a strip beneath it.\n    const heroFrames = (list, pageNum) => {\n      const innerW = dims.w - 2 * GM, innerH = dims.h - 2 * GM;\n      const heroH = Math.round(innerH * 0.6);\n      const rest = list.slice(1), c = rest.length;\n      const cw = (innerW - (c - 1) * GG) / c, ch = innerH - heroH - GG;\n      const hb = fitBox((list[0] && list[0].ratio) || 1, GM, GM, innerW, heroH);\n      const out = [_frame(list[0], pageNum, hb.x, hb.y, hb.w, hb.h, 0)];\n      rest.forEach((ph, k) => {\n        const b = fitBox((ph && ph.ratio) || 1, GM + k * (cw + GG), GM + heroH + GG, cw, ch);\n        out.push(_frame(ph, pageNum, b.x, b.y, b.w, b.h, k + 1));\n      });\n      return out;\n    };\n\n    // \u2500\u2500 Capture-time ordering \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // A sortable timestamp per photo so the book reads in the order the pictures\n    // were taken. Best source first: EXIF/restored dateTaken \u2192 a date encoded in\n    // the filename (IMG_20230604_153012, PXL_\u2026, Screenshot_\u2026) \u2192 the file's modified\n    // time \u2192 upload order as a stable last resort.\n    const _ts = (ph) => {\n      if (!ph) return 0;\n      try {\n        if (ph.dateTaken) { const d = Date.parse(ph.dateTaken); if (!isNaN(d)) return d; }\n      } catch (e) {}\n      try {\n        const m = String(ph.filename || \"\").match(\n          /(20\\d{2})[-_.]?(0[1-9]|1[0-2])[-_.]?(0[1-9]|[12]\\d|3[01])(?:[-_.T ]?([01]\\d|2[0-3])[-_.:]?([0-5]\\d)[-_.:]?([0-5]\\d)?)?/);\n        if (m) { const d = Date.UTC(+m[1], +m[2] - 1, +m[3], +(m[4] || 0), +(m[5] || 0), +(m[6] || 0)); if (!isNaN(d)) return d; }\n      } catch (e) {}\n      try { if (ph.file && ph.file.lastModified) return ph.file.lastModified; } catch (e) {}\n      return 1e12 + (ph.addedIndex || 0);\n    };\n    const _sortQueue = (q) => q.sort((a, b) => (_ts(a) - _ts(b)) || ((a.addedIndex || 0) - (b.addedIndex || 0)));\n\n    // \u2500\u2500 Moment grouping \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // Split the chronological queue into exactly `nPages` contiguous chunks that\n    // sum to every photo (each page \u2265 1 when there are at least as many photos as\n    // pages). Chunk boundaries start evenly spaced (balanced page density), then\n    // each is nudged up to \u00B12 photos to fall on the LARGEST nearby time gap \u2014 so a\n    // page breaks between two separate moments rather than mid-burst. No absolute\n    // threshold, so it adapts to any shoot.\n    const _momentChunks = (tsArr, nPages) => {\n      const N = tsArr.length, sizes = new Array(Math.max(0, nPages)).fill(0);\n      if (nPages <= 0) return sizes;\n      if (N <= nPages) { for (let i = 0; i < N; i++) sizes[i] = 1; return sizes; }\n      const gap = (i) => (i > 0 && i < N) ? Math.max(0, tsArr[i] - tsArr[i - 1]) : -1;\n      const bnd = []; for (let k = 0; k <= nPages; k++) bnd.push(Math.round(k * N / nPages));\n      const W = 2;\n      for (let k = 1; k < nPages; k++) {\n        const lo = Math.max(bnd[k - 1] + 1, bnd[k] - W);\n        const hi = Math.min(Math.round((k + 1) * N / nPages) - 1, bnd[k] + W);\n        let bi = bnd[k], bg = gap(bnd[k]);\n        for (let i = lo; i <= hi; i++) { const g = gap(i); if (g > bg) { bg = g; bi = i; } }\n        bnd[k] = bi;\n      }\n      for (let k = 1; k < nPages; k++) {\n        if (bnd[k] <= bnd[k - 1]) bnd[k] = bnd[k - 1] + 1;\n        if (bnd[k] > N - (nPages - k)) bnd[k] = N - (nPages - k);   // leave \u22651 for the pages after\n      }\n      bnd[nPages] = N;\n      for (let k = 0; k < nPages; k++) sizes[k] = Math.max(0, bnd[k + 1] - bnd[k]);\n      return sizes;\n    };\n\n    // \u2500\u2500 Designed-layout selection (orientation-aware + varied) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // Curated pools of hand-designed TEMPLATE_DEFS layouts per photo count (the\n    // sparse/tiny-floating ones are left out so no page looks empty). For a page's\n    // photos we score each candidate by how well its SLOTS' shapes match the\n    // photos' shapes (object-fit:cover crops the mismatch), avoid repeating the\n    // previous layout, and add a little jitter \u2014 so landscapes land in wide slots,\n    // portraits in tall slots, and the sequence never looks mechanical.\n    const _MAGIC_TPL = {\n      1: [\"t2\", \"t24\", \"t5\", \"t35\"],\n      2: [\"t34\", \"t19\", \"t31\", \"t30\", \"t28\", \"t32\", \"t33\", \"t8\"],\n      3: [\"t22\", \"t4\", \"t9\", \"t27\", \"t1\", \"t11\", \"t29\"],\n      4: [\"t23\", \"t18\", \"t15\", \"t6\", \"t14\"],\n      5: [\"t16\", \"t17\", \"t20\", \"t21\"],\n      7: [\"t25\", \"t26\"],\n      9: [\"t7\"],\n    };\n    const _slotAR = (s) => (s && s.w > 0 && s.h > 0) ? (s.w / s.h) * PAGE_A : PAGE_A;\n    const _assign = (slots, photos) => {\n      const ar = slots.map(_slotAR);\n      // Fill the most extreme-shaped slots first so a lone landscape isn't stolen by\n      // a near-square slot before the wide slot gets its turn.\n      const order = slots.map((_, i) => i)\n        .sort((a, b) => Math.abs(Math.log(ar[b])) - Math.abs(Math.log(ar[a])));\n      const used = new Array(photos.length).fill(false);\n      const asn = new Array(slots.length).fill(-1);\n      let total = 0;\n      order.forEach((si) => {\n        let bi = -1, bs = -1;\n        photos.forEach((ph, pi) => {\n          if (used[pi]) return;\n          const sc = _fit((ph && ph.ratio) || 1, ar[si]);\n          if (sc > bs) { bs = sc; bi = pi; }\n        });\n        if (bi >= 0) { asn[si] = bi; used[bi] = true; total += bs; }\n      });\n      return { asn: asn, score: photos.length ? total / photos.length : 0 };\n    };\n    const _pickTpl = (photos, lastTid) => {\n      const pool = _MAGIC_TPL[photos.length];\n      if (!pool) return null;\n      let best = null;\n      pool.forEach((tid) => {\n        const slots = TEMPLATE_DEFS[tid];\n        if (!slots || slots.length !== photos.length) return;\n        const a = _assign(slots, photos);\n        let sc = a.score + Math.random() * 0.08;\n        if (tid === lastTid) sc -= 0.18;              // don't repeat the last layout\n        if (!best || sc > best.sc) best = { tid: tid, asn: a.asn, sc: sc, raw: a.score };\n      });\n      return best;\n    };\n    const _tplFrames = (tid, asn, photos, pageNum) => {\n      const slots = TEMPLATE_DEFS[tid] || [];\n      const out = [];\n      slots.forEach((s, si) => {\n        const pi = asn[si];\n        if (pi == null || pi < 0) return;\n        const ph = photos[pi];\n        if (!ph) return;\n        let x = s.x / 100 * dims.w, y = s.y / 100 * dims.h, w = s.w / 100 * dims.w, h = s.h / 100 * dims.h;\n        // A photo that doesn't suit its slot shape is INSET (shown whole, centred)\n        // rather than cropped \u2014 the slot still anchors the designed composition.\n        const pr = (ph && ph.ratio) || 1;\n        if (_fit(pr, w / h) < 0.72) { const b = fitBox(pr, x, y, w, h); x = b.x; y = b.y; w = b.w; h = b.h; }\n        out.push(_frame(ph, pageNum, x, y, w, h, si));\n      });\n      return out;\n    };\n\n    // \u2500\u2500 \"Is this page exactly as Magic left it?\" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // A signature of everything on the page, recorded in editor.magicPages after\n    // each run. On a re-run a page whose signature still matches is one Magic laid\n    // out and the customer has NOT touched, so it may be re-flowed. Any move,\n    // resize, rotation, photo swap, deletion or added sticker changes the\n    // signature, and the page becomes the customer's - permanently off limits.\n    const _pageSig = (p) => {\n      const sl = editor.pageSlots[p] || {};\n      const rt = editor.pageRot[p] || {};\n      const si = Math.floor((p - 1) / 2);\n      const sp = (p >= 1 && editor.spreadPhoto) ? editor.spreadPhoto[si] : null;\n      const objs = (editor.pageObjects[p] || [])\n        .map((o) => [o.type, o.photoId || \"\", o.x, o.y, o.w, o.h].join(\":\")).join(\"|\");\n      return [sl.A || \"\", sl.B || \"\", rt.A || 0, rt.B || 0, objs,\n        sp ? [sp.photoId, sp.panX, sp.panY, sp.zoom].join(\":\") : \"\"].join(\"~\");\n    };\n    const _magicOwned = (p) => {\n      const rec = editor.magicPages && editor.magicPages[p];\n      if (!rec) return false;\n      if (editor.pageTemplate && editor.pageTemplate[p]) return false;   // customer's layout\n      const fr = editor.pageFrames && editor.pageFrames[p];\n      if (fr && Object.values(fr).some((v) => v)) return false;\n      return rec === _pageSig(p);\n    };\n\n    // \u2500\u2500 What to place \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // Photos not used anywhere yet. ignoreSearch/ignoreUsedFilter: the tray's\n    // search box and \"Hide used\" toggle are a VIEW, and must never shrink the set\n    // of photos that goes into the book. Magic does its own used-check here.\n    const _unplaced = () => {\n      const used = (function () { try { return getUsedPhotoIds(); } catch (e) { return new Set(); } })();\n      return applyPhotoFilters(editor.photos, { ignoreSearch: true, ignoreUsedFilter: true })\n        .filter((ph) => ph && !used.has(ph.id));\n    };\n    let queue = _sortQueue(_unplaced());   // chronological: the book reads as it was shot\n    if (!queue.length) return;   // nothing new to place - never reshuffle for no reason\n\n    pushUndoState(\"Smart Creation\");\n\n    // \u2500\u2500 1. Fill layouts the customer chose but left empty \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // A \"Choose layout\" page counts as occupied, so the page loop below skips it -\n    // but its empty frames are slots the customer deliberately made, and leaving\n    // them blank while photos go unplaced defeats the point of the button.\n    for (let p = 0; p <= editor.totalPages && queue.length; p++) {\n      if (isLockedPage(p)) continue;\n      const tid = getPageTemplateId(p);\n      if (!tid) continue;\n      const defs = (typeof TEMPLATE_DEFS !== \"undefined\" && TEMPLATE_DEFS[tid]) || [];\n      if (!defs.length) continue;\n      if (!editor.pageFrames[p]) editor.pageFrames[p] = {};\n      defs.forEach((d) => {\n        if (!queue.length || !d || !d.key) return;\n        if (editor.pageFrames[p][d.key]) return;          // already filled - leave it alone\n        editor.pageFrames[p][d.key] = queue.shift().id;\n      });\n    }\n\n    // \u2500\u2500 2. Reclaim the pages Magic itself laid out \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // Without this, a customer who runs Magic, uploads more photos and runs it\n    // again is told there are no empty pages - and those photos never land, which\n    // breaks the whole \"every photo is used\" promise in the most ordinary\n    // workflow there is. Magic's own UNTOUCHED pages are fair game: their photos\n    // go back in the queue and the book is re-flowed evenly around the new ones.\n    // Anything the customer made, or edited even slightly, is never reclaimed.\n    const owned = [];\n    for (let p = 0; p <= editor.totalPages; p++) {\n      if (!isLockedPage(p) && _magicOwned(p)) owned.push(p);\n    }\n    if (owned.length) {\n      owned.forEach((p) => {\n        if (editor.pageSlots[p]) { editor.pageSlots[p].A = null; editor.pageSlots[p].B = null; }\n        if (Array.isArray(editor.pageObjects[p])) {\n          editor.pageObjects[p] = editor.pageObjects[p].filter((o) => !(o && o._magic));\n        }\n        const si = Math.floor((p - 1) / 2);\n        if (p >= 1 && editor.spreadPhoto && editor.spreadPhoto[si] && editor.spreadPhoto[si]._magic) {\n          delete editor.spreadPhoto[si];\n        }\n        if (editor.magicPages) delete editor.magicPages[p];\n      });\n      queue = _sortQueue(_unplaced());   // the reclaimed photos count as unplaced again\n    }\n\n    // \u2500\u2500 3. Where the rest can go \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    const pages = [];\n    for (let p = 0; p <= editor.totalPages; p++) {\n      if (isLockedPage(p)) continue;          // books: page 1 is fixed. Magazines have none.\n      if (_dmbPageOccupied(p)) continue;\n      pages.push(p);\n    }\n\n    // Record what Magic leaves behind, so the next run can tell its own work from\n    // the customer's, then repaint.\n    const _finish = () => {\n      editor.magicPages = editor.magicPages || {};\n      pages.forEach((p) => { if (_dmbPageOccupied(p)) editor.magicPages[p] = _pageSig(p); });\n      builderMode = \"pages\";\n      renderPhotoTray();\n      renderThumbsRail();\n      renderBuilderCanvas();\n    };\n\n    if (!queue.length) { _finish(); return; }     // the layouts took everything\n    if (!pages.length) {\n      // Say so rather than doing nothing - a button that appears to ignore you is\n      // worse than a refusal that explains itself.\n      try {\n        showChoiceDialog(\n          \"No empty pages left\",\n          \"Every page already has something on it, so there is nowhere to put your remaining \" +\n          queue.length + \" photo\" + (queue.length > 1 ? \"s\" : \"\") +\n          \". Clear a page or choose more pages, then try again.\",\n          [{ id: \"ok\", label: \"OK\" }]\n        );\n      } catch (e) {}\n      _finish();\n      return;\n    }\n\n    // \u2500\u2500 Spreads, planned UP FRONT \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // A full-bleed spread spends TWO pages on ONE photo. The previous version\n    // could only afford one when a neighbouring page happened to have no photos\n    // of its own, so a book with at least as many photos as pages got NONE.\n    // Planning them before the quotas fixes that without weakening the\n    // guarantee: the pages a spread claims leave the pool, and the photos that\n    // remain are distributed over what is left - the rest just runs a touch denser.\n    const P = pages.length;\n    const spreadEvenly = (count, total) => {\n      const out = new Array(total).fill(0);\n      if (count <= 0 || total <= 0) return out;\n      for (let k = 0; k < total; k++) {\n        if (Math.floor((k + 1) * count / total) > Math.floor(k * count / total)) out[k] = 1;\n      }\n      return out;\n    };\n    const isWide = (ph) => { const t = kind(ph); return t === \"landscape\" || t === \"pano\"; };\n\n    // A spread needs the LEFT page of a physical spread and its neighbour, both\n    // free. Pages pair up as (1,2), (3,4), ...\n    const candidates = [];\n    for (let k = 0; k + 1 < P; k++) {\n      if (pages[k] >= 1 && pages[k] % 2 === 1 && pages[k + 1] === pages[k] + 1) candidates.push(k);\n    }\n    let wantSpreads = Math.min(\n      Math.floor(P / 8),                  // roughly one spread every 8 pages\n      queue.filter(isWide).length,        // each one needs a wide photo\n      candidates.length,\n      Math.max(0, (P - 1) >> 1),          // always leave an ordinary page behind\n      Math.max(0, queue.length - 1)       // and at least one photo for the rest\n    );\n    // Don't spend two pages on one photo when the book is already packed: past\n    // roughly three photos a page a spread reads as a mistake, not a flourish.\n    if (queue.length > P * 3) wantSpreads = 0;\n\n    const spreadAt = new Set();\n    if (wantSpreads > 0) {\n      const stride = P / wantSpreads;\n      let next = stride / 2, lastK = -2;\n      for (const k of candidates) {\n        if (spreadAt.size >= wantSpreads) break;\n        if (k <= lastK + 1) continue;          // never overlap the previous spread\n        if (k < next - 0.5) continue;          // keep them evenly spaced\n        spreadAt.add(k); lastK = k; next += stride;\n      }\n    }\n    // Reserve a wide photo per spread NOW, so the ordinary pages cannot eat them\n    // before the loop reaches the spread slots.\n    const spreadPool = [];\n    spreadAt.forEach(() => {\n      // Take the photo whose shape best matches the DOUBLE-page box, not merely\n      // the first wide one: across a 1.41 spread a 1.5 landscape loses almost\n      // nothing, while a 2.4 panorama loses about 40% of its width.\n      let bi = -1, bs = -1;\n      queue.forEach((ph, idx) => {\n        if (!isWide(ph)) return;\n        const sc = _fit((ph && ph.ratio) || 1, SPREAD_A);\n        if (sc > bs) { bs = sc; bi = idx; }\n      });\n      if (bi >= 0) spreadPool.push(queue.splice(bi, 1)[0]);\n    });\n    while (spreadPool.length < spreadAt.size) spreadAt.delete(Array.from(spreadAt).pop());\n\n    // \u2500\u2500 Quotas for every page that is NOT part of a spread \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // Each gets `base`, and `extra` of them get one more, so the quotas sum to\n    // EXACTLY the photos left after the spreads took theirs. More photos than\n    // pages just means denser pages - never a dropped photo.\n    //\n    // The extras are spread EVENLY through the book (Bresenham), not handed to\n    // the first `extra` pages. Front-loading produced a visible density step -\n    // \"2 2 2 2 2 2 1 1 1 ...\" - and, with fewer photos than pages, packed them\n    // all into the front and left the whole back half blank.\n    const normalIdx = [];\n    for (let k = 0; k < P; k++) {\n      if (spreadAt.has(k)) { k += 1; continue; }   // the partner page goes with it\n      normalIdx.push(k);\n    }\n    const quota = new Array(P).fill(0);\n    if (normalIdx.length) {\n      // Moment-aware: contiguous chronological chunks, balanced but broken on the\n      // big time gaps, summing to exactly the photos left (each page \u2265 1 when there\n      // are at least as many photos as pages \u2014 so no page is left empty).\n      const chunks = _momentChunks(queue.map(_ts), normalIdx.length);\n      normalIdx.forEach((k, j) => { quota[k] = chunks[j] || 0; });\n    }\n\n    const _ensurePage = (pp) => {\n      if (!editor.pageSlots[pp]) editor.pageSlots[pp] = { A: null, B: null };\n      if (!editor.pageRot[pp])   editor.pageRot[pp]   = { A: 0, B: 0 };\n      if (!Array.isArray(editor.pageObjects[pp])) editor.pageObjects[pp] = [];\n    };\n\n    let variety = 0;\n    let _lastTid = null;   // last designed layout used, so we never repeat back-to-back\n    for (let k = 0; k < P; k++) {\n      const p = pages[k];\n\n      if (spreadAt.has(k)) {\n        const ph = spreadPool.shift();\n        if (ph) {\n          _ensurePage(p); _ensurePage(pages[k + 1]);\n          editor.spreadPhoto[(p - 1) / 2] =\n            { photoId: ph.id, panX: 0, panY: 0, zoom: 1, _magic: true };\n        }\n        k += 1;                                  // the partner page is consumed\n        continue;\n      }\n\n      const q = quota[k];\n      if (q <= 0 || !queue.length) continue;\n      _ensurePage(p);\n\n      if (q >= 2) {\n        // Prefer a hand-designed layout whose slot shapes match THESE photos\n        // (orientation-aware, varied, never the same as the previous page). Counts\n        // with no designed template (6, 8) fall back to the aspect-matched grid.\n        const take = queue.splice(0, Math.min(q, queue.length));\n        const pick = _pickTpl(take, _lastTid);\n        // Use a designed layout when its slots genuinely suit these photos; otherwise\n        // the aspect-matched grid (which can make wide or tall cells to fit the shots\n        // and shows each photo whole) carries the page.\n        if (pick && pick.raw >= 0.72) {\n          _lastTid = pick.tid;\n          _tplFrames(pick.tid, pick.asn, take, p).forEach((f) => editor.pageObjects[p].push(f));\n        } else {\n          const useHero = (variety % 3 === 2) && take.length >= 3 && take.length <= 4 && isWide(take[0]);\n          (useHero ? heroFrames(take, p) : gridFrames(take, p)).forEach((f) => editor.pageObjects[p].push(f));\n        }\n        variety += 1;\n        continue;\n      }\n\n      // q === 1 - one photo owning the page. Fill it edge to edge when the photo is\n      // close to the page's own shape (at most ~15% lost); otherwise house the WHOLE\n      // picture in a frame cut to its own aspect, centred - never crop a lone photo.\n      const only = queue.shift();\n      if (_fit((only && only.ratio) || 1, PAGE_A) >= 0.85) editor.pageSlots[p].A = only.id;\n      else editor.pageObjects[p].push(centeredFrame(only, p));\n      variety += 1;\n    }\n\n    // A reserved spread photo that never found its slot goes back in the queue so\n    // the backstop below can still place it - nothing is allowed to fall out here.\n    if (spreadPool.length) { spreadPool.splice(0).forEach((ph) => queue.push(ph)); }\n\n    // \u2500\u2500 Backstop \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // The quotas already sum to the queue length, so this should never run. It\n    // exists so \"not a single photo is left behind\" holds even if a future edit\n    // breaks that arithmetic: re-grid the last page from scratch with everything\n    // it holds plus the strays, which cannot overlap because the grid is rebuilt.\n    if (queue.length) {\n      const p = pages[pages.length - 1];\n      _ensurePage(p);\n      const ids = [];\n      if (editor.pageSlots[p].A) { ids.push(editor.pageSlots[p].A); editor.pageSlots[p].A = null; }\n      if (editor.pageSlots[p].B) { ids.push(editor.pageSlots[p].B); editor.pageSlots[p].B = null; }\n      (editor.pageObjects[p] || []).forEach((o) => {\n        if (o && o._magic && o.type === \"photo-frame\" && o.photoId) ids.push(o.photoId);\n      });\n      editor.pageObjects[p] = (editor.pageObjects[p] || [])\n        .filter((o) => !(o && o._magic && o.type === \"photo-frame\"));\n      const all = ids.map((id) => { try { return getPhotoById(id); } catch (e) { return null; } })\n        .filter(Boolean).concat(queue.splice(0));\n      gridFrames(all, p).forEach((f) => editor.pageObjects[p].push(f));\n    }\n\n    _finish();\n  }\n\n  function getCurrentBuilderStateSnapshot() {\n    return {\n      book_title: getEffectiveBookTitle(),\n      pages_selected: Number(pagesEl?.value || 0),\n      coverState: cloneState(coverState),\n      editor: cloneState(editor),\n      builderMode,\n      currentPhotoSort,\n      currentPhotoSearch,\n      hideUsedPhotos,\n      // The customer's recent-colours palette rides with the design so it appears on\n      // their other devices too (it's otherwise localStorage-only = per device).\n      recentColors: dmbGetRecentColors()\n    };\n  }\n\n  // Apply a SAVED design snapshot onto the live editor/coverState. EXTRACTED from the\n  // ?restore=1 Phase-3 block so Step-2 render mode reconstructs state through the EXACT\n  // same code - there is no second implementation to drift. Photos must already be in\n  // editor.photos (cover URLs are rehydrated from their ids at the end).\n  function _dmbApplySavedEditorState(_saved) {\n    const _cl = (v) => { try { return JSON.parse(JSON.stringify(v || {})); } catch (e) { return {}; } };\n    const _se = (_saved && _saved.editor) || {};\n    const _pg = _se.totalPages || Number(_saved && _saved.pages_selected) || 0;\n    editor.totalPages     = _pg;\n    editor.editablePages  = _se.editablePages  || Math.max(0, _pg - 1);\n    editor.totalSpreads   = _se.totalSpreads   || Math.max(1, Math.ceil(_pg / 2));\n    // Restore WHERE THEY WERE. This was hardcoded to 0, so every refresh landed on\n    // spread 0 (pages 1 and 2) no matter which spread was open. Page 0 is stored as -1,\n    // which is why that case looked like its own bug: it is the same bug, just the most\n    // visible instance of it. Clamped to the book's real range, because a saved index\n    // can outlive a page-count reduction and an out-of-range index renders nothing.\n    // (2026-10-12)\n    var _sp = Number(_se.currentSpread);\n    if (!isFinite(_sp)) _sp = 0;\n    var _maxSp = Math.max(0, (editor.totalSpreads || 1) - 1);\n    editor.currentSpread  = Math.max(-1, Math.min(_sp, _maxSp));\n    editor.pageSlots      = _cl(_se.pageSlots);\n    editor.pageRot        = _cl(_se.pageRot);\n    editor.pageTemplate   = _cl(_se.pageTemplate);\n    editor.pageFrames     = _cl(_se.pageFrames);\n    editor.pageFrameRot   = _cl(_se.pageFrameRot);\n    editor.pageFrameShapes= _cl(_se.pageFrameShapes);\n    editor.pageObjects    = _cl(_se.pageObjects);\n    editor.pageBackgrounds= _cl(_se.pageBackgrounds);\n    editor.magicPages     = _cl(_se.magicPages);\n    editor.spreadPhoto    = _cl(_se.spreadPhoto);\n    editor.extraSpreads   = _se.extraSpreads || 0;\n    editor.orientation    = (_se.orientation === \"landscape\" ? \"landscape\" : \"portrait\");\n    editor.coverObjects   = Array.isArray(_se.coverObjects)   ? _cl(_se.coverObjects)   : [];\n    editor.coverUserTexts = Array.isArray(_se.coverUserTexts) ? _cl(_se.coverUserTexts) : [];\n    editor.cropRects      = _cl(_se.cropRects || {});   // crop ratios for derived photos\n    if (!Array.isArray(editor.coverObjects)) try { migrateCoverObjectsToFlat(); } catch (e) {}\n    if (_saved && _saved.coverState) {\n      const _cs = _saved.coverState;\n      [\"frontImageUrl\", \"backImageUrl\", \"bleedImageUrl\", \"spineImageUrl\", \"imageUrl\"].forEach((k) => {\n        if (_cs[k] && String(_cs[k]).startsWith(\"blob:\")) {\n          _cs[k] = \"\";\n          // A dead blob: URL means the URL is gone, NOT that the photo is gone.\n          // EVERY photo URL in the builder is createObjectURL(), so every wrap\n          // image looks exactly like this after a reload - this branch is the\n          // normal case, not an edge case.\n          //\n          // The front and back faces keep their ids here and\n          // _dmbRehydrateCoverPhotoUrls() (a few lines below) rebuilds their URLs\n          // from those ids. The wrap used to have its id AND its zone deleted\n          // instead, so the rehydrate had nothing left to work with and the\n          // wrap-around background was DESTROYED on restore - then autosaved in\n          // its stripped form. The printed book came out with a white front and\n          // spine while the back kept its own face photo. Verified 2026-09-12.\n          //\n          // Keep the id. Only drop the zone when there is no id to recover from,\n          // so a legacy design cannot leave a zone pointing at nothing.\n          if (k === \"bleedImageUrl\" && !_cs.bleedImageId) { _cs.bleedZone = \"\"; }\n        }\n      });\n      Object.keys(_cs).forEach((k) => { try { coverState[k] = _cs[k]; } catch (e) {} });\n    }\n    builderMode = (_saved && _saved.builderMode) || \"pages\";\n    // Rebuild cover face/bleed URLs from their preserved photo ids (the blob: URLs were\n    // cleared above). No-op for lazy records: url stays \"\" and the photo is drawn by id.\n    try { _dmbRehydrateCoverPhotoUrls(); } catch (e) {}\n    return _pg;\n  }\n\n  function applyBuilderStateSnapshot(snapshot) {\n    if (!snapshot) return;\n\n    _dmbApplyRecentColors(snapshot);   // bring across the saved colour palette\n\n    if (pagesEl && snapshot.pages_selected) {\n      pagesEl.value = String(snapshot.pages_selected);\n      updatePrice();\n      refreshPhotoRule();\n    }\n\n    if (snapshot.coverState) {\n      const cs = snapshot.coverState;\n      coverState.template      = cs.template      || \"default\";\n      coverState.title1        = cs.title1        || \"\";\n      coverState.title2        = cs.title2        || \"\";\n      coverState.color         = cs.color         || \"#ffffff\";\n      coverState.imageUrl      = cs.imageUrl      || \"\";\n      coverState.imageFile     = cs.imageFile     || null;\n      coverState.frontLayout   = cs.frontLayout   || null;\n      coverState.backLayout    = cs.backLayout    || null;\n      coverState.frontFrames   = cloneState(cs.frontFrames   || {});\n      coverState.backFrames    = cloneState(cs.backFrames    || {});\n      coverState.frontFrameRot = cloneState(cs.frontFrameRot || {});\n      coverState.backFrameRot  = cloneState(cs.backFrameRot  || {});\n      coverState.frontFrameShapes = cloneState(cs.frontFrameShapes || {});\n      coverState.backFrameShapes  = cloneState(cs.backFrameShapes  || {});\n      coverState.bleedImageUrl = cs.bleedImageUrl || \"\";\n      coverState.bleedImageId  = cs.bleedImageId  || \"\";\n      coverState.bleedZone     = cs.bleedZone     || \"\";\n      coverState.bleedImageRot = cs.bleedImageRot || 0;\n      coverState.spineImageUrl = cs.spineImageUrl || \"\";\n      if (cs.textStyles) coverState.textStyles = cloneState(cs.textStyles);\n      syncCoverInputsFromState();\n    }\n\n    if (snapshot.editor) {\n      const se = snapshot.editor;\n      editor.totalPages    = se.totalPages    || 0;\n      editor.editablePages = se.editablePages || 0;\n      editor.totalSpreads  = se.totalSpreads  || Math.ceil((se.totalPages || 0) / 2);\n      editor.currentSpread = se.currentSpread || 0;\n      editor.pageSlots     = cloneState(se.pageSlots     || {});\n      editor.pageRot       = cloneState(se.pageRot       || {});\n      editor.pageTemplate  = cloneState(se.pageTemplate  || {});\n      editor.pageFrames    = cloneState(se.pageFrames    || {});\n      editor.pageFrameRot  = cloneState(se.pageFrameRot  || {});\n      editor.pageFrameShapes = cloneState(se.pageFrameShapes || {});\n      editor.pageObjects      = cloneState(se.pageObjects      || {});\n      editor.pageBackgrounds  = cloneState(se.pageBackgrounds  || {});\n      editor.magicPages       = cloneState(se.magicPages       || {});\n      editor.spreadPhoto      = cloneState(se.spreadPhoto      || {});\n      editor.coverObjects     = cloneState(se.coverObjects     || []);\n      if (!Array.isArray(editor.coverObjects)) migrateCoverObjectsToFlat();\n      editor.coverUserTexts = cloneState(se.coverUserTexts || []);\n      // Crop geometry (0\u20131 ratios per derived crop id) \u2014 lets the server render /\n      // another device re-bake crops from the print master. See editor.cropRects.\n      editor.cropRects = cloneState(se.cropRects || {});\n      // Restore extra spreads so price display and spread adder stay in sync\n      editor.extraSpreads = se.extraSpreads || 0;\n      // Restore the chosen book size (A4/A5) so price + variant follow the design\n      editor.bookSize = (se.bookSize === \"A5\" ? \"A5\" : \"A4\");\n      // Restore orientation so a landscape design reopens landscape\n      editor.orientation = (se.orientation === \"landscape\" ? \"landscape\" : \"portrait\");\n    }\n\n    builderMode = snapshot.builderMode || \"pages\";\n    currentPhotoSort = snapshot.currentPhotoSort || \"earliest\";\n    currentPhotoSearch = snapshot.currentPhotoSearch || \"\";\n    hideUsedPhotos = !!snapshot.hideUsedPhotos;\n\n    if (photoSortSelect) photoSortSelect.value = currentPhotoSort;\n    if (hideUsedPhotosToggle) hideUsedPhotosToggle.checked = hideUsedPhotos;\n\n    updatePrice();          // re-display price including any restored extraSpreads\n    try { _dmbSyncSizeToggleUI(); } catch (e) {}   // reflect restored A4/A5 choice\n    try { _dmbSyncOrientationToggleUI(); } catch (e) {}   // reflect restored orientation\n    _syncSpreadAdderUI();   // re-sync spread adder counter + visibility\n    updateCoverPreview();\n    renderPhotoTray();\n    renderThumbsRail();\n    renderBuilderCanvas();\n    // Full state restore \u2014 every page changed; clear and re-render preview cache\n    try { _dmbPvReinitAll(); } catch(e) {}\n  }\n\n  const editor = {\n    totalPages: 0,\n    editablePages: 0,\n    totalSpreads: 0,\n    currentSpread: 0,\n    extraSpreads: 0,      // count of extra spreads added by customer (each = 2 pages, +$1.99)\n    bookSize: \"A4\",       // \"A4\" (default) or \"A5\" \u2014 chosen via the top-bar toggle (book only)\n    orientation: \"portrait\", // \"portrait\" (default) or \"landscape\" \u2014 top-bar toggle\n    pageSlots: {},\n    pageRot: {},\n    pageTemplate: {},\n    pageFrames: {},\n    pageFrameRot: {},\n    pageObjects: {},\n    pageBackgrounds: {},  // { [pageNumber]: \"#rrggbb\" } \u2014 per-page background color\n    magicPages: {},       // { [pageNumber]: signature } \u2014 pages \"Do the Magic\" laid out\n                          // and the customer has not touched since. See _pageSig.\n    coverObjects: [],\n    coverUserTexts: [],   // [{id, zone}] \u2014 user-added text nodes on the cover\n    photos: [],\n    // { [derivedCropId]: { srcPid, x, y, w, h } } \u2014 crop geometry as 0\u20131 ratios of\n    // the SOURCE image, recorded by applyCrop. Lets any resolution of the source\n    // (proxy locally, print master on the server) reproduce the exact same crop,\n    // and lets a restore on another device re-bake derived photos it never held.\n    cropRects: {}\n  };\n\n  // \u2500\u2500 Orientation (Portrait default / Landscape) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // SINGLE SOURCE OF TRUTH for page dimensions, so the toggle flips the whole\n  // book (canvas, thumbnails, PDF, object bounds) from one place. In PORTRAIT\n  // these return exactly the historical numbers (270 \u00D7 382), so portrait\n  // behaviour is byte-for-byte unchanged \u2014 landscape just swaps the two axes.\n  var DMB_PAGE_SHORT_PX = 270;\n  var DMB_PAGE_LONG_PX  = Math.round(270 * 1.414);   // 382\n  function dmbIsLandscape() { return !!(editor && editor.orientation === \"landscape\"); }\n  function getPageDims() {\n    return dmbIsLandscape()\n      ? { w: DMB_PAGE_LONG_PX,  h: DMB_PAGE_SHORT_PX }   // landscape: 382 \u00D7 270\n      : { w: DMB_PAGE_SHORT_PX, h: DMB_PAGE_LONG_PX };   // portrait:  270 \u00D7 382\n  }\n  function getPageAspectCss() { var d = getPageDims(); return d.w + \" / \" + d.h; }\n\n  // Cover TEXT vertical position is stored CANONICAL (relative to the portrait\n  // face = 424px) and scaled to the current face at render time. Portrait scale\n  // = 1 (identical to before); landscape = 212/424 so text never clips off the\n  // shorter face. `left`/width are unscaled (face width stays 300px on-screen).\n  function _dmbCoverTopScale() {\n    return dmbIsLandscape() ? (Math.round(300 * 210 / 297) / Math.round(300 * 297 / 210)) : 1;\n  }\n  // Resolve a CSS length that MIGHT be a percentage into px, against the element's\n  // own containing block.\n  //\n  // Cover SPINE text is created with top:\"50%\" (see addDefaultText) so it centres on\n  // the face whatever that face's height is. Everywhere that did the obvious\n  // parseFloat(node.style.top) on such a node read the NUMBER 50 and wrote back the\n  // STRING \"50px\" - reinterpreting \"half way down the 424px face\" (212px) as \"50px\n  // from the top\", moving the text 162px up. parseFloat is only ever safe on a value\n  // you yourself wrote as px. (2026-09-14)\n  function _dmbLenPx(node, prop) {\n    var raw = (node && node.style) ? (node.style[prop] || \"\") : \"\";\n    var n = parseFloat(raw);\n    if (!isFinite(n)) return 0;\n    if (String(raw).trim().slice(-1) !== \"%\") return n;\n    var par = node.offsetParent || node.parentElement;\n    var vertical = (prop === \"top\" || prop === \"bottom\" || prop === \"height\");\n    var base = par ? (vertical ? par.clientHeight : par.clientWidth) : 0;\n    return (n / 100) * base;\n  }\n\n  function _dmbCoverCanonicalTop(v) {\n    // A percentage is ALREADY face-relative, which is exactly what \"canonical\" means\n    // here, so it passes through untouched. It used to be run through parseFloat and\n    // re-emitted with \"px\", which turned a spine title's top:50% into top:50px the\n    // first time its state was saved - and because that was written INTO saved state,\n    // the text jumped up the cover on the next re-render and stayed there. (2026-09-14)\n    var str = String(v == null ? \"\" : v).trim();\n    if (str.slice(-1) === \"%\") return str;\n    var t = parseFloat(str);\n    return isFinite(t) ? ((t / _dmbCoverTopScale()) + \"px\") : (v || \"\");\n  }\n\n  // Keep a CHOSEN LAYOUT preset's frames at their EXACT portrait shape on any\n  // page. Portrait \u2192 unchanged. Landscape \u2192 fit the whole layout into a centered\n  // portrait-aspect column (letterboxed, empty space left/right) so no frame is\n  // stretched. Maps a frame's portrait-% {x,y,w,h} to the current page's %.\n  function _dmbLayoutFramePct(f) {\n    var bw = getPageDims().w, bh = getPageDims().h;\n    var boxW = Math.min(bh * (DMB_PAGE_SHORT_PX / DMB_PAGE_LONG_PX), bw); // portrait-aspect width\n    var wPct = (boxW / bw) * 100, lPct = (100 - wPct) / 2;\n    return { x: lPct + f.x * (wPct / 100), y: f.y, w: f.w * (wPct / 100), h: f.h };\n  }\n\n  // Two-photo (A/B) placement on a single page: stacked (top/bottom) in portrait,\n  // side-by-side (left/right) in landscape \u2014 so a wide page reads naturally.\n  function _dmbTwoUpPlacement() {\n    return dmbIsLandscape()\n      ? { a: { top: 0, bottom: 0, left: 0, width: \"50%\" },\n          b: { top: 0, bottom: 0, right: 0, width: \"50%\" } }\n      : { a: { top: 0, height: \"50%\" },\n          b: { bottom: 0, height: \"50%\" } };\n  }\n\n  // Remap free (PIXEL-positioned) objects when the page shape flips, so text,\n  // stickers, icons and manual photo-frames stay on the page instead of falling\n  // off. Percentage-based things (auto-layout slot frames, cover %-frames) adapt\n  // on their own and are deliberately left alone. Each object is center-mapped to\n  // the same RELATIVE spot on the new shape, keeps its size, and is clamped\n  // inside. (Known limitation: clamping makes a Portrait\u2192Landscape\u2192Portrait round\n  // trip drift slightly for edge-hugging objects.)\n  function _dmbRemapPxObjects(arr, fromW, fromH, toW, toH) {\n    if (!Array.isArray(arr)) return;\n    arr.forEach(function (o) {\n      if (!o || typeof o.x !== \"number\" || typeof o.y !== \"number\") return;\n      var w = (typeof o.w === \"number\" ? o.w : 0);\n      var h = (typeof o.h === \"number\" ? o.h : 0);\n      var cx = (o.x + w / 2) / fromW;   // relative center 0..1\n      var cy = (o.y + h / 2) / fromH;\n      var nx = cx * toW - w / 2;\n      var ny = cy * toH - h / 2;\n      o.x = Math.max(0, Math.min(nx, Math.max(0, toW - w)));\n      o.y = Math.max(0, Math.min(ny, Math.max(0, toH - h)));\n    });\n  }\n\n  function reshapeForOrientation(from, to) {\n    if (from === to) return;\n    var portrait  = { w: DMB_PAGE_SHORT_PX, h: DMB_PAGE_LONG_PX };\n    var landscape = { w: DMB_PAGE_LONG_PX,  h: DMB_PAGE_SHORT_PX };\n    var f = (from === \"landscape\") ? landscape : portrait;\n    var t = (to   === \"landscape\") ? landscape : portrait;\n\n    // Interior free objects (per page).\n    if (editor.pageObjects && typeof editor.pageObjects === \"object\") {\n      Object.keys(editor.pageObjects).forEach(function (p) {\n        _dmbRemapPxObjects(editor.pageObjects[p], f.w, f.h, t.w, t.h);\n      });\n    }\n    // Cover free objects: the horizontal extent (back|spine|front) is unchanged by\n    // orientation \u2014 only the face HEIGHT flips (424 \u2194 212). Remap Y so objects\n    // stay on the face for the owner to rearrange. (Positions are approximate.)\n    var faceFromH = (from === \"landscape\") ? Math.round(300 * 210 / 297) : Math.round(300 * 297 / 210);\n    var faceToH   = (to   === \"landscape\") ? Math.round(300 * 210 / 297) : Math.round(300 * 297 / 210);\n    var isMag = !!window.DMB_MAGAZINE_MODE;\n    var spreadW = isMag ? 300 * 2 : (300 * 2 + 22);\n    _dmbRemapPxObjects(editor.coverObjects, spreadW, faceFromH, spreadW, faceToH);\n    // NOTE: cover TEXT nodes are handled differently \u2014 their `top` is stored\n    // canonical (portrait face) and scaled at render time (_dmbCoverTopScale),\n    // so they never clip and flipping can't corrupt them. Nothing to do here.\n  }\n\n  // Silent auto-save (no banner) \u2014 debounced so it doesn't fire on every keystroke\n  let _autoSaveTimer = null;\n  function autoSave() {\n    clearTimeout(_autoSaveTimer);\n    _autoSaveTimer = setTimeout(() => {\n      try {\n        const payload = getCurrentBuilderStateSnapshot();\n        localStorage.setItem(_dmbProjKey(), JSON.stringify(payload));\n      } catch {}\n      _dmbAutoPersist();\n      try { _dmbCloudPersist(); } catch (e) {}\n    }, 800);\n  }\n\n  // \u2500\u2500 Issue 1: keep the per-customer progress saved continuously so a refresh\n  // or app-switch can resume without re-uploading. State \u2192 localStorage (fast,\n  // reliable); photos \u2192 IndexedDB (saved when the photo set changes). \u2500\u2500\n  function _dmbWriteProgressState() {\n    if (_dmbRenderMode()) return;   // render mode: never writes localStorage/IDB/cloud\n    if (!editor || !editor.totalPages) return;   // nothing meaningful to save yet\n    // Mirror heavy crop images to IndexedDB (debounced); the localStorage copy below\n    // is stripped of them so it stays tiny + can't overflow the ~5 MB cap.\n    try { _dmbPersistObjImages(); } catch (e) {}\n    // Every local save also schedules the durable R2 backup (debounced, everyone \u2014\n    // guests + signed-in), so no edit path (e.g. a colour-only change) is missed.\n    try { _dmbCloudPersist(); } catch (e) {}\n    try {\n      const p = getCurrentBuilderStateSnapshot();\n      _dmbStripObjImagesFromSnapshot(p);   // drop inline base64 (kept in IndexedDB)\n      p.savedAt = new Date().toISOString();\n      p._saveType = window.DMB_CARD_MODE ? \"card\" : window.DMB_MAGAZINE_MODE ? \"magazine\" : \"book\";\n      p._templateKey = getTemplateKey() || \"custom\";\n      let prev = null;\n      try { prev = JSON.parse(localStorage.getItem(getCustomerSaveKey()) || \"null\"); } catch (e) {}\n      const urlCover = new URLSearchParams(window.location.search).get(\"cover\") || getTemplateKey() || \"\";\n      p._coverHandle = urlCover || (prev && prev._coverHandle) || \"\";\n      localStorage.setItem(getCustomerSaveKey(), JSON.stringify(p));\n    } catch (e) {}\n  }\n  let _dmbStateTimer = null;\n  function _dmbAutoPersist() {\n    clearTimeout(_dmbStateTimer);\n    _dmbStateTimer = setTimeout(_dmbWriteProgressState, 600);\n  }\n  let _dmbPhotoTimer = null;\n  function _dmbPersistPhotos() {\n    if (_dmbRenderMode()) return;   // render mode: no IndexedDB writes\n    clearTimeout(_dmbPhotoTimer);\n    _dmbPhotoTimer = setTimeout(() => {\n      try {\n        const ph = editor.photos || [];\n        if (!ph.length) return;\n        const sig = ph.map((p) => p.id).join(\",\");\n        if (sig === _dmbPersistPhotos._sig) return;   // unchanged set \u2014 skip churn\n        // Only record the signature as saved AFTER a confirmed successful write,\n        // so a failed save (quota / private browsing) is retried, not skipped.\n        _savePhotosToIDB(getCustomerSaveKey(), ph).then((ok) => {\n          if (ok) { _dmbPersistPhotos._sig = sig; }\n          else { _dmbNotifySaveTrouble(); }\n        }).catch(() => { _dmbNotifySaveTrouble(); });\n      } catch (e) {}\n    }, 1200);\n  }\n\n  // One-time, gentle warning when photos can't be persisted on this device\n  // (storage full, private browsing, or IndexedDB disabled). Prevents the\n  // \"silent loss\" where a customer designs a whole book that never saved.\n  let _dmbSaveTroubleShown = false;\n  function _dmbNotifySaveTrouble() {\n    if (_dmbSaveTroubleShown) return;\n    _dmbSaveTroubleShown = true;\n    try {\n      const el = document.createElement(\"div\");\n      el.style.cssText = \"position:fixed;left:50%;bottom:20px;transform:translateX(-50%);z-index:1000001;background:#7a1f1f;color:#fff;border-radius:12px;padding:13px 18px;max-width:92vw;font-size:13px;font-weight:700;line-height:1.45;box-shadow:0 10px 40px rgba(0,0,0,.4);display:flex;gap:12px;align-items:center;\";\n      el.innerHTML = '<span>\u26A0\uFE0F We couldn\\'t save your photos on this device (storage may be full or private browsing is on). Your design may not be saved \u2014 consider using a laptop or iPad.</span>';\n      const x = document.createElement(\"button\");\n      x.textContent = \"\u00D7\";\n      x.style.cssText = \"background:rgba(255,255,255,.2);border:none;color:#fff;width:26px;height:26px;border-radius:999px;font-size:16px;cursor:pointer;flex-shrink:0;\";\n      x.addEventListener(\"click\", () => { try { el.remove(); } catch (e) {} });\n      el.appendChild(x);\n      document.body.appendChild(el);\n      setTimeout(() => { try { el.remove(); } catch (e) {} }, 12000);\n    } catch (e) {}\n  }\n\n  // Save state synchronously when the page is hidden/closed (leaving the app).\n  // Also flush the photo save: a photo added in the last ~1.2s (before the\n  // debounce fired) would otherwise never be persisted.\n  function _dmbFlushOnHide() {\n    if (_dmbRenderMode()) return;              // render mode: no save on pagehide/visibilitychange\n    try { _dmbTextStyleCommit(); } catch (e) {}   // flush a debounced text-style edit before leaving\n    _dmbWriteProgressState();\n    // Flush the ACCOUNT (cloud) manifest too \u2014 the debounced cloud save may not\n    // have captured the latest photo set, and a cross-device reopen reads ONLY the\n    // cloud. keepalive lets this survive the unload. (Same-device is already covered\n    // by the IDB save below + the cloud/IDB merge on restore.)\n    try { _dmbCloudFlushNow(true); } catch (e) {}\n    try {\n      const ph = editor.photos || [];\n      if (!ph.length) return;\n      const sig = ph.map((p) => p.id).join(\",\");\n      if (sig === _dmbPersistPhotos._sig) return;   // already saved\n      _savePhotosToIDB(getCustomerSaveKey(), ph).then((ok) => {\n        if (ok) _dmbPersistPhotos._sig = sig;\n      }).catch(() => {});\n    } catch (e) {}\n  }\n  window.addEventListener(\"pagehide\", _dmbFlushOnHide);\n  document.addEventListener(\"visibilitychange\", () => {\n    if (document.visibilityState === \"hidden\") _dmbFlushOnHide();\n  });\n\n  // Resume banner shown on a fresh load when saved progress exists (issue 1).\n  // Full-screen loader (the vacation-emoji orbit) shown while a saved design is being\n  // restored \u2014 covers the \"photos take a moment to appear\" gap after clicking Resume.\n  function _dmbShowRestoreLoader() {\n    if (document.getElementById(\"dmbRestoreLoader\")) return;\n    const ov = document.createElement(\"div\");\n    ov.id = \"dmbRestoreLoader\";\n    ov.style.cssText = \"position:fixed;inset:0;z-index:1000002;display:flex;flex-direction:column;align-items:center;justify-content:center;background:rgba(250,247,242,0.98);gap:6px;\";\n    try {\n      const node = _dmbPvMakeLoadingNode();\n      const lbl = node.querySelector(\"div:last-child\"); if (lbl) lbl.textContent = \"Loading your photos & layout\";\n      ov.appendChild(node);\n    } catch (e) {}\n    const msg = document.createElement(\"div\");\n    msg.textContent = \"\u2728 Bringing your design back\u2026\";\n    msg.style.cssText = \"font-size:15px;font-weight:800;color:#1e1e1e;margin-top:2px;\";\n    ov.appendChild(msg);\n    document.body.appendChild(ov);\n    // Safety: never leave the loader stuck if a restore step throws.\n    setTimeout(_dmbHideRestoreLoader, 25000);\n  }\n  function _dmbHideRestoreLoader() { const o = document.getElementById(\"dmbRestoreLoader\"); if (o) o.remove(); }\n\n  // Centered \"welcome back\" modal offering Resume / Start fresh. onResume runs after\n  // the loader is shown; onFresh is optional.\n  function _dmbResumeModal(onResume, onFresh) {\n    if (document.getElementById(\"dmbResumeModal\")) return;\n    const ov = document.createElement(\"div\");\n    ov.id = \"dmbResumeModal\";\n    ov.style.cssText = \"position:fixed;inset:0;z-index:1000000;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,0.45);padding:20px;\";\n    const card = document.createElement(\"div\");\n    card.style.cssText = \"background:#fff;border-radius:18px;max-width:380px;width:100%;padding:28px 24px 22px;box-shadow:0 24px 70px rgba(0,0,0,.4);text-align:center;\";\n    card.innerHTML =\n      '<div style=\"font-size:36px;margin-bottom:8px;\">\uD83D\uDCC2</div>' +\n      '<h3 style=\"margin:0 0 8px;font-size:20px;font-weight:900;color:#1e1e1e;\">Welcome back!</h3>' +\n      '<p style=\"margin:0 0 20px;font-size:14px;line-height:1.55;color:#555;\">We saved your design. Pick up where you left off, or start fresh.</p>';\n    const resumeBtn = document.createElement(\"button\");\n    resumeBtn.textContent = \"Resume my design\";\n    resumeBtn.style.cssText = \"display:block;width:100%;background:#6d3bd4;color:#fff;border:none;border-radius:10px;padding:14px;cursor:pointer;font-weight:900;font-size:15px;margin-bottom:10px;\";\n    const freshBtn = document.createElement(\"button\");\n    freshBtn.textContent = \"Start fresh\";\n    freshBtn.style.cssText = \"display:block;width:100%;background:#f1ebe0;color:#555;border:none;border-radius:10px;padding:12px;cursor:pointer;font-weight:800;font-size:14px;\";\n    card.appendChild(resumeBtn); card.appendChild(freshBtn);\n    ov.appendChild(card); document.body.appendChild(ov);\n    resumeBtn.addEventListener(\"click\", () => { ov.remove(); _dmbShowRestoreLoader(); try { onResume(); } catch (e) {} });\n    freshBtn.addEventListener(\"click\", () => { ov.remove(); try { onFresh && onFresh(); } catch (e) {} });\n  }\n\n  function _dmbShowResumeBanner() {\n    _dmbResumeModal(\n      () => {\n        try { const u = new URL(window.location.href); u.searchParams.set(\"restore\", \"1\"); window.location.assign(u.toString()); }\n        catch (e) { window.location.search = (window.location.search ? window.location.search + \"&\" : \"?\") + \"restore=1\"; }\n      },\n      async () => { try { await clearCustomerProgress(); } catch (e) {} }\n    );\n  }\n\n  // \u2500\u2500 PDF Preview Cache \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // Pages are rendered to JPEG dataURLs in the background using the SAME\n  // renderPage() / renderCoverSpread() code path as the real PDF export.\n  // A 2-second debounce ensures re-renders only happen after edits settle.\n  var _dmbPvCache   = {};       // pageNumber \u2192 JPEG dataURL\n  var _dmbPvCover   = null;     // { back, spine, front } JPEG dataURLs\n  var _dmbPvDirty   = new Set();\n  var _dmbPvRendering = new Set();\n  var _dmbPvTimer   = null;\n  var _dmbPvRunning = false;\n  var _dmbPvRefresh = null;     // callback set when preview overlay is open\n  var _dmbPvGen     = 0;        // generation counter \u2014 incremented on reinit so\n                                // in-flight renders from the previous generation\n                                // know to discard their result instead of writing\n                                // stale data to the (already cleared) cache.\n\n  function _dmbPvMarkDirty(key) {\n    _dmbPvDirty.add(key);\n    clearTimeout(_dmbPvTimer);\n    _dmbPvTimer = setTimeout(_dmbPvRun, 2000);\n  }\n\n  // Vacation-emoji orbit loading animation\n  function _dmbPvMakeLoadingNode() {\n    var emojis = [\"\u2708\uFE0F\",\"\uD83D\uDEC2\",\"\uD83E\uDDF3\",\"\uD83D\uDDFA\uFE0F\",\"\uD83D\uDCF8\",\"\uD83C\uDFD6\uFE0F\"];\n    var outer = document.createElement(\"div\");\n    outer.style.cssText = \"display:flex;flex-direction:column;align-items:center;justify-content:center;gap:14px;width:100%;height:100%;min-height:200px;\";\n    var ring = document.createElement(\"div\");\n    ring.style.cssText = \"position:relative;width:130px;height:130px;\";\n    emojis.forEach(function(em, i) {\n      var s = document.createElement(\"span\");\n      s.className = \"dmb-pv-emoji\";\n      s.textContent = em;\n      s.style.animationDelay = (-i * (3.5 / emojis.length)).toFixed(2) + \"s\";\n      s.style.animationDuration = (3.2 + i * 0.2).toFixed(1) + \"s\";\n      ring.appendChild(s);\n    });\n    var lbl = document.createElement(\"div\");\n    lbl.textContent = \"Rendering\u2026\";\n    lbl.style.cssText = \"color:#888;font-size:11px;letter-spacing:0.4px;\";\n    outer.appendChild(ring);\n    outer.appendChild(lbl);\n    return outer;\n  }\n\n  async function _dmbPvRun() {\n    if (_dmbPvRunning) return;\n    if (_dmbRenderMode()) return;   // render mode: NO background previews (CPU + _dmbNoMasterPids poisoning)\n    _dmbPvRunning = true;\n    var _myGen = _dmbPvGen; // snapshot generation at start \u2014 used to detect reinit\n    try {\n      while (_dmbPvDirty.size > 0) {\n        // Abort entire loop if a reinit happened while we were awaiting\n        if (_dmbPvGen !== _myGen) break;\n        var _pvKeys = Array.from(_dmbPvDirty);\n        for (var _pki = 0; _pki < _pvKeys.length; _pki++) {\n          if (_dmbPvGen !== _myGen) break; // reinit mid-loop \u2014 stop immediately\n          var _pvKey = _pvKeys[_pki];\n          if (_dmbPvRendering.has(_pvKey)) continue;\n          _dmbPvDirty.delete(_pvKey);\n          _dmbPvRendering.add(_pvKey);\n          try {\n            var _pvResult = await new Promise(function(res, rej) {\n              var _pvK = _pvKey;\n              buildPdfBlobOnePagePerBookPage(null, _pvK === \"cover\"\n                ? { cover: true,  resolve: res, reject: rej }\n                : { page:  _pvK,  resolve: res, reject: rej });\n            });\n            // Discard result if a reinit happened while we were rendering\n            if (_dmbPvGen !== _myGen) break;\n            if (_pvKey === \"cover\") {\n              _dmbPvCover = {\n                back:  _pvResult.back.toDataURL(\"image/jpeg\", 0.93),\n                spine: _pvResult.spine.toDataURL(\"image/jpeg\", 0.93),\n                front: _pvResult.front.toDataURL(\"image/jpeg\", 0.93),\n              };\n            } else {\n              _dmbPvCache[_pvKey] = _pvResult.toDataURL(\"image/jpeg\", 0.93);\n            }\n            if (typeof _dmbPvRefresh === \"function\") _dmbPvRefresh(_pvKey);\n          } catch(_pvErr) {\n            if (_dmbPvGen !== _myGen) break; // reinit \u2014 don't retry stale page\n            console.warn(\"[DMB Preview] render failed for\", _pvKey, \":\", _pvErr && _pvErr.message);\n            // Re-mark dirty so it retries on the next edit\n            _dmbPvDirty.add(_pvKey);\n          } finally {\n            _dmbPvRendering.delete(_pvKey);\n          }\n          // Yield between pages so the UI stays responsive\n          await new Promise(function(r) { setTimeout(r, 80); });\n        }\n      }\n    } finally {\n      // Only reset running flag if still our generation \u2014 a newer run took over\n      if (_dmbPvGen === _myGen) _dmbPvRunning = false;\n    }\n  }\n\n  function _dmbPvInitAll() {\n    setTimeout(function() {\n      _dmbPvDirty.add(\"cover\");\n      for (var _pvp = 0; _pvp <= (editor.totalPages || 0); _pvp++) {\n        _dmbPvDirty.add(_pvp);\n      }\n      _dmbPvRunning = false; // allow fresh start\n      _dmbPvRun();\n    }, 1800);\n  }\n\n  // Call after any full state restore (undo/redo, progress restore, project load)\n  // that changes ALL pages at once \u2014 clears stale cache and re-renders everything.\n  function _dmbPvReinitAll() {\n    _dmbPvGen++;            // invalidate ALL in-flight renders from previous generation\n    _dmbPvCache   = {};\n    _dmbPvCover   = null;\n    _dmbPvDirty.clear();\n    _dmbPvRendering.clear();\n    clearTimeout(_dmbPvTimer);\n    _dmbPvRunning = false;\n    _dmbPvInitAll();\n  }\n  // \u2500\u2500 End PDF Preview Cache \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n  // \u2500\u2500 Contain-to-page \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // Clips a free item to its page / cover-zone boundary using a CSS clip-path\n  // polygon computed in the element's own (pre-rotation) coordinate space.\n  //\n  //   obj.contained  0 = off  |  1 = page / zone  |  2 = full spread (cover)\n  //\n  // The polygon technique:\n  //   \u2022 Take the 4 corners of the clip rectangle in page coordinates\n  //   \u2022 Translate them relative to the element centre, then inverse-rotate them\n  //     into the element's local space (so the polygon follows any rotation)\n  //   \u2022 CSS clip-path clips in local space \u2192 after transform the visible region\n  //     is exactly the original rectangle in page space \u2713\n\n  function _dmbContainBounds(obj, isCover) {\n    var FACE_PX  = 300, SPINE_PX = COVER_SPINE_W;\n    var FACE_H   = dmbIsLandscape() ? Math.round(300 * 210 / 297) : Math.round(300 * 297 / 210); // portrait 424, landscape 212\n    var _pd = getPageDims();\n    var PAGE_W   = _pd.w,  PAGE_H = _pd.h; // portrait 270\u00D7382, landscape 382\u00D7270\n    // Magazine has no spine \u2014 front face starts at COVER_BACK_W (300), not COVER_FRONT_X (322)\n    var isMag    = !!window.DMB_MAGAZINE_MODE;\n    var FRONT_X  = isMag ? FACE_PX : (FACE_PX + SPINE_PX);  // 300 (mag) | 322 (book)\n    var SPREAD_W = isMag ? FACE_PX * 2 : (FACE_PX * 2 + SPINE_PX); // 600 | 622\n    if (!isCover) {\n      // Horizontal containment follows the object: a spanning shape clipped at the fold\n      // would be amputated on screen while the PDF printed it whole, because the render\n      // path's own clip is gated on obj.coverZone and so never applies to a page object.\n      // Screen and print must agree. Vertical containment is unchanged - the top and\n      // bottom page edges are real edges on any sheet. (2026-09-14)\n      var _ox = (obj && obj.x) || 0, _ow = (obj && obj.w) || 0;\n      return { l: Math.min(0, _ox), t: 0, r: Math.max(PAGE_W, _ox + _ow), b: PAGE_H };\n    }\n    if ((obj.contained || 0) === 2)\n      return { l: 0, t: 0, r: SPREAD_W, b: FACE_H }; // full spread\n    var zone = obj.coverZone || \"front\";\n    if (zone === \"back\")  return { l: 0,        t: 0, r: FACE_PX,          b: FACE_H };\n    if (zone === \"spine\") return { l: FACE_PX,   t: 0, r: FACE_PX + SPINE_PX, b: FACE_H };\n    return                        { l: FRONT_X,  t: 0, r: FRONT_X + FACE_PX,  b: FACE_H };\n  }\n\n  function _dmbContainClipPath(obj, bounds) {\n    var L = obj.x || 0, T = obj.y || 0, W = obj.w || 100, H = obj.h || 100;\n    var R    = -(obj.rotation || 0) * Math.PI / 180; // inverse rotation\n    var cosR = Math.cos(R), sinR = Math.sin(R);\n    var cx   = L + W / 2, cy = T + H / 2;\n    var pts  = [[bounds.l, bounds.t],[bounds.r, bounds.t],\n                [bounds.r, bounds.b],[bounds.l, bounds.b]].map(function(c) {\n      var dx = c[0] - cx, dy = c[1] - cy;\n      var lx = dx * cosR - dy * sinR + W / 2;\n      var ly = dx * sinR + dy * cosR + H / 2;\n      return lx.toFixed(1) + \"px \" + ly.toFixed(1) + \"px\";\n    });\n    return \"polygon(\" + pts.join(\", \") + \")\";\n  }\n\n  function _dmbApplyContain(wrapEl, obj, isCover) {\n    if (!wrapEl) return;\n    if (!obj || !obj.contained) { wrapEl.style.clipPath = \"\"; return; }\n    wrapEl.style.clipPath = _dmbContainClipPath(obj, _dmbContainBounds(obj, isCover));\n  }\n\n  function _dmbContainBtnText(obj, isCover) {\n    var s = obj.contained || 0;\n    if (s === 0)            return \"Contain: Off\";\n    if (isCover && s === 2) return \"Contain: Spread \u2713\";\n    if (isCover)            return \"Contain: Zone \u2713\";\n    return \"Contain: On \u2713\";\n  }\n\n  function _dmbContainCycle(obj, wrapEl, isCover) {\n    pushUndoState(\"Contain to page\");\n    var max = isCover ? 3 : 2;\n    obj.contained = ((obj.contained || 0) + 1) % max;\n    _dmbApplyContain(wrapEl, obj, isCover);\n    [\"dmbMapContainBtn\",\"dmbShapeContainBtn\",\"dmbFrameContainBtn\"].forEach(function(id) {\n      var btn = document.getElementById(id);\n      if (btn) btn.textContent = _dmbContainBtnText(obj, isCover);\n    });\n  }\n\n  // Re-apply contain clips to all visible item wrappers after any state change\n  function _dmbReapplyAllContains() {\n    document.querySelectorAll(\"[data-object-id]\").forEach(function(el) {\n      var objId   = el.dataset.objectId;\n      var isCover = !!el.closest(\".dmb-cover-preview\");\n      var obj = null;\n      if (isCover) {\n        obj = (editor.coverObjects || []).find(function(o) { return o.id === objId; });\n      } else {\n        var arrs = Object.values(editor.pageObjects || {});\n        for (var _i = 0; _i < arrs.length; _i++) {\n          if (!Array.isArray(arrs[_i])) continue;\n          var _f = arrs[_i].find(function(o) { return o.id === objId; });\n          if (_f) { obj = _f; break; }\n        }\n      }\n      if (obj) _dmbApplyContain(el, obj, isCover);\n    });\n  }\n  // \u2500\u2500 End Contain-to-page \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n  // Cover zone layout constants (px)\n  const COVER_BACK_W = 300; // matches .dmb-cover-face { width: 300px } in CSS\n  // Spine = 8 mm. The 300px face represents the 210 mm cover, so 8 mm \u2248 8/210\u00D7300 \u2248 11px.\n  // This makes the on-screen spine proportional to the real 8 mm (was 22px \u2248 15 mm) and,\n  // since the PDF spine crop reads this same value, the print isn't horizontally squished.\n  const COVER_SPINE_W = 11;\n  const COVER_FRONT_X = COVER_BACK_W + COVER_SPINE_W; // 311\n\n  const DMB_MAG_ICONS = [\n    { id: \"vogue\", label: \"Vogue\", file: \"mag-icon-vogue.png\" },\n    { id: \"elle\", label: \"Elle\", file: \"mag-icon-elle.png\" },\n    { id: \"harpers-bazaar\", label: \"Harper's Bazaar\", file: \"mag-icon-harpers-bazaar.png\" },\n    { id: \"harpers-bazaar-arabian\", label: \"Harper's Bazaar Arabian\", file: \"mag-icon-harpers-bazaar-arabian.png\" },\n  ];\n  const _dmbIconColorCache = {};\n\n  async function dmbGetColoredIconDataUrl(iconId, color) {\n    const key = iconId + \"|\" + (color || \"#000000\");\n    if (_dmbIconColorCache[key]) return _dmbIconColorCache[key];\n    const iconUrl = dmbMagIconUrl(iconId);\n    try {\n      const resp = await fetch(iconUrl, { mode: \"cors\", cache: \"force-cache\" });\n      const blob = await resp.blob();\n      const blobUrl = URL.createObjectURL(blob);\n      const img = await new Promise((res, rej) => {\n        const i = new Image();\n        i.onload = () => res(i);\n        i.onerror = rej;\n        i.src = blobUrl;\n      });\n      URL.revokeObjectURL(blobUrl);\n      const c = document.createElement(\"canvas\");\n      c.width = img.naturalWidth; c.height = img.naturalHeight;\n      const cx = c.getContext(\"2d\");\n      cx.drawImage(img, 0, 0);\n      cx.globalCompositeOperation = \"source-in\";\n      cx.fillStyle = color || \"#000000\";\n      cx.fillRect(0, 0, c.width, c.height);\n      const dataUrl = c.toDataURL(\"image/png\");\n      _dmbIconColorCache[key] = dataUrl;\n      return dataUrl;\n    } catch(e) { return iconUrl; }\n  }\n\n  function dmbMagIconUrl(iconId) {\n    const ic = DMB_MAG_ICONS.find((i) => i.id === iconId);\n    if (!ic) return \"\";\n    const base = (window.DMB_ASSET_BASE || \"\").replace(/\\/[^/]+$/, \"/\");\n    return base + ic.file;\n  }\n\n  function dmbFindObjectById(id, targetType) {\n    if (targetType === \"cover\") {\n      return (editor.coverObjects || []).find((o) => o.id === id) || null;\n    }\n    let found = null;\n    Object.values(editor.pageObjects || {}).forEach((arr) => {\n      if (!found) found = (arr || []).find((o) => o.id === id) || null;\n    });\n    return found;\n  }\n\n  let selectedTextNode = null;\n  let selectedFrameObject = null;\n  let dmbRotateHandle = null;\n  let lastToolbarSide = \"left\";\n  let activeMarkerType = null;\n  let pendingMapMarkerPlacement = false;\n  let currentInsertedMap = null;\n\n  // Migrate old {front,back,spine} format to a flat array using full-cover x coordinates\n  function migrateCoverObjectsToFlat() {\n    if (Array.isArray(editor.coverObjects)) return;\n    const old = editor.coverObjects || {};\n    const flat = [];\n    (old.back || []).forEach(o => flat.push(Object.assign({}, o)));\n    (old.spine || []).forEach(o => flat.push(Object.assign({}, o, { x: (o.x || 0) + COVER_BACK_W })));\n    (old.front || []).forEach(o => flat.push(Object.assign({}, o, { x: (o.x || 0) + COVER_FRONT_X })));\n    editor.coverObjects = flat;\n  }\n\n  function ensureCoverObjects() {\n    if (!Array.isArray(editor.coverObjects)) migrateCoverObjectsToFlat();\n    return editor.coverObjects;\n  }\n\n  function getCoverObjectsForZone(zone) {\n    return ensureCoverObjects();\n  }\n\n  function pushObjectToCoverZone(zone, obj) {\n    ensureCoverObjects().push(obj);\n    return obj;\n  }\n\n  function getCurrentCoverZone() {\n    return activeCoverZone || \"front\";\n  }\n\n  // Put a NEWLY INSERTED cover item on the face the user actually has selected.\n  //\n  // The cover is ONE continuous coordinate space, not three separate canvases:\n  //   back  x 0 .. 300      spine x 300 .. 311      front x 311 .. 611\n  // (in magazine mode there is no spine, so the front face starts at 300.)\n  // So every insert that used a hardcoded corner - x:20 for stickers and icons,\n  // x:60 for shapes - was not \"unplaced\", it was explicitly landing on the BACK\n  // face, whichever face the user had selected. Maps, text, photo frames and\n  // layouts each grew their own zone maths over time; stickers, icons and shapes\n  // never did. This is that maths in one place.\n  //\n  // The item keeps the SAME inset it has always had - it just gets measured from\n  // the selected face's left edge instead of from the cover's. The clamp keeps a\n  // large item from spilling onto the neighbouring face, which would look exactly\n  // like the bug being fixed. (2026-09-14)\n  function _dmbPlaceOnActiveCoverFace(obj, insetX, insetY) {\n    if (!obj) return obj;\n    const zone = getCurrentCoverZone();\n    const w = Number(obj.w) || 0, h = Number(obj.h) || 0;\n    const faceH = Math.round(COVER_BACK_W * 1.414);   // A4 face, same ratio as the CSS\n    if (zone === \"spine\") {\n      // 11px of spine cannot hold an inset. Centre the item on the spine instead so\n      // it straddles the fold symmetrically - the only sensible reading of \"put this\n      // on the spine\" for anything wider than a hairline.\n      obj.x = Math.round(COVER_BACK_W + (COVER_SPINE_W - w) / 2);\n      obj.y = Math.round((faceH - h) / 2);\n      return obj;\n    }\n    const startX = (zone === \"back\") ? 0\n                 : (window.DMB_MAGAZINE_MODE ? COVER_BACK_W : COVER_FRONT_X);\n    obj.x = Math.min(startX + (insetX || 0), startX + Math.max(0, COVER_BACK_W - w));\n    obj.y = Math.min((insetY || 0), Math.max(0, faceH - h));\n    // Tag the face as well. Until an item has been dragged once obj.coverZone is\n    // unset, and dmbAlignObjectInZone's else-branch then treats it as FRONT - so\n    // \"align left\" on a freshly added BACK-cover sticker threw it onto the front\n    // face. Drag-end (_dmbAutoAssignCoverZone) has always written this field; the\n    // insert now agrees with it from the start instead of waiting for a drag.\n    // Deliberately NOT set for the spine above: the zone clip would slice the item\n    // down to the 11px spine and it would look like it had vanished.\n    obj.coverZone = zone;\n    return obj;\n  }\n\n  function getObjectByTarget(targetType, pageNumber, zone, objectId) {\n    const arr = getObjectListForTarget(targetType, pageNumber, zone);\n    return arr.find((o) => o.id === objectId) || null;\n  }\n\n  function deleteFrameObjectByTarget(targetType, pageNumber, zone, objectId) {\n    const arr = getObjectListForTarget(targetType, pageNumber, zone);\n    const idx = arr.findIndex((o) => o.id === objectId);\n    if (idx >= 0) arr.splice(idx, 1);\n    if (selectedFrameObject && selectedFrameObject.id === objectId) {\n      selectedFrameObject = null;\n    }\n  }\n\n  function markActiveCoverZone() {\n    [\"coverFront\", \"coverBack\", \"coverSpine\"].forEach((id) => {\n      const el = document.getElementById(id);\n      if (el) el.classList.remove(\"dmb-cover-zone-active\");\n    });\n\n    const targetId =\n      activeCoverZone === \"back\"\n        ? \"coverBack\"\n        : activeCoverZone === \"spine\"\n        ? \"coverSpine\"\n        : \"coverFront\";\n\n    const activeEl = document.getElementById(targetId);\n    if (activeEl) activeEl.classList.add(\"dmb-cover-zone-active\");\n\n    updateCoverZoneButtons();\n  }\n\n  function duplicateFrameObjectByTarget(targetType, pageNumber, zone, objectId) {\n    const obj = getObjectByTarget(targetType, pageNumber, zone, objectId);\n    if (!obj) return null;\n\n    const copy = {\n      ...cloneState(obj),\n      id: uid(),\n      x: obj.x + 20,\n      y: obj.y + 20,\n      z: Date.now()\n    };\n\n    getObjectListForTarget(targetType, pageNumber, zone).push(copy);\n    return copy;\n  }\n\n  // \u2500\u2500 Z-ORDER STEP HELPERS (2026-05) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // \"\u2B06 Front\" / \"\u2B07 Back\" buttons move an object ONE STEP at a time through\n  // the stack (not jump-to-top / jump-to-bottom).\n  // After the swap, z values are renormalised to multiples of 1000 to avoid\n  // collisions. The BOTTOM item gets z=0 when it reaches the very last\n  // position so it goes behind photos (which use z=1 in makePlacedImageBox).\n  // The PDF renderer (renderPage / renderCoverSpread) reads these z values to\n  // decide whether to draw an object before or after the photo layer.\n  function _stepZ(targetType, pageNumber, zone, objectId, direction) {\n    const list = getObjectListForTarget(targetType, pageNumber, zone);\n    if (!list || list.length === 0) return;\n\n    // Sort by current z (ascending = bottom to top)\n    list.sort((a, b) => (a.z || 0) - (b.z || 0));\n    const idx = list.findIndex(o => o.id === objectId);\n    if (idx === -1) return;\n\n    const newIdx = direction === \"up\"\n      ? Math.min(list.length - 1, idx + 1)\n      : Math.max(0, idx - 1);\n\n    if (newIdx === idx) return; // already at top/bottom of the object stack\n\n    // Swap positions in the sorted array\n    const tmp = list[idx];\n    list[idx] = list[newIdx];\n    list[newIdx] = tmp;\n\n    // Reassign clean z values: step 0 = z 1000, step 1 = z 2000, etc.\n    // The bottom-most item (idx 0) gets z 1000 \u2014 still above placed photos (z=1)\n    // unless this is a \"send backward\" where the object was already at the bottom,\n    // in which case we drop it below photos by setting z = 0.\n    list.forEach((o, i) => { o.z = (i + 1) * 1000; });\n\n    // If sending backward and the object ended up at the very bottom of the\n    // stack, drop it below photos (z=0) so it goes behind images too.\n    if (direction === \"down\" && newIdx === 0) {\n      list[0].z = 0;\n    }\n  }\n\n  function bringFrameForwardByTarget(targetType, pageNumber, zone, objectId) {\n    _stepZ(targetType, pageNumber, zone, objectId, \"up\");\n  }\n\n  function sendFrameBackwardByTarget(targetType, pageNumber, zone, objectId) {\n    _stepZ(targetType, pageNumber, zone, objectId, \"down\");\n  }\n\n  // \u2500\u2500 UNIFIED one-step layering across ALL item types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // Text, stickers, mag-icons, shapes, maps and photo-frames on the same surface\n  // (cover overlay or a page body) share ONE z-order. Forward/back moves the item\n  // one step past the adjacent item of ANY type. z is written to both the DOM and\n  // each item's model, so the builder, the PDF preview and Save Design all agree.\n  const _DMB_LAYER_SEL = \".dmb-editable-text,.dmb-sticker-object,.dmb-mag-icon-object,.dmb-shape-object,.dmb-map-object,.dmb-photo-frame-object\";\n  function _dmbLayerZOf(el) { return Number(el.dataset.z || el.style.zIndex || 0) || 0; }\n  function _dmbLayerSetZ(el, z) {\n    const zs = String(z);\n    el.dataset.z = zs;\n    el.style.zIndex = zs;\n    try {\n      if (el.classList.contains(\"dmb-editable-text\")) {\n        if (el.id && coverState.textStyles && coverState.textStyles[el.id]) coverState.textStyles[el.id].z = zs;\n        const pn = parseInt(el.dataset.pageNumber);\n        if (Number.isFinite(pn) && editor.pageObjects && Array.isArray(editor.pageObjects[pn])) {\n          const o = editor.pageObjects[pn].find((x) => x.id === el.id); if (o) o.z = z;\n        }\n      } else if (el.dataset.objectId) {\n        const oid = el.dataset.objectId;\n        let o = (editor.coverObjects || []).find((x) => x.id === oid);\n        if (!o && editor.pageObjects) {\n          Object.keys(editor.pageObjects).forEach((p) => {\n            const arr = editor.pageObjects[p];\n            if (Array.isArray(arr)) { const f = arr.find((x) => x.id === oid); if (f) o = f; }\n          });\n        }\n        if (o) o.z = z;\n      }\n    } catch (e) {}\n  }\n  function _dmbUnifiedLayerStep(el, dir) {\n    if (!el) return false;\n    const container = el.closest(\"#coverOverlay\") || el.closest(\"[data-page-body='true']\") || el.closest(\"[data-page-body]\") || el.parentElement;\n    if (!container) return false;\n    const nodes = Array.from(container.querySelectorAll(_DMB_LAYER_SEL));\n    if (nodes.length < 2) return false;\n    nodes.sort((a, b) => _dmbLayerZOf(a) - _dmbLayerZOf(b));   // bottom \u2192 top\n    const i = nodes.indexOf(el);\n    if (i === -1) return false;\n    const j = dir === \"forward\" ? i + 1 : i - 1;\n    if (j < 0 || j >= nodes.length) return false;             // already top/bottom\n    nodes.splice(i, 1);\n    nodes.splice(j, 0, el);\n    // Clean ascending z (1000,2000,\u2026): above placed photos (z=1), collision-free.\n    nodes.forEach((n, idx) => _dmbLayerSetZ(n, (idx + 1) * 1000));\n    // Sent fully to the bottom \u2192 drop behind the photo layer (matches _stepZ).\n    if (dir === \"backward\" && nodes[0] === el) _dmbLayerSetZ(el, 0);\n    return true;\n  }\n  // Run a layer step for the selected element, then persist + refresh the preview.\n  function _dmbDoLayerStep(el, dir) {\n    if (!el || !el.isConnected) return;\n    pushUndoState(dir === \"forward\" ? \"Bring Forward\" : \"Send Backward\");\n    _dmbUnifiedLayerStep(el, dir);\n    try { _dmbAutoPersist(); } catch (e) {}\n    try {\n      if (builderMode === \"cover\") _dmbPvMarkDirty(\"cover\");\n      else { _dmbPvMarkDirty(editor.currentSpread * 2 + 1); _dmbPvMarkDirty(editor.currentSpread * 2 + 2); }\n    } catch (e) {}\n  }\n  function _dmbSelectedFrameEl() {\n    if (!selectedFrameObject) return null;\n    try { return document.querySelector('[data-object-id=\"' + selectedFrameObject.id + '\"]'); } catch (e) { return null; }\n  }\n  function ensurePageObjects(pageNumber) {\n    if (!editor.pageObjects) editor.pageObjects = {};\n    if (!editor.pageObjects[pageNumber]) editor.pageObjects[pageNumber] = [];\n    return editor.pageObjects[pageNumber];\n  }\n\n  function getCurrentVisiblePageNumber() {\n    if (builderMode !== \"pages\") return null;\n    const leftPage = editor.currentSpread * 2 + 1;\n    if (leftPage > editor.totalPages) return null;\n    if (!isLockedPage(leftPage)) return leftPage;\n    const rightPage = leftPage + 1;\n    if (rightPage <= editor.totalPages && !isLockedPage(rightPage)) return rightPage;\n    return null;\n  }\n\n  function newFrameObject(target) {\n    return {\n      id: uid(),\n      type: \"photo-frame\",\n      targetType: target?.targetType || \"page\",\n      page: target?.page ?? null,\n      zone: target?.zone || null,\n      x: 40,\n      y: 40,\n      w: 220,\n      h: 160,\n      z: Date.now(),\n      photoId: null\n    };\n  }\n\n  function getObjectById(pageNumber, objectId) {\n    const arr = ensurePageObjects(pageNumber);\n    return arr.find((o) => o.id === objectId) || null;\n  }\n\n  function deleteFrameObject(pageNumber, objectId) {\n    const arr = ensurePageObjects(pageNumber);\n    const idx = arr.findIndex((o) => o.id === objectId);\n    if (idx >= 0) arr.splice(idx, 1);\n    if (selectedFrameObject && selectedFrameObject.id === objectId) {\n      selectedFrameObject = null;\n    }\n  }\n\n  function duplicateFrameObject(pageNumber, objectId) {\n    const obj = getObjectById(pageNumber, objectId);\n    if (!obj) return null;\n    const copy = {\n      ...cloneState(obj),\n      id: uid(),\n      x: obj.x + 20,\n      y: obj.y + 20,\n      z: Date.now()\n    };\n    ensurePageObjects(pageNumber).push(copy);\n    return copy;\n  }\n\n  function bringFrameForward(pageNumber, objectId) {\n    const obj = getObjectById(pageNumber, objectId);\n    if (!obj) return;\n    obj.z = Date.now();\n  }\n\n  function sendFrameBackward(pageNumber, objectId) {\n    const arr = ensurePageObjects(pageNumber);\n    const minZ = arr.length ? Math.min(...arr.map((o) => Number(o.z || 0))) : 0;\n    const obj = getObjectById(pageNumber, objectId);\n    if (!obj) return;\n    obj.z = minZ - 1;\n  }\n\n  // \u2500\u2500 Hide the floating toolbars WHILE an item is being moved/resized \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // The blue frame bar (and the text/map/shape bars) block the view during a drag,\n  // so we hide the open bar the instant the pointer actually moves on an item and\n  // bring the correct one back \u2014 repositioned \u2014 the moment the pointer is released.\n  // Works for every item type on desktop AND touch. Each show fn records itself in\n  // `_dmbLastBarShow`, which we replay on release.\n  let _dmbDragSuppress = false;\n  let _dmbLastBarShow = null;\n  let _dmbBarDown = null;\n  function _dmbHideBarsForDrag() {\n    // DISABLED (per user request): the edit menus now dock in the fixed ribbon, so\n    // they no longer cover the item being moved/resized \u2014 the menu must stay open\n    // throughout a drag/resize/nudge and only close when the item is deselected.\n    // Kept as a no-op so every existing caller is harmless.\n    return;\n  }\n  function _dmbReshowBarAfterDrag() {\n    if (typeof _dmbLastBarShow === \"function\") { try { _dmbLastBarShow(); } catch (e) {} }\n  }\n  // Cheap rotate-handle tracker for use DURING a drag: keeps the fixed-positioned\n  // rotate handle on the moving object without re-running the full (heavy) toolbar\n  // re-init (which rewrites every form control, hides other bars, re-docks \u2014 all\n  // pointless mid-drag since the bar is already open & docked). Deduped to 1 rAF/frame.\n  let _dmbRotTrackRAF = null;\n  function _dmbTrackRotHandle(el) {\n    if (_dmbRotTrackRAF) cancelAnimationFrame(_dmbRotTrackRAF);\n    _dmbRotTrackRAF = requestAnimationFrame(() => {\n      _dmbRotTrackRAF = null;\n      const h = el && el._dmbRotHandle;\n      if (!h || h.style.display === \"none\" || !el.isConnected) return;\n      const r = el.getBoundingClientRect();\n      h.style.left = (r.right + 14) + \"px\";\n      h.style.top  = (r.bottom + 14) + \"px\";\n    });\n  }\n  // The floating edit bars + rotate handles are position:fixed, so a page scroll\n  // would leave them stranded while the object moves away behind them. Reposition\n  // them to follow the selected object on scroll/resize.\n  function _dmbRepositionFloatingChrome() {\n    const _vis = (el) => el && el.style.display !== \"none\";\n    const _mapB = document.getElementById(\"dmbMapEditBar\");\n    const _shpB = document.getElementById(\"dmbShapeEditBar\");\n    const _txtB = document.getElementById(\"dmbTextToolbar\");\n    const _frmB = document.getElementById(\"dmbFrameToolbar\");\n    const barOpen = _vis(_mapB) || _vis(_shpB) ||\n      (_txtB && _txtB.classList.contains(\"is-open\")) ||\n      (_frmB && _frmB.classList.contains(\"is-open\"));\n    if (!barOpen) return;   // nothing selected \u2014 leave the rotate handles alone\n    // The edit bars now dock inside the sticky ribbon and ride along on scroll,\n    // so only the object's rotate handle (fixed-positioned) needs re-tracking.\n    document.querySelectorAll(\n      \".dmb-shape-object.dmb-selected, .dmb-map-object.dmb-selected, \" +\n      \".dmb-photo-frame-object.is-selected, .dmb-sticker-object.is-selected, .dmb-mag-icon-object.is-selected\"\n    ).forEach((el) => {\n      const h = el._dmbRotHandle;\n      if (!h || h.style.display === \"none\" || !el.isConnected) return;\n      const r = el.getBoundingClientRect();\n      h.style.left = (r.right + 14) + \"px\";\n      h.style.top  = (r.bottom + 14) + \"px\";\n    });\n  }\n  (function _dmbWireChromeReposition() {\n    let raf = 0;\n    const onScrollResize = () => {\n      if (raf) return;\n      raf = requestAnimationFrame(() => { raf = 0; try { _dmbRepositionFloatingChrome(); } catch (e) {} });\n    };\n    // capture:true also catches scrolls from inner scrollable containers.\n    window.addEventListener(\"scroll\", onScrollResize, true);\n    window.addEventListener(\"resize\", onScrollResize);\n  })();\n\n  // \u2500\u2500 Rotate-handle governor \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // The object rotate handles (`.dmb-map-rotate-handle`, one per photo-frame /\n  // map / shape / sticker / mag-icon) are position:fixed at z-index 999999 and\n  // are shown/hidden/tracked by ~10 scattered call sites. That fragility caused\n  // recurring bugs: a handle not following its object, not disappearing on\n  // deselect, or showing THROUGH a modal that opened over it (it out-ranks every\n  // modal on z-index, and nothing hides it). This is a single per-frame safety\n  // net that enforces ONE invariant, self-healing whatever a call site missed:\n  //   a fixed rotate handle is visible  \u21D4  its object is selected + on-screen\n  //                                        + no modal/overlay is open.\n  // It only does work while something is selected or a handle is showing, so it\n  // is free at idle. Fast per-drag tracking still comes from _dmbTrackRotHandle;\n  // this just catches the cases those handlers don't (nudge, zoom, re-render,\n  // a modal opening, an orphaned/stale handle).\n  const _DMB_SEL_OBJ_SEL =\n    \".dmb-shape-object.dmb-selected,.dmb-map-object.dmb-selected,\" +\n    \".dmb-photo-frame-object.is-selected,.dmb-sticker-object.is-selected,.dmb-mag-icon-object.is-selected\";\n  function _dmbGovernRotHandles() {\n    // During an active drag the fast per-drag tracker (_dmbTrackRotHandle) already\n    // keeps the handle on the object, and no modal can open mid-drag \u2014 so skip the\n    // governor entirely to keep the perf-sensitive drag path free of its work.\n    if (_dmbDragSuppress) return;\n    const handles = document.querySelectorAll(\".dmb-map-rotate-handle\");\n    const txt = document.getElementById(\"dmbTextRotateHandle\");\n    const txtVisible = !!(txt && txt.classList.contains(\"is-visible\"));\n    // Cheap gate (no getComputedStyle): anything to govern this frame?\n    let anyHandleShown = txtVisible;\n    if (!anyHandleShown) { for (let i = 0; i < handles.length; i++) { if (handles[i].style.display !== \"none\") { anyHandleShown = true; break; } } }\n    const sel = document.querySelector(_DMB_SEL_OBJ_SEL);\n    if (!sel && !anyHandleShown) return;   // idle \u2014 nothing selected, nothing visible\n\n    const open = _dmbModalOpen();          // pricier check \u2014 only reached when active\n    const vw = window.innerWidth, vh = window.innerHeight;\n    // Reads first (rects), then writes, to avoid layout thrash.\n    const wanted = [];\n    if (!open && sel) {\n      document.querySelectorAll(_DMB_SEL_OBJ_SEL).forEach((el) => {\n        const h = el._dmbRotHandle;\n        if (!h || !el.isConnected) return;\n        const r = el.getBoundingClientRect();\n        if ((r.width === 0 && r.height === 0) ||           // not laid out\n            r.right < 0 || r.bottom < 0 || r.left > vw || r.top > vh) return;  // off-screen\n        h._dmbWantLeft = (r.right + 14) + \"px\";\n        h._dmbWantTop  = (r.bottom + 14) + \"px\";\n        wanted.push(h);\n      });\n    }\n    for (let i = 0; i < handles.length; i++) {\n      const h = handles[i];\n      if (wanted.indexOf(h) !== -1) {\n        h.style.left = h._dmbWantLeft; h.style.top = h._dmbWantTop;\n        if (h.style.display === \"none\") h.style.display = \"\";\n      } else if (h.style.display !== \"none\") {\n        h.style.display = \"none\";        // deselected / stale / off-screen / modal open\n      }\n    }\n    // Text rotate handle (position:absolute, z 100002 \u2014 also out-ranks modals):\n    // hide it whenever a modal is open or no text box is selected.\n    if (txtVisible) {\n      const frame = document.getElementById(\"dmbTextSelFrame\");\n      const textSelected = !!(frame && frame.style.display !== \"none\");\n      if (open || !textSelected) txt.classList.remove(\"is-visible\");\n    }\n  }\n  (function _dmbWireRotGovernor() {\n    const loop = () => { try { _dmbGovernRotHandles(); } catch (e) {} requestAnimationFrame(loop); };\n    requestAnimationFrame(loop);\n  })();\n\n  // Phone-style toast: a translucent pill that slides down from the top, holds\n  // briefly, then fades out. Used for transient confirmations (e.g. \"Restored\u2026\").\n  function _dmbToast(message, ms) {\n    try {\n      let host = document.getElementById(\"dmbToastHost\");\n      if (!host) {\n        host = document.createElement(\"div\");\n        host.id = \"dmbToastHost\";\n        document.body.appendChild(host);\n      }\n      const t = document.createElement(\"div\");\n      t.className = \"dmb-toast\";\n      const clean = String(message == null ? \"\" : message).replace(/^\\s*[\u2705\u2714\u2713\u2705]\\s*/, \"\").trim();\n      t.innerHTML = '<span class=\"dmb-toast-ic\"><svg viewBox=\"0 0 24 24\" width=\"15\" height=\"15\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"20 6 9 17 4 12\"/></svg></span><span class=\"dmb-toast-txt\"></span>';\n      t.querySelector(\".dmb-toast-txt\").textContent = clean;\n      host.appendChild(t);\n      requestAnimationFrame(() => t.classList.add(\"dmb-toast-in\"));\n      const dur = ms || 2000;\n      setTimeout(() => {\n        t.classList.remove(\"dmb-toast-in\");\n        t.classList.add(\"dmb-toast-out\");\n        setTimeout(() => { try { t.remove(); } catch (e) {} }, 340);\n      }, dur);\n    } catch (e) {}\n  }\n\n  // Wire the \"nothing selected\" ribbon (Add tiles + Page quick actions). Each\n  // button proxies the existing control so there's a single source of truth.\n  (function _dmbWireEmptyRibbon() {\n    const ph = document.getElementById(\"dmbRibbonPlaceholder\");\n    if (!ph || ph.dataset.dmbWired) return;\n    ph.dataset.dmbWired = \"1\";\n    const clickById = (id) => { const b = document.getElementById(id); if (b) b.click(); };\n    const clickPageAction = (label) => {\n      const b = Array.from(document.querySelectorAll(\".dmb-page-action-btn\"))\n        .find((x) => x.textContent.trim() === label);\n      if (b) b.click();\n    };\n    const ADD = { text: \"canvasTextBtn\", photo: \"canvasPhotoBtn\", shape: \"canvasShapeBtn\", map: \"addMyMapBtn\", sticker: \"dmbStickersBtn\" };\n    ph.addEventListener(\"click\", (e) => {\n      const add = e.target.closest(\"[data-dmb-add]\");\n      if (add) { const id = ADD[add.getAttribute(\"data-dmb-add\")]; if (id) clickById(id); return; }\n      const pg = e.target.closest(\"[data-dmb-page]\");\n      if (!pg) return;\n      const act = pg.getAttribute(\"data-dmb-page\");\n      if (act === \"undo\") clickById(\"undoBtn\");\n      else if (act === \"redo\") clickById(\"redoBtn\");\n      else if (act === \"magic\") clickById(\"smartCreationBtn\");\n      else if (act === \"layout\") clickPageAction(\"Choose layout\");\n      else if (act === \"duplicate\") clickPageAction(\"Duplicate page\");\n      else if (act === \"bg\") _dmbOpenDeskBgPop(pg);\n    });\n  })();\n\n  // \u2500\u2500 Desktop \"Background\" toolbar control \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // A background-colour picker in the over-canvas ribbon. Works for pages (current\n  // spread, Left/Right/Both) AND the cover (recolours coverState.color), for books\n  // and magazines alike \u2014 mirrors the mobile ribbon's Background popup. The model,\n  // rendering and PDF export already honour editor.pageBackgrounds / coverState.color.\n  let _deskBgTarget = \"both\";\n  let _deskBgPop = null;\n  function _deskTargetPageNums() {\n    var bodies = Array.prototype.slice.call(document.querySelectorAll('[data-page-body][data-page-number]'));\n    var nums = bodies.map(function (b) { return parseInt(b.getAttribute(\"data-page-number\"), 10); }).filter(function (n) { return isFinite(n); });\n    if (!nums.length) return [];\n    if (_deskBgTarget === \"left\") return nums.slice(0, 1);\n    if (_deskBgTarget === \"right\") return nums.slice(-1);\n    return nums;\n  }\n  // One undo step per colour drag: a live (input) scrub pushes once, later scrub\n  // events in the same drag reuse that step. Discrete actions always push.\n  var _deskBgUndoTaken = false;\n  function _deskUndoOnce(label, live) {\n    if (!live || !_deskBgUndoTaken) pushUndoState(label);\n    _deskBgUndoTaken = !!live;\n  }\n  function _deskApplyBg(color, live) {\n    var isDefault = !color || String(color).toLowerCase() === \"#ffffff\";\n    if (builderMode === \"cover\") {\n      _deskUndoOnce(\"Cover Background\", live);\n      coverState.color = isDefault ? \"#ffffff\" : color;\n      try { saveCoverDesignToStorage(); } catch (e) {}\n      try { if (coverColorInput) coverColorInput.value = coverState.color; } catch (e) {}\n      try { if (coverColorSwatch) coverColorSwatch.style.background = coverState.color; } catch (e) {}\n      renderCoverCanvas();\n      return;\n    }\n    var targets = _deskTargetPageNums();\n    if (!targets.length) return;\n    _deskUndoOnce(\"Page Background\", live);\n    if (!editor.pageBackgrounds) editor.pageBackgrounds = {};\n    targets.forEach(function (p) { if (isDefault) delete editor.pageBackgrounds[p]; else editor.pageBackgrounds[p] = color; });\n    renderBuilderCanvas();\n    try { renderThumbsRail(); } catch (e) {}\n  }\n  function _deskSyncBgUI() {\n    var pop = _deskBgPop; if (!pop) return;\n    var isCover = (builderMode === \"cover\");\n    var title = pop.querySelector(\"#dmbDeskBgTitle\");\n    var toggle = pop.querySelector(\".dmb-desk-bg-target\");\n    if (title) title.textContent = isCover ? \"Cover background\" : \"Page background\";\n    if (toggle) toggle.style.display = isCover ? \"none\" : \"\";\n    var cur = \"#ffffff\";\n    if (isCover) cur = coverState.color || \"#ffffff\";\n    else { var t = _deskTargetPageNums(); if (t.length) cur = (editor.pageBackgrounds && editor.pageBackgrounds[t[0]]) || \"#ffffff\"; }\n    var inp = pop.querySelector(\"#dmbDeskBgInput\"); if (inp) inp.value = (cur && cur.charAt(0) === \"#\") ? cur : \"#ffffff\";\n    var grid = pop.querySelector(\"#dmbDeskBgSwatches\");\n    var empty = pop.querySelector(\"#dmbDeskBgEmpty\");\n    var recents = (typeof dmbGetRecentColors === \"function\" ? dmbGetRecentColors() : []) || [];\n    if (grid) {\n      grid.innerHTML = \"\";\n      recents.slice(0, 10).forEach(function (hex) {\n        var b = document.createElement(\"button\");\n        b.type = \"button\"; b.className = \"dmb-desk-bg-swatch\"; b.dataset.hex = hex; b.style.background = hex;\n        if (String(hex).toLowerCase() === String(cur).toLowerCase()) b.classList.add(\"active\");\n        grid.appendChild(b);\n      });\n    }\n    if (empty) empty.style.display = recents.length ? \"none\" : \"\";\n  }\n  function _dmbBuildDeskBgPop() {\n    if (_deskBgPop) return _deskBgPop;\n    var pop = document.createElement(\"div\");\n    pop.id = \"dmbDeskBgPop\";\n    // NOT \".dmb-text-color-pop\" \u2014 that class is managed by _dmbMobileRibbonPopovers,\n    // which on desktop strips our inline fixed position and drops the popup to the\n    // page's bottom-left. Own class only; shell styling lives in .dmb-desk-bgpop.\n    pop.className = \"dmb-desk-bgpop\";\n    pop.hidden = true;\n    pop.innerHTML =\n      '<div class=\"dmb-text-color-pop-title\" id=\"dmbDeskBgTitle\">Page background</div>' +\n      '<div class=\"dmb-desk-bg-target\" role=\"group\" aria-label=\"Which page\">' +\n        '<button type=\"button\" data-tgt=\"left\">Left</button>' +\n        '<button type=\"button\" data-tgt=\"right\">Right</button>' +\n        '<button type=\"button\" data-tgt=\"both\" class=\"active\">Both</button>' +\n      '</div>' +\n      '<div class=\"dmb-desk-bg-swatches\" id=\"dmbDeskBgSwatches\"></div>' +\n      '<div class=\"dmb-desk-bg-empty\" id=\"dmbDeskBgEmpty\">Pick a colour below.</div>' +\n      '<button type=\"button\" class=\"dmb-desk-bg-custom\" id=\"dmbDeskBgCustom\">\uFF0B Custom colour\u2026' +\n        '<input type=\"color\" class=\"dmb-desk-bg-input\" id=\"dmbDeskBgInput\" value=\"#ffffff\"></button>' +\n      '<button type=\"button\" class=\"dmb-desk-bg-remove\" id=\"dmbDeskBgRemove\">Remove background</button>';\n    document.body.appendChild(pop);\n    _deskBgPop = pop;\n\n    pop.querySelector(\".dmb-desk-bg-target\").addEventListener(\"click\", function (e) {\n      var b = e.target.closest(\"button[data-tgt]\"); if (!b) return;\n      _deskBgTarget = b.dataset.tgt;\n      pop.querySelectorAll(\".dmb-desk-bg-target button\").forEach(function (x) { x.classList.toggle(\"active\", x === b); });\n    });\n    pop.querySelector(\"#dmbDeskBgSwatches\").addEventListener(\"click\", function (e) {\n      var sw = e.target.closest(\".dmb-desk-bg-swatch\"); if (!sw) return;\n      _deskApplyBg(sw.dataset.hex); _deskSyncBgUI();\n    });\n    var inp = pop.querySelector(\"#dmbDeskBgInput\");\n    var customBtn = pop.querySelector(\"#dmbDeskBgCustom\");\n    customBtn.addEventListener(\"click\", function (e) { if (e.target === inp) return; try { inp.click(); } catch (er) {} });\n    inp.addEventListener(\"input\", function () { _deskApplyBg(inp.value, true); });\n    inp.addEventListener(\"change\", function () { try { dmbAddRecentColor(inp.value); } catch (e) {} _deskApplyBg(inp.value, true); _deskSyncBgUI(); });\n    pop.querySelector(\"#dmbDeskBgRemove\").addEventListener(\"click\", function () { _deskApplyBg(\"\"); _deskSyncBgUI(); });\n\n    document.addEventListener(\"mousedown\", function (e) {\n      if (pop.hidden) return;\n      if (pop.contains(e.target)) return;\n      if (e.target.closest && e.target.closest('[data-dmb-page=\"bg\"]')) return;\n      pop.hidden = true;\n    });\n    return pop;\n  }\n  function _dmbOpenDeskBgPop(anchorBtn) {\n    var pop = _dmbBuildDeskBgPop();\n    if (!pop.hidden) { pop.hidden = true; return; }   // toggle closed\n    _deskBgUndoTaken = false;\n    _deskSyncBgUI();\n    pop.hidden = false;\n    try {\n      var r = anchorBtn.getBoundingClientRect();\n      pop.style.position = \"fixed\";\n      pop.style.top = (r.bottom + 6) + \"px\";\n      pop.style.left = Math.max(8, Math.min(r.left, window.innerWidth - 228)) + \"px\";\n      pop.style.zIndex = \"2147483000\";\n    } catch (e) {}\n  }\n\n  // On phones the object menu docks inside the horizontally-scrolling ribbon, whose\n  // overflow (and the photo dock below) would clip/cover any popup that opens\n  // downward. Fix: when a colour palette / dropdown inside the mobile ribbon opens,\n  // pin it as a FIXED element ABOVE its button (fixed escapes ancestor overflow),\n  // so it floats over the canvas instead of hiding under the photo tray.\n  (function _dmbMobileRibbonPopovers() {\n    // Every custom popup that can open from the mobile ribbon: the colour wheels\n    // (Text/Fill/Border/Shape/Sticker all share .dmb-text-color-pop), the map\n    // options menu, and the text-align menu. Native <select> dropdowns already\n    // render above everything, so they need no help.\n    const POP_SEL = \".dmb-text-color-pop, .dmb-mapopts-menu, .dmb-align-menu\";\n    const isMob = () => window.matchMedia && window.matchMedia(\"(max-width: 749px), (orientation: portrait) and (pointer: coarse) and (max-width: 1024px)\").matches;\n    function isOpen(pop) {\n      if (pop.hidden) return false;\n      const cs = getComputedStyle(pop);\n      return cs.display !== \"none\" && cs.visibility !== \"hidden\";\n    }\n    function place(pop) {\n      // Pin as a BOTTOM SHEET just above the bottom tab bar (#dmbMshTabs sits at\n      // the very bottom of the shell and is always visible). We must NOT anchor\n      // above the ribbon: #dmbMshRibbon sits near the TOP of the mobile shell\n      // (right under the page-nav), so anchoring above it threw the sheet off the\n      // top of the screen \u2014 which is why nothing appeared. A bottom-pinned sheet\n      // can never fly off-screen regardless of where the ribbon or edit bar live.\n      // left+right (not width) stops the compact menus collapsing to a sliver;\n      // position:fixed + !important escapes every ancestor's overflow/stacking so\n      // it works even if the bar never docked into the ribbon.\n      let bottomPx = 12;\n      const tabs = document.getElementById(\"dmbMshTabs\");\n      if (tabs) { const b = tabs.getBoundingClientRect(); if (b.height > 0) bottomPx = Math.max(12, window.innerHeight - b.top + 8); }\n      const set = (k, v) => pop.style.setProperty(k, v, \"important\");\n      set(\"position\", \"fixed\");\n      set(\"top\", \"auto\");\n      set(\"left\", \"10px\");\n      set(\"right\", \"10px\");\n      set(\"width\", \"auto\");\n      set(\"min-width\", \"0\");\n      set(\"max-width\", \"none\");\n      set(\"bottom\", bottomPx + \"px\");\n      set(\"max-height\", Math.max(200, window.innerHeight - bottomPx - 70) + \"px\");\n      set(\"overflow-y\", \"auto\");\n      set(\"z-index\", \"2147483000\");\n    }\n    function clear(pop) {\n      [\"position\",\"top\",\"right\",\"bottom\",\"left\",\"width\",\"min-width\",\"max-width\",\"z-index\",\"max-height\",\"overflow-y\"].forEach((p) => pop.style.removeProperty(p));\n    }\n    // Re-entrancy guard: place()/clear() write inline styles, which the observer\n    // would see as new mutations \u2192 infinite loop. Ignore mutations we caused.\n    let writing = false;\n    function sync() {\n      if (writing) return;\n      writing = true;\n      try {\n        const mob = isMob();\n        document.querySelectorAll(POP_SEL).forEach((pop) => {\n          if (mob && isOpen(pop)) place(pop); else clear(pop);\n        });\n      } finally {\n        // Clear the guard on the next macrotask, AFTER the observer has delivered\n        // (and we've ignored) the mutations from our own writes. setTimeout \u2014 not\n        // rAF, which is paused while the tab/pane is backgrounded.\n        setTimeout(() => { writing = false; }, 0);\n      }\n    }\n    // Fire on ANY open, however it was triggered \u2014 no dependence on the click\n    // target or on the bar having docked into the ribbon. The observer catches\n    // the hidden/style/class flip that opens the popup.\n    try {\n      const mo = new MutationObserver(() => sync());\n      mo.observe(document.documentElement, { subtree: true, attributes: true, attributeFilter: [\"hidden\", \"style\", \"class\"] });\n    } catch (e) {}\n    document.addEventListener(\"click\", () => setTimeout(sync, 0), true);\n    window.addEventListener(\"resize\", sync);\n    window.addEventListener(\"scroll\", () => { if (isMob()) sync(); }, true);\n  })();\n\n  // Unified mobile colour sheet \u2014 built to behave EXACTLY like the page-background\n  // wheel: a self-contained popup attached to <body>, opened by a simple tap. On\n  // phones, EVERY colour-wheel button opens THIS instead of its in-ribbon popup.\n  // The in-ribbon popups depended on the object's edit bar docking into the ribbon\n  // AND the observer lifting them out of the ribbon's overflow \u2014 a chain that was\n  // unreliable on iOS (\"nothing happens on tap\"). Values are written back to each\n  // wheel's real hidden <input type=color>, so every existing apply path\n  // (text / shape / map / sticker) runs unchanged.\n  (function _dmbMshColorSheets() {\n    const isMob = () => window.matchMedia && window.matchMedia(\"(max-width: 749px), (orientation: portrait) and (pointer: coarse) and (max-width: 1024px)\").matches;\n    // wheel button id \u2192 [target <input type=color> id, sheet title]\n    const WHEELS = {\n      dmbTextColorBtn:      [\"dmbTextColor\",           \"Text colour\"],\n      dmbShapeFillBtn:      [\"dmbShapeFill\",           \"Fill colour\"],\n      dmbShapeStrokeBtn:    [\"dmbShapeStroke\",         \"Border colour\"],\n      dmbEditFillBtn:       [\"dmbEditFill\",            \"Fill colour\"],\n      dmbEditStrokeBtn:     [\"dmbEditStroke\",          \"Border colour\"],\n      dmbFrameIconColorBtn: [\"dmbFrameIconColorInput\", \"Colour\"]\n    };\n    let sheet = null, target = null;\n    function build() {\n      if (sheet) return sheet;\n      sheet = document.createElement(\"div\");\n      sheet.id = \"dmbMshColorSheet\";\n      sheet.className = \"dmb-text-color-pop dmb-msh-bgpop\";   // the observer floats it as a bottom sheet\n      sheet.hidden = true;\n      sheet.innerHTML =\n        '<div class=\"dmb-text-color-pop-title\" id=\"dmbMshColorSheetTitle\">Colour</div>' +\n        '<div class=\"dmb-msh-bg-swatches\" id=\"dmbMshColorSwatches\"></div>' +\n        '<div id=\"dmbMshColorEmpty\" style=\"font-size:12px;color:#9a9a9a;padding:2px 2px 0\" hidden>No saved colours yet \u2014 pick one with \u201C\uFF0B Custom colour\u2026\u201D.</div>' +\n        '<button type=\"button\" class=\"dmb-msh-bg-custom\">\uFF0B Custom colour\u2026' +\n          '<input type=\"color\" class=\"dmb-msh-bg-input dmb-color-overlay\" value=\"#000000\"></button>';\n      document.body.appendChild(sheet);\n      const input = sheet.querySelector(\".dmb-msh-bg-input\");\n      const swWrap = sheet.querySelector(\"#dmbMshColorSwatches\");\n      const emptyMsg = sheet.querySelector(\"#dmbMshColorEmpty\");\n      // `save` = add to the saved palette. Only true on a FINAL pick (custom change /\n      // swatch tap) \u2014 NOT during live scrubbing \u2014 so the palette only ever holds\n      // colours the user actually chose (never generated/proposed).\n      const write = (hex, save) => {\n        if (!target || !hex) return;\n        try {\n          target.value = hex;\n          target.dispatchEvent(new Event(\"input\",  { bubbles: true }));   // live preview (applies)\n          // Only fire 'change' on a FINAL pick \u2014 the target's own change listener\n          // saves to the palette, so firing it during live scrubbing would add every\n          // intermediate colour. Live scrub applies via 'input' only.\n          if (save) {\n            target.dispatchEvent(new Event(\"change\", { bubbles: true }));\n            if (hex.toLowerCase() !== \"#ffffff\") dmbAddRecentColor(hex);\n          }\n        } catch (e) {}\n      };\n      // The palette = the user's SAVED colours (shared with desktop via\n      // dmbGetRecentColors / localStorage). No preset/generated swatches.\n      sheet._renderSwatches = () => {\n        const colors = ((typeof dmbGetRecentColors === \"function\") ? dmbGetRecentColors() : []) || [];\n        swWrap.innerHTML = colors.map((h) =>\n          '<button type=\"button\" class=\"dmb-msh-bg-swatch\" data-hex=\"' + h + '\" title=\"' + h + '\" style=\"background:' + h + '\"></button>').join(\"\");\n        const has = colors.length > 0;\n        swWrap.hidden = !has;\n        if (emptyMsg) emptyMsg.hidden = has;\n      };\n      swWrap.addEventListener(\"click\", (e) => {\n        const b = e.target.closest(\".dmb-msh-bg-swatch\"); if (!b) return;\n        // Apply the colour but KEEP the sheet open \u2014 it only closes when the user\n        // dismisses it (outside tap). Mark the tapped swatch active for feedback.\n        e.stopPropagation();\n        write(b.dataset.hex, true);\n        swWrap.querySelectorAll(\".dmb-msh-bg-swatch\").forEach((s) => s.classList.toggle(\"active\", s === b));\n      });\n      const custom = sheet.querySelector(\".dmb-msh-bg-custom\");\n      // NO preventDefault \u2014 a bubbled preventDefault cancels the native picker on iOS.\n      custom.addEventListener(\"click\", (e) => { e.stopPropagation(); if (e.target === input) return; try { input.click(); } catch (e2) {} });\n      input.addEventListener(\"input\",  () => write(input.value, false));   // live preview, not saved\n      // Final pick \u2192 apply + save + refresh swatches. Do NOT hide the sheet here:\n      // some devices fire 'change' WHILE the picker is still open (dragging the\n      // gradient), and hiding the sheet removes the <input> the picker is attached\n      // to, which dismisses the picker mid-edit. The sheet closes on outside-tap.\n      input.addEventListener(\"change\", () => { write(input.value, true); sheet._renderSwatches(); });\n      // Outside-tap closes the sheet \u2014 but NOT while the native colour picker is up\n      // (the picker steals focus; a tap that returns focus must not kill the sheet).\n      document.addEventListener(\"click\", (e) => {\n        if (sheet.hidden) return;\n        if (e.target.closest(\"#dmbMshColorSheet\")) return;\n        if (document.activeElement === input) return;   // picker still focused/open\n        sheet.hidden = true;\n      });\n      return sheet;\n    }\n    function open(inputEl, title) {\n      const s = build();\n      target = inputEl;\n      const t = s.querySelector(\"#dmbMshColorSheetTitle\"); if (t) t.textContent = title || \"Colour\";\n      const input = s.querySelector(\".dmb-msh-bg-input\");\n      try { input.value = /^#[0-9a-f]{6}$/i.test(inputEl.value) ? inputEl.value : \"#000000\"; } catch (e) {}\n      if (typeof s._renderSwatches === \"function\") s._renderSwatches();   // refresh saved colours each open\n      s.hidden = false;\n    }\n    // Capture phase so we intercept BEFORE each wheel button's own in-ribbon toggle.\n    document.addEventListener(\"click\", (e) => {\n      if (!isMob()) return;\n      for (const btnId in WHEELS) {\n        if (e.target.closest(\"#\" + btnId)) {\n          const inputEl = document.getElementById(WHEELS[btnId][0]);\n          if (inputEl) { e.preventDefault(); e.stopPropagation(); open(inputEl, WHEELS[btnId][1]); }\n          return;\n        }\n      }\n    }, true);\n  })();\n\n  /* \u2500\u2500 5 mm \"safe area\" guides \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   * While an object is being dragged, draw a proportional rectangle 5 mm inside\n   * each page edge (real A4 = 210 \u00D7 297 mm) so the customer can keep content 5 mm\n   * clear of the trim. Purely visual: no snapping, no clamping, no messages. The\n   * inset is recomputed from each page's current pixel size, so it stays correct\n   * at any zoom/screen size (works on laptop and phone alike). Removed on release.\n   * \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n  let _dmbBleedGuideEls = [];\n  function _dmbShowBleedGuides() {\n    _dmbHideBleedGuides();\n    const BLEED_MM = 5;\n    // px-per-mm from HEIGHT (always the full page height) \u2192 correct 5 mm at any scale.\n    // Real page height: A4 = 297 mm, A5 = 210 mm.\n    const realHmm = (editor && editor.bookSize === \"A5\") ? 210 : 297;\n    const mkGuide = (host, left, top, width, height) => {\n      if (width <= 0 || height <= 0) return;\n      const g = document.createElement(\"div\");\n      g.className = \"dmb-bleed-guide\";\n      g.style.cssText =\n        \"position:absolute;pointer-events:none;z-index:2147483647;box-sizing:border-box;\" +\n        \"left:\" + left + \"px;top:\" + top + \"px;width:\" + width + \"px;height:\" + height + \"px;\" +\n        \"border:1px dashed rgba(107,98,214,.75);border-radius:1px;\" +\n        // Own compositing layer + contain: the dashed border is rasterized once and just\n        // composited, so dragging an object underneath doesn't force it to repaint.\n        \"transform:translateZ(0);will-change:transform;contain:layout paint;\";\n      host.appendChild(g);\n      _dmbBleedGuideEls.push(g);\n    };\n    // Interior A4 pages \u2014 guide drawn inside each page body. (Hosts already establish\n    // a positioning context for their absolute objects, so no getComputedStyle needed\n    // \u2014 reading it every frame during a re-render forced a reflow and caused drag lag.)\n    document.querySelectorAll('#spreadCanvas [data-page-body=\"true\"]').forEach((pg) => {\n      const h = pg.clientHeight; if (!h) return;\n      const inset = (BLEED_MM / realHmm) * h;\n      mkGuide(pg, inset, inset, pg.clientWidth - 2 * inset, pg.clientHeight - 2 * inset);\n    });\n    // Cover \u2014 one guide per FACE (back + front, each a real A4). The cover is drawn as\n    // back + spine + front with an object overlay (z:9999) on top, and each face is its\n    // own stacking context (z:1), so a face-level guide would hide behind the overlay.\n    // Draw the guides on the .dmb-cover-preview container instead, above the overlay,\n    // positioned over each face \u2014 never across the spine.\n    const cover = document.querySelector(\".dmb-cover-preview\");\n    if (cover) {\n      cover.querySelectorAll(\".dmb-cover-face\").forEach((face) => {\n        const h = face.clientHeight; if (!h) return;\n        const inset = (BLEED_MM / realHmm) * h;\n        mkGuide(cover, face.offsetLeft + inset, face.offsetTop + inset, face.clientWidth - 2 * inset, face.clientHeight - 2 * inset);\n      });\n    }\n  }\n  function _dmbHideBleedGuides() {\n    if (_dmbBleedGuideEls.length) {\n      for (let i = 0; i < _dmbBleedGuideEls.length; i++) { try { _dmbBleedGuideEls[i].remove(); } catch (e) {} }\n      _dmbBleedGuideEls = [];\n    } else {\n      const ex = document.querySelectorAll(\".dmb-bleed-guide\");   // fallback (e.g. leftovers)\n      if (ex.length) ex.forEach((g) => g.remove());\n    }\n  }\n\n  (function _dmbWireDragBarSuppression() {\n    const ITEM_SEL = \".dmb-frame-handle,.dmb-photo-frame-object,.dmb-sticker-object,.dmb-mag-icon-object,.dmb-map-object,.dmb-shape-object,.dmb-editable-text,.dmb-map-marker\";\n    const BAR_SEL  = \"#dmbFrameToolbar,#dmbTextToolbar,#dmbMapEditBar,#dmbShapeEditBar\";\n    document.addEventListener(\"pointerdown\", (e) => {\n      const t = e.target;\n      if (t && t.closest && t.closest(BAR_SEL)) { _dmbBarDown = null; return; } // clicking a bar button\n      const onItem = t && t.closest && t.closest(ITEM_SEL);\n      _dmbBarDown = onItem ? { x: e.clientX, y: e.clientY } : null;\n    }, true);\n    document.addEventListener(\"pointermove\", (e) => {\n      if (!_dmbBarDown) return;\n      if (_dmbDragSuppress) return;   // already dragging \u2014 do NO per-move work (guides\n                                      // were created once at drag-start and persist)\n      if (Math.hypot(e.clientX - _dmbBarDown.x, e.clientY - _dmbBarDown.y) > 3) {\n        _dmbDragSuppress = true;\n        _dmbHideBarsForDrag();\n        try { _dmbShowBleedGuides(); } catch (e2) {}   // show the 5 mm safe-area guides during the drag\n      }\n    }, true);\n    const _end = () => {\n      if (_dmbDragSuppress) { _dmbDragSuppress = false; _dmbReshowBarAfterDrag(); }\n      try { _dmbHideBleedGuides(); } catch (e2) {}      // remove guides on release\n      _dmbBarDown = null;\n    };\n    document.addEventListener(\"pointerup\", _end, true);\n    document.addEventListener(\"pointercancel\", _end, true);\n  })();\n\n  // SAFETY NET for touch drag lag that GROWS over a session: most object drag\n  // handlers remove their document \"pointermove\" listener only on \"pointerup\", NOT\n  // on \"pointercancel\". On touch a cancelled pointer (second finger, OS reclaim,\n  // gesture conflict) then leaks that drag's pointermove handler FOREVER \u2014 every\n  // future move runs it too, so the builder gets laggier the more you drag. When a\n  // cancel lands during an active object drag/press, synthesize a matching pointerup\n  // so every handler's onUp cleanup path runs and nothing leaks.\n  (function _dmbCancelLeakGuard() {\n    let _synth = false;   // reentrancy guard (our synthetic pointerup must not loop)\n    document.addEventListener(\"pointercancel\", (e) => {\n      if (_synth) return;\n      // A bare pointerup is harmless when no drag is active (the per-drag onUp\n      // handlers only exist during a drag; nothing else keys off a lone pointerup),\n      // so synthesize one on EVERY cancel \u2014 this runs whatever cleanup a live drag\n      // registered, whether or not the suppression flags were already cleared.\n      _synth = true;\n      try {\n        document.dispatchEvent(new PointerEvent(\"pointerup\", {\n          bubbles: true, cancelable: true,\n          pointerId: e.pointerId, pointerType: e.pointerType, isPrimary: e.isPrimary,\n          clientX: e.clientX, clientY: e.clientY\n        }));\n      } catch (err) {}\n      _synth = false;\n    }, true);\n  })();\n\n  function hideFrameToolbar() {\n    const bar = document.getElementById(\"dmbFrameToolbar\");\n    if (bar) bar.classList.remove(\"is-open\");\n    document.querySelectorAll(\".dmb-photo-frame-object, .dmb-sticker-object\").forEach((el) => {\n      el.classList.remove(\"is-selected\");\n      if (el._dmbRotHandle) el._dmbRotHandle.style.display = \"none\";\n    });\n    selectedFrameObject = null;\n    try { _dmbUpdateRibbonPlaceholder(); } catch (e) {}\n  }\n\n  // Reposition the frame toolbar (and the object's rotation handle) inside a\n  // deduped rAF. Reading getBoundingClientRect right after a drag's style write\n  // forces a synchronous reflow every frame \u2014 doing it in rAF removes that\n  // layout thrashing, which is what made touch drag/resize lag (\"slack\").\n  let _dmbToolbarPosRAF = null;\n  function _dmbPositionFrameToolbar(bar, el, root) {\n    if (_dmbToolbarPosRAF) cancelAnimationFrame(_dmbToolbarPosRAF);\n    _dmbToolbarPosRAF = requestAnimationFrame(() => {\n      _dmbToolbarPosRAF = null;\n      if (!bar || !el || !el.isConnected || !root) return;\n      const er = el.getBoundingClientRect();\n      // Dock the frame toolbar into the fixed ribbon; the rotate handle stays on\n      // the object (fixed-positioned, follows it on scroll).\n      bar.classList.add(\"is-open\");\n      _dmbDockBar(bar);\n      if (el._dmbRotHandle) {\n        el._dmbRotHandle.style.left = (er.right + 14) + \"px\";\n        el._dmbRotHandle.style.top = (er.bottom + 14) + \"px\";\n        el._dmbRotHandle.style.display = \"\";\n      }\n    });\n  }\n\n  function showFrameToolbarFor(el, obj, meta) {\n    const bar = document.getElementById(\"dmbFrameToolbar\");\n    const root = document.getElementById(\"dmb-root\");\n    if (!bar || !root || !el || !obj) return;\n    // Remember how to re-open this bar, and stay hidden while a drag is in progress.\n    _dmbLastBarShow = () => showFrameToolbarFor(el, obj, meta);\n    // (drag no longer hides the bar \u2014 it stays open while moving/resizing)\n\n    // During a drag this is called on every pointermove for the SAME object.\n    // Only do the one-time selection bookkeeping when the selection actually\n    // changes \u2014 the rest of the time we just reposition (cheaply, via rAF).\n    const _sameSel = selectedFrameObject && selectedFrameObject.id === obj.id && el.classList.contains(\"is-selected\");\n\n    // Fast path while dragging THIS already-selected object: the bar is already open\n    // and docked, so skip the per-move re-init and just track the rotate handle.\n    if (_dmbDragSuppress && _sameSel) { _dmbTrackRotHandle(el); return; }\n\n    selectedFrameObject = {\n      id: obj.id,\n      type: obj.type || \"sticker\",\n      targetType: meta?.targetType || obj.targetType || \"page\",\n      pageNumber: meta?.pageNumber ?? obj.page ?? null,\n      zone: meta?.zone || obj.zone || null\n    };\n\n    // Cheap button-state updates (kept always so e.g. the polaroid shadow label\n    // refreshes when toggled while the object stays selected).\n    const iconControls = document.getElementById(\"dmbFrameIconControls\");\n    if (iconControls) iconControls.style.display = obj.type === \"mag-icon\" ? \"\" : \"none\";\n    const _containBtn = document.getElementById(\"dmbFrameContainBtn\");\n    if (_containBtn) {\n      const _showContain = obj.type !== \"photo-frame\";\n      _containBtn.style.display = _showContain ? \"\" : \"none\";\n      if (_showContain) _containBtn.textContent = _dmbContainBtnText(obj, meta?.targetType === \"cover\");\n    }\n    const _shadowBtn = document.getElementById(\"dmbFramePolaroidShadowBtn\");\n    if (_shadowBtn) {\n      if (obj.polaroid) {\n        _shadowBtn.style.display = \"\";\n        _shadowBtn.textContent = (obj.shadow === false) ? \"Shadow: Off\" : \"Shadow: On\";\n      } else {\n        _shadowBtn.style.display = \"none\";\n      }\n    }\n    if (obj.type === \"mag-icon\") {\n      const colorInput = document.getElementById(\"dmbFrameIconColorInput\");\n      if (colorInput) colorInput.value = obj.color || \"#000000\";\n      try { _dmbSyncFrameIconDot(); } catch (e) {}\n    }\n\n    if (!_sameSel) {\n      // Heavy one-time work \u2014 only on a genuine (re)selection, never per move.\n      hideTextToolbar();\n      dmbCloseEditBar();\n      dmbCloseShapeEditBar();\n      document.querySelectorAll(\".dmb-photo-frame-object, .dmb-sticker-object\").forEach((node) => {\n        if (node === el) return;\n        node.classList.remove(\"is-selected\");\n        if (node._dmbRotHandle) node._dmbRotHandle.style.display = \"none\";\n      });\n      el.classList.add(\"is-selected\");\n    }\n\n    _dmbPositionFrameToolbar(bar, el, root);\n  }\n\n  function ensureFrameToolbar() {\n    if (document.getElementById(\"dmbFrameToolbar\")) return;\n\n    const root = document.getElementById(\"dmb-root\");\n    if (!root) return;\n\n    const bar = document.createElement(\"div\");\n    bar.id = \"dmbFrameToolbar\";\n    bar.className = \"dmb-frame-toolbar dmb-ribbon-bar\";\n    bar.innerHTML = `\n      <div class=\"dmb-ribbon-r1\">\n        <button type=\"button\" id=\"dmbFrameShapeBtn\" class=\"dmb-frame-tool-btn dmb-frame-shape-chip\" title=\"Frame shape\"><svg viewBox=\"0 0 22 22\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\"><circle cx=\"11\" cy=\"11\" r=\"9\"/><polygon points=\"11,4 13.5,9 19,9.5 15,13.5 16.5,19 11,16 5.5,19 7,13.5 3,9.5 8.5,9\" fill=\"currentColor\" stroke=\"none\" opacity=\"0.45\"/></svg> Frame shape</button>\n        <button type=\"button\" id=\"dmbFramePolaroidShadowBtn\" class=\"dmb-frame-tool-btn\" title=\"Toggle polaroid shadow\" style=\"display:none;\">Shadow: On</button>\n        <span id=\"dmbFrameIconControls\" style=\"display:none;\">\n          <span class=\"dmb-text-color-wrap\" id=\"dmbFrameColorWrap\">\n            <button type=\"button\" id=\"dmbFrameIconColorBtn\" class=\"dmb-frame-tool-btn dmb-text-colorwheel\" title=\"Icon colour\" aria-haspopup=\"true\">\n              <span class=\"dmb-cw-ring\"></span><span class=\"dmb-cw-dot\" id=\"dmbFrameIconColorDot\"></span>\n            </button>\n            <input type=\"color\" id=\"dmbFrameIconColorInput\" value=\"#000000\" style=\"position:absolute;opacity:0;pointer-events:none;width:0;height:0;\">\n            <div class=\"dmb-text-color-pop\" id=\"dmbFrameColorPop\" hidden>\n              <div class=\"dmb-text-color-pop-title\">Icon colour</div>\n              <button type=\"button\" id=\"dmbFrameColorCustom\" class=\"dmb-text-color-custom\">\uFF0B Custom colour\u2026</button>\n            </div>\n          </span>\n          <button type=\"button\" id=\"dmbFrameIconFitBtn\" class=\"dmb-frame-tool-btn\" title=\"Fit to page\">Fit to page</button>\n        </span>\n      </div>\n      <div class=\"dmb-ribbon-r2\">\n        <span class=\"dmb-rgrp\">\n          <span class=\"dmb-seg\" role=\"group\" aria-label=\"Align on page\">\n            <button type=\"button\" id=\"dmbFrameAlignLeftBtn\" title=\"Align left on page\" aria-label=\"Align left on page\"><svg viewBox=\"0 0 24 24\" width=\"15\" height=\"15\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"><line x1=\"4\" y1=\"6\" x2=\"20\" y2=\"6\"/><line x1=\"4\" y1=\"12\" x2=\"13\" y2=\"12\"/><line x1=\"4\" y1=\"18\" x2=\"17\" y2=\"18\"/></svg></button>\n            <button type=\"button\" id=\"dmbFrameAlignCenterBtn\" title=\"Centre on page\" aria-label=\"Centre on page\"><svg viewBox=\"0 0 24 24\" width=\"15\" height=\"15\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"><line x1=\"4\" y1=\"6\" x2=\"20\" y2=\"6\"/><line x1=\"7\" y1=\"12\" x2=\"17\" y2=\"12\"/><line x1=\"6\" y1=\"18\" x2=\"18\" y2=\"18\"/></svg></button>\n            <button type=\"button\" id=\"dmbFrameAlignRightBtn\" title=\"Align right on page\" aria-label=\"Align right on page\"><svg viewBox=\"0 0 24 24\" width=\"15\" height=\"15\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"><line x1=\"4\" y1=\"6\" x2=\"20\" y2=\"6\"/><line x1=\"11\" y1=\"12\" x2=\"20\" y2=\"12\"/><line x1=\"7\" y1=\"18\" x2=\"20\" y2=\"18\"/></svg></button>\n          </span>\n          <span class=\"dmb-rgl\">on page</span>\n        </span>\n        <span class=\"dmb-rdiv\"></span>\n        <span class=\"dmb-rgrp\">\n          <span class=\"dmb-rrow\">\n            <button type=\"button\" id=\"dmbFrameFrontBtn\" class=\"dmb-ic-btn\" title=\"Bring forward\" aria-label=\"Bring forward\"><svg viewBox=\"0 0 24 24\" width=\"15\" height=\"15\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"12\" y1=\"19\" x2=\"12\" y2=\"5\"/><polyline points=\"5 12 12 5 19 12\"/></svg></button>\n            <button type=\"button\" id=\"dmbFrameBackBtn\" class=\"dmb-ic-btn\" title=\"Send backward\" aria-label=\"Send backward\"><svg viewBox=\"0 0 24 24\" width=\"15\" height=\"15\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"12\" y1=\"5\" x2=\"12\" y2=\"19\"/><polyline points=\"19 12 12 19 5 12\"/></svg></button>\n          </span>\n          <span class=\"dmb-rgl\">layer</span>\n        </span>\n        <span class=\"dmb-rdiv\"></span>\n        <span class=\"dmb-rgrp\">\n          <span class=\"dmb-rrow\">\n            <button type=\"button\" id=\"dmbFrameDuplicateBtn\" class=\"dmb-ic-btn\" title=\"Duplicate\" aria-label=\"Duplicate\"><svg viewBox=\"0 0 24 24\" width=\"15\" height=\"15\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"9\" y=\"9\" width=\"12\" height=\"12\" rx=\"2\"/><path d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"/></svg></button>\n            <button type=\"button\" id=\"dmbFrameContainBtn\" class=\"dmb-contain-chip\" title=\"Contain to page \u2014 clip item to page/zone boundary\" style=\"display:none;\">Contain: Off</button>\n            <button type=\"button\" id=\"dmbFrameDeleteBtn\" class=\"dmb-ic-btn\" title=\"Delete\" aria-label=\"Delete\"><svg viewBox=\"0 0 24 24\" width=\"15\" height=\"15\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"3 6 5 6 21 6\"/><path d=\"M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6\"/><path d=\"M10 11v6\"/><path d=\"M14 11v6\"/><path d=\"M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2\"/></svg></button>\n          </span>\n          <span class=\"dmb-rgl\">edit</span>\n        </span>\n      </div>\n    `;\n    root.appendChild(bar);\n\n  [\"left\", \"center\", \"right\"].forEach(function(align) {\n    const btnId = { left: \"dmbFrameAlignLeftBtn\", center: \"dmbFrameAlignCenterBtn\", right: \"dmbFrameAlignRightBtn\" }[align];\n    document.getElementById(btnId)?.addEventListener(\"click\", () => {\n      if (!selectedFrameObject) return;\n      const { id, targetType } = selectedFrameObject;\n      let obj = null;\n      if (targetType === \"cover\") {\n        obj = (editor.coverObjects || []).find(function(o) { return o.id === id; });\n      } else {\n        Object.values(editor.pageObjects || {}).forEach(function(arr) {\n          if (!obj) { const f = arr.find(function(o) { return o.id === id; }); if (f) obj = f; }\n        });\n      }\n      const el = document.querySelector(`[data-object-id=\"${id}\"]`);\n      if (!obj || !el) return;\n      dmbAlignObjectInZone(el, obj, align, targetType);\n      showFrameToolbarFor(el, obj, { targetType, pageNumber: selectedFrameObject.pageNumber, zone: selectedFrameObject.zone });\n    });\n  });\n\n  document.getElementById(\"dmbFrameDuplicateBtn\")?.addEventListener(\"click\", () => {\n    if (!selectedFrameObject) return;\n    pushUndoState(\"Duplicate Frame\");\n    const dup = duplicateFrameObjectByTarget(\n      selectedFrameObject.targetType,\n      selectedFrameObject.pageNumber,\n      selectedFrameObject.zone,\n      selectedFrameObject.id\n    );\n    if (builderMode === \"cover\") renderCoverCanvas(); else renderSpread();\n    if (dup) {\n      requestAnimationFrame(() => {\n        const el = document.querySelector(`.dmb-photo-frame-object[data-object-id=\"${dup.id}\"]`);\n        if (el) {\n          showFrameToolbarFor(el, dup, {\n            targetType: dup.targetType,\n            pageNumber: dup.page ?? null,\n            zone: dup.zone || null\n          });\n        }\n      });\n    }\n  });\n\n  document.getElementById(\"dmbFrameFrontBtn\")?.addEventListener(\"click\", () => {\n    _dmbDoLayerStep(_dmbSelectedFrameEl(), \"forward\");\n  });\n\n  document.getElementById(\"dmbFrameBackBtn\")?.addEventListener(\"click\", () => {\n    _dmbDoLayerStep(_dmbSelectedFrameEl(), \"backward\");\n  });\n\n  document.getElementById(\"dmbFrameShapeBtn\")?.addEventListener(\"click\", () => {\n    if (!selectedFrameObject) return;\n    const obj = dmbFindObjectById(selectedFrameObject.id, selectedFrameObject.targetType);\n    const btn = document.getElementById(\"dmbFrameShapeBtn\");\n    if (!btn) return;\n    showDmbFramePicker(btn, obj ? (obj.frameShape || \"\") : \"\", (shapeId) => {\n      if (obj) {\n        obj.frameShape = shapeId;\n        const el = document.querySelector(`[data-object-id=\"${selectedFrameObject.id}\"] img`);\n        if (el) applyDmbFrameShape(el, shapeId);\n      }\n      if (builderMode === \"cover\") renderCoverCanvas(); else renderSpread();\n    });\n  });\n\n  document.getElementById(\"dmbFramePolaroidShadowBtn\")?.addEventListener(\"click\", () => {\n    if (!selectedFrameObject) return;\n    const obj = dmbFindObjectById(selectedFrameObject.id, selectedFrameObject.targetType);\n    if (!obj || !obj.polaroid) return;\n    pushUndoState(\"Toggle Polaroid Shadow\");\n    obj.shadow = (obj.shadow === false) ? true : false;\n    if (builderMode === \"cover\") renderCoverCanvas(); else renderSpread();\n    requestAnimationFrame(() => {\n      const el = document.querySelector(`.dmb-photo-frame-object[data-object-id=\"${obj.id}\"]`);\n      if (el) showFrameToolbarFor(el, obj, {\n        targetType: selectedFrameObject.targetType,\n        pageNumber: selectedFrameObject.pageNumber,\n        zone: selectedFrameObject.zone\n      });\n    });\n  });\n\n  document.getElementById(\"dmbFrameDeleteBtn\")?.addEventListener(\"click\", () => {\n    if (!selectedFrameObject) return;\n    pushUndoState(\"Delete Frame\");\n    deleteFrameObjectByTarget(\n      selectedFrameObject.targetType,\n      selectedFrameObject.pageNumber,\n      selectedFrameObject.zone,\n      selectedFrameObject.id\n    );\n    hideFrameToolbar();\n    if (builderMode === \"cover\") renderCoverCanvas(); else renderSpread();\n  });\n\n  // \u2500\u2500 Full desktop keyboard control of the selected canvas object \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  //   Delete/Backspace = delete \u00B7 arrows = nudge (Shift = 10px, else 1px) \u00B7\n  //   Ctrl/\u2318+D = duplicate \u00B7 Ctrl/\u2318+C / V = copy / paste \u00B7 Ctrl/\u2318+Z = undo \u00B7\n  //   Ctrl/\u2318+Shift+Z or Ctrl+Y = redo \u00B7 Esc = deselect.\n  //   Works for photo frames, stickers, magazine icons, shapes, maps and text.\n  //   All shortcuts are ignored while typing in a text box / input so they never\n  //   eat characters (the browser's own text editing + undo keep working there).\n  let _dmbClipboard = null;\n  let _dmbNudgeBurst = false, _dmbNudgeTimer = null;\n  function _dmbBeginNudgeUndo() {\n    if (!_dmbNudgeBurst) {\n      pushUndoState(\"Move\");                 // one undo entry per burst\n      _dmbNudgeBurst = true;\n      _dmbDragSuppress = true;               // hide the floating menu while nudging\u2026\n      try { _dmbHideBarsForDrag(); } catch (e) {}\n    }\n    clearTimeout(_dmbNudgeTimer);\n    _dmbNudgeTimer = setTimeout(() => {       // \u2026and bring it back once the keys go quiet\n      _dmbNudgeBurst = false;\n      _dmbDragSuppress = false;\n      try { _dmbReshowBarAfterDrag(); } catch (e) {}\n    }, 500);\n  }\n  function _dmbActiveSel() {\n    if (typeof selectedTextNode !== \"undefined\" && selectedTextNode && selectedTextNode.isConnected) return { kind: \"text\", node: selectedTextNode };\n    if (dmbEditingMapObj && dmbEditingMapEl && dmbEditingMapEl.isConnected) return { kind: \"map\", obj: dmbEditingMapObj, el: dmbEditingMapEl };\n    if (dmbEditingShapeObj && dmbEditingShapeEl && dmbEditingShapeEl.isConnected) return { kind: \"shape\", obj: dmbEditingShapeObj, el: dmbEditingShapeEl };\n    if (selectedFrameObject) {\n      const el = document.querySelector('[data-object-id=\"' + selectedFrameObject.id + '\"]');\n      const obj = dmbFindObjectById(selectedFrameObject.id, selectedFrameObject.targetType);\n      if (el && obj) return { kind: \"frame\", obj, el, sfo: selectedFrameObject };\n    }\n    return null;\n  }\n  function _dmbReselectObj(obj) {\n    const el = document.querySelector('[data-object-id=\"' + obj.id + '\"]');\n    if (!el) return;\n    if (obj.type === \"map\")        { try { dmbOpenEditBar(obj, el); } catch (e) {} }\n    else if (obj.type === \"shape\") { try { dmbOpenShapeEditBar(obj, el); } catch (e) {} }\n    else { try { showFrameToolbarFor(el, obj, { targetType: obj.targetType, pageNumber: obj.page, zone: obj.zone }); } catch (e) {} }\n  }\n\n  // \u2500\u2500 Click-through selection \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // A text box has a big, mostly-transparent hit area, so a small shape tucked\n  // under it is hard to click. These helpers let a click that lands on a text\n  // box's EMPTY area (or an Alt-click anywhere) select the item BENEATH instead.\n  function _dmbFindAnyObjectById(id) {\n    return dmbFindObjectById(id, \"page\") || dmbFindObjectById(id, \"cover\");\n  }\n  // True when (x,y) is over the node's actual glyphs (not its transparent padding).\n  function _dmbPointOnGlyphs(node, x, y) {\n    try {\n      const r = document.createRange();\n      r.selectNodeContents(node);\n      const rects = r.getClientRects();\n      const PAD = 4;\n      for (const rc of rects) {\n        if (rc.width < 0.5 && rc.height < 0.5) continue;\n        if (x >= rc.left - PAD && x <= rc.right + PAD && y >= rc.top - PAD && y <= rc.bottom + PAD) return true;\n      }\n    } catch (e) {}\n    return false;\n  }\n  // The topmost selectable canvas item under (x,y), skipping `exceptEl` and the\n  // floating toolbars. Returns { el, obj } or null.\n  function _dmbSelectableBeneath(x, y, exceptEl) {\n    let stack = [];\n    try { stack = document.elementsFromPoint(x, y) || []; } catch (e) { return null; }\n    for (const el of stack) {\n      if (!el || el === exceptEl || (exceptEl && exceptEl.contains && exceptEl.contains(el))) continue;\n      if (el.closest && (el.closest(\"#dmbTextToolbar\") || el.closest(\"#dmbFrameToolbar\") ||\n                         el.closest(\".dmb-map-edit-bar\") || el.closest(\".dmb-text-sel-frame\"))) continue;\n      // Frames, stickers, shapes and maps all carry data-object-id.\n      const objEl = el.closest && el.closest(\"[data-object-id]\");\n      if (objEl && objEl !== exceptEl) {\n        const obj = _dmbFindAnyObjectById(objEl.getAttribute(\"data-object-id\"));\n        if (obj) return { el: objEl, obj };\n      }\n      // Another text box beneath.\n      const txt = el.closest && el.closest(\".dmb-editable-text\");\n      if (txt && txt !== exceptEl) return { el: txt, obj: { __text: true, node: txt } };\n    }\n    return null;\n  }\n  // Select a specific item (any type) by its element/obj.\n  function _dmbSelectObjectEl(obj, el) {\n    if (!obj) return;\n    if (obj.__text) { try { showTextToolbarFor(obj.node); } catch (e) {} return; }\n    try { hideTextToolbar(); } catch (e) {}\n    if (obj.type === \"map\")        { try { dmbOpenEditBar(obj, el); } catch (e) {} }\n    else if (obj.type === \"shape\") { try { dmbOpenShapeEditBar(obj, el); } catch (e) {} }\n    else { try { showFrameToolbarFor(el, obj, { targetType: obj.targetType, pageNumber: obj.page, zone: obj.zone }); } catch (e) {} }\n  }\n  function _dmbNudge(sel, dx, dy) {\n    _dmbBeginNudgeUndo();\n    if (sel.kind === \"text\") {\n      const n = sel.node;\n      // Resolve through _dmbLenPx first: a spine title's top is \"50%\", and adding dy\n      // to the bare number 50 turned one arrow press into a 162px jump up the cover.\n      // Converting to px HERE is right - the user is moving it, so it becomes an\n      // absolute position - but it must convert from where the text actually is.\n      n.style.left = (_dmbLenPx(n, \"left\") + dx) + \"px\";\n      n.style.top  = (_dmbLenPx(n, \"top\")  + dy) + \"px\";\n      try { saveCoverTextNodeState(n); } catch (e) {}\n      try { savePageTextState(n); } catch (e) {}\n      try { showTextToolbarFor(n); } catch (e) {}\n      // Show the same alignment/spacing guides as a mouse drag (no snapping).\n      try {\n        const parent = n.parentElement;\n        if (parent) {\n          const { gH, gV } = dmbGetSnapGuides(parent);\n          const _isCover = !!parent.closest(\".dmb-cover-preview\");\n          const _tpn = parseInt(n.dataset.pageNumber);\n          const _tmeta = _isCover ? { targetType: \"cover\" } : (Number.isFinite(_tpn) ? { targetType: \"page\", pageNumber: _tpn } : null);\n          const _PL = 6, _PT = 2;   // matches text padding 2px 6px (glyph box)\n          const _gw = Math.max(0, (n.offsetWidth || 132) - _PL * 2) || 120;\n          const _gh = Math.max(0, (n.offsetHeight || 34) - _PT * 2) || 26;\n          const _st = { id: n.id, x: _dmbLenPx(n, \"left\") + _PL, y: _dmbLenPx(n, \"top\") + _PT, w: _gw, h: _gh };\n          dmbCheckSnap(_st, parent, gH, gV, _tmeta, true);\n        }\n      } catch (e) {}\n      return;\n    }\n    const obj = sel.obj, el = sel.el;\n    if (sel.kind === \"shape\" && obj.shapeType === \"line\") {\n      _dmbLineMigrate(obj);\n      obj.x1 += dx; obj.y1 += dy; obj.x2 += dx; obj.y2 += dy;\n      if (obj.bx != null) { obj.bx += dx; obj.by += dy; }\n      try { _dmbLineRefreshEl(obj, el); } catch (e) {}\n      try { dmbOpenShapeEditBar(obj, el); } catch (e) {}\n      try { _dmbInvalidateSibCache(); } catch (e) {}\n      return;\n    }\n    obj.x = (obj.x || 0) + dx; obj.y = (obj.y || 0) + dy;\n    el.style.left = obj.x + \"px\"; el.style.top = obj.y + \"px\";\n    if (obj.coverZone) { try { dmbApplyZoneClip(el, obj); } catch (e) {} }\n    const _meta = sel.sfo ? { targetType: sel.sfo.targetType, pageNumber: sel.sfo.pageNumber, zone: sel.sfo.zone } : { targetType: obj.targetType, pageNumber: obj.page, zone: obj.zone };\n    if (sel.kind === \"map\")        { try { dmbOpenEditBar(obj, el); } catch (e) {} }\n    else if (sel.kind === \"shape\") { try { dmbOpenShapeEditBar(obj, el); } catch (e) {} }\n    else { try { showFrameToolbarFor(el, obj, _meta); } catch (e) {} }\n    try { _dmbInvalidateSibCache(); } catch (e) {}\n    // Show the same guides/labels as a mouse drag (render only, no snapping).\n    try {\n      const container = el.closest('[data-page-body=\"true\"], .dmb-cover-preview') || el.parentElement;\n      if (container) { const { gH, gV } = dmbGetSnapGuides(container); dmbCheckSnap(obj, container, gH, gV, { targetType: _meta.targetType, pageNumber: _meta.pageNumber }, true); }\n    } catch (e) {}\n  }\n  function _dmbClickBtn(id) { const b = document.getElementById(id); if (b) { b.click(); return true; } return false; }\n  // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n  // MULTI-SELECT, stage 1: ctrl or cmd click to add and remove, then move, nudge\n  // or delete the whole set together.\n  //\n  // Built ALONGSIDE the single-selection model, not in place of it. Every edit bar,\n  // resize frame and per-type drag handler in this file assumes exactly one selected\n  // thing, and rewriting that is where the risk lives. It is also not needed for a\n  // group move: a set of two or more simply takes precedence, intercepting the press\n  // in the CAPTURE phase before the per-type drag can start, and handing control back\n  // the moment the set drops below two. (2026-10-12)\n  const _DMB_SEL_SEL = \".dmb-editable-text, .dmb-map-object, .dmb-shape-object, \" +\n                       \".dmb-photo-frame-object, .dmb-sticker-object\";\n  let _dmbMulti = [];                                  // [{ el, isText, id }]\n\n  function _dmbMultiCount() {\n    _dmbMulti = _dmbMulti.filter((m) => m && m.el && m.el.isConnected);\n    return _dmbMulti.length;\n  }\n  function _dmbMultiIndexOf(el) {\n    for (let i = 0; i < _dmbMulti.length; i++) if (_dmbMulti[i].el === el) return i;\n    return -1;\n  }\n  function _dmbMultiPaint() {\n    try {\n      document.querySelectorAll(\".dmb-multi-sel\").forEach((n) => n.classList.remove(\"dmb-multi-sel\"));\n      _dmbMulti.forEach((m) => { if (m.el && m.el.isConnected) m.el.classList.add(\"dmb-multi-sel\"); });\n    } catch (e) {}\n  }\n  function _dmbMultiClear() { _dmbMulti = []; _dmbMultiPaint(); }\n\n  // The object record plus the array that owns it. Objects can live on any page or on\n  // the cover, and the DOM only carries the id, so this is the bridge back to state.\n  function _dmbFindObjRecord(id) {\n    if (!id) return null;\n    try {\n      if (Array.isArray(editor.coverObjects)) {\n        const c = editor.coverObjects.find((o) => o && o.id === id);\n        if (c) return { obj: c, list: editor.coverObjects };\n      }\n      const po = editor.pageObjects || {};\n      const keys = Object.keys(po);\n      for (let i = 0; i < keys.length; i++) {\n        const arr = po[keys[i]];\n        if (!Array.isArray(arr)) continue;\n        const o = arr.find((v) => v && v.id === id);\n        if (o) return { obj: o, list: arr };\n      }\n    } catch (e) {}\n    return null;\n  }\n\n  // Text keeps its position in CSS; everything else keeps it in the model.\n  function _dmbMultiPos(m) {\n    try {\n      if (m.isText) return { x: _dmbLenPx(m.el, \"left\"), y: _dmbLenPx(m.el, \"top\") };\n      const rec = _dmbFindObjRecord(m.id);\n      if (!rec) return null;\n      return { x: rec.obj.x || 0, y: rec.obj.y || 0 };\n    } catch (e) { return null; }\n  }\n\n  function _dmbMultiMoveTo(m, x, y) {\n    try {\n      if (m.isText) {\n        m.el.style.left = Math.round(x) + \"px\";\n        m.el.style.top  = Math.round(y) + \"px\";\n        return;\n      }\n      const rec = _dmbFindObjRecord(m.id);\n      if (!rec) return;\n      const o = rec.obj;\n      const ddx = x - (o.x || 0), ddy = y - (o.y || 0);\n      o.x = x; o.y = y;\n      // A line's box is DERIVED from its endpoints, so they have to travel too or the\n      // next recompute pulls it straight back to where it was.\n      if (o.type === \"shape\" && o.shapeType === \"line\") {\n        if (o.x1 != null) o.x1 += ddx;\n        if (o.x2 != null) o.x2 += ddx;\n        if (o.bx != null) o.bx += ddx;\n        if (o.y1 != null) o.y1 += ddy;\n        if (o.y2 != null) o.y2 += ddy;\n        if (o.by != null) o.by += ddy;\n      }\n      m.el.style.left = Math.round(x) + \"px\";\n      m.el.style.top  = Math.round(y) + \"px\";\n    } catch (e) {}\n  }\n\n  function _dmbMultiSave() {\n    try {\n      _dmbMulti.forEach((m) => {\n        if (!m.isText || !m.el || !m.el.isConnected) return;\n        saveCoverTextNodeState(m.el);\n        savePageTextState(m.el);\n      });\n    } catch (e) {}\n    try { saveCoverDesignToStorage(); } catch (e) {}\n    try { _dmbWriteProgressState(); } catch (e) {}\n    try { if (typeof _dmbCloudPersist === \"function\") _dmbCloudPersist(); } catch (e) {}\n  }\n\n  function _dmbMultiNudge(dx, dy) {\n    if (_dmbMultiCount() < 1) return false;\n    pushUndoState(\"Move Selection\");\n    _dmbMulti.forEach((m) => {\n      const p = _dmbMultiPos(m);\n      if (p) _dmbMultiMoveTo(m, p.x + dx, p.y + dy);\n    });\n    _dmbMultiSave();\n    return true;\n  }\n\n  function _dmbMultiDelete() {\n    if (_dmbMultiCount() < 1) return false;\n    pushUndoState(\"Delete Selection\");\n    const members = _dmbMulti.slice();\n    _dmbMultiClear();\n    members.forEach((m) => {\n      try {\n        if (m.isText) {\n          if (typeof _dmbRemoveTextNode === \"function\") _dmbRemoveTextNode(m.el);\n          else if (m.el && m.el.isConnected) m.el.remove();\n          return;\n        }\n        const rec = _dmbFindObjRecord(m.id);\n        if (rec) {\n          const i = rec.list.indexOf(rec.obj);\n          if (i >= 0) rec.list.splice(i, 1);\n        }\n        if (m.el && m.el.isConnected) m.el.remove();\n      } catch (e) {}\n    });\n    try { _dmbDeselectAll(); } catch (e) {}\n    try { if (builderMode === \"cover\") renderCoverCanvas(); else renderSpread(); } catch (e) {}\n    try { renderThumbsRail(); } catch (e) {}\n    _dmbMultiSave();\n    return true;\n  }\n\n  // ---- ctrl / cmd click toggles membership -------------------------------\n  document.addEventListener(\"pointerdown\", function (e) {\n    try {\n      if (!(e.ctrlKey || e.metaKey)) return;\n      const el = (e.target && e.target.closest) ? e.target.closest(_DMB_SEL_SEL) : null;\n      if (!el) return;\n      e.preventDefault(); e.stopPropagation();\n      const i = _dmbMultiIndexOf(el);\n      if (i >= 0) _dmbMulti.splice(i, 1);\n      else _dmbMulti.push({\n        el: el,\n        isText: el.classList.contains(\"dmb-editable-text\"),\n        id: (el.dataset && el.dataset.objectId) || el.id || \"\",\n      });\n      _dmbMultiPaint();\n    } catch (err) {}\n  }, true);\n\n  // ---- a plain press on a member drags the whole group --------------------\n  document.addEventListener(\"pointerdown\", function (e) {\n    try {\n      if (e.ctrlKey || e.metaKey) return;\n      if (_dmbMultiCount() < 2) return;\n      const el = (e.target && e.target.closest) ? e.target.closest(_DMB_SEL_SEL) : null;\n      if (!el || _dmbMultiIndexOf(el) < 0) {\n        // Pressing the CANVAS drops the group. Pressing a toolbar must not, or every\n        // attempt to act on the selection would destroy it first.\n        const onCanvas = e.target && e.target.closest &&\n          e.target.closest('[data-page-body=\"true\"], #coverOverlay, [data-spread-row]');\n        if (onCanvas) _dmbMultiClear();\n        return;\n      }\n      e.preventDefault(); e.stopPropagation();\n      const Z = (typeof _nodeOnScreenScale === \"function\" ? (_nodeOnScreenScale(el) || 1) : 1);\n      const sx = e.clientX, sy = e.clientY;\n      const start = _dmbMulti.map((m) => ({ m: m, p: _dmbMultiPos(m) })).filter((r) => r.p);\n      let moved = false;\n      const onMove = function (ev) {\n        const dx = (ev.clientX - sx) / Z, dy = (ev.clientY - sy) / Z;\n        // A 2px threshold so a plain click on a member does not count as a move and\n        // push a pointless undo step.\n        if (!moved && Math.abs(dx) < 2 && Math.abs(dy) < 2) return;\n        if (!moved) { moved = true; try { pushUndoState(\"Move Selection\"); } catch (er) {} }\n        start.forEach((r) => _dmbMultiMoveTo(r.m, r.p.x + dx, r.p.y + dy));\n      };\n      const onUp = function () {\n        document.removeEventListener(\"pointermove\", onMove, true);\n        document.removeEventListener(\"pointerup\", onUp, true);\n        document.removeEventListener(\"pointercancel\", onUp, true);\n        if (moved) _dmbMultiSave();\n      };\n      document.addEventListener(\"pointermove\", onMove, true);\n      document.addEventListener(\"pointerup\", onUp, true);\n      document.addEventListener(\"pointercancel\", onUp, true);\n    } catch (err) {}\n  }, true);\n\n  // ---- marquee: drag on empty canvas to select what it covers --------------\n  // The test is done entirely in CLIENT coordinates, comparing getBoundingClientRect\n  // of the marquee against each candidate. That is deliberate: objects live in\n  // per-page coordinates, the two pages of a spread sit at different offsets, the\n  // canvas has its own zoom, and rotated items have a box that does not match their\n  // stored x/y/w/h. Converting all of that would be four chances to be subtly wrong.\n  // Screen space is already the common space, and because zoom scales the marquee and\n  // the objects equally, an AREA RATIO measured there is exactly the ratio the\n  // customer sees. (2026-10-12)\n  const _DMB_MARQUEE_COVER = 0.9;        // how much of an item must be inside\n\n  // Things that sit INSIDE a page but are not the page: resize and rotate handles,\n  // the text selection frame, the spread photo layer, the little menus. Each starts\n  // a drag of its own, and several of them are SIBLINGS of the object they belong to\n  // rather than children, so the \"started on an item\" test above cannot see them.\n  // Without this, resizing a text box by a corner dot ALSO dragged a selection\n  // rectangle across the page: both gestures armed on the same press, and this one\n  // listens in the capture phase, so the handle's own stopPropagation came too late\n  // to prevent it. The [class*=\"handle\"] entry is deliberately broad, so a handle\n  // added later is covered without anyone having to remember this list. (2026-10-13)\n  const _DMB_NO_MARQUEE = '[class*=\"handle\"], [data-handle], [data-h], ' +\n    '.dmb-text-sel-frame, .dmb-text-sel-h, .dmb-map-marker, ' +\n    '.dmb-photo-menu, .dmb-photo-hover-menu, [data-spread-img-layer], ' +\n    '.dmb-insert-zone, .dmb-page-del';\n\n  // Drop any native text selection. Guarded because a selection inside an ACTIVE\n  // edit is the caret: clearing that would throw the customer out of their own\n  // typing position, so a box being edited is left alone.\n  function _dmbDropNativeSelection() {\n    try {\n      const sel = window.getSelection();\n      if (!sel || sel.isCollapsed || !sel.rangeCount) return;\n      const a = document.activeElement;\n      if (a && a.isContentEditable && a.classList &&\n          a.classList.contains(\"is-editing\")) return;\n      sel.removeAllRanges();\n    } catch (e) {}\n  }\n\n  function _dmbMarqueeSelect(r, add) {\n    try {\n      if (!add) _dmbMulti = [];\n      document.querySelectorAll(_DMB_SEL_SEL).forEach(function (el) {\n        if (!el || !el.isConnected) return;\n        const b = el.getBoundingClientRect();\n        const area = b.width * b.height;\n        if (area <= 0) return;                       // hidden or not laid out\n        const ix = Math.max(0, Math.min(r.right, b.right) - Math.max(r.left, b.left));\n        const iy = Math.max(0, Math.min(r.bottom, b.bottom) - Math.max(r.top, b.top));\n        if ((ix * iy) / area < _DMB_MARQUEE_COVER) return;\n        if (_dmbMultiIndexOf(el) >= 0) return;\n        _dmbMulti.push({\n          el: el,\n          isText: el.classList.contains(\"dmb-editable-text\"),\n          id: (el.dataset && el.dataset.objectId) || el.id || \"\",\n        });\n      });\n      _dmbMultiPaint();\n    } catch (e) {}\n  }\n\n  document.addEventListener(\"pointerdown\", function (e) {\n    try {\n      if (e.button !== 0) return;\n      const t = e.target;\n      if (!t || !t.closest) return;\n      if (t.closest(_DMB_SEL_SEL)) return;           // started on an item, not the canvas\n      if (t.closest(\"input, textarea, select, button, .dmb-ribbon-bar\")) return;\n      if (t.closest(_DMB_NO_MARQUEE)) return;        // started on something that drags itself\n      if (!t.closest('[data-page-body=\"true\"], #coverOverlay, [data-spread-row]')) return;\n\n      // Holding a modifier ADDS to the existing group instead of replacing it.\n      const add = !!(e.ctrlKey || e.metaKey || e.shiftKey);\n      const sx = e.clientX, sy = e.clientY;\n      let box = null, armed = false;\n\n      const onMove = function (ev) {\n        const dx = ev.clientX - sx, dy = ev.clientY - sy;\n        // Arm only once this is clearly a drag, so a plain click still falls through to\n        // the canvas deselect handler exactly as before.\n        if (!armed) {\n          if (Math.abs(dx) < 4 && Math.abs(dy) < 4) return;\n          armed = true;\n          // Dragging across a page also drags a native TEXT selection through every\n          // caption it crosses (the boxes are contenteditable). Suppressing it needs\n          // both halves: the class stops any NEW selection from extending, and the\n          // clear below drops whatever the browser already took between pointerdown\n          // and the 4px arm threshold.\n          document.body.classList.add(\"dmb-marquee-on\");\n          _dmbDropNativeSelection();\n          box = document.createElement(\"div\");\n          box.className = \"dmb-marquee\";\n          document.body.appendChild(box);\n        }\n        _dmbDropNativeSelection();\n        box.style.left   = Math.min(sx, ev.clientX) + \"px\";\n        box.style.top    = Math.min(sy, ev.clientY) + \"px\";\n        box.style.width  = Math.abs(dx) + \"px\";\n        box.style.height = Math.abs(dy) + \"px\";\n        ev.preventDefault();\n      };\n      const onUp = function () {\n        document.removeEventListener(\"pointermove\", onMove, true);\n        document.removeEventListener(\"pointerup\", onUp, true);\n        document.removeEventListener(\"pointercancel\", onUp, true);\n        document.body.classList.remove(\"dmb-marquee-on\");   // before the early return\n        if (!armed || !box) return;                  // it was a click, not a drag\n        _dmbDropNativeSelection();\n        const r = box.getBoundingClientRect();\n        box.remove();\n        _dmbMarqueeSelect(r, add);\n      };\n      document.addEventListener(\"pointermove\", onMove, true);\n      document.addEventListener(\"pointerup\", onUp, true);\n      document.addEventListener(\"pointercancel\", onUp, true);\n    } catch (err) {}\n  }, true);\n\n  // ---- group copy and duplicate -------------------------------------------\n  // Both go through the EXISTING per-type duplicators rather than a second\n  // implementation. Duplicating text is not a matter of cloning a node: it has to be\n  // registered in coverUserTexts or pageObjects, get its handlers attached and a fresh\n  // z assigned, and a parallel copy of that logic is exactly how two paths drift apart\n  // and one of them starts producing text the PDF cannot see.\n  //\n  // Those helpers each push their own undo state, so the batch suppresses\n  // pushUndoState for its duration and pushes exactly one step itself. Both the object\n  // and the text duplicators offset by the same +20, so a group keeps its internal\n  // layout precisely. (2026-10-12)\n  let _dmbMultiClip = null;\n\n  function _dmbMultiIdSet() {\n    const out = new Set();\n    try {\n      document.querySelectorAll(_DMB_SEL_SEL).forEach(function (el) {\n        out.add((el.dataset && el.dataset.objectId) || el.id || \"\");\n      });\n    } catch (e) {}\n    return out;\n  }\n\n  // Whatever appeared since `before` is the new copy, whatever type it is. Diffing ids\n  // avoids every duplicator having to report back what it made.\n  function _dmbMultiSelectNewSince(before) {\n    try {\n      _dmbMulti = [];\n      document.querySelectorAll(_DMB_SEL_SEL).forEach(function (el) {\n        const id = (el.dataset && el.dataset.objectId) || el.id || \"\";\n        if (!id || before.has(id)) return;\n        _dmbMulti.push({ el: el, isText: el.classList.contains(\"dmb-editable-text\"), id: id });\n      });\n      _dmbMultiPaint();\n    } catch (e) {}\n  }\n\n  function _dmbMultiDuplicate(label) {\n    if (_dmbMultiCount() < 1) return false;\n    const members = _dmbMulti.slice();\n    const before = _dmbMultiIdSet();\n    const _realPush = pushUndoState;\n    _realPush(label || \"Duplicate Selection\");\n    try {\n      pushUndoState = function () {};          // one step for the whole batch\n      members.forEach(function (m) {\n        try {\n          if (m.isText) {\n            if (!m.el || !m.el.isConnected) return;\n            selectedTextNode = m.el;           // the toolbar button acts on the selection\n            _dmbClickBtn(\"dmbDuplicateTextBtn\");\n            return;\n          }\n          const rec = _dmbFindObjRecord(m.id);\n          if (!rec || !rec.obj) return;\n          const o = rec.obj;\n          duplicateFrameObjectByTarget(o.targetType, o.page, o.zone, o.id);\n        } catch (e) {}\n      });\n    } finally {\n      pushUndoState = _realPush;\n    }\n    try { if (builderMode === \"cover\") renderCoverCanvas(); else renderSpread(); } catch (e) {}\n    try { renderThumbsRail(); } catch (e) {}\n    _dmbMultiSave();\n    // The copies become the selection, so the next drag moves the new ones and the\n    // originals stay where they are.\n    requestAnimationFrame(function () { _dmbMultiSelectNewSince(before); });\n    return true;\n  }\n\n  function _dmbMultiCopy() {\n    if (_dmbMultiCount() < 1) return false;\n    _dmbMultiClip = _dmbMulti.map(function (m) { return { id: m.id, isText: m.isText }; });\n    return true;\n  }\n\n  function _dmbMultiPaste() {\n    if (!_dmbMultiClip || !_dmbMultiClip.length) return false;\n    // Re-resolve against the live DOM: the copied items may have been moved, edited or\n    // deleted between the copy and the paste.\n    const live = [];\n    _dmbMultiClip.forEach(function (c) {\n      let el = null;\n      try {\n        el = c.isText ? document.getElementById(c.id)\n                      : document.querySelector('[data-object-id=\"' + c.id + '\"]');\n      } catch (e) {}\n      if (el && el.isConnected) live.push({ el: el, isText: c.isText, id: c.id });\n    });\n    if (!live.length) return false;\n    const keep = _dmbMulti;\n    _dmbMulti = live;\n    const ok = _dmbMultiDuplicate(\"Paste Selection\");\n    if (!ok) _dmbMulti = keep;\n    return ok;\n  }\n\n  // The group had a bounding box with corner handles here (proportional resize of\n  // the whole selection). REMOVED at the owner's request on 2026-10-13: every\n  // selected item already draws its own frame, so a second box around them read as\n  // clutter rather than a control. The selection itself is unchanged - it still\n  // moves, nudges, deletes, copies and duplicates as a group; only the extra box\n  // and the resize-by-handle it carried are gone.\n\n  function _dmbDoDelete(sel) {\n    if (sel.kind === \"text\")  return _dmbClickBtn(\"dmbDeleteTextBtn\");\n    if (sel.kind === \"map\")   return _dmbClickBtn(\"dmbEditDelete\");\n    if (sel.kind === \"shape\") return _dmbClickBtn(\"dmbShapeDelete\");\n    return _dmbClickBtn(\"dmbFrameDeleteBtn\");\n  }\n  function _dmbDoDuplicate(sel) {\n    if (sel.kind === \"text\")  return _dmbClickBtn(\"dmbDuplicateTextBtn\");\n    if (sel.kind === \"frame\") return _dmbClickBtn(\"dmbFrameDuplicateBtn\");\n    if (sel.kind === \"shape\") return _dmbClickBtn(\"dmbShapeDuplicateBtn\");\n    // map has no toolbar Dup button \u2192 duplicate generically\n    const obj = sel.obj;\n    pushUndoState(\"Duplicate\");\n    const dup = duplicateFrameObjectByTarget(obj.targetType, obj.page, obj.zone, obj.id);\n    if (builderMode === \"cover\") renderCoverCanvas(); else renderSpread();\n    if (dup) requestAnimationFrame(() => _dmbReselectObj(dup));\n    return true;\n  }\n  function _dmbDoCopy(sel) {\n    if (sel.kind === \"text\") { _dmbClipboard = { kind: \"text\", node: sel.node }; return; }\n    _dmbClipboard = { kind: \"obj\", data: cloneState(sel.obj) };\n  }\n  function _dmbDoPaste() {\n    if (!_dmbClipboard) return;\n    if (_dmbClipboard.kind === \"text\") { if (_dmbClipboard.node && _dmbClipboard.node.isConnected) { selectedTextNode = _dmbClipboard.node; _dmbClickBtn(\"dmbDuplicateTextBtn\"); } return; }\n    const d = _dmbClipboard.data; if (!d) return;\n    pushUndoState(\"Paste\");\n    const copy = { ...cloneState(d), id: uid(), x: (d.x || 0) + 14, y: (d.y || 0) + 14, z: Date.now() };\n    const list = getObjectListForTarget(d.targetType, d.page, d.zone);\n    if (!list) return;\n    list.push(copy);\n    if (builderMode === \"cover\") renderCoverCanvas(); else renderSpread();\n    requestAnimationFrame(() => _dmbReselectObj(copy));\n  }\n  function _dmbDeselectAll() {\n    try { hideFrameToolbar(); } catch (e) {}\n    try { hideTextToolbar(); } catch (e) {}\n    try { dmbCloseEditBar(); } catch (e) {}\n    try { dmbCloseShapeEditBar(); } catch (e) {}\n  }\n  // True when a builder modal/overlay is open \u2014 the canvas shortcuts must NOT fire then\n  // (e.g. the crop or spread-preview overlays own the arrow keys; a dialog owns Enter/Esc).\n  function _dmbModalOpen() {\n    const ids = [\"dmbDupPageModal\", \"dmbCropOverlay\", \"dmbPreviewOverlay\", \"dmbTutorialModal\", \"dmbMapModal\",\n      \"dmbStickerModal\", \"dmbMagIconModal\", \"dmbCustomStickerModal\", \"dmbCoverTemplateModal\", \"dmbCardModal\",\n      \"dmbMobileQualityModal\", \"dmbResumeModal\", \"dmbPhoneUploadModal\", \"dmbPdfOverlay\", \"dmbPublishTplModal\"];\n    for (let i = 0; i < ids.length; i++) {\n      const el = document.getElementById(ids[i]);\n      if (el && getComputedStyle(el).display !== \"none\") return true;\n    }\n    // Fallback: any full-screen high-z fixed overlay dropped straight on <body> (e.g. the\n    // generic choice dialog), but never the builder itself.\n    const kids = document.body.children;\n    for (let i = 0; i < kids.length; i++) {\n      const el = kids[i];\n      if (!el || el.nodeType !== 1) continue;\n      if (el.querySelector && el.querySelector('[data-page-body], #dmb-root, .dmb-builder-leftpanel')) continue;\n      const cs = getComputedStyle(el);\n      if (cs.position !== \"fixed\" || cs.display === \"none\" || cs.visibility === \"hidden\") continue;\n      if ((parseInt(cs.zIndex) || 0) < 9999) continue;\n      const r = el.getBoundingClientRect();\n      if (r.width > window.innerWidth * 0.6 && r.height > window.innerHeight * 0.6) return true;\n    }\n    return false;\n  }\n  document.addEventListener(\"keydown\", (e) => {\n    if (_dmbModalOpen()) return;   // a dialog/overlay is up \u2014 leave the keys to it\n    const ae = document.activeElement;\n    const editing = ae && (ae.isContentEditable || ae.tagName === \"INPUT\" || ae.tagName === \"TEXTAREA\");\n    const mod = e.ctrlKey || e.metaKey;\n\n    // Undo / redo \u2014 global (no selection needed). Skipped while typing so the\n    // browser's own text-undo still works inside inputs / text boxes.\n    if (mod && !editing && (e.key === \"z\" || e.key === \"Z\")) { e.preventDefault(); if (e.shiftKey) redoLastAction(); else undoLastAction(); return; }\n    if (mod && !editing && (e.key === \"y\" || e.key === \"Y\")) { e.preventDefault(); redoLastAction(); return; }\n\n    if (editing) return;   // never steal keys while typing\n\n    const sel = _dmbActiveSel();\n\n    // A group of two or more answers these keys first; the single selection may not\n    // even exist while a group is held.\n    if (mod && (e.key === \"v\" || e.key === \"V\") && _dmbMultiClip && _dmbMultiClip.length) {\n      e.preventDefault(); _dmbMultiPaste(); return;\n    }\n    if (_dmbMultiCount() >= 1) {\n      if (mod && (e.key === \"d\" || e.key === \"D\")) { e.preventDefault(); _dmbMultiDuplicate(); return; }\n      if (mod && (e.key === \"c\" || e.key === \"C\")) { e.preventDefault(); _dmbMultiCopy(); return; }\n      if (e.key === \"Delete\" || e.key === \"Backspace\") { e.preventDefault(); _dmbMultiDelete(); return; }\n      if (e.key === \"Escape\") { e.preventDefault(); _dmbMultiClear(); return; }\n      const _ms = e.shiftKey ? 10 : 1;\n      let _mx = 0, _my = 0;\n      if (e.key === \"ArrowLeft\") _mx = -_ms;\n      else if (e.key === \"ArrowRight\") _mx = _ms;\n      else if (e.key === \"ArrowUp\") _my = -_ms;\n      else if (e.key === \"ArrowDown\") _my = _ms;\n      if (_mx || _my) { e.preventDefault(); _dmbMultiNudge(_mx, _my); return; }\n      // Anything else falls through to the normal single-selection handling below.\n    }\n\n    if (mod && (e.key === \"d\" || e.key === \"D\")) { if (sel) { e.preventDefault(); _dmbDoDuplicate(sel); } return; }\n    if (mod && (e.key === \"c\" || e.key === \"C\")) { if (sel) { e.preventDefault(); _dmbDoCopy(sel); } return; }\n    if (mod && (e.key === \"v\" || e.key === \"V\")) { if (_dmbClipboard) { e.preventDefault(); _dmbDoPaste(); } return; }\n\n    if (!sel) return;\n\n    if (e.key === \"Delete\" || e.key === \"Backspace\") { e.preventDefault(); _dmbDoDelete(sel); return; }\n    if (e.key === \"Escape\") { e.preventDefault(); _dmbDeselectAll(); return; }\n    // Enter / F2 on a SELECTED text box \u2192 jump into writing mode (caret editing).\n    if (sel.kind === \"text\" && (e.key === \"Enter\" || e.key === \"F2\")) { e.preventDefault(); enterTextEditMode(sel.node); return; }\n\n    const step = e.shiftKey ? 10 : 1;\n    let dx = 0, dy = 0;\n    if (e.key === \"ArrowLeft\") dx = -step;\n    else if (e.key === \"ArrowRight\") dx = step;\n    else if (e.key === \"ArrowUp\") dy = -step;\n    else if (e.key === \"ArrowDown\") dy = step;\n    else return;\n    e.preventDefault();\n    _dmbNudge(sel, dx, dy);\n  });\n\n  // Icon colour: the colour-wheel button opens a palette popup (recent swatches +\n  // custom picker), matching the text toolbar.\n  const _fColorBtn = document.getElementById(\"dmbFrameIconColorBtn\");\n  const _fColorPop = document.getElementById(\"dmbFrameColorPop\");\n  const _fColorCustom = document.getElementById(\"dmbFrameColorCustom\");\n  const _fColorInput = document.getElementById(\"dmbFrameIconColorInput\");\n  if (_fColorBtn && _fColorPop) {\n    _fColorBtn.addEventListener(\"click\", (e) => {\n      e.preventDefault(); e.stopPropagation();\n      const willOpen = _fColorPop.hidden;\n      _fColorPop.hidden = !willOpen;\n      const mm = document.getElementById(\"dmbFrameMoreMenu\"); if (mm && willOpen) mm.hidden = true;\n    });\n    _fColorPop.addEventListener(\"click\", (e) => {\n      if (e.target.closest(\".dmb-recent-swatch\")) _fColorPop.hidden = true;\n    });\n  }\n  if (_fColorCustom && _fColorInput) {\n    _fColorCustom.addEventListener(\"click\", (e) => {\n      e.stopPropagation();                          // identical to the background wheel \u2014 NO preventDefault\n      if (e.target === _fColorInput) return;        // overlay input tapped \u2192 let it open the native picker\n      try { _fColorInput.click(); } catch (e2) {}   // desktop fallback: opens the OS colour picker\n    });\n  }\n  if (_fColorInput && _fColorPop) {\n    try { dmbEnhanceColorInput(_fColorInput, _fColorPop); } catch (e) {}\n    try { if (_fColorCustom) _fColorPop.appendChild(_fColorCustom); } catch (e) {}\n    try { _dmbColorTapFix(_fColorCustom, _fColorInput); } catch (e) {}\n  }\n\n  document.getElementById(\"dmbFrameIconColorInput\")?.addEventListener(\"input\", (e) => {\n    if (!selectedFrameObject) return;\n    const newColor = e.target.value;\n    try { _dmbSyncFrameIconDot(); } catch (e2) {}\n    const obj = dmbFindObjectById(selectedFrameObject.id, selectedFrameObject.targetType);\n    if (!obj) return;\n    obj.color = newColor;\n    _dmbIconColorCache[obj.iconId + \"|\" + newColor] = null;\n    dmbGetColoredIconDataUrl(obj.iconId, newColor).then((dataUrl) => {\n      const imgEl = document.querySelector(`[data-object-id=\"${obj.id}\"] img`);\n      if (imgEl) imgEl.src = dataUrl;\n    });\n    pushUndoState(\"Recolor Icon\");\n  });\n  document.getElementById(\"dmbFrameIconColorInput\")?.addEventListener(\"change\", () => {\n    const p = document.getElementById(\"dmbFrameColorPop\"); if (p) p.hidden = true;\n  });\n\n  // \"More\" dropdown (align-on-page + duplicate).\n  const _fMoreToggle = document.getElementById(\"dmbFrameMoreToggle\");\n  const _fMoreMenu = document.getElementById(\"dmbFrameMoreMenu\");\n  if (_fMoreToggle && _fMoreMenu) {\n    _fMoreToggle.addEventListener(\"click\", (e) => {\n      e.preventDefault(); e.stopPropagation();\n      const willOpen = _fMoreMenu.hidden;\n      _fMoreMenu.hidden = !willOpen;\n      const cp = document.getElementById(\"dmbFrameColorPop\"); if (cp && willOpen) cp.hidden = true;\n    });\n    _fMoreMenu.addEventListener(\"click\", (e) => {\n      if (e.target.closest(\".dmb-text-more-item\")) _fMoreMenu.hidden = true;\n    });\n  }\n  // Outside-click closes the frame-toolbar popups (bound once with the toolbar).\n  document.addEventListener(\"click\", (e) => {\n    if (!e.target.closest(\"#dmbFrameColorWrap\")) { const p = document.getElementById(\"dmbFrameColorPop\"); if (p) p.hidden = true; }\n    if (!e.target.closest(\"#dmbFrameMoreDD\")) { const p = document.getElementById(\"dmbFrameMoreMenu\"); if (p) p.hidden = true; }\n  });\n\n  document.getElementById(\"dmbFrameIconFitBtn\")?.addEventListener(\"click\", () => {\n    if (!selectedFrameObject) return;\n    const obj = dmbFindObjectById(selectedFrameObject.id, selectedFrameObject.targetType);\n    if (!obj || obj.type !== \"mag-icon\") return;\n    const el = document.querySelector(`[data-object-id=\"${obj.id}\"]`);\n    const container = el?.parentElement;\n    if (!el || !container) return;\n    const cW = container.offsetWidth || 270;\n    const cH = container.offsetHeight || 382;\n    const PAD = 5;\n    const aspect = (obj.naturalW && obj.naturalH) ? obj.naturalW / obj.naturalH : obj.w / (obj.h || 1);\n    let nw, nh, nx, ny;\n    if (aspect >= 1) {\n      nw = cW - PAD * 2; nh = Math.round(nw / aspect); nx = PAD; ny = Math.round((cH - nh) / 2);\n    } else {\n      nh = cH - PAD * 2; nw = Math.round(nh * aspect); ny = PAD; nx = Math.round((cW - nw) / 2);\n    }\n    pushUndoState(\"Fit Icon to Page\");\n    obj.x = nx; obj.y = ny; obj.w = nw; obj.h = nh;\n    if (builderMode === \"cover\") renderCoverCanvas(); else renderSpread();\n    requestAnimationFrame(() => {\n      const newEl = document.querySelector(`[data-object-id=\"${obj.id}\"]`);\n      if (newEl) showFrameToolbarFor(newEl, obj, { targetType: selectedFrameObject.targetType, pageNumber: selectedFrameObject.pageNumber, zone: selectedFrameObject.zone });\n    });\n  });\n\n  // \u2500\u2500 Contain-to-page button (frame toolbar) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  document.getElementById(\"dmbFrameContainBtn\")?.addEventListener(\"click\", () => {\n    if (!selectedFrameObject) return;\n    const _cObj = dmbFindObjectById(selectedFrameObject.id, selectedFrameObject.targetType);\n    const _cEl  = document.querySelector(`[data-object-id=\"${selectedFrameObject.id}\"]`);\n    if (!_cObj || !_cEl) return;\n    const _cIsCover = selectedFrameObject.targetType === \"cover\";\n    _dmbContainCycle(_cObj, _cEl, _cIsCover);\n  });\n\n  }\n\n  function getTextToolbar() {\n    return document.getElementById(\"dmbTextToolbar\");\n  }\n\n  function getDmbRoot() {\n    return document.getElementById(\"dmb-root\");\n  }\n\n  function rgbToHex(rgb) {\n    const m = String(rgb || \"\").match(/\\d+/g);\n    if (!m || m.length < 3) return \"#000000\";\n    return \"#\" + m.slice(0, 3).map((x) => Number(x).toString(16).padStart(2, \"0\")).join(\"\");\n  }\n\n  var _rotDegTip = null;\n  function showRotDegTooltip(deg, clientX, clientY) {\n    if (!_rotDegTip) {\n      _rotDegTip = document.createElement(\"div\");\n      _rotDegTip.style.cssText = \"position:fixed;z-index:99999;background:rgba(0,0,0,0.78);color:#fff;font-size:12px;font-weight:700;padding:3px 10px;border-radius:20px;pointer-events:none;white-space:nowrap;font-family:sans-serif;letter-spacing:0.3px;\";\n      document.body.appendChild(_rotDegTip);\n    }\n    _rotDegTip.textContent = Math.round(((deg % 360) + 360) % 360) + \"\u00B0\";\n    _rotDegTip.style.left = (clientX + 18) + \"px\";\n    _rotDegTip.style.top = (clientY - 12) + \"px\";\n    _rotDegTip.style.display = \"block\";\n  }\n  function hideRotDegTooltip() {\n    if (_rotDegTip) _rotDegTip.style.display = \"none\";\n  }\n\n  function ensureRotateHandle() {\n    if (dmbRotateHandle && document.body.contains(dmbRotateHandle)) return dmbRotateHandle;\n\n    const root = getDmbRoot();\n    if (!root) return null;\n\n    const handle = document.createElement(\"div\");\n    handle.id = \"dmbTextRotateHandle\";\n    handle.className = \"dmb-text-rotate-handle\";\n    handle.innerHTML = `\n      <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\">\n        <path d=\"M12 4a8 8 0 1 0 7.2 4.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n        <path d=\"M19.5 4.5v5h-5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n      </svg>\n    `;\n\n    root.appendChild(handle);\n    dmbRotateHandle = handle;\n\n    let rotating = false;\n    let centerX = 0;\n    let centerY = 0;\n    let startAngle = 0;\n    let startRotate = 0;\n\n    const onMove = (e) => {\n      if (!rotating || !selectedTextNode) return;\n\n      const angle = Math.atan2(e.clientY - centerY, e.clientX - centerX) * 180 / Math.PI;\n      const raw  = startRotate + (angle - startAngle);\n      const next = _dmbSnapAngle(raw); // Fix 3: 45\u00B0 magnetic snap\n\n      selectedTextNode.dataset.rotate = String(next);\n      selectedTextNode.style.transform = `rotate(${next}deg)`;\n      saveCoverTextNodeState(selectedTextNode);\n      showTextToolbarFor(selectedTextNode);\n      showRotDegTooltip(next, e.clientX, e.clientY);\n    };\n\n    const onUp = () => {\n      rotating = false;\n      hideRotDegTooltip();\n      document.removeEventListener(\"pointermove\", onMove);\n      document.removeEventListener(\"pointerup\", onUp);\n    };\n\n    handle.addEventListener(\"pointerdown\", (e) => {\n      if (!selectedTextNode) return;\n\n      e.preventDefault();\n      e.stopPropagation();\n\n      const rect = selectedTextNode.getBoundingClientRect();\n      centerX = rect.left + rect.width / 2;\n      centerY = rect.top + rect.height / 2;\n\n      startAngle = Math.atan2(e.clientY - centerY, e.clientX - centerX) * 180 / Math.PI;\n      startRotate = Number(selectedTextNode.dataset.rotate || \"0\");\n\n      rotating = true;\n      document.addEventListener(\"pointermove\", onMove);\n      document.addEventListener(\"pointerup\", onUp);\n    });\n\n    return handle;\n  }\n\n  function positionRotateHandle(node) {\n    const handle = ensureRotateHandle();\n    const root = getDmbRoot();\n    if (!handle || !root || !node) return;\n\n    const nodeRect = node.getBoundingClientRect();\n    const rootRect = root.getBoundingClientRect();\n\n    const left = nodeRect.left - rootRect.left + (nodeRect.width / 2) - 13;\n    const top = nodeRect.bottom - rootRect.top + 10;\n\n    handle.style.left = `${left}px`;\n    handle.style.top = `${top}px`;\n    handle.classList.add(\"is-visible\");\n  }\n\n  function hideRotateHandle() {\n    const handle = ensureRotateHandle();\n    if (handle) handle.classList.remove(\"is-visible\");\n  }\n\n  // \u2500\u2500 Text selection frame: 4 corner circles (font size) + 2 side handles (width) \u2500\u2500\n  // Overlays the selected text exactly (same left/top/size/rotation) as a sibling,\n  // so the handles sit on the rotated box corners. Body has pointer-events:none so\n  // the text stays clickable; only the handles capture pointer events.\n  // z === 0 is a REAL layer (\"send fully to back\", set by _dmbUnifiedLayerStep),\n  // not a missing value. `obj.z || fallback` silently promoted it back to the top\n  // on screen while the PDF kept drawing it behind the photos.\n  function _dmbZOf(obj, fallback) {\n    var z = obj && obj.z;\n    return (z === 0 || z === \"0\" || (z != null && z !== \"\")) ? Number(z) || 0 : fallback;\n  }\n\n  // \u2500\u2500 Top of the stack for a NEW item \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // Anything the customer adds must land ABOVE everything already on that page -\n  // they just chose to put it there, so it should not appear buried. Scans the\n  // real z values on the target (pages: editor.pageObjects[pn]; cover: the free\n  // coverObjects plus coverState.textStyles, which stores cover text separately)\n  // and returns one more than the highest. Floored at Date.now() so it stays\n  // consistent with the timestamp-based z values every other insert has always\n  // used, and so items added later still beat items added earlier.\n  function _dmbNextTopZ(pageNumber) {\n    var max = 0;\n    var bump = function (v) {\n      var x = parseFloat(v);\n      if (isFinite(x) && x > max) max = x;\n    };\n    try {\n      if (pageNumber === \"cover\") {\n        (editor.coverObjects || []).forEach(function (o) { if (o) bump(o.z); });\n        var ts = (typeof coverState !== \"undefined\" && coverState) ? coverState.textStyles : null;\n        if (ts) Object.keys(ts).forEach(function (k) { if (ts[k]) bump(ts[k].z); });\n      } else {\n        var objs = (editor.pageObjects &&\n          (editor.pageObjects[pageNumber] || editor.pageObjects[String(pageNumber)])) || [];\n        if (Array.isArray(objs)) objs.forEach(function (o) { if (o) bump(o.z); });\n      }\n    } catch (e) {}\n    return Math.max(max + 1, Date.now());\n  }\n\n  // \u2500\u2500 Sticker crop that survives asset re-uploads \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // A sticker's cropX/Y/W/H is the tight bounding box of its non-transparent pixels,\n  // measured in the pixel space of the image analysed at INSERT time. Shopify Files\n  // can later serve the very same url at a different size (re-upload / CDN\n  // transform), and older objects never recorded which size they were measured on\n  // (cropRefW/H) - so the stored rect can point at the wrong region of the image the\n  // renderer actually loaded; drawImage clamps it and stretches what is left (the\n  // squashed, smeared cross on the Baptism template: rect measured on a ~1000px\n  // copy, image now 3691px). Rule: use the stored rect ONLY when it was measured on\n  // an image of exactly this size; otherwise re-derive the tight box from THIS\n  // image's alpha (same rule as insert, on a <=1024px scan so a 3700px PNG is never\n  // read at full size) and cache it per url+size.\n  const _dmbStickerCropCache = new Map();\n  function _dmbStickerCropFor(obj, img) {\n    const nw = (img && (img.naturalWidth || img.width)) || 0, nh = (img && (img.naturalHeight || img.height)) || 0;\n    if (!nw || !nh) return null;\n    if (obj && obj.cropW > 0 && obj.cropH > 0 && obj.cropRefW === nw && obj.cropRefH === nh) {\n      return { cropX: obj.cropX || 0, cropY: obj.cropY || 0, cropW: obj.cropW, cropH: obj.cropH };\n    }\n    const key = String((img.currentSrc || img.src || (obj && obj.url) || \"\")).slice(0, 300) + \"|\" + nw + \"x\" + nh;\n    if (_dmbStickerCropCache.has(key)) return _dmbStickerCropCache.get(key);\n    let out = null;\n    try {\n      const sc = Math.min(1, 1024 / Math.max(nw, nh));\n      const sw = Math.max(1, Math.round(nw * sc)), sh = Math.max(1, Math.round(nh * sc));\n      const c = document.createElement(\"canvas\"); c.width = sw; c.height = sh;\n      const x = c.getContext(\"2d\", { willReadFrequently: true });\n      x.drawImage(img, 0, 0, sw, sh);\n      const px = x.getImageData(0, 0, sw, sh).data;\n      let minX = sw, minY = sh, maxX = -1, maxY = -1;\n      for (let yy = 0; yy < sh; yy++) {\n        const row = yy * sw * 4;\n        for (let xx = 0; xx < sw; xx++) {\n          if (px[row + xx * 4 + 3] > 10) {\n            if (xx < minX) minX = xx; if (xx > maxX) maxX = xx;\n            if (yy < minY) minY = yy; if (yy > maxY) maxY = yy;\n          }\n        }\n      }\n      c.width = 0; c.height = 0;\n      if (maxX >= minX && maxY >= minY) {\n        // Back to full-resolution pixels, padded by one scan pixel so an edge is never shaved.\n        const inv = 1 / sc;\n        const X0 = Math.max(0, Math.floor((minX - 1) * inv)), Y0 = Math.max(0, Math.floor((minY - 1) * inv));\n        const X1 = Math.min(nw, Math.ceil((maxX + 2) * inv)), Y1 = Math.min(nh, Math.ceil((maxY + 2) * inv));\n        out = { cropX: X0, cropY: Y0, cropW: Math.max(1, X1 - X0), cropH: Math.max(1, Y1 - Y0) };\n      } else {\n        out = { cropX: 0, cropY: 0, cropW: nw, cropH: nh };   // fully transparent: draw as-is\n      }\n    } catch (e) {\n      // Tainted canvas: the pixels cannot be read, so the tight box cannot be\n      // measured. Fall back to the stored rect when it at least fits this image (it\n      // is correct whenever the asset has not been re-uploaded), but mark the result\n      // a GUESS and return WITHOUT caching, so a later CORS-clean load of the same\n      // image replaces it with a real measurement.\n      return (obj && obj.cropW > 0 && obj.cropH > 0 && (obj.cropX || 0) + obj.cropW <= nw && (obj.cropY || 0) + obj.cropH <= nh)\n        ? { cropX: obj.cropX || 0, cropY: obj.cropY || 0, cropW: obj.cropW, cropH: obj.cropH, guessed: true }\n        : { cropX: 0, cropY: 0, cropW: nw, cropH: nh, guessed: true };\n    }\n    _dmbStickerCropCache.set(key, out);\n    return out;\n  }\n\n  function _nodeOnScreenScale(node) {\n    const op = node && node.offsetParent;\n    if (!op) return 1;\n    const r = op.getBoundingClientRect();\n    return (r.width / (op.offsetWidth || r.width)) || 1;\n  }\n\n  // Re-centre a spine cover text on the spine (glyph centre \u2192 spine centre x).\n  // Used after resizing so the spine text never drifts off-centre (esp. in the PDF\n  // preview). No-op for non-spine text, magazines, or detached nodes.\n  function _dmbRecenterSpineText(node) {\n    try {\n      if (!node || !node.isConnected || window.DMB_MAGAZINE_MODE) return;\n      const _ov = document.getElementById(\"coverOverlay\");\n      if (!_ov) return;\n      const _ut = (editor.coverUserTexts || []).find((en) => en.id === node.id);\n      const _ovr = _ov.getBoundingClientRect();\n      const _Z = (_ovr.width / (_ov.offsetWidth || 622)) || 1;\n      const r = node.getBoundingClientRect();\n      if (r.width === 0 && r.height === 0) return;\n      const nvx = ((r.left + r.width / 2) - _ovr.left) / _Z;\n      const onSpine = (_ut && _ut.zone === \"spine\") ||\n        (nvx >= COVER_BACK_W && nvx <= COVER_BACK_W + COVER_SPINE_W);\n      if (!onSpine) return;\n      // Glyph (letter) centre, accounting for the rotation in the transform string.\n      let gcx = nvx;\n      const _tf = node.style.transform;\n      node.style.transform = \"none\";\n      const _ur = node.getBoundingClientRect();\n      const _rng = document.createRange(); _rng.selectNodeContents(node);\n      const _gr = _rng.getBoundingClientRect();\n      node.style.transform = _tf;\n      if (_gr && _gr.width > 0 && _gr.height > 0) {\n        const _gdx = ((_gr.left + _gr.width  / 2) - (_ur.left + _ur.width  / 2)) / _Z;\n        const _gdy = ((_gr.top  + _gr.height / 2) - (_ur.top  + _ur.height / 2)) / _Z;\n        let _eff = parseFloat(node.dataset.rotate || \"0\") || 0;\n        if (!_eff) { const _m = (_tf || \"\").match(/rotate\\(\\s*(-?[\\d.]+)deg/); if (_m) _eff = parseFloat(_m[1]) || 0; }\n        const _rad = _eff * Math.PI / 180;\n        gcx = nvx + (_gdx * Math.cos(_rad) - _gdy * Math.sin(_rad));\n      }\n      const _spineCx = COVER_BACK_W + COVER_SPINE_W / 2;\n      const _dx = _spineCx - gcx;\n      if (Math.abs(_dx) > 0.5) {\n        node.style.left = ((parseFloat(node.style.left) || 0) + _dx) + \"px\";\n        saveCoverTextNodeState(node);\n      }\n    } catch (e) {}\n  }\n\n  // \u2500\u2500 Alignment lines while a text box is being RESIZED \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // Straight into _dmbResizeSnap, the same function every photo frame, map, shape\n  // and sticker already uses for its own resize. So a text box lines up against the\n  // same references, draws the same blue lines (solid for an edge, dashed for a\n  // centre), the same pink equal-spacing bars and the same live size chip - rather\n  // than a second implementation that would drift away from all of it.\n  //\n  // `mode` is the handle: \"ml\"/\"mr\" for the side grips, \"tl\"/\"tr\"/\"bl\"/\"br\" for the\n  // corners. Text is measured by its GLYPH box (inset by the 2px 6px padding), the\n  // same box the move drag snaps, so text lines up letter to letter.\n  //\n  // `apply` asks for the width correction back. The SIDE grips take it: the width is\n  // theirs to set, so the edge lands exactly on what it lines up with. The CORNER\n  // dots do not. They drive the font size, which is a whole number of pixels, and one\n  // step of it moves the edge of a line of text by several px - so there is no size\n  // that lands the corner exactly, and pulling at the width behind the drag would\n  // only fight it. They show the lines and leave the size to the finger. (2026-10-13)\n  // Effective rotation of a text node, from wherever it is kept.\n  function _dmbNodeRotation(node) {\n    let r = parseFloat(node && node.dataset ? node.dataset.rotate : 0) || 0;\n    if (!r) {\n      const m = String((node && node.style && node.style.transform) || \"\").match(/rotate\\(\\s*(-?[\\d.]+)deg/);\n      if (m) r = parseFloat(m[1]) || 0;\n    }\n    return r;\n  }\n\n  function _dmbTextResizeGuides(node, mode, apply) {\n    try {\n      const parent = node && node.parentElement;\n      if (!parent || typeof _dmbResizeSnap !== \"function\") return 0;\n      const TX_PL = 6, TX_PT = 2;                 // matches the CSS padding: 2px 6px\n      const isCover = !!parent.closest(\".dmb-cover-preview\");\n      const pn = parseInt(node.dataset.pageNumber);\n      const meta = isCover ? { targetType: \"cover\" }\n                 : (Number.isFinite(pn) ? { targetType: \"page\", pageNumber: pn } : null);\n      if (!meta) return 0;\n      const left = _dmbLenPx(node, \"left\"), top = _dmbLenPx(node, \"top\");\n      const w = node.offsetWidth || 0, h = node.offsetHeight || 0;\n      if (!w || !h) return 0;\n      const obj = {\n        id: node.id,\n        x: left + TX_PL, y: top + TX_PT,\n        w: Math.max(0, w - TX_PL * 2), h: Math.max(0, h - TX_PT * 2),\n        // The rotation can live in EITHER place: dataset.rotate for an ordinary box,\n        // or the transform string for spine text (translate(-50%,-50%) rotate(-90deg)\n        // with dataset.rotate still \"0\"). Read both, so _dmbResizeSnap applies its own\n        // rotated-item guard and a spine title never gets lines drawn against an\n        // upright box it is not.\n        rotation: _dmbNodeRotation(node),\n      };\n      const w0 = obj.w;\n      _dmbResizeSnap(obj, parent, meta, mode);     // mutates obj, draws the guides\n      return apply ? (obj.w - w0) : 0;             // the width correction, in model px\n    } catch (e) { return 0; }\n  }\n\n  // Touch drags fire pointermove faster than the screen refreshes, and each one\n  // forces a layout. Keep only the newest event and run it once per frame; flush()\n  // applies a pending move so the drop lands exactly where the finger ended.\n  function _dmbRafMove(fn) {\n    let raf = 0, pending = null;\n    const run = function () { raf = 0; const ev = pending; pending = null; if (ev) fn(ev); };\n    const wrapped = function (ev) { pending = ev; if (!raf) raf = requestAnimationFrame(run); };\n    wrapped.flush = function () { if (raf) { cancelAnimationFrame(raf); raf = 0; } run(); };\n    return wrapped;\n  }\n\n  function ensureTextSelectFrame() {\n    let frame = document.getElementById(\"dmbTextSelFrame\");\n    if (frame) return frame;\n    frame = document.createElement(\"div\");\n    frame.id = \"dmbTextSelFrame\";\n    frame.className = \"dmb-text-sel-frame\";\n    // Inline styles so it works even if the CSS file is cached/stale.\n    // z-index must clear EVERY item layer, not just the default ones. On the cover,\n    // photo-frames are boosted to 100001 (one above the cover drag overlay at\n    // 100000), so a frame pinned at 100000 slid BEHIND a cover item \u2014 the text\n    // glyphs (z = Date.now()) showed on top but the selection box/handles hid\n    // behind the item. Max-int keeps the frame above all items on every surface.\n    // The frame body is pointer-events:none, so this never blocks taps on text.\n    frame.style.cssText = \"position:absolute;border:1.5px solid #7c8aff;box-sizing:border-box;\" +\n      \"pointer-events:none;z-index:2147483647;display:none;\";\n    // Handles look EXACTLY as before (same dots/pills, same positions). The ONLY\n    // change is invisible: each visible mark is wrapped in a larger TRANSPARENT hit\n    // pad (44px touch / 28px desktop) so a fingertip lands on it reliably. The dot/pill\n    // is a pointer-events:none child centred in the pad, so nothing looks different.\n    const _coarse = window.matchMedia && window.matchMedia(\"(pointer: coarse)\").matches;\n    const HIT  = _coarse ? 44 : 28;                      // invisible finger pad\n    const _cSz = _coarse ? 12 : 10;                      // visible corner dot \u2014 small & subtle\n    const _sW  = _coarse ? 8 : 6, _sH = _coarse ? 20 : 16;   // visible side pill \u2014 small & subtle\n    [[\"tl\", 0, 0], [\"tr\", 100, 0], [\"bl\", 0, 100], [\"br\", 100, 100]].forEach(function (c) {\n      const pad = document.createElement(\"div\");\n      pad.className = \"dmb-text-sel-h dmb-text-sel-corner\";\n      pad.dataset.h = c[0];\n      pad.style.cssText = \"position:absolute;background:transparent;border:0;pointer-events:auto;z-index:3;touch-action:none;cursor:nwse-resize;\" +\n        \"transform:translate(-50%,-50%);width:\" + HIT + \"px;height:\" + HIT + \"px;left:\" + c[1] + \"%;top:\" + c[2] + \"%;\";\n      const dot = document.createElement(\"div\");\n      dot.style.cssText = \"position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);pointer-events:none;\" +\n        \"background:#fff;border:2px solid #7c8aff;border-radius:999px;width:\" + _cSz + \"px;height:\" + _cSz + \"px;\";\n      pad.appendChild(dot);\n      frame.appendChild(pad);\n    });\n    [[\"ml\", 0, 50], [\"mr\", 100, 50]].forEach(function (c) {\n      const pad = document.createElement(\"div\");\n      pad.className = \"dmb-text-sel-h dmb-text-sel-side\";\n      pad.dataset.h = c[0];\n      pad.style.cssText = \"position:absolute;background:transparent;border:0;pointer-events:auto;z-index:2;touch-action:none;cursor:ew-resize;\" +\n        \"transform:translate(-50%,-50%);width:\" + HIT + \"px;height:\" + HIT + \"px;left:\" + c[1] + \"%;top:\" + c[2] + \"%;\";\n      const pill = document.createElement(\"div\");\n      pill.style.cssText = \"position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);pointer-events:none;\" +\n        \"background:#fff;border:2px solid #7c8aff;border-radius:4px;width:\" + _sW + \"px;height:\" + _sH + \"px;\";\n      pad.appendChild(pill);\n      frame.appendChild(pad);\n    });\n\n    // Corner drag \u2192 font size (radial distance from the text centre; rotation-proof).\n    frame.querySelectorAll(\".dmb-text-sel-corner\").forEach(function (h) {\n      h.addEventListener(\"pointerdown\", function (e) {\n        if (!selectedTextNode) return;\n        e.preventDefault(); e.stopPropagation();\n        const node = selectedTextNode;\n        // The corner is a WHOLE-BOX font-size control. Flatten any per-word size runs\n        // first so the whole text resizes (otherwise run-spans keep their own size and\n        // the drag only changes the box width).\n        try { _dmbStripRunProp(node, \"font-size\"); } catch (e2) {}\n        const rect = node.getBoundingClientRect();\n        const cx = rect.left + rect.width / 2, cy = rect.top + rect.height / 2;\n        const startDist = Math.hypot(e.clientX - cx, e.clientY - cy) || 1;\n        const startFont = parseFloat(window.getComputedStyle(node).fontSize) || 28;\n        // Resize anchored at the OPPOSITE corner: only the two edges meeting the\n        // dragged corner move; the far corner stays put. The box width scales with\n        // the font (so text keeps fitting), and we shift left/top so the opposite\n        // corner is the fixed anchor.\n        const _hdir = h.dataset.h || \"br\";\n        const _growRight = _hdir.indexOf(\"r\") >= 0; // right corner \u2192 anchor LEFT edge\n        const _growDown  = _hdir.indexOf(\"b\") >= 0; // bottom corner \u2192 anchor TOP edge\n        const startEW   = parseFloat(node.style.width) || 0;  // explicit (locked) width, if any\n        const startW    = node.offsetWidth;\n        const startH    = node.offsetHeight;\n        const startLeft = _dmbLenPx(node, \"left\");\n        const startTop  = _dmbLenPx(node, \"top\");\n        // Spine text must STAY centred on the spine \u2014 the opposite-corner anchor\n        // (below) would drift it off-centre, so for spine we re-centre instead.\n        const _utC = (editor.coverUserTexts || []).find((en) => en.id === node.id);\n        const _isSpineTxt = !window.DMB_MAGAZINE_MODE && !!(_utC && _utC.zone === \"spine\") &&\n                            !!node.closest(\".dmb-cover-preview\");\n        try { h.setPointerCapture(e.pointerId); } catch (_) {}\n        const move = _dmbRafMove(function (ev) {\n          const d = Math.hypot(ev.clientX - cx, ev.clientY - cy);\n          const nf = Math.max(8, Math.min(400, Math.round(startFont * (d / startDist))));\n          node.style.fontSize = nf + \"px\";\n          if (startEW > 0) {\n            node.style.width = Math.max(1, Math.round(startEW * (nf / startFont))) + \"px\";\n          }\n          // Non-spine: keep the opposite corner anchored. Spine re-centring + state\n          // saves are deferred to drag-END (below) to keep touch dragging smooth \u2014\n          // they do forced layout reads that are too costly to run every move.\n          if (!_isSpineTxt) {\n            const dw = node.offsetWidth  - startW;\n            const dh = node.offsetHeight - startH;\n            node.style.left = (_growRight ? startLeft : startLeft - dw) + \"px\";\n            node.style.top  = (_growDown  ? startTop  : startTop  - dh) + \"px\";\n          }\n          // Lines only: the corner drives the font size, so there is no width of its\n          // own to snap. Measured AFTER the anchor above, on the final geometry.\n          _dmbTextResizeGuides(node, _hdir, false);\n          positionTextSelectFrame(node); positionRotateHandle(node);\n          const fsi = document.getElementById(\"dmbFontSize\"); if (fsi) fsi.value = String(nf);\n        });\n        const up = function () {\n          move.flush();\n          document.removeEventListener(\"pointermove\", move);\n          document.removeEventListener(\"pointerup\", up);\n          document.removeEventListener(\"pointercancel\", up);\n          if (_isSpineTxt) _dmbRecenterSpineText(node);\n          saveCoverTextNodeState(node); savePageTextState(node);\n          positionTextSelectFrame(node); positionRotateHandle(node);\n          pushUndoState(\"Resize Text\"); saveCoverDesignToStorage();\n        };\n        document.addEventListener(\"pointermove\", move);\n        document.addEventListener(\"pointerup\", up);\n        document.addEventListener(\"pointercancel\", up);\n      });\n    });\n\n    // Side drag \u2192 box width. The edge you are HOLDING is the only one that moves;\n    // the opposite edge stays exactly where it is. Drag is projected onto the box's\n    // local x-axis. (It used to grow about the centre, which meant pulling the right\n    // grip also pushed the left edge out - half the box moved on its own.)\n    frame.querySelectorAll(\".dmb-text-sel-side\").forEach(function (h) {\n      h.addEventListener(\"pointerdown\", function (e) {\n        if (!selectedTextNode) return;\n        e.preventDefault(); e.stopPropagation();\n        const node = selectedTextNode;\n        const rot = (parseFloat(node.dataset.rotate || \"0\") || 0) * Math.PI / 180;\n        const ux = Math.cos(rot), uy = Math.sin(rot);     // box local-x unit (screen)\n        // ---- The floor, measured once --------------------------------------\n        // .dmb-editable-text carries min-width:120px, both in the stylesheet and\n        // inline at creation, so that a brand-new EMPTY box is big enough to tap and\n        // type into. During an explicit width drag that floor used to fight the user:\n        // offsetWidth stopped at 120 while the requested width kept falling to the old\n        // 40px clamp, and because `left` was derived from the REQUESTED width the box\n        // stopped getting narrower and began SLIDING sideways instead - which is what\n        // it looked like from the outside: \"it shrinks to a point, then just moves\".\n        //\n        // An explicit drag outranks a convenience minimum, so the minimum is released\n        // below. The honest floor is the widest UNBREAKABLE word: narrower than that\n        // and the glyphs spill outside the box, and the PDF - which replays the saved\n        // box - would no longer match the screen. Measured here with the minimum\n        // temporarily out of the way, then everything is put back, because a tap on\n        // the grip that never turns into a drag must change nothing. (2026-09-14)\n        let floorW = 24;\n        const _mw0 = node.style.minWidth, _ws0 = node.style.whiteSpace, _w0 = node.style.width;\n        try {\n          node.style.minWidth = \"0\";\n          node.style.whiteSpace = \"pre-wrap\";\n          node.style.width = \"min-content\";\n          floorW = Math.max(24, node.offsetWidth);\n        } catch (_) {}\n        node.style.width = _w0; node.style.whiteSpace = _ws0; node.style.minWidth = _mw0;\n        const startW = node.offsetWidth;\n        // Never let the floor be WIDER than the box already is - an old design whose\n        // box is already narrower than its longest word must not jump wider the\n        // instant the grip is touched.\n        floorW = Math.max(24, Math.min(floorW, startW));\n        const startLeft = _dmbLenPx(node, \"left\");\n        const startTop  = _dmbLenPx(node, \"top\");\n        const sx = e.clientX, sy = e.clientY;\n        const Z = _nodeOnScreenScale(node);\n        const sign = (h.dataset.h === \"mr\") ? 1 : -1;\n        let _released = false;\n        try { h.setPointerCapture(e.pointerId); } catch (_) {}\n        // Set the width, then put the box back where the opposite edge has to be.\n        // Factored out because the snap below has to run it a second time.\n        const _applyW = function (wPx) {\n          node.style.width = Math.max(floorW, Math.round(wPx)) + \"px\";\n          // Anchor on what the browser ACTUALLY rendered, never on what we asked for.\n          // If anything still refuses to shrink, `applied` simply stops changing and\n          // the box holds still. A box must never keep moving while its width stands\n          // still - that was the whole bug.\n          const applied = node.offsetWidth - startW;\n          // ---- hold the opposite edge -------------------------------------\n          // The element is positioned by its top-left but ROTATES about its centre,\n          // so \"keep one edge still\" is not simply \"leave left alone\": widening by\n          // `applied` moves the centre `applied/2` along the box's own x-axis, and\n          // that axis is (ux, uy). Undo exactly that much so the far edge lands back\n          // on itself. At 0 degrees it reduces to left unchanged (right grip) or\n          // left -= applied (left grip), which is what it looks like on screen.\n          const _half = applied / 2;\n          if (sign > 0) {                                   // right grip \u2192 left edge holds\n            node.style.left = (startLeft + _half * (ux - 1)) + \"px\";\n            node.style.top  = (startTop  + _half * uy) + \"px\";\n          } else {                                          // left grip \u2192 right edge holds\n            node.style.left = (startLeft - _half * (ux + 1)) + \"px\";\n            node.style.top  = (startTop  - _half * uy) + \"px\";\n          }\n        };\n        const move = _dmbRafMove(function (ev) {\n          if (!_released) {\n            // First real movement: this is a deliberate resize, so the new-box\n            // minimum stands down and narrowing implies wrapping.\n            _released = true;\n            node.style.minWidth = \"0\";\n            node.style.whiteSpace = \"pre-wrap\";\n          }\n          const dproj = ((ev.clientX - sx) * ux + (ev.clientY - sy) * uy) / Z;\n          _applyW(startW + sign * dproj);\n          // The edge the finger is holding snaps to whatever it lines up with, and\n          // the blue lines show what that was. Re-derived from the pointer on every\n          // move BEFORE this runs, so a snap can never accumulate: let go of the\n          // alignment and the edge follows the finger again.\n          const _sdw = _dmbTextResizeGuides(node, sign > 0 ? \"mr\" : \"ml\", true);\n          if (Math.abs(_sdw) > 0.01) _applyW(node.offsetWidth + _sdw);\n          // Spine re-centre + state saves deferred to drag-END for smooth touch.\n          positionTextSelectFrame(node); positionRotateHandle(node);\n        });\n        const up = function () {\n          move.flush();\n          document.removeEventListener(\"pointermove\", move);\n          document.removeEventListener(\"pointerup\", up);\n          document.removeEventListener(\"pointercancel\", up);\n          _dmbRecenterSpineText(node);   // keep spine text on the spine centre\n          saveCoverTextNodeState(node); savePageTextState(node);\n          positionTextSelectFrame(node); positionRotateHandle(node);\n          pushUndoState(\"Resize Text Width\"); saveCoverDesignToStorage();\n        };\n        document.addEventListener(\"pointermove\", move);\n        document.addEventListener(\"pointerup\", up);\n        document.addEventListener(\"pointercancel\", up);\n      });\n    });\n\n    return frame;\n  }\n\n  function positionTextSelectFrame(node) {\n    if (!node) return;\n    const frame = ensureTextSelectFrame();\n    const parent = node.parentElement;\n    if (!parent) return;\n    if (frame.parentElement !== parent) parent.appendChild(frame);\n    frame.style.left = node.offsetLeft + \"px\";\n    frame.style.top = node.offsetTop + \"px\";\n    frame.style.width = node.offsetWidth + \"px\";\n    frame.style.height = node.offsetHeight + \"px\";\n    frame.style.transform = node.style.transform || \"\";\n    frame.style.transformOrigin = \"center center\";\n    // Same colour + thickness (1.5px #7c8aff); dashed while writing, solid when selected.\n    frame.style.borderStyle = node.classList.contains(\"is-editing\") ? \"dashed\" : \"solid\";\n    frame.style.display = \"block\";\n  }\n\n  function hideTextSelectFrame() {\n    const frame = document.getElementById(\"dmbTextSelFrame\");\n    if (frame) frame.style.display = \"none\";\n  }\n\n  // \u2500\u2500 Text box modes \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // A text box has two states:\n  //   \u2022 SELECTED  \u2192 solid handle frame; node is contenteditable=\"false\" and blurred,\n  //                 so the global arrow-key handler MOVES the box (like any object).\n  //   \u2022 EDITING   \u2192 dashed box; node is contenteditable=\"true\" and focused, so the\n  //                 arrow keys move the caret inside the text (native behaviour).\n  // Flipping contenteditable is what routes the arrow keys: the global keydown skips\n  // whenever document.activeElement is editable.\n  function enterTextEditMode(node) {\n    if (!node) return;\n    node.setAttribute(\"contenteditable\", \"true\");\n    node.classList.add(\"is-editing\");\n    // Writing mode keeps the SAME selection frame + handles, only the frame border\n    // switches to dashed (handled in positionTextSelectFrame).\n    showTextToolbarFor(node);\n    try {\n      node.focus({ preventScroll: true });\n      const r = document.createRange();\n      r.selectNodeContents(node);\n      r.collapse(false);               // caret at the end, ready to type\n      const s = window.getSelection();\n      s.removeAllRanges(); s.addRange(r);\n    } catch (e) {}\n  }\n  function enterTextSelectMode(node) {\n    if (!node) return;\n    node.classList.remove(\"is-editing\");\n    node.setAttribute(\"contenteditable\", \"false\");   // no caret while merely selected\n    if (document.activeElement === node) { try { node.blur(); } catch (e) {} }\n    showTextToolbarFor(node);          // select + solid handle frame\n  }\n\n  function showTextToolbarFor(node) {\n    const toolbar = getTextToolbar();\n    const root = getDmbRoot();\n    if (!toolbar || !node || !root) return;\n    _dmbLastBarShow = () => showTextToolbarFor(node);\n    // (drag no longer hides the text toolbar \u2014 it stays open while moving/resizing)\n\n    // During a drag this is called every pointermove for the SAME node. Run the\n    // heavy selection bookkeeping (class sweep, getComputedStyle, font sync) ONLY\n    // on a genuine selection change; otherwise just reposition. Big touch-lag win.\n    const _sameSel = (selectedTextNode === node) && node.classList.contains(\"is-selected\");\n    if (!_sameSel) {\n      // Switching to a different box \u2192 commit the previous box's pending style edit\n      // now, so its burst closes and the new box starts a fresh undo burst.\n      try { _dmbTextStyleCommit(); } catch (e) {}\n      hideFrameToolbar();\n      dmbCloseShapeEditBar(); // close shape bar when text toolbar opens\n      dmbCloseEditBar();\n      selectedTextNode = node;\n      document.querySelectorAll(\".dmb-editable-text\").forEach((el) => {\n        // Any OTHER box loses selection AND its writing state, so switching boxes\n        // never leaves a stale dashed/editable box behind.\n        if (el !== node) {\n          el.classList.remove(\"is-editing\");\n          el.style.outline = \"none\"; el.style.outlineOffset = \"\";\n          if (el.getAttribute(\"contenteditable\") === \"true\") el.setAttribute(\"contenteditable\", \"false\");\n        }\n        el.classList.remove(\"is-selected\");\n      });\n      node.classList.add(\"is-selected\");\n    }\n\n    // Fast path while dragging THIS already-selected text: the toolbar is already\n    // open & docked \u2014 skip the re-dock and just keep the selection frame + rotate\n    // handle on the moving text.\n    if (_dmbDragSuppress && _sameSel) {\n      positionTextSelectFrame(node);\n      positionRotateHandle(node);\n      return;\n    }\n\n    // Dock the text toolbar into the fixed ribbon instead of floating it above\n    // the text box.\n    toolbar.classList.add(\"is-open\");\n    _dmbDockBar(toolbar);\n\n    // Sync the toolbar inputs from the node ONLY on a real selection change\n    // (getComputedStyle + the font-family scan are too costly to run every move).\n    if (!_sameSel) {\n      const fontFamily = document.getElementById(\"dmbFontFamily\");\n      const fontSize = document.getElementById(\"dmbFontSize\");\n      const textColor = document.getElementById(\"dmbTextColor\");\n      const textAlign = document.getElementById(\"dmbTextAlign\");\n      const cs = window.getComputedStyle(node);\n      if (fontSize) fontSize.value = String(Math.round(parseFloat(cs.fontSize) || 28));\n      if (textColor) textColor.value = rgbToHex(cs.color);\n      if (textAlign) textAlign.value = cs.textAlign || \"left\";\n      try { _dmbSyncTextColorDot(); } catch (e) {}\n      try { _dmbSyncAlignIcon(); } catch (e) {}\n      if (fontFamily) {\n        const ff = (cs.fontFamily || \"\").toLowerCase();\n        for (const opt of fontFamily.options) {\n          const v = opt.value.toLowerCase().replace(/['\"]/g, \"\");\n          if (ff.includes(v.split(\",\")[0].trim())) {\n            fontFamily.value = opt.value;\n            break;\n          }\n        }\n      }\n    }\n\n    positionRotateHandle(node);\n    // Frame + handles show in BOTH modes; positionTextSelectFrame draws its border\n    // dashed while writing (is-editing) and solid while merely selected.\n    positionTextSelectFrame(node);\n  }\n\n  function hideTextToolbar() {\n    try { _dmbTextStyleCommit(); } catch (e) {}   // flush any pending text-style edit on deselect\n    const toolbar = getTextToolbar();\n    if (toolbar) toolbar.classList.remove(\"is-open\");\n    try { _dmbCloseTextMenus(null); } catch (e) {}   // never reopen a stale popup\n    hideTextSelectFrame();\n\n    document.querySelectorAll(\".dmb-editable-text\").forEach((el) => {\n      el.classList.remove(\"is-selected\");\n      el.classList.remove(\"is-editing\");\n      el.style.outline = \"none\"; el.style.outlineOffset = \"\";\n      if (el.getAttribute(\"contenteditable\") === \"true\") el.setAttribute(\"contenteditable\", \"false\");\n    });\n\n    hideRotateHandle();\n    selectedTextNode = null;\n    try { _dmbUpdateRibbonPlaceholder(); } catch (e) {}\n  }\n\n  // Fully discard a text box: remove its DOM node, purge it from every persisted\n  // store, and close its menu. Used for empty boxes the user abandons (blur /\n  // click-away) so an empty box never lingers on the canvas or in saved state.\n  function _dmbRemoveTextNode(node) {\n    if (!node) return;\n    if (node.id) {\n      if (Array.isArray(editor.coverUserTexts)) {\n        editor.coverUserTexts = editor.coverUserTexts.filter((en) => en.id !== node.id);\n      }\n      if (coverState.textStyles) delete coverState.textStyles[node.id];\n      const _pn = parseInt(node.dataset.pageNumber);\n      if (Number.isFinite(_pn) && Array.isArray(editor.pageObjects?.[_pn])) {\n        editor.pageObjects[_pn] = editor.pageObjects[_pn].filter((o) => o.id !== node.id);\n      }\n    }\n    if (selectedTextNode === node) hideTextToolbar();\n    node.remove();\n    try { saveCoverDesignToStorage(); } catch (_) {}\n  }\n\n  // \u2500\u2500 Fix 5: Auto-zone assignment \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // Automatically assigns the cover zone (front/spine/back) based on where\n  // the centre of the object sits after a drag. No dialog \u2014 silent assignment.\n  // The zone drives clipping (dmbApplyZoneClip) and alignment (dmbAlignObjectInZone).\n  function _dmbAutoAssignCoverZone(obj, el) {\n    if (window.DMB_MAGAZINE_MODE) {\n      // Magazine: no spine \u2014 just front/back split at COVER_BACK_W\n      var cx = (obj.x || 0) + (obj.w || el.offsetWidth || 0) / 2;\n      obj.coverZone = cx < COVER_BACK_W ? \"back\" : \"front\";\n    } else {\n      var cx = (obj.x || 0) + (obj.w || el.offsetWidth || 0) / 2;\n      if (cx < COVER_BACK_W) obj.coverZone = \"back\";\n      else if (cx < COVER_FRONT_X) obj.coverZone = \"spine\";\n      else obj.coverZone = \"front\";\n    }\n    dmbApplyZoneClip(el, obj);\n    // Re-apply contain clip with the newly assigned zone\n    try { if (obj.contained) _dmbApplyContain(el, obj, true); } catch(e) {}\n  }\n\n  // Legacy wrapper \u2014 kept for any remaining callers but now auto-assigns\n  // instead of showing a dialog.\n  function checkCoverZoneBoundaries(getLeft, getWidth, setLeft, setZone) {\n    // No-op: zone assignment is now handled by _dmbAutoAssignCoverZone\n    // which is called directly from each pointerup handler.\n  }\n\n  function makeTextInteractive(node) {\n    let dragging = false;\n    let startX = 0;\n    let startY = 0;\n    let startLeft = 0;\n    let startTop = 0;\n    let moved = false;\n    // Mode the box was in when the current gesture began (\"idle\" | \"editing\" | \"solid\").\n    // Drives the click toggle: 1st click on an idle box edits, next click solidifies.\n    let _modeAtDown = \"idle\";\n    // Natural glyph width for drag clamp (computed at drag start without forced CSS width\n    // so a wide alignment box like width:220px doesn't trap the visible text in the middle).\n    let _dragNW = 0;\n    // Pointer id of the active drag \u2014 captured once a real move begins so touch\n    // devices keep delivering pointermove/up (and don't bail to pointercancel).\n    let _ptrId = null;\n    // Spine text is always horizontally centred \u2014 during a drag only vertical\n    // movement is allowed (horizontal is pinned to the start = centred position).\n    let _spineLock = false;\n    // Cached at drag start (constant during a position drag) so onMove avoids\n    // per-move layout reflows: the on-screen scale and the clamp bounds.\n    let _dragZ = 1, _dragBounds = null;\n    let _dragFrame = null, _dragRot = null, _fr0L = 0, _fr0T = 0, _rh0L = 0, _rh0T = 0;\n\n    const onMove = _dmbRafMove((e) => {\n      if (!dragging) return;\n\n      // Convert screen-pixel finger movement into canvas-model pixels. The\n      // builder canvas is rendered with transform:scale(currentZoom) (50% on\n      // phones), so a raw clientX delta would move the text by only `zoom`\u00D7 the\n      // finger distance \u2014 the object lags behind the finger on touch. Dividing\n      // by the node's on-screen scale makes 1 finger px == 1 model px again.\n      const _sdx = e.clientX - startX;\n      const _sdy = e.clientY - startY;\n      const _Z = _dragZ;   // cached at drag start (no per-move reflow)\n      const dx = _sdx / _Z;\n      const dy = _sdy / _Z;\n\n      if (!moved && (Math.abs(_sdx) > 2 || Math.abs(_sdy) > 2)) {\n        moved = true;\n        // Past the threshold this press is a MOVE, not a caret tap: cancel any native\n        // text-selection the mousedown started and drop the box out of writing (it goes\n        // solid / not-dashed) so the drag relocates the whole box.\n        try { window.getSelection().removeAllRanges(); } catch (e2) {}\n        enterTextSelectMode(node);\n        // Capture the pointer on the first real movement so the whole drag is\n        // delivered here even on touch (and a plain tap stays a tap-to-edit).\n        try { if (_ptrId != null) node.setPointerCapture(_ptrId); } catch (e2) {}\n      }\n      if (!moved) return;   // sub-threshold jitter on a tap \u2192 no move / no snap guides\n\n      let newLeft = startLeft + dx;\n      let newTop = startTop + dy;\n\n      const parent = node.parentElement;\n      // Centre-based bounds (keep the box CENTRE on the cover/page; the rotated\n      // extent may spill past the edges). Precomputed once at drag start \u2014 see the\n      // pointerdown handler \u2014 so onMove does NO layout reflows here.\n      if (parent && _dragBounds) {\n        newLeft = Math.max(_dragBounds.xMin, Math.min(newLeft, _dragBounds.xMax));\n        newTop  = Math.max(_dragBounds.yMin, Math.min(newTop,  _dragBounds.yMax));\n      }\n\n      node.style.left = `${newLeft}px`;\n      node.style.top = `${newTop}px`;\n      // Full sibling snap for text: shows guides AND snaps to all other objects.\n      // The snap uses the GLYPH box (visible text), not the element box.  The text\n      // element has padding 2px 6px, so the box left edge is 6px left of the first\n      // letter and the box right edge is 6px right of the last letter.  We inset by\n      // the padding so the alignment line lands on the actual letter edges, then\n      // convert the snapped glyph-x back to the element box-x.\n      if (parent) {\n        const { gH: tGH, gV: tGV } = dmbGetSnapGuides(parent);\n        const _isCoverTxt = !!parent.closest(\".dmb-cover-preview\");\n        const _tpn = parseInt(node.dataset.pageNumber);\n        const _tmeta = _isCoverTxt\n          ? { targetType: \"cover\" }\n          : (Number.isFinite(_tpn) ? { targetType: \"page\", pageNumber: _tpn } : null);\n        const _TX_PL = 6, _TX_PT = 2; // matches CSS padding: 2px 6px\n        const _glyphW = Math.max(0, (node.offsetWidth  || 132) - _TX_PL * 2) || 120;\n        const _glyphH = Math.max(0, (node.offsetHeight || 34)  - _TX_PT * 2) || 26;\n        const _snapTxt = {\n          id: node.id,\n          x: newLeft + _TX_PL, y: newTop + _TX_PT,\n          w: _glyphW,\n          h: _glyphH,\n        };\n        dmbCheckSnap(_snapTxt, parent, tGH, tGV, _tmeta);\n        // Convert snapped glyph position back to the element box position\n        newLeft = _snapTxt.x - _TX_PL;\n        newTop  = _snapTxt.y - _TX_PT;\n        // CRITICAL: re-apply to the DOM so the snap actually moves the text.\n        // Without this the snapped position is computed but never written, so the\n        // guide appears but the text does not snap into place.\n        node.style.left = `${newLeft}px`;\n        node.style.top  = `${newTop}px`;\n      }\n      // Spine: keep the horizontal position pinned (centred) \u2014 vertical only.\n      if (_spineLock) {\n        newLeft = startLeft;\n        node.style.left = `${newLeft}px`;\n      }\n      // State saves are deferred to drag-END (onUp) so touch dragging stays smooth.\n      showTextToolbarFor(node);\n      // The selection frame + rotate handle no longer hide during a drag (the menu\n      // stays open), so they must FOLLOW the text as it moves \u2014 otherwise the box\n      // outline lags behind and snaps over only on release.\n      // Follow the box by its offset from drag start (no layout reads per frame).\n      // The handle lives in screen space, so its offset is scaled by the canvas zoom.\n      if (_dragFrame) {\n        _dragFrame.style.left = (_fr0L + (newLeft - startLeft)) + \"px\";\n        _dragFrame.style.top  = (_fr0T + (newTop - startTop)) + \"px\";\n      }\n      if (_dragRot) {\n        _dragRot.style.left = (_rh0L + (newLeft - startLeft) * _dragZ) + \"px\";\n        _dragRot.style.top  = (_rh0T + (newTop - startTop) * _dragZ) + \"px\";\n      }\n    });\n\n    const onUp = () => {\n      onMove.flush();\n      dragging = false;\n      document.removeEventListener(\"pointermove\", onMove);\n      document.removeEventListener(\"pointerup\", onUp);\n      document.removeEventListener(\"pointercancel\", onUp);\n      try { if (_ptrId != null) node.releasePointerCapture(_ptrId); } catch (e2) {}\n      _ptrId = null;\n      if (!moved) {\n        // A TAP (no drag): the browser already placed the caret where the user clicked.\n        // Enter WRITING mode without disturbing that caret \u2014 do NOT call\n        // enterTextEditMode(), which collapses the caret to the end of the text.\n        node.setAttribute(\"contenteditable\", \"true\");\n        node.classList.add(\"is-editing\");\n        showTextToolbarFor(node);\n        try { if (document.activeElement !== node) node.focus({ preventScroll: true }); } catch (e2) {}\n        return;\n      }\n      positionTextSelectFrame(node); positionRotateHandle(node);   // exact, once, on release\n      pushUndoState(\"Move Text\");\n      if (moved && node.closest(\".dmb-cover-preview\")) {\n        // Determine which cover zone the text now sits in (by its visual centre x,\n        // measured from the full-cover overlay so the zoom transform is handled)\n        // and update its stored zone. If it landed on the spine, snap it to the\n        // spine's horizontal centre \u2014 vertical position is kept as dropped.\n        const _ov = document.getElementById(\"coverOverlay\");\n        if (_ov) {\n          const _ovr = _ov.getBoundingClientRect();\n          const _Zs  = (_ovr.width / (_ov.offsetWidth || 622)) || 1;\n          const _nr  = node.getBoundingClientRect();\n          const _cx  = ((_nr.left + _nr.width / 2) - _ovr.left) / _Zs;\n          let _zone;\n          if (window.DMB_MAGAZINE_MODE) {\n            _zone = _cx < COVER_BACK_W ? \"back\" : \"front\";\n          } else if (_cx < COVER_BACK_W) {\n            _zone = \"back\";\n          } else if (_cx <= COVER_BACK_W + COVER_SPINE_W) {\n            _zone = \"spine\";\n          } else {\n            _zone = \"front\";\n          }\n          const _ut = (editor.coverUserTexts || []).find((en) => en.id === node.id);\n          if (_ut) _ut.zone = _zone;\n          if (_zone === \"spine\" && !window.DMB_MAGAZINE_MODE) {\n            // Centre the LETTERS (glyph centre), not the box \u2014 for \u221290\u00B0 spine text\n            // the in-box line-height gap becomes a horizontal offset after rotation.\n            let _gcx = _cx;\n            try {\n              const _tf = node.style.transform;\n              node.style.transform = \"none\";\n              const _ur = node.getBoundingClientRect();\n              const _rng = document.createRange(); _rng.selectNodeContents(node);\n              const _gr = _rng.getBoundingClientRect();\n              node.style.transform = _tf;\n              if (_gr && _gr.width > 0 && _gr.height > 0) {\n                const _gdx = ((_gr.left + _gr.width  / 2) - (_ur.left + _ur.width  / 2)) / _Zs;\n                const _gdy = ((_gr.top  + _gr.height / 2) - (_ur.top  + _ur.height / 2)) / _Zs;\n                let _eff = parseFloat(node.dataset.rotate || \"0\") || 0;\n                if (!_eff) { const _m = (_tf || \"\").match(/rotate\\(\\s*(-?[\\d.]+)deg/); if (_m) _eff = parseFloat(_m[1]) || 0; }\n                const _rad = _eff * Math.PI / 180;\n                _gcx = _cx + (_gdx * Math.cos(_rad) - _gdy * Math.sin(_rad));\n              }\n            } catch (e) {}\n            const _spineCx = COVER_BACK_W + COVER_SPINE_W / 2;\n            const _dx = _spineCx - _gcx;\n            node.style.left = ((parseFloat(node.style.left) || 0) + _dx) + \"px\";\n          }\n        }\n      }\n      else if (moved) {\n        // Interior page cross-page move: if the box centre now sits over the OTHER\n        // page body of the spread, reparent it there (and reindex it in\n        // editor.pageObjects). Dropping over a LOCKED page or the gutter snaps it\n        // back inside its current page.\n        try {\n          const _oldBody = node.parentElement;\n          const _spread = _oldBody && _oldBody.closest(\"#spreadCanvas\");\n          if (_spread) {\n            const _nr = node.getBoundingClientRect();\n            const _cx = _nr.left + _nr.width / 2, _cy = _nr.top + _nr.height / 2;\n            const _Z = _dragZ || 1;\n            const _bodies = Array.from(_spread.querySelectorAll('[data-page-body=\"true\"]'));\n            let _hit = null;\n            for (const b of _bodies) { const r = b.getBoundingClientRect(); if (_cx >= r.left && _cx <= r.right && _cy >= r.top && _cy <= r.bottom) { _hit = b; break; } }\n            const _clampBack = (body) => {\n              const bw = node.offsetWidth, bh = node.offsetHeight;\n              let l = parseFloat(node.style.left) || 0, t = parseFloat(node.style.top) || 0;\n              l = Math.max(0, Math.min(l, Math.max(0, (body.offsetWidth  || 0) - bw)));\n              t = Math.max(0, Math.min(t, Math.max(0, (body.offsetHeight || 0) - bh)));\n              node.style.left = l + \"px\"; node.style.top = t + \"px\";\n            };\n            if (_hit && _hit !== _oldBody) {\n              const _newPn = parseInt(_hit.dataset.pageNumber);\n              if (Number.isFinite(_newPn) && !isLockedPage(_newPn)) {\n                const _oldPn = parseInt(node.dataset.pageNumber);\n                const _nbr = _hit.getBoundingClientRect();\n                const _newLeft = (_nr.left - _nbr.left) / _Z;\n                const _newTop  = (_nr.top  - _nbr.top ) / _Z;\n                _hit.appendChild(node);\n                node.style.left = _newLeft + \"px\";\n                node.style.top  = _newTop  + \"px\";\n                node.dataset.pageNumber = String(_newPn);\n                if (Number.isFinite(_oldPn) && Array.isArray(editor.pageObjects[_oldPn])) {\n                  const _i = editor.pageObjects[_oldPn].findIndex((o) => o.id === node.id);\n                  if (_i >= 0) {\n                    const _obj = editor.pageObjects[_oldPn].splice(_i, 1)[0];\n                    if (!Array.isArray(editor.pageObjects[_newPn])) editor.pageObjects[_newPn] = [];\n                    editor.pageObjects[_newPn].push(_obj);\n                  }\n                }\n              } else {\n                _clampBack(_oldBody);   // locked target \u2192 keep it on its own page\n              }\n            } else if (!_hit) {\n              _clampBack(_oldBody);     // dropped over the gutter \u2192 snap inside\n            }\n          }\n        } catch (e) {}\n      }\n      // Persist the final position once, at drag-end (deferred from onMove so the\n      // drag stays smooth on touch).\n      saveCoverTextNodeState(node);\n      savePageTextState(node);\n    };\n\n    node.addEventListener(\"pointerdown\", (e) => {\n      if (e.target.closest(\"#dmbTextToolbar\")) return;\n      if (e.target.closest(\"#dmbTextRotateHandle\")) return;\n      // Click-through: if this text box isn't the active one and the press lands on\n      // its transparent area (not the letters) \u2014 or the user Alt-clicks anywhere \u2014\n      // and a selectable item sits beneath that point, select THAT item instead. This\n      // keeps small shapes tucked under a text box reachable with a normal click.\n      try {\n        const _idleText = (selectedTextNode !== node);\n        if ((_idleText && !_dmbPointOnGlyphs(node, e.clientX, e.clientY)) || e.altKey) {\n          const _beneath = _dmbSelectableBeneath(e.clientX, e.clientY, node);\n          if (_beneath) {\n            e.preventDefault(); e.stopPropagation();\n            _dmbSelectObjectEl(_beneath.obj, _beneath.el);\n            return;\n          }\n        }\n      } catch (_pt) {}\n      _modeAtDown = node.classList.contains(\"is-editing\") ? \"editing\"\n                  : (selectedTextNode === node ? \"solid\" : \"idle\");\n      moved = false;\n\n      // \u2500\u2500 Writing mode: a drag across the LETTERS selects them \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n      // Dragging used to relocate the box from every state, writing included,\n      // and it cancelled the native selection on purpose. That is why no part of\n      // a text box could be selected: the only gesture that can pick out a few\n      // words was being spent on moving the box.\n      //\n      // So in writing mode (dashed box, caret in the text) a press that lands on\n      // the glyphs is left entirely to the browser - no capture, no listeners,\n      // nothing armed - and it drag-selects exactly as text does anywhere else.\n      // The box is still moved by dragging it while it is SELECTED (solid frame,\n      // one click away), and still moved from writing mode by grabbing it\n      // anywhere other than the letters, which on a centred box is most of it.\n      // This is the same split Canva and Slides use. (2026-10-12)\n      if (_modeAtDown === \"editing\") {\n        let _onGlyphs = false;\n        try { _onGlyphs = _dmbPointOnGlyphs(node, e.clientX, e.clientY); } catch (_g) {}\n        if (_onGlyphs) { dragging = false; return; }\n      }\n      // Unified gesture (Canva/Slides-style): a plain TAP drops the caret exactly\n      // where the user clicks (native contenteditable), a DRAG past 2px relocates the\n      // whole box. So we make the box editable NOW and DON'T preventDefault, letting\n      // the browser place the caret; onMove converts the press into a move the moment\n      // the finger travels far enough (going solid / not-dashed and cancelling the\n      // native text-selection). Works from idle, solid AND editing states.\n      if (selectedTextNode !== node) showTextToolbarFor(node);   // select (solid frame)\n      if (node.getAttribute(\"contenteditable\") !== \"true\") node.setAttribute(\"contenteditable\", \"true\");\n\n      dragging = true;\n      _ptrId = e.pointerId;\n      startX = e.clientX;\n      startY = e.clientY;\n      // Resolve through _dmbLenPx: a spine title's top is \"50%\". Reading it as the\n      // bare number 50 made the text teleport ~162px UP the spine the instant the\n      // drag passed its 2px threshold - and the spine lock below pins only the\n      // HORIZONTAL position, so nothing caught the vertical jump. (2026-09-14)\n      startLeft = _dmbLenPx(node, \"left\");\n      startTop = _dmbLenPx(node, \"top\");\n      // Spine cover text stays horizontally centred \u2014 lock horizontal during drag.\n      {\n        const _ut = (editor.coverUserTexts || []).find((en) => en.id === node.id);\n        _spineLock = !!(_ut && _ut.zone === \"spine\") &&\n                     !window.DMB_MAGAZINE_MODE &&\n                     !!node.closest(\".dmb-cover-preview\");\n      }\n      // Compute natural glyph width without any forced CSS width so the drag clamp\n      // uses the actual visible text extent (not an alignment box that may be wider).\n      { const _sw = node.style.width; node.style.width = ''; _dragNW = node.offsetWidth || 80; node.style.width = _sw; }\n\n      // Cache the scale + clamp bounds ONCE (cover/parent rects, scale and box size\n      // are all constant during a position drag) so onMove does no layout reflows.\n      _dragZ = _nodeOnScreenScale(node) || 1;\n      {\n        const _pp = node.parentElement;\n        const _bw0 = node.offsetWidth, _bh0 = node.offsetHeight;\n        let _xMn, _xMx;\n        const _cw = _pp && _pp.closest(\".dmb-cover-preview\");\n        if (_cw) {\n          const _cr = _cw.getBoundingClientRect();\n          const _pr = _pp.getBoundingClientRect();\n          _xMn = Math.round((_cr.left  - _pr.left) / _dragZ - _bw0 / 2);\n          _xMx = Math.round((_cr.right - _pr.left) / _dragZ - _bw0 / 2);\n        } else {\n          // Interior page: let the box travel across the spread's OTHER page during\n          // the drag (it is reparented to that page on drop). Clamp to the whole\n          // spread, expressed in THIS page body's coordinate system.\n          const _spread = _pp && _pp.closest(\"#spreadCanvas\");\n          if (_spread) {\n            const _sr = _spread.getBoundingClientRect();\n            const _pr = _pp.getBoundingClientRect();\n            _xMn = Math.round((_sr.left  - _pr.left) / _dragZ - _bw0 / 2);\n            _xMx = Math.round((_sr.right - _pr.left) / _dragZ - _bw0 / 2);\n          } else {\n            _xMn = Math.round(-_bw0 / 2);\n            _xMx = Math.round((_pp ? _pp.offsetWidth : 0) - _bw0 / 2);\n          }\n        }\n        _dragBounds = {\n          xMin: _xMn, xMax: _xMx,\n          yMin: Math.round(-_bh0 / 2),\n          yMax: Math.round((_pp ? _pp.offsetHeight : 0) - _bh0 / 2),\n        };\n      }\n      // Selection frame + rotate handle: read their resting position ONCE here. During\n      // the drag they move by the same offset as the box (no layout reads per frame);\n      // onUp re-measures them exactly.\n      positionTextSelectFrame(node); positionRotateHandle(node);\n      const _dragFr = ensureTextSelectFrame();\n      const _dragRh = ensureRotateHandle();\n      _dragFrame = _dragFr; _dragRot = _dragRh;\n      _fr0L = parseFloat(_dragFr.style.left) || 0; _fr0T = parseFloat(_dragFr.style.top) || 0;\n      _rh0L = parseFloat(_dragRh.style.left) || 0; _rh0T = parseFloat(_dragRh.style.top) || 0;\n\n      document.addEventListener(\"pointermove\", onMove);\n      document.addEventListener(\"pointerup\", onUp);\n      document.addEventListener(\"pointercancel\", onUp);\n    });\n\n    node.addEventListener(\"click\", (e) => {\n      e.stopPropagation();   // don't let the canvas deselect the box\n      moved = false;\n      // Tap-vs-drag and caret placement are handled by the pointer gesture\n      // (pointerdown / onMove / onUp) \u2014 nothing to toggle here.\n    });\n\n    // Prevent native browser text-selection drag from bubbling to the page drop handler\n    node.addEventListener(\"dragstart\", (e) => { e.preventDefault(); e.stopPropagation(); });\n\n    // Intercept Backspace / Delete when the text box is empty.\n    // Without this, the browser removes the DOM node directly but leaves it in\n    // editor.pageObjects / editor.coverObjects, so it reappears on the next\n    // re-render.  We replicate the same cleanup the Delete toolbar button does.\n    node.addEventListener(\"keydown\", (e) => {\n      // Escape leaves writing mode \u2192 back to SELECTED (solid box, arrows move it).\n      if (e.key === \"Escape\") { e.preventDefault(); e.stopPropagation(); enterTextSelectMode(node); return; }\n      if (e.key !== \"Backspace\" && e.key !== \"Delete\") return;\n      if (node.textContent.trim() !== \"\") return; // still has content \u2014 let browser handle it\n      // Empty box: do NOT delete it. Keep the box on the canvas (it shows its\n      // placeholder) so a stray Backspace on a just-added text can't wipe the\n      // whole box. An empty box is discarded only when the user clicks away\n      // while it's still empty (see the blur / click-out handlers below).\n      e.preventDefault();\n    });\n\n    node.addEventListener(\"blur\", () => {\n      // Defer to let focus settle (clicking a toolbar control blurs the text).\n      setTimeout(() => {\n        const ae = document.activeElement;\n        // Focus moved into a toolbar control (font size, colour\u2026) \u2014 keep the\n        // editing session and its menu alive.\n        if (ae && ae.closest && ae.closest(\"#dmbTextToolbar\")) {\n          if (selectedTextNode === node) showTextToolbarFor(node);\n          return;\n        }\n        if (ae === node) return; // caret returned to the text itself\n        // Left empty \u2192 drop the box together with its menu, immediately.\n        if (!captureVisualText(node).trim()) { _dmbRemoveTextNode(node); return; }\n        // Non-empty: preserve previous behaviour (keep it selected if it still\n        // is; an outside click will already have deselected it).\n        if (selectedTextNode === node) showTextToolbarFor(node);\n      }, 0);\n    });\n  }\n\n  // Make a text box fit the page (or cover face) it sits on: wrap the text, keep the\n  // box inside the trim, then step the font down until the whole thing fits. Used\n  // after a paste, where the incoming text can be far longer than the box was drawn\n  // for and would otherwise run straight off the sheet. (2026-10-12)\n  // \u2500\u2500 Paste with nothing open \u2192 a new text box \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // A paste only reaches here when NEITHER item clipboard holds anything: the\n  // Ctrl+V key handler takes the event (and preventDefaults, which stops the paste\n  // event ever firing) whenever an item or a group was copied, so a copied frame\n  // still pastes as a frame. What is left is text from outside the builder, and the\n  // only sensible place for that is a box of its own. (2026-10-13)\n  function _dmbBuilderBusy() {\n    try {\n      if (document.documentElement.classList.contains(\"dmbce-open\")) return true;  // card editor\n      const ids = [\"dmbPreviewOverlay\", \"dmbProofOverlay\", \"dmbCardModal\", \"dmbNoticeDialog\", \"dmbDupPageModal\"];\n      for (let i = 0; i < ids.length; i++) {\n        const el = document.getElementById(ids[i]);\n        if (el && el.offsetParent !== null) return true;\n      }\n    } catch (e) {}\n    return false;\n  }\n\n  function _dmbPasteAsNewTextBox(txt) {\n    // Go through the Add text button, so the box is REGISTERED exactly like every\n    // other one: right page or cover zone, id, z, model entry, handlers. A second\n    // implementation here is precisely how a box ends up on screen but missing from\n    // the PDF.\n    const btn = document.getElementById(\"canvasTextBtn\") || document.getElementById(\"addTextBtn\");\n    if (!btn) return false;\n    const before = document.querySelectorAll(\".dmb-editable-text\").length;\n    btn.click();\n    const node = (typeof selectedTextNode !== \"undefined\") ? selectedTextNode : null;\n    if (!node || !node.isConnected) return false;\n    if (document.querySelectorAll(\".dmb-editable-text\").length <= before) return false;\n    node.textContent = txt;\n    try { _dmbFitTextToSheet(node); } catch (e) {}          // wrap + shrink onto the sheet\n    try { saveCoverTextNodeState(node); } catch (e) {}\n    try { savePageTextState(node); } catch (e) {}\n    try { saveCoverDesignToStorage(); } catch (e) {}\n    try { _dmbWriteProgressState(); } catch (e) {}\n    try { if (typeof _dmbCloudPersist === \"function\") _dmbCloudPersist(); } catch (e) {}\n    // Land SELECTED, not writing. The text is already there, and a selected box is\n    // the one you can drag straight into place (in writing mode a drag across the\n    // letters selects them instead).\n    try { enterTextSelectMode(node); } catch (e) {}\n    try { positionTextSelectFrame(node); positionRotateHandle(node); } catch (e) {}\n    return true;\n  }\n\n  document.addEventListener(\"paste\", function (e) {\n    try {\n      if (typeof _dmbRenderMode === \"function\" && _dmbRenderMode()) return;\n      // Something that takes typing already has the focus: a box being written in,\n      // a toolbar field, a search box. Its own handler owns the paste.\n      const a = document.activeElement;\n      if (a && (a.isContentEditable || a.tagName === \"INPUT\" ||\n                a.tagName === \"TEXTAREA\" || a.tagName === \"SELECT\")) return;\n      if (e.target && e.target.closest &&\n          e.target.closest(\"input, textarea, [contenteditable='true']\")) return;\n      if (_dmbBuilderBusy()) return;\n      // The builder has to be the thing on screen, and it has to have somewhere to\n      // put the box.\n      const host = document.querySelector('[data-page-body=\"true\"], #coverOverlay');\n      if (!host || host.offsetParent === null) return;\n\n      let txt = \"\";\n      try {\n        const cd = e.clipboardData || window.clipboardData;\n        txt = (cd && cd.getData(\"text/plain\")) || \"\";\n      } catch (err) {}\n      // Normalise the line endings exactly as the in-box paste does, then drop the\n      // blank edges a copy from a web page nearly always brings with it. An image on\n      // the clipboard leaves this empty, and nothing happens.\n      txt = txt.replace(/\\r\\n?/g, \"\\n\").replace(/^\\s+|\\s+$/g, \"\");\n      if (!txt) return;\n      e.preventDefault();\n      _dmbPasteAsNewTextBox(txt);\n    } catch (err) {}\n  }, true);\n\n  function _dmbFitTextToSheet(node) {\n    try {\n      if (!node) return;\n      var host = node.closest('[data-page-body=\"true\"]') ||\n                 node.closest(\"#coverOverlay\") ||\n                 node.closest(\".dmb-cover-preview\") ||\n                 node.parentElement;\n      if (!host) return;\n      var isPage = !!node.closest('[data-page-body=\"true\"]');\n      var HW = host.offsetWidth || 0, HH = host.offsetHeight || 0;\n      if (!HW || !HH) return;\n      var M = 10;                                  // keep clear of the trim edge\n\n      // Long pasted text must WRAP; without this a single line runs off the sheet and\n      // no amount of shrinking brings it back.\n      node.style.whiteSpace = \"pre-wrap\";\n      node.style.minWidth = \"0\";                   // an explicit width outranks the\n                                                   // 120px new-box minimum\n\n      // ---- width ----\n      var availW = Math.max(60, HW - M * 2);\n      var w = node.offsetWidth || 0;\n      if (!w || w > availW) w = availW;\n      if (w < 60) w = Math.min(availW, 160);\n      node.style.width = Math.round(w) + \"px\";\n\n      // ---- horizontal position: only for PAGE text ----\n      // Cover text is zone-anchored and spine text is centred with a percentage top;\n      // nudging either from here would undo that, so leave cover boxes where they are.\n      if (isPage) {\n        var left = _dmbLenPx(node, \"left\");\n        if (left + w > HW - M) left = Math.max(M, HW - M - w);\n        if (left < M) left = M;\n        node.style.left = Math.round(left) + \"px\";\n      }\n\n      // ---- font size: step down until the height fits below the box top ----\n      var top = isPage ? _dmbLenPx(node, \"top\") : 0;\n      var availH = Math.max(40, HH - M - (isPage ? top : M));\n      var fs = parseFloat(window.getComputedStyle(node).fontSize) || 28;\n      var guard = 0;\n      while (node.offsetHeight > availH && fs > 6 && guard++ < 80) {\n        fs = Math.max(6, fs - Math.max(1, Math.round(fs * 0.08)));\n        node.style.fontSize = fs + \"px\";\n      }\n\n      // Still too tall at the minimum size: slide the box up rather than let it hang\n      // off the bottom of the sheet. Page text only, for the same reason as above.\n      if (isPage && node.offsetHeight > availH) {\n        node.style.top = Math.max(M, HH - M - node.offsetHeight) + \"px\";\n      }\n\n      try {\n        var fsi = document.getElementById(\"dmbFontSize\");\n        if (fsi) fsi.value = String(Math.round(fs));\n      } catch (e) {}\n    } catch (e) {}\n  }\n\n  function attachTextNodeHandlers(node) {\n    if (!node) return;\n    if (node.dataset.dmbTextReady === \"true\") return;\n    node.dataset.dmbTextReady = \"true\";\n\n    node.classList.add(\"dmb-editable-text\");\n    node.style.position = \"absolute\";\n    node.dataset.z = node.dataset.z || String(Date.now());\n    node.style.zIndex = node.dataset.z;\n    node.style.transformOrigin = \"center center\";\n    // Touch drag fix: without touch-action:none the browser claims the drag\n    // gesture on this (contenteditable) node and fires pointercancel instead of\n    // pointerup, so the move is never committed (no pushUndoState \u2192 no autosave\n    // \u2192 preview/PDF revert to the template position). A tap still focuses the\n    // node for editing; only pan/scroll-from-this-node is suppressed.\n    node.style.touchAction = \"none\";\n\n    if (!node.style.left) node.style.left = \"40px\";\n    if (!node.style.top) node.style.top = \"40px\";\n\n    // Boxes start non-editable (idle / SELECTED). Writing mode flips this to \"true\"\n    // via enterTextEditMode, so a stray focus can never put the arrow keys into\n    // caret-move mode on an unselected box.\n    node.setAttribute(\"contenteditable\", \"false\");\n\n    // Mode transitions are owned by makeTextInteractive's pointerdown/click; here we\n    // only stop the click bubbling to the canvas deselect handler.\n    node.addEventListener(\"click\", (e) => { e.stopPropagation(); });\n\n    // Paste carries the SOURCE's markup: Word and web pages bring their own font,\n    // size and colour inline, so pasted text landed looking nothing like the box it\n    // was pasted into. Take the plain text only, so it inherits the box's own styling,\n    // then re-fit the box to the sheet. execCommand keeps the browser's native undo\n    // stack, which a manual range insert would break. (2026-10-12)\n    node.addEventListener(\"paste\", (e) => {\n      let txt = \"\";\n      try {\n        const cd = e.clipboardData || window.clipboardData;\n        txt = (cd && cd.getData(\"text/plain\")) || \"\";\n      } catch (err) {}\n      if (!txt) return;                       // nothing usable: let the browser handle it\n      e.preventDefault();\n      txt = txt.replace(/\\r\\n?/g, \"\\n\");\n      let ok = false;\n      try { ok = document.execCommand(\"insertText\", false, txt); } catch (err) {}\n      if (!ok) {\n        // Fallback for anywhere execCommand is unavailable.\n        try {\n          const sel = window.getSelection();\n          if (sel && sel.rangeCount) {\n            const r = sel.getRangeAt(0);\n            r.deleteContents();\n            const tn = document.createTextNode(txt);\n            r.insertNode(tn);\n            r.setStartAfter(tn); r.collapse(true);\n            sel.removeAllRanges(); sel.addRange(r);\n          } else {\n            node.textContent = (node.textContent || \"\") + txt;\n          }\n        } catch (err) {}\n      }\n      // Drop anything the browser slipped in that is not ours, keeping what IS:\n      // <br> line breaks and the spans that carry per-word formatting. This used to\n      // flatten the box to textContent whenever it held ANY element, which quietly\n      // joined every line into one (textContent has no line breaks) and, since the\n      // per-word looks arrived, wiped those too.\n      try { _dmbStripForeignMarkup(node); } catch (err) {}\n      try { _dmbFitTextToSheet(node); } catch (err) {}\n      // Run the normal save path so the PDF capture and autosave see the new text.\n      try { node.dispatchEvent(new Event(\"input\", { bubbles: true })); } catch (err) {}\n      try { positionTextSelectFrame(node); positionRotateHandle(node); } catch (err) {}\n      try { pushUndoState(\"Paste Text\"); } catch (err) {}\n    });\n\n    node.addEventListener(\"input\", () => {\n      // Keep empty boxes visible (they show the placeholder); an abandoned empty\n      // box is removed on blur / click-out, not hidden here.\n      node.style.display = \"block\";\n      if (\n        node.id === \"coverFrontTitle1\" ||\n        node.id === \"coverBackTitle1\" ||\n        node.id === \"coverSpineTitle1\"\n      ) {\n        coverState.title1 = node.textContent.trim();\n        if (coverTitle1Input) coverTitle1Input.value = coverState.title1;\n      }\n\n      if (\n        node.id === \"coverFrontTitle2\" ||\n        node.id === \"coverBackTitle2\"\n      ) {\n        coverState.title2 = node.textContent.trim();\n        if (coverTitle2Input) coverTitle2Input.value = coverState.title2;\n      }\n      saveCoverTextNodeState(node);\n      savePageTextState(node);\n      saveCoverDesignToStorage();\n      // Persist to the progress-state key that the ?restore=1 refresh flow reads \u2014\n      // IMMEDIATELY, not on a debounce. Text edits used to write only the\n      // cover-specific storage and relied on the debounced autosave / pagehide flush\n      // to reach the restore key, so text typed just before a refresh could vanish\n      // while the background colour (saved earlier) survived. A per-keystroke ~8 KB\n      // localStorage write is cheap.\n      try { _dmbWriteProgressState(); } catch (e) {}\n      try { if (typeof _dmbCloudPersist === \"function\") _dmbCloudPersist(); } catch (e) {}\n      showTextToolbarFor(node);\n    });\n\n    makeTextInteractive(node);\n  }\n\n  function wireTextToolbar() {\n    if (wireTextToolbar._done) return;\n    wireTextToolbar._done = true;\n\n    ensureRotateHandle();\n\n    const fontFamily = document.getElementById(\"dmbFontFamily\");\n    const fontSize = document.getElementById(\"dmbFontSize\");\n    const textColor = document.getElementById(\"dmbTextColor\");\n    const underlineBtn = document.getElementById(\"dmbUnderlineBtn\");\n    const textAlign = document.getElementById(\"dmbTextAlign\");\n    const duplicateBtn = document.getElementById(\"dmbDuplicateTextBtn\");\n    const deleteBtn = document.getElementById(\"dmbDeleteTextBtn\");\n    const applyAllBtn = document.getElementById(\"dmbApplyAllTextBtn\");\n    const textFrontBtn = document.getElementById(\"dmbTextFrontBtn\");\n    const textBackBtn = document.getElementById(\"dmbTextBackBtn\");\n    const textPosLeft = document.getElementById(\"dmbTextPosLeft\");\n    const textPosCenter = document.getElementById(\"dmbTextPosCenter\");\n    const textPosRight = document.getElementById(\"dmbTextPosRight\");\n\n    // \u2500\u2500 Format painter \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // Select a text box, click the brush, then click another box to give it the first\n    // one's look.\n    //\n    // It copies ONLY the properties that are actually persisted by\n    // saveCoverTextNodeState / savePageTextState. Copying anything beyond that set\n    // would look correct on screen and then silently revert on the next reload or in\n    // the printed PDF, which is worse than not copying it at all. (2026-10-12)\n    const fmtPaintBtn = document.getElementById(\"dmbFormatPaintBtn\");\n    const _FMT_PROPS = [\"color\", \"fontFamily\", \"fontSize\", \"fontWeight\", \"textAlign\", \"textDecoration\"];\n    let _fmtClip = null;\n\n    function _fmtSetArmed(on) {\n      if (!on) _fmtClip = null;\n      try { document.body.classList.toggle(\"dmb-fmt-painting\", !!on); } catch (e) {}\n      if (fmtPaintBtn) fmtPaintBtn.classList.toggle(\"is-active\", !!on);\n      if (on) document.addEventListener(\"pointerdown\", _fmtOnPick, true);\n      else    document.removeEventListener(\"pointerdown\", _fmtOnPick, true);\n    }\n\n    // Capture phase, so the paint lands before the box's own drag/select handlers get\n    // the event and start moving it instead.\n    function _fmtOnPick(e) {\n      const t = (e.target && e.target.closest) ? e.target.closest(\".dmb-editable-text\") : null;\n      if (!t) { _fmtSetArmed(false); return; }   // clicked away: disarm, change nothing\n      e.preventDefault(); e.stopPropagation();\n      if (!_fmtClip) { _fmtSetArmed(false); return; }\n      pushUndoState(\"Copy Formatting\");\n      // Any per-word look on the TARGET is cleared first. \"Make this box look like\n      // that one\" has no meaning while some of its words are still overriding the\n      // answer - the brush would appear to do nothing to them.\n      try { _dmbFlattenRuns(t); } catch (err) {}\n      _FMT_PROPS.forEach(function (p) { if (_fmtClip[p]) t.style[p] = _fmtClip[p]; });\n      try { saveCoverTextNodeState(t); savePageTextState(t); saveCoverDesignToStorage(); } catch (err) {}\n      try { _dmbWriteProgressState(); } catch (err) {}\n      try { if (typeof _dmbCloudPersist === \"function\") _dmbCloudPersist(); } catch (err) {}\n      try { positionTextSelectFrame(t); positionRotateHandle(t); } catch (err) {}\n      _fmtSetArmed(false);                       // one box per click of the brush\n    }\n\n    if (fmtPaintBtn) {\n      fmtPaintBtn.addEventListener(\"click\", function (e) {\n        e.preventDefault(); e.stopPropagation();\n        if (document.body.classList.contains(\"dmb-fmt-painting\")) { _fmtSetArmed(false); return; }\n        if (!selectedTextNode) return;           // nothing to copy from\n        const cs = window.getComputedStyle(selectedTextNode);\n        _fmtClip = {};\n        // Prefer the inline value (what gets saved); fall back to computed so a box\n        // styled only by the stylesheet still copies something sensible.\n        _FMT_PROPS.forEach(function (p) {\n          _fmtClip[p] = selectedTextNode.style[p] || (cs && cs[p]) || \"\";\n        });\n        _fmtSetArmed(true);\n      });\n    }\n    document.addEventListener(\"keydown\", function (e) {\n      if (e.key === \"Escape\") _fmtSetArmed(false);\n    });\n    function applyToSelected(fn) {\n      if (!selectedTextNode) return;\n      const node = selectedTextNode;\n      // One undo snapshot per burst (captures the PRE-edit state).\n      _dmbTextStyleBeginUndo();\n      fn(node);                                     // visual change \u2014 applied + painted now\n      try { showTextToolbarFor(node); } catch (e) {}  // light (same selection): just reposition\n      _dmbTextStyleScheduleCommit(node);            // heavy saves debounced, off the hot path\n      // Only re-focus the text node if the user is NOT currently interacting with\n      // a control that owns focus. Re-focusing would steal focus back to the text:\n      //  \u2022 a toolbar control (e.g. the font-size input) \u2014 every digit after the\n      //    first would land in the text content instead of the input;\n      //  \u2022 a colour picker / the mobile colour sheet \u2014 stealing focus from an open\n      //    <input type=color> DISMISSES the native picker mid-edit (text-only bug:\n      //    shapes/maps don't re-focus anything, so their picker stayed open).\n      const _active = document.activeElement;\n      const _ownsFocus = _active && (\n        _active.closest(\"#dmbTextToolbar\") ||\n        _active.closest(\"#dmbMshColorSheet\") ||\n        (_active.tagName === \"INPUT\" && _active.type === \"color\")\n      );\n      if (!_ownsFocus) {\n        try { node.focus(); } catch (e) {}\n      }\n    }\n\n    if (fontFamily) {\n      fontFamily.addEventListener(\"change\", () => {\n        // Words selected \u2192 only those words. Nothing selected \u2192 the whole box,\n        // exactly as before.\n        if (_dmbApplyToTextSelection({ fontFamily: fontFamily.value }, \"Font\")) return;\n        applyToSelected((node) => {\n          _dmbStripRunProp(node, \"font-family\");   // whole box \u2192 override any per-word runs\n          node.style.fontFamily = fontFamily.value;\n        });\n      });\n    }\n\n    if (fontSize) {\n      const applyFontSize = () => {\n        const v = Number(fontSize.value);\n        if (!Number.isFinite(v) || v <= 0) return;\n\n        if (_dmbApplyToTextSelection({ fontSize: v + \"px\" }, \"Font Size\")) return;\n        applyToSelected((node) => {\n          _dmbStripRunProp(node, \"font-size\");   // whole box \u2192 override any per-word runs\n          node.style.fontSize = `${v}px`;\n        });\n      };\n\n      fontSize.addEventListener(\"input\", applyFontSize);\n      fontSize.addEventListener(\"change\", applyFontSize);\n    }\n\n    if (textColor) {\n      textColor.addEventListener(\"input\", () => {\n        if (_dmbApplyToTextSelection({ color: textColor.value }, \"Text Colour\")) {\n          try { _dmbSyncTextColorDot(); } catch (e) {}\n          return;\n        }\n        applyToSelected((node) => {\n          _dmbStripRunProp(node, \"color\");   // whole box \u2192 override any per-word runs\n          node.style.color = textColor.value;\n        });\n        try { _dmbSyncTextColorDot(); } catch (e) {}\n      });\n      // Recently-used colours now live INSIDE the colour-wheel popup.\n      try { dmbEnhanceColorInput(textColor, document.getElementById(\"dmbTextColorPop\")); } catch (e) {}\n      // Keep the custom-colour button last (the swatch strip is appended after it).\n      try {\n        const _cc = document.getElementById(\"dmbTextColorCustom\");\n        const _cp = document.getElementById(\"dmbTextColorPop\");\n        if (_cc && _cp) _cp.appendChild(_cc);\n        _dmbColorTapFix(_cc, textColor);\n      } catch (e) {}\n      try { _dmbSyncTextColorDot(); } catch (e) {}\n    }\n\n    // Colour-wheel button \u2192 toggle the palette popup.\n    const colorBtn = document.getElementById(\"dmbTextColorBtn\");\n    const colorPop = document.getElementById(\"dmbTextColorPop\");\n    const colorCustom = document.getElementById(\"dmbTextColorCustom\");\n    if (colorBtn && colorPop) {\n      colorBtn.addEventListener(\"click\", (e) => {\n        e.preventDefault(); e.stopPropagation();\n        const willOpen = colorPop.hidden;\n        _dmbCloseTextMenus(colorPop);\n        colorPop.hidden = !willOpen;\n      });\n      // Picking a recent swatch closes the popup.\n      colorPop.addEventListener(\"click\", (e) => {\n        if (e.target.closest(\".dmb-recent-swatch\")) colorPop.hidden = true;\n      });\n    }\n    if (colorCustom && textColor) {\n      colorCustom.addEventListener(\"click\", (e) => {\n        e.stopPropagation();                       // identical to the background wheel \u2014 NO preventDefault\n        if (e.target === textColor) return;        // overlay input tapped \u2192 let it open the native picker\n        try { textColor.click(); } catch (e2) {}   // desktop fallback: opens the OS colour picker\n      });\n    }\n    if (textColor && colorPop) {\n      textColor.addEventListener(\"change\", () => { colorPop.hidden = true; });\n    }\n\n    // Alignment icon dropdown \u2192 drives the hidden <select> (reuses its handler).\n    const alignToggle = document.getElementById(\"dmbTextAlignToggle\");\n    const alignMenu = document.getElementById(\"dmbTextAlignMenu\");\n    if (alignToggle && alignMenu) {\n      alignToggle.addEventListener(\"click\", (e) => {\n        e.preventDefault(); e.stopPropagation();\n        const willOpen = alignMenu.hidden;\n        _dmbCloseTextMenus(alignMenu);\n        alignMenu.hidden = !willOpen;\n      });\n      alignMenu.querySelectorAll(\".dmb-align-opt\").forEach((opt) => {\n        opt.addEventListener(\"click\", (e) => {\n          e.preventDefault(); e.stopPropagation();\n          if (textAlign) {\n            textAlign.value = opt.getAttribute(\"data-align\");\n            textAlign.dispatchEvent(new Event(\"change\", { bubbles: true }));\n          }\n          try { _dmbSyncAlignIcon(); } catch (e2) {}\n          alignMenu.hidden = true;\n        });\n      });\n    }\n    try { _dmbSyncAlignIcon(); } catch (e) {}\n\n    // \"More\" dropdown: on-page position (\u2190/\u2194/\u2192) + Duplicate.\n    const moreToggle = document.getElementById(\"dmbTextMoreToggle\");\n    const moreMenu = document.getElementById(\"dmbTextMoreMenu\");\n    if (moreToggle && moreMenu) {\n      moreToggle.addEventListener(\"click\", (e) => {\n        e.preventDefault(); e.stopPropagation();\n        const willOpen = moreMenu.hidden;\n        _dmbCloseTextMenus(moreMenu);\n        moreMenu.hidden = !willOpen;\n      });\n      moreMenu.addEventListener(\"click\", (e) => {\n        if (e.target.closest(\".dmb-text-more-item\")) moreMenu.hidden = true;\n      });\n    }\n\n    // Outside-click closes every text-toolbar popup (bound once).\n    if (!wireTextToolbar._popClose) {\n      wireTextToolbar._popClose = true;\n      document.addEventListener(\"click\", (e) => {\n        if (!e.target.closest(\"#dmbTextColorWrap\")) { const p = document.getElementById(\"dmbTextColorPop\"); if (p) p.hidden = true; }\n        if (!e.target.closest(\"#dmbTextAlignDD\")) { const p = document.getElementById(\"dmbTextAlignMenu\"); if (p) p.hidden = true; }\n        if (!e.target.closest(\"#dmbTextMoreDD\")) { const p = document.getElementById(\"dmbTextMoreMenu\"); if (p) p.hidden = true; }\n      });\n    }\n\n    if (underlineBtn) {\n      underlineBtn.addEventListener(\"click\", () => {\n        // On a selection the toggle has to read the SELECTION's state, not the\n        // box's, or underlining three words would report back the wrong answer\n        // and the next click would underline them a second time.\n        if (selectedTextNode && _dmbActiveTextRange(selectedTextNode)) {\n          const _off = _dmbSelUnderlined(selectedTextNode);\n          if (_dmbApplyToTextSelection({ textDecoration: _off ? \"none\" : \"underline\" }, \"Underline\")) return;\n        }\n        applyToSelected((node) => {\n          node.style.textDecoration =\n            node.style.textDecoration === \"underline\" ? \"none\" : \"underline\";\n        });\n      });\n    }\n\n    if (textAlign) {\n      textAlign.addEventListener(\"change\", () => {\n        applyToSelected((node) => {\n          node.style.textAlign = textAlign.value;\n          node.style.width = \"220px\";\n        });\n      });\n    }\n    if (textFrontBtn) {\n      textFrontBtn.addEventListener(\"click\", () => {\n        _dmbDoLayerStep(selectedTextNode, \"forward\");\n        if (selectedTextNode) showTextToolbarFor(selectedTextNode);\n      });\n    }\n\n    if (textBackBtn) {\n      textBackBtn.addEventListener(\"click\", () => {\n        _dmbDoLayerStep(selectedTextNode, \"backward\");\n        if (selectedTextNode) showTextToolbarFor(selectedTextNode);\n      });\n    }\n\n    if (textPosLeft) {\n      textPosLeft.addEventListener(\"click\", () => {\n        if (selectedTextNode) dmbAlignTextNode(selectedTextNode, \"left\");\n      });\n    }\n    if (textPosCenter) {\n      textPosCenter.addEventListener(\"click\", () => {\n        if (selectedTextNode) dmbAlignTextNode(selectedTextNode, \"center\");\n      });\n    }\n    if (textPosRight) {\n      textPosRight.addEventListener(\"click\", () => {\n        if (selectedTextNode) dmbAlignTextNode(selectedTextNode, \"right\");\n      });\n    }\n\n    if (duplicateBtn) {\n      duplicateBtn.addEventListener(\"click\", () => {\n        if (!selectedTextNode || !selectedTextNode.parentElement) return;\n\n        const clone = selectedTextNode.cloneNode(true);\n        clone.dataset.dmbTextReady = \"\";\n        // Same %-resolution as the drag: duplicating a spine title read top:\"50%\" as\n        // 50 and dropped the copy at 70px - 162px ABOVE the original instead of 20px\n        // below it - then saved that px value permanently. The `|| 40` fallback is\n        // kept verbatim so nothing else about this handler changes.\n        clone.style.left = `${(_dmbLenPx(selectedTextNode, \"left\") || 40) + 20}px`;\n        clone.style.top = `${(_dmbLenPx(selectedTextNode, \"top\") || 40) + 20}px`;\n        clone.dataset.rotate = selectedTextNode.dataset.rotate || \"0\";\n        clone.style.transform = selectedTextNode.style.transform || \"\";\n        clone.dataset.z = String(Date.now());\n        clone.style.zIndex = clone.dataset.z;\n\n        // Register clone in the data model so it survives re-renders\n        const newId = (builderMode === \"cover\" ? \"utext_\" : \"pgtext_\") + Date.now() + \"_\" + Math.random().toString(36).slice(2);\n        clone.id = newId;\n\n        if (builderMode === \"cover\") {\n          const zone = activeCoverZone === \"back\" ? \"back\" : activeCoverZone === \"spine\" ? \"spine\" : \"front\";\n          if (!Array.isArray(editor.coverUserTexts)) editor.coverUserTexts = [];\n          editor.coverUserTexts.push({ id: newId, zone });\n        } else {\n          const pn = parseInt(selectedTextNode.dataset.pageNumber);\n          if (Number.isFinite(pn)) {\n            clone.dataset.pageNumber = String(pn);\n            if (!Array.isArray(editor.pageObjects[pn])) editor.pageObjects[pn] = [];\n            editor.pageObjects[pn].push({\n              id: newId, type: \"text\",\n              text: captureVisualText(clone),\n              whiteSpace: clone.style.whiteSpace || \"\",\n              left: clone.style.left || \"40px\",\n              top: clone.style.top || \"40px\",\n              fontSize: clone.style.fontSize || \"28px\",\n              fontWeight: clone.style.fontWeight || \"800\",\n              color: clone.style.color || \"#666\",\n              fontFamily: clone.style.fontFamily || \"\",\n              textAlign: clone.style.textAlign || \"\",\n              textDecoration: clone.style.textDecoration || \"\",\n              transform: clone.style.transform || \"\",\n              rotate: clone.dataset.rotate || \"0\",\n              z: clone.dataset.z || String(Date.now()),\n              width: clone.style.width || \"\"\n            });\n          }\n        }\n\n        attachTextNodeHandlers(clone);\n        selectedTextNode.parentElement.appendChild(clone);\n        // Save AFTER the append: boxWidth/boxHeight and the captured line breaks can\n        // only be measured on a node that is actually laid out. Saving beforehand\n        // stored zeros and an empty renderedLines, so the PDF re-wrapped the copy\n        // from scratch and a duplicated multi-line caption printed as one long line.\n        if (builderMode === \"cover\") { try { saveCoverTextNodeState(clone); } catch (e) {} }\n        else { try { savePageTextState(clone); } catch (e) {} }\n        enterTextSelectMode(clone);   // duplicate lands SELECTED (solid), arrows move it\n      });\n    }\n\n    if (deleteBtn) {\n      deleteBtn.addEventListener(\"click\", () => {\n        if (!selectedTextNode) return;\n        const node = selectedTextNode;\n        pushUndoState(\"Delete Text\");\n        // Remove from registry so it doesn't reappear on re-render\n        if (node.id) {\n          editor.coverUserTexts = (editor.coverUserTexts || []).filter((e) => e.id !== node.id);\n          if (coverState.textStyles) delete coverState.textStyles[node.id];\n          const _dpn = parseInt(node.dataset.pageNumber);\n          if (Number.isFinite(_dpn) && Array.isArray(editor.pageObjects?.[_dpn])) {\n            editor.pageObjects[_dpn] = editor.pageObjects[_dpn].filter((o) => o.id !== node.id);\n          }\n        }\n        hideTextToolbar();\n        node.remove();\n      });\n    }\n\n    if (applyAllBtn) {\n      applyAllBtn.addEventListener(\"click\", () => {\n        if (!selectedTextNode) return;\n\n        pushUndoState(\"Apply Text Style To All\");\n        const styles = {\n          fontFamily: selectedTextNode.style.fontFamily,\n          fontSize: selectedTextNode.style.fontSize,\n          color: selectedTextNode.style.color,\n          textDecoration: selectedTextNode.style.textDecoration,\n          textAlign: selectedTextNode.style.textAlign,\n          transform: selectedTextNode.style.transform,\n          width: selectedTextNode.style.width,\n          // width can switch a box between wrapping and not; carry the flag with it\n          // or the model and the DOM disagree about how the text breaks.\n          whiteSpace: selectedTextNode.style.whiteSpace\n        };\n\n        document.querySelectorAll(\".dmb-editable-text\").forEach((el) => {\n          Object.assign(el.style, styles);\n          el.dataset.rotate = selectedTextNode.dataset.rotate || \"0\";\n          // Write it through to the MODEL \u2014 the PDF reads nothing else.\n          try { saveCoverTextNodeState(el); } catch (e) {}\n          try { savePageTextState(el); } catch (e) {}\n        });\n        try { saveCoverDesignToStorage(); } catch (e) {}\n        try { _dmbAutoPersist(); } catch (e) {}\n      });\n    }\n\n    document.addEventListener(\"click\", (e) => {\n      const clickedText = e.target.closest(\".dmb-editable-text\");\n      const clickedToolbar = e.target.closest(\"#dmbTextToolbar\");\n      const clickedRibbon = e.target.closest(\".dmb-stage-header\") || e.target.closest(\"#dmbMshRibbon\");   // desktop OR mobile ribbon\n      const clickedRotateHandle = e.target.closest(\"#dmbTextRotateHandle\");\n      const clickedSelFrame = e.target.closest(\"#dmbTextSelFrame\");\n      const clickedColorSheet = e.target.closest(\"#dmbMshColorSheet\");   // mobile body colour sheet \u2014 keep the text selected\n      if (!clickedText && !clickedToolbar && !clickedRibbon && !clickedRotateHandle && !clickedSelFrame && !clickedColorSheet) {\n        // If the selected box is empty, discard it together with its menu so an\n        // abandoned text box never lingers; otherwise just close the menu.\n        const _prev = selectedTextNode;\n        if (_prev && !captureVisualText(_prev).trim()) {\n          _dmbRemoveTextNode(_prev);\n        } else {\n          hideTextToolbar();\n        }\n      }\n    });\n\n    // Deselect a frame object (sticker / photo / mag-icon) when the press lands\n    // anywhere outside it \u2014 the 8 resize dots + floating toolbar + rotate handle\n    // should disappear, matching the text-box behavior above. Covers presses on the\n    // spread background AND anywhere off the spread (gutter, gray area, etc.).\n    // Uses a CAPTURE-phase pointerdown (like the shape edit-bar deselect) because\n    // the sticker/canvas layers call stopPropagation() on click/pointerdown, so a\n    // bubbling click never reaches document.\n    document.addEventListener(\"pointerdown\", (e) => {\n      if (!selectedFrameObject) return;\n      const t = e.target;\n      if (t && t.closest && (\n        t.closest(\".dmb-photo-frame-object\") ||   // on a photo frame\n        t.closest(\".dmb-sticker-object\")     ||   // on a sticker / mag-icon\n        t.closest(\"#dmbFrameToolbar\")        ||   // on the floating toolbar\n        t.closest(\".dmb-stage-header\")       ||   // anywhere in the fixed ribbon\n        t.closest(\"#dmbMshRibbon\")           ||   // mobile ribbon (idle + docked bars)\n        t.closest(\"#dmbMshColorSheet\")       ||   // mobile body colour sheet \u2014 keep the frame selected\n        t.closest(\".dmb-frame-handle\")       ||   // on a resize dot\n        t.closest(\".dmb-map-rotate-handle\")  ||   // on the rotate handle\n        t.closest(\".dmb-map-rot-float\")           // rotate handle (float variant)\n      )) return;\n      hideFrameToolbar();\n    }, { capture: true });\n\n    window.addEventListener(\"resize\", () => {\n      if (selectedTextNode) showTextToolbarFor(selectedTextNode);\n    });\n  }\n\n  function saveProjectLocalWithMessage() {\n    const payload = getCurrentBuilderStateSnapshot();\n    try {\n      localStorage.setItem(_dmbProjKey(), JSON.stringify(payload));\n      if (saveMsg) {\n        saveMsg.textContent = \"Saved \u2705\";\n        saveMsg.style.display = \"block\";\n        setTimeout(() => { saveMsg.style.display = \"none\"; }, 1400);\n      }\n    } catch {\n      alert(\"Could not save on this device.\");\n    }\n  }\n\n  function openSavedProjectLocal() {\n    const raw = loadProjectLocal();\n    if (!raw) {\n      alert(\"No saved project found.\");\n      return;\n    }\n    applyBuilderStateSnapshot(raw);\n  }\n\n  function startNewProjectLocal() {\n    const ok = window.confirm(\"Start a new project? Unsaved changes may be lost.\");\n    if (!ok) return;\n\n    try {\n      localStorage.removeItem(_dmbProjKey());\n    } catch {}\n\n    window.location.href = window.DMB_CARD_MODE\n      ? \"/pages/design-my-book?type=card&step=2&mode=customize\"\n      : window.DMB_MAGAZINE_MODE\n      ? \"/pages/design-my-magazine?step=2&mode=customize\"\n      : \"/pages/design-my-book?step=2&mode=customize\";\n  }\n\n  function openProjectMenuPrompt() {\n    const choice = window.prompt(\"Type one: save / open / new\", \"save\");\n    if (!choice) return;\n\n    const v = choice.trim().toLowerCase();\n\n    if (v === \"save\") {\n      saveProjectLocalWithMessage();\n    } else if (v === \"open\") {\n      openSavedProjectLocal();\n    } else if (v === \"new\") {\n      startNewProjectLocal();\n    }\n  }\n\n  function openHistoryPanel() {\n    if (!undoStack.length) {\n      alert(\"No history yet.\");\n      return;\n    }\n\n    const lines = undoStack\n      .map((s, i) => `${i + 1}. ${s.label || \"Edited\"}`)\n      .join(\"\\n\");\n\n    const pick = window.prompt(`History:\\n\\n${lines}\\n\\nType a number to restore that step:`, \"\");\n    if (!pick) return;\n\n    const idx = Number(pick);\n    if (!Number.isInteger(idx) || idx < 1 || idx > undoStack.length) return;\n\n    const target = undoStack[idx - 1];\n    if (!target) return;\n\n    editor.currentSpread = target.currentSpread;\n    editor.pageSlots = cloneState(target.pageSlots);\n    editor.pageRot = cloneState(target.pageRot);\n    editor.pageTemplate = cloneState(target.pageTemplate);\n    editor.pageFrames = cloneState(target.pageFrames);\n    editor.pageFrameRot = cloneState(target.pageFrameRot);\n    builderMode = target.builderMode || \"pages\";\n    editor.pageObjects = cloneState(target.pageObjects || {});\n    editor.coverObjects = cloneState(target.coverObjects || []);\n    if (!Array.isArray(editor.coverObjects)) migrateCoverObjectsToFlat();\n    coverState.textStyles = cloneState(target.coverState?.textStyles || {});\n    coverState.template = target.coverState?.template || \"default\";\n    coverState.title1 = target.coverState?.title1 || \"\";\n    coverState.title2 = target.coverState?.title2 || \"\";\n    coverState.color = target.coverState?.color || \"#ffffff\";\n\n    syncCoverInputsFromState();\n    renderPhotoTray();\n    renderThumbsRail();\n    renderBuilderCanvas();\n  }\n\n  function ensurePreviewOverlay() {\n    if (document.getElementById(\"dmbPreviewOverlay\")) return;\n\n    const overlay = document.createElement(\"div\");\n    overlay.id = \"dmbPreviewOverlay\";\n    overlay.style.position = \"fixed\";\n    overlay.style.inset = \"0\";\n    overlay.style.background = \"rgba(0,0,0,0.82)\";\n    overlay.style.zIndex = \"99999\";\n    overlay.style.display = \"none\";\n    overlay.style.alignItems = \"center\";\n    overlay.style.justifyContent = \"center\";\n    overlay.style.padding = \"24px\";\n\n    const panel = document.createElement(\"div\");\n    panel.id = \"dmbPreviewPanel\";\n    panel.style.width = \"min(1200px, 96vw)\";\n    panel.style.height = \"min(92vh, 920px)\";\n    panel.style.background = \"#1a1a1a\";\n    panel.style.borderRadius = \"20px\";\n    panel.style.display = \"flex\";\n    panel.style.flexDirection = \"column\";\n    panel.style.overflow = \"hidden\";\n\n    const head = document.createElement(\"div\");\n    head.id = \"dmbPreviewHead\";\n    head.style.cssText = \"display:flex;justify-content:space-between;align-items:center;padding:14px 20px;border-bottom:1px solid #333;flex-shrink:0;\";\n\n    const titleWrap = document.createElement(\"div\");\n    titleWrap.id = \"dmbPreviewTitleWrap\";\n    titleWrap.style.cssText = \"display:flex;align-items:center;gap:16px;\";\n\n    const titleEl = document.createElement(\"div\");\n    titleEl.id = \"dmbPreviewTitle\";\n    titleEl.textContent = \"Preview\";\n    titleEl.style.cssText = \"font-weight:900;font-size:16px;color:#fff;\";\n\n    const navWrap = document.createElement(\"div\");\n    navWrap.id = \"dmbPreviewNavWrap\";\n    navWrap.style.cssText = \"display:flex;align-items:center;gap:10px;\";\n\n    const prevBtn = document.createElement(\"button\");\n    prevBtn.id = \"dmbPreviewPrev\";\n    prevBtn.type = \"button\";\n    prevBtn.textContent = \"\u25C0 Prev\";\n    prevBtn.style.cssText = \"padding:7px 14px;border-radius:8px;border:1px solid #555;background:#2a2a2a;color:#fff;font-weight:700;font-size:13px;cursor:pointer;\";\n\n    const spreadLabel = document.createElement(\"div\");\n    spreadLabel.id = \"dmbPreviewSpreadLabel\";\n    spreadLabel.style.cssText = \"font-size:12px;color:#aaa;min-width:80px;text-align:center;\";\n\n    const nextBtn = document.createElement(\"button\");\n    nextBtn.id = \"dmbPreviewNext\";\n    nextBtn.type = \"button\";\n    nextBtn.textContent = \"Next \u25B6\";\n    nextBtn.style.cssText = \"padding:7px 14px;border-radius:8px;border:1px solid #555;background:#2a2a2a;color:#fff;font-weight:700;font-size:13px;cursor:pointer;\";\n\n    const closeBtn = document.createElement(\"button\");\n    closeBtn.id = \"dmbPreviewClose\";\n    closeBtn.type = \"button\";\n    closeBtn.style.cssText = \"padding:7px 16px;border-radius:8px;border:1px solid #555;background:#333;color:#fff;font-weight:700;font-size:13px;cursor:pointer;\";\n    closeBtn.textContent = \"Close\";\n\n    const body = document.createElement(\"div\");\n    body.id = \"dmbPreviewBody\";\n    body.style.cssText = \"flex:1 1 auto;overflow:hidden;display:flex;align-items:center;justify-content:center;padding:24px;background:#111;\";\n\n    closeBtn.addEventListener(\"click\", () => { overlay.style.display = \"none\"; });\n\n    // \u2500\u2500 Confirm banner (shown only in confirm-mode) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    const confirmBanner = document.createElement(\"div\");\n    confirmBanner.id = \"dmbPreviewConfirmBanner\";\n    confirmBanner.style.cssText = \"display:none;flex-shrink:0;background:#1a1a1a;border-top:1px solid #333;padding:16px 24px;display:none;flex-direction:column;align-items:center;gap:12px;\";\n\n    const confirmMsg = document.createElement(\"div\");\n    confirmMsg.style.cssText = \"color:#ffd700;font-size:14px;font-weight:700;text-align:center;\";\n    confirmMsg.textContent = \"\uD83D\uDCCB Review every page carefully using the arrows above \u2014 cover, special page, and all spreads. When everything looks perfect, confirm below.\";\n\n    const confirmBtn = document.createElement(\"button\");\n    confirmBtn.id = \"dmbPreviewConfirmBtn\";\n    confirmBtn.type = \"button\";\n    confirmBtn.textContent = \"\u2705 Looks perfect \u2014 Confirm & Place Order\";\n    confirmBtn.style.cssText = \"background:#22c55e;color:#fff;border:none;border-radius:10px;padding:14px 32px;font-size:15px;font-weight:900;cursor:pointer;letter-spacing:0.3px;\";\n    confirmBtn.addEventListener(\"mouseover\", () => { confirmBtn.style.background = \"#16a34a\"; });\n    confirmBtn.addEventListener(\"mouseout\",  () => { confirmBtn.style.background = \"#22c55e\"; });\n\n    confirmBanner.appendChild(confirmMsg);\n    confirmBanner.appendChild(confirmBtn);\n\n    navWrap.appendChild(prevBtn);\n    navWrap.appendChild(spreadLabel);\n    navWrap.appendChild(nextBtn);\n    titleWrap.appendChild(titleEl);\n    titleWrap.appendChild(navWrap);\n    head.appendChild(titleWrap);\n    head.appendChild(closeBtn);\n    panel.appendChild(head);\n    panel.appendChild(body);\n    panel.appendChild(confirmBanner);\n    overlay.appendChild(panel);\n    document.body.appendChild(overlay);\n\n    // \u2500\u2500 Mobile layout overrides (applied inline \u2014 more reliable than CSS on dynamic elements) \u2500\u2500\n    if (window.innerWidth <= 749) {\n      panel.style.width         = \"100vw\";\n      panel.style.height        = \"100dvh\";\n      panel.style.borderRadius  = \"0\";\n      panel.style.maxHeight     = \"100dvh\";\n\n      head.style.position       = \"relative\";\n      head.style.flexDirection  = \"column\";\n      head.style.alignItems     = \"flex-start\";\n      head.style.padding        = \"10px 14px\";\n      head.style.gap            = \"6px\";\n\n      titleWrap.style.flexDirection = \"column\";\n      titleWrap.style.alignItems    = \"flex-start\";\n      titleWrap.style.gap           = \"6px\";\n      titleWrap.style.width         = \"100%\";\n      titleWrap.style.paddingRight  = \"80px\"; // space for close button\n\n      titleEl.style.fontSize    = \"14px\";\n      titleEl.style.whiteSpace  = \"nowrap\";\n      titleEl.style.overflow    = \"hidden\";\n      titleEl.style.textOverflow = \"ellipsis\";\n      titleEl.style.maxWidth    = \"100%\";\n\n      closeBtn.style.position   = \"absolute\";\n      closeBtn.style.top        = \"10px\";\n      closeBtn.style.right      = \"14px\";\n      closeBtn.style.padding    = \"8px 16px\";\n      closeBtn.style.fontSize   = \"14px\";\n\n      navWrap.style.gap         = \"8px\";\n\n      body.style.padding        = \"10px\";\n    }\n\n    // Keyboard navigation\n    overlay.addEventListener(\"keydown\", (e) => {\n      if (e.key === \"ArrowLeft\") { e.preventDefault(); prevBtn.click(); }\n      if (e.key === \"ArrowRight\") { e.preventDefault(); nextBtn.click(); }\n      if (e.key === \"Escape\") { overlay.style.display = \"none\"; }\n    });\n    overlay.setAttribute(\"tabindex\", \"0\");\n  }\n\n  function getSelectedTextNode() {\n    return selectedTextNode || null;\n  }\n\n  function bringSelectedTextToFront() {\n    const node = getSelectedTextNode();\n    if (!node) return;\n    const z = String(Date.now());\n    node.dataset.z = z;\n    node.style.zIndex = z;\n    showTextToolbarFor(node);\n  }\n\n  function sendSelectedTextToBack() {\n    const node = getSelectedTextNode();\n    if (!node) return;\n\n    const all = Array.from(document.querySelectorAll(\".dmb-editable-text, .dmb-photo-frame-object, .dmb-map-object\"));\n    let minZ = 1;\n\n    all.forEach((el) => {\n      if (el === node) return;\n      const z = Number(el.dataset.z || el.style.zIndex || 1);\n      if (!Number.isNaN(z)) minZ = Math.min(minZ, z);\n    });\n\n    const next = String(minZ - 1);\n    node.dataset.z = next;\n    node.style.zIndex = next;\n    showTextToolbarFor(node);\n  }\n\n  function buildPreviewPageNode(pageNumber) {\n    if (pageNumber === 0) {\n      const coverWrap = document.createElement(\"div\");\n      // Carry the landscape context so the CLONED faces render wide/short exactly\n      // like the builder (otherwise they'd revert to the portrait aspect).\n      coverWrap.className = \"dmb-cover-preview\" + (dmbIsLandscape() ? \" dmb-cover-landscape\" : \"\");\n      coverWrap.style.padding = \"0\";\n\n      let back = $(\"coverBack\")?.cloneNode(true);\n      let spine = window.DMB_MAGAZINE_MODE ? null : ($(\"coverSpine\")?.cloneNode(true) ?? null);\n      let front = $(\"coverFront\")?.cloneNode(true);\n\n      if (!back || (!window.DMB_MAGAZINE_MODE && !spine) || !front) {\n        // Cover not currently in DOM \u2014 temporarily render it to capture full fidelity\n        // (stickers, texts, maps, logo) then restore the previous view.\n        const savedMode = builderMode;\n        builderMode = \"cover\";\n        renderCoverCanvas();\n        back = $(\"coverBack\")?.cloneNode(true);\n        spine = window.DMB_MAGAZINE_MODE ? null : ($(\"coverSpine\")?.cloneNode(true) ?? null);\n        front = $(\"coverFront\")?.cloneNode(true);\n        const _overlayTmp = $(\"coverOverlay\")?.cloneNode(true);\n        // Restore previous view\n        builderMode = savedMode;\n        if (savedMode !== \"cover\") renderSpread();\n        // Build the preview wrapper from the temporary render and return early\n        [back, spine, front].filter(Boolean).forEach(el => {\n          el.style.cursor = \"default\";\n          el.style.pointerEvents = \"none\";\n          el.querySelectorAll('[contenteditable]').forEach(n => n.setAttribute('contenteditable', 'false'));\n        });\n        if (back) coverWrap.appendChild(back);\n        if (spine) coverWrap.appendChild(spine);\n        if (front) coverWrap.appendChild(front);\n        if (_overlayTmp) {\n          _overlayTmp.style.pointerEvents = \"none\";\n          _overlayTmp.style.zIndex = \"9999\";\n          _overlayTmp.querySelectorAll('[contenteditable]').forEach(n => n.setAttribute('contenteditable', 'false'));\n          coverWrap.appendChild(_overlayTmp);\n        }\n        return coverWrap;\n      }\n\n      [back, spine, front].filter(Boolean).forEach(el => {\n        el.style.cursor = \"default\";\n        el.style.pointerEvents = \"none\";\n        el.querySelectorAll('[contenteditable]').forEach(n => n.setAttribute('contenteditable', 'false'));\n      });\n\n      coverWrap.appendChild(back);\n      if (spine) coverWrap.appendChild(spine);\n      coverWrap.appendChild(front);\n      // Include the overlay so stickers, objects, and logo appear in preview\n      const _previewOverlay = $(\"coverOverlay\")?.cloneNode(true);\n      if (_previewOverlay) {\n        _previewOverlay.style.pointerEvents = \"none\";\n        _previewOverlay.style.zIndex = \"9999\";\n        _previewOverlay.querySelectorAll('[contenteditable]').forEach(n => n.setAttribute('contenteditable', 'false'));\n        coverWrap.appendChild(_previewOverlay);\n      }\n      return coverWrap;\n    }\n\n    return makeA4Page(pageNumber, pageNumber % 2 === 0 ? \"R\" : \"L\", true);\n  }\n\n  function openPreviewOverlay(confirmMode) {\n    ensurePreviewOverlay();\n    const overlay     = document.getElementById(\"dmbPreviewOverlay\");\n    const body        = document.getElementById(\"dmbPreviewBody\");\n    const prevBtn     = document.getElementById(\"dmbPreviewPrev\");\n    const nextBtn     = document.getElementById(\"dmbPreviewNext\");\n    const spreadLabel = document.getElementById(\"dmbPreviewSpreadLabel\");\n    const titleEl     = document.getElementById(\"dmbPreviewTitle\");\n    const confirmBanner = document.getElementById(\"dmbPreviewConfirmBanner\");\n    const confirmBtn    = document.getElementById(\"dmbPreviewConfirmBtn\");\n    if (!overlay || !body) return;\n\n    // \u2500\u2500 Confirm banner \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    if (confirmBanner) {\n      if (confirmMode) {\n        confirmBanner.style.display = \"flex\";\n        // Show a \"please review\" message for 4.5s before revealing the confirm button\n        if (confirmBtn) confirmBtn.style.display = \"none\";\n        const _reviewMsg = document.getElementById(\"dmbPreviewReviewMsg\") || document.createElement(\"div\");\n        _reviewMsg.id = \"dmbPreviewReviewMsg\";\n        _reviewMsg.className = \"dmb-pv-review-msg\";\n        _reviewMsg.innerHTML = \"\u2728 <strong>Almost there!</strong> Flip through every page carefully using the arrows \u2014 what you see is <em>exactly</em> what we'll print and deliver to you. Take your time! \uD83D\uDCD6\";\n        _reviewMsg.style.opacity = \"1\";\n        if (!_reviewMsg.parentNode) confirmBanner.insertBefore(_reviewMsg, confirmBtn);\n        setTimeout(function() {\n          _reviewMsg.style.opacity = \"0\";\n          setTimeout(function() {\n            if (_reviewMsg.parentNode) _reviewMsg.remove();\n            if (confirmBtn) confirmBtn.style.display = \"\";\n          }, 600);\n        }, 4500);\n      } else {\n        confirmBanner.style.display = \"none\";\n      }\n    }\n\n    // Wire confirm button to the actual cart flow (only once)\n    if (confirmMode && confirmBtn && !confirmBtn.dataset.wired) {\n      confirmBtn.dataset.wired = \"1\";\n      confirmBtn.addEventListener(\"click\", () => {\n        overlay.style.display = \"none\";\n        _dmbPvRefresh = null;\n        if (previewBtn) previewBtn.classList.remove(\"dmb-preview-needs-check\");\n        addDesignedBookToCart();\n      });\n    }\n\n    // Preview opened \u2014 remove the \"needs check\" pulse from preview button\n    if (previewBtn) previewBtn.classList.remove(\"dmb-preview-needs-check\");\n\n    // Hide all floating rotation handles \u2014 they live on document.body and would\n    // otherwise show on top of the preview overlay.\n    document.querySelectorAll(\".dmb-map-rot-float\").forEach((el) => { el.style.display = \"none\"; });\n\n    // FIX 1 \u2014 flush any pending re-renders immediately (don't wait for the 2s\n    // debounce).  This eliminates the staleness window: even if the customer\n    // opens Preview within milliseconds of their last edit, the affected page(s)\n    // start re-rendering right now and show the loading spinner until done,\n    // rather than silently displaying the pre-edit cached image.\n    clearTimeout(_dmbPvTimer);\n    if (_dmbPvDirty.size > 0) _dmbPvRun();\n\n    // Build spreads list: cover \u2192 page 0 \u2192 page pairs\n    const totalSpreads = editor.totalSpreads || 1;\n    const spreads = [{ isCover: true }, { isPage0: true }];\n    for (let s = 0; s < totalSpreads; s++) {\n      const left = s * 2 + 1;\n      const right = s * 2 + 2;\n      spreads.push({ isCover: false, isPage0: false, left, right });\n    }\n\n    let currentIdx = 0;\n\n    // \u2500\u2500 ONE scale for every view \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // Every view used to fit ITSELF: each image was sized by HEIGHT only, so a lone\n    // page 0 filled the height while a two-page spread had to shrink to fit the\n    // width, and the three cover faces (back + spine + front = 428 mm) simply\n    // overflowed a phone and were clipped - which is why the spine and the right\n    // edge of the front cover went missing. Pick the mm->px factor ONCE from the\n    // WIDEST view so every sheet is drawn at the same size in every view and the\n    // widest one still fits.\n    const _pvLS   = (typeof dmbIsLandscape === \"function\") ? dmbIsLandscape() : false;\n    const _pvPW   = _pvLS ? 297 : 210;      // page / cover-face width  (mm)\n    const _pvPH   = _pvLS ? 210 : 297;      // page / cover height      (mm)\n    const _pvSPW  = 8;                      // spine thickness          (mm)\n    const _pvMag  = !!window.DMB_MAGAZINE_MODE;\n    const _pvWidest = Math.max(_pvPW * 2 + (_pvMag ? 0 : _pvSPW), _pvPW * 2);\n    const _PV_LABEL_H = 20;                 // reserved under every sheet, in all views\n    function _pvScale() {\n      let bw = 0, bh = 0;\n      try {\n        const cs = getComputedStyle(body);\n        bw = body.clientWidth  - (parseFloat(cs.paddingLeft) || 0) - (parseFloat(cs.paddingRight)  || 0);\n        bh = body.clientHeight - (parseFloat(cs.paddingTop)  || 0) - (parseFloat(cs.paddingBottom) || 0);\n      } catch (e) {}\n      // A body that is not laid out yet (hidden, or measured in the same tick it\n      // was shown) reports 0. Never return 0 - that draws every sheet at zero\n      // width - fall back to the window box and ask for one re-fit next frame.\n      if (!(bw > 0) || !(bh > 0)) {\n        _pvNeedRefit = true;\n        if (!(bw > 0)) bw = Math.max(120, (window.innerWidth  || 360) - 48);\n        if (!(bh > 0)) bh = Math.max(120, (window.innerHeight || 640) - 230);\n      }\n      return Math.max(0.05, Math.min((bh - _PV_LABEL_H) / _pvPH, bw / _pvWidest));\n    }\n    let _pvNeedRefit = false, _pvRefitTries = 0;\n\n    // \u2500\u2500 Helper: create a page image from the PDF cache, or a loading spinner \u2500\u2500\n    // The sheet is given an EXPLICIT px size from _pvScale(), so a page is the same\n    // size whether it is shown alone (page 0) or beside its partner in a spread.\n    function _pvPageNode(pn, k, wMm) {\n      const hPx = _pvPH * k, wPx = (wMm || _pvPW) * k;\n      const slot = document.createElement(\"div\");\n      slot.style.cssText = \"flex:0 0 auto;min-width:0;display:flex;flex-direction:column;\" +\n        \"align-items:center;justify-content:center;overflow:hidden;width:\" + wPx.toFixed(2) + \"px;\";\n      // A page that is dirty or mid-render must NEVER show its stale cached image \u2014\n      // the customer would confirm/order from an out-of-date preview. Show the\n      // emoji loader until the fresh render lands (_dmbPvRefresh re-renders us).\n      const _stale = _dmbPvDirty.has(pn) || _dmbPvRendering.has(pn);\n      const url = _stale ? null : _dmbPvCache[pn];\n      if (url) {\n        const img = document.createElement(\"img\");\n        img.src = url;\n        img.style.cssText = \"display:block;width:\" + wPx.toFixed(2) + \"px;height:\" + hPx.toFixed(2) + \"px;\" +\n          \"box-shadow:0 4px 20px rgba(0,0,0,0.6);border-radius:2px;\";\n        slot.appendChild(img);\n      } else {\n        // Not yet rendered \u2014 schedule if needed and show loading animation\n        if (!_dmbPvRendering.has(pn) && !_dmbPvDirty.has(pn)) _dmbPvMarkDirty(pn);\n        const hold = document.createElement(\"div\");\n        hold.style.cssText = \"width:\" + wPx.toFixed(2) + \"px;height:\" + hPx.toFixed(2) + \"px;\" +\n          \"display:flex;align-items:center;justify-content:center;\";\n        hold.appendChild(_dmbPvMakeLoadingNode());\n        slot.appendChild(hold);\n      }\n      const lbl = document.createElement(\"div\");\n      lbl.style.cssText = \"height:\" + _PV_LABEL_H + \"px;flex:none;\";\n      slot.appendChild(lbl);\n      return slot;\n    }\n\n    function renderSpreadPreview(idx) {\n      body.innerHTML = \"\";\n      const s = spreads[idx];\n\n      if (s.isCover) {\n        // \u2500\u2500 Cover: back face | spine | front face \u2014 proportional PDF images \u2500\u2500\n        titleEl.textContent = \"Preview \u2014 Cover\";\n        const wrap = document.createElement(\"div\");\n        wrap.style.cssText = \"display:flex;flex-wrap:nowrap;gap:0;height:100%;align-items:center;justify-content:center;max-width:100%;\";\n        // Same staleness rule as pages: a dirty/re-rendering cover shows the\n        // loader, never the pre-edit cached faces.\n        const _coverStale = _dmbPvDirty.has(\"cover\") || _dmbPvRendering.has(\"cover\");\n        if (_dmbPvCover && !_coverStale) {\n          const faces = window.DMB_MAGAZINE_MODE\n            // Magazine: back cover | front cover (no spine strip, same left\u2192right as book)\n            ? [\n                { url: _dmbPvCover.back,  label: \"Back\"  },\n                { url: _dmbPvCover.front, label: \"Front\" },\n              ]\n            // Book: back face | spine strip | front face\n            : [\n                { url: _dmbPvCover.back,  label: \"Back\"  },\n                { url: _dmbPvCover.spine, label: \"Spine\" },\n                { url: _dmbPvCover.front, label: \"Front\" },\n              ];\n          const _kC = _pvScale();\n          faces.forEach(function(f) {\n            // The spine is a real 8 mm strip: at the shared scale it is a few px wide,\n            // so its column must be sized from mm like the faces (a height-only fit\n            // pushed the row past the panel and the spine + the front cover's right\n            // edge were clipped away on a phone).\n            const _fw = (f.label === \"Spine\" ? _pvSPW : _pvPW) * _kC;\n            const _fh = _pvPH * _kC;\n            const col = document.createElement(\"div\");\n            col.style.cssText = \"display:flex;flex-direction:column;align-items:center;flex:0 0 auto;\" +\n              \"width:\" + _fw.toFixed(2) + \"px;\";\n            if (f.label === \"Spine\") col.classList.add(\"dmb-pv-spine-col\");\n            const img = document.createElement(\"img\");\n            img.src = f.url;\n            img.style.cssText = \"display:block;width:\" + _fw.toFixed(2) + \"px;height:\" + _fh.toFixed(2) + \"px;\" +\n              \"box-shadow:0 4px 20px rgba(0,0,0,0.6);border-radius:2px;\";\n            const lbl = document.createElement(\"div\");\n            lbl.textContent = f.label;\n            lbl.style.cssText = \"height:\" + _PV_LABEL_H + \"px;line-height:\" + _PV_LABEL_H + \"px;color:#888;\" +\n              \"font-size:10px;letter-spacing:0.6px;text-transform:uppercase;flex:none;white-space:nowrap;\" +\n              \"overflow:visible;text-align:center;\";\n            col.appendChild(img);\n            col.appendChild(lbl);\n            wrap.appendChild(col);\n          });\n        } else {\n          // Cover not rendered yet \u2014 schedule and show spinner\n          if (!_dmbPvRendering.has(\"cover\") && !_dmbPvDirty.has(\"cover\")) _dmbPvMarkDirty(\"cover\");\n          wrap.appendChild(_dmbPvMakeLoadingNode());\n        }\n        body.appendChild(wrap);\n\n      } else if (s.isPage0) {\n        // \u2500\u2500 Special inner-cover page \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n        titleEl.textContent = \"Preview \u2014 Spread 0\";\n        const wrap = document.createElement(\"div\");\n        wrap.style.cssText = \"display:flex;align-items:center;justify-content:center;height:100%;width:100%;\";\n        wrap.appendChild(_pvPageNode(0, _pvScale()));\n        body.appendChild(wrap);\n\n      } else {\n        // \u2500\u2500 Regular spread \u2014 two pages side by side with 5px gap \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n        const left = s.left, right = s.right;\n        titleEl.textContent = \"Preview \u2014 Spread \" + (idx - 1);\n        const wrap = document.createElement(\"div\");\n        wrap.style.cssText = \"display:flex;gap:0;align-items:center;height:100%;width:100%;justify-content:center;\";\n        const _kS = _pvScale();\n        if (left  <= editor.totalPages) wrap.appendChild(_pvPageNode(left,  _kS));\n        if (right <= editor.totalPages) wrap.appendChild(_pvPageNode(right, _kS));\n        body.appendChild(wrap);\n      }\n\n      spreadLabel.textContent = (idx + 1) + \" / \" + spreads.length;\n      if (prevBtn) prevBtn.disabled = idx === 0;\n      if (nextBtn) nextBtn.disabled = idx === spreads.length - 1;\n\n      // The scale had to be guessed because the body was not measurable yet.\n      // Re-render once the browser has laid it out (bounded, so a genuinely\n      // zero-sized panel can never loop).\n      if (_pvNeedRefit && _pvRefitTries < 3) {\n        _pvNeedRefit = false; _pvRefitTries++;\n        requestAnimationFrame(function () { try { renderSpreadPreview(idx); } catch (e) {} });\n      } else {\n        _pvNeedRefit = false;\n      }\n    }\n\n    // The scale is computed from the body box, so a rotate / resize must re-fit.\n    // Replace any listener from a previous open so they cannot accumulate.\n    try { if (overlay._pvResize) window.removeEventListener(\"resize\", overlay._pvResize); } catch (e) {}\n    overlay._pvResize = function () {\n      try { if (overlay.style.display !== \"none\") renderSpreadPreview(currentIdx); } catch (e) {}\n    };\n    window.addEventListener(\"resize\", overlay._pvResize);\n\n    if (prevBtn) {\n      prevBtn.onclick = () => {\n        if (currentIdx > 0) { currentIdx--; renderSpreadPreview(currentIdx); }\n      };\n    }\n    if (nextBtn) {\n      nextBtn.onclick = () => {\n        if (currentIdx < spreads.length - 1) { currentIdx++; renderSpreadPreview(currentIdx); }\n      };\n    }\n\n    // Confirm gating: while any page/cover is still (re)rendering, the order\n    // button is disabled \u2014 the customer can only confirm a preview that is\n    // fully up to date (goes with the stale\u2192loader rule in _pvPageNode).\n    function _pvSyncConfirmState() {\n      if (!confirmBtn) return;\n      const busy = _dmbPvDirty.size > 0 || _dmbPvRendering.size > 0;\n      if (!confirmBtn.dataset.label) confirmBtn.dataset.label = confirmBtn.textContent;\n      confirmBtn.disabled = busy;\n      confirmBtn.style.opacity = busy ? \"0.55\" : \"\";\n      confirmBtn.style.cursor  = busy ? \"wait\" : \"\";\n      confirmBtn.textContent = busy ? \"Rendering your pages\u2026 \u23F3\" : confirmBtn.dataset.label;\n    }\n    _pvSyncConfirmState();\n\n    // When a page finishes rendering in the background, refresh if it's visible\n    _dmbPvRefresh = function(key) {\n      _pvSyncConfirmState();\n      const s = spreads[currentIdx];\n      if (!s) return;\n      let affected = false;\n      if (key === \"cover\" && s.isCover) affected = true;\n      else if (key === 0 && s.isPage0) affected = true;\n      else if (!s.isCover && !s.isPage0 && (key === s.left || key === s.right)) affected = true;\n      if (affected) renderSpreadPreview(currentIdx);\n    };\n\n    // Keyboard: attach to overlay\n    overlay.onkeydown = (e) => {\n      if (e.key === \"ArrowLeft\" || e.key === \"ArrowUp\") { e.preventDefault(); if (currentIdx > 0) { currentIdx--; renderSpreadPreview(currentIdx); } }\n      if (e.key === \"ArrowRight\" || e.key === \"ArrowDown\") { e.preventDefault(); if (currentIdx < spreads.length - 1) { currentIdx++; renderSpreadPreview(currentIdx); } }\n      if (e.key === \"Escape\") { overlay.style.display = \"none\"; _dmbPvRefresh = null; document.querySelectorAll(\".dmb-map-rot-float\").forEach((el) => { el.style.display = \"\"; }); }\n    };\n\n    // Clear live-refresh hook when overlay closes\n    overlay.querySelector && overlay.querySelector(\"[type=button]\"); // noop \u2014 close handled via button below\n    const _closeBtns = overlay.querySelectorAll(\"button\");\n    _closeBtns.forEach(function(b) {\n      if (b.textContent.trim() === \"Close\") {\n        b.onclick = function() { overlay.style.display = \"none\"; _dmbPvRefresh = null; document.querySelectorAll(\".dmb-map-rot-float\").forEach((el) => { el.style.display = \"\"; }); };\n      }\n    });\n\n    // Show FIRST: the sheet scale is measured from the body box, and a\n    // display:none body reports 0x0 - which rendered every sheet at zero width.\n    overlay.style.display = \"flex\";\n    renderSpreadPreview(0);\n    overlay.focus();\n  }\n\n  function syncBuilderModeButtons() {\n    if (coverThumbBtn) {\n      coverThumbBtn.classList.toggle(\"active\", builderMode === \"cover\");\n    }\n    const canvasToolbar = document.querySelector(\".dmb-canvas-toolbar-left\");\n    if (canvasToolbar) canvasToolbar.style.display = (builderMode === \"cover\") ? \"\" : \"none\";\n  }\n\n  function activateLeftTab(key) {\n    document.querySelector(`.dmb-icon-tab[data-tab=\"${key}\"]`)?.click();\n  }\n\n  function openProjectMenuPrompt() {\n    const choice = window.prompt(\"Type one: save / open / new\", \"save\");\n    if (!choice) return;\n\n    const v = choice.trim().toLowerCase();\n\n    if (v === \"save\") {\n      saveProjectLocalWithMessage();\n    } else if (v === \"open\") {\n      openSavedProjectLocal();\n    } else if (v === \"new\") {\n      startNewProjectLocal();\n    }\n  }\n\n  function initCoverZoneButtons() {\n    const frontBtn = $(\"coverZoneFrontBtn\");\n    const backBtn = $(\"coverZoneBackBtn\");\n    const spineBtn = $(\"coverZoneSpineBtn\");\n\n    if (frontBtn) {\n      frontBtn.addEventListener(\"click\", () => {\n        activeCoverZone = \"front\";\n        markActiveCoverZone();\n      });\n    }\n\n    if (backBtn) {\n      backBtn.addEventListener(\"click\", () => {\n        activeCoverZone = \"back\";\n        markActiveCoverZone();\n      });\n    }\n\n    if (spineBtn) {\n      spineBtn.addEventListener(\"click\", () => {\n        activeCoverZone = \"spine\";\n        markActiveCoverZone();\n      });\n    }\n\n    updateCoverZoneButtons();\n  }\n\n  function initProjectMenu() {\n    const btn = $(\"projectMenuBtn\");\n    if (!btn) return;\n\n    btn.onclick = (e) => {\n      e.preventDefault();\n      e.stopPropagation();\n      openProjectMenuPrompt();\n    };\n  }\n\n  function openHistoryPanel() {\n    if (!undoStack.length) {\n      alert(\"No history yet.\");\n      return;\n    }\n\n    const lines = undoStack\n      .map((s, i) => `${i + 1}. ${s.label || \"Edited\"}`)\n      .join(\"\\n\");\n\n    const pick = window.prompt(`History:\\n\\n${lines}\\n\\nType a number to restore that step:`, \"\");\n    if (!pick) return;\n\n    const idx = Number(pick);\n    if (!Number.isInteger(idx) || idx < 1 || idx > undoStack.length) return;\n\n    const target = undoStack[idx - 1];\n    if (!target) return;\n\n    editor.currentSpread = target.currentSpread;\n    editor.pageSlots = cloneState(target.pageSlots);\n    editor.pageRot = cloneState(target.pageRot);\n    editor.pageTemplate = cloneState(target.pageTemplate);\n    editor.pageFrames = cloneState(target.pageFrames);\n    editor.pageFrameRot = cloneState(target.pageFrameRot);\n    builderMode = target.builderMode || \"pages\";\n\n    coverState.template = target.coverState?.template || \"default\";\n    coverState.title1 = target.coverState?.title1 || \"\";\n    coverState.title2 = target.coverState?.title2 || \"\";\n    coverState.color = target.coverState?.color || \"#ffffff\";\n\n    syncCoverInputsFromState();\n    renderPhotoTray();\n    renderThumbsRail();\n    renderBuilderCanvas();\n  }\n\n  function initHistoryButton() {\n    const btn = $(\"historyBtn\");\n    if (!btn) return;\n\n    btn.onclick = (e) => {\n      e.preventDefault();\n      e.stopPropagation();\n      openHistoryPanel();\n    };\n  }\n\n\n  // ---------- cover editor state ----------\n  const coverState = {\n    template: \"default\",\n    title1: \"\",\n    title2: \"\",\n    color: \"#ffffff\",\n    imageUrl: \"\",\n    imageFile: null,\n    textStyles: {},\n    // Photo grid layouts for front/back cover faces\n    frontLayout: null,\n    backLayout: null,\n    frontFrames: {},\n    backFrames: {},\n    frontFrameRot: {},\n    backFrameRot: {},\n    frontImageUrl: \"\",\n    backImageUrl: \"\",\n    frontImageId: \"\",\n    backImageId: \"\",\n    frontImageRot: 0,\n    backImageRot: 0,\n    frontFrameShapes: {},\n    backFrameShapes: {},\n    spineImageUrl: \"\",\n    bleedImageUrl: \"\",\n    bleedImageId: \"\",\n    bleedZone: \"\",\n    bleedImageRot: 0,\n    // Magazine only: set true once the default front-cover free photo frame has been\n    // seeded, so it is never re-added after the customer deletes it.\n    magFrontFrameSeeded: false\n  };\n\n  function getEffectiveBookTitle() {\n    // Use first user-added cover text as book title\n    if (Array.isArray(editor.coverUserTexts) && editor.coverUserTexts.length > 0) {\n      const first = editor.coverUserTexts[0];\n      const txt = (coverState.textStyles?.[first.id]?.text || \"\").trim();\n      if (txt) return txt;\n    }\n    const legacy = (titleEl?.value || \"\").trim();\n    return legacy || \"Untitled\";\n  }\n\n  function saveCoverDesignToStorage() {\n    // Any cover mutation (resize/move/edit/photo/color/\u2026) routes through here, so\n    // invalidate the cached cover PREVIEW/PDF images now. Without this the preview\n    // keeps showing a stale render (e.g. the original template font size) after the\n    // user resizes cover text \u2014 the builder shows the new size but the preview/PDF\n    // shows the old one. Debounced background regen (see _dmbPvMarkDirty).\n    try { _dmbPvMarkDirty(\"cover\"); } catch (e) {}\n\n    const templateKey = getTemplateKey();\n    if (!templateKey) return;\n\n    try {\n      localStorage.setItem(`dmb_cover_design_${templateKey}`, JSON.stringify({\n        template: coverState.template,\n        title1: coverState.title1,\n        title2: coverState.title2,\n        color: coverState.color,\n        textStyles: coverState.textStyles || {},\n        frontLayout: coverState.frontLayout || null,\n        backLayout: coverState.backLayout || null,\n        frontFrames: coverState.frontFrames || {},\n        backFrames: coverState.backFrames || {},\n        frontFrameRot: coverState.frontFrameRot || {},\n        backFrameRot: coverState.backFrameRot || {},\n        frontImageUrl: coverState.frontImageUrl || \"\",\n        backImageUrl: coverState.backImageUrl || \"\",\n        frontImageId: coverState.frontImageId || \"\",\n        backImageId: coverState.backImageId || \"\",\n        frontImageRot: coverState.frontImageRot || 0,\n        backImageRot: coverState.backImageRot || 0,\n        frontFrameShapes: coverState.frontFrameShapes || {},\n        backFrameShapes: coverState.backFrameShapes || {},\n        spineImageUrl: coverState.spineImageUrl || \"\",\n        bleedImageUrl: coverState.bleedImageUrl || \"\",\n        bleedImageId: coverState.bleedImageId || \"\",\n        bleedZone: coverState.bleedZone || \"\",\n        bleedImageRot: coverState.bleedImageRot || 0,\n        magFrontFrameSeeded: !!coverState.magFrontFrameSeeded\n      }));\n    } catch {}\n  }\n\n  function loadCoverDesignFromStorage() {\n    const templateKey = getTemplateKey();\n    if (!templateKey) return null;\n\n    try {\n      const raw = localStorage.getItem(`dmb_cover_design_${templateKey}`);\n      if (!raw) return null;\n      return JSON.parse(raw);\n    } catch {\n      return null;\n    }\n  }\n\n  function applyCoverTemplate(templateId) {\n    coverState.template = templateId || \"default\";\n\n    const frontTitle1 = $(\"coverFrontTitle1\");\n    const frontTitle2 = $(\"coverFrontTitle2\");\n    const spineTitle1 = $(\"coverSpineTitle1\");\n    const backTitle2 = $(\"coverBackTitle2\");\n    const frontImage = $(\"coverFrontImage\");\n\n    if (!frontTitle1 || !frontTitle2 || !spineTitle1 || !backTitle2) return;\n\n    // reset\n    frontTitle1.style.left = \"12%\";\n    frontTitle1.style.top = \"12%\";\n    frontTitle1.style.width = \"76%\";\n    frontTitle1.style.textAlign = \"center\";\n\n    frontTitle2.style.left = \"12%\";\n    frontTitle2.style.top = \"24%\";\n    frontTitle2.style.width = \"76%\";\n    frontTitle2.style.textAlign = \"center\";\n\n    if (frontImage) {\n      frontImage.style.left = \"12%\";\n      frontImage.style.top = \"34%\";\n      frontImage.style.width = \"76%\";\n      frontImage.style.height = \"38%\";\n    }\n\n    spineTitle1.style.left = \"50%\";\n    spineTitle1.style.top = \"50%\";\n    spineTitle1.style.transform = \"translate(-50%,-50%) rotate(-90deg)\";\n\n    backTitle2.style.left = \"12%\";\n    backTitle2.style.top = \"12%\";\n    backTitle2.style.width = \"76%\";\n    backTitle2.style.textAlign = \"center\";\n\n    // future templates go here\n    if (templateId === \"default\") {\n      // already default\n    }\n\n    const items = document.querySelectorAll(\".cover-template-item\");\n    items.forEach((btn) => {\n      const active = btn.getAttribute(\"data-template\") === coverState.template;\n      btn.style.borderColor = active ? \"#111\" : \"#ddd\";\n      btn.style.background = active ? \"#f3f3f3\" : \"#fff\";\n    });\n\n    saveCoverDesignToStorage();\n  }\n\n  function updateCoverPreview() {\n    coverState.title1 = (coverTitle1Input?.value || \"\").trim();\n    coverState.title2 = (coverTitle2Input?.value || \"\").trim();\n    coverState.color = (coverColorInput?.value || \"#ffffff\").trim() || \"#ffffff\";\n\n    if (builderMode === \"cover\") {\n      // Update background color in-place so user-added text/frames are preserved\n      [\"coverBack\", \"coverSpine\", \"coverFront\"].forEach((id) => {\n        const el = document.getElementById(id);\n        if (el) el.style.background = coverState.color;\n      });\n      // Update default title nodes in-place\n      [[\"coverFrontTitle1\", \"coverBackTitle1\", \"coverSpineTitle1\"], coverState.title1,\n       [\"coverFrontTitle2\", \"coverBackTitle2\"], coverState.title2].forEach((item, i, arr) => {\n        if (i % 2 === 0) {\n          const ids = item;\n          const val = arr[i + 1];\n          ids.forEach((id) => {\n            const el = document.getElementById(id);\n            if (el) {\n              el.textContent = val;\n              el.style.display = val ? \"block\" : \"none\";\n            }\n          });\n        }\n      });\n    }\n\n    saveCoverDesignToStorage();\n  }\n\n  function initCoverDesigner() {\n    const saved = loadCoverDesignFromStorage();\n    if (saved) {\n      coverState.template = saved.template || \"default\";\n      coverState.title1 = saved.title1 || \"\";\n      coverState.title2 = saved.title2 || \"\";\n      coverState.color = saved.color || \"#ffffff\";\n      coverState.frontLayout = saved.frontLayout || null;\n      coverState.backLayout = saved.backLayout || null;\n      coverState.frontFrames = saved.frontFrames || {};\n      coverState.backFrames = saved.backFrames || {};\n      coverState.frontFrameRot = saved.frontFrameRot || {};\n      coverState.backFrameRot = saved.backFrameRot || {};\n      coverState.frontImageUrl = saved.frontImageUrl || \"\";\n      coverState.backImageUrl = saved.backImageUrl || \"\";\n      coverState.frontImageId = saved.frontImageId || \"\";\n      coverState.backImageId = saved.backImageId || \"\";\n      coverState.frontImageRot = saved.frontImageRot || 0;\n      coverState.backImageRot = saved.backImageRot || 0;\n      coverState.frontFrameShapes = saved.frontFrameShapes || {};\n      coverState.backFrameShapes = saved.backFrameShapes || {};\n      coverState.spineImageUrl = saved.spineImageUrl || \"\";\n      coverState.bleedImageUrl = saved.bleedImageUrl || \"\";\n      coverState.bleedImageId = saved.bleedImageId || \"\";\n      coverState.bleedZone = saved.bleedZone || \"\";\n      coverState.bleedImageRot = saved.bleedImageRot || 0;\n      coverState.magFrontFrameSeeded = !!saved.magFrontFrameSeeded;\n    }\n\n    if (coverTitle1Input && !coverTitle1Input.value) coverTitle1Input.value = coverState.title1;\n    if (coverTitle2Input && !coverTitle2Input.value) coverTitle2Input.value = coverState.title2;\n    if (coverColorInput) coverColorInput.value = coverState.color;\n\n    // Cover template modal\n    const _ctModal = document.getElementById(\"dmbCoverTemplateModal\");\n    const _ctClose = document.getElementById(\"dmbCoverTemplateModalClose\");\n\n    if (chooseCoverTemplateBtn && _ctModal) {\n      chooseCoverTemplateBtn.addEventListener(\"click\", (e) => {\n        e.preventDefault();\n        _ctModal.style.display = \"flex\";\n      });\n    }\n    if (_ctClose && _ctModal) {\n      _ctClose.addEventListener(\"click\", () => { _ctModal.style.display = \"none\"; });\n    }\n    if (_ctModal) {\n      _ctModal.addEventListener(\"click\", (e) => {\n        if (e.target === _ctModal) _ctModal.style.display = \"none\";\n      });\n    }\n\n    // Delegated (not per-button) so template cards added dynamically from R2 work\n    // too. Clicking a card now loads that template's REAL design (R2-first, asset\n    // fallback) \u2014 not just the legacy default-node reposition. Because applying a\n    // template REPLACES the cover, we confirm first when there's cover work to lose.\n    const _ctGrid = document.getElementById(\"coverTemplateList\");\n    if (_ctGrid) {\n      _ctGrid.addEventListener(\"click\", (e) => {\n        const btn = e.target.closest(\".cover-template-item\");\n        if (!btn || !_ctGrid.contains(btn)) return;\n        e.preventDefault();\n        const templateId = btn.getAttribute(\"data-template\") || \"default\";\n        const hasCoverWork = (Array.isArray(editor.coverObjects) && editor.coverObjects.length > 0)\n                             || (coverState.template && coverState.template !== \"default\");\n        if (hasCoverWork && !confirm(\"Apply this cover template? It replaces your current cover design.\")) return;\n        applyCoverTemplate(templateId);                       // set state + highlight\n        if (templateId && templateId !== \"default\") {\n          // Load the actual template payload (predesigned or R2-saved). force=true: this is\n          // the customer explicitly choosing to replace their cover (confirmed above).\n          Promise.resolve(dmbApplyCoverTemplate(templateId, true))\n            .then(() => { coverState.template = templateId; })   // keep highlight correct after load\n            .catch(() => {});\n        }\n        if (_ctModal) _ctModal.style.display = \"none\";\n        updateCoverPreview();\n      });\n    }\n\n    const chooseCoverLayoutBtn = $(\"chooseCoverLayoutBtn\");\n    if (chooseCoverLayoutBtn) {\n      chooseCoverLayoutBtn.addEventListener(\"click\", (e) => {\n        e.preventDefault();\n        // Use the currently active cover zone (front/back) \u2014 spine doesn't support layouts\n        // Open the Layouts SIDE PANEL (same as for pages). The panel applies its\n        // layouts to the cover while the cover is the active builder mode.\n        const _isMobTabs = window.matchMedia && window.matchMedia(\"(max-width: 749px), (orientation: portrait) and (pointer: coarse) and (max-width: 1024px)\").matches;\n        if (_isMobTabs) {\n          document.querySelector('.dmb-msh-tab[data-tab=\"layouts\"]')?.click();\n        } else {\n          document.querySelector('.dmb-icon-tab[data-tab=\"layouts\"]')?.click();\n        }\n      });\n    }\n\n    if (coverTitle1Input) {\n      coverTitle1Input.addEventListener(\"input\", updateCoverPreview);\n    }\n    if (coverTitle2Input) {\n      coverTitle2Input.addEventListener(\"input\", updateCoverPreview);\n    }\n\n    if (coverColorSwatch && coverColorInput) {\n      // Phone fix: overlay the REAL <input type=color> on the swatch so a tap lands on\n      // it directly. iOS ignores a programmatic .click() on a hidden colour input (and\n      // the preventDefault below cancels the native picker), which is why tapping the\n      // cover colour did NOTHING on the phone. Desktop is unaffected \u2014 _dmbColorTapFix\n      // no-ops above 749px and the .click() proxy below still runs there.\n      try { _dmbColorTapFix(coverColorSwatch, coverColorInput); } catch (e) {}\n      coverColorSwatch.addEventListener(\"click\", (e) => {\n        // Mobile: the tap already hit the overlaid input \u2192 let it open natively (NEVER\n        // preventDefault or re-.click() \u2014 both kill the iOS picker). Desktop: proxy the tap.\n        if (e.target === coverColorInput) return;\n        e.preventDefault();\n        coverColorInput.click();\n      });\n\n      coverColorInput.addEventListener(\"input\", () => {\n        coverColorSwatch.style.background = coverColorInput.value || \"#ffffff\";\n        updateCoverPreview();\n      });\n\n      coverColorInput.addEventListener(\"change\", () => {\n        coverColorSwatch.style.background = coverColorInput.value || \"#ffffff\";\n        updateCoverPreview();\n        try { _dmbWriteProgressState(); } catch (e) {}   // flush colour to the restore key now\n      });\n\n      coverColorSwatch.style.background = coverColorInput.value || \"#ffffff\";\n      // Recently-used colors: strip goes below the swatch row, inside its wrapper.\n      try { dmbEnhanceColorInput(coverColorInput, (coverColorInput.closest(\".dmb-color-row\") || coverColorInput).parentElement); } catch (e) {}\n    } else if (coverColorInput) {\n      coverColorInput.addEventListener(\"input\", updateCoverPreview);\n      coverColorInput.addEventListener(\"change\", updateCoverPreview);\n    }\n\n    if (coverImageInput) {\n      coverImageInput.addEventListener(\"change\", () => {\n        const file = coverImageInput.files && coverImageInput.files[0];\n        if (!file) {\n          coverState.imageFile = null;\n          coverState.imageUrl = \"\";\n          updateCoverPreview();\n          return;\n        }\n\n        coverState.imageFile = file;\n        try {\n          if (coverState.imageUrl && coverState.imageUrl.startsWith(\"blob:\")) {\n            URL.revokeObjectURL(coverState.imageUrl);\n          }\n        } catch {}\n\n        coverState.imageUrl = URL.createObjectURL(file);\n        updateCoverPreview();\n      });\n    }\n\n    applyCoverTemplate(coverState.template);\n    updateCoverPreview();\n  }\n\n  // ---------- page template helpers ----------\n  function getActiveDesignSurface() {\n    return document.querySelector(\".dmb-page.is-active .dmb-page-inner\")\n        || document.querySelector(\".dmb-page.is-active\")\n        || document.getElementById(\"spreadCanvas\");\n  }\n\n  /* \u2500\u2500 MAP COUNTRY LIST \u2500\u2500 */\n  const DMB_COUNTRY_KEYS = [\n    \"afghanistan\",\"albania\",\"algeria\",\"andorra\",\"angola\",\"antigua-and-barbuda\",\"argentina\",\n    \"armenia\",\"australia\",\"austria\",\"azerbaijan\",\"bahamas\",\"bahrain\",\"bangladesh\",\n    \"barbados\",\"belarus\",\"belgium\",\"belize\",\"benin\",\"bermuda\",\"bhutan\",\"bolivia\",\n    \"bosnia-herzegovina-2\",\"botswana\",\"brazil\",\"brunei-darussalam\",\"bulgaria\",\n    \"burkina-faso\",\"burundi\",\"cambodia\",\"cameroon\",\"canada\",\"cape-verde\",\n    \"central-african-republic\",\"chad\",\"chile\",\"china\",\"colombia\",\"comoros\",\"congo\",\n    \"congo-dr\",\"costa-rica\",\"croatia\",\"cuba\",\"cyprus\",\"czech-republic\",\"denmark\",\"djibouti\",\n    \"dominica\",\"dominican-republic\",\"ecuador\",\"egypt\",\"el-salvador\",\"equatorial-guinea\",\n    \"eritrea\",\"estonia\",\"eswatini\",\"ethiopia\",\"fiji\",\"finland\",\"france\",\"gabon\",\"gambia\",\n    \"georgia\",\"germany\",\"ghana\",\"greece\",\"grenada\",\"guatemala\",\"guinea\",\"guinea-bissau\",\n    \"guyana\",\"haiti\",\"honduras\",\"hong-kong\",\"hungary\",\"iceland\",\"india\",\"indonesia\",\"iran\",\n    \"iraq\",\"ireland\",\"italy\",\"ivory-coast\",\"jamaica\",\"japan\",\"jordan\",\"kazakhstan\",\"kenya\",\n    \"kiribati\",\"kosovo\",\"kuwait\",\"kyrgyzstan\",\"laos\",\"latvia\",\"lebanon\",\"lesotho\",\"liberia\",\n    \"libya\",\"liechtenstein\",\"lithuania\",\"luxembourg\",\"macedonia\",\"madagascar\",\"malawi\",\n    \"malaysia\",\"maldives\",\"mali\",\"malta\",\"marshall-islands\",\"mauritania\",\"mauritius\",\n    \"mexico\",\"micronesia\",\"moldova\",\"monaco\",\"mongolia\",\"montenegro\",\"morocco\",\"mozambique\",\n    \"myanmar\",\"namibia\",\"nauru\",\"nepal\",\"netherlands\",\"new-zealand\",\"nicaragua\",\"niger\",\n    \"nigeria\",\"north-korea\",\"norway\",\"oman\",\"pakistan\",\"palau\",\"palestine\",\"panama\",\"papua\",\n    \"paraguay\",\"peru\",\"philippines\",\"poland\",\"portugal\",\"puerto-rico\",\"qatar\",\"romania\",\n    \"russia\",\"rwanda\",\"saint-kitts-and-nevis\",\"saint-lucia\",\n    \"saint-vincent-and-the-grenadines\",\"samoa\",\"san-marino\",\"sao-tome-and-principe\",\n    \"saudi-arabia\",\"senegal\",\"serbia\",\"seychelles\",\"sierra-leone\",\"singapore\",\"slovakia\",\n    \"slovenia\",\"solomon-islands\",\"somalia\",\"south-africa\",\"south-korea\",\"south-sudan\",\n    \"spain\",\"sri-lanka\",\"sudan\",\"suriname\",\"sweden\",\"switzerland\",\"syria\",\"taiwan\",\n    \"tajikistan\",\"tanzania\",\"thailand\",\"timor-leste\",\"togo\",\"tonga\",\"trinidad-and-tobago\",\n    \"tunisia\",\"turkey\",\"turkmenistan\",\"tuvalu\",\"uganda\",\"ukraine\",\"united-arab-emirates\",\n    \"united-kingdom\",\"uruguay\",\"usa\",\"uzbekistan\",\"vanuatu\",\"vatican-city\",\"venezuela\",\n    \"vietnam\",\"vietnam-with-islands\",\"yemen\",\"zambia\",\"zimbabwe\"\n  ];\n\n  // Continent maps: same machinery as country maps, but each internal <path> is a\n  // COUNTRY (not a province). Listed first in the picker. `hideProvinces` on these\n  // hides the country borders (single silhouette) \u2014 the UI relabels it accordingly.\n  const DMB_CONTINENT_KEYS = [\"africa\",\"asia\",\"europe\",\"north-america\",\"oceania\",\"south-america\"];\n  function dmbIsContinentKey(key) { return DMB_CONTINENT_KEYS.indexOf(key) !== -1; }\n\n  function dmbKeyToName(key) {\n    const overrides = {\n      \"north-america\": \"North America\",\n      \"south-america\": \"South America\",\n      \"antigua-and-barbuda\": \"Antigua & Barbuda\",\n      \"equatorial-guinea\": \"Equatorial Guinea\",\n      \"guinea-bissau\": \"Guinea-Bissau\",\n      \"marshall-islands\": \"Marshall Islands\",\n      \"north-korea\": \"North Korea\",\n      \"sao-tome-and-principe\": \"S\u00E3o Tom\u00E9 & Pr\u00EDncipe\",\n      \"saint-kitts-and-nevis\": \"St. Kitts & Nevis\",\n      \"saint-lucia\": \"St. Lucia\",\n      \"saint-vincent-and-the-grenadines\": \"St. Vincent & Grenadines\",\n      \"solomon-islands\": \"Solomon Islands\",\n      \"south-sudan\": \"South Sudan\",\n      \"timor-leste\": \"Timor-Leste\",\n      \"vatican-city\": \"Vatican City\",\n      \"micronesia\": \"Micronesia\",\n      \"bosnia-herzegovina-2\": \"Bosnia & Herzegovina\",\n      \"brunei-darussalam\": \"Brunei\",\n      \"cape-verde\": \"Cabo Verde\",\n      \"central-african-republic\": \"Central African Republic\",\n      \"congo-dr\": \"Congo (DR)\",\n      \"costa-rica\": \"Costa Rica\",\n      \"czech-republic\": \"Czech Republic\",\n      \"dominican-republic\": \"Dominican Republic\",\n      \"el-salvador\": \"El Salvador\",\n      \"hong-kong\": \"Hong Kong\",\n      \"ivory-coast\": \"C\u00F4te d'Ivoire\",\n      \"new-zealand\": \"New Zealand\",\n      \"papua\": \"Papua New Guinea\",\n      \"puerto-rico\": \"Puerto Rico\",\n      \"san-marino\": \"San Marino\",\n      \"saudi-arabia\": \"Saudi Arabia\",\n      \"sierra-leone\": \"Sierra Leone\",\n      \"south-africa\": \"South Africa\",\n      \"south-korea\": \"South Korea\",\n      \"sri-lanka\": \"Sri Lanka\",\n      \"trinidad-and-tobago\": \"Trinidad & Tobago\",\n      \"united-arab-emirates\": \"United Arab Emirates\",\n      \"united-kingdom\": \"United Kingdom\",\n      \"usa\": \"United States\",\n      \"vietnam-with-islands\": \"Vietnam (with Islands)\",\n      \"burkina-faso\": \"Burkina Faso\",\n      \"eswatini\": \"Eswatini (Swaziland)\",\n    };\n    return overrides[key] || key.split(\"-\").map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(\" \");\n  }\n\n  /* \u2500\u2500 MAP MODAL STATE \u2500\u2500 */\n  let dmbModalSelectedKey = null;\n  let dmbModalSvgCache = {};\n\n  function dmbGetAssetUrl(filename) {\n    const full = (window.DMB_ASSET_BASE || \"\");\n    // Carry the design-my-book.js \"?v=\u2026\" version onto map/asset URLs, so updating an\n    // asset (e.g. a country map) actually refreshes it instead of serving a stale cache.\n    const q = full.indexOf(\"?\");\n    const query = q >= 0 ? full.slice(q) : \"\";\n    const base = full.replace(/\\?.*$/, \"\").replace(/\\/[^/]+$/, \"/\");\n    return base + filename + query;\n  }\n\n  function dmbGetModalStyles() {\n    return {\n      fill: document.getElementById(\"dmbMapFill\")?.value || \"#d9d9d9\",\n      stroke: document.getElementById(\"dmbMapStroke\")?.value || \"#111111\",\n      strokeWidth: parseFloat(document.getElementById(\"dmbMapStrokeWidth\")?.value ?? 1.2),\n      strokeDasharray: document.getElementById(\"dmbMapStrokeStyle\")?.value || \"\",\n      opacity: parseFloat(document.getElementById(\"dmbMapOpacity\")?.value ?? 1),\n      showOutline: document.getElementById(\"dmbMapShowOutline\")?.checked || false,\n      hideProvinces: document.getElementById(\"dmbMapHideProvinces\")?.checked || false,\n      transparentFill: document.getElementById(\"dmbMapTransparentFill\")?.checked || false,\n    };\n  }\n\n  function dmbEnsureOutlineFilter(svgEl, strokeColor, strokeWidth, filterId, hollowCenter, builderW) {\n    const ns = \"http://www.w3.org/2000/svg\";\n    let defs = svgEl.querySelector(\"defs\");\n    if (!defs) {\n      defs = document.createElementNS(ns, \"defs\");\n      svgEl.insertBefore(defs, svgEl.firstChild);\n    }\n    const old = defs.querySelector(\"#\" + filterId);\n    if (old) old.remove();\n\n    const filter = document.createElementNS(ns, \"filter\");\n    filter.id = filterId;\n    filter.setAttribute(\"x\", \"-20%\");\n    filter.setAttribute(\"y\", \"-20%\");\n    filter.setAttribute(\"width\", \"140%\");\n    filter.setAttribute(\"height\", \"140%\");\n\n    // Honour 0 (Width 0 -> no outline) via parseFloat, defaulting to 1.2 only when\n    // the value is genuinely unset.\n    let _sw = parseFloat(strokeWidth);\n    if (!isFinite(_sw)) _sw = 1.2;\n\n    // \u2500\u2500 RADIUS IS IN CSS PIXELS HERE, NOT VIEWBOX UNITS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // The map SVGs have NO inner <g>, so `g` below resolves to the <svg> element\n    // itself and the filter is applied to the ROOT svg. A filter on a root svg runs\n    // in that element's CSS-pixel space - NOT in its viewBox user space - so a\n    // radius of R paints an R-PIXEL ring at whatever size the element is rendered.\n    // The Width value already means CSS px (the paths use non-scaling-stroke), so\n    // the raw value is exactly right and any viewBox conversion is wrong.\n    //\n    // This is also why the same number behaves differently in print: drawSvgObj\n    // rasterises the SAME markup into a bitmap `_mapK` times wider than the builder\n    // box, so the ring there must be scaled by _mapK to come out the same physical\n    // thickness (see the matching note in drawSvgObj).\n    //\n    // History, so this is not \"fixed\" a third time: on 2026-09-11 a viewBox\n    // conversion (radius = sw * vbW / builderW) was added here to make a thin\n    // builder ring match print. It made the ring vbW/builderW times too thick\n    // (Amsterdam template: vbW 636.5, box 170 -> 4.49px instead of 1.2px) and the\n    // real mismatch was in drawSvgObj's own radius, which is fixed there instead.\n    // Which space the radius is in depends on WHICH element the filter lands on\n    // (see the `g` lookup in dmbApplyStylesToSvg - the same expression):\n    //   root <svg>  -> CSS-pixel space  -> raw value  (every map ships without a <g>)\n    //   inner <g>   -> user space       -> convert via vbW / rendered width\n    // Branching on it keeps both correct if a map is ever exported with a group.\n    let outlineRadius = Math.max(0, _sw);\n    try {\n      if (svgEl.querySelector(\":scope > g\")) {\n        const _vb  = (svgEl.getAttribute(\"viewBox\") || \"\").trim().split(/[\\s,]+/);\n        const _vbW = (_vb.length >= 3) ? (parseFloat(_vb[2]) || 0) : 0;\n        let _bw = parseFloat(builderW);\n        if (!isFinite(_bw) || _bw <= 0) _bw = svgEl.clientWidth || 0;\n        if (_vbW > 0 && _bw > 0) outlineRadius = Math.max(0, _sw * _vbW / _bw);\n      }\n    } catch (e) {}\n\n    // Step 1: dilate the combined silhouette\n    const morph = document.createElementNS(ns, \"feMorphology\");\n    morph.setAttribute(\"operator\", \"dilate\");\n    morph.setAttribute(\"radius\", String(outlineRadius));\n    morph.setAttribute(\"in\", \"SourceAlpha\");\n    morph.setAttribute(\"result\", \"dilated\");\n\n    // Step 2: subtract original silhouette \u2192 outer ring only\n    const ring = document.createElementNS(ns, \"feComposite\");\n    ring.setAttribute(\"in\", \"dilated\");\n    ring.setAttribute(\"in2\", \"SourceAlpha\");\n    ring.setAttribute(\"operator\", \"out\");\n    ring.setAttribute(\"result\", \"ring\");\n\n    // Step 3: flood with stroke color\n    const flood = document.createElementNS(ns, \"feFlood\");\n    flood.setAttribute(\"flood-color\", strokeColor || \"#111111\");\n    flood.setAttribute(\"result\", \"color\");\n\n    // Step 4: clip color to ring shape\n    const comp = document.createElementNS(ns, \"feComposite\");\n    comp.setAttribute(\"in\", \"color\");\n    comp.setAttribute(\"in2\", \"ring\");\n    comp.setAttribute(\"operator\", \"in\");\n    comp.setAttribute(\"result\", \"coloredRing\");\n\n    if (hollowCenter) {\n      // Output: ring only, interior transparent (used with transparent fill)\n      filter.append(morph, ring, flood, comp);\n    } else {\n      // Output: original map content + outer ring drawn on top\n      const merge = document.createElementNS(ns, \"feMerge\");\n      const mn1 = document.createElementNS(ns, \"feMergeNode\");\n      mn1.setAttribute(\"in\", \"SourceGraphic\");\n      const mn2 = document.createElementNS(ns, \"feMergeNode\");\n      mn2.setAttribute(\"in\", \"coloredRing\");\n      merge.appendChild(mn1);\n      merge.appendChild(mn2);\n      filter.append(morph, ring, flood, comp, merge);\n    }\n\n    defs.appendChild(filter);\n  }\n\n  function dmbApplyStylesToSvg(svgEl, styles) {\n    const effectiveFill = styles.transparentFill ? \"none\" : (styles.fill || \"#d9d9d9\");\n    // When showOutline is active with transparent fill, paths still need an opaque fill\n    // so SourceAlpha can capture the country silhouette. The filter hides the fill visually.\n    const silhouetteFill = styles.fill || \"#d9d9d9\";\n    const pathFill = (styles.showOutline && styles.transparentFill) ? silhouetteFill : effectiveFill;\n\n    const filterId = styles.filterId || \"dmbOutlineFilter\";\n    const g = svgEl.querySelector(\":scope > g\") || svgEl;\n\n    if (styles.showOutline) {\n      dmbEnsureOutlineFilter(svgEl, styles.stroke, styles.strokeWidth, filterId, !!styles.transparentFill, styles.w);\n      g.setAttribute(\"filter\", \"url(#\" + filterId + \")\");\n    } else {\n      g.removeAttribute(\"filter\");\n      svgEl.removeAttribute(\"filter\");\n    }\n\n    svgEl.style.opacity = styles.opacity != null ? String(styles.opacity) : \"1\";\n\n    svgEl.querySelectorAll(\"path\").forEach((path) => {\n      path.style.fill = pathFill;\n      path.style.stroke = styles.hideProvinces ? pathFill : (styles.stroke || \"#111111\");\n      path.style.strokeWidth = styles.hideProvinces ? \"1\" : (styles.strokeWidth || 1.2);\n      path.style.strokeDasharray = styles.hideProvinces ? \"\" : (styles.strokeDasharray || \"\");\n      path.style.opacity = \"\";\n      path.style.vectorEffect = \"non-scaling-stroke\";\n    });\n  }\n\n  /* Fit the preview viewBox around the country PLUS its outline ring.\n   *\n   * dmbEnsureOutlineFilter hangs the feMorphology on the ROOT <svg> (no map asset\n   * has an inner <g>), so the ring radius is in CSS PIXELS while the viewBox is in\n   * user units - and the ring is painted OUTSIDE the silhouette's own bbox. The\n   * outermost <svg> clips to its viewport (the UA rule svg:not(:root){overflow:hidden}),\n   * so a viewBox cropped tight to the silhouette cuts the ring off.\n   *\n   * With xMidYMid meet the scale is s = min(BW/(Wc+2p), BH/(Hc+2p)); requiring the\n   * pad to render as at least R css px (p*s >= R) gives\n   *     p = R * max( Wc / (BW - 2R),  Hc / (BH - 2R) )\n   * The max() picks whichever axis the meet-fit is limited by, so this is exact:\n   * country + ring fills the box edge to edge and no smaller.\n   *\n   * Always derived from the RAW content box stashed on the node, so repeated\n   * refits (every slider move) never compound. (2026-09-13) */\n  function dmbFitPreviewViewBox(svgEl, styles) {\n    try {\n      const bb = String((svgEl && svgEl.dataset && svgEl.dataset.dmbBbox) || \"\").split(\" \").map(parseFloat);\n      if (bb.length !== 4 || !isFinite(bb[2]) || bb[2] <= 0 || bb[3] <= 0) return;\n      const host = svgEl.parentElement;\n      const cs = host ? getComputedStyle(host) : null;\n      const BW = host ? (host.clientWidth  - (parseFloat(cs.paddingLeft) || 0) - (parseFloat(cs.paddingRight)  || 0)) : 0;\n      const BH = host ? (host.clientHeight - (parseFloat(cs.paddingTop)  || 0) - (parseFloat(cs.paddingBottom) || 0)) : 0;\n      const sw = Math.max(0, parseFloat(styles && styles.strokeWidth) || 0);\n      const R  = ((styles && styles.showOutline) ? sw : 0) + sw / 2 + 1;\n      let p = 0;\n      if (BW > 2 * R && BH > 2 * R) {\n        p = R * Math.max(bb[2] / (BW - 2 * R), bb[3] / (BH - 2 * R));\n      } else {\n        p = bb[2] * 0.02;                      // box too small to reason about\n      }\n      svgEl.setAttribute(\"viewBox\",\n        (bb[0] - p).toFixed(2) + \" \" + (bb[1] - p).toFixed(2) + \" \" +\n        (bb[2] + p * 2).toFixed(2) + \" \" + (bb[3] + p * 2).toFixed(2));\n      svgEl.setAttribute(\"preserveAspectRatio\", \"xMidYMid meet\");\n    } catch (e) {}\n  }\n\n  function dmbUpdateModalPreview() {\n    const preview = document.getElementById(\"dmbMapPreview\");\n    if (!preview) return;\n    const svgEl = preview.querySelector(\"svg\");\n    if (!svgEl) return;\n    const st = dmbGetModalStyles();\n    dmbFitPreviewViewBox(svgEl, st);   // the ring width may just have changed\n    dmbApplyStylesToSvg(svgEl, st);\n  }\n\n  async function dmbSelectCountry(key) {\n    dmbModalSelectedKey = key;\n\n    /* highlight selected row */\n    document.querySelectorAll(\".dmb-country-item\").forEach(el => el.classList.toggle(\"dmb-country-item--active\", el.dataset.key === key));\n\n    /* Continents subdivide by country, so relabel the \"provinces\" control. */\n    const _hpLbl = document.getElementById(\"dmbMapHideProvincesLbl\");\n    if (_hpLbl) _hpLbl.textContent = dmbIsContinentKey(key) ? \"Hide borders\" : \"Hide provinces\";\n\n    const preview = document.getElementById(\"dmbMapPreview\");\n    const insertBtn = document.getElementById(\"dmbMapInsertBtn\");\n    if (!preview) return;\n\n    preview.innerHTML = '<span class=\"dmb-map-preview-placeholder\">Loading\u2026</span>';\n\n    try {\n      if (!dmbModalSvgCache[key]) {\n        const url = dmbGetAssetUrl(\"dmb-map-\" + key + \".svg\");\n        const res = await fetch(url);\n        if (!res.ok) throw new Error(\"Not found\");\n        dmbModalSvgCache[key] = await res.text();\n      }\n\n      const parser = new DOMParser();\n      const doc = parser.parseFromString(dmbModalSvgCache[key], \"image/svg+xml\");\n      const svgEl = doc.querySelector(\"svg\");\n      if (!svgEl) throw new Error(\"No SVG element\");\n\n      svgEl.removeAttribute(\"width\");\n      svgEl.removeAttribute(\"height\");\n      svgEl.style.width = \"100%\";\n      svgEl.style.height = \"100%\";\n      svgEl.style.display = \"block\";\n\n      // Attach BEFORE styling: the outline radius is calibrated against the SVG's\n      // laid-out width, which is 0 while the node is still detached - that would\n      // draw the first preview with a hairline ring and only correct it once a\n      // style control was touched.\n      preview.innerHTML = \"\";\n      preview.appendChild(svgEl);\n\n      // \u2500\u2500 Give it a viewBox, or it does not scale at all \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n      // These MapSVG assets ship with NO viewBox attribute. With width/height\n      // stripped above, the browser draws them 1:1 in their own user units\n      // anchored top-left, so any country bigger than the preview box is simply\n      // CUT OFF - measured: Russia 1224 units wide, Bangladesh 600 tall, France\n      // 596x584, against a preview box of roughly 640x340 CSS px. That is the\n      // \"some countries aren't fitting\" report, and it is also why small\n      // countries looked fine: they happened to be under the box size.\n      //\n      // Cropping to the real content bounds does double duty - it makes the\n      // shape scale to FIT and fills the box as large as it can. Same 2% pad as\n      // dmbCropSvgToContent so the preview frames a country exactly like the map\n      // that actually gets inserted on the page. (2026-09-13)\n      try {\n        let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;\n        svgEl.querySelectorAll(\"path, polygon, polyline, circle, ellipse, rect\").forEach(function (el) {\n          try {\n            const b = el.getBBox();\n            if (b.width > 0 || b.height > 0) {\n              minX = Math.min(minX, b.x); minY = Math.min(minY, b.y);\n              maxX = Math.max(maxX, b.x + b.width); maxY = Math.max(maxY, b.y + b.height);\n            }\n          } catch (e2) {}\n        });\n        if (isFinite(minX) && maxX > minX && maxY > minY) {\n          // Stash the RAW content box; dmbFitPreviewViewBox re-derives the pad from\n          // it on every style change, so refits never compound.\n          svgEl.dataset.dmbBbox = [minX, minY, maxX - minX, maxY - minY].join(\" \");\n          dmbFitPreviewViewBox(svgEl, dmbGetModalStyles());\n        }\n      } catch (e) {}\n\n      dmbApplyStylesToSvg(svgEl, dmbGetModalStyles());\n\n      if (insertBtn) insertBtn.disabled = false;\n    } catch (e) {\n      preview.innerHTML = '<span class=\"dmb-map-preview-placeholder\">Preview unavailable</span>';\n    }\n  }\n\n  function dmbBuildCountryList(filter) {\n    const list = document.getElementById(\"dmbCountryList\");\n    if (!list) return;\n    const q = (filter || \"\").toLowerCase().trim();\n    list.innerHTML = \"\";\n    const addItem = (key) => {\n      const name = dmbKeyToName(key);\n      if (q && !name.toLowerCase().includes(q) && !key.includes(q)) return false;\n      const btn = document.createElement(\"button\");\n      btn.type = \"button\";\n      btn.className = \"dmb-country-item\";\n      btn.dataset.key = key;\n      btn.textContent = name;\n      if (key === dmbModalSelectedKey) btn.classList.add(\"dmb-country-item--active\");\n      btn.addEventListener(\"click\", () => dmbSelectCountry(key));\n      list.appendChild(btn);\n      return true;\n    };\n    const addHeader = (text) => {\n      const h = document.createElement(\"div\");\n      h.className = \"dmb-country-group\";\n      h.textContent = text;\n      list.appendChild(h);\n    };\n    // Continents first (each subdivided BY COUNTRY), then the individual countries.\n    let contHeader = false;\n    DMB_CONTINENT_KEYS.forEach((key) => {\n      if (!contHeader) { addHeader(\"Continents\"); contHeader = true; }\n      const shown = addItem(key);\n      if (!shown && contHeader && !list.querySelector(\".dmb-country-item\")) {\n        // header added but nothing under it yet \u2014 leave; trailing empty header is cleaned below\n      }\n    });\n    let countryHeader = false;\n    DMB_COUNTRY_KEYS.forEach((key) => {\n      if (!countryHeader) { addHeader(\"Countries\"); countryHeader = true; }\n      addItem(key);\n    });\n    // Remove any group header that ended up with no items beneath it (e.g. a search\n    // that matched only countries leaves an empty \"Continents\" header).\n    list.querySelectorAll(\".dmb-country-group\").forEach((h) => {\n      const next = h.nextElementSibling;\n      if (!next || next.classList.contains(\"dmb-country-group\")) h.remove();\n    });\n  }\n\n  function dmbOpenMapModal() {\n    const modal = document.getElementById(\"dmbMapModal\");\n    if (!modal) return;\n    // Ribbon-opened by default: forget any page the per-page button pinned last\n    // time, so the picker asks again instead of reusing a stale target.\n    _dmbMapTargetPage = null;\n    dmbModalSelectedKey = null;\n    const preview = document.getElementById(\"dmbMapPreview\");\n    if (preview) preview.innerHTML = '<span class=\"dmb-map-preview-placeholder\">Select a country to preview</span>';\n    const insertBtn = document.getElementById(\"dmbMapInsertBtn\");\n    if (insertBtn) insertBtn.disabled = true;\n    const search = document.getElementById(\"dmbMapSearch\");\n    if (search) search.value = \"\";\n    const ooModal = document.getElementById(\"dmbMapShowOutline\");\n    if (ooModal) ooModal.checked = false;\n    const hpModal = document.getElementById(\"dmbMapHideProvinces\");\n    if (hpModal) hpModal.checked = false;\n    const tfModal = document.getElementById(\"dmbMapTransparentFill\");\n    if (tfModal) tfModal.checked = false;\n    dmbBuildCountryList(\"\");\n    modal.style.display = \"flex\";\n  }\n\n  function dmbCloseMapModal() {\n    const modal = document.getElementById(\"dmbMapModal\");\n    if (modal) modal.style.display = \"none\";\n  }\n\n  function initMapModal() {\n    const modal = document.getElementById(\"dmbMapModal\");\n    if (!modal || modal.dataset.dmbModalInit) return;\n    modal.dataset.dmbModalInit = \"1\";\n\n    document.getElementById(\"dmbMapModalClose\")?.addEventListener(\"click\", dmbCloseMapModal);\n    modal.querySelector(\".dmb-map-modal-backdrop\")?.addEventListener(\"click\", dmbCloseMapModal);\n\n    document.getElementById(\"dmbMapSearch\")?.addEventListener(\"input\", (e) => {\n      dmbBuildCountryList(e.target.value);\n    });\n\n    /* style controls \u2192 live preview update */\n    [\"dmbMapFill\",\"dmbMapStroke\",\"dmbMapStrokeWidth\",\"dmbMapStrokeStyle\",\"dmbMapOpacity\",\"dmbMapShowOutline\",\"dmbMapHideProvinces\",\"dmbMapTransparentFill\"].forEach((id) => {\n      document.getElementById(id)?.addEventListener(\"change\", () => dmbUpdateModalPreview());\n      document.getElementById(id)?.addEventListener(\"input\", () => {\n        const wv = document.getElementById(\"dmbMapStrokeWidthVal\");\n        if (wv) wv.textContent = document.getElementById(\"dmbMapStrokeWidth\")?.value;\n        const ov = document.getElementById(\"dmbMapOpacityVal\");\n        if (ov) ov.textContent = Math.round(parseFloat(document.getElementById(\"dmbMapOpacity\")?.value) * 100) + \"%\";\n        dmbUpdateModalPreview();\n      });\n    });\n\n    document.getElementById(\"dmbMapInsertBtn\")?.addEventListener(\"click\", async () => {\n      if (!dmbModalSelectedKey || !dmbModalSvgCache[dmbModalSelectedKey]) return;\n      // Settle the destination BEFORE building anything, so a cancel costs nothing.\n      const _target = await _dmbResolveMapTargetPage();\n      if (_target === null) return;\n      const styles = dmbGetModalStyles();\n      const svgMarkup = dmbModalSvgCache[dmbModalSelectedKey];\n\n      const doInsert = (finalSvg) => {\n        if (window.insertMapIntoActivePage) {\n          window.insertMapIntoActivePage(finalSvg, dmbModalSelectedKey, styles, _target);\n        }\n        dmbCloseMapModal();\n        const markStayBtn = document.getElementById(\"markStayBtn\");\n        if (markStayBtn) markStayBtn.style.display = \"inline-flex\";\n      };\n\n      const islands = dmbFindIslandPaths(svgMarkup);\n      if (islands.length > 0) {\n        showChoiceDialog(\n          \"Islands detected\",\n          `This map has ${islands.length} island(s) that appear separate from the main landmass. Include them?`,\n          [\n            { id: \"yes\", label: \"Yes, include islands\" },\n            { id: \"no\", label: \"No, main landmass only\" }\n          ]\n        ).then(choice => {\n          if (!choice) return;\n          doInsert(choice === \"no\" ? dmbRemoveIslandPaths(svgMarkup, islands) : svgMarkup);\n        });\n      } else {\n        doInsert(svgMarkup);\n      }\n    });\n\n    document.addEventListener(\"keydown\", (e) => {\n      if (e.key === \"Escape\" && modal.style.display !== \"none\") dmbCloseMapModal();\n    });\n\n    /* Mobile \"one sheet\" layout (Variant B): inject a \"Style \u25BE\" toggle that\n       collapses the style controls, so the country list + sticky Insert button\n       dominate the bottom sheet. Desktop hides this toggle via CSS and always\n       shows the controls. */\n    const _mapRight = modal.querySelector(\".dmb-map-modal-right\");\n    const _mapControls = modal.querySelector(\".dmb-map-style-controls\");\n    if (_mapRight && _mapControls && !document.getElementById(\"dmbMapStyleToggle\")) {\n      const t = document.createElement(\"button\");\n      t.type = \"button\";\n      t.id = \"dmbMapStyleToggle\";\n      t.className = \"dmb-map-style-toggle\";\n      t.innerHTML = 'Style <span class=\"dmb-mst-caret\">&#9662;</span>';\n      _mapRight.insertBefore(t, _mapControls);\n      t.addEventListener(\"click\", () => {\n        _mapRight.classList.toggle(\"dmb-map-styles-open\");\n      });\n    }\n  }\n\n  /* \u2500\u2500 MAP EDIT BAR (floating, shown when a placed map is clicked) \u2500\u2500 */\n  let dmbEditingMapObj = null;\n  let dmbEditingMapEl = null;\n  let dmbEditBarUndoTimer = null;\n\n  /* \u2500\u2500 SHAPE EDIT BAR (floating, shown when a placed shape is clicked) \u2500\u2500\n   * WARNING: Keep dmbEditingShapeObj/El in sync with the edit bar. Any\n   * function that opens a different toolbar (text, map, frame) must call\n   * dmbCloseShapeEditBar() so only one floating bar is ever visible.\n   * \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n  let dmbEditingShapeObj = null;\n  let dmbEditingShapeEl  = null;\n  let _dmbShapeBarInit   = false;  // guard so we only wire bar events once\n  let _dmbLineModeActive = false;  // true while waiting for a two-point line drag\n\n  function dmbApplyEditBarToObj() {\n    if (!dmbEditingMapObj || !dmbEditingMapEl) return;\n    const fill = document.getElementById(\"dmbEditFill\")?.value || \"#d9d9d9\";\n    const stroke = document.getElementById(\"dmbEditStroke\")?.value || \"#111111\";\n    const strokeWidth = parseFloat(document.getElementById(\"dmbEditWidth\")?.value ?? 1.2);\n    const strokeDasharray = document.getElementById(\"dmbEditStrokeStyle\")?.value || \"\";\n    const opacity = parseFloat(document.getElementById(\"dmbEditOpacity\")?.value ?? 1);\n    const showOutline = document.getElementById(\"dmbEditShowOutline\")?.checked || false;\n    const hideProvinces = document.getElementById(\"dmbEditHideProvinces\")?.checked || false;\n    const transparentFill = document.getElementById(\"dmbEditTransparentFill\")?.checked || false;\n\n    dmbEditingMapObj.fill = fill;\n    dmbEditingMapObj.stroke = stroke;\n    dmbEditingMapObj.strokeWidth = strokeWidth;\n    dmbEditingMapObj.strokeDasharray = strokeDasharray;\n    dmbEditingMapObj.opacity = opacity;\n    dmbEditingMapObj.showOutline = showOutline;\n    dmbEditingMapObj.hideProvinces = hideProvinces;\n    dmbEditingMapObj.transparentFill = transparentFill;\n\n    const wv = document.getElementById(\"dmbEditWidthVal\");\n    if (wv) wv.textContent = strokeWidth;\n    const ov = document.getElementById(\"dmbEditOpacityVal\");\n    if (ov) ov.textContent = Math.round(opacity * 100) + \"%\";\n\n    try { _dmbSyncMapDots(); } catch (e) {}\n\n    const svgEl = dmbEditingMapEl.querySelector(\"svg\");\n    if (svgEl) dmbApplyStylesToSvg(svgEl, { fill, stroke, strokeWidth, strokeDasharray, opacity, showOutline, hideProvinces, transparentFill, filterId: dmbEditingMapObj.filterId, w: dmbEditingMapObj.w });\n\n    // Debounce undo so rapid slider drags collapse into one undo step\n    clearTimeout(dmbEditBarUndoTimer);\n    dmbEditBarUndoTimer = setTimeout(() => { pushUndoState(\"Edit Map Style\"); }, 600);\n  }\n\n  function dmbOpenEditBar(obj, el) {\n    _dmbLastBarShow = () => dmbOpenEditBar(obj, el);\n    // Fast path while dragging THIS already-selected map: skip the heavy per-move\n    // re-init (hiding other bars, rewriting all form controls, re-docking) \u2014 the bar\n    // is already open & docked. Just keep the rotate handle on the object.\n    if (_dmbDragSuppress && dmbEditingMapObj === obj && dmbEditingMapEl === el && el.classList.contains(\"dmb-selected\")) {\n      _dmbTrackRotHandle(el); return;\n    }\n    // (drag no longer hides the map edit bar \u2014 it stays open while moving/resizing)\n    hideTextToolbar();\n    hideFrameToolbar();\n    dmbCloseShapeEditBar(); // close shape bar when map bar opens\n    // Deselect any previously selected map\n    document.querySelectorAll(\".dmb-map-object.dmb-selected\").forEach((m) => {\n      if (m !== el) {\n        m.classList.remove(\"dmb-selected\");\n        m._dmbRotHandle?.style && (m._dmbRotHandle.style.display = \"none\");\n      }\n    });\n\n    dmbEditingMapObj = obj;\n    dmbEditingMapEl = el;\n    el.classList.add(\"dmb-selected\");\n    // Show rotation handle if stored\n    if (el._dmbRotHandle) el._dmbRotHandle.style.display = \"\";\n\n    const bar = document.getElementById(\"dmbMapEditBar\");\n    if (!bar) return;\n\n    document.getElementById(\"dmbEditFill\").value = obj.fill || \"#d9d9d9\";\n    document.getElementById(\"dmbEditStroke\").value = obj.stroke || \"#111111\";\n    try { _dmbSyncMapDots(); } catch (e) {}\n    document.getElementById(\"dmbEditWidth\").value = obj.strokeWidth ?? 1.2;\n    document.getElementById(\"dmbEditWidthVal\").textContent = obj.strokeWidth ?? 1.2;\n    document.getElementById(\"dmbEditStrokeStyle\").value = obj.strokeDasharray || \"\";\n    document.getElementById(\"dmbEditOpacity\").value = obj.opacity ?? 1;\n    document.getElementById(\"dmbEditOpacityVal\").textContent = Math.round((obj.opacity ?? 1) * 100) + \"%\";\n    const ooEl = document.getElementById(\"dmbEditShowOutline\");\n    if (ooEl) ooEl.checked = obj.showOutline || false;\n    const hpEl = document.getElementById(\"dmbEditHideProvinces\");\n    if (hpEl) hpEl.checked = obj.hideProvinces || false;\n    // Continent maps subdivide by country, so the \"provinces\" toggle is really the\n    // country-borders toggle \u2014 relabel it.\n    const hpLbl = document.getElementById(\"dmbEditHideProvincesLbl\");\n    if (hpLbl) hpLbl.textContent = dmbIsContinentKey(obj.mapKey || \"\") ? \"No borders\" : \"No provinces\";\n    const tfEl = document.getElementById(\"dmbEditTransparentFill\");\n    if (tfEl) tfEl.checked = obj.transparentFill || false;\n    try { _dmbSyncMapDots(); } catch (e) {}\n    try { _dmbSyncMapModeUI(); } catch (e) {}\n\n    // Update contain button text\n    const _mcBtn = document.getElementById(\"dmbMapContainBtn\");\n    if (_mcBtn) _mcBtn.textContent = _dmbContainBtnText(obj, !!el.closest(\".dmb-cover-preview\"));\n\n    /* Dock the bar into the header's empty space (top-left), not over the canvas. */\n    bar.style.display = \"block\";\n    _dmbDockBar(bar);\n  }\n\n  function dmbCloseEditBar() {\n    const bar = document.getElementById(\"dmbMapEditBar\");\n    if (bar) bar.style.display = \"none\";\n    if (dmbEditingMapEl) {\n      dmbEditingMapEl.classList.remove(\"dmb-selected\");\n      if (dmbEditingMapEl._dmbRotHandle) dmbEditingMapEl._dmbRotHandle.style.display = \"none\";\n    }\n    dmbEditingMapObj = null;\n    dmbEditingMapEl = null;\n    try { _dmbUpdateRibbonPlaceholder(); } catch (e) {}\n  }\n\n  function initMapEditBar() {\n    const bar = document.getElementById(\"dmbMapEditBar\");\n    if (!bar || bar.dataset.dmbBarInit) return;\n    bar.dataset.dmbBarInit = \"1\";\n\n    [\"dmbEditFill\",\"dmbEditStroke\",\"dmbEditWidth\",\"dmbEditStrokeStyle\",\"dmbEditOpacity\",\"dmbEditShowOutline\",\"dmbEditHideProvinces\",\"dmbEditTransparentFill\"].forEach((id) => {\n      document.getElementById(id)?.addEventListener(\"input\", dmbApplyEditBarToObj);\n      document.getElementById(id)?.addEventListener(\"change\", dmbApplyEditBarToObj);\n    });\n\n    // Fill-mode segmented (Solid / Outline / None) \u2192 drive the hidden checkboxes.\n    document.querySelectorAll(\"#dmbEditFillMode .dmb-map-seg-btn\").forEach((btn) => {\n      btn.addEventListener(\"click\", () => {\n        const m = btn.dataset.fillmode;\n        const oo = document.getElementById(\"dmbEditShowOutline\");\n        const tf = document.getElementById(\"dmbEditTransparentFill\");\n        if (oo) oo.checked = (m === \"outline\");\n        if (tf) tf.checked = (m === \"outline\" || m === \"none\");\n        dmbApplyEditBarToObj();\n        _dmbSyncMapModeUI();\n      });\n    });\n    // Borders toggle \u2192 hideProvinces (inverse). (Legacy control \u2014 null-safe if absent.)\n    document.getElementById(\"dmbEditBordersBtn\")?.addEventListener(\"click\", () => {\n      const hp = document.getElementById(\"dmbEditHideProvinces\");\n      if (hp) hp.checked = !hp.checked;\n      dmbApplyEditBarToObj();\n      _dmbSyncMapModeUI();\n    });\n\n    // \"Map options\" dropdown (show outline / transparent fill / no provinces).\n    const _mOptToggle = document.getElementById(\"dmbMapOptsToggle\");\n    const _mOptMenu   = document.getElementById(\"dmbMapOptsMenu\");\n    if (_mOptToggle && _mOptMenu) {\n      const _isMob = () => window.matchMedia && window.matchMedia(\"(max-width: 749px), (orientation: portrait) and (pointer: coarse) and (max-width: 1024px)\").matches;\n      let _mOptSlot = null;  // placeholder marking the menu's home inside the edit bar\n      // Phones: lift the menu out of the ribbon onto <body> BEFORE showing it, so it\n      // can't be trapped by the ribbon's overflow/stacking. This is the same fix that\n      // made the colour sheet reliable on iOS (\"nothing happens on tap\") \u2014 the menu\n      // used to depend on the popover-observer catching it while still inside the\n      // horizontally-scrolling ribbon, which rendered it off-screen. Once on <body>\n      // the shared observer pins it as a bottom sheet reliably. Desktop is untouched.\n      function _openMapOpts() {\n        if (_isMob() && _mOptMenu.parentElement !== document.body) {\n          _mOptSlot = document.createComment(\"dmb-mapopts-home\");\n          _mOptMenu.parentElement.insertBefore(_mOptSlot, _mOptMenu);\n          document.body.appendChild(_mOptMenu);\n        }\n        _mOptMenu.hidden = false;\n      }\n      function _closeMapOpts() {\n        _mOptMenu.hidden = true;\n        if (_mOptSlot && _mOptSlot.parentElement) {           // return it home\n          _mOptSlot.parentElement.insertBefore(_mOptMenu, _mOptSlot);\n          _mOptSlot.remove(); _mOptSlot = null;\n        }\n      }\n      _mOptToggle.addEventListener(\"click\", (e) => {\n        e.preventDefault(); e.stopPropagation();\n        if (_mOptMenu.hidden) _openMapOpts(); else _closeMapOpts();\n      });\n      document.addEventListener(\"click\", (e) => {\n        if (_mOptMenu.hidden) return;\n        if (e.target === _mOptToggle || e.target.closest(\"#dmbMapOptsToggle\")) return;\n        if (e.target.closest(\"#dmbMapOptsMenu\") || e.target.closest(\"#dmbMapOptsDD\")) return;\n        _closeMapOpts();\n      });\n    }\n\n    // Colour-wheel palettes for Fill + Border (recent swatches + custom picker) \u2014 the\n    // same book colour palette used by text/shapes/stickers.\n    [[\"dmbEditFillBtn\",\"dmbEditFillPop\",\"dmbEditFillCustom\",\"dmbEditFill\"],\n     [\"dmbEditStrokeBtn\",\"dmbEditStrokePop\",\"dmbEditStrokeCustom\",\"dmbEditStroke\"]\n    ].forEach(([btnId, popId, customId, inputId]) => {\n      const btn = document.getElementById(btnId);\n      const pop = document.getElementById(popId);\n      const custom = document.getElementById(customId);\n      const input = document.getElementById(inputId);\n      if (btn && pop) {\n        btn.addEventListener(\"click\", (e) => {\n          e.preventDefault(); e.stopPropagation();\n          const willOpen = pop.hidden;\n          [\"dmbEditFillPop\",\"dmbEditStrokePop\"].forEach((id) => { const p = document.getElementById(id); if (p && p !== pop) p.hidden = true; });\n          pop.hidden = !willOpen;\n        });\n        pop.addEventListener(\"click\", (e) => { if (e.target.closest(\".dmb-recent-swatch\")) pop.hidden = true; });\n      }\n      if (custom && input) {\n        custom.addEventListener(\"click\", (e) => { e.stopPropagation(); if (e.target === input) return; try { input.click(); } catch (e2) {} });   // identical to the background wheel \u2014 NO preventDefault (it cancels the native picker on iOS)\n      }\n      if (input && pop) {\n        try { dmbEnhanceColorInput(input, pop); } catch (e) {}\n        try { if (custom) pop.appendChild(custom); } catch (e) {}\n        try { _dmbColorTapFix(custom, input); } catch (e) {}\n        input.addEventListener(\"input\", () => { try { _dmbSyncMapDots(); } catch (e) {} });\n        input.addEventListener(\"change\", () => { pop.hidden = true; });\n      }\n    });\n    document.addEventListener(\"click\", (e) => {\n      if (!e.target.closest(\"#dmbEditFillWrap\"))   { const p = document.getElementById(\"dmbEditFillPop\");   if (p) p.hidden = true; }\n      if (!e.target.closest(\"#dmbEditStrokeWrap\")) { const p = document.getElementById(\"dmbEditStrokePop\"); if (p) p.hidden = true; }\n    });\n\n    document.getElementById(\"dmbEditDone\")?.addEventListener(\"click\", dmbCloseEditBar);\n\n    // Contain-to-page button\n    document.getElementById(\"dmbMapContainBtn\")?.addEventListener(\"click\", () => {\n      if (!dmbEditingMapObj || !dmbEditingMapEl) return;\n      const _isCover = !!dmbEditingMapEl.closest(\".dmb-cover-preview\");\n      _dmbContainCycle(dmbEditingMapObj, dmbEditingMapEl, _isCover);\n    });\n\n    [\"left\", \"center\", \"right\"].forEach(function(align) {\n      const btnId = { left: \"dmbEditAlignLeft\", center: \"dmbEditAlignCenter\", right: \"dmbEditAlignRight\" }[align];\n      document.getElementById(btnId)?.addEventListener(\"click\", () => {\n        if (!dmbEditingMapObj || !dmbEditingMapEl) return;\n        const tType = dmbEditingMapEl.closest(\".dmb-cover-preview\") ? \"cover\" : \"page\";\n        dmbAlignObjectInZone(dmbEditingMapEl, dmbEditingMapObj, align, tType);\n        _dmbDockBar(document.getElementById(\"dmbMapEditBar\"));\n      });\n    });\n\n    document.getElementById(\"dmbEditFrontBtn\")?.addEventListener(\"click\", () => {\n      _dmbDoLayerStep(dmbEditingMapEl, \"forward\");\n    });\n\n    document.getElementById(\"dmbEditBackBtn\")?.addEventListener(\"click\", () => {\n      _dmbDoLayerStep(dmbEditingMapEl, \"backward\");\n    });\n\n    document.getElementById(\"dmbEditDelete\")?.addEventListener(\"click\", () => {\n      if (!dmbEditingMapObj) return;\n      const objId = dmbEditingMapObj.id;\n      // pushUndoState makes the deletion undoable AND persists it to the restore key\n      // (via the flush in pushUndoState). Without it, deleting a map was NOT saved \u2014\n      // on refresh the deleted map reappeared (the saved copy still had it).\n      pushUndoState(\"Delete Map\");\n      if (editor.pageObjects) {\n        Object.keys(editor.pageObjects).forEach((p) => {\n          const arr = editor.pageObjects[p];\n          if (Array.isArray(arr)) {\n            const idx = arr.findIndex((o) => o.id === objId);\n            if (idx !== -1) arr.splice(idx, 1);\n          }\n        });\n      }\n      if (Array.isArray(editor.coverObjects)) {\n        const idx = editor.coverObjects.findIndex((o) => o.id === objId);\n        if (idx !== -1) editor.coverObjects.splice(idx, 1);\n      }\n      dmbCloseEditBar();\n      if (typeof renderBuilderCanvas === \"function\") renderBuilderCanvas();\n    });\n\n    // Close edit bar when pointer lands outside both the bar and any map object.\n    // Use capture so stopPropagation on stickers/frames/text doesn't prevent this.\n    document.addEventListener(\"pointerdown\", (e) => {\n      if (!dmbEditingMapObj) return;\n      const bar = document.getElementById(\"dmbMapEditBar\");\n      if (bar && bar.contains(e.target)) return; // clicked inside edit bar\n      if (e.target.closest(\".dmb-stage-header\")) return; // anywhere in the fixed ribbon\n      if (e.target.closest(\"#dmbMshRibbon\")) return; // mobile ribbon (idle + docked bars)\n      if (e.target.closest(\"#dmbMshColorSheet\")) return; // mobile body colour sheet \u2014 keep the map selected\n      if (e.target.closest(\"#dmbMapOptsMenu\")) return; // \"Map options\" popover (moved to <body> on phones) \u2014 keep the map selected so its toggles apply\n      if (e.target.closest(\".dmb-map-object\")) return; // clicked on a map\n      dmbCloseEditBar();\n    }, { capture: true });\n  }\n\n  /* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n     SHAPE EDIT BAR \u2014 open / close / apply / init\n     WARNING: All changes to shape appearance must go through dmbApplyShapeEditBar.\n     Do NOT update obj.fill/stroke/etc. from anywhere else \u2014 always update the\n     bar controls first then call dmbApplyShapeEditBar() so the SVG stays in sync.\n     \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n  // Return SVG string for a given shape type + style properties.\n  // viewBox 0 0 100 100; caller wraps in a div of the desired pixel size.\n  // Turn a border-style keyword + stroke width into an SVG dash attribute string.\n  // Dash lengths scale with the stroke width so they look right at any thickness.\n  // Values are in screen px because every shape stroke uses non-scaling-stroke.\n  function _dmbShapeDashAttrs(strokeStyle, sw) {\n    const w = Math.max(1, sw || 1);\n    if (strokeStyle === \"dashed\") return `stroke-dasharray=\"${(w * 2.4).toFixed(1)},${(w * 1.7).toFixed(1)}\"`;\n    if (strokeStyle === \"dotted\") return `stroke-dasharray=\"0.1,${(w * 1.9).toFixed(1)}\" stroke-linecap=\"round\"`;\n    return \"\";\n  }\n\n  // \u2500\u2500 LINE subsystem (endpoint-based) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // A line is stored with two endpoints (x1,y1)-(x2,y2) in page/container coords,\n  // a stroke `thickness`, and a `lineType` (\"straight\" | \"elbowed\" | \"curved\").\n  // Its bounding box (x,y,w,h; rotation always 0) is kept in sync so the rest of\n  // the system (move / snap / z-order / duplicate / PDF) keeps working.\n  function _dmbLinePad(obj) { return Math.max(2, (obj.thickness || obj.h || 8) / 2 + 3); }\n  // Migrate an old-style line (rotated rect: w=length, h=thickness, rotation=angle)\n  // to the endpoint model. No-op if already migrated.\n  function _dmbLineMigrate(obj) {\n    if (obj.x1 != null && obj.y1 != null && obj.x2 != null && obj.y2 != null) {\n      if (obj.thickness == null) obj.thickness = obj.h || 8;\n      if (!obj.lineType) obj.lineType = \"straight\";\n      return;\n    }\n    const w = obj.w || 100, h = obj.h || 8;\n    const cx = (obj.x || 0) + w / 2, cy = (obj.y || 0) + h / 2;\n    const th = (obj.rotation || 0) * Math.PI / 180;\n    const hx = Math.cos(th) * w / 2, hy = Math.sin(th) * w / 2;\n    obj.x1 = cx - hx; obj.y1 = cy - hy;\n    obj.x2 = cx + hx; obj.y2 = cy + hy;\n    obj.thickness = h;\n    obj.lineType = obj.lineType || \"straight\";\n    obj.rotation = 0;\n  }\n  function _dmbLineCtrlPoint(a, b) {\n    const mx = (a.x + b.x) / 2, my = (a.y + b.y) / 2;\n    const dx = b.x - a.x, dy = b.y - a.y, len = Math.hypot(dx, dy) || 1;\n    const nx = -dy / len, ny = dx / len;               // perpendicular unit\n    const off = Math.min(140, len * 0.28);\n    return { x: mx + nx * off, y: my + ny * off };\n  }\n  function _dmbLineElbowPoint(a, b) {\n    return (Math.abs(b.x - a.x) >= Math.abs(b.y - a.y)) ? { x: b.x, y: a.y } : { x: a.x, y: b.y };\n  }\n  // Curved lines carry a draggable on-curve handle (bx,by) \u2014 the point the curve\n  // passes THROUGH at its middle. Default = a gentle perpendicular bow.\n  function _dmbLineEnsureCurve(obj) {\n    if (obj.lineType !== \"curved\") return;\n    if (obj.bx != null && obj.by != null) return;\n    const ax = obj.x1, ay = obj.y1, bx2 = obj.x2, by2 = obj.y2;\n    const mx = (ax + bx2) / 2, my = (ay + by2) / 2;\n    const dx = bx2 - ax, dy = by2 - ay, len = Math.hypot(dx, dy) || 1;\n    const nx = -dy / len, ny = dx / len;\n    const off = Math.max(14, Math.min(140, len * 0.22));\n    obj.bx = mx + nx * off; obj.by = my + ny * off;\n  }\n  // Quadratic control so the curve passes through the handle (bx,by) at t=0.5:\n  //   B(0.5) = 0.25*P0 + 0.5*C + 0.25*P2  =>  C = 2H - 0.5*(P0+P2)\n  function _dmbLineQuadControl(obj) {\n    return { x: 2 * obj.bx - 0.5 * (obj.x1 + obj.x2), y: 2 * obj.by - 0.5 * (obj.y1 + obj.y2) };\n  }\n  // Decompose the handle into {t along chord, perp signed distance} so it can be\n  // re-placed proportionally after an endpoint moves (keeps the curve's shape).\n  function _dmbLineDecompose(obj) {\n    const ax = obj.x1, ay = obj.y1, dx = obj.x2 - ax, dy = obj.y2 - ay;\n    const len2 = dx * dx + dy * dy || 1, len = Math.sqrt(len2);\n    const t = ((obj.bx - ax) * dx + (obj.by - ay) * dy) / len2;\n    const px = ax + dx * t, py = ay + dy * t;\n    const nx = -dy / len, ny = dx / len;\n    const perp = (obj.bx - px) * nx + (obj.by - py) * ny;\n    return { t, perp };\n  }\n  function _dmbLineRecompose(obj, d) {\n    const ax = obj.x1, ay = obj.y1, dx = obj.x2 - ax, dy = obj.y2 - ay;\n    const len = Math.hypot(dx, dy) || 1;\n    const nx = -dy / len, ny = dx / len;\n    const px = ax + dx * d.t, py = ay + dy * d.t;\n    obj.bx = px + nx * d.perp; obj.by = py + ny * d.perp;\n  }\n  function _dmbLineRecomputeBox(obj) {\n    _dmbLineMigrate(obj);\n    const a = { x: obj.x1, y: obj.y1 }, b = { x: obj.x2, y: obj.y2 };\n    const pts = [a, b];\n    if (obj.lineType === \"curved\") { _dmbLineEnsureCurve(obj); pts.push(_dmbLineQuadControl(obj)); pts.push({ x: obj.bx, y: obj.by }); }\n    else if (obj.lineType === \"elbowed\") pts.push(_dmbLineElbowPoint(a, b));\n    const pad = _dmbLinePad(obj);\n    const minX = Math.min.apply(null, pts.map(p => p.x)) - pad;\n    const minY = Math.min.apply(null, pts.map(p => p.y)) - pad;\n    const maxX = Math.max.apply(null, pts.map(p => p.x)) + pad;\n    const maxY = Math.max.apply(null, pts.map(p => p.y)) + pad;\n    obj.x = minX; obj.y = minY;\n    obj.w = Math.max(1, maxX - minX); obj.h = Math.max(1, maxY - minY);\n    obj.rotation = 0;\n  }\n  function _dmbLineDash(style, tw) {\n    if (style === \"dashed\") return `stroke-dasharray=\"${(tw * 2.4).toFixed(1)},${(tw * 1.7).toFixed(1)}\"`;\n    if (style === \"dotted\") return `stroke-dasharray=\"0.1,${(tw * 1.9).toFixed(1)}\"`;\n    return \"\";\n  }\n  function buildLineSvg(obj) {\n    _dmbLineRecomputeBox(obj);\n    const a = { x: obj.x1 - obj.x, y: obj.y1 - obj.y };\n    const b = { x: obj.x2 - obj.x, y: obj.y2 - obj.y };\n    const col = (obj.fill && obj.fill !== \"none\" && obj.fill !== \"transparent\") ? obj.fill : (obj.stroke || \"#111111\");\n    const tw = Math.max(1, obj.thickness || 8);\n    let d;\n    if (obj.lineType === \"curved\") {\n      _dmbLineEnsureCurve(obj);\n      const c = _dmbLineQuadControl(obj);\n      const lc = { x: c.x - obj.x, y: c.y - obj.y };\n      d = `M${a.x.toFixed(1)} ${a.y.toFixed(1)} Q ${lc.x.toFixed(1)} ${lc.y.toFixed(1)} ${b.x.toFixed(1)} ${b.y.toFixed(1)}`;\n    } else if (obj.lineType === \"elbowed\") {\n      const e = _dmbLineElbowPoint({ x: obj.x1, y: obj.y1 }, { x: obj.x2, y: obj.y2 });\n      const le = { x: e.x - obj.x, y: e.y - obj.y };\n      d = `M${a.x.toFixed(1)} ${a.y.toFixed(1)} L ${le.x.toFixed(1)} ${le.y.toFixed(1)} L ${b.x.toFixed(1)} ${b.y.toFixed(1)}`;\n    } else {\n      d = `M${a.x.toFixed(1)} ${a.y.toFixed(1)} L ${b.x.toFixed(1)} ${b.y.toFixed(1)}`;\n    }\n    const op = (obj.opacity != null) ? obj.opacity : 1;\n    return `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 ${obj.w.toFixed(1)} ${obj.h.toFixed(1)}\" width=\"100%\" height=\"100%\" preserveAspectRatio=\"none\" style=\"display:block;overflow:visible;opacity:${op};\"><path d=\"${d}\" fill=\"none\" stroke=\"${col}\" stroke-width=\"${tw}\" stroke-linecap=\"round\" stroke-linejoin=\"round\" ${_dmbLineDash(obj.strokeStyle, tw)}/></svg>`;\n  }\n  // Rebuild the SVG + reposition the 2 endpoint handles + resize the wrapper.\n  function _dmbLineRefreshEl(obj, el) {\n    if (!el) return;\n    _dmbLineRecomputeBox(obj);\n    el.style.left = obj.x + \"px\"; el.style.top = obj.y + \"px\";\n    el.style.width = obj.w + \"px\"; el.style.height = obj.h + \"px\";\n    const old = el.querySelector(\"svg\");\n    const tmp = document.createElement(\"div\"); tmp.innerHTML = buildLineSvg(obj);\n    const neu = tmp.firstElementChild;\n    if (old && neu) old.replaceWith(neu); else if (neu) el.insertBefore(neu, el.firstChild);\n    const h1 = el.querySelector('.dmb-line-end-handle[data-end=\"1\"]');\n    const h2 = el.querySelector('.dmb-line-end-handle[data-end=\"2\"]');\n    if (h1) { h1.style.left = (obj.x1 - obj.x) + \"px\"; h1.style.top = (obj.y1 - obj.y) + \"px\"; }\n    if (h2) { h2.style.left = (obj.x2 - obj.x) + \"px\"; h2.style.top = (obj.y2 - obj.y) + \"px\"; }\n    // Curve control handle (CSS shows it only when the line is selected AND curved).\n    el.classList.toggle(\"dmb-line-curved\", obj.lineType === \"curved\");\n    const hm = el.querySelector('.dmb-line-mid-handle');\n    if (hm && obj.lineType === \"curved\") {\n      _dmbLineEnsureCurve(obj);\n      hm.style.left = (obj.bx - obj.x) + \"px\"; hm.style.top = (obj.by - obj.y) + \"px\";\n    }\n  }\n  // Render a line object into the canvas: wrapper + line SVG + 2 endpoint handles,\n  // whole-line move drag, and per-endpoint drag. Selection opens the shape edit bar.\n  function _dmbRenderLineObject(obj, container, targetType, pageNumber, zone) {\n    _dmbLineRecomputeBox(obj);\n    const wrap = document.createElement(\"div\");\n    wrap.className = \"dmb-shape-object dmb-line-object\";\n    wrap.dataset.objectId = obj.id;\n    const _lz = (obj.z === 0 || obj.z) ? obj.z : 30;   // 0 is a real z (\"send to back\")\n    wrap.dataset.z = String(_lz);\n    wrap.style.cssText = `position:absolute;left:${obj.x}px;top:${obj.y}px;width:${obj.w}px;height:${obj.h}px;z-index:${_lz};transform-origin:center center;overflow:visible;cursor:move;`;\n    wrap.innerHTML = buildLineSvg(obj);\n\n    [\"1\", \"2\"].forEach((which) => {\n      const h = document.createElement(\"div\");\n      h.className = \"dmb-line-end-handle\";\n      h.dataset.end = which;\n      h.style.left = ((which === \"1\" ? obj.x1 : obj.x2) - obj.x) + \"px\";\n      h.style.top  = ((which === \"1\" ? obj.y1 : obj.y2) - obj.y) + \"px\";\n      wrap.appendChild(h);\n    });\n\n    // Curve control handle (the on-curve point the customer drags to bend the line).\n    const hMid = document.createElement(\"div\");\n    hMid.className = \"dmb-line-mid-handle\";\n    wrap.appendChild(hMid);\n    if (obj.lineType === \"curved\") {\n      _dmbLineEnsureCurve(obj);\n      wrap.classList.add(\"dmb-line-curved\");\n      hMid.style.left = (obj.bx - obj.x) + \"px\"; hMid.style.top = (obj.by - obj.y) + \"px\";\n    }\n\n    function repositionBar() {\n      const bar = document.getElementById(\"dmbShapeEditBar\");\n      if (!bar || bar.style.display === \"none\") return;\n      _dmbDockBar(bar);   // docked at the top \u2014 stays put while editing\n    }\n\n    wrap.addEventListener(\"click\", (e) => {\n      if (e.target.closest(\".dmb-line-end-handle,.dmb-line-mid-handle\")) return;\n      e.stopPropagation();\n      dmbOpenShapeEditBar(obj, wrap);\n    });\n\n    // Endpoint drag \u2014 keeps the curve's shape proportional (decompose/recompose).\n    wrap.querySelectorAll(\".dmb-line-end-handle\").forEach((h) => {\n      h.addEventListener(\"pointerdown\", (e) => {\n        e.preventDefault(); e.stopPropagation();\n        dmbOpenShapeEditBar(obj, wrap);\n        const which = h.dataset.end;\n        const _Z = _nodeOnScreenScale(wrap) || 1;\n        const cRect = container.getBoundingClientRect();\n        const _cW = container.offsetWidth  || 270;\n        const _cH = container.offsetHeight || 382;\n        const _curveD = (obj.lineType === \"curved\" && obj.bx != null) ? _dmbLineDecompose(obj) : null;\n        // A line drawn across the fold could not be EDITED across it: this clamped the\n        // endpoint to [0, _cW], its own page, so the first tug on either end snapped the\n        // whole line back onto one sheet and it could never reach over again. Endpoints\n        // now share the spread with everything else that spans. (2026-09-14)\n        const _epX = (targetType === \"page\") ? _dmbPointRangeX(pageNumber, _cW) : { lo: 0, hi: _cW };\n        const onMove = (ev) => {\n          // Y still clamps to the sheet - top and bottom are real edges on any page.\n          const px = Math.max(_epX.lo, Math.min(_epX.hi, (ev.clientX - cRect.left) / _Z));\n          const py = Math.max(0, Math.min(_cH, (ev.clientY - cRect.top)  / _Z));\n          if (which === \"1\") { obj.x1 = px; obj.y1 = py; } else { obj.x2 = px; obj.y2 = py; }\n          if (_curveD) _dmbLineRecompose(obj, _curveD);\n          _dmbLineRefreshEl(obj, wrap);\n          repositionBar();\n        };\n        const onUp = () => {\n          document.removeEventListener(\"pointermove\", onMove);\n          document.removeEventListener(\"pointerup\", onUp);\n          pushUndoState(\"Edit Line\");\n        };\n        document.addEventListener(\"pointermove\", onMove);\n        document.addEventListener(\"pointerup\", onUp);\n      });\n    });\n\n    // Curve control-point drag \u2014 moves the on-curve handle (bx,by).\n    hMid.addEventListener(\"pointerdown\", (e) => {\n      e.preventDefault(); e.stopPropagation();\n      dmbOpenShapeEditBar(obj, wrap);\n      const _Z = _nodeOnScreenScale(wrap) || 1;\n      const cRect = container.getBoundingClientRect();\n      const _cW = container.offsetWidth  || 270;\n      const _cH = container.offsetHeight || 382;\n      // The third handle. Its two neighbours (the endpoints and the whole-line drag)\n      // were freed to cross the fold; this one still pinned the curve's control point to\n      // its own page, so on a right-hand page bx floored at 0 and the bulge of a curve\n      // could never be placed on the facing sheet at all. Worse, _dmbLineRecomputeBox\n      // derives obj.x/obj.w FROM x1/x2/bx, so clamping bx silently shifted and shrank the\n      // line's box mid-drag - which then feeds the overhang and z-index logic. (2026-09-14)\n      const _cpX = (targetType === \"page\") ? _dmbPointRangeX(pageNumber, _cW) : { lo: 0, hi: _cW };\n      const onMove = (ev) => {\n        obj.bx = Math.max(_cpX.lo, Math.min(_cpX.hi, (ev.clientX - cRect.left) / _Z));\n        obj.by = Math.max(0, Math.min(_cH, (ev.clientY - cRect.top)  / _Z));\n        _dmbLineRefreshEl(obj, wrap);\n        repositionBar();\n      };\n      const onUp = () => {\n        document.removeEventListener(\"pointermove\", onMove);\n        document.removeEventListener(\"pointerup\", onUp);\n        pushUndoState(\"Edit Line\");\n      };\n      document.addEventListener(\"pointermove\", onMove);\n      document.addEventListener(\"pointerup\", onUp);\n    });\n\n    // Whole-line move\n    wrap.addEventListener(\"pointerdown\", (e) => {\n      if (e.target.closest(\".dmb-line-end-handle,.dmb-line-mid-handle\")) return;\n      e.preventDefault(); e.stopPropagation();\n      dmbOpenShapeEditBar(obj, wrap);\n      const _Z = _nodeOnScreenScale(wrap) || 1;\n      const sx = e.clientX, sy = e.clientY;\n      const o = { x1: obj.x1, y1: obj.y1, x2: obj.x2, y2: obj.y2, bx: obj.bx, by: obj.by };\n      // Clamp the whole-line move by DELTA (clamping the endpoints one by one would\n      // skew the line instead of moving it). Same rule as the shape drag: the bbox\n      // may hang at most half off \u2014 that is deliberate bleed \u2014 but never further.\n      const _b0 = { x: obj.x, y: obj.y, w: obj.w, h: obj.h };\n      const _cW = container.offsetWidth  || 270;\n      const _cH = container.offsetHeight || 382;\n      // Same spread-wide range the shape and map drags use, so a spanning line can be\n      // moved along the fold instead of being pulled back onto one page.\n      const _rX  = (targetType === \"page\") ? _dmbObjDragRangeX(obj, pageNumber, _cW)\n                                           : { lo: -_b0.w / 2, hi: _cW - _b0.w / 2 };\n      const _loX = _rX.lo - _b0.x, _hiX = _rX.hi - _b0.x;\n      const _loY = -_b0.h / 2 - _b0.y, _hiY = _cH - _b0.h / 2 - _b0.y;\n      const onMove = (ev) => {\n        let dx = (ev.clientX - sx) / _Z, dy = (ev.clientY - sy) / _Z;\n        if (_hiX > _loX) dx = Math.max(_loX, Math.min(_hiX, dx));\n        if (_hiY > _loY) dy = Math.max(_loY, Math.min(_hiY, dy));\n        obj.x1 = o.x1 + dx; obj.y1 = o.y1 + dy; obj.x2 = o.x2 + dx; obj.y2 = o.y2 + dy;\n        if (o.bx != null) { obj.bx = o.bx + dx; obj.by = o.by + dy; }\n        _dmbLineRefreshEl(obj, wrap);\n        repositionBar();\n      };\n      const onUp = async () => {\n        document.removeEventListener(\"pointermove\", onMove);\n        document.removeEventListener(\"pointerup\", onUp);\n        pushUndoState(\"Move Line\");\n        if (targetType === \"cover\") { try { _dmbAutoAssignCoverZone(obj, wrap); } catch (e2) {} }\n        else if (targetType === \"page\" && typeof pageNumber === \"number\") {\n          try { await dmbHandleCrossPageMove(obj, pageNumber); } catch (e2) {}\n        }\n      };\n      document.addEventListener(\"pointermove\", onMove);\n      document.addEventListener(\"pointerup\", onUp);\n    });\n\n    container.appendChild(wrap);\n  }\n\n  function buildShapeSvg(shapeType, fill, stroke, strokeWidth, opacity, cornerRadius, w, h, strokeStyle) {\n    const f  = fill        || \"#6366f1\";\n    const s  = stroke      || \"transparent\";\n    const sw = strokeWidth || 0;\n    const op = (opacity !== undefined && opacity !== null) ? opacity : 1;\n    const half = sw / 2;\n    // For rect/rounded-rect: inset the stroked path by sw/2 REAL PIXELS so the\n    // border's outer edge hugs the shape box at ANY size (and matches the PDF,\n    // which insets by sw/2 px). The 0-100 viewBox is stretched to w\u00D7h px, so a\n    // viewBox inset of (50*sw/w) == sw/2 px on X and (50*sw/h) == sw/2 px on Y.\n    // A constant `half` only lined up when the box was ~100px; on big shapes it\n    // scaled up into a visible gap between the box edge and the border. Falls back\n    // to `half` when w/h are unknown.\n    const ixR = (w && w > 0) ? (50 * sw / w) : half;\n    const iyR = (h && h > 0) ? (50 * sw / h) : half;\n    // vector-effect=\"non-scaling-stroke\" on every inner element:\n    // With preserveAspectRatio=\"none\", the X and Y scales differ for non-square shapes,\n    // making stroke-width render thicker on one axis than the other. non-scaling-stroke\n    // keeps the stroke exactly `sw` screen-pixels wide on ALL sides regardless of shape\n    // aspect ratio \u2014 perfectly uniform borders every time.\n    const vens = `vector-effect=\"non-scaling-stroke\" ${_dmbShapeDashAttrs(strokeStyle, sw)}`;\n    let inner = \"\";\n    switch (shapeType) {\n      case \"rect\":\n        // stroke-linejoin=\"miter\" \u2192 sharp 90\u00B0 outer stroke corners (not rounded)\n        inner = `<rect x=\"${ixR}\" y=\"${iyR}\" width=\"${100 - 2 * ixR}\" height=\"${100 - 2 * iyR}\" rx=\"0\" ry=\"0\" fill=\"${f}\" stroke=\"${s}\" stroke-width=\"${sw}\" stroke-linejoin=\"miter\" ${vens}/>`;\n        break;\n      case \"rounded-rect\": {\n        // cx clamped 0-50: 50 = perfect semicircle; stored as viewBox % of 0-100 space\n        const _cr = Math.max(0, Math.min(50, (cornerRadius !== undefined && cornerRadius !== null) ? cornerRadius : 12));\n        inner = `<rect x=\"${ixR}\" y=\"${iyR}\" width=\"${100 - 2 * ixR}\" height=\"${100 - 2 * iyR}\" rx=\"${_cr}\" ry=\"${_cr}\" fill=\"${f}\" stroke=\"${s}\" stroke-width=\"${sw}\" stroke-linejoin=\"round\" ${vens}/>`;\n        break;\n      }\n      case \"circle\":\n        inner = `<ellipse cx=\"50\" cy=\"50\" rx=\"${50 - half}\" ry=\"${50 - half}\" fill=\"${f}\" stroke=\"${s}\" stroke-width=\"${sw}\" ${vens}/>`;\n        break;\n      case \"triangle\":\n        inner = `<polygon points=\"50,${half} ${100-half},${100-half} ${half},${100-half}\" fill=\"${f}\" stroke=\"${s}\" stroke-width=\"${sw}\" stroke-linejoin=\"round\" ${vens}/>`;\n        break;\n      case \"star\":\n        inner = `<polygon points=\"50,2 61.8,33.8 95.6,35.2 69,56.2 78.2,88.8 50,70 21.8,88.8 31,56.2 4.4,35.2 38.2,33.8\" fill=\"${f}\" stroke=\"${s}\" stroke-width=\"${sw}\" stroke-linejoin=\"round\" ${vens}/>`;\n        break;\n      case \"heart\":\n        inner = `<path d=\"M50,85 C20,70 5,55 5,38 C5,22 16,10 28,10 C36,10 44,15 50,22 C56,15 64,10 72,10 C84,10 95,22 95,38 C95,55 80,70 50,85 Z\" fill=\"${f}\" stroke=\"${s}\" stroke-width=\"${sw}\" stroke-linejoin=\"round\" ${vens}/>`;\n        break;\n      case \"arrow\":\n        inner = `<polygon points=\"5,35 60,35 60,15 95,50 60,85 60,65 5,65\" fill=\"${f}\" stroke=\"${s}\" stroke-width=\"${sw}\" stroke-linejoin=\"round\" ${vens}/>`;\n        break;\n      case \"pentagon\":\n        inner = `<polygon points=\"50,2 95.6,35.2 78.2,88.8 21.8,88.8 4.4,35.2\" fill=\"${f}\" stroke=\"${s}\" stroke-width=\"${sw}\" stroke-linejoin=\"round\" ${vens}/>`;\n        break;\n      case \"hexagon\":\n        inner = `<polygon points=\"98,50 74,91.6 26,91.6 2,50 26,8.4 74,8.4\" fill=\"${f}\" stroke=\"${s}\" stroke-width=\"${sw}\" stroke-linejoin=\"round\" ${vens}/>`;\n        break;\n      case \"line\":\n        inner = `<rect x=\"0\" y=\"0\" width=\"100\" height=\"100\" fill=\"${f}\" stroke=\"${s}\" stroke-width=\"${sw}\" stroke-linejoin=\"round\" ${vens}/>`;\n        break;\n      default:\n        inner = `<rect x=\"0\" y=\"0\" width=\"100\" height=\"100\" fill=\"${f}\" stroke=\"${s}\" stroke-width=\"${sw}\" stroke-linejoin=\"round\" ${vens}/>`;\n    }\n    return `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 100 100\" width=\"100%\" height=\"100%\" preserveAspectRatio=\"none\" style=\"display:block;overflow:visible;opacity:${op};\">${inner}</svg>`;\n  }\n\n  // Draw a shape on a Canvas 2D context at (0,0,dw,dh) \u2014 used for PDF rendering.\n  // ctx should already be saved/translated/rotated by the caller.\n  //\n  // UNITS \u2014 the whole correctness story lives here. On screen the shape is an SVG\n  // with viewBox \"0 0 100 100\" stretched to obj.w x obj.h CSS px, and EVERY inner\n  // element carries vector-effect=\"non-scaling-stroke\". So the border is exactly\n  // `strokeWidth` CSS px on all four sides however big or lopsided the box is, and\n  // the dash lengths from _dmbShapeDashAttrs are in those same CSS px.\n  //\n  // The PDF canvas is `pxScale` times bigger than the builder (PAGE_SCALE 9.19),\n  // so the printed pen must be strokeWidth * pxScale CANVAS px. Building the path\n  // inside ctx.scale(dw/100, dh/100) and stroking there \u2014 which is what this used\n  // to do \u2014 gets that wrong twice: the pen comes out strokeWidth * dw/100 wide\n  // (right only for a shape exactly 100 builder px across) and it is ELLIPTICAL\n  // whenever dw != dh, so the same outline is thicker on the top than on the sides.\n  // Rect-likes had the mirror-image bug: they stroked in canvas px with lineWidth\n  // = strokeWidth, i.e. ~9.2x TOO THIN, turning a 3 mm frame into a 0.3 mm hairline.\n  //\n  // Fix: scale the PATH COORDINATES and never scale the context. Fill and stroke\n  // both then happen in canvas px, so the pen is isotropic and correctly sized.\n  function drawShapeOnCanvas(ctx, shapeType, fill, stroke, strokeWidth, dw, dh, cornerRadius, strokeStyle, pxScale) {\n    const f  = fill        || \"#6366f1\";\n    const s  = (strokeWidth && strokeWidth > 0) ? (stroke || \"#000\") : \"transparent\";\n    const sw = strokeWidth || 0;                        // BUILDER px\n    const P  = (typeof pxScale === \"number\" && pxScale > 0) ? pxScale : 1;\n    const SX = dw / 100, SY = dh / 100;                 // viewBox unit -> canvas px\n    // The pen, in canvas px. Clamped so a very wide border can never swallow the box\n    // (the SVG side is clamped implicitly by the viewBox).\n    const swPx = Math.min(sw * P, Math.max(0, Math.min(dw, dh) * 0.99));\n    // Dash lengths are screen px on the SVG side (non-scaling-stroke) -> * P here.\n    // The dotted pattern's leading 0.1 is a screen px too and must scale with them.\n    const _wsw = Math.max(1, sw || 1) * P;\n    const _dashPx = strokeStyle === \"dashed\" ? [_wsw * 2.4, _wsw * 1.7]\n                  : strokeStyle === \"dotted\" ? [0.1 * P, _wsw * 1.9]\n                  : [];\n    const _dashCap = strokeStyle === \"dotted\" ? \"round\" : \"butt\";\n    // Path insets, mirroring buildShapeSvg exactly:\n    //   rect / rounded-rect  inset by sw/2 REAL px per side  (SVG x = 50*sw/w units)\n    //   circle / triangle    inset by sw/2 VIEWBOX units     (SVG `half`)\n    const inR   = swPx / 2;\n    const halfV = sw / 2;\n    ctx.save();\n    ctx.beginPath();\n    switch (shapeType) {\n      case \"rect\":\n        ctx.rect(inR, inR, Math.max(0, dw - swPx), Math.max(0, dh - swPx));\n        break;\n      case \"rounded-rect\": {\n        // cornerRadius is stored in viewBox % (0-50). SVG rx/ry in a stretched\n        // viewBox give ELLIPTICAL corners, so scale each axis independently.\n        const _crV = Math.max(0, Math.min(50, (cornerRadius !== undefined && cornerRadius !== null) ? cornerRadius : 12));\n        _dmbRoundRectPath(ctx, inR, inR, Math.max(0, dw - swPx), Math.max(0, dh - swPx), _crV * SX, _crV * SY);\n        break;\n      }\n      case \"circle\":\n        ctx.ellipse(50 * SX, 50 * SY,\n                    Math.max(0, 50 - halfV) * SX, Math.max(0, 50 - halfV) * SY,\n                    0, 0, Math.PI * 2);\n        break;\n      case \"triangle\":\n        ctx.moveTo(50 * SX, halfV * SY);\n        ctx.lineTo((100 - halfV) * SX, (100 - halfV) * SY);\n        ctx.lineTo(halfV * SX, (100 - halfV) * SY);\n        ctx.closePath();\n        break;\n      case \"star\": {\n        const starPts = [[50,2],[61.8,33.8],[95.6,35.2],[69,56.2],[78.2,88.8],[50,70],[21.8,88.8],[31,56.2],[4.4,35.2],[38.2,33.8]];\n        ctx.moveTo(starPts[0][0] * SX, starPts[0][1] * SY);\n        for (let i = 1; i < starPts.length; i++) ctx.lineTo(starPts[i][0] * SX, starPts[i][1] * SY);\n        ctx.closePath();\n        break;\n      }\n      case \"heart\":\n        ctx.moveTo(50 * SX, 85 * SY);\n        ctx.bezierCurveTo(20 * SX, 70 * SY, 5 * SX, 55 * SY, 5 * SX, 38 * SY);\n        ctx.bezierCurveTo(5 * SX, 22 * SY, 16 * SX, 10 * SY, 28 * SX, 10 * SY);\n        ctx.bezierCurveTo(36 * SX, 10 * SY, 44 * SX, 15 * SY, 50 * SX, 22 * SY);\n        ctx.bezierCurveTo(56 * SX, 15 * SY, 64 * SX, 10 * SY, 72 * SX, 10 * SY);\n        ctx.bezierCurveTo(84 * SX, 10 * SY, 95 * SX, 22 * SY, 95 * SX, 38 * SY);\n        ctx.bezierCurveTo(95 * SX, 55 * SY, 80 * SX, 70 * SY, 50 * SX, 85 * SY);\n        ctx.closePath();\n        break;\n      case \"arrow\": {\n        const arrowPts = [[5,35],[60,35],[60,15],[95,50],[60,85],[60,65],[5,65]];\n        ctx.moveTo(arrowPts[0][0] * SX, arrowPts[0][1] * SY);\n        for (let i = 1; i < arrowPts.length; i++) ctx.lineTo(arrowPts[i][0] * SX, arrowPts[i][1] * SY);\n        ctx.closePath();\n        break;\n      }\n      case \"pentagon\": {\n        const pentPts = [[50,2],[95.6,35.2],[78.2,88.8],[21.8,88.8],[4.4,35.2]];\n        ctx.moveTo(pentPts[0][0] * SX, pentPts[0][1] * SY);\n        for (let i = 1; i < pentPts.length; i++) ctx.lineTo(pentPts[i][0] * SX, pentPts[i][1] * SY);\n        ctx.closePath();\n        break;\n      }\n      case \"hexagon\": {\n        const hexPts = [[98,50],[74,91.6],[26,91.6],[2,50],[26,8.4],[74,8.4]];\n        ctx.moveTo(hexPts[0][0] * SX, hexPts[0][1] * SY);\n        for (let i = 1; i < hexPts.length; i++) ctx.lineTo(hexPts[i][0] * SX, hexPts[i][1] * SY);\n        ctx.closePath();\n        break;\n      }\n      case \"line\":\n      default:\n        ctx.rect(0, 0, dw, dh);\n        break;\n    }\n    // Only fill if not transparent \u2014 canvas ignores fillStyle=\"none\" and renders\n    // black instead, so we must skip ctx.fill() entirely for outline-only shapes.\n    if (f !== \"none\" && f !== \"transparent\") {\n      ctx.fillStyle = f;\n      ctx.fill();\n    }\n    if (swPx > 0 && s !== \"transparent\") {\n      ctx.strokeStyle = s;\n      ctx.lineWidth   = swPx;\n      // buildShapeSvg: miter on plain rect (sharp 90 corners), round everywhere else.\n      ctx.lineJoin    = (shapeType === \"rect\") ? \"miter\" : \"round\";\n      if (_dashPx.length && ctx.setLineDash) {\n        ctx.setLineDash(_dashPx);\n        ctx.lineCap = _dashCap;\n      }\n      ctx.stroke();\n      if (ctx.setLineDash) ctx.setLineDash([]);\n    }\n    ctx.restore();\n  }\n  function dmbOpenShapeEditBar(obj, el) {\n    _dmbLastBarShow = () => dmbOpenShapeEditBar(obj, el);\n    // Fast path while dragging THIS already-selected shape: skip the heavy per-move\n    // re-init \u2014 the bar is already open & docked. Just track the rotate handle.\n    if (_dmbDragSuppress && dmbEditingShapeObj === obj && dmbEditingShapeEl === el && el.classList.contains(\"dmb-selected\")) {\n      _dmbTrackRotHandle(el); return;\n    }\n    // (drag no longer hides the shape edit bar \u2014 it stays open while moving/resizing)\n    // Close other floating bars so only one is visible at a time\n    hideTextToolbar();\n    hideFrameToolbar();\n    dmbCloseEditBar();\n\n    // Deselect previously selected shape\n    document.querySelectorAll(\".dmb-shape-object.dmb-selected\").forEach((m) => {\n      if (m !== el) {\n        m.classList.remove(\"dmb-selected\");\n        if (m._dmbRotHandle) m._dmbRotHandle.style.display = \"none\";\n      }\n    });\n\n    dmbEditingShapeObj = obj;\n    dmbEditingShapeEl  = el;\n    el.classList.add(\"dmb-selected\");\n    if (el._dmbRotHandle) el._dmbRotHandle.style.display = \"\";\n\n    const bar = document.getElementById(\"dmbShapeEditBar\");\n    if (!bar) return;\n    initShapeEditBar(); // idempotent \u2014 only wires events once\n\n    document.getElementById(\"dmbShapeFill\").value    = obj.fill      || \"#6366f1\";\n    document.getElementById(\"dmbShapeStroke\").value  = obj.stroke    || \"#000000\";\n    const _ssEl = document.getElementById(\"dmbShapeStrokeStyle\");\n    if (_ssEl) _ssEl.value = obj.strokeStyle || \"solid\";\n    document.getElementById(\"dmbShapeStrokeW\").value = obj.strokeWidth ?? 0;\n    document.getElementById(\"dmbShapeStrokeWVal\").textContent = obj.strokeWidth ?? 0;\n    document.getElementById(\"dmbShapeOpacity\").value  = obj.opacity ?? 1;\n    document.getElementById(\"dmbShapeOpacityVal\").textContent = Math.round((obj.opacity ?? 1) * 100) + \"%\";\n    document.getElementById(\"dmbShapeRotation\").value = Math.round(obj.rotation || 0);\n\n    const _isLine = (obj.shapeType === \"line\");\n    // Show thickness slider only for line shapes; hide for all others\n    const thicknessWrap = document.getElementById(\"dmbShapeThicknessWrap\");\n    const thicknessInput = document.getElementById(\"dmbShapeThickness\");\n    const thicknessVal   = document.getElementById(\"dmbShapeThicknessVal\");\n    if (thicknessWrap && thicknessInput && thicknessVal) {\n      if (_isLine) {\n        _dmbLineMigrate(obj);\n        thicknessWrap.style.display = \"\";\n        thicknessInput.value = Math.round(obj.thickness || 8);\n        thicknessVal.textContent = Math.round(obj.thickness || 8) + \"px\";\n      } else {\n        thicknessWrap.style.display = \"none\";\n      }\n    }\n\n    // Line-type dropdown (Straight / Elbowed / Curved) \u2014 lines only.\n    const _ltWrap = document.getElementById(\"dmbShapeLineTypeWrap\");\n    const _ltSel  = document.getElementById(\"dmbShapeLineType\");\n    if (_ltWrap) _ltWrap.style.display = _isLine ? \"\" : \"none\";\n    if (_isLine && _ltSel) _ltSel.value = obj.lineType || \"straight\";\n\n    // A line has no separate border / rotation controls \u2014 its colour is Fill and its\n    // width is Thickness. Hide those so the bar only shows what applies.\n    const _borderField = document.getElementById(\"dmbShapeBorderField\");\n    const _strokeWWrap = document.getElementById(\"dmbShapeStrokeWWrap\");\n    const _rotateWrap  = document.getElementById(\"dmbShapeRotateWrap\");\n    if (_borderField) _borderField.style.display = _isLine ? \"none\" : \"\";\n    if (_strokeWWrap) _strokeWWrap.style.display = _isLine ? \"none\" : \"\";\n    if (_rotateWrap)  _rotateWrap.style.display  = _isLine ? \"none\" : \"\";\n\n    // Corner radius slider \u2014 only for rounded-rect\n    const _crWrap  = document.getElementById(\"dmbShapeCornerRadiusWrap\");\n    const _crInput = document.getElementById(\"dmbShapeCornerRadius\");\n    const _crVal   = document.getElementById(\"dmbShapeCornerRadiusVal\");\n    if (_crWrap) {\n      const _isRounded = obj.shapeType === \"rounded-rect\";\n      _crWrap.style.display = _isRounded ? \"\" : \"none\";\n      if (_isRounded && _crInput) {\n        const _crCurrent = (obj.cornerRadius !== undefined && obj.cornerRadius !== null) ? obj.cornerRadius : 12;\n        _crInput.value = _crCurrent;\n        if (_crVal) _crVal.textContent = _crCurrent;\n      }\n    }\n\n    // Outline-only toggle: hidden for lines (lines are fills by definition)\n    const _outlineChk   = document.getElementById(\"dmbShapeOutlineOnly\");\n    const _fillLabelEl  = document.getElementById(\"dmbShapeFillLabel\");\n    if (_outlineChk) {\n      const _isOutline = (obj.fill === \"none\" || obj.fill === \"transparent\");\n      _outlineChk.checked = _isOutline;\n      _outlineChk.closest(\"label\").style.display = (obj.shapeType === \"line\") ? \"none\" : \"\";\n      if (_fillLabelEl) _fillLabelEl.style.opacity = _isOutline ? \"0.4\" : \"1\";\n    }\n    try { _dmbSyncShapeDots(); } catch (e) {}\n\n    // Update contain button text\n    const _scBtn = document.getElementById(\"dmbShapeContainBtn\");\n    if (_scBtn) _scBtn.textContent = _dmbContainBtnText(obj, !!el.closest(\".dmb-cover-preview\"));\n\n    bar.style.display = \"block\";\n    _dmbDockBar(bar);\n  }\n\n  function dmbCloseShapeEditBar() {\n    const bar = document.getElementById(\"dmbShapeEditBar\");\n    if (bar) bar.style.display = \"none\";\n    if (dmbEditingShapeEl) {\n      dmbEditingShapeEl.classList.remove(\"dmb-selected\");\n      if (dmbEditingShapeEl._dmbRotHandle) dmbEditingShapeEl._dmbRotHandle.style.display = \"none\";\n    }\n    dmbEditingShapeObj = null;\n    dmbEditingShapeEl  = null;\n    try { _dmbUpdateRibbonPlaceholder(); } catch (e) {}\n  }\n\n  function dmbApplyShapeEditBar() {\n    if (!dmbEditingShapeObj || !dmbEditingShapeEl) return;\n    const obj = dmbEditingShapeObj;\n\n    // \u2500\u2500 LINE: colour / opacity / dash / thickness / type; refresh in place \u2500\u2500\u2500\u2500\u2500\u2500\n    if (obj.shapeType === \"line\") {\n      _dmbLineMigrate(obj);\n      const _lf = document.getElementById(\"dmbShapeFill\");\n      if (_lf) obj.fill = _lf.value || \"#6366f1\";\n      obj.strokeStyle = document.getElementById(\"dmbShapeStrokeStyle\")?.value || \"solid\";\n      const _lo = document.getElementById(\"dmbShapeOpacity\");\n      if (_lo) { obj.opacity = parseFloat(_lo.value); const _ov = document.getElementById(\"dmbShapeOpacityVal\"); if (_ov) _ov.textContent = Math.round(obj.opacity * 100) + \"%\"; }\n      const _lt = document.getElementById(\"dmbShapeLineType\");\n      if (_lt) obj.lineType = _lt.value || \"straight\";\n      const _th = document.getElementById(\"dmbShapeThickness\");\n      if (_th) { obj.thickness = Math.max(1, parseFloat(_th.value) || 8); const _tv = document.getElementById(\"dmbShapeThicknessVal\"); if (_tv) _tv.textContent = Math.round(obj.thickness) + \"px\"; }\n      obj.rotation = 0;\n      try { _dmbSyncShapeDots(); } catch (e) {}\n      try { _dmbLineRefreshEl(obj, dmbEditingShapeEl); } catch (e) {}\n      return;\n    }\n\n    const _isOutlineOnly = document.getElementById(\"dmbShapeOutlineOnly\")?.checked || false;\n    const _fillLbl       = document.getElementById(\"dmbShapeFillLabel\");\n    // When outline-only: set fill to \"none\" and dim the fill picker\n    obj.fill = _isOutlineOnly ? \"none\" : (document.getElementById(\"dmbShapeFill\").value || \"#6366f1\");\n    if (_fillLbl) _fillLbl.style.opacity = _isOutlineOnly ? \"0.4\" : \"1\";\n    obj.stroke      = document.getElementById(\"dmbShapeStroke\").value;\n    obj.strokeWidth = parseFloat(document.getElementById(\"dmbShapeStrokeW\").value) || 0;\n    obj.strokeStyle = document.getElementById(\"dmbShapeStrokeStyle\")?.value || \"solid\";\n    obj.opacity     = parseFloat(document.getElementById(\"dmbShapeOpacity\").value);\n    obj.rotation    = parseFloat(document.getElementById(\"dmbShapeRotation\").value) || 0;\n    // Auto-enable a default stroke when switching to outline-only with no stroke,\n    // OR when picking a dashed/dotted border while the width is still 0 (so the\n    // chosen style is actually visible).\n    if ((_isOutlineOnly || obj.strokeStyle !== \"solid\") && obj.strokeWidth < 1) {\n      obj.strokeWidth = 2;\n      const _swEl = document.getElementById(\"dmbShapeStrokeW\");\n      if (_swEl) _swEl.value = 2;\n    }\n    document.getElementById(\"dmbShapeStrokeWVal\").textContent = obj.strokeWidth;\n    document.getElementById(\"dmbShapeOpacityVal\").textContent = Math.round(obj.opacity * 100) + \"%\";\n    try { _dmbSyncShapeDots(); } catch (e) {}\n\n    // Corner radius \u2014 only for rounded-rect\n    if (obj.shapeType === \"rounded-rect\") {\n      const _crEl  = document.getElementById(\"dmbShapeCornerRadius\");\n      const _crValEl = document.getElementById(\"dmbShapeCornerRadiusVal\");\n      if (_crEl) {\n        obj.cornerRadius = Math.max(0, Math.min(50, parseInt(_crEl.value) || 12));\n        if (_crValEl) _crValEl.textContent = obj.cornerRadius;\n      }\n    }\n\n    // Line thickness: changing h re-centers the line vertically around its old midpoint\n    if (obj.shapeType === \"line\") {\n      const thicknessInput = document.getElementById(\"dmbShapeThickness\");\n      const thicknessVal   = document.getElementById(\"dmbShapeThicknessVal\");\n      if (thicknessInput) {\n        const newH = Math.max(1, parseFloat(thicknessInput.value) || 8);\n        const oldCenterY = (obj.y || 0) + (obj.h || 8) / 2;\n        obj.h = newH;\n        obj.y = oldCenterY - newH / 2;          // keep line centred at same Y\n        dmbEditingShapeEl.style.height = newH + \"px\";\n        dmbEditingShapeEl.style.top    = obj.y + \"px\";\n        if (thicknessVal) thicknessVal.textContent = newH + \"px\";\n      }\n    }\n\n    // Update the live SVG inside the shape wrapper.\n    // CRITICAL: do NOT use innerHTML = ... on shWrap \u2014 it destroys the 8 resize handle\n    // divs that are siblings of the SVG. Replace only the SVG element in-place.\n    const _shOldSvg = dmbEditingShapeEl.querySelector(\"svg\");\n    const _shTmp    = document.createElement(\"div\");\n    _shTmp.innerHTML = buildShapeSvg(obj.shapeType, obj.fill, obj.stroke, obj.strokeWidth, obj.opacity, obj.cornerRadius, obj.w, obj.h, obj.strokeStyle);\n    const _shNewSvg  = _shTmp.firstElementChild;\n    if (_shOldSvg && _shNewSvg) {\n      _shOldSvg.replaceWith(_shNewSvg);\n    } else if (_shNewSvg) {\n      dmbEditingShapeEl.prepend(_shNewSvg); // no existing SVG \u2014 add it first\n    }\n    dmbEditingShapeEl.style.transform = `rotate(${obj.rotation}deg)`;\n  }\n\n  function initShapeEditBar() {\n    if (_dmbShapeBarInit) return;\n    _dmbShapeBarInit = true;\n\n    // Wire all shape bar controls \u2014 including dmbShapeThickness for line shapes\n    // and dmbShapeCornerRadius for rounded-rect\n    [\"dmbShapeFill\",\"dmbShapeStroke\",\"dmbShapeStrokeStyle\",\"dmbShapeStrokeW\",\"dmbShapeOpacity\",\"dmbShapeRotation\",\"dmbShapeThickness\",\"dmbShapeCornerRadius\",\"dmbShapeLineType\"].forEach((id) => {\n      document.getElementById(id)?.addEventListener(\"input\",  dmbApplyShapeEditBar);\n      document.getElementById(id)?.addEventListener(\"change\", dmbApplyShapeEditBar);\n    });\n    // Outline-only checkbox fires on \"change\" only\n    document.getElementById(\"dmbShapeOutlineOnly\")?.addEventListener(\"change\", dmbApplyShapeEditBar);\n\n    // Colour-wheel palettes for Fill and Border (recent swatches + custom picker),\n    // matching the text/frame toolbars.\n    [[\"dmbShapeFillBtn\",\"dmbShapeFillPop\",\"dmbShapeFillCustom\",\"dmbShapeFill\",\"dmbShapeFillWrap\"],\n     [\"dmbShapeStrokeBtn\",\"dmbShapeStrokePop\",\"dmbShapeStrokeCustom\",\"dmbShapeStroke\",\"dmbShapeStrokeWrap\"]\n    ].forEach(([btnId, popId, customId, inputId]) => {\n      const btn = document.getElementById(btnId);\n      const pop = document.getElementById(popId);\n      const custom = document.getElementById(customId);\n      const input = document.getElementById(inputId);\n      if (btn && pop) {\n        btn.addEventListener(\"click\", (e) => {\n          e.preventDefault(); e.stopPropagation();\n          const willOpen = pop.hidden;\n          [\"dmbShapeFillPop\",\"dmbShapeStrokePop\"].forEach((id) => { const p = document.getElementById(id); if (p && p !== pop) p.hidden = true; });\n          pop.hidden = !willOpen;\n        });\n        pop.addEventListener(\"click\", (e) => { if (e.target.closest(\".dmb-recent-swatch\")) pop.hidden = true; });\n      }\n      if (custom && input) {\n        custom.addEventListener(\"click\", (e) => { e.stopPropagation(); if (e.target === input) return; try { input.click(); } catch (e2) {} });   // identical to the background wheel \u2014 NO preventDefault (it cancels the native picker on iOS)\n      }\n      if (input && pop) {\n        try { dmbEnhanceColorInput(input, pop); } catch (e) {}\n        try { if (custom) pop.appendChild(custom); } catch (e) {}\n        try { _dmbColorTapFix(custom, input); } catch (e) {}\n        input.addEventListener(\"input\", () => { try { _dmbSyncShapeDots(); } catch (e) {} });\n        input.addEventListener(\"change\", () => { pop.hidden = true; });\n      }\n    });\n    // Outside-click closes the shape colour popups.\n    document.addEventListener(\"click\", (e) => {\n      if (!e.target.closest(\"#dmbShapeFillWrap\"))   { const p = document.getElementById(\"dmbShapeFillPop\");   if (p) p.hidden = true; }\n      if (!e.target.closest(\"#dmbShapeStrokeWrap\")) { const p = document.getElementById(\"dmbShapeStrokePop\"); if (p) p.hidden = true; }\n    });\n\n    document.getElementById(\"dmbShapeDone\")?.addEventListener(\"click\", dmbCloseShapeEditBar);\n\n    // \"More\" dropdown (align, layer order, duplicate, contain, delete).\n    const _shMoreToggle = document.getElementById(\"dmbShapeMoreToggle\");\n    const _shMoreMenu   = document.getElementById(\"dmbShapeMoreMenu\");\n    if (_shMoreToggle && _shMoreMenu) {\n      _shMoreToggle.addEventListener(\"click\", (e) => {\n        e.preventDefault(); e.stopPropagation();\n        _shMoreMenu.hidden = !_shMoreMenu.hidden;\n      });\n      _shMoreMenu.addEventListener(\"click\", (e) => {\n        if (e.target.closest(\".dmb-text-more-item\")) _shMoreMenu.hidden = true;\n      });\n      document.addEventListener(\"click\", (e) => {\n        if (!e.target.closest(\"#dmbShapeMoreDD\")) { const m = document.getElementById(\"dmbShapeMoreMenu\"); if (m) m.hidden = true; }\n      });\n    }\n\n    // Contain-to-page button\n    document.getElementById(\"dmbShapeContainBtn\")?.addEventListener(\"click\", () => {\n      if (!dmbEditingShapeObj || !dmbEditingShapeEl) return;\n      const _isCover = !!dmbEditingShapeEl.closest(\".dmb-cover-preview\");\n      _dmbContainCycle(dmbEditingShapeObj, dmbEditingShapeEl, _isCover);\n    });\n\n    document.getElementById(\"dmbShapeFrontBtn\")?.addEventListener(\"click\", () => {\n      _dmbDoLayerStep(dmbEditingShapeEl, \"forward\");\n    });\n\n    document.getElementById(\"dmbShapeBackBtn\")?.addEventListener(\"click\", () => {\n      _dmbDoLayerStep(dmbEditingShapeEl, \"backward\");\n    });\n\n    [\"left\",\"center\",\"right\"].forEach((align) => {\n      const btnId = { left:\"dmbShapeAlignLeft\", center:\"dmbShapeAlignCenter\", right:\"dmbShapeAlignRight\" }[align];\n      document.getElementById(btnId)?.addEventListener(\"click\", () => {\n        if (!dmbEditingShapeObj || !dmbEditingShapeEl) return;\n        const tType = dmbEditingShapeEl.closest(\".dmb-cover-preview\") ? \"cover\" : \"page\";\n        dmbAlignObjectInZone(dmbEditingShapeEl, dmbEditingShapeObj, align, tType);\n        _dmbDockBar(document.getElementById(\"dmbShapeEditBar\"));\n      });\n    });\n\n    document.getElementById(\"dmbShapeDelete\")?.addEventListener(\"click\", () => {\n      if (!dmbEditingShapeObj) return;\n      const objId = dmbEditingShapeObj.id;\n      // Persist the deletion (+ make it undoable). Without this the deleted shape\n      // reappeared on refresh \u2014 the saved copy still held it.\n      pushUndoState(\"Delete Shape\");\n      if (editor.pageObjects) {\n        Object.keys(editor.pageObjects).forEach((p) => {\n          const arr = editor.pageObjects[p];\n          if (Array.isArray(arr)) {\n            const idx = arr.findIndex((o) => o.id === objId);\n            if (idx !== -1) arr.splice(idx, 1);\n          }\n        });\n      }\n      if (Array.isArray(editor.coverObjects)) {\n        const idx = editor.coverObjects.findIndex((o) => o.id === objId);\n        if (idx !== -1) editor.coverObjects.splice(idx, 1);\n      }\n      if (dmbEditingShapeEl?._dmbRotHandle) dmbEditingShapeEl._dmbRotHandle.remove();\n      dmbCloseShapeEditBar();\n      if (typeof renderBuilderCanvas === \"function\") renderBuilderCanvas();\n    });\n\n    // Duplicate the selected shape into a brand-new, independent element (fresh id,\n    // offset a touch, added to the SAME surface), then select the copy. Also the\n    // target of Ctrl+D on a shape.\n    document.getElementById(\"dmbShapeDuplicateBtn\")?.addEventListener(\"click\", () => {\n      if (!dmbEditingShapeObj) return;\n      const src = dmbEditingShapeObj;\n      const copy = { ...cloneState(src), id: uid(), x: (src.x || 0) + 20, y: (src.y || 0) + 20, z: Date.now() };\n      // Lines carry endpoints \u2014 offset those too so the copy is shifted, not overlaid.\n      if (copy.shapeType === \"line\" && copy.x1 != null) {\n        copy.x1 += 20; copy.y1 += 20; copy.x2 += 20; copy.y2 += 20;\n        if (copy.bx != null) { copy.bx += 20; copy.by += 20; }\n      }\n      let added = false;\n      if (editor.pageObjects) {\n        for (const p of Object.keys(editor.pageObjects)) {\n          const arr = editor.pageObjects[p];\n          if (Array.isArray(arr) && arr.some((o) => o.id === src.id)) { pushUndoState(\"Duplicate Shape\"); arr.push(copy); added = true; break; }\n        }\n      }\n      if (!added && Array.isArray(editor.coverObjects) && editor.coverObjects.some((o) => o.id === src.id)) {\n        pushUndoState(\"Duplicate Shape\"); editor.coverObjects.push(copy); added = true;\n      }\n      if (!added) return;\n      if (typeof renderBuilderCanvas === \"function\") renderBuilderCanvas();\n      requestAnimationFrame(() => {\n        const el = document.querySelector('[data-object-id=\"' + copy.id + '\"]');\n        if (el) { try { dmbOpenShapeEditBar(copy, el); } catch (e) {} }\n      });\n    });\n\n    // Close when clicking outside both the bar and any shape object\n    document.addEventListener(\"pointerdown\", (e) => {\n      if (!dmbEditingShapeObj) return;\n      const bar = document.getElementById(\"dmbShapeEditBar\");\n      if (bar && bar.contains(e.target))            return; // inside bar\n      if (e.target.closest(\".dmb-stage-header\"))    return; // anywhere in the fixed ribbon\n      if (e.target.closest(\"#dmbMshRibbon\"))        return; // mobile ribbon (idle + docked bars)\n      if (e.target.closest(\"#dmbMshColorSheet\"))    return; // mobile body colour sheet \u2014 keep the shape selected\n      if (e.target.closest(\".dmb-shape-object\"))    return; // on a shape\n      if (e.target.closest(\".dmb-frame-handle\"))    return; // resize handle\n      dmbCloseShapeEditBar();\n    }, { capture: true });\n  }\n\n  // Insert a new shape object into the currently active page/cover.\n  // WARNING: Do NOT use a bare `currentPage` variable \u2014 it is not defined.\n  // Use the same spread/side logic as addDefaultText and map insertion:\n  //   - cover mode  \u2192 editor.coverObjects\n  //   - spread -1   \u2192 page 0\n  //   - otherwise   \u2192 derived from editor.currentSpread + lastToolbarSide\n  function insertShapeIntoActivePage(shapeType) {\n    const isCover = (builderMode === \"cover\");\n    let pn = null;\n    if (!isCover) {\n      if (editor.currentSpread === -1) {\n        pn = 0;\n      } else {\n        const _lp = editor.currentSpread * 2 + 1;\n        const _rp = _lp + 1;\n        if (lastToolbarSide === \"right\" && _rp <= editor.totalPages && !isLockedPage(_rp)) {\n          pn = _rp;\n        } else if (_lp <= editor.totalPages && !isLockedPage(_lp)) {\n          pn = _lp;\n        } else if (_rp <= editor.totalPages && !isLockedPage(_rp)) {\n          pn = _rp;\n        }\n      }\n    }\n    // Abort silently if there is no valid target page (locked / out of range)\n    if (!isCover && pn === null) return;\n\n    const isLine  = shapeType === \"line\";\n    const shapeObj = {\n      id:          _dmbOid(\"shape_\"),\n      type:        \"shape\",\n      shapeType:   shapeType,\n      x:           60,\n      y:           60,\n      w:           isLine ? 160 : 120,\n      h:           isLine ?   8 : 120,\n      fill:        \"#6366f1\",\n      stroke:      \"#000000\",\n      strokeWidth: 0,\n      strokeStyle: \"solid\",\n      opacity:     1,\n      rotation:    0,\n      z:           _dmbNextTopZ(isCover ? \"cover\" : pn),\n    };\n    pushUndoState(\"Add Shape\");\n    if (isCover) {\n      if (!Array.isArray(editor.coverObjects)) editor.coverObjects = [];\n      _dmbPlaceOnActiveCoverFace(shapeObj, 60, 60);   // land on the SELECTED face\n      editor.coverObjects.push(shapeObj);\n      renderCoverCanvas();\n    } else {\n      if (!editor.pageObjects) editor.pageObjects = {};\n      if (!Array.isArray(editor.pageObjects[pn])) editor.pageObjects[pn] = [];\n      editor.pageObjects[pn].push(shapeObj);\n      renderBuilderCanvas();\n    }\n  }\n\n  function initMapTools() {\n    initMapModal();\n    initMapEditBar();\n\n    const addMyMapBtn = document.getElementById(\"addMyMapBtn\");\n    const markStayBtn = document.getElementById(\"markStayBtn\");\n    const markerPickerMenu = document.getElementById(\"markerPickerMenu\");\n\n    if (addMyMapBtn && !addMyMapBtn.dataset.boundMapClick) {\n      addMyMapBtn.dataset.boundMapClick = \"true\";\n      addMyMapBtn.addEventListener(\"click\", () => {\n        dmbOpenMapModal();\n      });\n    }\n\n    if (markStayBtn && !markStayBtn.dataset.boundMarkerClick) {\n      markStayBtn.dataset.boundMarkerClick = \"true\";\n      markStayBtn.addEventListener(\"click\", () => {\n        if (!currentInsertedMap) return;\n        if (!markerPickerMenu) return;\n        if (markerPickerMenu.style.display === \"block\") {\n          markerPickerMenu.style.display = \"none\";\n        } else {\n          const rect = markStayBtn.getBoundingClientRect();\n          markerPickerMenu.style.top = (rect.bottom + 4) + \"px\";\n          markerPickerMenu.style.left = rect.left + \"px\";\n          markerPickerMenu.style.width = rect.width + \"px\";\n          markerPickerMenu.style.display = \"block\";\n        }\n      });\n    }\n\n    if (markerPickerMenu && !markerPickerMenu.dataset.boundMarkerMenu) {\n      markerPickerMenu.dataset.boundMarkerMenu = \"true\";\n      markerPickerMenu.addEventListener(\"click\", (e) => {\n        const btn = e.target.closest(\"[data-marker-type]\");\n        if (!btn) return;\n        activeMarkerType = btn.dataset.markerType;\n        pendingMapMarkerPlacement = true;\n        markerPickerMenu.style.display = \"none\";\n      });\n    }\n  }\n\n  function initStickerLibrary() {\n    const btn = document.getElementById(\"dmbBrowseStickersBtn\");\n    if (!btn || btn.dataset.boundStickerLib) return;\n    btn.dataset.boundStickerLib = \"true\";\n\n    let stickerData = [];\n    let selectedStickerUrl = null;\n    let selectedStickerName = null;\n\n    const _stickerUrl = (window.DMB_MAGAZINE_MODE && window.DMB_SELECTED_MAGAZINE_STYLE?.stickersUrl)\n      ? window.DMB_SELECTED_MAGAZINE_STYLE.stickersUrl\n      : (window.DMB_STICKERS_JSON_URL || \"\");\n    fetch(_stickerUrl)\n      .then((r) => (r.ok ? r.json() : []))\n      .then((data) => { stickerData = Array.isArray(data) ? data : []; })\n      .catch(() => { stickerData = []; });\n\n    const modal = document.getElementById(\"dmbStickerModal\");\n    const grid = document.getElementById(\"dmbStickerGrid\");\n    const search = document.getElementById(\"dmbStickerSearch\");\n    const insertBtn = document.getElementById(\"dmbStickerInsertBtn\");\n    const closeBtn = document.getElementById(\"dmbStickerCloseBtn\");\n    const cancelBtn = document.getElementById(\"dmbStickerCancelBtn\");\n\n    function closeModal() { if (modal) modal.style.display = \"none\"; }\n\n    function renderGrid(filter) {\n      if (!grid) return;\n      const q = (filter || \"\").toLowerCase().trim();\n      const items = q ? stickerData.filter((s) => s.name.toLowerCase().includes(q)) : stickerData;\n      grid.innerHTML = \"\";\n      selectedStickerUrl = null;\n      selectedStickerName = null;\n      if (insertBtn) insertBtn.disabled = true;\n\n      if (items.length === 0) {\n        grid.innerHTML =\n          stickerData.length === 0\n            ? '<p style=\"grid-column:1/-1;text-align:center;color:#888;padding:24px 0;\">No stickers uploaded yet.<br><small>Upload PNG images to Shopify Files and add them to custom-stickers.json</small></p>'\n            : '<p style=\"grid-column:1/-1;text-align:center;color:#888;\">No stickers match your search.</p>';\n        return;\n      }\n\n      items.forEach((sticker) => {\n        const cell = document.createElement(\"div\");\n        cell.style.cssText =\n          \"border:2px solid transparent;border-radius:8px;padding:8px 6px;cursor:pointer;text-align:center;background:#f7f7f7;transition:border-color .15s;\";\n\n        const img = document.createElement(\"img\");\n        img.loading = \"lazy\";      // phones can't keep a whole collection of full-size\n        img.decoding = \"async\";    // PNGs decoded at once \u2192 many render blank; defer offscreen\n        img.src = sticker.url;\n        img.alt = sticker.name;\n        img.style.cssText =\n          \"width:80px;height:80px;object-fit:contain;display:block;margin:0 auto 6px;\";\n\n        const label = document.createElement(\"div\");\n        label.textContent = sticker.name;\n        label.style.cssText =\n          \"font-size:11px;color:#555;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;\";\n\n        cell.appendChild(img);\n        cell.appendChild(label);\n\n        cell.addEventListener(\"click\", () => {\n          grid.querySelectorAll(\"[data-sticker-selected]\").forEach((c) => {\n            c.style.borderColor = \"transparent\";\n            delete c.dataset.stickerSelected;\n          });\n          cell.style.borderColor = \"#4a90e2\";\n          cell.dataset.stickerSelected = \"true\";\n          selectedStickerUrl = sticker.url;\n          selectedStickerName = sticker.name;\n          if (insertBtn) insertBtn.disabled = false;\n        });\n\n        grid.appendChild(cell);\n      });\n    }\n\n    btn.addEventListener(\"click\", () => {\n      if (!modal) return;\n      renderGrid(\"\");\n      if (search) search.value = \"\";\n      modal.style.display = \"flex\";\n    });\n\n    closeBtn?.addEventListener(\"click\", closeModal);\n    cancelBtn?.addEventListener(\"click\", closeModal);\n    modal?.addEventListener(\"click\", (e) => { if (e.target === modal) closeModal(); });\n    search?.addEventListener(\"input\", () => renderGrid(search.value));\n\n    insertBtn?.addEventListener(\"click\", () => {\n      if (!selectedStickerUrl) return;\n      const pageNumber = getActivePageNumberForMap();\n      if (pageNumber == null) { closeModal(); return; }\n      const isCover = pageNumber === \"cover\";\n      const url = selectedStickerUrl;\n      const name = selectedStickerName || \"Sticker\";\n      closeModal();\n\n      const tempImg = new Image();\n      tempImg.crossOrigin = \"anonymous\";\n      let _libStickerDone = false;\n      tempImg.onload = () => {\n        if (_libStickerDone) return; _libStickerDone = true;\n        const origW = tempImg.naturalWidth || 100;\n        const origH = tempImg.naturalHeight || 100;\n\n        // Find tight bounding box of non-transparent pixels and crop to it\n        let cropX = 0, cropY = 0, cropW = origW, cropH = origH;\n        let tightUrl = null;\n        try {\n          const ofc = document.createElement(\"canvas\");\n          ofc.width = origW; ofc.height = origH;\n          const ofCtx = ofc.getContext(\"2d\");\n          ofCtx.drawImage(tempImg, 0, 0);\n          const px = ofCtx.getImageData(0, 0, origW, origH).data;\n          let minX = origW, minY = origH, maxX = 0, maxY = 0;\n          for (let y = 0; y < origH; y++) {\n            for (let x = 0; x < origW; x++) {\n              if (px[(y * origW + x) * 4 + 3] > 10) {\n                if (x < minX) minX = x;\n                if (x > maxX) maxX = x;\n                if (y < minY) minY = y;\n                if (y > maxY) maxY = y;\n              }\n            }\n          }\n          if (maxX >= minX && maxY >= minY) {\n            cropX = minX; cropY = minY;\n            cropW = maxX - minX + 1; cropH = maxY - minY + 1;\n          }\n          // Export the cropped area as a data URL so the img element needs no offset tricks\n          const cropCanvas = document.createElement(\"canvas\");\n          cropCanvas.width = cropW; cropCanvas.height = cropH;\n          cropCanvas.getContext(\"2d\").drawImage(ofc, cropX, cropY, cropW, cropH, 0, 0, cropW, cropH);\n          tightUrl = cropCanvas.toDataURL(\"image/png\");\n        } catch(e) { /* CORS fallback \u2014 use original URL */ }\n\n        const MAX = 150;\n        let w = cropW, h = cropH;\n        if (w > MAX || h > MAX) {\n          const scale = MAX / Math.max(w, h);\n          w = Math.round(w * scale);\n          h = Math.round(h * scale);\n        }\n        const stickerObj = {\n          id: _dmbOid(\"sticker_\"),\n          type: \"sticker\",\n          name,\n          url,\n          tightUrl,\n          x: 20,\n          y: 20,\n          w,\n          h,\n          z: _dmbNextTopZ(isCover ? \"cover\" : pageNumber),\n          rotation: 0,\n          cropX,\n          cropY,\n          cropW,\n          cropH,\n          cropRefW: origW,   // size the crop was measured on - see _dmbStickerCropFor\n          cropRefH: origH,\n        };\n        pushUndoState(\"Insert Sticker\");\n        if (isCover) {\n          if (!Array.isArray(editor.coverObjects)) editor.coverObjects = [];\n          _dmbPlaceOnActiveCoverFace(stickerObj, 20, 20);   // land on the SELECTED face\n          editor.coverObjects.push(stickerObj);\n        } else {\n          if (!editor.pageObjects) editor.pageObjects = {};\n          if (!Array.isArray(editor.pageObjects[pageNumber])) editor.pageObjects[pageNumber] = [];\n          editor.pageObjects[pageNumber].push(stickerObj);\n        }\n        if (typeof renderBuilderCanvas === \"function\") renderBuilderCanvas();\n        if (typeof renderThumbsRail === \"function\") renderThumbsRail();\n      };\n      tempImg.onerror = () => {\n        if (_libStickerDone) return; _libStickerDone = true;\n        // fallback to 100x100 if image can't be measured\n        const stickerObj = {\n          id: _dmbOid(\"sticker_\"),\n          type: \"sticker\",\n          name,\n          url,\n          x: 20,\n          y: 20,\n          w: 100,\n          h: 100,\n          z: _dmbNextTopZ(isCover ? \"cover\" : pageNumber),\n          rotation: 0,\n        };\n        pushUndoState(\"Insert Sticker\");\n        if (isCover) {\n          if (!Array.isArray(editor.coverObjects)) editor.coverObjects = [];\n          _dmbPlaceOnActiveCoverFace(stickerObj, 20, 20);   // land on the SELECTED face\n          editor.coverObjects.push(stickerObj);\n        } else {\n          if (!editor.pageObjects) editor.pageObjects = {};\n          if (!Array.isArray(editor.pageObjects[pageNumber])) editor.pageObjects[pageNumber] = [];\n          editor.pageObjects[pageNumber].push(stickerObj);\n        }\n        if (typeof renderBuilderCanvas === \"function\") renderBuilderCanvas();\n        if (typeof renderThumbsRail === \"function\") renderThumbsRail();\n      };\n      tempImg.src = url;\n    });\n  }\n\n  function _commitMagIconObj(obj) {\n    pushUndoState(\"Insert Icon\");\n    const pageNumber = getActivePageNumberForMap();\n    if (pageNumber === \"cover\") {\n      if (!Array.isArray(editor.coverObjects)) editor.coverObjects = [];\n      _dmbPlaceOnActiveCoverFace(obj, 20, 20);   // land on the SELECTED face\n      editor.coverObjects.push(obj);\n      renderCoverCanvas();\n    } else {\n      const pn = pageNumber;\n      if (!editor.pageObjects) editor.pageObjects = {};\n      if (!Array.isArray(editor.pageObjects[pn])) editor.pageObjects[pn] = [];\n      editor.pageObjects[pn].push(obj);\n      if (typeof renderSpread === \"function\") renderSpread();\n    }\n    if (typeof renderThumbsRail === \"function\") renderThumbsRail();\n  }\n\n  function insertMagIcon(iconId, color) {\n    const base = (window.DMB_ASSET_BASE || \"\").replace(/\\/[^/]+$/, \"/\");\n    const ic = DMB_MAG_ICONS.find((i) => i.id === iconId);\n    if (!ic) return;\n    const url = base + ic.file;\n    const img = new Image();\n    img.crossOrigin = \"anonymous\";\n    let _magIconDone = false;\n    img.onload = () => {\n      if (_magIconDone) return; _magIconDone = true;\n      const naturalW = img.naturalWidth || 300;\n      const naturalH = img.naturalHeight || 120;\n      const MAX = 250;\n      let w = naturalW, h = naturalH;\n      if (w > MAX) { h = Math.round(h * MAX / w); w = MAX; }\n      if (h > MAX) { w = Math.round(w * MAX / h); h = MAX; }\n      _commitMagIconObj({ id: _dmbOid(\"mag-icon_\"), type: \"mag-icon\", iconId, color: color || \"#000000\", x: 20, y: 20, w, h, naturalW, naturalH, z: Date.now(), rotation: 0 });\n    };\n    img.onerror = () => {\n      if (_magIconDone) return; _magIconDone = true;\n      _commitMagIconObj({ id: _dmbOid(\"mag-icon_\"), type: \"mag-icon\", iconId, color: color || \"#000000\", x: 20, y: 20, w: 200, h: 80, naturalW: 200, naturalH: 80, z: Date.now(), rotation: 0 });\n    };\n    img.src = url;\n  }\n\n  function initMagIconPanel() {\n    const modal = document.getElementById(\"dmbMagIconModal\");\n    const closeBtn = document.getElementById(\"dmbMagIconModalClose\");\n    const colorInput = document.getElementById(\"dmbMagIconModalColor\");\n    const grid = document.getElementById(\"dmbMagIconModalGrid\");\n    if (!modal) return;\n    if (modal.dataset.boundMagIcon) return;\n    modal.dataset.boundMagIcon = \"true\";\n\n    // Open modal\n    document.getElementById(\"dmbMagIconsBtn\")?.addEventListener(\"click\", () => {\n      modal.style.display = \"flex\";\n    });\n\n    // Close modal\n    closeBtn?.addEventListener(\"click\", () => { modal.style.display = \"none\"; });\n    modal.addEventListener(\"click\", (e) => { if (e.target === modal) modal.style.display = \"none\"; });\n\n    // Insert icon on click\n    grid?.addEventListener(\"click\", (e) => {\n      const btn = e.target.closest(\".dmb-mag-icon-pick-btn\");\n      if (!btn) return;\n      insertMagIcon(btn.dataset.iconId, colorInput ? colorInput.value : \"#000000\");\n      modal.style.display = \"none\";\n    });\n  }\n\n  function initCustomStickersPanel() {\n    const modal = document.getElementById(\"dmbCustomStickerModal\");\n    const closeBtn = document.getElementById(\"dmbCustomStickerModalClose\");\n    const grid = document.getElementById(\"dmbCustomStickerModalGrid\");\n    if (!modal) return;\n    if (modal.dataset.boundCustomStickers) return;\n    modal.dataset.boundCustomStickers = \"true\";\n\n    document.getElementById(\"dmbStickersBtn\")?.addEventListener(\"click\", () => {\n      modal.style.display = \"flex\";\n    });\n\n    closeBtn?.addEventListener(\"click\", () => { modal.style.display = \"none\"; });\n    modal.addEventListener(\"click\", (e) => { if (e.target === modal) modal.style.display = \"none\"; });\n\n    grid?.addEventListener(\"click\", (e) => {\n      const btn = e.target.closest(\".dmb-mag-icon-pick-btn\");\n      if (!btn) return;\n      const url = btn.dataset.stickerUrl;\n      const name = btn.dataset.stickerName || \"Sticker\";\n      modal.style.display = \"none\";\n\n      const pageNumber = getActivePageNumberForMap();\n      if (pageNumber == null) return;\n      const isCover = pageNumber === \"cover\";\n\n      const tempImg = new Image();\n      tempImg.crossOrigin = \"anonymous\";\n      let _stickerDone = false;\n      tempImg.onload = () => {\n        if (_stickerDone) return; _stickerDone = true;\n        const origW = tempImg.naturalWidth || 100;\n        const origH = tempImg.naturalHeight || 100;\n        let cropX = 0, cropY = 0, cropW = origW, cropH = origH, tightUrl = null;\n        try {\n          const ofc = document.createElement(\"canvas\");\n          ofc.width = origW; ofc.height = origH;\n          const ofCtx = ofc.getContext(\"2d\");\n          ofCtx.drawImage(tempImg, 0, 0);\n          const px = ofCtx.getImageData(0, 0, origW, origH).data;\n          let minX = origW, minY = origH, maxX = 0, maxY = 0;\n          for (let y = 0; y < origH; y++) {\n            for (let x = 0; x < origW; x++) {\n              if (px[(y * origW + x) * 4 + 3] > 10) {\n                if (x < minX) minX = x; if (x > maxX) maxX = x;\n                if (y < minY) minY = y; if (y > maxY) maxY = y;\n              }\n            }\n          }\n          if (maxX >= minX && maxY >= minY) { cropX = minX; cropY = minY; cropW = maxX - minX + 1; cropH = maxY - minY + 1; }\n          const cc = document.createElement(\"canvas\"); cc.width = cropW; cc.height = cropH;\n          cc.getContext(\"2d\").drawImage(ofc, cropX, cropY, cropW, cropH, 0, 0, cropW, cropH);\n          tightUrl = cc.toDataURL(\"image/png\");\n        } catch(e) {}\n        const MAX = 150;\n        let w = cropW, h = cropH;\n        if (w > MAX || h > MAX) { const s = MAX / Math.max(w, h); w = Math.round(w * s); h = Math.round(h * s); }\n        const obj = { id: _dmbOid(\"sticker_\"), type: \"sticker\", name, url, tightUrl, x: 20, y: 20, w, h, z: Date.now(), rotation: 0, cropX, cropY, cropW, cropH, cropRefW: origW, cropRefH: origH };\n        pushUndoState(\"Insert Sticker\");\n        if (isCover) { if (!Array.isArray(editor.coverObjects)) editor.coverObjects = []; _dmbPlaceOnActiveCoverFace(obj, 20, 20); editor.coverObjects.push(obj); }\n        else { if (!editor.pageObjects) editor.pageObjects = {}; if (!Array.isArray(editor.pageObjects[pageNumber])) editor.pageObjects[pageNumber] = []; editor.pageObjects[pageNumber].push(obj); }\n        if (typeof renderBuilderCanvas === \"function\") renderBuilderCanvas();\n        if (typeof renderThumbsRail === \"function\") renderThumbsRail();\n      };\n      tempImg.onerror = () => {\n        if (_stickerDone) return; _stickerDone = true;\n        const obj = { id: _dmbOid(\"sticker_\"), type: \"sticker\", name, url, x: 20, y: 20, w: 100, h: 100, z: Date.now(), rotation: 0 };\n        pushUndoState(\"Insert Sticker\");\n        if (isCover) { if (!Array.isArray(editor.coverObjects)) editor.coverObjects = []; _dmbPlaceOnActiveCoverFace(obj, 20, 20); editor.coverObjects.push(obj); }\n        else { if (!editor.pageObjects) editor.pageObjects = {}; if (!Array.isArray(editor.pageObjects[pageNumber])) editor.pageObjects[pageNumber] = []; editor.pageObjects[pageNumber].push(obj); }\n        if (typeof renderBuilderCanvas === \"function\") renderBuilderCanvas();\n        if (typeof renderThumbsRail === \"function\") renderThumbsRail();\n      };\n      tempImg.src = url;\n    });\n  }\n\n  function initPhoneUpload() {\n    [\"importPhoneBtn\", \"step1bPhoneBtn\"].forEach((id) => {\n      const btn = document.getElementById(id);\n      if (!btn || btn.dataset.boundPhone) return;\n      btn.dataset.boundPhone = \"true\";\n      btn.addEventListener(\"click\", () => {\n        const m = document.getElementById(\"photoImportMenu\");   // close the \"+ Add\" sheet behind the modal\n        if (m) m.style.display = \"none\";\n        openPhoneUploadModal();\n      });\n    });\n  }\n\n  // Shows the customer's supplied QR image (theme asset dmb-phone-qr.png) in the\n  // popup, served from Shopify's own CDN via dmbGetAssetUrl (the same reliable asset\n  // path the country maps use).\n  function _dmbRenderPhoneQR() {\n    const box = document.getElementById(\"dmbPhoneQR\");\n    if (!box) return;\n    let src = \"dmb-phone-qr.png\";\n    try { src = dmbGetAssetUrl(\"dmb-phone-qr.png\"); } catch (e) {}\n    box.innerHTML = '<img src=\"' + src + '\" alt=\"Scan to upload from phone\" style=\"width:100%;height:100%;object-fit:contain;display:block;\">';\n  }\n\n  function openPhoneUploadModal() {\n    {\n      // R2 RELAY (replaces the old PeerJS direct WebRTC link, which silently\n      // failed on cellular\u2194Wi-Fi \u2014 no TURN server \u2014 while the phone showed\n      // \"Sent\"). The phone uploads each photo to the shared session folder on\n      // R2; we poll it here and pull new photos down. 6-digit code so parallel\n      // customers can't collide.\n      const sessionId = String(Math.floor(100000 + Math.random() * 900000));\n      const uploadUrl = window.location.origin + \"/pages/phone-upload?s=\" + sessionId;\n      const qrSrc = null; // QR generated via qrcode.js into canvas\n\n      let modal = document.getElementById(\"dmbPhoneUploadModal\");\n      if (!modal) {\n        modal = document.createElement(\"div\");\n        modal.id = \"dmbPhoneUploadModal\";\n        modal.style.cssText =\n          \"position:fixed;inset:0;z-index:2147483647;background:rgba(0,0,0,.6);display:flex;align-items:center;justify-content:center;\";\n        modal.innerHTML = [\n          '<div style=\"background:#fff;border-radius:16px;padding:28px 24px;max-width:360px;width:92vw;text-align:center;position:relative;\">',\n          '<button id=\"dmbPhoneModalClose\" style=\"position:absolute;top:12px;right:16px;background:none;border:none;font-size:24px;cursor:pointer;line-height:1;\">&#x2715;</button>',\n          '<h3 style=\"margin:0 0 6px;font-size:17px;\">Upload from Phone</h3>',\n          '<p style=\"margin:0 0 14px;color:#666;font-size:13px;\">1. Scan the QR code with your phone camera (or open ' + window.location.host + '/pages/phone-upload)<br>2. Enter the session code below when asked</p>',\n          '<div id=\"dmbPhoneQR\" style=\"width:200px;height:200px;display:flex;align-items:center;justify-content:center;margin:0 auto 16px;border-radius:8px;border:1px solid #eee;overflow:hidden;\"></div>',\n          '<div style=\"background:#f5f5f5;border-radius:12px;padding:12px 20px;margin-bottom:16px;\">',\n          '<div style=\"font-size:11px;color:#999;margin-bottom:4px;letter-spacing:1px;text-transform:uppercase;\">Your session code</div>',\n          '<div id=\"dmbPhoneCode\" style=\"font-size:38px;font-weight:900;letter-spacing:6px;color:#111;\"></div>',\n          '</div>',\n          '<div id=\"dmbPhoneStatus\" style=\"font-size:14px;color:#999;padding:4px 0;\">Waiting for phone to connect\\u2026</div>',\n          '<div id=\"dmbPhoneCount\" style=\"font-size:13px;color:#2e7d32;margin-top:6px;display:none;\"></div>',\n          '</div>',\n        ].join(\"\");\n        document.body.appendChild(modal);\n        modal.addEventListener(\"click\", (e) => { if (e.target === modal) closeModal(); });\n      }\n\n      const statusEl = document.getElementById(\"dmbPhoneStatus\");\n      const countEl  = document.getElementById(\"dmbPhoneCount\");\n      const codeEl   = document.getElementById(\"dmbPhoneCode\");\n      const closeBtn = document.getElementById(\"dmbPhoneModalClose\");\n\n      codeEl.textContent = sessionId;\n      statusEl.textContent = \"Waiting for phone to connect\\u2026\";\n      statusEl.style.color = \"#999\";\n      countEl.style.display = \"none\";\n      countEl.textContent = \"\";\n      modal.style.display = \"flex\";\n      _dmbRenderPhoneQR(uploadUrl);   // regenerate for THIS session's code\n\n      let receivedCount = 0;\n      let phoneSeen = false;\n      const PROXY = window.DMB_DESIGNS_PROXY || \"/apps/designs\";\n      const sessQ = \"id=session&sid=\" + encodeURIComponent(\"pu\" + sessionId) + \"&asg=1\";\n      const seenPids = new Set([\"hello\", \"phone-hello\"]);\n      let fetching = false;\n      // Timers live on window so a stale closeModal closure (the backdrop-click\n      // listener is bound once, at modal creation) still stops the CURRENT poll.\n      clearInterval(window._dmbPuPoll); clearInterval(window._dmbPuSlow);\n\n      // Open the session: write the \"hello\" marker the phone validates against.\n      try {\n        fetch(PROXY + \"/photo?\" + sessQ + \"&pid=hello\", {\n          method: \"POST\", headers: { \"content-type\": \"application/json\" },\n          body: JSON.stringify({ ts: Date.now() }), credentials: \"same-origin\",\n        });\n      } catch (e) {}\n\n      async function injectFile(file) {\n        // Inject into step1b if builder not open yet, otherwise into builder tray\n        const step1bEl = document.getElementById(\"step1b\");\n        if (step1bEl && step1bEl.style.display !== \"none\") {\n          const input = document.getElementById(\"step1bFileInput\");\n          if (input && typeof DataTransfer !== \"undefined\") {\n            const dt = new DataTransfer();\n            for (const f of (input.files || [])) dt.items.add(f);\n            dt.items.add(file);\n            input.files = dt.files;\n            input.dispatchEvent(new Event(\"change\", { bubbles: true }));\n            return;\n          }\n        }\n        // \u2500\u2500 Derive HERE, because this branch bypasses compressFilesForMobile \u2500\u2500\n        // The step1b branch above dispatches a change event, which routes through\n        // compressFilesForMobile and produces the 2500px editing proxy + the print\n        // master. THIS branch (builder already open) went straight into bookFiles,\n        // so a relay photo arrived as a raw original with no master at all - which\n        // is exactly why a book built by adding photos from the phone mid-session\n        // reported \"0 of N photos at full print quality\".\n        //\n        // Awaited on purpose: the derivation decodes one image at a time, and the\n        // relay delivers photos one at a time, so this keeps the whole path to a\n        // single decode in flight - the same memory envelope as any other import.\n        // (2026-09-14)\n        let _f = file;\n        if (typeof _dmbPrintMasterOn === \"function\" && _dmbPrintMasterOn()) {\n          try { _f = (await compressFilesForMobile([file]))[0] || file; } catch (e2) {}\n        }\n        bookFiles = mergeFilesKeepOld(bookFiles, [_f]);\n        validatePhotos();\n        // renderPhotoTray() alone was NOT enough and was the bug: the tray draws\n        // from editor.photos, and NOTHING here rebuilt that from the new\n        // bookFiles. So a phone-relay photo bumped the \"N photos selected\"\n        // counter (which reads bookFiles) and the tray redrew the OLD list -\n        // the photo only appeared later, when some other action happened to call\n        // refreshBuilderPhotos(). Adding one photo from the device made every\n        // waiting relay photo pop in at once, which is exactly what was reported.\n        //\n        // refreshBuilderPhotos() is the only bridge bookFiles -> editor.photos.\n        // Debounced because the relay delivers photos one at a time: a burst of\n        // arrivals collapses into a single rebuild instead of one per photo.\n        try { _dmbWarmMeta(_f); } catch (e2) {}        // start its decode now\n        try {\n          clearTimeout(injectFile._t);\n          injectFile._t = setTimeout(function () {\n            try { refreshBuilderPhotos(); } catch (e3) {}\n          }, 200);\n        } catch (e4) { try { refreshBuilderPhotos(); } catch (e5) {} }\n      }\n\n      async function pollOnce() {\n        if (fetching) return;\n        fetching = true;\n        // Re-assert the \"hello\" marker each poll (until the phone is seen) so a phone\n        // that scans a moment early \u2014 or after the first write hiccuped \u2014 still finds\n        // the session while it retries for ~15s.\n        if (!phoneSeen) {\n          try {\n            fetch(PROXY + \"/photo?\" + sessQ + \"&pid=hello\", {\n              method: \"POST\", headers: { \"content-type\": \"application/json\" },\n              body: JSON.stringify({ ts: Date.now() }), credentials: \"same-origin\",\n            });\n          } catch (e) {}\n        }\n        try {\n          const r = await fetch(PROXY + \"/photos?\" + sessQ, { credentials: \"same-origin\" });\n          if (!r.ok) return;\n          const d = await r.json();\n          const list = (d && d.photos) || [];\n          // Phone announces itself with a marker so we can show \"connected\".\n          if (!phoneSeen && list.some((p) => p.pid === \"phone-hello\")) {\n            phoneSeen = true;\n            statusEl.textContent = \"\\uD83D\\uDCF1 Phone connected! Select photos on your phone.\";\n            statusEl.style.color = \"#2e7d32\";\n          }\n          for (const p of list) {\n            if (!p || !p.pid || seenPids.has(p.pid)) continue;\n            seenPids.add(p.pid);\n            try {\n              const pr = await fetch(PROXY + \"/photo?\" + sessQ + \"&pid=\" + encodeURIComponent(p.pid), { credentials: \"same-origin\" });\n              if (!pr.ok) { seenPids.delete(p.pid); continue; }   // retry next poll\n              const blob = await pr.blob();\n              if (!blob || !blob.size) { seenPids.delete(p.pid); continue; }\n              // Filename travels inside the pid (after \"__\").\n              const name = decodeURIComponent((p.pid.split(\"__\")[1] || (\"phone-\" + Date.now() + \".jpg\")));\n              const file = new File([blob], name, { type: blob.type || \"image/jpeg\", lastModified: Date.now() });\n              // QR-relay photos already live on R2 at phone resolution \u2014 mark them\n              // so the print-master pipeline doesn't try to derive a master (which\n              // would just re-encode the same bytes). They stay proxy-only.\n              try { file._dmbRelay = true; } catch (e2) {}\n              await injectFile(file);\n              receivedCount++;\n              countEl.style.display = \"block\";\n              countEl.textContent = receivedCount + \" photo\" + (receivedCount > 1 ? \"s\" : \"\") + \" received \\u2713\";\n            } catch (e) { seenPids.delete(p.pid); }\n          }\n        } catch (e) {} finally { fetching = false; }\n      }\n\n      window._dmbPuPoll = setInterval(pollOnce, 2500);\n      pollOnce();\n\n      function closeModal() {\n        modal.style.display = \"none\";\n        clearInterval(window._dmbPuPoll); window._dmbPuPoll = null;\n        // Keep a slow background poll for 5 more minutes \\u2014 photos the phone is\n        // still sending after the popup closes must not be lost.\n        clearInterval(window._dmbPuSlow);\n        window._dmbPuSlow = setInterval(pollOnce, 6000);\n        setTimeout(() => { clearInterval(window._dmbPuSlow); }, 5 * 60 * 1000);\n      }\n\n      closeBtn.onclick = closeModal;\n    }\n  }\n\n  function enableMapMarkerPlacement() {\n    document.addEventListener(\"click\", function(e) {\n      if (!pendingMapMarkerPlacement || !activeMarkerType) return;\n\n      const mapObject = e.target.closest(\".dmb-map-object\");\n      if (!mapObject) return;\n\n      const svg = mapObject.querySelector(\"svg\");\n      if (!svg) return;\n\n      const svgRect = svg.getBoundingClientRect();\n      const x = ((e.clientX - svgRect.left) / svgRect.width) * 100;\n      const y = ((e.clientY - svgRect.top) / svgRect.height) * 100;\n\n      if (!Array.isArray(currentInsertedMap.markers)) {\n        currentInsertedMap.markers = [];\n      }\n\n      currentInsertedMap.markers.push({\n        type: activeMarkerType,\n        x,\n        y\n      });\n\n      pendingMapMarkerPlacement = false;\n\n      if (typeof renderBuilderCanvas === \"function\") renderBuilderCanvas();\n      if (typeof renderThumbsRail === \"function\") renderThumbsRail();\n    });\n  }\n\n  function dmbCropSvgToContent(svgMarkup) {\n    // Returns { svg: croppedMarkup, aspect: width/height }\n    // Crops the SVG viewBox to tightly fit actual path content so the frame matches the visible map.\n    try {\n      const parser = new DOMParser();\n      const doc = parser.parseFromString(svgMarkup, \"image/svg+xml\");\n      const svg = doc.querySelector(\"svg\");\n      if (!svg) return { svg: svgMarkup, aspect: 1 };\n\n      const host = document.createElement(\"div\");\n      host.style.cssText = \"position:fixed;left:-9999px;top:-9999px;visibility:hidden;width:1px;height:1px;overflow:visible;\";\n      document.body.appendChild(host);\n      const svgClone = svg.cloneNode(true);\n      svgClone.setAttribute(\"width\", \"1000\");\n      svgClone.setAttribute(\"height\", \"1000\");\n      host.appendChild(svgClone);\n\n      const elements = Array.from(svgClone.querySelectorAll(\"path, polygon, polyline, circle, ellipse, rect\"));\n      let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;\n      elements.forEach(function(el) {\n        try {\n          const b = el.getBBox();\n          if (b.width > 0 || b.height > 0) {\n            minX = Math.min(minX, b.x);\n            minY = Math.min(minY, b.y);\n            maxX = Math.max(maxX, b.x + b.width);\n            maxY = Math.max(maxY, b.y + b.height);\n          }\n        } catch(e2) {}\n      });\n      document.body.removeChild(host);\n\n      if (!isFinite(minX) || maxX <= minX || maxY <= minY) {\n        return { svg: svgMarkup, aspect: 1 };\n      }\n\n      const pad = (maxX - minX) * 0.02; // 2% padding each side\n      const nx = minX - pad, ny = minY - pad;\n      const nw = maxX - minX + pad * 2;\n      const nh = maxY - minY + pad * 2;\n\n      svg.setAttribute(\"viewBox\", `${nx} ${ny} ${nw} ${nh}`);\n      svg.removeAttribute(\"width\");\n      svg.removeAttribute(\"height\");\n\n      return { svg: new XMLSerializer().serializeToString(svg), aspect: nw / nh };\n    } catch(e) { return { svg: svgMarkup, aspect: 1 }; }\n  }\n\n  function dmbFindIslandPaths(svgMarkup) {\n    try {\n      const parser = new DOMParser();\n      const doc = parser.parseFromString(svgMarkup, \"image/svg+xml\");\n      const svg = doc.querySelector(\"svg\");\n      if (!svg) return [];\n      const vb = svg.getAttribute(\"viewBox\");\n      let vbW = 100;\n      if (vb) { const parts = vb.trim().split(/[\\s,]+/); vbW = parseFloat(parts[2]) || 100; }\n      const threshold = Math.max(5, 14 * vbW / 270);\n      const host = document.createElement(\"div\");\n      host.style.cssText = \"position:fixed;left:-9999px;top:-9999px;visibility:hidden;width:1px;height:1px;overflow:visible;\";\n      document.body.appendChild(host);\n      const svgClone = svg.cloneNode(true);\n      svgClone.setAttribute(\"width\", \"500\"); svgClone.setAttribute(\"height\", \"500\");\n      host.appendChild(svgClone);\n      const elements = Array.from(svgClone.querySelectorAll(\"path, polygon, polyline, circle, ellipse, rect\"));\n      const boxes = elements.map(el => { try { return el.getBBox(); } catch(e) { return null; } });\n      document.body.removeChild(host);\n      if (elements.length < 2) return [];\n      const islands = [];\n      boxes.forEach((b, i) => {\n        if (!b || b.width === 0 && b.height === 0) return;\n        let minDist = Infinity;\n        boxes.forEach((b2, j) => {\n          if (i === j || !b2) return;\n          const dx = Math.max(0, Math.max(b.x, b2.x) - Math.min(b.x + b.width, b2.x + b2.width));\n          const dy = Math.max(0, Math.max(b.y, b2.y) - Math.min(b.y + b.height, b2.y + b2.height));\n          const dist = Math.sqrt(dx * dx + dy * dy);\n          if (dist < minDist) minDist = dist;\n        });\n        if (minDist > threshold) islands.push({ index: i });\n      });\n      return islands;\n    } catch (e) { return []; }\n  }\n\n  function dmbRemoveIslandPaths(svgMarkup, islandIndices) {\n    try {\n      const parser = new DOMParser();\n      const doc = parser.parseFromString(svgMarkup, \"image/svg+xml\");\n      const svg = doc.querySelector(\"svg\");\n      if (!svg) return svgMarkup;\n      const elements = Array.from(svg.querySelectorAll(\"path, polygon, polyline, circle, ellipse, rect\"));\n      islandIndices.map(i => i.index).sort((a, b) => b - a).forEach(idx => {\n        const el = elements[idx];\n        if (el && el.parentNode) el.parentNode.removeChild(el);\n      });\n      return new XMLSerializer().serializeToString(svg);\n    } catch (e) { return svgMarkup; }\n  }\n\n  function insertMapIntoActivePage(svgMarkup, mapKey, styles, forcedPage) {\n    const pageNumber = (forcedPage !== undefined && forcedPage !== null)\n      ? forcedPage : getActivePageNumberForMap();\n    if (pageNumber == null) return;\n\n    const isCover = pageNumber === \"cover\";\n    const s = styles || {};\n    const mapId = _dmbOid(\"map_\");\n\n    // Crop SVG viewBox to actual content bounds so frame matches visible map exactly\n    const cropped = dmbCropSvgToContent(svgMarkup);\n    svgMarkup = cropped.svg;\n\n    // Size map to fit its natural aspect ratio (not fill the whole page)\n    let mapX = 0, mapY = 0, mapW, mapH;\n    const MAX_MAP_SIZE = 180; // max starting dimension in px\n    const naturalAspect = cropped.aspect;\n    if (naturalAspect >= 1) {\n      mapW = MAX_MAP_SIZE;\n      mapH = Math.round(MAX_MAP_SIZE / naturalAspect);\n    } else {\n      mapH = MAX_MAP_SIZE;\n      mapW = Math.round(MAX_MAP_SIZE * naturalAspect);\n    }\n\n    if (isCover) {\n      const zone = (typeof activeCoverZone !== \"undefined\" && activeCoverZone) || \"front\";\n      const faceW = 300;\n      const faceH = Math.round(faceW * 1.414);\n      const zoneStartX = zone === \"front\" ? (window.DMB_MAGAZINE_MODE ? COVER_BACK_W : COVER_FRONT_X)\n                       : zone === \"back\"  ? 0\n                       : COVER_BACK_W;\n      mapX = zoneStartX + Math.round((faceW - mapW) / 2);\n      mapY = Math.round((faceH - mapH) / 2);\n    } else {\n      // Center in page body (270px wide, ~338px tall)\n      mapX = Math.round((270 - mapW) / 2);\n      mapY = Math.round((338 - mapH) / 2);\n    }\n\n    const mapObj = {\n      id: mapId,\n      type: \"map\",\n      name: mapKey || \"Map\",\n      mapKey: mapKey || \"\",\n      svg: svgMarkup,\n      x: mapX,\n      y: mapY,\n      w: mapW,\n      h: mapH,\n      // Was a hardcoded 20 - far below the Date.now() z every other insert uses,\n      // so a newly added map was buried under everything already on the page.\n      z: _dmbNextTopZ(pageNumber),\n      rotation: 0,\n      fill: s.fill || \"#d9d9d9\",\n      stroke: s.stroke || \"#111111\",\n      strokeWidth: s.strokeWidth != null ? s.strokeWidth : 1.2,\n      strokeDasharray: s.strokeDasharray || \"\",\n      opacity: s.opacity != null ? s.opacity : 1,\n      showOutline: s.showOutline || false,\n      hideProvinces: s.hideProvinces || false,\n      transparentFill: s.transparentFill || false,\n      filterId: \"dmbOF_\" + mapId,\n      markers: []\n    };\n\n    pushUndoState(\"Add Map\");   // persist to the restore key + make it undoable\n    if (isCover) {\n      if (!Array.isArray(editor.coverObjects)) editor.coverObjects = [];\n      editor.coverObjects.push(mapObj);\n    } else {\n      if (!editor.pageObjects || typeof editor.pageObjects !== \"object\") editor.pageObjects = {};\n      if (!Array.isArray(editor.pageObjects[pageNumber])) editor.pageObjects[pageNumber] = [];\n      editor.pageObjects[pageNumber].push(mapObj);\n    }\n\n    currentInsertedMap = mapObj;\n    pendingMapMarkerPlacement = false;\n\n    if (typeof renderBuilderCanvas === \"function\") renderBuilderCanvas();\n    if (typeof renderThumbsRail === \"function\") renderThumbsRail();\n  }\n  window.insertMapIntoActivePage = insertMapIntoActivePage;\n\n  function renderMapObject(obj, parent, pageIndex) {\n    const wrap = document.createElement(\"div\");\n    wrap.className = \"dmb-map-object\";\n    wrap.dataset.objectId = obj.id;\n    wrap.style.position = \"absolute\";\n    wrap.style.left = (obj.x || 0) + \"%\";\n    wrap.style.top = (obj.y || 0) + \"%\";\n    wrap.style.width = (obj.width || 70) + \"%\";\n    wrap.style.height = (obj.height || 70) + \"%\";\n    wrap.style.transform = `rotate(${obj.rotation || 0}deg)`;\n    wrap.style.transformOrigin = \"center center\";\n    wrap.style.cursor = \"move\";\n\n    wrap.innerHTML = obj.svg || \"\";\n\n    const svg = wrap.querySelector(\"svg\");\n    if (svg) {\n      svg.style.width = \"100%\";\n      svg.style.height = \"100%\";\n      svg.style.display = \"block\";\n\n      svg.querySelectorAll(\"path\").forEach((path) => {\n        path.style.fill = obj.fill || \"#d9d9d9\";\n        path.style.stroke = obj.stroke || \"#111111\";\n        path.style.strokeWidth = obj.strokeWidth || 1.2;\n        path.style.strokeDasharray = obj.strokeDasharray || \"\";\n        path.style.opacity = obj.opacity || 1;\n        path.style.vectorEffect = \"non-scaling-stroke\";\n      });\n    }\n\n    if (Array.isArray(obj.markers)) {\n      obj.markers.forEach((markerData) => {\n        const marker = createMarkerElement(markerData.type || \"pin\", markerData.color || \"#e74c3c\");\n        marker.style.left = (markerData.x || 50) + \"%\";\n        marker.style.top  = (markerData.y || 50) + \"%\";\n        wrap.appendChild(marker);\n        attachMarkerInteractions(marker, markerData, wrap);\n      });\n    }\n\n    parent.appendChild(wrap);\n    currentInsertedMap = obj;\n\n    /* click \u2192 open edit bar */\n    wrap.addEventListener(\"click\", (e) => {\n      if (e.target.closest(\".dmb-map-marker\")) return;\n      dmbOpenEditBar(obj, wrap);\n    });\n  }\n\n\n\n\n  function enableBasicDragging(node, container) {\n    let dragging = false;\n    let startX = 0;\n    let startY = 0;\n    let startLeft = 0;\n    let startTop = 0;\n\n    node.addEventListener(\"pointerdown\", (e) => {\n      if (e.target.closest(\".dmb-map-marker\")) return;\n      dragging = true;\n      startX = e.clientX;\n      startY = e.clientY;\n      startLeft = parseFloat(node.style.left || 0);\n      startTop = parseFloat(node.style.top || 0);\n      node.setPointerCapture?.(e.pointerId);\n      e.preventDefault();\n    });\n\n    node.addEventListener(\"pointermove\", (e) => {\n      if (!dragging) return;\n      const dx = e.clientX - startX;\n      const dy = e.clientY - startY;\n      node.style.left = `${startLeft + dx}px`;\n      node.style.top = `${startTop + dy}px`;\n    });\n\n    node.addEventListener(\"pointerup\", () => {\n      dragging = false;\n    });\n\n    node.addEventListener(\"pointercancel\", () => {\n      dragging = false;\n    });\n  }\n\n  function createMarkerElement(markerType, color) {\n    const c = color || \"#e74c3c\";\n    const marker = document.createElement(\"div\");\n    marker.className = \"dmb-map-marker\";\n    marker.dataset.markerType = markerType;\n    marker.style.position = \"absolute\";\n    marker.style.transform = \"translate(-50%, -50%)\";\n    marker.style.zIndex = \"10\";\n    marker.style.cursor = \"move\";\n    marker.style.userSelect = \"none\";\n\n    if (markerType === \"dot\") {\n      const dot = document.createElement(\"div\");\n      dot.className = \"dmb-map-marker-dot\";\n      dot.style.backgroundColor = c;\n      marker.appendChild(dot);\n    } else if (markerType === \"pin\") {\n      marker.innerHTML = `<svg width=\"18\" height=\"26\" viewBox=\"0 0 18 26\" xmlns=\"http://www.w3.org/2000/svg\"><ellipse cx=\"9\" cy=\"9\" rx=\"7.5\" ry=\"7.5\" fill=\"${c}\" stroke=\"white\" stroke-width=\"1.5\"/><polygon points=\"9,26 3.5,15 14.5,15\" fill=\"${c}\"/><circle cx=\"9\" cy=\"9\" r=\"2.8\" fill=\"white\"/></svg>`;\n    } else if (markerType === \"heart\") {\n      marker.style.fontSize = \"22px\";\n      marker.style.lineHeight = \"1\";\n      marker.style.color = c;\n      marker.style.textShadow = \"0 1px 3px rgba(0,0,0,0.4)\";\n      marker.textContent = \"\u2665\";\n    } else if (markerType === \"star\") {\n      marker.style.fontSize = \"22px\";\n      marker.style.lineHeight = \"1\";\n      marker.style.color = c;\n      marker.style.textShadow = \"0 1px 3px rgba(0,0,0,0.4)\";\n      marker.textContent = \"\u2605\";\n    }\n\n    return marker;\n  }\n\n  function updateMarkerColor(markerEl, markerType, color) {\n    if (markerType === \"dot\") {\n      const dot = markerEl.querySelector(\".dmb-map-marker-dot\");\n      if (dot) dot.style.backgroundColor = color;\n    } else if (markerType === \"pin\") {\n      markerEl.querySelectorAll(\"ellipse, polygon\").forEach(function(el) { el.setAttribute(\"fill\", color); });\n    } else {\n      markerEl.style.color = color;\n    }\n  }\n\n  function attachMarkerInteractions(marker, markerData, wrap) {\n    // Drag to reposition \u2014 updates markerData.x / markerData.y in %\n    marker.addEventListener(\"pointerdown\", function(e) {\n      if (e.button !== 0) return;\n      e.stopPropagation();\n      e.preventDefault();\n      const wRect = wrap.getBoundingClientRect();\n      const startX = e.clientX, startY = e.clientY;\n      const startPctX = markerData.x || 0, startPctY = markerData.y || 0;\n      marker.setPointerCapture(e.pointerId);\n      function onMove(ev) {\n        markerData.x = Math.max(0, Math.min(100, startPctX + ((ev.clientX - startX) / wRect.width)  * 100));\n        markerData.y = Math.max(0, Math.min(100, startPctY + ((ev.clientY - startY) / wRect.height) * 100));\n        marker.style.left = markerData.x + \"%\";\n        marker.style.top  = markerData.y + \"%\";\n      }\n      function onUp() {\n        document.removeEventListener(\"pointermove\", onMove);\n        document.removeEventListener(\"pointerup\", onUp);\n        if (typeof renderThumbsRail === \"function\") renderThumbsRail();\n      }\n      document.addEventListener(\"pointermove\", onMove);\n      document.addEventListener(\"pointerup\", onUp);\n    });\n\n    // Click to change color\n    marker.addEventListener(\"click\", function(e) {\n      e.stopPropagation();\n      const inp = document.createElement(\"input\");\n      inp.type = \"color\";\n      inp.value = markerData.color || \"#e74c3c\";\n      inp.style.cssText = \"position:fixed;opacity:0;width:0;height:0;pointer-events:none;\";\n      document.body.appendChild(inp);\n      inp.addEventListener(\"input\", function() {\n        markerData.color = inp.value;\n        updateMarkerColor(marker, markerData.type, inp.value);\n      });\n      inp.addEventListener(\"change\", function() {\n        markerData.color = inp.value;\n        updateMarkerColor(marker, markerData.type, inp.value);\n        if (typeof renderThumbsRail === \"function\") renderThumbsRail();\n        setTimeout(function() { inp.remove(); }, 500);\n      });\n      inp.click();\n    });\n  }\n\n  function enableBasicMarkerDragging(marker, container) {\n    let dragging = false;\n    let startX = 0;\n    let startY = 0;\n    let startLeft = 0;\n    let startTop = 0;\n\n    marker.addEventListener(\"pointerdown\", (e) => {\n      dragging = true;\n      startX = e.clientX;\n      startY = e.clientY;\n      startLeft = parseFloat(marker.style.left || 0);\n      startTop = parseFloat(marker.style.top || 0);\n      marker.setPointerCapture?.(e.pointerId);\n      e.stopPropagation();\n      e.preventDefault();\n    });\n\n    marker.addEventListener(\"pointermove\", (e) => {\n      if (!dragging) return;\n      const dx = e.clientX - startX;\n      const dy = e.clientY - startY;\n      marker.style.left = `${startLeft + dx}px`;\n      marker.style.top = `${startTop + dy}px`;\n    });\n\n    marker.addEventListener(\"pointerup\", () => {\n      dragging = false;\n    });\n\n    marker.addEventListener(\"pointercancel\", () => {\n      dragging = false;\n    });\n  }\n\n\n  function captureVisualText(el) {\n    // Serialize a text box's content to a string, preserving ONLY explicit line\n    // breaks: real \"\\n\" characters, <br> elements, and block-level children that\n    // the browser creates when the user presses Enter.\n    //\n    // It must NOT convert *soft* (CSS) wraps into hard newlines. The old version\n    // measured every character's on-screen top and inserted a \"\\n\" wherever a word\n    // wrapped to a new visual line. That baked the wrapping into the saved text, so\n    // a word that merely wrapped because the box was a touch narrow became a\n    // PERMANENT new line: backspacing the break merged it, but the next save\n    // re-detected the soft wrap and re-inserted the break (it \"snapped back\" after\n    // one action). The PDF renderer re-wraps pre-wrap boxes on its own, so soft\n    // wraps never need to be persisted. (Bonus: this drops a per-character\n    // getBoundingClientRect \u2014 a real typing/save lag source on touch.)\n    var result = [];\n    var curLine = \"\";\n    function walk(node) {\n      if (node.nodeType === 3) {\n        var parts = (node.nodeValue || \"\").split(\"\\n\");\n        for (var i = 0; i < parts.length; i++) {\n          if (i > 0) { result.push(curLine); curLine = \"\"; }\n          curLine += parts[i];\n        }\n      } else if (node.nodeName === \"BR\") {\n        result.push(curLine); curLine = \"\";\n      } else if (node.nodeType === 1) {\n        var disp = window.getComputedStyle(node).display || \"\";\n        var isBlock = disp !== \"inline\" && disp !== \"inline-block\" && disp !== \"none\";\n        if (isBlock && (result.length > 0 || curLine !== \"\")) { result.push(curLine); curLine = \"\"; }\n        node.childNodes.forEach(walk);\n      }\n    }\n    el.childNodes.forEach(walk);\n    result.push(curLine);\n    while (result.length > 1 && result[result.length - 1] === \"\") result.pop();\n    return result.join(\"\\n\");\n  }\n\n  // Derive a cover text node's zone (back/spine/front) from its horizontal centre,\n  // mirroring _dmbAutoAssignCoverZone. Used to re-register an orphaned text box.\n  function _dmbZoneForCoverNode(node) {\n    var left = parseFloat(node && node.style ? node.style.left : 0) || 0;\n    var w = (node && node.offsetWidth) || 0;\n    var cx = left + w / 2;\n    if (window.DMB_MAGAZINE_MODE) return cx < COVER_BACK_W ? \"back\" : \"front\";\n    if (cx < COVER_BACK_W) return \"back\";\n    if (cx < COVER_FRONT_X) return \"spine\";\n    return \"front\";\n  }\n\n  function saveCoverTextNodeState(node) {\n    if (!node || !node.id) return;\n    if (!coverState.textStyles) coverState.textStyles = {};\n    // Do NOT delete the box on a momentary empty. The 'input' handler saves on\n    // every keystroke, so clearing a template box to retype it (or a just-added\n    // box before the first character) passes through an empty state. Removing it\n    // from coverUserTexts orphaned the box \u2014 the retyped text still showed on\n    // screen (the DOM node lives) but was gone from state, so it was MISSING FROM\n    // THE PDF and wiped on the next re-render. Abandoned empty boxes are removed\n    // only on blur / click-out (see _dmbRemoveTextNode). Persist whatever's there.\n    const _coverTxt = captureVisualText(node);\n    // Carry the MEASURED fields forward. They are written by _captureCoverTextBoxes\n    // (which needs a live #coverOverlay) and are what lets the PDF place the title\n    // on its real ink centre instead of re-deriving it from box + line-height.\n    const _prevTS = (coverState.textStyles && coverState.textStyles[node.id]) || {};\n    // Inline runs: `rich` is measured-free so it is recomputed on every save;\n    // `richLines` needs the laid-out overlay, so it is carried forward here and\n    // refreshed by _captureCoverTextBoxes - exactly how renderedLines works.\n    const _richNow = _dmbSerializeRich(node);\n    coverState.textStyles[node.id] = {\n      renderedLines: _prevTS.renderedLines || \"\",\n      rich: _richNow,\n      richLines: _richNow ? (_prevTS.richLines || \"\") : \"\",\n      visCx:   _prevTS.visCx,   visCy:   _prevTS.visCy,\n      glyphCx: _prevTS.glyphCx, glyphCy: _prevTS.glyphCy,\n      text: _coverTxt,\n      left: node.style.left || \"\",\n      // Only the top is stored canonical (portrait-referenced) so landscape text\n      // lands on the shorter face; size stays exactly as the user set it.\n      top: _dmbCoverCanonicalTop(node.style.top),\n      // Store only explicit CSS width \u2014 NOT the parent's offsetWidth fallback.\n      width: node.style.width || \"\",\n      boxWidth:  String(node.offsetWidth  || 0),\n      boxHeight: String(node.offsetHeight || 0),\n      color: node.style.color || \"\",\n      fontFamily: node.style.fontFamily || \"\",\n      fontSize: node.style.fontSize || \"\",\n      fontWeight: node.style.fontWeight || \"\",\n      textAlign: node.style.textAlign || \"\",\n      textDecoration: node.style.textDecoration || \"\",\n      whiteSpace: node.style.whiteSpace || \"\",\n      transform: node.style.transform || \"\",\n      // Clamp floating-point drift (e.g. -2.84e-14) to \"0\" at save time so\n      // templates and session state never store near-zero garbage rotations.\n      rotate: (Math.abs(parseFloat(node.dataset.rotate || \"0\")) < 0.001 ? \"0\" : (node.dataset.rotate || \"0\")),\n      z: node.dataset.z || node.style.zIndex || \"20\"\n    };\n    // Self-heal registration: ensure a cover USER text box is listed in\n    // coverUserTexts so the PDF and re-renders include it. Repairs boxes orphaned\n    // by the earlier empty-strip bug, and re-registers a box the instant the user\n    // types into it. Guarded to nodes actually inside #coverOverlay so built-in\n    // titles and interior/page text can't be misfiled as cover user texts.\n    if (Array.isArray(editor.coverUserTexts) &&\n        !editor.coverUserTexts.some((e) => e.id === node.id)) {\n      const _ov = document.getElementById(\"coverOverlay\");\n      if (_ov && _ov.contains(node)) {\n        editor.coverUserTexts.push({ id: node.id, zone: _dmbZoneForCoverNode(node) });\n      }\n    }\n    // Refresh the measurements while the overlay still exists, rAF-debounced so a\n    // keystroke costs one layout pass. Without this the values above are only ever\n    // as fresh as the last time the cover view was open - and a customer who edits\n    // the cover, switches to Pages and checks out would print a stale capture.\n    try {\n      const _ovNow = document.getElementById(\"coverOverlay\");\n      if (_ovNow && _ovNow.contains(node) && typeof _dmbCaptureCoverTextBoxesFn === \"function\") {\n        if (!saveCoverTextNodeState._raf) {\n          saveCoverTextNodeState._raf = requestAnimationFrame(function () {\n            saveCoverTextNodeState._raf = 0;\n            try { _dmbCaptureCoverTextBoxesFn(); } catch (e) {}\n          });\n        }\n      }\n    } catch (e) {}\n  }\n\n  // -- Capture the browser's REAL rendered line breaks for a wrapped text box --\n  // A box narrowed with the side grip wraps at word boundaries (white-space:\n  // pre-wrap) and the BROWSER decides the breaks. The PDF must reuse the SAME\n  // words-per-line: re-wrapping with canvas measureText can break at a different\n  // word than the builder shows, which reflows the whole box (one line is ~25 mm\n  // on paper). Returns a JSON array of lines, or \"\" when the box is not wrapped\n  // (the PDF then splits on newlines only, which is exact).\n  // Measurement MUST happen un-rotated or getClientRects tops do not group into\n  // lines; pass alreadyUnrotated=true when the caller has already done that.\n  // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n  // INLINE FORMATTING - a look that belongs to SOME OF THE WORDS\n  //\n  // Everything else in this file treats a text box as one look: colour, font,\n  // size and underline live on the element and the PDF reads them off the saved\n  // object. Styling a few words means a box can hold several looks at once, and\n  // that has to survive three separate journeys, not one:\n  //\n  //   the DOM   spans inside the box while the customer is editing it\n  //   storage   localStorage / the cloud design, reopened on any device\n  //   the PDF   the canvas renderer, which must draw run by run\n  //\n  // So two fields are stored, because one cannot do both jobs:\n  //\n  //   rich       the content as a flat list of segments with EXPLICIT breaks.\n  //              This is what rebuilds the DOM. It must never contain a soft\n  //              (CSS) wrap, for exactly the reason obj.text must not: a wrap\n  //              written down as a break becomes a permanent line break.\n  //   richLines  the same content split into the VISUAL lines on screen, each\n  //              line a list of runs. This is what the PDF draws, so a wrap that\n  //              falls in the middle of a coloured phrase is split correctly.\n  //\n  // A box with a single look stores NEITHER - both stay empty and every path\n  // behaves exactly as it did before this existed. (2026-10-12)\n  const _DMB_RUN_PROPS = { c: \"color\", ff: \"fontFamily\", fs: \"fontSize\",\n                           fw: \"fontWeight\", fi: \"fontStyle\", td: \"textDecoration\" };\n  const _DMB_RUN_KEYS = Object.keys(_DMB_RUN_PROPS);\n\n  function _dmbRunStyleEmpty(st) {\n    if (!st) return true;\n    for (var i = 0; i < _DMB_RUN_KEYS.length; i++) if (st[_DMB_RUN_KEYS[i]]) return false;\n    return true;\n  }\n  function _dmbRunStyleSame(a, b) {\n    a = a || {}; b = b || {};\n    for (var i = 0; i < _DMB_RUN_KEYS.length; i++) {\n      var k = _DMB_RUN_KEYS[i];\n      if ((a[k] || \"\") !== (b[k] || \"\")) return false;\n    }\n    return true;\n  }\n\n  // The look that applies to one text node, relative to its box: the inline\n  // styles of every wrapper between them, innermost winning. Only inline style is\n  // read (never computed style) - a computed read would pick up the BOX's own\n  // font and record it on every run, which would then outlive a later change to\n  // the box. The legacy tags are here because contenteditable can still produce\n  // them on an undo or a paste.\n  function _dmbRunStyleOf(textNode, host) {\n    var out = {};\n    var chain = [], el = textNode ? textNode.parentNode : null;\n    while (el && el !== host) { chain.push(el); el = el.parentNode; }\n    if (el !== host) return out;                     // not inside this box\n    for (var i = chain.length - 1; i >= 0; i--) {    // outermost first\n      var n = chain[i], st = n.style;\n      if (st) {\n        for (var j = 0; j < _DMB_RUN_KEYS.length; j++) {\n          var k = _DMB_RUN_KEYS[j], v = st[_DMB_RUN_PROPS[k]];\n          if (v) out[k] = v;\n        }\n      }\n      var nm = n.nodeName;\n      if (nm === \"B\" || nm === \"STRONG\") { if (!out.fw) out.fw = \"800\"; }\n      if (nm === \"I\" || nm === \"EM\")     out.fi = \"italic\";\n      if (nm === \"U\")                    out.td = \"underline\";\n    }\n    return out;\n  }\n\n  // Flat segments + explicit breaks. Returns \"\" for a box with one look, so\n  // nothing is stored and nothing downstream changes.\n  function _dmbSerializeRich(host) {\n    if (!host) return \"\";\n    var segs = [], any = false;\n    function push(t, st) {\n      if (!t) return;\n      if (!_dmbRunStyleEmpty(st)) any = true;\n      var last = segs[segs.length - 1];\n      if (last && typeof last.t === \"string\" && _dmbRunStyleSame(last.s, st)) { last.t += t; return; }\n      segs.push(_dmbRunStyleEmpty(st) ? { t: t } : { t: t, s: st });\n    }\n    function brk() { segs.push({ br: 1 }); }\n    function walk(node) {\n      if (node.nodeType === 3) {\n        var st = _dmbRunStyleOf(node, host);\n        var parts = (node.nodeValue || \"\").split(\"\\n\");\n        for (var i = 0; i < parts.length; i++) { if (i > 0) brk(); push(parts[i], st); }\n      } else if (node.nodeName === \"BR\") {\n        brk();\n      } else if (node.nodeType === 1) {\n        var disp = \"\";\n        try { disp = window.getComputedStyle(node).display || \"\"; } catch (e) {}\n        var isBlock = disp !== \"inline\" && disp !== \"inline-block\" && disp !== \"none\";\n        if (isBlock && segs.length) brk();\n        node.childNodes.forEach(walk);\n      }\n    }\n    try { host.childNodes.forEach(walk); } catch (e) { return \"\"; }\n    if (!any) return \"\";\n    while (segs.length && segs[segs.length - 1].br) segs.pop();\n    // Last gate: the segments must spell EXACTLY what captureVisualText saved as\n    // obj.text. If the two ever disagreed, a reopened box would show different\n    // words or different breaks from the ones the PDF prints - so on any\n    // disagreement the formatting is dropped and the plain text stands. Losing a\n    // colour is recoverable; printing the wrong line breaks is not.\n    try {\n      var plain = segs.map(function (g) { return g.br ? \"\\n\" : (g.t || \"\"); }).join(\"\");\n      if (plain !== captureVisualText(host)) return \"\";\n    } catch (e) { return \"\"; }\n    return JSON.stringify(segs);\n  }\n\n  // Rebuild the box's content from stored segments. Built with createElement and\n  // assigned one style property at a time - never innerHTML - so a design file\n  // can only ever restore text and the six properties above.\n  function _dmbApplyRich(host, json, fsMap) {\n    if (!host || !json) return false;\n    var segs = null;\n    try { segs = JSON.parse(json); } catch (e) { return false; }\n    if (!Array.isArray(segs) || !segs.length) return false;\n    try {\n      while (host.firstChild) host.removeChild(host.firstChild);\n      segs.forEach(function (g) {\n        if (!g) return;\n        if (g.br) { host.appendChild(document.createElement(\"br\")); return; }\n        if (typeof g.t !== \"string\" || !g.t) return;\n        if (_dmbRunStyleEmpty(g.s)) { host.appendChild(document.createTextNode(g.t)); return; }\n        var sp = document.createElement(\"span\");\n        sp.setAttribute(\"data-dmb-run\", \"1\");\n        _DMB_RUN_KEYS.forEach(function (k) {\n          if (!g.s[k]) return;\n          var v = String(g.s[k]);\n          if (k === \"fs\" && typeof fsMap === \"function\") {\n            v = fsMap(parseFloat(v) || 0) || v;\n          }\n          try { sp.style[_DMB_RUN_PROPS[k]] = v; } catch (e) {}\n        });\n        sp.appendChild(document.createTextNode(g.t));\n        host.appendChild(sp);\n      });\n      return true;\n    } catch (e) { return false; }\n  }\n\n  // Strip every run back to one look. Used by the format painter: \"make this box\n  // look like that one\" cannot mean anything else while some of its words are\n  // still overriding the answer.\n  // Unwrap every element in a text box except <br> and our own run spans, keeping\n  // the text and the line breaks exactly where they are.\n  function _dmbStripForeignMarkup(host) {\n    try {\n      if (!host || !host.querySelectorAll) return false;\n      const els = host.querySelectorAll(\"*\");\n      let n = 0;\n      for (let i = els.length - 1; i >= 0; i--) {\n        const el = els[i];\n        if (el.nodeName === \"BR\") continue;\n        if (el.nodeName === \"SPAN\" && el.getAttribute(\"data-dmb-run\") === \"1\") continue;\n        // A BLOCK element is a line break in its own right - contenteditable makes\n        // one every time Enter is pressed, and captureVisualText reads it as a new\n        // line. Unwrapping it would silently join two lines, so only INLINE\n        // leftovers are unwrapped. Anything a block carries is read by\n        // _dmbRunStyleOf like any other wrapper, so nothing is lost by leaving it.\n        let _disp = \"\";\n        try { _disp = window.getComputedStyle(el).display || \"\"; } catch (e2) {}\n        if (_disp && _disp !== \"inline\" && _disp !== \"inline-block\") continue;\n        const p = el.parentNode;\n        if (!p) continue;\n        while (el.firstChild) p.insertBefore(el.firstChild, el);\n        p.removeChild(el);\n        n++;\n      }\n      if (n) host.normalize();\n      return n > 0;\n    } catch (e) { return false; }\n  }\n\n  function _dmbFlattenRuns(host) {\n    try {\n      if (!host || !host.querySelector) return false;\n      if (!host.querySelector(\"span, b, strong, i, em, u, font\")) return false;\n      host.textContent = captureVisualText(host);\n      return true;\n    } catch (e) { return false; }\n  }\n\n  // Remove ONE css property from every per-word run span so a WHOLE-BOX change to\n  // that property (toolbar with no text selected, or the corner-handle resize) is no\n  // longer overridden by a run's own value. Other per-word properties (e.g. colour\n  // when you change the size) are kept; a run left with no inline style is unwrapped.\n  function _dmbStripRunProp(host, cssProp) {\n    try {\n      if (!host || !host.querySelectorAll) return;\n      host.querySelectorAll(\"span[data-dmb-run]\").forEach(function (sp) {\n        try {\n          sp.style.removeProperty(cssProp);\n          var styleAttr = sp.getAttribute(\"style\");\n          if (!styleAttr || !styleAttr.trim()) {\n            var parent = sp.parentNode; if (!parent) return;\n            while (sp.firstChild) parent.insertBefore(sp.firstChild, sp);\n            parent.removeChild(sp);\n          }\n        } catch (e) {}\n      });\n      try { host.normalize(); } catch (e) {}\n    } catch (e) {}\n  }\n\n  // The visual lines, each as a list of runs. Deliberately NOT built on\n  // _dmbCaptureRenderedLines: that one groups words by the top of their rect,\n  // which is sound only while every word is the same size. Two sizes on one line\n  // share a BASELINE, not a top, so a small word beside a big one looks exactly\n  // like a new line to that test. Line breaks are found here by the word going\n  // BACKWARDS horizontally, which is true of a real break and of nothing else:\n  // within a line each word starts at or after the previous word's right edge.\n  // The vertical test is kept as well, but only between words of equal height,\n  // where it is the original behaviour.\n  function _dmbCaptureLineRuns(node, alreadyUnrotated) {\n    if (!node) return \"\";\n    var tf = null;\n    try {\n      if (!alreadyUnrotated) { tf = node.style.transform; node.style.transform = \"none\"; }\n      var lines = [], cur = [];\n      var pRight = null, pTop = null, pH = null;\n      var pushRun = function (t, st) {\n        if (!t) return;\n        var last = cur[cur.length - 1];\n        if (last && _dmbRunStyleSame(last.s, st)) { last.t += t; return; }\n        cur.push(_dmbRunStyleEmpty(st) ? { t: t } : { t: t, s: st });\n      };\n      var endLine = function () {\n        // Trailing whitespace at a wrap is not drawn, so it is not stored.\n        while (cur.length) {\n          var last = cur[cur.length - 1];\n          last.t = last.t.replace(/\\s+$/, \"\");\n          if (last.t) break;\n          cur.pop();\n        }\n        lines.push(cur); cur = [];\n      };\n      var tw = document.createTreeWalker(node, NodeFilter.SHOW_TEXT);\n      var wr = document.createRange(), tn;\n      while ((tn = tw.nextNode())) {\n        var st = _dmbRunStyleOf(tn, node);\n        var txt = tn.textContent || \"\";\n        var i = 0;\n        while (i < txt.length) {\n          var m = /^(\\s+|\\S+)/.exec(txt.slice(i));\n          var chunk = m ? m[1] : txt.slice(i);\n          if (/\\S/.test(chunk)) {\n            wr.setStart(tn, i); wr.setEnd(tn, i + chunk.length);\n            var r0 = wr.getClientRects()[0];\n            if (r0 && r0.height > 0) {\n              var sameSize = (pH == null) || Math.abs(r0.height - pH) <= Math.max(1, pH * 0.1);\n              var broke = (pRight != null && r0.left < pRight - 1) ||\n                          (sameSize && pTop != null && (r0.top - pTop) > pH * 0.5);\n              if (broke) { endLine(); }\n              pRight = r0.right; pTop = r0.top; pH = r0.height;\n            }\n          }\n          pushRun(chunk, st);\n          i += chunk.length;\n        }\n      }\n      endLine();\n      if (lines.length && lines.some(function (ln) { return ln.length; })) return JSON.stringify(lines);\n    } catch (e) {} finally {\n      if (tf !== null) { try { node.style.transform = tf; } catch (e2) {} }\n    }\n    return \"\";\n  }\n\n  // ---- applying a look to the SELECTED WORDS -------------------------------\n  // The toolbar controls steal focus (a <select>, a colour input, a button), and\n  // on some browsers that drops the highlight. So the last real selection inside\n  // a text box is remembered and used when the control fires. A COLLAPSED caret\n  // clears it on purpose: clicking into a box and then picking a colour has\n  // always meant \"the whole box\", and it still does.\n  let _dmbLastTextRange = null;\n  document.addEventListener(\"selectionchange\", function () {\n    try {\n      const sel = window.getSelection();\n      if (!sel || !sel.rangeCount) return;\n      const r = sel.getRangeAt(0);\n      const c = r.commonAncestorContainer;\n      const el = c && (c.nodeType === 3 ? c.parentNode : c);\n      const box = (el && el.closest) ? el.closest(\".dmb-editable-text\") : null;\n      if (!box) return;                        // a selection elsewhere changes nothing\n      _dmbLastTextRange = r.collapsed ? null : r.cloneRange();\n    } catch (e) {}\n  });\n\n  function _dmbActiveTextRange(host) {\n    const ok = function (r) {\n      if (!r || r.collapsed) return null;\n      const c = r.commonAncestorContainer;\n      const el = c && (c.nodeType === 3 ? c.parentNode : c);\n      return (el && host && host.contains(el)) ? r : null;\n    };\n    try {\n      const sel = window.getSelection();\n      if (sel && sel.rangeCount) { const r = ok(sel.getRangeAt(0)); if (r) return r; }\n    } catch (e) {}\n    try {\n      if (_dmbLastTextRange && _dmbLastTextRange.startContainer &&\n          _dmbLastTextRange.startContainer.isConnected) return ok(_dmbLastTextRange);\n    } catch (e) {}\n    return null;\n  }\n\n  // Is every selected word underlined? Drives the U button's toggle, which has to\n  // mean \"underline this selection\" / \"take it off this selection\".\n  function _dmbSelUnderlined(host) {\n    const nodes = _dmbSelTextNodes(host);\n    if (!nodes.length) return false;\n    const boxU = !!(host.style.textDecoration && host.style.textDecoration.indexOf(\"underline\") !== -1);\n    for (let i = 0; i < nodes.length; i++) {\n      if (!/\\S/.test(nodes[i].nodeValue || \"\")) continue;\n      const td = _dmbRunStyleOf(nodes[i], host).td;\n      const on = (td != null && td !== \"\") ? (td.indexOf(\"underline\") !== -1) : boxU;\n      if (!on) return false;\n    }\n    return true;\n  }\n\n  // The text nodes the selection covers WHOLE, after splitting the two boundary\n  // nodes so a part-word selection becomes its own node.\n  function _dmbSelTextNodes(host, split) {\n    const r = _dmbActiveTextRange(host);\n    if (!r) return [];\n    if (split) {\n      try {\n        if (r.endContainer.nodeType === 3 && r.endOffset < (r.endContainer.nodeValue || \"\").length) {\n          r.endContainer.splitText(r.endOffset);\n        }\n        if (r.startContainer.nodeType === 3 && r.startOffset > 0) {\n          const after = r.startContainer.splitText(r.startOffset);\n          r.setStart(after, 0);\n        }\n      } catch (e) { return []; }\n    }\n    const out = [];\n    try {\n      const tw = document.createTreeWalker(host, NodeFilter.SHOW_TEXT);\n      let tn;\n      while ((tn = tw.nextNode())) {\n        if (!tn.nodeValue) continue;\n        try {\n          if (r.comparePoint(tn, 0) >= 0 && r.comparePoint(tn, tn.nodeValue.length) <= 0) out.push(tn);\n        } catch (e) {}\n      }\n    } catch (e) {}\n    return out;\n  }\n\n  function _dmbStyleSelection(host, props) {\n    const nodes = _dmbSelTextNodes(host, true);\n    if (!nodes.length) return false;\n    nodes.forEach(function (t) {\n      let holder = t.parentNode;\n      const reusable = holder && holder !== host && holder.nodeName === \"SPAN\" &&\n                       holder.childNodes.length === 1 &&\n                       holder.getAttribute && holder.getAttribute(\"data-dmb-run\") === \"1\";\n      if (!reusable) {\n        const sp = document.createElement(\"span\");\n        sp.setAttribute(\"data-dmb-run\", \"1\");\n        // Nothing is copied onto the new wrapper: it sits INSIDE whatever was\n        // already there, so CSS inheritance carries the rest of the look.\n        t.parentNode.insertBefore(sp, t);\n        sp.appendChild(t);\n        holder = sp;\n      }\n      Object.keys(props).forEach(function (k) {\n        try { holder.style[k] = props[k]; } catch (e) {}\n      });\n    });\n    try {\n      const sel = window.getSelection();\n      const nr = document.createRange();\n      nr.setStartBefore(nodes[0]);\n      nr.setEndAfter(nodes[nodes.length - 1]);\n      sel.removeAllRanges(); sel.addRange(nr);\n      _dmbLastTextRange = nr.cloneRange();\n    } catch (e) {}\n    return true;\n  }\n\n  // Route one toolbar change to the selected words. Returns false when there is\n  // no selection, and the caller then does what it always did to the whole box.\n  function _dmbApplyToTextSelection(props, label) {\n    const node = (typeof selectedTextNode !== \"undefined\") ? selectedTextNode : null;\n    if (!node || !node.isConnected) return false;\n    if (!_dmbActiveTextRange(node)) return false;\n    try { pushUndoState(label || \"Text Edit\"); } catch (e) {}\n    if (!_dmbStyleSelection(node, props)) return false;\n    try { saveCoverTextNodeState(node); } catch (e) {}\n    try { savePageTextState(node); } catch (e) {}\n    try { saveCoverDesignToStorage(); } catch (e) {}\n    try { _dmbWriteProgressState(); } catch (e) {}\n    try { if (typeof _dmbCloudPersist === \"function\") _dmbCloudPersist(); } catch (e) {}\n    try { positionTextSelectFrame(node); positionRotateHandle(node); } catch (e) {}\n    return true;\n  }\n\n  function _dmbCaptureRenderedLines(node, alreadyUnrotated) {\n    if (!node) return \"\";\n    let _tf = null;\n    try {\n      if (!alreadyUnrotated) { _tf = node.style.transform; node.style.transform = \"none\"; }\n      if (getComputedStyle(node).whiteSpace !== \"pre-wrap\") return \"\";\n      const _lines = [];\n      let _curTop = null, _cur = \"\";\n      const _tw = document.createTreeWalker(node, NodeFilter.SHOW_TEXT);\n      const _wr = document.createRange();\n      let _tn;\n      while ((_tn = _tw.nextNode())) {\n        const _txt = _tn.textContent;\n        let _i = 0;\n        while (_i < _txt.length) {\n          const _m = /^(\\s+|\\S+)/.exec(_txt.slice(_i));\n          const _chunk = _m ? _m[1] : _txt.slice(_i);\n          if (/\\S/.test(_chunk)) {\n            _wr.setStart(_tn, _i); _wr.setEnd(_tn, _i + _chunk.length);\n            const _r0 = _wr.getClientRects()[0];\n            if (_r0 && _r0.height > 0) {\n              if (_curTop !== null && (_r0.top - _curTop) > _r0.height * 0.5) {\n                _lines.push(_cur.replace(/\\s+$/, \"\")); _cur = \"\";\n              }\n              _curTop = _r0.top;\n            }\n          }\n          _cur += _chunk;\n          _i += _chunk.length;\n        }\n      }\n      _lines.push(_cur.replace(/\\s+$/, \"\"));\n      if (_lines.length && _lines.some((l) => l !== \"\")) return JSON.stringify(_lines);\n    } catch (e) {} finally {\n      if (_tf !== null) { try { node.style.transform = _tf; } catch (e2) {} }\n    }\n    return \"\";\n  }\n\n  // -- Wrap a text object the way the BROWSER would, for designs saved before\n  // page text stored its own line breaks. Builds a throwaway offscreen replica of\n  // the box (same class, width, font and padding as the real node in renderSpread)\n  // and reads the real line breaks off it. This is the same layout engine that put\n  // the text on screen, so it beats canvas measureText - which measures glyphs in a\n  // different context and can break at a different word, reflowing the whole box.\n  // Everything stays in BUILDER px (unscaled): wrapping depends on the ratio of text\n  // width to box width, so the 300 DPI scale factor is irrelevant here.\n  function _dmbWrapLinesViaDom(obj) {\n    if (!obj || !obj.text) return null;\n    let d = null;\n    try {\n      d = document.createElement(\"div\");\n      d.className = \"dmb-editable-text\";          // min-width / line-height / text-align\n      d.style.position = \"absolute\";\n      d.style.left = \"-99999px\";\n      d.style.top = \"0\";\n      d.style.opacity = \"0\";                      // keeps layout (display:none would not)\n      d.style.pointerEvents = \"none\";\n      d.style.transform = \"none\";                 // measure un-rotated\n      // Cover items store width as a bare number; page objects store \"180px\".\n      let _w = String(obj.width == null ? \"\" : obj.width).trim();\n      if (_w && /^[0-9.]+$/.test(_w)) _w += \"px\";\n      if (_w) d.style.width = _w;\n      d.style.fontSize = obj.fontSize || \"28px\";\n      d.style.fontWeight = obj.fontWeight || \"800\";\n      d.style.fontFamily = obj.fontFamily || \"'Londrina Solid', sans-serif\";\n      if (obj.textAlign) d.style.textAlign = obj.textAlign;\n      if (obj.textDecoration) d.style.textDecoration = obj.textDecoration;\n      d.style.whiteSpace = obj.whiteSpace || \"pre\";\n      d.style.padding = \"2px 6px\";                // matches the real node + TX_PL/TX_PT\n      d.textContent = obj.text;\n      document.body.appendChild(d);\n      const _json = _dmbCaptureRenderedLines(d, true);\n      if (!_json) return null;\n      const _arr = JSON.parse(_json);\n      return (Array.isArray(_arr) && _arr.length) ? _arr : null;\n    } catch (e) {\n      return null;\n    } finally {\n      if (d) { try { d.remove(); } catch (e2) {} }\n    }\n  }\n\n  function savePageTextState(node) {\n    if (!node || !node.id) return;\n    const pn = parseInt(node.dataset.pageNumber);\n    if (!Number.isFinite(pn)) return;\n    if (!Array.isArray(editor.pageObjects?.[pn])) return;\n    // If the text box has been fully cleared, remove it from state so a\n    // re-render (triggered by adding stickers, frames, etc.) cannot resurrect it.\n    // Do NOT delete the box on a momentary empty (input saves per keystroke;\n    // clearing to retype would orphan it \u2192 missing from PDF + wiped on re-render).\n    // Abandoned empties are removed on blur / click-out via _dmbRemoveTextNode.\n    const _capturedText = captureVisualText(node);\n    const obj = editor.pageObjects[pn].find((o) => o.id === node.id);\n    if (!obj) return;\n    obj.text = _capturedText;\n    obj.left = node.style.left || \"\";\n    obj.top = node.style.top || \"\";\n    // Store only explicit CSS width \u2014 NOT the parent offsetWidth fallback.\n    // The parent fallback (\u2248270px page width) is meaningless for positioning and\n    // causes wrong textAlign centering in the PDF (text centred across whole page).\n    obj.width = node.style.width || \"\";\n    obj.color = node.style.color || \"\";\n    obj.fontFamily = node.style.fontFamily || \"\";\n    obj.fontSize = node.style.fontSize || \"\";\n    obj.fontWeight = node.style.fontWeight || \"\";\n    obj.textAlign = node.style.textAlign || \"\";\n    obj.textDecoration = node.style.textDecoration || \"\";\n    obj.whiteSpace = node.style.whiteSpace || \"\";\n    obj.transform = node.style.transform || \"\";\n    // Clamp floating-point drift (e.g. -2.84e-14) to \"0\" at save time.\n    obj.rotate = (Math.abs(parseFloat(node.dataset.rotate || \"0\")) < 0.001 ? \"0\" : (node.dataset.rotate || \"0\"));\n    obj.z = node.dataset.z || node.style.zIndex || \"20\";\n    // \u2500\u2500 boxWidth / boxHeight: exact DOM element dimensions (pre-rotation) \u2500\u2500\n    // The PDF renderer uses these to compute the rotation pivot (element centre =\n    // left + boxWidth/2, top + boxHeight/2).  Canvas text measurement can differ\n    // from the browser by 1\u20133px; for rotated text that small error visibly shifts\n    // the anchor.  Storing the real DOM size here eliminates the discrepancy.\n    // (Cover text already saves these \u2014 this brings page text in line with it.)\n    obj.boxWidth  = String(node.offsetWidth  || 0);\n    obj.boxHeight = String(node.offsetHeight || 0);\n    // Rendered line breaks. Cover text has always stored these; PAGE text did not,\n    // so a narrowed page text box was RE-wrapped by the PDF with canvas metrics and\n    // could break at a different word than the customer designed.\n    obj.renderedLines = _dmbCaptureRenderedLines(node);\n    // Inline runs. `rich` rebuilds the box, `richLines` is what the PDF draws.\n    // Both are \"\" for a box with one look, which is every box until someone\n    // styles part of one.\n    obj.rich = _dmbSerializeRich(node);\n    obj.richLines = obj.rich ? _dmbCaptureLineRuns(node) : \"\";\n  }\n\n  function applyCoverTextNodeState(node) {\n    if (!node || !node.id) return;\n    const saved = coverState.textStyles && coverState.textStyles[node.id];\n    if (!saved) return;\n\n    if (saved.text != null) node.textContent = saved.text;\n    if (saved.left) node.style.left = saved.left;\n    if (saved.top) {\n      // Scale canonical (portrait-referenced) top to the current face so a\n      // landscape face never clips the text. Portrait scale = 1 \u2192 unchanged.\n      // Keep a percentage as a percentage - scaling only applies to canonical px.\n      // (The sibling restore inside renderCoverCanvas already guarded this; this one\n      // did not, so it re-corrupted spine text even after the save was fixed.)\n      var _tpRaw = String(saved.top).trim();\n      var _tp = parseFloat(_tpRaw);\n      node.style.top = (_tpRaw.slice(-1) !== \"%\" && isFinite(_tp))\n        ? ((_tp * _dmbCoverTopScale()) + \"px\") : saved.top;\n    }\n    if (saved.width) node.style.width = saved.width;\n    if (saved.color) node.style.color = saved.color;\n    if (saved.fontFamily) node.style.fontFamily = saved.fontFamily;\n    if (saved.fontSize) node.style.fontSize = saved.fontSize;\n    if (saved.textAlign) node.style.textAlign = saved.textAlign;\n    if (saved.textDecoration) node.style.textDecoration = saved.textDecoration;\n    if (saved.transform) node.style.transform = saved.transform;\n    if (saved.rotate != null) node.dataset.rotate = saved.rotate;\n    if (saved.z != null) {\n      node.dataset.z = saved.z;\n      node.style.zIndex = saved.z;\n    }\n  }\n\n\n  function getPageTemplateId(pageNumber) {\n    return editor.pageTemplate?.[pageNumber] || null;\n  }\n\n  function isTemplatePage(pageNumber) {\n    return !!getPageTemplateId(pageNumber);\n  }\n\n  function ensureTemplateState(pageNumber, templateId) {\n    if (!editor.pageTemplate) editor.pageTemplate = {};\n    if (!editor.pageFrames) editor.pageFrames = {};\n    if (!editor.pageFrameRot) editor.pageFrameRot = {};\n\n    editor.pageTemplate[pageNumber] = templateId || null;\n\n    if (templateId) {\n      const def = TEMPLATE_DEFS[templateId] || [];\n      editor.pageFrames[pageNumber] = editor.pageFrames[pageNumber] || {};\n      editor.pageFrameRot[pageNumber] = editor.pageFrameRot[pageNumber] || {};\n\n      def.forEach((f) => {\n        if (!(f.key in editor.pageFrames[pageNumber])) editor.pageFrames[pageNumber][f.key] = null;\n        if (!(f.key in editor.pageFrameRot[pageNumber])) editor.pageFrameRot[pageNumber][f.key] = 0;\n      });\n\n      Object.keys(editor.pageFrames[pageNumber]).forEach((k) => {\n        if (!def.find((d) => d.key === k)) delete editor.pageFrames[pageNumber][k];\n      });\n      Object.keys(editor.pageFrameRot[pageNumber]).forEach((k) => {\n        if (!def.find((d) => d.key === k)) delete editor.pageFrameRot[pageNumber][k];\n      });\n    } else {\n      editor.pageFrames[pageNumber] = {};\n      editor.pageFrameRot[pageNumber] = {};\n    }\n  }\n\n  function clearPageAllLayouts(pageNumber) {\n    if (isLockedPage(pageNumber)) return;\n    if (editor.pageSlots?.[pageNumber]) {\n      editor.pageSlots[pageNumber].A = null;\n      editor.pageSlots[pageNumber].B = null;\n    }\n    if (editor.pageRot?.[pageNumber]) {\n      editor.pageRot[pageNumber].A = 0;\n      editor.pageRot[pageNumber].B = 0;\n    }\n    if (editor.pageFrames?.[pageNumber]) {\n      Object.keys(editor.pageFrames[pageNumber]).forEach((k) => editor.pageFrames[pageNumber][k] = null);\n    }\n    if (editor.pageFrameRot?.[pageNumber]) {\n      Object.keys(editor.pageFrameRot[pageNumber]).forEach((k) => editor.pageFrameRot[pageNumber][k] = 0);\n    }\n    if (editor.pageObjects?.[pageNumber]) {\n      // Keep whatever reaches across the fold. Clearing THIS page's layout must not\n      // silently destroy content the customer can see on the FACING page - a spanning\n      // object belongs to the spread as much as to this page, and the no-photos branch\n      // of setPageTemplateWithConfirm reaches here with no confirmation at all.\n      const _objs = editor.pageObjects[pageNumber];\n      editor.pageObjects[pageNumber] = Array.isArray(_objs)\n        ? _objs.filter(function (o) { return _dmbObjOverhangs(o, pageNumber); })\n        : [];\n    }\n    // Note: spreadPhoto is independent \u2014 only removed via its own X button\n  }\n\n  function clearPagePhotosOnly(pageNumber) {\n    if (isLockedPage(pageNumber)) return;\n    if (editor.pageSlots?.[pageNumber]) {\n      editor.pageSlots[pageNumber].A = null;\n      editor.pageSlots[pageNumber].B = null;\n    }\n    if (editor.pageRot?.[pageNumber]) {\n      editor.pageRot[pageNumber].A = 0;\n      editor.pageRot[pageNumber].B = 0;\n    }\n    if (editor.pageFrames?.[pageNumber]) {\n      Object.keys(editor.pageFrames[pageNumber]).forEach(k => { editor.pageFrames[pageNumber][k] = null; });\n    }\n    if (editor.pageFrameRot?.[pageNumber]) {\n      Object.keys(editor.pageFrameRot[pageNumber]).forEach(k => { editor.pageFrameRot[pageNumber][k] = 0; });\n    }\n    // Note: spreadPhoto is independent \u2014 only removed via its own X button\n  }\n\n  // Copy an entire page's layout/content onto another page. `withPhotos=false` empties\n  // every frame (keeps the structure \u2014 template, frame shapes, text, stickers, shapes &\n  // background) so the copy can be filled with different photos. Fully REPLACES `dst`.\n  // Spread photos are per-spread, not per-page, so they are intentionally left alone.\n  function _dmbDuplicatePageTo(src, dst, withPhotos) {\n    if (isLockedPage(dst) || src === dst) return;\n    const _cl = (v, fb) => (v == null ? fb : cloneState(v));\n    editor.pageTemplate    = editor.pageTemplate    || {};\n    editor.pageSlots       = editor.pageSlots       || {};\n    editor.pageRot         = editor.pageRot         || {};\n    editor.pageFrames      = editor.pageFrames      || {};\n    editor.pageFrameRot    = editor.pageFrameRot    || {};\n    editor.pageFrameShapes = editor.pageFrameShapes || {};\n    editor.pageObjects     = editor.pageObjects     || {};\n    editor.pageBackgrounds = editor.pageBackgrounds || {};\n\n    editor.pageTemplate[dst]    = editor.pageTemplate[src] || null;\n    editor.pageFrameShapes[dst] = _cl(editor.pageFrameShapes[src], {});                     // frame shapes = layout, always\n    editor.pageFrameRot[dst]    = withPhotos ? _cl(editor.pageFrameRot[src], {}) : {};\n    editor.pageFrames[dst]      = withPhotos ? _cl(editor.pageFrames[src], {}) : {};\n    editor.pageSlots[dst]       = withPhotos ? _cl(editor.pageSlots[src], { A: null, B: null }) : { A: null, B: null };\n    editor.pageRot[dst]         = withPhotos ? _cl(editor.pageRot[src], { A: 0, B: 0 }) : { A: 0, B: 0 };\n\n    if (editor.pageBackgrounds[src] != null) editor.pageBackgrounds[dst] = editor.pageBackgrounds[src];\n    else delete editor.pageBackgrounds[dst];\n\n    const srcObjs = Array.isArray(editor.pageObjects[src]) ? editor.pageObjects[src] : [];\n    editor.pageObjects[dst] = srcObjs.map((o) => {\n      const c = cloneState(o);\n      c.id = uid();\n      c.page = dst;\n      c.targetType = \"page\";\n      if (!withPhotos && c.type === \"photo-frame\") c.photoId = null;   // empty the frame\n      return c;\n    });\n    // A spanning object is positioned against ONE specific fold. Copied verbatim onto a\n    // page of the opposite parity its overhang points the wrong way entirely - off the\n    // OUTER edge, onto a sheet the customer never put it near. Pull the fold-side\n    // overhang back inside the destination; deliberate bleed off the outer edge is left\n    // exactly as it was, and a same-parity copy with a real partner keeps its span.\n    try {\n      const _dstLeft = (dst % 2 === 1);\n      const _dstSib  = _dstLeft ? dst + 1 : dst - 1;\n      const _keepSpan = ((src % 2) === (dst % 2)) &&\n                        _dstSib >= 1 && _dstSib <= (editor.totalPages || 0);\n      if (!_keepSpan) {\n        const _PWd = getPageDims().w;\n        editor.pageObjects[dst].forEach(function (c) {\n          const ow = c.w || 0, x0 = c.x || 0;\n          const nx = _dstLeft ? Math.min(x0, _PWd - ow) : Math.max(x0, 0);\n          if (nx === x0) return;\n          const d = nx - x0;\n          c.x = nx;\n          if (c.type === \"shape\" && c.shapeType === \"line\") {\n            if (c.x1 != null) c.x1 += d;\n            if (c.x2 != null) c.x2 += d;\n            if (c.bx != null) c.bx += d;\n          }\n          if (c.straddle) delete c.straddle;\n        });\n      }\n    } catch (e) {}\n    try { _dmbInvalidateSibCache(); } catch (e) {}\n  }\n\n  // Elegant two-step \"Duplicate page\" modal. Step 1: destination \u2014 a spread button\n  // and a \"Choose another page\" button that expands a small inline number field right\n  // under it. Step 2: copy with photos or layout-only. Animated, dark, self-contained.\n  function _dmbOpenDuplicatePageModal(srcPage) {\n    if (document.getElementById(\"dmbDupPageModal\")) return;\n    const total = editor.totalPages;\n    const other = (srcPage % 2 === 1) ? (srcPage + 1) : (srcPage - 1);\n    const otherValid = other >= 2 && other <= total && !isLockedPage(other) && other !== srcPage;\n\n    const el = (tag, cls, txt) => { const e = document.createElement(tag); if (cls) e.className = cls; if (txt != null) e.textContent = txt; return e; };\n    const optBtn = (title, subtitle, primary) => {\n      const b = el(\"button\", \"dmb-dupm-opt\" + (primary ? \" primary\" : \"\")); b.type = \"button\";\n      const tw = el(\"span\", \"dmb-dupm-optlabel\");\n      tw.appendChild(el(\"span\", \"dmb-dupm-optt\", title));\n      if (subtitle) tw.appendChild(el(\"span\", \"dmb-dupm-opts2\", subtitle));\n      b.appendChild(tw);\n      b.insertAdjacentHTML(\"beforeend\", '<svg class=\"dmb-dupm-chev\" viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.4\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"9 6 15 12 9 18\"/></svg>');\n      return b;\n    };\n\n    const ov = el(\"div\", \"dmb-dupm-overlay\"); ov.id = \"dmbDupPageModal\";\n    const modal = el(\"div\", \"dmb-dupm\");\n    ov.appendChild(modal); document.body.appendChild(ov);\n    requestAnimationFrame(() => { ov.classList.add(\"is-open\"); modal.classList.add(\"is-open\"); });\n\n    const close = () => { ov.classList.remove(\"is-open\"); modal.classList.remove(\"is-open\"); document.removeEventListener(\"keydown\", onEsc); setTimeout(() => { try { ov.remove(); } catch (e) {} }, 200); };\n    const onEsc = (e) => { if (e.key === \"Escape\") close(); };\n    ov.addEventListener(\"click\", (e) => { if (e.target === ov) close(); });\n    document.addEventListener(\"keydown\", onEsc);\n\n    function screenDestination() {\n      modal.innerHTML = \"\";\n      modal.appendChild(el(\"div\", \"dmb-dupm-title\", \"Duplicate page\"));\n      modal.appendChild(el(\"div\", \"dmb-dupm-sub\", \"Copy page \" + srcPage + \" to another page.\"));\n      const opts = el(\"div\", \"dmb-dupm-opts\"); modal.appendChild(opts);\n\n      if (otherValid) {\n        const b = optBtn(\"Other page on this spread\", \"Page \" + other, true);\n        b.addEventListener(\"click\", () => screenOptions(other));\n        opts.appendChild(b);\n      }\n\n      const b2 = optBtn(\"Choose another page\u2026\", \"Type a page number\", false);\n      const expand = el(\"div\", \"dmb-dupm-expand\");\n      const row = el(\"div\", \"dmb-dupm-row\");\n      const input = el(\"input\", \"dmb-dupm-input\"); input.type = \"number\"; input.min = \"2\"; input.max = String(total); input.placeholder = \"e.g. \" + (otherValid ? Math.min(total, Math.max(2, srcPage + 2)) : 2);\n      const go = el(\"button\", \"dmb-dupm-go\", \"Copy\"); go.type = \"button\";\n      row.appendChild(input); row.appendChild(go);\n      const hint = el(\"div\", \"dmb-dupm-hint\", \"Any page from 2 to \" + total);\n      expand.appendChild(row); expand.appendChild(hint);\n\n      let open = false;\n      b2.addEventListener(\"click\", () => { open = !open; expand.classList.toggle(\"is-open\", open); b2.classList.toggle(\"is-armed\", open); if (open) setTimeout(() => input.focus(), 80); });\n      const err = (m) => { hint.className = \"dmb-dupm-hint dmb-dupm-err\"; hint.textContent = m; };\n      const submit = () => {\n        const v = parseInt(input.value, 10);\n        if (!Number.isFinite(v) || v < 2 || v > total || isLockedPage(v)) return err(\"Enter a page between 2 and \" + total + \".\");\n        if (v === srcPage) return err(\"That's the same page you're on.\");\n        screenOptions(v);\n      };\n      go.addEventListener(\"click\", submit);\n      input.addEventListener(\"keydown\", (e) => { if (e.key === \"Enter\") { e.preventDefault(); submit(); } });\n      input.addEventListener(\"input\", () => { hint.className = \"dmb-dupm-hint\"; hint.textContent = \"Any page from 2 to \" + total; });\n      opts.appendChild(b2); opts.appendChild(expand);\n\n      const cancel = el(\"button\", \"dmb-dupm-cancel\", \"Cancel\"); cancel.type = \"button\"; cancel.addEventListener(\"click\", close); modal.appendChild(cancel);\n    }\n\n    function screenOptions(dst) {\n      modal.innerHTML = \"\";\n      modal.appendChild(el(\"div\", \"dmb-dupm-title\", \"Duplicate page\"));\n      modal.appendChild(el(\"div\", \"dmb-dupm-sub\", \"Copy page \" + srcPage + \" \u2192 page \" + dst + \".\"));\n      if (pageHasAnyContent(dst)) modal.appendChild(el(\"div\", \"dmb-dupm-warn\", \"Page \" + dst + \" already has content \u2014 it will be replaced.\"));\n      const opts = el(\"div\", \"dmb-dupm-opts\"); modal.appendChild(opts);\n      const bp = optBtn(\"With photos\", \"An exact copy of this page\", true);\n      bp.addEventListener(\"click\", () => doCopy(dst, true));\n      const bl = optBtn(\"Layout only\", \"Same design, frames left empty\", false);\n      bl.addEventListener(\"click\", () => doCopy(dst, false));\n      opts.appendChild(bp); opts.appendChild(bl);\n      const back = el(\"button\", \"dmb-dupm-cancel\", \"\u2190 Back\"); back.type = \"button\"; back.addEventListener(\"click\", screenDestination); modal.appendChild(back);\n    }\n\n    function doCopy(dst, withPhotos) {\n      pushUndoState(\"Duplicate Page\");\n      _dmbDuplicatePageTo(srcPage, dst, withPhotos);\n      editor.currentSpread = Math.floor((dst - 1) / 2);\n      renderSpread(); renderThumbsRail(); renderPhotoTray();\n      close();\n    }\n\n    screenDestination();\n  }\n\n  function pageHasAnyPhotos(pageNumber) {\n    if (isTemplatePage(pageNumber)) {\n      const m = editor.pageFrames?.[pageNumber] || {};\n      return Object.values(m).some(Boolean);\n    }\n    const d = editor.pageSlots?.[pageNumber];\n    return !!(d && (d.A || d.B));\n  }\n\n  // Does this page's spread PARTNER hold an object that reaches across the fold onto\n  // THIS page? The PDF already draws that overhang (getSpreadOverflowObjs in the render\n  // path), so a page it covers is emphatically not blank. Kept in ONE helper because\n  // the two callers below answer opposite questions and drifted apart once already.\n  //\n  // Rotation-aware on purpose: a rotated object's footprint is its rotated bounding\n  // box, not its x..x+w box, so the plain test both under-reports here and - in the\n  // render path - silently dropped one half of a rotated straddler. (2026-09-14)\n  // Does object `o`, filed on `pageNumber`, reach across the FOLD onto its spread\n  // partner? Only the fold side counts: an object bleeding off the OUTER edge of a\n  // page is a deliberate full-bleed look and lands on no other sheet.\n  //\n  // Rotation-aware: drawFreeObjs rotates about the object centre, so the footprint is\n  // the rotated bounding box, not x..x+w. Half-width of that box for a w x h rectangle\n  // at angle t is (|w*cos t| + |h*sin t|) / 2 - verified against brute-forced corners.\n  function _dmbObjOverhangs(o, pageNumber) {\n    try {\n      if (!o) return false;\n      const pn = Number(pageNumber);\n      if (!pn || pn < 1) return false;                  // page 0 has no spread partner\n      const isLeftPg = (pn % 2 === 1);                  // spreads are (2k+1, 2k+2)\n      const sib = isLeftPg ? pn + 1 : pn - 1;\n      if (sib < 1 || sib > (editor.totalPages || 0)) return false;\n      const PW = getPageDims().w;\n      const ow = o.w || 0;\n      const rot = (o.rotation || 0) * Math.PI / 180;\n      const cx = (o.x || 0) + ow / 2;\n      const half = rot\n        ? (Math.abs(ow * Math.cos(rot)) + Math.abs((o.h || 0) * Math.sin(rot))) / 2\n        : ow / 2;\n      return isLeftPg ? (cx + half > PW) : (cx - half < 0);\n    } catch (e) { return false; }\n  }\n\n  // Does this page's spread PARTNER hold an object that reaches onto THIS page? The\n  // PDF already draws that overhang (getSpreadOverflowObjs in the render path), so a\n  // page it covers is emphatically not blank.\n  function _dmbPartnerOverhang(pageNumber) {\n    try {\n      const pn = Number(pageNumber);\n      if (!pn || pn < 1) return false;\n      const sib = (pn % 2 === 1) ? pn + 1 : pn - 1;\n      if (sib < 1 || sib > (editor.totalPages || 0)) return false;\n      const objs = editor.pageObjects?.[sib] || editor.pageObjects?.[String(sib)] || [];\n      if (!Array.isArray(objs) || !objs.length) return false;\n      return objs.some(function (o) { return _dmbObjOverhangs(o, sib); });\n    } catch (e) { return false; }\n  }\n\n  // Horizontal drag range for a page object. A shape or map may be placed anywhere\n  // across the SPREAD: its CENTRE decides which page owns it, and dmbHandleCrossPageMove\n  // hands ownership over the moment the centre crosses the fold. Every other type keeps\n  // the old single-page range, so a photo frame or text box still cannot be lost in the\n  // gutter by accident.\n  // Horizontal range a bare POINT may reach - a line's ENDPOINT, which has no width of\n  // its own so the box-based range above does not describe it. A line is a shape, and\n  // shapes may span a spread, so an endpoint may be carried across the fold onto the\n  // facing page. Falls back to the single page whenever there is no partner (page 0, the\n  // last page of an odd book, the cover).\n  function _dmbPointRangeX(pageNumber, pageW) {\n    try {\n      const pn = Number(pageNumber);\n      if (!pn || pn < 1) return { lo: 0, hi: pageW };\n      const isLeftPg = (pn % 2 === 1);\n      const sib = isLeftPg ? pn + 1 : pn - 1;\n      if (sib < 1 || sib > (editor.totalPages || 0)) return { lo: 0, hi: pageW };\n      return isLeftPg ? { lo: 0, hi: pageW * 2 } : { lo: -pageW, hi: pageW };\n    } catch (e) { return { lo: 0, hi: pageW }; }\n  }\n\n  function _dmbObjDragRangeX(obj, pageNumber, pageW) {\n    const halfW = ((obj && obj.w) || 0) / 2;\n    const lo0 = -halfW, hi0 = pageW - halfW;\n    try {\n      if (!obj || (obj.type !== \"shape\" && obj.type !== \"map\" && obj.type !== \"photo-frame\")) return { lo: lo0, hi: hi0 };\n      const pn = Number(pageNumber);\n      if (!pn || pn < 1) return { lo: lo0, hi: hi0 };\n      const isLeftPg = (pn % 2 === 1);\n      const sib = isLeftPg ? pn + 1 : pn - 1;\n      if (sib < 1 || sib > (editor.totalPages || 0)) return { lo: lo0, hi: hi0 };\n      return isLeftPg ? { lo: lo0, hi: hi0 + pageW } : { lo: lo0 - pageW, hi: hi0 };\n    } catch (e) { return { lo: lo0, hi: hi0 }; }\n  }\n\n  function pageHasAnyContent(pageNumber) {\n    if (isLockedPage(pageNumber)) return false;\n    const si = Math.floor((pageNumber - 1) / 2);\n    if (editor.spreadPhoto?.[si]) return true;\n    // ANY free object placed on the page counts as content \u2014 a text box, sticker,\n    // shape, map, line, or a photo-frame (even an empty one) \u2014 not just photos.\n    const freeObjs = editor.pageObjects?.[pageNumber] || editor.pageObjects?.[String(pageNumber)] || [];\n    if (Array.isArray(freeObjs) && freeObjs.length > 0) return true;\n    // Half of the neighbour's object is still ink on this sheet.\n    if (_dmbPartnerOverhang(pageNumber)) return true;\n    // A custom (non-white) page background is content too.\n    const bg = editor.pageBackgrounds?.[pageNumber];\n    if (bg && String(bg).toLowerCase() !== \"#ffffff\") return true;\n    return pageHasAnyPhotos(pageNumber);\n  }\n\n  // STRICTER than pageHasAnyContent: a page is \"completely empty\" only when the\n  // customer put NOTHING on it \u2014 no free object at all (text, sticker, shape,\n  // map, or any photo-frame, even an empty one), no spread photo covering it, no\n  // template slot photo, and no custom (non-white) background. Used for the\n  // empty-sheet drop offer at add-to-cart.\n  function pageIsCompletelyEmpty(pageNumber) {\n    const objs = editor.pageObjects?.[pageNumber] || editor.pageObjects?.[String(pageNumber)] || [];\n    if (Array.isArray(objs) && objs.length > 0) return false;\n    // This one decides whether the sheet is OFFERED FOR DELETION at add-to-cart, so a\n    // false \"empty\" here does not merely mislabel a page - it drops printed content.\n    if (_dmbPartnerOverhang(pageNumber)) return false;\n    const si = Math.floor((pageNumber - 1) / 2);\n    if (editor.spreadPhoto?.[si]?.photoId) return false;\n    if (pageHasAnyPhotos(pageNumber)) return false;\n    const bg = editor.pageBackgrounds?.[pageNumber];\n    if (bg && String(bg).toLowerCase() !== \"#ffffff\") return false;\n    return true;\n  }\n\n  // Physical sheets are facing pairs: page 1 alone, then (2,3),(4,5),\u2026 and the\n  // last page alone. Only the paired MIDDLE sheets can be dropped, and only when\n  // BOTH their pages are completely empty. Returns the empty ones, e.g. [[2,3],[6,7]].\n  function _dmbGetEmptySheets() {\n    const N = editor.totalPages || 0;\n    const out = [];\n    for (let p = 2; p + 1 <= N - 1; p += 2) {     // pairs (2,3),(4,5),\u2026,(N-2,N-1)\n      if (pageIsCompletelyEmpty(p) && pageIsCompletelyEmpty(p + 1)) out.push([p, p + 1]);\n    }\n    return out;\n  }\n\n  /* The builder's one dialog, in the same ivory \"Keepsake\" dress as the Add to\n   * Cart screens: warm scrim, ivory card, the site's roman heading face, and a\n   * pill action. Replaces the browser's own confirm()/alert() boxes, which show\n   * the raw domain name and cannot be styled at all.\n   *\n   * showChoiceDialog(title, message, options[, extra]) -> Promise<id|null>\n   *   options: [{ id, label, danger? }]   danger = destructive (warm red)\n   *   extra:   { imageUrl }               small preview above the title\n   * Two options lay out side by side; three or more stack. Backdrop click and\n   * Escape both resolve null, so there is always a way out. (2026-09-13) */\n  function showChoiceDialog(title, message, options, extra) {\n    return new Promise((resolve) => {\n      const X = extra || {};\n      const SERIF = \"var(--font-heading--family,'Playfair Display'),Georgia,'Times New Roman',serif\";\n      const SANS  = \"var(--font-body--family,'Inter'),system-ui,-apple-system,sans-serif\";\n\n      const overlay = document.createElement(\"div\");\n      overlay.style.cssText = \"position:fixed;inset:0;z-index:1000030;background:rgba(44,40,34,0.55);\" +\n        \"display:flex;align-items:center;justify-content:center;padding:20px;font-family:\" + SANS + \";\" +\n        \"opacity:0;transition:opacity .14s ease;\";\n\n      const modal = document.createElement(\"div\");\n      modal.style.cssText = \"background:#F7F3EC;border:1px solid #E7E1D4;border-radius:16px;\" +\n        \"padding:26px 24px 20px;max-width:340px;width:100%;text-align:center;box-sizing:border-box;\" +\n        \"box-shadow:0 24px 60px rgba(44,40,34,0.32);transform:translateY(6px);transition:transform .14s ease;\";\n\n      if (X.imageUrl) {\n        const im = document.createElement(\"img\");\n        im.src = X.imageUrl;\n        im.alt = \"\";\n        im.style.cssText = \"display:block;width:76px;height:76px;object-fit:cover;border-radius:10px;\" +\n          \"margin:0 auto 14px;box-shadow:0 3px 12px rgba(44,40,34,0.22);\";\n        modal.appendChild(im);\n      }\n\n      const ttl = document.createElement(\"div\");\n      ttl.style.cssText = \"font-family:\" + SERIF + \";font-weight:700;font-size:20px;line-height:1.2;color:#2C2822;\";\n      ttl.textContent = title;\n      modal.appendChild(ttl);\n\n      if (message) {\n        const msg = document.createElement(\"div\");\n        msg.style.cssText = \"font-size:13px;color:#6B6258;line-height:1.5;margin-top:8px;\";\n        msg.textContent = message;\n        modal.appendChild(msg);\n      }\n\n      const row = (options || []).length === 2;\n      const btnWrap = document.createElement(\"div\");\n      btnWrap.style.cssText = \"display:flex;gap:8px;margin-top:20px;\" +\n        (row ? \"flex-direction:row;justify-content:center;\" : \"flex-direction:column;\");\n      modal.appendChild(btnWrap);\n\n      overlay.appendChild(modal);\n      document.body.appendChild(overlay);\n      requestAnimationFrame(() => { overlay.style.opacity = \"1\"; modal.style.transform = \"translateY(0)\"; });\n\n      let done = false;\n      const cleanup = (id) => {\n        if (done) return; done = true;\n        document.removeEventListener(\"keydown\", onKey);\n        overlay.style.opacity = \"0\";\n        setTimeout(() => { try { overlay.remove(); } catch (e) {} }, 140);\n        resolve(id);\n      };\n      const onKey = (e) => { if (e.key === \"Escape\") { e.preventDefault(); cleanup(null); } };\n      document.addEventListener(\"keydown\", onKey);\n\n      (options || []).forEach((opt) => {\n        const btn = document.createElement(\"button\");\n        btn.type = \"button\";\n        btn.textContent = opt.label;\n        const isCancel = opt.id === \"cancel\";\n        const bg = opt.danger ? \"#A6402F\" : (isCancel ? \"transparent\" : \"#6B62D6\");\n        btn.style.cssText = \"font-family:inherit;font-weight:600;font-size:13.5px;cursor:pointer;\" +\n          \"border-radius:99px;padding:11px 20px;\" + (row ? \"flex:1 1 0;min-width:0;\" : \"\") +\n          \"background:\" + bg + \";\" +\n          \"color:\" + (isCancel ? \"#6B6258\" : \"#fff\") + \";\" +\n          \"border:1px solid \" + (isCancel ? \"#DDD5C4\" : bg) + \";\";\n        btn.addEventListener(\"click\", () => cleanup(opt.id));\n        btnWrap.appendChild(btn);\n      });\n\n      overlay.addEventListener(\"click\", (e) => { if (e.target === overlay) cleanup(null); });\n    });\n  }\n\n  async function handleSpreadZoneDrop(zone, payload, leftPage, rightPage, si) {\n    const photoId = payload?.photoId;\n    if (!photoId) return;\n    const used = getUsedPhotoIds();\n    if (payload.type === \"photo\" && used.has(photoId)) showReuseNotice();\n\n    if (zone === \"left\") {\n      const hasContent = pageHasAnyContent(leftPage);\n      if (hasContent) {\n        const choice = await showChoiceDialog(\"Page has content\", \"This page already has content. What would you like to do?\", [\n          { id: \"replace\", label: \"Replace existing content\" },\n          { id: \"add\", label: \"Add alongside\" },\n          { id: \"cancel\", label: \"Cancel\" }\n        ]);\n        if (!choice || choice === \"cancel\") return;\n        if (choice === \"replace\") {\n          pushUndoState();\n          clearPageAllLayouts(leftPage);\n          editor.pageSlots[leftPage].A = photoId;\n        } else {\n          pushUndoState();\n          if (!editor.pageSlots[leftPage].B) {\n            editor.pageSlots[leftPage].B = editor.pageSlots[leftPage].A ? photoId : null;\n            if (!editor.pageSlots[leftPage].A) editor.pageSlots[leftPage].A = photoId;\n            else editor.pageSlots[leftPage].B = photoId;\n          } else {\n            editor.pageSlots[leftPage].A = photoId;\n          }\n        }\n      } else {\n        pushUndoState();\n        if (!editor.pageSlots[leftPage]) editor.pageSlots[leftPage] = { A: null, B: null };\n        editor.pageSlots[leftPage].A = photoId;\n      }\n    } else if (zone === \"right\") {\n      if (!rightPage || rightPage > editor.totalPages) return;\n      const hasContent = pageHasAnyContent(rightPage);\n      if (hasContent) {\n        const choice = await showChoiceDialog(\"Page has content\", \"This page already has content. What would you like to do?\", [\n          { id: \"replace\", label: \"Replace existing content\" },\n          { id: \"add\", label: \"Add alongside\" },\n          { id: \"cancel\", label: \"Cancel\" }\n        ]);\n        if (!choice || choice === \"cancel\") return;\n        if (choice === \"replace\") {\n          pushUndoState();\n          clearPageAllLayouts(rightPage);\n          editor.pageSlots[rightPage].A = photoId;\n        } else {\n          pushUndoState();\n          if (!editor.pageSlots[rightPage]) editor.pageSlots[rightPage] = { A: null, B: null };\n          if (!editor.pageSlots[rightPage].A) editor.pageSlots[rightPage].A = photoId;\n          else if (!editor.pageSlots[rightPage].B) editor.pageSlots[rightPage].B = photoId;\n          else editor.pageSlots[rightPage].A = photoId;\n        }\n      } else {\n        pushUndoState();\n        if (!editor.pageSlots[rightPage]) editor.pageSlots[rightPage] = { A: null, B: null };\n        editor.pageSlots[rightPage].A = photoId;\n      }\n    } else if (zone === \"spread\") {\n      const leftHas = pageHasAnyContent(leftPage);\n      const rightHas = rightPage <= editor.totalPages && pageHasAnyContent(rightPage);\n      if (leftHas || rightHas) {\n        let clearMsg = \"Placing a full-spread photo will clear content on \";\n        if (leftHas && rightHas) clearMsg += \"both pages.\";\n        else if (leftHas) clearMsg += \"the left page.\";\n        else clearMsg += \"the right page.\";\n        const choice = await showChoiceDialog(\"Full spread photo\", clearMsg, [\n          { id: \"continue\", label: \"Continue\" },\n          { id: \"cancel\", label: \"Cancel\" }\n        ]);\n        if (!choice || choice === \"cancel\") return;\n      }\n      pushUndoState();\n      clearPageAllLayouts(leftPage);\n      if (rightPage <= editor.totalPages) clearPageAllLayouts(rightPage);\n      if (!editor.spreadPhoto) editor.spreadPhoto = {};\n      editor.spreadPhoto[si] = { photoId, panX: 0, panY: 0, zoom: 1 };\n    }\n    renderSpread();\n    renderThumbsRail();\n    renderPhotoTray();\n  }\n\n  async function setPageTemplateWithConfirm(pageNumber, templateId) {\n    if (isLockedPage(pageNumber)) return false;\n\n    const pageHasPhotos = pageHasAnyPhotos(pageNumber);\n    if (pageHasPhotos) {\n      const choice = await showChoiceDialog(\n        \"This page has photos\",\n        \"How would you like to apply the layout?\",\n        [\n          { id: \"over\", label: \"Apply layout over existing photos\" },\n          { id: \"replace\", label: \"Remove photos and apply layout\" },\n          { id: \"cancel\", label: \"Cancel\" }\n        ]\n      );\n      if (!choice || choice === \"cancel\") return false;\n      pushUndoState();\n      if (choice === \"replace\") clearPageAllLayouts(pageNumber);\n    } else {\n      pushUndoState();\n      clearPageAllLayouts(pageNumber);\n    }\n\n    // A chosen layout now drops individual FREE photo-frames (one per slot) so\n    // each is clickable / movable / resizable / deletable like a lonely frame and\n    // the card layouts \u2014 instead of a locked template. Default (null) \u2192 no frames.\n    if (templateId && TEMPLATE_DEFS[templateId]) _dmbSpawnLayoutFrames(pageNumber, templateId);\n    else ensureTemplateState(pageNumber, null);\n    renderSpread();\n    renderThumbsRail();\n    renderPhotoTray();\n    return true;\n  }\n\n  // Spawn a layout as individual FREE photo-frame objects (one per TEMPLATE_DEFS\n  // slot, % \u2192 page px). Reuses the whole lonely-frame system (render, drag,\n  // resize, delete via keyboard, photo-drop, PDF export). Replaces a previous\n  // layout's frames but keeps manually-added frames, text and stickers.\n  function _dmbSpawnLayoutFrames(pageNumber, templateId) {\n    const slots = TEMPLATE_DEFS[templateId] || [];\n    const dims = getPageDims();\n    ensureTemplateState(pageNumber, null);                 // skip the locked-template render path\n    const arr = ensurePageObjects(pageNumber);\n    for (let i = arr.length - 1; i >= 0; i--) if (arr[i].type === \"photo-frame\" && arr[i].fromLayout) arr.splice(i, 1);\n    const now = Date.now();\n    slots.forEach((s, i) => {\n      const obj = newFrameObject({ targetType: \"page\", page: pageNumber });\n      obj.fromLayout = true;\n      obj.x = Math.round(s.x / 100 * dims.w);\n      obj.y = Math.round(s.y / 100 * dims.h);\n      obj.w = Math.round(s.w / 100 * dims.w);\n      obj.h = Math.round(s.h / 100 * dims.h);\n      obj.z = now + i;\n      arr.push(obj);\n    });\n  }\n\n  function rotateCCW90(editorObj, pageNumber, slot) {\n    if (!editorObj.pageRot[pageNumber]) {\n      editorObj.pageRot[pageNumber] = { A: 0, B: 0 };\n    }\n    const cur = editorObj.pageRot[pageNumber][slot] || 0;\n    editorObj.pageRot[pageNumber][slot] = (cur - 90 + 360) % 360;\n  }\n\n  function rotateFrameCCW90(editorObj, pageNumber, slotKey) {\n    if (isTemplatePage(pageNumber)) {\n      if (!editorObj.pageFrameRot[pageNumber]) editorObj.pageFrameRot[pageNumber] = {};\n      const cur = editorObj.pageFrameRot[pageNumber][slotKey] || 0;\n      editorObj.pageFrameRot[pageNumber][slotKey] = (cur - 90 + 360) % 360;\n    } else {\n      rotateCCW90(editorObj, pageNumber, slotKey);\n    }\n  }\n\n  function getFramePhotoId(pageNumber, slotKey) {\n    if (isTemplatePage(pageNumber)) return editor.pageFrames?.[pageNumber]?.[slotKey] || null;\n    return editor.pageSlots?.[pageNumber]?.[slotKey] || null;\n  }\n\n  function setFramePhotoId(pageNumber, slotKey, photoId) {\n    if (isTemplatePage(pageNumber)) editor.pageFrames[pageNumber][slotKey] = photoId || null;\n    else editor.pageSlots[pageNumber][slotKey] = photoId || null;\n  }\n\n  function getFrameRotation(pageNumber, slotKey) {\n    if (isTemplatePage(pageNumber)) return editor.pageFrameRot?.[pageNumber]?.[slotKey] || 0;\n    return editor.pageRot?.[pageNumber]?.[slotKey] || 0;\n  }\n\n  function setFrameRotation(pageNumber, slotKey, deg) {\n    if (isTemplatePage(pageNumber)) editor.pageFrameRot[pageNumber][slotKey] = deg || 0;\n    else editor.pageRot[pageNumber][slotKey] = deg || 0;\n  }\n\n  function clearFrame(pageNumber, slotKey) {\n    setFramePhotoId(pageNumber, slotKey, null);\n    setFrameRotation(pageNumber, slotKey, 0);\n  }\n\n  function getBodyRelativeFrameRects(bodyEl, pageNumber) {\n    const tid = getPageTemplateId(pageNumber);\n    if (!tid) return null;\n\n    const def = TEMPLATE_DEFS[tid];\n    const w = bodyEl.clientWidth || 1;\n    const h = bodyEl.clientHeight || 1;\n\n    return def.map((f) => ({\n      key: f.key,\n      x: (f.x / 100) * w,\n      y: (f.y / 100) * h,\n      w: (f.w / 100) * w,\n      h: (f.h / 100) * h\n    }));\n  }\n\n  function pickNearestTemplateFrame(bodyEl, pageNumber, clientX, clientY) {\n    const rects = getBodyRelativeFrameRects(bodyEl, pageNumber);\n    if (!rects || !rects.length) return null;\n\n    const b = bodyEl.getBoundingClientRect();\n    const x = clientX - b.left;\n    const y = clientY - b.top;\n\n    for (const r of rects) {\n      if (x >= r.x && x <= r.x + r.w && y >= r.y && y <= r.y + r.h) return r;\n    }\n\n    let best = null;\n    let bestD = Infinity;\n    rects.forEach((r) => {\n      const cx = r.x + r.w / 2;\n      const cy = r.y + r.h / 2;\n      const d = (cx - x) ** 2 + (cy - y) ** 2;\n      if (d < bestD) {\n        bestD = d;\n        best = r;\n      }\n    });\n    return best;\n  }\n\n  function renderTemplateMini(el, templateId) {\n    el.innerHTML = \"\";\n    const defs = TEMPLATE_DEFS[templateId] || [];\n    defs.forEach((f) => {\n      const d = document.createElement(\"div\");\n      d.className = \"dmb-template-mini-frame\";\n      d.style.left = f.x + \"%\";\n      d.style.top = f.y + \"%\";\n      d.style.width = f.w + \"%\";\n      d.style.height = f.h + \"%\";\n      el.appendChild(d);\n    });\n  }\n\n  function buildTemplatePanel(pageNumber, pageEl) {\n    const panel = document.createElement(\"div\");\n    panel.className = \"dmb-template-panel\";\n    panel.style.display = \"none\";\n\n    const title = document.createElement(\"div\");\n    title.className = \"dmb-template-panel-title\";\n    title.textContent = \"Choose template\";\n    panel.appendChild(title);\n\n    const list = document.createElement(\"div\");\n    list.className = \"dmb-template-list\";\n    panel.appendChild(list);\n\n    const addItem = (id, name, subtitle, isDefault = false) => {\n      const btn = document.createElement(\"button\");\n      btn.type = \"button\";\n      btn.className = \"dmb-template-item\";\n\n      const currentId = getPageTemplateId(pageNumber);\n      if ((isDefault && !currentId) || (!isDefault && currentId === id)) {\n        btn.classList.add(\"active\");\n      }\n\n      const row = document.createElement(\"div\");\n      row.className = \"dmb-template-item-row\";\n\n      const mini = document.createElement(\"div\");\n      mini.className = \"dmb-template-mini\";\n\n      if (isDefault) {\n        const a = document.createElement(\"div\");\n        a.className = \"dmb-template-mini-frame\";\n        a.style.left = \"0\";\n        a.style.top = \"0\";\n        a.style.width = \"100%\";\n        a.style.height = \"49%\";\n\n        const b = document.createElement(\"div\");\n        b.className = \"dmb-template-mini-frame\";\n        b.style.left = \"0\";\n        b.style.top = \"51%\";\n        b.style.width = \"100%\";\n        b.style.height = \"49%\";\n\n        mini.appendChild(a);\n        mini.appendChild(b);\n      } else {\n        renderTemplateMini(mini, id);\n      }\n\n      const txt = document.createElement(\"div\");\n      txt.style.minWidth = \"0\";\n\n      const nm = document.createElement(\"div\");\n      nm.className = \"dmb-template-item-name\";\n      nm.textContent = name;\n\n      const sub = document.createElement(\"div\");\n      sub.className = \"dmb-template-item-sub\";\n      sub.textContent = subtitle;\n\n      txt.appendChild(nm);\n      txt.appendChild(sub);\n\n      row.appendChild(mini);\n      row.appendChild(txt);\n      btn.appendChild(row);\n\n      btn.addEventListener(\"click\", async (e) => {\n        e.preventDefault();\n        e.stopPropagation();\n\n        if (isDefault) await setPageTemplateWithConfirm(pageNumber, null);\n        else await setPageTemplateWithConfirm(pageNumber, id);\n      });\n\n      list.appendChild(btn);\n    };\n\n    addItem(null, \"Default Layout\", \"1 photo full / 2-photo split\", true);\n\n    Object.keys(TEMPLATE_DEFS).forEach((id) => {\n      addItem(id, TEMPLATE_NAMES[id] || id.toUpperCase(), `${TEMPLATE_DEFS[id].length} frame(s)`);\n    });\n\n    pageEl.appendChild(panel);\n    return panel;\n  }\n\n  // ---------- elements ----------\n  const params = new URLSearchParams(window.location.search);\n  const stepParam    = params.get(\"step\");\n  const mode         = params.get(\"mode\");\n  const restoreParam = params.get(\"restore\");\n  const coverTitleParam = params.get(\"cover_title\");\n  const coverImgParam = params.get(\"cover_img\");\n  const coverHandleParam = params.get(\"cover\");\n  const templateParam = params.get(\"template\");\n\n  const step1 = $(\"step1\");\n  const step2 = $(\"step2\");\n  const step3 = $(\"step3\");\n  const builderStep = step2;\n  const exportTemplateBtn = $(\"exportTemplateBtn\");\n\n  const selectedCoverBox = $(\"selectedCoverBox\");\n  const selectedCoverImg = $(\"selectedCoverImg\");\n  const selectedCoverTitle = $(\"selectedCoverTitle\");\n  const bookDetails = $(\"bookDetails\");\n  const customizeBlock = $(\"customizeBlock\");\n\n  const titleEl = $(\"bookTitle\"); // legacy fallback\n  const pagesEl = $(\"pages\");\n  const mainError = $(\"mainError\");\n\n  const priceBox = $(\"priceBox\");\n  const oldPriceEl = $(\"oldPrice\");\n  const newPriceEl = $(\"newPrice\");\n\n  const photosEl = $(\"bookPhotos\");\n  const photoRuleEl = $(\"photoRule\");\n  const photoCountEl = $(\"photoCount\");\n  const photoErrorEl = $(\"photoError\");\n  const photoPreviewEl = $(\"photoPreview\");\n\n  // cover designer\n  const chooseCoverTemplateBtn = $(\"chooseCoverTemplateBtn\");\n  const coverTemplatePanel = $(\"coverTemplatePanel\");\n  const coverTitle1Input = $(\"coverTitle1\");\n  const coverTitle2Input = $(\"coverTitle2\");\n  const coverColorInput = $(\"coverColor\");\n  const coverColorSwatch = $(\"coverColorSwatch\");\n  const coverImageInput = $(\"coverImageInput\");\n\n  // editor ui\n  const editorPhotoTray = $(\"editorPhotoTray\");\n  const bookMeta = $(\"bookMeta\");\n  const spreadIndexEl = $(\"spreadIndex\");\n  const spreadCanvas = $(\"spreadCanvas\");\n  const prevSpreadBtn = $(\"prevSpreadBtn\");\n  const nextSpreadBtn = $(\"nextSpreadBtn\");\n  const backToDetailsBtn = $(\"backToDetailsBtn\");\n  const saveLayoutBtn = $(\"saveLayoutBtn\");\n  const saveTemplateBtn = $(\"saveTemplateBtn\");\n  const saveMsg = $(\"saveMsg\");\n  const undoBtn = $(\"undoBtn\");\n  const addToCartBtn = $(\"addToCartBtn\");\n  const thumbsRail = $(\"thumbsRail\");\n  const spreadViewport = $(\"spreadViewport\");\n  const redoBtn = $(\"redoBtn\");\n  const previewBtn = $(\"previewBtn\");\n  const zoomInBtn = $(\"zoomInBtn\");\n  const zoomOutBtn = $(\"zoomOutBtn\");\n  const zoomResetBtn = $(\"zoomResetBtn\");\n  const smartCreationBtn = $(\"smartCreationBtn\");\n  const coverThumbBtn = $(\"coverThumbBtn\");\n  const canvasTextBtn = $(\"canvasTextBtn\");\n  const canvasPhotoBtn = $(\"canvasPhotoBtn\");\n  const canvasLayoutBtn = $(\"canvasLayoutBtn\");\n  const canvasShapeBtn = $(\"canvasShapeBtn\");\n  const canvasLayersBtn = $(\"canvasLayersBtn\");\n  const canvasDeleteBtn = $(\"canvasDeleteBtn\");\n  const addTextBtn = $(\"addTextBtn\");\n  const projectMenuBtn = $(\"projectMenuBtn\");\n  const historyBtn = $(\"historyBtn\");\n  const addMorePhotosBtn = $(\"addMorePhotosBtn\");\n  const importDeviceBtn = $(\"importDeviceBtn\");\n  const importCameraBtn = $(\"importCameraBtn\");\n  const cameraPhotos = $(\"cameraPhotos\");\n  const photoSortSelect = $(\"photoSortSelect\");\n  const hideUsedPhotosToggle = $(\"hideUsedPhotosToggle\");\n  const coverThumbBtnEl = $(\"coverThumbBtn\");\n\n  if (!step1 || !builderStep) return;\n\n  // ---------- state ----------\n  let bookFiles = [];\n  let currentMin = 0;\n  let currentMax = 0;\n  let hideUsedPhotos = false;\n  let currentPhotoSort = \"earliest\";\n  let currentPhotoSearch = \"\";\n  // Default canvas zoom: 50% on phones (so the very first render is already 50% \u2014\n  // no lag, no 100%\u219250% flash), 100% on iPad/desktop.\n  function _dmbDefaultZoom() {\n    return (((\"ontouchstart\" in window) || (navigator.maxTouchPoints > 0)) &&\n      Math.min(window.screen.width || 9999, window.screen.height || 9999) <= 500) ? 0.5 : 1;\n  }\n  let currentZoom = _dmbDefaultZoom();\n  let redoStack = [];\n  let builderMode = \"pages\";\n  let selectedObject = null;\n  let activeCoverZone = \"front\";\n\n  // ---------- undo ----------\n  const undoStack = [];\n  const UNDO_LIMIT = 50;\n\n  function cloneState(obj) {\n    return JSON.parse(JSON.stringify(obj));\n  }\n\n  // Build the template payload with test photos stripped. Shared by \"Save to\n  // store\" and the download fallback so the two can never drift apart.\n  function _dmbBuildStrippedTemplatePayload() {\n    const payload = buildTemplatePayload();\n    // Strip test photos \u2014 keep everything else (text, stickers, icons, maps)\n    payload.coverState.frontImageUrl = \"\";\n    payload.coverState.backImageUrl = \"\";\n    payload.coverState.imageUrl = \"\";\n    payload.coverState.imageFile = null;\n    // \u2500\u2500 Strip the photo IDS too, not only the URLs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // Photo ids are per-customer. Published templates carried the TEMPLATE\n    // AUTHOR's ids, which exist in nobody else's library, so every customer who\n    // applied one got a cover declaring a front photo that could not resolve:\n    // the PDF drew nothing (silently), and render mode failed the whole job on\n    // the phantom id - dropping the order to the unguarded browser path with NO\n    // proof screen. A template ships a design, never someone else's photos.\n    payload.coverState.frontImageId = \"\";\n    payload.coverState.backImageId  = \"\";\n    payload.coverState.bleedImageId = \"\";\n    // A blob: URL is this session's object URL and is dead everywhere else. Real\n    // https template URLs are kept - those work for every customer.\n    [\"bleedImageUrl\", \"spineImageUrl\"].forEach(function (k) {\n      if (String(payload.coverState[k] || \"\").startsWith(\"blob:\")) payload.coverState[k] = \"\";\n    });\n    if (!payload.coverState.bleedImageUrl) {\n      payload.coverState.bleedZone = \"\"; payload.coverState.bleedImageRot = 0;\n    }\n    payload.coverObjects = (payload.coverObjects || []).map(obj =>\n      obj.type === \"photo-frame\" ? { ...obj, imageUrl: null, imageData: null } : obj\n    );\n    if (payload.page0?.pageSlots) payload.page0.pageSlots = { A: null, B: null };\n    return payload;\n  }\n\n  // Upload each cover/page object's inline base64 crop image to R2 as a separate\n  // template file and swap the base64 for a GET url, IN PLACE on the payload. Keeps\n  // the template JSON tiny so it loads fast (was ~20 MB inlined \u2192 failed to load \u2192\n  // blank cover). Rendering is unchanged: the url serves the exact same cropped PNG.\n  async function _dmbExternalizeTemplateImages(id, password, payload, proxy) {\n    const jobs = [];\n    const process = (arr) => {\n      if (!Array.isArray(arr)) return;\n      arr.forEach((o) => {\n        if (!o || !o.id) return;\n        let du = null, field = null;\n        if (typeof o.tightUrl === \"string\" && o.tightUrl.slice(0, 5) === \"data:\") { du = o.tightUrl; field = \"tightUrl\"; }\n        else if (typeof o.url === \"string\" && o.url.slice(0, 5) === \"data:\") { du = o.url; field = \"url\"; }\n        if (!du) return;\n        jobs.push((async () => {\n          try {\n            const blob = await (await fetch(du)).blob();\n            const r = await fetch(proxy + \"/template-image?id=\" + encodeURIComponent(id) + \"&oid=\" + encodeURIComponent(o.id), {\n              method: \"POST\", credentials: \"same-origin\",\n              headers: { \"content-type\": blob.type || \"image/png\", \"x-template-password\": password },\n              body: blob,\n            });\n            if (r && r.ok) {\n              o[field] = proxy + \"/template-image?id=\" + encodeURIComponent(id) + \"&oid=\" + encodeURIComponent(o.id);\n            }\n          } catch (e) {}\n        })());\n      });\n    };\n    process(payload.coverObjects);\n    if (payload.page0 && payload.page0.pageObjects) {\n      Object.keys(payload.page0.pageObjects).forEach((k) => process(payload.page0.pageObjects[k]));\n    }\n    await Promise.all(jobs);\n  }\n\n  // The template id = the active cover/magazine handle, so save & load stay\n  // symmetric with _dmbFetchTemplateData (which fetches by this same handle).\n  function _dmbActiveTemplateId() {\n    if (window.DMB_MAGAZINE_MODE) {\n      const mh = sessionStorage.getItem(\"dmb_mag_template\") || coverHandleParam || \"\";\n      return (mh || \"\").trim();\n    }\n    return (coverHandleParam || \"\").trim();\n  }\n\n  function exportTemplateJSON() {\n    const payload = _dmbBuildStrippedTemplatePayload();\n    const json = JSON.stringify(payload, null, 2);\n    const blob = new Blob([json], { type: \"application/json\" });\n    const url = URL.createObjectURL(blob);\n    const a = document.createElement(\"a\");\n    a.href = url;\n    // Use the book handle so each book type has its own template file.\n    // e.g. ?cover=photobook-20x20 \u2192 cover-photobook-20x20.json\n    // Falls back to cover-default.json when no handle is in the URL.\n    const _handle = _dmbActiveTemplateId();\n    a.download = _handle ? (\"cover-\" + _handle + \".json\") : \"cover-default.json\";\n    document.body.appendChild(a);\n    a.click();\n    document.body.removeChild(a);\n    URL.revokeObjectURL(url);\n  }\n\n  // \"Save to store\": publish the current design straight to R2 as the template\n  // for this book \u2014 no download \u2192 hand-off \u2192 push. The password is verified in\n  // the Worker (the prompt just collects it). Falls back to a download if the\n  // save call fails, so the owner is never stuck.\n  function saveTemplateToStore() {\n    // Uses an in-page modal instead of native window.prompt/confirm/alert. Browsers\n    // (Chrome/Safari) SILENTLY suppress those dialogs once a page has shown a few of\n    // them (\"Don't allow this page to prompt you again\") \u2014 after which prompt()\n    // returns null with no UI, so the Publish button appeared to do NOTHING. The\n    // modal can't be suppressed, and it also lets the admin type the template id when\n    // the URL has no ?cover= handle (previously a dead-end alert).\n    _dmbShowPublishTemplateModal();\n  }\n\n  function _dmbShowPublishTemplateModal() {\n    if (document.getElementById(\"dmbPublishTplModal\")) return;\n    const landscape = (typeof editor !== \"undefined\" && editor && editor.orientation === \"landscape\");\n    let presetId = \"\";\n    try { presetId = _dmbActiveTemplateId() || \"\"; } catch (e) {}\n    let title = presetId;\n    try { const l = (window._dmbCoverTplImages || {})[presetId]; if (l && l.title) title = l.title; } catch (e) {}\n\n    const ov = document.createElement(\"div\");\n    ov.id = \"dmbPublishTplModal\";\n    ov.style.cssText = \"position:fixed;inset:0;z-index:1000002;display:flex;align-items:center;justify-content:center;background:rgba(17,17,17,.55);padding:18px;\";\n    ov.innerHTML =\n      '<div style=\"background:#fff;border-radius:16px;max-width:340px;width:100%;padding:22px 20px;box-shadow:0 24px 70px rgba(0,0,0,.35);font-family:inherit;box-sizing:border-box;\">' +\n        '<label style=\"display:block;font-size:13px;font-weight:700;color:#444;margin:0 0 6px;\">Admin PassCode</label>' +\n        '<input id=\"dmbPubTplPw\" type=\"password\" autocomplete=\"off\" style=\"width:100%;box-sizing:border-box;padding:11px 12px;border:1px solid #ccc;border-radius:9px;font-size:14px;margin:0 0 8px;\">' +\n        '<div id=\"dmbPubTplMsg\" style=\"min-height:16px;font-size:13px;font-weight:600;line-height:1.4;margin:0 0 12px;\"></div>' +\n        '<button id=\"dmbPubTplGo\" type=\"button\" style=\"width:100%;background:#1e1e1e;color:#fff;border:none;border-radius:10px;padding:12px;font-size:14px;font-weight:800;cursor:pointer;\">Publish</button>' +\n      '</div>';\n    document.body.appendChild(ov);\n\n    const close = () => { try { ov.remove(); } catch (e) {} };\n    const msg = (t, ok) => { const m = document.getElementById(\"dmbPubTplMsg\"); if (m) { m.textContent = t; m.style.color = ok ? \"#1a7f37\" : \"#c0392b\"; } };\n    ov.addEventListener(\"click\", (e) => { if (e.target === ov) close(); });   // click outside to close\n\n    async function _doPublish() {\n      const pwEl = document.getElementById(\"dmbPubTplPw\");\n      const goBtn = document.getElementById(\"dmbPubTplGo\");\n      const id = presetId;                       // template id comes from the ?cover= handle\n      const password = (pwEl.value || \"\");\n      if (!id) { msg(\"Open a book with a cover in the URL first.\"); return; }\n      if (!password) { msg(\"Enter the admin passcode.\"); pwEl.focus(); return; }\n      let ttl = title || id;\n      try { const l = (window._dmbCoverTplImages || {})[id]; if (l && l.title) ttl = l.title; } catch (e) {}\n      goBtn.disabled = true; goBtn.textContent = \"Publishing\u2026\"; msg(\"Saving\u2026\", true);\n      try {\n        const payload = _dmbBuildStrippedTemplatePayload();\n        const proxy = window.DMB_DESIGNS_PROXY || \"/apps/designs\";\n        // Externalize the heavy base64 crop images (sticker/icon `tightUrl`) to R2\n        // as separate files and replace them with GET urls, so the template JSON is\n        // tiny. Inlining them made templates ~20 MB \u2192 they failed to load and the\n        // cover came up blank. Rendering is unchanged (the url points at the exact\n        // same cropped PNG).\n        msg(\"Uploading images\u2026\", true);\n        try { await _dmbExternalizeTemplateImages(id, password, payload, proxy); } catch (e) {}\n        msg(\"Saving\u2026\", true);\n        const resp = await fetch(proxy + \"/save-template\", {\n          method: \"POST\", credentials: \"same-origin\",\n          headers: { \"content-type\": \"application/json\" },\n          body: JSON.stringify({ password, id, title: ttl, orientation: landscape ? \"landscape\" : \"portrait\", json: payload })\n        });\n        let data = {}; try { data = await resp.json(); } catch (e) {}\n        if (resp.ok && data.ok) {\n          msg(\"\u2713 Published.\", true);\n          goBtn.textContent = \"Published \u2713\";\n          setTimeout(close, 1600);\n          return;\n        }\n        goBtn.disabled = false; goBtn.textContent = \"Publish\";\n        if (resp.status === 401) { msg(\"Incorrect passcode \u2014 not saved.\"); return; }\n        if (resp.status === 503) { msg(\"Publishing isn't configured (the passcode secret is missing).\"); return; }\n        msg(\"Couldn't publish (\" + (data.error || (\"HTTP \" + resp.status)) + \").\");\n      } catch (e) {\n        goBtn.disabled = false; goBtn.textContent = \"Publish\";\n        msg(\"Network error (\" + (e && e.message ? e.message : \"failed\") + \").\");\n      }\n    }\n\n    document.getElementById(\"dmbPubTplGo\").addEventListener(\"click\", _doPublish);\n    document.getElementById(\"dmbPubTplPw\").addEventListener(\"keydown\", (e) => { if (e.key === \"Enter\") _doPublish(); });\n    setTimeout(() => { try { document.getElementById(\"dmbPubTplPw\").focus(); } catch (e) {} }, 50);\n  }\n\n\n  // \u2500\u2500 Predesigned-cover template: cache + background prefetch \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // The template (cover-<handle>.json) is fetched over the network. Fetching it\n  // only at builder-entry meant a slow/flaky connection could leave the cover\n  // blank. So we WARM it as soon as the handle is known (on the upload step) and\n  // cache the result per handle, making the later apply instant. Failed fetches\n  // are NOT cached, so a later attempt retries.\n  const _dmbTplCache = {};   // handle \u2192 Promise<data|null>\n  function _dmbFetchTemplateData(tag) {\n    // Load order: R2 first (self-service, theme-independent), then the theme\n    // asset as a fallback. The ~60 pre-existing predesigned covers still live as\n    // theme assets, so nothing that works today breaks \u2014 R2 just 404s for those\n    // and we fall through. Any template saved via \"Save to store\" lives in R2 and\n    // wins from then on, so it survives theme swaps.\n    const ls = (typeof editor !== \"undefined\" && editor && editor.orientation === \"landscape\");\n\n    // \u2500\u2500 R2 (through the App Proxy). Shopify signs the request so the Worker\n    //    trusts it; unsaved templates return 404 and we move on to the asset. \u2500\u2500\n    const _fetchR2 = (t) => {\n      const proxy = window.DMB_DESIGNS_PROXY || \"/apps/designs\";\n      // Cache-bust the R2 read. It previously had NO buster (the asset path below\n      // does), so after the owner re-published a template it kept showing the OLD\n      // version on OTHER devices (the publishing device showed its live/in-memory\n      // copy, so it \"worked there\" but not on the phone). A per-load token + no-store\n      // makes a re-published template appear on the next load anywhere. Extra query\n      // params are still covered by Shopify's App-Proxy signature (like &ls=), so the\n      // Worker keeps trusting the request. Templates load ~once per session, so the\n      // dropped edge-caching is negligible.\n      const _bust = \"&v=\" + Date.now();\n      return fetch(proxy + \"/template?id=\" + encodeURIComponent(t) + \"&ls=\" + (ls ? 1 : 0) + _bust,\n                   { credentials: \"same-origin\", cache: \"no-store\" })\n        .then((r) => (r && r.ok) ? r.json() : null)\n        .catch(() => null);\n    };\n\n    // \u2500\u2500 Theme asset (legacy home of the predesigned covers). \u2500\u2500\n    const _fetchAsset = (t) => {\n      const base = new URL(window.DMB_ASSET_BASE, window.location.href);\n      base.pathname = base.pathname.replace(/\\/[^\\/]+$/, \"/cover-\" + t + \".json\");\n      // Cache-buster. Shopify's CDN caches these JSONs for a YEAR at a fixed URL,\n      // so the OLD version of an updated template kept serving forever (updates\n      // never showed). A ~5-minute time bucket keeps the URL stable within a short\n      // window (still well cached) but rolls over often enough that a re-pushed\n      // template appears within a few minutes \u2014 no JS deploy required.\n      base.search = \"?v=\" + Math.floor(Date.now() / 300000);\n      return fetch(base.toString(), { cache: \"no-store\" });\n    };\n\n    // Try as-is; if it 404s, retry uppercased (predesigned codes are stored\n    // uppercase, e.g. cover-HCITA2603.json) so a mis-cased serial_number still\n    // resolves. Custom lowercase handles succeed on the first try.\n    const up = String(tag).toUpperCase();\n    return _fetchR2(tag)\n      .then((d) => d || (up !== tag ? _fetchR2(up) : null))\n      .then((d) => {\n        if (d) return d;\n        return _fetchAsset(tag)\n          .then((resp) => {\n            if (resp && resp.ok) return resp;\n            return (up !== tag) ? _fetchAsset(up) : resp;\n          })\n          .then((resp) => (resp && resp.ok) ? resp.json() : null)\n          .catch(() => null);\n      });\n  }\n  function _dmbWarmTemplate(tag) {\n    if (!tag) return Promise.resolve(null);\n    let p = _dmbTplCache[tag];\n    if (!p) {\n      p = _dmbFetchTemplateData(tag);\n      _dmbTplCache[tag] = p;\n      // Drop a failed result so the next request retries instead of reusing null.\n      p.then((data) => { if (!data) { try { delete _dmbTplCache[tag]; } catch (e) {} } });\n    }\n    return p;\n  }\n\n  async function dmbApplyCoverTemplate(tag, force) {\n    if (!tag) return;\n    // A predesigned template is only a STARTING point. The moment a customer has ANY\n    // cover design, we must NEVER overwrite it with the template \u2014 the printed PDF has to\n    // reproduce exactly the cover they made, edited or not. Only an explicit, confirmed\n    // \"apply this template\" (force) may replace an existing cover. This blocks the\n    // automatic open/restore paths from silently reverting or swapping the cover.\n    if (!force) {\n      const _hasCoverWork =\n        (Array.isArray(editor.coverObjects) && editor.coverObjects.length > 0) ||\n        (Array.isArray(editor.coverUserTexts) && editor.coverUserTexts.length > 0) ||\n        !!coverState.frontImageId || !!coverState.frontImageUrl ||\n        !!coverState.backImageId  || !!coverState.backImageUrl  ||\n        !!coverState.bleedImageId || !!coverState.bleedImageUrl || !!coverState.spineImageUrl ||\n        !!(coverState.title1 && String(coverState.title1).trim()) ||\n        !!(coverState.title2 && String(coverState.title2).trim());\n      if (_hasCoverWork) return;   // keep the customer's cover \u2014 do NOT re-apply the template\n    }\n    try {\n      // Use the prefetched copy when warming already ran (instant); otherwise this\n      // starts the fetch now. If the warmed result was null (failed earlier), make\n      // one fresh attempt before giving up.\n      let data = await _dmbWarmTemplate(tag);\n      if (!data) data = await _dmbFetchTemplateData(tag);\n      if (!data) return;\n      // Apply from a private deep copy so editing the cover never mutates the\n      // cached template (the cache may be reused if the template is applied again).\n      data = cloneState(data);\n      if (data.coverState) {\n        Object.assign(coverState, data.coverState);\n        syncCoverInputsFromState();\n      }\n      if (Array.isArray(data.coverObjects)) {\n        editor.coverObjects = data.coverObjects;\n      }\n      if (data.activeCoverZone) activeCoverZone = data.activeCoverZone;\n      if (data.coverUserTexts) editor.coverUserTexts = cloneState(data.coverUserTexts);\n      // Spine text in templates starts at 6px \u2014 the spine is a thin 8 mm strip, so\n      // the templates' larger spine sizes would overflow it. Customers can resize\n      // afterwards (this only sets the template's starting size, stored per-design).\n      try {\n        (editor.coverUserTexts || []).forEach(function (t) {\n          if (t && t.zone === \"spine\" && coverState.textStyles && coverState.textStyles[t.id]) {\n            coverState.textStyles[t.id].fontSize = \"6px\";\n          }\n        });\n      } catch (e) {}\n      if (data.page0) {\n        if (!editor.pageSlots) editor.pageSlots = {};\n        if (!editor.pageFrames) editor.pageFrames = {};\n        if (!editor.pageTemplate) editor.pageTemplate = {};\n        if (!editor.pageObjects) editor.pageObjects = {};\n        if (!editor.pageRot) editor.pageRot = {};\n        editor.pageSlots[0] = cloneState(data.page0.pageSlots || { A: null, B: null });\n        editor.pageFrames[0] = cloneState(data.page0.pageFrames || {});\n        editor.pageTemplate[0] = data.page0.pageTemplate || null;\n        editor.pageObjects[0] = cloneState(data.page0.pageObjects || []);\n        editor.pageRot[0] = cloneState(data.page0.pageRot || { A: 0, B: 0 });\n      }\n      // A predesigned template may ship a fixed-slot cover layout (frontLayout/\n      // frontFrames). Convert it to deletable free photo-frames so the customer can\n      // move/resize/delete them like any other frame.\n      try { _dmbConvertCoverFixedLayouts(); } catch (e) {}\n      renderCoverCanvas();\n      // Template also replaces page 0 content \u2014 dirty-mark it too\n      try { _dmbPvMarkDirty(0); } catch(e) {}\n    } catch (err) {\n      console.warn(\"[DMB] Cover template load failed:\", err);\n    }\n  }\n\n  function getTemplateKey() {\n    const liveTemplate = new URLSearchParams(window.location.search).get(\"template\");\n    return (liveTemplate || \"\").trim();\n  }\n\n  // \u2500\u2500 Customer progress save/load (per-book, persists across sessions) \u2500\u2500\u2500\u2500\u2500\u2500\n\n  function getCustomerSaveKey() {\n    // RENDER MODE: the design id comes from the JOB RECORD - no URL param, no\n    // localStorage read, no mint. Without this, a render URL (which carries no\n    // ?design/?cover/?template) makes _dmbActiveCustomId() MINT and WRITE a random\n    // id, and then every photo fetch and staging call targets a design that does\n    // not exist.\n    if (_dmbRenderMode() && window.__DMB_RENDER_JOB) return \"dmb_progress_\" + window.__DMB_RENDER_JOB;\n    const type = window.DMB_CARD_MODE ? \"card\" : window.DMB_MAGAZINE_MODE ? \"mag\" : \"book\";\n    // Predesigned template or a product cover handle \u2192 keep the existing per-template/\n    // per-product key (UNCHANGED \u2014 no behaviour change, no orphaning of existing saves).\n    let suffix = getTemplateKey();                    // predesigned template\n    if (!suffix) {\n      try { suffix = new URLSearchParams(location.search).get(\"cover\") || \"\"; } catch (e) {}\n    }\n    if (suffix) return `dmb_progress_${type}_${suffix}`;\n    // Custom book (no template, no cover): give it its OWN permanent id so two\n    // custom books never overwrite each other. Honour ?design= (resume links),\n    // else the remembered \"active\" id (so a revisit still resumes the current one).\n    let id = \"\";\n    try { id = new URLSearchParams(location.search).get(\"design\") || \"\"; } catch (e) {}\n    if (!id) id = _dmbActiveCustomId(type);\n    return `dmb_progress_${type}_${id}`;\n  }\n  // Persistent per-browser id of the custom design currently being worked on.\n  function _dmbActiveCustomId(type) {\n    const k = \"dmb_active_design_\" + type;\n    try {\n      let id = localStorage.getItem(k);\n      if (!id) {\n        // Back-compat: keep using a legacy shared \"custom\" design if one exists.\n        id = localStorage.getItem(`dmb_progress_${type}_custom`)\n          ? \"custom\"\n          : (\"d\" + Date.now().toString(36) + Math.random().toString(36).slice(2, 6));\n        localStorage.setItem(k, id);\n      }\n      return id;\n    } catch (e) { return \"custom\"; }\n  }\n  // Start a brand-new custom design slot (old one is preserved under its id).\n  function _dmbRotateCustomId() {\n    const type = window.DMB_CARD_MODE ? \"card\" : window.DMB_MAGAZINE_MODE ? \"mag\" : \"book\";\n    try {\n      // Only meaningful for custom books (no template / cover handle).\n      if (getTemplateKey()) return;\n      if (new URLSearchParams(location.search).get(\"cover\")) return;\n      localStorage.setItem(\"dmb_active_design_\" + type, \"d\" + Date.now().toString(36) + Math.random().toString(36).slice(2, 6));\n      // Drop any ?design= from the URL so the new active id is used.\n      const u = new URL(location.href); u.searchParams.delete(\"design\");\n      history.replaceState(null, \"\", u.pathname + u.search + u.hash);\n    } catch (e) {}\n  }\n\n  // \u2500\u2500 IndexedDB photo persistence \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  function _openPhotoDB() {\n    return new Promise((resolve, reject) => {\n      const req = indexedDB.open(\"dmb_photo_store\", 1);\n      req.onupgradeneeded = (e) => {\n        const db = e.target.result;\n        if (!db.objectStoreNames.contains(\"blobs\")) db.createObjectStore(\"blobs\");\n      };\n      req.onsuccess = (e) => resolve(e.target.result);\n      req.onerror = () => reject(req.error);\n    });\n  }\n\n  // Returns true only when bytes were actually written, false otherwise \u2014 so the\n  // caller can retry and/or warn the customer instead of silently losing photos.\n  // Per-saveKey set of pids already written to IndexedDB this session, so repeated\n  // saves during an upload only touch NEW photos (no re-reading the whole library).\n  const _dmbIdbSavedByKey = {};\n  async function _savePhotosToIDB(saveKey, photos) {\n    if (!Array.isArray(photos) || !photos.length) return false;\n    try {\n      // PHONE-MEMORY CRITICAL: write ONE photo at a time. The old code read EVERY\n      // photo's full ArrayBuffer into a single array before writing (to dodge IDB's\n      // auto-commit between awaits) \u2014 so the entire library sat in RAM as buffers\n      // ON TOP of the File blobs, and it re-ran on every upload batch. On a phone\n      // that doubling is what triggered \"a problem repeatedly occurred\" (WebKit\n      // OOM). Here each photo is read + written in its OWN transaction (self-\n      // contained, so no auto-commit race), then its buffer is released before the\n      // next \u2014 peak memory is ONE photo's bytes, regardless of library size.\n      //\n      // Bytes are stored as ArrayBuffers (NOT Blobs): iOS keeps IDB Blobs as\n      // file-backed refs that can be purged and read back EMPTY \u2192 blank photos.\n      let saved = _dmbIdbSavedByKey[saveKey];\n      if (!saved) { saved = new Set(); _dmbIdbSavedByKey[saveKey] = saved; }\n\n      const db = await _openPhotoDB();\n      const metaAll = [];\n      const metaFor = (p, type) => ({\n        id: p.id, ratio: p.ratio, filename: p.filename,\n        dateTaken: p.dateTaken, tags: p.tags, addedIndex: p.addedIndex,\n        mime: type || p._dmbMime || \"image/jpeg\"\n      });\n\n      for (const ph of photos) {\n        if (!ph || !ph.id) continue;\n        if (saved.has(ph.id)) { metaAll.push(metaFor(ph)); continue; }   // already on disk \u2014 don't re-read\n        let src = ph.file || null;\n        if ((!src || (src.size != null && src.size === 0)) && ph.url) {\n          try { src = await (await fetch(ph.url)).blob(); } catch (e) { /* keep src */ }\n        }\n        if (!src || !src.size) continue;              // can't read now \u2192 retry on a later save\n        const type = src.type || \"image/jpeg\";\n        let buf = null;\n        try { buf = await src.arrayBuffer(); } catch (e) { buf = null; }\n        if (!buf || !buf.byteLength) continue;\n        try {\n          const tx = db.transaction(\"blobs\", \"readwrite\");   // one photo per transaction\n          tx.objectStore(\"blobs\").put({ buf, type, bytes: buf.byteLength }, saveKey + \"__\" + ph.id);\n          await new Promise((res, rej) => { tx.oncomplete = res; tx.onerror = () => rej(tx.error); });\n          saved.add(ph.id);\n          try { ph._dmbMime = type; } catch (e) {}\n          metaAll.push(metaFor(ph, type));\n        } catch (e) { /* leave unsaved \u2192 retried next call */ }\n        buf = null;                                    // release this photo's bytes now\n        await new Promise((r) => setTimeout(r, 0));    // yield so the browser can reclaim\n      }\n\n      // Only touch the meta record when we have something to record \u2014 never clobber\n      // a good manifest with an empty one (e.g. every read failed).\n      if (metaAll.length) {\n        try {\n          const txm = db.transaction(\"blobs\", \"readwrite\");\n          txm.objectStore(\"blobs\").put(metaAll, saveKey + \"__meta\");\n          await new Promise((res, rej) => { txm.oncomplete = res; txm.onerror = () => rej(txm.error); });\n        } catch (e) {}\n      }\n      db.close();\n      return metaAll.length > 0;\n    } catch(e) { console.warn(\"[DMB] savePhotos:\", e); return false; }\n  }\n\n  // Fetch ONE photo's full-res bytes back from R2, using the right identity:\n  //   \u2022 signed-in \u2192 the App Proxy appends the verified logged_in_customer_id, so\n  //     the Worker reads from the customer's namespace (no sid needed).\n  //   \u2022 guest     \u2192 add &sid=<guest id> so the Worker reads designs/guest_<sid>/\u2026\n  // This is what lets a signed-in customer recover photos that IndexedDB evicted\n  // (the reported \"most pictures missing on the laptop\" case) and what makes the\n  // per-photo restore fallback below work for EVERYONE, not just guests.\n  async function _dmbFetchPhotoBlob(designId, pid, kind) {\n    if (!window.DMB_DESIGNS_PROXY || !pid) return null;\n    let url;\n    if (_dmbRenderMode() && _DMB_RM.token) {\n      // RENDER MODE: token-only identity - no login, no guest sid. The Worker resolves\n      // owner + designId from the job record; nothing identifying is in the URL. This\n      // also stops _dmbPhotoBackup.guestId() minting a dmb_guest_id in localStorage,\n      // which would address designs/guest_<random>/ and 404 every single photo.\n      url = window.DMB_DESIGNS_PROXY + \"/render-photo?t=\" + encodeURIComponent(_DMB_RM.token) +\n            \"&pid=\" + encodeURIComponent(pid);\n    } else {\n      if (!designId) return null;\n      url = window.DMB_DESIGNS_PROXY + \"/photo?id=\" + encodeURIComponent(designId) + \"&pid=\" + encodeURIComponent(pid);\n      if (!window.DMB_CUSTOMER_ID) {\n        try { const sid = _dmbPhotoBackup.guestId(); if (sid) url += \"&sid=\" + encodeURIComponent(sid); } catch (e) {}\n      }\n    }\n    // kind=\"master\" fetches the ~3500px print master from photos-orig/{pid}. A miss\n    // there is a legitimate absence (legacy/relay photo, evicted guest master), so\n    // masters get ONE attempt \u2014 the caller falls back to the proxy immediately.\n    if (kind === \"master\") url += \"&kind=master\";\n    const _attempts = kind === \"master\" ? 1 : 3;\n    // Retry a few times: this feeds the PDF render when full-res was released in\n    // light-memory mode, so a transient network blip must never blank a page. R2\n    // has the photo (uploaded at pick time), so a retry reliably succeeds.\n    for (let attempt = 0; attempt < _attempts; attempt++) {\n      try {\n        const r = await fetch(url, { credentials: \"same-origin\" });\n        if (r.ok) { const b = await r.blob(); if (b && b.size) return b; }\n      } catch (e) {}\n      if (attempt < _attempts - 1) await new Promise((res) => setTimeout(res, 500 * (attempt + 1)));\n    }\n    return null;\n  }\n\n  // Every photo id the CURRENT restored layout points at (page slots, spreads,\n  // template frames, free photo-frames, cover). Used to detect which photos a\n  // restore is missing so we can pull just those back from the cloud.\n  function _dmbCollectReferencedPhotoIds() {\n    const ids = new Set();\n    const add = (v) => { if (v && typeof v === \"string\") ids.add(v); };\n    try { const ps = editor.pageSlots || {}; Object.keys(ps).forEach((k) => { const s = ps[k] || {}; add(s.A); add(s.B); }); } catch (e) {}\n    try { const sp = editor.spreadPhoto || {}; Object.keys(sp).forEach((k) => { if (sp[k]) add(sp[k].photoId); }); } catch (e) {}\n    try { const pf = editor.pageFrames || {}; Object.keys(pf).forEach((k) => { const m = pf[k] || {}; Object.keys(m).forEach((kk) => add(m[kk])); }); } catch (e) {}\n    try { const po = editor.pageObjects || {}; Object.keys(po).forEach((k) => { (po[k] || []).forEach((o) => { if (o && o.photoId) add(o.photoId); }); }); } catch (e) {}\n    try { (editor.coverObjects || []).forEach((o) => { if (o && o.photoId) add(o.photoId); }); } catch (e) {}\n    try { Object.keys(coverState || {}).forEach((k) => { if (/imageid$|photoid$/i.test(k)) add(coverState[k]); }); } catch (e) {}\n    return ids;\n  }\n\n  // The FULL photo manifest for a design from the cloud \u2014 every photo that was\n  // uploaded, PLACED OR UNUSED (the tray keeps unused photos, and they're saved to\n  // R2 + listed in meta.photos). Returns [{id,filename,ratio,dateTaken,...}] or [].\n  async function _dmbFetchDesignManifestPhotos(designId) {\n    if (!window.DMB_DESIGNS_PROXY || !designId) return [];\n    const readMeta = (d) => {\n      const meta = d && d.meta;\n      if (!meta) return [];\n      if (Array.isArray(meta.photos) && meta.photos.length) return meta.photos;\n      if (Array.isArray(meta.photoIds)) return meta.photoIds.map((id) => ({ id }));\n      return [];\n    };\n    try {\n      if (window.DMB_CUSTOMER_ID) {\n        const r = await fetch(window.DMB_DESIGNS_PROXY + \"/load?id=\" + encodeURIComponent(designId), { credentials: \"same-origin\" });\n        if (r.ok) return readMeta(await r.json());\n      } else {\n        let sid = \"\"; try { sid = _dmbPhotoBackup.guestId(); } catch (e) {}\n        if (sid) {\n          const r = await fetch(window.DMB_DESIGNS_PROXY + \"/load-guest?id=\" + encodeURIComponent(designId) + \"&sid=\" + encodeURIComponent(sid), { credentials: \"same-origin\" });\n          if (r.ok) return readMeta(await r.json());\n        }\n      }\n    } catch (e) {}\n    return [];\n  }\n\n  // ADDITIVE restore repair: bring back EVERY photo the saved design has in the\n  // cloud that isn't already in editor.photos \u2014 including UNUSED photos still in\n  // the tray (they're in the cloud manifest, not just the ones placed on pages).\n  // Also covers anything the current layout references. Serial \u2192 memory-safe even\n  // when many are missing. Never removes/replaces. Returns how many were recovered.\n  async function _dmbReconcileMissingPhotosFromCloud() {\n    if (!window.DMB_DESIGNS_PROXY || !Array.isArray(editor.photos)) return 0;\n    let designId = \"\";\n    try { designId = getCustomerSaveKey().replace(/^dmb_progress_/, \"\"); } catch (e) {}\n    if (!designId) return 0;\n\n    // Authoritative full list from the cloud (placed AND unused).\n    let manifest = [];\n    try { manifest = await _dmbFetchDesignManifestPhotos(designId); } catch (e) {}\n    const metaById = {};\n    manifest.forEach((mp) => { if (mp && mp.id) metaById[mp.id] = mp; });\n\n    // Want = everything in the manifest \u222A everything the layout references.\n    const want = new Set();\n    manifest.forEach((mp) => { if (mp && mp.id) want.add(mp.id); });\n    _dmbCollectReferencedPhotoIds().forEach((id) => { if (id) want.add(id); });\n\n    const present = new Set(editor.photos.map((p) => p && p.id));\n    const missing = [];\n    want.forEach((id) => { if (id && !present.has(id)) missing.push(id); });\n    if (!missing.length) return 0;\n\n    let added = 0;\n    // Print-master path in light-mem: do NOT serially download every missing photo\n    // (a 300-photo restore would fetch 300 blobs before the builder settles). A\n    // photo the current spread doesn't show, whose manifest meta is complete, is\n    // restored as a LAZY record \u2014 bytes stay on R2 (_r2), it self-heals on view\n    // (_dmbRecoverPhotoUrl) and in the PDF (_dmbRenderImg). On-spread photos and\n    // ones with incomplete meta still download eagerly, exactly like today.\n    const _lazyKeep = (_dmbPrintMasterOn() && _dmbLightMem()) ? _dmbCurrentSpreadPhotoIds() : null;\n    for (const pid of missing) {\n      const _mp0 = metaById[pid];\n      if (_lazyKeep && !_lazyKeep.has(pid) && _mp0 && typeof _mp0.ratio === \"number\" && _mp0.ratio > 0) {\n        editor.photos.push({\n          id: pid, file: null, url: \"\", _r2: true,\n          ratio: _mp0.ratio, filename: _mp0.filename || \"photo.jpg\",\n          dateTaken: _mp0.dateTaken || \"\", tags: _mp0.tags || [],\n          addedIndex: (typeof _mp0.addedIndex === \"number\") ? _mp0.addedIndex : editor.photos.length,\n        });\n        added++;\n        continue;\n      }\n      let blob = null;\n      try { blob = await _dmbFetchPhotoBlob(designId, pid); } catch (e) {}\n      if (!blob || !blob.size) continue;\n      const mp = metaById[pid] || {};\n      let f = null;\n      try { f = new File([blob], mp.filename || \"photo.jpg\", { type: blob.type || \"image/jpeg\" }); } catch (e) { f = blob; }\n      let ratio = mp.ratio;\n      if (!ratio) { try { ratio = (await _dmbCheapMeta(f)).ratio || 1; } catch (e) { ratio = 1; } }\n      editor.photos.push({\n        id: pid, file: f, url: URL.createObjectURL(blob),\n        ratio: ratio || 1, filename: mp.filename || \"photo.jpg\",\n        dateTaken: mp.dateTaken || \"\", tags: mp.tags || [],\n        addedIndex: (typeof mp.addedIndex === \"number\") ? mp.addedIndex : editor.photos.length,\n      });\n      added++;\n    }\n    // Restore the original tray order (by addedIndex) so recovered photos slot in\n    // where they were, not just appended at the end.\n    try { editor.photos.sort((a, b) => (a.addedIndex || 0) - (b.addedIndex || 0)); } catch (e) {}\n    return added;\n  }\n\n  // \u2500\u2500 Level 2: light-memory mode \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // Goal: never hold every full-res photo in RAM. On phones (where RAM is the\n  // constraint) and only for LARGE books, once a photo is safely on R2 its\n  // resident full-res bytes are released when it's not on the spread being\n  // viewed. The tray keeps showing its small thumbnail; a placed photo that was\n  // released self-heals on view via _dmbBindPhotoImg \u2192 _dmbRecoverPhotoUrl (and\n  // the PDF via _dmbRenderImg) \u2014 both re-fetch the full-res original from R2 by\n  // id. So the worst case of releasing something still needed is a brief re-fetch,\n  // never a blank or a quality change. Default: ON for phones, OFF on desktop;\n  // window.DMB_LIGHT_PHOTO_MEM = true/false forces it either way.\n  const _DMB_LIGHT_MEM_MIN_PHOTOS = 80;   // small/medium books never release (no churn)\n  function _dmbLightMem() {\n    if (typeof window === \"undefined\") return false;\n    if (_dmbRenderMode()) return true;   // lazy photo records: bytes stream from R2 on demand\n    if (window.DMB_LIGHT_PHOTO_MEM === true) return true;\n    if (window.DMB_LIGHT_PHOTO_MEM === false) return false;\n    try { return window.innerWidth <= 749; } catch (e) { return false; }   // phones by default\n  }\n\n  // \u2500\u2500 PRINT MASTERS (flag-gated, default OFF) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // window.DMB_PRINT_MASTER = true turns on the full-quality pipeline: every\n  // imported photo yields TWO derivatives from ONE memory-safe scaled decode \u2014\n  //   \u2022 a ~3500px PRINT MASTER (true 300 DPI on a full A4) uploaded to R2 at\n  //     photos-orig/{pid} (fetched back with &kind=master; feeds the Step-2\n  //     server render + the desktop client PDF),\n  //   \u2022 a 2500px editing PROXY that takes the photo's place everywhere else\n  //     (same size/quality as today's mobile proxy \u2192 zero regression).\n  // Masters ride the existing instant-backup /photo endpoint, so the flag also\n  // requires DMB_DESIGNS_PROXY. While OFF, every branch below is dead code.\n  const _DMB_MASTER_DIM = 3500, _DMB_MASTER_Q = 0.9;\n  const _DMB_PROXY_DIM  = 2500, _DMB_PROXY_Q  = 0.88;   // == today's mobile proxy\n  const _DMB_MASTER_SUBBATCH = 10;  // flagged import sub-batch (bounds resident masters)\n  function _dmbPrintMasterOn() {\n    try {\n      // A server render is ALWAYS master-fed. This also gates the cover face/bleed\n      // draw-BY-ID branch, which is the only way a lazy cover photo gets drawn at all.\n      if (_dmbRenderMode()) return !!window.DMB_DESIGNS_PROXY;\n      return !!window.DMB_PRINT_MASTER && !!window.DMB_DESIGNS_PROXY;\n    } catch (e) { return false; }\n  }\n  // Master fetch-miss memo: a pid whose master 404'd (legacy photo, relay photo,\n  // evicted guest master) is not re-tried this session \u2014 the proxy serves instead.\n  const _dmbNoMasterPids = new Set();\n  // One-time capability probe: an OLD Worker ignores &kind=master and would write\n  // the master OVER the proxy at photos/{pid}. Never upload a master until the\n  // Worker proves it routes kind (GET /caps \u2192 {masters:true}).\n  let _dmbMasterCapsP = null;\n  function _dmbMasterCaps() {\n    if (!_dmbMasterCapsP) {\n      _dmbMasterCapsP = (async () => {\n        let ok = false;\n        try {\n          const r = await fetch((window.DMB_DESIGNS_PROXY || \"\") + \"/caps\", { credentials: \"same-origin\" });\n          const j = r.ok ? await r.json().catch(() => null) : null;\n          ok = !!(j && j.masters === true);\n        } catch (e) { ok = false; }\n        // A transient failure (flaky phone connection) must NOT be remembered as\n        // \"this Worker cannot take masters\" for the rest of the session - that\n        // silently dropped every master of every later import with no symptom\n        // except \"0 of N at full print quality\". Only a positive answer is cached.\n        if (!ok) _dmbMasterCapsP = null;\n        return ok;\n      })();\n    }\n    return _dmbMasterCapsP;\n  }\n  // Background master uploader: a serial promise chain SEPARATE from the instant-\n  // backup queue, so the batch-upload wait (which gates the import modal) drains on\n  // PROXIES only \u2014 masters keep uploading quietly behind it. The blob is freed the\n  // moment its upload settles (success OR final failure), so masters never pile up.\n  let _dmbMasterChain = Promise.resolve();\n  let _dmbMasterPendingCount = 0;\n  // Session tally of master outcomes - surfaced in the server-render trace so a\n  // low \"at full print quality\" count can be explained instead of guessed at.\n  const _dmbMasterStats = { queued: 0, ok: 0, failed: 0, nocaps: 0, decode_failed: 0, relay: 0 };\n  try { window.__DMB_MASTER_STATS = _dmbMasterStats; } catch (e) {}\n  function _dmbQueueMasterUpload(f, designId, sid, pid) {\n    // pid: explicit for the customer cloud path (editor.photos ids); the guest\n    // instant-backup queue stamps f._dmbPid instead.\n    const _pid = pid || (f && f._dmbPid);\n    if (!f || !f._dmbMaster || !_pid) return;\n    if (f._dmbMasterQueued) return;            // one-shot per File, whichever path asks first\n    f._dmbMasterQueued = true;\n    _dmbMasterPendingCount++;\n    _dmbMasterChain = _dmbMasterChain.then(async () => {\n      const blob = f._dmbMaster;\n      let _state = \"failed\";\n      try {\n        if (!blob || !(await _dmbMasterCaps())) { _state = \"nocaps\"; return; }   // old Worker \u2192 proxy-only, no overwrite risk\n        let u = (window.DMB_DESIGNS_PROXY || \"\") + \"/photo?id=\" + encodeURIComponent(designId) +\n                \"&pid=\" + encodeURIComponent(_pid) + (sid ? \"&sid=\" + encodeURIComponent(sid) : \"\") +\n                \"&kind=master\";\n        for (let a = 0; a < 2; a++) {\n          try {\n            const r = await fetch(u, { method: \"POST\", headers: { \"content-type\": \"image/jpeg\" }, body: blob, credentials: \"same-origin\" });\n            if (r.ok) {\n              const j = await r.json().catch(() => null);\n              _state = (j && j.kind === \"master\") ? \"ok\" : \"nocaps\";\n              return;                                       // no echo = old Worker slipped through caps; stop (do not retry into photos/)\n            }\n          } catch (e) {}\n          if (a === 0) await new Promise((res) => setTimeout(res, 1200));\n        }\n      } catch (e) {} finally {\n        try { f._dmbMaster = null; f._dmbMasterState = _state; _dmbMasterStats[_state] = (_dmbMasterStats[_state] || 0) + 1; } catch (e) {}\n        _dmbMasterPendingCount = Math.max(0, _dmbMasterPendingCount - 1);\n      }\n    });\n    return _dmbMasterChain;\n  }\n\n  // Release resident full-res bytes of photos NOT in the keep-set. Keeps the\n  // small thumbUrl. Only acts in light-memory mode and only for photos CONFIRMED\n  // on R2 (_r2). Bytes stay safe on R2 and are re-fetched on demand when needed.\n  function _dmbReleaseDistantFullRes(keepIds) {\n    if (!_dmbLightMem() || !Array.isArray(editor.photos)) return 0;\n    const keep = keepIds instanceof Set ? keepIds : new Set(keepIds || []);\n    let freed = 0;\n    for (const ph of editor.photos) {\n      if (!ph || keep.has(ph.id) || !ph._r2) continue;\n      if (ph.url && String(ph.url).startsWith(\"blob:\")) { try { URL.revokeObjectURL(ph.url); } catch (e) {} ph.url = \"\"; }\n      if (ph.file) { ph.file = null; freed++; }\n    }\n    return freed;\n  }\n\n  // Photo ids referenced by the spread currently on screen (+ cover) \u2014 the set\n  // we must KEEP resident so nothing visible has to re-fetch.\n  function _dmbCurrentSpreadPhotoIds() {\n    const ids = new Set();\n    const add = (v) => { if (v && typeof v === \"string\") ids.add(v); };\n    try {\n      const si = editor.currentSpread || 0;\n      // Keep a small window around the viewed spread so quick back/forward paging\n      // doesn't thrash. Page mapping is best-effort (varies by book/magazine plan);\n      // being generous here only means keeping a few extra photos resident.\n      for (let s = si - 1; s <= si + 1; s++) {\n        if (s < 0) continue;\n        for (const p of [2 * s + 1, 2 * s + 2, 0]) {\n          const sl = (editor.pageSlots || {})[p] || {}; add(sl.A); add(sl.B);\n          const pf = (editor.pageFrames || {})[p] || {}; Object.keys(pf).forEach((k) => add(pf[k]));\n          const po = (editor.pageObjects || {})[p] || []; po.forEach((o) => { if (o && o.photoId) add(o.photoId); });\n        }\n        const sp = (editor.spreadPhoto || {})[s]; if (sp) add(sp.photoId);\n      }\n      (editor.coverObjects || []).forEach((o) => { if (o && o.photoId) add(o.photoId); });\n      Object.keys(coverState || {}).forEach((k) => { if (/imageid$|photoid$/i.test(k)) add(coverState[k]); });\n      [\"frontFrames\", \"backFrames\"].forEach((fk) => { const m = coverState && coverState[fk]; if (m) Object.keys(m).forEach((k) => add(m[k])); });\n    } catch (e) {}\n    return ids;\n  }\n\n  // Called from the render flow. Releases distant full-res only when we've moved\n  // to a new spread, in light-mem mode, on a large enough book.\n  let _dmbLastReleaseSpread = -999;\n  function _dmbMaybeReleaseForSpread() {\n    try {\n      if (!_dmbLightMem()) return;\n      if (!Array.isArray(editor.photos) || editor.photos.length < _DMB_LIGHT_MEM_MIN_PHOTOS) return;\n      const cur = editor.currentSpread || 0;\n      if (cur === _dmbLastReleaseSpread) return;      // same spread \u2192 nothing new to free\n      _dmbLastReleaseSpread = cur;\n      _dmbReleaseDistantFullRes(_dmbCurrentSpreadPhotoIds());\n    } catch (e) {}\n  }\n\n  async function _loadPhotosFromIDB(saveKey) {\n    try {\n      const db = await _openPhotoDB();\n      const tx = db.transaction(\"blobs\", \"readonly\");\n      const store = tx.objectStore(\"blobs\");\n      const meta = await new Promise(res => {\n        const r = store.get(saveKey + \"__meta\");\n        r.onsuccess = () => res(r.result || []);\n        r.onerror = () => res([]);\n      });\n      if (!meta.length) { db.close(); return []; }\n      const photos = [];\n      for (const m of meta) {\n        const rec = await new Promise(res => {\n          const r = store.get(saveKey + \"__\" + m.id);\n          r.onsuccess = () => res(r.result || null);\n          r.onerror = () => res(null);\n        });\n\n        // Rebuild a Blob from whatever format the entry is stored in:\n        //  \u2022 { buf, type } \u2014 current format (ArrayBuffer, reliable on iOS)\n        //  \u2022 Blob          \u2014 legacy entries saved before the ArrayBuffer switch\n        //  \u2022 ArrayBuffer   \u2014 defensive fallback\n        let blob = null;\n        if (rec) {\n          if (rec instanceof Blob) {\n            blob = rec;\n          } else if (rec.buf) {\n            try { blob = new Blob([rec.buf], { type: rec.type || m.mime || \"image/jpeg\" }); } catch (e) {}\n          } else if (rec instanceof ArrayBuffer) {\n            try { blob = new Blob([rec], { type: m.mime || \"image/jpeg\" }); } catch (e) {}\n          }\n        }\n\n        // Only keep photos whose bytes actually came back (size > 0). An empty\n        // entry means the data was lost/evicted \u2014 skip it rather than showing a\n        // blank/white image.\n        if (blob && blob.size) {\n          // Reconstruct a REAL File so bookFiles holds real files, not empty\n          // 0-byte placeholders (otherwise a later refreshBuilderPhotos() rebuilds\n          // the images from empty files and they break after a click once restored).\n          let _f = null;\n          try { _f = new File([blob], (m.filename || \"photo.jpg\"), { type: blob.type || \"image/jpeg\" }); }\n          catch (e) { _f = blob; }\n          photos.push({ ...m, file: _f, url: URL.createObjectURL(blob) });\n        } else if (window.DMB_DESIGNS_PROXY) {\n          // Local bytes are gone (IndexedDB evicted \u2014 the iOS data-loss case, and\n          // the signed-in \"most photos missing on the laptop\" case). Pull this\n          // photo back from R2 using the right identity so the design isn't lost.\n          // Print-master path in light-mem: an off-spread photo is restored LAZY\n          // (no download now \u2014 bytes stay on R2, _r2:true, self-heals on view/PDF)\n          // so a big-book restore doesn't serially download hundreds of blobs.\n          try {\n            const _keepIDB = (_dmbPrintMasterOn() && _dmbLightMem()) ? _dmbCurrentSpreadPhotoIds() : null;\n            if (_keepIDB && m.id && !_keepIDB.has(m.id) && typeof m.ratio === \"number\" && m.ratio > 0) {\n              photos.push({ ...m, file: null, url: \"\", _r2: true });\n            } else {\n              const _did = String(saveKey).replace(/^dmb_progress_/, \"\");\n              const _rb = await _dmbFetchPhotoBlob(_did, m.id);\n              if (_rb && _rb.size) {\n                let _f2 = null;\n                try { _f2 = new File([_rb], (m.filename || \"photo.jpg\"), { type: _rb.type || \"image/jpeg\" }); }\n                catch (e) { _f2 = _rb; }\n                photos.push({ ...m, file: _f2, url: URL.createObjectURL(_rb) });\n              }\n            }\n          } catch (e) {}\n        }\n      }\n      db.close();\n      return photos;\n    } catch(e) { console.warn(\"[DMB] loadPhotos:\", e); return []; }\n  }\n\n  // \u2500\u2500 IndexedDB persistence for OBJECT crop images (sticker / cover-icon\n  //    \"tightUrl\" base64, and any inline data: URL). These used to live INLINE in\n  //    the design JSON in localStorage \u2014 each is a full PNG, so a handful of them\n  //    (a) made every save write hundreds of KB (lag) and (b) overflowed the ~5 MB\n  //    localStorage cap, after which setItem threw and was SILENTLY swallowed \u2192\n  //    \"only the first sticker survived a refresh.\" We now mirror them into\n  //    IndexedDB (hundreds of MB, same store as photos) and strip them from the\n  //    localStorage state, which stays tiny + instant. Rendering is byte-identical\n  //    (same base64 reattached on restore); nothing about size/placement changes.\n  function _dmbCollectObjImages() {\n    const out = [], seen = {};\n    const scan = (arr) => {\n      if (!Array.isArray(arr)) return;\n      arr.forEach((o) => {\n        if (!o || !o.id || seen[o.id]) return;\n        let du = null, field = null;\n        if (typeof o.tightUrl === \"string\" && o.tightUrl.slice(0, 5) === \"data:\") { du = o.tightUrl; field = \"tightUrl\"; }\n        else if (typeof o.url === \"string\" && o.url.slice(0, 5) === \"data:\") { du = o.url; field = \"url\"; }\n        if (du) { out.push({ id: o.id, dataUrl: du, field: field }); seen[o.id] = true; }\n      });\n    };\n    try { scan(editor.coverObjects); } catch (e) {}\n    try { if (editor.pageObjects) Object.keys(editor.pageObjects).forEach((k) => scan(editor.pageObjects[k])); } catch (e) {}\n    return out;\n  }\n\n  // Ids whose crop image is CONFIRMED written to IndexedDB \u2014 only those are safe to\n  // strip from the localStorage snapshot (an image not yet mirrored stays inline so\n  // an instant refresh can't lose it).\n  const _dmbObjImgSaved = new Set();\n  async function _dmbSaveObjImagesToIDB(saveKey, imgs) {\n    if (!Array.isArray(imgs) || !imgs.length) return false;\n    try {\n      const db = await _openPhotoDB();\n      const tx = db.transaction(\"blobs\", \"readwrite\");\n      const store = tx.objectStore(\"blobs\");\n      const prevMeta = await new Promise((res) => { const r = store.get(saveKey + \"__objimgmeta\"); r.onsuccess = () => res(r.result || []); r.onerror = () => res([]); });\n      const known = {}; (prevMeta || []).forEach((m) => { if (m && m.id) known[m.id] = true; });\n      const meta = (prevMeta || []).slice();\n      imgs.forEach((im) => {\n        store.put({ dataUrl: im.dataUrl, field: im.field }, saveKey + \"__objimg__\" + im.id);\n        if (!known[im.id]) { meta.push({ id: im.id, field: im.field }); known[im.id] = true; }\n      });\n      store.put(meta, saveKey + \"__objimgmeta\");\n      await new Promise((res, rej) => { tx.oncomplete = res; tx.onerror = () => rej(tx.error); });\n      db.close();\n      imgs.forEach((im) => _dmbObjImgSaved.add(im.id));   // now safe to strip from localStorage\n      return true;\n    } catch (e) { return false; }\n  }\n\n  async function _dmbLoadObjImagesFromIDB(saveKey) {\n    const map = {};\n    try {\n      const db = await _openPhotoDB();\n      const tx = db.transaction(\"blobs\", \"readonly\");\n      const store = tx.objectStore(\"blobs\");\n      const meta = await new Promise((res) => { const r = store.get(saveKey + \"__objimgmeta\"); r.onsuccess = () => res(r.result || []); r.onerror = () => res([]); });\n      for (const m of (meta || [])) {\n        if (!m || !m.id) continue;\n        const rec = await new Promise((res) => { const r = store.get(saveKey + \"__objimg__\" + m.id); r.onsuccess = () => res(r.result || null); r.onerror = () => res(null); });\n        if (rec && rec.dataUrl) map[m.id] = { dataUrl: rec.dataUrl, field: rec.field || \"tightUrl\" };\n      }\n      db.close();\n    } catch (e) {}\n    return map;\n  }\n\n  // Reattach IDB-stored crop images onto the (freshly restored) live objects by id.\n  function _dmbReattachObjImages(imgMap) {\n    if (!imgMap) return;\n    const patch = (arr) => {\n      if (!Array.isArray(arr)) return;\n      arr.forEach((o) => {\n        if (!o || !o.id) return;\n        const rec = imgMap[o.id];\n        if (rec && rec.dataUrl) { try { o[rec.field || \"tightUrl\"] = rec.dataUrl; } catch (e) {} _dmbObjImgSaved.add(o.id); }\n      });\n    };\n    try { patch(editor.coverObjects); } catch (e) {}\n    try { if (editor.pageObjects) Object.keys(editor.pageObjects).forEach((k) => patch(editor.pageObjects[k])); } catch (e) {}\n  }\n\n  // Debounced mirror of crop images \u2192 IndexedDB. Images don't change after an\n  // object is added, so this rarely re-runs (guarded by a signature of the id set).\n  let _dmbObjImgTimer = null, _dmbObjImgSig = \"\";\n  function _dmbPersistObjImages() {\n    clearTimeout(_dmbObjImgTimer);\n    _dmbObjImgTimer = setTimeout(() => {\n      try {\n        const imgs = _dmbCollectObjImages();\n        if (!imgs.length) return;\n        const sig = imgs.map((i) => i.id).join(\",\");\n        if (sig === _dmbObjImgSig) return;\n        _dmbSaveObjImagesToIDB(getCustomerSaveKey(), imgs).then((ok) => { if (ok) _dmbObjImgSig = sig; });\n      } catch (e) {}\n    }, 350);\n  }\n\n  // Strip inline base64 crop images out of a snapshot CLONE before it's written to\n  // localStorage (they live in IndexedDB now). Only mutates the passed clone.\n  function _dmbStripObjImagesFromSnapshot(snap) {\n    try {\n      const strip = (arr) => {\n        if (!Array.isArray(arr)) return;\n        arr.forEach((o) => {\n          if (!o || !o.id) return;\n          if (!_dmbObjImgSaved.has(o.id)) return;   // keep inline until it's in IndexedDB\n          if (typeof o.tightUrl === \"string\" && o.tightUrl.slice(0, 5) === \"data:\") o.tightUrl = \"\";\n          if (typeof o.url === \"string\" && o.url.slice(0, 5) === \"data:\") o.url = \"\";\n        });\n      };\n      if (snap && snap.editor) {\n        strip(snap.editor.coverObjects);\n        if (snap.editor.pageObjects) Object.keys(snap.editor.pageObjects).forEach((k) => strip(snap.editor.pageObjects[k]));\n      }\n    } catch (e) {}\n    return snap;\n  }\n\n  // Re-key restored IDB photos to the IDs the saved layout actually references.\n  // Page slots / template frames / spread photos / cover frames all store photo\n  // IDs. If the IDB photos were written in a different session (or were re-id'd\n  // at some point), those references no longer resolve via getPhotoById() \u2014 so\n  // the tray (which renders the photo array directly) looks perfect, but every\n  // PLACED photo on a spread renders as a blank white box. Match each restored\n  // photo to a saved-state photo by addedIndex first, then filename, and adopt\n  // its saved ID so the layout resolves again. No-op when the IDs already line up.\n  function _dmbReconcilePhotoIds(idbPhotos, savedEditor) {\n    try {\n      const saved = (savedEditor && Array.isArray(savedEditor.photos)) ? savedEditor.photos : [];\n      if (!saved.length || !idbPhotos || !idbPhotos.length) return;\n      // Fast path: if every saved photo ID is already present, nothing to fix.\n      const idbIds = new Set(idbPhotos.map(p => p.id));\n      if (saved.every(sp => idbIds.has(sp.id))) return;\n      const usedSaved = new Set();\n      const pick = (pred) => {\n        for (let i = 0; i < saved.length; i++) {\n          if (!usedSaved.has(i) && pred(saved[i])) { usedSaved.add(i); return saved[i]; }\n        }\n        return null;\n      };\n      idbPhotos.forEach((np) => {\n        let m = pick(sp => sp.addedIndex === np.addedIndex && sp.filename === np.filename);\n        if (!m) m = pick(sp => sp.filename && sp.filename === np.filename);\n        if (!m) m = pick(sp => sp.addedIndex === np.addedIndex);\n        if (m && m.id) np.id = m.id;\n      });\n    } catch (e) {}\n  }\n\n  // On restore, the cover face/bleed image URLs are blob: URLs that get cleared\n  // (they're dead after a reload). The photo IDs are preserved though, so rebuild\n  // the URLs from the restored photos \u2014 otherwise a photo dropped directly on the\n  // cover front/back comes back blank even though it's still in the tray.\n  // Must run AFTER editor.photos has been loaded (getPhotoById needs it).\n  function _dmbRehydrateCoverPhotoUrls() {\n    try {\n      [[\"frontImageId\", \"frontImageUrl\"],\n       [\"backImageId\", \"backImageUrl\"],\n       [\"bleedImageId\", \"bleedImageUrl\"]].forEach(([idK, urlK]) => {\n        if ((!coverState[urlK] || !String(coverState[urlK]).length) && coverState[idK]) {\n          const p = getPhotoById(coverState[idK]);\n          if (p && p.url) coverState[urlK] = p.url;\n        }\n      });\n    } catch (e) {}\n  }\n\n  /* ===================================================================\n     Cloud design storage (server-side, for LOGGED-IN customers)\n     Mirrors the local save to a Cloudflare Worker (via the Shopify App\n     Proxy at /apps/designs) so designs survive browser eviction and work\n     across devices. Purely additive \u2014 local IndexedDB/localStorage still\n     runs; cloud is an extra layer that activates only when logged in.\n     =================================================================== */\n  const _dmbCloud = {\n    _uploaded: {},                                   // designId -> Set(photoId) already uploaded this session\n    enabled() { return !!(window.DMB_CUSTOMER_ID && window.DMB_DESIGNS_PROXY); },\n    base() { return window.DMB_DESIGNS_PROXY || \"/apps/designs\"; },\n    // Stable id for this design = the local save-key suffix (per template/cover handle).\n    designId() { return getCustomerSaveKey().replace(/^dmb_progress_/, \"\"); },\n    _f(path, opts) { return fetch(this.base() + path, Object.assign({ credentials: \"same-origin\" }, opts || {})); },\n\n    async saveState(snapshot, meta) {\n      if (!this.enabled()) return false;\n      try {\n        const id = this.designId();\n        const r = await this._f(`/save?id=${encodeURIComponent(id)}`, {\n          method: \"POST\", headers: { \"content-type\": \"application/json\" },\n          body: JSON.stringify({ design: snapshot, meta: meta || {} }),\n        });\n        return r.ok;\n      } catch (e) { return false; }\n    },\n\n    // PRINT MASTERS for SIGNED-IN customers. The only other place masters were\n    // queued is the guest instant-backup queue (_dmbPhotoBackup, guests only) -\n    // it is disabled the moment a customer is logged in, so a signed-in customer's\n    // photos NEVER got a master and every proof read \"0 of N at full print\n    // quality\" while the server rendered from the proxies. Mirrors the proxy\n    // upload below exactly: same design id, no sid, so both land under the same\n    // owner prefix. One-shot per File: the blob is freed once its upload settles.\n    queueMasters(photos) {\n      if (!this.enabled() || !Array.isArray(photos) || !photos.length) return 0;\n      let n = 0;\n      try {\n        if (!_dmbPrintMasterOn()) return 0;\n        const id = this.designId();\n        photos.forEach(function (ph) {\n          try {\n            if (ph && ph.id && ph.file && ph.file._dmbMaster && !ph.file._dmbMasterQueued) {\n              _dmbQueueMasterUpload(ph.file, id, \"\", ph.id); n++;\n            }\n          } catch (e) {}\n        });\n      } catch (e) {}\n      return n;\n    },\n\n    async savePhotosDelta(photos) {\n      if (!this.enabled() || !Array.isArray(photos) || !photos.length) return;\n      try { this.queueMasters(photos); } catch (e) {}\n      const id = this.designId();\n      let up = this._uploaded[id];\n      if (!up) {\n        up = new Set();\n        // Build the \"already uploaded\" set from what R2 ACTUALLY holds (/photos lists\n        // real objects) \u2014 NOT the design manifest (meta.photoIds), which can claim a\n        // photo is saved when its bytes never reached R2 (old book) or were deleted\n        // after an order. This makes the delta re-upload genuinely-missing photos, so\n        // a design that's complete on one device self-heals on the others.\n        let gotList = false;\n        try {\n          const lr = await this._f(`/photos?id=${encodeURIComponent(id)}`);\n          if (lr.ok) { gotList = true; const d = await lr.json(); ((d && d.photos) || []).forEach(p => { const pid = (p && (p.pid || p.id)); if (pid) up.add(pid); }); }\n        } catch (e) {}\n        // Only if the reality check FAILED (endpoint/network) do we trust the manifest,\n        // so a transient error never triggers a full re-upload of every photo.\n        if (!gotList) {\n          try {\n            const lr = await this._f(`/load?id=${encodeURIComponent(id)}`);\n            if (lr.ok) { const d = await lr.json(); ((d.meta && d.meta.photoIds) || []).forEach(x => up.add(x)); }\n          } catch (e) {}\n        }\n        this._uploaded[id] = up;\n      }\n      let uploaded = 0;\n      for (const ph of photos) {\n        if (up.has(ph.id)) continue;\n        let src = ph.file || null;\n        if ((!src || src.size === 0) && ph.url) { try { src = await (await fetch(ph.url)).blob(); } catch (e) {} }\n        if (!src || !src.size) continue;   // no local bytes here (e.g. the device that's MISSING them) \u2192 nothing we can push\n        try {\n          const r = await this._f(`/photo?id=${encodeURIComponent(id)}&pid=${encodeURIComponent(ph.id)}`, {\n            method: \"POST\", headers: { \"content-type\": src.type || \"image/jpeg\" }, body: src,\n          });\n          if (r.ok) { up.add(ph.id); uploaded++; }\n        } catch (e) {}\n      }\n      return uploaded;\n    },\n\n    // Force a one-off backfill: re-upload any photo we hold locally that R2 is missing,\n    // WITHOUT needing a design change. Fixes old books whose photos never fully reached\n    // R2 (so another device shows blank frames). No-op on a device that lacks the bytes.\n    async backfillPhotos() {\n      try { this._uploaded[this.designId()] = null; } catch (e) {}   // re-check R2 reality\n      let n = 0;\n      try { n = await this.savePhotosDelta(editor.photos || []); } catch (e) {}\n      // If we actually re-uploaded missing photos, the cloud copy was incomplete/old \u2192\n      // push the full current design too (carries the colour palette + completes it).\n      if (n > 0) { try { _dmbCloudPersist(); } catch (e) {} }\n      return n;\n    },\n\n    // Returns { design, meta, photos:[{id,file,url,...}] } or null.\n    async load(id) {\n      if (!this.enabled()) return null;\n      id = id || this.designId();\n      try {\n        const r = await this._f(`/load?id=${encodeURIComponent(id)}`);\n        if (!r.ok) return null;\n        const data = await r.json();\n        const manifest = (data.meta && data.meta.photos) || [];\n        const metaById = new Map();\n        manifest.forEach(m => { if (m && m.id) metaById.set(m.id, m); });\n        // Download ONLY the photos the design actually PLACES (plus whatever the manifest\n        // lists) \u2014 NOT the customer's whole uploaded library. R2 can hold dozens of\n        // never-placed photos; downloading them all made the restore time out and show\n        // NOTHING. The placed set is small, so the book loads fast and complete.\n        let realPids = [];\n        try {\n          const lr = await this._f(`/photos?id=${encodeURIComponent(id)}`);\n          if (lr.ok) { const d = await lr.json(); realPids = ((d && d.photos) || []).map(p => p && (p.pid || p.id)).filter(Boolean); }\n        } catch (e) {}\n        const realSet = new Set(realPids);\n        const idsToFetch = new Set();\n        // Download ONLY the photos the design PLACES (what the book needs to render) \u2014\n        // NOT the whole manifest. A manifest bloated by churn (e.g. 56, incl. duplicates\n        // and another book's photos) made the restore time out and show NOTHING on\n        // slower devices. The placed set is tiny, so the book renders fast & reliably.\n        _dmbRefsFromDesign(data.design).forEach(pid => { if (!realPids.length || realSet.has(pid)) idsToFetch.add(pid); });\n        // Download photos in PARALLEL (a small worker pool) \u2014 sequential one-at-a-time\n        // downloads made a 21-photo restore blow past the caller's timeout and return\n        // NOTHING (losing even the cover/layout). Parallel keeps it well under the cap.\n        const photos = [];\n        const _idArr = Array.from(idsToFetch);\n        const _self = this;\n        let _idx = 0;\n        const _worker = async () => {\n          while (_idx < _idArr.length) {\n            const pid = _idArr[_idx++];\n            try {\n              const pr = await _self._f(`/photo?id=${encodeURIComponent(id)}&pid=${encodeURIComponent(pid)}`);\n              if (!pr.ok) continue;\n              const blob = await pr.blob();\n              if (!blob || !blob.size) continue;\n              const mp = metaById.get(pid) || { id: pid };\n              let f = null;\n              try { f = new File([blob], mp.filename || \"photo.jpg\", { type: blob.type || \"image/jpeg\" }); } catch (e) { f = blob; }\n              photos.push(Object.assign({}, mp, { id: pid, file: f, url: URL.createObjectURL(blob) }));\n            } catch (e) {}\n          }\n        };\n        await Promise.all(Array.from({ length: Math.min(6, _idArr.length || 1) }, _worker));\n        // Remember what R2 ACTUALLY has (reality), so a later save re-uploads only the\n        // genuinely-missing photos \u2014 not the (possibly lying) manifest.\n        this._uploaded[id] = new Set(realPids.length ? realPids : manifest.map(m => m.id));\n        // Stash the REST of the book's photo library (its manifest, minus the placed\n        // photos we already fetched) so the caller can pull it into the tray in the\n        // BACKGROUND, after the book has rendered. Only ids R2 actually has.\n        this._pendingLib = {\n          id,\n          entries: manifest.filter(m => m && m.id && !idsToFetch.has(m.id) && (!realPids.length || realSet.has(m.id))),\n        };\n        return { design: data.design, meta: data.meta, photos };\n      } catch (e) { return null; }\n    },\n\n    // Pull the stashed rest-of-library into the tray in the BACKGROUND (parallel,\n    // non-blocking). Safe: loading never triggers a cloud write (edit-gated).\n    async loadPendingLib(onBatch) {\n      const pl = this._pendingLib; this._pendingLib = null;\n      if (!pl || !Array.isArray(pl.entries) || !pl.entries.length) return 0;\n      const id = pl.id, self = this; let i = 0, added = 0;\n      const worker = async () => {\n        while (i < pl.entries.length) {\n          const mp = pl.entries[i++];\n          try {\n            const pr = await self._f(`/photo?id=${encodeURIComponent(id)}&pid=${encodeURIComponent(mp.id)}`);\n            if (!pr.ok) continue;\n            const blob = await pr.blob(); if (!blob || !blob.size) continue;\n            let f = null; try { f = new File([blob], mp.filename || \"photo.jpg\", { type: blob.type || \"image/jpeg\" }); } catch (e) { f = blob; }\n            if (!editor.photos) editor.photos = [];\n            if (!editor.photos.some(p => p.id === mp.id)) { editor.photos.push(Object.assign({}, mp, { file: f, url: URL.createObjectURL(blob) })); added++; if (added % 4 === 0) { try { onBatch && onBatch(); } catch (e) {} } }\n          } catch (e) {}\n        }\n      };\n      await Promise.all(Array.from({ length: 6 }, worker));\n      try { onBatch && onBatch(); } catch (e) {}\n      return added;\n    },\n\n    // Background: pull EVERY remaining photo R2 holds for this design into the tray,\n    // AFTER the book itself has rendered. Recovers spare/tray photos whose ids no longer\n    // match the saved manifest (churn), without ever blocking the initial load. Parallel,\n    // best-effort; calls onBatch() as photos arrive so the tray can refresh live.\n    async loadTrayLibrary(onBatch) {\n      if (!this.enabled()) return 0;\n      const id = this.designId();\n      let realPids = [];\n      try { const lr = await this._f(`/photos?id=${encodeURIComponent(id)}`); if (lr.ok) { const d = await lr.json(); realPids = ((d && d.photos) || []).map(p => p && (p.pid || p.id)).filter(Boolean); } } catch (e) {}\n      const have = new Set((editor.photos || []).map(p => p.id));\n      const missing = realPids.filter(pid => !have.has(pid));\n      if (!missing.length) return 0;\n      const self = this; let i = 0, added = 0;\n      const worker = async () => {\n        while (i < missing.length) {\n          const pid = missing[i++];\n          try {\n            const pr = await self._f(`/photo?id=${encodeURIComponent(id)}&pid=${encodeURIComponent(pid)}`);\n            if (!pr.ok) continue;\n            const blob = await pr.blob(); if (!blob || !blob.size) continue;\n            let f = null; try { f = new File([blob], pid + \".jpg\", { type: blob.type || \"image/jpeg\" }); } catch (e) { f = blob; }\n            if (!editor.photos) editor.photos = [];\n            if (!editor.photos.some(p => p.id === pid)) { editor.photos.push({ id: pid, file: f, url: URL.createObjectURL(blob) }); added++; if (added % 4 === 0) { try { onBatch && onBatch(); } catch (e) {} } }\n          } catch (e) {}\n        }\n      };\n      await Promise.all(Array.from({ length: 6 }, worker));\n      try { onBatch && onBatch(); } catch (e) {}\n      return added;\n    },\n\n    async list() {\n      if (!this.enabled()) return [];\n      try { const r = await this._f(\"/list\"); if (r.ok) return (await r.json()).designs || []; } catch (e) {}\n      return [];\n    },\n\n    // Claim a guest's provisional R2 photos into THIS account (server-side copy \u2014\n    // fast + reliable, doesn't depend on IndexedDB or a slow foreground re-upload).\n    // Called right after a guest signs in. Returns { ok, claimed, pids } or null.\n    async claim(designId) {\n      if (!this.enabled()) return null;\n      const id = designId || this.designId();\n      let sid = \"\";\n      try { sid = _dmbPhotoBackup.guestId(); } catch (e) {}\n      if (!sid) return null;\n      try {\n        const r = await this._f(`/claim?id=${encodeURIComponent(id)}&sid=${encodeURIComponent(sid)}`, { method: \"POST\" });\n        if (!r.ok) return null;\n        return await r.json();\n      } catch (e) { return null; }\n    },\n  };\n\n  // A stable per-BROWSER id, so we can tell \"another device saved\" from \"we saved\".\n  function _dmbDeviceId() {\n    try {\n      let d = localStorage.getItem(\"dmb_device_id\");\n      if (!d) { d = (Date.now().toString(36) + Math.random().toString(36).slice(2, 10)); localStorage.setItem(\"dmb_device_id\", d); }\n      return d;\n    } catch (e) { return \"dev\"; }\n  }\n  // Cross-device sync bookkeeping (the version of the design THIS device currently holds).\n  let _dmbCurRev = null, _dmbCurSavedAt = null, _dmbLastSavedSig = null;\n  // Content signature: identical design+photo-set \u2192 identical string. Used as `rev`,\n  // so a re-save of UNCHANGED content produces the same rev (no false \"newer\" \u2192 no\n  // cross-device reload ping-pong). savedAt (time) only orders genuinely different content.\n  function _dmbCheapSig(snapshot, ph) {\n    let s = \"\";\n    try { s = JSON.stringify(snapshot); } catch (e) { s = \"\" + Date.now(); }\n    try { s += \"|\" + (ph || []).map(p => p.id).join(\",\"); } catch (e) {}\n    let h = 0; for (let i = 0; i < s.length; i++) h = (Math.imul(31, h) + s.charCodeAt(i)) | 0;\n    return (h >>> 0).toString(36) + \"-\" + s.length.toString(36);\n  }\n\n  // Debounced cloud mirror of the current design (state + photo deltas).\n  let _dmbCloudTimer = null;\n  function _dmbBuildCloudMeta(snapshot, ph) {\n    return {\n      title: (typeof getEffectiveBookTitle === \"function\" ? getEffectiveBookTitle() : (snapshot.book_title || \"\")),\n      pages: snapshot.pages_selected || editor.totalPages || 0,\n      _saveType: window.DMB_CARD_MODE ? \"card\" : window.DMB_MAGAZINE_MODE ? \"magazine\" : \"book\",\n      _coverHandle: (new URLSearchParams(location.search).get(\"cover\") || getTemplateKey() || \"\"),\n      photos: ph.map(p => ({ id: p.id, filename: p.filename, ratio: p.ratio, dateTaken: p.dateTaken, tags: p.tags, addedIndex: p.addedIndex })),\n      photoIds: ph.map(p => p.id),\n      // Cross-device version stamp \u2014 /list already returns meta fields, so the other\n      // device sees a newer save without any Worker change. rev = content hash.\n      savedAt: new Date().toISOString(),\n      rev: _dmbCheapSig(snapshot, ph),\n      device: _dmbDeviceId(),\n    };\n  }\n  // \u2500\u2500 Background R2 mirror of the FULL design \u2014 for EVERYONE (signed-in AND\n  //    guests). Local (localStorage + IndexedDB) already saves instantly (Phase 1);\n  //    this is the durable OFF-DEVICE copy so a lost wifi / closed tab / cleared\n  //    storage / new device keeps the work. Debounced, and retried automatically\n  //    when the connection returns (online / tab-visible listeners below). Photos\n  //    upload as separate full-res binaries; the sticker/icon crop base64 stays\n  //    INLINE in this JSON (R2 has no 5 MB limit) so the R2 copy is complete on its\n  //    own. Guest copies live under designs/guest_<sid>/ and are promoted to the\n  //    account by /claim on sign-in, or cleaned by /delete-guest + the R2 lifecycle.\n  let _dmbR2Dirty = false, _dmbR2Busy = false, _dmbDesignFinalized = false;\n  // The cloud copy is only written after a GENUINE user edit (set in pushUndoState) \u2014\n  // never from merely opening/restoring a book. This is what finally stops a device\n  // that loaded a partial/odd copy from silently re-saving it over the good cloud data.\n  let _dmbUserEdited = false;\n  function _dmbCloudPersist() {\n    if (_dmbRenderMode()) return;              // render mode: never touches the cloud copy\n    if (_dmbDesignFinalized) return;   // added to cart \u2192 don't re-create the R2 copy\n    if (!window.DMB_DESIGNS_PROXY || !editor || !editor.totalPages) return;\n    _dmbR2Dirty = true;\n    clearTimeout(_dmbCloudTimer);\n    _dmbCloudTimer = setTimeout(_dmbR2Flush, 1800);\n  }\n  async function _dmbR2Flush() {\n    if (_dmbRenderMode()) return;              // render mode: read-only on R2 (staged pages aside)\n    if (_dmbDesignFinalized) return;\n    if (_dmbR2Busy || !_dmbR2Dirty) return;\n    if (!window.DMB_DESIGNS_PROXY || !editor || !editor.totalPages) return;\n    if (navigator.onLine === false) return;                 // offline \u2192 wait for 'online'\n    // NEVER persist a BLANK design \u2014 a transient empty state during load/openEditor\n    // must never overwrite good cloud data with nothing. Require at least one photo,\n    // one placed frame, or a cover element.\n    try {\n      const _refs = _dmbDesignPhotoRefs();\n      const _blank = !((editor.photos || []).length) && _refs.size === 0 && !((editor.coverObjects || []).length) && !((editor.coverUserTexts || []).length);\n      if (_blank) { _dmbR2Dirty = false; return; }\n    } catch (e) {}\n    // Only write to the cloud after a genuine user edit \u2014 never from just opening/\n    // restoring a book (that's how a device with a partial copy re-saved it over good\n    // data). Local storage still saves; only the shared cloud copy is held back.\n    if (!_dmbUserEdited) { _dmbR2Dirty = false; return; }\n    _dmbR2Busy = true;\n    _dmbR2Dirty = false;                                     // optimistic; re-flag on failure\n    try {\n      const snapshot = getCurrentBuilderStateSnapshot();\n      try { if (snapshot.editor && Array.isArray(snapshot.editor.photos)) snapshot.editor.photos = []; } catch (e) {}\n      const meta = _dmbBuildCloudMeta(snapshot, editor.photos || []);\n      // Content unchanged since the last cloud write? Skip it \u2014 saves bandwidth AND\n      // prevents a redundant version bump. (rev is a content hash.)\n      if (meta.rev === _dmbLastSavedSig) { _dmbCurRev = meta.rev; _dmbR2Busy = false; return; }\n      let ok = false;\n      if (window.DMB_CUSTOMER_ID && _dmbCloud.enabled()) {\n        // (Anti-truncation photo-count guard REMOVED 2026-08-28: with auto-sync OFF the\n        // churn source is gone, and the guard was LOCKING a bloated record \u2014 it stopped\n        // the user cleaning a wrongly-inflated tray back down. The blank-save guard above\n        // still prevents the catastrophic empty overwrite.)\n        ok = await _dmbCloud.saveState(snapshot, meta);      // signed-in \u2192 /save\n        if (ok) { _dmbLastSavedSig = meta.rev; _dmbCurRev = meta.rev; _dmbCurSavedAt = meta.savedAt; }   // our own latest \u2014 don't pull it back\n        try { await _dmbCloud.savePhotosDelta(editor.photos || []); } catch (e) {}\n      } else {\n        let sid = \"\"; try { sid = _dmbPhotoBackup.guestId(); } catch (e) {}\n        const gid = getCustomerSaveKey().replace(/^dmb_progress_/, \"\");\n        if (sid && gid) {\n          const r = await fetch(window.DMB_DESIGNS_PROXY + \"/save-guest?id=\" + encodeURIComponent(gid) + \"&sid=\" + encodeURIComponent(sid), {\n            method: \"POST\", credentials: \"same-origin\",\n            headers: { \"content-type\": \"application/json\" },\n            body: JSON.stringify({ design: snapshot, meta: meta }),\n          });\n          ok = !!(r && r.ok);\n          try { if (typeof _dmbBackupPhotos === \"function\") _dmbBackupPhotos(); } catch (e) {}  // guest photos \u2192 R2\n        }\n      }\n      if (!ok) _dmbR2Dirty = true;\n    } catch (e) { _dmbR2Dirty = true; }\n    finally {\n      _dmbR2Busy = false;\n      if (_dmbR2Dirty) { clearTimeout(_dmbCloudTimer); _dmbCloudTimer = setTimeout(_dmbR2Flush, 8000); }  // retry\n    }\n  }\n  (function _dmbWireR2Retry() {\n    try {\n      window.addEventListener(\"online\", function () { if (_dmbR2Dirty) _dmbR2Flush(); });\n      document.addEventListener(\"visibilitychange\", function () { if (document.visibilityState === \"visible\" && _dmbR2Dirty) _dmbR2Flush(); });\n    } catch (e) {}\n  })();\n  // Immediate (non-debounced) cloud manifest flush \u2014 used when LEAVING so the\n  // account manifest (meta.photos) is never stale vs the design. The photo BYTES\n  // are uploaded separately (instant backup + savePhotosDelta) and stay on R2, so\n  // this only makes the photo LIST current \u2014 cheap. keepalive:true lets it survive\n  // a tab-close/unload. Returns the fetch promise (awaitable for in-app nav), else null.\n  function _dmbCloudFlushNow(keepalive) {\n    if (_dmbRenderMode()) return null;         // render mode: no keepalive save on unload\n    try {\n      if (_dmbDesignFinalized) return null;   // added to cart \u2192 don't re-create the R2 copy\n      if (!window.DMB_DESIGNS_PROXY || !editor || !editor.totalPages) return null;\n      const snapshot = getCurrentBuilderStateSnapshot();\n      try { if (snapshot.editor && Array.isArray(snapshot.editor.photos)) snapshot.editor.photos = []; } catch (e) {}\n      const ph = editor.photos || [];\n      const meta = _dmbBuildCloudMeta(snapshot, ph);\n      if (window.DMB_CUSTOMER_ID && _dmbCloud.enabled()) {\n        return fetch(_dmbCloud.base() + \"/save?id=\" + encodeURIComponent(_dmbCloud.designId()), {\n          method: \"POST\", headers: { \"content-type\": \"application/json\" },\n          body: JSON.stringify({ design: snapshot, meta: meta }),\n          credentials: \"same-origin\", keepalive: !!keepalive,\n        });\n      }\n      // Guest keepalive flush \u2192 guest R2 backup, so a last-second edit before the\n      // tab closes still lands off-device (the debounced sync may not have fired).\n      let sid = \"\"; try { sid = _dmbPhotoBackup.guestId(); } catch (e) {}\n      const gid = getCustomerSaveKey().replace(/^dmb_progress_/, \"\");\n      if (sid && gid) {\n        return fetch(window.DMB_DESIGNS_PROXY + \"/save-guest?id=\" + encodeURIComponent(gid) + \"&sid=\" + encodeURIComponent(sid), {\n          method: \"POST\", headers: { \"content-type\": \"application/json\" },\n          body: JSON.stringify({ design: snapshot, meta: meta }),\n          credentials: \"same-origin\", keepalive: !!keepalive,\n        });\n      }\n      return null;\n    } catch (e) { return null; }\n  }\n\n  // \u2500\u2500 Option A \u00B7 Step 2: guest photo backup to R2 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // Streams each photo's bytes to R2 so they survive IndexedDB eviction (the iOS\n  // data-loss case). Logged-in customers are already covered by _dmbCloud above;\n  // this covers GUESTS, keyed by a persistent per-browser id (dmb_guest_id). The\n  // upload is cheap on memory (fetch streams the File) and runs one photo at a time.\n  const _dmbPhotoBackup = {\n    _uploaded: {},                                   // designId -> Set(pid)\n    base() { return window.DMB_DESIGNS_PROXY || \"\"; },\n    enabled() { return !!window.DMB_DESIGNS_PROXY && !window.DMB_CUSTOMER_ID; },  // guests only\n    designId() { try { return getCustomerSaveKey().replace(/^dmb_progress_/, \"\"); } catch (e) { return \"default\"; } },\n    guestId() {\n      let g = null;\n      try {\n        g = localStorage.getItem(\"dmb_guest_id\");\n        if (!g) { g = \"g\" + Date.now().toString(36) + Math.random().toString(36).slice(2, 8); localStorage.setItem(\"dmb_guest_id\", g); }\n      } catch (e) {}\n      return g;\n    },\n    _q() { const g = this.guestId(); return g ? (\"&sid=\" + encodeURIComponent(g)) : \"\"; },\n    async uploadAll(photos) {\n      if (!this.enabled() || !this.guestId() || !Array.isArray(photos) || !photos.length) return;\n      const id = this.designId();\n      let up = this._uploaded[id]; if (!up) { up = new Set(); this._uploaded[id] = up; }\n      for (const ph of photos) {\n        if (!ph || !ph.id || up.has(ph.id)) continue;\n        let src = ph.file;\n        if ((!src || !src.size) && ph.url) { try { src = await (await fetch(ph.url)).blob(); } catch (e) {} }\n        if (!src || !src.size) continue;\n        try {\n          const r = await fetch(this.base() + \"/photo?id=\" + encodeURIComponent(id) + \"&pid=\" + encodeURIComponent(ph.id) + this._q(),\n            { method: \"POST\", headers: { \"content-type\": src.type || \"image/jpeg\" }, body: src, credentials: \"same-origin\" });\n          if (r.ok) up.add(ph.id);\n        } catch (e) {}\n        await new Promise((r) => setTimeout(r, 0));   // yield so memory is reclaimed between photos\n      }\n    },\n    // Restore fallback: pull a single photo's bytes back from R2 when the local copy is gone.\n    async fetchBlob(designId, pid) {\n      if (!window.DMB_DESIGNS_PROXY || !this.guestId()) return null;\n      try {\n        const r = await fetch(this.base() + \"/photo?id=\" + encodeURIComponent(designId) + \"&pid=\" + encodeURIComponent(pid) + this._q(), { credentials: \"same-origin\" });\n        if (!r.ok) return null;\n        const b = await r.blob();\n        return b && b.size ? b : null;\n      } catch (e) { return null; }\n    },\n  };\n\n  let _dmbBackupTimer = null;\n  function _dmbBackupPhotos() {\n    if (_dmbRenderMode()) return;              // render mode: never re-uploads photos\n    if (!_dmbPhotoBackup.enabled()) return;\n    clearTimeout(_dmbBackupTimer);\n    _dmbBackupTimer = setTimeout(() => {\n      try { _dmbPhotoBackup.uploadAll(editor.photos || []); } catch (e) {}\n    }, 2500);\n  }\n\n  // \u2500\u2500 INSTANT photo backup (upload step) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // The June backup (_dmbPhotoBackup/_dmbCloud) only kicks in AFTER the customer\n  // enters the builder \u2014 a phone-tab crash during the UPLOAD STEP lost every\n  // picked photo and bounced them to the landing page. This module streams each\n  // photo to R2 the moment it is picked (serial queue, one at a time), keeps a\n  // localStorage manifest, and offers a \"your photos are safe\" restore after a\n  // crash. Works for guests (guest_<sid>) AND signed-in customers (the Worker's\n  // verifiedIdentity prefers the customer id when logged in). Files upload\n  // byte-for-byte \u2014 no recompression, no quality change.\n  const _dmbInstantBackup = {\n    _queue: [], _inFlight: 0, _failed: [], _done: 0, _total: 0, _badgeT: null,\n    enabled() { return !!window.DMB_DESIGNS_PROXY; },\n    designId() { try { return getCustomerSaveKey().replace(/^dmb_progress_/, \"\"); } catch (e) { return \"default\"; } },\n    pending() { return this._queue.length + this._inFlight; },\n    _mKey() { return \"dmb_upload_backup_\" + this.designId(); },\n    _manifest() { try { return JSON.parse(localStorage.getItem(this._mKey()) || \"null\") || { photos: [] }; } catch (e) { return { photos: [] }; } },\n    _saveManifest(m) { try { localStorage.setItem(this._mKey(), JSON.stringify(m)); } catch (e) {} },\n    clearManifest() { try { localStorage.removeItem(this._mKey()); } catch (e) {} },\n    removeFromManifest(pid) {\n      const m = this._manifest();\n      m.photos = (m.photos || []).filter((p) => p.id !== pid);\n      this._saveManifest(m);\n    },\n    // Tell the later builder-stage backups this pid is already on R2 so they skip it.\n    _markUploaded(pid) {\n      const id = this.designId();\n      try { let s1 = _dmbPhotoBackup._uploaded[id]; if (!s1) { s1 = new Set(); _dmbPhotoBackup._uploaded[id] = s1; } s1.add(pid); } catch (e) {}\n      try { let s2 = _dmbCloud._uploaded[id]; if (!s2) { s2 = new Set(); _dmbCloud._uploaded[id] = s2; } s2.add(pid); } catch (e) {}\n      // Mark the in-memory photo as safely on R2 so Level-2 light-memory mode\n      // (window.DMB_LIGHT_PHOTO_MEM) knows it can release its full-res bytes.\n      try { const _pp = (editor.photos || []).find((p) => p && p.id === pid); if (_pp) _pp._r2 = true; } catch (e) {}\n    },\n    enqueue(file) {\n      if (!this.enabled() || !file || !file.size || file._dmbPid) return;\n      file._dmbPid = uid();\n      this._queue.push(file);\n      this._total++;\n      this._badge();\n      this._drain();\n    },\n    retryFailed() {\n      if (!this._failed.length) return;\n      this._queue.push.apply(this._queue, this._failed.splice(0));\n      this._badge();\n      this._drain();\n    },\n    async _uploadOne(f, id, sid) {\n      let ok = false;\n      for (let a = 0; a < 2 && !ok; a++) {           // 2 attempts per photo\n        try {\n          const r = await fetch(\n            (window.DMB_DESIGNS_PROXY || \"\") + \"/photo?id=\" + encodeURIComponent(id) +\n            \"&pid=\" + encodeURIComponent(f._dmbPid) + (sid ? \"&sid=\" + encodeURIComponent(sid) : \"\"),\n            { method: \"POST\", headers: { \"content-type\": f.type || \"image/jpeg\" }, body: f, credentials: \"same-origin\" }\n          );\n          ok = r.ok;\n        } catch (e) {}\n        if (!ok && a === 0) await new Promise((r) => setTimeout(r, 900));\n      }\n      if (ok) {\n        this._done++;\n        this._markUploaded(f._dmbPid);\n        const m = this._manifest();\n        if (!(m.photos || []).some((p) => p.id === f._dmbPid)) {\n          m.photos.push({ id: f._dmbPid, filename: f.name || \"photo.jpg\", type: f.type || \"image/jpeg\" });\n        }\n        m.sid = sid; m.ts = Date.now();\n        this._saveManifest(m);\n        // Print master (flag ON): once the PROXY is safely at photos/{pid}, push the\n        // ~3500px master to photos-orig/{pid} on the BACKGROUND chain. It's outside\n        // this queue on purpose: the import modal waits on proxies only, and the\n        // master blob is freed as soon as its upload settles.\n        try { if (_dmbPrintMasterOn() && f._dmbMaster) _dmbQueueMasterUpload(f, id, sid); } catch (e) {}\n      } else {\n        this._failed.push(f);\n      }\n      this._badge();\n    },\n    async _drain() {\n      if (this._draining) return;\n      this._draining = true;\n      const id = this.designId();\n      const sid = _dmbPhotoBackup.guestId();\n      // Parallel uploads: fetch() STREAMS the File (no decode, low memory), so\n      // concurrency is cheap. 6-wide + no inter-upload delay pushes photos to R2 as\n      // fast as the connection allows (bandwidth-bound, not client-bound). Files stay\n      // full-resolution \u2014 byte-for-byte, no quality loss.\n      const self = this;\n      async function worker() {\n        while (self._queue.length) {\n          const f = self._queue.shift();\n          if (!f) break;\n          self._inFlight++;\n          try { await self._uploadOne(f, id, sid); } catch (e) { try { self._failed.push(f); } catch (e2) {} }\n          self._inFlight--;\n        }\n      }\n      const n = Math.min(6, Math.max(1, this._queue.length));\n      const workers = []; for (let w = 0; w < n; w++) workers.push(worker());\n      await Promise.all(workers);\n      this._draining = false;\n      this._badge();\n      // A file enqueued in the tiny window while workers were exiting would sit\n      // unprocessed until the next pick \u2014 catch it.\n      if (this._queue.length) this._drain();\n    },\n    // Badge removed \u2014 photo backup runs silently in the background (no visible pill).\n    _badge() {\n      var b = document.getElementById(\"dmbInstantBackupBadge\");\n      if (b) { try { b.remove(); } catch (e) {} }\n    },\n    // Full-screen wait used by the \"Do the Magic\"/\"Customize\" gate: don't leave\n    // the upload step while photos are still being secured.\n    async waitWithOverlay() {\n      let ov = document.getElementById(\"dmbBackupWaitOverlay\");\n      if (!ov) {\n        ov = document.createElement(\"div\");\n        ov.id = \"dmbBackupWaitOverlay\";\n        ov.style.cssText = \"position:fixed;inset:0;z-index:1000003;display:flex;flex-direction:column;align-items:center;justify-content:center;background:rgba(250,247,242,0.97);gap:8px;\";\n        try { ov.appendChild(_dmbPvMakeLoadingNode()); } catch (e) {}\n        const t = document.createElement(\"div\");\n        t.id = \"dmbBackupWaitMsg\";\n        t.style.cssText = \"font-size:15px;font-weight:800;color:#1e1e1e;\";\n        ov.appendChild(t);\n        document.body.appendChild(ov);\n      }\n      const msg = ov.querySelector(\"#dmbBackupWaitMsg\");\n      while (this.pending() > 0) {\n        if (msg) msg.textContent = \"\u2601\uFE0F Securing your photos\u2026 \" + this.pending() + \" left\";\n        await new Promise((r) => setTimeout(r, 250));\n      }\n      try { ov.remove(); } catch (e) {}\n    },\n    // Crash recovery: a previous upload session streamed photos to R2 \u2014 offer to\n    // bring them back instead of making the customer re-pick everything.\n    async offerRestore() {\n      if (!this.enabled() || bookFiles.length) return;\n      const m = this._manifest();\n      if (!m || !Array.isArray(m.photos) || !m.photos.length) return;\n      if (document.getElementById(\"dmbUploadRestoreModal\")) return;\n      const n = m.photos.length;\n      const self = this;\n      const ov = document.createElement(\"div\");\n      ov.id = \"dmbUploadRestoreModal\";\n      ov.style.cssText = \"position:fixed;inset:0;z-index:1000000;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,0.45);padding:20px;\";\n      const card = document.createElement(\"div\");\n      card.style.cssText = \"background:#fff;border-radius:18px;max-width:380px;width:100%;padding:28px 24px 22px;box-shadow:0 24px 70px rgba(0,0,0,.4);text-align:center;\";\n      card.innerHTML =\n        '<div style=\"font-size:36px;margin-bottom:8px;\">\uD83D\uDCF8</div>' +\n        '<h3 style=\"margin:0 0 8px;font-size:20px;font-weight:900;color:#1e1e1e;\">Your photos are safe!</h3>' +\n        '<p style=\"margin:0 0 20px;font-size:14px;line-height:1.55;color:#555;\">' + n + ' photo' + (n !== 1 ? 's' : '') + ' from your last visit ' + (n !== 1 ? 'are' : 'is') + ' saved securely \u2014 bring ' + (n !== 1 ? 'them' : 'it') + ' back?</p>';\n      const yes = document.createElement(\"button\");\n      yes.textContent = \"Restore my photos\";\n      yes.style.cssText = \"display:block;width:100%;background:#6d3bd4;color:#fff;border:none;border-radius:10px;padding:14px;cursor:pointer;font-weight:900;font-size:15px;margin-bottom:10px;\";\n      const no = document.createElement(\"button\");\n      no.textContent = \"Start fresh\";\n      no.style.cssText = \"display:block;width:100%;background:#f1ebe0;color:#555;border:none;border-radius:10px;padding:12px;cursor:pointer;font-weight:800;font-size:14px;\";\n      card.appendChild(yes); card.appendChild(no);\n      ov.appendChild(card); document.body.appendChild(ov);\n      no.addEventListener(\"click\", () => { ov.remove(); self.clearManifest(); });\n      yes.addEventListener(\"click\", async () => {\n        ov.remove();\n        try { _dmbShowRestoreLoader(); } catch (e) {}\n        const did = self.designId();\n        const files = [];\n        for (const p of m.photos) {\n          try {\n            const b = await _dmbPhotoBackup.fetchBlob(did, p.id);\n            if (!b) continue;\n            const f = new File([b], p.filename || \"photo.jpg\", { type: p.type || b.type || \"image/jpeg\" });\n            f._dmbPid = p.id;          // already on R2 \u2014 enqueue() skips it\n            self._markUploaded(p.id);\n            files.push(f);\n          } catch (e) {}\n        }\n        if (files.length) {\n          bookFiles = mergeFilesKeepOld(bookFiles, files);\n          try { validatePhotos(); } catch (e) {}\n          try { renderUploadGrid(); } catch (e) {}\n          try { refreshBuilderPhotos(); } catch (e) {}\n        }\n        try { _dmbHideRestoreLoader(); } catch (e) {}\n        if (!files.length) alert(\"We couldn't reach your saved photos right now \u2014 please check your connection and refresh, or add them again.\");\n      });\n    },\n\n    // Silent restore (NO popup) \u2014 used when a GUEST refreshes the upload step, so an\n    // accidental refresh doesn't lose their just-picked photos. Same download+inject\n    // as the \"yes\" branch above, minus the offer dialog. Returns true if it restored.\n    async restoreSilently() {\n      if (!this.enabled() || bookFiles.length) return false;\n      const m = this._manifest();\n      if (!m || !Array.isArray(m.photos) || !m.photos.length) return false;\n      const self = this;\n      const did = self.designId();\n      try { _dmbShowRestoreLoader(); } catch (e) {}\n      const files = [];\n      for (const p of m.photos) {\n        try {\n          const b = await _dmbPhotoBackup.fetchBlob(did, p.id);\n          if (!b) continue;\n          const f = new File([b], p.filename || \"photo.jpg\", { type: p.type || b.type || \"image/jpeg\" });\n          f._dmbPid = p.id;\n          self._markUploaded(p.id);\n          files.push(f);\n        } catch (e) {}\n      }\n      if (files.length) {\n        bookFiles = mergeFilesKeepOld(bookFiles, files);\n        try { validatePhotos(); } catch (e) {}\n        try { renderUploadGrid(); } catch (e) {}\n        try { refreshBuilderPhotos(); } catch (e) {}\n      }\n      try { _dmbHideRestoreLoader(); } catch (e) {}\n      return files.length > 0;\n    },\n  };\n\n  // Saved designs folder (theme page) \u2014 single source of truth for the link.\n  var DMB_SAVED_DESIGNS_URL = \"/pages/my-saved-designs\";\n\n  // Clean builder path used as the post-login return target. We deliberately keep\n  // it MINIMAL (just ?restore=1) \u2014 Shopify's new customer accounts validate the\n  // return_url and silently fall back to /account when it's a complex URL (extra\n  // params like step=\u2026). A clean same-origin path is far more likely to be honored,\n  // so the customer actually lands back on the builder instead of the account page.\n  function _dmbBuilderRestorePath() {\n    var base = window.DMB_CARD_MODE ? \"/pages/design-my-book?type=card&restore=1\"\n      : (window.DMB_MAGAZINE_MODE ? \"/pages/design-my-magazine\" : \"/pages/design-my-book\") + \"?restore=1\";\n    // CRITICAL: carry the design's identity (cover/template/design) through the login\n    // round-trip. getCustomerSaveKey() derives the save key from these params \u2014 drop\n    // them and the key CHANGES after login, so the restore can't find the guest's\n    // saved layout + photos and they appear lost. (Custom books are stable via a\n    // stored id; template/cover designs MUST keep their param.)\n    try {\n      var sp = new URLSearchParams(location.search);\n      [\"cover\", \"template\", \"design\"].forEach(function (k) {\n        var v = sp.get(k);\n        if (v) base += \"&\" + k + \"=\" + encodeURIComponent(v);\n      });\n    } catch (e) {}\n    return base;\n  }\n  function _dmbLoginUrlBackToBuilder() {\n    var u = window.DMB_LOGIN_URL || \"/account/login\";\n    try {\n      u += (u.indexOf(\"?\") >= 0 ? \"&\" : \"?\") + \"return_url=\" + encodeURIComponent(_dmbBuilderRestorePath());\n    } catch (e) {}\n    return u;\n  }\n\n  // Login URL that returns to THIS page (the upload step) \u2014 used by the mandatory\n  // upload-page sign-in gate so a guest lands back on the same upload page (now\n  // signed in) and can proceed. (The builder variant above returns to ?step=2.)\n  function _dmbLoginUrlBackToUpload() {\n    var u = window.DMB_LOGIN_URL || \"/account/login\";\n    try {\n      var back = location.pathname + location.search;\n      u += (u.indexOf(\"?\") >= 0 ? \"&\" : \"?\") + \"return_url=\" + encodeURIComponent(back);\n    } catch (e) {}\n    return u;\n  }\n\n  // Central \"leaving TO sign in\" redirect. Used by EVERY sign-in button so the\n  // behaviour is consistent: (1) mark __dmbGuestSigningIn so the guest's provisional\n  // work is NOT discarded on the unload that this redirect triggers; (2) flush the\n  // design locally so it survives the round-trip; (3) set dmb_save_after_login so\n  // ON RETURN (now signed in) it's mirrored to the account. Then go to login.\n  async function _dmbGoSignIn(loginUrl) {\n    try { window.__dmbGuestSigningIn = true; } catch (e) {}\n    try { localStorage.setItem(\"dmb_save_after_login\", getCustomerSaveKey()); } catch (e) {}\n    try { _dmbLeaveGuardDismissed = true; } catch (e) {}\n    // Lightweight feedback so the button doesn't look frozen while we finish the\n    // photo write (usually <1s; capped at 5s) before the redirect.\n    var _ov = null;\n    try {\n      _ov = document.createElement(\"div\");\n      _ov.style.cssText = \"position:fixed;inset:0;z-index:1000006;display:flex;align-items:center;justify-content:center;background:rgba(17,17,17,.55);color:#fff;font-size:15px;font-weight:800;text-align:center;padding:20px;\";\n      _ov.textContent = \"Saving your design\u2026 \u2601\uFE0F\";\n      document.body.appendChild(_ov);\n      // Fallback: if the redirect is somehow blocked, don't leave the overlay stuck.\n      setTimeout(function () { try { if (_ov && _ov.parentNode) _ov.remove(); } catch (e) {} }, 6000);\n    } catch (e) {}\n    // Persist the layout, then WAIT for the photo write before redirecting \u2014 a\n    // fire-and-forget IDB save races the navigation, so photos added in the last\n    // moment before Sign-in could be dropped and appear lost after login.\n    try { _dmbWriteProgressState(); } catch (e) {}\n    try {\n      if (editor && Array.isArray(editor.photos) && editor.photos.length) {\n        await Promise.race([\n          _savePhotosToIDB(getCustomerSaveKey(), editor.photos),\n          new Promise(function (r) { setTimeout(r, 5000); }),   // never trap the redirect\n        ]);\n      }\n    } catch (e) {}\n    location.assign(loginUrl || _dmbLoginUrlBackToBuilder());\n  }\n\n  // Discard a guest's provisional work when they LEAVE WITHOUT signing in: delete\n  // their R2 photos (best-effort beacon; App Proxy signs it) and wipe the local\n  // cache, so nothing lingers on the server and a later guest visit has nothing to\n  // resume. Called only on a real page unload (see the pagehide listener), never on\n  // a tab/app switch, and never when signing in (__dmbGuestSigningIn).\n  function _dmbGuestDiscardOnLeave() {\n    if (_dmbRenderMode()) return;              // render mode: never deletes anything\n    try {\n      var did = _dmbInstantBackup.designId();\n      var sid = _dmbPhotoBackup.guestId();\n      if (did && sid && window.DMB_DESIGNS_PROXY && navigator.sendBeacon) {\n        navigator.sendBeacon(window.DMB_DESIGNS_PROXY + \"/delete-guest?id=\" +\n          encodeURIComponent(did) + \"&sid=\" + encodeURIComponent(sid));\n      }\n    } catch (e) {}\n    try { localStorage.removeItem(getCustomerSaveKey()); } catch (e) {}\n    try { localStorage.removeItem(\"dmb_save_after_login\"); } catch (e) {}\n    try { if (_dmbInstantBackup.clearManifest) _dmbInstantBackup.clearManifest(); } catch (e) {}\n    try { _clearPhotosFromIDB(getCustomerSaveKey()); } catch (e) {}\n  }\n  // True when THIS page load is a refresh/reload (vs a fresh navigation).\n  function _dmbWasReload() {\n    try {\n      var _nav = performance.getEntriesByType && performance.getEntriesByType(\"navigation\")[0];\n      if (_nav && _nav.type) return _nav.type === \"reload\";\n      if (performance.navigation) return performance.navigation.type === 1;\n    } catch (e) {}\n    return false;\n  }\n  // True when this load CONTINUES the same browser tab (a refresh or in-tab nav),\n  // vs a brand-new tab (fresh visit / bookmark / reopen after close). sessionStorage\n  // survives refreshes on EVERY browser (incl. mobile Safari / bfcache) but is empty\n  // in a new tab \u2014 far more reliable than Navigation Timing's reload type, which\n  // mis-reports refreshes as \"navigate\" on some devices and was letting a guest's\n  // refresh be treated as a fresh visit and DISCARD their design. Read the flag\n  // (was this tab already active?) BEFORE setting it for the next load.\n  var _DMB_TAB_CONTINUED = (function () {\n    if (_dmbRenderMode()) return false;   // render mode: no sessionStorage write\n    try {\n      var _was = sessionStorage.getItem(\"dmb_tab_active_v1\") === \"1\";\n      sessionStorage.setItem(\"dmb_tab_active_v1\", \"1\");\n      return _was;\n    } catch (e) { return false; }\n  })();\n  // \u2500\u2500 Guest session lifecycle (decided at LOAD, by navigation type) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // A guest's work is provisional. We CAN'T tell a refresh from a close at unload\n  // time (both look identical), and discarding on unload nuked a design on an\n  // accidental refresh. So instead we decide on the NEXT load:\n  //   \u2022 RELOAD (refresh) \u2192 keep everything; the ?restore flow continues the design.\n  //   \u2022 Any other entry (fresh visit / bookmark / back after closing) \u2192 discard the\n  //     previous provisional work (R2 + local) so a returning guest starts clean and\n  //     is never offered a resume.\n  // \"Close & never return\" is cleaned by the R2 lifecycle. Signed-in customers are\n  // exempt (they have real saved designs).\n  (function _dmbGuestSessionLifecycle() {\n    try {\n      if (window.DMB_CUSTOMER_ID || !window.DMB_DESIGNS_PROXY) return;\n      if (_dmbRenderMode()) return;   // render mode: never discards a guest design\n      if (_dmbWasReload() || _DMB_TAB_CONTINUED) return; // refresh / same tab \u2192 keep + restore\n      // A sign-in is in progress (they clicked \"Sign in to save\"): KEEP their work\n      // even on a navigate return, in case login was cancelled/failed and they came\n      // back to try again \u2014 otherwise clicking Sign-in then backing out would delete\n      // the very design they were trying to save. (A SUCCESSFUL sign-in returns\n      // signed-in \u2192 already exempt above; the flag is consumed once signed in.)\n      try { if (localStorage.getItem(\"dmb_save_after_login\")) return; } catch (e) {}\n      var _hasStale = false;\n      try { _hasStale = !!localStorage.getItem(getCustomerSaveKey()); } catch (e) {}\n      if (_hasStale) _dmbGuestDiscardOnLeave();          // fresh entry \u2192 clear stale work\n    } catch (e) {}\n  })();\n\n  // Clear confirmation after a design is saved to the account. Replaces the plain\n  // toast so the customer can SEE where their designs live (the Saved designs\n  // folder) and isn't left wondering whether/where it saved.\n  function _dmbShowSavedConfirm() {\n    var ov = document.getElementById(\"dmbSavedConfirm\");\n    if (ov) { ov.style.display = \"flex\"; return; }\n    ov = document.createElement(\"div\");\n    ov.id = \"dmbSavedConfirm\";\n    ov.style.cssText = \"position:fixed;inset:0;z-index:1000001;display:flex;align-items:center;justify-content:center;background:rgba(17,17,17,.55);padding:18px;\";\n    var card = document.createElement(\"div\");\n    card.style.cssText = \"background:#f4efe7;border-radius:20px;max-width:360px;width:100%;padding:30px 26px 22px;text-align:center;position:relative;box-shadow:0 24px 70px rgba(0,0,0,.35);font-family:inherit;\";\n    card.innerHTML =\n      '<div style=\"font-size:34px;margin:2px 0 6px;\">\u2705</div>' +\n      '<h3 style=\"margin:0 0 6px;font-size:22px;font-weight:800;color:#1e1e1e;\">Saved to your account</h3>' +\n      '<p style=\"margin:0 0 20px;font-size:14px;line-height:1.55;color:#a06f86;font-weight:600;\">Your design is saved and you can reopen it on any device from your Saved designs folder.</p>' +\n      '<a href=\"' + DMB_SAVED_DESIGNS_URL + '\" style=\"display:block;text-decoration:none;box-sizing:border-box;background:#1e1e1e;color:#fff;border-radius:11px;padding:13px 18px;font-size:15px;font-weight:800;\">\uD83D\uDCC1 View my saved designs</a>' +\n      '<button type=\"button\" id=\"dmbSavedConfirmStay\" style=\"margin-top:10px;background:transparent;border:none;color:#9a8f82;font-size:13px;font-weight:600;cursor:pointer;padding:6px;\">Keep editing</button>';\n    ov.appendChild(card);\n    document.body.appendChild(ov);\n    var close = function () { ov.style.display = \"none\"; };\n    card.querySelector(\"#dmbSavedConfirmStay\").addEventListener(\"click\", close);\n    ov.addEventListener(\"click\", function (e) { if (e.target === ov) close(); });\n  }\n\n  // Guest nudge: \"Sign in to save your design + open it on any device.\"\n  // Dismissible, once per session, only when NOT logged in.\n  function _dmbMaybeShowSignInPrompt() {\n    if (_dmbRenderMode()) return;   // render mode: no UI\n    if (window.DMB_CUSTOMER_ID) return;                       // already signed in\n    if (!window.DMB_LOGIN_URL) return;\n    try { if (sessionStorage.getItem(\"dmb_signin_prompted\")) return; sessionStorage.setItem(\"dmb_signin_prompted\", \"1\"); } catch (e) {}\n    if (document.getElementById(\"dmbSignInPrompt\")) return;\n    // Light-card popup matching the site aesthetic, with an \u2715 close in the corner.\n    let bottomPx = 20;\n    try {\n      const tabs = document.getElementById(\"dmbMshTabs\");\n      if (tabs) { const b = tabs.getBoundingClientRect(); if (b.height > 0 && b.top < window.innerHeight) bottomPx = Math.max(20, window.innerHeight - b.top + 12); }\n    } catch (e) {}\n    const el = document.createElement(\"div\");\n    el.id = \"dmbSignInPrompt\";\n    el.style.cssText = \"position:fixed;bottom:\" + bottomPx + \"px;left:50%;transform:translateX(-50%);z-index:999998;background:#ffffff;color:#3d3833;border:1px solid #e7e1d4;border-radius:18px;padding:22px 24px 18px;box-shadow:0 18px 50px rgba(70,60,40,.28);width:min(340px,calc(100vw - 28px));box-sizing:border-box;font-family:inherit;text-align:center;\";\n    el.innerHTML =\n      '<button type=\"button\" id=\"dmbSignInPromptX\" aria-label=\"Close\" style=\"position:absolute;top:9px;right:12px;border:none;background:transparent;font-size:26px;line-height:1;color:#a89e8b;cursor:pointer;padding:4px 6px;\">&times;</button>' +\n      '<div style=\"font-size:32px;margin:0 0 6px;\">&#128190;</div>' +\n      '<h3 style=\"margin:0 0 5px;font-size:19px;font-weight:800;color:#3d3833;\">Save your work</h3>' +\n      '<p style=\"margin:0 0 16px;font-size:13.5px;line-height:1.5;color:#8a8272;font-weight:600;\">Sign in to save your design and open it on any device.</p>' +\n      '<button type=\"button\" id=\"dmbSignInPromptGo\" style=\"display:block;width:100%;box-sizing:border-box;background:#6b62d6;color:#fff;border:none;border-radius:11px;padding:13px 18px;font-size:15px;font-weight:800;cursor:pointer;\">Sign in</button>';\n    document.body.appendChild(el);\n    el.querySelector(\"#dmbSignInPromptGo\").addEventListener(\"click\", () => { _dmbGoSignIn(); });\n    el.querySelector(\"#dmbSignInPromptX\").addEventListener(\"click\", () => { el.remove(); });\n    setTimeout(() => { if (el.isConnected) el.remove(); }, 20000);\n  }\n\n  // \"Sign in to save\" popup shown when a GUEST taps Save Design. Cream palette to\n  // match the home sign-in popup. Signed-in users skip this and save directly.\n  // opts.mandatory  \u2192 no \u2715 / no \"Maybe later\" / no overlay-close (must sign in)\n  // opts.loginUrl   \u2192 where the \"Sign in\" button redirects (default: back to builder)\n  function _dmbShowSaveSignIn(opts) {\n    opts = opts || {};\n    var mandatory = !!opts.mandatory;\n    var loginUrl  = opts.loginUrl || _dmbLoginUrlBackToBuilder();\n    let ov = document.getElementById(\"dmbSaveSignIn\");\n    if (ov) { ov.style.display = \"flex\"; return; }\n    ov = document.createElement(\"div\");\n    ov.id = \"dmbSaveSignIn\";\n    ov.style.cssText = \"position:fixed;inset:0;z-index:1000001;display:flex;align-items:center;justify-content:center;background:rgba(17,17,17,.55);padding:18px;\";\n    const card = document.createElement(\"div\");\n    card.style.cssText = \"background:#f4efe7;border-radius:20px;max-width:360px;width:100%;padding:30px 26px 22px;text-align:center;position:relative;box-shadow:0 24px 70px rgba(0,0,0,.35);font-family:inherit;\";\n    card.innerHTML =\n      (mandatory ? '' : '<button type=\"button\" id=\"dmbSaveSignInX\" aria-label=\"Close\" style=\"position:absolute;top:10px;right:14px;border:none;background:transparent;font-size:26px;line-height:1;color:#8a8278;cursor:pointer;padding:4px;\">&times;</button>') +\n      '<div style=\"font-size:34px;margin:2px 0 6px;\">\uD83D\uDCBE</div>' +\n      '<h3 style=\"margin:0 0 6px;font-size:22px;font-weight:800;color:#1e1e1e;\">Sign in to save</h3>' +\n      '<p style=\"margin:0 0 20px;font-size:14px;line-height:1.55;color:#a06f86;font-weight:600;\">Sign in to save your design and open it on all your devices.</p>' +\n      '<button type=\"button\" id=\"dmbSaveSignInGo\" style=\"display:block;width:100%;box-sizing:border-box;background:#1e1e1e;color:#fff;border:none;border-radius:11px;padding:13px 18px;font-size:15px;font-weight:800;cursor:pointer;\">Sign in</button>' +\n      (mandatory ? '' : '<button type=\"button\" id=\"dmbSaveSignInLater\" style=\"margin-top:10px;background:transparent;border:none;color:#9a8f82;font-size:13px;font-weight:600;cursor:pointer;padding:6px;\">Maybe later</button>');\n    ov.appendChild(card);\n    document.body.appendChild(ov);\n    const close = () => { ov.style.display = \"none\"; };\n    if (!mandatory) {\n      var _x = card.querySelector(\"#dmbSaveSignInX\");     if (_x) _x.addEventListener(\"click\", close);\n      var _l = card.querySelector(\"#dmbSaveSignInLater\"); if (_l) _l.addEventListener(\"click\", close);\n      ov.addEventListener(\"click\", (e) => { if (e.target === ov) close(); });\n    }\n    card.querySelector(\"#dmbSaveSignInGo\").addEventListener(\"click\", () => { _dmbGoSignIn(loginUrl); });\n  }\n\n  // After a guest signs in via the Save Design popup, finish the save: once the\n  // design has restored, mirror it to the customer's account on R2 (state + photos),\n  // so it's saved permanently and reachable on any device. Runs once, then clears.\n  function _dmbMaybeSaveAfterLogin() {\n    if (!window.DMB_CUSTOMER_ID) return;\n    let flag = null;\n    try { flag = localStorage.getItem(\"dmb_save_after_login\"); } catch (e) {}\n    if (!flag) return;\n    let tries = 0;\n    const tick = () => {\n      tries++;\n      const ready = editor && editor.totalPages > 0;\n      if (ready) {\n        // settle a moment so restored photos are in editor.photos before we upload\n        setTimeout(async () => {\n          try { localStorage.removeItem(\"dmb_save_after_login\"); } catch (e) {}\n          try {\n            // \u2500\u2500 CLAIM FIRST \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n            // Copy the guest's R2 photos straight into the account server-side \u2014\n            // fast + reliable, independent of IndexedDB and the slow foreground\n            // upload below. Mark the copied pids as already-uploaded so the delta\n            // save doesn't re-push them (it then only handles the rare photo that\n            // never reached R2 during the guest session).\n            try {\n              const _did = _dmbCloud.designId();\n              const _claimed = await _dmbCloud.claim(_did);\n              if (_claimed && Array.isArray(_claimed.pids) && _claimed.pids.length) {\n                _dmbCloud._uploaded[_did] = _dmbCloud._uploaded[_did] || new Set();\n                _claimed.pids.forEach((p) => _dmbCloud._uploaded[_did].add(p));\n              }\n            } catch (e) {}\n            await saveCustomerProgress();                                   // local\n            if (typeof _dmbCloudPersist === \"function\") _dmbCloudPersist();  // account (state + any un-claimed photos \u2192 R2)\n            _dmbShowSavedConfirm();   // clear confirmation + link to the Saved designs folder\n          } catch (e) {}\n        }, 1500);\n        return;\n      }\n      if (tries < 40) setTimeout(tick, 500);   // wait up to ~20s for the restore\n      else { try { localStorage.removeItem(\"dmb_save_after_login\"); } catch (e) {} }\n    };\n    setTimeout(tick, 800);\n  }\n\n  // Small bottom toast (reused for save confirmations).\n  function _dmbToast(msg) {\n    try {\n      const el = document.createElement(\"div\");\n      el.style.cssText = \"position:fixed;left:50%;bottom:20px;transform:translateX(-50%);z-index:1000002;background:#1e1e1e;color:#fff;border-radius:12px;padding:13px 18px;max-width:92vw;font-size:14px;font-weight:700;box-shadow:0 10px 40px rgba(0,0,0,.4);\";\n      el.textContent = msg;\n      document.body.appendChild(el);\n      setTimeout(() => { try { el.remove(); } catch (e) {} }, 5000);\n    } catch (e) {}\n  }\n\n  async function _clearPhotosFromIDB(saveKey) {\n    try {\n      const db = await _openPhotoDB();\n      const tx = db.transaction(\"blobs\", \"readwrite\");\n      const store = tx.objectStore(\"blobs\");\n      const keys = await new Promise(res => {\n        const r = store.getAllKeys();\n        r.onsuccess = () => res(r.result || []);\n        r.onerror = () => res([]);\n      });\n      keys.filter(k => String(k).startsWith(saveKey + \"__\")).forEach(k => store.delete(k));\n      await new Promise(res => { tx.oncomplete = res; tx.onerror = res; });\n      db.close();\n    } catch(e) {}\n  }\n\n  // \u2500\u2500 Public save / load / clear \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  async function saveCustomerProgress() {\n    const key = getCustomerSaveKey();\n    let _localOk = true;\n    try {\n      // Save builder state to localStorage (with timestamp)\n      const payload = getCurrentBuilderStateSnapshot();\n      payload.savedAt = new Date().toISOString();\n      payload._saveType = window.DMB_CARD_MODE ? \"card\" : window.DMB_MAGAZINE_MODE ? \"magazine\" : \"book\";\n      payload._templateKey = getTemplateKey() || \"custom\";\n      // Cover handle: try URL ?cover= param, then ?template= param, then carry\n      // forward whatever was stored in the previous save (so re-saving from\n      // ?restore=1 \u2014 which has no ?cover= \u2014 doesn't lose the original handle).\n      const _urlCover = new URLSearchParams(window.location.search).get(\"cover\") || getTemplateKey() || \"\";\n      const _prevSave = loadCustomerProgress();\n      payload._coverHandle = _urlCover || (_prevSave && _prevSave._coverHandle) || \"\";\n      // The photos array is dead weight in localStorage \u2014 restore reads photos\n      // back from IndexedDB, never from here (see applyBuilderStateSnapshot). For\n      // a big book it's the bulk of the payload, so dropping it keeps this write\n      // well under the ~5 MB localStorage quota.\n      try {\n        if (payload.editor && Array.isArray(payload.editor.photos)) payload.editor.photos = [];\n      } catch (_) {}\n      // Heavy crop images (sticker/icon base64) also come OUT \u2014 they live in\n      // IndexedDB (mirrored below) and are reattached on restore. Keeps this write\n      // small so a few stickers can't overflow the ~5 MB cap and silently drop.\n      try { _dmbStripObjImagesFromSnapshot(payload); } catch (_) {}\n      try { _dmbPersistObjImages(); } catch (_) {}\n      localStorage.setItem(key, JSON.stringify(payload));\n    } catch(e) {\n      // localStorage full/unavailable \u2014 do NOT alert and do NOT abort. Photos\n      // still go to IndexedDB below, logged-in customers also get the cloud save,\n      // and (critically) the order flow must never be blocked by a failed local\n      // autosave. We just record that the local copy didn't land.\n      _localOk = false;\n      console.warn(\"saveCustomerProgress: localStorage write failed:\", e);\n    }\n    // Save photos to IndexedDB in background \u2014 show progress\n    if (saveMsg) { saveMsg.textContent = \"Saving photos\u2026\"; saveMsg.style.display = \"block\"; }\n    await _savePhotosToIDB(key, editor.photos || []);\n    if (saveMsg) {\n      saveMsg.textContent = _localOk ? \"All saved \u2705\" : \"Saved\";\n      setTimeout(() => { saveMsg.style.display = \"none\"; }, 2500);\n    }\n    return _localOk;\n  }\n\n  function loadCustomerProgress() {\n    try {\n      const raw = localStorage.getItem(getCustomerSaveKey());\n      return raw ? JSON.parse(raw) : null;\n    } catch { return null; }\n  }\n\n  async function clearCustomerProgress() {\n    const key = getCustomerSaveKey();\n    try { localStorage.removeItem(key); } catch {}\n    await _clearPhotosFromIDB(key);\n  }\n\n  // Delete THIS design's cloud/R2 copy so it drops out of \"Saved Designs\". Used\n  // after add-to-cart: clearCustomerProgress() only wipes the LOCAL copy, so the\n  // account copy (which Saved Designs lists from R2) kept showing the book. Covers\n  // signed-in (/design DELETE) and guests (/delete-guest).\n  async function _dmbDeleteCloudDesign() {\n    // Stop the background R2 sync from re-creating what we're about to delete.\n    _dmbDesignFinalized = true;\n    try { clearTimeout(_dmbCloudTimer); } catch (e) {}\n    _dmbR2Dirty = false;\n    if (!window.DMB_DESIGNS_PROXY) return;\n    const proxy = window.DMB_DESIGNS_PROXY;\n    let id = \"\";\n    try { id = getCustomerSaveKey().replace(/^dmb_progress_/, \"\"); } catch (e) {}\n    if (!id) return;\n    try {\n      if (window.DMB_CUSTOMER_ID) {\n        await fetch(proxy + \"/design?id=\" + encodeURIComponent(id), { method: \"DELETE\", credentials: \"same-origin\", keepalive: true });\n      } else {\n        let sid = \"\"; try { sid = _dmbPhotoBackup.guestId(); } catch (e) {}\n        if (sid) await fetch(proxy + \"/delete-guest?id=\" + encodeURIComponent(id) + \"&sid=\" + encodeURIComponent(sid), { method: \"POST\", credentials: \"same-origin\", keepalive: true });\n      }\n    } catch (e) {}\n  }\n\n  // Cross-device / cleared-cache resume: a logged-in customer with no local copy\n  // may still have a cloud design. Offer to restore it via the ?restore=1 flow.\n  // Reload the builder with ?restore=1 so the tested restore flow auto-loads the\n  // saved design (local + cloud) in place.\n  function _dmbReloadWithRestore(href) {\n    try { const u = new URL(href, location.origin); u.searchParams.set(\"restore\", \"1\"); location.replace(u.toString()); }\n    catch (e) { location.search = (location.search ? location.search + \"&\" : \"?\") + \"restore=1\"; }\n  }\n  // Builder URL that resumes a specific saved design (from its /list meta).\n  function _dmbResumeUrlFor(d) {\n    if (!d || !d.id) return null;\n    const t = d._saveType || String(d.id).split(\"_\")[0];\n    const isMag = (t === \"magazine\" || t === \"mag\");\n    const base = isMag ? \"/pages/design-my-magazine\" : \"/pages/design-my-book\";\n    let u = base + \"?restore=1\";\n    if (d._coverHandle) {\n      u += isMag ? \"&template=\" + encodeURIComponent(d._coverHandle) : \"&cover=\" + encodeURIComponent(d._coverHandle);\n    } else {\n      const suffix = String(d.id).replace(/^(book|magazine|mag|card)_/, \"\");  // custom design \u2192 resume by id\n      if (suffix) u += \"&design=\" + encodeURIComponent(suffix);\n    }\n    return u;\n  }\n  // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n  // Instant cross-device sync (signed-in). Poll the photo-free /list every few\n  // seconds while the tab is visible; when ANOTHER device saves a newer version of\n  // THIS design, auto-load it (newest wins). No Worker change \u2014 the version stamp\n  // (savedAt / rev / device) rides inside meta, which /list already returns.\n  // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n  let _dmbSyncTimer = null, _dmbSyncPendingRev = null, _dmbSyncReadyAt = 0, _dmbSyncBusy = false;\n  function _dmbSyncActive() {\n    return !!(window.DMB_CUSTOMER_ID && _dmbCloud && _dmbCloud.enabled() && !_dmbDesignFinalized);\n  }\n  async function _dmbSyncRemote() {\n    try { const list = await _dmbCloud.list(); const curId = _dmbCloud.designId(); return (list || []).find((d) => d && d.id === curId) || null; }\n    catch (e) { return null; }\n  }\n  function _dmbSyncPull(remote) {\n    if (!remote) return;\n    if (_dmbR2Dirty) { try { _dmbR2Flush(); } catch (e) {} return; }   // we hold newer local edits \u2192 keep ours\n    if (_dmbDragSuppress) return;                                       // mid drag / nudge \u2014 wait for a calm moment\n    try { if (_dmbModalOpen()) return; } catch (e) {}\n    const u = _dmbResumeUrlFor(remote);\n    if (u) { _dmbSyncBusy = true; try { location.assign(u); } catch (e) { _dmbSyncBusy = false; } }\n  }\n  async function _dmbSyncCheck(immediate) {\n    if (!_dmbSyncActive() || _dmbSyncBusy) return;\n    if (Date.now() < _dmbSyncReadyAt) return;                          // settle grace right after (re)load\n    if (_dmbR2Dirty) { try { _dmbR2Flush(); } catch (e) {} return; }   // push ours first so it can win\n    const remote = await _dmbSyncRemote();\n    if (!remote || !remote.rev) return;\n    // \u2500\u2500 Photo-library protection \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // NEVER let a version with FEWER photos win \u2014 that truncation is exactly how this\n    // book's tray got wiped (a device saved with an empty photo list). If WE hold more\n    // photos than the cloud copy, PUSH ours (safe: only adds) and do not pull.\n    const _remotePhotos = (remote.photoIds || []).length;\n    const _localPhotos  = (editor && editor.photos ? editor.photos.length : 0);\n    if (_localPhotos > _remotePhotos) { try { _dmbCloudPersist(); } catch (e) {} return; }\n    // Same CONTENT as what we hold (rev is a content hash) \u2192 nothing to do, even if the\n    // other device re-saved it with a newer time. This is what kills the reload loop.\n    if (remote.rev === _dmbCurRev) { if (remote.savedAt) _dmbCurSavedAt = remote.savedAt; _dmbSyncPendingRev = null; return; }\n    // Different content, but it's OUR device's own newer save \u2014 adopt as current.\n    if (remote.device && remote.device === _dmbDeviceId()) { _dmbCurRev = remote.rev; _dmbCurSavedAt = remote.savedAt || _dmbCurSavedAt; return; }\n    // Another device holds genuinely different content \u2014 pull if it's newer by time.\n    const newer = remote.savedAt && (!_dmbCurSavedAt || remote.savedAt > _dmbCurSavedAt);\n    if (!newer) return;\n    // Pull instantly if the user just refocused this tab; otherwise wait one poll for\n    // the other device to settle (so we don't reload on its every keystroke).\n    if (immediate || _dmbSyncPendingRev === remote.rev) _dmbSyncPull(remote);\n    else _dmbSyncPendingRev = remote.rev;\n  }\n\n  // \u2500\u2500 Cross-device DIAGNOSTIC (open the book with ?dmbdiag=1) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // Shows the real facts on THIS device: the design id in use, how many photos the\n  // layout references, how many are actually in R2, how many locally. Lets us pinpoint\n  // WHY photos differ across devices instead of guessing.\n  // Photo ids a DESIGN object PLACES (template slots, layout frames, full-spread, free\n  // frames, cover). `design` = { editor:{...}, coverState:{...} }.\n  function _dmbRefsFromDesign(design) {\n    const s = new Set();\n    try {\n      const E = (design && design.editor) || {};\n      Object.values(E.pageSlots || {}).forEach(ps => { if (ps) { if (ps.A) s.add(ps.A); if (ps.B) s.add(ps.B); } });\n      Object.values(E.pageFrames || {}).forEach(fr => { if (fr) Object.values(fr).forEach(v => { if (v) s.add(v); }); });\n      Object.values(E.spreadPhoto || {}).forEach(sp => { if (sp && sp.photoId) s.add(sp.photoId); });\n      Object.values(E.pageObjects || {}).forEach(arr => { if (Array.isArray(arr)) arr.forEach(o => { if (o && o.type === \"photo-frame\" && o.photoId) s.add(o.photoId); }); });\n      (E.coverObjects || []).forEach(o => { if (o && o.type === \"photo-frame\" && o.photoId) s.add(o.photoId); });\n      const cs = (design && design.coverState) || {};\n      [\"frontImageId\", \"backImageId\", \"bleedImageId\"].forEach(k => { if (cs[k]) s.add(cs[k]); });\n    } catch (e) {}\n    return s;\n  }\n  // Same, for the CURRENT live builder (used by the diagnostic).\n  function _dmbDesignPhotoRefs() {\n    return _dmbRefsFromDesign({ editor: (typeof editor !== \"undefined\" ? editor : {}), coverState: (typeof coverState !== \"undefined\" ? coverState : {}) });\n  }\n  async function _dmbDiagPanel() {\n    if (document.getElementById(\"dmbDiagPanel\")) return;\n    const proxy = window.DMB_DESIGNS_PROXY || \"/apps/designs\";\n    const cid = window.DMB_CUSTOMER_ID || \"(not signed in)\";\n    let designId = \"?\"; try { designId = _dmbCloud.designId(); } catch (e) {}\n    const refs = Array.from(_dmbDesignPhotoRefs());\n    const localCount = (editor && editor.photos ? editor.photos.length : 0);\n    let r2 = [], r2err = \"\", listEntry = null;\n    try {\n      const rr = await fetch(proxy + \"/photos?id=\" + encodeURIComponent(designId), { credentials: \"same-origin\" });\n      if (rr.ok) { const d = await rr.json(); r2 = (d.photos || []).map(p => p.pid || p.id); } else r2err = \"HTTP \" + rr.status;\n    } catch (e) { r2err = String(e && e.message || e); }\n    try { const list = await _dmbCloud.list(); listEntry = (list || []).find(d => d && d.id === designId) || null; } catch (e) {}\n    const r2set = new Set(r2);\n    const refsInR2 = refs.filter(id => r2set.has(id)).length;\n    const missing = refs.filter(id => !r2set.has(id));\n    const localSet = new Set((editor && editor.photos || []).map(p => p.id));\n    const refsLocal = refs.filter(id => localSet.has(id)).length;\n\n    const restoreQ = (new URLSearchParams(location.search).get(\"restore\") === \"1\") ? \"YES (loaded from cloud)\" : \"no (loaded local)\";\n    let pageItems = 0, coverItems = 0, pageCount = 0, extra = 0;\n    try {\n      const E = editor || {};\n      Object.values(E.pageObjects || {}).forEach(a => { if (Array.isArray(a)) pageItems += a.length; });\n      coverItems = ((E.coverObjects || []).length) + ((E.coverUserTexts || []).length);\n      pageCount = E.totalPages || 0; extra = E.extraSpreads || 0;\n    } catch (e) {}\n    const lines = [\n      \"CODE VERSION: 20260828b\",\n      \"Pages: \" + pageCount + \" (extra spreads \" + extra + \")  |  items on pages: \" + pageItems + \"  |  cover items: \" + coverItems,\n      \"DEVICE: \" + (navigator.userAgent.slice(0, 60)),\n      \"Loaded via ?restore=1: \" + restoreQ,\n      \"Signed-in customer: \" + cid,\n      \"Design id (this device): \" + designId,\n      \"\",\n      \"PHOTOS IN THE TRAY (editor.photos): \" + localCount,\n      \"Photos the layout PLACES: \" + refs.length,\n      \"  \u2022 those present in R2:   \" + refsInR2 + \" / \" + refs.length,\n      \"  \u2022 those loaded locally:  \" + refsLocal + \" / \" + refs.length,\n      \"Total objects in R2 for this id: \" + (r2err ? (\"ERROR \" + r2err) : r2.length),\n      \"\",\n      \"/list entry (cloud record): \" + (listEntry ? (\"photoIds=\" + ((listEntry.photoIds || []).length) + \", savedAt=\" + (listEntry.savedAt || \"?\") + \", device=\" + (listEntry.device || \"?\")) : \"NOT FOUND\"),\n      \"\",\n      \"Missing-from-R2 sample: \" + (missing.slice(0, 6).join(\", \") || \"(none)\"),\n      \"R2 pids sample: \" + (r2.slice(0, 8).join(\", \") || \"(none)\"),\n    ];\n    const ov = document.createElement(\"div\");\n    ov.id = \"dmbDiagPanel\";\n    ov.style.cssText = \"position:fixed;inset:0;z-index:1000005;background:rgba(0,0,0,.75);display:flex;align-items:center;justify-content:center;padding:16px;\";\n    const card = document.createElement(\"div\");\n    card.style.cssText = \"background:#111;color:#eee;border:1px solid #444;border-radius:14px;max-width:560px;width:100%;padding:18px;font-family:ui-monospace,Menlo,monospace;font-size:12.5px;line-height:1.55;box-shadow:0 20px 60px rgba(0,0,0,.6);\";\n    const ta = document.createElement(\"textarea\");\n    ta.readOnly = true; ta.value = lines.join(\"\\n\");\n    ta.style.cssText = \"width:100%;height:300px;background:#0b0b0b;color:#cfe;border:1px solid #333;border-radius:8px;padding:10px;font:inherit;resize:vertical;box-sizing:border-box;\";\n    const title = document.createElement(\"div\"); title.textContent = \"\u2601\uFE0F Cross-device photo diagnostic\"; title.style.cssText = \"font-weight:900;font-size:14px;margin-bottom:10px;color:#fff;font-family:system-ui,sans-serif;\";\n    const row = document.createElement(\"div\"); row.style.cssText = \"display:flex;gap:8px;margin-top:12px;\";\n    const copyBtn = document.createElement(\"button\"); copyBtn.textContent = \"Copy\"; copyBtn.style.cssText = \"flex:1;background:#6d6de6;color:#111;border:none;border-radius:8px;padding:10px;font-weight:800;cursor:pointer;font-family:system-ui,sans-serif;\";\n    copyBtn.onclick = () => { try { ta.select(); document.execCommand(\"copy\"); copyBtn.textContent = \"Copied \u2713\"; } catch (e) {} };\n    const closeBtn = document.createElement(\"button\"); closeBtn.textContent = \"Close\"; closeBtn.style.cssText = \"flex:1;background:#333;color:#fff;border:none;border-radius:8px;padding:10px;font-weight:800;cursor:pointer;font-family:system-ui,sans-serif;\";\n    closeBtn.onclick = () => { try { ov.remove(); } catch (e) {} };\n    card.appendChild(title); card.appendChild(ta); row.appendChild(copyBtn); row.appendChild(closeBtn); card.appendChild(row);\n    ov.appendChild(card); document.body.appendChild(ov);\n  }\n  try { if (new URLSearchParams(location.search).get(\"dmbdiag\") === \"1\") setTimeout(() => { try { _dmbDiagPanel(); } catch (e) {} }, 5000); } catch (e) {}\n  // AUTOMATIC cross-device sync is DISABLED (2026-08-27). Its polling + auto-pull +\n  // heal-push churned designs across devices and degraded a real customer book. Until it\n  // is redesigned as an explicit, conflict-safe \"Sync now\" action, this is a no-op:\n  // designs still save to the cloud on edit and restore on resume (the safe originals),\n  // just with no background device-to-device propagation. The save-side SAFETY guards\n  // (empty-save + anti-truncation in _dmbR2Flush) stay ON.\n  function _dmbStartCrossDeviceSync() {\n    return;   // disabled \u2014 see comment above\n  }\n\n  // Signed-in customer with no local copy: OFFER to resume their saved cloud design\n  // (prefer the one matching this context, else the most recent). A banner \u2014 it never\n  // auto-forces, so deliberately starting a fresh design still works.\n  async function _dmbAutoCloudRestore() {\n    if (restoreParam === \"1\") return;\n    try {\n      const list = await _dmbCloud.list();\n      if (!list || !list.length) return;\n      // ONLY offer a restore for THIS exact book (its own design id). Previously\n      // this defaulted to list[0] (the most-recently-saved design) when the current\n      // id wasn't found \u2014 so opening a NEW book offered the LAST book's design +\n      // photos. A book with no saved design of its own now restores nothing.\n      const curId = _dmbCloud.designId();\n      const m = list.find((d) => d && d.id === curId);\n      if (m) _dmbOfferCloudResume(m);\n    } catch (e) {}\n  }\n  // Centered \"welcome back\" modal offering to resume a saved cloud design.\n  function _dmbOfferCloudResume(target) {\n    if (!target) return;\n    _dmbResumeModal(\n      () => { const u = _dmbResumeUrlFor(target); if (u) location.assign(u); },\n      () => {}\n    );\n  }\n\n  // Discreet, page-styled \"restore this book's saved design\" card on the upload\n  // step \u2014 REPLACES the old auto \"Your photos are safe\" popup, which fired on any\n  // book (its manifest key was shared for custom books). This one:\n  //   \u2022 only for SIGNED-IN customers,\n  //   \u2022 only when a saved design for THIS EXACT book exists (cloud id match) OR\n  //     this book has photos from an interrupted upload (local manifest),\n  //   \u2022 inserted just above the \"+ Add Photos\" button, never as a blocking popup.\n  async function _dmbShowRestoreThisBook() {\n    try {\n      if (!window.DMB_CUSTOMER_ID || !_dmbCloud.enabled()) return;   // signed-in only\n      if (bookFiles && bookFiles.length) return;                     // already adding photos\n      if (document.getElementById(\"dmbRestoreThisBook\")) return;\n\n      // A saved cloud design for THIS EXACT book only (scoped by design id) \u2014 so it\n      // never appears on a different book, only where a real saved design exists.\n      let target = null;\n      try {\n        const list = await _dmbCloud.list();\n        const curId = _dmbCloud.designId();\n        target = (list || []).find((d) => d && d.id === curId) || null;\n      } catch (e) {}\n      if (!target) return;\n      if (bookFiles && bookFiles.length) return;                     // photos added while we fetched\n      if (document.getElementById(\"dmbRestoreThisBook\")) return;\n\n      const btn = document.querySelector(\".dmb-upload2-btn--primary\");\n      if (!btn || !btn.parentElement) return;\n\n      const n = target.photos ? target.photos.length : (target.photoIds ? target.photoIds.length : 0);\n      const card = document.createElement(\"div\");\n      card.id = \"dmbRestoreThisBook\";\n      card.style.cssText = \"display:flex;align-items:center;gap:12px;background:#fff;border:1px solid #e4ded3;border-radius:14px;padding:12px 14px;margin-bottom:14px;box-shadow:0 6px 20px rgba(0,0,0,.06);\";\n      const txt = document.createElement(\"div\");\n      txt.style.cssText = \"flex:1;min-width:0;text-align:left;\";\n      txt.innerHTML =\n        '<div style=\"font-weight:900;color:#1e1e1e;font-size:14px;\">\uD83D\uDCC2 You have a saved design for this book</div>' +\n        '<div style=\"font-size:12.5px;color:#666;font-weight:600;margin-top:2px;\">' +\n        (n ? (\"Pick up where you left off \u2014 \" + n + \" photo\" + (n === 1 ? \"\" : \"s\") + \" saved.\") : \"Pick up right where you left off.\") +\n        '</div>';\n      const go = document.createElement(\"button\");\n      go.type = \"button\"; go.textContent = \"Restore\";\n      go.style.cssText = \"flex-shrink:0;background:#6d3bd4;color:#fff;border:none;border-radius:10px;padding:10px 18px;font-weight:900;font-size:14px;cursor:pointer;\";\n      go.addEventListener(\"click\", () => { const u = _dmbResumeUrlFor(target); if (u) location.assign(u); });\n      card.appendChild(txt); card.appendChild(go);\n      btn.parentElement.insertBefore(card, btn);\n    } catch (e) {}\n  }\n\n  // Set when the user actively entered the builder from the upload step (they are\n  // now designing THIS design) \u2014 suppresses the \"Resume from where you left off?\"\n  // banner, which would otherwise pop up on top of the design they just started\n  // (progress is saved immediately on entry, so it would always trigger).\n  let _dmbSuppressResumeOffer = false;\n\n  function offerCustomerProgressRestore() {\n    // If we arrived via ?restore=1, the IIFE below already handles restore\n    // automatically \u2014 don't show the banner on top of that.\n    if (restoreParam === \"1\") return;\n    // Just came from the upload flow \u2192 already in this design, don't offer resume.\n    if (_dmbSuppressResumeOffer) return;\n    // Only offer resume to a SIGNED-IN customer who has a saved version of THIS\n    // exact design (per-design save key). Guests never see it \u2014 their local\n    // autosave shouldn't trigger a \"resume?\" prompt on a design they're in.\n    if (!window.DMB_CUSTOMER_ID) return;\n    const saved = loadCustomerProgress();\n    if (!saved) {\n      // No local copy \u2014 a logged-in customer may have a cloud design. Auto-restore it.\n      if (_dmbCloud.enabled()) { _dmbAutoCloudRestore(); }\n      return;\n    }\n\n    const banner = document.createElement(\"div\");\n    banner.style.cssText = \"position:fixed;bottom:20px;left:50%;transform:translateX(-50%);background:#1e1e1e;color:#fff;border-radius:12px;padding:14px 20px;display:flex;align-items:center;gap:14px;z-index:999999;box-shadow:0 8px 30px rgba(0,0,0,.5);font-size:14px;font-weight:700;max-width:92vw;\";\n    banner.innerHTML = `<span>\uD83D\uDCC2 Resume from where you left off?</span>`;\n\n    const yesBtn = document.createElement(\"button\");\n    yesBtn.textContent = \"Resume\";\n    yesBtn.style.cssText = \"background:#4a90e2;color:#fff;border:none;border-radius:8px;padding:8px 16px;cursor:pointer;font-weight:900;font-size:13px;flex-shrink:0;\";\n\n    const noBtn = document.createElement(\"button\");\n    noBtn.textContent = \"Start fresh\";\n    noBtn.style.cssText = \"background:#444;color:#fff;border:none;border-radius:8px;padding:8px 14px;cursor:pointer;font-weight:700;font-size:13px;flex-shrink:0;\";\n\n    banner.appendChild(yesBtn);\n    banner.appendChild(noBtn);\n    document.body.appendChild(banner);\n\n    yesBtn.addEventListener(\"click\", async () => {\n      banner.remove();\n      if (saveMsg) { saveMsg.textContent = \"Restoring\u2026\"; saveMsg.style.display = \"block\"; }\n\n      const _cl = v => { try { return JSON.parse(JSON.stringify(v || {})); } catch(e) { return {}; } };\n      const se  = saved.editor || {};\n      _dmbApplyRecentColors(saved);   // sync the colour palette\n\n      // \u2500\u2500 1. Load IDB photos \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n      const idbPhotos = await _loadPhotosFromIDB(getCustomerSaveKey());\n      // Re-key restored photos to the IDs the saved layout references, so placed\n      // photos resolve (otherwise spreads show blank/white while the tray is fine).\n      _dmbReconcilePhotoIds(idbPhotos, se);\n      // Only replace editor.photos if IDB has them (IDs now match saved page slots)\n      const useIdb = idbPhotos.length > 0;\n      if (useIdb) {\n        editor.photos = idbPhotos;\n        try { bookFiles = idbPhotos.map(p => p.file || new File([], p.filename || \"photo\")); } catch(e) {}\n      }\n      // If no IDB photos: keep current editor.photos (this session's uploads).\n      // We skip restoring page-slot assignments since IDs won't match.\n\n      // \u2500\u2500 2. Always restore cover state (does not depend on photo IDs) \u2500\u2500\u2500\u2500\u2500\n      if (saved.coverState) {\n        const cs = saved.coverState;\n        [\"frontImageUrl\",\"backImageUrl\",\"bleedImageUrl\",\"spineImageUrl\",\"imageUrl\"].forEach(k => {\n          if (cs[k] && String(cs[k]).startsWith(\"blob:\")) {\n            // Keep bleedImageId + bleedZone. _dmbRehydrateCoverPhotoUrls() below\n            // rebuilds the URL from the id; deleting the id here destroyed the\n            // wrap-around cover background on EVERY resume. See the full note in\n            // _dmbApplySavedEditorState.\n            cs[k] = \"\"; if (k === \"bleedImageUrl\" && !cs.bleedImageId) { cs.bleedZone = \"\"; }\n          }\n        });\n        Object.keys(cs).forEach(k => { try { coverState[k] = cs[k]; } catch(e) {} });\n        if (coverColorSwatch) coverColorSwatch.style.background = coverState.color || \"#ffffff\";\n        try { syncCoverInputsFromState(); } catch(e) {}\n      }\n\n      // \u2500\u2500 3. Always restore page structure (layout templates, stickers, maps)\n      editor.pageTemplate    = _cl(se.pageTemplate);\n      editor.pageFrames      = _cl(se.pageFrames);\n      editor.pageFrameRot    = _cl(se.pageFrameRot);\n      editor.pageFrameShapes = _cl(se.pageFrameShapes);\n      editor.pageObjects     = _cl(se.pageObjects);\n      editor.coverObjects    = Array.isArray(se.coverObjects)   ? _cl(se.coverObjects)   : [];\n      editor.coverUserTexts  = Array.isArray(se.coverUserTexts) ? _cl(se.coverUserTexts) : [];\n      if (!Array.isArray(editor.coverObjects)) try { migrateCoverObjectsToFlat(); } catch(e) {}\n\n      // \u2500\u2500 4. Restore photo assignments ONLY if IDB photos are available \u2500\u2500\u2500\u2500\n      if (useIdb) {\n        editor.pageSlots   = _cl(se.pageSlots);\n        editor.pageRot     = _cl(se.pageRot);\n        editor.spreadPhoto = _cl(se.spreadPhoto || {});\n      }\n      // If no IDB: keep current pageSlots (user will drag fresh photos to pages)\n\n      // \u2500\u2500 5. Restore pages count + extra spreads \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n      // Restore the EXTRA SPREADS first (added spreads = +2 pages / +1 spread each).\n      editor.extraSpreads = se.extraSpreads || 0;\n      const pg = se.totalPages || Number(saved.pages_selected) || editor.totalPages || 0;\n      if (pg) {\n        editor.totalPages    = pg;\n        editor.editablePages = se.editablePages || Math.max(0, pg - 1);\n        editor.totalSpreads  = se.totalSpreads  || Math.max(1, Math.ceil(pg / 2));\n        // The #pages dropdown holds the BASE page count (a real option), NOT the\n        // total-with-extra-spreads \u2014 renderSpread recomputes total = base + extras.\n        // Setting it to the total (e.g. 32) selects nothing \u2192 the builder renders\n        // a blank \"Spread 1/1\". So set the base and let extraSpreads add the rest.\n        const _base = Number(saved.pages_selected) || Math.max(0, pg - (editor.extraSpreads || 0) * 2) || pg;\n        const _pEl = document.getElementById(\"pages\");\n        if (_pEl) _pEl.value = String(_base);\n        const _s1bEl = document.getElementById(\"step1bPages\");   // keep the upload-step dropdown in sync too\n        if (_s1bEl) _s1bEl.value = String(_base);\n        try { updatePrice(); } catch(e) {}\n        try { _syncSpreadAdderUI(); } catch(e) {}\n      }\n      editor.currentSpread = 0;\n      builderMode = saved.builderMode || \"pages\";\n\n      // Rebuild cover face/bleed image URLs from their preserved photo IDs\n      // (the blob: URLs were cleared in step 2). Photos are loaded by now.\n      if (useIdb) { try { _dmbRehydrateCoverPhotoUrls(); } catch(e) {} }\n\n      // Reattach sticker/icon crop images (base64) from IndexedDB (stripped from state).\n      try { _dmbReattachObjImages(await _dmbLoadObjImagesFromIDB(getCustomerSaveKey())); } catch(e) {}\n\n      // \u2500\u2500 6. Re-render \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n      try { syncCoverInputsFromState(); } catch(e) {}\n      try { renderPhotoTray(); }          catch(e) {}\n      try { renderThumbsRail(); }         catch(e) {}\n      // Explicitly re-render the cover even if builderMode is \"pages\",\n      // because coverState + coverObjects were just restored.\n      try { renderCoverCanvas(); }        catch(e) {}\n      try { renderBuilderCanvas(); }      catch(e) {}\n      // Progress restore replaces ALL pages \u2014 clear stale preview cache\n      try { _dmbPvReinitAll(); }          catch(e) {}\n\n      if (saveMsg) saveMsg.style.display = \"none\";\n      _dmbToast(useIdb\n        ? `Restored \u2014 ${idbPhotos.length} photos + full layout`\n        : \"Cover & layout restored \u2014 drag your photos onto the pages\");\n\n      // \u2500\u2500 7. Cloud reconcile (ADDITIVE) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n      // Recover any photo the layout references but IndexedDB no longer has \u2014\n      // partial eviction (the reported \"most photos missing on the laptop\"\n      // case) or a full eviction / different device. When IDB had nothing but a\n      // saved layout exists AND the cloud is reachable, also restore the page\n      // assignments (ids match the cloud photo ids) so pulled-back photos land\n      // in their right places instead of the \"drag your photos\" fallback.\n      try {\n        const _proxy = !!window.DMB_DESIGNS_PROXY;\n        if (_proxy && !useIdb) {\n          editor.pageSlots   = _cl(se.pageSlots);\n          editor.pageRot     = _cl(se.pageRot);\n          editor.spreadPhoto = _cl(se.spreadPhoto || {});\n          try { _dmbRehydrateCoverPhotoUrls(); } catch (e) {}\n        }\n        const _recovered = await _dmbReconcileMissingPhotosFromCloud();\n        if (_recovered > 0 || (_proxy && !useIdb)) {\n          try { bookFiles = editor.photos.map((p) => p.file || new File([], p.filename || \"photo\")); } catch (e) {}\n          try { renderPhotoTray(); }     catch (e) {}\n          try { renderThumbsRail(); }    catch (e) {}\n          try { renderCoverCanvas(); }   catch (e) {}\n          try { renderBuilderCanvas(); } catch (e) {}\n          try { _dmbPvReinitAll(); }     catch (e) {}\n        }\n        if (_recovered > 0) {\n          if (saveMsg) saveMsg.style.display = \"none\";\n          _dmbToast(`Restored \u2014 recovered ${_recovered} photo${_recovered === 1 ? \"\" : \"s\"} from the cloud`);\n        }\n      } catch (e) {}\n    });\n\n    noBtn.addEventListener(\"click\", async () => {\n      banner.remove();\n      // Custom book: keep the previous design and start a NEW id (so a second\n      // custom book never overwrites the first). Templates/products: clear as before.\n      const _isCustom = !getTemplateKey() && !(new URLSearchParams(location.search).get(\"cover\"));\n      if (_isCustom) { _dmbRotateCustomId(); }\n      else { await clearCustomerProgress(); }\n    });\n\n    setTimeout(() => { if (banner.isConnected) banner.remove(); }, 30000);\n  }\n\n  function buildTemplatePayload() {\n    return {\n      coverState: cloneState(coverState),\n      coverObjects: cloneState(ensureCoverObjects()),\n      activeCoverZone: activeCoverZone || \"front\",\n      coverUserTexts: cloneState(editor.coverUserTexts || []),\n      page0: {\n        pageSlots: cloneState(editor.pageSlots?.[0] || { A: null, B: null }),\n        pageFrames: cloneState(editor.pageFrames?.[0] || {}),\n        pageTemplate: editor.pageTemplate?.[0] || null,\n        pageObjects: cloneState(editor.pageObjects?.[0] || []),\n        pageRot: cloneState(editor.pageRot?.[0] || { A: 0, B: 0 })\n      }\n    };\n  }\n\n  function saveCurrentTemplate() {\n    const templateKey = getTemplateKey();\n\n    if (!templateKey) {\n      alert(\"No template key found in the URL.\");\n      return;\n    }\n\n    try {\n      const payload = buildTemplatePayload();\n      localStorage.setItem(getTemplateStorageKey(templateKey), JSON.stringify(payload));\n\n      if (saveMsg) {\n        saveMsg.textContent = `Template ${templateKey} saved \u2705`;\n        saveMsg.style.display = \"block\";\n        setTimeout(() => { saveMsg.style.display = \"none\"; }, 1600);\n      }\n    } catch (err) {\n      console.error(err);\n      alert(\"Could not save template.\");\n    }\n  }\n\n  function loadTemplateByKey(templateKey) {\n    if (!templateKey) return false;\n\n    try {\n      const raw = localStorage.getItem(getTemplateStorageKey(templateKey));\n      if (!raw) return false;\n\n      const payload = JSON.parse(raw);\n\n      coverState.template = payload.coverState?.template || \"default\";\n      coverState.title1 = payload.coverState?.title1 || \"\";\n      coverState.title2 = payload.coverState?.title2 || \"\";\n      coverState.color = payload.coverState?.color || \"#ffffff\";\n      coverState.imageUrl = payload.coverState?.imageUrl || \"\";\n      coverState.imageFile = payload.coverState?.imageFile || null;\n      coverState.textStyles = cloneState(payload.coverState?.textStyles || {});\n\n      editor.coverObjects = cloneState(payload.coverObjects || []);\n      if (!Array.isArray(editor.coverObjects)) migrateCoverObjectsToFlat();\n      activeCoverZone = payload.activeCoverZone || \"front\";\n      editor.coverUserTexts = cloneState(payload.coverUserTexts || []);\n\n      // Restore Page 0 state\n      if (payload.page0) {\n        if (!editor.pageSlots) editor.pageSlots = {};\n        if (!editor.pageFrames) editor.pageFrames = {};\n        if (!editor.pageTemplate) editor.pageTemplate = {};\n        if (!editor.pageObjects) editor.pageObjects = {};\n        if (!editor.pageRot) editor.pageRot = {};\n        editor.pageSlots[0] = cloneState(payload.page0.pageSlots || { A: null, B: null });\n        editor.pageFrames[0] = cloneState(payload.page0.pageFrames || {});\n        editor.pageTemplate[0] = payload.page0.pageTemplate || null;\n        editor.pageObjects[0] = cloneState(payload.page0.pageObjects || []);\n        editor.pageRot[0] = cloneState(payload.page0.pageRot || { A: 0, B: 0 });\n      }\n\n      syncCoverInputsFromState();\n      builderMode = \"cover\";\n      renderBuilderCanvas();\n      // Template also replaces page 0 content \u2014 dirty-mark it too\n      try { _dmbPvMarkDirty(0); } catch(e) {}\n      return true;\n    } catch (err) {\n      console.error(err);\n      return false;\n    }\n  }\n\n  function getTemplateStorageKey(templateKey) {\n    return `dmb_template_${templateKey}`;\n  }\n\n  function buildTemplatePayload() {\n    return {\n      coverState: cloneState(coverState),\n      coverObjects: cloneState(ensureCoverObjects()),\n      activeCoverZone: activeCoverZone || \"front\",\n      coverUserTexts: cloneState(editor.coverUserTexts || []),\n      page0: {\n        pageSlots: cloneState(editor.pageSlots?.[0] || { A: null, B: null }),\n        pageFrames: cloneState(editor.pageFrames?.[0] || {}),\n        pageTemplate: editor.pageTemplate?.[0] || null,\n        pageObjects: cloneState(editor.pageObjects?.[0] || []),\n        pageRot: cloneState(editor.pageRot?.[0] || { A: 0, B: 0 })\n      }\n    };\n  }\n\n  function saveCurrentTemplate() {\n    const templateKey = getTemplateKey();\n\n    if (!templateKey) {\n      alert(\"No template key found in the URL.\");\n      return;\n    }\n\n    try {\n      const payload = buildTemplatePayload();\n      localStorage.setItem(getTemplateStorageKey(templateKey), JSON.stringify(payload));\n\n      if (saveMsg) {\n        saveMsg.textContent = `Template ${templateKey} saved \u2705`;\n        saveMsg.style.display = \"block\";\n        setTimeout(() => { saveMsg.style.display = \"none\"; }, 1600);\n      }\n    } catch (err) {\n      console.error(err);\n      alert(\"Could not save template.\");\n    }\n  }\n\n  // All per-page structural maps that an edit can touch. Capturing/restoring the FULL\n  // set is what makes undo/redo work for every action (background colours, frame shapes,\n  // full-spread photos, etc.) \u2014 not just the handful the old snapshot listed.\n  const _DMB_UNDO_MAPS = [\n    \"pageSlots\", \"pageRot\", \"pageTemplate\", \"pageFrames\", \"pageFrameRot\",\n    \"pageFrameShapes\", \"pageObjects\", \"pageBackgrounds\", \"spreadPhoto\", \"magicPages\"\n  ];\n  function _dmbCaptureState(label) {\n    const s = {\n      label: label || \"Edited\",\n      builderMode,\n      currentSpread: editor.currentSpread,\n      totalPages: editor.totalPages,\n      editablePages: editor.editablePages,\n      totalSpreads: editor.totalSpreads,\n      extraSpreads: editor.extraSpreads,\n      bookSize: editor.bookSize,\n      orientation: editor.orientation,\n      coverObjects: cloneState(ensureCoverObjects()),\n      coverUserTexts: cloneState(editor.coverUserTexts || []),\n      coverState: cloneState(coverState)\n    };\n    _DMB_UNDO_MAPS.forEach((k) => { s[k] = cloneState(editor[k] || {}); });\n    return s;\n  }\n  function _dmbRestoreState(s) {\n    if (!s) return;\n    builderMode = s.builderMode || \"pages\";\n    editor.currentSpread = s.currentSpread;\n    if (s.totalPages != null)    editor.totalPages    = s.totalPages;\n    if (s.editablePages != null) editor.editablePages = s.editablePages;\n    if (s.totalSpreads != null)  editor.totalSpreads  = s.totalSpreads;\n    editor.extraSpreads = s.extraSpreads || 0;\n    if (s.bookSize)    editor.bookSize    = s.bookSize;\n    if (s.orientation) editor.orientation = s.orientation;\n    _DMB_UNDO_MAPS.forEach((k) => { editor[k] = cloneState(s[k] || {}); });\n    editor.coverObjects = cloneState(s.coverObjects || []);\n    if (!Array.isArray(editor.coverObjects)) migrateCoverObjectsToFlat();\n    editor.coverUserTexts = cloneState(s.coverUserTexts || []);\n    // Restore the ENTIRE coverState (front/back/spine/bleed photos, frames, layouts,\n    // shapes, rotations, colour, text styles) \u2014 not just a handful of fields.\n    if (s.coverState) Object.keys(s.coverState).forEach((k) => { coverState[k] = cloneState(s.coverState[k]); });\n    // Re-sync all dependent UI, then re-render.\n    try { syncCoverInputsFromState(); } catch (e) {}\n    try { _syncSpreadAdderUI(); } catch (e) {}\n    try { updatePrice(); } catch (e) {}\n    try { refreshPhotoRule(); } catch (e) {}\n    try { syncBuilderModeButtons(); } catch (e) {}\n    try { _dmbInvalidateSibCache(); } catch (e) {}\n    renderBuilderCanvas();\n    renderThumbsRail();\n    renderPhotoTray();\n    autoSave();\n    try { _dmbReapplyAllContains(); } catch (e) {}\n    try { _dmbPvReinitAll(); } catch (e) {}\n  }\n\n  function pushUndoState(actionLabel = \"Edited\") {\n    _dmbUserEdited = true;      // a real edit happened \u2192 cloud saving is now allowed\n    _dmbInvalidateSibCache();   // objects changed \u2192 snap-sibling cache is stale\n    undoStack.push(_dmbCaptureState(actionLabel));\n\n    if (undoStack.length > UNDO_LIMIT) undoStack.shift();\n    redoStack = [];\n    autoSave();\n    // ALSO flush the restore-key snapshot ~immediately, so a refresh right after an\n    // edit can't lose it (autoSave alone waits 800ms + 600ms and each new edit cancels\n    // the pending timer \u2014 rapid edits then refreshed within ~1.4s were never written,\n    // e.g. \"only the background persisted, stickers/map/text gone\"). IMPORTANT: use a\n    // MICROTASK, not a synchronous call \u2014 pushUndoState runs BEFORE the caller mutates\n    // editor (it snapshots the PRE-edit state for undo), so a synchronous write here\n    // would persist the OLD state (missing the just-added/removed object). The\n    // microtask runs after the caller's synchronous add/remove + render finish, so it\n    // captures the POST-edit state, and it still fires before the browser can process\n    // a refresh. queueMicrotask isn't cancellable, so rapid edits each get saved.\n    try {\n      queueMicrotask(function () { try { _dmbWriteProgressState(); } catch (e) {} });\n    } catch (e) { try { _dmbWriteProgressState(); } catch (_) {} }\n    // Re-apply contain clips \u2014 position/size/zone may have changed\n    try { _dmbReapplyAllContains(); } catch(e) {}\n\n    // \u2500\u2500 Dirty-mark the PDF preview cache \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // pushUndoState is called for most meaningful changes (sticker moves,\n    // photo changes, text edits, template changes, shape resizes, map edits).\n    // NOTE: spread photo pan/zoom drag does NOT call pushUndoState \u2014 that\n    // case is handled separately in the spread photo mouseup handler.\n    try {\n      if (builderMode === \"cover\") {\n        _dmbPvMarkDirty(\"cover\");\n      } else if (editor.currentSpread === -1) {\n        _dmbPvMarkDirty(0);\n      } else {\n        _dmbPvMarkDirty(editor.currentSpread * 2 + 1);\n        _dmbPvMarkDirty(editor.currentSpread * 2 + 2);\n      }\n    } catch(e) {}\n  }\n\n  // \u2500\u2500 Text style edits: snappy live apply + deferred heavy commit \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // Changing a text box's font / size / colour used to run the FULL heavy path on\n  // every keystroke (a whole-design JSON snapshot + a before-paint _dmbWriteProgressState\n  // that reads, parses and rewrites the entire saved design in localStorage), which\n  // blocked the repaint \u2014 the change only appeared seconds later. Now the style is\n  // applied + painted immediately; one undo snapshot is taken per burst; and the heavy\n  // persistence is debounced to run once after the user stops (and flushed on\n  // deselect / page-hide / selection change so nothing is ever lost).\n  let _dmbTextStyleBurst = false;\n  let _dmbTextStyleTimer = null;\n  let _dmbTextStylePending = null;\n  function _dmbTextStyleBeginUndo() {\n    if (_dmbTextStyleBurst) return;\n    _dmbTextStyleBurst = true;\n    try {\n      _dmbInvalidateSibCache();\n      undoStack.push(_dmbCaptureState(\"Text Edit\"));\n      if (undoStack.length > UNDO_LIMIT) undoStack.shift();\n      redoStack = [];\n    } catch (e) {}\n  }\n  function _dmbTextStyleScheduleCommit(node) {\n    if (node) _dmbTextStylePending = node;\n    clearTimeout(_dmbTextStyleTimer);\n    _dmbTextStyleTimer = setTimeout(_dmbTextStyleCommit, 450);\n  }\n  function _dmbTextStyleCommit() {\n    clearTimeout(_dmbTextStyleTimer);\n    const node = _dmbTextStylePending;\n    _dmbTextStyleBurst = false;\n    _dmbTextStylePending = null;\n    if (!node || !node.isConnected) return;\n    try { saveCoverTextNodeState(node); } catch (e) {}\n    try { savePageTextState(node); } catch (e) {}\n    try { saveCoverDesignToStorage(); } catch (e) {}\n    try { autoSave(); } catch (e) {}\n    try { _dmbWriteProgressState(); } catch (e) {}\n    try { if (typeof _dmbCloudPersist === \"function\") _dmbCloudPersist(); } catch (e) {}\n    try { _dmbReapplyAllContains(); } catch (e) {}\n    try {\n      if (builderMode === \"cover\") _dmbPvMarkDirty(\"cover\");\n      else if (editor.currentSpread === -1) _dmbPvMarkDirty(0);\n      else { _dmbPvMarkDirty(editor.currentSpread * 2 + 1); _dmbPvMarkDirty(editor.currentSpread * 2 + 2); }\n    } catch (e) {}\n  }\n  function _dmbTextStyleCancelBurst() {\n    clearTimeout(_dmbTextStyleTimer);\n    _dmbTextStyleBurst = false;\n    _dmbTextStylePending = null;\n  }\n\n  function undoLastAction() {\n    if (!undoStack.length) return;\n    try { _dmbTextStyleCancelBurst(); } catch (e) {}\n    // Capture the CURRENT (post-edit) state onto the redo stack, then restore the prior one.\n    redoStack.push(_dmbCaptureState(\"Redo\"));\n    if (redoStack.length > UNDO_LIMIT) redoStack.shift();\n    _dmbRestoreState(undoStack.pop());\n  }\n\n  function redoLastAction() {\n    if (!redoStack.length) return;\n    try { _dmbTextStyleCancelBurst(); } catch (e) {}\n    undoStack.push(_dmbCaptureState(\"Undo\"));\n    if (undoStack.length > UNDO_LIMIT) undoStack.shift();\n    _dmbRestoreState(redoStack.pop());\n  }\n  // ---------- cover storage ----------\n  function saveCoverToStorage(cover) {\n    try { localStorage.setItem(\"dmb_selected_cover\", JSON.stringify(cover)); } catch {}\n  }\n\n  function loadCoverFromStorage() {\n    try {\n      const raw = localStorage.getItem(\"dmb_selected_cover\");\n      if (!raw) return null;\n      return JSON.parse(raw);\n    } catch {\n      return null;\n    }\n  }\n\n  function syncStepVisibility() {\n    if (!step1 || !builderStep) return;\n\n    const builderVisible = getComputedStyle(builderStep).display !== \"none\";\n    if (builderVisible) {\n      step1.style.display = \"none\";\n      if (bookDetails) bookDetails.style.display = \"none\";\n    }\n  }\n\n  syncStepVisibility();\n\n  try {\n    const obs = new MutationObserver(syncStepVisibility);\n    obs.observe(builderStep, { attributes: true, attributeFilter: [\"style\", \"class\"] });\n  } catch {}\n\n  document.addEventListener(\"click\", () => setTimeout(syncStepVisibility, 0), true);\n\n  if (mode !== \"customize\" && (coverTitleParam || coverImgParam || coverHandleParam)) {\n    saveCoverToStorage({\n      title: coverTitleParam || \"\",\n      img: coverImgParam || \"\",\n      handle: coverHandleParam || \"\"\n    });\n  }\n\n  if (mode === \"customize\") {\n    try { localStorage.removeItem(\"dmb_selected_cover\"); } catch {}\n    try { sessionStorage.removeItem(\"dmb_selected_cover\"); } catch {}\n  }\n\n  const storedCover = loadCoverFromStorage();\n  const hasCoverInUrl = !!(coverTitleParam || coverImgParam || coverHandleParam);\n\n  try {\n    const noStep = !stepParam;\n    const noMode = !mode;\n    const noCoverParams = !hasCoverInUrl;\n\n    if (!_dmbRenderMode() && noStep && noMode && noCoverParams) {\n      localStorage.removeItem(\"dmb_selected_cover\");\n      sessionStorage.removeItem(\"dmb_selected_cover\");\n      localStorage.removeItem(\"dmb_mode\");\n    }\n  } catch {}\n\n  const shouldShowBuilder =\n    stepParam === \"2\" ||\n    mode === \"customize\" ||\n    hasCoverInUrl ||\n    restoreParam === \"1\";   // \u2190 \"Continue editing\" from saved books page\n\n  const shouldShowUpload = stepParam === \"upload\" && restoreParam !== \"1\";\n\n  // Issue 1: on a fresh load (not already restoring or opening the builder),\n  // if saved progress exists, offer to resume it instead of re-uploading.\n  // Signed-in customers only \u2014 guests don't get a resume prompt (same rule as\n  // the in-builder offer).\n  if (restoreParam !== \"1\" && !shouldShowBuilder && window.DMB_CUSTOMER_ID) {\n    setTimeout(async () => {\n      try {\n        const _sv = loadCustomerProgress();\n        if (_sv && _sv.editor && (_sv.editor.totalPages || _sv.pages_selected)) {\n          _dmbShowResumeBanner();\n          return;\n        }\n        // No local copy (different device, or the browser evicted it) \u2014 the design\n        // may still be safe in the customer's account. Offer to resume it from cloud.\n        if (_dmbCloud.enabled()) { _dmbAutoCloudRestore(); }\n      } catch (e) {}\n    }, 800);\n  }\n\n  // Builder opened DIRECTLY (refresh, or a ?cover=/template link) for a signed-in\n  // customer \u2014 the auto-open shows a fresh builder and suppresses the in-builder\n  // resume offer. If THIS design has a saved cloud copy, surface the resume banner so\n  // their work is one click away (and clearly not lost). Offer-only: it only appears\n  // when the current context MATCHES a saved design, so a brand-new design isn't nagged.\n  if (restoreParam !== \"1\" && shouldShowBuilder && window.DMB_CUSTOMER_ID) {\n    setTimeout(async () => {\n      try {\n        if (!_dmbCloud.enabled()) return;\n        const list = await _dmbCloud.list();\n        const curId = _dmbCloud.designId();\n        const m = list && list.find((d) => d && d.id === curId);\n        if (m) _dmbOfferCloudResume(m);\n      } catch (e) {}\n    }, 1600);\n  }\n\n  if (_dmbRenderMode()) {\n    // \u2500\u2500 RENDER MODE (Step 2, full-quality server render) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // A headless job page: show the builder shell (renderCoverCanvas needs\n    // #spreadCanvas, and the engine reads #coverOverlay z-index for cover z-order)\n    // and hand off to the render module at the end of this file. No upload step, no\n    // ?restore=1 flow, no landing page, no resume offers, no tours.\n    step1.style.display = \"none\";\n    builderStep.style.display = \"block\";\n    if (bookDetails) bookDetails.style.display = \"none\";\n    { const _s1b = document.getElementById(\"step1b\"); if (_s1b) _s1b.style.display = \"none\"; }\n    _dmbRenderModeBoot();      // hoisted; yields a macrotask before touching anything\n  } else if (shouldShowUpload) {\n    step1.style.display = \"none\";\n    builderStep.style.display = \"none\";\n    if (bookDetails) bookDetails.style.display = \"none\";\n    const s1b = document.getElementById(\"step1b\");\n    if (s1b) s1b.style.display = \"flex\";\n    try { initPhoneUpload(); } catch(e) {}\n    // Heads-up on landing: photos upload in batches of 30.\n    setTimeout(function () { try { _dmbShowLimitNotice(); } catch (e) {} }, 500);\n\n    // Restore on the upload step:\n    //  \u2022 Signed-in \u2192 a DISCREET inline card (not a popup), and ONLY when a saved\n    //    design exists for THIS EXACT book. See _dmbShowRestoreThisBook.\n    //  \u2022 Guest who REFRESHED this step \u2192 restore SILENTLY (no popup), so an accidental\n    //    refresh doesn't lose their just-picked photos. A guest arriving FRESH gets\n    //    nothing (their provisional work was already discarded \u2014 no resume by design).\n    if (window.DMB_CUSTOMER_ID) {\n      setTimeout(function () { try { _dmbShowRestoreThisBook(); } catch (e) {} }, 600);\n    } else if (_dmbWasReload() || _DMB_TAB_CONTINUED) {\n      setTimeout(function () { try { _dmbInstantBackup.restoreSilently(); } catch (e) {} }, 600);\n    }\n\n    // Prefetch the cover template NOW (the handle is already in the URL / magazine\n    // session) so it's cached and applies instantly when the builder opens \u2014\n    // removes the blank-cover window when the at-entry fetch is slow/flaky.\n    try {\n      const _warmHandles = [];\n      if (coverHandleParam) _warmHandles.push(coverHandleParam);\n      try { const _mh = sessionStorage.getItem(\"dmb_mag_template\"); if (_mh && _warmHandles.indexOf(_mh) < 0) _warmHandles.push(_mh); } catch (e) {}\n      _warmHandles.forEach(function (h) { try { _dmbWarmTemplate(h); } catch (e) {} });\n    } catch (e) {}\n\n    // \u2500\u2500 Mobile add-photos notice \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // Phones only (iPad/laptop are the smoother devices). Dismissible popup,\n    // shown every time the add-photos step is reached.\n    function _dmbShowUploadMobileNotice() {\n      if (window.innerWidth > 749) return;                       // phones only\n      if (document.getElementById(\"dmbUploadMobileNotice\")) return;\n      const ov = document.createElement(\"div\");\n      ov.id = \"dmbUploadMobileNotice\";\n      ov.style.cssText = \"position:fixed;inset:0;z-index:1000000;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,0.55);padding:20px;\";\n      ov.innerHTML =\n        '<div style=\"background:#fff;border-radius:18px;max-width:380px;width:100%;padding:26px 22px 22px;box-shadow:0 20px 60px rgba(0,0,0,.35);text-align:center;position:relative;\">' +\n          '<button type=\"button\" id=\"dmbUploadNoticeX\" aria-label=\"Close\" style=\"position:absolute;top:12px;right:12px;width:30px;height:30px;border:none;background:#f1f1f1;border-radius:999px;font-size:18px;line-height:1;cursor:pointer;color:#444;padding:0;\">&times;</button>' +\n          '<div style=\"font-size:34px;margin-bottom:8px;\">&#128241;</div>' +\n          '<h3 style=\"margin:0 0 10px;font-size:18px;font-weight:800;color:#1e1e1e;\">A quick note before you upload</h3>' +\n          '<p style=\"margin:0 0 14px;font-size:14px;line-height:1.55;color:#555;\">Uploading a large batch of photos &mdash; or very high-resolution images &mdash; from a phone can sometimes be slow or interrupted. For the smoothest experience, we gently recommend designing on a <strong>laptop or iPad</strong>.</p>' +\n          '<p style=\"margin:0 0 18px;font-size:13px;line-height:1.55;color:#888;\">Even when designing on your laptop or iPad, you can upload photos straight from your phone using the &ldquo;Upload from Phone&rdquo; option. &#128156;</p>' +\n          '<button type=\"button\" id=\"dmbUploadNoticeGotIt\" style=\"background:#1e1e1e;color:#fff;border:none;border-radius:10px;padding:12px 26px;font-size:14px;font-weight:800;cursor:pointer;width:100%;\">Got it</button>' +\n        '</div>';\n      document.body.appendChild(ov);\n      const close = () => { try { ov.remove(); } catch(e) {} };\n      ov.querySelector(\"#dmbUploadNoticeX\").addEventListener(\"click\", close);\n      ov.querySelector(\"#dmbUploadNoticeGotIt\").addEventListener(\"click\", close);\n      ov.addEventListener(\"click\", (e) => { if (e.target === ov) close(); });\n    }\n    try { _dmbShowUploadMobileNotice(); } catch(e) {}\n\n    // Register upload step handlers here (early), in case the IIFE fails later.\n    // Both \"Do the Magic\" (auto-arrange) and \"Customize\" (manual) share this flow;\n    // the only difference is whether smartCreateBook() runs at the end.\n    async function _proceedFromUpload(autoArrange) {\n      const errEl = document.getElementById(\"uploadStepError\");\n      const pagesSel = document.getElementById(\"step1bPages\");\n\n      // \u2500\u2500 Guard 1: pages must be selected \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n      if (!pagesSel?.value) {\n        if (errEl) { errEl.textContent = window.DMB_MAGAZINE_MODE ? \"Please select the number of pages for your magazine.\" : \"Please select the number of pages for your book.\"; errEl.style.display = \"block\"; }\n        return;\n      }\n\n      // \u2500\u2500 Guard 2: minimum photos (base pages \u2212 4) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n      // Extra spreads added later inside the builder do NOT affect this check.\n      const _minRequired = MIN_PHOTOS;   // flat 20, independent of page count\n      const _uploaded    = bookFiles.length;\n      if (_minRequired > 0 && _uploaded < _minRequired) {\n        const _need = _minRequired - _uploaded;\n        if (errEl) {\n          errEl.textContent =\n            `\uD83D\uDCF8 Add ${_need} more photo${_need !== 1 ? \"s\" : \"\"} to start designing! ` +\n            `You have ${_uploaded}, you need at least ${_minRequired}.`;\n          errEl.style.display = \"block\";\n        }\n        return;\n      }\n\n      if (errEl) errEl.style.display = \"none\";\n\n      // NOTE: the R2 instant backup keeps draining in the BACKGROUND \u2014 entering\n      // the builder is just a div swap (no navigation), so the queue survives.\n      // Never block the customer on it (an early gate here made big batches feel\n      // stuck for minutes). Failed uploads retry silently; the badge shows state.\n      try { _dmbInstantBackup.retryFailed(); } catch (e) {}\n\n      if (pagesEl) pagesEl.value = pagesSel.value;\n      if (s1b) s1b.style.display = \"none\";\n      if (step1) step1.style.display = \"none\";\n      if (builderStep) builderStep.style.display = \"block\";\n      // We're actively starting/continuing THIS design \u2014 don't let the in-builder\n      // \"Resume from where you left off?\" banner pop up over it.\n      _dmbSuppressResumeOffer = true;\n      try { await openEditor(); } catch(err) { console.error(\"openEditor failed:\", err); }\n      if (window.DMB_MAGAZINE_MODE) {\n        const magHandle = sessionStorage.getItem(\"dmb_mag_template\");\n        if (magHandle) {\n          sessionStorage.removeItem(\"dmb_mag_template\");\n          // Persist handle in URL so saveCurrentTemplate() names the file correctly\n          try {\n            const _u = new URL(window.location.href);\n            _u.searchParams.set(\"cover\", magHandle);\n            history.replaceState(null, \"\", _u.toString());\n          } catch {}\n          try { await dmbApplyCoverTemplate(magHandle); } catch(err) {}\n        }\n      } else {\n        if (coverHandleParam) {\n          try { await dmbApplyCoverTemplate(coverHandleParam); } catch(err) { console.warn(\"Cover template apply failed:\", err); }\n        }\n      }\n\n      // \"Do the Magic\" auto-arranges the photos across the interior pages;\n      // \"Customize\" leaves the pages empty for manual placement. Either way\n      // everything stays fully editable afterward.\n      if (autoArrange) {\n        try { smartCreateBook(); } catch(err) { console.warn(\"smartCreateBook failed:\", err); }\n      }\n\n      // Rewrite the URL so a page REFRESH re-enters the builder and restores this\n      // design, instead of dropping back to the upload step (the old behaviour:\n      // the URL stayed ?step=upload). The design (localStorage) and photos\n      // (IndexedDB) are autosaved continuously and flushed on pagehide, so\n      // ?restore=1 rebuilds it. The cover handle (if any) is left intact so the\n      // restore reads the same per-design save key.\n      try {\n        const _u = new URL(window.location.href);\n        _u.searchParams.set(\"step\", \"2\");\n        _u.searchParams.set(\"restore\", \"1\");\n        history.replaceState(null, \"\", _u.toString());\n      } catch (e) {}\n      // Write an initial snapshot now so an instant refresh already has data.\n      try { _dmbWriteProgressState(); _dmbPersistPhotos(); } catch (e) {}\n    }\n\n    const _magicBtn = document.getElementById(\"dmbDoMagicBtn\");\n    if (_magicBtn) _magicBtn.addEventListener(\"click\", function() { _proceedFromUpload(true); });\n\n    const _customizeBtn = document.getElementById(\"dmbCustomizeBtn\");\n    if (_customizeBtn) _customizeBtn.addEventListener(\"click\", function() { _proceedFromUpload(false); });\n\n    const _backBtn = document.getElementById(\"backToHomeBtn\");\n    if (_backBtn) {\n      _backBtn.addEventListener(\"click\", function() {\n        window.location.assign(window.DMB_CARD_MODE ? \"/pages/design-my-book?type=card\" : window.DMB_MAGAZINE_MODE ? \"/pages/design-my-magazine\" : \"/pages/design-my-book\");\n      });\n    }\n\n  } else if (shouldShowBuilder) {\n      step1.style.display = \"none\";\n      builderStep.style.display = \"block\";\n      if (bookDetails) bookDetails.style.display = \"none\";\n\n      // ?restore=1: load saved state, then open editor (so editor inits around\n      // the pre-loaded data), then re-apply state to overwrite anything editor\n      // touched during init, then render.\n      if (restoreParam === \"1\") {\n        _dmbShowRestoreLoader();   // emoji loader until the design + photos finish loading\n        (async () => {\n          let _saved = loadCustomerProgress();\n          // Only BLOCK the restore on the cloud when there is NO usable local copy.\n          // _dmbCloud.load() downloads every photo sequentially (one round-trip per\n          // image) with NO timeout, so awaiting it even when a local design already\n          // exists left signed-in customers staring at an empty builder on refresh \u2014\n          // for tens of seconds on a full book, or FOREVER if a single photo fetch\n          // stalled (which looked exactly like \"everything disappeared\"). Guests never\n          // hit this because they have no cloud, which is why their refresh restores\n          // instantly. A same-device refresh has the whole design in localStorage +\n          // IndexedDB, so restore from that immediately (same fast path as guests).\n          // The cloud is only needed when the local copy is gone (different device /\n          // cleared cache) \u2014 and even then it's time-boxed so a stalled download can\n          // never hang the builder.\n          let _cloud = null;\n          const _haveLocal = !!(_saved && _saved.editor);\n          if (_dmbCloud.enabled() && !_haveLocal) {\n            try {\n              _cloud = await Promise.race([\n                _dmbCloud.load(),\n                new Promise((res) => setTimeout(() => res(null), 45000)),\n              ]);\n            } catch (e) {}\n          }\n          if ((!_saved || !_saved.editor) && _cloud && _cloud.design && _cloud.design.editor) {\n            _saved = _cloud.design;\n          }\n          // GUEST recovery: local storage was evicted mid-session (iOS/Safari can\n          // purge localStorage + IndexedDB) but the R2 backup has the full design.\n          if ((!_saved || !_saved.editor) && !window.DMB_CUSTOMER_ID && window.DMB_DESIGNS_PROXY) {\n            try {\n              let _gsid = \"\"; try { _gsid = _dmbPhotoBackup.guestId(); } catch (e) {}\n              const _gid = getCustomerSaveKey().replace(/^dmb_progress_/, \"\");\n              if (_gsid && _gid) {\n                const _gr = await Promise.race([\n                  fetch(window.DMB_DESIGNS_PROXY + \"/load-guest?id=\" + encodeURIComponent(_gid) + \"&sid=\" + encodeURIComponent(_gsid), { credentials: \"same-origin\" }),\n                  new Promise((res) => setTimeout(() => res(null), 15000)),\n                ]);\n                if (_gr && _gr.ok) { const _gd = await _gr.json(); if (_gd && _gd.design && _gd.design.editor) _saved = _gd.design; }\n              }\n            } catch (e) {}\n          }\n          if (!_saved || !_saved.editor) {\n            try { await openEditor(); } catch(e) {}\n            _dmbHideRestoreLoader();\n            return;\n          }\n          if (saveMsg) { saveMsg.textContent = \"Restoring your design\u2026\"; saveMsg.style.display = \"block\"; }\n\n          const _cl = v => { try { return JSON.parse(JSON.stringify(v || {})); } catch(e) { return {}; } };\n          const _se = _saved.editor || {};\n          _dmbApplyRecentColors(_saved);   // sync the colour palette (cross-device)\n          const _pg = _se.totalPages || Number(_saved.pages_selected) || 0;\n          // The #pages dropdown holds the BASE count (a real option), NOT the total\n          // that includes added spreads (e.g. 32) \u2014 setting it to a non-option value\n          // selects nothing and renderSpread then reads 0 \u2192 blank \"Spread 1/1\". Use\n          // the base; extraSpreads (restored below) adds the rest back.\n          const _base = Number(_saved.pages_selected) || Math.max(0, _pg - (_se.extraSpreads || 0) * 2) || _pg;\n          try { const _s1bP = document.getElementById(\"step1bPages\"); if (_s1bP && _base) _s1bP.value = String(_base); } catch (e) {}\n\n          // \u2500\u2500 Phase 1: pre-load state so openEditor sees it \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n          // MERGE cloud + local IndexedDB photos (union by id). The cloud photo\n          // manifest can LAG the local copy \u2014 the account save is debounced and is\n          // NOT flushed on leave \u2014 so cloud alone may be missing recently-added\n          // photos, while the local design (loaded above) still references them\n          // (\u2192 error/white boxes on pages, missing from the tray). Taking the union\n          // guarantees every photo the design references resolves. Prefer the cloud\n          // copy for shared ids; fill any gaps from IDB.\n          let _cloudPhotos = (_cloud && Array.isArray(_cloud.photos)) ? _cloud.photos : [];\n          let _idbPhotos = [];\n          try { _idbPhotos = (await _loadPhotosFromIDB(getCustomerSaveKey())) || []; } catch (e) {}\n          let _rPhotos;\n          if (_cloudPhotos.length && _idbPhotos.length) {\n            const _pById = new Map();\n            _idbPhotos.forEach(function (p) { if (p && p.id) _pById.set(p.id, p); });\n            _cloudPhotos.forEach(function (p) { if (p && p.id) _pById.set(p.id, p); }); // cloud wins on overlap\n            _rPhotos = Array.from(_pById.values());\n          } else {\n            _rPhotos = _cloudPhotos.length ? _cloudPhotos : _idbPhotos;\n          }\n          // Align restored photo IDs with the saved layout so placed photos\n          // resolve (otherwise spreads render blank/white while the tray is fine).\n          _dmbReconcilePhotoIds(_rPhotos, _se);\n          if (_rPhotos.length) {\n            editor.photos = _rPhotos;\n            try { bookFiles = _rPhotos.map(p => p.file || new File([], p.filename || \"photo\")); } catch(e) {}\n          }\n          const _pElR = document.getElementById(\"pages\");\n          if (_pElR && _base) _pElR.value = String(_base);\n\n          // \u2500\u2500 Phase 2: open editor (UI init only \u2014 skip state reset) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n          // skipStateReset=true preserves the IDB photos loaded in Phase 1\n          // and prevents openEditor from clearing editor.coverObjects / pageSlots.\n          editor.totalPages    = _pg;\n          editor.editablePages = _se.editablePages || Math.max(0, _pg - 1);\n          editor.totalSpreads  = _se.totalSpreads  || Math.max(1, Math.ceil(_pg / 2));\n          try { await openEditor({ skipStateReset: true }); } catch(e) { console.warn(\"[DMB] restore openEditor:\", e); }\n\n          // \u2500\u2500 Phase 3: apply ALL saved values (SHARED with Step-2 render mode) \u2500\u2500\n          //    (belt-and-suspenders: openEditor may still have touched some fields)\n          // Re-apply IDB photos FIRST \u2014 openEditor() above overwrote editor.photos with\n          // empty bookFiles \u2014 so _dmbApplySavedEditorState can rehydrate cover URLs by id.\n          if (_rPhotos.length) {\n            editor.photos = _rPhotos;\n            try { bookFiles = _rPhotos.map(p => p.file || new File([], p.filename || \"photo\")); } catch(e) {}\n          }\n          _dmbApplySavedEditorState(_saved);\n          if (_pElR && _base) _pElR.value = String(_base);\n\n          // Reattach sticker/icon crop images (base64) from IndexedDB onto the\n          // restored objects \u2014 they were stripped from the localStorage state.\n          try { _dmbReattachObjImages(await _dmbLoadObjImagesFromIDB(getCustomerSaveKey())); } catch(e) {}\n\n          // \u2500\u2500 Phase 4: sync UI inputs then render \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n          try { syncCoverInputsFromState(); }  catch(e) {}\n          try { updatePrice(); }               catch(e) {}\n          try { _syncSpreadAdderUI(); }        catch(e) {}     // reflect restored added spreads\n          try { _dmbSyncOrientationToggleUI(); } catch(e) {}   // reflect restored orientation\n          try { renderPhotoTray(); }           catch(e) {}\n          try { renderThumbsRail(); }          catch(e) {}\n          // Always render the cover (Phase 3 restored coverState + coverObjects\n          // but renderBuilderCanvas only renders the cover when builderMode=\"cover\").\n          // Since saved builderMode is typically \"pages\", explicitly render both.\n          try { renderCoverCanvas(); }         catch(e) {}\n          try { renderBuilderCanvas(); }       catch(e) {}\n          try { _dmbHideRestoreLoader(); }     catch(e) {}   // design is on screen now\n          // The book is on screen (rendered from just its placed photos). Now pull the\n          // REST of the book's photo library into the tray in the background \u2014 safe,\n          // because cloud saving is edit-gated so this can't overwrite anything.\n          try { _dmbCloud.loadPendingLib(() => { try { renderPhotoTray(); } catch (e) {} }); } catch (e) {}\n\n          if (saveMsg) saveMsg.style.display = \"none\";\n          _dmbToast(_rPhotos.length\n            ? `Restored \u2014 ${_rPhotos.length} photos + full layout`\n            : \"Layout restored (re-upload photos to fill pages)\");\n\n          // ADDITIVE reconcile: same-device refreshes use the local IDB copy only\n          // (cloud is skipped when a local copy exists), so a partial IndexedDB\n          // eviction would still leave placed photos missing here. Pull back any\n          // referenced-but-absent photo from R2 by id, then re-render if we did.\n          try {\n            const _rec = await _dmbReconcileMissingPhotosFromCloud();\n            if (_rec > 0) {\n              try { bookFiles = editor.photos.map((p) => p.file || new File([], p.filename || \"photo\")); } catch (e) {}\n              try { renderPhotoTray(); }     catch (e) {}\n              try { renderThumbsRail(); }    catch (e) {}\n              try { renderCoverCanvas(); }   catch (e) {}\n              try { renderBuilderCanvas(); } catch (e) {}\n              if (saveMsg) saveMsg.style.display = \"none\";\n              _dmbToast(`Restored \u2014 recovered ${_rec} photo${_rec === 1 ? \"\" : \"s\"} from the cloud`);\n            }\n          } catch (e) {}\n        })();\n      }\n    } else {\n      step1.style.display = \"flex\";\n      builderStep.style.display = \"none\";\n      if (bookDetails) bookDetails.style.display = \"none\";\n    }\n  const coverToShow =\n    mode !== \"customize\"\n      ? (coverImgParam || coverTitleParam\n          ? { title: coverTitleParam || \"\", img: coverImgParam || \"\" }\n          : storedCover)\n      : null;\n\n  if (selectedCoverBox && selectedCoverImg && selectedCoverTitle) {\n    if (mode !== \"customize\" && coverToShow && (coverToShow.img || coverToShow.title)) {\n      selectedCoverBox.style.display = \"flex\";\n      if (coverToShow.img) selectedCoverImg.src = coverToShow.img;\n      selectedCoverImg.alt = coverToShow.title || \"Selected cover\";\n      selectedCoverTitle.textContent = coverToShow.title || \"\";\n    } else {\n      selectedCoverBox.style.display = \"none\";\n    }\n  }\n\n  // ---------- Step 2 ----------\n  function updatePrice() {\n    if (!pagesEl || !priceBox || !oldPriceEl || !newPriceEl) return;\n    // Magazines price from their own product; books (and magazine-fallback) use PRICES.\n    let data = null;\n    if (window.DMB_MAGAZINE_MODE) {\n      const me = _dmbMagEntry(pagesEl.value, editor.bookSize);\n      if (me) data = { old: me.old, new: me.new };\n    }\n    if (!data) {\n      const _sizeTbl = PRICES[editor.bookSize || \"A4\"] || PRICES.A4;\n      data = _sizeTbl[pagesEl.value];\n    }\n    if (data) {\n      // Add extra spread cost ($1.99 \u00D7 extraSpreads) on top of base price\n      const extraCost = (editor.extraSpreads || 0) * EXTRA_SPREAD_PRICE;\n      // Only show the struck-through \"old\" price when there's a real compare-at.\n      const showOld = data.old && data.old > data.new;\n      oldPriceEl.textContent = showOld ? fmtMoney(data.old + extraCost) : \"\";\n      oldPriceEl.style.display = showOld ? \"\" : \"none\";\n      newPriceEl.textContent = fmtMoney(data.new + extraCost);\n      priceBox.style.display = \"block\";\n    } else {\n      priceBox.style.display = \"none\";\n    }\n  }\n\n  function makeThumbWithRemove(file, idx, onRemove) {\n    const wrap = document.createElement(\"div\");\n    wrap.style.position = \"relative\";\n    wrap.style.width = \"70px\";\n    wrap.style.height = \"70px\";\n\n    const img = document.createElement(\"img\");\n    img.src = URL.createObjectURL(file);\n    img.alt = file.name;\n    img.style.width = \"70px\";\n    img.style.height = \"70px\";\n    img.style.objectFit = \"cover\";\n    img.style.borderRadius = \"10px\";\n    img.style.border = \"1px solid #ddd\";\n    wrap.appendChild(img);\n\n    // \u2500\u2500 Circle-X remove button (Fix 1) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    const btn = document.createElement(\"button\");\n    btn.type = \"button\";\n    btn.setAttribute(\"aria-label\", \"Remove image\");\n    btn.innerHTML = `<svg viewBox=\"0 0 24 24\" width=\"12\" height=\"12\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M8 8l8 8M16 8l-8 8\"/></svg>`;\n    btn.style.cssText = \"position:absolute;top:-7px;right:-7px;width:22px;height:22px;border-radius:999px;border:none;background:#111;color:#fff;cursor:pointer;display:flex;align-items:center;justify-content:center;padding:0;\";\n\n    btn.addEventListener(\"click\", (e) => {\n      e.preventDefault();\n      onRemove(idx);\n    });\n\n    wrap.appendChild(btn);\n    return wrap;\n  }\n\n  function renderPreview(filesArr, containerEl, maxThumbs, onRemove) {\n    if (!containerEl) return;\n    containerEl.innerHTML = \"\";\n\n    if (!filesArr.length) {\n      containerEl.style.display = \"none\";\n      return;\n    }\n\n    containerEl.style.display = \"flex\";\n\n    filesArr.slice(0, maxThumbs).forEach((file, idx) => {\n      containerEl.appendChild(makeThumbWithRemove(file, idx, onRemove));\n    });\n\n    if (filesArr.length > maxThumbs) {\n      const more = document.createElement(\"div\");\n      more.textContent = `+${filesArr.length - maxThumbs} more`;\n      more.style.fontSize = \"12px\";\n      more.style.color = \"#666\";\n      more.style.alignSelf = \"center\";\n      containerEl.appendChild(more);\n    }\n  }\n\n  function refreshPhotoRule() {\n    // \u2500\u2500 Minimum photos rule: min = BASE pages \u2212 4 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // IMPORTANT: use pagesEl.value (the dropdown selection), NOT editor.totalPages.\n    // editor.totalPages includes extra spreads added inside the builder, but\n    // the photo upload step requirement must NOT change when spreads are added.\n    // Example: 24 pages \u2192 minimum 20 photos. Adding 2 spreads \u2192 still 20.\n    const pages = Number(pagesEl?.value || 0);\n    if (pages > 0) {\n      currentMin = MIN_PHOTOS;        // flat 20, independent of page count\n      currentMax = pages + 8;\n      if (photoRuleEl) {\n        photoRuleEl.textContent = `Upload at least ${currentMin} photos to start`;\n        photoRuleEl.style.display = \"block\";\n      }\n    } else {\n      currentMin = 0;\n      currentMax = 0;\n      if (photoRuleEl) {\n        photoRuleEl.textContent = \"Select pages to see how many photos you need\";\n        photoRuleEl.style.display = \"block\";\n      }\n    }\n    if (photoErrorEl) photoErrorEl.style.display = \"none\";\n    validatePhotos();\n  }\n\n  function validatePhotos() {\n    const count = bookFiles.length;\n    if (photoCountEl) {\n      photoCountEl.style.display = count ? \"block\" : \"none\";\n      photoCountEl.textContent = count ? `${count} photo${count !== 1 ? \"s\" : \"\"} selected` : \"\";\n    }\n    if (photoErrorEl) {\n      if (currentMin > 0 && count < currentMin) {\n        const need = currentMin - count;\n        photoErrorEl.textContent =\n          `\uD83D\uDCF8 Add ${need} more photo${need !== 1 ? \"s\" : \"\"} to start designing! ` +\n          `You have ${count}, you need at least ${currentMin}.`;\n        photoErrorEl.style.display = \"block\";\n      } else {\n        photoErrorEl.style.display = \"none\";\n      }\n    }\n    return count > 0 && (currentMin === 0 || count >= currentMin);\n  }\n\n  window.editorAutoOpened = window.editorAutoOpened || false;\n\n  async function tryAutoOpenEditor() {\n    const selectedPages = Number(pagesEl?.value || 0);\n\n    if (!selectedPages || bookFiles.length === 0) {\n      window.editorAutoOpened = false;\n      return;\n    }\n\n    // \u2500\u2500 Minimum photo check: block auto-open if not enough photos \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // Minimum = base pages \u2212 4 (extra spreads added inside the builder are\n    // NOT factored in \u2014 this check is for the upload step only).\n    const minNeeded = selectedPages > 0 ? MIN_PHOTOS : 0;\n    if (minNeeded > 0 && bookFiles.length < minNeeded) {\n      window.editorAutoOpened = false;\n      // Ensure the error message is visible\n      refreshPhotoRule();\n      return;\n    }\n\n    // Don't reset if builder is already open with content\n    const alreadyOpen =\n      builderStep &&\n      builderStep.style.display === \"block\" &&\n      editor.totalPages > 0;\n\n    if (alreadyOpen) return;\n\n    const alreadyMatching = editor.totalPages === selectedPages;\n\n    if (window.editorAutoOpened && alreadyMatching) return;\n\n    window.editorAutoOpened = true;\n    // Same as the button path: we're actively entering THIS design, so suppress\n    // the \"Resume from where you left off?\" banner.\n    _dmbSuppressResumeOffer = true;\n\n    try {\n      await openEditor();\n      // Auto-open must also apply the predesigned cover template \u2014 otherwise the\n      // cover comes up blank (previously only the Do-the-Magic/Customize buttons\n      // applied it). No-op for custom books (their cover-<handle>.json 404s).\n      try {\n        if (window.DMB_MAGAZINE_MODE) {\n          const _mh = sessionStorage.getItem(\"dmb_mag_template\");\n          if (_mh) {\n            sessionStorage.removeItem(\"dmb_mag_template\");\n            try { const _u = new URL(location.href); _u.searchParams.set(\"cover\", _mh); history.replaceState(null, \"\", _u.toString()); } catch (e) {}\n            await dmbApplyCoverTemplate(_mh);\n          }\n        } else if (coverHandleParam) {\n          await dmbApplyCoverTemplate(coverHandleParam);\n        }\n      } catch (e) {}\n    } catch (err) {\n      window.editorAutoOpened = false;\n      console.error(\"tryAutoOpenEditor failed:\", err);\n    }\n  }\n  \n  function validateCustomizeRequired() {\n    if (mode !== \"customize\") return true;\n    return !!getEffectiveBookTitle();\n  }\n\n  function removeBookFileAt(idx) {\n    if (idx < 0 || idx >= bookFiles.length) return;\n    bookFiles.splice(idx, 1);\n    syncInputFiles(photosEl, bookFiles);\n    validatePhotos();\n  }\n\n  if (pagesEl) {\n    pagesEl.addEventListener(\"change\", () => {\n      updatePrice();\n      refreshPhotoRule();\n      initEmptyBookFromPages();\n    });\n  }\n\n  if (!templateParam && restoreParam !== \"1\") {\n    // Only reset to defaults when NOT restoring from saved progress\n    coverState.template = \"default\";\n    coverState.title1 = \"\";\n    coverState.title2 = \"\";\n    coverState.color = \"#ffffff\";\n    coverState.imageUrl = \"\";\n    coverState.imageFile = null;\n    coverState.textStyles = {};\n    editor.coverObjects = [];\n  }\n\n\n  // --- frame rendering helpers (second-IIFE versions, have full access to editor/state) ---\n\n  function dmbGetSnapGuides(cont) {\n    let gH = cont.querySelector(\".dmb-snap-guide-h\");\n    let gV = cont.querySelector(\".dmb-snap-guide-v\");\n    if (!gH) {\n      gH = document.createElement(\"div\");\n      gH.className = \"dmb-snap-guide-h\";\n      cont.appendChild(gH);\n    }\n    if (!gV) {\n      gV = document.createElement(\"div\");\n      gV.className = \"dmb-snap-guide-v\";\n      cont.appendChild(gV);\n    }\n    // NOTE: these two are vestigial - dmbCheckSnap takes gH/gV as parameters and never\n    // touches them. Every line the customer sees comes from the POOLED guides, which are\n    // hosted on the spread row so they can cross the fold and out-stack both pages.\n    return { gH, gV };\n  }\n\n  // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n  // Fix 6 \u2014 Smart alignment guides (object-to-object + container centre)\n  // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n  //\n  // `dmbCheckSnap` is called during every drag pointermove. It:\n  //   1. Gathers sibling objects on the same page/cover\n  //   2. Computes 5 reference edges per object (left, centreX, right, top, centreY, bottom)\n  //   3. Snaps obj.x / obj.y when within SNAP_T px of any reference\n  //   4. Shows dynamic guide lines at each matched edge\n  //\n  // Guide-line pool: we lazily create <div> elements inside the container and\n  // reuse them across frames to avoid DOM churn (max ~8 visible at once).\n\n  const _SNAP_T = 4;                 // snap threshold in px (snap when within 4px, jump exactly onto the line)\n  const _GUIDE_HIDE_MS = 500;        // guides stay visible 500 ms after drag stops\n\n  // Where a snap overlay must LIVE to be able to draw over both pages.\n  //\n  // Raising the z-index was not enough, and the reason is worth writing down. A new\n  // item's z is floored at Date.now(), which CSS clamps to the 32-bit maximum, so the\n  // overlays and the items end up on the SAME z-index and the winner is decided by DOM\n  // order. Parented inside the left page, a guide comes before every item in the right\n  // page, so it always lost there - which is exactly what it looked like: fine over the\n  // page being dragged on, behind the item it was measuring against.\n  //\n  // Hosting the overlay on the spread row fixes it structurally: a child appended to the\n  // row comes after BOTH page divs, so it wins the tie over either page, and it beats a\n  // lifted item (z 9000, also a child of the row) on z-index outright. It also means a\n  // horizontal guide simply spans the row - no negative-inset trickery.\n  //\n  // Returns the host plus the page's offset inside it, which every caller adds to its\n  // page-space coordinates. Falls back to the container (cover, page 0, previews) where\n  // there is no spread row, and the offsets are then zero and nothing changes. (2026-09-18)\n  function _dmbOverlayHost(container) {\n    try {\n      const pageEl = container && container.parentElement;\n      const row = pageEl && pageEl.parentElement;\n      if (row && row.dataset && row.dataset.spreadRow) {\n        return { host: row, ox: pageEl.offsetLeft || 0, oy: pageEl.offsetTop || 0 };\n      }\n    } catch (e) {}\n    return { host: container, ox: 0, oy: 0 };\n  }\n\n  function _dmbGetGuidePool(container) {\n    const host = _dmbOverlayHost(container).host;\n    if (!host._snapPool) host._snapPool = [];\n    return host._snapPool;\n  }\n\n  // axis: \"h\" (horizontal = constant Y) or \"v\" (vertical = constant X)\n  // isCenter: true \u2192 dashed line (centre-to-centre snap), false \u2192 solid (edge snap)\n  function _dmbAcquireGuide(container, axis, isCenter) {\n    const pool = _dmbGetGuidePool(container);\n    let el = pool.find(g => g._axis === axis && g._inUse === false);\n    if (!el) {\n      el = document.createElement(\"div\");\n      el._axis = axis;\n      el.style.pointerEvents = \"none\";\n      el.style.position = \"absolute\";\n      el.style.zIndex = \"2147483647\";   // 32-bit max: items clamp here too, DOM order wins (see note)\n      el.style.opacity = \"0\";\n      el.style.transition = \"opacity 0.08s ease\";\n      if (axis === \"h\") {\n        el.style.left = \"0\"; el.style.right = \"0\"; el.style.height = \"2px\";\n      } else {\n        el.style.top = \"0\"; el.style.bottom = \"0\"; el.style.width = \"2px\";\n      }\n      // On the spread row, left:0/right:0 already spans BOTH pages, and top:0/bottom:0\n      // spans the full sheet height - the row is exactly the spread.\n      _dmbOverlayHost(container).host.appendChild(el);\n      pool.push(el);\n    }\n    el._inUse = true;\n    // Re-apply class every acquisition so dashed/solid toggles correctly on reuse\n    const base = axis === \"h\" ? \"dmb-snap-guide-h dmb-snap-guide-dynamic\" : \"dmb-snap-guide-v dmb-snap-guide-dynamic\";\n    el.className = isCenter ? base + \" dmb-snap-guide-center\" : base;\n    // Re-assert the spans on every acquisition: the pool hands the same element back to\n    // different drags, and an earlier attempt at this problem used to write negative\n    // insets here.\n    if (axis === \"h\") { el.style.left = \"0\"; el.style.right = \"0\"; }\n    else              { el.style.top  = \"0\"; el.style.bottom = \"0\"; }\n    return el;\n  }\n\n  function _dmbReleaseAllGuides(container) {\n    const pool = _dmbGetGuidePool(container);\n    pool.forEach(g => {\n      g._inUse = false;\n      g.style.opacity = \"0\";\n    });\n  }\n\n  // Show alignment guides + snap during a \"lifted\" drag (page item moved to spread-row).\n  // During lift the item is positioned in spread-row coords; snap needs page-body coords.\n  // Returns { dx, dy } \u2014 the snap delta to add to the lifted position.\n  function _dmbLiftSnap(spreadLeft, spreadTop, objW, objH, objId, liftState, gH, gV, targetType, pageNumber) {\n    if (!liftState || !liftState.origParent || !liftState.pagesRow) return { dx: 0, dy: 0 };\n    // Cache the page\u2194spread-row offset once per drag \u2014 the rects don't move while\n    // dragging, so re-reading them every move forced two needless reflows.\n    if (!liftState._snapOff) {\n      // Divide the screen-px offset by the canvas zoom so it's in the SAME model\n      // units as the dragged coords (sSox etc. are already /_Z). Without this the\n      // snapped position drifts by a constant amount whenever zoom \u2260 100%.\n      const _Z = (typeof _nodeOnScreenScale === \"function\" ? (_nodeOnScreenScale(liftState.origParent) || 1) : 1);\n      const _pR = liftState.pagesRow.getBoundingClientRect();\n      const _pb = liftState.origParent.getBoundingClientRect();\n      liftState._snapOff = { x: (_pb.left - _pR.left) / _Z, y: (_pb.top - _pR.top) / _Z };\n    }\n    const _offX = liftState._snapOff.x;\n    const _offY = liftState._snapOff.y;\n    const _so = { id: objId, x: spreadLeft - _offX, y: spreadTop - _offY, w: objW, h: objH };\n    dmbCheckSnap(_so, liftState.origParent, gH, gV, { targetType, pageNumber });\n    return { dx: _so.x - (spreadLeft - _offX), dy: _so.y - (spreadTop - _offY) };\n  }\n\n  // Cache of the snap siblings for the CURRENT drag. dmbCheckSnap runs on every\n  // pointermove and measuring each text sibling's DOM box (scrollWidth/offsetWidth/\n  // offsetHeight via getElementById) forces a layout reflow per sibling per move \u2014\n  // a major touch-lag source. Siblings don't change during a drag, so we cache the\n  // computed list and reuse it; it's invalidated whenever a change is committed\n  // (pushUndoState) so the next drag re-measures fresh.\n  let _dmbSibCache = null; // { key, list }\n  let _dmbSnapGen = 0;     // bumped on every committed change; used to cache container size per drag\n  function _dmbInvalidateSibCache() { _dmbSibCache = null; _dmbSnapGen++; }\n  function _dmbGetSiblingObjects(obj, targetType, pageNumber) {\n    const _ck = targetType + \":\" + (pageNumber == null ? \"_\" : pageNumber) + \":\" + (obj && obj.id);\n    if (_dmbSibCache && _dmbSibCache.key === _ck) return _dmbSibCache.list;\n\n    let arr;\n    if (targetType === \"cover\") {\n      arr = editor.coverObjects || [];\n    } else if (pageNumber != null) {\n      arr = ensurePageObjects(pageNumber);\n      // The two pages of a spread are ONE sheet of paper to the customer, so an item on\n      // the FACING page has to behave like a sibling: same top edge, same centre line,\n      // same equal-spacing run. Snapping only ever saw the owner page, so two frames a\n      // few pixels apart across the fold could not be lined up at all.\n      //\n      // Bring the partner's objects into THIS page's coordinate space by shifting them\n      // one page width; everything downstream - the five reference edges, the centre\n      // lines, the equal-spacing bars, the mm labels - then works unchanged. (2026-09-18)\n      try {\n        const _pn = Number(pageNumber);\n        if (_pn >= 1) {\n          const _isLeftPg = (_pn % 2 === 1);          // spreads are (2k+1, 2k+2)\n          const _sib = _isLeftPg ? _pn + 1 : _pn - 1;\n          if (_sib >= 1 && _sib <= (editor.totalPages || 0)) {\n            const _dx = _isLeftPg ? getPageDims().w : -getPageDims().w;\n            const _sibArr = ensurePageObjects(_sib) || [];\n            arr = arr.concat(_sibArr.map(function (o) {\n              if (!o) return o;\n              // A shifted COPY: the real object must not move just because something on\n              // the other page is being dragged near it.\n              const c = Object.assign({}, o);\n              if (c.type === \"text\") c.left = (parseFloat(c.left || \"0\") + _dx) + \"px\";\n              else                   c.x    = (c.x || 0) + _dx;\n              return c;\n            }));\n          }\n        }\n      } catch (e) {}\n    } else {\n      _dmbSibCache = { key: _ck, list: [] };\n      return _dmbSibCache.list;\n    }\n    // Normalise text objects (they use left/top strings instead of x/y numbers)\n    // so the snap engine can treat every sibling uniformly.  Text is inset by its\n    // padding (2px 6px) so its snap edges are the GLYPH edges (first/last letter),\n    // matching the dragged-text behaviour \u2014 text-to-text aligns letter-to-letter.\n    const _TX_PL = 6, _TX_PT = 2;\n    const list = arr\n      .filter(o => o && o.id !== obj.id)\n      .map(o => {\n        if (o.type !== \"text\") return o;\n        const el = document.getElementById(o.id);\n        const boxW = el ? Math.max(el.scrollWidth, el.offsetWidth) : 132;\n        const boxH = el ? (el.offsetHeight || 34)                 : 34;\n        return {\n          ...o,\n          x: parseFloat(o.left || \"0\") + _TX_PL,\n          y: parseFloat(o.top  || \"0\") + _TX_PT,\n          w: Math.max(0, boxW - _TX_PL * 2),\n          h: Math.max(0, boxH - _TX_PT * 2),\n        };\n      })\n      .filter(o => (o.w || 0) > 4 && (o.h || 0) > 4);\n    _dmbSibCache = { key: _ck, list };\n    return list;\n  }\n\n  // \u2500\u2500 Smart-guide extras: mm conversion + pooled labels & spacing bars \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // Model-px \u2192 millimetres for the surface being edited, so guide labels show the\n  // REAL printed distance (this is a print product; mm is the meaningful unit).\n  function _dmbMmPerPx(container, targetType) {\n    try {\n      const ls = dmbIsLandscape();\n      if (targetType === \"cover\") {\n        const wmm = window.DMB_CARD_MODE ? (ls ? 178 : 127) : (ls ? 297 : 210);\n        return wmm / 300;                       // 300 model-px == one cover face\n      }\n      let wmm;\n      if (window.DMB_CARD_MODE) wmm = ls ? 178 : 127;\n      else if (editor && editor.bookSize === \"A5\") wmm = ls ? 210 : 148;\n      else wmm = ls ? 297 : 210;\n      const px = (container && container.offsetWidth) || 270;\n      return wmm / px;\n    } catch (e) { return 210 / 270; }\n  }\n  // Format a model-px-derived millimetre distance as CENTIMETRES with NO unit suffix\n  // (e.g. 10 mm \u2192 \"1\", 4 mm \u2192 \"0.4\", 15 mm \u2192 \"1.5\").\n  function _dmbFmtCm(mm) {\n    const v = Math.round((mm / 10) * 10) / 10;\n    return v === Math.round(v) ? String(Math.round(v)) : v.toFixed(1);\n  }\n\n  // mm-distance chips \u2014 pooled per container, reused across frames (like the guide lines).\n  function _dmbAcquireLabel(container) {\n    const host = _dmbOverlayHost(container).host;\n    if (!host._snapLabels) host._snapLabels = [];\n    let el = host._snapLabels.find(l => l._inUse === false);\n    if (!el) {\n      el = document.createElement(\"div\");\n      el.className = \"dmb-snap-label\";\n      el.style.position = \"absolute\";\n      el.style.zIndex = \"2147483647\";   // 32-bit max; acquired last, so the chips stay on top\n      el.style.pointerEvents = \"none\";\n      el.style.opacity = \"0\";\n      host.appendChild(el);\n      host._snapLabels.push(el);\n    }\n    el._inUse = true;\n    el.className = \"dmb-snap-label\";\n    return el;\n  }\n  // Spacing bars \u2014 the coloured line drawn along a gap. axis \"x\" = horizontal gap.\n  function _dmbAcquireSpace(container, axis) {\n    const host = _dmbOverlayHost(container).host;\n    if (!host._snapSpaces) host._snapSpaces = [];\n    let el = host._snapSpaces.find(s => s._inUse === false && s._axis === axis);\n    if (!el) {\n      el = document.createElement(\"div\");\n      el._axis = axis;\n      el.style.position = \"absolute\";\n      el.style.zIndex = \"2147483647\";   // 32-bit max; acquired after the guides, so it sits above them\n      el.style.pointerEvents = \"none\";\n      el.style.opacity = \"0\";\n      host.appendChild(el);\n      host._snapSpaces.push(el);\n    }\n    el._inUse = true;\n    el.className = \"dmb-snap-space dmb-snap-space-\" + axis;\n    return el;\n  }\n  function _dmbReleaseExtras(container) {\n    const host = _dmbOverlayHost(container).host;\n    (host._snapLabels || []).forEach(l => { l._inUse = false; l.style.opacity = \"0\"; });\n    (host._snapSpaces || []).forEach(s => { s._inUse = false; s.style.opacity = \"0\"; });\n  }\n  // Draw one spacing bar + mm chip along `axis` from a\u2192b at perpendicular offset `perp`.\n  // `equal` renders it in the \"matched spacing\" highlight colour.\n  function _dmbDrawSpace(container, axis, a, b, perp, mmpp, equal) {\n    const len = b - a;\n    if (len < 1) return;\n    const bar = _dmbAcquireSpace(container, axis);\n    if (equal) bar.className += \" dmb-snap-space-eq\";\n    // a/b/perp arrive in PAGE space; the bar now lives on the spread row, so shift by the\n    // page's offset inside it. Zero everywhere there is no row (cover, page 0, preview).\n    const { ox: _sox, oy: _soy } = _dmbOverlayHost(container);\n    if (axis === \"x\") {\n      bar.style.left = (a + _sox) + \"px\"; bar.style.width = len + \"px\";\n      bar.style.top = (perp + _soy) + \"px\"; bar.style.height = \"\";\n    } else {\n      bar.style.top = (a + _soy) + \"px\"; bar.style.height = len + \"px\";\n      bar.style.left = (perp + _sox) + \"px\"; bar.style.width = \"\";\n    }\n    bar.style.opacity = \"1\";\n    const lab = _dmbAcquireLabel(container);\n    if (equal) lab.className += \" dmb-snap-label-eq\";\n    lab.textContent = _dmbFmtCm(len * mmpp);\n    const mid = (a + b) / 2;\n    if (axis === \"x\") { lab.style.left = (mid + _sox) + \"px\"; lab.style.top = (perp + _soy) + \"px\"; lab.style.transform = \"translate(-50%,-135%)\"; }\n    else              { lab.style.left = (perp + _sox) + \"px\"; lab.style.top = (mid + _soy) + \"px\"; lab.style.transform = \"translate(-50%,-50%)\"; }\n    lab.style.opacity = \"1\";\n  }\n\n  // _noSnap = true \u2192 render the guides/labels for the object's CURRENT position but\n  // do NOT move it (used by arrow-key nudging, which must stay pixel-exact). In that\n  // mode a guide only shows when the object is already essentially on it (\u22640.75px).\n  function dmbCheckSnap(obj, container, gH, gV, _meta, _noSnap) {\n    // _meta = { targetType, pageNumber } \u2014 optional; when absent falls back to container-centre only\n    // Cache the container size per drag (it doesn't change mid-drag) to avoid an\n    // offsetWidth/Height reflow every move. Invalidated via _dmbSnapGen on commits.\n    let cW, cH;\n    if (container._dmbSizeGen === _dmbSnapGen && container._dmbCW) {\n      cW = container._dmbCW; cH = container._dmbCH;\n    } else {\n      cW = container.offsetWidth || 270;\n      cH = container.offsetHeight || 338;\n      container._dmbCW = cW; container._dmbCH = cH; container._dmbSizeGen = _dmbSnapGen;\n    }\n\n    // Dragged object edges\n    const dL = obj.x, dR = obj.x + obj.w, dCx = obj.x + obj.w / 2;\n    const dT = obj.y, dB = obj.y + obj.h, dCy = obj.y + obj.h / 2;\n\n    // Collect snap candidates: { axis:\"x\"|\"y\", objEdge, refEdge }\n    const snapsX = [];\n    const snapsY = [];\n\n    // Container vertical centre (Y mid-point \u2014 same for all targets)\n    snapsY.push({ ref: cH / 2, edges: [dCy], label: \"cc\" });\n\n    // Container edges (top=0, bottom=cH)\n    snapsY.push({ ref: 0,  edges: [dT], label: \"ce\" });\n    snapsY.push({ ref: cH, edges: [dB], label: \"ce\" });\n\n    // Horizontal snap targets depend on target type\n    const targetType = _meta?.targetType;\n    const pageNumber = _meta?.pageNumber;\n\n    if (targetType === \"cover\") {\n      // Cover: snap to each zone's centre and edges\n      const _magFX = window.DMB_MAGAZINE_MODE ? COVER_BACK_W : COVER_FRONT_X;\n      // Back zone: 0 \u2192 COVER_BACK_W\n      snapsX.push({ ref: COVER_BACK_W / 2, edges: [dCx], label: \"cc\" });\n      snapsX.push({ ref: 0,  edges: [dL], label: \"ce\" });\n      snapsX.push({ ref: COVER_BACK_W, edges: [dR], label: \"ce\" });\n      // Front zone: _magFX \u2192 _magFX + 300\n      snapsX.push({ ref: _magFX + 150, edges: [dCx], label: \"cc\" });\n      snapsX.push({ ref: _magFX, edges: [dL], label: \"ce\" });\n      snapsX.push({ ref: _magFX + 300, edges: [dR], label: \"ce\" });\n      // Spine zone (books only)\n      if (!window.DMB_MAGAZINE_MODE && COVER_SPINE_W > 0) {\n        snapsX.push({ ref: COVER_BACK_W + COVER_SPINE_W / 2, edges: [dCx], label: \"cc\" });\n        snapsX.push({ ref: COVER_BACK_W, edges: [dL], label: \"ce\" });\n        snapsX.push({ ref: COVER_BACK_W + COVER_SPINE_W, edges: [dR], label: \"ce\" });\n      }\n    } else {\n      // Page objects: snap to container centre and edges\n      snapsX.push({ ref: cW / 2, edges: [dCx], label: \"cc\" });\n      snapsX.push({ ref: 0,  edges: [dL], label: \"ce\" });\n      snapsX.push({ ref: cW, edges: [dR], label: \"ce\" });\n    }\n\n    // Sibling object edges (alignment candidates)\n    const siblings = (targetType != null) ? _dmbGetSiblingObjects(obj, targetType, pageNumber) : [];\n\n    for (const sib of siblings) {\n      const sL = sib.x, sR = sib.x + sib.w, sCx = sib.x + sib.w / 2;\n      const sT = sib.y, sB = sib.y + sib.h, sCy = sib.y + sib.h / 2;\n      snapsX.push({ ref: sL,  edges: [dL, dR], label: \"obj\" });\n      snapsX.push({ ref: sR,  edges: [dL, dR], label: \"obj\" });\n      snapsX.push({ ref: sCx, edges: [dCx],    label: \"cc\"  }); // centre\u2192dashed\n      snapsY.push({ ref: sT,  edges: [dT, dB], label: \"obj\" });\n      snapsY.push({ ref: sB,  edges: [dT, dB], label: \"obj\" });\n      snapsY.push({ ref: sCy, edges: [dCy],    label: \"cc\"  }); // centre\u2192dashed\n    }\n\n    // Best ALIGNMENT snap per axis\n    let bestX = null, bestXDist = _SNAP_T + 1;\n    for (const s of snapsX) for (const e of s.edges) {\n      const d = Math.abs(e - s.ref);\n      if (d < bestXDist) { bestXDist = d; bestX = { ref: s.ref, edge: e, delta: s.ref - e, kind: s.label }; }\n    }\n    let bestY = null, bestYDist = _SNAP_T + 1;\n    for (const s of snapsY) for (const e of s.edges) {\n      const d = Math.abs(e - s.ref);\n      if (d < bestYDist) { bestYDist = d; bestY = { ref: s.ref, edge: e, delta: s.ref - e, kind: s.label }; }\n    }\n\n    // \u2500\u2500 Distribution / equal-spacing snap \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // Look for a target position that makes the gap to a neighbour EQUAL to an\n    // already-existing gap (centre equally between two items, or continue a chain\n    // with the same spacing). Chosen only when it's closer than the alignment snap.\n    const _ov = (a1, a2, b1, b2) => Math.min(a2, b2) - Math.max(a1, b1);\n    // Every adjacent pair of perpendicularly-overlapping siblings \u2192 a reference gap\n    // {G, a, b, perp, axis}. Collected for BOTH axes so the dragged item can match a\n    // spacing that exists ANYWHERE on the spread, horizontal OR vertical.\n    function _collectRefGaps(sibs, axis) {\n      const out = [];\n      for (const s of sibs) {\n        let nr = null;\n        for (const t of sibs) {\n          if (t === s) continue;\n          if (t.e1 >= s.e2 - 0.5 && _ov(s.p1, s.p2, t.p1, t.p2) > 0) { if (!nr || t.e1 < nr.e1) nr = t; }\n        }\n        if (nr) { const G = nr.e1 - s.e2; if (G > 0.5) { const pa = Math.max(s.p1, nr.p1), pb = Math.min(s.p2, nr.p2); out.push({ G, a: s.e2, b: nr.e1, perp: (pa + pb) / 2, axis }); } }\n      }\n      return out;\n    }\n    // Solve one axis. `mainAxis` = \"x\" or \"y\". `sibs` are on that axis; `refGaps` is the\n    // COMBINED gap list from both axes (so an X move can match a Y spacing and vice versa).\n    function _distCand(mainAxis, aStart, aEnd, aW, dPerp, sibs, refGaps) {\n      let L = null, R = null;\n      for (const s of sibs) {\n        if (_ov(dPerp.p1, dPerp.p2, s.p1, s.p2) <= 0) continue;\n        if (s.e2 <= aStart + 0.5) { if (!L || s.e2 > L.e2) L = s; }\n        else if (s.e1 >= aEnd - 0.5) { if (!R || s.e1 < R.e1) R = s; }\n      }\n      const dMid = (dPerp.p1 + dPerp.p2) / 2;\n      const cands = [];\n      // (a) centre EQUALLY between the left & right neighbour on this axis\n      if (L && R && (R.e1 - L.e2 - aW) >= 0) {\n        const target = (L.e2 + R.e1 - aW) / 2;\n        cands.push({ target, gaps: [{ axis: mainAxis, a: L.e2, b: target, perp: dMid }, { axis: mainAxis, a: target + aW, b: R.e1, perp: dMid }] });\n      }\n      // (b) match ANY reference gap (either axis): place the item that same distance\n      //     from its nearest neighbour, and highlight BOTH the item's gap and the\n      //     original reference gap wherever it lives.\n      for (const rg of refGaps) {\n        if (L) { const target = L.e2 + rg.G; cands.push({ target, gaps: [{ axis: mainAxis, a: L.e2, b: target, perp: dMid }, { axis: rg.axis, a: rg.a, b: rg.b, perp: rg.perp }] }); }\n        if (R) { const target = R.e1 - aW - rg.G; cands.push({ target, gaps: [{ axis: mainAxis, a: target + aW, b: R.e1, perp: dMid }, { axis: rg.axis, a: rg.a, b: rg.b, perp: rg.perp }] }); }\n      }\n      let best = null, bestD = _SNAP_T + 1;\n      for (const c of cands) { const d = Math.abs(c.target - aStart); if (d < bestD) { bestD = d; best = c; } }\n      return best ? { target: best.target, gaps: best.gaps, dist: bestD } : null;\n    }\n\n    let distX = null, distY = null;\n    if (targetType != null && siblings.length) {\n      const sibX = siblings.map(s => ({ e1: s.x, e2: s.x + s.w, p1: s.y, p2: s.y + s.h }));\n      const sibY = siblings.map(s => ({ e1: s.y, e2: s.y + s.h, p1: s.x, p2: s.x + s.w }));\n      const refGaps = _collectRefGaps(sibX, \"x\").concat(_collectRefGaps(sibY, \"y\"));\n      distX = _distCand(\"x\", dL, dR, obj.w, { p1: dT, p2: dB }, sibX, refGaps);\n      distY = _distCand(\"y\", dT, dB, obj.h, { p1: dL, p2: dR }, sibY, refGaps);\n    }\n\n    // Apply the closest of {alignment, distribution} per axis. In _noSnap (arrow-key)\n    // mode nothing moves and a guide only reflects an alignment the object is ALREADY\n    // essentially on (\u22640.75px), so the lines/labels appear exactly when it lines up.\n    const T = _noSnap ? 0.75 : _SNAP_T;\n    const alignXok = bestX && bestXDist <= T;\n    let appliedX = null;\n    if (distX && distX.dist <= T && (!alignXok || distX.dist < bestXDist)) { if (!_noSnap) obj.x = distX.target; appliedX = \"dist\"; }\n    else if (alignXok) { if (!_noSnap) obj.x += bestX.delta; appliedX = \"align\"; }\n\n    const alignYok = bestY && bestYDist <= T;\n    let appliedY = null;\n    if (distY && distY.dist <= T && (!alignYok || distY.dist < bestYDist)) { if (!_noSnap) obj.y = distY.target; appliedY = \"dist\"; }\n    else if (alignYok) { if (!_noSnap) obj.y += bestY.delta; appliedY = \"align\"; }\n\n    // \u2500\u2500 Render guides + mm labels \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // Rendering is wrapped so any DOM hiccup can never break the drag itself\n    // (the position snap above has already been applied).\n    try {\n    _dmbReleaseAllGuides(container);\n    _dmbReleaseExtras(container);\n    const mmpp = _dmbMmPerPx(container, targetType);\n    const fL = obj.x, fR = obj.x + obj.w, fCx = obj.x + obj.w / 2;\n    const fT = obj.y, fB = obj.y + obj.h, fCy = obj.y + obj.h / 2;\n\n    // Alignment lines (blue solid = edge, dashed = centre)\n    const { ox: _gox, oy: _goy } = _dmbOverlayHost(container);\n    if (appliedX === \"align\") { const ln = _dmbAcquireGuide(container, \"v\", bestX.kind === \"cc\"); ln.style.left = (bestX.ref + _gox) + \"px\"; ln.style.transform = \"\"; ln.style.opacity = \"1\"; }\n    if (appliedY === \"align\") { const ln = _dmbAcquireGuide(container, \"h\", bestY.kind === \"cc\"); ln.style.top = (bestY.ref + _goy) + \"px\"; ln.style.transform = \"\"; ln.style.opacity = \"1\"; }\n\n    // Distribution: highlight the equal gaps (pink) with labels. Each gap draws on its\n    // OWN axis (g.axis) at its own row/column, so a matched horizontal spacing lights up\n    // horizontally and a matched vertical spacing lights up vertically \u2014 even together.\n    if (appliedX === \"dist\" && distX) distX.gaps.forEach(g => _dmbDrawSpace(container, g.axis, g.a, g.b, g.perp, mmpp, true));\n    if (appliedY === \"dist\" && distY) distY.gaps.forEach(g => _dmbDrawSpace(container, g.axis, g.a, g.b, g.perp, mmpp, true));\n\n    // Live spacing labels: distance from the item to its nearest neighbour on each\n    // side \u2014 or to the page/zone edge when there's no neighbour \u2014 so the customer\n    // always sees the real space. Shown per axis only when distribution isn't already\n    // drawing equal bars there (avoids double lines).\n    function _liveSides(sibs, e1, e2, lo, hi, axis, perp) {\n      let L = null, R = null;\n      for (const s of sibs) {\n        if (s.e2 <= e1 + 0.5) { if (!L || s.e2 > L.e2) L = s; }\n        else if (s.e1 >= e2 - 0.5) { if (!R || s.e1 < R.e1) R = s; }\n      }\n      const gL = { a: L ? L.e2 : lo, b: e1 };\n      const gR = { a: e2, b: R ? R.e1 : hi };\n      if (gL.b - gL.a >= 1) _dmbDrawSpace(container, axis, gL.a, gL.b, perp, mmpp, false);\n      if (gR.b - gR.a >= 1) _dmbDrawSpace(container, axis, gR.a, gR.b, perp, mmpp, false);\n    }\n    // Rebuild the in-line sibling sets from the FINAL position for accurate live gaps.\n    if (targetType != null && siblings.length) {\n      if (appliedX !== \"dist\") {\n        const rs = siblings.filter(s => _ov(fT, fB, s.y, s.y + s.h) > Math.min(obj.h, s.h) * 0.2).map(s => ({ e1: s.x, e2: s.x + s.w }));\n        _liveSides(rs, fL, fR, 0, cW, \"x\", fCy);\n      }\n      if (appliedY !== \"dist\") {\n        const cs = siblings.filter(s => _ov(fL, fR, s.x, s.x + s.w) > Math.min(obj.w, s.w) * 0.2).map(s => ({ e1: s.y, e2: s.y + s.h }));\n        _liveSides(cs, fT, fB, 0, cH, \"y\", fCx);\n      }\n    }\n\n    // Auto-hide everything shortly after the drag stops moving\n    clearTimeout(container._snapHideT);\n    container._snapHideT = setTimeout(() => { _dmbReleaseAllGuides(container); _dmbReleaseExtras(container); }, _GUIDE_HIDE_MS);\n    } catch (_snapErr) { try { _dmbReleaseAllGuides(container); _dmbReleaseExtras(container); } catch (_e2) {} }\n  }\n\n  // \u2500\u2500 Resize-time snapping \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // Snaps the MOVING edge(s) of `obj` (which edges move is decided by the handle\n  // `mode`: tl/tr/bl/br/ml/mr/tm/bm) onto alignment references (page/zone edges +\n  // sibling edges & centres), keeping the anchored opposite edge fixed. Draws the\n  // guide line(s) and a live W\u00D7H mm size chip. Skipped for rotated items (their\n  // edges aren't axis-aligned). Mutates obj.{x,y,w,h}; caller re-applies styles.\n  function _dmbResizeSnap(obj, container, meta, mode) {\n    try {\n      if (!obj || !mode) return;\n      const rot = (((obj.rotation || 0) % 360) + 360) % 360;\n      if (rot > 0.5 && rot < 359.5) return;               // rotated \u2192 skip\n      const targetType = meta && meta.targetType;\n      const pageNumber = meta && meta.pageNumber;\n      let cW, cH;\n      if (container._dmbSizeGen === _dmbSnapGen && container._dmbCW) { cW = container._dmbCW; cH = container._dmbCH; }\n      else { cW = container.offsetWidth || 270; cH = container.offsetHeight || 338; container._dmbCW = cW; container._dmbCH = cH; container._dmbSizeGen = _dmbSnapGen; }\n      const mmpp = _dmbMmPerPx(container, targetType);\n      const sibs = (targetType != null) ? _dmbGetSiblingObjects(obj, targetType, pageNumber) : [];\n      const refX = [], refY = [];\n      if (targetType === \"cover\") {\n        const _magFX = window.DMB_MAGAZINE_MODE ? COVER_BACK_W : COVER_FRONT_X;\n        const zones = [[0, COVER_BACK_W], [_magFX, _magFX + 300]];\n        if (!window.DMB_MAGAZINE_MODE && COVER_SPINE_W > 0) zones.push([COVER_BACK_W, COVER_BACK_W + COVER_SPINE_W]);\n        zones.forEach(([x1, x2]) => { refX.push(x1, x2, (x1 + x2) / 2); });\n      } else { refX.push(0, cW, cW / 2); }\n      refY.push(0, cH, cH / 2);\n      for (const s of sibs) { refX.push(s.x, s.x + s.w, s.x + s.w / 2); refY.push(s.y, s.y + s.h, s.y + s.h / 2); }\n      const nearest = (refs, val) => { let best = null, bd = _SNAP_T + 1; for (const r of refs) { const d = Math.abs(r - val); if (d < bd) { bd = d; best = r; } } return (best != null && bd <= _SNAP_T) ? { ref: best, dist: bd } : null; };\n      const MIND = 24;\n      const movingLeft   = mode === \"tl\" || mode === \"ml\" || mode === \"bl\";\n      const movingRight  = mode === \"tr\" || mode === \"mr\" || mode === \"br\";\n      const movingTop    = mode === \"tl\" || mode === \"tm\" || mode === \"tr\";\n      const movingBottom = mode === \"bl\" || mode === \"bm\" || mode === \"br\";\n      const _ov = (a1, a2, b1, b2) => Math.min(a2, b2) - Math.max(a1, b1);\n\n      // Sibling edges projected onto each axis, with the perpendicular span kept so\n      // we only consider neighbours the frame actually lines up against.\n      const arrX = sibs.map(s => ({ lo: s.x, hi: s.x + s.w, pp1: s.y, pp2: s.y + s.h }));\n      const arrY = sibs.map(s => ({ lo: s.y, hi: s.y + s.h, pp1: s.x, pp2: s.x + s.w }));\n      // Nearest sibling boundary strictly on the `dir` side of `pos` that overlaps the\n      // perpendicular span [pp1,pp2]; falls back to the page/zone edge `fb`.\n      const _neighEdge = (arr, pos, dir, pp1, pp2, fb) => {\n        let best = null;\n        for (const s of arr) {\n          if (_ov(pp1, pp2, s.pp1, s.pp2) <= 0) continue;\n          if (dir > 0) { if (s.lo >= pos - 0.5 && (best == null || s.lo < best)) best = s.lo; }\n          else         { if (s.hi <= pos + 0.5 && (best == null || s.hi > best)) best = s.hi; }\n        }\n        return best != null ? best : fb;\n      };\n\n      _dmbReleaseAllGuides(container); _dmbReleaseExtras(container);\n      let snapV = null, snapH = null, xEqual = false, yEqual = false;\n\n      // \u2500\u2500 X axis (left/right handle) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n      // Prefer an EQUAL-SPACING snap (moving-side gap == the fixed anchored-side gap)\n      // over plain edge alignment when it's at least as close, so resizing a frame\n      // until its two side gaps match lights both up in pink \u2014 same as the move path.\n      if (movingLeft || movingRight) {\n        const pT = obj.y, pB = obj.y + obj.h;\n        const movEdge  = movingRight ? (obj.x + obj.w) : obj.x;\n        const anchEdge = movingRight ? obj.x : (obj.x + obj.w);\n        const dir = movingRight ? 1 : -1;\n        const movNeighbor  = _neighEdge(arrX, movEdge,  dir,  pT, pB, movingRight ? cW : 0);\n        const anchNeighbor = _neighEdge(arrX, anchEdge, -dir, pT, pB, movingRight ? 0  : cW);\n        const anchGap = movingRight ? (anchEdge - anchNeighbor) : (anchNeighbor - anchEdge);\n        let eq = null;\n        if (anchGap > 0.5) {\n          const target = movingRight ? (movNeighbor - anchGap) : (movNeighbor + anchGap);\n          const okW = movingRight ? (target - obj.x >= MIND) : ((obj.x + obj.w) - target >= MIND);\n          const movGap = movingRight ? (movNeighbor - target) : (target - movNeighbor);\n          if (okW && movGap > 0.5) { const d = Math.abs(target - movEdge); if (d <= _SNAP_T) eq = { target, dist: d, movNeighbor, anchNeighbor }; }\n        }\n        const al = nearest(refX, movEdge);\n        if (eq && (!al || eq.dist <= al.dist)) {\n          if (movingRight) { obj.w = Math.max(MIND, eq.target - obj.x); }\n          else { const right = obj.x + obj.w; obj.x = Math.min(eq.target, right - MIND); obj.w = right - obj.x; }\n          xEqual = true;\n          const perp = obj.y + obj.h / 2;\n          const nMov = movingRight ? (obj.x + obj.w) : obj.x;\n          if (movingRight) { _dmbDrawSpace(container, \"x\", nMov, eq.movNeighbor, perp, mmpp, true); _dmbDrawSpace(container, \"x\", eq.anchNeighbor, obj.x, perp, mmpp, true); }\n          else             { _dmbDrawSpace(container, \"x\", eq.movNeighbor, nMov, perp, mmpp, true); _dmbDrawSpace(container, \"x\", obj.x + obj.w, eq.anchNeighbor, perp, mmpp, true); }\n        } else if (al) {\n          if (movingRight) { obj.w = Math.max(MIND, al.ref - obj.x); snapV = obj.x + obj.w; }\n          else { const right = obj.x + obj.w; obj.x = Math.min(al.ref, right - MIND); obj.w = right - obj.x; snapV = obj.x; }\n        }\n      }\n\n      // \u2500\u2500 Y axis (top/bottom handle) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n      if (movingTop || movingBottom) {\n        const pL = obj.x, pR = obj.x + obj.w;\n        const movEdge  = movingBottom ? (obj.y + obj.h) : obj.y;\n        const anchEdge = movingBottom ? obj.y : (obj.y + obj.h);\n        const dir = movingBottom ? 1 : -1;\n        const movNeighbor  = _neighEdge(arrY, movEdge,  dir,  pL, pR, movingBottom ? cH : 0);\n        const anchNeighbor = _neighEdge(arrY, anchEdge, -dir, pL, pR, movingBottom ? 0  : cH);\n        const anchGap = movingBottom ? (anchEdge - anchNeighbor) : (anchNeighbor - anchEdge);\n        let eq = null;\n        if (anchGap > 0.5) {\n          const target = movingBottom ? (movNeighbor - anchGap) : (movNeighbor + anchGap);\n          const okH = movingBottom ? (target - obj.y >= MIND) : ((obj.y + obj.h) - target >= MIND);\n          const movGap = movingBottom ? (movNeighbor - target) : (target - movNeighbor);\n          if (okH && movGap > 0.5) { const d = Math.abs(target - movEdge); if (d <= _SNAP_T) eq = { target, dist: d, movNeighbor, anchNeighbor }; }\n        }\n        const al = nearest(refY, movEdge);\n        if (eq && (!al || eq.dist <= al.dist)) {\n          if (movingBottom) { obj.h = Math.max(MIND, eq.target - obj.y); }\n          else { const bottom = obj.y + obj.h; obj.y = Math.min(eq.target, bottom - MIND); obj.h = bottom - obj.y; }\n          yEqual = true;\n          const perp = obj.x + obj.w / 2;\n          const nMov = movingBottom ? (obj.y + obj.h) : obj.y;\n          if (movingBottom) { _dmbDrawSpace(container, \"y\", nMov, eq.movNeighbor, perp, mmpp, true); _dmbDrawSpace(container, \"y\", eq.anchNeighbor, obj.y, perp, mmpp, true); }\n          else              { _dmbDrawSpace(container, \"y\", eq.movNeighbor, nMov, perp, mmpp, true); _dmbDrawSpace(container, \"y\", obj.y + obj.h, eq.anchNeighbor, perp, mmpp, true); }\n        } else if (al) {\n          if (movingBottom) { obj.h = Math.max(MIND, al.ref - obj.y); snapH = obj.y + obj.h; }\n          else { const bottom = obj.y + obj.h; obj.y = Math.min(al.ref, bottom - MIND); obj.h = bottom - obj.y; snapH = obj.y; }\n        }\n      }\n\n      const { ox: _rox, oy: _roy } = _dmbOverlayHost(container);\n      if (snapV != null) { const ln = _dmbAcquireGuide(container, \"v\", false); ln.style.left = (snapV + _rox) + \"px\"; ln.style.transform = \"\"; ln.style.opacity = \"1\"; }\n      if (snapH != null) { const ln = _dmbAcquireGuide(container, \"h\", false); ln.style.top = (snapH + _roy) + \"px\"; ln.style.transform = \"\"; ln.style.opacity = \"1\"; }\n      const lab = _dmbAcquireLabel(container);\n      lab.textContent = _dmbFmtCm(obj.w * mmpp) + \" \u00D7 \" + _dmbFmtCm(obj.h * mmpp);\n      lab.style.left = (obj.x + obj.w / 2 + _rox) + \"px\"; lab.style.top = (obj.y + _roy) + \"px\"; lab.style.transform = \"translate(-50%,-135%)\"; lab.style.opacity = \"1\";\n\n      // Live spacing labels \u2014 mirror the whole-frame move path (dmbCheckSnap \u2192\n      // _liveSides): show the gap from each edge of the (now-resized) frame to its\n      // nearest neighbour, or to the page/zone edge when there's none. Skipped on an\n      // axis where the equal-spacing bars above already drew the gaps (avoids double).\n      try {\n        const fL = obj.x, fR = obj.x + obj.w, fT = obj.y, fB = obj.y + obj.h;\n        const fCx = fL + obj.w / 2, fCy = fT + obj.h / 2;\n        const _liveSides = (arr, e1, e2, lo, hi, axis, perp) => {\n          let L = null, R = null;\n          for (const s of arr) {\n            if (s.e2 <= e1 + 0.5) { if (!L || s.e2 > L.e2) L = s; }\n            else if (s.e1 >= e2 - 0.5) { if (!R || s.e1 < R.e1) R = s; }\n          }\n          const gL = { a: L ? L.e2 : lo, b: e1 };\n          const gR = { a: e2, b: R ? R.e1 : hi };\n          if (gL.b - gL.a >= 1) _dmbDrawSpace(container, axis, gL.a, gL.b, perp, mmpp, false);\n          if (gR.b - gR.a >= 1) _dmbDrawSpace(container, axis, gR.a, gR.b, perp, mmpp, false);\n        };\n        if (sibs.length) {\n          if (!xEqual) { const rs = sibs.filter(s => _ov(fT, fB, s.y, s.y + s.h) > Math.min(obj.h, s.h) * 0.2).map(s => ({ e1: s.x, e2: s.x + s.w })); _liveSides(rs, fL, fR, 0, cW, \"x\", fCy); }\n          if (!yEqual) { const cs = sibs.filter(s => _ov(fL, fR, s.x, s.x + s.w) > Math.min(obj.w, s.w) * 0.2).map(s => ({ e1: s.y, e2: s.y + s.h })); _liveSides(cs, fT, fB, 0, cH, \"y\", fCx); }\n        }\n      } catch (e) {}\n\n      clearTimeout(container._snapHideT);\n      container._snapHideT = setTimeout(() => { _dmbReleaseAllGuides(container); _dmbReleaseExtras(container); }, _GUIDE_HIDE_MS);\n    } catch (e) {}\n  }\n\n  function dmbApplyZoneClip(el, obj) {\n    if (!obj.coverZone) { el.style.clipPath = \"\"; return; }\n    const FACE_W = 300;\n    let zoneX1, zoneX2;\n    if (obj.coverZone === \"back\") {\n      zoneX1 = 0; zoneX2 = COVER_BACK_W;\n    } else if (obj.coverZone === \"spine\") {\n      zoneX1 = COVER_BACK_W; zoneX2 = COVER_FRONT_X;\n    } else {\n      zoneX1 = window.DMB_MAGAZINE_MODE ? COVER_BACK_W : COVER_FRONT_X;\n      zoneX2 = zoneX1 + FACE_W;\n    }\n    const leftClip = Math.max(0, zoneX1 - (obj.x || 0));\n    const rightClip = Math.max(0, (obj.x || 0) + (obj.w || 0) - zoneX2);\n    el.style.clipPath = (leftClip === 0 && rightClip === 0) ? \"\" : `inset(0 ${rightClip}px 0 ${leftClip}px)`;\n  }\n\n  function dmbAlignObjectInZone(el, obj, align, targetType) {\n    let zoneX = 0, zoneW;\n    if (targetType === \"cover\") {\n      const _magFX = window.DMB_MAGAZINE_MODE ? COVER_BACK_W : COVER_FRONT_X;\n      if (obj.coverZone === \"back\") { zoneX = 0; zoneW = COVER_BACK_W; }\n      else if (obj.coverZone === \"spine\") { zoneX = COVER_BACK_W; zoneW = COVER_SPINE_W; }\n      else { zoneX = _magFX; zoneW = 300; }\n    } else {\n      const container = el.closest('[data-page-body=\"true\"]') || el.parentElement;\n      zoneW = container ? container.offsetWidth : 270;\n      // A spanning shape or map belongs to the SPREAD, so align it across the spread.\n      // Aligning it to one page was the only control that silently destroyed a span:\n      // one tap on the align buttons and the object jumped wholly onto its own sheet.\n      const _apn = (container && container.dataset) ? Number(container.dataset.pageNumber) : NaN;\n      if (Number.isFinite(_apn) && _dmbObjOverhangs(obj, _apn)) {\n        const _ar = _dmbPointRangeX(_apn, zoneW);\n        zoneX = _ar.lo; zoneW = _ar.hi - _ar.lo;\n      }\n    }\n    const elementW = obj.w || el.offsetWidth || 100;\n    let newX;\n    if (align === \"left\") newX = zoneX;\n    else if (align === \"center\") newX = Math.round(zoneX + (zoneW - elementW) / 2);\n    else newX = zoneX + zoneW - elementW;\n    // A line's box is DERIVED from its endpoints, so writing obj.x alone achieved\n    // nothing - the next _dmbLineRecomputeBox rebuilt the box from x1/x2/bx and put the\n    // line straight back. Align has therefore always been a silent no-op on lines. Carry\n    // the endpoints by the same delta so the button does what it says. (2026-09-14)\n    const _adx = newX - (obj.x || 0);\n    obj.x = newX;\n    if (_adx && obj.type === \"shape\" && obj.shapeType === \"line\") {\n      if (obj.x1 != null) obj.x1 += _adx;\n      if (obj.x2 != null) obj.x2 += _adx;\n      if (obj.bx != null) obj.bx += _adx;\n    }\n    el.style.left = newX + \"px\";\n    if (obj.coverZone) dmbApplyZoneClip(el, obj);\n    pushUndoState(\"Align \" + align.charAt(0).toUpperCase() + align.slice(1));\n  }\n\n  function dmbAlignTextNode(node, align) {\n    const parent = node.parentElement;\n    if (!parent) return;\n    let zoneX = 0, zoneW;\n    const coverWrap = parent.closest(\".dmb-cover-preview\");\n    if (coverWrap) {\n      const utEntry = (editor.coverUserTexts || []).find(function(e) { return e.id === node.id; });\n      const zone = utEntry?.zone || activeCoverZone;\n      const _magFX = window.DMB_MAGAZINE_MODE ? COVER_BACK_W : COVER_FRONT_X;\n      if (zone === \"back\") { zoneX = 0; zoneW = COVER_BACK_W; }\n      else if (zone === \"spine\") { zoneX = COVER_BACK_W; zoneW = COVER_SPINE_W; }\n      else { zoneX = _magFX; zoneW = 300; }\n    } else {\n      zoneW = parent.offsetWidth;\n    }\n    // scrollWidth captures actual text content width even when it overflows an\n    // explicit CSS width (e.g. width:220px set by the textAlign dropdown).\n    // offsetWidth alone returns the box size, not the visual text extent.\n    const nodeW = Math.max(node.offsetWidth, node.scrollWidth) || 120;\n    let newX;\n    if (align === \"left\") newX = zoneX;\n    else if (align === \"center\") newX = Math.round(zoneX + (zoneW - nodeW) / 2);\n    else newX = zoneX + zoneW - nodeW;\n    node.style.left = newX + \"px\";\n    // Save to BOTH cover state AND page objects so the new position is\n    // reflected in the PDF for both cover text and page text.\n    // WARNING: do NOT remove savePageTextState \u2014 without it, position alignment\n    // buttons (\u2190 \u2194 \u2192) leave editor.pageObjects.left stale and the PDF shows\n    // the text at the old position instead of the new one.\n    saveCoverTextNodeState(node);\n    savePageTextState(node);\n    pushUndoState(\"Align Text \" + align.charAt(0).toUpperCase() + align.slice(1));\n    showTextToolbarFor(node);\n  }\n\n  function getObjectListForTarget(targetType, pageNumber) {\n    if (targetType === \"cover\") {\n      if (!Array.isArray(editor.coverObjects)) migrateCoverObjectsToFlat();\n      return editor.coverObjects;\n    }\n    if (!editor.pageObjects || typeof editor.pageObjects !== \"object\") {\n      editor.pageObjects = {};\n    }\n    const p = Number(pageNumber);\n    if (p == null || isNaN(p) || p < 0) return [];\n    if (!Array.isArray(editor.pageObjects[p])) editor.pageObjects[p] = [];\n    return editor.pageObjects[p];\n  }\n\n  // \u2500\u2500 Cross-page move helper \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  async function dmbHandleCrossPageMove(obj, sourcePageNumber) {\n    const si = editor.currentSpread;\n    if (si < 0 || si == null) return;\n    const leftPage  = si * 2 + 1;\n    const rightPage = si * 2 + 2;\n    if (sourcePageNumber !== leftPage && sourcePageNumber !== rightPage) return;\n    if (rightPage > (editor.totalPages || 0)) return;\n\n    const PAGE_W   = getPageDims().w;\n    const isOnLeft = (sourcePageNumber === leftPage);\n    const cx       = obj.x + (obj.w || 0) / 2;\n    const objRight = obj.x + (obj.w || 0);\n\n    let targetPage = sourcePageNumber;\n    let straddling = false;\n    if (isOnLeft) {\n      if      (cx > PAGE_W)    targetPage = rightPage;\n      else if (objRight > PAGE_W) straddling = true;\n    } else {\n      if      (cx < 0)         targetPage = leftPage;\n      else if (obj.x < 0)      straddling = true;\n    }\n\n    // No longer across the fold - drop the \"deliberate\" marker so a later accidental\n    // overhang asks again rather than inheriting this object's history.\n    if (!straddling && obj.straddle) { try { delete obj.straddle; } catch (e) {} }\n\n    if (straddling && (obj.type === \"shape\" || obj.type === \"map\" || obj.type === \"photo-frame\")) {\n      // THE FEATURE: shapes and maps are allowed to span both pages of a spread. The\n      // render path already prints cross-fold overhang (getSpreadOverflowObjs), and the\n      // per-page fingerprint already hashes the facing page's objects (pageFp's\n      // partnerObjs), so what is on screen is what prints and editing it re-renders both\n      // sheets. Nothing here needs to move the object at all - it stays filed on the page\n      // its CENTRE is on, which is what the two branches above already decided.\n      //\n      // Every other type still has to pick a page: a photo frame or a text box split down\n      // the gutter is almost always a slip, not a design. (2026-09-14)\n      obj.straddle = true;\n      return;\n    }\n\n    if (straddling) {\n      const choice = await showChoiceDialog(\n        \"Object spans both pages\",\n        // Reached only by text and stickers now: photo frames, shapes and maps are all\n        // allowed across the fold, and the PDF prints the overhang on both sheets.\n        \"This item crosses the divider between two pages. Where should it go?\",\n        [\n          { id: \"stay\", label: `Keep on Page ${sourcePageNumber}` },\n          { id: \"move\", label: `Move to Page ${isOnLeft ? rightPage : leftPage}` }\n        ]\n      );\n      if (choice !== \"move\") return;\n      targetPage = isOnLeft ? rightPage : leftPage;\n    }\n\n    if (targetPage === sourcePageNumber) return;\n\n    const src = editor.pageObjects[sourcePageNumber];\n    if (!Array.isArray(src)) return;\n    const idx = src.findIndex(o => o.id === obj.id);\n    if (idx === -1) return;\n    src.splice(idx, 1);\n\n    const _dxPage = isOnLeft ? -PAGE_W : PAGE_W;\n    obj.x += _dxPage;\n    // A line stores endpoints, not a box \u2014 the box is recomputed from them, so the\n    // endpoints must move too or the next recompute puts the line back.\n    if (obj.type === \"shape\" && obj.shapeType === \"line\") {\n      if (obj.x1 != null) obj.x1 += _dxPage;\n      if (obj.x2 != null) obj.x2 += _dxPage;\n      if (obj.bx != null) obj.bx += _dxPage;\n    }\n\n    if (!Array.isArray(editor.pageObjects[targetPage])) editor.pageObjects[targetPage] = [];\n    editor.pageObjects[targetPage].push(obj);\n\n    if (typeof renderBuilderCanvas === \"function\") renderBuilderCanvas();\n    if (typeof renderThumbsRail   === \"function\") renderThumbsRail();\n  }\n\n  function renderFreeFrameObjects(container, meta) {\n    const targetType = meta?.targetType || \"page\";\n    const pageNumber = meta?.pageNumber ?? null;\n    const zone = meta?.zone || null;\n    // Pages: z >= 60 keeps frames above the spread overlay (z:50).\n    // Cover: use own z-value \u2014 drops handled by peek-under method, not z-index.\n    const minFrameZ = targetType === \"cover\" ? 0 : 60;\n\n    const rawObjs = getObjectListForTarget(targetType, pageNumber, zone);\n    const objs = Array.isArray(rawObjs)\n      ? rawObjs.slice().sort((a, b) => Number(a.z || 0) - Number(b.z || 0))\n      : [];\n\n    objs.forEach((obj) => {\n      if (obj.type === \"map\") {\n        const wrap = document.createElement(\"div\");\n        wrap.className = \"dmb-map-object\";\n        wrap.dataset.objectId = obj.id;\n        wrap.style.position = \"absolute\";\n        wrap.style.left = obj.x + \"px\";\n        wrap.style.top = obj.y + \"px\";\n        wrap.style.width = obj.w + \"px\";\n        wrap.style.height = obj.h + \"px\";\n        var _zMap = Number(_dmbZOf(obj, 20)) || 0;\n        if (_dmbObjOverhangs(obj, pageNumber)) _zMap = Math.max(1, _zMap);\n        wrap.style.zIndex = String(_zMap);\n        wrap.dataset.z = String(obj.z || 20);\n        wrap.style.cursor = \"move\";\n        wrap.style.overflow = \"visible\";\n        wrap.style.transform = `rotate(${obj.rotation || 0}deg)`;\n        wrap.style.transformOrigin = \"center center\";\n        if (obj.coverZone) dmbApplyZoneClip(wrap, obj);\n        if (obj.contained) _dmbApplyContain(wrap, obj, targetType === \"cover\");\n\n        const mapParser = new DOMParser();\n        const svgDoc = mapParser.parseFromString(obj.svg || \"\", \"image/svg+xml\");\n        const svgEl = svgDoc.querySelector(\"svg\");\n        if (svgEl) {\n          if (!svgEl.getAttribute(\"viewBox\")) {\n            const origW = svgEl.getAttribute(\"width\");\n            const origH = svgEl.getAttribute(\"height\");\n            if (origW && origH) {\n              svgEl.setAttribute(\"viewBox\", `0 0 ${parseFloat(origW)} ${parseFloat(origH)}`);\n            }\n          }\n          svgEl.removeAttribute(\"width\");\n          svgEl.removeAttribute(\"height\");\n          svgEl.style.width = \"100%\";\n          svgEl.style.height = \"100%\";\n          svgEl.style.display = \"block\";\n          dmbApplyStylesToSvg(svgEl, obj);\n          wrap.appendChild(svgEl);\n        }\n\n        if (Array.isArray(obj.markers)) {\n          obj.markers.forEach((markerData) => {\n            const marker = createMarkerElement(markerData.type || \"pin\", markerData.color || \"#e74c3c\");\n            marker.style.left = (markerData.x || 50) + \"%\";\n            marker.style.top  = (markerData.y || 50) + \"%\";\n            wrap.appendChild(marker);\n            attachMarkerInteractions(marker, markerData, wrap);\n          });\n        }\n\n        const mapHandles = [\n          [\"tl\",\"0\",\"0\"],[\"tm\",\"50%\",\"0\"],[\"tr\",\"100%\",\"0\"],\n          [\"ml\",\"0\",\"50%\"],[\"mr\",\"100%\",\"50%\"],\n          [\"bl\",\"0\",\"100%\"],[\"bm\",\"50%\",\"100%\"],[\"br\",\"100%\",\"100%\"]\n        ];\n        mapHandles.forEach(([pos, l, t]) => {\n          const h = document.createElement(\"div\");\n          h.className = `dmb-frame-handle dmb-frame-handle-${pos}`;\n          h.dataset.handle = pos;\n          h.style.left = l;\n          h.style.top = t;\n          wrap.appendChild(h);\n        });\n\n        /* \u2500\u2500 rotation handle (fixed-position, appended to body to escape overflow:hidden) \u2500\u2500 */\n        const rotHandle = document.createElement(\"div\");\n        rotHandle.className = \"dmb-map-rotate-handle dmb-map-rot-float\";\n        rotHandle.innerHTML = \"&#8635;\";\n        rotHandle.title = \"Rotate\";\n\n        const degDisplay = document.createElement(\"div\");\n        degDisplay.className = \"dmb-map-rotate-deg dmb-map-rot-float\";\n        degDisplay.textContent = Math.round(obj.rotation || 0) + \"\\u00b0\";\n        degDisplay.style.display = \"none\";\n\n        rotHandle.style.display = \"none\"; // hidden until map is selected\n        document.body.appendChild(rotHandle);\n        document.body.appendChild(degDisplay);\n        wrap._dmbRotHandle = rotHandle; // store reference for show/hide\n\n        function updateRotPos() {\n          if (!wrap.isConnected) { rotHandle.remove(); degDisplay.remove(); return; }\n          const r = wrap.getBoundingClientRect();\n          // Place handle at the actual BR corner of the bounding box, offset outward\n          rotHandle.style.left = (r.right + 14) + \"px\";\n          rotHandle.style.top = (r.bottom + 14) + \"px\";\n          degDisplay.style.left = (r.right + 14) + \"px\";\n          degDisplay.style.top = (r.bottom + 40) + \"px\";\n        }\n\n        requestAnimationFrame(updateRotPos);\n\n        rotHandle.addEventListener(\"pointerdown\", (e) => {\n          e.preventDefault();\n          e.stopPropagation();\n          const startRot = obj.rotation || 0;\n          const r = wrap.getBoundingClientRect();\n          const cx = r.left + r.width / 2;\n          const cy = r.top + r.height / 2;\n          const startAng = Math.atan2(e.clientY - cy, e.clientX - cx) * 180 / Math.PI;\n          degDisplay.style.display = \"block\";\n          const onMove = (ev) => {\n            const ang = Math.atan2(ev.clientY - cy, ev.clientX - cx) * 180 / Math.PI;\n            obj.rotation = _dmbSnapAngle(startRot + (ang - startAng));\n            wrap.style.transform = `rotate(${obj.rotation}deg)`;\n            degDisplay.textContent = Math.round(obj.rotation) + \"\\u00b0\";\n            updateRotPos();\n            showRotDegTooltip(obj.rotation, ev.clientX, ev.clientY);\n          };\n          const onUp = () => {\n            degDisplay.style.display = \"none\";\n            hideRotDegTooltip();\n            document.removeEventListener(\"pointermove\", onMove);\n            document.removeEventListener(\"pointerup\", onUp);\n          };\n          document.addEventListener(\"pointermove\", onMove);\n          document.addEventListener(\"pointerup\", onUp);\n        });\n\n        /* \u2500\u2500 snap guides \u2500\u2500 */\n        const { gH, gV } = dmbGetSnapGuides(container);\n\n        let mDragMode = null;\n        let mSx = 0, mSy = 0, mOx = 0, mOy = 0, mOw = 0, mOh = 0, mAspect = 1;\n        const MIND = 40;\n\n        wrap.addEventListener(\"pointerdown\", (e) => {\n          if (e.target.closest(\".dmb-frame-handle\")) return;\n          if (e.target.closest(\".dmb-map-marker\")) return;\n          e.preventDefault();\n          e.stopPropagation();\n          if (obj.contained) wrap.style.clipPath = \"\"; // show full during drag\n          // e.preventDefault() suppresses the click event, so select here directly\n          dmbOpenEditBar(obj, wrap);\n          mDragMode = \"move\";\n          mSx = e.clientX; mSy = e.clientY;\n          mOx = obj.x; mOy = obj.y;\n          // On-screen scale of the canvas (transform:scale(currentZoom)). Used to\n          // turn screen-pixel finger deltas into model pixels so touch dragging at\n          // <100% zoom tracks the finger exactly instead of lagging.\n          const _Z = _nodeOnScreenScale(wrap) || 1;\n          let _liftMap = null;\n          if (targetType === \"page\") {\n            const _pr = wrap.closest(\"[data-spread-row]\");\n            if (_pr) {\n              const _wr = wrap.getBoundingClientRect(), _rr = _pr.getBoundingClientRect();\n              _liftMap = { pagesRow: _pr, origParent: wrap.parentElement, origZ: wrap.style.zIndex };\n              mOx = (_wr.left - _rr.left) / _Z; mOy = (_wr.top - _rr.top) / _Z;\n              wrap.style.left = mOx + \"px\"; wrap.style.top = mOy + \"px\"; wrap.style.zIndex = \"9000\"; // below guides (9999)\n              _pr.appendChild(wrap);\n            }\n          }\n          const onMove = (ev) => {\n            if (mDragMode !== \"move\") return;\n            const dx = (ev.clientX - mSx) / _Z, dy = (ev.clientY - mSy) / _Z;\n            if (_liftMap) {\n              const _rawL = mOx + dx, _rawT = mOy + dy;\n              const { dx: _sdx, dy: _sdy } = _dmbLiftSnap(_rawL, _rawT, obj.w, obj.h, obj.id, _liftMap, gH, gV, targetType, pageNumber);\n              wrap.style.left = (_rawL + _sdx) + \"px\"; wrap.style.top = (_rawT + _sdy) + \"px\";\n            } else {\n              // Clamp to keep at least half the object inside the page\n              const _cW = container.offsetWidth  || 270;\n              const _cH = container.offsetHeight || 382;\n              // Fallback branch (no spread row to lift into: cover, page 0). Aligned with\n            // the release clamp so a map can never be yanked onto one sheet if the lift\n            // ever fails - _dmbObjDragRangeX returns the single-page range anyway\n            // wherever there is no spread partner, so this changes nothing today.\n            const _rangeXf = _dmbObjDragRangeX(obj, pageNumber, _cW);\n            obj.x = Math.max(_rangeXf.lo, Math.min(_rangeXf.hi, mOx + dx));\n              obj.y = Math.max(-obj.h / 2, Math.min(_cH - obj.h / 2, mOy + dy));\n              wrap.style.left = obj.x + \"px\"; wrap.style.top = obj.y + \"px\";\n              if (obj.coverZone) dmbApplyZoneClip(wrap, obj);\n              dmbCheckSnap(obj, container, gH, gV, { targetType, pageNumber });\n              wrap.style.left = obj.x + \"px\"; wrap.style.top = obj.y + \"px\";\n            }\n            updateRotPos();\n          };\n          const onUp = async () => {\n            mDragMode = null;\n            document.removeEventListener(\"pointermove\", onMove);\n            document.removeEventListener(\"pointerup\", onUp);\n            if (_liftMap) {\n              const _rx = parseFloat(wrap.style.left), _ry = parseFloat(wrap.style.top);\n              const _pgR = _liftMap.origParent?.parentElement?.getBoundingClientRect();\n              const _rR  = _liftMap.pagesRow.getBoundingClientRect();\n              obj.x = _rx - (_pgR ? (_pgR.left - _rR.left) / _Z : 0); obj.y = _ry;\n              // Clamp so object can't land fully outside its destination page\n              const _cW = _liftMap.origParent.offsetWidth  || 270;\n              const _cH = _liftMap.origParent.offsetHeight || 382;\n              const _rangeXm = _dmbObjDragRangeX(obj, pageNumber, _cW);\n              obj.x = Math.max(_rangeXm.lo, Math.min(_rangeXm.hi, obj.x));\n              obj.y = Math.max(-obj.h / 2, Math.min(_cH - obj.h / 2, obj.y));\n              wrap.style.left = obj.x + \"px\"; wrap.style.top = obj.y + \"px\";\n              wrap.style.zIndex = _liftMap.origZ; _liftMap.origParent.appendChild(wrap); _liftMap = null;\n            }\n            pushUndoState(\"Move Map\");\n            if (targetType === \"cover\") {\n              _dmbAutoAssignCoverZone(obj, wrap);\n            } else if (targetType === \"page\" && typeof pageNumber === \"number\") {\n              await dmbHandleCrossPageMove(obj, pageNumber);\n            }\n          };\n          document.addEventListener(\"pointermove\", onMove);\n          document.addEventListener(\"pointerup\", onUp);\n        });\n\n        wrap.querySelectorAll(\".dmb-frame-handle\").forEach((handle) => {\n          handle.addEventListener(\"pointerdown\", (e) => {\n            e.preventDefault();\n            e.stopPropagation();\n            mDragMode = handle.dataset.handle;\n            mSx = e.clientX; mSy = e.clientY;\n            mOx = obj.x; mOy = obj.y; mOw = obj.w; mOh = obj.h;\n            mAspect = mOh > 0 ? mOw / mOh : 1;\n            const _Z = _nodeOnScreenScale(wrap) || 1;\n            const onMove = (ev) => {\n              const dx = (ev.clientX - mSx) / _Z;\n              const dy = (ev.clientY - mSy) / _Z;\n              let newW, newH;\n\n              // Width-driven handles (all except top/bottom middle)\n              if ([\"tl\",\"ml\",\"bl\"].includes(mDragMode)) newW = Math.max(MIND, mOw - dx);\n              else if ([\"tr\",\"mr\",\"br\"].includes(mDragMode)) newW = Math.max(MIND, mOw + dx);\n\n              // Height-driven handles (top/bottom middle only)\n              if (mDragMode === \"tm\") newH = Math.max(MIND, mOh - dy);\n              else if (mDragMode === \"bm\") newH = Math.max(MIND, mOh + dy);\n\n              // Compute the other dimension proportionally\n              if (newW !== undefined) newH = Math.max(MIND, Math.round(newW / mAspect));\n              else if (newH !== undefined) newW = Math.max(MIND, Math.round(newH * mAspect));\n\n              obj.w = newW; obj.h = newH;\n\n              // Position adjustments to keep the correct anchor\n              if ([\"tl\",\"ml\",\"bl\"].includes(mDragMode)) obj.x = mOx + (mOw - newW);\n              else if (mDragMode === \"tm\" || mDragMode === \"bm\") obj.x = mOx + (mOw - newW) / 2;\n              if ([\"tl\",\"tm\",\"tr\"].includes(mDragMode)) obj.y = mOy + (mOh - newH);\n              else if (mDragMode === \"ml\" || mDragMode === \"mr\") obj.y = mOy + (mOh - newH) / 2;\n\n              _dmbResizeSnap(obj, container, { targetType, pageNumber }, mDragMode);\n              wrap.style.left = obj.x + \"px\";\n              wrap.style.top = obj.y + \"px\";\n              wrap.style.width = obj.w + \"px\";\n              wrap.style.height = obj.h + \"px\";\n              if (obj.coverZone) dmbApplyZoneClip(wrap, obj);\n              updateRotPos();\n            };\n            const onUp = () => {\n              mDragMode = null;\n              document.removeEventListener(\"pointermove\", onMove);\n              document.removeEventListener(\"pointerup\", onUp);\n              pushUndoState(\"Resize Map\");\n              if (targetType === \"cover\") {\n                _dmbAutoAssignCoverZone(obj, wrap);\n              }\n            };\n            document.addEventListener(\"pointermove\", onMove);\n            document.addEventListener(\"pointerup\", onUp);\n          });\n        });\n\n        currentInsertedMap = obj;\n        container.appendChild(wrap);\n        requestAnimationFrame(updateRotPos);\n        return;\n      }\n\n      if (obj.type === \"sticker\") {\n        // Ensure minimum valid dimensions (preserve user-set sizes \u2014 never overwrite on re-render)\n        if ((obj.w || 0) < 1 || (obj.h || 0) < 1) {\n          obj.w = 100; obj.h = 100;\n        }\n\n        const sWrap = document.createElement(\"div\");\n        sWrap.className = \"dmb-sticker-object\";\n        sWrap.dataset.objectId = obj.id;\n        sWrap.style.position = \"absolute\";\n        sWrap.style.left = (obj.x || 20) + \"px\";\n        sWrap.style.top = (obj.y || 20) + \"px\";\n        sWrap.style.width = obj.w + \"px\";\n        sWrap.style.height = obj.h + \"px\";\n        sWrap.style.zIndex = String(_dmbZOf(obj, 30));\n        sWrap.dataset.z = String(_dmbZOf(obj, 30));\n        sWrap.style.cursor = \"move\";\n        sWrap.style.transform = `rotate(${obj.rotation || 0}deg)`;\n        sWrap.style.transformOrigin = \"center center\";\n        if (obj.coverZone) dmbApplyZoneClip(sWrap, obj);\n        if (obj.contained) _dmbApplyContain(sWrap, obj, targetType === \"cover\");\n\n        const sImg = document.createElement(\"img\");\n        // CORS so the crop resolver can read this image's alpha (cdn.shopify.com and\n        // theme assets send ACAO:*; the PDF path already depends on it). If a host\n        // refuses, reload once without it - the sticker must never vanish on screen.\n        try {\n          if (/^https?:/i.test(String(obj.tightUrl || obj.url || \"\"))) {\n            sImg.crossOrigin = \"anonymous\";\n            let _sTry = 0;\n            const _sRetry = function () {\n              const _b = obj.tightUrl || obj.url || \"\";\n              if (!_b) return;\n              _sTry++;\n              if (_sTry === 1) {\n                // Usual cause: the image is already in the HTTP cache from a NON-CORS\n                // load, so the CORS request is refused from cache. Force one fresh\n                // fetch before giving up on readable pixels (readable = measurable\n                // crop = the sticker is not deformed).\n                sImg.addEventListener(\"error\", _sRetry, { once: true });\n                sImg.src = _b + (_b.indexOf(\"?\") >= 0 ? \"&\" : \"?\") + \"dmbcors=1\";\n              } else {\n                sImg.crossOrigin = null;\n                sImg.src = _b;\n              }\n            };\n            sImg.addEventListener(\"error\", _sRetry, { once: true });\n          }\n        } catch (e) {}\n        // Use the pre-cropped data URL if available; fall back to original CDN URL\n        sImg.src = obj.tightUrl || obj.url || \"\";\n        sImg.alt = obj.name || \"\";\n        sImg.draggable = false;\n        sImg.style.display = \"block\";\n        sImg.style.pointerEvents = \"none\";\n        // Crop parity with the PDF. Without tightUrl both renderers load the FULL\n        // PNG, but only the PDF re-crops it - so show the same window here.\n        const _sCrop = (!obj.tightUrl && obj.cropW > 0 && obj.cropH > 0);\n        let _sClip = null;\n        const _sApplySize = () => {\n          if (!_sCrop) {\n            sImg.style.width = obj.w + \"px\";\n            sImg.style.height = obj.h + \"px\";\n            return;\n          }\n          const _nw = sImg.naturalWidth || obj.cropW, _nh = sImg.naturalHeight || obj.cropH;\n          // Same resolver as the PDF, so screen and print show the same window.\n          const _c = (sImg.naturalWidth ? _dmbStickerCropFor(obj, sImg) : null) ||\n                     { cropX: obj.cropX || 0, cropY: obj.cropY || 0, cropW: obj.cropW, cropH: obj.cropH };\n          sImg.style.position = \"absolute\";\n          sImg.style.width  = (_nw / _c.cropW * obj.w) + \"px\";\n          sImg.style.height = (_nh / _c.cropH * obj.h) + \"px\";\n          sImg.style.left   = (-(_c.cropX || 0) / _c.cropW * obj.w) + \"px\";\n          sImg.style.top    = (-(_c.cropY || 0) / _c.cropH * obj.h) + \"px\";\n        };\n        if (_sCrop) {\n          // Its own clipping layer: overflow:hidden on sWrap would also swallow\n          // the resize handles, which are children of the wrapper.\n          _sClip = document.createElement(\"div\");\n          _sClip.style.cssText = \"position:absolute;inset:0;overflow:hidden;pointer-events:none;\";\n          _sClip.appendChild(sImg);\n          sWrap.appendChild(_sClip);\n          if (sImg.complete && sImg.naturalWidth) _sApplySize();\n          else sImg.addEventListener(\"load\", _sApplySize, { once: true });\n        } else {\n          sWrap.appendChild(sImg);\n        }\n        _sApplySize();\n\n        // Place handles at exact pixel positions on the sticker wrapper corners/edges\n        function sStickerHandlePts(w, h) {\n          return { tl:[0,0], tm:[w/2,0], tr:[w,0], ml:[0,h/2], mr:[w,h/2], bl:[0,h], bm:[w/2,h], br:[w,h] };\n        }\n        function updateStickerHandles(w, h) {\n          const pts = sStickerHandlePts(w, h);\n          sWrap.querySelectorAll(\".dmb-frame-handle\").forEach((el) => {\n            const [lx, ty] = pts[el.dataset.handle] || [0, 0];\n            el.style.left = lx + \"px\";\n            el.style.top = ty + \"px\";\n          });\n        }\n        const sInitW = obj.w || 100, sInitH = obj.h || 100;\n        const sInitPts = sStickerHandlePts(sInitW, sInitH);\n        [[\"tl\",\"tl\"],[\"tm\",\"tm\"],[\"tr\",\"tr\"],[\"ml\",\"ml\"],[\"mr\",\"mr\"],[\"bl\",\"bl\"],[\"bm\",\"bm\"],[\"br\",\"br\"]].forEach(([pos]) => {\n          const h = document.createElement(\"div\");\n          h.className = `dmb-frame-handle dmb-frame-handle-${pos}`;\n          h.dataset.handle = pos;\n          const [lx, ty] = sInitPts[pos];\n          h.style.left = lx + \"px\";\n          h.style.top = ty + \"px\";\n          sWrap.appendChild(h);\n        });\n\n        const { gH: sGH, gV: sGV } = dmbGetSnapGuides(container);\n\n        sWrap.addEventListener(\"click\", (e) => {\n          if (e.target.closest(\".dmb-frame-handle\")) return;\n          e.stopPropagation();\n          showFrameToolbarFor(sWrap, obj, { targetType, pageNumber, zone });\n        });\n\n        let sDMode = null, sSx = 0, sSy = 0, sSox = 0, sSoy = 0, sSow = 0, sSoh = 0;\n        const S_MIN = 30;\n\n        sWrap.addEventListener(\"pointerdown\", (e) => {\n          if (e.target.closest(\".dmb-frame-handle\")) return;\n          e.preventDefault();\n          e.stopPropagation();\n          if (obj.contained) sWrap.style.clipPath = \"\"; // show full during drag\n          showFrameToolbarFor(sWrap, obj, { targetType, pageNumber, zone });\n          sDMode = \"move\";\n          sSx = e.clientX; sSy = e.clientY;\n          sSox = obj.x; sSoy = obj.y;\n          const _Z = _nodeOnScreenScale(sWrap) || 1;\n          const _cW = container.offsetWidth  || 270;   // cache once \u2014 avoids a per-move reflow\n          const _cH = container.offsetHeight || 382;\n          let _liftS = null;\n          if (targetType === \"page\") {\n            const _pr = sWrap.closest(\"[data-spread-row]\");\n            if (_pr) {\n              const _wr = sWrap.getBoundingClientRect(), _rr = _pr.getBoundingClientRect();\n              _liftS = { pagesRow: _pr, origParent: sWrap.parentElement, origZ: sWrap.style.zIndex };\n              sSox = (_wr.left - _rr.left) / _Z; sSoy = (_wr.top - _rr.top) / _Z;\n              sWrap.style.left = sSox + \"px\"; sWrap.style.top = sSoy + \"px\"; sWrap.style.zIndex = \"9000\"; // below guides (9999)\n              _pr.appendChild(sWrap);\n            }\n          }\n          const onMove = (ev) => {\n            if (sDMode !== \"move\") return;\n            const dx = (ev.clientX - sSx) / _Z, dy = (ev.clientY - sSy) / _Z;\n            if (_liftS) {\n              const _rawL = sSox + dx, _rawT = sSoy + dy;\n              const { dx: _sdx, dy: _sdy } = _dmbLiftSnap(_rawL, _rawT, obj.w, obj.h, obj.id, _liftS, sGH, sGV, targetType, pageNumber);\n              sWrap.style.left = (_rawL + _sdx) + \"px\"; sWrap.style.top = (_rawT + _sdy) + \"px\";\n            } else {\n              obj.x = Math.max(-obj.w / 2, Math.min(_cW - obj.w / 2, sSox + dx));\n              obj.y = Math.max(-obj.h / 2, Math.min(_cH - obj.h / 2, sSoy + dy));\n              sWrap.style.left = obj.x + \"px\"; sWrap.style.top = obj.y + \"px\";\n              if (obj.coverZone) dmbApplyZoneClip(sWrap, obj);\n              dmbCheckSnap(obj, container, sGH, sGV, { targetType, pageNumber });\n              sWrap.style.left = obj.x + \"px\"; sWrap.style.top = obj.y + \"px\";\n              showFrameToolbarFor(sWrap, obj, { targetType, pageNumber, zone });\n            }\n          };\n          const onUp = async () => {\n            sDMode = null;\n            document.removeEventListener(\"pointermove\", onMove);\n            document.removeEventListener(\"pointerup\", onUp);\n            if (_liftS) {\n              const _rx = parseFloat(sWrap.style.left), _ry = parseFloat(sWrap.style.top);\n              const _pgR = _liftS.origParent?.parentElement?.getBoundingClientRect();\n              const _rR  = _liftS.pagesRow.getBoundingClientRect();\n              obj.x = _rx - (_pgR ? (_pgR.left - _rR.left) / _Z : 0); obj.y = _ry;\n              const _cW = _liftS.origParent.offsetWidth  || 270;\n              const _cH = _liftS.origParent.offsetHeight || 382;\n              obj.x = Math.max(-obj.w / 2, Math.min(_cW - obj.w / 2, obj.x));\n              obj.y = Math.max(-obj.h / 2, Math.min(_cH - obj.h / 2, obj.y));\n              sWrap.style.left = obj.x + \"px\"; sWrap.style.top = obj.y + \"px\";\n              sWrap.style.zIndex = _liftS.origZ; _liftS.origParent.appendChild(sWrap); _liftS = null;\n            }\n            pushUndoState(\"Move Sticker\");\n            if (targetType === \"cover\") {\n              _dmbAutoAssignCoverZone(obj, sWrap);\n            } else if (targetType === \"page\" && typeof pageNumber === \"number\") {\n              await dmbHandleCrossPageMove(obj, pageNumber);\n            }\n          };\n          document.addEventListener(\"pointermove\", onMove);\n          document.addEventListener(\"pointerup\", onUp);\n        });\n\n        sWrap.querySelectorAll(\".dmb-frame-handle\").forEach((handle) => {\n          handle.addEventListener(\"pointerdown\", (e) => {\n            e.preventDefault();\n            e.stopPropagation();\n            showFrameToolbarFor(sWrap, obj, { targetType, pageNumber, zone });\n            sDMode = handle.dataset.handle;\n            sSx = e.clientX; sSy = e.clientY;\n            sSox = obj.x; sSoy = obj.y;\n            sSow = obj.w || 100; sSoh = obj.h || 100;\n            const sAspect = sSow / (sSoh || 1);\n            const _Z = _nodeOnScreenScale(sWrap) || 1;\n            const onMove = (ev) => {\n              const dx = (ev.clientX - sSx) / _Z;\n              const dy = (ev.clientY - sSy) / _Z;\n              let nx = sSox, ny = sSoy, nw = sSow, nh = sSoh;\n              if (sDMode === \"tm\" || sDMode === \"bm\") {\n                nh = Math.max(S_MIN, sDMode === \"tm\" ? sSoh - dy : sSoh + dy);\n                if (sDMode === \"tm\") ny = sSoy + sSoh - nh;\n                nw = Math.max(S_MIN, Math.round(nh * sAspect));\n                nx = sSox + (sSow - nw) / 2;\n              } else if (sDMode === \"ml\" || sDMode === \"mr\") {\n                nw = Math.max(S_MIN, sDMode === \"ml\" ? sSow - dx : sSow + dx);\n                if (sDMode === \"ml\") nx = sSox + sSow - nw;\n                nh = Math.max(S_MIN, Math.round(nw / sAspect));\n                ny = sSoy + (sSoh - nh) / 2;\n              } else {\n                if (sDMode.includes(\"r\")) nw = Math.max(S_MIN, sSow + dx);\n                if (sDMode.includes(\"l\")) { nw = Math.max(S_MIN, sSow - dx); nx = sSox + sSow - nw; }\n                nh = Math.max(S_MIN, Math.round(nw / sAspect));\n                if (sDMode.includes(\"t\")) ny = sSoy + sSoh - nh;\n              }\n              obj.x = nx; obj.y = ny; obj.w = nw; obj.h = nh;\n              _dmbResizeSnap(obj, container, { targetType, pageNumber }, sDMode);\n              sWrap.style.left = obj.x + \"px\"; sWrap.style.top = obj.y + \"px\";\n              sWrap.style.width = obj.w + \"px\"; sWrap.style.height = obj.h + \"px\";\n              _sApplySize();\n              updateStickerHandles(obj.w, obj.h);\n              if (obj.coverZone) dmbApplyZoneClip(sWrap, obj);\n              showFrameToolbarFor(sWrap, obj, { targetType, pageNumber, zone });\n            };\n            const onUp = () => {\n              sDMode = null;\n              document.removeEventListener(\"pointermove\", onMove);\n              document.removeEventListener(\"pointerup\", onUp);\n              pushUndoState(\"Resize Sticker\");\n              if (targetType === \"cover\") {\n                _dmbAutoAssignCoverZone(obj, sWrap);\n              }\n            };\n            document.addEventListener(\"pointermove\", onMove);\n            document.addEventListener(\"pointerup\", onUp);\n          });\n        });\n\n        /* \u2500\u2500 rotation handle (fixed-position, appended to body to escape overflow:hidden) \u2500\u2500 */\n        const sRotHandle = document.createElement(\"div\");\n        sRotHandle.className = \"dmb-map-rotate-handle dmb-map-rot-float\";\n        sRotHandle.innerHTML = \"&#8635;\";\n        sRotHandle.title = \"Rotate\";\n        sRotHandle.style.display = \"none\";\n        document.body.appendChild(sRotHandle);\n        sWrap._dmbRotHandle = sRotHandle;\n\n        function updateStickerRotPos() {\n          if (!sWrap.isConnected) { sRotHandle.remove(); return; }\n          const r = sWrap.getBoundingClientRect();\n          sRotHandle.style.left = (r.right + 14) + \"px\";\n          sRotHandle.style.top = (r.bottom + 14) + \"px\";\n        }\n\n        requestAnimationFrame(updateStickerRotPos);\n\n        sRotHandle.addEventListener(\"pointerdown\", (e) => {\n          e.preventDefault();\n          e.stopPropagation();\n          const startRot = obj.rotation || 0;\n          const r = sWrap.getBoundingClientRect();\n          const cx = r.left + r.width / 2;\n          const cy = r.top + r.height / 2;\n          const startAng = Math.atan2(e.clientY - cy, e.clientX - cx) * 180 / Math.PI;\n          const onMove = (ev) => {\n            const ang = Math.atan2(ev.clientY - cy, ev.clientX - cx) * 180 / Math.PI;\n            obj.rotation = _dmbSnapAngle(startRot + (ang - startAng));\n            sWrap.style.transform = `rotate(${obj.rotation}deg)`;\n            updateStickerRotPos();\n            showRotDegTooltip(obj.rotation, ev.clientX, ev.clientY);\n          };\n          const onUp = () => {\n            hideRotDegTooltip();\n            document.removeEventListener(\"pointermove\", onMove);\n            document.removeEventListener(\"pointerup\", onUp);\n            pushUndoState(\"Rotate Sticker\");\n          };\n          document.addEventListener(\"pointermove\", onMove);\n          document.addEventListener(\"pointerup\", onUp);\n        });\n\n        container.appendChild(sWrap);\n        return;\n      }\n\n      /* \u2500\u2500 SHAPE OBJECTS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n       * Shapes follow the same drag/resize/rotate pattern as stickers, but\n       * use an inline SVG instead of an <img>. The shape type (rect, circle, etc.)\n       * is stored in obj.shapeType. Editing is via dmbShapeEditBar.\n       *\n       * WARNING: Do NOT merge this block with the sticker block above \u2014 shapes\n       * use dmbOpenShapeEditBar, not showFrameToolbarFor.\n       * \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n      if (obj.type === \"shape\" && obj.shapeType === \"line\") {\n        // Lines have their own endpoint-based renderer (2 draggable ends, no box).\n        _dmbRenderLineObject(obj, container, targetType, pageNumber, zone);\n        return;\n      }\n\n      if (obj.type === \"shape\") {\n        if ((obj.w || 0) < 1 || (obj.h || 0) < 1) { obj.w = 120; obj.h = 120; }\n\n        const shWrap = document.createElement(\"div\");\n        shWrap.className = \"dmb-shape-object\";\n        shWrap.dataset.objectId = obj.id;\n        shWrap.style.position        = \"absolute\";\n        shWrap.style.left            = (obj.x || 60) + \"px\";\n        shWrap.style.top             = (obj.y || 60) + \"px\";\n        shWrap.style.width           = obj.w + \"px\";\n        shWrap.style.height          = obj.h + \"px\";\n        var _zShape = Number(_dmbZOf(obj, 30)) || 0;\n        if (_dmbObjOverhangs(obj, pageNumber)) _zShape = Math.max(1, _zShape);\n        shWrap.style.zIndex          = String(_zShape);\n        shWrap.style.transform       = `rotate(${obj.rotation || 0}deg)`;\n        shWrap.style.transformOrigin = \"center center\";\n        shWrap.style.overflow        = \"visible\";\n        shWrap.dataset.z             = String(_dmbZOf(obj, 30));\n        if (obj.coverZone) dmbApplyZoneClip(shWrap, obj);\n        if (obj.contained) _dmbApplyContain(shWrap, obj, targetType === \"cover\");\n\n        // The SVG fills the wrapper completely\n        shWrap.innerHTML = buildShapeSvg(obj.shapeType, obj.fill, obj.stroke, obj.strokeWidth, obj.opacity, obj.cornerRadius, obj.w, obj.h, obj.strokeStyle);\n\n        // 8 resize handles (same positions as maps/stickers)\n        const shHandles = [\n          [\"tl\",\"0\",\"0\"],[\"tm\",\"50%\",\"0\"],[\"tr\",\"100%\",\"0\"],\n          [\"ml\",\"0\",\"50%\"],[\"mr\",\"100%\",\"50%\"],\n          [\"bl\",\"0\",\"100%\"],[\"bm\",\"50%\",\"100%\"],[\"br\",\"100%\",\"100%\"]\n        ];\n        shHandles.forEach(([pos, l, t]) => {\n          const hEl = document.createElement(\"div\");\n          hEl.className = `dmb-frame-handle dmb-frame-handle-${pos}`;\n          hEl.dataset.handle = pos;\n          hEl.style.left = l;\n          hEl.style.top  = t;\n          shWrap.appendChild(hEl);\n        });\n\n        // Rotation handle (body-level, escapes overflow:hidden)\n        const shRotHandle = document.createElement(\"div\");\n        shRotHandle.className = \"dmb-map-rotate-handle dmb-map-rot-float\";\n        shRotHandle.innerHTML = \"&#8635;\";\n        shRotHandle.title     = \"Rotate\";\n        shRotHandle.style.display = \"none\";\n        document.body.appendChild(shRotHandle);\n        shWrap._dmbRotHandle = shRotHandle;\n\n        function updateShRotPos() {\n          if (!shWrap.isConnected) { shRotHandle.remove(); return; }\n          const r = shWrap.getBoundingClientRect();\n          shRotHandle.style.left = (r.right + 14) + \"px\";\n          shRotHandle.style.top  = (r.bottom + 14) + \"px\";\n        }\n        requestAnimationFrame(updateShRotPos);\n\n        shRotHandle.addEventListener(\"pointerdown\", (e) => {\n          e.preventDefault(); e.stopPropagation();\n          const startRot = obj.rotation || 0;\n          const r  = shWrap.getBoundingClientRect();\n          const cx = r.left + r.width  / 2;\n          const cy = r.top  + r.height / 2;\n          const startAng = Math.atan2(e.clientY - cy, e.clientX - cx) * 180 / Math.PI;\n          const onMove = (ev) => {\n            const ang = Math.atan2(ev.clientY - cy, ev.clientX - cx) * 180 / Math.PI;\n            obj.rotation = _dmbSnapAngle(startRot + (ang - startAng));\n            shWrap.style.transform = `rotate(${obj.rotation}deg)`;\n            // Sync rotation input in edit bar if this shape is selected\n            const rotInput = document.getElementById(\"dmbShapeRotation\");\n            if (rotInput && dmbEditingShapeObj === obj) rotInput.value = Math.round(obj.rotation);\n            updateShRotPos();\n            showRotDegTooltip(obj.rotation, ev.clientX, ev.clientY);\n          };\n          const onUp = () => {\n            hideRotDegTooltip();\n            document.removeEventListener(\"pointermove\", onMove);\n            document.removeEventListener(\"pointerup\",  onUp);\n            pushUndoState(\"Rotate Shape\");\n          };\n          document.addEventListener(\"pointermove\", onMove);\n          document.addEventListener(\"pointerup\",   onUp);\n        });\n\n        // Snap guides\n        const { gH: shGH, gV: shGV } = dmbGetSnapGuides(container);\n\n        let shDMode = null, shSx = 0, shSy = 0, shOx = 0, shOy = 0, shOw = 0, shOh = 0;\n        const SH_MIN = 10;\n\n        // Click \u2192 open shape edit bar\n        shWrap.addEventListener(\"click\", (e) => {\n          if (e.target.closest(\".dmb-frame-handle\")) return;\n          e.stopPropagation();\n          dmbOpenShapeEditBar(obj, shWrap);\n        });\n\n        // Pointer move/drag\n        shWrap.addEventListener(\"pointerdown\", (e) => {\n          if (e.target.closest(\".dmb-frame-handle\")) return;\n          e.preventDefault(); e.stopPropagation();\n          if (obj.contained) shWrap.style.clipPath = \"\"; // show full during drag\n          dmbOpenShapeEditBar(obj, shWrap);\n          shDMode = \"move\";\n          shSx = e.clientX; shSy = e.clientY;\n          shOx = obj.x; shOy = obj.y;\n          const _Z = _nodeOnScreenScale(shWrap) || 1;\n          const _cW = container.offsetWidth  || 270;   // cache once \u2014 container size can't\n          const _cH = container.offsetHeight || 382;   // change mid-drag; reading it each\n                                                        // move forced a reflow every frame\n          const _rangeX = _dmbObjDragRangeX(obj, pageNumber, _cW);   // spread-wide for shape/map\n          const onMove = (ev) => {\n            if (shDMode !== \"move\") return;\n            const dx = (ev.clientX - shSx) / _Z, dy = (ev.clientY - shSy) / _Z;\n            obj.x = Math.max(_rangeX.lo, Math.min(_rangeX.hi, shOx + dx));\n            obj.y = Math.max(-obj.h / 2, Math.min(_cH - obj.h / 2, shOy + dy));\n            shWrap.style.left = obj.x + \"px\";\n            shWrap.style.top  = obj.y + \"px\";\n            if (obj.coverZone) dmbApplyZoneClip(shWrap, obj);\n            dmbCheckSnap(obj, container, shGH, shGV, { targetType, pageNumber });\n            shWrap.style.left = obj.x + \"px\"; shWrap.style.top = obj.y + \"px\";\n            // Bar is docked at the top \u2014 no need to move it while dragging the shape.\n          };\n          const onUp = async () => {\n            shDMode = null;\n            document.removeEventListener(\"pointermove\", onMove);\n            document.removeEventListener(\"pointerup\",  onUp);\n            pushUndoState(\"Move Shape\");\n            if (targetType === \"cover\") {\n              _dmbAutoAssignCoverZone(obj, shWrap);\n            } else if (targetType === \"page\" && typeof pageNumber === \"number\") {\n              await dmbHandleCrossPageMove(obj, pageNumber);\n            }\n          };\n          document.addEventListener(\"pointermove\", onMove);\n          document.addEventListener(\"pointerup\",  onUp);\n        });\n\n        // Resize handles\n        shWrap.querySelectorAll(\".dmb-frame-handle\").forEach((handle) => {\n          handle.addEventListener(\"pointerdown\", (e) => {\n            e.preventDefault(); e.stopPropagation();\n            shDMode = handle.dataset.handle;\n            shSx = e.clientX; shSy = e.clientY;\n            shOx = obj.x; shOy = obj.y;\n            shOw = obj.w || 120; shOh = obj.h || 120;\n            const _Z = _nodeOnScreenScale(shWrap) || 1;\n            const onMove = (ev) => {\n              const dx = (ev.clientX - shSx) / _Z, dy = (ev.clientY - shSy) / _Z;\n              let nx = shOx, ny = shOy, nw = shOw, nh = shOh;\n              if (shDMode.includes(\"r\")) nw = Math.max(SH_MIN, shOw + dx);\n              if (shDMode.includes(\"l\")) { nw = Math.max(SH_MIN, shOw - dx); nx = shOx + shOw - nw; }\n              if (shDMode.includes(\"b\")) nh = Math.max(SH_MIN, shOh + dy);\n              if (shDMode.includes(\"t\")) { nh = Math.max(SH_MIN, shOh - dy); ny = shOy + shOh - nh; }\n              // For tm/bm (top/bottom middle) \u2192 height only\n              if (shDMode === \"tm\") { nh = Math.max(SH_MIN, shOh - dy); ny = shOy + shOh - nh; nw = shOw; }\n              if (shDMode === \"bm\") { nh = Math.max(SH_MIN, shOh + dy); nw = shOw; }\n              if (shDMode === \"ml\") { nw = Math.max(SH_MIN, shOw - dx); nx = shOx + shOw - nw; nh = shOh; }\n              if (shDMode === \"mr\") { nw = Math.max(SH_MIN, shOw + dx); nh = shOh; }\n              obj.x = nx; obj.y = ny; obj.w = nw; obj.h = nh;\n              _dmbResizeSnap(obj, container, { targetType, pageNumber }, shDMode);\n              shWrap.style.left   = obj.x + \"px\";\n              shWrap.style.top    = obj.y + \"px\";\n              shWrap.style.width  = obj.w + \"px\";\n              shWrap.style.height = obj.h + \"px\";\n              if (obj.coverZone) dmbApplyZoneClip(shWrap, obj);\n            };\n            const onUp = () => {\n              shDMode = null;\n              document.removeEventListener(\"pointermove\", onMove);\n              document.removeEventListener(\"pointerup\",  onUp);\n              // Rebuild the SVG at the new size: rect/rounded-rect borders inset by\n              // sw/2 px, which is size-dependent, and a live resize only rescales the\n              // existing SVG (doesn't rebuild it). Replace just the <svg> child so the\n              // 8 resize handles (siblings) survive.\n              try {\n                const _old = shWrap.querySelector(\"svg\");\n                if (_old) {\n                  const _tmp = document.createElement(\"div\");\n                  _tmp.innerHTML = buildShapeSvg(obj.shapeType, obj.fill, obj.stroke, obj.strokeWidth, obj.opacity, obj.cornerRadius, obj.w, obj.h, obj.strokeStyle);\n                  const _new = _tmp.firstElementChild;\n                  if (_new) _old.replaceWith(_new);\n                }\n              } catch (e) {}\n              pushUndoState(\"Resize Shape\");\n            };\n            document.addEventListener(\"pointermove\", onMove);\n            document.addEventListener(\"pointerup\",  onUp);\n          });\n        });\n\n        container.appendChild(shWrap);\n        return;\n      }\n\n      if (obj.type === \"mag-icon\") {\n        if ((obj.w || 0) < 1 || (obj.h || 0) < 1) { obj.w = 150; obj.h = 60; }\n        const miWrap = document.createElement(\"div\");\n        miWrap.className = \"dmb-sticker-object dmb-mag-icon-object\";\n        miWrap.dataset.objectId = obj.id;\n        miWrap.dataset.iconId = obj.iconId || \"\";\n        miWrap.style.cssText = `position:absolute;left:${obj.x||20}px;top:${obj.y||20}px;width:${obj.w}px;height:${obj.h}px;z-index:${obj.z||30};cursor:move;transform:rotate(${obj.rotation||0}deg);transform-origin:center center;`;\n        miWrap.dataset.z = String(_dmbZOf(obj, 30));\n        if (obj.coverZone) dmbApplyZoneClip(miWrap, obj);\n        if (obj.contained) _dmbApplyContain(miWrap, obj, targetType === \"cover\");\n\n        const miImgEl = document.createElement(\"img\");\n        miImgEl.draggable = false;\n        miImgEl.style.cssText = \"width:100%;height:100%;object-fit:contain;display:block;pointer-events:none;\";\n        miWrap.appendChild(miImgEl);\n        dmbGetColoredIconDataUrl(obj.iconId, obj.color || \"#000000\").then((dataUrl) => {\n          if (miImgEl.isConnected) miImgEl.src = dataUrl;\n        });\n\n        function miHandlePts(w, h) { return { tl:[0,0], tm:[w/2,0], tr:[w,0], ml:[0,h/2], mr:[w,h/2], bl:[0,h], bm:[w/2,h], br:[w,h] }; }\n        function updateMiHandles(w, h) {\n          const pts = miHandlePts(w, h);\n          miWrap.querySelectorAll(\".dmb-frame-handle\").forEach((el) => {\n            const [lx, ty] = pts[el.dataset.handle] || [0, 0];\n            el.style.left = lx + \"px\"; el.style.top = ty + \"px\";\n          });\n        }\n        const miInitPts = miHandlePts(obj.w, obj.h);\n        [[\"tl\"],[\"tm\"],[\"tr\"],[\"ml\"],[\"mr\"],[\"bl\"],[\"bm\"],[\"br\"]].forEach(([pos]) => {\n          const hEl = document.createElement(\"div\");\n          hEl.className = `dmb-frame-handle dmb-frame-handle-${pos}`;\n          hEl.dataset.handle = pos;\n          const [lx, ty] = miInitPts[pos];\n          hEl.style.left = lx + \"px\"; hEl.style.top = ty + \"px\";\n          miWrap.appendChild(hEl);\n        });\n\n        const { gH: miGH, gV: miGV } = dmbGetSnapGuides(container);\n\n        miWrap.addEventListener(\"click\", (e) => {\n          if (e.target.closest(\".dmb-frame-handle\")) return;\n          e.stopPropagation();\n          showFrameToolbarFor(miWrap, obj, { targetType, pageNumber, zone });\n        });\n\n        let miDMode = null, miSx = 0, miSy = 0, miOx = 0, miOy = 0, miOw = 0, miOh = 0;\n        const MI_MIN = 20;\n\n        miWrap.addEventListener(\"pointerdown\", (e) => {\n          if (e.target.closest(\".dmb-frame-handle\")) return;\n          e.preventDefault(); e.stopPropagation();\n          if (obj.contained) miWrap.style.clipPath = \"\"; // show full during drag\n          showFrameToolbarFor(miWrap, obj, { targetType, pageNumber, zone });\n          miDMode = \"move\"; miSx = e.clientX; miSy = e.clientY; miOx = obj.x; miOy = obj.y;\n          const _Z = _nodeOnScreenScale(miWrap) || 1;\n          let _liftMI = null;\n          if (targetType === \"page\") {\n            const _pr = miWrap.closest(\"[data-spread-row]\");\n            if (_pr) {\n              const _wr = miWrap.getBoundingClientRect(), _rr = _pr.getBoundingClientRect();\n              _liftMI = { pagesRow: _pr, origParent: miWrap.parentElement, origZ: miWrap.style.zIndex };\n              miOx = (_wr.left - _rr.left) / _Z; miOy = (_wr.top - _rr.top) / _Z;\n              miWrap.style.left = miOx + \"px\"; miWrap.style.top = miOy + \"px\"; miWrap.style.zIndex = \"9000\"; // below guides (9999)\n              _pr.appendChild(miWrap);\n            }\n          }\n          const onMove = (ev) => {\n            if (miDMode !== \"move\") return;\n            const dx = (ev.clientX - miSx) / _Z, dy = (ev.clientY - miSy) / _Z;\n            if (_liftMI) {\n              const _rawL = miOx + dx, _rawT = miOy + dy;\n              const { dx: _sdx, dy: _sdy } = _dmbLiftSnap(_rawL, _rawT, obj.w, obj.h, obj.id, _liftMI, miGH, miGV, targetType, pageNumber);\n              miWrap.style.left = (_rawL + _sdx) + \"px\"; miWrap.style.top = (_rawT + _sdy) + \"px\";\n            } else {\n              obj.x = miOx + dx; obj.y = miOy + dy;\n              miWrap.style.left = obj.x + \"px\"; miWrap.style.top = obj.y + \"px\";\n              if (obj.coverZone) dmbApplyZoneClip(miWrap, obj);\n              dmbCheckSnap(obj, container, miGH, miGV, { targetType, pageNumber });\n              miWrap.style.left = obj.x + \"px\"; miWrap.style.top = obj.y + \"px\";\n              showFrameToolbarFor(miWrap, obj, { targetType, pageNumber, zone });\n            }\n          };\n          const onUp = async () => {\n            miDMode = null;\n            document.removeEventListener(\"pointermove\", onMove);\n            document.removeEventListener(\"pointerup\", onUp);\n            if (_liftMI) {\n              const _rx = parseFloat(miWrap.style.left), _ry = parseFloat(miWrap.style.top);\n              const _pgR = _liftMI.origParent?.parentElement?.getBoundingClientRect();\n              const _rR  = _liftMI.pagesRow.getBoundingClientRect();\n              obj.x = _rx - (_pgR ? (_pgR.left - _rR.left) / _Z : 0); obj.y = _ry;\n              miWrap.style.left = obj.x + \"px\"; miWrap.style.top = obj.y + \"px\";\n              miWrap.style.zIndex = _liftMI.origZ; _liftMI.origParent.appendChild(miWrap); _liftMI = null;\n            }\n            pushUndoState(\"Move Icon\");\n            if (targetType === \"cover\") _dmbAutoAssignCoverZone(obj, miWrap);\n            else if (targetType === \"page\" && typeof pageNumber === \"number\") await dmbHandleCrossPageMove(obj, pageNumber);\n          };\n          document.addEventListener(\"pointermove\", onMove);\n          document.addEventListener(\"pointerup\", onUp);\n        });\n\n        miWrap.querySelectorAll(\".dmb-frame-handle\").forEach((handle) => {\n          handle.addEventListener(\"pointerdown\", (e) => {\n            e.preventDefault(); e.stopPropagation();\n            showFrameToolbarFor(miWrap, obj, { targetType, pageNumber, zone });\n            miDMode = handle.dataset.handle; miSx = e.clientX; miSy = e.clientY;\n            miOx = obj.x; miOy = obj.y; miOw = obj.w || 150; miOh = obj.h || 60;\n            const miAspect = miOw / (miOh || 1);\n            const _Z = _nodeOnScreenScale(miWrap) || 1;\n            const onMove = (ev) => {\n              const dx = (ev.clientX - miSx) / _Z, dy = (ev.clientY - miSy) / _Z;\n              let nx = miOx, ny = miOy, nw = miOw, nh = miOh;\n              if (miDMode === \"tm\" || miDMode === \"bm\") {\n                nh = Math.max(MI_MIN, miDMode === \"tm\" ? miOh - dy : miOh + dy);\n                if (miDMode === \"tm\") ny = miOy + miOh - nh;\n                nw = Math.max(MI_MIN, Math.round(nh * miAspect)); nx = miOx + (miOw - nw) / 2;\n              } else if (miDMode === \"ml\" || miDMode === \"mr\") {\n                nw = Math.max(MI_MIN, miDMode === \"ml\" ? miOw - dx : miOw + dx);\n                if (miDMode === \"ml\") nx = miOx + miOw - nw;\n                nh = Math.max(MI_MIN, Math.round(nw / miAspect)); ny = miOy + (miOh - nh) / 2;\n              } else {\n                if (miDMode.includes(\"r\")) nw = Math.max(MI_MIN, miOw + dx);\n                if (miDMode.includes(\"l\")) { nw = Math.max(MI_MIN, miOw - dx); nx = miOx + miOw - nw; }\n                nh = Math.max(MI_MIN, Math.round(nw / miAspect));\n                if (miDMode.includes(\"t\")) ny = miOy + miOh - nh;\n              }\n              obj.x = nx; obj.y = ny; obj.w = nw; obj.h = nh;\n              _dmbResizeSnap(obj, container, { targetType, pageNumber }, miDMode);\n              miWrap.style.left = obj.x + \"px\"; miWrap.style.top = obj.y + \"px\";\n              miWrap.style.width = obj.w + \"px\"; miWrap.style.height = obj.h + \"px\";\n              updateMiHandles(obj.w, obj.h);\n              if (obj.coverZone) dmbApplyZoneClip(miWrap, obj);\n              showFrameToolbarFor(miWrap, obj, { targetType, pageNumber, zone });\n            };\n            const onUp = () => {\n              miDMode = null;\n              document.removeEventListener(\"pointermove\", onMove);\n              document.removeEventListener(\"pointerup\", onUp);\n              pushUndoState(\"Resize Icon\");\n              if (targetType === \"cover\") _dmbAutoAssignCoverZone(obj, miWrap);\n            };\n            document.addEventListener(\"pointermove\", onMove);\n            document.addEventListener(\"pointerup\", onUp);\n          });\n        });\n\n        const miRotHandle = document.createElement(\"div\");\n        miRotHandle.className = \"dmb-map-rotate-handle dmb-map-rot-float\";\n        miRotHandle.innerHTML = \"&#8635;\"; miRotHandle.title = \"Rotate\"; miRotHandle.style.display = \"none\";\n        document.body.appendChild(miRotHandle);\n        miWrap._dmbRotHandle = miRotHandle;\n\n        function updateMiRotPos() {\n          if (!miWrap.isConnected) { miRotHandle.remove(); return; }\n          const r = miWrap.getBoundingClientRect();\n          miRotHandle.style.left = (r.right + 14) + \"px\"; miRotHandle.style.top = (r.bottom + 14) + \"px\";\n        }\n        requestAnimationFrame(updateMiRotPos);\n\n        miRotHandle.addEventListener(\"pointerdown\", (e) => {\n          e.preventDefault(); e.stopPropagation();\n          const startRot = obj.rotation || 0;\n          const r = miWrap.getBoundingClientRect();\n          const cx = r.left + r.width / 2, cy = r.top + r.height / 2;\n          const startAng = Math.atan2(e.clientY - cy, e.clientX - cx) * 180 / Math.PI;\n          const onMove = (ev) => {\n            const ang = Math.atan2(ev.clientY - cy, ev.clientX - cx) * 180 / Math.PI;\n            obj.rotation = _dmbSnapAngle(startRot + (ang - startAng));\n            miWrap.style.transform = `rotate(${obj.rotation}deg)`;\n            updateMiRotPos();\n            showRotDegTooltip(obj.rotation, ev.clientX, ev.clientY);\n          };\n          const onUp = () => {\n            hideRotDegTooltip();\n            document.removeEventListener(\"pointermove\", onMove);\n            document.removeEventListener(\"pointerup\", onUp);\n            pushUndoState(\"Rotate Icon\");\n          };\n          document.addEventListener(\"pointermove\", onMove);\n          document.addEventListener(\"pointerup\", onUp);\n        });\n\n        container.appendChild(miWrap);\n        return;\n      }\n\n      if (obj.type === \"text\") {\n        // Safety net: never re-create an empty text box \u2014 it was deleted.\n        // The primary cleanup happens in savePageTextState, but stale data in\n        // older localStorage saves may still have {text: \"\"} objects.\n        if (!obj.text || !obj.text.trim()) return;\n        const txt = document.createElement(\"div\");\n        txt.id = obj.id;\n        txt.textContent = obj.text || \"\";\n        // Part-of-the-box formatting, rebuilt from segments. Falls through to the\n        // plain text above if anything about the stored value is off.\n        if (obj.rich) { try { _dmbApplyRich(txt, obj.rich); } catch (eR) {} }\n        txt.classList.add(\"dmb-editable-text\");\n        txt.style.position = \"absolute\";\n        txt.style.left = obj.left || \"40px\";\n        txt.style.top = obj.top || \"40px\";\n        if (obj.width) {\n          txt.style.width = obj.width;\n          // An explicit width outranks .dmb-editable-text's min-width:120px, which\n          // exists only so a brand-new EMPTY box is big enough to tap. Without this\n          // release, a box narrowed below 120px with the side grip snapped back wide\n          // on the next re-render. (2026-09-14)\n          txt.style.minWidth = \"0\";\n        }\n        txt.style.fontSize = obj.fontSize || \"28px\";\n        txt.style.fontWeight = obj.fontWeight || \"800\";\n        txt.style.color = obj.color || \"#666\";\n        txt.style.fontFamily = obj.fontFamily || \"'Londrina Solid', sans-serif\";\n        if (obj.textAlign) txt.style.textAlign = obj.textAlign;\n        if (obj.textDecoration) txt.style.textDecoration = obj.textDecoration;\n        if (obj.whiteSpace) txt.style.whiteSpace = obj.whiteSpace;\n        if (obj.transform) txt.style.transform = obj.transform;\n        if (obj.rotate) txt.dataset.rotate = obj.rotate;\n        txt.dataset.z = String(_dmbZOf(obj, Date.now()));\n        txt.style.zIndex = txt.dataset.z;\n        txt.dataset.pageNumber = String(pageNumber);\n        txt.style.padding = \"2px 6px\";\n        txt.style.background = \"transparent\";\n        txt.style.outline = \"none\";\n        attachTextNodeHandlers(txt);\n        container.appendChild(txt);\n        return;\n      }\n\n      if (obj.type !== \"photo-frame\") return;\n\n      const frame = document.createElement(\"div\");\n      frame.className = \"dmb-photo-frame-object\";\n      frame.dataset.objectId = obj.id;\n      frame.style.left = `${obj.x}px`;\n      frame.style.top = `${obj.y}px`;\n      frame.style.width = `${obj.w}px`;\n      frame.style.height = `${obj.h}px`;\n      const z = _dmbZOf(obj, 10);\n      // On pages: z >= 60 (above spread overlay z:50). On cover: z >= 100001 (above cover drag overlay z:100000).\n      // z === 0 is the explicit \"behind the photos\" layer and must NOT be lifted \u2014\n      // the PDF draws it there, so lifting it here made the frame print as nothing.\n      frame.style.zIndex = String(z === 0 ? 0 : Math.max(z, minFrameZ));\n      frame.dataset.z = String(z);\n      if (obj.rotation) frame.style.transform = `rotate(${obj.rotation}deg)`;\n      if (obj.coverZone) dmbApplyZoneClip(frame, obj);\n\n      // \u2500\u2500 Polaroid frame styling: white mat + thicker bottom + light shadow \u2500\u2500\n      const _isPoly = !!obj.polaroid;\n      let _polyInsetCss = null;\n      if (_isPoly) {\n        const _pi = dmbPolaroidInsetVals(obj.w, obj.h);\n        _polyInsetCss = `${_pi.top}px ${_pi.right}px ${_pi.bottom}px ${_pi.left}px`;\n        frame.classList.add(\"dmb-polaroid\");\n        frame.style.background = \"#ffffff\";\n        frame.style.borderRadius = \"2px\";\n        frame.style.boxShadow = (obj.shadow !== false) ? \"0 6px 16px rgba(0,0,0,0.18)\" : \"none\";\n      }\n\n      if (obj.photoId) {\n        const ph = getPhotoById(obj.photoId);\n        if (ph) {\n          // Overflow visible so buttons escape; inner clip keeps photo contained\n          frame.style.overflow = \"visible\";\n          const photoClip = document.createElement(\"div\");\n          photoClip.style.cssText = `position:absolute;inset:${_polyInsetCss || \"0\"};overflow:hidden;${_isPoly ? \"\" : \"border-radius:inherit;\"}pointer-events:none;`;\n          const img = document.createElement(\"img\");\n          img.src = ph.url; img.alt = \"\"; img.draggable = false;\n          img.style.cssText = \"position:absolute;inset:0;width:100%;height:100%;object-fit:cover;pointer-events:none;\";\n          if (obj.frameShape) applyDmbFrameShape(img, obj.frameShape);\n          photoClip.appendChild(img);\n          frame.appendChild(photoClip);\n\n          // Adaptive button layout based on frame width\n          const _btnSVGs = [\n            { aria:\"Remove photo\",  title:\"Remove photo\",  svg:`<svg viewBox=\"0 0 14 14\" width=\"13\" height=\"13\" aria-hidden=\"true\"><path d=\"M2 2l10 10M12 2L2 12\" stroke=\"#fff\" stroke-width=\"2.2\" stroke-linecap=\"round\"/></svg>`, fn:(e)=>{ e.preventDefault();e.stopPropagation();pushUndoState(\"Remove Photo\");obj.photoId=null;if(builderMode===\"cover\")renderCoverCanvas();else renderSpread();renderThumbsRail();renderPhotoTray(); }},\n            { aria:\"Rotate\",        title:\"Rotate 90\u00B0 anti-clockwise\", svg:`<svg viewBox=\"0 0 24 24\" width=\"13\" height=\"13\" aria-hidden=\"true\"><path d=\"M12 4a8 8 0 1 0 7.2 4.5\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\"/><path d=\"M19.5 4.5v5h-5\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>`, fn:(e)=>{ e.preventDefault();e.stopPropagation();pushUndoState(\"Rotate Photo\");obj.rotation=((obj.rotation||0)-90+360)%360;if(builderMode===\"cover\")renderCoverCanvas();else renderSpread();renderThumbsRail(); }},\n            { aria:\"Crop\",          title:\"Crop photo\",    svg:`<svg viewBox=\"0 0 24 24\" width=\"13\" height=\"13\" aria-hidden=\"true\"><path d=\"M7 3v11a3 3 0 0 0 3 3h11\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\"/><path d=\"M17 21V10a3 3 0 0 0-3-3H3\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\"/></svg>`, fn:(e)=>{ e.preventDefault();e.stopPropagation();openCropModal(obj.photoId, { get:()=>obj.photoId, set:(id)=>{ obj.photoId=id; } }); }},\n            { aria:\"Frame shape\",   title:\"Frame shape\",   svg:`<svg viewBox=\"0 0 22 22\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"#fff\" stroke-width=\"1.8\"><circle cx=\"11\" cy=\"11\" r=\"9\"/><polygon points=\"11,4 13,8.5 18,9 14.5,12.5 15.5,17.5 11,15 6.5,17.5 7.5,12.5 4,9 9,8.5\" fill=\"#fff\" stroke=\"none\" opacity=\"0.7\"/></svg>`, fn:(e)=>{ e.preventDefault();e.stopPropagation();showDmbFramePicker(e.currentTarget,obj.frameShape||\"\",(s)=>{ pushUndoState(\"Frame Shape\");obj.frameShape=s;applyDmbFrameShape(img,s); }); }}\n          ];\n          const _bs = \"border-radius:999px;border:none;background:rgba(0,0,0,0.72);color:#fff;cursor:pointer;display:flex;align-items:center;justify-content:center;padding:0;\";\n          const fw = obj.w || 100;\n\n          if (fw >= 130) {\n            // Wide: single row top-right\n            _btnSVGs.forEach((d, i) => {\n              const b = document.createElement(\"button\"); b.type=\"button\";\n              b.setAttribute(\"aria-label\",d.aria); b.title=d.title; b.innerHTML=d.svg;\n              b.style.cssText=`position:absolute;top:4px;right:${4+i*26}px;width:24px;height:24px;z-index:11;${_bs}`;\n              b.addEventListener(\"click\",d.fn); frame.appendChild(b);\n            });\n          } else if (fw >= 65) {\n            // Medium: 2\u00D72 grid top-right\n            _btnSVGs.forEach((d, i) => {\n              const r=Math.floor(i/2), c=i%2;\n              const b = document.createElement(\"button\"); b.type=\"button\";\n              b.setAttribute(\"aria-label\",d.aria); b.title=d.title; b.innerHTML=d.svg;\n              b.style.cssText=`position:absolute;top:${4+r*26}px;right:${4+c*26}px;width:24px;height:24px;z-index:11;${_bs}`;\n              b.addEventListener(\"click\",d.fn); frame.appendChild(b);\n            });\n          } else {\n            // Narrow: single \u22EF toggle\n            const mb = document.createElement(\"button\"); mb.type=\"button\"; mb.title=\"Options\";\n            mb.innerHTML=\"\u00B7\u00B7\u00B7\";\n            mb.style.cssText=`position:absolute;top:2px;right:2px;width:22px;height:22px;z-index:12;font-size:13px;line-height:1;${_bs}`;\n            const pop = document.createElement(\"div\");\n            pop.style.cssText=\"position:absolute;top:26px;right:0;background:rgba(20,20,20,0.92);border-radius:8px;padding:4px;display:none;flex-direction:column;gap:2px;z-index:20;min-width:110px;\";\n            _btnSVGs.forEach(d => {\n              const b = document.createElement(\"button\"); b.type=\"button\";\n              b.innerHTML=`<span style=\"display:flex;align-items:center;gap:6px;\">${d.svg}<span style=\"font-size:11px;font-weight:700;color:#fff;\">${d.aria}</span></span>`;\n              b.style.cssText=\"background:none;border:none;cursor:pointer;padding:4px 8px;border-radius:6px;width:100%;text-align:left;\";\n              b.addEventListener(\"click\",(ev)=>{ pop.style.display=\"none\"; d.fn(ev); });\n              pop.appendChild(b);\n            });\n            mb.addEventListener(\"click\",(e)=>{ e.preventDefault();e.stopPropagation(); const _open=pop.style.display===\"none\"; document.querySelectorAll(\".dmb-frame-popup-open\").forEach(p=>{p.style.display=\"none\";p.classList.remove(\"dmb-frame-popup-open\");}); if(_open){pop.style.display=\"flex\";pop.classList.add(\"dmb-frame-popup-open\");} });\n            frame.appendChild(mb); frame.appendChild(pop);\n          }\n        }\n      } else {\n        const plus = document.createElement(\"div\");\n        plus.className = \"dmb-photo-frame-plus\";\n        plus.innerHTML = '<span style=\"font-size:22px;display:block;\">+</span><span style=\"font-size:11px;opacity:.7;\">Drop photo here</span>';\n        if (_isPoly) {\n          plus.style.position = \"absolute\";\n          plus.style.inset = _polyInsetCss;\n          plus.style.background = \"#f3efe6\";\n          plus.style.display = \"flex\";\n          plus.style.flexDirection = \"column\";\n          plus.style.alignItems = \"center\";\n          plus.style.justifyContent = \"center\";\n        }\n        frame.appendChild(plus);\n      }\n\n      const handles = [\n        [\"tl\", \"0\", \"0\"], [\"tm\", \"50%\", \"0\"], [\"tr\", \"100%\", \"0\"],\n        [\"ml\", \"0\", \"50%\"], [\"mr\", \"100%\", \"50%\"],\n        [\"bl\", \"0\", \"100%\"], [\"bm\", \"50%\", \"100%\"], [\"br\", \"100%\", \"100%\"]\n      ];\n      handles.forEach(([pos, left, top]) => {\n        const h = document.createElement(\"div\");\n        h.className = `dmb-frame-handle dmb-frame-handle-${pos}`;\n        h.dataset.handle = pos;\n        h.style.left = left;\n        h.style.top = top;\n        frame.appendChild(h);\n      });\n\n      /* \u2500\u2500 Frame rotation handle (Fix 4) \u2500\u2500 */\n      const _fRotHandle2 = document.createElement(\"div\");\n      _fRotHandle2.className = \"dmb-map-rotate-handle dmb-map-rot-float\";\n      _fRotHandle2.innerHTML = \"&#8635;\";\n      _fRotHandle2.title = \"Rotate frame\";\n      _fRotHandle2.style.display = \"none\";\n      document.body.appendChild(_fRotHandle2);\n      frame._dmbRotHandle = _fRotHandle2;\n\n      function _updateFrameRotPos2() {\n        if (!frame.isConnected) { _fRotHandle2.remove(); return; }\n        const _rr = frame.getBoundingClientRect();\n        _fRotHandle2.style.left = (_rr.right + 14) + \"px\";\n        _fRotHandle2.style.top = (_rr.bottom + 14) + \"px\";\n      }\n      requestAnimationFrame(_updateFrameRotPos2);\n\n      _fRotHandle2.addEventListener(\"pointerdown\", (e) => {\n        e.preventDefault(); e.stopPropagation();\n        const _startRot = obj.rotation || 0;\n        const _rr2 = frame.getBoundingClientRect();\n        const _cx = _rr2.left + _rr2.width / 2, _cy = _rr2.top + _rr2.height / 2;\n        const _startAng = Math.atan2(e.clientY - _cy, e.clientX - _cx) * 180 / Math.PI;\n        const _onMove = (ev) => {\n          const _ang = Math.atan2(ev.clientY - _cy, ev.clientX - _cx) * 180 / Math.PI;\n          obj.rotation = _dmbSnapAngle(_startRot + (_ang - _startAng));\n          frame.style.transform = `rotate(${obj.rotation}deg)`;\n          _updateFrameRotPos2();\n          showRotDegTooltip(obj.rotation, ev.clientX, ev.clientY);\n        };\n        const _onUp = () => {\n          hideRotDegTooltip();\n          document.removeEventListener(\"pointermove\", _onMove);\n          document.removeEventListener(\"pointerup\", _onUp);\n          pushUndoState(\"Rotate Frame\");\n        };\n        document.addEventListener(\"pointermove\", _onMove);\n        document.addEventListener(\"pointerup\", _onUp);\n      });\n\n      frame.addEventListener(\"click\", (e) => {\n        e.stopPropagation();\n        showFrameToolbarFor(frame, obj, { targetType, pageNumber, zone });\n      });\n\n      frame.addEventListener(\"dragover\", (e) => {\n        e.preventDefault();\n        const payload = getDragPayload(e);\n        e.dataTransfer.dropEffect = (payload && payload.type === \"placed\") ? \"move\" : \"copy\";\n      });\n\n      frame.addEventListener(\"drop\", (e) => {\n        e.preventDefault();\n        e.stopPropagation();\n        const payload = getDragPayload(e);\n        if (!payload || !payload.photoId) return;\n        pushUndoState(\"Drop Photo In Frame\");\n        obj.photoId = payload.photoId;\n        if (builderMode === \"cover\") renderCoverCanvas(); else renderSpread();\n        renderPhotoTray();\n        renderThumbsRail();\n      });\n\n      let dragMode = null;\n      let sx = 0, sy = 0, ox = 0, oy = 0, ow = 0, oh = 0;\n\n      const { gH: fGH, gV: fGV } = dmbGetSnapGuides(container);\n\n      frame.addEventListener(\"pointerdown\", (e) => {\n        if (e.target.closest(\".dmb-frame-handle\")) return;\n        // Let action buttons (crop, remove, rotate, etc.) receive their click events.\n        // pointerdown.preventDefault() suppresses the click event for the whole pointer\n        // sequence, so buttons inside the frame would silently do nothing without this guard.\n        if (e.target.closest(\"button\")) return;\n        e.preventDefault();\n        e.stopPropagation();\n        showFrameToolbarFor(frame, obj, { targetType, pageNumber, zone });\n        dragMode = \"move\";\n        sx = e.clientX; sy = e.clientY;\n        ox = obj.x; oy = obj.y;\n        const _Z = _nodeOnScreenScale(frame) || 1;\n        let _liftF = null;\n        if (targetType === \"page\") {\n          const _pr = frame.closest(\"[data-spread-row]\");\n          if (_pr) {\n            _liftF = { pagesRow: _pr, origParent: frame.parentElement, origZ: frame.style.zIndex };\n            // Use CSS-based page offset instead of getBoundingClientRect().\n            // getBoundingClientRect gives the AXIS-ALIGNED bounding box of the rotated\n            // element, which differs from the CSS left/top (the pre-rotation anchor).\n            // Using offsetLeft/offsetTop avoids the position jump on rotated frames.\n            const _pageEl = _liftF.origParent.parentElement; // the page div\n            const _pageOffL = (_pageEl && _pageEl.offsetLeft) || 0;\n            const _pageOffT = (_pageEl && _pageEl.offsetTop)  || 0;\n            ox = obj.x + _pageOffL;\n            oy = obj.y + _pageOffT;\n            _liftF._offL = _pageOffL; _liftF._offT = _pageOffT;   // exact offsets for the snap (see onMove)\n            frame.style.left = ox + \"px\"; frame.style.top = oy + \"px\"; frame.style.zIndex = \"9000\"; // below guides (9999)\n            _pr.appendChild(frame);\n          }\n        }\n\n        // Coalesce pointermoves to one update per animation frame: on touch the\n        // finger fires moves faster than the screen refreshes, and each one did snap\n        // geometry + guide drawing + a style write/read on a transform-scaled canvas\n        // (forced reflow) \u2014 the main cause of the drag lag. We keep only the latest\n        // event and apply it once per frame; the frame still tracks the finger.\n        let _mvRaf = false, _mvEv = null;\n        const _applyMove = () => {\n          _mvRaf = false;\n          const ev = _mvEv;\n          if (!ev || dragMode !== \"move\") return;\n          const dx = (ev.clientX - sx) / _Z, dy = (ev.clientY - sy) / _Z;\n          if (_liftF) {\n            // Snap in the page-body's OWN model coordinates: subtract the exact page\n            // offset we added at lift start, snap, then re-add it. This keeps the snap\n            // math in the same units the siblings use, so aligned edges land EXACTLY\n            // (the old _dmbLiftSnap mixed getBoundingClientRect px with layout px, which\n            // left a constant drift).\n            const _rawL = ox + dx, _rawT = oy + dy;\n            const _so = { id: obj.id, x: _rawL - _liftF._offL, y: _rawT - _liftF._offT, w: obj.w, h: obj.h };\n            dmbCheckSnap(_so, _liftF.origParent, fGH, fGV, { targetType, pageNumber });\n            frame.style.left = (_so.x + _liftF._offL) + \"px\";\n            frame.style.top  = (_so.y + _liftF._offT) + \"px\";\n          } else {\n            obj.x = ox + dx; obj.y = oy + dy;\n            frame.style.left = `${obj.x}px`; frame.style.top = `${obj.y}px`;\n            if (obj.coverZone) dmbApplyZoneClip(frame, obj);\n            dmbCheckSnap(obj, container, fGH, fGV, { targetType, pageNumber });\n            frame.style.left = `${obj.x}px`; frame.style.top = `${obj.y}px`;\n            showFrameToolbarFor(frame, obj, { targetType, pageNumber, zone });\n          }\n        };\n        const onMove = (ev) => {\n          if (dragMode !== \"move\") return;\n          _mvEv = ev;\n          if (_mvRaf) return;\n          _mvRaf = true;\n          requestAnimationFrame(_applyMove);\n        };\n        const onUp = async () => {\n          if (_mvRaf) { _applyMove(); }   // flush the last coalesced move so the drop is exact\n          dragMode = null;\n          document.removeEventListener(\"pointermove\", onMove);\n          document.removeEventListener(\"pointerup\", onUp);\n          if (_liftF) {\n            const _rx = parseFloat(frame.style.left), _ry = parseFloat(frame.style.top);\n            // Use same CSS-based offset as the lift start (no getBoundingClientRect)\n            const _pageEl2 = _liftF.origParent?.parentElement;\n            const _pOff = (_pageEl2 && _pageEl2.offsetLeft) || 0;\n            const _pOffT = (_pageEl2 && _pageEl2.offsetTop)  || 0;\n            obj.x = _rx - _pOff;\n            obj.y = _ry - _pOffT;\n            frame.style.left = obj.x + \"px\"; frame.style.top = obj.y + \"px\";\n            frame.style.zIndex = _liftF.origZ; _liftF.origParent.appendChild(frame); _liftF = null;\n          }\n          pushUndoState(\"Move Frame\");\n          if (targetType === \"cover\") {\n            _dmbAutoAssignCoverZone(obj, frame);\n          } else if (targetType === \"page\" && typeof pageNumber === \"number\") {\n            await dmbHandleCrossPageMove(obj, pageNumber);\n          }\n        };\n        document.addEventListener(\"pointermove\", onMove);\n        document.addEventListener(\"pointerup\", onUp);\n      });\n\n      frame.querySelectorAll(\".dmb-frame-handle\").forEach((handle) => {\n        handle.addEventListener(\"pointerdown\", (e) => {\n          e.preventDefault();\n          e.stopPropagation();\n          showFrameToolbarFor(frame, obj, { targetType, pageNumber, zone });\n          dragMode = handle.dataset.handle;\n          sx = e.clientX; sy = e.clientY;\n          ox = obj.x; oy = obj.y; ow = obj.w; oh = obj.h;\n          const _Z = _nodeOnScreenScale(frame) || 1;\n\n          const onMove = (ev) => {\n            const dx = (ev.clientX - sx) / _Z;\n            const dy = (ev.clientY - sy) / _Z;\n            // Decompose screen-space delta into the element's rotated local coordinate system.\n            // Without this, resizing a rotated frame pulls in the wrong direction.\n            // For non-rotated frames (rotation=0): cos=1, sin=0 \u2192 ldx=dx, ldy=dy (no change).\n            const _rot = (obj.rotation || 0) * Math.PI / 180;\n            const _cos = Math.cos(_rot), _sin = Math.sin(_rot);\n            const ldx = dx * _cos + dy * _sin;   // delta along element's local X axis\n            const ldy = -dx * _sin + dy * _cos;  // delta along element's local Y axis\n\n            if (dragMode.includes(\"r\")) obj.w = Math.max(60, ow + ldx);\n            if (dragMode.includes(\"b\")) obj.h = Math.max(60, oh + ldy);\n            if (dragMode.includes(\"l\")) {\n              const nextW = Math.max(60, ow - ldx);\n              const diff = ow - nextW;\n              // The left edge moves in the element's local -X direction; convert to parent space\n              obj.x = ox + diff * _cos;\n              obj.y = oy + diff * _sin;\n              obj.w = nextW;\n            }\n            if (dragMode.includes(\"t\")) {\n              const nextH = Math.max(60, oh - ldy);\n              const diff = oh - nextH;\n              // The top edge moves in the element's local -Y direction; convert to parent space\n              obj.x = ox - diff * _sin;\n              obj.y = oy + diff * _cos;\n              obj.h = nextH;\n            }\n            _dmbResizeSnap(obj, container, { targetType, pageNumber }, dragMode);\n            frame.style.left = `${obj.x}px`;\n            frame.style.top = `${obj.y}px`;\n            frame.style.width = `${obj.w}px`;\n            frame.style.height = `${obj.h}px`;\n            if (obj.coverZone) dmbApplyZoneClip(frame, obj);\n            showFrameToolbarFor(frame, obj, { targetType, pageNumber, zone });\n          };\n          const onUp = () => {\n            dragMode = null;\n            document.removeEventListener(\"pointermove\", onMove);\n            document.removeEventListener(\"pointerup\", onUp);\n            pushUndoState(\"Resize Frame\");\n            if (targetType === \"cover\") {\n              _dmbAutoAssignCoverZone(obj, frame);\n            }\n          };\n          document.addEventListener(\"pointermove\", onMove);\n          document.addEventListener(\"pointerup\", onUp);\n        });\n      });\n\n      container.appendChild(frame);\n    });\n  }\n  // --- end frame rendering helpers ---\n\n  // Holder so the PDF/preview export path can run the SAME cover-text box capture\n  // that renderCoverCanvas defines (it's a closure local to renderCoverCanvas).\n  // Forcing a fresh synchronous capture right before a cover export guarantees the\n  // stored visCx/visCy/glyphCx/glyphCy/boxWidth reflect the CURRENT orientation, so\n  // the PDF's live-mirror + glyph paths never fall back to stale (e.g. portrait)\n  // geometry after a Portrait/Landscape flip. (Single source of truth \u2014 no duped\n  // measurement logic.) See _dmbCaptureCoverTextBoxesFn() call in renderCoverSpread.\n  let _dmbCaptureCoverTextBoxesFn = null;\n\n  function renderCoverCanvas() {\n    // Remember WHICH VIEW this is. The snapshot already carries editor.currentSpread\n    // and builderMode, but nothing was saving on NAVIGATION - every _dmbAutoPersist\n    // call sat on an edit path. So a refresh returned the customer to wherever they\n    // last TYPED rather than wherever they were looking, which on a long book means a\n    // completely different spread, or the cover. Debounced at 600ms, and\n    // _dmbWriteProgressState ignores an empty editor, so this cannot overwrite good\n    // state with a half-built one during a restore. (2026-10-12)\n    try { _dmbAutoPersist(); } catch (e) {}\n    if (!spreadCanvas || !spreadIndexEl) return;\n    // Migrate any fixed-slot cover layout (frontLayout/backLayout) into deletable free\n    // photo-frames. The %\u2192px math matches where the locked slots drew, so this is\n    // visually lossless; it just makes template/layout frames selectable + deletable.\n    // No-op once converted (frontLayout/backLayout become null). Idempotent + cheap.\n    try { _dmbConvertCoverFixedLayouts(); } catch (e) {}\n\n    // Magazine: seed ONE typical (deletable / movable / resizable) free photo frame on\n    // the FRONT cover \u2014 once \u2014 in place of the old locked full-face grey slot. Only when\n    // the front has no full-bleed photo and no front frame yet; the persisted\n    // coverState.magFrontFrameSeeded flag stops it coming back after the user deletes it.\n    try {\n      if (window.DMB_MAGAZINE_MODE && !_dmbRenderMode() && !coverState.magFrontFrameSeeded) {\n        coverState.magFrontFrameSeeded = true;\n        var _mcArr = ensureCoverObjects();\n        var _hasFrontFrame = _mcArr.some(function (o) { return o && o.type === \"photo-frame\" && o.coverZone === \"front\"; });\n        if (!coverState.frontImageUrl && !_hasFrontFrame) {\n          var _mf = newFrameObject({ targetType: \"cover\" });\n          _mf.coverZone = \"front\";\n          // px in the full-spread space; magazine front face spans x 300..600, y 0..424.\n          // Centred frame with a ~33px / ~44px margin so the background shows around it.\n          _mf.x = 333; _mf.y = 44; _mf.w = 234; _mf.h = 336;\n          _mcArr.push(_mf);\n        }\n        try { saveCoverDesignToStorage(); } catch (e) {}\n        try { _dmbWriteProgressState(); } catch (e) {}\n        try { if (typeof _dmbCloudPersist === \"function\") _dmbCloudPersist(); } catch (e) {}\n      }\n    } catch (e) {}\n\n    // Reset the viewport to fit the cover (clears any stale fixed height left over\n    // from a spread's forceSpreadLayout \u2014 otherwise the cover shows empty space below it).\n    if (spreadViewport) {\n      spreadViewport.style.height = \"auto\";\n      spreadViewport.style.minHeight = \"0\";\n      spreadViewport.style.maxHeight = \"760px\";\n    }\n    // Remove orphaned fixed-position rotation handles before re-render\n    document.querySelectorAll(\".dmb-map-rot-float\").forEach((el) => el.remove());\n    // Cover is the first view: nothing before it, Spread 0 comes next.\n    if (prevSpreadBtn) prevSpreadBtn.disabled = true;\n    if (nextSpreadBtn) nextSpreadBtn.disabled = editor.totalSpreads <= 0;\n    // Mark cover dirty so PDF preview re-renders after this change (2s debounce)\n    _dmbPvMarkDirty(\"cover\");\n\n    spreadCanvas.innerHTML = \"\";\n\n    const wrap = document.createElement(\"div\");\n    wrap.className = \"dmb-cover-preview\";\n    // Landscape: widen the cover faces (CSS flips the face aspect ratio). Elements\n    // carry over (colours/fonts/text) but positions are approximate \u2014 the owner\n    // rearranges and re-publishes the landscape (-ls) template.\n    if (dmbIsLandscape()) wrap.classList.add(\"dmb-cover-landscape\");\n\n    const back = document.createElement(\"div\");\n    back.className = \"dmb-cover-face dmb-cover-back\";\n    back.id = \"coverBack\";\n    back.style.cursor = \"pointer\";\n    if (coverState.backImageUrl) {\n      back.style.background = coverState.color || \"#ffffff\";\n      back.style.overflow = \"hidden\";\n      const _backImgDiv = document.createElement(\"div\");\n      _backImgDiv.style.cssText = `position:absolute;inset:0;background:url(${coverState.backImageUrl}) center/cover no-repeat;transform:rotate(${coverState.backImageRot||0}deg);transform-origin:center center;z-index:0;pointer-events:none;`;\n      back.appendChild(_backImgDiv);\n      // Control buttons (X, rotate, crop, frames) \u2014 same style as page photos\n      const _bRm = document.createElement(\"button\");\n      _bRm.type=\"button\"; _bRm.setAttribute(\"aria-label\",\"Remove photo\"); _bRm.title=\"Remove photo\";\n      _bRm.innerHTML=`<svg viewBox=\"0 0 14 14\" width=\"13\" height=\"13\" aria-hidden=\"true\"><path d=\"M2 2l10 10M12 2L2 12\" stroke=\"#fff\" stroke-width=\"2.2\" stroke-linecap=\"round\"/></svg>`;\n      _bRm.style.cssText=\"position:absolute;top:8px;right:8px;width:28px;height:28px;border-radius:999px;border:none;background:rgba(0,0,0,0.72);color:#fff;cursor:pointer;z-index:10;display:flex;align-items:center;justify-content:center;padding:0;\";\n      _bRm.addEventListener(\"click\",(e)=>{ e.preventDefault(); e.stopPropagation(); pushUndoState(\"Remove Photo\"); coverState.backImageUrl=\"\"; coverState.backImageId=\"\"; coverState.backImageRot=0; saveCoverDesignToStorage(); renderCoverCanvas(); renderThumbsRail(); renderPhotoTray(); });\n      back.appendChild(_bRm);\n      const _bRot = document.createElement(\"button\");\n      _bRot.type=\"button\"; _bRot.setAttribute(\"aria-label\",\"Rotate 90 degrees anti-clockwise\"); _bRot.title=\"Rotate 90\u00B0 anti-clockwise\";\n      _bRot.innerHTML=`<svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" aria-hidden=\"true\"><path d=\"M12 4a8 8 0 1 0 7.2 4.5\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\"/><path d=\"M19.5 4.5v5h-5\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>`;\n      _bRot.style.cssText=\"position:absolute;top:8px;right:40px;width:28px;height:28px;border-radius:999px;border:none;background:rgba(0,0,0,0.72);cursor:pointer;z-index:10;display:flex;align-items:center;justify-content:center;padding:0;\";\n      _bRot.addEventListener(\"click\",(e)=>{ e.preventDefault(); e.stopPropagation(); pushUndoState(\"Rotate Photo\"); coverState.backImageRot=((coverState.backImageRot||0)-90+360)%360; saveCoverDesignToStorage(); renderCoverCanvas(); renderThumbsRail(); });\n      back.appendChild(_bRot);\n      const _bCrop = document.createElement(\"button\");\n      _bCrop.type=\"button\"; _bCrop.setAttribute(\"aria-label\",\"Crop photo\"); _bCrop.title=\"Crop photo\";\n      _bCrop.innerHTML=`<svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" aria-hidden=\"true\"><path d=\"M7 3v11a3 3 0 0 0 3 3h11\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\"/><path d=\"M17 21V10a3 3 0 0 0-3-3H3\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\"/></svg>`;\n      _bCrop.style.cssText=\"position:absolute;top:8px;right:72px;width:28px;height:28px;border-radius:999px;border:none;background:rgba(0,0,0,0.72);cursor:pointer;z-index:10;display:flex;align-items:center;justify-content:center;padding:0;\";\n      _bCrop.addEventListener(\"click\",(e)=>{ e.preventDefault(); e.stopPropagation(); if(coverState.backImageId) openCropModal(coverState.backImageId, { get:()=>coverState.backImageId, set:(id)=>{ coverState.backImageId=id; coverState.backImageUrl=getPhotoById(id)?.url||\"\"; } }); });\n      back.appendChild(_bCrop);\n      const _bFrame = document.createElement(\"button\");\n      _bFrame.type=\"button\"; _bFrame.setAttribute(\"aria-label\",\"Frame shape\"); _bFrame.title=\"Frame shape\";\n      _bFrame.innerHTML=`<svg viewBox=\"0 0 22 22\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"#fff\" stroke-width=\"1.8\"><circle cx=\"11\" cy=\"11\" r=\"9\"/><polygon points=\"11,4 13,8.5 18,9 14.5,12.5 15.5,17.5 11,15 6.5,17.5 7.5,12.5 4,9 9,8.5\" fill=\"#fff\" stroke=\"none\" opacity=\"0.7\"/></svg>`;\n      _bFrame.style.cssText=\"position:absolute;top:8px;right:104px;width:28px;height:28px;border-radius:999px;border:none;background:rgba(0,0,0,0.72);cursor:pointer;z-index:10;display:flex;align-items:center;justify-content:center;padding:0;\";\n      _bFrame.addEventListener(\"click\",(e)=>{ e.preventDefault(); e.stopPropagation(); showDmbFramePicker(_bFrame, coverState.backImageFrameShape||\"\", (shapeId)=>{ pushUndoState(\"Frame Shape\"); coverState.backImageFrameShape=shapeId; applyDmbFrameShape(_backImgDiv, shapeId); saveCoverDesignToStorage(); }); });\n      back.appendChild(_bFrame);\n    } else {\n      // Magazines used to show a locked full-face grey \"Drop photo here\" slot here\n      // (hid the background, not deletable). Now books AND magazines just show the\n      // background colour; photos go into the typical free frame (seeded below) or\n      // onto the face by dropping directly.\n      back.style.background = coverState.color || \"#ffffff\";\n    }\n    back.addEventListener(\"click\", (e) => {\n      e.stopPropagation();\n      activeCoverZone = \"back\";\n      if (typeof markActiveCoverZone === \"function\") markActiveCoverZone();\n      if (typeof hideTextToolbar === \"function\") hideTextToolbar();\n    });\n    back.addEventListener(\"dragover\", (e) => { e.preventDefault(); e.dataTransfer.dropEffect = \"copy\"; back.style.outline = \"2px solid #111\"; back.style.outlineOffset = \"-2px\"; });\n    back.addEventListener(\"dragleave\", () => { back.style.outline = \"none\"; });\n    back.addEventListener(\"drop\", (e) => {\n      e.preventDefault(); back.style.outline = \"none\";\n      const payload = getDragPayload(e);\n      const photoId = payload?.photoId || (e.dataTransfer.getData(\"text/plain\") || \"\").trim();\n      if (!photoId) return;\n      const photo = getPhotoById(photoId);\n      if (!photo) return;\n      pushUndoState(\"Drop Photo On Cover Back\");\n      coverState.backImageUrl = photo.url;\n      coverState.backImageId = photoId;\n      coverState.backImageRot = 0;\n      saveCoverDesignToStorage();\n      renderCoverCanvas();\n    });\n\n\n    let spine = null;\n    if (!window.DMB_MAGAZINE_MODE) {\n      spine = document.createElement(\"div\");\n      spine.className = \"dmb-cover-spine\";\n      spine.style.background = coverState.color || \"#ffffff\";\n      spine.id = \"coverSpine\";\n      spine.style.cursor = \"pointer\";\n      // Hide internal seam borders when a bleed photo covers those junctions\n      if (coverState.bleedZone === \"full\" || coverState.bleedZone === \"spine-back\")  spine.style.borderLeft  = \"none\";\n      if (coverState.bleedZone === \"full\" || coverState.bleedZone === \"front-spine\") spine.style.borderRight = \"none\";\n      if (coverState.spineImageUrl) {\n        const _spineImgDiv = document.createElement(\"div\");\n        _spineImgDiv.style.cssText = `position:absolute;inset:0;background:url(${coverState.spineImageUrl}) center/cover no-repeat;z-index:0;pointer-events:none;`;\n        spine.appendChild(_spineImgDiv);\n      }\n      spine.addEventListener(\"click\", (e) => {\n        e.stopPropagation();\n        activeCoverZone = \"spine\";\n        if (typeof markActiveCoverZone === \"function\") markActiveCoverZone();\n        if (typeof hideTextToolbar === \"function\") hideTextToolbar();\n      });\n    }\n\n\n    const front = document.createElement(\"div\");\n    front.className = \"dmb-cover-face dmb-cover-front\";\n    front.id = \"coverFront\";\n    front.style.cursor = \"pointer\";\n    if (coverState.frontImageUrl) {\n      front.style.background = coverState.color || \"#ffffff\";\n      front.style.overflow = \"hidden\";\n      const _frontImgDiv = document.createElement(\"div\");\n      _frontImgDiv.style.cssText = `position:absolute;inset:0;background:url(${coverState.frontImageUrl}) center/cover no-repeat;transform:rotate(${coverState.frontImageRot||0}deg);transform-origin:center center;z-index:0;pointer-events:none;`;\n      front.appendChild(_frontImgDiv);\n      // Control buttons (X, rotate, crop, frames) \u2014 same style as page photos\n      const _fRm = document.createElement(\"button\");\n      _fRm.type=\"button\"; _fRm.setAttribute(\"aria-label\",\"Remove photo\"); _fRm.title=\"Remove photo\";\n      _fRm.innerHTML=`<svg viewBox=\"0 0 14 14\" width=\"13\" height=\"13\" aria-hidden=\"true\"><path d=\"M2 2l10 10M12 2L2 12\" stroke=\"#fff\" stroke-width=\"2.2\" stroke-linecap=\"round\"/></svg>`;\n      _fRm.style.cssText=\"position:absolute;top:8px;right:8px;width:28px;height:28px;border-radius:999px;border:none;background:rgba(0,0,0,0.72);color:#fff;cursor:pointer;z-index:10;display:flex;align-items:center;justify-content:center;padding:0;\";\n      _fRm.addEventListener(\"click\",(e)=>{ e.preventDefault(); e.stopPropagation(); pushUndoState(\"Remove Photo\"); coverState.frontImageUrl=\"\"; coverState.frontImageId=\"\"; coverState.frontImageRot=0; saveCoverDesignToStorage(); renderCoverCanvas(); renderThumbsRail(); renderPhotoTray(); });\n      front.appendChild(_fRm);\n      const _fRot = document.createElement(\"button\");\n      _fRot.type=\"button\"; _fRot.setAttribute(\"aria-label\",\"Rotate 90 degrees anti-clockwise\"); _fRot.title=\"Rotate 90\u00B0 anti-clockwise\";\n      _fRot.innerHTML=`<svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" aria-hidden=\"true\"><path d=\"M12 4a8 8 0 1 0 7.2 4.5\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\"/><path d=\"M19.5 4.5v5h-5\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>`;\n      _fRot.style.cssText=\"position:absolute;top:8px;right:40px;width:28px;height:28px;border-radius:999px;border:none;background:rgba(0,0,0,0.72);cursor:pointer;z-index:10;display:flex;align-items:center;justify-content:center;padding:0;\";\n      _fRot.addEventListener(\"click\",(e)=>{ e.preventDefault(); e.stopPropagation(); pushUndoState(\"Rotate Photo\"); coverState.frontImageRot=((coverState.frontImageRot||0)-90+360)%360; saveCoverDesignToStorage(); renderCoverCanvas(); renderThumbsRail(); });\n      front.appendChild(_fRot);\n      const _fCrop = document.createElement(\"button\");\n      _fCrop.type=\"button\"; _fCrop.setAttribute(\"aria-label\",\"Crop photo\"); _fCrop.title=\"Crop photo\";\n      _fCrop.innerHTML=`<svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" aria-hidden=\"true\"><path d=\"M7 3v11a3 3 0 0 0 3 3h11\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\"/><path d=\"M17 21V10a3 3 0 0 0-3-3H3\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\"/></svg>`;\n      _fCrop.style.cssText=\"position:absolute;top:8px;right:72px;width:28px;height:28px;border-radius:999px;border:none;background:rgba(0,0,0,0.72);cursor:pointer;z-index:10;display:flex;align-items:center;justify-content:center;padding:0;\";\n      _fCrop.addEventListener(\"click\",(e)=>{ e.preventDefault(); e.stopPropagation(); if(coverState.frontImageId) openCropModal(coverState.frontImageId, { get:()=>coverState.frontImageId, set:(id)=>{ coverState.frontImageId=id; coverState.frontImageUrl=getPhotoById(id)?.url||\"\"; } }); });\n      front.appendChild(_fCrop);\n      const _fFrame = document.createElement(\"button\");\n      _fFrame.type=\"button\"; _fFrame.setAttribute(\"aria-label\",\"Frame shape\"); _fFrame.title=\"Frame shape\";\n      _fFrame.innerHTML=`<svg viewBox=\"0 0 22 22\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"#fff\" stroke-width=\"1.8\"><circle cx=\"11\" cy=\"11\" r=\"9\"/><polygon points=\"11,4 13,8.5 18,9 14.5,12.5 15.5,17.5 11,15 6.5,17.5 7.5,12.5 4,9 9,8.5\" fill=\"#fff\" stroke=\"none\" opacity=\"0.7\"/></svg>`;\n      _fFrame.style.cssText=\"position:absolute;top:8px;right:104px;width:28px;height:28px;border-radius:999px;border:none;background:rgba(0,0,0,0.72);cursor:pointer;z-index:10;display:flex;align-items:center;justify-content:center;padding:0;\";\n      _fFrame.addEventListener(\"click\",(e)=>{ e.preventDefault(); e.stopPropagation(); showDmbFramePicker(_fFrame, coverState.frontImageFrameShape||\"\", (shapeId)=>{ pushUndoState(\"Frame Shape\"); coverState.frontImageFrameShape=shapeId; applyDmbFrameShape(_frontImgDiv, shapeId); saveCoverDesignToStorage(); }); });\n      front.appendChild(_fFrame);\n    } else {\n      // (Magazine grey full-face slot removed \u2014 see the back face above.) Front shows\n      // the background colour; the default free photo frame is seeded in coverObjects.\n      front.style.background = coverState.color || \"#ffffff\";\n    }\n    front.addEventListener(\"click\", (e) => {\n      e.stopPropagation();\n      activeCoverZone = \"front\";\n      if (typeof markActiveCoverZone === \"function\") markActiveCoverZone();\n      if (typeof hideTextToolbar === \"function\") hideTextToolbar();\n    });\n    front.addEventListener(\"dragover\", (e) => { e.preventDefault(); e.dataTransfer.dropEffect = \"copy\"; front.style.outline = \"2px solid #111\"; front.style.outlineOffset = \"-2px\"; });\n    front.addEventListener(\"dragleave\", () => { front.style.outline = \"none\"; });\n    front.addEventListener(\"drop\", (e) => {\n      e.preventDefault(); front.style.outline = \"none\";\n      const payload = getDragPayload(e);\n      const photoId = payload?.photoId || (e.dataTransfer.getData(\"text/plain\") || \"\").trim();\n      if (!photoId) return;\n      const photo = getPhotoById(photoId);\n      if (!photo) return;\n      pushUndoState(\"Drop Photo On Cover Front\");\n      coverState.frontImageUrl = photo.url;\n      coverState.frontImageId = photoId;\n      coverState.frontImageRot = 0;\n      saveCoverDesignToStorage();\n      renderCoverCanvas();\n    });\n\n\n    const overlay = document.createElement(\"div\");\n    overlay.id = \"coverOverlay\";\n    overlay.className = \"dmb-cover-overlay\";\n    overlay.style.zIndex = \"9999\";\n\n    wrap.appendChild(back);\n    if (spine) wrap.appendChild(spine);\n    wrap.appendChild(front);\n    wrap.appendChild(overlay);\n\n    // \u2500\u2500 Builder-only \"Front\" / \"Back\" face labels \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // A small caption directly UNDER each cover face so the customer can tell which\n    // face is the front and which is the back (books AND magazines; the spine gets\n    // nothing). Hung off the WRAP (already position:relative), NOT the faces \u2014 a face\n    // with a photo sets overflow:hidden and would clip a label placed inside it.\n    // Pure UI: pointer-events:none, absolutely positioned so it changes no dimension,\n    // never in the PDF (export uses renderCoverSpread, not this DOM), skipped in render\n    // mode and hidden in the order-preview modal via CSS.\n    if (!_dmbRenderMode()) {\n      var _tagFaceW  = 300;                                 // matches .dmb-cover-face width\n      var _tagSpineW = window.DMB_MAGAZINE_MODE ? 0 : 11;   // matches .dmb-cover-spine width\n      [[\"Back\", 0], [\"Front\", _tagFaceW + _tagSpineW]].forEach(function (p) {\n        var tag = document.createElement(\"div\");\n        tag.className = \"dmb-cover-face-tag\";\n        tag.textContent = p[0];\n        tag.setAttribute(\"aria-hidden\", \"true\");\n        tag.style.left = p[1] + \"px\";\n        tag.style.width = _tagFaceW + \"px\";\n        wrap.appendChild(tag);\n      });\n    }\n\n    // \u2500\u2500 SAFE-ZONE GUIDE LINES \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // Add two vertical dashed guide lines (14px from left/right edge) to each\n    // cover face: back, spine, and front.  They show ONLY while an object is\n    // being dragged (class dmb-cover-guides-on on the wrap) and disappear on\n    // pointerup.  Guides are purely visual (pointer-events:none) \u2014 the customer\n    // can place objects outside them if they wish.\n    //\n    // WARNING: Do NOT add these to the overlay \u2014 they belong inside each face\n    // so they appear behind dragged objects (overlay z:9999) and serve as a\n    // passive reference, never blocking interaction.\n    (function _addCoverSafeGuides() {\n      function addGuidesToFace(faceEl) {\n        [\"left\", \"right\"].forEach(function(side) {\n          var g = document.createElement(\"div\");\n          g.className = \"dmb-cover-safe-guide dmb-cover-safe-guide-\" + side;\n          g.setAttribute(\"aria-hidden\", \"true\");\n          faceEl.appendChild(g);\n        });\n      }\n      addGuidesToFace(back);\n      if (spine) addGuidesToFace(spine);\n      addGuidesToFace(front);\n\n      // Show guides on any pointerdown inside the cover preview.\n      // Capture phase so we catch events on deeply nested children (objects\n      // in the overlay) before they stop propagation.\n      wrap.addEventListener(\"pointerdown\", function() {\n        wrap.classList.add(\"dmb-cover-guides-on\");\n      }, { capture: true });\n\n      // Global pointerup hides guides on all cover previews.\n      // Guard with a flag so we only add this listener once per page load\n      // (renderCoverCanvas is called on every state change \u2014 without the guard\n      //  we would accumulate identical listeners).\n      if (!window._dmbCoverGuideHideWired) {\n        window._dmbCoverGuideHideWired = true;\n        document.addEventListener(\"pointerup\", function() {\n          document.querySelectorAll(\".dmb-cover-preview.dmb-cover-guides-on\")\n            .forEach(function(w) { w.classList.remove(\"dmb-cover-guides-on\"); });\n        });\n      }\n    })();\n    // \u2500\u2500 end safe-zone guides \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n    // \u2500\u2500 Cover drag overlay (same style as spread drop overlay) \u2500\u2500\n    const coverDragOverlay = document.createElement(\"div\");\n    coverDragOverlay.className = \"dmb-cover-drag-overlay\";\n\n    function makeCoverZone(label) {\n      const zone = document.createElement(\"div\");\n      zone.className = \"dmb-cover-drop-zone\";\n      const lbl = document.createElement(\"div\");\n      lbl.className = \"dmb-cover-zone-label\";\n      lbl.textContent = label;\n      zone.appendChild(lbl);\n      return zone;\n    }\n\n    let coverDropZones;\n    if (window.DMB_MAGAZINE_MODE) {\n      const zBack  = makeCoverZone(\"Back\");\n      const zFront = makeCoverZone(\"Front\");\n      coverDragOverlay.appendChild(zBack);\n      coverDragOverlay.appendChild(zFront);\n      coverDropZones = [\n        { el: zBack,  targets: [\"back\"] },\n        { el: zFront, targets: [\"front\"] }\n      ];\n    } else {\n      const zBack       = makeCoverZone(\"Back\");\n      const zBackSpine  = makeCoverZone(\"Back + Spine\");\n      const zFull       = makeCoverZone(\"Full cover\");\n      const zSpineFront = makeCoverZone(\"Spine + Front\");\n      const zFront      = makeCoverZone(\"Front\");\n      coverDragOverlay.appendChild(zBack);\n      coverDragOverlay.appendChild(zBackSpine);\n      coverDragOverlay.appendChild(zFull);\n      coverDragOverlay.appendChild(zSpineFront);\n      coverDragOverlay.appendChild(zFront);\n      coverDropZones = [\n        { el: zBack,       targets: [\"back\"] },\n        { el: zBackSpine,  targets: [\"back\", \"spine\"] },\n        { el: zFull,       targets: [\"back\", \"spine\", \"front\"] },\n        { el: zSpineFront, targets: [\"spine\", \"front\"] },\n        { el: zFront,      targets: [\"front\"] }\n      ];\n    }\n\n    wrap.appendChild(coverDragOverlay);\n\n    let coverWrapEnter = 0;\n    wrap.addEventListener(\"dragenter\", (e) => {\n      if (!getDragPayload(e)) return;\n      coverWrapEnter++;\n      coverDragOverlay.classList.add(\"active\");\n    });\n    wrap.addEventListener(\"dragleave\", (e) => {\n      if (e.relatedTarget && wrap.contains(e.relatedTarget)) return;\n      if (--coverWrapEnter <= 0) { coverWrapEnter = 0; coverDragOverlay.classList.remove(\"active\"); }\n    });\n    wrap.addEventListener(\"dragover\", (e) => { if (getDragPayload(e)) e.preventDefault(); });\n\n    // Peek at the element underneath the cover drag overlay at screen position (cx,cy).\n    // We briefly hide the overlay so elementFromPoint sees what's beneath it.\n    function _elementUnderOverlay(cx, cy) {\n      const saved = coverDragOverlay.style.display;\n      coverDragOverlay.style.display = \"none\";\n      const el = document.elementFromPoint(cx, cy);\n      coverDragOverlay.style.display = saved;\n      return el;\n    }\n\n    // Return the nearest cover photo-frame DOM element at screen point, or null.\n    function _coverFrameElAtPoint(cx, cy) {\n      const under = _elementUnderOverlay(cx, cy);\n      if (!under) return null;\n      // Free photo-frame (in overlay)\n      const free = under.closest(\".dmb-photo-frame-object\");\n      if (free) return free;\n      // Layout frame (direct child of front/back face with data-cover-frame-key)\n      const layout = under.closest(\"[data-cover-frame-key]\");\n      if (layout) return layout;\n      return null;\n    }\n\n    function _clearCoverFrameHover() {\n      document.querySelectorAll(\".dmb-cover-frame-dragover\")\n        .forEach(f => f.classList.remove(\"dmb-cover-frame-dragover\"));\n    }\n\n    coverDropZones.forEach(({ el, targets }) => {\n      let zc = 0;\n      el._pendingCoverFrame = null;\n\n      el.addEventListener(\"dragenter\", (e) => {\n        e.preventDefault(); e.stopPropagation();\n        if (++zc > 0) el.classList.add(\"drag-over\");\n      });\n      el.addEventListener(\"dragleave\", () => {\n        if (--zc <= 0) { zc = 0; el.classList.remove(\"drag-over\"); }\n        _clearCoverFrameHover();\n        el._pendingCoverFrame = null;\n      });\n      el.addEventListener(\"dragover\", (e) => {\n        e.preventDefault(); e.stopPropagation();\n        // Peek under overlay to find frames and show black-border highlight\n        const frameEl = _coverFrameElAtPoint(e.clientX, e.clientY);\n        _clearCoverFrameHover();\n        if (frameEl) { frameEl.classList.add(\"dmb-cover-frame-dragover\"); }\n        el._pendingCoverFrame = frameEl || null;\n      });\n      el.addEventListener(\"drop\", (e) => {\n        e.preventDefault(); e.stopPropagation();\n        _clearCoverFrameHover();\n        coverWrapEnter = 0; coverDragOverlay.classList.remove(\"active\"); el.classList.remove(\"drag-over\");\n        const payload = getDragPayload(e);\n        if (!payload) return;\n        const photoId = payload.photoId || (e.dataTransfer.getData(\"text/plain\") || \"\").trim();\n        if (!photoId) return;\n        const photo = getPhotoById(photoId);\n        if (!photo) return;\n\n        // \u2500\u2500 Route to frame if cursor was over one \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n        const frameEl = el._pendingCoverFrame || _coverFrameElAtPoint(e.clientX, e.clientY);\n        el._pendingCoverFrame = null;\n        if (frameEl) {\n          // Free photo-frame (stored in editor.coverObjects)\n          if (frameEl.dataset.objectId) {\n            const obj = (editor.coverObjects || []).find(o => o.id === frameEl.dataset.objectId);\n            if (obj) {\n              pushUndoState(\"Drop Photo In Cover Frame\");\n              obj.photoId = photoId;\n              saveCoverDesignToStorage();\n              renderCoverCanvas(); renderThumbsRail(); renderPhotoTray();\n              return;\n            }\n          }\n          // Layout frame (coverState.frontFrames / backFrames)\n          if (frameEl.dataset.coverFrameKey) {\n            const fZone = frameEl.dataset.coverZone || \"front\";\n            const fKey  = frameEl.dataset.coverFrameKey;\n            pushUndoState(\"Drop Photo In Cover Layout Frame\");\n            if (fZone === \"front\") { if (!coverState.frontFrames) coverState.frontFrames = {}; coverState.frontFrames[fKey] = photoId; }\n            else                   { if (!coverState.backFrames)  coverState.backFrames  = {}; coverState.backFrames[fKey]  = photoId; }\n            saveCoverDesignToStorage();\n            renderCoverCanvas(); renderThumbsRail(); renderPhotoTray();\n            return;\n          }\n        }\n\n        // \u2500\u2500 Zone drop (original logic) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n        pushUndoState(\"Drop Photo On Cover\");\n        if (targets.length > 1) {\n          // Multi-zone: seamless bleed image\n          if (targets.includes(\"front\")) { coverState.frontImageUrl = \"\"; coverState.frontImageId = \"\"; coverState.frontImageRot = 0; }\n          if (targets.includes(\"back\"))  { coverState.backImageUrl  = \"\"; coverState.backImageId  = \"\"; coverState.backImageRot  = 0; }\n          coverState.spineImageUrl = \"\";\n          coverState.bleedImageUrl = photo.url;\n          coverState.bleedImageId  = photoId;\n          coverState.bleedImageRot = 0;\n          coverState.bleedZone     = targets.includes(\"back\") && targets.includes(\"front\") ? \"full\"\n                                   : targets.includes(\"back\") ? \"spine-back\" : \"front-spine\";\n        } else {\n          // Single zone: individual face image, clear any bleed that covers it\n          if (targets.includes(\"front\")) {\n            coverState.frontImageUrl = photo.url; coverState.frontImageId = photoId; coverState.frontImageRot = 0;\n            if (coverState.bleedZone === \"front-spine\" || coverState.bleedZone === \"full\") { coverState.bleedImageUrl = \"\"; coverState.bleedImageId = \"\"; coverState.bleedZone = \"\"; coverState.bleedImageRot = 0; }\n          }\n          if (targets.includes(\"back\")) {\n            coverState.backImageUrl = photo.url; coverState.backImageId = photoId; coverState.backImageRot = 0;\n            if (coverState.bleedZone === \"spine-back\" || coverState.bleedZone === \"full\") { coverState.bleedImageUrl = \"\"; coverState.bleedImageId = \"\"; coverState.bleedZone = \"\"; coverState.bleedImageRot = 0; }\n          }\n        }\n        saveCoverDesignToStorage();\n        renderCoverCanvas();\n        renderThumbsRail();\n        renderPhotoTray();\n      });\n    });\n\n    spreadCanvas.appendChild(wrap);\n\n    // Fit the cover (incl. its Front/Back labels) into the canvas height so there is\n    // no vertical scroll. Desktop only; re-fits on window resize.\n    if (!window._dmbCoverFitResizeWired) {\n      window._dmbCoverFitResizeWired = true;\n      window.addEventListener(\"resize\", function () {\n        if (builderMode === \"cover\") requestAnimationFrame(_dmbFitCoverIntoView);\n      });\n    }\n    requestAnimationFrame(_dmbFitCoverIntoView);\n\n    // Render photo grid layout frames inside each face (clipped to face bounds)\n    function renderCoverFaceFrames(faceEl, zone) {\n      const layoutId = zone === \"front\" ? coverState.frontLayout : coverState.backLayout;\n      if (!layoutId) return;\n      const defs   = TEMPLATE_DEFS[layoutId] || [];\n      const frames = zone === \"front\" ? coverState.frontFrames : coverState.backFrames;\n      faceEl.style.overflow = \"hidden\";\n      defs.forEach((f) => {\n        const fw = document.createElement(\"div\");\n        fw.style.cssText = `position:absolute;left:${f.x}%;top:${f.y}%;width:${f.w}%;height:${f.h}%;border:1px solid #eee;background:#f5f5f5;overflow:hidden;z-index:2;box-sizing:border-box;`;\n        fw.dataset.coverZone = zone;\n        fw.dataset.coverFrameKey = f.key;\n        const pid = frames[f.key] || null;\n        if (!pid) {\n          const hint = document.createElement(\"div\");\n          hint.style.cssText = \"position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;pointer-events:none;background:rgba(220,220,220,0.55);border:1.5px dashed #ccc;border-radius:2px;\";\n          hint.innerHTML = `<svg width=\"26\" height=\"26\" viewBox=\"0 0 26 26\" fill=\"none\"><circle cx=\"13\" cy=\"13\" r=\"11\" stroke=\"#c0c0c0\" stroke-width=\"1.5\"/><line x1=\"13\" y1=\"7.5\" x2=\"13\" y2=\"18.5\" stroke=\"#bbb\" stroke-width=\"2\" stroke-linecap=\"round\"/><line x1=\"7.5\" y1=\"13\" x2=\"18.5\" y2=\"13\" stroke=\"#bbb\" stroke-width=\"2\" stroke-linecap=\"round\"/></svg><span style=\"font-size:8px;font-weight:900;color:#bbb;letter-spacing:0.4px;text-transform:uppercase;\">Drop photo</span>`;\n          fw.appendChild(hint);\n        } else {\n          const photo = getPhotoById(pid);\n          if (photo) {\n            const frameRots = zone === \"front\" ? coverState.frontFrameRot : coverState.backFrameRot;\n            const frameShapes = zone === \"front\" ? coverState.frontFrameShapes : coverState.backFrameShapes;\n            const frameRot = frameRots[f.key] || 0;\n            const frameShape = frameShapes[f.key] || \"\";\n            const img = document.createElement(\"img\");\n            _dmbBindPhotoImg(img, photo);\n            img.draggable = false;\n            img.style.cssText = `position:absolute;inset:0;width:100%;height:100%;object-fit:cover;pointer-events:none;transform:rotate(${frameRot}deg);transform-origin:center center;`;\n            if (frameShape) applyDmbFrameShape(img, frameShape);\n            fw.appendChild(img);\n            // Control buttons \u2014 X, rotate, crop, frames\n            const _lRm = document.createElement(\"button\");\n            _lRm.type=\"button\"; _lRm.setAttribute(\"aria-label\",\"Remove photo\"); _lRm.title=\"Remove photo\";\n            _lRm.innerHTML=`<svg viewBox=\"0 0 14 14\" width=\"13\" height=\"13\" aria-hidden=\"true\"><path d=\"M2 2l10 10M12 2L2 12\" stroke=\"#fff\" stroke-width=\"2.2\" stroke-linecap=\"round\"/></svg>`;\n            _lRm.style.cssText=\"position:absolute;top:4px;right:4px;width:24px;height:24px;border-radius:999px;border:none;background:rgba(0,0,0,0.72);color:#fff;cursor:pointer;z-index:10;display:flex;align-items:center;justify-content:center;padding:0;\";\n            _lRm.addEventListener(\"click\",(e)=>{ e.preventDefault(); e.stopPropagation(); pushUndoState(\"Remove Photo\"); if(zone===\"front\") coverState.frontFrames[f.key]=null; else coverState.backFrames[f.key]=null; saveCoverDesignToStorage(); renderCoverCanvas(); renderThumbsRail(); renderPhotoTray(); });\n            fw.appendChild(_lRm);\n            const _lRot = document.createElement(\"button\");\n            _lRot.type=\"button\"; _lRot.setAttribute(\"aria-label\",\"Rotate 90 degrees anti-clockwise\"); _lRot.title=\"Rotate 90\u00B0 anti-clockwise\";\n            _lRot.innerHTML=`<svg viewBox=\"0 0 24 24\" width=\"12\" height=\"12\" aria-hidden=\"true\"><path d=\"M12 4a8 8 0 1 0 7.2 4.5\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\"/><path d=\"M19.5 4.5v5h-5\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>`;\n            _lRot.style.cssText=\"position:absolute;top:4px;right:32px;width:24px;height:24px;border-radius:999px;border:none;background:rgba(0,0,0,0.72);cursor:pointer;z-index:10;display:flex;align-items:center;justify-content:center;padding:0;\";\n            _lRot.addEventListener(\"click\",(e)=>{ e.preventDefault(); e.stopPropagation(); pushUndoState(\"Rotate Photo\"); const rots = zone===\"front\"?coverState.frontFrameRot:coverState.backFrameRot; rots[f.key]=((rots[f.key]||0)-90+360)%360; saveCoverDesignToStorage(); renderCoverCanvas(); renderThumbsRail(); });\n            fw.appendChild(_lRot);\n            const _lCrop = document.createElement(\"button\");\n            _lCrop.type=\"button\"; _lCrop.setAttribute(\"aria-label\",\"Crop photo\"); _lCrop.title=\"Crop photo\";\n            _lCrop.innerHTML=`<svg viewBox=\"0 0 24 24\" width=\"12\" height=\"12\" aria-hidden=\"true\"><path d=\"M7 3v11a3 3 0 0 0 3 3h11\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\"/><path d=\"M17 21V10a3 3 0 0 0-3-3H3\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\"/></svg>`;\n            _lCrop.style.cssText=\"position:absolute;top:4px;right:60px;width:24px;height:24px;border-radius:999px;border:none;background:rgba(0,0,0,0.72);cursor:pointer;z-index:10;display:flex;align-items:center;justify-content:center;padding:0;\";\n            _lCrop.addEventListener(\"click\",(e)=>{ e.preventDefault(); e.stopPropagation(); openCropModal(pid, { get:()=>{ const m=zone===\"front\"?coverState.frontFrames:coverState.backFrames; return m[f.key]; }, set:(id)=>{ const m=zone===\"front\"?coverState.frontFrames:coverState.backFrames; m[f.key]=id; } }); });\n            fw.appendChild(_lCrop);\n            const _lFrame = document.createElement(\"button\");\n            _lFrame.type=\"button\"; _lFrame.setAttribute(\"aria-label\",\"Frame shape\"); _lFrame.title=\"Frame shape\";\n            _lFrame.innerHTML=`<svg viewBox=\"0 0 22 22\" width=\"12\" height=\"12\" fill=\"none\" stroke=\"#fff\" stroke-width=\"1.8\"><circle cx=\"11\" cy=\"11\" r=\"9\"/><polygon points=\"11,4 13,8.5 18,9 14.5,12.5 15.5,17.5 11,15 6.5,17.5 7.5,12.5 4,9 9,8.5\" fill=\"#fff\" stroke=\"none\" opacity=\"0.7\"/></svg>`;\n            _lFrame.style.cssText=\"position:absolute;top:4px;right:88px;width:24px;height:24px;border-radius:999px;border:none;background:rgba(0,0,0,0.72);cursor:pointer;z-index:10;display:flex;align-items:center;justify-content:center;padding:0;\";\n            _lFrame.addEventListener(\"click\",(e)=>{ e.preventDefault(); e.stopPropagation(); const shapes = zone===\"front\"?coverState.frontFrameShapes:coverState.backFrameShapes; showDmbFramePicker(_lFrame, shapes[f.key]||\"\", (shapeId)=>{ pushUndoState(\"Frame Shape\"); shapes[f.key]=shapeId; applyDmbFrameShape(img, shapeId); saveCoverDesignToStorage(); }); });\n            fw.appendChild(_lFrame);\n          }\n        }\n        fw.addEventListener(\"dragover\", (e) => {\n          e.preventDefault(); e.stopPropagation();\n          e.dataTransfer.dropEffect = \"copy\";\n          fw.style.outline = \"2px solid #111\"; fw.style.outlineOffset = \"-2px\";\n        });\n        fw.addEventListener(\"dragleave\", () => { fw.style.outline = \"none\"; });\n        fw.addEventListener(\"drop\", (e) => {\n          e.preventDefault(); e.stopPropagation();\n          fw.style.outline = \"none\";\n          const payload = getDragPayload(e);\n          const photoId = (payload?.photoId) || ((e.dataTransfer.getData(\"text/plain\") || \"\").trim());\n          if (!photoId) return;\n          pushUndoState(\"Drop Photo In Cover Frame\");\n          if (zone === \"front\") coverState.frontFrames[f.key] = photoId;\n          else coverState.backFrames[f.key] = photoId;\n          saveCoverDesignToStorage();\n          renderCoverCanvas();\n        });\n        faceEl.appendChild(fw);\n      });\n    }\n    renderCoverFaceFrames(back, \"back\");\n    renderCoverFaceFrames(front, \"front\");\n\n    // \u2500\u2500 Seamless bleed image (spans back+spine+front or a subset) \u2500\u2500\n    if (coverState.bleedImageUrl && coverState.bleedZone) {\n      const _bleedZoneMap = {\n        \"spine-back\":  { x: 0,              w: COVER_BACK_W + COVER_SPINE_W },\n        \"full\":        { x: 0,              w: COVER_BACK_W + COVER_SPINE_W + 300 },\n        \"front-spine\": { x: COVER_BACK_W,   w: COVER_SPINE_W + 300 }\n      };\n      const _bz = _bleedZoneMap[coverState.bleedZone];\n      if (_bz) {\n        const bleedWrap = document.createElement(\"div\");\n        // Extend 1px beyond each covered edge to absorb border seams\n        const _bleedLeft = _bz.x > 0 ? _bz.x - 1 : 0;\n        const _bleedW    = _bz.x > 0 ? _bz.w + 2 : _bz.w + 1;\n        bleedWrap.style.cssText = `position:absolute;left:${_bleedLeft}px;top:0;width:${_bleedW}px;height:100%;overflow:hidden;z-index:5;`;\n        const bleedImg = document.createElement(\"div\");\n        bleedImg.style.cssText = `position:absolute;inset:0;background:url(${coverState.bleedImageUrl}) center/cover no-repeat;transform:rotate(${coverState.bleedImageRot||0}deg);transform-origin:center center;pointer-events:none;`;\n        bleedWrap.appendChild(bleedImg);\n        // Control buttons \u2014 top-right of the bleed wrap (left/back corner side)\n        const _blRm = document.createElement(\"button\");\n        _blRm.type=\"button\"; _blRm.setAttribute(\"aria-label\",\"Remove photo\"); _blRm.title=\"Remove photo\";\n        _blRm.innerHTML=`<svg viewBox=\"0 0 14 14\" width=\"13\" height=\"13\" aria-hidden=\"true\"><path d=\"M2 2l10 10M12 2L2 12\" stroke=\"#fff\" stroke-width=\"2.2\" stroke-linecap=\"round\"/></svg>`;\n        _blRm.style.cssText=\"position:absolute;top:8px;right:8px;width:28px;height:28px;border-radius:999px;border:none;background:rgba(0,0,0,0.72);color:#fff;cursor:pointer;z-index:10;display:flex;align-items:center;justify-content:center;padding:0;\";\n        _blRm.addEventListener(\"click\",(e)=>{ e.preventDefault(); e.stopPropagation(); pushUndoState(\"Remove Bleed Photo\"); coverState.bleedImageUrl=\"\"; coverState.bleedImageId=\"\"; coverState.bleedZone=\"\"; coverState.bleedImageRot=0; saveCoverDesignToStorage(); renderCoverCanvas(); renderThumbsRail(); renderPhotoTray(); });\n        bleedWrap.appendChild(_blRm);\n        const _blRot = document.createElement(\"button\");\n        _blRot.type=\"button\"; _blRot.setAttribute(\"aria-label\",\"Rotate 90 degrees anti-clockwise\"); _blRot.title=\"Rotate 90\u00B0 anti-clockwise\";\n        _blRot.innerHTML=`<svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" aria-hidden=\"true\"><path d=\"M12 4a8 8 0 1 0 7.2 4.5\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\"/><path d=\"M19.5 4.5v5h-5\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>`;\n        _blRot.style.cssText=\"position:absolute;top:8px;right:40px;width:28px;height:28px;border-radius:999px;border:none;background:rgba(0,0,0,0.72);cursor:pointer;z-index:10;display:flex;align-items:center;justify-content:center;padding:0;\";\n        _blRot.addEventListener(\"click\",(e)=>{ e.preventDefault(); e.stopPropagation(); pushUndoState(\"Rotate Bleed Photo\"); coverState.bleedImageRot=((coverState.bleedImageRot||0)-90+360)%360; saveCoverDesignToStorage(); renderCoverCanvas(); renderThumbsRail(); });\n        bleedWrap.appendChild(_blRot);\n        const _blCrop = document.createElement(\"button\");\n        _blCrop.type=\"button\"; _blCrop.setAttribute(\"aria-label\",\"Crop photo\"); _blCrop.title=\"Crop photo\";\n        _blCrop.innerHTML=`<svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" aria-hidden=\"true\"><path d=\"M7 3v11a3 3 0 0 0 3 3h11\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\"/><path d=\"M17 21V10a3 3 0 0 0-3-3H3\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\"/></svg>`;\n        _blCrop.style.cssText=\"position:absolute;top:8px;right:72px;width:28px;height:28px;border-radius:999px;border:none;background:rgba(0,0,0,0.72);cursor:pointer;z-index:10;display:flex;align-items:center;justify-content:center;padding:0;\";\n        _blCrop.addEventListener(\"click\",(e)=>{ e.preventDefault(); e.stopPropagation(); if(coverState.bleedImageId) openCropModal(coverState.bleedImageId, { get:()=>coverState.bleedImageId, set:(id)=>{ coverState.bleedImageId=id; coverState.bleedImageUrl=getPhotoById(id)?.url||\"\"; } }); });\n        bleedWrap.appendChild(_blCrop);\n        wrap.insertBefore(bleedWrap, overlay);\n      }\n    }\n\n    renderFreeFrameObjects(overlay, { targetType: \"cover\" });\n\n    // Re-create user-added text nodes (survive re-renders via editor.coverUserTexts)\n    // All user texts live in the overlay so they drag seamlessly across back/spine/front\n    if (Array.isArray(editor.coverUserTexts)) {\n      const _magFrontX = window.DMB_MAGAZINE_MODE ? COVER_BACK_W : COVER_FRONT_X;\n      const zoneOffsets = { back: 40, spine: COVER_BACK_W + 5, front: _magFrontX + 40 };\n      editor.coverUserTexts.forEach(({ id, zone }) => {\n        const saved = coverState.textStyles && coverState.textStyles[id];\n        const utxt = document.createElement(\"div\");\n        utxt.id = id;\n        utxt.contentEditable = \"true\";\n        utxt.classList.add(\"dmb-editable-text\");\n        utxt.style.position = \"absolute\";\n        utxt.style.minWidth = \"120px\";\n        utxt.style.padding = \"2px 6px\";\n        utxt.style.background = \"transparent\";\n        utxt.style.outline = \"none\";\n        utxt.style.fontSize = saved?.fontSize || \"28px\";\n        utxt.style.fontWeight = \"800\";\n        utxt.style.color = saved?.color || \"#666\";\n        utxt.style.fontFamily = saved?.fontFamily || \"'Londrina Solid', sans-serif\";\n        utxt.style.left = saved?.left || (zoneOffsets[zone] ?? (_magFrontX + 40)) + \"px\";\n        // `top` is stored canonical (portrait face). Scale to the current face so\n        // landscape cover text sits INSIDE the shorter face instead of hanging off\n        // below it. Portrait scale = 1 \u2192 identical.\n        var _utTopRaw = saved?.top || \"40px\";\n        var _utTopNum = parseFloat(_utTopRaw);\n        utxt.style.top = (isFinite(_utTopNum) && !String(_utTopRaw).endsWith(\"%\"))\n          ? ((_utTopNum * _dmbCoverTopScale()) + \"px\") : _utTopRaw;\n        // \u2500\u2500 Cross-device lock \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n        // Pin the box to its design-time width so a ROTATED text pivots around the\n        // exact same centre on every device. Without this the box auto-sizes to the\n        // text, and the same font measures a few px differently on iOS vs laptop,\n        // shifting rotated text on iPad/phone. boxWidth is the laptop-measured width,\n        // so the laptop is unchanged and the iPad/phone now match it.\n        if (saved?.boxWidth && parseFloat(saved.boxWidth) > 0) {\n          utxt.style.width = saved.boxWidth + \"px\";\n        } else if (saved?.width) {\n          utxt.style.width = saved.width;\n        }\n        // An explicit width outranks the 120px new-box minimum set above, otherwise a\n        // cover box narrowed with the side grip snapped back wide on re-render.\n        if (utxt.style.width) utxt.style.minWidth = \"0\";\n        // Ensure z is always persisted \u2014 if not saved yet, assign one now so\n        // the PDF render uses the same value as the builder DOM.\n        if (!saved?.z) {\n          const _newZ = String(Date.now());\n          if (!coverState.textStyles) coverState.textStyles = {};\n          if (!coverState.textStyles[id]) coverState.textStyles[id] = {};\n          coverState.textStyles[id].z = _newZ;\n        }\n        utxt.dataset.z = coverState.textStyles?.[id]?.z || String(Date.now());\n        utxt.style.zIndex = utxt.dataset.z;\n        if (saved?.textAlign) utxt.style.textAlign = saved.textAlign;\n        if (saved?.textDecoration) utxt.style.textDecoration = saved.textDecoration;\n        if (saved?.whiteSpace) utxt.style.whiteSpace = saved.whiteSpace;\n        // In landscape, uniformly shrink the text (font + box) by the same factor\n        // as the vertical position, so the composition keeps the SAME proportions\n        // it had on the taller portrait cover \u2014 just fitted. Display-only: stored\n        // fontSize/width stay canonical (no save drift). transform-origin top-left\n        // keeps the element anchored at its (scaled) top-left position.\n        if (saved?.transform) utxt.style.transform = saved.transform;\n        if (saved?.rotate) utxt.dataset.rotate = saved.rotate;\n        utxt.textContent = saved?.text || \"\";\n        if (saved?.rich) { try { _dmbApplyRich(utxt, saved.rich); } catch (eR) {} }\n        utxt.dataset.placeholder = \"Type here\";\n        attachTextNodeHandlers(utxt);\n        // Sync to panel when text is edited directly on the cover\n        utxt.addEventListener(\"input\", () => {\n          const panelInput = document.querySelector(`#dmbCoverTextList input[data-text-id=\"${id}\"]`);\n          if (panelInput) panelInput.value = utxt.textContent;\n        });\n        overlay.appendChild(utxt);  // overlay spans full cover \u2014 seamless drag\n      });\n    }\n\n    // Logo watermark \u2014 appended to overlay last so it always renders above stickers/maps/texts\n    if (window.DMB_LOGO_URL) {\n      const logoImg = document.createElement(\"img\");\n      logoImg.className = \"dmb-logo-watermark\";\n      logoImg.src = window.DMB_LOGO_URL;\n      const _logoLeft = COVER_BACK_W - Math.round(COVER_BACK_W * 0.06) - 90;\n      const _logoZ = Math.max(\n        ...(editor.coverObjects || []).map(function(o) { return Number(o.z) || 0; }),\n        ...(editor.coverUserTexts || []).map(function(u) { var s = coverState.textStyles && coverState.textStyles[u.id]; return s ? (Number(s.z) || 0) : 0; }),\n        99999\n      ) + 1;\n      logoImg.style.cssText = \"position:absolute;bottom:6%;left:\" + _logoLeft + \"px;width:90px;opacity:0.85;pointer-events:none;display:block;z-index:\" + _logoZ + \";\";\n      overlay.appendChild(logoImg);\n    }\n\n    if (typeof renderCoverTextList === \"function\") renderCoverTextList();\n\n    if (typeof markActiveCoverZone === \"function\") {\n      markActiveCoverZone();\n    }\n\n    // \u2500\u2500 Capture the EXACT rendered box size for every cover text node \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // The PDF uses boxWidth/boxHeight to compute the rotation pivot\n    // (center = left + boxWidth/2). It MUST equal the browser's real\n    // offsetWidth/Height or rotated text lands in the wrong place.\n    //\n    // FIX: always OVERWRITE (do not skip when a value already exists) \u2014 templates\n    // can ship a stale boxWidth (authored under different width semantics), and a\n    // stale pivot slides rotated text in the PDF. Also re-measure AFTER fonts load,\n    // because measuring with a fallback font gives the wrong width.\n    const _captureCoverTextBoxes = () => {\n      // RENDER MODE: never re-measure. The saved geometry was captured by the\n      // CUSTOMER's own browser with this same code, so freezing it reproduces exactly\n      // what they saw; re-measuring here would use the container's fonts and can move\n      // a word wrap (a visible defect, not sub-pixel noise). &livetext=1 opts back in.\n      // This guard must live HERE: renderCoverCanvas also schedules this capture via\n      // requestAnimationFrame and document.fonts.ready, which the call-site guards miss.\n      if (_dmbRenderMode() && !_dmbRenderLiveText()) return;\n      if (!coverState.textStyles) return;\n      const _ov = document.getElementById(\"coverOverlay\");\n      if (!_ov) return;\n      const _ovr = _ov.getBoundingClientRect();\n      // Actual on-screen scale of the overlay (handles the zoom transform).\n      const _Z = (_ovr.width / (_ov.offsetWidth || 622)) || 1;\n      // id \u2192 cover zone (front/spine/back) so we can special-case the spine.\n      const _zoneById = {};\n      (editor.coverUserTexts || []).forEach(t => { if (t && t.id) _zoneById[t.id] = t.zone; });\n      let _changed = false;\n      _ov.querySelectorAll(\".dmb-editable-text\").forEach(node => {\n        if (!node.id || !coverState.textStyles[node.id]) return;\n        const s = coverState.textStyles[node.id];\n        if (!(node.offsetWidth > 0)) return;\n\n        // Box AABB centre (= rotation centre) in cover-space px.\n        const r = node.getBoundingClientRect();\n        let nvx = ((r.left + r.width  / 2) - _ovr.left) / _Z;\n        const nvy = ((r.top  + r.height / 2) - _ovr.top)  / _Z;\n\n        // \u2500\u2500 ROBUST GLYPH CENTRE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n        // The letters do NOT necessarily sit at the box centre (line-height /\n        // vertical-align push them around inside the box). Measuring a Range on a\n        // rotated node is unreliable in Safari, so: temporarily strip the rotation,\n        // measure the real glyph box vs the element box (no repaint happens between\n        // these synchronous reads), restore the rotation, then rotate the glyph's\n        // offset and add it to the box centre. This yields the letters' TRUE centre.\n        let gcx = nvx, gcy = nvy;\n        try {\n          const _tf = node.style.transform;\n          node.style.transform = \"none\";\n          const _ur = node.getBoundingClientRect();              // un-rotated box\n          const _rng = document.createRange(); _rng.selectNodeContents(node);\n          const _gr = _rng.getBoundingClientRect();              // un-rotated glyphs\n\n          // \u2500\u2500 Capture the browser's REAL line breaks for wrapped boxes \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n          // Boxes narrowed with the side grip wrap at word boundaries\n          // (white-space:pre-wrap). The PDF used to RE-wrap with canvas\n          // measurements, which can break at a different word than the builder\n          // shows. Read the rendered lines here (word rects grouped by top, while\n          // the node is un-rotated) and store them so the PDF reuses them 1:1.\n          // Node is already un-rotated here (transform \"none\" above), so the shared\n          // capture measures directly. One source of truth: _dmbCaptureRenderedLines.\n          const _rlJson = _dmbCaptureRenderedLines(node, true);\n          if (s.renderedLines !== _rlJson) _changed = true;\n          s.renderedLines = _rlJson;   // \"\" when not wrapped \u2192 PDF falls back to \\n split\n          // Same measurement, run by run, for a box whose words carry their own\n          // look. Only measured when there is something to measure.\n          const _rrJson = s.rich ? _dmbCaptureLineRuns(node, true) : \"\";\n          if (s.richLines !== _rrJson) _changed = true;\n          s.richLines = _rrJson;\n\n          node.style.transform = _tf;                            // restore (no paint between)\n          if (_gr && _gr.width > 0 && _gr.height > 0) {\n            const _gdx = ((_gr.left + _gr.width  / 2) - (_ur.left + _ur.width  / 2)) / _Z;\n            const _gdy = ((_gr.top  + _gr.height / 2) - (_ur.top  + _ur.height / 2)) / _Z;\n            // Effective rotation: dataset.rotate, or the rotate() inside the CSS\n            // transform string. Spine/centred text keeps its rotation in the\n            // transform (translate(-50%,-50%) rotate(-90deg)) with dataset.rotate=0,\n            // so without this the glyph offset would rotate by the wrong angle.\n            let _eff = parseFloat(node.dataset.rotate || \"0\") || 0;\n            if (!_eff) {\n              const _m = (_tf || \"\").match(/rotate\\(\\s*(-?[\\d.]+)deg/);\n              if (_m) _eff = parseFloat(_m[1]) || 0;\n            }\n            const _rad = _eff * Math.PI / 180;\n            gcx = nvx + (_gdx * Math.cos(_rad) - _gdy * Math.sin(_rad));\n            gcy = nvy + (_gdx * Math.sin(_rad) + _gdy * Math.cos(_rad));\n          }\n        } catch (e) {}\n\n        // \u2500\u2500 SPINE: keep the LETTERS horizontally centred on the spine \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n        // Centre on the glyph centre (gcx), NOT the box centre: for \u221290\u00B0 spine text\n        // the in-box line-height gap (smaller above the line than below) becomes a\n        // HORIZONTAL offset once rotated, so centring the box leaves the letters\n        // visibly off to one side. Shifting style.left by (spineCx \u2212 gcx) lands the\n        // letters dead-centre. Vertical untouched. Books only \u2014 magazines have none.\n        // \"On the spine\" = saved zone is spine OR the box centre sits in the strip\n        // [COVER_BACK_W, COVER_BACK_W + COVER_SPINE_W] (catches dragged-on texts\n        // whose stored zone was never updated).\n        const _onSpine = !window.DMB_MAGAZINE_MODE &&\n          (_zoneById[node.id] === \"spine\" ||\n           (nvx >= COVER_BACK_W && nvx <= COVER_BACK_W + COVER_SPINE_W));\n        if (_onSpine) {\n          const _spineCx = COVER_BACK_W + COVER_SPINE_W / 2;\n          const _dx = _spineCx - gcx;\n          if (Math.abs(_dx) > 0.5) {\n            node.style.left = ((parseFloat(node.style.left) || 0) + _dx) + \"px\";\n            s.left = node.style.left;\n            nvx += _dx;       // box centre shifts with the box\n            gcx = _spineCx;   // letters now sit exactly on the spine centre\n            _changed = true;\n          }\n          // Correct a stale zone so the drag lock + future logic treat it as spine.\n          if (_zoneById[node.id] !== \"spine\") {\n            const _ut = (editor.coverUserTexts || []).find((en) => en.id === node.id);\n            if (_ut) { _ut.zone = \"spine\"; _zoneById[node.id] = \"spine\"; }\n          }\n        }\n\n        if (s.boxWidth !== String(node.offsetWidth) || s.visCx !== String(nvx) ||\n            s.glyphCx !== String(gcx) || s.glyphCy !== String(gcy)) _changed = true;\n        s.boxWidth  = String(node.offsetWidth);\n        s.boxHeight = String(node.offsetHeight);\n        s.visCx = String(nvx);\n        s.visCy = String(nvy);\n        s.glyphCx = String(gcx);\n        s.glyphCy = String(gcy);\n      });\n      // The PDF preview is cached; refresh it so it picks up the new geometry.\n      if (_changed) { try { _dmbPvMarkDirty(\"cover\"); } catch (e) {} }\n    };\n    // Expose to the export path (renderCoverSpread) so it can force a fresh capture\n    // in the current orientation right before drawing the cover.\n    _dmbCaptureCoverTextBoxesFn = _captureCoverTextBoxes;\n    requestAnimationFrame(_captureCoverTextBoxes);\n    if (document.fonts && document.fonts.ready) {\n      document.fonts.ready.then(() => requestAnimationFrame(_captureCoverTextBoxes));\n    }\n\n    spreadIndexEl.textContent = \"Cover\";\n  }\n\n  updatePrice();\n  refreshPhotoRule();\n  _syncSpreadAdderUI(); // sync counter display on load\n  initCoverDesigner();\n\n  wireTextToolbar();\n  ensureFrameToolbar();\n  initCoverZoneButtons();\n\n  if (builderStep) {\n    initUnifiedTabs();\n    builderMode = \"cover\";\n    syncBuilderModeButtons();\n    renderCoverCanvas();\n    renderThumbsRail();\n\n    if (bookMeta) {\n      const selectedPages = Number(pagesEl?.value || 0);\n      if (selectedPages > 0) {\n        bookMeta.textContent = `${selectedPages} pages (${Math.max(0, selectedPages - 1)} to design) \u2022 ${editor.photos.filter((p) => !_dmbIsCropDerived(p)).length} photos`;\n      } else {\n        bookMeta.textContent = \"Start by choosing pages, photos, and customizing your cover.\";\n      }\n    }\n  }\n\n  if (templateParam && restoreParam !== \"1\") {\n    // Load the user's saved template state \u2014 skip if ?restore=1 is handling it.\n    loadTemplateByKey(templateParam);\n  }\n\n  // ---------- editor core ----------\n  function getUsedPhotoIds() {\n    const used = new Set();\n\n    // From page 0, not page 1: Magic puts a photo in page 0's slot A, and starting\n    // at 1 meant that photo was reported UNUSED - so a second Magic run placed it\n    // again somewhere else (a duplicate) and the \"Hide used\" filter kept showing it.\n    for (let p = 0; p <= editor.totalPages; p++) {\n      const tid = getPageTemplateId(p);\n\n      if (tid) {\n        const fm = editor.pageFrames?.[p] || {};\n        Object.values(fm).forEach((id) => { if (id) used.add(id); });\n      } else {\n        const A = editor.pageSlots[p]?.A;\n        const B = editor.pageSlots[p]?.B;\n        if (A) used.add(A);\n        if (B) used.add(B);\n      }\n    }\n\n    // Also include full-spread photos\n    if (editor.spreadPhoto) {\n      Object.values(editor.spreadPhoto).forEach((sp) => { if (sp?.photoId) used.add(sp.photoId); });\n    }\n\n    // Include photos placed in free photo frames (editor.pageObjects)\n    if (editor.pageObjects) {\n      Object.values(editor.pageObjects).forEach((objs) => {\n        if (!Array.isArray(objs)) return;\n        objs.forEach((o) => { if (o.type === \"photo-frame\" && o.photoId) used.add(o.photoId); });\n      });\n    }\n    // Include free frames on cover\n    if (Array.isArray(editor.coverObjects)) {\n      editor.coverObjects.forEach((o) => { if (o.type === \"photo-frame\" && o.photoId) used.add(o.photoId); });\n    }\n\n    // Include page 0 (special page)\n    const tid0 = getPageTemplateId(0);\n    if (tid0) {\n      const fm0 = editor.pageFrames?.[0] || {};\n      Object.values(fm0).forEach((id) => { if (id) used.add(id); });\n    } else {\n      const A0 = editor.pageSlots[0]?.A;\n      const B0 = editor.pageSlots[0]?.B;\n      if (A0) used.add(A0);\n      if (B0) used.add(B0);\n    }\n\n    // A placement holding a DERIVED crop means its ORIGINAL source is \"used\" too \u2014 so the\n    // original greys out / hides in the tray just like a normally-placed photo.\n    Array.from(used).forEach((id) => { const s = _dmbCropSourceId(id); if (s !== id) used.add(s); });\n\n    return used;\n  }\n\n  function getPhotoById(id) {\n    return editor.photos.find((p) => p.id === id) || null;\n  }\n\n  // \u2500\u2500 White-photo recovery \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // Placed/tray/thumbnail images render from a local object URL of the picked\n  // file. That blob can die mid-session: the browser evicts it under memory\n  // pressure (a batch of large photos) or simply can't decode the format\n  // (e.g. iPhone HEIC). The <img> then shows a blank WHITE box with no way back.\n  // But every photo was streamed to R2 at pick time, so we re-fetch the bytes by\n  // pid, rebuild a fresh object URL, update the shared record (so later renders\n  // use the good URL too), and retry the element. Fetches are de-duped per pid so\n  // N on-screen copies of the same photo trigger just one request.\n  const _dmbPhotoRecovery = {};\n  function _dmbRecoverPhotoUrl(ph) {\n    if (!ph || !ph.id) return Promise.resolve(null);\n    if (_dmbPhotoRecovery[ph.id]) return _dmbPhotoRecovery[ph.id];\n    const p = (async function () {\n      if (!window.DMB_DESIGNS_PROXY) return null;\n      try {\n        const designId = getCustomerSaveKey().replace(/^dmb_progress_/, \"\");\n        // Identity-correct fetch: works for signed-in customers too (not just\n        // guests). This is what lets a released or evicted photo self-heal on\n        // screen for a logged-in user.\n        const blob = await _dmbFetchPhotoBlob(designId, ph.id);\n        if (!blob || !blob.size) return null;\n        const fresh = URL.createObjectURL(blob);\n        ph.url = fresh;\n        try { if (!ph.file || !ph.file.size) ph.file = new File([blob], ph.filename || \"photo.jpg\", { type: blob.type || \"image/jpeg\" }); } catch (e) {}\n        return fresh;\n      } catch (e) { return null; }\n    })();\n    _dmbPhotoRecovery[ph.id] = p;\n    // Drop the cache shortly after it settles so a later transient can retry.\n    p.then(function () {}, function () {}).then(function () {\n      setTimeout(function () { if (_dmbPhotoRecovery[ph.id] === p) delete _dmbPhotoRecovery[ph.id]; }, 4000);\n    });\n    return p;\n  }\n  // Point an <img> at a photo's URL AND wire self-healing: on a load error, pull\n  // the bytes back from R2 once and swap in a fresh URL. One recovery try per\n  // element (no retry loop if the photo is truly gone).\n  function _dmbBindPhotoImg(img, ph) {\n    if (!img) return;\n    img.src = (ph && ph.url) ? ph.url : \"\";\n    if (!ph || img._dmbRecoverBound) return;\n    img._dmbRecoverBound = true;\n    img.addEventListener(\"error\", function () {\n      if (img._dmbRecoverTried) return;\n      img._dmbRecoverTried = true;\n      _dmbRecoverPhotoUrl(ph).then(function (fresh) { if (fresh) img.src = fresh; });\n    });\n  }\n\n  function applyRotationAndCover(imgEl, boxEl, deg) {\n    imgEl.style.objectFit = \"cover\";\n    imgEl.style.objectPosition = \"center\";\n    imgEl.style.transformOrigin = \"center\";\n\n    if (deg % 180 !== 0) {\n      const w = boxEl.clientWidth || 1;\n      const h = boxEl.clientHeight || 1;\n      const s = Math.max(w / h, h / w);\n      imgEl.style.transform = `rotate(${deg}deg) scale(${s})`;\n    } else {\n      imgEl.style.transform = `rotate(${deg}deg)`;\n    }\n  }\n\n  async function getImageMetaFromFile(file) {\n    const url = URL.createObjectURL(file);\n    try {\n      const img = new Image();\n      img.decoding = \"async\";\n      // Never await img.decode() unguarded: it can stay unsettled forever (large\n      // image + tab not painting). This runs while HOLDING the meta semaphore, so\n      // one hang here would leak a slot and, after a few, block every photo-meta\n      // read \u2014 i.e. the builder would never finish opening. Wait for load (that's\n      // all we need to read naturalWidth/Height) and time-box the whole thing.\n      const w8 = new Promise((res, rej) => {\n        img.onload = () => res(true);\n        img.onerror = () => rej(new Error(\"image load failed\"));\n      });\n      img.src = url;\n      await Promise.race([\n        w8,\n        new Promise((_, rej) => setTimeout(() => rej(new Error(\"meta timeout\")), 8000)),\n      ]);\n      const w = img.naturalWidth || 1;\n      const h = img.naturalHeight || 1;\n      return { w, h, ratio: w / h };\n    } catch {\n      return { w: 1, h: 1, ratio: 1 };\n    } finally {\n      URL.revokeObjectURL(url);\n    }\n  }\n\n  // \u2500\u2500 Memory-safe aspect-ratio read (phone-crash fix) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // The OLD path decoded EVERY photo at full resolution via <img>.decode() just\n  // to read its width/height, and both openEditor and refreshBuilderPhotos did it\n  // with Promise.all \u2014 so picking 300 photos fired 300 simultaneous full-res\n  // bitmap decodes and iOS Safari ran out of memory and killed the tab.\n  //\n  // This reads the ratio from a TINY resized decode (createImageBitmap with a\n  // small resizeWidth \u2192 a few hundred KB, closed immediately) and gates ALL\n  // decodes through a global semaphore so at most _DMB_META_MAX run at once,\n  // no matter how many callers fan out. Only the aspect RATIO is used downstream\n  // (portrait/landscape choice + display box), never the absolute pixels, so the\n  // resized read is exact enough and full-res quality is never involved here.\n  /* Settle a promise no matter what: resolve with `fallback` if it rejects OR if\n   * it has not settled within `ms`. createImageBitmap is a NATIVE promise that can\n   * simply never settle on iOS Safari for some HEIC / very large files - the same\n   * hazard already documented for img.decode(). Unguarded, the await below never\n   * returns, its `finally` never runs, and the meta semaphore permanently loses a\n   * slot; three such files starve a max-3 gate, every later meta read blocks on\n   * acquire, and refreshBuilderPhotos' Promise.all never resolves. The visible\n   * symptom is newly added photos never appearing in the tray while the\n   * \"N photos selected\" counter - which reads bookFiles, updated earlier - has\n   * already moved. (2026-09-13) */\n  function _dmbSettleWithin(p, ms, fallback) {\n    let t = null;\n    return Promise.race([\n      Promise.resolve(p).then((v) => v, () => fallback),\n      new Promise((res) => { t = setTimeout(() => res(fallback), ms); }),\n    ]).then((v) => { try { clearTimeout(t); } catch (e) {} return v; });\n  }\n  const _DMB_META_TIMEOUT = 8000;\n\n  const _dmbMetaSem = { n: 0, max: 3, q: [] };\n  function _dmbMetaAcquire() {\n    return new Promise((res) => {\n      if (_dmbMetaSem.n < _dmbMetaSem.max) { _dmbMetaSem.n++; res(); }\n      else _dmbMetaSem.q.push(res);\n    });\n  }\n  function _dmbMetaRelease() {\n    _dmbMetaSem.n = Math.max(0, _dmbMetaSem.n - 1);\n    const next = _dmbMetaSem.q.shift();\n    if (next) { _dmbMetaSem.n++; next(); }\n  }\n  async function _dmbCheapMeta(file) {\n    if (!file) return { w: 1, h: 1, ratio: 1 };\n    await _dmbMetaAcquire();\n    try {\n      let bmp = null;\n      if (typeof createImageBitmap === \"function\") {\n        // imageOrientation:\"from-image\" so the ratio matches what the <img> actually\n        // displays (browsers auto-apply EXIF orientation to <img> but NOT to\n        // createImageBitmap by default) \u2014 otherwise EXIF-rotated shots get a swapped\n        // ratio and land in a wrong-aspect box, looking stretched/squished.\n        // Every decode is time-boxed so this function ALWAYS reaches its finally\n        // and releases the semaphore slot.\n        bmp = await _dmbSettleWithin(\n          createImageBitmap(file, { resizeWidth: 256, resizeQuality: \"low\", imageOrientation: \"from-image\" }),\n          _DMB_META_TIMEOUT, null);\n        if (!bmp) {\n          bmp = await _dmbSettleWithin(\n            createImageBitmap(file, { resizeWidth: 256, resizeQuality: \"low\" }), _DMB_META_TIMEOUT, null);\n        }\n        if (!bmp) {   // older Safari: no options\n          bmp = await _dmbSettleWithin(createImageBitmap(file), _DMB_META_TIMEOUT, null);\n        }\n      }\n      if (bmp) {\n        const w = bmp.width || 1, h = bmp.height || 1;\n        if (bmp.close) bmp.close();\n        return { w, h, ratio: w / h };\n      }\n      // Fallback for browsers without createImageBitmap: the original <img> decode.\n      // Heavier, but it still runs under the semaphore we hold (max _DMB_META_MAX\n      // at once), so it can't fan out into the old crash.\n      const meta = await _dmbSettleWithin(getImageMetaFromFile(file), _DMB_META_TIMEOUT, null);\n      return meta || { w: 1, h: 1, ratio: 1 };\n    } catch (e) {\n      return { w: 1, h: 1, ratio: 1 };\n    } finally {\n      _dmbMetaRelease();\n    }\n  }\n\n  /* \u2500\u2500 Photo-meta warm cache \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   * Decoding every uploaded photo to read its dimensions is the slow step that\n   * makes the cover lag when the builder first opens (openEditor awaits ALL\n   * metas before rendering). To hide that latency we start the decode the moment\n   * a file is added on the upload step, while the user is still picking photos /\n   * choosing pages. By the time they reach the builder the work is usually done,\n   * so openEditor resolves instantly from cache instead of decoding from cold.\n   * Keyed by the File object (WeakMap \u2192 no leaks); stores the in-flight Promise\n   * so a still-decoding file is awaited once, never decoded twice. The decode\n   * itself is the memory-safe, concurrency-capped _dmbCheapMeta above. */\n  const _dmbMetaCache = new WeakMap();\n  function _dmbWarmMeta(file) {\n    if (!file) return Promise.resolve({ w: 1, h: 1, ratio: 1 });\n    let p = _dmbMetaCache.get(file);\n    if (!p) { p = _dmbCheapMeta(file); _dmbMetaCache.set(file, p); }\n    return p;\n  }\n  function _dmbWarmMetaFor(files) {\n    try { (files || []).forEach((f) => { if (f) _dmbWarmMeta(f); }); } catch (e) {}\n  }\n\n  // Cap the photo scroll box so its BOTTOM lines up with the bottom of the\n  // builder/cover box \u2014 the box \"comes up\" to the builder and photos scroll inside.\n  let _syncTrayQueued = false;\n  function syncPhotoTrayHeight() {\n    const panel = document.querySelector(\".dmb-builder-leftpanel\");\n    const stage = document.querySelector(\".dmb-canvas-stage\");\n    if (!panel || !stage) return;\n    // Phones use the flex mobile shell \u2014 the desktop \"match the panel height to the\n    // canvas column\" hack sets an INLINE height that pins the docked panel short and\n    // clips the photo tray. Disable it (and clear any stale inline height) on mobile.\n    if (window.matchMedia && window.matchMedia(\"(max-width: 749px), (orientation: portrait) and (pointer: coarse) and (max-width: 1024px)\").matches) {\n      if (panel.style.height) panel.style.height = \"\";\n      return;\n    }\n    if (builderStep && getComputedStyle(builderStep).display === \"none\") return;\n    if (_syncTrayQueued) return;\n    _syncTrayQueued = true;\n    requestAnimationFrame(() => {\n      _syncTrayQueued = false;\n      // Match the photo-tray BOX height to the builder/cover box, so their bottoms\n      // line up; the photo grid scrolls inside.\n      const h = Math.round(stage.getBoundingClientRect().height);\n      if (h > 0) panel.style.height = h + \"px\";\n    });\n  }\n  window.addEventListener(\"resize\", () => { try { syncPhotoTrayHeight(); } catch (e) {} });\n\n  /* ===================================================================\n     Tray photo menu (Remove / Duplicate / Add to photobook)\n     Desktop: opens from the \u22EF button. Touch (phone/iPad): opens on a\n     quick tap of the photo (a scroll or hold-drag is NOT a tap).\n     =================================================================== */\n\n  function dmbIsTouchLike() {\n    // Phones AND tablets get the \"hold then drag\" hint. Touch capability covers\n    // real tablets/phones; the width fallback (<=1024px) guarantees any tablet-\n    // sized screen is included even if its touch flags are reported oddly.\n    return !!(\n      (\"ontouchstart\" in window) ||\n      (navigator.maxTouchPoints > 0) ||\n      (window.matchMedia && window.matchMedia(\"(pointer: coarse)\").matches) ||\n      (window.innerWidth <= 1024)\n    );\n  }\n\n  // The pages currently visible in the open spread (left first, then right).\n  function dmbCurrentSpreadPageNums() {\n    if (editor.currentSpread === -1) return [0];\n    const lp = editor.currentSpread * 2 + 1;\n    const rp = lp + 1;\n    const arr = [];\n    if (lp <= editor.totalPages && !isLockedPage(lp)) arr.push(lp);\n    if (rp <= editor.totalPages && !isLockedPage(rp)) arr.push(rp);\n    return arr;\n  }\n\n  // First empty slot/frame on a page, or null if the page is full.\n  function dmbFirstEmptySlotOnPage(pn) {\n    if (isLockedPage(pn)) return null;\n    if (isTemplatePage(pn)) {\n      const def = TEMPLATE_DEFS[getPageTemplateId(pn)] || [];\n      for (const f of def) { if (!getFramePhotoId(pn, f.key)) return f.key; }\n      return null;\n    }\n    if (!editor.pageSlots[pn]) editor.pageSlots[pn] = { A: null, B: null };\n    if (!editor.pageSlots[pn].A) return \"A\";\n    if (!editor.pageSlots[pn].B) return \"B\";\n    return null;\n  }\n\n  // Scan the whole book for the first page with a free slot.\n  function dmbFindFirstFreePageSlot() {\n    for (let p = 1; p <= editor.totalPages; p++) {\n      if (isLockedPage(p)) continue;\n      const slot = dmbFirstEmptySlotOnPage(p);\n      if (slot) return { page: p, slot: slot };\n    }\n    return null;\n  }\n\n  function dmbPlacePhotoInSlot(pn, slot, photoId) {\n    if (!isTemplatePage(pn)) {\n      if (!editor.pageSlots[pn]) editor.pageSlots[pn] = { A: null, B: null };\n      if (!editor.pageRot[pn]) editor.pageRot[pn] = { A: 0, B: 0 };\n    }\n    setFramePhotoId(pn, slot, photoId);\n    setFrameRotation(pn, slot, 0);\n  }\n\n  // When viewing the cover, \"Add to photobook\" drops onto front, then back.\n  function dmbPlacePhotoOnCover(photoId) {\n    const photo = getPhotoById(photoId);\n    if (!photo) return false;\n    let zone = null;\n    if (!coverState.frontImageUrl) zone = \"front\";\n    else if (!coverState.backImageUrl) zone = \"back\";\n    else return false;\n    pushUndoState(\"Add To Cover\");\n    if (zone === \"front\") {\n      coverState.frontImageUrl = photo.url; coverState.frontImageId = photoId; coverState.frontImageRot = 0;\n    } else {\n      coverState.backImageUrl = photo.url; coverState.backImageId = photoId; coverState.backImageRot = 0;\n    }\n    saveCoverDesignToStorage();\n    renderCoverCanvas(); renderThumbsRail(); renderPhotoTray();\n    return zone;\n  }\n\n  function addPhotoToBook(photoId) {\n    const used = getUsedPhotoIds();\n    const isReused = used.has(photoId);\n\n    // Cover mode \u2192 place on the cover (front, then back).\n    if (builderMode === \"cover\") {\n      const zone = dmbPlacePhotoOnCover(photoId);\n      if (zone) dmbAddedToast(zone === \"front\" ? \"Added to cover (front)\" : \"Added to cover (back)\");\n      else dmbActionToast(\"Your cover already has front and back photos.\", null, null);\n      return;\n    }\n\n    // Pages \u2192 fill the first empty slot on the open spread (left page, then right).\n    for (const pn of dmbCurrentSpreadPageNums()) {\n      const slot = dmbFirstEmptySlotOnPage(pn);\n      if (slot) {\n        pushUndoState(\"Add To Photobook\");\n        dmbPlacePhotoInSlot(pn, slot, photoId);\n        if (isReused) showReuseNotice();\n        renderSpread(); renderThumbsRail(); renderPhotoTray();\n        dmbAddedToast(\"Added to page \" + pn);\n        return;\n      }\n    }\n\n    // Open spread is full \u2192 offer the next free page (give the customer the choice).\n    const next = dmbFindFirstFreePageSlot();\n    if (next) {\n      dmbActionToast(\"This spread is full.\", \"Add to page \" + next.page + \" \u2192\", () => {\n        pushUndoState(\"Add To Photobook\");\n        dmbPlacePhotoInSlot(next.page, next.slot, photoId);\n        if (isReused) showReuseNotice();\n        builderMode = \"pages\";\n        editor.currentSpread = Math.floor((next.page - 1) / 2);\n        renderBuilderCanvas();\n        dmbAddedToast(\"Added to page \" + next.page);\n      });\n    } else {\n      dmbActionToast(\"Your book is full \u2014 add more pages to place this photo.\", null, null);\n    }\n  }\n\n  async function dmbRemovePhoto(ph) {\n    // Was window.confirm(), which renders the browser's own grey box with the\n    // domain name in it. Shows the actual photo so there is no doubt which one\n    // is about to go.\n    const ok = await showChoiceDialog(\n      \"Remove this photo?\",\n      \"It will be taken out of your library and off any page it sits on.\",\n      [{ id: \"cancel\", label: \"Keep it\" }, { id: \"remove\", label: \"Remove\", danger: true }],\n      { imageUrl: (ph && (ph.thumbUrl || ph.url)) || \"\" }\n    );\n    if (ok !== \"remove\") return;\n    const idx = editor.photos.findIndex((x) => x.id === ph.id);\n    if (idx >= 0) {\n      bookFiles.splice(idx, 1);\n      editor.photos.splice(idx, 1);\n      renderPhotoTray();\n      renderThumbsRail();\n      renderBuilderCanvas();\n    }\n  }\n\n  function dmbDuplicatePhoto(ph) {\n    if (ph.file) {\n      const clone = new File([ph.file], ph.file.name, { type: ph.file.type });\n      bookFiles.push(clone);\n      try { _dmbWarmMeta(clone); } catch (e) {}\n      refreshBuilderPhotos();\n    }\n  }\n\n  // --- the popup menu itself ---\n  let _dmbOpenPhotoMenu = null;\n  function _dmbPhotoMenuOutside(e) {\n    if (_dmbOpenPhotoMenu && !_dmbOpenPhotoMenu.contains(e.target)) closeDmbPhotoMenu();\n  }\n  function closeDmbPhotoMenu() {\n    if (_dmbOpenPhotoMenu) { _dmbOpenPhotoMenu.remove(); _dmbOpenPhotoMenu = null; }\n    document.removeEventListener(\"pointerdown\", _dmbPhotoMenuOutside, true);\n    window.removeEventListener(\"scroll\", closeDmbPhotoMenu, true);\n  }\n  function openDmbPhotoMenu(anchorEl, ph) {\n    closeDmbPhotoMenu();\n    const menu = document.createElement(\"div\");\n    menu.className = \"dmb-photo-menu\";\n\n    const mk = (label, cls, fn) => {\n      const b = document.createElement(\"button\");\n      b.type = \"button\";\n      b.className = \"dmb-photo-menu-btn\" + (cls ? \" \" + cls : \"\");\n      b.textContent = label;\n      b.addEventListener(\"click\", (e) => { e.preventDefault(); e.stopPropagation(); closeDmbPhotoMenu(); fn(); });\n      return b;\n    };\n\n    menu.appendChild(mk(\"Add to photobook\", \"dmb-photo-menu-add\", () => addPhotoToBook(ph.id)));\n    menu.appendChild(mk(\"Duplicate\", \"\", () => dmbDuplicatePhoto(ph)));\n    menu.appendChild(mk(\"Remove photo\", \"dmb-photo-menu-remove\", () => dmbRemovePhoto(ph)));\n\n    if (dmbIsTouchLike()) {\n      const hint = document.createElement(\"div\");\n      hint.className = \"dmb-photo-menu-hint\";\n      hint.innerHTML = '<span class=\"dmb-photo-menu-hint-or\">or</span>Hold the photo for a moment, then drag to the editor';\n      menu.appendChild(hint);\n    }\n\n    document.body.appendChild(menu);\n    _dmbOpenPhotoMenu = menu;\n\n    // Position under the photo, clamped to the viewport.\n    const r = anchorEl.getBoundingClientRect();\n    const mw = menu.offsetWidth, mh = menu.offsetHeight;\n    let left = r.left;\n    let top = r.bottom + 6;\n    if (left + mw > window.innerWidth - 8) left = window.innerWidth - 8 - mw;\n    if (left < 8) left = 8;\n    if (top + mh > window.innerHeight - 8) top = r.top - mh - 6;\n    if (top < 8) top = 8;\n    menu.style.left = left + \"px\";\n    menu.style.top = top + \"px\";\n\n    setTimeout(() => {\n      document.addEventListener(\"pointerdown\", _dmbPhotoMenuOutside, true);\n      window.addEventListener(\"scroll\", closeDmbPhotoMenu, true);\n    }, 0);\n  }\n\n  function dmbAddedToast(text) {\n    let n = document.getElementById(\"dmbAddedToast\");\n    if (!n) { n = document.createElement(\"div\"); n.id = \"dmbAddedToast\"; n.className = \"dmb-mini-toast\"; document.body.appendChild(n); }\n    n.textContent = text;\n    n.style.display = \"block\"; n.style.opacity = \"1\";\n    clearTimeout(dmbAddedToast._t);\n    dmbAddedToast._t = setTimeout(() => { n.style.opacity = \"0\"; setTimeout(() => { n.style.display = \"none\"; }, 200); }, 1400);\n  }\n\n  function dmbActionToast(message, actionLabel, onAction) {\n    let n = document.getElementById(\"dmbActionToast\");\n    if (n) n.remove();\n    n = document.createElement(\"div\");\n    n.id = \"dmbActionToast\";\n    n.className = \"dmb-action-toast\";\n    const msg = document.createElement(\"span\");\n    msg.textContent = message;\n    n.appendChild(msg);\n    if (actionLabel && onAction) {\n      const b = document.createElement(\"button\");\n      b.type = \"button\";\n      b.className = \"dmb-action-toast-btn\";\n      b.textContent = actionLabel;\n      b.addEventListener(\"click\", () => { n.remove(); onAction(); });\n      n.appendChild(b);\n    }\n    const x = document.createElement(\"button\");\n    x.type = \"button\";\n    x.className = \"dmb-action-toast-x\";\n    x.setAttribute(\"aria-label\", \"Dismiss\");\n    x.textContent = \"\u2715\";\n    x.addEventListener(\"click\", () => n.remove());\n    n.appendChild(x);\n    document.body.appendChild(n);\n    clearTimeout(dmbActionToast._t);\n    dmbActionToast._t = setTimeout(() => { if (n && n.isConnected) n.remove(); }, 6000);\n  }\n\n  // \u2500\u2500 Photo-tray thumbnails (Option A: stop the upload crash) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // The tray must NEVER decode full-size photos \u2014 that's what blows past iOS's\n  // memory ceiling when many photos are added. Instead each tray cell shows a tiny\n  // (~320px) thumbnail. Thumbnails are generated ONE AT A TIME (a serial queue) so\n  // only one photo is ever decoded at a moment, and cached by photo id.\n  const _DMB_THUMB_PLACEHOLDER =\n    \"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='40'%3E%3Crect width='40' height='40' fill='%23eceff4'/%3E%3C/svg%3E\";\n  const _dmbThumbCache = {};        // photoId -> small objectURL\n  const _dmbThumbQueued = {};       // photoId -> true (queued/in-flight)\n  let _dmbThumbQ = [];\n  let _dmbThumbBusy = false;\n\n  async function _dmbMakeThumbBlob(file, maxDim) {\n    maxDim = maxDim || 320;\n    try {\n      let bmp;\n      try { bmp = await createImageBitmap(file, { resizeWidth: maxDim, resizeQuality: \"medium\" }); }\n      catch (e) { bmp = await createImageBitmap(file); }     // older Safari: no resize option\n      const s = Math.min(1, maxDim / Math.max(bmp.width, bmp.height));\n      const w = Math.max(1, Math.round(bmp.width * s));\n      const h = Math.max(1, Math.round(bmp.height * s));\n      const c = document.createElement(\"canvas\"); c.width = w; c.height = h;\n      c.getContext(\"2d\").drawImage(bmp, 0, 0, w, h);\n      if (bmp.close) bmp.close();\n      const blob = await new Promise((res) => c.toBlob(res, \"image/jpeg\", 0.7));\n      c.width = 0; c.height = 0;                              // free canvas memory now\n      return blob;\n    } catch (e) { return null; }\n  }\n\n  async function _dmbThumbDrain() {\n    if (_dmbThumbBusy) return;\n    _dmbThumbBusy = true;\n    while (_dmbThumbQ.length) {\n      const job = _dmbThumbQ.shift();\n      const ph = job.ph;\n      if (!ph || _dmbThumbCache[ph.id]) continue;\n      let src = ph.file;\n      if ((!src || !src.size) && ph.url) { try { src = await (await fetch(ph.url)).blob(); } catch (e) {} }\n      // Print-master path: a lazily-released photo (no file, no url \u2014 bytes live on\n      // R2) must NEVER hit the fail/purge path just because its thumb came late.\n      // Pull it back from R2 once; recovery also restores ph.url/ph.file for later.\n      if ((!src || !src.size) && _dmbPrintMasterOn() && ph._r2 && ph.id) {\n        try { const u = await _dmbRecoverPhotoUrl(ph); if (u) src = await (await fetch(u)).blob(); } catch (e) {}\n      }\n      if (!src || !src.size) { _dmbHandleThumbFail(ph); continue; }\n      const blob = await _dmbMakeThumbBlob(src, 320);\n      if (blob && blob.size) {\n        const u = URL.createObjectURL(blob);\n        _dmbThumbCache[ph.id] = u;\n        ph.thumbUrl = u;\n        // Update any live tray <img> for this photo without a full re-render.\n        document.querySelectorAll('img[data-dmb-thumb=\"' + ph.id + '\"]').forEach((im) => { im.src = u; });\n      } else {\n        _dmbHandleThumbFail(ph);\n      }\n      // Yield so the browser can reclaim memory between photos (prevents pile-up).\n      await new Promise((r) => setTimeout(r, 0));\n    }\n    _dmbThumbBusy = false;\n  }\n\n  // A photo's thumbnail could not be generated. Decide whether its image is\n  // permanently unrecoverable and, if so, auto-remove it everywhere (per the user's\n  // choice) so no blank box shows and no page frame prints blank.\n  // SAFETY: a photo that still has its uploaded file blob is NEVER deleted \u2014 that\n  // guards against transient decode/memory failures on a good photo; it's just\n  // retried. Only a photo whose in-memory data is GONE (evicted/lost) is removed \u2014\n  // immediately if it has no URL to recover from, or after a failed URL retry.\n  function _dmbHandleThumbFail(ph) {\n    if (!ph || _dmbThumbCache[ph.id] || ph.thumbUrl) return;      // recovered \u2014 fine now\n    if (ph.file && ph.file.size) { _dmbThumbQueued[ph.id] = false; return; }   // still have the file \u2014 retry, never delete\n    if (!ph.url) { _dmbPurgeBrokenPhoto(ph.id); return; }         // no in-memory data AND no URL \u2192 unrecoverable\n    // Has a URL: only delete after two failures AND a definitive image-load error,\n    // so a flaky-but-valid URL is never removed.\n    ph._thumbFails = (ph._thumbFails || 0) + 1;\n    if (ph._thumbFails < 2) { _dmbThumbQueued[ph.id] = false; return; }\n    if (ph._verifying) return;\n    ph._verifying = true;\n    const test = new Image();\n    test.onload = () => { ph._verifying = false; ph._thumbFails = 0; _dmbThumbQueued[ph.id] = false; _dmbQueueThumb(ph); };\n    test.onerror = () => { ph._verifying = false; _dmbPurgeBrokenPhoto(ph.id); };\n    test.src = ph.url;\n  }\n\n  // Remove a broken photo from the album AND clear it from every page/cover\n  // placement (mirrors getUsedPhotoIds' traversal) so nothing is left printing blank.\n  function _dmbPurgeBrokenPhoto(phId) {\n    if (!phId) return;\n    const idx = editor.photos ? editor.photos.findIndex((x) => x.id === phId) : -1;\n    if (idx < 0) return;                                          // already gone\n    try {\n      for (let p = 0; p <= (editor.totalPages || 0); p++) {\n        const fm = editor.pageFrames && editor.pageFrames[p];\n        if (fm) Object.keys(fm).forEach((k) => { if (fm[k] === phId) fm[k] = null; });\n        const slots = editor.pageSlots && editor.pageSlots[p];\n        if (slots) { if (slots.A === phId) slots.A = null; if (slots.B === phId) slots.B = null; }\n      }\n      if (editor.spreadPhoto) {\n        Object.keys(editor.spreadPhoto).forEach((k) => {\n          const sp = editor.spreadPhoto[k];\n          if (sp && sp.photoId === phId) editor.spreadPhoto[k] = null;\n        });\n      }\n      if (editor.pageObjects) {\n        Object.values(editor.pageObjects).forEach((objs) => {\n          if (Array.isArray(objs)) objs.forEach((o) => { if (o && o.type === \"photo-frame\" && o.photoId === phId) o.photoId = null; });\n        });\n      }\n      if (Array.isArray(editor.coverObjects)) {\n        editor.coverObjects.forEach((o) => { if (o && o.type === \"photo-frame\" && o.photoId === phId) o.photoId = null; });\n      }\n    } catch (e) {}\n    // Remove the photo itself (bookFiles is parallel-indexed to editor.photos).\n    if (typeof bookFiles !== \"undefined\" && Array.isArray(bookFiles)) bookFiles.splice(idx, 1);\n    editor.photos.splice(idx, 1);\n    console.warn(\"[DMB] Auto-removed a photo whose image was missing:\", phId);\n    try { renderPhotoTray(); } catch (e) {}\n    try { renderThumbsRail(); } catch (e) {}\n    try { if (typeof builderMode !== \"undefined\" && builderMode === \"cover\") renderCoverCanvas(); else renderSpread(); } catch (e) {}\n  }\n\n  function _dmbQueueThumb(ph) {\n    if (!ph || _dmbThumbCache[ph.id] || _dmbThumbQueued[ph.id]) return;\n    _dmbThumbQueued[ph.id] = true;\n    _dmbThumbQ.push({ ph });\n    _dmbThumbDrain();\n  }\n\n  // Display src for the page-rail mini previews. NEVER returns the full-res photo\n  // URL \u2014 a large book places a photo on every page, and one full-res <img> per\n  // page decoded at once is the other phone-crash source. Returns the cached\n  // 320px thumb when ready, else a tiny placeholder and queues generation; the\n  // thumb drain hot-swaps any <img data-dmb-thumb=\"pid\"> the moment it's ready.\n  function _dmbMiniThumbSrc(ph) {\n    if (!ph) return _DMB_THUMB_PLACEHOLDER;\n    const t = ph.thumbUrl || _dmbThumbCache[ph.id];\n    if (!t) { try { _dmbQueueThumb(ph); } catch (e) {} }\n    return t || _DMB_THUMB_PLACEHOLDER;\n  }\n\n  function renderPhotoTray() {\n    if (_dmbRenderMode()) return;   // the PDF never reads the tray (skips 4 persistence calls too)\n    if (!editorPhotoTray) return;\n    syncPhotoTrayHeight();\n\n    // Keep saved progress current so a refresh / app-switch can resume (issue 1).\n    if (typeof _dmbPersistPhotos === \"function\") _dmbPersistPhotos();\n    if (typeof _dmbAutoPersist === \"function\") _dmbAutoPersist();\n    if (typeof _dmbCloudPersist === \"function\") _dmbCloudPersist();\n    if (typeof _dmbBackupPhotos === \"function\") _dmbBackupPhotos();   // guest photo backup \u2192 R2\n\n    const used = getUsedPhotoIds();\n    const photos = applyPhotoFilters(editor.photos);\n\n    editorPhotoTray.innerHTML = \"\";\n\n    photos.forEach((ph) => {\n      const isUsed = used.has(ph.id);\n\n      const t = document.createElement(\"div\");\n      t.className = \"dmb-photo-thumb\";\n      t.draggable = true;\n\n      t.addEventListener(\"dragstart\", (e) => {\n        setDragPayload(e, { type: \"photo\", photoId: ph.id });\n      });\n\n      // Touch (phone/iPad): a quick tap opens the menu. A scroll or a\n      // hold-and-drag involves finger movement, so it is NOT treated as a tap.\n      let _tapX = 0, _tapY = 0, _tapT = 0, _tapTouch = false;\n      t.addEventListener(\"pointerdown\", (e) => {\n        _tapTouch = (e.pointerType !== \"mouse\");\n        _tapX = e.clientX; _tapY = e.clientY; _tapT = Date.now();\n      });\n      t.addEventListener(\"pointerup\", (e) => {\n        if (!_tapTouch) return;                                   // desktop uses the \u22EF button\n        if (e.target.closest && e.target.closest(\".dmb-photo-hover-menu\")) return; // \u22EF handles itself\n        const moved = Math.hypot(e.clientX - _tapX, e.clientY - _tapY);\n        const elapsed = Date.now() - _tapT;\n        if (moved < 10 && elapsed < 600) {\n          e.preventDefault();\n          openDmbPhotoMenu(t, ph);\n        }\n      });\n\n      // Touch drag-and-drop (phones/iPad): press-and-drag the thumb onto a page/cover\n      // frame to place the photo. Desktop keeps the native HTML5 draggable above.\n      _dmbWireThumbTouchDrag(t, ph);\n\n      const img = document.createElement(\"img\");\n      // Use the small thumbnail \u2014 NEVER the full-size photo (that's the crash).\n      const _tu = ph.thumbUrl || _dmbThumbCache[ph.id];\n      img.src = _tu || _DMB_THUMB_PLACEHOLDER;\n      img.dataset.dmbThumb = ph.id;\n      img.alt = \"photo\";\n      img.draggable = false;\n      img.loading = \"lazy\";\n      img.decoding = \"async\";\n      t.appendChild(img);\n      if (!_tu) _dmbQueueThumb(ph);   // generate it (serially) and swap in when ready\n\n      if (isUsed) {\n        const badge = document.createElement(\"div\");\n        badge.className = \"dmb-photo-used\";\n        badge.textContent = \"USED\";\n        t.appendChild(badge);\n      }\n\n      const menuWrap = document.createElement(\"div\");\n      menuWrap.className = \"dmb-photo-hover-menu\";\n\n      const menuBtn = document.createElement(\"button\");\n      menuBtn.type = \"button\";\n      menuBtn.className = \"dmb-photo-hover-menu-btn\";\n      menuBtn.textContent = \"\u22EF\";\n      menuWrap.appendChild(menuBtn);\n\n      menuBtn.addEventListener(\"click\", (e) => {\n        e.preventDefault();\n        e.stopPropagation();\n        openDmbPhotoMenu(t, ph);\n      });\n\n      t.appendChild(menuWrap);\n      editorPhotoTray.appendChild(t);\n    });\n    // Phones: give every tile its natural-aspect width (= tray height \u00D7 photo\n    // aspect) so photos fill their frames edge-to-edge with no white bars.\n    try { requestAnimationFrame(_dmbSizePhotoTiles); } catch (e) {}\n    // Desktop: justified gallery \u2014 set each tile's --asp so the flexbox packs rows.\n    try { requestAnimationFrame(_dmbJustifyPhotoTray); } catch (e) {}\n  }\n\n  // Desktop photo tray = justified gallery. For each tile, set the CSS var --asp to\n  // the photo's real aspect ratio (read from the loaded thumbnail); the CSS then uses\n  // it for aspect-ratio + flex so each row fills the width with photos at their true\n  // proportions (no cropping). Recomputes on image load (async thumbnail swap).\n  function _dmbJustifyPhotoTray() {\n    if (window.matchMedia && window.matchMedia(\"(max-width: 749px), (orientation: portrait) and (pointer: coarse) and (max-width: 1024px)\").matches) return; // mobile uses the strip\n    const tray = document.getElementById(\"editorPhotoTray\");\n    if (!tray) return;\n    Array.from(tray.children).forEach((tile) => {\n      const img = tile.querySelector(\"img\");\n      if (!img) return;\n      const setAsp = () => {\n        const nw = img.naturalWidth, nh = img.naturalHeight;\n        if (nw && nh) tile.style.setProperty(\"--asp\", (nw / nh).toFixed(4));\n      };\n      setAsp();\n      if (!img._dmbAspHook) { img._dmbAspHook = true; img.addEventListener(\"load\", setAsp); }\n    });\n  }\n\n  // Size each photo tile on phones DETERMINISTICALLY: explicit height = the strip\n  // height, and explicit width = height \u00D7 the image's real aspect ratio (read from\n  // the loaded thumbnail). The image then fills that exact frame (object-fit:contain,\n  // never cropped). This does NOT rely on flex width:auto re-shrinking when the\n  // thumbnail swaps in \u2014 which was leaving a grey gap beside portrait photos \u2014 so\n  // there is never a gap. Recomputes on every image load (covers the async\n  // placeholder \u2192 real-thumbnail swap).\n  function _dmbSizePhotoTiles() {\n    if (!(window.matchMedia && window.matchMedia(\"(max-width: 749px), (orientation: portrait) and (pointer: coarse) and (max-width: 1024px)\").matches)) return;\n    const tray = document.getElementById(\"editorPhotoTray\");\n    const scroll = document.getElementById(\"dmbPicturesScrollArea\");\n    if (!tray || !scroll) return;\n    const H = scroll.clientHeight || tray.clientHeight || 0;\n    if (!H) { requestAnimationFrame(_dmbSizePhotoTiles); return; }\n    Array.from(tray.children).forEach((tile) => {\n      const img = tile.querySelector(\"img\");\n      const fit = () => {\n        // Measure height once and use the SAME value for height AND width, so the\n        // tile's aspect exactly matches the photo's \u2014 no letterbox, no gap. (Using\n        // two different measurements let a scrollbar-induced height change skew it.)\n        const HH = scroll.clientHeight || tray.clientHeight || H;\n        if (!HH) return;\n        const nw = img && img.naturalWidth, nh = img && img.naturalHeight;\n        const W = Math.max(24, Math.round(HH * ((nw && nh) ? nw / nh : 1)));\n        // setProperty(..., \"important\") \u2014 the tile's CSS width/height are `!important`,\n        // and a stylesheet !important beats a plain inline style, so we MUST mark these\n        // inline values important too or the sizing is silently ignored.\n        tile.style.setProperty(\"height\", HH + \"px\", \"important\");\n        tile.style.setProperty(\"width\", W + \"px\", \"important\");\n      };\n      fit();\n      if (img && !img._dmbFitHook) { img._dmbFitHook = true; img.addEventListener(\"load\", fit); }\n    });\n  }\n\n\n  // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n  // PAGE REORDER / INSERT ENGINE (desktop drag-to-reorder + insert-a-spread).\n  // Pages 0 (Special) and 1 (locked SPECIAL) are fixed. Movable pages are 2..N.\n  // Each page carries 8 structures keyed by page number; a spread photo is keyed\n  // by spread index (a page pair). This engine rebuilds the movable region from a\n  // `sources` list (each entry = an old page number to move, or null for a new\n  // blank page), remapping all 8 structures and re-pairing spread photos (dropping\n  // any that a single-page move splits apart). Validated by a standalone unit test.\n  // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n  const _DMB_PAGE_STRUCTS = [\"pageSlots\",\"pageRot\",\"pageTemplate\",\"pageFrames\",\"pageFrameRot\",\"pageFrameShapes\",\"pageObjects\",\"pageBackgrounds\"];\n\n  function _dmbApplyPageSources(sources) {\n    const ed = editor;\n    const oldTotal = ed.totalPages;\n    // 1) capture old bundles for movable pages (2..N)\n    const oldBundles = {};\n    for (let p = 2; p <= oldTotal; p++) {\n      oldBundles[p] = {};\n      _DMB_PAGE_STRUCTS.forEach((k) => { oldBundles[p][k] = ed[k] ? ed[k][p] : undefined; });\n    }\n    // 2) capture old spread photos as pairs of old page numbers\n    const oldSpreadPhotos = [];\n    const oldSpreads = Math.ceil(oldTotal / 2);\n    for (let si = 0; si < oldSpreads; si++) {\n      const sp = ed.spreadPhoto && ed.spreadPhoto[si];\n      if (sp && sp.photoId) oldSpreadPhotos.push({ left: si * 2 + 1, right: si * 2 + 2, photo: sp });\n    }\n    // 3) clear movable region (page >= 2) in all 8 structures\n    _DMB_PAGE_STRUCTS.forEach((k) => {\n      if (!ed[k]) { ed[k] = {}; return; }\n      Object.keys(ed[k]).forEach((key) => { if (+key >= 2) delete ed[k][key]; });\n    });\n    // 4) write the new positions (2..)\n    const newPosOfOld = {};\n    for (let i = 0; i < sources.length; i++) {\n      const newP = i + 2, src = sources[i];\n      if (src == null) {\n        ed.pageSlots[newP] = { A: null, B: null }; ed.pageRot[newP] = { A: 0, B: 0 };\n        ed.pageTemplate[newP] = null; ed.pageFrames[newP] = {}; ed.pageFrameRot[newP] = {};\n        ed.pageFrameShapes[newP] = {}; ed.pageObjects[newP] = [];\n      } else {\n        const b = oldBundles[src] || {};\n        _DMB_PAGE_STRUCTS.forEach((k) => { if (b[k] !== undefined) ed[k][newP] = b[k]; });\n        newPosOfOld[src] = newP;\n      }\n    }\n    // 5) rebuild spreadPhoto \u2014 keep only pairs still adjacent on a valid (odd,even) boundary\n    const dropped = [];\n    const nsp = {};\n    oldSpreadPhotos.forEach(({ left, right, photo }) => {\n      const nl = newPosOfOld[left], nr = newPosOfOld[right];\n      if (nl && nr && nr === nl + 1 && nl % 2 === 1) nsp[(nl - 1) / 2] = photo;\n      else dropped.push(photo.photoId);\n    });\n    ed.spreadPhoto = nsp;\n    // 6) recount\n    ed.totalPages = sources.length + 1;\n    ed.editablePages = Math.max(0, ed.totalPages - 1);\n    ed.totalSpreads = Math.ceil(ed.totalPages / 2);\n    // 7) A spanning object is placed against a SPECIFIC pairing of two pages. Re-ordering\n    // re-pairs them, so that placement no longer describes anything the customer chose -\n    // and an object still hanging over the fold would now print onto a sheet it was never\n    // put near. Pull the fold-side overhang back in, the same judgement step 5 above makes\n    // when it drops a spread photo whose pair no longer sits together. Outer-edge bleed is\n    // untouched, because that never depended on the pairing. (2026-09-14)\n    try {\n      const _PWr = getPageDims().w;\n      Object.keys(ed.pageObjects || {}).forEach(function (k) {\n        const pn = Number(k);\n        const arr = ed.pageObjects[k];\n        if (!pn || pn < 1 || !Array.isArray(arr)) return;\n        const isLeftPg = (pn % 2 === 1);\n        arr.forEach(function (o) {\n          if (!o) return;\n          const ow = o.w || 0, x0 = o.x || 0;\n          const nx = isLeftPg ? Math.min(x0, _PWr - ow) : Math.max(x0, 0);\n          if (nx === x0) return;\n          const d = nx - x0;\n          o.x = nx;\n          if (o.type === \"shape\" && o.shapeType === \"line\") {\n            if (o.x1 != null) o.x1 += d;\n            if (o.x2 != null) o.x2 += d;\n            if (o.bx != null) o.bx += d;\n          }\n          if (o.straddle) delete o.straddle;\n        });\n      });\n    } catch (e) {}\n    return { dropped };\n  }\n\n  // List of currently movable page numbers (2..N).\n  function _dmbMovablePages() { const a = []; for (let p = 2; p <= editor.totalPages; p++) a.push(p); return a; }\n\n  // Would moving page `from` to movable-index `toIdx` split any full-spread photo?\n  function _dmbSingleMoveWouldSplit(from, toIdx) {\n    const seq = _dmbMovablePages(); const i = seq.indexOf(from); if (i < 0) return false;\n    seq.splice(i, 1); seq.splice(toIdx, 0, from);\n    const pos = {}; seq.forEach((p, idx) => { pos[p] = idx + 2; });\n    const spreads = Math.ceil(editor.totalPages / 2);\n    for (let si = 0; si < spreads; si++) {\n      const sp = editor.spreadPhoto && editor.spreadPhoto[si]; if (!sp || !sp.photoId) continue;\n      const nl = pos[si * 2 + 1], nr = pos[si * 2 + 2];\n      if (!(nl && nr && nr === nl + 1 && nl % 2 === 1)) return true;\n    }\n    return false;\n  }\n\n  // Commit after any structural change: navigate safely, re-price, re-render, persist.\n  function _dmbReorderCommit(label) {\n    editor.currentSpread = Math.min(Math.max(0, editor.currentSpread), Math.max(0, editor.totalSpreads - 1));\n    // NOTE: the undo snapshot is taken by each caller BEFORE it mutates (so undo restores\n    // the pre-reorder state); it must NOT be taken here (that would capture the post-edit state).\n    try { updatePrice(); } catch (e) {}\n    try { refreshPhotoRule(); } catch (e) {}\n    try { _syncSpreadAdderUI(); } catch (e) {}\n    try { renderThumbsRail(); } catch (e) {}\n    try { renderBuilderCanvas(); } catch (e) {}\n    try { _dmbPvReinitAll(); } catch (e) {}\n    // Persist (structural change \u2192 save + re-key everything for restore).\n    try { if (typeof _dmbPersistPhotos === \"function\") _dmbPersistPhotos(); } catch (e) {}\n    try { if (typeof _dmbAutoPersist === \"function\") _dmbAutoPersist(); } catch (e) {}\n    try { if (typeof _dmbCloudPersist === \"function\") _dmbCloudPersist(); } catch (e) {}\n  }\n\n  // Move a single page to a new movable index (0-based among 2..N). Caller has already\n  // resolved the split-warning + swap/insert choice.\n  function _dmbMoveSinglePage(from, toIdx) {\n    const seq = _dmbMovablePages(); const i = seq.indexOf(from); if (i < 0) return;\n    pushUndoState(\"Move page\");\n    seq.splice(i, 1); seq.splice(toIdx, 0, from);\n    _dmbApplyPageSources(seq);\n    _dmbReorderCommit(\"Move page\");\n  }\n\n  // Swap two single pages (used for the \"swap\" drop choice).\n  function _dmbSwapPages(a, b) {\n    const seq = _dmbMovablePages(); const ia = seq.indexOf(a), ib = seq.indexOf(b);\n    if (ia < 0 || ib < 0) return;\n    pushUndoState(\"Swap pages\");\n    const t = seq[ia]; seq[ia] = seq[ib]; seq[ib] = t;\n    _dmbApplyPageSources(seq);\n    _dmbReorderCommit(\"Swap pages\");\n  }\n\n  // Spread mode: page 2 is fixed; reorder the full pairs (3,4),(5,6)\u2026 to pair-slot `toPairIdx`.\n  function _dmbSpreadPairs() {\n    const pairs = []; let trailing = null;\n    for (let p = 3; p <= editor.totalPages; p += 2) { if (p + 1 <= editor.totalPages) pairs.push([p, p + 1]); else trailing = p; }\n    return { pairs, trailing };\n  }\n  function _dmbMoveSpread(l, r, toPairIdx) {\n    pushUndoState(\"Move spread\");\n    const { pairs, trailing } = _dmbSpreadPairs();\n    let ps = pairs.filter((pr) => !(pr[0] === l && pr[1] === r));\n    ps.splice(toPairIdx, 0, [l, r]);\n    const seq = editor.totalPages >= 2 ? [2] : [];\n    ps.forEach((pr) => seq.push(pr[0], pr[1]));\n    if (trailing != null) seq.push(trailing);\n    _dmbApplyPageSources(seq);\n    _dmbReorderCommit(\"Move spread\");\n  }\n\n  // Delete a single content page (2..N). Everything after it shifts up by one and the\n  // total drops by one (odd totals are fine \u2014 the last spread just becomes a single page,\n  // same as a trailing page). The locked Special pages (0 and 1) can't be deleted. Full-\n  // spread photos whose two halves are no longer adjacent get dropped by _dmbApplyPageSources.\n  function _dmbDeletePage(p) {\n    if (p < 2 || isLockedPage(p)) return;\n    if (editor.totalPages <= 1) return;\n    // Magazines cannot lose a single page: see _dmbMagRemoveSpreadPair.\n    if (window.DMB_MAGAZINE_MODE) { _dmbMagRemoveSpreadPair(p); return; }\n    pushUndoState(\"Delete page\");\n    // While the customer still has added extra spreads, each deletion first eats into those\n    // (drops one +$0.99 charge and keeps extraSpreads from outrunning the real page count).\n    // Once extras are exhausted, further deletions trim the paid base tier with no refund.\n    if ((editor.extraSpreads || 0) > 0) {\n      editor.extraSpreads = Math.max(0, editor.extraSpreads - 1);\n    }\n    const seq = _dmbMovablePages().filter((x) => x !== p);\n    _dmbApplyPageSources(seq);\n    _dmbReorderCommit(\"Delete page\");\n  }\n\n  // \u2500\u2500 A notice the customer has to read \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // Deliberately NOT showChoiceDialog: no buttons, no backdrop click, no Escape.\n  // The only way out is the X in the top right corner, so it cannot be dismissed\n  // by the stray tap that closes everything else on a phone. Used where something\n  // happened that the customer did not ask for and has to understand, which is why\n  // it explains rather than asks. (2026-10-12)\n  function _dmbNoticeDialog(title, message) {\n    try {\n      const SERIF = \"var(--font-heading--family,'Playfair Display'),Georgia,'Times New Roman',serif\";\n      const SANS  = \"var(--font-body--family,'Inter'),system-ui,-apple-system,sans-serif\";\n      const prev = document.getElementById(\"dmbNoticeDialog\");\n      if (prev) prev.remove();\n\n      const overlay = document.createElement(\"div\");\n      overlay.id = \"dmbNoticeDialog\";\n      overlay.style.cssText = \"position:fixed;inset:0;z-index:1000040;background:rgba(44,40,34,0.55);\" +\n        \"display:flex;align-items:center;justify-content:center;padding:20px;font-family:\" + SANS + \";\" +\n        \"opacity:0;transition:opacity .14s ease;\";\n\n      const modal = document.createElement(\"div\");\n      modal.style.cssText = \"position:relative;background:#F7F3EC;border:1px solid #E7E1D4;border-radius:16px;\" +\n        \"padding:30px 26px 24px;max-width:360px;width:100%;text-align:center;box-sizing:border-box;\" +\n        \"box-shadow:0 24px 60px rgba(44,40,34,0.32);transform:translateY(6px);transition:transform .14s ease;\";\n\n      const x = document.createElement(\"button\");\n      x.type = \"button\";\n      x.setAttribute(\"aria-label\", \"Close\");\n      x.style.cssText = \"position:absolute;top:8px;right:8px;width:34px;height:34px;border:0;background:transparent;\" +\n        \"color:#6B6258;cursor:pointer;border-radius:999px;display:flex;align-items:center;justify-content:center;\";\n      x.innerHTML = '<svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" ' +\n        'stroke-width=\"2.4\" stroke-linecap=\"round\" aria-hidden=\"true\"><path d=\"M6 6l12 12M18 6L6 18\"/></svg>';\n      modal.appendChild(x);\n\n      const ttl = document.createElement(\"div\");\n      ttl.style.cssText = \"font-family:\" + SERIF + \";font-weight:700;font-size:20px;line-height:1.2;color:#2C2822;\";\n      ttl.textContent = title;\n      modal.appendChild(ttl);\n\n      const msg = document.createElement(\"div\");\n      msg.style.cssText = \"font-size:13px;color:#6B6258;line-height:1.55;margin-top:10px;\";\n      msg.textContent = message;\n      modal.appendChild(msg);\n\n      overlay.appendChild(modal);\n      document.body.appendChild(overlay);\n      requestAnimationFrame(function () { overlay.style.opacity = \"1\"; modal.style.transform = \"translateY(0)\"; });\n\n      const close = function () {\n        overlay.style.opacity = \"0\";\n        setTimeout(function () { try { overlay.remove(); } catch (e) {} }, 150);\n      };\n      x.addEventListener(\"click\", function (e) { e.preventDefault(); e.stopPropagation(); close(); });\n      // Swallow every other way out, including a click on the backdrop.\n      overlay.addEventListener(\"click\", function (e) { e.stopPropagation(); });\n      return overlay;\n    } catch (e) { return null; }\n  }\n\n  // \u2500\u2500 Magazines change length two spreads at a time \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // A magazine is printed on folded sheets: one sheet carries FOUR pages, so the\n  // total can only move in fours. One spread is two pages, which is half a sheet\n  // and cannot be printed on its own. Books are bound differently and are\n  // untouched by any of this. (2026-10-12)\n  const _DMB_MAG_SPREAD_STEP = 2;          // spreads per add / remove, magazines only\n  function _dmbMagStep() { return window.DMB_MAGAZINE_MODE ? _DMB_MAG_SPREAD_STEP : 1; }\n\n  // Price of one magazine add (two spreads). Live from the \"spread\" product, so it\n  // is never out of step with what the cart charges.\n  function _dmbMagPairCost() {\n    try { return fmtMoney(EXTRA_SPREAD_PRICE * _DMB_MAG_SPREAD_STEP); } catch (e) { return \"\"; }\n  }\n\n  function _dmbMagPairNotice(added) {\n    const cost = _dmbMagPairCost();\n    const money = added\n      ? (cost ? (\" They are charged as two spreads, so \" + cost + \" has been added to your price.\") : \"\")\n      : (cost ? (\" The \" + cost + \" they cost has come off your price.\") : \"\");\n    _dmbNoticeDialog(\n      added ? \"Two spreads added\" : \"Two spreads removed\",\n      \"A magazine is printed on folded sheets, so its pages always come in fours. \" +\n      \"That is why two spreads (4 pages) were \" + (added ? \"added\" : \"removed\") +\n      \" together instead of one.\" + money +\n      \" If that is not what you wanted, press Undo.\"\n    );\n  }\n\n  function _dmbMagFixedLengthNotice() {\n    _dmbNoticeDialog(\n      \"Magazine pages come in fours\",\n      \"A magazine is printed on folded sheets, so pages cannot be taken away one at a \" +\n      \"time. This magazine is the length you chose at the start, and only spreads you \" +\n      \"added yourself can be removed, two at a time. To make it shorter, choose a \" +\n      \"smaller magazine from the page count, or clear this page and use it for \" +\n      \"something else.\"\n    );\n  }\n\n  // Insert a new blank spread (2 pages) at movable index `atIdx`; +1 extra spread (price).\n  function _dmbInsertSpreadAt(atIdx) {\n    const step = _dmbMagStep();            // magazines: 2 spreads = one folded sheet\n    pushUndoState(step > 1 ? \"Insert 2 spreads\" : \"Insert spread\");\n    const seq = _dmbMovablePages();\n    const blanks = [];\n    for (let i = 0; i < step * 2; i++) blanks.push(null);\n    seq.splice.apply(seq, [atIdx, 0].concat(blanks));\n    _dmbApplyPageSources(seq);\n    editor.extraSpreads = (editor.extraSpreads || 0) + step;\n    _dmbReorderCommit(\"Insert spread\");\n    if (step > 1) _dmbMagPairNotice(true);\n  }\n\n  // Magazine removal: take out the whole four page sheet the page sits on.\n  // Only spreads the customer ADDED can go: the rest is the length they chose and\n  // paid for, and the recompute in renderSpread would put them straight back.\n  function _dmbMagRemoveSpreadPair(p) {\n    const extra = editor.extraSpreads || 0;\n    if (extra < _DMB_MAG_SPREAD_STEP) { _dmbMagFixedLengthNotice(); return; }\n    if (editor.totalPages < 6) { _dmbMagFixedLengthNotice(); return; }\n    pushUndoState(\"Remove 2 spreads\");\n    // Start at the spread this page belongs to, then slide the four page window\n    // back inside the movable range if it would run off either end. Removing four\n    // CONSECUTIVE pages shifts everything after by an even number, so every\n    // remaining page keeps its side of the fold.\n    const si = Math.floor((p - 1) / 2);\n    let start = 2 * si + 1;\n    start = Math.max(2, Math.min(start, editor.totalPages - 3));\n    const kill = {};\n    for (let i = 0; i < 4; i++) kill[start + i] = true;\n    _dmbApplyPageSources(_dmbMovablePages().filter(function (x) { return !kill[x]; }));\n    editor.extraSpreads = Math.max(0, extra - _DMB_MAG_SPREAD_STEP);\n    _dmbReorderCommit(\"Remove 2 spreads\");\n    _dmbMagPairNotice(false);\n  }\n\n  // Overlay a page's \"free\" objects (stickers, text, shapes, magazine icons) into a mini\n  // thumbnail at scaled positions, so a pages-tray card mirrors the real page. Photo-frame\n  // photos and slot/template photos are drawn by the main thumbnail code; this adds the\n  // decorative layers on top. Returns how many overlay elements were added.\n  let _dmbMiniFilterSeq = 0;   // unique <filter> id per map thumbnail\n  function _dmbRenderPageOverlay(mini, objs, PW, PH) {\n    if (!mini || !Array.isArray(objs) || !objs.length || !PW || !PH) return 0;\n    let added = 0;\n    // container-type lets text size scale with the thumbnail via cqw units.\n    mini.style.containerType = \"inline-size\";\n    const pct = (v, d) => ((v || 0) / d * 100).toFixed(1);\n    const sorted = objs.slice().sort((a, b) => (a.z || 0) - (b.z || 0));\n    sorted.forEach((o) => {\n      if (!o || !o.type) return;\n      const rot = o.rotation || 0;\n      if (o.type === \"sticker\") {\n        const src = o.tightUrl || o.url; if (!src) return;\n        const img = document.createElement(\"img\");\n        img.src = src; img.draggable = false;\n        img.style.cssText = `position:absolute;left:${pct(o.x,PW)}%;top:${pct(o.y,PH)}%;width:${pct(o.w,PW)}%;height:${pct(o.h,PH)}%;object-fit:contain;transform:rotate(${rot}deg);transform-origin:center center;`;\n        mini.appendChild(img); added++;\n      } else if (o.type === \"mag-icon\" && o.iconId && typeof dmbMagIconUrl === \"function\") {\n        const img = document.createElement(\"img\");\n        img.src = dmbMagIconUrl(o.iconId); img.draggable = false;\n        img.style.cssText = `position:absolute;left:${pct(o.x,PW)}%;top:${pct(o.y,PH)}%;width:${pct(o.w,PW)}%;height:${pct(o.h,PH)}%;object-fit:contain;transform:rotate(${rot}deg);`;\n        mini.appendChild(img); added++;\n      } else if (o.type === \"shape\") {\n        // Render the ACTUAL shape/line SVG (same builders as the canvas) so the\n        // thumbnail matches \u2014 outline-only shapes, borders, and true line geometry\n        // all show, instead of a flat colored box.\n        const d = document.createElement(\"div\");\n        d.style.cssText = `position:absolute;left:${pct(o.x,PW)}%;top:${pct(o.y,PH)}%;width:${pct(o.w,PW)}%;height:${pct(o.h,PH)}%;transform:rotate(${rot}deg);transform-origin:center center;overflow:visible;`;\n        try {\n          d.innerHTML = (o.shapeType === \"line\")\n            ? buildLineSvg(o)\n            : buildShapeSvg(o.shapeType, o.fill, o.stroke, o.strokeWidth, (o.opacity != null ? o.opacity : 1), o.cornerRadius, o.w, o.h, o.strokeStyle);\n          // buildShapeSvg emits vector-effect:non-scaling-stroke, i.e. a stroke of\n          // exactly `strokeWidth` CSS PIXELS whatever size the shape is drawn at.\n          // That is right on the canvas (1 builder px = 1 CSS px) but wrong here: a\n          // thumbnail is a fraction of the page, so the border keeps its pixel width\n          // while the shape shrinks and reads several times too heavy - the same\n          // thing that made the tray's map look wrong.\n          // `mini` is a container-type:inline-size box, so expressing the width in\n          // cqw (% of the thumbnail's width) makes it track the thumbnail at any\n          // size, and stays a single isotropic length - which is why the stroke uses\n          // non-scaling-stroke in the first place (a 0..100 viewBox stretched to a\n          // non-square box would otherwise give a thicker pen on one axis).\n          // Lines are already correct: buildLineSvg uses a viewBox of the object's\n          // own size with NO vector-effect, so its stroke scales on its own.\n          if (o.shapeType !== \"line\" && PW > 0) {\n            const _cq = (v) => (v / PW * 100).toFixed(3) + \"cqw\";\n            d.querySelectorAll(\"[stroke-width]\").forEach((el) => {\n              const _sw = parseFloat(el.getAttribute(\"stroke-width\")) || 0;\n              if (_sw > 0) el.style.strokeWidth = _cq(_sw);\n              const _da = (el.getAttribute(\"stroke-dasharray\") || \"\").trim();\n              if (_da) {\n                el.style.strokeDasharray = _da.split(/[\\s,]+/)\n                  .map((v) => _cq(parseFloat(v) || 0)).join(\",\");\n              }\n            });\n          }\n        } catch (e) { return; }\n        mini.appendChild(d); added++;\n      } else if (o.type === \"map\") {\n        const d = document.createElement(\"div\");\n        d.style.cssText = `position:absolute;left:${pct(o.x,PW)}%;top:${pct(o.y,PH)}%;width:${pct(o.w,PW)}%;height:${pct(o.h,PH)}%;transform:rotate(${rot}deg);transform-origin:center center;`;\n        d.innerHTML = o.svg || \"\";\n        const _msvg = d.querySelector(\"svg\");\n        if (_msvg) {\n          _msvg.style.width = \"100%\"; _msvg.style.height = \"100%\"; _msvg.style.display = \"block\";\n          // Re-use the CANVAS styler, so the tray obeys the same rules the page does\n          // - hideProvinces, transparentFill, and the showOutline SILHOUETTE ring -\n          // instead of stroking every province path and showing internal borders the\n          // page hides. Two things make it scale correctly here:\n          //   1. wrap the paths in a <g> FIRST. dmbApplyStylesToSvg hangs the filter\n          //      on `svgEl.querySelector(\":scope > g\") || svgEl`, so with a <g> the\n          //      filter runs in USER space and dmbEnsureOutlineFilter converts the\n          //      radius by vbW / o.w - the ring then tracks the thumbnail at any\n          //      size with nothing to measure. (On the page the map has no <g>, the\n          //      filter lands on the root <svg>, and CSS-pixel space is correct\n          //      because 1 builder px == 1 CSS px there.)\n          //   2. the path stroke-width it sets carries non-scaling-stroke, i.e. a\n          //      constant number of CSS px, so convert those to user units too.\n          try {\n            const _g = document.createElementNS(\"http://www.w3.org/2000/svg\", \"g\");\n            while (_msvg.firstChild) _g.appendChild(_msvg.firstChild);\n            _msvg.appendChild(_g);\n            dmbApplyStylesToSvg(_msvg, Object.assign({}, o, {\n              filterId: \"dmbOFmini\" + (++_dmbMiniFilterSeq),\n            }));\n          } catch (e) {}\n          let _mk = 1;\n          try {\n            const _vb  = (_msvg.getAttribute(\"viewBox\") || \"\").trim().split(/[\\s,]+/);\n            const _vbW = (_vb.length >= 3) ? (parseFloat(_vb[2]) || 0) : 0;\n            const _ow  = parseFloat(o.w) || 0;\n            if (_vbW > 0 && _ow > 0) _mk = _vbW / _ow;\n          } catch (e) {}\n          if (_mk !== 1) {\n            _msvg.querySelectorAll(\"path\").forEach((path) => {\n              const _sw = parseFloat(path.style.strokeWidth) || 0;\n              if (_sw > 0) path.style.strokeWidth = (_sw * _mk).toFixed(3);\n              const _da = (path.style.strokeDasharray || \"\").trim();\n              if (_da) {\n                path.style.strokeDasharray = _da.split(/[\\s,]+/)\n                  .map((v) => ((parseFloat(v) || 0) * _mk).toFixed(3)).join(\",\");\n              }\n              path.style.vectorEffect = \"\";   // user units now: must scale with the map\n            });\n          }\n        }\n        mini.appendChild(d); added++;\n      } else if (o.type === \"text\" && o.text && o.text.trim()) {\n        const d = document.createElement(\"div\");\n        d.textContent = o.text;\n        if (o.rich) {\n          // Same share-of-the-width unit the box itself uses, so a word set in a\n          // bigger size stays bigger RELATIVE to the rest instead of printing at\n          // its real pixel size inside a 90px thumbnail.\n          try {\n            _dmbApplyRich(d, o.rich, function (px) {\n              return px > 0 ? ((px / PW * 100).toFixed(2) + \"cqw\") : \"\";\n            });\n          } catch (eR) {}\n        }\n        const fpx = parseFloat(o.fontSize || 28) || 28;\n        const leftPx = parseFloat(o.left || 0) || 0;\n        const topPx = parseFloat(o.top || 0) || 0;\n        const wCss = o.width ? `width:${(parseFloat(o.width) / PW * 100).toFixed(1)}%;` : \"\";\n        d.style.cssText = `position:absolute;left:${(leftPx/PW*100).toFixed(1)}%;top:${(topPx/PH*100).toFixed(1)}%;${wCss}font-size:${(fpx/PW*100).toFixed(2)}cqw;line-height:1.2;font-weight:${o.fontWeight||800};color:${o.color||\"#666\"};font-family:${o.fontFamily||\"'Londrina Solid', sans-serif\"};${o.textAlign?`text-align:${o.textAlign};`:\"\"}${o.textDecoration?`text-decoration:${o.textDecoration};`:\"\"}white-space:${o.whiteSpace||\"pre\"};transform:${o.transform||\"none\"};transform-origin:center center;overflow:hidden;`;\n        mini.appendChild(d); added++;\n      }\n    });\n    return added;\n  }\n\n  function renderThumbsRail() {\n    if (_dmbRenderMode()) return;   // 300 thumbnails of pure memory\n    if (!thumbsRail) return;\n\n    thumbsRail.innerHTML = \"\";\n\n    // Page 0 thumb (special page \u2014 always first)\n    {\n      const card0 = document.createElement(\"button\");\n      card0.type = \"button\";\n      card0.className = \"dmb-page-thumb\";\n      card0.dataset.pageNum = \"0\"; card0.dataset.locked = \"1\";\n      card0.style.textAlign = \"left\";\n\n      const label0 = document.createElement(\"div\");\n      label0.textContent = \"Special Page\";\n      label0.style.fontWeight = \"900\";\n      label0.style.fontSize = \"12px\";\n      label0.style.marginBottom = \"6px\";\n      card0.appendChild(label0);\n\n      const mini0 = document.createElement(\"div\");\n      mini0.style.width = \"100%\";\n      mini0.style.aspectRatio = getPageAspectCss();\n      mini0.style.borderRadius = \"10px\";\n      mini0.style.border = \"1px solid #eee\";\n      mini0.style.overflow = \"hidden\";\n      mini0.style.position = \"relative\";\n      const _pageBg0 = editor.pageBackgrounds?.[0];\n      mini0.style.background = _pageBg0 || \"#fafafa\";\n\n      const thumbTemplateId0 = getPageTemplateId(0);\n      if (thumbTemplateId0) {\n        const tag0 = document.createElement(\"div\");\n        tag0.textContent = TEMPLATE_NAMES[thumbTemplateId0] || thumbTemplateId0.toUpperCase();\n        tag0.style.cssText = \"position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:900;color:#555;\";\n        mini0.appendChild(tag0);\n        (TEMPLATE_DEFS[thumbTemplateId0] || []).forEach((f) => {\n          const r = document.createElement(\"div\");\n          r.style.position = \"absolute\";\n          r.style.left = f.x + \"%\";\n          r.style.top = f.y + \"%\";\n          r.style.width = f.w + \"%\";\n          r.style.height = f.h + \"%\";\n          r.style.border = \"1px solid #ddd\";\n          r.style.boxSizing = \"border-box\";\n          mini0.appendChild(r);\n        });\n        const pf0 = editor.pageFrames?.[0] || {};\n        Object.entries(pf0).forEach(([slotKey, photoId]) => {\n          if (!photoId) return;\n          const frameDef = (TEMPLATE_DEFS[thumbTemplateId0] || []).find((x) => x.key === slotKey);\n          const ph = getPhotoById(photoId);\n          if (!frameDef || !ph) return;\n          const img = document.createElement(\"img\");\n          img.src = _dmbMiniThumbSrc(ph);\n          img.dataset.dmbThumb = ph.id;\n          img.style.cssText = `position:absolute;left:${frameDef.x}%;top:${frameDef.y}%;width:${frameDef.w}%;height:${frameDef.h}%;object-fit:cover;`;\n          const _tShape0 = editor.pageFrameShapes?.[0]?.[slotKey];\n          if (_tShape0) applyDmbFrameShape(img, _tShape0);\n          mini0.appendChild(img);\n        });\n      } else {\n        const A0 = editor.pageSlots[0]?.A;\n        const B0 = editor.pageSlots[0]?.B;\n        if (A0 && !B0) {\n          const ph = getPhotoById(A0);\n          const img = document.createElement(\"img\");\n          img.src = _dmbMiniThumbSrc(ph);\n          if (ph) img.dataset.dmbThumb = ph.id;\n          img.style.cssText = \"width:100%;height:100%;object-fit:cover;\";\n          mini0.appendChild(img);\n        } else if (A0 && B0) {\n          const phA = getPhotoById(A0);\n          const phB = getPhotoById(B0);\n          const top = document.createElement(\"div\");\n          top.style.cssText = \"position:absolute;left:0;right:0;top:0;height:50%;\";\n          const imgA = document.createElement(\"img\");\n          imgA.src = _dmbMiniThumbSrc(phA);\n          if (phA) imgA.dataset.dmbThumb = phA.id;\n          imgA.style.cssText = \"width:100%;height:100%;object-fit:cover;\";\n          top.appendChild(imgA);\n          const bot = document.createElement(\"div\");\n          bot.style.cssText = \"position:absolute;left:0;right:0;bottom:0;height:50%;\";\n          const imgB = document.createElement(\"img\");\n          imgB.src = _dmbMiniThumbSrc(phB);\n          if (phB) imgB.dataset.dmbThumb = phB.id;\n          imgB.style.cssText = \"width:100%;height:100%;object-fit:cover;\";\n          bot.appendChild(imgB);\n          mini0.appendChild(top);\n          mini0.appendChild(bot);\n        } else {\n          const _freeObjs0 = Array.isArray(editor.pageObjects?.[0]) ? editor.pageObjects[0] : [];\n          const _frames0 = _freeObjs0.filter(o => o.type === \"photo-frame\" && o.photoId);\n          const _pd0 = getPageDims(); const PW0 = _pd0.w, PH0 = _pd0.h;\n          if (_frames0.length > 0) {\n            _frames0.forEach(o => {\n              const _fph = getPhotoById(o.photoId);\n              if (!_fph) return;\n              const _fi = document.createElement(\"img\");\n              _fi.src = _dmbMiniThumbSrc(_fph);\n              _fi.dataset.dmbThumb = _fph.id;\n              _fi.style.cssText = `position:absolute;left:${(o.x/PW0*100).toFixed(1)}%;top:${(o.y/PH0*100).toFixed(1)}%;width:${(o.w/PW0*100).toFixed(1)}%;height:${(o.h/PH0*100).toFixed(1)}%;object-fit:cover;border-radius:2px;`;\n              if (o.frameShape) applyDmbFrameShape(_fi, o.frameShape);\n              mini0.appendChild(_fi);\n            });\n          }\n          // stickers / text / shapes drawn by _dmbRenderPageOverlay below.\n        }\n      }\n\n      // Mirror decorative layers (stickers, text, shapes) on the Special Page card too.\n      {\n        const _pd0o = getPageDims();\n        _dmbRenderPageOverlay(mini0, editor.pageObjects?.[0], _pd0o.w, _pd0o.h);\n        if (!mini0.children.length && !_pageBg0) {\n          const empty0 = document.createElement(\"div\");\n          empty0.textContent = \"Empty\";\n          empty0.style.cssText = \"position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:900;color:#aaa;\";\n          mini0.appendChild(empty0);\n        }\n      }\n\n      card0.appendChild(mini0);\n      card0.addEventListener(\"click\", () => {\n        builderMode = \"pages\";\n        editor.currentSpread = -1;\n        renderBuilderCanvas();\n        // (no auto-scroll on page-thumbnail click \u2014 let the customer control scrolling)\n      });\n      thumbsRail.appendChild(card0);\n    }\n\n    for (let p = 1; p <= editor.totalPages; p++) {\n      const card = document.createElement(\"button\");\n      card.type = \"button\";\n      card.className = \"dmb-page-thumb\";\n      card.dataset.pageNum = String(p);\n      if (p === 1 || isLockedPage(p)) card.dataset.locked = \"1\";\n      card.style.textAlign = \"left\";\n\n      const label = document.createElement(\"div\");\n      label.textContent = `Page ${p}`;\n      label.style.fontWeight = \"900\";\n      label.style.fontSize = \"12px\";\n      label.style.marginBottom = \"6px\";\n      card.appendChild(label);\n\n      const mini = document.createElement(\"div\");\n      mini.style.width = \"100%\";\n      mini.style.aspectRatio = getPageAspectCss();\n      mini.style.borderRadius = \"10px\";\n      mini.style.border = \"1px solid #eee\";\n      mini.style.overflow = \"hidden\";\n      mini.style.position = \"relative\";\n      // Mirror the page's background color (falls back to the empty-page grey).\n      const _pageBg = editor.pageBackgrounds?.[p];\n      mini.style.background = _pageBg || \"#fafafa\";\n\n      if (isLockedPage(p)) {\n        const sp = document.createElement(\"div\");\n        sp.textContent = \"SPECIAL\";\n        sp.style.position = \"absolute\";\n        sp.style.inset = \"0\";\n        sp.style.display = \"flex\";\n        sp.style.alignItems = \"center\";\n        sp.style.justifyContent = \"center\";\n        sp.style.fontSize = \"11px\";\n        sp.style.fontWeight = \"900\";\n        sp.style.color = \"#777\";\n        mini.appendChild(sp);\n      } else {\n        const thumbTemplateId = getPageTemplateId(p);\n\n        if (thumbTemplateId) {\n          const tag = document.createElement(\"div\");\n          tag.textContent = TEMPLATE_NAMES[thumbTemplateId] || thumbTemplateId.toUpperCase();\n          tag.style.position = \"absolute\";\n          tag.style.inset = \"0\";\n          tag.style.display = \"flex\";\n          tag.style.alignItems = \"center\";\n          tag.style.justifyContent = \"center\";\n          tag.style.fontSize = \"10px\";\n          tag.style.fontWeight = \"900\";\n          tag.style.color = \"#555\";\n          mini.appendChild(tag);\n\n          (TEMPLATE_DEFS[thumbTemplateId] || []).forEach((f) => {\n            const _fp = _dmbLayoutFramePct(f);\n            const r = document.createElement(\"div\");\n            r.style.position = \"absolute\";\n            r.style.left = _fp.x + \"%\";\n            r.style.top = _fp.y + \"%\";\n            r.style.width = _fp.w + \"%\";\n            r.style.height = _fp.h + \"%\";\n            r.style.border = \"1px solid #ddd\";\n            r.style.boxSizing = \"border-box\";\n            mini.appendChild(r);\n          });\n\n          const pageFrames = editor.pageFrames?.[p] || {};\n          Object.entries(pageFrames).forEach(([slotKey, photoId]) => {\n            if (!photoId) return;\n            const frameDef = (TEMPLATE_DEFS[thumbTemplateId] || []).find((x) => x.key === slotKey);\n            const ph = getPhotoById(photoId);\n            if (!frameDef || !ph) return;\n\n            const _fpi = _dmbLayoutFramePct(frameDef);\n            const img = document.createElement(\"img\");\n            img.src = _dmbMiniThumbSrc(ph);\n            img.dataset.dmbThumb = ph.id;\n            img.style.position = \"absolute\";\n            img.style.left = _fpi.x + \"%\";\n            img.style.top = _fpi.y + \"%\";\n            img.style.width = _fpi.w + \"%\";\n            img.style.height = _fpi.h + \"%\";\n            img.style.objectFit = \"cover\";\n            const _tShape = editor.pageFrameShapes?.[p]?.[slotKey];\n            if (_tShape) applyDmbFrameShape(img, _tShape);\n            mini.appendChild(img);\n          });\n        } else {\n          // Check for full-spread photo first\n          const thumbSi = Math.floor((p - 1) / 2);\n          const thumbSp = editor.spreadPhoto?.[thumbSi];\n          if (thumbSp?.photoId) {\n            const ph = getPhotoById(thumbSp.photoId);\n            if (ph) {\n              const img = document.createElement(\"img\");\n              img.src = _dmbMiniThumbSrc(ph);\n              img.dataset.dmbThumb = ph.id;\n              img.style.width = \"100%\";\n              img.style.height = \"100%\";\n              img.style.objectFit = \"cover\";\n              mini.appendChild(img);\n            }\n          }\n\n          const A = editor.pageSlots[p]?.A;\n          const B = editor.pageSlots[p]?.B;\n\n          if (!thumbSp?.photoId && A && !B) {\n            const ph = getPhotoById(A);\n            const img = document.createElement(\"img\");\n            img.src = _dmbMiniThumbSrc(ph);\n            if (ph) img.dataset.dmbThumb = ph.id;\n            img.style.width = \"100%\";\n            img.style.height = \"100%\";\n            img.style.objectFit = \"cover\";\n            mini.appendChild(img);\n          } else if (!thumbSp?.photoId && A && B) {\n            const phA = getPhotoById(A);\n            const phB = getPhotoById(B);\n\n            const _thumbLS = dmbIsLandscape();\n            const top = document.createElement(\"div\");\n            top.style.position = \"absolute\";\n            if (_thumbLS) { top.style.left = \"0\"; top.style.top = \"0\"; top.style.bottom = \"0\"; top.style.width = \"50%\"; }\n            else { top.style.left = \"0\"; top.style.right = \"0\"; top.style.top = \"0\"; top.style.height = \"50%\"; }\n\n            const imgA = document.createElement(\"img\");\n            imgA.src = _dmbMiniThumbSrc(phA);\n            if (phA) imgA.dataset.dmbThumb = phA.id;\n            imgA.style.width = \"100%\";\n            imgA.style.height = \"100%\";\n            imgA.style.objectFit = \"cover\";\n            top.appendChild(imgA);\n\n            const bot = document.createElement(\"div\");\n            bot.style.position = \"absolute\";\n            if (_thumbLS) { bot.style.right = \"0\"; bot.style.top = \"0\"; bot.style.bottom = \"0\"; bot.style.width = \"50%\"; }\n            else { bot.style.left = \"0\"; bot.style.right = \"0\"; bot.style.bottom = \"0\"; bot.style.height = \"50%\"; }\n\n            const imgB = document.createElement(\"img\");\n            imgB.src = _dmbMiniThumbSrc(phB);\n            if (phB) imgB.dataset.dmbThumb = phB.id;\n            imgB.style.width = \"100%\";\n            imgB.style.height = \"100%\";\n            imgB.style.objectFit = \"cover\";\n            bot.appendChild(imgB);\n\n            const divider = document.createElement(\"div\");\n            divider.style.position = \"absolute\";\n            divider.style.background = \"rgba(0,0,0,0.12)\";\n            if (_thumbLS) { divider.style.top = \"0\"; divider.style.bottom = \"0\"; divider.style.left = \"50%\"; divider.style.width = \"1px\"; }\n            else { divider.style.left = \"0\"; divider.style.right = \"0\"; divider.style.top = \"50%\"; divider.style.height = \"1px\"; }\n\n            mini.appendChild(top);\n            mini.appendChild(bot);\n            mini.appendChild(divider);\n          } else if (!thumbSp?.photoId) {\n            // Check for free objects (frames with photos, text, stickers, maps)\n            const _freeObjs = Array.isArray(editor.pageObjects?.[p]) ? editor.pageObjects[p] : [];\n            const _framesWithPhoto = _freeObjs.filter(o => o.type === \"photo-frame\" && o.photoId);\n            const _pdT = getPageDims(); const PAGE_W_THUMB = _pdT.w, PAGE_H_THUMB = _pdT.h;\n\n            if (_framesWithPhoto.length > 0) {\n              // Render free frame photos at scaled positions\n              _framesWithPhoto.forEach(o => {\n                const _fph = getPhotoById(o.photoId);\n                if (!_fph) return;\n                const _fi = document.createElement(\"img\");\n                _fi.src = _dmbMiniThumbSrc(_fph);\n                _fi.dataset.dmbThumb = _fph.id;\n                _fi.style.cssText = `position:absolute;left:${(o.x/PAGE_W_THUMB*100).toFixed(1)}%;top:${(o.y/PAGE_H_THUMB*100).toFixed(1)}%;width:${(o.w/PAGE_W_THUMB*100).toFixed(1)}%;height:${(o.h/PAGE_H_THUMB*100).toFixed(1)}%;object-fit:cover;border-radius:2px;`;\n                if (o.frameShape) applyDmbFrameShape(_fi, o.frameShape);\n                mini.appendChild(_fi);\n              });\n            }\n            // Stickers / text / shapes (and remaining free objects) are drawn by\n            // _dmbRenderPageOverlay below; the \"Empty\" label is decided after that.\n          }\n        }\n      }\n\n      // Mirror decorative layers (stickers, text, shapes, mag-icons) on top, then only\n      // label the card \"Empty\" if nothing at all rendered and no background color is set.\n      if (!isLockedPage(p)) {\n        const _pd = getPageDims();\n        _dmbRenderPageOverlay(mini, editor.pageObjects?.[p], _pd.w, _pd.h);\n        if (!mini.children.length && !_pageBg) {\n          const empty = document.createElement(\"div\");\n          empty.textContent = \"Empty\";\n          empty.style.cssText = \"position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:900;color:#aaa;\";\n          mini.appendChild(empty);\n        }\n      }\n\n      card.appendChild(mini);\n\n      // Hover delete (circle-X): removes just this one page. Only on real content\n      // pages (2..N, never the locked Special pages). Sits on top of the card and\n      // swallows its own mouse/drag events so it never starts a reorder-drag.\n      if (p >= 2 && !isLockedPage(p)) {\n        const del = document.createElement(\"button\");\n        del.type = \"button\";\n        del.className = \"dmb-page-del\";\n        del.setAttribute(\"aria-label\", \"Delete page \" + p);\n        del.title = \"Delete this page\";\n        del.draggable = false;\n        del.innerHTML = `<svg viewBox=\"0 0 24 24\" width=\"12\" height=\"12\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\" aria-hidden=\"true\"><path d=\"M7 7l10 10M17 7L7 17\"/></svg>`;\n        del.addEventListener(\"mousedown\", (e) => { e.stopPropagation(); });\n        del.addEventListener(\"dragstart\", (e) => { e.preventDefault(); e.stopPropagation(); });\n        del.addEventListener(\"click\", (e) => {\n          e.preventDefault();\n          e.stopPropagation();\n          _dmbDeletePage(p);\n        });\n        card.appendChild(del);\n      }\n\n      card.addEventListener(\"click\", () => {\n        builderMode = \"pages\";\n        editor.currentSpread = Math.floor((p - 1) / 2);\n        renderBuilderCanvas();\n        // (no auto-scroll on page-thumbnail click \u2014 let the customer control scrolling)\n      });\n\n      thumbsRail.appendChild(card);\n    }\n    // Desktop: layer drag-to-reorder + insert affordances over the rail.\n    try { _dmbBuildPageReorderUI(); } catch (e) {}\n  }\n\n  // \u2500\u2500 Page reorder UI (desktop only) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // Groups the page thumbnails into spread pairs, adds a \u28FF grip per movable pair\n  // (drag = move whole spread), makes each movable page draggable (drag = move that\n  // one page), shows a black insertion line while dragging, and reveals a \"+\" in the\n  // gaps between spreads to insert a new blank spread. Single-page drops ask\n  // swap-vs-insert; a drop that would split a full-spread photo warns first.\n  let _dmbPageDrag = null;   // { kind:'page'|'spread', page?, pair? }\n\n  // Build an off-screen drag ghost that shows BOTH pages of a spread side by side.\n  // The native drag image would only show the single card that fired `dragstart`,\n  // which looks like just one page is moving. We clone both cards, strip the delete\n  // \"X\"/insert-zones, lay them flush together, and hand it to setDragImage(). The\n  // node lives off-screen and is removed on the next tick.\n  function _dmbBuildSpreadDragGhost(pair) {\n    const cl = _dmbCardByPage(pair[0]), cr = _dmbCardByPage(pair[1]);\n    if (!cl || !cr) return null;\n    const wrap = document.createElement(\"div\");\n    wrap.className = \"dmb-spread-drag-ghost\";\n    // Bigger, fully opaque, bold dark frame \u2014 browsers render drag images with some\n    // built-in transparency, so we over-build the contrast/size to stay clearly visible.\n    wrap.style.cssText = \"position:fixed;top:-10000px;left:-10000px;display:flex;gap:0;\" +\n      \"pointer-events:none;background:#fff;border:3px solid #111;border-radius:14px;\" +\n      \"overflow:hidden;box-shadow:0 12px 30px rgba(0,0,0,.45);opacity:0.65;\";\n    [cl, cr].forEach((card, i) => {\n      const clone = card.cloneNode(true);\n      clone.classList.remove(\"dmb-pt-big\", \"active\");\n      clone.style.transform = \"none\";\n      clone.style.margin = \"0\";\n      clone.style.border = \"none\";\n      clone.style.borderRadius = \"0\";\n      clone.style.width = \"150px\";       // scale each page up (default thumb is 112px)\n      clone.style.background = \"#fff\";\n      if (i === 1) clone.style.borderLeft = \"2px solid #111\";   // center seam of the spread\n      clone.querySelectorAll(\".dmb-page-del, .dmb-insert-zone, .dmb-drop-line, .dmb-spread-box\")\n        .forEach((n) => n.remove());\n      wrap.appendChild(clone);\n    });\n    document.body.appendChild(wrap);\n    return wrap;\n  }\n\n  function _dmbBuildPageReorderUI() {\n    if (!thumbsRail) return;\n    if (window.matchMedia && window.matchMedia(\"(max-width: 749px), (orientation: portrait) and (pointer: coarse) and (max-width: 1024px)\").matches) return; // desktop only\n    thumbsRail.classList.add(\"dmb-rail-reorder\");\n    const cards = Array.from(thumbsRail.querySelectorAll(\".dmb-page-thumb\"));\n    // index cards by page number\n    const cardByPage = {};\n    cards.forEach((c) => { cardByPage[c.dataset.pageNum] = c; });\n\n    // Tag each spread pair on BOTH its cards (drives spread-hover + spread-drag).\n    const { pairs } = _dmbSpreadPairs();\n    pairs.forEach(([l, r]) => {\n      const a = cardByPage[String(l)], b = cardByPage[String(r)];\n      if (a) { a._dmbPair = [l, r]; a.classList.add(\"dmb-pt-spread-start\"); }\n      if (b) { b._dmbPair = [l, r]; b.classList.add(\"dmb-pt-spread-end\"); }\n    });\n\n    // Movable cards are draggable. The drag KIND (single page vs whole spread) comes\n    // from the current HOVER MODE (set by _dmbRailHover): hovering a page BODY enlarges\n    // just that page \u2192 single-page drag; hovering BETWEEN the pair or within a few px\n    // AROUND it enlarges both pages \u2192 whole-spread drag. (No \u28FF grip.)\n    cards.forEach((c) => {\n      const p = +c.dataset.pageNum;\n      if (c.dataset.locked === \"1\" || p < 2) return;\n      c.draggable = true;\n      c.classList.add(\"dmb-pt-draggable\");\n      c.addEventListener(\"dragstart\", (e) => {\n        _dmbPageDrag = (_dmbHoverMode && _dmbHoverMode.kind === \"spread\" && _dmbHoverMode.pair)\n          ? { kind: \"spread\", pair: _dmbHoverMode.pair }\n          : { kind: \"page\", page: p };\n        try { e.dataTransfer.effectAllowed = \"move\"; e.dataTransfer.setData(\"text/x-dmb\", \"1\"); } catch (err) {}\n        // Whole-spread drag \u2192 show BOTH pages in the drag ghost so it's obvious the\n        // pair moves together (native drag would only ghost the one card under the cursor).\n        if (_dmbPageDrag.kind === \"spread\" && _dmbPageDrag.pair && e.dataTransfer && e.dataTransfer.setDragImage) {\n          try {\n            const ghost = _dmbBuildSpreadDragGhost(_dmbPageDrag.pair);\n            if (ghost) {\n              e.dataTransfer.setDragImage(ghost, ghost.offsetWidth / 2, ghost.offsetHeight / 2);\n              setTimeout(() => { try { ghost.remove(); } catch (_e) {} }, 0);\n            }\n          } catch (err) {}\n        }\n        thumbsRail.classList.add(\"dmb-rail-dragging\");\n        _dmbClearHover();   // stop enlarging while dragging\n      });\n      c.addEventListener(\"dragend\", () => { _dmbPageDrag = null; thumbsRail.classList.remove(\"dmb-rail-dragging\"); _dmbClearDropLine(); });\n    });\n\n    // Insertion line (reused).\n    if (!thumbsRail.querySelector(\".dmb-drop-line\")) {\n      const line = document.createElement(\"div\"); line.className = \"dmb-drop-line\"; thumbsRail.appendChild(line);\n    }\n    // Spread-selection rectangle (the platform shown under a pair in spread mode).\n    if (!thumbsRail.querySelector(\".dmb-spread-box\")) {\n      const b = document.createElement(\"div\"); b.className = \"dmb-spread-box\"; thumbsRail.appendChild(b);\n    }\n\n    _dmbWirePageRailHover();\n    _dmbWirePageRailDnD();\n    _dmbBuildInsertZones(cardByPage, pairs);   // collapsed gaps that open on hover (CSS)\n  }\n\n  // Rail-level hover: enlarge ONE page (hover its body) or BOTH pages of a pair (hover\n  // between them / within ~20px around the pair), and remember the resulting drag mode.\n  let _dmbHoverMode = null;   // null | {kind:'single',page} | {kind:'spread',pair}\n  function _dmbWirePageRailHover() {\n    if (thumbsRail._dmbHoverWired) return;\n    thumbsRail._dmbHoverWired = true;\n    thumbsRail.addEventListener(\"mousemove\", (e) => { if (!_dmbPageDrag) _dmbRailHover(e); });\n    thumbsRail.addEventListener(\"mouseleave\", () => _dmbClearHover());\n  }\n  function _dmbCardByPage(p) { return thumbsRail.querySelector('.dmb-page-thumb[data-page-num=\"' + p + '\"]'); }\n  function _dmbSetHover(mode) {\n    _dmbHoverMode = mode;\n    thumbsRail.querySelectorAll(\".dmb-pt-big\").forEach((el) => el.classList.remove(\"dmb-pt-big\"));\n    const box = thumbsRail.querySelector(\".dmb-spread-box\");\n    if (box) box.style.display = \"none\";\n    if (!mode) return;\n    if (mode.kind === \"spread\" && mode.pair) {\n      const cl = _dmbCardByPage(mode.pair[0]), cr = _dmbCardByPage(mode.pair[1]);\n      [cl, cr].forEach((c) => { if (c) c.classList.add(\"dmb-pt-big\"); });\n      if (cl && cr && box) {\n        // Rectangle UNDER both pages of the pair (a platform holding the spread).\n        const PAD = 11;\n        const left   = Math.min(cl.offsetLeft, cr.offsetLeft) - PAD;\n        const right  = Math.max(cl.offsetLeft + cl.offsetWidth, cr.offsetLeft + cr.offsetWidth) + PAD;\n        const top    = Math.min(cl.offsetTop, cr.offsetTop) - PAD;\n        const bottom = Math.max(cl.offsetTop + cl.offsetHeight, cr.offsetTop + cr.offsetHeight) + PAD;\n        box.style.left = left + \"px\"; box.style.top = top + \"px\";\n        box.style.width = (right - left) + \"px\"; box.style.height = (bottom - top) + \"px\";\n        box.style.display = \"block\";\n      }\n    } else if (mode.kind === \"single\") {\n      const c = _dmbCardByPage(mode.page); if (c) c.classList.add(\"dmb-pt-big\");\n    }\n  }\n  function _dmbClearHover() { _dmbSetHover(null); }\n  function _dmbRailHover(e) {\n    const M = 22;   // \"a certain px\" band for the SPREAD trigger (seam + top + bottom)\n    const movable = Array.from(thumbsRail.querySelectorAll(\".dmb-page-thumb\"))\n      .filter((c) => +c.dataset.pageNum >= 2)\n      .sort((a, b) => (+a.dataset.pageNum) - (+b.dataset.pageNum));\n    if (!movable.length) { _dmbClearHover(); return; }\n    const x = e.clientX, y = e.clientY;\n    // over a card?\n    let over = null;\n    for (const c of movable) { const r = c.getBoundingClientRect(); if (x >= r.left && x <= r.right && y >= r.top && y <= r.bottom) { over = c; break; } }\n    if (over) {\n      const r = over.getBoundingClientRect();\n      let spread = false;\n      if (over._dmbPair) {\n        // Spread trigger = the pair's shared boundary: the SEAM edge (facing its\n        // partner) + top + bottom. The OUTER edge (facing a between-spreads gap) is\n        // left free so the insert-\"+\" zone can be nice and sensitive there.\n        const isStart = over._dmbPair[0] === (+over.dataset.pageNum);\n        const nearSeam = isStart ? ((r.right - x) < M) : ((x - r.left) < M);\n        spread = nearSeam || (y - r.top < M) || (r.bottom - y < M);\n      }\n      _dmbSetHover(spread ? { kind: \"spread\", pair: over._dmbPair } : { kind: \"single\", page: +over.dataset.pageNum });\n      return;\n    }\n    // in a gap: is it the SEAM within a pair? \u2192 spread. Otherwise clear (insert-zone CSS handles gaps).\n    let left = null, right = null;\n    for (let i = 0; i < movable.length; i++) {\n      const r = movable[i].getBoundingClientRect();\n      if (r.right <= x) left = movable[i];\n      if (!right && r.left >= x) right = movable[i];\n    }\n    if (left && right && left._dmbPair && left._dmbPair[0] === (+left.dataset.pageNum) && left._dmbPair[1] === (+right.dataset.pageNum)) {\n      _dmbSetHover({ kind: \"spread\", pair: left._dmbPair });\n    } else {\n      _dmbClearHover();\n    }\n  }\n\n  function _dmbClearDropLine() {\n    const line = thumbsRail && thumbsRail.querySelector(\".dmb-drop-line\");\n    if (line) line.style.display = \"none\";\n  }\n\n  // Compute the drop gap under the cursor: returns { idx, x } where idx is the\n  // movable-index (0-based among 2..N) the dragged item would be inserted BEFORE.\n  function _dmbDropTargetAtX(clientX) {\n    const movable = Array.from(thumbsRail.querySelectorAll('.dmb-page-thumb'))\n      .filter((c) => +c.dataset.pageNum >= 2)\n      .sort((a, b) => (+a.dataset.pageNum) - (+b.dataset.pageNum));\n    if (!movable.length) return null;\n    // Determine insertion index by comparing cursor with each card's mid-x.\n    let idx = movable.length; let lineX = null;\n    for (let i = 0; i < movable.length; i++) {\n      const r = movable[i].getBoundingClientRect();\n      const mid = r.left + r.width / 2;\n      if (clientX < mid) { idx = i; lineX = r.left - 4; break; }\n    }\n    if (lineX == null) { const r = movable[movable.length - 1].getBoundingClientRect(); lineX = r.right + 4; }\n    return { idx, lineX };\n  }\n\n  function _dmbWirePageRailDnD() {\n    if (thumbsRail._dmbDndWired) return; // wire the rail-level listeners once\n    thumbsRail._dmbDndWired = true;\n    const line = () => thumbsRail.querySelector(\".dmb-drop-line\");\n    thumbsRail.addEventListener(\"dragover\", (e) => {\n      if (!_dmbPageDrag) return;\n      e.preventDefault();\n      try { e.dataTransfer.dropEffect = \"move\"; } catch (err) {}\n      const t = _dmbDropTargetAtX(e.clientX);\n      const l = line();\n      if (t && l) {\n        const railRect = thumbsRail.getBoundingClientRect();\n        l.style.display = \"block\";\n        l.style.left = (t.lineX - railRect.left + thumbsRail.scrollLeft) + \"px\";\n      }\n    });\n    thumbsRail.addEventListener(\"drop\", (e) => {\n      if (!_dmbPageDrag) return;\n      e.preventDefault();\n      const t = _dmbDropTargetAtX(e.clientX);\n      _dmbClearDropLine();\n      const drag = _dmbPageDrag; _dmbPageDrag = null;\n      thumbsRail.classList.remove(\"dmb-rail-dragging\");\n      if (!t) return;\n      _dmbHandlePageDrop(drag, t.idx);\n    });\n    thumbsRail.addEventListener(\"dragleave\", (e) => {\n      if (e.target === thumbsRail && !thumbsRail.contains(e.relatedTarget)) _dmbClearDropLine();\n    });\n  }\n\n  // Handle a completed drop. `idx` = movable-index (0-based among 2..N) to insert BEFORE.\n  async function _dmbHandlePageDrop(drag, idx) {\n    const movable = _dmbMovablePages();\n    if (drag.kind === \"spread\") {\n      const [l, r] = drag.pair;\n      const { pairs } = _dmbSpreadPairs();\n      const origSlot = pairs.findIndex((pr) => pr[0] === l);\n      if (origSlot < 0) return;\n      let rawTarget = Math.max(0, Math.min(Math.round((idx - 1) / 2), pairs.length));\n      let toPairIdx = (origSlot < rawTarget) ? rawTarget - 1 : rawTarget;\n      toPairIdx = Math.max(0, Math.min(toPairIdx, pairs.length - 1));\n      if (toPairIdx === origSlot) return;\n      _dmbMoveSpread(l, r, toPairIdx);\n      return;\n    }\n    // single page\n    const from = drag.page;\n    const fromIdx = movable.indexOf(from);\n    if (fromIdx < 0) return;\n    if (idx === fromIdx || idx === fromIdx + 1) return;   // dropped in its own slot\n    const swapPartner = movable[idx];                     // page displaced (undefined if end)\n    let mode = \"insert\";\n    if (swapPartner != null) {\n      const choice = await showChoiceDialog(\n        \"Move this page\",\n        `Drop Page ${from} here \u2014 swap it with Page ${swapPartner}, or insert it and shift the rest?`,\n        [ { id: \"insert\", label: `Insert Page ${from} here (shift the rest)` },\n          { id: \"swap\",   label: `Swap Page ${from} and Page ${swapPartner}` },\n          { id: \"cancel\", label: \"Cancel\" } ]\n      );\n      if (!choice || choice === \"cancel\") return;\n      mode = choice;\n    }\n    if (mode === \"swap\") { _dmbSwapPages(from, swapPartner); return; }\n    // insert \u2014 adjust target index for the removal of `from`\n    const toIdx = (fromIdx < idx) ? idx - 1 : idx;\n    if (_dmbSingleMoveWouldSplit(from, toIdx)) {\n      const ok = await showChoiceDialog(\n        \"Full-spread photo\",\n        \"Moving this page will separate a photo that spans a whole spread, so that photo will be removed. Continue?\",\n        [ { id: \"yes\", label: \"Move it (remove the spread photo)\" }, { id: \"no\", label: \"Cancel\" } ]\n      );\n      if (ok !== \"yes\") return;\n    }\n    _dmbMoveSinglePage(from, toIdx);\n  }\n\n  // Hover-\"+\" zones in the gaps BETWEEN spreads (click inserts a blank spread there).\n  function _dmbBuildInsertZones(cardByPage, pairs) {\n    const mk = (movableIdx) => {\n      const z = document.createElement(\"div\");\n      z.className = \"dmb-insert-zone\";\n      try {\n        z.title = window.DMB_MAGAZINE_MODE\n          ? (\"Insert two blank spreads here (4 pages, \" + _dmbMagPairCost() + \")\")\n          : (\"Insert a blank spread here (2 pages, \" + fmtMoney(EXTRA_SPREAD_PRICE) + \")\");\n      } catch (e) { z.title = \"Insert a blank spread here\"; }\n      const plus = document.createElement(\"div\");\n      plus.className = \"dmb-insert-plus\"; plus.textContent = \"+\";\n      z.appendChild(plus);\n      z.addEventListener(\"click\", (e) => { e.preventDefault(); e.stopPropagation(); _dmbInsertSpreadAt(movableIdx); });\n      return z;\n    };\n    pairs.forEach(([l]) => {\n      const startCard = cardByPage[String(l)];\n      if (startCard && startCard.parentNode) startCard.parentNode.insertBefore(mk(l - 2), startCard);\n    });\n    const movableCards = Array.from(thumbsRail.querySelectorAll(\".dmb-page-thumb\")).filter((c) => +c.dataset.pageNum >= 2);\n    const last = movableCards[movableCards.length - 1];\n    if (last && last.parentNode) {\n      const z = mk(_dmbMovablePages().length);\n      if (last.nextSibling) last.parentNode.insertBefore(z, last.nextSibling); else last.parentNode.appendChild(z);\n    }\n  }\n\n  // \"+ Add a spread\" \u2192 ask before/after the CURRENT spread, then insert a blank spread.\n  async function _dmbAddSpreadPrompt() {\n    const cur = (typeof editor.currentSpread === \"number\") ? editor.currentSpread : 0;\n    // Magazines get two spreads per add, so every label here says so.\n    const _mt = window.DMB_MAGAZINE_MODE ? \"Add 2 spreads\" : \"Add a spread\";\n    const _mw = window.DMB_MAGAZINE_MODE ? \"two blank spreads\" : \"the new blank spread\";\n    // Say the cost before anything is added, while Cancel is still on the screen.\n    let _mc = \"\";\n    try {\n      _mc = window.DMB_MAGAZINE_MODE\n        ? (\" A magazine adds pages in fours, so this is 4 pages for \" + _dmbMagPairCost() + \".\")\n        : (\" That is 2 pages for \" + fmtMoney(EXTRA_SPREAD_PRICE) + \".\");\n    } catch (e) {}\n    // On the Special Page (currentSpread -1) or the first spread (0), pages 1 & 2 are the\n    // fixed intro \u2014 a new spread can't go before them, only AFTER page 2. Tell the customer\n    // and add it there, instead of offering a \"before\" that isn't allowed.\n    if (cur <= 0) {\n      const ok = await showChoiceDialog(\n        _mt,\n        \"Pages 1 and 2 are the fixed intro, so new pages can only be added after page 2.\" + _mc,\n        [ { id: \"ok\",     label: \"Add after page 2\" },\n          { id: \"cancel\", label: \"Cancel\" } ]\n      );\n      if (!ok || ok === \"cancel\") return;\n      _dmbInsertSpreadAt(1);   // movable index 1 = right after page 2\n      return;\n    }\n    const si = Math.max(0, cur);\n    const choice = await showChoiceDialog(\n      _mt,\n      \"Where would you like \" + _mw + \"?\" + _mc,\n      [ { id: \"after\",  label: \"After the spread I'm on\" },\n        { id: \"before\", label: \"Before the spread I'm on\" },\n        { id: \"cancel\", label: \"Cancel\" } ]\n    );\n    if (!choice || choice === \"cancel\") return;\n    const movLen = _dmbMovablePages().length;\n    let movableIdx = (choice === \"before\") ? (si * 2 - 1) : (si * 2 + 1);\n    // Never allow inserting before page 2 (min index 1); clamp to the end otherwise.\n    movableIdx = Math.max(1, Math.min(movableIdx, movLen));\n    _dmbInsertSpreadAt(movableIdx);\n  }\n\n  function updateCoverZoneButtons() {\n    const frontBtn = $(\"coverZoneFrontBtn\");\n    const backBtn = $(\"coverZoneBackBtn\");\n    const spineBtn = $(\"coverZoneSpineBtn\");\n\n    [frontBtn, backBtn, spineBtn].forEach((btn) => {\n      if (btn) btn.classList.remove(\"active\");\n    });\n\n    if (activeCoverZone === \"front\" && frontBtn) frontBtn.classList.add(\"active\");\n    if (activeCoverZone === \"back\" && backBtn) backBtn.classList.add(\"active\");\n    if (activeCoverZone === \"spine\" && spineBtn) spineBtn.classList.add(\"active\");\n  }\n  function makeHint(text) {\n    const hint = document.createElement(\"div\");\n    hint.textContent = text;\n    hint.style.position = \"absolute\";\n    hint.style.inset = \"14px\";\n    hint.style.border = \"2px dashed #ddd\";\n    hint.style.borderRadius = \"12px\";\n    hint.style.display = \"flex\";\n    hint.style.alignItems = \"center\";\n    hint.style.justifyContent = \"center\";\n    hint.style.color = \"#888\";\n    hint.style.fontWeight = \"900\";\n    hint.style.fontSize = \"12px\";\n    hint.style.pointerEvents = \"none\";\n    return hint;\n  }\n\n  function handleDropOnPage(pageNumber, clientY, payload, dataTransfer, bodyEl, clientX) {\n    if (isLockedPage(pageNumber)) return;\n\n    if (isTemplatePage(pageNumber)) {\n      const photoId = (() => {\n        if (payload && payload.photoId) return payload.photoId;\n\n        const tp = dataTransfer ? (dataTransfer.getData(\"text/plain\") || \"\").trim() : \"\";\n        if (!tp) return \"\";\n\n        if (tp[0] === \"{\") {\n          try {\n            const obj = JSON.parse(tp);\n            return obj.photoId || \"\";\n          } catch {\n            return \"\";\n          }\n        }\n        return tp;\n      })();\n\n      if (!photoId) return;\n\n      const targetRect = pickNearestTemplateFrame(bodyEl, pageNumber, clientX ?? 0, clientY ?? 0);\n      if (!targetRect) return;\n\n      const slotKey = targetRect.key;\n      const currentInTarget = getFramePhotoId(pageNumber, slotKey);\n\n      const used = getUsedPhotoIds();\n      const isReusedPhoto = payload?.type === \"photo\" && used.has(photoId);\n      if (isReusedPhoto) showReuseNotice();\n\n      pushUndoState();\n\n      if (payload && payload.type === \"placed\") {\n        const fromPage = Number(payload.fromPage);\n        const fromSlot = payload.fromSlot;\n\n        if (fromPage && fromSlot) {\n          if (fromPage === pageNumber && fromSlot === slotKey) return;\n\n          const replaced = currentInTarget;\n          const replacedRot = getFrameRotation(pageNumber, slotKey);\n\n          setFramePhotoId(pageNumber, slotKey, photoId);\n          setFrameRotation(pageNumber, slotKey, 0);\n\n          if (replaced) {\n            setFramePhotoId(fromPage, fromSlot, replaced);\n            setFrameRotation(fromPage, fromSlot, replacedRot);\n          } else {\n            clearFrame(fromPage, fromSlot);\n\n            if (!isTemplatePage(fromPage)) {\n              if (fromSlot === \"A\" && editor.pageSlots[fromPage]?.B) {\n                editor.pageSlots[fromPage].A = editor.pageSlots[fromPage].B;\n                editor.pageSlots[fromPage].B = null;\n                editor.pageRot[fromPage].A = editor.pageRot[fromPage].B;\n                editor.pageRot[fromPage].B = 0;\n              }\n            }\n          }\n\n          renderSpread();\n          renderThumbsRail();\n          renderPhotoTray();\n          return;\n        }\n      }\n\n      setFramePhotoId(pageNumber, slotKey, photoId);\n      setFrameRotation(pageNumber, slotKey, 0);\n\n      renderSpread();\n      renderThumbsRail();\n      renderPhotoTray();\n      return;\n    }\n\n    const photoId = (() => {\n      if (payload && payload.photoId) return payload.photoId;\n\n      const tp = dataTransfer ? (dataTransfer.getData(\"text/plain\") || \"\").trim() : \"\";\n      if (!tp) return \"\";\n\n      if (tp[0] === \"{\") {\n        try {\n          const obj = JSON.parse(tp);\n          return obj.photoId || \"\";\n        } catch {\n          return \"\";\n        }\n      }\n      return tp;\n    })();\n\n    if (!photoId) return;\n\n    const targetA = editor.pageSlots[pageNumber]?.A || null;\n    const targetB = editor.pageSlots[pageNumber]?.B || null;\n    const targetRotA = editor.pageRot?.[pageNumber]?.A || 0;\n    const targetRotB = editor.pageRot?.[pageNumber]?.B || 0;\n\n    const used = getUsedPhotoIds();\n    const isReusedPhoto = payload?.type === \"photo\" && used.has(photoId);\n    if (isReusedPhoto) showReuseNotice();\n\n    const rect = bodyEl.getBoundingClientRect();\n    const y = (typeof clientY === \"number\" ? clientY : rect.top) - rect.top;\n    const mid = rect.height / 2;\n    const droppedOnTop = y < mid;\n\n    pushUndoState();\n\n    if (payload && payload.type === \"placed\") {\n      const fromPage = Number(payload.fromPage);\n      const fromSlot = payload.fromSlot;\n\n      if (fromPage && fromSlot && editor.pageSlots[fromPage]) {\n        if (\n          !(\n            (fromPage === pageNumber && fromSlot === \"A\" && targetA === photoId && !targetB) ||\n            (fromPage === pageNumber && fromSlot === \"B\" && targetB === photoId)\n          )\n        ) {\n          editor.pageSlots[fromPage][fromSlot] = null;\n          editor.pageRot[fromPage][fromSlot] = 0;\n\n          if (!editor.pageSlots[fromPage].A && editor.pageSlots[fromPage].B) {\n            editor.pageSlots[fromPage].A = editor.pageSlots[fromPage].B;\n            editor.pageSlots[fromPage].B = null;\n            editor.pageRot[fromPage].A = editor.pageRot[fromPage].B;\n            editor.pageRot[fromPage].B = 0;\n          }\n        }\n      }\n    }\n\n    if (!targetA && !targetB) {\n      editor.pageSlots[pageNumber].A = photoId;\n      editor.pageSlots[pageNumber].B = null;\n      editor.pageRot[pageNumber].A = 0;\n      editor.pageRot[pageNumber].B = 0;\n\n      renderSpread();\n      renderThumbsRail();\n      renderPhotoTray();\n      return;\n    }\n\n    if (targetA && !targetB) {\n      const old = targetA;\n      const oldRot = targetRotA;\n\n      if (droppedOnTop) {\n        editor.pageSlots[pageNumber].A = photoId;\n        editor.pageSlots[pageNumber].B = old;\n        editor.pageRot[pageNumber].A = 0;\n        editor.pageRot[pageNumber].B = oldRot;\n      } else {\n        editor.pageSlots[pageNumber].A = old;\n        editor.pageSlots[pageNumber].B = photoId;\n        editor.pageRot[pageNumber].A = oldRot;\n        editor.pageRot[pageNumber].B = 0;\n      }\n\n      renderSpread();\n      renderThumbsRail();\n      renderPhotoTray();\n      return;\n    }\n\n    if (targetA && targetB) {\n      const replaceSlot = droppedOnTop ? \"A\" : \"B\";\n      const replaced = editor.pageSlots[pageNumber][replaceSlot];\n      const replacedRot = editor.pageRot[pageNumber][replaceSlot] || 0;\n\n      editor.pageSlots[pageNumber][replaceSlot] = photoId;\n      editor.pageRot[pageNumber][replaceSlot] = 0;\n\n      if (payload && payload.type === \"placed\") {\n        const fromPage = Number(payload.fromPage);\n        const fromSlot = payload.fromSlot;\n\n        if (replaced && fromPage && fromSlot && editor.pageSlots[fromPage]) {\n          editor.pageSlots[fromPage][fromSlot] = replaced;\n          editor.pageRot[fromPage][fromSlot] = replacedRot;\n\n          if (!editor.pageSlots[fromPage].A && editor.pageSlots[fromPage].B) {\n            editor.pageSlots[fromPage].A = editor.pageSlots[fromPage].B;\n            editor.pageSlots[fromPage].B = null;\n            editor.pageRot[fromPage].A = editor.pageRot[fromPage].B;\n            editor.pageRot[fromPage].B = 0;\n          }\n        }\n      }\n\n      renderSpread();\n      renderThumbsRail();\n      renderPhotoTray();\n    }\n  }\n\n  // ---------- Non-destructive crop model ----------\n  // Cropping never overwrites a source photo. Instead it makes a DERIVED photo (id =\n  // \"<sourceId>__dmbCropOf__<unique>\") holding the cropped bytes, keeps the original in\n  // the tray untouched, and points ONLY that one placement (page slot, free frame, cover\n  // zone, layout frame) at the derived photo. The id itself encodes the source, so the\n  // relationship round-trips through save/restore with no schema change, and Reset simply\n  // points the placement back at the source and drops the derived photo.\n  const _DMB_CROP_TAG = \"__dmbCropOf__\";\n  function _dmbIsCropDerived(ph) {\n    return !!(ph && typeof ph.id === \"string\" && ph.id.indexOf(_DMB_CROP_TAG) !== -1);\n  }\n  function _dmbCropSourceId(id) {\n    if (typeof id !== \"string\") return id;\n    const i = id.indexOf(_DMB_CROP_TAG);\n    return i === -1 ? id : id.slice(0, i);\n  }\n  // Remove a derived crop photo from editor.photos IF nothing else still references it.\n  function _dmbGcDerivedPhoto(derivedId) {\n    if (!derivedId || derivedId.indexOf(_DMB_CROP_TAG) === -1) return;\n    let stillUsed = false;\n    try { stillUsed = getUsedPhotoIds().has(derivedId); } catch (e) { stillUsed = true; }\n    // Cover main/bleed zones aren't in getUsedPhotoIds \u2014 check them explicitly.\n    try {\n      [\"frontImageId\", \"backImageId\", \"bleedImageId\", \"spineImageId\", \"imageId\"].forEach((k) => {\n        if (coverState && coverState[k] === derivedId) stillUsed = true;\n      });\n    } catch (e) {}\n    if (stillUsed) return;\n    const idx = editor.photos.findIndex((p) => p && p.id === derivedId);\n    if (idx !== -1) {\n      try { URL.revokeObjectURL(editor.photos[idx].url); } catch (e) {}\n      editor.photos.splice(idx, 1);\n    }\n    try { if (editor.cropRects) delete editor.cropRects[derivedId]; } catch (e) {}\n  }\n\n  // ---------- Crop Modal ----------\n  let DMB_CROP = {\n    open: false,\n    photoId: null,      // the SOURCE photo id being cropped (always the original)\n    placement: null,    // { get:()=>currentId, set:(id)=>{...} } \u2014 where the crop is written\n    currentId: null,    // the id the placement held when the modal opened (source or derived)\n    img: null,\n    canvas: null,\n    ctx: null,\n    rect: null,\n    dragMode: null,\n    start: null,\n    imgDraw: null\n  };\n\n  function ensureCropModal() {\n    if (document.getElementById(\"dmbCropOverlay\")) return;\n\n    const overlay = document.createElement(\"div\");\n    overlay.id = \"dmbCropOverlay\";\n    overlay.style.position = \"absolute\";\n    overlay.style.inset = \"0\";\n    overlay.style.background = \"rgba(0,0,0,0.65)\";\n    overlay.style.display = \"none\";\n    overlay.style.alignItems = \"center\";\n    overlay.style.justifyContent = \"center\";\n    overlay.style.zIndex = \"9999\";\n\n    const panel = document.createElement(\"div\");\n    panel.style.width = \"min(92vw, 900px)\";\n    panel.style.maxHeight = \"90vh\";\n    panel.style.background = \"#fff\";\n    panel.style.borderRadius = \"16px\";\n    panel.style.overflow = \"hidden\";\n    panel.style.boxShadow = \"0 20px 60px rgba(0,0,0,0.35)\";\n    panel.style.display = \"flex\";\n    panel.style.flexDirection = \"column\";\n\n    const head = document.createElement(\"div\");\n    head.style.display = \"flex\";\n    head.style.alignItems = \"center\";\n    head.style.justifyContent = \"space-between\";\n    head.style.padding = \"12px 14px\";\n    head.style.borderBottom = \"1px solid #eee\";\n\n    const title = document.createElement(\"div\");\n    title.textContent = \"Crop photo\";\n    title.style.fontWeight = \"900\";\n    title.style.fontSize = \"14px\";\n\n    const hint = document.createElement(\"div\");\n    hint.textContent = \"Drag to draw a crop box. Drag inside the box to move it.\";\n    hint.style.fontSize = \"12px\";\n    hint.style.color = \"#666\";\n    hint.style.fontWeight = \"700\";\n    hint.style.marginLeft = \"10px\";\n\n    const left = document.createElement(\"div\");\n    left.style.display = \"flex\";\n    left.style.alignItems = \"center\";\n    left.appendChild(title);\n    left.appendChild(hint);\n\n    const actions = document.createElement(\"div\");\n    actions.style.display = \"flex\";\n    actions.style.gap = \"10px\";\n\n    const mkBtn = (txt) => {\n      const b = document.createElement(\"button\");\n      b.type = \"button\";\n      b.textContent = txt;\n      b.style.border = \"1px solid #111\";\n      b.style.borderRadius = \"999px\";\n      b.style.padding = \"8px 12px\";\n      b.style.fontWeight = \"900\";\n      b.style.cursor = \"pointer\";\n      b.style.background = \"#fff\";\n      return b;\n    };\n\n    const cancelBtn = mkBtn(\"Cancel\");\n    const resetOrigBtn = mkBtn(\"Reset to original\");   // revert this placement's crop\n    resetOrigBtn.id = \"dmbCropResetOrig\";\n    resetOrigBtn.style.borderColor = \"#c0392b\";\n    resetOrigBtn.style.color = \"#c0392b\";\n    resetOrigBtn.style.display = \"none\";               // shown only when a crop is applied\n    const resetBtn = mkBtn(\"Clear box\");               // clears the drawn selection\n    const doneBtn = mkBtn(\"Done\");\n    doneBtn.style.background = \"#111\";\n    doneBtn.style.color = \"#fff\";\n\n    actions.appendChild(cancelBtn);\n    actions.appendChild(resetOrigBtn);\n    actions.appendChild(resetBtn);\n    actions.appendChild(doneBtn);\n\n    head.appendChild(left);\n    head.appendChild(actions);\n\n    const body = document.createElement(\"div\");\n    body.style.padding = \"12px\";\n    body.style.background = \"#fafafa\";\n    body.style.display = \"flex\";\n    body.style.justifyContent = \"center\";\n    body.style.alignItems = \"center\";\n\n    const canvas = document.createElement(\"canvas\");\n    canvas.id = \"dmbCropCanvas\";\n    canvas.style.background = \"#000\";\n    canvas.style.borderRadius = \"12px\";\n    canvas.style.maxWidth = \"100%\";\n    canvas.style.maxHeight = \"70vh\";\n    canvas.style.touchAction = \"none\";\n\n    body.appendChild(canvas);\n\n    panel.appendChild(head);\n    panel.appendChild(body);\n    overlay.appendChild(panel);\n\n    const root = document.getElementById(\"dmb-root\");\n    if (root) {\n      const cs = getComputedStyle(root);\n      if (cs.position === \"static\") root.style.position = \"relative\";\n      root.appendChild(overlay);\n    } else {\n      document.body.appendChild(overlay);\n    }\n\n    cancelBtn.addEventListener(\"click\", closeCropModal);\n    resetBtn.addEventListener(\"click\", () => {\n      DMB_CROP.rect = null;\n      drawCropCanvas();\n    });\n    resetOrigBtn.addEventListener(\"click\", resetCropToOriginal);\n    doneBtn.addEventListener(\"click\", applyCrop);\n\n    const onDown = (e) => {\n      if (!DMB_CROP.open) return;\n      const { x, y } = canvasPoint(e);\n      const r = DMB_CROP.rect;\n\n      if (r && x >= r.x && x <= r.x + r.w && y >= r.y && y <= r.y + r.h) {\n        DMB_CROP.dragMode = \"move\";\n        DMB_CROP.start = { x, y, rx: r.x, ry: r.y };\n      } else {\n        DMB_CROP.dragMode = \"new\";\n        DMB_CROP.start = { x, y };\n        DMB_CROP.rect = { x, y, w: 0, h: 0 };\n      }\n      e.preventDefault();\n    };\n\n    const onMove = (e) => {\n      if (!DMB_CROP.open || !DMB_CROP.dragMode) return;\n      const { x, y } = canvasPoint(e);\n      const r = DMB_CROP.rect;\n\n      if (DMB_CROP.dragMode === \"new\" && DMB_CROP.start && r) {\n        r.x = Math.min(DMB_CROP.start.x, x);\n        r.y = Math.min(DMB_CROP.start.y, y);\n        r.w = Math.abs(x - DMB_CROP.start.x);\n        r.h = Math.abs(y - DMB_CROP.start.y);\n      }\n\n      if (DMB_CROP.dragMode === \"move\" && DMB_CROP.start && r) {\n        const dx = x - DMB_CROP.start.x;\n        const dy = y - DMB_CROP.start.y;\n        r.x = DMB_CROP.start.rx + dx;\n        r.y = DMB_CROP.start.ry + dy;\n      }\n\n      clampCropRect();\n      drawCropCanvas();\n      e.preventDefault();\n    };\n\n    const onUp = () => {\n      if (!DMB_CROP.open) return;\n      DMB_CROP.dragMode = null;\n      DMB_CROP.start = null;\n    };\n\n    canvas.addEventListener(\"pointerdown\", onDown);\n    canvas.addEventListener(\"pointermove\", onMove);\n    canvas.addEventListener(\"pointerup\", onUp);\n    canvas.addEventListener(\"pointercancel\", onUp);\n\n    DMB_CROP.canvas = canvas;\n    DMB_CROP.ctx = canvas.getContext(\"2d\");\n  }\n\n  // openCropModal(currentId, placement?)\n  //   currentId  \u2014 the id the placement currently holds (source OR a derived crop).\n  //   placement  \u2014 { get:()=>id, set:(id)=>{\u2026} } describing where to write the result.\n  //                (Optional for safety; without it the crop can be drawn but not applied.)\n  // The modal ALWAYS loads the original source image, so re-cropping starts from the full\n  // photo and \"Reset to original\" is available whenever a crop is currently applied.\n  function openCropModal(currentId, placement) {\n    ensureCropModal();\n\n    const srcId = _dmbCropSourceId(currentId);\n    const src = getPhotoById(srcId) || getPhotoById(currentId);\n    if (!src) return;\n\n    const overlay = document.getElementById(\"dmbCropOverlay\");\n    const canvas = DMB_CROP.canvas;\n\n    DMB_CROP.open = true;\n    DMB_CROP.photoId = srcId;\n    DMB_CROP.currentId = currentId;\n    DMB_CROP.placement = placement || null;\n    DMB_CROP.rect = null;\n\n    // Reset-to-original is only meaningful when the placement currently shows a crop.\n    const resetBtn = document.getElementById(\"dmbCropResetOrig\");\n    if (resetBtn) resetBtn.style.display = (currentId !== srcId) ? \"\" : \"none\";\n\n    const maxW = Math.min(window.innerWidth * 0.92, 900);\n    const maxH = Math.min(window.innerHeight * 0.70, 700);\n    canvas.width = Math.floor(maxW);\n    canvas.height = Math.floor(maxH);\n\n    const img = new Image();\n    img.decoding = \"async\";\n    img.onload = () => {\n      DMB_CROP.img = img;\n      computeImageDraw();\n      drawCropCanvas();\n    };\n    if (!_dmbPrintMasterOn()) {\n      img.src = src.url;                       // legacy path \u2014 unchanged\n    } else {\n      // Print-master flag: crop against the best pixels available \u2014 the ~3500px\n      // R2 master when it exists (so applyCrop bakes at print quality), else the\n      // resident url/file, else the R2 proxy (lazily-restored photo, no local bytes).\n      (async () => {\n        let _u = \"\", _tmp = \"\";\n        try {\n          if (src.id && !_dmbIsCropDerived(src) && !_dmbNoMasterPids.has(src.id)) {\n            let _did = \"\"; try { _did = getCustomerSaveKey().replace(/^dmb_progress_/, \"\"); } catch (e) {}\n            const _mb = await _dmbFetchPhotoBlob(_did, src.id, \"master\");\n            if (_mb && _mb.size) { _tmp = _u = URL.createObjectURL(_mb); }\n            else _dmbNoMasterPids.add(src.id);\n          }\n        } catch (e) {}\n        if (!_u) _u = src.url || \"\";\n        if (!_u && src.file && src.file.size) { try { _tmp = _u = URL.createObjectURL(src.file); } catch (e) {} }\n        if (!_u) { try { _u = (await _dmbRecoverPhotoUrl(src)) || \"\"; } catch (e) {} }\n        if (_tmp) {\n          const _rev = () => { try { URL.revokeObjectURL(_tmp); } catch (e) {} };\n          img.addEventListener(\"load\", _rev, { once: true });\n          img.addEventListener(\"error\", _rev, { once: true });\n        }\n        img.src = _u;\n      })();\n    }\n\n    if (overlay) overlay.style.display = \"flex\";\n  }\n\n  function closeCropModal() {\n    const overlay = document.getElementById(\"dmbCropOverlay\");\n    if (overlay) overlay.style.display = \"none\";\n    DMB_CROP.open = false;\n    DMB_CROP.photoId = null;\n    DMB_CROP.placement = null;\n    DMB_CROP.currentId = null;\n    DMB_CROP.img = null;\n    DMB_CROP.rect = null;\n    DMB_CROP.dragMode = null;\n    DMB_CROP.start = null;\n  }\n\n  function canvasPoint(e) {\n    const rect = DMB_CROP.canvas.getBoundingClientRect();\n    return {\n      x: (e.clientX - rect.left) * (DMB_CROP.canvas.width / rect.width),\n      y: (e.clientY - rect.top) * (DMB_CROP.canvas.height / rect.height)\n    };\n  }\n\n  function computeImageDraw() {\n    const img = DMB_CROP.img;\n    const c = DMB_CROP.canvas;\n    if (!img || !c) return;\n\n    const scale = Math.min(c.width / img.naturalWidth, c.height / img.naturalHeight);\n    const dw = img.naturalWidth * scale;\n    const dh = img.naturalHeight * scale;\n    const ox = (c.width - dw) / 2;\n    const oy = (c.height - dh) / 2;\n\n    DMB_CROP.imgDraw = { scale, ox, oy, dw, dh };\n  }\n\n  function clampCropRect() {\n    const r = DMB_CROP.rect;\n    const d = DMB_CROP.imgDraw;\n    if (!r || !d) return;\n\n    const minSize = 20;\n\n    r.w = Math.max(minSize, r.w);\n    r.h = Math.max(minSize, r.h);\n\n    const minX = d.ox;\n    const minY = d.oy;\n    const maxX = d.ox + d.dw;\n    const maxY = d.oy + d.dh;\n\n    if (r.x < minX) r.x = minX;\n    if (r.y < minY) r.y = minY;\n    if (r.x + r.w > maxX) r.x = maxX - r.w;\n    if (r.y + r.h > maxY) r.y = maxY - r.h;\n\n    r.w = Math.min(r.w, maxX - minX);\n    r.h = Math.min(r.h, maxY - minY);\n  }\n\n  function drawCropCanvas() {\n    const ctx = DMB_CROP.ctx;\n    const c = DMB_CROP.canvas;\n    const img = DMB_CROP.img;\n    const d = DMB_CROP.imgDraw;\n    if (!ctx || !c) return;\n\n    ctx.clearRect(0, 0, c.width, c.height);\n    ctx.fillStyle = \"#000\";\n    ctx.fillRect(0, 0, c.width, c.height);\n\n    if (!img || !d) return;\n\n    ctx.drawImage(img, d.ox, d.oy, d.dw, d.dh);\n\n    ctx.fillStyle = \"rgba(0,0,0,0.45)\";\n    ctx.fillRect(0, 0, c.width, c.height);\n\n    const r = DMB_CROP.rect;\n    if (r) {\n      clampCropRect();\n\n      ctx.save();\n      ctx.globalCompositeOperation = \"destination-out\";\n      ctx.fillRect(r.x, r.y, r.w, r.h);\n      ctx.restore();\n\n      ctx.strokeStyle = \"#fff\";\n      ctx.lineWidth = 2;\n      ctx.strokeRect(r.x, r.y, r.w, r.h);\n    }\n  }\n\n  // Re-render + save after a crop/reset changes a placement.\n  function _dmbCropAfter() {\n    const pl = DMB_CROP.placement;\n    if (pl && typeof pl.after === \"function\") { try { pl.after(); } catch (e) {} }\n    else {\n      if (builderMode === \"cover\") { try { saveCoverDesignToStorage(); } catch (e) {} renderCoverCanvas(); }\n      else { try { renderSpread(); } catch (e) {} }\n      try { renderThumbsRail(); } catch (e) {}\n      try { renderPhotoTray(); } catch (e) {}\n    }\n    // Persist the new derived-photo bytes + the updated layout so the crop (and Reset)\n    // survive a reload. A fresh derived id has no dedup conflict, so a normal save is enough.\n    try { _dmbPersistPhotos._sig = null; } catch (e) {}\n    try { if (typeof _dmbPersistPhotos === \"function\") _dmbPersistPhotos(); } catch (e) {}\n    try { if (typeof _dmbBackupPhotos === \"function\") _dmbBackupPhotos(); } catch (e) {}\n    try { if (typeof _dmbCloudPersist === \"function\") _dmbCloudPersist(); } catch (e) {}\n  }\n\n  async function applyCrop() {\n    const img = DMB_CROP.img;\n    const d = DMB_CROP.imgDraw;\n    const r = DMB_CROP.rect;\n    const srcId = DMB_CROP.photoId;\n    const placement = DMB_CROP.placement;\n\n    if (!img || !d || !r || !srcId) return;\n    const src = getPhotoById(srcId);\n    if (!src) return;\n    if (!placement || typeof placement.set !== \"function\") { closeCropModal(); return; }\n\n    const sx = Math.max(0, Math.round((r.x - d.ox) / d.scale));\n    const sy = Math.max(0, Math.round((r.y - d.oy) / d.scale));\n    const sw = Math.max(1, Math.round(r.w / d.scale));\n    const sh = Math.max(1, Math.round(r.h / d.scale));\n\n    const out = document.createElement(\"canvas\");\n    out.width = sw;\n    out.height = sh;\n    const octx = out.getContext(\"2d\");\n    octx.drawImage(img, sx, sy, sw, sh, 0, 0, sw, sh);\n\n    const blob = await new Promise((res) => out.toBlob(res, \"image/jpeg\", 0.92));\n    if (!blob) return;\n\n    try { pushUndoState(\"Crop photo\"); } catch (e) {}\n\n    // Build a DERIVED photo that holds the cropped bytes. The original `src` is untouched\n    // and stays in the tray. The id encodes the source so the link survives save/restore.\n    const derivedId = srcId + _DMB_CROP_TAG + Date.now().toString(36) + Math.random().toString(36).slice(2, 6);\n    const newUrl = URL.createObjectURL(blob);\n    const derived = {\n      id: derivedId,\n      file: new File([blob], src.file?.name || src.filename || \"cropped.jpg\", { type: \"image/jpeg\" }),\n      url: newUrl,\n      ratio: sw / sh,\n      filename: src.filename || \"\",\n      dateTaken: src.dateTaken || \"\",\n      tags: [],\n      addedIndex: (typeof src.addedIndex === \"number\" ? src.addedIndex : editor.photos.length)\n    };\n    editor.photos.push(derived);\n\n    // Record the crop as 0\u20131 ratios of the SOURCE image. Resolution-independent:\n    // the server render (or a restore on another device) reproduces the exact same\n    // crop from the print master even though it never saw these baked pixels.\n    try {\n      const _iw = img.naturalWidth || 1, _ih = img.naturalHeight || 1;\n      editor.cropRects = editor.cropRects || {};\n      editor.cropRects[derivedId] = { srcPid: srcId, x: sx / _iw, y: sy / _ih, w: sw / _iw, h: sh / _ih };\n    } catch (e) {}\n\n    const oldId = DMB_CROP.currentId;   // may be a previous derived crop to replace\n    placement.set(derivedId);\n    if (oldId && oldId !== derivedId && oldId.indexOf(_DMB_CROP_TAG) !== -1) _dmbGcDerivedPhoto(oldId);\n\n    _dmbCropAfter();\n    closeCropModal();\n  }\n\n  // Revert this placement back to its original (uncropped) source photo.\n  function resetCropToOriginal() {\n    const placement = DMB_CROP.placement;\n    const srcId = DMB_CROP.photoId;\n    const oldId = DMB_CROP.currentId;\n    if (!placement || typeof placement.set !== \"function\" || !srcId) { closeCropModal(); return; }\n    try { pushUndoState(\"Reset crop\"); } catch (e) {}\n    placement.set(srcId);\n    if (oldId && oldId !== srcId && oldId.indexOf(_DMB_CROP_TAG) !== -1) _dmbGcDerivedPhoto(oldId);\n    _dmbCropAfter();\n    closeCropModal();\n  }\n\n  function makePlacedImageBox(photoId, pos, pageNumber, slot, isPreview = false, noButtons = false) {\n    const ph = getPhotoById(photoId);\n\n    const box = document.createElement(\"div\");\n    box.style.position = \"absolute\";\n    box.style.left = \"0\";\n    box.style.right = \"0\";\n    // Horizontal placement (used for side-by-side 2-up in landscape). When a\n    // width is given with one pinned edge, release the opposite edge so width\n    // takes effect. Numbers \u2192 px; strings (e.g. \"50%\") used as-is.\n    if (pos.left  != null) box.style.left  = (typeof pos.left  === \"number\" ? pos.left  + \"px\" : pos.left);\n    if (pos.right != null) box.style.right = (typeof pos.right === \"number\" ? pos.right + \"px\" : pos.right);\n    if (pos.width != null) {\n      box.style.width = pos.width;\n      if (pos.left  != null) box.style.right = \"auto\";\n      if (pos.right != null) box.style.left  = \"auto\";\n    }\n    if (pos.top != null) box.style.top = (typeof pos.top === \"number\" ? pos.top + \"px\" : pos.top);\n    if (pos.bottom != null) box.style.bottom = (typeof pos.bottom === \"number\" ? pos.bottom + \"px\" : pos.bottom);\n    if (pos.height != null) box.style.height = pos.height;\n    box.style.zIndex = \"1\";\n    box.style.overflow = \"hidden\";\n    box.style.background = \"#fff\";\n    box.style.cursor = \"grab\";\n    box.draggable = true;\n\n    box.addEventListener(\"dragover\", (e) => {\n      e.preventDefault();\n      const payload = getDragPayload(e);\n      e.dataTransfer.dropEffect = (payload && payload.type === \"placed\") ? \"move\" : \"copy\";\n    });\n\n    box.addEventListener(\"drop\", (e) => {\n      e.preventDefault();\n      const parent = box.parentElement;\n      if (!parent) return;\n      const payload = getDragPayload(e);\n      // Find nearest ancestor with a frameKey (works even when placed inside fwClip)\n      const frameEl = parent.closest ? parent.closest(\"[data-frame-key]\") : null;\n      if (frameEl && frameEl.dataset.frameKey) {\n        e.stopPropagation(); // frame handles it \u2014 don't let pagesRow see this drop\n        const fKey = frameEl.dataset.frameKey;\n        const fPage = Number(frameEl.dataset.framePage) || pageNumber;\n        const photoId = (function() {\n          if (payload && payload.photoId) return payload.photoId;\n          const tp = e.dataTransfer ? (e.dataTransfer.getData(\"text/plain\") || \"\").trim() : \"\";\n          if (!tp) return \"\";\n          if (tp[0] === \"{\") { try { return JSON.parse(tp).photoId || \"\"; } catch(ex) { return \"\"; } }\n          return tp;\n        })();\n        if (!photoId) return;\n        const used = getUsedPhotoIds();\n        if (payload && payload.type === \"photo\" && used.has(photoId)) showReuseNotice();\n        pushUndoState(\"Replace Photo In Frame\");\n        if (payload && payload.type === \"placed\") {\n          const fromPage = Number(payload.fromPage);\n          const fromSlot = payload.fromSlot;\n          if (fromPage && fromSlot) {\n            const currentInTarget = getFramePhotoId(fPage, fKey);\n            const replacedRot = getFrameRotation(fPage, fKey);\n            setFramePhotoId(fPage, fKey, photoId);\n            setFrameRotation(fPage, fKey, 0);\n            if (currentInTarget) {\n              setFramePhotoId(fromPage, fromSlot, currentInTarget);\n              setFrameRotation(fromPage, fromSlot, replacedRot);\n            } else {\n              clearFrame(fromPage, fromSlot);\n            }\n          } else {\n            setFramePhotoId(fPage, fKey, photoId);\n            setFrameRotation(fPage, fKey, 0);\n          }\n        } else {\n          setFramePhotoId(fPage, fKey, photoId);\n          setFrameRotation(fPage, fKey, 0);\n        }\n        renderSpread();\n        renderThumbsRail();\n        renderPhotoTray();\n      }\n      // non-frame drop: let event bubble to pagesRow for zone detection\n    });\n\n    box.addEventListener(\"dragstart\", (e) => {\n      setDragPayload(e, { type: \"placed\", photoId, fromPage: pageNumber, fromSlot: slot });\n    });\n\n    const img = document.createElement(\"img\");\n    _dmbBindPhotoImg(img, ph);\n    img.alt = \"placed\";\n    img.style.position = \"absolute\";\n    img.style.inset = \"0\";\n    img.style.width = \"100%\";\n    img.style.height = \"100%\";\n    img.style.pointerEvents = \"none\";\n    box.appendChild(img);\n\n    const deg = getFrameRotation(pageNumber, slot);\n    applyRotationAndCover(img, box, deg);\n    box.dataset.placedImg = \"1\";\n    box.dataset.placedRot = String(deg);\n    requestAnimationFrame(() => {\n      const latestDeg = getFrameRotation(pageNumber, slot);\n      applyRotationAndCover(img, box, latestDeg);\n      box.dataset.placedRot = String(latestDeg);\n    });\n\n    if (!isPreview && !noButtons) {\n      const rm = document.createElement(\"button\");\n      rm.type = \"button\";\n      rm.innerHTML = `<svg viewBox=\"0 0 14 14\" width=\"13\" height=\"13\" aria-hidden=\"true\"><path d=\"M2 2l10 10M12 2L2 12\" stroke=\"#fff\" stroke-width=\"2.2\" stroke-linecap=\"round\"/></svg>`;\n      rm.setAttribute(\"aria-label\", \"Remove photo\");\n      rm.title = \"Remove photo\";\n      rm.style.position = \"absolute\";\n      rm.style.top = \"8px\";\n      rm.style.right = \"8px\";\n      rm.style.width = \"28px\";\n      rm.style.height = \"28px\";\n      rm.style.borderRadius = \"999px\";\n      rm.style.border = \"none\";\n      rm.style.background = \"rgba(0,0,0,0.72)\";\n      rm.style.color = \"#fff\";\n      rm.style.cursor = \"pointer\";\n      rm.style.zIndex = \"4\";\n      rm.style.display = \"flex\";\n      rm.style.alignItems = \"center\";\n      rm.style.justifyContent = \"center\";\n      rm.style.padding = \"0\";\n\n      rm.addEventListener(\"click\", (e) => {\n        e.preventDefault();\n        e.stopPropagation();\n        pushUndoState(\"Remove Photo\");\n        clearFrame(pageNumber, slot);\n\n        if (!isTemplatePage(pageNumber)) {\n          if (slot === \"A\" && editor.pageSlots[pageNumber].B) {\n            editor.pageSlots[pageNumber].A = editor.pageSlots[pageNumber].B;\n            editor.pageSlots[pageNumber].B = null;\n            editor.pageRot[pageNumber].A = editor.pageRot[pageNumber].B;\n            editor.pageRot[pageNumber].B = 0;\n          }\n        }\n\n        renderBuilderCanvas();\n        renderThumbsRail();\n        renderPhotoTray();\n      });\n\n      box.appendChild(rm);\n\n      const rotBtn = document.createElement(\"button\");\n      rotBtn.type = \"button\";\n      rotBtn.setAttribute(\"aria-label\", \"Rotate 90 degrees anti-clockwise\");\n      rotBtn.title = \"Rotate 90\u00B0 anti-clockwise\";\n      rotBtn.innerHTML = `\n        <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" aria-hidden=\"true\">\n          <path d=\"M12 4a8 8 0 1 0 7.2 4.5\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n          <path d=\"M19.5 4.5v5h-5\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n        </svg>\n      `;\n      rotBtn.style.position = \"absolute\";\n      rotBtn.style.top = \"8px\";\n      rotBtn.style.right = \"40px\";\n      rotBtn.style.width = \"28px\";\n      rotBtn.style.height = \"28px\";\n      rotBtn.style.borderRadius = \"999px\";\n      rotBtn.style.border = \"none\";\n      rotBtn.style.background = \"rgba(0,0,0,0.72)\";\n      rotBtn.style.cursor = \"pointer\";\n      rotBtn.style.zIndex = \"4\";\n      rotBtn.style.display = \"flex\";\n      rotBtn.style.flexDirection = \"column\";\n      rotBtn.style.alignItems = \"center\";\n      rotBtn.style.justifyContent = \"center\";\n      rotBtn.style.gap = \"1px\";\n      rotBtn.style.padding = \"0\";\n\n      rotBtn.addEventListener(\"click\", (e) => {\n        e.preventDefault();\n        e.stopPropagation();\n        pushUndoState(\"Rotate Photo\");\n        rotateFrameCCW90(editor, pageNumber, slot);\n        renderBuilderCanvas();\n        renderThumbsRail();\n      });\n\n      box.appendChild(rotBtn);\n\n      const cropBtn = document.createElement(\"button\");\n      cropBtn.type = \"button\";\n      cropBtn.setAttribute(\"aria-label\", \"Crop photo\");\n      cropBtn.title = \"Crop photo\";\n      cropBtn.innerHTML = `\n        <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" aria-hidden=\"true\">\n          <path d=\"M7 3v11a3 3 0 0 0 3 3h11\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n          <path d=\"M17 21V10a3 3 0 0 0-3-3H3\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n        </svg>\n      `;\n      cropBtn.style.position = \"absolute\";\n      cropBtn.style.top = \"8px\";\n      cropBtn.style.right = \"72px\";\n      cropBtn.style.width = \"28px\";\n      cropBtn.style.height = \"28px\";\n      cropBtn.style.borderRadius = \"999px\";\n      cropBtn.style.border = \"none\";\n      cropBtn.style.background = \"rgba(0,0,0,0.72)\";\n      cropBtn.style.cursor = \"pointer\";\n      cropBtn.style.zIndex = \"4\";\n      cropBtn.style.display = \"flex\";\n      cropBtn.style.alignItems = \"center\";\n      cropBtn.style.justifyContent = \"center\";\n      cropBtn.style.padding = \"0\";\n\n      cropBtn.addEventListener(\"click\", (e) => {\n        e.preventDefault();\n        e.stopPropagation();\n        openCropModal(photoId, {\n          get: () => editor.pageSlots?.[pageNumber]?.[slot],\n          set: (id) => { if (!editor.pageSlots[pageNumber]) editor.pageSlots[pageNumber] = {}; editor.pageSlots[pageNumber][slot] = id; }\n        });\n      });\n\n      box.appendChild(cropBtn);\n\n      function forwardDropToBody(e) {\n        e.preventDefault();\n        e.stopPropagation();\n        const body = box.parentElement;\n        if (!body) return;\n        const payload = getDragPayload(e);\n        handleDropOnPage(pageNumber, e.clientY, payload, e.dataTransfer, body, e.clientX);\n      }\n\n      rm.addEventListener(\"dragover\", (e) => e.preventDefault());\n      rm.addEventListener(\"drop\", forwardDropToBody);\n\n      rotBtn.addEventListener(\"dragover\", (e) => e.preventDefault());\n      rotBtn.addEventListener(\"drop\", forwardDropToBody);\n\n      cropBtn.addEventListener(\"dragover\", (e) => e.preventDefault());\n      cropBtn.addEventListener(\"drop\", forwardDropToBody);\n\n      // Frames button\n      const frameShapeBtn = document.createElement(\"button\");\n      frameShapeBtn.type = \"button\";\n      frameShapeBtn.setAttribute(\"aria-label\", \"Frame shape\");\n      frameShapeBtn.title = \"Frame shape\";\n      frameShapeBtn.innerHTML = `<svg viewBox=\"0 0 22 22\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"#fff\" stroke-width=\"1.8\"><circle cx=\"11\" cy=\"11\" r=\"9\"/><polygon points=\"11,4 13,8.5 18,9 14.5,12.5 15.5,17.5 11,15 6.5,17.5 7.5,12.5 4,9 9,8.5\" fill=\"#fff\" stroke=\"none\" opacity=\"0.7\"/></svg>`;\n      frameShapeBtn.style.cssText = \"position:absolute;top:8px;right:104px;width:28px;height:28px;border-radius:999px;border:none;background:rgba(0,0,0,0.72);cursor:pointer;z-index:4;display:flex;align-items:center;justify-content:center;padding:0;\";\n      frameShapeBtn.addEventListener(\"click\", (e) => {\n        e.preventDefault(); e.stopPropagation();\n        const current = getSlotFrameShape(pageNumber, slot);\n        showDmbFramePicker(frameShapeBtn, current, (shapeId) => {\n          pushUndoState(\"Frame Shape\");\n          setSlotFrameShape(pageNumber, slot, shapeId);\n          renderBuilderCanvas();\n          renderThumbsRail();\n        });\n      });\n      frameShapeBtn.addEventListener(\"dragover\", (e) => e.preventDefault());\n      frameShapeBtn.addEventListener(\"drop\", forwardDropToBody);\n      box.appendChild(frameShapeBtn);\n\n      // Apply stored frame shape to the placed image\n      const _placedImg = box.querySelector(\"img\");\n      if (_placedImg) applyDmbFrameShape(_placedImg, getSlotFrameShape(pageNumber, slot));\n    }\n\n    return box;\n  }\n\n  function makeA4Page(pageNumber, side, isPreview = false) {\n    const page = document.createElement(\"div\");\n    const _pdPage = getPageDims();\n    page.style.width = _pdPage.w + \"px\";\n    page.style.aspectRatio = _pdPage.w + \" / \" + _pdPage.h;\n    page.style.border = \"1px solid #e5e5e5\";\n    page.style.borderRadius = \"0\";\n    page.style.background = \"#fff\";\n    page.style.position = \"relative\";\n    page.style.overflow = \"visible\";\n    page.style.boxShadow = \"0 6px 18px rgba(0,0,0,0.06)\";\n\n    const body = document.createElement(\"div\");\n    body.style.position = \"absolute\";\n    body.style.inset = \"0\";\n    // Use stored page background color if set; fall back to default page colour\n    const _pageBg = editor.pageBackgrounds?.[pageNumber];\n    body.style.background = _pageBg || (isPreview ? \"#ffffff\" : \"#fcfcfc\");\n    body.style.zIndex = \"auto\";\n    body.setAttribute(\"data-page-body\", \"true\");\n    body.dataset.pageNumber = String(pageNumber);\n    page.appendChild(body);\n\n    if (isLockedPage(pageNumber)) {\n      if (!isPreview) {\n        const special = document.createElement(\"div\");\n        special.style.position = \"absolute\";\n        special.style.inset = \"0\";\n        special.style.display = \"flex\";\n        special.style.alignItems = \"center\";\n        special.style.justifyContent = \"center\";\n        special.style.textAlign = \"center\";\n        special.style.padding = \"24px\";\n        special.style.fontWeight = \"900\";\n        special.style.fontSize = \"13px\";\n        special.style.color = \"#bbb\";\n        special.textContent = \"Can't be edited\";\n        body.appendChild(special);\n      }\n      renderFreeFrameObjects(body, { targetType: \"page\", pageNumber: pageNumber });\n      return page;\n    }\n\n    // \u2500\u2500 Spread photo \u2500\u2500\n    const si = Math.floor((pageNumber - 1) / 2);\n    const isLeftPage = (side === \"L\");\n    const sp = editor.spreadPhoto?.[si];\n    if (sp && sp.photoId) {\n      const ph = getPhotoById(sp.photoId);\n      if (ph) {\n        const templateIdForSpread = getPageTemplateId(pageNumber);\n        const imgWrap = document.createElement(\"div\");\n        imgWrap.style.position = \"absolute\";\n        imgWrap.style.width = \"200%\";\n        imgWrap.style.height = \"100%\";\n        imgWrap.style.left = isLeftPage ? \"0\" : \"-100%\";\n        imgWrap.style.top = \"0\";\n        imgWrap.style.transformOrigin = \"50% 50%\";\n        imgWrap.setAttribute(\"data-spread-img-layer\", \"true\");\n        imgWrap.setAttribute(\"data-spread-img-side\", side);\n        const px = sp.panX || 0, py = sp.panY || 0, zoom = sp.zoom || 1, rot = sp.rotation || 0;\n        imgWrap.style.transform = `translate(${px}px,${py}px) scale(${zoom}) rotate(${rot}deg)`;\n        // Store transform values for PDF pre-rendering (avoids html2canvas transform issues)\n        imgWrap.dataset.pdfPanX = String(px);\n        imgWrap.dataset.pdfPanY = String(py);\n        imgWrap.dataset.pdfZoom = String(zoom);\n        imgWrap.dataset.pdfRotation = String(rot);\n        if (templateIdForSpread || isPreview) {\n          imgWrap.style.cursor = \"default\";\n          imgWrap.style.pointerEvents = \"none\";\n          imgWrap.style.zIndex = \"0\";\n        } else {\n          imgWrap.style.cursor = \"move\";\n          imgWrap.style.zIndex = \"1\";\n        }\n        const img = document.createElement(\"img\");\n        _dmbBindPhotoImg(img, ph);\n        img.style.width = \"100%\";\n        img.style.height = \"100%\";\n        img.style.objectFit = \"cover\";\n        img.style.pointerEvents = \"none\";\n        img.style.display = \"block\";\n        img.draggable = false;\n        imgWrap.appendChild(img);\n        body.appendChild(imgWrap);\n        if (!templateIdForSpread && !isPreview) {\n          imgWrap.addEventListener(\"mousedown\", (e) => {\n            if (e.button !== 0) return;\n            spreadPhotoDragState = { type: \"pan\", si, startX: e.clientX, startY: e.clientY, startPanX: sp.panX || 0, startPanY: sp.panY || 0 };\n            e.preventDefault();\n            e.stopPropagation();\n          });\n        }\n        if (!isLeftPage && !isPreview) {\n          // X button (matches regular photos style)\n          const rmBtn = document.createElement(\"button\");\n          rmBtn.type = \"button\";\n          rmBtn.innerHTML = `<svg viewBox=\"0 0 14 14\" width=\"13\" height=\"13\" aria-hidden=\"true\"><path d=\"M2 2l10 10M12 2L2 12\" stroke=\"#fff\" stroke-width=\"2.2\" stroke-linecap=\"round\"/></svg>`;\n          rmBtn.setAttribute(\"aria-label\", \"Remove spread photo\");\n          rmBtn.style.cssText = \"position:absolute;top:8px;right:8px;width:28px;height:28px;border-radius:999px;border:none;background:rgba(0,0,0,0.72);color:#fff;cursor:pointer;z-index:40;display:flex;align-items:center;justify-content:center;padding:0;\";\n          rmBtn.addEventListener(\"click\", (e) => {\n            e.stopPropagation();\n            pushUndoState();\n            editor.spreadPhoto[si] = null;\n            renderSpread(); renderThumbsRail(); renderPhotoTray();\n          });\n          body.appendChild(rmBtn);\n          // Rotate button\n          const rotBtn = document.createElement(\"button\");\n          rotBtn.type = \"button\";\n          rotBtn.setAttribute(\"aria-label\", \"Rotate 90\u00B0\");\n          rotBtn.innerHTML = `<svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" aria-hidden=\"true\"><path d=\"M12 4a8 8 0 1 0 7.2 4.5\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\"/><path d=\"M19.5 4.5v5h-5\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>`;\n          rotBtn.style.cssText = \"position:absolute;top:8px;right:40px;width:28px;height:28px;border-radius:999px;border:none;background:rgba(0,0,0,0.72);cursor:pointer;z-index:40;display:flex;align-items:center;justify-content:center;padding:0;\";\n          rotBtn.addEventListener(\"click\", (e) => {\n            e.preventDefault(); e.stopPropagation();\n            sp.rotation = ((sp.rotation || 0) + 90) % 360;\n            renderSpread(); renderThumbsRail();\n          });\n          body.appendChild(rotBtn);\n          // Crop button\n          const cropBtn = document.createElement(\"button\");\n          cropBtn.type = \"button\";\n          cropBtn.setAttribute(\"aria-label\", \"Crop photo\");\n          cropBtn.innerHTML = `<svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" aria-hidden=\"true\"><path d=\"M7 3v11a3 3 0 0 0 3 3h11\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\"/><path d=\"M17 21V10a3 3 0 0 0-3-3H3\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\"/></svg>`;\n          cropBtn.style.cssText = \"position:absolute;top:8px;right:72px;width:28px;height:28px;border-radius:999px;border:none;background:rgba(0,0,0,0.72);cursor:pointer;z-index:40;display:flex;align-items:center;justify-content:center;padding:0;\";\n          cropBtn.addEventListener(\"click\", (e) => {\n            e.preventDefault(); e.stopPropagation();\n            openCropModal(sp.photoId, { get: () => sp.photoId, set: (id) => { sp.photoId = id; } });\n          });\n          body.appendChild(cropBtn);\n        }\n        if (!templateIdForSpread) {\n          renderFreeFrameObjects(body, { targetType: \"page\", pageNumber });\n          return page;\n        }\n        // Has template: fall through to render template frames on top of spread photo\n      }\n    }\n\n    const msg = document.createElement(\"div\");\n    msg.style.position = \"absolute\";\n    msg.style.left = \"12px\";\n    msg.style.right = \"12px\";\n    msg.style.bottom = \"12px\";\n    msg.style.padding = \"10px 12px\";\n    msg.style.borderRadius = \"12px\";\n    msg.style.background = \"rgba(0,0,0,0.70)\";\n    msg.style.color = \"#fff\";\n    msg.style.fontWeight = \"900\";\n    msg.style.fontSize = \"12px\";\n    msg.style.display = \"none\";\n    msg.style.zIndex = \"4\";\n    msg.textContent = \"To add another picture to this page, drag another picture over it.\";\n    msg.style.pointerEvents = \"none\";\n    if (!isPreview) body.appendChild(msg);\n\n    const templateIdForBody = getPageTemplateId(pageNumber);\n    if (templateIdForBody) {\n      const defs = TEMPLATE_DEFS[templateIdForBody] || [];\n      const hasAny = Object.values(editor.pageFrames?.[pageNumber] || {}).some(Boolean);\n\n      if (!hasAny && !isPreview) body.appendChild(makeHint(\"Choose a frame, then drop a photo\"));\n\n      // Render slot photos as background (when layout applied \"over\" existing photos)\n      const slotA = editor.pageSlots?.[pageNumber]?.A;\n      const slotB = editor.pageSlots?.[pageNumber]?.B;\n      if (slotA || slotB) {\n        if (slotA && !slotB) {\n          const bg = makePlacedImageBox(slotA, { top: 0, bottom: 0 }, pageNumber, \"A\", isPreview);\n          bg.style.zIndex = \"0\";\n          bg.style.pointerEvents = \"none\";\n          body.appendChild(bg);\n        } else if (slotA && slotB) {\n          // Layout background stays as designed (stacked) \u2014 the layout itself is\n          // kept at its portrait shape, so its background matches.\n          const bgA = makePlacedImageBox(slotA, { top: 0, height: \"50%\" }, pageNumber, \"A\", isPreview);\n          bgA.style.zIndex = \"0\"; bgA.style.pointerEvents = \"none\";\n          const bgB = makePlacedImageBox(slotB, { bottom: 0, height: \"50%\" }, pageNumber, \"B\", isPreview);\n          bgB.style.zIndex = \"0\"; bgB.style.pointerEvents = \"none\";\n          body.appendChild(bgA);\n          body.appendChild(bgB);\n        }\n      }\n\n      defs.forEach((f) => {\n        const _fp = _dmbLayoutFramePct(f);   // keep the frame's exact portrait shape\n        const frameWrap = document.createElement(\"div\");\n        frameWrap.style.position = \"absolute\";\n        frameWrap.style.left = _fp.x + \"%\";\n        frameWrap.style.top = _fp.y + \"%\";\n        frameWrap.style.width = _fp.w + \"%\";\n        frameWrap.style.height = _fp.h + \"%\";\n        frameWrap.style.border = \"1px solid #eee\";\n        frameWrap.style.background = \"#fff\";\n        frameWrap.style.overflow = \"visible\";  // buttons escape; inner clip handles photo\n        frameWrap.style.zIndex = \"60\";  // above spread overlay (z-index:50) so drops land on frame\n        frameWrap.dataset.frameKey = f.key;\n        frameWrap.dataset.framePage = String(pageNumber);\n\n        const fwClip = document.createElement(\"div\");\n        fwClip.style.cssText = \"position:absolute;inset:0;overflow:hidden;border-radius:inherit;\";\n        frameWrap.appendChild(fwClip);\n\n        const pid = getFramePhotoId(pageNumber, f.key);\n\n        if (!pid && !isPreview) {\n          const hint = document.createElement(\"div\");\n          hint.style.cssText = \"position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;pointer-events:none;background:rgba(220,220,220,0.55);border:1.5px dashed #ccc;border-radius:2px;\";\n          hint.innerHTML = `<svg width=\"26\" height=\"26\" viewBox=\"0 0 26 26\" fill=\"none\"><circle cx=\"13\" cy=\"13\" r=\"11\" stroke=\"#c0c0c0\" stroke-width=\"1.5\"/><line x1=\"13\" y1=\"7.5\" x2=\"13\" y2=\"18.5\" stroke=\"#bbb\" stroke-width=\"2\" stroke-linecap=\"round\"/><line x1=\"7.5\" y1=\"13\" x2=\"18.5\" y2=\"13\" stroke=\"#bbb\" stroke-width=\"2\" stroke-linecap=\"round\"/></svg><span style=\"font-size:8px;font-weight:900;color:#bbb;letter-spacing:0.4px;text-transform:uppercase;\">Drop photo</span>`;\n          fwClip.appendChild(hint);\n        } else if (pid) {\n          const placed = makePlacedImageBox(pid, { top: 0, bottom: 0 }, pageNumber, f.key, isPreview, true);\n          placed.style.cssText += \";position:absolute;left:0;right:0;top:0;bottom:0;height:auto;\";\n          fwClip.appendChild(placed);\n          // Re-apply stored frame shape after image loads\n          const _storedShape = editor.pageFrameShapes?.[pageNumber]?.[f.key] || '';\n          if (_storedShape) {\n            const _si = placed.querySelector('img');\n            if (_si) applyDmbFrameShape(_si, _storedShape);\n          }\n\n          if (!isPreview) {\n            const _tBtnSVGs = [\n              { aria:\"Remove\", title:\"Remove photo\", svg:`<svg viewBox=\"0 0 14 14\" width=\"12\" height=\"12\" aria-hidden=\"true\"><path d=\"M2 2l10 10M12 2L2 12\" stroke=\"#fff\" stroke-width=\"2.2\" stroke-linecap=\"round\"/></svg>`, fn:(e)=>{ e.preventDefault();e.stopPropagation();pushUndoState(\"Remove Photo\");clearFrame(pageNumber,f.key);renderSpread();renderThumbsRail();renderPhotoTray(); }},\n              { aria:\"Rotate\", title:\"Rotate 90\u00B0 anti-clockwise\", svg:`<svg viewBox=\"0 0 24 24\" width=\"12\" height=\"12\" aria-hidden=\"true\"><path d=\"M12 4a8 8 0 1 0 7.2 4.5\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\"/><path d=\"M19.5 4.5v5h-5\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>`, fn:(e)=>{ e.preventDefault();e.stopPropagation();pushUndoState(\"Rotate Photo\");const rots=editor.pageFrameRot?.[pageNumber]||{};rots[f.key]=((rots[f.key]||0)-90+360)%360;if(!editor.pageFrameRot)editor.pageFrameRot={};editor.pageFrameRot[pageNumber]=rots;renderSpread();renderThumbsRail(); }},\n              { aria:\"Crop\",   title:\"Crop photo\",    svg:`<svg viewBox=\"0 0 24 24\" width=\"12\" height=\"12\" aria-hidden=\"true\"><path d=\"M7 3v11a3 3 0 0 0 3 3h11\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\"/><path d=\"M17 21V10a3 3 0 0 0-3-3H3\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\"/></svg>`, fn:(e)=>{ e.preventDefault();e.stopPropagation();openCropModal(pid, { get:()=>editor.pageFrames?.[pageNumber]?.[f.key], set:(id)=>{ if(!editor.pageFrames[pageNumber]) editor.pageFrames[pageNumber]={}; editor.pageFrames[pageNumber][f.key]=id; } }); }},\n              { aria:\"Shape\",  title:\"Frame shape\",   svg:`<svg viewBox=\"0 0 22 22\" width=\"12\" height=\"12\" fill=\"none\" stroke=\"#fff\" stroke-width=\"1.8\"><circle cx=\"11\" cy=\"11\" r=\"9\"/><polygon points=\"11,4 13,8.5 18,9 14.5,12.5 15.5,17.5 11,15 6.5,17.5 7.5,12.5 4,9 9,8.5\" fill=\"#fff\" stroke=\"none\" opacity=\"0.7\"/></svg>`, fn:(e)=>{ e.preventDefault();e.stopPropagation(); if(!editor.pageFrameShapes)editor.pageFrameShapes={}; if(!editor.pageFrameShapes[pageNumber])editor.pageFrameShapes[pageNumber]={}; const curShape=editor.pageFrameShapes[pageNumber][f.key]||''; showDmbFramePicker(e.currentTarget, curShape, (shapeId)=>{ pushUndoState(\"Frame Shape\"); if(!editor.pageFrameShapes)editor.pageFrameShapes={}; if(!editor.pageFrameShapes[pageNumber])editor.pageFrameShapes[pageNumber]={}; editor.pageFrameShapes[pageNumber][f.key]=shapeId; const _img=fwClip.querySelector('img'); if(_img)applyDmbFrameShape(_img,shapeId); renderSpread(); }); }}\n            ];\n            const _tbs = \"border-radius:999px;border:none;background:rgba(0,0,0,0.72);color:#fff;cursor:pointer;display:flex;align-items:center;justify-content:center;padding:0;\";\n            // Always use \u00B7\u00B7\u00B7 popup for layout frames (clean at any size)\n            const mb=document.createElement(\"button\");mb.type=\"button\";mb.title=\"Options\";\n            mb.innerHTML=\"\u00B7\u00B7\u00B7\";\n            mb.style.cssText=`position:absolute;top:3px;right:3px;width:22px;height:22px;z-index:12;font-size:14px;line-height:1;${_tbs}`;\n            const pop=document.createElement(\"div\");\n            pop.style.cssText=\"position:absolute;top:26px;right:0;background:rgba(20,20,20,0.92);border-radius:8px;padding:3px;display:none;flex-direction:column;gap:2px;z-index:20;min-width:110px;\";\n            _tBtnSVGs.forEach(d=>{\n              const b=document.createElement(\"button\");b.type=\"button\";\n              b.innerHTML=`<span style=\"display:flex;align-items:center;gap:6px;\">${d.svg}<span style=\"font-size:11px;font-weight:700;color:#fff;\">${d.aria}</span></span>`;\n              b.style.cssText=\"background:none;border:none;cursor:pointer;padding:4px 8px;border-radius:6px;width:100%;text-align:left;\";\n              b.addEventListener(\"click\",(ev)=>{pop.style.display=\"none\";d.fn(ev);});\n              pop.appendChild(b);\n            });\n            mb.addEventListener(\"click\",(e)=>{e.preventDefault();e.stopPropagation(); const _open=pop.style.display===\"none\"; document.querySelectorAll(\".dmb-frame-popup-open\").forEach(p=>{p.style.display=\"none\";p.classList.remove(\"dmb-frame-popup-open\");}); if(_open){pop.style.display=\"flex\";pop.classList.add(\"dmb-frame-popup-open\");}});\n            frameWrap.appendChild(mb);frameWrap.appendChild(pop);\n          }\n        }\n\n        frameWrap.addEventListener(\"dragover\", (e) => {\n          e.preventDefault();\n          e.stopPropagation();\n          const payload = getDragPayload(e);\n          e.dataTransfer.dropEffect = (payload && payload.type === \"placed\") ? \"move\" : \"copy\";\n          frameWrap.style.outline = \"2px solid #111\";\n          frameWrap.style.outlineOffset = \"-2px\";\n          body.style.outline = \"none\";\n        });\n\n        frameWrap.addEventListener(\"dragleave\", () => {\n          frameWrap.style.outline = \"none\";\n        });\n\n        frameWrap.addEventListener(\"drop\", (e) => {\n          e.preventDefault();\n          e.stopPropagation();\n          frameWrap.style.outline = \"none\";\n          body.style.outline = \"none\";\n          const payload = getDragPayload(e);\n          const photoId = (() => {\n            if (payload && payload.photoId) return payload.photoId;\n            const tp = e.dataTransfer ? (e.dataTransfer.getData(\"text/plain\") || \"\").trim() : \"\";\n            if (!tp) return \"\";\n            if (tp[0] === \"{\") { try { return JSON.parse(tp).photoId || \"\"; } catch { return \"\"; } }\n            return tp;\n          })();\n          if (!photoId) return;\n          const used = getUsedPhotoIds();\n          if (payload?.type === \"photo\" && used.has(photoId)) showReuseNotice();\n          pushUndoState(\"Drop Photo In Frame\");\n          if (payload && payload.type === \"placed\") {\n            const fromPage = Number(payload.fromPage);\n            const fromSlot = payload.fromSlot;\n            if (fromPage && fromSlot) {\n              const currentInTarget = getFramePhotoId(pageNumber, f.key);\n              const replacedRot = getFrameRotation(pageNumber, f.key);\n              setFramePhotoId(pageNumber, f.key, photoId);\n              setFrameRotation(pageNumber, f.key, 0);\n              if (currentInTarget) {\n                setFramePhotoId(fromPage, fromSlot, currentInTarget);\n                setFrameRotation(fromPage, fromSlot, replacedRot);\n              } else {\n                clearFrame(fromPage, fromSlot);\n              }\n            } else {\n              setFramePhotoId(pageNumber, f.key, photoId);\n              setFrameRotation(pageNumber, f.key, 0);\n            }\n          } else {\n            setFramePhotoId(pageNumber, f.key, photoId);\n            setFrameRotation(pageNumber, f.key, 0);\n          }\n          renderSpread();\n          renderThumbsRail();\n          renderPhotoTray();\n        });\n\n        body.appendChild(frameWrap);\n      });\n\n      renderFreeFrameObjects(body, { targetType: \"page\", pageNumber: pageNumber });\n      return page;\n    }\n\n    if (!editor.pageSlots[pageNumber]) {\n      editor.pageSlots[pageNumber] = { A: null, B: null };\n    }\n    if (!editor.pageRot[pageNumber]) {\n      editor.pageRot[pageNumber] = { A: 0, B: 0 };\n    }\n    if (!editor.pageTemplate[pageNumber]) {\n      editor.pageTemplate[pageNumber] = null;\n    }\n    if (!editor.pageFrames[pageNumber]) {\n      editor.pageFrames[pageNumber] = {};\n    }\n    if (!editor.pageFrameRot[pageNumber]) {\n      editor.pageFrameRot[pageNumber] = {};\n    }\n\n\n    const A = editor.pageSlots[pageNumber].A;\n    const B = editor.pageSlots[pageNumber].B;\n\n    if (!A && !B) {\n      // Only show the dashed \"Drop a photo here\" placeholder when the page is\n      // TRULY empty \u2014 not when it already holds a free-frame photo, a spread\n      // photo, text, a sticker, or a background. (Slots being empty isn't enough:\n      // the magic layout + \"Choose layout\" place photos as free frames, which left\n      // this dashed box showing behind real content.)\n      if (!isPreview && pageIsCompletelyEmpty(pageNumber)) body.appendChild(makeHint(\"Drop a photo here\"));\n    } else if (A && !B) {\n      body.appendChild(makePlacedImageBox(A, { top: 0, bottom: 0 }, pageNumber, \"A\", isPreview));\n      if (!isPreview) {\n        msg.style.display = \"block\";\n        // Auto-dismiss after 4 seconds\n        setTimeout(() => { msg.style.opacity = \"0\"; setTimeout(() => { msg.style.display = \"none\"; }, 400); }, 4000);\n      }\n    } else {\n      const _2 = _dmbTwoUpPlacement();\n      body.appendChild(makePlacedImageBox(A, _2.a, pageNumber, \"A\", isPreview));\n      body.appendChild(makePlacedImageBox(B, _2.b, pageNumber, \"B\", isPreview));\n\n      // Divider between the two photos: horizontal (mid-height) in portrait,\n      // vertical (mid-width) in landscape.\n      const divider = document.createElement(\"div\");\n      divider.style.position = \"absolute\";\n      divider.style.background = \"#eee\";\n      divider.style.zIndex = \"2\";\n      divider.style.pointerEvents = \"none\";\n      if (dmbIsLandscape()) {\n        divider.style.top = \"0\"; divider.style.bottom = \"0\";\n        divider.style.left = \"50%\"; divider.style.width = \"1px\";\n      } else {\n        divider.style.left = \"0\"; divider.style.right = \"0\";\n        divider.style.top = \"50%\"; divider.style.height = \"1px\";\n      }\n      body.appendChild(divider);\n    }\n\n    \n    body.addEventListener(\"dragover\", (e) => {\n      e.preventDefault();\n      const payload = getDragPayload(e);\n      e.dataTransfer.dropEffect = (payload && payload.type === \"placed\") ? \"move\" : \"copy\";\n      body.style.outline = \"2px solid #111\";\n      body.style.outlineOffset = \"-2px\";\n    });\n\n    body.addEventListener(\"dragleave\", () => {\n      body.style.outline = \"none\";\n    });\n\n    body.addEventListener(\"drop\", (e) => {\n      e.preventDefault();\n      e.stopPropagation();\n      body.style.outline = \"none\";\n      const payload = getDragPayload(e);\n      handleDropOnPage(pageNumber, e.clientY, payload, e.dataTransfer, body);\n    });\n\n    page.addEventListener(\"dragover\", (e) => {\n      if (isLockedPage(pageNumber)) return;\n      e.preventDefault();\n      const payload = getDragPayload(e);\n      e.dataTransfer.dropEffect = (payload && payload.type === \"placed\") ? \"move\" : \"copy\";\n    });\n\n    page.addEventListener(\"drop\", (e) => {\n      if (isLockedPage(pageNumber)) return;\n      e.preventDefault();\n      const payload = getDragPayload(e);\n      handleDropOnPage(pageNumber, e.clientY, payload, e.dataTransfer, body);\n    });\n    renderFreeFrameObjects(body, { targetType: \"page\", pageNumber: pageNumber });\n    return page;\n  }\n\n  function renderUnifiedCoverObjects(stageEl) {\n    const objs = (editor.coverObjects || []).slice().sort((a, b) => (a.z || 0) - (b.z || 0));\n\n    objs.forEach((obj) => {\n      if (obj.type === \"text\") {\n        // render text object\n      }\n\n      if (obj.type === \"photo-frame\") {\n        // render frame object\n      }\n    });\n  }\n\n  function forceSpreadLayout() {\n    if (!spreadViewport || !spreadCanvas) return;\n\n    spreadViewport.style.overflowX = \"auto\";\n    spreadViewport.style.overflowY = \"auto\";\n    spreadViewport.style.padding = \"12px\";\n    spreadViewport.style.minHeight = \"0\";       // fit the canvas \u2014 no forced empty space below it\n    spreadViewport.style.height = \"auto\";\n    spreadViewport.style.maxHeight = \"760px\";   // large spreads still scroll inside this cap\n    spreadViewport.style.boxSizing = \"border-box\";\n\n    spreadCanvas.style.display = \"flex\";\n    spreadCanvas.style.flexWrap = \"nowrap\";\n    spreadCanvas.style.justifyContent = \"flex-start\";\n    spreadCanvas.style.alignItems = \"flex-start\";\n    spreadCanvas.style.gap = \"14px\";\n    spreadCanvas.style.width = \"max-content\";\n    spreadCanvas.style.minWidth = \"0\";\n    spreadCanvas.style.margin = \"0\";\n    spreadCanvas.style.boxSizing = \"border-box\";\n\n    // Mobile: shrink the spread so both action rails stay on-screen.\n    if (typeof _mshFitSpread === \"function\") { _mshFitSpread(); requestAnimationFrame(_mshFitSpread); }\n  }\n\n  // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n  // MOBILE SHELL (Phase 1) \u2014 reshapes the builder chrome on phones (<=749px)\n  // into the clean mockup structure: fixed top app bar (back/undo/redo/title/\n  // preview/Order/menu), a Prev \u00B7 page-dropdown \u00B7 Next nav under the stage, and\n  // a fixed bottom tab bar (Photos/Covers/Layouts/Stickers/Text). It only BUILDS\n  // new chrome that PROXIES to the existing controls + drives navigation with\n  // direct closure access (editor/builderMode/renderSpread). Desktop is untouched\n  // \u2014 every new element is display:none until the max-width:749px media query.\n  // Shared JS \u21D2 books, magazines and cards all get it. Idempotent.\n  // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n  const _MSH_SVG = {\n    back:  '<svg viewBox=\"0 0 24 24\" width=\"22\" height=\"22\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"19\" y1=\"12\" x2=\"5\" y2=\"12\"/><polyline points=\"12 19 5 12 12 5\"/></svg>',\n    undo:  '<svg viewBox=\"0 0 24 24\" width=\"22\" height=\"22\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"9 14 4 9 9 4\"/><path d=\"M4 9h11a5 5 0 0 1 5 5v1\"/></svg>',\n    redo:  '<svg viewBox=\"0 0 24 24\" width=\"22\" height=\"22\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"15 14 20 9 15 4\"/><path d=\"M20 9H9a5 5 0 0 0-5 5v1\"/></svg>',\n    book:  '<svg viewBox=\"0 0 24 24\" width=\"20\" height=\"20\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z\"/><path d=\"M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z\"/></svg>',\n    save:  '<svg viewBox=\"0 0 24 24\" width=\"20\" height=\"20\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z\"/><polyline points=\"17 21 17 13 7 13 7 21\"/><polyline points=\"7 3 7 8 15 8\"/></svg>',\n    card:  '<svg viewBox=\"0 0 24 24\" width=\"21\" height=\"21\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"2\" y=\"5\" width=\"20\" height=\"14\" rx=\"2\"/><line x1=\"12\" y1=\"9\" x2=\"12\" y2=\"15\"/><line x1=\"9\" y1=\"12\" x2=\"15\" y2=\"12\"/></svg>',\n    lock:  '<svg viewBox=\"0 0 24 24\" width=\"15\" height=\"15\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\"/><path d=\"M7 11V7a5 5 0 0 1 10 0v4\"/></svg>',\n    menu:  '<svg viewBox=\"0 0 24 24\" width=\"22\" height=\"22\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"3\" y1=\"6\" x2=\"21\" y2=\"6\"/><line x1=\"3\" y1=\"12\" x2=\"21\" y2=\"12\"/><line x1=\"3\" y1=\"18\" x2=\"21\" y2=\"18\"/></svg>',\n    photos:'<svg viewBox=\"0 0 24 24\" width=\"22\" height=\"22\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/><circle cx=\"8.5\" cy=\"8.5\" r=\"1.6\"/><path d=\"M21 15l-5-5L5 21\"/></svg>',\n    cover: '<svg viewBox=\"0 0 24 24\" width=\"22\" height=\"22\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M4 4h11a3 3 0 0 1 3 3v13H7a3 3 0 0 1-3-3z\"/><line x1=\"8\" y1=\"4\" x2=\"8\" y2=\"20\"/></svg>',\n    layouts:'<svg viewBox=\"0 0 24 24\" width=\"22\" height=\"22\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/><line x1=\"3\" y1=\"9\" x2=\"21\" y2=\"9\"/><line x1=\"12\" y1=\"9\" x2=\"12\" y2=\"21\"/></svg>',\n    stickers:'<svg viewBox=\"0 0 24 24\" width=\"22\" height=\"22\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 2l2.9 6.3 6.9.7-5.1 4.6 1.4 6.8L12 17.8 5.9 20.4l1.4-6.8L2.2 9l6.9-.7z\"/></svg>',\n    text: '<svg viewBox=\"0 0 24 24\" width=\"22\" height=\"22\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"4 7 4 4 20 4 20 7\"/><line x1=\"12\" y1=\"4\" x2=\"12\" y2=\"20\"/><line x1=\"9\" y1=\"20\" x2=\"15\" y2=\"20\"/></svg>'\n  };\n  const _MSH_TABS = [\n    { key: \"images\",   label: \"Photos\",   icon: _MSH_SVG.photos },\n    { key: \"cover\",    label: \"Covers\",   icon: _MSH_SVG.cover },\n    { key: \"layouts\",  label: \"Layouts\",  icon: _MSH_SVG.layouts },\n    { key: \"stickers\", label: \"Stickers\", icon: _MSH_SVG.stickers }\n  ];\n  let _mshBuilt = false;\n\n  // Desktop: relocate the \"Add a spread\" controls inline into the bottom nav bar,\n  // centered in the gap between \"Next\" and \"Add to Cart\". On phones this is a no-op \u2014\n  // the adder keeps its own row there (mobile shell relies on it as a child of stage).\n  function _dmbPlaceSpreadAdderDesktop() {\n    if (window.matchMedia && window.matchMedia(\"(max-width: 749px), (orientation: portrait) and (pointer: coarse) and (max-width: 1024px)\").matches) return;\n    const adder = document.getElementById(\"dmbSpreadAdderRow\");\n    const bar = document.querySelector(\".dmb-canvas-stage .dmb-stage-bottombar\");\n    const cartBtn = document.getElementById(\"addToCartBtn\");\n    if (!adder || !bar || !cartBtn || cartBtn.parentNode !== bar) return;\n    if (adder.parentNode === bar) return; // already placed\n    adder.classList.add(\"dmb-spread-adder-inbar\");\n    bar.insertBefore(adder, cartBtn);\n  }\n\n  // Desktop: auto-hide the scrollbars on the photo tray AND the pages tray (thumb\n  // appears while scrolling / hovering, fades away when idle \u2014 the phone's overlay feel).\n  function _dmbWireAutoHideScroll(el) {\n    if (!el || el._dmbAutoHideWired) return;\n    el._dmbAutoHideWired = true;\n    let t = null;\n    el.addEventListener(\"scroll\", () => {\n      el.classList.add(\"dmb-scrolling\");\n      if (t) clearTimeout(t);\n      t = setTimeout(() => el.classList.remove(\"dmb-scrolling\"), 900);\n    }, { passive: true });\n  }\n  function _dmbInitPhotoScrollAutoHide() {\n    _dmbWireAutoHideScroll(document.getElementById(\"dmbPicturesScrollArea\"));\n    _dmbWireAutoHideScroll(document.getElementById(\"thumbsRail\"));\n    _dmbWireAutoHideScroll(document.querySelector(\".dmb-pages-strip\"));\n  }\n\n  // Phone debugging with no desktop console available. Add ?mshdebug=1 to the builder\n  // URL and any init failure is printed on screen. Without the flag this renders\n  // nothing at all, so customers never see it. (2026-10-07)\n  // STICKY debug flag. openEditor rewrites the URL to ?step=2&restore=1 when the\n  // builder opens, which dropped ?mshdebug=1 and switched this diagnostic off at\n  // exactly the moment it was needed. Latch it once at load into sessionStorage so it\n  // survives the rewrite and any navigation within the site. Accepts the hash form too,\n  // since a query string can be lost when a link is shared through a chat app.\n  var _DMB_DIAG_ON = false;\n  try {\n    var _dq = new URLSearchParams(location.search).get(\"mshdebug\") === \"1\";\n    var _dh = (location.hash || \"\").indexOf(\"mshdebug\") >= 0;\n    if (_dq || _dh) sessionStorage.setItem(\"dmbMshDebug\", \"1\");\n    _DMB_DIAG_ON = sessionStorage.getItem(\"dmbMshDebug\") === \"1\";\n  } catch (e) { _DMB_DIAG_ON = false; }\n\n  // \u2500\u2500 Self-correct when the browser lies about the viewport width \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // The whole mobile layout hangs off \"is the window narrower than 749px\". Safari's\n  // \"Request Desktop Website\" makes a phone report ~980px, so that stops matching and\n  // the customer gets the DESKTOP builder on a phone, with no error and no way to know\n  // why. The setting is remembered per site per device, which is why one phone looks\n  // right and another does not, and why it reads as though the site was changed.\n  //\n  // A customer will never go hunting in Safari settings, so the site has to fix itself.\n  // screen.width still reports the REAL device width even in desktop mode, so the lie is\n  // detectable: a coarse pointer on a small physical screen claiming a wide viewport.\n  // When that happens, re-emit the mobile block's rules unconditionally. Reading them\n  // back out of the stylesheet means there is only ever ONE copy to maintain. (2026-10-07)\n  function _dmbFixFakeDesktopViewport() {\n    try {\n      var coarse = window.matchMedia && window.matchMedia(\"(pointer: coarse)\").matches;\n      var smallScreen = (window.screen && window.screen.width ? window.screen.width : 9999) <= 834;\n      var wideViewport = window.innerWidth > 749;\n      var el = document.getElementById(\"dmbForcedMobileCSS\");\n      if (!(coarse && smallScreen && wideViewport)) {\n        if (el) el.remove();\n        document.documentElement.classList.remove(\"dmb-forced-mobile\");\n        return false;\n      }\n      document.documentElement.classList.add(\"dmb-forced-mobile\");\n      if (el) return true;                       // already applied\n      var out = [];\n      for (var i = 0; i < document.styleSheets.length; i++) {\n        var sh = document.styleSheets[i];\n        if (!sh.href || sh.href.indexOf(\"design-my-book.css\") < 0) continue;\n        var rules = null;\n        try { rules = sh.cssRules; } catch (e) { continue; }   // cross-origin, skip\n        if (!rules) continue;\n        for (var j = 0; j < rules.length; j++) {\n          var r = rules[j];\n          if (!r || r.type !== 4) continue;                    // CSSMediaRule only\n          var cond = r.conditionText || (r.media && r.media.mediaText) || \"\";\n          if (cond.indexOf(\"749px\") < 0) continue;             // the mobile breakpoint\n          for (var k = 0; k < r.cssRules.length; k++) out.push(r.cssRules[k].cssText);\n        }\n      }\n      if (!out.length) return false;\n      var st = document.createElement(\"style\");\n      st.id = \"dmbForcedMobileCSS\";\n      st.textContent = out.join(String.fromCharCode(10));\n      document.head.appendChild(st);\n      try { console.warn(\"[dmb] desktop-mode viewport detected (\" + window.innerWidth +\n        \"px reported, screen is \" + window.screen.width + \"px); mobile layout forced.\"); } catch (e) {}\n      return true;\n    } catch (e) { return false; }\n  }\n  try {\n    _dmbFixFakeDesktopViewport();\n    window.addEventListener(\"resize\", function () { setTimeout(_dmbFixFakeDesktopViewport, 150); });\n    window.addEventListener(\"orientationchange\", function () { setTimeout(_dmbFixFakeDesktopViewport, 350); });\n  } catch (e) {}\n\n  function _dmbShowInitDiag() {\n    if (!_DMB_DIAG_ON) return;\n    var lines = [];\n    try {\n      var _bld = \"?\";\n      try {\n        var _sc = document.querySelector('script[src*=\"design-my-book.js\"]');\n        _bld = ((_sc && _sc.src || \"\").match(/mncb=([A-Za-z0-9]+)/) || [])[1] || \"?\";\n      } catch (e) {}\n      lines.push(\"build \" + _bld + \" | width \" + window.innerWidth + \"px | shell nodes \" +\n                 document.querySelectorAll('[class*=\"dmb-msh\"]').length);\n      // The single most useful line: did openEditor even REACH its init block? An\n      // undefined array means it stopped earlier, which points at a hang rather than\n      // a throw and tells us to look before the init calls, not at the shell itself.\n      lines.push(\"openEditor reached init block: \" +\n                 (window.__DMB_INIT_ERRS === undefined ? \"NO - it stopped earlier\" : \"yes\"));\n      lines.push(\"screen.width \" + (window.screen && window.screen.width) +\n                 \" | forced-mobile \" + (document.getElementById(\"dmbForcedMobileCSS\") ? \"YES\" : \"no\"));\n      lines.push(\"builder step2 visible: \" +\n                 (function () { var e = document.getElementById(\"step2\");\n                   return e ? (getComputedStyle(e).display !== \"none\" ? \"yes\" : \"no\") : \"missing\"; })());\n      lines.push(\"fullscreen class: \" +\n                 (document.body.classList.contains(\"dmb-msh-fullscreen\") ? \"yes\" : \"NO\"));\n      if (window.__DMB_MSH_ERR) lines.push(\"SHELL: \" + String(window.__DMB_MSH_ERR).slice(0, 400));\n      (window.__DMB_INIT_ERRS || []).forEach(function (m) { lines.push(\"INIT: \" + m.slice(0, 200)); });\n      if (!window.__DMB_MSH_ERR && !(window.__DMB_INIT_ERRS || []).length) lines.push(\"no init errors recorded\");\n    } catch (e) { lines.push(\"diag failed: \" + e); }\n    var box = document.getElementById(\"dmbInitDiag\") || document.createElement(\"div\");\n    box.id = \"dmbInitDiag\";\n    box.style.cssText = \"position:fixed;left:6px;right:6px;top:70px;z-index:2147483647;\" +\n      \"background:#1b1b1b;color:#ffd9d2;font:11px/1.45 ui-monospace,Menlo,monospace;\" +\n      \"padding:10px 12px;border-radius:8px;white-space:pre-wrap;max-height:45vh;overflow:auto;\" +\n      \"border:2px solid #ff7a5c;box-shadow:0 6px 24px rgba(0,0,0,.5);\";\n    box.textContent = lines.join(\"\\n\");\n    if (!box.parentNode) document.body.appendChild(box);\n  }\n\n  // The diagnostic above only runs at the END of openEditor, which is useless if\n  // openEditor is what failed. Schedule it independently too, so the box appears even\n  // when nothing downstream ever runs. Only active with ?mshdebug=1. (2026-10-07)\n  try {\n    if (_DMB_DIAG_ON) {\n      [3000, 8000, 16000, 30000].forEach(function (ms) {\n        setTimeout(function () { try { _dmbShowInitDiag(); } catch (e) {} }, ms);\n      });\n    }\n  } catch (e) {}\n\n  function _dmbInitMobileShell() {\n    const step2 = document.getElementById(\"step2\");\n    if (!step2) return;\n    if (_mshBuilt) { _mshSync(); return; }\n    _mshBuilt = true;\n\n    const clickId = (id) => { const el = document.getElementById(id); if (el) el.click(); };\n\n    // \u2500\u2500 Top app bar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    const top = document.createElement(\"header\");\n    top.id = \"dmbMshTop\";\n    top.className = \"dmb-msh-top\";\n    top.innerHTML =\n      '<button type=\"button\" class=\"dmb-msh-ic\" data-act=\"back\" aria-label=\"Back\">' + _MSH_SVG.back + '</button>' +\n      '<button type=\"button\" class=\"dmb-msh-ic\" data-act=\"undo\" aria-label=\"Undo\">' + _MSH_SVG.undo + '</button>' +\n      '<button type=\"button\" class=\"dmb-msh-ic\" data-act=\"redo\" aria-label=\"Redo\">' + _MSH_SVG.redo + '</button>' +\n      '<button type=\"button\" class=\"dmb-msh-ic\" data-act=\"addcard\" aria-label=\"Add card\">' + _MSH_SVG.card + '</button>' +\n      '<div class=\"dmb-msh-a4a5 dmb-msh-a4a5-hdr\" role=\"group\" aria-label=\"Book size\">' +\n        '<button type=\"button\" data-size=\"A4\">A4</button>' +\n        '<button type=\"button\" data-size=\"A5\">A5</button>' +\n      '</div>' +\n      '<div class=\"dmb-msh-title\" id=\"dmbMshTitle\">Book title</div>' +\n      '<button type=\"button\" class=\"dmb-msh-ic\" data-act=\"savedesign\" aria-label=\"Save design\">' + _MSH_SVG.save + '</button>' +\n      '<button type=\"button\" class=\"dmb-msh-ic\" data-act=\"preview\" aria-label=\"Preview\">' + _MSH_SVG.book + '</button>' +\n      '<button type=\"button\" class=\"dmb-msh-order\" data-act=\"order\">' + _MSH_SVG.lock + '<span>Order</span></button>' +\n      '<button type=\"button\" class=\"dmb-msh-ic\" data-act=\"menu\" aria-label=\"Menu\">' + _MSH_SVG.menu + '</button>';\n    step2.appendChild(top);\n    // Wire the header A4/A5 toggle to the real .dmb-size-opt buttons + keep it in sync.\n    const _hdrA4A5 = top.querySelector(\".dmb-msh-a4a5-hdr\");\n    if (_hdrA4A5) {\n      const syncA4A5 = () => {\n        const active = document.querySelector(\".dmb-size-opt.is-active\");\n        const cur = active ? active.getAttribute(\"data-size\") : \"A4\";\n        _hdrA4A5.querySelectorAll(\"button\").forEach((b) =>\n          b.classList.toggle(\"active\", b.getAttribute(\"data-size\") === cur));\n      };\n      _hdrA4A5.addEventListener(\"click\", (e) => {\n        const b = e.target.closest(\"button[data-size]\"); if (!b) return;\n        const real = document.querySelector('.dmb-size-opt[data-size=\"' + b.getAttribute(\"data-size\") + '\"]');\n        if (real) real.click();\n        syncA4A5();\n      });\n      document.querySelectorAll(\".dmb-size-opt\").forEach((btn) =>\n        btn.addEventListener(\"click\", () => setTimeout(syncA4A5, 0)));\n      syncA4A5();\n    }\n\n    // \u2500\u2500 Overflow menu sheet (hamburger) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    const menu = document.createElement(\"div\");\n    menu.id = \"dmbMshMenu\";\n    menu.className = \"dmb-msh-menu\";\n    menu.innerHTML =\n      '<div class=\"dmb-msh-menu-card\">' +\n        '<div class=\"dmb-msh-menu-pages\"><span>Pages in book</span><div class=\"dmb-msh-sizewrap\"><select id=\"dmbMshSizeSelect\" class=\"dmb-msh-sizesel\" aria-label=\"Number of pages\"></select></div></div>' +\n        '<button type=\"button\" data-proxy=\"saveLayoutBtn\">Save</button>' +\n        // Save design / Do the magic / Add card / A4-A5 now live on the builder (quick-actions row + page-nav toggle).\n        '<button type=\"button\" data-proxy=\"historyBtn\">History</button>' +\n        // \"Project\" (save/open/new via a typed window.prompt) REMOVED 2026-09-10:\n        // it duplicated autosave + cloud resume + History, and its \"open\" replaced\n        // the current design with an old snapshot WITHOUT any confirmation (and\n        // dropped the front/back cover photos, which its restore never copied).\n        '<button type=\"button\" data-proxy=\"videoTutorialBtn\">Video tutorial</button>' +\n        '<button type=\"button\" data-proxy=\"exportTemplateBtn\">Publish template</button>' +\n        '<div class=\"dmb-msh-menu-close\">Close</div>' +\n      '</div>';\n    step2.appendChild(menu);\n    const closeMenu = () => menu.classList.remove(\"open\");\n    menu.addEventListener(\"click\", (e) => {\n      if (e.target === menu || e.target.classList.contains(\"dmb-msh-menu-close\")) { closeMenu(); return; }\n      const b = e.target.closest(\"[data-proxy],[data-proxysel]\");\n      if (!b) return;\n      const id = b.getAttribute(\"data-proxy\");\n      const sel = b.getAttribute(\"data-proxysel\");\n      const el = id ? document.getElementById(id) : document.querySelector(sel);\n      if (el) el.click();\n      closeMenu();\n    });\n\n    top.addEventListener(\"click\", (e) => {\n      const b = e.target.closest(\"[data-act]\"); if (!b) return;\n      const act = b.getAttribute(\"data-act\");\n      if (act === \"back\") clickId(\"backToDetailsBtn\");\n      else if (act === \"undo\") clickId(\"undoBtn\");\n      else if (act === \"redo\") clickId(\"redoBtn\");\n      else if (act === \"savedesign\") clickId(\"saveTemplateBtn\");\n      else if (act === \"addcard\") { const c = document.querySelector(\".dmb-add-card-cta\"); if (c) c.click(); }\n      else if (act === \"preview\") clickId(\"previewBtn\");\n      else if (act === \"order\") clickId(\"addToCartBtn\");\n      else if (act === \"menu\") menu.classList.toggle(\"open\");\n    });\n\n    // \u2500\u2500 Page navigation (Prev \u00B7 page dropdown \u00B7 Next \u00B7 count) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    const stage = step2.querySelector(\".dmb-canvas-stage\");\n    const pagenav = document.createElement(\"div\");\n    pagenav.id = \"dmbMshPagenav\";\n    pagenav.className = \"dmb-msh-pagenav\";\n    pagenav.innerHTML =\n      '<button type=\"button\" class=\"dmb-msh-nav dmb-msh-nav-prev\" data-act=\"prev\"><span class=\"dmb-msh-nav-ar\">&#8249;</span><span class=\"dmb-msh-nav-lbl\">Prev</span></button>' +\n      '<div class=\"dmb-msh-selwrap\"><select id=\"dmbMshPageSelect\" class=\"dmb-msh-select\"></select></div>' +\n      '<button type=\"button\" class=\"dmb-msh-nav dmb-msh-nav-next\" data-act=\"next\"><span class=\"dmb-msh-nav-lbl\">Next</span><span class=\"dmb-msh-nav-ar\">&#8250;</span></button>' +\n      '<button type=\"button\" class=\"dmb-msh-addpage\" data-act=\"addpage\" aria-haspopup=\"true\">\uFF0B Add page</button>';\n    if (stage) {\n      const adder = document.getElementById(\"dmbSpreadAdderRow\");\n      if (adder && adder.parentNode === stage) stage.insertBefore(pagenav, adder);\n      else stage.appendChild(pagenav);\n    }\n    pagenav.querySelector('[data-act=\"prev\"]').addEventListener(\"click\", () => clickId(\"prevSpreadBtn\"));\n    pagenav.querySelector('[data-act=\"next\"]').addEventListener(\"click\", () => clickId(\"nextSpreadBtn\"));\n    // \"\uFF0B Add page\" \u2192 a small sheet with the supported ways to add pages. A book\n    // adds pages as spreads (2-page sheets), so that's the primary option.\n    pagenav.querySelector('[data-act=\"addpage\"]').addEventListener(\"click\", () => {\n      const old = document.getElementById(\"dmbMshAddMenu\");\n      if (old) { old.remove(); return; }\n      const m = document.createElement(\"div\");\n      m.id = \"dmbMshAddMenu\";\n      m.className = \"dmb-msh-addmenu\";\n      // Magazines add two spreads at a time (one folded sheet), so the button has to\n      // say so - and it has to WORK: the \"+/-\" adder row it used to click exists only\n      // in the book section, so on a magazine this did nothing at all.\n      const _magAdd = !!window.DMB_MAGAZINE_MODE;\n      const _addTtl = _magAdd ? \"Add 2 spreads\" : \"Add a spread\";\n      let _addSub;\n      try {\n        _addSub = (_magAdd ? \"4 pages \u00B7 \" : \"2 pages \u00B7 \") +\n                  fmtMoney(EXTRA_SPREAD_PRICE * (_magAdd ? 2 : 1)) + \" \u00B7 added where you choose\";\n      } catch (e) { _addSub = _magAdd ? \"4 pages\" : \"2 pages\"; }\n      m.innerHTML =\n        '<div class=\"dmb-msh-addmenu-card\">' +\n          '<div class=\"dmb-msh-addmenu-title\">Add pages</div>' +\n          '<button type=\"button\" data-add=\"spread\"><b>' + _addTtl + '</b><small>' + _addSub + '</small></button>' +\n          '<button type=\"button\" data-add=\"dup\"><b>Duplicate this spread</b><small>copy the current two pages</small></button>' +\n          '<button type=\"button\" class=\"dmb-msh-addmenu-cancel\" data-add=\"cancel\">Cancel</button>' +\n        '</div>';\n      step2.appendChild(m);\n      m.addEventListener(\"click\", (e) => {\n        const b = e.target.closest(\"[data-add]\");\n        if (e.target === m || !b || b.getAttribute(\"data-add\") === \"cancel\") { m.remove(); return; }\n        const a = b.getAttribute(\"data-add\");\n        if (a === \"spread\") {\n          const el = document.getElementById(\"dmbAddSpreadBtn\");\n          if (el) el.click();\n          else { try { _dmbAddSpreadPrompt(); } catch (e) {} }   // magazines have no adder row\n        }\n        else if (a === \"dup\") {\n          const el = Array.from(document.querySelectorAll(\".dmb-page-action-btn\")).find((x) => x.textContent.trim() === \"Duplicate page\");\n          if (el) el.click();\n        }\n        m.remove();\n      });\n    });\n    const pageSel = pagenav.querySelector(\"#dmbMshPageSelect\");\n    pageSel.addEventListener(\"change\", () => {\n      const v = pageSel.value;\n      if (v === \"cover\") {\n        const t = document.querySelector('.dmb-icon-tab[data-tab=\"cover\"]');\n        if (t) t.click();\n      } else {\n        const si = parseInt(v, 10) || 0;\n        try { builderMode = \"pages\"; } catch (e) {}\n        editor.currentSpread = si;\n        try { if (typeof syncBuilderModeButtons === \"function\") syncBuilderModeButtons(); } catch (e) {}\n        renderSpread();\n      }\n      _mshSync();\n    });\n\n    // \u2500\u2500 Page-count dropdown (the \"N pages\" chip) \u2014 mirrors the real #pages\n    //    select; changing it rebuilds the book at the new page count. \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    const sizeSel = document.getElementById(\"dmbMshSizeSelect\");\n    const pagesEl = document.getElementById(\"pages\");\n    if (sizeSel && pagesEl) {\n      Array.from(pagesEl.options).forEach((o) => {\n        if (!o.value) return; // skip the \"Select pages\" placeholder\n        const opt = document.createElement(\"option\");\n        opt.value = o.value;\n        opt.textContent = o.textContent;\n        sizeSel.appendChild(opt);\n      });\n      sizeSel.addEventListener(\"change\", () => {\n        pagesEl.value = sizeSel.value;\n        pagesEl.dispatchEvent(new Event(\"change\", { bubbles: true }));\n        _mshSync();\n      });\n    }\n\n    // \u2500\u2500 Icon ribbon (under the page-nav row) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // Replaces the per-page side rails so the canvas gets the full width. Idle:\n    // a scrollable row of Add + Page quick-actions (icons). When an object is\n    // selected its edit bar docks HERE instead (mobile has no top ribbon \u2014\n    // .dmb-stage-header is display:none), via _dmbDockBar's mobile branch.\n    (function _mshBuildRibbon() {\n      if (document.getElementById(\"dmbMshRibbon\")) return;\n      const S = 'fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"';\n      const ICO = {\n        text:    '<svg viewBox=\"0 0 24 24\" '+S+'><path d=\"M4 7V5h16v2\"/><path d=\"M12 5v14\"/><path d=\"M9 19h6\"/></svg>',\n        photo:   '<svg viewBox=\"0 0 24 24\" '+S+'><rect x=\"3\" y=\"5\" width=\"18\" height=\"14\" rx=\"2\"/><circle cx=\"8.5\" cy=\"10\" r=\"1.5\"/><path d=\"M21 16l-5-5-7 7\"/></svg>',\n        shape:   '<svg viewBox=\"0 0 24 24\" '+S+'><rect x=\"4\" y=\"4\" width=\"16\" height=\"16\" rx=\"2\"/></svg>',\n        map:     '<svg viewBox=\"0 0 24 24\" '+S+'><path d=\"M9 4 3 6v14l6-2 6 2 6-2V4l-6 2z\"/><path d=\"M9 4v14M15 6v14\"/></svg>',\n        sticker: '<svg viewBox=\"0 0 24 24\" '+S+'><path d=\"M14 3H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h9l5-5V5a2 2 0 0 0-2-2z\"/><path d=\"M14 21v-4a2 2 0 0 1 2-2h4\"/></svg>',\n        undo:    '<svg viewBox=\"0 0 24 24\" '+S+'><path d=\"M9 14 4 9l5-5\"/><path d=\"M4 9h11a5 5 0 0 1 0 10h-3\"/></svg>',\n        redo:    '<svg viewBox=\"0 0 24 24\" '+S+'><path d=\"m15 14 5-5-5-5\"/><path d=\"M20 9H9a5 5 0 0 0 0 10h3\"/></svg>',\n        layout:  '<svg viewBox=\"0 0 24 24\" '+S+'><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/><path d=\"M3 9h18M9 21V9\"/></svg>',\n        dup:     '<svg viewBox=\"0 0 24 24\" '+S+'><rect x=\"9\" y=\"9\" width=\"12\" height=\"12\" rx=\"2\"/><path d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"/></svg>',\n        magic:   '<svg viewBox=\"0 0 24 24\" '+S+'><path d=\"M5 21 15 11l-2-2L3 19z\"/><path d=\"M17 3l.9 2.1L20 6l-2.1.9L17 9l-.9-2.1L14 6z\"/></svg>',\n        clear:   '<svg viewBox=\"0 0 24 24\" '+S+'><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/><path d=\"M9 9l6 6M15 9l-6 6\"/></svg>',\n        bg:      '<svg viewBox=\"0 0 24 24\" '+S+'><path d=\"M12 3s6 6.5 6 10.5a6 6 0 0 1-12 0C6 9.5 12 3 12 3z\"/></svg>'\n      };\n      const btn = (act, lbl, ic) =>\n        '<button type=\"button\" class=\"dmb-msh-rb\" data-rb=\"' + act + '\"><span class=\"dmb-msh-rb-ic\">' + ic + '</span><span class=\"dmb-msh-rb-lbl\">' + lbl + '</span></button>';\n      const ribbon = document.createElement(\"div\");\n      ribbon.id = \"dmbMshRibbon\";\n      ribbon.className = \"dmb-msh-ribbon\";\n      const idleRow = document.createElement(\"div\");\n      idleRow.id = \"dmbMshRibbonIdle\";\n      idleRow.className = \"dmb-msh-ribbon-idle\";\n      // Undo/Redo live in the top bar; Stickers/Layouts are bottom tabs \u2014 so they're\n      // NOT duplicated here. Ribbon = add tools + page actions unique to it.\n      idleRow.innerHTML =\n        btn(\"text\",\"Text\",ICO.text) + btn(\"photo\",\"Photo\",ICO.photo) + btn(\"shape\",\"Shape\",ICO.shape) +\n        btn(\"map\",\"Map\",ICO.map) +\n        '<span class=\"dmb-msh-rb-sep\"></span>' +\n        btn(\"bg\",\"Background\",ICO.bg) + btn(\"dup\",\"Duplicate\",ICO.dup) + btn(\"magic\",\"Magic\",ICO.magic) + btn(\"clear\",\"Clear\",ICO.clear);\n      ribbon.appendChild(idleRow);\n      if (stage) {\n        const adder = document.getElementById(\"dmbSpreadAdderRow\");\n        if (adder && adder.parentNode === stage) stage.insertBefore(ribbon, adder);\n        else if (pagenav.nextSibling) stage.insertBefore(ribbon, pagenav.nextSibling);\n        else stage.appendChild(ribbon);\n      }\n      const byId = (id) => { const el = document.getElementById(id); if (el) el.click(); };\n      const pageAct = (label) => {\n        const el = Array.from(document.querySelectorAll(\".dmb-page-action-btn\")).find((x) => x.textContent.trim() === label);\n        if (el) el.click();\n      };\n\n      // \u2500\u2500 Page background colour (mobile) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n      // A ribbon popup that colours the CURRENT spread's page(s). The user picks\n      // which page with a Left/Right/Both toggle (default Both). Writes go to the\n      // existing editor.pageBackgrounds model (same one the desktop rail uses), so\n      // rendering, undo, save, duplicate and PDF export already handle it.\n      // The popup carries class .dmb-text-color-pop so _dmbMobileRibbonPopovers\n      // floats it as a full-width bottom sheet above everything.\n      let _mshBgTarget = \"both\";\n      const bgPop = document.createElement(\"div\");\n      bgPop.id = \"dmbMshBgPop\";\n      bgPop.className = \"dmb-text-color-pop dmb-msh-bgpop\";\n      bgPop.hidden = true;\n      bgPop.innerHTML =\n        '<div class=\"dmb-text-color-pop-title\">Page background</div>' +\n        '<div class=\"dmb-msh-bg-target\" role=\"group\" aria-label=\"Which page to colour\">' +\n          '<button type=\"button\" data-tgt=\"left\">Left</button>' +\n          '<button type=\"button\" data-tgt=\"right\">Right</button>' +\n          '<button type=\"button\" data-tgt=\"both\" class=\"active\">Both</button>' +\n        '</div>' +\n        '<div class=\"dmb-msh-bg-swatches\" id=\"dmbMshBgSwatches\"></div>' +\n        '<div id=\"dmbMshBgEmpty\" style=\"font-size:12px;color:#9a9a9a;padding:2px 2px 0\" hidden>No saved colours yet \u2014 pick one with \u201C\uFF0B Custom colour\u2026\u201D.</div>' +\n        '<button type=\"button\" class=\"dmb-msh-bg-custom\">\uFF0B Custom colour\u2026' +\n          '<input type=\"color\" class=\"dmb-msh-bg-input\" value=\"#ffffff\" tabindex=\"-1\" aria-hidden=\"true\"></button>' +\n        '<button type=\"button\" class=\"dmb-msh-bg-remove\">Remove background</button>';\n      document.body.appendChild(bgPop);\n      const _bgInput = bgPop.querySelector(\".dmb-msh-bg-input\");\n\n      function _mshCurrentPages() {\n        // Cover view can be currentSpread === -1 OR builderMode === \"cover\" while\n        // currentSpread is still 0 \u2014 both mean \"no page spread is showing\", so the\n        // background must target the cover, not the (hidden) first spread's pages.\n        if (typeof builderMode !== \"undefined\" && builderMode === \"cover\") return null;\n        const si = editor && editor.currentSpread;\n        if (si == null || si < 0) return null;   // cover mode\n        return { left: si * 2 + 1, right: si * 2 + 2 };\n      }\n      function _mshApplyBg(color) {\n        const norm = (color || \"#ffffff\").toLowerCase();\n        const pages = _mshCurrentPages();\n        if (!pages) {   // cover mode \u2192 colour the cover instead\n          try {\n            if (typeof coverState !== \"undefined\") {\n              pushUndoState(\"Cover Background\");\n              coverState.color = (norm !== \"#ffffff\") ? norm : \"#ffffff\";\n              if (typeof saveCoverDesignToStorage === \"function\") saveCoverDesignToStorage();\n              if (typeof renderCoverCanvas === \"function\") renderCoverCanvas();\n            }\n          } catch (e) {}\n          return;\n        }\n        let targets = _mshBgTarget === \"left\" ? [pages.left]\n                    : _mshBgTarget === \"right\" ? [pages.right]\n                    : [pages.left, pages.right];\n        targets = targets.filter((p) => p >= 1 && p <= (editor.totalPages || 0));\n        if (!targets.length) return;\n        try { pushUndoState(\"Page Background\"); } catch (e) {}\n        editor.pageBackgrounds = editor.pageBackgrounds || {};\n        targets.forEach((p) => {\n          if (norm !== \"#ffffff\") editor.pageBackgrounds[p] = norm;\n          else delete editor.pageBackgrounds[p];\n        });\n        try { renderBuilderCanvas(); } catch (e) {}\n        try { renderThumbsRail(); } catch (e) {}\n      }\n      function _mshSyncBgUI() {\n        // Reflect the target page's current colour into the picker + active swatch.\n        const pages = _mshCurrentPages();\n        let cur = \"#ffffff\";\n        if (pages) {\n          const p = (_mshBgTarget === \"right\") ? pages.right : pages.left;\n          cur = (editor.pageBackgrounds && editor.pageBackgrounds[p]) || \"#ffffff\";\n        } else if (typeof coverState !== \"undefined\") {\n          cur = coverState.color || \"#ffffff\";\n        }\n        cur = String(cur).toLowerCase();\n        _bgInput.value = /^#[0-9a-f]{6}$/i.test(cur) ? cur : \"#ffffff\";\n        // Palette = the user's SAVED colours only (no presets/generated swatches).\n        const _sw = bgPop.querySelector(\"#dmbMshBgSwatches\");\n        const _colors = ((typeof dmbGetRecentColors === \"function\") ? dmbGetRecentColors() : []) || [];\n        if (_sw) {\n          _sw.innerHTML = _colors.map((h) =>\n            '<button type=\"button\" class=\"dmb-msh-bg-swatch\" data-hex=\"' + h + '\" title=\"' + h + '\" style=\"background:' + h + '\"></button>').join(\"\");\n          _sw.hidden = _colors.length === 0;\n          _sw.querySelectorAll(\".dmb-msh-bg-swatch\").forEach((b) =>\n            b.classList.toggle(\"active\", (b.dataset.hex || \"\").toLowerCase() === cur));\n        }\n        const _em = bgPop.querySelector(\"#dmbMshBgEmpty\"); if (_em) _em.hidden = _colors.length > 0;\n        // Cover mode has no Left/Right \u2014 hide the toggle there.\n        const tgt = bgPop.querySelector(\".dmb-msh-bg-target\");\n        if (tgt) tgt.style.display = pages ? \"\" : \"none\";\n      }\n      function _mshToggleBgPop() {\n        const willOpen = bgPop.hidden;\n        if (willOpen) _mshSyncBgUI();\n        bgPop.hidden = !willOpen;\n      }\n      // target toggle\n      bgPop.querySelectorAll(\".dmb-msh-bg-target button\").forEach((b) => {\n        b.addEventListener(\"click\", (e) => {\n          e.preventDefault(); e.stopPropagation();\n          _mshBgTarget = b.dataset.tgt;\n          bgPop.querySelectorAll(\".dmb-msh-bg-target button\").forEach((x) => x.classList.toggle(\"active\", x === b));\n          _mshSyncBgUI();\n        });\n      });\n      // saved-colour swatches (delegated \u2014 the list is re-rendered on each open)\n      const _bgSwWrap = bgPop.querySelector(\"#dmbMshBgSwatches\");\n      if (_bgSwWrap) _bgSwWrap.addEventListener(\"click\", (e) => {\n        const b = e.target.closest(\".dmb-msh-bg-swatch\"); if (!b) return;\n        e.preventDefault(); e.stopPropagation();\n        _mshApplyBg(b.dataset.hex);\n        try { if ((b.dataset.hex || \"\").toLowerCase() !== \"#ffffff\") dmbAddRecentColor(b.dataset.hex); } catch (e2) {}\n        _mshSyncBgUI();\n      });\n      // custom colour \u2014 overlay the real input over the button so an iOS tap opens\n      // the native wheel (a programmatic .click() is ignored on iOS). Desktop still\n      // works via the .click() fallback below.\n      const _bgCustomBtn = bgPop.querySelector(\".dmb-msh-bg-custom\");\n      _bgCustomBtn.addEventListener(\"click\", (e) => {\n        e.stopPropagation();\n        if (e.target !== _bgInput) { try { _bgInput.click(); } catch (e2) {} }\n      });\n      try { _dmbColorTapFix(_bgCustomBtn, _bgInput); } catch (e) {}\n      _bgInput.addEventListener(\"input\", () => _mshApplyBg(_bgInput.value));   // live scrub\n      _bgInput.addEventListener(\"change\", () => { _mshApplyBg(_bgInput.value); _mshSyncBgUI(); try { dmbAddRecentColor(_bgInput.value); } catch (e) {} });\n      // remove\n      bgPop.querySelector(\".dmb-msh-bg-remove\").addEventListener(\"click\", (e) => {\n        e.stopPropagation(); _mshApplyBg(\"#ffffff\"); _mshSyncBgUI();\n      });\n      // close on outside tap (but not the opening tap on the ribbon Background btn,\n      // and not while the native colour picker is up \u2014 it steals focus and the tap\n      // that returns focus must not kill the sheet mid-edit).\n      document.addEventListener(\"click\", (e) => {\n        if (bgPop.hidden) return;\n        if (e.target.closest(\"#dmbMshBgPop\") || e.target.closest('[data-rb=\"bg\"]')) return;\n        if (document.activeElement === _bgInput) return;\n        bgPop.hidden = true;\n      });\n\n      idleRow.addEventListener(\"click\", (e) => {\n        const b = e.target.closest(\"[data-rb]\"); if (!b) return;\n        const a = b.getAttribute(\"data-rb\");\n        if (a === \"text\") byId(\"canvasTextBtn\");\n        else if (a === \"photo\") byId(\"canvasPhotoBtn\");\n        else if (a === \"shape\") byId(\"canvasShapeBtn\");\n        else if (a === \"map\") byId(\"addMyMapBtn\");\n        else if (a === \"sticker\") byId(\"dmbStickersBtn\");\n        else if (a === \"bg\") _mshToggleBgPop();\n        else if (a === \"undo\") byId(\"undoBtn\");\n        else if (a === \"redo\") byId(\"redoBtn\");\n        else if (a === \"magic\") byId(\"smartCreationBtn\");\n        else if (a === \"layout\") pageAct(\"Choose layout\");\n        else if (a === \"dup\") pageAct(\"Duplicate page\");\n        else if (a === \"clear\") pageAct(\"Clear page\");\n      });\n    })();\n\n    // \u2500\u2500 Bottom tab bar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    const tabbar = document.createElement(\"nav\");\n    tabbar.id = \"dmbMshTabs\";\n    tabbar.className = \"dmb-msh-tabs\";\n    tabbar.innerHTML = _MSH_TABS.map((t) =>\n      '<button type=\"button\" class=\"dmb-msh-tab\" data-tab=\"' + t.key + '\">' +\n        '<span class=\"dmb-msh-tab-ic\">' + t.icon + '</span>' +\n        '<span class=\"dmb-msh-tab-lbl\">' + t.label + '</span>' +\n      '</button>'\n    ).join(\"\");\n    step2.appendChild(tabbar);\n    tabbar.addEventListener(\"click\", (e) => {\n      const b = e.target.closest(\".dmb-msh-tab\"); if (!b) return;\n      const key = b.getAttribute(\"data-tab\");\n      const iconTab = document.querySelector('.dmb-icon-tab[data-tab=\"' + key + '\"]');\n      if (iconTab) {\n        iconTab.click();\n      } else {\n        // No matching icon-tab (e.g. Text) \u2014 activate its side panel directly.\n        document.querySelectorAll(\".dmb-icon-tab\").forEach((t) => t.classList.remove(\"active\"));\n        document.querySelectorAll(\".dmb-side-panel\").forEach((p) => p.classList.remove(\"active\"));\n        const panel = document.querySelector('.dmb-side-panel[data-panel=\"' + key + '\"]');\n        if (panel) panel.classList.add(\"active\");\n        const lt = document.getElementById(\"dmbLeftTitle\");\n        if (lt) lt.textContent = (_MSH_TABS.find((x) => x.key === key) || {}).label || \"Panel\";\n      }\n      if (key === \"layouts\") {\n        _mshBuildLayoutsPanel();\n        // Re-fit the A4 thumbnails once the panel is visible AND its height has\n        // settled. A single rAF fires before the activation reflow finishes (tray\n        // still short \u2192 thumbnails too narrow), so re-run on a couple of timeouts.\n        const _fit = () => { try { window._mshSizeLayoutItems && window._mshSizeLayoutItems(); } catch (e) {} };\n        requestAnimationFrame(_fit);\n        setTimeout(_fit, 180);\n        setTimeout(_fit, 450);\n      }\n      _mshSyncTabs();\n    });\n\n    // Resync the page dropdown whenever the spread index text changes\n    // (fires on every renderSpread \u2014 covers Prev/Next, add-spread, restore\u2026).\n    const idxEl = document.getElementById(\"spreadIndex\");\n    if (idxEl && window.MutationObserver) {\n      new MutationObserver(() => _mshSync()).observe(idxEl, { childList: true, characterData: true, subtree: true });\n    }\n    // Keep the bottom-tab highlight in sync when panels change elsewhere.\n    document.querySelectorAll(\".dmb-icon-tab\").forEach((t) =>\n      t.addEventListener(\"click\", () => setTimeout(_mshSyncTabs, 0))\n    );\n\n    // Full-screen the builder on phones: add a body class while #step2 is\n    // visible so the CSS can hide the theme header/footer and lock page scroll.\n    // Toggled with #step2's inline display so the upload step / other pages stay\n    // normal. (Effects are gated by the max-width:749px media query, so desktop\n    // is unaffected even though the class is present.)\n    try {\n      const _mshFS = () => {\n        const s = step2.getAttribute(\"style\") || \"\";\n        const shown = !/display\\s*:\\s*none/i.test(s);\n        document.body.classList.toggle(\"dmb-msh-fullscreen\", shown);\n      };\n      _mshFS();\n      new MutationObserver(_mshFS).observe(step2, { attributes: true, attributeFilter: [\"style\"] });\n    } catch (e) {}\n\n    // Re-fit the spread on resize / rotate so the action rails stay visible.\n    let _mshRz;\n    window.addEventListener(\"resize\", () => {\n      clearTimeout(_mshRz);\n      _mshRz = setTimeout(() => { _mshFitSpread(); _mshSync(); try { _dmbSizePhotoTiles(); } catch (e) {} }, 150);\n    });\n    window.addEventListener(\"orientationchange\", () => setTimeout(() => { _mshFitSpread(); try { _dmbSizePhotoTiles(); } catch (e) {} }, 350));\n\n    // Re-fit whenever the canvas re-renders \u2014 this covers the COVER view and\n    // every spread action/refresh, so the size stays stable and always largest-fit.\n    if (spreadCanvas && window.MutationObserver) {\n      let _mshRaf;\n      new MutationObserver(() => {\n        cancelAnimationFrame(_mshRaf);\n        _mshRaf = requestAnimationFrame(_mshFitSpread);\n      }).observe(spreadCanvas, { childList: true });\n    }\n\n    // Photos panel (phones): lift Sort + \"Hide used\" onto the \"Pictures\" title\n    // row and drop the redundant Pages control (the nav-bar page-count dropdown\n    // replaces it). CSS turns the tray into a horizontal swipe row.\n    if (window.matchMedia && window.matchMedia(\"(max-width: 749px), (orientation: portrait) and (pointer: coarse) and (max-width: 1024px)\").matches) {\n      try {\n        const head = step2.querySelector(\".dmb-leftpanel-head\");\n        const addBtn = document.getElementById(\"addMorePhotosBtn\");\n        const hideUsed = step2.querySelector(\".dmb-hide-used-inline\");\n        const sortSel = document.getElementById(\"photoSortSelect\");\n        if (head && addBtn && !head.dataset.mshHead) {\n          head.dataset.mshHead = \"1\";\n          // Replace the \"Pictures/Images\" title with the \"Do the Magic\" action \u2014 it\n          // takes the title's place (per request); proxies the real #smartCreationBtn.\n          const _lt = document.getElementById(\"dmbLeftTitle\");\n          if (_lt) _lt.style.display = \"none\";\n          // No header row: the whole dock is the photo tray. \"Add more pictures\"\n          // becomes a compact \uFF0B tile at the START of the strip; \"Hide used\" a small\n          // toggle pinned in the dock's top-right. \"Do the Magic\" lives in the ribbon.\n          const picSec = step2.querySelector('.dmb-side-panel[data-panel=\"images\"] .dmb-pictures-section');\n          addBtn.innerHTML = '<span class=\"dmb-msh-addtile-plus\">\uFF0B</span><span class=\"dmb-msh-addtile-lbl\">Add</span>';\n          addBtn.classList.add(\"dmb-msh-addtile\");\n          if (picSec) picSec.insertBefore(addBtn, picSec.firstChild);\n          const dock = step2.querySelector(\".dmb-builder-leftpanel\");\n          if (hideUsed && dock) { hideUsed.classList.add(\"dmb-msh-hidetoggle\"); dock.appendChild(hideUsed); }\n          if (sortSel) sortSel.style.display = \"none\";   // not in the requested layout\n          _mshBuildPhotoScrollbar();\n          // Covers panel: the swatch label reads \"Cover background\" per the mockup.\n          const cvLbl = step2.querySelector('.dmb-side-panel[data-panel=\"cover\"] .dmb-form-grid .dmb-mini-label');\n          if (cvLbl) cvLbl.textContent = \"Cover background\";\n          // \"Choose cover layout\" should NOT open a popup on mobile \u2014 send the user\n          // to the inline Layouts tab (which now applies to the cover). Capture-phase\n          // + stopImmediatePropagation cancels the modal-opening handler.\n          const _ccl = document.getElementById(\"chooseCoverLayoutBtn\");\n          if (_ccl) _ccl.addEventListener(\"click\", (ev) => {\n            ev.preventDefault(); ev.stopImmediatePropagation();\n            const lt = document.querySelector('.dmb-msh-tab[data-tab=\"layouts\"]');\n            if (lt) lt.click();\n          }, true);\n        }\n      } catch (e) {}\n    }\n\n    _mshSync();\n  }\n\n  // Rebuild the page dropdown + count + prev/next state from the live model.\n  function _mshSync() {\n    const sel = document.getElementById(\"dmbMshPageSelect\");\n    if (!sel) return;\n    const total = Math.max(1, editor.totalSpreads || 1);\n    const cur = editor.currentSpread;\n    // Navigation model (matches prev/next buttons): Cover (builderMode \"cover\")\n    // \u2192 Page 0 / \"Spread 0\" (currentSpread === -1, a single first page) \u2192 1\u20132\n    // (currentSpread 0) \u2192 3\u20134 (1) \u2192 \u2026 Cover is a MODE, so currentSpread === -1\n    // is Page 0, NOT the cover \u2014 the old code conflated the two, which hid Page 0.\n    const inCover = (typeof builderMode !== \"undefined\" && builderMode === \"cover\");\n    let html = '<option value=\"cover\">Cover</option>';\n    html += '<option value=\"-1\">0</option>';   // Page 0 \u2014 the single first page\n    for (let si = 0; si < total; si++) {\n      html += '<option value=\"' + si + '\">' + (si * 2 + 1) + '\u2013' + (si * 2 + 2) + '</option>';\n    }\n    sel.innerHTML = html;\n    sel.value = inCover ? \"cover\" : String(cur);   // cur === -1 \u2192 \"-1\" (Page 0)\n    const sizeSel = document.getElementById(\"dmbMshSizeSelect\");\n    const pagesEl = document.getElementById(\"pages\");\n    if (sizeSel && pagesEl && pagesEl.value) sizeSel.value = pagesEl.value;\n    const prev = document.querySelector('#dmbMshPagenav [data-act=\"prev\"]');\n    const next = document.querySelector('#dmbMshPagenav [data-act=\"next\"]');\n    // Cover is the first view (nothing before it); every other view can go back\n    // (Page 0 \u2192 Cover, spread 0 \u2192 Page 0, spread n \u2192 n-1). Next stops on the last.\n    if (prev) prev.disabled = inCover;\n    if (next) next.disabled = !inCover && cur >= total - 1;\n    const title = document.getElementById(\"dmbMshTitle\");\n    if (title) { try { title.textContent = getEffectiveBookTitle() || \"Book title\"; } catch (e) {} }\n    _mshSyncTabs();\n  }\n\n  // Highlight the bottom tab matching the currently-active side panel.\n  function _mshSyncTabs() {\n    const active = document.querySelector(\".dmb-side-panel.active\");\n    const key = active ? active.getAttribute(\"data-panel\") : null;\n    document.querySelectorAll(\".dmb-msh-tab\").forEach((t) =>\n      t.classList.toggle(\"active\", t.getAttribute(\"data-tab\") === key)\n    );\n    // The lifted \"add pictures\" pill + \"hide used\" belong to the Photos panel only.\n    const showHead = (key === \"images\");\n    document.querySelectorAll(\".dmb-msh-headitem\").forEach((el) => {\n      el.style.display = showHead ? \"\" : \"none\";\n    });\n    // Header only on the Photos panel (title + add pill + hide-used). Covers,\n    // Layouts and Stickers have no header/title per the mockups.\n    const _head = document.querySelector(\".dmb-leftpanel-head\");\n    if (_head) _head.style.display = (key === \"images\") ? \"\" : \"none\";\n    // \"Hide used\" is pinned to the dock (not inside the images panel), so it must\n    // be toggled explicitly \u2014 otherwise it lingers over Layouts/Covers/Stickers.\n    const _hu = document.querySelector(\".dmb-msh-hidetoggle\");\n    if (_hu) _hu.style.display = (key === \"images\") ? \"\" : \"none\";\n  }\n\n  // Apply a layout to a cover face directly (no confirm popup) \u2014 mirrors the\n  // \"apply over, keep photos\" branch of setCoverLayoutWithConfirm.\n  function _mshApplyCoverLayoutDirect(zone, templateId) {\n    if (typeof coverState === \"undefined\") return;\n    try { pushUndoState(); } catch (e) {}\n    const isBack = (zone === \"back\");\n    const lKey = isBack ? \"backLayout\" : \"frontLayout\";\n    const fKey = isBack ? \"backFrames\" : \"frontFrames\";\n    const rKey = isBack ? \"backFrameRot\" : \"frontFrameRot\";\n    coverState[fKey] = coverState[fKey] || {};\n    coverState[rKey] = coverState[rKey] || {};\n    coverState[lKey] = templateId || null;\n    if (templateId && TEMPLATE_DEFS[templateId]) {\n      const defs = TEMPLATE_DEFS[templateId];\n      defs.forEach((f) => {\n        if (!(f.key in coverState[fKey])) coverState[fKey][f.key] = null;\n        if (!(f.key in coverState[rKey])) coverState[rKey][f.key] = 0;\n      });\n      Object.keys(coverState[fKey]).forEach((k) => {\n        if (!defs.find((d) => d.key === k)) delete coverState[fKey][k];\n      });\n    } else {\n      coverState[fKey] = {}; coverState[rKey] = {};\n    }\n    // Convert the just-set fixed layout into deletable free frames BEFORE saving, so\n    // the persisted design stores free frames (not a fixed layout). renderCoverCanvas\n    // would convert anyway, but that runs after this save.\n    try { _dmbConvertCoverFixedLayouts(); } catch (e) {}\n    try { saveCoverDesignToStorage(); } catch (e) {}\n    builderMode = \"cover\";\n    renderCoverCanvas();\n  }\n\n  // Phones: scale the whole spread (both pages PLUS the flanking per-page action\n  // rails) down with CSS zoom so nothing is cut off the screen. Width-fit only;\n  // no-ops on desktop and when it already fits. getBoundingClientRect accounts\n  // for zoom, so drag/drop hit-testing still maps correctly.\n  function _mshFitSpread() {\n    try {\n      if (!(window.matchMedia && window.matchMedia(\"(max-width: 749px), (orientation: portrait) and (pointer: coarse) and (max-width: 1024px)\").matches)) return;\n      if (!spreadViewport || !spreadCanvas) return;\n      // The cover render path (renderCoverCanvas) forces the viewport to\n      // height:auto / maxHeight:760 for the DESKTOP layout. In the phone shell the\n      // viewport is position:absolute; inset:0 and the centring depends on it being\n      // that full-size box \u2014 so clear those inline overrides and let the CSS govern,\n      // otherwise the cover anchors off-centre and runs off the screen.\n      spreadViewport.style.height = \"\"; spreadViewport.style.minHeight = \"\"; spreadViewport.style.maxHeight = \"\";\n      // iOS Safari ignores CSS `zoom` on this canvas (that was the whole bug \u2014 the\n      // cover/spread never shrank). Scale with `transform` instead, which the\n      // builder already uses everywhere and iOS supports. Clear any legacy zoom.\n      spreadCanvas.style.zoom = \"\";\n      // transform doesn't change the layout box, so scrollWidth/Height = the true\n      // unscaled content size regardless of the current transform.\n      const naturalW = spreadCanvas.scrollWidth;\n      const naturalH = spreadCanvas.scrollHeight;\n      // Measure the STAGE box (fixed height, via CSS) \u2014 NOT the viewport, which\n      // sizes to the tall content and would report the wrong available height.\n      const stage = spreadViewport.parentElement || spreadViewport;\n      const availW = stage.clientWidth - 10;\n      const availH = stage.clientHeight - 10;\n      if (naturalW < 4 || naturalH < 4 || availW < 4 || availH < 4) {\n        // Layout not ready (common right after a cover re-render) \u2014 try again next\n        // frame rather than leaving the canvas un-centred. Bounded so it can't spin.\n        _mshFitSpread._t = (_mshFitSpread._t || 0) + 1;\n        if (_mshFitSpread._t <= 15) requestAnimationFrame(_mshFitSpread);\n        return;\n      }\n      _mshFitSpread._t = 0;\n      // Largest size that fits BOTH width and height: keeps every side button on\n      // screen for spreads, and shows the WHOLE cover. Deterministic \u21D2 never\n      // changes; uniform scale \u21D2 text/stickers keep exact proportions.\n      const fit = Math.min(1, availW / naturalW, availH / naturalH);\n      currentZoom = Math.max(0.1, fit);\n      // Absolutely CENTER the canvas in the stage and scale about its centre \u2014 the\n      // layout box stays 680px but translate(-50%,-50%) centres it, so nothing is\n      // clipped top/bottom or left/right. iOS-safe (transform, not `zoom`).\n      spreadCanvas.style.transformOrigin = \"center center\";\n      spreadCanvas.style.transform = \"translate(-50%, -50%) scale(\" + currentZoom + \")\";\n    } catch (e) {}\n  }\n\n  // Phones: a visible horizontal scrollbar (\u25C4 track \u25BA) under the photo tray, since\n  // native scrollbars are hidden on touch. Arrows scroll; the thumb tracks position.\n  function _mshBuildPhotoScrollbar() {\n    try {\n      const scroller = document.getElementById(\"dmbPicturesScrollArea\");\n      if (!scroller || document.getElementById(\"dmbMshScrollbar\")) return;\n      const bar = document.createElement(\"div\");\n      bar.id = \"dmbMshScrollbar\";\n      bar.className = \"dmb-msh-scrollbar\";\n      bar.innerHTML =\n        '<button type=\"button\" class=\"dmb-msh-sb-arrow\" data-dir=\"-1\" aria-label=\"Scroll left\">&#9664;</button>' +\n        '<div class=\"dmb-msh-sb-track\"><div class=\"dmb-msh-sb-thumb\"></div></div>' +\n        '<button type=\"button\" class=\"dmb-msh-sb-arrow\" data-dir=\"1\" aria-label=\"Scroll right\">&#9654;</button>';\n      scroller.parentNode.insertBefore(bar, scroller.nextSibling);\n      const thumb = bar.querySelector(\".dmb-msh-sb-thumb\");\n      const update = () => {\n        const sw = scroller.scrollWidth, cw = scroller.clientWidth;\n        if (sw <= cw + 2) { thumb.style.width = \"100%\"; thumb.style.left = \"0%\"; return; }\n        thumb.style.width = Math.max(14, (cw / sw) * 100) + \"%\";\n        thumb.style.left = (scroller.scrollLeft / sw * 100) + \"%\";\n      };\n      scroller.addEventListener(\"scroll\", update, { passive: true });\n      bar.addEventListener(\"click\", (e) => {\n        const a = e.target.closest(\"[data-dir]\"); if (!a) return;\n        const dir = parseInt(a.getAttribute(\"data-dir\"), 10) || 1;\n        scroller.scrollBy({ left: dir * scroller.clientWidth * 0.7, behavior: \"smooth\" });\n      });\n      const tray = document.getElementById(\"editorPhotoTray\");\n      if (tray && window.MutationObserver) {\n        new MutationObserver(() => requestAnimationFrame(update)).observe(tray, { childList: true });\n      }\n      requestAnimationFrame(update);\n    } catch (e) {}\n  }\n\n  // Shared inline Layouts panel \u2014 used on BOTH mobile and desktop. It replaces the\n  // side-panel content with a Left/Right/Both target, category chips by photo-frame\n  // count, and layout thumbnails. Tapping one applies DIRECTLY (no confirm popup),\n  // keeping existing photos/text. On mobile the thumbnails are a horizontal tray\n  // sized by JS (iOS aspect-ratio bug); on desktop they're a vertical A4 grid via\n  // CSS aspect-ratio. Keep the two arrangements in sync when editing.\n  function _dmbRenderLayoutsPanel(opts) {\n    opts = opts || {};\n    const mobile = !!opts.mobile;\n    try {\n      const panel = document.querySelector('.dmb-side-panel[data-panel=\"layouts\"]');\n      if (!panel || typeof TEMPLATE_DEFS === \"undefined\") return;\n\n      // Categories by photo-frame count: 1, 2, 3, and \"4+\" (everything \u22654).\n      const CATS = [\n        { key: \"1\", label: \"1 Photo\",  test: (n) => n === 1 },\n        { key: \"2\", label: \"2 Photos\", test: (n) => n === 2 },\n        { key: \"3\", label: \"3 Photos\", test: (n) => n === 3 },\n        { key: \"4\", label: \"4+\",       test: (n) => n >= 4 }\n      ];\n      const idsFor = (cat) => Object.keys(TEMPLATE_DEFS).filter((id) => cat.test((TEMPLATE_DEFS[id] || []).length || 1));\n\n      const wrap = document.createElement(\"div\");\n      wrap.className = \"dmb-msh-lay\" + (mobile ? \"\" : \" dmb-lay-desktop\");\n      const _targetHTML =\n        '<div class=\"dmb-msh-lay-target\" role=\"group\" aria-label=\"Apply layout to\">' +\n          '<button type=\"button\" data-tgt=\"left\">Left</button>' +\n          '<button type=\"button\" data-tgt=\"right\">Right</button>' +\n          '<button type=\"button\" data-tgt=\"both\">Both</button>' +\n        '</div>';\n      // MOBILE: Left/Right/Both + a photo-count DROPDOWN share one compact row, then\n      // the swipe tray \u2014 so the whole picker fits the panel with no vertical scroll.\n      // DESKTOP: keep the category chip row.\n      wrap.innerHTML = mobile\n        ? ('<div class=\"dmb-msh-lay-head\">' + _targetHTML +\n             '<select class=\"dmb-msh-lay-catsel\" aria-label=\"Number of photos\"></select>' +\n           '</div>' +\n           '<div class=\"dmb-msh-lay-tray\"></div>')\n        : (_targetHTML + '<div class=\"dmb-msh-lay-cats\"></div><div class=\"dmb-msh-lay-tray\"></div>');\n      panel.innerHTML = \"\";\n      panel.appendChild(wrap);\n\n      const catsEl = wrap.querySelector(\".dmb-msh-lay-cats\");     // desktop chips\n      const catSel = wrap.querySelector(\".dmb-msh-lay-catsel\");   // mobile dropdown\n      const trayEl = wrap.querySelector(\".dmb-msh-lay-tray\");\n      let target = (opts.defaultTarget === \"right\" || opts.defaultTarget === \"both\") ? opts.defaultTarget : \"left\";\n      const _avail = CATS.filter((cat) => idsFor(cat).length);\n      let activeCat = _avail[0] || CATS[0];\n\n      // Initial target highlight (reflects which page/menu opened the panel).\n      wrap.querySelectorAll(\".dmb-msh-lay-target button\").forEach((x) =>\n        x.classList.toggle(\"active\", x.getAttribute(\"data-tgt\") === target));\n\n      wrap.querySelector(\".dmb-msh-lay-target\").addEventListener(\"click\", (e) => {\n        const b = e.target.closest(\"[data-tgt]\"); if (!b) return;\n        target = b.getAttribute(\"data-tgt\");\n        wrap.querySelectorAll(\".dmb-msh-lay-target button\").forEach((x) => x.classList.toggle(\"active\", x === b));\n      });\n\n      if (catSel) {\n        // Mobile: categories become dropdown options.\n        _avail.forEach((cat) => {\n          const o = document.createElement(\"option\");\n          o.value = cat.key; o.textContent = cat.label;\n          catSel.appendChild(o);\n        });\n        catSel.value = activeCat.key;\n        catSel.addEventListener(\"change\", () => {\n          activeCat = CATS.find((c) => c.key === catSel.value) || activeCat;\n          renderTray();\n        });\n      } else if (catsEl) {\n        // Desktop: categories are chip buttons.\n        _avail.forEach((cat) => {\n          const b = document.createElement(\"button\");\n          b.type = \"button\"; b.setAttribute(\"data-cat\", cat.key);\n          b.textContent = cat.label;\n          if (cat === activeCat) b.classList.add(\"active\");\n          b.addEventListener(\"click\", () => {\n            activeCat = cat;\n            catsEl.querySelectorAll(\"button\").forEach((x) => x.classList.toggle(\"active\", x === b));\n            renderTray();\n          });\n          catsEl.appendChild(b);\n        });\n      }\n\n      // Apply the tapped layout DIRECTLY \u2014 NO confirm popup. Uses the existing\n      // free-frame spawner (keeps any existing photos/text, replaces prior layout\n      // frames). Applies to the targeted page(s) of the current spread, or the\n      // active cover face when in cover mode.\n      function applyTo(id) {\n        if (typeof builderMode !== \"undefined\" && builderMode === \"cover\") {\n          const zone = (typeof activeCoverZone !== \"undefined\" && activeCoverZone === \"back\") ? \"back\" : \"front\";\n          _mshApplyCoverLayoutDirect(zone, id);\n          return;\n        }\n        const lp = editor.currentSpread * 2 + 1;\n        const rp = editor.currentSpread * 2 + 2;\n        const targets = target === \"both\" ? [lp, rp] : target === \"right\" ? [rp] : [lp];\n        try { pushUndoState(); } catch (e) {}\n        targets.forEach((p) => {\n          if (p > editor.totalPages) return;\n          if (typeof isLockedPage === \"function\" && isLockedPage(p)) return;\n          if (id && TEMPLATE_DEFS[id]) _dmbSpawnLayoutFrames(p, id);\n          else ensureTemplateState(p, null);\n        });\n        renderSpread(); renderThumbsRail(); renderPhotoTray();\n      }\n\n      // MOBILE only: size every thumbnail to a TRUE A4-portrait shape \u2014 the items\n      // fill the tray height (CSS height:100%), so we read that real height and set\n      // each item's width = height / 1.4142. Avoids CSS aspect-ratio (iOS Safari\n      // mishandles it on flex items). Desktop uses CSS aspect-ratio in a grid.\n      const A4 = 1.41421;\n      function sizeItems() {\n        if (!mobile) return;\n        const items = trayEl.querySelectorAll(\".dmb-msh-lay-item\");\n        if (!items.length) return;\n        // Compute from the TRAY's own height (stable once the panel is visible) \u2014\n        // reading the mini's clientHeight lagged during layout settle and left the\n        // thumbnails too narrow. Item is border-box with 5px padding all round, so\n        // the inner page = trayH - 10; width = inner/A4 + 10 \u2192 true A4 portrait.\n        const h = trayEl.clientHeight;\n        if (!h) { requestAnimationFrame(sizeItems); return; }\n        const itemW = Math.round((h - 10) / A4 + 10);\n        items.forEach((it) => { it.style.width = itemW + \"px\"; });\n      }\n      if (mobile) window._mshSizeLayoutItems = sizeItems;\n      // The tray's height only settles after the tab becomes visible / the panel\n      // reflows, so a single early sizeItems() leaves stale (too-narrow) widths.\n      // A ResizeObserver re-sizes the thumbnails to the true A4 shape whenever the\n      // tray's box changes (tab open, rotate, keyboard) \u2014 self-healing.\n      if (mobile && window.ResizeObserver) {\n        try { new ResizeObserver(() => { try { sizeItems(); } catch (e) {} }).observe(trayEl); } catch (e) {}\n      }\n\n      function renderTray() {\n        trayEl.innerHTML = \"\";\n        idsFor(activeCat).forEach((id) => {\n          const item = document.createElement(\"button\");\n          item.type = \"button\"; item.className = \"dmb-msh-lay-item\";\n          item.title = (typeof TEMPLATE_NAMES !== \"undefined\" && TEMPLATE_NAMES[id]) || id;\n          const mini = document.createElement(\"div\");\n          mini.className = \"dmb-template-mini\";\n          renderTemplateMini(mini, id);\n          item.appendChild(mini);\n          item.addEventListener(\"click\", () => applyTo(id));\n          trayEl.appendChild(item);\n        });\n        if (mobile) requestAnimationFrame(sizeItems);\n      }\n      renderTray();\n      if (mobile) {\n        // Keep the A4 proportion correct after rotate / viewport changes.\n        window.addEventListener(\"resize\", () => requestAnimationFrame(sizeItems));\n        window.addEventListener(\"orientationchange\", () => setTimeout(sizeItems, 350));\n      }\n    } catch (e) {}\n  }\n\n  // Phones: build the mobile tray once, lazily (its DOM persists across tab shows).\n  let _mshLayBuilt = false;\n  function _mshBuildLayoutsPanel(target) {\n    if (!(window.matchMedia && window.matchMedia(\"(max-width: 749px), (orientation: portrait) and (pointer: coarse) and (max-width: 1024px)\").matches)) return;\n    // Build once for a plain tab open, but ALWAYS rebuild when a specific side is\n    // requested (from a page's \"Choose layout\") so the Left/Right toggle pre-selects it.\n    if (_mshLayBuilt && !target) return;\n    _dmbRenderLayoutsPanel({ mobile: true, defaultTarget: target || \"left\" });\n    _mshLayBuilt = true;\n  }\n\n  // Desktop: (re)build the inline grid each time Layouts is opened, so the target\n  // reflects which page (or the far-left menu) triggered it.\n  function _dmbBuildDesktopLayoutsPanel(defaultTarget) {\n    if (window.matchMedia && window.matchMedia(\"(max-width: 749px), (orientation: portrait) and (pointer: coarse) and (max-width: 1024px)\").matches) return;\n    _dmbRenderLayoutsPanel({ mobile: false, defaultTarget: defaultTarget || \"left\" });\n  }\n\n  function renderSpread() {\n    // Remember WHICH VIEW this is. The snapshot already carries editor.currentSpread\n    // and builderMode, but nothing was saving on NAVIGATION - every _dmbAutoPersist\n    // call sat on an edit path. So a refresh returned the customer to wherever they\n    // last TYPED rather than wherever they were looking, which on a long book means a\n    // completely different spread, or the cover. Debounced at 600ms, and\n    // _dmbWriteProgressState ignores an empty editor, so this cannot overwrite good\n    // state with a half-built one during a restore. (2026-10-12)\n    try { _dmbAutoPersist(); } catch (e) {}\n    // Level 2: on a new spread (phones, large books), free the full-res bytes of\n    // photos not near the current view \u2014 keeps RAM flat; released photos re-fetch\n    // from R2 on demand. No-op on desktop / small books / when photos aren't on R2.\n    _dmbMaybeReleaseForSpread();\n    // Hide the logo watermark \u2014 it belongs only on the back cover\n    document.querySelectorAll(\".dmb-logo-watermark\").forEach((el) => { el.style.display = \"none\"; });\n    // Remove any orphaned fixed-position rotation handles from a previous spread render.\n    // These are appended to document.body to escape overflow:hidden and are not destroyed\n    // when the spread DOM is rebuilt \u2014 so we sweep them explicitly before each re-render.\n    document.querySelectorAll(\".dmb-map-rot-float\").forEach((el) => el.remove());\n    if (!spreadCanvas || !spreadIndexEl || !prevSpreadBtn || !nextSpreadBtn) return;\n\n    const selectedPages = Number(document.getElementById(\"pages\")?.value || 0);\n    // Include any extra spreads the customer added (+2 pages / +1 spread each),\n    // otherwise this recompute would wipe them out on every re-render \u2014 leaving\n    // the \"Spread X / Y\" total stale and the Next button unable to reach them.\n    const _extraSpreads = editor.extraSpreads || 0;\n    if (selectedPages > 0) {\n      // Magazines: fixed per-size layout (page 0 + N spreads + trailing single) PLUS\n      // any spreads the customer added. Those used to be ignored here, which is why\n      // inserting one in a magazine appeared to do nothing: this recompute put the\n      // page count straight back. They are added in TWOS (see _dmbMagStep), so the\n      // trailing single page stays trailing and no half sheet is ever printed.\n      const _plan = _dmbPagePlan(selectedPages);\n      // An ODD count on a magazine cannot be real - it can only be left over from\n      // that old behaviour, where an inserted spread was counted and then dropped.\n      // Round it down so nobody is charged for pages that are not there.\n      if (window.DMB_MAGAZINE_MODE && (_extraSpreads % 2)) {\n        editor.extraSpreads = _extraSpreads - 1;\n      }\n      const _extra = editor.extraSpreads || 0;\n      editor.totalPages = _plan.totalPages + _extra * 2;\n      editor.editablePages = Math.max(0, editor.totalPages - 1);\n      editor.totalSpreads = _plan.totalSpreads + _extra;\n    } else {\n      editor.totalPages = 0;\n      editor.editablePages = 0;\n      editor.totalSpreads = 1;\n    }\n    if (editor.currentSpread > editor.totalSpreads - 1) editor.currentSpread = 0;\n\n    // Mark the currently-displayed pages dirty so PDF preview re-renders (2s debounce)\n    if (editor.currentSpread === -1) {\n      _dmbPvMarkDirty(0);\n    } else {\n      _dmbPvMarkDirty(editor.currentSpread * 2 + 1);\n      _dmbPvMarkDirty(editor.currentSpread * 2 + 2);\n    }\n\n    spreadCanvas.innerHTML = \"\";\n\n    // \u2500\u2500 Page 0 (special single page \u2014 before all spreads) \u2500\u2500\n    if (editor.currentSpread === -1) {\n      spreadIndexEl.textContent = \"Spread 0 / \" + editor.totalSpreads;\n      const page0El = makeA4Page(0, \"L\");\n\n      const dropOverlay0 = document.createElement(\"div\");\n      dropOverlay0.className = \"dmb-spread-drag-overlay\";\n      const zone0 = document.createElement(\"div\");\n      zone0.className = \"dmb-spread-drop-zone\";\n      zone0.style.flex = \"1\";\n      const zone0Lbl = document.createElement(\"div\");\n      zone0Lbl.className = \"dmb-drop-zone-label\";\n      zone0Lbl.textContent = \"Drop photo here\";\n      zone0.appendChild(zone0Lbl);\n      dropOverlay0.appendChild(zone0);\n\n      const pagesRow0 = document.createElement(\"div\");\n      pagesRow0.style.cssText = \"position:relative;display:flex;gap:0;flex-shrink:0;\";\n      pagesRow0.appendChild(page0El);\n      pagesRow0.appendChild(dropOverlay0);\n\n      let ec0 = 0;\n      pagesRow0.addEventListener(\"dragenter\", (e) => {\n        if (!getDragPayload(e)) return;\n        ec0++;\n        dropOverlay0.classList.add(\"active\");\n      });\n      pagesRow0.addEventListener(\"dragleave\", (e) => {\n        if (e.relatedTarget && pagesRow0.contains(e.relatedTarget)) return;\n        if (--ec0 <= 0) { ec0 = 0; dropOverlay0.classList.remove(\"active\"); }\n      });\n      pagesRow0.addEventListener(\"dragover\", (e) => { if (getDragPayload(e)) e.preventDefault(); });\n      pagesRow0.addEventListener(\"drop\", async (e) => {\n        e.preventDefault();\n        ec0 = 0; dropOverlay0.classList.remove(\"active\");\n        const payload = getDragPayload(e);\n        if (!payload) return;\n        await handleSpreadZoneDrop(\"left\", payload, 0, null, -1);\n      });\n\n      const lbl0 = document.createElement(\"div\");\n      lbl0.className = \"dmb-page-label\";\n      lbl0.textContent = \"Spread 0\";\n      lbl0.style.width = getPageDims().w + \"px\";\n      const labelsRow0 = document.createElement(\"div\");\n      labelsRow0.style.display = \"flex\";\n      labelsRow0.appendChild(lbl0);\n\n      const pagesMain0 = document.createElement(\"div\");\n      pagesMain0.style.cssText = \"display:flex;flex-direction:column;gap:6px;\";\n      pagesMain0.appendChild(labelsRow0);\n      pagesMain0.appendChild(pagesRow0);\n\n      const outerRow0 = document.createElement(\"div\");\n      outerRow0.style.cssText = \"display:flex;gap:8px;align-items:flex-start;\";\n      outerRow0.appendChild(buildActionBtns(0));\n      outerRow0.appendChild(pagesMain0);\n\n      spreadCanvas.appendChild(outerRow0);\n      forceSpreadLayout();\n      if (spreadViewport) spreadViewport.scrollLeft = 0;\n      prevSpreadBtn.disabled = false;   // Prev on Spread 0 goes back to the cover\n      nextSpreadBtn.disabled = editor.totalSpreads <= 0;\n      return;\n    }\n\n    const spreadNum = editor.currentSpread + 1;\n    spreadIndexEl.textContent = `Spread ${spreadNum} / ${editor.totalSpreads}`;\n\n    const leftPage = editor.currentSpread * 2 + 1;\n    const rightPage = editor.currentSpread * 2 + 2;\n    const si = editor.currentSpread;\n\n    // \u2500\u2500 Pages row (position:relative, pages touch \u2014 no gap) \u2500\u2500\n    const pagesRow = document.createElement(\"div\");\n    pagesRow.style.position = \"relative\";\n    pagesRow.style.display = \"flex\";\n    pagesRow.style.gap = \"0\";\n    pagesRow.style.flexShrink = \"0\";\n    pagesRow.dataset.spreadRow = \"true\";\n\n    // \u2500\u2500 Labels row (label per page, each 270px wide to align exactly with its page) \u2500\u2500\n    const labelsRow = document.createElement(\"div\");\n    labelsRow.style.display = \"flex\";\n    labelsRow.style.gap = \"0\";\n\n    const leftPageEl = (leftPage <= editor.totalPages) ? makeA4Page(leftPage, \"L\") : null;\n    if (leftPageEl) {\n      pagesRow.appendChild(leftPageEl);\n      const lbl = document.createElement(\"div\");\n      lbl.className = \"dmb-page-label\";\n      lbl.textContent = `Page ${leftPage} (L)`;\n      lbl.style.width = getPageDims().w + \"px\";\n      labelsRow.appendChild(lbl);\n    }\n\n    const rightPageEl = (rightPage <= editor.totalPages) ? makeA4Page(rightPage, \"R\") : null;\n    if (rightPageEl) {\n      pagesRow.appendChild(rightPageEl);\n      const lbl = document.createElement(\"div\");\n      lbl.className = \"dmb-page-label\";\n      lbl.textContent = `Page ${rightPage} (R)`;\n      lbl.style.width = getPageDims().w + \"px\";\n      labelsRow.appendChild(lbl);\n    }\n\n\n    // \u2500\u2500 Spine gradient (absolute, overlaid on the seam between the two pages) \u2500\u2500\n    const leftHasContent = leftPage <= editor.totalPages && pageHasAnyContent(leftPage);\n    const rightHasContent = rightPage <= editor.totalPages && pageHasAnyContent(rightPage);\n\n    if (leftPageEl && rightPageEl) {\n      const spine = document.createElement(\"div\");\n      spine.className = \"dmb-spread-spine\" +\n        (leftHasContent ? \" left-filled\" : \"\") +\n        (rightHasContent ? \" right-filled\" : \"\");\n      // Center the spine on the seam between the two pages. CSS defaults to the\n      // portrait seam (259px); override with the real page width so it stays\n      // centered in landscape too (seam = page width, minus half the 22px spine).\n      spine.style.left = (getPageDims().w - 11) + \"px\";\n      const spineL = document.createElement(\"div\");\n      spineL.className = \"dmb-spread-spine-left\";\n      const spineR = document.createElement(\"div\");\n      spineR.className = \"dmb-spread-spine-right\";\n      spine.appendChild(spineL);\n      spine.appendChild(spineR);\n      pagesRow.appendChild(spine);\n    }\n\n    // \u2500\u2500 Drop overlay (absolute, covers pagesRow exactly) \u2500\u2500\n    const dropOverlay = document.createElement(\"div\");\n    dropOverlay.className = \"dmb-spread-drag-overlay\";\n\n    function makeZone(label, disabled) {\n      const zone = document.createElement(\"div\");\n      zone.className = \"dmb-spread-drop-zone\" + (disabled ? \" disabled\" : \"\");\n      const lbl = document.createElement(\"div\");\n      lbl.className = \"dmb-drop-zone-label\";\n      lbl.textContent = label;\n      zone.appendChild(lbl);\n      return zone;\n    }\n\n    const leftZone = makeZone(isLockedPage(leftPage) ? \"Locked\" : \"Left page\", isLockedPage(leftPage));\n    const centerZone = makeZone(\"Full spread\", isLockedPage(leftPage) || !rightPageEl);\n    const rightZone = makeZone(\"Right page\", !rightPageEl);\n    dropOverlay.appendChild(leftZone);\n    dropOverlay.appendChild(centerZone);\n    dropOverlay.appendChild(rightZone);\n    pagesRow.appendChild(dropOverlay);\n\n    // \u2500\u2500 Drag events on pagesRow \u2500\u2500\n    // Overlay (z-index:50) is pointer-events:auto \u2014 zones receive drops for background area.\n    // Template frames (z-index:60) and free frames (z-index:60) are ABOVE the overlay,\n    // so they receive drops directly via their own stopPropagation handlers.\n    let enterCount = 0;\n    function clearZoneHighlights() {\n      leftZone.classList.remove(\"drag-over\");\n      centerZone.classList.remove(\"drag-over\");\n      rightZone.classList.remove(\"drag-over\");\n    }\n    pagesRow.addEventListener(\"dragenter\", (e) => {\n      if (!getDragPayload(e)) return;\n      enterCount++;\n      dropOverlay.classList.add(\"active\");\n    });\n    pagesRow.addEventListener(\"dragleave\", (e) => {\n      if (e.relatedTarget && pagesRow.contains(e.relatedTarget)) return;\n      if (--enterCount <= 0) { enterCount = 0; dropOverlay.classList.remove(\"active\"); clearZoneHighlights(); }\n    });\n    pagesRow.addEventListener(\"dragover\", (e) => { if (getDragPayload(e)) e.preventDefault(); });\n\n    // Zone-level highlighting and drop handling (original approach \u2014 reliable)\n    [leftZone, centerZone, rightZone].forEach(zone => {\n      if (zone.classList.contains(\"disabled\")) return;\n      let zc = 0;\n      zone.addEventListener(\"dragenter\", (e) => { e.preventDefault(); e.stopPropagation(); if (++zc > 0) zone.classList.add(\"drag-over\"); });\n      zone.addEventListener(\"dragleave\", () => { if (--zc <= 0) { zc = 0; zone.classList.remove(\"drag-over\"); } });\n      zone.addEventListener(\"dragover\",  (e) => { e.preventDefault(); e.stopPropagation(); });\n    });\n\n    leftZone.addEventListener(\"drop\", async (e) => {\n      e.preventDefault(); e.stopPropagation();\n      enterCount = 0; dropOverlay.classList.remove(\"active\"); clearZoneHighlights();\n      const payload = getDragPayload(e);\n      if (!payload || isLockedPage(leftPage)) return;\n      await handleSpreadZoneDrop(\"left\", payload, leftPage, rightPage, si);\n    });\n    centerZone.addEventListener(\"drop\", async (e) => {\n      e.preventDefault(); e.stopPropagation();\n      enterCount = 0; dropOverlay.classList.remove(\"active\"); clearZoneHighlights();\n      const payload = getDragPayload(e);\n      if (!payload || isLockedPage(leftPage) || !rightPageEl) return;\n      await handleSpreadZoneDrop(\"spread\", payload, leftPage, rightPage, si);\n    });\n    rightZone.addEventListener(\"drop\", async (e) => {\n      e.preventDefault(); e.stopPropagation();\n      enterCount = 0; dropOverlay.classList.remove(\"active\"); clearZoneHighlights();\n      const payload = getDragPayload(e);\n      if (!payload || !rightPageEl) return;\n      await handleSpreadZoneDrop(\"right\", payload, leftPage, rightPage, si);\n    });\n\n    // Fallback: pagesRow catches any drops that reach it (e.g. spine area)\n    pagesRow.addEventListener(\"drop\", async (e) => {\n      e.preventDefault();\n      enterCount = 0; dropOverlay.classList.remove(\"active\"); clearZoneHighlights();\n      const payload = getDragPayload(e);\n      if (!payload) return;\n      const pr = pagesRow.getBoundingClientRect();\n      const dx = e.clientX - pr.left;\n      const w  = pr.width;\n      let zone = !rightPageEl ? \"left\" : dx < w / 3 ? \"left\" : dx > w * 2 / 3 ? \"right\" : \"spread\";\n      if (zone === \"left\" && isLockedPage(leftPage)) return;\n      if (zone !== \"left\" && !rightPageEl) return;\n      await handleSpreadZoneDrop(zone, payload, leftPage, rightPage, si);\n    });\n\n    // \u2500\u2500 Action buttons helper \u2500\u2500\n    function buildActionBtns(pNum) {\n      const actions = document.createElement(\"div\");\n      actions.className = \"dmb-page-outer-actions\";\n\n      const clearBtn = document.createElement(\"button\");\n      clearBtn.type = \"button\";\n      clearBtn.className = \"dmb-page-action-btn\";\n      clearBtn.textContent = \"Clear page\";\n      clearBtn.addEventListener(\"click\", async () => {\n        if (!pageHasAnyContent(pNum)) return;\n        const choice = await showChoiceDialog(\n          \"Clear page\",\n          \"What would you like to remove?\",\n          [\n            { id: \"photos\", label: \"Remove photos only (keep layout & text)\" },\n            { id: \"all\", label: \"Remove everything\" },\n            { id: \"cancel\", label: \"Cancel\" }\n          ]\n        );\n        if (!choice || choice === \"cancel\") return;\n        pushUndoState(\"Clear Page\");\n        if (choice === \"photos\") clearPagePhotosOnly(pNum);\n        else clearPageAllLayouts(pNum);\n        renderSpread(); renderThumbsRail(); renderPhotoTray();\n      });\n\n      const layoutBtn = document.createElement(\"button\");\n      layoutBtn.type = \"button\";\n      layoutBtn.className = \"dmb-page-action-btn\";\n      layoutBtn.textContent = \"Choose layout\";\n      layoutBtn.addEventListener(\"click\", async () => {\n        const psi = Math.floor((pNum - 1) / 2);\n        if (editor.spreadPhoto?.[psi]) {\n          const choice = await showChoiceDialog(\n            \"Full spread photo active\",\n            \"This page has a full-spread photo. What would you like to do?\",\n            [\n              { id: \"keep\", label: \"Keep spread photo, pick layout on top\" },\n              { id: \"remove\", label: \"Remove spread photo, pick layout\" },\n              { id: \"cancel\", label: \"Cancel\" }\n            ]\n          );\n          if (!choice || choice === \"cancel\") return;\n          if (choice === \"remove\") { pushUndoState(); editor.spreadPhoto[psi] = null; renderSpread(); return; }\n        }\n        // Open the inline Layouts panel with the target pre-set to THIS page\n        // (odd page = left, even = right of the spread), then activate the tab.\n        const _layTarget = (pNum % 2 === 1) ? \"left\" : \"right\";\n        window._dmbPendingLayoutTarget = _layTarget;\n        document.querySelector('.dmb-icon-tab[data-tab=\"layouts\"]')?.click();\n        // Robustness: build the panel directly with the correct side too, so the Left/Right\n        // toggle is pre-selected even if the tab was already active (a synthetic re-click\n        // wouldn't rebuild it) \u2014 desktop and phone.\n        try {\n          if (window.matchMedia && window.matchMedia(\"(max-width: 749px), (orientation: portrait) and (pointer: coarse) and (max-width: 1024px)\").matches) {\n            // Open the shell's Layouts tab (the panel shows only while its tab is\n            // active), then build it with the page side.\n            document.querySelector('.dmb-msh-tab[data-tab=\"layouts\"]')?.click();\n            if (typeof _mshBuildLayoutsPanel === \"function\") _mshBuildLayoutsPanel(_layTarget);\n          } else if (typeof _dmbBuildDesktopLayoutsPanel === \"function\") {\n            _dmbBuildDesktopLayoutsPanel(_layTarget);\n          }\n        } catch (e) {}\n      });\n\n      // \u2500\u2500 Duplicate this page's layout onto another page \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n      const dupPageBtn = document.createElement(\"button\");\n      dupPageBtn.type = \"button\";\n      dupPageBtn.className = \"dmb-page-action-btn\";\n      dupPageBtn.textContent = \"Duplicate page\";\n      dupPageBtn.addEventListener(\"click\", async () => {\n        if (!pageHasAnyContent(pNum)) { await showChoiceDialog(\"Duplicate page\", \"This page is empty \u2014 there's nothing to copy yet.\", [{ id: \"ok\", label: \"OK\" }]); return; }\n        _dmbOpenDuplicatePageModal(pNum);\n      });\n\n      const textBtn = document.createElement(\"button\");\n      textBtn.type = \"button\";\n      textBtn.className = \"dmb-page-action-btn\";\n      textBtn.textContent = \"Add text\";\n      textBtn.addEventListener(\"click\", (e) => {\n        // Stop this click from bubbling to the document click-out handler \u2014 it\n        // would otherwise see the brand-new (empty) text box and delete it on the\n        // same click, so the button appeared to do nothing.\n        e.stopPropagation();\n        // Set lastToolbarSide so addDefaultText targets the correct page body\n        lastToolbarSide = (pNum % 2 === 0) ? \"right\" : \"left\";\n        if (canvasTextBtn) canvasTextBtn.click();\n      });\n\n      const frameBtn = document.createElement(\"button\");\n      frameBtn.type = \"button\";\n      frameBtn.className = \"dmb-page-action-btn\";\n      frameBtn.textContent = \"Add photo frame\";\n      frameBtn.addEventListener(\"click\", () => {\n        lastToolbarSide = (pNum % 2 === 0) ? \"right\" : \"left\";\n        if (canvasPhotoBtn) canvasPhotoBtn.click();\n      });\n\n      const polaroidBtn = document.createElement(\"button\");\n      polaroidBtn.type = \"button\";\n      polaroidBtn.className = \"dmb-page-action-btn\";\n      polaroidBtn.textContent = \"Add polaroid\";\n      polaroidBtn.addEventListener(\"click\", () => {\n        lastToolbarSide = (pNum % 2 === 0) ? \"right\" : \"left\";\n        dmbAddFrameToActiveTarget({ polaroid: true });\n      });\n\n      // \u2500\u2500 SHAPES BUTTON (pages) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n      // Sets lastToolbarSide so insertShapeIntoActivePage targets this page.\n      // Then opens the floating shape picker panel (same as Shapes toolbar btn).\n      // WARNING: Do NOT redirect to canvasShapeBtn.click() alone \u2014 the picker\n      // panel toggle code also repositions the panel; call that directly instead.\n      const shapesBtn = document.createElement(\"button\");\n      shapesBtn.type = \"button\";\n      shapesBtn.className = \"dmb-page-action-btn\";\n      shapesBtn.textContent = \"Add shape\";\n      shapesBtn.addEventListener(\"click\", () => {\n        lastToolbarSide = (pNum % 2 === 0) ? \"right\" : \"left\";\n        // Open the shape picker panel (re-use the canvasShapeBtn click logic)\n        if (canvasShapeBtn) canvasShapeBtn.click();\n      });\n\n      // \u2500\u2500 MAP BUTTON (pages) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n      // Same contract as the shape button: stamp lastToolbarSide for THIS page so\n      // getActivePageNumberForMap targets it, then open the picker. Without a\n      // per-page entry point the only way in was the top ribbon, which left the\n      // target page to whatever was touched last.\n      const mapBtn = document.createElement(\"button\");\n      mapBtn.type = \"button\";\n      mapBtn.className = \"dmb-page-action-btn\";\n      mapBtn.textContent = \"Add map\";\n      mapBtn.addEventListener(\"click\", () => {\n        lastToolbarSide = (pNum % 2 === 0) ? \"right\" : \"left\";\n        if (typeof dmbOpenMapModal === \"function\") dmbOpenMapModal();\n        _dmbMapTargetPage = pNum;   // AFTER open - dmbOpenMapModal clears it\n      });\n\n      // \u2500\u2500 Page background colour \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n      // A colour swatch button that opens a native colour picker.\n      // Shows \"\u00D7\" when a custom colour is set so it can be cleared.\n      // WARNING: keep the hidden <input type=\"color\"> INSIDE the button so\n      // clicking the button triggers the colour picker on all browsers.\n      const bgBtn = document.createElement(\"button\");\n      bgBtn.type = \"button\";\n      bgBtn.className = \"dmb-page-action-btn dmb-page-bg-btn\";\n      bgBtn.title = \"Page background colour\";\n\n      const bgColorInput = document.createElement(\"input\");\n      bgColorInput.type = \"color\";\n      bgColorInput.className = \"dmb-page-bg-input\";\n      bgColorInput.value = editor.pageBackgrounds?.[pNum] || \"#ffffff\";\n      bgColorInput.tabIndex = -1;\n      bgColorInput.setAttribute(\"aria-hidden\", \"true\");\n\n      const bgSwatch = document.createElement(\"span\");\n      bgSwatch.className = \"dmb-page-bg-swatch\";\n      bgSwatch.style.background = editor.pageBackgrounds?.[pNum] || \"#ffffff\";\n\n      const bgLabel = document.createElement(\"span\");\n      bgLabel.textContent = \"Background\";\n\n      // \u2500\u2500 Circle-X clear background colour (Fix 1) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n      const bgClear = document.createElement(\"span\");\n      bgClear.className = \"dmb-page-bg-clear\";\n      bgClear.innerHTML = `<svg viewBox=\"0 0 24 24\" width=\"11\" height=\"11\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M8 8l8 8M16 8l-8 8\"/></svg>`;\n      bgClear.title = \"Remove background colour\";\n      bgClear.style.display = editor.pageBackgrounds?.[pNum] ? \"inline\" : \"none\";\n\n      // LIVE update \u2014 used while dragging in the native picker ('input' fires\n      // continuously). It updates the visible page background + swatch directly,\n      // WITHOUT re-rendering. (A re-render rebuilds this action rail and destroys\n      // the <input> the picker is attached to, which closes the picker mid-drag and\n      // applies whatever colour the finger was on \u2014 the reported bug.)\n      // Take the undo snapshot ONCE per picker session, on the first change and BEFORE the\n      // background is actually mutated \u2014 so undo restores the colour from before this edit\n      // (live scrubbing during the drag must not each create their own history entry).\n      let _bgUndoTaken = false;\n      function _snapBgOnce() { if (!_bgUndoTaken) { pushUndoState(\"Page Background\"); _bgUndoTaken = true; } }\n      function _liveBgColor(color) {\n        if (!editor.pageBackgrounds) editor.pageBackgrounds = {};\n        if (color && color.toLowerCase() !== \"#ffffff\") editor.pageBackgrounds[pNum] = color;\n        else delete editor.pageBackgrounds[pNum];\n        bgSwatch.style.background = color || \"#ffffff\";\n        bgClear.style.display = editor.pageBackgrounds?.[pNum] ? \"inline\" : \"none\";\n        const bodyEl = document.querySelector('[data-page-body][data-page-number=\"' + pNum + '\"]');\n        if (bodyEl) bodyEl.style.background = color || \"#fcfcfc\";\n      }\n      // COMMIT \u2014 used when the picker closes ('change') or on the clear button:\n      // record the colour + do the full re-render (safe now \u2014 the picker is closed).\n      function _commitBgColor(color) {\n        _snapBgOnce();   // covers the change-only case (some browsers skip 'input')\n        _liveBgColor(color);\n        bgColorInput.value = color || \"#ffffff\";\n        renderBuilderCanvas();\n        renderThumbsRail();\n        _bgUndoTaken = false;   // end of this edit session\n      }\n\n      bgColorInput.addEventListener(\"input\", () => { _snapBgOnce(); _liveBgColor(bgColorInput.value); });\n      bgColorInput.addEventListener(\"change\", () => _commitBgColor(bgColorInput.value));\n\n      // Clear button: stop propagation so it doesn't re-open the colour picker\n      bgClear.addEventListener(\"click\", (e) => {\n        e.stopPropagation();\n        _commitBgColor(\"#ffffff\");\n      });\n\n      bgBtn.appendChild(bgColorInput);\n      bgBtn.appendChild(bgSwatch);\n      bgBtn.appendChild(bgLabel);\n      bgBtn.appendChild(bgClear);\n\n      actions.appendChild(clearBtn);\n      actions.appendChild(layoutBtn);\n      actions.appendChild(dupPageBtn);\n      actions.appendChild(textBtn);\n      actions.appendChild(frameBtn);\n      actions.appendChild(polaroidBtn);\n      actions.appendChild(shapesBtn);\n      actions.appendChild(mapBtn);\n      actions.appendChild(bgBtn);\n      // Recently-used colors for this page's background (strip below the actions).\n      try { dmbEnhanceColorInput(bgColorInput, actions); } catch (e) {}\n      return actions;\n    }\n\n    // \u2500\u2500 Outer row: [left actions] + [labels stacked above pages] + [right actions] \u2500\u2500\n    const pagesMain = document.createElement(\"div\");\n    pagesMain.style.display = \"flex\";\n    pagesMain.style.flexDirection = \"column\";\n    pagesMain.style.gap = \"6px\";\n    pagesMain.appendChild(labelsRow);\n    pagesMain.appendChild(pagesRow);\n\n    const outerRow = document.createElement(\"div\");\n    outerRow.style.display = \"flex\";\n    outerRow.style.gap = \"8px\";\n    outerRow.style.alignItems = \"flex-start\";\n\n    if (leftPageEl && !isLockedPage(leftPage)) outerRow.appendChild(buildActionBtns(leftPage));\n    outerRow.appendChild(pagesMain);\n    if (rightPageEl && !isLockedPage(rightPage)) outerRow.appendChild(buildActionBtns(rightPage));\n\n    spreadCanvas.appendChild(outerRow);\n    forceSpreadLayout();\n    if (spreadViewport) spreadViewport.scrollLeft = 0;\n    prevSpreadBtn.disabled = editor.currentSpread < 0;\n    nextSpreadBtn.disabled = editor.currentSpread >= editor.totalSpreads - 1;\n  }\n\n  function showLayoutsForPage(pageNumber) {\n    const panel = document.querySelector('.dmb-side-panel[data-panel=\"layouts\"]');\n    if (!panel) return;\n    panel.innerHTML = \"\";\n    const section = document.createElement(\"div\");\n    section.className = \"dmb-section\";\n    section.style.cssText = \"flex:1 1 0;min-height:0;display:flex;flex-direction:column;overflow:hidden;\";\n    const title = document.createElement(\"div\");\n    title.className = \"dmb-mini-label\";\n    title.textContent = `Layouts \u2014 Page ${pageNumber}`;\n    section.appendChild(title);\n    const listWrap = document.createElement(\"div\");\n    listWrap.style.cssText = \"overflow-y:auto;flex:1 1 0;min-height:0;margin-top:6px;\";\n    const list = document.createElement(\"div\");\n    list.className = \"dmb-template-list\";\n    const addItem = (id, name, subtitle, isDefault = false) => {\n      const btn = document.createElement(\"button\");\n      btn.type = \"button\";\n      btn.className = \"dmb-template-item\";\n      const currentId = getPageTemplateId(pageNumber);\n      if ((isDefault && !currentId) || (!isDefault && currentId === id)) btn.classList.add(\"active\");\n      const row = document.createElement(\"div\");\n      row.className = \"dmb-template-item-row\";\n      const mini = document.createElement(\"div\");\n      mini.className = \"dmb-template-mini\";\n      if (isDefault) {\n        const a = document.createElement(\"div\");\n        a.className = \"dmb-template-mini-frame\";\n        a.style.cssText = \"left:0;top:0;width:100%;height:49%\";\n        const b = document.createElement(\"div\");\n        b.className = \"dmb-template-mini-frame\";\n        b.style.cssText = \"left:0;top:51%;width:100%;height:49%\";\n        mini.appendChild(a);\n        mini.appendChild(b);\n      } else {\n        renderTemplateMini(mini, id);\n      }\n      const txt = document.createElement(\"div\");\n      txt.style.minWidth = \"0\";\n      const nm = document.createElement(\"div\");\n      nm.className = \"dmb-template-item-name\";\n      nm.textContent = name;\n      const sub = document.createElement(\"div\");\n      sub.className = \"dmb-template-item-sub\";\n      sub.textContent = subtitle;\n      txt.appendChild(nm);\n      txt.appendChild(sub);\n      row.appendChild(mini);\n      row.appendChild(txt);\n      btn.appendChild(row);\n      btn.addEventListener(\"click\", async (e) => {\n        e.preventDefault();\n        e.stopPropagation();\n        if (isDefault) await setPageTemplateWithConfirm(pageNumber, null);\n        else await setPageTemplateWithConfirm(pageNumber, id);\n      });\n      list.appendChild(btn);\n    };\n    addItem(null, \"Default Layout\", \"1 photo full / 2-photo split\", true);\n    Object.keys(TEMPLATE_DEFS).forEach((id) => {\n      addItem(id, TEMPLATE_NAMES[id] || id.toUpperCase(), `${TEMPLATE_DEFS[id].length} frame(s)`);\n    });\n    listWrap.appendChild(list);\n    section.appendChild(listWrap);\n    panel.appendChild(section);\n    document.querySelector('.dmb-icon-tab[data-tab=\"layouts\"]')?.click();\n  }\n\n  function showLayoutsForCoverZone(zone) {\n    // Use a dedicated floating modal so we don't touch the shared layouts panel\n    const existing = document.getElementById(\"dmb-cover-layout-modal\");\n    if (existing) existing.remove();\n\n    const overlay = document.createElement(\"div\");\n    overlay.id = \"dmb-cover-layout-modal\";\n    overlay.style.cssText = \"position:fixed;inset:0;background:rgba(0,0,0,0.6);z-index:999999;display:flex;align-items:center;justify-content:center;\";\n\n    const modal = document.createElement(\"div\");\n    modal.style.cssText = \"background:#1e1e1e;border:1px solid #444;border-radius:16px;padding:20px;width:340px;max-height:80vh;display:flex;flex-direction:column;box-shadow:0 20px 60px rgba(0,0,0,0.7);\";\n\n    const header = document.createElement(\"div\");\n    header.style.cssText = \"display:flex;align-items:center;justify-content:space-between;margin-bottom:12px;\";\n    const ttl = document.createElement(\"div\");\n    ttl.style.cssText = \"font-weight:900;font-size:15px;color:#fff;\";\n    ttl.textContent = `Cover Layout \u2014 ${zone === \"front\" ? \"Front\" : \"Back\"}`;\n    const closeBtn = document.createElement(\"button\");\n    closeBtn.type = \"button\";\n    closeBtn.textContent = \"\u2715\";\n    closeBtn.style.cssText = \"background:none;border:none;color:#aaa;font-size:18px;cursor:pointer;padding:0 4px;\";\n    closeBtn.addEventListener(\"click\", () => overlay.remove());\n    header.appendChild(ttl);\n    header.appendChild(closeBtn);\n    modal.appendChild(header);\n\n    const listWrap = document.createElement(\"div\");\n    listWrap.style.cssText = \"overflow-y:auto;flex:1;\";\n    const list = document.createElement(\"div\");\n    list.className = \"dmb-template-list\";\n\n    const currentId = zone === \"front\" ? coverState.frontLayout : coverState.backLayout;\n\n    const addItem = (id, name, subtitle, isNone = false) => {\n      const btn = document.createElement(\"button\");\n      btn.type = \"button\";\n      btn.className = \"dmb-template-item\";\n      if ((isNone && !currentId) || (!isNone && currentId === id)) btn.classList.add(\"active\");\n      const row = document.createElement(\"div\");\n      row.className = \"dmb-template-item-row\";\n      const mini = document.createElement(\"div\");\n      mini.className = \"dmb-template-mini\";\n      if (isNone) {\n        const a = document.createElement(\"div\");\n        a.className = \"dmb-template-mini-frame\";\n        a.style.cssText = \"left:0;top:0;width:100%;height:100%\";\n        mini.appendChild(a);\n      } else {\n        renderTemplateMini(mini, id);\n      }\n      const txt = document.createElement(\"div\");\n      txt.style.minWidth = \"0\";\n      const nm = document.createElement(\"div\");\n      nm.className = \"dmb-template-item-name\";\n      nm.textContent = name;\n      const sub = document.createElement(\"div\");\n      sub.className = \"dmb-template-item-sub\";\n      sub.textContent = subtitle;\n      txt.appendChild(nm);\n      txt.appendChild(sub);\n      row.appendChild(mini);\n      row.appendChild(txt);\n      btn.appendChild(row);\n      btn.addEventListener(\"click\", async (e) => {\n        e.preventDefault();\n        e.stopPropagation();\n        overlay.remove();\n        await setCoverLayoutWithConfirm(zone, isNone ? null : id);\n      });\n      list.appendChild(btn);\n    };\n\n    addItem(null, \"No Layout\", \"Remove layout\", true);\n    Object.keys(TEMPLATE_DEFS).forEach((id) => {\n      addItem(id, TEMPLATE_NAMES[id] || id.toUpperCase(), `${TEMPLATE_DEFS[id].length} frame(s)`);\n    });\n\n    listWrap.appendChild(list);\n    modal.appendChild(listWrap);\n    overlay.appendChild(modal);\n    overlay.addEventListener(\"click\", (e) => { if (e.target === overlay) overlay.remove(); });\n    document.body.appendChild(overlay);\n  }\n\n  // A cover face is 300 \u00D7 424 px on screen (.dmb-cover-face: width:300, aspect 1/1.414).\n  // A layout's TEMPLATE_DEFS slots are % of a FACE; convert to overlay px (the free-frame\n  // coordinate space) by offsetting with the face's left edge in the cover overlay.\n  const _DMB_COVER_FACE_W = 300;\n  const _DMB_COVER_FACE_H = 424;\n  function _dmbCoverFaceLeftPx(zone) {\n    if (zone === \"back\") return 0;\n    return window.DMB_MAGAZINE_MODE ? COVER_BACK_W : COVER_FRONT_X;   // front\n  }\n  // Spawn a cover layout as individual FREE photo-frame objects (targetType:\"cover\"),\n  // one per TEMPLATE_DEFS slot \u2014 exactly like the interior-page _dmbSpawnLayoutFrames.\n  // They then reuse the whole cover free-frame system: select / drag / resize / rotate /\n  // delete (incl. keyboard) / photo-drop / PDF export. Replaces this zone's PREVIOUS\n  // layout frames (fromLayout) but keeps manually-added frames, text, stickers.\n  function _dmbSpawnCoverLayoutFrames(zone, templateId, carryPhotoIds) {\n    const arr = ensureCoverObjects();\n    for (let i = arr.length - 1; i >= 0; i--) {\n      const o = arr[i];\n      if (o && o.type === \"photo-frame\" && o.fromLayout && (o.coverZone || \"front\") === zone) arr.splice(i, 1);\n    }\n    const slots = (templateId && TEMPLATE_DEFS[templateId]) || [];\n    const faceLeft = _dmbCoverFaceLeftPx(zone);\n    const carry = Array.isArray(carryPhotoIds) ? carryPhotoIds : [];\n    const now = Date.now();\n    slots.forEach((f, i) => {\n      const obj = newFrameObject({ targetType: \"cover\" });\n      obj.fromLayout = true;\n      obj.coverZone = zone;\n      obj.x = Math.round(faceLeft + (f.x / 100) * _DMB_COVER_FACE_W);\n      obj.y = Math.round((f.y / 100) * _DMB_COVER_FACE_H);\n      obj.w = Math.round((f.w / 100) * _DMB_COVER_FACE_W);\n      obj.h = Math.round((f.h / 100) * _DMB_COVER_FACE_H);\n      obj.z = now + i;                       // render clamps to >= minFrameZ (100001) on cover\n      if (carry[i]) obj.photoId = carry[i];\n      arr.push(obj);\n    });\n  }\n  // Convert any fixed-slot cover layout currently in coverState (e.g. from an applied\n  // predesigned template or an old saved design) into free frames, carrying each slot's\n  // photo. Then clear the fixed layout so the old locked-slot renderer no longer runs.\n  function _dmbConvertCoverFixedLayouts() {\n    [\"front\", \"back\"].forEach((zone) => {\n      const layoutId = zone === \"front\" ? coverState.frontLayout : coverState.backLayout;\n      if (!layoutId || !TEMPLATE_DEFS[layoutId]) return;\n      const framesMap = (zone === \"front\" ? coverState.frontFrames : coverState.backFrames) || {};\n      const carry = (TEMPLATE_DEFS[layoutId] || []).map((f) => framesMap[f.key] || null);\n      if (zone === \"front\") { coverState.frontLayout = null; coverState.frontFrames = {}; coverState.frontFrameRot = {}; coverState.frontFrameShapes = {}; }\n      else                  { coverState.backLayout  = null; coverState.backFrames  = {}; coverState.backFrameRot  = {}; coverState.backFrameShapes  = {}; }\n      _dmbSpawnCoverLayoutFrames(zone, layoutId, carry);\n    });\n  }\n\n  async function setCoverLayoutWithConfirm(zone, templateId) {\n    // Photos that could be preserved = this zone's current layout free-frames + any\n    // legacy fixed-slot photos still in coverState (old designs).\n    const legacyFrames = (zone === \"front\" ? coverState.frontFrames : coverState.backFrames) || {};\n    const existingFree = ensureCoverObjects().filter(\n      (o) => o && o.type === \"photo-frame\" && o.fromLayout && (o.coverZone || \"front\") === zone && o.photoId\n    );\n    const carryPool = existingFree.map((o) => o.photoId).concat(Object.values(legacyFrames).filter(Boolean));\n    const hasCoverImage = zone === \"front\" && !!coverState.imageUrl;\n\n    let carry = [];\n    if ((carryPool.length || hasCoverImage) && templateId) {\n      const choice = await showChoiceDialog(\n        \"This cover face has photos\",\n        \"How would you like to apply the layout?\",\n        [\n          { id: \"over\",    label: \"Apply layout, keep existing photos\" },\n          { id: \"replace\", label: \"Remove photos and apply layout\" },\n          { id: \"cancel\",  label: \"Cancel\" }\n        ]\n      );\n      if (!choice || choice === \"cancel\") return;\n      if (choice === \"over\") carry = carryPool;\n      else if (choice === \"replace\" && zone === \"front\") { coverState.imageUrl = \"\"; coverState.imageFile = null; }\n    }\n\n    // Clear any legacy fixed-slot layout for this zone \u2014 free frames supersede it.\n    if (zone === \"front\") { coverState.frontLayout = null; coverState.frontFrames = {}; coverState.frontFrameRot = {}; coverState.frontFrameShapes = {}; }\n    else                  { coverState.backLayout  = null; coverState.backFrames  = {}; coverState.backFrameRot  = {}; coverState.backFrameShapes  = {}; }\n\n    _dmbSpawnCoverLayoutFrames(zone, templateId, carry);   // templateId null \u2192 just clears this zone's layout frames\n\n    saveCoverDesignToStorage();\n    builderMode = \"cover\";\n    renderCoverCanvas();\n  }\n\n  function _showMobileQualityWarning() {\n    // Only on phones, only once per session. Returns true if the Pro tip showed.\n    if (window.innerWidth > 749) return false;\n    if (sessionStorage.getItem(\"dmb_mobile_warned\")) return false;\n    sessionStorage.setItem(\"dmb_mobile_warned\", \"1\");\n    const modal = document.getElementById(\"dmbMobileQualityModal\");\n    if (!modal) return false;\n    modal.style.display = \"flex\";\n    const dismiss = () => { modal.style.display = \"none\"; _dmbMaybeShowRotateNotice(); };\n    document.getElementById(\"dmbMobileQualityContinue\")?.addEventListener(\"click\", dismiss, { once: true });\n    document.getElementById(\"dmbMobileQualityGotIt\")?.addEventListener(\"click\", dismiss, { once: true });\n    return true;\n  }\n\n  // \u2500\u2500 \"Rotate your device\" prompt \u2014 phones + iPads, portrait only \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // Shows while the builder is open on a real touch device held vertically;\n  // auto-hides the moment they turn it to landscape. On phones it's triggered\n  // after the Pro tip is dismissed; on iPads (no Pro tip) right when the\n  // builder opens. \"Continue anyway\" lets portrait users dismiss it.\n  function _dmbIsRealTouch() {\n    return (\"ontouchstart\" in window) || (navigator.maxTouchPoints > 0);\n  }\n  let _dmbRotateDismissed = false;\n  function _dmbEnsureRotateNotice() {\n    let el = document.getElementById(\"dmbRotateNotice\");\n    if (el) return el;\n    el = document.createElement(\"div\");\n    el.id = \"dmbRotateNotice\";\n    el.className = \"dmb-rotate-notice\";\n    el.style.display = \"none\";\n    el.innerHTML =\n      '<div class=\"dmb-rotate-box\">' +\n        '<div class=\"dmb-rotate-icon\">\uD83D\uDD04</div>' +\n        '<h3 class=\"dmb-rotate-title\">Rotate your device</h3>' +\n        '<p class=\"dmb-rotate-msg\">The builder works best in <strong>landscape</strong> \u2014 turn your device sideways for more room to design.</p>' +\n        '<button type=\"button\" class=\"dmb-btn\" id=\"dmbRotateContinue\">Continue anyway</button>' +\n      '</div>';\n    document.body.appendChild(el);\n    el.querySelector(\"#dmbRotateContinue\").addEventListener(\"click\", function () {\n      _dmbRotateDismissed = true;\n      el.style.display = \"none\";\n    });\n    return el;\n  }\n  function _dmbMaybeShowRotateNotice() {\n    const el = _dmbEnsureRotateNotice();\n    if (!el) return;\n    const portrait = window.matchMedia\n      ? window.matchMedia(\"(orientation: portrait)\").matches\n      : (window.innerHeight > window.innerWidth);\n    const builderOpen = !!(builderStep && builderStep.style.display !== \"none\");\n    const proTipOpen = document.getElementById(\"dmbMobileQualityModal\")?.style.display === \"flex\";\n    // Rotate banner removed \u2014 the builder is portrait-only (CSS keeps it portrait\n    // even when the phone is turned), so we never prompt to rotate.\n    const show = false;\n    el.style.display = show ? \"flex\" : \"none\";\n  }\n  window.addEventListener(\"resize\", _dmbMaybeShowRotateNotice);\n  window.addEventListener(\"orientationchange\", function () { setTimeout(_dmbMaybeShowRotateNotice, 250); });\n\n  // \u2500\u2500 Phone landscape: desktop layout, but zoom ONLY the canvas (3rd column) \u2500\n  // Columns 1 & 2 stay at their readable slim sizes; only the canvas/spread is\n  // scaled to ~5/9 (via the builder's own canvas zoom, so it integrates with the\n  // zoom slider) so the spread fits without scrolling. Phones only \u2014 iPad keeps\n  // its full-size desktop builder. The .dmb-phone-zoom class drives the slim\n  // column CSS; applied once on enter, restored on exit so it doesn't fight a\n  // manual zoom.\n  function _dmbUpdateBuilderViewport() {\n    const builderOpen = !!(builderStep && builderStep.style.display !== \"none\");\n    const landscape = window.matchMedia\n      ? window.matchMedia(\"(orientation: landscape)\").matches\n      : (window.innerWidth > window.innerHeight);\n    // Phones ONLY (never iPad): a phone's physical short side is small (\u2264500px).\n    const isPhone = Math.min(window.screen.width || 9999, window.screen.height || 9999) <= 500;\n    // Slim desktop-layout columns: phone + landscape only. (The 50% canvas zoom\n    // is handled by the default currentZoom on phones \u2014 set at init so the first\n    // render is already 50% with no lag and nothing resets it on close.)\n    // Portrait-only: never switch the builder into the landscape layout. The\n    // mobile shell stays portrait; CSS rotates it to portrait when the phone is\n    // physically turned sideways. (landscape/isPhone kept for reference.)\n    void (builderOpen && landscape && isPhone);\n    document.documentElement.classList.toggle(\"dmb-phone-zoom\", false);\n  }\n  window.addEventListener(\"resize\", _dmbUpdateBuilderViewport);\n  window.addEventListener(\"orientationchange\", function () { setTimeout(_dmbUpdateBuilderViewport, 250); });\n  // One observer covers entering AND leaving the builder (display toggles).\n  if (typeof builderStep !== \"undefined\" && builderStep && \"MutationObserver\" in window) {\n    new MutationObserver(_dmbUpdateBuilderViewport).observe(builderStep, { attributes: true, attributeFilter: [\"style\"] });\n  }\n\n  async function openEditor({ skipStateReset = false } = {}) {\n    // Drop the early \"skip-intro\" guard classes (they use display:flex !important to\n    // prevent a load flash). Now that we're entering the builder, removing them lets\n    // the upload step actually hide \u2014 otherwise the page stays scrollable between the\n    // photo-upload step and the builder.\n    document.documentElement.classList.remove('dmb-skip-intro', 'dmb-skip-intro--upload');\n    // A card has no interior pages \u2014 it's just the Front/Back cover. Use a fixed\n    // small page count so openEditor proceeds (interior pages are created but\n    // hidden by .dmb-card CSS); the builder then lands in cover mode below.\n    const selectedPages = window.DMB_CARD_MODE ? 2 : Number(document.getElementById(\"pages\")?.value || 0);\n    if (!selectedPages) return;\n\n    if (step1) step1.style.display = \"none\";\n    if (builderStep) builderStep.style.display = \"block\";\n    // Hide the upload step and mark the URL as \"in builder\" so a page REFRESH\n    // restores the design instead of dropping back to the upload step. Centralised\n    // here so EVERY entry path is covered \u2014 the Do-the-Magic/Customize buttons AND\n    // the auto-open that fires once enough photos are uploaded (which previously\n    // skipped this and left the URL at ?step=upload).\n    { const _s1bEl = document.getElementById(\"step1b\"); if (_s1bEl) _s1bEl.style.display = \"none\"; }\n    if (!_dmbRenderMode()) try {\n      const _bu = new URL(window.location.href);\n      if (_bu.searchParams.get(\"step\") !== \"2\" || _bu.searchParams.get(\"restore\") !== \"1\") {\n        _bu.searchParams.set(\"step\", \"2\");\n        _bu.searchParams.set(\"restore\", \"1\");\n        history.replaceState(null, \"\", _bu.toString());\n      }\n    } catch (e) {}\n    const _proTipShown = _showMobileQualityWarning();\n    // iPad (and phones already warned) get the rotate prompt right away; on a\n    // first-time phone it fires after the Pro tip is dismissed instead.\n    if (!_proTipShown) _dmbMaybeShowRotateNotice();\n    // Apply the phone auto-zoom (50%) after the open renders settle.\n    requestAnimationFrame(() => { try { _dmbUpdateBuilderViewport(); } catch (e) {} });\n    if (bookDetails) bookDetails.style.display = \"none\";\n\n    if (!skipStateReset) {\n      // Magazine sizes use the fixed plan (page 0 + N spreads + trailing single);\n      // books/cards keep ceil(pages/2). Must match initEmptyBookFromPages so the\n      // trailing single page + its objects exist from the first render.\n      const _plan = _dmbPagePlan(selectedPages);\n      editor.totalPages = _plan.totalPages;\n      editor.editablePages = Math.max(0, editor.totalPages - 1);\n      editor.totalSpreads = _plan.totalSpreads;\n      editor.currentSpread = 0;\n\n      editor.pageSlots = {};\n      editor.pageRot = {};\n      editor.pageTemplate = {};\n      editor.pageFrames = {};\n      editor.pageFrameRot = {};\n      editor.pageObjects = {};\n      editor.spreadPhoto = {};\n      editor.coverObjects = [];\n      editor.coverUserTexts = [];\n      for (let p = 1; p <= editor.totalPages; p++) {\n        editor.pageSlots[p] = { A: null, B: null };\n        editor.pageRot[p] = { A: 0, B: 0 };\n        editor.pageTemplate[p] = null;\n        editor.pageFrames[p] = {};\n        editor.pageFrameRot[p] = {};\n        editor.pageObjects[p] = [];\n      }\n      const totalSpreads = editor.totalSpreads || Math.ceil(selectedPages / 2);\n      for (let s = 0; s < totalSpreads; s++) editor.spreadPhoto[s] = null;\n    }\n    initSpreadPhotoDragListeners();\n\n    const files = skipStateReset ? [] : (bookFiles || []);\n    // Reuse decodes already warmed during the upload step (see _dmbWarmMeta);\n    // any file not yet warmed is decoded here as a fallback.\n    // TIME-BOXED, and this is why the mobile shell vanished on phones.\n    //\n    // Promise.all never settles if ONE of its promises never settles, and _dmbWarmMeta\n    // decodes an image - the exact operation already proven to hang forever on a fully\n    // loaded image (see the img.decode note). One bad photo out of twenty four and this\n    // await simply never returns, so openEditor stops here: no init calls, no\n    // _dmbInitMobileShell, no body class, and the builder falls back to the desktop\n    // layout. Nothing throws, so there is nothing in the console and nothing for a\n    // try/catch to catch, which is what made it look like a reverted deploy.\n    //\n    // refreshBuilderPhotos already wraps this identical call in _dmbSettleWithin; this\n    // copy was missed. Same guard, same fallback meta. (2026-10-07)\n    const metas = await Promise.all(files.map(function (f) {\n      return _dmbSettleWithin(_dmbWarmMeta(f), _DMB_META_TIMEOUT + 2000, { w: 1, h: 1, ratio: 1 });\n    }));\n\n    if (!skipStateReset) {\n      // Print-master path: keep the PICK-time pid (f._dmbPid) as the photo id so\n      // the R2 backup, _r2 marking, light-mem release and master fetches all agree\n      // \u2014 a fresh uid() here silently orphaned every one of those. Also carry _r2/\n      // thumb state forward and keep released off-spread photos lazy (no object\n      // URL), exactly like refreshBuilderPhotos.\n      const _pm = _dmbPrintMasterOn();\n      const _prevById = {};\n      try { if (_pm) (editor.photos || []).forEach((p) => { if (p && p.id) _prevById[p.id] = p; }); } catch (e) {}\n      const _keep = (_pm && _dmbLightMem()) ? _dmbCurrentSpreadPhotoIds() : null;\n      editor.photos = files.map((f, i) => {\n        if (!_pm) return {\n          id: uid(),\n          file: f,\n          url: URL.createObjectURL(f),\n          ratio: metas[i]?.ratio ?? 1,\n          filename: String(f.name || \"\").toLowerCase(),\n          dateTaken: metas[i]?.date || \"\",\n          tags: [],\n          addedIndex: i\n        };\n        const pid = f._dmbPid || uid();\n        const prev = _prevById[pid] || {};\n        const lazy = !!(_keep && prev._r2 && !_keep.has(pid));\n        return {\n          id: pid,\n          file: f,\n          url: prev.url || (lazy ? \"\" : URL.createObjectURL(f)),\n          ratio: metas[i]?.ratio ?? prev.ratio ?? 1,\n          filename: String(f.name || \"\").toLowerCase(),\n          dateTaken: metas[i]?.date || prev.dateTaken || \"\",\n          tags: prev.tags || [],\n          addedIndex: i,\n          _r2: prev._r2 || false,\n          thumbUrl: prev.thumbUrl || \"\"\n        };\n      });\n    }\n\n    if (bookMeta) {\n      bookMeta.textContent =\n        `${getEffectiveBookTitle()} \u2022 ${editor.totalPages} pages (${editor.editablePages} to design) \u2022 ${editor.photos.filter((p) => !_dmbIsCropDerived(p)).length} photos`;\n    }\n\n    // These eight used to run bare. openEditor is async, so the FIRST one to throw\n    // rejected the promise and silently skipped everything after it - including\n    // _dmbInitMobileShell below. The visible result is a phone falling back to the\n    // desktop builder layout with no error shown anywhere, which is exactly the\n    // failure this is here to stop. Isolate them so one failure cannot cascade, and\n    // record every failure instead of discarding it. (2026-10-07)\n    window.__DMB_INIT_ERRS = [];\n    [\n      [\"initUnifiedTabs\", initUnifiedTabs],\n      [\"initPhotoImportMenu\", initPhotoImportMenu],\n      [\"initPhotoSearchSortControls\", initPhotoSearchSortControls],\n      [\"initProjectMenu\", initProjectMenu],\n      [\"initHistoryButton\", initHistoryButton],\n      [\"syncCoverInputsFromState\", syncCoverInputsFromState],\n      [\"ensurePreviewOverlay\", ensurePreviewOverlay],\n      [\"wireTextToolbar\", wireTextToolbar],\n    ].forEach(function (step) {\n      try { step[1](); }\n      catch (e) {\n        try {\n          window.__DMB_INIT_ERRS.push(step[0] + \": \" + ((e && e.message) || String(e)));\n          console.error(\"[dmb init] \" + step[0] + \" failed:\", e);\n        } catch (e2) {}\n      }\n    });\n    try { initMapTools(); } catch(e) {}\n    try { enableMapMarkerPlacement(); } catch(e) {}\n    try { initStickerLibrary(); } catch(e) {}\n    try { initMagIconPanel(); } catch(e) {}\n    try { initCustomStickersPanel(); } catch(e) {}\n    try { initPhoneUpload(); } catch(e) {}\n    try { _dmbInitMobileShell(); }\n    catch (e) {\n      // _mshBuilt is set at the TOP of _dmbInitMobileShell, so a throw halfway used to\n      // leave the flag true and every later call returned early through _mshSync. The\n      // shell stayed half built for the life of the page. Clear the partial DOM and the\n      // flag so the next openEditor gets a clean attempt.\n      try {\n        window.__DMB_MSH_ERR = (e && (e.stack || e.message)) || String(e);\n        console.error(\"[dmb] mobile shell init failed:\", e);\n      } catch (e2) {}\n      try {\n        document.querySelectorAll('#step2 [class*=\"dmb-msh\"]').forEach(function (n) { n.remove(); });\n        _mshBuilt = false;\n      } catch (e3) {}\n    }\n    try { _dmbShowInitDiag(); } catch (e) {}\n    try { window.__DMB_OPENEDITOR_DONE = true; } catch (e) {}\n    try { _dmbPlaceSpreadAdderDesktop(); } catch(e) {}\n    try { _dmbInitPhotoScrollAutoHide(); } catch(e) {}\n\n    // Global dragend: clear any lingering spread overlays if drag ends without a drop\n    document.addEventListener(\"dragend\", () => {\n      document.querySelectorAll(\".dmb-spread-drag-overlay.active\").forEach(el => { el.classList.remove(\"active\"); });\n      document.querySelectorAll(\".dmb-spread-drop-zone.drag-over\").forEach(el => { el.classList.remove(\"drag-over\"); });\n      document.querySelectorAll(\".dmb-cover-drag-overlay.active\").forEach(el => { el.classList.remove(\"active\"); });\n    });\n\n    // Global click: close any open \u00B7\u00B7\u00B7 option popups when clicking outside\n    document.addEventListener(\"click\", (e) => {\n      if (!e.target.closest(\"[data-frame-key]\") && !e.target.closest(\".dmb-photo-frame-object\")) {\n        document.querySelectorAll(\".dmb-frame-popup-open\").forEach(pop => {\n          pop.style.display = \"none\";\n          pop.classList.remove(\"dmb-frame-popup-open\");\n        });\n      }\n    });\n\n    document.dispatchEvent(new CustomEvent(\"dmb:editorOpened\"));\n\n    // A FRESH book opens on the cover, which is where designing starts. A RESTORE must\n    // NOT: forcing the cover here threw away the builderMode and currentSpread that the\n    // snapshot had just put back, so a refresh always landed on the cover no matter\n    // which spread the customer was reading. skipStateReset is precisely the \"state was\n    // restored, do not reset it\" signal, passed by both the ?restore=1 reload and the\n    // put-the-builder-back path. (2026-10-12)\n    const _keepRestoredView = !!skipStateReset && builderMode === \"pages\";\n    if (!_keepRestoredView) builderMode = \"cover\";\n    syncBuilderModeButtons();\n    // Render mode draws no tray/rail: the PDF never reads them, and 300 thumbnails is\n    // pure memory. renderPhotoTray is also the biggest persistence cluster in the file.\n    if (!_dmbRenderMode()) { renderPhotoTray(); renderThumbsRail(); }\n    if (_keepRestoredView) {\n      // Clamp first: a saved spread index can outlive a page-count change, and an\n      // out-of-range index renders nothing at all.\n      try {\n        const _maxSp = Math.max(0, (editor.totalSpreads || 1) - 1);\n        if (editor.currentSpread > _maxSp) editor.currentSpread = _maxSp;\n        if (editor.currentSpread < -1) editor.currentSpread = -1;\n      } catch (e) {}\n      renderBuilderCanvas();\n    } else {\n      renderCoverCanvas();\n    }\n\n    // Render mode stops here. One early return replaces individual guards for the\n    // progress-restore offer, cross-device sync, the post-login save, the TOUR (which\n    // on a fresh profile clicks #undoBtn -> pushUndoState -> _dmbUserEdited = true,\n    // unlocking the guest R2 save), the add-to-cart hint, the sign-in prompt, the\n    // background preview engine and the scroll. setCanvasZoom(1) pins the overlay\n    // scale so cover geometry is not read at a device-dependent zoom.\n    if (_dmbRenderMode()) { try { setCanvasZoom(1); } catch (e) {} return; }\n\n    // Offer to restore saved customer progress (after first render)\n    setTimeout(() => { try { offerCustomerProgressRestore(); } catch(e) {} }, 600);\n    setTimeout(() => { try { _dmbStartCrossDeviceSync(); } catch(e) {} }, 900);   // instant multi-device sync\n    try { _dmbMaybeSaveAfterLogin(); } catch(e) {}   // finish a guest's save after they sign in\n\n    // Start builder tour for first-time visitors \u2014 but NOT on phones: the tour\n    // targets the old desktop layout, so it highlights the wrong elements in the\n    // mobile shell. (A mobile-specific tour can be added later.)\n    if (!(window.matchMedia && window.matchMedia(\"(max-width: 749px), (orientation: portrait) and (pointer: coarse) and (max-width: 1024px)\").matches)) {\n      setTimeout(() => { try { dmbStartTour(); } catch(e) {} }, 1200);\n    }\n    // If the tour is already done it won't run (so no \"tourEnded\" event fires) \u2014\n    // show the add-to-cart hint directly (once) so returning visitors still get it.\n    try {\n      if (localStorage.getItem(\"dmb_tour_v1_done\")) {\n        setTimeout(() => { try { _dmbShowAddCartHint(); } catch(e) {} }, 1500);\n      }\n    } catch(e) {}\n\n    // Nudge guests to sign in so their design is saved + available on any device.\n    setTimeout(() => { try { _dmbMaybeShowSignInPrompt(); } catch(e) {} }, 1800);\n\n    // Kick off background PDF preview rendering for all pages\n    try { _dmbPvInitAll(); } catch(e) {}\n\n    // Open at the device default (50% on phones, 100% on iPad/desktop).\n    setCanvasZoom(_dmbDefaultZoom());\n\n    // Keep the page at the top (header + announcement bar visible) when the builder\n    // opens, instead of auto-scrolling down to the builder. The customer controls scroll.\n    window.scrollTo({ top: 0, behavior: \"auto\" });\n  }\n\n  function closeEditorBackToDetails() {\n    document.querySelectorAll(\".dmb-logo-watermark\").forEach((el) => el.remove());\n    if (step1) step1.style.display = \"flex\";\n    if (builderStep) builderStep.style.display = \"none\";\n    if (bookDetails) bookDetails.style.display = \"none\";\n  }\n\n  let _spreadDragListenersAdded = false;\n  function initSpreadPhotoDragListeners() {\n    if (_spreadDragListenersAdded) return;\n    _spreadDragListenersAdded = true;\n    const _pdZ = getPageDims(); const PAGE_W = _pdZ.w, PAGE_H = _pdZ.h;\n    document.addEventListener(\"mousemove\", (e) => {\n      if (!spreadPhotoDragState) return;\n      const { type, si, startX, startY, startPanX, startPanY, startZoom } = spreadPhotoDragState;\n      const sp = editor.spreadPhoto?.[si];\n      if (!sp) { spreadPhotoDragState = null; return; }\n      if (type === \"pan\") {\n        const zoom = sp.zoom || 1;\n        const maxX = PAGE_W * (zoom - 1), maxY = (PAGE_H / 2) * (zoom - 1);\n        sp.panX = Math.max(-maxX, Math.min(maxX, startPanX + (e.clientX - startX)));\n        sp.panY = Math.max(-maxY, Math.min(maxY, startPanY + (e.clientY - startY)));\n        spreadCanvas.querySelectorAll(\"[data-spread-img-layer]\").forEach(l => {\n          l.style.transform = `translate(${sp.panX}px,${sp.panY}px) scale(${zoom}) rotate(${sp.rotation || 0}deg)`;\n        });\n      } else if (type === \"resize\") {\n        const newZoom = Math.max(1, Math.min(4, startZoom + (e.clientX - startX) / 150));\n        const maxX = PAGE_W * (newZoom - 1), maxY = (PAGE_H / 2) * (newZoom - 1);\n        sp.zoom = newZoom;\n        sp.panX = Math.max(-maxX, Math.min(maxX, startPanX));\n        sp.panY = Math.max(-maxY, Math.min(maxY, startPanY));\n        spreadCanvas.querySelectorAll(\"[data-spread-img-layer]\").forEach(l => {\n          l.style.transform = `translate(${sp.panX}px,${sp.panY}px) scale(${newZoom}) rotate(${sp.rotation || 0}deg)`;\n        });\n      }\n    });\n    document.addEventListener(\"mouseup\", () => {\n      if (spreadPhotoDragState) {\n        const _pvDragSi = spreadPhotoDragState.si;\n        spreadPhotoDragState = null;\n        renderThumbsRail();\n        // Dirty-mark both pages of the affected spread so the PDF preview\n        // re-renders with the new pan/zoom/rotation. This drag handler never\n        // calls pushUndoState, so we hook directly here.\n        try { _dmbPvMarkDirty(_pvDragSi * 2 + 1); _dmbPvMarkDirty(_pvDragSi * 2 + 2); } catch(e) {}\n      }\n    });\n  }\n\n  function chooseDistributedPages(pages, k) {\n    const n = pages.length;\n    if (k <= 0) return new Set();\n    if (k >= n) return new Set(pages);\n\n    const chosen = [];\n    for (let i = 0; i < k; i++) {\n      const idx = Math.round((i + 0.5) * (n / k) - 0.5);\n      chosen.push(pages[Math.max(0, Math.min(n - 1, idx))]);\n    }\n\n    const set = new Set(chosen);\n\n    let guard = 0;\n    while (guard++ < 1000) {\n      let changed = false;\n      const sorted = Array.from(set).sort((a, b) => a - b);\n\n      for (let i = 0; i < sorted.length - 1; i++) {\n        if (sorted[i + 1] === sorted[i] + 1) {\n          const bad = sorted[i + 1];\n          const pos = pages.indexOf(bad);\n          const options = [pages[pos + 1], pages[pos - 1]].filter(Boolean);\n\n          for (const opt of options) {\n            if (!set.has(opt) && !set.has(opt - 1) && !set.has(opt + 1)) {\n              set.delete(bad);\n              set.add(opt);\n              changed = true;\n              break;\n            }\n          }\n        }\n      }\n\n      if (!changed) break;\n    }\n\n    return set;\n  }\n\n  // \u2500\u2500 Magazine pricing/variants (separate \"custom-magazine\" product) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // Built from window.DMB_MAGAZINE_VARIANTS (exposed by design-my-magazine.liquid)\n  // into { \"PAGES|SIZE\": {id, new, old} }, normalised so page keys are plain digits\n  // and size is A4/A5 regardless of the product's option order. Null if the product\n  // isn't set up \u2192 callers fall back to the book product.\n  // var (NOT let): updatePrice() runs during init BEFORE this line is reached,\n  // and a `let` here is in its temporal dead zone at that moment \u2014 on magazine\n  // pages that threw a ReferenceError that silently killed the rest of the\n  // file's initialization (dead Edit buttons etc.). var hoists to undefined.\n  var _dmbMagMapCache;\n  function _dmbMagVariantMap() {\n    if (_dmbMagMapCache !== undefined) return _dmbMagMapCache;\n    const arr = window.DMB_MAGAZINE_VARIANTS;\n    if (!Array.isArray(arr) || !arr.length) { _dmbMagMapCache = null; return null; }\n    const isSz = (s) => /^a[45]$/i.test(String(s).trim());\n    const map = {};\n    arr.forEach((v) => {\n      const o1 = String(v.o1 || \"\"), o2 = String(v.o2 || \"\");\n      const size = isSz(o1) ? o1.trim().toUpperCase() : isSz(o2) ? o2.trim().toUpperCase() : \"A4\";\n      const pages = String(isSz(o1) ? o2 : o1).replace(/\\D/g, \"\");\n      if (!pages) return;\n      map[pages + \"|\" + size] = { id: v.id, new: Number(v.price) || 0, old: Number(v.compare) || 0 };\n    });\n    _dmbMagMapCache = Object.keys(map).length ? map : null;\n    return _dmbMagMapCache;\n  }\n  function _dmbMagEntry(pagesValue, sizeValue) {\n    const m = _dmbMagVariantMap(); if (!m) return null;\n    const pages = String(pagesValue || \"\").replace(/\\D/g, \"\");\n    const size = sizeValue === \"A5\" ? \"A5\" : \"A4\";\n    return m[pages + \"|\" + size] || m[pages + \"|A4\"] || null;\n  }\n\n  function getDmbVariantIdByPages(pagesValue, sizeValue) {\n    // Magazines use their own product when it's set up; else fall through to books.\n    if (window.DMB_MAGAZINE_MODE) {\n      const me = _dmbMagEntry(pagesValue, sizeValue);\n      if (me && me.id) return me.id;\n    }\n    // Page count \u00D7 Size \u2192 Shopify variant id. Sourced from BOOK_VARIANT_IDS, which\n    // is the hardcoded fallback MERGED with the live custom-photo-book variants\n    // (see _dmbIngestBookVariants). Size defaults to A4 so any caller that doesn't\n    // pass a size (e.g. magazine) is unchanged.\n    const row = BOOK_VARIANT_IDS[String(pagesValue || \"\")];\n    if (!row) return null;\n    return row[sizeValue === \"A5\" ? \"A5\" : \"A4\"] || row.A4 || null;\n  }\n\n  // \u2500\u2500\u2500 PDF generation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // Page order \u2014 book:     front cover \u2192 spine \u2192 back cover \u2192 pages 1..N\n  //              magazine: front cover \u2192 blank \u2192 pages 0..N \u2192 blank \u2192 back cover\n  // Programmatic PDF \u2014 each element drawn directly from source at 300 DPI.\n  // No html2canvas: photos are taken straight from their original files/URLs.\n  // _pvOpt: { cover:true, resolve, reject } for cover preview\n  //         { page:N,   resolve, reject } for single page preview\n  //         null/undefined for normal full PDF export\n  async function buildPdfBlobOnePagePerBookPage(onProgress, _pvOpt, _excludePages, _stream) {\n\n    // \u2500\u2500 Load jsPDF only (no html2canvas needed) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    function loadScript(src) {\n      return new Promise(function(resolve, reject) {\n        if (document.querySelector('script[src=\"' + src + '\"]')) { resolve(); return; }\n        var s = document.createElement(\"script\");\n        s.src = src;\n        s.onload = resolve;\n        s.onerror = function() { reject(new Error(\"Could not load \" + src)); };\n        document.head.appendChild(s);\n      });\n    }\n    // Preview mode skips jsPDF entirely \u2014 it only needs canvas rendering\n    // Preview mode skips jsPDF entirely - it only needs canvas rendering. The STREAMING\n    // path never constructs a jsPDF either (addCanvasPage hands the JPEG off and\n    // returns), so RENDER MODE skips the CDN dependency too: one less piece of egress\n    // that can abort a whole server job before a single page is drawn.\n    if (!_pvOpt && !(_dmbRenderMode() && _stream && _stream.onPage))\n      await loadScript(\"https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js\");\n\n    // \u2500\u2500 Sizing constants \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // Both the INTERIOR pages and the COVER faces flip with orientation\n    // (landscape = 297 \u00D7 210). Portrait books are byte-for-byte unchanged.\n    // Landscape cover element positions are approximate until re-authored via an\n    // -ls template. Spine thickness is physical (page count) so it stays constant.\n    var _dmbLS     = dmbIsLandscape();\n    var A4_W_MM    = _dmbLS ? 297 : 210;   // interior page (flips)\n    var A4_H_MM    = _dmbLS ? 210 : 297;\n    // Card = 5\u00D77in (127\u00D7178mm) face; book/magazine cover = A-series (210\u00D7297).\n    var _dmbCard   = !!window.DMB_CARD_MODE;\n    var COVER_W_MM = _dmbCard ? (_dmbLS ? 178 : 127) : (_dmbLS ? 297 : 210);   // cover face (flips with orientation)\n    var COVER_H_MM = _dmbCard ? (_dmbLS ? 127 : 178) : (_dmbLS ? 210 : 297);\n    var FACE_PX    = 300;\n    var FACE_H_PX  = Math.round(FACE_PX * COVER_H_MM / COVER_W_MM);   // portrait 424, landscape 212\n    var SPINE_PX   = COVER_SPINE_W;   // matches the on-screen spine so the PDF crop isn't squished\n    var SPINE_W_MM = 8;   // fixed physical spine thickness: exactly 8 mm (both orientations)\n    var PAGE_W_PX  = getPageDims().w;   // portrait 270, landscape 382\n\n    // Output canvas at full 300 DPI \u2014 resolution/sharpness preserved (crisp text, logos,\n    // fine lines). Upload size is reduced instead via stronger JPEG compression (0.85 below),\n    // which is the least-visible way to shrink the data so big books still finish on a slow\n    // (~2 Mbps) connection.\n    var MM2PX      = 300 / 25.4;\n    // EVERY book \u2014 big or small \u2014 now uses the SAME near-lossless JPEG quality (0.96).\n    // (Previously books \u2265100 pages were dropped to 0.85 to shrink the upload on slow\n    // connections; the owner wants big books at full quality too. Big books render\n    // page-by-page via the streaming path and free each page, so per-page memory is\n    // unchanged \u2014 only the finished PDF is larger. Overridable via\n    // window.DMB_PDF_JPEG_QUALITY if a specific book ever needs a smaller file.)\n    var _jpegQuality = (typeof window.DMB_PDF_JPEG_QUALITY === \"number\")\n      ? window.DMB_PDF_JPEG_QUALITY : 0.96;\n    var OUT_W      = Math.round(A4_W_MM * MM2PX);    // interior width  (portrait 2480 / landscape 3508)\n    var OUT_H      = Math.round(A4_H_MM * MM2PX);    // interior height (portrait 3508 / landscape 2480)\n    var PAGE_SCALE = OUT_W / PAGE_W_PX;\n    var FACE_SCALE = Math.round(COVER_W_MM * MM2PX) / FACE_PX;   // cover scale from the PORTRAIT face\n    var FACE_OUT_H = Math.round(FACE_H_PX * FACE_SCALE);\n\n    // jsPDF is only needed for the real export, not preview rendering\n    var jsPDF = (!_pvOpt && window.jspdf) ? window.jspdf.jsPDF : null;\n    var pdf   = null;\n\n    // \u2500\u2500 CHUNK mode (admin split-download) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // When _stream.chunk is a number N, the SAME render pipeline produces SEPARATE small\n    // PDFs instead of one huge one: the 3 cover pages become \"cover\", then every N book\n    // pages become \"1\", \"2\", \"3\"\u2026 Each finished chunk is a jsPDF blob, returned as an array.\n    var _chunkPdf = null, _chunkPageCount = 0, _chunkNo = 0, _chunkOut = [];\n    function _finishChunk(name) {\n      if (_chunkPdf) { _chunkOut.push({ name: name, blob: _chunkPdf.output(\"blob\") }); }\n      _chunkPdf = null; _chunkPageCount = 0;\n    }\n    function _maybeCloseInteriorChunk() {\n      if (_stream && _stream.chunk && _chunkPageCount >= _stream.chunk) {\n        _chunkNo++; _finishChunk(String(_chunkNo));\n      }\n    }\n    // \u2500\u2500 Phase 1 streaming export (build+verify only; not wired into checkout) \u2500\u2500\n    // When _stream.onPage is provided, each finished page is handed off and freed\n    // instead of being accumulated in jsPDF, so peak memory \u2248 one page. The per-page\n    // JPEG is the SAME canvas.toDataURL(...) the in-memory path uses, so the bytes\n    // are identical. _pageSeq records page order + dimensions for the returned\n    // manifest (what the server \"stapler\" assembles from).\n    _stream = _stream || null;\n    var _pageSeq = [];\n\n    // \u2500\u2500 Canvas helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    function mkCanvas(w, h) {\n      var c = document.createElement(\"canvas\");\n      c.width  = Math.max(1, Math.round(w));\n      c.height = Math.max(1, Math.round(h));\n      var ctx = c.getContext(\"2d\");\n      // \u2500\u2500 Quality: always use high-quality image smoothing for PDF rendering \u2500\u2500\n      // Default browser setting is \"low\", which produces aliasing artefacts\n      // (white/black dots, noise) when downscaling large camera photos to the\n      // 300 DPI canvas. \"high\" uses bicubic/Lanczos resampling \u2192 clean result.\n      ctx.imageSmoothingEnabled = true;\n      ctx.imageSmoothingQuality = \"high\";\n      ctx.fillStyle = \"#ffffff\";\n      ctx.fillRect(0, 0, c.width, c.height);\n      return { canvas: c, ctx: ctx };\n    }\n\n    function loadImg(url) {\n      return new Promise(function(res, rej) {\n        if (!url) return rej(new Error(\"No URL\"));\n        var img = new Image();\n        if (!url.startsWith(\"blob:\") && !url.startsWith(\"data:\")) img.crossOrigin = \"anonymous\";\n        img.onload  = function() { res(img); };\n        img.onerror = function() { rej(new Error(\"Failed: \" + url.slice(0, 60))); };\n        img.src = url;\n      });\n    }\n\n    // \u2500\u2500 Level 2 render helpers (need loadImg, so they live in this scope) \u2500\u2500\u2500\u2500\u2500\n    // See _dmbLightMem / _dmbReleaseDistantFullRes (defined at module level).\n    // Decode a blob into an <img>, then revoke its object URL immediately. The\n    // decoded pixels stay in the element, so the compressed blob is freed right\n    // after decode \u2192 only ONE full-res image is resident at a time in a render.\n    async function _dmbDecodeBlobToImg(blob) {\n      const u = URL.createObjectURL(blob);\n      const img = new Image();\n      img.decoding = \"async\";\n      // Wait for LOAD, not decode(). Canvas drawing only needs the image loaded,\n      // and img.decode() is NOT reliable to await: on a large image in a tab the\n      // browser isn't painting (customer switched tabs mid-export), Chrome can\n      // leave the returned promise unsettled FOREVER \u2014 which used to hang the\n      // whole PDF render with no error and no timeout. Verified live: an image\n      // with complete=true and naturalWidth=3500 still never settled decode().\n      // So load is the real gate; decode() is best-effort, time-boxed, and its\n      // failure is ignored.\n      const loaded = new Promise((res, rej) => {\n        img.onload = () => res(true);\n        img.onerror = () => rej(new Error(\"image load failed\"));\n      });\n      img.src = u;\n      try {\n        if (!img.complete || !img.naturalWidth) await loaded;\n        await Promise.race([\n          img.decode().catch(() => {}),\n          new Promise((res) => setTimeout(res, 3000)),\n        ]);\n      } finally {\n        // Safe once loaded: the element keeps its own copy of the pixels.\n        try { URL.revokeObjectURL(u); } catch (e) {}\n      }\n      if (!img.naturalWidth) throw new Error(\"decode failed\");\n      return img;\n    }\n\n    // Full-res image for RENDER. Light-mem OFF \u2192 exactly loadImg(ph.url)\n    // (unchanged). ON \u2192 resident url/file if present, else full-res streamed from\n    // R2 by id, one image at a time. Same full-res bytes either way \u2014 quality is\n    // identical; this only changes WHERE the bytes live between uses.\n    async function _dmbRenderImg(ph) {\n      if (!ph) throw new Error(\"no photo\");\n      // Print-master (flag ON, DESKTOP only): the client PDF prefers the ~3500px\n      // print master from R2 over the resident proxy \u2014 under the flag the local\n      // file is a 2500px proxy, so without this the desktop PDF would LOSE quality.\n      // Mobile stays on the proxy path (decoding hundreds of 3500px masters is the\n      // exact crash we removed \u2014 its full-quality PDF comes from the server render).\n      // Crop-derived photos are baked from local pixels; no master exists for them.\n      // A pid that misses once is memoized and never re-tried this session.\n      // RENDER MODE + crops=rebake: a crop-derived photo was baked in the CUSTOMER's\n      // browser. Re-bake it from the SOURCE master + editor.cropRects, honouring EXIF.\n      // The DEFAULT (crops=prefer-r2) falls through to photos/{derivedId} below - those\n      // are the exact bytes the customer's own PDF draws, i.e. the parity source.\n      if (_dmbRenderMode() && _DMB_RM.crops === \"rebake\" && _dmbIsCropDerived(ph)) {\n        try { const _ci = await _dmbRenderCropDerivedImg(ph); if (_ci) return _ci; } catch (e) {}\n      }\n      if (_dmbPrintMasterOn() && !_dmbIsMobileUpload() && ph.id && !_dmbIsCropDerived(ph) && !_dmbNoMasterPids.has(ph.id)) {\n        try {\n          // If this photo's master might still be uploading, let the chain finish\n          // first \u2014 otherwise a just-imported photo would memoize a false miss.\n          if (_dmbMasterPendingCount > 0) { try { await _dmbMasterChain; } catch (e) {} }\n          let _mdid = \"\"; try { _mdid = getCustomerSaveKey().replace(/^dmb_progress_/, \"\"); } catch (e) {}\n          const _mb = await _dmbFetchPhotoBlob(_mdid, ph.id, \"master\");\n          if (_mb && _mb.size) return await _dmbDecodeBlobToImg(_mb);\n          _dmbNoMasterPids.add(ph.id);\n          if (_dmbRenderMode()) _dmbRenderNote(\"master_miss\", ph.id);\n        } catch (e) {\n          try { _dmbNoMasterPids.add(ph.id); } catch (e2) {}\n          if (_dmbRenderMode()) _dmbRenderNote(\"master_miss\", ph.id);\n        }\n      }\n      if (!_dmbLightMem()) return loadImg(ph.url);        // legacy path \u2014 byte-identical\n      if (ph.url) { try { return await loadImg(ph.url); } catch (e) {} }\n      if (ph.file && ph.file.size) { try { return await _dmbDecodeBlobToImg(ph.file); } catch (e) {} }\n      let designId = \"\"; try { designId = getCustomerSaveKey().replace(/^dmb_progress_/, \"\"); } catch (e) {}\n      const blob = await _dmbFetchPhotoBlob(designId, ph.id);\n      if (!blob) {\n        // RENDER MODE: last chance for a derived crop whose baked bytes are gone from R2.\n        if (_dmbRenderMode() && _dmbIsCropDerived(ph)) {\n          try { const _ci = await _dmbRenderCropDerivedImg(ph); if (_ci) return _ci; } catch (e) {}\n        }\n        if (_dmbRenderMode()) _dmbRenderNote(\"photo_missing\", ph.id);\n        throw new Error(\"photo unavailable: \" + (ph.id || \"?\"));\n      }\n      return _dmbDecodeBlobToImg(blob);\n    }\n    // Cover face/bleed photos store a URL and (for tray photos) the photo id.\n    // Under the print-master flag, resolve by id through _dmbRenderImg \u2014 master\n    // quality on desktop, R2 self-heal when the resident URL was released/died.\n    // Flag OFF (or no id, e.g. the front background upload): exactly loadImg(url).\n    async function _dmbCoverImg(urlVal, idVal) {\n      if (_dmbPrintMasterOn() && idVal) {\n        try {\n          const _cph = getPhotoById(idVal);\n          if (_cph) return await _dmbRenderImg(_cph);\n        } catch (e) { if (_dmbRenderMode()) _dmbRenderNote(\"photo_missing\", String(idVal)); }\n      }\n      if (_dmbRenderMode() && !urlVal) _dmbRenderNote(\"photo_missing\", String(idVal || \"cover-bg\"));\n      return loadImg(urlVal);\n    }\n    try { if (typeof window !== \"undefined\") window.__dmbLightMem = { on: _dmbLightMem, renderImg: _dmbRenderImg, release: _dmbReleaseDistantFullRes, keep: _dmbCurrentSpreadPhotoIds }; } catch (e) {}\n\n    // \u2500\u2500 Photo drawing helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // _setHiQ: always re-apply imageSmoothingQuality after ctx.save() because\n    // save/restore does NOT preserve the smoothing setting in all browsers.\n    function _setHiQ(ctx) {\n      ctx.imageSmoothingEnabled = true;\n      ctx.imageSmoothingQuality = \"high\";\n    }\n\n    // _downsampleImg: stepped downsampling for very large photos.\n    // A single drawImage from e.g. 4000px \u2192 600px can still look noisy even\n    // with \"high\" smoothing. Halving in steps produces a cleaner result.\n    // Returns a canvas that can be used as the source for the final drawImage.\n    // If the photo is small enough (< 2\u00D7 the target), returns the original img.\n    function _downsampleImg(img, targetW, targetH) {\n      var sW = img.naturalWidth, sH = img.naturalHeight;\n      // Only step-down if source is more than 2\u00D7 the target dimension\n      if (sW <= targetW * 2 && sH <= targetH * 2) return img;\n      var cur = img;\n      while (sW > targetW * 2 || sH > targetH * 2) {\n        var nW = Math.max(Math.round(sW / 2), targetW);\n        var nH = Math.max(Math.round(sH / 2), targetH);\n        var step = document.createElement(\"canvas\");\n        step.width = nW; step.height = nH;\n        var sCtx = step.getContext(\"2d\");\n        sCtx.imageSmoothingEnabled = true;\n        sCtx.imageSmoothingQuality = \"high\";\n        sCtx.drawImage(cur, 0, 0, nW, nH);\n        cur = step;\n        sW = nW; sH = nH;\n      }\n      return cur;\n    }\n\n    // object-fit:cover + pan/zoom/rotation into a clipped destination rect\n    function drawFit(ctx, img, dx, dy, dw, dh, panX, panY, zoom, rotDeg) {\n      panX = panX || 0; panY = panY || 0; zoom = zoom || 1; rotDeg = rotDeg || 0;\n      var iA = img.naturalWidth / img.naturalHeight;\n      var fA = dw / dh;\n      var sw, sh;\n      if (iA > fA) { sh = dh; sw = sh * iA; } else { sw = dw; sh = sw / iA; }\n      // Pre-downsample large source photos for clean anti-aliasing\n      var src = _downsampleImg(img, Math.round(sw), Math.round(sh));\n      ctx.save();\n      _setHiQ(ctx);\n      ctx.beginPath(); ctx.rect(dx, dy, dw, dh); ctx.clip();\n      ctx.translate(dx + dw / 2 + panX, dy + dh / 2 + panY);\n      if (rotDeg) ctx.rotate(rotDeg * Math.PI / 180);\n      ctx.scale(zoom, zoom);\n      ctx.drawImage(src, -sw / 2, -sh / 2, sw, sh);\n      ctx.restore();\n    }\n\n    // object-fit:cover + rotation-only (matches applyRotationAndCover)\n    function drawRotFit(ctx, img, dx, dy, dw, dh, rotDeg) {\n      rotDeg = rotDeg || 0;\n      var swapped = (Math.abs(Math.round(rotDeg) % 180) === 90);\n      var iW = img.naturalWidth, iH = img.naturalHeight;\n      // Cover-fit at the photo's OWN aspect (object-fit:cover happens before the\n      // CSS rotate on screen). Using the swapped aspect here scaled the photo\n      // non-uniformly into the draw rect, so every 90/270-rotated photo printed\n      // stretched while the builder showed it correct.\n      var iA = iW / iH, fA = dw / dh;\n      var sw, sh;\n      if (iA > fA) { sh = dh; sw = sh * iA; } else { sw = dw; sh = sw / iA; }\n      var es = swapped ? Math.max(dw / dh, dh / dw) : 1;\n      // Pre-downsample large source photos for clean anti-aliasing. The ctx.scale(es)\n      // below magnifies the drawn rect, so downsample to the FINAL painted size.\n      var src = _downsampleImg(img, Math.round(sw * es), Math.round(sh * es));\n      ctx.save();\n      _setHiQ(ctx);\n      ctx.beginPath(); ctx.rect(dx, dy, dw, dh); ctx.clip();\n      ctx.translate(dx + dw / 2, dy + dh / 2);\n      if (rotDeg) ctx.rotate(rotDeg * Math.PI / 180);\n      if (es !== 1) ctx.scale(es, es);\n      ctx.drawImage(src, -sw / 2, -sh / 2, sw, sh);\n      ctx.restore();\n    }\n\n    // Build a styled SVG string from a map object (fill/stroke/opacity applied)\n    function styledSvg(obj) {\n      // Builds a CLEAN, minimal SVG for canvas/PDF rendering.\n      //\n      // Raw SVG map files contain non-standard namespace declarations\n      // (xmlns:mapsvg, xmlns:dc, xmlns:rdf, xmlns:sodipodi, etc.) and embedded\n      // <metadata>/<defs> blocks. When serialised and loaded as a blob/data URL\n      // on a <canvas>, most browsers silently refuse to paint them \u2192 maps invisible.\n      //\n      // This function builds the SVG from scratch with:\n      //   \u2022 Only xmlns=\"http://www.w3.org/2000/svg\" (no custom namespaces)\n      //   \u2022 Only <path> elements with fill/stroke applied as attributes\n      //   \u2022 A proper <defs><filter> when showOutline is active (morphology ring)\n      //   \u2022 No metadata, no vector-effect (stroke-width recalibrated in drawSvgObj)\n      //\n      // showOutline + transparentFill (\"outline only, hollow interior\"):\n      //   Paths keep the fill colour (not \"none\") so SVG SourceAlpha has a\n      //   silhouette to dilate. The hollow-centre filter output hides the fill\n      //   visually and shows only the outer ring.\n      //\n      // hideProvinces + transparentFill (no provinces, hollow):\n      //   Province strokes are hidden (stroke = fill). Visibility comes from\n      //   showOutline; without it the map is intentionally transparent.\n      try {\n        var doc = new DOMParser().parseFromString(obj.svg || \"\", \"image/svg+xml\");\n        var svgEl = doc.querySelector(\"svg\");\n        if (!svgEl) return obj.svg || \"\";\n\n        // \u2500\u2500 Resolve viewBox \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n        var vb = svgEl.getAttribute(\"viewBox\");\n        if (!vb) {\n          var vw = svgEl.getAttribute(\"width\"), vh = svgEl.getAttribute(\"height\");\n          if (vw && vh) vb = \"0 0 \" + parseFloat(vw) + \" \" + parseFloat(vh);\n        }\n\n        var opacity     = (obj.opacity != null) ? String(obj.opacity) : \"1\";\n        var baseFill    = obj.fill   || \"#d9d9d9\";\n        var strokeColor = obj.stroke || \"#111111\";\n        var sw          = Math.max(0.5, parseFloat(obj.strokeWidth || 1.2));\n\n        // When showOutline + transparentFill: SourceAlpha needs a real fill to\n        // capture the silhouette; the filter hides the fill visually.\n        var fill = (obj.transparentFill && !obj.showOutline) ? \"none\" : baseFill;\n\n        // Province strokes: when hideProvinces, make stroke = fill so internal\n        // borders blend away. When fill is \"none\" (and no showOutline), this\n        // results in an intentionally invisible map.\n        var stroke      = obj.hideProvinces ? fill : strokeColor;\n        var strokeWidth = obj.hideProvinces ? \"1\"  : String(sw);\n        var dasharray   = (!obj.hideProvinces && obj.strokeDasharray) ? String(obj.strokeDasharray) : null;\n\n        // \u2500\u2500 showOutline: morphology-based outer-silhouette filter \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n        // Mirrors dmbEnsureOutlineFilter() used in the builder DOM.\n        // radius is in SVG user-units \u2014 drawSvgObj will recalibrate it via\n        // the same scaledSW logic it uses for path stroke-width.\n        var filterDefs   = \"\";\n        var groupFilter  = \"\";\n        if (obj.showOutline) {\n          var filterId  = (obj.filterId || \"dmbOF_pdf\").replace(/[^a-zA-Z0-9_-]/g, \"_\");\n          var hollow    = !!obj.transparentFill;\n          var filterBody =\n              '<feMorphology operator=\"dilate\" radius=\"' + sw + '\" in=\"SourceAlpha\" result=\"dilated\"/>'\n            + '<feComposite in=\"dilated\" in2=\"SourceAlpha\" operator=\"out\" result=\"ring\"/>'\n            + '<feFlood flood-color=\"' + strokeColor + '\" result=\"color\"/>'\n            + '<feComposite in=\"color\" in2=\"ring\" operator=\"in\" result=\"coloredRing\"/>';\n\n          if (hollow) {\n            // Output: outer ring only \u2014 interior remains transparent\n            filterBody += '<feComposite in=\"coloredRing\" in2=\"SourceAlpha\" operator=\"out\"/>';\n          } else {\n            // Output: original content + outer ring drawn on top\n            filterBody += '<feMerge>'\n              + '<feMergeNode in=\"SourceGraphic\"/>'\n              + '<feMergeNode in=\"coloredRing\"/>'\n              + '</feMerge>';\n          }\n\n          filterDefs  = '<defs>'\n            + '<filter id=\"' + filterId + '\" x=\"-20%\" y=\"-20%\" width=\"140%\" height=\"140%\">'\n            + filterBody + '</filter></defs>';\n          groupFilter = ' filter=\"url(#' + filterId + ')\"';\n        }\n\n        // \u2500\u2500 Paths \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n        var paths = [];\n        svgEl.querySelectorAll(\"path\").forEach(function(p) {\n          var d = p.getAttribute(\"d\");\n          if (!d) return;\n          var a = 'd=\"' + d.replace(/\"/g, \"&quot;\") + '\"'\n            + ' fill=\"' + fill + '\"'\n            + ' stroke=\"' + stroke + '\"'\n            + ' stroke-width=\"' + strokeWidth + '\"';\n          if (dasharray) a += ' stroke-dasharray=\"' + dasharray + '\"';\n          paths.push('<path ' + a + '/>');\n        });\n\n        return '<svg xmlns=\"http://www.w3.org/2000/svg\"'\n          + (vb ? ' viewBox=\"' + vb + '\"' : '')\n          + ' opacity=\"' + opacity + '\">'\n          + filterDefs\n          + '<g' + groupFilter + '>' + paths.join(\"\") + '</g>'\n          + '</svg>';\n      } catch(e) { return obj.svg || \"\"; }\n    }\n\n    async function drawSvgObj(ctx, obj, dx, dy, dw, dh) {\n      var markup = styledSvg(obj);\n      // styledSvg sets vector-effect=\"non-scaling-stroke\" \u2014 correct for the\n      // builder (always 1.2px on screen) but UNRELIABLE when SVG is loaded as\n      // an external image (browsers silently ignore vector-effect in that context).\n      // Result: stroke scales by viewBoxWidth/renderWidth. Geographic SVGs have\n      // large viewBox coordinates \u2192 stroke \u2192 0px \u2192 map completely invisible.\n      //\n      // FIX: parse the SVG viewBox, remove vector-effect from all paths, and\n      // recompute stroke-width in SVG user-units so the stroke renders as exactly\n      // TARGET_STROKE_PX pixels in the intermediate bitmap \u2014 regardless of viewBox.\n      // ctx.drawImage then upscales to the PDF canvas size, giving a clearly\n      // visible ~5\u20138px border line at print resolution.\n\n      var builderW = Math.max(1, obj.w || 200);\n      var builderH = Math.max(1, obj.h || 200);\n      // ONE factor for both axes, so the bitmap's aspect always equals the\n      // destination's. Rasterise at the real print scale (dw / builderW) rather\n      // than a fixed x2, capped so a full-page map cannot blow past the browser's\n      // maximum bitmap dimension.\n      var _mapK   = Math.max(2, Math.min(dw / builderW, 4096 / Math.max(builderW, builderH)));\n      var renderW = Math.round(builderW * _mapK);  // intermediate bitmap width\n      var renderH = Math.round(builderH * _mapK);  // intermediate bitmap height\n\n      try {\n        var tmpDoc = new DOMParser().parseFromString(markup, \"image/svg+xml\");\n        var tmpSvg = tmpDoc.querySelector(\"svg\");\n        if (tmpSvg) {\n          // \u2500\u2500 Set explicit bitmap dimensions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n          tmpSvg.setAttribute(\"width\",  String(renderW));\n          tmpSvg.setAttribute(\"height\", String(renderH));\n\n          // \u2500\u2500 Compute viewBox-aware stroke width \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n          // In the builder, vector-effect:non-scaling-stroke makes the stroke\n          // always appear as exactly obj.strokeWidth CSS pixels regardless of\n          // zoom.  For the PDF we must convert that pixel value to SVG user\n          // units at the intermediate bitmap resolution.\n          //\n          // Formula:\n          //   builderW pixels wide \u2194 vbW SVG user units\n          //   \u2192 1 SVG unit = builderW/vbW px in the builder\n          //   \u2192 user wants userSW px \u2192 in SVG units = userSW * (vbW / builderW)\n          //\n          // renderW = builderW * 2, so the render is 2\u00D7 the builder size and\n          // the stroke appears 2\u00D7 larger in the bitmap \u2014 ctx.drawImage then\n          // scales it down proportionally when drawing onto the PDF canvas.\n          var vbStr   = tmpSvg.getAttribute(\"viewBox\") || \"\";\n          var vbParts = vbStr.trim().split(/[\\s,]+/);\n          // viewBox = \"minX minY width height\" \u2192 width is index 2\n          var vbW        = (vbParts.length >= 3) ? (parseFloat(vbParts[2]) || renderW) : renderW;\n          var svgPxScale = renderW / vbW; // how many px per SVG user unit\n          // User's intended stroke width in builder CSS pixels\n          var userSW   = Math.max(0.5, parseFloat(obj.strokeWidth || 1.2));\n          // Convert to SVG user units so it renders proportionally in the PDF\n          var scaledSW = userSW * vbW / Math.max(1, builderW);\n\n          tmpSvg.querySelectorAll(\"path\").forEach(function(p) {\n            // Remove vector-effect \u2014 it's ignored in external-image context\n            // and causes unpredictable stroke scaling\n            p.removeAttribute(\"vector-effect\");\n            // Apply viewBox-calibrated stroke width (skip if stroke already none)\n            if (p.getAttribute(\"stroke\") !== \"none\") {\n              p.setAttribute(\"stroke-width\", String(scaledSW.toFixed(4)));\n            }\n          });\n          // \u2500\u2500 Outline ring: USER UNITS here, unlike the builder \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n          // styledSvg wraps the paths in an inner `<g filter=...>` (see the return\n          // statement above), so this filter runs in the SVG's USER space and the\n          // radius is in viewBox units - same space as a path's stroke-width.\n          // scaledSW is therefore correct:\n          //   ring_canvas = R * (renderW/vbW) * (dw/renderW) = R * dw/vbW\n          //               = userSW * dw/builderW = userSW * PAGE_SCALE   \u2713\n          // The BUILDER is the odd one out: the stored map markup has no <g>, so\n          // dmbEnsureOutlineFilter's filter lands on the ROOT <svg>, which runs in\n          // CSS-pixel space and needs the RAW value. Verified 2026-09-12 across all\n          // 208 dmb-map-*.svg assets and 46 template maps: none contains a <g>.\n          tmpSvg.querySelectorAll(\"feMorphology\").forEach(function(fe) {\n            fe.setAttribute(\"radius\", String(scaledSW.toFixed(4)));\n          });\n          // #24: stroke-dasharray is measured in CSS px on screen (the builder keeps\n          // vector-effect:non-scaling-stroke). Here vector-effect is stripped, so the\n          // values must be converted to user units the same way the width is, or a\n          // dashed province border prints as a dense stipple.\n          var _dashK = vbW / Math.max(1, builderW);\n          if (_dashK !== 1) {\n            tmpSvg.querySelectorAll(\"path[stroke-dasharray]\").forEach(function(p) {\n              var _da = (p.getAttribute(\"stroke-dasharray\") || \"\").trim();\n              if (!_da) return;\n              p.setAttribute(\"stroke-dasharray\", _da.split(/[\\s,]+/).map(function(v) {\n                var n = parseFloat(v);\n                return isFinite(n) ? (n * _dashK).toFixed(4) : v;\n              }).join(\" \"));\n            });\n          }\n\n          markup = new XMLSerializer().serializeToString(tmpSvg);\n        }\n      } catch(e) {}\n\n      // \u2500\u2500 Three rendering paths (all draw at renderW\u00D7renderH,\n      //    ctx.drawImage upscales to the final dw\u00D7dh on the PDF canvas) \u2500\u2500\u2500\u2500\u2500\u2500\n      var _mapName = (obj && obj.name) || 'map';\n\n      // Path 1: createImageBitmap \u2014 fastest; may fail for complex SVGs.\n      try {\n        var svgBlob1 = new Blob([markup], { type: \"image/svg+xml\" });\n        var bitmap   = await createImageBitmap(svgBlob1, {\n          resizeWidth:  renderW,\n          resizeHeight: renderH\n        });\n        ctx.save(); ctx.drawImage(bitmap, dx, dy, dw, dh); ctx.restore();\n        return;\n      } catch(e1) { /* fall through */ }\n\n      // Path 2: Blob URL + <img> \u2014 reliable fallback. Always revoke the URL.\n      try {\n        var svgBlob2 = new Blob([markup], { type: \"image/svg+xml\" });\n        var blobUrl  = URL.createObjectURL(svgBlob2);\n        try {\n          var blobImg = await loadImg(blobUrl);\n          ctx.save(); ctx.drawImage(blobImg, dx, dy, dw, dh); ctx.restore();\n          return;\n        } finally {\n          URL.revokeObjectURL(blobUrl);\n        }\n      } catch(e2) { /* fall through */ }\n\n      // Path 3: base64 data URL \u2014 last resort (avoids URL-encoding issues).\n      try {\n        var b64     = btoa(unescape(encodeURIComponent(markup)));\n        var dataUrl = \"data:image/svg+xml;base64,\" + b64;\n        var dataImg = await loadImg(dataUrl);\n        ctx.save(); ctx.drawImage(dataImg, dx, dy, dw, dh); ctx.restore();\n      } catch(e3) {\n        console.error('[DMB] Map \"' + _mapName + '\" failed all 3 render paths:', e3.message || e3);\n      }\n    }\n\n    // Render free objects (stickers / maps / photo-frames) onto ctx\n    async function drawFreeObjs(ctx, objects, scale) {\n      if (!Array.isArray(objects)) return;\n      var sorted = objects.slice().sort(function(a, b) { return (a.z || 0) - (b.z || 0); });\n      for (var i = 0; i < sorted.length; i++) {\n        var obj = sorted[i];\n        if (!obj || !obj.type) continue;\n        // Lines: migrate old rect-model + sync the axis-aligned bbox (rotation 0)\n        // BEFORE the transform below reads x/y/w/h/rotation.\n        if (obj.type === \"shape\" && obj.shapeType === \"line\") { try { _dmbLineRecomputeBox(obj); } catch (e) {} }\n        // .dmb-photo-frame-object carries `border:2px dashed transparent` with\n        // box-sizing:border-box, so on screen the photo sits 2px inside the box.\n        var _fbw = (obj.type === \"photo-frame\") ? 2 * scale : 0;\n        var ox = (obj.x || 0) * scale;\n        var oy = (obj.y || 0) * scale;\n        var ow = (obj.w || 100) * scale;\n        var oh = (obj.h || 100) * scale;\n        var rotDeg = obj.rotation || 0;\n        try {\n          ctx.save();\n          // \u2500\u2500 Contain-to-page PDF clip \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n          // CSS clip-path only works in the browser \u2014 the canvas needs its own\n          // clip rect. For cover items with obj.contained, clip the drawing to\n          // the zone/spread boundary in the SPREAD canvas coordinate system\n          // (before the item's own transforms are applied).\n          if (obj.coverZone) {\n            var _cFPS  = FACE_PX  * scale;\n            var _cSPS  = SPINE_PX * scale;\n            // Magazine has no spine \u2014 front starts at FACE_PX (300), books at COVER_FRONT_X (322)\n            var _cFXS  = (window.DMB_MAGAZINE_MODE ? FACE_PX : COVER_FRONT_X) * scale;\n            var _cCL, _cCR;\n            if (obj.contained === 2) {\n              _cCL = 0;\n              _cCR = (window.DMB_MAGAZINE_MODE ? FACE_PX * 2 : FACE_PX * 2 + SPINE_PX) * scale;\n            } else {\n              if (obj.coverZone === \"back\")       { _cCL = 0;      _cCR = _cFPS; }\n              else if (obj.coverZone === \"spine\") { _cCL = _cFPS;  _cCR = _cFPS + _cSPS; }\n              else /* front */                    { _cCL = _cFXS;  _cCR = _cFXS + _cFPS; }\n            }\n            ctx.beginPath();\n            ctx.rect(_cCL, 0, _cCR - _cCL, FACE_OUT_H);\n            ctx.clip();\n          }\n          ctx.translate(ox + ow / 2, oy + oh / 2);\n          if (rotDeg) ctx.rotate(rotDeg * Math.PI / 180);\n          ctx.translate(-ow / 2, -oh / 2);\n          if (obj.type === \"sticker\" && (obj.tightUrl || obj.url)) {\n            if (obj.tightUrl) {\n              // Pre-cropped data URL \u2014 no CORS issues, use it directly\n              const stickerImg = await loadImg(obj.tightUrl);\n              ctx.drawImage(stickerImg, 0, 0, ow, oh);\n            } else {\n              // Older sticker without tightUrl: fetch via XHR to get a fresh CORS response\n              // and avoid canvas tainting from the browser's non-CORS thumbnail cache\n              const corsUrl = await (async () => {\n                try {\n                  const resp = await fetch(obj.url, { mode: \"cors\", cache: \"no-store\" });\n                  const blob = await resp.blob();\n                  return URL.createObjectURL(blob);\n                } catch(fe) { return obj.url; }\n              })();\n              var stickerImg = null;\n              try {\n                stickerImg = await loadImg(corsUrl);\n              } catch (sErr) {\n                try { stickerImg = await loadImg(obj.url); } catch (sErr2) { stickerImg = null; }\n              }\n              if (corsUrl.startsWith(\"blob:\")) URL.revokeObjectURL(corsUrl);\n              if (!stickerImg) {\n                try { _dmbRenderNote(\"photo_missing\", \"sticker:\" + (obj.name || obj.id || obj.url)); } catch (e) {}\n                console.warn(\"Sticker failed to load, omitted from PDF:\", obj.url);\n                throw new Error(\"sticker unavailable: \" + (obj.name || obj.url));\n              }\n              const _sc = (obj.cropX !== undefined && obj.cropW) ? _dmbStickerCropFor(obj, stickerImg) : null;\n              if (_sc) {\n                ctx.drawImage(stickerImg, _sc.cropX, _sc.cropY, _sc.cropW, _sc.cropH, 0, 0, ow, oh);\n              } else {\n                ctx.drawImage(stickerImg, 0, 0, ow, oh);\n              }\n            }\n          } else if (obj.type === \"mag-icon\" && obj.iconId) {\n            try {\n              const iconUrl = dmbMagIconUrl(obj.iconId);\n              const corsUrl = await (async () => {\n                try {\n                  const resp = await fetch(iconUrl, { mode: \"cors\", cache: \"no-store\" });\n                  const blob = await resp.blob();\n                  return URL.createObjectURL(blob);\n                } catch(fe) { return iconUrl; }\n              })();\n              const iconImg = await loadImg(corsUrl);\n              if (corsUrl.startsWith(\"blob:\")) URL.revokeObjectURL(corsUrl);\n              const tmpC = document.createElement(\"canvas\");\n              tmpC.width = ow; tmpC.height = oh;\n              const tmpX = tmpC.getContext(\"2d\");\n              // Screen uses object-fit:contain - letterbox, never distort.\n              var _inw = iconImg.naturalWidth || ow, _inh = iconImg.naturalHeight || oh;\n              var _icr = Math.min(ow / _inw, oh / _inh);\n              var _idw = _inw * _icr, _idh = _inh * _icr;\n              tmpX.drawImage(iconImg, (ow - _idw) / 2, (oh - _idh) / 2, _idw, _idh);\n              tmpX.globalCompositeOperation = \"source-in\";\n              tmpX.fillStyle = obj.color || \"#000000\";\n              tmpX.fillRect(0, 0, ow, oh);\n              ctx.drawImage(tmpC, 0, 0, ow, oh);\n            } catch(iconErr) {}\n          } else if (obj.type === \"map\" && obj.svg) {\n            await drawSvgObj(ctx, obj, 0, 0, ow, oh);\n            // Draw markers on top of the map SVG\n            if (Array.isArray(obj.markers) && obj.markers.length > 0) {\n              // Fixed CSS sizes from createMarkerElement / .dmb-map-marker-dot,\n              // taken to print scale. The element is centred on the anchor point\n              // (inline transform: translate(-50%,-50%)).\n              var mS = scale;\n              obj.markers.forEach(function(md) {\n                var mX = ((md.x || 0) / 100) * ow;\n                var mY = ((md.y || 0) / 100) * oh;\n                var mColor = md.color || \"#e74c3c\";\n                var mType  = md.type  || \"pin\";\n                ctx.save();\n                if (mType === \"dot\") {\n                  // 14x14 box, 2px white border inside it (border-box).\n                  ctx.beginPath(); ctx.arc(mX, mY, 7 * mS, 0, Math.PI * 2);\n                  ctx.fillStyle = \"#ffffff\"; ctx.fill();\n                  ctx.beginPath(); ctx.arc(mX, mY, 5 * mS, 0, Math.PI * 2);\n                  ctx.fillStyle = mColor; ctx.fill();\n                } else if (mType === \"pin\") {\n                  // The 18x26 SVG, centred on the anchor: head at y=9, tip at y=26.\n                  var _px = mX - 9 * mS, _py = mY - 13 * mS;\n                  ctx.beginPath();\n                  ctx.arc(_px + 9 * mS, _py + 9 * mS, 7.5 * mS, 0, Math.PI * 2);\n                  ctx.fillStyle = mColor; ctx.fill();\n                  ctx.strokeStyle = \"#ffffff\"; ctx.lineWidth = 1.5 * mS; ctx.stroke();\n                  ctx.beginPath();\n                  ctx.moveTo(_px + 9 * mS,    _py + 26 * mS);\n                  ctx.lineTo(_px + 3.5 * mS,  _py + 15 * mS);\n                  ctx.lineTo(_px + 14.5 * mS, _py + 15 * mS);\n                  ctx.closePath();\n                  ctx.fillStyle = mColor; ctx.fill();\n                  ctx.beginPath();\n                  ctx.arc(_px + 9 * mS, _py + 9 * mS, 2.8 * mS, 0, Math.PI * 2);\n                  ctx.fillStyle = \"#ffffff\"; ctx.fill();\n                } else {\n                  // font-size 22px, inherited UI face, NOT bold, text-shadow 0 1px 3px.\n                  var mChar = mType === \"heart\" ? \"\\u2665\" : \"\\u2605\";\n                  ctx.font = (22 * mS) + \"px system-ui, -apple-system, \\\"Segoe UI\\\", Roboto, sans-serif\";\n                  ctx.fillStyle = mColor; ctx.textBaseline = \"middle\"; ctx.textAlign = \"center\";\n                  ctx.shadowColor = \"rgba(0,0,0,0.4)\";\n                  ctx.shadowBlur = 3 * mS; ctx.shadowOffsetY = 1 * mS;\n                  ctx.fillText(mChar, mX, mY);\n                  ctx.shadowBlur = 0; ctx.shadowOffsetY = 0;\n                }\n                ctx.restore();\n              });\n            }\n          } else if (obj.type === \"photo-frame\") {\n            if (obj.polaroid) {\n              // White polaroid mat (+ optional light shadow); photo inset into the window.\n              ctx.save();\n              if (obj.shadow !== false) {\n                ctx.shadowColor = \"rgba(0,0,0,0.18)\";\n                ctx.shadowBlur = 16 * scale;\n                ctx.shadowOffsetY = 6 * scale;\n              }\n              ctx.fillStyle = \"#ffffff\";\n              ctx.fillRect(0, 0, ow, oh);\n              ctx.restore();\n              var _piP = dmbPolaroidInsetVals(obj.w, obj.h);\n              var _wx = _fbw + _piP.left * scale, _wy = _fbw + _piP.top * scale;\n              var _ww = ow - 2 * _fbw - (_piP.left + _piP.right) * scale;\n              var _wh = oh - 2 * _fbw - (_piP.top + _piP.bottom) * scale;\n              if (obj.photoId) {\n                var _phP = getPhotoById(obj.photoId);\n                if (_phP) {\n                  ctx.save();\n                  ctx.translate(_wx, _wy);\n                  if (obj.frameShape) applyCanvasFrameClip(ctx, obj.frameShape, _ww, _wh, scale);\n                  drawFit(ctx, await _dmbRenderImg(_phP), 0, 0, _ww, _wh);\n                  ctx.restore();\n                }\n              }\n            } else if (obj.photoId) {\n              var _ph = getPhotoById(obj.photoId);\n              if (_ph) {\n                ctx.save();\n                ctx.translate(_fbw, _fbw);\n                if (obj.frameShape) applyCanvasFrameClip(ctx, obj.frameShape, ow - 2 * _fbw, oh - 2 * _fbw, scale);\n                drawFit(ctx, await _dmbRenderImg(_ph), 0, 0, ow - 2 * _fbw, oh - 2 * _fbw);\n                ctx.restore();\n              }\n            }\n          } else if (obj.type === \"shape\") {\n            // \u2500\u2500 SHAPE PDF rendering \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n            // The generic transform above (translate-to-centre, rotate, translate-back)\n            // has already been applied. drawShapeOnCanvas draws in 0..100 space\n            // scaled to ow\u00D7oh. Opacity is applied via globalAlpha then reset.\n            // WARNING: Do NOT move this block outside the try{}/catch() \u2014 ctx.save()\n            // above must pair with ctx.restore() at line 11718.\n            ctx.globalAlpha = (obj.opacity !== undefined && obj.opacity !== null) ? obj.opacity : 1;\n            if (obj.shapeType === \"line\") {\n              // Draw the line path in top-left-relative coords (origin is the bbox\n              // top-left after the generic transform; rotation is 0 for lines).\n              var _lc = (obj.fill && obj.fill !== \"none\" && obj.fill !== \"transparent\") ? obj.fill : (obj.stroke || \"#111111\");\n              var _ltw = Math.max(1, (obj.thickness || 8)) * scale;\n              var _lp1 = { x: (obj.x1 - obj.x) * scale, y: (obj.y1 - obj.y) * scale };\n              var _lp2 = { x: (obj.x2 - obj.x) * scale, y: (obj.y2 - obj.y) * scale };\n              ctx.strokeStyle = _lc; ctx.lineWidth = _ltw; ctx.lineCap = \"round\"; ctx.lineJoin = \"round\";\n              if (obj.strokeStyle === \"dashed\")      ctx.setLineDash([_ltw * 2.4, _ltw * 1.7]);\n              else if (obj.strokeStyle === \"dotted\") ctx.setLineDash([0.1 * scale, _ltw * 1.9]);\n              else                                   ctx.setLineDash([]);\n              ctx.beginPath();\n              ctx.moveTo(_lp1.x, _lp1.y);\n              if (obj.lineType === \"elbowed\") {\n                var _le = _dmbLineElbowPoint({ x: obj.x1, y: obj.y1 }, { x: obj.x2, y: obj.y2 });\n                ctx.lineTo((_le.x - obj.x) * scale, (_le.y - obj.y) * scale);\n                ctx.lineTo(_lp2.x, _lp2.y);\n              } else if (obj.lineType === \"curved\") {\n                _dmbLineEnsureCurve(obj);\n                var _lcc = _dmbLineQuadControl(obj);\n                ctx.quadraticCurveTo((_lcc.x - obj.x) * scale, (_lcc.y - obj.y) * scale, _lp2.x, _lp2.y);\n              } else {\n                ctx.lineTo(_lp2.x, _lp2.y);\n              }\n              ctx.stroke();\n              ctx.setLineDash([]);\n            } else {\n              var _shOp  = (obj.opacity !== undefined && obj.opacity !== null) ? obj.opacity : 1;\n              var _shPad = Math.ceil((obj.strokeWidth || 0) * scale) + 2;\n              var _shTW  = Math.ceil(ow) + _shPad * 2, _shTH = Math.ceil(oh) + _shPad * 2;\n              var _shHasBoth = (obj.fill && obj.fill !== \"none\" && obj.fill !== \"transparent\") &&\n                               (obj.strokeWidth > 0) && obj.stroke && obj.stroke !== \"transparent\";\n              if (_shOp < 1 && _shHasBoth && _shTW * _shTH <= 6000000) {\n                var _shC = document.createElement(\"canvas\");\n                _shC.width = _shTW; _shC.height = _shTH;\n                var _shX = _shC.getContext(\"2d\");\n                _shX.translate(_shPad, _shPad);\n                drawShapeOnCanvas(_shX, obj.shapeType || \"rect\", obj.fill || \"#6366f1\", obj.stroke || \"transparent\", obj.strokeWidth || 0, ow, oh, obj.cornerRadius, obj.strokeStyle, scale);\n                ctx.globalAlpha = _shOp;\n                ctx.drawImage(_shC, -_shPad, -_shPad);\n              } else {\n                drawShapeOnCanvas(ctx, obj.shapeType || \"rect\", obj.fill || \"#6366f1\", obj.stroke || \"transparent\", obj.strokeWidth || 0, ow, oh, obj.cornerRadius, obj.strokeStyle, scale);\n              }\n            }\n            ctx.globalAlpha = 1;\n          } else if (obj.type === \"text\" && obj.text) {\n            // \u2550\u2550 TEXT PDF RENDERING \u2014 replicates HTML/CSS builder exactly \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n            //\n            // The builder renders text as position:absolute divs with these properties:\n            //   \u2022 padding: 2px 6px  \u2192 text content starts 6px from element left edge,\n            //                          2px from element top edge\n            //   \u2022 transform-origin: center center  \u2192 rotation around element CENTRE\n            //   \u2022 white-space: pre  \u2192 ONLY \\n splits lines, NO word-wrapping ever\n            //   \u2022 line-height: normal \u2248 1.2\u00D7 font-size (browser default)\n            //   \u2022 transform: rotate(Rdeg)  \u2192 rotation stored in obj.rotate / obj.transform\n            //   \u2022 transform: translate(-50%,-50%) \u2192 obj.left/top is VISUAL CENTRE,\n            //                                        not top-left of element\n            //\n            // BUGS FIXED vs previous version:\n            //   1. Line height changed 1.1 \u2192 1.2 to match browser default\n            //   2. Word-wrap removed (white-space:pre = \\n only)\n            //   3. Padding offset added (6px H, 2px V) to non-centred text\n            //   4. Rotation anchor moved from top-left \u2192 element centre\n            //   5. Centred text vertical offset: txFontSize/2 \u2192 txTotalH/2 (multi-line)\n            //   6. Font quotes: single quotes \u2192 double quotes for canvas compatibility\n            //   7. textDecoration underline drawn manually (canvas has no text-decoration)\n            //   8. obj.width no longer falls back to parent width (fixed in savePageTextState)\n            //\n            // WARNING: ctx.restore() below undoes the generic translate+rotate applied at\n            // the top of this for-loop. Do NOT collapse the restore/save pair \u2014 text uses\n            // its own coordinate system (left/top) instead of the generic x/y.\n            ctx.restore(); // undo generic object transform (translate-to-centre, rotate)\n            ctx.save();\n            // Force-load every custom font before drawing on canvas.\n            // document.fonts.ready only waits for fonts already in the loading\n            // queue \u2014 it does NOT trigger loads for fonts not yet used in the DOM.\n            // If a font hasn't been rendered in a live element, canvas falls back\n            // to a system font and both Preview and PDF show the wrong typeface.\n            // document.fonts.load(\"weight size 'family'\") forces the browser to\n            // fetch and decode the font file regardless of DOM usage.\n            try { await document.fonts.ready; } catch(e2) {}\n            try {\n              var _fontFamilies = [\n                \"900 28px 'Londrina Solid'\", \"normal 28px 'Aurelia'\",\n                \"normal 28px 'Burbank Big Condensed'\", \"normal 28px 'White Garden Wide'\",\n                \"normal 28px 'Vogue Moderne'\", \"normal 28px 'GFS Didot'\",\n                \"normal 28px 'Prata'\", \"normal 28px 'Theano Didot'\",\n                \"normal 28px 'Lindys Diner'\", \"normal 28px 'Chomsky'\",\n                \"normal 28px 'Anton'\", \"normal 28px '70s Disco'\",\n                \"normal 28px 'Geraldine'\", \"normal 28px 'Lemon Drops'\",\n                \"normal 28px 'Mouldy Cheese'\", \"normal 28px 'Nature Beauty'\",\n                \"normal 28px 'Super Maples'\", \"normal 28px 'The Heart of Everything'\",\n                \"normal 28px 'Tomatoes'\", \"normal 28px 'Wedding Day'\"\n              ];\n              // Load each family at BOTH the authored weight and 800 \u2014 ctx.font below\n              // draws at (obj.fontWeight || 800), and font matching under a weight the\n              // browser never loaded can silently fall back to a system font.\n              var _fontLoads = [];\n              _fontFamilies.forEach(function(f) {\n                _fontLoads.push(document.fonts.load(f));\n                _fontLoads.push(document.fonts.load(f.replace(/^\\S+/, \"800\")));\n              });\n              await Promise.allSettled(_fontLoads);\n            } catch(e3) {}\n\n            // \u2500\u2500 Padding constants matching builder CSS \"padding: 2px 6px\" \u2500\u2500\u2500\u2500\u2500\u2500\n            var TX_PL = 6, TX_PT = 2; // horizontal pad (each side), vertical pad (each side)\n\n            var txLeft    = parseFloat(obj.left || \"0\");\n            var txTop     = parseFloat(obj.top  || \"0\");\n            var txFontPx  = parseFloat(obj.fontSize || \"28\");\n            var txFontSize = txFontPx * scale;\n\n            // Font: replace ' with \" so multi-word names work in canvas ctx.font\n            var txFontFam = (obj.fontFamily || \"sans-serif\").replace(/'/g, '\"');\n            ctx.font      = (obj.fontWeight || \"800\") + \" \" + txFontSize + \"px \" + txFontFam;\n            ctx.fillStyle  = obj.color || \"#666\";\n            ctx.textBaseline = \"alphabetic\";\n            ctx.textAlign    = \"left\"; // reset; set correctly per line below\n            // The box's own face. Drawing a run switches ctx.font, so everything\n            // below puts this back afterwards and no later measurement can be taken\n            // through a run's font by accident.\n            var _txBaseFont = ctx.font;\n\n            // \u2500\u2500 Line height: match builder CSS \"line-height: 1.1\" exactly \u2500\u2500\u2500\u2500\u2500\u2500\n            // The previous value (1.2) was larger than the CSS value, so each\n            // text box's content sat progressively lower in the PDF than in the\n            // builder, causing stacked text boxes to appear increasingly mis-spaced.\n            var txLineH = txFontSize * 1.1;\n\n            // \u2500\u2500 Half-leading offset \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n            // CSS line-height: 1.1 distributes the 0.1\u00D7fontSize \"leading\" equally\n            // above and below each glyph (half-leading = 0.05\u00D7fontSize).  The top\n            // padding starts the LINE BOX at top+2, but the glyph itself is\n            // 0.05\u00D7fontSize lower inside that box.  canvas textBaseline:\"top\" draws\n            // at the em-square top (no half-leading), so we add it back so the first\n            // visible glyph lands at the same pixel as in the browser.\n            var txHalfLead = txFontSize * 0.05;   // superseded by txFirstBase below\n\n            // First baseline, from the font's REAL vertical metrics. CSS puts it at\n            //   contentTop + (lineHeight - (ascent + descent)) / 2 + ascent\n            // The constant above simplifies that to contentTop + 0.05em, which only\n            // holds when ascent + descent == 1em. Anton, Burbank and the other\n            // display faces are far taller, so their text printed several\n            // millimetres below where the builder drew it. The glyph-centre branch\n            // already used real metrics; these branches now do too.\n            var _txM0  = ctx.measureText(\"Mg\");\n            var _txFA  = (_txM0 && _txM0.fontBoundingBoxAscent  > 0) ? _txM0.fontBoundingBoxAscent  : txFontSize * 0.80;\n            var _txFD  = (_txM0 && _txM0.fontBoundingBoxDescent > 0) ? _txM0.fontBoundingBoxDescent : txFontSize * 0.20;\n            var txFirstBase = (txLineH - (_txFA + _txFD)) / 2 + _txFA;\n            // Underline sits just below the baseline, proportional to the descent.\n            var _txUOff = (_txFD > 0)\n              ? Math.max(txFontSize * 0.06, Math.min(_txFD * 0.55, txFontSize * 0.2))\n              : txFontSize * 0.11;\n\n            // \u2500\u2500 Lines: split on \\n. If the box was narrowed with the side grip\n            //    (white-space:pre-wrap), word-wrap each paragraph to the box content\n            //    width so the PDF matches the builder. Otherwise \\n only (no wrap). \u2500\u2500\n            var _txWraps  = (obj.whiteSpace === \"pre-wrap\");\n            var _txWrapW  = (obj.width && parseFloat(obj.width) > 0) ? (parseFloat(obj.width) - TX_PL * 2) * scale : 0;\n            var txLines = null;\n            // Prefer the builder's CAPTURED rendered lines (see _captureCoverTextBoxes):\n            // the exact word-per-line the customer sees. Canvas re-wrapping below is\n            // only the fallback (canvas glyph widths can wrap at a different word).\n            if (_txWraps && obj.renderedLines) {\n              try {\n                var _capL = JSON.parse(obj.renderedLines);\n                if (Array.isArray(_capL) && _capL.length) txLines = _capL;\n              } catch (eCL) {}\n            }\n            // No captured lines (design saved before page text stored them): let the\n            // BROWSER wrap an offscreen replica instead of guessing with canvas\n            // metrics, so older designs also print the breaks the builder shows.\n            if (!txLines && _txWraps) {\n              var _domL = _dmbWrapLinesViaDom(obj);\n              if (_domL && _domL.length) txLines = _domL;\n            }\n            if (txLines) {\n              // captured or DOM-measured \u2014 nothing to compute\n            } else if (_txWraps && _txWrapW > 0) {\n              txLines = [];\n              obj.text.split(\"\\n\").forEach(function (_para) {\n                var _words = _para.split(\" \");\n                var _cur = \"\";\n                for (var _wi = 0; _wi < _words.length; _wi++) {\n                  var _test = _cur ? (_cur + \" \" + _words[_wi]) : _words[_wi];\n                  if (!_cur || ctx.measureText(_test).width <= _txWrapW) { _cur = _test; }\n                  else { txLines.push(_cur); _cur = _words[_wi]; }\n                }\n                txLines.push(_cur);\n              });\n            } else {\n              txLines = obj.text.split(\"\\n\");\n            }\n            // \u2500\u2500 Inline runs: a box whose WORDS carry their own look \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n            // obj.richLines is the box split into the visual lines on screen, each\n            // line a list of runs. It exists only for a box that actually holds more\n            // than one look, so every other box goes down the identical path it\n            // always did - not a reimplementation of it.\n            //\n            // The guard refuses a richLines that no longer spells the same text as\n            // obj.text. A stale capture would print the wrong WORDS, and losing a\n            // colour on one render is recoverable where that is not.\n            var _rl = null;\n            if (obj.richLines) {\n              try {\n                var _rlP = JSON.parse(obj.richLines);\n                var _rlStyled = Array.isArray(_rlP) && _rlP.length && _rlP.some(function (ln) {\n                  return Array.isArray(ln) && ln.some(function (r) {\n                    return r && r.s && Object.keys(r.s).some(function (k) { return !!r.s[k]; });\n                  });\n                });\n                if (_rlStyled) {\n                  var _cand = _rlP.map(function (ln) {\n                    return (Array.isArray(ln) ? ln : []).filter(function (r) {\n                      return r && typeof r.t === \"string\";\n                    });\n                  });\n                  var _candTxt = _cand.map(function (ln) {\n                    return ln.map(function (r) { return r.t; }).join(\"\");\n                  }).join(\"\");\n                  var _strip = function (v) { return String(v == null ? \"\" : v).replace(/\\s+/g, \"\"); };\n                  if (_strip(_candTxt) === _strip(obj.text)) _rl = _cand;\n                }\n              } catch (eRL) {}\n            }\n            if (_rl) txLines = _rl.map(function (ln) { return ln.map(function (r) { return r.t; }).join(\"\"); });\n\n            // One run's face: everything it did not override comes from the box.\n            function _txRunFace(r) {\n              var st = (r && r.s) || {};\n              var px = st.fs ? parseFloat(st.fs) : txFontPx;\n              if (!isFinite(px) || px <= 0) px = txFontPx;\n              px *= scale;\n              var fam  = String(st.ff || obj.fontFamily || \"sans-serif\").replace(/'/g, '\"');\n              var wt   = st.fw || obj.fontWeight || \"800\";\n              var ital = (st.fi === \"italic\" || st.fi === \"oblique\") ? \"italic \" : \"\";\n              // An explicit \"none\" on the run has to BEAT the box's underline, so the\n              // test is \"did the run say anything\", not \"is it underlined\".\n              var und  = (st.td != null && st.td !== \"\")\n                ? (String(st.td).indexOf(\"underline\") !== -1)\n                : !!(obj.textDecoration && obj.textDecoration.indexOf(\"underline\") !== -1);\n              return { font: ital + wt + \" \" + px + \"px \" + fam, px: px,\n                       color: st.c || obj.color || \"#666\", under: und };\n            }\n            function _txRunLineW(ln) {\n              var w = 0;\n              for (var i = 0; i < ln.length; i++) {\n                ctx.font = _txRunFace(ln[i]).font;\n                w += ctx.measureText(ln[i].t).width;\n              }\n              ctx.font = _txBaseFont;\n              return w;\n            }\n            // How tall one line is, by the actual CSS rule for a line box: every run\n            // contributes its own line-height (1.1 x its OWN size) split equally above\n            // and below its glyphs, and the line is as tall as the tallest\n            // contributor. The box's own font always takes part - that is the CSS\n            // strut, and it is why one small word inside a big box does not pull the\n            // lines closer together. With a single size this collapses exactly to\n            // txFirstBase and txLineH, which is why an ordinary box still lands on\n            // the same pixel it did before.\n            function _txRunLineMetrics(ln) {\n              var above = 0, below = 0;\n              var take = function (fontStr, px) {\n                ctx.font = fontStr;\n                var m = ctx.measureText(\"Mg\");\n                var A = (m && m.fontBoundingBoxAscent  > 0) ? m.fontBoundingBoxAscent  : px * 0.80;\n                var D = (m && m.fontBoundingBoxDescent > 0) ? m.fontBoundingBoxDescent : px * 0.20;\n                var half = (px * 1.1 - (A + D)) / 2;\n                if (half + A > above) above = half + A;\n                if (half + D > below) below = half + D;\n              };\n              take(_txBaseFont, txFontSize);\n              for (var i = 0; i < ln.length; i++) { var f = _txRunFace(ln[i]); take(f.font, f.px); }\n              ctx.font = _txBaseFont;\n              return { above: above, below: below, h: above + below };\n            }\n            // A line's real INK extents, for the glyph-centre anchor.\n            function _txRunLineInk(ln) {\n              var asc = 0, dsc = 0;\n              for (var i = 0; i < ln.length; i++) {\n                var f = _txRunFace(ln[i]);\n                ctx.font = f.font;\n                var m = ctx.measureText(ln[i].t);\n                var a = (m.actualBoundingBoxAscent  != null) ? m.actualBoundingBoxAscent  : f.px * 0.70;\n                var d = (m.actualBoundingBoxDescent != null) ? m.actualBoundingBoxDescent : f.px * 0.20;\n                if (a > asc) asc = a;\n                if (d > dsc) dsc = d;\n              }\n              ctx.font = _txBaseFont;\n              return { asc: asc, dsc: dsc };\n            }\n            // Baseline of line li, measured DOWN from the top of the content box.\n            var _rlBase = null, _rlTotalH = 0;\n            if (_rl) {\n              _rlBase = [];\n              var _rlAcc = 0;\n              for (var _rli = 0; _rli < _rl.length; _rli++) {\n                var _rlM = _txRunLineMetrics(_rl[_rli]);\n                _rlBase.push(_rlAcc + _rlM.above);\n                _rlAcc += _rlM.h;\n              }\n              _rlTotalH = _rlAcc;\n            }\n            function _txBaseAt(li) {\n              return _rlBase ? _rlBase[li] : (li * txLineH + txFirstBase);\n            }\n            // Draw one line, run by run, from an exact left edge and baseline.\n            function _txRunDraw(ln, x, yBase) {\n              for (var i = 0; i < ln.length; i++) {\n                var f = _txRunFace(ln[i]);\n                ctx.font = f.font; ctx.fillStyle = f.color;\n                ctx.textAlign = \"left\"; ctx.textBaseline = \"alphabetic\";\n                var w = ctx.measureText(ln[i].t).width;\n                ctx.fillText(ln[i].t, x, yBase);\n                if (f.under && /\\S/.test(ln[i].t)) {\n                  var m2 = ctx.measureText(\"Mg\");\n                  var D2 = (m2 && m2.fontBoundingBoxDescent > 0) ? m2.fontBoundingBoxDescent : f.px * 0.2;\n                  var off = Math.max(f.px * 0.06, Math.min(D2 * 0.55, f.px * 0.2));\n                  ctx.save();\n                  ctx.strokeStyle = f.color;\n                  ctx.lineWidth = Math.max(1, f.px * 0.075);\n                  ctx.beginPath();\n                  ctx.moveTo(x, yBase + off);\n                  ctx.lineTo(x + w, yBase + off);\n                  ctx.stroke();\n                  ctx.restore();\n                }\n                x += w;\n              }\n              ctx.font = _txBaseFont; ctx.fillStyle = obj.color || \"#666\";\n            }\n\n            var txTotalH = _rl ? _rlTotalH : (txLines.length * txLineH); // total height of all lines\n\n            // \u2500\u2500 Width constraint (for alignment only, NOT wrapping) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n            var txMaxW = (obj.width && parseFloat(obj.width) > 0)\n              ? parseFloat(obj.width) * scale : 0;\n\n            // \u2500\u2500 Rotation: prefer obj.rotate dataset, override with transform string \u2500\u2500\n            // Both are stored in degrees.\n            var txRotD = parseFloat(obj.rotate || \"0\") || 0;\n            if (obj.transform) {\n              // FIX: regex handles scientific notation (e.g. \"rotate(-2.84217e-14deg)\")\n              // so floating-point drift doesn't parse as \"-2.84217\" (a real rotation).\n              var _txRotM = obj.transform.match(/rotate\\(\\s*(-?[\\d.]+(?:[eE][+-]?\\d+)?)/);\n              if (_txRotM) txRotD = parseFloat(_txRotM[1]);\n            }\n            // Clamp near-zero float drift to exactly 0. Values like -2.84e-14 accumulate\n            // from repeated rotate-then-unrotate operations and should use the simpler\n            // non-rotated rendering path \u2014 not the rotated path with a ~0\u00B0 rotation.\n            if (Math.abs(txRotD) < 0.001) txRotD = 0;\n\n            // \u2500\u2500 Layout mode detection \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n            // txCentered: transform contains translate(-50%,-50%) so obj.left/top\n            // is the VISUAL CENTRE of the element (not top-left of the box).\n            var txCentered = !!(obj.transform && obj.transform.indexOf(\"translate(-50%\") !== -1);\n\n            // \u2500\u2500 Natural content width = widest line (canvas coords) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n            // When obj.width is not set (txMaxW === 0), we still need a reference\n            // width so that centre/right alignment works correctly across all lines.\n            // Example: \"our\\nstory\" centred \u2192 \"story\" is widest; \"our\" must be\n            // centred WITHIN \"story\"'s width, not left-aligned at the same x.\n            // txEffectiveW is the width used for alignment (explicit or natural).\n            var txNaturalW = 0;\n            txLines.forEach(function(l, li) {\n              var w = _rl ? _txRunLineW(_rl[li]) : ctx.measureText(l).width;\n              if (w > txNaturalW) txNaturalW = w;\n            });\n            // Alignment reference = the element's CONTENT-box width. The browser\n            // centres / right-aligns text within content (= box width \u2212 horizontal\n            // padding). Prefer the REAL measured box (boxWidth) so it matches the\n            // builder exactly \u2014 including min-width boxes wider than the glyphs;\n            // fall back to an explicit width, then to the natural glyph width.\n            // .dmb-editable-text { min-width: 120px } - an auto-width caption is\n            // never narrower than that on screen, so a box with no stored boxWidth\n            // must not collapse to the glyph width here. Spine text clears min-width\n            // inline (it takes the txCentered branch), so it is excluded.\n            var TX_MINW = 120;\n            var _txBoxWpx = (obj.boxWidth && parseFloat(obj.boxWidth) > 0)\n              ? parseFloat(obj.boxWidth)\n              : (txMaxW > 0 ? txMaxW / scale\n                            : (txCentered ? (txNaturalW / scale + TX_PL * 2)\n                                          : Math.max(TX_MINW, txNaturalW / scale + TX_PL * 2)));\n            var txContentW = Math.max(0, _txBoxWpx - TX_PL * 2) * scale;\n            var txEffectiveW = txContentW > 0 ? txContentW : txNaturalW;\n\n            // \u2500\u2500 Helper: draw one line with correct alignment + optional underline \u2500\u2500\n            // xOrigin: canvas-coord left edge of the content area\n            // We always use ctx.textAlign = \"left\" and compute x manually so that\n            // each line is independently centred/right-aligned within txEffectiveW.\n            // This matches CSS text-align behaviour for multi-line text.\n            function _txDrawLine(line, xOrigin, y, li) {\n              var lw = (_rl && li != null) ? _txRunLineW(_rl[li]) : ctx.measureText(line).width;\n              var x;\n              ctx.textAlign = \"left\"; // always draw left-to-right; we shift x ourselves\n              // Unset alignment defaults to CENTER to match the builder CSS\n              // (.dmb-editable-text { text-align:center }). Only an explicit\n              // \"left\" left-aligns.\n              var _ta = obj.textAlign || \"center\";\n              if (_ta === \"center\") {\n                // Centre this line within the content width\n                x = xOrigin + (txEffectiveW - lw) / 2;\n              } else if (_ta === \"right\") {\n                // Right-align this line to the content width's right edge\n                x = xOrigin + txEffectiveW - lw;\n              } else {\n                // Left-align (explicit)\n                x = xOrigin;\n              }\n              // A line of runs is drawn from the same x this function just worked\n              // out, so alignment is decided in ONE place for both kinds of box.\n              if (_rl && li != null) { _txRunDraw(_rl[li], x, y); return; }\n              ctx.fillText(line, x, y);\n              // Underline: canvas has no text-decoration \u2014 draw a line manually\n              if (obj.textDecoration && obj.textDecoration.indexOf(\"underline\") !== -1) {\n                // y is the line's em-top (textBaseline:\"top\"): baseline \u2248 +0.88em,\n                // browser underline \u2248 +0.1em below that \u2192 0.98em (1.05 sat too low).\n                ctx.save();\n                ctx.strokeStyle = obj.color || \"#666\";\n                ctx.lineWidth   = Math.max(1, txFontSize * 0.075);\n                ctx.beginPath();\n                ctx.moveTo(x,      y + _txUOff);\n                ctx.lineTo(x + lw, y + _txUOff);\n                ctx.stroke();\n                ctx.restore();\n              }\n            }\n\n            var _hasGlyph = (obj.glyphCx !== undefined && obj.glyphCx !== null && obj.glyphCx !== \"\" &&\n                            obj.glyphCy !== undefined && obj.glyphCy !== null && obj.glyphCy !== \"\");\n            var _hasVis = (obj.visCx !== undefined && obj.visCx !== null && obj.visCx !== \"\" &&\n                           obj.visCy !== undefined && obj.visCy !== null && obj.visCy !== \"\");\n\n            if (_hasGlyph && _rl) {\n              // \u2500\u2500 GLYPH-CENTRE LAYOUT, run by run \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n              // Same anchor as the branch below (the letters' real ink centre), but\n              // the ink box is measured across the runs: its top is the first line's\n              // baseline less that line's tallest ascent, its bottom the last line's\n              // baseline plus its deepest descent. Centre that on the anchor and\n              // every line follows from the baselines already computed above.\n              ctx.translate(parseFloat(obj.glyphCx) * scale, parseFloat(obj.glyphCy) * scale);\n              if (txRotD) ctx.rotate(txRotD * Math.PI / 180);\n              var _rInk = _rl.map(_txRunLineInk);\n              var _rW   = _rl.map(_txRunLineW);\n              var _rN   = _rl.length;\n              var _rShift = ((_txBaseAt(0) - _rInk[0].asc) +\n                             (_txBaseAt(_rN - 1) + _rInk[_rN - 1].dsc)) / 2;\n              var _rMax = 0;\n              _rW.forEach(function (w) { if (w > _rMax) _rMax = w; });\n              var _rTa = obj.textAlign || \"center\";\n              for (var _rI = 0; _rI < _rN; _rI++) {\n                var _rX = (_rTa === \"right\") ? (_rMax / 2 - _rW[_rI])\n                        : (_rTa === \"left\")  ? (-_rMax / 2)\n                                             : (-_rW[_rI] / 2);\n                _txRunDraw(_rl[_rI], _rX, _txBaseAt(_rI) - _rShift);\n              }\n\n            } else if (_hasGlyph) {\n              // \u2500\u2500 GLYPH-CENTRE LAYOUT (most exact) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n              // Anchor on the letters' real measured INK centre (glyphCx/glyphCy \u2014\n              // captured from the live DOM via Range.getBoundingClientRect, which\n              // spans ALL lines) and lay the block out from the font's actual ink\n              // metrics. This removes every box/padding/line-height/baseline\n              // assumption \u2014 what the builder shows IS what the PDF draws.\n              // Includes txCentered (spine) text: the glyph capture reads the\n              // rotation from the CSS transform string, so glyphCx/glyphCy are the\n              // true post-rotation letter centre.\n              // 2026-07-10: extended from single-line to MULTI-LINE. Multi-line\n              // previously used the box-based MEASURED path below, whose\n              // \"+0.05em half-leading with textBaseline:top\" placement only holds\n              // for fonts whose ascent+descent \u2248 1em \u2014 display fonts (Anton,\n              // Burbank\u2026) have bigger vertical metrics, so multi-line titles\n              // drifted vertically in the preview/PDF while single lines were exact.\n              ctx.translate(parseFloat(obj.glyphCx) * scale, parseFloat(obj.glyphCy) * scale);\n              if (txRotD) ctx.rotate(txRotD * Math.PI / 180);\n              ctx.textBaseline = \"alphabetic\";\n              // Per-line ink metrics. Baseline-to-baseline spacing IS the CSS\n              // line-height (txLineH = 1.1em) \u2014 the browser applies that literally,\n              // so only the block's END metrics (first ascent / last descent) are\n              // needed to centre the ink box on the anchor.\n              var _gms = txLines.map(function(l) { return ctx.measureText(l); });\n              var _ascOf = function(m) { return (m.actualBoundingBoxAscent  != null) ? m.actualBoundingBoxAscent  : txFontSize * 0.70; };\n              var _dscOf = function(m) { return (m.actualBoundingBoxDescent != null) ? m.actualBoundingBoxDescent : txFontSize * 0.20; };\n              var _n = txLines.length;\n              // Ink box spans firstBaseline\u2212asc0 \u2026 firstBaseline+(n\u22121)\u00B7lineH+dscLast.\n              // Centre it on the origin \u2192 first baseline sits at:\n              var _b0 = (_ascOf(_gms[0]) - (_n - 1) * txLineH - _dscOf(_gms[_n - 1])) / 2;\n              // Horizontal: centre-aligned lines centre on the anchor. Left/right\n              // lines align to the block's widest line \u2014 for those alignments the\n              // DOM ink box the capture measured is exactly [inkLeft, inkLeft+wMax],\n              // so its centre (= the anchor) sits wMax/2 from the shared edge.\n              var _wMax = 0; _gms.forEach(function(m) { if (m.width > _wMax) _wMax = m.width; });\n              var _ta2 = obj.textAlign || \"center\";\n              txLines.forEach(function(line, li) {\n                var _y  = _b0 + li * txLineH;\n                var _lw = _gms[li].width;\n                var _x;\n                ctx.textAlign = \"left\";\n                if (_ta2 === \"right\")     _x =  _wMax / 2 - _lw;\n                else if (_ta2 === \"left\") _x = -_wMax / 2;\n                else                      _x = -_lw / 2;          // center (default)\n                ctx.fillText(line, _x, _y);\n                if (obj.textDecoration && obj.textDecoration.indexOf(\"underline\") !== -1) {\n                  // Underline position from the font's REAL metrics: browsers put it\n                  // just below the baseline, proportional to the font's descent \u2014\n                  // a fixed 0.12em was visibly off for fonts with unusual metrics.\n                  var _fbd = _gms[li].fontBoundingBoxDescent;\n                  var _uOff = (_fbd != null && _fbd > 0)\n                    ? Math.max(txFontSize * 0.06, Math.min(_fbd * 0.55, txFontSize * 0.2))\n                    : txFontSize * 0.11;\n                  ctx.save();\n                  ctx.strokeStyle = obj.color || \"#666\";\n                  ctx.lineWidth = Math.max(1, txFontSize * 0.075);\n                  ctx.beginPath();\n                  ctx.moveTo(_x,       _y + _uOff);\n                  ctx.lineTo(_x + _lw, _y + _uOff);\n                  ctx.stroke();\n                  ctx.restore();\n                }\n              });\n\n            } else if (_hasVis && !txCentered) {\n              // \u2500\u2500 MEASURED LAYOUT (exact browser geometry) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n              // visCx/visCy = the element's TRUE visual centre (axis-aligned\n              // bounding-box centre = rotation centre), measured from the live DOM\n              // in cover-space px; boxWidth/boxHeight = the real offsetWidth/Height.\n              // This reproduces the builder pixel-for-pixel: anchor, size, rotation\n              // and alignment all come from what the browser actually rendered, so\n              // there is no %\u2192px / pivot / line-box re-derivation to drift.\n              var _mbw = (obj.boxWidth  && parseFloat(obj.boxWidth)  > 0) ? parseFloat(obj.boxWidth)  : (txNaturalW / scale + TX_PL * 2);\n              var _mbh = (obj.boxHeight && parseFloat(obj.boxHeight) > 0) ? parseFloat(obj.boxHeight) : (txTotalH / scale + TX_PT * 2);\n              ctx.translate(parseFloat(obj.visCx) * scale, parseFloat(obj.visCy) * scale);\n              if (txRotD) ctx.rotate(txRotD * Math.PI / 180);\n              // Box spans [-bw/2,+bw/2] around the centre; content is inset by padding.\n              // First line's em-top = box top + top padding + half-leading.\n              var _mox = (-_mbw / 2 + TX_PL) * scale;\n              var _moy = (-_mbh / 2 + TX_PT) * scale + txFirstBase;\n              txLines.forEach(function(line, li) {\n                _txDrawLine(line, _mox, _moy - txFirstBase + _txBaseAt(li), li);\n              });\n\n            } else if (txCentered) {\n              // \u2500\u2500 CENTRED LAYOUT \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n              // obj.left/top is the VISUAL CENTRE of the element.\n              // Translate canvas origin to that centre, apply any rotation, then\n              // draw the whole text block centred on origin.\n              //\n              // Vertical: place line 0 at y = -(txTotalH/2), increment by txLineH.\n              // FIX: previous code used txFontSize/2 instead of txTotalH/2,\n              //      which misplaced multi-line blocks (all lines shifted down).\n              ctx.translate(txLeft * scale, txTop * scale);\n              if (txRotD) ctx.rotate(txRotD * Math.PI / 180);\n\n              txLines.forEach(function(line, li) {\n                var lw  = _rl ? _txRunLineW(_rl[li]) : ctx.measureText(line).width;\n                // y is the BASELINE now: the block's em-top is -txTotalH/2 and the\n                // first baseline sits txFirstBase below it.\n                var y   = _txBaseAt(li) - txTotalH / 2;\n                if (_rl) { _txRunDraw(_rl[li], -lw / 2, y); return; }\n                // Horizontal: always centre each line around origin (x = 0)\n                ctx.textAlign = \"center\";\n                ctx.fillText(line, 0, y);\n                if (obj.textDecoration && obj.textDecoration.indexOf(\"underline\") !== -1) {\n                  // Same 1.05 \u2192 0.98 em-top offset correction as _txDrawLine.\n                  ctx.save();\n                  ctx.strokeStyle = obj.color || \"#666\";\n                  ctx.lineWidth   = Math.max(1, txFontSize * 0.075);\n                  ctx.beginPath();\n                  ctx.moveTo(-lw / 2, y + _txUOff);\n                  ctx.lineTo( lw / 2, y + _txUOff);\n                  ctx.stroke();\n                  ctx.restore();\n                }\n              });\n\n            } else {\n              // \u2500\u2500 NORMAL LAYOUT (obj.left/top is element box top-left) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n              //\n              // Builder: text content starts at (left + TX_PL, top + TX_PT) in page px.\n              // FIX: previous code ignored padding \u2192 text appeared 6px left, 2px above\n              //      where it should be.\n              //\n              // Rotation: CSS transform-origin is CENTER of element box, so rotation\n              // anchor is (left + boxW/2, top + boxH/2) \u2014 NOT the top-left corner.\n              // FIX: previous code rotated around (left, top) which caused rotated\n              //      text to appear in the wrong position.\n\n              // \u2500\u2500 Element box dimensions for the rotation anchor \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n              // CRITICAL for rotated text: the rotation anchor is the element CENTRE\n              // (left + boxW/2, top + boxH/2). If boxW is estimated from canvas text\n              // measurement it can differ from the browser's CSS rendering by 1\u20133px;\n              // at 90\u00B0 that error shifts the anchor visibly up/down in the PDF.\n              //\n              // FIX: prefer obj.boxWidth / obj.boxHeight saved from the real DOM\n              // offsetWidth / offsetHeight at design time. Fall back to measurement.\n              // Same box the alignment uses - including the CSS min-width floor -\n              // so the rotation pivot and the text alignment can never disagree.\n              var _txBoxW = _txBoxWpx, _txBoxH;\n              if (obj.boxHeight && parseFloat(obj.boxHeight) > 0) {\n                _txBoxH = parseFloat(obj.boxHeight);\n              } else {\n                _txBoxH = txTotalH / scale + TX_PT * 2;\n              }\n\n              if (txRotD) {\n                // \u2500\u2500 Rotated non-centred text \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n                // 1. Translate to element centre\n                // 2. Rotate (matches CSS transform-origin: center center)\n                // 3. Draw text with offset from rotated centre back to content origin\n                var _txCx = txLeft + _txBoxW / 2; // centre X in page px\n                var _txCy = txTop  + _txBoxH / 2; // centre Y in page px\n                ctx.translate(_txCx * scale, _txCy * scale);\n                ctx.rotate(txRotD * Math.PI / 180);\n\n                // Content origin relative to (now-rotated) element centre,\n                // + half-leading so glyph lands at same pixel as in browser.\n                var _rxOrigin = (-_txBoxW / 2 + TX_PL) * scale;\n                var _ryOrigin = (-_txBoxH / 2 + TX_PT) * scale + txFirstBase;\n                txLines.forEach(function(line, li) {\n                  _txDrawLine(line, _rxOrigin, _ryOrigin - txFirstBase + _txBaseAt(li), li);\n                });\n\n              } else {\n                // \u2500\u2500 Non-rotated non-centred text \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n                // Offset drawing origin by padding + half-leading to reach the\n                // exact pixel where the browser places the first glyph.\n                var _nxOrigin = (txLeft + TX_PL) * scale;\n                var _nyOrigin = (txTop  + TX_PT) * scale + txFirstBase;\n                txLines.forEach(function(line, li) {\n                  _txDrawLine(line, _nxOrigin, _nyOrigin - txFirstBase + _txBaseAt(li), li);\n                });\n              }\n            }\n          }\n          ctx.restore();\n        } catch(e) { ctx.restore(); }\n      }\n    }\n\n    // Encode canvas as JPEG and add as a PDF page.\n    // Quality 0.96: near-maximum JPEG quality. Each decode\u2192canvas\u2192re-encode cycle\n    // loses fidelity; 0.96 minimises that loss while keeping file size reasonable.\n    // \"NONE\" disables jsPDF's own image compression (we handle it via toDataURL).\n    async function addCanvasPage(canvas, wMm, hMm) {\n      var orient = hMm >= wMm ? \"portrait\" : \"landscape\";\n      var _i = _pageSeq.length;\n      // SHARDING (render mode only): every shard walks - and therefore NUMBERS - every\n      // page identically; only the pages in this shard's range are encoded and staged.\n      // Skipping an encode cannot change the bytes of the pages that ARE encoded.\n      if (_stream && typeof _stream.skip === \"function\" && _stream.skip(_i)) {\n        _pageSeq.push({ index: _i, wMm: wMm, hMm: hMm, orient: orient, skipped: true });\n        try { canvas.width = 0; canvas.height = 0; } catch (e) {}\n        return;\n      }\n      // ONE JPEG encode, shared by BOTH paths \u2192 guarantees identical page bytes.\n      var _jpeg = canvas.toDataURL(\"image/jpeg\", _jpegQuality);   // 0.96 for every book now (big + small)\n      _pageSeq.push({ index: _i, wMm: wMm, hMm: hMm, orient: orient });\n      if (_stream && _stream.onPage) {\n        // PROOF PREVIEW: a second, small encode off the SAME canvas - taken here because\n        // two lines down the canvas is freed and this pixel data is gone for good. Same\n        // image, scaled: the proof cannot drift from what prints, because there is only\n        // ever one render. Wrapped in try/catch and null on failure, so a browser that\n        // refuses the extra canvas still stages a perfectly good page. (2026-09-14)\n        var _prev = null;\n        if (_stream.wantPreview) {\n          try {\n            var _cw = canvas.width, _ch = canvas.height;\n            var _sc = _DMB_PROOF_PREV_DIM / Math.max(_cw || 1, _ch || 1);\n            if (_sc < 1) {\n              var _pc = document.createElement(\"canvas\");\n              _pc.width  = Math.max(1, Math.round(_cw * _sc));\n              _pc.height = Math.max(1, Math.round(_ch * _sc));\n              var _px = _pc.getContext(\"2d\");\n              _px.imageSmoothingEnabled = true;\n              try { _px.imageSmoothingQuality = \"high\"; } catch (e) {}\n              _px.drawImage(canvas, 0, 0, _pc.width, _pc.height);\n              _prev = _pc.toDataURL(\"image/jpeg\", _DMB_PROOF_PREV_Q);\n              try { _pc.width = 0; _pc.height = 0; } catch (e) {}\n            }\n          } catch (e) { _prev = null; }\n        }\n        // STREAM: hand the finished page off (upload) and free it now \u2014 no jsPDF,\n        // so nothing accumulates. Peak memory stays at ~one page.\n        try { canvas.width = 0; canvas.height = 0; } catch (e) {}\n        await _stream.onPage({ index: _i, wMm: wMm, hMm: hMm, orient: orient, jpeg: _jpeg, preview: _prev });\n        return;\n      }\n      if (_stream && _stream.chunk) {\n        // CHUNK: add this page to the current small PDF (a fresh one is started when the\n        // previous chunk is finished by _finishChunk / _maybeCloseInteriorChunk).\n        if (!_chunkPdf) _chunkPdf = new jsPDF({ unit: \"mm\", format: [wMm, hMm], orientation: orient, compress: true });\n        else _chunkPdf.addPage([wMm, hMm], orient);\n        _chunkPdf.addImage(_jpeg, \"JPEG\", 0, 0, wMm, hMm, undefined, \"NONE\");\n        _chunkPageCount++;\n        try { canvas.width = 0; canvas.height = 0; } catch (e) {}\n        return;\n      }\n      // ACCUMULATE (unchanged): embed the SAME jpeg bytes verbatim (DCTDecode).\n      if (!pdf) pdf = new jsPDF({ unit: \"mm\", format: [wMm, hMm], orientation: orient, compress: true });\n      else pdf.addPage([wMm, hMm], orient);\n      pdf.addImage(_jpeg, \"JPEG\", 0, 0, wMm, hMm, undefined, \"NONE\");\n      // Parity-test tap (test-only): capture the exact bytes this path embeds.\n      if (_stream && _stream.tap) _stream.tap({ index: _i, wMm: wMm, hMm: hMm, orient: orient, jpeg: _jpeg });\n    }\n\n    // \u2500\u2500 Cover spread \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    async function renderCoverSpread() {\n      var isMag = !!window.DMB_MAGAZINE_MODE;\n      var spreadUiW = isMag ? FACE_PX * 2 : (FACE_PX * 2 + SPINE_PX);\n      var spreadCvW = Math.round(spreadUiW * FACE_SCALE);\n      var spreadCvH = FACE_OUT_H;\n      var cc = mkCanvas(spreadCvW, spreadCvH);\n      var ctx = cc.ctx;\n\n      // \u2500\u2500 Force a fresh live capture in the CURRENT orientation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n      // The cover text nodes on screen are already laid out in the current\n      // orientation; re-measure them now so visCx/visCy/glyphCx/glyphCy/boxWidth\n      // are current (not stale portrait geometry after a flip). This makes the\n      // live-mirror + glyph paths below reproduce the builder exactly and stops\n      // _dmbCoverTopScale from being applied inconsistently. No-ops safely if the\n      // cover overlay isn't laid out (offsetWidth 0 \u2192 same fallback as before).\n      // Belt-and-braces with the guard inside _captureCoverTextBoxes itself.\n      if (typeof _dmbCaptureCoverTextBoxesFn === \"function\" && !(_dmbRenderMode() && !_dmbRenderLiveText())) {\n        try { _dmbCaptureCoverTextBoxesFn(); } catch (e) {}\n      }\n\n      // Background\n      ctx.fillStyle = coverState.color || \"#ffffff\";\n      ctx.fillRect(0, 0, spreadCvW, spreadCvH);\n\n      // \u2500\u2500 Build cover object lists early so we can split below/above photos \u2500\u2500\n      var _domZc = {};\n      document.querySelectorAll(\"#coverOverlay [style*='z-index'], #coverOverlay [data-z]\").forEach(function(el) {\n        var z = parseInt(el.style.zIndex || el.dataset.z || \"0\", 10);\n        if (el.id)               _domZc[el.id]               = z;\n        if (el.dataset.objectId) _domZc[el.dataset.objectId] = z;\n      });\n      var _allCoverItems = (editor.coverObjects || []).map(function(o) {\n        return Object.assign({}, o, { z: _domZc[o.id] !== undefined ? _domZc[o.id] : (o.z || 0) });\n      });\n      if (Array.isArray(editor.coverUserTexts)) {\n        // Live overlay geometry \u2014 used to read each text's REAL on-screen box\n        // (the selection box / \"4 circles\") directly, so the PDF reproduces the\n        // builder exactly with no %\u2192px / pivot re-derivation.\n        var _ovEl  = document.getElementById(\"coverOverlay\");\n        var _ovRect = _ovEl ? _ovEl.getBoundingClientRect() : null;\n        var _ovZ = (_ovRect && _ovEl.offsetWidth) ? (_ovRect.width / _ovEl.offsetWidth) || 1 : 1;\n        editor.coverUserTexts.forEach(function(entry) {\n          var s = coverState.textStyles && coverState.textStyles[entry.id];\n          if (!s || !s.text) return;\n          var lStr = s.left || \"0\", tStr = s.top || \"0\";\n          var lPx = lStr.endsWith(\"%\") ? (parseFloat(lStr)/100) * spreadUiW : parseFloat(lStr);\n          // top is stored canonical (portrait face); scale to the current face so\n          // landscape cover text isn't clipped in the PDF. Portrait scale = 1.\n          var tPx = tStr.endsWith(\"%\") ? (parseFloat(tStr)/100) * FACE_H_PX  : (parseFloat(tStr) * _dmbCoverTopScale());\n          var wStr = s.width || \"\";\n          var wPx = wStr.endsWith(\"%\") ? (parseFloat(wStr) / 100) * spreadUiW : parseFloat(wStr) || 0;\n\n          // \u2500\u2500 Read the LIVE rendered box straight from the DOM \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n          // visCx/visCy = centre of the on-screen (rotated) selection box; boxW/H =\n          // the real offsetWidth/Height. This is the user's \"place it where the box\n          // is\" approach and removes the rotation-pivot drift entirely.\n          var _vcx = s.visCx, _vcy = s.visCy, _bw = s.boxWidth, _bh = s.boxHeight;\n          var _node = document.getElementById(entry.id);\n          var _usedLive = false;\n          // Glyph centre comes from the robust capture (un-rotate + Range + re-rotate),\n          // stored in s.glyphCx/glyphCy. That is the letters' true centre.\n          var _gcx = s.glyphCx || \"\", _gcy = s.glyphCy || \"\";\n          if (!(_dmbRenderMode() && !_dmbRenderLiveText()) && _node && _ovRect && _ovEl.contains(_node) && _node.offsetWidth > 0) {\n            var _nr = _node.getBoundingClientRect();\n            _vcx = String(((_nr.left + _nr.width  / 2) - _ovRect.left) / _ovZ);\n            _vcy = String(((_nr.top  + _nr.height / 2) - _ovRect.top)  / _ovZ);\n            _bw  = String(_node.offsetWidth);\n            _bh  = String(_node.offsetHeight);\n            _usedLive = true;\n          }\n          // The on-screen cover text is shrunk in landscape via a CSS transform\n          // (display only), so its stored font/box are still portrait-size. Scale\n          // them here by the same factor so the preview/PDF matches the builder.\n          // Portrait scale = 1 \u2192 unchanged.\n          _allCoverItems.push({ type:\"text\", text:s.text, left:String(lPx), top:String(tPx),\n            fontSize:s.fontSize||\"28px\", fontWeight:s.fontWeight||\"800\",\n            color:s.color||\"#666\", fontFamily:s.fontFamily||\"'Londrina Solid',sans-serif\",\n            textAlign:s.textAlign||\"\", textDecoration:s.textDecoration||\"\",\n            whiteSpace:s.whiteSpace||\"\",\n            renderedLines:s.renderedLines||\"\",\n            rich:s.rich||\"\", richLines:s.richLines||\"\",\n            transform:s.transform||\"\", rotate:s.rotate||\"0\",\n            width: wPx > 0 ? String(wPx) : \"\",\n            boxWidth:  _bw || \"\",\n            boxHeight: _bh || \"\",\n            visCx: _vcx != null ? _vcx : \"\",\n            visCy: _vcy != null ? _vcy : \"\",\n            glyphCx: _gcx, glyphCy: _gcy,\n            z: _domZc[entry.id] !== undefined ? _domZc[entry.id] : Number(s.z || 0)\n          });\n        });\n      }\n      // The cover overlay sits above every photo in the DOM, so cover items are\n      // never behind them - unlike interior pages, there is no below/above split.\n      var _belowCover = [];\n      var _aboveCover = _allCoverItems;\n\n      // Draw objects that are BEHIND photos first\n      try { if (_belowCover.length) await drawFreeObjs(ctx, _belowCover, FACE_SCALE); } catch(e) {}\n\n      // Front cover background image (from coverImageInput upload)\n      if (coverState.imageUrl) {\n        try {\n          var frontX = (isMag ? FACE_PX : FACE_PX + SPINE_PX) * FACE_SCALE;\n          drawFit(ctx, await loadImg(coverState.imageUrl), frontX, 0, FACE_PX * FACE_SCALE, spreadCvH);\n        } catch(e) { if (_dmbRenderMode()) _dmbRenderNote(\"photo_missing\", \"cover-front-bg\"); }\n      }\n\n      // Front cover dragged photo (drag-and-drop from photo grid onto front face)\n      if (coverState.frontImageUrl || (_dmbPrintMasterOn() && coverState.frontImageId)) {\n        try {\n          var frontFaceX = (isMag ? FACE_PX : FACE_PX + SPINE_PX) * FACE_SCALE;\n          // frontImageRot is applied on screen (the cover photo's rotate button) and\n          // must reach the PDF too - only the BLEED photo's rotation used to.\n          drawFit(ctx, await _dmbCoverImg(coverState.frontImageUrl, coverState.frontImageId), frontFaceX, 0, FACE_PX * FACE_SCALE, spreadCvH,\n            0, 0, 1, coverState.frontImageRot || 0);\n        } catch(e) { if (_dmbRenderMode()) _dmbRenderNote(\"photo_missing\", \"cover-front\"); }\n      }\n\n      // Back cover dragged photo (drag-and-drop from photo grid onto back face)\n      if (coverState.backImageUrl || (_dmbPrintMasterOn() && coverState.backImageId)) {\n        try {\n          drawFit(ctx, await _dmbCoverImg(coverState.backImageUrl, coverState.backImageId), 0, 0, FACE_PX * FACE_SCALE, spreadCvH,\n            0, 0, 1, coverState.backImageRot || 0);\n        } catch(e) { if (_dmbRenderMode()) _dmbRenderNote(\"photo_missing\", \"cover-back\"); }\n      }\n\n      // Spine photo. It renders on screen inside the spine strip (a div with\n      // background:url(...) center/cover) but was NEVER drawn into the PDF, so a\n      // cover carrying one printed a blank spine. Only legacy designs and published\n      // templates can hold it - no current UI sets it - and a multi-zone bleed\n      // clears it, so this can never fight the bleed draw below. Magazines have no\n      // spine. No spineImageId exists, so this resolves by URL like the front\n      // background upload does.\n      if (!isMag && coverState.spineImageUrl) {\n        try {\n          drawFit(ctx, await loadImg(coverState.spineImageUrl),\n            FACE_PX * FACE_SCALE, 0, SPINE_PX * FACE_SCALE, spreadCvH);\n        } catch(e) { if (_dmbRenderMode()) _dmbRenderNote(\"photo_missing\", \"cover-spine\"); }\n      }\n\n      // Seamless bleed photo spanning multiple cover zones\n      if ((coverState.bleedImageUrl || (_dmbPrintMasterOn() && coverState.bleedImageId)) && coverState.bleedZone) {\n        try {\n          var _bleedZones = {\n            \"spine-back\":  { dx: 0,                              dw: (FACE_PX + SPINE_PX) * FACE_SCALE },\n            \"full\":        { dx: 0,                              dw: spreadCvW },\n            \"front-spine\": { dx: FACE_PX * FACE_SCALE,           dw: (SPINE_PX + FACE_PX) * FACE_SCALE }\n          };\n          var _bpz = _bleedZones[coverState.bleedZone];\n          if (_bpz) {\n            drawFit(ctx, await _dmbCoverImg(coverState.bleedImageUrl, coverState.bleedImageId),\n              _bpz.dx, 0, _bpz.dw, spreadCvH,\n              0, 0, 1, coverState.bleedImageRot || 0);\n          }\n        } catch(e) { if (_dmbRenderMode()) _dmbRenderNote(\"photo_missing\", \"cover-wrap\"); }\n      }\n\n      // Draw photo grid layout frames on front cover\n      if (coverState.frontLayout) {\n        var frontLayoutDefs = TEMPLATE_DEFS[coverState.frontLayout] || [];\n        var frontFaceX = (isMag ? FACE_PX : FACE_PX + SPINE_PX) * FACE_SCALE;\n        for (var cfi = 0; cfi < frontLayoutDefs.length; cfi++) {\n          var cff = frontLayoutDefs[cfi];\n          var cfpid = coverState.frontFrames && coverState.frontFrames[cff.key];\n          if (!cfpid) continue;\n          try {\n            var cfph = getPhotoById(cfpid);\n            if (!cfph) continue;\n            var cfrot = (coverState.frontFrameRot && coverState.frontFrameRot[cff.key]) || 0;\n            drawRotFit(ctx, await _dmbRenderImg(cfph),\n              frontFaceX + (cff.x / 100) * FACE_PX * FACE_SCALE,\n              (cff.y / 100) * FACE_OUT_H,\n              (cff.w / 100) * FACE_PX * FACE_SCALE,\n              (cff.h / 100) * FACE_OUT_H, cfrot);\n          } catch(e) {}\n        }\n      }\n\n      // Draw photo grid layout frames on back cover\n      if (coverState.backLayout) {\n        var backLayoutDefs = TEMPLATE_DEFS[coverState.backLayout] || [];\n        for (var cbi = 0; cbi < backLayoutDefs.length; cbi++) {\n          var cbf = backLayoutDefs[cbi];\n          var cbpid = coverState.backFrames && coverState.backFrames[cbf.key];\n          if (!cbpid) continue;\n          try {\n            var cbph = getPhotoById(cbpid);\n            if (!cbph) continue;\n            var cbrot = (coverState.backFrameRot && coverState.backFrameRot[cbf.key]) || 0;\n            drawRotFit(ctx, await _dmbRenderImg(cbph),\n              (cbf.x / 100) * FACE_PX * FACE_SCALE,\n              (cbf.y / 100) * FACE_OUT_H,\n              (cbf.w / 100) * FACE_PX * FACE_SCALE,\n              (cbf.h / 100) * FACE_OUT_H, cbrot);\n          } catch(e) {}\n        }\n      }\n\n      // Draw objects that are IN FRONT of photos (z >= 1)\n      try { await drawFreeObjs(ctx, _aboveCover, FACE_SCALE); } catch(e) {}\n\n      // Logo watermark on back cover \u2014 drawn last so it is always above all objects\n      if (window.DMB_LOGO_URL) {\n        try {\n          var logoImg = await loadImg(window.DMB_LOGO_URL);\n          var logoW = 90 * FACE_SCALE;\n          var logoH = logoW * (logoImg.naturalHeight / (logoImg.naturalWidth || 1));\n          // Mirror the DOM exactly: left = faceW - round(faceW*0.06) - 90, bottom = 6%.\n          var logoX = (FACE_PX - Math.round(FACE_PX * 0.06) - 90) * FACE_SCALE;\n          var logoY = FACE_OUT_H - FACE_H_PX * 0.06 * FACE_SCALE - logoH;\n          ctx.save(); ctx.globalAlpha = 0.85;\n          ctx.drawImage(logoImg, logoX, logoY, logoW, logoH);\n          ctx.restore();\n        } catch(e) {}\n      }\n\n      return cc.canvas;\n    }\n\n    // Crop a zone from the cover spread canvas\n    function cropCoverZone(spreadCanvas, zone) {\n      var isMag = !!window.DMB_MAGAZINE_MODE;\n      var bw = Math.round(FACE_PX * FACE_SCALE);\n      var sw = Math.round(SPINE_PX * FACE_SCALE);\n      var h  = spreadCanvas.height;\n      var sx, cw;\n      if (zone === \"back\")        { sx = 0;             cw = bw; }\n      else if (zone === \"spine\")  { sx = bw;            cw = sw; }\n      else /* front */            { sx = isMag ? bw : bw + sw; cw = bw; }\n      var cc = mkCanvas(cw, h);\n      cc.ctx.drawImage(spreadCanvas, sx, 0, cw, h, 0, 0, cw, h);\n      return cc.canvas;\n    }\n\n    // \u2500\u2500 Page rendering \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    async function renderPage(pageNumber, side) {\n      var cc  = mkCanvas(OUT_W, OUT_H);\n      var ctx = cc.ctx;\n\n      // \u2500\u2500 Per-page background colour \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n      // mkCanvas fills with white by default. If the customer chose a custom\n      // background colour for this page, fill the canvas with it now so it\n      // shows under photos and all objects.\n      var _bgColor = editor.pageBackgrounds?.[pageNumber];\n      if (_bgColor && _bgColor.toLowerCase() !== \"#ffffff\") {\n        ctx.fillStyle = _bgColor;\n        ctx.fillRect(0, 0, OUT_W, OUT_H);\n      }\n\n      // Helper: get free objects for this page (try both numeric and string key)\n      function getPageFreeObjs(pn) {\n        if (!editor.pageObjects) return [];\n        var objs = editor.pageObjects[pn] || editor.pageObjects[String(pn)] || [];\n        return Array.isArray(objs) ? objs : [];\n      }\n\n      // Objects filed under this page's spread PARTNER that reach across the fold\n      // onto this page. The builder shows them (one continuous spread, pages are\n      // overflow:visible); each PDF page is a separate canvas, so without this the\n      // overhanging half is simply cut off at the sheet edge.\n      function getSpreadOverflowObjs(pn) {\n        if (!pn || pn < 1) return [];                  // page 0 has no partner\n        var isLeftPg = (pn % 2 === 1);                 // spreads are (2k+1, 2k+2)\n        var sib = isLeftPg ? pn + 1 : pn - 1;\n        if (sib < 1 || sib > (editor.totalPages || 0)) return [];\n        var dxPg = isLeftPg ? PAGE_W_PX : -PAGE_W_PX;  // partner space -> this page\n        var out = [];\n        getPageFreeObjs(sib).forEach(function (o) {\n          if (!o) return;\n          var ox = (o.x || 0) + dxPg;\n          var ow2 = o.w || 0;\n          // Cull on the ROTATED footprint. drawFreeObjs rotates about the object centre,\n          // so a rotated object occupies its rotated bounding box - wider than x..x+w for\n          // anything not axis-aligned. Testing the unrotated box discarded objects that\n          // genuinely reach this sheet, and the customer got one half of a tilted shape\n          // with the other half missing and no error anywhere. (2026-09-14)\n          var _rot  = (o.rotation || 0) * Math.PI / 180;\n          var _cx   = ox + ow2 / 2;\n          var _half = _rot\n            ? (Math.abs(ow2 * Math.cos(_rot)) + Math.abs((o.h || 0) * Math.sin(_rot))) / 2\n            : ow2 / 2;\n          if (_cx + _half <= 0 || _cx - _half >= PAGE_W_PX) return;   // does not reach this page\n          var c = Object.assign({}, o, { x: ox });\n          // A line's box is derived from its endpoints, so shift those too.\n          if (c.type === \"shape\" && c.shapeType === \"line\") {\n            if (c.x1 != null) c.x1 = c.x1 + dxPg;\n            if (c.x2 != null) c.x2 = c.x2 + dxPg;\n            if (c.bx != null) c.bx = c.bx + dxPg;\n          }\n          out.push(c);\n        });\n        return out;\n      }\n\n      // Page 0 (the special inner-cover page) uses the same full rendering path\n      // as every other page \u2014 it handles templates, slot photos with rotation,\n      // spread photos, template frames, and the z-order split (below/above photos).\n      // The old simplified page-0 handler was missing all of those features,\n      // which caused slot photos to not appear when rotation was applied, and\n      // template-frame photos to never appear in the PDF.\n\n      if (isLockedPage(pageNumber)) {\n        // The locked page still FACES page 2, so it still receives that page's overhang.\n        // This early return used to skip the overflow collection entirely (it happens\n        // further down), so an object hanging off page 2 across the fold printed on every\n        // page except the one it actually reached. drawFreeObjs sorts by z itself, so\n        // concatenating is enough to keep the stacking order correct. (2026-09-14)\n        try {\n          await drawFreeObjs(ctx,\n            getPageFreeObjs(pageNumber).concat(getSpreadOverflowObjs(pageNumber)),\n            PAGE_SCALE);\n        } catch(e) {}\n        return cc.canvas;\n      }\n\n      var si         = Math.floor((pageNumber - 1) / 2);\n      var isLeft     = (side === \"L\");\n      var sp         = editor.spreadPhoto && editor.spreadPhoto[si];\n      var templateId = getPageTemplateId(pageNumber);\n      var _spreadRendered = false;\n\n      // \u2500\u2500 Spread photo \u2500\u2500\n      if (sp && sp.photoId) {\n        try {\n          var sph = getPhotoById(sp.photoId);\n          if (sph) {\n            var sImg  = await _dmbRenderImg(sph);\n            var spW   = OUT_W * 2;\n            var spH   = OUT_H;\n            var offX  = isLeft ? 0 : -OUT_W;\n            var spIA  = sImg.naturalWidth / sImg.naturalHeight;\n            var spFA  = spW / spH;\n            var spSW, spSH;\n            if (spIA > spFA) { spSH = spH; spSW = spSH * spIA; } else { spSW = spW; spSH = spSW / spIA; }\n            var sPanX   = (sp.panX || 0) * PAGE_SCALE;\n            var sPanY   = (sp.panY || 0) * PAGE_SCALE;\n            var sZoom   = sp.zoom || 1;\n            var sRotDeg = sp.rotation || 0;\n            ctx.save();\n            ctx.beginPath(); ctx.rect(0, 0, OUT_W, OUT_H); ctx.clip();\n            ctx.translate(spW / 2 + offX + sPanX, spH / 2 + sPanY);\n            if (sRotDeg) ctx.rotate(sRotDeg * Math.PI / 180);\n            ctx.scale(sZoom, sZoom);\n            ctx.drawImage(sImg, -spSW / 2, -spSH / 2, spSW, spSH);\n            ctx.restore();\n            _spreadRendered = true;\n          }\n        } catch(e) {}\n      }\n\n      // \u2500\u2500 Z-ORDER FIX: split free objects around the photo layer \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n      // Photos (makePlacedImageBox) use z-index 1 in the builder.\n      // \"Send backward\" (\u2B07 Back button) sets obj.z = 0, so those objects must\n      // be drawn on canvas BEFORE photos to appear behind them in the PDF.\n      // Objects with z\u22651 (default and \"bring forward\") are drawn AFTER photos.\n      // DO NOT merge _belowObjs and _aboveObjs into a single drawFreeObjs call.\n      var _freeObjsAll = getPageFreeObjs(pageNumber).concat(getSpreadOverflowObjs(pageNumber));\n      var _belowObjs   = _freeObjsAll.filter(function(o){ return (o.z || 0) < 1; }); // behind photos\n      var _aboveObjs   = _freeObjsAll.filter(function(o){ return (o.z || 0) >= 1; }); // above photos\n      try { if (_belowObjs.length) await drawFreeObjs(ctx, _belowObjs, PAGE_SCALE); } catch(e) {}\n\n      // \u2500\u2500 Template layout \u2500\u2500\n      if (templateId) {\n        var defs  = TEMPLATE_DEFS[templateId] || [];\n        var slotA = editor.pageSlots && editor.pageSlots[pageNumber] && editor.pageSlots[pageNumber].A;\n        var slotB = editor.pageSlots && editor.pageSlots[pageNumber] && editor.pageSlots[pageNumber].B;\n        if (slotA) {\n          ctx.save();\n          try {\n            var phA = getPhotoById(slotA);\n            if (phA) {\n              var _tAh = slotB ? OUT_H / 2 : OUT_H;\n              var _tAShape = getSlotFrameShape(pageNumber, \"A\");\n              if (_tAShape) applyCanvasFrameClip(ctx, _tAShape, OUT_W, _tAh, PAGE_SCALE);\n              drawRotFit(ctx, await _dmbRenderImg(phA), 0, 0, OUT_W, _tAh,\n                (editor.pageRot && editor.pageRot[pageNumber] && editor.pageRot[pageNumber].A) || 0);\n            }\n          } catch(e) {} finally { ctx.restore(); }\n        }\n        if (slotB) {\n          ctx.save();\n          try {\n            var phB = getPhotoById(slotB);\n            if (phB) {\n              var _tBShape = getSlotFrameShape(pageNumber, \"B\");\n              ctx.translate(0, OUT_H / 2);\n              if (_tBShape) applyCanvasFrameClip(ctx, _tBShape, OUT_W, OUT_H / 2, PAGE_SCALE);\n              drawRotFit(ctx, await _dmbRenderImg(phB), 0, 0, OUT_W, OUT_H / 2,\n                (editor.pageRot && editor.pageRot[pageNumber] && editor.pageRot[pageNumber].B) || 0);\n            }\n          } catch(e) {} finally { ctx.restore(); }\n        }\n        for (var di = 0; di < defs.length; di++) {\n          var f    = defs[di];\n          var fpid = editor.pageFrames && editor.pageFrames[pageNumber] && editor.pageFrames[pageNumber][f.key];\n          if (!fpid) continue;\n          try {\n            var fph  = getPhotoById(fpid);\n            if (!fph) continue;\n            var frot = (editor.pageFrameRot && editor.pageFrameRot[pageNumber] && editor.pageFrameRot[pageNumber][f.key]) || 0;\n            var _fp = _dmbLayoutFramePct(f);   // keep the frame's exact portrait shape (letterbox in landscape)\n            var _fx = (_fp.x / 100) * OUT_W, _fy = (_fp.y / 100) * OUT_H;\n            var _fw = (_fp.w / 100) * OUT_W, _fh = (_fp.h / 100) * OUT_H;\n            var _fShape = (editor.pageFrameShapes && editor.pageFrameShapes[pageNumber] && editor.pageFrameShapes[pageNumber][f.key]) || \"\";\n            ctx.save();\n            try {\n              if (_fShape) { ctx.translate(_fx, _fy); applyCanvasFrameClip(ctx, _fShape, _fw, _fh, PAGE_SCALE); ctx.translate(-_fx, -_fy); }\n              drawRotFit(ctx, await _dmbRenderImg(fph), _fx, _fy, _fw, _fh, frot);\n            } finally { ctx.restore(); }\n          } catch(e) {}\n        }\n      } else if (!_spreadRendered) {\n        // \u2500\u2500 Default A/B slot layout (direct drag-and-drop; no template/frames) \u2500\u2500\n        // Each slot photo can carry a SHAPE MASK (the \u2B50 \"Frame shape\" button \u2192\n        // circle/heart/star/\u2026), stored in editor.pageFrameShapes[page][slot]. The\n        // builder clips the <img> to it (applyDmbFrameShape); the PDF/preview must\n        // clip the canvas to the same shape or the mask is lost (rendered as a full\n        // rectangle). Clip inside a save()/restore() so it only affects this slot.\n        var A = editor.pageSlots && editor.pageSlots[pageNumber] && editor.pageSlots[pageNumber].A;\n        var B = editor.pageSlots && editor.pageSlots[pageNumber] && editor.pageSlots[pageNumber].B;\n        if (A) {\n          ctx.save();\n          try {\n            var pA = getPhotoById(A);\n            if (pA) {\n              var _aw = dmbIsLandscape() ? (B ? OUT_W / 2 : OUT_W) : OUT_W;\n              var _ah = dmbIsLandscape() ? OUT_H : (B ? OUT_H / 2 : OUT_H);\n              var _aShape = getSlotFrameShape(pageNumber, \"A\");\n              if (_aShape) applyCanvasFrameClip(ctx, _aShape, _aw, _ah, PAGE_SCALE);\n              drawRotFit(ctx, await _dmbRenderImg(pA), 0, 0, _aw, _ah,\n                (editor.pageRot && editor.pageRot[pageNumber] && editor.pageRot[pageNumber].A) || 0);\n            }\n          } catch(e) {} finally { ctx.restore(); }\n        }\n        if (B) {\n          ctx.save();\n          try {\n            var pB = getPhotoById(B);\n            if (pB) {\n              var _bx = dmbIsLandscape() ? OUT_W / 2 : 0;\n              var _by = dmbIsLandscape() ? 0 : OUT_H / 2;\n              var _bw = dmbIsLandscape() ? OUT_W / 2 : OUT_W;\n              var _bh = dmbIsLandscape() ? OUT_H : OUT_H / 2;\n              var _bShape = getSlotFrameShape(pageNumber, \"B\");\n              ctx.translate(_bx, _by);\n              if (_bShape) applyCanvasFrameClip(ctx, _bShape, _bw, _bh, PAGE_SCALE);\n              drawRotFit(ctx, await _dmbRenderImg(pB), 0, 0, _bw, _bh,\n                (editor.pageRot && editor.pageRot[pageNumber] && editor.pageRot[pageNumber].B) || 0);\n            }\n          } catch(e) {} finally { ctx.restore(); }\n        }\n      }\n\n      // \u2500\u2500 Objects \"above photos\" (z >= 1) drawn AFTER photos \u2500\u2500\n      try { if (_aboveObjs.length) await drawFreeObjs(ctx, _aboveObjs, PAGE_SCALE); } catch(e) {}\n      return cc.canvas;\n    }\n\n    // \u2500\u2500 Preview mode: render one canvas and return early \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // When _pvOpt is set we skip jsPDF entirely, render only the requested\n    // page/cover, and hand the canvas(es) back via _pvOpt.resolve().\n    if (_pvOpt) {\n      try {\n        if (_pvOpt.cover) {\n          var _pvSpread = await renderCoverSpread();\n          _pvOpt.resolve({\n            back:  cropCoverZone(_pvSpread, \"back\"),\n            spine: cropCoverZone(_pvSpread, \"spine\"),\n            front: cropCoverZone(_pvSpread, \"front\"),\n          });\n        } else {\n          var _pvPn   = _pvOpt.page;\n          var _pvSide = (_pvPn === 0) ? \"L\" : (_pvPn % 2 === 0 ? \"R\" : \"L\");\n          _pvOpt.resolve(await renderPage(_pvPn, _pvSide));\n        }\n      } catch(_pvErr) {\n        if (typeof _pvOpt.reject === \"function\") _pvOpt.reject(_pvErr);\n      }\n      return;\n    }\n\n    // \u2500\u2500 Build PDF \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // Total steps: book = front+spine+back (3); magazine = front+blank+blank+back (4),\n    // plus page 0 + all interior pages.\n    var _coverSteps = window.DMB_MAGAZINE_MODE ? 4 : 3;\n    var _totalSteps = _coverSteps + 1 + (editor.totalPages || 0);\n    var _doneSteps  = 0;\n    function _onPageDone() {\n      _doneSteps++;\n      if (typeof onProgress === \"function\") onProgress(_doneSteps, _totalSteps);\n    }\n\n    // \u2500\u2500 INCREMENTAL RENDER (render mode only) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // _stream.skipRender(i) says \"the Worker already holds page i for this exact\n    // content - do not render it\". The page still CONSUMES its index (a placeholder\n    // entry in _pageSeq) so every other page keeps the same number as a full run.\n    // The cover spread is only rendered if at least one cover face needs it.\n    function _reusedAt(i) {\n      return !!(_stream && typeof _stream.skipRender === \"function\" && _stream.skipRender(i));\n    }\n    function _pushReused(wMm, hMm) {\n      var _i = _pageSeq.length;\n      _pageSeq.push({ index: _i, wMm: wMm, hMm: hMm, orient: hMm >= wMm ? \"portrait\" : \"landscape\", skipped: true, reused: true });\n    }\n    var coverSpread = null;\n    async function _cover() {\n      if (!coverSpread) coverSpread = await renderCoverSpread();\n      return coverSpread;\n    }\n    async function _addCoverFace(zone, wMm, hMm) {\n      if (_reusedAt(_pageSeq.length)) { _pushReused(wMm, hMm); return; }\n      await addCanvasPage(cropCoverZone(await _cover(), zone), wMm, hMm);\n    }\n\n    // A blank white page at the given mm size \u2014 used only for magazines (the inside\n    // faces of the front & back covers).\n    async function _addBlankPage(wMm, hMm) {\n      if (_reusedAt(_pageSeq.length)) { _pushReused(wMm, hMm); return; }\n      var _bc = document.createElement(\"canvas\");\n      _bc.width  = Math.max(1, Math.round(wMm * MM2PX));\n      _bc.height = Math.max(1, Math.round(hMm * MM2PX));\n      var _bctx = _bc.getContext(\"2d\");\n      if (_bctx) { _bctx.fillStyle = \"#ffffff\"; _bctx.fillRect(0, 0, _bc.width, _bc.height); }\n      await addCanvasPage(_bc, wMm, hMm);\n    }\n\n    // Cover pages use the fixed PORTRAIT cover dims (see note in Sizing constants).\n    if (window.DMB_MAGAZINE_MODE) {\n      // Magazine order: FRONT COVER \u00B7 blank \u00B7 [interior pages] \u00B7 blank \u00B7 BACK COVER.\n      // The two blanks are the inside faces of the covers (magazine cover = page size,\n      // 210\u00D7297). The back cover is emitted AFTER the pages (below), so the covers are\n      // NOT contiguous \u2014 hence no \"cover\" chunk split here.\n      await _addCoverFace(\"front\", COVER_W_MM, COVER_H_MM); _onPageDone();\n      await _addBlankPage(COVER_W_MM, COVER_H_MM); _onPageDone();   // inside front cover\n    } else {\n      await _addCoverFace(\"front\", COVER_W_MM, COVER_H_MM); _onPageDone();\n      await _addCoverFace(\"spine\", SPINE_W_MM, COVER_H_MM); _onPageDone();\n      await _addCoverFace(\"back\",  COVER_W_MM, COVER_H_MM); _onPageDone();\n      if (_stream && _stream.chunk) _finishChunk(\"cover\");\n    }   // cover front+spine+back \u2192 \"cover\" PDF\n\n    // One transient retry per page (a photo blob momentarily unavailable during a long\n    // render). Kept to a SINGLE extra attempt so a big book's render is not multiplied.\n    async function _renderPageOnce(pnum, side) {\n      try { return await renderPage(pnum, side); }\n      catch (err) {\n        await new Promise(function (r) { setTimeout(r, 300); });\n        return await renderPage(pnum, side);   // one retry, then let it throw normally\n      }\n    }\n\n    // Page 0 (special page before page 1)\n    if (_reusedAt(_pageSeq.length)) { _pushReused(A4_W_MM, A4_H_MM); _onPageDone(); }\n    else { await addCanvasPage(await _renderPageOnce(0, \"L\"), A4_W_MM, A4_H_MM); _onPageDone(); }\n    _maybeCloseInteriorChunk();\n\n    for (var p = 1; p <= (editor.totalPages || 0); p++) {\n      // Empty sheets the customer chose to drop are skipped (both pages of the\n      // sheet are in _excludePages). The remaining pages render unchanged.\n      if (_excludePages && _excludePages.has(p)) { _onPageDone(); continue; }\n      // Unchanged since the last proof (Worker holds its image) -> placeholder only.\n      if (_reusedAt(_pageSeq.length)) { _pushReused(A4_W_MM, A4_H_MM); _onPageDone(); continue; }\n      await addCanvasPage(await _renderPageOnce(p, p % 2 === 0 ? \"R\" : \"L\"), A4_W_MM, A4_H_MM);\n      _onPageDone();\n      _maybeCloseInteriorChunk();\n    }\n\n    // Magazine: after the interior pages, a blank (inside back cover) then the BACK COVER.\n    if (window.DMB_MAGAZINE_MODE) {\n      await _addBlankPage(COVER_W_MM, COVER_H_MM); _onPageDone();   // inside back cover\n      await _addCoverFace(\"back\", COVER_W_MM, COVER_H_MM); _onPageDone();\n    }\n\n    if (_stream && _stream.chunk) {\n      if (_chunkPdf) { _chunkNo++; _finishChunk(String(_chunkNo)); }   // trailing partial chunk\n      return { chunked: true, chunks: _chunkOut };\n    }\n    if (_stream && _stream.onPage) {\n      // Streaming: pages already handed off + freed. Return the manifest the stapler\n      // assembles from (order + per-page dimensions); no blob is built in the tab.\n      return { streamed: true, count: _pageSeq.length, pages: _pageSeq };\n    }\n    if (!pdf) throw new Error(\"No pages were rendered \u2014 check that pages are selected.\");\n    return pdf.output(\"blob\");\n  }\n\n  // \u2500\u2500\u2500 End PDF generation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n  // \u2500\u2500 Phase 1: streaming export + parity self-test (NOT wired into checkout) \u2500\u2500\n  // Nothing below runs during a normal Add to Cart \u2014 the live purchase path still\n  // uses the in-memory export above. These are entry points for testing the\n  // streaming render in the real builder console before any server \"stapler\" or\n  // checkout wiring exists.\n\n  // Render page-by-page, handing each finished page to onPage(pg) and freeing it\n  // right away (peak memory \u2248 one page). Resolves to a manifest {streamed, count,\n  // pages:[{index,wMm,hMm,orient}]}. pg = {index, wMm, hMm, orient, jpeg(dataURL)}.\n  // The server stapler (later) turns the handed-off JPEGs into the final PDF.\n  window.__dmbBuildPdfStreaming = function (onPage, onProgress, excludePages, opts) {\n    // `opts` is additive and optional, so every existing caller behaves identically.\n    return buildPdfBlobOnePagePerBookPage(onProgress || null, null, excludePages || null,\n      Object.assign({ onPage: onPage }, opts || {}));\n  };\n\n  // Admin split-download: render the SAME book into separate small PDFs \u2014 one \"cover\" PDF\n  // (front+spine+back) and one PDF per `pagesPerChunk` book pages (\"1\",\"2\",\"3\"\u2026). Returns\n  // { chunked:true, chunks:[{name, blob}] }. Used by the password-gated admin ZIP button.\n  window.__dmbBuildPdfChunks = function (pagesPerChunk, onProgress) {\n    return buildPdfBlobOnePagePerBookPage(onProgress || null, null, null, { chunk: Math.max(1, pagesPerChunk || 10) });\n  };\n\n  // Parity proof \u2014 render the CURRENT design BOTH ways and confirm every page's\n  // JPEG is byte-identical. Run in the builder console:\n  //   await __dmbVerifyStreamParity()\n  // Expect { byteIdentical: true, mismatchedPageIndexes: [] }.\n  window.__dmbVerifyStreamParity = async function (opts) {\n    opts = opts || {};\n    var exclude = opts.exclude || null;\n    // Accumulate path (today's real export) with a tap capturing each page's bytes.\n    var accum = [];\n    var accBlob = await buildPdfBlobOnePagePerBookPage(null, null, exclude, { tap: function (pg) { accum.push(pg.jpeg); } });\n    // Streaming path \u2014 capture each page's bytes as they're handed off.\n    var strm = [];\n    await buildPdfBlobOnePagePerBookPage(null, null, exclude, { onPage: function (pg) { strm.push(pg.jpeg); } });\n    var n = Math.max(accum.length, strm.length), diffs = [];\n    for (var i = 0; i < n; i++) { if (accum[i] !== strm[i]) diffs.push(i); }\n    var report = {\n      accumulatePages: accum.length,\n      streamPages: strm.length,\n      identicalPages: n - diffs.length,\n      mismatchedPageIndexes: diffs,\n      byteIdentical: diffs.length === 0 && accum.length === strm.length && accum.length > 0,\n      accumulateBlobBytes: accBlob ? accBlob.size : null\n    };\n    try { console.log(\"[DMB stream parity]\", JSON.stringify(report)); } catch (e) {}\n    return report;\n  };\n\n  // First-time callout that points at \"Add to Cart\", shown ONCE right after the\n  // tour. Explains that adding to cart keeps the book safe (and locks it).\n  function _dmbShowAddCartHint() {\n    try { if (localStorage.getItem(\"dmb_addcart_hint_done\")) return; } catch (e) {}\n    if (document.getElementById(\"dmbAddCartHint\")) return;\n    const btn = document.getElementById(\"addToCartBtn\");\n    if (!btn) return;\n    const r0 = btn.getBoundingClientRect();\n    if (!(r0.width > 0 && r0.height > 0)) return;   // button not visible yet\n\n    const wrap = document.createElement(\"div\");\n    wrap.id = \"dmbAddCartHint\";\n    wrap.style.cssText = \"position:fixed;z-index:1000004;width:272px;max-width:86vw;background:#1e1e1e;color:#fff;border-radius:14px;padding:16px 18px;box-shadow:0 16px 50px rgba(0,0,0,.42);font-family:inherit;\";\n    wrap.innerHTML =\n      '<button type=\"button\" id=\"dmbAddCartHintX\" aria-label=\"Close\" style=\"position:absolute;top:7px;right:10px;border:none;background:transparent;color:#bcbcbc;font-size:22px;line-height:1;cursor:pointer;padding:2px 4px;\">&times;</button>' +\n      '<div style=\"font-weight:900;font-size:15px;margin:0 16px 6px 0;\">When you\u2019re done</div>' +\n      '<div style=\"font-size:13px;line-height:1.55;color:#e9e9e9;\">Add your book to the cart to keep it safe \u2014 then order whenever you like. Just note: once it\u2019s in the cart, the design is final and can\u2019t be edited.</div>' +\n      '<div id=\"dmbAddCartHintArrow\" style=\"position:absolute;width:16px;height:16px;background:#1e1e1e;transform:rotate(45deg);\"></div>';\n    document.body.appendChild(wrap);\n\n    function place() {\n      const b = btn.getBoundingClientRect();\n      const w = wrap.getBoundingClientRect();\n      const gap = 14;\n      let above = true;\n      let top = b.top - w.height - gap;\n      if (top < 8) { above = false; top = b.bottom + gap; }\n      top = Math.max(8, Math.min(top, window.innerHeight - w.height - 8));\n      let left = b.left + b.width / 2 - w.width / 2;\n      left = Math.max(8, Math.min(left, window.innerWidth - w.width - 8));\n      wrap.style.top = top + \"px\";\n      wrap.style.left = left + \"px\";\n      const arrow = document.getElementById(\"dmbAddCartHintArrow\");\n      if (arrow) {\n        const ax = b.left + b.width / 2 - left - 8;\n        arrow.style.left = Math.max(14, Math.min(ax, w.width - 30)) + \"px\";\n        if (above) { arrow.style.bottom = \"-7px\"; arrow.style.top = \"\"; }\n        else       { arrow.style.top = \"-7px\"; arrow.style.bottom = \"\"; }\n      }\n    }\n    place();\n    const _rp = function () { place(); };\n    window.addEventListener(\"resize\", _rp);\n    window.addEventListener(\"scroll\", _rp, { passive: true });\n\n    wrap.querySelector(\"#dmbAddCartHintX\").addEventListener(\"click\", function () {\n      try { localStorage.setItem(\"dmb_addcart_hint_done\", \"1\"); } catch (e) {}\n      window.removeEventListener(\"resize\", _rp);\n      window.removeEventListener(\"scroll\", _rp);\n      wrap.remove();\n    });\n  }\n  document.addEventListener(\"dmb:tourEnded\", function () {\n    setTimeout(function () { try { _dmbShowAddCartHint(); } catch (e) {} }, 400);\n  });\n\n  // Every photo id the design references \u2014 interior template slots, spread photos,\n  // template frames, free photo-frames, and the cover. Used to make sure none are\n  // missing before an order is placed (a missing photo => a blank printed page).\n  function _dmbReferencedPhotoIds() {\n    const ids = new Set();\n    const add = (v) => { if (v) ids.add(v); };\n    const ps = editor.pageSlots || {};\n    for (const k in ps) { if (ps[k]) { add(ps[k].A); add(ps[k].B); } }\n    const sp = editor.spreadPhoto || {};\n    for (const k in sp) { if (sp[k]) add(sp[k].photoId); }\n    const pf = editor.pageFrames || {};\n    for (const k in pf) { const m = pf[k] || {}; for (const s in m) add(m[s]); }\n    const po = editor.pageObjects || {};\n    for (const k in po) { (po[k] || []).forEach((o) => { if (o && o.type === \"photo-frame\") add(o.photoId); }); }\n    (editor.coverObjects || []).forEach((o) => { if (o) add(o.photoId); });\n    [\"frontFrames\", \"backFrames\"].forEach((f) => { const m = (coverState && coverState[f]) || {}; for (const s in m) add(m[s]); });\n    // The cover's OWN photos: the front/back face images and the wrap-around\n    // background. They were missing from this list, so the hard add-to-cart guard\n    // (\"never let a book with MISSING photos be ordered\") never looked at the\n    // cover at all - a cover photo the device had dropped went straight through\n    // and printed as a blank face.\n    // The guard's existing rule is unchanged and is what makes this safe: a photo\n    // that is IN the library but whose image is dead counts as broken, while an id\n    // that is not in the library at all is treated as a stale reference and\n    // skipped. So a cover that simply has no photo can never be blocked.\n    [\"frontImageId\", \"backImageId\", \"bleedImageId\"].forEach((k) => add(coverState && coverState[k]));\n    return Array.from(ids);\n  }\n\n  // Does an image URL actually load? (8s timeout so a hang can't stall checkout.)\n  function _dmbImgLoads(url) {\n    return new Promise((resolve) => {\n      if (!url) { resolve(false); return; }\n      let done = false;\n      const finish = (ok) => { if (!done) { done = true; resolve(ok); } };\n      const im = new Image();\n      im.onload = () => finish(im.naturalWidth > 0);\n      im.onerror = () => finish(false);\n      setTimeout(() => finish(false), 8000);\n      try { im.src = url; } catch (e) { finish(false); }\n    });\n  }\n\n  // How many referenced photos would print BLANK. Deliberately conservative so it\n  // never blocks a legit order over a stale slot reference:\n  //  \u2022 If the design references photos but the library is EMPTY \u2192 total eviction\n  //    (the guest week-later case) \u2192 all are missing.\n  //  \u2022 Otherwise only count photos that ARE in the library but whose image is dead\n  //    (a revoked/broken blob). Ids absent from the library are treated as stale\n  //    refs and ignored (they wouldn't render anyway).\n  async function _dmbCountMissingPhotos() {\n    const ids = _dmbReferencedPhotoIds();\n    if (!ids.length) return 0;\n    if (!Array.isArray(editor.photos) || editor.photos.length === 0) return ids.length;\n    let broken = 0;\n    await Promise.all(ids.map(async (id) => {\n      const ph = getPhotoById(id);\n      if (!ph || !ph.url) return;                 // not in library \u2192 stale ref, skip\n      if (!(await _dmbImgLoads(ph.url))) broken++; // in library but image is dead\n    }));\n    return broken;\n  }\n\n  // \u2500\u2500 Leave guard: nudge GUESTS to sign in before they leave the builder \u2500\u2500\u2500\u2500\u2500\u2500\n  // Only guests with a design in progress (signed-in customers are already backed\n  // up to the cloud). Custom \"sign in to save\" modal when they click a link that\n  // leaves the builder; native browser warning on tab-close/refresh/back (browsers\n  // don't allow custom UI there).\n  let _dmbLeaveGuardDismissed = false;\n  function _dmbLeaveGuardActive() {\n    if (_dmbRenderMode()) return false;        // render mode: never block a close/navigation\n    if (window.DMB_CUSTOMER_ID) return false;                 // signed in \u2192 safe\n    if (_dmbLeaveGuardDismissed) return false;                // they chose \"leave\"\n    if (!editor || !(editor.totalPages > 0)) return false;    // no real work yet\n    const bs = document.getElementById(\"step2\");\n    return !!(bs && bs.style.display !== \"none\");              // only while editing\n  }\n\n  window.addEventListener(\"beforeunload\", function (e) {\n    // Intentional in-app hop (e.g. Book \u21C6 Card): the design is snapshot-saved on\n    // unload, so don't nag with the browser's \"Leave site?\" warning.\n    if (window.__dmbIntentionalNav) return;\n    // NOTE: no \"photos still uploading\" block anymore \u2014 uploads run in the background\n    // and the design's photos live in IndexedDB (+ self-heal / claim on sign-in), so\n    // leaving mid-upload no longer loses them. Only the guest \"sign in to save\" leave\n    // warning remains.\n    if (!_dmbLeaveGuardActive()) return;\n    e.preventDefault();\n    e.returnValue = \"\";   // triggers the browser's generic \"Leave site?\" warning\n  });\n\n  function _dmbShowLeaveGuardModal(onLeave) {\n    let ov = document.getElementById(\"dmbLeaveGuard\");\n    if (ov) { ov._onLeave = onLeave; ov.style.display = \"flex\"; return; }\n    ov = document.createElement(\"div\");\n    ov.id = \"dmbLeaveGuard\";\n    ov.style.cssText = \"position:fixed;inset:0;z-index:1000003;display:flex;align-items:center;justify-content:center;background:rgba(17,17,17,.55);padding:18px;\";\n    ov._onLeave = onLeave;\n    const card = document.createElement(\"div\");\n    card.style.cssText = \"background:#f4efe7;border-radius:20px;max-width:360px;width:100%;padding:30px 26px 22px;text-align:center;position:relative;box-shadow:0 24px 70px rgba(0,0,0,.35);font-family:inherit;\";\n    card.innerHTML =\n      '<div style=\"font-size:34px;margin:2px 0 6px;\">\uD83D\uDCBE</div>' +\n      '<h3 style=\"margin:0 0 6px;font-size:22px;font-weight:800;color:#1e1e1e;\">Save your work?</h3>' +\n      '<p style=\"margin:0 0 20px;font-size:14px;line-height:1.55;color:#a06f86;font-weight:600;\">You\u2019re not signed in, so your design could be lost later. Sign in to keep it safe on any device.</p>' +\n      '<button type=\"button\" id=\"dmbLeaveSignIn\" style=\"display:block;width:100%;box-sizing:border-box;background:#1e1e1e;color:#fff;border:none;border-radius:11px;padding:13px 18px;font-size:15px;font-weight:800;cursor:pointer;\">Sign in to save</button>' +\n      '<button type=\"button\" id=\"dmbLeaveGo\" style=\"margin-top:10px;background:transparent;border:none;color:#9a8f82;font-size:13px;font-weight:600;cursor:pointer;padding:6px;\">Leave without saving</button>';\n    ov.appendChild(card);\n    document.body.appendChild(ov);\n    card.querySelector(\"#dmbLeaveSignIn\").addEventListener(\"click\", function () { _dmbGoSignIn(); });\n    card.querySelector(\"#dmbLeaveGo\").addEventListener(\"click\", function () {\n      _dmbLeaveGuardDismissed = true;               // also disables the native warning\n      ov.style.display = \"none\";\n      const fn = ov._onLeave; if (typeof fn === \"function\") fn();\n    });\n    ov.addEventListener(\"click\", function (e) { if (e.target === ov) ov.style.display = \"none\"; });\n  }\n\n  // Intercept clicks on links that LEAVE the builder page (logo, home, menu\u2026).\n  document.addEventListener(\"click\", function (e) {\n    const a = e.target && e.target.closest ? e.target.closest(\"a[href]\") : null;\n    if (!a) return;\n    const raw = a.getAttribute(\"href\") || \"\";\n    if (!raw || raw.charAt(0) === \"#\" || raw.indexOf(\"javascript:\") === 0) return;\n    if (a.target === \"_blank\") return;               // new tab \u2192 this one stays open\n    let url; try { url = new URL(raw, location.href); } catch (err) { return; }\n    if (url.origin === location.origin && url.pathname === location.pathname) return; // staying here\n\n    // No \"wait for uploads\" hold anymore \u2014 uploads run in the background; photos are\n    // in IndexedDB and self-heal / claim on sign-in, so leaving mid-upload is safe.\n    // Only the guest \"sign in to save\" nudge remains.\n    if (!_dmbLeaveGuardActive()) return;\n    e.preventDefault(); e.stopPropagation();\n    if (e.stopImmediatePropagation) e.stopImmediatePropagation();\n    _dmbShowLeaveGuardModal(function () { window.location.href = url.href; });\n  }, true);\n\n  // \u2500\u2500 Idle nudge: after a guest sits inactive with a design, prompt sign-in \u2500\u2500\u2500\u2500\n  // Light-card popup matching the site aesthetic: warm white card, periwinkle\n  // \"Sign in\" button, and an \u2715 in the top-right corner to close. Floats above the\n  // mobile bottom tab bar so it never hides behind it. Once per session.\n  function _dmbShowIdleSaveNudge() {\n    if (document.getElementById(\"dmbIdleNudge\")) return;\n    // Sit clear of the mobile bottom tab bar (#dmbMshTabs); fall back to 20px on desktop.\n    let bottomPx = 20;\n    try {\n      const tabs = document.getElementById(\"dmbMshTabs\");\n      if (tabs) { const b = tabs.getBoundingClientRect(); if (b.height > 0 && b.top < window.innerHeight) bottomPx = Math.max(20, window.innerHeight - b.top + 12); }\n    } catch (e) {}\n    const el = document.createElement(\"div\");\n    el.id = \"dmbIdleNudge\";\n    el.style.cssText = \"position:fixed;bottom:\" + bottomPx + \"px;left:50%;transform:translateX(-50%);z-index:999997;background:#ffffff;color:#3d3833;border:1px solid #e7e1d4;border-radius:18px;padding:22px 24px 18px;box-shadow:0 18px 50px rgba(70,60,40,.28);width:min(340px,calc(100vw - 28px));box-sizing:border-box;font-family:inherit;text-align:center;\";\n    el.innerHTML =\n      '<button type=\"button\" id=\"dmbIdleNudgeX\" aria-label=\"Close\" style=\"position:absolute;top:9px;right:12px;border:none;background:transparent;font-size:26px;line-height:1;color:#a89e8b;cursor:pointer;padding:4px 6px;\">&times;</button>' +\n      '<div style=\"font-size:32px;margin:0 0 6px;\">&#128190;</div>' +\n      '<h3 style=\"margin:0 0 5px;font-size:19px;font-weight:800;color:#3d3833;\">Save your work</h3>' +\n      '<p style=\"margin:0 0 16px;font-size:13.5px;line-height:1.5;color:#8a8272;font-weight:600;\">Sign in to save your design and open it on any device.</p>' +\n      '<button type=\"button\" id=\"dmbIdleNudgeGo\" style=\"display:block;width:100%;box-sizing:border-box;background:#6b62d6;color:#fff;border:none;border-radius:11px;padding:13px 18px;font-size:15px;font-weight:800;cursor:pointer;\">Sign in</button>';\n    document.body.appendChild(el);\n    el.querySelector(\"#dmbIdleNudgeGo\").addEventListener(\"click\", function () { _dmbGoSignIn(); });\n    el.querySelector(\"#dmbIdleNudgeX\").addEventListener(\"click\", function () { el.remove(); });\n  }\n  (function _dmbWireIdleNudge() {\n    const IDLE_MS = 150000;   // ~2.5 min of no interaction\n    let _timer = null, _fired = false;\n    function _fire() {\n      if (_dmbRenderMode()) return;   // render mode: no UI\n      if (_fired) return;\n      if (window.DMB_CUSTOMER_ID) return;                          // signed in \u2192 skip\n      if (!editor || !(editor.totalPages > 0)) { _reset(); return; } // no work yet\n      const bs = document.getElementById(\"step2\");\n      if (!(bs && bs.style.display !== \"none\")) { _reset(); return; } // not editing\n      _fired = true;\n      try { _dmbShowIdleSaveNudge(); } catch (e) {}\n    }\n    function _reset() {\n      if (_fired) return;\n      clearTimeout(_timer);\n      _timer = setTimeout(_fire, IDLE_MS);\n    }\n    [\"pointerdown\", \"keydown\", \"touchstart\", \"wheel\"].forEach(function (ev) {\n      document.addEventListener(ev, _reset, { passive: true });\n    });\n    _reset();\n  })();\n\n  async function addDesignedBookToCart() {\n    const pagesSelected = String(pagesEl?.value || \"\");\n    const sizeSelected = (editor.bookSize === \"A5\" ? \"A5\" : \"A4\");\n    const variantId = getDmbVariantIdByPages(pagesSelected, sizeSelected);\n\n    if (!variantId) {\n      alert(\"Please select the number of pages before adding to cart.\");\n      return;\n    }\n\n    const titleOk = !!getEffectiveBookTitle();\n    if (!titleOk) {\n      alert(\"Please add at least one text to the cover before adding to cart.\");\n      return;\n    }\n\n    // \u2500\u2500 Guest safety step (mandatory when NOT signed in) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // A guest's photos live only on this device and the browser can clear them\n    // after ~a week \u2014 which produced a silent all-white book. Make them check,\n    // and offer sign-in (which backs the design up so it can never be lost).\n    if (!window.DMB_CUSTOMER_ID) {\n      const _g = await showChoiceDialog(\n        \"One check before you order\",\n        \"You're not signed in, so your design is saved only on this device and its photos can be lost later. Please make sure every page still shows your photos. Signing in keeps your book safe on any device.\",\n        [\n          { id: \"preview\", label: \"Preview my book first\" },\n          { id: \"signin\",  label: \"Sign in to keep it safe\" },\n          { id: \"ok\",      label: \"I checked \u2014 it's correct\" }\n        ]\n      );\n      if (!_g || _g === \"cancel\") return;\n      if (_g === \"preview\") { try { openPreviewOverlay(); } catch (e) {} return; }\n      if (_g === \"signin\")  { try { _dmbGoSignIn(); } catch (e) {} return; }\n      // _g === \"ok\" \u2192 fall through to the hard photo check below\n    }\n\n    // \u2500\u2500 Hard guard: never let a book with MISSING photos be ordered \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // This runs for everyone and is the real safety net \u2014 even if the customer\n    // clicked \"it's correct\", a photo the device dropped is caught here instead\n    // of shipping a blank book.\n    if (saveMsg) { saveMsg.textContent = \"Checking your photos\u2026\"; saveMsg.style.display = \"block\"; }\n    const _missingPhotos = await _dmbCountMissingPhotos();\n    if (saveMsg) saveMsg.style.display = \"none\";\n    if (_missingPhotos > 0) {\n      await showChoiceDialog(\n        \"Some photos are missing\",\n        _missingPhotos + \" photo\" + (_missingPhotos > 1 ? \"s\" : \"\") + \" in your book can't be found on this device anymore, so it would print blank. Please reopen your design and re-add your photos, then order again.\",\n        [{ id: \"ok\", label: \"OK\" }]\n      );\n      if (addToCartBtn) { addToCartBtn.disabled = false; addToCartBtn.textContent = \"Add to Cart\"; }\n      return;\n    }\n\n    // NOTE: Minimum photo check is enforced at the upload step (continueToBuilderBtn\n    // and tryAutoOpenEditor), so no need to re-block here at cart time.\n\n    // \u2500\u2500 Empty-sheet offer \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // A \"sheet\" is a facing pair (2,3),(4,5),\u2026 If BOTH its pages are completely\n    // empty, offer to drop the whole sheet (both pages \u2014 keeps the count even and\n    // bindable). \"Keep\" \u2192 PDF exactly as before. \"Remove\" \u2192 those pages are\n    // skipped when building the PDF.\n    let _excludePages = null;\n    // Books only \u2014 magazines weren't in scope, so they behave exactly as before.\n    const _emptySheets = window.DMB_MAGAZINE_MODE ? [] : _dmbGetEmptySheets();\n    if (_emptySheets.length > 0) {\n      const _list = _emptySheets.map(function (s) { return s[0] + \"\u2013\" + s[1]; }).join(\", \");\n      const _n = _emptySheets.length;\n      const _choice = await showChoiceDialog(\n        _n + (_n > 1 ? \" empty sheets found\" : \" empty sheet found\"),\n        \"Pages \" + _list + \" \" + (_n > 1 ? \"are\" : \"is\") +\n          \" completely blank. Do you want to keep these empty pages in your book, or remove them?\",\n        [\n          { id: \"keep\",   label: \"Keep the empty pages\" },\n          { id: \"remove\", label: \"Remove the empty sheet\" + (_n > 1 ? \"s\" : \"\") },\n          { id: \"cancel\", label: \"Go back and edit\" }\n        ]\n      );\n      if (!_choice || _choice === \"cancel\") return;   // abort \u2014 let them keep editing\n      if (_choice === \"remove\") {\n        _excludePages = new Set();\n        _emptySheets.forEach(function (s) { _excludePages.add(s[0]); _excludePages.add(s[1]); });\n      }\n    }\n    const _removedPages   = _excludePages ? _excludePages.size : 0;\n    const _pdfPageCount   = Math.max(0, (editor.totalPages || 0) - _removedPages);\n\n    const selectedCover = loadCoverFromStorage ? (loadCoverFromStorage() || null) : null;\n    const _extraSpreads = editor.extraSpreads || 0;\n\n    let properties = {\n      \"Book Title\": getEffectiveBookTitle(),\n      \"Size\": sizeSelected,\n      \"Orientation\": (editor.orientation === \"landscape\" ? \"Landscape\" : \"Portrait\"),\n      \"Pages Selected\": pagesSelected,\n      \"Extra Spreads\": String(_extraSpreads),\n      \"Removed Sheets\": String(_removedPages / 2),\n      \"PDF Pages\": String(_pdfPageCount),\n      \"Total Pages\": String(editor?.totalPages || Number(pagesSelected)),\n      \"Editable Pages\": String(editor?.editablePages ?? (Number(pagesSelected) - 1)),\n      \"Mode\": mode === \"customize\" ? \"customize\" : \"collection\",\n      \"Selected Cover\": selectedCover?.title || selectedCover?.handle || selectedCover?.name || \"\",\n      \"Photos Uploaded\": String(Array.isArray(bookFiles) ? bookFiles.length : (editor?.photos?.length || 0)),\n      \"Special Page Text\": String(SPECIAL_PAGE_TEXT || \"\"),\n      \"Design Version\": \"DMB v1\",\n      \"Cover Template\": coverState.template || \"default\",\n      \"Cover Title 1\": coverState.title1 || \"\",\n      \"Cover Title 2\": coverState.title2 || \"\",\n      \"Cover Color\": coverState.color || \"#ffffff\"\n    };\n\n    try {\n      properties[\"Layout JSON\"] = JSON.stringify({\n        page_slots: editor?.pageSlots || {},\n        page_rot: editor?.pageRot || {},\n        page_template: editor?.pageTemplate || {},\n        page_frames: editor?.pageFrames || {},\n        page_frame_rot: editor?.pageFrameRot || {},\n        total_pages: editor?.totalPages || Number(pagesSelected) || 0,\n        editable_pages: editor?.editablePages || Math.max(0, (Number(pagesSelected) || 0) - 1)\n      });\n    } catch {\n      properties[\"Layout JSON\"] = \"Layout serialization failed\";\n    }\n\n    const hiddenProps = {};\n    Object.keys(properties).forEach((k) => {\n      hiddenProps[\"_\" + String(k).toLowerCase().replace(/[^a-z0-9]+/g, \"_\").replace(/^_+|_+$/g, \"\")] = properties[k];\n    });\n\n    hiddenProps[\"Design Summary\"] =\n      `${properties[\"Book Title\"] || \"\"} \u2022 ${properties[\"Pages Selected\"] || \"\"} pages \u2022 ${properties[\"Photos Uploaded\"] || \"\"} photos`;\n\n    properties = hiddenProps;\n\n    const pdfOverlay = document.getElementById(\"dmbPdfOverlay\");\n    const pdfOverlayMsg = document.getElementById(\"dmbPdfOverlayMsg\");\n    const showPdfOverlay = (msg) => {\n      if (pdfOverlayMsg) pdfOverlayMsg.textContent = msg;\n      if (pdfOverlay) pdfOverlay.style.display = \"flex\";\n    };\n    const hidePdfOverlay = () => { if (pdfOverlay) pdfOverlay.style.display = \"none\"; };\n\n    if (addToCartBtn) {\n      addToCartBtn.disabled = true;\n      addToCartBtn.textContent = \"Adding...\";\n    }\n\n    showPdfOverlay((\"Binding your \" + (window.DMB_MAGAZINE_MODE ? \"magazine\" : \"book\")));\n    // Show generation progress bar\n    const _gProgWrap  = document.getElementById(\"dmbUploadProgressWrap\");\n    const _gProgBar   = document.getElementById(\"dmbUploadProgressBar\");\n    const _gProgLabel = document.getElementById(\"dmbUploadProgressLabel\");\n    const _gProgMsg   = document.getElementById(\"dmbPdfOverlayMsg\");\n    if (_gProgWrap) _gProgWrap.style.display = \"block\";\n    function _onGenProgress(done, total) {\n      const pct = Math.round((done / total) * 100);\n      if (_gProgBar)   _gProgBar.style.width   = pct + \"%\";\n      const _genMsgs = [\n        \"Bringing your memories to life\",\n        \"Turning your photos into something to keep\",\n        \"Your story is taking shape\",\n        \"Crafting something beautiful\",\n        \"Weaving your moments together\",\n        \"Every page tells a story\",\n        \"Adding the finishing touches\",\n        \"Almost a masterpiece\"\n      ];\n      if (_gProgLabel) _gProgLabel.textContent = _genMsgs[Math.floor((done / total) * (_genMsgs.length - 1))];\n      if (_gProgMsg)   _gProgMsg.textContent   = (\"Binding your \" + (window.DMB_MAGAZINE_MODE ? \"magazine\" : \"book\"));\n    }\n\n    // \u2500\u2500 Keep a digital copy? (option: stays in My Saved Designs after the order ships) \u2500\u2500\n    // Signed-in customers only: the copy lives in their account. Guests are sent to\n    // sign in first, because their photos exist only on this device.\n    let _keepCopy = false;\n    {\n      const _k = await showChoiceDialog(\n        \"Keep a digital copy?\",\n        \"Would you like to keep a digital copy of this book on the website so you can edit it later? It stays in My Saved Designs after your order ships.\",\n        [\n          { id: \"keep\",   label: window.DMB_CUSTOMER_ID ? \"Yes, keep a copy\" : \"Yes, sign in to keep a copy\" },\n          { id: \"print\",  label: \"No, just print it\" },\n          { id: \"cancel\", label: \"Cancel\" }\n        ]\n      );\n      if (!_k || _k === \"cancel\") return;\n      if (_k === \"keep\") {\n        if (!window.DMB_CUSTOMER_ID) { try { _dmbGoSignIn(); } catch (e) {} return; }\n        _keepCopy = true;\n        // Make sure the account copy is current before it is ordered.\n        try { if (typeof _dmbCloudFlushNow === \"function\") await _dmbCloudFlushNow(false); } catch (e) {}\n      }\n    }\n\n    // \u2500\u2500 Step 1: Save the design BEFORE generating the PDF \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    // The design is persisted so the customer never loses their work regardless\n    // of what happens next. It is only cleared after a confirmed successful\n    // upload AND cart add. If anything fails, they can just press Add to Cart\n    // again and their design is right there waiting.\n    showPdfOverlay(\"Saving your design\");\n    // Best-effort local save. The design is being ORDERED right now (its PDF is\n    // generated + uploaded and added to the cart below), and for logged-in\n    // customers it's also mirrored to the cloud \u2014 so a failed local autosave\n    // (e.g. localStorage full) must NEVER block checkout. Non-fatal on purpose.\n    try {\n      if (saveMsg) saveMsg.style.display = \"none\";\n      await saveCustomerProgress();\n      if (saveMsg) saveMsg.style.display = \"none\";\n    } catch(saveErr) {\n      console.warn(\"Local design save failed \u2014 continuing with order:\", saveErr);\n    }\n\n    try {\n      // \u2500\u2500 Step 2+3: Produce the print PDF and get its Drive file id (`up`). \u2500\u2500\n      // LARGE books (> _DMB_STREAM_MIN_PAGES actual pages) use the STREAMING path so\n      // a big book can't exhaust a phone's memory: render each page, upload it, and\n      // let the Cloud Run stapler assemble the PDF. The output is byte-identical.\n      // Everything else uses today's proven in-browser path. If streaming fails for\n      // ANY reason we fall back to the in-browser path \u2014 an order is never blocked.\n      // Payment still happens AFTER this, exactly as before.\n      let up = null;\n      const _pagesActual = Number(editor && editor.totalPages) || 0;\n\n      // \u2500\u2500 STEP 4: FULL-QUALITY SERVER RENDER (window.DMB_SERVER_RENDER) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\n      // Renders on the server from the 3500px print-masters and makes the customer\n      // approve the REAL pages before anything is bound. Fails SOFT: on any problem\n      // - or if the customer rejects the proof - it returns null, aborts the job and\n      // drops through to the two existing paths below, so an order is never blocked.\n      try { window.__DMB_SR_ELIGIBLE = _dmbServerRenderOn(); } catch (e) {}\n      if (_dmbServerRenderOn()) {\n        try {\n          up = await _dmbServerRenderAndBind(_excludePages, showPdfOverlay, _onGenProgress, hidePdfOverlay);\n          // The customer pressed \"Something's wrong - go back\" on the proof. The job\n          // is already aborted server-side and they want to keep editing, so put the\n          // builder back exactly as they left it and stop. Anything else here would\n          // rebuild the whole PDF in the browser behind an overlay they cannot close.\n          if (up === \"__dmb_proof_rejected__\") {\n            hidePdfOverlay();\n            if (_gProgWrap)  _gProgWrap.style.display = \"none\";\n            if (_gProgBar)   _gProgBar.style.width    = \"0%\";\n            if (_gProgLabel) _gProgLabel.textContent  = \"\";\n            if (addToCartBtn) { addToCartBtn.disabled = false; addToCartBtn.textContent = \"Add to Cart\"; }\n            return;\n          }\n          if (up && _gProgBar)   _gProgBar.style.width  = \"100%\";\n          if (up && _gProgLabel) _gProgLabel.textContent = (\"Your \" + (window.DMB_MAGAZINE_MODE ? \"magazine\" : \"book\") + \" is ready\");\n          if (!up) showPdfOverlay((\"Binding your \" + (window.DMB_MAGAZINE_MODE ? \"magazine\" : \"book\")));   // render failed -> browser fallback\n        } catch (srErr) {\n          console.warn(\"Server render failed:\", srErr);\n          up = null;\n        }\n      }\n      // The in-browser fallback (buildPdfBlobOnePagePerBookPage without streaming) holds\n      // EVERY page in jsPDF at once, so it reliably runs out of memory beyond ~60 pages.\n      // Big books therefore NEVER use it \u2014 they retry the server streaming path instead.\n      const _bigBook = _pagesActual > 60;\n      const _streamEligible =\n        window.DMB_STREAM_PDF_ENABLED !== false &&          // master kill-switch (liquid)\n        !!window.DMB_DESIGNS_PROXY &&\n        typeof window.__dmbBuildPdfStreaming === \"function\" &&\n        _pagesActual > _DMB_STREAM_MIN_PAGES;\n\n      if (!up && _streamEligible) {\n        // SINGLE streaming pass \u2014 never re-render a big book (that doubled the load and\n        // could exhaust the device). If it fails, we surface the saved-book retry message.\n        try {\n          showPdfOverlay((\"Binding your \" + (window.DMB_MAGAZINE_MODE ? \"magazine\" : \"book\")));\n          up = await _dmbStreamingBuildAndUpload(\"\", _excludePages, _onGenProgress, showPdfOverlay, hidePdfOverlay);\n          // \"Something's wrong, go back\" on the fallback's proof. Same sentinel and the\n          // same handling as the server path: put the builder back and stop. Falling\n          // through would bind and charge for a book they just rejected.\n          if (up === \"__dmb_proof_rejected__\") {\n            hidePdfOverlay();\n            if (_gProgWrap)  _gProgWrap.style.display = \"none\";\n            if (_gProgBar)   _gProgBar.style.width    = \"0%\";\n            if (_gProgLabel) _gProgLabel.textContent  = \"\";\n            if (addToCartBtn) { addToCartBtn.disabled = false; addToCartBtn.textContent = \"Add to Cart\"; }\n            return;\n          }\n          if (_gProgBar)   _gProgBar.style.width  = \"100%\";\n          if (_gProgLabel) _gProgLabel.textContent = (\"Your \" + (window.DMB_MAGAZINE_MODE ? \"magazine\" : \"book\") + \" is ready\");\n        } catch (streamErr) {\n          console.warn(\"Streaming PDF failed:\", streamErr);\n          up = null;\n        }\n      }\n\n      if (!up) {\n        if (_bigBook) {\n          // Do NOT attempt the in-browser accumulate build for a big book \u2014 it would grind\n          // for minutes and then crash the tab. The design is already saved (Step 1), so the\n          // outer catch shows the \"saved in Saved Books, please try again\" message.\n          throw new Error(\"Big book server binding unavailable \u2014 design saved, please retry shortly.\");\n        }\n        // Small book \u2014 the in-browser path is safe (few pages, low memory).\n        showPdfOverlay((\"Binding your \" + (window.DMB_MAGAZINE_MODE ? \"magazine\" : \"book\")));\n        const pdfBlob = await buildPdfBlobOnePagePerBookPage(_onGenProgress, null, _excludePages);\n        if (_gProgBar)   _gProgBar.style.width  = \"100%\";\n        if (_gProgLabel) _gProgLabel.textContent = (\"Your \" + (window.DMB_MAGAZINE_MODE ? \"magazine\" : \"book\") + \" is ready\");\n        showPdfOverlay((\"Sending your \" + (window.DMB_MAGAZINE_MODE ? \"magazine\" : \"book\")));\n        up = await uploadPdfToGoogleDrive(pdfBlob);\n      }\n\n      properties[\"_pdf_view\"]      = up.viewUrl;\n      properties[\"_pdf_download\"]  = up.downloadUrl;\n      properties[\"_pdf_file_id\"]   = up.id;\n      // Link this line to its stored design so its photos can be auto-deleted from\n      // R2 after the order (retention webhook). Works for BOTH logged-in customers\n      // (owner = customer id) and guests (owner = guest_<sid>).\n      try {\n        properties[\"_design_id\"] = getCustomerSaveKey().replace(/^dmb_progress_/, \"\");\n        if (_keepCopy) properties[\"_keep_copy\"] = \"yes\";\n        if (!window.DMB_CUSTOMER_ID && typeof _dmbPhotoBackup !== \"undefined\") {\n          const _sid = _dmbPhotoBackup.guestId();\n          if (_sid) properties[\"_design_sid\"] = _sid;\n        }\n      } catch (e) {}\n\n      // \u2500\u2500 Step 4: Add to cart \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n      showPdfOverlay(\"Adding to your cart\");\n      const _cartItems = [{ id: variantId, quantity: 1, properties }];\n      const _extraSpreadVid = window.DMB_EXTRA_SPREAD_VARIANT_ID;\n      if (_extraSpreads > 0 && _extraSpreadVid) {\n        _cartItems.push({ id: _extraSpreadVid, quantity: _extraSpreads });\n      }\n\n      const res = await fetch(\"/cart/add.js\", {\n        method: \"POST\",\n        headers: { \"Content-Type\": \"application/json\", \"Accept\": \"application/json\" },\n        body: JSON.stringify({ items: _cartItems })\n      });\n\n      if (!res.ok) {\n        // Cart add failed \u2014 trash the Drive file we just uploaded so it doesn't\n        // accumulate as an orphan. Customer's design is still saved; they retry\n        // and a fresh upload + cart add will happen together.\n        try {\n          await postToScript({ action: \"trash_file\", fileId: up.id }, 2);\n        } catch(te) { /* non-fatal \u2014 cleanupOldChunks will catch it eventually */ }\n        let msg = \"Failed to add to cart.\";\n        try { const err = await res.json(); msg = err?.description || err?.message || msg; } catch {}\n        throw new Error(msg);\n      }\n\n      // \u2500\u2500 Step 5: Added to cart \u2014 DO NOT destroy the customer's design/photos yet \u2500\u2500\n      // The item is only in the CART; the customer has NOT paid. If we deleted the design\n      // + R2 photos now (as before) and they abandoned checkout \u2014 or anything went wrong\n      // after this point \u2014 their book and every photo would be gone permanently. So we\n      // KEEP the recoverable cloud/R2 copy and its photos; the post-order retention webhook\n      // (linked via _design_id / _design_sid on the line item) removes them AFTER the order\n      // is actually placed. We only stop the local leftover from popping a resume prompt.\n      try { localStorage.removeItem(getCustomerSaveKey()); } catch (e) {}\n      try {\n        localStorage.setItem(\"design_my_book_last_added\", JSON.stringify({\n          added_at: new Date().toISOString(),\n          pages_selected: pagesSelected,\n          properties\n        }));\n      } catch {}\n\n      // Order placed \u2014 the design is now in the cart, so the leave guard must not\n      // pop a \"Leave site?\" warning on this intended redirect.\n      try { _dmbLeaveGuardDismissed = true; } catch (e) {}\n      window.location.href = \"/\";\n\n    } catch (err) {\n      // \u2500\u2500 Any failure: hide overlay, show friendly message, re-enable button \u2500\u2500\n      // The design is already saved (Step 1) so the customer can try again at\n      // any time \u2014 their book will be exactly as they left it.\n      hidePdfOverlay();\n      const _msg = \"Due to a connection issue your book could not be processed. It's saved in your Saved Books \u2014 please try again on a better Wi-Fi connection. \uD83D\uDCF6\";\n\n      // Show as a dismissible banner above the builder (not a blocking alert)\n      const _errBanner = document.createElement(\"div\");\n      _errBanner.style.cssText = \"position:fixed;top:16px;left:50%;transform:translateX(-50%);z-index:999999;background:#1e1e1e;color:#fff;border-radius:12px;padding:14px 20px;max-width:92vw;font-size:14px;font-weight:700;text-align:center;box-shadow:0 8px 30px rgba(0,0,0,.5);display:flex;align-items:center;gap:12px;\";\n      _errBanner.innerHTML = `<span>${_msg}</span><button type=\"button\" style=\"background:#444;border:none;border-radius:8px;color:#fff;padding:6px 12px;cursor:pointer;font-size:13px;font-weight:900;flex-shrink:0;\">OK</button>`;\n      _errBanner.querySelector(\"button\").onclick = () => _errBanner.remove();\n      document.body.appendChild(_errBanner);\n      setTimeout(() => { if (_errBanner.isConnected) _errBanner.remove(); }, 12000);\n\n      if (addToCartBtn) {\n        addToCartBtn.disabled = false;\n        addToCartBtn.textContent = \"Add to Cart\";\n      }\n    }\n  }\n\n  // Navigation order: Cover \u2192 Spread 0 (currentSpread -1) \u2192 Spread 1 (0) \u2192 \u2026 \u2192 last.\n  if (prevSpreadBtn) {\n    prevSpreadBtn.addEventListener(\"click\", () => {\n      if (builderMode === \"cover\") return;        // cover is the first view \u2014 nothing before it\n      if (editor.currentSpread <= -1) {           // Spread 0 \u2192 back to the cover\n        builderMode = \"cover\";\n        renderBuilderCanvas();\n        return;\n      }\n      editor.currentSpread--;                     // spread n \u2192 n-1 (0 \u2192 -1 = Spread 0)\n      renderBuilderCanvas();\n    });\n  }\n\n  if (nextSpreadBtn) {\n    nextSpreadBtn.addEventListener(\"click\", () => {\n      if (builderMode === \"cover\") {              // cover \u2192 Spread 0\n        builderMode = \"pages\";\n        editor.currentSpread = -1;\n        renderBuilderCanvas();\n        return;\n      }\n      if (editor.currentSpread < editor.totalSpreads - 1) {\n        editor.currentSpread++;\n        renderBuilderCanvas();\n      }\n    });\n  }\n\n  if (undoBtn) {\n    undoBtn.addEventListener(\"click\", undoLastAction);\n  }\n\n  if (addToCartBtn) {\n    addToCartBtn.addEventListener(\"click\", () => {\n      // ONE review, of the REAL thing. With the server render on, the customer is\n      // shown the ACTUAL rendered pages (the proof) before anything is bound - that\n      // is strictly better than this preview, which is the browser's approximation\n      // rendered BEFORE the print pages exist. Showing both would be two review\n      // screens back to back for the same decision.\n      // Server render OFF (today's default) -> unchanged: confirm-mode preview.\n      if (_dmbServerRenderOn()) { addDesignedBookToCart(); return; }\n      // Open preview in confirm mode \u2014 customer must review every page\n      // before the PDF is generated and the order is placed.\n      openPreviewOverlay(true);\n    });\n  }\n\n  // \u2500\u2500 Extra spread buttons \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // \"+\" adds a spread (2 pages, +$1.99); \"\u2212\" removes the last added spread.\n  // Buttons are hidden until the customer selects a page count.\n  const _addSpreadBtn    = $(\"dmbAddSpreadBtn\");\n  const _removeSpreadBtn = $(\"dmbRemoveSpreadBtn\");\n  if (_addSpreadBtn)    _addSpreadBtn.addEventListener(\"click\", () => {\n    // Ask before/after the current spread and insert there (desktop AND phone).\n    try { _dmbAddSpreadPrompt(); } catch (e) { addExtraSpread(); }\n  });\n  if (_removeSpreadBtn) _removeSpreadBtn.addEventListener(\"click\", removeExtraSpread);\n  function initMagazineStylePicker() {\n    const styles = window.DMB_MAGAZINE_STYLES || [];\n    const grid = document.getElementById(\"dmbMagStyleGrid\");\n    const continueBtn = document.getElementById(\"continueToMagBuilderBtn\");\n    const backBtn = document.getElementById(\"backToUploadBtn\");\n    if (!grid) return;\n\n    // Only init once\n    if (grid.dataset.initialized) return;\n    grid.dataset.initialized = \"1\";\n\n    let selectedStyle = null;\n\n    styles.forEach(function(s) {\n      const card = document.createElement(\"div\");\n      card.className = \"dmb-mag-style-card\";\n      card.dataset.styleId = s.id;\n      card.style.cssText =\n        \"display:flex;flex-direction:column;align-items:center;gap:8px;padding:12px;\" +\n        \"border:2px solid #ddd;border-radius:10px;cursor:pointer;transition:border-color .15s,background .15s;\";\n\n      if (s.img) {\n        const img = document.createElement(\"img\");\n        img.src = s.img;\n        img.alt = s.label;\n        img.style.cssText = \"width:100%;height:140px;object-fit:cover;border-radius:6px;\";\n        card.appendChild(img);\n      } else {\n        const ph = document.createElement(\"div\");\n        ph.style.cssText =\n          \"width:100%;height:140px;background:#f0f0f0;border-radius:6px;\" +\n          \"display:flex;align-items:center;justify-content:center;color:#bbb;font-size:12px;text-align:center;padding:8px;box-sizing:border-box;\";\n        ph.textContent = \"Template coming soon\";\n        card.appendChild(ph);\n      }\n\n      const lbl = document.createElement(\"div\");\n      lbl.style.cssText = \"font-size:13px;font-weight:600;text-align:center;\";\n      lbl.textContent = s.label;\n      card.appendChild(lbl);\n\n      card.addEventListener(\"click\", function() {\n        grid.querySelectorAll(\".dmb-mag-style-card\").forEach(function(c) {\n          c.style.borderColor = \"#ddd\";\n          c.style.background = \"\";\n        });\n        card.style.borderColor = \"#111\";\n        card.style.background = \"#f5f5f5\";\n        selectedStyle = s.id;\n        window.DMB_SELECTED_MAGAZINE_STYLE = s.id;\n        if (continueBtn) continueBtn.disabled = false;\n      });\n\n      grid.appendChild(card);\n    });\n\n    if (continueBtn) {\n      continueBtn.disabled = true;\n      continueBtn.addEventListener(\"click\", async function() {\n        if (!selectedStyle) return;\n        const s1c = document.getElementById(\"step1c\");\n        if (s1c) s1c.style.display = \"none\";\n        if (builderStep) builderStep.style.display = \"block\";\n        try { await openEditor(); } catch(err) {\n          console.error(\"openEditor failed:\", err);\n          if (s1c) s1c.style.display = \"flex\";\n          if (builderStep) builderStep.style.display = \"none\";\n        }\n      });\n    }\n\n    if (backBtn) {\n      backBtn.addEventListener(\"click\", function() {\n        const s1c = document.getElementById(\"step1c\");\n        const s1b = document.getElementById(\"step1b\");\n        if (s1c) s1c.style.display = \"none\";\n        if (s1b) s1b.style.display = \"flex\";\n      });\n    }\n  }\n\n  if (backToDetailsBtn) {\n    backToDetailsBtn.addEventListener(\"click\", closeEditorBackToDetails);\n  }\n\n  if (saveLayoutBtn) {\n    saveLayoutBtn.style.display = \"none\";\n  }\n\n  window.addEventListener(\"beforeunload\", () => {\n    if (_dmbRenderMode()) return;   // render mode: no localStorage snapshot\n    try {\n      const payload = getCurrentBuilderStateSnapshot();\n      localStorage.setItem(_dmbProjKey(), JSON.stringify(payload));\n      // NOTE: we do NOT auto-save to the per-book customer key here.\n      // That key is only written by explicit \"Save Template\" clicks so it\n      // never gets corrupted by navigating away mid-session.\n    } catch {}\n  });\n  if (saveTemplateBtn) {\n    saveTemplateBtn.addEventListener(\"click\", () => {\n      // Guests must sign in to save (so the design follows them across devices).\n      if (!window.DMB_CUSTOMER_ID) { _dmbShowSaveSignIn(); return; }\n      Promise.resolve(saveCustomerProgress()).then(function () {\n        if (typeof _dmbCloudPersist === \"function\") _dmbCloudPersist();  // mirror to the account\n        _dmbShowSavedConfirm();   // confirm + clear link to the Saved designs folder\n      });\n    });\n  }\n\n  // Reflect editor.bookSize on the toggle's two segments (active = filled).\n  // Function declaration so it's hoisted \u2014 applyBuilderStateSnapshot calls it\n  // earlier in the file when restoring a saved design.\n  function _dmbSyncSizeToggleUI() {\n    const wrap = document.getElementById(\"dmbSizeToggle\");\n    if (!wrap) return;\n    const cur = editor.bookSize === \"A5\" ? \"A5\" : \"A4\";\n    wrap.querySelectorAll(\".dmb-size-opt\").forEach(function (b) {\n      const on = b.dataset.size === cur;\n      b.classList.toggle(\"is-active\", on);\n      b.setAttribute(\"aria-pressed\", on ? \"true\" : \"false\");\n    });\n  }\n\n  // \u2500\u2500 A4 / A5 size toggle (book builder only) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // The pill lives in the top bar between History and Undo. It only flips a\n  // label/price/variant \u2014 the design itself never changes (A4 & A5 share the\n  // same shape). Absent on the magazine builder, so all of this no-ops there.\n  (function _wireSizeToggle() {\n    const wrap = document.getElementById(\"dmbSizeToggle\");\n    if (!wrap) return;                       // no toggle on this page \u2192 skip\n    if (editor.bookSize !== \"A5\") editor.bookSize = \"A4\";\n    _dmbSyncSizeToggleUI();\n    wrap.querySelectorAll(\".dmb-size-opt\").forEach(function (btn) {\n      btn.addEventListener(\"click\", function () {\n        const sz = btn.dataset.size === \"A5\" ? \"A5\" : \"A4\";\n        if (editor.bookSize === sz) return;\n        editor.bookSize = sz;\n        _dmbSyncSizeToggleUI();\n        try { updatePrice(); } catch (e) {}   // refresh displayed price for the size\n        try { autoSave(); } catch (e) {}       // remember the choice with the design\n      });\n    });\n  })();\n\n  // Reflect editor.orientation on the toggle's two segments (active = filled).\n  // Hoisted so applyBuilderStateSnapshot can call it when restoring a design.\n  function _dmbSyncOrientationToggleUI() {\n    const wrap = document.getElementById(\"dmbOrientToggle\");\n    if (!wrap) return;\n    const cur = editor.orientation === \"landscape\" ? \"landscape\" : \"portrait\";\n    wrap.querySelectorAll(\".dmb-size-opt\").forEach(function (b) {\n      const on = b.dataset.orient === cur;\n      b.classList.toggle(\"is-active\", on);\n      b.setAttribute(\"aria-pressed\", on ? \"true\" : \"false\");\n    });\n  }\n\n  // \u2500\u2500 Portrait / Landscape orientation toggle \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // Sits next to the A4/A5 pill. Flipping RESHAPES the page: %-based photo\n  // layouts re-flow automatically, and free objects (text/stickers/frames) are\n  // center-mapped to the new shape so nothing is lost. Confirm first when there's\n  // a design to keep. Absent on pages without the pill (no-op there).\n  (function _wireOrientationToggle() {\n    const wrap = document.getElementById(\"dmbOrientToggle\");\n    if (!wrap) return;\n    if (editor.orientation !== \"landscape\") editor.orientation = \"portrait\";\n    _dmbSyncOrientationToggleUI();\n    wrap.querySelectorAll(\".dmb-size-opt\").forEach(function (btn) {\n      btn.addEventListener(\"click\", function () {\n        const to   = btn.dataset.orient === \"landscape\" ? \"landscape\" : \"portrait\";\n        const from = editor.orientation === \"landscape\" ? \"landscape\" : \"portrait\";\n        if (from === to) return;\n        const hasWork = (editor.totalPages || 0) > 0;\n        if (hasWork && !confirm(\n          to === \"landscape\"\n            ? \"Switch to Landscape? Your photos and text are kept \u2014 the layout adjusts to the wider shape.\"\n            : \"Switch back to Portrait? Your photos and text are kept \u2014 the layout adjusts to the taller shape.\"\n        )) return;\n        try { reshapeForOrientation(from, to); } catch (e) {}\n        editor.orientation = to;\n        _dmbSyncOrientationToggleUI();\n        // Re-render everything to the new shape.\n        try { renderBuilderCanvas(); } catch (e) {}\n        try { if (builderMode === \"cover\") renderCoverCanvas(); } catch (e) {}  // flip cover face too\n        try { renderThumbsRail(); } catch (e) {}\n        try { updateCoverPreview(); } catch (e) {}\n        try { _dmbPvReinitAll(); } catch (e) {}     // rebuild PDF-preview cache at new shape\n        try { autoSave(); } catch (e) {}\n      });\n    });\n  })();\n  window.addEventListener(\"resize\", () => {\n    if (builderStep && builderStep.style.display === \"block\") {\n      try { renderBuilderCanvas(); } catch(e) {}\n      renderThumbsRail();\n      renderPhotoTray();\n    }\n  });\n\n  if (redoBtn) redoBtn.addEventListener(\"click\", redoLastAction);\n\n  if (previewBtn) {\n    previewBtn.addEventListener(\"click\", () => {\n      openPreviewOverlay();\n    });\n  }\n\n  // \u2500\u2500 Tutorial Modal \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  (function() {\n    const tutBtn    = document.getElementById(\"videoTutorialBtn\");\n    const tutModal  = document.getElementById(\"dmbTutorialModal\");\n    const tutClose  = document.getElementById(\"dmbTutorialClose\");\n    const tourBtn   = document.getElementById(\"dmbStartTourBtn\");\n    const qrImg     = document.getElementById(\"dmbTutorialQRImg\");\n    const qrPh      = document.getElementById(\"dmbTutorialQRPlaceholder\");\n\n    if (!tutBtn || !tutModal) return;\n\n    // If QR image src is set, show the image and hide the placeholder\n    if (qrImg && qrImg.src && qrImg.src !== window.location.href) {\n      qrImg.style.display = \"block\";\n      if (qrPh) qrPh.style.display = \"none\";\n    }\n\n    function openModal()  { tutModal.style.display = \"flex\"; }\n    function closeModal() { tutModal.style.display = \"none\"; }\n\n    tutBtn.addEventListener(\"click\", openModal);\n    if (tutClose) tutClose.addEventListener(\"click\", closeModal);\n\n    // Click outside the inner box closes the modal\n    tutModal.addEventListener(\"click\", (e) => {\n      if (e.target === tutModal) closeModal();\n    });\n\n    // Escape key closes the modal\n    document.addEventListener(\"keydown\", (e) => {\n      if (e.key === \"Escape\" && tutModal.style.display !== \"none\") closeModal();\n    });\n\n    // Start Tour button\n    if (tourBtn) {\n      tourBtn.addEventListener(\"click\", () => {\n        closeModal();\n        setTimeout(() => {\n          if (window.dmbRestartTour) window.dmbRestartTour();\n        }, 200);\n      });\n    }\n  })();\n  if (exportTemplateBtn) {\n    exportTemplateBtn.addEventListener(\"click\", () => { saveTemplateToStore(); });\n  }\n\n  const zoomRange = $(\"zoomRange\");\n\n  if (zoomRange) {\n    zoomRange.addEventListener(\"input\", () => {\n      setCanvasZoom(Number(zoomRange.value) / 100);\n    });\n  }\n\n  if (zoomInBtn) {\n    zoomInBtn.addEventListener(\"click\", () => {\n      const next = Math.min(200, Math.round(currentZoom * 100) + 10);\n      setCanvasZoom(next / 100);\n    });\n  }\n\n  if (zoomOutBtn) {\n    zoomOutBtn.addEventListener(\"click\", () => {\n      const next = Math.max(25, Math.round(currentZoom * 100) - 10);\n      setCanvasZoom(next / 100);\n    });\n  }\n\n  if (smartCreationBtn) {\n    smartCreationBtn.addEventListener(\"click\", () => {\n      smartCreateBook();\n    });\n  }\n\n  if (coverThumbBtn) {\n    coverThumbBtn.addEventListener(\"click\", () => {\n      builderMode = \"cover\";\n      syncBuilderModeButtons();\n      renderBuilderCanvas();\n      activateLeftTab(\"cover\");\n    });\n  }\n\n  if (canvasLayoutBtn) {\n    canvasLayoutBtn.addEventListener(\"click\", () => {\n      document.querySelector('.dmb-icon-tab[data-tab=\"layouts\"]')?.click();\n    });\n  }\n\n  if (canvasTextBtn || addTextBtn) {\n    const addDefaultText = (e) => {\n      // Stop this click from bubbling to the document click-out handler, which\n      // would otherwise see the brand-new (still empty) box and remove it on the\n      // same click \u2014 making the button appear to do nothing.\n      if (e && e.stopPropagation) e.stopPropagation();\n      let target = null;\n\n      const _magFrontX3 = window.DMB_MAGAZINE_MODE ? COVER_BACK_W : COVER_FRONT_X;\n      let coverTextDefaultX = _magFrontX3 + 40;\n      if (builderMode === \"cover\") {\n        // Place text in the overlay (spans full cover) so drag is seamless across all zones\n        target = document.getElementById(\"coverOverlay\") || spreadCanvas;\n        const zoneOffsets = { back: 40, spine: COVER_BACK_W + 5, front: _magFrontX3 + 40 };\n        coverTextDefaultX = zoneOffsets[activeCoverZone] ?? (_magFrontX3 + 40);\n      } else {\n        const pageBodies = spreadCanvas?.querySelectorAll('[data-page-body=\"true\"]');\n        let bodyIdx = lastToolbarSide === \"right\" && pageBodies && pageBodies.length > 1 ? 1 : 0;\n        // Never drop text onto a LOCKED page (e.g. the inside-front-cover page 1 on\n        // spread 0). If the preferred side is locked, use the other page body.\n        if (pageBodies && pageBodies[bodyIdx] && pageBodies.length > 1) {\n          const _pn = parseInt(pageBodies[bodyIdx].dataset.pageNumber);\n          if (Number.isFinite(_pn) && isLockedPage(_pn)) bodyIdx = bodyIdx === 0 ? 1 : 0;\n        }\n        target = pageBodies && pageBodies.length ? pageBodies[bodyIdx] : null;\n      }\n\n      if (!target) return;\n      pushUndoState(\"Add Text\");\n      const txt = document.createElement(\"div\");\n      // Start empty with a CSS placeholder (not real text), so the box appears\n      // immediately, ready to type \u2014 nothing to select/delete first, and a stray\n      // Backspace can't wipe it.\n      txt.textContent = \"\";\n      txt.dataset.placeholder = \"Type here\";\n      txt.contentEditable = \"true\";\n      txt.classList.add(\"dmb-editable-text\");\n      // Spine: rotate -90\u00B0 so text reads along the spine\n      if (builderMode === \"cover\" && activeCoverZone === \"spine\") {\n        const spineCenter = COVER_BACK_W + Math.round(COVER_SPINE_W / 2);\n        txt.style.left = spineCenter + \"px\";\n        txt.style.top = \"50%\";\n        txt.style.transform = \"translate(-50%, -50%) rotate(-90deg)\";\n        txt.style.minWidth = \"\";\n      } else {\n        txt.style.left = (builderMode === \"cover\" ? coverTextDefaultX : 40) + \"px\";\n        txt.style.top = \"40px\";\n        txt.style.minWidth = \"120px\";\n      }\n      txt.style.padding = \"2px 6px\";\n      // Spine text defaults small (6px) to fit the thin 8 mm spine; everywhere else 28px.\n      txt.style.fontSize = (builderMode === \"cover\" && activeCoverZone === \"spine\") ? \"6px\" : \"28px\";\n      txt.style.fontWeight = \"800\";\n      txt.style.color = \"#666\";\n      txt.style.fontFamily = \"'Londrina Solid', sans-serif\";\n      txt.style.background = \"transparent\";\n      txt.style.outline = \"none\";\n      txt.dataset.z = String(Date.now());\n      txt.style.zIndex = txt.dataset.z;\n\n      // Track user-added cover text so it survives re-renders\n      if (builderMode === \"cover\") {\n        const utId = \"utext_\" + Date.now() + \"_\" + Math.random().toString(36).slice(2);\n        txt.id = utId;\n        const zone = activeCoverZone === \"back\" ? \"back\" : activeCoverZone === \"spine\" ? \"spine\" : \"front\";\n        if (!Array.isArray(editor.coverUserTexts)) editor.coverUserTexts = [];\n        editor.coverUserTexts.push({ id: utId, zone });\n        // Don't persist the box yet \u2014 it's empty, and saveCoverTextNodeState\n        // strips empty boxes from coverUserTexts. State is saved once the user\n        // types (input handler). If they never type, blur/click-out removes it.\n      }\n\n      if (builderMode !== \"cover\") {\n        const _pn = parseInt(target.dataset.pageNumber);\n        if (Number.isFinite(_pn)) {\n          txt.dataset.pageNumber = String(_pn);\n          const _txtId = \"pgtext_\" + Date.now() + \"_\" + Math.random().toString(36).slice(2);\n          txt.id = _txtId;\n          if (!Array.isArray(editor.pageObjects[_pn])) editor.pageObjects[_pn] = [];\n          editor.pageObjects[_pn].push({\n            id: _txtId, type: \"text\",\n            text: txt.textContent || \"\",\n            left: txt.style.left || \"40px\", top: txt.style.top || \"40px\",\n            fontSize: txt.style.fontSize || \"28px\", fontWeight: txt.style.fontWeight || \"800\",\n            color: txt.style.color || \"#666\", fontFamily: txt.style.fontFamily || \"'Londrina Solid', sans-serif\",\n            textAlign: \"\", textDecoration: \"\", transform: \"\", rotate: \"0\",\n            z: txt.dataset.z || String(Date.now()), width: \"\"\n          });\n        }\n      }\n      attachTextNodeHandlers(txt);\n      target.appendChild(txt);\n      // A brand-new box opens straight in writing mode so the user can type at once.\n      enterTextEditMode(txt);\n    };\n\n    if (canvasTextBtn) canvasTextBtn.addEventListener(\"click\", addDefaultText);\n    if (addTextBtn) addTextBtn.addEventListener(\"click\", addDefaultText);\n  }\n\n  function dmbAddFrameToActiveTarget(opts) {\n    opts = opts || {};\n    let target;\n\n    if (builderMode === \"cover\") {\n      target = {\n        targetType: \"cover\",\n        zone: getCurrentCoverZone()\n      };\n    } else {\n      let pageNumber = null;\n      if (editor.currentSpread === -1) {\n        pageNumber = 0;\n      } else {\n        const leftPage = editor.currentSpread * 2 + 1;\n        const rightPage = leftPage + 1;\n        if (lastToolbarSide === \"right\") {\n          if (rightPage <= editor.totalPages && !isLockedPage(rightPage)) pageNumber = rightPage;\n          else if (leftPage <= editor.totalPages && !isLockedPage(leftPage)) pageNumber = leftPage;\n        } else {\n          if (leftPage <= editor.totalPages && !isLockedPage(leftPage)) pageNumber = leftPage;\n          else if (rightPage <= editor.totalPages && !isLockedPage(rightPage)) pageNumber = rightPage;\n        }\n      }\n\n      if (pageNumber == null) {\n        const msg = document.getElementById(\"saveMsg\");\n        if (msg) {\n          msg.textContent = \"No editable page on this spread.\";\n          msg.style.display = \"block\";\n          setTimeout(() => { msg.style.display = \"none\"; }, 1400);\n        }\n        return;\n      }\n\n      target = {\n        targetType: \"page\",\n        page: pageNumber\n      };\n    }\n\n    pushUndoState(opts.polaroid ? \"Add Polaroid\" : \"Add Photo Frame\");\n\n    const obj = newFrameObject(target);\n    if (opts.polaroid) {\n      obj.polaroid = true;\n      obj.shadow = true;     // default ON (light)\n      obj.w = 200;\n      obj.h = 230;           // taller than wide \u2192 room for the bottom strip\n    }\n\n    if (target.targetType === \"cover\") {\n      // Offset x to full-cover coordinates based on the active zone\n      const _magFrontX4 = window.DMB_MAGAZINE_MODE ? COVER_BACK_W : COVER_FRONT_X;\n      const zoneOffsets = { back: 0, spine: COVER_BACK_W, front: _magFrontX4 };\n      // ?? not || : the BACK face offset is 0, which is FALSY, so `||` fell through to\n      // the front offset and a photo frame added with the back face selected landed on\n      // the FRONT. Zone-aware code that is wrong for exactly one zone. (2026-09-14)\n      obj.x = (zoneOffsets[target.zone] ?? _magFrontX4) + 40;\n      ensureCoverObjects().push(obj);\n    } else {\n      ensurePageObjects(target.page).push(obj);\n    }\n\n    if (builderMode === \"cover\") renderCoverCanvas(); else renderSpread();\n\n    requestAnimationFrame(() => {\n      const el = document.querySelector(`.dmb-photo-frame-object[data-object-id=\"${obj.id}\"]`);\n      if (el) {\n        showFrameToolbarFor(el, obj, {\n          targetType: obj.targetType,\n          pageNumber: obj.page ?? null,\n          zone: obj.zone || null\n        });\n      }\n    });\n  }\n\n  if (canvasPhotoBtn) {\n    canvasPhotoBtn.addEventListener(\"click\", () => dmbAddFrameToActiveTarget());\n  }\n\n  /* \u2500\u2500 SHAPE PICKER \u2014 open / close + item click handler \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   * Clicking \"Shapes\" in the toolbar toggles the floating shape picker panel.\n   * Clicking a shape in the panel either adds it immediately (all except line)\n   * or activates \"line draw mode\" where the next canvas drag creates the line.\n   *\n   * WARNING: Line draw mode adds a one-shot pointerdown listener on the canvas\n   * container. Always cancel _dmbLineModeActive via Escape or on completion so\n   * subsequent canvas clicks work normally.\n   * \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n  (function initShapePickerPanel() {\n    const panel   = document.getElementById(\"dmbShapePickerPanel\");\n    const closeBtn = document.getElementById(\"dmbShapePickerClose\");\n    if (!panel) return;\n\n    // Float the panel OVER the left photos panel (not below the Shapes button, and\n    // without replacing the panel's content). Falls back to sitting under the button\n    // when the left panel isn't on screen (e.g. mobile layout).\n    function positionShapePanel() {\n      const lp = document.querySelector(\".dmb-builder-leftpanel\");\n      const r = lp && lp.getBoundingClientRect();\n      if (r && r.width > 40 && r.height > 40) {\n        const pad = 10;\n        panel.style.top       = (r.top + pad) + \"px\";\n        panel.style.left      = (r.left + pad) + \"px\";\n        panel.style.width     = (r.width - pad * 2) + \"px\";\n        panel.style.maxHeight = (r.height - pad * 2) + \"px\";\n        panel.style.overflowY = \"auto\";\n        return;\n      }\n      if (!canvasShapeBtn) return;\n      const btnRect = canvasShapeBtn.getBoundingClientRect();\n      panel.style.top       = (btnRect.bottom + 6) + \"px\";\n      panel.style.left      = Math.max(8, btnRect.left) + \"px\";\n      panel.style.width     = \"\";\n      panel.style.maxHeight = \"\";\n      panel.style.overflowY = \"\";\n    }\n\n    if (canvasShapeBtn) {\n      canvasShapeBtn.addEventListener(\"click\", () => {\n        if (panel.style.display === \"none\" || !panel.style.display) {\n          positionShapePanel();\n          panel.style.display = \"block\";\n          canvasShapeBtn.classList.add(\"is-active\");\n        } else {\n          panel.style.display = \"none\";\n          canvasShapeBtn.classList.remove(\"is-active\");\n        }\n      });\n    }\n\n    closeBtn?.addEventListener(\"click\", () => {\n      panel.style.display = \"none\";\n      canvasShapeBtn?.classList.remove(\"is-active\");\n    });\n\n    // Close picker when clicking outside it\n    document.addEventListener(\"pointerdown\", (e) => {\n      if (!panel || panel.style.display === \"none\") return;\n      if (panel.contains(e.target))                 return;\n      if (e.target === canvasShapeBtn)              return; // handled by toggle above\n      panel.style.display = \"none\";\n      canvasShapeBtn?.classList.remove(\"is-active\");\n    }, { capture: true });\n\n    // Shape item click \u2192 add shape or activate line mode\n    panel.querySelectorAll(\".dmb-shape-item[data-shape]\").forEach((btn) => {\n      btn.addEventListener(\"click\", () => {\n        const shapeType = btn.dataset.shape;\n        panel.style.display = \"none\";\n        canvasShapeBtn?.classList.remove(\"is-active\");\n\n        if (shapeType === \"line\") {\n          // \u2500\u2500 LINE DRAW MODE \u2500\u2500 press where the line starts, release where it ends.\n          //\n          // The mechanism was already this; it was the plumbing that made it feel\n          // impossible to place. Three things were wrong, all fixed here (2026-09-14):\n          //\n          //  1. The line was filed to the page `lastToolbarSide` happened to point at,\n          //     NOT the page it was drawn on - while its coordinates were measured\n          //     against the page actually pressed. Draw on the right-hand page while the\n          //     toolbar last touched the left and the line was stored on the left page at\n          //     the right page's coordinates, i.e. somewhere else entirely.\n          //  2. Arming used per-element listeners with { once: true }. The first\n          //     pointerdown consumed them whatever happened, so a tap, a nudge under the\n          //     8px threshold, or a canvas re-render between arming and drawing all left\n          //     line mode silently dead - reopen the picker and try again.\n          //  3. The listeners sat on the page bodies, below the object drag handlers, so\n          //     pressing on anything already on the page started a DRAG instead of a line.\n          //\n          // Now: one delegated capture-phase listener that stays armed until a line is\n          // actually drawn or Escape is pressed.\n          _dmbLineModeActive = true;\n          document.body.classList.add(\"dmb-line-draw-mode\");\n\n          const endLineMode = () => {\n            _dmbLineModeActive = false;\n            document.body.classList.remove(\"dmb-line-draw-mode\");\n            document.removeEventListener(\"keydown\", cancelLine);\n            document.removeEventListener(\"pointerdown\", startLineDraw, true);\n          };\n          const cancelLine = (e) => { if (e.key === \"Escape\") endLineMode(); };\n          document.addEventListener(\"keydown\", cancelLine);\n\n          const startLineDraw = (downEvt) => {\n            if (!_dmbLineModeActive) return;\n            // Delegated: find the canvas under the pointer NOW, so a re-render between\n            // arming and drawing cannot leave this pointing at a detached element.\n            const target = (downEvt.target && downEvt.target.closest)\n              ? downEvt.target.closest('[data-page-body=\"true\"], .dmb-cover-preview, #coverOverlay')\n              : null;\n            if (!target) return;              // pressed off-canvas - stay armed\n            const tRect    = target.getBoundingClientRect();\n            // Canvas zoom: the page is CSS-scaled, so client px != page px.\n            const _lz      = (tRect.width / (target.offsetWidth || tRect.width)) || 1;\n            const _lcW     = target.offsetWidth  || (tRect.width  / _lz);\n            const _lcH     = target.offsetHeight || (tRect.height / _lz);\n            // A line is a SHAPE, and shapes may span both pages of a spread, so the draw\n            // is allowed to run across the fold onto the facing page. Y still clamps to\n            // the sheet - top and bottom are real edges on any page.\n            const _pnT     = Number(target.dataset && target.dataset.pageNumber);\n            const _isPg    = Number.isFinite(_pnT) && _pnT >= 1;\n            const _isLeft  = _isPg && (_pnT % 2 === 1);\n            const _sibPg   = _isPg ? (_isLeft ? _pnT + 1 : _pnT - 1) : 0;\n            const _hasSib  = _isPg && _sibPg >= 1 && _sibPg <= (editor.totalPages || 0);\n            const _xLo     = (_hasSib && !_isLeft) ? -_lcW : 0;\n            const _xHi     = (_hasSib &&  _isLeft) ? _lcW * 2 : _lcW;\n            const _lclampX = (v) => Math.max(_xLo, Math.min(_xHi, v));\n            const _lclampY = (v) => Math.max(0, Math.min(_lcH, v));\n            const startX   = _lclampX((downEvt.clientX - tRect.left) / _lz);\n            const startY   = _lclampY((downEvt.clientY - tRect.top)  / _lz);\n\n            // Live preview element\n            const preview = document.createElement(\"div\");\n            preview.style.cssText = \"position:absolute;pointer-events:none;background:#6366f1;opacity:0.6;transform-origin:left center;z-index:9999;border-radius:4px;\";\n            target.style.position = \"relative\";\n            target.appendChild(preview);\n\n            const onMove = (mv) => {\n              const ex = _lclampX((mv.clientX - tRect.left) / _lz);\n              const ey = _lclampY((mv.clientY - tRect.top)  / _lz);\n              const dx = ex - startX, dy = ey - startY;\n              const len = Math.sqrt(dx*dx + dy*dy);\n              const ang = Math.atan2(dy, dx) * 180 / Math.PI;\n              preview.style.width     = len + \"px\";\n              preview.style.height    = \"8px\";\n              preview.style.left      = startX + \"px\";\n              preview.style.top       = (startY - 4) + \"px\";\n              preview.style.transform = `rotate(${ang}deg)`;\n            };\n\n            const onUp = (upEvt) => {\n              preview.remove();\n              document.removeEventListener(\"pointermove\", onMove);\n\n              const ex   = _lclampX((upEvt.clientX - tRect.left) / _lz);\n              const ey   = _lclampY((upEvt.clientY - tRect.top)  / _lz);\n              const dx   = ex - startX, dy = ey - startY;\n              const len  = Math.sqrt(dx*dx + dy*dy);\n              // A tap, or a drag too short to be a line. STAY ARMED: this used to end\n              // line mode outright, so one twitchy press meant reopening the picker.\n              if (len < 8) return;\n              endLineMode();\n\n              const isCover = (builderMode === \"cover\");\n              // File it on the page it was DRAWN on. tRect, startX and startY are all\n              // measured against `target`, so any other page here stores the line at\n              // coordinates that mean nothing on it.\n              let pn = null;\n              if (!isCover) {\n                if (_isPg && !isLockedPage(_pnT)) {\n                  pn = _pnT;\n                } else if (editor.currentSpread === -1) {\n                  pn = 0;\n                } else {\n                  const _lp = editor.currentSpread * 2 + 1;\n                  const _rp = _lp + 1;\n                  if (lastToolbarSide === \"right\" && _rp <= editor.totalPages && !isLockedPage(_rp)) {\n                    pn = _rp;\n                  } else if (_lp <= editor.totalPages && !isLockedPage(_lp)) {\n                    pn = _lp;\n                  } else {\n                    pn = (_rp <= editor.totalPages) ? _rp : null;\n                  }\n                }\n              }\n              const lineObj = {\n                id: _dmbOid(\"shape_\"), type: \"shape\", shapeType: \"line\", lineType: \"straight\",\n                x1: startX, y1: startY, x2: ex, y2: ey,   // endpoints in page coords\n                thickness: 8,\n                fill: \"#6366f1\", stroke: \"transparent\", strokeWidth: 0, strokeStyle: \"solid\",\n                opacity: 1, rotation: 0, z: Date.now(),\n              };\n              try { _dmbLineRecomputeBox(lineObj); } catch (e) {}\n              pushUndoState(\"Add Line\");\n              if (isCover) {\n                if (!Array.isArray(editor.coverObjects)) editor.coverObjects = [];\n                editor.coverObjects.push(lineObj);\n                renderCoverCanvas();\n              } else if (pn !== null) {\n                if (!editor.pageObjects) editor.pageObjects = {};\n                if (!Array.isArray(editor.pageObjects[pn])) editor.pageObjects[pn] = [];\n                editor.pageObjects[pn].push(lineObj);\n                renderBuilderCanvas();\n              }\n            };\n\n            document.addEventListener(\"pointermove\", onMove);\n            document.addEventListener(\"pointerup\", onUp, { once: true });\n            // Capture phase + stopPropagation: in line mode the press belongs to the\n            // line, not to whatever object happens to be under the finger.\n            downEvt.preventDefault();\n            downEvt.stopPropagation();\n          };\n\n          document.addEventListener(\"pointerdown\", startLineDraw, true);\n        } else {\n          // All other shapes: add immediately at centre of canvas\n          insertShapeIntoActivePage(shapeType);\n        }\n      });\n    });\n  })();\n\n  if (canvasLayersBtn) {\n    canvasLayersBtn.addEventListener(\"click\", () => {\n      const msg = document.getElementById(\"saveMsg\");\n      if (msg) {\n        msg.textContent = \"Layers panel is not active yet.\";\n        msg.style.display = \"block\";\n        setTimeout(() => { msg.style.display = \"none\"; }, 1400);\n      }\n    });\n  }\n\n  if (canvasDeleteBtn) {\n    canvasDeleteBtn.addEventListener(\"click\", () => {\n      if (selectedTextNode) {\n        const node = selectedTextNode;\n        hideTextToolbar();\n        node.remove();\n        return;\n      }\n\n      if (selectedFrameObject) {\n        pushUndoState(\"Delete Frame\");\n        deleteFrameObjectByTarget(\n          selectedFrameObject.targetType,\n          selectedFrameObject.pageNumber,\n          selectedFrameObject.zone,\n          selectedFrameObject.id\n        );\n        hideFrameToolbar();\n        renderBuilderCanvas();\n        return;\n      }\n\n      const msg = document.getElementById(\"saveMsg\");\n      if (msg) {\n        msg.textContent = \"Select a text or frame first.\";\n        msg.style.display = \"block\";\n        setTimeout(() => { msg.style.display = \"none\"; }, 1400);\n      }\n    });\n  }\n\n\n  // \u2500\u2500 Per-batch photo limit + \"add more\" callout \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // Photos are added in batches of at most _DMB_BATCH_LIMIT. This keeps each\n  // upload light (the compress/decode loop only ever handles a small set at once)\n  // and makes big libraries additive instead of one huge, memory-heavy drop. When\n  // a selection exceeds the limit we keep the first N and tell the customer to add\n  // the rest in another batch, then point them at the \"Add photos\" button.\n  const _DMB_BATCH_LIMIT = 30;\n\n  function _dmbToast(msg, ms) {\n    try {\n      const el = document.createElement(\"div\");\n      el.style.cssText = \"position:fixed;left:50%;bottom:24px;transform:translateX(-50%);z-index:1000002;background:#1e1e1e;color:#fff;border-radius:12px;padding:12px 18px;max-width:90vw;font-size:14px;font-weight:700;line-height:1.4;box-shadow:0 10px 40px rgba(0,0,0,.35);text-align:center;\";\n      el.textContent = msg;\n      document.body.appendChild(el);\n      setTimeout(() => { try { el.remove(); } catch (e) {} }, ms || 4200);\n    } catch (e) {}\n  }\n\n  // Is this the memory-constrained (phone) upload path? Matches compressFilesForMobile.\n  function _dmbIsMobileUpload() {\n    // Render mode is never the phone path. This is the ONLY gate on the print-master\n    // branch in _dmbRenderImg, so a small headless viewport would otherwise render\n    // PROXY quality silently, with no error and no signal.\n    if (_dmbRenderMode()) return false;\n    try { return window.innerWidth <= 749; } catch (e) { return false; }\n  }\n\n  // Cap a freshly-picked file list to the batch limit; warns if it trimmed some.\n  // DESKTOP/LAPTOP: no limit \u2014 there's no memory crash there, so return everything.\n  function _dmbCapBatch(arr) {\n    const files = Array.from(arr || []);\n    if (_dmbPrintMasterOn()) return files;                   // print-master path \u2192 no cap (imports in internal sub-batches)\n    if (!_dmbIsMobileUpload()) return files;                 // laptop \u2192 unlimited batch\n    if (files.length <= _DMB_BATCH_LIMIT) return files;\n    const kept = files.slice(0, _DMB_BATCH_LIMIT);\n    _dmbToast(\"You can add up to \" + _DMB_BATCH_LIMIT + \" photos at a time. The first \" + _DMB_BATCH_LIMIT + \" were added \u2014 tap \u201CAdd photos\u201D again to add the rest.\", 6500);\n    return kept;\n  }\n\n  // \"Tour-style\" callout anchored to the visible Add-photos button, inviting the\n  // customer to add another batch. Has an X to dismiss; auto-hides on its own.\n  let _dmbAddMoreCalloutEl = null;\n  function _dmbHideAddMoreCallout() {\n    try { if (_dmbAddMoreCalloutEl) { _dmbAddMoreCalloutEl.remove(); _dmbAddMoreCalloutEl = null; } } catch (e) {}\n  }\n  function _dmbShowAddMoreCallout() {\n    // The mobile shell has its own Photos tab for adding more photos, so this\n    // floating callout (anchored to the old add-photos button) is redundant and\n    // mis-positions over the new top bar \u2014 skip it ONLY inside the builder\n    // (dmb-msh-fullscreen). It still shows on the upload-photos step.\n    if (window.matchMedia && window.matchMedia(\"(max-width: 749px), (orientation: portrait) and (pointer: coarse) and (max-width: 1024px)\").matches\n        && document.body.classList.contains(\"dmb-msh-fullscreen\")) return;\n    try {\n      _dmbHideAddMoreCallout();\n      // Prefer whichever Add-photos control is visible right now.\n      const cands = [document.getElementById(\"addMorePhotosBtn\"), document.querySelector(\".dmb-upload2-btn--primary\")];\n      let btn = null;\n      for (const c of cands) { if (c && c.offsetParent !== null) { btn = c; break; } }\n      if (!btn) btn = cands.find(Boolean);\n      if (!btn) return;\n      // Make sure the button (and the callout beneath it) are actually on screen.\n      try { btn.scrollIntoView({ block: \"center\", behavior: \"smooth\" }); } catch (e) {}\n      // One-time pulse styles for the button so it visibly draws the eye.\n      if (!document.getElementById(\"dmbAddMoreStyle\")) {\n        const st = document.createElement(\"style\"); st.id = \"dmbAddMoreStyle\";\n        st.textContent = \"@keyframes dmbAddPulse{0%{box-shadow:0 0 0 0 rgba(74,144,226,.6)}70%{box-shadow:0 0 0 12px rgba(74,144,226,0)}100%{box-shadow:0 0 0 0 rgba(74,144,226,0)}}.dmb-add-pulse{animation:dmbAddPulse 1.3s ease-out 3}\";\n        document.head.appendChild(st);\n      }\n      try { btn.classList.add(\"dmb-add-pulse\"); setTimeout(() => { try { btn.classList.remove(\"dmb-add-pulse\"); } catch (e) {} }, 4200); } catch (e) {}\n      const place = () => {\n        if (!_dmbAddMoreCalloutEl) return;\n        const r = btn.getBoundingClientRect();\n        _dmbAddMoreCalloutEl.style.top  = Math.min(window.innerHeight - 70, r.bottom + 10) + \"px\";\n        _dmbAddMoreCalloutEl.style.left = Math.max(8, Math.min(window.innerWidth - 236, r.left)) + \"px\";\n      };\n      const box = document.createElement(\"div\");\n      box.style.cssText = \"position:fixed;z-index:1000003;background:#4a90e2;color:#fff;border-radius:12px;padding:12px 34px 12px 14px;font-size:14px;font-weight:800;box-shadow:0 8px 28px rgba(0,0,0,.4);max-width:230px;line-height:1.35;\";\n      box.textContent = \"\u2795 Add more pictures\";\n      const arrow = document.createElement(\"div\");\n      arrow.style.cssText = \"position:absolute;top:-7px;left:22px;width:0;height:0;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #4a90e2;\";\n      box.appendChild(arrow);\n      const x = document.createElement(\"button\");\n      x.type = \"button\"; x.textContent = \"\u00D7\"; x.setAttribute(\"aria-label\", \"Close\");\n      x.style.cssText = \"position:absolute;top:3px;right:6px;background:transparent;border:none;color:#fff;font-size:19px;font-weight:900;cursor:pointer;line-height:1;padding:2px 4px;\";\n      x.addEventListener(\"click\", (e) => { e.stopPropagation(); _dmbHideAddMoreCallout(); });\n      box.appendChild(x);\n      document.body.appendChild(box);\n      _dmbAddMoreCalloutEl = box;\n      place();\n      const onMove = () => place();\n      window.addEventListener(\"scroll\", onMove, { passive: true });\n      window.addEventListener(\"resize\", onMove);\n      // Tapping the button itself dismisses the callout.\n      btn.addEventListener(\"click\", _dmbHideAddMoreCallout, { once: true });\n      setTimeout(_dmbHideAddMoreCallout, 20000);   // persist ~20s so it's not missed\n    } catch (e) {}\n  }\n\n  // \u2500\u2500 Blocking \"Uploading pictures\u2026\" modal \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // Shown while a batch is compressed + streamed to the cloud. It CANNOT be\n  // dismissed by hand: it closes itself when the batch's cloud upload finishes.\n  // This forces one batch at a time \u2014 a second batch can't pile on while the\n  // first is still in memory, which is what pushed the phone over its limit.\n  let _dmbUploadModalEl = null;\n  function _dmbShowUploadModal(msg) {\n    try {\n      if (!document.getElementById(\"dmbUplStyle\")) {\n        const st = document.createElement(\"style\"); st.id = \"dmbUplStyle\";\n        st.textContent = \"@keyframes dmbUplSpin{to{transform:rotate(360deg)}}\";\n        document.head.appendChild(st);\n      }\n      if (_dmbUploadModalEl) { _dmbSetUploadModalMsg(msg); return; }\n      const ov = document.createElement(\"div\");\n      ov.setAttribute(\"role\", \"alertdialog\");\n      ov.style.cssText = \"position:fixed;inset:0;z-index:1000010;background:rgba(15,17,22,.72);display:flex;align-items:center;justify-content:center;touch-action:none;\";\n      // Swallow taps so nothing behind the modal (incl. the file button) is usable.\n      ov.addEventListener(\"click\", (e) => { e.stopPropagation(); e.preventDefault(); }, true);\n      ov.addEventListener(\"touchstart\", (e) => { e.stopPropagation(); }, true);\n      const card = document.createElement(\"div\");\n      card.style.cssText = \"background:#fff;border-radius:16px;padding:26px 24px 22px;max-width:84vw;width:320px;text-align:center;box-shadow:0 20px 60px rgba(0,0,0,.45);\";\n      card.innerHTML =\n        '<div style=\"width:44px;height:44px;margin:0 auto 16px;border:4px solid #e6e9ef;border-top-color:#4a90e2;border-radius:50%;animation:dmbUplSpin .9s linear infinite;\"></div>'\n        + '<div class=\"dmb-upl-msg\" style=\"font-size:16px;font-weight:800;color:#1e1e1e;\">' + (msg || \"Uploading your pictures\u2026\") + '</div>'\n        + '<div style=\"margin-top:8px;font-size:13px;color:#666;font-weight:600;line-height:1.4;\">Keep this page open. You can add more once this finishes.</div>';\n      ov.appendChild(card);\n      document.body.appendChild(ov);\n      _dmbUploadModalEl = ov;\n    } catch (e) {}\n  }\n  function _dmbSetUploadModalMsg(msg) {\n    try { if (_dmbUploadModalEl && msg) { const m = _dmbUploadModalEl.querySelector(\".dmb-upl-msg\"); if (m) m.textContent = msg; } } catch (e) {}\n  }\n  function _dmbHideUploadModal() {\n    try { if (_dmbUploadModalEl) { _dmbUploadModalEl.remove(); _dmbUploadModalEl = null; } } catch (e) {}\n  }\n\n  // One-time (per session) heads-up shown when the customer lands on the upload\n  // step, so they know photos go in batches of 30 before they start selecting.\n  function _dmbShowLimitNotice() {\n    if (!_dmbIsMobileUpload()) return;                       // no batch limit on laptop \u2192 no notice\n    if (_dmbPrintMasterOn()) return;                         // print-master path has no 30-cap \u2192 no notice\n    try { if (sessionStorage.getItem(\"dmb_limit_notice_seen\") === \"1\") return; } catch (e) {}\n    try {\n      if (document.getElementById(\"dmbLimitNotice\")) return;\n      const ov = document.createElement(\"div\");\n      ov.id = \"dmbLimitNotice\";\n      ov.style.cssText = \"position:fixed;inset:0;z-index:1000009;background:rgba(15,17,22,.6);display:flex;align-items:center;justify-content:center;padding:16px;\";\n      const card = document.createElement(\"div\");\n      card.style.cssText = \"background:#fff;border-radius:16px;padding:24px 22px 20px;max-width:340px;width:100%;text-align:center;box-shadow:0 20px 60px rgba(0,0,0,.4);\";\n      card.innerHTML =\n        '<div style=\"font-size:34px;margin-bottom:8px;\">\uD83D\uDCF8</div>'\n        + '<div style=\"font-size:17px;font-weight:900;color:#1e1e1e;margin-bottom:8px;\">Add up to 30 photos at a time</div>'\n        + '<div style=\"font-size:14px;color:#555;font-weight:600;line-height:1.45;\">Pick up to 30 photos and we\\'ll upload them. When they\\'re done, tap <b>Add photos</b> again for the next batch \u2014 as many batches as you like.</div>';\n      const btn = document.createElement(\"button\");\n      btn.type = \"button\"; btn.textContent = \"Got it\";\n      btn.style.cssText = \"margin-top:18px;background:#4a90e2;color:#fff;border:none;border-radius:10px;padding:12px 26px;font-size:15px;font-weight:800;cursor:pointer;width:100%;\";\n      const close = () => { try { sessionStorage.setItem(\"dmb_limit_notice_seen\", \"1\"); } catch (e) {} try { ov.remove(); } catch (e) {} };\n      btn.addEventListener(\"click\", close);\n      card.appendChild(btn);\n      ov.appendChild(card);\n      document.body.appendChild(ov);\n    } catch (e) {}\n  }\n\n  // Wait until this batch's photos have finished streaming to R2 (instant backup\n  // queue drained), so we only let the customer add more once memory is clear.\n  // Time-boxed so a stalled network can never trap them behind the modal.\n  async function _dmbWaitForBatchUpload(maxMs) {\n    maxMs = maxMs || 90000;\n    const start = Date.now();\n    while (Date.now() - start < maxMs) {\n      let pending = 0;\n      try { pending = _dmbInstantBackup.pending(); } catch (e) { pending = 0; }\n      try {\n        const done = _dmbInstantBackup._done || 0, total = _dmbInstantBackup._total || 0;\n        if (total > 0) _dmbSetUploadModalMsg(\"Uploading your pictures\u2026 \" + Math.min(done, total) + \" / \" + total);\n      } catch (e) {}\n      if (!pending) break;\n      await new Promise((r) => setTimeout(r, 350));\n    }\n  }\n\n  document.addEventListener(\"change\", async (e) => {\n    const fileInput = e.target.closest(\"#bookPhotos, #step1bFileInput\");\n    const cameraInput = e.target.closest(\"#cameraPhotos, #step1bCameraInput\");\n\n    // The batch limit + blocking upload modal are ONLY for phones (the memory-\n    // constrained path). On a laptop there's no cap and no blocking popup.\n    const _mob = _dmbIsMobileUpload();\n\n    if (fileInput) {\n      // PRINT-MASTER phone path (flag-gated): no user-facing cap. The whole pick is\n      // processed in internal sub-batches of _DMB_MASTER_SUBBATCH so only a handful\n      // of photos are ever decoded / resident at once \u2014 each sub-batch is derived\n      // (master+proxy), merged, streamed to R2, then its distant full-res released\n      // before the next sub-batch starts. Same one blocking modal for the whole pick.\n      if (_mob && _dmbPrintMasterOn()) {\n        const all = Array.from(fileInput.files || []);\n        if (!all.length) return;\n        _dmbShowUploadModal(\"Uploading your pictures\u2026\");\n        try {\n          for (let i = 0; i < all.length; i += _DMB_MASTER_SUBBATCH) {\n            const chunk = all.slice(i, i + _DMB_MASTER_SUBBATCH);\n            const picked = await compressFilesForMobile(chunk);   // flag branch \u2192 derivatives\n            bookFiles = mergeFilesKeepOld(bookFiles, picked);\n            validatePhotos();\n            renderUploadGrid();\n            refreshBuilderPhotos();\n            try { _dmbCloud.queueMasters(editor.photos || []); } catch (e) {}   // signed-in: masters start now\n            await _dmbWaitForBatchUpload();          // drain THIS sub-batch's proxies to R2\n            // Proactively release full-res of everything R2-confirmed and off-spread,\n            // so residency stays ~one sub-batch deep no matter how many were picked.\n            try { _dmbLastReleaseSpread = -999; _dmbMaybeReleaseForSpread(); } catch (e2) {}\n          }\n          try { syncInputFiles(fileInput, bookFiles); } catch (e2) {}\n          await tryAutoOpenEditor();\n        } finally {\n          _dmbHideUploadModal(); _dmbShowAddMoreCallout();\n        }\n        return;\n      }\n\n      // Phone: cap to the batch limit AND immediately trim the input down to those\n      // files, so any extra originals the OS picker returned (e.g. 45 selected \u2192 15\n      // extra, up to ~10 MB each) are released BEFORE we decode anything.\n      const kept = _dmbCapBatch(fileInput.files);   // laptop \u2192 returns everything\n      if (_mob) { try { syncInputFiles(fileInput, kept); } catch (e2) {} _dmbShowUploadModal(\"Uploading your pictures\u2026\"); }\n      try {\n        const picked = await compressFilesForMobile(kept);\n        bookFiles = mergeFilesKeepOld(bookFiles, picked);\n        syncInputFiles(fileInput, bookFiles);\n        validatePhotos();\n        renderUploadGrid();\n        refreshBuilderPhotos();\n        if (_mob) await _dmbWaitForBatchUpload();   // hold the modal until this batch is safely on R2\n        await tryAutoOpenEditor();\n      } finally {\n        if (_mob) { _dmbHideUploadModal(); _dmbShowAddMoreCallout(); }\n      }\n      return;\n    }\n\n    if (cameraInput) {\n      const kept = _dmbCapBatch(cameraInput.files);\n      if (_mob) _dmbShowUploadModal(\"Uploading your picture\u2026\");\n      try {\n        const picked = await compressFilesForMobile(kept);\n        bookFiles = mergeFilesKeepOld(bookFiles, picked);\n        validatePhotos();\n        renderUploadGrid();\n        refreshBuilderPhotos();\n        if (_mob) await _dmbWaitForBatchUpload();\n        await tryAutoOpenEditor();\n      } finally {\n        if (_mob) { _dmbHideUploadModal(); _dmbShowAddMoreCallout(); }\n      }\n    }\n  });\n\n\n  // ---- Drag & Drop zone in step 1b (Upload Your Photos page) ----\n  const step1bDragZone = document.getElementById(\"step1bDragDropZone\");\n  if (step1bDragZone) {\n    step1bDragZone.addEventListener(\"dragover\", (e) => {\n      e.preventDefault();\n      step1bDragZone.classList.add(\"drag-over\");\n    });\n    step1bDragZone.addEventListener(\"dragleave\", () => {\n      step1bDragZone.classList.remove(\"drag-over\");\n    });\n    step1bDragZone.addEventListener(\"drop\", async (e) => {\n      e.preventDefault();\n      step1bDragZone.classList.remove(\"drag-over\");\n      let files = _dmbCapBatch(Array.from(e.dataTransfer?.files || []).filter(f => f.type.startsWith(\"image/\")));\n      if (!files.length) return;\n      // Print-master flag: dropped files get master+proxy derivatives too, so a\n      // master exists for every photo no matter which entry path it used.\n      if (_dmbPrintMasterOn()) { try { files = await compressFilesForMobile(files); } catch (e2) {} }\n      bookFiles = mergeFilesKeepOld(bookFiles, files);\n      validatePhotos();\n      renderUploadGrid();\n      refreshBuilderPhotos();\n      _dmbShowAddMoreCallout();\n    });\n    step1bDragZone.addEventListener(\"click\", () => {\n      const input = document.getElementById(\"step1bFileInput\");\n      if (input) input.click();\n    });\n  }\n\n  // ---- Drag & Drop zone inside \"Add more photos\" popup (builder) ----\n  const builderDragDropZone = document.getElementById(\"builderDragDropZone\");\n  if (builderDragDropZone) {\n    builderDragDropZone.addEventListener(\"dragover\", (e) => {\n      e.preventDefault();\n      builderDragDropZone.classList.add(\"drag-over\");\n    });\n    builderDragDropZone.addEventListener(\"dragleave\", () => {\n      builderDragDropZone.classList.remove(\"drag-over\");\n    });\n    builderDragDropZone.addEventListener(\"drop\", async (e) => {\n      e.preventDefault();\n      builderDragDropZone.classList.remove(\"drag-over\");\n      const menu = document.getElementById(\"photoImportMenu\");\n      if (menu) menu.style.display = \"none\";\n      let files = _dmbCapBatch(Array.from(e.dataTransfer?.files || []).filter(f => f.type.startsWith(\"image/\")));\n      if (!files.length) return;\n      // Print-master flag: dropped files get master+proxy derivatives too (see step1b zone).\n      if (_dmbPrintMasterOn()) { try { files = await compressFilesForMobile(files); } catch (e2) {} }\n      bookFiles = mergeFilesKeepOld(bookFiles, files);\n      // Keep the counter and the tray in step. The phone-relay path went the\n      // other way round (counter only) and photos silently never appeared.\n      try { validatePhotos(); } catch (e2) {}\n      refreshBuilderPhotos();\n      _dmbShowAddMoreCallout();\n    });\n  }\n\n  // ---- Scroll arrow for step 1b: show when footer scrolls out of view ----\n  // Show/hide the \"\u2193 Select Pages & Continue\" cue. Looks up elements live so it\n  // can be called the instant photos are uploaded (renderUploadGrid) \u2014 not only\n  // when the IntersectionObserver happens to fire, which is what made it lag.\n  function _dmbUpdateScrollArrow() {\n    const btn = document.getElementById(\"dmbScrollArrowBtn\");\n    const footer = document.getElementById(\"step1bFooter\");\n    const step1bEl = document.getElementById(\"step1b\");\n    if (!btn || !footer) return;\n    const isStep1bVisible = step1bEl && step1bEl.style.display !== \"none\";\n    const hasPhotos = bookFiles.length > 0;\n    const r = footer.getBoundingClientRect();\n    const footerVisible = r.top < window.innerHeight && r.bottom > 0;\n    btn.style.display = (isStep1bVisible && hasPhotos && !footerVisible) ? \"flex\" : \"none\";\n  }\n\n  const step1bFooter = document.getElementById(\"step1bFooter\");\n  const dmbScrollArrowBtn = document.getElementById(\"dmbScrollArrowBtn\");\n\n  if (step1bFooter && dmbScrollArrowBtn) {\n    dmbScrollArrowBtn.addEventListener(\"click\", () => {\n      step1bFooter.scrollIntoView({ behavior: \"smooth\", block: \"center\" });\n    });\n    new IntersectionObserver(_dmbUpdateScrollArrow, { threshold: 0.5 }).observe(step1bFooter);\n    window.addEventListener(\"scroll\", _dmbUpdateScrollArrow, { passive: true });\n  }\n\n  window.__dmbDebug = {\n    getState: () => ({\n      pagesValue: pagesEl?.value,\n      totalPages: editor.totalPages,\n      totalSpreads: editor.totalSpreads,\n      currentSpread: editor.currentSpread,\n      builderMode,\n      photos: editor.photos?.length || 0,\n      step1: step1?.style.display,\n      step2: builderStep?.style.display\n    })\n  };\n  try { initPhoneUpload(); } catch (_) {}\n\n  document.addEventListener(\"click\", async (e) => {\n    const addBtn = e.target.closest(\"#addMorePhotosBtn\");\n    const deviceBtn = e.target.closest(\"#importDeviceBtn\");\n    const cameraBtn = e.target.closest(\"#importCameraBtn\");\n    const projectBtn = e.target.closest(\"#projectMenuBtn\");\n    const historyBtn = e.target.closest(\"#historyBtn\");\n\n    const menu = $(\"photoImportMenu\");\n    const bookInput = $(\"bookPhotos\");\n    const cameraInput = $(\"cameraPhotos\");\n\n    if (addBtn) {\n      e.preventDefault();\n      e.stopPropagation();\n      if (menu) {\n        if (menu.style.display === \"block\") {\n          menu.style.display = \"none\";\n        } else {\n          const rect = addBtn.getBoundingClientRect();\n          menu.style.top = (rect.bottom + 4) + \"px\";\n          menu.style.left = rect.left + \"px\";\n          menu.style.width = rect.width + \"px\";\n          menu.style.display = \"block\";\n        }\n      }\n      return;\n    }\n\n    if (deviceBtn) {\n      e.preventDefault();\n      e.stopPropagation();\n      if (menu) menu.style.display = \"none\";\n      if (bookInput) bookInput.click();\n      return;\n    }\n\n    if (cameraBtn) {\n      e.preventDefault();\n      e.stopPropagation();\n      if (menu) menu.style.display = \"none\";\n      if (cameraInput) cameraInput.click();\n      return;\n    }\n\n    if (projectBtn) {\n      e.preventDefault();\n      e.stopPropagation();\n      openProjectMenuPrompt();\n      return;\n    }\n\n    if (historyBtn) {\n      e.preventDefault();\n      e.stopPropagation();\n      openHistoryPanel();\n      return;\n    }\n    if (menu && !e.target.closest(\"#photoImportMenu\")) {\n      menu.style.display = \"none\";\n    }\n  }, true);\ndocument.addEventListener(\"click\", (e) => {\n  const mirror = e.target.closest(\".dmb-tool-mirror\");\n  if (!mirror) return;\n\n  lastToolbarSide = \"right\";\n  const targetSelector = mirror.getAttribute(\"data-click\");\n  const realBtn = targetSelector ? document.querySelector(targetSelector) : null;\n  if (realBtn) realBtn.click();\n  lastToolbarSide = \"left\";\n});\n\ndocument.addEventListener(\"click\", (e) => {\n  const inFrame = e.target.closest(\".dmb-photo-frame-object\");\n  const inFrameToolbar = e.target.closest(\"#dmbFrameToolbar\");\n  if (!inFrame && !inFrameToolbar) {\n    hideFrameToolbar();\n  }\n});\n\n// ======= UPLOAD STEP =======\n// Upload-grid thumbnails through a SERIAL queue of small (320px) thumbs \u2014 the\n// grid used to hand the <img> the full-resolution file, so a phone decoded\n// every full-size photo at once just to paint 70px squares. That decode spike\n// is the main \"tab crashed and reloaded\" trigger during upload. One decode at\n// a time, cached per file, keeps memory flat.\nconst _dmbUpThumbCache = new Map();\nconst _dmbUpThumbQ = [];\nlet _dmbUpThumbBusy = false;\nfunction _dmbUploadThumb(file, img) {\n  const hit = _dmbUpThumbCache.get(file);\n  if (hit) { img.src = hit; return; }\n  _dmbUpThumbQ.push([file, img]);\n  if (_dmbUpThumbBusy) return;\n  _dmbUpThumbBusy = true;\n  (async () => {\n    while (_dmbUpThumbQ.length) {\n      const [f, el] = _dmbUpThumbQ.shift();\n      let url = _dmbUpThumbCache.get(f);\n      if (!url) {\n        // HARD TIMEOUT: a file the browser can't decode (e.g. HEIC on Windows)\n        // must never hang the queue \u2014 that would leave every later photo's\n        // thumbnail blank and look like the photos \"never arrived\".\n        try {\n          const b = await Promise.race([\n            _dmbMakeThumbBlob(f, 320),\n            new Promise((r) => setTimeout(() => r(null), 4000)),\n          ]);\n          if (b) url = URL.createObjectURL(b);\n        } catch (e) {}\n        if (!url) { try { url = URL.createObjectURL(f); } catch (e) {} }   // fallback: original\n        if (url) _dmbUpThumbCache.set(f, url);\n      }\n      if (url) el.src = url;\n      await new Promise((r) => setTimeout(r, 25));   // yield between decodes\n    }\n    _dmbUpThumbBusy = false;\n  })();\n}\n\nfunction renderUploadGrid() {\n  const grid = document.getElementById(\"uploadPhotoGrid\");\n  const countEl = document.getElementById(\"uploadPhotoCount\");\n  if (!grid) return;\n\n  grid.innerHTML = \"\";\n  const count = bookFiles.length;\n\n  if (countEl) {\n    countEl.style.display = count ? \"block\" : \"none\";\n    countEl.textContent = count ? `${count} photo${count !== 1 ? \"s\" : \"\"} selected` : \"\";\n  }\n\n  // Update the \"\u2193 Select Pages & Continue\" cue right away when photos change.\n  if (typeof _dmbUpdateScrollArrow === \"function\") _dmbUpdateScrollArrow();\n\n  // When photos are newly added, briefly flash a slim black \"belt\" around the size\n  // selector for 600ms to nudge the customer to pick a size. Debounced: phone\n  // uploads drip photos in one at a time (a renderUploadGrid call per arrival), and\n  // without this guard the belt would re-flash on every single photo, looking like a\n  // persistent flickering black box. One flash per burst is all the nudge needs.\n  if (count > (renderUploadGrid._prevCount || 0)) {\n    const _now = Date.now();\n    if (_now - (renderUploadGrid._lastFlash || 0) > 4000) {\n      renderUploadGrid._lastFlash = _now;\n      const _sizeSel = document.getElementById(\"step1bPages\");\n      if (_sizeSel) {\n        _sizeSel.classList.add(\"dmb-belt-flash\");\n        setTimeout(() => { _sizeSel.classList.remove(\"dmb-belt-flash\"); }, 600);\n      }\n    }\n  }\n  renderUploadGrid._prevCount = count;\n\n  bookFiles.forEach((file, idx) => {\n    const thumb = document.createElement(\"div\");\n    thumb.className = \"dmb-upload-grid-thumb\";\n\n    const img = document.createElement(\"img\");\n    img.alt = \"\";\n    img.decoding = \"async\";\n    thumb.appendChild(img);\n    _dmbUploadThumb(file, img);   // serial small-thumb queue (memory-safe)\n\n    const removeBtn = document.createElement(\"button\");\n    removeBtn.type = \"button\";\n    removeBtn.className = \"dmb-upload-grid-thumb-remove\";\n    removeBtn.setAttribute(\"aria-label\", \"Remove photo\");\n    removeBtn.innerHTML = `<svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\" aria-hidden=\"true\"><path d=\"M6 6l12 12M18 6l-12 12\"/></svg>`;\n    removeBtn.addEventListener(\"click\", () => {\n      const _rf = bookFiles[idx];\n      bookFiles.splice(idx, 1);\n      // Deleted photo shouldn't come back in a crash-restore.\n      try { if (_rf && _rf._dmbPid) _dmbInstantBackup.removeFromManifest(_rf._dmbPid); } catch (e) {}\n      renderUploadGrid();\n    });\n    thumb.appendChild(removeBtn);\n\n    grid.appendChild(thumb);\n  });\n}\n\ndocument.addEventListener(\"click\", async (e) => {\n  if (e.target.closest(\"#backToHomeBtn\")) {\n    const s1b = document.getElementById(\"step1b\");\n    if (s1b) s1b.style.display = \"none\";\n    if (step1) step1.style.display = \"flex\";\n    return;\n  }\n\n  if (e.target.closest(\".dmb-mag-template-btn\")) {\n    const btn = e.target.closest(\".dmb-mag-template-btn\");\n    const handle = btn.dataset.handle;\n\n    // Check if there's a saved design for this template\n    const _savedKey = handle ? \"dmb_progress_mag_\" + handle : null;\n    const _hasSave  = _savedKey ? !!localStorage.getItem(_savedKey) : false;\n\n    if (_hasSave) {\n      // Ask: continue or start fresh\n      showChoiceDialog(\n        \"You have a saved design\",\n        \"We found a saved version of this magazine. Would you like to pick up where you left off?\",\n        [\n          { id: \"continue\", label: \"\uD83D\uDCC2 Continue editing\" },\n          { id: \"new\",      label: \"\uD83C\uDD95 Start fresh\" }\n        ]\n      ).then(choice => {\n        if (!choice || choice === \"new\") {\n          // Clear saved progress and go to upload\n          try { localStorage.removeItem(_savedKey); } catch(er) {}\n          _clearPhotosFromIDB(_savedKey).catch(() => {});\n          if (handle) sessionStorage.setItem(\"dmb_mag_template\", handle);\n          window.location.href = handle\n            ? \"/pages/design-my-magazine?step=upload&cover=\" + encodeURIComponent(handle)\n            : \"/pages/design-my-magazine?step=upload\";\n        } else {\n          // Restore directly \u2014 skip upload\n          if (handle) sessionStorage.setItem(\"dmb_mag_template\", handle);\n          window.location.href = handle\n            ? \"/pages/design-my-magazine?template=\" + encodeURIComponent(handle) + \"&restore=1\"\n            : \"/pages/design-my-magazine?restore=1\";\n        }\n      });\n      return;\n    }\n\n    // No saved design \u2014 go straight to upload as before\n    if (handle) sessionStorage.setItem(\"dmb_mag_template\", handle);\n    window.location.href = handle\n      ? \"/pages/design-my-magazine?step=upload&cover=\" + encodeURIComponent(handle)\n      : \"/pages/design-my-magazine?step=upload\";\n    return;\n  }\n\n  if (e.target.closest(\"#continueToBuilderBtn\")) {\n    // In magazine mode the IIFE handler already manages the flow\n    if (window.DMB_MAGAZINE_MODE) return;\n\n    const errEl = document.getElementById(\"uploadStepError\");\n    const pagesSel = document.getElementById(\"step1bPages\");\n\n    if (!pagesSel?.value) {\n      if (errEl) { errEl.textContent = \"Please select the number of pages for your book.\"; errEl.style.display = \"block\"; }\n      return;\n    }\n    if (errEl) errEl.style.display = \"none\";\n\n    if (pagesEl) pagesEl.value = pagesSel.value;\n\n    const s1b = document.getElementById(\"step1b\");\n    if (s1b) s1b.style.display = \"none\";\n    if (step1) step1.style.display = \"none\";\n    if (builderStep) builderStep.style.display = \"block\";\n\n    try {\n      await openEditor();\n    } catch (err) {\n      console.error(\"openEditor failed:\", err);\n      if (s1b) s1b.style.display = \"flex\";\n      if (builderStep) builderStep.style.display = \"none\";\n      if (errEl) { errEl.textContent = \"Something went wrong opening the builder. Check the console for details.\"; errEl.style.display = \"block\"; }\n    }\n    return;\n  }\n});\n\n  /* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   * STEP 2 \u2014 SERVER RENDER MODE (window.DMB_RENDER_MODE)\n   * Reconstructs a design from a job token and drives the SAME canvas PDF engine\n   * customers use (buildPdfBlobOnePagePerBookPage via __dmbBuildPdfStreaming), fed\n   * from the 3500px print masters. No UI, no storage, no cloud writes, no history.\n   * Everything here is inert unless the render snippet armed the flag.\n   * \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n  function _dmbRenderSignal(phase, done, total) {\n    try {\n      window.__DMB_RENDER_PROGRESS = { phase: phase, done: done || 0, total: total || 0,\n        staged: _DMB_RM.staged, part: _DMB_RM.part, ts: Date.now() };\n    } catch (e) {}\n  }\n  // Liveness beat: the cover spread can run for minutes without emitting progress.\n  // A moving ts with frozen done/staged = \"alive but slow\"; a frozen ts = main thread\n  // gone. The renderer distinguishes the two.\n  function _dmbRenderBeat(on) {\n    try { if (_DMB_RM.hb) { clearInterval(_DMB_RM.hb); _DMB_RM.hb = null; } } catch (e) {}\n    if (!on) return;\n    _DMB_RM.hb = setInterval(function () {\n      try {\n        var p = window.__DMB_RENDER_PROGRESS;\n        if (p) { p.ts = Date.now(); window.__DMB_RENDER_PROGRESS = p; }\n      } catch (e) {}\n    }, 2000);\n  }\n  async function _dmbRenderPost(path, body) {\n    try {\n      var r = await fetch(_DMB_RM.proxy + path + \"?t=\" + encodeURIComponent(_DMB_RM.token), {\n        method: \"POST\", headers: { \"content-type\": \"application/json\" },\n        credentials: \"same-origin\", body: JSON.stringify(body || {}),\n      });\n      return r.ok ? (await r.json().catch(function () { return {}; })) : null;\n    } catch (e) { return null; }\n  }\n  async function _dmbRenderSha256(blob) {\n    try {\n      var buf = await blob.arrayBuffer();\n      var h = await crypto.subtle.digest(\"SHA-256\", buf);\n      return { sha256: Array.from(new Uint8Array(h)).map(function (x) { return x.toString(16).padStart(2, \"0\"); }).join(\"\"),\n               bytes: buf.byteLength };\n    } catch (e) { return { sha256: \"\", bytes: 0 }; }\n  }\n\n  /* \u2500\u2500 Fonts: never render silently in a fallback face \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   * The engine force-loads a fixed family list inside Promise.allSettled + try/catch,\n   * so a 404 renders the whole book in sans-serif and reports success. Render mode\n   * verifies every family the DESIGN actually uses and FAILS the job instead \u2014 a book\n   * printed in the wrong font is worse than a failed job, and a failed job falls back\n   * to the client pipeline, which has the fonts. */\n  var _DMB_RM_WEBFONTS = [\"Londrina Solid\",\"Aurelia\",\"Burbank Big Condensed\",\"White Garden Wide\",\n    \"Vogue Moderne\",\"GFS Didot\",\"Prata\",\"Theano Didot\",\"Lindys Diner\",\"Chomsky\",\"Anton\",\"70s Disco\",\n    \"Geraldine\",\"Lemon Drops\",\"Mouldy Cheese\",\"Nature Beauty\",\"Super Maples\",\"The Heart of Everything\",\n    \"Tomatoes\",\"Wedding Day\"];   // must match the @font-face list in design-my-book.css\n  function _dmbRenderFamilyOf(v) {\n    return String(v || \"\").split(\",\")[0].trim().replace(/^['\"]|['\"]$/g, \"\");\n  }\n  function _dmbRenderCollectFaces() {\n    var out = {};\n    function add(fam, w) {\n      var f = _dmbRenderFamilyOf(fam) || \"Londrina Solid\";\n      var wt = String(parseInt(w, 10) || 800);\n      out[f + \"|\" + wt] = { family: f, weight: wt };\n      out[f + \"|800\"]   = { family: f, weight: \"800\" };   // the engine force-loads 800 too\n    }\n    try { Object.keys(coverState.textStyles || {}).forEach(function (k) {\n      var s = coverState.textStyles[k] || {}; add(s.fontFamily, s.fontWeight);\n    }); } catch (e) {}\n    try { (editor.coverObjects || []).forEach(function (o) {\n      if (o && o.type === \"text\") add(o.fontFamily, o.fontWeight);\n    }); } catch (e) {}\n    try { Object.keys(editor.pageObjects || {}).forEach(function (p) {\n      (editor.pageObjects[p] || []).forEach(function (o) {\n        if (o && o.type === \"text\") add(o.fontFamily, o.fontWeight);\n      });\n    }); } catch (e) {}\n    add(\"Londrina Solid\", 800);\n    return Object.keys(out).map(function (k) { return out[k]; });\n  }\n  // Metric probe: a family that resolved to the last-resort fallback measures exactly\n  // like a guaranteed-missing family. Works for system fonts, which fonts.check cannot\n  // prove (they have no @font-face).\n  function _dmbRenderFontAvailable(familyQuoted, weight) {\n    try {\n      var c = document.createElement(\"canvas\").getContext(\"2d\");\n      var S = \"MNwi@1lIO0Wm-gj\";\n      c.font = weight + \" 72px 'dmb__missing__'\";\n      var base = c.measureText(S).width;\n      c.font = weight + \" 72px \" + familyQuoted + \", 'dmb__missing__'\";\n      return Math.abs(c.measureText(S).width - base) > 0.5;\n    } catch (e) { return true; }\n  }\n  async function _dmbRenderFontGate() {\n    var faces = _dmbRenderCollectFaces();\n    try { await Promise.race([document.fonts.ready, new Promise(function (r) { setTimeout(r, 20000); })]); } catch (e) {}\n    for (var i = 0; i < faces.length; i++) {\n      var f = faces[i], q = '\"' + f.family + '\"', spec = f.weight + \" 40px \" + q;\n      if (_DMB_RM_WEBFONTS.indexOf(f.family) >= 0) {\n        try {\n          await Promise.race([document.fonts.load(spec),\n            new Promise(function (_, rej) { setTimeout(function () { rej(new Error(\"font timeout\")); }, 15000); })]);\n        } catch (e) {}\n      }\n      var ok = false;\n      try { ok = document.fonts.check(spec) || document.fonts.check(\"40px \" + q); } catch (e) {}\n      if (!ok) ok = _dmbRenderFontAvailable(q, f.weight);\n      var label = f.weight + \" \" + f.family;\n      if (ok) { _DMB_RM.fonts.loaded.push(label); }\n      else if (_DMB_RM_WEBFONTS.indexOf(f.family) >= 0) { _DMB_RM.fonts.missing.push(label); }\n      else { _DMB_RM.fonts.system.push(label); }   // container substitute; warn, don't fail\n    }\n    try { await document.fonts.ready; } catch (e) {}\n    if (_DMB_RM.fonts.missing.length && window.DMB_RENDER_STRICT_FONTS !== false) {\n      throw new Error(\"fonts_missing: \" + _DMB_RM.fonts.missing.join(\", \"));\n    }\n  }\n\n  /* \u2500\u2500 Derived crops: re-bake from the SOURCE master + editor.cropRects \u2500\u2500\u2500\u2500\u2500\u2500\n   * cropRects ratios are of the ORIENTED image the crop modal displayed (an <img>,\n   * so EXIF was applied) \u2014 decode with imageOrientation:\"from-image\" or an\n   * EXIF-rotated original crops the wrong region. Same drawImage + toBlob(0.92)\n   * recipe as applyCrop. */\n  async function _dmbRenderCropDerivedImg(ph) {\n    var rect = null;\n    try { rect = (editor.cropRects || {})[ph.id]; } catch (e) {}\n    var src = (rect && rect.srcPid) || _dmbCropSourceId(ph.id);\n    if (!rect || !src || !(rect.w > 0) || !(rect.h > 0)) { _dmbRenderNote(\"crop_fallback\", ph.id); return null; }\n    var blob = await _dmbFetchPhotoBlob(_DMB_RM.job, src, \"master\");\n    if (!blob || !blob.size) blob = await _dmbFetchPhotoBlob(_DMB_RM.job, src);\n    if (!blob || !blob.size) { _dmbRenderNote(\"crop_fallback\", ph.id); return null; }\n    var bmp = await createImageBitmap(blob, { imageOrientation: \"from-image\" });\n    var sw = Math.max(1, Math.round(rect.w * bmp.width));\n    var sh = Math.max(1, Math.round(rect.h * bmp.height));\n    var c = document.createElement(\"canvas\");\n    c.width = sw; c.height = sh;\n    var cx = c.getContext(\"2d\");\n    cx.imageSmoothingEnabled = true; cx.imageSmoothingQuality = \"high\";\n    cx.drawImage(bmp, Math.round(rect.x * bmp.width), Math.round(rect.y * bmp.height), sw, sh, 0, 0, sw, sh);\n    try { bmp.close(); } catch (e) {}\n    var out = await new Promise(function (res) { c.toBlob(res, \"image/jpeg\", 0.92); });\n    try { c.width = 0; c.height = 0; } catch (e) {}\n    if (!out) { _dmbRenderNote(\"crop_fallback\", ph.id); return null; }\n    var u = URL.createObjectURL(out), img = new Image();\n    img.decoding = \"async\";\n    await new Promise(function (res, rej) {\n      img.onload  = function () { res(); };\n      img.onerror = function () { rej(new Error(\"crop decode failed\")); };\n      img.src = u;\n    });\n    try { URL.revokeObjectURL(u); } catch (e) {}\n    _dmbRenderNote(\"crop_rebake\");\n    return img;\n  }\n\n  /* \u2500\u2500 State helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n  function _dmbRenderEnsurePageOption(n) {\n    if (!n) return;\n    [\"pages\", \"step1bPages\"].forEach(function (id) {\n      var el = document.getElementById(id);\n      if (!el) return;\n      var has = false;\n      for (var i = 0; i < el.options.length; i++) if (Number(el.options[i].value) === Number(n)) has = true;\n      if (!has) {   // page count no longer a product variant \u2192 openEditor bails SILENTLY at 21919\n        var o = document.createElement(\"option\");\n        o.value = String(n); o.textContent = String(n);\n        el.appendChild(o);\n      }\n      el.value = String(n);\n    });\n  }\n  // LAZY photo records \u2014 ids + meta only. Bytes stay on R2; _dmbRenderImg streams each\n  // master on demand and the engine frees it per page, so a 300-photo book never bulk-\n  // downloads anything. Shape copied from the builder's own cloud-reconcile branch.\n  function _dmbRenderBuildLazyPhotos(meta) {\n    var man = (meta && Array.isArray(meta.photos) && meta.photos.length) ? meta.photos\n      : ((meta && Array.isArray(meta.photoIds)) ? meta.photoIds.map(function (id) { return { id: id }; }) : []);\n    var byId = {}, order = [];\n    man.forEach(function (m) { if (m && m.id && !byId[m.id]) { byId[m.id] = m; order.push(m.id); } });\n    // A referenced id with NO record makes getPhotoById return null and the placement\n    // is dropped silently \u2014 never let that happen.\n    try {\n      _dmbCollectReferencedPhotoIds().forEach(function (id) {\n        if (id && !byId[id]) { byId[id] = { id: id }; order.push(id); }\n      });\n    } catch (e) {}\n    var out = order.map(function (id, i) {\n      var m = byId[id] || {};\n      return { id: id, file: null, url: \"\", _r2: true,\n        ratio: (typeof m.ratio === \"number\" && m.ratio > 0) ? m.ratio : 1,\n        filename: m.filename || \"photo.jpg\", dateTaken: m.dateTaken || \"\",\n        tags: Array.isArray(m.tags) ? m.tags : [],\n        addedIndex: (typeof m.addedIndex === \"number\") ? m.addedIndex : i };\n    });\n    out.sort(function (a, b) { return (a.addedIndex || 0) - (b.addedIndex || 0); });\n    editor.photos = out;\n    try { bookFiles = out.map(function (p) { return new File([], p.filename || \"photo\"); }); } catch (e) {}\n    return out.length;\n  }\n\n  /* \u2500\u2500 Boot \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n  async function _dmbRenderModeBoot() {\n    // Called during module evaluation: yield ONE macrotask so every later declaration\n    // (and window.__dmbBuildPdfStreaming) exists before this body runs.\n    await new Promise(function (r) { setTimeout(r, 0); });\n    var t0 = Date.now();\n    _dmbRenderBeat(true);\n    try {\n      var q = new URLSearchParams(location.search);\n      _DMB_RM.token = (q.get(\"t\") || \"\").trim();\n      _DMB_RM.proxy = window.DMB_DESIGNS_PROXY || \"/apps/designs\";\n      if (!/^[a-f0-9]{32}$/.test(_DMB_RM.token)) throw new Error(\"bad render token\");\n      window.__DMB_RENDER_TOKEN = _DMB_RM.token;\n      _DMB_RM.hash = q.get(\"hash\") === \"1\";\n      window.__DMB_RENDER_LIVETEXT = q.get(\"livetext\") === \"1\";\n      _DMB_RM.part  = Math.max(0, Number(q.get(\"part\")  || 0) || 0);\n      _DMB_RM.parts = Math.max(1, Number(q.get(\"parts\") || 1) || 1);\n      var rg = String(q.get(\"pages\") || \"\").match(/^(\\d{1,4})-(\\d{1,4})$/);\n      if (rg) { _DMB_RM.from = Number(rg[1]); _DMB_RM.to = Number(rg[2]); }\n\n      /* 1 \u2500\u2500 design + photo meta + job params, TOKEN ONLY */\n      _dmbRenderSignal(\"load\", 0, 0);\n      // Three separate reads on purpose: /render-design streams the RAW design body\n      // through (the R2 copy keeps sticker crop base64 INLINE, so it can be tens of MB\n      // and must never be parsed + re-stringified inside the Worker isolate), while\n      // meta and the job facts are small.\n      var _rq = function (p) {\n        return fetch(_DMB_RM.proxy + p + \"?t=\" + encodeURIComponent(_DMB_RM.token),\n          { credentials: \"same-origin\", cache: \"no-store\" });\n      };\n      var rD = await _rq(\"/render-design\");\n      if (!rD.ok) throw new Error(\"render-design HTTP \" + rD.status);\n      var saved = await rD.json();\n      var rI = await _rq(\"/render-info\");\n      if (!rI.ok) throw new Error(\"render-info HTTP \" + rI.status);\n      var jb = await rI.json();\n      var meta = {};\n      try { var rM = await _rq(\"/render-meta\"); if (rM.ok) meta = await rM.json(); } catch (e) {}\n      if (!saved || !saved.editor) throw new Error(\"design has no editor state\");\n      _DMB_RM.job = String(jb.job || meta.id || \"\");\n      if (!_DMB_RM.job) throw new Error(\"no design id\");\n      window.__DMB_RENDER_JOB = _DMB_RM.job;      // getCustomerSaveKey() reads this\n      _DMB_RM.crops = jb.crops === \"rebake\" ? \"rebake\" : \"prefer-r2\";\n      // A magazine rendered on the book page silently produces the WRONG page order\n      // and cover size \u2014 fail loudly instead.\n      if (!!jb.magazine !== !!window.DMB_MAGAZINE_MODE) throw new Error(\"wrong page for this design\");\n      var ex = Array.isArray(jb.exclude) ? jb.exclude.map(Number).filter(function (n) { return isFinite(n) && n >= 1; }) : [];\n      // Incremental render facts (both optional; absent = render everything, positional).\n      _DMB_RM.fps   = (Array.isArray(jb.fps) && jb.fps.length) ? jb.fps.map(String) : null;\n      _DMB_RM.reuse = (Array.isArray(jb.reuse) && jb.reuse.length) ? new Set(jb.reuse.map(Number)) : null;\n\n      /* 2 \u2500\u2500 #pages must hold the BASE count or openEditor() returns silently */\n      var _se = saved.editor || {};\n      var _pg = _se.totalPages || Number(saved.pages_selected) || 0;\n      if (!_pg) throw new Error(\"design has no pages\");\n      var _base = Number(saved.pages_selected) || Math.max(0, _pg - (_se.extraSpreads || 0) * 2) || _pg;\n      _dmbRenderEnsurePageOption(_base);\n      editor.totalPages    = _pg;\n      editor.editablePages = _se.editablePages || Math.max(0, _pg - 1);\n      editor.totalSpreads  = _se.totalSpreads  || Math.max(1, Math.ceil(_pg / 2));\n\n      /* 3 \u2500\u2500 editor UI init (every side effect is render-mode gated) */\n      _dmbRenderSignal(\"state\", 0, 0);\n      await openEditor({ skipStateReset: true });\n\n      /* 4 \u2500\u2500 state: the SAME code the ?restore=1 flow uses */\n      _dmbApplySavedEditorState(saved);\n      var nph = _dmbRenderBuildLazyPhotos(meta);\n      try { _dmbRehydrateCoverPhotoUrls(); } catch (e) {}   // no-op for lazy records (url stays \"\")\n      // Sticker/icon crop base64 is INLINE in the R2 design.json, so no IDB reattach.\n      try { syncCoverInputsFromState(); } catch (e) {}\n\n      /* 5 \u2500\u2500 fonts BEFORE any measuring or cover render */\n      _dmbRenderSignal(\"fonts\", 0, 0);\n      await _dmbRenderFontGate();\n\n      /* 6 \u2500\u2500 cover DOM (z-order + legacy fixed-layout conversion), then settle */\n      _dmbRenderSignal(\"crops\", 0, 0);\n      setCanvasZoom(1);\n      // COVER ONLY. renderBuilderCanvas() would dispatch to renderSpread, which\n      // recomputes totalPages/editablePages/totalSpreads from the #pages DOM value -\n      // and #pages holds the BASE count, so any design with extra spreads would render\n      // SHORT. Interior pages are entirely state-driven and need no DOM.\n      builderMode = \"cover\";\n      try { renderCoverCanvas(); } catch (e) {}\n      // Re-assert the counts in case anything above touched them.\n      editor.totalPages    = _pg;\n      editor.editablePages = _se.editablePages || Math.max(0, _pg - 1);\n      editor.totalSpreads  = _se.totalSpreads  || Math.max(1, Math.ceil(_pg / 2));\n      // NEVER await a paint unguarded. requestAnimationFrame does NOT fire while the\n      // document is hidden or the instance is throttled, so a bare double-rAF await can\n      // hang the whole job forever - verified live: document.hidden true, rAF never\n      // fired, the render sat in phase \"crops\" with a healthy heartbeat. Same rule as\n      // the img.decode() fix: settle on a timeout and carry on. Two frames are a\n      // nice-to-have for layout settling, not a correctness requirement - the page\n      // bytes come from canvas draws, not from anything painted on screen.\n      await Promise.race([\n        new Promise(function (res) { requestAnimationFrame(function () { requestAnimationFrame(res); }); }),\n        new Promise(function (res) { setTimeout(res, 1500); }),\n      ]);\n      try {\n        await Promise.race([\n          document.fonts.ready,\n          new Promise(function (res) { setTimeout(res, 5000); }),\n        ]);\n      } catch (e) {}\n\n      /* 7 \u2500\u2500 render + stage through the EXISTING streaming seam */\n      _DMB_RM.exclude = ex.length ? new Set(ex) : null;\n      // The fingerprint list must line up with the sequence this page is about to\n      // emit. If it does not (client and page disagree on the page count), drop BOTH\n      // fps and reuse and render everything positionally - never reuse by a guess.\n      try {\n        var _expect = (window.DMB_MAGAZINE_MODE ? 5 : 4) + (editor.totalPages || 0) - (_DMB_RM.exclude ? _DMB_RM.exclude.size : 0);\n        if (_DMB_RM.fps && _DMB_RM.fps.length !== _expect) { _DMB_RM.fps = null; _DMB_RM.reuse = null; }\n        if (!_DMB_RM.fps) _DMB_RM.reuse = null;\n      } catch (e) { _DMB_RM.fps = null; _DMB_RM.reuse = null; }\n      var sharded = _DMB_RM.from >= 0;\n      var hashes = [], lastPost = 0;\n      _dmbRenderSignal(\"render\", 0, (window.DMB_MAGAZINE_MODE ? 4 : 3) + 1 + (editor.totalPages || 0));\n      var manifest = await buildPdfBlobOnePagePerBookPage(\n        function (done, total) {\n          _DMB_RM.count = total;\n          _dmbRenderSignal(\"render\", done, total);\n          var now = Date.now();\n          if (now - lastPost > 10000) {\n            lastPost = now;\n            _dmbRenderPost(\"/render-progress\", { phase: \"render\", done: done, total: total, staged: _DMB_RM.staged });\n          }\n        },\n        null, _DMB_RM.exclude,\n        {\n          // Sharding skips the ENCODE, never the numbering: _pageSeq indices are\n          // identical to a single-shard run.\n          skip: sharded ? function (i) { return i < _DMB_RM.from || i > _DMB_RM.to; } : undefined,\n          // Reused pages are not even RENDERED (skip() above only skips the encode).\n          skipRender: _DMB_RM.reuse ? function (i) { return _DMB_RM.reuse.has(i); } : undefined,\n          // Ask addCanvasPage for the small proof copy of each page.\n          wantPreview: true,\n          onPage: async function (pg) {\n            var blob = _dmbDataUrlToBlob(pg.jpeg);\n            if (_DMB_RM.hash) {\n              var h = await _dmbRenderSha256(blob);\n              hashes.push({ index: pg.index, wMm: pg.wMm, hMm: pg.hMm, orient: pg.orient, sha256: h.sha256, bytes: h.bytes });\n            }\n            var _fp = (_DMB_RM.fps && _DMB_RM.fps[pg.index]) || \"\";\n            await _dmbUploadRenderPage(_DMB_RM.proxy, \"\", pg.index, blob, _fp, pg.wMm, pg.hMm);   // /render-stage?t&i[&fp], 8 x 90 s\n            _DMB_RM.staged++;\n            // AFTER the page is safely staged, never before: the printable page is the\n            // thing that matters and nothing about the proof may delay or endanger it.\n            if (pg.preview) {\n              try { await _dmbUploadRenderPreview(_DMB_RM.proxy, pg.index, _dmbDataUrlToBlob(pg.preview), _fp); } catch (e) {}\n            }\n          },\n        }\n      );\n\n      var seq = (manifest && manifest.pages) ? manifest.pages : [];\n      if (!seq.length) throw new Error(\"render produced no pages\");\n      // A silently blank page must never reach the printer.\n      if (_DMB_RM.notes.photo_missing.length)\n        throw new Error(\"photos unavailable: \" + _DMB_RM.notes.photo_missing.slice(0, 8).join(\", \"));\n      if (_DMB_RM.crops === \"prefer-r2\" && _DMB_RM.notes.crop_fallback.length)\n        throw new Error(\"crop_unrecoverable: \" + _DMB_RM.notes.crop_fallback.slice(0, 8).join(\", \"));\n      var mine = seq.filter(function (p) { return !p.skipped; })\n                    .map(function (p) { return { index: p.index, wMm: p.wMm, hMm: p.hMm }; });\n      if (mine.length !== _DMB_RM.staged)\n        throw new Error(\"staged \" + _DMB_RM.staged + \" of \" + mine.length + \" pages\");\n\n      _dmbRenderSignal(\"done\", _DMB_RM.count, _DMB_RM.count);\n      var _donePayload = {\n        part: _DMB_RM.part, parts: _DMB_RM.parts, count: seq.length, pages: mine,\n        masterMisses: _DMB_RM.notes.master_miss, crops: _DMB_RM.notes.crop_rebake,\n        cropFallbacks: _DMB_RM.notes.crop_fallback, fonts: _DMB_RM.fonts,\n      };\n      var doneRes = await _dmbRenderPost(\"/render-done\", _donePayload);\n      if (!doneRes || doneRes.ok !== true) {\n        // One retry, then FAIL. _dmbRenderPost collapses every non-2xx to null, so a\n        // rejected manifest looks exactly like a network blip - and reporting ok:true\n        // here would hang the job with the renderer believing it had succeeded.\n        await new Promise(function (r2) { setTimeout(r2, 2000); });\n        doneRes = await _dmbRenderPost(\"/render-done\", _donePayload);\n        if (!doneRes || doneRes.ok !== true) throw new Error(\"render-done rejected by the worker\");\n      }\n      window.__DMB_RENDER_RESULT = {\n        ok: true, error: null, pages: mine.length, count: seq.length,\n        indexes: mine.map(function (p) { return p.index; }), staged: _DMB_RM.staged, photos: nph,\n        crops: _DMB_RM.notes.crop_rebake, cropFallbacks: _DMB_RM.notes.crop_fallback,\n        masterMisses: _DMB_RM.notes.master_miss, photoMissing: _DMB_RM.notes.photo_missing,\n        fonts: _DMB_RM.fonts, hashes: _DMB_RM.hash ? hashes : undefined,\n        assemble: doneRes || null, ms: Date.now() - t0,\n      };\n    } catch (err) {\n      var msg = String((err && err.message) || err).slice(0, 300);\n      try { await _dmbRenderPost(\"/render-fail\", { error: msg }); } catch (e) {}\n      window.__DMB_RENDER_RESULT = {\n        ok: false, error: msg, pages: 0, count: 0, staged: _DMB_RM.staged,\n        masterMisses: _DMB_RM.notes.master_miss, photoMissing: _DMB_RM.notes.photo_missing,\n        cropFallbacks: _DMB_RM.notes.crop_fallback, fonts: _DMB_RM.fonts, ms: Date.now() - t0,\n      };\n      _dmbRenderSignal(\"error\", 0, 0);\n      try { console.error(\"[DMB render] \" + msg); } catch (e) {}\n    }\n    _dmbRenderBeat(false);\n    window.__DMB_RENDER_DONE = true;   // ALWAYS set \u2014 Puppeteer must never hang\n  }\n\n  /* Parity harness (test-only, inert for customers): hash every page THIS browser\n   * would produce, through the exact same streaming seam.\n   *   const a = await window.__dmbHashPagesLocal(null);   */\n  window.__dmbHashPagesLocal = async function (excludeArr, onProgress) {\n    var ex = (Array.isArray(excludeArr) && excludeArr.length) ? new Set(excludeArr.map(Number)) : null;\n    var out = [];\n    var manifest = await window.__dmbBuildPdfStreaming(async function (pg) {\n      var h = await _dmbRenderSha256(_dmbDataUrlToBlob(pg.jpeg));\n      out.push({ index: pg.index, wMm: pg.wMm, hMm: pg.hMm, orient: pg.orient, sha256: h.sha256, bytes: h.bytes });\n    }, onProgress || null, ex);\n    return { count: out.length, pages: out, seq: (manifest && manifest.pages) || [] };\n  };\n\n\n\n  /* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   * STEP 4 \u2014 SERVER RENDER AT CHECKOUT (window.DMB_SERVER_RENDER)\n   *\n   * Replaces the in-browser render at Add to Cart with the full-quality server\n   * render, and puts a MANDATORY PROOF in front of the customer: they approve the\n   * ACTUAL rendered pages before anything is bound or added to the cart.\n   *\n   * Fails SOFT by design. Any problem - stale design, no masters, renderer down,\n   * customer rejects - aborts the job and returns null, and the caller falls\n   * straight through to today's proven in-browser pipeline. An order is never\n   * blocked by this path.\n   * \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n  const _DMB_SR_TRACE = [];\n  function _dmbSrTrace(stage, extra) {\n    try {\n      _DMB_SR_TRACE.push({ stage: stage, ms: Date.now(), info: extra === undefined ? null : extra });\n      window.__DMB_SR_TRACE = _DMB_SR_TRACE;\n      console.log(\"[DMB sr] \" + stage + (extra !== undefined ? \" \" + JSON.stringify(extra) : \"\"));\n    } catch (e) {}\n  }\n\n  function _dmbServerRenderOn() {\n    try {\n      return window.DMB_SERVER_RENDER === true &&\n             !!window.DMB_DESIGNS_PROXY &&\n             !window.DMB_CARD_MODE &&          // cards have their own PDF path\n             !_dmbRenderMode();                // never from inside a render page\n    } catch (e) { return false; }\n  }\n\n  function _dmbSrIds() {\n    let sid = \"\"; try { sid = _dmbPhotoBackup.guestId() || \"\"; } catch (e) {}\n    let id = \"\"; try { id = getCustomerSaveKey().replace(/^dmb_progress_/, \"\"); } catch (e) {}\n    return { id, sid, qs: \"id=\" + encodeURIComponent(id) + (sid ? \"&sid=\" + encodeURIComponent(sid) : \"\") };\n  }\n\n  /* The server renders from the CLOUD copy, and that copy is normally BEHIND: the\n   * R2 save is debounced and edit-gated, and Add to Cart only saves locally. Push\n   * the current design up and wait for it, or the customer could be sold a book\n   * missing their last few edits. Returns the rev we just wrote. */\n  async function _dmbSrForceCloudSave() {\n    let rev = \"\";\n    try {\n      const snap = getCurrentBuilderStateSnapshot();\n      try { if (snap.editor && Array.isArray(snap.editor.photos)) snap.editor.photos = []; } catch (e) {}\n      rev = (_dmbBuildCloudMeta(snap, editor.photos || []) || {}).rev || \"\";\n    } catch (e) {}\n    try {\n      const r = await _dmbCloudFlushNow(false);\n      if (r && !r.ok) throw new Error(\"cloud save HTTP \" + r.status);\n      if (!r) throw new Error(\"cloud save unavailable\");\n    } catch (e) { throw new Error(\"could not save your design to the cloud: \" + ((e && e.message) || e)); }\n    return rev;\n  }\n\n  /* \u2500\u2500 INCREMENTAL RENDER: per-page content fingerprints \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   * One fingerprint per PDF page, in the exact order the render page emits them\n   * (see buildPdfBlobOnePagePerBookPage). The Worker keeps every staged page under\n   * its fingerprint; at the next Add to Cart, pages whose fingerprint is unchanged\n   * are reused as-is and only the rest are rendered.\n   *\n   * CONSERVATIVE BY CONSTRUCTION. A page's fingerprint covers everything its pixels\n   * can depend on: the global block (orientation, magazine, page count, JPEG quality,\n   * logo/asset base, and the builder BUILD itself - so any deploy invalidates every\n   * cached page), the page's own state, its FACING page's objects (they can overhang\n   * the fold onto this page), the spread photo, and every referenced photo's\n   * identity + crop rect + whether a print master exists for it (master vs proxy\n   * changes the pixels). When in doubt a field is included: a needless re-render\n   * costs seconds; a wrongly reused page would be printed. */\n  function _dmbSrStableStringify(v) {\n    const seen = new Set();\n    const walk = function (x) {\n      if (x === null || typeof x !== \"object\") {\n        if (typeof x === \"string\" && x.slice(0, 5) === \"blob:\") return \"\";   // session-local, never stable\n        if (typeof x === \"function\" || typeof x === \"undefined\") return null;\n        return x;\n      }\n      if (typeof File !== \"undefined\" && x instanceof File) return \"[file]\";\n      if (typeof Blob !== \"undefined\" && x instanceof Blob) return \"[blob]\";\n      if (seen.has(x)) return \"[cycle]\";\n      seen.add(x);\n      let out;\n      if (Array.isArray(x)) out = x.map(walk);\n      else { out = {}; Object.keys(x).sort().forEach(function (k) { out[k] = walk(x[k]); }); }\n      seen.delete(x);\n      return out;\n    };\n    return JSON.stringify(walk(v));\n  }\n  async function _dmbSrHash(str) {\n    try {\n      const buf = new TextEncoder().encode(str);\n      const h = await crypto.subtle.digest(\"SHA-256\", buf);\n      return Array.from(new Uint8Array(h)).map(function (b) { return b.toString(16).padStart(2, \"0\"); }).join(\"\").slice(0, 40);\n    } catch (e) {\n      let h = 0; for (let i = 0; i < str.length; i++) h = (Math.imul(31, h) + str.charCodeAt(i)) | 0;\n      return \"c\" + (h >>> 0).toString(36) + \"-\" + str.length.toString(36);\n    }\n  }\n  async function _dmbSrPageFingerprints(snapshot, excludeSet, masterHave) {\n    const ed = (snapshot && snapshot.editor) || {};\n    const cs = (snapshot && snapshot.coverState) || {};\n    const isMag = !!window.DMB_MAGAZINE_MODE;\n    const N = Number(ed.totalPages) || 0;\n    const ls = ed.orientation === \"landscape\";\n    const pageDims  = ls ? { wMm: 297, hMm: 210 } : { wMm: 210, hMm: 297 };   // interior + cover face (A-series)\n    const spineDims = { wMm: 8, hMm: pageDims.hMm };\n    let build = \"\";\n    try { const sc = document.querySelector('script[src*=\"design-my-book.js\"]'); build = (sc && sc.src && (sc.src.match(/mncb=([A-Za-z0-9]+)/) || [])[1]) || \"\"; } catch (e) {}\n    const G = {\n      v: \"fp1\", build: build, mag: isMag, orient: ed.orientation || \"portrait\", total: N,\n      extra: Number(ed.extraSpreads) || 0, q: (typeof window.DMB_PDF_JPEG_QUALITY === \"number\") ? window.DMB_PDF_JPEG_QUALITY : 0.96,\n      logo: String(window.DMB_LOGO_URL || \"\"), assets: String(window.DMB_ASSET_BASE || \"\"),\n    };\n    const get = function (m, k) { if (!m) return undefined; return (m[k] !== undefined) ? m[k] : m[String(k)]; };\n    const photoSig = function (pid) {\n      if (!pid || typeof pid !== \"string\") return null;\n      const src = (typeof _dmbCropSourceId === \"function\" && _dmbIsCropDerived && _dmbIsCropDerived(pid)) ? _dmbCropSourceId(pid) : pid;\n      return pid + \":\" + (masterHave && masterHave.has(pid) ? \"m\" : \"p\") +\n             (src !== pid ? (\"<\" + src + \":\" + (masterHave && masterHave.has(src) ? \"m\" : \"p\")) : \"\") +\n             \":\" + _dmbSrStableStringify(get(ed.cropRects, pid) || null);\n    };\n    const objPids = function (objs) {\n      const out = [];\n      (objs || []).forEach(function (o) { if (o && o.photoId) out.push(o.photoId); });\n      return out;\n    };\n    // coverState.textStyles is a SHARED store: page text (pgtext_*) keeps its style\n    // there too. Page-owned entries belong to the page's fingerprint, not the cover's.\n    const pageOwnedIds = new Set();\n    try { Object.keys(ed.pageObjects || {}).forEach(function (k) { (ed.pageObjects[k] || []).forEach(function (o) { if (o && o.id) pageOwnedIds.add(String(o.id)); }); }); } catch (e) {}\n    const tsOf = function (objs) {\n      const out = {};\n      try { (objs || []).forEach(function (o) { if (o && o.id && cs.textStyles && cs.textStyles[o.id]) out[o.id] = cs.textStyles[o.id]; }); } catch (e) {}\n      return out;\n    };\n    const pageFp = async function (p) {\n      const partner = (p >= 1) ? ((p % 2 === 1) ? p + 1 : p - 1) : 0;\n      const partnerOk = partner >= 1 && partner <= N;\n      const si = Math.floor((p - 1) / 2);\n      const objs = get(ed.pageObjects, p) || [];\n      const pObjs = partnerOk ? (get(ed.pageObjects, partner) || []) : [];\n      const slots = get(ed.pageSlots, p) || null;\n      const frames = get(ed.pageFrames, p) || null;\n      const spread = (p >= 1) ? (get(ed.spreadPhoto, si) || null) : null;\n      const pids = [].concat(objPids(objs), objPids(pObjs));\n      if (slots) { if (slots.A) pids.push(slots.A); if (slots.B) pids.push(slots.B); }\n      if (frames) Object.keys(frames).forEach(function (k) { if (frames[k]) pids.push(frames[k]); });\n      if (spread && spread.photoId) pids.push(spread.photoId);\n      const body = {\n        G: G, p: p, locked: (typeof isLockedPage === \"function\") ? !!isLockedPage(p) : false,\n        bg: get(ed.pageBackgrounds, p) || null, objs: objs, partnerObjs: pObjs, partnerOk: partnerOk,\n        spread: spread, tpl: get(ed.pageTemplate, p) || null, slots: slots, rot: get(ed.pageRot, p) || null,\n        frameShapes: get(ed.pageFrameShapes, p) || null, frames: frames, frameRot: get(ed.pageFrameRot, p) || null,\n        photos: Array.from(new Set(pids)).sort().map(photoSig),\n        ts: tsOf(objs), partnerTs: tsOf(pObjs),\n      };\n      return await _dmbSrHash(_dmbSrStableStringify(body));\n    };\n    // Cover hash: the whole coverState EXCEPT textStyles entries owned by pages\n    // (otherwise every page-text edit re-rendered all three cover faces).\n    const csForCover = Object.assign({}, cs);\n    try {\n      const ts = {};\n      Object.keys(cs.textStyles || {}).forEach(function (id) { if (!pageOwnedIds.has(String(id))) ts[id] = cs.textStyles[id]; });\n      csForCover.textStyles = ts;\n    } catch (e) {}\n    const coverBody = { G: G, cover: csForCover, coverObjects: ed.coverObjects || [], coverUserTexts: ed.coverUserTexts || [], photos: [] };\n    try {\n      const cp = [];\n      objPids(ed.coverObjects).forEach(function (x) { cp.push(x); });\n      Object.keys(cs || {}).forEach(function (k) { if (/imageid$|photoid$/i.test(k) && cs[k]) cp.push(cs[k]); });\n      [cs.frontFrames, cs.backFrames].forEach(function (m) { if (m) Object.keys(m).forEach(function (k) { if (m[k]) cp.push(m[k]); }); });\n      coverBody.photos = Array.from(new Set(cp)).sort().map(photoSig);\n    } catch (e) {}\n    const coverFp = await _dmbSrHash(_dmbSrStableStringify(coverBody));\n    const blankFp = await _dmbSrHash(_dmbSrStableStringify({ G: G, blank: true, dims: pageDims }));\n\n    const fps = [], dims = [];\n    const push = function (fp, d) { fps.push(fp); dims.push({ wMm: d.wMm, hMm: d.hMm }); };\n    if (isMag) { push(\"f\" + coverFp, pageDims); push(\"b\" + blankFp, pageDims); }\n    else { push(\"f\" + coverFp, pageDims); push(\"s\" + coverFp, spineDims); push(\"k\" + coverFp, pageDims); }\n    push(\"p\" + (await pageFp(0)), pageDims);\n    for (let p = 1; p <= N; p++) {\n      if (excludeSet && excludeSet.has(p)) continue;\n      push(\"p\" + (await pageFp(p)), pageDims);\n    }\n    if (isMag) { push(\"b\" + blankFp, pageDims); push(\"k\" + coverFp, pageDims); }\n    return { fps: fps, dims: dims };\n  }\n\n  /* How many of this design's photos actually have a print master. Reported, never\n   * assumed - the whole point of the project is that \"full quality\" is a fact. */\n  async function _dmbSrMasterCoverage(qs) {\n    try {\n      const base = window.DMB_DESIGNS_PROXY;\n      const [pr, mr] = await Promise.all([\n        fetch(base + \"/photos?\" + qs, { credentials: \"same-origin\" }),\n        fetch(base + \"/photos?\" + qs + \"&kind=master\", { credentials: \"same-origin\" }),\n      ]);\n      const P = ((await pr.json().catch(() => ({}))).photos) || [];\n      const M = ((await mr.json().catch(() => ({}))).photos) || [];\n      const have = new Set(M.map((x) => x.pid));\n      const used = (function () { try { return _dmbCollectReferencedPhotoIds(); } catch (e) { return new Set(); } })();\n      const relevant = P.filter((p) => !used.size || used.has(p.pid));\n      return { total: relevant.length, withMaster: relevant.filter((p) => have.has(p.pid)).length, have: have };\n    } catch (e) { return { total: 0, withMaster: 0 }; }\n  }\n\n  /* \u2500\u2500 Proof screen \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   * Shows the REAL staged pages - the exact images that go into the PDF - not a\n   * re-render. This is the guard against the engine's swallowing try/catch: a\n   * sticker or photo that failed to load leaves a BLANK area and still reports\n   * success, and this is where a human sees it before paying.\n   *\n   * Presented the way the Preview panel presents the book: one SPREAD at a time\n   * (back|spine|front, then the inner-cover page, then facing pairs) with prev /\n   * next, so the customer reads it like the finished book instead of scanning a\n   * contact sheet. Only the current spread and its neighbours are fetched, so a\n   * 300-page book does not pull 300 full-size images. */\n  function _dmbSrProofScreen(pages, qs, info) {\n    return new Promise(function (resolve) {\n      const base  = window.DMB_DESIGNS_PROXY;\n      const isMag = !!window.DMB_MAGAZINE_MODE;\n      const isPhone = window.innerWidth <= 749;\n      // prev=1 asks for the small proof copy. No client-side fallback is needed: the\n      // Worker serves the full-resolution page whenever a preview is missing, so a job\n      // rendered before previews existed behaves exactly as it does today.\n      const srcOf = function (p) { return base + \"/render-proof-page?\" + qs + \"&i=\" + p.index + \"&prev=1\" + (p.fp ? \"&v=\" + encodeURIComponent(String(p.fp).slice(0, 24)) : \"\"); };\n      const LABEL_H = 18;   // px reserved under each page for its caption\n\n      // \u2500\u2500 Split the staged list into cover faces + interior pages \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n      // Book PDF order: front cover, spine, back cover, page 0, pages 1..N.\n      // Magazine: front cover, blank, interior..., blank, back cover.\n      const byIdx = pages.slice().sort(function (a, b) { return a.index - b.index; });\n      const last  = byIdx.length - 1;\n      let front = null, spine = null, back = null;\n      const interior = [];\n      byIdx.forEach(function (p, i) {\n        if (p.index === 0)                      { front = p; return; }\n        if (!isMag && p.wMm < 30)               { spine = p; return; }\n        if (!isMag && p.index === 2)            { back  = p; return; }\n        if (isMag && i === last)                { back  = p; return; }\n        // A magazine PDF carries two MECHANICAL blank sheets that the customer never\n        // designed and cannot act on: the inside faces of its covers, emitted by\n        // _addBlankPage as index 1 and last-1 (see the sequence at the \"Magazine order\"\n        // comment in buildPdfBlobOnePagePerBookPage). They were landing in `interior`,\n        // which did two bad things at once: the first blank became the sheet captioned\n        // \"Inside cover\", so a magazine proof opened on a blank white page as if\n        // something had gone wrong, and every page after it was numbered one too high\n        // because the numbering is just this array's index. Keep them out. (2026-09-15)\n        if (isMag && (i === 1 || i === last - 1)) return;\n        interior.push(p);\n      });\n\n      // With the blanks gone, interior[k] IS builder page k for a magazine, so number\n      // them the way the builder does. A BOOK's page 0 keeps its \"Inside cover\" name:\n      // there it really is the sheet facing the inside front cover, and the book has no\n      // blank in between. A magazine's page 0 is an ordinary first page.\n      const pageNoOf = function (p) {\n        const k = interior.indexOf(p);\n        if (isMag) return \"Page \" + Math.max(0, k);\n        return k <= 0 ? \"Inside cover\" : \"Page \" + k;\n      };\n\n      // \u2500\u2500 Views: cover, then the inner-cover page alone, then facing pairs \u2500\u2500\n      // Every view is a row of sheets laid out by their REAL mm, so a spine is\n      // exactly as tall as the faces beside it and a pair meets at the fold.\n      const views = [];\n      const faceOrder = isMag\n        ? [{ p: back, l: \"Back\" }, { p: front, l: \"Front\" }]\n        : [{ p: back, l: \"Back\" }, { p: spine, l: \"Spine\" }, { p: front, l: \"Front\" }];\n      const coverItems = faceOrder.filter(function (f) { return !!f.p; });\n      if (coverItems.length) views.push({ title: \"Your cover\", items: coverItems });\n      if (interior.length) {\n        const _firstL = isMag ? pageNoOf(interior[0]) : \"Inside cover\";\n        views.push({ title: _firstL, items: [{ p: interior[0], l: _firstL }] });\n        for (let i = 1; i < interior.length; i += 2) {\n          const pair = interior.slice(i, i + 2).map(function (p) { return { p: p, l: pageNoOf(p) }; });\n          views.push({ title: pair.map(function (x) { return x.l; }).join(\" \u2013 \"), items: pair });\n        }\n      }\n      if (!views.length) views.push({ title: \"\", items: [] });\n\n      // Width : height of each view from the page dimensions, so ONE sheet height\n      // fits every view - the reader never changes size between cover, inside\n      // cover and spreads. Falls back to A4 when a dimension is missing.\n      views.forEach(function (v) {\n        let w = 0, h = 0;\n        v.items.forEach(function (it) {\n          const pw = Number(it.p && it.p.wMm) || 210, ph = Number(it.p && it.p.hMm) || 297;\n          w += pw; if (ph > h) h = ph;\n        });\n        v.wMm = w; v.hMm = h || 297; v.aspect = w / (h || 297);\n      });\n      const maxAspect = views.reduce(function (m, v) { return Math.max(m, v.aspect || 0); }, 0) || 0.71;\n\n      // \u2550\u2550 \"Keepsake\" proof screen \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n      // Full-bleed ivory instead of a charcoal panel on a black scrim: the\n      // screen becomes the inside of the book. A roman display line (the site's\n      // own heading font via --font-heading--family) over a hairline ornament,\n      // sheets floating on the ivory ground, and ONE pill action.\n      //\n      // Every icon is inline SVG, never an emoji character - the same codepoint\n      // renders as soft 3D art on iOS, a flat cartoon on Windows and a third way\n      // on Android, and this is the last screen before payment. (2026-09-12)\n      const K = {\n        ground: \"#F7F3EC\", hair: \"#E7E1D4\", hairStrong: \"#DDD5C4\",\n        ink: \"#2C2822\", muted: \"#8C8372\", faint: \"#A89E8B\",\n        peri: \"#6B62D6\", gold: \"#C9B98E\", warn: \"#9A7B2E\"\n      };\n      const K_SERIF = \"var(--font-heading--family,'Playfair Display'),Georgia,'Times New Roman',serif\";\n      const K_SANS  = \"var(--font-body--family,'Inter'),system-ui,-apple-system,sans-serif\";\n      const NOUN    = isMag ? \"magazine\" : \"book\";\n      const ARROW   = isPhone ? 36 : 40;   // round nav button\n      const AGAP    = isPhone ? 0  : 24;   // gap between an arrow and the sheets\n\n      const ov = document.createElement(\"div\");\n      ov.id = \"dmbProofOverlay\";\n      ov.style.cssText = \"position:fixed;inset:0;z-index:1000020;background:\" + K.ground + \";\" +\n        \"display:flex;align-items:stretch;justify-content:center;font-family:\" + K_SANS + \";\";\n\n      const panel = document.createElement(\"div\");\n      panel.style.cssText = \"width:100%;max-width:1180px;box-sizing:border-box;overflow:hidden;\" +\n        \"display:flex;flex-direction:column;padding:\" +\n        (isPhone ? \"16px 12px 12px\" : \"28px 32px 20px\") + \";\";\n\n      // \u2500\u2500 Head: a FIXED roman headline, then the view caption \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n      // The headline never changes as pages are turned, so the head cannot\n      // reflow; the view name and sheet counter live in the caption beneath it.\n      const head = document.createElement(\"div\");\n      head.style.cssText = \"flex:0 0 auto;text-align:center;\";\n\n      const headline = document.createElement(\"div\");\n      headline.style.cssText = \"font-family:\" + K_SERIF + \";font-weight:700;color:\" + K.ink + \";\" +\n        \"line-height:1.14;font-size:\" + (isPhone ? \"23px\" : \"30px\") + \";\";\n      headline.textContent = \"Before we print it\";\n\n      const orn = document.createElement(\"div\");\n      orn.style.cssText = \"display:flex;align-items:center;justify-content:center;gap:9px;margin-top:9px;\";\n      const _ornRule = '<span style=\"display:block;width:' + (isPhone ? 44 : 64) +\n        'px;height:1px;background:' + K.hairStrong + '\"></span>';\n      orn.innerHTML = _ornRule +\n        '<svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M12 2.5l2.3 6.1 6.2.4-4.8 4 1.6 6-5.3-3.4-5.3 3.4 1.6-6-4.8-4 6.2-.4z\" fill=\"' + K.gold + '\"/></svg>' +\n        _ornRule;\n\n      const titleEl = document.createElement(\"div\");\n      titleEl.style.cssText = \"margin-top:9px;font-size:12px;color:\" + K.muted + \";\" +\n        \"white-space:nowrap;overflow:hidden;text-overflow:ellipsis;\";\n\n      head.appendChild(headline); head.appendChild(orn); head.appendChild(titleEl);\n\n      // The reassurance line costs vertical space the sheets need on a phone,\n      // where the swipe hint is redundant anyway (they can just swipe).\n      if (!isPhone) {\n        const sub = document.createElement(\"div\");\n        sub.style.cssText = \"margin-top:4px;font-size:12px;color:\" + K.faint + \";\";\n        // Count what they can actually TURN THROUGH, not what the PDF contains: saying\n        // \"the actual 28 printed pages\" while showing 26 sheets invites exactly the kind\n        // of \"is something missing?\" doubt this screen exists to remove.\n        const _shown = coverItems.length + interior.length;\n        sub.textContent = \"These are the actual \" + _shown +\n          \" printed pages. Turn through every one and make sure it looks right.\" +\n          (isMag ? \" The inside faces of the cover print blank.\" : \"\");\n        head.appendChild(sub);\n      }\n\n      // The \"N of M photos at full print quality\" line used to sit here. It was\n      // removed on 2026-09-14 because it measured the wrong thing and said it at the\n      // worst possible moment - to a customer with their card out.\n      //\n      // What it actually counted was a PIPELINE fact: \"does an object exist at\n      // photos-orig/{pid}\". That is not a print-quality fact, and it was wrong in\n      // BOTH directions:\n      //   - false alarm: every crop-derived photo counts as a miss even though it\n      //     prints from its source master, and (until the same day's fix) every\n      //     phone-relay photo counted as a miss while carrying MORE pixels than the\n      //     master it was being compared against;\n      //   - false calm: a book of genuine 1000px photos that WILL print soft scores\n      //     a perfect N of N, because each one is its own \"master\".\n      // It also cannot see how a photo is PLACED - the same file is sharp as a\n      // quarter-page inset and soft full-bleed - which is the only thing that\n      // decides whether print quality is actually at risk.\n      //\n      // The number is still recorded for the owner in _dmbSrTrace(\"coverage\"), where\n      // it is useful as a pipeline health signal. A real customer-facing warning\n      // needs effective DPI at placement size; until that exists, saying nothing is\n      // more honest than saying something false.\n\n      // \u2500\u2500 Arrows: flanking the sheets on a laptop, beneath them on a phone \u2500\u2500\n      // A phone needs every pixel of width for a two-page spread, so the arrows\n      // move below rather than stealing it from the sheets.\n      function arrowBtn(dir) {\n        const b = document.createElement(\"button\");\n        b.type = \"button\";\n        b.setAttribute(\"aria-label\", dir < 0 ? \"Previous sheet\" : \"Next sheet\");\n        b.style.cssText = \"flex:0 0 auto;width:\" + ARROW + \"px;height:\" + ARROW + \"px;border-radius:50%;\" +\n          \"border:1px solid \" + K.hairStrong + \";background:transparent;color:\" + K.ink + \";padding:0;\" +\n          \"display:flex;align-items:center;justify-content:center;cursor:pointer;\" +\n          \"transition:background .15s ease,opacity .15s ease;\";\n        b.innerHTML = '<svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M' +\n          (dir < 0 ? \"14.5 5.5L8 12l6.5 6.5\" : \"9.5 5.5L16 12l-6.5 6.5\") +\n          '\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>';\n        b.addEventListener(\"click\", function () { go(dir); });\n        b.addEventListener(\"mouseenter\", function () { if (!b.disabled) b.style.background = \"#EFE8DA\"; });\n        b.addEventListener(\"mouseleave\", function () { b.style.background = \"transparent\"; });\n        return b;\n      }\n      const prevBtn = arrowBtn(-1);\n      const nextBtn = arrowBtn(1);\n\n      const PADX = isPhone ? 4 : 12;   // body padding-x per side (sheetHeight subtracts 2x)\n      const body = document.createElement(\"div\");\n      body.style.cssText = \"position:relative;flex:1 1 auto;min-height:0;display:flex;align-items:center;\" +\n        \"justify-content:center;gap:\" + AGAP + \"px;padding:\" + (isPhone ? \"10px 4px\" : \"18px 12px\") + \";\";\n\n      const stage = document.createElement(\"div\");\n      stage.style.cssText = \"flex:0 1 auto;min-width:0;display:flex;justify-content:center;\";\n\n      if (isPhone) {\n        body.appendChild(stage);\n      } else {\n        // PINNED, not flanking. The arrows used to be flex siblings of the stage, so\n        // justify-content:center centred the WHOLE row - arrows included - and a narrow\n        // view (the single inside-cover page) pulled both arrows inward while a full\n        // spread pushed them back out. They moved on every turn, which is the one thing\n        // a page-turn control must never do: the customer is aiming at a target that\n        // relocates under the cursor between clicks.\n        //\n        // Anchored to the body's own edges they hold ONE position for every sheet. The\n        // sheet-height reserve below is deliberately left alone - it still subtracts the\n        // arrows and their gaps - so the widest sheet stops clear of them exactly as it\n        // did, and taking them out of the flex flow cannot change the measured height\n        // (they are shorter than the sheets and the padding is symmetric). (2026-09-14)\n        [prevBtn, nextBtn].forEach(function (b) {\n          b.style.position = \"absolute\";\n          b.style.top = \"50%\";\n          b.style.transform = \"translateY(-50%)\";\n          b.style.zIndex = \"2\";\n        });\n        prevBtn.style.left  = PADX + \"px\";\n        nextBtn.style.right = PADX + \"px\";\n        body.appendChild(prevBtn); body.appendChild(stage); body.appendChild(nextBtn);\n      }\n\n      let phoneNav = null;\n      if (isPhone) {\n        phoneNav = document.createElement(\"div\");\n        phoneNav.style.cssText = \"flex:0 0 auto;display:flex;align-items:center;\" +\n          \"justify-content:center;gap:48px;padding:2px 0 8px;\";\n        phoneNav.appendChild(prevBtn); phoneNav.appendChild(nextBtn);\n      }\n\n      // \u2500\u2500 One common sheet height for every view \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n      // The tallest sheet that fits the WIDEST view (cover or a spread) inside\n      // the body; single pages then use the same height and simply sit narrower,\n      // so the reader never changes size between cover, inside cover and spreads.\n      function sheetHeight() {\n        // clientWidth/clientHeight INCLUDE padding, so subtract BOTH sides of it.\n        const reserve = isPhone ? 0 : 2 * (ARROW + AGAP);   // arrows + their gaps\n        const padX    = isPhone ? 8  : 24;                  // 2 x body padding-x\n        const padY    = isPhone ? 20 : 36;                  // 2 x body padding-y\n        let bw = Math.max(0, body.clientWidth  - padX - reserve);\n        let bh = Math.max(0, body.clientHeight - padY - LABEL_H);\n        if (!bw || !bh) {\n          // Not laid out yet. Fall back to the viewport box rather than\n          // returning 0 - a zero height paints every sheet invisible, which is\n          // precisely how the Preview panel once shipped broken.\n          bw = Math.max(0, window.innerWidth  - (isPhone ? 32 : 88) - reserve);\n          bh = Math.max(0, window.innerHeight - (isPhone ? 250 : 270));\n          if (!bw || !bh) return 0;\n        }\n        return Math.max(40, Math.min(bh, bw / maxAspect));\n      }\n\n      function sheetNode(it, hPx) {\n        const p = it.p;\n        const pw = Number(p && p.wMm) || 210, ph = Number(p && p.hMm) || 297;\n        const wPx = hPx * (pw / ph);\n        const slot = document.createElement(\"div\");\n        slot.style.cssText = \"flex:0 0 auto;display:flex;flex-direction:column;align-items:center;\" +\n          \"width:\" + wPx.toFixed(2) + \"px;\";\n        const img = document.createElement(\"img\");\n        img.decoding = \"async\";\n        img.alt = it.l || \"\";\n        img.src = srcOf(p);\n        img.style.cssText = \"display:block;background:#fff;width:\" + wPx.toFixed(2) + \"px;\" +\n          \"height:\" + hPx.toFixed(2) + \"px;border-radius:1px;cursor:zoom-in;\" +\n          \"box-shadow:0 6px 22px rgba(60,50,35,0.20),0 1px 3px rgba(60,50,35,0.12);\";\n        img.addEventListener(\"click\", function () { try { window.open(img.src, \"_blank\"); } catch (e) {} });\n        const lbl = document.createElement(\"div\");\n        // A spine slot is only ~9px wide, so its caption can never render as\n        // anything but a clipped fragment (\"SP\"). Blank it and keep the row\n        // height, rather than printing debris between BACK and FRONT.\n        lbl.textContent = (wPx < 40) ? \"\" : (it.l || \"\");\n        lbl.style.cssText = \"height:\" + LABEL_H + \"px;line-height:\" + LABEL_H + \"px;color:\" + K.faint + \";\" +\n          \"font-size:9px;letter-spacing:0.12em;text-transform:uppercase;white-space:nowrap;\" +\n          \"overflow:hidden;max-width:100%;\";\n        slot.appendChild(img); slot.appendChild(lbl);\n        return slot;\n      }\n\n      let idx = 0;\n      let seenEnd = false;\n      function render() {\n        stage.innerHTML = \"\";\n        const v = views[idx];\n        // Caption first: it is the only head content written per-render, so\n        // measuring before it is filled sizes view 1 against a shorter head.\n        titleEl.textContent = (v.title || (\"Your \" + NOUN)) +\n          \" \u00B7 sheet \" + (idx + 1) + \" of \" + views.length;\n        if (idx === views.length - 1) seenEnd = true;\n        updateOk();\n        const hPx = sheetHeight();\n\n        // Park the arrows against the WIDEST sheet row rather than the body edge, so\n        // they sit exactly where the two-page spread put them and stay there for the\n        // narrow views too. hPx does not depend on the current view (that is the whole\n        // point of one common sheet height), so maxAspect * hPx is constant and this\n        // lands on the same pixel every render. When the widest view is limited by\n        // WIDTH rather than height the expression collapses to PADX on its own - the\n        // old outer position - so nothing regresses on a short, wide window.\n        if (!isPhone && body.clientWidth) {\n          const _inset = Math.max(PADX, Math.round((body.clientWidth - maxAspect * hPx) / 2) - AGAP - ARROW);\n          prevBtn.style.left  = _inset + \"px\";\n          nextBtn.style.right = _inset + \"px\";\n        }\n\n        const row = document.createElement(\"div\");\n        row.style.cssText = \"display:flex;gap:0;align-items:flex-start;justify-content:center;\";\n        v.items.forEach(function (it) { row.appendChild(sheetNode(it, hPx)); });\n        stage.appendChild(row);\n\n        prevBtn.disabled = (idx === 0);\n        nextBtn.disabled = (idx === views.length - 1);\n        [prevBtn, nextBtn].forEach(function (b) {\n          b.style.opacity = b.disabled ? \"0.3\" : \"1\";\n          b.style.cursor  = b.disabled ? \"default\" : \"pointer\";\n        });\n        preload(idx + 1); preload(idx - 1);\n      }\n\n      // Warm the neighbouring views so turning a page is instant, without\n      // fetching the whole book up front.\n      const warmed = new Set();\n      function preload(i) {\n        if (i < 0 || i >= views.length) return;\n        views[i].items.forEach(function (it) {\n          const p = it.p;\n          if (!p || warmed.has(p.index)) return;\n          warmed.add(p.index);\n          const im = new Image(); im.decoding = \"async\"; im.src = srcOf(p);\n        });\n      }\n\n      function go(d) {\n        const n = Math.max(0, Math.min(views.length - 1, idx + d));\n        if (n !== idx) { idx = n; render(); }\n      }\n\n      const onKey = function (e) {\n        if (e.key === \"ArrowLeft\")  { e.preventDefault(); go(-1); }\n        if (e.key === \"ArrowRight\") { e.preventDefault(); go(1); }\n      };\n      document.addEventListener(\"keydown\", onKey);\n      const onResize = function () { render(); };\n      window.addEventListener(\"resize\", onResize);\n\n      // Swipe on touch - the phone has no other affordance beside the arrows.\n      let tX = 0, tY = 0;\n      body.addEventListener(\"touchstart\", function (e) {\n        if (!e.touches || !e.touches[0]) return;\n        tX = e.touches[0].clientX; tY = e.touches[0].clientY;\n      }, { passive: true });\n      body.addEventListener(\"touchend\", function (e) {\n        const t = e.changedTouches && e.changedTouches[0];\n        if (!t) return;\n        const dx = t.clientX - tX, dy = t.clientY - tY;\n        if (Math.abs(dx) > 50 && Math.abs(dx) > Math.abs(dy)) go(dx < 0 ? 1 : -1);\n      }, { passive: true });\n\n      // \u2500\u2500 Foot: one pill action, the reject path demoted to a quiet link \u2500\u2500\u2500\n      const foot = document.createElement(\"div\");\n      foot.style.cssText = \"flex:0 0 auto;display:flex;flex-direction:column;align-items:center;gap:10px;\" +\n        \"border-top:1px solid \" + K.hair + \";padding-top:\" + (isPhone ? \"13px\" : \"16px\") + \";\";\n\n      const hint = document.createElement(\"div\");\n      // FIXED height, not min-height: this line is written per-render, and an\n      // empty vs filled box differed by 4.4px - enough to size view 1 against a\n      // taller body and overflow its sheets. Nothing in the head or the foot may\n      // change height between renders. (2026-09-12)\n      hint.style.cssText = \"font-size:11.5px;color:\" + K.faint + \";text-align:center;\" +\n        \"height:18px;line-height:18px;overflow:hidden;\";\n\n      const ok = document.createElement(\"button\");\n      ok.type = \"button\";\n      ok.style.cssText = \"display:inline-flex;align-items:center;gap:8px;border:none;border-radius:99px;\" +\n        \"background:\" + K.peri + \";color:#fff;cursor:pointer;font-family:inherit;font-weight:600;\" +\n        \"font-size:\" + (isPhone ? \"14px\" : \"15px\") + \";letter-spacing:0.01em;\" +\n        \"padding:\" + (isPhone ? \"12px 26px\" : \"13px 32px\") + \";\" +\n        \"box-shadow:0 2px 10px rgba(107,98,214,0.28);\";\n      ok.innerHTML = '<svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M12 20.6l-1.5-1.4C5.4 14.6 2.5 11.9 2.5 8.6 2.5 6 4.5 4 7.1 4c1.5 0 2.9.7 3.8 1.8l1.1 1.3 1.1-1.3C14 4.7 15.4 4 16.9 4 19.5 4 21.5 6 21.5 8.6c0 3.3-2.9 6-8 10.6z\" fill=\"#fff\" opacity=\"0.92\"/></svg><span></span>';\n      const okLabel = ok.querySelector(\"span\");\n      okLabel.textContent = \"Yes \u2014 print my \" + NOUN;\n\n      const backBtn = document.createElement(\"button\");\n      backBtn.type = \"button\";\n      backBtn.textContent = \"Something's wrong, let me fix it\";\n      backBtn.style.cssText = \"background:none;border:none;cursor:pointer;font-family:inherit;padding:2px 4px;\" +\n        \"font-size:12px;color:\" + K.muted + \";text-decoration:underline;text-underline-offset:3px;\";\n\n      // Nudge them to actually look at the whole book, but never block the sale.\n      function updateOk() {\n        hint.textContent = seenEnd ? \"\" : \"Turn to the last page to check everything\";\n      }\n\n      let settled = false;\n      const done = function (v) {\n        if (settled) return; settled = true;\n        document.removeEventListener(\"keydown\", onKey);\n        window.removeEventListener(\"resize\", onResize);\n        try { ov.remove(); } catch (e) {}\n        resolve(v);\n      };\n      backBtn.addEventListener(\"click\", function () { done(\"reject\"); });\n      ok.addEventListener(\"click\", function () {\n        ok.disabled = true; ok.style.opacity = \"0.6\"; ok.style.cursor = \"default\";\n        okLabel.textContent = \"Preparing\";\n        done(\"approve\");\n      });\n\n      foot.appendChild(hint); foot.appendChild(ok); foot.appendChild(backBtn);\n      panel.appendChild(head); panel.appendChild(body);\n      if (phoneNav) panel.appendChild(phoneNav);\n      panel.appendChild(foot);\n      ov.appendChild(panel);\n      document.body.appendChild(ov);\n\n      // Measure AFTER the panel is in the document, otherwise clientWidth is 0.\n      render();\n    });\n  }\n  async function _dmbSrPoll(qs) {\n    const r = await fetch(window.DMB_DESIGNS_PROXY + \"/assemble-status?\" + qs, { credentials: \"same-origin\" });\n    return await r.json().catch(function () { return { status: \"unknown\" }; });\n  }\n  function _dmbSrAbort(qs) {\n    try {\n      return fetch(window.DMB_DESIGNS_PROXY + \"/render-abort?\" + qs, { method: \"POST\", credentials: \"same-origin\" });\n    } catch (e) { return Promise.resolve(); }\n  }\n\n  /* Returns { id, viewUrl, downloadUrl } shaped exactly like uploadPdfToGoogleDrive,\n   * or NULL to fall back to the in-browser pipeline. Never throws past the caller. */\n  async function _dmbServerRenderAndBind(excludePages, onStatus, onProgress, onHide) {\n    _dmbSrTrace(\"enter\");\n    const ids = _dmbSrIds();\n    if (!ids.id) { _dmbSrTrace(\"no_design_id\"); return null; }\n    const qs = ids.qs;\n    const say = function (m) { try { if (onStatus) onStatus(m); } catch (e) {} };\n\n    try {\n      say(\"Saving your design\");\n      _dmbSrTrace(\"cloud_save\");\n      const rev = await _dmbSrForceCloudSave();\n      _dmbSrTrace(\"cloud_saved\", rev.slice(0, 8));\n\n      // Masters upload on a BACKGROUND chain behind the proxies: the import modal\n      // only waits for proxies, so on a phone that chain can still be running\n      // minutes later - and this render reads from photos-orig/. Wait for it,\n      // exactly as the in-browser PDF path does. Without this the proof reported\n      // \"0 of N at full print quality\" and the server rendered from the proxies.\n      if (_dmbMasterPendingCount > 0) {\n        _dmbSrTrace(\"masters_pending\", _dmbMasterPendingCount);\n        const _n0 = _dmbMasterPendingCount;\n        let _tick = null;\n        try {\n          say(\"Uploading your full-quality photos\");\n          _tick = setInterval(function () {\n            say(\"Uploading your full-quality photos \" + Math.max(0, _n0 - _dmbMasterPendingCount) + \" / \" + _n0);\n          }, 500);\n          // The chain is re-assigned as items are queued; loop until nothing is left.\n          for (let _g = 0; _g < 50 && _dmbMasterPendingCount > 0; _g++) {\n            try { await _dmbMasterChain; } catch (e) {}\n          }\n        } finally { if (_tick) clearInterval(_tick); }\n        _dmbSrTrace(\"masters_done\", _dmbMasterPendingCount);\n      }\n\n      const cov = await _dmbSrMasterCoverage(qs);\n      _dmbSrTrace(\"coverage\", Object.assign({}, cov, { stats: _dmbMasterStats }));\n\n      say(\"Preparing your pages\");\n      const ex = (excludePages && excludePages.size) ? Array.from(excludePages) : [];\n      // Per-page fingerprints from the SAME snapshot that was just saved (the render\n      // page loads that saved state), so \"unchanged\" means unchanged in what it sees.\n      let _fpr = null;\n      try {\n        _fpr = await _dmbSrPageFingerprints(getCurrentBuilderStateSnapshot(), excludePages, (cov && cov.have) || null);\n        _dmbSrTrace(\"fps\", _fpr && _fpr.fps.length);\n      } catch (e) { _fpr = null; _dmbSrTrace(\"fps_failed\", String((e && e.message) || e)); }\n      const mint = await fetch(window.DMB_DESIGNS_PROXY + \"/render-job?\" + qs, {\n        method: \"POST\", headers: { \"content-type\": \"application/json\" }, credentials: \"same-origin\",\n        body: JSON.stringify(Object.assign({ rev: rev, exclude: ex, crops: \"prefer-r2\" },\n          _fpr ? { fps: _fpr.fps, dims: _fpr.dims } : {})),\n      });\n      const mj = await mint.json().catch(function () { return null; });\n      _dmbSrTrace(\"mint\", { http: mint.status, ok: !!(mj && mj.ok), err: mj && mj.error, reuse: mj && mj.reuse, total: mj && mj.total });\n      if (!mint.ok || !mj || !mj.ok) throw new Error((mj && mj.error) || (\"mint HTTP \" + mint.status));\n      if (mj.reuse > 0) say(\"Keeping \" + mj.reuse + \" unchanged page\" + (mj.reuse === 1 ? \"\" : \"s\"));\n\n      // \u2500\u2500 Wait for the render. This is the SAME wait the customer has today (their\n      //    phone rendering every page), only it happens on a server that cannot run\n      //    out of memory - so it is not new waiting, it is the same wait moved.\n      const deadline = Date.now() + 40 * 60 * 1000;\n      let st = null, lastBeat = Date.now(), lastDone = -1;\n      for (;;) {\n        await new Promise(function (r) { setTimeout(r, 3000); });\n        if (Date.now() > deadline) throw new Error(\"render timed out\");\n        st = await _dmbSrPoll(qs);\n        if (!st) continue;\n        if (st.status === \"error\") throw new Error(st.error || \"render failed\");\n        if (st.phase === \"proof\") { _dmbSrTrace(\"proof_ready\"); break; }\n        if (st.status === \"done\") break;                  // already bound (resumed job)\n        const d = Number(st.done) || 0, t = Number(st.total) || 0;\n        if (d !== lastDone) { lastDone = d; lastBeat = Date.now(); }\n        else if (Date.now() - lastBeat > 12 * 60 * 1000) throw new Error(\"render stalled\");\n        if (t) {\n          say(\"Printing your pages\u2026 \" + d + \" / \" + t);\n          try { if (onProgress) onProgress(d, t); } catch (e) {}\n        }\n      }\n\n      // \u2500\u2500 MANDATORY PROOF \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n      if (st.status !== \"done\") {\n        const pr = await fetch(window.DMB_DESIGNS_PROXY + \"/render-proof?\" + qs, { credentials: \"same-origin\" });\n        const pj = await pr.json().catch(function () { return null; });\n        if (!pj || !pj.ok || !(pj.pages || []).length) throw new Error(\"proof unavailable\");\n\n        // The overlay helpers are locals of the cart handler, so it hands us its hide fn.\n        try { if (onHide) onHide(); } catch (e) {}\n        _dmbSrTrace(\"proof_shown\", (pj.pages || []).length);\n        const choice = await _dmbSrProofScreen(pj.pages, qs, cov);\n        _dmbSrTrace(\"proof_choice\", choice);\n        // REJECTED is not a FAILURE. null means \"the server render did not work,\n        // fall back to the browser\"; this sentinel means \"the customer wants to keep\n        // editing\", which must stop the checkout entirely. Conflating the two made\n        // \"go back\" start a full in-browser render behind a stuck overlay.\n        if (choice !== \"approve\") { await _dmbSrAbort(qs); return \"__dmb_proof_rejected__\"; }\n        say((\"Binding your \" + (window.DMB_MAGAZINE_MODE ? \"magazine\" : \"book\")));\n\n        const ap = await fetch(window.DMB_DESIGNS_PROXY + \"/render-approve?\" + qs, {\n          method: \"POST\", headers: { \"content-type\": \"application/json\" },\n          credentials: \"same-origin\", body: JSON.stringify({ rev: pj.rev || rev }),\n        });\n        const aj = await ap.json().catch(function () { return null; });\n        if (!ap.ok || !aj || !aj.ok) throw new Error((aj && aj.error) || (\"approve HTTP \" + ap.status));\n      }\n\n      // \u2500\u2500 Binding (the existing stapler chain, untouched) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n      // Binding is the slowest leg and it used to show ONE static line for up to\n      // 12 minutes, so a job that was working looked identical to one that had\n      // died - which is how a customer reads it, and then they reload and lose the\n      // whole render. The stapler pulls every page image out of R2 one at a time,\n      // builds the PDF and uploads it to Drive, so minutes IS normal on a big\n      // book. Show the clock and say so. (2026-09-13)\n      const bindBy = Date.now() + 12 * 60 * 1000;\n      const bindT0 = Date.now();\n      for (;;) {\n        if (st && st.status === \"done\" && st.id) break;\n        await new Promise(function (r) { setTimeout(r, 3000); });\n        if (Date.now() > bindBy) throw new Error(\"binding timed out\");\n        st = await _dmbSrPoll(qs);\n        if (st && st.status === \"error\") throw new Error(st.error || \"binding failed\");\n        try {\n          var _el = Math.max(0, Math.round((Date.now() - bindT0) / 1000));\n          // NO clock: a stopwatch counting up on a checkout screen reads as\n          // \"something is wrong\", which is the opposite of reassuring. The\n          // breathing book mark already proves the screen is alive; the staged\n          // message below is what actually stops someone reloading - and losing\n          // the whole render - when a big book legitimately takes minutes.\n          say(\"Binding your \" + (window.DMB_MAGAZINE_MODE ? \"magazine\" : \"book\"));\n          var _lbl = document.getElementById(\"dmbUploadProgressLabel\");\n          if (_lbl) {\n            _lbl.textContent = (_el < 45)\n              ? \"Stitching your pages into one file\"\n              : \"Still working. A big book takes a few minutes \\u2014 please keep this tab open.\";\n          }\n        } catch (e) {}\n      }\n      if (!st.id) throw new Error(\"no file id\");\n      _dmbSrTrace(\"bound\", { pages: st.pages, bytes: st.bytes });\n      return { id: st.id, viewUrl: st.viewUrl, downloadUrl: st.downloadUrl };\n    } catch (err) {\n      _dmbSrTrace(\"FAILED\", String((err && err.message) || err).slice(0, 200));\n      // Free the job so the /assemble interlock cannot block the in-browser fallback.\n      try { await _dmbSrAbort(qs); } catch (e) {}\n      return null;\n    }\n  }\n\n})(); // closes the main (() => { app IIFE at line 699\n\n"],
  "mappings": "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",
  "names": ["applyDmbFrameShape", "showDmbFramePicker", "editor", "builderMode", "renderCoverCanvas", "renderSpread", "syncBuilderModeButtons", "setCanvasZoom", "currentZoom", "_dmbSnapAngle", "getDragPayload", "pushUndoState", "pagesEl", "bookMeta", "renderThumbsRail", "mode", "initUnifiedTabs", "zoomRange", "renderPhotoTray", "getPhotoById", "migrateCoverObjectsToFlat", "showFrameToolbarFor", "_dmbPointOnGlyphs", "_dmbSelectableBeneath", "_dmbSelectObjectEl", "dmbApplyZoneClip", "_dmbModalOpen", "showRotDegTooltip", "hideRotDegTooltip", "attachTextNodeHandlers", "titleEl", "activateLeftTab", "initCoverDesigner", "_openMapOpts", "_closeMapOpts", "bar", "closeModal", "st", "builderStep", "_dmbShowUploadMobileNotice", "_liveSides", "updateRotPos", "sStickerHandlePts", "updateStickerHandles", "updateStickerRotPos", "_cW", "_cH", "updateShRotPos", "miHandlePts", "updateMiHandles", "updateMiRotPos", "openCropModal", "photoId", "used", "forwardDropToBody", "step2", "applyTo", "sizeItems", "renderTray", "p", "stickerImg", "_txRunFace", "_txRunLineW", "i", "_txRunLineMetrics", "_txRunLineInk", "_txBaseAt", "_txRunDraw", "_txDrawLine", "spreadCanvas", "historyBtn"]
}
